1 /* $NetBSD: acpi_user.c,v 1.5 2020/08/20 15:54:12 riastradh 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.5 2020/08/20 15:54:12 riastradh 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 <fcntl.h> 43 #include <stdlib.h> 44 #include <string.h> 45 #include <unistd.h> 46 47 #include "acpidump.h" 48 49 static char machdep_acpi_root[] = "hw.acpi.root"; 50 static int acpi_mem_fd = -1; 51 52 struct acpi_user_mapping { 53 LIST_ENTRY(acpi_user_mapping) link; 54 vm_offset_t pa; 55 caddr_t va; 56 size_t size; 57 }; 58 59 static LIST_HEAD(acpi_user_mapping_list, acpi_user_mapping) maplist; 60 61 static void 62 acpi_user_init(void) 63 { 64 65 if (acpi_mem_fd == -1) { 66 acpi_mem_fd = open("/dev/mem", O_RDONLY); 67 if (acpi_mem_fd == -1) 68 err(EXIT_FAILURE, "opening /dev/mem"); 69 LIST_INIT(&maplist); 70 } 71 } 72 73 static struct acpi_user_mapping * 74 acpi_user_find_mapping(vm_offset_t pa, size_t size) 75 { 76 struct acpi_user_mapping *map; 77 78 /* First search for an existing mapping */ 79 for (map = LIST_FIRST(&maplist); map; map = LIST_NEXT(map, link)) { 80 if (map->pa <= pa && map->size >= pa + size - map->pa) 81 return map; 82 } 83 84 /* Then create a new one */ 85 size = round_page(pa + size) - trunc_page(pa); 86 pa = trunc_page(pa); 87 map = malloc(sizeof(struct acpi_user_mapping)); 88 if (!map) 89 errx(EXIT_FAILURE, "out of memory"); 90 map->pa = pa; 91 map->va = mmap(0, size, PROT_READ, MAP_SHARED, acpi_mem_fd, pa); 92 map->size = size; 93 if ((intptr_t) map->va == -1) 94 err(EXIT_FAILURE, "can't map address"); 95 LIST_INSERT_HEAD(&maplist, map, link); 96 97 return map; 98 } 99 100 static ACPI_TABLE_RSDP * 101 acpi_get_rsdp(u_long addr) 102 { 103 ACPI_TABLE_RSDP rsdp; 104 size_t len; 105 106 /* Read in the table signature and check it. */ 107 pread(acpi_mem_fd, &rsdp, 8, addr); 108 if (memcmp(rsdp.Signature, "RSD PTR ", 8)) 109 return (NULL); 110 111 /* Read the entire table. */ 112 pread(acpi_mem_fd, &rsdp, sizeof(rsdp), addr); 113 114 /* Check the standard checksum. */ 115 if (acpi_checksum(&rsdp, ACPI_RSDP_CHECKSUM_LENGTH) != 0) 116 return (NULL); 117 118 /* Check extended checksum if table version >= 2. */ 119 if (rsdp.Revision >= 2 && 120 acpi_checksum(&rsdp, ACPI_RSDP_XCHECKSUM_LENGTH) != 0) 121 return (NULL); 122 123 /* If the revision is 0, assume a version 1 length. */ 124 if (rsdp.Revision == 0) 125 len = sizeof(ACPI_RSDP_COMMON); 126 else 127 len = rsdp.Length; 128 129 return (acpi_map_physical(addr, len)); 130 } 131 132 static ACPI_TABLE_RSDP * 133 acpi_scan_rsd_ptr(void) 134 { 135 #if defined(__amd64__) || defined(__i386__) 136 ACPI_TABLE_RSDP *rsdp; 137 u_long addr, end; 138 139 /* 140 * On ia32, scan physical memory for the RSD PTR if above failed. 141 * According to section 5.2.2 of the ACPI spec, we only consider 142 * two regions for the base address: 143 * 1. EBDA (1 KB area addressed by the 16 bit pointer at 0x40E 144 * 2. High memory (0xE0000 - 0xFFFFF) 145 */ 146 addr = ACPI_EBDA_PTR_LOCATION; 147 pread(acpi_mem_fd, &addr, sizeof(uint16_t), addr); 148 addr <<= 4; 149 end = addr + ACPI_EBDA_WINDOW_SIZE; 150 for (; addr < end; addr += 16) 151 if ((rsdp = acpi_get_rsdp(addr)) != NULL) 152 return rsdp; 153 addr = ACPI_HI_RSDP_WINDOW_BASE; 154 end = addr + ACPI_HI_RSDP_WINDOW_SIZE; 155 for (; addr < end; addr += 16) 156 if ((rsdp = acpi_get_rsdp(addr)) != NULL) 157 return rsdp; 158 #endif /* __amd64__ || __i386__ */ 159 return NULL; 160 } 161 162 /* 163 * Public interfaces 164 */ 165 ACPI_TABLE_RSDP * 166 acpi_find_rsd_ptr(void) 167 { 168 ACPI_TABLE_RSDP *rsdp; 169 u_long addr = 0; 170 size_t len = sizeof(addr); 171 172 acpi_user_init(); 173 174 if (sysctlbyname(machdep_acpi_root, &addr, &len, NULL, 0) != 0) 175 addr = 0; 176 if (addr != 0 && (rsdp = acpi_get_rsdp(addr)) != NULL) 177 return rsdp; 178 179 return acpi_scan_rsd_ptr(); 180 } 181 182 void * 183 acpi_map_physical(vm_offset_t pa, size_t size) 184 { 185 struct acpi_user_mapping *map; 186 187 map = acpi_user_find_mapping(pa, size); 188 return (map->va + (pa - map->pa)); 189 } 190 191 ACPI_TABLE_HEADER * 192 dsdt_load_file(char *infile) 193 { 194 ACPI_TABLE_HEADER *sdt; 195 uint8_t *dp; 196 struct stat sb; 197 198 if ((acpi_mem_fd = open(infile, O_RDONLY)) == -1) 199 errx(EXIT_FAILURE, "opening %s", infile); 200 201 LIST_INIT(&maplist); 202 203 if (fstat(acpi_mem_fd, &sb) == -1) 204 errx(EXIT_FAILURE, "fstat %s", infile); 205 206 dp = mmap(0, sb.st_size, PROT_READ, MAP_PRIVATE, acpi_mem_fd, 0); 207 if (dp == MAP_FAILED) 208 errx(EXIT_FAILURE, "mmap %s", infile); 209 210 sdt = (ACPI_TABLE_HEADER *)dp; 211 if (strncmp((char *)dp, ACPI_SIG_DSDT, 4) != 0) 212 errx(EXIT_FAILURE, "DSDT signature mismatch"); 213 214 if (sdt->Length > sb.st_size) 215 errx(EXIT_FAILURE, 216 "corrupt DSDT: table size (%"PRIu32" bytes), file size " 217 "(%"PRIu64" bytes)", 218 sdt->Length, sb.st_size); 219 220 if (acpi_checksum(sdt, sdt->Length) != 0) 221 errx(EXIT_FAILURE, "DSDT checksum mismatch"); 222 223 return sdt; 224 } 225