Skip to main content

framework_lib/
nvme.rs

1use std::error::Error;
2use std::fs::File;
3use std::os::unix::io::AsRawFd;
4
5use nix::ioctl_readwrite;
6
7// Constants from the NVMe specification for the Identify Controller data structure.
8const IDENTIFY_BUFFER_SIZE: usize = 4096;
9const MODEL_NUMBER_OFFSET: usize = 24;
10const MODEL_NUMBER_LEN: usize = 40;
11const FIRMWARE_REV_OFFSET: usize = 64;
12const FIRMWARE_REV_LEN: usize = 8;
13
14// NVMe Admin Command opcodes and parameters.
15const NVME_ADMIN_IDENTIFY_OPCODE: u8 = 0x06;
16const NVME_IDENTIFY_CNS_CTRL: u32 = 0x01; // CNS value for "Identify Controller"
17
18#[repr(C)]
19#[derive(Debug, Default)]
20struct NvmeAdminCmd {
21    opcode: u8,
22    flags: u8,
23    rsvd1: u16,
24    nsid: u32,
25    cdw2: u32,
26    cdw3: u32,
27    metadata: u64,
28    addr: u64,
29    metadata_len: u32,
30    data_len: u32,
31    cdw10: u32,
32    cdw11: u32,
33    cdw12: u32,
34    cdw13: u32,
35    cdw14: u32,
36    cdw15: u32,
37    timeout_ms: u32,
38    result: u32,
39}
40
41ioctl_readwrite!(nvme_admin_cmd_ioctl, b'N', 0x41, NvmeAdminCmd);
42
43/// A struct to hold the retrieved device information.
44#[derive(Debug)]
45pub struct NvmeInfo {
46    pub model_number: String,
47    pub firmware_version: String,
48}
49
50fn parse_string(buffer: &[u8], offset: usize, len: usize) -> String {
51    let bytes = &buffer[offset..offset + len];
52    String::from_utf8_lossy(bytes)
53        .trim_end_matches('\0')
54        .trim()
55        .to_string()
56}
57
58/// Sends an NVMe Identify Controller command and returns the firmware version.
59pub fn get_nvme_firmware_version(device_path: &str) -> Result<NvmeInfo, Box<dyn Error>> {
60    let file = File::open(device_path)
61        .map_err(|e| format!("Failed to open NVMe device {}: {}", device_path, e))?;
62    let fd = file.as_raw_fd();
63
64    let mut buffer = vec![0u8; IDENTIFY_BUFFER_SIZE];
65    let mut cmd = NvmeAdminCmd {
66        opcode: NVME_ADMIN_IDENTIFY_OPCODE,
67        addr: buffer.as_mut_ptr() as u64,
68        data_len: buffer.len() as u32,
69        cdw10: NVME_IDENTIFY_CNS_CTRL,
70        ..Default::default()
71    };
72
73    let status = unsafe { nvme_admin_cmd_ioctl(fd, &mut cmd)? };
74    if status != 0 {
75        return Err(format!("NVMe command failed with status code: {:#x}", status).into());
76    }
77
78    let model_number = parse_string(&buffer, MODEL_NUMBER_OFFSET, MODEL_NUMBER_LEN);
79    let firmware_version = parse_string(&buffer, FIRMWARE_REV_OFFSET, FIRMWARE_REV_LEN);
80
81    Ok(NvmeInfo {
82        model_number,
83        firmware_version,
84    })
85}