From 5d1ed37dfce80d8a924cda37a5784f25a128f0f9 Mon Sep 17 00:00:00 2001 From: Nicholas Bishop Date: Fri, 24 Jan 2025 20:21:23 -0500 Subject: [PATCH] test-runner: Clean up device path tests Split the conversion tests out into separate functions. This is a bit more verbose, but makes it clearer exactly what is being tested. The conversion tests now use a hardcoded test `DevicePath`, so we no longer need to `cfg` the tests for different arches based on what devices QEMU creates. This also makes it clearer what effects `DisplayOnly` and `AllowShortcuts` can have on the text conversion. --- uefi-test-runner/src/proto/device_path.rs | 318 ++++++++++++++-------- 1 file changed, 207 insertions(+), 111 deletions(-) diff --git a/uefi-test-runner/src/proto/device_path.rs b/uefi-test-runner/src/proto/device_path.rs index 573db8d27..25e6b8357 100644 --- a/uefi-test-runner/src/proto/device_path.rs +++ b/uefi-test-runner/src/proto/device_path.rs @@ -1,123 +1,219 @@ -use alloc::string::ToString; +use alloc::boxed::Box; use alloc::vec::Vec; -use uefi::boot; -use uefi::proto::device_path::text::*; -use uefi::proto::device_path::{DevicePath, LoadedImageDevicePath}; +use uefi::proto::device_path::build::{self, DevicePathBuilder}; +use uefi::proto::device_path::text::{ + AllowShortcuts, DevicePathFromText, DevicePathToText, DisplayOnly, +}; +use uefi::proto::device_path::{messaging, DevicePath, DevicePathNode, LoadedImageDevicePath}; use uefi::proto::loaded_image::LoadedImage; use uefi::proto::media::disk::DiskIo; +use uefi::{boot, cstr16}; pub fn test() { - info!("Running device path protocol test"); + info!("Running device path tests"); - // test 1/2: test low-level API by directly opening all protocols + test_convert_device_path_to_text(); + test_device_path_to_string(); + + test_convert_device_node_to_text(); + test_device_path_node_to_string(); + + test_convert_text_to_device_path(); + test_convert_text_to_device_node(); + + // Get the current executable's device path via the `LoadedImage` protocol. + let loaded_image = boot::open_protocol_exclusive::(boot::image_handle()).unwrap(); + let device_path = + boot::open_protocol_exclusive::(loaded_image.device().unwrap()).unwrap(); + + // Get the `LoadedImageDevicePath`. Verify it start with the same nodes as + // `device_path`. + let loaded_image_device_path = + boot::open_protocol_exclusive::(boot::image_handle()).unwrap(); + for (n1, n2) in device_path + .node_iter() + .zip(loaded_image_device_path.node_iter()) { - let loaded_image = boot::open_protocol_exclusive::(boot::image_handle()) - .expect("Failed to open LoadedImage protocol"); - - let device_path = - boot::open_protocol_exclusive::(loaded_image.device().unwrap()) - .expect("Failed to open DevicePath protocol"); - - let device_path_to_text = boot::open_protocol_exclusive::( - boot::get_handle_for_protocol::() - .expect("Failed to get DevicePathToText handle"), - ) - .expect("Failed to open DevicePathToText protocol"); - - let device_path_from_text = boot::open_protocol_exclusive::( - boot::get_handle_for_protocol::() - .expect("Failed to get DevicePathFromText handle"), - ) - .expect("Failed to open DevicePathFromText protocol"); - - // Test round-trip conversion from path to text and back. - let device_path_string = device_path_to_text - .convert_device_path_to_text(&device_path, DisplayOnly(false), AllowShortcuts(false)) - .unwrap(); - assert_eq!( - *device_path_from_text - .convert_text_to_device_path(&device_path_string) - .unwrap(), - *device_path - ); - - for path in device_path.node_iter() { - info!( - "path: type={:?}, subtype={:?}, length={}", - path.device_type(), - path.sub_type(), - path.length(), - ); - - let text = device_path_to_text - .convert_device_node_to_text(path, DisplayOnly(true), AllowShortcuts(false)) - .expect("Failed to convert device path to text"); - let text = &*text; - info!("path name: {text}"); - - let convert = device_path_from_text - .convert_text_to_device_node(text) - .expect("Failed to convert text to device path"); - assert_eq!(*path, *convert); - } - - // Get the `LoadedImageDevicePath`. Verify it start with the same nodes as - // `device_path`. - let loaded_image_device_path = - boot::open_protocol_exclusive::(boot::image_handle()) - .expect("Failed to open LoadedImageDevicePath protocol"); - - for (n1, n2) in device_path - .node_iter() - .zip(loaded_image_device_path.node_iter()) - { - assert_eq!(n1, n2); - } - - // Test `locate_device_path`. - let mut dp = &*device_path; - boot::locate_device_path::(&mut dp).unwrap(); + assert_eq!(n1, n2); } - // test 2/2: test high-level to-string api - { - let loaded_image_device_path = - boot::open_protocol_exclusive::(boot::image_handle()) - .expect("Failed to open LoadedImageDevicePath protocol"); - let device_path: &DevicePath = &loaded_image_device_path; - - let path_components = device_path - .node_iter() - .map(|node| node.to_string(DisplayOnly(false), AllowShortcuts(false))) - .map(|str| str.unwrap().to_string()) - .collect::>(); - - let expected_device_path_str_components = &[ - "PciRoot(0x0)", - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - "Pci(0x1F,0x2)", - #[cfg(target_arch = "aarch64")] - "Pci(0x4,0x0)", - // Sata device only used on x86. - // See xtask utility. - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - "Sata(0x0,0xFFFF,0x0)", - "HD(1,MBR,0xBE1AFDFA,0x3F,0xFBFC1)", - "\\efi\\boot\\test_runner.efi", - ]; - let expected_device_path_str = expected_device_path_str_components.join("/"); - - assert_eq!( - path_components.as_slice(), - expected_device_path_str_components - ); - - // Test that to_string works for device_paths - let path = device_path - .to_string(DisplayOnly(false), AllowShortcuts(false)) + // Test finding a handle by device path. + let mut dp = &*device_path; + boot::locate_device_path::(&mut dp).unwrap(); +} + +fn create_test_device_path() -> Box { + let mut v = Vec::new(); + DevicePathBuilder::with_vec(&mut v) + // Add an ATAPI node because edk2 displays it differently depending on + // the value of `DisplayOnly`. + .push(&build::messaging::Atapi { + primary_secondary: messaging::PrimarySecondary::PRIMARY, + master_slave: messaging::MasterSlave::MASTER, + logical_unit_number: 1, + }) + .unwrap() + // Add a messaging::vendor node because edk2 displays it differently + // depending on the value of `AllowShortcuts`. + .push(&build::messaging::Vendor { + vendor_guid: messaging::Vendor::PC_ANSI, + vendor_defined_data: &[], + }) + .unwrap() + .finalize() + .unwrap() + .to_boxed() +} + +/// Test `DevicePathToText::convert_device_path_to_text`. +fn test_convert_device_path_to_text() { + let path = create_test_device_path(); + + let proto = boot::open_protocol_exclusive::( + boot::get_handle_for_protocol::().unwrap(), + ) + .unwrap(); + + let to_text = |display_only, allow_shortcuts| { + proto + .convert_device_path_to_text(&path, display_only, allow_shortcuts) .unwrap() - .to_string(); + }; + + assert_eq!( + &*to_text(DisplayOnly(true), AllowShortcuts(true)), + cstr16!("Ata(0x1)/VenPcAnsi()") + ); + assert_eq!( + &*to_text(DisplayOnly(true), AllowShortcuts(false)), + cstr16!("Ata(0x1)/VenMsg(E0C14753-F9BE-11D2-9A0C-0090273FC14D)") + ); + assert_eq!( + &*to_text(DisplayOnly(false), AllowShortcuts(true)), + cstr16!("Ata(Primary,Master,0x1)/VenPcAnsi()") + ); + assert_eq!( + &*to_text(DisplayOnly(false), AllowShortcuts(false)), + cstr16!("Ata(Primary,Master,0x1)/VenMsg(E0C14753-F9BE-11D2-9A0C-0090273FC14D)") + ); +} - assert_eq!(path, expected_device_path_str); - } +/// Test `DevicePath::to_string`. +fn test_device_path_to_string() { + let path = create_test_device_path(); + + let to_text = + |display_only, allow_shortcuts| path.to_string(display_only, allow_shortcuts).unwrap(); + + assert_eq!( + &*to_text(DisplayOnly(true), AllowShortcuts(true)), + cstr16!("Ata(0x1)/VenPcAnsi()") + ); + assert_eq!( + &*to_text(DisplayOnly(true), AllowShortcuts(false)), + cstr16!("Ata(0x1)/VenMsg(E0C14753-F9BE-11D2-9A0C-0090273FC14D)") + ); + assert_eq!( + &*to_text(DisplayOnly(false), AllowShortcuts(true)), + cstr16!("Ata(Primary,Master,0x1)/VenPcAnsi()") + ); + assert_eq!( + &*to_text(DisplayOnly(false), AllowShortcuts(false)), + cstr16!("Ata(Primary,Master,0x1)/VenMsg(E0C14753-F9BE-11D2-9A0C-0090273FC14D)") + ); +} + +/// Test `DevicePathToText::convert_device_node_to_text`. +fn test_convert_device_node_to_text() { + let path = create_test_device_path(); + let nodes: Vec<_> = path.node_iter().collect(); + + let proto = boot::open_protocol_exclusive::( + boot::get_handle_for_protocol::().unwrap(), + ) + .unwrap(); + + let to_text = |node, display_only, allow_shortcuts| { + proto + .convert_device_node_to_text(node, display_only, allow_shortcuts) + .unwrap() + }; + + assert_eq!( + &*to_text(nodes[0], DisplayOnly(true), AllowShortcuts(true)), + cstr16!("Ata(0x1)") + ); + assert_eq!( + &*to_text(nodes[0], DisplayOnly(false), AllowShortcuts(true)), + cstr16!("Ata(Primary,Master,0x1)") + ); + assert_eq!( + &*to_text(nodes[1], DisplayOnly(false), AllowShortcuts(true)), + cstr16!("VenPcAnsi()") + ); + assert_eq!( + &*to_text(nodes[1], DisplayOnly(false), AllowShortcuts(false)), + cstr16!("VenMsg(E0C14753-F9BE-11D2-9A0C-0090273FC14D)") + ); +} + +/// Test `DevicePathNode::to_string`. +fn test_device_path_node_to_string() { + let path = create_test_device_path(); + let nodes: Vec<_> = path.node_iter().collect(); + + let to_text = |node: &DevicePathNode, display_only, allow_shortcuts| { + node.to_string(display_only, allow_shortcuts).unwrap() + }; + + assert_eq!( + &*to_text(nodes[0], DisplayOnly(true), AllowShortcuts(true)), + cstr16!("Ata(0x1)") + ); + assert_eq!( + &*to_text(nodes[0], DisplayOnly(false), AllowShortcuts(true)), + cstr16!("Ata(Primary,Master,0x1)") + ); + assert_eq!( + &*to_text(nodes[1], DisplayOnly(false), AllowShortcuts(true)), + cstr16!("VenPcAnsi()") + ); + assert_eq!( + &*to_text(nodes[1], DisplayOnly(false), AllowShortcuts(false)), + cstr16!("VenMsg(E0C14753-F9BE-11D2-9A0C-0090273FC14D)") + ); +} + +/// Test `DevicePathFromText::convert_text_to_device_path`. +fn test_convert_text_to_device_path() { + let text = cstr16!("Ata(Primary,Master,0x1)/VenMsg(E0C14753-F9BE-11D2-9A0C-0090273FC14D)"); + let expected_path = create_test_device_path(); + + let proto = boot::open_protocol_exclusive::( + boot::get_handle_for_protocol::().unwrap(), + ) + .unwrap(); + + assert_eq!( + &*proto.convert_text_to_device_path(text).unwrap(), + &*expected_path + ); +} + +/// Test `DevicePathFromText::convert_text_to_device_node`. +fn test_convert_text_to_device_node() { + let path = create_test_device_path(); + let expected_node = path.node_iter().next().unwrap(); + + let proto = boot::open_protocol_exclusive::( + boot::get_handle_for_protocol::().unwrap(), + ) + .unwrap(); + + assert_eq!( + &*proto + .convert_text_to_device_node(cstr16!("Ata(Primary,Master,0x1)")) + .unwrap(), + expected_node, + ); }