From e9779d9e3edc0cbadd183be17291715afcc4ca33 Mon Sep 17 00:00:00 2001 From: Oliver Smith-Denny Date: Tue, 19 May 2026 09:18:32 -0700 Subject: [PATCH] pi_dispatcher: Add UI Names to Prints Currently when various states occur, such as drivers not dispatched, only the GUID of the driver is printed. This is useful, but also it is nicer to have the UI name printed so the GUID does not have to be cross-referenced. This parses the UI name from the FV, if present, and also prints it. --- patina_dxe_core/src/pi_dispatcher.rs | 118 ++++++++++++++++++++++++++- 1 file changed, 114 insertions(+), 4 deletions(-) diff --git a/patina_dxe_core/src/pi_dispatcher.rs b/patina_dxe_core/src/pi_dispatcher.rs index 352f6841c..9163c72f7 100644 --- a/patina_dxe_core/src/pi_dispatcher.rs +++ b/patina_dxe_core/src/pi_dispatcher.rs @@ -17,6 +17,7 @@ mod section_decompress; use alloc::{ boxed::Box, collections::{BTreeMap, BTreeSet}, + string::{String, ToString}, vec::Vec, }; use core::{cmp::Ordering, ffi::c_void}; @@ -105,7 +106,11 @@ impl PiDispatcher

{ /// Displays drivers that were discovered but not dispatched. pub fn display_discovered_not_dispatched(&self) { for driver in &self.dispatcher_context.lock().pending_drivers { - log::warn!("Driver {:?} found but not dispatched.", guid_fmt!(driver.file_name)); + log::warn!( + "Driver {} ({:?}) found but not dispatched.", + driver.name.as_deref().unwrap_or("Unnamed"), + guid_fmt!(driver.file_name) + ); } } @@ -231,7 +236,7 @@ impl PiDispatcher

{ let driver_candidates: Vec<_> = dispatcher.pending_drivers.drain(..).collect(); let mut scheduled_driver_candidates = Vec::new(); for mut candidate in driver_candidates { - log::debug!(target: "patina_internal_depex", "Evaluating depex for candidate: {:?}", guid_fmt!(candidate.file_name)); + log::debug!(target: "patina_internal_depex", "Evaluating depex for candidate: {} ({:?})", candidate.name.as_deref().unwrap_or("Unnamed"), guid_fmt!(candidate.file_name)); let depex_satisfied = match candidate.depex { Some(ref mut depex) => depex.eval(&PROTOCOL_DB.registered_protocols()), None => dispatcher.arch_protocols_available, @@ -299,7 +304,8 @@ impl PiDispatcher

{ } efi::Status::SECURITY_VIOLATION => { log::info!( - "Deferring driver: {:?} due to security status: {:x?}", + "Deferring driver: {} ({:?}) due to security status: {:x?}", + driver.name.as_deref().unwrap_or("Unnamed"), guid_fmt!(driver.file_name), efi::Status::SECURITY_VIOLATION ); @@ -307,7 +313,8 @@ impl PiDispatcher

{ } unexpected_status => { log::info!( - "Dropping driver: {:?} due to security status: {:x?}", + "Dropping driver: {} ({:?}) due to security status: {:x?}", + driver.name.as_deref().unwrap_or("Unnamed"), guid_fmt!(driver.file_name), unexpected_status ); @@ -504,6 +511,7 @@ struct PendingDriver { firmware_volume_handle: efi::Handle, device_path: *mut efi::protocols::device_path::Protocol, file_name: efi::Guid, + name: Option, depex: Option, pe32: Section, image_handle: Option, @@ -662,6 +670,16 @@ impl DispatcherContext { .transpose()? .map(Depex::from); + let name = sections + .iter() + .find(|x| x.section_type() == Some(ffs::section::Type::UserInterface)) + .and_then(|x| x.try_content_as_slice().ok()) + .map(|data| { + let chars: Vec = + data.chunks_exact(2).map(|c| u16::from_le_bytes([c[0], c[1]])).collect(); + String::from_utf16_lossy(&chars).trim_end_matches('\0').to_string() + }); + if let Some(pe32_section) = sections.into_iter().find(|x| x.section_type() == Some(ffs::section::Type::Pe32)) { @@ -717,6 +735,7 @@ impl DispatcherContext { self.pending_drivers.push(PendingDriver { file_name, + name, firmware_volume_handle: handle, pe32: pe32_section, device_path: full_device_path_for_file, @@ -1730,6 +1749,97 @@ mod tests { }); } + #[test] + fn test_display_discovered_not_dispatched_with_named_driver() { + with_locked_state(|| { + use patina_ffs::section::SectionHeader; + + static CORE: MockCore = MockCore::new(NullSectionExtractor::new()); + CORE.override_instance(); + + // Build a minimal PE32 section (content doesn't matter for this test). + let pe32_header = SectionHeader::Standard(ffs::section::raw_type::PE32, 4); + let pe32_section = Section::new_from_header_with_data(pe32_header, vec![0u8; 4]).expect("PE32 section"); + + let test_guid = + efi::Guid::from_fields(0xAABBCCDD, 0x1122, 0x3344, 0x55, 0x66, &[0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC]); + + // Driver with a UI name. + CORE.pi_dispatcher.dispatcher_context.lock().pending_drivers.push(PendingDriver { + firmware_volume_handle: std::ptr::null_mut(), + device_path: std::ptr::null_mut(), + file_name: test_guid, + name: Some(String::from("TestDriver")), + depex: None, + pe32: pe32_section, + image_handle: None, + security_status: efi::Status::NOT_READY, + }); + + // Build another PE32 section for the unnamed driver. + let pe32_header2 = SectionHeader::Standard(ffs::section::raw_type::PE32, 4); + let pe32_section2 = Section::new_from_header_with_data(pe32_header2, vec![0u8; 4]).expect("PE32 section"); + + // Driver without a UI name. + CORE.pi_dispatcher.dispatcher_context.lock().pending_drivers.push(PendingDriver { + firmware_volume_handle: std::ptr::null_mut(), + device_path: std::ptr::null_mut(), + file_name: test_guid, + name: None, + depex: None, + pe32: pe32_section2, + image_handle: None, + security_status: efi::Status::NOT_READY, + }); + + assert_eq!(CORE.pi_dispatcher.dispatcher_context.lock().pending_drivers.len(), 2); + assert_eq!( + CORE.pi_dispatcher.dispatcher_context.lock().pending_drivers[0].name.as_deref(), + Some("TestDriver") + ); + assert!(CORE.pi_dispatcher.dispatcher_context.lock().pending_drivers[1].name.is_none()); + + // Verify display doesn't panic for both named and unnamed drivers. + CORE.pi_dispatcher.display_discovered_not_dispatched(); + }); + } + + #[test] + fn test_display_discovered_not_dispatched_parses_ui_name_from_fv() { + let mut file = File::open(test_collateral!("DXEFV.Fv")).unwrap(); + let mut fv: Vec = Vec::new(); + file.read_to_end(&mut fv).expect("failed to read test file"); + let fv = fv.into_boxed_slice(); + let fv_raw = Box::into_raw(fv); + + with_locked_state(|| { + static CORE: MockCore = MockCore::new(NullSectionExtractor::new()); + CORE.override_instance(); + + // SAFETY: fv_raw points to a valid boxed slice that outlives this closure. + let handle = unsafe { + CORE.pi_dispatcher + .fv_data + .lock() + .install_firmware_volume(fv_raw.expose_provenance() as u64, None) + .unwrap() + }; + + CORE.pi_dispatcher.add_fv_handles(vec![handle]).expect("Failed to add FV handle"); + + let has_named = + CORE.pi_dispatcher.dispatcher_context.lock().pending_drivers.iter().any(|d| d.name.is_some()); + // At least one driver in the test FV should have a UI name section. + assert!(has_named, "Expected at least one driver with a parsed UI name"); + + // Verify display doesn't panic with a mix of named/unnamed drivers. + CORE.pi_dispatcher.display_discovered_not_dispatched(); + }); + + // SAFETY: fv_raw was created from Box::into_raw and is dropped only once here. + let _dropped_fv = unsafe { Box::from_raw(fv_raw) }; + } + #[test] fn test_dispatch_with_corrupted_fv_section() { set_logger();