/* $OpenBSD: acpidump.c,v 1.13 2015/10/03 01:05:12 deraadt Exp $ */ /* * Copyright (c) 2000 Mitsuru IWASAKI * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define vm_page_size sysconf(_SC_PAGESIZE) #define PRINTFLAG(xx) \ do { \ if (facp->flags & ACPI_FACP_FLAG_## xx) { \ fprintf(fhdr, "%c%s", sep, #xx); sep = ','; \ } \ } while (0) typedef unsigned long vm_offset_t; struct ACPIrsdp { u_char signature[8]; u_char sum; u_char oem[6]; u_char res; u_int32_t addr; } __packed; struct ACPIsdt { u_char signature[4]; u_int32_t len; u_char rev; u_char check; u_char oemid[6]; u_char oemtblid[8]; u_int32_t oemrev; u_char creator[4]; u_int32_t crerev; #define SIZEOF_SDT_HDR 36 /* struct size except body */ u_int32_t body[1];/* This member should be casted */ } __packed; struct ACPIgas { u_int8_t address_space_id; #define ACPI_GAS_MEMORY 0 #define ACPI_GAS_IO 1 #define ACPI_GAS_PCI 2 #define ACPI_GAS_EMBEDDED 3 #define ACPI_GAS_SMBUS 4 #define ACPI_GAS_FIXED 0x7f u_int8_t register_bit_width; u_int8_t register_bit_offset; u_int8_t res; u_int64_t address; } __packed; struct FACPbody { u_int32_t facs_ptr; u_int32_t dsdt_ptr; u_int8_t int_model; #define ACPI_FACP_INTMODEL_PIC 0 /* Standard PC-AT PIC */ #define ACPI_FACP_INTMODEL_APIC 1 /* Multiple APIC */ u_char reserved1; u_int16_t sci_int; u_int32_t smi_cmd; u_int8_t acpi_enable; u_int8_t acpi_disable; u_int8_t s4biosreq; u_int8_t reserved2; u_int32_t pm1a_evt_blk; u_int32_t pm1b_evt_blk; u_int32_t pm1a_cnt_blk; u_int32_t pm1b_cnt_blk; u_int32_t pm2_cnt_blk; u_int32_t pm_tmr_blk; u_int32_t gpe0_blk; u_int32_t gpe1_blk; u_int8_t pm1_evt_len; u_int8_t pm1_cnt_len; u_int8_t pm2_cnt_len; u_int8_t pm_tmr_len; u_int8_t gpe0_len; u_int8_t gpe1_len; u_int8_t gpe1_base; u_int8_t reserved3; u_int16_t p_lvl2_lat; u_int16_t p_lvl3_lat; u_int16_t flush_size; u_int16_t flush_stride; u_int8_t duty_off; u_int8_t duty_width; u_int8_t day_alrm; u_int8_t mon_alrm; u_int8_t century; u_int16_t iapc_boot_arch; u_char reserved4[1]; u_int32_t flags; #define ACPI_FACP_FLAG_WBINVD 1 /* WBINVD is correctly supported */ #define ACPI_FACP_FLAG_WBINVD_FLUSH 2 /* WBINVD flushes caches */ #define ACPI_FACP_FLAG_PROC_C1 4 /* C1 power state supported */ #define ACPI_FACP_FLAG_P_LVL2_UP 8 /* C2 power state works on SMP */ #define ACPI_FACP_FLAG_PWR_BUTTON 16 /* Power button uses control method */ #define ACPI_FACP_FLAG_SLP_BUTTON 32 /* Sleep button uses control method */ #define ACPI_FACP_FLAG_FIX_RTC 64 /* RTC wakeup not supported */ #define ACPI_FACP_FLAG_RTC_S4 128 /* RTC can wakeup from S4 state */ #define ACPI_FACP_FLAG_TMR_VAL_EXT 256 /* TMR_VAL is 32bit */ #define ACPI_FACP_FLAG_DCK_CAP 512 /* Can support docking */ struct ACPIgas reset_reg; u_int8_t reset_value; u_int8_t reserved5[3]; u_int64_t x_firmware_ctrl; u_int64_t x_dsdt; struct ACPIgas x_pm1a_evt_blk; struct ACPIgas x_pm1b_evt_blk; struct ACPIgas x_pm1a_cnt_blk; struct ACPIgas x_pm1b_cnt_blk; struct ACPIgas x_pm2_cnt_blk; struct ACPIgas x_pm_tmr_blk; struct ACPIgas x_gpe0_blk; struct ACPIgas x_gpe1_blk; } __packed; struct acpi_user_mapping { LIST_ENTRY(acpi_user_mapping) link; vm_offset_t pa; caddr_t va; size_t size; }; LIST_HEAD(acpi_user_mapping_list, acpi_user_mapping) maplist; int acpi_mem_fd = -1; char *aml_dumpfile; FILE *fhdr; int acpi_checksum(void *_p, size_t _length); struct acpi_user_mapping *acpi_user_find_mapping(vm_offset_t _pa, size_t _size); void *acpi_map_physical(vm_offset_t _pa, size_t _size); void acpi_user_init(void); struct ACPIrsdp *acpi_find_rsd_ptr(void); void acpi_print_string(char *_s, size_t _length); void acpi_print_rsd_ptr(struct ACPIrsdp *_rp); struct ACPIsdt *acpi_map_sdt(vm_offset_t _pa); void aml_dump(struct ACPIsdt *_hdr); void acpi_print_sdt(struct ACPIsdt *_sdp); void acpi_print_rsdt(struct ACPIsdt *_rsdp); void acpi_print_facp(struct FACPbody *_facp); void acpi_print_dsdt(struct ACPIsdt *_dsdp); void acpi_handle_dsdt(struct ACPIsdt *_dsdp); void acpi_handle_facp(struct FACPbody *_facp); void acpi_handle_rsdt(struct ACPIsdt *_rsdp); void asl_dump_from_devmem(void); void usage(void); u_long bios_acpi_addr(void); struct ACPIsdt dsdt_header = { "DSDT", 0, 1, 0, "OEMID", "OEMTBLID", 0x12345678, "CRTR", 0x12345678 }; int acpi_checksum(void *p, size_t length) { u_int8_t *bp; u_int8_t sum; bp = p; sum = 0; while (length--) sum += *bp++; return (sum); } struct acpi_user_mapping * acpi_user_find_mapping(vm_offset_t pa, size_t size) { struct acpi_user_mapping *map; int page_mask = getpagesize() - 1; /* First search for an existing mapping */ for (map = LIST_FIRST(&maplist); map; map = LIST_NEXT(map, link)) { if (map->pa <= pa && map->size >= pa + size - map->pa) return (map); } /* Then create a new one */ #undef round_page #undef trunc_page #define round_page(x) (((x) + page_mask) & ~page_mask) #define trunc_page(x) ((x) & ~page_mask) size = round_page(pa + size) - trunc_page(pa); pa = trunc_page(pa); #undef round_page #undef trunc_page map = malloc(sizeof(struct acpi_user_mapping)); if (!map) errx(1, "out of memory"); map->pa = pa; map->va = mmap(0, size, PROT_READ, MAP_SHARED, acpi_mem_fd, pa); map->size = size; if (map->va == MAP_FAILED) err(1, "can't map address"); LIST_INSERT_HEAD(&maplist, map, link); return (map); } void * acpi_map_physical(vm_offset_t pa, size_t size) { struct acpi_user_mapping *map; map = acpi_user_find_mapping(pa, size); return (map->va + (pa - map->pa)); } void acpi_user_init(void) { if (acpi_mem_fd == -1) { acpi_mem_fd = open("/dev/mem", O_RDONLY); if (acpi_mem_fd == -1) err(1, "opening /dev/mem"); LIST_INIT(&maplist); } } struct ACPIrsdp * acpi_find_rsd_ptr(void) { int i; u_int8_t buf[sizeof(struct ACPIrsdp)]; u_long addr; if ((addr = bios_acpi_addr()) != 0) { lseek(acpi_mem_fd, addr, SEEK_SET); read(acpi_mem_fd, buf, 16); if (!memcmp(buf, "RSD PTR ", 8)) { read(acpi_mem_fd, buf + 16, sizeof(struct ACPIrsdp) - 16); if (!acpi_checksum(buf, sizeof(struct ACPIrsdp))) return (acpi_map_physical(addr, sizeof(struct ACPIrsdp))); } lseek(acpi_mem_fd, 0, SEEK_SET); } for (i = 0; i < 1024 * 1024; i += 16) { lseek(acpi_mem_fd, i, SEEK_SET); read(acpi_mem_fd, buf, 16); if (!memcmp(buf, "RSD PTR ", 8)) { /* Read the rest of the structure */ read(acpi_mem_fd, buf + 16, sizeof(struct ACPIrsdp) - 16); /* Verify checksum before accepting it. */ if (acpi_checksum(buf, sizeof(struct ACPIrsdp))) continue; return (acpi_map_physical(i, sizeof(struct ACPIrsdp))); } } return (0); } void acpi_print_string(char *s, size_t length) { int c; /* Trim trailing spaces and NULLs */ while (length > 0 && (s[length - 1] == ' ' || s[length - 1] == '\0')) length--; while (length--) { c = *s++; fputc(c, fhdr); } } void acpi_print_rsd_ptr(struct ACPIrsdp *rp) { fprintf(fhdr, "\n"); fprintf(fhdr, "RSD PTR: Checksum=%d, OEMID=", rp->sum); acpi_print_string(rp->oem, 6); fprintf(fhdr, ", RsdtAddress=0x%08x\n", rp->addr); fprintf(fhdr, "\n"); } struct ACPIsdt * acpi_map_sdt(vm_offset_t pa) { struct ACPIsdt *sp; sp = acpi_map_physical(pa, sizeof(struct ACPIsdt)); sp = acpi_map_physical(pa, sp->len); return (sp); } void aml_dump(struct ACPIsdt *hdr) { static int hdr_index; char name[PATH_MAX]; int fd; mode_t mode; snprintf(name, sizeof(name), "%s.%c%c%c%c.%d", aml_dumpfile, hdr->signature[0], hdr->signature[1], hdr->signature[2], hdr->signature[3], hdr_index++); mode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); fd = open(name, O_WRONLY | O_CREAT | O_TRUNC, mode); if (fd == -1) err(1, "aml_dump"); write(fd, hdr, SIZEOF_SDT_HDR); write(fd, hdr->body, hdr->len - SIZEOF_SDT_HDR); close(fd); } void acpi_print_sdt(struct ACPIsdt *sdp) { fprintf(fhdr, "\n"); acpi_print_string(sdp->signature, 4); fprintf(fhdr, ": Length=%d, Revision=%d, Checksum=%d,\n", sdp->len, sdp->rev, sdp->check); fprintf(fhdr, "\tOEMID="); acpi_print_string(sdp->oemid, 6); fprintf(fhdr, ", OEM Table ID="); acpi_print_string(sdp->oemtblid, 8); fprintf(fhdr, ", OEM Revision=0x%x,\n", sdp->oemrev); fprintf(fhdr, "\tCreator ID="); acpi_print_string(sdp->creator, 4); fprintf(fhdr, ", Creator Revision=0x%x\n", sdp->crerev); fprintf(fhdr, "\n"); if (!memcmp(sdp->signature, "DSDT", 4)) memcpy(&dsdt_header, sdp, sizeof(dsdt_header)); } void acpi_print_rsdt(struct ACPIsdt *rsdp) { int i, entries; acpi_print_sdt(rsdp); entries = (rsdp->len - SIZEOF_SDT_HDR) / sizeof(u_int32_t); fprintf(fhdr, "\n"); fprintf(fhdr, "\tEntries={ "); for (i = 0; i < entries; i++) { if (i > 0) fprintf(fhdr, ", "); fprintf(fhdr, "0x%08x", rsdp->body[i]); } fprintf(fhdr, " }\n"); fprintf(fhdr, "\n"); } void acpi_print_facp(struct FACPbody *facp) { char sep; fprintf(fhdr, "\n"); fprintf(fhdr, "\tDSDT=0x%x\n", facp->dsdt_ptr); fprintf(fhdr, "\tINT_MODEL=%s\n", facp->int_model ? "APIC" : "PIC"); fprintf(fhdr, "\tSCI_INT=%d\n", facp->sci_int); fprintf(fhdr, "\tSMI_CMD=0x%x, ", facp->smi_cmd); fprintf(fhdr, "ACPI_ENABLE=0x%x, ", facp->acpi_enable); fprintf(fhdr, "ACPI_DISABLE=0x%x, ", facp->acpi_disable); fprintf(fhdr, "S4BIOS_REQ=0x%x\n", facp->s4biosreq); if (facp->pm1a_evt_blk) fprintf(fhdr, "\tPM1a_EVT_BLK=0x%x-0x%x\n", facp->pm1a_evt_blk, facp->pm1a_evt_blk + facp->pm1_evt_len - 1); if (facp->pm1b_evt_blk) fprintf(fhdr, "\tPM1b_EVT_BLK=0x%x-0x%x\n", facp->pm1b_evt_blk, facp->pm1b_evt_blk + facp->pm1_evt_len - 1); if (facp->pm1a_cnt_blk) fprintf(fhdr, "\tPM1a_CNT_BLK=0x%x-0x%x\n", facp->pm1a_cnt_blk, facp->pm1a_cnt_blk + facp->pm1_cnt_len - 1); if (facp->pm1b_cnt_blk) fprintf(fhdr, "\tPM1b_CNT_BLK=0x%x-0x%x\n", facp->pm1b_cnt_blk, facp->pm1b_cnt_blk + facp->pm1_cnt_len - 1); if (facp->pm2_cnt_blk) fprintf(fhdr, "\tPM2_CNT_BLK=0x%x-0x%x\n", facp->pm2_cnt_blk, facp->pm2_cnt_blk + facp->pm2_cnt_len - 1); if (facp->pm_tmr_blk) fprintf(fhdr, "\tPM2_TMR_BLK=0x%x-0x%x\n", facp->pm_tmr_blk, facp->pm_tmr_blk + facp->pm_tmr_len - 1); if (facp->gpe0_blk) fprintf(fhdr, "\tPM2_GPE0_BLK=0x%x-0x%x\n", facp->gpe0_blk, facp->gpe0_blk + facp->gpe0_len - 1); if (facp->gpe1_blk) fprintf(fhdr, "\tPM2_GPE1_BLK=0x%x-0x%x, GPE1_BASE=%d\n", facp->gpe1_blk, facp->gpe1_blk + facp->gpe1_len - 1, facp->gpe1_base); fprintf(fhdr, "\tP_LVL2_LAT=%dms, P_LVL3_LAT=%dms\n", facp->p_lvl2_lat, facp->p_lvl3_lat); fprintf(fhdr, "\tFLUSH_SIZE=%d, FLUSH_STRIDE=%d\n", facp->flush_size, facp->flush_stride); fprintf(fhdr, "\tDUTY_OFFSET=%d, DUTY_WIDTH=%d\n", facp->duty_off, facp->duty_width); fprintf(fhdr, "\tDAY_ALRM=%d, MON_ALRM=%d, CENTURY=%d\n", facp->day_alrm, facp->mon_alrm, facp->century); fprintf(fhdr, "\tFlags="); sep = '{'; PRINTFLAG(WBINVD); PRINTFLAG(WBINVD_FLUSH); PRINTFLAG(PROC_C1); PRINTFLAG(P_LVL2_UP); PRINTFLAG(PWR_BUTTON); PRINTFLAG(SLP_BUTTON); PRINTFLAG(FIX_RTC); PRINTFLAG(RTC_S4); PRINTFLAG(TMR_VAL_EXT); PRINTFLAG(DCK_CAP); fprintf(fhdr, "}\n"); fprintf(fhdr, "\n"); } void acpi_print_dsdt(struct ACPIsdt *dsdp) { acpi_print_sdt(dsdp); } void acpi_handle_dsdt(struct ACPIsdt *dsdp) { u_int8_t *dp; u_int8_t *end; acpi_print_dsdt(dsdp); dp = (u_int8_t *)dsdp->body; end = (u_int8_t *)dsdp + dsdp->len; } void acpi_handle_facp(struct FACPbody *facp) { struct ACPIsdt *dsdp; acpi_print_facp(facp); dsdp = (struct ACPIsdt *) acpi_map_sdt(facp->dsdt_ptr); if (acpi_checksum(dsdp, dsdp->len)) errx(1, "DSDT is corrupt"); acpi_handle_dsdt(dsdp); aml_dump(dsdp); } void acpi_handle_rsdt(struct ACPIsdt *rsdp) { int i; int entries; struct ACPIsdt *sdp; aml_dump(rsdp); entries = (rsdp->len - SIZEOF_SDT_HDR) / sizeof(u_int32_t); acpi_print_rsdt(rsdp); for (i = 0; i < entries; i++) { sdp = (struct ACPIsdt *) acpi_map_sdt(rsdp->body[i]); if (acpi_checksum(sdp, sdp->len)) errx(1, "RSDT entry %d is corrupt", i); aml_dump(sdp); if (!memcmp(sdp->signature, "FACP", 4)) { acpi_handle_facp((struct FACPbody *) sdp->body); } else { acpi_print_sdt(sdp); } } } void asl_dump_from_devmem(void) { struct ACPIrsdp *rp; struct ACPIsdt *rsdp; char name[PATH_MAX]; snprintf(name, sizeof(name), "%s.headers", aml_dumpfile); acpi_user_init(); if (tame("stdio wpath cpath", NULL) == -1) err(1, "tame"); rp = acpi_find_rsd_ptr(); if (!rp) errx(1, "Can't find ACPI information"); fhdr = fopen(name, "w"); if (fhdr == NULL) err(1, "asl_dump_from_devmem"); acpi_print_rsd_ptr(rp); rsdp = (struct ACPIsdt *) acpi_map_sdt(rp->addr); if (memcmp(rsdp->signature, "RSDT", 4) || acpi_checksum(rsdp, rsdp->len)) errx(1, "RSDT is corrupted"); acpi_handle_rsdt(rsdp); fclose(fhdr); } void usage(void) { extern char *__progname; fprintf(stderr, "%s -o prefix_for_output\n", __progname); exit(1); } int main(int argc, char *argv[]) { char c; while ((c = getopt(argc, argv, "o:")) != -1) { switch (c) { case 'o': aml_dumpfile = optarg; break; default: usage(); break; } } if (aml_dumpfile == NULL) usage(); asl_dump_from_devmem(); return (0); } u_long bios_acpi_addr(void) { kvm_t *kd; struct nlist nl[2]; bios_efiinfo_t efiinfo; u_long ptr; memset(&nl, 0, sizeof(nl)); kd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, NULL); if (kd == NULL) goto on_error; nl[0].n_name = "_bios_efiinfo"; if (kvm_nlist(kd, nl) == -1) goto on_error; if (kvm_read(kd, nl[0].n_value, &ptr, sizeof(ptr)) == -1) goto on_error; if (kvm_read(kd, ptr, &efiinfo, sizeof(efiinfo)) == -1) goto on_error; kvm_close(kd); return (efiinfo.config_acpi); on_error: if (kd != NULL) kvm_close(kd); return (0); }