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