mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
vmm: platform: add structured SMBIOS config
Extend SMBIOS System Information with manufacturer, product, version, family, sku, serial, and uuid fields, add a chassis asset tag, and pass a structured SMBIOS config from --platform into arch setup. Keep OEM strings and legacy serial_number/uuid options working for compatibility. The platform option naming follows `dmidecode -s <field>`. Fields: - system_manufacturer - system_product_name - system_version - system_family - system_serial_number - system_uuid - chassis_asset_tag On-behalf-of: SAP leander.kohler@sap.com Signed-off-by: Leander Kohler <leander.kohler@cyberus-technology.de>
This commit is contained in:
committed by
Rob Bradford
parent
e097d7d495
commit
063caca4a8
@@ -31,7 +31,7 @@ use linux_loader::loader::elf::start_info::{
|
||||
hvm_memmap_table_entry, hvm_modlist_entry, hvm_start_info,
|
||||
};
|
||||
use log::{debug, error, info};
|
||||
pub use smbios::SmbiosConfig;
|
||||
pub use smbios::{SmbiosChassisConfig, SmbiosConfig, SmbiosSystem};
|
||||
use thiserror::Error;
|
||||
use vm_memory::{
|
||||
Address, Bytes, GuestAddress, GuestAddressSpace, GuestMemory, GuestMemoryAtomic,
|
||||
|
||||
@@ -35,16 +35,24 @@ pub enum Error {
|
||||
/// Failure to parse uuid, uuid format may be error
|
||||
#[error("Failure to parse uuid: {1}")]
|
||||
ParseUuid(#[source] uuid::Error, String),
|
||||
/// SMBIOS string index overflow (u8 limit reached).
|
||||
#[error("SMBIOS string index overflow (u8 limit reached: {})", u8::MAX)]
|
||||
TooManyStrings,
|
||||
}
|
||||
|
||||
pub type Result<T> = result::Result<T, Error>;
|
||||
|
||||
// Constants sourced from SMBIOS Spec 3.2.0.
|
||||
// Constants sourced from SMBIOS Spec 3.9.0.
|
||||
const SM3_MAGIC_IDENT: &[u8; 5usize] = b"_SM3_";
|
||||
const BIOS_INFORMATION: u8 = 0;
|
||||
const SYSTEM_INFORMATION: u8 = 1;
|
||||
const OEM_STRINGS: u8 = 11;
|
||||
const SYSTEM_ENCLOSURE: u8 = 3;
|
||||
const END_OF_TABLE: u8 = 127;
|
||||
const SYSTEM_WAKE_UP_TYPE_UNKNOWN: u8 = 0x02;
|
||||
const CHASSIS_TYPE_UNKNOWN: u8 = 0x02;
|
||||
const CHASSIS_STATE_UNKNOWN: u8 = 0x02;
|
||||
const CHASSIS_SECURITY_STATUS_NONE: u8 = 0x03;
|
||||
const PCI_SUPPORTED: u64 = 1 << 7;
|
||||
const IS_VIRTUAL_MACHINE: u8 = 1 << 4;
|
||||
pub const DEFAULT_SYSTEM_MANUFACTURER: &str = "Cloud Hypervisor";
|
||||
@@ -52,9 +60,25 @@ pub const DEFAULT_SYSTEM_PRODUCT_NAME: &str = "cloud-hypervisor";
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
||||
pub struct SmbiosConfig {
|
||||
pub system: Option<SmbiosSystem>,
|
||||
pub chassis: Option<SmbiosChassisConfig>,
|
||||
pub oem_strings: Box<[String]>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
||||
pub struct SmbiosSystem {
|
||||
pub manufacturer: Option<String>,
|
||||
pub product_name: Option<String>,
|
||||
pub version: Option<String>,
|
||||
pub serial_number: Option<String>,
|
||||
pub uuid: Option<String>,
|
||||
pub oem_strings: Box<[String]>,
|
||||
pub sku_number: Option<String>,
|
||||
pub family: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
||||
pub struct SmbiosChassisConfig {
|
||||
pub asset_tag: Option<String>,
|
||||
}
|
||||
|
||||
impl SmbiosConfig {
|
||||
@@ -130,6 +154,33 @@ struct SmbiosOemStrings {
|
||||
count: u8,
|
||||
}
|
||||
|
||||
/// SMBIOS Chassis Table (Type 3) as defined in DMTF SMBIOS 3.9.0:
|
||||
/// https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.9.0.pdf
|
||||
/// Note: trailing fields are omitted, so this structure is not complete.
|
||||
#[repr(C, packed)]
|
||||
#[derive(Default, Copy, Clone)]
|
||||
struct SmbiosChassis {
|
||||
r#type: u8,
|
||||
length: u8,
|
||||
handle: u16,
|
||||
manufacturer: u8,
|
||||
chassis_type: u8,
|
||||
version: u8,
|
||||
serial_number: u8,
|
||||
asset_tag: u8,
|
||||
bootup_state: u8,
|
||||
power_supply_state: u8,
|
||||
thermal_state: u8,
|
||||
security_status: u8,
|
||||
oem_defined: u32,
|
||||
height: u8,
|
||||
number_of_power_cords: u8,
|
||||
contained_element_count: u8,
|
||||
contained_element_record_length: u8,
|
||||
// followed by contained element records (optional, variable-length)
|
||||
// followed by sku_number: u8, rack_type: u8, rack_height: u8
|
||||
}
|
||||
|
||||
#[repr(C, packed)]
|
||||
#[derive(Default, Copy, Clone)]
|
||||
struct SmbiosEndOfTable {
|
||||
@@ -147,6 +198,8 @@ unsafe impl ByteValued for SmbiosSysInfo {}
|
||||
// SAFETY: data structure only contain a series of integers
|
||||
unsafe impl ByteValued for SmbiosOemStrings {}
|
||||
// SAFETY: data structure only contain a series of integers
|
||||
unsafe impl ByteValued for SmbiosChassis {}
|
||||
// SAFETY: data structure only contain a series of integers
|
||||
unsafe impl ByteValued for SmbiosEndOfTable {}
|
||||
|
||||
fn write_and_incr<T: ByteValued>(
|
||||
@@ -200,44 +253,125 @@ fn write_string_terminator(
|
||||
}
|
||||
}
|
||||
|
||||
/// Allocate the next string index for an SMBIOS string-set.
|
||||
///
|
||||
/// Per SMBIOS DSP0134, index `0` means "no string", so valid indices run from
|
||||
/// `1` to `255`. Returns `0` when `present` is `false`. Otherwise returns the
|
||||
/// current value of `*next` and advances it by one. Fails with
|
||||
/// [`Error::TooManyStrings`] once all 255 indices have been used: `next`
|
||||
/// starts at `1`, so it can only be `0` here after wrapping past `255`.
|
||||
fn alloc_index(next: &mut u8, present: bool) -> Result<u8> {
|
||||
if !present {
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
let idx = *next;
|
||||
if idx == 0 {
|
||||
return Err(Error::TooManyStrings);
|
||||
}
|
||||
|
||||
*next = next.wrapping_add(1);
|
||||
Ok(idx)
|
||||
}
|
||||
|
||||
fn write_type1_system(
|
||||
mem: &GuestMemoryMmap,
|
||||
curptr: &mut GuestAddress,
|
||||
handle: &mut u16,
|
||||
serial_number: Option<&str>,
|
||||
uuid: Option<&str>,
|
||||
system: Option<&SmbiosSystem>,
|
||||
) -> Result<()> {
|
||||
*handle += 1;
|
||||
|
||||
let manufacturer = system
|
||||
.and_then(|s| s.manufacturer.as_deref())
|
||||
.unwrap_or(DEFAULT_SYSTEM_MANUFACTURER);
|
||||
let product = system
|
||||
.and_then(|s| s.product_name.as_deref())
|
||||
.unwrap_or(DEFAULT_SYSTEM_PRODUCT_NAME);
|
||||
let version = system.and_then(|s| s.version.as_deref());
|
||||
let serial = system.and_then(|s| s.serial_number.as_deref());
|
||||
let uuid = system.and_then(|s| s.uuid.as_deref());
|
||||
let sku = system.and_then(|s| s.sku_number.as_deref());
|
||||
let family = system.and_then(|s| s.family.as_deref());
|
||||
|
||||
let uuid_number = uuid
|
||||
.map(Uuid::parse_str)
|
||||
.transpose()
|
||||
.map_err(|e| Error::ParseUuid(e, uuid.unwrap().to_string()))?
|
||||
.unwrap_or(Uuid::nil());
|
||||
let serial_idx = serial_number.map(|_| 3).unwrap_or_default();
|
||||
|
||||
let smbios_sysinfo = SmbiosSysInfo {
|
||||
let mut next = 1u8;
|
||||
let manufacturer_idx = alloc_index(&mut next, true)?;
|
||||
let product_idx = alloc_index(&mut next, true)?;
|
||||
let version_idx = alloc_index(&mut next, version.is_some())?;
|
||||
let serial_idx = alloc_index(&mut next, serial.is_some())?;
|
||||
let sku_idx = alloc_index(&mut next, sku.is_some())?;
|
||||
let family_idx = alloc_index(&mut next, family.is_some())?;
|
||||
|
||||
let sys = SmbiosSysInfo {
|
||||
r#type: SYSTEM_INFORMATION,
|
||||
length: mem::size_of::<SmbiosSysInfo>() as u8,
|
||||
handle: *handle,
|
||||
manufacturer: 1, // First string written in this section
|
||||
product_name: 2, // Second string written in this section
|
||||
manufacturer: manufacturer_idx,
|
||||
product_name: product_idx,
|
||||
version: version_idx,
|
||||
serial_number: serial_idx,
|
||||
uuid: uuid_number.to_bytes_le(),
|
||||
..Default::default()
|
||||
wake_up_type: SYSTEM_WAKE_UP_TYPE_UNKNOWN,
|
||||
sku: sku_idx,
|
||||
family: family_idx,
|
||||
};
|
||||
|
||||
*curptr = write_and_incr(mem, smbios_sysinfo, *curptr)?;
|
||||
*curptr = write_string(mem, DEFAULT_SYSTEM_MANUFACTURER, *curptr)?;
|
||||
*curptr = write_string(mem, DEFAULT_SYSTEM_PRODUCT_NAME, *curptr)?;
|
||||
*curptr = write_opt_string(mem, serial_number, *curptr)?;
|
||||
*curptr = write_and_incr(mem, sys, *curptr)?;
|
||||
*curptr = write_string(mem, manufacturer, *curptr)?;
|
||||
*curptr = write_string(mem, product, *curptr)?;
|
||||
*curptr = write_opt_string(mem, version, *curptr)?;
|
||||
*curptr = write_opt_string(mem, serial, *curptr)?;
|
||||
*curptr = write_opt_string(mem, sku, *curptr)?;
|
||||
*curptr = write_opt_string(mem, family, *curptr)?;
|
||||
*curptr = write_and_incr(mem, 0u8, *curptr)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_type3_chassis(
|
||||
mem: &GuestMemoryMmap,
|
||||
curptr: &mut GuestAddress,
|
||||
handle: &mut u16,
|
||||
chassis: &SmbiosChassisConfig,
|
||||
) -> Result<()> {
|
||||
*handle += 1;
|
||||
|
||||
let asset_tag = chassis.asset_tag.as_deref();
|
||||
let mut next = 1u8;
|
||||
let asset_idx = alloc_index(&mut next, asset_tag.is_some())?;
|
||||
|
||||
let ch = SmbiosChassis {
|
||||
r#type: SYSTEM_ENCLOSURE,
|
||||
length: mem::size_of::<SmbiosChassis>() as u8,
|
||||
handle: *handle,
|
||||
manufacturer: 0,
|
||||
chassis_type: CHASSIS_TYPE_UNKNOWN,
|
||||
version: 0,
|
||||
serial_number: 0,
|
||||
asset_tag: asset_idx,
|
||||
bootup_state: CHASSIS_STATE_UNKNOWN,
|
||||
power_supply_state: CHASSIS_STATE_UNKNOWN,
|
||||
thermal_state: CHASSIS_STATE_UNKNOWN,
|
||||
security_status: CHASSIS_SECURITY_STATUS_NONE,
|
||||
contained_element_count: 0,
|
||||
contained_element_record_length: 0,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
*curptr = write_and_incr(mem, ch, *curptr)?;
|
||||
*curptr = write_opt_string(mem, asset_tag, *curptr)?;
|
||||
*curptr = write_string_terminator(mem, *curptr, asset_tag.is_some())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn setup_smbios(mem: &GuestMemoryMmap, smbios: Option<&SmbiosConfig>) -> Result<u64> {
|
||||
let serial_number = smbios.and_then(|cfg| cfg.serial_number.as_deref());
|
||||
let uuid = smbios.and_then(|cfg| cfg.uuid.as_deref());
|
||||
let system = smbios.and_then(|cfg| cfg.system.as_ref());
|
||||
let chassis = smbios.and_then(|cfg| cfg.chassis.as_ref());
|
||||
let oem_strings: &[String] = smbios.map_or(&[], |cfg| &cfg.oem_strings);
|
||||
let physptr = GuestAddress(SMBIOS_START)
|
||||
.checked_add(mem::size_of::<Smbios30Entrypoint>() as u64)
|
||||
@@ -263,7 +397,11 @@ pub fn setup_smbios(mem: &GuestMemoryMmap, smbios: Option<&SmbiosConfig>) -> Res
|
||||
curptr = write_and_incr(mem, 0u8, curptr)?;
|
||||
}
|
||||
|
||||
write_type1_system(mem, &mut curptr, &mut handle, serial_number, uuid)?;
|
||||
write_type1_system(mem, &mut curptr, &mut handle, system)?;
|
||||
|
||||
if let Some(chassis) = chassis {
|
||||
write_type3_chassis(mem, &mut curptr, &mut handle, chassis)?;
|
||||
}
|
||||
|
||||
if !oem_strings.is_empty() {
|
||||
handle += 1;
|
||||
|
||||
Reference in New Issue
Block a user