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