diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index d941fc5dccc..e3f8a91fcff 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -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 diff --git a/arch/x86/core/CMakeLists.txt b/arch/x86/core/CMakeLists.txt index 9c94423461a..7f1f4ac59a7 100644 --- a/arch/x86/core/CMakeLists.txt +++ b/arch/x86/core/CMakeLists.txt @@ -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) diff --git a/arch/x86/core/acpi.c b/arch/x86/core/acpi.c new file mode 100644 index 00000000000..36ac09c2783 --- /dev/null +++ b/arch/x86/core/acpi.c @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2019 Intel Corporation + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +/* + * 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; +} diff --git a/arch/x86/core/ia32/prep_c.c b/arch/x86/core/ia32/prep_c.c index b63c2778cc8..76e1c6db189 100644 --- a/arch/x86/core/ia32/prep_c.c +++ b/arch/x86/core/ia32/prep_c.c @@ -5,6 +5,7 @@ */ #include +#include 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 diff --git a/boards/x86/qemu_x86/board.cmake b/boards/x86/qemu_x86/board.cmake index 880a59ba77e..f9e9c6c4397 100644 --- a/boards/x86/qemu_x86/board.cmake +++ b/boards/x86/qemu_x86/board.cmake @@ -19,7 +19,6 @@ set(QEMU_FLAGS_${ARCH} -device isa-debug-exit,iobase=0xf4,iosize=0x04 ${REBOOT_FLAG} -nographic - -no-acpi ) # TODO: Support debug diff --git a/include/arch/x86/acpi.h b/include/arch/x86/acpi.h new file mode 100644 index 00000000000..3d0c8bab747 --- /dev/null +++ b/include/arch/x86/acpi.h @@ -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 */