extern crate alloc;
use alloc::vec::Vec;
use core::mem::size_of;
use core::ptr::addr_of;
use log::debug;
use zerocopy::{FromBytes, Immutable, IntoBytes};
use crate::fwcfg::FwCfg;
#[derive(Debug, FromBytes, IntoBytes, Immutable)]
#[repr(C, packed)]
struct SmbiosV2 {
pub anchor: [u8; 4],
pub checksum: u8,
pub length: u8,
pub major: u8,
pub minor: u8,
pub maxsize: u16,
pub revision: u8,
pub fa: [u8; 5],
pub ias: [u8; 5],
pub ic: u8,
pub tablelength: u16,
pub tableaddress: u32,
pub count: u16,
pub bcdrevision: u8,
}
#[derive(Debug, FromBytes, IntoBytes, Immutable)]
#[repr(C, packed)]
struct SmbiosV3 {
pub anchor: [u8; 5],
pub checksum: u8,
pub length: u8,
pub major: u8,
pub minor: u8,
pub docrev: u8,
pub revision: u8,
pub _reserved: u8,
pub maxsize: u32,
pub address: u64,
}
#[derive(Debug, FromBytes, IntoBytes, Immutable)]
#[repr(C, packed)]
struct SmbiosType0 {
table_type: u8,
length: u8,
handle: u16,
vendor: u8, bios_version: u8, bios_segment: u16,
bios_date: u8, bios_size: u8,
bios_characteristics: [u32; 2],
bios_characteristics_ext: [u8; 2],
bios_major: u8,
bios_minor: u8,
ec_major: u8,
ec_minor: u8,
}
pub struct SmbiosLoader<'f> {
fwcfg: &'f dyn FwCfg,
anchor_item: u16,
tables_item: u16,
anchor_size: u32,
tables_size: u32,
version: Option<(u8, u8)>,
anchor: Option<*const ()>,
bios_major: u8,
bios_minor: u8,
bios_vendor: Option<&'f str>,
bios_version: Option<&'f str>,
bios_date: Option<&'f str>,
}
impl<'f> SmbiosLoader<'f> {
pub fn new(fwcfg: &'f dyn FwCfg) -> Option<Self> {
let (anchor_item, anchor_size) = fwcfg.findfile("etc/smbios/smbios-anchor")?;
let (tables_item, tables_size) = fwcfg.findfile("etc/smbios/smbios-tables")?;
if anchor_size as usize != size_of::<SmbiosV2>()
&& anchor_size as usize != size_of::<SmbiosV3>()
{
return None;
}
Some(Self {
fwcfg,
anchor_item,
tables_item,
anchor_size,
tables_size,
version: None,
anchor: None,
bios_major: 0,
bios_minor: 0,
bios_vendor: None,
bios_version: None,
bios_date: None,
})
}
pub fn set_bios(
&mut self,
major: u8,
minor: u8,
vendor: &'f str,
version: &'f str,
date: &'f str,
) {
self.bios_major = major;
self.bios_minor = minor;
self.bios_vendor = Some(vendor);
self.bios_version = Some(version);
self.bios_date = Some(date);
}
fn type0(&self) -> Option<Vec<u8>> {
let mut blob = Vec::new();
let zero: [u8; 1] = [0];
let characteristics = 1 << 3; let ext1 = 1 << 0; let ext2 = (1 << 3) | (1 << 4); let type0 = SmbiosType0 {
table_type: 0,
length: size_of::<SmbiosType0>() as u8,
handle: 0,
vendor: 1,
bios_version: 2,
bios_segment: 0,
bios_date: 3,
bios_size: 0,
bios_characteristics: [characteristics, 0],
bios_characteristics_ext: [ext1, ext2],
bios_major: self.bios_major,
bios_minor: self.bios_minor,
ec_major: self.bios_major,
ec_minor: self.bios_minor,
};
blob.extend_from_slice(type0.as_bytes());
blob.extend_from_slice(self.bios_vendor?.as_bytes());
blob.extend_from_slice(&zero);
blob.extend_from_slice(self.bios_version?.as_bytes());
blob.extend_from_slice(&zero);
blob.extend_from_slice(self.bios_date?.as_bytes());
blob.extend_from_slice(&zero);
blob.extend_from_slice(&zero);
Some(blob)
}
pub fn load_tables<A>(&mut self, alloc_func: A) -> Result<(), &str>
where
A: Fn(usize, usize) -> Option<&'static mut [u8]>,
{
let anchor = alloc_func(self.anchor_size as usize, 64).ok_or("allocation failed")?;
self.fwcfg.read_dma(
Some(self.anchor_item),
anchor.as_mut_ptr().cast(),
self.anchor_size as usize,
);
let type0 = self.type0().unwrap_or_default();
let tables =
alloc_func(self.tables_size as usize + type0.len(), 64).ok_or("allocation failed")?;
tables[..type0.len()].copy_from_slice(&type0);
self.fwcfg.read_dma(
Some(self.tables_item),
tables[type0.len()..].as_mut_ptr().cast(),
self.tables_size as usize,
);
if self.anchor_size as usize == size_of::<SmbiosV2>() {
let (mut v2, _) = SmbiosV2::read_from_prefix(anchor).unwrap();
if !type0.is_empty() {
let tl = v2.tablelength;
debug!("smbios2: qemu {}, type0 {}", tl, type0.len());
v2.tablelength += type0.len() as u16;
v2.count += 1;
}
v2.tableaddress = addr_of!(*tables).addr() as u32;
let sum1: u32 = v2.as_bytes()[16..].iter().map(|b| *b as u32).sum();
let fixup1 = (0u32.wrapping_sub(sum1) & 0xff) as u8;
v2.ic = fixup1;
let sum2: u32 = v2.as_bytes().iter().map(|b| *b as u32).sum();
let fixup2 = (0u32.wrapping_sub(sum2) & 0xff) as u8;
v2.checksum = fixup2;
v2.write_to_prefix(anchor).unwrap();
self.version = Some((v2.major, v2.minor));
}
if self.anchor_size as usize == size_of::<SmbiosV3>() {
let (mut v3, _) = SmbiosV3::read_from_prefix(anchor).unwrap();
if !type0.is_empty() {
let tl = v3.maxsize;
debug!("smbios3: qemu {}, type0 {}", tl, type0.len());
v3.maxsize += type0.len() as u32;
}
v3.address = addr_of!(*tables).addr() as u64;
let sum: u32 = v3.as_bytes().iter().map(|b| *b as u32).sum();
let fixup = (0u32.wrapping_sub(sum) & 0xff) as u8;
v3.checksum = fixup;
v3.write_to_prefix(anchor).unwrap();
self.version = Some((v3.major, v3.minor));
}
self.anchor = Some(anchor.as_ptr().cast());
Ok(())
}
pub fn version(&self) -> Option<(u8, u8)> {
self.version
}
pub fn anchor(&self) -> Option<*const ()> {
self.anchor
}
}