1dc14d97bSMichael Lotz /* 2dc14d97bSMichael Lotz * Copyright 2008-2011, Michael Lotz, mmlr@mlotz.ch. 3dc14d97bSMichael Lotz * Distributed under the terms of the MIT License. 4dc14d97bSMichael Lotz */ 5dc14d97bSMichael Lotz 6dc14d97bSMichael Lotz #include <arch/x86/ioapic.h> 7dc14d97bSMichael Lotz 8dc14d97bSMichael Lotz #include <int.h> 9dc14d97bSMichael Lotz #include <vm/vm.h> 10dc14d97bSMichael Lotz 11dc14d97bSMichael Lotz #include "irq_routing_table.h" 12dc14d97bSMichael Lotz 13dc14d97bSMichael Lotz #include <ACPI.h> 14dc14d97bSMichael Lotz #include <AutoDeleter.h> 15dc14d97bSMichael Lotz #include <safemode.h> 16dc14d97bSMichael Lotz #include <string.h> 17dc14d97bSMichael Lotz #include <stdio.h> 18dc14d97bSMichael Lotz 19dc14d97bSMichael Lotz #include <arch/x86/apic.h> 20dc14d97bSMichael Lotz #include <arch/x86/arch_int.h> 21*d897a478SPawel Dziepak #include <arch/x86/arch_smp.h> 22dc14d97bSMichael Lotz #include <arch/x86/pic.h> 23dc14d97bSMichael Lotz 24d11e32a8SMichael Lotz // to gain access to the ACPICA types 25d11e32a8SMichael Lotz #include "acpi.h" 26d11e32a8SMichael Lotz 27dc14d97bSMichael Lotz 28dc14d97bSMichael Lotz //#define TRACE_IOAPIC 29dc14d97bSMichael Lotz #ifdef TRACE_IOAPIC 30*d897a478SPawel Dziepak # define TRACE(...) dprintf(__VA_ARGS__) 31dc14d97bSMichael Lotz #else 32*d897a478SPawel Dziepak # define TRACE(...) (void)0 33dc14d97bSMichael Lotz #endif 34dc14d97bSMichael Lotz 35dc14d97bSMichael Lotz 36dc14d97bSMichael Lotz // ACPI interrupt models 37dc14d97bSMichael Lotz #define ACPI_INTERRUPT_MODEL_PIC 0 38dc14d97bSMichael Lotz #define ACPI_INTERRUPT_MODEL_APIC 1 39dc14d97bSMichael Lotz #define ACPI_INTERRUPT_MODEL_SAPIC 2 40dc14d97bSMichael Lotz 41dc14d97bSMichael Lotz 42dc14d97bSMichael Lotz // Definitions for a 82093AA IO APIC controller 430f91697fSMichael Lotz #define IO_APIC_ID 0x00 44dc14d97bSMichael Lotz #define IO_APIC_VERSION 0x01 45dc14d97bSMichael Lotz #define IO_APIC_ARBITRATION 0x02 46dc14d97bSMichael Lotz #define IO_APIC_REDIRECTION_TABLE 0x10 // entry = base + 2 * index 47dc14d97bSMichael Lotz 480f91697fSMichael Lotz // Fields for the id register 490f91697fSMichael Lotz #define IO_APIC_ID_SHIFT 24 5063475256SMichael Lotz #define IO_APIC_ID_MASK 0xff 510f91697fSMichael Lotz 52dc14d97bSMichael Lotz // Fields for the version register 53dc14d97bSMichael Lotz #define IO_APIC_VERSION_SHIFT 0 54dc14d97bSMichael Lotz #define IO_APIC_VERSION_MASK 0xff 55dc14d97bSMichael Lotz #define IO_APIC_MAX_REDIRECTION_ENTRY_SHIFT 16 56dc14d97bSMichael Lotz #define IO_APIC_MAX_REDIRECTION_ENTRY_MASK 0xff 57dc14d97bSMichael Lotz 58dc14d97bSMichael Lotz // Fields of each redirection table entry 59dc14d97bSMichael Lotz #define IO_APIC_DESTINATION_FIELD_SHIFT 56 60f91cbddeSMichael Lotz #define IO_APIC_DESTINATION_FIELD_MASK 0xff 6174e9ede3SMichael Lotz #define IO_APIC_INTERRUPT_MASKED (1 << 16) 6274e9ede3SMichael Lotz #define IO_APIC_TRIGGER_MODE_EDGE (0 << 16) 6374e9ede3SMichael Lotz #define IO_APIC_TRIGGER_MODE_LEVEL (1 << 15) 6474e9ede3SMichael Lotz #define IO_APIC_TRIGGER_MODE_MASK (1 << 15) 6574e9ede3SMichael Lotz #define IO_APIC_REMOTE_IRR (1 << 14) 6674e9ede3SMichael Lotz #define IO_APIC_PIN_POLARITY_HIGH_ACTIVE (0 << 13) 6774e9ede3SMichael Lotz #define IO_APIC_PIN_POLARITY_LOW_ACTIVE (1 << 13) 6874e9ede3SMichael Lotz #define IO_APIC_PIN_POLARITY_MASK (1 << 13) 6974e9ede3SMichael Lotz #define IO_APIC_DELIVERY_STATUS_PENDING (1 << 12) 7074e9ede3SMichael Lotz #define IO_APIC_DESTINATION_MODE_PHYSICAL (0 << 11) 7174e9ede3SMichael Lotz #define IO_APIC_DESTINATION_MODE_LOGICAL (1 << 11) 7274e9ede3SMichael Lotz #define IO_APIC_DESTINATION_MODE_MASK (1 << 11) 7374e9ede3SMichael Lotz #define IO_APIC_DELIVERY_MODE_MASK (7 << 8) 7474e9ede3SMichael Lotz #define IO_APIC_DELIVERY_MODE_FIXED (0 << 8) 7574e9ede3SMichael Lotz #define IO_APIC_DELIVERY_MODE_LOWEST_PRIO (1 << 8) 7674e9ede3SMichael Lotz #define IO_APIC_DELIVERY_MODE_SMI (2 << 8) 7774e9ede3SMichael Lotz #define IO_APIC_DELIVERY_MODE_NMI (4 << 8) 7874e9ede3SMichael Lotz #define IO_APIC_DELIVERY_MODE_INIT (5 << 8) 7974e9ede3SMichael Lotz #define IO_APIC_DELIVERY_MODE_EXT_INT (7 << 8) 80dc14d97bSMichael Lotz #define IO_APIC_INTERRUPT_VECTOR_SHIFT 0 81dc14d97bSMichael Lotz #define IO_APIC_INTERRUPT_VECTOR_MASK 0xff 82dc14d97bSMichael Lotz 8322d72c8eSMichael Lotz #define MAX_SUPPORTED_REDIRECTION_ENTRIES 64 84fb5a1727SMichael Lotz #define ISA_INTERRUPT_COUNT 16 85fb5a1727SMichael Lotz 86dc14d97bSMichael Lotz 875d01e61aSMichael Lotz struct ioapic_registers { 88dc14d97bSMichael Lotz volatile uint32 io_register_select; 89dc14d97bSMichael Lotz uint32 reserved[3]; 90dc14d97bSMichael Lotz volatile uint32 io_window_register; 915d01e61aSMichael Lotz }; 92dc14d97bSMichael Lotz 93dc14d97bSMichael Lotz 945d01e61aSMichael Lotz struct ioapic { 955d01e61aSMichael Lotz uint8 number; 9663475256SMichael Lotz uint8 apic_id; 9763475256SMichael Lotz uint32 version; 985d01e61aSMichael Lotz uint8 max_redirection_entry; 995d01e61aSMichael Lotz uint8 global_interrupt_base; 1005d01e61aSMichael Lotz uint8 global_interrupt_last; 1015d01e61aSMichael Lotz uint64 level_triggered_mask; 10286d59c04SMichael Lotz uint64 nmi_mask; 1035d01e61aSMichael Lotz 1045d01e61aSMichael Lotz area_id register_area; 1055d01e61aSMichael Lotz ioapic_registers* registers; 1065d01e61aSMichael Lotz 1075d01e61aSMichael Lotz ioapic* next; 1085d01e61aSMichael Lotz }; 1095d01e61aSMichael Lotz 1105d01e61aSMichael Lotz 1118908aef9SMichael Lotz static ioapic* sIOAPICs = NULL; 112fb5a1727SMichael Lotz static int32 sSourceOverrides[ISA_INTERRUPT_COUNT]; 113dc14d97bSMichael Lotz 114dc14d97bSMichael Lotz 115dc14d97bSMichael Lotz // #pragma mark - I/O APIC 116dc14d97bSMichael Lotz 117dc14d97bSMichael Lotz 11863475256SMichael Lotz static void 11963475256SMichael Lotz print_ioapic(struct ioapic& ioapic) 12063475256SMichael Lotz { 121fa6327c9SAlex Smith dprintf("io-apic %u has range %u-%u, %u entries, version 0x%08" B_PRIx32 122fa6327c9SAlex Smith ", apic-id %u\n", ioapic.number, ioapic.global_interrupt_base, 12363475256SMichael Lotz ioapic.global_interrupt_last, ioapic.max_redirection_entry + 1, 12463475256SMichael Lotz ioapic.version, ioapic.apic_id); 12563475256SMichael Lotz } 12663475256SMichael Lotz 12763475256SMichael Lotz 1285d01e61aSMichael Lotz static inline struct ioapic* 1295d01e61aSMichael Lotz find_ioapic(int32 gsi) 130dc14d97bSMichael Lotz { 1315d01e61aSMichael Lotz if (gsi < 0) 1325d01e61aSMichael Lotz return NULL; 1335d01e61aSMichael Lotz 1348908aef9SMichael Lotz struct ioapic* current = sIOAPICs; 1355d01e61aSMichael Lotz while (current != NULL) { 1365d01e61aSMichael Lotz if (gsi >= current->global_interrupt_base 1375d01e61aSMichael Lotz && gsi <= current->global_interrupt_last) { 1385d01e61aSMichael Lotz return current; 1395d01e61aSMichael Lotz } 1405d01e61aSMichael Lotz 1415d01e61aSMichael Lotz current = current->next; 1425d01e61aSMichael Lotz } 1435d01e61aSMichael Lotz 1445d01e61aSMichael Lotz return NULL; 1455d01e61aSMichael Lotz } 1465d01e61aSMichael Lotz 1475d01e61aSMichael Lotz 1485d01e61aSMichael Lotz static inline uint32 1495d01e61aSMichael Lotz ioapic_read_32(struct ioapic& ioapic, uint8 registerSelect) 1505d01e61aSMichael Lotz { 1515d01e61aSMichael Lotz ioapic.registers->io_register_select = registerSelect; 1525d01e61aSMichael Lotz return ioapic.registers->io_window_register; 153dc14d97bSMichael Lotz } 154dc14d97bSMichael Lotz 155dc14d97bSMichael Lotz 156dc14d97bSMichael Lotz static inline void 1575d01e61aSMichael Lotz ioapic_write_32(struct ioapic& ioapic, uint8 registerSelect, uint32 value) 158dc14d97bSMichael Lotz { 1595d01e61aSMichael Lotz ioapic.registers->io_register_select = registerSelect; 1605d01e61aSMichael Lotz ioapic.registers->io_window_register = value; 161dc14d97bSMichael Lotz } 162dc14d97bSMichael Lotz 163dc14d97bSMichael Lotz 164dc14d97bSMichael Lotz static inline uint64 1655d01e61aSMichael Lotz ioapic_read_64(struct ioapic& ioapic, uint8 registerSelect) 166dc14d97bSMichael Lotz { 1675d01e61aSMichael Lotz ioapic.registers->io_register_select = registerSelect + 1; 1685d01e61aSMichael Lotz uint64 result = ioapic.registers->io_window_register; 169dc14d97bSMichael Lotz result <<= 32; 1705d01e61aSMichael Lotz ioapic.registers->io_register_select = registerSelect; 1715d01e61aSMichael Lotz result |= ioapic.registers->io_window_register; 172dc14d97bSMichael Lotz return result; 173dc14d97bSMichael Lotz } 174dc14d97bSMichael Lotz 175dc14d97bSMichael Lotz 176dc14d97bSMichael Lotz static inline void 1770798779aSMichael Lotz ioapic_write_64(struct ioapic& ioapic, uint8 registerSelect, uint64 value, 1780798779aSMichael Lotz bool maskFirst) 179dc14d97bSMichael Lotz { 1800798779aSMichael Lotz ioapic.registers->io_register_select 1810798779aSMichael Lotz = registerSelect + (maskFirst ? 0 : 1); 1820798779aSMichael Lotz ioapic.registers->io_window_register 1830798779aSMichael Lotz = (uint32)(value >> (maskFirst ? 0 : 32)); 1840798779aSMichael Lotz ioapic.registers->io_register_select 1850798779aSMichael Lotz = registerSelect + (maskFirst ? 1 : 0); 1860798779aSMichael Lotz ioapic.registers->io_window_register 1870798779aSMichael Lotz = (uint32)(value >> (maskFirst ? 32 : 0)); 188dc14d97bSMichael Lotz } 189dc14d97bSMichael Lotz 190dc14d97bSMichael Lotz 1919ae3fdcbSMichael Lotz static void 1929ae3fdcbSMichael Lotz ioapic_configure_pin(struct ioapic& ioapic, uint8 pin, uint8 vector, 19374e9ede3SMichael Lotz uint8 triggerPolarity, uint16 deliveryMode) 1949ae3fdcbSMichael Lotz { 1959ae3fdcbSMichael Lotz uint64 entry = ioapic_read_64(ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2); 19674e9ede3SMichael Lotz entry &= ~(IO_APIC_TRIGGER_MODE_MASK | IO_APIC_PIN_POLARITY_MASK 19774e9ede3SMichael Lotz | IO_APIC_INTERRUPT_VECTOR_MASK | IO_APIC_DELIVERY_MODE_MASK); 1989ae3fdcbSMichael Lotz 1999ae3fdcbSMichael Lotz if (triggerPolarity & B_LEVEL_TRIGGERED) { 20074e9ede3SMichael Lotz entry |= IO_APIC_TRIGGER_MODE_LEVEL; 2019ae3fdcbSMichael Lotz ioapic.level_triggered_mask |= ((uint64)1 << pin); 2029ae3fdcbSMichael Lotz } else { 20374e9ede3SMichael Lotz entry |= IO_APIC_TRIGGER_MODE_EDGE; 2049ae3fdcbSMichael Lotz ioapic.level_triggered_mask &= ~((uint64)1 << pin); 2059ae3fdcbSMichael Lotz } 2069ae3fdcbSMichael Lotz 2079ae3fdcbSMichael Lotz if (triggerPolarity & B_LOW_ACTIVE_POLARITY) 20874e9ede3SMichael Lotz entry |= IO_APIC_PIN_POLARITY_LOW_ACTIVE; 2099ae3fdcbSMichael Lotz else 21074e9ede3SMichael Lotz entry |= IO_APIC_PIN_POLARITY_HIGH_ACTIVE; 2119ae3fdcbSMichael Lotz 21274e9ede3SMichael Lotz entry |= deliveryMode; 2139ae3fdcbSMichael Lotz entry |= (vector + ARCH_INTERRUPT_BASE) << IO_APIC_INTERRUPT_VECTOR_SHIFT; 2149ae3fdcbSMichael Lotz ioapic_write_64(ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2, entry, true); 2159ae3fdcbSMichael Lotz } 2169ae3fdcbSMichael Lotz 2179ae3fdcbSMichael Lotz 218dc14d97bSMichael Lotz static bool 219eda74390SMichael Lotz ioapic_is_spurious_interrupt(int32 gsi) 220dc14d97bSMichael Lotz { 221dc14d97bSMichael Lotz // the spurious interrupt vector is initialized to the max value in smp 222eda74390SMichael Lotz return gsi == 0xff - ARCH_INTERRUPT_BASE; 223dc14d97bSMichael Lotz } 224dc14d97bSMichael Lotz 225dc14d97bSMichael Lotz 226dc14d97bSMichael Lotz static bool 2275d01e61aSMichael Lotz ioapic_is_level_triggered_interrupt(int32 gsi) 228dc14d97bSMichael Lotz { 2295d01e61aSMichael Lotz struct ioapic* ioapic = find_ioapic(gsi); 2305d01e61aSMichael Lotz if (ioapic == NULL) 231dc14d97bSMichael Lotz return false; 232dc14d97bSMichael Lotz 2335d01e61aSMichael Lotz uint8 pin = gsi - ioapic->global_interrupt_base; 234f91cbddeSMichael Lotz return (ioapic->level_triggered_mask & ((uint64)1 << pin)) != 0; 235dc14d97bSMichael Lotz } 236dc14d97bSMichael Lotz 237dc14d97bSMichael Lotz 238dc14d97bSMichael Lotz static bool 239dc14d97bSMichael Lotz ioapic_end_of_interrupt(int32 num) 240dc14d97bSMichael Lotz { 241dc14d97bSMichael Lotz apic_end_of_interrupt(); 242dc14d97bSMichael Lotz return true; 243dc14d97bSMichael Lotz } 244dc14d97bSMichael Lotz 245dc14d97bSMichael Lotz 246dc14d97bSMichael Lotz static void 247*d897a478SPawel Dziepak ioapic_assign_interrupt_to_cpu(int32 gsi, int32 cpu) 248*d897a478SPawel Dziepak { 249*d897a478SPawel Dziepak if (gsi < ISA_INTERRUPT_COUNT && sSourceOverrides[gsi] != 0) 250*d897a478SPawel Dziepak gsi = sSourceOverrides[gsi]; 251*d897a478SPawel Dziepak 252*d897a478SPawel Dziepak struct ioapic* ioapic = find_ioapic(gsi); 253*d897a478SPawel Dziepak if (ioapic == NULL) 254*d897a478SPawel Dziepak return; 255*d897a478SPawel Dziepak 256*d897a478SPawel Dziepak uint32 apicid = x86_get_cpu_apic_id(cpu); 257*d897a478SPawel Dziepak 258*d897a478SPawel Dziepak uint8 pin = gsi - ioapic->global_interrupt_base; 259*d897a478SPawel Dziepak TRACE("ioapic_assign_interrupt_to_cpu: gsi %ld (io-apic %u pin %u) to" 260*d897a478SPawel Dziepak " cpu %ld (apic_id %lu)\n", gsi, ioapic->number, pin, cpu, apicid); 261*d897a478SPawel Dziepak 262*d897a478SPawel Dziepak uint64 entry = ioapic_read_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2); 263*d897a478SPawel Dziepak entry &= ~(uint64(IO_APIC_DESTINATION_FIELD_MASK) 264*d897a478SPawel Dziepak << IO_APIC_DESTINATION_FIELD_SHIFT); 265*d897a478SPawel Dziepak entry |= uint64(apicid) << IO_APIC_DESTINATION_FIELD_SHIFT; 266*d897a478SPawel Dziepak ioapic_write_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2, entry, false); 267*d897a478SPawel Dziepak } 268*d897a478SPawel Dziepak 269*d897a478SPawel Dziepak 270*d897a478SPawel Dziepak static void 2715d01e61aSMichael Lotz ioapic_enable_io_interrupt(int32 gsi) 272dc14d97bSMichael Lotz { 273fb5a1727SMichael Lotz // If enabling an overriden source is attempted, enable the override entry 274cb4e75d3SMichael Lotz // instead. An interrupt handler was installed at the override GSI to relay 275fb5a1727SMichael Lotz // interrupts to the overriden source. 276fb5a1727SMichael Lotz if (gsi < ISA_INTERRUPT_COUNT && sSourceOverrides[gsi] != 0) 277fb5a1727SMichael Lotz gsi = sSourceOverrides[gsi]; 278fb5a1727SMichael Lotz 2795d01e61aSMichael Lotz struct ioapic* ioapic = find_ioapic(gsi); 2805d01e61aSMichael Lotz if (ioapic == NULL) 281dc14d97bSMichael Lotz return; 282dc14d97bSMichael Lotz 2835d01e61aSMichael Lotz uint8 pin = gsi - ioapic->global_interrupt_base; 284*d897a478SPawel Dziepak TRACE("ioapic_enable_io_interrupt: gsi %ld -> io-apic %u pin %u\n", 285*d897a478SPawel Dziepak gsi, ioapic->number, pin); 286dc14d97bSMichael Lotz 2875d01e61aSMichael Lotz uint64 entry = ioapic_read_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2); 28874e9ede3SMichael Lotz entry &= ~IO_APIC_INTERRUPT_MASKED; 2890798779aSMichael Lotz ioapic_write_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2, entry, false); 290dc14d97bSMichael Lotz } 291dc14d97bSMichael Lotz 292dc14d97bSMichael Lotz 293dc14d97bSMichael Lotz static void 2945d01e61aSMichael Lotz ioapic_disable_io_interrupt(int32 gsi) 295dc14d97bSMichael Lotz { 2965d01e61aSMichael Lotz struct ioapic* ioapic = find_ioapic(gsi); 2975d01e61aSMichael Lotz if (ioapic == NULL) 298dc14d97bSMichael Lotz return; 299dc14d97bSMichael Lotz 3005d01e61aSMichael Lotz uint8 pin = gsi - ioapic->global_interrupt_base; 301*d897a478SPawel Dziepak TRACE("ioapic_disable_io_interrupt: gsi %ld -> io-apic %u pin %u\n", 302*d897a478SPawel Dziepak gsi, ioapic->number, pin); 303dc14d97bSMichael Lotz 3045d01e61aSMichael Lotz uint64 entry = ioapic_read_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2); 30574e9ede3SMichael Lotz entry |= IO_APIC_INTERRUPT_MASKED; 3060798779aSMichael Lotz ioapic_write_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2, entry, true); 307dc14d97bSMichael Lotz } 308dc14d97bSMichael Lotz 309dc14d97bSMichael Lotz 310dc14d97bSMichael Lotz static void 3115d01e61aSMichael Lotz ioapic_configure_io_interrupt(int32 gsi, uint32 config) 312dc14d97bSMichael Lotz { 3135d01e61aSMichael Lotz struct ioapic* ioapic = find_ioapic(gsi); 3145d01e61aSMichael Lotz if (ioapic == NULL) 315dc14d97bSMichael Lotz return; 316dc14d97bSMichael Lotz 3175d01e61aSMichael Lotz uint8 pin = gsi - ioapic->global_interrupt_base; 318*d897a478SPawel Dziepak TRACE("ioapic_configure_io_interrupt: gsi %ld -> io-apic %u pin %u; " 319*d897a478SPawel Dziepak "config 0x%08lx\n", gsi, ioapic->number, pin, config); 320dc14d97bSMichael Lotz 3219ae3fdcbSMichael Lotz ioapic_configure_pin(*ioapic, pin, gsi, config, 3229ae3fdcbSMichael Lotz IO_APIC_DELIVERY_MODE_FIXED); 3235d01e61aSMichael Lotz } 3245d01e61aSMichael Lotz 3255d01e61aSMichael Lotz 3265d01e61aSMichael Lotz static status_t 3275d01e61aSMichael Lotz ioapic_map_ioapic(struct ioapic& ioapic, phys_addr_t physicalAddress) 3285d01e61aSMichael Lotz { 3295d01e61aSMichael Lotz ioapic.register_area = vm_map_physical_memory(B_SYSTEM_TEAM, "io-apic", 3305d01e61aSMichael Lotz (void**)&ioapic.registers, ioapic.registers != NULL ? B_EXACT_ADDRESS 3315d01e61aSMichael Lotz : B_ANY_KERNEL_ADDRESS, B_PAGE_SIZE, B_KERNEL_READ_AREA 332d11e32a8SMichael Lotz | B_KERNEL_WRITE_AREA, physicalAddress, ioapic.registers != NULL); 3335d01e61aSMichael Lotz if (ioapic.register_area < 0) { 3345d01e61aSMichael Lotz panic("mapping io-apic %u failed", ioapic.number); 3355d01e61aSMichael Lotz return ioapic.register_area; 3365d01e61aSMichael Lotz } 3375d01e61aSMichael Lotz 338*d897a478SPawel Dziepak TRACE("mapped io-apic %u to %p\n", ioapic.number, ioapic.registers); 339d11e32a8SMichael Lotz 34063475256SMichael Lotz ioapic.version = ioapic_read_32(ioapic, IO_APIC_VERSION); 34163475256SMichael Lotz if (ioapic.version == 0xffffffff) { 3425d01e61aSMichael Lotz dprintf("io-apic %u seems inaccessible, not using it\n", 3435d01e61aSMichael Lotz ioapic.number); 3445d01e61aSMichael Lotz vm_delete_area(B_SYSTEM_TEAM, ioapic.register_area, true); 3455d01e61aSMichael Lotz ioapic.register_area = -1; 3465d01e61aSMichael Lotz ioapic.registers = NULL; 3475d01e61aSMichael Lotz return B_ERROR; 3485d01e61aSMichael Lotz } 3495d01e61aSMichael Lotz 3505d01e61aSMichael Lotz ioapic.max_redirection_entry 35163475256SMichael Lotz = ((ioapic.version >> IO_APIC_MAX_REDIRECTION_ENTRY_SHIFT) 3525d01e61aSMichael Lotz & IO_APIC_MAX_REDIRECTION_ENTRY_MASK); 35322d72c8eSMichael Lotz if (ioapic.max_redirection_entry >= MAX_SUPPORTED_REDIRECTION_ENTRIES) { 35422d72c8eSMichael Lotz dprintf("io-apic %u entry count exceeds max supported, only using the " 35522d72c8eSMichael Lotz "first %u entries", ioapic.number, 35622d72c8eSMichael Lotz (uint8)MAX_SUPPORTED_REDIRECTION_ENTRIES); 35722d72c8eSMichael Lotz ioapic.max_redirection_entry = MAX_SUPPORTED_REDIRECTION_ENTRIES - 1; 35822d72c8eSMichael Lotz } 3595d01e61aSMichael Lotz 3605d01e61aSMichael Lotz ioapic.global_interrupt_last 3615d01e61aSMichael Lotz = ioapic.global_interrupt_base + ioapic.max_redirection_entry; 3625d01e61aSMichael Lotz 363aa373c43SMichael Lotz ioapic.nmi_mask = 0; 364aa373c43SMichael Lotz 3655d01e61aSMichael Lotz return B_OK; 3665d01e61aSMichael Lotz } 3675d01e61aSMichael Lotz 3685d01e61aSMichael Lotz 3695d01e61aSMichael Lotz static status_t 37063475256SMichael Lotz ioapic_initialize_ioapic(struct ioapic& ioapic, uint8 targetAPIC) 3715d01e61aSMichael Lotz { 37263475256SMichael Lotz // program the APIC ID 37363475256SMichael Lotz ioapic_write_32(ioapic, IO_APIC_ID, ioapic.apic_id << IO_APIC_ID_SHIFT); 37463475256SMichael Lotz 37586d59c04SMichael Lotz // program the interrupt vectors of the io-apic 376aa373c43SMichael Lotz ioapic.level_triggered_mask = 0; 377e9d55697SMichael Lotz uint8 gsi = ioapic.global_interrupt_base; 378e9d55697SMichael Lotz for (uint8 i = 0; i <= ioapic.max_redirection_entry; i++, gsi++) { 3795d01e61aSMichael Lotz // initialize everything to deliver to the boot CPU in physical mode 3805d01e61aSMichael Lotz // and masked until explicitly enabled through enable_io_interrupt() 38163475256SMichael Lotz uint64 entry = ((uint64)targetAPIC << IO_APIC_DESTINATION_FIELD_SHIFT) 38274e9ede3SMichael Lotz | IO_APIC_INTERRUPT_MASKED | IO_APIC_DESTINATION_MODE_PHYSICAL 3837a8ce431SMichael Lotz | ((gsi + ARCH_INTERRUPT_BASE) << IO_APIC_INTERRUPT_VECTOR_SHIFT); 3845d01e61aSMichael Lotz 385e9d55697SMichael Lotz if (gsi == 0) { 386e9d55697SMichael Lotz // make GSI 0 into an external interrupt 38774e9ede3SMichael Lotz entry |= IO_APIC_TRIGGER_MODE_EDGE 38874e9ede3SMichael Lotz | IO_APIC_PIN_POLARITY_HIGH_ACTIVE 38974e9ede3SMichael Lotz | IO_APIC_DELIVERY_MODE_EXT_INT; 390fb5a1727SMichael Lotz } else if (gsi < ISA_INTERRUPT_COUNT) { 391fb5a1727SMichael Lotz // identity map the legacy ISA interrupts 39274e9ede3SMichael Lotz entry |= IO_APIC_TRIGGER_MODE_EDGE 39374e9ede3SMichael Lotz | IO_APIC_PIN_POLARITY_HIGH_ACTIVE 39474e9ede3SMichael Lotz | IO_APIC_DELIVERY_MODE_FIXED; 3955d01e61aSMichael Lotz } else { 3965d01e61aSMichael Lotz // and the rest are PCI interrupts 39774e9ede3SMichael Lotz entry |= IO_APIC_TRIGGER_MODE_LEVEL 39874e9ede3SMichael Lotz | IO_APIC_PIN_POLARITY_LOW_ACTIVE 39974e9ede3SMichael Lotz | IO_APIC_DELIVERY_MODE_FIXED; 400f91cbddeSMichael Lotz ioapic.level_triggered_mask |= ((uint64)1 << i); 4015d01e61aSMichael Lotz } 4025d01e61aSMichael Lotz 4030798779aSMichael Lotz ioapic_write_64(ioapic, IO_APIC_REDIRECTION_TABLE + 2 * i, entry, true); 4045d01e61aSMichael Lotz } 4055d01e61aSMichael Lotz 4065d01e61aSMichael Lotz return B_OK; 407dc14d97bSMichael Lotz } 408dc14d97bSMichael Lotz 409dc14d97bSMichael Lotz 410fb5a1727SMichael Lotz static int32 411fb5a1727SMichael Lotz ioapic_source_override_handler(void* data) 412d11e32a8SMichael Lotz { 413fa6327c9SAlex Smith int32 vector = (addr_t)data; 414fb5a1727SMichael Lotz bool levelTriggered = ioapic_is_level_triggered_interrupt(vector); 415fb5a1727SMichael Lotz return int_io_interrupt_handler(vector, levelTriggered); 416d11e32a8SMichael Lotz } 417d11e32a8SMichael Lotz 418fb5a1727SMichael Lotz 419fb5a1727SMichael Lotz static status_t 420fb5a1727SMichael Lotz acpi_enumerate_ioapics(acpi_table_madt* madt) 421fb5a1727SMichael Lotz { 4228908aef9SMichael Lotz struct ioapic* lastIOAPIC = sIOAPICs; 423d11e32a8SMichael Lotz 424d11e32a8SMichael Lotz acpi_subtable_header* apicEntry 425d11e32a8SMichael Lotz = (acpi_subtable_header*)((uint8*)madt + sizeof(acpi_table_madt)); 426d11e32a8SMichael Lotz void* end = ((uint8*)madt + madt->Header.Length); 427d11e32a8SMichael Lotz while (apicEntry < end) { 428d11e32a8SMichael Lotz switch (apicEntry->Type) { 429d11e32a8SMichael Lotz case ACPI_MADT_TYPE_IO_APIC: 430d11e32a8SMichael Lotz { 431d11e32a8SMichael Lotz acpi_madt_io_apic* info = (acpi_madt_io_apic*)apicEntry; 432fa6327c9SAlex Smith dprintf("found io-apic with address 0x%08" B_PRIx32 ", global " 433fa6327c9SAlex Smith "interrupt base %" B_PRIu32 ", apic-id %u\n", 434fa6327c9SAlex Smith (uint32)info->Address, (uint32)info->GlobalIrqBase, 435fa6327c9SAlex Smith info->Id); 436d11e32a8SMichael Lotz 437d11e32a8SMichael Lotz struct ioapic* ioapic 438d11e32a8SMichael Lotz = (struct ioapic*)malloc(sizeof(struct ioapic)); 439d11e32a8SMichael Lotz if (ioapic == NULL) { 440d11e32a8SMichael Lotz dprintf("ran out of memory while allocating io-apic " 441d11e32a8SMichael Lotz "structure\n"); 442d11e32a8SMichael Lotz return B_NO_MEMORY; 443d11e32a8SMichael Lotz } 444d11e32a8SMichael Lotz 4458908aef9SMichael Lotz ioapic->number 4468908aef9SMichael Lotz = lastIOAPIC != NULL ? lastIOAPIC->number + 1 : 0; 44763475256SMichael Lotz ioapic->apic_id = info->Id; 448d11e32a8SMichael Lotz ioapic->global_interrupt_base = info->GlobalIrqBase; 449d11e32a8SMichael Lotz ioapic->registers = NULL; 450d11e32a8SMichael Lotz ioapic->next = NULL; 451d11e32a8SMichael Lotz 452fa6327c9SAlex Smith dprintf("mapping io-apic %u at physical address %#" B_PRIx32 453fa6327c9SAlex Smith "\n", ioapic->number, (uint32)info->Address); 454d11e32a8SMichael Lotz status_t status = ioapic_map_ioapic(*ioapic, info->Address); 45563475256SMichael Lotz if (status != B_OK) { 45663475256SMichael Lotz free(ioapic); 45763475256SMichael Lotz break; 45863475256SMichael Lotz } 45963475256SMichael Lotz 46063475256SMichael Lotz print_ioapic(*ioapic); 4618908aef9SMichael Lotz 4628908aef9SMichael Lotz if (lastIOAPIC == NULL) 4638908aef9SMichael Lotz sIOAPICs = ioapic; 4648908aef9SMichael Lotz else 465d11e32a8SMichael Lotz lastIOAPIC->next = ioapic; 4668908aef9SMichael Lotz 467d11e32a8SMichael Lotz lastIOAPIC = ioapic; 468d11e32a8SMichael Lotz break; 469d11e32a8SMichael Lotz } 470aa373c43SMichael Lotz 471aa373c43SMichael Lotz case ACPI_MADT_TYPE_NMI_SOURCE: 472aa373c43SMichael Lotz { 473aa373c43SMichael Lotz acpi_madt_nmi_source* info 474aa373c43SMichael Lotz = (acpi_madt_nmi_source*)apicEntry; 475fa6327c9SAlex Smith dprintf("found nmi source global irq %" B_PRIu32 ", flags " 476fa6327c9SAlex Smith "0x%04x\n", (uint32)info->GlobalIrq, 477fa6327c9SAlex Smith (uint16)info->IntiFlags); 478aa373c43SMichael Lotz 479aa373c43SMichael Lotz struct ioapic* ioapic = find_ioapic(info->GlobalIrq); 480aa373c43SMichael Lotz if (ioapic == NULL) { 481aa373c43SMichael Lotz dprintf("nmi source for gsi that is not mapped to any " 482aa373c43SMichael Lotz " io-apic\n"); 483aa373c43SMichael Lotz break; 484aa373c43SMichael Lotz } 485aa373c43SMichael Lotz 486aa373c43SMichael Lotz uint8 pin = info->GlobalIrq - ioapic->global_interrupt_base; 487aa373c43SMichael Lotz ioapic->nmi_mask |= (uint64)1 << pin; 488aa373c43SMichael Lotz break; 489aa373c43SMichael Lotz } 490fb5a1727SMichael Lotz } 491fb5a1727SMichael Lotz 492fb5a1727SMichael Lotz apicEntry 493fb5a1727SMichael Lotz = (acpi_subtable_header*)((uint8*)apicEntry + apicEntry->Length); 494fb5a1727SMichael Lotz } 495fb5a1727SMichael Lotz 496fb5a1727SMichael Lotz return B_OK; 497fb5a1727SMichael Lotz } 498fb5a1727SMichael Lotz 499fb5a1727SMichael Lotz 5009ae3fdcbSMichael Lotz static inline uint32 5019ae3fdcbSMichael Lotz acpi_madt_convert_inti_flags(uint16 flags) 5029ae3fdcbSMichael Lotz { 5039ae3fdcbSMichael Lotz uint32 config = 0; 5049ae3fdcbSMichael Lotz switch (flags & ACPI_MADT_POLARITY_MASK) { 5059ae3fdcbSMichael Lotz case ACPI_MADT_POLARITY_ACTIVE_LOW: 5069ae3fdcbSMichael Lotz config = B_LOW_ACTIVE_POLARITY; 5079ae3fdcbSMichael Lotz break; 5089ae3fdcbSMichael Lotz default: 5099ae3fdcbSMichael Lotz dprintf("invalid polarity in inti flags\n"); 5109ae3fdcbSMichael Lotz // fall through and assume active high 5119ae3fdcbSMichael Lotz case ACPI_MADT_POLARITY_ACTIVE_HIGH: 5129ae3fdcbSMichael Lotz case ACPI_MADT_POLARITY_CONFORMS: 5139ae3fdcbSMichael Lotz config = B_HIGH_ACTIVE_POLARITY; 5149ae3fdcbSMichael Lotz break; 5159ae3fdcbSMichael Lotz } 5169ae3fdcbSMichael Lotz 5179ae3fdcbSMichael Lotz switch (flags & ACPI_MADT_TRIGGER_MASK) { 5189ae3fdcbSMichael Lotz case ACPI_MADT_TRIGGER_LEVEL: 5199ae3fdcbSMichael Lotz config |= B_LEVEL_TRIGGERED; 5209ae3fdcbSMichael Lotz break; 5219ae3fdcbSMichael Lotz default: 522bc409b49SMichael Lotz dprintf("invalid trigger mode in inti flags\n"); 5239ae3fdcbSMichael Lotz // fall through and assume edge triggered 5249ae3fdcbSMichael Lotz case ACPI_MADT_TRIGGER_CONFORMS: 5259ae3fdcbSMichael Lotz case ACPI_MADT_TRIGGER_EDGE: 5269ae3fdcbSMichael Lotz config |= B_EDGE_TRIGGERED; 5279ae3fdcbSMichael Lotz break; 5289ae3fdcbSMichael Lotz } 5299ae3fdcbSMichael Lotz 5309ae3fdcbSMichael Lotz return config; 5319ae3fdcbSMichael Lotz } 5329ae3fdcbSMichael Lotz 5339ae3fdcbSMichael Lotz 534fb5a1727SMichael Lotz static void 535fb5a1727SMichael Lotz acpi_configure_source_overrides(acpi_table_madt* madt) 536fb5a1727SMichael Lotz { 537fb5a1727SMichael Lotz acpi_subtable_header* apicEntry 538fb5a1727SMichael Lotz = (acpi_subtable_header*)((uint8*)madt + sizeof(acpi_table_madt)); 539fb5a1727SMichael Lotz void* end = ((uint8*)madt + madt->Header.Length); 540fb5a1727SMichael Lotz while (apicEntry < end) { 541fb5a1727SMichael Lotz switch (apicEntry->Type) { 542fb5a1727SMichael Lotz case ACPI_MADT_TYPE_INTERRUPT_OVERRIDE: 543fb5a1727SMichael Lotz { 544fb5a1727SMichael Lotz acpi_madt_interrupt_override* info 545fb5a1727SMichael Lotz = (acpi_madt_interrupt_override*)apicEntry; 546fb5a1727SMichael Lotz dprintf("found interrupt override for bus %u, source irq %u, " 547fa6327c9SAlex Smith "global irq %" B_PRIu32 ", flags 0x%08" B_PRIx32 "\n", 548fa6327c9SAlex Smith info->Bus, info->SourceIrq, (uint32)info->GlobalIrq, 549fb5a1727SMichael Lotz (uint32)info->IntiFlags); 550fb5a1727SMichael Lotz 551fb5a1727SMichael Lotz if (info->SourceIrq >= ISA_INTERRUPT_COUNT) { 552fb5a1727SMichael Lotz dprintf("source override exceeds isa interrupt count\n"); 553fb5a1727SMichael Lotz break; 554fb5a1727SMichael Lotz } 555fb5a1727SMichael Lotz 556fb5a1727SMichael Lotz if (info->SourceIrq != info->GlobalIrq) { 557fb5a1727SMichael Lotz // we need a vector mapping 558fb5a1727SMichael Lotz install_io_interrupt_handler(info->GlobalIrq, 559c59b279bSJérôme Duval &ioapic_source_override_handler, 560c59b279bSJérôme Duval (void*)(addr_t)info->SourceIrq, B_NO_ENABLE_COUNTER); 561fb5a1727SMichael Lotz 562fb5a1727SMichael Lotz sSourceOverrides[info->SourceIrq] = info->GlobalIrq; 563fb5a1727SMichael Lotz } 564fb5a1727SMichael Lotz 565fb5a1727SMichael Lotz // configure non-standard polarity/trigger modes 5669ae3fdcbSMichael Lotz uint32 config = acpi_madt_convert_inti_flags(info->IntiFlags); 567fb5a1727SMichael Lotz ioapic_configure_io_interrupt(info->GlobalIrq, config); 568fb5a1727SMichael Lotz break; 569fb5a1727SMichael Lotz } 5700414a203SMichael Lotz 57186d59c04SMichael Lotz case ACPI_MADT_TYPE_NMI_SOURCE: 57286d59c04SMichael Lotz { 57386d59c04SMichael Lotz acpi_madt_nmi_source* info 57486d59c04SMichael Lotz = (acpi_madt_nmi_source*)apicEntry; 575fa6327c9SAlex Smith dprintf("found nmi source global irq %" B_PRIu32 ", flags " 576fa6327c9SAlex Smith "0x%04x\n", (uint32)info->GlobalIrq, 577fa6327c9SAlex Smith (uint16)info->IntiFlags); 57886d59c04SMichael Lotz 57986d59c04SMichael Lotz struct ioapic* ioapic = find_ioapic(info->GlobalIrq); 580aa373c43SMichael Lotz if (ioapic == NULL) 58186d59c04SMichael Lotz break; 58286d59c04SMichael Lotz 58386d59c04SMichael Lotz uint8 pin = info->GlobalIrq - ioapic->global_interrupt_base; 5849ae3fdcbSMichael Lotz uint32 config = acpi_madt_convert_inti_flags(info->IntiFlags); 5859ae3fdcbSMichael Lotz ioapic_configure_pin(*ioapic, pin, info->GlobalIrq, config, 5869ae3fdcbSMichael Lotz IO_APIC_DELIVERY_MODE_NMI); 58786d59c04SMichael Lotz break; 58886d59c04SMichael Lotz } 58986d59c04SMichael Lotz 5900414a203SMichael Lotz #ifdef TRACE_IOAPIC 5910414a203SMichael Lotz case ACPI_MADT_TYPE_LOCAL_APIC: 5920414a203SMichael Lotz { 5930414a203SMichael Lotz // purely informational 5940414a203SMichael Lotz acpi_madt_local_apic* info = (acpi_madt_local_apic*)apicEntry; 5950414a203SMichael Lotz dprintf("found local apic with id %u, processor id %u, " 5960414a203SMichael Lotz "flags 0x%08lx\n", info->Id, info->ProcessorId, 5970414a203SMichael Lotz (uint32)info->LapicFlags); 5980414a203SMichael Lotz break; 5990414a203SMichael Lotz } 6000414a203SMichael Lotz 6010414a203SMichael Lotz case ACPI_MADT_TYPE_LOCAL_APIC_NMI: 6020414a203SMichael Lotz { 6030414a203SMichael Lotz // TODO: take these into account, but at apic.cpp 6040414a203SMichael Lotz acpi_madt_local_apic_nmi* info 6050414a203SMichael Lotz = (acpi_madt_local_apic_nmi*)apicEntry; 6060414a203SMichael Lotz dprintf("found local apic nmi source for processor %u, " 6070414a203SMichael Lotz "flags 0x%04x, local int %u\n", info->ProcessorId, 6080414a203SMichael Lotz (uint16)info->IntiFlags, info->Lint); 6090414a203SMichael Lotz break; 6100414a203SMichael Lotz } 6110414a203SMichael Lotz 6120414a203SMichael Lotz case ACPI_MADT_TYPE_LOCAL_APIC_OVERRIDE: 6130414a203SMichael Lotz { 6140414a203SMichael Lotz // TODO: take these into account, but at apic.cpp 6150414a203SMichael Lotz acpi_madt_local_apic_override* info 6160414a203SMichael Lotz = (acpi_madt_local_apic_override*)apicEntry; 6170414a203SMichael Lotz dprintf("found local apic override with address 0x%016llx\n", 6180414a203SMichael Lotz (uint64)info->Address); 6190414a203SMichael Lotz break; 6200414a203SMichael Lotz } 6210414a203SMichael Lotz 6220414a203SMichael Lotz default: 6230414a203SMichael Lotz dprintf("found unhandled subtable of type %u length %u\n", 6240414a203SMichael Lotz apicEntry->Type, apicEntry->Length); 6250414a203SMichael Lotz break; 6260414a203SMichael Lotz #endif 627d11e32a8SMichael Lotz } 628d11e32a8SMichael Lotz 629d11e32a8SMichael Lotz apicEntry 630d11e32a8SMichael Lotz = (acpi_subtable_header*)((uint8*)apicEntry + apicEntry->Length); 631d11e32a8SMichael Lotz } 632d11e32a8SMichael Lotz } 633d11e32a8SMichael Lotz 634d11e32a8SMichael Lotz 635d11e32a8SMichael Lotz static status_t 636dc14d97bSMichael Lotz acpi_set_interrupt_model(acpi_module_info* acpiModule, uint32 interruptModel) 637dc14d97bSMichael Lotz { 638dc14d97bSMichael Lotz acpi_object_type model; 639dc14d97bSMichael Lotz model.object_type = ACPI_TYPE_INTEGER; 640dc14d97bSMichael Lotz model.data.integer = interruptModel; 641dc14d97bSMichael Lotz 642dc14d97bSMichael Lotz acpi_objects parameter; 643dc14d97bSMichael Lotz parameter.count = 1; 644dc14d97bSMichael Lotz parameter.pointer = &model; 645dc14d97bSMichael Lotz 646dc14d97bSMichael Lotz dprintf("setting ACPI interrupt model to %s\n", 647dc14d97bSMichael Lotz interruptModel == 0 ? "PIC" 648dc14d97bSMichael Lotz : (interruptModel == 1 ? "APIC" 649dc14d97bSMichael Lotz : (interruptModel == 2 ? "SAPIC" 650dc14d97bSMichael Lotz : "unknown"))); 651dc14d97bSMichael Lotz 652dc14d97bSMichael Lotz return acpiModule->evaluate_method(NULL, "\\_PIC", ¶meter, NULL); 653dc14d97bSMichael Lotz } 654dc14d97bSMichael Lotz 655dc14d97bSMichael Lotz 656a56cbb2aSMichael Lotz bool 657a56cbb2aSMichael Lotz ioapic_is_interrupt_available(int32 gsi) 658a56cbb2aSMichael Lotz { 65986d59c04SMichael Lotz struct ioapic* ioapic = find_ioapic(gsi); 66086d59c04SMichael Lotz if (ioapic == NULL) 66186d59c04SMichael Lotz return false; 66286d59c04SMichael Lotz 66386d59c04SMichael Lotz uint8 pin = gsi - ioapic->global_interrupt_base; 66486d59c04SMichael Lotz return (ioapic->nmi_mask & ((uint64)1 << pin)) == 0; 665a56cbb2aSMichael Lotz } 666a56cbb2aSMichael Lotz 667a56cbb2aSMichael Lotz 668dc14d97bSMichael Lotz void 669dc14d97bSMichael Lotz ioapic_init(kernel_args* args) 670dc14d97bSMichael Lotz { 671dc14d97bSMichael Lotz static const interrupt_controller ioapicController = { 672dc14d97bSMichael Lotz "82093AA IOAPIC", 673dc14d97bSMichael Lotz &ioapic_enable_io_interrupt, 674dc14d97bSMichael Lotz &ioapic_disable_io_interrupt, 675dc14d97bSMichael Lotz &ioapic_configure_io_interrupt, 676dc14d97bSMichael Lotz &ioapic_is_spurious_interrupt, 677dc14d97bSMichael Lotz &ioapic_is_level_triggered_interrupt, 678*d897a478SPawel Dziepak &ioapic_end_of_interrupt, 679*d897a478SPawel Dziepak &ioapic_assign_interrupt_to_cpu, 680dc14d97bSMichael Lotz }; 681dc14d97bSMichael Lotz 68262d36f98SAlex Smith if (args->arch_args.apic == NULL) 683dc14d97bSMichael Lotz return; 684dc14d97bSMichael Lotz 6858908aef9SMichael Lotz if (args->arch_args.ioapic_phys == 0) { 6868908aef9SMichael Lotz dprintf("no io-apics available, not using io-apics for interrupt " 6878908aef9SMichael Lotz "routing\n"); 6888908aef9SMichael Lotz return; 6898908aef9SMichael Lotz } 6908908aef9SMichael Lotz 691dc14d97bSMichael Lotz if (get_safemode_boolean(B_SAFEMODE_DISABLE_IOAPIC, false)) { 6925d01e61aSMichael Lotz dprintf("io-apics explicitly disabled, not using io-apics for " 6935d01e61aSMichael Lotz "interrupt routing\n"); 694dc14d97bSMichael Lotz return; 695dc14d97bSMichael Lotz } 696dc14d97bSMichael Lotz 697dc14d97bSMichael Lotz // load acpi module 698dc14d97bSMichael Lotz status_t status; 699dc14d97bSMichael Lotz acpi_module_info* acpiModule; 700dc14d97bSMichael Lotz status = get_module(B_ACPI_MODULE_NAME, (module_info**)&acpiModule); 701dc14d97bSMichael Lotz if (status != B_OK) { 7025d01e61aSMichael Lotz dprintf("acpi module not available, not configuring io-apics\n"); 703dc14d97bSMichael Lotz return; 704dc14d97bSMichael Lotz } 705dc14d97bSMichael Lotz BPrivate::CObjectDeleter<const char, status_t> 706dc14d97bSMichael Lotz acpiModulePutter(B_ACPI_MODULE_NAME, put_module); 707dc14d97bSMichael Lotz 708fb5a1727SMichael Lotz acpi_table_madt* madt = NULL; 709fb5a1727SMichael Lotz if (acpiModule->get_table(ACPI_SIG_MADT, 0, (void**)&madt) != B_OK) { 710fb5a1727SMichael Lotz dprintf("failed to get MADT from ACPI, not configuring io-apics\n"); 711fb5a1727SMichael Lotz return; 712fb5a1727SMichael Lotz } 713fb5a1727SMichael Lotz 714fb5a1727SMichael Lotz status = acpi_enumerate_ioapics(madt); 715d11e32a8SMichael Lotz if (status != B_OK) { 716ca67ddb3SMichael Lotz // We don't treat this case as fatal just yet. If we are able to 717ca67ddb3SMichael Lotz // route everything with the available IO-APICs we're fine, if not 718ca67ddb3SMichael Lotz // we will fail at the routing preparation stage further down. 719ca67ddb3SMichael Lotz dprintf("failed to enumerate all io-apics, working with what we got\n"); 720d11e32a8SMichael Lotz } 721d11e32a8SMichael Lotz 722d11e32a8SMichael Lotz // switch to the APIC interrupt model before retrieving the IRQ routing 723dc14d97bSMichael Lotz // table as it will return different settings depending on the model 724dc14d97bSMichael Lotz status = acpi_set_interrupt_model(acpiModule, ACPI_INTERRUPT_MODEL_APIC); 725dc14d97bSMichael Lotz if (status != B_OK) { 726dc14d97bSMichael Lotz dprintf("failed to put ACPI into APIC interrupt model, ignoring\n"); 727dc14d97bSMichael Lotz // don't abort, as the _PIC method is optional and as long as there 728dc14d97bSMichael Lotz // aren't different routings based on it this is non-fatal 729dc14d97bSMichael Lotz } 730dc14d97bSMichael Lotz 731dc14d97bSMichael Lotz IRQRoutingTable table; 732dc14d97bSMichael Lotz status = prepare_irq_routing(acpiModule, table, 733eda74390SMichael Lotz &ioapic_is_interrupt_available); 734dc14d97bSMichael Lotz if (status != B_OK) { 7355d01e61aSMichael Lotz dprintf("IRQ routing preparation failed, not configuring io-apics\n"); 736dc14d97bSMichael Lotz acpi_set_interrupt_model(acpiModule, ACPI_INTERRUPT_MODEL_PIC); 737dc14d97bSMichael Lotz // revert to PIC interrupt model just in case 738dc14d97bSMichael Lotz return; 739dc14d97bSMichael Lotz } 740dc14d97bSMichael Lotz 7415d01e61aSMichael Lotz // use the boot CPU as the target for all interrupts 74263475256SMichael Lotz uint8 targetAPIC = args->arch_args.cpu_apic_id[0]; 7435d01e61aSMichael Lotz 7448908aef9SMichael Lotz struct ioapic* current = sIOAPICs; 7455d01e61aSMichael Lotz while (current != NULL) { 7465d01e61aSMichael Lotz status = ioapic_initialize_ioapic(*current, targetAPIC); 7475d01e61aSMichael Lotz if (status != B_OK) { 7485d01e61aSMichael Lotz panic("failed to initialize io-apic %u", current->number); 7495d01e61aSMichael Lotz acpi_set_interrupt_model(acpiModule, ACPI_INTERRUPT_MODEL_PIC); 7505d01e61aSMichael Lotz return; 7515d01e61aSMichael Lotz } 7525d01e61aSMichael Lotz 7535d01e61aSMichael Lotz current = current->next; 7545d01e61aSMichael Lotz } 7555d01e61aSMichael Lotz 756cb4e75d3SMichael Lotz #ifdef TRACE_IOAPIC 757cb4e75d3SMichael Lotz dprintf("trying interrupt routing:\n"); 758cb4e75d3SMichael Lotz print_irq_routing_table(table); 759cb4e75d3SMichael Lotz #endif 760cb4e75d3SMichael Lotz 761dc14d97bSMichael Lotz status = enable_irq_routing(acpiModule, table); 762dc14d97bSMichael Lotz if (status != B_OK) { 763dc14d97bSMichael Lotz panic("failed to enable IRQ routing"); 764dc14d97bSMichael Lotz // if it failed early on it might still work in PIC mode 765dc14d97bSMichael Lotz acpi_set_interrupt_model(acpiModule, ACPI_INTERRUPT_MODEL_PIC); 766dc14d97bSMichael Lotz return; 767dc14d97bSMichael Lotz } 768dc14d97bSMichael Lotz 769cb4e75d3SMichael Lotz print_irq_routing_table(table); 770cb4e75d3SMichael Lotz 771fb5a1727SMichael Lotz // configure the source overrides, but let the PCI config below override it 772fb5a1727SMichael Lotz acpi_configure_source_overrides(madt); 773fb5a1727SMichael Lotz 7745d01e61aSMichael Lotz // configure IO-APIC interrupts from PCI routing table 775dc14d97bSMichael Lotz for (int i = 0; i < table.Count(); i++) { 776dc14d97bSMichael Lotz irq_routing_entry& entry = table.ElementAt(i); 777dc14d97bSMichael Lotz ioapic_configure_io_interrupt(entry.irq, 778dc14d97bSMichael Lotz entry.polarity | entry.trigger_mode); 779dc14d97bSMichael Lotz } 780dc14d97bSMichael Lotz 781a56cbb2aSMichael Lotz // kill the local ints on the local APIC 782a56cbb2aSMichael Lotz apic_disable_local_ints(); 783a56cbb2aSMichael Lotz // TODO: This uses the assumption that our init is running on the 784a56cbb2aSMichael Lotz // boot CPU and only the boot CPU has the local ints configured 785a56cbb2aSMichael Lotz // because it was running in legacy PIC mode. Possibly the other 786a56cbb2aSMichael Lotz // local APICs of the other CPUs have them configured as well. It 787a56cbb2aSMichael Lotz // shouldn't really harm, but should eventually be corrected. 788a56cbb2aSMichael Lotz 789dc14d97bSMichael Lotz // disable the legacy PIC 790fb5a1727SMichael Lotz uint16 legacyInterrupts; 791fb5a1727SMichael Lotz pic_disable(legacyInterrupts); 792fb5a1727SMichael Lotz 793fb5a1727SMichael Lotz // enable previsouly enabled legacy interrupts 794fb5a1727SMichael Lotz for (uint8 i = 0; i < 16; i++) { 795fb5a1727SMichael Lotz if ((legacyInterrupts & (1 << i)) != 0) 796fb5a1727SMichael Lotz ioapic_enable_io_interrupt(i); 797fb5a1727SMichael Lotz } 798dc14d97bSMichael Lotz 799fc2d7cb0SMichael Lotz // mark the interrupt vectors reserved so they aren't used for other stuff 800fc2d7cb0SMichael Lotz current = sIOAPICs; 801fc2d7cb0SMichael Lotz while (current != NULL) { 802fc2d7cb0SMichael Lotz reserve_io_interrupt_vectors(current->max_redirection_entry + 1, 8036a164daaSPawel Dziepak current->global_interrupt_base, INTERRUPT_TYPE_IRQ); 804fc2d7cb0SMichael Lotz current = current->next; 805fc2d7cb0SMichael Lotz } 806fc2d7cb0SMichael Lotz 807dc14d97bSMichael Lotz // prefer the ioapic over the normal pic 8085d01e61aSMichael Lotz dprintf("using io-apics for interrupt routing\n"); 809dc14d97bSMichael Lotz arch_int_set_interrupt_controller(ioapicController); 810dc14d97bSMichael Lotz } 811