Skip to content

Commit ff4607d

Browse files
committed
uefi: Add safe protocol bindings for EFI_EXT_SCSI_PASS_THRU_PROTOCOL
1 parent cbd28fe commit ff4607d

File tree

9 files changed

+729
-2
lines changed

9 files changed

+729
-2
lines changed

uefi-raw/src/protocol/scsi.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ pub struct ScsiIoScsiRequestPacket {
8484
///
8585
/// A `timeout` value of 0 indicates that the function will wait indefinitely for
8686
/// the execution to complete. If the execution time exceeds the specified `timeout`
87-
/// (greater than 0), the function will return `EFI_TIMEOUT`.
87+
/// (greater than 0), the function will return [`Status::TIMEOUT`].
8888
pub timeout: u64,
8989

9090
/// A pointer to the data buffer for reading from the device in read and bidirectional commands.

uefi-test-runner/src/proto/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ pub fn test() {
2525
shell_params::test();
2626
string::test();
2727
misc::test();
28+
scsi::test();
2829

2930
#[cfg(any(
3031
target_arch = "x86",
@@ -73,6 +74,7 @@ mod misc;
7374
mod network;
7475
mod pi;
7576
mod rng;
77+
mod scsi;
7678
mod shell_params;
7779
#[cfg(any(
7880
target_arch = "x86",
+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// SPDX-License-Identifier: MIT OR Apache-2.0
2+
3+
mod pass_thru;
4+
5+
pub fn test() {
6+
pass_thru::test();
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
// SPDX-License-Identifier: MIT OR Apache-2.0
2+
3+
use uefi::proto::scsi::pass_thru::ExtScsiPassThru;
4+
use uefi::proto::scsi::ScsiRequestBuilder;
5+
6+
pub fn test() {
7+
info!("Running extended SCSI Pass Thru tests");
8+
test_allocating_api();
9+
test_reusing_buffer_api();
10+
}
11+
12+
fn test_allocating_api() {
13+
let scsi_ctrl_handles = uefi::boot::find_handles::<ExtScsiPassThru>().unwrap();
14+
assert_eq!(scsi_ctrl_handles.len(), 2);
15+
16+
let mut found_drive = false;
17+
18+
for handle in scsi_ctrl_handles {
19+
let scsi_pt = uefi::boot::open_protocol_exclusive::<ExtScsiPassThru>(handle).unwrap();
20+
for device in scsi_pt.iter_devices() {
21+
// see: https://www.seagate.com/files/staticfiles/support/docs/manual/Interface%20manuals/100293068j.pdf
22+
// 3.6 INQUIRY command
23+
let request = ScsiRequestBuilder::read(scsi_pt.io_align())
24+
.with_timeout(core::time::Duration::from_millis(500))
25+
.with_command_data(&[0x12, 0x00, 0x00, 0x00, 0xFF, 0x00])
26+
.unwrap()
27+
.with_read_buffer(255)
28+
.unwrap()
29+
.build();
30+
let Ok(response) = device.execute_command(request) else {
31+
continue; // no device
32+
};
33+
let bfr = response.read_buffer().unwrap();
34+
// more no device checks
35+
if bfr.len() < 32 {
36+
continue;
37+
}
38+
if bfr[0] & 0b00011111 == 0x1F {
39+
continue;
40+
}
41+
42+
// found device
43+
let vendor_id = core::str::from_utf8(&bfr[8..16]).unwrap().trim();
44+
let product_id = core::str::from_utf8(&bfr[16..32]).unwrap().trim();
45+
if vendor_id == "uefi-rs" && product_id == "ExtScsiPassThru" {
46+
info!(
47+
"Found Testdisk at: {:?} | {}",
48+
device.target(),
49+
device.lun()
50+
);
51+
found_drive = true;
52+
}
53+
}
54+
}
55+
56+
assert!(found_drive);
57+
}
58+
59+
fn test_reusing_buffer_api() {
60+
let scsi_ctrl_handles = uefi::boot::find_handles::<ExtScsiPassThru>().unwrap();
61+
assert_eq!(scsi_ctrl_handles.len(), 2);
62+
63+
let mut found_drive = false;
64+
65+
for handle in scsi_ctrl_handles {
66+
let scsi_pt = uefi::boot::open_protocol_exclusive::<ExtScsiPassThru>(handle).unwrap();
67+
let mut cmd_bfr = scsi_pt.alloc_io_buffer(6).unwrap();
68+
cmd_bfr.copy_from(&[0x12, 0x00, 0x00, 0x00, 0xFF, 0x00]);
69+
let mut read_bfr = scsi_pt.alloc_io_buffer(255).unwrap();
70+
71+
for device in scsi_pt.iter_devices() {
72+
// see: https://www.seagate.com/files/staticfiles/support/docs/manual/Interface%20manuals/100293068j.pdf
73+
// 3.6 INQUIRY command
74+
let request = ScsiRequestBuilder::read(scsi_pt.io_align())
75+
.with_timeout(core::time::Duration::from_millis(500))
76+
.use_command_buffer(&mut cmd_bfr)
77+
.unwrap()
78+
.use_read_buffer(&mut read_bfr)
79+
.unwrap()
80+
.build();
81+
let Ok(response) = device.execute_command(request) else {
82+
continue; // no device
83+
};
84+
let bfr = response.read_buffer().unwrap();
85+
// more no device checks
86+
if bfr.len() < 32 {
87+
continue;
88+
}
89+
if bfr[0] & 0b00011111 == 0x1F {
90+
continue;
91+
}
92+
93+
// found device
94+
let vendor_id = core::str::from_utf8(&bfr[8..16]).unwrap().trim();
95+
let product_id = core::str::from_utf8(&bfr[16..32]).unwrap().trim();
96+
if vendor_id == "uefi-rs" && product_id == "ExtScsiPassThru" {
97+
info!(
98+
"Found Testdisk at: {:?} | {}",
99+
device.target(),
100+
device.lun()
101+
);
102+
found_drive = true;
103+
}
104+
}
105+
}
106+
107+
assert!(found_drive);
108+
}

uefi/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
- Added `boot::signal_event`.
55
- Added conversions between `proto::network::IpAddress` and `core::net` types.
66
- Added conversions between `proto::network::MacAddress` and the `[u8; 6]` type that's more commonly used to represent MAC addresses.
7+
- Added `proto::scsi::pass_thru::ExtScsiPassThru`.
78

89
## Changed
910
- **Breaking:** Removed `BootPolicyError` as `BootPolicy` construction is no

uefi/src/proto/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ pub mod misc;
2020
pub mod network;
2121
pub mod pi;
2222
pub mod rng;
23+
#[cfg(feature = "alloc")]
24+
pub mod scsi;
2325
pub mod security;
2426
pub mod shell_params;
2527
pub mod shim;

0 commit comments

Comments
 (0)