1 /* 2 * Copyright (c) 2009 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Sepherosa Ziehau <sepherosa@gmail.com> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 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 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 3. Neither the name of The DragonFly Project nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific, prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #include <sys/param.h> 36 #include <sys/kernel.h> 37 #include <sys/systm.h> 38 39 #include <machine/pmap.h> 40 41 #include "acpi_sdt.h" 42 #include "acpi_sdt_var.h" 43 44 #define SDT_VPRINTF(fmt, arg...) \ 45 do { \ 46 if (bootverbose) \ 47 kprintf("ACPI SDT: " fmt , ##arg); \ 48 } while (0) 49 50 #define ACPI_RSDP_EBDA_MAPSZ 1024 51 #define ACPI_RSDP_BIOS_MAPSZ 0x20000 52 #define ACPI_RSDP_BIOS_MAPADDR 0xe0000 53 54 #define ACPI_RSDP_ALIGN 16 55 56 #define ACPI_RSDP_SIGLEN 8 57 #define ACPI_RSDP_SIG "RSD PTR " 58 59 /* Root System Description Pointer */ 60 struct acpi_rsdp { 61 uint8_t rsdp_sig[ACPI_RSDP_SIGLEN]; 62 uint8_t rsdp_cksum; 63 uint8_t rsdp_oem_id[6]; 64 uint8_t rsdp_rev; 65 uint32_t rsdp_rsdt; 66 uint32_t rsdp_len; 67 uint64_t rsdp_xsdt; 68 uint8_t rsdp_ext_cksum; 69 uint8_t rsdp_rsvd[3]; 70 } __packed; 71 72 /* Extended System Description Table */ 73 struct acpi_xsdt { 74 struct acpi_sdth xsdt_hdr; 75 uint64_t xsdt_ents[1]; 76 } __packed; 77 78 /* Root System Description Table */ 79 struct acpi_rsdt { 80 struct acpi_sdth rsdt_hdr; 81 uint32_t rsdt_ents[1]; 82 } __packed; 83 84 typedef vm_paddr_t (*sdt_search_t)(vm_paddr_t, const uint8_t *); 85 86 static const struct acpi_rsdp *sdt_rsdp_search(const uint8_t *, int); 87 static vm_paddr_t sdt_search_xsdt(vm_paddr_t, const uint8_t *); 88 static vm_paddr_t sdt_search_rsdt(vm_paddr_t, const uint8_t *); 89 90 extern u_long ebda_addr; 91 92 static sdt_search_t sdt_search_func; 93 static vm_paddr_t sdt_search_paddr; 94 95 static void 96 sdt_probe(void) 97 { 98 const struct acpi_rsdp *rsdp; 99 vm_size_t mapsz; 100 uint8_t *ptr; 101 102 if (ebda_addr != 0) { 103 mapsz = ACPI_RSDP_EBDA_MAPSZ; 104 ptr = pmap_mapdev(ebda_addr, mapsz); 105 106 rsdp = sdt_rsdp_search(ptr, mapsz); 107 if (rsdp == NULL) { 108 SDT_VPRINTF("RSDP not in EBDA\n"); 109 pmap_unmapdev((vm_offset_t)ptr, mapsz); 110 111 ptr = NULL; 112 mapsz = 0; 113 } else { 114 SDT_VPRINTF("RSDP in EBDA\n"); 115 goto found_rsdp; 116 } 117 } 118 119 mapsz = ACPI_RSDP_BIOS_MAPSZ; 120 ptr = pmap_mapdev(ACPI_RSDP_BIOS_MAPADDR, mapsz); 121 122 rsdp = sdt_rsdp_search(ptr, mapsz); 123 if (rsdp == NULL) { 124 kprintf("sdt_probe: no RSDP\n"); 125 pmap_unmapdev((vm_offset_t)ptr, mapsz); 126 return; 127 } else { 128 SDT_VPRINTF("RSDP in BIOS mem\n"); 129 } 130 131 found_rsdp: 132 if (rsdp->rsdp_rev != 2) { 133 sdt_search_func = sdt_search_rsdt; 134 sdt_search_paddr = rsdp->rsdp_rsdt; 135 } else { 136 sdt_search_func = sdt_search_xsdt; 137 sdt_search_paddr = rsdp->rsdp_xsdt; 138 } 139 pmap_unmapdev((vm_offset_t)ptr, mapsz); 140 } 141 SYSINIT(sdt_probe, SI_BOOT2_PRESMP, SI_ORDER_FIRST, sdt_probe, 0); 142 143 static const struct acpi_rsdp * 144 sdt_rsdp_search(const uint8_t *target, int size) 145 { 146 const struct acpi_rsdp *rsdp; 147 int i; 148 149 KKASSERT(size > sizeof(*rsdp)); 150 151 for (i = 0; i < size - sizeof(*rsdp); i += ACPI_RSDP_ALIGN) { 152 rsdp = (const struct acpi_rsdp *)&target[i]; 153 if (memcmp(rsdp->rsdp_sig, ACPI_RSDP_SIG, 154 ACPI_RSDP_SIGLEN) == 0) 155 return rsdp; 156 } 157 return NULL; 158 } 159 160 void * 161 sdt_sdth_map(vm_paddr_t paddr) 162 { 163 struct acpi_sdth *sdth; 164 vm_size_t mapsz; 165 166 sdth = pmap_mapdev(paddr, sizeof(*sdth)); 167 mapsz = sdth->sdth_len; 168 pmap_unmapdev((vm_offset_t)sdth, sizeof(*sdth)); 169 170 if (mapsz < sizeof(*sdth)) 171 return NULL; 172 173 return pmap_mapdev(paddr, mapsz); 174 } 175 176 void 177 sdt_sdth_unmap(struct acpi_sdth *sdth) 178 { 179 pmap_unmapdev((vm_offset_t)sdth, sdth->sdth_len); 180 } 181 182 static vm_paddr_t 183 sdt_search_xsdt(vm_paddr_t xsdt_paddr, const uint8_t *sig) 184 { 185 struct acpi_xsdt *xsdt; 186 vm_paddr_t sdt_paddr = 0; 187 int i, nent; 188 189 if (xsdt_paddr == 0) { 190 kprintf("sdt_search_xsdt: XSDT paddr == 0\n"); 191 return 0; 192 } 193 194 xsdt = sdt_sdth_map(xsdt_paddr); 195 if (xsdt == NULL) { 196 kprintf("sdt_search_xsdt: can't map XSDT\n"); 197 return 0; 198 } 199 200 if (memcmp(xsdt->xsdt_hdr.sdth_sig, ACPI_XSDT_SIG, 201 ACPI_SDTH_SIGLEN) != 0) { 202 kprintf("sdt_search_xsdt: not XSDT\n"); 203 goto back; 204 } 205 206 if (xsdt->xsdt_hdr.sdth_rev != 1) { 207 kprintf("sdt_search_xsdt: unknown XSDT revision %d\n", 208 xsdt->xsdt_hdr.sdth_rev); 209 } 210 211 if (xsdt->xsdt_hdr.sdth_len < sizeof(xsdt->xsdt_hdr)) { 212 kprintf("sdt_search_xsdt: invalid XSDT length %u\n", 213 xsdt->xsdt_hdr.sdth_len); 214 goto back; 215 } 216 217 nent = (xsdt->xsdt_hdr.sdth_len - sizeof(xsdt->xsdt_hdr)) / 218 sizeof(xsdt->xsdt_ents[0]); 219 for (i = 0; i < nent; ++i) { 220 struct acpi_sdth *sdth; 221 222 if (xsdt->xsdt_ents[i] == 0) 223 continue; 224 225 sdth = sdt_sdth_map(xsdt->xsdt_ents[i]); 226 if (sdth != NULL) { 227 int ret; 228 229 ret = memcmp(sdth->sdth_sig, sig, ACPI_SDTH_SIGLEN); 230 sdt_sdth_unmap(sdth); 231 232 if (ret == 0) { 233 sdt_paddr = xsdt->xsdt_ents[i]; 234 break; 235 } 236 } 237 } 238 back: 239 sdt_sdth_unmap(&xsdt->xsdt_hdr); 240 return sdt_paddr; 241 } 242 243 static vm_paddr_t 244 sdt_search_rsdt(vm_paddr_t rsdt_paddr, const uint8_t *sig) 245 { 246 struct acpi_rsdt *rsdt; 247 vm_paddr_t sdt_paddr = 0; 248 int i, nent; 249 250 if (rsdt_paddr == 0) { 251 kprintf("sdt_search_rsdt: RSDT paddr == 0\n"); 252 return 0; 253 } 254 255 rsdt = sdt_sdth_map(rsdt_paddr); 256 if (rsdt == NULL) { 257 kprintf("sdt_search_rsdt: can't map RSDT\n"); 258 return 0; 259 } 260 261 if (memcmp(rsdt->rsdt_hdr.sdth_sig, ACPI_RSDT_SIG, 262 ACPI_SDTH_SIGLEN) != 0) { 263 kprintf("sdt_search_rsdt: not RSDT\n"); 264 goto back; 265 } 266 267 if (rsdt->rsdt_hdr.sdth_rev != 1) { 268 kprintf("sdt_search_rsdt: unknown RSDT revision %d\n", 269 rsdt->rsdt_hdr.sdth_rev); 270 } 271 272 if (rsdt->rsdt_hdr.sdth_len < sizeof(rsdt->rsdt_hdr)) { 273 kprintf("sdt_search_rsdt: invalid RSDT length %u\n", 274 rsdt->rsdt_hdr.sdth_len); 275 goto back; 276 } 277 278 nent = (rsdt->rsdt_hdr.sdth_len - sizeof(rsdt->rsdt_hdr)) / 279 sizeof(rsdt->rsdt_ents[0]); 280 for (i = 0; i < nent; ++i) { 281 struct acpi_sdth *sdth; 282 283 if (rsdt->rsdt_ents[i] == 0) 284 continue; 285 286 sdth = sdt_sdth_map(rsdt->rsdt_ents[i]); 287 if (sdth != NULL) { 288 int ret; 289 290 ret = memcmp(sdth->sdth_sig, sig, ACPI_SDTH_SIGLEN); 291 sdt_sdth_unmap(sdth); 292 293 if (ret == 0) { 294 sdt_paddr = rsdt->rsdt_ents[i]; 295 break; 296 } 297 } 298 } 299 back: 300 sdt_sdth_unmap(&rsdt->rsdt_hdr); 301 return sdt_paddr; 302 } 303 304 vm_paddr_t 305 sdt_search(const uint8_t *sig) 306 { 307 if (sdt_search_func == NULL) 308 return 0; 309 return sdt_search_func(sdt_search_paddr, sig); 310 } 311