use crate::{SMBiosStruct, UndefinedStruct};
use serde::{ser::SerializeStruct, Serialize, Serializer};
use std::fmt;
use std::ops::Deref;
pub struct SMBiosInformation<'a> {
parts: &'a UndefinedStruct,
}
impl<'a> SMBiosStruct<'a> for SMBiosInformation<'a> {
const STRUCT_TYPE: u8 = 0u8;
fn new(parts: &'a UndefinedStruct) -> Self {
Self { parts }
}
fn parts(&self) -> &'a UndefinedStruct {
self.parts
}
}
impl<'a> SMBiosInformation<'a> {
pub fn vendor(&self) -> Option<String> {
self.parts.get_field_string(0x4)
}
pub fn version(&self) -> Option<String> {
self.parts.get_field_string(0x5)
}
pub fn starting_address_segment(&self) -> Option<u16> {
self.parts.get_field_word(0x6)
}
pub fn release_date(&self) -> Option<String> {
self.parts.get_field_string(0x8)
}
pub fn rom_size(&self) -> Option<u8> {
self.parts.get_field_byte(0x9)
}
pub fn characteristics(&self) -> Option<BiosCharacteristics> {
self.parts
.get_field_dword(0xA)
.map(|raw| BiosCharacteristics::from(raw))
}
pub fn bios_vendor_reserved_characteristics(&self) -> Option<u16> {
self.parts.get_field_word(0xE)
}
pub fn system_vendor_reserved_characteristics(&self) -> Option<u16> {
self.parts.get_field_word(0x10)
}
pub fn characteristics_extension0(&self) -> Option<BiosCharacteristicsExtension0> {
self.parts
.get_field_byte(0x12)
.map(|raw| BiosCharacteristicsExtension0::from(raw))
}
pub fn characteristics_extension1(&self) -> Option<BiosCharacteristicsExtension1> {
self.parts
.get_field_byte(0x13)
.map(|raw| BiosCharacteristicsExtension1::from(raw))
}
pub fn system_bios_major_release(&self) -> Option<u8> {
self.parts.get_field_byte(0x14)
}
pub fn system_bios_minor_release(&self) -> Option<u8> {
self.parts.get_field_byte(0x15)
}
pub fn e_c_firmware_major_release(&self) -> Option<u8> {
self.parts.get_field_byte(0x16)
}
pub fn e_c_firmware_minor_release(&self) -> Option<u8> {
self.parts.get_field_byte(0x17)
}
pub fn extended_rom_size(&self) -> Option<ExtendedRomSize> {
self.parts
.get_field_word(0x18)
.map(|raw| ExtendedRomSize::from(raw))
}
}
#[derive(Serialize, Debug, PartialEq, Eq)]
pub enum ExtendedRomSize {
Megabytes(u16),
Gigabytes(u16),
Undefined(u16),
}
impl From<u16> for ExtendedRomSize {
fn from(raw: u16) -> Self {
let unit = raw & 0b11000000_00000000;
let size = raw & 0b00111111_11111111;
if unit == 0b00000000_00000000 {
ExtendedRomSize::Megabytes(size)
} else if unit == 0b01000000_00000000 {
ExtendedRomSize::Gigabytes(size)
} else {
ExtendedRomSize::Undefined(raw)
}
}
}
impl fmt::Debug for SMBiosInformation<'_> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_struct(std::any::type_name::<SMBiosInformation<'_>>())
.field("header", &self.parts.header)
.field("vendor", &self.vendor())
.field("version", &self.version())
.field("starting_address_segment", &self.starting_address_segment())
.field("release_date", &self.release_date())
.field("rom_size", &self.rom_size())
.field("characteristics", &self.characteristics())
.field(
"bios_vendor_reserved_characteristics",
&self.bios_vendor_reserved_characteristics(),
)
.field(
"system_vendor_reserved_characteristics",
&self.system_vendor_reserved_characteristics(),
)
.field(
"characteristics_extension0",
&self.characteristics_extension0(),
)
.field(
"characteristics_extension1",
&self.characteristics_extension1(),
)
.field(
"system_bios_major_release",
&self.system_bios_major_release(),
)
.field(
"system_bios_minor_release",
&self.system_bios_minor_release(),
)
.field(
"e_c_firmware_major_release",
&self.e_c_firmware_major_release(),
)
.field(
"e_c_firmware_minor_release",
&self.e_c_firmware_minor_release(),
)
.field("extended_rom_size", &self.extended_rom_size())
.finish()
}
}
impl Serialize for SMBiosInformation<'_> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut state = serializer.serialize_struct("SMBiosInformation", 16)?;
state.serialize_field("header", &self.parts.header)?;
state.serialize_field("vendor", &self.vendor())?;
state.serialize_field("version", &self.version())?;
state.serialize_field("starting_address_segment", &self.starting_address_segment())?;
state.serialize_field("release_date", &self.release_date())?;
state.serialize_field("rom_size", &self.rom_size())?;
state.serialize_field("characteristics", &self.characteristics())?;
state.serialize_field(
"bios_vendor_reserved_characteristics",
&self.bios_vendor_reserved_characteristics(),
)?;
state.serialize_field(
"system_vendor_reserved_characteristics",
&self.system_vendor_reserved_characteristics(),
)?;
state.serialize_field(
"characteristics_extension0",
&self.characteristics_extension0(),
)?;
state.serialize_field(
"characteristics_extension1",
&self.characteristics_extension1(),
)?;
state.serialize_field(
"system_bios_major_release",
&self.system_bios_major_release(),
)?;
state.serialize_field(
"system_bios_minor_release",
&self.system_bios_minor_release(),
)?;
state.serialize_field(
"e_c_firmware_major_release",
&self.e_c_firmware_major_release(),
)?;
state.serialize_field(
"e_c_firmware_minor_release",
&self.e_c_firmware_minor_release(),
)?;
state.serialize_field("extended_rom_size", &self.extended_rom_size())?;
state.end()
}
}
#[derive(PartialEq, Eq)]
pub struct BiosCharacteristics {
pub raw: u32,
}
impl Deref for BiosCharacteristics {
type Target = u32;
fn deref(&self) -> &Self::Target {
&self.raw
}
}
impl From<u32> for BiosCharacteristics {
fn from(raw: u32) -> Self {
BiosCharacteristics { raw }
}
}
impl BiosCharacteristics {
pub fn unknown(&self) -> bool {
self.raw & 0x00000004 == 0x00000004
}
pub fn bios_characteristics_not_supported(&self) -> bool {
self.raw & 0x00000008 == 0x00000008
}
pub fn isa_supported(&self) -> bool {
self.raw & 0x00000010 == 0x00000010
}
pub fn mca_supported(&self) -> bool {
self.raw & 0x00000020 == 0x00000020
}
pub fn eisa_supported(&self) -> bool {
self.raw & 0x00000040 == 0x00000040
}
pub fn pci_supported(&self) -> bool {
self.raw & 0x00000080 == 0x00000080
}
pub fn pcmcia_supported(&self) -> bool {
self.raw & 0x00000100 == 0x00000100
}
pub fn plug_and_play_supported(&self) -> bool {
self.raw & 0x00000200 == 0x00000200
}
pub fn apm_supported(&self) -> bool {
self.raw & 0x00000400 == 0x00000400
}
pub fn bios_upgradeable(&self) -> bool {
self.raw & 0x00000800 == 0x00000800
}
pub fn bios_shadowing_allowed(&self) -> bool {
self.raw & 0x00001000 == 0x00001000
}
pub fn vlvesa_supported(&self) -> bool {
self.raw & 0x00002000 == 0x00002000
}
pub fn escd_support_available(&self) -> bool {
self.raw & 0x00004000 == 0x00004000
}
pub fn boot_from_cdsupported(&self) -> bool {
self.raw & 0x00008000 == 0x00008000
}
pub fn selectable_boot_supported(&self) -> bool {
self.raw & 0x00010000 == 0x00010000
}
pub fn bios_rom_socketed(&self) -> bool {
self.raw & 0x00020000 == 0x00020000
}
pub fn boot_from_pcmcia_supported(&self) -> bool {
self.raw & 0x00040000 == 0x00040000
}
pub fn edd_specification_supported(&self) -> bool {
self.raw & 0x00080000 == 0x00080000
}
pub fn floppy_nec_japanese_supported(&self) -> bool {
self.raw & 0x00100000 == 0x00100000
}
pub fn floppy_toshiba_japanese_supported(&self) -> bool {
self.raw & 0x00200000 == 0x00200000
}
pub fn floppy_525_360_supported(&self) -> bool {
self.raw & 0x00400000 == 0x00400000
}
pub fn floppy_525_12_supported(&self) -> bool {
self.raw & 0x00800000 == 0x00800000
}
pub fn floppy_35_720_supported(&self) -> bool {
self.raw & 0x01000000 == 0x01000000
}
pub fn floppy_35_288_supported(&self) -> bool {
self.raw & 0x02000000 == 0x02000000
}
pub fn print_screen_service_supported(&self) -> bool {
self.raw & 0x04000000 == 0x04000000
}
pub fn keyboard_8042services_supported(&self) -> bool {
self.raw & 0x08000000 == 0x08000000
}
pub fn serial_services_supported(&self) -> bool {
self.raw & 0x10000000 == 0x10000000
}
pub fn printer_services_supported(&self) -> bool {
self.raw & 0x20000000 == 0x20000000
}
pub fn cga_mono_video_services_supported(&self) -> bool {
self.raw & 0x40000000 == 0x40000000
}
pub fn nec_pc_98supported(&self) -> bool {
self.raw & 0x80000000 == 0x80000000
}
}
impl fmt::Debug for BiosCharacteristics {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_struct(std::any::type_name::<BiosCharacteristics>())
.field("raw", &self.raw)
.field("unknown", &self.unknown())
.field(
"bios_characteristics_not_supported",
&self.bios_characteristics_not_supported(),
)
.field("isa_supported", &self.isa_supported())
.field("mca_supported", &self.mca_supported())
.field("eisa_supported", &self.eisa_supported())
.field("pci_supported", &self.pci_supported())
.field("pcmcia_supported", &self.pcmcia_supported())
.field("plug_and_play_supported", &self.plug_and_play_supported())
.field("apm_supported", &self.apm_supported())
.field("bios_upgradeable", &self.bios_upgradeable())
.field("bios_shadowing_allowed", &self.bios_shadowing_allowed())
.field("vlvesa_supported", &self.vlvesa_supported())
.field("escd_support_available", &self.escd_support_available())
.field("boot_from_cdsupported", &self.boot_from_cdsupported())
.field(
"selectable_boot_supported",
&self.selectable_boot_supported(),
)
.field("bios_rom_socketed", &self.bios_rom_socketed())
.field(
"boot_from_pcmcia_supported",
&self.boot_from_pcmcia_supported(),
)
.field(
"edd_specification_supported",
&self.edd_specification_supported(),
)
.field(
"floppy_nec_japanese_supported",
&self.floppy_nec_japanese_supported(),
)
.field(
"floppy_toshiba_japanese_supported",
&self.floppy_toshiba_japanese_supported(),
)
.field("floppy_525_360_supported", &self.floppy_525_360_supported())
.field("floppy_525_12_supported", &self.floppy_525_12_supported())
.field("floppy_35_720_supported", &self.floppy_35_720_supported())
.field("floppy_35_288_supported", &self.floppy_35_288_supported())
.field(
"print_screen_service_supported",
&self.print_screen_service_supported(),
)
.field(
"keyboard_8042services_supported",
&self.keyboard_8042services_supported(),
)
.field(
"serial_services_supported",
&self.serial_services_supported(),
)
.field(
"printer_services_supported",
&self.printer_services_supported(),
)
.field(
"cga_mono_video_services_supported",
&self.cga_mono_video_services_supported(),
)
.field("nec_pc_98supported", &self.nec_pc_98supported())
.finish()
}
}
impl Serialize for BiosCharacteristics {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut state = serializer.serialize_struct("BiosCharacteristics", 31)?;
state.serialize_field("raw", &self.raw)?;
state.serialize_field("unknown", &self.unknown())?;
state.serialize_field(
"bios_characteristics_not_supported",
&self.bios_characteristics_not_supported(),
)?;
state.serialize_field("isa_supported", &self.isa_supported())?;
state.serialize_field("mca_supported", &self.mca_supported())?;
state.serialize_field("eisa_supported", &self.eisa_supported())?;
state.serialize_field("pci_supported", &self.pci_supported())?;
state.serialize_field("pcmcia_supported", &self.pcmcia_supported())?;
state.serialize_field("plug_and_play_supported", &self.plug_and_play_supported())?;
state.serialize_field("apm_supported", &self.apm_supported())?;
state.serialize_field("bios_upgradeable", &self.bios_upgradeable())?;
state.serialize_field("bios_shadowing_allowed", &self.bios_shadowing_allowed())?;
state.serialize_field("vlvesa_supported", &self.vlvesa_supported())?;
state.serialize_field("escd_support_available", &self.escd_support_available())?;
state.serialize_field("boot_from_cdsupported", &self.boot_from_cdsupported())?;
state.serialize_field(
"selectable_boot_supported",
&self.selectable_boot_supported(),
)?;
state.serialize_field("bios_rom_socketed", &self.bios_rom_socketed())?;
state.serialize_field(
"boot_from_pcmcia_supported",
&self.boot_from_pcmcia_supported(),
)?;
state.serialize_field(
"edd_specification_supported",
&self.edd_specification_supported(),
)?;
state.serialize_field(
"floppy_nec_japanese_supported",
&self.floppy_nec_japanese_supported(),
)?;
state.serialize_field(
"floppy_toshiba_japanese_supported",
&self.floppy_toshiba_japanese_supported(),
)?;
state.serialize_field("floppy_525_360_supported", &self.floppy_525_360_supported())?;
state.serialize_field("floppy_525_12_supported", &self.floppy_525_12_supported())?;
state.serialize_field("floppy_35_720_supported", &self.floppy_35_720_supported())?;
state.serialize_field("floppy_35_288_supported", &self.floppy_35_288_supported())?;
state.serialize_field(
"print_screen_service_supported",
&self.print_screen_service_supported(),
)?;
state.serialize_field(
"keyboard_8042services_supported",
&self.keyboard_8042services_supported(),
)?;
state.serialize_field(
"serial_services_supported",
&self.serial_services_supported(),
)?;
state.serialize_field(
"printer_services_supported",
&self.printer_services_supported(),
)?;
state.serialize_field(
"cga_mono_video_services_supported",
&self.cga_mono_video_services_supported(),
)?;
state.serialize_field("nec_pc_98supported", &self.nec_pc_98supported())?;
state.end()
}
}
#[derive(PartialEq, Eq)]
pub struct BiosCharacteristicsExtension0 {
pub raw: u8,
}
impl Deref for BiosCharacteristicsExtension0 {
type Target = u8;
fn deref(&self) -> &Self::Target {
&self.raw
}
}
impl From<u8> for BiosCharacteristicsExtension0 {
fn from(raw: u8) -> Self {
BiosCharacteristicsExtension0 { raw }
}
}
impl BiosCharacteristicsExtension0 {
pub fn acpi_is_supported(&self) -> bool {
self.raw & 0x01 == 0x01
}
pub fn usb_legacy_is_supported(&self) -> bool {
self.raw & 0x02 == 0x02
}
pub fn agp_is_supported(&self) -> bool {
self.raw & 0x04 == 0x04
}
pub fn i2oboot_is_supported(&self) -> bool {
self.raw & 0x08 == 0x08
}
pub fn ls120super_disk_boot_is_supported(&self) -> bool {
self.raw & 0x10 == 0x10
}
pub fn atapi_zip_drive_boot_is_supported(&self) -> bool {
self.raw & 0x20 == 0x20
}
pub fn boot_1394is_supported(&self) -> bool {
self.raw & 0x40 == 0x40
}
pub fn smart_battery_is_supported(&self) -> bool {
self.raw & 0x80 == 0x80
}
}
impl fmt::Debug for BiosCharacteristicsExtension0 {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_struct(std::any::type_name::<BiosCharacteristicsExtension0>())
.field("raw", &self.raw)
.field("acpi_is_supported", &self.acpi_is_supported())
.field("usb_legacy_is_supported", &self.usb_legacy_is_supported())
.field("agp_is_supported", &self.agp_is_supported())
.field("i2oboot_is_supported", &self.i2oboot_is_supported())
.field(
"ls120super_disk_boot_is_supported",
&self.ls120super_disk_boot_is_supported(),
)
.field(
"atapi_zip_drive_boot_is_supported",
&self.atapi_zip_drive_boot_is_supported(),
)
.field("boot_1394is_supported", &self.boot_1394is_supported())
.field(
"smart_battery_is_supported",
&self.smart_battery_is_supported(),
)
.finish()
}
}
impl Serialize for BiosCharacteristicsExtension0 {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut state = serializer.serialize_struct("BiosCharacteristicsExtension0", 9)?;
state.serialize_field("raw", &self.raw)?;
state.serialize_field("acpi_is_supported", &self.acpi_is_supported())?;
state.serialize_field("usb_legacy_is_supported", &self.usb_legacy_is_supported())?;
state.serialize_field("agp_is_supported", &self.agp_is_supported())?;
state.serialize_field("i2oboot_is_supported", &self.i2oboot_is_supported())?;
state.serialize_field(
"ls120super_disk_boot_is_supported",
&self.ls120super_disk_boot_is_supported(),
)?;
state.serialize_field(
"atapi_zip_drive_boot_is_supported",
&self.atapi_zip_drive_boot_is_supported(),
)?;
state.serialize_field("boot_1394is_supported", &self.boot_1394is_supported())?;
state.serialize_field(
"smart_battery_is_supported",
&self.smart_battery_is_supported(),
)?;
state.end()
}
}
#[derive(PartialEq, Eq)]
pub struct BiosCharacteristicsExtension1 {
pub raw: u8,
}
impl Deref for BiosCharacteristicsExtension1 {
type Target = u8;
fn deref(&self) -> &Self::Target {
&self.raw
}
}
impl From<u8> for BiosCharacteristicsExtension1 {
fn from(raw: u8) -> Self {
BiosCharacteristicsExtension1 { raw }
}
}
impl BiosCharacteristicsExtension1 {
pub fn bios_boot_specification_is_supported(&self) -> bool {
self.raw & 0x01 == 0x01
}
pub fn fkey_initiated_network_boot_is_supported(&self) -> bool {
self.raw & 0x02 == 0x02
}
pub fn targeted_content_distribution_is_supported(&self) -> bool {
self.raw & 0x04 == 0x04
}
pub fn uefi_specification_is_supported(&self) -> bool {
self.raw & 0x08 == 0x08
}
pub fn smbios_table_describes_avirtual_machine(&self) -> bool {
self.raw & 0x10 == 0x10
}
}
impl fmt::Debug for BiosCharacteristicsExtension1 {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_struct(std::any::type_name::<BiosCharacteristicsExtension1>())
.field("raw", &self.raw)
.field(
"bios_boot_specification_is_supported",
&self.bios_boot_specification_is_supported(),
)
.field(
"fkey_initiated_network_boot_is_supported",
&self.fkey_initiated_network_boot_is_supported(),
)
.field(
"targeted_content_distribution_is_supported",
&self.targeted_content_distribution_is_supported(),
)
.field(
"uefi_specification_is_supported",
&self.uefi_specification_is_supported(),
)
.field(
"smbios_table_describes_avirtual_machine",
&self.smbios_table_describes_avirtual_machine(),
)
.finish()
}
}
impl Serialize for BiosCharacteristicsExtension1 {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut state = serializer.serialize_struct("BiosCharacteristicsExtension1", 6)?;
state.serialize_field("raw", &self.raw)?;
state.serialize_field(
"bios_boot_specification_is_supported",
&self.bios_boot_specification_is_supported(),
)?;
state.serialize_field(
"fkey_initiated_network_boot_is_supported",
&self.fkey_initiated_network_boot_is_supported(),
)?;
state.serialize_field(
"targeted_content_distribution_is_supported",
&self.targeted_content_distribution_is_supported(),
)?;
state.serialize_field(
"uefi_specification_is_supported",
&self.uefi_specification_is_supported(),
)?;
state.serialize_field(
"smbios_table_describes_avirtual_machine",
&self.smbios_table_describes_avirtual_machine(),
)?;
state.end()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn unit_test() {
let struct_type0 = vec![
0x00, 0x18, 0x00, 0x00, 0x01, 0x02, 0x00, 0xF0, 0x03, 0xFF, 0x80, 0x98, 0x8B, 0x3F,
0x01, 0x00, 0x11, 0x00, 0x03, 0x0D, 0x00, 0x21, 0x11, 0x2D, 0x4C, 0x45, 0x4E, 0x4F,
0x56, 0x4F, 0x00, 0x53, 0x30, 0x33, 0x4B, 0x54, 0x33, 0x33, 0x41, 0x00, 0x30, 0x38,
0x2F, 0x30, 0x36, 0x2F, 0x32, 0x30, 0x31, 0x39, 0x00, 0x00,
];
let parts = UndefinedStruct::new(&struct_type0);
let test_struct = SMBiosInformation::new(&parts);
assert_eq!(test_struct.vendor(), Some("LENOVO".to_string()));
assert_eq!(test_struct.version(), Some("S03KT33A".to_string()));
assert_eq!(test_struct.starting_address_segment(), Some(61440));
assert_eq!(test_struct.release_date(), Some("08/06/2019".to_string()));
assert_eq!(test_struct.rom_size(), Some(255));
assert_eq!(
test_struct.characteristics(),
Some(BiosCharacteristics::from(1066113152))
);
assert_eq!(test_struct.bios_vendor_reserved_characteristics(), Some(1));
assert_eq!(
test_struct.system_vendor_reserved_characteristics(),
Some(17)
);
assert_eq!(
test_struct.characteristics_extension0(),
Some(BiosCharacteristicsExtension0::from(3))
);
assert_eq!(
test_struct.characteristics_extension1(),
Some(BiosCharacteristicsExtension1::from(13))
);
assert_eq!(test_struct.system_bios_major_release(), Some(0));
assert_eq!(test_struct.system_bios_minor_release(), Some(33));
assert_eq!(test_struct.e_c_firmware_major_release(), Some(17));
assert_eq!(test_struct.e_c_firmware_minor_release(), Some(45));
assert!(test_struct.extended_rom_size().is_none());
let struct_type0 = vec![
0x00, 0x1A, 0x00, 0x00, 0x01, 0x02, 0x00, 0xF0, 0x03, 0xFF, 0x80, 0x98, 0x8B, 0x3F,
0x01, 0x00, 0x11, 0x00, 0x03, 0x0D, 0x00, 0x21, 0x11, 0x2D, 0x30, 0x40, 0x4C, 0x45,
0x4E, 0x4F, 0x56, 0x4F, 0x00, 0x53, 0x30, 0x33, 0x4B, 0x54, 0x33, 0x33, 0x41, 0x00,
0x30, 0x38, 0x2F, 0x30, 0x36, 0x2F, 0x32, 0x30, 0x31, 0x39, 0x00, 0x00,
];
let parts = UndefinedStruct::new(&struct_type0);
let test_struct = SMBiosInformation::new(&parts);
let extended_rom_size = test_struct.extended_rom_size().unwrap();
assert_eq!(extended_rom_size, ExtendedRomSize::from(0x4030));
match extended_rom_size {
ExtendedRomSize::Gigabytes(size) => assert_eq!(size, 48),
_ => panic!("incorrect unit"),
}
println!("{:?}", test_struct);
}
}