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
87struct ioapic_registers {
88	volatile uint32	io_register_select;
89	uint32			reserved[3];
90	volatile uint32	io_window_register;
91};
92
93
94struct 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
111static int32 sIOAPICPhys = 0;
112static ioapic* sIOAPICs = NULL;
113static int32 sSourceOverrides[ISA_INTERRUPT_COUNT];
114
115
116// #pragma mark - I/O APIC
117
118
119static void
120print_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
129static inline struct ioapic*
130find_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
149static inline uint32
150ioapic_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
157static inline void
158ioapic_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
165static inline uint64
166ioapic_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
177static inline void
178ioapic_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
192static void
193ioapic_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
219static bool
220ioapic_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
227static bool
228ioapic_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
239static bool
240ioapic_end_of_interrupt(int32 num)
241{
242	apic_end_of_interrupt();
243	return true;
244}
245
246
247static void
248ioapic_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
272static void
273ioapic_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
297static void
298ioapic_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
314static void
315ioapic_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
331static status_t
332ioapic_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
374static status_t
375ioapic_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
415static int32
416ioapic_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
424static status_t
425acpi_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
505static inline uint32
506acpi_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
539static void
540acpi_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
640static status_t
641acpi_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
661bool
662ioapic_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
673void
674ioapic_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
682void
683ioapic_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