1#![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 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 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 let entry_point = EntryPointV2 {
203 signature: u32::from_le(entry_point.signature),
204 struct_max: u16::from_le(entry_point.struct_max),
205 smbios_len: u16::from_le(entry_point.smbios_len),
206 smbios_address: u32::from_le(entry_point.smbios_address),
207 smbios_count: u16::from_le(entry_point.smbios_count),
208 ..entry_point
209 };
210 lib_ensure!(
211 entry_point.len as usize >= mem::size_of::<EntryPointV2>(),
212 InvalidEntryPointError::BadSize(entry_point.len)
213 );
214 EntryPoint::V2(entry_point)
215 }
216 EntryPointFormat::V3 => {
217 lib_ensure!(
218 sub_buffer.len() >= mem::size_of::<EntryPointV3>(),
219 InvalidEntryPointError::BadSize(sub_buffer.len() as u8)
220 );
221 let_as_struct!(entry_point, EntryPointV3, sub_buffer);
222 let entry_point = EntryPointV3 {
223 smbios_len_max: u32::from_le(entry_point.smbios_len_max),
224 smbios_address: u64::from_le(entry_point.smbios_address),
225 ..entry_point
226 };
227 lib_ensure!(
228 entry_point.len as usize >= mem::size_of::<EntryPointV3>(),
229 InvalidEntryPointError::BadSize(entry_point.len)
230 );
231 EntryPoint::V3(entry_point)
232 }
233 };
234
235 lib_ensure!(
236 entry_point.major() >= 2,
237 InvalidEntryPointError::TooOldVersion(entry_point.major())
238 );
239
240 lib_ensure!(
241 sub_buffer.len() as u8 >= entry_point.len(),
242 InvalidEntryPointError::BadSize(sub_buffer.len() as u8)
243 );
244
245 let mut sum = 0u8;
246 for val in &sub_buffer[0..(entry_point.len() as usize)] {
247 sum = sum.wrapping_add(*val);
248 }
249 lib_ensure!(sum == 0, InvalidEntryPointError::BadChecksum(sum));
250
251 Ok(entry_point)
252 })
253 }
254}
255
256#[repr(C)]
264#[repr(packed)]
265#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
266pub struct EntryPointV2 {
267 pub signature: u32,
268 pub checksum: u8,
269 pub len: u8,
270 pub major: u8,
271 pub minor: u8,
272 pub struct_max: u16,
273 pub revision: u8,
274 pub formatted: [u8; 5],
275 pub dmi_signature: [u8; 5],
276 pub dmi_checksum: u8,
277 pub smbios_len: u16,
278 pub smbios_address: u32,
279 pub smbios_count: u16,
280 pub bcd_revision: u8,
281}
282
283#[repr(C)]
287#[repr(packed)]
288#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
289pub struct EntryPointV3 {
290 pub signature: [u8; 5],
291 pub checksum: u8,
292 pub len: u8,
293 pub major: u8,
294 pub minor: u8,
295 pub docrev: u8,
296 pub revision: u8,
297 _reserved: u8,
298 pub smbios_len_max: u32,
299 pub smbios_address: u64,
300}
301
302#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
304pub struct SmbiosVersion {
305 pub major: u8,
306 pub minor: u8,
307}
308
309impl From<(usize, usize)> for SmbiosVersion {
310 fn from(other: (usize, usize)) -> SmbiosVersion {
311 SmbiosVersion {
312 major: other.0 as u8,
313 minor: other.1 as u8,
314 }
315 }
316}
317
318#[derive(Debug)]
320pub enum InvalidEntryPointError {
321 NotFound,
323 TooOldVersion(u8),
325 BadSize(u8),
327 BadChecksum(u8),
329}
330
331impl fmt::Display for InvalidEntryPointError {
332 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
333 match self {
334 InvalidEntryPointError::NotFound => write!(f, "Input did not contain a valid SMBIOS entry point"),
335 InvalidEntryPointError::TooOldVersion(version) => {
336 write!(f, "Input version number was below 2.0: {version}")
337 }
338 InvalidEntryPointError::BadSize(size) => {
339 write!(f, "Input contained an invalid-sized SMBIOS entry: {size}")
340 }
341 InvalidEntryPointError::BadChecksum(checksum) => {
342 write!(f, "SMBIOS entry point has an invalid checksum: {checksum}")
343 }
344 }
345 }
346}
347
348#[cfg(feature = "std")]
349impl std::error::Error for InvalidEntryPointError {}
350
351fn find_signature(buffer: &[u8]) -> Option<(EntryPointFormat, usize)> {
352 static STRIDE: usize = 16;
353 static V2_SIG: &[u8; 4] = &[0x5f, 0x53, 0x4d, 0x5f];
354 static V3_SIG: &[u8; 5] = &[0x5f, 0x53, 0x4d, 0x33, 0x5f];
355
356 for (idx, chunk) in buffer.chunks(STRIDE).enumerate() {
357 if chunk.starts_with(V2_SIG) {
358 return Some((EntryPointFormat::V2, idx * STRIDE));
359 } else if chunk.starts_with(V3_SIG) {
360 return Some((EntryPointFormat::V3, idx * STRIDE));
361 }
362 }
363
364 None
365}
366
367#[derive(Clone, Debug, Eq, Hash, PartialEq)]
370pub struct Structures<'buffer> {
371 smbios_version: SmbiosVersion,
372 smbios_len: u32,
373 idx: u32,
374 buffer: &'buffer [u8],
375}
376
377#[derive(Clone, Debug, Eq, Hash, PartialEq)]
379pub enum Structure<'buffer> {
380 Bios(Bios<'buffer>),
381 System(System<'buffer>),
382 BaseBoard(BaseBoard<'buffer>),
383 Enclosure(Enclosure<'buffer>),
384 Processor(Processor<'buffer>),
385 Cache(Cache<'buffer>),
386 PortConnector(PortConnector<'buffer>),
387 SystemSlots(SystemSlots<'buffer>),
388 OemStrings(OemStrings<'buffer>),
389 SystemConfigurationOptions(SystemConfigurationOptions<'buffer>),
390 BiosLanguage(BiosLanguage<'buffer>),
391 GroupAssociations(GroupAssociations<'buffer>),
392 SystemEventLog(SystemEventLog<'buffer>),
393 MemoryDevice(MemoryDevice<'buffer>),
394 MemoryError32(MemoryError32),
395 MemoryArrayMappedAddress(MemoryArrayMappedAddress),
396 MemoryDeviceMappedAddress(MemoryDeviceMappedAddress),
397 BuiltInPointingDevice(BuiltInPointingDevice),
398 PortableBattery(PortableBattery<'buffer>),
399 PhysicalMemoryArray(PhysicalMemoryArray),
400 Other(RawStructure<'buffer>),
401}
402
403#[derive(Debug)]
406pub enum MalformedStructureError {
407 BadSize(u32, u8),
409 UnterminatedStrings(u32),
411 InvalidStringIndex(InfoType, u16, u8),
413 InvalidSlice(core::array::TryFromSliceError),
415 InvalidFormattedSectionLength(InfoType, u16, &'static str, u8),
418}
419
420impl fmt::Display for MalformedStructureError {
421 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
422 match self {
423 MalformedStructureError::BadSize(offset, length) => {
424 write!(
425 f,
426 "Structure at offset {offset} with length {length} extends beyond SMBIOS"
427 )
428 }
429 MalformedStructureError::UnterminatedStrings(offset) => {
430 write!(f, "Structure at offset {offset} with unterminated strings")
431 }
432 MalformedStructureError::InvalidStringIndex(info_type, handle, index) => {
433 write!(
434 f,
435 "Structure {info_type:?} with handle {handle} has invalid string index {index}"
436 )
437 }
438 MalformedStructureError::InvalidSlice(cause) => {
439 write!(f, "{cause}")
440 }
441 MalformedStructureError::InvalidFormattedSectionLength(info_type, handle, spec, length) => {
442 write!(
443 f,
444 "Formatted section length of structure {info_type:?} with handle {handle} should be {spec}{length} bytes"
445 )
446 }
447 }
448 }
449}
450
451#[cfg(feature = "std")]
452impl std::error::Error for MalformedStructureError {
453 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
454 match self {
455 MalformedStructureError::InvalidSlice(ref cause) => Some(cause),
456 _ => None,
457 }
458 }
459}
460
461#[doc(hidden)]
462fn find_nulnul(buf: &[u8]) -> Option<usize> {
464 for i in 0..buf.len() {
465 if i + 1 >= buf.len() {
466 return None;
467 }
468
469 if buf[i] == 0 && buf[i + 1] == 0 {
470 return Some(i + 1);
471 }
472 }
473
474 None
475}
476
477impl<'buffer> Iterator for Structures<'buffer> {
478 type Item = Result<Structure<'buffer>, MalformedStructureError>;
479
480 fn next(&mut self) -> Option<Self::Item> {
481 let structure = match self.next_raw()? {
482 Ok(s) => s,
483 Err(e) => {
484 self.smbios_len = self.idx;
488 return Some(Err(e));
489 }
490 };
491
492 if self.smbios_version.major >= 3 && structure.info == InfoType::End {
497 self.smbios_len = self.idx;
498 }
499
500 Some(match structure.info {
501 InfoType::Bios => Bios::try_from(structure).map(Structure::Bios),
502 InfoType::System => System::try_from(structure).map(Structure::System),
503 InfoType::BaseBoard => BaseBoard::try_from(structure).map(Structure::BaseBoard),
504 InfoType::Enclosure => Enclosure::try_from(structure).map(Structure::Enclosure),
505 InfoType::Processor => Processor::try_from(structure).map(Structure::Processor),
506 InfoType::Cache => Cache::try_from(structure).map(Structure::Cache),
507 InfoType::PortConnector => PortConnector::try_from(structure).map(Structure::PortConnector),
508 InfoType::SystemSlots => SystemSlots::try_from(structure).map(Structure::SystemSlots),
509 InfoType::OemStrings => OemStrings::try_from(structure).map(Structure::OemStrings),
510 InfoType::SystemConfigurationOptions => {
511 SystemConfigurationOptions::try_from(structure).map(Structure::SystemConfigurationOptions)
512 }
513 InfoType::BiosLanguage => BiosLanguage::try_from(structure).map(Structure::BiosLanguage),
514 InfoType::GroupAssociations => GroupAssociations::try_from(structure).map(Structure::GroupAssociations),
515 InfoType::SystemEventLog => SystemEventLog::try_from(structure).map(Structure::SystemEventLog),
516 InfoType::PhysicalMemoryArray => {
517 PhysicalMemoryArray::try_from(structure).map(Structure::PhysicalMemoryArray)
518 }
519 InfoType::MemoryDevice => MemoryDevice::try_from(structure).map(Structure::MemoryDevice),
520 InfoType::MemoryError32 => MemoryError32::try_from(structure).map(Structure::MemoryError32),
521 InfoType::MemoryArrayMappedAddress => {
522 MemoryArrayMappedAddress::try_from(structure).map(Structure::MemoryArrayMappedAddress)
523 }
524 InfoType::MemoryDeviceMappedAddress => {
525 MemoryDeviceMappedAddress::try_from(structure).map(Structure::MemoryDeviceMappedAddress)
526 }
527 InfoType::BuiltInPointingDevice => {
528 BuiltInPointingDevice::try_from(structure).map(Structure::BuiltInPointingDevice)
529 }
530 InfoType::PortableBattery => PortableBattery::try_from(structure).map(Structure::PortableBattery),
531 _ => Ok(Structure::Other(structure)),
532 })
533 }
534}
535
536impl<'buffer> Structures<'buffer> {
537 fn next_raw(&mut self) -> Option<Result<RawStructure<'buffer>, MalformedStructureError>> {
538 if (self.idx + mem::size_of::<HeaderPacked>() as u32) > self.smbios_len {
539 return None;
540 }
541
542 let working = &self.buffer[(self.idx as usize)..];
543 let_as_struct!(header, HeaderPacked, working);
544 let header = HeaderPacked {
545 handle: u16::from_le(header.handle),
546 ..header
547 };
548
549 let strings_idx: u32 = self.idx + header.len as u32;
550 if strings_idx >= self.smbios_len {
551 return Some(Err(MalformedStructureError::BadSize(self.idx, header.len)));
552 }
553
554 let term = find_nulnul(&self.buffer[(strings_idx as usize)..]);
555 let strings_len = match term {
556 Some(terminator) => (terminator + 1) as u32,
557 None => {
558 return Some(Err(MalformedStructureError::UnterminatedStrings(self.idx)));
559 }
560 };
561
562 let structure = RawStructure {
563 version: self.smbios_version,
564 info: header.kind.into(),
565 length: header.len,
566 handle: header.handle,
567 data: &self.buffer[(self.idx + mem::size_of::<HeaderPacked>() as u32) as usize..strings_idx as usize],
568 strings: &self.buffer[strings_idx as usize..(strings_idx + strings_len) as usize],
569 };
570
571 self.idx = strings_idx + strings_len;
572
573 Some(Ok(structure))
574 }
575}
576
577#[doc(hidden)]
578#[repr(C)]
579#[repr(packed)]
580struct HeaderPacked {
581 kind: u8,
582 len: u8,
583 handle: u16,
584}
585
586#[derive(Clone, Debug, Eq, Hash, PartialEq)]
588pub struct RawStructure<'buffer> {
589 pub version: SmbiosVersion,
590 pub info: InfoType,
591 pub length: u8,
592 pub handle: u16,
593 pub data: &'buffer [u8],
594 strings: &'buffer [u8],
595}
596
597pub trait TryFromBytes<'a, T>: Sized {
599 fn try_from_bytes(_: &'a [u8]) -> Result<Self, TryFromSliceError>;
600}
601
602impl<'a> TryFromBytes<'a, u8> for u8 {
603 fn try_from_bytes(bytes: &'a [u8]) -> Result<Self, TryFromSliceError> {
604 bytes.try_into().map(u8::from_le_bytes)
605 }
606}
607impl<'a> TryFromBytes<'a, u16> for u16 {
608 fn try_from_bytes(bytes: &'a [u8]) -> Result<Self, TryFromSliceError> {
609 bytes.try_into().map(u16::from_le_bytes)
610 }
611}
612impl<'a> TryFromBytes<'a, u32> for u32 {
613 fn try_from_bytes(bytes: &'a [u8]) -> Result<Self, TryFromSliceError> {
614 bytes.try_into().map(u32::from_le_bytes)
615 }
616}
617impl<'a> TryFromBytes<'a, u64> for u64 {
618 fn try_from_bytes(bytes: &'a [u8]) -> Result<Self, TryFromSliceError> {
619 bytes.try_into().map(u64::from_le_bytes)
620 }
621}
622impl<'a> TryFromBytes<'a, u128> for u128 {
623 fn try_from_bytes(bytes: &'a [u8]) -> Result<Self, TryFromSliceError> {
624 bytes.try_into().map(u128::from_le_bytes)
625 }
626}
627
628impl<'buffer> RawStructure<'buffer> {
629 fn strings(&self) -> StructureStrings<'buffer> {
631 StructureStrings::new(self.strings)
632 }
633
634 pub fn find_string(&self, idx: u8) -> Result<&'buffer str, MalformedStructureError> {
641 if idx == 0 {
642 Ok("")
643 } else {
644 self.strings()
645 .nth((idx - 1) as usize)
646 .ok_or(MalformedStructureError::InvalidStringIndex(self.info, self.handle, idx))
647 }
648 }
649 pub fn get<T: TryFromBytes<'buffer, T>>(&self, offset: usize) -> Result<T, MalformedStructureError> {
661 let start = offset - 4;
663 let size = core::mem::size_of::<T>();
664 let slice = self.data.get(start..(start + size)).unwrap_or(&[]);
665 TryFromBytes::try_from_bytes(slice).map_err(MalformedStructureError::InvalidSlice)
666 }
667 pub fn get_slice(&self, offset: usize, size: usize) -> Option<&'buffer [u8]> {
669 self.data.get(offset - 4..offset - 4 + size)
670 }
671 pub fn get_string(&self, offset: usize) -> Result<&'buffer str, MalformedStructureError> {
673 self.get::<u8>(offset).and_then(|idx| self.find_string(idx))
674 }
675}
676
677#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
679pub struct StructureStrings<'a> {
680 bytes: &'a [u8],
681 start: usize,
682}
683
684impl<'a> StructureStrings<'a> {
685 fn new(bytes: &'a [u8]) -> Self {
686 Self { bytes, start: 0 }
687 }
688}
689impl<'a> Iterator for StructureStrings<'a> {
690 type Item = &'a str;
691
692 fn next(&mut self) -> Option<Self::Item> {
693 let slice = self
694 .bytes
695 .get(self.start..)?
696 .split(|elm| *elm == 0)
697 .nth(0)
698 .filter(|slice| !slice.is_empty())?;
699 self.start += slice.len() + 1;
700 str::from_utf8(slice).ok()
701 }
702}
703
704#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
706pub enum InfoType {
707 Bios,
708 System,
709 BaseBoard,
710 Enclosure,
711 Processor,
712 Cache,
713 PortConnector,
714 SystemSlots,
715 OemStrings,
716 SystemConfigurationOptions,
717 GroupAssociations,
718 SystemEventLog,
719 BiosLanguage,
720 PhysicalMemoryArray,
721 MemoryDevice,
722 MemoryError32,
723 MemoryArrayMappedAddress,
724 MemoryDeviceMappedAddress,
725 BuiltInPointingDevice,
726 PortableBattery,
727 SystemBoot,
728 Oem(u8),
729 End,
730}
731
732impl From<u8> for InfoType {
733 fn from(kind: u8) -> InfoType {
734 match kind {
735 0 => InfoType::Bios,
736 1 => InfoType::System,
737 2 => InfoType::BaseBoard,
738 3 => InfoType::Enclosure,
739 4 => InfoType::Processor,
740 7 => InfoType::Cache,
741 8 => InfoType::PortConnector,
742 9 => InfoType::SystemSlots,
743 11 => InfoType::OemStrings,
744 12 => InfoType::SystemConfigurationOptions,
745 13 => InfoType::BiosLanguage,
746 14 => InfoType::GroupAssociations,
747 15 => InfoType::SystemEventLog,
748 16 => InfoType::PhysicalMemoryArray,
749 17 => InfoType::MemoryDevice,
750 18 => InfoType::MemoryError32,
751 19 => InfoType::MemoryArrayMappedAddress,
752 20 => InfoType::MemoryDeviceMappedAddress,
753 21 => InfoType::BuiltInPointingDevice,
754 22 => InfoType::PortableBattery,
755 32 => InfoType::SystemBoot,
756 127 => InfoType::End,
757 t => InfoType::Oem(t),
758 }
759 }
760}
761impl fmt::Display for InfoType {
762 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
763 match self {
764 InfoType::Bios => write!(f, "BIOS Information"),
765 InfoType::System => write!(f, "System Information"),
766 InfoType::BaseBoard => write!(f, "Baseboard (or Module) Information"),
767 InfoType::Enclosure => write!(f, "System Enclosure or Chassis"),
768 InfoType::Processor => write!(f, "Processor Information"),
769 InfoType::Cache => write!(f, "Cache Information"),
772 InfoType::PortConnector => write!(f, "Port Connector Information"),
773 InfoType::SystemSlots => write!(f, "System Slots"),
774 InfoType::OemStrings => write!(f, "OEM Strings"),
776 InfoType::SystemConfigurationOptions => write!(f, "System Configuration Options"),
777 InfoType::BiosLanguage => write!(f, "BIOS Language Information"),
778 InfoType::GroupAssociations => write!(f, "Group Associations"),
779 InfoType::SystemEventLog => write!(f, "System Event Log"),
780 InfoType::PhysicalMemoryArray => write!(f, "Physical Memory Array"),
781 InfoType::MemoryDevice => write!(f, "Memory Device"),
782 InfoType::MemoryError32 => write!(f, "32-Bit Memory Error Information"),
783 InfoType::MemoryArrayMappedAddress => write!(f, "Memory Array Mapped Address"),
784 InfoType::MemoryDeviceMappedAddress => write!(f, "Memory Device Mapped Address"),
785 InfoType::BuiltInPointingDevice => write!(f, "Built-in Pointing Device"),
786 InfoType::PortableBattery => write!(f, "Portable Battery"),
787 InfoType::SystemBoot => write!(f, "System Boot Information"),
797 InfoType::End => write!(f, "End-of-Table"),
811 InfoType::Oem(t) => write!(f, "OEM: {t}"),
812 }
813 }
814}
815
816#[cfg(test)]
817mod tests {
818 use super::*;
819
820 const DMIDECODE_BIN: &[u8] = include_bytes!("../tests/data/dmidecode.bin");
821 const ENTRY_V2_BIN: &[u8] = include_bytes!("../tests/data/entry.bin");
822 const DMI_V2_BIN: &[u8] = include_bytes!("../tests/data/dmi.bin");
823 const ENTRY_V3_BIN: &[u8] = include_bytes!("../tests/data/entry_v3.bin");
824 const DMI_V3_BIN: &[u8] = include_bytes!("../tests/data/dmi_v3.bin");
825 const DMI_V3_SHORT: &[u8] = include_bytes!("../tests/data/dmi_v3_short.bin");
826 const ENTRY_V3_SHORT: &[u8] = include_bytes!("../tests/data/entry_v3_short.bin");
827
828 #[test]
829 fn found_smbios_entry() {
830 EntryPoint::search(ENTRY_V2_BIN).unwrap();
831 EntryPoint::search(DMIDECODE_BIN).unwrap();
832 }
833
834 #[test]
835 fn found_smbios_entry_v3() {
836 EntryPoint::search(ENTRY_V3_BIN).unwrap();
837 }
838
839 #[test]
840 #[should_panic]
841 fn doesnt_find_smbios_entry() {
842 EntryPoint::search(DMI_V2_BIN).unwrap();
843 }
844
845 #[test]
846 fn found_signature() {
847 find_signature(ENTRY_V2_BIN).unwrap();
848 find_signature(ENTRY_V3_BIN).unwrap();
849 find_signature(DMIDECODE_BIN).unwrap();
850 }
851
852 #[test]
853 #[should_panic]
854 fn doesnt_find_signature() {
855 find_signature(DMI_V2_BIN).unwrap();
856 find_signature(DMI_V3_BIN).unwrap();
857 }
858
859 #[test]
860 fn iterator_through_structures() {
861 let entry_point = EntryPoint::search(DMIDECODE_BIN).unwrap();
862 for s in entry_point
863 .structures(&DMIDECODE_BIN[(entry_point.smbios_address() as usize)..])
864 .filter_map(|s| s.ok())
865 {
866 println!("{s:?}");
867 }
868 }
869
870 #[test]
871 fn iterator_through_structures_v3_short() {
872 let entry_point = EntryPoint::search(ENTRY_V3_SHORT).unwrap();
873 for s in entry_point.structures(DMI_V3_SHORT).filter_map(|s| s.ok()) {
874 println!("{s:?}");
875 }
876 }
877
878 #[test]
879 fn iterator_through_structures_v3() {
880 let entry_point = EntryPoint::search(ENTRY_V3_BIN).unwrap();
881 for s in entry_point.structures(DMI_V3_BIN).filter_map(|s| s.ok()) {
882 println!("{s:?}");
883 }
884 }
885
886 #[test]
887 fn find_nulnul_empty() {
888 let buf = [];
889 assert_eq!(find_nulnul(&buf), None);
890 }
891
892 #[test]
893 fn find_nulnul_single_char() {
894 let buf = [0];
895 assert_eq!(find_nulnul(&buf), None);
896 }
897
898 #[test]
899 fn find_nulnul_trivial() {
900 let buf = [0, 0];
901 assert_eq!(find_nulnul(&buf), Some(1));
902 }
903
904 #[test]
905 fn find_nulnul_with_data() {
906 let buf = [1, 2, 3, 4, 0, 5, 4, 3, 2, 1, 0, 0];
907 assert_eq!(find_nulnul(&buf), Some(11));
908 }
909
910 #[test]
911 fn find_nulnul_with_data_more_at_end() {
912 let buf = [1, 2, 3, 4, 0, 5, 4, 3, 2, 1, 0, 0, 1, 2, 3];
913 assert_eq!(find_nulnul(&buf), Some(11));
914 }
915
916 #[test]
917 fn structure_strings() {
918 use pretty_assertions::assert_eq as pretty_assert_eq;
919 use std::prelude::v1::*;
920
921 let regular_bytes = &[65, 66, 67, 0, 68, 69, 0, 70, 0, 71, 72, 73, 0, 0];
922 let regular_ss = StructureStrings::new(regular_bytes).collect::<Vec<_>>();
923 pretty_assert_eq!(vec!["ABC", "DE", "F", "GHI"], regular_ss, "Regular bytes");
924
925 let zero_bytes = &[0, 0];
926 let zero_ss = StructureStrings::new(zero_bytes).collect::<Vec<_>>();
927 pretty_assert_eq!(vec![""; 0], zero_ss, "Zero bytes");
928
929 let no_tail_bytes = &[65, 66, 67, 0, 68, 69, 0, 70, 0, 71, 72, 73];
930 let no_tail_ss = StructureStrings::new(no_tail_bytes).collect::<Vec<_>>();
931 pretty_assert_eq!(vec!["ABC", "DE", "F", "GHI"], no_tail_ss, "Regular bytes");
932
933 let invalid_order1_bytes = &[65, 66, 67, 0, 0, 68, 69, 0, 0, 0, 0, 0];
934 let invalid_order1_ss = StructureStrings::new(invalid_order1_bytes).collect::<Vec<_>>();
935 pretty_assert_eq!(vec!["ABC"], invalid_order1_ss, "Invalid order 1 bytes");
936
937 let invalid_order2_bytes = &[0, 0, 65, 66, 67];
938 let invalid_order2_ss = StructureStrings::new(invalid_order2_bytes).collect::<Vec<&str>>();
939 pretty_assert_eq!(vec![""; 0], invalid_order2_ss, "Invalid order 2 bytes");
940 }
941}