1 /* $OpenBSD: acpimadt.c,v 1.37 2018/06/29 17:39:18 kettenis Exp $ */ 2 /* 3 * Copyright (c) 2006 Mark Kettenis <kettenis@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/param.h> 19 #include <sys/systm.h> 20 #include <sys/device.h> 21 #include <sys/malloc.h> 22 23 #include <machine/apicvar.h> 24 #include <machine/cpuvar.h> 25 #include <machine/bus.h> 26 27 #include <dev/acpi/acpireg.h> 28 #include <dev/acpi/acpivar.h> 29 #include <dev/acpi/acpidev.h> 30 #include <dev/acpi/amltypes.h> 31 #include <dev/acpi/dsdt.h> 32 33 #include <machine/i8259.h> 34 #include <machine/i82093reg.h> 35 #include <machine/i82093var.h> 36 #include <machine/i82489reg.h> 37 #include <machine/i82489var.h> 38 39 #include <machine/mpbiosvar.h> 40 41 #include "ioapic.h" 42 43 int acpimadt_match(struct device *, void *, void *); 44 void acpimadt_attach(struct device *, struct device *, void *); 45 46 struct cfattach acpimadt_ca = { 47 sizeof(struct device), acpimadt_match, acpimadt_attach 48 }; 49 50 struct cfdriver acpimadt_cd = { 51 NULL, "acpimadt", DV_DULL 52 }; 53 54 int acpimadt_validate(struct acpi_madt *); 55 int acpimadt_cfg_intr(int, uint32_t *); 56 int acpimadt_print(void *, const char *); 57 58 int 59 acpimadt_match(struct device *parent, void *match, void *aux) 60 { 61 struct acpi_attach_args *aaa = aux; 62 struct acpi_table_header *hdr; 63 64 /* 65 * If we do not have a table, it is not us 66 */ 67 if (aaa->aaa_table == NULL) 68 return (0); 69 70 /* 71 * If it is an MADT table, we can attach 72 */ 73 hdr = (struct acpi_table_header *)aaa->aaa_table; 74 if (memcmp(hdr->signature, MADT_SIG, sizeof(MADT_SIG) - 1) != 0) 75 return (0); 76 77 return (1); 78 } 79 80 int 81 acpimadt_validate(struct acpi_madt *madt) 82 { 83 caddr_t addr = (caddr_t)(madt + 1); 84 85 while (addr < (caddr_t)madt + madt->hdr.length) { 86 union acpi_madt_entry *entry = (union acpi_madt_entry *)addr; 87 uint8_t length = entry->madt_lapic.length; 88 89 if (length < 2) 90 return (0); 91 92 if (addr + length > (caddr_t)madt + madt->hdr.length) 93 return (0); 94 95 switch (entry->madt_lapic.apic_type) { 96 case ACPI_MADT_LAPIC: 97 if (length != sizeof(entry->madt_lapic)) 98 return (0); 99 break; 100 case ACPI_MADT_IOAPIC: 101 if (length != sizeof(entry->madt_ioapic)) 102 return (0); 103 break; 104 case ACPI_MADT_OVERRIDE: 105 if (length != sizeof(entry->madt_override)) 106 return (0); 107 break; 108 case ACPI_MADT_NMI: 109 if (length != sizeof(entry->madt_nmi)) 110 return (0); 111 break; 112 case ACPI_MADT_LAPIC_NMI: 113 if (length != sizeof(entry->madt_lapic_nmi)) 114 return (0); 115 break; 116 case ACPI_MADT_LAPIC_OVERRIDE: 117 if (length != sizeof(entry->madt_lapic_override)) 118 return (0); 119 break; 120 case ACPI_MADT_IO_SAPIC: 121 if (length != sizeof(entry->madt_io_sapic)) 122 return (0); 123 break; 124 case ACPI_MADT_LOCAL_SAPIC: 125 if (length != sizeof(entry->madt_local_sapic)) 126 return (0); 127 break; 128 case ACPI_MADT_PLATFORM_INT: 129 if (length != sizeof(entry->madt_platform_int)) 130 return (0); 131 break; 132 case ACPI_MADT_X2APIC: 133 if (length != sizeof(entry->madt_x2apic)) 134 return (0); 135 break; 136 case ACPI_MADT_X2APIC_NMI: 137 if (length != sizeof(entry->madt_x2apic_nmi)) 138 return (0); 139 break; 140 } 141 142 addr += length; 143 } 144 145 return (1); 146 } 147 148 struct mp_bus acpimadt_busses[256]; 149 struct mp_bus acpimadt_isa_bus; 150 151 int 152 acpimadt_cfg_intr(int flags, uint32_t *redir) 153 { 154 int mpspo = (flags >> MPS_INTPO_SHIFT) & MPS_INTPO_MASK; 155 int mpstrig = (flags >> MPS_INTTR_SHIFT) & MPS_INTTR_MASK; 156 157 *redir &= ~IOAPIC_REDLO_DEL_MASK; 158 switch (mpspo) { 159 case MPS_INTPO_DEF: 160 case MPS_INTPO_ACTHI: 161 *redir &= ~IOAPIC_REDLO_ACTLO; 162 break; 163 case MPS_INTPO_ACTLO: 164 *redir |= IOAPIC_REDLO_ACTLO; 165 break; 166 default: 167 return (0); 168 } 169 170 *redir |= (IOAPIC_REDLO_DEL_LOPRI << IOAPIC_REDLO_DEL_SHIFT); 171 172 switch (mpstrig) { 173 case MPS_INTTR_LEVEL: 174 *redir |= IOAPIC_REDLO_LEVEL; 175 break; 176 case MPS_INTTR_DEF: 177 case MPS_INTTR_EDGE: 178 *redir &= ~IOAPIC_REDLO_LEVEL; 179 break; 180 default: 181 return (0); 182 } 183 184 return (1); 185 } 186 187 static uint8_t lapic_map[256]; 188 189 void 190 acpimadt_attach(struct device *parent, struct device *self, void *aux) 191 { 192 struct acpi_softc *acpi_sc = (struct acpi_softc *)parent; 193 struct device *mainbus = parent->dv_parent->dv_parent; 194 struct acpi_attach_args *aaa = aux; 195 struct acpi_madt *madt = (struct acpi_madt *)aaa->aaa_table; 196 caddr_t addr = (caddr_t)(madt + 1); 197 struct aml_value arg; 198 struct mp_intr_map *map; 199 struct ioapic_softc *apic; 200 int nlapic_nmis = 0; 201 int pin; 202 203 /* Do some sanity checks before committing to run in APIC mode. */ 204 if (!acpimadt_validate(madt)) { 205 printf(": invalid, skipping\n"); 206 return; 207 } 208 209 printf(" addr 0x%x", madt->local_apic_address); 210 if (madt->flags & ACPI_APIC_PCAT_COMPAT) 211 printf(": PC-AT compat"); 212 printf("\n"); 213 214 /* Tell the BIOS we will be using APIC mode. */ 215 memset(&arg, 0, sizeof(arg)); 216 arg.type = AML_OBJTYPE_INTEGER; 217 arg.v_integer = 1; 218 219 aml_evalname(acpi_sc, NULL, "\\_PIC", 1, &arg, NULL); 220 221 mp_busses = acpimadt_busses; 222 mp_nbusses = nitems(acpimadt_busses); 223 mp_isa_bus = &acpimadt_isa_bus; 224 225 lapic_boot_init(madt->local_apic_address); 226 227 /* 1st pass, get CPUs and IOAPICs */ 228 while (addr < (caddr_t)madt + madt->hdr.length) { 229 union acpi_madt_entry *entry = (union acpi_madt_entry *)addr; 230 struct cpu_attach_args caa; 231 struct apic_attach_args aaa; 232 233 switch (entry->madt_lapic.apic_type) { 234 case ACPI_MADT_LAPIC_OVERRIDE: 235 if (entry->madt_lapic_override.lapic_address != 236 madt->local_apic_address) { 237 printf("%s: ignored LAPIC override 0x%llx\n", 238 self->dv_xname, 239 entry->madt_lapic_override.lapic_address); 240 } 241 break; 242 case ACPI_MADT_LAPIC: 243 dprintf("%s: LAPIC: acpi_proc_id %x, apic_id %x, flags 0x%x\n", 244 self->dv_xname, entry->madt_lapic.acpi_proc_id, 245 entry->madt_lapic.apic_id, 246 entry->madt_lapic.flags); 247 248 if ((entry->madt_lapic.flags & ACPI_PROC_ENABLE) == 0) 249 break; 250 251 lapic_map[entry->madt_lapic.acpi_proc_id] = 252 entry->madt_lapic.apic_id; 253 254 memset(&caa, 0, sizeof(struct cpu_attach_args)); 255 if (lapic_cpu_number() == entry->madt_lapic.apic_id) 256 caa.cpu_role = CPU_ROLE_BP; 257 else { 258 caa.cpu_role = CPU_ROLE_AP; 259 ncpusfound++; 260 } 261 caa.caa_name = "cpu"; 262 caa.cpu_apicid = entry->madt_lapic.apic_id; 263 caa.cpu_acpi_proc_id = entry->madt_lapic.acpi_proc_id; 264 #ifdef MULTIPROCESSOR 265 caa.cpu_func = &mp_cpu_funcs; 266 #endif 267 #ifdef __i386__ 268 /* 269 * XXX utterly wrong. These are the 270 * cpu_feature/cpu_id from the BSP cpu, now 271 * being given to another cpu. This is 272 * bullshit. 273 */ 274 extern int cpu_id, cpu_feature; 275 caa.cpu_signature = cpu_id; 276 caa.feature_flags = cpu_feature; 277 #endif 278 279 config_found(mainbus, &caa, acpimadt_print); 280 break; 281 case ACPI_MADT_IOAPIC: 282 dprintf("%s: IOAPIC: acpi_ioapic_id %x, address 0x%x, global_int_base 0x%x\n", 283 self->dv_xname, entry->madt_ioapic.acpi_ioapic_id, 284 entry->madt_ioapic.address, 285 entry->madt_ioapic.global_int_base); 286 287 memset(&aaa, 0, sizeof(struct apic_attach_args)); 288 aaa.aaa_name = "ioapic"; 289 aaa.apic_id = entry->madt_ioapic.acpi_ioapic_id; 290 aaa.apic_address = entry->madt_ioapic.address; 291 aaa.apic_vecbase = entry->madt_ioapic.global_int_base; 292 293 config_found(mainbus, &aaa, acpimadt_print); 294 break; 295 case ACPI_MADT_LAPIC_NMI: 296 nlapic_nmis++; 297 break; 298 case ACPI_MADT_X2APIC: 299 dprintf("%s: X2APIC: acpi_proc_uid %x, apic_id %x, flags 0x%x\n", 300 self->dv_xname, entry->madt_x2apic.acpi_proc_uid, 301 entry->madt_x2apic.apic_id, 302 entry->madt_x2apic.flags); 303 304 if (entry->madt_x2apic.apic_id > 255 || 305 (entry->madt_x2apic.flags & ACPI_PROC_ENABLE) == 0) 306 break; 307 308 memset(&caa, 0, sizeof(struct cpu_attach_args)); 309 if (lapic_cpu_number() == entry->madt_x2apic.apic_id) 310 caa.cpu_role = CPU_ROLE_BP; 311 else { 312 caa.cpu_role = CPU_ROLE_AP; 313 ncpusfound++; 314 } 315 caa.caa_name = "cpu"; 316 caa.cpu_apicid = entry->madt_x2apic.apic_id; 317 caa.cpu_acpi_proc_id = entry->madt_x2apic.acpi_proc_uid; 318 #ifdef MULTIPROCESSOR 319 caa.cpu_func = &mp_cpu_funcs; 320 #endif 321 #ifdef __i386__ 322 /* 323 * XXX utterly wrong. These are the 324 * cpu_feature/cpu_id from the BSP cpu, now 325 * being given to another cpu. This is 326 * bullshit. 327 */ 328 extern int cpu_id, cpu_feature; 329 caa.cpu_signature = cpu_id; 330 caa.feature_flags = cpu_feature; 331 #endif 332 333 config_found(mainbus, &caa, acpimadt_print); 334 break; 335 } 336 addr += entry->madt_lapic.length; 337 } 338 339 mp_intrs = mallocarray(nlapic_nmis, sizeof(struct mp_intr_map), 340 M_DEVBUF, M_NOWAIT); 341 if (mp_intrs == NULL) 342 return; 343 344 /* 2nd pass, get interrupt overrides */ 345 addr = (caddr_t)(madt + 1); 346 while (addr < (caddr_t)madt + madt->hdr.length) { 347 union acpi_madt_entry *entry = (union acpi_madt_entry *)addr; 348 349 switch (entry->madt_lapic.apic_type) { 350 case ACPI_MADT_LAPIC: 351 case ACPI_MADT_IOAPIC: 352 break; 353 354 case ACPI_MADT_OVERRIDE: 355 dprintf("%s: OVERRIDE: bus %x, source %x, global_int %x, flags %x\n", 356 self->dv_xname, entry->madt_override.bus, 357 entry->madt_override.source, 358 entry->madt_override.global_int, 359 entry->madt_override.flags); 360 361 pin = entry->madt_override.global_int; 362 apic = ioapic_find_bybase(pin); 363 364 map = malloc(sizeof(*map), M_DEVBUF, M_NOWAIT | M_ZERO); 365 if (map == NULL) 366 return; 367 368 map->ioapic = apic; 369 map->ioapic_pin = pin - apic->sc_apic_vecbase; 370 map->bus_pin = entry->madt_override.source; 371 map->flags = entry->madt_override.flags; 372 373 if (!acpimadt_cfg_intr(entry->madt_override.flags, &map->redir)) { 374 printf("%s: bogus override for pin %d\n", 375 self->dv_xname, pin); 376 free(map, M_DEVBUF, sizeof(*map)); 377 break; 378 } 379 380 map->ioapic_ih = APIC_INT_VIA_APIC | 381 ((apic->sc_apicid << APIC_INT_APIC_SHIFT) | 382 (pin << APIC_INT_PIN_SHIFT)); 383 384 apic->sc_pins[pin].ip_map = map; 385 386 map->next = mp_isa_bus->mb_intrs; 387 mp_isa_bus->mb_intrs = map; 388 break; 389 390 case ACPI_MADT_LAPIC_NMI: 391 dprintf("%s: LAPIC_NMI: acpi_proc_id %x, local_apic_lint %x, flags %x\n", 392 self->dv_xname, entry->madt_lapic_nmi.acpi_proc_id, 393 entry->madt_lapic_nmi.local_apic_lint, 394 entry->madt_lapic_nmi.flags); 395 396 pin = entry->madt_lapic_nmi.local_apic_lint; 397 398 map = &mp_intrs[mp_nintrs++]; 399 memset(map, 0, sizeof *map); 400 map->cpu_id = lapic_map[entry->madt_lapic_nmi.acpi_proc_id]; 401 map->ioapic_pin = pin; 402 map->flags = entry->madt_lapic_nmi.flags; 403 404 if ((pin != 0 && pin != 1) || 405 !acpimadt_cfg_intr(entry->madt_lapic_nmi.flags, &map->redir)) { 406 printf("%s: bogus nmi for apid %d\n", 407 self->dv_xname, map->cpu_id); 408 mp_nintrs--; 409 break; 410 } 411 412 map->redir &= ~IOAPIC_REDLO_DEL_MASK; 413 map->redir |= (IOAPIC_REDLO_DEL_NMI << IOAPIC_REDLO_DEL_SHIFT); 414 break; 415 416 case ACPI_MADT_X2APIC: 417 case ACPI_MADT_X2APIC_NMI: 418 break; 419 420 default: 421 printf("%s: unknown apic structure type %x\n", 422 self->dv_xname, entry->madt_lapic.apic_type); 423 } 424 425 addr += entry->madt_lapic.length; 426 } 427 428 /* 429 * ISA interrupts are supposed to be identity mapped unless 430 * there is an override, in which case we will already have a 431 * mapping for the interrupt. 432 */ 433 for (pin = 0; pin < ICU_LEN; pin++) { 434 /* Skip if we already have a mapping for this interrupt. */ 435 for (map = mp_isa_bus->mb_intrs; map != NULL; map = map->next) 436 if (map->bus_pin == pin) 437 break; 438 if (map != NULL) 439 continue; 440 441 apic = ioapic_find_bybase(pin); 442 443 map = malloc(sizeof(*map), M_DEVBUF, M_NOWAIT | M_ZERO); 444 if (map == NULL) 445 return; 446 447 map->ioapic = apic; 448 map->ioapic_pin = pin; 449 map->bus_pin = pin; 450 map->redir = (IOAPIC_REDLO_DEL_LOPRI << IOAPIC_REDLO_DEL_SHIFT); 451 452 map->ioapic_ih = APIC_INT_VIA_APIC | 453 ((apic->sc_apicid << APIC_INT_APIC_SHIFT) | 454 (pin << APIC_INT_PIN_SHIFT)); 455 456 apic->sc_pins[pin].ip_map = map; 457 458 map->next = mp_isa_bus->mb_intrs; 459 mp_isa_bus->mb_intrs = map; 460 } 461 } 462 463 int 464 acpimadt_print(void *aux, const char *pnp) 465 { 466 struct apic_attach_args *aaa = aux; 467 468 if (pnp) 469 printf("%s at %s:", aaa->aaa_name, pnp); 470 471 return (UNCONF); 472 } 473