diff options
Diffstat (limited to 'contrib/syslinux-4.02/com32/sysdump/acpi.c')
-rw-r--r-- | contrib/syslinux-4.02/com32/sysdump/acpi.c | 259 |
1 files changed, 259 insertions, 0 deletions
diff --git a/contrib/syslinux-4.02/com32/sysdump/acpi.c b/contrib/syslinux-4.02/com32/sysdump/acpi.c new file mode 100644 index 0000000..8671fc8 --- /dev/null +++ b/contrib/syslinux-4.02/com32/sysdump/acpi.c @@ -0,0 +1,259 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2010 Intel Corporation; author: H. Peter Anvin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston MA 02110-1301, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * Dump ACPI information + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include "sysdump.h" +#include "backend.h" +#include "rbtree.h" + +struct acpi_rsdp { + uint8_t magic[8]; /* "RSD PTR " */ + uint8_t csum; + char oemid[6]; + uint8_t rev; + uint32_t rsdt_addr; + uint32_t len; + uint64_t xsdt_addr; + uint8_t xcsum; + uint8_t rsvd[3]; +}; + +struct acpi_hdr { + char sig[4]; /* Signature */ + uint32_t len; + uint8_t rev; + uint8_t csum; + char oemid[6]; + char oemtblid[16]; + uint32_t oemrev; + uint32_t creatorid; + uint32_t creatorrev; +}; + +struct acpi_rsdt { + struct acpi_hdr hdr; + uint32_t entry[0]; +}; + +struct acpi_xsdt { + struct acpi_hdr hdr; + uint64_t entry[0]; +}; + +static struct rbtree *rb_types, *rb_addrs; + +static bool rb_has(struct rbtree **tree, uint64_t key) +{ + struct rbtree *node; + + node = rb_search(*tree, key); + if (node && node->key == key) + return true; + + node = malloc(sizeof *node); + if (node) { + node->key = key; + *tree = rb_insert(*tree, node); + } + return false; +} + +static inline bool addr_ok(uint64_t addr) +{ + /* We can only handle 32-bit addresses for now... */ + return addr <= 0xffffffff; +} + +enum tbl_errs { + ERR_NONE, /* No errors */ + ERR_CSUM, /* Invalid checksum */ + ERR_SIZE, /* Impossibly large table */ + ERR_NOSIG /* No signature */ +}; + +static uint8_t checksum_range(const void *start, uint32_t size) +{ + const uint8_t *p = start; + uint8_t csum = 0; + + while (size--) + csum += *p++; + + return csum; +} + +static enum tbl_errs is_valid_table(const void *ptr) +{ + const struct acpi_hdr *hdr = ptr; + + if (hdr->sig[0] == 0) + return ERR_NOSIG; + + if (hdr->len < 10 || hdr->len > (1 << 20)) { + /* Either insane or too large to dump */ + return ERR_SIZE; + } + + return checksum_range(hdr, hdr->len) == 0 ? ERR_NONE : ERR_CSUM; +} + +static const struct acpi_rsdp *scan_for_rsdp(uint32_t base, uint32_t end) +{ + for (base &= ~15; base < end-20; base += 16) { + const struct acpi_rsdp *rsdp = (const struct acpi_rsdp *)base; + + if (memcmp(rsdp->magic, "RSD PTR ", 8)) + continue; + + if (checksum_range(rsdp, 20)) + continue; + + if (rsdp->rev > 0) { + if (base + rsdp->len >= end || + checksum_range(rsdp, rsdp->len)) + continue; + } + + return rsdp; + } + + return NULL; +} + +static const struct acpi_rsdp *find_rsdp(void) +{ + uint32_t ebda; + const struct acpi_rsdp *rsdp; + + ebda = (*(uint16_t *)0x40e) << 4; + if (ebda >= 0x70000 && ebda < 0xa0000) { + rsdp = scan_for_rsdp(ebda, ebda+1024); + + if (rsdp) + return rsdp; + } + + return scan_for_rsdp(0xe0000, 0x100000); +} + +static void dump_table(struct backend *be, + const char name[], const void *ptr, uint32_t len) +{ + char namebuf[64]; + uint32_t name_key = *(uint32_t *)name; + + if (rb_has(&rb_addrs, (size_t)ptr)) + return; /* Already dumped this table */ + + if (!rb_has(&rb_types, name_key)) { + snprintf(namebuf, sizeof namebuf, "acpi/%4.4s", name); + cpio_mkdir(be, namebuf); + } + + snprintf(namebuf, sizeof namebuf, "acpi/%4.4s/%08x", name, (uint32_t)ptr); + cpio_hdr(be, MODE_FILE, len, namebuf); + + write_data(be, ptr, len); +} + +static void dump_rsdt(struct backend *be, const struct acpi_rsdp *rsdp) +{ + const struct acpi_rsdt *rsdt; + uint32_t i, n; + + rsdt = (const struct acpi_rsdt *)rsdp->rsdt_addr; + + if (memcmp(rsdt->hdr.sig, "RSDT", 4) || is_valid_table(rsdt) > ERR_CSUM) + return; + + dump_table(be, rsdt->hdr.sig, rsdt, rsdt->hdr.len); + + if (rsdt->hdr.len < 36) + return; + + n = (rsdt->hdr.len - 36) >> 2; + + for (i = 0; i < n; i++) { + const struct acpi_hdr *hdr = (const struct acpi_hdr *)(rsdt->entry[i]); + + if (is_valid_table(hdr) <= ERR_CSUM) + dump_table(be, hdr->sig, hdr, hdr->len); + } +} + +static void dump_xsdt(struct backend *be, const struct acpi_rsdp *rsdp) +{ + const struct acpi_xsdt *xsdt; + uint32_t rsdp_len = rsdp->rev > 0 ? rsdp->len : 20; + uint32_t i, n; + + if (rsdp_len < 34) + return; + + if (!addr_ok(rsdp->xsdt_addr)) + return; + + xsdt = (const struct acpi_xsdt *)(size_t)rsdp->xsdt_addr; + + if (memcmp(xsdt->hdr.sig, "XSDT", 4) || is_valid_table(xsdt) > ERR_CSUM) + return; + + dump_table(be, xsdt->hdr.sig, xsdt, xsdt->hdr.len); + + if (xsdt->hdr.len < 36) + return; + + n = (xsdt->hdr.len - 36) >> 3; + + for (i = 0; i < n; i++) { + const struct acpi_hdr *hdr; + if (addr_ok(xsdt->entry[i])) { + hdr = (const struct acpi_hdr *)(size_t)(xsdt->entry[i]); + + if (is_valid_table(hdr) <= ERR_CSUM) + dump_table(be, hdr->sig, hdr, hdr->len); + } + } +} + +void dump_acpi(struct backend *be) +{ + const struct acpi_rsdp *rsdp; + uint32_t rsdp_len; + + rsdp = find_rsdp(); + + printf("Dumping ACPI... "); + + if (!rsdp) + return; /* No ACPI information found */ + + cpio_mkdir(be, "acpi"); + + rsdp_len = rsdp->rev > 0 ? rsdp->len : 20; + + dump_table(be, "RSDP", rsdp, rsdp_len); + + dump_rsdt(be, rsdp); + dump_xsdt(be, rsdp); + + rb_destroy(rb_types); + rb_destroy(rb_addrs); + + printf("done.\n"); +} |