From e12e5dd7cf7891d68deea92fc71f1dc4f118dde0 Mon Sep 17 00:00:00 2001 From: Alex Zenla Date: Sat, 3 Oct 2026 17:48:07 -0700 Subject: [PATCH] fix(menu): open the pointers shared so the graphical menu doesn't hang An exclusive open makes the firmware disconnect the drivers that use the protocol, and on some firmware that call never returns. Prefer the pointer on the console input handle, which belongs to the console splitter. --- crates/boot/src/menu/graphical.rs | 47 +++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/crates/boot/src/menu/graphical.rs b/crates/boot/src/menu/graphical.rs index 88d63cc..b169c2c 100644 --- a/crates/boot/src/menu/graphical.rs +++ b/crates/boot/src/menu/graphical.rs @@ -8,11 +8,12 @@ use alloc::vec::Vec; use anyhow::{Context, Result}; use core::time::Duration; use eficore::framebuffer::Framebuffer; -use uefi::Event; -use uefi::boot::ScopedProtocol; +use uefi::boot::{OpenProtocolAttributes, OpenProtocolParams, ScopedProtocol}; +use uefi::proto::ProtocolPointer; use uefi::proto::console::gop::{BltPixel, GraphicsOutput}; use uefi::proto::console::pointer::{AbsolutePointer, Pointer}; use uefi::proto::console::text::{Input, Key, ScanCode}; +use uefi::{Event, Handle}; /// How often the countdown and the logo are updated. const TICK: Duration = Duration::from_millis(125); @@ -91,15 +92,45 @@ struct Mouse { absolute: Option>, } +/// Open the protocol `P` on `handle` without taking it from the firmware. +/// An exclusive open makes the firmware disconnect every driver that is using the protocol, +/// and on some firmware that never returns. +fn open_shared(handle: Handle) -> uefi::Result> { + // SAFETY: The protocols opened this way are only used from this thread, and the firmware + // keeps them installed for as long as the menu is open. + unsafe { + uefi::boot::open_protocol::

( + OpenProtocolParams { + handle, + agent: uefi::boot::image_handle(), + controller: None, + }, + OpenProtocolAttributes::GetProtocol, + ) + } +} + +/// Open the pointer protocol `P`, preferring the one on the console input handle. +/// That one belongs to the console splitter, which combines every device and is never removed. +/// Otherwise, the first device that has the protocol is used. +fn open_pointer() -> Option> { + // SAFETY: The handle in the system table is either null or a valid handle. + let console = uefi::table::system_table_raw() + .and_then(|table| unsafe { Handle::from_ptr(table.as_ref().stdin_handle) }); + console + .and_then(|handle| open_shared::

(handle).ok()) + .or_else(|| { + uefi::boot::get_handle_for_protocol::

() + .and_then(open_shared::

) + .ok() + }) +} + impl Mouse { /// Open the pointing devices, if the firmware has any. fn open() -> Option { - let relative = uefi::boot::get_handle_for_protocol::() - .and_then(uefi::boot::open_protocol_exclusive::) - .ok(); - let absolute = uefi::boot::get_handle_for_protocol::() - .and_then(uefi::boot::open_protocol_exclusive::) - .ok(); + let relative = open_pointer::(); + let absolute = open_pointer::(); (relative.is_some() || absolute.is_some()).then_some(Self { relative, absolute }) }