1 /* $NetBSD: acpi_user.c,v 1.6 2020/12/06 02:57:30 jmcneill Exp $ */ 2 3 /*- 4 * Copyright (c) 1999 Doug Rabson 5 * Copyright (c) 2000 Mitsuru IWASAKI <iwasaki@FreeBSD.org> 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 * $FreeBSD: head/usr.sbin/acpi/acpidump/acpi_user.c 251186 2013-05-31 17:23:38Z jkim $ 30 */ 31 32 #include <sys/cdefs.h> 33 __RCSID("$NetBSD: acpi_user.c,v 1.6 2020/12/06 02:57:30 jmcneill Exp $"); 34 35 #include <sys/param.h> 36 #include <sys/mman.h> 37 #include <sys/queue.h> 38 #include <sys/stat.h> 39 #include <sys/sysctl.h> 40 41 #include <err.h> 42 #include <errno.h> 43 #include <fcntl.h> 44 #include <stdlib.h> 45 #include <string.h> 46 #include <unistd.h> 47 48 #include "acpidump.h" 49 50 static char machdep_acpi_root[] = "hw.acpi.root"; 51 static int acpi_mem_fd = -1; 52 static bool acpi_mem_mmap = false; 53 54 struct acpi_user_mapping { 55 LIST_ENTRY(acpi_user_mapping) link; 56 vm_offset_t pa; 57 caddr_t va; 58 size_t size; 59 }; 60 61 static LIST_HEAD(acpi_user_mapping_list, acpi_user_mapping) maplist; 62 63 static void 64 acpi_user_init(void) 65 { 66 int saved_errno, use_devmem; 67 const char *s; 68 69 if (acpi_mem_fd == -1) { 70 s = getenv("ACPIDUMP_USE_DEVMEM"); 71 use_devmem = s != NULL && *s == '1'; 72 if (!use_devmem) { 73 acpi_mem_fd = open("/dev/acpi", O_RDONLY); 74 } 75 if (acpi_mem_fd == -1) { 76 saved_errno = errno; 77 acpi_mem_fd = open("/dev/mem", O_RDONLY); 78 if (acpi_mem_fd == -1) { 79 errno = saved_errno; 80 } else { 81 acpi_mem_mmap = true; 82 } 83 } 84 if (acpi_mem_fd == -1) { 85 err(EXIT_FAILURE, "opening /dev/acpi"); 86 } 87 LIST_INIT(&maplist); 88 } 89 } 90 91 static void * 92 acpi_user_read_table(vm_offset_t pa, size_t psize) 93 { 94 void *data; 95 ssize_t len; 96 97 data = calloc(1, psize); 98 if (!data) 99 errx(EXIT_FAILURE, "out of memory"); 100 101 len = pread(acpi_mem_fd, data, psize, pa); 102 if (len == -1) 103 errx(EXIT_FAILURE, "can't read table"); 104 105 return data; 106 } 107 108 static struct acpi_user_mapping * 109 acpi_user_find_mapping(vm_offset_t pa, size_t size) 110 { 111 struct acpi_user_mapping *map; 112 113 /* First search for an existing mapping */ 114 for (map = LIST_FIRST(&maplist); map; map = LIST_NEXT(map, link)) { 115 if (map->pa <= pa && map->size >= pa + size - map->pa) 116 return map; 117 } 118 119 /* Then create a new one */ 120 if (acpi_mem_mmap) { 121 size = round_page(pa + size) - trunc_page(pa); 122 pa = trunc_page(pa); 123 } 124 map = malloc(sizeof(struct acpi_user_mapping)); 125 if (!map) 126 errx(EXIT_FAILURE, "out of memory"); 127 map->pa = pa; 128 if (acpi_mem_mmap) { 129 map->va = mmap(0, size, PROT_READ, MAP_SHARED, acpi_mem_fd, pa); 130 if ((intptr_t) map->va == -1) 131 err(EXIT_FAILURE, "can't map address"); 132 } else { 133 map->va = acpi_user_read_table(pa, size); 134 } 135 map->size = size; 136 LIST_INSERT_HEAD(&maplist, map, link); 137 138 return map; 139 } 140 141 static ACPI_TABLE_RSDP * 142 acpi_get_rsdp(u_long addr) 143 { 144 ACPI_TABLE_RSDP rsdp; 145 size_t len; 146 147 /* Read in the table signature and check it. */ 148 pread(acpi_mem_fd, &rsdp, 8, addr); 149 if (memcmp(rsdp.Signature, "RSD PTR ", 8)) 150 return (NULL); 151 152 /* Read the entire table. */ 153 pread(acpi_mem_fd, &rsdp, sizeof(rsdp), addr); 154 155 /* Check the standard checksum. */ 156 if (acpi_checksum(&rsdp, ACPI_RSDP_CHECKSUM_LENGTH) != 0) 157 return (NULL); 158 159 /* Check extended checksum if table version >= 2. */ 160 if (rsdp.Revision >= 2 && 161 acpi_checksum(&rsdp, ACPI_RSDP_XCHECKSUM_LENGTH) != 0) 162 return (NULL); 163 164 /* If the revision is 0, assume a version 1 length. */ 165 if (rsdp.Revision == 0) 166 len = sizeof(ACPI_RSDP_COMMON); 167 else 168 len = rsdp.Length; 169 170 return (acpi_map_physical(addr, len)); 171 } 172 173 static ACPI_TABLE_RSDP * 174 acpi_scan_rsd_ptr(void) 175 { 176 #if defined(__amd64__) || defined(__i386__) 177 ACPI_TABLE_RSDP *rsdp; 178 u_long addr, end; 179 180 /* 181 * On ia32, scan physical memory for the RSD PTR if above failed. 182 * According to section 5.2.2 of the ACPI spec, we only consider 183 * two regions for the base address: 184 * 1. EBDA (1 KB area addressed by the 16 bit pointer at 0x40E 185 * 2. High memory (0xE0000 - 0xFFFFF) 186 */ 187 addr = ACPI_EBDA_PTR_LOCATION; 188 pread(acpi_mem_fd, &addr, sizeof(uint16_t), addr); 189 addr <<= 4; 190 end = addr + ACPI_EBDA_WINDOW_SIZE; 191 for (; addr < end; addr += 16) 192 if ((rsdp = acpi_get_rsdp(addr)) != NULL) 193 return rsdp; 194 addr = ACPI_HI_RSDP_WINDOW_BASE; 195 end = addr + ACPI_HI_RSDP_WINDOW_SIZE; 196 for (; addr < end; addr += 16) 197 if ((rsdp = acpi_get_rsdp(addr)) != NULL) 198 return rsdp; 199 #endif /* __amd64__ || __i386__ */ 200 return NULL; 201 } 202 203 /* 204 * Public interfaces 205 */ 206 ACPI_TABLE_RSDP * 207 acpi_find_rsd_ptr(void) 208 { 209 ACPI_TABLE_RSDP *rsdp; 210 u_long addr = 0; 211 size_t len = sizeof(addr); 212 213 acpi_user_init(); 214 215 if (sysctlbyname(machdep_acpi_root, &addr, &len, NULL, 0) != 0) 216 addr = 0; 217 if (addr != 0 && (rsdp = acpi_get_rsdp(addr)) != NULL) 218 return rsdp; 219 220 return acpi_scan_rsd_ptr(); 221 } 222 223 void * 224 acpi_map_physical(vm_offset_t pa, size_t size) 225 { 226 struct acpi_user_mapping *map; 227 228 map = acpi_user_find_mapping(pa, size); 229 return (map->va + (pa - map->pa)); 230 } 231 232 ACPI_TABLE_HEADER * 233 dsdt_load_file(char *infile) 234 { 235 ACPI_TABLE_HEADER *sdt; 236 uint8_t *dp; 237 struct stat sb; 238 239 if ((acpi_mem_fd = open(infile, O_RDONLY)) == -1) 240 errx(EXIT_FAILURE, "opening %s", infile); 241 242 LIST_INIT(&maplist); 243 244 if (fstat(acpi_mem_fd, &sb) == -1) 245 errx(EXIT_FAILURE, "fstat %s", infile); 246 247 dp = mmap(0, sb.st_size, PROT_READ, MAP_PRIVATE, acpi_mem_fd, 0); 248 if (dp == MAP_FAILED) 249 errx(EXIT_FAILURE, "mmap %s", infile); 250 251 sdt = (ACPI_TABLE_HEADER *)dp; 252 if (strncmp((char *)dp, ACPI_SIG_DSDT, 4) != 0) 253 errx(EXIT_FAILURE, "DSDT signature mismatch"); 254 255 if (sdt->Length > sb.st_size) 256 errx(EXIT_FAILURE, 257 "corrupt DSDT: table size (%"PRIu32" bytes), file size " 258 "(%"PRIu64" bytes)", 259 sdt->Length, sb.st_size); 260 261 if (acpi_checksum(sdt, sdt->Length) != 0) 262 errx(EXIT_FAILURE, "DSDT checksum mismatch"); 263 264 return sdt; 265 } 266