arch: Create ACPI IORT table

The virtual IOMMU exposed through virtio-iommu device has a dependency
on ACPI. It needs to expose the device ID of the virtio-iommu device,
and all the other devices attached to this virtual IOMMU. The IDs are
expressed from a PCI bus perspective, based on segment, bus, device and
function.

The guest relies on the topology description provided by the IORT table
to attach devices to the virtio-iommu device.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
This commit is contained in:
Sebastien Boeuf 2019-10-01 23:56:37 -07:00 committed by Samuel Ortiz
parent f40adff2a1
commit 03352f45f9
4 changed files with 124 additions and 1 deletions

View File

@ -17,6 +17,7 @@ pub fn configure_system(
_cmdline_size: usize,
_num_cpus: u8,
_serial_enabled: bool,
_virt_iommu: Option<(u32, &[u32])>,
) -> super::Result<()> {
Ok(())
}

View File

@ -10,6 +10,8 @@ use vm_memory::{GuestAddress, GuestMemoryMmap};
use vm_memory::{Address, ByteValued, Bytes};
use std::convert::TryInto;
use super::layout;
#[repr(packed)]
@ -53,6 +55,57 @@ struct PCIRangeEntry {
_reserved: u32,
}
#[repr(packed)]
#[derive(Default)]
struct IortParavirtIommuNode {
pub type_: u8,
pub length: u16,
pub revision: u8,
_reserved1: u32,
pub num_id_mappings: u32,
pub ref_id_mappings: u32,
pub device_id: u32,
_reserved2: [u32; 3],
pub model: u32,
pub flags: u32,
_reserved3: [u32; 4],
}
#[repr(packed)]
#[derive(Default)]
struct IortPciRootComplexNode {
pub type_: u8,
pub length: u16,
pub revision: u8,
_reserved1: u32,
pub num_id_mappings: u32,
pub ref_id_mappings: u32,
pub mem_access_props: IortMemoryAccessProperties,
pub ats_attr: u32,
pub pci_seg_num: u32,
pub mem_addr_size_limit: u8,
_reserved2: [u8; 3],
}
#[repr(packed)]
#[derive(Default)]
struct IortMemoryAccessProperties {
pub cca: u32,
pub ah: u8,
_reserved: u16,
pub maf: u8,
}
#[repr(packed)]
#[derive(Default)]
struct IortIdMapping {
pub input_base: u32,
pub num_of_ids: u32,
pub ouput_base: u32,
pub output_ref: u32,
pub flags: u32,
}
pub fn create_dsdt_table(
serial_enabled: bool,
start_of_device_area: GuestAddress,
@ -247,6 +300,7 @@ pub fn create_acpi_tables(
serial_enabled: bool,
start_of_device_area: GuestAddress,
end_of_device_area: GuestAddress,
virt_iommu: Option<(u32, &[u32])>,
) -> GuestAddress {
// RSDP is at the EBDA
let rsdp_offset = layout::RSDP_POINTER;
@ -349,6 +403,65 @@ pub fn create_acpi_tables(
.expect("Error writing MCFG table");
tables.push(mcfg_offset.0);
let (prev_tbl_len, prev_tbl_off) = if let Some((iommu_id, dev_ids)) = &virt_iommu {
// IORT
let mut iort = SDT::new(*b"IORT", 36, 1, *b"CLOUDH", *b"CHIORT ", 1);
// IORT number of nodes
iort.append(2u32);
// IORT offset to array of IORT nodes
iort.append(48u32);
// IORT reserved 4 bytes
iort.append(0u32);
// IORT paravirtualized IOMMU node
iort.append(IortParavirtIommuNode {
type_: 128,
length: 56,
revision: 0,
num_id_mappings: 0,
ref_id_mappings: 56,
device_id: *iommu_id,
model: 1,
..Default::default()
});
let num_entries = dev_ids.len();
let length: u16 = (36 + (20 * num_entries)).try_into().unwrap();
// IORT PCI root complex node
iort.append(IortPciRootComplexNode {
type_: 2,
length,
revision: 0,
num_id_mappings: num_entries as u32,
ref_id_mappings: 36,
ats_attr: 0,
pci_seg_num: 0,
mem_addr_size_limit: 255,
..Default::default()
});
for dev_id in dev_ids.iter() {
// IORT ID mapping
iort.append(IortIdMapping {
input_base: *dev_id,
num_of_ids: 1,
ouput_base: *dev_id,
output_ref: 48,
flags: 0,
});
}
let iort_offset = mcfg_offset.checked_add(mcfg.len() as u64).unwrap();
guest_mem
.write_slice(iort.as_slice(), iort_offset)
.expect("Error writing IORT table");
tables.push(iort_offset.0);
(iort.len(), iort_offset)
} else {
(mcfg.len(), mcfg_offset)
};
// XSDT
let mut xsdt = SDT::new(*b"XSDT", 36, 1, *b"CLOUDH", *b"CHXSDT ", 1);
for table in tables {
@ -356,7 +469,7 @@ pub fn create_acpi_tables(
}
xsdt.update_checksum();
let xsdt_offset = mcfg_offset.checked_add(mcfg.len() as u64).unwrap();
let xsdt_offset = prev_tbl_off.checked_add(prev_tbl_len as u64).unwrap();
guest_mem
.write_slice(xsdt.as_slice(), xsdt_offset)
.expect("Error writing XSDT table");

View File

@ -104,6 +104,7 @@ pub fn arch_memory_regions(size: GuestUsize) -> Vec<(GuestAddress, usize, Region
/// * `cmdline_addr` - Address in `guest_mem` where the kernel command line was loaded.
/// * `cmdline_size` - Size of the kernel command line in bytes including the null terminator.
/// * `num_cpus` - Number of virtual CPUs the guest will have.
#[allow(clippy::too_many_arguments)]
pub fn configure_system(
guest_mem: &GuestMemoryMmap,
cmdline_addr: GuestAddress,
@ -112,6 +113,7 @@ pub fn configure_system(
setup_hdr: Option<setup_header>,
_serial_enabled: bool,
_end_of_range: GuestAddress,
_virt_iommu: Option<(u32, &[u32])>,
) -> super::Result<()> {
const KERNEL_BOOT_FLAG_MAGIC: u16 = 0xaa55;
const KERNEL_HDR_MAGIC: u32 = 0x53726448;
@ -183,6 +185,7 @@ pub fn configure_system(
_serial_enabled,
start_of_device_area,
_end_of_range,
_virt_iommu,
);
params.0.acpi_rsdp_addr = rsdp_addr.0;
}
@ -251,6 +254,7 @@ mod tests {
None,
false,
GuestAddress((1 << 36) - 1),
None,
);
assert!(config_err.is_err());
assert_eq!(
@ -277,6 +281,7 @@ mod tests {
None,
false,
GuestAddress((1 << 36) - 1),
None,
)
.unwrap();
@ -297,6 +302,7 @@ mod tests {
None,
false,
GuestAddress((1 << 36) - 1),
None,
)
.unwrap();
@ -317,6 +323,7 @@ mod tests {
None,
false,
GuestAddress((1 << 36) - 1),
None,
)
.unwrap();
}

View File

@ -745,6 +745,7 @@ impl Vm {
Some(hdr),
self.config.serial.mode != ConsoleOutputMode::Off,
end_of_range,
None,
)
.map_err(|_| Error::CmdLine)?;
@ -765,6 +766,7 @@ impl Vm {
None,
self.config.serial.mode != ConsoleOutputMode::Off,
end_of_range,
None,
)
.map_err(|_| Error::CmdLine)?;