Skip to content

Commit bca67e0

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

File tree

9 files changed

+741
-2
lines changed

9 files changed

+741
-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,112 @@
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+
15+
// On I440FX and Q35 (both x86 machines), Qemu configures an IDE and a SATA controller
16+
// by default respectively. We manually configure an additional SCSI controller.
17+
// Thus, we should see two controllers with support for EXT_SCSI_PASS_THRU on this platform
18+
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
19+
assert_eq!(scsi_ctrl_handles.len(), 2);
20+
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
21+
assert_eq!(scsi_ctrl_handles.len(), 1);
22+
23+
let mut found_drive = false;
24+
for handle in scsi_ctrl_handles {
25+
let scsi_pt = uefi::boot::open_protocol_exclusive::<ExtScsiPassThru>(handle).unwrap();
26+
for device in scsi_pt.iter_devices() {
27+
// see: https://www.seagate.com/files/staticfiles/support/docs/manual/Interface%20manuals/100293068j.pdf
28+
// 3.6 INQUIRY command
29+
let request = ScsiRequestBuilder::read(scsi_pt.io_align())
30+
.with_timeout(core::time::Duration::from_millis(500))
31+
.with_command_data(&[0x12, 0x00, 0x00, 0x00, 0xFF, 0x00])
32+
.unwrap()
33+
.with_read_buffer(255)
34+
.unwrap()
35+
.build();
36+
let Ok(response) = device.execute_command(request) else {
37+
continue; // no device
38+
};
39+
let bfr = response.read_buffer().unwrap();
40+
// more no device checks
41+
if bfr.len() < 32 {
42+
continue;
43+
}
44+
if bfr[0] & 0b00011111 == 0x1F {
45+
continue;
46+
}
47+
48+
// found device
49+
let vendor_id = core::str::from_utf8(&bfr[8..16]).unwrap().trim();
50+
let product_id = core::str::from_utf8(&bfr[16..32]).unwrap().trim();
51+
if vendor_id == "uefi-rs" && product_id == "ExtScsiPassThru" {
52+
info!(
53+
"Found Testdisk at: {:?} | {}",
54+
device.target(),
55+
device.lun()
56+
);
57+
found_drive = true;
58+
}
59+
}
60+
}
61+
62+
assert!(found_drive);
63+
}
64+
65+
fn test_reusing_buffer_api() {
66+
let scsi_ctrl_handles = uefi::boot::find_handles::<ExtScsiPassThru>().unwrap();
67+
68+
let mut found_drive = false;
69+
for handle in scsi_ctrl_handles {
70+
let scsi_pt = uefi::boot::open_protocol_exclusive::<ExtScsiPassThru>(handle).unwrap();
71+
let mut cmd_bfr = scsi_pt.alloc_io_buffer(6).unwrap();
72+
cmd_bfr.copy_from(&[0x12, 0x00, 0x00, 0x00, 0xFF, 0x00]);
73+
let mut read_bfr = scsi_pt.alloc_io_buffer(255).unwrap();
74+
75+
for device in scsi_pt.iter_devices() {
76+
// see: https://www.seagate.com/files/staticfiles/support/docs/manual/Interface%20manuals/100293068j.pdf
77+
// 3.6 INQUIRY command
78+
let request = ScsiRequestBuilder::read(scsi_pt.io_align())
79+
.with_timeout(core::time::Duration::from_millis(500))
80+
.use_command_buffer(&mut cmd_bfr)
81+
.unwrap()
82+
.use_read_buffer(&mut read_bfr)
83+
.unwrap()
84+
.build();
85+
let Ok(response) = device.execute_command(request) else {
86+
continue; // no device
87+
};
88+
let bfr = response.read_buffer().unwrap();
89+
// more no device checks
90+
if bfr.len() < 32 {
91+
continue;
92+
}
93+
if bfr[0] & 0b00011111 == 0x1F {
94+
continue;
95+
}
96+
97+
// found device
98+
let vendor_id = core::str::from_utf8(&bfr[8..16]).unwrap().trim();
99+
let product_id = core::str::from_utf8(&bfr[16..32]).unwrap().trim();
100+
if vendor_id == "uefi-rs" && product_id == "ExtScsiPassThru" {
101+
info!(
102+
"Found Testdisk at: {:?} | {}",
103+
device.target(),
104+
device.lun()
105+
);
106+
found_drive = true;
107+
}
108+
}
109+
}
110+
111+
assert!(found_drive);
112+
}

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)