Home | History | Annotate | Download | only in sysdump
      1 /* ----------------------------------------------------------------------- *
      2  *
      3  *   Copyright 2010 Intel Corporation; author: H. Peter Anvin
      4  *
      5  *   This program is free software; you can redistribute it and/or modify
      6  *   it under the terms of the GNU General Public License as published by
      7  *   the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
      8  *   Boston MA 02110-1301, USA; either version 2 of the License, or
      9  *   (at your option) any later version; incorporated herein by reference.
     10  *
     11  * ----------------------------------------------------------------------- */
     12 
     13 /*
     14  * Dump ACPI information
     15  */
     16 
     17 #include <stdio.h>
     18 #include <string.h>
     19 #include <stdlib.h>
     20 #include "sysdump.h"
     21 #include "rbtree.h"
     22 
     23 struct acpi_rsdp {
     24     uint8_t  magic[8];		/* "RSD PTR " */
     25     uint8_t  csum;
     26     char     oemid[6];
     27     uint8_t  rev;
     28     uint32_t rsdt_addr;
     29     uint32_t len;
     30     uint64_t xsdt_addr;
     31     uint8_t  xcsum;
     32     uint8_t  rsvd[3];
     33 };
     34 
     35 struct acpi_hdr {
     36     char     sig[4];		/* Signature */
     37     uint32_t len;
     38     uint8_t  rev;
     39     uint8_t  csum;
     40     char     oemid[6];
     41     char     oemtblid[16];
     42     uint32_t oemrev;
     43     uint32_t creatorid;
     44     uint32_t creatorrev;
     45 };
     46 
     47 struct acpi_rsdt {
     48     struct acpi_hdr hdr;
     49     uint32_t entry[0];
     50 };
     51 
     52 struct acpi_xsdt {
     53     struct acpi_hdr hdr;
     54     uint64_t entry[0];
     55 };
     56 
     57 static struct rbtree *rb_types, *rb_addrs;
     58 
     59 static bool rb_has(struct rbtree **tree, uint64_t key)
     60 {
     61     struct rbtree *node;
     62 
     63     node = rb_search(*tree, key);
     64     if (node && node->key == key)
     65 	return true;
     66 
     67     node = malloc(sizeof *node);
     68     if (node) {
     69 	node->key = key;
     70 	*tree = rb_insert(*tree, node);
     71     }
     72     return false;
     73 }
     74 
     75 static inline bool addr_ok(uint64_t addr)
     76 {
     77     /* We can only handle 32-bit addresses for now... */
     78     return addr <= 0xffffffff;
     79 }
     80 
     81 enum tbl_errs {
     82     ERR_NONE,			/* No errors */
     83     ERR_CSUM,			/* Invalid checksum */
     84     ERR_SIZE,			/* Impossibly large table */
     85     ERR_NOSIG			/* No signature */
     86 };
     87 
     88 static uint8_t checksum_range(const void *start, uint32_t size)
     89 {
     90     const uint8_t *p = start;
     91     uint8_t csum = 0;
     92 
     93     while (size--)
     94 	csum += *p++;
     95 
     96     return csum;
     97 }
     98 
     99 static enum tbl_errs is_valid_table(const void *ptr)
    100 {
    101     const struct acpi_hdr *hdr = ptr;
    102 
    103     if (hdr->sig[0] == 0)
    104 	return ERR_NOSIG;
    105 
    106     if (hdr->len < 10 || hdr->len > (1 << 20)) {
    107 	/* Either insane or too large to dump */
    108 	return ERR_SIZE;
    109     }
    110 
    111     return checksum_range(hdr, hdr->len) == 0 ? ERR_NONE : ERR_CSUM;
    112 }
    113 
    114 static const struct acpi_rsdp *scan_for_rsdp(uint32_t base, uint32_t end)
    115 {
    116     for (base &= ~15; base < end-20; base += 16) {
    117 	const struct acpi_rsdp *rsdp = (const struct acpi_rsdp *)base;
    118 
    119 	if (memcmp(rsdp->magic, "RSD PTR ", 8))
    120 	    continue;
    121 
    122 	if (checksum_range(rsdp, 20))
    123 	    continue;
    124 
    125 	if (rsdp->rev > 0) {
    126 	    if (base + rsdp->len >= end ||
    127 		checksum_range(rsdp, rsdp->len))
    128 		continue;
    129 	}
    130 
    131 	return rsdp;
    132     }
    133 
    134     return NULL;
    135 }
    136 
    137 static const struct acpi_rsdp *find_rsdp(void)
    138 {
    139     uint32_t ebda;
    140     const struct acpi_rsdp *rsdp;
    141 
    142     ebda = (*(uint16_t *)0x40e) << 4;
    143     if (ebda >= 0x70000 && ebda < 0xa0000) {
    144 	rsdp = scan_for_rsdp(ebda, ebda+1024);
    145 
    146 	if (rsdp)
    147 	    return rsdp;
    148     }
    149 
    150     return scan_for_rsdp(0xe0000, 0x100000);
    151 }
    152 
    153 static void dump_table(struct upload_backend *be,
    154 		       const char name[], const void *ptr, uint32_t len)
    155 {
    156     char namebuf[64];
    157     uint32_t name_key = *(uint32_t *)name;
    158 
    159     if (rb_has(&rb_addrs, (size_t)ptr))
    160 	return;			/* Already dumped this table */
    161 
    162     if (!rb_has(&rb_types, name_key)) {
    163 	snprintf(namebuf, sizeof namebuf, "acpi/%4.4s", name);
    164 	cpio_mkdir(be, namebuf);
    165     }
    166 
    167     snprintf(namebuf, sizeof namebuf, "acpi/%4.4s/%08x", name, (uint32_t)ptr);
    168     cpio_hdr(be, MODE_FILE, len, namebuf);
    169 
    170     write_data(be, ptr, len);
    171 }
    172 
    173 static void dump_rsdt(struct upload_backend *be, const struct acpi_rsdp *rsdp)
    174 {
    175     const struct acpi_rsdt *rsdt;
    176     uint32_t i, n;
    177 
    178     rsdt = (const struct acpi_rsdt *)rsdp->rsdt_addr;
    179 
    180     if (memcmp(rsdt->hdr.sig, "RSDT", 4) || is_valid_table(rsdt) > ERR_CSUM)
    181 	return;
    182 
    183     dump_table(be, rsdt->hdr.sig, rsdt, rsdt->hdr.len);
    184 
    185     if (rsdt->hdr.len < 36)
    186 	return;
    187 
    188     n = (rsdt->hdr.len - 36) >> 2;
    189 
    190     for (i = 0; i < n; i++) {
    191 	const struct acpi_hdr *hdr = (const struct acpi_hdr *)(rsdt->entry[i]);
    192 
    193 	if (is_valid_table(hdr) <= ERR_CSUM)
    194 	    dump_table(be, hdr->sig, hdr, hdr->len);
    195     }
    196 }
    197 
    198 static void dump_xsdt(struct upload_backend *be, const struct acpi_rsdp *rsdp)
    199 {
    200     const struct acpi_xsdt *xsdt;
    201     uint32_t rsdp_len = rsdp->rev > 0 ? rsdp->len : 20;
    202     uint32_t i, n;
    203 
    204     if (rsdp_len < 34)
    205 	return;
    206 
    207     if (!addr_ok(rsdp->xsdt_addr))
    208 	return;
    209 
    210     xsdt = (const struct acpi_xsdt *)(size_t)rsdp->xsdt_addr;
    211 
    212     if (memcmp(xsdt->hdr.sig, "XSDT", 4) || is_valid_table(xsdt) > ERR_CSUM)
    213 	return;
    214 
    215     dump_table(be, xsdt->hdr.sig, xsdt, xsdt->hdr.len);
    216 
    217     if (xsdt->hdr.len < 36)
    218 	return;
    219 
    220     n = (xsdt->hdr.len - 36) >> 3;
    221 
    222     for (i = 0; i < n; i++) {
    223 	const struct acpi_hdr *hdr;
    224 	if (addr_ok(xsdt->entry[i])) {
    225 	    hdr = (const struct acpi_hdr *)(size_t)(xsdt->entry[i]);
    226 
    227 	    if (is_valid_table(hdr) <= ERR_CSUM)
    228 		dump_table(be, hdr->sig, hdr, hdr->len);
    229 	}
    230     }
    231 }
    232 
    233 void dump_acpi(struct upload_backend *be)
    234 {
    235     const struct acpi_rsdp *rsdp;
    236     uint32_t rsdp_len;
    237 
    238     rsdp = find_rsdp();
    239 
    240     printf("Dumping ACPI... ");
    241 
    242     if (!rsdp)
    243 	return;			/* No ACPI information found */
    244 
    245     cpio_mkdir(be, "acpi");
    246 
    247     rsdp_len = rsdp->rev > 0 ? rsdp->len : 20;
    248 
    249     dump_table(be, "RSDP", rsdp, rsdp_len);
    250 
    251     dump_rsdt(be, rsdp);
    252     dump_xsdt(be, rsdp);
    253 
    254     rb_destroy(rb_types);
    255     rb_destroy(rb_addrs);
    256 
    257     printf("done.\n");
    258 }
    259