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 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#[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#[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#[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#[derive(Debug)]
307pub enum InvalidEntryPointError {
308 NotFound,
310 TooOldVersion(u8),
312 BadSize(u8),
314 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#[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#[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#[derive(Debug)]
393pub enum MalformedStructureError {
394 BadSize(u32, u8),
396 UnterminatedStrings(u32),
398 InvalidStringIndex(InfoType, u16, u8),
400 InvalidSlice(core::array::TryFromSliceError),
402 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)]
452fn 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 self.smbios_len = self.idx;
478 return Some(Err(e));
479 }
480 };
481
482 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#[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
583pub 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 fn strings(&self) -> StructureStrings<'buffer> {
617 StructureStrings::new(self.strings)
618 }
619
620 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 pub fn get<T: TryFromBytes<'buffer, T>>(&self, offset: usize) -> Result<T, MalformedStructureError> {
647 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 pub fn get_slice(&self, offset: usize, size: usize) -> Option<&'buffer [u8]> {
655 self.data.get(offset - 4..offset - 4 + size)
656 }
657 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#[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#[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::Cache => write!(f, "Cache Information"),
758 InfoType::PortConnector => write!(f, "Port Connector Information"),
759 InfoType::SystemSlots => write!(f, "System Slots"),
760 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::SystemBoot => write!(f, "System Boot Information"),
783 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}