xref: /haiku/src/system/kernel/arch/x86/ioapic.cpp (revision d897a478d7c01054aad29b23f7f545073c797530)
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 
11dc14d97bSMichael Lotz #include "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>
21*d897a478SPawel 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
30*d897a478SPawel Dziepak #	define TRACE(...) dprintf(__VA_ARGS__)
31dc14d97bSMichael Lotz #else
32*d897a478SPawel 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)
6274e9ede3SMichael Lotz #define IO_APIC_TRIGGER_MODE_EDGE			(0 << 16)
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 
1118908aef9SMichael Lotz static ioapic* sIOAPICs = NULL;
112fb5a1727SMichael Lotz static int32 sSourceOverrides[ISA_INTERRUPT_COUNT];
113dc14d97bSMichael Lotz 
114dc14d97bSMichael Lotz 
115dc14d97bSMichael Lotz // #pragma mark - I/O APIC
116dc14d97bSMichael Lotz 
117dc14d97bSMichael Lotz 
11863475256SMichael Lotz static void
11963475256SMichael Lotz print_ioapic(struct ioapic& ioapic)
12063475256SMichael Lotz {
121fa6327c9SAlex Smith 	dprintf("io-apic %u has range %u-%u, %u entries, version 0x%08" B_PRIx32
122fa6327c9SAlex Smith 		", apic-id %u\n", ioapic.number, ioapic.global_interrupt_base,
12363475256SMichael Lotz 		ioapic.global_interrupt_last, ioapic.max_redirection_entry + 1,
12463475256SMichael Lotz 		ioapic.version, ioapic.apic_id);
12563475256SMichael Lotz }
12663475256SMichael Lotz 
12763475256SMichael Lotz 
1285d01e61aSMichael Lotz static inline struct ioapic*
1295d01e61aSMichael Lotz find_ioapic(int32 gsi)
130dc14d97bSMichael Lotz {
1315d01e61aSMichael Lotz 	if (gsi < 0)
1325d01e61aSMichael Lotz 		return NULL;
1335d01e61aSMichael Lotz 
1348908aef9SMichael Lotz 	struct ioapic* current = sIOAPICs;
1355d01e61aSMichael Lotz 	while (current != NULL) {
1365d01e61aSMichael Lotz 		if (gsi >= current->global_interrupt_base
1375d01e61aSMichael Lotz 			&& gsi <= current->global_interrupt_last) {
1385d01e61aSMichael Lotz 			return current;
1395d01e61aSMichael Lotz 		}
1405d01e61aSMichael Lotz 
1415d01e61aSMichael Lotz 		current = current->next;
1425d01e61aSMichael Lotz 	}
1435d01e61aSMichael Lotz 
1445d01e61aSMichael Lotz 	return NULL;
1455d01e61aSMichael Lotz }
1465d01e61aSMichael Lotz 
1475d01e61aSMichael Lotz 
1485d01e61aSMichael Lotz static inline uint32
1495d01e61aSMichael Lotz ioapic_read_32(struct ioapic& ioapic, uint8 registerSelect)
1505d01e61aSMichael Lotz {
1515d01e61aSMichael Lotz 	ioapic.registers->io_register_select = registerSelect;
1525d01e61aSMichael Lotz 	return ioapic.registers->io_window_register;
153dc14d97bSMichael Lotz }
154dc14d97bSMichael Lotz 
155dc14d97bSMichael Lotz 
156dc14d97bSMichael Lotz static inline void
1575d01e61aSMichael Lotz ioapic_write_32(struct ioapic& ioapic, uint8 registerSelect, uint32 value)
158dc14d97bSMichael Lotz {
1595d01e61aSMichael Lotz 	ioapic.registers->io_register_select = registerSelect;
1605d01e61aSMichael Lotz 	ioapic.registers->io_window_register = value;
161dc14d97bSMichael Lotz }
162dc14d97bSMichael Lotz 
163dc14d97bSMichael Lotz 
164dc14d97bSMichael Lotz static inline uint64
1655d01e61aSMichael Lotz ioapic_read_64(struct ioapic& ioapic, uint8 registerSelect)
166dc14d97bSMichael Lotz {
1675d01e61aSMichael Lotz 	ioapic.registers->io_register_select = registerSelect + 1;
1685d01e61aSMichael Lotz 	uint64 result = ioapic.registers->io_window_register;
169dc14d97bSMichael Lotz 	result <<= 32;
1705d01e61aSMichael Lotz 	ioapic.registers->io_register_select = registerSelect;
1715d01e61aSMichael Lotz 	result |= ioapic.registers->io_window_register;
172dc14d97bSMichael Lotz 	return result;
173dc14d97bSMichael Lotz }
174dc14d97bSMichael Lotz 
175dc14d97bSMichael Lotz 
176dc14d97bSMichael Lotz static inline void
1770798779aSMichael Lotz ioapic_write_64(struct ioapic& ioapic, uint8 registerSelect, uint64 value,
1780798779aSMichael Lotz 	bool maskFirst)
179dc14d97bSMichael Lotz {
1800798779aSMichael Lotz 	ioapic.registers->io_register_select
1810798779aSMichael Lotz 		= registerSelect + (maskFirst ? 0 : 1);
1820798779aSMichael Lotz 	ioapic.registers->io_window_register
1830798779aSMichael Lotz 		= (uint32)(value >> (maskFirst ? 0 : 32));
1840798779aSMichael Lotz 	ioapic.registers->io_register_select
1850798779aSMichael Lotz 		= registerSelect + (maskFirst ? 1 : 0);
1860798779aSMichael Lotz 	ioapic.registers->io_window_register
1870798779aSMichael Lotz 		= (uint32)(value >> (maskFirst ? 32 : 0));
188dc14d97bSMichael Lotz }
189dc14d97bSMichael Lotz 
190dc14d97bSMichael Lotz 
1919ae3fdcbSMichael Lotz static void
1929ae3fdcbSMichael Lotz ioapic_configure_pin(struct ioapic& ioapic, uint8 pin, uint8 vector,
19374e9ede3SMichael Lotz 	uint8 triggerPolarity, uint16 deliveryMode)
1949ae3fdcbSMichael Lotz {
1959ae3fdcbSMichael Lotz 	uint64 entry = ioapic_read_64(ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2);
19674e9ede3SMichael Lotz 	entry &= ~(IO_APIC_TRIGGER_MODE_MASK | IO_APIC_PIN_POLARITY_MASK
19774e9ede3SMichael Lotz 		| IO_APIC_INTERRUPT_VECTOR_MASK | IO_APIC_DELIVERY_MODE_MASK);
1989ae3fdcbSMichael Lotz 
1999ae3fdcbSMichael Lotz 	if (triggerPolarity & B_LEVEL_TRIGGERED) {
20074e9ede3SMichael Lotz 		entry |= IO_APIC_TRIGGER_MODE_LEVEL;
2019ae3fdcbSMichael Lotz 		ioapic.level_triggered_mask |= ((uint64)1 << pin);
2029ae3fdcbSMichael Lotz 	} else {
20374e9ede3SMichael Lotz 		entry |= IO_APIC_TRIGGER_MODE_EDGE;
2049ae3fdcbSMichael Lotz 		ioapic.level_triggered_mask &= ~((uint64)1 << pin);
2059ae3fdcbSMichael Lotz 	}
2069ae3fdcbSMichael Lotz 
2079ae3fdcbSMichael Lotz 	if (triggerPolarity & B_LOW_ACTIVE_POLARITY)
20874e9ede3SMichael Lotz 		entry |= IO_APIC_PIN_POLARITY_LOW_ACTIVE;
2099ae3fdcbSMichael Lotz 	else
21074e9ede3SMichael Lotz 		entry |= IO_APIC_PIN_POLARITY_HIGH_ACTIVE;
2119ae3fdcbSMichael Lotz 
21274e9ede3SMichael Lotz 	entry |= deliveryMode;
2139ae3fdcbSMichael Lotz 	entry |= (vector + ARCH_INTERRUPT_BASE) << IO_APIC_INTERRUPT_VECTOR_SHIFT;
2149ae3fdcbSMichael Lotz 	ioapic_write_64(ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2, entry, true);
2159ae3fdcbSMichael Lotz }
2169ae3fdcbSMichael Lotz 
2179ae3fdcbSMichael Lotz 
218dc14d97bSMichael Lotz static bool
219eda74390SMichael Lotz ioapic_is_spurious_interrupt(int32 gsi)
220dc14d97bSMichael Lotz {
221dc14d97bSMichael Lotz 	// the spurious interrupt vector is initialized to the max value in smp
222eda74390SMichael Lotz 	return gsi == 0xff - ARCH_INTERRUPT_BASE;
223dc14d97bSMichael Lotz }
224dc14d97bSMichael Lotz 
225dc14d97bSMichael Lotz 
226dc14d97bSMichael Lotz static bool
2275d01e61aSMichael Lotz ioapic_is_level_triggered_interrupt(int32 gsi)
228dc14d97bSMichael Lotz {
2295d01e61aSMichael Lotz 	struct ioapic* ioapic = find_ioapic(gsi);
2305d01e61aSMichael Lotz 	if (ioapic == NULL)
231dc14d97bSMichael Lotz 		return false;
232dc14d97bSMichael Lotz 
2335d01e61aSMichael Lotz 	uint8 pin = gsi - ioapic->global_interrupt_base;
234f91cbddeSMichael Lotz 	return (ioapic->level_triggered_mask & ((uint64)1 << pin)) != 0;
235dc14d97bSMichael Lotz }
236dc14d97bSMichael Lotz 
237dc14d97bSMichael Lotz 
238dc14d97bSMichael Lotz static bool
239dc14d97bSMichael Lotz ioapic_end_of_interrupt(int32 num)
240dc14d97bSMichael Lotz {
241dc14d97bSMichael Lotz 	apic_end_of_interrupt();
242dc14d97bSMichael Lotz 	return true;
243dc14d97bSMichael Lotz }
244dc14d97bSMichael Lotz 
245dc14d97bSMichael Lotz 
246dc14d97bSMichael Lotz static void
247*d897a478SPawel Dziepak ioapic_assign_interrupt_to_cpu(int32 gsi, int32 cpu)
248*d897a478SPawel Dziepak {
249*d897a478SPawel Dziepak 	if (gsi < ISA_INTERRUPT_COUNT && sSourceOverrides[gsi] != 0)
250*d897a478SPawel Dziepak 		gsi = sSourceOverrides[gsi];
251*d897a478SPawel Dziepak 
252*d897a478SPawel Dziepak 	struct ioapic* ioapic = find_ioapic(gsi);
253*d897a478SPawel Dziepak 	if (ioapic == NULL)
254*d897a478SPawel Dziepak 		return;
255*d897a478SPawel Dziepak 
256*d897a478SPawel Dziepak 	uint32 apicid = x86_get_cpu_apic_id(cpu);
257*d897a478SPawel Dziepak 
258*d897a478SPawel Dziepak 	uint8 pin = gsi - ioapic->global_interrupt_base;
259*d897a478SPawel Dziepak 	TRACE("ioapic_assign_interrupt_to_cpu: gsi %ld (io-apic %u pin %u) to"
260*d897a478SPawel Dziepak 		" cpu %ld (apic_id %lu)\n", gsi, ioapic->number, pin, cpu, apicid);
261*d897a478SPawel Dziepak 
262*d897a478SPawel Dziepak 	uint64 entry = ioapic_read_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2);
263*d897a478SPawel Dziepak 	entry &= ~(uint64(IO_APIC_DESTINATION_FIELD_MASK)
264*d897a478SPawel Dziepak 			<< IO_APIC_DESTINATION_FIELD_SHIFT);
265*d897a478SPawel Dziepak 	entry |= uint64(apicid) << IO_APIC_DESTINATION_FIELD_SHIFT;
266*d897a478SPawel Dziepak 	ioapic_write_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2, entry, false);
267*d897a478SPawel Dziepak }
268*d897a478SPawel Dziepak 
269*d897a478SPawel Dziepak 
270*d897a478SPawel Dziepak static void
2715d01e61aSMichael Lotz ioapic_enable_io_interrupt(int32 gsi)
272dc14d97bSMichael Lotz {
273fb5a1727SMichael Lotz 	// If enabling an overriden source is attempted, enable the override entry
274cb4e75d3SMichael Lotz 	// instead. An interrupt handler was installed at the override GSI to relay
275fb5a1727SMichael Lotz 	// interrupts to the overriden source.
276fb5a1727SMichael Lotz 	if (gsi < ISA_INTERRUPT_COUNT && sSourceOverrides[gsi] != 0)
277fb5a1727SMichael Lotz 		gsi = sSourceOverrides[gsi];
278fb5a1727SMichael Lotz 
2795d01e61aSMichael Lotz 	struct ioapic* ioapic = find_ioapic(gsi);
2805d01e61aSMichael Lotz 	if (ioapic == NULL)
281dc14d97bSMichael Lotz 		return;
282dc14d97bSMichael Lotz 
2835d01e61aSMichael Lotz 	uint8 pin = gsi - ioapic->global_interrupt_base;
284*d897a478SPawel Dziepak 	TRACE("ioapic_enable_io_interrupt: gsi %ld -> io-apic %u pin %u\n",
285*d897a478SPawel Dziepak 		gsi, ioapic->number, pin);
286dc14d97bSMichael Lotz 
2875d01e61aSMichael Lotz 	uint64 entry = ioapic_read_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2);
28874e9ede3SMichael Lotz 	entry &= ~IO_APIC_INTERRUPT_MASKED;
2890798779aSMichael Lotz 	ioapic_write_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2, entry, false);
290dc14d97bSMichael Lotz }
291dc14d97bSMichael Lotz 
292dc14d97bSMichael Lotz 
293dc14d97bSMichael Lotz static void
2945d01e61aSMichael Lotz ioapic_disable_io_interrupt(int32 gsi)
295dc14d97bSMichael Lotz {
2965d01e61aSMichael Lotz 	struct ioapic* ioapic = find_ioapic(gsi);
2975d01e61aSMichael Lotz 	if (ioapic == NULL)
298dc14d97bSMichael Lotz 		return;
299dc14d97bSMichael Lotz 
3005d01e61aSMichael Lotz 	uint8 pin = gsi - ioapic->global_interrupt_base;
301*d897a478SPawel Dziepak 	TRACE("ioapic_disable_io_interrupt: gsi %ld -> io-apic %u pin %u\n",
302*d897a478SPawel Dziepak 		gsi, ioapic->number, pin);
303dc14d97bSMichael Lotz 
3045d01e61aSMichael Lotz 	uint64 entry = ioapic_read_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2);
30574e9ede3SMichael Lotz 	entry |= IO_APIC_INTERRUPT_MASKED;
3060798779aSMichael Lotz 	ioapic_write_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2, entry, true);
307dc14d97bSMichael Lotz }
308dc14d97bSMichael Lotz 
309dc14d97bSMichael Lotz 
310dc14d97bSMichael Lotz static void
3115d01e61aSMichael Lotz ioapic_configure_io_interrupt(int32 gsi, uint32 config)
312dc14d97bSMichael Lotz {
3135d01e61aSMichael Lotz 	struct ioapic* ioapic = find_ioapic(gsi);
3145d01e61aSMichael Lotz 	if (ioapic == NULL)
315dc14d97bSMichael Lotz 		return;
316dc14d97bSMichael Lotz 
3175d01e61aSMichael Lotz 	uint8 pin = gsi - ioapic->global_interrupt_base;
318*d897a478SPawel Dziepak 	TRACE("ioapic_configure_io_interrupt: gsi %ld -> io-apic %u pin %u; "
319*d897a478SPawel Dziepak 		"config 0x%08lx\n", gsi, ioapic->number, pin, config);
320dc14d97bSMichael Lotz 
3219ae3fdcbSMichael Lotz 	ioapic_configure_pin(*ioapic, pin, gsi, config,
3229ae3fdcbSMichael Lotz 		IO_APIC_DELIVERY_MODE_FIXED);
3235d01e61aSMichael Lotz }
3245d01e61aSMichael Lotz 
3255d01e61aSMichael Lotz 
3265d01e61aSMichael Lotz static status_t
3275d01e61aSMichael Lotz ioapic_map_ioapic(struct ioapic& ioapic, phys_addr_t physicalAddress)
3285d01e61aSMichael Lotz {
3295d01e61aSMichael Lotz 	ioapic.register_area = vm_map_physical_memory(B_SYSTEM_TEAM, "io-apic",
3305d01e61aSMichael Lotz 		(void**)&ioapic.registers, ioapic.registers != NULL ? B_EXACT_ADDRESS
3315d01e61aSMichael Lotz 		: B_ANY_KERNEL_ADDRESS, B_PAGE_SIZE, B_KERNEL_READ_AREA
332d11e32a8SMichael Lotz 		| B_KERNEL_WRITE_AREA, physicalAddress, ioapic.registers != NULL);
3335d01e61aSMichael Lotz 	if (ioapic.register_area < 0) {
3345d01e61aSMichael Lotz 		panic("mapping io-apic %u failed", ioapic.number);
3355d01e61aSMichael Lotz 		return ioapic.register_area;
3365d01e61aSMichael Lotz 	}
3375d01e61aSMichael Lotz 
338*d897a478SPawel Dziepak 	TRACE("mapped io-apic %u to %p\n", ioapic.number, ioapic.registers);
339d11e32a8SMichael Lotz 
34063475256SMichael Lotz 	ioapic.version = ioapic_read_32(ioapic, IO_APIC_VERSION);
34163475256SMichael Lotz 	if (ioapic.version == 0xffffffff) {
3425d01e61aSMichael Lotz 		dprintf("io-apic %u seems inaccessible, not using it\n",
3435d01e61aSMichael Lotz 			ioapic.number);
3445d01e61aSMichael Lotz 		vm_delete_area(B_SYSTEM_TEAM, ioapic.register_area, true);
3455d01e61aSMichael Lotz 		ioapic.register_area = -1;
3465d01e61aSMichael Lotz 		ioapic.registers = NULL;
3475d01e61aSMichael Lotz 		return B_ERROR;
3485d01e61aSMichael Lotz 	}
3495d01e61aSMichael Lotz 
3505d01e61aSMichael Lotz 	ioapic.max_redirection_entry
35163475256SMichael Lotz 		= ((ioapic.version >> IO_APIC_MAX_REDIRECTION_ENTRY_SHIFT)
3525d01e61aSMichael Lotz 			& IO_APIC_MAX_REDIRECTION_ENTRY_MASK);
35322d72c8eSMichael Lotz 	if (ioapic.max_redirection_entry >= MAX_SUPPORTED_REDIRECTION_ENTRIES) {
35422d72c8eSMichael Lotz 		dprintf("io-apic %u entry count exceeds max supported, only using the "
35522d72c8eSMichael Lotz 			"first %u entries", ioapic.number,
35622d72c8eSMichael Lotz 			(uint8)MAX_SUPPORTED_REDIRECTION_ENTRIES);
35722d72c8eSMichael Lotz 		ioapic.max_redirection_entry = MAX_SUPPORTED_REDIRECTION_ENTRIES - 1;
35822d72c8eSMichael Lotz 	}
3595d01e61aSMichael Lotz 
3605d01e61aSMichael Lotz 	ioapic.global_interrupt_last
3615d01e61aSMichael Lotz 		= ioapic.global_interrupt_base + ioapic.max_redirection_entry;
3625d01e61aSMichael Lotz 
363aa373c43SMichael Lotz 	ioapic.nmi_mask = 0;
364aa373c43SMichael Lotz 
3655d01e61aSMichael Lotz 	return B_OK;
3665d01e61aSMichael Lotz }
3675d01e61aSMichael Lotz 
3685d01e61aSMichael Lotz 
3695d01e61aSMichael Lotz static status_t
37063475256SMichael Lotz ioapic_initialize_ioapic(struct ioapic& ioapic, uint8 targetAPIC)
3715d01e61aSMichael Lotz {
37263475256SMichael Lotz 	// program the APIC ID
37363475256SMichael Lotz 	ioapic_write_32(ioapic, IO_APIC_ID, ioapic.apic_id << IO_APIC_ID_SHIFT);
37463475256SMichael Lotz 
37586d59c04SMichael Lotz 	// program the interrupt vectors of the io-apic
376aa373c43SMichael Lotz 	ioapic.level_triggered_mask = 0;
377e9d55697SMichael Lotz 	uint8 gsi = ioapic.global_interrupt_base;
378e9d55697SMichael Lotz 	for (uint8 i = 0; i <= ioapic.max_redirection_entry; i++, gsi++) {
3795d01e61aSMichael Lotz 		// initialize everything to deliver to the boot CPU in physical mode
3805d01e61aSMichael Lotz 		// and masked until explicitly enabled through enable_io_interrupt()
38163475256SMichael Lotz 		uint64 entry = ((uint64)targetAPIC << IO_APIC_DESTINATION_FIELD_SHIFT)
38274e9ede3SMichael Lotz 			| IO_APIC_INTERRUPT_MASKED | IO_APIC_DESTINATION_MODE_PHYSICAL
3837a8ce431SMichael Lotz 			| ((gsi + ARCH_INTERRUPT_BASE) << IO_APIC_INTERRUPT_VECTOR_SHIFT);
3845d01e61aSMichael Lotz 
385e9d55697SMichael Lotz 		if (gsi == 0) {
386e9d55697SMichael Lotz 			// make GSI 0 into an external interrupt
38774e9ede3SMichael Lotz 			entry |= IO_APIC_TRIGGER_MODE_EDGE
38874e9ede3SMichael Lotz 				| IO_APIC_PIN_POLARITY_HIGH_ACTIVE
38974e9ede3SMichael Lotz 				| IO_APIC_DELIVERY_MODE_EXT_INT;
390fb5a1727SMichael Lotz 		} else if (gsi < ISA_INTERRUPT_COUNT) {
391fb5a1727SMichael Lotz 			// identity map the legacy ISA interrupts
39274e9ede3SMichael Lotz 			entry |= IO_APIC_TRIGGER_MODE_EDGE
39374e9ede3SMichael Lotz 				| IO_APIC_PIN_POLARITY_HIGH_ACTIVE
39474e9ede3SMichael Lotz 				| IO_APIC_DELIVERY_MODE_FIXED;
3955d01e61aSMichael Lotz 		} else {
3965d01e61aSMichael Lotz 			// and the rest are PCI interrupts
39774e9ede3SMichael Lotz 			entry |= IO_APIC_TRIGGER_MODE_LEVEL
39874e9ede3SMichael Lotz 				| IO_APIC_PIN_POLARITY_LOW_ACTIVE
39974e9ede3SMichael Lotz 				| IO_APIC_DELIVERY_MODE_FIXED;
400f91cbddeSMichael Lotz 			ioapic.level_triggered_mask |= ((uint64)1 << i);
4015d01e61aSMichael Lotz 		}
4025d01e61aSMichael Lotz 
4030798779aSMichael Lotz 		ioapic_write_64(ioapic, IO_APIC_REDIRECTION_TABLE + 2 * i, entry, true);
4045d01e61aSMichael Lotz 	}
4055d01e61aSMichael Lotz 
4065d01e61aSMichael Lotz 	return B_OK;
407dc14d97bSMichael Lotz }
408dc14d97bSMichael Lotz 
409dc14d97bSMichael Lotz 
410fb5a1727SMichael Lotz static int32
411fb5a1727SMichael Lotz ioapic_source_override_handler(void* data)
412d11e32a8SMichael Lotz {
413fa6327c9SAlex Smith 	int32 vector = (addr_t)data;
414fb5a1727SMichael Lotz 	bool levelTriggered = ioapic_is_level_triggered_interrupt(vector);
415fb5a1727SMichael Lotz 	return int_io_interrupt_handler(vector, levelTriggered);
416d11e32a8SMichael Lotz }
417d11e32a8SMichael Lotz 
418fb5a1727SMichael Lotz 
419fb5a1727SMichael Lotz static status_t
420fb5a1727SMichael Lotz acpi_enumerate_ioapics(acpi_table_madt* madt)
421fb5a1727SMichael Lotz {
4228908aef9SMichael Lotz 	struct ioapic* lastIOAPIC = sIOAPICs;
423d11e32a8SMichael Lotz 
424d11e32a8SMichael Lotz 	acpi_subtable_header* apicEntry
425d11e32a8SMichael Lotz 		= (acpi_subtable_header*)((uint8*)madt + sizeof(acpi_table_madt));
426d11e32a8SMichael Lotz 	void* end = ((uint8*)madt + madt->Header.Length);
427d11e32a8SMichael Lotz 	while (apicEntry < end) {
428d11e32a8SMichael Lotz 		switch (apicEntry->Type) {
429d11e32a8SMichael Lotz 			case ACPI_MADT_TYPE_IO_APIC:
430d11e32a8SMichael Lotz 			{
431d11e32a8SMichael Lotz 				acpi_madt_io_apic* info = (acpi_madt_io_apic*)apicEntry;
432fa6327c9SAlex Smith 				dprintf("found io-apic with address 0x%08" B_PRIx32 ", global "
433fa6327c9SAlex Smith 					"interrupt base %" B_PRIu32 ", apic-id %u\n",
434fa6327c9SAlex Smith 					(uint32)info->Address, (uint32)info->GlobalIrqBase,
435fa6327c9SAlex Smith 					info->Id);
436d11e32a8SMichael Lotz 
437d11e32a8SMichael Lotz 				struct ioapic* ioapic
438d11e32a8SMichael Lotz 					= (struct ioapic*)malloc(sizeof(struct ioapic));
439d11e32a8SMichael Lotz 				if (ioapic == NULL) {
440d11e32a8SMichael Lotz 					dprintf("ran out of memory while allocating io-apic "
441d11e32a8SMichael Lotz 						"structure\n");
442d11e32a8SMichael Lotz 					return B_NO_MEMORY;
443d11e32a8SMichael Lotz 				}
444d11e32a8SMichael Lotz 
4458908aef9SMichael Lotz 				ioapic->number
4468908aef9SMichael Lotz 					= lastIOAPIC != NULL ? lastIOAPIC->number + 1 : 0;
44763475256SMichael Lotz 				ioapic->apic_id = info->Id;
448d11e32a8SMichael Lotz 				ioapic->global_interrupt_base = info->GlobalIrqBase;
449d11e32a8SMichael Lotz 				ioapic->registers = NULL;
450d11e32a8SMichael Lotz 				ioapic->next = NULL;
451d11e32a8SMichael Lotz 
452fa6327c9SAlex Smith 				dprintf("mapping io-apic %u at physical address %#" B_PRIx32
453fa6327c9SAlex Smith 					"\n", ioapic->number, (uint32)info->Address);
454d11e32a8SMichael Lotz 				status_t status = ioapic_map_ioapic(*ioapic, info->Address);
45563475256SMichael Lotz 				if (status != B_OK) {
45663475256SMichael Lotz 					free(ioapic);
45763475256SMichael Lotz 					break;
45863475256SMichael Lotz 				}
45963475256SMichael Lotz 
46063475256SMichael Lotz 				print_ioapic(*ioapic);
4618908aef9SMichael Lotz 
4628908aef9SMichael Lotz 				if (lastIOAPIC == NULL)
4638908aef9SMichael Lotz 					sIOAPICs = ioapic;
4648908aef9SMichael Lotz 				else
465d11e32a8SMichael Lotz 					lastIOAPIC->next = ioapic;
4668908aef9SMichael Lotz 
467d11e32a8SMichael Lotz 				lastIOAPIC = ioapic;
468d11e32a8SMichael Lotz 				break;
469d11e32a8SMichael Lotz 			}
470aa373c43SMichael Lotz 
471aa373c43SMichael Lotz 			case ACPI_MADT_TYPE_NMI_SOURCE:
472aa373c43SMichael Lotz 			{
473aa373c43SMichael Lotz 				acpi_madt_nmi_source* info
474aa373c43SMichael Lotz 					= (acpi_madt_nmi_source*)apicEntry;
475fa6327c9SAlex Smith 				dprintf("found nmi source global irq %" B_PRIu32 ", flags "
476fa6327c9SAlex Smith 					"0x%04x\n", (uint32)info->GlobalIrq,
477fa6327c9SAlex Smith 					(uint16)info->IntiFlags);
478aa373c43SMichael Lotz 
479aa373c43SMichael Lotz 				struct ioapic* ioapic = find_ioapic(info->GlobalIrq);
480aa373c43SMichael Lotz 				if (ioapic == NULL) {
481aa373c43SMichael Lotz 					dprintf("nmi source for gsi that is not mapped to any "
482aa373c43SMichael Lotz 						" io-apic\n");
483aa373c43SMichael Lotz 					break;
484aa373c43SMichael Lotz 				}
485aa373c43SMichael Lotz 
486aa373c43SMichael Lotz 				uint8 pin = info->GlobalIrq - ioapic->global_interrupt_base;
487aa373c43SMichael Lotz 				ioapic->nmi_mask |= (uint64)1 << pin;
488aa373c43SMichael Lotz 				break;
489aa373c43SMichael Lotz 			}
490fb5a1727SMichael Lotz 		}
491fb5a1727SMichael Lotz 
492fb5a1727SMichael Lotz 		apicEntry
493fb5a1727SMichael Lotz 			= (acpi_subtable_header*)((uint8*)apicEntry + apicEntry->Length);
494fb5a1727SMichael Lotz 	}
495fb5a1727SMichael Lotz 
496fb5a1727SMichael Lotz 	return B_OK;
497fb5a1727SMichael Lotz }
498fb5a1727SMichael Lotz 
499fb5a1727SMichael Lotz 
5009ae3fdcbSMichael Lotz static inline uint32
5019ae3fdcbSMichael Lotz acpi_madt_convert_inti_flags(uint16 flags)
5029ae3fdcbSMichael Lotz {
5039ae3fdcbSMichael Lotz 	uint32 config = 0;
5049ae3fdcbSMichael Lotz 	switch (flags & ACPI_MADT_POLARITY_MASK) {
5059ae3fdcbSMichael Lotz 		case ACPI_MADT_POLARITY_ACTIVE_LOW:
5069ae3fdcbSMichael Lotz 			config = B_LOW_ACTIVE_POLARITY;
5079ae3fdcbSMichael Lotz 			break;
5089ae3fdcbSMichael Lotz 		default:
5099ae3fdcbSMichael Lotz 			dprintf("invalid polarity in inti flags\n");
5109ae3fdcbSMichael Lotz 			// fall through and assume active high
5119ae3fdcbSMichael Lotz 		case ACPI_MADT_POLARITY_ACTIVE_HIGH:
5129ae3fdcbSMichael Lotz 		case ACPI_MADT_POLARITY_CONFORMS:
5139ae3fdcbSMichael Lotz 			config = B_HIGH_ACTIVE_POLARITY;
5149ae3fdcbSMichael Lotz 			break;
5159ae3fdcbSMichael Lotz 	}
5169ae3fdcbSMichael Lotz 
5179ae3fdcbSMichael Lotz 	switch (flags & ACPI_MADT_TRIGGER_MASK) {
5189ae3fdcbSMichael Lotz 		case ACPI_MADT_TRIGGER_LEVEL:
5199ae3fdcbSMichael Lotz 			config |= B_LEVEL_TRIGGERED;
5209ae3fdcbSMichael Lotz 			break;
5219ae3fdcbSMichael Lotz 		default:
522bc409b49SMichael Lotz 			dprintf("invalid trigger mode in inti flags\n");
5239ae3fdcbSMichael Lotz 			// fall through and assume edge triggered
5249ae3fdcbSMichael Lotz 		case ACPI_MADT_TRIGGER_CONFORMS:
5259ae3fdcbSMichael Lotz 		case ACPI_MADT_TRIGGER_EDGE:
5269ae3fdcbSMichael Lotz 			config |= B_EDGE_TRIGGERED;
5279ae3fdcbSMichael Lotz 			break;
5289ae3fdcbSMichael Lotz 	}
5299ae3fdcbSMichael Lotz 
5309ae3fdcbSMichael Lotz 	return config;
5319ae3fdcbSMichael Lotz }
5329ae3fdcbSMichael Lotz 
5339ae3fdcbSMichael Lotz 
534fb5a1727SMichael Lotz static void
535fb5a1727SMichael Lotz acpi_configure_source_overrides(acpi_table_madt* madt)
536fb5a1727SMichael Lotz {
537fb5a1727SMichael Lotz 	acpi_subtable_header* apicEntry
538fb5a1727SMichael Lotz 		= (acpi_subtable_header*)((uint8*)madt + sizeof(acpi_table_madt));
539fb5a1727SMichael Lotz 	void* end = ((uint8*)madt + madt->Header.Length);
540fb5a1727SMichael Lotz 	while (apicEntry < end) {
541fb5a1727SMichael Lotz 		switch (apicEntry->Type) {
542fb5a1727SMichael Lotz 			case ACPI_MADT_TYPE_INTERRUPT_OVERRIDE:
543fb5a1727SMichael Lotz 			{
544fb5a1727SMichael Lotz 				acpi_madt_interrupt_override* info
545fb5a1727SMichael Lotz 					= (acpi_madt_interrupt_override*)apicEntry;
546fb5a1727SMichael Lotz 				dprintf("found interrupt override for bus %u, source irq %u, "
547fa6327c9SAlex Smith 					"global irq %" B_PRIu32 ", flags 0x%08" B_PRIx32 "\n",
548fa6327c9SAlex Smith 					info->Bus, info->SourceIrq, (uint32)info->GlobalIrq,
549fb5a1727SMichael Lotz 					(uint32)info->IntiFlags);
550fb5a1727SMichael Lotz 
551fb5a1727SMichael Lotz 				if (info->SourceIrq >= ISA_INTERRUPT_COUNT) {
552fb5a1727SMichael Lotz 					dprintf("source override exceeds isa interrupt count\n");
553fb5a1727SMichael Lotz 					break;
554fb5a1727SMichael Lotz 				}
555fb5a1727SMichael Lotz 
556fb5a1727SMichael Lotz 				if (info->SourceIrq != info->GlobalIrq) {
557fb5a1727SMichael Lotz 					// we need a vector mapping
558fb5a1727SMichael Lotz 					install_io_interrupt_handler(info->GlobalIrq,
559c59b279bSJérôme Duval 						&ioapic_source_override_handler,
560c59b279bSJérôme Duval 						(void*)(addr_t)info->SourceIrq, B_NO_ENABLE_COUNTER);
561fb5a1727SMichael Lotz 
562fb5a1727SMichael Lotz 					sSourceOverrides[info->SourceIrq] = info->GlobalIrq;
563fb5a1727SMichael Lotz 				}
564fb5a1727SMichael Lotz 
565fb5a1727SMichael Lotz 				// configure non-standard polarity/trigger modes
5669ae3fdcbSMichael Lotz 				uint32 config = acpi_madt_convert_inti_flags(info->IntiFlags);
567fb5a1727SMichael Lotz 				ioapic_configure_io_interrupt(info->GlobalIrq, config);
568fb5a1727SMichael Lotz 				break;
569fb5a1727SMichael Lotz 			}
5700414a203SMichael Lotz 
57186d59c04SMichael Lotz 			case ACPI_MADT_TYPE_NMI_SOURCE:
57286d59c04SMichael Lotz 			{
57386d59c04SMichael Lotz 				acpi_madt_nmi_source* info
57486d59c04SMichael Lotz 					= (acpi_madt_nmi_source*)apicEntry;
575fa6327c9SAlex Smith 				dprintf("found nmi source global irq %" B_PRIu32 ", flags "
576fa6327c9SAlex Smith 					"0x%04x\n", (uint32)info->GlobalIrq,
577fa6327c9SAlex Smith 					(uint16)info->IntiFlags);
57886d59c04SMichael Lotz 
57986d59c04SMichael Lotz 				struct ioapic* ioapic = find_ioapic(info->GlobalIrq);
580aa373c43SMichael Lotz 				if (ioapic == NULL)
58186d59c04SMichael Lotz 					break;
58286d59c04SMichael Lotz 
58386d59c04SMichael Lotz 				uint8 pin = info->GlobalIrq - ioapic->global_interrupt_base;
5849ae3fdcbSMichael Lotz 				uint32 config = acpi_madt_convert_inti_flags(info->IntiFlags);
5859ae3fdcbSMichael Lotz 				ioapic_configure_pin(*ioapic, pin, info->GlobalIrq, config,
5869ae3fdcbSMichael Lotz 					IO_APIC_DELIVERY_MODE_NMI);
58786d59c04SMichael Lotz 				break;
58886d59c04SMichael Lotz 			}
58986d59c04SMichael Lotz 
5900414a203SMichael Lotz #ifdef TRACE_IOAPIC
5910414a203SMichael Lotz 			case ACPI_MADT_TYPE_LOCAL_APIC:
5920414a203SMichael Lotz 			{
5930414a203SMichael Lotz 				// purely informational
5940414a203SMichael Lotz 				acpi_madt_local_apic* info = (acpi_madt_local_apic*)apicEntry;
5950414a203SMichael Lotz 				dprintf("found local apic with id %u, processor id %u, "
5960414a203SMichael Lotz 					"flags 0x%08lx\n", info->Id, info->ProcessorId,
5970414a203SMichael Lotz 					(uint32)info->LapicFlags);
5980414a203SMichael Lotz 				break;
5990414a203SMichael Lotz 			}
6000414a203SMichael Lotz 
6010414a203SMichael Lotz 			case ACPI_MADT_TYPE_LOCAL_APIC_NMI:
6020414a203SMichael Lotz 			{
6030414a203SMichael Lotz 				// TODO: take these into account, but at apic.cpp
6040414a203SMichael Lotz 				acpi_madt_local_apic_nmi* info
6050414a203SMichael Lotz 					= (acpi_madt_local_apic_nmi*)apicEntry;
6060414a203SMichael Lotz 				dprintf("found local apic nmi source for processor %u, "
6070414a203SMichael Lotz 					"flags 0x%04x, local int %u\n", info->ProcessorId,
6080414a203SMichael Lotz 					(uint16)info->IntiFlags, info->Lint);
6090414a203SMichael Lotz 				break;
6100414a203SMichael Lotz 			}
6110414a203SMichael Lotz 
6120414a203SMichael Lotz 			case ACPI_MADT_TYPE_LOCAL_APIC_OVERRIDE:
6130414a203SMichael Lotz 			{
6140414a203SMichael Lotz 				// TODO: take these into account, but at apic.cpp
6150414a203SMichael Lotz 				acpi_madt_local_apic_override* info
6160414a203SMichael Lotz 					= (acpi_madt_local_apic_override*)apicEntry;
6170414a203SMichael Lotz 				dprintf("found local apic override with address 0x%016llx\n",
6180414a203SMichael Lotz 					(uint64)info->Address);
6190414a203SMichael Lotz 				break;
6200414a203SMichael Lotz 			}
6210414a203SMichael Lotz 
6220414a203SMichael Lotz 			default:
6230414a203SMichael Lotz 				dprintf("found unhandled subtable of type %u length %u\n",
6240414a203SMichael Lotz 					apicEntry->Type, apicEntry->Length);
6250414a203SMichael Lotz 				break;
6260414a203SMichael Lotz #endif
627d11e32a8SMichael Lotz 		}
628d11e32a8SMichael Lotz 
629d11e32a8SMichael Lotz 		apicEntry
630d11e32a8SMichael Lotz 			= (acpi_subtable_header*)((uint8*)apicEntry + apicEntry->Length);
631d11e32a8SMichael Lotz 	}
632d11e32a8SMichael Lotz }
633d11e32a8SMichael Lotz 
634d11e32a8SMichael Lotz 
635d11e32a8SMichael Lotz static status_t
636dc14d97bSMichael Lotz acpi_set_interrupt_model(acpi_module_info* acpiModule, uint32 interruptModel)
637dc14d97bSMichael Lotz {
638dc14d97bSMichael Lotz 	acpi_object_type model;
639dc14d97bSMichael Lotz 	model.object_type = ACPI_TYPE_INTEGER;
640dc14d97bSMichael Lotz 	model.data.integer = interruptModel;
641dc14d97bSMichael Lotz 
642dc14d97bSMichael Lotz 	acpi_objects parameter;
643dc14d97bSMichael Lotz 	parameter.count = 1;
644dc14d97bSMichael Lotz 	parameter.pointer = &model;
645dc14d97bSMichael Lotz 
646dc14d97bSMichael Lotz 	dprintf("setting ACPI interrupt model to %s\n",
647dc14d97bSMichael Lotz 		interruptModel == 0 ? "PIC"
648dc14d97bSMichael Lotz 		: (interruptModel == 1 ? "APIC"
649dc14d97bSMichael Lotz 		: (interruptModel == 2 ? "SAPIC"
650dc14d97bSMichael Lotz 		: "unknown")));
651dc14d97bSMichael Lotz 
652dc14d97bSMichael Lotz 	return acpiModule->evaluate_method(NULL, "\\_PIC", &parameter, NULL);
653dc14d97bSMichael Lotz }
654dc14d97bSMichael Lotz 
655dc14d97bSMichael Lotz 
656a56cbb2aSMichael Lotz bool
657a56cbb2aSMichael Lotz ioapic_is_interrupt_available(int32 gsi)
658a56cbb2aSMichael Lotz {
65986d59c04SMichael Lotz 	struct ioapic* ioapic = find_ioapic(gsi);
66086d59c04SMichael Lotz 	if (ioapic == NULL)
66186d59c04SMichael Lotz 		return false;
66286d59c04SMichael Lotz 
66386d59c04SMichael Lotz 	uint8 pin = gsi - ioapic->global_interrupt_base;
66486d59c04SMichael Lotz 	return (ioapic->nmi_mask & ((uint64)1 << pin)) == 0;
665a56cbb2aSMichael Lotz }
666a56cbb2aSMichael Lotz 
667a56cbb2aSMichael Lotz 
668dc14d97bSMichael Lotz void
669dc14d97bSMichael Lotz ioapic_init(kernel_args* args)
670dc14d97bSMichael Lotz {
671dc14d97bSMichael Lotz 	static const interrupt_controller ioapicController = {
672dc14d97bSMichael Lotz 		"82093AA IOAPIC",
673dc14d97bSMichael Lotz 		&ioapic_enable_io_interrupt,
674dc14d97bSMichael Lotz 		&ioapic_disable_io_interrupt,
675dc14d97bSMichael Lotz 		&ioapic_configure_io_interrupt,
676dc14d97bSMichael Lotz 		&ioapic_is_spurious_interrupt,
677dc14d97bSMichael Lotz 		&ioapic_is_level_triggered_interrupt,
678*d897a478SPawel Dziepak 		&ioapic_end_of_interrupt,
679*d897a478SPawel Dziepak 		&ioapic_assign_interrupt_to_cpu,
680dc14d97bSMichael Lotz 	};
681dc14d97bSMichael Lotz 
68262d36f98SAlex Smith 	if (args->arch_args.apic == NULL)
683dc14d97bSMichael Lotz 		return;
684dc14d97bSMichael Lotz 
6858908aef9SMichael Lotz 	if (args->arch_args.ioapic_phys == 0) {
6868908aef9SMichael Lotz 		dprintf("no io-apics available, not using io-apics for interrupt "
6878908aef9SMichael Lotz 			"routing\n");
6888908aef9SMichael Lotz 		return;
6898908aef9SMichael Lotz 	}
6908908aef9SMichael Lotz 
691dc14d97bSMichael Lotz 	if (get_safemode_boolean(B_SAFEMODE_DISABLE_IOAPIC, false)) {
6925d01e61aSMichael Lotz 		dprintf("io-apics explicitly disabled, not using io-apics for "
6935d01e61aSMichael Lotz 			"interrupt routing\n");
694dc14d97bSMichael Lotz 		return;
695dc14d97bSMichael Lotz 	}
696dc14d97bSMichael Lotz 
697dc14d97bSMichael Lotz 	// load acpi module
698dc14d97bSMichael Lotz 	status_t status;
699dc14d97bSMichael Lotz 	acpi_module_info* acpiModule;
700dc14d97bSMichael Lotz 	status = get_module(B_ACPI_MODULE_NAME, (module_info**)&acpiModule);
701dc14d97bSMichael Lotz 	if (status != B_OK) {
7025d01e61aSMichael Lotz 		dprintf("acpi module not available, not configuring io-apics\n");
703dc14d97bSMichael Lotz 		return;
704dc14d97bSMichael Lotz 	}
705dc14d97bSMichael Lotz 	BPrivate::CObjectDeleter<const char, status_t>
706dc14d97bSMichael Lotz 		acpiModulePutter(B_ACPI_MODULE_NAME, put_module);
707dc14d97bSMichael Lotz 
708fb5a1727SMichael Lotz 	acpi_table_madt* madt = NULL;
709fb5a1727SMichael Lotz 	if (acpiModule->get_table(ACPI_SIG_MADT, 0, (void**)&madt) != B_OK) {
710fb5a1727SMichael Lotz 		dprintf("failed to get MADT from ACPI, not configuring io-apics\n");
711fb5a1727SMichael Lotz 		return;
712fb5a1727SMichael Lotz 	}
713fb5a1727SMichael Lotz 
714fb5a1727SMichael Lotz 	status = acpi_enumerate_ioapics(madt);
715d11e32a8SMichael Lotz 	if (status != B_OK) {
716ca67ddb3SMichael Lotz 		// We don't treat this case as fatal just yet. If we are able to
717ca67ddb3SMichael Lotz 		// route everything with the available IO-APICs we're fine, if not
718ca67ddb3SMichael Lotz 		// we will fail at the routing preparation stage further down.
719ca67ddb3SMichael Lotz 		dprintf("failed to enumerate all io-apics, working with what we got\n");
720d11e32a8SMichael Lotz 	}
721d11e32a8SMichael Lotz 
722d11e32a8SMichael Lotz 	// switch to the APIC interrupt model before retrieving the IRQ routing
723dc14d97bSMichael Lotz 	// table as it will return different settings depending on the model
724dc14d97bSMichael Lotz 	status = acpi_set_interrupt_model(acpiModule, ACPI_INTERRUPT_MODEL_APIC);
725dc14d97bSMichael Lotz 	if (status != B_OK) {
726dc14d97bSMichael Lotz 		dprintf("failed to put ACPI into APIC interrupt model, ignoring\n");
727dc14d97bSMichael Lotz 		// don't abort, as the _PIC method is optional and as long as there
728dc14d97bSMichael Lotz 		// aren't different routings based on it this is non-fatal
729dc14d97bSMichael Lotz 	}
730dc14d97bSMichael Lotz 
731dc14d97bSMichael Lotz 	IRQRoutingTable table;
732dc14d97bSMichael Lotz 	status = prepare_irq_routing(acpiModule, table,
733eda74390SMichael Lotz 		&ioapic_is_interrupt_available);
734dc14d97bSMichael Lotz 	if (status != B_OK) {
7355d01e61aSMichael Lotz 		dprintf("IRQ routing preparation failed, not configuring io-apics\n");
736dc14d97bSMichael Lotz 		acpi_set_interrupt_model(acpiModule, ACPI_INTERRUPT_MODEL_PIC);
737dc14d97bSMichael Lotz 			// revert to PIC interrupt model just in case
738dc14d97bSMichael Lotz 		return;
739dc14d97bSMichael Lotz 	}
740dc14d97bSMichael Lotz 
7415d01e61aSMichael Lotz 	// use the boot CPU as the target for all interrupts
74263475256SMichael Lotz 	uint8 targetAPIC = args->arch_args.cpu_apic_id[0];
7435d01e61aSMichael Lotz 
7448908aef9SMichael Lotz 	struct ioapic* current = sIOAPICs;
7455d01e61aSMichael Lotz 	while (current != NULL) {
7465d01e61aSMichael Lotz 		status = ioapic_initialize_ioapic(*current, targetAPIC);
7475d01e61aSMichael Lotz 		if (status != B_OK) {
7485d01e61aSMichael Lotz 			panic("failed to initialize io-apic %u", current->number);
7495d01e61aSMichael Lotz 			acpi_set_interrupt_model(acpiModule, ACPI_INTERRUPT_MODEL_PIC);
7505d01e61aSMichael Lotz 			return;
7515d01e61aSMichael Lotz 		}
7525d01e61aSMichael Lotz 
7535d01e61aSMichael Lotz 		current = current->next;
7545d01e61aSMichael Lotz 	}
7555d01e61aSMichael Lotz 
756cb4e75d3SMichael Lotz #ifdef TRACE_IOAPIC
757cb4e75d3SMichael Lotz 	dprintf("trying interrupt routing:\n");
758cb4e75d3SMichael Lotz 	print_irq_routing_table(table);
759cb4e75d3SMichael Lotz #endif
760cb4e75d3SMichael Lotz 
761dc14d97bSMichael Lotz 	status = enable_irq_routing(acpiModule, table);
762dc14d97bSMichael Lotz 	if (status != B_OK) {
763dc14d97bSMichael Lotz 		panic("failed to enable IRQ routing");
764dc14d97bSMichael Lotz 		// if it failed early on it might still work in PIC mode
765dc14d97bSMichael Lotz 		acpi_set_interrupt_model(acpiModule, ACPI_INTERRUPT_MODEL_PIC);
766dc14d97bSMichael Lotz 		return;
767dc14d97bSMichael Lotz 	}
768dc14d97bSMichael Lotz 
769cb4e75d3SMichael Lotz 	print_irq_routing_table(table);
770cb4e75d3SMichael Lotz 
771fb5a1727SMichael Lotz 	// configure the source overrides, but let the PCI config below override it
772fb5a1727SMichael Lotz 	acpi_configure_source_overrides(madt);
773fb5a1727SMichael Lotz 
7745d01e61aSMichael Lotz 	// configure IO-APIC interrupts from PCI routing table
775dc14d97bSMichael Lotz 	for (int i = 0; i < table.Count(); i++) {
776dc14d97bSMichael Lotz 		irq_routing_entry& entry = table.ElementAt(i);
777dc14d97bSMichael Lotz 		ioapic_configure_io_interrupt(entry.irq,
778dc14d97bSMichael Lotz 			entry.polarity | entry.trigger_mode);
779dc14d97bSMichael Lotz 	}
780dc14d97bSMichael Lotz 
781a56cbb2aSMichael Lotz 	// kill the local ints on the local APIC
782a56cbb2aSMichael Lotz 	apic_disable_local_ints();
783a56cbb2aSMichael Lotz 		// TODO: This uses the assumption that our init is running on the
784a56cbb2aSMichael Lotz 		// boot CPU and only the boot CPU has the local ints configured
785a56cbb2aSMichael Lotz 		// because it was running in legacy PIC mode. Possibly the other
786a56cbb2aSMichael Lotz 		// local APICs of the other CPUs have them configured as well. It
787a56cbb2aSMichael Lotz 		// shouldn't really harm, but should eventually be corrected.
788a56cbb2aSMichael Lotz 
789dc14d97bSMichael Lotz 	// disable the legacy PIC
790fb5a1727SMichael Lotz 	uint16 legacyInterrupts;
791fb5a1727SMichael Lotz 	pic_disable(legacyInterrupts);
792fb5a1727SMichael Lotz 
793fb5a1727SMichael Lotz 	// enable previsouly enabled legacy interrupts
794fb5a1727SMichael Lotz 	for (uint8 i = 0; i < 16; i++) {
795fb5a1727SMichael Lotz 		if ((legacyInterrupts & (1 << i)) != 0)
796fb5a1727SMichael Lotz 			ioapic_enable_io_interrupt(i);
797fb5a1727SMichael Lotz 	}
798dc14d97bSMichael Lotz 
799fc2d7cb0SMichael Lotz 	// mark the interrupt vectors reserved so they aren't used for other stuff
800fc2d7cb0SMichael Lotz 	current = sIOAPICs;
801fc2d7cb0SMichael Lotz 	while (current != NULL) {
802fc2d7cb0SMichael Lotz 		reserve_io_interrupt_vectors(current->max_redirection_entry + 1,
8036a164daaSPawel Dziepak 			current->global_interrupt_base, INTERRUPT_TYPE_IRQ);
804fc2d7cb0SMichael Lotz 		current = current->next;
805fc2d7cb0SMichael Lotz 	}
806fc2d7cb0SMichael Lotz 
807dc14d97bSMichael Lotz 	// prefer the ioapic over the normal pic
8085d01e61aSMichael Lotz 	dprintf("using io-apics for interrupt routing\n");
809dc14d97bSMichael Lotz 	arch_int_set_interrupt_controller(ioapicController);
810dc14d97bSMichael Lotz }
811