blob: 55d899477ca3abc1d1ea948ea29b1d3326a0f9e8 [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.
#include "acpi.h"
#include <xefi.h>
#include <zircon/boot/driver-config.h>
#include <vector>
#include <gtest/gtest.h>
namespace {
#define ROUNDUP(size, align) (((size) + ((align)-1)) & ~((align)-1))
// Update the value of the given checksum.
void update_checksum(void *data, size_t size, uint8_t &checksum) {
checksum = 0;
uint8_t accum = 0;
uint8_t *buf = (uint8_t *)data;
for (size_t i = 0; i < size; i++) {
accum += buf[i];
}
checksum = (uint8_t)(0x100 - accum);
}
// EfiConfigTable assembles a valid efi_configuration table that contains
// an RSDP entry.
class EfiConfigTable {
public:
// A fake XSDT table structure that allows for 4 table entries.
struct __attribute__((packed)) fake_acpi_xsdt {
// We set up this padding so that the start of the entries array is 64-bit
// aligned, while also maintaining that the header and entries array are
// adjacent as ACPI expects.
uint8_t padding[4];
acpi_sdt_hdr_t hdr;
// We only test the extended system description table as the root system
// description table uses 32-bit physical addresses, which we can't test
// in our 64-bit host toolchain.
uint64_t entries[4];
};
fake_acpi_xsdt xsdt_;
acpi_rsdp_t rsdp_;
EfiConfigTable(uint8_t revision, uint8_t rsdp_position);
~EfiConfigTable();
void CorruptRsdpGuid();
void CorruptRsdpV1Checksum();
void CorruptRsdpV2Checksum();
void CorruptRsdpSignature();
void CorruptXsdtSignature();
void CorruptXsdtChecksum();
acpi_spcr_t *AddSpcrTable();
acpi_madt_t *AddMadtTable();
void AddInterruptControllerToMadt(acpi_madt_t *madt, void *controller, size_t size);
efi_configuration_table *RawTable();
private:
uint8_t rsdp_position_;
std::vector<efi_configuration_table> table_;
int AddPointerToXsdt(uint64_t addr);
};
EfiConfigTable::EfiConfigTable(uint8_t revision, uint8_t position) {
// Initialize the XSDT structure.
memset(&xsdt_, 0, sizeof(xsdt_));
memcpy(&xsdt_.hdr.signature, &kXsdtSignature, sizeof(kXsdtSignature));
xsdt_.hdr.revision = 1;
memset(xsdt_.entries, 0, sizeof(xsdt_.entries));
xsdt_.hdr.length = sizeof(acpi_sdt_hdr_t);
update_checksum(&xsdt_.hdr, xsdt_.hdr.length, xsdt_.hdr.checksum);
// Initialize the RSDP structure.
rsdp_position_ = position;
rsdp_ = acpi_rsdp_t{
.revision = revision,
};
memcpy(&rsdp_.signature, &kAcpiRsdpSignature, sizeof(kAcpiRsdpSignature));
update_checksum(&rsdp_, ACPI_RSDP_V1_SIZE, rsdp_.checksum);
if (revision >= 2) {
rsdp_.xsdt_address = (uint64_t)&xsdt_.hdr;
rsdp_.length = sizeof(acpi_rsdp_t);
update_checksum(&rsdp_, rsdp_.length, rsdp_.extended_checksum);
}
efi_guid guid = ACPI_TABLE_GUID;
if (revision >= 2) {
guid = ACPI_20_TABLE_GUID;
}
// Construct the EFI configuration table.
table_.resize(rsdp_position_);
table_.push_back(efi_configuration_table{
.VendorGuid = guid,
.VendorTable = &rsdp_,
});
}
EfiConfigTable::~EfiConfigTable() {
// Free up all of the memory used by the ACPI tables.
auto num_entries = (xsdt_.hdr.length - sizeof(acpi_sdt_hdr_t)) / sizeof(uint64_t);
for (size_t i = 0; i < num_entries; i++) {
delete[] (uint8_t *)xsdt_.entries[i];
}
}
void EfiConfigTable::CorruptRsdpGuid() { table_[rsdp_position_].VendorGuid.data1 ^= 0x1; }
void EfiConfigTable::CorruptRsdpSignature() {
rsdp_.signature ^= 0x1;
// The checksums should still be correct.
update_checksum(&rsdp_, ACPI_RSDP_V1_SIZE, rsdp_.checksum);
if (rsdp_.revision >= 2) {
update_checksum(&rsdp_, rsdp_.length, rsdp_.extended_checksum);
}
}
void EfiConfigTable::CorruptRsdpV1Checksum() {
rsdp_.checksum ^= 0x1;
// The v2 checksum, if present, should still be correct.
if (rsdp_.revision >= 2) {
update_checksum(&rsdp_, rsdp_.length, rsdp_.extended_checksum);
}
}
void EfiConfigTable::CorruptRsdpV2Checksum() { rsdp_.extended_checksum ^= 0x1; }
void EfiConfigTable::CorruptXsdtSignature() {
xsdt_.hdr.signature[0] ^= 1;
update_checksum(&xsdt_.hdr, xsdt_.hdr.length, xsdt_.hdr.checksum);
}
void EfiConfigTable::CorruptXsdtChecksum() { xsdt_.hdr.checksum ^= 1; }
void EfiConfigTable::AddInterruptControllerToMadt(acpi_madt_t *madt, void *controller,
size_t size) {
uint8_t *next_table_start = (uint8_t *)madt + madt->hdr.length;
madt->hdr.length += size;
memcpy(next_table_start, controller, size);
update_checksum(madt, madt->hdr.length, madt->hdr.checksum);
}
acpi_madt_t *EfiConfigTable::AddMadtTable() {
// We allocate extra space for the interrupt controller structures.
auto buffer = new uint8_t[512];
auto madt = (acpi_madt_t *)buffer;
madt->hdr.length = sizeof(acpi_madt_t);
memcpy(&madt->hdr.signature, kMadtSignature, sizeof(kMadtSignature));
update_checksum(madt, madt->hdr.length, madt->hdr.checksum);
if (AddPointerToXsdt((uint64_t)madt)) {
return nullptr;
}
return madt;
}
acpi_spcr_t *EfiConfigTable::AddSpcrTable() {
auto buffer = new uint8_t[sizeof(acpi_spcr_t)];
auto spcr = (acpi_spcr_t *)buffer;
spcr->hdr.length = sizeof(acpi_spcr_t);
memcpy(&spcr->hdr.signature, kSpcrSignature, sizeof(kSpcrSignature));
update_checksum(spcr, spcr->hdr.length, spcr->hdr.checksum);
if (AddPointerToXsdt((uint64_t)spcr)) {
return nullptr;
}
return spcr;
}
int EfiConfigTable::AddPointerToXsdt(uint64_t addr) {
auto num_entries = (xsdt_.hdr.length - sizeof(acpi_sdt_hdr_t)) / sizeof(uint64_t);
if (num_entries >= sizeof(xsdt_.entries) / sizeof(uint64_t)) {
return -1;
}
xsdt_.entries[num_entries] = addr;
xsdt_.hdr.length += sizeof(uint64_t);
update_checksum(&xsdt_.hdr, xsdt_.hdr.length, xsdt_.hdr.checksum);
return 0;
}
efi_configuration_table *EfiConfigTable::RawTable() { return &table_[0]; }
// Checks if the given topologies are equal. Returns 0 if equal, 1 if not.
// Currently only checks equality for ARM, as the x86 path isn't exercised.
void check_topology_eq(zbi_topology_node_t *got, zbi_topology_node_t *want) {
ASSERT_EQ(got->entity_type, want->entity_type);
ASSERT_EQ(got->parent_index, want->parent_index);
ASSERT_EQ(got->entity.processor.logical_id_count, want->entity.processor.logical_id_count);
for (uint8_t i = 0; i < got->entity.processor.logical_id_count; i++) {
ASSERT_EQ(got->entity.processor.logical_ids[i], want->entity.processor.logical_ids[i]);
}
ASSERT_EQ(got->entity.processor.flags, want->entity.processor.flags);
ASSERT_EQ(got->entity.processor.architecture, want->entity.processor.architecture);
zbi_topology_arm_info_t got_arm_info = got->entity.processor.architecture_info.arm;
zbi_topology_arm_info_t want_arm_info = want->entity.processor.architecture_info.arm;
ASSERT_EQ(got_arm_info.cluster_1_id, want_arm_info.cluster_1_id);
ASSERT_EQ(got_arm_info.cluster_2_id, want_arm_info.cluster_2_id);
ASSERT_EQ(got_arm_info.cluster_3_id, want_arm_info.cluster_3_id);
ASSERT_EQ(got_arm_info.cpu_id, want_arm_info.cpu_id);
ASSERT_EQ(got_arm_info.gic_id, want_arm_info.gic_id);
}
TEST(Acpi, RsdpMissing) {
auto efi_config_table = EfiConfigTable(1, 0);
efi_config_table.CorruptRsdpGuid();
EXPECT_EQ(load_acpi_rsdp(efi_config_table.RawTable(), 1), nullptr);
}
TEST(Acpi, RsdpBadSignature) {
auto efi_config_table = EfiConfigTable(1, 0);
efi_config_table.CorruptRsdpSignature();
EXPECT_EQ(load_acpi_rsdp(efi_config_table.RawTable(), 1), nullptr);
}
TEST(Acpi, RsdpBadV1Checksum) {
auto efi_config_table = EfiConfigTable(1, 0);
efi_config_table.CorruptRsdpV1Checksum();
EXPECT_EQ(load_acpi_rsdp(efi_config_table.RawTable(), 1), nullptr);
}
TEST(Acpi, RsdpV1Success) {
auto efi_config_table = EfiConfigTable(1, 0);
EXPECT_EQ(load_acpi_rsdp(efi_config_table.RawTable(), 1), &efi_config_table.rsdp_);
}
TEST(Acpi, RsdpBadV2Checksum) {
auto efi_config_table = EfiConfigTable(2, 0);
efi_config_table.CorruptRsdpV2Checksum();
EXPECT_EQ(load_acpi_rsdp(efi_config_table.RawTable(), 1), nullptr);
}
TEST(Acpi, RsdpV2Success) {
auto efi_config_table = EfiConfigTable(2, 0);
EXPECT_EQ(load_acpi_rsdp(efi_config_table.RawTable(), 1), &efi_config_table.rsdp_);
}
TEST(Acpi, RsdpAtEnd) {
auto efi_config_table = EfiConfigTable(2, 5);
EXPECT_EQ(load_acpi_rsdp(efi_config_table.RawTable(), 6), &efi_config_table.rsdp_);
}
TEST(Acpi, LoadBySignatureInvalidXsdtSignature) {
auto efi_config_table = EfiConfigTable(2, 0);
efi_config_table.AddSpcrTable();
efi_config_table.CorruptXsdtSignature();
EXPECT_EQ(load_table_with_signature(&efi_config_table.rsdp_, (uint8_t *)kSpcrSignature), nullptr);
}
TEST(Acpi, LoadBySignatureInvalidXsdtChecksum) {
auto efi_config_table = EfiConfigTable(2, 0);
efi_config_table.AddSpcrTable();
efi_config_table.CorruptXsdtChecksum();
EXPECT_EQ(load_table_with_signature(&efi_config_table.rsdp_, (uint8_t *)kSpcrSignature), nullptr);
}
TEST(Acpi, LoadBySignatureTableNotFound) {
auto efi_config_table = EfiConfigTable(2, 0);
EXPECT_NE(efi_config_table.AddMadtTable(), nullptr);
EXPECT_EQ(load_table_with_signature(&efi_config_table.rsdp_, (uint8_t *)kSpcrSignature), nullptr);
}
TEST(Acpi, LoadBySignatureInvalidTableChecksum) {
auto efi_config_table = EfiConfigTable(2, 0);
auto spcr = efi_config_table.AddSpcrTable();
EXPECT_NE(spcr, nullptr);
spcr->hdr.checksum ^= 1;
EXPECT_EQ(load_table_with_signature(&efi_config_table.rsdp_, (uint8_t *)kSpcrSignature), nullptr);
}
TEST(Acpi, LoadBySignatureSuccess) {
auto efi_config_table = EfiConfigTable(2, 0);
auto spcr = efi_config_table.AddSpcrTable();
EXPECT_NE(spcr, nullptr);
EXPECT_EQ(load_table_with_signature(&efi_config_table.rsdp_, (uint8_t *)kSpcrSignature),
&spcr->hdr);
}
TEST(Acpi, SpcrTypeToKdrvNullInput) { EXPECT_EQ(spcr_type_to_kdrv(nullptr), (uint32_t)0); }
TEST(Acpi, SpcrTypeToKdrvRevision1) {
acpi_spcr_t spcr = {
.hdr =
acpi_sdt_hdr_t{
.revision = 1,
},
};
EXPECT_EQ(spcr_type_to_kdrv(&spcr), (uint32_t)0);
}
TEST(Acpi, SpcrTypeToKdrvUnsupportedDevice) {
acpi_spcr_t spcr = {
.hdr =
acpi_sdt_hdr_t{
.revision = 3,
},
.interface_type = 0x0001,
};
EXPECT_EQ(spcr_type_to_kdrv(&spcr), (uint32_t)0);
}
TEST(Acpi, SpcrTypeToKdrvSuccess) {
acpi_spcr_t spcr = {
.hdr =
acpi_sdt_hdr_t{
.revision = 3,
},
.interface_type = 0x0003,
};
EXPECT_EQ(spcr_type_to_kdrv(&spcr), (uint32_t)KDRV_PL011_UART);
}
TEST(Acpi, UartDriverFromSpcrIrq) {
acpi_spcr_t spcr = {
.base_address =
acpi_gas_t{
.address = 0x80000,
},
.interrupt_type = 0x1,
.irq = 33,
.gsiv = 48,
};
dcfg_simple_t uart_driver;
uart_driver_from_spcr(&spcr, &uart_driver);
EXPECT_EQ(uart_driver.mmio_phys, (uint32_t)0x80000);
EXPECT_EQ(uart_driver.irq, (uint32_t)33);
}
TEST(Acpi, UartDriverFromSpcrGsiv) {
acpi_spcr_t spcr = {
.base_address =
acpi_gas_t{
.address = 0x80000,
},
.interrupt_type = 0x10,
.irq = 33,
.gsiv = 48,
};
dcfg_simple_t uart_driver;
uart_driver_from_spcr(&spcr, &uart_driver);
EXPECT_EQ(uart_driver.mmio_phys, (uint32_t)0x80000);
EXPECT_EQ(uart_driver.irq, (uint32_t)48);
}
TEST(Acpi, TopologyFromMadtTooManyCpus) {
auto efi_config_table = EfiConfigTable(2, 0);
auto madt = efi_config_table.AddMadtTable();
EXPECT_NE(madt, nullptr);
// Construct a dual core system.
auto gicd = acpi_madt_gicd_t{
.type = kInterruptControllerTypeGicd,
.length = sizeof(acpi_madt_gicd_t),
};
efi_config_table.AddInterruptControllerToMadt(madt, &gicd, sizeof(gicd));
auto gicc1 = acpi_madt_gicc_t{
.type = kInterruptControllerTypeGicc,
.length = sizeof(acpi_madt_gicc_t),
.cpu_interface_number = 0xf,
.mpidr = 0x4000030201,
};
efi_config_table.AddInterruptControllerToMadt(madt, &gicc1, sizeof(gicc1));
auto gicc2 = acpi_madt_gicc_t{
.type = kInterruptControllerTypeGicc,
.length = sizeof(acpi_madt_gicc_t),
.cpu_interface_number = 0x8,
.mpidr = 0x26001a0703,
};
efi_config_table.AddInterruptControllerToMadt(madt, &gicc2, sizeof(gicc2));
// Parse the CPU topology.
zbi_topology_node_t nodes[1];
auto num_nodes = topology_from_madt(madt, nodes, 1);
EXPECT_EQ(num_nodes, 1);
zbi_topology_node_t expected[1] = {
{
.entity_type = ZBI_TOPOLOGY_ENTITY_PROCESSOR,
.parent_index = ZBI_TOPOLOGY_NO_PARENT,
.entity =
{
.processor =
{
.logical_ids = {0},
.logical_id_count = 1,
.flags = ZBI_TOPOLOGY_PROCESSOR_PRIMARY,
.architecture = ZBI_TOPOLOGY_ARCH_ARM,
.architecture_info =
{
.arm =
{
.cluster_1_id = 0x2,
.cluster_2_id = 0x3,
.cluster_3_id = 0x40,
.cpu_id = 0x1,
.gic_id = 0xf,
},
},
},
},
},
};
check_topology_eq(&expected[0], &nodes[0]);
}
TEST(Acpi, TopologyFromMadtSuccess) {
auto efi_config_table = EfiConfigTable(2, 0);
auto madt = (acpi_madt_t *)efi_config_table.AddMadtTable();
EXPECT_NE(madt, nullptr);
// Construct a dual core system.
auto gicd = acpi_madt_gicd_t{
.type = kInterruptControllerTypeGicd,
.length = sizeof(acpi_madt_gicd_t),
};
efi_config_table.AddInterruptControllerToMadt(madt, &gicd, sizeof(gicd));
auto gicc1 = acpi_madt_gicc_t{
.type = kInterruptControllerTypeGicc,
.length = sizeof(acpi_madt_gicc_t),
.cpu_interface_number = 0xf,
.mpidr = 0x4000030201,
};
efi_config_table.AddInterruptControllerToMadt(madt, &gicc1, sizeof(gicc1));
auto gicc2 = acpi_madt_gicc_t{
.type = kInterruptControllerTypeGicc,
.length = sizeof(acpi_madt_gicc_t),
.cpu_interface_number = 0x8,
.mpidr = 0x26001a0703,
};
efi_config_table.AddInterruptControllerToMadt(madt, &gicc2, sizeof(gicc2));
// Parse the CPU topology from that MADT.
const uint8_t expected_num_nodes = 2;
zbi_topology_node_t nodes[expected_num_nodes];
auto num_nodes = topology_from_madt(madt, nodes, expected_num_nodes);
EXPECT_EQ(num_nodes, expected_num_nodes);
zbi_topology_node_t expected[expected_num_nodes] = {
{
.entity_type = ZBI_TOPOLOGY_ENTITY_PROCESSOR,
.parent_index = ZBI_TOPOLOGY_NO_PARENT,
.entity =
{
.processor =
{
.logical_ids = {0},
.logical_id_count = 1,
.flags = ZBI_TOPOLOGY_PROCESSOR_PRIMARY,
.architecture = ZBI_TOPOLOGY_ARCH_ARM,
.architecture_info =
{
.arm =
{
.cluster_1_id = 0x2,
.cluster_2_id = 0x3,
.cluster_3_id = 0x40,
.cpu_id = 0x1,
.gic_id = 0xf,
},
},
},
},
},
{
.entity_type = ZBI_TOPOLOGY_ENTITY_PROCESSOR,
.parent_index = ZBI_TOPOLOGY_NO_PARENT,
.entity =
{
.processor =
{
.logical_ids = {1},
.logical_id_count = 1,
.flags = 0,
.architecture = ZBI_TOPOLOGY_ARCH_ARM,
.architecture_info =
{
.arm =
{
.cluster_1_id = 0x7,
.cluster_2_id = 0x1a,
.cluster_3_id = 0x26,
.cpu_id = 0x3,
.gic_id = 0x8,
},
},
},
},
},
};
for (int i = 0; i < expected_num_nodes; i++) {
check_topology_eq(&expected[i], &nodes[i]);
}
}
TEST(Acpi, GicDriverFromMadtNoGicd) {
auto efi_config_table = EfiConfigTable(2, 0);
auto madt = (acpi_madt_t *)efi_config_table.AddMadtTable();
EXPECT_NE(madt, nullptr);
auto gicc1 = acpi_madt_gicc_t{
.type = kInterruptControllerTypeGicc,
.length = sizeof(acpi_madt_gicc_t),
.cpu_interface_number = 0xf,
.mpidr = 0x4000030201,
};
efi_config_table.AddInterruptControllerToMadt(madt, &gicc1, sizeof(gicc1));
dcfg_arm_gicv2_driver_t v2;
dcfg_arm_gicv3_driver_t v3;
EXPECT_EQ(gic_driver_from_madt(madt, &v2, &v3), 0);
}
TEST(Acpi, GicDriverFromMadtV2NoGicc) {
auto efi_config_table = EfiConfigTable(2, 0);
auto madt = (acpi_madt_t *)efi_config_table.AddMadtTable();
EXPECT_NE(madt, nullptr);
auto gicd = acpi_madt_gicd_t{
.type = kInterruptControllerTypeGicd,
.length = sizeof(acpi_madt_gicd_t),
.gic_version = 0x2,
};
efi_config_table.AddInterruptControllerToMadt(madt, &gicd, sizeof(gicd));
auto gic_msi = acpi_madt_gic_msi_t{
.type = kInterruptControllerTypeGicMsiFrame,
.length = sizeof(acpi_madt_gic_msi_t),
.physical_base_address = 0x40000,
};
efi_config_table.AddInterruptControllerToMadt(madt, &gic_msi, sizeof(gic_msi));
dcfg_arm_gicv2_driver_t v2;
dcfg_arm_gicv3_driver_t v3;
EXPECT_EQ(gic_driver_from_madt(madt, &v2, &v3), 0);
}
TEST(Acpi, GicDriverFromMadtV2NoGicMsi) {
auto efi_config_table = EfiConfigTable(2, 0);
auto madt = (acpi_madt_t *)efi_config_table.AddMadtTable();
EXPECT_NE(madt, nullptr);
auto gicd = acpi_madt_gicd_t{
.type = kInterruptControllerTypeGicd,
.length = sizeof(acpi_madt_gicd_t),
.physical_base_address = 0x30000,
.gic_version = 0x2,
};
efi_config_table.AddInterruptControllerToMadt(madt, &gicd, sizeof(gicd));
auto gicc = acpi_madt_gicc_t{
.type = kInterruptControllerTypeGicc,
.length = sizeof(acpi_madt_gicc_t),
.cpu_interface_number = 0xf,
.physical_base_address = 0x10000,
.mpidr = 0x4000030201,
};
efi_config_table.AddInterruptControllerToMadt(madt, &gicc, sizeof(gicc));
dcfg_arm_gicv2_driver_t v2;
dcfg_arm_gicv3_driver_t v3;
EXPECT_EQ(gic_driver_from_madt(madt, &v2, &v3), 2);
dcfg_arm_gicv2_driver_t expected = {
.mmio_phys = 0x10000,
.msi_frame_phys = 0x0,
.gicd_offset = 0x20000,
.gicc_offset = 0x0,
.ipi_base = 0,
.optional = true,
.use_msi = false,
};
ASSERT_EQ(memcmp(&expected, &v2, sizeof(expected)), 0);
}
TEST(Acpi, GicDriverFromMadtV2GiccBase) {
auto efi_config_table = EfiConfigTable(2, 0);
auto madt = (acpi_madt_t *)efi_config_table.AddMadtTable();
EXPECT_NE(madt, nullptr);
auto gicc = acpi_madt_gicc_t{
.type = kInterruptControllerTypeGicc,
.length = sizeof(acpi_madt_gicc_t),
.cpu_interface_number = 0xf,
.physical_base_address = 0x10000,
.mpidr = 0x4000030201,
};
efi_config_table.AddInterruptControllerToMadt(madt, &gicc, sizeof(gicc));
auto gicd = acpi_madt_gicd_t{
.type = kInterruptControllerTypeGicd,
.length = sizeof(acpi_madt_gicd_t),
.physical_base_address = 0x30000,
.gic_version = 0x2,
};
efi_config_table.AddInterruptControllerToMadt(madt, &gicd, sizeof(gicd));
auto gic_msi = acpi_madt_gic_msi_t{
.type = kInterruptControllerTypeGicMsiFrame,
.length = sizeof(acpi_madt_gic_msi_t),
.physical_base_address = 0x40000,
};
efi_config_table.AddInterruptControllerToMadt(madt, &gic_msi, sizeof(gic_msi));
dcfg_arm_gicv2_driver_t v2;
dcfg_arm_gicv3_driver_t v3;
EXPECT_EQ(gic_driver_from_madt(madt, &v2, &v3), 2);
dcfg_arm_gicv2_driver_t expected = {
.mmio_phys = 0x10000,
.msi_frame_phys = 0x40000,
.gicd_offset = 0x20000,
.gicc_offset = 0x0,
.ipi_base = 0,
.optional = true,
.use_msi = true,
};
ASSERT_EQ(memcmp(&expected, &v2, sizeof(dcfg_arm_gicv2_driver_t)), 0);
}
TEST(Acpi, GicDriverFromMadtV2GicdBase) {
auto efi_config_table = EfiConfigTable(2, 0);
auto madt = (acpi_madt_t *)efi_config_table.AddMadtTable();
EXPECT_NE(madt, nullptr);
auto gicc = acpi_madt_gicc_t{
.type = kInterruptControllerTypeGicc,
.length = sizeof(acpi_madt_gicc_t),
.cpu_interface_number = 0xf,
.physical_base_address = 0x30000,
.mpidr = 0x4000030201,
};
efi_config_table.AddInterruptControllerToMadt(madt, &gicc, sizeof(gicc));
auto gicd = acpi_madt_gicd_t{
.type = kInterruptControllerTypeGicd,
.length = sizeof(acpi_madt_gicd_t),
.physical_base_address = 0x20000,
.gic_version = 0x2,
};
efi_config_table.AddInterruptControllerToMadt(madt, &gicd, sizeof(gicd));
auto gic_msi = acpi_madt_gic_msi_t{
.type = kInterruptControllerTypeGicMsiFrame,
.length = sizeof(acpi_madt_gic_msi_t),
.physical_base_address = 0x40000,
};
efi_config_table.AddInterruptControllerToMadt(madt, &gic_msi, sizeof(gic_msi));
dcfg_arm_gicv2_driver_t v2;
dcfg_arm_gicv3_driver_t v3;
EXPECT_EQ(gic_driver_from_madt(madt, &v2, &v3), 2);
dcfg_arm_gicv2_driver_t expected = {
.mmio_phys = 0x20000,
.msi_frame_phys = 0x40000,
.gicd_offset = 0x0,
.gicc_offset = 0x10000,
.ipi_base = 0,
.optional = true,
.use_msi = true,
};
ASSERT_EQ(memcmp(&expected, &v2, sizeof(dcfg_arm_gicv2_driver_t)), 0);
}
TEST(Acpi, GicDriverFromMadtV3NoGicr) {
auto efi_config_table = EfiConfigTable(2, 0);
auto madt = (acpi_madt_t *)efi_config_table.AddMadtTable();
EXPECT_NE(madt, nullptr);
auto gicd = acpi_madt_gicd_t{
.type = kInterruptControllerTypeGicd,
.length = sizeof(acpi_madt_gicd_t),
.gic_version = 0x3,
};
efi_config_table.AddInterruptControllerToMadt(madt, &gicd, sizeof(gicd));
dcfg_arm_gicv2_driver_t v2;
dcfg_arm_gicv3_driver_t v3;
EXPECT_EQ(gic_driver_from_madt(madt, &v2, &v3), 0);
}
TEST(Acpi, GicDriverFromMadtV3GicdBase) {
auto efi_config_table = EfiConfigTable(2, 0);
auto madt = (acpi_madt_t *)efi_config_table.AddMadtTable();
EXPECT_NE(madt, nullptr);
auto gicd = acpi_madt_gicd_t{
.type = kInterruptControllerTypeGicd,
.length = sizeof(acpi_madt_gicd_t),
.physical_base_address = 0x20000,
.gic_version = 0x3,
};
efi_config_table.AddInterruptControllerToMadt(madt, &gicd, sizeof(gicd));
auto gicr = acpi_madt_gicr_t{
.type = kInterruptControllerTypeGicr,
.length = sizeof(acpi_madt_gicr_t),
.discovery_range_base_address = 0xf0000,
};
efi_config_table.AddInterruptControllerToMadt(madt, &gicr, sizeof(gicr));
dcfg_arm_gicv2_driver_t v2;
dcfg_arm_gicv3_driver_t v3;
EXPECT_EQ(gic_driver_from_madt(madt, &v2, &v3), 3);
dcfg_arm_gicv3_driver_t expected = {
.mmio_phys = 0x20000,
.gicd_offset = 0x0,
.gicr_offset = 0xd0000,
.gicr_stride = kGicv3rDefaultStride,
.ipi_base = 0,
.optional = true,
};
ASSERT_EQ(memcmp(&expected, &v3, sizeof(dcfg_arm_gicv3_driver_t)), 0);
}
TEST(Acpi, GicDriverFromMadtV3GicrBase) {
auto efi_config_table = EfiConfigTable(2, 0);
auto madt = (acpi_madt_t *)efi_config_table.AddMadtTable();
EXPECT_NE(madt, nullptr);
auto gicd = acpi_madt_gicd_t{
.type = kInterruptControllerTypeGicd,
.length = sizeof(acpi_madt_gicd_t),
.physical_base_address = 0x80000,
.gic_version = 0x3,
};
efi_config_table.AddInterruptControllerToMadt(madt, &gicd, sizeof(gicd));
auto gicr = acpi_madt_gicr_t{
.type = kInterruptControllerTypeGicr,
.length = sizeof(acpi_madt_gicr_t),
.discovery_range_base_address = 0x10000,
};
efi_config_table.AddInterruptControllerToMadt(madt, &gicr, sizeof(gicr));
dcfg_arm_gicv2_driver_t v2;
dcfg_arm_gicv3_driver_t v3;
EXPECT_EQ(gic_driver_from_madt(madt, &v2, &v3), 3);
dcfg_arm_gicv3_driver_t expected = {
.mmio_phys = 0x10000,
.gicd_offset = 0x70000,
.gicr_offset = 0x0,
.gicr_stride = kGicv3rDefaultStride,
.ipi_base = 0,
.optional = true,
};
ASSERT_EQ(memcmp(&expected, &v3, sizeof(dcfg_arm_gicv3_driver_t)), 0);
}
TEST(Acpi, PsciDriverFromFadtNotPsciCompliant) {
auto fadt = acpi_fadt_t{};
dcfg_arm_psci_driver_t cfg;
EXPECT_EQ(psci_driver_from_fadt(&fadt, &cfg), -1);
}
TEST(Acpi, PsciDriverFromFadtNoHvc) {
auto fadt = acpi_fadt_t{
.arm_boot_arch = (uint16_t)kPsciCompliant,
};
dcfg_arm_psci_driver_t cfg;
EXPECT_EQ(psci_driver_from_fadt(&fadt, &cfg), 0);
EXPECT_EQ(cfg.use_hvc, 0);
}
TEST(Acpi, PsciDriverFromFadtUseHvc) {
auto fadt = acpi_fadt_t{
.arm_boot_arch = (uint16_t)(kPsciCompliant | kPsciUseHvc),
};
dcfg_arm_psci_driver_t cfg;
EXPECT_EQ(psci_driver_from_fadt(&fadt, &cfg), 0);
EXPECT_NE(cfg.use_hvc, 0);
}
TEST(Acpi, TimerDriverFromGtdt) {
auto gtdt = acpi_gtdt_t{
.nonsecure_el1_timer_gsiv = 30,
.virtual_el1_timer_gsiv = 27,
};
dcfg_arm_generic_timer_driver_t timer;
timer_from_gtdt(&gtdt, &timer);
EXPECT_EQ(timer.irq_phys, (uint32_t)30);
EXPECT_EQ(timer.irq_virt, (uint32_t)27);
}
} // namespace