arch/x86: rudimentary ACPI support
Implement a simple ACPI parser with enough functionality to enumerate CPU cores and determine their local APIC IDs. Signed-off-by: Charles E. Youse <charles.youse@intel.com>
This commit is contained in:
parent
c0c4ba8516
commit
1ffab8a5f2
6 changed files with 232 additions and 1 deletions
|
@ -82,6 +82,12 @@ choice
|
|||
Reboot via the RST_CNT register, going back to BIOS.
|
||||
endchoice
|
||||
|
||||
config X86_ACPI
|
||||
bool "ACPI (Advanced Configuration and Power Interface) support"
|
||||
default n
|
||||
help
|
||||
Allow retrieval of platform configuration at runtime.
|
||||
|
||||
config X86_MULTIBOOT
|
||||
bool "Generate multiboot header"
|
||||
default y
|
||||
|
|
|
@ -10,6 +10,7 @@ endif ()
|
|||
zephyr_library_sources(cpuhalt.c)
|
||||
zephyr_library_sources_if_kconfig(pcie.c)
|
||||
zephyr_library_sources_if_kconfig(reboot_rst_cnt.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_X86_ACPI acpi.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_X86_MULTIBOOT multiboot.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_X86_VERY_EARLY_CONSOLE early_serial.c)
|
||||
|
||||
|
|
133
arch/x86/core/acpi.c
Normal file
133
arch/x86/core/acpi.c
Normal file
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
* Copyright (c) 2019 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.
|
||||
*/
|
||||
|
||||
static struct x86_acpi_madt *madt;
|
||||
|
||||
/*
|
||||
* ACPI structures use a simple checksum, such that
|
||||
* summing all the bytes in the structure yields 0.
|
||||
*/
|
||||
|
||||
static bool validate_checksum(void *buf, int len)
|
||||
{
|
||||
u8_t *cp = buf;
|
||||
u8_t checksum = 0;
|
||||
|
||||
while (len--) {
|
||||
checksum += *(cp++);
|
||||
}
|
||||
|
||||
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_x86_acpi_init(void)
|
||||
{
|
||||
/*
|
||||
* First, find the RSDP by probing "well-known" areas of memory.
|
||||
*/
|
||||
|
||||
struct x86_acpi_rsdp *rsdp = NULL;
|
||||
|
||||
static const struct {
|
||||
u32_t base;
|
||||
u32_t top;
|
||||
} area[] = {
|
||||
{ 0x000E0000, 0x00100000 }, /* BIOS ROM */
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
for (int i = 0; area[i].base && area[i].top && !rsdp; ++i) {
|
||||
u32_t addr = area[i].base;
|
||||
|
||||
while (addr < area[i].top) {
|
||||
struct x86_acpi_rsdp *probe = UINT_TO_POINTER(addr);
|
||||
|
||||
if ((probe->signature == X86_ACPI_RSDP_SIGNATURE) &&
|
||||
(validate_checksum(probe, sizeof(*probe)))) {
|
||||
rsdp = probe;
|
||||
break;
|
||||
}
|
||||
|
||||
addr += 0x10;
|
||||
}
|
||||
}
|
||||
|
||||
if (rsdp == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Then, validate the RSDT fingered by the RSDP.
|
||||
*/
|
||||
|
||||
struct x86_acpi_rsdt *rsdt = UINT_TO_POINTER(rsdp->rsdt);
|
||||
if ((rsdt->sdt.signature != X86_ACPI_RSDT_SIGNATURE) ||
|
||||
!validate_checksum(rsdt, rsdt->sdt.length)) {
|
||||
rsdt = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Finally, probe each SDT listed in the RSDT to find the MADT.
|
||||
* If it's valid, then remember it for later.
|
||||
*/
|
||||
|
||||
int nr_sdts = (rsdt->sdt.length - sizeof(rsdt)) / sizeof(u32_t);
|
||||
for (int i = 0; i < nr_sdts; ++i) {
|
||||
struct x86_acpi_sdt *sdt = UINT_TO_POINTER(rsdt->sdts[i]);
|
||||
|
||||
if ((sdt->signature == X86_ACPI_MADT_SIGNATURE)
|
||||
&& validate_checksum(sdt, sdt->length)) {
|
||||
madt = (struct x86_acpi_madt *) sdt;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the 'n'th CPU entry from the ACPI MADT, or NULL if not available.
|
||||
*/
|
||||
|
||||
struct x86_acpi_cpu *z_x86_acpi_get_cpu(int n)
|
||||
{
|
||||
u32_t base = POINTER_TO_UINT(madt);
|
||||
u32_t offset;
|
||||
|
||||
if (madt) {
|
||||
offset = POINTER_TO_UINT(madt->entries) - base;
|
||||
|
||||
while (offset < madt->sdt.length) {
|
||||
struct x86_acpi_madt_entry *entry;
|
||||
|
||||
entry = (struct x86_acpi_madt_entry *) (offset + base);
|
||||
|
||||
if (entry->type == X86_ACPI_MADT_ENTRY_CPU) {
|
||||
if (n) {
|
||||
--n;
|
||||
} else {
|
||||
return (struct x86_acpi_cpu *) entry;
|
||||
}
|
||||
}
|
||||
|
||||
offset += entry->length;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
|
||||
#include <kernel_structs.h>
|
||||
#include <arch/x86/acpi.h>
|
||||
|
||||
FUNC_NORETURN void z_x86_prep_c(void)
|
||||
{
|
||||
|
@ -15,6 +16,9 @@ FUNC_NORETURN void z_x86_prep_c(void)
|
|||
#ifdef CONFIG_X86_VERY_EARLY_CONSOLE
|
||||
z_x86_early_serial_init();
|
||||
#endif
|
||||
#ifdef CONFIG_X86_ACPI
|
||||
z_x86_acpi_init();
|
||||
#endif
|
||||
#ifdef CONFIG_X86_MMU
|
||||
z_x86_paging_init();
|
||||
#endif
|
||||
|
|
|
@ -19,7 +19,6 @@ set(QEMU_FLAGS_${ARCH}
|
|||
-device isa-debug-exit,iobase=0xf4,iosize=0x04
|
||||
${REBOOT_FLAG}
|
||||
-nographic
|
||||
-no-acpi
|
||||
)
|
||||
|
||||
# TODO: Support debug
|
||||
|
|
88
include/arch/x86/acpi.h
Normal file
88
include/arch/x86/acpi.h
Normal file
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Intel Corporation
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_ARCH_X86_ACPI_H
|
||||
#define ZEPHYR_INCLUDE_ARCH_X86_ACPI_H
|
||||
|
||||
#ifndef _ASMLANGUAGE
|
||||
|
||||
extern void z_x86_acpi_init(void);
|
||||
extern struct x86_acpi_cpu *z_x86_acpi_get_cpu(int);
|
||||
|
||||
/*
|
||||
* The RSDP is a "floating" structure which simply points to the RSDT.
|
||||
*/
|
||||
|
||||
#define X86_ACPI_RSDP_SIGNATURE 0x2052545020445352 /* 'RSD PTR ' */
|
||||
|
||||
struct x86_acpi_rsdp {
|
||||
u64_t signature; /* X86_ACPI_RSDP_SIG */
|
||||
u8_t checksum;
|
||||
u8_t dontcare[7];
|
||||
u32_t rsdt; /* physical address of RSDT */
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* All SDTs have a common header.
|
||||
*/
|
||||
|
||||
struct x86_acpi_sdt {
|
||||
u32_t signature;
|
||||
u32_t length; /* in bytes, of entire table */
|
||||
u8_t dontcare0;
|
||||
u8_t checksum; /* of entire table */
|
||||
u8_t dontcare1[26];
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* The RSDT is the container for an array of pointers to other SDTs.
|
||||
*/
|
||||
|
||||
#define X86_ACPI_RSDT_SIGNATURE 0x54445352 /* 'RSDT' */
|
||||
|
||||
struct x86_acpi_rsdt {
|
||||
struct x86_acpi_sdt sdt;
|
||||
u32_t sdts[]; /* physical addresses of other SDTs */
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* The MADT is the "Multiple APIC Descriptor Table", which
|
||||
* we use to enumerate the CPUs and I/O APICs in the system.
|
||||
*/
|
||||
|
||||
#define X86_ACPI_MADT_SIGNATURE 0x43495041 /* 'APIC' */
|
||||
|
||||
struct x86_acpi_madt_entry {
|
||||
u8_t type;
|
||||
u8_t length;
|
||||
} __packed;
|
||||
|
||||
struct x86_acpi_madt {
|
||||
struct x86_acpi_sdt sdt;
|
||||
u32_t lapic; /* local APIC MMIO address */
|
||||
u32_t flags; /* see X86_ACPI_MADT_FLAGS_* below */
|
||||
struct x86_acpi_madt_entry entries[];
|
||||
} __packed;
|
||||
|
||||
#define X86_ACPI_MADT_FLAGS_PICS 0x01 /* legacy 8259s installed */
|
||||
|
||||
/*
|
||||
* There's an MADT "local APIC" entry for each CPU in the system.
|
||||
*/
|
||||
|
||||
#define X86_ACPI_MADT_ENTRY_CPU 0
|
||||
|
||||
struct x86_acpi_cpu {
|
||||
struct x86_acpi_madt_entry entry;
|
||||
u8_t dontcare;
|
||||
u8_t id; /* local APIC ID */
|
||||
u8_t flags; /* see X86_ACPI_MADT_CPU_FLAGS_* */
|
||||
} __packed;
|
||||
|
||||
#define X86_ACPI_CPU_FLAGS_ENABLED 0x01
|
||||
|
||||
#endif /* _ASMLANGUAGE */
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_ARCH_X86_ACPI_H */
|
Loading…
Add table
Add a link
Reference in a new issue