From 84dfeb5f5355c1f12363250c09e88a252a4ad14b Mon Sep 17 00:00:00 2001 From: tkchia Date: Sun, 22 Oct 2023 16:47:10 +0000 Subject: [PATCH] [metal] Locate some ACPI tables, for later hardware detection Specifically the code now tries to find the ACPI RSDP, RSDT/XSDT, FADT, & MADT tables, whether in legacy BIOS bootup mode or in a UEFI bootup. These are useful for figuring out how to (re)enable asynchronous interrupts in legacy 8259 PIC mode. --- Makefile | 11 +- examples/examples.mk | 1 + examples/vga2.c | 2 + libc/intrin/interrupts.S | 1 + libc/irq/acpi-fadt-init.S | 43 +++++++ libc/irq/acpi-fadt.c | 80 ++++++++++++ libc/irq/acpi-madt-init.S | 51 ++++++++ libc/irq/acpi-madt.c | 81 ++++++++++++ libc/irq/acpi-xsdt-init.S | 46 +++++++ libc/irq/acpi-xsdt.c | 243 +++++++++++++++++++++++++++++++++++ libc/irq/acpi.internal.h | 232 +++++++++++++++++++++++++++++++++ libc/irq/irq.mk | 57 ++++++++ libc/libc.mk | 1 + libc/nt/efi.h | 12 ++ libc/runtime/efimain.greg.c | 26 ++++ libc/runtime/mman.internal.h | 9 +- 16 files changed, 889 insertions(+), 7 deletions(-) create mode 100644 libc/irq/acpi-fadt-init.S create mode 100644 libc/irq/acpi-fadt.c create mode 100644 libc/irq/acpi-madt-init.S create mode 100644 libc/irq/acpi-madt.c create mode 100644 libc/irq/acpi-xsdt-init.S create mode 100644 libc/irq/acpi-xsdt.c create mode 100644 libc/irq/acpi.internal.h create mode 100644 libc/irq/irq.mk diff --git a/Makefile b/Makefile index 8f484a74f31..6f2f0f64b56 100644 --- a/Makefile +++ b/Makefile @@ -136,11 +136,13 @@ include third_party/puff/puff.mk # │ include libc/elf/elf.mk # │ include ape/ape.mk # │ include libc/fmt/fmt.mk # │ -include libc/vga/vga.mk #─┘ +include libc/vga/vga.mk # │ +include libc/irq/irq.mk #─┘ include libc/calls/calls.mk #─┐ -include third_party/nsync/nsync.mk # │ -include libc/runtime/runtime.mk # ├──SYSTEMS RUNTIME -include third_party/double-conversion/dc.mk # │ You can issue system calls +include libc/irq/irq.mk # ├──SYSTEMS RUNTIME +include third_party/nsync/nsync.mk # │ You can issue system calls +include libc/runtime/runtime.mk # │ +include third_party/double-conversion/dc.mk # │ include libc/crt/crt.mk # │ include third_party/dlmalloc/dlmalloc.mk #─┘ include libc/mem/mem.mk #─┐ @@ -340,6 +342,7 @@ COSMOPOLITAN_OBJECTS = \ LIBC_RUNTIME \ THIRD_PARTY_NSYNC \ LIBC_ELF \ + LIBC_IRQ \ LIBC_CALLS \ LIBC_SYSV_CALLS \ LIBC_VGA \ diff --git a/examples/examples.mk b/examples/examples.mk index 9c30c18a325..aa0ac561bf8 100644 --- a/examples/examples.mk +++ b/examples/examples.mk @@ -46,6 +46,7 @@ EXAMPLES_DIRECTDEPS = \ LIBC_DNS \ LIBC_FMT \ LIBC_INTRIN \ + LIBC_IRQ \ LIBC_LOG \ LIBC_MEM \ LIBC_NEXGEN32E \ diff --git a/examples/vga2.c b/examples/vga2.c index 3bc90bd1bad..0494796ee8e 100644 --- a/examples/vga2.c +++ b/examples/vga2.c @@ -26,6 +26,8 @@ __static_yoink("vga_console"); __static_yoink("_idt"); +__static_yoink("_AcpiMadtFlags"); +__static_yoink("_AcpiBootFlags"); __static_yoink("EfiMain"); int main(int argc, char *argv[]) { diff --git a/libc/intrin/interrupts.S b/libc/intrin/interrupts.S index 4daabd3c01d..fe8cf5f9c6d 100644 --- a/libc/intrin/interrupts.S +++ b/libc/intrin/interrupts.S @@ -25,6 +25,7 @@ │ OTHER DEALINGS IN THE SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/dce.h" +#include "libc/intrin/kprintf.h" #include "libc/macros.internal.h" #include "libc/intrin/kprintf.h" #include "libc/runtime/pc.internal.h" diff --git a/libc/irq/acpi-fadt-init.S b/libc/irq/acpi-fadt-init.S new file mode 100644 index 00000000000..7894a02f99f --- /dev/null +++ b/libc/irq/acpi-fadt-init.S @@ -0,0 +1,43 @@ +/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ +│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ This is free and unencumbered software released into the public domain. │ +│ │ +│ Anyone is free to copy, modify, publish, use, compile, sell, or │ +│ distribute this software, either in source code form or as a compiled │ +│ binary, for any purpose, commercial or non-commercial, and by any │ +│ means. │ +│ │ +│ In jurisdictions that recognize copyright laws, the author or authors │ +│ of this software dedicate any and all copyright interest in the │ +│ software to the public domain. We make this dedication for the benefit │ +│ of the public at large and to the detriment of our heirs and │ +│ successors. We intend this dedication to be an overt act of │ +│ relinquishment in perpetuity of all present and future rights to this │ +│ software under copyright law. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR │ +│ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ +│ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR │ +│ OTHER DEALINGS IN THE SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/dce.h" +#include "libc/irq/acpi.internal.h" +#include "libc/macros.internal.h" +#include "libc/runtime/pc.internal.h" + + .init.start 312,_init_acpi_fadt + push %rdi + push %rsi + call _AcpiFadtInit + pop %rsi + pop %rdi + .init.end 312,_init_acpi_fadt + .data +_AcpiBootFlags: + .short kAcpiFadtLegacyDevices | kAcpiFadt8042 + .endobj _AcpiBootFlags,globl + .previous diff --git a/libc/irq/acpi-fadt.c b/libc/irq/acpi-fadt.c new file mode 100644 index 00000000000..588a7e68211 --- /dev/null +++ b/libc/irq/acpi-fadt.c @@ -0,0 +1,80 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ This is free and unencumbered software released into the public domain. │ +│ │ +│ Anyone is free to copy, modify, publish, use, compile, sell, or │ +│ distribute this software, either in source code form or as a compiled │ +│ binary, for any purpose, commercial or non-commercial, and by any │ +│ means. │ +│ │ +│ In jurisdictions that recognize copyright laws, the author or authors │ +│ of this software dedicate any and all copyright interest in the │ +│ software to the public domain. We make this dedication for the benefit │ +│ of the public at large and to the detriment of our heirs and │ +│ successors. We intend this dedication to be an overt act of │ +│ relinquishment in perpetuity of all present and future rights to this │ +│ software under copyright law. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR │ +│ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ +│ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR │ +│ OTHER DEALINGS IN THE SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/dce.h" +#include "libc/intrin/kprintf.h" +#include "libc/irq/acpi.internal.h" + +#ifdef __x86_64__ + +textstartup static void _AcpiDsdtInit(uintptr_t dsdt_phy) { + const AcpiTableDsdt *dsdt; + size_t length; + if (!dsdt_phy) { + KWARNF("FADT: no DSDT"); + return; + } + dsdt = _AcpiMapTable(dsdt_phy); + KINFOF("FADT: DSDT @ %p", dsdt); + length = dsdt->Header.Length; + if (length <= offsetof(AcpiTableDsdt, Aml)) { + KWARNF("DSDT: no AML?"); + return; + } + /* TODO: parse AML to discover hardware configuration */ +} + +textstartup void _AcpiFadtInit(void) { + if (IsMetal()) { + const AcpiTableFadt *fadt; + size_t length; + uint16_t flags; + uintptr_t dsdt_phy = 0; + if (!_AcpiSuccess(_AcpiGetTable("FACP", 0, (void **)&fadt))) { + KINFOF("no FADT found"); + return; + } + length = fadt->Header.Length; + KINFOF("FADT @ %p,+%#zx", fadt, length); + _Static_assert(offsetof(AcpiTableFadt, Dsdt) == 40); + _Static_assert(offsetof(AcpiTableFadt, BootFlags) == 109); + _Static_assert(offsetof(AcpiTableFadt, XDsdt) == 140); + if (length >= offsetof(AcpiTableFadt, BootFlags) + sizeof(fadt->BootFlags)) + { + _AcpiBootFlags = flags = fadt->BootFlags; + KINFOF("FADT: boot flags %#x", (unsigned)flags); + } + if (length >= offsetof(AcpiTableFadt, XDsdt) + sizeof(fadt->XDsdt) && + fadt->XDsdt) { + dsdt_phy = fadt->XDsdt; + } else if (length >= offsetof(AcpiTableFadt, Dsdt) + sizeof(fadt->Dsdt)) { + dsdt_phy = fadt->Dsdt; + } + _AcpiDsdtInit(dsdt_phy); + } +} + +#endif /* __x86_64__ */ diff --git a/libc/irq/acpi-madt-init.S b/libc/irq/acpi-madt-init.S new file mode 100644 index 00000000000..04c506a42e7 --- /dev/null +++ b/libc/irq/acpi-madt-init.S @@ -0,0 +1,51 @@ +/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ +│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ This is free and unencumbered software released into the public domain. │ +│ │ +│ Anyone is free to copy, modify, publish, use, compile, sell, or │ +│ distribute this software, either in source code form or as a compiled │ +│ binary, for any purpose, commercial or non-commercial, and by any │ +│ means. │ +│ │ +│ In jurisdictions that recognize copyright laws, the author or authors │ +│ of this software dedicate any and all copyright interest in the │ +│ software to the public domain. We make this dedication for the benefit │ +│ of the public at large and to the detriment of our heirs and │ +│ successors. We intend this dedication to be an overt act of │ +│ relinquishment in perpetuity of all present and future rights to this │ +│ software under copyright law. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR │ +│ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ +│ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR │ +│ OTHER DEALINGS IN THE SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/dce.h" +#include "libc/irq/acpi.internal.h" +#include "libc/macros.internal.h" +#include "libc/runtime/pc.internal.h" + + .init.start 311,_init_acpi_madt + push %rdi + push %rsi + call _AcpiMadtInit + pop %rsi + pop %rdi + .init.end 311,_init_acpi_madt + .data +_AcpiMadtFlags: + .long kAcpiMadtPcAtCompat + .endobj _AcpiMadtFlags,globl + .previous + .bss +_AcpiNumIoApics: + .skip 8 + .endobj _AcpiNumIoApics,globl +_AcpiIoApics: + .skip 8 + .endobj _AcpiIoApics,globl + .previous diff --git a/libc/irq/acpi-madt.c b/libc/irq/acpi-madt.c new file mode 100644 index 00000000000..69dec60dc3c --- /dev/null +++ b/libc/irq/acpi-madt.c @@ -0,0 +1,81 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ This is free and unencumbered software released into the public domain. │ +│ │ +│ Anyone is free to copy, modify, publish, use, compile, sell, or │ +│ distribute this software, either in source code form or as a compiled │ +│ binary, for any purpose, commercial or non-commercial, and by any │ +│ means. │ +│ │ +│ In jurisdictions that recognize copyright laws, the author or authors │ +│ of this software dedicate any and all copyright interest in the │ +│ software to the public domain. We make this dedication for the benefit │ +│ of the public at large and to the detriment of our heirs and │ +│ successors. We intend this dedication to be an overt act of │ +│ relinquishment in perpetuity of all present and future rights to this │ +│ software under copyright law. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR │ +│ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ +│ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR │ +│ OTHER DEALINGS IN THE SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/dce.h" +#include "libc/inttypes.h" +#include "libc/irq/acpi.internal.h" + +#ifdef __x86_64__ + +textstartup void _AcpiMadtInit(void) { + if (IsMetal()) { + const AcpiTableMadt *madt; + size_t length, num_io_apics = 0; + const char *madt_end, *p; + const AcpiSubtableHeader *h; + const AcpiMadtIoApic **icp; + uint32_t flags; + if (!_AcpiSuccess(_AcpiGetTable("APIC", 0, (void **)&madt))) { + KINFOF("no MADT found"); + return; + } + length = madt->Header.Length; + if (length < sizeof(*madt)) { + KDIEF("MADT has short length %#zx", length); + } + _AcpiMadtFlags = flags = madt->Flags; + KINFOF("MADT @ %p, flags %#" PRIx32, madt, flags); + madt_end = (char *)madt + length; + p = madt->Subtable; + while (p != madt_end) { + h = (const AcpiSubtableHeader *)p; + switch (h->Type) { + case kAcpiMadtIoApic: + ++num_io_apics; + } + p += h->Length; + } + KINFOF("MADT: %zu I/O APIC(s)", num_io_apics); + icp = _AcpiOsAllocate(num_io_apics * sizeof(const AcpiMadtIoApic *)); + if (icp) { + _AcpiIoApics = icp; + p = madt->Subtable; + while (p != madt_end) { + h = (const AcpiSubtableHeader *)p; + switch (h->Type) { + case kAcpiMadtIoApic: + *icp++ = (const AcpiMadtIoApic *)p; + } + p += h->Length; + } + _AcpiNumIoApics = num_io_apics; + } else { + KWARNF("MADT: no memory for I/O APICs"); + } + } +} + +#endif /* __x86_64__ */ diff --git a/libc/irq/acpi-xsdt-init.S b/libc/irq/acpi-xsdt-init.S new file mode 100644 index 00000000000..158285ff95e --- /dev/null +++ b/libc/irq/acpi-xsdt-init.S @@ -0,0 +1,46 @@ +/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ +│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ This is free and unencumbered software released into the public domain. │ +│ │ +│ Anyone is free to copy, modify, publish, use, compile, sell, or │ +│ distribute this software, either in source code form or as a compiled │ +│ binary, for any purpose, commercial or non-commercial, and by any │ +│ means. │ +│ │ +│ In jurisdictions that recognize copyright laws, the author or authors │ +│ of this software dedicate any and all copyright interest in the │ +│ software to the public domain. We make this dedication for the benefit │ +│ of the public at large and to the detriment of our heirs and │ +│ successors. We intend this dedication to be an overt act of │ +│ relinquishment in perpetuity of all present and future rights to this │ +│ software under copyright law. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR │ +│ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ +│ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR │ +│ OTHER DEALINGS IN THE SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/dce.h" +#include "libc/irq/acpi.internal.h" +#include "libc/macros.internal.h" +#include "libc/runtime/pc.internal.h" + + .init.start 310,_init_acpi_xsdt + push %rdi + push %rsi + call _AcpiXsdtInit + pop %rsi + pop %rdi + .init.end 310,_init_acpi_xsdt + .bss +_AcpiXsdtNumEntries: + .skip 8 + .endobj _AcpiXsdtNumEntries,globl +_AcpiXsdtEntries: + .skip 8 + .endobj _AcpiXsdtEntries,globl + .previous diff --git a/libc/irq/acpi-xsdt.c b/libc/irq/acpi-xsdt.c new file mode 100644 index 00000000000..9b20c5ab06c --- /dev/null +++ b/libc/irq/acpi-xsdt.c @@ -0,0 +1,243 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ This is free and unencumbered software released into the public domain. │ +│ │ +│ Anyone is free to copy, modify, publish, use, compile, sell, or │ +│ distribute this software, either in source code form or as a compiled │ +│ binary, for any purpose, commercial or non-commercial, and by any │ +│ means. │ +│ │ +│ In jurisdictions that recognize copyright laws, the author or authors │ +│ of this software dedicate any and all copyright interest in the │ +│ software to the public domain. We make this dedication for the benefit │ +│ of the public at large and to the detriment of our heirs and │ +│ successors. We intend this dedication to be an overt act of │ +│ relinquishment in perpetuity of all present and future rights to this │ +│ software under copyright law. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR │ +│ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ +│ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR │ +│ OTHER DEALINGS IN THE SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/dce.h" +#include "libc/intrin/atomic.h" +#include "libc/intrin/bits.h" +#include "libc/intrin/directmap.internal.h" +#include "libc/intrin/kprintf.h" +#include "libc/irq/acpi.internal.h" +#include "libc/log/color.internal.h" +#include "libc/macros.internal.h" +#include "libc/nt/efi.h" +#include "libc/runtime/pc.internal.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/map.h" +#include "libc/sysv/consts/prot.h" + +#ifdef __x86_64__ + +textstartup static void *_AcpiOsMapRoMemory(uintptr_t phy, size_t n) { + __invert_and_perm_ref_memory_area(__get_mm(), __get_pml4t(), phy, n, + PAGE_XD); + return (void *)(BANE + phy); +} + +textstartup static void *_AcpiOsMapMemory(uintptr_t phy, size_t n) { + __invert_and_perm_ref_memory_area(__get_mm(), __get_pml4t(), phy, n, + PAGE_XD | PAGE_RW); + return (void *)(BANE + phy); +} + +textstartup void *_AcpiOsMapUncachedMemory(uintptr_t phy, size_t n) { + __invert_and_perm_ref_memory_area(__get_mm(), __get_pml4t(), phy, n, + PAGE_XD | PAGE_PCD | PAGE_RW); + return (void *)(BANE + phy); +} + +textstartup static void *_AcpiOsAllocatePages(size_t n) { + struct DirectMap dm = sys_mmap_metal(NULL, n, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + void *addr = dm.addr; + if (addr == (void *)-1) addr = NULL; + return addr; +} + +textstartup void *_AcpiOsAllocate(size_t n) { + static _Atomic(char *) slack = NULL; + char *addr = NULL; + size_t align = __BIGGEST_ALIGNMENT__, use; + if (n >= 4096) return _AcpiOsAllocatePages(n); + n = ROUNDUP(n, align); + for (;;) { + addr = atomic_exchange(&slack, NULL); + if (!addr) { + addr = _AcpiOsAllocatePages(4096); + if (!addr) return NULL; + } + use = (uintptr_t)addr % 4096 + n; + if (use <= 4096) { + if (use < 4096) atomic_store(&slack, addr + n); + return addr; + } + } +} + +textstartup static uint8_t _AcpiTbChecksum(const uint8_t *p, size_t n) { + uint8_t c = 0; + while (n-- != 0) c += *p++; + return c; +} + +textstartup static AcpiStatus _AcpiTbVerifyChecksum(const uint8_t *p, + size_t n) { + uint8_t sum = _AcpiTbChecksum(p, n); + if (!sum) return kAcpiOk; + KWARNF("bad ACPI table cksum %#x != 0 @ %p,+%#zx", (unsigned)sum, p, n); + return kAcpiExBadChecksum; +} + +textstartup static AcpiStatus _AcpiRsdpVerifyChecksums(const uint8_t *p) { + const AcpiTableRsdp *q = (const AcpiTableRsdp *)p; + size_t length = offsetof(AcpiTableRsdp, Length); + AcpiStatus sta = _AcpiTbVerifyChecksum(p, length); + if (!_AcpiSuccess(sta)) return sta; + if (q->Revision <= 1) return kAcpiOk; + length = q->Length; + if (length < offsetof(AcpiTableRsdp, Reserved)) { + KWARNF("malformed ACPI 2+ RSDP, length %#zx < %#zx", + length, offsetof(AcpiTableRsdp, Reserved)); + if (length < offsetof(AcpiTableRsdp, RsdtPhysicalAddress) + + sizeof(q->RsdtPhysicalAddress)) { + return kAcpiExBadHeader; + } + return kAcpiOk; + } + return _AcpiTbVerifyChecksum(p, length); +} + +textstartup static bool _AcpiTbIsValidRsdp(const uint8_t *p) { + const AcpiTableRsdp *q = (const AcpiTableRsdp *)p; + if (READ64LE(q->Signature) != READ64LE("RSD PTR ")) return false; + KINFOF("\"RSD PTR \" @ %p, ACPI rev %u", q, (unsigned)q->Revision); + if (!_AcpiSuccess(_AcpiRsdpVerifyChecksums(p))) return false; + return true; +} + +textstartup static const AcpiTableRsdp *_AcpiFindRsdp(void) { + const size_t PARA_SZ = 0x10; + size_t off; + struct mman *mm = __get_mm(); + uint64_t rsdp_phy = mm->pc_acpi_rsdp; + uint16_t ebda_para; + const uint8_t *area; + if (rsdp_phy) return _AcpiOsMapRoMemory(rsdp_phy, sizeof(AcpiTableRsdp)); + /* + * "OSPM finds the Root System Description Pointer (RSDP) structure by + * searching physical memory ranges on 16-byte boundaries for a valid + * Root System Description Pointer structure signature and checksum match + * as follows: + * * The first 1 KB of the Extended BIOS Data Area (EBDA). For EISA or + * MCA systems, the EBDA can be found in the two-byte location 40:0Eh + * on the BIOS data area. + * * The BIOS read-only memory space between 0E0000h and 0FFFFFh." + * — Advanced Configuration and Power Interface (ACPI) Specification, + * Release 6.5, §5.2.5.1 + */ + ebda_para = *(const uint16_t *)(BANE + PC_BIOS_DATA_AREA + 0xE); + if (ebda_para) { + area = _AcpiOsMapMemory((uint32_t)ebda_para * PARA_SZ, 1024); + KINFOF("search EBDA @ %p for RSDP", area); + off = 1024; + while (off != 0) { + off -= PARA_SZ; + if (_AcpiTbIsValidRsdp(area + off)) { + return (const AcpiTableRsdp *)(area + off); + } + } + } + area = _AcpiOsMapMemory(0xE0000, 0xFFFFF + 1 - 0xE0000); + KINFOF("search ROM BIOS for RSDP"); + off = ROUNDUP(0xFFFFF + 1 - 0xE0000, PARA_SZ); + while (off != 0) { + off -= PARA_SZ; + if (_AcpiTbIsValidRsdp(area + off)) { + return (const AcpiTableRsdp *)(area + off); + } + } + return NULL; +} + +textstartup void *_AcpiMapTable(uintptr_t phy) { + void *p = _AcpiOsMapRoMemory(phy, sizeof(AcpiTableHeader)); + AcpiTableHeader *hdr = p; + size_t length = hdr->Length; + if (length < sizeof(*hdr)) { + KDIEF("ACPI table @ %p has short length %#zx", p, length); + } else { + p = _AcpiOsMapRoMemory(phy, length); + } + _AcpiTbVerifyChecksum((const uint8_t *)p, length); + return p; +} + +textstartup void _AcpiXsdtInit(void) { + if (IsMetal()) { + size_t nents, i; + void **ents = NULL; + const AcpiTableRsdp *rsdp = _AcpiFindRsdp(); + if (!rsdp) { + KINFOF("no RSDP found"); + return; + } + KINFOF("RSDP @ %p", rsdp); + if (rsdp->Revision <= 1 || + rsdp->Length < offsetof(AcpiTableRsdp, Reserved) || + !rsdp->XsdtPhysicalAddress) { + const AcpiTableRsdt *rsdt = _AcpiMapTable(rsdp->RsdtPhysicalAddress); + nents = (rsdt->Header.Length - sizeof(rsdt->Header)) / sizeof(uint32_t); + KINFOF("RSDT @ %p, %#zx entries", rsdt, nents); + ents = _AcpiOsAllocate(nents * sizeof(AcpiTableHeader *)); + if (ents) { + for (i = 0; i < nents; ++i) { + ents[i] = _AcpiMapTable(rsdt->TableOffsetEntry[i]); + } + } + } else { + const AcpiTableXsdt *xsdt = _AcpiMapTable(rsdp->XsdtPhysicalAddress); + nents = (xsdt->Header.Length - sizeof(xsdt->Header)) / sizeof(uint64_t); + KINFOF("XSDT @ %p, %#zx entries", xsdt, nents); + ents = _AcpiOsAllocate(nents * sizeof(AcpiTableHeader *)); + if (ents) { + for (i = 0; i < nents; ++i) { + ents[i] = _AcpiMapTable(xsdt->TableOffsetEntry[i]); + } + } + } + if (!ents) { + KWARNF("no memory for mapped RSDT/XSDT entries"); + nents = 0; + } + _AcpiXsdtEntries = ents; + _AcpiXsdtNumEntries = nents; + } +} + +textstartup AcpiStatus _AcpiGetTableImpl(uint32_t sig, uint32_t inst, + void **phdr) { + void **p = _AcpiXsdtEntries; + size_t n = _AcpiXsdtNumEntries; + while (n-- != 0) { + AcpiTableHeader *h = *p++; + if (READ32LE(h->Signature) != sig) continue; + if (inst-- != 0) continue; + *phdr = h; + return kAcpiOk; + } + return kAcpiExNotFound; +} + +#endif /* __x86_64__ */ diff --git a/libc/irq/acpi.internal.h b/libc/irq/acpi.internal.h new file mode 100644 index 00000000000..d0e594a1b7d --- /dev/null +++ b/libc/irq/acpi.internal.h @@ -0,0 +1,232 @@ +#ifndef COSMOPOLITAN_LIBC_IRQ_ACPI_INTERNAL_H_ +#define COSMOPOLITAN_LIBC_IRQ_ACPI_INTERNAL_H_ +#include "libc/intrin/bits.h" +#include "libc/intrin/kprintf.h" +#include "libc/log/color.internal.h" + +/** + * AcpiStatus values. + */ +#define kAcpiOk 0x0000 +#define kAcpiExNotFound 0x0005 +#define kAcpiExBadHeader 0x2002 +#define kAcpiExBadChecksum 0x2003 + +/** + * Flags for AcpiTableMadt::Flags. + */ +#define kAcpiMadtPcAtCompat 0x0001 + +/** + * Flags for AcpiTableFadt::BootFlags. + */ +#define kAcpiFadtLegacyDevices 0x0001 +#define kAcpiFadt8042 0x0002 +#define kAcpiFadtNoVga 0x0004 +#define kAcpiFadtNoMsi 0x0008 +#define kAcpiFadtNoAspm 0x0010 +#define kAcpiFadtNoCmosRtc 0x0020 + +/** + * Values for AcpiSubtableHeader::Type under an AcpiTableMadt. + */ +#define kAcpiMadtLocalApic 0 +#define kAcpiMadtIoApic 1 +#define kAcpiMadtIntOverride 2 + +#if !(__ASSEMBLER__ + __LINKER__ + 0) + +/** + * @fileoverview Declarations for bare metal code to interact with ACPI + * + * @see UEFI Forum, Inc. Advanced Configuration and Power Interface (ACPI) + * Specification, Version 6.5, 2022. https://uefi.org/specifications + * @see Intel Corporation. ACPI Component Architecture: User Guide and + * Programmer Reference, Revision 6.2, 2017. https://acpica.org + */ + +COSMOPOLITAN_C_START_ + +/** + * @internal + * Structure of an ACPI Root System Description Pointer (RSDP) table. This + * points to either an RSDT, or an XSDT, or both. + */ +typedef struct { + uint8_t Signature[8]; + uint8_t Checksum; + uint8_t OemId[6]; + uint8_t Revision; + uint32_t RsdtPhysicalAddress; + uint32_t Length; + uint64_t XsdtPhysicalAddress; + uint8_t ExtendedChecksum; + uint8_t Reserved[3]; +} AcpiTableRsdp; + +/** + * @internal + * Structure of a System Description Table Header, which appears at the + * beginning of each ACPI system description table. + */ +typedef struct thatispacked { + uint8_t Signature[4]; + uint32_t Length; + uint8_t Revision; + uint8_t Checksum; + uint8_t OemId[6]; + uint8_t OemTableId[8]; + uint32_t OemRevision; + uint8_t AslCompilerId[4]; + uint32_t AslCompilerRevision; +} AcpiTableHeader; + +/** + * @internal + * ACPI Root System Description Table (RSDT) structure. + */ +typedef struct thatispacked { + AcpiTableHeader Header; + uint32_t TableOffsetEntry[]; +} AcpiTableRsdt; + +/** + * @internal + * ACPI Extended System Description Table (XSDT) structure. + */ +typedef struct thatispacked { + AcpiTableHeader Header; + uint64_t TableOffsetEntry[]; +} AcpiTableXsdt; + +typedef struct thatispacked { + uint8_t SpaceId; + uint8_t BitWidth; + uint8_t BitOffset; + uint8_t AccessWidth; + uint64_t Address; +} AcpiGenericAddress; + +typedef struct thatispacked { + uint8_t Type; + uint8_t Length; +} AcpiSubtableHeader; + +typedef struct thatispacked { + AcpiSubtableHeader Header; + uint8_t Id; + uint8_t Reserved; + uint32_t Address; + uint32_t GlobalIrqBase; +} AcpiMadtIoApic; + +/** + * @internal + * ACPI Multiple APIC Description Table (MADT) structure. + */ +typedef struct thatispacked { + AcpiTableHeader Header; + uint32_t Address; /* local APIC address */ + uint32_t Flags; /* multiple APIC flags */ + char Subtable[]; /* ...interrupt controller structures... */ +} AcpiTableMadt; + +/** + * @internal + * Fixed ACPI Description Table (FADT) structure. + */ +typedef struct thatispacked { + AcpiTableHeader Header; + uint32_t Facs; + uint32_t Dsdt; + uint8_t Model; + uint8_t PreferredProfile; + uint16_t SciInterrupt; + uint32_t SmiCommand; + uint8_t AcpiEnable; + uint8_t AcpiDisable; + uint8_t S4BiosRequest; + uint8_t PstateControl; + uint32_t Pm1aEventBlock; + uint32_t Pm1bEventBlock; + uint32_t Pm1aControlBlock; + uint32_t Pm1bControlBlock; + uint32_t Pm2ControlBlock; + uint32_t PmTimerBlock; + uint32_t Gpe0Block; + uint32_t Gpe1Block; + uint8_t Pm1EventLength; + uint8_t Pm1ControlLength; + uint8_t Pm2ControlLength; + uint8_t PmTimerLength; + uint8_t Gpe0BlockLength; + uint8_t Gpe1BlockLength; + uint8_t Gpe1Base; + uint8_t CstControl; + uint16_t C2Latency; + uint16_t C3Latency; + uint16_t FlushSize; + uint16_t FlushStride; + uint8_t DutyOffset; + uint8_t DutyWidth; + uint8_t DayAlarm; + uint8_t MonthAlarm; + uint8_t Century; + uint16_t BootFlags; + uint8_t Reserved; + uint32_t Flags; + AcpiGenericAddress ResetRegister; + uint8_t ResetValue; + uint16_t ArmBootFlags; + uint8_t MinorRevision; + uint64_t XFacs; + uint64_t XDsdt; + AcpiGenericAddress XPm1aEventBlock; + AcpiGenericAddress XPm1bEventBlock; + AcpiGenericAddress XPm1aControlBlock; + AcpiGenericAddress XPm1bControlBlock; + AcpiGenericAddress XPm2ControlBlock; + AcpiGenericAddress XPmTimerBlock; + AcpiGenericAddress XGpe0Block; + AcpiGenericAddress XGpe1Block; + AcpiGenericAddress SleepControl; + AcpiGenericAddress SleepStatus; + uint64_t HypervisorId; +} AcpiTableFadt; + +/** + * @internal + * ACPI Differentiated System Description Table (DSDT) structure. + */ +typedef struct thatispacked { + AcpiTableHeader Header; + uint8_t Aml[]; +} AcpiTableDsdt; + +typedef uint32_t AcpiStatus; + +extern size_t _AcpiXsdtNumEntries, _AcpiNumIoApics; +extern void **_AcpiXsdtEntries; +extern uint16_t _AcpiBootFlags; +extern uint32_t _AcpiMadtFlags; +extern const AcpiMadtIoApic **_AcpiIoApics; + +extern void *_AcpiOsMapUncachedMemory(uintptr_t, size_t); +extern void *_AcpiOsAllocate(size_t); +extern void *_AcpiMapTable(uintptr_t); + +extern AcpiStatus _AcpiGetTableImpl(uint32_t, uint32_t, void **); + +forceinline bool _AcpiSuccess(AcpiStatus __sta) { + return __sta == kAcpiOk; +} + +forceinline AcpiStatus _AcpiGetTable(const char __sig[4], uint32_t __inst, + void **__phdr) { + uint8_t __sig_copy[4] = { __sig[0], __sig[1], __sig[2], __sig[3] }; + return _AcpiGetTableImpl(READ32LE(__sig_copy), __inst, __phdr); +} + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_IRQ_ACPI_INTERNAL_H_ */ diff --git a/libc/irq/irq.mk b/libc/irq/irq.mk new file mode 100644 index 00000000000..348e5fef213 --- /dev/null +++ b/libc/irq/irq.mk @@ -0,0 +1,57 @@ +#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐ +#───vi: set et ft=make ts=8 tw=8 fenc=utf-8 :vi───────────────────────┘ + +PKGS += LIBC_IRQ + +LIBC_IRQ_ARTIFACTS += LIBC_IRQ_A +LIBC_IRQ_A = o/$(MODE)/libc/irq/irq.a +LIBC_IRQ_A_FILES := $(wildcard libc/irq/*) +LIBC_IRQ_A_HDRS = $(filter %.h,$(LIBC_IRQ_A_FILES)) +LIBC_IRQ_A_INCS = $(filter %.inc,$(LIBC_IRQ_A_FILES)) +LIBC_IRQ_A_SRCS_S = $(filter %.S,$(LIBC_IRQ_A_FILES)) +LIBC_IRQ_A_SRCS_C = $(filter %.c,$(LIBC_IRQ_A_FILES)) + +LIBC_IRQ = \ + $(LIBC_IRQ_A_DEPS) \ + $(LIBC_IRQ_A) + +LIBC_IRQ_A_SRCS = \ + $(LIBC_IRQ_A_SRCS_S) \ + $(LIBC_IRQ_A_SRCS_C) + +LIBC_IRQ_A_OBJS = \ + $(LIBC_IRQ_A_SRCS_S:%.S=o/$(MODE)/%.o) \ + $(LIBC_IRQ_A_SRCS_C:%.c=o/$(MODE)/%.o) + +LIBC_IRQ_A_CHECKS = \ + $(LIBC_IRQ_A).pkg \ + $(LIBC_IRQ_A_HDRS:%=o/$(MODE)/%.ok) + +LIBC_IRQ_A_DIRECTDEPS = \ + LIBC_INTRIN \ + LIBC_STR \ + LIBC_SYSV + +LIBC_IRQ_A_DEPS := \ + $(call uniq,$(foreach x,$(LIBC_IRQ_A_DIRECTDEPS),$($(x)))) + +$(LIBC_IRQ_A):libc/irq/ \ + $(LIBC_IRQ_A).pkg \ + $(LIBC_IRQ_A_OBJS) + +$(LIBC_IRQ_A).pkg: \ + $(LIBC_IRQ_A_OBJS) \ + $(foreach x,$(LIBC_IRQ_A_DIRECTDEPS),$($(x)_A).pkg) + +LIBC_IRQ_LIBS = $(foreach x,$(LIBC_IRQ_ARTIFACTS),$($(x))) +LIBC_IRQ_SRCS = $(foreach x,$(LIBC_IRQ_ARTIFACTS),$($(x)_SRCS)) +LIBC_IRQ_HDRS = $(foreach x,$(LIBC_IRQ_ARTIFACTS),$($(x)_HDRS)) +LIBC_IRQ_INCS = $(foreach x,$(LIBC_IRQ_ARTIFACTS),$($(x)_INCS)) +LIBC_IRQ_BINS = $(foreach x,$(LIBC_IRQ_ARTIFACTS),$($(x)_BINS)) +LIBC_IRQ_CHECKS = $(foreach x,$(LIBC_IRQ_ARTIFACTS),$($(x)_CHECKS)) +LIBC_IRQ_OBJS = $(foreach x,$(LIBC_IRQ_ARTIFACTS),$($(x)_OBJS)) +LIBC_IRQ_TESTS = $(foreach x,$(LIBC_IRQ_ARTIFACTS),$($(x)_TESTS)) +$(LIBC_IRQ_OBJS): $(BUILD_FILES) libc/irq/irq.mk + +.PHONY: o/$(MODE)/libc/irq +o/$(MODE)/libc/irq: $(LIBC_IRQ_CHECKS) diff --git a/libc/libc.mk b/libc/libc.mk index a45865d8c2e..4e90cd3ebec 100644 --- a/libc/libc.mk +++ b/libc/libc.mk @@ -20,6 +20,7 @@ o/$(MODE)/libc: o/$(MODE)/libc/calls \ o/$(MODE)/libc/elf \ o/$(MODE)/libc/fmt \ o/$(MODE)/libc/intrin \ + o/$(MODE)/libc/irq \ o/$(MODE)/libc/log \ o/$(MODE)/libc/mem \ o/$(MODE)/libc/nexgen32e \ diff --git a/libc/nt/efi.h b/libc/nt/efi.h index b17718489fa..1a403d0630e 100644 --- a/libc/nt/efi.h +++ b/libc/nt/efi.h @@ -103,6 +103,18 @@ 0x96, 0xFB, 0x7A, 0xDE, 0xD0, 0x80, 0x51, 0x6A \ } \ } +#define ACPI_20_TABLE_GUID \ + { \ + 0x8868E871, 0xE4F1, 0x11D3, { \ + 0xBC, 0x22, 0x00, 0x80, 0xC7, 0x3C, 0x88, 0x81 \ + } \ + } +#define ACPI_10_TABLE_GUID \ + { \ + 0xEB9D2D30, 0x2D88, 0x11D3, { \ + 0x9A, 0x16, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D \ + } \ + } #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ diff --git a/libc/runtime/efimain.greg.c b/libc/runtime/efimain.greg.c index 4e953be0115..cee4fcff098 100644 --- a/libc/runtime/efimain.greg.c +++ b/libc/runtime/efimain.greg.c @@ -43,6 +43,8 @@ struct EfiArgs { static EFI_GUID kEfiLoadedImageProtocol = LOADED_IMAGE_PROTOCOL; static EFI_GUID kEfiGraphicsOutputProtocol = GRAPHICS_OUTPUT_PROTOCOL; +static const EFI_GUID kEfiAcpi20TableGuid = ACPI_20_TABLE_GUID; +static const EFI_GUID kEfiAcpi10TableGuid = ACPI_10_TABLE_GUID; extern const char vga_console[]; extern void _EfiPostboot(struct mman *, uint64_t *, uintptr_t, char **); @@ -117,6 +119,25 @@ static void EfiInitVga(struct mman *mm, EFI_SYSTEM_TABLE *SystemTable) { GraphMode->FrameBufferSize, 0); } +static void EfiInitAcpi(struct mman *mm, EFI_SYSTEM_TABLE *SystemTable) { + void *rsdp1 = NULL, *rsdp2 = NULL; + uintptr_t n = SystemTable->NumberOfTableEntries, i; + EFI_CONFIGURATION_TABLE *tab; + for (i = 0, tab = SystemTable->ConfigurationTable; i < n; ++i, ++tab) { + EFI_GUID *guid = &tab->VendorGuid; + if (memcmp(guid, &kEfiAcpi20TableGuid, sizeof(EFI_GUID)) == 0) { + rsdp2 = tab->VendorTable; + } else if (memcmp(guid, &kEfiAcpi20TableGuid, sizeof(EFI_GUID)) == 0) { + rsdp1 = tab->VendorTable; + } + } + if (rsdp2) { + mm->pc_acpi_rsdp = (uintptr_t)rsdp2; + } else { + mm->pc_acpi_rsdp = (uintptr_t)rsdp1; + } +} + /** * EFI Application Entrypoint. * @@ -203,6 +224,11 @@ __msabi EFI_STATUS EfiMain(EFI_HANDLE ImageHandle, */ if (_weaken(vga_console)) EfiInitVga(mm, SystemTable); + /* + * Gets a pointer to the ACPI RSDP. + */ + EfiInitAcpi(mm, SystemTable); + /* * Asks UEFI which parts of our RAM we're allowed to use. */ diff --git a/libc/runtime/mman.internal.h b/libc/runtime/mman.internal.h index e51f05cd34f..8530aaf0bed 100644 --- a/libc/runtime/mman.internal.h +++ b/libc/runtime/mman.internal.h @@ -39,9 +39,12 @@ struct mman { struct { /* 0x1d48 — starting cursor pos. */ unsigned short y, x; } pc_video_curs_info; - unsigned short pc_video_char_height; /* 0x1d4c — character height (useful - for setting cursor shape - in text mode) */ + unsigned short pc_video_char_height; /* 0x1d4c — character height (useful + for setting cursor shape + in text mode) */ + uint64_t pc_acpi_rsdp; /* 0x1d50 — pointer to ACPI RSDP; + NULL means to search for + it in legacy BIOS areas */ }; COSMOPOLITAN_C_END_