1#![no_std]
58
59#[cfg(any(feature = "std", test))]
60#[macro_use]
61extern crate std;
62#[macro_use]
63extern crate bitflags;
64#[cfg(test)]
65extern crate lazy_static;
66#[cfg(test)]
67extern crate pretty_assertions;
68
69use core::array::TryFromSliceError;
70use core::convert::TryInto;
71use core::fmt;
72use core::mem;
73use core::str;
74
75#[macro_export]
76#[doc(hidden)]
77macro_rules! let_as_struct {
78 ($name:ident, $ty:ty, $data:expr) => {
79 use core::ptr;
80 let $name: $ty = unsafe { ptr::read($data.as_ptr() as *const _) };
81 };
82}
83
84#[doc(hidden)]
85macro_rules! lib_ensure {
86 ($cond:expr, $e:expr) => {
87 if !($cond) {
88 return Err($e);
89 }
90 };
91}
92
93#[macro_use]
94pub mod bitfield;
95
96pub mod structures;
97pub use structures::*;
98
99#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
100enum EntryPointFormat {
101 V2,
102 V3,
103}
104
105#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
106pub enum EntryPoint {
107 V2(EntryPointV2),
108 V3(EntryPointV3),
109}
110
111impl EntryPoint {
112 #[allow(clippy::len_without_is_empty)]
113 pub fn len(&self) -> u8 {
114 match self {
115 EntryPoint::V2(point) => point.len,
116 EntryPoint::V3(point) => point.len,
117 }
118 }
119 pub fn major(&self) -> u8 {
120 match self {
121 EntryPoint::V2(point) => point.major,
122 EntryPoint::V3(point) => point.major,
123 }
124 }
125 pub fn minor(&self) -> u8 {
126 match self {
127 EntryPoint::V2(point) => point.minor,
128 EntryPoint::V3(point) => point.minor,
129 }
130 }
131 pub fn revision(&self) -> u8 {
132 match self {
133 EntryPoint::V2(point) => point.revision,
134 EntryPoint::V3(point) => point.revision,
135 }
136 }
137 pub fn smbios_address(&self) -> u64 {
138 match self {
139 EntryPoint::V2(point) => point.smbios_address as u64,
140 EntryPoint::V3(point) => point.smbios_address,
141 }
142 }
143 pub fn smbios_len(&self) -> u32 {
144 match self {
145 EntryPoint::V2(point) => point.smbios_len as u32,
146 EntryPoint::V3(point) => point.smbios_len_max,
147 }
148 }
149 pub fn to_version(&self) -> SmbiosVersion {
150 SmbiosVersion {
151 major: self.major(),
152 minor: self.minor(),
153 }
154 }
155
156 pub fn structures<'buffer>(&self, buffer: &'buffer [u8]) -> Structures<'buffer> {
176 Structures {
177 smbios_version: self.to_version(),
178 smbios_len: self.smbios_len(),
179 idx: 0u32,
180 buffer,
181 }
182 }
183
184 pub fn search(buffer: &[u8]) -> Result<EntryPoint, InvalidEntryPointError> {
202 find_signature(buffer)
203 .ok_or(InvalidEntryPointError::NotFound)
204 .and_then(|(kind, start)| {
205 let sub_buffer = &buffer[start..];
206
207 let entry_point = match kind {
208 EntryPointFormat::V2 => {
209 lib_ensure!(
210 sub_buffer.len() >= mem::size_of::<EntryPointV2>(),
211 InvalidEntryPointError::BadSize(sub_buffer.len() as u8)
212 );
213 let_as_struct!(entry_point, EntryPointV2, sub_buffer);
214 lib_ensure!(
215 entry_point.len as usize >= mem::size_of::<EntryPointV2>(),
216 InvalidEntryPointError::BadSize(entry_point.len)
217 );
218 EntryPoint::V2(entry_point)
219 }
220 EntryPointFormat::V3 => {
221 lib_ensure!(
222 sub_buffer.len() >= mem::size_of::<EntryPointV3>(),
223 InvalidEntryPointError::BadSize(sub_buffer.len() as u8)
224 );
225 let_as_struct!(entry_point, EntryPointV3, sub_buffer);
226 lib_ensure!(
227 entry_point.len as usize >= mem::size_of::<EntryPointV3>(),
228 InvalidEntryPointError::BadSize(entry_point.len)
229 );
230 EntryPoint::V3(entry_point)
231 }
232 };
233
234 lib_ensure!(
235 entry_point.major() >= 2,
236 InvalidEntryPointError::TooOldVersion(entry_point.major())
237 );
238
239 lib_ensure!(
240 sub_buffer.len() as u8 >= entry_point.len(),
241 InvalidEntryPointError::BadSize(sub_buffer.len() as u8)
242 );
243
244 let mut sum = 0u8;
245 for val in &sub_buffer[0..(entry_point.len() as usize)] {
246 sum = sum.wrapping_add(*val);
247 }
248 lib_ensure!(sum == 0, InvalidEntryPointError::BadChecksum(sum));
249
250 Ok(entry_point)
251 })
252 }
253}
254
255#[repr(C)]
263#[repr(packed)]
264#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
265pub struct EntryPointV2 {
266 pub signature: u32,
267 pub checksum: u8,
268 pub len: u8,
269 pub major: u8,
270 pub minor: u8,
271 pub struct_max: u16,
272 pub revision: u8,
273 pub formatted: [u8; 5],
274 pub dmi_signature: [u8; 5],
275 pub dmi_checksum: u8,
276 pub smbios_len: u16,
277 pub smbios_address: u32,
278 pub smbios_count: u16,
279 pub bcd_revision: u8,
280}
281
282#[repr(C)]
286#[repr(packed)]
287#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
288pub struct EntryPointV3 {
289 pub signature: [u8; 5],
290 pub checksum: u8,
291 pub len: u8,
292 pub major: u8,
293 pub minor: u8,
294 pub docrev: u8,
295 pub revision: u8,
296 _reserved: u8,
297 pub smbios_len_max: u32,
298 pub smbios_address: u64,
299}
300
301#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
303pub struct SmbiosVersion {
304 pub major: u8,
305 pub minor: u8,
306}
307
308impl From<(usize, usize)> for SmbiosVersion {
309 fn from(other: (usize, usize)) -> SmbiosVersion {
310 SmbiosVersion {
311 major: other.0 as u8,
312 minor: other.1 as u8,
313 }
314 }
315}
316
317#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
318struct SmbiosBound {
319 len: u16,
320 count: u16,
321}
322
323#[derive(Debug)]
325pub enum InvalidEntryPointError {
326 NotFound,
328 TooOldVersion(u8),
330 BadSize(u8),
332 BadChecksum(u8),
334}
335
336impl fmt::Display for InvalidEntryPointError {
337 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
338 match self {
339 InvalidEntryPointError::NotFound => write!(f, "Input did not contain a valid SMBIOS entry point"),
340 InvalidEntryPointError::TooOldVersion(version) => {
341 write!(f, "Input version number was below 2.0: {}", version)
342 }
343 InvalidEntryPointError::BadSize(size) => {
344 write!(f, "Input contained an invalid-sized SMBIOS entry: {}", size)
345 }
346 InvalidEntryPointError::BadChecksum(checksum) => {
347 write!(f, "SMBIOS entry point has an invalid checksum: {}", checksum)
348 }
349 }
350 }
351}
352
353#[cfg(feature = "std")]
354impl std::error::Error for InvalidEntryPointError {}
355
356fn find_signature(buffer: &[u8]) -> Option<(EntryPointFormat, usize)> {
357 static STRIDE: usize = 16;
358 static V2_SIG: &[u8; 4] = &[0x5f, 0x53, 0x4d, 0x5f];
359 static V3_SIG: &[u8; 5] = &[0x5f, 0x53, 0x4d, 0x33, 0x5f];
360
361 for (idx, chunk) in buffer.chunks(STRIDE).enumerate() {
362 if chunk.starts_with(V2_SIG) {
363 return Some((EntryPointFormat::V2, idx * STRIDE));
364 } else if chunk.starts_with(V3_SIG) {
365 return Some((EntryPointFormat::V3, idx * STRIDE));
366 }
367 }
368
369 None
370}
371
372#[derive(Clone, Debug, Eq, Hash, PartialEq)]
375pub struct Structures<'buffer> {
376 smbios_version: SmbiosVersion,
377 smbios_len: u32,
378 idx: u32,
379 buffer: &'buffer [u8],
380}
381
382#[derive(Clone, Debug, Eq, Hash, PartialEq)]
384pub enum Structure<'buffer> {
385 Bios(Bios<'buffer>),
386 System(System<'buffer>),
387 BaseBoard(BaseBoard<'buffer>),
388 Enclosure(Enclosure<'buffer>),
389 Processor(Processor<'buffer>),
390 Cache(Cache<'buffer>),
391 PortConnector(PortConnector<'buffer>),
392 SystemSlots(SystemSlots<'buffer>),
393 OemStrings(OemStrings<'buffer>),
394 SystemConfigurationOptions(SystemConfigurationOptions<'buffer>),
395 BiosLanguage(BiosLanguage<'buffer>),
396 GroupAssociations(GroupAssociations<'buffer>),
397 SystemEventLog(SystemEventLog<'buffer>),
398 MemoryDevice(MemoryDevice<'buffer>),
399 MemoryError32(MemoryError32),
400 MemoryArrayMappedAddress(MemoryArrayMappedAddress),
401 MemoryDeviceMappedAddress(MemoryDeviceMappedAddress),
402 BuiltInPointingDevice(BuiltInPointingDevice),
403 PortableBattery(PortableBattery<'buffer>),
404 PhysicalMemoryArray(PhysicalMemoryArray),
405 Other(RawStructure<'buffer>),
406}
407
408#[derive(Debug)]
411pub enum MalformedStructureError {
412 BadSize(u32, u8),
414 UnterminatedStrings(u32),
416 InvalidStringIndex(InfoType, u16, u8),
418 InvalidSlice(core::array::TryFromSliceError),
420 InvalidFormattedSectionLength(InfoType, u16, &'static str, u8),
423 InvalidProcessorFamily,
425}
426
427impl fmt::Display for MalformedStructureError {
428 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
429 match self {
430 MalformedStructureError::BadSize(offset, length) => {
431 write!(
432 f,
433 "Structure at offset {} with length {} extends beyond SMBIOS",
434 offset, length
435 )
436 }
437 MalformedStructureError::UnterminatedStrings(offset) => {
438 write!(f, "Structure at offset {} with unterminated strings", offset)
439 }
440 MalformedStructureError::InvalidStringIndex(info_type, handle, index) => {
441 write!(
442 f,
443 "Structure {:?} with handle {} has invalid string index {}",
444 info_type, handle, index
445 )
446 }
447 MalformedStructureError::InvalidSlice(cause) => {
448 write!(f, "{}", cause)
449 }
450 MalformedStructureError::InvalidFormattedSectionLength(info_type, handle, spec, length) => {
451 write!(
452 f,
453 "Formatted section length of structure {:?} with handle {} should be {}{} bytes",
454 info_type, handle, spec, length
455 )
456 }
457 MalformedStructureError::InvalidProcessorFamily => {
458 write!(f, "Invalid processor family")
459 }
460 }
461 }
462}
463
464#[cfg(feature = "std")]
465impl std::error::Error for MalformedStructureError {
466 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
467 match self {
468 MalformedStructureError::InvalidSlice(ref cause) => Some(cause),
469 _ => None,
470 }
471 }
472}
473
474#[doc(hidden)]
475fn find_nulnul(buf: &[u8]) -> Option<usize> {
477 for i in 0..buf.len() {
478 if i + 1 >= buf.len() {
479 return None;
480 }
481
482 if buf[i] == 0 && buf[i + 1] == 0 {
483 return Some(i + 1);
484 }
485 }
486
487 None
488}
489
490impl<'buffer> Iterator for Structures<'buffer> {
491 type Item = Result<Structure<'buffer>, MalformedStructureError>;
492
493 fn next(&mut self) -> Option<Self::Item> {
494 let structure = match self.next_raw()? {
495 Ok(s) => s,
496 Err(e) => {
497 self.smbios_len = self.idx;
501 return Some(Err(e));
502 }
503 };
504
505 if self.smbios_version.major >= 3 && structure.info == InfoType::End {
510 self.smbios_len = self.idx;
511 }
512
513 Some(match structure.info {
514 InfoType::Bios => Bios::try_from(structure).map(Structure::Bios),
515 InfoType::System => System::try_from(structure).map(Structure::System),
516 InfoType::BaseBoard => BaseBoard::try_from(structure).map(Structure::BaseBoard),
517 InfoType::Enclosure => Enclosure::try_from(structure).map(Structure::Enclosure),
518 InfoType::Processor => Processor::try_from(structure).map(Structure::Processor),
519 InfoType::Cache => Cache::try_from(structure).map(Structure::Cache),
520 InfoType::PortConnector => PortConnector::try_from(structure).map(Structure::PortConnector),
521 InfoType::SystemSlots => SystemSlots::try_from(structure).map(Structure::SystemSlots),
522 InfoType::OemStrings => OemStrings::try_from(structure).map(Structure::OemStrings),
523 InfoType::SystemConfigurationOptions => {
524 SystemConfigurationOptions::try_from(structure).map(Structure::SystemConfigurationOptions)
525 }
526 InfoType::BiosLanguage => BiosLanguage::try_from(structure).map(Structure::BiosLanguage),
527 InfoType::GroupAssociations => GroupAssociations::try_from(structure).map(Structure::GroupAssociations),
528 InfoType::SystemEventLog => SystemEventLog::try_from(structure).map(Structure::SystemEventLog),
529 InfoType::PhysicalMemoryArray => {
530 PhysicalMemoryArray::try_from(structure).map(Structure::PhysicalMemoryArray)
531 }
532 InfoType::MemoryDevice => MemoryDevice::try_from(structure).map(Structure::MemoryDevice),
533 InfoType::MemoryError32 => MemoryError32::try_from(structure).map(Structure::MemoryError32),
534 InfoType::MemoryArrayMappedAddress => {
535 MemoryArrayMappedAddress::try_from(structure).map(Structure::MemoryArrayMappedAddress)
536 }
537 InfoType::MemoryDeviceMappedAddress => {
538 MemoryDeviceMappedAddress::try_from(structure).map(Structure::MemoryDeviceMappedAddress)
539 }
540 InfoType::BuiltInPointingDevice => {
541 BuiltInPointingDevice::try_from(structure).map(Structure::BuiltInPointingDevice)
542 }
543 InfoType::PortableBattery => PortableBattery::try_from(structure).map(Structure::PortableBattery),
544 _ => Ok(Structure::Other(structure)),
545 })
546 }
547}
548
549impl<'buffer> Structures<'buffer> {
550 fn next_raw(&mut self) -> Option<Result<RawStructure<'buffer>, MalformedStructureError>> {
551 if (self.idx + mem::size_of::<HeaderPacked>() as u32) > self.smbios_len {
552 return None;
553 }
554
555 let working = &self.buffer[(self.idx as usize)..];
556 let_as_struct!(header, HeaderPacked, working);
557
558 let strings_idx: u32 = self.idx + header.len as u32;
559 if strings_idx >= self.smbios_len {
560 return Some(Err(MalformedStructureError::BadSize(self.idx, header.len)));
561 }
562
563 let term = find_nulnul(&self.buffer[(strings_idx as usize)..]);
564 let strings_len = match term {
565 Some(terminator) => (terminator + 1) as u32,
566 None => {
567 return Some(Err(MalformedStructureError::UnterminatedStrings(self.idx)));
568 }
569 };
570
571 let structure = RawStructure {
572 version: self.smbios_version,
573 info: header.kind.into(),
574 length: header.len,
575 handle: header.handle,
576 data: &self.buffer[(self.idx + mem::size_of::<HeaderPacked>() as u32) as usize..strings_idx as usize],
577 strings: &self.buffer[strings_idx as usize..(strings_idx + strings_len) as usize],
578 };
579
580 self.idx = strings_idx + strings_len;
581
582 Some(Ok(structure))
583 }
584}
585
586#[doc(hidden)]
587#[repr(C)]
588#[repr(packed)]
589struct HeaderPacked {
590 kind: u8,
591 len: u8,
592 handle: u16,
593}
594
595#[derive(Clone, Debug, Eq, Hash, PartialEq)]
597pub struct RawStructure<'buffer> {
598 pub version: SmbiosVersion,
599 pub info: InfoType,
600 pub length: u8,
601 pub handle: u16,
602 pub data: &'buffer [u8],
603 strings: &'buffer [u8],
604}
605
606pub trait TryFromBytes<'a, T>: Sized {
608 fn try_from_bytes(_: &'a [u8]) -> Result<Self, TryFromSliceError>;
609}
610
611impl<'a> TryFromBytes<'a, u8> for u8 {
612 fn try_from_bytes(bytes: &'a [u8]) -> Result<Self, TryFromSliceError> {
613 bytes.try_into().map(u8::from_le_bytes)
614 }
615}
616impl<'a> TryFromBytes<'a, u16> for u16 {
617 fn try_from_bytes(bytes: &'a [u8]) -> Result<Self, TryFromSliceError> {
618 bytes.try_into().map(u16::from_le_bytes)
619 }
620}
621impl<'a> TryFromBytes<'a, u32> for u32 {
622 fn try_from_bytes(bytes: &'a [u8]) -> Result<Self, TryFromSliceError> {
623 bytes.try_into().map(u32::from_le_bytes)
624 }
625}
626impl<'a> TryFromBytes<'a, u64> for u64 {
627 fn try_from_bytes(bytes: &'a [u8]) -> Result<Self, TryFromSliceError> {
628 bytes.try_into().map(u64::from_le_bytes)
629 }
630}
631impl<'a> TryFromBytes<'a, u128> for u128 {
632 fn try_from_bytes(bytes: &'a [u8]) -> Result<Self, TryFromSliceError> {
633 bytes.try_into().map(u128::from_le_bytes)
634 }
635}
636
637impl<'buffer> RawStructure<'buffer> {
638 fn strings(&self) -> StructureStrings<'buffer> {
640 StructureStrings::new(self.strings)
641 }
642
643 pub fn find_string(&self, idx: u8) -> Result<&'buffer str, MalformedStructureError> {
650 if idx == 0 {
651 Ok("")
652 } else {
653 self.strings()
654 .nth((idx - 1) as usize)
655 .ok_or(MalformedStructureError::InvalidStringIndex(self.info, self.handle, idx))
656 }
657 }
658 pub fn get<T: TryFromBytes<'buffer, T>>(&self, offset: usize) -> Result<T, MalformedStructureError> {
670 let start = offset - 4;
672 let size = core::mem::size_of::<T>();
673 let slice = self.data.get(start..(start + size)).unwrap_or(&[]);
674 TryFromBytes::try_from_bytes(slice).map_err(MalformedStructureError::InvalidSlice)
675 }
676 pub fn get_slice(&self, offset: usize, size: usize) -> Option<&'buffer [u8]> {
678 self.data.get(offset - 4..offset - 4 + size)
679 }
680 pub fn get_string(&self, offset: usize) -> Result<&'buffer str, MalformedStructureError> {
682 self.get::<u8>(offset).and_then(|idx| self.find_string(idx))
683 }
684}
685
686#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
688pub struct StructureStrings<'a> {
689 bytes: &'a [u8],
690 start: usize,
691}
692
693impl<'a> StructureStrings<'a> {
694 fn new(bytes: &'a [u8]) -> Self {
695 Self { bytes, start: 0 }
696 }
697}
698impl<'a> Iterator for StructureStrings<'a> {
699 type Item = &'a str;
700
701 fn next(&mut self) -> Option<Self::Item> {
702 let slice = self
703 .bytes
704 .get(self.start..)?
705 .split(|elm| *elm == 0)
706 .nth(0)
707 .filter(|slice| !slice.is_empty())?;
708 self.start += slice.len() + 1;
709 str::from_utf8(slice).ok()
710 }
711}
712
713#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
715pub enum InfoType {
716 Bios,
717 System,
718 BaseBoard,
719 Enclosure,
720 Processor,
721 Cache,
722 PortConnector,
723 SystemSlots,
724 OemStrings,
725 SystemConfigurationOptions,
726 GroupAssociations,
727 SystemEventLog,
728 BiosLanguage,
729 PhysicalMemoryArray,
730 MemoryDevice,
731 MemoryError32,
732 MemoryArrayMappedAddress,
733 MemoryDeviceMappedAddress,
734 BuiltInPointingDevice,
735 PortableBattery,
736 SystemBoot,
737 Oem(u8),
738 End,
739}
740
741impl From<u8> for InfoType {
742 fn from(kind: u8) -> InfoType {
743 match kind {
744 0 => InfoType::Bios,
745 1 => InfoType::System,
746 2 => InfoType::BaseBoard,
747 3 => InfoType::Enclosure,
748 4 => InfoType::Processor,
749 7 => InfoType::Cache,
750 8 => InfoType::PortConnector,
751 9 => InfoType::SystemSlots,
752 11 => InfoType::OemStrings,
753 12 => InfoType::SystemConfigurationOptions,
754 13 => InfoType::BiosLanguage,
755 14 => InfoType::GroupAssociations,
756 15 => InfoType::SystemEventLog,
757 16 => InfoType::PhysicalMemoryArray,
758 17 => InfoType::MemoryDevice,
759 18 => InfoType::MemoryError32,
760 19 => InfoType::MemoryArrayMappedAddress,
761 20 => InfoType::MemoryDeviceMappedAddress,
762 21 => InfoType::BuiltInPointingDevice,
763 22 => InfoType::PortableBattery,
764 32 => InfoType::SystemBoot,
765 127 => InfoType::End,
766 t => InfoType::Oem(t),
767 }
768 }
769}
770impl fmt::Display for InfoType {
771 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
772 match self {
773 InfoType::Bios => write!(f, "BIOS Information"),
774 InfoType::System => write!(f, "System Information"),
775 InfoType::BaseBoard => write!(f, "Baseboard (or Module) Information"),
776 InfoType::Enclosure => write!(f, "System Enclosure or Chassis"),
777 InfoType::Processor => write!(f, "Processor Information"),
778 InfoType::Cache => write!(f, "Cache Information"),
781 InfoType::PortConnector => write!(f, "Port Connector Information"),
782 InfoType::SystemSlots => write!(f, "System Slots"),
783 InfoType::OemStrings => write!(f, "OEM Strings"),
785 InfoType::SystemConfigurationOptions => write!(f, "System Configuration Options"),
786 InfoType::BiosLanguage => write!(f, "BIOS Language Information"),
787 InfoType::GroupAssociations => write!(f, "Group Associations"),
788 InfoType::SystemEventLog => write!(f, "System Event Log"),
789 InfoType::PhysicalMemoryArray => write!(f, "Physical Memory Array"),
790 InfoType::MemoryDevice => write!(f, "Memory Device"),
791 InfoType::MemoryError32 => write!(f, "32-Bit Memory Error Information"),
792 InfoType::MemoryArrayMappedAddress => write!(f, "Memory Array Mapped Address"),
793 InfoType::MemoryDeviceMappedAddress => write!(f, "Memory Device Mapped Address"),
794 InfoType::BuiltInPointingDevice => write!(f, "Built-in Pointing Device"),
795 InfoType::PortableBattery => write!(f, "Portable Battery"),
796 InfoType::SystemBoot => write!(f, "System Boot Information"),
806 InfoType::End => write!(f, "End-of-Table"),
820 InfoType::Oem(t) => write!(f, "OEM: {}", t),
821 }
822 }
823}
824
825#[cfg(test)]
826mod tests {
827 use super::*;
828
829 const DMIDECODE_BIN: &[u8] = include_bytes!("../tests/data/dmidecode.bin");
830 const ENTRY_V2_BIN: &[u8] = include_bytes!("../tests/data/entry.bin");
831 const DMI_V2_BIN: &[u8] = include_bytes!("../tests/data/dmi.bin");
832 const ENTRY_V3_BIN: &[u8] = include_bytes!("../tests/data/entry_v3.bin");
833 const DMI_V3_BIN: &[u8] = include_bytes!("../tests/data/dmi_v3.bin");
834 const DMI_V3_SHORT: &[u8] = include_bytes!("../tests/data/dmi_v3_short.bin");
835 const ENTRY_V3_SHORT: &[u8] = include_bytes!("../tests/data/entry_v3_short.bin");
836
837 #[test]
838 fn found_smbios_entry() {
839 EntryPoint::search(ENTRY_V2_BIN).unwrap();
840 EntryPoint::search(DMIDECODE_BIN).unwrap();
841 }
842
843 #[test]
844 fn found_smbios_entry_v3() {
845 EntryPoint::search(ENTRY_V3_BIN).unwrap();
846 }
847
848 #[test]
849 #[should_panic]
850 fn doesnt_find_smbios_entry() {
851 EntryPoint::search(DMI_V2_BIN).unwrap();
852 }
853
854 #[test]
855 fn found_signature() {
856 find_signature(ENTRY_V2_BIN).unwrap();
857 find_signature(ENTRY_V3_BIN).unwrap();
858 find_signature(DMIDECODE_BIN).unwrap();
859 }
860
861 #[test]
862 #[should_panic]
863 fn doesnt_find_signature() {
864 find_signature(DMI_V2_BIN).unwrap();
865 find_signature(DMI_V3_BIN).unwrap();
866 }
867
868 #[test]
869 fn iterator_through_structures() {
870 let entry_point = EntryPoint::search(DMIDECODE_BIN).unwrap();
871 for s in entry_point
872 .structures(&DMIDECODE_BIN[(entry_point.smbios_address() as usize)..])
873 .filter_map(|s| s.ok())
874 {
875 println!("{:?}", s);
876 }
877 }
878
879 #[test]
880 fn iterator_through_structures_v3_short() {
881 let entry_point = EntryPoint::search(ENTRY_V3_SHORT).unwrap();
882 for s in entry_point.structures(DMI_V3_SHORT).filter_map(|s| s.ok()) {
883 println!("{:?}", s);
884 }
885 }
886
887 #[test]
888 fn iterator_through_structures_v3() {
889 let entry_point = EntryPoint::search(ENTRY_V3_BIN).unwrap();
890 for s in entry_point.structures(DMI_V3_BIN).filter_map(|s| s.ok()) {
891 println!("{:?}", s);
892 }
893 }
894
895 #[test]
896 fn find_nulnul_empty() {
897 let buf = [];
898 assert_eq!(find_nulnul(&buf), None);
899 }
900
901 #[test]
902 fn find_nulnul_single_char() {
903 let buf = [0];
904 assert_eq!(find_nulnul(&buf), None);
905 }
906
907 #[test]
908 fn find_nulnul_trivial() {
909 let buf = [0, 0];
910 assert_eq!(find_nulnul(&buf), Some(1));
911 }
912
913 #[test]
914 fn find_nulnul_with_data() {
915 let buf = [1, 2, 3, 4, 0, 5, 4, 3, 2, 1, 0, 0];
916 assert_eq!(find_nulnul(&buf), Some(11));
917 }
918
919 #[test]
920 fn find_nulnul_with_data_more_at_end() {
921 let buf = [1, 2, 3, 4, 0, 5, 4, 3, 2, 1, 0, 0, 1, 2, 3];
922 assert_eq!(find_nulnul(&buf), Some(11));
923 }
924
925 #[test]
926 fn structure_strings() {
927 use pretty_assertions::assert_eq;
928 use std::prelude::v1::*;
929
930 let regular_bytes = &[65, 66, 67, 0, 68, 69, 0, 70, 0, 71, 72, 73, 0, 0];
931 let regular_ss = StructureStrings::new(regular_bytes).collect::<Vec<_>>();
932 assert_eq!(vec!["ABC", "DE", "F", "GHI"], regular_ss, "Regular bytes");
933
934 let zero_bytes = &[0, 0];
935 let zero_ss = StructureStrings::new(zero_bytes).collect::<Vec<_>>();
936 assert_eq!(vec![""; 0], zero_ss, "Zero bytes");
937
938 let no_tail_bytes = &[65, 66, 67, 0, 68, 69, 0, 70, 0, 71, 72, 73];
939 let no_tail_ss = StructureStrings::new(no_tail_bytes).collect::<Vec<_>>();
940 assert_eq!(vec!["ABC", "DE", "F", "GHI"], no_tail_ss, "Regular bytes");
941
942 let invalid_order1_bytes = &[65, 66, 67, 0, 0, 68, 69, 0, 0, 0, 0, 0];
943 let invalid_order1_ss = StructureStrings::new(invalid_order1_bytes).collect::<Vec<_>>();
944 assert_eq!(vec!["ABC"], invalid_order1_ss, "Invalid order 1 bytes");
945
946 let invalid_order2_bytes = &[0, 0, 65, 66, 67];
947 let invalid_order2_ss = StructureStrings::new(invalid_order2_bytes).collect::<Vec<&str>>();
948 assert_eq!(vec![""; 0], invalid_order2_ss, "Invalid order 2 bytes");
949 }
950}