1 /* 2 * Copyright 2008-2011, Michael Lotz, mmlr@mlotz.ch. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 #include <arch/x86/ioapic.h> 7 8 #include <int.h> 9 #include <vm/vm.h> 10 11 #include "irq_routing_table.h" 12 13 #include <ACPI.h> 14 #include <AutoDeleter.h> 15 #include <safemode.h> 16 #include <string.h> 17 #include <stdio.h> 18 19 #include <arch/x86/apic.h> 20 #include <arch/x86/arch_int.h> 21 #include <arch/x86/arch_smp.h> 22 #include <arch/x86/pic.h> 23 24 // to gain access to the ACPICA types 25 #include "acpi.h" 26 27 28 //#define TRACE_IOAPIC 29 #ifdef TRACE_IOAPIC 30 # define TRACE(...) dprintf(__VA_ARGS__) 31 #else 32 # define TRACE(...) (void)0 33 #endif 34 35 36 // ACPI interrupt models 37 #define ACPI_INTERRUPT_MODEL_PIC 0 38 #define ACPI_INTERRUPT_MODEL_APIC 1 39 #define ACPI_INTERRUPT_MODEL_SAPIC 2 40 41 42 // Definitions for a 82093AA IO APIC controller 43 #define IO_APIC_ID 0x00 44 #define IO_APIC_VERSION 0x01 45 #define IO_APIC_ARBITRATION 0x02 46 #define IO_APIC_REDIRECTION_TABLE 0x10 // entry = base + 2 * index 47 48 // Fields for the id register 49 #define IO_APIC_ID_SHIFT 24 50 #define IO_APIC_ID_MASK 0xff 51 52 // Fields for the version register 53 #define IO_APIC_VERSION_SHIFT 0 54 #define IO_APIC_VERSION_MASK 0xff 55 #define IO_APIC_MAX_REDIRECTION_ENTRY_SHIFT 16 56 #define IO_APIC_MAX_REDIRECTION_ENTRY_MASK 0xff 57 58 // Fields of each redirection table entry 59 #define IO_APIC_DESTINATION_FIELD_SHIFT 56 60 #define IO_APIC_DESTINATION_FIELD_MASK 0xff 61 #define IO_APIC_INTERRUPT_MASKED (1 << 16) 62 #define IO_APIC_TRIGGER_MODE_EDGE (0 << 16) 63 #define IO_APIC_TRIGGER_MODE_LEVEL (1 << 15) 64 #define IO_APIC_TRIGGER_MODE_MASK (1 << 15) 65 #define IO_APIC_REMOTE_IRR (1 << 14) 66 #define IO_APIC_PIN_POLARITY_HIGH_ACTIVE (0 << 13) 67 #define IO_APIC_PIN_POLARITY_LOW_ACTIVE (1 << 13) 68 #define IO_APIC_PIN_POLARITY_MASK (1 << 13) 69 #define IO_APIC_DELIVERY_STATUS_PENDING (1 << 12) 70 #define IO_APIC_DESTINATION_MODE_PHYSICAL (0 << 11) 71 #define IO_APIC_DESTINATION_MODE_LOGICAL (1 << 11) 72 #define IO_APIC_DESTINATION_MODE_MASK (1 << 11) 73 #define IO_APIC_DELIVERY_MODE_MASK (7 << 8) 74 #define IO_APIC_DELIVERY_MODE_FIXED (0 << 8) 75 #define IO_APIC_DELIVERY_MODE_LOWEST_PRIO (1 << 8) 76 #define IO_APIC_DELIVERY_MODE_SMI (2 << 8) 77 #define IO_APIC_DELIVERY_MODE_NMI (4 << 8) 78 #define IO_APIC_DELIVERY_MODE_INIT (5 << 8) 79 #define IO_APIC_DELIVERY_MODE_EXT_INT (7 << 8) 80 #define IO_APIC_INTERRUPT_VECTOR_SHIFT 0 81 #define IO_APIC_INTERRUPT_VECTOR_MASK 0xff 82 83 #define MAX_SUPPORTED_REDIRECTION_ENTRIES 64 84 #define ISA_INTERRUPT_COUNT 16 85 86 87 struct ioapic_registers { 88 volatile uint32 io_register_select; 89 uint32 reserved[3]; 90 volatile uint32 io_window_register; 91 }; 92 93 94 struct ioapic { 95 uint8 number; 96 uint8 apic_id; 97 uint32 version; 98 uint8 max_redirection_entry; 99 uint8 global_interrupt_base; 100 uint8 global_interrupt_last; 101 uint64 level_triggered_mask; 102 uint64 nmi_mask; 103 104 area_id register_area; 105 ioapic_registers* registers; 106 107 ioapic* next; 108 }; 109 110 111 static ioapic* sIOAPICs = NULL; 112 static int32 sSourceOverrides[ISA_INTERRUPT_COUNT]; 113 114 115 // #pragma mark - I/O APIC 116 117 118 static void 119 print_ioapic(struct ioapic& ioapic) 120 { 121 dprintf("io-apic %u has range %u-%u, %u entries, version 0x%08" B_PRIx32 122 ", apic-id %u\n", ioapic.number, ioapic.global_interrupt_base, 123 ioapic.global_interrupt_last, ioapic.max_redirection_entry + 1, 124 ioapic.version, ioapic.apic_id); 125 } 126 127 128 static inline struct ioapic* 129 find_ioapic(int32 gsi) 130 { 131 if (gsi < 0) 132 return NULL; 133 134 struct ioapic* current = sIOAPICs; 135 while (current != NULL) { 136 if (gsi >= current->global_interrupt_base 137 && gsi <= current->global_interrupt_last) { 138 return current; 139 } 140 141 current = current->next; 142 } 143 144 return NULL; 145 } 146 147 148 static inline uint32 149 ioapic_read_32(struct ioapic& ioapic, uint8 registerSelect) 150 { 151 ioapic.registers->io_register_select = registerSelect; 152 return ioapic.registers->io_window_register; 153 } 154 155 156 static inline void 157 ioapic_write_32(struct ioapic& ioapic, uint8 registerSelect, uint32 value) 158 { 159 ioapic.registers->io_register_select = registerSelect; 160 ioapic.registers->io_window_register = value; 161 } 162 163 164 static inline uint64 165 ioapic_read_64(struct ioapic& ioapic, uint8 registerSelect) 166 { 167 ioapic.registers->io_register_select = registerSelect + 1; 168 uint64 result = ioapic.registers->io_window_register; 169 result <<= 32; 170 ioapic.registers->io_register_select = registerSelect; 171 result |= ioapic.registers->io_window_register; 172 return result; 173 } 174 175 176 static inline void 177 ioapic_write_64(struct ioapic& ioapic, uint8 registerSelect, uint64 value, 178 bool maskFirst) 179 { 180 ioapic.registers->io_register_select 181 = registerSelect + (maskFirst ? 0 : 1); 182 ioapic.registers->io_window_register 183 = (uint32)(value >> (maskFirst ? 0 : 32)); 184 ioapic.registers->io_register_select 185 = registerSelect + (maskFirst ? 1 : 0); 186 ioapic.registers->io_window_register 187 = (uint32)(value >> (maskFirst ? 32 : 0)); 188 } 189 190 191 static void 192 ioapic_configure_pin(struct ioapic& ioapic, uint8 pin, uint8 vector, 193 uint8 triggerPolarity, uint16 deliveryMode) 194 { 195 uint64 entry = ioapic_read_64(ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2); 196 entry &= ~(IO_APIC_TRIGGER_MODE_MASK | IO_APIC_PIN_POLARITY_MASK 197 | IO_APIC_INTERRUPT_VECTOR_MASK | IO_APIC_DELIVERY_MODE_MASK); 198 199 if (triggerPolarity & B_LEVEL_TRIGGERED) { 200 entry |= IO_APIC_TRIGGER_MODE_LEVEL; 201 ioapic.level_triggered_mask |= ((uint64)1 << pin); 202 } else { 203 entry |= IO_APIC_TRIGGER_MODE_EDGE; 204 ioapic.level_triggered_mask &= ~((uint64)1 << pin); 205 } 206 207 if (triggerPolarity & B_LOW_ACTIVE_POLARITY) 208 entry |= IO_APIC_PIN_POLARITY_LOW_ACTIVE; 209 else 210 entry |= IO_APIC_PIN_POLARITY_HIGH_ACTIVE; 211 212 entry |= deliveryMode; 213 entry |= (vector + ARCH_INTERRUPT_BASE) << IO_APIC_INTERRUPT_VECTOR_SHIFT; 214 ioapic_write_64(ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2, entry, true); 215 } 216 217 218 static bool 219 ioapic_is_spurious_interrupt(int32 gsi) 220 { 221 // the spurious interrupt vector is initialized to the max value in smp 222 return gsi == 0xff - ARCH_INTERRUPT_BASE; 223 } 224 225 226 static bool 227 ioapic_is_level_triggered_interrupt(int32 gsi) 228 { 229 struct ioapic* ioapic = find_ioapic(gsi); 230 if (ioapic == NULL) 231 return false; 232 233 uint8 pin = gsi - ioapic->global_interrupt_base; 234 return (ioapic->level_triggered_mask & ((uint64)1 << pin)) != 0; 235 } 236 237 238 static bool 239 ioapic_end_of_interrupt(int32 num) 240 { 241 apic_end_of_interrupt(); 242 return true; 243 } 244 245 246 static void 247 ioapic_assign_interrupt_to_cpu(int32 gsi, int32 cpu) 248 { 249 if (gsi < ISA_INTERRUPT_COUNT && sSourceOverrides[gsi] != 0) 250 gsi = sSourceOverrides[gsi]; 251 252 struct ioapic* ioapic = find_ioapic(gsi); 253 if (ioapic == NULL) 254 return; 255 256 uint32 apicid = x86_get_cpu_apic_id(cpu); 257 258 uint8 pin = gsi - ioapic->global_interrupt_base; 259 TRACE("ioapic_assign_interrupt_to_cpu: gsi %ld (io-apic %u pin %u) to" 260 " cpu %ld (apic_id %lu)\n", gsi, ioapic->number, pin, cpu, apicid); 261 262 uint64 entry = ioapic_read_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2); 263 entry &= ~(uint64(IO_APIC_DESTINATION_FIELD_MASK) 264 << IO_APIC_DESTINATION_FIELD_SHIFT); 265 entry |= uint64(apicid) << IO_APIC_DESTINATION_FIELD_SHIFT; 266 ioapic_write_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2, entry, false); 267 } 268 269 270 static void 271 ioapic_enable_io_interrupt(int32 gsi) 272 { 273 // If enabling an overriden source is attempted, enable the override entry 274 // instead. An interrupt handler was installed at the override GSI to relay 275 // interrupts to the overriden source. 276 if (gsi < ISA_INTERRUPT_COUNT && sSourceOverrides[gsi] != 0) 277 gsi = sSourceOverrides[gsi]; 278 279 struct ioapic* ioapic = find_ioapic(gsi); 280 if (ioapic == NULL) 281 return; 282 283 x86_set_irq_source(gsi, IRQ_SOURCE_IOAPIC); 284 285 uint8 pin = gsi - ioapic->global_interrupt_base; 286 TRACE("ioapic_enable_io_interrupt: gsi %ld -> io-apic %u pin %u\n", 287 gsi, ioapic->number, pin); 288 289 uint64 entry = ioapic_read_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2); 290 entry &= ~IO_APIC_INTERRUPT_MASKED; 291 ioapic_write_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2, entry, false); 292 } 293 294 295 static void 296 ioapic_disable_io_interrupt(int32 gsi) 297 { 298 struct ioapic* ioapic = find_ioapic(gsi); 299 if (ioapic == NULL) 300 return; 301 302 uint8 pin = gsi - ioapic->global_interrupt_base; 303 TRACE("ioapic_disable_io_interrupt: gsi %ld -> io-apic %u pin %u\n", 304 gsi, ioapic->number, pin); 305 306 uint64 entry = ioapic_read_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2); 307 entry |= IO_APIC_INTERRUPT_MASKED; 308 ioapic_write_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2, entry, true); 309 } 310 311 312 static void 313 ioapic_configure_io_interrupt(int32 gsi, uint32 config) 314 { 315 struct ioapic* ioapic = find_ioapic(gsi); 316 if (ioapic == NULL) 317 return; 318 319 uint8 pin = gsi - ioapic->global_interrupt_base; 320 TRACE("ioapic_configure_io_interrupt: gsi %ld -> io-apic %u pin %u; " 321 "config 0x%08lx\n", gsi, ioapic->number, pin, config); 322 323 ioapic_configure_pin(*ioapic, pin, gsi, config, 324 IO_APIC_DELIVERY_MODE_FIXED); 325 } 326 327 328 static status_t 329 ioapic_map_ioapic(struct ioapic& ioapic, phys_addr_t physicalAddress) 330 { 331 ioapic.register_area = vm_map_physical_memory(B_SYSTEM_TEAM, "io-apic", 332 (void**)&ioapic.registers, ioapic.registers != NULL ? B_EXACT_ADDRESS 333 : B_ANY_KERNEL_ADDRESS, B_PAGE_SIZE, B_KERNEL_READ_AREA 334 | B_KERNEL_WRITE_AREA, physicalAddress, ioapic.registers != NULL); 335 if (ioapic.register_area < 0) { 336 panic("mapping io-apic %u failed", ioapic.number); 337 return ioapic.register_area; 338 } 339 340 TRACE("mapped io-apic %u to %p\n", ioapic.number, ioapic.registers); 341 342 ioapic.version = ioapic_read_32(ioapic, IO_APIC_VERSION); 343 if (ioapic.version == 0xffffffff) { 344 dprintf("io-apic %u seems inaccessible, not using it\n", 345 ioapic.number); 346 vm_delete_area(B_SYSTEM_TEAM, ioapic.register_area, true); 347 ioapic.register_area = -1; 348 ioapic.registers = NULL; 349 return B_ERROR; 350 } 351 352 ioapic.max_redirection_entry 353 = ((ioapic.version >> IO_APIC_MAX_REDIRECTION_ENTRY_SHIFT) 354 & IO_APIC_MAX_REDIRECTION_ENTRY_MASK); 355 if (ioapic.max_redirection_entry >= MAX_SUPPORTED_REDIRECTION_ENTRIES) { 356 dprintf("io-apic %u entry count exceeds max supported, only using the " 357 "first %u entries", ioapic.number, 358 (uint8)MAX_SUPPORTED_REDIRECTION_ENTRIES); 359 ioapic.max_redirection_entry = MAX_SUPPORTED_REDIRECTION_ENTRIES - 1; 360 } 361 362 ioapic.global_interrupt_last 363 = ioapic.global_interrupt_base + ioapic.max_redirection_entry; 364 365 ioapic.nmi_mask = 0; 366 367 return B_OK; 368 } 369 370 371 static status_t 372 ioapic_initialize_ioapic(struct ioapic& ioapic, uint8 targetAPIC) 373 { 374 // program the APIC ID 375 ioapic_write_32(ioapic, IO_APIC_ID, ioapic.apic_id << IO_APIC_ID_SHIFT); 376 377 // program the interrupt vectors of the io-apic 378 ioapic.level_triggered_mask = 0; 379 uint8 gsi = ioapic.global_interrupt_base; 380 for (uint8 i = 0; i <= ioapic.max_redirection_entry; i++, gsi++) { 381 // initialize everything to deliver to the boot CPU in physical mode 382 // and masked until explicitly enabled through enable_io_interrupt() 383 uint64 entry = ((uint64)targetAPIC << IO_APIC_DESTINATION_FIELD_SHIFT) 384 | IO_APIC_INTERRUPT_MASKED | IO_APIC_DESTINATION_MODE_PHYSICAL 385 | ((gsi + ARCH_INTERRUPT_BASE) << IO_APIC_INTERRUPT_VECTOR_SHIFT); 386 387 if (gsi == 0) { 388 // make GSI 0 into an external interrupt 389 entry |= IO_APIC_TRIGGER_MODE_EDGE 390 | IO_APIC_PIN_POLARITY_HIGH_ACTIVE 391 | IO_APIC_DELIVERY_MODE_EXT_INT; 392 } else if (gsi < ISA_INTERRUPT_COUNT) { 393 // identity map the legacy ISA interrupts 394 entry |= IO_APIC_TRIGGER_MODE_EDGE 395 | IO_APIC_PIN_POLARITY_HIGH_ACTIVE 396 | IO_APIC_DELIVERY_MODE_FIXED; 397 } else { 398 // and the rest are PCI interrupts 399 entry |= IO_APIC_TRIGGER_MODE_LEVEL 400 | IO_APIC_PIN_POLARITY_LOW_ACTIVE 401 | IO_APIC_DELIVERY_MODE_FIXED; 402 ioapic.level_triggered_mask |= ((uint64)1 << i); 403 } 404 405 ioapic_write_64(ioapic, IO_APIC_REDIRECTION_TABLE + 2 * i, entry, true); 406 } 407 408 return B_OK; 409 } 410 411 412 static int32 413 ioapic_source_override_handler(void* data) 414 { 415 int32 vector = (addr_t)data; 416 bool levelTriggered = ioapic_is_level_triggered_interrupt(vector); 417 return int_io_interrupt_handler(vector, levelTriggered); 418 } 419 420 421 static status_t 422 acpi_enumerate_ioapics(acpi_table_madt* madt) 423 { 424 struct ioapic* lastIOAPIC = sIOAPICs; 425 426 acpi_subtable_header* apicEntry 427 = (acpi_subtable_header*)((uint8*)madt + sizeof(acpi_table_madt)); 428 void* end = ((uint8*)madt + madt->Header.Length); 429 while (apicEntry < end) { 430 switch (apicEntry->Type) { 431 case ACPI_MADT_TYPE_IO_APIC: 432 { 433 acpi_madt_io_apic* info = (acpi_madt_io_apic*)apicEntry; 434 dprintf("found io-apic with address 0x%08" B_PRIx32 ", global " 435 "interrupt base %" B_PRIu32 ", apic-id %u\n", 436 (uint32)info->Address, (uint32)info->GlobalIrqBase, 437 info->Id); 438 439 struct ioapic* ioapic 440 = (struct ioapic*)malloc(sizeof(struct ioapic)); 441 if (ioapic == NULL) { 442 dprintf("ran out of memory while allocating io-apic " 443 "structure\n"); 444 return B_NO_MEMORY; 445 } 446 447 ioapic->number 448 = lastIOAPIC != NULL ? lastIOAPIC->number + 1 : 0; 449 ioapic->apic_id = info->Id; 450 ioapic->global_interrupt_base = info->GlobalIrqBase; 451 ioapic->registers = NULL; 452 ioapic->next = NULL; 453 454 dprintf("mapping io-apic %u at physical address %#" B_PRIx32 455 "\n", ioapic->number, (uint32)info->Address); 456 status_t status = ioapic_map_ioapic(*ioapic, info->Address); 457 if (status != B_OK) { 458 free(ioapic); 459 break; 460 } 461 462 print_ioapic(*ioapic); 463 464 if (lastIOAPIC == NULL) 465 sIOAPICs = ioapic; 466 else 467 lastIOAPIC->next = ioapic; 468 469 lastIOAPIC = ioapic; 470 break; 471 } 472 473 case ACPI_MADT_TYPE_NMI_SOURCE: 474 { 475 acpi_madt_nmi_source* info 476 = (acpi_madt_nmi_source*)apicEntry; 477 dprintf("found nmi source global irq %" B_PRIu32 ", flags " 478 "0x%04x\n", (uint32)info->GlobalIrq, 479 (uint16)info->IntiFlags); 480 481 struct ioapic* ioapic = find_ioapic(info->GlobalIrq); 482 if (ioapic == NULL) { 483 dprintf("nmi source for gsi that is not mapped to any " 484 " io-apic\n"); 485 break; 486 } 487 488 uint8 pin = info->GlobalIrq - ioapic->global_interrupt_base; 489 ioapic->nmi_mask |= (uint64)1 << pin; 490 break; 491 } 492 } 493 494 apicEntry 495 = (acpi_subtable_header*)((uint8*)apicEntry + apicEntry->Length); 496 } 497 498 return B_OK; 499 } 500 501 502 static inline uint32 503 acpi_madt_convert_inti_flags(uint16 flags) 504 { 505 uint32 config = 0; 506 switch (flags & ACPI_MADT_POLARITY_MASK) { 507 case ACPI_MADT_POLARITY_ACTIVE_LOW: 508 config = B_LOW_ACTIVE_POLARITY; 509 break; 510 default: 511 dprintf("invalid polarity in inti flags\n"); 512 // fall through and assume active high 513 case ACPI_MADT_POLARITY_ACTIVE_HIGH: 514 case ACPI_MADT_POLARITY_CONFORMS: 515 config = B_HIGH_ACTIVE_POLARITY; 516 break; 517 } 518 519 switch (flags & ACPI_MADT_TRIGGER_MASK) { 520 case ACPI_MADT_TRIGGER_LEVEL: 521 config |= B_LEVEL_TRIGGERED; 522 break; 523 default: 524 dprintf("invalid trigger mode in inti flags\n"); 525 // fall through and assume edge triggered 526 case ACPI_MADT_TRIGGER_CONFORMS: 527 case ACPI_MADT_TRIGGER_EDGE: 528 config |= B_EDGE_TRIGGERED; 529 break; 530 } 531 532 return config; 533 } 534 535 536 static void 537 acpi_configure_source_overrides(acpi_table_madt* madt) 538 { 539 acpi_subtable_header* apicEntry 540 = (acpi_subtable_header*)((uint8*)madt + sizeof(acpi_table_madt)); 541 void* end = ((uint8*)madt + madt->Header.Length); 542 while (apicEntry < end) { 543 switch (apicEntry->Type) { 544 case ACPI_MADT_TYPE_INTERRUPT_OVERRIDE: 545 { 546 acpi_madt_interrupt_override* info 547 = (acpi_madt_interrupt_override*)apicEntry; 548 dprintf("found interrupt override for bus %u, source irq %u, " 549 "global irq %" B_PRIu32 ", flags 0x%08" B_PRIx32 "\n", 550 info->Bus, info->SourceIrq, (uint32)info->GlobalIrq, 551 (uint32)info->IntiFlags); 552 553 if (info->SourceIrq >= ISA_INTERRUPT_COUNT) { 554 dprintf("source override exceeds isa interrupt count\n"); 555 break; 556 } 557 558 if (info->SourceIrq != info->GlobalIrq) { 559 // we need a vector mapping 560 install_io_interrupt_handler(info->GlobalIrq, 561 &ioapic_source_override_handler, 562 (void*)(addr_t)info->SourceIrq, B_NO_ENABLE_COUNTER); 563 564 sSourceOverrides[info->SourceIrq] = info->GlobalIrq; 565 } 566 567 // configure non-standard polarity/trigger modes 568 uint32 config = acpi_madt_convert_inti_flags(info->IntiFlags); 569 ioapic_configure_io_interrupt(info->GlobalIrq, config); 570 break; 571 } 572 573 case ACPI_MADT_TYPE_NMI_SOURCE: 574 { 575 acpi_madt_nmi_source* info 576 = (acpi_madt_nmi_source*)apicEntry; 577 dprintf("found nmi source global irq %" B_PRIu32 ", flags " 578 "0x%04x\n", (uint32)info->GlobalIrq, 579 (uint16)info->IntiFlags); 580 581 struct ioapic* ioapic = find_ioapic(info->GlobalIrq); 582 if (ioapic == NULL) 583 break; 584 585 uint8 pin = info->GlobalIrq - ioapic->global_interrupt_base; 586 uint32 config = acpi_madt_convert_inti_flags(info->IntiFlags); 587 ioapic_configure_pin(*ioapic, pin, info->GlobalIrq, config, 588 IO_APIC_DELIVERY_MODE_NMI); 589 break; 590 } 591 592 #ifdef TRACE_IOAPIC 593 case ACPI_MADT_TYPE_LOCAL_APIC: 594 { 595 // purely informational 596 acpi_madt_local_apic* info = (acpi_madt_local_apic*)apicEntry; 597 dprintf("found local apic with id %u, processor id %u, " 598 "flags 0x%08lx\n", info->Id, info->ProcessorId, 599 (uint32)info->LapicFlags); 600 break; 601 } 602 603 case ACPI_MADT_TYPE_LOCAL_APIC_NMI: 604 { 605 // TODO: take these into account, but at apic.cpp 606 acpi_madt_local_apic_nmi* info 607 = (acpi_madt_local_apic_nmi*)apicEntry; 608 dprintf("found local apic nmi source for processor %u, " 609 "flags 0x%04x, local int %u\n", info->ProcessorId, 610 (uint16)info->IntiFlags, info->Lint); 611 break; 612 } 613 614 case ACPI_MADT_TYPE_LOCAL_APIC_OVERRIDE: 615 { 616 // TODO: take these into account, but at apic.cpp 617 acpi_madt_local_apic_override* info 618 = (acpi_madt_local_apic_override*)apicEntry; 619 dprintf("found local apic override with address 0x%016llx\n", 620 (uint64)info->Address); 621 break; 622 } 623 624 default: 625 dprintf("found unhandled subtable of type %u length %u\n", 626 apicEntry->Type, apicEntry->Length); 627 break; 628 #endif 629 } 630 631 apicEntry 632 = (acpi_subtable_header*)((uint8*)apicEntry + apicEntry->Length); 633 } 634 } 635 636 637 static status_t 638 acpi_set_interrupt_model(acpi_module_info* acpiModule, uint32 interruptModel) 639 { 640 acpi_object_type model; 641 model.object_type = ACPI_TYPE_INTEGER; 642 model.integer.integer = interruptModel; 643 644 acpi_objects parameter; 645 parameter.count = 1; 646 parameter.pointer = &model; 647 648 dprintf("setting ACPI interrupt model to %s\n", 649 interruptModel == 0 ? "PIC" 650 : (interruptModel == 1 ? "APIC" 651 : (interruptModel == 2 ? "SAPIC" 652 : "unknown"))); 653 654 return acpiModule->evaluate_method(NULL, "\\_PIC", ¶meter, NULL); 655 } 656 657 658 bool 659 ioapic_is_interrupt_available(int32 gsi) 660 { 661 struct ioapic* ioapic = find_ioapic(gsi); 662 if (ioapic == NULL) 663 return false; 664 665 uint8 pin = gsi - ioapic->global_interrupt_base; 666 return (ioapic->nmi_mask & ((uint64)1 << pin)) == 0; 667 } 668 669 670 void 671 ioapic_init(kernel_args* args) 672 { 673 static const interrupt_controller ioapicController = { 674 "82093AA IOAPIC", 675 &ioapic_enable_io_interrupt, 676 &ioapic_disable_io_interrupt, 677 &ioapic_configure_io_interrupt, 678 &ioapic_is_spurious_interrupt, 679 &ioapic_is_level_triggered_interrupt, 680 &ioapic_end_of_interrupt, 681 &ioapic_assign_interrupt_to_cpu, 682 }; 683 684 if (args->arch_args.apic == NULL) 685 return; 686 687 if (args->arch_args.ioapic_phys == 0) { 688 dprintf("no io-apics available, not using io-apics for interrupt " 689 "routing\n"); 690 return; 691 } 692 693 if (get_safemode_boolean(B_SAFEMODE_DISABLE_IOAPIC, false)) { 694 dprintf("io-apics explicitly disabled, not using io-apics for " 695 "interrupt routing\n"); 696 return; 697 } 698 699 // load acpi module 700 status_t status; 701 acpi_module_info* acpiModule; 702 status = get_module(B_ACPI_MODULE_NAME, (module_info**)&acpiModule); 703 if (status != B_OK) { 704 dprintf("acpi module not available, not configuring io-apics\n"); 705 return; 706 } 707 BPrivate::CObjectDeleter<const char, status_t> 708 acpiModulePutter(B_ACPI_MODULE_NAME, put_module); 709 710 acpi_table_madt* madt = NULL; 711 if (acpiModule->get_table(ACPI_SIG_MADT, 0, (void**)&madt) != B_OK) { 712 dprintf("failed to get MADT from ACPI, not configuring io-apics\n"); 713 return; 714 } 715 716 status = acpi_enumerate_ioapics(madt); 717 if (status != B_OK) { 718 // We don't treat this case as fatal just yet. If we are able to 719 // route everything with the available IO-APICs we're fine, if not 720 // we will fail at the routing preparation stage further down. 721 dprintf("failed to enumerate all io-apics, working with what we got\n"); 722 } 723 724 // switch to the APIC interrupt model before retrieving the IRQ routing 725 // table as it will return different settings depending on the model 726 status = acpi_set_interrupt_model(acpiModule, ACPI_INTERRUPT_MODEL_APIC); 727 if (status != B_OK) { 728 dprintf("failed to put ACPI into APIC interrupt model, ignoring\n"); 729 // don't abort, as the _PIC method is optional and as long as there 730 // aren't different routings based on it this is non-fatal 731 } 732 733 IRQRoutingTable table; 734 status = prepare_irq_routing(acpiModule, table, 735 &ioapic_is_interrupt_available); 736 if (status != B_OK) { 737 dprintf("IRQ routing preparation failed, not configuring io-apics\n"); 738 acpi_set_interrupt_model(acpiModule, ACPI_INTERRUPT_MODEL_PIC); 739 // revert to PIC interrupt model just in case 740 return; 741 } 742 743 // use the boot CPU as the target for all interrupts 744 uint8 targetAPIC = args->arch_args.cpu_apic_id[0]; 745 746 struct ioapic* current = sIOAPICs; 747 while (current != NULL) { 748 status = ioapic_initialize_ioapic(*current, targetAPIC); 749 if (status != B_OK) { 750 panic("failed to initialize io-apic %u", current->number); 751 acpi_set_interrupt_model(acpiModule, ACPI_INTERRUPT_MODEL_PIC); 752 return; 753 } 754 755 current = current->next; 756 } 757 758 #ifdef TRACE_IOAPIC 759 dprintf("trying interrupt routing:\n"); 760 print_irq_routing_table(table); 761 #endif 762 763 status = enable_irq_routing(acpiModule, table); 764 if (status != B_OK) { 765 panic("failed to enable IRQ routing"); 766 // if it failed early on it might still work in PIC mode 767 acpi_set_interrupt_model(acpiModule, ACPI_INTERRUPT_MODEL_PIC); 768 return; 769 } 770 771 print_irq_routing_table(table); 772 773 // configure the source overrides, but let the PCI config below override it 774 acpi_configure_source_overrides(madt); 775 776 // configure IO-APIC interrupts from PCI routing table 777 for (int i = 0; i < table.Count(); i++) { 778 irq_routing_entry& entry = table.ElementAt(i); 779 ioapic_configure_io_interrupt(entry.irq, 780 entry.polarity | entry.trigger_mode); 781 } 782 783 // kill the local ints on the local APIC 784 apic_disable_local_ints(); 785 // TODO: This uses the assumption that our init is running on the 786 // boot CPU and only the boot CPU has the local ints configured 787 // because it was running in legacy PIC mode. Possibly the other 788 // local APICs of the other CPUs have them configured as well. It 789 // shouldn't really harm, but should eventually be corrected. 790 791 // disable the legacy PIC 792 uint16 legacyInterrupts; 793 pic_disable(legacyInterrupts); 794 795 // enable previsouly enabled legacy interrupts 796 for (uint8 i = 0; i < 16; i++) { 797 if ((legacyInterrupts & (1 << i)) != 0) 798 ioapic_enable_io_interrupt(i); 799 } 800 801 // mark the interrupt vectors reserved so they aren't used for other stuff 802 current = sIOAPICs; 803 while (current != NULL) { 804 reserve_io_interrupt_vectors(current->max_redirection_entry + 1, 805 current->global_interrupt_base, INTERRUPT_TYPE_IRQ); 806 807 for (int32 i = 0; i < current->max_redirection_entry + 1; i++) { 808 x86_set_irq_source(current->global_interrupt_base + i, 809 IRQ_SOURCE_IOAPIC); 810 } 811 812 current = current->next; 813 } 814 815 // prefer the ioapic over the normal pic 816 dprintf("using io-apics for interrupt routing\n"); 817 arch_int_set_interrupt_controller(ioapicController); 818 } 819