mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 19:43:32 +00:00
fa20edc44d
- Remove most __ASSEMBLER__ __LINKER__ ifdefs - Rename libc/intrin/bits.h to libc/serialize.h - Block pthread cancelation in fchmodat() polyfill - Remove `clang-format off` statements in third_party
242 lines
9.5 KiB
C
242 lines
9.5 KiB
C
/*-*- 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/serialize.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__ */
|