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