preserve usb as top efi boot option

This commit is contained in:
Aiden McClelland
2026-03-05 15:04:06 -07:00
parent 5a0cd302de
commit d31f762d5a
2 changed files with 102 additions and 2 deletions

View File

@@ -62,12 +62,27 @@ fi
chroot /media/startos/next bash -e << "EOF" chroot /media/startos/next bash -e << "EOF"
if [ -f /boot/grub/grub.cfg ]; then if [ -f /boot/grub/grub.cfg ]; then
grub-install --no-nvram /dev/$(eval $(lsblk -o MOUNTPOINT,PKNAME -P | grep 'MOUNTPOINT="/media/startos/root"') && echo $PKNAME) grub-install /dev/$(eval $(lsblk -o MOUNTPOINT,PKNAME -P | grep 'MOUNTPOINT="/media/startos/root"') && echo $PKNAME)
update-grub update-grub
fi fi
EOF EOF
# Promote the USB installer boot entry back to first in EFI boot order.
# The entry number was saved during initial OS install.
if [ -d /sys/firmware/efi ] && [ -f /media/startos/config/efi-installer-entry ]; then
USB_ENTRY=$(cat /media/startos/config/efi-installer-entry)
if [ -n "$USB_ENTRY" ]; then
CURRENT_ORDER=$(efibootmgr | grep BootOrder | sed 's/BootOrder: //')
OTHER_ENTRIES=$(echo "$CURRENT_ORDER" | tr ',' '\n' | grep -v "$USB_ENTRY" | tr '\n' ',' | sed 's/,$//')
if [ -n "$OTHER_ENTRIES" ]; then
efibootmgr -o "$USB_ENTRY,$OTHER_ENTRIES"
else
efibootmgr -o "$USB_ENTRY"
fi
fi
fi
sync sync
umount -Rl /media/startos/next umount -Rl /media/startos/next

View File

@@ -27,6 +27,63 @@ use crate::util::serde::IoFormat;
mod gpt; mod gpt;
mod mbr; mod mbr;
/// Get the EFI BootCurrent entry number (the entry firmware used to boot).
/// Returns None on non-EFI systems or if BootCurrent is not set.
async fn get_efi_boot_current() -> Result<Option<String>, Error> {
let efi_output = String::from_utf8(
Command::new("efibootmgr")
.invoke(ErrorKind::Grub)
.await?,
)
.map_err(|e| Error::new(eyre!("efibootmgr output not valid UTF-8: {e}"), ErrorKind::Grub))?;
Ok(efi_output
.lines()
.find(|line| line.starts_with("BootCurrent:"))
.and_then(|line| line.strip_prefix("BootCurrent:"))
.map(|s| s.trim().to_string()))
}
/// Promote a specific boot entry to first in the EFI boot order.
async fn promote_efi_entry(entry: &str) -> Result<(), Error> {
let efi_output = String::from_utf8(
Command::new("efibootmgr")
.invoke(ErrorKind::Grub)
.await?,
)
.map_err(|e| Error::new(eyre!("efibootmgr output not valid UTF-8: {e}"), ErrorKind::Grub))?;
let current_order = efi_output
.lines()
.find(|line| line.starts_with("BootOrder:"))
.and_then(|line| line.strip_prefix("BootOrder:"))
.map(|s| s.trim())
.unwrap_or("");
if current_order.is_empty() || current_order.starts_with(entry) {
return Ok(());
}
let other_entries: Vec<&str> = current_order
.split(',')
.filter(|e| e.trim() != entry)
.collect();
let new_order = if other_entries.is_empty() {
entry.to_string()
} else {
format!("{},{}", entry, other_entries.join(","))
};
Command::new("efibootmgr")
.arg("-o")
.arg(&new_order)
.invoke(ErrorKind::Grub)
.await?;
Ok(())
}
/// Probe a squashfs image to determine its target architecture /// Probe a squashfs image to determine its target architecture
async fn probe_squashfs_arch(squashfs_path: &Path) -> Result<InternedString, Error> { async fn probe_squashfs_arch(squashfs_path: &Path) -> Result<InternedString, Error> {
let output = String::from_utf8( let output = String::from_utf8(
@@ -359,7 +416,6 @@ pub async fn install_os_to(
"riscv64" => install.arg("--target=riscv64-efi"), "riscv64" => install.arg("--target=riscv64-efi"),
_ => &mut install, _ => &mut install,
}; };
install.arg("--no-nvram");
} }
install install
.arg(disk_path) .arg(disk_path)
@@ -429,6 +485,21 @@ pub async fn install_os(
}); });
let use_efi = tokio::fs::metadata("/sys/firmware/efi").await.is_ok(); let use_efi = tokio::fs::metadata("/sys/firmware/efi").await.is_ok();
// Save the boot entry we booted from (the USB installer) before grub-install
// overwrites the boot order.
let boot_current = if use_efi {
match get_efi_boot_current().await {
Ok(entry) => entry,
Err(e) => {
tracing::warn!("Failed to get EFI BootCurrent: {e}");
None
}
}
} else {
None
};
let InstallOsResult { part_info, rootfs } = install_os_to( let InstallOsResult { part_info, rootfs } = install_os_to(
"/run/live/medium/live/filesystem.squashfs", "/run/live/medium/live/filesystem.squashfs",
&disk.logicalname, &disk.logicalname,
@@ -440,6 +511,20 @@ pub async fn install_os(
) )
.await?; .await?;
// grub-install prepends its new entry to the EFI boot order, overriding the
// USB-first priority. Promote the USB entry (identified by BootCurrent from
// when we booted the installer) back to first, and persist the entry number
// so the upgrade script can do the same.
if let Some(ref entry) = boot_current {
if let Err(e) = promote_efi_entry(entry).await {
tracing::warn!("Failed to restore EFI boot order: {e}");
}
let efi_entry_path = rootfs.path().join("config/efi-installer-entry");
if let Err(e) = tokio::fs::write(&efi_entry_path, entry).await {
tracing::warn!("Failed to save EFI installer entry number: {e}");
}
}
ctx.config ctx.config
.mutate(|c| c.os_partitions = Some(part_info.clone())); .mutate(|c| c.os_partitions = Some(part_info.clone()));