summaryrefslogtreecommitdiffstats
path: root/contrib/syslinux-4.02/com32/sysdump/acpi.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/syslinux-4.02/com32/sysdump/acpi.c')
-rw-r--r--contrib/syslinux-4.02/com32/sysdump/acpi.c259
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");
+}