1dc14d97bSMichael Lotz /*
2dc14d97bSMichael Lotz * Copyright 2008-2011, Michael Lotz, mmlr@mlotz.ch.
3dc14d97bSMichael Lotz * Distributed under the terms of the MIT License.
4dc14d97bSMichael Lotz */
5dc14d97bSMichael Lotz
6dc14d97bSMichael Lotz #include <arch/x86/ioapic.h>
7dc14d97bSMichael Lotz
8dc14d97bSMichael Lotz #include <int.h>
9dc14d97bSMichael Lotz #include <vm/vm.h>
10dc14d97bSMichael Lotz
11fe2bcea7Smilek7 #include "acpi_irq_routing_table.h"
12dc14d97bSMichael Lotz
13dc14d97bSMichael Lotz #include <ACPI.h>
14dc14d97bSMichael Lotz #include <AutoDeleter.h>
15dc14d97bSMichael Lotz #include <safemode.h>
16dc14d97bSMichael Lotz #include <string.h>
17dc14d97bSMichael Lotz #include <stdio.h>
18dc14d97bSMichael Lotz
19dc14d97bSMichael Lotz #include <arch/x86/apic.h>
20dc14d97bSMichael Lotz #include <arch/x86/arch_int.h>
21d897a478SPawel Dziepak #include <arch/x86/arch_smp.h>
22dc14d97bSMichael Lotz #include <arch/x86/pic.h>
23dc14d97bSMichael Lotz
24d11e32a8SMichael Lotz // to gain access to the ACPICA types
25d11e32a8SMichael Lotz #include "acpi.h"
26d11e32a8SMichael Lotz
27dc14d97bSMichael Lotz
28dc14d97bSMichael Lotz //#define TRACE_IOAPIC
29dc14d97bSMichael Lotz #ifdef TRACE_IOAPIC
30d897a478SPawel Dziepak # define TRACE(...) dprintf(__VA_ARGS__)
31dc14d97bSMichael Lotz #else
32d897a478SPawel Dziepak # define TRACE(...) (void)0
33dc14d97bSMichael Lotz #endif
34dc14d97bSMichael Lotz
35dc14d97bSMichael Lotz
36dc14d97bSMichael Lotz // ACPI interrupt models
37dc14d97bSMichael Lotz #define ACPI_INTERRUPT_MODEL_PIC 0
38dc14d97bSMichael Lotz #define ACPI_INTERRUPT_MODEL_APIC 1
39dc14d97bSMichael Lotz #define ACPI_INTERRUPT_MODEL_SAPIC 2
40dc14d97bSMichael Lotz
41dc14d97bSMichael Lotz
42dc14d97bSMichael Lotz // Definitions for a 82093AA IO APIC controller
430f91697fSMichael Lotz #define IO_APIC_ID 0x00
44dc14d97bSMichael Lotz #define IO_APIC_VERSION 0x01
45dc14d97bSMichael Lotz #define IO_APIC_ARBITRATION 0x02
46dc14d97bSMichael Lotz #define IO_APIC_REDIRECTION_TABLE 0x10 // entry = base + 2 * index
47dc14d97bSMichael Lotz
480f91697fSMichael Lotz // Fields for the id register
490f91697fSMichael Lotz #define IO_APIC_ID_SHIFT 24
5063475256SMichael Lotz #define IO_APIC_ID_MASK 0xff
510f91697fSMichael Lotz
52dc14d97bSMichael Lotz // Fields for the version register
53dc14d97bSMichael Lotz #define IO_APIC_VERSION_SHIFT 0
54dc14d97bSMichael Lotz #define IO_APIC_VERSION_MASK 0xff
55dc14d97bSMichael Lotz #define IO_APIC_MAX_REDIRECTION_ENTRY_SHIFT 16
56dc14d97bSMichael Lotz #define IO_APIC_MAX_REDIRECTION_ENTRY_MASK 0xff
57dc14d97bSMichael Lotz
58dc14d97bSMichael Lotz // Fields of each redirection table entry
59dc14d97bSMichael Lotz #define IO_APIC_DESTINATION_FIELD_SHIFT 56
60f91cbddeSMichael Lotz #define IO_APIC_DESTINATION_FIELD_MASK 0xff
6174e9ede3SMichael Lotz #define IO_APIC_INTERRUPT_MASKED (1 << 16)
623108c9beSMichael Lotz #define IO_APIC_TRIGGER_MODE_EDGE (0 << 15)
6374e9ede3SMichael Lotz #define IO_APIC_TRIGGER_MODE_LEVEL (1 << 15)
6474e9ede3SMichael Lotz #define IO_APIC_TRIGGER_MODE_MASK (1 << 15)
6574e9ede3SMichael Lotz #define IO_APIC_REMOTE_IRR (1 << 14)
6674e9ede3SMichael Lotz #define IO_APIC_PIN_POLARITY_HIGH_ACTIVE (0 << 13)
6774e9ede3SMichael Lotz #define IO_APIC_PIN_POLARITY_LOW_ACTIVE (1 << 13)
6874e9ede3SMichael Lotz #define IO_APIC_PIN_POLARITY_MASK (1 << 13)
6974e9ede3SMichael Lotz #define IO_APIC_DELIVERY_STATUS_PENDING (1 << 12)
7074e9ede3SMichael Lotz #define IO_APIC_DESTINATION_MODE_PHYSICAL (0 << 11)
7174e9ede3SMichael Lotz #define IO_APIC_DESTINATION_MODE_LOGICAL (1 << 11)
7274e9ede3SMichael Lotz #define IO_APIC_DESTINATION_MODE_MASK (1 << 11)
7374e9ede3SMichael Lotz #define IO_APIC_DELIVERY_MODE_MASK (7 << 8)
7474e9ede3SMichael Lotz #define IO_APIC_DELIVERY_MODE_FIXED (0 << 8)
7574e9ede3SMichael Lotz #define IO_APIC_DELIVERY_MODE_LOWEST_PRIO (1 << 8)
7674e9ede3SMichael Lotz #define IO_APIC_DELIVERY_MODE_SMI (2 << 8)
7774e9ede3SMichael Lotz #define IO_APIC_DELIVERY_MODE_NMI (4 << 8)
7874e9ede3SMichael Lotz #define IO_APIC_DELIVERY_MODE_INIT (5 << 8)
7974e9ede3SMichael Lotz #define IO_APIC_DELIVERY_MODE_EXT_INT (7 << 8)
80dc14d97bSMichael Lotz #define IO_APIC_INTERRUPT_VECTOR_SHIFT 0
81dc14d97bSMichael Lotz #define IO_APIC_INTERRUPT_VECTOR_MASK 0xff
82dc14d97bSMichael Lotz
8322d72c8eSMichael Lotz #define MAX_SUPPORTED_REDIRECTION_ENTRIES 64
84fb5a1727SMichael Lotz #define ISA_INTERRUPT_COUNT 16
85fb5a1727SMichael Lotz
86dc14d97bSMichael Lotz
875d01e61aSMichael Lotz struct ioapic_registers {
88dc14d97bSMichael Lotz volatile uint32 io_register_select;
89dc14d97bSMichael Lotz uint32 reserved[3];
90dc14d97bSMichael Lotz volatile uint32 io_window_register;
915d01e61aSMichael Lotz };
92dc14d97bSMichael Lotz
93dc14d97bSMichael Lotz
945d01e61aSMichael Lotz struct ioapic {
955d01e61aSMichael Lotz uint8 number;
9663475256SMichael Lotz uint8 apic_id;
9763475256SMichael Lotz uint32 version;
985d01e61aSMichael Lotz uint8 max_redirection_entry;
995d01e61aSMichael Lotz uint8 global_interrupt_base;
1005d01e61aSMichael Lotz uint8 global_interrupt_last;
1015d01e61aSMichael Lotz uint64 level_triggered_mask;
10286d59c04SMichael Lotz uint64 nmi_mask;
1035d01e61aSMichael Lotz
1045d01e61aSMichael Lotz area_id register_area;
1055d01e61aSMichael Lotz ioapic_registers* registers;
1065d01e61aSMichael Lotz
1075d01e61aSMichael Lotz ioapic* next;
1085d01e61aSMichael Lotz };
1095d01e61aSMichael Lotz
1105d01e61aSMichael Lotz
111e223e8e9SAugustin Cavalier static int32 sIOAPICPhys = 0;
1128908aef9SMichael Lotz static ioapic* sIOAPICs = NULL;
113fb5a1727SMichael Lotz static int32 sSourceOverrides[ISA_INTERRUPT_COUNT];
114dc14d97bSMichael Lotz
115dc14d97bSMichael Lotz
116dc14d97bSMichael Lotz // #pragma mark - I/O APIC
117dc14d97bSMichael Lotz
118dc14d97bSMichael Lotz
11963475256SMichael Lotz static void
print_ioapic(struct ioapic & ioapic)12063475256SMichael Lotz print_ioapic(struct ioapic& ioapic)
12163475256SMichael Lotz {
122fa6327c9SAlex Smith dprintf("io-apic %u has range %u-%u, %u entries, version 0x%08" B_PRIx32
123fa6327c9SAlex Smith ", apic-id %u\n", ioapic.number, ioapic.global_interrupt_base,
12463475256SMichael Lotz ioapic.global_interrupt_last, ioapic.max_redirection_entry + 1,
12563475256SMichael Lotz ioapic.version, ioapic.apic_id);
12663475256SMichael Lotz }
12763475256SMichael Lotz
12863475256SMichael Lotz
1295d01e61aSMichael Lotz static inline struct ioapic*
find_ioapic(int32 gsi)1305d01e61aSMichael Lotz find_ioapic(int32 gsi)
131dc14d97bSMichael Lotz {
1325d01e61aSMichael Lotz if (gsi < 0)
1335d01e61aSMichael Lotz return NULL;
1345d01e61aSMichael Lotz
1358908aef9SMichael Lotz struct ioapic* current = sIOAPICs;
1365d01e61aSMichael Lotz while (current != NULL) {
1375d01e61aSMichael Lotz if (gsi >= current->global_interrupt_base
1385d01e61aSMichael Lotz && gsi <= current->global_interrupt_last) {
1395d01e61aSMichael Lotz return current;
1405d01e61aSMichael Lotz }
1415d01e61aSMichael Lotz
1425d01e61aSMichael Lotz current = current->next;
1435d01e61aSMichael Lotz }
1445d01e61aSMichael Lotz
1455d01e61aSMichael Lotz return NULL;
1465d01e61aSMichael Lotz }
1475d01e61aSMichael Lotz
1485d01e61aSMichael Lotz
1495d01e61aSMichael Lotz static inline uint32
ioapic_read_32(struct ioapic & ioapic,uint8 registerSelect)1505d01e61aSMichael Lotz ioapic_read_32(struct ioapic& ioapic, uint8 registerSelect)
1515d01e61aSMichael Lotz {
1525d01e61aSMichael Lotz ioapic.registers->io_register_select = registerSelect;
1535d01e61aSMichael Lotz return ioapic.registers->io_window_register;
154dc14d97bSMichael Lotz }
155dc14d97bSMichael Lotz
156dc14d97bSMichael Lotz
157dc14d97bSMichael Lotz static inline void
ioapic_write_32(struct ioapic & ioapic,uint8 registerSelect,uint32 value)1585d01e61aSMichael Lotz ioapic_write_32(struct ioapic& ioapic, uint8 registerSelect, uint32 value)
159dc14d97bSMichael Lotz {
1605d01e61aSMichael Lotz ioapic.registers->io_register_select = registerSelect;
1615d01e61aSMichael Lotz ioapic.registers->io_window_register = value;
162dc14d97bSMichael Lotz }
163dc14d97bSMichael Lotz
164dc14d97bSMichael Lotz
165dc14d97bSMichael Lotz static inline uint64
ioapic_read_64(struct ioapic & ioapic,uint8 registerSelect)1665d01e61aSMichael Lotz ioapic_read_64(struct ioapic& ioapic, uint8 registerSelect)
167dc14d97bSMichael Lotz {
1685d01e61aSMichael Lotz ioapic.registers->io_register_select = registerSelect + 1;
1695d01e61aSMichael Lotz uint64 result = ioapic.registers->io_window_register;
170dc14d97bSMichael Lotz result <<= 32;
1715d01e61aSMichael Lotz ioapic.registers->io_register_select = registerSelect;
1725d01e61aSMichael Lotz result |= ioapic.registers->io_window_register;
173dc14d97bSMichael Lotz return result;
174dc14d97bSMichael Lotz }
175dc14d97bSMichael Lotz
176dc14d97bSMichael Lotz
177dc14d97bSMichael Lotz static inline void
ioapic_write_64(struct ioapic & ioapic,uint8 registerSelect,uint64 value,bool maskFirst)1780798779aSMichael Lotz ioapic_write_64(struct ioapic& ioapic, uint8 registerSelect, uint64 value,
1790798779aSMichael Lotz bool maskFirst)
180dc14d97bSMichael Lotz {
1810798779aSMichael Lotz ioapic.registers->io_register_select
1820798779aSMichael Lotz = registerSelect + (maskFirst ? 0 : 1);
1830798779aSMichael Lotz ioapic.registers->io_window_register
1840798779aSMichael Lotz = (uint32)(value >> (maskFirst ? 0 : 32));
1850798779aSMichael Lotz ioapic.registers->io_register_select
1860798779aSMichael Lotz = registerSelect + (maskFirst ? 1 : 0);
1870798779aSMichael Lotz ioapic.registers->io_window_register
1880798779aSMichael Lotz = (uint32)(value >> (maskFirst ? 32 : 0));
189dc14d97bSMichael Lotz }
190dc14d97bSMichael Lotz
191dc14d97bSMichael Lotz
1929ae3fdcbSMichael Lotz static void
ioapic_configure_pin(struct ioapic & ioapic,uint8 pin,uint8 vector,uint8 triggerPolarity,uint16 deliveryMode)1939ae3fdcbSMichael Lotz ioapic_configure_pin(struct ioapic& ioapic, uint8 pin, uint8 vector,
19474e9ede3SMichael Lotz uint8 triggerPolarity, uint16 deliveryMode)
1959ae3fdcbSMichael Lotz {
1969ae3fdcbSMichael Lotz uint64 entry = ioapic_read_64(ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2);
19774e9ede3SMichael Lotz entry &= ~(IO_APIC_TRIGGER_MODE_MASK | IO_APIC_PIN_POLARITY_MASK
19874e9ede3SMichael Lotz | IO_APIC_INTERRUPT_VECTOR_MASK | IO_APIC_DELIVERY_MODE_MASK);
1999ae3fdcbSMichael Lotz
2009ae3fdcbSMichael Lotz if (triggerPolarity & B_LEVEL_TRIGGERED) {
20174e9ede3SMichael Lotz entry |= IO_APIC_TRIGGER_MODE_LEVEL;
2029ae3fdcbSMichael Lotz ioapic.level_triggered_mask |= ((uint64)1 << pin);
2039ae3fdcbSMichael Lotz } else {
20474e9ede3SMichael Lotz entry |= IO_APIC_TRIGGER_MODE_EDGE;
2059ae3fdcbSMichael Lotz ioapic.level_triggered_mask &= ~((uint64)1 << pin);
2069ae3fdcbSMichael Lotz }
2079ae3fdcbSMichael Lotz
2089ae3fdcbSMichael Lotz if (triggerPolarity & B_LOW_ACTIVE_POLARITY)
20974e9ede3SMichael Lotz entry |= IO_APIC_PIN_POLARITY_LOW_ACTIVE;
2109ae3fdcbSMichael Lotz else
21174e9ede3SMichael Lotz entry |= IO_APIC_PIN_POLARITY_HIGH_ACTIVE;
2129ae3fdcbSMichael Lotz
21374e9ede3SMichael Lotz entry |= deliveryMode;
2149ae3fdcbSMichael Lotz entry |= (vector + ARCH_INTERRUPT_BASE) << IO_APIC_INTERRUPT_VECTOR_SHIFT;
2159ae3fdcbSMichael Lotz ioapic_write_64(ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2, entry, true);
2169ae3fdcbSMichael Lotz }
2179ae3fdcbSMichael Lotz
2189ae3fdcbSMichael Lotz
219dc14d97bSMichael Lotz static bool
ioapic_is_spurious_interrupt(int32 gsi)220eda74390SMichael Lotz ioapic_is_spurious_interrupt(int32 gsi)
221dc14d97bSMichael Lotz {
222dc14d97bSMichael Lotz // the spurious interrupt vector is initialized to the max value in smp
223eda74390SMichael Lotz return gsi == 0xff - ARCH_INTERRUPT_BASE;
224dc14d97bSMichael Lotz }
225dc14d97bSMichael Lotz
226dc14d97bSMichael Lotz
227dc14d97bSMichael Lotz static bool
ioapic_is_level_triggered_interrupt(int32 gsi)2285d01e61aSMichael Lotz ioapic_is_level_triggered_interrupt(int32 gsi)
229dc14d97bSMichael Lotz {
2305d01e61aSMichael Lotz struct ioapic* ioapic = find_ioapic(gsi);
2315d01e61aSMichael Lotz if (ioapic == NULL)
232dc14d97bSMichael Lotz return false;
233dc14d97bSMichael Lotz
2345d01e61aSMichael Lotz uint8 pin = gsi - ioapic->global_interrupt_base;
235f91cbddeSMichael Lotz return (ioapic->level_triggered_mask & ((uint64)1 << pin)) != 0;
236dc14d97bSMichael Lotz }
237dc14d97bSMichael Lotz
238dc14d97bSMichael Lotz
239dc14d97bSMichael Lotz static bool
ioapic_end_of_interrupt(int32 num)240dc14d97bSMichael Lotz ioapic_end_of_interrupt(int32 num)
241dc14d97bSMichael Lotz {
242dc14d97bSMichael Lotz apic_end_of_interrupt();
243dc14d97bSMichael Lotz return true;
244dc14d97bSMichael Lotz }
245dc14d97bSMichael Lotz
246dc14d97bSMichael Lotz
247dc14d97bSMichael Lotz static void
ioapic_assign_interrupt_to_cpu(int32 gsi,int32 cpu)248d897a478SPawel Dziepak ioapic_assign_interrupt_to_cpu(int32 gsi, int32 cpu)
249d897a478SPawel Dziepak {
250d897a478SPawel Dziepak if (gsi < ISA_INTERRUPT_COUNT && sSourceOverrides[gsi] != 0)
251d897a478SPawel Dziepak gsi = sSourceOverrides[gsi];
252d897a478SPawel Dziepak
253d897a478SPawel Dziepak struct ioapic* ioapic = find_ioapic(gsi);
254d897a478SPawel Dziepak if (ioapic == NULL)
255d897a478SPawel Dziepak return;
256d897a478SPawel Dziepak
257d897a478SPawel Dziepak uint32 apicid = x86_get_cpu_apic_id(cpu);
258d897a478SPawel Dziepak
259d897a478SPawel Dziepak uint8 pin = gsi - ioapic->global_interrupt_base;
2603108c9beSMichael Lotz TRACE("ioapic_assign_interrupt_to_cpu: gsi %" B_PRId32
2613108c9beSMichael Lotz " (io-apic %u pin %u) to cpu %" B_PRId32 " (apic_id %" B_PRIx32 ")\n",
2623108c9beSMichael Lotz gsi, ioapic->number, pin, cpu, apicid);
263d897a478SPawel Dziepak
264d897a478SPawel Dziepak uint64 entry = ioapic_read_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2);
265d897a478SPawel Dziepak entry &= ~(uint64(IO_APIC_DESTINATION_FIELD_MASK)
266d897a478SPawel Dziepak << IO_APIC_DESTINATION_FIELD_SHIFT);
267d897a478SPawel Dziepak entry |= uint64(apicid) << IO_APIC_DESTINATION_FIELD_SHIFT;
268d897a478SPawel Dziepak ioapic_write_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2, entry, false);
269d897a478SPawel Dziepak }
270d897a478SPawel Dziepak
271d897a478SPawel Dziepak
272d897a478SPawel Dziepak static void
ioapic_enable_io_interrupt(int32 gsi)2735d01e61aSMichael Lotz ioapic_enable_io_interrupt(int32 gsi)
274dc14d97bSMichael Lotz {
275fb5a1727SMichael Lotz // If enabling an overriden source is attempted, enable the override entry
276cb4e75d3SMichael Lotz // instead. An interrupt handler was installed at the override GSI to relay
277fb5a1727SMichael Lotz // interrupts to the overriden source.
278fb5a1727SMichael Lotz if (gsi < ISA_INTERRUPT_COUNT && sSourceOverrides[gsi] != 0)
279fb5a1727SMichael Lotz gsi = sSourceOverrides[gsi];
280fb5a1727SMichael Lotz
2815d01e61aSMichael Lotz struct ioapic* ioapic = find_ioapic(gsi);
2825d01e61aSMichael Lotz if (ioapic == NULL)
283dc14d97bSMichael Lotz return;
284dc14d97bSMichael Lotz
28566395144SPawel Dziepak x86_set_irq_source(gsi, IRQ_SOURCE_IOAPIC);
28666395144SPawel Dziepak
2875d01e61aSMichael Lotz uint8 pin = gsi - ioapic->global_interrupt_base;
2883108c9beSMichael Lotz TRACE("ioapic_enable_io_interrupt: gsi %" B_PRId32
2893108c9beSMichael Lotz " -> io-apic %u pin %u\n", gsi, ioapic->number, pin);
290dc14d97bSMichael Lotz
2915d01e61aSMichael Lotz uint64 entry = ioapic_read_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2);
29274e9ede3SMichael Lotz entry &= ~IO_APIC_INTERRUPT_MASKED;
2930798779aSMichael Lotz ioapic_write_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2, entry, false);
294dc14d97bSMichael Lotz }
295dc14d97bSMichael Lotz
296dc14d97bSMichael Lotz
297dc14d97bSMichael Lotz static void
ioapic_disable_io_interrupt(int32 gsi)2985d01e61aSMichael Lotz ioapic_disable_io_interrupt(int32 gsi)
299dc14d97bSMichael Lotz {
3005d01e61aSMichael Lotz struct ioapic* ioapic = find_ioapic(gsi);
3015d01e61aSMichael Lotz if (ioapic == NULL)
302dc14d97bSMichael Lotz return;
303dc14d97bSMichael Lotz
3045d01e61aSMichael Lotz uint8 pin = gsi - ioapic->global_interrupt_base;
3053108c9beSMichael Lotz TRACE("ioapic_disable_io_interrupt: gsi %" B_PRId32
3063108c9beSMichael Lotz " -> io-apic %u pin %u\n", gsi, ioapic->number, pin);
307dc14d97bSMichael Lotz
3085d01e61aSMichael Lotz uint64 entry = ioapic_read_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2);
30974e9ede3SMichael Lotz entry |= IO_APIC_INTERRUPT_MASKED;
3100798779aSMichael Lotz ioapic_write_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2, entry, true);
311dc14d97bSMichael Lotz }
312dc14d97bSMichael Lotz
313dc14d97bSMichael Lotz
314dc14d97bSMichael Lotz static void
ioapic_configure_io_interrupt(int32 gsi,uint32 config)3155d01e61aSMichael Lotz ioapic_configure_io_interrupt(int32 gsi, uint32 config)
316dc14d97bSMichael Lotz {
3175d01e61aSMichael Lotz struct ioapic* ioapic = find_ioapic(gsi);
3185d01e61aSMichael Lotz if (ioapic == NULL)
319dc14d97bSMichael Lotz return;
320dc14d97bSMichael Lotz
3215d01e61aSMichael Lotz uint8 pin = gsi - ioapic->global_interrupt_base;
3223108c9beSMichael Lotz TRACE("ioapic_configure_io_interrupt: gsi %" B_PRId32
3233108c9beSMichael Lotz " -> io-apic %u pin %u; config 0x%08" B_PRIx32 "\n", gsi,
3243108c9beSMichael Lotz ioapic->number, pin, config);
325dc14d97bSMichael Lotz
3269ae3fdcbSMichael Lotz ioapic_configure_pin(*ioapic, pin, gsi, config,
3279ae3fdcbSMichael Lotz IO_APIC_DELIVERY_MODE_FIXED);
3285d01e61aSMichael Lotz }
3295d01e61aSMichael Lotz
3305d01e61aSMichael Lotz
3315d01e61aSMichael Lotz static status_t
ioapic_map_ioapic(struct ioapic & ioapic,phys_addr_t physicalAddress)3325d01e61aSMichael Lotz ioapic_map_ioapic(struct ioapic& ioapic, phys_addr_t physicalAddress)
3335d01e61aSMichael Lotz {
3345d01e61aSMichael Lotz ioapic.register_area = vm_map_physical_memory(B_SYSTEM_TEAM, "io-apic",
3355d01e61aSMichael Lotz (void**)&ioapic.registers, ioapic.registers != NULL ? B_EXACT_ADDRESS
3365d01e61aSMichael Lotz : B_ANY_KERNEL_ADDRESS, B_PAGE_SIZE, B_KERNEL_READ_AREA
337d11e32a8SMichael Lotz | B_KERNEL_WRITE_AREA, physicalAddress, ioapic.registers != NULL);
3385d01e61aSMichael Lotz if (ioapic.register_area < 0) {
3395d01e61aSMichael Lotz panic("mapping io-apic %u failed", ioapic.number);
3405d01e61aSMichael Lotz return ioapic.register_area;
3415d01e61aSMichael Lotz }
3425d01e61aSMichael Lotz
343d897a478SPawel Dziepak TRACE("mapped io-apic %u to %p\n", ioapic.number, ioapic.registers);
344d11e32a8SMichael Lotz
34563475256SMichael Lotz ioapic.version = ioapic_read_32(ioapic, IO_APIC_VERSION);
34663475256SMichael Lotz if (ioapic.version == 0xffffffff) {
3475d01e61aSMichael Lotz dprintf("io-apic %u seems inaccessible, not using it\n",
3485d01e61aSMichael Lotz ioapic.number);
3495d01e61aSMichael Lotz vm_delete_area(B_SYSTEM_TEAM, ioapic.register_area, true);
3505d01e61aSMichael Lotz ioapic.register_area = -1;
3515d01e61aSMichael Lotz ioapic.registers = NULL;
3525d01e61aSMichael Lotz return B_ERROR;
3535d01e61aSMichael Lotz }
3545d01e61aSMichael Lotz
3555d01e61aSMichael Lotz ioapic.max_redirection_entry
35663475256SMichael Lotz = ((ioapic.version >> IO_APIC_MAX_REDIRECTION_ENTRY_SHIFT)
3575d01e61aSMichael Lotz & IO_APIC_MAX_REDIRECTION_ENTRY_MASK);
35822d72c8eSMichael Lotz if (ioapic.max_redirection_entry >= MAX_SUPPORTED_REDIRECTION_ENTRIES) {
359*b140858bSPulkoMandy dprintf("io-apic %u entry count %d exceeds max supported, only using the "
360*b140858bSPulkoMandy "first %u entries\n", ioapic.number, ioapic.max_redirection_entry,
36122d72c8eSMichael Lotz (uint8)MAX_SUPPORTED_REDIRECTION_ENTRIES);
36222d72c8eSMichael Lotz ioapic.max_redirection_entry = MAX_SUPPORTED_REDIRECTION_ENTRIES - 1;
36322d72c8eSMichael Lotz }
3645d01e61aSMichael Lotz
3655d01e61aSMichael Lotz ioapic.global_interrupt_last
3665d01e61aSMichael Lotz = ioapic.global_interrupt_base + ioapic.max_redirection_entry;
3675d01e61aSMichael Lotz
368aa373c43SMichael Lotz ioapic.nmi_mask = 0;
369aa373c43SMichael Lotz
3705d01e61aSMichael Lotz return B_OK;
3715d01e61aSMichael Lotz }
3725d01e61aSMichael Lotz
3735d01e61aSMichael Lotz
3745d01e61aSMichael Lotz static status_t
ioapic_initialize_ioapic(struct ioapic & ioapic,uint8 targetAPIC)37563475256SMichael Lotz ioapic_initialize_ioapic(struct ioapic& ioapic, uint8 targetAPIC)
3765d01e61aSMichael Lotz {
37763475256SMichael Lotz // program the APIC ID
37863475256SMichael Lotz ioapic_write_32(ioapic, IO_APIC_ID, ioapic.apic_id << IO_APIC_ID_SHIFT);
37963475256SMichael Lotz
38086d59c04SMichael Lotz // program the interrupt vectors of the io-apic
381aa373c43SMichael Lotz ioapic.level_triggered_mask = 0;
382e9d55697SMichael Lotz uint8 gsi = ioapic.global_interrupt_base;
383e9d55697SMichael Lotz for (uint8 i = 0; i <= ioapic.max_redirection_entry; i++, gsi++) {
3845d01e61aSMichael Lotz // initialize everything to deliver to the boot CPU in physical mode
3855d01e61aSMichael Lotz // and masked until explicitly enabled through enable_io_interrupt()
38663475256SMichael Lotz uint64 entry = ((uint64)targetAPIC << IO_APIC_DESTINATION_FIELD_SHIFT)
38774e9ede3SMichael Lotz | IO_APIC_INTERRUPT_MASKED | IO_APIC_DESTINATION_MODE_PHYSICAL
3887a8ce431SMichael Lotz | ((gsi + ARCH_INTERRUPT_BASE) << IO_APIC_INTERRUPT_VECTOR_SHIFT);
3895d01e61aSMichael Lotz
390e9d55697SMichael Lotz if (gsi == 0) {
391e9d55697SMichael Lotz // make GSI 0 into an external interrupt
39274e9ede3SMichael Lotz entry |= IO_APIC_TRIGGER_MODE_EDGE
39374e9ede3SMichael Lotz | IO_APIC_PIN_POLARITY_HIGH_ACTIVE
39474e9ede3SMichael Lotz | IO_APIC_DELIVERY_MODE_EXT_INT;
395fb5a1727SMichael Lotz } else if (gsi < ISA_INTERRUPT_COUNT) {
396fb5a1727SMichael Lotz // identity map the legacy ISA interrupts
39774e9ede3SMichael Lotz entry |= IO_APIC_TRIGGER_MODE_EDGE
39874e9ede3SMichael Lotz | IO_APIC_PIN_POLARITY_HIGH_ACTIVE
39974e9ede3SMichael Lotz | IO_APIC_DELIVERY_MODE_FIXED;
4005d01e61aSMichael Lotz } else {
4015d01e61aSMichael Lotz // and the rest are PCI interrupts
40274e9ede3SMichael Lotz entry |= IO_APIC_TRIGGER_MODE_LEVEL
40374e9ede3SMichael Lotz | IO_APIC_PIN_POLARITY_LOW_ACTIVE
40474e9ede3SMichael Lotz | IO_APIC_DELIVERY_MODE_FIXED;
405f91cbddeSMichael Lotz ioapic.level_triggered_mask |= ((uint64)1 << i);
4065d01e61aSMichael Lotz }
4075d01e61aSMichael Lotz
4080798779aSMichael Lotz ioapic_write_64(ioapic, IO_APIC_REDIRECTION_TABLE + 2 * i, entry, true);
4095d01e61aSMichael Lotz }
4105d01e61aSMichael Lotz
4115d01e61aSMichael Lotz return B_OK;
412dc14d97bSMichael Lotz }
413dc14d97bSMichael Lotz
414dc14d97bSMichael Lotz
415fb5a1727SMichael Lotz static int32
ioapic_source_override_handler(void * data)416fb5a1727SMichael Lotz ioapic_source_override_handler(void* data)
417d11e32a8SMichael Lotz {
418fa6327c9SAlex Smith int32 vector = (addr_t)data;
419fb5a1727SMichael Lotz bool levelTriggered = ioapic_is_level_triggered_interrupt(vector);
420fb5a1727SMichael Lotz return int_io_interrupt_handler(vector, levelTriggered);
421d11e32a8SMichael Lotz }
422d11e32a8SMichael Lotz
423fb5a1727SMichael Lotz
424fb5a1727SMichael Lotz static status_t
acpi_enumerate_ioapics(acpi_table_madt * madt)425fb5a1727SMichael Lotz acpi_enumerate_ioapics(acpi_table_madt* madt)
426fb5a1727SMichael Lotz {
4278908aef9SMichael Lotz struct ioapic* lastIOAPIC = sIOAPICs;
428d11e32a8SMichael Lotz
429d11e32a8SMichael Lotz acpi_subtable_header* apicEntry
430d11e32a8SMichael Lotz = (acpi_subtable_header*)((uint8*)madt + sizeof(acpi_table_madt));
431d11e32a8SMichael Lotz void* end = ((uint8*)madt + madt->Header.Length);
432d11e32a8SMichael Lotz while (apicEntry < end) {
433d11e32a8SMichael Lotz switch (apicEntry->Type) {
434d11e32a8SMichael Lotz case ACPI_MADT_TYPE_IO_APIC:
435d11e32a8SMichael Lotz {
436d11e32a8SMichael Lotz acpi_madt_io_apic* info = (acpi_madt_io_apic*)apicEntry;
437fa6327c9SAlex Smith dprintf("found io-apic with address 0x%08" B_PRIx32 ", global "
438fa6327c9SAlex Smith "interrupt base %" B_PRIu32 ", apic-id %u\n",
439fa6327c9SAlex Smith (uint32)info->Address, (uint32)info->GlobalIrqBase,
440fa6327c9SAlex Smith info->Id);
441d11e32a8SMichael Lotz
442d11e32a8SMichael Lotz struct ioapic* ioapic
443d11e32a8SMichael Lotz = (struct ioapic*)malloc(sizeof(struct ioapic));
444d11e32a8SMichael Lotz if (ioapic == NULL) {
445d11e32a8SMichael Lotz dprintf("ran out of memory while allocating io-apic "
446d11e32a8SMichael Lotz "structure\n");
447d11e32a8SMichael Lotz return B_NO_MEMORY;
448d11e32a8SMichael Lotz }
449d11e32a8SMichael Lotz
4508908aef9SMichael Lotz ioapic->number
4518908aef9SMichael Lotz = lastIOAPIC != NULL ? lastIOAPIC->number + 1 : 0;
45263475256SMichael Lotz ioapic->apic_id = info->Id;
453d11e32a8SMichael Lotz ioapic->global_interrupt_base = info->GlobalIrqBase;
454d11e32a8SMichael Lotz ioapic->registers = NULL;
455d11e32a8SMichael Lotz ioapic->next = NULL;
456d11e32a8SMichael Lotz
457fa6327c9SAlex Smith dprintf("mapping io-apic %u at physical address %#" B_PRIx32
458fa6327c9SAlex Smith "\n", ioapic->number, (uint32)info->Address);
459d11e32a8SMichael Lotz status_t status = ioapic_map_ioapic(*ioapic, info->Address);
46063475256SMichael Lotz if (status != B_OK) {
46163475256SMichael Lotz free(ioapic);
46263475256SMichael Lotz break;
46363475256SMichael Lotz }
46463475256SMichael Lotz
46563475256SMichael Lotz print_ioapic(*ioapic);
4668908aef9SMichael Lotz
4678908aef9SMichael Lotz if (lastIOAPIC == NULL)
4688908aef9SMichael Lotz sIOAPICs = ioapic;
4698908aef9SMichael Lotz else
470d11e32a8SMichael Lotz lastIOAPIC->next = ioapic;
4718908aef9SMichael Lotz
472d11e32a8SMichael Lotz lastIOAPIC = ioapic;
473d11e32a8SMichael Lotz break;
474d11e32a8SMichael Lotz }
475aa373c43SMichael Lotz
476aa373c43SMichael Lotz case ACPI_MADT_TYPE_NMI_SOURCE:
477aa373c43SMichael Lotz {
478aa373c43SMichael Lotz acpi_madt_nmi_source* info
479aa373c43SMichael Lotz = (acpi_madt_nmi_source*)apicEntry;
480fa6327c9SAlex Smith dprintf("found nmi source global irq %" B_PRIu32 ", flags "
481fa6327c9SAlex Smith "0x%04x\n", (uint32)info->GlobalIrq,
482fa6327c9SAlex Smith (uint16)info->IntiFlags);
483aa373c43SMichael Lotz
484aa373c43SMichael Lotz struct ioapic* ioapic = find_ioapic(info->GlobalIrq);
485aa373c43SMichael Lotz if (ioapic == NULL) {
486aa373c43SMichael Lotz dprintf("nmi source for gsi that is not mapped to any "
487aa373c43SMichael Lotz " io-apic\n");
488aa373c43SMichael Lotz break;
489aa373c43SMichael Lotz }
490aa373c43SMichael Lotz
491aa373c43SMichael Lotz uint8 pin = info->GlobalIrq - ioapic->global_interrupt_base;
492aa373c43SMichael Lotz ioapic->nmi_mask |= (uint64)1 << pin;
493aa373c43SMichael Lotz break;
494aa373c43SMichael Lotz }
495fb5a1727SMichael Lotz }
496fb5a1727SMichael Lotz
497fb5a1727SMichael Lotz apicEntry
498fb5a1727SMichael Lotz = (acpi_subtable_header*)((uint8*)apicEntry + apicEntry->Length);
499fb5a1727SMichael Lotz }
500fb5a1727SMichael Lotz
501fb5a1727SMichael Lotz return B_OK;
502fb5a1727SMichael Lotz }
503fb5a1727SMichael Lotz
504fb5a1727SMichael Lotz
5059ae3fdcbSMichael Lotz static inline uint32
acpi_madt_convert_inti_flags(uint16 flags)5069ae3fdcbSMichael Lotz acpi_madt_convert_inti_flags(uint16 flags)
5079ae3fdcbSMichael Lotz {
5089ae3fdcbSMichael Lotz uint32 config = 0;
5099ae3fdcbSMichael Lotz switch (flags & ACPI_MADT_POLARITY_MASK) {
5109ae3fdcbSMichael Lotz case ACPI_MADT_POLARITY_ACTIVE_LOW:
5119ae3fdcbSMichael Lotz config = B_LOW_ACTIVE_POLARITY;
5129ae3fdcbSMichael Lotz break;
5139ae3fdcbSMichael Lotz default:
5149ae3fdcbSMichael Lotz dprintf("invalid polarity in inti flags\n");
5159ae3fdcbSMichael Lotz // fall through and assume active high
5169ae3fdcbSMichael Lotz case ACPI_MADT_POLARITY_ACTIVE_HIGH:
5179ae3fdcbSMichael Lotz case ACPI_MADT_POLARITY_CONFORMS:
5189ae3fdcbSMichael Lotz config = B_HIGH_ACTIVE_POLARITY;
5199ae3fdcbSMichael Lotz break;
5209ae3fdcbSMichael Lotz }
5219ae3fdcbSMichael Lotz
5229ae3fdcbSMichael Lotz switch (flags & ACPI_MADT_TRIGGER_MASK) {
5239ae3fdcbSMichael Lotz case ACPI_MADT_TRIGGER_LEVEL:
5249ae3fdcbSMichael Lotz config |= B_LEVEL_TRIGGERED;
5259ae3fdcbSMichael Lotz break;
5269ae3fdcbSMichael Lotz default:
527bc409b49SMichael Lotz dprintf("invalid trigger mode in inti flags\n");
5289ae3fdcbSMichael Lotz // fall through and assume edge triggered
5299ae3fdcbSMichael Lotz case ACPI_MADT_TRIGGER_CONFORMS:
5309ae3fdcbSMichael Lotz case ACPI_MADT_TRIGGER_EDGE:
5319ae3fdcbSMichael Lotz config |= B_EDGE_TRIGGERED;
5329ae3fdcbSMichael Lotz break;
5339ae3fdcbSMichael Lotz }
5349ae3fdcbSMichael Lotz
5359ae3fdcbSMichael Lotz return config;
5369ae3fdcbSMichael Lotz }
5379ae3fdcbSMichael Lotz
5389ae3fdcbSMichael Lotz
539fb5a1727SMichael Lotz static void
acpi_configure_source_overrides(acpi_table_madt * madt)540fb5a1727SMichael Lotz acpi_configure_source_overrides(acpi_table_madt* madt)
541fb5a1727SMichael Lotz {
542fb5a1727SMichael Lotz acpi_subtable_header* apicEntry
543fb5a1727SMichael Lotz = (acpi_subtable_header*)((uint8*)madt + sizeof(acpi_table_madt));
544fb5a1727SMichael Lotz void* end = ((uint8*)madt + madt->Header.Length);
545fb5a1727SMichael Lotz while (apicEntry < end) {
546fb5a1727SMichael Lotz switch (apicEntry->Type) {
547fb5a1727SMichael Lotz case ACPI_MADT_TYPE_INTERRUPT_OVERRIDE:
548fb5a1727SMichael Lotz {
549fb5a1727SMichael Lotz acpi_madt_interrupt_override* info
550fb5a1727SMichael Lotz = (acpi_madt_interrupt_override*)apicEntry;
551fb5a1727SMichael Lotz dprintf("found interrupt override for bus %u, source irq %u, "
552fa6327c9SAlex Smith "global irq %" B_PRIu32 ", flags 0x%08" B_PRIx32 "\n",
553fa6327c9SAlex Smith info->Bus, info->SourceIrq, (uint32)info->GlobalIrq,
554fb5a1727SMichael Lotz (uint32)info->IntiFlags);
555fb5a1727SMichael Lotz
556fb5a1727SMichael Lotz if (info->SourceIrq >= ISA_INTERRUPT_COUNT) {
557fb5a1727SMichael Lotz dprintf("source override exceeds isa interrupt count\n");
558fb5a1727SMichael Lotz break;
559fb5a1727SMichael Lotz }
560fb5a1727SMichael Lotz
561fb5a1727SMichael Lotz if (info->SourceIrq != info->GlobalIrq) {
562fb5a1727SMichael Lotz // we need a vector mapping
563fb5a1727SMichael Lotz install_io_interrupt_handler(info->GlobalIrq,
564c59b279bSJérôme Duval &ioapic_source_override_handler,
565c59b279bSJérôme Duval (void*)(addr_t)info->SourceIrq, B_NO_ENABLE_COUNTER);
566fb5a1727SMichael Lotz
567fb5a1727SMichael Lotz sSourceOverrides[info->SourceIrq] = info->GlobalIrq;
568fb5a1727SMichael Lotz }
569fb5a1727SMichael Lotz
570fb5a1727SMichael Lotz // configure non-standard polarity/trigger modes
5719ae3fdcbSMichael Lotz uint32 config = acpi_madt_convert_inti_flags(info->IntiFlags);
572fb5a1727SMichael Lotz ioapic_configure_io_interrupt(info->GlobalIrq, config);
573fb5a1727SMichael Lotz break;
574fb5a1727SMichael Lotz }
5750414a203SMichael Lotz
57686d59c04SMichael Lotz case ACPI_MADT_TYPE_NMI_SOURCE:
57786d59c04SMichael Lotz {
57886d59c04SMichael Lotz acpi_madt_nmi_source* info
57986d59c04SMichael Lotz = (acpi_madt_nmi_source*)apicEntry;
580fa6327c9SAlex Smith dprintf("found nmi source global irq %" B_PRIu32 ", flags "
581fa6327c9SAlex Smith "0x%04x\n", (uint32)info->GlobalIrq,
582fa6327c9SAlex Smith (uint16)info->IntiFlags);
58386d59c04SMichael Lotz
58486d59c04SMichael Lotz struct ioapic* ioapic = find_ioapic(info->GlobalIrq);
585aa373c43SMichael Lotz if (ioapic == NULL)
58686d59c04SMichael Lotz break;
58786d59c04SMichael Lotz
58886d59c04SMichael Lotz uint8 pin = info->GlobalIrq - ioapic->global_interrupt_base;
5899ae3fdcbSMichael Lotz uint32 config = acpi_madt_convert_inti_flags(info->IntiFlags);
5909ae3fdcbSMichael Lotz ioapic_configure_pin(*ioapic, pin, info->GlobalIrq, config,
5919ae3fdcbSMichael Lotz IO_APIC_DELIVERY_MODE_NMI);
59286d59c04SMichael Lotz break;
59386d59c04SMichael Lotz }
59486d59c04SMichael Lotz
5950414a203SMichael Lotz #ifdef TRACE_IOAPIC
5960414a203SMichael Lotz case ACPI_MADT_TYPE_LOCAL_APIC:
5970414a203SMichael Lotz {
5980414a203SMichael Lotz // purely informational
5990414a203SMichael Lotz acpi_madt_local_apic* info = (acpi_madt_local_apic*)apicEntry;
6000414a203SMichael Lotz dprintf("found local apic with id %u, processor id %u, "
6013108c9beSMichael Lotz "flags 0x%08" B_PRIx32 "\n", info->Id, info->ProcessorId,
6020414a203SMichael Lotz (uint32)info->LapicFlags);
6030414a203SMichael Lotz break;
6040414a203SMichael Lotz }
6050414a203SMichael Lotz
6060414a203SMichael Lotz case ACPI_MADT_TYPE_LOCAL_APIC_NMI:
6070414a203SMichael Lotz {
6080414a203SMichael Lotz // TODO: take these into account, but at apic.cpp
6090414a203SMichael Lotz acpi_madt_local_apic_nmi* info
6100414a203SMichael Lotz = (acpi_madt_local_apic_nmi*)apicEntry;
6110414a203SMichael Lotz dprintf("found local apic nmi source for processor %u, "
6120414a203SMichael Lotz "flags 0x%04x, local int %u\n", info->ProcessorId,
6130414a203SMichael Lotz (uint16)info->IntiFlags, info->Lint);
6140414a203SMichael Lotz break;
6150414a203SMichael Lotz }
6160414a203SMichael Lotz
6170414a203SMichael Lotz case ACPI_MADT_TYPE_LOCAL_APIC_OVERRIDE:
6180414a203SMichael Lotz {
6190414a203SMichael Lotz // TODO: take these into account, but at apic.cpp
6200414a203SMichael Lotz acpi_madt_local_apic_override* info
6210414a203SMichael Lotz = (acpi_madt_local_apic_override*)apicEntry;
6223108c9beSMichael Lotz dprintf("found local apic override with address 0x%016" B_PRIx64
6233108c9beSMichael Lotz "\n", (uint64)info->Address);
6240414a203SMichael Lotz break;
6250414a203SMichael Lotz }
6260414a203SMichael Lotz
6270414a203SMichael Lotz default:
6280414a203SMichael Lotz dprintf("found unhandled subtable of type %u length %u\n",
6290414a203SMichael Lotz apicEntry->Type, apicEntry->Length);
6300414a203SMichael Lotz break;
6310414a203SMichael Lotz #endif
632d11e32a8SMichael Lotz }
633d11e32a8SMichael Lotz
634d11e32a8SMichael Lotz apicEntry
635d11e32a8SMichael Lotz = (acpi_subtable_header*)((uint8*)apicEntry + apicEntry->Length);
636d11e32a8SMichael Lotz }
637d11e32a8SMichael Lotz }
638d11e32a8SMichael Lotz
639d11e32a8SMichael Lotz
640d11e32a8SMichael Lotz static status_t
acpi_set_interrupt_model(acpi_module_info * acpiModule,uint32 interruptModel)641dc14d97bSMichael Lotz acpi_set_interrupt_model(acpi_module_info* acpiModule, uint32 interruptModel)
642dc14d97bSMichael Lotz {
643dc14d97bSMichael Lotz acpi_object_type model;
644dc14d97bSMichael Lotz model.object_type = ACPI_TYPE_INTEGER;
6452bd8cdc1SJérôme Duval model.integer.integer = interruptModel;
646dc14d97bSMichael Lotz
647dc14d97bSMichael Lotz acpi_objects parameter;
648dc14d97bSMichael Lotz parameter.count = 1;
649dc14d97bSMichael Lotz parameter.pointer = &model;
650dc14d97bSMichael Lotz
651dc14d97bSMichael Lotz dprintf("setting ACPI interrupt model to %s\n",
6523108c9beSMichael Lotz interruptModel == ACPI_INTERRUPT_MODEL_PIC ? "PIC"
6533108c9beSMichael Lotz : (interruptModel == ACPI_INTERRUPT_MODEL_APIC ? "APIC"
6543108c9beSMichael Lotz : (interruptModel == ACPI_INTERRUPT_MODEL_SAPIC ? "SAPIC"
655dc14d97bSMichael Lotz : "unknown")));
656dc14d97bSMichael Lotz
657dc14d97bSMichael Lotz return acpiModule->evaluate_method(NULL, "\\_PIC", ¶meter, NULL);
658dc14d97bSMichael Lotz }
659dc14d97bSMichael Lotz
660dc14d97bSMichael Lotz
661a56cbb2aSMichael Lotz bool
ioapic_is_interrupt_available(int32 gsi)662a56cbb2aSMichael Lotz ioapic_is_interrupt_available(int32 gsi)
663a56cbb2aSMichael Lotz {
66486d59c04SMichael Lotz struct ioapic* ioapic = find_ioapic(gsi);
66586d59c04SMichael Lotz if (ioapic == NULL)
66686d59c04SMichael Lotz return false;
66786d59c04SMichael Lotz
66886d59c04SMichael Lotz uint8 pin = gsi - ioapic->global_interrupt_base;
66986d59c04SMichael Lotz return (ioapic->nmi_mask & ((uint64)1 << pin)) == 0;
670a56cbb2aSMichael Lotz }
671a56cbb2aSMichael Lotz
672a56cbb2aSMichael Lotz
673dc14d97bSMichael Lotz void
ioapic_preinit(kernel_args * args)674e223e8e9SAugustin Cavalier ioapic_preinit(kernel_args* args)
675e223e8e9SAugustin Cavalier {
676e223e8e9SAugustin Cavalier sIOAPICPhys = args->arch_args.ioapic_phys;
677e223e8e9SAugustin Cavalier
678e223e8e9SAugustin Cavalier // The real IO-APIC initialization occurs after PCI initialization.
679e223e8e9SAugustin Cavalier }
680e223e8e9SAugustin Cavalier
681e223e8e9SAugustin Cavalier
682e223e8e9SAugustin Cavalier void
ioapic_init()683e223e8e9SAugustin Cavalier ioapic_init()
684dc14d97bSMichael Lotz {
685dc14d97bSMichael Lotz static const interrupt_controller ioapicController = {
686dc14d97bSMichael Lotz "82093AA IOAPIC",
687dc14d97bSMichael Lotz &ioapic_enable_io_interrupt,
688dc14d97bSMichael Lotz &ioapic_disable_io_interrupt,
689dc14d97bSMichael Lotz &ioapic_configure_io_interrupt,
690dc14d97bSMichael Lotz &ioapic_is_spurious_interrupt,
691dc14d97bSMichael Lotz &ioapic_is_level_triggered_interrupt,
692d897a478SPawel Dziepak &ioapic_end_of_interrupt,
693d897a478SPawel Dziepak &ioapic_assign_interrupt_to_cpu,
694dc14d97bSMichael Lotz };
695dc14d97bSMichael Lotz
696e223e8e9SAugustin Cavalier if (!apic_available())
697dc14d97bSMichael Lotz return;
698dc14d97bSMichael Lotz
699e223e8e9SAugustin Cavalier if (sIOAPICPhys == 0) {
7008908aef9SMichael Lotz dprintf("no io-apics available, not using io-apics for interrupt "
7018908aef9SMichael Lotz "routing\n");
7028908aef9SMichael Lotz return;
7038908aef9SMichael Lotz }
7048908aef9SMichael Lotz
705dc14d97bSMichael Lotz if (get_safemode_boolean(B_SAFEMODE_DISABLE_IOAPIC, false)) {
7065d01e61aSMichael Lotz dprintf("io-apics explicitly disabled, not using io-apics for "
7075d01e61aSMichael Lotz "interrupt routing\n");
708dc14d97bSMichael Lotz return;
709dc14d97bSMichael Lotz }
710dc14d97bSMichael Lotz
711e223e8e9SAugustin Cavalier // load ACPI module
712dc14d97bSMichael Lotz status_t status;
713dc14d97bSMichael Lotz acpi_module_info* acpiModule;
714dc14d97bSMichael Lotz status = get_module(B_ACPI_MODULE_NAME, (module_info**)&acpiModule);
715dc14d97bSMichael Lotz if (status != B_OK) {
716e223e8e9SAugustin Cavalier dprintf("ACPI module not available, not configuring io-apics\n");
717dc14d97bSMichael Lotz return;
718dc14d97bSMichael Lotz }
71936aafa56SX512 BPrivate::CObjectDeleter<const char, status_t, put_module>
72036aafa56SX512 acpiModulePutter(B_ACPI_MODULE_NAME);
721dc14d97bSMichael Lotz
722fb5a1727SMichael Lotz acpi_table_madt* madt = NULL;
723fb5a1727SMichael Lotz if (acpiModule->get_table(ACPI_SIG_MADT, 0, (void**)&madt) != B_OK) {
724fb5a1727SMichael Lotz dprintf("failed to get MADT from ACPI, not configuring io-apics\n");
725fb5a1727SMichael Lotz return;
726fb5a1727SMichael Lotz }
727fb5a1727SMichael Lotz
728fb5a1727SMichael Lotz status = acpi_enumerate_ioapics(madt);
729d11e32a8SMichael Lotz if (status != B_OK) {
730ca67ddb3SMichael Lotz // We don't treat this case as fatal just yet. If we are able to
731ca67ddb3SMichael Lotz // route everything with the available IO-APICs we're fine, if not
732ca67ddb3SMichael Lotz // we will fail at the routing preparation stage further down.
733ca67ddb3SMichael Lotz dprintf("failed to enumerate all io-apics, working with what we got\n");
734d11e32a8SMichael Lotz }
735d11e32a8SMichael Lotz
736d11e32a8SMichael Lotz // switch to the APIC interrupt model before retrieving the IRQ routing
737dc14d97bSMichael Lotz // table as it will return different settings depending on the model
738dc14d97bSMichael Lotz status = acpi_set_interrupt_model(acpiModule, ACPI_INTERRUPT_MODEL_APIC);
739dc14d97bSMichael Lotz if (status != B_OK) {
740dc14d97bSMichael Lotz dprintf("failed to put ACPI into APIC interrupt model, ignoring\n");
741dc14d97bSMichael Lotz // don't abort, as the _PIC method is optional and as long as there
742dc14d97bSMichael Lotz // aren't different routings based on it this is non-fatal
743dc14d97bSMichael Lotz }
744dc14d97bSMichael Lotz
745dc14d97bSMichael Lotz IRQRoutingTable table;
746dc14d97bSMichael Lotz status = prepare_irq_routing(acpiModule, table,
747eda74390SMichael Lotz &ioapic_is_interrupt_available);
748dc14d97bSMichael Lotz if (status != B_OK) {
7495d01e61aSMichael Lotz dprintf("IRQ routing preparation failed, not configuring io-apics\n");
750dc14d97bSMichael Lotz acpi_set_interrupt_model(acpiModule, ACPI_INTERRUPT_MODEL_PIC);
751dc14d97bSMichael Lotz // revert to PIC interrupt model just in case
752dc14d97bSMichael Lotz return;
753dc14d97bSMichael Lotz }
754dc14d97bSMichael Lotz
7555d01e61aSMichael Lotz // use the boot CPU as the target for all interrupts
756e223e8e9SAugustin Cavalier uint8 targetAPIC = x86_get_cpu_apic_id(0);
7575d01e61aSMichael Lotz
7588908aef9SMichael Lotz struct ioapic* current = sIOAPICs;
7595d01e61aSMichael Lotz while (current != NULL) {
7605d01e61aSMichael Lotz status = ioapic_initialize_ioapic(*current, targetAPIC);
7615d01e61aSMichael Lotz if (status != B_OK) {
7625d01e61aSMichael Lotz panic("failed to initialize io-apic %u", current->number);
7635d01e61aSMichael Lotz acpi_set_interrupt_model(acpiModule, ACPI_INTERRUPT_MODEL_PIC);
7645d01e61aSMichael Lotz return;
7655d01e61aSMichael Lotz }
7665d01e61aSMichael Lotz
7675d01e61aSMichael Lotz current = current->next;
7685d01e61aSMichael Lotz }
7695d01e61aSMichael Lotz
770cb4e75d3SMichael Lotz #ifdef TRACE_IOAPIC
771cb4e75d3SMichael Lotz dprintf("trying interrupt routing:\n");
772cb4e75d3SMichael Lotz print_irq_routing_table(table);
773cb4e75d3SMichael Lotz #endif
774cb4e75d3SMichael Lotz
775dc14d97bSMichael Lotz status = enable_irq_routing(acpiModule, table);
776dc14d97bSMichael Lotz if (status != B_OK) {
777dc14d97bSMichael Lotz panic("failed to enable IRQ routing");
778dc14d97bSMichael Lotz // if it failed early on it might still work in PIC mode
779dc14d97bSMichael Lotz acpi_set_interrupt_model(acpiModule, ACPI_INTERRUPT_MODEL_PIC);
780dc14d97bSMichael Lotz return;
781dc14d97bSMichael Lotz }
782dc14d97bSMichael Lotz
783cb4e75d3SMichael Lotz print_irq_routing_table(table);
784cb4e75d3SMichael Lotz
785fb5a1727SMichael Lotz // configure the source overrides, but let the PCI config below override it
786fb5a1727SMichael Lotz acpi_configure_source_overrides(madt);
787fb5a1727SMichael Lotz
7885d01e61aSMichael Lotz // configure IO-APIC interrupts from PCI routing table
789dc14d97bSMichael Lotz for (int i = 0; i < table.Count(); i++) {
790dc14d97bSMichael Lotz irq_routing_entry& entry = table.ElementAt(i);
791dc14d97bSMichael Lotz ioapic_configure_io_interrupt(entry.irq,
792dc14d97bSMichael Lotz entry.polarity | entry.trigger_mode);
793dc14d97bSMichael Lotz }
794dc14d97bSMichael Lotz
795a56cbb2aSMichael Lotz // kill the local ints on the local APIC
796a56cbb2aSMichael Lotz apic_disable_local_ints();
797a56cbb2aSMichael Lotz // TODO: This uses the assumption that our init is running on the
798a56cbb2aSMichael Lotz // boot CPU and only the boot CPU has the local ints configured
799a56cbb2aSMichael Lotz // because it was running in legacy PIC mode. Possibly the other
800a56cbb2aSMichael Lotz // local APICs of the other CPUs have them configured as well. It
801a56cbb2aSMichael Lotz // shouldn't really harm, but should eventually be corrected.
802a56cbb2aSMichael Lotz
803dc14d97bSMichael Lotz // disable the legacy PIC
804fb5a1727SMichael Lotz uint16 legacyInterrupts;
805fb5a1727SMichael Lotz pic_disable(legacyInterrupts);
806fb5a1727SMichael Lotz
807fb5a1727SMichael Lotz // enable previsouly enabled legacy interrupts
808fb5a1727SMichael Lotz for (uint8 i = 0; i < 16; i++) {
809fb5a1727SMichael Lotz if ((legacyInterrupts & (1 << i)) != 0)
810fb5a1727SMichael Lotz ioapic_enable_io_interrupt(i);
811fb5a1727SMichael Lotz }
812dc14d97bSMichael Lotz
813fc2d7cb0SMichael Lotz // mark the interrupt vectors reserved so they aren't used for other stuff
814fc2d7cb0SMichael Lotz current = sIOAPICs;
815fc2d7cb0SMichael Lotz while (current != NULL) {
816fc2d7cb0SMichael Lotz reserve_io_interrupt_vectors(current->max_redirection_entry + 1,
8176a164daaSPawel Dziepak current->global_interrupt_base, INTERRUPT_TYPE_IRQ);
81866395144SPawel Dziepak
81966395144SPawel Dziepak for (int32 i = 0; i < current->max_redirection_entry + 1; i++) {
82066395144SPawel Dziepak x86_set_irq_source(current->global_interrupt_base + i,
82166395144SPawel Dziepak IRQ_SOURCE_IOAPIC);
82266395144SPawel Dziepak }
82366395144SPawel Dziepak
824fc2d7cb0SMichael Lotz current = current->next;
825fc2d7cb0SMichael Lotz }
826fc2d7cb0SMichael Lotz
827dc14d97bSMichael Lotz // prefer the ioapic over the normal pic
8285d01e61aSMichael Lotz dprintf("using io-apics for interrupt routing\n");
829dc14d97bSMichael Lotz arch_int_set_interrupt_controller(ioapicController);
830dc14d97bSMichael Lotz }
831