diff --git a/vmm/src/acpi.rs b/vmm/src/acpi.rs index 0b93affac..f14f104b4 100644 --- a/vmm/src/acpi.rs +++ b/vmm/src/acpi.rs @@ -3,7 +3,6 @@ // SPDX-License-Identifier: Apache-2.0 // use acpi_tables::{ - aml, aml::Aml, rsdp::RSDP, sdt::{GenericAddress, SDT}, @@ -16,6 +15,7 @@ use std::convert::TryInto; use std::sync::{Arc, Mutex}; use crate::cpu::CpuManager; +use crate::device_manager::DeviceManager; use arch::layout; #[repr(packed)] @@ -79,127 +79,14 @@ struct IortIdMapping { pub flags: u32, } -fn create_ged_device() -> Vec { - aml::Device::new( - "_SB_.GED_".into(), - vec![ - &aml::Name::new("_HID".into(), &"ACPI0013"), - &aml::Name::new("_UID".into(), &aml::ZERO), - &aml::Name::new( - "_CRS".into(), - &aml::ResourceTemplate::new(vec![&aml::Interrupt::new( - true, true, false, false, 5, - )]), - ), - &aml::OpRegion::new("GDST".into(), aml::OpRegionSpace::SystemIO, 0xb000, 0x1), - &aml::Field::new( - "GDST".into(), - aml::FieldAccessType::Byte, - aml::FieldUpdateRule::WriteAsZeroes, - vec![aml::FieldEntry::Named(*b"GDAT", 8)], - ), - &aml::Method::new( - "_EVT".into(), - 1, - true, - vec![&aml::If::new( - &aml::Equal::new(&aml::Path::new("GDAT"), &aml::ONE), - vec![&aml::MethodCall::new("\\_SB_.CPUS.CTFY".into(), vec![])], - )], - ), - ], - ) - .to_aml_bytes() -} - pub fn create_dsdt_table( - serial_enabled: bool, - start_of_device_area: GuestAddress, - end_of_device_area: GuestAddress, + device_manager: &DeviceManager, cpu_manager: &Arc>, ) -> SDT { - let pci_dsdt_data = aml::Device::new( - "_SB_.PCI0".into(), - vec![ - &aml::Name::new("_HID".into(), &aml::EISAName::new("PNP0A08")), - &aml::Name::new("_CID".into(), &aml::EISAName::new("PNP0A03")), - &aml::Name::new("_ADR".into(), &aml::ZERO), - &aml::Name::new("_SEG".into(), &aml::ZERO), - &aml::Name::new("_UID".into(), &aml::ZERO), - &aml::Name::new("SUPP".into(), &aml::ZERO), - &aml::Name::new( - "_CRS".into(), - &aml::ResourceTemplate::new(vec![ - &aml::AddressSpace::new_bus_number(0x0u16, 0xffu16), - &aml::IO::new(0xcf8, 0xcf8, 1, 0x8), - &aml::AddressSpace::new_io(0x0u16, 0xcf7u16), - &aml::AddressSpace::new_io(0xd00u16, 0xffffu16), - &aml::AddressSpace::new_memory( - aml::AddressSpaceCachable::NotCacheable, - true, - layout::MEM_32BIT_DEVICES_START.0 as u32, - (layout::MEM_32BIT_DEVICES_START.0 + layout::MEM_32BIT_DEVICES_SIZE - 1) - as u32, - ), - &aml::AddressSpace::new_memory( - aml::AddressSpaceCachable::NotCacheable, - true, - start_of_device_area.0, - end_of_device_area.0, - ), - ]), - ), - ], - ) - .to_aml_bytes(); - - let mbrd_dsdt_data = aml::Device::new( - "_SB_.MBRD".into(), - vec![ - &aml::Name::new("_HID".into(), &aml::EISAName::new("PNP0C02")), - &aml::Name::new("_UID".into(), &aml::ZERO), - &aml::Name::new( - "_CRS".into(), - &aml::ResourceTemplate::new(vec![&aml::Memory32Fixed::new( - true, - layout::PCI_MMCONFIG_START.0 as u32, - layout::PCI_MMCONFIG_SIZE as u32, - )]), - ), - ], - ) - .to_aml_bytes(); - - let com1_dsdt_data = aml::Device::new( - "_SB_.COM1".into(), - vec![ - &aml::Name::new("_HID".into(), &aml::EISAName::new("PNP0501")), - &aml::Name::new("_UID".into(), &aml::ZERO), - &aml::Name::new( - "_CRS".into(), - &aml::ResourceTemplate::new(vec![ - &aml::Interrupt::new(true, true, false, false, 4), - &aml::IO::new(0x3f8, 0x3f8, 0, 0x8), - ]), - ), - ], - ) - .to_aml_bytes(); - - let s5_sleep_data = - aml::Name::new("_S5_".into(), &aml::Package::new(vec![&5u8])).to_aml_bytes(); - - let ged_data = create_ged_device(); - // DSDT let mut dsdt = SDT::new(*b"DSDT", 36, 6, *b"CLOUDH", *b"CHDSDT ", 1); - dsdt.append_slice(pci_dsdt_data.as_slice()); - dsdt.append_slice(mbrd_dsdt_data.as_slice()); - if serial_enabled { - dsdt.append_slice(com1_dsdt_data.as_slice()); - } - dsdt.append_slice(s5_sleep_data.as_slice()); - dsdt.append_slice(ged_data.as_slice()); + + dsdt.append_slice(device_manager.to_aml_bytes().as_slice()); dsdt.append_slice(cpu_manager.lock().unwrap().to_aml_bytes().as_slice()); dsdt @@ -207,10 +94,7 @@ pub fn create_dsdt_table( pub fn create_acpi_tables( guest_mem: &GuestMemoryMmap, - serial_enabled: bool, - start_of_device_area: GuestAddress, - end_of_device_area: GuestAddress, - virt_iommu: Option<(u32, &[u32])>, + device_manager: &DeviceManager, cpu_manager: &Arc>, ) -> GuestAddress { // RSDP is at the EBDA @@ -218,12 +102,7 @@ pub fn create_acpi_tables( let mut tables: Vec = Vec::new(); // DSDT - let dsdt = create_dsdt_table( - serial_enabled, - start_of_device_area, - end_of_device_area, - cpu_manager, - ); + let dsdt = create_dsdt_table(device_manager, cpu_manager); let dsdt_offset = rsdp_offset.checked_add(RSDP::len() as u64).unwrap(); guest_mem .write_slice(dsdt.as_slice(), dsdt_offset) @@ -288,64 +167,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 (prev_tbl_len, prev_tbl_off) = + if let Some((iommu_id, dev_ids)) = &device_manager.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 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); + let num_entries = dev_ids.len(); + let length: u16 = (36 + (20 * num_entries)).try_into().unwrap(); - (iort.len(), iort_offset) - } else { - (mcfg.len(), mcfg_offset) - }; + // 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); diff --git a/vmm/src/device_manager.rs b/vmm/src/device_manager.rs index cd6d18586..955a30e91 100644 --- a/vmm/src/device_manager.rs +++ b/vmm/src/device_manager.rs @@ -9,9 +9,11 @@ // SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause // -use crate::config::ConsoleOutputMode; +use crate::config::{ConsoleOutputMode, VmConfig}; use crate::vm::VmInfo; - +#[cfg(feature = "acpi")] +use acpi_tables::{aml, aml::Aml}; +use arch::layout; use devices::{ioapic, HotPlugNotificationType}; use kvm_bindings::{kvm_irq_routing_entry, kvm_userspace_memory_region}; use kvm_ioctls::*; @@ -393,6 +395,13 @@ pub struct DeviceManager { // ACPI GED notification device #[cfg(feature = "acpi")] ged_notification_device: Option>>, + + // Dimensions of the device area + start_of_device_area: GuestAddress, + end_of_device_area: GuestAddress, + + // VM configuration + config: Arc>, } impl DeviceManager { @@ -474,6 +483,10 @@ impl DeviceManager { )?; } + let start_of_device_area = vm_info.start_of_device_area; + let end_of_device_area = vm_info.end_of_device_area; + let config = vm_info.vm_cfg.clone(); + Ok(DeviceManager { address_manager, console, @@ -483,6 +496,9 @@ impl DeviceManager { virt_iommu, #[cfg(feature = "acpi")] ged_notification_device, + start_of_device_area, + end_of_device_area, + config, }) } @@ -1540,3 +1556,125 @@ impl Drop for DeviceManager { } } } + +#[cfg(feature = "acpi")] +fn create_ged_device() -> Vec { + aml::Device::new( + "_SB_.GED_".into(), + vec![ + &aml::Name::new("_HID".into(), &"ACPI0013"), + &aml::Name::new("_UID".into(), &aml::ZERO), + &aml::Name::new( + "_CRS".into(), + &aml::ResourceTemplate::new(vec![&aml::Interrupt::new( + true, true, false, false, 5, + )]), + ), + &aml::OpRegion::new("GDST".into(), aml::OpRegionSpace::SystemIO, 0xb000, 0x1), + &aml::Field::new( + "GDST".into(), + aml::FieldAccessType::Byte, + aml::FieldUpdateRule::WriteAsZeroes, + vec![aml::FieldEntry::Named(*b"GDAT", 8)], + ), + &aml::Method::new( + "_EVT".into(), + 1, + true, + vec![&aml::If::new( + &aml::Equal::new(&aml::Path::new("GDAT"), &aml::ONE), + vec![&aml::MethodCall::new("\\_SB_.CPUS.CTFY".into(), vec![])], + )], + ), + ], + ) + .to_aml_bytes() +} + +#[cfg(feature = "acpi")] +impl Aml for DeviceManager { + fn to_aml_bytes(&self) -> Vec { + let mut bytes = Vec::new(); + let pci_dsdt_data = aml::Device::new( + "_SB_.PCI0".into(), + vec![ + &aml::Name::new("_HID".into(), &aml::EISAName::new("PNP0A08")), + &aml::Name::new("_CID".into(), &aml::EISAName::new("PNP0A03")), + &aml::Name::new("_ADR".into(), &aml::ZERO), + &aml::Name::new("_SEG".into(), &aml::ZERO), + &aml::Name::new("_UID".into(), &aml::ZERO), + &aml::Name::new("SUPP".into(), &aml::ZERO), + &aml::Name::new( + "_CRS".into(), + &aml::ResourceTemplate::new(vec![ + &aml::AddressSpace::new_bus_number(0x0u16, 0xffu16), + &aml::IO::new(0xcf8, 0xcf8, 1, 0x8), + &aml::AddressSpace::new_io(0x0u16, 0xcf7u16), + &aml::AddressSpace::new_io(0xd00u16, 0xffffu16), + &aml::AddressSpace::new_memory( + aml::AddressSpaceCachable::NotCacheable, + true, + layout::MEM_32BIT_DEVICES_START.0 as u32, + (layout::MEM_32BIT_DEVICES_START.0 + layout::MEM_32BIT_DEVICES_SIZE - 1) + as u32, + ), + &aml::AddressSpace::new_memory( + aml::AddressSpaceCachable::NotCacheable, + true, + self.start_of_device_area.0, + self.end_of_device_area.0, + ), + ]), + ), + ], + ) + .to_aml_bytes(); + + let mbrd_dsdt_data = aml::Device::new( + "_SB_.MBRD".into(), + vec![ + &aml::Name::new("_HID".into(), &aml::EISAName::new("PNP0C02")), + &aml::Name::new("_UID".into(), &aml::ZERO), + &aml::Name::new( + "_CRS".into(), + &aml::ResourceTemplate::new(vec![&aml::Memory32Fixed::new( + true, + layout::PCI_MMCONFIG_START.0 as u32, + layout::PCI_MMCONFIG_SIZE as u32, + )]), + ), + ], + ) + .to_aml_bytes(); + + let com1_dsdt_data = aml::Device::new( + "_SB_.COM1".into(), + vec![ + &aml::Name::new("_HID".into(), &aml::EISAName::new("PNP0501")), + &aml::Name::new("_UID".into(), &aml::ZERO), + &aml::Name::new( + "_CRS".into(), + &aml::ResourceTemplate::new(vec![ + &aml::Interrupt::new(true, true, false, false, 4), + &aml::IO::new(0x3f8, 0x3f8, 0, 0x8), + ]), + ), + ], + ) + .to_aml_bytes(); + + let s5_sleep_data = + aml::Name::new("_S5_".into(), &aml::Package::new(vec![&5u8])).to_aml_bytes(); + + let ged_data = create_ged_device(); + + bytes.extend_from_slice(pci_dsdt_data.as_slice()); + bytes.extend_from_slice(mbrd_dsdt_data.as_slice()); + if self.config.lock().unwrap().serial.mode != ConsoleOutputMode::Off { + bytes.extend_from_slice(com1_dsdt_data.as_slice()); + } + bytes.extend_from_slice(s5_sleep_data.as_slice()); + bytes.extend_from_slice(ged_data.as_slice()); + bytes + } +} diff --git a/vmm/src/vm.rs b/vmm/src/vm.rs index 378572062..7c1e46529 100755 --- a/vmm/src/vm.rs +++ b/vmm/src/vm.rs @@ -152,6 +152,8 @@ pub struct VmInfo<'a> { pub memory: &'a Arc>, pub vm_fd: &'a Arc, pub vm_cfg: Arc>, + pub start_of_device_area: GuestAddress, + pub end_of_device_area: GuestAddress, } #[derive(Clone, Copy, Debug, Deserialize, Serialize, PartialEq)] @@ -433,6 +435,14 @@ impl Vm { .ok_or(Error::MemoryRangeAllocation)?; } + let end_of_device_area = GuestAddress((1 << get_host_cpu_phys_bits()) - 1); + let mem_end = guest_memory.end_addr(); + let start_of_device_area = if mem_end < arch::layout::MEM_32BIT_RESERVED_START { + arch::layout::RAM_64BIT_START + } else { + mem_end.unchecked_add(1) + }; + // Convert the guest memory into an Arc. The point being able to use it // anywhere in the code, no matter which thread might use it. // Add the RwLock aspect to guest memory as we might want to perform @@ -443,6 +453,8 @@ impl Vm { memory: &guest_memory, vm_fd: &fd, vm_cfg: config.clone(), + start_of_device_area, + end_of_device_area, }; let device_manager = DeviceManager::new( @@ -526,26 +538,11 @@ impl Vm { #[cfg(feature = "acpi")] { - rsdp_addr = Some({ - let end_of_range = GuestAddress((1 << get_host_cpu_phys_bits()) - 1); - - let mem_end = mem.end_addr(); - let start_of_device_area = if mem_end < arch::layout::MEM_32BIT_RESERVED_START { - arch::layout::RAM_64BIT_START - } else { - mem_end.unchecked_add(1) - }; - - use crate::config::ConsoleOutputMode; - crate::acpi::create_acpi_tables( - &mem, - self.config.lock().unwrap().serial.mode != ConsoleOutputMode::Off, - start_of_device_area, - end_of_range, - self.devices.virt_iommu(), - &self.cpu_manager, - ) - }); + rsdp_addr = Some(crate::acpi::create_acpi_tables( + &mem, + &self.devices, + &self.cpu_manager, + )); } match entry_addr.setup_header {