smbioslib/windows/
win_struct.rs

1use serde::{ser::SerializeStruct, Serialize, Serializer};
2use std::{
3    convert::TryInto,
4    fmt,
5    io::{Error, ErrorKind},
6};
7
8use crate::core::{SMBiosData, SMBiosVersion};
9
10/// # Raw SMBIOS Data
11///
12/// When Windows kernel32 [GetSystemFirmwareTable](https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemfirmwaretable) function is called for RSMB,
13/// the raw SMBIOS table provider ('RSMB') it retrieves the contents of this
14/// raw SMBIOS firmware table structure.
15pub struct WinSMBiosData {
16    windows_header: Vec<u8>,
17    /// SMBios table data
18    pub smbios_data: SMBiosData,
19}
20
21impl WinSMBiosData {
22    /// Offset of the Used20CallingMethod field (0)
23    pub const USED20_CALLING_METHOD_OFFSET: usize = 0usize;
24
25    /// Offset of the SMBIOSMajorVersion field (1)
26    pub const SMBIOS_MAJOR_VERSION_OFFSET: usize = 1usize;
27
28    /// Offset of the SMBIOSMinorVersion field (2)
29    pub const SMBIOS_MINOR_VERSION_OFFSET: usize = 2usize;
30
31    /// Offset of the DMIRevision field (3)
32    pub const DMI_REVISION_OFFSET: usize = 3usize;
33
34    /// Offset of the Length field (4)
35    pub const TABLE_DATA_LENGTH_OFFSET: usize = 4usize;
36
37    /// Offset of the SMBIOSTableData field (8)
38    pub const SMBIOS_TABLE_DATA_OFFSET: usize = 8usize;
39
40    /// Creates an instance of [WinSMBiosData]
41    ///
42    /// To retrieve this structure on a windows system call load_windows_smbios_data().
43    ///
44    /// The new() is provided publicly to allow loading data from other sources
45    /// such as a file or from memory array as is done with testing.
46    pub fn new(raw_smbios_data: Vec<u8>) -> Result<WinSMBiosData, Error> {
47        if !WinSMBiosData::is_valid_win_smbios_data(&raw_smbios_data) {
48            Err(Error::new(
49                ErrorKind::InvalidData,
50                "Invalid WinSMBiosData structure",
51            ))
52        } else {
53            let windows_header =
54                Vec::from(&raw_smbios_data[..WinSMBiosData::SMBIOS_TABLE_DATA_OFFSET]);
55            let version = WinSMBiosData::version_from_raw_header(&windows_header);
56            Ok(WinSMBiosData {
57                windows_header,
58                smbios_data: {
59                    SMBiosData::from_vec_and_version(
60                        Vec::from(&raw_smbios_data[WinSMBiosData::SMBIOS_TABLE_DATA_OFFSET..]),
61                        Some(version),
62                    )
63                },
64            })
65        }
66    }
67
68    /// Verify if a block of data is a valid WinSMBiosData structure
69    ///
70    /// This only checks if the structure itself is valid and not whether the contained
71    /// [SMBiosData] structure is valid or not.
72    pub fn is_valid_win_smbios_data(raw_data: &Vec<u8>) -> bool {
73        let length = raw_data.len();
74        if length <= WinSMBiosData::SMBIOS_TABLE_DATA_OFFSET {
75            return false;
76        }
77
78        // retrieve the table data length field
79        let slice = raw_data
80            .get(
81                WinSMBiosData::TABLE_DATA_LENGTH_OFFSET
82                    ..WinSMBiosData::TABLE_DATA_LENGTH_OFFSET + 4,
83            )
84            .unwrap();
85        let table_data_length = u32::from_le_bytes(
86            slice
87                .try_into()
88                .expect("array length does not match type width"),
89        ) as usize;
90
91        table_data_length == length - WinSMBiosData::SMBIOS_TABLE_DATA_OFFSET
92    }
93
94    /// The raw SMBIOS data this structure is wrapping
95    pub fn raw_smbios_data(&self) -> &[u8] {
96        self.windows_header.as_slice()
97    }
98
99    /// Used20CallingMethod
100    pub fn used20_calling_method(&self) -> u8 {
101        self.windows_header[WinSMBiosData::USED20_CALLING_METHOD_OFFSET]
102    }
103
104    /// SMBIOS major version
105    pub fn smbios_major_version(&self) -> u8 {
106        self.windows_header[WinSMBiosData::SMBIOS_MAJOR_VERSION_OFFSET]
107    }
108
109    /// SMBIOS minor version
110    pub fn smbios_minor_version(&self) -> u8 {
111        self.windows_header[WinSMBiosData::SMBIOS_MINOR_VERSION_OFFSET]
112    }
113
114    /// DMI revision
115    pub fn dmi_revision(&self) -> u8 {
116        self.windows_header[WinSMBiosData::DMI_REVISION_OFFSET]
117    }
118
119    fn version_from_raw_header(windows_header: &Vec<u8>) -> SMBiosVersion {
120        SMBiosVersion {
121            major: windows_header[WinSMBiosData::SMBIOS_MAJOR_VERSION_OFFSET],
122            minor: windows_header[WinSMBiosData::SMBIOS_MINOR_VERSION_OFFSET],
123            revision: windows_header[WinSMBiosData::DMI_REVISION_OFFSET],
124        }
125    }
126
127    /// Length of the smbios table data
128    pub fn table_data_length(&self) -> u32 {
129        let slice = self
130            .windows_header
131            .get(
132                WinSMBiosData::TABLE_DATA_LENGTH_OFFSET
133                    ..WinSMBiosData::TABLE_DATA_LENGTH_OFFSET + 4,
134            )
135            .unwrap();
136        u32::from_le_bytes(
137            slice
138                .try_into()
139                .expect("array length does not match type width"),
140        )
141    }
142}
143
144impl fmt::Debug for WinSMBiosData {
145    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
146        fmt.debug_struct(std::any::type_name::<WinSMBiosData>())
147            .field("used20_calling_method", &self.used20_calling_method())
148            .field("smbios_major_version", &self.smbios_major_version())
149            .field("smbios_minor_version", &self.smbios_minor_version())
150            .field("dmi_revision", &self.dmi_revision())
151            .field("table_data_length", &self.table_data_length())
152            .field("smbios_data", &self.smbios_data)
153            .finish()
154    }
155}
156
157impl Serialize for WinSMBiosData {
158    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
159    where
160        S: Serializer,
161    {
162        let mut state = serializer.serialize_struct("WinSMBiosData", 6)?;
163        state.serialize_field("used20_calling_method", &self.used20_calling_method())?;
164        state.serialize_field("smbios_major_version", &self.smbios_major_version())?;
165        state.serialize_field("smbios_minor_version", &self.smbios_minor_version())?;
166        state.serialize_field("dmi_revision", &self.dmi_revision())?;
167        state.serialize_field("table_data_length", &self.table_data_length())?;
168        state.serialize_field("smbios_data", &self.smbios_data)?;
169        state.end()
170    }
171}
172
173#[cfg(test)]
174mod tests {
175    use super::*;
176
177    #[test]
178    fn test_is_valid_raw_smbios_data() {
179        // Good structure (lengths are correct)
180        let struct_data = vec![0x00u8, 0x03, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0xAB];
181        assert!(WinSMBiosData::is_valid_win_smbios_data(&struct_data));
182
183        // Bad structure (too short)
184        let struct_data = vec![0x00u8, 0x03, 0x03];
185        assert!(!WinSMBiosData::is_valid_win_smbios_data(&struct_data));
186
187        // Bad structure (bad table data length)
188        let struct_data = vec![0x00u8, 0x03, 0x03, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xAB];
189        assert!(!WinSMBiosData::is_valid_win_smbios_data(&struct_data));
190    }
191
192    #[test]
193    fn test_win_smbios_data_headers() {
194        let raw_win_data = vec![0x00u8, 0x03, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00];
195
196        let win_smbios_data = WinSMBiosData::new(raw_win_data).unwrap();
197
198        assert_eq!(win_smbios_data.used20_calling_method(), 0x00);
199        assert_eq!(win_smbios_data.smbios_major_version(), 0x03);
200        assert_eq!(win_smbios_data.smbios_minor_version(), 0x04);
201        assert_eq!(win_smbios_data.dmi_revision(), 0x00);
202        assert_eq!(win_smbios_data.table_data_length(), 0x01);
203    }
204
205    #[test]
206    fn test_win_smbios_data_constructor() {
207        let raw_win_data = vec![0x00u8, 0x03, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0xFF];
208
209        let win_smbios_data = WinSMBiosData::new(raw_win_data.clone()).unwrap();
210
211        assert_eq!(
212            win_smbios_data.windows_header.as_slice(),
213            &raw_win_data[..8]
214        );
215    }
216}