virtio-devices, vmm: Update virtio-iommu to rely on VIOT

Since using the VIRTIO configuration to expose the virtual IOMMU
topology has been deprecated, the virtio-iommu implementation must be
updated.

In order to follow the latest patchset that is about to be merged in the
upstream Linux kernel, it must rely on ACPI, and in particular the newly
introduced VIOT table to expose the information about the list of PCI
devices attached to the virtual IOMMU.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
This commit is contained in:
Sebastien Boeuf
2021-06-14 15:38:24 +02:00
parent de7cfd61a0
commit a6fe4aa7e9
6 changed files with 151 additions and 141 deletions

View File

@@ -109,6 +109,32 @@ impl MemoryAffinity {
}
}
#[repr(packed)]
#[derive(Default)]
struct ViotVirtioPciNode {
pub type_: u8,
_reserved: u8,
pub length: u16,
pub pci_segment: u16,
pub pci_bdf_number: u16,
_reserved2: [u8; 8],
}
#[repr(packed)]
#[derive(Default)]
struct ViotPciRangeNode {
pub type_: u8,
_reserved: u8,
pub length: u16,
pub endpoint_start: u32,
pub pci_segment_start: u16,
pub pci_segment_end: u16,
pub pci_bdf_start: u16,
pub pci_bdf_end: u16,
pub output_node: u16,
_reserved2: [u8; 6],
}
pub fn create_dsdt_table(
device_manager: &Arc<Mutex<DeviceManager>>,
cpu_manager: &Arc<Mutex<CpuManager>>,
@@ -360,6 +386,42 @@ fn create_iort_table() -> Sdt {
iort
}
fn create_viot_table(iommu_bdf: u32, devices_bdf: &[u32]) -> Sdt {
// VIOT
let mut viot = Sdt::new(*b"VIOT", 36, 0, *b"CLOUDH", *b"CHVIOT ", 0);
// Node count
viot.append((devices_bdf.len() + 1) as u16);
// Node offset
viot.append(48u16);
// VIOT reserved 8 bytes
viot.append_slice(&[0u8; 8]);
// Virtio-iommu based on virtio-pci node
viot.append(ViotVirtioPciNode {
type_: 3,
length: 16,
pci_segment: 0,
pci_bdf_number: iommu_bdf as u16,
..Default::default()
});
for device_bdf in devices_bdf {
viot.append(ViotPciRangeNode {
type_: 1,
length: 24,
endpoint_start: *device_bdf,
pci_segment_start: 0,
pci_segment_end: 0,
pci_bdf_start: *device_bdf as u16,
pci_bdf_end: *device_bdf as u16,
output_node: 48,
..Default::default()
});
}
viot
}
pub fn create_acpi_tables(
guest_mem: &GuestMemoryMmap,
device_manager: &Arc<Mutex<DeviceManager>>,
@@ -489,6 +551,20 @@ pub fn create_acpi_tables(
prev_tbl_off = iort_offset;
}
// VIOT
if let Some((iommu_bdf, devices_bdf)) = device_manager.lock().unwrap().iommu_attached_devices()
{
let viot = create_viot_table(*iommu_bdf, devices_bdf);
let viot_offset = prev_tbl_off.checked_add(prev_tbl_len).unwrap();
guest_mem
.write_slice(viot.as_slice(), viot_offset)
.expect("Error writing VIOT table");
tables.push(viot_offset.0);
prev_tbl_len = viot.len() as u64;
prev_tbl_off = viot_offset;
}
// XSDT
let mut xsdt = Sdt::new(*b"XSDT", 36, 1, *b"CLOUDH", *b"CHXSDT ", 1);
for table in tables {

View File

@@ -869,6 +869,12 @@ pub struct DeviceManager {
// Paravirtualized IOMMU
iommu_device: Option<Arc<Mutex<virtio_devices::Iommu>>>,
// PCI information about devices attached to the paravirtualized IOMMU
// It contains the virtual IOMMU PCI BDF along with the list of PCI BDF
// representing the devices attached to the virtual IOMMU. This is useful
// information for filling the ACPI VIOT table.
iommu_attached_devices: Option<(u32, Vec<u32>)>,
// Bitmap of PCI devices to hotplug.
pci_devices_up: u32,
@@ -975,6 +981,7 @@ impl DeviceManager {
legacy_interrupt_manager: None,
passthrough_device: None,
iommu_device: None,
iommu_attached_devices: None,
pci_devices_up: 0,
pci_devices_down: 0,
pci_irq_slots: [0; 32],
@@ -1196,15 +1203,8 @@ impl DeviceManager {
iommu_attached_devices.append(&mut vfio_iommu_device_ids);
if let Some(iommu_device) = iommu_device {
iommu_device
.lock()
.unwrap()
.attach_pci_devices(0, iommu_attached_devices);
// Because we determined the virtio-iommu b/d/f, we have to
// add the device to the PCI topology now. Otherwise, the
// b/d/f won't match the virtio-iommu device as expected.
self.add_virtio_pci_device(iommu_device, &mut pci_bus, &None, iommu_id)?;
let dev_id = self.add_virtio_pci_device(iommu_device, &mut pci_bus, &None, iommu_id)?;
self.iommu_attached_devices = Some((dev_id, iommu_attached_devices));
}
let pci_bus = Arc::new(Mutex::new(pci_bus));
@@ -3486,6 +3486,10 @@ impl DeviceManager {
.trigger_key(3)
.map_err(DeviceManagerError::AArch64PowerButtonNotification)
}
pub fn iommu_attached_devices(&self) -> &Option<(u32, Vec<u32>)> {
&self.iommu_attached_devices
}
}
#[cfg(feature = "acpi")]