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