smbioslib/windows/
win_struct.rs1use serde::{ser::SerializeStruct, Serialize, Serializer};
2use std::{
3 convert::TryInto,
4 fmt,
5 io::{Error, ErrorKind},
6};
7
8use crate::core::{SMBiosData, SMBiosVersion};
9
10pub struct WinSMBiosData {
16 windows_header: Vec<u8>,
17 pub smbios_data: SMBiosData,
19}
20
21impl WinSMBiosData {
22 pub const USED20_CALLING_METHOD_OFFSET: usize = 0usize;
24
25 pub const SMBIOS_MAJOR_VERSION_OFFSET: usize = 1usize;
27
28 pub const SMBIOS_MINOR_VERSION_OFFSET: usize = 2usize;
30
31 pub const DMI_REVISION_OFFSET: usize = 3usize;
33
34 pub const TABLE_DATA_LENGTH_OFFSET: usize = 4usize;
36
37 pub const SMBIOS_TABLE_DATA_OFFSET: usize = 8usize;
39
40 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 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 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 pub fn raw_smbios_data(&self) -> &[u8] {
96 self.windows_header.as_slice()
97 }
98
99 pub fn used20_calling_method(&self) -> u8 {
101 self.windows_header[WinSMBiosData::USED20_CALLING_METHOD_OFFSET]
102 }
103
104 pub fn smbios_major_version(&self) -> u8 {
106 self.windows_header[WinSMBiosData::SMBIOS_MAJOR_VERSION_OFFSET]
107 }
108
109 pub fn smbios_minor_version(&self) -> u8 {
111 self.windows_header[WinSMBiosData::SMBIOS_MINOR_VERSION_OFFSET]
112 }
113
114 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 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 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 let struct_data = vec![0x00u8, 0x03, 0x03];
185 assert!(!WinSMBiosData::is_valid_win_smbios_data(&struct_data));
186
187 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}