blob: 7aff0893b60dd4715667d4ed3e9a4a4b6073171f [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SRC_FIRMWARE_GIGABOOT_CPP_ACPI_H_
#define SRC_FIRMWARE_GIGABOOT_CPP_ACPI_H_
#include <lib/zbi-format/cpu.h>
#include <lib/zbi-format/driver-config.h>
#include <array>
#include <optional>
#include <span>
#include <variant>
#include <efi/types.h>
namespace gigaboot {
// ACPI signatures
using AcpiTableSignature = std::array<uint8_t, 4>;
constexpr AcpiTableSignature kXsdtSignature = {'X', 'S', 'D', 'T'};
constexpr AcpiTableSignature kRsdtSignature = {'R', 'S', 'D', 'T'};
// ACPI constants
constexpr uint8_t kPsciCompliant = 0x1;
constexpr uint8_t kPsciUseHvc = 0x2;
// The ARM GICv3 spec states that 0x20000 is the default GICR stride.
constexpr uint64_t kGicv3rDefaultStride = 0x20000;
// System Description Table.
// Parent structure for all SDT tables.
struct __attribute__((packed)) SdtHeader {
AcpiTableSignature signature;
uint32_t length;
uint8_t revision;
uint8_t checksum;
uint8_t oem_id[6];
uint8_t oem_table_id[8];
uint32_t oem_revision;
uint32_t creator_id;
uint32_t creator_revision;
};
static_assert(sizeof(SdtHeader) == 36, "System Description Table Header is the wrong size");
struct __attribute__((packed)) InterruptControllerHeader {
uint8_t type;
uint8_t length;
};
static_assert(sizeof(InterruptControllerHeader) == 2,
"Interrupt Controller Header is the wrong size");
// The Multiple APIC (Advanced Programmable Interrupt Controller) Description Table
// In memory, followed by a list of adjacent interrupt controllers.
struct __attribute__((packed)) AcpiMadt {
constexpr static AcpiTableSignature kSig = {'A', 'P', 'I', 'C'};
SdtHeader hdr;
uint32_t local_ic_address;
uint32_t flags;
class Controllers {
public:
class Iterator {
public:
using value_type = InterruptControllerHeader;
const value_type& operator*() const { return *m_ptr; }
const value_type* operator->() const { return m_ptr; }
Iterator operator++() {
m_ptr = reinterpret_cast<const value_type*>(reinterpret_cast<const uint8_t*>(m_ptr) +
m_ptr->length);
return *this;
}
Iterator operator++(int) {
Iterator tmp = *this;
++(*this);
return tmp;
}
friend bool operator==(const Iterator& a, const Iterator& b) { return a.m_ptr == b.m_ptr; }
friend bool operator!=(const Iterator& a, const Iterator& b) { return !(a == b); }
private:
friend Controllers;
explicit Iterator(const value_type* ptr) : m_ptr(ptr) {}
const value_type* m_ptr;
};
Iterator begin() const { return Iterator(base_); }
Iterator end() const {
return Iterator(reinterpret_cast<const InterruptControllerHeader*>(
reinterpret_cast<const uint8_t*>(base_) + length_));
}
private:
friend AcpiMadt;
Controllers(const InterruptControllerHeader* base, size_t length)
: base_(base), length_(length) {}
const InterruptControllerHeader* base_;
size_t length_;
};
// Return an iterator over the followup interrupt controllers after the MADT in memory.
Controllers controllers() const {
return {reinterpret_cast<const InterruptControllerHeader*>(this + 1),
hdr.length - sizeof(*this)};
}
// Given an out-parameter span of topology nodes,
// returns a span of CPU topology nodes.
// The 'nodes' param is the space provided for the return value.
std::span<zbi_topology_node_t> GetTopology(std::span<zbi_topology_node_t> nodes) const;
struct GicDescriptor {
std::variant<zbi_dcfg_arm_gic_v2_driver_t, zbi_dcfg_arm_gic_v3_driver_t> driver;
uint32_t zbi_type;
};
// Return a GIC driver config structure and associated ZBI type if available,
// returns std::nullopt if there is no GIC driver available.
std::optional<GicDescriptor> GetGicDriver() const;
};
static_assert(sizeof(AcpiMadt) == 44, "MADT is the wrong size");
// Generic Address Structure
// Used to define the position of registers.
struct __attribute__((packed)) AcpiGas {
uint8_t address_space_id;
uint8_t register_bit_width;
uint8_t register_bit_offset;
uint8_t access_size;
uint64_t address;
};
static_assert(sizeof(AcpiGas) == 12, "GAS is the wrong size");
// The Fixed ACPI Description Table
// Contains information about fixed registers.
struct __attribute__((packed)) AcpiFadt {
constexpr static AcpiTableSignature kSig = {'F', 'A', 'C', 'P'};
SdtHeader hdr;
uint32_t firmware_ctrl;
uint32_t dsdt;
uint8_t reserved;
uint8_t preferred_pm_profile;
uint16_t sci_int;
uint32_t smi_cmd;
uint8_t acpi_enable;
uint8_t acpi_disable;
uint8_t s4bios_req;
uint8_t pstate_cnt;
uint32_t pm1a_evt_blk;
uint32_t pm1b_evt_blk;
uint32_t pm1a_cnt_blk;
uint32_t pm1b_cnt_blk;
uint32_t pm2_cnt_blk;
uint32_t pm_tmr_blk;
uint32_t gpe0_blk;
uint32_t gpe1_blk;
uint8_t pm1_evt_len;
uint8_t pm1_cnt_len;
uint8_t pm2_cnt_len;
uint8_t pm_tmr_len;
uint8_t gpe0_blk_len;
uint8_t gpe1_blk_len;
uint8_t gpe1_base;
uint8_t cst_cnt;
uint16_t p_lvl2_lat;
uint16_t p_lvl3_lat;
uint16_t flush_size;
uint16_t flush_stride;
uint8_t duty_offset;
uint8_t duty_width;
uint8_t day_alrm;
uint8_t mon_alrm;
uint8_t century;
uint16_t iapc_boot_arch;
uint8_t reserved2;
uint32_t flags;
uint8_t reset_reg[12];
uint8_t reset_value;
uint16_t arm_boot_arch;
uint8_t fadt_minor_version;
uint64_t x_firmware_ctrl;
uint64_t x_dsdt;
AcpiGas x_pm1a_evt_blk;
AcpiGas x_pm1b_evt_blk;
AcpiGas x_pm1a_cnt_blk;
AcpiGas x_pm1b_cnt_blk;
AcpiGas x_pm2_cnt_blk;
AcpiGas x_pm_tmr_blk;
AcpiGas x_gpe0_blk;
AcpiGas x_gpe1_blk;
AcpiGas sleep_control_reg;
AcpiGas sleep_status_reg;
uint64_t hypervisor_vendory_identity;
// If this table is Power State Coordination Interface (PSCI) compliant,
// return a config structure for the PSCI driver.
std::optional<zbi_dcfg_arm_psci_driver_t> GetPsciDriver() const;
};
static_assert(sizeof(AcpiFadt) == 276, "FADT is the wrong size");
// Generic Timer Description Table
struct __attribute__((packed)) AcpiGtdt {
constexpr static AcpiTableSignature kSig = {'G', 'T', 'D', 'T'};
SdtHeader hdr;
uint64_t cnt_control_base;
uint32_t reserved;
uint32_t secure_el1_timer_gsiv;
uint32_t secure_el1_timer_flags;
uint32_t nonsecure_el1_timer_gsiv;
uint32_t nonsecure_el1_timer_flags;
uint32_t virtual_el1_timer_gsiv;
uint32_t virtual_el1_timer_flags;
uint32_t el2_timer_gsiv;
uint32_t el2_timer_flags;
uint64_t cnt_read_base;
uint32_t platform_timer_count;
uint32_t platform_timer_offset;
uint32_t virtual_el2_timer_gsiv;
uint32_t virtual_el2_timer_flags;
// Return a config structure for the generic timer.
zbi_dcfg_arm_generic_timer_driver_t GetTimer() const;
};
static_assert(sizeof(AcpiGtdt) == 104, "GTDT is the wrong size");
struct __attribute__((packed)) AcpiMadtGicInterface {
static constexpr uint8_t kType = 0xb;
InterruptControllerHeader hdr;
uint16_t reserved;
uint32_t cpu_interface_number;
uint32_t acpi_processor_uid;
uint32_t flags;
uint32_t parking_protocol_version;
uint32_t performance_interrupt_gsiv;
uint64_t parked_address;
uint64_t physical_base_address;
uint64_t gicv;
uint64_t gich;
uint32_t vgic_maintenance_interrupt;
uint64_t gicr_base_address;
uint64_t mpidr;
uint8_t processor_power_class;
uint8_t reserved2;
uint16_t spe_overflow_interrupt;
};
static_assert(sizeof(AcpiMadtGicInterface) == 80, "MADT GICC is the wrong size");
struct __attribute__((packed)) AcpiMadtGicDistributor {
static constexpr uint8_t kType = 0xc;
InterruptControllerHeader hdr;
uint16_t reserved;
uint32_t gic_id;
uint64_t physical_base_address;
uint32_t system_vector_base;
uint8_t gic_version;
uint8_t reserved2[3];
};
static_assert(sizeof(AcpiMadtGicDistributor) == 24, "MADT GICD is the wrong size");
// Gic Message Signaled Interrupts table
struct __attribute__((packed)) AcpiGicMsi {
static constexpr uint8_t kType = 0xd;
InterruptControllerHeader hdr;
uint16_t reserved;
uint32_t gic_msi_frame_id;
uint64_t physical_base_address;
uint32_t flags;
uint16_t spi_count;
uint16_t spi_base;
};
static_assert(sizeof(AcpiGicMsi) == 24, "MADT GIC MSI is the wrong size");
struct __attribute__((packed)) AcpiMadtGicRedistributor {
static constexpr uint8_t kType = 0xe;
InterruptControllerHeader hdr;
uint16_t reserved;
uint64_t discovery_range_base_address;
uint32_t discovery_range_length;
};
static_assert(sizeof(AcpiMadtGicRedistributor) == 16, "MADT GICR is the wrong size");
// Serial Port Console Redirection table
struct __attribute__((packed)) AcpiSpcr {
constexpr static AcpiTableSignature kSig = {'S', 'P', 'C', 'R'};
SdtHeader hdr;
uint8_t interface_type;
uint8_t reserved[3];
AcpiGas base_address;
uint8_t interrupt_type;
uint8_t irq;
uint32_t gsiv;
uint8_t baud_rate;
uint8_t parity;
uint8_t stop_bits;
uint8_t flow_control;
uint8_t terminal_type;
uint8_t language;
uint16_t pci_device_id;
uint16_t pci_vendor_id;
uint8_t pci_bus_number;
uint8_t pci_device_number;
uint8_t pci_function_number;
uint32_t pci_flags;
uint8_t pci_segment;
uint32_t uart_clock_frequency;
// Return a UART driver config structure.
zbi_dcfg_simple_t DeriveUartDriver() const;
// Returns a numeric constant describing the serial driver type.
// Returns 0 if the interface type is unsupported.
uint32_t GetKdrv() const;
};
static_assert(sizeof(AcpiSpcr) == 80, "SPCR is the wrong size");
// Given an interrupt controller header,
// check that its type field matches the type constant of IntCtrlType.
// If the field matches, reinterpret the header and (subsequent memory)
// as a controller of type IntCtrlType.
// Return nullptr if the type field does not match.
template <typename IntCtrlType>
const IntCtrlType* GetInterruptController(const InterruptControllerHeader& hdr) {
static_assert(offsetof(IntCtrlType, hdr) == 0, "Nonzero offset of interrupt controller header");
static_assert(std::is_same_v<decltype(IntCtrlType::hdr), InterruptControllerHeader>,
"Invalid type for interrupt controller header");
static_assert(std::is_same_v<decltype(IntCtrlType::kType), const uint8_t>,
"Invalid type constant for controller");
return (hdr.type == IntCtrlType::kType) ? reinterpret_cast<const IntCtrlType*>(&hdr) : nullptr;
}
constexpr std::array<uint8_t, 8> kAcpiRsdpSignature = {'R', 'S', 'D', ' ', 'P', 'T', 'R', ' '};
static_assert(sizeof(kAcpiRsdpSignature) == 8, "Unexpected size for RSDP signature constant");
// Root System Description Pointer
// If revision >= 1, check xsdt_address for the Extended System Description Table (XSDT).
// If revision == 0, check rsdt_address for the Root System Description Table (RSDT).
struct __attribute__((packed)) AcpiRsdp {
std::array<uint8_t, 8> signature;
uint8_t checksum;
uint8_t oem_id[6];
uint8_t revision;
uint32_t rsdt_address;
// Available in ACPI version 2.0.
uint32_t length;
uint64_t xsdt_address;
uint8_t extended_checksum;
uint8_t reserved[3];
// Load the given table type, if available.
template <typename Table>
const Table* LoadTable() const {
static_assert(offsetof(Table, hdr) == 0, "Nonzero offset of SDT header");
static_assert(std::is_same_v<decltype(Table::hdr), SdtHeader>, "Invalid type for SDT header");
return reinterpret_cast<const Table*>(LoadTableWithSignature(Table::kSig));
}
private:
// Load the table with the given signature, if available.
const SdtHeader* LoadTableWithSignature(AcpiTableSignature) const;
};
static_assert(sizeof(AcpiRsdp) == 36, "RSDP is the wrong size");
// Find and validate the pointer to the Root System Description Table.
const AcpiRsdp* FindAcpiRsdp();
constexpr size_t kAcpiRsdpV1Size = offsetof(AcpiRsdp, length);
constexpr efi_guid kAcpiTableGuid = ACPI_TABLE_GUID;
constexpr efi_guid kAcpi20TableGuid = ACPI_20_TABLE_GUID;
} // namespace gigaboot
#endif // SRC_FIRMWARE_GIGABOOT_CPP_ACPI_H_