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 "acpi_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 << 15) 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 %" B_PRId32 260 " (io-apic %u pin %u) to cpu %" B_PRId32 " (apic_id %" B_PRIx32 ")\n", 261 gsi, ioapic->number, pin, cpu, apicid); 262 263 uint64 entry = ioapic_read_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2); 264 entry &= ~(uint64(IO_APIC_DESTINATION_FIELD_MASK) 265 << IO_APIC_DESTINATION_FIELD_SHIFT); 266 entry |= uint64(apicid) << IO_APIC_DESTINATION_FIELD_SHIFT; 267 ioapic_write_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2, entry, false); 268 } 269 270 271 static void 272 ioapic_enable_io_interrupt(int32 gsi) 273 { 274 // If enabling an overriden source is attempted, enable the override entry 275 // instead. An interrupt handler was installed at the override GSI to relay 276 // interrupts to the overriden source. 277 if (gsi < ISA_INTERRUPT_COUNT && sSourceOverrides[gsi] != 0) 278 gsi = sSourceOverrides[gsi]; 279 280 struct ioapic* ioapic = find_ioapic(gsi); 281 if (ioapic == NULL) 282 return; 283 284 x86_set_irq_source(gsi, IRQ_SOURCE_IOAPIC); 285 286 uint8 pin = gsi - ioapic->global_interrupt_base; 287 TRACE("ioapic_enable_io_interrupt: gsi %" B_PRId32 288 " -> io-apic %u pin %u\n", gsi, ioapic->number, pin); 289 290 uint64 entry = ioapic_read_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2); 291 entry &= ~IO_APIC_INTERRUPT_MASKED; 292 ioapic_write_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2, entry, false); 293 } 294 295 296 static void 297 ioapic_disable_io_interrupt(int32 gsi) 298 { 299 struct ioapic* ioapic = find_ioapic(gsi); 300 if (ioapic == NULL) 301 return; 302 303 uint8 pin = gsi - ioapic->global_interrupt_base; 304 TRACE("ioapic_disable_io_interrupt: gsi %" B_PRId32 305 " -> io-apic %u pin %u\n", gsi, ioapic->number, pin); 306 307 uint64 entry = ioapic_read_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2); 308 entry |= IO_APIC_INTERRUPT_MASKED; 309 ioapic_write_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2, entry, true); 310 } 311 312 313 static void 314 ioapic_configure_io_interrupt(int32 gsi, uint32 config) 315 { 316 struct ioapic* ioapic = find_ioapic(gsi); 317 if (ioapic == NULL) 318 return; 319 320 uint8 pin = gsi - ioapic->global_interrupt_base; 321 TRACE("ioapic_configure_io_interrupt: gsi %" B_PRId32 322 " -> io-apic %u pin %u; config 0x%08" B_PRIx32 "\n", gsi, 323 ioapic->number, pin, config); 324 325 ioapic_configure_pin(*ioapic, pin, gsi, config, 326 IO_APIC_DELIVERY_MODE_FIXED); 327 } 328 329 330 static status_t 331 ioapic_map_ioapic(struct ioapic& ioapic, phys_addr_t physicalAddress) 332 { 333 ioapic.register_area = vm_map_physical_memory(B_SYSTEM_TEAM, "io-apic", 334 (void**)&ioapic.registers, ioapic.registers != NULL ? B_EXACT_ADDRESS 335 : B_ANY_KERNEL_ADDRESS, B_PAGE_SIZE, B_KERNEL_READ_AREA 336 | B_KERNEL_WRITE_AREA, physicalAddress, ioapic.registers != NULL); 337 if (ioapic.register_area < 0) { 338 panic("mapping io-apic %u failed", ioapic.number); 339 return ioapic.register_area; 340 } 341 342 TRACE("mapped io-apic %u to %p\n", ioapic.number, ioapic.registers); 343 344 ioapic.version = ioapic_read_32(ioapic, IO_APIC_VERSION); 345 if (ioapic.version == 0xffffffff) { 346 dprintf("io-apic %u seems inaccessible, not using it\n", 347 ioapic.number); 348 vm_delete_area(B_SYSTEM_TEAM, ioapic.register_area, true); 349 ioapic.register_area = -1; 350 ioapic.registers = NULL; 351 return B_ERROR; 352 } 353 354 ioapic.max_redirection_entry 355 = ((ioapic.version >> IO_APIC_MAX_REDIRECTION_ENTRY_SHIFT) 356 & IO_APIC_MAX_REDIRECTION_ENTRY_MASK); 357 if (ioapic.max_redirection_entry >= MAX_SUPPORTED_REDIRECTION_ENTRIES) { 358 dprintf("io-apic %u entry count exceeds max supported, only using the " 359 "first %u entries", ioapic.number, 360 (uint8)MAX_SUPPORTED_REDIRECTION_ENTRIES); 361 ioapic.max_redirection_entry = MAX_SUPPORTED_REDIRECTION_ENTRIES - 1; 362 } 363 364 ioapic.global_interrupt_last 365 = ioapic.global_interrupt_base + ioapic.max_redirection_entry; 366 367 ioapic.nmi_mask = 0; 368 369 return B_OK; 370 } 371 372 373 static status_t 374 ioapic_initialize_ioapic(struct ioapic& ioapic, uint8 targetAPIC) 375 { 376 // program the APIC ID 377 ioapic_write_32(ioapic, IO_APIC_ID, ioapic.apic_id << IO_APIC_ID_SHIFT); 378 379 // program the interrupt vectors of the io-apic 380 ioapic.level_triggered_mask = 0; 381 uint8 gsi = ioapic.global_interrupt_base; 382 for (uint8 i = 0; i <= ioapic.max_redirection_entry; i++, gsi++) { 383 // initialize everything to deliver to the boot CPU in physical mode 384 // and masked until explicitly enabled through enable_io_interrupt() 385 uint64 entry = ((uint64)targetAPIC << IO_APIC_DESTINATION_FIELD_SHIFT) 386 | IO_APIC_INTERRUPT_MASKED | IO_APIC_DESTINATION_MODE_PHYSICAL 387 | ((gsi + ARCH_INTERRUPT_BASE) << IO_APIC_INTERRUPT_VECTOR_SHIFT); 388 389 if (gsi == 0) { 390 // make GSI 0 into an external interrupt 391 entry |= IO_APIC_TRIGGER_MODE_EDGE 392 | IO_APIC_PIN_POLARITY_HIGH_ACTIVE 393 | IO_APIC_DELIVERY_MODE_EXT_INT; 394 } else if (gsi < ISA_INTERRUPT_COUNT) { 395 // identity map the legacy ISA interrupts 396 entry |= IO_APIC_TRIGGER_MODE_EDGE 397 | IO_APIC_PIN_POLARITY_HIGH_ACTIVE 398 | IO_APIC_DELIVERY_MODE_FIXED; 399 } else { 400 // and the rest are PCI interrupts 401 entry |= IO_APIC_TRIGGER_MODE_LEVEL 402 | IO_APIC_PIN_POLARITY_LOW_ACTIVE 403 | IO_APIC_DELIVERY_MODE_FIXED; 404 ioapic.level_triggered_mask |= ((uint64)1 << i); 405 } 406 407 ioapic_write_64(ioapic, IO_APIC_REDIRECTION_TABLE + 2 * i, entry, true); 408 } 409 410 return B_OK; 411 } 412 413 414 static int32 415 ioapic_source_override_handler(void* data) 416 { 417 int32 vector = (addr_t)data; 418 bool levelTriggered = ioapic_is_level_triggered_interrupt(vector); 419 return int_io_interrupt_handler(vector, levelTriggered); 420 } 421 422 423 static status_t 424 acpi_enumerate_ioapics(acpi_table_madt* madt) 425 { 426 struct ioapic* lastIOAPIC = sIOAPICs; 427 428 acpi_subtable_header* apicEntry 429 = (acpi_subtable_header*)((uint8*)madt + sizeof(acpi_table_madt)); 430 void* end = ((uint8*)madt + madt->Header.Length); 431 while (apicEntry < end) { 432 switch (apicEntry->Type) { 433 case ACPI_MADT_TYPE_IO_APIC: 434 { 435 acpi_madt_io_apic* info = (acpi_madt_io_apic*)apicEntry; 436 dprintf("found io-apic with address 0x%08" B_PRIx32 ", global " 437 "interrupt base %" B_PRIu32 ", apic-id %u\n", 438 (uint32)info->Address, (uint32)info->GlobalIrqBase, 439 info->Id); 440 441 struct ioapic* ioapic 442 = (struct ioapic*)malloc(sizeof(struct ioapic)); 443 if (ioapic == NULL) { 444 dprintf("ran out of memory while allocating io-apic " 445 "structure\n"); 446 return B_NO_MEMORY; 447 } 448 449 ioapic->number 450 = lastIOAPIC != NULL ? lastIOAPIC->number + 1 : 0; 451 ioapic->apic_id = info->Id; 452 ioapic->global_interrupt_base = info->GlobalIrqBase; 453 ioapic->registers = NULL; 454 ioapic->next = NULL; 455 456 dprintf("mapping io-apic %u at physical address %#" B_PRIx32 457 "\n", ioapic->number, (uint32)info->Address); 458 status_t status = ioapic_map_ioapic(*ioapic, info->Address); 459 if (status != B_OK) { 460 free(ioapic); 461 break; 462 } 463 464 print_ioapic(*ioapic); 465 466 if (lastIOAPIC == NULL) 467 sIOAPICs = ioapic; 468 else 469 lastIOAPIC->next = ioapic; 470 471 lastIOAPIC = ioapic; 472 break; 473 } 474 475 case ACPI_MADT_TYPE_NMI_SOURCE: 476 { 477 acpi_madt_nmi_source* info 478 = (acpi_madt_nmi_source*)apicEntry; 479 dprintf("found nmi source global irq %" B_PRIu32 ", flags " 480 "0x%04x\n", (uint32)info->GlobalIrq, 481 (uint16)info->IntiFlags); 482 483 struct ioapic* ioapic = find_ioapic(info->GlobalIrq); 484 if (ioapic == NULL) { 485 dprintf("nmi source for gsi that is not mapped to any " 486 " io-apic\n"); 487 break; 488 } 489 490 uint8 pin = info->GlobalIrq - ioapic->global_interrupt_base; 491 ioapic->nmi_mask |= (uint64)1 << pin; 492 break; 493 } 494 } 495 496 apicEntry 497 = (acpi_subtable_header*)((uint8*)apicEntry + apicEntry->Length); 498 } 499 500 return B_OK; 501 } 502 503 504 static inline uint32 505 acpi_madt_convert_inti_flags(uint16 flags) 506 { 507 uint32 config = 0; 508 switch (flags & ACPI_MADT_POLARITY_MASK) { 509 case ACPI_MADT_POLARITY_ACTIVE_LOW: 510 config = B_LOW_ACTIVE_POLARITY; 511 break; 512 default: 513 dprintf("invalid polarity in inti flags\n"); 514 // fall through and assume active high 515 case ACPI_MADT_POLARITY_ACTIVE_HIGH: 516 case ACPI_MADT_POLARITY_CONFORMS: 517 config = B_HIGH_ACTIVE_POLARITY; 518 break; 519 } 520 521 switch (flags & ACPI_MADT_TRIGGER_MASK) { 522 case ACPI_MADT_TRIGGER_LEVEL: 523 config |= B_LEVEL_TRIGGERED; 524 break; 525 default: 526 dprintf("invalid trigger mode in inti flags\n"); 527 // fall through and assume edge triggered 528 case ACPI_MADT_TRIGGER_CONFORMS: 529 case ACPI_MADT_TRIGGER_EDGE: 530 config |= B_EDGE_TRIGGERED; 531 break; 532 } 533 534 return config; 535 } 536 537 538 static void 539 acpi_configure_source_overrides(acpi_table_madt* madt) 540 { 541 acpi_subtable_header* apicEntry 542 = (acpi_subtable_header*)((uint8*)madt + sizeof(acpi_table_madt)); 543 void* end = ((uint8*)madt + madt->Header.Length); 544 while (apicEntry < end) { 545 switch (apicEntry->Type) { 546 case ACPI_MADT_TYPE_INTERRUPT_OVERRIDE: 547 { 548 acpi_madt_interrupt_override* info 549 = (acpi_madt_interrupt_override*)apicEntry; 550 dprintf("found interrupt override for bus %u, source irq %u, " 551 "global irq %" B_PRIu32 ", flags 0x%08" B_PRIx32 "\n", 552 info->Bus, info->SourceIrq, (uint32)info->GlobalIrq, 553 (uint32)info->IntiFlags); 554 555 if (info->SourceIrq >= ISA_INTERRUPT_COUNT) { 556 dprintf("source override exceeds isa interrupt count\n"); 557 break; 558 } 559 560 if (info->SourceIrq != info->GlobalIrq) { 561 // we need a vector mapping 562 install_io_interrupt_handler(info->GlobalIrq, 563 &ioapic_source_override_handler, 564 (void*)(addr_t)info->SourceIrq, B_NO_ENABLE_COUNTER); 565 566 sSourceOverrides[info->SourceIrq] = info->GlobalIrq; 567 } 568 569 // configure non-standard polarity/trigger modes 570 uint32 config = acpi_madt_convert_inti_flags(info->IntiFlags); 571 ioapic_configure_io_interrupt(info->GlobalIrq, config); 572 break; 573 } 574 575 case ACPI_MADT_TYPE_NMI_SOURCE: 576 { 577 acpi_madt_nmi_source* info 578 = (acpi_madt_nmi_source*)apicEntry; 579 dprintf("found nmi source global irq %" B_PRIu32 ", flags " 580 "0x%04x\n", (uint32)info->GlobalIrq, 581 (uint16)info->IntiFlags); 582 583 struct ioapic* ioapic = find_ioapic(info->GlobalIrq); 584 if (ioapic == NULL) 585 break; 586 587 uint8 pin = info->GlobalIrq - ioapic->global_interrupt_base; 588 uint32 config = acpi_madt_convert_inti_flags(info->IntiFlags); 589 ioapic_configure_pin(*ioapic, pin, info->GlobalIrq, config, 590 IO_APIC_DELIVERY_MODE_NMI); 591 break; 592 } 593 594 #ifdef TRACE_IOAPIC 595 case ACPI_MADT_TYPE_LOCAL_APIC: 596 { 597 // purely informational 598 acpi_madt_local_apic* info = (acpi_madt_local_apic*)apicEntry; 599 dprintf("found local apic with id %u, processor id %u, " 600 "flags 0x%08" B_PRIx32 "\n", info->Id, info->ProcessorId, 601 (uint32)info->LapicFlags); 602 break; 603 } 604 605 case ACPI_MADT_TYPE_LOCAL_APIC_NMI: 606 { 607 // TODO: take these into account, but at apic.cpp 608 acpi_madt_local_apic_nmi* info 609 = (acpi_madt_local_apic_nmi*)apicEntry; 610 dprintf("found local apic nmi source for processor %u, " 611 "flags 0x%04x, local int %u\n", info->ProcessorId, 612 (uint16)info->IntiFlags, info->Lint); 613 break; 614 } 615 616 case ACPI_MADT_TYPE_LOCAL_APIC_OVERRIDE: 617 { 618 // TODO: take these into account, but at apic.cpp 619 acpi_madt_local_apic_override* info 620 = (acpi_madt_local_apic_override*)apicEntry; 621 dprintf("found local apic override with address 0x%016" B_PRIx64 622 "\n", (uint64)info->Address); 623 break; 624 } 625 626 default: 627 dprintf("found unhandled subtable of type %u length %u\n", 628 apicEntry->Type, apicEntry->Length); 629 break; 630 #endif 631 } 632 633 apicEntry 634 = (acpi_subtable_header*)((uint8*)apicEntry + apicEntry->Length); 635 } 636 } 637 638 639 static status_t 640 acpi_set_interrupt_model(acpi_module_info* acpiModule, uint32 interruptModel) 641 { 642 acpi_object_type model; 643 model.object_type = ACPI_TYPE_INTEGER; 644 model.integer.integer = interruptModel; 645 646 acpi_objects parameter; 647 parameter.count = 1; 648 parameter.pointer = &model; 649 650 dprintf("setting ACPI interrupt model to %s\n", 651 interruptModel == ACPI_INTERRUPT_MODEL_PIC ? "PIC" 652 : (interruptModel == ACPI_INTERRUPT_MODEL_APIC ? "APIC" 653 : (interruptModel == ACPI_INTERRUPT_MODEL_SAPIC ? "SAPIC" 654 : "unknown"))); 655 656 return acpiModule->evaluate_method(NULL, "\\_PIC", ¶meter, NULL); 657 } 658 659 660 bool 661 ioapic_is_interrupt_available(int32 gsi) 662 { 663 struct ioapic* ioapic = find_ioapic(gsi); 664 if (ioapic == NULL) 665 return false; 666 667 uint8 pin = gsi - ioapic->global_interrupt_base; 668 return (ioapic->nmi_mask & ((uint64)1 << pin)) == 0; 669 } 670 671 672 void 673 ioapic_init(kernel_args* args) 674 { 675 static const interrupt_controller ioapicController = { 676 "82093AA IOAPIC", 677 &ioapic_enable_io_interrupt, 678 &ioapic_disable_io_interrupt, 679 &ioapic_configure_io_interrupt, 680 &ioapic_is_spurious_interrupt, 681 &ioapic_is_level_triggered_interrupt, 682 &ioapic_end_of_interrupt, 683 &ioapic_assign_interrupt_to_cpu, 684 }; 685 686 if (args->arch_args.apic == NULL) 687 return; 688 689 if (args->arch_args.ioapic_phys == 0) { 690 dprintf("no io-apics available, not using io-apics for interrupt " 691 "routing\n"); 692 return; 693 } 694 695 if (get_safemode_boolean(B_SAFEMODE_DISABLE_IOAPIC, false)) { 696 dprintf("io-apics explicitly disabled, not using io-apics for " 697 "interrupt routing\n"); 698 return; 699 } 700 701 // load acpi module 702 status_t status; 703 acpi_module_info* acpiModule; 704 status = get_module(B_ACPI_MODULE_NAME, (module_info**)&acpiModule); 705 if (status != B_OK) { 706 dprintf("acpi module not available, not configuring io-apics\n"); 707 return; 708 } 709 BPrivate::CObjectDeleter<const char, status_t, put_module> 710 acpiModulePutter(B_ACPI_MODULE_NAME); 711 712 acpi_table_madt* madt = NULL; 713 if (acpiModule->get_table(ACPI_SIG_MADT, 0, (void**)&madt) != B_OK) { 714 dprintf("failed to get MADT from ACPI, not configuring io-apics\n"); 715 return; 716 } 717 718 status = acpi_enumerate_ioapics(madt); 719 if (status != B_OK) { 720 // We don't treat this case as fatal just yet. If we are able to 721 // route everything with the available IO-APICs we're fine, if not 722 // we will fail at the routing preparation stage further down. 723 dprintf("failed to enumerate all io-apics, working with what we got\n"); 724 } 725 726 // switch to the APIC interrupt model before retrieving the IRQ routing 727 // table as it will return different settings depending on the model 728 status = acpi_set_interrupt_model(acpiModule, ACPI_INTERRUPT_MODEL_APIC); 729 if (status != B_OK) { 730 dprintf("failed to put ACPI into APIC interrupt model, ignoring\n"); 731 // don't abort, as the _PIC method is optional and as long as there 732 // aren't different routings based on it this is non-fatal 733 } 734 735 IRQRoutingTable table; 736 status = prepare_irq_routing(acpiModule, table, 737 &ioapic_is_interrupt_available); 738 if (status != B_OK) { 739 dprintf("IRQ routing preparation failed, not configuring io-apics\n"); 740 acpi_set_interrupt_model(acpiModule, ACPI_INTERRUPT_MODEL_PIC); 741 // revert to PIC interrupt model just in case 742 return; 743 } 744 745 // use the boot CPU as the target for all interrupts 746 uint8 targetAPIC = args->arch_args.cpu_apic_id[0]; 747 748 struct ioapic* current = sIOAPICs; 749 while (current != NULL) { 750 status = ioapic_initialize_ioapic(*current, targetAPIC); 751 if (status != B_OK) { 752 panic("failed to initialize io-apic %u", current->number); 753 acpi_set_interrupt_model(acpiModule, ACPI_INTERRUPT_MODEL_PIC); 754 return; 755 } 756 757 current = current->next; 758 } 759 760 #ifdef TRACE_IOAPIC 761 dprintf("trying interrupt routing:\n"); 762 print_irq_routing_table(table); 763 #endif 764 765 status = enable_irq_routing(acpiModule, table); 766 if (status != B_OK) { 767 panic("failed to enable IRQ routing"); 768 // if it failed early on it might still work in PIC mode 769 acpi_set_interrupt_model(acpiModule, ACPI_INTERRUPT_MODEL_PIC); 770 return; 771 } 772 773 print_irq_routing_table(table); 774 775 // configure the source overrides, but let the PCI config below override it 776 acpi_configure_source_overrides(madt); 777 778 // configure IO-APIC interrupts from PCI routing table 779 for (int i = 0; i < table.Count(); i++) { 780 irq_routing_entry& entry = table.ElementAt(i); 781 ioapic_configure_io_interrupt(entry.irq, 782 entry.polarity | entry.trigger_mode); 783 } 784 785 // kill the local ints on the local APIC 786 apic_disable_local_ints(); 787 // TODO: This uses the assumption that our init is running on the 788 // boot CPU and only the boot CPU has the local ints configured 789 // because it was running in legacy PIC mode. Possibly the other 790 // local APICs of the other CPUs have them configured as well. It 791 // shouldn't really harm, but should eventually be corrected. 792 793 // disable the legacy PIC 794 uint16 legacyInterrupts; 795 pic_disable(legacyInterrupts); 796 797 // enable previsouly enabled legacy interrupts 798 for (uint8 i = 0; i < 16; i++) { 799 if ((legacyInterrupts & (1 << i)) != 0) 800 ioapic_enable_io_interrupt(i); 801 } 802 803 // mark the interrupt vectors reserved so they aren't used for other stuff 804 current = sIOAPICs; 805 while (current != NULL) { 806 reserve_io_interrupt_vectors(current->max_redirection_entry + 1, 807 current->global_interrupt_base, INTERRUPT_TYPE_IRQ); 808 809 for (int32 i = 0; i < current->max_redirection_entry + 1; i++) { 810 x86_set_irq_source(current->global_interrupt_base + i, 811 IRQ_SOURCE_IOAPIC); 812 } 813 814 current = current->next; 815 } 816 817 // prefer the ioapic over the normal pic 818 dprintf("using io-apics for interrupt routing\n"); 819 arch_int_set_interrupt_controller(ioapicController); 820 } 821