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> 21dc14d97bSMichael Lotz #include <arch/x86/pic.h> 22dc14d97bSMichael Lotz 23d11e32a8SMichael Lotz // to gain access to the ACPICA types 24d11e32a8SMichael Lotz #include "acpi.h" 25d11e32a8SMichael Lotz 26dc14d97bSMichael Lotz 27dc14d97bSMichael Lotz //#define TRACE_IOAPIC 28dc14d97bSMichael Lotz #ifdef TRACE_IOAPIC 29dc14d97bSMichael Lotz # define TRACE(x) dprintf x 30dc14d97bSMichael Lotz #else 31dc14d97bSMichael Lotz # define TRACE(x) ; 32dc14d97bSMichael Lotz #endif 33dc14d97bSMichael Lotz 34dc14d97bSMichael Lotz 35dc14d97bSMichael Lotz // ACPI interrupt models 36dc14d97bSMichael Lotz #define ACPI_INTERRUPT_MODEL_PIC 0 37dc14d97bSMichael Lotz #define ACPI_INTERRUPT_MODEL_APIC 1 38dc14d97bSMichael Lotz #define ACPI_INTERRUPT_MODEL_SAPIC 2 39dc14d97bSMichael Lotz 40dc14d97bSMichael Lotz 41dc14d97bSMichael Lotz // Definitions for a 82093AA IO APIC controller 42dc14d97bSMichael Lotz #define IO_APIC_IDENTIFICATION 0x00 43dc14d97bSMichael Lotz #define IO_APIC_VERSION 0x01 44dc14d97bSMichael Lotz #define IO_APIC_ARBITRATION 0x02 45dc14d97bSMichael Lotz #define IO_APIC_REDIRECTION_TABLE 0x10 // entry = base + 2 * index 46dc14d97bSMichael Lotz 47dc14d97bSMichael Lotz // Fields for the version register 48dc14d97bSMichael Lotz #define IO_APIC_VERSION_SHIFT 0 49dc14d97bSMichael Lotz #define IO_APIC_VERSION_MASK 0xff 50dc14d97bSMichael Lotz #define IO_APIC_MAX_REDIRECTION_ENTRY_SHIFT 16 51dc14d97bSMichael Lotz #define IO_APIC_MAX_REDIRECTION_ENTRY_MASK 0xff 52dc14d97bSMichael Lotz 53dc14d97bSMichael Lotz // Fields of each redirection table entry 54dc14d97bSMichael Lotz #define IO_APIC_DESTINATION_FIELD_SHIFT 56 55dc14d97bSMichael Lotz #define IO_APIC_DESTINATION_FIELD_MASK 0x0f 56dc14d97bSMichael Lotz #define IO_APIC_INTERRUPT_MASK_SHIFT 16 57dc14d97bSMichael Lotz #define IO_APIC_INTERRUPT_MASKED 1 58dc14d97bSMichael Lotz #define IO_APIC_INTERRUPT_UNMASKED 0 59dc14d97bSMichael Lotz #define IO_APIC_TRIGGER_MODE_SHIFT 15 60dc14d97bSMichael Lotz #define IO_APIC_TRIGGER_MODE_EDGE 0 61dc14d97bSMichael Lotz #define IO_APIC_TRIGGER_MODE_LEVEL 1 62dc14d97bSMichael Lotz #define IO_APIC_REMOTE_IRR_SHIFT 14 63dc14d97bSMichael Lotz #define IO_APIC_PIN_POLARITY_SHIFT 13 64dc14d97bSMichael Lotz #define IO_APIC_PIN_POLARITY_HIGH_ACTIVE 0 65dc14d97bSMichael Lotz #define IO_APIC_PIN_POLARITY_LOW_ACTIVE 1 66dc14d97bSMichael Lotz #define IO_APIC_DELIVERY_STATUS_SHIFT 12 67dc14d97bSMichael Lotz #define IO_APIC_DELIVERY_STATUS_IDLE 0 68dc14d97bSMichael Lotz #define IO_APIC_DELIVERY_STATUS_PENDING 1 69dc14d97bSMichael Lotz #define IO_APIC_DESTINATION_MODE_SHIFT 11 70dc14d97bSMichael Lotz #define IO_APIC_DESTINATION_MODE_PHYSICAL 0 71dc14d97bSMichael Lotz #define IO_APIC_DESTINATION_MODE_LOGICAL 1 72dc14d97bSMichael Lotz #define IO_APIC_DELIVERY_MODE_SHIFT 8 73dc14d97bSMichael Lotz #define IO_APIC_DELIVERY_MODE_MASK 0x07 74dc14d97bSMichael Lotz #define IO_APIC_DELIVERY_MODE_FIXED 0 75dc14d97bSMichael Lotz #define IO_APIC_DELIVERY_MODE_LOWEST_PRIO 1 76dc14d97bSMichael Lotz #define IO_APIC_DELIVERY_MODE_SMI 2 77dc14d97bSMichael Lotz #define IO_APIC_DELIVERY_MODE_NMI 4 78dc14d97bSMichael Lotz #define IO_APIC_DELIVERY_MODE_INIT 5 79dc14d97bSMichael Lotz #define IO_APIC_DELIVERY_MODE_EXT_INT 7 80dc14d97bSMichael Lotz #define IO_APIC_INTERRUPT_VECTOR_SHIFT 0 81dc14d97bSMichael Lotz #define IO_APIC_INTERRUPT_VECTOR_MASK 0xff 82dc14d97bSMichael Lotz 83dc14d97bSMichael Lotz 845d01e61aSMichael Lotz struct ioapic_registers { 85dc14d97bSMichael Lotz volatile uint32 io_register_select; 86dc14d97bSMichael Lotz uint32 reserved[3]; 87dc14d97bSMichael Lotz volatile uint32 io_window_register; 885d01e61aSMichael Lotz }; 89dc14d97bSMichael Lotz 90dc14d97bSMichael Lotz 915d01e61aSMichael Lotz struct ioapic { 925d01e61aSMichael Lotz uint8 number; 935d01e61aSMichael Lotz uint8 max_redirection_entry; 945d01e61aSMichael Lotz uint8 global_interrupt_base; 955d01e61aSMichael Lotz uint8 global_interrupt_last; 965d01e61aSMichael Lotz uint64 level_triggered_mask; 975d01e61aSMichael Lotz 985d01e61aSMichael Lotz area_id register_area; 995d01e61aSMichael Lotz ioapic_registers* registers; 1005d01e61aSMichael Lotz 1015d01e61aSMichael Lotz ioapic* next; 1025d01e61aSMichael Lotz }; 1035d01e61aSMichael Lotz 1045d01e61aSMichael Lotz 1055d01e61aSMichael Lotz static ioapic sIOAPICs = { 0, 0, 0, 0, 0, -1, NULL, NULL }; 106dc14d97bSMichael Lotz 107dc14d97bSMichael Lotz 108dc14d97bSMichael Lotz // #pragma mark - I/O APIC 109dc14d97bSMichael Lotz 110dc14d97bSMichael Lotz 1115d01e61aSMichael Lotz static inline struct ioapic* 1125d01e61aSMichael Lotz find_ioapic(int32 gsi) 113dc14d97bSMichael Lotz { 1145d01e61aSMichael Lotz if (gsi < 0) 1155d01e61aSMichael Lotz return NULL; 1165d01e61aSMichael Lotz 1175d01e61aSMichael Lotz struct ioapic* current = &sIOAPICs; 1185d01e61aSMichael Lotz while (current != NULL) { 1195d01e61aSMichael Lotz if (gsi >= current->global_interrupt_base 1205d01e61aSMichael Lotz && gsi <= current->global_interrupt_last) { 1215d01e61aSMichael Lotz return current; 1225d01e61aSMichael Lotz } 1235d01e61aSMichael Lotz 1245d01e61aSMichael Lotz current = current->next; 1255d01e61aSMichael Lotz } 1265d01e61aSMichael Lotz 1275d01e61aSMichael Lotz return NULL; 1285d01e61aSMichael Lotz } 1295d01e61aSMichael Lotz 1305d01e61aSMichael Lotz 1315d01e61aSMichael Lotz static inline uint32 1325d01e61aSMichael Lotz ioapic_read_32(struct ioapic& ioapic, uint8 registerSelect) 1335d01e61aSMichael Lotz { 1345d01e61aSMichael Lotz ioapic.registers->io_register_select = registerSelect; 1355d01e61aSMichael Lotz return ioapic.registers->io_window_register; 136dc14d97bSMichael Lotz } 137dc14d97bSMichael Lotz 138dc14d97bSMichael Lotz 139dc14d97bSMichael Lotz static inline void 1405d01e61aSMichael Lotz ioapic_write_32(struct ioapic& ioapic, uint8 registerSelect, uint32 value) 141dc14d97bSMichael Lotz { 1425d01e61aSMichael Lotz ioapic.registers->io_register_select = registerSelect; 1435d01e61aSMichael Lotz ioapic.registers->io_window_register = value; 144dc14d97bSMichael Lotz } 145dc14d97bSMichael Lotz 146dc14d97bSMichael Lotz 147dc14d97bSMichael Lotz static inline uint64 1485d01e61aSMichael Lotz ioapic_read_64(struct ioapic& ioapic, uint8 registerSelect) 149dc14d97bSMichael Lotz { 1505d01e61aSMichael Lotz ioapic.registers->io_register_select = registerSelect + 1; 1515d01e61aSMichael Lotz uint64 result = ioapic.registers->io_window_register; 152dc14d97bSMichael Lotz result <<= 32; 1535d01e61aSMichael Lotz ioapic.registers->io_register_select = registerSelect; 1545d01e61aSMichael Lotz result |= ioapic.registers->io_window_register; 155dc14d97bSMichael Lotz return result; 156dc14d97bSMichael Lotz } 157dc14d97bSMichael Lotz 158dc14d97bSMichael Lotz 159dc14d97bSMichael Lotz static inline void 1605d01e61aSMichael Lotz ioapic_write_64(struct ioapic& ioapic, uint8 registerSelect, uint64 value) 161dc14d97bSMichael Lotz { 1625d01e61aSMichael Lotz ioapic.registers->io_register_select = registerSelect; 1635d01e61aSMichael Lotz ioapic.registers->io_window_register = (uint32)value; 1645d01e61aSMichael Lotz ioapic.registers->io_register_select = registerSelect + 1; 1655d01e61aSMichael Lotz ioapic.registers->io_window_register = (uint32)(value >> 32); 166dc14d97bSMichael Lotz } 167dc14d97bSMichael Lotz 168dc14d97bSMichael Lotz 169dc14d97bSMichael Lotz static bool 170*eda74390SMichael Lotz ioapic_is_interrupt_available(int32 gsi) 171*eda74390SMichael Lotz { 172*eda74390SMichael Lotz return find_ioapic(gsi) != NULL; 173*eda74390SMichael Lotz } 174*eda74390SMichael Lotz 175*eda74390SMichael Lotz 176*eda74390SMichael Lotz static bool 177*eda74390SMichael Lotz ioapic_is_spurious_interrupt(int32 gsi) 178dc14d97bSMichael Lotz { 179dc14d97bSMichael Lotz // the spurious interrupt vector is initialized to the max value in smp 180*eda74390SMichael Lotz return gsi == 0xff - ARCH_INTERRUPT_BASE; 181dc14d97bSMichael Lotz } 182dc14d97bSMichael Lotz 183dc14d97bSMichael Lotz 184dc14d97bSMichael Lotz static bool 1855d01e61aSMichael Lotz ioapic_is_level_triggered_interrupt(int32 gsi) 186dc14d97bSMichael Lotz { 1875d01e61aSMichael Lotz struct ioapic* ioapic = find_ioapic(gsi); 1885d01e61aSMichael Lotz if (ioapic == NULL) 189dc14d97bSMichael Lotz return false; 190dc14d97bSMichael Lotz 1915d01e61aSMichael Lotz uint8 pin = gsi - ioapic->global_interrupt_base; 1925d01e61aSMichael Lotz return (ioapic->level_triggered_mask & (1 << pin)) != 0; 193dc14d97bSMichael Lotz } 194dc14d97bSMichael Lotz 195dc14d97bSMichael Lotz 196dc14d97bSMichael Lotz static bool 197dc14d97bSMichael Lotz ioapic_end_of_interrupt(int32 num) 198dc14d97bSMichael Lotz { 199dc14d97bSMichael Lotz apic_end_of_interrupt(); 200dc14d97bSMichael Lotz return true; 201dc14d97bSMichael Lotz } 202dc14d97bSMichael Lotz 203dc14d97bSMichael Lotz 204dc14d97bSMichael Lotz static void 2055d01e61aSMichael Lotz ioapic_enable_io_interrupt(int32 gsi) 206dc14d97bSMichael Lotz { 2075d01e61aSMichael Lotz struct ioapic* ioapic = find_ioapic(gsi); 2085d01e61aSMichael Lotz if (ioapic == NULL) 209dc14d97bSMichael Lotz return; 210dc14d97bSMichael Lotz 2115d01e61aSMichael Lotz uint8 pin = gsi - ioapic->global_interrupt_base; 2125d01e61aSMichael Lotz TRACE(("ioapic_enable_io_interrupt: gsi %ld -> io-apic %u pin %u\n", 2135d01e61aSMichael Lotz gsi, ioapic->number, pin)); 214dc14d97bSMichael Lotz 2155d01e61aSMichael Lotz uint64 entry = ioapic_read_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2); 216dc14d97bSMichael Lotz entry &= ~(1 << IO_APIC_INTERRUPT_MASK_SHIFT); 217dc14d97bSMichael Lotz entry |= IO_APIC_INTERRUPT_UNMASKED << IO_APIC_INTERRUPT_MASK_SHIFT; 2185d01e61aSMichael Lotz ioapic_write_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2, entry); 2195d01e61aSMichael Lotz // TODO: Take writing order into account! We must not unmask the entry 2205d01e61aSMichael Lotz // before the other half is valid. 221dc14d97bSMichael Lotz } 222dc14d97bSMichael Lotz 223dc14d97bSMichael Lotz 224dc14d97bSMichael Lotz static void 2255d01e61aSMichael Lotz ioapic_disable_io_interrupt(int32 gsi) 226dc14d97bSMichael Lotz { 2275d01e61aSMichael Lotz struct ioapic* ioapic = find_ioapic(gsi); 2285d01e61aSMichael Lotz if (ioapic == NULL) 229dc14d97bSMichael Lotz return; 230dc14d97bSMichael Lotz 2315d01e61aSMichael Lotz uint8 pin = gsi - ioapic->global_interrupt_base; 2325d01e61aSMichael Lotz TRACE(("ioapic_disable_io_interrupt: gsi %ld -> io-apic %u pin %u\n", 2335d01e61aSMichael Lotz gsi, ioapic->number, pin)); 234dc14d97bSMichael Lotz 2355d01e61aSMichael Lotz uint64 entry = ioapic_read_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2); 236dc14d97bSMichael Lotz entry &= ~(1 << IO_APIC_INTERRUPT_MASK_SHIFT); 237dc14d97bSMichael Lotz entry |= IO_APIC_INTERRUPT_MASKED << IO_APIC_INTERRUPT_MASK_SHIFT; 2385d01e61aSMichael Lotz ioapic_write_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2, entry); 2395d01e61aSMichael Lotz // TODO: Take writing order into account! We must not modify the entry 2405d01e61aSMichael Lotz // before it is masked. 241dc14d97bSMichael Lotz } 242dc14d97bSMichael Lotz 243dc14d97bSMichael Lotz 244dc14d97bSMichael Lotz static void 2455d01e61aSMichael Lotz ioapic_configure_io_interrupt(int32 gsi, uint32 config) 246dc14d97bSMichael Lotz { 2475d01e61aSMichael Lotz struct ioapic* ioapic = find_ioapic(gsi); 2485d01e61aSMichael Lotz if (ioapic == NULL) 249dc14d97bSMichael Lotz return; 250dc14d97bSMichael Lotz 2515d01e61aSMichael Lotz uint8 pin = gsi - ioapic->global_interrupt_base; 2525d01e61aSMichael Lotz TRACE(("ioapic_configure_io_interrupt: gsi %ld -> io-apic %u pin %u; " 2535d01e61aSMichael Lotz "config 0x%08lx\n", gsi, ioapic->number, pin, config)); 254dc14d97bSMichael Lotz 2555d01e61aSMichael Lotz uint64 entry = ioapic_read_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2); 256dc14d97bSMichael Lotz entry &= ~((1 << IO_APIC_TRIGGER_MODE_SHIFT) 257dc14d97bSMichael Lotz | (1 << IO_APIC_PIN_POLARITY_SHIFT) 258dc14d97bSMichael Lotz | (IO_APIC_INTERRUPT_VECTOR_MASK << IO_APIC_INTERRUPT_VECTOR_SHIFT)); 259dc14d97bSMichael Lotz 260dc14d97bSMichael Lotz if (config & B_LEVEL_TRIGGERED) { 261dc14d97bSMichael Lotz entry |= (IO_APIC_TRIGGER_MODE_LEVEL << IO_APIC_TRIGGER_MODE_SHIFT); 2625d01e61aSMichael Lotz ioapic->level_triggered_mask |= (1 << pin); 263dc14d97bSMichael Lotz } else { 264dc14d97bSMichael Lotz entry |= (IO_APIC_TRIGGER_MODE_EDGE << IO_APIC_TRIGGER_MODE_SHIFT); 2655d01e61aSMichael Lotz ioapic->level_triggered_mask &= ~(1 << pin); 266dc14d97bSMichael Lotz } 267dc14d97bSMichael Lotz 268dc14d97bSMichael Lotz if (config & B_LOW_ACTIVE_POLARITY) 269dc14d97bSMichael Lotz entry |= (IO_APIC_PIN_POLARITY_LOW_ACTIVE << IO_APIC_PIN_POLARITY_SHIFT); 270dc14d97bSMichael Lotz else 271dc14d97bSMichael Lotz entry |= (IO_APIC_PIN_POLARITY_HIGH_ACTIVE << IO_APIC_PIN_POLARITY_SHIFT); 272dc14d97bSMichael Lotz 273dc14d97bSMichael Lotz entry |= (pin + ARCH_INTERRUPT_BASE) << IO_APIC_INTERRUPT_VECTOR_SHIFT; 2745d01e61aSMichael Lotz ioapic_write_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2, entry); 2755d01e61aSMichael Lotz } 2765d01e61aSMichael Lotz 2775d01e61aSMichael Lotz 2785d01e61aSMichael Lotz static status_t 2795d01e61aSMichael Lotz ioapic_map_ioapic(struct ioapic& ioapic, phys_addr_t physicalAddress) 2805d01e61aSMichael Lotz { 2815d01e61aSMichael Lotz ioapic.register_area = vm_map_physical_memory(B_SYSTEM_TEAM, "io-apic", 2825d01e61aSMichael Lotz (void**)&ioapic.registers, ioapic.registers != NULL ? B_EXACT_ADDRESS 2835d01e61aSMichael Lotz : B_ANY_KERNEL_ADDRESS, B_PAGE_SIZE, B_KERNEL_READ_AREA 284d11e32a8SMichael Lotz | B_KERNEL_WRITE_AREA, physicalAddress, ioapic.registers != NULL); 2855d01e61aSMichael Lotz if (ioapic.register_area < 0) { 2865d01e61aSMichael Lotz panic("mapping io-apic %u failed", ioapic.number); 2875d01e61aSMichael Lotz return ioapic.register_area; 2885d01e61aSMichael Lotz } 2895d01e61aSMichael Lotz 290d11e32a8SMichael Lotz dprintf("mapped io-apic %u to %p\n", ioapic.number, ioapic.registers); 291d11e32a8SMichael Lotz 2925d01e61aSMichael Lotz uint32 version = ioapic_read_32(ioapic, IO_APIC_VERSION); 2935d01e61aSMichael Lotz if (version == 0xffffffff) { 2945d01e61aSMichael Lotz dprintf("io-apic %u seems inaccessible, not using it\n", 2955d01e61aSMichael Lotz ioapic.number); 2965d01e61aSMichael Lotz vm_delete_area(B_SYSTEM_TEAM, ioapic.register_area, true); 2975d01e61aSMichael Lotz ioapic.register_area = -1; 2985d01e61aSMichael Lotz ioapic.registers = NULL; 2995d01e61aSMichael Lotz return B_ERROR; 3005d01e61aSMichael Lotz } 3015d01e61aSMichael Lotz 3025d01e61aSMichael Lotz ioapic.max_redirection_entry 3035d01e61aSMichael Lotz = ((version >> IO_APIC_MAX_REDIRECTION_ENTRY_SHIFT) 3045d01e61aSMichael Lotz & IO_APIC_MAX_REDIRECTION_ENTRY_MASK); 3055d01e61aSMichael Lotz 3065d01e61aSMichael Lotz ioapic.global_interrupt_last 3075d01e61aSMichael Lotz = ioapic.global_interrupt_base + ioapic.max_redirection_entry; 3085d01e61aSMichael Lotz 3095d01e61aSMichael Lotz dprintf("io-apic %u has range %u-%u, %u entries\n", ioapic.number, 3105d01e61aSMichael Lotz ioapic.global_interrupt_base, ioapic.global_interrupt_last, 3115d01e61aSMichael Lotz ioapic.max_redirection_entry + 1); 3125d01e61aSMichael Lotz 3135d01e61aSMichael Lotz return B_OK; 3145d01e61aSMichael Lotz } 3155d01e61aSMichael Lotz 3165d01e61aSMichael Lotz 3175d01e61aSMichael Lotz static status_t 3185d01e61aSMichael Lotz ioapic_initialize_ioapic(struct ioapic& ioapic, uint64 targetAPIC) 3195d01e61aSMichael Lotz { 3205d01e61aSMichael Lotz // program the interrupt vectors of the io-apic 3215d01e61aSMichael Lotz ioapic.level_triggered_mask = 0; 3225d01e61aSMichael Lotz for (uint8 i = 0; i <= ioapic.max_redirection_entry; i++) { 3235d01e61aSMichael Lotz // initialize everything to deliver to the boot CPU in physical mode 3245d01e61aSMichael Lotz // and masked until explicitly enabled through enable_io_interrupt() 3255d01e61aSMichael Lotz uint64 entry = (targetAPIC << IO_APIC_DESTINATION_FIELD_SHIFT) 3265d01e61aSMichael Lotz | (IO_APIC_INTERRUPT_MASKED << IO_APIC_INTERRUPT_MASK_SHIFT) 3275d01e61aSMichael Lotz | (IO_APIC_DESTINATION_MODE_PHYSICAL << IO_APIC_DESTINATION_MODE_SHIFT) 3285d01e61aSMichael Lotz | ((i + ARCH_INTERRUPT_BASE) << IO_APIC_INTERRUPT_VECTOR_SHIFT); 3295d01e61aSMichael Lotz 3305d01e61aSMichael Lotz if (i == 0) { 3315d01e61aSMichael Lotz // make redirection entry 0 into an external interrupt 3325d01e61aSMichael Lotz entry |= (IO_APIC_TRIGGER_MODE_EDGE << IO_APIC_TRIGGER_MODE_SHIFT) 3335d01e61aSMichael Lotz | (IO_APIC_PIN_POLARITY_HIGH_ACTIVE << IO_APIC_PIN_POLARITY_SHIFT) 3345d01e61aSMichael Lotz | (IO_APIC_DELIVERY_MODE_EXT_INT << IO_APIC_DELIVERY_MODE_SHIFT); 3355d01e61aSMichael Lotz } else if (i < 16) { 3365d01e61aSMichael Lotz // make 1-15 ISA interrupts 3375d01e61aSMichael Lotz entry |= (IO_APIC_TRIGGER_MODE_EDGE << IO_APIC_TRIGGER_MODE_SHIFT) 3385d01e61aSMichael Lotz | (IO_APIC_PIN_POLARITY_HIGH_ACTIVE << IO_APIC_PIN_POLARITY_SHIFT) 3395d01e61aSMichael Lotz | (IO_APIC_DELIVERY_MODE_FIXED << IO_APIC_DELIVERY_MODE_SHIFT); 3405d01e61aSMichael Lotz } else { 3415d01e61aSMichael Lotz // and the rest are PCI interrupts 3425d01e61aSMichael Lotz entry |= (IO_APIC_TRIGGER_MODE_LEVEL << IO_APIC_TRIGGER_MODE_SHIFT) 3435d01e61aSMichael Lotz | (IO_APIC_PIN_POLARITY_LOW_ACTIVE << IO_APIC_PIN_POLARITY_SHIFT) 3445d01e61aSMichael Lotz | (IO_APIC_DELIVERY_MODE_FIXED << IO_APIC_DELIVERY_MODE_SHIFT); 3455d01e61aSMichael Lotz ioapic.level_triggered_mask |= (1 << i); 3465d01e61aSMichael Lotz } 3475d01e61aSMichael Lotz 3485d01e61aSMichael Lotz ioapic_write_64(ioapic, IO_APIC_REDIRECTION_TABLE + 2 * i, entry); 3495d01e61aSMichael Lotz } 3505d01e61aSMichael Lotz 3515d01e61aSMichael Lotz return B_OK; 352dc14d97bSMichael Lotz } 353dc14d97bSMichael Lotz 354dc14d97bSMichael Lotz 355dc14d97bSMichael Lotz static status_t 356d11e32a8SMichael Lotz acpi_enumerate_ioapics(acpi_module_info* acpi) 357d11e32a8SMichael Lotz { 358d11e32a8SMichael Lotz acpi_table_madt* madt = NULL; 359d11e32a8SMichael Lotz if (acpi->get_table(ACPI_SIG_MADT, 0, (void**)&madt) != B_OK) { 360d11e32a8SMichael Lotz dprintf("failed to get MADT from ACPI, not configuring io-apics\n"); 361d11e32a8SMichael Lotz return B_ERROR; 362d11e32a8SMichael Lotz } 363d11e32a8SMichael Lotz 364d11e32a8SMichael Lotz struct ioapic* lastIOAPIC = &sIOAPICs; 365d11e32a8SMichael Lotz 366d11e32a8SMichael Lotz acpi_subtable_header* apicEntry 367d11e32a8SMichael Lotz = (acpi_subtable_header*)((uint8*)madt + sizeof(acpi_table_madt)); 368d11e32a8SMichael Lotz void* end = ((uint8*)madt + madt->Header.Length); 369d11e32a8SMichael Lotz while (apicEntry < end) { 370d11e32a8SMichael Lotz switch (apicEntry->Type) { 371d11e32a8SMichael Lotz case ACPI_MADT_TYPE_IO_APIC: 372d11e32a8SMichael Lotz { 373d11e32a8SMichael Lotz acpi_madt_io_apic* info = (acpi_madt_io_apic*)apicEntry; 374d11e32a8SMichael Lotz dprintf("found io-apic with address %lu and global interrupt " 375d11e32a8SMichael Lotz "base %lu\n", (uint32)info->Address, 376d11e32a8SMichael Lotz (uint32)info->GlobalIrqBase); 377d11e32a8SMichael Lotz 378d11e32a8SMichael Lotz if (find_ioapic((int32)info->GlobalIrqBase) != NULL) { 379d11e32a8SMichael Lotz // we've already mapped this IO-APIC (at boot) 380d11e32a8SMichael Lotz break; 381d11e32a8SMichael Lotz } 382d11e32a8SMichael Lotz 383d11e32a8SMichael Lotz struct ioapic* ioapic 384d11e32a8SMichael Lotz = (struct ioapic*)malloc(sizeof(struct ioapic)); 385d11e32a8SMichael Lotz if (ioapic == NULL) { 386d11e32a8SMichael Lotz dprintf("ran out of memory while allocating io-apic " 387d11e32a8SMichael Lotz "structure\n"); 388d11e32a8SMichael Lotz return B_NO_MEMORY; 389d11e32a8SMichael Lotz } 390d11e32a8SMichael Lotz 391d11e32a8SMichael Lotz ioapic->number = lastIOAPIC->number + 1; 392d11e32a8SMichael Lotz ioapic->global_interrupt_base = info->GlobalIrqBase; 393d11e32a8SMichael Lotz ioapic->registers = NULL; 394d11e32a8SMichael Lotz ioapic->next = NULL; 395d11e32a8SMichael Lotz 396d11e32a8SMichael Lotz dprintf("mapping io-apic %u at physical address %p\n", 397d11e32a8SMichael Lotz ioapic->number, (void*)info->Address); 398d11e32a8SMichael Lotz status_t status = ioapic_map_ioapic(*ioapic, info->Address); 399d11e32a8SMichael Lotz if (status != B_OK) { 400d11e32a8SMichael Lotz free(ioapic); 401d11e32a8SMichael Lotz return status; 402d11e32a8SMichael Lotz } 403d11e32a8SMichael Lotz 404d11e32a8SMichael Lotz lastIOAPIC->next = ioapic; 405d11e32a8SMichael Lotz lastIOAPIC = ioapic; 406d11e32a8SMichael Lotz break; 407d11e32a8SMichael Lotz } 408d11e32a8SMichael Lotz } 409d11e32a8SMichael Lotz 410d11e32a8SMichael Lotz apicEntry 411d11e32a8SMichael Lotz = (acpi_subtable_header*)((uint8*)apicEntry + apicEntry->Length); 412d11e32a8SMichael Lotz } 413d11e32a8SMichael Lotz 414d11e32a8SMichael Lotz return B_OK; 415d11e32a8SMichael Lotz } 416d11e32a8SMichael Lotz 417d11e32a8SMichael Lotz 418d11e32a8SMichael Lotz static status_t 419dc14d97bSMichael Lotz acpi_set_interrupt_model(acpi_module_info* acpiModule, uint32 interruptModel) 420dc14d97bSMichael Lotz { 421dc14d97bSMichael Lotz acpi_object_type model; 422dc14d97bSMichael Lotz model.object_type = ACPI_TYPE_INTEGER; 423dc14d97bSMichael Lotz model.data.integer = interruptModel; 424dc14d97bSMichael Lotz 425dc14d97bSMichael Lotz acpi_objects parameter; 426dc14d97bSMichael Lotz parameter.count = 1; 427dc14d97bSMichael Lotz parameter.pointer = &model; 428dc14d97bSMichael Lotz 429dc14d97bSMichael Lotz dprintf("setting ACPI interrupt model to %s\n", 430dc14d97bSMichael Lotz interruptModel == 0 ? "PIC" 431dc14d97bSMichael Lotz : (interruptModel == 1 ? "APIC" 432dc14d97bSMichael Lotz : (interruptModel == 2 ? "SAPIC" 433dc14d97bSMichael Lotz : "unknown"))); 434dc14d97bSMichael Lotz 435dc14d97bSMichael Lotz return acpiModule->evaluate_method(NULL, "\\_PIC", ¶meter, NULL); 436dc14d97bSMichael Lotz } 437dc14d97bSMichael Lotz 438dc14d97bSMichael Lotz 439dc14d97bSMichael Lotz void 440dc14d97bSMichael Lotz ioapic_map(kernel_args* args) 441dc14d97bSMichael Lotz { 442dc14d97bSMichael Lotz if (args->arch_args.apic == NULL) { 443dc14d97bSMichael Lotz dprintf("no local apic available\n"); 444dc14d97bSMichael Lotz return; 445dc14d97bSMichael Lotz } 446dc14d97bSMichael Lotz 447dc14d97bSMichael Lotz if (args->arch_args.ioapic == NULL) { 4485d01e61aSMichael Lotz dprintf("no io-apic available, not using io-apics for interrupt " 4495d01e61aSMichael Lotz "routing\n"); 450dc14d97bSMichael Lotz return; 451dc14d97bSMichael Lotz } 452dc14d97bSMichael Lotz 4535d01e61aSMichael Lotz // map in the first IO-APIC 4545d01e61aSMichael Lotz sIOAPICs.registers = (ioapic_registers*)args->arch_args.ioapic; 4555d01e61aSMichael Lotz ioapic_map_ioapic(sIOAPICs, args->arch_args.ioapic_phys); 456dc14d97bSMichael Lotz } 457dc14d97bSMichael Lotz 458dc14d97bSMichael Lotz 459dc14d97bSMichael Lotz void 460dc14d97bSMichael Lotz ioapic_init(kernel_args* args) 461dc14d97bSMichael Lotz { 462dc14d97bSMichael Lotz static const interrupt_controller ioapicController = { 463dc14d97bSMichael Lotz "82093AA IOAPIC", 464dc14d97bSMichael Lotz &ioapic_enable_io_interrupt, 465dc14d97bSMichael Lotz &ioapic_disable_io_interrupt, 466dc14d97bSMichael Lotz &ioapic_configure_io_interrupt, 467dc14d97bSMichael Lotz &ioapic_is_spurious_interrupt, 468dc14d97bSMichael Lotz &ioapic_is_level_triggered_interrupt, 469dc14d97bSMichael Lotz &ioapic_end_of_interrupt 470dc14d97bSMichael Lotz }; 471dc14d97bSMichael Lotz 4725d01e61aSMichael Lotz if (sIOAPICs.register_area < 0 || sIOAPICs.registers == NULL) 473dc14d97bSMichael Lotz return; 474dc14d97bSMichael Lotz 475dc14d97bSMichael Lotz #if 0 476dc14d97bSMichael Lotz if (get_safemode_boolean(B_SAFEMODE_DISABLE_IOAPIC, false)) { 4775d01e61aSMichael Lotz dprintf("io-apics explicitly disabled, not using io-apics for " 4785d01e61aSMichael Lotz "interrupt routing\n"); 479dc14d97bSMichael Lotz return; 480dc14d97bSMichael Lotz } 481dc14d97bSMichael Lotz #else 482dc14d97bSMichael Lotz // TODO: This can be removed once IO-APIC code is broadly tested 483dc14d97bSMichael Lotz if (!get_safemode_boolean(B_SAFEMODE_ENABLE_IOAPIC, false)) { 4845d01e61aSMichael Lotz dprintf("io-apics not enabled, not using io-apics for interrupt " 485dc14d97bSMichael Lotz "routing\n"); 486dc14d97bSMichael Lotz return; 487dc14d97bSMichael Lotz } 488dc14d97bSMichael Lotz #endif 489dc14d97bSMichael Lotz 490dc14d97bSMichael Lotz // load acpi module 491dc14d97bSMichael Lotz status_t status; 492dc14d97bSMichael Lotz acpi_module_info* acpiModule; 493dc14d97bSMichael Lotz status = get_module(B_ACPI_MODULE_NAME, (module_info**)&acpiModule); 494dc14d97bSMichael Lotz if (status != B_OK) { 4955d01e61aSMichael Lotz dprintf("acpi module not available, not configuring io-apics\n"); 496dc14d97bSMichael Lotz return; 497dc14d97bSMichael Lotz } 498dc14d97bSMichael Lotz BPrivate::CObjectDeleter<const char, status_t> 499dc14d97bSMichael Lotz acpiModulePutter(B_ACPI_MODULE_NAME, put_module); 500dc14d97bSMichael Lotz 501d11e32a8SMichael Lotz status = acpi_enumerate_ioapics(acpiModule); 502d11e32a8SMichael Lotz if (status != B_OK) { 503d11e32a8SMichael Lotz dprintf("failed to enumerate all io-apics, not using io-apics for " 504d11e32a8SMichael Lotz "interrupt routing\n"); 505d11e32a8SMichael Lotz return; 506d11e32a8SMichael Lotz } 507d11e32a8SMichael Lotz 508d11e32a8SMichael Lotz // switch to the APIC interrupt model before retrieving the IRQ routing 509dc14d97bSMichael Lotz // table as it will return different settings depending on the model 510dc14d97bSMichael Lotz status = acpi_set_interrupt_model(acpiModule, ACPI_INTERRUPT_MODEL_APIC); 511dc14d97bSMichael Lotz if (status != B_OK) { 512dc14d97bSMichael Lotz dprintf("failed to put ACPI into APIC interrupt model, ignoring\n"); 513dc14d97bSMichael Lotz // don't abort, as the _PIC method is optional and as long as there 514dc14d97bSMichael Lotz // aren't different routings based on it this is non-fatal 515dc14d97bSMichael Lotz } 516dc14d97bSMichael Lotz 517dc14d97bSMichael Lotz IRQRoutingTable table; 518dc14d97bSMichael Lotz status = prepare_irq_routing(acpiModule, table, 519*eda74390SMichael Lotz &ioapic_is_interrupt_available); 520dc14d97bSMichael Lotz if (status != B_OK) { 5215d01e61aSMichael Lotz dprintf("IRQ routing preparation failed, not configuring io-apics\n"); 522dc14d97bSMichael Lotz acpi_set_interrupt_model(acpiModule, ACPI_INTERRUPT_MODEL_PIC); 523dc14d97bSMichael Lotz // revert to PIC interrupt model just in case 524dc14d97bSMichael Lotz return; 525dc14d97bSMichael Lotz } 526dc14d97bSMichael Lotz 527dc14d97bSMichael Lotz print_irq_routing_table(table); 528dc14d97bSMichael Lotz 5295d01e61aSMichael Lotz // use the boot CPU as the target for all interrupts 5305d01e61aSMichael Lotz uint64 targetAPIC = args->arch_args.cpu_apic_id[0]; 5315d01e61aSMichael Lotz 5325d01e61aSMichael Lotz struct ioapic* current = &sIOAPICs; 5335d01e61aSMichael Lotz while (current != NULL) { 5345d01e61aSMichael Lotz status = ioapic_initialize_ioapic(*current, targetAPIC); 5355d01e61aSMichael Lotz if (status != B_OK) { 5365d01e61aSMichael Lotz panic("failed to initialize io-apic %u", current->number); 5375d01e61aSMichael Lotz acpi_set_interrupt_model(acpiModule, ACPI_INTERRUPT_MODEL_PIC); 5385d01e61aSMichael Lotz return; 5395d01e61aSMichael Lotz } 5405d01e61aSMichael Lotz 5415d01e61aSMichael Lotz current = current->next; 5425d01e61aSMichael Lotz } 5435d01e61aSMichael Lotz 544dc14d97bSMichael Lotz status = enable_irq_routing(acpiModule, table); 545dc14d97bSMichael Lotz if (status != B_OK) { 546dc14d97bSMichael Lotz panic("failed to enable IRQ routing"); 547dc14d97bSMichael Lotz // if it failed early on it might still work in PIC mode 548dc14d97bSMichael Lotz acpi_set_interrupt_model(acpiModule, ACPI_INTERRUPT_MODEL_PIC); 549dc14d97bSMichael Lotz return; 550dc14d97bSMichael Lotz } 551dc14d97bSMichael Lotz 5525d01e61aSMichael Lotz // configure IO-APIC interrupts from PCI routing table 553dc14d97bSMichael Lotz for (int i = 0; i < table.Count(); i++) { 554dc14d97bSMichael Lotz irq_routing_entry& entry = table.ElementAt(i); 555dc14d97bSMichael Lotz ioapic_configure_io_interrupt(entry.irq, 556dc14d97bSMichael Lotz entry.polarity | entry.trigger_mode); 557dc14d97bSMichael Lotz } 558dc14d97bSMichael Lotz 559dc14d97bSMichael Lotz // disable the legacy PIC 560dc14d97bSMichael Lotz pic_disable(); 561dc14d97bSMichael Lotz 562dc14d97bSMichael Lotz // prefer the ioapic over the normal pic 5635d01e61aSMichael Lotz dprintf("using io-apics for interrupt routing\n"); 564dc14d97bSMichael Lotz arch_int_set_interrupt_controller(ioapicController); 565dc14d97bSMichael Lotz } 566