1 /* $OpenBSD: acpidump.c,v 1.7 2010/08/08 14:40:19 jmc Exp $ */ 2 /* 3 * Copyright (c) 2000 Mitsuru IWASAKI <iwasaki@FreeBSD.org> 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 */ 28 29 #include <sys/types.h> 30 31 #include <assert.h> 32 #include <err.h> 33 #include <fcntl.h> 34 #include <stdio.h> 35 #include <stdlib.h> 36 #include <string.h> 37 #include <unistd.h> 38 39 #include <sys/mman.h> 40 #include <sys/queue.h> 41 #include <sys/types.h> 42 #include <sys/stat.h> 43 #include <sys/syslimits.h> 44 45 #include <uvm/uvm_extern.h> 46 47 #define vm_page_size sysconf(_SC_PAGESIZE) 48 #define PRINTFLAG(xx) \ 49 do { \ 50 if (facp->flags & ACPI_FACP_FLAG_## xx) { \ 51 fprintf(fhdr, "%c%s", sep, #xx); sep = ','; \ 52 } \ 53 } while (0) 54 55 56 typedef unsigned long vm_offset_t; 57 58 struct ACPIrsdp { 59 u_char signature[8]; 60 u_char sum; 61 u_char oem[6]; 62 u_char res; 63 u_int32_t addr; 64 } __packed; 65 66 struct ACPIsdt { 67 u_char signature[4]; 68 u_int32_t len; 69 u_char rev; 70 u_char check; 71 u_char oemid[6]; 72 u_char oemtblid[8]; 73 u_int32_t oemrev; 74 u_char creator[4]; 75 u_int32_t crerev; 76 #define SIZEOF_SDT_HDR 36 /* struct size except body */ 77 u_int32_t body[1];/* This member should be casted */ 78 } __packed; 79 80 struct ACPIgas { 81 u_int8_t address_space_id; 82 #define ACPI_GAS_MEMORY 0 83 #define ACPI_GAS_IO 1 84 #define ACPI_GAS_PCI 2 85 #define ACPI_GAS_EMBEDDED 3 86 #define ACPI_GAS_SMBUS 4 87 #define ACPI_GAS_FIXED 0x7f 88 u_int8_t register_bit_width; 89 u_int8_t register_bit_offset; 90 u_int8_t res; 91 u_int64_t address; 92 } __packed; 93 94 struct FACPbody { 95 u_int32_t facs_ptr; 96 u_int32_t dsdt_ptr; 97 u_int8_t int_model; 98 #define ACPI_FACP_INTMODEL_PIC 0 /* Standard PC-AT PIC */ 99 #define ACPI_FACP_INTMODEL_APIC 1 /* Multiple APIC */ 100 u_char reserved1; 101 u_int16_t sci_int; 102 u_int32_t smi_cmd; 103 u_int8_t acpi_enable; 104 u_int8_t acpi_disable; 105 u_int8_t s4biosreq; 106 u_int8_t reserved2; 107 u_int32_t pm1a_evt_blk; 108 u_int32_t pm1b_evt_blk; 109 u_int32_t pm1a_cnt_blk; 110 u_int32_t pm1b_cnt_blk; 111 u_int32_t pm2_cnt_blk; 112 u_int32_t pm_tmr_blk; 113 u_int32_t gpe0_blk; 114 u_int32_t gpe1_blk; 115 u_int8_t pm1_evt_len; 116 u_int8_t pm1_cnt_len; 117 u_int8_t pm2_cnt_len; 118 u_int8_t pm_tmr_len; 119 u_int8_t gpe0_len; 120 u_int8_t gpe1_len; 121 u_int8_t gpe1_base; 122 u_int8_t reserved3; 123 u_int16_t p_lvl2_lat; 124 u_int16_t p_lvl3_lat; 125 u_int16_t flush_size; 126 u_int16_t flush_stride; 127 u_int8_t duty_off; 128 u_int8_t duty_width; 129 u_int8_t day_alrm; 130 u_int8_t mon_alrm; 131 u_int8_t century; 132 u_int16_t iapc_boot_arch; 133 u_char reserved4[1]; 134 u_int32_t flags; 135 #define ACPI_FACP_FLAG_WBINVD 1 /* WBINVD is correctly supported */ 136 #define ACPI_FACP_FLAG_WBINVD_FLUSH 2 /* WBINVD flushes caches */ 137 #define ACPI_FACP_FLAG_PROC_C1 4 /* C1 power state supported */ 138 #define ACPI_FACP_FLAG_P_LVL2_UP 8 /* C2 power state works on SMP */ 139 #define ACPI_FACP_FLAG_PWR_BUTTON 16 /* Power button uses control method */ 140 #define ACPI_FACP_FLAG_SLP_BUTTON 32 /* Sleep button uses control method */ 141 #define ACPI_FACP_FLAG_FIX_RTC 64 /* RTC wakeup not supported */ 142 #define ACPI_FACP_FLAG_RTC_S4 128 /* RTC can wakeup from S4 state */ 143 #define ACPI_FACP_FLAG_TMR_VAL_EXT 256 /* TMR_VAL is 32bit */ 144 #define ACPI_FACP_FLAG_DCK_CAP 512 /* Can support docking */ 145 struct ACPIgas reset_reg; 146 u_int8_t reset_value; 147 u_int8_t reserved5[3]; 148 u_int64_t x_firmware_ctrl; 149 u_int64_t x_dsdt; 150 struct ACPIgas x_pm1a_evt_blk; 151 struct ACPIgas x_pm1b_evt_blk; 152 struct ACPIgas x_pm1a_cnt_blk; 153 struct ACPIgas x_pm1b_cnt_blk; 154 struct ACPIgas x_pm2_cnt_blk; 155 struct ACPIgas x_pm_tmr_blk; 156 struct ACPIgas x_gpe0_blk; 157 struct ACPIgas x_gpe1_blk; 158 } __packed; 159 160 struct acpi_user_mapping { 161 LIST_ENTRY(acpi_user_mapping) link; 162 vm_offset_t pa; 163 caddr_t va; 164 size_t size; 165 }; 166 167 LIST_HEAD(acpi_user_mapping_list, acpi_user_mapping) maplist; 168 169 int acpi_mem_fd = -1; 170 char *aml_dumpfile; 171 FILE *fhdr; 172 173 struct ACPIsdt dsdt_header = { 174 "DSDT", 0, 1, 0, "OEMID", "OEMTBLID", 0x12345678, "CRTR", 0x12345678 175 }; 176 177 int 178 acpi_checksum(void *p, size_t length) 179 { 180 u_int8_t *bp; 181 u_int8_t sum; 182 183 bp = p; 184 sum = 0; 185 while (length--) 186 sum += *bp++; 187 188 return (sum); 189 } 190 191 struct acpi_user_mapping * 192 acpi_user_find_mapping(vm_offset_t pa, size_t size) 193 { 194 struct acpi_user_mapping *map; 195 196 /* First search for an existing mapping */ 197 for (map = LIST_FIRST(&maplist); map; map = LIST_NEXT(map, link)) { 198 if (map->pa <= pa && map->size >= pa + size - map->pa) 199 return (map); 200 } 201 202 /* Then create a new one */ 203 size = round_page(pa + size) - trunc_page(pa); 204 pa = trunc_page(pa); 205 map = malloc(sizeof(struct acpi_user_mapping)); 206 if (!map) 207 errx(1, "out of memory"); 208 map->pa = pa; 209 map->va = mmap(0, size, PROT_READ, MAP_SHARED, acpi_mem_fd, pa); 210 map->size = size; 211 if (map->va == MAP_FAILED) 212 err(1, "can't map address"); 213 LIST_INSERT_HEAD(&maplist, map, link); 214 215 return (map); 216 } 217 218 void * 219 acpi_map_physical(vm_offset_t pa, size_t size) 220 { 221 struct acpi_user_mapping *map; 222 223 map = acpi_user_find_mapping(pa, size); 224 return (map->va + (pa - map->pa)); 225 } 226 227 void 228 acpi_user_init(void) 229 { 230 if (acpi_mem_fd == -1) { 231 acpi_mem_fd = open("/dev/mem", O_RDONLY); 232 if (acpi_mem_fd == -1) 233 err(1, "opening /dev/mem"); 234 LIST_INIT(&maplist); 235 } 236 } 237 238 struct ACPIrsdp * 239 acpi_find_rsd_ptr() 240 { 241 int i; 242 u_int8_t buf[sizeof(struct ACPIrsdp)]; 243 244 acpi_user_init(); 245 for (i = 0; i < 1024 * 1024; i += 16) { 246 lseek(acpi_mem_fd, i, SEEK_SET); 247 read(acpi_mem_fd, buf, 16); 248 if (!memcmp(buf, "RSD PTR ", 8)) { 249 /* Read the rest of the structure */ 250 read(acpi_mem_fd, buf + 16, sizeof(struct ACPIrsdp) - 16); 251 252 /* Verify checksum before accepting it. */ 253 if (acpi_checksum(buf, sizeof(struct ACPIrsdp))) 254 continue; 255 256 return (acpi_map_physical(i, sizeof(struct ACPIrsdp))); 257 } 258 } 259 260 return (0); 261 } 262 263 void 264 acpi_print_string(char *s, size_t length) 265 { 266 int c; 267 268 /* Trim trailing spaces and NULLs */ 269 while (length > 0 && (s[length - 1] == ' ' || s[length - 1] == '\0')) 270 length--; 271 272 while (length--) { 273 c = *s++; 274 fputc(c, fhdr); 275 } 276 } 277 278 void 279 acpi_print_rsd_ptr(struct ACPIrsdp *rp) 280 { 281 fprintf(fhdr, "\n"); 282 fprintf(fhdr, "RSD PTR: Checksum=%d, OEMID=", rp->sum); 283 acpi_print_string(rp->oem, 6); 284 fprintf(fhdr, ", RsdtAddress=0x%08x\n", rp->addr); 285 fprintf(fhdr, "\n"); 286 } 287 288 struct ACPIsdt * 289 acpi_map_sdt(vm_offset_t pa) 290 { 291 struct ACPIsdt *sp; 292 293 sp = acpi_map_physical(pa, sizeof(struct ACPIsdt)); 294 sp = acpi_map_physical(pa, sp->len); 295 return (sp); 296 } 297 298 void 299 aml_dump(struct ACPIsdt *hdr) 300 { 301 static int hdr_index; 302 char name[PATH_MAX]; 303 int fd; 304 mode_t mode; 305 306 snprintf(name, sizeof(name), "%s.%c%c%c%c.%d", 307 aml_dumpfile, hdr->signature[0], hdr->signature[1], 308 hdr->signature[2], hdr->signature[3], 309 hdr_index++); 310 311 mode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 312 fd = open(name, O_WRONLY | O_CREAT | O_TRUNC, mode); 313 if (fd == -1) 314 err(1, "aml_dump"); 315 316 write(fd, hdr, SIZEOF_SDT_HDR); 317 write(fd, hdr->body, hdr->len - SIZEOF_SDT_HDR); 318 close(fd); 319 } 320 321 void 322 acpi_print_sdt(struct ACPIsdt *sdp) 323 { 324 fprintf(fhdr, "\n"); 325 acpi_print_string(sdp->signature, 4); 326 fprintf(fhdr, ": Length=%d, Revision=%d, Checksum=%d,\n", 327 sdp->len, sdp->rev, sdp->check); 328 fprintf(fhdr, "\tOEMID="); 329 acpi_print_string(sdp->oemid, 6); 330 fprintf(fhdr, ", OEM Table ID="); 331 acpi_print_string(sdp->oemtblid, 8); 332 fprintf(fhdr, ", OEM Revision=0x%x,\n", sdp->oemrev); 333 fprintf(fhdr, "\tCreator ID="); 334 acpi_print_string(sdp->creator, 4); 335 fprintf(fhdr, ", Creator Revision=0x%x\n", sdp->crerev); 336 fprintf(fhdr, "\n"); 337 if (!memcmp(sdp->signature, "DSDT", 4)) 338 memcpy(&dsdt_header, sdp, sizeof(dsdt_header)); 339 } 340 341 void 342 acpi_print_rsdt(struct ACPIsdt *rsdp) 343 { 344 int i, entries; 345 346 acpi_print_sdt(rsdp); 347 entries = (rsdp->len - SIZEOF_SDT_HDR) / sizeof(u_int32_t); 348 fprintf(fhdr, "\n"); 349 fprintf(fhdr, "\tEntries={ "); 350 for (i = 0; i < entries; i++) { 351 if (i > 0) 352 fprintf(fhdr, ", "); 353 fprintf(fhdr, "0x%08x", rsdp->body[i]); 354 } 355 fprintf(fhdr, " }\n"); 356 fprintf(fhdr, "\n"); 357 } 358 359 void 360 acpi_print_facp(struct FACPbody *facp) 361 { 362 char sep; 363 364 fprintf(fhdr, "\n"); 365 fprintf(fhdr, "\tDSDT=0x%x\n", facp->dsdt_ptr); 366 fprintf(fhdr, "\tINT_MODEL=%s\n", facp->int_model ? "APIC" : "PIC"); 367 fprintf(fhdr, "\tSCI_INT=%d\n", facp->sci_int); 368 fprintf(fhdr, "\tSMI_CMD=0x%x, ", facp->smi_cmd); 369 fprintf(fhdr, "ACPI_ENABLE=0x%x, ", facp->acpi_enable); 370 fprintf(fhdr, "ACPI_DISABLE=0x%x, ", facp->acpi_disable); 371 fprintf(fhdr, "S4BIOS_REQ=0x%x\n", facp->s4biosreq); 372 if (facp->pm1a_evt_blk) 373 fprintf(fhdr, "\tPM1a_EVT_BLK=0x%x-0x%x\n", 374 facp->pm1a_evt_blk, 375 facp->pm1a_evt_blk + facp->pm1_evt_len - 1); 376 if (facp->pm1b_evt_blk) 377 fprintf(fhdr, "\tPM1b_EVT_BLK=0x%x-0x%x\n", 378 facp->pm1b_evt_blk, 379 facp->pm1b_evt_blk + facp->pm1_evt_len - 1); 380 if (facp->pm1a_cnt_blk) 381 fprintf(fhdr, "\tPM1a_CNT_BLK=0x%x-0x%x\n", 382 facp->pm1a_cnt_blk, 383 facp->pm1a_cnt_blk + facp->pm1_cnt_len - 1); 384 if (facp->pm1b_cnt_blk) 385 fprintf(fhdr, "\tPM1b_CNT_BLK=0x%x-0x%x\n", 386 facp->pm1b_cnt_blk, 387 facp->pm1b_cnt_blk + facp->pm1_cnt_len - 1); 388 if (facp->pm2_cnt_blk) 389 fprintf(fhdr, "\tPM2_CNT_BLK=0x%x-0x%x\n", 390 facp->pm2_cnt_blk, 391 facp->pm2_cnt_blk + facp->pm2_cnt_len - 1); 392 if (facp->pm_tmr_blk) 393 fprintf(fhdr, "\tPM2_TMR_BLK=0x%x-0x%x\n", 394 facp->pm_tmr_blk, 395 facp->pm_tmr_blk + facp->pm_tmr_len - 1); 396 if (facp->gpe0_blk) 397 fprintf(fhdr, "\tPM2_GPE0_BLK=0x%x-0x%x\n", 398 facp->gpe0_blk, 399 facp->gpe0_blk + facp->gpe0_len - 1); 400 if (facp->gpe1_blk) 401 fprintf(fhdr, "\tPM2_GPE1_BLK=0x%x-0x%x, GPE1_BASE=%d\n", 402 facp->gpe1_blk, 403 facp->gpe1_blk + facp->gpe1_len - 1, 404 facp->gpe1_base); 405 fprintf(fhdr, "\tP_LVL2_LAT=%dms, P_LVL3_LAT=%dms\n", 406 facp->p_lvl2_lat, facp->p_lvl3_lat); 407 fprintf(fhdr, "\tFLUSH_SIZE=%d, FLUSH_STRIDE=%d\n", 408 facp->flush_size, facp->flush_stride); 409 fprintf(fhdr, "\tDUTY_OFFSET=%d, DUTY_WIDTH=%d\n", 410 facp->duty_off, facp->duty_width); 411 fprintf(fhdr, "\tDAY_ALRM=%d, MON_ALRM=%d, CENTURY=%d\n", 412 facp->day_alrm, facp->mon_alrm, facp->century); 413 fprintf(fhdr, "\tFlags="); 414 sep = '{'; 415 416 PRINTFLAG(WBINVD); 417 PRINTFLAG(WBINVD_FLUSH); 418 PRINTFLAG(PROC_C1); 419 PRINTFLAG(P_LVL2_UP); 420 PRINTFLAG(PWR_BUTTON); 421 PRINTFLAG(SLP_BUTTON); 422 PRINTFLAG(FIX_RTC); 423 PRINTFLAG(RTC_S4); 424 PRINTFLAG(TMR_VAL_EXT); 425 PRINTFLAG(DCK_CAP); 426 427 fprintf(fhdr, "}\n"); 428 fprintf(fhdr, "\n"); 429 } 430 431 void 432 acpi_print_dsdt(struct ACPIsdt *dsdp) 433 { 434 acpi_print_sdt(dsdp); 435 } 436 437 void 438 acpi_handle_dsdt(struct ACPIsdt *dsdp) 439 { 440 u_int8_t *dp; 441 u_int8_t *end; 442 443 acpi_print_dsdt(dsdp); 444 445 dp = (u_int8_t *)dsdp->body; 446 end = (u_int8_t *)dsdp + dsdp->len; 447 } 448 449 void 450 acpi_handle_facp(struct FACPbody *facp) 451 { 452 struct ACPIsdt *dsdp; 453 454 acpi_print_facp(facp); 455 dsdp = (struct ACPIsdt *) acpi_map_sdt(facp->dsdt_ptr); 456 if (acpi_checksum(dsdp, dsdp->len)) 457 errx(1, "DSDT is corrupt"); 458 acpi_handle_dsdt(dsdp); 459 aml_dump(dsdp); 460 } 461 462 void 463 acpi_handle_rsdt(struct ACPIsdt *rsdp) 464 { 465 int i; 466 int entries; 467 struct ACPIsdt *sdp; 468 469 aml_dump(rsdp); 470 entries = (rsdp->len - SIZEOF_SDT_HDR) / sizeof(u_int32_t); 471 acpi_print_rsdt(rsdp); 472 for (i = 0; i < entries; i++) { 473 sdp = (struct ACPIsdt *) acpi_map_sdt(rsdp->body[i]); 474 if (acpi_checksum(sdp, sdp->len)) 475 errx(1, "RSDT entry %d is corrupt", i); 476 aml_dump(sdp); 477 if (!memcmp(sdp->signature, "FACP", 4)) { 478 acpi_handle_facp((struct FACPbody *) sdp->body); 479 } else { 480 acpi_print_sdt(sdp); 481 } 482 } 483 } 484 485 void 486 asl_dump_from_devmem(void) 487 { 488 struct ACPIrsdp *rp; 489 struct ACPIsdt *rsdp; 490 char name[PATH_MAX]; 491 492 snprintf(name, sizeof(name), "%s.headers", aml_dumpfile); 493 494 rp = acpi_find_rsd_ptr(); 495 if (!rp) 496 errx(1, "Can't find ACPI information"); 497 498 fhdr = fopen(name, "w"); 499 if (fhdr == NULL) 500 err(1, "asl_dump_from_devmem"); 501 502 acpi_print_rsd_ptr(rp); 503 rsdp = (struct ACPIsdt *) acpi_map_sdt(rp->addr); 504 if (memcmp(rsdp->signature, "RSDT", 4) || 505 acpi_checksum(rsdp, rsdp->len)) 506 errx(1, "RSDT is corrupted"); 507 508 acpi_handle_rsdt(rsdp); 509 510 fclose(fhdr); 511 } 512 513 void 514 usage(void) 515 { 516 extern char *__progname; 517 518 fprintf(stderr, "%s -o prefix_for_output\n", __progname); 519 exit(1); 520 } 521 522 int 523 main(int argc, char *argv[]) 524 { 525 char c; 526 527 while ((c = getopt(argc, argv, "o:")) != -1) { 528 if (c == 'o') 529 aml_dumpfile = optarg; 530 else 531 usage(); 532 } 533 534 if (aml_dumpfile == NULL) 535 usage(); 536 537 asl_dump_from_devmem(); 538 539 return (0); 540 } 541