xref: /haiku/src/system/kernel/arch/x86/ioapic.cpp (revision eda743903ea64c85411430c0a7e660d77f51e246)
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>
21dc14d97bSMichael Lotz #include <arch/x86/pic.h>
22dc14d97bSMichael Lotz 
23d11e32a8SMichael Lotz // to gain access to the ACPICA types
24d11e32a8SMichael Lotz #include "acpi.h"
25d11e32a8SMichael Lotz 
26dc14d97bSMichael Lotz 
27dc14d97bSMichael Lotz //#define TRACE_IOAPIC
28dc14d97bSMichael Lotz #ifdef TRACE_IOAPIC
29dc14d97bSMichael Lotz #	define TRACE(x) dprintf x
30dc14d97bSMichael Lotz #else
31dc14d97bSMichael Lotz #	define TRACE(x) ;
32dc14d97bSMichael Lotz #endif
33dc14d97bSMichael Lotz 
34dc14d97bSMichael Lotz 
35dc14d97bSMichael Lotz // ACPI interrupt models
36dc14d97bSMichael Lotz #define ACPI_INTERRUPT_MODEL_PIC	0
37dc14d97bSMichael Lotz #define ACPI_INTERRUPT_MODEL_APIC	1
38dc14d97bSMichael Lotz #define ACPI_INTERRUPT_MODEL_SAPIC	2
39dc14d97bSMichael Lotz 
40dc14d97bSMichael Lotz 
41dc14d97bSMichael Lotz // Definitions for a 82093AA IO APIC controller
42dc14d97bSMichael Lotz #define IO_APIC_IDENTIFICATION				0x00
43dc14d97bSMichael Lotz #define IO_APIC_VERSION						0x01
44dc14d97bSMichael Lotz #define IO_APIC_ARBITRATION					0x02
45dc14d97bSMichael Lotz #define IO_APIC_REDIRECTION_TABLE			0x10 // entry = base + 2 * index
46dc14d97bSMichael Lotz 
47dc14d97bSMichael Lotz // Fields for the version register
48dc14d97bSMichael Lotz #define IO_APIC_VERSION_SHIFT				0
49dc14d97bSMichael Lotz #define IO_APIC_VERSION_MASK				0xff
50dc14d97bSMichael Lotz #define IO_APIC_MAX_REDIRECTION_ENTRY_SHIFT	16
51dc14d97bSMichael Lotz #define IO_APIC_MAX_REDIRECTION_ENTRY_MASK	0xff
52dc14d97bSMichael Lotz 
53dc14d97bSMichael Lotz // Fields of each redirection table entry
54dc14d97bSMichael Lotz #define IO_APIC_DESTINATION_FIELD_SHIFT		56
55dc14d97bSMichael Lotz #define IO_APIC_DESTINATION_FIELD_MASK		0x0f
56dc14d97bSMichael Lotz #define IO_APIC_INTERRUPT_MASK_SHIFT		16
57dc14d97bSMichael Lotz #define IO_APIC_INTERRUPT_MASKED			1
58dc14d97bSMichael Lotz #define IO_APIC_INTERRUPT_UNMASKED			0
59dc14d97bSMichael Lotz #define IO_APIC_TRIGGER_MODE_SHIFT			15
60dc14d97bSMichael Lotz #define IO_APIC_TRIGGER_MODE_EDGE			0
61dc14d97bSMichael Lotz #define IO_APIC_TRIGGER_MODE_LEVEL			1
62dc14d97bSMichael Lotz #define IO_APIC_REMOTE_IRR_SHIFT			14
63dc14d97bSMichael Lotz #define IO_APIC_PIN_POLARITY_SHIFT			13
64dc14d97bSMichael Lotz #define IO_APIC_PIN_POLARITY_HIGH_ACTIVE	0
65dc14d97bSMichael Lotz #define IO_APIC_PIN_POLARITY_LOW_ACTIVE		1
66dc14d97bSMichael Lotz #define IO_APIC_DELIVERY_STATUS_SHIFT		12
67dc14d97bSMichael Lotz #define IO_APIC_DELIVERY_STATUS_IDLE		0
68dc14d97bSMichael Lotz #define IO_APIC_DELIVERY_STATUS_PENDING		1
69dc14d97bSMichael Lotz #define IO_APIC_DESTINATION_MODE_SHIFT		11
70dc14d97bSMichael Lotz #define IO_APIC_DESTINATION_MODE_PHYSICAL	0
71dc14d97bSMichael Lotz #define IO_APIC_DESTINATION_MODE_LOGICAL	1
72dc14d97bSMichael Lotz #define IO_APIC_DELIVERY_MODE_SHIFT			8
73dc14d97bSMichael Lotz #define IO_APIC_DELIVERY_MODE_MASK			0x07
74dc14d97bSMichael Lotz #define IO_APIC_DELIVERY_MODE_FIXED			0
75dc14d97bSMichael Lotz #define IO_APIC_DELIVERY_MODE_LOWEST_PRIO	1
76dc14d97bSMichael Lotz #define IO_APIC_DELIVERY_MODE_SMI			2
77dc14d97bSMichael Lotz #define IO_APIC_DELIVERY_MODE_NMI			4
78dc14d97bSMichael Lotz #define IO_APIC_DELIVERY_MODE_INIT			5
79dc14d97bSMichael Lotz #define IO_APIC_DELIVERY_MODE_EXT_INT		7
80dc14d97bSMichael Lotz #define IO_APIC_INTERRUPT_VECTOR_SHIFT		0
81dc14d97bSMichael Lotz #define IO_APIC_INTERRUPT_VECTOR_MASK		0xff
82dc14d97bSMichael Lotz 
83dc14d97bSMichael Lotz 
845d01e61aSMichael Lotz struct ioapic_registers {
85dc14d97bSMichael Lotz 	volatile uint32	io_register_select;
86dc14d97bSMichael Lotz 	uint32			reserved[3];
87dc14d97bSMichael Lotz 	volatile uint32	io_window_register;
885d01e61aSMichael Lotz };
89dc14d97bSMichael Lotz 
90dc14d97bSMichael Lotz 
915d01e61aSMichael Lotz struct ioapic {
925d01e61aSMichael Lotz 	uint8				number;
935d01e61aSMichael Lotz 	uint8				max_redirection_entry;
945d01e61aSMichael Lotz 	uint8				global_interrupt_base;
955d01e61aSMichael Lotz 	uint8				global_interrupt_last;
965d01e61aSMichael Lotz 	uint64				level_triggered_mask;
975d01e61aSMichael Lotz 
985d01e61aSMichael Lotz 	area_id				register_area;
995d01e61aSMichael Lotz 	ioapic_registers*	registers;
1005d01e61aSMichael Lotz 
1015d01e61aSMichael Lotz 	ioapic*				next;
1025d01e61aSMichael Lotz };
1035d01e61aSMichael Lotz 
1045d01e61aSMichael Lotz 
1055d01e61aSMichael Lotz static ioapic sIOAPICs = { 0, 0, 0, 0, 0, -1, NULL, NULL };
106dc14d97bSMichael Lotz 
107dc14d97bSMichael Lotz 
108dc14d97bSMichael Lotz // #pragma mark - I/O APIC
109dc14d97bSMichael Lotz 
110dc14d97bSMichael Lotz 
1115d01e61aSMichael Lotz static inline struct ioapic*
1125d01e61aSMichael Lotz find_ioapic(int32 gsi)
113dc14d97bSMichael Lotz {
1145d01e61aSMichael Lotz 	if (gsi < 0)
1155d01e61aSMichael Lotz 		return NULL;
1165d01e61aSMichael Lotz 
1175d01e61aSMichael Lotz 	struct ioapic* current = &sIOAPICs;
1185d01e61aSMichael Lotz 	while (current != NULL) {
1195d01e61aSMichael Lotz 		if (gsi >= current->global_interrupt_base
1205d01e61aSMichael Lotz 			&& gsi <= current->global_interrupt_last) {
1215d01e61aSMichael Lotz 			return current;
1225d01e61aSMichael Lotz 		}
1235d01e61aSMichael Lotz 
1245d01e61aSMichael Lotz 		current = current->next;
1255d01e61aSMichael Lotz 	}
1265d01e61aSMichael Lotz 
1275d01e61aSMichael Lotz 	return NULL;
1285d01e61aSMichael Lotz }
1295d01e61aSMichael Lotz 
1305d01e61aSMichael Lotz 
1315d01e61aSMichael Lotz static inline uint32
1325d01e61aSMichael Lotz ioapic_read_32(struct ioapic& ioapic, uint8 registerSelect)
1335d01e61aSMichael Lotz {
1345d01e61aSMichael Lotz 	ioapic.registers->io_register_select = registerSelect;
1355d01e61aSMichael Lotz 	return ioapic.registers->io_window_register;
136dc14d97bSMichael Lotz }
137dc14d97bSMichael Lotz 
138dc14d97bSMichael Lotz 
139dc14d97bSMichael Lotz static inline void
1405d01e61aSMichael Lotz ioapic_write_32(struct ioapic& ioapic, uint8 registerSelect, uint32 value)
141dc14d97bSMichael Lotz {
1425d01e61aSMichael Lotz 	ioapic.registers->io_register_select = registerSelect;
1435d01e61aSMichael Lotz 	ioapic.registers->io_window_register = value;
144dc14d97bSMichael Lotz }
145dc14d97bSMichael Lotz 
146dc14d97bSMichael Lotz 
147dc14d97bSMichael Lotz static inline uint64
1485d01e61aSMichael Lotz ioapic_read_64(struct ioapic& ioapic, uint8 registerSelect)
149dc14d97bSMichael Lotz {
1505d01e61aSMichael Lotz 	ioapic.registers->io_register_select = registerSelect + 1;
1515d01e61aSMichael Lotz 	uint64 result = ioapic.registers->io_window_register;
152dc14d97bSMichael Lotz 	result <<= 32;
1535d01e61aSMichael Lotz 	ioapic.registers->io_register_select = registerSelect;
1545d01e61aSMichael Lotz 	result |= ioapic.registers->io_window_register;
155dc14d97bSMichael Lotz 	return result;
156dc14d97bSMichael Lotz }
157dc14d97bSMichael Lotz 
158dc14d97bSMichael Lotz 
159dc14d97bSMichael Lotz static inline void
1605d01e61aSMichael Lotz ioapic_write_64(struct ioapic& ioapic, uint8 registerSelect, uint64 value)
161dc14d97bSMichael Lotz {
1625d01e61aSMichael Lotz 	ioapic.registers->io_register_select = registerSelect;
1635d01e61aSMichael Lotz 	ioapic.registers->io_window_register = (uint32)value;
1645d01e61aSMichael Lotz 	ioapic.registers->io_register_select = registerSelect + 1;
1655d01e61aSMichael Lotz 	ioapic.registers->io_window_register = (uint32)(value >> 32);
166dc14d97bSMichael Lotz }
167dc14d97bSMichael Lotz 
168dc14d97bSMichael Lotz 
169dc14d97bSMichael Lotz static bool
170*eda74390SMichael Lotz ioapic_is_interrupt_available(int32 gsi)
171*eda74390SMichael Lotz {
172*eda74390SMichael Lotz 	return find_ioapic(gsi) != NULL;
173*eda74390SMichael Lotz }
174*eda74390SMichael Lotz 
175*eda74390SMichael Lotz 
176*eda74390SMichael Lotz static bool
177*eda74390SMichael Lotz ioapic_is_spurious_interrupt(int32 gsi)
178dc14d97bSMichael Lotz {
179dc14d97bSMichael Lotz 	// the spurious interrupt vector is initialized to the max value in smp
180*eda74390SMichael Lotz 	return gsi == 0xff - ARCH_INTERRUPT_BASE;
181dc14d97bSMichael Lotz }
182dc14d97bSMichael Lotz 
183dc14d97bSMichael Lotz 
184dc14d97bSMichael Lotz static bool
1855d01e61aSMichael Lotz ioapic_is_level_triggered_interrupt(int32 gsi)
186dc14d97bSMichael Lotz {
1875d01e61aSMichael Lotz 	struct ioapic* ioapic = find_ioapic(gsi);
1885d01e61aSMichael Lotz 	if (ioapic == NULL)
189dc14d97bSMichael Lotz 		return false;
190dc14d97bSMichael Lotz 
1915d01e61aSMichael Lotz 	uint8 pin = gsi - ioapic->global_interrupt_base;
1925d01e61aSMichael Lotz 	return (ioapic->level_triggered_mask & (1 << pin)) != 0;
193dc14d97bSMichael Lotz }
194dc14d97bSMichael Lotz 
195dc14d97bSMichael Lotz 
196dc14d97bSMichael Lotz static bool
197dc14d97bSMichael Lotz ioapic_end_of_interrupt(int32 num)
198dc14d97bSMichael Lotz {
199dc14d97bSMichael Lotz 	apic_end_of_interrupt();
200dc14d97bSMichael Lotz 	return true;
201dc14d97bSMichael Lotz }
202dc14d97bSMichael Lotz 
203dc14d97bSMichael Lotz 
204dc14d97bSMichael Lotz static void
2055d01e61aSMichael Lotz ioapic_enable_io_interrupt(int32 gsi)
206dc14d97bSMichael Lotz {
2075d01e61aSMichael Lotz 	struct ioapic* ioapic = find_ioapic(gsi);
2085d01e61aSMichael Lotz 	if (ioapic == NULL)
209dc14d97bSMichael Lotz 		return;
210dc14d97bSMichael Lotz 
2115d01e61aSMichael Lotz 	uint8 pin = gsi - ioapic->global_interrupt_base;
2125d01e61aSMichael Lotz 	TRACE(("ioapic_enable_io_interrupt: gsi %ld -> io-apic %u pin %u\n",
2135d01e61aSMichael Lotz 		gsi, ioapic->number, pin));
214dc14d97bSMichael Lotz 
2155d01e61aSMichael Lotz 	uint64 entry = ioapic_read_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2);
216dc14d97bSMichael Lotz 	entry &= ~(1 << IO_APIC_INTERRUPT_MASK_SHIFT);
217dc14d97bSMichael Lotz 	entry |= IO_APIC_INTERRUPT_UNMASKED << IO_APIC_INTERRUPT_MASK_SHIFT;
2185d01e61aSMichael Lotz 	ioapic_write_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2, entry);
2195d01e61aSMichael Lotz 		// TODO: Take writing order into account! We must not unmask the entry
2205d01e61aSMichael Lotz 		// before the other half is valid.
221dc14d97bSMichael Lotz }
222dc14d97bSMichael Lotz 
223dc14d97bSMichael Lotz 
224dc14d97bSMichael Lotz static void
2255d01e61aSMichael Lotz ioapic_disable_io_interrupt(int32 gsi)
226dc14d97bSMichael Lotz {
2275d01e61aSMichael Lotz 	struct ioapic* ioapic = find_ioapic(gsi);
2285d01e61aSMichael Lotz 	if (ioapic == NULL)
229dc14d97bSMichael Lotz 		return;
230dc14d97bSMichael Lotz 
2315d01e61aSMichael Lotz 	uint8 pin = gsi - ioapic->global_interrupt_base;
2325d01e61aSMichael Lotz 	TRACE(("ioapic_disable_io_interrupt: gsi %ld -> io-apic %u pin %u\n",
2335d01e61aSMichael Lotz 		gsi, ioapic->number, pin));
234dc14d97bSMichael Lotz 
2355d01e61aSMichael Lotz 	uint64 entry = ioapic_read_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2);
236dc14d97bSMichael Lotz 	entry &= ~(1 << IO_APIC_INTERRUPT_MASK_SHIFT);
237dc14d97bSMichael Lotz 	entry |= IO_APIC_INTERRUPT_MASKED << IO_APIC_INTERRUPT_MASK_SHIFT;
2385d01e61aSMichael Lotz 	ioapic_write_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2, entry);
2395d01e61aSMichael Lotz 		// TODO: Take writing order into account! We must not modify the entry
2405d01e61aSMichael Lotz 		// before it is masked.
241dc14d97bSMichael Lotz }
242dc14d97bSMichael Lotz 
243dc14d97bSMichael Lotz 
244dc14d97bSMichael Lotz static void
2455d01e61aSMichael Lotz ioapic_configure_io_interrupt(int32 gsi, uint32 config)
246dc14d97bSMichael Lotz {
2475d01e61aSMichael Lotz 	struct ioapic* ioapic = find_ioapic(gsi);
2485d01e61aSMichael Lotz 	if (ioapic == NULL)
249dc14d97bSMichael Lotz 		return;
250dc14d97bSMichael Lotz 
2515d01e61aSMichael Lotz 	uint8 pin = gsi - ioapic->global_interrupt_base;
2525d01e61aSMichael Lotz 	TRACE(("ioapic_configure_io_interrupt: gsi %ld -> io-apic %u pin %u; "
2535d01e61aSMichael Lotz 		"config 0x%08lx\n", gsi, ioapic->number, pin, config));
254dc14d97bSMichael Lotz 
2555d01e61aSMichael Lotz 	uint64 entry = ioapic_read_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2);
256dc14d97bSMichael Lotz 	entry &= ~((1 << IO_APIC_TRIGGER_MODE_SHIFT)
257dc14d97bSMichael Lotz 		| (1 << IO_APIC_PIN_POLARITY_SHIFT)
258dc14d97bSMichael Lotz 		| (IO_APIC_INTERRUPT_VECTOR_MASK << IO_APIC_INTERRUPT_VECTOR_SHIFT));
259dc14d97bSMichael Lotz 
260dc14d97bSMichael Lotz 	if (config & B_LEVEL_TRIGGERED) {
261dc14d97bSMichael Lotz 		entry |= (IO_APIC_TRIGGER_MODE_LEVEL << IO_APIC_TRIGGER_MODE_SHIFT);
2625d01e61aSMichael Lotz 		ioapic->level_triggered_mask |= (1 << pin);
263dc14d97bSMichael Lotz 	} else {
264dc14d97bSMichael Lotz 		entry |= (IO_APIC_TRIGGER_MODE_EDGE << IO_APIC_TRIGGER_MODE_SHIFT);
2655d01e61aSMichael Lotz 		ioapic->level_triggered_mask &= ~(1 << pin);
266dc14d97bSMichael Lotz 	}
267dc14d97bSMichael Lotz 
268dc14d97bSMichael Lotz 	if (config & B_LOW_ACTIVE_POLARITY)
269dc14d97bSMichael Lotz 		entry |= (IO_APIC_PIN_POLARITY_LOW_ACTIVE << IO_APIC_PIN_POLARITY_SHIFT);
270dc14d97bSMichael Lotz 	else
271dc14d97bSMichael Lotz 		entry |= (IO_APIC_PIN_POLARITY_HIGH_ACTIVE << IO_APIC_PIN_POLARITY_SHIFT);
272dc14d97bSMichael Lotz 
273dc14d97bSMichael Lotz 	entry |= (pin + ARCH_INTERRUPT_BASE) << IO_APIC_INTERRUPT_VECTOR_SHIFT;
2745d01e61aSMichael Lotz 	ioapic_write_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2, entry);
2755d01e61aSMichael Lotz }
2765d01e61aSMichael Lotz 
2775d01e61aSMichael Lotz 
2785d01e61aSMichael Lotz static status_t
2795d01e61aSMichael Lotz ioapic_map_ioapic(struct ioapic& ioapic, phys_addr_t physicalAddress)
2805d01e61aSMichael Lotz {
2815d01e61aSMichael Lotz 	ioapic.register_area = vm_map_physical_memory(B_SYSTEM_TEAM, "io-apic",
2825d01e61aSMichael Lotz 		(void**)&ioapic.registers, ioapic.registers != NULL ? B_EXACT_ADDRESS
2835d01e61aSMichael Lotz 		: B_ANY_KERNEL_ADDRESS, B_PAGE_SIZE, B_KERNEL_READ_AREA
284d11e32a8SMichael Lotz 		| B_KERNEL_WRITE_AREA, physicalAddress, ioapic.registers != NULL);
2855d01e61aSMichael Lotz 	if (ioapic.register_area < 0) {
2865d01e61aSMichael Lotz 		panic("mapping io-apic %u failed", ioapic.number);
2875d01e61aSMichael Lotz 		return ioapic.register_area;
2885d01e61aSMichael Lotz 	}
2895d01e61aSMichael Lotz 
290d11e32a8SMichael Lotz 	dprintf("mapped io-apic %u to %p\n", ioapic.number, ioapic.registers);
291d11e32a8SMichael Lotz 
2925d01e61aSMichael Lotz 	uint32 version = ioapic_read_32(ioapic, IO_APIC_VERSION);
2935d01e61aSMichael Lotz 	if (version == 0xffffffff) {
2945d01e61aSMichael Lotz 		dprintf("io-apic %u seems inaccessible, not using it\n",
2955d01e61aSMichael Lotz 			ioapic.number);
2965d01e61aSMichael Lotz 		vm_delete_area(B_SYSTEM_TEAM, ioapic.register_area, true);
2975d01e61aSMichael Lotz 		ioapic.register_area = -1;
2985d01e61aSMichael Lotz 		ioapic.registers = NULL;
2995d01e61aSMichael Lotz 		return B_ERROR;
3005d01e61aSMichael Lotz 	}
3015d01e61aSMichael Lotz 
3025d01e61aSMichael Lotz 	ioapic.max_redirection_entry
3035d01e61aSMichael Lotz 		= ((version >> IO_APIC_MAX_REDIRECTION_ENTRY_SHIFT)
3045d01e61aSMichael Lotz 			& IO_APIC_MAX_REDIRECTION_ENTRY_MASK);
3055d01e61aSMichael Lotz 
3065d01e61aSMichael Lotz 	ioapic.global_interrupt_last
3075d01e61aSMichael Lotz 		= ioapic.global_interrupt_base + ioapic.max_redirection_entry;
3085d01e61aSMichael Lotz 
3095d01e61aSMichael Lotz 	dprintf("io-apic %u has range %u-%u, %u entries\n", ioapic.number,
3105d01e61aSMichael Lotz 		ioapic.global_interrupt_base, ioapic.global_interrupt_last,
3115d01e61aSMichael Lotz 		ioapic.max_redirection_entry + 1);
3125d01e61aSMichael Lotz 
3135d01e61aSMichael Lotz 	return B_OK;
3145d01e61aSMichael Lotz }
3155d01e61aSMichael Lotz 
3165d01e61aSMichael Lotz 
3175d01e61aSMichael Lotz static status_t
3185d01e61aSMichael Lotz ioapic_initialize_ioapic(struct ioapic& ioapic, uint64 targetAPIC)
3195d01e61aSMichael Lotz {
3205d01e61aSMichael Lotz 	// program the interrupt vectors of the io-apic
3215d01e61aSMichael Lotz 	ioapic.level_triggered_mask = 0;
3225d01e61aSMichael Lotz 	for (uint8 i = 0; i <= ioapic.max_redirection_entry; i++) {
3235d01e61aSMichael Lotz 		// initialize everything to deliver to the boot CPU in physical mode
3245d01e61aSMichael Lotz 		// and masked until explicitly enabled through enable_io_interrupt()
3255d01e61aSMichael Lotz 		uint64 entry = (targetAPIC << IO_APIC_DESTINATION_FIELD_SHIFT)
3265d01e61aSMichael Lotz 			| (IO_APIC_INTERRUPT_MASKED << IO_APIC_INTERRUPT_MASK_SHIFT)
3275d01e61aSMichael Lotz 			| (IO_APIC_DESTINATION_MODE_PHYSICAL << IO_APIC_DESTINATION_MODE_SHIFT)
3285d01e61aSMichael Lotz 			| ((i + ARCH_INTERRUPT_BASE) << IO_APIC_INTERRUPT_VECTOR_SHIFT);
3295d01e61aSMichael Lotz 
3305d01e61aSMichael Lotz 		if (i == 0) {
3315d01e61aSMichael Lotz 			// make redirection entry 0 into an external interrupt
3325d01e61aSMichael Lotz 			entry |= (IO_APIC_TRIGGER_MODE_EDGE << IO_APIC_TRIGGER_MODE_SHIFT)
3335d01e61aSMichael Lotz 				| (IO_APIC_PIN_POLARITY_HIGH_ACTIVE << IO_APIC_PIN_POLARITY_SHIFT)
3345d01e61aSMichael Lotz 				| (IO_APIC_DELIVERY_MODE_EXT_INT << IO_APIC_DELIVERY_MODE_SHIFT);
3355d01e61aSMichael Lotz 		} else if (i < 16) {
3365d01e61aSMichael Lotz 			// make 1-15 ISA interrupts
3375d01e61aSMichael Lotz 			entry |= (IO_APIC_TRIGGER_MODE_EDGE << IO_APIC_TRIGGER_MODE_SHIFT)
3385d01e61aSMichael Lotz 				| (IO_APIC_PIN_POLARITY_HIGH_ACTIVE << IO_APIC_PIN_POLARITY_SHIFT)
3395d01e61aSMichael Lotz 				| (IO_APIC_DELIVERY_MODE_FIXED << IO_APIC_DELIVERY_MODE_SHIFT);
3405d01e61aSMichael Lotz 		} else {
3415d01e61aSMichael Lotz 			// and the rest are PCI interrupts
3425d01e61aSMichael Lotz 			entry |= (IO_APIC_TRIGGER_MODE_LEVEL << IO_APIC_TRIGGER_MODE_SHIFT)
3435d01e61aSMichael Lotz 				| (IO_APIC_PIN_POLARITY_LOW_ACTIVE << IO_APIC_PIN_POLARITY_SHIFT)
3445d01e61aSMichael Lotz 				| (IO_APIC_DELIVERY_MODE_FIXED << IO_APIC_DELIVERY_MODE_SHIFT);
3455d01e61aSMichael Lotz 			ioapic.level_triggered_mask |= (1 << i);
3465d01e61aSMichael Lotz 		}
3475d01e61aSMichael Lotz 
3485d01e61aSMichael Lotz 		ioapic_write_64(ioapic, IO_APIC_REDIRECTION_TABLE + 2 * i, entry);
3495d01e61aSMichael Lotz 	}
3505d01e61aSMichael Lotz 
3515d01e61aSMichael Lotz 	return B_OK;
352dc14d97bSMichael Lotz }
353dc14d97bSMichael Lotz 
354dc14d97bSMichael Lotz 
355dc14d97bSMichael Lotz static status_t
356d11e32a8SMichael Lotz acpi_enumerate_ioapics(acpi_module_info* acpi)
357d11e32a8SMichael Lotz {
358d11e32a8SMichael Lotz 	acpi_table_madt* madt = NULL;
359d11e32a8SMichael Lotz 	if (acpi->get_table(ACPI_SIG_MADT, 0, (void**)&madt) != B_OK) {
360d11e32a8SMichael Lotz 		dprintf("failed to get MADT from ACPI, not configuring io-apics\n");
361d11e32a8SMichael Lotz 		return B_ERROR;
362d11e32a8SMichael Lotz 	}
363d11e32a8SMichael Lotz 
364d11e32a8SMichael Lotz 	struct ioapic* lastIOAPIC = &sIOAPICs;
365d11e32a8SMichael Lotz 
366d11e32a8SMichael Lotz 	acpi_subtable_header* apicEntry
367d11e32a8SMichael Lotz 		= (acpi_subtable_header*)((uint8*)madt + sizeof(acpi_table_madt));
368d11e32a8SMichael Lotz 	void* end = ((uint8*)madt + madt->Header.Length);
369d11e32a8SMichael Lotz 	while (apicEntry < end) {
370d11e32a8SMichael Lotz 		switch (apicEntry->Type) {
371d11e32a8SMichael Lotz 			case ACPI_MADT_TYPE_IO_APIC:
372d11e32a8SMichael Lotz 			{
373d11e32a8SMichael Lotz 				acpi_madt_io_apic* info = (acpi_madt_io_apic*)apicEntry;
374d11e32a8SMichael Lotz 				dprintf("found io-apic with address %lu and global interrupt "
375d11e32a8SMichael Lotz 					"base %lu\n", (uint32)info->Address,
376d11e32a8SMichael Lotz 					(uint32)info->GlobalIrqBase);
377d11e32a8SMichael Lotz 
378d11e32a8SMichael Lotz 				if (find_ioapic((int32)info->GlobalIrqBase) != NULL) {
379d11e32a8SMichael Lotz 					// we've already mapped this IO-APIC (at boot)
380d11e32a8SMichael Lotz 					break;
381d11e32a8SMichael Lotz 				}
382d11e32a8SMichael Lotz 
383d11e32a8SMichael Lotz 				struct ioapic* ioapic
384d11e32a8SMichael Lotz 					= (struct ioapic*)malloc(sizeof(struct ioapic));
385d11e32a8SMichael Lotz 				if (ioapic == NULL) {
386d11e32a8SMichael Lotz 					dprintf("ran out of memory while allocating io-apic "
387d11e32a8SMichael Lotz 						"structure\n");
388d11e32a8SMichael Lotz 					return B_NO_MEMORY;
389d11e32a8SMichael Lotz 				}
390d11e32a8SMichael Lotz 
391d11e32a8SMichael Lotz 				ioapic->number = lastIOAPIC->number + 1;
392d11e32a8SMichael Lotz 				ioapic->global_interrupt_base = info->GlobalIrqBase;
393d11e32a8SMichael Lotz 				ioapic->registers = NULL;
394d11e32a8SMichael Lotz 				ioapic->next = NULL;
395d11e32a8SMichael Lotz 
396d11e32a8SMichael Lotz 				dprintf("mapping io-apic %u at physical address %p\n",
397d11e32a8SMichael Lotz 					ioapic->number, (void*)info->Address);
398d11e32a8SMichael Lotz 				status_t status = ioapic_map_ioapic(*ioapic, info->Address);
399d11e32a8SMichael Lotz 				if (status != B_OK) {
400d11e32a8SMichael Lotz 					free(ioapic);
401d11e32a8SMichael Lotz 					return status;
402d11e32a8SMichael Lotz 				}
403d11e32a8SMichael Lotz 
404d11e32a8SMichael Lotz 				lastIOAPIC->next = ioapic;
405d11e32a8SMichael Lotz 				lastIOAPIC = ioapic;
406d11e32a8SMichael Lotz 				break;
407d11e32a8SMichael Lotz 			}
408d11e32a8SMichael Lotz 		}
409d11e32a8SMichael Lotz 
410d11e32a8SMichael Lotz 		apicEntry
411d11e32a8SMichael Lotz 			= (acpi_subtable_header*)((uint8*)apicEntry + apicEntry->Length);
412d11e32a8SMichael Lotz 	}
413d11e32a8SMichael Lotz 
414d11e32a8SMichael Lotz 	return B_OK;
415d11e32a8SMichael Lotz }
416d11e32a8SMichael Lotz 
417d11e32a8SMichael Lotz 
418d11e32a8SMichael Lotz static status_t
419dc14d97bSMichael Lotz acpi_set_interrupt_model(acpi_module_info* acpiModule, uint32 interruptModel)
420dc14d97bSMichael Lotz {
421dc14d97bSMichael Lotz 	acpi_object_type model;
422dc14d97bSMichael Lotz 	model.object_type = ACPI_TYPE_INTEGER;
423dc14d97bSMichael Lotz 	model.data.integer = interruptModel;
424dc14d97bSMichael Lotz 
425dc14d97bSMichael Lotz 	acpi_objects parameter;
426dc14d97bSMichael Lotz 	parameter.count = 1;
427dc14d97bSMichael Lotz 	parameter.pointer = &model;
428dc14d97bSMichael Lotz 
429dc14d97bSMichael Lotz 	dprintf("setting ACPI interrupt model to %s\n",
430dc14d97bSMichael Lotz 		interruptModel == 0 ? "PIC"
431dc14d97bSMichael Lotz 		: (interruptModel == 1 ? "APIC"
432dc14d97bSMichael Lotz 		: (interruptModel == 2 ? "SAPIC"
433dc14d97bSMichael Lotz 		: "unknown")));
434dc14d97bSMichael Lotz 
435dc14d97bSMichael Lotz 	return acpiModule->evaluate_method(NULL, "\\_PIC", &parameter, NULL);
436dc14d97bSMichael Lotz }
437dc14d97bSMichael Lotz 
438dc14d97bSMichael Lotz 
439dc14d97bSMichael Lotz void
440dc14d97bSMichael Lotz ioapic_map(kernel_args* args)
441dc14d97bSMichael Lotz {
442dc14d97bSMichael Lotz 	if (args->arch_args.apic == NULL) {
443dc14d97bSMichael Lotz 		dprintf("no local apic available\n");
444dc14d97bSMichael Lotz 		return;
445dc14d97bSMichael Lotz 	}
446dc14d97bSMichael Lotz 
447dc14d97bSMichael Lotz 	if (args->arch_args.ioapic == NULL) {
4485d01e61aSMichael Lotz 		dprintf("no io-apic available, not using io-apics for interrupt "
4495d01e61aSMichael Lotz 			"routing\n");
450dc14d97bSMichael Lotz 		return;
451dc14d97bSMichael Lotz 	}
452dc14d97bSMichael Lotz 
4535d01e61aSMichael Lotz 	// map in the first IO-APIC
4545d01e61aSMichael Lotz 	sIOAPICs.registers = (ioapic_registers*)args->arch_args.ioapic;
4555d01e61aSMichael Lotz 	ioapic_map_ioapic(sIOAPICs, args->arch_args.ioapic_phys);
456dc14d97bSMichael Lotz }
457dc14d97bSMichael Lotz 
458dc14d97bSMichael Lotz 
459dc14d97bSMichael Lotz void
460dc14d97bSMichael Lotz ioapic_init(kernel_args* args)
461dc14d97bSMichael Lotz {
462dc14d97bSMichael Lotz 	static const interrupt_controller ioapicController = {
463dc14d97bSMichael Lotz 		"82093AA IOAPIC",
464dc14d97bSMichael Lotz 		&ioapic_enable_io_interrupt,
465dc14d97bSMichael Lotz 		&ioapic_disable_io_interrupt,
466dc14d97bSMichael Lotz 		&ioapic_configure_io_interrupt,
467dc14d97bSMichael Lotz 		&ioapic_is_spurious_interrupt,
468dc14d97bSMichael Lotz 		&ioapic_is_level_triggered_interrupt,
469dc14d97bSMichael Lotz 		&ioapic_end_of_interrupt
470dc14d97bSMichael Lotz 	};
471dc14d97bSMichael Lotz 
4725d01e61aSMichael Lotz 	if (sIOAPICs.register_area < 0 || sIOAPICs.registers == NULL)
473dc14d97bSMichael Lotz 		return;
474dc14d97bSMichael Lotz 
475dc14d97bSMichael Lotz #if 0
476dc14d97bSMichael Lotz 	if (get_safemode_boolean(B_SAFEMODE_DISABLE_IOAPIC, false)) {
4775d01e61aSMichael Lotz 		dprintf("io-apics explicitly disabled, not using io-apics for "
4785d01e61aSMichael Lotz 			"interrupt routing\n");
479dc14d97bSMichael Lotz 		return;
480dc14d97bSMichael Lotz 	}
481dc14d97bSMichael Lotz #else
482dc14d97bSMichael Lotz 	// TODO: This can be removed once IO-APIC code is broadly tested
483dc14d97bSMichael Lotz 	if (!get_safemode_boolean(B_SAFEMODE_ENABLE_IOAPIC, false)) {
4845d01e61aSMichael Lotz 		dprintf("io-apics not enabled, not using io-apics for interrupt "
485dc14d97bSMichael Lotz 			"routing\n");
486dc14d97bSMichael Lotz 		return;
487dc14d97bSMichael Lotz 	}
488dc14d97bSMichael Lotz #endif
489dc14d97bSMichael Lotz 
490dc14d97bSMichael Lotz 	// load acpi module
491dc14d97bSMichael Lotz 	status_t status;
492dc14d97bSMichael Lotz 	acpi_module_info* acpiModule;
493dc14d97bSMichael Lotz 	status = get_module(B_ACPI_MODULE_NAME, (module_info**)&acpiModule);
494dc14d97bSMichael Lotz 	if (status != B_OK) {
4955d01e61aSMichael Lotz 		dprintf("acpi module not available, not configuring io-apics\n");
496dc14d97bSMichael Lotz 		return;
497dc14d97bSMichael Lotz 	}
498dc14d97bSMichael Lotz 	BPrivate::CObjectDeleter<const char, status_t>
499dc14d97bSMichael Lotz 		acpiModulePutter(B_ACPI_MODULE_NAME, put_module);
500dc14d97bSMichael Lotz 
501d11e32a8SMichael Lotz 	status = acpi_enumerate_ioapics(acpiModule);
502d11e32a8SMichael Lotz 	if (status != B_OK) {
503d11e32a8SMichael Lotz 		dprintf("failed to enumerate all io-apics, not using io-apics for "
504d11e32a8SMichael Lotz 			"interrupt routing\n");
505d11e32a8SMichael Lotz 		return;
506d11e32a8SMichael Lotz 	}
507d11e32a8SMichael Lotz 
508d11e32a8SMichael Lotz 	// switch to the APIC interrupt model before retrieving the IRQ routing
509dc14d97bSMichael Lotz 	// table as it will return different settings depending on the model
510dc14d97bSMichael Lotz 	status = acpi_set_interrupt_model(acpiModule, ACPI_INTERRUPT_MODEL_APIC);
511dc14d97bSMichael Lotz 	if (status != B_OK) {
512dc14d97bSMichael Lotz 		dprintf("failed to put ACPI into APIC interrupt model, ignoring\n");
513dc14d97bSMichael Lotz 		// don't abort, as the _PIC method is optional and as long as there
514dc14d97bSMichael Lotz 		// aren't different routings based on it this is non-fatal
515dc14d97bSMichael Lotz 	}
516dc14d97bSMichael Lotz 
517dc14d97bSMichael Lotz 	IRQRoutingTable table;
518dc14d97bSMichael Lotz 	status = prepare_irq_routing(acpiModule, table,
519*eda74390SMichael Lotz 		&ioapic_is_interrupt_available);
520dc14d97bSMichael Lotz 	if (status != B_OK) {
5215d01e61aSMichael Lotz 		dprintf("IRQ routing preparation failed, not configuring io-apics\n");
522dc14d97bSMichael Lotz 		acpi_set_interrupt_model(acpiModule, ACPI_INTERRUPT_MODEL_PIC);
523dc14d97bSMichael Lotz 			// revert to PIC interrupt model just in case
524dc14d97bSMichael Lotz 		return;
525dc14d97bSMichael Lotz 	}
526dc14d97bSMichael Lotz 
527dc14d97bSMichael Lotz 	print_irq_routing_table(table);
528dc14d97bSMichael Lotz 
5295d01e61aSMichael Lotz 	// use the boot CPU as the target for all interrupts
5305d01e61aSMichael Lotz 	uint64 targetAPIC = args->arch_args.cpu_apic_id[0];
5315d01e61aSMichael Lotz 
5325d01e61aSMichael Lotz 	struct ioapic* current = &sIOAPICs;
5335d01e61aSMichael Lotz 	while (current != NULL) {
5345d01e61aSMichael Lotz 		status = ioapic_initialize_ioapic(*current, targetAPIC);
5355d01e61aSMichael Lotz 		if (status != B_OK) {
5365d01e61aSMichael Lotz 			panic("failed to initialize io-apic %u", current->number);
5375d01e61aSMichael Lotz 			acpi_set_interrupt_model(acpiModule, ACPI_INTERRUPT_MODEL_PIC);
5385d01e61aSMichael Lotz 			return;
5395d01e61aSMichael Lotz 		}
5405d01e61aSMichael Lotz 
5415d01e61aSMichael Lotz 		current = current->next;
5425d01e61aSMichael Lotz 	}
5435d01e61aSMichael Lotz 
544dc14d97bSMichael Lotz 	status = enable_irq_routing(acpiModule, table);
545dc14d97bSMichael Lotz 	if (status != B_OK) {
546dc14d97bSMichael Lotz 		panic("failed to enable IRQ routing");
547dc14d97bSMichael Lotz 		// if it failed early on it might still work in PIC mode
548dc14d97bSMichael Lotz 		acpi_set_interrupt_model(acpiModule, ACPI_INTERRUPT_MODEL_PIC);
549dc14d97bSMichael Lotz 		return;
550dc14d97bSMichael Lotz 	}
551dc14d97bSMichael Lotz 
5525d01e61aSMichael Lotz 	// configure IO-APIC interrupts from PCI routing table
553dc14d97bSMichael Lotz 	for (int i = 0; i < table.Count(); i++) {
554dc14d97bSMichael Lotz 		irq_routing_entry& entry = table.ElementAt(i);
555dc14d97bSMichael Lotz 		ioapic_configure_io_interrupt(entry.irq,
556dc14d97bSMichael Lotz 			entry.polarity | entry.trigger_mode);
557dc14d97bSMichael Lotz 	}
558dc14d97bSMichael Lotz 
559dc14d97bSMichael Lotz 	// disable the legacy PIC
560dc14d97bSMichael Lotz 	pic_disable();
561dc14d97bSMichael Lotz 
562dc14d97bSMichael Lotz 	// prefer the ioapic over the normal pic
5635d01e61aSMichael Lotz 	dprintf("using io-apics for interrupt routing\n");
564dc14d97bSMichael Lotz 	arch_int_set_interrupt_controller(ioapicController);
565dc14d97bSMichael Lotz }
566