[e16e8f2] | 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 | } |
---|