xref: /haiku/src/system/kernel/arch/x86/ioapic.cpp (revision 9f3bdf3d039430b5172c424def20ce5d9f7367d4)
1 /*
2  * Copyright 2008-2011, Michael Lotz, mmlr@mlotz.ch.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 #include <arch/x86/ioapic.h>
7 
8 #include <int.h>
9 #include <vm/vm.h>
10 
11 #include "acpi_irq_routing_table.h"
12 
13 #include <ACPI.h>
14 #include <AutoDeleter.h>
15 #include <safemode.h>
16 #include <string.h>
17 #include <stdio.h>
18 
19 #include <arch/x86/apic.h>
20 #include <arch/x86/arch_int.h>
21 #include <arch/x86/arch_smp.h>
22 #include <arch/x86/pic.h>
23 
24 // to gain access to the ACPICA types
25 #include "acpi.h"
26 
27 
28 //#define TRACE_IOAPIC
29 #ifdef TRACE_IOAPIC
30 #	define TRACE(...) dprintf(__VA_ARGS__)
31 #else
32 #	define TRACE(...) (void)0
33 #endif
34 
35 
36 // ACPI interrupt models
37 #define ACPI_INTERRUPT_MODEL_PIC	0
38 #define ACPI_INTERRUPT_MODEL_APIC	1
39 #define ACPI_INTERRUPT_MODEL_SAPIC	2
40 
41 
42 // Definitions for a 82093AA IO APIC controller
43 #define IO_APIC_ID							0x00
44 #define IO_APIC_VERSION						0x01
45 #define IO_APIC_ARBITRATION					0x02
46 #define IO_APIC_REDIRECTION_TABLE			0x10 // entry = base + 2 * index
47 
48 // Fields for the id register
49 #define IO_APIC_ID_SHIFT					24
50 #define IO_APIC_ID_MASK						0xff
51 
52 // Fields for the version register
53 #define IO_APIC_VERSION_SHIFT				0
54 #define IO_APIC_VERSION_MASK				0xff
55 #define IO_APIC_MAX_REDIRECTION_ENTRY_SHIFT	16
56 #define IO_APIC_MAX_REDIRECTION_ENTRY_MASK	0xff
57 
58 // Fields of each redirection table entry
59 #define IO_APIC_DESTINATION_FIELD_SHIFT		56
60 #define IO_APIC_DESTINATION_FIELD_MASK		0xff
61 #define IO_APIC_INTERRUPT_MASKED			(1 << 16)
62 #define IO_APIC_TRIGGER_MODE_EDGE			(0 << 15)
63 #define IO_APIC_TRIGGER_MODE_LEVEL			(1 << 15)
64 #define IO_APIC_TRIGGER_MODE_MASK			(1 << 15)
65 #define IO_APIC_REMOTE_IRR					(1 << 14)
66 #define IO_APIC_PIN_POLARITY_HIGH_ACTIVE	(0 << 13)
67 #define IO_APIC_PIN_POLARITY_LOW_ACTIVE		(1 << 13)
68 #define IO_APIC_PIN_POLARITY_MASK			(1 << 13)
69 #define IO_APIC_DELIVERY_STATUS_PENDING		(1 << 12)
70 #define IO_APIC_DESTINATION_MODE_PHYSICAL	(0 << 11)
71 #define IO_APIC_DESTINATION_MODE_LOGICAL	(1 << 11)
72 #define IO_APIC_DESTINATION_MODE_MASK		(1 << 11)
73 #define IO_APIC_DELIVERY_MODE_MASK			(7 << 8)
74 #define IO_APIC_DELIVERY_MODE_FIXED			(0 << 8)
75 #define IO_APIC_DELIVERY_MODE_LOWEST_PRIO	(1 << 8)
76 #define IO_APIC_DELIVERY_MODE_SMI			(2 << 8)
77 #define IO_APIC_DELIVERY_MODE_NMI			(4 << 8)
78 #define IO_APIC_DELIVERY_MODE_INIT			(5 << 8)
79 #define IO_APIC_DELIVERY_MODE_EXT_INT		(7 << 8)
80 #define IO_APIC_INTERRUPT_VECTOR_SHIFT		0
81 #define IO_APIC_INTERRUPT_VECTOR_MASK		0xff
82 
83 #define MAX_SUPPORTED_REDIRECTION_ENTRIES	64
84 #define ISA_INTERRUPT_COUNT					16
85 
86 
87 struct ioapic_registers {
88 	volatile uint32	io_register_select;
89 	uint32			reserved[3];
90 	volatile uint32	io_window_register;
91 };
92 
93 
94 struct ioapic {
95 	uint8				number;
96 	uint8				apic_id;
97 	uint32				version;
98 	uint8				max_redirection_entry;
99 	uint8				global_interrupt_base;
100 	uint8				global_interrupt_last;
101 	uint64				level_triggered_mask;
102 	uint64				nmi_mask;
103 
104 	area_id				register_area;
105 	ioapic_registers*	registers;
106 
107 	ioapic*				next;
108 };
109 
110 
111 static int32 sIOAPICPhys = 0;
112 static ioapic* sIOAPICs = NULL;
113 static int32 sSourceOverrides[ISA_INTERRUPT_COUNT];
114 
115 
116 // #pragma mark - I/O APIC
117 
118 
119 static void
120 print_ioapic(struct ioapic& ioapic)
121 {
122 	dprintf("io-apic %u has range %u-%u, %u entries, version 0x%08" B_PRIx32
123 		", apic-id %u\n", ioapic.number, ioapic.global_interrupt_base,
124 		ioapic.global_interrupt_last, ioapic.max_redirection_entry + 1,
125 		ioapic.version, ioapic.apic_id);
126 }
127 
128 
129 static inline struct ioapic*
130 find_ioapic(int32 gsi)
131 {
132 	if (gsi < 0)
133 		return NULL;
134 
135 	struct ioapic* current = sIOAPICs;
136 	while (current != NULL) {
137 		if (gsi >= current->global_interrupt_base
138 			&& gsi <= current->global_interrupt_last) {
139 			return current;
140 		}
141 
142 		current = current->next;
143 	}
144 
145 	return NULL;
146 }
147 
148 
149 static inline uint32
150 ioapic_read_32(struct ioapic& ioapic, uint8 registerSelect)
151 {
152 	ioapic.registers->io_register_select = registerSelect;
153 	return ioapic.registers->io_window_register;
154 }
155 
156 
157 static inline void
158 ioapic_write_32(struct ioapic& ioapic, uint8 registerSelect, uint32 value)
159 {
160 	ioapic.registers->io_register_select = registerSelect;
161 	ioapic.registers->io_window_register = value;
162 }
163 
164 
165 static inline uint64
166 ioapic_read_64(struct ioapic& ioapic, uint8 registerSelect)
167 {
168 	ioapic.registers->io_register_select = registerSelect + 1;
169 	uint64 result = ioapic.registers->io_window_register;
170 	result <<= 32;
171 	ioapic.registers->io_register_select = registerSelect;
172 	result |= ioapic.registers->io_window_register;
173 	return result;
174 }
175 
176 
177 static inline void
178 ioapic_write_64(struct ioapic& ioapic, uint8 registerSelect, uint64 value,
179 	bool maskFirst)
180 {
181 	ioapic.registers->io_register_select
182 		= registerSelect + (maskFirst ? 0 : 1);
183 	ioapic.registers->io_window_register
184 		= (uint32)(value >> (maskFirst ? 0 : 32));
185 	ioapic.registers->io_register_select
186 		= registerSelect + (maskFirst ? 1 : 0);
187 	ioapic.registers->io_window_register
188 		= (uint32)(value >> (maskFirst ? 32 : 0));
189 }
190 
191 
192 static void
193 ioapic_configure_pin(struct ioapic& ioapic, uint8 pin, uint8 vector,
194 	uint8 triggerPolarity, uint16 deliveryMode)
195 {
196 	uint64 entry = ioapic_read_64(ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2);
197 	entry &= ~(IO_APIC_TRIGGER_MODE_MASK | IO_APIC_PIN_POLARITY_MASK
198 		| IO_APIC_INTERRUPT_VECTOR_MASK | IO_APIC_DELIVERY_MODE_MASK);
199 
200 	if (triggerPolarity & B_LEVEL_TRIGGERED) {
201 		entry |= IO_APIC_TRIGGER_MODE_LEVEL;
202 		ioapic.level_triggered_mask |= ((uint64)1 << pin);
203 	} else {
204 		entry |= IO_APIC_TRIGGER_MODE_EDGE;
205 		ioapic.level_triggered_mask &= ~((uint64)1 << pin);
206 	}
207 
208 	if (triggerPolarity & B_LOW_ACTIVE_POLARITY)
209 		entry |= IO_APIC_PIN_POLARITY_LOW_ACTIVE;
210 	else
211 		entry |= IO_APIC_PIN_POLARITY_HIGH_ACTIVE;
212 
213 	entry |= deliveryMode;
214 	entry |= (vector + ARCH_INTERRUPT_BASE) << IO_APIC_INTERRUPT_VECTOR_SHIFT;
215 	ioapic_write_64(ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2, entry, true);
216 }
217 
218 
219 static bool
220 ioapic_is_spurious_interrupt(int32 gsi)
221 {
222 	// the spurious interrupt vector is initialized to the max value in smp
223 	return gsi == 0xff - ARCH_INTERRUPT_BASE;
224 }
225 
226 
227 static bool
228 ioapic_is_level_triggered_interrupt(int32 gsi)
229 {
230 	struct ioapic* ioapic = find_ioapic(gsi);
231 	if (ioapic == NULL)
232 		return false;
233 
234 	uint8 pin = gsi - ioapic->global_interrupt_base;
235 	return (ioapic->level_triggered_mask & ((uint64)1 << pin)) != 0;
236 }
237 
238 
239 static bool
240 ioapic_end_of_interrupt(int32 num)
241 {
242 	apic_end_of_interrupt();
243 	return true;
244 }
245 
246 
247 static void
248 ioapic_assign_interrupt_to_cpu(int32 gsi, int32 cpu)
249 {
250 	if (gsi < ISA_INTERRUPT_COUNT && sSourceOverrides[gsi] != 0)
251 		gsi = sSourceOverrides[gsi];
252 
253 	struct ioapic* ioapic = find_ioapic(gsi);
254 	if (ioapic == NULL)
255 		return;
256 
257 	uint32 apicid = x86_get_cpu_apic_id(cpu);
258 
259 	uint8 pin = gsi - ioapic->global_interrupt_base;
260 	TRACE("ioapic_assign_interrupt_to_cpu: gsi %" B_PRId32
261 		" (io-apic %u pin %u) to cpu %" B_PRId32 " (apic_id %" B_PRIx32 ")\n",
262 		gsi, ioapic->number, pin, cpu, apicid);
263 
264 	uint64 entry = ioapic_read_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2);
265 	entry &= ~(uint64(IO_APIC_DESTINATION_FIELD_MASK)
266 			<< IO_APIC_DESTINATION_FIELD_SHIFT);
267 	entry |= uint64(apicid) << IO_APIC_DESTINATION_FIELD_SHIFT;
268 	ioapic_write_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2, entry, false);
269 }
270 
271 
272 static void
273 ioapic_enable_io_interrupt(int32 gsi)
274 {
275 	// If enabling an overriden source is attempted, enable the override entry
276 	// instead. An interrupt handler was installed at the override GSI to relay
277 	// interrupts to the overriden source.
278 	if (gsi < ISA_INTERRUPT_COUNT && sSourceOverrides[gsi] != 0)
279 		gsi = sSourceOverrides[gsi];
280 
281 	struct ioapic* ioapic = find_ioapic(gsi);
282 	if (ioapic == NULL)
283 		return;
284 
285 	x86_set_irq_source(gsi, IRQ_SOURCE_IOAPIC);
286 
287 	uint8 pin = gsi - ioapic->global_interrupt_base;
288 	TRACE("ioapic_enable_io_interrupt: gsi %" B_PRId32
289 		" -> io-apic %u pin %u\n", gsi, ioapic->number, pin);
290 
291 	uint64 entry = ioapic_read_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2);
292 	entry &= ~IO_APIC_INTERRUPT_MASKED;
293 	ioapic_write_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2, entry, false);
294 }
295 
296 
297 static void
298 ioapic_disable_io_interrupt(int32 gsi)
299 {
300 	struct ioapic* ioapic = find_ioapic(gsi);
301 	if (ioapic == NULL)
302 		return;
303 
304 	uint8 pin = gsi - ioapic->global_interrupt_base;
305 	TRACE("ioapic_disable_io_interrupt: gsi %" B_PRId32
306 		" -> io-apic %u pin %u\n", gsi, ioapic->number, pin);
307 
308 	uint64 entry = ioapic_read_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2);
309 	entry |= IO_APIC_INTERRUPT_MASKED;
310 	ioapic_write_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2, entry, true);
311 }
312 
313 
314 static void
315 ioapic_configure_io_interrupt(int32 gsi, uint32 config)
316 {
317 	struct ioapic* ioapic = find_ioapic(gsi);
318 	if (ioapic == NULL)
319 		return;
320 
321 	uint8 pin = gsi - ioapic->global_interrupt_base;
322 	TRACE("ioapic_configure_io_interrupt: gsi %" B_PRId32
323 		" -> io-apic %u pin %u; config 0x%08" B_PRIx32 "\n", gsi,
324 		ioapic->number, pin, config);
325 
326 	ioapic_configure_pin(*ioapic, pin, gsi, config,
327 		IO_APIC_DELIVERY_MODE_FIXED);
328 }
329 
330 
331 static status_t
332 ioapic_map_ioapic(struct ioapic& ioapic, phys_addr_t physicalAddress)
333 {
334 	ioapic.register_area = vm_map_physical_memory(B_SYSTEM_TEAM, "io-apic",
335 		(void**)&ioapic.registers, ioapic.registers != NULL ? B_EXACT_ADDRESS
336 		: B_ANY_KERNEL_ADDRESS, B_PAGE_SIZE, B_KERNEL_READ_AREA
337 		| B_KERNEL_WRITE_AREA, physicalAddress, ioapic.registers != NULL);
338 	if (ioapic.register_area < 0) {
339 		panic("mapping io-apic %u failed", ioapic.number);
340 		return ioapic.register_area;
341 	}
342 
343 	TRACE("mapped io-apic %u to %p\n", ioapic.number, ioapic.registers);
344 
345 	ioapic.version = ioapic_read_32(ioapic, IO_APIC_VERSION);
346 	if (ioapic.version == 0xffffffff) {
347 		dprintf("io-apic %u seems inaccessible, not using it\n",
348 			ioapic.number);
349 		vm_delete_area(B_SYSTEM_TEAM, ioapic.register_area, true);
350 		ioapic.register_area = -1;
351 		ioapic.registers = NULL;
352 		return B_ERROR;
353 	}
354 
355 	ioapic.max_redirection_entry
356 		= ((ioapic.version >> IO_APIC_MAX_REDIRECTION_ENTRY_SHIFT)
357 			& IO_APIC_MAX_REDIRECTION_ENTRY_MASK);
358 	if (ioapic.max_redirection_entry >= MAX_SUPPORTED_REDIRECTION_ENTRIES) {
359 		dprintf("io-apic %u entry count %d exceeds max supported, only using the "
360 			"first %u entries\n", ioapic.number, ioapic.max_redirection_entry,
361 			(uint8)MAX_SUPPORTED_REDIRECTION_ENTRIES);
362 		ioapic.max_redirection_entry = MAX_SUPPORTED_REDIRECTION_ENTRIES - 1;
363 	}
364 
365 	ioapic.global_interrupt_last
366 		= ioapic.global_interrupt_base + ioapic.max_redirection_entry;
367 
368 	ioapic.nmi_mask = 0;
369 
370 	return B_OK;
371 }
372 
373 
374 static status_t
375 ioapic_initialize_ioapic(struct ioapic& ioapic, uint8 targetAPIC)
376 {
377 	// program the APIC ID
378 	ioapic_write_32(ioapic, IO_APIC_ID, ioapic.apic_id << IO_APIC_ID_SHIFT);
379 
380 	// program the interrupt vectors of the io-apic
381 	ioapic.level_triggered_mask = 0;
382 	uint8 gsi = ioapic.global_interrupt_base;
383 	for (uint8 i = 0; i <= ioapic.max_redirection_entry; i++, gsi++) {
384 		// initialize everything to deliver to the boot CPU in physical mode
385 		// and masked until explicitly enabled through enable_io_interrupt()
386 		uint64 entry = ((uint64)targetAPIC << IO_APIC_DESTINATION_FIELD_SHIFT)
387 			| IO_APIC_INTERRUPT_MASKED | IO_APIC_DESTINATION_MODE_PHYSICAL
388 			| ((gsi + ARCH_INTERRUPT_BASE) << IO_APIC_INTERRUPT_VECTOR_SHIFT);
389 
390 		if (gsi == 0) {
391 			// make GSI 0 into an external interrupt
392 			entry |= IO_APIC_TRIGGER_MODE_EDGE
393 				| IO_APIC_PIN_POLARITY_HIGH_ACTIVE
394 				| IO_APIC_DELIVERY_MODE_EXT_INT;
395 		} else if (gsi < ISA_INTERRUPT_COUNT) {
396 			// identity map the legacy ISA interrupts
397 			entry |= IO_APIC_TRIGGER_MODE_EDGE
398 				| IO_APIC_PIN_POLARITY_HIGH_ACTIVE
399 				| IO_APIC_DELIVERY_MODE_FIXED;
400 		} else {
401 			// and the rest are PCI interrupts
402 			entry |= IO_APIC_TRIGGER_MODE_LEVEL
403 				| IO_APIC_PIN_POLARITY_LOW_ACTIVE
404 				| IO_APIC_DELIVERY_MODE_FIXED;
405 			ioapic.level_triggered_mask |= ((uint64)1 << i);
406 		}
407 
408 		ioapic_write_64(ioapic, IO_APIC_REDIRECTION_TABLE + 2 * i, entry, true);
409 	}
410 
411 	return B_OK;
412 }
413 
414 
415 static int32
416 ioapic_source_override_handler(void* data)
417 {
418 	int32 vector = (addr_t)data;
419 	bool levelTriggered = ioapic_is_level_triggered_interrupt(vector);
420 	return int_io_interrupt_handler(vector, levelTriggered);
421 }
422 
423 
424 static status_t
425 acpi_enumerate_ioapics(acpi_table_madt* madt)
426 {
427 	struct ioapic* lastIOAPIC = sIOAPICs;
428 
429 	acpi_subtable_header* apicEntry
430 		= (acpi_subtable_header*)((uint8*)madt + sizeof(acpi_table_madt));
431 	void* end = ((uint8*)madt + madt->Header.Length);
432 	while (apicEntry < end) {
433 		switch (apicEntry->Type) {
434 			case ACPI_MADT_TYPE_IO_APIC:
435 			{
436 				acpi_madt_io_apic* info = (acpi_madt_io_apic*)apicEntry;
437 				dprintf("found io-apic with address 0x%08" B_PRIx32 ", global "
438 					"interrupt base %" B_PRIu32 ", apic-id %u\n",
439 					(uint32)info->Address, (uint32)info->GlobalIrqBase,
440 					info->Id);
441 
442 				struct ioapic* ioapic
443 					= (struct ioapic*)malloc(sizeof(struct ioapic));
444 				if (ioapic == NULL) {
445 					dprintf("ran out of memory while allocating io-apic "
446 						"structure\n");
447 					return B_NO_MEMORY;
448 				}
449 
450 				ioapic->number
451 					= lastIOAPIC != NULL ? lastIOAPIC->number + 1 : 0;
452 				ioapic->apic_id = info->Id;
453 				ioapic->global_interrupt_base = info->GlobalIrqBase;
454 				ioapic->registers = NULL;
455 				ioapic->next = NULL;
456 
457 				dprintf("mapping io-apic %u at physical address %#" B_PRIx32
458 					"\n", ioapic->number, (uint32)info->Address);
459 				status_t status = ioapic_map_ioapic(*ioapic, info->Address);
460 				if (status != B_OK) {
461 					free(ioapic);
462 					break;
463 				}
464 
465 				print_ioapic(*ioapic);
466 
467 				if (lastIOAPIC == NULL)
468 					sIOAPICs = ioapic;
469 				else
470 					lastIOAPIC->next = ioapic;
471 
472 				lastIOAPIC = ioapic;
473 				break;
474 			}
475 
476 			case ACPI_MADT_TYPE_NMI_SOURCE:
477 			{
478 				acpi_madt_nmi_source* info
479 					= (acpi_madt_nmi_source*)apicEntry;
480 				dprintf("found nmi source global irq %" B_PRIu32 ", flags "
481 					"0x%04x\n", (uint32)info->GlobalIrq,
482 					(uint16)info->IntiFlags);
483 
484 				struct ioapic* ioapic = find_ioapic(info->GlobalIrq);
485 				if (ioapic == NULL) {
486 					dprintf("nmi source for gsi that is not mapped to any "
487 						" io-apic\n");
488 					break;
489 				}
490 
491 				uint8 pin = info->GlobalIrq - ioapic->global_interrupt_base;
492 				ioapic->nmi_mask |= (uint64)1 << pin;
493 				break;
494 			}
495 		}
496 
497 		apicEntry
498 			= (acpi_subtable_header*)((uint8*)apicEntry + apicEntry->Length);
499 	}
500 
501 	return B_OK;
502 }
503 
504 
505 static inline uint32
506 acpi_madt_convert_inti_flags(uint16 flags)
507 {
508 	uint32 config = 0;
509 	switch (flags & ACPI_MADT_POLARITY_MASK) {
510 		case ACPI_MADT_POLARITY_ACTIVE_LOW:
511 			config = B_LOW_ACTIVE_POLARITY;
512 			break;
513 		default:
514 			dprintf("invalid polarity in inti flags\n");
515 			// fall through and assume active high
516 		case ACPI_MADT_POLARITY_ACTIVE_HIGH:
517 		case ACPI_MADT_POLARITY_CONFORMS:
518 			config = B_HIGH_ACTIVE_POLARITY;
519 			break;
520 	}
521 
522 	switch (flags & ACPI_MADT_TRIGGER_MASK) {
523 		case ACPI_MADT_TRIGGER_LEVEL:
524 			config |= B_LEVEL_TRIGGERED;
525 			break;
526 		default:
527 			dprintf("invalid trigger mode in inti flags\n");
528 			// fall through and assume edge triggered
529 		case ACPI_MADT_TRIGGER_CONFORMS:
530 		case ACPI_MADT_TRIGGER_EDGE:
531 			config |= B_EDGE_TRIGGERED;
532 			break;
533 	}
534 
535 	return config;
536 }
537 
538 
539 static void
540 acpi_configure_source_overrides(acpi_table_madt* madt)
541 {
542 	acpi_subtable_header* apicEntry
543 		= (acpi_subtable_header*)((uint8*)madt + sizeof(acpi_table_madt));
544 	void* end = ((uint8*)madt + madt->Header.Length);
545 	while (apicEntry < end) {
546 		switch (apicEntry->Type) {
547 			case ACPI_MADT_TYPE_INTERRUPT_OVERRIDE:
548 			{
549 				acpi_madt_interrupt_override* info
550 					= (acpi_madt_interrupt_override*)apicEntry;
551 				dprintf("found interrupt override for bus %u, source irq %u, "
552 					"global irq %" B_PRIu32 ", flags 0x%08" B_PRIx32 "\n",
553 					info->Bus, info->SourceIrq, (uint32)info->GlobalIrq,
554 					(uint32)info->IntiFlags);
555 
556 				if (info->SourceIrq >= ISA_INTERRUPT_COUNT) {
557 					dprintf("source override exceeds isa interrupt count\n");
558 					break;
559 				}
560 
561 				if (info->SourceIrq != info->GlobalIrq) {
562 					// we need a vector mapping
563 					install_io_interrupt_handler(info->GlobalIrq,
564 						&ioapic_source_override_handler,
565 						(void*)(addr_t)info->SourceIrq, B_NO_ENABLE_COUNTER);
566 
567 					sSourceOverrides[info->SourceIrq] = info->GlobalIrq;
568 				}
569 
570 				// configure non-standard polarity/trigger modes
571 				uint32 config = acpi_madt_convert_inti_flags(info->IntiFlags);
572 				ioapic_configure_io_interrupt(info->GlobalIrq, config);
573 				break;
574 			}
575 
576 			case ACPI_MADT_TYPE_NMI_SOURCE:
577 			{
578 				acpi_madt_nmi_source* info
579 					= (acpi_madt_nmi_source*)apicEntry;
580 				dprintf("found nmi source global irq %" B_PRIu32 ", flags "
581 					"0x%04x\n", (uint32)info->GlobalIrq,
582 					(uint16)info->IntiFlags);
583 
584 				struct ioapic* ioapic = find_ioapic(info->GlobalIrq);
585 				if (ioapic == NULL)
586 					break;
587 
588 				uint8 pin = info->GlobalIrq - ioapic->global_interrupt_base;
589 				uint32 config = acpi_madt_convert_inti_flags(info->IntiFlags);
590 				ioapic_configure_pin(*ioapic, pin, info->GlobalIrq, config,
591 					IO_APIC_DELIVERY_MODE_NMI);
592 				break;
593 			}
594 
595 #ifdef TRACE_IOAPIC
596 			case ACPI_MADT_TYPE_LOCAL_APIC:
597 			{
598 				// purely informational
599 				acpi_madt_local_apic* info = (acpi_madt_local_apic*)apicEntry;
600 				dprintf("found local apic with id %u, processor id %u, "
601 					"flags 0x%08" B_PRIx32 "\n", info->Id, info->ProcessorId,
602 					(uint32)info->LapicFlags);
603 				break;
604 			}
605 
606 			case ACPI_MADT_TYPE_LOCAL_APIC_NMI:
607 			{
608 				// TODO: take these into account, but at apic.cpp
609 				acpi_madt_local_apic_nmi* info
610 					= (acpi_madt_local_apic_nmi*)apicEntry;
611 				dprintf("found local apic nmi source for processor %u, "
612 					"flags 0x%04x, local int %u\n", info->ProcessorId,
613 					(uint16)info->IntiFlags, info->Lint);
614 				break;
615 			}
616 
617 			case ACPI_MADT_TYPE_LOCAL_APIC_OVERRIDE:
618 			{
619 				// TODO: take these into account, but at apic.cpp
620 				acpi_madt_local_apic_override* info
621 					= (acpi_madt_local_apic_override*)apicEntry;
622 				dprintf("found local apic override with address 0x%016" B_PRIx64
623 					"\n", (uint64)info->Address);
624 				break;
625 			}
626 
627 			default:
628 				dprintf("found unhandled subtable of type %u length %u\n",
629 					apicEntry->Type, apicEntry->Length);
630 				break;
631 #endif
632 		}
633 
634 		apicEntry
635 			= (acpi_subtable_header*)((uint8*)apicEntry + apicEntry->Length);
636 	}
637 }
638 
639 
640 static status_t
641 acpi_set_interrupt_model(acpi_module_info* acpiModule, uint32 interruptModel)
642 {
643 	acpi_object_type model;
644 	model.object_type = ACPI_TYPE_INTEGER;
645 	model.integer.integer = interruptModel;
646 
647 	acpi_objects parameter;
648 	parameter.count = 1;
649 	parameter.pointer = &model;
650 
651 	dprintf("setting ACPI interrupt model to %s\n",
652 		interruptModel == ACPI_INTERRUPT_MODEL_PIC ? "PIC"
653 		: (interruptModel == ACPI_INTERRUPT_MODEL_APIC ? "APIC"
654 		: (interruptModel == ACPI_INTERRUPT_MODEL_SAPIC ? "SAPIC"
655 		: "unknown")));
656 
657 	return acpiModule->evaluate_method(NULL, "\\_PIC", &parameter, NULL);
658 }
659 
660 
661 bool
662 ioapic_is_interrupt_available(int32 gsi)
663 {
664 	struct ioapic* ioapic = find_ioapic(gsi);
665 	if (ioapic == NULL)
666 		return false;
667 
668 	uint8 pin = gsi - ioapic->global_interrupt_base;
669 	return (ioapic->nmi_mask & ((uint64)1 << pin)) == 0;
670 }
671 
672 
673 void
674 ioapic_preinit(kernel_args* args)
675 {
676 	sIOAPICPhys = args->arch_args.ioapic_phys;
677 
678 	// The real IO-APIC initialization occurs after PCI initialization.
679 }
680 
681 
682 void
683 ioapic_init()
684 {
685 	static const interrupt_controller ioapicController = {
686 		"82093AA IOAPIC",
687 		&ioapic_enable_io_interrupt,
688 		&ioapic_disable_io_interrupt,
689 		&ioapic_configure_io_interrupt,
690 		&ioapic_is_spurious_interrupt,
691 		&ioapic_is_level_triggered_interrupt,
692 		&ioapic_end_of_interrupt,
693 		&ioapic_assign_interrupt_to_cpu,
694 	};
695 
696 	if (!apic_available())
697 		return;
698 
699 	if (sIOAPICPhys == 0) {
700 		dprintf("no io-apics available, not using io-apics for interrupt "
701 			"routing\n");
702 		return;
703 	}
704 
705 	if (get_safemode_boolean(B_SAFEMODE_DISABLE_IOAPIC, false)) {
706 		dprintf("io-apics explicitly disabled, not using io-apics for "
707 			"interrupt routing\n");
708 		return;
709 	}
710 
711 	// load ACPI module
712 	status_t status;
713 	acpi_module_info* acpiModule;
714 	status = get_module(B_ACPI_MODULE_NAME, (module_info**)&acpiModule);
715 	if (status != B_OK) {
716 		dprintf("ACPI module not available, not configuring io-apics\n");
717 		return;
718 	}
719 	BPrivate::CObjectDeleter<const char, status_t, put_module>
720 		acpiModulePutter(B_ACPI_MODULE_NAME);
721 
722 	acpi_table_madt* madt = NULL;
723 	if (acpiModule->get_table(ACPI_SIG_MADT, 0, (void**)&madt) != B_OK) {
724 		dprintf("failed to get MADT from ACPI, not configuring io-apics\n");
725 		return;
726 	}
727 
728 	status = acpi_enumerate_ioapics(madt);
729 	if (status != B_OK) {
730 		// We don't treat this case as fatal just yet. If we are able to
731 		// route everything with the available IO-APICs we're fine, if not
732 		// we will fail at the routing preparation stage further down.
733 		dprintf("failed to enumerate all io-apics, working with what we got\n");
734 	}
735 
736 	// switch to the APIC interrupt model before retrieving the IRQ routing
737 	// table as it will return different settings depending on the model
738 	status = acpi_set_interrupt_model(acpiModule, ACPI_INTERRUPT_MODEL_APIC);
739 	if (status != B_OK) {
740 		dprintf("failed to put ACPI into APIC interrupt model, ignoring\n");
741 		// don't abort, as the _PIC method is optional and as long as there
742 		// aren't different routings based on it this is non-fatal
743 	}
744 
745 	IRQRoutingTable table;
746 	status = prepare_irq_routing(acpiModule, table,
747 		&ioapic_is_interrupt_available);
748 	if (status != B_OK) {
749 		dprintf("IRQ routing preparation failed, not configuring io-apics\n");
750 		acpi_set_interrupt_model(acpiModule, ACPI_INTERRUPT_MODEL_PIC);
751 			// revert to PIC interrupt model just in case
752 		return;
753 	}
754 
755 	// use the boot CPU as the target for all interrupts
756 	uint8 targetAPIC = x86_get_cpu_apic_id(0);
757 
758 	struct ioapic* current = sIOAPICs;
759 	while (current != NULL) {
760 		status = ioapic_initialize_ioapic(*current, targetAPIC);
761 		if (status != B_OK) {
762 			panic("failed to initialize io-apic %u", current->number);
763 			acpi_set_interrupt_model(acpiModule, ACPI_INTERRUPT_MODEL_PIC);
764 			return;
765 		}
766 
767 		current = current->next;
768 	}
769 
770 #ifdef TRACE_IOAPIC
771 	dprintf("trying interrupt routing:\n");
772 	print_irq_routing_table(table);
773 #endif
774 
775 	status = enable_irq_routing(acpiModule, table);
776 	if (status != B_OK) {
777 		panic("failed to enable IRQ routing");
778 		// if it failed early on it might still work in PIC mode
779 		acpi_set_interrupt_model(acpiModule, ACPI_INTERRUPT_MODEL_PIC);
780 		return;
781 	}
782 
783 	print_irq_routing_table(table);
784 
785 	// configure the source overrides, but let the PCI config below override it
786 	acpi_configure_source_overrides(madt);
787 
788 	// configure IO-APIC interrupts from PCI routing table
789 	for (int i = 0; i < table.Count(); i++) {
790 		irq_routing_entry& entry = table.ElementAt(i);
791 		ioapic_configure_io_interrupt(entry.irq,
792 			entry.polarity | entry.trigger_mode);
793 	}
794 
795 	// kill the local ints on the local APIC
796 	apic_disable_local_ints();
797 		// TODO: This uses the assumption that our init is running on the
798 		// boot CPU and only the boot CPU has the local ints configured
799 		// because it was running in legacy PIC mode. Possibly the other
800 		// local APICs of the other CPUs have them configured as well. It
801 		// shouldn't really harm, but should eventually be corrected.
802 
803 	// disable the legacy PIC
804 	uint16 legacyInterrupts;
805 	pic_disable(legacyInterrupts);
806 
807 	// enable previsouly enabled legacy interrupts
808 	for (uint8 i = 0; i < 16; i++) {
809 		if ((legacyInterrupts & (1 << i)) != 0)
810 			ioapic_enable_io_interrupt(i);
811 	}
812 
813 	// mark the interrupt vectors reserved so they aren't used for other stuff
814 	current = sIOAPICs;
815 	while (current != NULL) {
816 		reserve_io_interrupt_vectors(current->max_redirection_entry + 1,
817 			current->global_interrupt_base, INTERRUPT_TYPE_IRQ);
818 
819 		for (int32 i = 0; i < current->max_redirection_entry + 1; i++) {
820 			x86_set_irq_source(current->global_interrupt_base + i,
821 				IRQ_SOURCE_IOAPIC);
822 		}
823 
824 		current = current->next;
825 	}
826 
827 	// prefer the ioapic over the normal pic
828 	dprintf("using io-apics for interrupt routing\n");
829 	arch_int_set_interrupt_controller(ioapicController);
830 }
831