Skip to content

build-rootfs: Transition from custom GRUB config to upstream standard (update-grub)#108

Merged
bjordiscollaku merged 10 commits intomainfrom
dev/bjordiscollaku
Feb 17, 2026
Merged

build-rootfs: Transition from custom GRUB config to upstream standard (update-grub)#108
bjordiscollaku merged 10 commits intomainfrom
dev/bjordiscollaku

Conversation

@bjordiscollaku
Copy link
Contributor

@bjordiscollaku bjordiscollaku commented Feb 5, 2026

Overview

This PR completely refactors the bootloader configuration logic in build-rootfs.sh. It abandons the manual, static construction of grub.cfg in favor of the standard Debian update-grub mechanism. This ensures robust compatibility with upstream kernel packages and automated menu generation.

The PR encompasses the definition of critical boot defaults (Step 7.5), the isolation of host vs. target environment variables, and a post-processing stage to ensure the generated image remains generic and portable despite being built on a host machine.

Detailed Changes

1. Configuration & Defaults (Step 7.5)

  • New Configuration Step: Introduced Step 7.5 to programmatically generate /etc/default/grub prior to the chroot phase.
  • Boot Parameters: Defined GRUB_CMDLINE_LINUX with critical hardware settings (earlycon, console=ttyMSM0, efi=noruntime) ensuring these apply to both Normal and Recovery boot modes.
  • Root Filesystem Addressing: Set GRUB_DISABLE_LINUX_UUID=true and enforced root=LABEL=system. This prepares the config for generic booting, ensuring the kernel does not rely on specific partition block UUIDs.
  • Host Isolation: Implemented the quoted heredoc syntax (cat <<'EOF') for writing the config file. This ensures that backticked commands (like `lsb_release`) are written literally to the file for execution inside the target, preventing accidental expansion/execution by the build host shell.
  • Directory Safety: Added explicit mkdir -p commands for /boot/grub and /etc/default. This resolves race conditions where update-grub (triggered by package installs) would fail due to missing directory structures.

2. Standardization & Lifecycle (Step 8)

  • Adoption of update-grub: Replaced the brittle manual cat > grub.cfg logic with the standard update-grub toolchain.
  • Dependency Management: Added grub-common, grub2-common, and lsb-release to the debootstrap seed list to ensure generation tools are present in the baseline rootfs.
  • Redundancy Cleanup: Removed the manual kernel version parsing logic (kernel_ver) and the explicit update-grub call in Step 8. We now delegate configuration generation entirely to the kernel package's postinst hook, which executes update-grub immediately after installation. This aligns the build process with standard Debian package lifecycle events.

3. Post-Processing & Sanitization

Because update-grub runs inside a chroot on the build host, it inherently "leaks" host-specific details. We added a sanitization layer to scrub these artifacts:

  • Enforce Label-Based Search: Implemented sed logic to strip host-detected UUID searches (search --fs-uuid ...) and replace them with search --label system. This ensures the bootloader can locate the kernel on any storage medium (SD, eMMC, NVMe).
  • Remove Host Device Paths: Added logic to scrub host-specific root paths (e.g., /dev/nvme0n1p1) that grub-probe incorrectly detects from the build server, strictly enforcing the generic root=LABEL=system argument.
  • Relative Symlink: Updated the legacy /boot/grub.cfg compatibility link to use a relative path (grub/grub.cfg) instead of an absolute one, ensuring correct file resolution both during runtime and when the rootfs is mounted on a host machine for inspection.

…rkflow

This commit replaces the static, manual generation of /boot/grub.cfg with the standard Debian update-grub mechanism. This change ensures that the bootloader configuration is automatically synchronized with installed kernels and adheres to standard distribution practices.

Key Changes:
* Standard Implementation: Replaced the manual file write of /boot/grub.cfg with the generation of /etc/default/grub followed by execution of update-grub.
* Dependency Management: Added grub-common and grub2-common to the debootstrap package list to provide the necessary tools (grub-mkconfig, grub-probe) inside the chroot.
* Correct Recovery Mode: Configured critical hardware arguments (console, root label, EFI) in GRUB_CMDLINE_LINUX. Unlike the previous single-entry manual config, this ensures that auto-generated "Recovery Mode" entries retain necessary drivers and serial console output.
* Filesystem Addressing: Set GRUB_DISABLE_LINUX_UUID=true to force root=LABEL=system, maintaining portability for the generic rootfs image.

Signed-off-by: Bjordis Collaku <bcollaku@qti.qualcomm.com>
Signed-off-by: Bjordis Collaku <bcollaku@qti.qualcomm.com>
This commit refactors the GRUB configuration logic by moving the generation of /etc/default/grub from the chroot environment to the host context.

Generating the configuration file inside the nested chroot command string caused shell quoting conflicts and prevented the reliable writing of the config file (specifically affecting heredoc termination and variable escaping).

Key Changes:
* Host-Side Generation: Extracted the config writing logic to a new pre-chroot step (Step 7.5). It now uses a quoted heredoc (cat <<'EOF') to write the file directly into the mounted rootfs.
* Stability: This ensures that variables and backticks (e.g., for `lsb_release`) are written literally to the file and not interpreted by the host shell.
* Clean Execution: The chroot step (Step 8) is now simplified to strictly run `update-grub`, relying on the correctly pre-seeded configuration file.

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

This commit resolves a build failure where `grub-mkconfig` (invoked by `update-grub`) exited with "Directory nonexistent".  The error occurred because the `/boot/grub` directory structure was not present in the rootfs prior to the execution of `update-grub`. The transition to host-side configuration requires explicitly creating these artifact directories, as they are not guaranteed to be populated by `debootstrap` at this stage.  Key Changes: * Explicitly added `mkdir -p` for `/boot/grub` and `/etc/default` in Step 7.5. * Ensures the destination directory exists before the host writes the configuration file or the chroot attempts to generate the boot menu.
This commit implements a post-processing stage for the GRUB configuration to resolve "host contamination" issues.

When `update-grub` runs inside the chroot, it probes the build host's hardware, incorrectly baking the host's filesystem UUIDs and block device paths (e.g., `/dev/nvme0n1p1`) into the target image. This patch scrubs these host-specific artifacts to ensure the generated rootfs is generic and portable.

Key Changes:
- Enforce Label-Based Search: Replaces the standard `search --fs-uuid` command with `search --label system` via sed. This ensures the bootloader can locate the kernel partition regardless of the underlying storage UUID.
- Sanitize Kernel Command Line: Strips the host-detected `root=/dev/xxx` parameter. This prevents conflicts with the intended `root=LABEL=system` argument defined in `/etc/default/grub`.
- Relative Compatibility Symlink: Updates the legacy `/boot/grub.cfg` symlink to use a relative path (`grub/grub.cfg`) instead of an absolute one, ensuring correct resolution on both the target and the build host.

Signed-off-by: Bjordis Collaku <bcollaku@qti.qualcomm.com>
Remove the manual kernel version extraction logic (kernel_ver calculation) from Step 8.

This variable was previously required for manually constructing grub.cfg menu entries. With the transition to the standard `update-grub` workflow, the kernel version is automatically detected by scanning /boot, making this explicit parsing redundant and unused.

Signed-off-by: Bjordis Collaku <bcollaku@qti.qualcomm.com>
This commit removes the explicit `update-grub` call from the build sequence, delegating bootloader configuration entirely to the kernel package's installation hooks.

The generation of `grub.cfg` is guaranteed by the kernel package's `postinst` script, which executes `update-grub` immediately following `update-initramfs`. Eliminating the secondary invocation in the build script reduces redundancy while maintaining reliance on standard package management lifecycle events to populate the bootloader configuration prior to image finalization.

Signed-off-by: Bjordis Collaku <bcollaku@qti.qualcomm.com>
@bjordiscollaku bjordiscollaku changed the title build-rootfs: Replace manual GRUB config with standard update-grub workflow build-rootfs: Transition from custom GRUB config to upstream standard (update-grub) Feb 5, 2026
This commit configures the GRUB bootloader to simultaneously output its menu to both the serial port and the attached display.

By default, `update-grub` prioritizes graphical terminals (gfxterm) or auto-detects a single output, often rendering the menu invisible on headless serial connections or embedded displays without full driver support. This change explicitly sets `GRUB_TERMINAL="serial console"`, ensuring the menu is accessible via the serial debug port (115200 baud) and the native text-mode console on HDMI/DP.

Signed-off-by: Bjordis Collaku <bcollaku@qti.qualcomm.com>
This commit introduces a platform-specific bootloader configuration stage for ARM64 Debian targets. 

Since the standard GRUB generation logic in the target environment does not  natively support automated DTB loading via variables, this change implements  a surgical post-processing step at the end of the chroot phase:

1. Dynamic Resolution: Uses a multi-path search (/usr/lib and /lib/firmware) to locate the 'glymur-crd.dtb' platform description.
2. Visibility: Establishes a stable '/boot/dtb' symlink to ensure the bootloader can access hardware descriptions within its filesystem scope.
3. Injection: Directly patches the generated 'grub.cfg' to include the 'devicetree' directive following the 'initrd' load sequence.

This approach ensures hardware-specific initialization is successful on first boot while maintaining compatibility with the host's existing distribution-specific GRUB scripts.

Signed-off-by: Bjordis Collaku <bcollaku@qti.qualcomm.com>
echo \"[INFO][CHROOT] Platform DTB resolved: \$DTB_PATH\"

# Ensure DTB is accessible in the bootloader's filesystem scope
ln -sf \"\$DTB_PATH\" /boot/dtb
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Not sure why first comment is not showing up, sorry for the dup)

/boot/dtb can only exist once; GRUB config generation will generate entries for each kernel, you want to tap into that to generate entries for each kernel with a devicetree line from the right kernel.

echo '[INFO][CHROOT] Debian target detected. Configuring platform Device Tree...'

# Locate the platform Device Tree Blob (DTB) in standard library or firmware paths
DTB_PATH=\$(find /usr/lib /lib/firmware -name \"glymur-crd.dtb\" -print -quit)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why hardcode glymur-crd.dtb? How will this image work on other systems?

Copy link
Contributor Author

@bjordiscollaku bjordiscollaku Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Conditional devicetree injection is to temporarily support glymur enablement in CI/nightly, up until multi-dtb (combined or FIT) dtb.bin is supported on glymur platform, just like in Hamoa IoT, in which case conditional injection of this hardcoded devicetree path in grub menuentry completely vanishes, alongside the "ln -sf $DTB_PATH /boot/dtb".

Result is then devicetree agnostic generalized grub config generated straight from update-grub (which runs as part of postinst script of the kernel deb package).

@bjordiscollaku bjordiscollaku merged commit 6fee171 into main Feb 17, 2026
5 of 6 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.

3 participants

Comments