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