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:
Charles E. Youse 2019-09-06 15:03:38 -04:00 committed by Andrew Boie
commit 1ffab8a5f2
6 changed files with 232 additions and 1 deletions

View file

@ -82,6 +82,12 @@ choice
Reboot via the RST_CNT register, going back to BIOS. Reboot via the RST_CNT register, going back to BIOS.
endchoice 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 config X86_MULTIBOOT
bool "Generate multiboot header" bool "Generate multiboot header"
default y default y

View file

@ -10,6 +10,7 @@ endif ()
zephyr_library_sources(cpuhalt.c) zephyr_library_sources(cpuhalt.c)
zephyr_library_sources_if_kconfig(pcie.c) zephyr_library_sources_if_kconfig(pcie.c)
zephyr_library_sources_if_kconfig(reboot_rst_cnt.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_MULTIBOOT multiboot.c)
zephyr_library_sources_ifdef(CONFIG_X86_VERY_EARLY_CONSOLE early_serial.c) zephyr_library_sources_ifdef(CONFIG_X86_VERY_EARLY_CONSOLE early_serial.c)

133
arch/x86/core/acpi.c Normal file
View 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;
}

View file

@ -5,6 +5,7 @@
*/ */
#include <kernel_structs.h> #include <kernel_structs.h>
#include <arch/x86/acpi.h>
FUNC_NORETURN void z_x86_prep_c(void) 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 #ifdef CONFIG_X86_VERY_EARLY_CONSOLE
z_x86_early_serial_init(); z_x86_early_serial_init();
#endif #endif
#ifdef CONFIG_X86_ACPI
z_x86_acpi_init();
#endif
#ifdef CONFIG_X86_MMU #ifdef CONFIG_X86_MMU
z_x86_paging_init(); z_x86_paging_init();
#endif #endif

View file

@ -19,7 +19,6 @@ set(QEMU_FLAGS_${ARCH}
-device isa-debug-exit,iobase=0xf4,iosize=0x04 -device isa-debug-exit,iobase=0xf4,iosize=0x04
${REBOOT_FLAG} ${REBOOT_FLAG}
-nographic -nographic
-no-acpi
) )
# TODO: Support debug # TODO: Support debug

88
include/arch/x86/acpi.h Normal file
View 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 */