1 /* $NetBSD: acpi_machdep.c,v 1.12 2016/01/28 23:50:04 htodd Exp $ */ 2 3 /* 4 * Copyright 2001 Wasabi Systems, Inc. 5 * All rights reserved. 6 * 7 * Written by Jason R. Thorpe for Wasabi Systems, Inc. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed for the NetBSD Project by 20 * Wasabi Systems, Inc. 21 * 4. The name of Wasabi Systems, Inc. may not be used to endorse 22 * or promote products derived from this software without specific prior 23 * written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 27 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 28 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC 29 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35 * POSSIBILITY OF SUCH DAMAGE. 36 */ 37 38 /* 39 * Machine-dependent routines for ACPICA. 40 */ 41 42 #include <sys/cdefs.h> 43 __KERNEL_RCSID(0, "$NetBSD: acpi_machdep.c,v 1.12 2016/01/28 23:50:04 htodd Exp $"); 44 45 #include <sys/param.h> 46 #include <sys/systm.h> 47 #include <sys/bus.h> 48 #include <sys/cpu.h> 49 #include <sys/device.h> 50 51 #include <uvm/uvm_extern.h> 52 53 #include <machine/cpufunc.h> 54 #include <machine/bootinfo.h> 55 56 #include <dev/acpi/acpica.h> 57 #include <dev/acpi/acpivar.h> 58 #include <dev/acpi/acpi_mcfg.h> 59 60 #include <machine/acpi_machdep.h> 61 #include <machine/mpbiosvar.h> 62 #include <machine/mpacpi.h> 63 #include <machine/i82093reg.h> 64 #include <machine/i82093var.h> 65 #include <machine/pic.h> 66 67 #include <x86/efi.h> 68 69 #include <dev/pci/pcivar.h> 70 71 #include <dev/isa/isareg.h> 72 #include <dev/isa/isavar.h> 73 74 #include "ioapic.h" 75 76 #include "acpica.h" 77 #include "opt_mpbios.h" 78 #include "opt_acpi.h" 79 80 ACPI_STATUS 81 acpi_md_OsInitialize(void) 82 { 83 return AE_OK; 84 } 85 86 ACPI_PHYSICAL_ADDRESS 87 acpi_md_OsGetRootPointer(void) 88 { 89 ACPI_PHYSICAL_ADDRESS PhysicalAddress; 90 ACPI_STATUS Status; 91 92 #ifndef XEN 93 /* If EFI is available, attempt to use it to locate the ACPI table. */ 94 if (efi_probe()) { 95 PhysicalAddress = efi_getcfgtblpa(&EFI_UUID_ACPI20); 96 if (!PhysicalAddress) 97 PhysicalAddress = efi_getcfgtblpa(&EFI_UUID_ACPI10); 98 if (PhysicalAddress) 99 return PhysicalAddress; 100 } 101 102 #endif 103 Status = AcpiFindRootPointer(&PhysicalAddress); 104 if (ACPI_FAILURE(Status)) 105 PhysicalAddress = 0; 106 107 return PhysicalAddress; 108 } 109 110 struct acpi_md_override { 111 int irq; 112 int pin; 113 int flags; 114 }; 115 116 #if NIOAPIC > 0 117 static ACPI_STATUS 118 acpi_md_findoverride(ACPI_SUBTABLE_HEADER *hdrp, void *aux) 119 { 120 ACPI_MADT_INTERRUPT_OVERRIDE *iop; 121 struct acpi_md_override *ovrp; 122 123 if (hdrp->Type != ACPI_MADT_TYPE_INTERRUPT_OVERRIDE) { 124 return AE_OK; 125 } 126 127 iop = (void *)hdrp; 128 ovrp = aux; 129 if (iop->SourceIrq == ovrp->irq) { 130 ovrp->pin = iop->GlobalIrq; 131 ovrp->flags = iop->IntiFlags; 132 } 133 return AE_OK; 134 } 135 #endif 136 137 ACPI_STATUS 138 acpi_md_OsInstallInterruptHandler(uint32_t InterruptNumber, 139 ACPI_OSD_HANDLER ServiceRoutine, void *Context, void **cookiep) 140 { 141 void *ih; 142 struct pic *pic; 143 #if NIOAPIC > 0 144 struct ioapic_softc *sc; 145 struct acpi_md_override ovr; 146 struct mp_intr_map tmpmap, *mip, **mipp = NULL; 147 #endif 148 int irq, pin, type, redir, mpflags; 149 150 /* 151 * ACPI interrupts default to level-triggered active-low. 152 */ 153 154 type = IST_LEVEL; 155 mpflags = (MPS_INTTR_LEVEL << 2) | MPS_INTPO_ACTLO; 156 redir = IOAPIC_REDLO_LEVEL | IOAPIC_REDLO_ACTLO; 157 158 #if NIOAPIC > 0 159 160 /* 161 * Apply any MADT override setting. 162 */ 163 164 ovr.irq = InterruptNumber; 165 ovr.pin = -1; 166 if (acpi_madt_map() == AE_OK) { 167 acpi_madt_walk(acpi_md_findoverride, &ovr); 168 acpi_madt_unmap(); 169 } else { 170 aprint_debug("acpi_madt_map() failed, can't check for MADT override\n"); 171 } 172 173 if (ovr.pin != -1) { 174 bool sci = InterruptNumber == AcpiGbl_FADT.SciInterrupt; 175 int polarity = ovr.flags & ACPI_MADT_POLARITY_MASK; 176 int trigger = ovr.flags & ACPI_MADT_TRIGGER_MASK; 177 178 InterruptNumber = ovr.pin; 179 if (polarity == ACPI_MADT_POLARITY_ACTIVE_HIGH || 180 (!sci && polarity == ACPI_MADT_POLARITY_CONFORMS)) { 181 mpflags &= ~MPS_INTPO_ACTLO; 182 mpflags |= MPS_INTPO_ACTHI; 183 redir &= ~IOAPIC_REDLO_ACTLO; 184 } 185 if (trigger == ACPI_MADT_TRIGGER_EDGE || 186 (!sci && trigger == ACPI_MADT_TRIGGER_CONFORMS)) { 187 type = IST_EDGE; 188 mpflags &= ~(MPS_INTTR_LEVEL << 2); 189 mpflags |= (MPS_INTTR_EDGE << 2); 190 redir &= ~IOAPIC_REDLO_LEVEL; 191 } 192 } 193 194 /* 195 * If the interrupt is handled via IOAPIC, update the map. 196 * If the map isn't set up yet, install a temporary one. 197 */ 198 199 sc = ioapic_find_bybase(InterruptNumber); 200 if (sc != NULL) { 201 pic = &sc->sc_pic; 202 203 if (pic->pic_type == PIC_IOAPIC) { 204 pin = (int)InterruptNumber - pic->pic_vecbase; 205 irq = -1; 206 } else { 207 irq = pin = (int)InterruptNumber; 208 } 209 210 mip = sc->sc_pins[pin].ip_map; 211 if (mip) { 212 mip->flags &= ~0xf; 213 mip->flags |= mpflags; 214 mip->redir &= ~(IOAPIC_REDLO_LEVEL | 215 IOAPIC_REDLO_ACTLO); 216 mip->redir |= redir; 217 } else { 218 mipp = &sc->sc_pins[pin].ip_map; 219 *mipp = &tmpmap; 220 tmpmap.redir = redir; 221 tmpmap.flags = mpflags; 222 } 223 } else 224 #endif 225 { 226 pic = &i8259_pic; 227 irq = pin = (int)InterruptNumber; 228 } 229 230 /* 231 * XXX probably, IPL_BIO is enough. 232 */ 233 ih = intr_establish(irq, pic, pin, type, IPL_TTY, 234 (int (*)(void *)) ServiceRoutine, Context, false); 235 236 #if NIOAPIC > 0 237 if (mipp) { 238 *mipp = NULL; 239 } 240 #endif 241 242 if (ih == NULL) 243 return AE_NO_MEMORY; 244 245 *cookiep = ih; 246 247 return AE_OK; 248 } 249 250 void 251 acpi_md_OsRemoveInterruptHandler(void *cookie) 252 { 253 intr_disestablish(cookie); 254 } 255 256 ACPI_STATUS 257 acpi_md_OsMapMemory(ACPI_PHYSICAL_ADDRESS PhysicalAddress, 258 uint32_t Length, void **LogicalAddress) 259 { 260 int rv; 261 262 rv = _x86_memio_map(x86_bus_space_mem, PhysicalAddress, 263 Length, 0, (bus_space_handle_t *)LogicalAddress); 264 265 return (rv != 0) ? AE_NO_MEMORY : AE_OK; 266 } 267 268 void 269 acpi_md_OsUnmapMemory(void *LogicalAddress, uint32_t Length) 270 { 271 (void) _x86_memio_unmap(x86_bus_space_mem, 272 (bus_space_handle_t)LogicalAddress, Length, NULL); 273 } 274 275 ACPI_STATUS 276 acpi_md_OsGetPhysicalAddress(void *LogicalAddress, 277 ACPI_PHYSICAL_ADDRESS *PhysicalAddress) 278 { 279 paddr_t pa; 280 281 if (pmap_extract(pmap_kernel(), (vaddr_t) LogicalAddress, &pa)) { 282 *PhysicalAddress = pa; 283 return AE_OK; 284 } 285 286 return AE_ERROR; 287 } 288 289 BOOLEAN 290 acpi_md_OsReadable(void *Pointer, uint32_t Length) 291 { 292 BOOLEAN rv = TRUE; 293 vaddr_t sva, eva; 294 pt_entry_t *pte; 295 296 sva = trunc_page((vaddr_t) Pointer); 297 eva = round_page((vaddr_t) Pointer + Length); 298 299 if (sva < VM_MIN_KERNEL_ADDRESS) 300 return FALSE; 301 302 for (; sva < eva; sva += PAGE_SIZE) { 303 pte = kvtopte(sva); 304 if ((*pte & PG_V) == 0) { 305 rv = FALSE; 306 break; 307 } 308 } 309 310 return rv; 311 } 312 313 BOOLEAN 314 acpi_md_OsWritable(void *Pointer, uint32_t Length) 315 { 316 BOOLEAN rv = TRUE; 317 vaddr_t sva, eva; 318 pt_entry_t *pte; 319 320 sva = trunc_page((vaddr_t) Pointer); 321 eva = round_page((vaddr_t) Pointer + Length); 322 323 if (sva < VM_MIN_KERNEL_ADDRESS) 324 return FALSE; 325 326 for (; sva < eva; sva += PAGE_SIZE) { 327 pte = kvtopte(sva); 328 if ((*pte & (PG_V|PG_W)) != (PG_V|PG_W)) { 329 rv = FALSE; 330 break; 331 } 332 } 333 334 return rv; 335 } 336 337 void 338 acpi_md_OsDisableInterrupt(void) 339 { 340 x86_disable_intr(); 341 } 342 343 void 344 acpi_md_OsEnableInterrupt(void) 345 { 346 x86_enable_intr(); 347 } 348 349 uint32_t 350 acpi_md_ncpus(void) 351 { 352 return kcpuset_countset(kcpuset_attached); 353 } 354 355 static bool 356 acpi_md_mcfg_validate(uint64_t addr, int bus_start, int *bus_end) 357 { 358 struct btinfo_memmap *bim; 359 uint64_t size, mapaddr, mapsize; 360 uint32_t type; 361 int i, n; 362 363 bim = lookup_bootinfo(BTINFO_MEMMAP); 364 if (bim == NULL) 365 return false; 366 367 size = *bus_end - bus_start + 1; 368 size *= ACPIMCFG_SIZE_PER_BUS; 369 for (i = 0; i < bim->num; i++) { 370 mapaddr = bim->entry[i].addr; 371 mapsize = bim->entry[i].size; 372 type = bim->entry[i].type; 373 374 aprint_debug("MCFG: MEMMAP: 0x%016" PRIx64 "-0x%016" PRIx64 375 ", size=0x%016" PRIx64 ", type=%d(%s)\n", 376 mapaddr, mapaddr + mapsize - 1, mapsize, type, 377 (type == BIM_Memory) ? "Memory" : 378 (type == BIM_Reserved) ? "Reserved" : 379 (type == BIM_ACPI) ? "ACPI" : 380 (type == BIM_NVS) ? "NVS" : 381 "unknown"); 382 383 switch (type) { 384 case BIM_ACPI: 385 case BIM_Reserved: 386 if (addr < mapaddr || addr >= mapaddr + mapsize) 387 break; 388 389 /* full map */ 390 if (addr + size <= mapaddr + mapsize) 391 return true; 392 393 /* partial map */ 394 n = (mapsize - (addr - mapaddr)) / 395 ACPIMCFG_SIZE_PER_BUS; 396 /* bus_start == bus_end is not allowed. */ 397 if (n > 1) { 398 *bus_end = bus_start + n - 1; 399 return true; 400 } 401 aprint_debug("MCFG: bus %d-%d, address 0x%016" PRIx64 402 ": invalid size: request 0x%016" PRIx64 ", " 403 "actual 0x%016" PRIx64 "\n", 404 bus_start, *bus_end, addr, size, mapsize); 405 break; 406 } 407 } 408 aprint_debug("MCFG: bus %d-%d, address 0x%016" PRIx64 ": " 409 "no valid region\n", bus_start, *bus_end, addr); 410 return false; 411 } 412 413 static uint32_t 414 acpi_md_mcfg_read(bus_space_tag_t bst, bus_space_handle_t bsh, bus_addr_t addr) 415 { 416 vaddr_t va = bsh + addr; 417 uint32_t data = (uint32_t) -1; 418 419 KASSERT(bst == x86_bus_space_mem); 420 421 __asm("movl %1, %0" : "=a" (data) : "m" (*(volatile uint32_t *)va)); 422 423 return data; 424 } 425 426 static void 427 acpi_md_mcfg_write(bus_space_tag_t bst, bus_space_handle_t bsh, bus_addr_t addr, 428 uint32_t data) 429 { 430 vaddr_t va = bsh + addr; 431 432 KASSERT(bst == x86_bus_space_mem); 433 434 __asm("movl %1, %0" : "=m" (*(volatile uint32_t *)va) : "a" (data)); 435 } 436 437 static const struct acpimcfg_ops acpi_md_mcfg_ops = { 438 .ao_validate = acpi_md_mcfg_validate, 439 440 .ao_read = acpi_md_mcfg_read, 441 .ao_write = acpi_md_mcfg_write, 442 }; 443 444 void 445 acpi_md_callback(struct acpi_softc *sc) 446 { 447 #ifdef MPBIOS 448 if (!mpbios_scanned) 449 #endif 450 mpacpi_find_interrupts(sc); 451 452 #ifndef XEN 453 acpi_md_sleep_init(); 454 #endif 455 456 acpimcfg_init(x86_bus_space_mem, &acpi_md_mcfg_ops); 457 } 458