smbioslib/core/
entry_point.rs

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
13/// # SMBIOS 2.1 (32 bit) Entry Point structure
14///
15/// On non-UEFI systems, the 32-bit SMBIOS Entry Point structure, can be located by application software
16/// by searching for the anchor-string on paragraph (16-byte) boundaries within the physical memory address
17/// range 000F0000h to 000FFFFFh. This entry point encapsulates an intermediate anchor string that is used
18/// by some existing DMI browsers.
19///
20/// On UEFI-based systems, the SMBIOS Entry Point structure can be located by looking in the EFI
21/// Configuration Table for the SMBIOS GUID (SMBIOS_TABLE_GUID, {EB9D2D31-2D88-11D3-9A16-
22/// 0090273FC14D}) and using the associated pointer. See section 4.6 of the UEFI Specification for details.
23/// See section 2.3 of the UEFI Specification for how to report the containing memory type.
24pub struct SMBiosEntryPoint32 {
25    raw: Vec<u8>,
26}
27
28impl<'a> SMBiosEntryPoint32 {
29    /// Minimum acceptable size of this structure
30    ///
31    /// TODO: Review DMTF SMBIOS document history and see
32    /// if structure sizes smaller than 0x1F existed.  If
33    /// so then change this structure design to return Option<>
34    /// values and adjust this size accordingly.
35    pub const MINIMUM_SIZE: usize = 0x1F;
36
37    /// Anchor String "_SM_" (offset 0)
38    pub const SM_ANCHOR: [u8; 4] = [b'_', b'S', b'M', b'_'];
39
40    /// Anchor String "_DMI_" (offset 0x10)
41    pub const DMI_ANCHOR: [u8; 5] = [b'_', b'D', b'M', b'I', b'_'];
42
43    /// Entry Point Structure Checksum Offset
44    pub const ENTRY_POINT_STRUCTURE_CHECKSUM_OFFSET: usize = 0x04;
45
46    /// Entry Point Length Offset
47    pub const ENTRY_POINT_LENGTH_OFFSET: usize = 0x05;
48
49    /// SMBIOS Major Version Offset
50    pub const MAJOR_VERSION_OFFSET: usize = 0x06;
51
52    /// SMBIOS Minor Version Offset
53    pub const MINOR_VERSION_OFFSET: usize = 0x07;
54
55    /// Maximum Structure Size Offset
56    pub const MAXIMUM_STRUCTURE_SIZE_OFFSET: usize = 0x08;
57
58    /// Entry Point Revision Offset
59    pub const ENTRY_POINT_REVISION_OFFSET: usize = 0x0A;
60
61    /// Formatted Area Offset
62    pub const FORMATTED_AREA_OFFSET: usize = 0x0B;
63
64    /// Intermediate Anchor String Offset
65    ///
66    /// NOTE: This field is paragraph-aligned, to allow legacy DMI browsers to
67    /// find this entry point within the SMBIOS Entry Point Structure.
68    pub const INTERMEDIATE_ANCHOR_OFFSET: usize = 0x10;
69
70    /// Intermediate Checksum Offset
71    pub const INTERMEDIATE_CHECKSUM_OFFSET: usize = 0x15;
72
73    /// Structure Table Length Offset
74    pub const STRUCTURE_TABLE_LENGTH_OFFSET: usize = 0x16;
75
76    /// Structure Table Address Offset
77    pub const STRUCTURE_TABLE_ADDRESS_OFFSET: usize = 0x18;
78
79    /// Number of SMBIOS Structures Offset
80    pub const NUMBER_OF_SMBIOS_STRUCTURES_OFFSET: usize = 0x1C;
81
82    /// SMBIOS BCD Revision Offset
83    pub const BCD_REVISION_OFFSET: usize = 0x1E;
84
85    /// Entry Point Structure Checksum
86    ///
87    /// Checksum of the Entry Point Structure (EPS)
88    ///
89    /// This value, when added to all other bytes in the EPS, results in
90    /// the value 00h (using 8-bit addition calculations). Values in the
91    /// EPS are summed starting at offset 00h, for `entry_point_length`
92    /// bytes.
93    pub fn entry_point_structure_checksum(&self) -> u8 {
94        self.raw[Self::ENTRY_POINT_STRUCTURE_CHECKSUM_OFFSET]
95    }
96
97    /// Entry Point Length
98    ///
99    /// Length of the Entry Point Structure, starting with the Anchor String
100    /// field, in bytes, currently 1Fh
101    ///
102    /// NOTE: This value was incorrectly stated in version 2.1 of this specification
103    /// as 1Eh. Because of this, there might be version 2.1
104    /// implementations that use either the 1Eh or the 1Fh value, but
105    /// version 2.2 or later implementations must use the 1Fh value.
106    pub fn entry_point_length(&self) -> u8 {
107        self.raw[Self::ENTRY_POINT_LENGTH_OFFSET]
108    }
109
110    /// SMBIOS Major Version
111    ///
112    /// Major version of this specification implemented in the table
113    /// structures (for example, the value is 0Ah (10) for revision 10.22 and
114    /// 02h for revision 2.1)
115    pub fn major_version(&self) -> u8 {
116        self.raw[Self::MAJOR_VERSION_OFFSET]
117    }
118
119    /// SMBIOS Minor Version
120    ///
121    /// Minor version of this specification implemented in the table
122    /// structures (for example, the value is 16h (22) for revision 10.22 and
123    /// 01h for revision 2.1)
124    pub fn minor_version(&self) -> u8 {
125        self.raw[Self::MINOR_VERSION_OFFSET]
126    }
127
128    /// Maximum Structure Size
129    ///
130    /// Size of the largest SMBIOS structure, in bytes, and encompasses
131    /// the structure’s formatted area and text strings
132    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    /// Entry Point Revision
141    ///
142    /// EPS revision implemented in this structure and identifies the
143    /// formatting of offsets 0Bh to 0Fh as follows:
144    /// - 00h Entry Point is based on SMBIOS 2.1 definition; formatted area is reserved and set to all 00h.
145    /// - 01h-FFh Reserved for assignment by this specification
146    pub fn entry_point_revision(&self) -> u8 {
147        self.raw[Self::ENTRY_POINT_REVISION_OFFSET]
148    }
149
150    /// Formatted Area
151    ///
152    /// Value present in the `entry_point_revision` field defines the
153    /// interpretation to be placed upon these 5 bytes
154    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    /// Intermediate Anchor String
161    ///
162    /// _DMI_, specified as five ASCII characters (5F 44 4D 49 5F).
163    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    /// Intermediate Checksum
170    ///
171    /// Checksum of Intermediate Entry Point Structure (IEPS).
172    ///
173    /// This value, when added to all other bytes in the IEPS, results in
174    /// the value 00h (using 8-bit addition calculations). Values in the
175    /// IEPS are summed starting at offset 10h, for 0Fh bytes.
176    pub fn intermediate_checksum(&self) -> u8 {
177        self.raw[Self::INTERMEDIATE_CHECKSUM_OFFSET]
178    }
179
180    /// Structure Table Length
181    ///
182    /// Total length of SMBIOS Structure Table, pointed to by the
183    /// `structure_table_address`, in bytes
184    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    /// Structure Table Address
193    ///
194    /// 32-bit physical starting address of the read-only SMBIOS
195    /// Structure Table, which can start at any 32-bit address
196    /// This area contains all of the SMBIOS structures fully packed
197    /// together. These structures can then be parsed to produce exactly
198    /// the same format as that returned from a Get SMBIOS Structure
199    /// function call.
200    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    /// Number of SMBIOS Structures
210    ///
211    /// Total number of structures present in the SMBIOS Structure Table
212    /// This is the value returned as NumStructures from the Get
213    /// SMBIOS Information function.
214    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    /// SMBIOS BCD Revision
224    ///
225    /// Indicates compliance with a revision of this specification
226    /// It is a BCD value where the upper nibble indicates the major
227    /// version and the lower nibble the minor version. For revision 2.1,
228    /// the returned value is 21h. If the value is 00h, only the Major and
229    /// Minor Versions in offsets 6 and 7 of the Entry Point Structure
230    /// provide the version information.
231    pub fn bcd_revision(&self) -> u8 {
232        self.raw[Self::BCD_REVISION_OFFSET]
233    }
234
235    /// Load this structure from a file
236    pub fn try_load_from_file(filename: &Path) -> Result<Self, Error> {
237        read(filename)?.try_into()
238    }
239
240    /// Load this structure by scanning a file within the given offsets,
241    /// looking for the [SMBiosEntryPoint32::SM_ANCHOR] string.
242    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        // Verify the EPS checksum
289        // The checksum is calculated for a length of `entry_point_length`
290        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        // Verify the IEPS checksum
316        // The checksum is calculated for a length of 0x0F
317        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
388/// # SMBIOS 3.0 (64 bit) Entry Point structure
389///
390/// On non-UEFI systems, the 64-bit SMBIOS Entry Point structure can be located by application software by
391/// searching for the anchor-string on paragraph (16-byte) boundaries within the physical memory address
392/// range 000F0000h to 000FFFFFh.
393///
394/// On UEFI-based systems, the SMBIOS Entry Point structure can be located by looking in the EFI
395/// Configuration Table for the SMBIOS 3.x GUID (SMBIOS3_TABLE_GUID, {F2FD1544-9794-4A2C-992E836 E5BBCF20E394}) and using the associated pointer. See section 4.6 of the UEFI Specification for details.
396/// See section 2.3 of the UEFI Specification for how to report the containing memory type.
397pub struct SMBiosEntryPoint64 {
398    raw: Vec<u8>,
399}
400
401impl<'a> SMBiosEntryPoint64 {
402    /// Minimum acceptable size of this structure
403    ///
404    /// TODO: Review DMTF SMBIOS document history and see
405    /// if structure sizes smaller than 0x18 existed.  If
406    /// so then change this structure design to return Option<>
407    /// values and adjust this size accordingly.
408    pub const MINIMUM_SIZE: usize = 0x18;
409
410    /// Anchor String "_SM3_" (offset 0)
411    pub const SM3_ANCHOR: [u8; 5] = [b'_', b'S', b'M', b'3', b'_'];
412
413    /// Entry Point Structure Checksum Offset
414    pub const ENTRY_POINT_STRUCTURE_CHECKSUM_OFFSET: usize = 0x05;
415
416    /// Entry Point Length Offset
417    pub const ENTRY_POINT_LENGTH_OFFSET: usize = 0x06;
418
419    /// SMBIOS Major Version Offset
420    pub const MAJOR_VERSION_OFFSET: usize = 0x07;
421
422    /// SMBIOS Minor Version Offset
423    pub const MINOR_VERSION_OFFSET: usize = 0x08;
424
425    /// SMBIOS Docrev
426    pub const DOCREV_OFFSET: usize = 0x09;
427
428    /// Entry Point Revision Offset
429    pub const ENTRY_POINT_REVISION_OFFSET: usize = 0x0A;
430
431    /// Structure Table Maximum Size Offset
432    pub const STRUCTURE_TABLE_MAXIMUM_SIZE_OFFSET: usize = 0x0C;
433
434    /// Structure Table Address Offset
435    pub const STRUCTURE_TABLE_ADDRESS_OFFSET: usize = 0x10;
436
437    /// Entry Point Structure Checksum
438    ///
439    /// Checksum of the Entry Point Structure (EPS)
440    ///
441    /// This value, when added to all other bytes in the EPS, results in
442    /// the value 00h (using 8-bit addition calculations). Values in the
443    /// EPS are summed starting at offset 00h, for `entry_point_length`
444    /// bytes.
445    pub fn entry_point_structure_checksum(&self) -> u8 {
446        self.raw[Self::ENTRY_POINT_STRUCTURE_CHECKSUM_OFFSET]
447    }
448
449    /// Entry Point Length
450    ///
451    /// Length of the Entry Point Structure, starting with the Anchor String
452    /// field, in bytes, currently 18h
453    pub fn entry_point_length(&self) -> u8 {
454        self.raw[Self::ENTRY_POINT_LENGTH_OFFSET]
455    }
456
457    /// SMBIOS Major Version
458    ///
459    /// Major version of this specification implemented in the table
460    /// structures (for example, the value is 0Ah (10) for revision 10.22 and
461    /// 02h for revision 2.1)
462    pub fn major_version(&self) -> u8 {
463        self.raw[Self::MAJOR_VERSION_OFFSET]
464    }
465
466    /// SMBIOS Minor Version
467    ///
468    /// Minor version of this specification implemented in the table
469    /// structures (for example, the value is 16h (22) for revision 10.22 and
470    /// 01h for revision 2.1)
471    pub fn minor_version(&self) -> u8 {
472        self.raw[Self::MINOR_VERSION_OFFSET]
473    }
474
475    /// SMBIOS Docrev
476    ///
477    /// Identifies the docrev of this specification implemented in the table
478    /// structures (for example, the value is 00h for revision 10.22.0 and
479    /// 01h for revision 2.7.1).
480    pub fn docrev(&self) -> u8 {
481        self.raw[Self::DOCREV_OFFSET]
482    }
483
484    /// Entry Point Revision
485    ///
486    /// EPS revision implemented in this structure and identifies the
487    /// formatting of offsets 0Bh and beyond as follows:
488    /// - 00h Reserved for assignment by this specification
489    /// - 01h Entry Point is based on SMBIOS 3.0 definition;
490    /// - 02h-FFh Reserved for assignment by this specification; offsets 0Ch-17h are defined per revision 01h
491    pub fn entry_point_revision(&self) -> u8 {
492        self.raw[Self::ENTRY_POINT_REVISION_OFFSET]
493    }
494
495    /// Structure Table Maximum Size
496    ///
497    /// Maximum size of SMBIOS Structure Table, pointed to by the
498    /// Structure Table Address, in bytes. The actual size is guaranteed
499    /// to be less or equal to the maximum size.
500    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    /// Structure Table Address
510    ///
511    /// The 64-bit physical starting address of the read-only SMBIOS
512    /// Structure Table, which can start at any 64-bit address. This area
513    /// contains all of the SMBIOS structures fully packed together
514    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    /// Load this structure from a file
524    pub fn try_load_from_file(filename: &Path) -> Result<Self, Error> {
525        read(filename)?.try_into()
526    }
527
528    /// Load this structure by scanning a file within the given offsets,
529    /// looking for the [SMBiosEntryPoint64::SM3_ANCHOR] string.
530    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        // Verify the checksum
623        // The checksum is calculated for a length of `entry_point_length`
624        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
638/// Verifies EPS and IEPS Checksums
639///
640/// The EPS and IEPS contain a checksum value.
641///
642/// The checksum value, when added to all other bytes in the EPS, results in
643/// the value 00h (using 8-bit addition [Wrapping] calculations).
644/// Values in the EPS are summed starting at offset 00h, for 'entry_point_length'
645/// bytes.
646fn 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}