diff --git a/arch/src/x86_64/smbios.rs b/arch/src/x86_64/smbios.rs index a59c39fef3..02965f4685 100644 --- a/arch/src/x86_64/smbios.rs +++ b/arch/src/x86_64/smbios.rs @@ -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 = result::Result; -// 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"; @@ -58,7 +62,7 @@ pub const DEFAULT_SYSTEM_PRODUCT_NAME: &str = "cloud-hypervisor"; pub struct SmbiosConfig { pub system: Option, pub chassis: Option, - pub oem_strings: Vec, + pub oem_strings: Box<[String]>, } #[derive(Clone, Debug, Default, PartialEq, Eq)] @@ -74,13 +78,15 @@ pub struct SmbiosSystem { #[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct SmbiosChassisConfig { - pub manufacturer: Option, - pub chassis_type: Option, - pub version: Option, - pub serial_number: Option, pub asset_tag: Option, } +impl SmbiosConfig { + pub fn is_empty(&self) -> bool { + *self == Self::default() + } +} + fn compute_checksum(v: &T) -> u8 { let v: *const T = v; // SAFETY: we are only reading the bytes within the size of the `T` reference `v`. @@ -247,6 +253,13 @@ 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 { if !present { return Ok(0); @@ -254,7 +267,6 @@ fn alloc_index(next: &mut u8, present: bool) -> Result { let idx = *next; if idx == 0 { - // wrapped around, next starts always initially at 1 return Err(Error::TooManyStrings); } @@ -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)?; @@ -338,10 +350,14 @@ fn write_type3_chassis( length: mem::size_of::() 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() @@ -356,7 +372,7 @@ fn write_type3_chassis( pub fn setup_smbios(mem: &GuestMemoryMmap, smbios: Option<&SmbiosConfig>) -> Result { 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::() as u64) .ok_or(Error::NotEnoughMemory)?; @@ -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() }; diff --git a/cloud-hypervisor/tests/common/tests_wrappers.rs b/cloud-hypervisor/tests/common/tests_wrappers.rs index 27e4c1f272..a236db3d5a 100644 --- a/cloud-hypervisor/tests/common/tests_wrappers.rs +++ b/cloud-hypervisor/tests/common/tests_wrappers.rs @@ -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::>() + .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() diff --git a/cloud-hypervisor/tests/integration.rs b/cloud-hypervisor/tests/integration.rs index 7b085a487c..21f34487b4 100644 --- a/cloud-hypervisor/tests/integration.rs +++ b/cloud-hypervisor/tests/integration.rs @@ -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); diff --git a/cloud-hypervisor/tests/integration_cvm.rs b/cloud-hypervisor/tests/integration_cvm.rs index 0f4b17d771..1b6bd0c4bf 100644 --- a/cloud-hypervisor/tests/integration_cvm.rs +++ b/cloud-hypervisor/tests/integration_cvm.rs @@ -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); diff --git a/virtio-devices/src/block.rs b/virtio-devices/src/block.rs index 6946924f31..f40f1a49a9 100644 --- a/virtio-devices/src/block.rs +++ b/virtio-devices/src/block.rs @@ -155,7 +155,7 @@ struct BlockEpollHandler { disk_image: Box, disk_nsectors: Arc, interrupt_cb: Arc, - serial: Vec, + serial: Box<[u8]>, kill_evt: EventFd, pause_evt: EventFd, writeback: Arc, @@ -164,7 +164,7 @@ struct BlockEpollHandler { inflight_requests: VecDeque<(u16, Request)>, rate_limiter: Option, access_platform: Option>, - host_cpus: Option>, + host_cpus: Option>, acked_features: u64, disable_sector0_writes: bool, } @@ -723,8 +723,8 @@ pub struct Block { seccomp_action: SeccompAction, rate_limiter: Option>, exit_evt: EventFd, - serial: Vec, - queue_affinity: BTreeMap>, + serial: Box<[u8]>, + queue_affinity: BTreeMap>, disable_sector0_writes: bool, lock_granularity_choice: LockGranularityChoice, device_status: Arc, @@ -755,7 +755,7 @@ impl Block { rate_limiter: Option>, exit_evt: EventFd, state: Option, - queue_affinity: BTreeMap>, + queue_affinity: BTreeMap>, sparse: bool, disable_sector0_writes: bool, lock_granularity: LockGranularityChoice, @@ -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 { diff --git a/vmm/src/config.rs b/vmm/src/config.rs index d7d1283067..acc3b0000b 100644 --- a/vmm/src/config.rs +++ b/vmm/src/config.rs @@ -724,7 +724,7 @@ impl CpusConfig { v.0.iter() .map(|(e1, e2)| CpuAffinity { vcpu: *e1, - host_cpus: e2.clone(), + host_cpus: e2.clone().into_boxed_slice(), }) .collect() }); @@ -913,13 +913,11 @@ impl PlatformConfig { .add("num_pci_segments") .add("iommu_segments") .add("iommu_address_width") - .add("oem_strings") .add("serial_number") .add("uuid") .add("oem_strings") .add("iommufd") - .add("vfio_p2p_dma") - .add("uuid"); + .add("vfio_p2p_dma"); for field in SMBIOS_STRING_FIELDS { parser.add(field.key); } @@ -944,8 +942,7 @@ impl PlatformConfig { let oem_strings = parser .convert::("oem_strings") .map_err(Error::ParsePlatform)? - .map(|v| v.0) - .unwrap_or_default(); + .map(|v| v.0.into_boxed_slice()); let iommufd = parser .convert::("iommufd") .map_err(Error::ParsePlatform)? @@ -1016,27 +1013,6 @@ impl PlatformConfig { warn!("'uuid' in --platform is deprecated; use 'system_uuid'."); } platform_config.system_uuid = platform_config.system_uuid.or(legacy_uuid); - #[cfg(feature = "tdx")] - let tdx = parser - .convert::("tdx") - .map_err(Error::ParsePlatform)? - .unwrap_or(Toggle(false)) - .0; - #[cfg(feature = "sev_snp")] - let sev_snp = parser - .convert::("sev_snp") - .map_err(Error::ParsePlatform)? - .unwrap_or(Toggle(false)) - .0; - - #[cfg(feature = "tdx")] - { - platform_config.tdx = tdx; - } - #[cfg(feature = "sev_snp")] - { - platform_config.sev_snp = sev_snp; - } Ok(platform_config) } @@ -1503,7 +1479,7 @@ impl DiskConfig { v.0.iter() .map(|(e1, e2)| VirtQueueAffinity { queue_index: *e1, - host_cpus: e2.clone(), + host_cpus: e2.clone().into_boxed_slice(), }) .collect() }); @@ -2601,7 +2577,7 @@ impl NumaConfig { let memory_zones = parser .convert::("memory_zones") .map_err(Error::ParseNuma)? - .map(|v| v.0); + .map(|v| v.0.into_boxed_slice()); let pci_segments = parser .convert::("pci_segments") .map_err(Error::ParseNuma)? @@ -3403,14 +3379,14 @@ impl VmConfig { } pub fn parse(vm_params: VmParams) -> Result { - let mut rate_limit_groups: Option> = None; + let mut rate_limit_groups: Option> = None; if let Some(rate_limit_group_list) = &vm_params.rate_limit_groups { let mut rate_limit_group_config_list = Vec::new(); for item in rate_limit_group_list.iter() { let rate_limit_group_config = RateLimiterGroupConfig::parse(item)?; rate_limit_group_config_list.push(rate_limit_group_config); } - rate_limit_groups = Some(rate_limit_group_config_list); + rate_limit_groups = Some(rate_limit_group_config_list.into_boxed_slice()); } let mut disks: Option> = None; @@ -3522,26 +3498,26 @@ impl VmConfig { vsock = Some(vsock_config); } - let mut pci_segments: Option> = None; + let mut pci_segments: Option> = None; if let Some(pci_segment_list) = &vm_params.pci_segments { let mut pci_segment_config_list = Vec::new(); for item in pci_segment_list.iter() { let pci_segment_config = PciSegmentConfig::parse(item)?; pci_segment_config_list.push(pci_segment_config); } - pci_segments = Some(pci_segment_config_list); + pci_segments = Some(pci_segment_config_list.into_boxed_slice()); } let platform = vm_params.platform.map(PlatformConfig::parse).transpose()?; - let mut numa: Option> = None; + let mut numa: Option> = None; if let Some(numa_list) = &vm_params.numa { let mut numa_config_list = Vec::new(); for item in numa_list.iter() { let numa_config = NumaConfig::parse(item)?; numa_config_list.push(numa_config); } - numa = Some(numa_config_list); + numa = Some(numa_config_list.into_boxed_slice()); } #[cfg(not(feature = "igvm"))] @@ -3579,13 +3555,14 @@ impl VmConfig { #[cfg(feature = "guest_debug")] let gdb = vm_params.gdb; - let mut landlock_rules: Option> = None; + let mut landlock_rules: Option> = None; if let Some(ll_rules) = vm_params.landlock_rules { landlock_rules = Some( ll_rules .iter() .map(|rule| LandlockConfig::parse(rule)) - .collect::>>()?, + .collect::>>()? + .into_boxed_slice(), ); } @@ -3859,16 +3836,16 @@ mod unit_tests { CpusConfig { boot_vcpus: 2, max_vcpus: 2, - affinity: Some(vec![ + affinity: Some(Box::new([ CpuAffinity { vcpu: 0, - host_cpus: vec![0, 2], + host_cpus: Box::new([0, 2]), }, CpuAffinity { vcpu: 1, - host_cpus: vec![1, 3], + host_cpus: Box::new([1, 3]), } - ]), + ])), ..Default::default() }, ); @@ -4250,24 +4227,24 @@ mod unit_tests { assert_eq!( DiskConfig::parse("path=/path/to_file,queue_affinity=[0@[1],1@[2],2@[3,4],3@[5-8]]")?, DiskConfig { - queue_affinity: Some(vec![ + queue_affinity: Some(Box::new([ VirtQueueAffinity { queue_index: 0, - host_cpus: vec![1], + host_cpus: Box::new([1]), }, VirtQueueAffinity { queue_index: 1, - host_cpus: vec![2], + host_cpus: Box::new([2]), }, VirtQueueAffinity { queue_index: 2, - host_cpus: vec![3, 4], + host_cpus: Box::new([3, 4]), }, VirtQueueAffinity { queue_index: 3, - host_cpus: vec![5, 6, 7, 8], + host_cpus: Box::new([5, 6, 7, 8]), } - ]), + ])), ..disk_fixture() } ); @@ -4785,14 +4762,14 @@ id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}" memory_zones=[mem0],pci_segments=[0]"; let expected_standard = NumaConfig { guest_numa_id: 1, - cpus: Some(vec![2, 3]), - distances: Some(vec![NumaDistance { + cpus: Some(Box::new([2, 3])), + distances: Some(Box::new([NumaDistance { destination: 0, distance: 20, - }]), + }])), device_id: None, - memory_zones: Some(vec!["mem0".to_string()]), - pci_segments: Some(vec![0]), + memory_zones: Some(Box::new(["mem0".to_string()])), + pci_segments: Some(Box::new([0])), }; assert_eq!(NumaConfig::parse(standard_input)?, expected_standard); // Successful generic initiator config parse @@ -4800,13 +4777,13 @@ id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}" let expected_gi = NumaConfig { guest_numa_id: 2, cpus: None, - distances: Some(vec![NumaDistance { + distances: Some(Box::new([NumaDistance { destination: 0, distance: 30, - }]), + }])), device_id: Some("vfio1".to_string()), memory_zones: None, - pci_segments: Some(vec![1]), + pci_segments: Some(Box::new([1])), }; assert_eq!(NumaConfig::parse(gi_input)?, expected_gi); Ok(()) @@ -4818,10 +4795,10 @@ id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}" let config = NumaConfig { guest_numa_id: 0, cpus: None, - distances: Some(vec![NumaDistance { + distances: Some(Box::new([NumaDistance { destination: 1, distance: 20, - }]), + }])), memory_zones: None, device_id: Some("vfio0".to_string()), pci_segments: None, @@ -4849,7 +4826,7 @@ id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}" // device_id and cpus specified let config = NumaConfig { guest_numa_id: 0, - cpus: Some(vec![0, 1]), + cpus: Some(Box::new([0, 1])), distances: None, device_id: Some("vfio0".to_string()), memory_zones: None, @@ -4866,7 +4843,7 @@ id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}" cpus: None, distances: None, device_id: Some("vfio0".to_string()), - memory_zones: Some(vec!["mem0".to_string()]), + memory_zones: Some(Box::new(["mem0".to_string()])), pci_segments: None, }; assert!(config.validate().is_err()); @@ -4877,13 +4854,13 @@ id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}" // No device_id let config = NumaConfig { guest_numa_id: 0, - cpus: Some(vec![0, 1]), - distances: Some(vec![NumaDistance { + cpus: Some(Box::new([0, 1])), + distances: Some(Box::new([NumaDistance { destination: 1, distance: 20, - }]), + }])), device_id: None, - memory_zones: Some(vec!["mem0".to_string()]), + memory_zones: Some(Box::new(["mem0".to_string()])), pci_segments: None, }; config.validate().unwrap(); @@ -5149,7 +5126,7 @@ id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}" iommu_address_width_bits: MAX_IOMMU_ADDRESS_WIDTH_BITS, system_serial_number: None, system_uuid: None, - oem_strings: Vec::new(), + oem_strings: None, iommufd: false, vfio_p2p_dma: default_platformconfig_vfio_p2p_dma(), system_manufacturer: None, @@ -5602,14 +5579,17 @@ id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}" let mut still_valid_config = valid_config.clone(); still_valid_config.platform = Some(PlatformConfig { - iommu_segments: Some(vec![1, 2, 3]), + iommu_segments: Some(Box::new([1, 2, 3])), ..platform_fixture() }); still_valid_config.validate().unwrap(); let mut invalid_config = valid_config.clone(); invalid_config.platform = Some(PlatformConfig { - iommu_segments: Some(vec![MAX_NUM_PCI_SEGMENTS + 1, MAX_NUM_PCI_SEGMENTS + 2]), + iommu_segments: Some(Box::new([ + MAX_NUM_PCI_SEGMENTS + 1, + MAX_NUM_PCI_SEGMENTS + 2, + ])), ..platform_fixture() }); assert_eq!( @@ -5631,7 +5611,7 @@ id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}" let mut still_valid_config = valid_config.clone(); still_valid_config.platform = Some(PlatformConfig { - iommu_segments: Some(vec![1, 2, 3]), + iommu_segments: Some(Box::new([1, 2, 3])), ..platform_fixture() }); still_valid_config.disks = Some(vec![DiskConfig { @@ -5646,7 +5626,7 @@ id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}" let mut still_valid_config = valid_config.clone(); still_valid_config.platform = Some(PlatformConfig { - iommu_segments: Some(vec![1, 2, 3]), + iommu_segments: Some(Box::new([1, 2, 3])), ..platform_fixture() }); still_valid_config.net = Some(vec![NetConfig { @@ -5661,7 +5641,7 @@ id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}" let mut still_valid_config = valid_config.clone(); still_valid_config.platform = Some(PlatformConfig { - iommu_segments: Some(vec![1, 2, 3]), + iommu_segments: Some(Box::new([1, 2, 3])), ..platform_fixture() }); still_valid_config.pmem = Some(vec![PmemConfig { @@ -5676,7 +5656,7 @@ id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}" let mut still_valid_config = valid_config.clone(); still_valid_config.platform = Some(PlatformConfig { - iommu_segments: Some(vec![1, 2, 3]), + iommu_segments: Some(Box::new([1, 2, 3])), ..platform_fixture() }); still_valid_config.devices = Some(vec![DeviceConfig { @@ -5691,7 +5671,7 @@ id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}" let mut still_valid_config = valid_config.clone(); still_valid_config.platform = Some(PlatformConfig { - iommu_segments: Some(vec![1, 2, 3]), + iommu_segments: Some(Box::new([1, 2, 3])), ..platform_fixture() }); still_valid_config.vsock = Some(VsockConfig { @@ -5707,7 +5687,7 @@ id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}" let mut invalid_config = valid_config.clone(); invalid_config.platform = Some(PlatformConfig { - iommu_segments: Some(vec![1, 2, 3]), + iommu_segments: Some(Box::new([1, 2, 3])), ..platform_fixture() }); invalid_config.disks = Some(vec![DiskConfig { @@ -5725,7 +5705,7 @@ id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}" let mut invalid_config = valid_config.clone(); invalid_config.platform = Some(PlatformConfig { - iommu_segments: Some(vec![1, 2, 3]), + iommu_segments: Some(Box::new([1, 2, 3])), ..platform_fixture() }); invalid_config.net = Some(vec![NetConfig { @@ -5744,7 +5724,7 @@ id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}" let mut invalid_config = valid_config.clone(); invalid_config.platform = Some(PlatformConfig { num_pci_segments: MAX_NUM_PCI_SEGMENTS, - iommu_segments: Some(vec![1, 2, 3]), + iommu_segments: Some(Box::new([1, 2, 3])), ..platform_fixture() }); invalid_config.pmem = Some(vec![PmemConfig { @@ -5762,7 +5742,7 @@ id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}" let mut invalid_config = valid_config.clone(); invalid_config.platform = Some(PlatformConfig { num_pci_segments: MAX_NUM_PCI_SEGMENTS, - iommu_segments: Some(vec![1, 2, 3]), + iommu_segments: Some(Box::new([1, 2, 3])), ..platform_fixture() }); invalid_config.devices = Some(vec![DeviceConfig { @@ -5779,7 +5759,7 @@ id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}" let mut invalid_config = valid_config.clone(); invalid_config.platform = Some(PlatformConfig { - iommu_segments: Some(vec![1, 2, 3]), + iommu_segments: Some(Box::new([1, 2, 3])), ..platform_fixture() }); invalid_config.vsock = Some(VsockConfig { @@ -5798,7 +5778,7 @@ id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}" let mut invalid_config = valid_config.clone(); invalid_config.memory.shared = true; invalid_config.platform = Some(PlatformConfig { - iommu_segments: Some(vec![1, 2, 3]), + iommu_segments: Some(Box::new([1, 2, 3])), ..platform_fixture() }); invalid_config.user_devices = Some(vec![UserDeviceConfig { @@ -5815,7 +5795,7 @@ id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}" let mut invalid_config = valid_config.clone(); invalid_config.platform = Some(PlatformConfig { - iommu_segments: Some(vec![1, 2, 3]), + iommu_segments: Some(Box::new([1, 2, 3])), ..platform_fixture() }); invalid_config.vdpa = Some(vec![VdpaConfig { @@ -5833,7 +5813,7 @@ id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}" let mut invalid_config = valid_config.clone(); invalid_config.memory.shared = true; invalid_config.platform = Some(PlatformConfig { - iommu_segments: Some(vec![1, 2, 3]), + iommu_segments: Some(Box::new([1, 2, 3])), ..platform_fixture() }); invalid_config.fs = Some(vec![FsConfig { @@ -5853,81 +5833,81 @@ id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}" num_pci_segments: 2, ..platform_fixture() }); - invalid_config.numa = Some(vec![ + invalid_config.numa = Some(Box::new([ NumaConfig { guest_numa_id: 0, - cpus: Some(vec![0]), - pci_segments: Some(vec![1]), + cpus: Some(Box::new([0])), + pci_segments: Some(Box::new([1])), ..numa_fixture() }, NumaConfig { guest_numa_id: 1, - cpus: Some(vec![1]), - pci_segments: Some(vec![1]), + cpus: Some(Box::new([1])), + pci_segments: Some(Box::new([1])), ..numa_fixture() }, - ]); + ])); assert_eq!( invalid_config.validate(), Err(ValidationError::PciSegmentReused(1, 0, 1)) ); let mut invalid_config = valid_config.clone(); - invalid_config.pci_segments = Some(vec![PciSegmentConfig { + invalid_config.pci_segments = Some(Box::new([PciSegmentConfig { pci_segment: 0, mmio32_aperture_weight: 1, mmio64_aperture_weight: 0, - }]); + }])); assert_eq!( invalid_config.validate(), Err(ValidationError::InvalidPciSegmentApertureWeight(0)) ); let mut invalid_config = valid_config.clone(); - invalid_config.pci_segments = Some(vec![PciSegmentConfig { + invalid_config.pci_segments = Some(Box::new([PciSegmentConfig { pci_segment: 0, mmio32_aperture_weight: 0, mmio64_aperture_weight: 1, - }]); + }])); assert_eq!( invalid_config.validate(), Err(ValidationError::InvalidPciSegmentApertureWeight(0)) ); let mut invalid_config = valid_config.clone(); - invalid_config.numa = Some(vec![ + invalid_config.numa = Some(Box::new([ NumaConfig { guest_numa_id: 0, - cpus: Some(vec![0]), + cpus: Some(Box::new([0])), ..numa_fixture() }, NumaConfig { guest_numa_id: 1, - cpus: Some(vec![1]), - pci_segments: Some(vec![0]), + cpus: Some(Box::new([1])), + pci_segments: Some(Box::new([0])), ..numa_fixture() }, - ]); + ])); assert_eq!( invalid_config.validate(), Err(ValidationError::DefaultPciSegmentInvalidNode(1)) ); let mut invalid_config = valid_config.clone(); - invalid_config.numa = Some(vec![ + invalid_config.numa = Some(Box::new([ NumaConfig { guest_numa_id: 0, - cpus: Some(vec![0]), - pci_segments: Some(vec![0]), + cpus: Some(Box::new([0])), + pci_segments: Some(Box::new([0])), ..numa_fixture() }, NumaConfig { guest_numa_id: 1, - cpus: Some(vec![1]), - pci_segments: Some(vec![1]), + cpus: Some(Box::new([1])), + pci_segments: Some(Box::new([1])), ..numa_fixture() }, - ]); + ])); assert_eq!( invalid_config.validate(), Err(ValidationError::InvalidPciSegment(1)) diff --git a/vmm/src/cpu.rs b/vmm/src/cpu.rs index b23ae0a413..83f000caec 100644 --- a/vmm/src/cpu.rs +++ b/vmm/src/cpu.rs @@ -752,7 +752,7 @@ pub struct CpuManager { #[cfg_attr(target_arch = "aarch64", allow(dead_code))] acpi_address: Option, proximity_domain_per_cpu: BTreeMap, - affinity: BTreeMap>, + affinity: BTreeMap>, dynamic: bool, hypervisor: Arc, #[cfg(feature = "sev_snp")] diff --git a/vmm/src/device_manager.rs b/vmm/src/device_manager.rs index 80598d1d02..d7b3d096de 100644 --- a/vmm/src/device_manager.rs +++ b/vmm/src/device_manager.rs @@ -712,8 +712,8 @@ pub(crate) struct AddressManager { pub(crate) mmio_bus: Arc, pub(crate) vm: Arc, device_tree: Arc>, - pci_mmio32_allocators: Vec>>, - pci_mmio64_allocators: Vec>>, + pci_mmio32_allocators: Box<[Arc>]>, + pci_mmio64_allocators: Box<[Arc>]>, } impl DeviceRelocation for AddressManager { @@ -1047,7 +1047,7 @@ pub struct DeviceManager { // Counter to keep track of the consumed device IDs. device_id_cnt: Wrapping, - pci_segments: Vec, + pci_segments: Box<[PciSegment]>, #[cfg_attr(target_arch = "aarch64", allow(dead_code))] // MSI Interrupt Manager @@ -1159,7 +1159,7 @@ fn create_mmio_allocators( num_pci_segments: u16, weights: &[u32], alignment: u64, -) -> Vec>> { +) -> Box<[Arc>]> { let total_weight: u32 = weights.iter().sum(); // Start each PCI segment mmio range on an aligned boundary @@ -1186,7 +1186,7 @@ fn create_mmio_allocators( i += weight; } - mmio_allocators + mmio_allocators.into_boxed_slice() } fn use_64bit_bar_for_virtio_device( @@ -1401,7 +1401,7 @@ impl DeviceManager { iommu_device: None, iommu_mapping: None, iommu_attached_devices: None, - pci_segments, + pci_segments: pci_segments.into_boxed_slice(), device_tree, exit_evt, reset_evt, @@ -4595,7 +4595,7 @@ impl DeviceManager { .map(|ic| ic.clone() as Arc>) } - pub(crate) fn pci_segments(&self) -> &Vec { + pub(crate) fn pci_segments(&self) -> &[PciSegment] { &self.pci_segments } diff --git a/vmm/src/vm_config.rs b/vmm/src/vm_config.rs index 8ea8f0e8ff..12ade3e9e0 100644 --- a/vmm/src/vm_config.rs +++ b/vmm/src/vm_config.rs @@ -31,7 +31,7 @@ pub(crate) trait ApplyLandlock { #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub struct CpuAffinity { pub vcpu: u32, - pub host_cpus: Vec, + pub host_cpus: Box<[usize]>, } #[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)] @@ -77,7 +77,7 @@ pub struct CpusConfig { #[serde(default = "default_cpuconfig_max_phys_bits")] pub max_phys_bits: u8, #[serde(default)] - pub affinity: Option>, + pub affinity: Option>, #[serde(default)] pub features: CpuFeatures, #[serde(default = "default_cpusconfig_nested")] @@ -126,7 +126,7 @@ pub struct PlatformConfig { #[serde(default = "default_platformconfig_num_pci_segments")] pub num_pci_segments: u16, #[serde(default)] - pub iommu_segments: Option>, + pub iommu_segments: Option>, #[serde(default = "default_platformconfig_iommu_address_width_bits")] pub iommu_address_width_bits: u8, #[serde(default, alias = "serial_number")] @@ -134,7 +134,7 @@ pub struct PlatformConfig { #[serde(default, alias = "uuid")] pub system_uuid: Option, #[serde(default)] - pub oem_strings: Vec, + pub oem_strings: Option>, #[serde(default)] pub system_manufacturer: Option, #[serde(default)] @@ -161,6 +161,8 @@ pub struct PlatformConfig { #[cfg(target_arch = "x86_64")] impl PlatformConfig { + /// Returns `None` if no SMBIOS-relevant platform fields are set, otherwise + /// `Some` with a [`SmbiosConfig`] built from the populated fields. pub fn smbios_config(&self) -> Option { let has_system = [ &self.system_serial_number, @@ -189,20 +191,15 @@ impl PlatformConfig { .clone() .map(|asset_tag| arch::x86_64::SmbiosChassisConfig { asset_tag: Some(asset_tag), - ..Default::default() }); let smbios = arch::x86_64::SmbiosConfig { system, chassis, - oem_strings: self.oem_strings.clone(), + oem_strings: self.oem_strings.clone().unwrap_or_default(), }; - if smbios.system.is_none() && smbios.chassis.is_none() && smbios.oem_strings.is_empty() { - None - } else { - Some(smbios) - } + (!smbios.is_empty()).then_some(smbios) } } @@ -333,7 +330,7 @@ pub struct RateLimiterGroupConfig { #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub struct VirtQueueAffinity { pub queue_index: u16, - pub host_cpus: Vec, + pub host_cpus: Box<[usize]>, } #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, Default)] @@ -377,7 +374,7 @@ pub struct DiskConfig { #[serde(default)] pub serial: Option, #[serde(default)] - pub queue_affinity: Option>, + pub queue_affinity: Option>, #[serde(default)] pub backing_files: bool, #[serde(default = "default_diskconfig_sparse")] @@ -850,13 +847,13 @@ pub struct NumaDistance { pub struct NumaConfig { pub guest_numa_id: u32, #[serde(default)] - pub cpus: Option>, + pub cpus: Option>, #[serde(default)] - pub distances: Option>, + pub distances: Option>, #[serde(default)] - pub memory_zones: Option>, + pub memory_zones: Option>, #[serde(default)] - pub pci_segments: Option>, + pub pci_segments: Option>, #[serde(default)] pub device_id: Option, } @@ -1088,7 +1085,7 @@ pub struct VmConfig { #[serde(default)] pub memory: MemoryConfig, pub payload: Option, - pub rate_limit_groups: Option>, + pub rate_limit_groups: Option>, pub disks: Option>, pub net: Option>, #[serde(default)] @@ -1115,13 +1112,13 @@ pub struct VmConfig { pub pvpanic: bool, #[serde(default)] pub iommu: bool, - pub numa: Option>, + pub numa: Option>, #[serde(default)] pub watchdog: bool, #[cfg(feature = "guest_debug")] #[serde(default)] pub gdb: bool, - pub pci_segments: Option>, + pub pci_segments: Option>, pub platform: Option, pub tpm: Option, // Preserved FDs are the ones that share the same life-time as its holding @@ -1136,7 +1133,7 @@ pub struct VmConfig { pub preserved_fds: Option>, #[serde(default)] pub landlock_enable: bool, - pub landlock_rules: Option>, + pub landlock_rules: Option>, #[cfg(feature = "ivshmem")] pub ivshmem: Option, }