1 //! Disk I/O protocols.
2 
3 use crate::proto::unsafe_protocol;
4 use crate::util::opt_nonnull_to_ptr;
5 use crate::{Event, Result, Status, StatusExt};
6 use core::ptr::NonNull;
7 use uefi_raw::protocol::disk::{DiskIo2Protocol, DiskIoProtocol};
8 
9 /// The disk I/O protocol.
10 ///
11 /// This protocol is used to abstract the block accesses of the block I/O
12 /// protocol to a more general offset-length protocol. Firmware is
13 /// responsible for adding this protocol to any block I/O interface that
14 /// appears in the system that does not already have a disk I/O protocol.
15 #[derive(Debug)]
16 #[repr(transparent)]
17 #[unsafe_protocol(DiskIoProtocol::GUID)]
18 pub struct DiskIo(DiskIoProtocol);
19 
20 impl DiskIo {
21     /// Reads bytes from the disk device.
22     ///
23     /// # Arguments:
24     /// * `media_id` - ID of the medium to be read.
25     /// * `offset` - Starting byte offset on the logical block I/O device to read from.
26     /// * `buffer` - Pointer to a buffer to read into.
27     ///
28     /// # Errors:
29     /// * `uefi::status::INVALID_PARAMETER` The read request contains device addresses that
30     ///                                     are not valid for the device.
31     /// * `uefi::status::DEVICE_ERROR`      The device reported an error while performing
32     ///                                     the read operation.
33     /// * `uefi::status::NO_MEDIA`          There is no medium in the device.
34     /// * `uefi::status::MEDIA_CHANGED`     `media_id` is not for the current medium.
read_disk(&self, media_id: u32, offset: u64, buffer: &mut [u8]) -> Result35     pub fn read_disk(&self, media_id: u32, offset: u64, buffer: &mut [u8]) -> Result {
36         unsafe {
37             (self.0.read_disk)(
38                 &self.0,
39                 media_id,
40                 offset,
41                 buffer.len(),
42                 buffer.as_mut_ptr().cast(),
43             )
44         }
45         .to_result()
46     }
47 
48     /// Writes bytes to the disk device.
49     ///
50     /// # Arguments:
51     /// * `media_id` - ID of the medium to be written.
52     /// * `offset` - Starting byte offset on the logical block I/O device to write to.
53     /// * `buffer` - Pointer to a buffer to write from.
54     ///
55     /// # Errors:
56     /// * `uefi::status::INVALID_PARAMETER` The write request contains device addresses that
57     ///                                     are not valid for the device.
58     /// * `uefi::status::DEVICE_ERROR`      The device reported an error while performing
59     ///                                     the write operation.
60     /// * `uefi::status::NO_MEDIA`          There is no medium in the device.
61     /// * `uefi::status::MEDIA_CHANGED`     `media_id` is not for the current medium.
62     /// * `uefi::status::WRITE_PROTECTED`   The device cannot be written to.
write_disk(&mut self, media_id: u32, offset: u64, buffer: &[u8]) -> Result63     pub fn write_disk(&mut self, media_id: u32, offset: u64, buffer: &[u8]) -> Result {
64         unsafe {
65             (self.0.write_disk)(
66                 &mut self.0,
67                 media_id,
68                 offset,
69                 buffer.len(),
70                 buffer.as_ptr().cast(),
71             )
72         }
73         .to_result()
74     }
75 }
76 
77 /// Asynchronous transaction token for disk I/O 2 operations.
78 #[repr(C)]
79 #[derive(Debug)]
80 pub struct DiskIo2Token {
81     /// Event to be signalled when an asynchronous disk I/O operation completes.
82     pub event: Option<Event>,
83     /// Transaction status code.
84     pub transaction_status: Status,
85 }
86 
87 /// The disk I/O 2 protocol.
88 ///
89 /// This protocol provides an extension to the disk I/O protocol to enable
90 /// non-blocking / asynchronous byte-oriented disk operation.
91 #[derive(Debug)]
92 #[repr(transparent)]
93 #[unsafe_protocol(DiskIo2Protocol::GUID)]
94 pub struct DiskIo2(DiskIo2Protocol);
95 
96 impl DiskIo2 {
97     /// Terminates outstanding asynchronous requests to the device.
98     ///
99     /// # Errors:
100     /// * `uefi::status::DEVICE_ERROR`  The device reported an error while performing
101     ///                                 the cancel operation.
cancel(&mut self) -> Result102     pub fn cancel(&mut self) -> Result {
103         unsafe { (self.0.cancel)(&mut self.0) }.to_result()
104     }
105 
106     /// Reads bytes from the disk device.
107     ///
108     /// # Arguments:
109     /// * `media_id` - ID of the medium to be read from.
110     /// * `offset` - Starting byte offset on the logical block I/O device to read from.
111     /// * `token` - Transaction token for asynchronous read.
112     /// * `len` - Buffer size.
113     /// * `buffer` - Buffer to read into.
114     ///
115     /// # Safety
116     ///
117     /// Because of the asynchronous nature of the disk transaction, manual lifetime
118     /// tracking is required.
119     ///
120     /// # Errors:
121     /// * `uefi::status::INVALID_PARAMETER` The read request contains device addresses
122     ///                                     that are not valid for the device.
123     /// * `uefi::status::OUT_OF_RESOURCES`  The request could not be completed due to
124     ///                                     a lack of resources.
125     /// * `uefi::status::MEDIA_CHANGED`     `media_id` is not for the current medium.
126     /// * `uefi::status::NO_MEDIA`          There is no medium in the device.
127     /// * `uefi::status::DEVICE_ERROR`      The device reported an error while performing
128     ///                                     the read operation.
read_disk_raw( &self, media_id: u32, offset: u64, token: Option<NonNull<DiskIo2Token>>, len: usize, buffer: *mut u8, ) -> Result129     pub unsafe fn read_disk_raw(
130         &self,
131         media_id: u32,
132         offset: u64,
133         token: Option<NonNull<DiskIo2Token>>,
134         len: usize,
135         buffer: *mut u8,
136     ) -> Result {
137         let token = opt_nonnull_to_ptr(token);
138         (self.0.read_disk_ex)(&self.0, media_id, offset, token.cast(), len, buffer.cast())
139             .to_result()
140     }
141 
142     /// Writes bytes to the disk device.
143     ///
144     /// # Arguments:
145     /// * `media_id` - ID of the medium to write to.
146     /// * `offset` - Starting byte offset on the logical block I/O device to write to.
147     /// * `token` - Transaction token for asynchronous write.
148     /// * `len` - Buffer size.
149     /// * `buffer` - Buffer to write from.
150     ///
151     /// # Safety
152     ///
153     /// Because of the asynchronous nature of the disk transaction, manual lifetime
154     /// tracking is required.
155     ///
156     /// # Errors:
157     /// * `uefi::status::INVALID_PARAMETER` The write request contains device addresses
158     ///                                     that are not valid for the device.
159     /// * `uefi::status::OUT_OF_RESOURCES`  The request could not be completed due to
160     ///                                     a lack of resources.
161     /// * `uefi::status::MEDIA_CHANGED`     `media_id` is not for the current medium.
162     /// * `uefi::status::NO_MEDIA`          There is no medium in the device.
163     /// * `uefi::status::DEVICE_ERROR`      The device reported an error while performing
164     ///                                     the write operation.
165     /// * `uefi::status::WRITE_PROTECTED`   The device cannot be written to.
write_disk_raw( &mut self, media_id: u32, offset: u64, token: Option<NonNull<DiskIo2Token>>, len: usize, buffer: *const u8, ) -> Result166     pub unsafe fn write_disk_raw(
167         &mut self,
168         media_id: u32,
169         offset: u64,
170         token: Option<NonNull<DiskIo2Token>>,
171         len: usize,
172         buffer: *const u8,
173     ) -> Result {
174         let token = opt_nonnull_to_ptr(token);
175         (self.0.write_disk_ex)(
176             &mut self.0,
177             media_id,
178             offset,
179             token.cast(),
180             len,
181             buffer.cast(),
182         )
183         .to_result()
184     }
185 
186     /// Flushes all modified data to the physical device.
187     ///
188     /// # Arguments:
189     /// * `token` - Transaction token for the asynchronous flush.
190     ///
191     /// # Errors:
192     /// * `uefi::status::OUT_OF_RESOURCES`  The request could not be completed due to
193     ///                                     a lack of resources.
194     /// * `uefi::status::MEDIA_CHANGED`     The medium in the device has changed since
195     ///                                     the last access.
196     /// * `uefi::status::NO_MEDIA`          There is no medium in the device.
197     /// * `uefi::status::DEVICE_ERROR`      The device reported an error while performing
198     ///                                     the flush operation.
199     /// * `uefi::status::WRITE_PROTECTED`   The device cannot be written to.
flush_disk(&mut self, token: Option<NonNull<DiskIo2Token>>) -> Result200     pub fn flush_disk(&mut self, token: Option<NonNull<DiskIo2Token>>) -> Result {
201         let token = opt_nonnull_to_ptr(token);
202         unsafe { (self.0.flush_disk_ex)(&mut self.0, token.cast()) }.to_result()
203     }
204 }
205