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:
parent
ba0a791635
commit
7fe8caebc0
3 changed files with 112 additions and 118 deletions
|
@ -1,118 +1,131 @@
|
|||
/*
|
||||
* 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 sum == 0;
|
||||
}
|
||||
|
||||
return (checksum == 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;
|
||||
|
||||
static const struct {
|
||||
uintptr_t base;
|
||||
uintptr_t top;
|
||||
} area[] = {
|
||||
{ 0x000E0000, 0x00100000 }, /* BIOS ROM */
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
for (int i = 0; area[i].base && area[i].top && !rsdp; ++i) {
|
||||
uintptr_t addr = area[i].base;
|
||||
|
||||
while (addr < area[i].top) {
|
||||
struct acpi_rsdp *probe = UINT_TO_POINTER(addr);
|
||||
|
||||
if ((probe->signature == ACPI_RSDP_SIGNATURE) &&
|
||||
(validate_checksum(probe, sizeof(*probe)))) {
|
||||
rsdp = probe;
|
||||
break;
|
||||
}
|
||||
|
||||
addr += 0x10;
|
||||
for (int i = 0; i < 1024/8; i++) {
|
||||
if (search[i] == magic) {
|
||||
return (void *)&search[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (rsdp == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Then, validate the RSDT fingered by the RSDP.
|
||||
/* If it's not there, then look for it in the last 128kb of
|
||||
* real mode memory.
|
||||
*/
|
||||
|
||||
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;
|
||||
search = (uint64_t *)0xe0000;
|
||||
for (int i = 0; i < 128*1024/8; i++) {
|
||||
if (search[i] == magic) {
|
||||
return (void *)&search[i];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Finally, probe each SDT listed in the RSDT to find the MADT.
|
||||
* If it's valid, then remember it for later.
|
||||
/* 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;
|
||||
}
|
||||
|
||||
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]);
|
||||
void *z_acpi_find_table(uint32_t signature)
|
||||
{
|
||||
struct acpi_rsdp *rsdp = find_rsdp();
|
||||
|
||||
if ((sdt->signature == ACPI_MADT_SIGNATURE)
|
||||
&& validate_checksum(sdt, sdt->length)) {
|
||||
madt = (struct acpi_madt *) sdt;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.length) {
|
||||
while (offset < madt->sdt.len) {
|
||||
struct acpi_madt_entry *entry;
|
||||
|
||||
entry = (struct acpi_madt_entry *) (offset + base);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
@ -66,14 +57,6 @@ struct acpi_madt {
|
|||
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 */
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue