dmidecode/
lib.rs

1//! # DMIDECODE
2//!
3//! This library reports information about system's hardware as described in system BIOS according
4//! to the SMBIOS/DMI standard. Each SMBIOS type refers to separate struct.
5//!
6//! SMBIOS specification defines the following data structures:
7//! - [BIOS Information](structures::bios "structures::bios") (Type 0)
8//! - [System Information](structures::system "structures::system") (Type 1)
9//! - [Baseboard (or Module) Information](structures::baseboard "structures::baseboard") (Type 2)
10//! - [System Enclosure or Chassis](structures::enclosure "structures::enclosure") (Type 3)
11//! - [Processor Information](structures::processor "structures::processor") (Type 4)
12//! - Memory Controller Information (Type 5, Obsolete)
13//! - Memory Module Information (Type 6, Obsolete)
14//! - [Cache Information](structures::cache "structures::cache") (Type 7)
15//! - [Port Connector Information](structures::port_connector "structures::port_connector") (Type 8)
16//! - [System Slots](structures::system_slots "structures::system_slots") (Type 9)
17//! - On Board Devices Information (Type 10, Obsolete)
18//! - [OEM Strings](structures::oem_strings "structures::oem_strings") (Type 11)
19//! - [System Configuration Options](structures::system_configuration_options "structures::system_configuration_options") (Type 12)
20//! - [BIOS Language Information](structures::bios_language "structures::bios_language") (Type 13)
21//! - [Group Associations](structures::group_associations "structures::group_associations") (Type 14)
22//! - [System Event Log](structures::system_event_log "structures::system_event_log") (Type 15)
23//! - [Physical Memory Array](structures::physical_memory_array "structures::physical_memory_array") (Type 16)
24//! - [Memory Device](structures::memory_device "structures::memory_device") (Type 17)
25//! - [32-Bit Memory Error Information](structures::memory_error_32 "structures::memory_error_32") (Type 18)
26//! - [Memory Array Mapped Address](structures::memory_array_mapped_address "structures::memory_array_mapped_address") (Type 19)
27//! - [Memory Device Mapped Address](structures::memory_device_mapped_address
28//! "structures::memory_device_mapped_address") (Type 20)
29//! - [Built-in Pointing Device](structures::built_in_pointing_device
30//! "structures::built_in_pointing_device") (Type 21)
31//! - [Portable Battery](structures::portable_battery "structures::portable_battery") (Type 22)
32//! - System Reset (Type 23)
33//! - Hardware Security (Type 24)
34//! - System Power Controls (Type 25)
35//! - Voltage Probe (Type 26)
36//! - Cooling Device (Type 27)
37//! - Temperature Probe (Type 28)
38//! - Electrical Current Probe (Type 29)
39//! - Out-of-Band Remote Access (Type 30)
40//! - Boot Integrity Services (BIS) Entry Point (Type 31)
41//! - System Boot Information (Type 32)
42//! - 64-Bit Memory Error Information (Type 33)
43//! - Management Device (Type 34)
44//! - Management Device Component (Type 35)
45//! - Management Device Threshold Data (Type 36)
46//! - Memory Channel (Type 37)
47//! - IPMI Device Information (Type 38)
48//! - System Power Supply (Type 39)
49//! - Additional Information (Type 40)
50//! - Onboard Devices Extended Information (Type 41)
51//! - Management Controller Host Interface (Type 42)
52//! - TPM Device (Type 43)
53//! - Processor Additional Information (Type 44)
54//! - Inactive (Type 126)
55//! - End-of-Table (Type 127)
56
57#![no_std]
58
59#[cfg(any(feature = "std", test))]
60#[macro_use]
61extern crate std;
62
63use core::array::TryFromSliceError;
64use core::convert::TryInto;
65use core::fmt;
66use core::mem;
67use core::str;
68
69#[macro_export]
70#[doc(hidden)]
71macro_rules! let_as_struct {
72    ($name:ident, $ty:ty, $data:expr) => {
73        use core::ptr;
74        let $name: $ty = unsafe { ptr::read($data.as_ptr() as *const _) };
75    };
76}
77
78#[doc(hidden)]
79macro_rules! lib_ensure {
80    ($cond:expr, $e:expr) => {
81        if !($cond) {
82            return Err($e);
83        }
84    };
85}
86
87#[macro_use]
88pub mod bitfield;
89
90pub mod structures;
91pub use structures::*;
92
93#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
94enum EntryPointFormat {
95    V2,
96    V3,
97}
98
99#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
100pub enum EntryPoint {
101    V2(EntryPointV2),
102    V3(EntryPointV3),
103}
104
105impl EntryPoint {
106    #[allow(clippy::len_without_is_empty)]
107    pub fn len(&self) -> u8 {
108        match self {
109            EntryPoint::V2(point) => point.len,
110            EntryPoint::V3(point) => point.len,
111        }
112    }
113    pub fn major(&self) -> u8 {
114        match self {
115            EntryPoint::V2(point) => point.major,
116            EntryPoint::V3(point) => point.major,
117        }
118    }
119    pub fn minor(&self) -> u8 {
120        match self {
121            EntryPoint::V2(point) => point.minor,
122            EntryPoint::V3(point) => point.minor,
123        }
124    }
125    pub fn revision(&self) -> u8 {
126        match self {
127            EntryPoint::V2(point) => point.revision,
128            EntryPoint::V3(point) => point.revision,
129        }
130    }
131    pub fn smbios_address(&self) -> u64 {
132        match self {
133            EntryPoint::V2(point) => point.smbios_address as u64,
134            EntryPoint::V3(point) => point.smbios_address,
135        }
136    }
137    pub fn smbios_len(&self) -> u32 {
138        match self {
139            EntryPoint::V2(point) => point.smbios_len as u32,
140            EntryPoint::V3(point) => point.smbios_len_max,
141        }
142    }
143    pub fn to_version(&self) -> SmbiosVersion {
144        SmbiosVersion {
145            major: self.major(),
146            minor: self.minor(),
147        }
148    }
149
150    /// Create an iterator across the SMBIOS structures found in `buffer`.
151    ///
152    /// # Example
153    ///
154    /// ```
155    /// use dmidecode::EntryPoint;
156    /// const DMIDECODE_BIN: &'static [u8] = include_bytes!("../tests/data/dmidecode.bin");
157    ///
158    /// let entry_point = EntryPoint::search(DMIDECODE_BIN).unwrap();
159    /// for s in entry_point.structures(&DMIDECODE_BIN[entry_point.smbios_address() as usize..]) {
160    ///     let table = s.unwrap();
161    ///     // process raw...
162    /// }
163    /// ```
164    pub fn structures<'buffer>(&self, buffer: &'buffer [u8]) -> Structures<'buffer> {
165        Structures {
166            smbios_version: self.to_version(),
167            smbios_len: self.smbios_len(),
168            idx: 0u32,
169            buffer,
170        }
171    }
172
173    /// Search for an instance of an SMBIOS `EntryPoint` in a memory `buffer`.
174    ///
175    /// # Example
176    ///
177    /// ```
178    /// use dmidecode::EntryPoint;
179    ///
180    /// const ENTRY_BIN: &'static [u8] = include_bytes!("../tests/data/entry.bin");
181    ///
182    /// let entry_point = EntryPoint::search(ENTRY_BIN);
183    /// ```
184    ///
185    /// # Errors
186    ///
187    /// If this function fails to find a valid SMBIOS `EntryPoint`, it will return
188    /// an `InvalidEntryPointError` variant.
189    pub fn search(buffer: &[u8]) -> Result<EntryPoint, InvalidEntryPointError> {
190        find_signature(buffer)
191            .ok_or(InvalidEntryPointError::NotFound)
192            .and_then(|(kind, start)| {
193                let sub_buffer = &buffer[start..];
194
195                let entry_point = match kind {
196                    EntryPointFormat::V2 => {
197                        lib_ensure!(
198                            sub_buffer.len() >= mem::size_of::<EntryPointV2>(),
199                            InvalidEntryPointError::BadSize(sub_buffer.len() as u8)
200                        );
201                        let_as_struct!(entry_point, EntryPointV2, sub_buffer);
202                        lib_ensure!(
203                            entry_point.len as usize >= mem::size_of::<EntryPointV2>(),
204                            InvalidEntryPointError::BadSize(entry_point.len)
205                        );
206                        EntryPoint::V2(entry_point)
207                    }
208                    EntryPointFormat::V3 => {
209                        lib_ensure!(
210                            sub_buffer.len() >= mem::size_of::<EntryPointV3>(),
211                            InvalidEntryPointError::BadSize(sub_buffer.len() as u8)
212                        );
213                        let_as_struct!(entry_point, EntryPointV3, sub_buffer);
214                        lib_ensure!(
215                            entry_point.len as usize >= mem::size_of::<EntryPointV3>(),
216                            InvalidEntryPointError::BadSize(entry_point.len)
217                        );
218                        EntryPoint::V3(entry_point)
219                    }
220                };
221
222                lib_ensure!(
223                    entry_point.major() >= 2,
224                    InvalidEntryPointError::TooOldVersion(entry_point.major())
225                );
226
227                lib_ensure!(
228                    sub_buffer.len() as u8 >= entry_point.len(),
229                    InvalidEntryPointError::BadSize(sub_buffer.len() as u8)
230                );
231
232                let mut sum = 0u8;
233                for val in &sub_buffer[0..(entry_point.len() as usize)] {
234                    sum = sum.wrapping_add(*val);
235                }
236                lib_ensure!(sum == 0, InvalidEntryPointError::BadChecksum(sum));
237
238                Ok(entry_point)
239            })
240    }
241}
242
243///
244/// An SMBIOSv2 `EntryPoint` structure.
245///
246/// The SMBIOS `EntryPoint` structure is used to verify that a set of SMBIOS tables exist
247/// in memory and what version of the SMBIOS specification should be used to
248/// access the tables.
249///
250#[repr(C)]
251#[repr(packed)]
252#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
253pub struct EntryPointV2 {
254    pub signature: u32,
255    pub checksum: u8,
256    pub len: u8,
257    pub major: u8,
258    pub minor: u8,
259    pub struct_max: u16,
260    pub revision: u8,
261    pub formatted: [u8; 5],
262    pub dmi_signature: [u8; 5],
263    pub dmi_checksum: u8,
264    pub smbios_len: u16,
265    pub smbios_address: u32,
266    pub smbios_count: u16,
267    pub bcd_revision: u8,
268}
269
270///
271/// An SMBIOSv3 `EntryPoint` structure.
272///
273#[repr(C)]
274#[repr(packed)]
275#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
276pub struct EntryPointV3 {
277    pub signature: [u8; 5],
278    pub checksum: u8,
279    pub len: u8,
280    pub major: u8,
281    pub minor: u8,
282    pub docrev: u8,
283    pub revision: u8,
284    _reserved: u8,
285    pub smbios_len_max: u32,
286    pub smbios_address: u64,
287}
288
289/// The version number associated with the Smbios `EntryPoint`
290#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
291pub struct SmbiosVersion {
292    pub major: u8,
293    pub minor: u8,
294}
295
296impl From<(usize, usize)> for SmbiosVersion {
297    fn from(other: (usize, usize)) -> SmbiosVersion {
298        SmbiosVersion {
299            major: other.0 as u8,
300            minor: other.1 as u8,
301        }
302    }
303}
304
305/// Failure type for trying to find the SMBIOS `EntryPoint` structure in memory.
306#[derive(Debug)]
307pub enum InvalidEntryPointError {
308    /// The SMBIOS `EntryPoint` structure was not found in the memory buffer.
309    NotFound,
310    /// The SMBIOS `EntryPoint` structure was versioned before 2.0.
311    TooOldVersion(u8),
312    /// The SMBIOS `EntryPoint` structure was smaller than the size of the SMBIOS 2.1 structure.
313    BadSize(u8),
314    /// The SMBIOS `EntryPoint` structure had an invalid checksum.
315    BadChecksum(u8),
316}
317
318impl fmt::Display for InvalidEntryPointError {
319    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
320        match self {
321            InvalidEntryPointError::NotFound => write!(f, "Input did not contain a valid SMBIOS entry point"),
322            InvalidEntryPointError::TooOldVersion(version) => {
323                write!(f, "Input version number was below 2.0: {}", version)
324            }
325            InvalidEntryPointError::BadSize(size) => {
326                write!(f, "Input contained an invalid-sized SMBIOS entry: {}", size)
327            }
328            InvalidEntryPointError::BadChecksum(checksum) => {
329                write!(f, "SMBIOS entry point has an invalid checksum: {}", checksum)
330            }
331        }
332    }
333}
334
335#[cfg(feature = "std")]
336impl std::error::Error for InvalidEntryPointError {}
337
338fn find_signature(buffer: &[u8]) -> Option<(EntryPointFormat, usize)> {
339    static STRIDE: usize = 16;
340    static V2_SIG: &[u8; 4] = &[0x5f, 0x53, 0x4d, 0x5f];
341    static V3_SIG: &[u8; 5] = &[0x5f, 0x53, 0x4d, 0x33, 0x5f];
342
343    for (idx, chunk) in buffer.chunks(STRIDE).enumerate() {
344        if chunk.starts_with(V2_SIG) {
345            return Some((EntryPointFormat::V2, idx * STRIDE));
346        } else if chunk.starts_with(V3_SIG) {
347            return Some((EntryPointFormat::V3, idx * STRIDE));
348        }
349    }
350
351    None
352}
353
354/// An iterator that traverses the SMBIOS structure tables.
355/// This struct is produced by the `structures` method on `EntryPoint`. See its documentation for more details.
356#[derive(Clone, Debug, Eq, Hash, PartialEq)]
357pub struct Structures<'buffer> {
358    smbios_version: SmbiosVersion,
359    smbios_len: u32,
360    idx: u32,
361    buffer: &'buffer [u8],
362}
363
364/// Variant structure for decoding the SMBIOS table types.
365#[derive(Clone, Debug, Eq, Hash, PartialEq)]
366pub enum Structure<'buffer> {
367    Bios(Bios<'buffer>),
368    System(System<'buffer>),
369    BaseBoard(BaseBoard<'buffer>),
370    Enclosure(Enclosure<'buffer>),
371    Processor(Processor<'buffer>),
372    Cache(Cache<'buffer>),
373    PortConnector(PortConnector<'buffer>),
374    SystemSlots(SystemSlots<'buffer>),
375    OemStrings(OemStrings<'buffer>),
376    SystemConfigurationOptions(SystemConfigurationOptions<'buffer>),
377    BiosLanguage(BiosLanguage<'buffer>),
378    GroupAssociations(GroupAssociations<'buffer>),
379    SystemEventLog(SystemEventLog<'buffer>),
380    MemoryDevice(MemoryDevice<'buffer>),
381    MemoryError32(MemoryError32),
382    MemoryArrayMappedAddress(MemoryArrayMappedAddress),
383    MemoryDeviceMappedAddress(MemoryDeviceMappedAddress),
384    BuiltInPointingDevice(BuiltInPointingDevice),
385    PortableBattery(PortableBattery<'buffer>),
386    PhysicalMemoryArray(PhysicalMemoryArray),
387    Other(RawStructure<'buffer>),
388}
389
390/// Failure type for trying to decode the SMBIOS `Structures` iterator into the `Structure` variant type.
391
392#[derive(Debug)]
393pub enum MalformedStructureError {
394    /// The SMBIOS structure exceeds the end of the memory buffer given to the `EntryPoint::structures` method.
395    BadSize(u32, u8),
396    /// The SMBIOS structure contains an unterminated strings section.
397    UnterminatedStrings(u32),
398    /// The SMBIOS structure contains an invalid string index.
399    InvalidStringIndex(InfoType, u16, u8),
400    /// This error returned when a conversion from a slice to an array fails.
401    InvalidSlice(core::array::TryFromSliceError),
402    /// The SMBIOS structure formatted section length does not correspond to SMBIOS reference
403    /// specification
404    InvalidFormattedSectionLength(InfoType, u16, &'static str, u8),
405}
406
407impl fmt::Display for MalformedStructureError {
408    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
409        match self {
410            MalformedStructureError::BadSize(offset, length) => {
411                write!(
412                    f,
413                    "Structure at offset {} with length {} extends beyond SMBIOS",
414                    offset, length
415                )
416            }
417            MalformedStructureError::UnterminatedStrings(offset) => {
418                write!(f, "Structure at offset {} with unterminated strings", offset)
419            }
420            MalformedStructureError::InvalidStringIndex(info_type, handle, index) => {
421                write!(
422                    f,
423                    "Structure {:?} with handle {} has invalid string index {}",
424                    info_type, handle, index
425                )
426            }
427            MalformedStructureError::InvalidSlice(cause) => {
428                write!(f, "{}", cause)
429            }
430            MalformedStructureError::InvalidFormattedSectionLength(info_type, handle, spec, length) => {
431                write!(
432                    f,
433                    "Formatted section length of structure {:?} with handle {} should be {}{} bytes",
434                    info_type, handle, spec, length
435                )
436            }
437        }
438    }
439}
440
441#[cfg(feature = "std")]
442impl std::error::Error for MalformedStructureError {
443    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
444        match self {
445            MalformedStructureError::InvalidSlice(ref cause) => Some(cause),
446            _ => None,
447        }
448    }
449}
450
451#[doc(hidden)]
452/// Finds the final nul nul terminator of a buffer and returns the index of the final nul
453fn find_nulnul(buf: &[u8]) -> Option<usize> {
454    for i in 0..buf.len() {
455        if i + 1 >= buf.len() {
456            return None;
457        }
458
459        if buf[i] == 0 && buf[i + 1] == 0 {
460            return Some(i + 1);
461        }
462    }
463
464    None
465}
466
467impl<'buffer> Iterator for Structures<'buffer> {
468    type Item = Result<Structure<'buffer>, MalformedStructureError>;
469
470    fn next(&mut self) -> Option<Self::Item> {
471        let structure = match self.next_raw()? {
472            Ok(s) => s,
473            Err(e) => {
474                // make any errors to get the raw structure stop
475                // future iterations. This will avoid any nfinite
476                // iterations when skipping errors
477                self.smbios_len = self.idx;
478                return Some(Err(e));
479            }
480        };
481
482        /*
483         * For SMBIOS v3 we have no exact table length and no item count,
484         * so stop at the end-of-table marker.
485         */
486        if self.smbios_version.major >= 3 && structure.info == InfoType::End {
487            self.smbios_len = self.idx;
488        }
489
490        Some(match structure.info {
491            InfoType::Bios => Bios::try_from(structure).map(Structure::Bios),
492            InfoType::System => System::try_from(structure).map(Structure::System),
493            InfoType::BaseBoard => BaseBoard::try_from(structure).map(Structure::BaseBoard),
494            InfoType::Enclosure => Enclosure::try_from(structure).map(Structure::Enclosure),
495            InfoType::Processor => Processor::try_from(structure).map(Structure::Processor),
496            InfoType::Cache => Cache::try_from(structure).map(Structure::Cache),
497            InfoType::PortConnector => PortConnector::try_from(structure).map(Structure::PortConnector),
498            InfoType::SystemSlots => SystemSlots::try_from(structure).map(Structure::SystemSlots),
499            InfoType::OemStrings => OemStrings::try_from(structure).map(Structure::OemStrings),
500            InfoType::SystemConfigurationOptions => {
501                SystemConfigurationOptions::try_from(structure).map(Structure::SystemConfigurationOptions)
502            }
503            InfoType::BiosLanguage => BiosLanguage::try_from(structure).map(Structure::BiosLanguage),
504            InfoType::GroupAssociations => GroupAssociations::try_from(structure).map(Structure::GroupAssociations),
505            InfoType::SystemEventLog => SystemEventLog::try_from(structure).map(Structure::SystemEventLog),
506            InfoType::PhysicalMemoryArray => {
507                PhysicalMemoryArray::try_from(structure).map(Structure::PhysicalMemoryArray)
508            }
509            InfoType::MemoryDevice => MemoryDevice::try_from(structure).map(Structure::MemoryDevice),
510            InfoType::MemoryError32 => MemoryError32::try_from(structure).map(Structure::MemoryError32),
511            InfoType::MemoryArrayMappedAddress => {
512                MemoryArrayMappedAddress::try_from(structure).map(Structure::MemoryArrayMappedAddress)
513            }
514            InfoType::MemoryDeviceMappedAddress => {
515                MemoryDeviceMappedAddress::try_from(structure).map(Structure::MemoryDeviceMappedAddress)
516            }
517            InfoType::BuiltInPointingDevice => {
518                BuiltInPointingDevice::try_from(structure).map(Structure::BuiltInPointingDevice)
519            }
520            InfoType::PortableBattery => PortableBattery::try_from(structure).map(Structure::PortableBattery),
521            _ => Ok(Structure::Other(structure)),
522        })
523    }
524}
525
526impl<'buffer> Structures<'buffer> {
527    fn next_raw(&mut self) -> Option<Result<RawStructure<'buffer>, MalformedStructureError>> {
528        if (self.idx + mem::size_of::<HeaderPacked>() as u32) > self.smbios_len {
529            return None;
530        }
531
532        let working = &self.buffer[(self.idx as usize)..];
533        let_as_struct!(header, HeaderPacked, working);
534
535        let strings_idx: u32 = self.idx + header.len as u32;
536        if strings_idx >= self.smbios_len {
537            return Some(Err(MalformedStructureError::BadSize(self.idx, header.len)));
538        }
539
540        let term = find_nulnul(&self.buffer[(strings_idx as usize)..]);
541        let strings_len = match term {
542            Some(terminator) => (terminator + 1) as u32,
543            None => {
544                return Some(Err(MalformedStructureError::UnterminatedStrings(self.idx)));
545            }
546        };
547
548        let structure = RawStructure {
549            version: self.smbios_version,
550            info: header.kind.into(),
551            length: header.len,
552            handle: header.handle,
553            data: &self.buffer[(self.idx + mem::size_of::<HeaderPacked>() as u32) as usize..strings_idx as usize],
554            strings: &self.buffer[strings_idx as usize..(strings_idx + strings_len) as usize],
555        };
556
557        self.idx = strings_idx + strings_len;
558
559        Some(Ok(structure))
560    }
561}
562
563#[doc(hidden)]
564#[repr(C)]
565#[repr(packed)]
566struct HeaderPacked {
567    kind: u8,
568    len: u8,
569    handle: u16,
570}
571
572/// The raw SMBIOS structure information for structures that are not handled by this crate, such as Oem structures.
573#[derive(Clone, Debug, Eq, Hash, PartialEq)]
574pub struct RawStructure<'buffer> {
575    pub version: SmbiosVersion,
576    pub info: InfoType,
577    pub length: u8,
578    pub handle: u16,
579    pub data: &'buffer [u8],
580    strings: &'buffer [u8],
581}
582
583/// General trait for slice -> unsigned conversion
584pub trait TryFromBytes<'a, T>: Sized {
585    fn try_from_bytes(_: &'a [u8]) -> Result<Self, TryFromSliceError>;
586}
587
588impl<'a> TryFromBytes<'a, u8> for u8 {
589    fn try_from_bytes(bytes: &'a [u8]) -> Result<Self, TryFromSliceError> {
590        bytes.try_into().map(u8::from_le_bytes)
591    }
592}
593impl<'a> TryFromBytes<'a, u16> for u16 {
594    fn try_from_bytes(bytes: &'a [u8]) -> Result<Self, TryFromSliceError> {
595        bytes.try_into().map(u16::from_le_bytes)
596    }
597}
598impl<'a> TryFromBytes<'a, u32> for u32 {
599    fn try_from_bytes(bytes: &'a [u8]) -> Result<Self, TryFromSliceError> {
600        bytes.try_into().map(u32::from_le_bytes)
601    }
602}
603impl<'a> TryFromBytes<'a, u64> for u64 {
604    fn try_from_bytes(bytes: &'a [u8]) -> Result<Self, TryFromSliceError> {
605        bytes.try_into().map(u64::from_le_bytes)
606    }
607}
608impl<'a> TryFromBytes<'a, u128> for u128 {
609    fn try_from_bytes(bytes: &'a [u8]) -> Result<Self, TryFromSliceError> {
610        bytes.try_into().map(u128::from_le_bytes)
611    }
612}
613
614impl<'buffer> RawStructure<'buffer> {
615    /// Return an iterator over the strings in the strings table.
616    fn strings(&self) -> StructureStrings<'buffer> {
617        StructureStrings::new(self.strings)
618    }
619
620    /// Find a string in the strings table by the string index.
621    /// If the string index is 0, the empty string is returned. Otherwise, the string corresponding
622    /// to that string index in the strings table is returned.
623    ///
624    /// # Errors
625    /// Returns a `MalformedStructureError::InvalidStringIndex` if the index is outside of the strings table.
626    pub fn find_string(&self, idx: u8) -> Result<&'buffer str, MalformedStructureError> {
627        if idx == 0 {
628            Ok("")
629        } else {
630            self.strings()
631                .nth((idx - 1) as usize)
632                .ok_or(MalformedStructureError::InvalidStringIndex(self.info, self.handle, idx))
633        }
634    }
635    /// Get value by offset declared in SMBIOS Reference Specification.\
636    /// Type meaning data length is mandatory:
637    /// - *BYTE*: u8
638    /// - *WORD*: u16
639    /// - *DWORD*: u32
640    /// - *QWORD*: u64
641    ///
642    /// The only error this method returned: [MalformedStructureError::InvalidSlice] (actually is
643    /// [core::array::TryFromSliceError]). If getting value index exceedes length of *Formatted
644    /// section* it may be ignored to return [None] value of structure field. In this case *Formatted
645    /// section* length automatically hide non-existing values
646    pub fn get<T: TryFromBytes<'buffer, T>>(&self, offset: usize) -> Result<T, MalformedStructureError> {
647        // Ignore header
648        let start = offset - 4;
649        let size = core::mem::size_of::<T>();
650        let slice = self.data.get(start..(start + size)).unwrap_or(&[]);
651        TryFromBytes::try_from_bytes(slice).map_err(MalformedStructureError::InvalidSlice)
652    }
653    /// Wrapper to self.data.get(..) with header offset correction
654    pub fn get_slice(&self, offset: usize, size: usize) -> Option<&'buffer [u8]> {
655        self.data.get(offset - 4..offset - 4 + size)
656    }
657    /// Get *STRING* by offset declared in SMBIOS Reference Specification
658    pub fn get_string(&self, offset: usize) -> Result<&'buffer str, MalformedStructureError> {
659        self.get::<u8>(offset).and_then(|idx| self.find_string(idx))
660    }
661}
662
663/// An iterator over structure strings
664#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
665pub struct StructureStrings<'a> {
666    bytes: &'a [u8],
667    start: usize,
668}
669
670impl<'a> StructureStrings<'a> {
671    fn new(bytes: &'a [u8]) -> Self {
672        Self { bytes, start: 0 }
673    }
674}
675impl<'a> Iterator for StructureStrings<'a> {
676    type Item = &'a str;
677
678    fn next(&mut self) -> Option<Self::Item> {
679        let slice = self
680            .bytes
681            .get(self.start..)?
682            .split(|elm| *elm == 0)
683            .nth(0)
684            .filter(|slice| !slice.is_empty())?;
685        self.start += slice.len() + 1;
686        str::from_utf8(slice).ok()
687    }
688}
689
690/// SMBIOS Table information variant
691#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
692pub enum InfoType {
693    Bios,
694    System,
695    BaseBoard,
696    Enclosure,
697    Processor,
698    Cache,
699    PortConnector,
700    SystemSlots,
701    OemStrings,
702    SystemConfigurationOptions,
703    GroupAssociations,
704    SystemEventLog,
705    BiosLanguage,
706    PhysicalMemoryArray,
707    MemoryDevice,
708    MemoryError32,
709    MemoryArrayMappedAddress,
710    MemoryDeviceMappedAddress,
711    BuiltInPointingDevice,
712    PortableBattery,
713    SystemBoot,
714    Oem(u8),
715    End,
716}
717
718impl From<u8> for InfoType {
719    fn from(kind: u8) -> InfoType {
720        match kind {
721            0 => InfoType::Bios,
722            1 => InfoType::System,
723            2 => InfoType::BaseBoard,
724            3 => InfoType::Enclosure,
725            4 => InfoType::Processor,
726            7 => InfoType::Cache,
727            8 => InfoType::PortConnector,
728            9 => InfoType::SystemSlots,
729            11 => InfoType::OemStrings,
730            12 => InfoType::SystemConfigurationOptions,
731            13 => InfoType::BiosLanguage,
732            14 => InfoType::GroupAssociations,
733            15 => InfoType::SystemEventLog,
734            16 => InfoType::PhysicalMemoryArray,
735            17 => InfoType::MemoryDevice,
736            18 => InfoType::MemoryError32,
737            19 => InfoType::MemoryArrayMappedAddress,
738            20 => InfoType::MemoryDeviceMappedAddress,
739            21 => InfoType::BuiltInPointingDevice,
740            22 => InfoType::PortableBattery,
741            32 => InfoType::SystemBoot,
742            127 => InfoType::End,
743            t => InfoType::Oem(t),
744        }
745    }
746}
747impl fmt::Display for InfoType {
748    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
749        match self {
750            InfoType::Bios => write!(f, "BIOS Information"),
751            InfoType::System => write!(f, "System Information"),
752            InfoType::BaseBoard => write!(f, "Baseboard (or Module) Information"),
753            InfoType::Enclosure => write!(f, "System Enclosure or Chassis"),
754            InfoType::Processor => write!(f, "Processor Information"),
755            //InfoType::                          => write!(f, "Memory Controller Information"),
756            //InfoType::                          => write!(f, "Memory Module Information"),
757            InfoType::Cache => write!(f, "Cache Information"),
758            InfoType::PortConnector => write!(f, "Port Connector Information"),
759            InfoType::SystemSlots => write!(f, "System Slots"),
760            //InfoType::                          => write!(f, "On Board Devices Information"),
761            InfoType::OemStrings => write!(f, "OEM Strings"),
762            InfoType::SystemConfigurationOptions => write!(f, "System Configuration Options"),
763            InfoType::BiosLanguage => write!(f, "BIOS Language Information"),
764            InfoType::GroupAssociations => write!(f, "Group Associations"),
765            InfoType::SystemEventLog => write!(f, "System Event Log"),
766            InfoType::PhysicalMemoryArray => write!(f, "Physical Memory Array"),
767            InfoType::MemoryDevice => write!(f, "Memory Device"),
768            InfoType::MemoryError32 => write!(f, "32-Bit Memory Error Information"),
769            InfoType::MemoryArrayMappedAddress => write!(f, "Memory Array Mapped Address"),
770            InfoType::MemoryDeviceMappedAddress => write!(f, "Memory Device Mapped Address"),
771            InfoType::BuiltInPointingDevice => write!(f, "Built-in Pointing Device"),
772            InfoType::PortableBattery => write!(f, "Portable Battery"),
773            //InfoType::                          => write!(f, "System Reset"),
774            //InfoType::                          => write!(f, "Hardware Security"),
775            //InfoType::                          => write!(f, "System Power Controls"),
776            //InfoType::                          => write!(f, "Voltage Probe"),
777            //InfoType::                          => write!(f, "Cooling Device"),
778            //InfoType::                          => write!(f, "Temperature Probe"),
779            //InfoType::                          => write!(f, "Electrical Current Probe"),
780            //InfoType::                          => write!(f, "Out-of-Band Remote Access"),
781            //InfoType::                          => write!(f, "Boot Integrity Services (BIS) Entry Point"),
782            InfoType::SystemBoot => write!(f, "System Boot Information"),
783            //InfoType::                          => write!(f, "64-Bit Memory Error Information"),
784            //InfoType::                          => write!(f, "Management Device"),
785            //InfoType::                          => write!(f, "Management Device Component"),
786            //InfoType::                          => write!(f, "Management Device Threshold Data"),
787            //InfoType::                          => write!(f, "Memory Channel"),
788            //InfoType::                          => write!(f, "IPMI Device Information"),
789            //InfoType::                          => write!(f, "System Power Supply"),
790            //InfoType::                          => write!(f, "Additional Information"),
791            //InfoType::                          => write!(f, "Onboard Devices Extended Information"),
792            //InfoType::                          => write!(f, "Management Controller Host Interface"),
793            //InfoType::                          => write!(f, "TPM Device"),
794            //InfoType::                          => write!(f, "Processor Additional Information"),
795            //InfoType::                          => write!(f, "Inactive"),
796            InfoType::End => write!(f, "End-of-Table"),
797            InfoType::Oem(t) => write!(f, "OEM: {}", t),
798        }
799    }
800}
801
802#[cfg(test)]
803mod tests {
804    use super::*;
805
806    const DMIDECODE_BIN: &[u8] = include_bytes!("../tests/data/dmidecode.bin");
807    const ENTRY_V2_BIN: &[u8] = include_bytes!("../tests/data/entry.bin");
808    const DMI_V2_BIN: &[u8] = include_bytes!("../tests/data/dmi.bin");
809    const ENTRY_V3_BIN: &[u8] = include_bytes!("../tests/data/entry_v3.bin");
810    const DMI_V3_BIN: &[u8] = include_bytes!("../tests/data/dmi_v3.bin");
811    const DMI_V3_SHORT: &[u8] = include_bytes!("../tests/data/dmi_v3_short.bin");
812    const ENTRY_V3_SHORT: &[u8] = include_bytes!("../tests/data/entry_v3_short.bin");
813
814    #[test]
815    fn found_smbios_entry() {
816        EntryPoint::search(ENTRY_V2_BIN).unwrap();
817        EntryPoint::search(DMIDECODE_BIN).unwrap();
818    }
819
820    #[test]
821    fn found_smbios_entry_v3() {
822        EntryPoint::search(ENTRY_V3_BIN).unwrap();
823    }
824
825    #[test]
826    #[should_panic]
827    fn doesnt_find_smbios_entry() {
828        EntryPoint::search(DMI_V2_BIN).unwrap();
829    }
830
831    #[test]
832    fn found_signature() {
833        find_signature(ENTRY_V2_BIN).unwrap();
834        find_signature(ENTRY_V3_BIN).unwrap();
835        find_signature(DMIDECODE_BIN).unwrap();
836    }
837
838    #[test]
839    #[should_panic]
840    fn doesnt_find_signature() {
841        find_signature(DMI_V2_BIN).unwrap();
842        find_signature(DMI_V3_BIN).unwrap();
843    }
844
845    #[test]
846    fn iterator_through_structures() {
847        let entry_point = EntryPoint::search(DMIDECODE_BIN).unwrap();
848        for s in entry_point
849            .structures(&DMIDECODE_BIN[(entry_point.smbios_address() as usize)..])
850            .filter_map(|s| s.ok())
851        {
852            println!("{:?}", s);
853        }
854    }
855
856    #[test]
857    fn iterator_through_structures_v3_short() {
858        let entry_point = EntryPoint::search(ENTRY_V3_SHORT).unwrap();
859        for s in entry_point.structures(DMI_V3_SHORT).filter_map(|s| s.ok()) {
860            println!("{:?}", s);
861        }
862    }
863
864    #[test]
865    fn iterator_through_structures_v3() {
866        let entry_point = EntryPoint::search(ENTRY_V3_BIN).unwrap();
867        for s in entry_point.structures(DMI_V3_BIN).filter_map(|s| s.ok()) {
868            println!("{:?}", s);
869        }
870    }
871
872    #[test]
873    fn find_nulnul_empty() {
874        let buf = [];
875        assert_eq!(find_nulnul(&buf), None);
876    }
877
878    #[test]
879    fn find_nulnul_single_char() {
880        let buf = [0];
881        assert_eq!(find_nulnul(&buf), None);
882    }
883
884    #[test]
885    fn find_nulnul_trivial() {
886        let buf = [0, 0];
887        assert_eq!(find_nulnul(&buf), Some(1));
888    }
889
890    #[test]
891    fn find_nulnul_with_data() {
892        let buf = [1, 2, 3, 4, 0, 5, 4, 3, 2, 1, 0, 0];
893        assert_eq!(find_nulnul(&buf), Some(11));
894    }
895
896    #[test]
897    fn find_nulnul_with_data_more_at_end() {
898        let buf = [1, 2, 3, 4, 0, 5, 4, 3, 2, 1, 0, 0, 1, 2, 3];
899        assert_eq!(find_nulnul(&buf), Some(11));
900    }
901
902    #[test]
903    fn structure_strings() {
904        use pretty_assertions::assert_eq;
905        use std::prelude::v1::*;
906
907        let regular_bytes = &[65, 66, 67, 0, 68, 69, 0, 70, 0, 71, 72, 73, 0, 0];
908        let regular_ss = StructureStrings::new(regular_bytes).collect::<Vec<_>>();
909        assert_eq!(vec!["ABC", "DE", "F", "GHI"], regular_ss, "Regular bytes");
910
911        let zero_bytes = &[0, 0];
912        let zero_ss = StructureStrings::new(zero_bytes).collect::<Vec<_>>();
913        assert_eq!(vec![""; 0], zero_ss, "Zero bytes");
914
915        let no_tail_bytes = &[65, 66, 67, 0, 68, 69, 0, 70, 0, 71, 72, 73];
916        let no_tail_ss = StructureStrings::new(no_tail_bytes).collect::<Vec<_>>();
917        assert_eq!(vec!["ABC", "DE", "F", "GHI"], no_tail_ss, "Regular bytes");
918
919        let invalid_order1_bytes = &[65, 66, 67, 0, 0, 68, 69, 0, 0, 0, 0, 0];
920        let invalid_order1_ss = StructureStrings::new(invalid_order1_bytes).collect::<Vec<_>>();
921        assert_eq!(vec!["ABC"], invalid_order1_ss, "Invalid order 1 bytes");
922
923        let invalid_order2_bytes = &[0, 0, 65, 66, 67];
924        let invalid_order2_ss = StructureStrings::new(invalid_order2_bytes).collect::<Vec<&str>>();
925        assert_eq!(vec![""; 0], invalid_order2_ss, "Invalid order 2 bytes");
926    }
927}