From 593f67368fe50e9d51dbd4b0e33c2bb8bf6576c3 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Sat, 6 Dec 2025 21:31:06 +0100 Subject: [PATCH 1/2] uefi: BlockIo2: fix unidiomatic code Unfortunately not discovered in the review. --- uefi/src/proto/media/block.rs | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/uefi/src/proto/media/block.rs b/uefi/src/proto/media/block.rs index 7dc3ecb92..b09dc9a04 100644 --- a/uefi/src/proto/media/block.rs +++ b/uefi/src/proto/media/block.rs @@ -237,7 +237,6 @@ impl BlockIO2 { /// * `media_id` - The media ID that the read request is for. /// * `lba` - The starting logical block address to read from on the device. /// * `token` - Transaction token for asynchronous read. - /// * `len` - Buffer size. /// * `buffer` - The target buffer of the read operation /// /// # Safety @@ -256,12 +255,20 @@ impl BlockIO2 { media_id: u32, lba: Lba, token: Option>, - len: usize, - buffer: *mut u8, + buffer: &mut [u8], ) -> Result { let token = opt_nonnull_to_ptr(token); - unsafe { (self.0.read_blocks_ex)(&self.0, media_id, lba, token.cast(), len, buffer.cast()) } - .to_result() + unsafe { + (self.0.read_blocks_ex)( + &self.0, + media_id, + lba, + token.cast(), + buffer.len(), + buffer.as_mut_ptr().cast(), + ) + } + .to_result() } /// Writes a specified number of blocks to the device. @@ -270,7 +277,6 @@ impl BlockIO2 { /// * `media_id` - The media ID that the write request is for. /// * `lba` - The starting logical block address to be written. /// * `token` - Transaction token for asynchronous write. - /// * `len` - Buffer size. /// * `buffer` - Buffer to be written from. /// /// # Safety @@ -290,12 +296,18 @@ impl BlockIO2 { media_id: u32, lba: Lba, token: Option>, - len: usize, - buffer: *const u8, + buffer: &[u8], ) -> Result { let token = opt_nonnull_to_ptr(token); unsafe { - (self.0.write_blocks_ex)(&mut self.0, media_id, lba, token.cast(), len, buffer.cast()) + (self.0.write_blocks_ex)( + &mut self.0, + media_id, + lba, + token.cast(), + buffer.len(), + buffer.as_ptr().cast(), + ) } .to_result() } From 295ec8d69daca351812d067b9e6b84ea2738db7d Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Sat, 6 Dec 2025 21:32:27 +0100 Subject: [PATCH 2/2] uefi-test-runner: add basic test for blockio protocols --- uefi-test-runner/src/proto/block.rs | 137 ++++++++++++++++++++++++++++ uefi-test-runner/src/proto/mod.rs | 10 +- 2 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 uefi-test-runner/src/proto/block.rs diff --git a/uefi-test-runner/src/proto/block.rs b/uefi-test-runner/src/proto/block.rs new file mode 100644 index 000000000..6806b67f7 --- /dev/null +++ b/uefi-test-runner/src/proto/block.rs @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 + +//! Very basic tests for the BlockIo and BlockIo2 protocols. +//! +//! We look for some well-known data on a few well-known disks +//! of our test environment. + +use alloc::string::String; +use uefi::boot::{OpenProtocolAttributes, OpenProtocolParams, ScopedProtocol}; +use uefi::proto::Protocol; +use uefi::proto::device_path::DevicePath; +use uefi::proto::device_path::text::{AllowShortcuts, DisplayOnly}; +use uefi::proto::media::block::{BlockIO, BlockIO2}; +use uefi::{CString16, Handle, boot}; +use uefi_raw::protocol::device_path::DeviceSubType; + +fn verify_block_device(dvp: &DevicePath, first_block: &[u8]) { + // We only look for storage technologies that we are interested in. + let storage_device_types = [ + DeviceSubType::MESSAGING_SCSI, + DeviceSubType::MESSAGING_NVME_NAMESPACE, + DeviceSubType::MESSAGING_SATA, + ]; + let storage_node = dvp + .node_iter() + .skip_while(|x| !storage_device_types.contains(&x.sub_type())) + .next() + .unwrap(); + let storage_node_string = storage_node + .to_string(DisplayOnly(true), AllowShortcuts(true)) + .unwrap(); + debug!("Storage technology: {storage_node_string}"); + + //debug!("First 512 bytes: {first_block:?}"); + match storage_node.sub_type() { + DeviceSubType::MESSAGING_SCSI => { /* empty disks so far, nothing to check for */ } + DeviceSubType::MESSAGING_NVME_NAMESPACE => { + /* empty disks so far, nothing to check for */ + } + DeviceSubType::MESSAGING_SATA => { + // We check that the right SATA disk indeed contains a correct + // FAT16 volume. + let expected = "MbrTestDisk"; + let contains_volume_label = first_block + .windows(expected.len()) + .any(|w| w == expected.as_bytes()); + let oem_name = { + let bytes = &first_block[3..10]; + String::from_utf8(bytes.to_vec()) + }; + let is_valid_fat = first_block[0] != 0 && oem_name.is_ok(); + if is_valid_fat && storage_node.data() == &[0x2, 0, 0xff, 0xff, 0x0, 0x0] { + if !contains_volume_label { + panic!( + "Sata disk {storage_node_string} does not contain {expected} in its first 512 bytes" + ) + } else { + debug!( + "Found volume label {expected} with OEM name: {}", + oem_name.unwrap() + ); + } + } + } + _ => unreachable!(), + } +} + +fn open_proto_and_dvp( + handle: Handle, +) -> (ScopedProtocol

, ScopedProtocol, CString16) { + let proto = unsafe { + boot::open_protocol::

( + OpenProtocolParams { + handle, + agent: boot::image_handle(), + controller: None, + }, + OpenProtocolAttributes::GetProtocol, + ) + .unwrap() + }; + let dvp = unsafe { + boot::open_protocol::( + OpenProtocolParams { + handle, + agent: boot::image_handle(), + controller: None, + }, + OpenProtocolAttributes::GetProtocol, + ) + .unwrap() + }; + let dvp_string = dvp + .to_string(DisplayOnly(true), AllowShortcuts(true)) + .unwrap(); + + (proto, dvp, dvp_string) +} + +fn test_blockio_protocol() { + info!("Testing BLOCKIO protocol"); + for handle in boot::find_handles::().unwrap() { + let (proto, dvp, dvp_string) = open_proto_and_dvp::(handle); + debug!("Found handle supporting protocol: {dvp_string}"); + debug!("media: {:?}", proto.media()); + let mut first_block = vec![0; 512]; + proto + .read_blocks(proto.media().media_id(), 0, &mut first_block) + .unwrap(); + + verify_block_device(&dvp, first_block.as_slice()); + } +} + +fn test_blockio2_protocol() { + info!("Testing BLOCKIO 2 protocol"); + + for handle in boot::find_handles::().unwrap() { + let (proto, dvp, dvp_string) = open_proto_and_dvp::(handle); + debug!("Found handle supporting protocol: {dvp_string}"); + debug!("media: {:?}", proto.media()); + let mut first_block = vec![0; 512]; + unsafe { + proto + .read_blocks_ex(proto.media().media_id(), 0, None, &mut first_block) + .unwrap(); + } + + verify_block_device(&dvp, first_block.as_slice()); + } +} + +pub fn test() { + test_blockio_protocol(); + test_blockio2_protocol(); +} diff --git a/uefi-test-runner/src/proto/mod.rs b/uefi-test-runner/src/proto/mod.rs index 5d66587c4..a6de6a083 100644 --- a/uefi-test-runner/src/proto/mod.rs +++ b/uefi-test-runner/src/proto/mod.rs @@ -9,10 +9,11 @@ pub fn test() { console::test(); - find_protocol(); + test_find_handles(); test_protocols_per_handle(); test_test_protocol(); + block::test(); debug::test(); device_path::test(); driver::test(); @@ -46,7 +47,11 @@ pub fn test() { tcg::test(); } -fn find_protocol() { +/// Tests that the [`boot::find_handles`] wrapper can find handles using the +/// [`Output`] protocol. +/// +/// [`Output`]: proto::console::text::Output +fn test_find_handles() { let handles = boot::find_handles::() .expect("Failed to retrieve list of handles"); @@ -76,6 +81,7 @@ fn test_test_protocol() { #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] mod ata; +mod block; mod console; mod debug; mod device_path;