Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 28 additions & 13 deletions arch/src/x86_64/smbios.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,19 +36,23 @@ pub enum 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)")]
#[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";
Expand All @@ -58,7 +62,7 @@ pub const DEFAULT_SYSTEM_PRODUCT_NAME: &str = "cloud-hypervisor";
pub struct SmbiosConfig {
pub system: Option<SmbiosSystem>,
pub chassis: Option<SmbiosChassisConfig>,
pub oem_strings: Vec<String>,
pub oem_strings: Box<[String]>,
}

#[derive(Clone, Debug, Default, PartialEq, Eq)]
Expand All @@ -74,13 +78,15 @@ pub struct SmbiosSystem {

#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct SmbiosChassisConfig {
pub manufacturer: Option<String>,
pub chassis_type: Option<u8>,
pub version: Option<String>,
pub serial_number: Option<String>,
pub asset_tag: Option<String>,
}

impl SmbiosConfig {
pub fn is_empty(&self) -> bool {
*self == Self::default()
}
}

fn compute_checksum<T: Copy>(v: &T) -> u8 {
let v: *const T = v;
// SAFETY: we are only reading the bytes within the size of the `T` reference `v`.
Expand Down Expand Up @@ -247,14 +253,20 @@ 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 {
// wrapped around, next starts always initially at 1
return Err(Error::TooManyStrings);
}

Expand Down Expand Up @@ -305,9 +317,9 @@ fn write_type1_system(
version: version_idx,
serial_number: serial_idx,
uuid: uuid_number.to_bytes_le(),
wake_up_type: SYSTEM_WAKE_UP_TYPE_UNKNOWN,
sku: sku_idx,
family: family_idx,
..Default::default()
};

*curptr = write_and_incr(mem, sys, *curptr)?;
Expand Down Expand Up @@ -338,10 +350,14 @@ fn write_type3_chassis(
length: mem::size_of::<SmbiosChassis>() as u8,
handle: *handle,
manufacturer: 0,
chassis_type: 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()
Expand All @@ -356,7 +372,7 @@ fn write_type3_chassis(
pub fn setup_smbios(mem: &GuestMemoryMmap, smbios: Option<&SmbiosConfig>) -> Result<u64> {
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(&[] as &[String], |cfg| cfg.oem_strings.as_slice());
let oem_strings: &[String] = smbios.map_or(&[], |cfg| &cfg.oem_strings);
let physptr = GuestAddress(SMBIOS_START)
.checked_add(mem::size_of::<Smbios30Entrypoint>() as u64)
.ok_or(Error::NotEnoughMemory)?;
Expand Down Expand Up @@ -552,9 +568,8 @@ mod unit_tests {
let smbios = SmbiosConfig {
chassis: Some(SmbiosChassisConfig {
asset_tag: Some("rack1".to_string()),
..Default::default()
}),
oem_strings: vec!["o1".to_string(), "o2".to_string()],
oem_strings: ["o1".to_string(), "o2".to_string()].into(),
..Default::default()
};

Expand Down
48 changes: 48 additions & 0 deletions cloud-hypervisor/tests/common/tests_wrappers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2325,6 +2325,54 @@ pub(crate) fn _test_dmi_oem_strings(guest: &Guest) {
handle_child_output(r, &output);
}

#[cfg(target_arch = "x86_64")]
pub(crate) fn _test_dmi_system_and_chassis(guest: &Guest) {
let fields = [
("system_manufacturer", "system-manufacturer", "Manufacturer"),
("system_product_name", "system-product-name", "ProductName"),
("system_version", "system-version", "Version"),
("system_family", "system-family", "Family"),
("system_sku_number", "system-sku-number", "SkuNumber"),
("chassis_asset_tag", "chassis-asset-tag", "AssetTag"),
];

let platform = fields
.iter()
.map(|(key, _, value)| format!("{key}={value}"))
.collect::<Vec<_>>()
.join(",");

let mut child = GuestCommand::new(guest)
.default_cpus()
.default_memory()
.default_kernel_cmdline_with_platform(Some(&platform))
.default_disks()
.default_net()
.capture_output()
.spawn()
.unwrap();

let r = std::panic::catch_unwind(|| {
guest.wait_vm_boot().unwrap();

for (_, dmidecode_field, expected) in fields {
assert_eq!(
guest
.ssh_command(&format!("sudo dmidecode -s {dmidecode_field}"))
.unwrap()
.trim(),
expected,
"DMI field {dmidecode_field} mismatch"
);
}
});

kill_child(&mut child);
let output = child.wait_with_output().unwrap();

handle_child_output(r, &output);
}

pub(crate) fn _test_serial_off(guest: &Guest) {
let mut child = GuestCommand::new(guest)
.default_cpus()
Expand Down
7 changes: 7 additions & 0 deletions cloud-hypervisor/tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1866,6 +1866,13 @@ mod common_parallel {
_test_dmi_oem_strings(&guest);
}

#[test]
#[cfg(target_arch = "x86_64")]
fn test_dmi_system_and_chassis() {
let guest = basic_regular_guest!(JAMMY_IMAGE_NAME);
_test_dmi_system_and_chassis(&guest);
}

#[test]
fn test_virtio_fs() {
_test_virtio_fs(&prepare_virtiofsd, false, false, None);
Expand Down
6 changes: 6 additions & 0 deletions cloud-hypervisor/tests/integration_cvm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,12 @@ mod common_cvm {
_test_dmi_oem_strings(&guest);
}

#[test]
fn test_dmi_system_and_chassis() {
let guest = basic_cvm_guest!(JAMMY_IMAGE_NAME);
_test_dmi_system_and_chassis(&guest);
}

#[test]
fn test_multiple_network_interfaces() {
let guest = basic_cvm_guest!(JAMMY_IMAGE_NAME);
Expand Down
14 changes: 8 additions & 6 deletions virtio-devices/src/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ struct BlockEpollHandler {
disk_image: Box<dyn AsyncIo>,
disk_nsectors: Arc<AtomicU64>,
interrupt_cb: Arc<dyn VirtioInterrupt>,
serial: Vec<u8>,
serial: Box<[u8]>,
kill_evt: EventFd,
pause_evt: EventFd,
writeback: Arc<AtomicBool>,
Expand All @@ -164,7 +164,7 @@ struct BlockEpollHandler {
inflight_requests: VecDeque<(u16, Request)>,
rate_limiter: Option<RateLimiterGroupHandle>,
access_platform: Option<Arc<dyn AccessPlatform>>,
host_cpus: Option<Vec<usize>>,
host_cpus: Option<Box<[usize]>>,
acked_features: u64,
disable_sector0_writes: bool,
}
Expand Down Expand Up @@ -723,8 +723,8 @@ pub struct Block {
seccomp_action: SeccompAction,
rate_limiter: Option<Arc<RateLimiterGroup>>,
exit_evt: EventFd,
serial: Vec<u8>,
queue_affinity: BTreeMap<u16, Vec<usize>>,
serial: Box<[u8]>,
queue_affinity: BTreeMap<u16, Box<[usize]>>,
disable_sector0_writes: bool,
lock_granularity_choice: LockGranularityChoice,
device_status: Arc<AtomicU8>,
Expand Down Expand Up @@ -755,7 +755,7 @@ impl Block {
rate_limiter: Option<Arc<RateLimiterGroup>>,
exit_evt: EventFd,
state: Option<BlockState>,
queue_affinity: BTreeMap<u16, Vec<usize>>,
queue_affinity: BTreeMap<u16, Box<[usize]>>,
sparse: bool,
disable_sector0_writes: bool,
lock_granularity: LockGranularityChoice,
Expand Down Expand Up @@ -862,7 +862,9 @@ impl Block {
(disk_nsectors, avail_features, 0, config, false)
};

let serial = serial.map_or_else(|| build_serial(&disk_path), Vec::from);
let serial = serial
.map_or_else(|| build_serial(&disk_path), Vec::from)
.into_boxed_slice();

Ok(Block {
common: VirtioCommon {
Expand Down
Loading
Loading