1 // Copyright 2024, The Android Open Source Project 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 //! Rust wrapper for `EFI_BLOCK_IO2_PROTOCOL`. 16 17 use crate::{ 18 efi_call, 19 protocol::{Protocol, ProtocolInfo}, 20 Event, EventNotify, EventType, Tpl, 21 }; 22 use efi_types::{ 23 EfiBlockIo2Protocol, EfiBlockIo2Token, EfiBlockIoMedia, EfiGuid, EFI_STATUS_NOT_READY, 24 }; 25 use gbl_async::{assert_return, yield_now}; 26 use gbl_storage::SliceMaybeUninit; 27 use liberror::{efi_status_to_result, Error, Result}; 28 29 /// EFI_BLOCK_IO2_PROTOCOL 30 pub struct BlockIo2Protocol; 31 32 impl ProtocolInfo for BlockIo2Protocol { 33 type InterfaceType = EfiBlockIo2Protocol; 34 35 const GUID: EfiGuid = 36 EfiGuid::new(0xa77b2472, 0xe282, 0x4e9f, [0xa2, 0x45, 0xc2, 0xc0, 0xe2, 0x7b, 0xbc, 0xc1]); 37 } 38 39 // Protocol interface wrappers. 40 impl Protocol<'_, BlockIo2Protocol> { 41 /// Syncs a non-blocking operation by waiting for the corresponding EFI event to be signaled. wait_io_completion(&self, event: &Event<'_, '_>) -> Result<()>42 async fn wait_io_completion(&self, event: &Event<'_, '_>) -> Result<()> { 43 let bs = self.efi_entry().system_table().boot_services(); 44 loop { 45 match bs.check_event(&event) { 46 Err(e) => { 47 // If we fail to check event/status, force reset the device to release any 48 // retained user buffer. The reset cannot fail. 49 self.reset(true).unwrap(); 50 return Err(e); 51 } 52 Ok(true) => return Ok(()), 53 _ => yield_now().await, 54 } 55 } 56 } 57 58 /// Wraps `EfiBlockIo2Protocol.read_blocks_ex`. read_blocks_ex( &self, lba: u64, buffer: &mut (impl SliceMaybeUninit + ?Sized), ) -> Result<()>59 pub async fn read_blocks_ex( 60 &self, 61 lba: u64, 62 buffer: &mut (impl SliceMaybeUninit + ?Sized), 63 ) -> Result<()> { 64 let bs = self.efi_entry().system_table().boot_services(); 65 // UEFI spec requires that NOTIFY_WAIT event be always created with a callback. 66 let mut notify_fn = &mut |_| (); 67 let mut notify = EventNotify::new(Tpl::Callback, &mut notify_fn); 68 // SAFETY: the notification callback never allocates, deallocates, or panics. 69 let event = 70 unsafe { bs.create_event_with_notification(EventType::NotifyWait, &mut notify) }?; 71 let mut token = 72 EfiBlockIo2Token { event: event.efi_event, transaction_status: EFI_STATUS_NOT_READY }; 73 // SAFETY: 74 // * `self.interface()?` guarantees self.interface is non-null and points to a valid object 75 // established by `Protocol::new()`. 76 // * `self.interface` is input parameter and will not be retained. It outlives the call. 77 // * `Self::wait_io_completion()` is called immediately after. It makes sure the IO is 78 // either completed successfully or is reset if `check_event` fails. Thus it's 79 // guaranteed that after `Self::wait_io_completion()` returns, `buffer` and `token` are 80 // not being retained by the UEFI firmware anymore. 81 // * `assert_return` asserts that `wait_io_completion` returns eventually. Otherwise it 82 // panics if the top level Future gets dropped before it returns. 83 unsafe { 84 efi_call!( 85 self.interface()?.read_blocks_ex, 86 self.interface, 87 self.media()?.media_id, 88 lba, 89 &mut token, 90 buffer.len(), 91 buffer.as_mut().as_mut_ptr() as _ 92 )?; 93 } 94 assert_return(self.wait_io_completion(&event)).await?; 95 efi_status_to_result(token.transaction_status) 96 } 97 98 /// Wraps `EfiBlockIo2Protocol.write_blocks_ex`. write_blocks_ex(&self, lba: u64, buffer: &mut [u8]) -> Result<()>99 pub async fn write_blocks_ex(&self, lba: u64, buffer: &mut [u8]) -> Result<()> { 100 let bs = self.efi_entry().system_table().boot_services(); 101 let mut notify_fn = &mut |_| (); 102 let mut notify = EventNotify::new(Tpl::Callback, &mut notify_fn); 103 // SAFETY: the notification callback never allocates, deallocates, or panics. 104 let event = 105 unsafe { bs.create_event_with_notification(EventType::NotifyWait, &mut notify) }?; 106 let mut token = 107 EfiBlockIo2Token { event: event.efi_event, transaction_status: EFI_STATUS_NOT_READY }; 108 // SAFETY: See safety comment for `Self::read_blocks_ex()`. 109 unsafe { 110 efi_call!( 111 self.interface()?.write_blocks_ex, 112 self.interface, 113 self.media()?.media_id, 114 lba, 115 &mut token, 116 buffer.len(), 117 buffer.as_mut_ptr() as _ 118 )?; 119 } 120 assert_return(self.wait_io_completion(&event)).await?; 121 efi_status_to_result(token.transaction_status) 122 } 123 124 /// Wraps `EFI_BLOCK_IO2_PROTOCOL.flush_blocks_ex()` flush_blocks_ex(&self) -> Result<()>125 pub async fn flush_blocks_ex(&self) -> Result<()> { 126 let bs = self.efi_entry().system_table().boot_services(); 127 let mut notify_fn = &mut |_| (); 128 let mut notify = EventNotify::new(Tpl::Callback, &mut notify_fn); 129 // SAFETY: the notification callback never allocates, deallocates, or panics. 130 let event = 131 unsafe { bs.create_event_with_notification(EventType::NotifyWait, &mut notify) }?; 132 let mut token = 133 EfiBlockIo2Token { event: event.efi_event, transaction_status: EFI_STATUS_NOT_READY }; 134 // SAFETY: See safety comment for `Self::read_blocks_ex()`. 135 unsafe { efi_call!(self.interface()?.flush_blocks_ex, self.interface, &mut token) }?; 136 assert_return(self.wait_io_completion(&event)).await?; 137 efi_status_to_result(token.transaction_status) 138 } 139 140 /// Wraps `EFI_BLOCK_IO2_PROTOCOL.reset()` reset(&self, extended_verification: bool) -> Result<()>141 pub fn reset(&self, extended_verification: bool) -> Result<()> { 142 // SAFETY: 143 // * See safety comment for `Self::read_blocks_ex()`. 144 // * The operation is synchronous, no need to call wait_io_completion(). 145 unsafe { efi_call!(self.interface()?.reset, self.interface, extended_verification) } 146 } 147 148 /// Gets a copy of the `EFI_BLOCK_IO2_PROTOCOL.Media` structure. media(&self) -> Result<EfiBlockIoMedia>149 pub fn media(&self) -> Result<EfiBlockIoMedia> { 150 let ptr = self.interface()?.media; 151 // SAFETY: Pointers to EFI data structure. 152 Ok(*unsafe { ptr.as_ref() }.ok_or(Error::InvalidInput)?) 153 } 154 } 155