Skip to content

build-dtb-image: recurse vendor subdirs and prefer Debian standard DTB path with usrmerge fallback; build-rootfs: bypass zz-update-grub systemd guard in chroot#133

Merged
bjordiscollaku merged 4 commits intomainfrom
fix/pkg-linux-qcom-boot-tools-compat
Apr 14, 2026
Merged

build-dtb-image: recurse vendor subdirs and prefer Debian standard DTB path with usrmerge fallback; build-rootfs: bypass zz-update-grub systemd guard in chroot#133
bjordiscollaku merged 4 commits intomainfrom
fix/pkg-linux-qcom-boot-tools-compat

Conversation

@bjordiscollaku
Copy link
Copy Markdown
Contributor

@bjordiscollaku bjordiscollaku commented Apr 3, 2026

Problem

Three independent failures surface when the CI pipeline switches from the
legacy build-kernel-deb.sh-generated package to the pkg-linux-qcom
Debian package (linux-image-<KVER>-qcom) built via debian/ metadata.

1 — build-dtb-image.sh: flat glob misses DTBs in vendor subdirectories

The FIT image build path collected DTBs using a flat glob:

cp -rap "${DTB_SRC}"/*.dtb* "${DTB_STAGE}/"

This assumes all DTB files reside directly in DTB_SRC. In practice,
DTBs are always installed one level deeper under a vendor subdirectory:

lib/firmware/<KVER>/device-tree/
  qcom/
    x1e80100-qcp.dtb
    sc8280xp-lenovo-thinkpad-x13s.dtb
    ...

When --kernel-deb is used, DTB_SRC resolves to the device-tree
directory (a symlink to the Debian-standard install path
usr/lib/linux-image-<KVER>/). The glob expands to nothing and the
build fails:

[INFO] Copying DTBs from /tmp/kernel-deb-ILIZ66/lib/firmware/7.0.0-rc5/device-tree ...
cp: cannot stat '/tmp/kernel-deb-ILIZ66/lib/firmware/7.0.0-rc5/device-tree/*.dtb*': No such file or directory
Error: Process completed with exit code 1.

2 — build-dtb-image.sh: device-tree directory not found in usrmerge packages

pkg-linux-qcom now installs the device-tree compat symlink under
usr/lib/firmware/<KVER>/device-tree (required to fix broken symlink
resolution on usrmerge systems where /lib → usr/lib). The previous
discovery glob only searched lib/firmware/*/device-tree:

dt_dirs=( "${DEB_DIR}/lib/firmware"/*/device-tree )

This expands to nothing for packages built with the updated debian/rules,
causing --kernel-deb mode to fail immediately after extraction.

3 — build-rootfs.sh: /boot/grub/grub.cfg never created in chroot

The pkg-linux-qcom postinst correctly follows the Debian standard of
delegating bootloader updates to /etc/kernel/postinst.d hooks. The
zz-update-grub hook (from grub-common) guards its update-grub call
with a systemd check:

if [ -d /run/systemd/system ]; then update-grub || true; fi

In a chroot, systemd is not running, so /run/systemd/system does not
exist and update-grub is silently skipped — /boot/grub/grub.cfg is
never created. The subsequent sed-based GRUB cleanup then fails:

sed: can't read /boot/grub/grub.cfg: No such file or directory
Error: Process completed with exit code 1.

The build-kernel-deb.sh-generated package called update-grub
unconditionally in its postinst, so it was unaffected.


Fix

1 — build-dtb-image.sh: recursive DTB collection

Replace the flat glob with find -L, which follows symlinks and recurses
into all vendor subdirectories, copying every *.dtb and *.dtbo file
flat into the mkimage staging directory (arch/arm64/boot/dts/qcom/),
as the ITS file expects:

while IFS= read -r dtb; do
    cp -p "${dtb}" "${DTB_STAGE}/"
done < <(find -L "${DTB_SRC}" \( -name '*.dtb' -o -name '*.dtbo' \) -type f)

find -L follows symlinks transparently, so this works correctly whether
device-tree is a real directory or a relative directory symlink (as
introduced in pkg-linux-qcom#11).

A post-copy count check and sorted DTB listing are also added so that an
empty or misconfigured DTB source is caught early with a clear diagnostic
rather than a cryptic mkimage FATAL ERROR:

[INFO] Staged 47 DTB file(s) to arch/arm64/boot/dts/qcom
[INFO] Staged DTBs:
        qcm6490-idp.dtb
        sc8280xp-lenovo-thinkpad-x13s.dtb
        ...

2 — build-dtb-image.sh: Debian-standard-first DTB discovery

Probe paths in order, falling back only if the preferred path is absent:

# 1. Debian standard install location (direct, no symlink — works on Debian and Ubuntu)
dt_dirs=( "${DEB_DIR}/usr/lib/linux-image-"*/ )
# 2. Ubuntu compat symlink (usrmerge layout)
if (( ${#dt_dirs[@]} == 0 )); then
    dt_dirs=( "${DEB_DIR}/usr/lib/firmware"/*/device-tree )
fi
# 3. Ubuntu compat symlink (legacy layout)
if (( ${#dt_dirs[@]} == 0 )); then
    dt_dirs=( "${DEB_DIR}/lib/firmware"/*/device-tree )
fi

Probing the Debian standard path first avoids any dependency on the Ubuntu
compat symlink and works correctly on pure Debian systems that never install
it. The fallback chain is fully backward compatible: legacy packages (built
by build-kernel-deb.sh) only populate lib/firmware/, so the script
falls through steps 1 and 2 and resolves via step 3 as before.

3 — build-rootfs.sh: explicit update-grub in chroot

Call update-grub explicitly in the chroot immediately after kernel
installation, bypassing the systemd guard. build-rootfs.sh owns the
chroot environment and is the right place to ensure grub.cfg is
generated — the kernel package postinst is intentionally unchanged:

yes "" | dpkg -i /linux-image-<KVER>-qcom.deb

# Run update-grub explicitly: the zz-update-grub hook skips it in a chroot
# because systemd is not running (/run/systemd/system absent).
update-grub

Changes

  • kernel/scripts/build-dtb-image.sh: replace cp -rap *.dtb* flat glob
    with recursive find -L + cp loop in FIT image mode; add staged DTB
    count check and listing; probe usr/lib/linux-image-*/ first (Debian
    standard), then usr/lib/firmware/*/device-tree (usrmerge), then
    lib/firmware/*/device-tree (legacy) for full backward compatibility
  • rootfs/scripts/build-rootfs.sh: call update-grub explicitly after
    kernel .deb installation to ensure /boot/grub/grub.cfg is generated
    in chroot environments where the zz-update-grub systemd guard skips it

Testing

# Fix 1+2 — DTB collection (pkg-linux-qcom package, Debian standard path)
./scripts/build-dtb-image.sh \
    --fit-image \
    --kernel-deb linux-image-7.0.0-rc5-qcom_1-1_arm64.deb \
    --metadata-commit 5d24fea316a512f85acf7a4528a118ce52223530 \
    --out dtb.bin
# Previously: cp: cannot stat '.../device-tree/*.dtb*': No such file or directory
#         or: No DTB directory found at '.../lib/firmware/*/device-tree'
# Now: DTBs collected from usr/lib/linux-image-.../ and staged correctly ✓
#      Falls back to lib/firmware/ for legacy packages ✓

# Fix 3 — GRUB config generation
./build-rootfs.sh \
    --product-conf qcom-product.conf \
    --seed seed.txt \
    --kernel-package linux-image-7.0.0-rc5-qcom_1-1_arm64.deb
# Previously: sed: can't read /boot/grub/grub.cfg: No such file or directory
# Now: update-grub runs in chroot, grub.cfg generated, sed cleanup succeeds ✓

@bjordiscollaku bjordiscollaku changed the title build-dtb-image: recurse vendor subdirs with find -L; build-rootfs: call update-grub explicitly to bypass zz-update-grub systemd guard in chroot build-dtb-image: recurse vendor subdirs and fix usrmerge DTB discovery; build-rootfs: call update-grub explicitly to bypass zz-update-grub systemd guard in chroot Apr 3, 2026
@bjordiscollaku bjordiscollaku force-pushed the fix/pkg-linux-qcom-boot-tools-compat branch from a6f0307 to dd9d3f1 Compare April 7, 2026 17:50
@bjordiscollaku bjordiscollaku changed the title build-dtb-image: recurse vendor subdirs and fix usrmerge DTB discovery; build-rootfs: call update-grub explicitly to bypass zz-update-grub systemd guard in chroot build-dtb-image: recurse vendor subdirs and prefer Debian standard DTB path with usrmerge fallback; build-rootfs: bypass zz-update-grub systemd guard in chroot Apr 9, 2026
@bjordiscollaku bjordiscollaku force-pushed the fix/pkg-linux-qcom-boot-tools-compat branch 2 times, most recently from a5f5a8f to 03c46c0 Compare April 14, 2026 17:26
The FIT image build path used a flat glob to collect DTBs from the
resolved source directory:

  cp -rap "${DTB_SRC}"/*.dtb* "${DTB_STAGE}/"

This assumes all DTB files reside directly in DTB_SRC. In practice,
DTBs are always installed one level deeper under a vendor subdirectory:

  lib/firmware/<KVER>/device-tree/qcom/*.dtb

When --kernel-deb is used, DTB_SRC resolves to the device-tree
directory (a symlink to usr/lib/linux-image-<KVER>/). The glob
expands to nothing because the DTBs are in qcom/, not at the top
level, causing the build to fail:

  cp: cannot stat '.../device-tree/*.dtb*': No such file or directory

Replace the flat glob with find -L, which follows symlinks and
recurses into all vendor subdirectories, copying every *.dtb and
*.dtbo file flat into the mkimage staging directory
(arch/arm64/boot/dts/qcom/) as the ITS file expects.

Also add a post-copy count check and a sorted DTB listing so that
an empty or misconfigured DTB source is caught early with a clear
diagnostic rather than a cryptic mkimage FATAL ERROR.

Signed-off-by: Bjordis Collaku <bcollaku@qti.qualcomm.com>
The /etc/kernel/postinst.d/zz-update-grub hook (grub-common) guards its
update-grub call with a systemd check:

  if [ -d /run/systemd/system ]; then update-grub || true; fi

In a chroot environment systemd is not running, so /run/systemd/system
does not exist and update-grub is silently skipped. This left
/boot/grub/grub.cfg absent, causing the subsequent sed-based GRUB
cleanup to fail with:

  sed: can't read /boot/grub/grub.cfg: No such file or directory

The build-kernel-deb.sh-generated package calls update-grub directly in
its postinst (unconditionally), so it was unaffected. The pkg-linux-qcom
package follows the Debian standard of delegating to postinst.d hooks,
which exposed this chroot-specific gap.

Fix: call update-grub explicitly in the chroot immediately after kernel
installation, bypassing the systemd guard. No changes to the kernel
package postinst — grub.cfg generation is the build script's
responsibility in a chroot context.

Signed-off-by: Bjordis Collaku <bcollaku@qti.qualcomm.com>
pkg-linux-qcom now installs the device-tree compat symlink under
usr/lib/firmware/<KVER>/device-tree (usrmerge layout). Update the
--kernel-deb DTB discovery to check usr/lib/firmware/ first and fall
back to lib/firmware/ for legacy packages, so both layouts are handled
transparently.

Signed-off-by: Bjordis Collaku <bcollaku@qti.qualcomm.com>
… mode

When extracting DTBs from a kernel .deb, probe paths in order:
  1. usr/lib/linux-image-*/        (Debian standard — direct, no symlink)
  2. usr/lib/firmware/*/device-tree (Ubuntu compat, usrmerge layout)
  3. lib/firmware/*/device-tree     (Ubuntu compat, legacy layout)

Probing the Debian standard path first avoids any dependency on the
Ubuntu compat symlink and works correctly on pure Debian systems that
never install it, as well as on Ubuntu regardless of usrmerge layout.

The fallback chain is fully backward compatible: legacy packages
(built by build-kernel-deb.sh) only populate lib/firmware/, so the
script falls through steps 1 and 2 and resolves via step 3 as before.

Signed-off-by: Bjordis Collaku <bcollaku@qti.qualcomm.com>
@bjordiscollaku bjordiscollaku force-pushed the fix/pkg-linux-qcom-boot-tools-compat branch from 03c46c0 to e5b64c7 Compare April 14, 2026 20:51
@bjordiscollaku bjordiscollaku merged commit eb9d808 into main Apr 14, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants