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