diff --git a/crates/boot/src/actions/chainload.rs b/crates/boot/src/actions/chainload.rs index 2f00106..f147e13 100644 --- a/crates/boot/src/actions/chainload.rs +++ b/crates/boot/src/actions/chainload.rs @@ -1,4 +1,5 @@ use crate::context::SproutContext; +use crate::generators::bls::BLS_INITRD_SLOTS; use crate::phases::before_handoff; use alloc::boxed::Box; use alloc::format; @@ -160,6 +161,24 @@ pub fn chainload(context: Rc, configuration: &ChainloadConfigurat .chain(configuration.linux_initrd_chain.iter()) .map(|item| context.stamp(item)); + // A BLS entry can have more initrds than the chain lists. Those would be dropped without a + // word, and then the kernel may not find its root, so say so. + let chain_length = configuration.linux_initrd_chain.len(); + if chain_length > 0 { + for slot in chain_length..BLS_INITRD_SLOTS { + let placeholder = format!("$initrd-{}", slot); + let value = context.stamp(&placeholder); + if !value.is_empty() && value != placeholder { + warn!( + "the entry has an initrd for slot {}, but linux-initrd-chain only lists {}, \ + so it is not loaded", + slot, chain_length + ); + break; + } + } + } + // Read each initrd and concatenate the contents in order. // Paths that are empty after stamping are skipped. let mut initrd: Option> = None; diff --git a/crates/boot/src/autoconfigure.rs b/crates/boot/src/autoconfigure.rs index 68b48e3..9abab2d 100644 --- a/crates/boot/src/autoconfigure.rs +++ b/crates/boot/src/autoconfigure.rs @@ -140,12 +140,18 @@ pub fn autoconfigure( } // In strict mode, one generator reads both partitions, with the root of Sprout's partition. - if strict - && strict_found - && let Some(root) = own_root - && let Err(error) = bls::add_generator(config, &root, true) - { - warn!("unable to add the bls generator: {:#}", error); + if strict && strict_found { + match own_root { + Some(root) => { + if let Err(error) = bls::add_generator(config, &root, true) { + warn!("unable to add the bls generator: {:#}", error); + } + } + None => warn!( + "found BLS entries, but not the partition that Sprout was loaded from, \ + so none are used in strict mode" + ), + } } Ok(()) diff --git a/crates/boot/src/boot_counter.rs b/crates/boot/src/boot_counter.rs index 3d8f646..bbb232a 100644 --- a/crates/boot/src/boot_counter.rs +++ b/crates/boot/src/boot_counter.rs @@ -29,6 +29,9 @@ pub struct BootCounterTarget { pub file_name: String, /// The extension of the file name, such as `.conf`, in its original case. pub extension: String, + /// Whether booting the entry uses up a try. An entry whose tries are not used up still + /// has them, so it can be bad. + pub counting: bool, } impl BootCounterTarget { diff --git a/crates/boot/src/generators/bls.rs b/crates/boot/src/generators/bls.rs index ec08763..979b6db 100644 --- a/crates/boot/src/generators/bls.rs +++ b/crates/boot/src/generators/bls.rs @@ -8,6 +8,7 @@ use alloc::{ vec::Vec, }; use anyhow::{Context, Result}; +use core::sync::atomic::{AtomicBool, Ordering as AtomicOrdering}; use core::{cmp::Ordering, str::FromStr}; use edera_sprout_bls::{ BlsEntry, BootCounter, is_reserved_entry_name, profile_id_suffix, sort_bls, strip_extension, @@ -37,6 +38,10 @@ const ARCHITECTURE: &str = "x64"; #[cfg(target_arch = "aarch64")] const ARCHITECTURE: &str = "aa64"; +/// Whether the extended boot loader partition was added by a generator in strict mode, which +/// does it once, so that more than one generator does not list its entries twice. +static STRICT_XBOOTLDR_ADDED: AtomicBool = AtomicBool::new(false); + /// The number of initrd slots set on each BLS entry. /// Entries set `initrd-0` through `initrd-31`, and slots without an initrd are empty. pub const BLS_INITRD_SLOTS: usize = 32; @@ -311,9 +316,7 @@ fn generate_type1( // systemd-boot only counts the tries of the entries it boots as a kernel or as a unified // kernel image, and not those of a plain EFI program. let counted = !(strict && entry.linux.is_none() && entry.uki.is_none()); - if let Some(counter) = boot_counter - && counted - { + if let Some(counter) = boot_counter { boot.set_boot_counter(BootCounterTarget { counter, filesystem: bls_resolved.filesystem_handle, @@ -321,6 +324,7 @@ fn generate_type1( id: name.clone(), file_name, extension, + counting: counted, }); } @@ -446,6 +450,7 @@ fn generate_type2( id: id.clone(), file_name: file_name.clone(), extension: extension.clone(), + counting: true, }); } found.push((entry, boot)); @@ -532,7 +537,9 @@ pub fn generate(context: Rc, bls: &BlsConfiguration) -> Result { let xbootldr_uki = uki_path.as_ref().map(|_| format!("{}\\EFI\\Linux", root)); diff --git a/crates/boot/src/main.rs b/crates/boot/src/main.rs index 333035e..8184c3e 100644 --- a/crates/boot/src/main.rs +++ b/crates/boot/src/main.rs @@ -372,7 +372,7 @@ fn run(reboot_on_error: &mut bool) -> Result<()> { ); // Acquire the entry that was saved by the previous boot. - let last_booted_entry = advisory( + let mut last_booted_entry = advisory( BootloaderInterface::get_last_booted_entry() .context("unable to get last booted entry from bootloader interface"), ); @@ -692,12 +692,14 @@ fn run(reboot_on_error: &mut bool) -> Result<()> { BootloaderInterface::set_last_booted_entry(&id) .context("unable to save last booted entry in bootloader interface"), ); + last_booted_entry = Some(id.clone()); } } else if last_booted_entry.is_some() { advisory( BootloaderInterface::remove_last_booted_entry() .context("unable to remove last booted entry in bootloader interface"), ); + last_booted_entry = None; } } @@ -710,7 +712,9 @@ fn run(reboot_on_error: &mut bool) -> Result<()> { // still be marked as good. // The tries that were left before this boot used one up, if it did. let mut consumed_tries_left = None; - if let Some(target) = entry.boot_counter() { + if let Some(target) = entry.boot_counter() + && target.counting + { match target.consume() { Ok(path) => { info!("updated boot counter of entry {}: {}", entry.name(), path);