summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@linux.intel.com>2010-06-18 18:28:43 -0700
committerH. Peter Anvin <hpa@linux.intel.com>2010-06-18 18:28:43 -0700
commite32525782abedebbd97a3bb6fe518a8c4c817b48 (patch)
treea30233c3a121c62ccf4eceb36c1a155cfde300b1
parent46971666d3fbe15db020029034747a64738754aa (diff)
downloadsyslinux-e32525782abedebbd97a3bb6fe518a8c4c817b48.tar.gz
sysdump: dump ACPI information
Dump ACPI tables. Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
-rw-r--r--com32/sysdump/acpi.c175
-rw-r--r--com32/sysdump/main.c1
-rw-r--r--com32/sysdump/sysdump.h1
3 files changed, 177 insertions, 0 deletions
diff --git a/com32/sysdump/acpi.c b/com32/sysdump/acpi.c
new file mode 100644
index 00000000..48b890ae
--- /dev/null
+++ b/com32/sysdump/acpi.c
@@ -0,0 +1,175 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * 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"
+
+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 xdst_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];
+};
+
+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];
+
+ /* XXX: this make cause the same directory to show up more than once */
+ 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);
+}
+
+void dump_acpi(struct backend *be)
+{
+ const struct acpi_rsdp *rsdp;
+ const struct acpi_rsdt *rsdt;
+ uint32_t rsdp_len;
+ uint32_t i, n;
+
+ rsdp = find_rsdp();
+
+ 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);
+
+ rsdt = (const struct acpi_rsdt *)rsdp->rsdt_addr;
+
+ if (memcmp(rsdt->hdr.sig, "RSDT", 4) || is_valid_table(rsdt) != ERR_NONE)
+ 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);
+ }
+}
diff --git a/com32/sysdump/main.c b/com32/sysdump/main.c
index d4a0320e..26f562be 100644
--- a/com32/sysdump/main.c
+++ b/com32/sysdump/main.c
@@ -41,6 +41,7 @@ static void dump_all(struct backend *be, const char *argv[])
dump_memory_map(be);
dump_memory(be);
dump_dmi(be);
+ dump_acpi(be);
dump_cpuid(be);
dump_pci(be);
dump_vesa_tables(be);
diff --git a/com32/sysdump/sysdump.h b/com32/sysdump/sysdump.h
index 61a04a2b..a5b963f8 100644
--- a/com32/sysdump/sysdump.h
+++ b/com32/sysdump/sysdump.h
@@ -7,6 +7,7 @@ void dump_memory_map(struct backend *);
void snapshot_lowmem(void);
void dump_memory(struct backend *);
void dump_dmi(struct backend *);
+void dump_acpi(struct backend *);
void dump_cpuid(struct backend *);
void dump_pci(struct backend *);
void dump_vesa_tables(struct backend *);