zephyr/arch/x86/core/acpi.c
Andy Ross 7fe8caebc0 arch/x86: Add z_acpi_find_table(), MCFG support
The existing minimal ACPI implementation was enough to find the MADT
table for dumping CPU info.  Enhance it with a slightly less minimal
implementation that can fetch any table, supports the ACPI 2.0 XSDT
directory (technically required on 64 bit systems so tables can live
>4G) and provides definitions for the MCFG table with the PCI
configuration pointers.

Note that there is no use case right now for high performance table
searching, so the "init" step has been removed and tables are probed
independently from scratch for each one requested (there are only
two).

Note also that the memory to which these tables point is not
understood by the Zephyr MMU configuration, so in long mode all ACPI
calls have to be done very early, before z_x86_paging_init() (or on a
build with the MMU initialization disabled).

Signed-off-by: Andy Ross <andrew.j.ross@intel.com>
2020-06-23 13:07:39 +02:00

147 lines
2.9 KiB
C

/*
* Copyright (c) 2020 Intel Corporation
* SPDX-License-Identifier: Apache-2.0
*/
#include <kernel.h>
#include <arch/x86/acpi.h>
struct acpi_rsdp {
char sig[8];
uint8_t csum;
char oemid[6];
uint8_t rev;
uint32_t rsdt_ptr;
uint32_t len;
uint64_t xsdt_ptr;
uint8_t ext_csum;
uint8_t _rsvd[3];
} __packed;
struct acpi_rsdt {
struct acpi_sdt sdt;
uint32_t table_ptrs[];
} __packed;
struct acpi_xsdt {
struct acpi_sdt sdt;
uint64_t table_ptrs[];
} __packed;
static bool check_sum(struct acpi_sdt *t)
{
uint8_t sum = 0, *p = (uint8_t *)t;
for (int i = 0; i < t->len; i++) {
sum += p[i];
}
return sum == 0;
}
static struct acpi_rsdp *find_rsdp(void)
{
uint64_t magic = 0x2052545020445352; /* == "RSD PTR " */
/* Physical (real mode!) address 0000:040e stores a (real
* mode!!) segment descriptor pointing to the 1kb Extended
* BIOS Data Area. Look there first.
*/
uint64_t *search = (void *)(long)(((int)*(uint16_t *)0x040eL) << 4);
for (int i = 0; i < 1024/8; i++) {
if (search[i] == magic) {
return (void *)&search[i];
}
}
/* If it's not there, then look for it in the last 128kb of
* real mode memory.
*/
search = (uint64_t *)0xe0000;
for (int i = 0; i < 128*1024/8; i++) {
if (search[i] == magic) {
return (void *)&search[i];
}
}
/* Now we're supposed to look in the UEFI system table, which
* is passed as a function argument to the bootloader and long
* forgotten by now...
*/
return NULL;
}
void *z_acpi_find_table(uint32_t signature)
{
struct acpi_rsdp *rsdp = find_rsdp();
if (!rsdp) {
return NULL;
}
struct acpi_rsdt *rsdt = (void *)(long)rsdp->rsdt_ptr;
if (rsdt && check_sum(&rsdt->sdt)) {
uint32_t *end = (uint32_t *)((char *)rsdt + rsdt->sdt.len);
for (uint32_t *tp = &rsdt->table_ptrs[0]; tp < end; tp++) {
struct acpi_sdt *t = (void *)(long)*tp;
if (t->sig == signature && check_sum(t)) {
return t;
}
}
}
if (rsdp->rev < 2) {
return NULL;
}
struct acpi_xsdt *xsdt = (void *)(long)rsdp->xsdt_ptr;
if (xsdt && check_sum(&xsdt->sdt)) {
uint64_t *end = (uint64_t *)((char *)xsdt + xsdt->sdt.len);
for (uint64_t *tp = &xsdt->table_ptrs[0]; tp < end; tp++) {
struct acpi_sdt *t = (void *)(long)*tp;
if (t->sig == signature && check_sum(t)) {
return t;
}
}
}
return NULL;
}
/*
* Return the 'n'th CPU entry from the ACPI MADT, or NULL if not available.
*/
struct acpi_cpu *z_acpi_get_cpu(int n)
{
struct acpi_madt *madt = z_acpi_find_table(ACPI_MADT_SIGNATURE);
uintptr_t base = POINTER_TO_UINT(madt);
uintptr_t offset;
if (madt) {
offset = POINTER_TO_UINT(madt->entries) - base;
while (offset < madt->sdt.len) {
struct acpi_madt_entry *entry;
entry = (struct acpi_madt_entry *) (offset + base);
if (entry->type == ACPI_MADT_ENTRY_CPU) {
if (n) {
--n;
} else {
return (struct acpi_cpu *) entry;
}
}
offset += entry->length;
}
}
return NULL;
}