/*- * Copyright (c) 1998 Doug Rabson * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "acpidump.h" #include "sysinstall.h" static void acpi_handle_apic(struct ACPIsdt *sdp); static struct ACPIsdt *acpi_map_sdt(vm_offset_t pa); static void acpi_handle_rsdt(struct ACPIsdt *rsdp); static struct acpi_user_mapping *acpi_user_find_mapping(vm_offset_t, size_t); static void * acpi_map_physical(vm_offset_t, size_t); /* Size of an address. 32-bit for ACPI 1.0, 64-bit for ACPI 2.0 and up. */ static int addr_size; static int ncpu; static void acpi_handle_apic(struct ACPIsdt *sdp) { struct MADTbody *madtp; struct MADT_APIC *mp; struct MADT_local_apic *apic; struct MADT_local_sapic *sapic; madtp = (struct MADTbody *) sdp->body; mp = (struct MADT_APIC *)madtp->body; while (((uintptr_t)mp) - ((uintptr_t)sdp) < sdp->len) { switch (mp->type) { case ACPI_MADT_APIC_TYPE_LOCAL_APIC: apic = &mp->body.local_apic; msgDebug("MADT: Found CPU APIC ID %d %s\n", apic->cpu_id, apic->flags & ACPI_MADT_APIC_LOCAL_FLAG_ENABLED ? "enabled" : "disabled"); if (apic->flags & ACPI_MADT_APIC_LOCAL_FLAG_ENABLED) ncpu++; break; case ACPI_MADT_APIC_TYPE_LOCAL_SAPIC: sapic = &mp->body.local_sapic; msgDebug("MADT: Found CPU SAPIC ID %d %s\n", sapic->cpu_id, sapic->flags & ACPI_MADT_APIC_LOCAL_FLAG_ENABLED ? "enabled" : "disabled"); /* XXX is enable flag the same? */ if (sapic->flags & ACPI_MADT_APIC_LOCAL_FLAG_ENABLED) ncpu++; break; default: break; } mp = (struct MADT_APIC *) ((char *)mp + mp->len); } } static 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); } static struct ACPIsdt * acpi_map_sdt(vm_offset_t pa) { struct ACPIsdt *sp; sp = acpi_map_physical(pa, sizeof(struct ACPIsdt)); if (sp != NULL) sp = acpi_map_physical(pa, sp->len); return (sp); } static void acpi_handle_rsdt(struct ACPIsdt *rsdp) { struct ACPIsdt *sdp; vm_offset_t addr; int entries, i; entries = (rsdp->len - SIZEOF_SDT_HDR) / addr_size; for (i = 0; i < entries; i++) { switch (addr_size) { case 4: addr = le32dec((char*)rsdp->body + i * addr_size); break; case 8: addr = le64dec((char*)rsdp->body + i * addr_size); break; default: assert((addr = 0)); } sdp = (struct ACPIsdt *)acpi_map_sdt(addr); if (sdp == NULL) { msgDebug("%s: unable to map sdt\n", __func__); continue; } if (acpi_checksum(sdp, sdp->len)) { #if 0 msgDebug("RSDT entry %d (sig %.4s) has bad checksum\n", i, sdp->signature); #endif continue; } if (!memcmp(sdp->signature, "APIC", 4)) acpi_handle_apic(sdp); } } static char machdep_acpi_root[] = "machdep.acpi_root"; static int acpi_mem_fd = -1; 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; static int acpi_user_init(void) { if (acpi_mem_fd == -1) { acpi_mem_fd = open(_PATH_MEM, O_RDONLY); if (acpi_mem_fd == -1) { msgDebug("%s: error opening %s: %s\n", __func__, _PATH_MEM, strerror(errno)); return 0; } LIST_INIT(&maplist); } return 1; } static struct acpi_user_mapping * acpi_user_find_mapping(vm_offset_t pa, size_t size) { struct acpi_user_mapping *map; /* 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 */ size = round_page(pa + size) - trunc_page(pa); pa = trunc_page(pa); map = malloc(sizeof(struct acpi_user_mapping)); if (!map) { msgDebug("%s: out of memory: %s\n", __func__, strerror(errno)); return (map); } map->pa = pa; map->va = mmap(0, size, PROT_READ, MAP_SHARED, acpi_mem_fd, pa); map->size = size; if ((intptr_t) map->va == -1) { msgDebug("%s: can't mmap address %lu size %lu: %s\n", __func__, (unsigned long) pa, (unsigned long) size, strerror(errno)); free(map); return (NULL); } LIST_INSERT_HEAD(&maplist, map, link); return (map); } static 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 == NULL ? NULL : map->va + (pa - map->pa)); } static struct ACPIrsdp * acpi_get_rsdp(u_long addr) { struct ACPIrsdp rsdp; size_t len; /* Read in the table signature and check it. */ pread(acpi_mem_fd, &rsdp, 8, addr); if (memcmp(rsdp.signature, "RSD PTR ", 8)) return (NULL); /* Read the entire table. */ pread(acpi_mem_fd, &rsdp, sizeof(rsdp), addr); /* Run the checksum only over the version 1 header. */ if (acpi_checksum(&rsdp, 20)) return (NULL); /* If the revision is 0, assume a version 1 length. */ if (rsdp.revision == 0) len = 20; else len = rsdp.length; /* XXX Should handle ACPI 2.0 RSDP extended checksum here. */ return (acpi_map_physical(addr, len)); } static const char * devstate(devinfo_state_t state) { switch (state) { case DIS_NOTPRESENT: return "not-present"; case DIS_ALIVE: return "alive"; case DIS_ATTACHED: return "attached"; case DIS_BUSY: return "busy"; default: return "unknown-state"; } } static int acpi0_check(struct devinfo_dev *dd, void *arg) { printf("%s: %s %s\n", __func__, dd->dd_name, devstate(dd->dd_state)); /* NB: device must be present AND attached */ if (strcmp(dd->dd_name, "acpi0") == 0) return (dd->dd_state == DIS_ATTACHED || dd->dd_state == DIS_BUSY); return devinfo_foreach_device_child(dd, acpi0_check, arg); } static int acpi0_present(void) { struct devinfo_dev *root; int found; found = 0; devinfo_init(); root = devinfo_handle_to_device(DEVINFO_ROOT_DEVICE); if (root != NULL) found = devinfo_foreach_device_child(root, acpi0_check, NULL); devinfo_free(); return found; } int acpi_detect(void) { struct ACPIrsdp *rp; struct ACPIsdt *rsdp; u_long addr; size_t len; if (!acpi0_present()) { msgDebug("%s: no acpi0 device located\n", __func__); return -1; } if (!acpi_user_init()) return -1; /* Attempt to use sysctl to find RSD PTR record. */ len = sizeof(addr); if (sysctlbyname(machdep_acpi_root, &addr, &len, NULL, 0) != 0) { msgDebug("%s: cannot find ACPI information\n", __func__); return -1; } rp = acpi_get_rsdp(addr); if (rp == NULL) { msgDebug("%s: cannot find ACPI information: " "sysctl %s does not point to RSDP\n", __func__, machdep_acpi_root); return -1; } if (rp->revision < 2) { rsdp = (struct ACPIsdt *)acpi_map_sdt(rp->rsdt_addr); if (rsdp == NULL) return -1; if (memcmp(rsdp->signature, "RSDT", 4) != 0 || acpi_checksum(rsdp, rsdp->len) != 0) { msgDebug("%s: RSDT is corrupted\n", __func__); return -1; } addr_size = sizeof(uint32_t); } else { rsdp = (struct ACPIsdt *)acpi_map_sdt(rp->xsdt_addr); if (rsdp == NULL) return -1; if (memcmp(rsdp->signature, "XSDT", 4) != 0 || acpi_checksum(rsdp, rsdp->len) != 0) { msgDebug("%s: XSDT is corrupted\n", __func__); return -1; } addr_size = sizeof(uint64_t); } ncpu = 0; acpi_handle_rsdt(rsdp); return (ncpu == 0 ? 1 : ncpu); }