diff --git a/src/main.rs b/src/main.rs index 8db44d326..4051b0df9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -155,6 +155,15 @@ fn create_app<'a, 'b>( .default_value(default_vcpus) .group("vm-config"), ) + .arg( + Arg::with_name("platform") + .long("platform") + .help( + "num_pci_segments=", + ) + .takes_value(true) + .group("vm-config"), + ) .arg( Arg::with_name("memory") .long("memory") @@ -687,6 +696,7 @@ mod unit_tests { watchdog: false, #[cfg(feature = "tdx")] tdx: None, + platform: None, }; aver_eq!(tb, expected_vm_config, result_vm_config); diff --git a/vmm/src/config.rs b/vmm/src/config.rs index aeb8be073..6acbe9361 100644 --- a/vmm/src/config.rs +++ b/vmm/src/config.rs @@ -32,6 +32,9 @@ pub const DEFAULT_QUEUE_SIZE_VUNET: u16 = 256; pub const DEFAULT_NUM_QUEUES_VUBLK: usize = 1; pub const DEFAULT_QUEUE_SIZE_VUBLK: u16 = 128; +pub const DEFAULT_NUM_PCI_SEGMENTS: u16 = 1; +const MAX_NUM_PCI_SEGMENTS: u16 = 16; + /// Errors associated with VM configuration parameters. #[derive(Debug)] pub enum Error { @@ -101,6 +104,8 @@ pub enum Error { ParseUserDevice(OptionParserError), /// Missing socket for userspace device ParseUserDeviceSocketMissing, + /// Failed parsing platform parameters + ParsePlatform(OptionParserError), } #[derive(Debug)] @@ -149,6 +154,8 @@ pub enum ValidationError { UserDevicesRequireSharedMemory, /// Memory zone is reused across NUMA nodes MemoryZoneReused(String, u32, u32), + /// Invalid number of PCI segments + InvalidNumPciSegments(u16), } type ValidationResult = std::result::Result; @@ -206,6 +213,13 @@ impl fmt::Display for ValidationError { s, u1, u2 ) } + InvalidNumPciSegments(n) => { + write!( + f, + "Number of PCI segments ({}) not in range of 1 to {}", + n, MAX_NUM_PCI_SEGMENTS + ) + } } } } @@ -258,6 +272,7 @@ impl fmt::Display for Error { ParseTdx(o) => write!(f, "Error parsing --tdx: {}", o), #[cfg(feature = "tdx")] FirmwarePathMissing => write!(f, "TDX firmware missing"), + ParsePlatform(o) => write!(f, "Error parsing --platform: {}", o), } } } @@ -288,6 +303,7 @@ pub struct VmParams<'a> { pub watchdog: bool, #[cfg(feature = "tdx")] pub tdx: Option<&'a str>, + pub platform: Option<&'a str>, } impl<'a> VmParams<'a> { @@ -316,6 +332,7 @@ impl<'a> VmParams<'a> { let sgx_epc: Option> = args.values_of("sgx-epc").map(|x| x.collect()); let numa: Option> = args.values_of("numa").map(|x| x.collect()); let watchdog = args.is_present("watchdog"); + let platform = args.value_of("platform"); #[cfg(feature = "tdx")] let tdx = args.value_of("tdx"); VmParams { @@ -342,6 +359,7 @@ impl<'a> VmParams<'a> { watchdog, #[cfg(feature = "tdx")] tdx, + platform, } } } @@ -484,6 +502,48 @@ impl Default for CpusConfig { } } +fn default_platformconfig_num_pci_segments() -> u16 { + DEFAULT_NUM_PCI_SEGMENTS +} + +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct PlatformConfig { + #[serde(default = "default_platformconfig_num_pci_segments")] + pub num_pci_segments: u16, +} + +impl PlatformConfig { + pub fn parse(platform: &str) -> Result { + let mut parser = OptionParser::new(); + parser.add("num_pci_segments"); + parser.parse(platform).map_err(Error::ParseCpus)?; + + let num_pci_segments: u16 = parser + .convert("num_pci_segments") + .map_err(Error::ParsePlatform)? + .unwrap_or(DEFAULT_NUM_PCI_SEGMENTS); + Ok(PlatformConfig { num_pci_segments }) + } + + pub fn validate(&self) -> ValidationResult<()> { + if self.num_pci_segments == 0 || self.num_pci_segments > MAX_NUM_PCI_SEGMENTS { + return Err(ValidationError::InvalidNumPciSegments( + self.num_pci_segments, + )); + } + + Ok(()) + } +} + +impl Default for PlatformConfig { + fn default() -> Self { + PlatformConfig { + num_pci_segments: DEFAULT_NUM_PCI_SEGMENTS, + } + } +} + #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] pub struct MemoryZoneConfig { pub id: String, @@ -1849,6 +1909,7 @@ pub struct VmConfig { pub watchdog: bool, #[cfg(feature = "tdx")] pub tdx: Option, + pub platform: Option, } impl VmConfig { @@ -1970,6 +2031,8 @@ impl VmConfig { } } + self.platform.as_ref().map(|p| p.validate()).transpose()?; + Ok(()) } @@ -2072,6 +2135,8 @@ impl VmConfig { vsock = Some(vsock_config); } + let platform = vm_params.platform.map(PlatformConfig::parse).transpose()?; + #[cfg(target_arch = "x86_64")] let mut sgx_epc: Option> = None; #[cfg(target_arch = "x86_64")] @@ -2137,6 +2202,7 @@ impl VmConfig { watchdog: vm_params.watchdog, #[cfg(feature = "tdx")] tdx, + platform, }; config.validate().map_err(Error::Validation)?; Ok(config) @@ -2747,6 +2813,7 @@ mod tests { watchdog: false, #[cfg(feature = "tdx")] tdx: None, + platform: None, }; assert!(valid_config.validate().is_ok()); @@ -2860,9 +2927,21 @@ mod tests { invalid_config.memory.hugepage_size = Some(2 << 20); assert!(invalid_config.validate().is_err()); - let mut invalid_config = valid_config; + let mut invalid_config = valid_config.clone(); invalid_config.memory.hugepages = true; invalid_config.memory.hugepage_size = Some(3 << 20); assert!(invalid_config.validate().is_err()); + + let mut still_valid_config = valid_config.clone(); + still_valid_config.platform = Some(PlatformConfig { + num_pci_segments: 16, + }); + assert!(still_valid_config.validate().is_ok()); + + let mut invalid_config = valid_config; + invalid_config.platform = Some(PlatformConfig { + num_pci_segments: 17, + }); + assert!(invalid_config.validate().is_err()); } }