From 0834eca8d4b3c7b06a4fcae3a4291baa26dd5d90 Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Fri, 18 Mar 2022 16:33:08 +0000 Subject: [PATCH] vmm: config: Validate IOMMU configuration Ensure devices that are specified to be on a PCI segment that is behind the IOMMU are IOMMU enabled if possible or error out for those devices that do not support it. Signed-off-by: Rob Bradford --- tests/integration.rs | 8 +- vmm/src/config.rs | 222 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 228 insertions(+), 2 deletions(-) diff --git a/tests/integration.rs b/tests/integration.rs index 30f0495d3..ce797d6bd 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -2130,7 +2130,13 @@ mod parallel { let (cmd_success, cmd_output) = remote_command_w_output( &api_socket, "add-disk", - Some(format!("path={},id=test0,pci_segment=1", test_disk_path.as_str()).as_str()), + Some( + format!( + "path={},id=test0,pci_segment=1,iommu=on", + test_disk_path.as_str() + ) + .as_str(), + ), ); assert!(cmd_success); assert!(String::from_utf8_lossy(&cmd_output) diff --git a/vmm/src/config.rs b/vmm/src/config.rs index d205bb299..ee8f6c191 100644 --- a/vmm/src/config.rs +++ b/vmm/src/config.rs @@ -164,6 +164,10 @@ pub enum ValidationError { InvalidPciSegment(u16), /// Balloon too big BalloonLargerThanRam(u64, u64), + /// On a IOMMU segment but not behind IOMMU + OnIommuSegment(u16), + // On a IOMMU segment but IOMMU not suported + IommuNotSupported(u16), } type ValidationResult = std::result::Result; @@ -236,6 +240,20 @@ impl fmt::Display for ValidationError { balloon_size, ram_size ) } + OnIommuSegment(pci_segment) => { + write!( + f, + "Device is on an IOMMU PCI segment ({}) but not placed behind IOMMU", + pci_segment + ) + } + IommuNotSupported(pci_segment) => { + write!( + f, + "Device is on an IOMMU PCI segment ({}) but does support being placed behind IOMMU", + pci_segment + ) + } } } } @@ -1091,6 +1109,12 @@ impl DiskConfig { if self.pci_segment >= platform_config.num_pci_segments { return Err(ValidationError::InvalidPciSegment(self.pci_segment)); } + + if let Some(iommu_segments) = platform_config.iommu_segments.as_ref() { + if iommu_segments.contains(&self.pci_segment) && !self.iommu { + return Err(ValidationError::OnIommuSegment(self.pci_segment)); + } + } } Ok(()) @@ -1381,6 +1405,12 @@ impl NetConfig { if self.pci_segment >= platform_config.num_pci_segments { return Err(ValidationError::InvalidPciSegment(self.pci_segment)); } + + if let Some(iommu_segments) = platform_config.iommu_segments.as_ref() { + if iommu_segments.contains(&self.pci_segment) && !self.iommu { + return Err(ValidationError::OnIommuSegment(self.pci_segment)); + } + } } Ok(()) @@ -1597,6 +1627,12 @@ impl FsConfig { if self.pci_segment >= platform_config.num_pci_segments { return Err(ValidationError::InvalidPciSegment(self.pci_segment)); } + + if let Some(iommu_segments) = platform_config.iommu_segments.as_ref() { + if iommu_segments.contains(&self.pci_segment) { + return Err(ValidationError::IommuNotSupported(self.pci_segment)); + } + } } Ok(()) @@ -1678,6 +1714,12 @@ impl PmemConfig { if self.pci_segment >= platform_config.num_pci_segments { return Err(ValidationError::InvalidPciSegment(self.pci_segment)); } + + if let Some(iommu_segments) = platform_config.iommu_segments.as_ref() { + if iommu_segments.contains(&self.pci_segment) && !self.iommu { + return Err(ValidationError::OnIommuSegment(self.pci_segment)); + } + } } Ok(()) @@ -1810,6 +1852,12 @@ impl DeviceConfig { if self.pci_segment >= platform_config.num_pci_segments { return Err(ValidationError::InvalidPciSegment(self.pci_segment)); } + + if let Some(iommu_segments) = platform_config.iommu_segments.as_ref() { + if iommu_segments.contains(&self.pci_segment) && !self.iommu { + return Err(ValidationError::OnIommuSegment(self.pci_segment)); + } + } } Ok(()) @@ -1855,6 +1903,12 @@ impl UserDeviceConfig { if self.pci_segment >= platform_config.num_pci_segments { return Err(ValidationError::InvalidPciSegment(self.pci_segment)); } + + if let Some(iommu_segments) = platform_config.iommu_segments.as_ref() { + if iommu_segments.contains(&self.pci_segment) { + return Err(ValidationError::IommuNotSupported(self.pci_segment)); + } + } } Ok(()) @@ -1916,6 +1970,12 @@ impl VdpaConfig { if self.pci_segment >= platform_config.num_pci_segments { return Err(ValidationError::InvalidPciSegment(self.pci_segment)); } + + if let Some(iommu_segments) = platform_config.iommu_segments.as_ref() { + if iommu_segments.contains(&self.pci_segment) { + return Err(ValidationError::IommuNotSupported(self.pci_segment)); + } + } } Ok(()) @@ -1980,6 +2040,12 @@ impl VsockConfig { if self.pci_segment >= platform_config.num_pci_segments { return Err(ValidationError::InvalidPciSegment(self.pci_segment)); } + + if let Some(iommu_segments) = platform_config.iommu_segments.as_ref() { + if iommu_segments.contains(&self.pci_segment) && !self.iommu { + return Err(ValidationError::OnIommuSegment(self.pci_segment)); + } + } } Ok(()) @@ -3333,11 +3399,165 @@ mod tests { }); assert!(still_valid_config.validate().is_ok()); - let mut invalid_config = valid_config; + let mut invalid_config = valid_config.clone(); invalid_config.platform = Some(PlatformConfig { num_pci_segments: 16, iommu_segments: Some(vec![17, 18]), }); assert!(invalid_config.validate().is_err()); + + let mut still_valid_config = valid_config.clone(); + still_valid_config.platform = Some(PlatformConfig { + num_pci_segments: 16, + iommu_segments: Some(vec![1, 2, 3]), + }); + still_valid_config.disks = Some(vec![DiskConfig { + iommu: true, + pci_segment: 1, + ..Default::default() + }]); + assert!(still_valid_config.validate().is_ok()); + + let mut still_valid_config = valid_config.clone(); + still_valid_config.platform = Some(PlatformConfig { + num_pci_segments: 16, + iommu_segments: Some(vec![1, 2, 3]), + }); + still_valid_config.net = Some(vec![NetConfig { + iommu: true, + pci_segment: 1, + ..Default::default() + }]); + assert!(still_valid_config.validate().is_ok()); + + let mut still_valid_config = valid_config.clone(); + still_valid_config.platform = Some(PlatformConfig { + num_pci_segments: 16, + iommu_segments: Some(vec![1, 2, 3]), + }); + still_valid_config.pmem = Some(vec![PmemConfig { + iommu: true, + pci_segment: 1, + ..Default::default() + }]); + assert!(still_valid_config.validate().is_ok()); + + let mut still_valid_config = valid_config.clone(); + still_valid_config.platform = Some(PlatformConfig { + num_pci_segments: 16, + iommu_segments: Some(vec![1, 2, 3]), + }); + still_valid_config.devices = Some(vec![DeviceConfig { + iommu: true, + pci_segment: 1, + ..Default::default() + }]); + assert!(still_valid_config.validate().is_ok()); + + let mut still_valid_config = valid_config.clone(); + still_valid_config.platform = Some(PlatformConfig { + num_pci_segments: 16, + iommu_segments: Some(vec![1, 2, 3]), + }); + still_valid_config.vsock = Some(VsockConfig { + iommu: true, + pci_segment: 1, + ..Default::default() + }); + assert!(still_valid_config.validate().is_ok()); + + let mut invalid_config = valid_config.clone(); + invalid_config.platform = Some(PlatformConfig { + num_pci_segments: 16, + iommu_segments: Some(vec![1, 2, 3]), + }); + invalid_config.disks = Some(vec![DiskConfig { + iommu: false, + pci_segment: 1, + ..Default::default() + }]); + assert!(invalid_config.validate().is_err()); + + let mut invalid_config = valid_config.clone(); + invalid_config.platform = Some(PlatformConfig { + num_pci_segments: 16, + iommu_segments: Some(vec![1, 2, 3]), + }); + invalid_config.net = Some(vec![NetConfig { + iommu: false, + pci_segment: 1, + ..Default::default() + }]); + assert!(invalid_config.validate().is_err()); + + let mut invalid_config = valid_config.clone(); + invalid_config.platform = Some(PlatformConfig { + num_pci_segments: 16, + iommu_segments: Some(vec![1, 2, 3]), + }); + invalid_config.pmem = Some(vec![PmemConfig { + iommu: false, + pci_segment: 1, + ..Default::default() + }]); + assert!(invalid_config.validate().is_err()); + + let mut invalid_config = valid_config.clone(); + invalid_config.platform = Some(PlatformConfig { + num_pci_segments: 16, + iommu_segments: Some(vec![1, 2, 3]), + }); + invalid_config.devices = Some(vec![DeviceConfig { + iommu: false, + pci_segment: 1, + ..Default::default() + }]); + assert!(invalid_config.validate().is_err()); + + let mut invalid_config = valid_config.clone(); + invalid_config.platform = Some(PlatformConfig { + num_pci_segments: 16, + iommu_segments: Some(vec![1, 2, 3]), + }); + invalid_config.vsock = Some(VsockConfig { + iommu: false, + pci_segment: 1, + ..Default::default() + }); + assert!(invalid_config.validate().is_err()); + + let mut invalid_config = valid_config.clone(); + invalid_config.memory.shared = true; + invalid_config.platform = Some(PlatformConfig { + num_pci_segments: 16, + iommu_segments: Some(vec![1, 2, 3]), + }); + invalid_config.user_devices = Some(vec![UserDeviceConfig { + pci_segment: 1, + ..Default::default() + }]); + assert!(invalid_config.validate().is_err()); + + let mut invalid_config = valid_config.clone(); + invalid_config.platform = Some(PlatformConfig { + num_pci_segments: 16, + iommu_segments: Some(vec![1, 2, 3]), + }); + invalid_config.vdpa = Some(vec![VdpaConfig { + pci_segment: 1, + ..Default::default() + }]); + assert!(invalid_config.validate().is_err()); + + let mut invalid_config = valid_config; + invalid_config.platform = Some(PlatformConfig { + num_pci_segments: 16, + iommu_segments: Some(vec![1, 2, 3]), + }); + invalid_config.fs = Some(vec![FsConfig { + pci_segment: 1, + ..Default::default() + }]); + assert!(invalid_config.validate().is_err()); } }