Skip to content

Commit

Permalink
[metal] Start parsing some ACPI AML
Browse files Browse the repository at this point in the history
  • Loading branch information
tkchia committed Sep 18, 2023
1 parent 07b999d commit fbc442c
Show file tree
Hide file tree
Showing 3 changed files with 337 additions and 14 deletions.
1 change: 1 addition & 0 deletions examples/vga2.c
Original file line number Diff line number Diff line change
Expand Up @@ -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[]) {
Expand Down
258 changes: 257 additions & 1 deletion libc/irq/acpi-fadt.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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) {
Expand Down
92 changes: 79 additions & 13 deletions libc/irq/acpi.internal.h
Original file line number Diff line number Diff line change
@@ -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"
Expand Down Expand Up @@ -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)

/**
Expand Down Expand Up @@ -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_
Expand Down

0 comments on commit fbc442c

Please sign in to comment.