1 /* $NetBSD: acpi_user.c,v 1.2 2009/12/22 08:44:03 cegger 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: src/usr.sbin/acpi/acpidump/acpi_user.c,v 1.15 2009/08/25 20:35:57 jhb Exp $ 30 */ 31 32 #include <sys/cdefs.h> 33 __RCSID("$NetBSD: acpi_user.c,v 1.2 2009/12/22 08:44:03 cegger 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 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 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(EXIT_FAILURE, "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(EXIT_FAILURE, "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(EXIT_FAILURE, "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 = ACPI_RSDP_REV0_SIZE; 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 int i; 168 uint8_t buf[sizeof(ACPI_TABLE_RSDP)]; 169 170 acpi_user_init(); 171 for (i = 0; i < 1024 * 1024; i += 16) { 172 read(acpi_mem_fd, buf, 16); 173 if (!memcmp(buf, "RSD PTR ", 8)) { 174 /* Read the rest of the structure */ 175 read(acpi_mem_fd, buf + 16, sizeof(ACPI_TABLE_RSDP) - 16); 176 177 /* Verify checksum before accepting it. */ 178 if (acpi_checksum(buf, sizeof(ACPI_TABLE_RSDP))) 179 continue; 180 return (acpi_map_physical(i, sizeof(ACPI_TABLE_RSDP))); 181 } 182 } 183 184 return acpi_scan_rsd_ptr(); 185 } 186 187 void * 188 acpi_map_physical(vm_offset_t pa, size_t size) 189 { 190 struct acpi_user_mapping *map; 191 192 map = acpi_user_find_mapping(pa, size); 193 return (map->va + (pa - map->pa)); 194 } 195 196 ACPI_TABLE_HEADER * 197 dsdt_load_file(char *infile) 198 { 199 ACPI_TABLE_HEADER *sdt; 200 uint8_t *dp; 201 struct stat sb; 202 203 if ((acpi_mem_fd = open(infile, O_RDONLY)) == -1) 204 errx(EXIT_FAILURE, "opening %s", infile); 205 206 LIST_INIT(&maplist); 207 208 if (fstat(acpi_mem_fd, &sb) == -1) 209 errx(EXIT_FAILURE, "fstat %s", infile); 210 211 dp = mmap(0, sb.st_size, PROT_READ, MAP_PRIVATE, acpi_mem_fd, 0); 212 if (dp == NULL) 213 errx(EXIT_FAILURE, "mmap %s", infile); 214 215 sdt = (ACPI_TABLE_HEADER *)dp; 216 if (strncmp((char *)dp, ACPI_SIG_DSDT, 4) != 0) 217 errx(EXIT_FAILURE, "DSDT signature mismatch"); 218 219 if (sdt->Length > sb.st_size) 220 errx(EXIT_FAILURE, 221 "corrupt DSDT: table size (%"PRIu32" bytes), file size " 222 "(%"PRIu64" bytes)", 223 sdt->Length, sb.st_size); 224 225 if (acpi_checksum(sdt, sdt->Length) != 0) 226 errx(EXIT_FAILURE, "DSDT checksum mismatch"); 227 228 return sdt; 229 } 230