xref: /haiku/src/system/kernel/arch/x86/ioapic.cpp (revision dc14d97b7f70aa318483d53e007913b26a12f5d0)
1*dc14d97bSMichael Lotz /*
2*dc14d97bSMichael Lotz  * Copyright 2008-2011, Michael Lotz, mmlr@mlotz.ch.
3*dc14d97bSMichael Lotz  * Distributed under the terms of the MIT License.
4*dc14d97bSMichael Lotz  */
5*dc14d97bSMichael Lotz 
6*dc14d97bSMichael Lotz #include <arch/x86/ioapic.h>
7*dc14d97bSMichael Lotz 
8*dc14d97bSMichael Lotz #include <int.h>
9*dc14d97bSMichael Lotz #include <vm/vm.h>
10*dc14d97bSMichael Lotz 
11*dc14d97bSMichael Lotz #include "irq_routing_table.h"
12*dc14d97bSMichael Lotz 
13*dc14d97bSMichael Lotz #include <ACPI.h>
14*dc14d97bSMichael Lotz #include <AutoDeleter.h>
15*dc14d97bSMichael Lotz #include <safemode.h>
16*dc14d97bSMichael Lotz #include <string.h>
17*dc14d97bSMichael Lotz #include <stdio.h>
18*dc14d97bSMichael Lotz 
19*dc14d97bSMichael Lotz #include <arch/x86/apic.h>
20*dc14d97bSMichael Lotz #include <arch/x86/arch_int.h>
21*dc14d97bSMichael Lotz #include <arch/x86/pic.h>
22*dc14d97bSMichael Lotz 
23*dc14d97bSMichael Lotz 
24*dc14d97bSMichael Lotz //#define TRACE_IOAPIC
25*dc14d97bSMichael Lotz #ifdef TRACE_IOAPIC
26*dc14d97bSMichael Lotz #	define TRACE(x) dprintf x
27*dc14d97bSMichael Lotz #else
28*dc14d97bSMichael Lotz #	define TRACE(x) ;
29*dc14d97bSMichael Lotz #endif
30*dc14d97bSMichael Lotz 
31*dc14d97bSMichael Lotz 
32*dc14d97bSMichael Lotz // ACPI interrupt models
33*dc14d97bSMichael Lotz #define ACPI_INTERRUPT_MODEL_PIC	0
34*dc14d97bSMichael Lotz #define ACPI_INTERRUPT_MODEL_APIC	1
35*dc14d97bSMichael Lotz #define ACPI_INTERRUPT_MODEL_SAPIC	2
36*dc14d97bSMichael Lotz 
37*dc14d97bSMichael Lotz 
38*dc14d97bSMichael Lotz // Definitions for a 82093AA IO APIC controller
39*dc14d97bSMichael Lotz #define IO_APIC_IDENTIFICATION				0x00
40*dc14d97bSMichael Lotz #define IO_APIC_VERSION						0x01
41*dc14d97bSMichael Lotz #define IO_APIC_ARBITRATION					0x02
42*dc14d97bSMichael Lotz #define IO_APIC_REDIRECTION_TABLE			0x10 // entry = base + 2 * index
43*dc14d97bSMichael Lotz 
44*dc14d97bSMichael Lotz // Fields for the version register
45*dc14d97bSMichael Lotz #define IO_APIC_VERSION_SHIFT				0
46*dc14d97bSMichael Lotz #define IO_APIC_VERSION_MASK				0xff
47*dc14d97bSMichael Lotz #define IO_APIC_MAX_REDIRECTION_ENTRY_SHIFT	16
48*dc14d97bSMichael Lotz #define IO_APIC_MAX_REDIRECTION_ENTRY_MASK	0xff
49*dc14d97bSMichael Lotz 
50*dc14d97bSMichael Lotz // Fields of each redirection table entry
51*dc14d97bSMichael Lotz #define IO_APIC_DESTINATION_FIELD_SHIFT		56
52*dc14d97bSMichael Lotz #define IO_APIC_DESTINATION_FIELD_MASK		0x0f
53*dc14d97bSMichael Lotz #define IO_APIC_INTERRUPT_MASK_SHIFT		16
54*dc14d97bSMichael Lotz #define IO_APIC_INTERRUPT_MASKED			1
55*dc14d97bSMichael Lotz #define IO_APIC_INTERRUPT_UNMASKED			0
56*dc14d97bSMichael Lotz #define IO_APIC_TRIGGER_MODE_SHIFT			15
57*dc14d97bSMichael Lotz #define IO_APIC_TRIGGER_MODE_EDGE			0
58*dc14d97bSMichael Lotz #define IO_APIC_TRIGGER_MODE_LEVEL			1
59*dc14d97bSMichael Lotz #define IO_APIC_REMOTE_IRR_SHIFT			14
60*dc14d97bSMichael Lotz #define IO_APIC_PIN_POLARITY_SHIFT			13
61*dc14d97bSMichael Lotz #define IO_APIC_PIN_POLARITY_HIGH_ACTIVE	0
62*dc14d97bSMichael Lotz #define IO_APIC_PIN_POLARITY_LOW_ACTIVE		1
63*dc14d97bSMichael Lotz #define IO_APIC_DELIVERY_STATUS_SHIFT		12
64*dc14d97bSMichael Lotz #define IO_APIC_DELIVERY_STATUS_IDLE		0
65*dc14d97bSMichael Lotz #define IO_APIC_DELIVERY_STATUS_PENDING		1
66*dc14d97bSMichael Lotz #define IO_APIC_DESTINATION_MODE_SHIFT		11
67*dc14d97bSMichael Lotz #define IO_APIC_DESTINATION_MODE_PHYSICAL	0
68*dc14d97bSMichael Lotz #define IO_APIC_DESTINATION_MODE_LOGICAL	1
69*dc14d97bSMichael Lotz #define IO_APIC_DELIVERY_MODE_SHIFT			8
70*dc14d97bSMichael Lotz #define IO_APIC_DELIVERY_MODE_MASK			0x07
71*dc14d97bSMichael Lotz #define IO_APIC_DELIVERY_MODE_FIXED			0
72*dc14d97bSMichael Lotz #define IO_APIC_DELIVERY_MODE_LOWEST_PRIO	1
73*dc14d97bSMichael Lotz #define IO_APIC_DELIVERY_MODE_SMI			2
74*dc14d97bSMichael Lotz #define IO_APIC_DELIVERY_MODE_NMI			4
75*dc14d97bSMichael Lotz #define IO_APIC_DELIVERY_MODE_INIT			5
76*dc14d97bSMichael Lotz #define IO_APIC_DELIVERY_MODE_EXT_INT		7
77*dc14d97bSMichael Lotz #define IO_APIC_INTERRUPT_VECTOR_SHIFT		0
78*dc14d97bSMichael Lotz #define IO_APIC_INTERRUPT_VECTOR_MASK		0xff
79*dc14d97bSMichael Lotz 
80*dc14d97bSMichael Lotz 
81*dc14d97bSMichael Lotz typedef struct ioapic_s {
82*dc14d97bSMichael Lotz 	volatile uint32	io_register_select;
83*dc14d97bSMichael Lotz 	uint32			reserved[3];
84*dc14d97bSMichael Lotz 	volatile uint32	io_window_register;
85*dc14d97bSMichael Lotz } ioapic;
86*dc14d97bSMichael Lotz 
87*dc14d97bSMichael Lotz static ioapic *sIOAPIC = NULL;
88*dc14d97bSMichael Lotz static uint32 sIOAPICMaxRedirectionEntry = 23;
89*dc14d97bSMichael Lotz 
90*dc14d97bSMichael Lotz static uint64 sLevelTriggeredInterrupts = 0;
91*dc14d97bSMichael Lotz 	// binary mask: 1 level, 0 edge
92*dc14d97bSMichael Lotz 
93*dc14d97bSMichael Lotz 
94*dc14d97bSMichael Lotz // #pragma mark - I/O APIC
95*dc14d97bSMichael Lotz 
96*dc14d97bSMichael Lotz 
97*dc14d97bSMichael Lotz static inline uint32
98*dc14d97bSMichael Lotz ioapic_read_32(uint8 registerSelect)
99*dc14d97bSMichael Lotz {
100*dc14d97bSMichael Lotz 	sIOAPIC->io_register_select = registerSelect;
101*dc14d97bSMichael Lotz 	return sIOAPIC->io_window_register;
102*dc14d97bSMichael Lotz }
103*dc14d97bSMichael Lotz 
104*dc14d97bSMichael Lotz 
105*dc14d97bSMichael Lotz static inline void
106*dc14d97bSMichael Lotz ioapic_write_32(uint8 registerSelect, uint32 value)
107*dc14d97bSMichael Lotz {
108*dc14d97bSMichael Lotz 	sIOAPIC->io_register_select = registerSelect;
109*dc14d97bSMichael Lotz 	sIOAPIC->io_window_register = value;
110*dc14d97bSMichael Lotz }
111*dc14d97bSMichael Lotz 
112*dc14d97bSMichael Lotz 
113*dc14d97bSMichael Lotz static inline uint64
114*dc14d97bSMichael Lotz ioapic_read_64(uint8 registerSelect)
115*dc14d97bSMichael Lotz {
116*dc14d97bSMichael Lotz 	sIOAPIC->io_register_select = registerSelect + 1;
117*dc14d97bSMichael Lotz 	uint64 result = sIOAPIC->io_window_register;
118*dc14d97bSMichael Lotz 	result <<= 32;
119*dc14d97bSMichael Lotz 	sIOAPIC->io_register_select = registerSelect;
120*dc14d97bSMichael Lotz 	result |= sIOAPIC->io_window_register;
121*dc14d97bSMichael Lotz 	return result;
122*dc14d97bSMichael Lotz }
123*dc14d97bSMichael Lotz 
124*dc14d97bSMichael Lotz 
125*dc14d97bSMichael Lotz static inline void
126*dc14d97bSMichael Lotz ioapic_write_64(uint8 registerSelect, uint64 value)
127*dc14d97bSMichael Lotz {
128*dc14d97bSMichael Lotz 	sIOAPIC->io_register_select = registerSelect;
129*dc14d97bSMichael Lotz 	sIOAPIC->io_window_register = (uint32)value;
130*dc14d97bSMichael Lotz 	sIOAPIC->io_register_select = registerSelect + 1;
131*dc14d97bSMichael Lotz 	sIOAPIC->io_window_register = (uint32)(value >> 32);
132*dc14d97bSMichael Lotz }
133*dc14d97bSMichael Lotz 
134*dc14d97bSMichael Lotz 
135*dc14d97bSMichael Lotz static bool
136*dc14d97bSMichael Lotz ioapic_is_spurious_interrupt(int32 num)
137*dc14d97bSMichael Lotz {
138*dc14d97bSMichael Lotz 	// the spurious interrupt vector is initialized to the max value in smp
139*dc14d97bSMichael Lotz 	return num == 0xff - ARCH_INTERRUPT_BASE;
140*dc14d97bSMichael Lotz }
141*dc14d97bSMichael Lotz 
142*dc14d97bSMichael Lotz 
143*dc14d97bSMichael Lotz static bool
144*dc14d97bSMichael Lotz ioapic_is_level_triggered_interrupt(int32 pin)
145*dc14d97bSMichael Lotz {
146*dc14d97bSMichael Lotz 	if (pin < 0 || pin > (int32)sIOAPICMaxRedirectionEntry)
147*dc14d97bSMichael Lotz 		return false;
148*dc14d97bSMichael Lotz 
149*dc14d97bSMichael Lotz 	return (sLevelTriggeredInterrupts & (1 << pin)) != 0;
150*dc14d97bSMichael Lotz }
151*dc14d97bSMichael Lotz 
152*dc14d97bSMichael Lotz 
153*dc14d97bSMichael Lotz static bool
154*dc14d97bSMichael Lotz ioapic_end_of_interrupt(int32 num)
155*dc14d97bSMichael Lotz {
156*dc14d97bSMichael Lotz 	apic_end_of_interrupt();
157*dc14d97bSMichael Lotz 	return true;
158*dc14d97bSMichael Lotz }
159*dc14d97bSMichael Lotz 
160*dc14d97bSMichael Lotz 
161*dc14d97bSMichael Lotz static void
162*dc14d97bSMichael Lotz ioapic_enable_io_interrupt(int32 pin)
163*dc14d97bSMichael Lotz {
164*dc14d97bSMichael Lotz 	if (pin < 0 || pin > (int32)sIOAPICMaxRedirectionEntry)
165*dc14d97bSMichael Lotz 		return;
166*dc14d97bSMichael Lotz 
167*dc14d97bSMichael Lotz 	TRACE(("ioapic_enable_io_interrupt: pin %ld\n", pin));
168*dc14d97bSMichael Lotz 
169*dc14d97bSMichael Lotz 	uint64 entry = ioapic_read_64(IO_APIC_REDIRECTION_TABLE + pin * 2);
170*dc14d97bSMichael Lotz 	entry &= ~(1 << IO_APIC_INTERRUPT_MASK_SHIFT);
171*dc14d97bSMichael Lotz 	entry |= IO_APIC_INTERRUPT_UNMASKED << IO_APIC_INTERRUPT_MASK_SHIFT;
172*dc14d97bSMichael Lotz 	ioapic_write_64(IO_APIC_REDIRECTION_TABLE + pin * 2, entry);
173*dc14d97bSMichael Lotz }
174*dc14d97bSMichael Lotz 
175*dc14d97bSMichael Lotz 
176*dc14d97bSMichael Lotz static void
177*dc14d97bSMichael Lotz ioapic_disable_io_interrupt(int32 pin)
178*dc14d97bSMichael Lotz {
179*dc14d97bSMichael Lotz 	if (pin < 0 || pin > (int32)sIOAPICMaxRedirectionEntry)
180*dc14d97bSMichael Lotz 		return;
181*dc14d97bSMichael Lotz 
182*dc14d97bSMichael Lotz 	TRACE(("ioapic_disable_io_interrupt: pin %ld\n", pin));
183*dc14d97bSMichael Lotz 
184*dc14d97bSMichael Lotz 	uint64 entry = ioapic_read_64(IO_APIC_REDIRECTION_TABLE + pin * 2);
185*dc14d97bSMichael Lotz 	entry &= ~(1 << IO_APIC_INTERRUPT_MASK_SHIFT);
186*dc14d97bSMichael Lotz 	entry |= IO_APIC_INTERRUPT_MASKED << IO_APIC_INTERRUPT_MASK_SHIFT;
187*dc14d97bSMichael Lotz 	ioapic_write_64(IO_APIC_REDIRECTION_TABLE + pin * 2, entry);
188*dc14d97bSMichael Lotz }
189*dc14d97bSMichael Lotz 
190*dc14d97bSMichael Lotz 
191*dc14d97bSMichael Lotz static void
192*dc14d97bSMichael Lotz ioapic_configure_io_interrupt(int32 pin, uint32 config)
193*dc14d97bSMichael Lotz {
194*dc14d97bSMichael Lotz 	if (pin < 0 || pin > (int32)sIOAPICMaxRedirectionEntry)
195*dc14d97bSMichael Lotz 		return;
196*dc14d97bSMichael Lotz 
197*dc14d97bSMichael Lotz 	TRACE(("ioapic_configure_io_interrupt: pin %ld; config 0x%08lx\n", pin,
198*dc14d97bSMichael Lotz 		config));
199*dc14d97bSMichael Lotz 
200*dc14d97bSMichael Lotz 	uint64 entry = ioapic_read_64(IO_APIC_REDIRECTION_TABLE + pin * 2);
201*dc14d97bSMichael Lotz 	entry &= ~((1 << IO_APIC_TRIGGER_MODE_SHIFT)
202*dc14d97bSMichael Lotz 		| (1 << IO_APIC_PIN_POLARITY_SHIFT)
203*dc14d97bSMichael Lotz 		| (IO_APIC_INTERRUPT_VECTOR_MASK << IO_APIC_INTERRUPT_VECTOR_SHIFT));
204*dc14d97bSMichael Lotz 
205*dc14d97bSMichael Lotz 	if (config & B_LEVEL_TRIGGERED) {
206*dc14d97bSMichael Lotz 		entry |= (IO_APIC_TRIGGER_MODE_LEVEL << IO_APIC_TRIGGER_MODE_SHIFT);
207*dc14d97bSMichael Lotz 		sLevelTriggeredInterrupts |= (1 << pin);
208*dc14d97bSMichael Lotz 	} else {
209*dc14d97bSMichael Lotz 		entry |= (IO_APIC_TRIGGER_MODE_EDGE << IO_APIC_TRIGGER_MODE_SHIFT);
210*dc14d97bSMichael Lotz 		sLevelTriggeredInterrupts &= ~(1 << pin);
211*dc14d97bSMichael Lotz 	}
212*dc14d97bSMichael Lotz 
213*dc14d97bSMichael Lotz 	if (config & B_LOW_ACTIVE_POLARITY)
214*dc14d97bSMichael Lotz 		entry |= (IO_APIC_PIN_POLARITY_LOW_ACTIVE << IO_APIC_PIN_POLARITY_SHIFT);
215*dc14d97bSMichael Lotz 	else
216*dc14d97bSMichael Lotz 		entry |= (IO_APIC_PIN_POLARITY_HIGH_ACTIVE << IO_APIC_PIN_POLARITY_SHIFT);
217*dc14d97bSMichael Lotz 
218*dc14d97bSMichael Lotz 	entry |= (pin + ARCH_INTERRUPT_BASE) << IO_APIC_INTERRUPT_VECTOR_SHIFT;
219*dc14d97bSMichael Lotz 	ioapic_write_64(IO_APIC_REDIRECTION_TABLE + pin * 2, entry);
220*dc14d97bSMichael Lotz }
221*dc14d97bSMichael Lotz 
222*dc14d97bSMichael Lotz 
223*dc14d97bSMichael Lotz static status_t
224*dc14d97bSMichael Lotz acpi_set_interrupt_model(acpi_module_info* acpiModule, uint32 interruptModel)
225*dc14d97bSMichael Lotz {
226*dc14d97bSMichael Lotz 	acpi_object_type model;
227*dc14d97bSMichael Lotz 	model.object_type = ACPI_TYPE_INTEGER;
228*dc14d97bSMichael Lotz 	model.data.integer = interruptModel;
229*dc14d97bSMichael Lotz 
230*dc14d97bSMichael Lotz 	acpi_objects parameter;
231*dc14d97bSMichael Lotz 	parameter.count = 1;
232*dc14d97bSMichael Lotz 	parameter.pointer = &model;
233*dc14d97bSMichael Lotz 
234*dc14d97bSMichael Lotz 	dprintf("setting ACPI interrupt model to %s\n",
235*dc14d97bSMichael Lotz 		interruptModel == 0 ? "PIC"
236*dc14d97bSMichael Lotz 		: (interruptModel == 1 ? "APIC"
237*dc14d97bSMichael Lotz 		: (interruptModel == 2 ? "SAPIC"
238*dc14d97bSMichael Lotz 		: "unknown")));
239*dc14d97bSMichael Lotz 
240*dc14d97bSMichael Lotz 	return acpiModule->evaluate_method(NULL, "\\_PIC", &parameter, NULL);
241*dc14d97bSMichael Lotz }
242*dc14d97bSMichael Lotz 
243*dc14d97bSMichael Lotz 
244*dc14d97bSMichael Lotz void
245*dc14d97bSMichael Lotz ioapic_map(kernel_args* args)
246*dc14d97bSMichael Lotz {
247*dc14d97bSMichael Lotz 	if (args->arch_args.apic == NULL) {
248*dc14d97bSMichael Lotz 		dprintf("no local apic available\n");
249*dc14d97bSMichael Lotz 		return;
250*dc14d97bSMichael Lotz 	}
251*dc14d97bSMichael Lotz 
252*dc14d97bSMichael Lotz 	if (args->arch_args.ioapic == NULL) {
253*dc14d97bSMichael Lotz 		dprintf("no ioapic available, not using ioapics for interrupt routing\n");
254*dc14d97bSMichael Lotz 		return;
255*dc14d97bSMichael Lotz 	}
256*dc14d97bSMichael Lotz 
257*dc14d97bSMichael Lotz 	// map in the (first) IO-APIC
258*dc14d97bSMichael Lotz 	sIOAPIC = (ioapic *)args->arch_args.ioapic;
259*dc14d97bSMichael Lotz 	if (vm_map_physical_memory(B_SYSTEM_TEAM, "ioapic", (void**)&sIOAPIC,
260*dc14d97bSMichael Lotz 			B_EXACT_ADDRESS, B_PAGE_SIZE,
261*dc14d97bSMichael Lotz 			B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA,
262*dc14d97bSMichael Lotz 			args->arch_args.ioapic_phys, true) < 0) {
263*dc14d97bSMichael Lotz 		panic("mapping the ioapic failed");
264*dc14d97bSMichael Lotz 		return;
265*dc14d97bSMichael Lotz 	}
266*dc14d97bSMichael Lotz }
267*dc14d97bSMichael Lotz 
268*dc14d97bSMichael Lotz 
269*dc14d97bSMichael Lotz void
270*dc14d97bSMichael Lotz ioapic_init(kernel_args* args)
271*dc14d97bSMichael Lotz {
272*dc14d97bSMichael Lotz 	static const interrupt_controller ioapicController = {
273*dc14d97bSMichael Lotz 		"82093AA IOAPIC",
274*dc14d97bSMichael Lotz 		&ioapic_enable_io_interrupt,
275*dc14d97bSMichael Lotz 		&ioapic_disable_io_interrupt,
276*dc14d97bSMichael Lotz 		&ioapic_configure_io_interrupt,
277*dc14d97bSMichael Lotz 		&ioapic_is_spurious_interrupt,
278*dc14d97bSMichael Lotz 		&ioapic_is_level_triggered_interrupt,
279*dc14d97bSMichael Lotz 		&ioapic_end_of_interrupt
280*dc14d97bSMichael Lotz 	};
281*dc14d97bSMichael Lotz 
282*dc14d97bSMichael Lotz 	if (sIOAPIC == NULL)
283*dc14d97bSMichael Lotz 		return;
284*dc14d97bSMichael Lotz 
285*dc14d97bSMichael Lotz #if 0
286*dc14d97bSMichael Lotz 	if (get_safemode_boolean(B_SAFEMODE_DISABLE_IOAPIC, false)) {
287*dc14d97bSMichael Lotz 		dprintf("ioapic explicitly disabled, not using ioapics for interrupt "
288*dc14d97bSMichael Lotz 			"routing\n");
289*dc14d97bSMichael Lotz 		return;
290*dc14d97bSMichael Lotz 	}
291*dc14d97bSMichael Lotz #else
292*dc14d97bSMichael Lotz 	// TODO: This can be removed once IO-APIC code is broadly tested
293*dc14d97bSMichael Lotz 	if (!get_safemode_boolean(B_SAFEMODE_ENABLE_IOAPIC, false)) {
294*dc14d97bSMichael Lotz 		dprintf("ioapic not enabled, not using ioapics for interrupt "
295*dc14d97bSMichael Lotz 			"routing\n");
296*dc14d97bSMichael Lotz 		return;
297*dc14d97bSMichael Lotz 	}
298*dc14d97bSMichael Lotz #endif
299*dc14d97bSMichael Lotz 
300*dc14d97bSMichael Lotz 	uint32 version = ioapic_read_32(IO_APIC_VERSION);
301*dc14d97bSMichael Lotz 	if (version == 0xffffffff) {
302*dc14d97bSMichael Lotz 		dprintf("ioapic seems inaccessible, not using it\n");
303*dc14d97bSMichael Lotz 		return;
304*dc14d97bSMichael Lotz 	}
305*dc14d97bSMichael Lotz 
306*dc14d97bSMichael Lotz 	// load acpi module
307*dc14d97bSMichael Lotz 	status_t status;
308*dc14d97bSMichael Lotz 	acpi_module_info* acpiModule;
309*dc14d97bSMichael Lotz 	status = get_module(B_ACPI_MODULE_NAME, (module_info**)&acpiModule);
310*dc14d97bSMichael Lotz 	if (status != B_OK) {
311*dc14d97bSMichael Lotz 		dprintf("acpi module not available, not configuring ioapic\n");
312*dc14d97bSMichael Lotz 		return;
313*dc14d97bSMichael Lotz 	}
314*dc14d97bSMichael Lotz 	BPrivate::CObjectDeleter<const char, status_t>
315*dc14d97bSMichael Lotz 		acpiModulePutter(B_ACPI_MODULE_NAME, put_module);
316*dc14d97bSMichael Lotz 
317*dc14d97bSMichael Lotz 	// switch to the APIC interrupt model before retrieving the irq routing
318*dc14d97bSMichael Lotz 	// table as it will return different settings depending on the model
319*dc14d97bSMichael Lotz 	status = acpi_set_interrupt_model(acpiModule, ACPI_INTERRUPT_MODEL_APIC);
320*dc14d97bSMichael Lotz 	if (status != B_OK) {
321*dc14d97bSMichael Lotz 		dprintf("failed to put ACPI into APIC interrupt model, ignoring\n");
322*dc14d97bSMichael Lotz 		// don't abort, as the _PIC method is optional and as long as there
323*dc14d97bSMichael Lotz 		// aren't different routings based on it this is non-fatal
324*dc14d97bSMichael Lotz 	}
325*dc14d97bSMichael Lotz 
326*dc14d97bSMichael Lotz 	sLevelTriggeredInterrupts = 0;
327*dc14d97bSMichael Lotz 	sIOAPICMaxRedirectionEntry
328*dc14d97bSMichael Lotz 		= ((version >> IO_APIC_MAX_REDIRECTION_ENTRY_SHIFT)
329*dc14d97bSMichael Lotz 			& IO_APIC_MAX_REDIRECTION_ENTRY_MASK);
330*dc14d97bSMichael Lotz 
331*dc14d97bSMichael Lotz 	TRACE(("ioapic has %lu entries\n", sIOAPICMaxRedirectionEntry + 1));
332*dc14d97bSMichael Lotz 
333*dc14d97bSMichael Lotz 	IRQRoutingTable table;
334*dc14d97bSMichael Lotz 	status = prepare_irq_routing(acpiModule, table,
335*dc14d97bSMichael Lotz 		sIOAPICMaxRedirectionEntry + 1);
336*dc14d97bSMichael Lotz 	if (status != B_OK) {
337*dc14d97bSMichael Lotz 		dprintf("IRQ routing preparation failed, not configuring ioapic.\n");
338*dc14d97bSMichael Lotz 		acpi_set_interrupt_model(acpiModule, ACPI_INTERRUPT_MODEL_PIC);
339*dc14d97bSMichael Lotz 			// revert to PIC interrupt model just in case
340*dc14d97bSMichael Lotz 		return;
341*dc14d97bSMichael Lotz 	}
342*dc14d97bSMichael Lotz 
343*dc14d97bSMichael Lotz 	print_irq_routing_table(table);
344*dc14d97bSMichael Lotz 
345*dc14d97bSMichael Lotz 	status = enable_irq_routing(acpiModule, table);
346*dc14d97bSMichael Lotz 	if (status != B_OK) {
347*dc14d97bSMichael Lotz 		panic("failed to enable IRQ routing");
348*dc14d97bSMichael Lotz 		// if it failed early on it might still work in PIC mode
349*dc14d97bSMichael Lotz 		acpi_set_interrupt_model(acpiModule, ACPI_INTERRUPT_MODEL_PIC);
350*dc14d97bSMichael Lotz 		return;
351*dc14d97bSMichael Lotz 	}
352*dc14d97bSMichael Lotz 
353*dc14d97bSMichael Lotz 	// use the boot CPU as the target for all interrupts
354*dc14d97bSMichael Lotz 	uint64 targetAPIC = args->arch_args.cpu_apic_id[0];
355*dc14d97bSMichael Lotz 
356*dc14d97bSMichael Lotz 	// program the interrupt vectors of the ioapic
357*dc14d97bSMichael Lotz 	for (uint32 i = 0; i <= sIOAPICMaxRedirectionEntry; i++) {
358*dc14d97bSMichael Lotz 		// initialize everything to deliver to the boot CPU in physical mode
359*dc14d97bSMichael Lotz 		// and masked until explicitly enabled through enable_io_interrupt()
360*dc14d97bSMichael Lotz 		uint64 entry = (targetAPIC << IO_APIC_DESTINATION_FIELD_SHIFT)
361*dc14d97bSMichael Lotz 			| (IO_APIC_INTERRUPT_MASKED << IO_APIC_INTERRUPT_MASK_SHIFT)
362*dc14d97bSMichael Lotz 			| (IO_APIC_DESTINATION_MODE_PHYSICAL << IO_APIC_DESTINATION_MODE_SHIFT)
363*dc14d97bSMichael Lotz 			| ((i + ARCH_INTERRUPT_BASE) << IO_APIC_INTERRUPT_VECTOR_SHIFT);
364*dc14d97bSMichael Lotz 
365*dc14d97bSMichael Lotz 		if (i == 0) {
366*dc14d97bSMichael Lotz 			// make redirection entry 0 into an external interrupt
367*dc14d97bSMichael Lotz 			entry |= (IO_APIC_TRIGGER_MODE_EDGE << IO_APIC_TRIGGER_MODE_SHIFT)
368*dc14d97bSMichael Lotz 				| (IO_APIC_PIN_POLARITY_HIGH_ACTIVE << IO_APIC_PIN_POLARITY_SHIFT)
369*dc14d97bSMichael Lotz 				| (IO_APIC_DELIVERY_MODE_EXT_INT << IO_APIC_DELIVERY_MODE_SHIFT);
370*dc14d97bSMichael Lotz 		} else if (i < 16) {
371*dc14d97bSMichael Lotz 			// make 1-15 ISA interrupts
372*dc14d97bSMichael Lotz 			entry |= (IO_APIC_TRIGGER_MODE_EDGE << IO_APIC_TRIGGER_MODE_SHIFT)
373*dc14d97bSMichael Lotz 				| (IO_APIC_PIN_POLARITY_HIGH_ACTIVE << IO_APIC_PIN_POLARITY_SHIFT)
374*dc14d97bSMichael Lotz 				| (IO_APIC_DELIVERY_MODE_FIXED << IO_APIC_DELIVERY_MODE_SHIFT);
375*dc14d97bSMichael Lotz 		} else {
376*dc14d97bSMichael Lotz 			// and the rest are PCI interrupts
377*dc14d97bSMichael Lotz 			entry |= (IO_APIC_TRIGGER_MODE_LEVEL << IO_APIC_TRIGGER_MODE_SHIFT)
378*dc14d97bSMichael Lotz 				| (IO_APIC_PIN_POLARITY_LOW_ACTIVE << IO_APIC_PIN_POLARITY_SHIFT)
379*dc14d97bSMichael Lotz 				| (IO_APIC_DELIVERY_MODE_FIXED << IO_APIC_DELIVERY_MODE_SHIFT);
380*dc14d97bSMichael Lotz 			sLevelTriggeredInterrupts |= (1 << i);
381*dc14d97bSMichael Lotz 		}
382*dc14d97bSMichael Lotz 
383*dc14d97bSMichael Lotz 		ioapic_write_64(IO_APIC_REDIRECTION_TABLE + 2 * i, entry);
384*dc14d97bSMichael Lotz 	}
385*dc14d97bSMichael Lotz 
386*dc14d97bSMichael Lotz 	// configure io apic interrupts from PCI routing table
387*dc14d97bSMichael Lotz 	for (int i = 0; i < table.Count(); i++) {
388*dc14d97bSMichael Lotz 		irq_routing_entry& entry = table.ElementAt(i);
389*dc14d97bSMichael Lotz 		ioapic_configure_io_interrupt(entry.irq,
390*dc14d97bSMichael Lotz 			entry.polarity | entry.trigger_mode);
391*dc14d97bSMichael Lotz 	}
392*dc14d97bSMichael Lotz 
393*dc14d97bSMichael Lotz 	// disable the legacy PIC
394*dc14d97bSMichael Lotz 	pic_disable();
395*dc14d97bSMichael Lotz 
396*dc14d97bSMichael Lotz 	// prefer the ioapic over the normal pic
397*dc14d97bSMichael Lotz 	dprintf("using ioapic for interrupt routing\n");
398*dc14d97bSMichael Lotz 	arch_int_set_interrupt_controller(ioapicController);
399*dc14d97bSMichael Lotz }
400