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