smbioslib/structs/types/
system_information.rs

1use crate::core::{strings::*, UndefinedStruct};
2use crate::SMBiosStruct;
3use serde::{ser::SerializeStruct, Serialize, Serializer};
4use std::{
5    array::TryFromSliceError,
6    convert::{TryFrom, TryInto},
7    fmt,
8    ops::Deref,
9};
10
11/// # System Information (Type 1)
12///
13/// The information in this structure defines attributes of the overall system and is intended to be associated
14/// with the Component ID group of the system’s MIF. An SMBIOS implementation is associated with a single
15/// system instance and contains one and only one System Information (Type 1) structure.
16///
17/// Compliant with:
18/// DMTF SMBIOS Reference Specification 3.4.0 (DSP0134)
19/// Document Date: 2020-07-17
20pub struct SMBiosSystemInformation<'a> {
21    parts: &'a UndefinedStruct,
22}
23
24impl<'a> SMBiosStruct<'a> for SMBiosSystemInformation<'a> {
25    const STRUCT_TYPE: u8 = 1u8;
26
27    fn new(parts: &'a UndefinedStruct) -> Self {
28        Self { parts }
29    }
30
31    fn parts(&self) -> &'a UndefinedStruct {
32        self.parts
33    }
34}
35
36impl<'a> SMBiosSystemInformation<'a> {
37    /// Manufacturer
38    pub fn manufacturer(&self) -> SMBiosString {
39        self.parts.get_field_string(0x04)
40    }
41
42    /// Product name
43    pub fn product_name(&self) -> SMBiosString {
44        self.parts.get_field_string(0x05)
45    }
46
47    /// Version
48    pub fn version(&self) -> SMBiosString {
49        self.parts.get_field_string(0x06)
50    }
51
52    /// Serial number
53    pub fn serial_number(&self) -> SMBiosString {
54        self.parts.get_field_string(0x07)
55    }
56
57    /// System UUID
58    pub fn uuid(&self) -> Option<SystemUuidData> {
59        self.parts
60            .get_field_data(0x08, 0x18)
61            .map(|raw| SystemUuidData::try_from(raw).expect("A GUID is 0x10 bytes"))
62    }
63
64    /// Wake-up type
65    ///
66    /// Identifies the event that caused the system to power up.
67    pub fn wakeup_type(&self) -> Option<SystemWakeUpTypeData> {
68        self.parts
69            .get_field_byte(0x18)
70            .map(|raw| SystemWakeUpTypeData::from(raw))
71    }
72
73    /// SKU Number
74    ///
75    /// This text string identifies a particular computer
76    /// configuration for sale. It is sometimes also
77    /// called a product ID or purchase order number.
78    /// This number is frequently found in existing
79    /// fields, but there is no standard format.
80    /// Typically for a given system board from a
81    /// given OEM, there are tens of unique
82    /// processor, memory, hard drive, and optical
83    /// drive configurations.
84    pub fn sku_number(&self) -> SMBiosString {
85        self.parts.get_field_string(0x19)
86    }
87
88    /// Family
89    ///
90    /// This text string identifies the family to which a
91    /// particular computer belongs. A family refers to
92    /// a set of computers that are similar but not
93    /// identical from a hardware or software point of
94    /// view. Typically, a family is composed of
95    /// different computer models, which have
96    /// different configurations and pricing points.
97    /// Computers in the same family often have
98    /// similar branding and cosmetic features.
99    pub fn family(&self) -> SMBiosString {
100        self.parts.get_field_string(0x1A)
101    }
102}
103
104impl fmt::Debug for SMBiosSystemInformation<'_> {
105    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
106        fmt.debug_struct(std::any::type_name::<SMBiosSystemInformation<'_>>())
107            .field("header", &self.parts.header)
108            .field("manufacturer", &self.manufacturer())
109            .field("product_name", &self.product_name())
110            .field("version", &self.version())
111            .field("serial_number", &self.serial_number())
112            .field("uuid", &self.uuid())
113            .field("wakeup_type", &self.wakeup_type())
114            .field("sku_number", &self.sku_number())
115            .field("family", &self.family())
116            .finish()
117    }
118}
119
120impl Serialize for SMBiosSystemInformation<'_> {
121    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
122    where
123        S: Serializer,
124    {
125        let mut state = serializer.serialize_struct("SMBiosSystemInformation", 9)?;
126        state.serialize_field("header", &self.parts.header)?;
127        state.serialize_field("manufacturer", &self.manufacturer())?;
128        state.serialize_field("product_name", &self.product_name())?;
129        state.serialize_field("version", &self.version())?;
130        state.serialize_field("serial_number", &self.serial_number())?;
131        state.serialize_field("uuid", &self.uuid())?;
132        state.serialize_field("wakeup_type", &self.wakeup_type())?;
133        state.serialize_field("sku_number", &self.sku_number())?;
134        state.serialize_field("family", &self.family())?;
135        state.end()
136    }
137}
138
139/// # System - UUID Data
140#[derive(Serialize, Debug)]
141pub enum SystemUuidData {
142    /// The ID is not currently present in the system, but it can be set
143    IdNotPresentButSettable,
144    /// The ID is not present in the system
145    IdNotPresent,
146    /// System UUID
147    Uuid(SystemUuid),
148}
149
150impl SystemUuidData {
151    fn new<'a>(array: &'a [u8; 0x10]) -> SystemUuidData {
152        if array.iter().all(|&x| x == 0) {
153            SystemUuidData::IdNotPresentButSettable
154        } else if array.iter().all(|&x| x == 0xFF) {
155            SystemUuidData::IdNotPresent
156        } else {
157            SystemUuidData::Uuid(SystemUuid::from(array))
158        }
159    }
160}
161
162impl<'a> TryFrom<&'a [u8]> for SystemUuidData {
163    type Error = TryFromSliceError;
164
165    fn try_from(raw: &'a [u8]) -> Result<Self, Self::Error> {
166        <&[u8; 0x10]>::try_from(raw).and_then(|array| Ok(SystemUuidData::new(array)))
167    }
168}
169
170impl fmt::Display for SystemUuidData {
171    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
172        match &*self {
173            SystemUuidData::IdNotPresent => write!(f, "IdNotPresent"),
174            SystemUuidData::IdNotPresentButSettable => write!(f, "IdNotPresentButSettable"),
175            SystemUuidData::Uuid(_system_uuid) => write!(f, "{}", &_system_uuid),
176        }
177    }
178}
179
180/// # System - UUID
181#[derive(PartialEq, Eq)]
182pub struct SystemUuid {
183    /// Raw byte array for this UUID
184    pub raw: [u8; 0x10],
185}
186
187impl SystemUuid {
188    /// Low field of the timestamp
189    pub fn time_low(&self) -> u32 {
190        u32::from_le_bytes(self.raw[..0x4].try_into().expect("incorrect size"))
191    }
192
193    /// Middle field of the timestamp
194    pub fn time_mid(&self) -> u16 {
195        u16::from_le_bytes(self.raw[0x4..0x6].try_into().expect("incorrect size"))
196    }
197
198    /// High field of the timestamp multiplexed with the version number
199    pub fn time_high_and_version(&self) -> u16 {
200        u16::from_le_bytes(self.raw[0x6..0x8].try_into().expect("incorrect size"))
201    }
202
203    /// High field of the clock sequence multiplexed with the variant
204    pub fn clock_seq_high_and_reserved(&self) -> u8 {
205        self.raw[0x8]
206    }
207
208    /// Low field of the clock sequence
209    pub fn clock_seq_low(&self) -> u8 {
210        self.raw[0x9]
211    }
212
213    /// Spatially unique node identifier
214    pub fn node(&self) -> &[u8; 6] {
215        self.raw[0xA..0x10].try_into().expect("incorrect size")
216    }
217}
218
219impl<'a> From<&'a [u8; 0x10]> for SystemUuid {
220    fn from(raw: &'a [u8; 0x10]) -> Self {
221        SystemUuid { raw: raw.clone() }
222    }
223}
224
225impl fmt::Display for SystemUuid {
226    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
227        // Example output:
228        // "00360fe7-d4d5-11e5-9c43-bc0000f00000"
229        // <TimeLow>-<TimeMid>-<TimeHiAndVersion>-<ClockSeqHiAndReserved><ClockSeqLow>-<Node[6]>
230
231        // Format is described in RFC4122, but the actual field contents are opaque and not
232        // significant to the SMBIOS specification, which is only concerned with the byte order.
233        // http://www.ietf.org/rfc/rfc4122.txt
234        // RFC4122: The hexadecimal values "a" through "f" are output as
235        // lower case characters and are case insensitive on input.
236        write!(
237            f,
238            "{:08x}-{:04x}-{:04x}-{:02x}{:02x}-",
239            self.time_low(),
240            self.time_mid(),
241            self.time_high_and_version(),
242            self.clock_seq_high_and_reserved(),
243            self.clock_seq_low()
244        )?;
245
246        self.node().iter().fold(Ok(()), |result, node_byte| {
247            result.and_then(|_| write!(f, "{:02x}", node_byte))
248        })
249    }
250}
251
252impl fmt::Debug for SystemUuid {
253    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
254        write!(f, "{}", &self)
255    }
256}
257
258impl Serialize for SystemUuid {
259    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
260    where
261        S: Serializer,
262    {
263        serializer.serialize_str(format!("{}", self).as_str())
264    }
265}
266
267/// # System - Wake-up Type Data
268pub struct SystemWakeUpTypeData {
269    /// Raw value
270    ///
271    /// _raw_ is most useful when _value_ is None.
272    /// This is most likely to occur when the standard was updated but
273    /// this library code has not been updated to match the current
274    /// standard.
275    pub raw: u8,
276    /// The contained [SystemWakeUpType] value
277    pub value: SystemWakeUpType,
278}
279
280impl fmt::Debug for SystemWakeUpTypeData {
281    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
282        fmt.debug_struct(std::any::type_name::<SystemWakeUpTypeData>())
283            .field("raw", &self.raw)
284            .field("value", &self.value)
285            .finish()
286    }
287}
288
289impl Serialize for SystemWakeUpTypeData {
290    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
291    where
292        S: Serializer,
293    {
294        let mut state = serializer.serialize_struct("SystemWakeUpTypeData", 2)?;
295        state.serialize_field("raw", &self.raw)?;
296        state.serialize_field("value", &self.value)?;
297        state.end()
298    }
299}
300
301impl fmt::Display for SystemWakeUpTypeData {
302    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
303        match &self.value {
304            SystemWakeUpType::None => write!(f, "{}", &self.raw),
305            _ => write!(f, "{:?}", &self.value),
306        }
307    }
308}
309
310impl Deref for SystemWakeUpTypeData {
311    type Target = SystemWakeUpType;
312
313    fn deref(&self) -> &Self::Target {
314        &self.value
315    }
316}
317
318/// # System - Wake-up Type
319#[derive(Serialize, Debug, PartialEq, Eq)]
320pub enum SystemWakeUpType {
321    /// Other
322    Other,
323    /// Unknown
324    Unknown,
325    /// APM Timer
326    ApmTimer,
327    /// Modem Ring
328    ModernRing,
329    /// LAN Remote
330    LanRemote,
331    /// Power Switch
332    PowerSwitch,
333    /// PCI PME#
334    PciPme,
335    /// AC Power Restored
336    ACPowerRestored,
337    /// A value unknown to this standard, check the raw value
338    None,
339}
340
341impl From<u8> for SystemWakeUpTypeData {
342    fn from(raw: u8) -> Self {
343        SystemWakeUpTypeData {
344            value: match raw {
345                0x01 => SystemWakeUpType::Other,
346                0x02 => SystemWakeUpType::Unknown,
347                0x03 => SystemWakeUpType::ApmTimer,
348                0x04 => SystemWakeUpType::ModernRing,
349                0x05 => SystemWakeUpType::LanRemote,
350                0x06 => SystemWakeUpType::PowerSwitch,
351                0x07 => SystemWakeUpType::PciPme,
352                0x08 => SystemWakeUpType::ACPowerRestored,
353                _ => SystemWakeUpType::None,
354            },
355            raw,
356        }
357    }
358}
359
360#[cfg(test)]
361mod tests {
362    use super::*;
363
364    #[test]
365    fn unit_test() {
366        let struct_type1 = vec![
367            0x01, 0x1B, 0x01, 0x00, 0x01, 0x02, 0x03, 0x04, 0xD2, 0x01, 0x25, 0x3E, 0x48, 0xE6,
368            0x11, 0xE8, 0xBA, 0xD3, 0x70, 0x20, 0x84, 0x0F, 0x9D, 0x47, 0x06, 0x05, 0x06, b'L',
369            b'E', b'N', b'O', b'V', b'O', 0x00, b'3', b'0', b'B', b'F', b'S', b'0', b'7', b'5',
370            b'0', b'0', 0x00, b'T', b'h', b'i', b'n', b'k', b'S', b't', b'a', b't', b'i', b'o',
371            b'n', b' ', b'P', b'5', b'2', b'0', 0x00, b'M', b'N', b'0', b'6', b'P', b'Q', b'R',
372            b'S', 0x00, b'L', b'E', b'N', b'O', b'V', b'O', b'_', b'M', b'T', b'_', b'3', b'0',
373            b'B', b'F', b'_', b'B', b'U', b'_', b'T', b'h', b'i', b'n', b'k', b'_', b'F', b'M',
374            b'_', b'T', b'h', b'i', b'n', b'k', b'S', b't', b'a', b't', b'i', b'o', b'n', b' ',
375            b'P', b'5', b'2', b'0', 0x00, b'T', b'h', b'i', b'n', b'k', b'S', b't', b'a', b't',
376            b'i', b'o', b'n', b' ', b'P', b'5', b'2', b'0', 0x00, 0x00,
377        ];
378
379        let parts = UndefinedStruct::new(&struct_type1);
380        let test_struct = SMBiosSystemInformation::new(&parts);
381
382        assert_eq!(test_struct.manufacturer().to_string(), "LENOVO".to_string());
383        assert_eq!(
384            test_struct.product_name().to_string(),
385            "30BFS07500".to_string()
386        );
387        assert_eq!(
388            test_struct.version().to_string(),
389            "ThinkStation P520".to_string()
390        );
391        assert_eq!(
392            test_struct.serial_number().to_string(),
393            "MN06PQRS".to_string()
394        );
395        assert_eq!(
396            format!("{:?}", test_struct.uuid()),
397            "Some(Uuid(3e2501d2-e648-e811-bad3-7020840f9d47))".to_string()
398        );
399        assert_eq!(
400            *test_struct.wakeup_type().unwrap(),
401            SystemWakeUpType::PowerSwitch
402        );
403        assert_eq!(
404            test_struct.sku_number().to_string(),
405            "LENOVO_MT_30BF_BU_Think_FM_ThinkStation P520".to_string()
406        );
407        assert_eq!(
408            test_struct.family().to_string(),
409            "ThinkStation P520".to_string()
410        );
411    }
412}