From e8e336176445dd9728d232bcc2b2d4582f1de669 Mon Sep 17 00:00:00 2001 From: Alex Zenla Date: Sat, 26 Sep 2026 20:12:42 -0700 Subject: [PATCH] fix(menu): show the boot menu when it is forced --force-menu and LoaderConfigTimeout=menu-force only stopped --boot and LoaderEntryOneShot from skipping the menu. The timeout was left as is, so with a zero timeout the menu booted the default entry without being drawn, and menu-force counted down and booted the default entry, where the Boot Loader Interface says it should wait for the user. Menus now take an optional timeout. menu-force waits for the user, and --force-menu keeps the configured timeout unless it is zero, in which case it waits for the user too. --- crates/boot/src/main.rs | 14 ++++++++++++-- crates/boot/src/menu.rs | 8 +++++--- crates/boot/src/menu/basic.rs | 8 ++++---- crates/boot/src/menu/simple.rs | 11 ++++++----- 4 files changed, 27 insertions(+), 14 deletions(-) diff --git a/crates/boot/src/main.rs b/crates/boot/src/main.rs index 723bcf4..9f3328e 100644 --- a/crates/boot/src/main.rs +++ b/crates/boot/src/main.rs @@ -291,11 +291,15 @@ fn run() -> Result<()> { .menu_timeout .unwrap_or(config.options.menu_timeout); + // Whether the boot menu should wait for the user, instead of counting down. + let mut wait_for_user = false; + // Apply bootloader interface timeout settings. match bootloader_interface_timeout { BootloaderInterfaceTimeout::MenuForce => { - // Force the boot menu. + // Force the boot menu, and wait for the user to choose an entry. force_boot_menu = true; + wait_for_user = true; } BootloaderInterfaceTimeout::MenuHidden | BootloaderInterfaceTimeout::MenuDisabled => { @@ -341,7 +345,13 @@ fn run() -> Result<()> { } // Convert the menu timeout to a duration. - let menu_timeout = Duration::from_secs(menu_timeout); + // A zero timeout boots without showing the menu, so a forced menu with a zero timeout + // waits for the user instead. + let menu_timeout = if wait_for_user || (force_boot_menu && menu_timeout == 0) { + None + } else { + Some(Duration::from_secs(menu_timeout)) + }; // Determine the menu style based on the options or configuration. // We prefer the options over the configuration to allow for overriding. diff --git a/crates/boot/src/menu.rs b/crates/boot/src/menu.rs index 3494a96..729fe59 100644 --- a/crates/boot/src/menu.rs +++ b/crates/boot/src/menu.rs @@ -26,10 +26,11 @@ const MAX_TIMER_DURATION: Duration = Duration::from_secs(u32::MAX as u64); /// A boot menu that can be shown to select an entry to boot. pub trait BootMenu { /// Select an entry from `entries` to boot. If no entry is chosen before `timeout` passes, - /// the default entry is selected. + /// the default entry is selected. If `timeout` is zero, the default entry is selected + /// without showing the menu. If `timeout` is [None], the menu waits for the user. fn select<'a>( &self, - timeout: Duration, + timeout: Option, entries: &'a [BootableEntry], ) -> Result<&'a BootableEntry>; } @@ -108,9 +109,10 @@ pub fn read_key(input: &mut Input, timeout: Option) -> Result( timer: &'live PlatformTimer, - timeout: Duration, + timeout: Option, style: MenuStyle, entries: &'live [BootableEntry], ) -> Result<&'live BootableEntry> { diff --git a/crates/boot/src/menu/basic.rs b/crates/boot/src/menu/basic.rs index 2ebc276..2b110ab 100644 --- a/crates/boot/src/menu/basic.rs +++ b/crates/boot/src/menu/basic.rs @@ -76,16 +76,16 @@ fn default_entry(entries: &[BootableEntry]) -> Result<&BootableEntry> { /// Selects an entry from the list of entries using the boot menu. fn select_with_input<'a>( input: &mut Input, - timeout: Duration, + timeout: Option, entries: &'a [BootableEntry], ) -> Result<&'a BootableEntry> { // If the timeout is zero, boot the default entry without showing the menu. - if timeout.is_zero() { + if timeout.is_some_and(|timeout| timeout.is_zero()) { return default_entry(entries); } // The time to wait for a key, or None once a key stops the countdown. - let mut countdown = Some(timeout); + let mut countdown = timeout; // The entry number typed so far, while more digits could still select another entry. let mut typed: Option = None; @@ -176,7 +176,7 @@ impl BootMenu for BasicMenu { /// within the context of the standard input device. fn select<'a>( &self, - timeout: Duration, + timeout: Option, entries: &'a [BootableEntry], ) -> Result<&'a BootableEntry> { uefi::system::with_stdin(move |input| select_with_input(input, timeout, entries)) diff --git a/crates/boot/src/menu/simple.rs b/crates/boot/src/menu/simple.rs index 9d63095..6f735f9 100644 --- a/crates/boot/src/menu/simple.rs +++ b/crates/boot/src/menu/simple.rs @@ -163,11 +163,12 @@ fn render( /// Run the menu until an entry is chosen, returning the index of that entry. /// The `selected` entry is booted if no key is pressed before `timeout` passes. +/// Without a timeout, the menu waits for the user. fn run( input: &mut Input, output: &mut Output, layout: &Layout, - timeout: Duration, + timeout: Option, entries: &[BootableEntry], mut selected: usize, ) -> Result { @@ -175,7 +176,7 @@ fn run( // The first visible entry, which changes when scrolling through a long list. let mut offset = 0; // The time left before booting, or None once a key stops the countdown. - let mut remaining = Some(timeout); + let mut remaining = timeout; loop { // Scroll so that the selected entry is visible. @@ -230,7 +231,7 @@ fn run( fn select_with_console<'a>( input: &mut Input, output: &mut Output, - timeout: Duration, + timeout: Option, entries: &'a [BootableEntry], ) -> Result<&'a BootableEntry> { let default = entries @@ -239,7 +240,7 @@ fn select_with_console<'a>( .context("no default entry available")?; // If the timeout is zero, boot the default entry without showing the menu. - if timeout.is_zero() { + if timeout.is_some_and(|timeout| timeout.is_zero()) { return Ok(&entries[default]); } @@ -269,7 +270,7 @@ impl BootMenu for SimpleMenu { /// within the context of the standard input and output devices. fn select<'a>( &self, - timeout: Duration, + timeout: Option, entries: &'a [BootableEntry], ) -> Result<&'a BootableEntry> { uefi::system::with_stdin(|input| {