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>
This commit is contained in:
Andy Ross 2020-06-13 05:51:10 -07:00 committed by Carles Cufí
commit 7fe8caebc0
3 changed files with 112 additions and 118 deletions

View file

@ -1,103 +1,115 @@
/*
* Copyright (c) 2019 Intel Corporation
* Copyright (c) 2020 Intel Corporation
* SPDX-License-Identifier: Apache-2.0
*/
#include <kernel.h>
#include <arch/x86/acpi.h>
/*
* Finding and walking the ACPI tables can be time consuming, so we do
* it once, early, and then cache the "interesting" results for later.
*/
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;
static struct acpi_madt *madt;
struct acpi_rsdt {
struct acpi_sdt sdt;
uint32_t table_ptrs[];
} __packed;
/*
* ACPI structures use a simple checksum, such that
* summing all the bytes in the structure yields 0.
*/
struct acpi_xsdt {
struct acpi_sdt sdt;
uint64_t table_ptrs[];
} __packed;
static bool validate_checksum(void *buf, int len)
static bool check_sum(struct acpi_sdt *t)
{
uint8_t *cp = buf;
uint8_t checksum = 0;
uint8_t sum = 0, *p = (uint8_t *)t;
while (len--) {
checksum += *(cp++);
for (int i = 0; i < t->len; i++) {
sum += p[i];
}
return (checksum == 0);
return sum == 0;
}
/*
* Called very early during initialization to find ACPI tables of interest.
* First, we find the RDSP, and if found, use that to find the RSDT, which
* we then use to find the MADT. (This function is long, but easy to follow.)
*/
void z_acpi_init(void)
static struct acpi_rsdp *find_rsdp(void)
{
/*
* First, find the RSDP by probing "well-known" areas of memory.
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);
struct acpi_rsdp *rsdp = NULL;
for (int i = 0; i < 1024/8; i++) {
if (search[i] == magic) {
return (void *)&search[i];
}
}
static const struct {
uintptr_t base;
uintptr_t top;
} area[] = {
{ 0x000E0000, 0x00100000 }, /* BIOS ROM */
{ 0, 0 }
};
/* 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];
}
}
for (int i = 0; area[i].base && area[i].top && !rsdp; ++i) {
uintptr_t addr = area[i].base;
/* 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;
}
while (addr < area[i].top) {
struct acpi_rsdp *probe = UINT_TO_POINTER(addr);
void *z_acpi_find_table(uint32_t signature)
{
struct acpi_rsdp *rsdp = find_rsdp();
if ((probe->signature == ACPI_RSDP_SIGNATURE) &&
(validate_checksum(probe, sizeof(*probe)))) {
rsdp = probe;
break;
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;
}
addr += 0x10;
}
}
if (rsdp == NULL) {
return;
if (rsdp->rev < 2) {
return NULL;
}
/*
* Then, validate the RSDT fingered by the RSDP.
*/
struct acpi_xsdt *xsdt = (void *)(long)rsdp->xsdt_ptr;
struct acpi_rsdt *rsdt = UINT_TO_POINTER(rsdp->rsdt);
if ((rsdt->sdt.signature != ACPI_RSDT_SIGNATURE) ||
!validate_checksum(rsdt, rsdt->sdt.length)) {
rsdt = NULL;
return;
}
if (xsdt && check_sum(&xsdt->sdt)) {
uint64_t *end = (uint64_t *)((char *)xsdt + xsdt->sdt.len);
/*
* Finally, probe each SDT listed in the RSDT to find the MADT.
* If it's valid, then remember it for later.
*/
for (uint64_t *tp = &xsdt->table_ptrs[0]; tp < end; tp++) {
struct acpi_sdt *t = (void *)(long)*tp;
int nr_sdts = (rsdt->sdt.length - sizeof(rsdt)) / sizeof(uint32_t);
for (int i = 0; i < nr_sdts; ++i) {
struct acpi_sdt *sdt = UINT_TO_POINTER(rsdt->sdts[i]);
if ((sdt->signature == ACPI_MADT_SIGNATURE)
&& validate_checksum(sdt, sdt->length)) {
madt = (struct acpi_madt *) sdt;
break;
if (t->sig == signature && check_sum(t)) {
return t;
}
}
}
return NULL;
}
/*
@ -106,13 +118,14 @@ void z_acpi_init(void)
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.length) {
while (offset < madt->sdt.len) {
struct acpi_madt_entry *entry;
entry = (struct acpi_madt_entry *) (offset + base);

View file

@ -30,10 +30,6 @@ FUNC_NORETURN void z_x86_prep_c(void *arg)
ARG_UNUSED(info);
#endif
#ifdef CONFIG_ACPI
z_acpi_init();
#endif
#ifdef CONFIG_X86_MMU
z_x86_paging_init();
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2019 Intel Corporation
* Copyright (c) 2020 Intel Corporation
* SPDX-License-Identifier: Apache-2.0
*/
@ -8,52 +8,43 @@
#ifndef _ASMLANGUAGE
extern void z_acpi_init(void);
extern struct acpi_cpu *z_acpi_get_cpu(int);
/*
* The RSDP is a "floating" structure which simply points to the RSDT.
*/
#define ACPI_RSDP_SIGNATURE 0x2052545020445352 /* 'RSD PTR ' */
struct acpi_rsdp {
uint64_t signature; /* ACPI_RSDP_SIG */
uint8_t checksum;
uint8_t dontcare[7];
uint32_t rsdt; /* physical address of RSDT */
} __packed;
/*
* All SDTs have a common header.
*/
extern void *z_acpi_find_table(uint32_t signature);
/* Standard table header */
struct acpi_sdt {
uint32_t signature;
uint32_t length; /* in bytes, of entire table */
uint8_t dontcare0;
uint8_t checksum; /* of entire table */
uint8_t dontcare1[26];
uint32_t sig;
uint32_t len;
uint8_t rev;
uint8_t csum;
char oemid[6];
char oem_table_id[8];
uint32_t oemrevision;
uint32_t creator_id;
uint32_t creator_rev;
} __packed;
/*
* The RSDT is the container for an array of pointers to other SDTs.
*/
/* MCFG table storing MMIO addresses for PCI configuration space */
#define ACPI_RSDT_SIGNATURE 0x54445352 /* 'RSDT' */
#define ACPI_MCFG_SIGNATURE 0x4746434d /* 'MCFG' */
struct acpi_rsdt {
struct acpi_mcfg {
struct acpi_sdt sdt;
uint32_t sdts[]; /* physical addresses of other SDTs */
uint64_t _rsvd;
struct {
uint64_t base_addr;
uint16_t seg_group_num;
uint8_t start_bus;
uint8_t end_bus;
} pci_segs[];
} __packed;
/*
* The MADT is the "Multiple APIC Descriptor Table", which
* we use to enumerate the CPUs and I/O APICs in the system.
*/
/* MADT table storing IO-APIC and multiprocessor configuration */
#define ACPI_MADT_SIGNATURE 0x43495041 /* 'APIC' */
#define ACPI_MADT_FLAGS_PICS 0x01 /* legacy 8259s installed */
#define ACPI_MADT_ENTRY_CPU 0
struct acpi_madt_entry {
uint8_t type;
uint8_t length;
@ -61,19 +52,11 @@ struct acpi_madt_entry {
struct acpi_madt {
struct acpi_sdt sdt;
uint32_t lapic; /* local APIC MMIO address */
uint32_t flags; /* see ACPI_MADT_FLAGS_* below */
uint32_t lapic; /* local APIC MMIO address */
uint32_t flags; /* see ACPI_MADT_FLAGS_* below */
struct acpi_madt_entry entries[];
} __packed;
#define ACPI_MADT_FLAGS_PICS 0x01 /* legacy 8259s installed */
/*
* There's an MADT "local APIC" entry for each CPU in the system.
*/
#define ACPI_MADT_ENTRY_CPU 0
struct acpi_cpu {
struct acpi_madt_entry entry;
uint8_t dontcare;
@ -81,6 +64,8 @@ struct acpi_cpu {
uint8_t flags; /* see ACPI_MADT_CPU_FLAGS_* */
} __packed;
struct acpi_cpu *z_acpi_get_cpu(int n);
#define ACPI_CPU_FLAGS_ENABLED 0x01
#endif /* _ASMLANGUAGE */