// Copyright © 2019 Intel Corporation // // SPDX-License-Identifier: Apache-2.0 // use acpi_tables::{ rsdp::RSDP, sdt::{GenericAddress, SDT}, }; use vm_memory::{GuestAddress, GuestMemoryMmap}; use vm_memory::{Address, ByteValued, Bytes}; #[repr(packed)] struct LocalAPIC { pub r#type: u8, pub length: u8, pub processor_id: u8, pub apic_id: u8, pub flags: u32, } #[repr(packed)] #[derive(Default)] struct IOAPIC { pub r#type: u8, pub length: u8, pub ioapic_id: u8, _reserved: u8, pub apic_address: u32, pub gsi_base: u32, } #[repr(packed)] #[derive(Default)] struct InterruptSourceOverride { pub r#type: u8, pub length: u8, pub bus: u8, pub source: u8, pub gsi: u32, pub flags: u16, } #[repr(packed)] #[derive(Default)] struct PCIRangeEntry { pub base_address: u64, pub segment: u16, pub start: u8, pub end: u8, _reserved: u32, } pub fn create_dsdt_table(serial_enabled: bool) -> SDT { /* The hex tables in this file are generated from the ASL below with: "iasl -tc " As the output contains a table header that is not required the first 40 bytes should be disregarded. */ /* Device (_SB.PCI0) { Name (_HID, EisaId ("PNP0A08") /* PCI Express Bus */) // _HID: Hardware ID Name (_CID, EisaId ("PNP0A03") /* PCI Bus */) // _CID: Compatible ID Name (_ADR, Zero) // _ADR: Address Name (_SEG, Zero) // _SEG: PCI Segment Name (_UID, Zero) // _UID: Unique ID Name (SUPP, Zero) } Scope (_SB.PCI0) { Name (_CRS, ResourceTemplate () // _CRS: Current Resource Settings { WordBusNumber (ResourceProducer, MinFixed, MaxFixed, PosDecode, 0x0000, // Granularity 0x0000, // Range Minimum 0x00FF, // Range Maximum 0x0000, // Translation Offset 0x0100, // Length ,, ) IO (Decode16, 0x0CF8, // Range Minimum 0x0CF8, // Range Maximum 0x01, // Alignment 0x08, // Length ) WordIO (ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange, 0x0000, // Granularity 0x0000, // Range Minimum 0x0CF7, // Range Maximum 0x0000, // Translation Offset 0x0CF8, // Length ,, , TypeStatic, DenseTranslation) WordIO (ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange, 0x0000, // Granularity 0x0D00, // Range Minimum 0xFFFF, // Range Maximum 0x0000, // Translation Offset 0xF300, // Length ,, , TypeStatic, DenseTranslation) DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, Cacheable, ReadWrite, 0x00000000, // Granularity 0x000A0000, // Range Minimum 0x000BFFFF, // Range Maximum 0x00000000, // Translation Offset 0x00020000, // Length ,, , AddressRangeMemory, TypeStatic) DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, NonCacheable, ReadWrite, 0x00000000, // Granularity 0xC0000000, // Range Minimum 0xFEBFFFFF, // Range Maximum 0x00000000, // Translation Offset 0x3EC00000, // Length ,, , AddressRangeMemory, TypeStatic) QWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, Cacheable, ReadWrite, 0x0000000000000000, // Granularity 0x0000000800000000, // Range Minimum 0x0000000FFFFFFFFF, // Range Maximum 0x0000000000000000, // Translation Offset 0x0000000800000000, // Length ,, , AddressRangeMemory, TypeStatic) }) } */ let pci_dsdt_data = [ 0x5Bu8, 0x82, 0x36, 0x2E, 0x5F, 0x53, 0x42, 0x5F, 0x50, 0x43, 0x49, 0x30, 0x08, 0x5F, 0x48, 0x49, 0x44, 0x0C, 0x41, 0xD0, 0x0A, 0x08, 0x08, 0x5F, 0x43, 0x49, 0x44, 0x0C, 0x41, 0xD0, 0x0A, 0x03, 0x08, 0x5F, 0x41, 0x44, 0x52, 0x00, 0x08, 0x5F, 0x53, 0x45, 0x47, 0x00, 0x08, 0x5F, 0x55, 0x49, 0x44, 0x00, 0x08, 0x53, 0x55, 0x50, 0x50, 0x00, 0x10, 0x41, 0x0B, 0x2E, 0x5F, 0x53, 0x42, 0x5F, 0x50, 0x43, 0x49, 0x30, 0x08, 0x5F, 0x43, 0x52, 0x53, 0x11, 0x40, 0x0A, 0x0A, 0x9C, 0x88, 0x0D, 0x00, 0x02, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x01, 0x47, 0x01, 0xF8, 0x0C, 0xF8, 0x0C, 0x01, 0x08, 0x88, 0x0D, 0x00, 0x01, 0x0C, 0x03, 0x00, 0x00, 0x00, 0x00, 0xF7, 0x0C, 0x00, 0x00, 0xF8, 0x0C, 0x88, 0x0D, 0x00, 0x01, 0x0C, 0x03, 0x00, 0x00, 0x00, 0x0D, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xF3, 0x87, 0x17, 0x00, 0x00, 0x0C, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0xFF, 0xFF, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x87, 0x17, 0x00, 0x00, 0x0C, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xFF, 0xFF, 0xBF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x3E, 0x8A, 0x2B, 0x00, 0x00, 0x0C, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x79, 0x00, ]; /* Device (_SB.COM1) { Name (_HID, EisaId ("PNP0501") /* 16550A-compatible COM Serial Port */) // _HID: Hardware ID Name (_UID, Zero) // _UID: Unique ID Name (_CRS, ResourceTemplate () // _CRS: Current Resource Settings { Interrupt (ResourceConsumer, Edge, ActiveHigh, Exclusive, ,, ) { 0x00000004, } IO (Decode16, 0x03F8, // Range Minimum 0x03F8, // Range Maximum 0x00, // Alignment 0x08, // Length ) }) } */ let com1_dsdt_data = [ 0x5Bu8, 0x82, 0x36, 0x2E, 0x5F, 0x53, 0x42, 0x5F, 0x43, 0x4F, 0x4D, 0x31, 0x08, 0x5F, 0x48, 0x49, 0x44, 0x0C, 0x41, 0xD0, 0x05, 0x01, 0x08, 0x5F, 0x55, 0x49, 0x44, 0x00, 0x08, 0x5F, 0x43, 0x52, 0x53, 0x11, 0x16, 0x0A, 0x13, 0x89, 0x06, 0x00, 0x03, 0x01, 0x04, 0x00, 0x00, 0x00, 0x47, 0x01, 0xF8, 0x03, 0xF8, 0x03, 0x00, 0x08, 0x79, 0x00, ]; /* Name (\_S5, Package (0x01) // _S5_: S5 System State { 0x05 }) */ let s5_sleep_data = [0x08u8, 0x5F, 0x53, 0x35, 0x5F, 0x12, 0x04, 0x01, 0x0A, 0x05]; // DSDT let mut dsdt = SDT::new(*b"DSDT", 36, 6, *b"CLOUDH", *b"CHDSDT ", 1); dsdt.append(pci_dsdt_data); if serial_enabled { dsdt.append(com1_dsdt_data); } dsdt.append(s5_sleep_data); dsdt } pub fn create_acpi_tables( guest_mem: &GuestMemoryMmap, num_cpus: u8, serial_enabled: bool, ) -> GuestAddress { // RSDP is at the EBDA let rsdp_offset = super::EBDA_START; let mut tables: Vec = Vec::new(); // DSDT let dsdt = create_dsdt_table(serial_enabled); let dsdt_offset = rsdp_offset.checked_add(RSDP::len() as u64).unwrap(); guest_mem .write_slice(dsdt.as_slice(), dsdt_offset) .expect("Error writing DSDT table"); // FACP aka FADT // Revision 6 of the ACPI FADT table is 276 bytes long let mut facp = SDT::new(*b"FACP", 276, 6, *b"CLOUDH", *b"CHFACP ", 1); // HW_REDUCED_ACPI and RESET_REG_SUP let fadt_flags: u32 = 1 << 20 | 1 << 10; facp.write(112, fadt_flags); // RESET_REG facp.write(116, GenericAddress::io_port_address(0x3c0)); // RESET_VALUE facp.write(128, 1u8); facp.write(131, 3u8); // FADT minor version facp.write(140, dsdt_offset.0); // X_DSDT // SLEEP_CONTROL_REG facp.write(244, GenericAddress::io_port_address(0x3c0)); // SLEEP_STATUS_REG facp.write(256, GenericAddress::io_port_address(0x3c0)); facp.write(268, b"CLOUDHYP"); // Hypervisor Vendor Identity facp.update_checksum(); let facp_offset = dsdt_offset.checked_add(dsdt.len() as u64).unwrap(); guest_mem .write_slice(facp.as_slice(), facp_offset) .expect("Error writing FACP table"); tables.push(facp_offset.0); // MADT let mut madt = SDT::new(*b"APIC", 44, 5, *b"CLOUDH", *b"CHMADT ", 1); madt.write(36, super::mptable::APIC_DEFAULT_PHYS_BASE); for cpu in 0..num_cpus { let lapic = LocalAPIC { r#type: 0, length: 8, processor_id: cpu, apic_id: cpu, flags: 1, }; madt.append(lapic); } madt.append(IOAPIC { r#type: 1, length: 12, ioapic_id: 0, apic_address: super::mptable::IO_APIC_DEFAULT_PHYS_BASE, gsi_base: 0, ..Default::default() }); madt.append(InterruptSourceOverride { r#type: 2, length: 10, bus: 0, source: 4, gsi: 4, flags: 0, }); let madt_offset = facp_offset.checked_add(facp.len() as u64).unwrap(); guest_mem .write_slice(madt.as_slice(), madt_offset) .expect("Error writing MADT table"); tables.push(madt_offset.0); // MCFG let mut mcfg = SDT::new(*b"MCFG", 36, 1, *b"CLOUDH", *b"CHMCFG ", 1); // MCFG reserved 8 bytes mcfg.append(0u64); // 32-bit PCI enhanced configuration mechanism mcfg.append(PCIRangeEntry { base_address: super::MEM_32BIT_DEVICES_GAP_SIZE, segment: 0, start: 0, end: 0xff, ..Default::default() }); let mcfg_offset = madt_offset.checked_add(madt.len() as u64).unwrap(); guest_mem .write_slice(mcfg.as_slice(), mcfg_offset) .expect("Error writing MCFG table"); tables.push(mcfg_offset.0); // XSDT let mut xsdt = SDT::new(*b"XSDT", 36, 1, *b"CLOUDH", *b"CHXSDT ", 1); for table in tables { xsdt.append(table); } xsdt.update_checksum(); let xsdt_offset = mcfg_offset.checked_add(mcfg.len() as u64).unwrap(); guest_mem .write_slice(xsdt.as_slice(), xsdt_offset) .expect("Error writing XSDT table"); // RSDP let rsdp = RSDP::new(*b"CLOUDH", xsdt_offset.0); guest_mem .write_slice(rsdp.as_slice(), rsdp_offset) .expect("Error writing RSDP"); rsdp_offset }