acpi: add dsdt parser
Create a list of devices found in the DSDT table. Add helper functions to find devices, walk the list and figure device informations like mmio ranges and irqs. Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
This commit is contained in:
parent
2abf972ab6
commit
654ba383b6
2
Makefile
2
Makefile
|
@ -42,7 +42,7 @@ SRC32FLAT=$(SRCBOTH) post.c e820map.c malloc.c romfile.c x86.c optionroms.c \
|
|||
hw/pcidevice.c hw/ahci.c hw/pvscsi.c hw/usb-xhci.c hw/usb-hub.c hw/sdcard.c \
|
||||
fw/coreboot.c fw/lzmadecode.c fw/multiboot.c fw/csm.c fw/biostables.c \
|
||||
fw/paravirt.c fw/shadow.c fw/pciinit.c fw/smm.c fw/smp.c fw/mtrr.c fw/xen.c \
|
||||
fw/acpi.c fw/mptable.c fw/pirtable.c fw/smbios.c fw/romfile_loader.c \
|
||||
fw/acpi.c fw/mptable.c fw/pirtable.c fw/smbios.c fw/romfile_loader.c fw/dsdt_parser.c \
|
||||
hw/virtio-ring.c hw/virtio-pci.c hw/virtio-mmio.c hw/virtio-blk.c hw/virtio-scsi.c \
|
||||
hw/tpm_drivers.c hw/nvme.c
|
||||
SRC32SEG=string.c output.c pcibios.c apm.c stacks.c hw/pci.c hw/serialio.c
|
||||
|
|
|
@ -524,6 +524,13 @@ menu "BIOS Tables"
|
|||
This option can be disabled for QEMU 1.6 and older
|
||||
to save some space in the ROM file.
|
||||
If unsure, say Y.
|
||||
config ACPI_PARSE
|
||||
bool "Include ACPI DSDT parser."
|
||||
default y
|
||||
help
|
||||
Support parsing ACPI DSDT for device probing.
|
||||
Needed to find virtio-mmio devices.
|
||||
If unsure, say Y.
|
||||
endmenu
|
||||
|
||||
source vgasrc/Kconfig
|
||||
|
|
|
@ -256,6 +256,7 @@ find_acpi_features(void)
|
|||
void *p = fadt;
|
||||
acpi_set_reset_reg(p + 116, *(u8 *)(p + 128));
|
||||
}
|
||||
acpi_dsdt_parse();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,668 @@
|
|||
// Support for parsing dsdt acpi tables
|
||||
//
|
||||
// Copyright (C) 2008,2009 Kevin O'Connor <kevin@koconnor.net>
|
||||
//
|
||||
// This file may be distributed under the terms of the GNU LGPLv3 license.
|
||||
|
||||
#include "config.h" // CONFIG_*
|
||||
#include "list.h" // hlist_*
|
||||
#include "malloc.h" // malloc_*
|
||||
#include "output.h" // dprintf
|
||||
#include "string.h" // memcpy
|
||||
#include "util.h"
|
||||
#include "std/acpi.h" // struct rsdp_descriptor
|
||||
|
||||
/****************************************************************
|
||||
* DSDT parser
|
||||
****************************************************************/
|
||||
|
||||
struct acpi_device {
|
||||
struct hlist_node node;
|
||||
char name[16];
|
||||
u8 *hid_aml;
|
||||
u8 *sta_aml;
|
||||
u8 *crs_data;
|
||||
int crs_size;
|
||||
};
|
||||
static struct hlist_head acpi_devices VARVERIFY32INIT;
|
||||
static const int parse_dumpdevs = 0;
|
||||
|
||||
struct parse_state {
|
||||
char name[32];
|
||||
struct acpi_device *dev;
|
||||
int error;
|
||||
int depth;
|
||||
};
|
||||
|
||||
static void parse_termlist(struct parse_state *s,
|
||||
u8 *ptr, int offset, int pkglength);
|
||||
|
||||
static void hex(const u8 *ptr, int count, int lvl, const char *item)
|
||||
{
|
||||
int l = 0, i;
|
||||
|
||||
do {
|
||||
dprintf(lvl, "%s: %04x: ", item, l);
|
||||
for (i = l; i < l+16; i += 4)
|
||||
dprintf(lvl, "%02x %02x %02x %02x ",
|
||||
ptr[i+0], ptr[i+1], ptr[i+2], ptr[i+3]);
|
||||
for (i = l; i < l+16; i++)
|
||||
dprintf(lvl, "%c", (ptr[i] > 0x20 && ptr[i] < 0x80)
|
||||
? ptr[i] : '.');
|
||||
dprintf(lvl, "\n");
|
||||
l += 16;
|
||||
} while (l < count);
|
||||
}
|
||||
|
||||
static u64 parse_resource_int(u8 *ptr, int count)
|
||||
{
|
||||
u64 value = 0;
|
||||
int index = 0;
|
||||
|
||||
for (index = 0; index < count; index++)
|
||||
value |= (u64)ptr[index] << (index * 8);
|
||||
return value;
|
||||
}
|
||||
|
||||
static int parse_resource_bit(u8 *ptr, int count)
|
||||
{
|
||||
int bit;
|
||||
|
||||
for (bit = 0; bit < count*8; bit++)
|
||||
if (ptr[bit/8] & (1 << (bit%8)))
|
||||
return bit;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_resource(u8 *ptr, int length, int *type, u64 *min, u64 *max)
|
||||
{
|
||||
int rname, rsize;
|
||||
u64 len;
|
||||
|
||||
*type = -1;
|
||||
*min = 0;
|
||||
*max = 0;
|
||||
len = 0;
|
||||
if (!(ptr[0] & 0x80)) {
|
||||
/* small resource */
|
||||
rname = (ptr[0] >> 3) & 0x0f;
|
||||
rsize = ptr[0] & 0x07;
|
||||
rsize++;
|
||||
switch (rname) {
|
||||
case 0x04: /* irq */
|
||||
*min = parse_resource_bit(ptr + 1, rsize);
|
||||
*max = *min;
|
||||
*type = 3;
|
||||
break;
|
||||
case 0x0f: /* end marker */
|
||||
return 0;
|
||||
case 0x08: /* io */
|
||||
*min = parse_resource_int(ptr + 2, 2);
|
||||
*max = parse_resource_int(ptr + 4, 2);
|
||||
if (*min == *max) {
|
||||
*max = *min + ptr[7] - 1;
|
||||
*type = 1;
|
||||
}
|
||||
break;
|
||||
case 0x09: /* fixed io */
|
||||
*min = parse_resource_int(ptr + 2, 2);
|
||||
*max = *min + ptr[4] - 1;
|
||||
*type = 1;
|
||||
break;
|
||||
default:
|
||||
dprintf(3, "%s: small: 0x%x (len %d)\n",
|
||||
__func__, rname, rsize);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* large resource */
|
||||
rname = ptr[0] & 0x7f;
|
||||
rsize = ptr[2] << 8 | ptr[1];
|
||||
rsize += 3;
|
||||
switch (rname) {
|
||||
case 0x06: /* 32-bit Fixed Location Memory Range Descriptor */
|
||||
*min = parse_resource_int(ptr + 4, 4);
|
||||
len = parse_resource_int(ptr + 8, 4);
|
||||
*max = *min + len - 1;
|
||||
*type = 0;
|
||||
break;
|
||||
case 0x07: /* DWORD Address Space Descriptor */
|
||||
*min = parse_resource_int(ptr + 10, 4);
|
||||
*max = parse_resource_int(ptr + 14, 4);
|
||||
*type = ptr[3];
|
||||
break;
|
||||
case 0x08: /* WORD Address Space Descriptor */
|
||||
*min = parse_resource_int(ptr + 8, 2);
|
||||
*max = parse_resource_int(ptr + 10, 2);
|
||||
*type = ptr[3];
|
||||
break;
|
||||
case 0x09: /* irq */
|
||||
*min = parse_resource_int(ptr + 5, 4);
|
||||
*max = *min;
|
||||
*type = 3;
|
||||
break;
|
||||
case 0x0a: /* QWORD Address Space Descriptor */
|
||||
*min = parse_resource_int(ptr + 14, 8);
|
||||
*max = parse_resource_int(ptr + 22, 8);
|
||||
*type = ptr[3];
|
||||
break;
|
||||
default:
|
||||
dprintf(3, "%s: large: 0x%x (len %d)\n", __func__, rname, rsize);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return rsize;
|
||||
}
|
||||
|
||||
static int find_resource(u8 *ptr, int len, int kind, u64 *min, u64 *max)
|
||||
{
|
||||
int type, size, offset = 0;
|
||||
|
||||
do {
|
||||
size = parse_resource(ptr + offset, len - offset,
|
||||
&type, min, max);
|
||||
if (kind == type)
|
||||
return 0;
|
||||
offset += size;
|
||||
} while (size > 0 && offset < len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int print_resources(const char *prefix, u8 *ptr, int len)
|
||||
{
|
||||
static const char *typename[] = { "mem", "i/o", "bus" };
|
||||
int type, size, offset = 0;
|
||||
u64 min, max;
|
||||
|
||||
do {
|
||||
size = parse_resource(ptr + offset, len - offset,
|
||||
&type, &min, &max);
|
||||
switch (type) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
dprintf(1, "%s%s 0x%llx -> 0x%llx\n",
|
||||
prefix, typename[type], min, max);
|
||||
break;
|
||||
case 3:
|
||||
dprintf(1, "%sirq %lld\n", prefix, min);
|
||||
break;
|
||||
}
|
||||
offset += size;
|
||||
} while (size > 0 && offset < len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int parse_nameseg(u8 *ptr, char **dst)
|
||||
{
|
||||
if (dst && *dst) {
|
||||
*(dst[0]++) = ptr[0];
|
||||
if (ptr[1] != '_')
|
||||
*(dst[0]++) = ptr[1];
|
||||
if (ptr[2] != '_')
|
||||
*(dst[0]++) = ptr[2];
|
||||
if (ptr[3] != '_')
|
||||
*(dst[0]++) = ptr[3];
|
||||
*(dst[0]) = 0;
|
||||
}
|
||||
return 4;
|
||||
}
|
||||
|
||||
static int parse_namestring(struct parse_state *s,
|
||||
u8 *ptr, const char *item)
|
||||
{
|
||||
char *dst = s->name;
|
||||
int offset = 0;
|
||||
int i, count;
|
||||
|
||||
for (;;) {
|
||||
switch (ptr[offset]) {
|
||||
case 0: /* null name */
|
||||
offset++;
|
||||
*(dst++) = 0;
|
||||
break;
|
||||
case 0x2e:
|
||||
offset++;
|
||||
offset += parse_nameseg(ptr + offset, &dst);
|
||||
*(dst++) = '.';
|
||||
offset += parse_nameseg(ptr + offset, &dst);
|
||||
break;
|
||||
case 0x2f:
|
||||
offset++;
|
||||
count = ptr[offset];
|
||||
offset++;
|
||||
for (i = 0; i < count; i++) {
|
||||
if (i)
|
||||
*(dst++) = '.';
|
||||
offset += parse_nameseg(ptr + offset, &dst);
|
||||
}
|
||||
break;
|
||||
case '\\':
|
||||
*(dst++) = '\\';
|
||||
offset++;
|
||||
continue;
|
||||
case '^':
|
||||
*(dst++) = '^';
|
||||
offset++;
|
||||
continue;
|
||||
case 'A' ... 'Z':
|
||||
case '_':
|
||||
offset += parse_nameseg(ptr, &dst);
|
||||
break;
|
||||
default:
|
||||
hex(ptr, 16, 3, __func__);
|
||||
s->error = 1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
dprintf(5, "%s: %d %s '%s'\n", __func__, s->depth,
|
||||
item, s->name);
|
||||
return offset;
|
||||
}
|
||||
|
||||
static int parse_termarg_int(u8 *ptr, int *error, u64 *dst)
|
||||
{
|
||||
u64 value;
|
||||
int offset = 1;
|
||||
|
||||
switch (ptr[0]) {
|
||||
case 0x00: /* zero */
|
||||
value = 0;
|
||||
break;
|
||||
case 0x01: /* one */
|
||||
value = 1;
|
||||
break;
|
||||
case 0x0a: /* byte prefix */
|
||||
value = ptr[1];
|
||||
offset++;
|
||||
break;
|
||||
case 0x0b: /* word prefix */
|
||||
value = ptr[1] |
|
||||
((unsigned long)ptr[2] << 8);
|
||||
offset += 2;
|
||||
break;
|
||||
case 0x0c: /* dword prefix */
|
||||
value = ptr[1] |
|
||||
((unsigned long)ptr[2] << 8) |
|
||||
((unsigned long)ptr[3] << 16) |
|
||||
((unsigned long)ptr[4] << 24);
|
||||
offset += 4;
|
||||
break;
|
||||
default:
|
||||
value = 0;
|
||||
hex(ptr, 16, 3, __func__);
|
||||
if (error)
|
||||
*error = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (dst)
|
||||
*dst = value;
|
||||
dprintf(5, "%s: 0x%llx\n", __func__, value);
|
||||
return offset;
|
||||
}
|
||||
|
||||
static int parse_pkglength(u8 *ptr, int *pkglength)
|
||||
{
|
||||
int offset = 2;
|
||||
|
||||
*pkglength = 0;
|
||||
switch (ptr[0] >> 6) {
|
||||
case 3:
|
||||
*pkglength |= ptr[3] << 20;
|
||||
offset++;
|
||||
case 2:
|
||||
*pkglength |= ptr[2] << 12;
|
||||
offset++;
|
||||
case 1:
|
||||
*pkglength |= ptr[1] << 4;
|
||||
*pkglength |= ptr[0] & 0x0f;
|
||||
return offset;
|
||||
case 0:
|
||||
default:
|
||||
*pkglength |= ptr[0] & 0x3f;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static int parse_pkg_common(struct parse_state *s,
|
||||
u8 *ptr, const char *item, int *pkglength)
|
||||
{
|
||||
int offset;
|
||||
|
||||
offset = parse_pkglength(ptr, pkglength);
|
||||
offset += parse_namestring(s, ptr + offset, item);
|
||||
return offset;
|
||||
}
|
||||
|
||||
static int parse_pkg_scope(struct parse_state *s,
|
||||
u8 *ptr)
|
||||
{
|
||||
int offset, pkglength;
|
||||
|
||||
offset = parse_pkg_common(s, ptr, "scope", &pkglength);
|
||||
parse_termlist(s, ptr, offset, pkglength);
|
||||
return pkglength;
|
||||
}
|
||||
|
||||
static int parse_pkg_device(struct parse_state *s,
|
||||
u8 *ptr)
|
||||
{
|
||||
int offset, pkglength;
|
||||
|
||||
offset = parse_pkg_common(s, ptr, "device", &pkglength);
|
||||
|
||||
s->dev = malloc_tmp(sizeof(struct acpi_device));
|
||||
if (!s->dev) {
|
||||
warn_noalloc();
|
||||
s->error = 1;
|
||||
return pkglength;
|
||||
}
|
||||
|
||||
memset(s->dev, 0, sizeof(struct acpi_device));
|
||||
hlist_add_head(&s->dev->node, &acpi_devices);
|
||||
strtcpy(s->dev->name, s->name, sizeof(s->name));
|
||||
parse_termlist(s, ptr, offset, pkglength);
|
||||
s->dev = NULL;
|
||||
|
||||
return pkglength;
|
||||
}
|
||||
|
||||
static int parse_pkg_buffer(struct parse_state *s,
|
||||
u8 *ptr)
|
||||
{
|
||||
u64 blen;
|
||||
int pkglength, offset;
|
||||
|
||||
offset = parse_pkglength(ptr, &pkglength);
|
||||
offset += parse_termarg_int(ptr + offset, &s->error, &blen);
|
||||
if (s->dev && strcmp(s->name, "_CRS") == 0) {
|
||||
s->dev->crs_data = ptr + offset;
|
||||
s->dev->crs_size = blen;
|
||||
}
|
||||
return pkglength;
|
||||
}
|
||||
|
||||
static int parse_pkg_skip(struct parse_state *s,
|
||||
u8 *ptr, int op, int name)
|
||||
{
|
||||
int pkglength, offset;
|
||||
char item[8];
|
||||
|
||||
snprintf(item, sizeof(item), "op %x", op);
|
||||
offset = parse_pkglength(ptr, &pkglength);
|
||||
if (name) {
|
||||
parse_namestring(s, ptr + offset, item);
|
||||
} else {
|
||||
dprintf(5, "%s: %s (%d)\n", __func__, item, pkglength);
|
||||
}
|
||||
return pkglength;
|
||||
}
|
||||
|
||||
static int parse_termobj(struct parse_state *s,
|
||||
u8 *ptr)
|
||||
{
|
||||
int offset = 1;
|
||||
|
||||
if (s->depth == 16) {
|
||||
dprintf(1, "%s: deep recursion\n", __func__);
|
||||
s->error = 1;
|
||||
return offset;
|
||||
}
|
||||
|
||||
s->depth++;
|
||||
switch (ptr[0]) {
|
||||
case 0x00: /* zero */
|
||||
break;
|
||||
case 0x01: /* one */
|
||||
break;
|
||||
case 0x08: /* name op */
|
||||
offset += parse_namestring(s, ptr + offset, "name");
|
||||
offset += parse_termobj(s, ptr + offset);
|
||||
if (s->dev && strcmp(s->name, "_HID") == 0)
|
||||
s->dev->hid_aml = ptr;
|
||||
if (s->dev && strcmp(s->name, "_STA") == 0)
|
||||
s->dev->sta_aml = ptr;
|
||||
break;
|
||||
case 0x0a: /* byte prefix */
|
||||
offset++;
|
||||
break;
|
||||
case 0x0b: /* word prefix */
|
||||
offset += 2;
|
||||
break;
|
||||
case 0x0c: /* dword prefix */
|
||||
offset += 4;
|
||||
break;
|
||||
case 0x0d: /* string prefix */
|
||||
while (ptr[offset])
|
||||
offset++;
|
||||
offset++;
|
||||
break;
|
||||
case 0x10: /* scope op */
|
||||
offset += parse_pkg_scope(s, ptr + offset);
|
||||
break;
|
||||
case 0x11: /* buffer op */
|
||||
offset += parse_pkg_buffer(s, ptr + offset);
|
||||
break;
|
||||
case 0x12: /* package op */
|
||||
case 0x13: /* var package op */
|
||||
offset += parse_pkg_skip(s, ptr + offset, ptr[0], 0);
|
||||
break;
|
||||
case 0x14: /* method op */
|
||||
offset += parse_pkg_skip(s, ptr + offset, ptr[0], 1);
|
||||
if (s->dev && strcmp(s->name, "_STA") == 0)
|
||||
s->dev->sta_aml = ptr;
|
||||
break;
|
||||
case 0x5b: /* ext op prefix */
|
||||
offset++;
|
||||
switch (ptr[1]) {
|
||||
case 0x01: /* mutex op */
|
||||
offset += parse_namestring(s, ptr + offset, "mutex");
|
||||
offset++; /* sync flags */
|
||||
break;
|
||||
case 0x80: /* op region op */
|
||||
offset += parse_namestring(s, ptr + offset, "op region");
|
||||
offset++; /* region space */
|
||||
offset += parse_termarg_int(ptr + offset, &s->error, NULL);
|
||||
offset += parse_termarg_int(ptr + offset, &s->error, NULL);
|
||||
break;
|
||||
case 0x81: /* field op */
|
||||
case 0x83: /* processor op */
|
||||
case 0x84: /* power resource op */
|
||||
case 0x85: /* thermal zone op */
|
||||
offset += parse_pkg_skip(s, ptr + offset, 0x5b00 | ptr[1], 1);
|
||||
break;
|
||||
case 0x82: /* device op */
|
||||
offset += parse_pkg_device(s, ptr + offset);
|
||||
break;
|
||||
default:
|
||||
hex(ptr, 16, 3, __func__);
|
||||
s->error = 1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
hex(ptr, 16, 3, __func__);
|
||||
s->error = 1;
|
||||
break;
|
||||
}
|
||||
s->depth--;
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
static void parse_termlist(struct parse_state *s,
|
||||
u8 *ptr, int offset, int pkglength)
|
||||
{
|
||||
for (;;) {
|
||||
offset += parse_termobj(s, ptr + offset);
|
||||
if (offset == pkglength)
|
||||
return;
|
||||
if (offset > pkglength) {
|
||||
dprintf(1, "%s: overrun: %d/%d\n", __func__,
|
||||
offset, pkglength);
|
||||
s->error = 1;
|
||||
return;
|
||||
}
|
||||
if (s->error) {
|
||||
dprintf(1, "%s: parse error, skip from %d/%d\n", __func__,
|
||||
offset, pkglength);
|
||||
s->error = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static struct acpi_device *acpi_dsdt_find(struct acpi_device *prev,
|
||||
const u8 *aml, int size)
|
||||
{
|
||||
struct acpi_device *dev;
|
||||
struct hlist_node *node;
|
||||
|
||||
if (!prev)
|
||||
node = acpi_devices.first;
|
||||
else
|
||||
node = prev->node.next;
|
||||
|
||||
for (; node != NULL; node = dev->node.next) {
|
||||
dev = container_of(node, struct acpi_device, node);
|
||||
if (!aml)
|
||||
return dev;
|
||||
if (!dev->hid_aml)
|
||||
continue;
|
||||
if (memcmp(dev->hid_aml + 5, aml, size) == 0)
|
||||
return dev;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int acpi_dsdt_present(struct acpi_device *dev)
|
||||
{
|
||||
if (!dev)
|
||||
return 0; /* no */
|
||||
if (!dev->sta_aml)
|
||||
return 1; /* yes */
|
||||
if (dev->sta_aml[0] == 0x14)
|
||||
return -1; /* unknown (can't evaluate method) */
|
||||
if (dev->sta_aml[0] == 0x08) {
|
||||
u64 value = 0;
|
||||
parse_termarg_int(dev->sta_aml + 5, NULL, &value);
|
||||
if (value == 0)
|
||||
return 0; /* no */
|
||||
else
|
||||
return 1; /* yes */
|
||||
}
|
||||
return -1; /* unknown (should not happen) */
|
||||
}
|
||||
|
||||
/****************************************************************
|
||||
* DSDT parser, public interface
|
||||
****************************************************************/
|
||||
|
||||
struct acpi_device *acpi_dsdt_find_string(struct acpi_device *prev,
|
||||
const char *hid)
|
||||
{
|
||||
if (!CONFIG_ACPI_PARSE)
|
||||
return NULL;
|
||||
|
||||
u8 aml[10];
|
||||
int len = snprintf((char*)aml, sizeof(aml), "\x0d%s", hid);
|
||||
return acpi_dsdt_find(prev, aml, len);
|
||||
}
|
||||
|
||||
struct acpi_device *acpi_dsdt_find_eisaid(struct acpi_device *prev, u16 eisaid)
|
||||
{
|
||||
if (!CONFIG_ACPI_PARSE)
|
||||
return NULL;
|
||||
u8 aml[] = {
|
||||
0x0c, 0x41, 0xd0,
|
||||
eisaid >> 8,
|
||||
eisaid & 0xff
|
||||
};
|
||||
return acpi_dsdt_find(prev, aml, 5);
|
||||
}
|
||||
|
||||
char *acpi_dsdt_name(struct acpi_device *dev)
|
||||
{
|
||||
if (!CONFIG_ACPI_PARSE || !dev)
|
||||
return NULL;
|
||||
return dev->name;
|
||||
}
|
||||
|
||||
int acpi_dsdt_find_io(struct acpi_device *dev, u64 *min, u64 *max)
|
||||
{
|
||||
if (!CONFIG_ACPI_PARSE || !dev || !dev->crs_data)
|
||||
return -1;
|
||||
return find_resource(dev->crs_data, dev->crs_size,
|
||||
1 /* I/O */, min, max);
|
||||
}
|
||||
|
||||
int acpi_dsdt_find_mem(struct acpi_device *dev, u64 *min, u64 *max)
|
||||
{
|
||||
if (!CONFIG_ACPI_PARSE || !dev || !dev->crs_data)
|
||||
return -1;
|
||||
return find_resource(dev->crs_data, dev->crs_size,
|
||||
0 /* mem */, min, max);
|
||||
}
|
||||
|
||||
int acpi_dsdt_find_irq(struct acpi_device *dev, u64 *irq)
|
||||
{
|
||||
u64 max;
|
||||
if (!CONFIG_ACPI_PARSE || !dev || !dev->crs_data)
|
||||
return -1;
|
||||
return find_resource(dev->crs_data, dev->crs_size,
|
||||
3 /* irq */, irq, &max);
|
||||
}
|
||||
|
||||
int acpi_dsdt_present_eisaid(u16 eisaid)
|
||||
{
|
||||
if (!CONFIG_ACPI_PARSE)
|
||||
return -1; /* unknown */
|
||||
if (hlist_empty(&acpi_devices))
|
||||
return -1; /* unknown (no dsdt table) */
|
||||
|
||||
struct acpi_device *dev = acpi_dsdt_find_eisaid(NULL, eisaid);
|
||||
return acpi_dsdt_present(dev);
|
||||
}
|
||||
|
||||
void acpi_dsdt_parse(void)
|
||||
{
|
||||
if (!CONFIG_ACPI_PARSE)
|
||||
return;
|
||||
|
||||
struct fadt_descriptor_rev1 *fadt = find_acpi_table(FACP_SIGNATURE);
|
||||
if (!fadt)
|
||||
return;
|
||||
u8 *dsdt = (void*)(fadt->dsdt);
|
||||
if (!dsdt)
|
||||
return;
|
||||
|
||||
u32 length = *(u32*)(dsdt + 4);
|
||||
u32 offset = 0x24;
|
||||
dprintf(1, "ACPI: parse DSDT at %p (len %d)\n", dsdt, length);
|
||||
|
||||
struct parse_state s;
|
||||
memset(&s, 0, sizeof(s));
|
||||
parse_termlist(&s, dsdt, offset, length);
|
||||
|
||||
if (!parse_dumpdevs)
|
||||
return;
|
||||
|
||||
struct acpi_device *dev;
|
||||
dprintf(1, "ACPI: dumping dsdt devices\n");
|
||||
for (dev = acpi_dsdt_find(NULL, NULL, 0);
|
||||
dev != NULL;
|
||||
dev = acpi_dsdt_find(dev, NULL, 0)) {
|
||||
dprintf(1, " %s", acpi_dsdt_name(dev));
|
||||
if (dev->hid_aml)
|
||||
dprintf(1, ", hid");
|
||||
if (dev->sta_aml)
|
||||
dprintf(1, ", sta (0x%x)", dev->sta_aml[0]);
|
||||
if (dev->crs_data)
|
||||
dprintf(1, ", crs");
|
||||
dprintf(1, "\n");
|
||||
if (dev->crs_data)
|
||||
print_resources(" ", dev->crs_data, dev->crs_size);
|
||||
}
|
||||
}
|
|
@ -237,9 +237,10 @@ qemu_platform_setup(void)
|
|||
|
||||
RsdpAddr = find_acpi_rsdp();
|
||||
|
||||
if (RsdpAddr)
|
||||
if (RsdpAddr) {
|
||||
acpi_dsdt_parse();
|
||||
return;
|
||||
|
||||
}
|
||||
/* If present, loader should have installed an RSDP.
|
||||
* Not installed? We might still be able to continue
|
||||
* using the builtin RSDP.
|
||||
|
|
11
src/util.h
11
src/util.h
|
@ -94,6 +94,17 @@ void display_uuid(void);
|
|||
void copy_table(void *pos);
|
||||
void smbios_setup(void);
|
||||
|
||||
// fw/dsdt_parser.c
|
||||
struct acpi_device;
|
||||
void acpi_dsdt_parse(void);
|
||||
struct acpi_device *acpi_dsdt_find_string(struct acpi_device *prev, const char *hid);
|
||||
struct acpi_device *acpi_dsdt_find_eisaid(struct acpi_device *prev, u16 eisaid);
|
||||
char *acpi_dsdt_name(struct acpi_device *dev);
|
||||
int acpi_dsdt_present_eisaid(u16 eisaid);
|
||||
int acpi_dsdt_find_io(struct acpi_device *dev, u64 *min, u64 *max);
|
||||
int acpi_dsdt_find_mem(struct acpi_device *dev, u64 *min, u64 *max);
|
||||
int acpi_dsdt_find_irq(struct acpi_device *dev, u64 *irq);
|
||||
|
||||
// fw/coreboot.c
|
||||
extern const char *CBvendor, *CBpart;
|
||||
struct cbfs_file;
|
||||
|
|
Loading…
Reference in New Issue