Skip to content

Commit 6ad7495

Browse files
committed
uefi: Add safe bindings for EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL
1 parent 6ab3d14 commit 6ad7495

File tree

9 files changed

+637
-7
lines changed

9 files changed

+637
-7
lines changed

Diff for: uefi-raw/src/protocol/nvme.rs

+66-7
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,63 @@ use crate::Status;
55
use core::ffi::c_void;
66
use uguid::{guid, Guid};
77

8-
#[derive(Debug)]
8+
bitflags::bitflags! {
9+
/// In an NVMe command, the `flags` field specifies which cdw (command specific word)
10+
/// contains a value.
11+
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
12+
#[repr(transparent)]
13+
pub struct NvmExpressCommandCdwValidity: u8 {
14+
const CDW_2 = 0x01;
15+
const CDW_3 = 0x02;
16+
const CDW_10 = 0x04;
17+
const CDW_11 = 0x08;
18+
const CDW_12 = 0x10;
19+
const CDW_13 = 0x20;
20+
const CDW_14 = 0x40;
21+
const CDW_15 = 0x80;
22+
}
23+
24+
/// Represents the `EFI_NVM_EXPRESS_PASS_THRU_ATTRIBUTES_*` defines from the UEFI specification.
25+
///
26+
/// # UEFI Specification Description
27+
/// Tells if the interface is for physical NVM Express controllers or logical NVM Express controllers.
28+
///
29+
/// Drivers for non-RAID NVM Express controllers will set both the `PHYSICAL` and the `LOGICAL` bit.
30+
///
31+
/// Drivers for RAID controllers that allow access to the underlying physical controllers will produces
32+
/// two protocol instances. One where the `LOGICAL` bit is set (representing the logical RAID volume),
33+
/// and one where the `PHYSICAL` bit is set, which can be used to access the underlying NVMe controllers.
34+
///
35+
/// Drivers for RAID controllers that do not allow access of the underlying NVMe controllers will only
36+
/// produce one protocol instance for the logical RAID volume with the `LOGICAL` bit set.
37+
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
38+
#[repr(transparent)]
39+
pub struct NvmExpressPassThruAttributes: u32 {
40+
/// If this bit is set, the interface is for directly addressable namespaces.
41+
const PHYSICAL = 0x0001;
42+
43+
/// If this bit is set, the interface is for a single logical namespace comprising multiple namespaces.
44+
const LOGICAL = 0x0002;
45+
46+
/// If this bit is set, the interface supports both blocking and non-blocking I/O.
47+
/// - All interfaces must support blocking I/O, but this bit indicates that non-blocking I/O is also supported.
48+
const NONBLOCKIO = 0x0004;
49+
50+
/// If this bit is set, the interface supports the NVM Express command set.
51+
const CMD_SET_NVM = 0x0008;
52+
}
53+
}
54+
55+
#[derive(Clone, Debug)]
956
#[repr(C)]
1057
pub struct NvmExpressPassThruMode {
11-
pub attributes: u32,
58+
pub attributes: NvmExpressPassThruAttributes,
1259
pub io_align: u32,
1360
pub nvme_version: u32,
1461
}
1562

1663
/// This structure maps to the NVM Express specification Submission Queue Entry
17-
#[derive(Debug)]
64+
#[derive(Debug, Default)]
1865
#[repr(C)]
1966
pub struct NvmExpressCommand {
2067
pub cdw0: u32,
@@ -30,8 +77,20 @@ pub struct NvmExpressCommand {
3077
pub cdw15: u32,
3178
}
3279

80+
newtype_enum! {
81+
/// Type of queues an NVMe command can be placed into
82+
/// (Which queue a command should be placed into depends on the command)
83+
#[derive(Default)]
84+
pub enum NvmExpressQueueType: u8 => {
85+
/// Admin Submission Queue
86+
ADMIN = 0,
87+
/// 1) I/O Submission Queue
88+
IO = 1,
89+
}
90+
}
91+
3392
/// This structure maps to the NVM Express specification Completion Queue Entry
34-
#[derive(Debug)]
93+
#[derive(Debug, Default)]
3594
#[repr(C)]
3695
pub struct NvmExpressCompletion {
3796
pub dw0: u32,
@@ -48,7 +107,7 @@ pub struct NvmExpressPassThruCommandPacket {
48107
pub transfer_length: u32,
49108
pub meta_data_buffer: *mut c_void,
50109
pub meta_data_length: u32,
51-
pub queue_type: u8,
110+
pub queue_type: NvmExpressQueueType,
52111
pub nvme_cmd: *const NvmExpressCommand,
53112
pub nvme_completion: *mut NvmExpressCompletion,
54113
}
@@ -58,7 +117,7 @@ pub struct NvmExpressPassThruCommandPacket {
58117
pub struct NvmExpressPassThruProtocol {
59118
pub mode: *const NvmExpressPassThruMode,
60119
pub pass_thru: unsafe extern "efiapi" fn(
61-
this: *const Self,
120+
this: *mut Self,
62121
namespace_id: u32,
63122
packet: *mut NvmExpressPassThruCommandPacket,
64123
event: *mut c_void,
@@ -68,7 +127,7 @@ pub struct NvmExpressPassThruProtocol {
68127
pub build_device_path: unsafe extern "efiapi" fn(
69128
this: *const Self,
70129
namespace_id: u32,
71-
device_path: *mut *mut DevicePathProtocol,
130+
device_path: *mut *const DevicePathProtocol,
72131
) -> Status,
73132
pub get_namespace: unsafe extern "efiapi" fn(
74133
this: *const Self,

Diff for: 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+
nvme::test();
2829

2930
#[cfg(any(
3031
target_arch = "x86",
@@ -71,6 +72,7 @@ mod loaded_image;
7172
mod media;
7273
mod misc;
7374
mod network;
75+
mod nvme;
7476
mod pi;
7577
mod rng;
7678
mod shell_params;

Diff for: uefi-test-runner/src/proto/nvme/mod.rs

+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+
}

Diff for: uefi-test-runner/src/proto/nvme/pass_thru.rs

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// SPDX-License-Identifier: MIT OR Apache-2.0
2+
3+
use core::time::Duration;
4+
use uefi::boot;
5+
use uefi::proto::device_path::text::{AllowShortcuts, DisplayOnly};
6+
use uefi::proto::device_path::DevicePath;
7+
use uefi::proto::media::block::BlockIO;
8+
use uefi::proto::nvme::pass_thru::NvmePassThru;
9+
use uefi::proto::nvme::{NvmeQueueType, NvmeRequestBuilder};
10+
11+
pub fn test() {
12+
info!("Running NVMe PassThru tests");
13+
14+
assert!(has_nvme_drive());
15+
}
16+
17+
fn has_nvme_drive() -> bool {
18+
let block_io_handles = boot::find_handles::<BlockIO>().unwrap();
19+
for handle in block_io_handles {
20+
let Ok(device_path) = boot::open_protocol_exclusive::<DevicePath>(handle) else {
21+
continue;
22+
};
23+
let mut device_path = &*device_path;
24+
25+
let Ok(nvme_pt_handle) = boot::locate_device_path::<NvmePassThru>(&mut device_path) else {
26+
continue;
27+
};
28+
let nvme_pt = boot::open_protocol_exclusive::<NvmePassThru>(nvme_pt_handle).unwrap();
29+
let device_path_str = device_path
30+
.to_string(DisplayOnly(true), AllowShortcuts(false))
31+
.unwrap();
32+
info!("- Successfully opened NVMe: {}", device_path_str);
33+
let mut nvme_ctrl = nvme_pt.controller();
34+
35+
let request = NvmeRequestBuilder::new(nvme_pt.io_align(), 0x06, NvmeQueueType::ADMIN)
36+
.with_timeout(Duration::from_millis(500))
37+
.with_cdw10(1) // we want info about controller
38+
.with_transfer_buffer(4096)
39+
.unwrap()
40+
.build();
41+
let result = nvme_ctrl.execute_command(request);
42+
if let Ok(result) = result {
43+
let bfr = result.transfer_buffer().unwrap();
44+
let serial = core::str::from_utf8(&bfr[4..24]).unwrap().trim();
45+
info!("Found NVMe with serial: '{}'", serial);
46+
if serial == "uefi-rsNvmePassThru" {
47+
return true;
48+
}
49+
}
50+
}
51+
52+
false
53+
}

Diff for: uefi/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
- Added `mem::AlignedBuffer`.
99
- Added `proto::device_path::DevicePath::append_path()`.
1010
- Added `proto::device_path::DevicePath::append_node()`.
11+
- Added `proto::nvme::pass_thru::NvmePassThru`.
1112

1213
## Changed
1314
- **Breaking:** Removed `BootPolicyError` as `BootPolicy` construction is no

Diff for: uefi/src/proto/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ pub mod loaded_image;
1818
pub mod media;
1919
pub mod misc;
2020
pub mod network;
21+
#[cfg(feature = "alloc")]
22+
pub mod nvme;
2123
pub mod pi;
2224
pub mod rng;
2325
pub mod security;

0 commit comments

Comments
 (0)