[metal] Partly parse ACPI Device Packages; extract _HID & _CRS

This commit is contained in:
tkchia 2023-09-20 22:31:11 +00:00
parent 4eba5688ae
commit d0f247f182
3 changed files with 281 additions and 8 deletions

View file

@ -41,3 +41,8 @@ _AcpiBootFlags:
.short kAcpiFadtLegacyDevices | kAcpiFadt8042 .short kAcpiFadtLegacyDevices | kAcpiFadt8042
.endobj _AcpiBootFlags,globl .endobj _AcpiBootFlags,globl
.previous .previous
.bss
_AcpiDefDevices:
.skip 8
.endobj _AcpiDefDevices,globl
.previous

View file

@ -25,14 +25,35 @@
OTHER DEALINGS IN THE SOFTWARE. OTHER DEALINGS IN THE SOFTWARE.
*/ */
#include "libc/dce.h" #include "libc/dce.h"
#include "libc/inttypes.h"
#include "libc/intrin/bswap.h"
#include "libc/irq/acpi.internal.h" #include "libc/irq/acpi.internal.h"
#include "libc/str/str.h" #include "libc/str/str.h"
#ifdef __x86_64__ #ifdef __x86_64__
/*
* Try to semi-correctly parse the ACPI Machine Language (AML) present in
* the Differentiated System Description Table (DSDT), in order to get an
* idea of what the system's hardware configuration is.
*
* (Parsing AML with anywhere close to 100% correctness à la Intel's
* ACPICA requires us to keep track of a _lot_ of information at parse
* time, & does not look quite necessary at the moment. We will see.)
*
* The logic for partially parsing AML is largely based on that of the
* SeaBIOS project, with some additions to handle AML features encountered
* on actual firmwares.
*
* @see libc/irq/acpi.internal.h
* @see src/fw/dsdt_parser.c in SeaBIOS, https://www.seabios.org/Download
*/
typedef struct { typedef struct {
/* lowest-level NameSeg of the most recent NameString/NamePath */ /* lowest-level NameSeg of the most recent NameString/NamePath */
uint8_t name_seg[4]; uint8_t name_seg[4];
/* device structure for current Device Package, or NULL if none */
AcpiDefDevice *dev;
} AmlParseState; } AmlParseState;
textstartup static void _AcpiCheckParseOverrun(const uint8_t *nxt, textstartup static void _AcpiCheckParseOverrun(const uint8_t *nxt,
@ -111,6 +132,7 @@ textstartup static const uint8_t *_AcpiParsePkgLength(const uint8_t *aml,
uint8_t lead = aml[0]; uint8_t lead = aml[0];
uint32_t pl = 0; uint32_t pl = 0;
const uint8_t *nxt = aml + 1 + (lead >> 6); const uint8_t *nxt = aml + 1 + (lead >> 6);
_AcpiCheckParseOverrun(nxt, aml_end);
switch (lead >> 6) { switch (lead >> 6) {
default: default:
pl = (uint32_t)aml[3] << 20; /* FALLTHRU */ pl = (uint32_t)aml[3] << 20; /* FALLTHRU */
@ -171,12 +193,138 @@ textstartup static const uint8_t *_AcpiParseTermArg(AmlParseState *parse,
nxt = _AcpiParseNameString(parse, nxt, aml_end); nxt = _AcpiParseNameString(parse, nxt, aml_end);
return nxt; return nxt;
default: default:
ACPI_FATAL("AML: unknown TermArg type %#x @ %p", (unsigned)aml[0], aml); ACPI_FATAL("AML: weird TermArg type %#x @ %p", (unsigned)aml[0], aml);
} }
} }
textstartup static const uint8_t *_AcpiParseTermArgInt(AmlParseState *parse,
const uint8_t *aml,
const uint8_t *aml_end,
uint64_t *value) {
const uint8_t *nxt = aml + 1;
uint64_t v = 0;
_AcpiCheckParseOverrun(nxt, aml_end);
switch (aml[0]) {
case kAmlZeroOp:
break;
case kAmlOneOp:
v = 1;
break;
case kAmlOnesOp:
--v;
break;
case kAmlByteOp:
v = *nxt++;
break;
case kAmlWordOp:
v = READ16LE(nxt);
nxt += 2;
break;
case kAmlDwordOp:
v = READ32LE(nxt);
nxt += 4;
break;
case kAmlQwordOp:
v = READ64LE(nxt);
nxt += 8;
break;
default:
ACPI_FATAL("AML: weird TermArg => Integer type %#x @ %p",
(unsigned)aml[0], aml);
}
*value = v;
return nxt;
}
static const uint8_t *_AcpiParseTermList(AmlParseState *, static const uint8_t *_AcpiParseTermList(AmlParseState *,
const uint8_t *, const uint8_t *); const uint8_t *, const uint8_t *);
static const uint8_t *_AcpiParseTermObj(AmlParseState *,
const uint8_t *, const uint8_t *);
textstartup static const uint8_t *
_AcpiParseDataObjectHid(AmlParseState *state, const uint8_t *aml,
const uint8_t *aml_end, AcpiDeviceHid *hid) {
const uint8_t *nxt = aml + 1, *end;
uint64_t value;
AcpiDeviceHid id;
switch (aml[0]) {
case kAmlStringOp:
end = nxt;
while (*end) ++end;
*hid = id = _AcpiCompressHid(nxt, end - nxt);
ACPI_INFO("AML: dev _HID \"%!s\"%s", nxt, id ? "" : " (!)");
return end + 1;
case kAmlZeroOp:
case kAmlOneOp:
case kAmlOnesOp:
case kAmlByteOp:
case kAmlWordOp:
case kAmlDwordOp:
case kAmlQwordOp:
nxt = _AcpiParseTermArgInt(state, aml, aml_end, &value);
if (!IsTiny()) {
char str[kAcpiDecompressHidMax];
if (!_AcpiDecompressHid(value, str)) {
ACPI_INFO("AML: dev _HID %#" PRIx64 " (!)", value);
} else {
ACPI_INFO("AML: dev _HID EISA %s", str);
}
}
*hid = value;
return nxt;
default:
ACPI_WARN("AML: weird _HID type %#x @ %p", (unsigned)aml[0], aml);
*hid = 0;
return _AcpiParseTermObj(state, aml, aml_end);
}
}
textstartup static const uint8_t *
_AcpiParseDataObjectCrs(AmlParseState *parse, const uint8_t *aml,
const uint8_t *aml_end, const uint8_t **crs,
size_t *crs_size) {
const uint8_t *nxt = aml + 1, *initer_end;
uint8_t *buf;
uint32_t pl;
uint64_t buf_size;
size_t initer_size;
switch (aml[0]) {
case kAmlBufferOp:
nxt = _AcpiParsePkgLength(nxt, aml_end, &pl);
initer_end = aml + 1 + pl;
nxt = _AcpiParseTermArgInt(parse, nxt, initer_end, &buf_size);
_AcpiCheckParseOverrun(nxt, initer_end);
/*
* The ACPI 6.5 spec § 19.6.10 says about Buffer objects,
* "If the BufferSize is larger than the length of the Initializer,
* the BufferSize is used as the final buffer size. At runtime, the
* AML interpreter will automatically pad zeros to the Initializer to
* match the BufferSize: ...
* "If the BufferSize is smaller than the length of the Initializer,
* the length of the Initializer is used as the buffer size: ..."
*/
initer_size = initer_end - nxt;
ACPI_INFO("AML: dev _CRS @ %p,+%#zx", nxt, initer_size);
if (buf_size <= initer_size) {
buf = (uint8_t *)nxt;
buf_size = initer_size;
} else {
buf = _AcpiOsAllocate(buf_size);
if (!buf) ACPI_FATAL("AML: no memory for _CRS");
memcpy(buf, nxt, initer_size);
memset(buf + initer_size, 0, buf_size - initer_size);
ACPI_INFO("AML: -> %p,+%#zx", buf, (size_t)buf_size);
}
*crs = buf;
*crs_size = buf_size;
return initer_end;
default:
ACPI_WARN("AML: weird _CRS type %#x @ %p", (unsigned)aml[0], aml);
*crs = NULL;
*crs_size = 0;
return _AcpiParseTermObj(parse, aml, aml_end);
}
}
textstartup static const uint8_t *_AcpiParseTermObj(AmlParseState *parse, textstartup static const uint8_t *_AcpiParseTermObj(AmlParseState *parse,
const uint8_t *aml, const uint8_t *aml,
@ -195,7 +343,15 @@ textstartup static const uint8_t *_AcpiParseTermObj(AmlParseState *parse,
return nxt; return nxt;
case kAmlNameOp: case kAmlNameOp:
nxt = _AcpiParseNameString(parse, nxt, aml_end); nxt = _AcpiParseNameString(parse, nxt, aml_end);
nxt = _AcpiParseTermObj(parse, nxt, aml_end); if (parse->dev && READ32LE(parse->name_seg) == READ32LE("_HID")) {
nxt = _AcpiParseDataObjectHid(parse, nxt, aml_end, &parse->dev->hid);
} else if (parse->dev && READ32LE(parse->name_seg) == READ32LE("_CRS")) {
nxt = _AcpiParseDataObjectCrs(parse, nxt, aml_end,
&parse->dev->crs, &parse->dev->crs_size);
} else {
nxt = _AcpiParseTermObj(parse, nxt, aml_end);
}
/* TODO: handle _STA? */
return nxt; return nxt;
case kAmlByteOp: case kAmlByteOp:
return nxt + 1; return nxt + 1;
@ -243,12 +399,36 @@ textstartup static const uint8_t *_AcpiParseTermObj(AmlParseState *parse,
/* ignore */ /* ignore */
return nxt + pl; return nxt + pl;
case kAmlXDeviceOp: case kAmlXDeviceOp:
ACPI_INFO("AML: DefDevice @ %p", aml); {
_AcpiParsePkgLength(nxt, aml_end, &pl); AmlParseState subparse;
/* ignore */ AcpiDefDevice tmp_dev, *dev;
return nxt + pl; nxt = _AcpiParsePkgLength(nxt, aml_end, &pl);
scope_end = aml + 2 + pl;
nxt = _AcpiParseNameString(parse, nxt, scope_end);
ACPI_INFO("AML: DefDevice %c%c%c%c @ %p",
(int)parse->name_seg[0], (int)parse->name_seg[1],
(int)parse->name_seg[2], (int)parse->name_seg[3], aml);
memset(&tmp_dev, 0, sizeof(tmp_dev));
subparse = *parse;
subparse.dev = &tmp_dev;
nxt = _AcpiParseTermList(&subparse, nxt, scope_end);
if (tmp_dev.hid && tmp_dev.crs && tmp_dev.crs_size) {
dev = _AcpiOsAllocate(sizeof(*dev));
if (!dev) {
ACPI_WARN("AML: no memory for %c%c%c%c dev info",
(int)parse->name_seg[0], (int)parse->name_seg[1],
(int)parse->name_seg[2], (int)parse->name_seg[3]);
} else {
*dev = tmp_dev;
dev->next = _AcpiDefDevices;
_AcpiDefDevices = dev;
ACPI_INFO("AML: -> %p", dev);
}
}
}
return nxt;
default: default:
ACPI_FATAL("AML: unknown TermObj type %#x %#x @ %p", ACPI_FATAL("AML: weird TermObj type %#x %#x @ %p",
(unsigned)kAmlExtendedPrefix, (unsigned)aml[1], aml); (unsigned)kAmlExtendedPrefix, (unsigned)aml[1], aml);
} }
case kAmlCreateByteFieldOp: case kAmlCreateByteFieldOp:
@ -264,7 +444,7 @@ textstartup static const uint8_t *_AcpiParseTermObj(AmlParseState *parse,
_AcpiParsePkgLength(nxt, aml_end, &pl); _AcpiParsePkgLength(nxt, aml_end, &pl);
return nxt + pl; return nxt + pl;
default: default:
ACPI_FATAL("AML: unknown TermObj type %#x @ %p", (unsigned)aml[0], aml); ACPI_FATAL("AML: weird TermObj type %#x @ %p", (unsigned)aml[0], aml);
} }
} }

View file

@ -2,6 +2,7 @@
#define COSMOPOLITAN_LIBC_IRQ_ACPI_INTERNAL_H_ #define COSMOPOLITAN_LIBC_IRQ_ACPI_INTERNAL_H_
#include "libc/dce.h" #include "libc/dce.h"
#include "libc/intrin/bits.h" #include "libc/intrin/bits.h"
#include "libc/intrin/bswap.h"
#include "libc/intrin/kprintf.h" #include "libc/intrin/kprintf.h"
#include "libc/log/color.internal.h" #include "libc/log/color.internal.h"
@ -270,6 +271,23 @@ typedef struct thatispacked {
uint8_t Aml[]; uint8_t Aml[];
} AcpiTableDsdt; } AcpiTableDsdt;
/**
* @internal
* Private type for a device's hardware id.
*/
typedef uint64_t AcpiDeviceHid;
/**
* @internal
* Private information about a device defined by an ACPI Device Package.
*/
typedef struct AcpiDefDevice {
struct AcpiDefDevice *next;
AcpiDeviceHid hid;
const uint8_t *crs;
size_t crs_size;
} AcpiDefDevice;
typedef uint32_t AcpiStatus; typedef uint32_t AcpiStatus;
extern size_t _AcpiXsdtNumEntries, _AcpiNumIoApics; extern size_t _AcpiXsdtNumEntries, _AcpiNumIoApics;
@ -277,6 +295,7 @@ extern void **_AcpiXsdtEntries;
extern uint16_t _AcpiBootFlags; extern uint16_t _AcpiBootFlags;
extern uint32_t _AcpiMadtFlags; extern uint32_t _AcpiMadtFlags;
extern const AcpiMadtIoApic **_AcpiIoApics; extern const AcpiMadtIoApic **_AcpiIoApics;
extern AcpiDefDevice *_AcpiDefDevices;
extern void *_AcpiOsMapUncachedMemory(uintptr_t, size_t); extern void *_AcpiOsMapUncachedMemory(uintptr_t, size_t);
extern void *_AcpiOsAllocate(size_t); extern void *_AcpiOsAllocate(size_t);
@ -294,6 +313,75 @@ forceinline AcpiStatus _AcpiGetTable(const char __sig[4], uint32_t __inst,
return _AcpiGetTableImpl(READ32LE(__sig_copy), __inst, __phdr); return _AcpiGetTableImpl(READ32LE(__sig_copy), __inst, __phdr);
} }
/**
* @internal
* Converts a hardware id for an EISA device from string form to numeric
* form. If the HID is not an EISA type id, return 0.
*
* TODO: when the need arises, find a reasonable way to "compress" ACPI &
* PCI device ids as well.
*/
forceinline AcpiDeviceHid
_AcpiCompressHid(const uint8_t *__hid, size_t __len) {
uint8_t __c;
unsigned __a0, __a1, __a2, __d0, __d1, __d2, __d3;
uint32_t __evalu;
if (__len != 7) return 0;
__c = __hid[0];
if (__c < 'A' || __c > 'Z') return 0;
__a0 = __c - 'A' + 1;
__c = __hid[1];
if (__c < 'A' || __c > 'Z') return 0;
__a1 = __c - 'A' + 1;
__c = __hid[2];
if (__c < 'A' || __c > 'Z') return 0;
__a2 = __c - 'A' + 1;
__c = __hid[3];
if (__c < '0' || __c > 'F' || (__c > '9' && __c < 'A')) return 0;
__d0 = __c <= '9' ? __c - '0' : __c - 'A' + 0xA;
__c = __hid[4];
if (__c < '0' || __c > 'F' || (__c > '9' && __c < 'A')) return 0;
__d1 = __c <= '9' ? __c - '0' : __c - 'A' + 0xA;
__c = __hid[5];
if (__c < '0' || __c > 'F' || (__c > '9' && __c < 'A')) return 0;
__d2 = __c <= '9' ? __c - '0' : __c - 'A' + 0xA;
__c = __hid[6];
if (__c < '0' || __c > 'F' || (__c > '9' && __c < 'A')) return 0;
__d3 = __c <= '9' ? __c - '0' : __c - 'A' + 0xA;
__evalu = (uint32_t)__a0 << 26 | (uint32_t)__a1 << 21 | (uint32_t)__a2 << 16 |
__d0 << 12 | __d1 << 8 | __d2 << 4 | __d3;
return bswap_32(__evalu);
}
#define kAcpiDecompressHidMax 8
forceinline bool _AcpiDecompressHid(uintmax_t __hid,
char __str[kAcpiDecompressHidMax]) {
uint32_t __evalu;
unsigned char __a0, __a1, __a2, __d0, __d1, __d2, __d3;
if (__hid >= (1ul << 31)) return false;
__evalu = bswap_32((uint32_t)__hid);
__a0 = (__evalu >> 26) & 0x1F;
if (!__a0 || __a0 > 26) return false;
__a1 = (__evalu >> 21) & 0x1F;
if (!__a1 || __a1 > 26) return false;
__a2 = (__evalu >> 16) & 0x1F;
if (!__a2 || __a2 > 26) return false;
__d0 = (__evalu >> 12) & 0xF;
__d1 = (__evalu >> 8) & 0xF;
__d2 = (__evalu >> 4) & 0xF;
__d3 = (__evalu >> 0) & 0xF;
__str[0] = __a0 - 1 + 'A';
__str[1] = __a1 - 1 + 'A';
__str[2] = __a2 - 1 + 'A';
__str[3] = __d0 <= 9 ? __d0 + '0' : __d0 - 0xA + 'A';
__str[4] = __d1 <= 9 ? __d1 + '0' : __d1 - 0xA + 'A';
__str[5] = __d2 <= 9 ? __d2 + '0' : __d2 - 0xA + 'A';
__str[6] = __d3 <= 9 ? __d3 + '0' : __d3 - 0xA + 'A';
__str[7] = 0;
return true;
}
#define ACPI_INFO(FMT, ...) \ #define ACPI_INFO(FMT, ...) \
do { \ do { \
if (!IsTiny()) { \ if (!IsTiny()) { \