summary refs log tree commit diff
path: root/x86_64/src/acpi.rs
diff options
context:
space:
mode:
Diffstat (limited to 'x86_64/src/acpi.rs')
-rw-r--r--x86_64/src/acpi.rs212
1 files changed, 212 insertions, 0 deletions
diff --git a/x86_64/src/acpi.rs b/x86_64/src/acpi.rs
new file mode 100644
index 0000000..7030bd8
--- /dev/null
+++ b/x86_64/src/acpi.rs
@@ -0,0 +1,212 @@
+// Copyright 2020 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+use acpi_tables::{rsdp::RSDP, sdt::SDT};
+use data_model::DataInit;
+use sys_util::{GuestAddress, GuestMemory};
+
+#[repr(C)]
+#[derive(Clone, Copy, Default)]
+struct LocalAPIC {
+    _type: u8,
+    _length: u8,
+    _processor_id: u8,
+    _apic_id: u8,
+    _flags: u32,
+}
+
+// Safe as LocalAPIC structure only contains raw data
+unsafe impl DataInit for LocalAPIC {}
+
+#[repr(C)]
+#[derive(Clone, Copy, Default)]
+struct IOAPIC {
+    _type: u8,
+    _length: u8,
+    _ioapic_id: u8,
+    _reserved: u8,
+    _apic_address: u32,
+    _gsi_base: u32,
+}
+
+// Safe as IOAPIC structure only contains raw data
+unsafe impl DataInit for IOAPIC {}
+
+const OEM_REVISION: u32 = 1;
+//DSDT
+const DSDT_REVISION: u8 = 6;
+// FADT
+const FADT_LEN: u32 = 276;
+const FADT_REVISION: u8 = 6;
+const FADT_MINOR_REVISION: u8 = 3;
+// FADT flags
+const FADT_POWER_BUTTON: u32 = (1 << 4);
+const FADT_SLEEP_BUTTON: u32 = (1 << 5);
+// FADT fields offset
+const FADT_FIELD_SCI_INTERRUPT: usize = 46;
+const FADT_FIELD_PM1A_EVENT_BLK_ADDR: usize = 56;
+const FADT_FIELD_PM1A_CONTROL_BLK_ADDR: usize = 64;
+const FADT_FIELD_PM1A_EVENT_BLK_LEN: usize = 88;
+const FADT_FIELD_PM1A_CONTROL_BLK_LEN: usize = 89;
+const FADT_FIELD_FLAGS: usize = 112;
+const FADT_FIELD_MINOR_REVISION: usize = 131;
+const FADT_FIELD_DSDT_ADDR: usize = 140;
+const FADT_FIELD_HYPERVISOR_ID: usize = 268;
+// MADT
+const MADT_LEN: u32 = 44;
+const MADT_REVISION: u8 = 5;
+// MADT fields offset
+const MADT_FIELD_LAPIC_ADDR: usize = 36;
+// MADT types
+const MADT_TYPE_LOCAL_APIC: u8 = 0;
+const MADT_TYPE_IO_APIC: u8 = 1;
+// MADT flags
+const MADT_ENABLED: u32 = 1;
+// XSDT
+const XSDT_REVISION: u8 = 1;
+
+/// Create ACPI tables and return the RSDP.
+/// The basic tables DSDT/FACP/MADT/XSDT are constructed in this function.
+/// # Arguments
+///
+/// * `guest_mem` - The guest memory where the tables will be stored.
+/// * `num_cpus` - Used to construct the MADT.
+/// * `sci_irq` - Used to fill the FACP SCI_INTERRUPT field, which
+///               is going to be used by the ACPI drivers to register
+///               sci handler.
+pub fn create_acpi_tables(guest_mem: &GuestMemory, num_cpus: u8, sci_irq: u32) -> GuestAddress {
+    // RSDP is at the HI RSDP WINDOW
+    let rsdp_offset = GuestAddress(super::ACPI_HI_RSDP_WINDOW_BASE);
+    let mut tables: Vec<u64> = Vec::new();
+
+    // DSDT
+    let dsdt = SDT::new(
+        *b"DSDT",
+        acpi_tables::HEADER_LEN,
+        DSDT_REVISION,
+        *b"CROSVM",
+        *b"CROSVMDT",
+        OEM_REVISION,
+    );
+    let dsdt_offset = rsdp_offset.checked_add(RSDP::len() as u64).unwrap();
+    guest_mem
+        .write_at_addr(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",
+        FADT_LEN,
+        FADT_REVISION,
+        *b"CROSVM",
+        *b"CROSVMDT",
+        OEM_REVISION,
+    );
+
+    let fadt_flags: u32 = FADT_POWER_BUTTON | FADT_SLEEP_BUTTON; // mask POWER and SLEEP BUTTON
+    facp.write(FADT_FIELD_FLAGS, fadt_flags);
+
+    // SCI Interrupt
+    facp.write(FADT_FIELD_SCI_INTERRUPT, sci_irq as u16);
+
+    // PM1A Event Block Address
+    facp.write(
+        FADT_FIELD_PM1A_EVENT_BLK_ADDR,
+        devices::acpi::ACPIPM_RESOURCE_BASE as u32,
+    );
+
+    // PM1A Control Block Address
+    facp.write(
+        FADT_FIELD_PM1A_CONTROL_BLK_ADDR,
+        devices::acpi::ACPIPM_RESOURCE_BASE as u32
+            + devices::acpi::ACPIPM_RESOURCE_EVENTBLK_LEN as u32,
+    );
+
+    // PM1 Event Block Length
+    facp.write(
+        FADT_FIELD_PM1A_EVENT_BLK_LEN,
+        devices::acpi::ACPIPM_RESOURCE_EVENTBLK_LEN as u8,
+    );
+
+    // PM1 Control Block Length
+    facp.write(
+        FADT_FIELD_PM1A_CONTROL_BLK_LEN,
+        devices::acpi::ACPIPM_RESOURCE_CONTROLBLK_LEN as u8,
+    );
+
+    facp.write(FADT_FIELD_MINOR_REVISION, FADT_MINOR_REVISION); // FADT minor version
+    facp.write(FADT_FIELD_DSDT_ADDR, dsdt_offset.0 as u64); // X_DSDT
+
+    facp.write(FADT_FIELD_HYPERVISOR_ID, *b"CROSVM"); // Hypervisor Vendor Identity
+
+    let facp_offset = dsdt_offset.checked_add(dsdt.len() as u64).unwrap();
+    guest_mem
+        .write_at_addr(facp.as_slice(), facp_offset)
+        .expect("Error writing FACP table");
+    tables.push(facp_offset.0);
+
+    // MADT
+    let mut madt = SDT::new(
+        *b"APIC",
+        MADT_LEN,
+        MADT_REVISION,
+        *b"CROSVM",
+        *b"CROSVMDT",
+        OEM_REVISION,
+    );
+    madt.write(
+        MADT_FIELD_LAPIC_ADDR,
+        super::mptable::APIC_DEFAULT_PHYS_BASE as u32,
+    );
+
+    for cpu in 0..num_cpus {
+        let lapic = LocalAPIC {
+            _type: MADT_TYPE_LOCAL_APIC,
+            _length: std::mem::size_of::<LocalAPIC>() as u8,
+            _processor_id: cpu,
+            _apic_id: cpu,
+            _flags: MADT_ENABLED,
+        };
+        madt.append(lapic);
+    }
+
+    madt.append(IOAPIC {
+        _type: MADT_TYPE_IO_APIC,
+        _length: std::mem::size_of::<IOAPIC>() as u8,
+        _apic_address: super::mptable::IO_APIC_DEFAULT_PHYS_BASE,
+        ..Default::default()
+    });
+
+    let madt_offset = facp_offset.checked_add(facp.len() as u64).unwrap();
+    guest_mem
+        .write_at_addr(madt.as_slice(), madt_offset)
+        .expect("Error writing MADT table");
+    tables.push(madt_offset.0);
+
+    // XSDT
+    let mut xsdt = SDT::new(
+        *b"XSDT",
+        acpi_tables::HEADER_LEN,
+        XSDT_REVISION,
+        *b"CROSVM",
+        *b"CROSVMDT",
+        OEM_REVISION,
+    );
+    for table in tables {
+        xsdt.append(table);
+    }
+
+    let xsdt_offset = madt_offset.checked_add(madt.len() as u64).unwrap();
+    guest_mem
+        .write_at_addr(xsdt.as_slice(), xsdt_offset)
+        .expect("Error writing XSDT table");
+
+    // RSDP
+    let rsdp = RSDP::new(*b"CROSVM", xsdt_offset.0);
+    guest_mem
+        .write_at_addr(rsdp.as_slice(), rsdp_offset)
+        .expect("Error writing RSDP");
+
+    rsdp_offset
+}