Skip to content

Commit 468c4af

Browse files
e820nicholasbishop
authored andcommitted
Make DiskIo2 I/O unsafe & add tests
1 parent 2c9fdee commit 468c4af

File tree

2 files changed

+118
-17
lines changed

2 files changed

+118
-17
lines changed

src/proto/media/disk.rs

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ impl DiskIo2 {
123123
/// * `media_id` - ID of the medium to be read from.
124124
/// * `offset` - Starting byte offset on the logical block I/O device to read from.
125125
/// * `token` - Transaction token for asynchronous read.
126+
/// * `len` - Buffer size.
126127
/// * `buffer` - Buffer to read into.
127128
///
128129
/// # Errors:
@@ -134,22 +135,15 @@ impl DiskIo2 {
134135
/// * `uefi::status::NO_MEDIA` There is no medium in the device.
135136
/// * `uefi::status::DEVICE_ERROR` The device reported an error while performing
136137
/// the read operation.
137-
pub fn read_disk(
138+
pub unsafe fn read_disk_raw(
138139
&self,
139140
media_id: u32,
140141
offset: u64,
141-
token: &mut DiskIo2Token,
142-
buffer: &mut [u8],
142+
token: *mut DiskIo2Token,
143+
len: usize,
144+
buffer: *mut u8,
143145
) -> Result {
144-
(self.read_disk_ex)(
145-
self,
146-
media_id,
147-
offset,
148-
token,
149-
buffer.len(),
150-
buffer.as_mut_ptr(),
151-
)
152-
.into()
146+
(self.read_disk_ex)(self, media_id, offset, &mut *token, len, buffer).into()
153147
}
154148

155149
/// Writes bytes to the disk device.
@@ -158,6 +152,7 @@ impl DiskIo2 {
158152
/// * `media_id` - ID of the medium to write to.
159153
/// * `offset` - Starting byte offset on the logical block I/O device to write to.
160154
/// * `token` - Transaction token for asynchronous write.
155+
/// * `len` - Buffer size.
161156
/// * `buffer` - Buffer to write from.
162157
///
163158
/// # Errors:
@@ -170,14 +165,15 @@ impl DiskIo2 {
170165
/// * `uefi::status::DEVICE_ERROR` The device reported an error while performing
171166
/// the write operation.
172167
/// * `uefi::status::WRITE_PROTECTED` The device cannot be written to.
173-
pub fn write_disk(
168+
pub unsafe fn write_disk_raw(
174169
&mut self,
175170
media_id: u32,
176171
offset: u64,
177-
token: &mut DiskIo2Token,
178-
buffer: &[u8],
172+
token: *mut DiskIo2Token,
173+
len: usize,
174+
buffer: *const u8,
179175
) -> Result {
180-
(self.write_disk_ex)(self, media_id, offset, token, buffer.len(), buffer.as_ptr()).into()
176+
(self.write_disk_ex)(self, media_id, offset, &mut *token, len, buffer).into()
181177
}
182178

183179
/// Flushes all modified data to the physical device.

uefi-test-runner/src/proto/media/known_disk.rs

Lines changed: 106 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
use alloc::string::ToString;
2+
use core::ffi::c_void;
3+
use core::ptr::NonNull;
24
use uefi::prelude::*;
5+
use uefi::proto::media::disk::{DiskIo, DiskIo2, DiskIo2Token};
36
use uefi::proto::media::file::{
47
Directory, File, FileAttribute, FileInfo, FileMode, FileSystemInfo,
58
};
69
use uefi::proto::media::fs::SimpleFileSystem;
7-
use uefi::table::boot::{OpenProtocolAttributes, OpenProtocolParams};
10+
use uefi::table::boot::{EventType, MemoryType, OpenProtocolAttributes, OpenProtocolParams, Tpl};
811
use uefi::table::runtime::{Daylight, Time, TimeParams};
12+
use uefi::Event;
913

1014
/// Test directory entry iteration.
1115
fn test_existing_dir(directory: &mut Directory) {
@@ -138,6 +142,103 @@ fn test_create_file(directory: &mut Directory) {
138142
file.write(b"test output data").unwrap();
139143
}
140144

145+
/// Tests raw disk I/O.
146+
fn test_raw_disk_io(handle: Handle, image: Handle, bt: &BootServices) {
147+
info!("Testing raw disk I/O");
148+
149+
// Open the disk I/O protocol on the input handle
150+
let disk_io = bt
151+
.open_protocol::<DiskIo>(
152+
OpenProtocolParams {
153+
handle,
154+
agent: image,
155+
controller: None,
156+
},
157+
OpenProtocolAttributes::GetProtocol,
158+
)
159+
.expect("Failed to get disk I/O protocol");
160+
161+
// Allocate a temporary buffer to read into
162+
const SIZE: usize = 512;
163+
let buf = bt
164+
.allocate_pool(MemoryType::LOADER_DATA, SIZE)
165+
.expect("Failed to allocate temporary buffer");
166+
167+
// SAFETY: A valid buffer of `SIZE` bytes was allocated above
168+
let slice = unsafe { core::slice::from_raw_parts_mut(buf, SIZE) };
169+
170+
// Read from the first sector of the disk into the buffer
171+
disk_io
172+
.read_disk(0, 0, slice)
173+
.expect("Failed to read from disk");
174+
175+
// Verify that the disk's MBR signature is correct
176+
assert_eq!(slice[510], 0x55);
177+
assert_eq!(slice[511], 0xaa);
178+
179+
info!("Raw disk I/O succeeded");
180+
bt.free_pool(buf).unwrap();
181+
}
182+
183+
/// Asynchronous disk I/O 2 transaction callback
184+
unsafe extern "efiapi" fn disk_io2_callback(_event: Event, ctx: Option<NonNull<c_void>>) {
185+
let ptr = ctx.unwrap().as_ptr() as *const u8;
186+
187+
// Verify that the disk's MBR signature is correct
188+
assert_eq!(*ptr.offset(510), 0x55);
189+
assert_eq!(*ptr.offset(511), 0xaa);
190+
}
191+
192+
/// Tests raw disk I/O through the DiskIo2 protocol.
193+
fn test_raw_disk_io2(handle: Handle, image: Handle, bt: &BootServices) {
194+
info!("Testing raw disk I/O 2");
195+
196+
// Open the disk I/O protocol on the input handle
197+
let disk_io2 = bt
198+
.open_protocol::<DiskIo2>(
199+
OpenProtocolParams {
200+
handle,
201+
agent: image,
202+
controller: None,
203+
},
204+
OpenProtocolAttributes::GetProtocol,
205+
)
206+
.expect("Failed to get disk I/O 2 protocol");
207+
208+
// Allocate a temporary buffer to read into
209+
const SIZE: usize = 512;
210+
let buf = bt
211+
.allocate_pool(MemoryType::LOADER_DATA, SIZE)
212+
.expect("Failed to allocate temporary buffer");
213+
214+
// Create an event callback for the disk read completion
215+
let event = unsafe {
216+
bt.create_event(
217+
EventType::NOTIFY_SIGNAL,
218+
Tpl::NOTIFY,
219+
Some(disk_io2_callback),
220+
NonNull::new(buf as *mut c_void),
221+
)
222+
.expect("Failed to create event for disk I/O 2 transaction")
223+
};
224+
225+
// Read from the first sector of the disk into the buffer
226+
// SAFETY: The cloned `event` is only used for this transaction
227+
unsafe {
228+
let mut token = DiskIo2Token {
229+
event: Some(event.unsafe_clone()),
230+
transaction_status: uefi::Status::SUCCESS,
231+
};
232+
disk_io2
233+
.read_disk_raw(0, 0, &mut token, SIZE, buf)
234+
.expect("Failed to read from disk");
235+
}
236+
237+
info!("Raw disk I/O 2 succeeded");
238+
bt.close_event(event).unwrap();
239+
bt.free_pool(buf).unwrap();
240+
}
241+
141242
/// Run various tests on a special test disk. The disk is created by
142243
/// xtask/src/disk.rs.
143244
pub fn test_known_disk(image: Handle, bt: &BootServices) {
@@ -154,6 +255,10 @@ pub fn test_known_disk(image: Handle, bt: &BootServices) {
154255

155256
let mut found_test_disk = false;
156257
for handle in handles {
258+
// Test raw disk I/O first
259+
test_raw_disk_io(handle, image, bt);
260+
test_raw_disk_io2(handle, image, bt);
261+
157262
let mut sfs = bt
158263
.open_protocol::<SimpleFileSystem>(
159264
OpenProtocolParams {

0 commit comments

Comments
 (0)