use serde::{ser::SerializeStruct, Serialize, Serializer};
use std::{
convert::TryFrom,
convert::TryInto,
fmt,
fs::{read, File},
io::{prelude::*, Error, ErrorKind, SeekFrom},
num::Wrapping,
ops::RangeBounds,
path::Path,
};
pub struct SMBiosEntryPoint32 {
raw: Vec<u8>,
}
impl<'a> SMBiosEntryPoint32 {
pub const MINIMUM_SIZE: usize = 0x1F;
pub const SM_ANCHOR: [u8; 4] = [b'_', b'S', b'M', b'_'];
pub const DMI_ANCHOR: [u8; 5] = [b'_', b'D', b'M', b'I', b'_'];
pub const ENTRY_POINT_STRUCTURE_CHECKSUM_OFFSET: usize = 0x04;
pub const ENTRY_POINT_LENGTH_OFFSET: usize = 0x05;
pub const MAJOR_VERSION_OFFSET: usize = 0x06;
pub const MINOR_VERSION_OFFSET: usize = 0x07;
pub const MAXIMUM_STRUCTURE_SIZE_OFFSET: usize = 0x08;
pub const ENTRY_POINT_REVISION_OFFSET: usize = 0x0A;
pub const FORMATTED_AREA_OFFSET: usize = 0x0B;
pub const INTERMEDIATE_ANCHOR_OFFSET: usize = 0x10;
pub const INTERMEDIATE_CHECKSUM_OFFSET: usize = 0x15;
pub const STRUCTURE_TABLE_LENGTH_OFFSET: usize = 0x16;
pub const STRUCTURE_TABLE_ADDRESS_OFFSET: usize = 0x18;
pub const NUMBER_OF_SMBIOS_STRUCTURES_OFFSET: usize = 0x1C;
pub const BCD_REVISION_OFFSET: usize = 0x1E;
pub fn entry_point_structure_checksum(&self) -> u8 {
self.raw[Self::ENTRY_POINT_STRUCTURE_CHECKSUM_OFFSET]
}
pub fn entry_point_length(&self) -> u8 {
self.raw[Self::ENTRY_POINT_LENGTH_OFFSET]
}
pub fn major_version(&self) -> u8 {
self.raw[Self::MAJOR_VERSION_OFFSET]
}
pub fn minor_version(&self) -> u8 {
self.raw[Self::MINOR_VERSION_OFFSET]
}
pub fn maximum_structure_size(&self) -> u16 {
u16::from_le_bytes(
self.raw[Self::MAXIMUM_STRUCTURE_SIZE_OFFSET..Self::MAXIMUM_STRUCTURE_SIZE_OFFSET + 2]
.try_into()
.expect("u16 is 2 bytes"),
)
}
pub fn entry_point_revision(&self) -> u8 {
self.raw[Self::ENTRY_POINT_REVISION_OFFSET]
}
pub fn formatted_area(&self) -> [u8; 5] {
self.raw[Self::FORMATTED_AREA_OFFSET..Self::FORMATTED_AREA_OFFSET + 5]
.try_into()
.expect("5 bytes")
}
pub fn intermediate_anchor(&self) -> [u8; 5] {
self.raw[Self::INTERMEDIATE_ANCHOR_OFFSET..Self::INTERMEDIATE_ANCHOR_OFFSET + 5]
.try_into()
.expect("5 bytes")
}
pub fn intermediate_checksum(&self) -> u8 {
self.raw[Self::INTERMEDIATE_CHECKSUM_OFFSET]
}
pub fn structure_table_length(&self) -> u16 {
u16::from_le_bytes(
self.raw[Self::STRUCTURE_TABLE_LENGTH_OFFSET..Self::STRUCTURE_TABLE_LENGTH_OFFSET + 2]
.try_into()
.expect("u16 is 2 bytes"),
)
}
pub fn structure_table_address(&self) -> u32 {
u32::from_le_bytes(
self.raw
[Self::STRUCTURE_TABLE_ADDRESS_OFFSET..Self::STRUCTURE_TABLE_ADDRESS_OFFSET + 4]
.try_into()
.expect("u32 is 4 bytes"),
)
}
pub fn number_of_smbios_structures(&self) -> u16 {
u16::from_le_bytes(
self.raw[Self::NUMBER_OF_SMBIOS_STRUCTURES_OFFSET
..Self::NUMBER_OF_SMBIOS_STRUCTURES_OFFSET + 2]
.try_into()
.expect("u16 is 2 bytes"),
)
}
pub fn bcd_revision(&self) -> u8 {
self.raw[Self::BCD_REVISION_OFFSET]
}
pub fn try_load_from_file(filename: &Path) -> Result<Self, Error> {
read(filename)?.try_into()
}
pub fn try_scan_from_file<T: Iterator<Item = u64>>(
file: &mut File,
range: T,
) -> Result<Self, Error>
where
T: RangeBounds<u64>,
{
let mut anchor = [0; 4];
for offset in range.step_by(0x10) {
file.seek(SeekFrom::Start(offset))?;
file.read_exact(&mut anchor)?;
if anchor == Self::SM_ANCHOR {
let mut length = [0; 2];
file.read_exact(&mut length)?;
let struct_length = length[1] as usize;
let mut entry_point_buffer = Vec::with_capacity(struct_length);
entry_point_buffer.resize(struct_length, 0);
file.seek(SeekFrom::Start(offset))?;
file.read_exact(&mut entry_point_buffer)?;
let entry_point: Self = entry_point_buffer.try_into()?;
return Ok(entry_point);
}
}
Err(Error::new(ErrorKind::UnexpectedEof, "Not found"))
}
}
impl<'a> TryFrom<Vec<u8>> for SMBiosEntryPoint32 {
type Error = Error;
fn try_from(raw: Vec<u8>) -> Result<Self, Self::Error> {
if raw.len() < Self::MINIMUM_SIZE {
return Err(Error::new(
ErrorKind::InvalidData,
"Slice is smaller than SMBiosEntryPoint32::MINIMUM_SIZE",
));
}
if !raw
.iter()
.zip(Self::SM_ANCHOR.iter())
.all(|pair| pair.0 == pair.1)
{
return Err(Error::new(ErrorKind::InvalidData, "_SM_ anchor not found"));
}
let entry_point_length = raw[Self::ENTRY_POINT_LENGTH_OFFSET] as usize;
match raw.get(0..entry_point_length) {
Some(checked_bytes) => {
if !verify_checksum(checked_bytes) {
return Err(Error::new(
ErrorKind::InvalidData,"Entry Point Structure checksum verification failed"));
}
}
None => return Err(Error::new(
ErrorKind::InvalidData,"The Entry Point Length field specified a value which exceeded the bounds of the Entry Point Structure")),
}
let intermediate_anchor: [u8; 5] = raw
[Self::INTERMEDIATE_ANCHOR_OFFSET..Self::INTERMEDIATE_ANCHOR_OFFSET + 5]
.try_into()
.expect("5 bytes");
if !intermediate_anchor
.iter()
.zip(Self::DMI_ANCHOR.iter())
.all(|pair| pair.0 == pair.1)
{
return Err(Error::new(ErrorKind::InvalidData, "_DMI_ anchor not found"));
}
let intermediate_entry_point_structure: [u8; 0x0F] = raw
[Self::INTERMEDIATE_ANCHOR_OFFSET..]
.try_into()
.expect("0x0F bytes");
if !verify_checksum(&intermediate_entry_point_structure) {
return Err(Error::new(
ErrorKind::InvalidData,
"Intermediate entry point structure checksum verification failed",
));
}
Ok(SMBiosEntryPoint32 { raw })
}
}
impl fmt::Debug for SMBiosEntryPoint32 {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_struct(std::any::type_name::<SMBiosEntryPoint32>())
.field(
"entry_point_structure_checksum",
&self.entry_point_structure_checksum(),
)
.field("entry_point_length", &self.entry_point_length())
.field("major_version", &self.major_version())
.field("minor_version", &self.minor_version())
.field("maximum_structure_size", &self.maximum_structure_size())
.field("entry_point_revision", &self.entry_point_revision())
.field("formatted_area", &self.formatted_area())
.field("intermediate_anchor", &self.intermediate_anchor())
.field("intermediate_checksum", &self.intermediate_checksum())
.field("structure_table_length", &self.structure_table_length())
.field("structure_table_address", &self.structure_table_address())
.field(
"number_of_smbios_structures",
&self.number_of_smbios_structures(),
)
.field("bcd_revision", &self.bcd_revision())
.finish()
}
}
impl Serialize for SMBiosEntryPoint32 {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut state = serializer.serialize_struct("SMBiosEntryPoint32", 13)?;
state.serialize_field(
"entry_point_structure_checksum",
&self.entry_point_structure_checksum(),
)?;
state.serialize_field("entry_point_length", &self.entry_point_length())?;
state.serialize_field("major_version", &self.major_version())?;
state.serialize_field("minor_version", &self.minor_version())?;
state.serialize_field("maximum_structure_size", &self.maximum_structure_size())?;
state.serialize_field("entry_point_revision", &self.entry_point_revision())?;
state.serialize_field("formatted_area", &self.formatted_area())?;
state.serialize_field("intermediate_anchor", &self.intermediate_anchor())?;
state.serialize_field("intermediate_checksum", &self.intermediate_checksum())?;
state.serialize_field("structure_table_length", &self.structure_table_length())?;
state.serialize_field("structure_table_address", &self.structure_table_address())?;
state.serialize_field(
"number_of_smbios_structures",
&self.number_of_smbios_structures(),
)?;
state.serialize_field("bcd_revision", &self.bcd_revision())?;
state.end()
}
}
pub struct SMBiosEntryPoint64 {
raw: Vec<u8>,
}
impl<'a> SMBiosEntryPoint64 {
pub const MINIMUM_SIZE: usize = 0x18;
pub const SM3_ANCHOR: [u8; 5] = [b'_', b'S', b'M', b'3', b'_'];
pub const ENTRY_POINT_STRUCTURE_CHECKSUM_OFFSET: usize = 0x05;
pub const ENTRY_POINT_LENGTH_OFFSET: usize = 0x06;
pub const MAJOR_VERSION_OFFSET: usize = 0x07;
pub const MINOR_VERSION_OFFSET: usize = 0x08;
pub const DOCREV_OFFSET: usize = 0x09;
pub const ENTRY_POINT_REVISION_OFFSET: usize = 0x0A;
pub const STRUCTURE_TABLE_MAXIMUM_SIZE_OFFSET: usize = 0x0C;
pub const STRUCTURE_TABLE_ADDRESS_OFFSET: usize = 0x10;
pub fn entry_point_structure_checksum(&self) -> u8 {
self.raw[Self::ENTRY_POINT_STRUCTURE_CHECKSUM_OFFSET]
}
pub fn entry_point_length(&self) -> u8 {
self.raw[Self::ENTRY_POINT_LENGTH_OFFSET]
}
pub fn major_version(&self) -> u8 {
self.raw[Self::MAJOR_VERSION_OFFSET]
}
pub fn minor_version(&self) -> u8 {
self.raw[Self::MINOR_VERSION_OFFSET]
}
pub fn docrev(&self) -> u8 {
self.raw[Self::DOCREV_OFFSET]
}
pub fn entry_point_revision(&self) -> u8 {
self.raw[Self::ENTRY_POINT_REVISION_OFFSET]
}
pub fn structure_table_maximum_size(&self) -> u32 {
u32::from_le_bytes(
self.raw[Self::STRUCTURE_TABLE_MAXIMUM_SIZE_OFFSET
..Self::STRUCTURE_TABLE_MAXIMUM_SIZE_OFFSET + 4]
.try_into()
.expect("u32 is 4 bytes"),
)
}
pub fn structure_table_address(&self) -> u64 {
u64::from_le_bytes(
self.raw
[Self::STRUCTURE_TABLE_ADDRESS_OFFSET..Self::STRUCTURE_TABLE_ADDRESS_OFFSET + 4]
.try_into()
.expect("u64 is 8 bytes"),
)
}
pub fn try_load_from_file(filename: &Path) -> Result<Self, Error> {
read(filename)?.try_into()
}
pub fn try_scan_from_file<T: Iterator<Item = u64>>(
file: &mut File,
range: T,
) -> Result<Self, Error>
where
T: RangeBounds<u64>,
{
let mut anchor = [0; 5];
for offset in range.step_by(0x10) {
file.seek(SeekFrom::Start(offset))?;
file.read_exact(&mut anchor)?;
if anchor == Self::SM3_ANCHOR {
let mut length = [0; 2];
file.read_exact(&mut length)?;
let struct_length = length[1] as usize;
let mut entry_point_buffer = Vec::with_capacity(struct_length);
entry_point_buffer.resize(struct_length, 0);
file.seek(SeekFrom::Start(offset))?;
file.read_exact(&mut entry_point_buffer)?;
let entry_point: Self = entry_point_buffer.try_into()?;
return Ok(entry_point);
}
}
Err(Error::new(ErrorKind::UnexpectedEof, "Not found"))
}
}
impl fmt::Debug for SMBiosEntryPoint64 {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_struct(std::any::type_name::<SMBiosEntryPoint64>())
.field(
"entry_point_structure_checksum",
&self.entry_point_structure_checksum(),
)
.field("entry_point_length", &self.entry_point_length())
.field("major_version", &self.major_version())
.field("minor_version", &self.minor_version())
.field("docrev", &self.docrev())
.field(
"structure_table_maximum_size",
&self.structure_table_maximum_size(),
)
.field("structure_table_address", &self.structure_table_address())
.finish()
}
}
impl Serialize for SMBiosEntryPoint64 {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut state = serializer.serialize_struct("SMBiosEntryPoint64", 7)?;
state.serialize_field(
"entry_point_structure_checksum",
&self.entry_point_structure_checksum(),
)?;
state.serialize_field("entry_point_length", &self.entry_point_length())?;
state.serialize_field("major_version", &self.major_version())?;
state.serialize_field("minor_version", &self.minor_version())?;
state.serialize_field("docrev", &self.docrev())?;
state.serialize_field(
"structure_table_maximum_size",
&self.structure_table_maximum_size(),
)?;
state.serialize_field("structure_table_address", &self.structure_table_address())?;
state.end()
}
}
impl<'a> TryFrom<Vec<u8>> for SMBiosEntryPoint64 {
type Error = Error;
fn try_from(raw: Vec<u8>) -> Result<Self, Self::Error> {
if raw.len() < Self::MINIMUM_SIZE {
return Err(Error::new(
ErrorKind::InvalidData,
"Slice is smaller than SMBiosEntryPoint64::MINIMUM_SIZE",
));
}
if !raw
.iter()
.zip(Self::SM3_ANCHOR.iter())
.all(|pair| pair.0 == pair.1)
{
return Err(Error::new(
ErrorKind::InvalidData,
"Expected _SM3_ identifier not found",
));
}
let entry_point_length = raw[Self::ENTRY_POINT_LENGTH_OFFSET] as usize;
match raw.get(0..entry_point_length) {
Some(checked_bytes) => {
if !verify_checksum(checked_bytes) {
return Err(Error::new(ErrorKind::InvalidData,"Entry Point Structure checksum verification failed"));
}
}
None => return Err(Error::new(ErrorKind::InvalidData,"The Entry Point Length field specified a value which exceeded the bounds of the Entry Point Structure")),
}
Ok(SMBiosEntryPoint64 { raw })
}
}
fn verify_checksum(data: &[u8]) -> bool {
let mut sum = Wrapping(0u8);
data.iter().for_each(|b| sum += Wrapping(*b));
sum == Wrapping(0)
}