mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2024-10-01 11:05:46 +00:00
eb60106159
The starting length of the MCFG table was too long resulting in the kernel trying to get extra MCFG entries from the table that weren't there resulting in the following error message from the kernel: PCI: no memory for MCFG entries The MCFG table also has an 8 bytes of padding at the start before the table begins. Signed-off-by: Rob Bradford <robert.bradford@intel.com>
319 lines
11 KiB
Rust
319 lines
11 KiB
Rust
// 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 <dsdt.asl>"
|
|
|
|
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
|
|
0xFEC00000, // Range Maximum
|
|
0x00000000, // Translation Offset
|
|
0x3EC00001, // 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, 0x00, 0x00, 0xC0, 0xFE, 0x00, 0x00,
|
|
0x00, 0x00, 0x01, 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<u64> = 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
|
|
}
|