mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-08-07 18:30:28 +00:00
[metal] Partly parse ACPI Device Packages; extract _HID & _CRS
This commit is contained in:
parent
4eba5688ae
commit
d0f247f182
3 changed files with 281 additions and 8 deletions
|
@ -41,3 +41,8 @@ _AcpiBootFlags:
|
|||
.short kAcpiFadtLegacyDevices | kAcpiFadt8042
|
||||
.endobj _AcpiBootFlags,globl
|
||||
.previous
|
||||
.bss
|
||||
_AcpiDefDevices:
|
||||
.skip 8
|
||||
.endobj _AcpiDefDevices,globl
|
||||
.previous
|
||||
|
|
|
@ -25,14 +25,35 @@
|
|||
│ OTHER DEALINGS IN THE SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/dce.h"
|
||||
#include "libc/inttypes.h"
|
||||
#include "libc/intrin/bswap.h"
|
||||
#include "libc/irq/acpi.internal.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
#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 {
|
||||
/* lowest-level NameSeg of the most recent NameString/NamePath */
|
||||
uint8_t name_seg[4];
|
||||
/* device structure for current Device Package, or NULL if none */
|
||||
AcpiDefDevice *dev;
|
||||
} AmlParseState;
|
||||
|
||||
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];
|
||||
uint32_t pl = 0;
|
||||
const uint8_t *nxt = aml + 1 + (lead >> 6);
|
||||
_AcpiCheckParseOverrun(nxt, aml_end);
|
||||
switch (lead >> 6) {
|
||||
default:
|
||||
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);
|
||||
return nxt;
|
||||
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 *,
|
||||
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,
|
||||
const uint8_t *aml,
|
||||
|
@ -195,7 +343,15 @@ textstartup static const uint8_t *_AcpiParseTermObj(AmlParseState *parse,
|
|||
return nxt;
|
||||
case kAmlNameOp:
|
||||
nxt = _AcpiParseNameString(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;
|
||||
case kAmlByteOp:
|
||||
return nxt + 1;
|
||||
|
@ -243,12 +399,36 @@ textstartup static const uint8_t *_AcpiParseTermObj(AmlParseState *parse,
|
|||
/* ignore */
|
||||
return nxt + pl;
|
||||
case kAmlXDeviceOp:
|
||||
ACPI_INFO("AML: DefDevice @ %p", aml);
|
||||
_AcpiParsePkgLength(nxt, aml_end, &pl);
|
||||
/* ignore */
|
||||
return nxt + pl;
|
||||
{
|
||||
AmlParseState subparse;
|
||||
AcpiDefDevice tmp_dev, *dev;
|
||||
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:
|
||||
ACPI_FATAL("AML: unknown TermObj type %#x %#x @ %p",
|
||||
ACPI_FATAL("AML: weird TermObj type %#x %#x @ %p",
|
||||
(unsigned)kAmlExtendedPrefix, (unsigned)aml[1], aml);
|
||||
}
|
||||
case kAmlCreateByteFieldOp:
|
||||
|
@ -264,7 +444,7 @@ textstartup static const uint8_t *_AcpiParseTermObj(AmlParseState *parse,
|
|||
_AcpiParsePkgLength(nxt, aml_end, &pl);
|
||||
return nxt + pl;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#define COSMOPOLITAN_LIBC_IRQ_ACPI_INTERNAL_H_
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/bits.h"
|
||||
#include "libc/intrin/bswap.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/log/color.internal.h"
|
||||
|
||||
|
@ -270,6 +271,23 @@ typedef struct thatispacked {
|
|||
uint8_t Aml[];
|
||||
} 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;
|
||||
|
||||
extern size_t _AcpiXsdtNumEntries, _AcpiNumIoApics;
|
||||
|
@ -277,6 +295,7 @@ extern void **_AcpiXsdtEntries;
|
|||
extern uint16_t _AcpiBootFlags;
|
||||
extern uint32_t _AcpiMadtFlags;
|
||||
extern const AcpiMadtIoApic **_AcpiIoApics;
|
||||
extern AcpiDefDevice *_AcpiDefDevices;
|
||||
|
||||
extern void *_AcpiOsMapUncachedMemory(uintptr_t, 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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, ...) \
|
||||
do { \
|
||||
if (!IsTiny()) { \
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue