From 27ef43954f656b12ef398e13cba44450b363bf65 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Fri, 2 Oct 2026 16:01:36 +0200 Subject: [PATCH] efi: Install grub efi modules needed for UKIs (in case they are not built in) We're running into issues with bootc with UKIs on aarch64, because the chain.mod file is not built in to the grub efi file. And anyway we don't know how the grub in the image is built, so just in case we always install fat and chain modules that are needed for UKI menu entries to boot. Signed-off-by: Alexander Larsson --- src/efi.rs | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/src/efi.rs b/src/efi.rs index f054ed93..b667e868 100644 --- a/src/efi.rs +++ b/src/efi.rs @@ -53,6 +53,15 @@ pub(crate) const SHIM: &str = "shimx64.efi"; #[cfg(target_arch = "riscv64")] pub(crate) const SHIM: &str = "shimriscv64.efi"; +#[cfg(target_arch = "aarch64")] +const GRUB_EFI_MODULE_DIR: &str = "arm64-efi"; + +#[cfg(target_arch = "x86_64")] +const GRUB_EFI_MODULE_DIR: &str = "x86_64-efi"; + +#[cfg(target_arch = "riscv64")] +const GRUB_EFI_MODULE_DIR: &str = "riscv64-efi"; + /// The mount path for uefi const EFIVARFS: &str = "/sys/firmware/efi/efivars"; @@ -63,6 +72,38 @@ const STUB_INFO_VAR_STR: &str = "StubInfo-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f"; /// The options of cp command for installation const OPTIONS: &[&str] = &["-rp", "--reflink=auto"]; +// GRUB needs FAT filesystem and chainloading support to be able to boot UKIs. +// Both may be packaged as external modules (not builtin), so copy them in to be safe. +const GRUB_EFI_MODULES: [&str; 2] = ["fat.mod", "chain.mod"]; + +/// Copy the external modules for booting UKIs to /boot when the image provides +/// them. A GRUB EFI binary may already contain these modules. +fn install_grub_efi_modules(source_root: &Dir, target_root: &Dir) -> Result<()> { + let target_dir = format!("boot/{}/{GRUB_EFI_MODULE_DIR}", grubconfigs::GRUB2DIR); + + for module in GRUB_EFI_MODULES { + let source = format!("usr/lib/grub/{GRUB_EFI_MODULE_DIR}/{module}"); + if !source_root.try_exists(&source)? { + continue; + } + + target_root.create_dir_all(&target_dir)?; + let target = target_root.open_dir(&target_dir)?; + target + .atomic_replace_with(module, |f| -> std::io::Result<()> { + let mut source = source_root.open(&source)?; + std::io::copy(&mut source, f)?; + f.get_ref() + .as_file() + .set_permissions(source.metadata()?.permissions())?; + Ok(()) + }) + .with_context(|| format!("Installing GRUB module {module}"))?; + } + + Ok(()) +} + /// Check if the given path is a mount point via statx(MOUNT_ROOT). fn is_mount_point(path: &Path) -> Result { use rustix::fs::{AtFlags, StatxAttributes, StatxFlags}; @@ -476,6 +517,9 @@ impl Component for Efi { drop(efidir); self.unmount().context("unmount after adopt")?; } + if get_bootloader()? == Bootloader::Grub { + install_grub_efi_modules(&rootcxt.sysroot, &rootcxt.sysroot)?; + } Ok(Some(InstalledContent { meta: updatemeta.clone(), filetree: Some(updatef), @@ -573,6 +617,11 @@ impl Component for Efi { } } } + if bootloader == Bootloader::Grub { + let dest_root = Dir::open_ambient_dir(dest_root, ambient_authority()) + .with_context(|| format!("opening destination root {dest_root}"))?; + install_grub_efi_modules(&src_dir, &dest_root)?; + } Ok(InstalledContent { meta, filetree: Some(ft), @@ -639,6 +688,9 @@ impl Component for Efi { drop(destdir); self.unmount().context("unmount after update")?; } + if bootloader == Bootloader::Grub { + install_grub_efi_modules(&rootcxt.sysroot, &rootcxt.sysroot)?; + } let adopted_from = None; Ok(InstalledContent {