1use serde::{ser::SerializeStruct, Serialize, Serializer};
2use std::{
3 convert::TryFrom,
4 convert::TryInto,
5 fmt,
6 fs::{read, File},
7 io::{prelude::*, Error, ErrorKind, SeekFrom},
8 num::Wrapping,
9 ops::RangeBounds,
10 path::Path,
11};
12
13pub struct SMBiosEntryPoint32 {
25 raw: Vec<u8>,
26}
27
28impl<'a> SMBiosEntryPoint32 {
29 pub const MINIMUM_SIZE: usize = 0x1F;
36
37 pub const SM_ANCHOR: [u8; 4] = [b'_', b'S', b'M', b'_'];
39
40 pub const DMI_ANCHOR: [u8; 5] = [b'_', b'D', b'M', b'I', b'_'];
42
43 pub const ENTRY_POINT_STRUCTURE_CHECKSUM_OFFSET: usize = 0x04;
45
46 pub const ENTRY_POINT_LENGTH_OFFSET: usize = 0x05;
48
49 pub const MAJOR_VERSION_OFFSET: usize = 0x06;
51
52 pub const MINOR_VERSION_OFFSET: usize = 0x07;
54
55 pub const MAXIMUM_STRUCTURE_SIZE_OFFSET: usize = 0x08;
57
58 pub const ENTRY_POINT_REVISION_OFFSET: usize = 0x0A;
60
61 pub const FORMATTED_AREA_OFFSET: usize = 0x0B;
63
64 pub const INTERMEDIATE_ANCHOR_OFFSET: usize = 0x10;
69
70 pub const INTERMEDIATE_CHECKSUM_OFFSET: usize = 0x15;
72
73 pub const STRUCTURE_TABLE_LENGTH_OFFSET: usize = 0x16;
75
76 pub const STRUCTURE_TABLE_ADDRESS_OFFSET: usize = 0x18;
78
79 pub const NUMBER_OF_SMBIOS_STRUCTURES_OFFSET: usize = 0x1C;
81
82 pub const BCD_REVISION_OFFSET: usize = 0x1E;
84
85 pub fn entry_point_structure_checksum(&self) -> u8 {
94 self.raw[Self::ENTRY_POINT_STRUCTURE_CHECKSUM_OFFSET]
95 }
96
97 pub fn entry_point_length(&self) -> u8 {
107 self.raw[Self::ENTRY_POINT_LENGTH_OFFSET]
108 }
109
110 pub fn major_version(&self) -> u8 {
116 self.raw[Self::MAJOR_VERSION_OFFSET]
117 }
118
119 pub fn minor_version(&self) -> u8 {
125 self.raw[Self::MINOR_VERSION_OFFSET]
126 }
127
128 pub fn maximum_structure_size(&self) -> u16 {
133 u16::from_le_bytes(
134 self.raw[Self::MAXIMUM_STRUCTURE_SIZE_OFFSET..Self::MAXIMUM_STRUCTURE_SIZE_OFFSET + 2]
135 .try_into()
136 .expect("u16 is 2 bytes"),
137 )
138 }
139
140 pub fn entry_point_revision(&self) -> u8 {
147 self.raw[Self::ENTRY_POINT_REVISION_OFFSET]
148 }
149
150 pub fn formatted_area(&self) -> [u8; 5] {
155 self.raw[Self::FORMATTED_AREA_OFFSET..Self::FORMATTED_AREA_OFFSET + 5]
156 .try_into()
157 .expect("5 bytes")
158 }
159
160 pub fn intermediate_anchor(&self) -> [u8; 5] {
164 self.raw[Self::INTERMEDIATE_ANCHOR_OFFSET..Self::INTERMEDIATE_ANCHOR_OFFSET + 5]
165 .try_into()
166 .expect("5 bytes")
167 }
168
169 pub fn intermediate_checksum(&self) -> u8 {
177 self.raw[Self::INTERMEDIATE_CHECKSUM_OFFSET]
178 }
179
180 pub fn structure_table_length(&self) -> u16 {
185 u16::from_le_bytes(
186 self.raw[Self::STRUCTURE_TABLE_LENGTH_OFFSET..Self::STRUCTURE_TABLE_LENGTH_OFFSET + 2]
187 .try_into()
188 .expect("u16 is 2 bytes"),
189 )
190 }
191
192 pub fn structure_table_address(&self) -> u32 {
201 u32::from_le_bytes(
202 self.raw
203 [Self::STRUCTURE_TABLE_ADDRESS_OFFSET..Self::STRUCTURE_TABLE_ADDRESS_OFFSET + 4]
204 .try_into()
205 .expect("u32 is 4 bytes"),
206 )
207 }
208
209 pub fn number_of_smbios_structures(&self) -> u16 {
215 u16::from_le_bytes(
216 self.raw[Self::NUMBER_OF_SMBIOS_STRUCTURES_OFFSET
217 ..Self::NUMBER_OF_SMBIOS_STRUCTURES_OFFSET + 2]
218 .try_into()
219 .expect("u16 is 2 bytes"),
220 )
221 }
222
223 pub fn bcd_revision(&self) -> u8 {
232 self.raw[Self::BCD_REVISION_OFFSET]
233 }
234
235 pub fn try_load_from_file(filename: &Path) -> Result<Self, Error> {
237 read(filename)?.try_into()
238 }
239
240 pub fn try_scan_from_file<T: Iterator<Item = u64>>(
243 file: &mut File,
244 range: T,
245 ) -> Result<Self, Error>
246 where
247 T: RangeBounds<u64>,
248 {
249 let mut anchor = [0; 4];
250 for offset in range.step_by(0x10) {
251 file.seek(SeekFrom::Start(offset))?;
252 file.read_exact(&mut anchor)?;
253 if anchor == Self::SM_ANCHOR {
254 let mut length = [0; 2];
255 file.read_exact(&mut length)?;
256 let struct_length = length[1] as usize;
257 let mut entry_point_buffer = Vec::with_capacity(struct_length);
258 entry_point_buffer.resize(struct_length, 0);
259 file.seek(SeekFrom::Start(offset))?;
260 file.read_exact(&mut entry_point_buffer)?;
261 let entry_point: Self = entry_point_buffer.try_into()?;
262 return Ok(entry_point);
263 }
264 }
265 Err(Error::new(ErrorKind::UnexpectedEof, "Not found"))
266 }
267}
268
269impl<'a> TryFrom<Vec<u8>> for SMBiosEntryPoint32 {
270 type Error = Error;
271
272 fn try_from(raw: Vec<u8>) -> Result<Self, Self::Error> {
273 if raw.len() < Self::MINIMUM_SIZE {
274 return Err(Error::new(
275 ErrorKind::InvalidData,
276 "Slice is smaller than SMBiosEntryPoint32::MINIMUM_SIZE",
277 ));
278 }
279
280 if !raw
281 .iter()
282 .zip(Self::SM_ANCHOR.iter())
283 .all(|pair| pair.0 == pair.1)
284 {
285 return Err(Error::new(ErrorKind::InvalidData, "_SM_ anchor not found"));
286 }
287
288 let entry_point_length = raw[Self::ENTRY_POINT_LENGTH_OFFSET] as usize;
291 match raw.get(0..entry_point_length) {
292 Some(checked_bytes) => {
293 if !verify_checksum(checked_bytes) {
294 return Err(Error::new(
295 ErrorKind::InvalidData,"Entry Point Structure checksum verification failed"));
296 }
297 }
298 None => return Err(Error::new(
299 ErrorKind::InvalidData,"The Entry Point Length field specified a value which exceeded the bounds of the Entry Point Structure")),
300 }
301
302 let intermediate_anchor: [u8; 5] = raw
303 [Self::INTERMEDIATE_ANCHOR_OFFSET..Self::INTERMEDIATE_ANCHOR_OFFSET + 5]
304 .try_into()
305 .expect("5 bytes");
306
307 if !intermediate_anchor
308 .iter()
309 .zip(Self::DMI_ANCHOR.iter())
310 .all(|pair| pair.0 == pair.1)
311 {
312 return Err(Error::new(ErrorKind::InvalidData, "_DMI_ anchor not found"));
313 }
314
315 let intermediate_entry_point_structure: [u8; 0x0F] = raw
318 [Self::INTERMEDIATE_ANCHOR_OFFSET..]
319 .try_into()
320 .expect("0x0F bytes");
321
322 if !verify_checksum(&intermediate_entry_point_structure) {
323 return Err(Error::new(
324 ErrorKind::InvalidData,
325 "Intermediate entry point structure checksum verification failed",
326 ));
327 }
328
329 Ok(SMBiosEntryPoint32 { raw })
330 }
331}
332
333impl fmt::Debug for SMBiosEntryPoint32 {
334 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
335 fmt.debug_struct(std::any::type_name::<SMBiosEntryPoint32>())
336 .field(
337 "entry_point_structure_checksum",
338 &self.entry_point_structure_checksum(),
339 )
340 .field("entry_point_length", &self.entry_point_length())
341 .field("major_version", &self.major_version())
342 .field("minor_version", &self.minor_version())
343 .field("maximum_structure_size", &self.maximum_structure_size())
344 .field("entry_point_revision", &self.entry_point_revision())
345 .field("formatted_area", &self.formatted_area())
346 .field("intermediate_anchor", &self.intermediate_anchor())
347 .field("intermediate_checksum", &self.intermediate_checksum())
348 .field("structure_table_length", &self.structure_table_length())
349 .field("structure_table_address", &self.structure_table_address())
350 .field(
351 "number_of_smbios_structures",
352 &self.number_of_smbios_structures(),
353 )
354 .field("bcd_revision", &self.bcd_revision())
355 .finish()
356 }
357}
358
359impl Serialize for SMBiosEntryPoint32 {
360 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
361 where
362 S: Serializer,
363 {
364 let mut state = serializer.serialize_struct("SMBiosEntryPoint32", 13)?;
365 state.serialize_field(
366 "entry_point_structure_checksum",
367 &self.entry_point_structure_checksum(),
368 )?;
369 state.serialize_field("entry_point_length", &self.entry_point_length())?;
370 state.serialize_field("major_version", &self.major_version())?;
371 state.serialize_field("minor_version", &self.minor_version())?;
372 state.serialize_field("maximum_structure_size", &self.maximum_structure_size())?;
373 state.serialize_field("entry_point_revision", &self.entry_point_revision())?;
374 state.serialize_field("formatted_area", &self.formatted_area())?;
375 state.serialize_field("intermediate_anchor", &self.intermediate_anchor())?;
376 state.serialize_field("intermediate_checksum", &self.intermediate_checksum())?;
377 state.serialize_field("structure_table_length", &self.structure_table_length())?;
378 state.serialize_field("structure_table_address", &self.structure_table_address())?;
379 state.serialize_field(
380 "number_of_smbios_structures",
381 &self.number_of_smbios_structures(),
382 )?;
383 state.serialize_field("bcd_revision", &self.bcd_revision())?;
384 state.end()
385 }
386}
387
388pub struct SMBiosEntryPoint64 {
398 raw: Vec<u8>,
399}
400
401impl<'a> SMBiosEntryPoint64 {
402 pub const MINIMUM_SIZE: usize = 0x18;
409
410 pub const SM3_ANCHOR: [u8; 5] = [b'_', b'S', b'M', b'3', b'_'];
412
413 pub const ENTRY_POINT_STRUCTURE_CHECKSUM_OFFSET: usize = 0x05;
415
416 pub const ENTRY_POINT_LENGTH_OFFSET: usize = 0x06;
418
419 pub const MAJOR_VERSION_OFFSET: usize = 0x07;
421
422 pub const MINOR_VERSION_OFFSET: usize = 0x08;
424
425 pub const DOCREV_OFFSET: usize = 0x09;
427
428 pub const ENTRY_POINT_REVISION_OFFSET: usize = 0x0A;
430
431 pub const STRUCTURE_TABLE_MAXIMUM_SIZE_OFFSET: usize = 0x0C;
433
434 pub const STRUCTURE_TABLE_ADDRESS_OFFSET: usize = 0x10;
436
437 pub fn entry_point_structure_checksum(&self) -> u8 {
446 self.raw[Self::ENTRY_POINT_STRUCTURE_CHECKSUM_OFFSET]
447 }
448
449 pub fn entry_point_length(&self) -> u8 {
454 self.raw[Self::ENTRY_POINT_LENGTH_OFFSET]
455 }
456
457 pub fn major_version(&self) -> u8 {
463 self.raw[Self::MAJOR_VERSION_OFFSET]
464 }
465
466 pub fn minor_version(&self) -> u8 {
472 self.raw[Self::MINOR_VERSION_OFFSET]
473 }
474
475 pub fn docrev(&self) -> u8 {
481 self.raw[Self::DOCREV_OFFSET]
482 }
483
484 pub fn entry_point_revision(&self) -> u8 {
492 self.raw[Self::ENTRY_POINT_REVISION_OFFSET]
493 }
494
495 pub fn structure_table_maximum_size(&self) -> u32 {
501 u32::from_le_bytes(
502 self.raw[Self::STRUCTURE_TABLE_MAXIMUM_SIZE_OFFSET
503 ..Self::STRUCTURE_TABLE_MAXIMUM_SIZE_OFFSET + 4]
504 .try_into()
505 .expect("u32 is 4 bytes"),
506 )
507 }
508
509 pub fn structure_table_address(&self) -> u64 {
515 u64::from_le_bytes(
516 self.raw
517 [Self::STRUCTURE_TABLE_ADDRESS_OFFSET..Self::STRUCTURE_TABLE_ADDRESS_OFFSET + 8]
518 .try_into()
519 .expect("u64 is 8 bytes"),
520 )
521 }
522
523 pub fn try_load_from_file(filename: &Path) -> Result<Self, Error> {
525 read(filename)?.try_into()
526 }
527
528 pub fn try_scan_from_file<T: Iterator<Item = u64>>(
531 file: &mut File,
532 range: T,
533 ) -> Result<Self, Error>
534 where
535 T: RangeBounds<u64>,
536 {
537 let mut anchor = [0; 5];
538 for offset in range.step_by(0x10) {
539 file.seek(SeekFrom::Start(offset))?;
540 file.read_exact(&mut anchor)?;
541 if anchor == Self::SM3_ANCHOR {
542 let mut length = [0; 2];
543 file.read_exact(&mut length)?;
544 let struct_length = length[1] as usize;
545 let mut entry_point_buffer = Vec::with_capacity(struct_length);
546 entry_point_buffer.resize(struct_length, 0);
547 file.seek(SeekFrom::Start(offset))?;
548 file.read_exact(&mut entry_point_buffer)?;
549 let entry_point: Self = entry_point_buffer.try_into()?;
550 return Ok(entry_point);
551 }
552 }
553 Err(Error::new(ErrorKind::UnexpectedEof, "Not found"))
554 }
555}
556
557impl fmt::Debug for SMBiosEntryPoint64 {
558 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
559 fmt.debug_struct(std::any::type_name::<SMBiosEntryPoint64>())
560 .field(
561 "entry_point_structure_checksum",
562 &self.entry_point_structure_checksum(),
563 )
564 .field("entry_point_length", &self.entry_point_length())
565 .field("major_version", &self.major_version())
566 .field("minor_version", &self.minor_version())
567 .field("docrev", &self.docrev())
568 .field(
569 "structure_table_maximum_size",
570 &self.structure_table_maximum_size(),
571 )
572 .field("structure_table_address", &self.structure_table_address())
573 .finish()
574 }
575}
576
577impl Serialize for SMBiosEntryPoint64 {
578 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
579 where
580 S: Serializer,
581 {
582 let mut state = serializer.serialize_struct("SMBiosEntryPoint64", 7)?;
583 state.serialize_field(
584 "entry_point_structure_checksum",
585 &self.entry_point_structure_checksum(),
586 )?;
587 state.serialize_field("entry_point_length", &self.entry_point_length())?;
588 state.serialize_field("major_version", &self.major_version())?;
589 state.serialize_field("minor_version", &self.minor_version())?;
590 state.serialize_field("docrev", &self.docrev())?;
591 state.serialize_field(
592 "structure_table_maximum_size",
593 &self.structure_table_maximum_size(),
594 )?;
595 state.serialize_field("structure_table_address", &self.structure_table_address())?;
596 state.end()
597 }
598}
599
600impl<'a> TryFrom<Vec<u8>> for SMBiosEntryPoint64 {
601 type Error = Error;
602
603 fn try_from(raw: Vec<u8>) -> Result<Self, Self::Error> {
604 if raw.len() < Self::MINIMUM_SIZE {
605 return Err(Error::new(
606 ErrorKind::InvalidData,
607 "Slice is smaller than SMBiosEntryPoint64::MINIMUM_SIZE",
608 ));
609 }
610
611 if !raw
612 .iter()
613 .zip(Self::SM3_ANCHOR.iter())
614 .all(|pair| pair.0 == pair.1)
615 {
616 return Err(Error::new(
617 ErrorKind::InvalidData,
618 "Expected _SM3_ identifier not found",
619 ));
620 }
621
622 let entry_point_length = raw[Self::ENTRY_POINT_LENGTH_OFFSET] as usize;
625 match raw.get(0..entry_point_length) {
626 Some(checked_bytes) => {
627 if !verify_checksum(checked_bytes) {
628 return Err(Error::new(ErrorKind::InvalidData,"Entry Point Structure checksum verification failed"));
629 }
630 }
631 None => return Err(Error::new(ErrorKind::InvalidData,"The Entry Point Length field specified a value which exceeded the bounds of the Entry Point Structure")),
632 }
633
634 Ok(SMBiosEntryPoint64 { raw })
635 }
636}
637
638fn verify_checksum(data: &[u8]) -> bool {
647 let mut sum = Wrapping(0u8);
648
649 data.iter().for_each(|b| sum += Wrapping(*b));
650
651 sum == Wrapping(0)
652}