2 * Copyright 2008-2011, Michael Lotz, mmlr@mlotz.ch.
3 * Distributed under the terms of the MIT License.
6 #include <arch/x86/ioapic.h>
11 #include "irq_routing_table.h"
14 #include <AutoDeleter.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
28 //#define TRACE_IOAPIC
30 # define TRACE(...) dprintf(__VA_ARGS__)
32 # define TRACE(...) (void)0
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
;
90 volatile uint32 io_window_register
;
98 uint8 max_redirection_entry
;
99 uint8 global_interrupt_base
;
100 uint8 global_interrupt_last
;
101 uint64 level_triggered_mask
;
104 area_id register_area
;
105 ioapic_registers
* registers
;
111 static ioapic
* sIOAPICs
= NULL
;
112 static int32 sSourceOverrides
[ISA_INTERRUPT_COUNT
];
115 // #pragma mark - I/O APIC
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
)
134 struct ioapic
* current
= sIOAPICs
;
135 while (current
!= NULL
) {
136 if (gsi
>= current
->global_interrupt_base
137 && gsi
<= current
->global_interrupt_last
) {
141 current
= current
->next
;
149 ioapic_read_32(struct ioapic
& ioapic
, uint8 registerSelect
)
151 ioapic
.registers
->io_register_select
= registerSelect
;
152 return ioapic
.registers
->io_window_register
;
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
;
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
;
170 ioapic
.registers
->io_register_select
= registerSelect
;
171 result
|= ioapic
.registers
->io_window_register
;
177 ioapic_write_64(struct ioapic
& ioapic
, uint8 registerSelect
, uint64 value
,
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));
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
);
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
;
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);
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
;
227 ioapic_is_level_triggered_interrupt(int32 gsi
)
229 struct ioapic
* ioapic
= find_ioapic(gsi
);
233 uint8 pin
= gsi
- ioapic
->global_interrupt_base
;
234 return (ioapic
->level_triggered_mask
& ((uint64
)1 << pin
)) != 0;
239 ioapic_end_of_interrupt(int32 num
)
241 apic_end_of_interrupt();
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
);
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);
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
);
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);
297 ioapic_disable_io_interrupt(int32 gsi
)
299 struct ioapic
* ioapic
= find_ioapic(gsi
);
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);
314 ioapic_configure_io_interrupt(int32 gsi
, uint32 config
)
316 struct ioapic
* ioapic
= find_ioapic(gsi
);
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
);
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",
348 vm_delete_area(B_SYSTEM_TEAM
, ioapic
.register_area
, true);
349 ioapic
.register_area
= -1;
350 ioapic
.registers
= NULL
;
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
;
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
);
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
;
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);
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
);
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
,
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 "
450 = lastIOAPIC
!= NULL
? lastIOAPIC
->number
+ 1 : 0;
451 ioapic
->apic_id
= info
->Id
;
452 ioapic
->global_interrupt_base
= info
->GlobalIrqBase
;
453 ioapic
->registers
= 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
) {
464 print_ioapic(*ioapic
);
466 if (lastIOAPIC
== NULL
)
469 lastIOAPIC
->next
= ioapic
;
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 "
490 uint8 pin
= info
->GlobalIrq
- ioapic
->global_interrupt_base
;
491 ioapic
->nmi_mask
|= (uint64
)1 << pin
;
497 = (acpi_subtable_header
*)((uint8
*)apicEntry
+ apicEntry
->Length
);
505 acpi_madt_convert_inti_flags(uint16 flags
)
508 switch (flags
& ACPI_MADT_POLARITY_MASK
) {
509 case ACPI_MADT_POLARITY_ACTIVE_LOW
:
510 config
= B_LOW_ACTIVE_POLARITY
;
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
;
521 switch (flags
& ACPI_MADT_TRIGGER_MASK
) {
522 case ACPI_MADT_TRIGGER_LEVEL
:
523 config
|= B_LEVEL_TRIGGERED
;
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
;
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");
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
);
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
);
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
);
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
);
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
);
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
);
627 dprintf("found unhandled subtable of type %u length %u\n",
628 apicEntry
->Type
, apicEntry
->Length
);
634 = (acpi_subtable_header
*)((uint8
*)apicEntry
+ apicEntry
->Length
);
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
;
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"
656 return acpiModule
->evaluate_method(NULL
, "\\_PIC", ¶meter
, NULL
);
661 ioapic_is_interrupt_available(int32 gsi
)
663 struct ioapic
* ioapic
= find_ioapic(gsi
);
667 uint8 pin
= gsi
- ioapic
->global_interrupt_base
;
668 return (ioapic
->nmi_mask
& ((uint64
)1 << pin
)) == 0;
673 ioapic_init(kernel_args
* args
)
675 static const interrupt_controller ioapicController
= {
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
)
689 if (args
->arch_args
.ioapic_phys
== 0) {
690 dprintf("no io-apics available, not using io-apics for interrupt "
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");
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");
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");
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
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
);
757 current
= current
->next
;
761 dprintf("trying interrupt routing:\n");
762 print_irq_routing_table(table
);
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
);
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
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
,
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
);