1/*- 2 * Copyright (c) 1998 Doug Rabson 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 * $FreeBSD$ 28 */ 29 30#include <sys/param.h> 31#include <sys/endian.h> 32#include <sys/mman.h> 33#include <sys/queue.h> 34#include <sys/stat.h> 35#include <sys/sysctl.h> 36#include <sys/wait.h> 37#include <assert.h> 38#include <err.h> 39#include <fcntl.h> 40#include <stdio.h> 41#include <stdlib.h> 42#include <string.h> 43#include <unistd.h> 44#include <paths.h> 45#include <devinfo.h> 46 47#include "acpidump.h" 48 49static void acpi_handle_apic(struct ACPIsdt *sdp); 50static struct ACPIsdt *acpi_map_sdt(vm_offset_t pa); 51static void acpi_handle_rsdt(struct ACPIsdt *rsdp); 52static struct acpi_user_mapping *acpi_user_find_mapping(vm_offset_t, size_t); 53static void * acpi_map_physical(vm_offset_t, size_t); 54 55/* Size of an address. 32-bit for ACPI 1.0, 64-bit for ACPI 2.0 and up. */ 56static int addr_size; 57 58static int ncpu; 59 60int acpi_detect(void); 61 62static void 63acpi_handle_apic(struct ACPIsdt *sdp) 64{ 65 struct MADTbody *madtp; 66 struct MADT_APIC *mp; 67 struct MADT_local_apic *apic; 68 struct MADT_local_sapic *sapic; 69 70 madtp = (struct MADTbody *) sdp->body; 71 mp = (struct MADT_APIC *)madtp->body; 72 while (((uintptr_t)mp) - ((uintptr_t)sdp) < sdp->len) { 73 switch (mp->type) { 74 case ACPI_MADT_APIC_TYPE_LOCAL_APIC: 75 apic = &mp->body.local_apic; 76 warnx("MADT: Found CPU APIC ID %d %s", 77 apic->cpu_id, 78 apic->flags & ACPI_MADT_APIC_LOCAL_FLAG_ENABLED ? 79 "enabled" : "disabled"); 80 if (apic->flags & ACPI_MADT_APIC_LOCAL_FLAG_ENABLED) 81 ncpu++; 82 break; 83 case ACPI_MADT_APIC_TYPE_LOCAL_SAPIC: 84 sapic = &mp->body.local_sapic; 85 warnx("MADT: Found CPU SAPIC ID %d %s", 86 sapic->cpu_id, 87 sapic->flags & ACPI_MADT_APIC_LOCAL_FLAG_ENABLED ? 88 "enabled" : "disabled"); 89 /* XXX is enable flag the same? */ 90 if (sapic->flags & ACPI_MADT_APIC_LOCAL_FLAG_ENABLED) 91 ncpu++; 92 break; 93 default: 94 break; 95 } 96 mp = (struct MADT_APIC *) ((char *)mp + mp->len); 97 } 98} 99 100static int 101acpi_checksum(void *p, size_t length) 102{ 103 u_int8_t *bp; 104 u_int8_t sum; 105 106 bp = p; 107 sum = 0; 108 while (length--) 109 sum += *bp++; 110 111 return (sum); 112} 113 114static struct ACPIsdt * 115acpi_map_sdt(vm_offset_t pa) 116{ 117 struct ACPIsdt *sp; 118 119 sp = acpi_map_physical(pa, sizeof(struct ACPIsdt)); 120 sp = acpi_map_physical(pa, sp->len); 121 return (sp); 122} 123 124static void 125acpi_handle_rsdt(struct ACPIsdt *rsdp) 126{ 127 struct ACPIsdt *sdp; 128 vm_offset_t addr; 129 int entries, i; 130 131 entries = (rsdp->len - SIZEOF_SDT_HDR) / addr_size; 132 for (i = 0; i < entries; i++) { 133 switch (addr_size) { 134 case 4: 135 addr = le32dec((char*)rsdp->body + i * addr_size); 136 break; 137 case 8: 138 addr = le64dec((char*)rsdp->body + i * addr_size); 139 break; 140 default: 141 assert((addr = 0)); 142 } 143 144 sdp = (struct ACPIsdt *)acpi_map_sdt(addr); 145 if (acpi_checksum(sdp, sdp->len)) { 146#if 0 147 warnx("RSDT entry %d (sig %.4s) has bad checksum", i, 148 sdp->signature); 149#endif 150 continue; 151 } 152 if (!memcmp(sdp->signature, "APIC", 4)) 153 acpi_handle_apic(sdp); 154 } 155} 156 157static char machdep_acpi_root[] = "machdep.acpi_root"; 158static int acpi_mem_fd = -1; 159 160struct 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 167LIST_HEAD(acpi_user_mapping_list, acpi_user_mapping) maplist; 168 169static void 170acpi_user_init(void) 171{ 172 173 if (acpi_mem_fd == -1) { 174 acpi_mem_fd = open(_PATH_MEM, O_RDONLY); 175 if (acpi_mem_fd == -1) 176 err(1, "opening " _PATH_MEM); 177 LIST_INIT(&maplist); 178 } 179} 180 181static struct acpi_user_mapping * 182acpi_user_find_mapping(vm_offset_t pa, size_t size) 183{ 184 struct acpi_user_mapping *map; 185 186 /* First search for an existing mapping */ 187 for (map = LIST_FIRST(&maplist); map; map = LIST_NEXT(map, link)) { 188 if (map->pa <= pa && map->size >= pa + size - map->pa) 189 return (map); 190 } 191 192 /* Then create a new one */ 193 size = round_page(pa + size) - trunc_page(pa); 194 pa = trunc_page(pa); 195 map = malloc(sizeof(struct acpi_user_mapping)); 196 if (!map) 197 errx(1, "out of memory"); 198 map->pa = pa; 199 map->va = mmap(0, size, PROT_READ, MAP_SHARED, acpi_mem_fd, pa); 200 map->size = size; 201 if ((intptr_t) map->va == -1) 202 err(1, "can't map address"); 203 LIST_INSERT_HEAD(&maplist, map, link); 204 205 return (map); 206} 207 208static void * 209acpi_map_physical(vm_offset_t pa, size_t size) 210{ 211 struct acpi_user_mapping *map; 212 213 map = acpi_user_find_mapping(pa, size); 214 return (map->va + (pa - map->pa)); 215} 216 217static struct ACPIrsdp * 218acpi_get_rsdp(u_long addr) 219{ 220 struct ACPIrsdp rsdp; 221 size_t len; 222 223 /* Read in the table signature and check it. */ 224 pread(acpi_mem_fd, &rsdp, 8, addr); 225 if (memcmp(rsdp.signature, "RSD PTR ", 8)) 226 return (NULL); 227 228 /* Read the entire table. */ 229 pread(acpi_mem_fd, &rsdp, sizeof(rsdp), addr); 230 231 /* Run the checksum only over the version 1 header. */ 232 if (acpi_checksum(&rsdp, 20)) 233 return (NULL); 234 235 /* If the revision is 0, assume a version 1 length. */ 236 if (rsdp.revision == 0) 237 len = 20; 238 else 239 len = rsdp.length; 240 241 /* XXX Should handle ACPI 2.0 RSDP extended checksum here. */ 242 243 return (acpi_map_physical(addr, len)); 244} 245 246static const char * 247devstate(devinfo_state_t state) 248{ 249 switch (state) { 250 case DS_NOTPRESENT: 251 return "not-present"; 252 case DS_ALIVE: 253 return "alive"; 254 case DS_ATTACHED: 255 return "attached"; 256 case DS_BUSY: 257 return "busy"; 258 default: 259 return "unknown-state"; 260 } 261} 262 263static int 264acpi0_check(struct devinfo_dev *dd, void *arg) 265{ 266 printf("%s: %s %s\n", __func__, dd->dd_name, devstate(dd->dd_state)); 267 /* NB: device must be present AND attached */ 268 if (strcmp(dd->dd_name, "acpi0") == 0) 269 return (dd->dd_state == DS_ATTACHED || 270 dd->dd_state == DS_BUSY); 271 return devinfo_foreach_device_child(dd, acpi0_check, arg); 272} 273 274static int 275acpi0_present(void) 276{ 277 struct devinfo_dev *root; 278 int found; 279 280 found = 0; 281 devinfo_init(); 282 root = devinfo_handle_to_device(DEVINFO_ROOT_DEVICE); 283 if (root != NULL) 284 found = devinfo_foreach_device_child(root, acpi0_check, NULL); 285 devinfo_free(); 286 return found; 287} 288 289int 290acpi_detect(void) 291{ 292 struct ACPIrsdp *rp; 293 struct ACPIsdt *rsdp; 294 u_long addr; 295 size_t len; 296 297 if (!acpi0_present()) { 298 warnx("no acpi0 device located"); 299 return -1; 300 } 301 302 acpi_user_init(); 303 304 /* Attempt to use sysctl to find RSD PTR record. */ 305 len = sizeof(addr); 306 if (sysctlbyname(machdep_acpi_root, &addr, &len, NULL, 0) != 0) { 307 warnx("cannot find ACPI information"); 308 return -1; 309 } 310 rp = acpi_get_rsdp(addr); 311 if (rp == NULL) { 312 warnx("cannot find ACPI information: sysctl %s does not point to RSDP", 313 machdep_acpi_root); 314 return -1; 315 } 316 if (rp->revision < 2) { 317 rsdp = (struct ACPIsdt *)acpi_map_sdt(rp->rsdt_addr); 318 if (memcmp(rsdp->signature, "RSDT", 4) != 0 || 319 acpi_checksum(rsdp, rsdp->len) != 0) 320 errx(1, "RSDT is corrupted"); 321 addr_size = sizeof(uint32_t); 322 } else { 323 rsdp = (struct ACPIsdt *)acpi_map_sdt(rp->xsdt_addr); 324 if (memcmp(rsdp->signature, "XSDT", 4) != 0 || 325 acpi_checksum(rsdp, rsdp->len) != 0) 326 errx(1, "XSDT is corrupted"); 327 addr_size = sizeof(uint64_t); 328 } 329 ncpu = 0; 330 acpi_handle_rsdt(rsdp); 331 return (ncpu == 0 ? 1 : ncpu); 332} 333