headers/bsd: Add sys/queue.h.
[haiku.git] / src / system / kernel / arch / x86 / ioapic.cpp
blob45212cfca48227c8f750340b66300806c7cf5bef
1 /*
2 * Copyright 2008-2011, Michael Lotz, mmlr@mlotz.ch.
3 * Distributed under the terms of the MIT License.
4 */
6 #include <arch/x86/ioapic.h>
8 #include <int.h>
9 #include <vm/vm.h>
11 #include "irq_routing_table.h"
13 #include <ACPI.h>
14 #include <AutoDeleter.h>
15 #include <safemode.h>
16 #include <string.h>
17 #include <stdio.h>
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>
24 // to gain access to the ACPICA types
25 #include "acpi.h"
28 //#define TRACE_IOAPIC
29 #ifdef TRACE_IOAPIC
30 # define TRACE(...) dprintf(__VA_ARGS__)
31 #else
32 # define TRACE(...) (void)0
33 #endif
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
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
48 // Fields for the id register
49 #define IO_APIC_ID_SHIFT 24
50 #define IO_APIC_ID_MASK 0xff
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
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
83 #define MAX_SUPPORTED_REDIRECTION_ENTRIES 64
84 #define ISA_INTERRUPT_COUNT 16
87 struct ioapic_registers {
88 volatile uint32 io_register_select;
89 uint32 reserved[3];
90 volatile uint32 io_window_register;
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;
104 area_id register_area;
105 ioapic_registers* registers;
107 ioapic* next;
111 static ioapic* sIOAPICs = NULL;
112 static int32 sSourceOverrides[ISA_INTERRUPT_COUNT];
115 // #pragma mark - I/O APIC
118 static void
119 print_ioapic(struct ioapic& ioapic)
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);
128 static inline struct ioapic*
129 find_ioapic(int32 gsi)
131 if (gsi < 0)
132 return NULL;
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;
141 current = current->next;
144 return NULL;
148 static inline uint32
149 ioapic_read_32(struct ioapic& ioapic, uint8 registerSelect)
151 ioapic.registers->io_register_select = registerSelect;
152 return ioapic.registers->io_window_register;
156 static inline void
157 ioapic_write_32(struct ioapic& ioapic, uint8 registerSelect, uint32 value)
159 ioapic.registers->io_register_select = registerSelect;
160 ioapic.registers->io_window_register = value;
164 static inline uint64
165 ioapic_read_64(struct ioapic& ioapic, uint8 registerSelect)
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;
176 static inline void
177 ioapic_write_64(struct ioapic& ioapic, uint8 registerSelect, uint64 value,
178 bool maskFirst)
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));
191 static void
192 ioapic_configure_pin(struct ioapic& ioapic, uint8 pin, uint8 vector,
193 uint8 triggerPolarity, uint16 deliveryMode)
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);
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);
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;
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);
218 static bool
219 ioapic_is_spurious_interrupt(int32 gsi)
221 // the spurious interrupt vector is initialized to the max value in smp
222 return gsi == 0xff - ARCH_INTERRUPT_BASE;
226 static bool
227 ioapic_is_level_triggered_interrupt(int32 gsi)
229 struct ioapic* ioapic = find_ioapic(gsi);
230 if (ioapic == NULL)
231 return false;
233 uint8 pin = gsi - ioapic->global_interrupt_base;
234 return (ioapic->level_triggered_mask & ((uint64)1 << pin)) != 0;
238 static bool
239 ioapic_end_of_interrupt(int32 num)
241 apic_end_of_interrupt();
242 return true;
246 static void
247 ioapic_assign_interrupt_to_cpu(int32 gsi, int32 cpu)
249 if (gsi < ISA_INTERRUPT_COUNT && sSourceOverrides[gsi] != 0)
250 gsi = sSourceOverrides[gsi];
252 struct ioapic* ioapic = find_ioapic(gsi);
253 if (ioapic == NULL)
254 return;
256 uint32 apicid = x86_get_cpu_apic_id(cpu);
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);
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);
271 static void
272 ioapic_enable_io_interrupt(int32 gsi)
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];
280 struct ioapic* ioapic = find_ioapic(gsi);
281 if (ioapic == NULL)
282 return;
284 x86_set_irq_source(gsi, IRQ_SOURCE_IOAPIC);
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);
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);
296 static void
297 ioapic_disable_io_interrupt(int32 gsi)
299 struct ioapic* ioapic = find_ioapic(gsi);
300 if (ioapic == NULL)
301 return;
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);
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);
313 static void
314 ioapic_configure_io_interrupt(int32 gsi, uint32 config)
316 struct ioapic* ioapic = find_ioapic(gsi);
317 if (ioapic == NULL)
318 return;
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);
325 ioapic_configure_pin(*ioapic, pin, gsi, config,
326 IO_APIC_DELIVERY_MODE_FIXED);
330 static status_t
331 ioapic_map_ioapic(struct ioapic& ioapic, phys_addr_t physicalAddress)
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;
342 TRACE("mapped io-apic %u to %p\n", ioapic.number, ioapic.registers);
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;
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;
364 ioapic.global_interrupt_last
365 = ioapic.global_interrupt_base + ioapic.max_redirection_entry;
367 ioapic.nmi_mask = 0;
369 return B_OK;
373 static status_t
374 ioapic_initialize_ioapic(struct ioapic& ioapic, uint8 targetAPIC)
376 // program the APIC ID
377 ioapic_write_32(ioapic, IO_APIC_ID, ioapic.apic_id << IO_APIC_ID_SHIFT);
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);
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);
407 ioapic_write_64(ioapic, IO_APIC_REDIRECTION_TABLE + 2 * i, entry, true);
410 return B_OK;
414 static int32
415 ioapic_source_override_handler(void* data)
417 int32 vector = (addr_t)data;
418 bool levelTriggered = ioapic_is_level_triggered_interrupt(vector);
419 return int_io_interrupt_handler(vector, levelTriggered);
423 static status_t
424 acpi_enumerate_ioapics(acpi_table_madt* madt)
426 struct ioapic* lastIOAPIC = sIOAPICs;
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:
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);
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;
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;
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;
464 print_ioapic(*ioapic);
466 if (lastIOAPIC == NULL)
467 sIOAPICs = ioapic;
468 else
469 lastIOAPIC->next = ioapic;
471 lastIOAPIC = ioapic;
472 break;
475 case ACPI_MADT_TYPE_NMI_SOURCE:
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);
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;
490 uint8 pin = info->GlobalIrq - ioapic->global_interrupt_base;
491 ioapic->nmi_mask |= (uint64)1 << pin;
492 break;
496 apicEntry
497 = (acpi_subtable_header*)((uint8*)apicEntry + apicEntry->Length);
500 return B_OK;
504 static inline uint32
505 acpi_madt_convert_inti_flags(uint16 flags)
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;
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;
534 return config;
538 static void
539 acpi_configure_source_overrides(acpi_table_madt* madt)
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:
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);
555 if (info->SourceIrq >= ISA_INTERRUPT_COUNT) {
556 dprintf("source override exceeds isa interrupt count\n");
557 break;
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);
566 sSourceOverrides[info->SourceIrq] = info->GlobalIrq;
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;
575 case ACPI_MADT_TYPE_NMI_SOURCE:
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);
583 struct ioapic* ioapic = find_ioapic(info->GlobalIrq);
584 if (ioapic == NULL)
585 break;
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;
594 #ifdef TRACE_IOAPIC
595 case ACPI_MADT_TYPE_LOCAL_APIC:
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;
605 case ACPI_MADT_TYPE_LOCAL_APIC_NMI:
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;
616 case ACPI_MADT_TYPE_LOCAL_APIC_OVERRIDE:
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;
626 default:
627 dprintf("found unhandled subtable of type %u length %u\n",
628 apicEntry->Type, apicEntry->Length);
629 break;
630 #endif
633 apicEntry
634 = (acpi_subtable_header*)((uint8*)apicEntry + apicEntry->Length);
639 static status_t
640 acpi_set_interrupt_model(acpi_module_info* acpiModule, uint32 interruptModel)
642 acpi_object_type model;
643 model.object_type = ACPI_TYPE_INTEGER;
644 model.integer.integer = interruptModel;
646 acpi_objects parameter;
647 parameter.count = 1;
648 parameter.pointer = &model;
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")));
656 return acpiModule->evaluate_method(NULL, "\\_PIC", &parameter, NULL);
660 bool
661 ioapic_is_interrupt_available(int32 gsi)
663 struct ioapic* ioapic = find_ioapic(gsi);
664 if (ioapic == NULL)
665 return false;
667 uint8 pin = gsi - ioapic->global_interrupt_base;
668 return (ioapic->nmi_mask & ((uint64)1 << pin)) == 0;
672 void
673 ioapic_init(kernel_args* args)
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,
686 if (args->arch_args.apic == NULL)
687 return;
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;
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;
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;
709 BPrivate::CObjectDeleter<const char, status_t>
710 acpiModulePutter(B_ACPI_MODULE_NAME, put_module);
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;
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");
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
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;
745 // use the boot CPU as the target for all interrupts
746 uint8 targetAPIC = args->arch_args.cpu_apic_id[0];
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;
757 current = current->next;
760 #ifdef TRACE_IOAPIC
761 dprintf("trying interrupt routing:\n");
762 print_irq_routing_table(table);
763 #endif
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;
773 print_irq_routing_table(table);
775 // configure the source overrides, but let the PCI config below override it
776 acpi_configure_source_overrides(madt);
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);
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.
793 // disable the legacy PIC
794 uint16 legacyInterrupts;
795 pic_disable(legacyInterrupts);
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);
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);
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);
814 current = current->next;
817 // prefer the ioapic over the normal pic
818 dprintf("using io-apics for interrupt routing\n");
819 arch_int_set_interrupt_controller(ioapicController);