|
1 | 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 |
2 | 2 |
|
3 | | -//! Block I/O protocols. |
| 3 | +//! Block I/O protocols [`BlockIO`] and [`BlockIO2`]. |
| 4 | +
|
| 5 | +use core::ptr::NonNull; |
4 | 6 |
|
5 | 7 | use crate::proto::unsafe_protocol; |
6 | | -use crate::{Result, StatusExt}; |
| 8 | +use crate::util::opt_nonnull_to_ptr; |
| 9 | +use crate::{Event, Result, Status, StatusExt}; |
7 | 10 |
|
8 | | -pub use uefi_raw::protocol::block::{BlockIoProtocol, Lba}; |
| 11 | +pub use uefi_raw::protocol::block::{BlockIo2Protocol, BlockIoProtocol, Lba}; |
9 | 12 |
|
10 | 13 | /// Block I/O [`Protocol`]. |
11 | 14 | /// |
@@ -186,3 +189,130 @@ impl BlockIOMedia { |
186 | 189 | self.0.optimal_transfer_length_granularity |
187 | 190 | } |
188 | 191 | } |
| 192 | + |
| 193 | +/// Asynchronous transaction token for Block I/O 2 operations. |
| 194 | +#[repr(C)] |
| 195 | +#[derive(Debug)] |
| 196 | +pub struct BlockIO2Token { |
| 197 | + /// Event to be signalled when an asynchronous block I/O operation |
| 198 | + /// completes. |
| 199 | + pub event: Option<Event>, |
| 200 | + /// Transaction status code. |
| 201 | + pub transaction_status: Status, |
| 202 | +} |
| 203 | + |
| 204 | +/// Block I/O 2 [`Protocol`]. |
| 205 | +/// |
| 206 | +/// The Block I/O 2 protocol defines an extension to the Block I/O protocol |
| 207 | +/// which enables the ability to read and write data at a block level in a |
| 208 | +/// non-blocking manner. |
| 209 | +/// |
| 210 | +/// [`Protocol`]: uefi::proto::Protocol |
| 211 | +#[derive(Debug)] |
| 212 | +#[repr(transparent)] |
| 213 | +#[unsafe_protocol(BlockIo2Protocol::GUID)] |
| 214 | +pub struct BlockIO2(BlockIo2Protocol); |
| 215 | + |
| 216 | +impl BlockIO2 { |
| 217 | + /// Pointer for block IO media. |
| 218 | + #[must_use] |
| 219 | + pub const fn media(&self) -> &BlockIOMedia { |
| 220 | + unsafe { &*self.0.media.cast::<BlockIOMedia>() } |
| 221 | + } |
| 222 | + |
| 223 | + /// Resets the block device hardware. |
| 224 | + /// |
| 225 | + /// # Arguments |
| 226 | + /// * `extended_verification` - Indicates that the driver may perform a more exhaustive verification operation of the device during reset. |
| 227 | + /// |
| 228 | + /// # Errors |
| 229 | + /// * [`Status::DEVICE_ERROR`] The block device is not functioning correctly and could not be reset. |
| 230 | + pub fn reset(&mut self, extended_verification: bool) -> Result { |
| 231 | + unsafe { (self.0.reset)(&mut self.0, extended_verification.into()) }.to_result() |
| 232 | + } |
| 233 | + |
| 234 | + /// Reads the requested number of blocks from the device. |
| 235 | + /// |
| 236 | + /// # Arguments |
| 237 | + /// * `media_id` - The media ID that the read request is for. |
| 238 | + /// * `lba` - The starting logical block address to read from on the device. |
| 239 | + /// * `token` - Transaction token for asynchronous read. |
| 240 | + /// * `len` - Buffer size. |
| 241 | + /// * `buffer` - The target buffer of the read operation |
| 242 | + /// |
| 243 | + /// # Safety |
| 244 | + /// Because of the asynchronous nature of the block transaction, manual lifetime |
| 245 | + /// tracking is required. |
| 246 | + /// |
| 247 | + /// # Errors |
| 248 | + /// * [`Status::INVALID_PARAMETER`] The read request contains LBAs that are not valid, or the buffer is not on proper alignment. |
| 249 | + /// * [`Status::OUT_OF_RESOURCES`] The request could not be completed due to a lack of resources. |
| 250 | + /// * [`Status::MEDIA_CHANGED`] The `media_id` is not for the current media. |
| 251 | + /// * [`Status::NO_MEDIA`] There is no media in the device. |
| 252 | + /// * [`Status::DEVICE_ERROR`] The device reported an error while performing the read operation. |
| 253 | + /// * [`Status::BAD_BUFFER_SIZE`] The buffer size parameter is not a multiple of the intrinsic block size of the device. |
| 254 | + pub unsafe fn read_blocks_ex( |
| 255 | + &self, |
| 256 | + media_id: u32, |
| 257 | + lba: Lba, |
| 258 | + token: Option<NonNull<BlockIO2Token>>, |
| 259 | + len: usize, |
| 260 | + buffer: *mut u8, |
| 261 | + ) -> Result { |
| 262 | + let token = opt_nonnull_to_ptr(token); |
| 263 | + unsafe { (self.0.read_blocks_ex)(&self.0, media_id, lba, token.cast(), len, buffer.cast()) } |
| 264 | + .to_result() |
| 265 | + } |
| 266 | + |
| 267 | + /// Writes a specified number of blocks to the device. |
| 268 | + /// |
| 269 | + /// # Arguments |
| 270 | + /// * `media_id` - The media ID that the write request is for. |
| 271 | + /// * `lba` - The starting logical block address to be written. |
| 272 | + /// * `token` - Transaction token for asynchronous write. |
| 273 | + /// * `len` - Buffer size. |
| 274 | + /// * `buffer` - Buffer to be written from. |
| 275 | + /// |
| 276 | + /// # Safety |
| 277 | + /// Because of the asynchronous nature of the block transaction, manual |
| 278 | + /// lifetime tracking is required. |
| 279 | + /// |
| 280 | + /// # Errors |
| 281 | + /// * [`Status::INVALID_PARAMETER`] The write request contains LBAs that are not valid, or the buffer is not on proper alignment. |
| 282 | + /// * [`Status::OUT_OF_RESOURCES`] The request could not be completed due to a lack of resources. |
| 283 | + /// * [`Status::MEDIA_CHANGED`] The `media_id` is not for the current media. |
| 284 | + /// * [`Status::NO_MEDIA`] There is no media in the device. |
| 285 | + /// * [`Status::DEVICE_ERROR`] The device reported an error while performing the write operation. |
| 286 | + /// * [`Status::WRITE_PROTECTED`] The device cannot be written to. |
| 287 | + /// * [`Status::BAD_BUFFER_SIZE`] The buffer size parameter is not a multiple of the intrinsic block size of the device. |
| 288 | + pub unsafe fn write_blocks_ex( |
| 289 | + &mut self, |
| 290 | + media_id: u32, |
| 291 | + lba: Lba, |
| 292 | + token: Option<NonNull<BlockIO2Token>>, |
| 293 | + len: usize, |
| 294 | + buffer: *const u8, |
| 295 | + ) -> Result { |
| 296 | + let token = opt_nonnull_to_ptr(token); |
| 297 | + unsafe { |
| 298 | + (self.0.write_blocks_ex)(&mut self.0, media_id, lba, token.cast(), len, buffer.cast()) |
| 299 | + } |
| 300 | + .to_result() |
| 301 | + } |
| 302 | + |
| 303 | + /// Flushes all modified data to the physical device. |
| 304 | + /// |
| 305 | + /// # Arguments |
| 306 | + /// * `token` - Transaction token for asynchronous flush. |
| 307 | + /// |
| 308 | + /// # Errors |
| 309 | + /// * [`Status::OUT_OF_RESOURCES`] The request could not be completed due to a lack of resources. |
| 310 | + /// * [`Status::MEDIA_CHANGED`] The media in the device has changed since the last access. |
| 311 | + /// * [`Status::NO_MEDIA`] There is no media in the device. |
| 312 | + /// * [`Status::DEVICE_ERROR`] The `media_id` is not for the current media. |
| 313 | + /// * [`Status::WRITE_PROTECTED`] The device cannot be written to. |
| 314 | + pub fn flush_blocks_ex(&mut self, token: Option<NonNull<BlockIO2Token>>) -> Result { |
| 315 | + let token = opt_nonnull_to_ptr(token); |
| 316 | + unsafe { (self.0.flush_blocks_ex)(&mut self.0, token.cast()) }.to_result() |
| 317 | + } |
| 318 | +} |
0 commit comments