From 4eba5688ae16134837184956e24fb8d7723ca041 Mon Sep 17 00:00:00 2001 From: tkchia Date: Mon, 18 Sep 2023 10:16:09 +0000 Subject: [PATCH] [metal] Start parsing some ACPI AML --- examples/vga2.c | 1 + libc/irq/acpi-fadt.c | 258 ++++++++++++++++++++++++++++++++++++++- libc/irq/acpi.internal.h | 92 ++++++++++++-- 3 files changed, 337 insertions(+), 14 deletions(-) diff --git a/examples/vga2.c b/examples/vga2.c index 83930bb98..714f947ce 100644 --- a/examples/vga2.c +++ b/examples/vga2.c @@ -27,6 +27,7 @@ __static_yoink("vga_console"); __static_yoink("_idt"); __static_yoink("_irq"); +__static_yoink("_AcpiBootFlags"); __static_yoink("EfiMain"); int main(int argc, char *argv[]) { diff --git a/libc/irq/acpi-fadt.c b/libc/irq/acpi-fadt.c index bf0484bbe..91ca3c073 100644 --- a/libc/irq/acpi-fadt.c +++ b/libc/irq/acpi-fadt.c @@ -26,12 +26,263 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/dce.h" #include "libc/irq/acpi.internal.h" +#include "libc/str/str.h" #ifdef __x86_64__ +typedef struct { + /* lowest-level NameSeg of the most recent NameString/NamePath */ + uint8_t name_seg[4]; +} AmlParseState; + +textstartup static void _AcpiCheckParseOverrun(const uint8_t *nxt, + const uint8_t *aml_end) { + if (nxt > aml_end) { + ACPI_FATAL("AML: parse overrun @ %p > %p", nxt, aml_end); + } +} + +textstartup static void _AcpiCheckLeadNameChar(const uint8_t *aml, uint8_t c) { + if ((c >= 'A' && c <= 'Z') || c == '_') return; + ACPI_FATAL("AML: bad NameSeg %#x %#x %#x %#x @ %p", + (unsigned)aml[0], (unsigned)aml[1], + (unsigned)aml[2], (unsigned)aml[3], aml); +} + +textstartup static void _AcpiCheckNameChar(const uint8_t *aml, uint8_t c) { + if (c >= '0' && c <= '9') return; + _AcpiCheckLeadNameChar(aml, c); +} + +textstartup static const uint8_t *_AcpiParseNameSeg(AmlParseState *parse, + const uint8_t *aml, + const uint8_t *aml_end) { + const uint8_t *nxt = aml + 4; + uint8_t c0 = aml[0], c1 = aml[1], c2 = aml[2], c3 = aml[3]; + _AcpiCheckLeadNameChar(aml, c0); + _AcpiCheckNameChar(aml, c1); + _AcpiCheckNameChar(aml, c2); + _AcpiCheckNameChar(aml, c3); + parse->name_seg[0] = c0; + parse->name_seg[1] = c1; + parse->name_seg[2] = c2; + parse->name_seg[3] = c3; + return nxt; +} + +textstartup static const uint8_t *_AcpiParseNameString(AmlParseState *parse, + const uint8_t *aml, + const uint8_t *aml_end) +{ + const uint8_t *nxt = aml + 1; + unsigned seg_count; + switch (aml[0]) { + case kAmlNullName: + parse->name_seg[0] = '_'; + parse->name_seg[1] = '_'; + parse->name_seg[2] = '_'; + parse->name_seg[3] = '_'; + return nxt; + case kAmlDualNamePrefix: + nxt = _AcpiParseNameSeg(parse, nxt, aml_end); + nxt = _AcpiParseNameSeg(parse, nxt, aml_end); + return nxt; + case kAmlMultiNamePrefix: + ++nxt; + seg_count = aml[1]; + if (!seg_count) { + ACPI_FATAL("AML: MultiNamePath with zero SegCount @ %p", aml); + } + while (seg_count-- != 0) { + nxt = _AcpiParseNameSeg(parse, nxt, aml_end); + } + return nxt; + case kAmlRootPrefix: + case kAmlParentPrefix: + return _AcpiParseNameString(parse, nxt, aml_end); + default: + return _AcpiParseNameSeg(parse, aml, aml_end); + } +} + +textstartup static const uint8_t *_AcpiParsePkgLength(const uint8_t *aml, + const uint8_t *aml_end, + uint32_t *pkg_length) { + uint8_t lead = aml[0]; + uint32_t pl = 0; + const uint8_t *nxt = aml + 1 + (lead >> 6); + switch (lead >> 6) { + default: + pl = (uint32_t)aml[3] << 20; /* FALLTHRU */ + case 2: + pl |= (uint32_t)aml[2] << 12; /* FALLTHRU */ + case 1: + pl |= (uint32_t)aml[1] << 4; + pl |= lead & 0x0F; + break; + case 0: + pl = lead; + } + if (pkg_length) *pkg_length = pl; + return nxt; +} + +textstartup static const uint8_t *_AcpiParseTermArg(AmlParseState *parse, + const uint8_t *aml, + const uint8_t *aml_end) { + const uint8_t *nxt = aml + 1; + switch (aml[0]) { + case kAmlZeroOp: + case kAmlOneOp: + case kAmlOnesOp: + return nxt; + case kAmlByteOp: + return nxt + 1; + case kAmlWordOp: + return nxt + 2; + case kAmlDwordOp: + return nxt + 4; + case kAmlQwordOp: + return nxt + 8; + case 'A' ... 'Z': + case '_': + case kAmlDualNamePrefix: + case kAmlMultiNamePrefix: + case kAmlRootPrefix: + case kAmlParentPrefix: + /* FIXME? */ + nxt = _AcpiParseNameString(parse, aml, aml_end); + ACPI_WARN("AML: assuming ...%c%c%c%c @ %p not method call", + (int)parse->name_seg[0], (int)parse->name_seg[1], + (int)parse->name_seg[2], (int)parse->name_seg[3], aml); + return nxt; + case kAmlAddOp: + case kAmlConcatOp: + case kAmlSubtractOp: + case kAmlMultiplyOp: + case kAmlShiftLeftOp: + case kAmlShiftRightOp: + case kAmlAndOp: + case kAmlNandOp: + case kAmlOrOp: + case kAmlNorOp: + nxt = _AcpiParseTermArg(parse, nxt, aml_end); + nxt = _AcpiParseTermArg(parse, nxt, aml_end); + nxt = _AcpiParseNameString(parse, nxt, aml_end); + return nxt; + default: + ACPI_FATAL("AML: unknown TermArg type %#x @ %p", (unsigned)aml[0], aml); + } +} + +static const uint8_t *_AcpiParseTermList(AmlParseState *, + const uint8_t *, const uint8_t *); + +textstartup static const uint8_t *_AcpiParseTermObj(AmlParseState *parse, + const uint8_t *aml, + const uint8_t *aml_end) { + const uint8_t *nxt = aml + 1, *scope_end; + uint32_t pl; + _AcpiCheckParseOverrun(nxt, aml_end); + switch (aml[0]) { + case kAmlZeroOp: + case kAmlOneOp: + case kAmlOnesOp: + return nxt; + case kAmlAliasOp: + nxt = _AcpiParseNameString(parse, nxt, aml_end); + nxt = _AcpiParseNameString(parse, nxt, aml_end); + return nxt; + case kAmlNameOp: + nxt = _AcpiParseNameString(parse, nxt, aml_end); + nxt = _AcpiParseTermObj(parse, nxt, aml_end); + return nxt; + case kAmlByteOp: + return nxt + 1; + case kAmlWordOp: + return nxt + 2; + case kAmlDwordOp: + return nxt + 4; + case kAmlStringOp: + while (*nxt) ++nxt; + return nxt + 1; + case kAmlQwordOp: + return nxt + 8; + case kAmlScopeOp: + nxt = _AcpiParsePkgLength(nxt, aml_end, &pl); + scope_end = aml + 1 + pl; + nxt = _AcpiParseNameString(parse, nxt, scope_end); + nxt = _AcpiParseTermList(parse, nxt, scope_end); + return nxt; + case kAmlBufferOp: + case kAmlPackageOp: + case kAmlVariablePackageOp: + case kAmlMethodOp: + _AcpiParsePkgLength(nxt, aml_end, &pl); + /* ignore */ + return nxt + pl; + case kAmlExtendedPrefix: + ++nxt; + switch (aml[1]) { + case kAmlXMutexOp: + nxt = _AcpiParseNameString(parse, nxt, aml_end); + ++nxt; + return nxt; + case kAmlXRegionOp: + nxt = _AcpiParseNameString(parse, nxt, aml_end); + ++nxt; + nxt = _AcpiParseTermArg(parse, nxt, aml_end); + nxt = _AcpiParseTermArg(parse, nxt, aml_end); + return nxt; + case kAmlXFieldOp: + case kAmlXProcessorOp: + case kAmlXPowerResourceOp: + case kAmlXThermalZoneOp: + case kAmlXIndexFieldOp: + _AcpiParsePkgLength(nxt, aml_end, &pl); + /* ignore */ + return nxt + pl; + case kAmlXDeviceOp: + ACPI_INFO("AML: DefDevice @ %p", aml); + _AcpiParsePkgLength(nxt, aml_end, &pl); + /* ignore */ + return nxt + pl; + default: + ACPI_FATAL("AML: unknown TermObj type %#x %#x @ %p", + (unsigned)kAmlExtendedPrefix, (unsigned)aml[1], aml); + } + case kAmlCreateByteFieldOp: + case kAmlCreateWordFieldOp: + case kAmlCreateDwordFieldOp: + case kAmlCreateQwordFieldOp: + nxt = _AcpiParseTermArg(parse, nxt, aml_end); + nxt = _AcpiParseTermArg(parse, nxt, aml_end); + nxt = _AcpiParseNameString(parse, nxt, aml_end); + return nxt; + case kAmlIfOp: + ACPI_WARN("AML: skipping DefIfElse @ %p", aml); + _AcpiParsePkgLength(nxt, aml_end, &pl); + return nxt + pl; + default: + ACPI_FATAL("AML: unknown TermObj type %#x @ %p", (unsigned)aml[0], aml); + } +} + +textstartup static const uint8_t *_AcpiParseTermList(AmlParseState *parse, + const uint8_t *aml, + const uint8_t *aml_end) { + const uint8_t *nxt = aml; + while (nxt != aml_end) { + nxt = _AcpiParseTermObj(parse, nxt, aml_end); + } + return nxt; +} + textstartup static void _AcpiDsdtInit(uintptr_t dsdt_phy) { const AcpiTableDsdt *dsdt; size_t length; + const uint8_t *aml, *aml_end; + AmlParseState parse; if (!dsdt_phy) { ACPI_WARN("FADT: no DSDT"); return; @@ -43,7 +294,12 @@ textstartup static void _AcpiDsdtInit(uintptr_t dsdt_phy) { ACPI_WARN("DSDT: no AML?"); return; } - /* TODO: parse AML to discover hardware configuration */ + length -= offsetof(AcpiTableDsdt, Aml); + aml = dsdt->Aml; + aml_end = aml + length; + ACPI_INFO("AML @ %p,+%#zx", aml, length); + memset(&parse, 0, sizeof(parse)); + _AcpiParseTermList(&parse, aml, aml_end); } textstartup void _AcpiFadtInit(void) { diff --git a/libc/irq/acpi.internal.h b/libc/irq/acpi.internal.h index c3698c339..2d8532b40 100644 --- a/libc/irq/acpi.internal.h +++ b/libc/irq/acpi.internal.h @@ -1,5 +1,6 @@ #ifndef COSMOPOLITAN_LIBC_IRQ_ACPI_INTERNAL_H_ #define COSMOPOLITAN_LIBC_IRQ_ACPI_INTERNAL_H_ +#include "libc/dce.h" #include "libc/intrin/bits.h" #include "libc/intrin/kprintf.h" #include "libc/log/color.internal.h" @@ -38,6 +39,67 @@ #define kAcpiMadtIoApic 1 #define kAcpiMadtIntOverride 2 +/** + * @internal + * ACPI Machine Language (AML) opcodes. + */ +#define kAmlZeroOp 0x00 +#define kAmlOneOp 0x01 +#define kAmlAliasOp 0x06 +#define kAmlNameOp 0x08 +#define kAmlByteOp 0x0A +#define kAmlWordOp 0x0B +#define kAmlDwordOp 0x0C +#define kAmlStringOp 0x0D +#define kAmlQwordOp 0x0E +#define kAmlScopeOp 0x10 +#define kAmlBufferOp 0x11 +#define kAmlPackageOp 0x12 +#define kAmlVariablePackageOp 0x13 +#define kAmlMethodOp 0x14 +#define kAmlExternalOp 0x15 +#define kAmlExtendedPrefix 0x5B +#define kAmlAddOp 0x72 +#define kAmlConcatOp 0x73 +#define kAmlSubtractOp 0x74 +#define kAmlMultiplyOp 0x77 +#define kAmlShiftLeftOp 0x79 +#define kAmlShiftRightOp 0x7A +#define kAmlAndOp 0x7B +#define kAmlNandOp 0x7C +#define kAmlOrOp 0x7D +#define kAmlNorOp 0x7E +#define kAmlCreateDwordFieldOp 0x8A +#define kAmlCreateWordFieldOp 0x8B +#define kAmlCreateByteFieldOp 0x8C +#define kAmlCreateBitFieldOp 0x8D +#define kAmlCreateQwordFieldOp 0x8F +#define kAmlIfOp 0xA0 +#define kAmlOnesOp 0xFF + +/** + * @internal + * ACPI Machine Language (AML) opcodes prefixed with kAmlExtendedPrefix. + */ +#define kAmlXMutexOp 0x01 +#define kAmlXRegionOp 0x80 +#define kAmlXFieldOp 0x81 +#define kAmlXDeviceOp 0x82 +#define kAmlXProcessorOp 0x83 +#define kAmlXPowerResourceOp 0x84 +#define kAmlXThermalZoneOp 0x85 +#define kAmlXIndexFieldOp 0x86 + +/** + * @internal + * ACPI Machine Language (AML) name prefix codes. + */ +#define kAmlNullName 0x00 +#define kAmlDualNamePrefix 0x2E +#define kAmlMultiNamePrefix 0x2F +#define kAmlRootPrefix 0x5C +#define kAmlParentPrefix 0x5E + #if !(__ASSEMBLER__ + __LINKER__ + 0) /** @@ -232,21 +294,25 @@ forceinline AcpiStatus _AcpiGetTable(const char __sig[4], uint32_t __inst, return _AcpiGetTableImpl(READ32LE(__sig_copy), __inst, __phdr); } -#define ACPI_INFO(FMT, ...) \ - do { \ - kprintf("%r%s%s:%d: " FMT "%s\n", \ - SUBTLE, __FILE__, __LINE__, ##__VA_ARGS__, RESET); \ +#define ACPI_INFO(FMT, ...) \ + do { \ + if (!IsTiny()) { \ + kprintf("%r%s%s:%d: " FMT "%s\n", \ + SUBTLE, __FILE__, __LINE__, ##__VA_ARGS__, RESET); \ + } \ } while (0) -#define ACPI_WARN(FMT, ...) \ - do { \ - kprintf("%r%s%s:%d: " FMT "%s\n", \ - BLUE1, __FILE__, __LINE__, ##__VA_ARGS__, RESET); \ +#define ACPI_WARN(FMT, ...) \ + do { \ + if (!IsTiny()) { \ + kprintf("%r%swarn: %s:%d: " FMT "%s\n", \ + BLUE1, __FILE__, __LINE__, ##__VA_ARGS__, RESET); \ + } \ } while (0) -#define ACPI_FATAL(FMT, ...) \ - do { \ - kprintf("%rfatal: %s%s:%d: " FMT "%s\n", \ - RED, __FILE__, __LINE__, ##__VA_ARGS__, RESET); \ - for (;;) asm volatile("cli\n\thlt"); \ +#define ACPI_FATAL(FMT, ...) \ + do { \ + kprintf("%r%sfatal: %s:%d: " FMT "%s\n", \ + RED, __FILE__, __LINE__, ##__VA_ARGS__, RESET); \ + for (;;) asm volatile("cli\n\thlt"); \ } while (0) COSMOPOLITAN_C_END_