From 0bcde9ae922287c97e561751d5675623465dda87 Mon Sep 17 00:00:00 2001 From: Filippo Casarin Date: Sun, 30 Nov 2025 23:27:16 +0100 Subject: [PATCH 1/2] uefi-raw: add BlockIo2 protocol --- uefi-raw/src/protocol/block.rs | 38 +++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/uefi-raw/src/protocol/block.rs b/uefi-raw/src/protocol/block.rs index fb6a98fa4..0907aad11 100644 --- a/uefi-raw/src/protocol/block.rs +++ b/uefi-raw/src/protocol/block.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -use crate::{Boolean, Guid, Status, guid}; +use crate::{Boolean, Event, Guid, Status, guid}; use core::ffi::c_void; /// Logical block address. @@ -54,3 +54,39 @@ pub struct BlockIoProtocol { impl BlockIoProtocol { pub const GUID: Guid = guid!("964e5b21-6459-11d2-8e39-00a0c969723b"); } + +#[repr(C)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)] +pub struct BlockIo2Token { + pub event: Event, + pub transaction_status: Status, +} + +#[derive(Debug)] +#[repr(C)] +pub struct BlockIo2Protocol { + pub media: *const BlockIoMedia, + pub reset: unsafe extern "efiapi" fn(this: *mut Self, extended_verification: Boolean) -> Status, + pub read_blocks_ex: unsafe extern "efiapi" fn( + this: *const Self, + media_id: u32, + lba: Lba, + token: *mut BlockIo2Token, + buffer_size: usize, + buffer: *mut c_void, + ) -> Status, + pub write_blocks_ex: unsafe extern "efiapi" fn( + this: *mut Self, + media_id: u32, + lba: Lba, + token: *mut BlockIo2Token, + buffer_size: usize, + buffer: *const c_void, + ) -> Status, + pub flush_blocks_ex: + unsafe extern "efiapi" fn(this: *mut Self, token: *mut BlockIo2Token) -> Status, +} + +impl BlockIo2Protocol { + pub const GUID: Guid = guid!("a77b2472-e282-4e9f-a245-c2c0e27bbcc1"); +} From 67b855ee6c3f857f69fdc510ba042ebe069b7893 Mon Sep 17 00:00:00 2001 From: Filippo Casarin Date: Sun, 30 Nov 2025 23:27:16 +0100 Subject: [PATCH 2/2] uefi: add BlockIO2 protocol wrapper --- uefi/CHANGELOG.md | 1 + uefi/src/proto/media/block.rs | 136 +++++++++++++++++++++++++++++++++- 2 files changed, 134 insertions(+), 3 deletions(-) diff --git a/uefi/CHANGELOG.md b/uefi/CHANGELOG.md index 06f70bc44..1286a0d07 100644 --- a/uefi/CHANGELOG.md +++ b/uefi/CHANGELOG.md @@ -6,6 +6,7 @@ - Added `proto::pci::root_bridge::PciRootBridgeIo::configuration()`. - Added `proto::pci::root_bridge::PciRootBridgeIo::enumerate()`. - Added `proto::nvme::pass_thru::NvmePassThru::broadcast()`. +- Added `proto::media::block::BlockIO2`. ## Changed - Changed ordering of `proto::pci::PciIoAddress` to (bus -> dev -> fun -> reg -> ext_reg). diff --git a/uefi/src/proto/media/block.rs b/uefi/src/proto/media/block.rs index 71ed48337..7dc3ecb92 100644 --- a/uefi/src/proto/media/block.rs +++ b/uefi/src/proto/media/block.rs @@ -1,11 +1,14 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -//! Block I/O protocols. +//! Block I/O protocols [`BlockIO`] and [`BlockIO2`]. + +use core::ptr::NonNull; use crate::proto::unsafe_protocol; -use crate::{Result, StatusExt}; +use crate::util::opt_nonnull_to_ptr; +use crate::{Event, Result, Status, StatusExt}; -pub use uefi_raw::protocol::block::{BlockIoProtocol, Lba}; +pub use uefi_raw::protocol::block::{BlockIo2Protocol, BlockIoProtocol, Lba}; /// Block I/O [`Protocol`]. /// @@ -186,3 +189,130 @@ impl BlockIOMedia { self.0.optimal_transfer_length_granularity } } + +/// Asynchronous transaction token for Block I/O 2 operations. +#[repr(C)] +#[derive(Debug)] +pub struct BlockIO2Token { + /// Event to be signalled when an asynchronous block I/O operation + /// completes. + pub event: Option, + /// Transaction status code. + pub transaction_status: Status, +} + +/// Block I/O 2 [`Protocol`]. +/// +/// The Block I/O 2 protocol defines an extension to the Block I/O protocol +/// which enables the ability to read and write data at a block level in a +/// non-blocking manner. +/// +/// [`Protocol`]: uefi::proto::Protocol +#[derive(Debug)] +#[repr(transparent)] +#[unsafe_protocol(BlockIo2Protocol::GUID)] +pub struct BlockIO2(BlockIo2Protocol); + +impl BlockIO2 { + /// Pointer for block IO media. + #[must_use] + pub const fn media(&self) -> &BlockIOMedia { + unsafe { &*self.0.media.cast::() } + } + + /// Resets the block device hardware. + /// + /// # Arguments + /// * `extended_verification` - Indicates that the driver may perform a more exhaustive verification operation of the device during reset. + /// + /// # Errors + /// * [`Status::DEVICE_ERROR`] The block device is not functioning correctly and could not be reset. + pub fn reset(&mut self, extended_verification: bool) -> Result { + unsafe { (self.0.reset)(&mut self.0, extended_verification.into()) }.to_result() + } + + /// Reads the requested number of blocks from the device. + /// + /// # Arguments + /// * `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 + /// Because of the asynchronous nature of the block transaction, manual lifetime + /// tracking is required. + /// + /// # Errors + /// * [`Status::INVALID_PARAMETER`] The read request contains LBAs that are not valid, or the buffer is not on proper alignment. + /// * [`Status::OUT_OF_RESOURCES`] The request could not be completed due to a lack of resources. + /// * [`Status::MEDIA_CHANGED`] The `media_id` is not for the current media. + /// * [`Status::NO_MEDIA`] There is no media in the device. + /// * [`Status::DEVICE_ERROR`] The device reported an error while performing the read operation. + /// * [`Status::BAD_BUFFER_SIZE`] The buffer size parameter is not a multiple of the intrinsic block size of the device. + pub unsafe fn read_blocks_ex( + &self, + media_id: u32, + lba: Lba, + token: Option>, + len: usize, + 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() + } + + /// Writes a specified number of blocks to the device. + /// + /// # Arguments + /// * `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 + /// Because of the asynchronous nature of the block transaction, manual + /// lifetime tracking is required. + /// + /// # Errors + /// * [`Status::INVALID_PARAMETER`] The write request contains LBAs that are not valid, or the buffer is not on proper alignment. + /// * [`Status::OUT_OF_RESOURCES`] The request could not be completed due to a lack of resources. + /// * [`Status::MEDIA_CHANGED`] The `media_id` is not for the current media. + /// * [`Status::NO_MEDIA`] There is no media in the device. + /// * [`Status::DEVICE_ERROR`] The device reported an error while performing the write operation. + /// * [`Status::WRITE_PROTECTED`] The device cannot be written to. + /// * [`Status::BAD_BUFFER_SIZE`] The buffer size parameter is not a multiple of the intrinsic block size of the device. + pub unsafe fn write_blocks_ex( + &mut self, + media_id: u32, + lba: Lba, + token: Option>, + len: usize, + buffer: *const 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()) + } + .to_result() + } + + /// Flushes all modified data to the physical device. + /// + /// # Arguments + /// * `token` - Transaction token for asynchronous flush. + /// + /// # Errors + /// * [`Status::OUT_OF_RESOURCES`] The request could not be completed due to a lack of resources. + /// * [`Status::MEDIA_CHANGED`] The media in the device has changed since the last access. + /// * [`Status::NO_MEDIA`] There is no media in the device. + /// * [`Status::DEVICE_ERROR`] The `media_id` is not for the current media. + /// * [`Status::WRITE_PROTECTED`] The device cannot be written to. + pub fn flush_blocks_ex(&mut self, token: Option>) -> Result { + let token = opt_nonnull_to_ptr(token); + unsafe { (self.0.flush_blocks_ex)(&mut self.0, token.cast()) }.to_result() + } +}