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 420f91697fSMichael Lotz #define IO_APIC_ID 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 470f91697fSMichael Lotz // Fields for the id register 480f91697fSMichael Lotz #define IO_APIC_ID_SHIFT 24 4963475256SMichael Lotz #define IO_APIC_ID_MASK 0xff 500f91697fSMichael Lotz 51dc14d97bSMichael Lotz // Fields for the version register 52dc14d97bSMichael Lotz #define IO_APIC_VERSION_SHIFT 0 53dc14d97bSMichael Lotz #define IO_APIC_VERSION_MASK 0xff 54dc14d97bSMichael Lotz #define IO_APIC_MAX_REDIRECTION_ENTRY_SHIFT 16 55dc14d97bSMichael Lotz #define IO_APIC_MAX_REDIRECTION_ENTRY_MASK 0xff 56dc14d97bSMichael Lotz 57dc14d97bSMichael Lotz // Fields of each redirection table entry 58dc14d97bSMichael Lotz #define IO_APIC_DESTINATION_FIELD_SHIFT 56 59dc14d97bSMichael Lotz #define IO_APIC_DESTINATION_FIELD_MASK 0x0f 60dc14d97bSMichael Lotz #define IO_APIC_INTERRUPT_MASK_SHIFT 16 61dc14d97bSMichael Lotz #define IO_APIC_INTERRUPT_MASKED 1 62dc14d97bSMichael Lotz #define IO_APIC_INTERRUPT_UNMASKED 0 63dc14d97bSMichael Lotz #define IO_APIC_TRIGGER_MODE_SHIFT 15 64dc14d97bSMichael Lotz #define IO_APIC_TRIGGER_MODE_EDGE 0 65dc14d97bSMichael Lotz #define IO_APIC_TRIGGER_MODE_LEVEL 1 66dc14d97bSMichael Lotz #define IO_APIC_REMOTE_IRR_SHIFT 14 67dc14d97bSMichael Lotz #define IO_APIC_PIN_POLARITY_SHIFT 13 68dc14d97bSMichael Lotz #define IO_APIC_PIN_POLARITY_HIGH_ACTIVE 0 69dc14d97bSMichael Lotz #define IO_APIC_PIN_POLARITY_LOW_ACTIVE 1 70dc14d97bSMichael Lotz #define IO_APIC_DELIVERY_STATUS_SHIFT 12 71dc14d97bSMichael Lotz #define IO_APIC_DELIVERY_STATUS_IDLE 0 72dc14d97bSMichael Lotz #define IO_APIC_DELIVERY_STATUS_PENDING 1 73dc14d97bSMichael Lotz #define IO_APIC_DESTINATION_MODE_SHIFT 11 74dc14d97bSMichael Lotz #define IO_APIC_DESTINATION_MODE_PHYSICAL 0 75dc14d97bSMichael Lotz #define IO_APIC_DESTINATION_MODE_LOGICAL 1 76dc14d97bSMichael Lotz #define IO_APIC_DELIVERY_MODE_SHIFT 8 77dc14d97bSMichael Lotz #define IO_APIC_DELIVERY_MODE_MASK 0x07 78dc14d97bSMichael Lotz #define IO_APIC_DELIVERY_MODE_FIXED 0 79dc14d97bSMichael Lotz #define IO_APIC_DELIVERY_MODE_LOWEST_PRIO 1 80dc14d97bSMichael Lotz #define IO_APIC_DELIVERY_MODE_SMI 2 81dc14d97bSMichael Lotz #define IO_APIC_DELIVERY_MODE_NMI 4 82dc14d97bSMichael Lotz #define IO_APIC_DELIVERY_MODE_INIT 5 83dc14d97bSMichael Lotz #define IO_APIC_DELIVERY_MODE_EXT_INT 7 84dc14d97bSMichael Lotz #define IO_APIC_INTERRUPT_VECTOR_SHIFT 0 85dc14d97bSMichael Lotz #define IO_APIC_INTERRUPT_VECTOR_MASK 0xff 86dc14d97bSMichael Lotz 87dc14d97bSMichael Lotz 885d01e61aSMichael Lotz struct ioapic_registers { 89dc14d97bSMichael Lotz volatile uint32 io_register_select; 90dc14d97bSMichael Lotz uint32 reserved[3]; 91dc14d97bSMichael Lotz volatile uint32 io_window_register; 925d01e61aSMichael Lotz }; 93dc14d97bSMichael Lotz 94dc14d97bSMichael Lotz 955d01e61aSMichael Lotz struct ioapic { 965d01e61aSMichael Lotz uint8 number; 9763475256SMichael Lotz uint8 apic_id; 9863475256SMichael Lotz uint32 version; 995d01e61aSMichael Lotz uint8 max_redirection_entry; 1005d01e61aSMichael Lotz uint8 global_interrupt_base; 1015d01e61aSMichael Lotz uint8 global_interrupt_last; 1025d01e61aSMichael Lotz uint64 level_triggered_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 11163475256SMichael Lotz static ioapic sIOAPICs = { 0, 0, 0, 0, 0, 0, 0, -1, NULL, NULL }; 112dc14d97bSMichael Lotz 113dc14d97bSMichael Lotz 114dc14d97bSMichael Lotz // #pragma mark - I/O APIC 115dc14d97bSMichael Lotz 116dc14d97bSMichael Lotz 11763475256SMichael Lotz static void 11863475256SMichael Lotz print_ioapic(struct ioapic& ioapic) 11963475256SMichael Lotz { 12063475256SMichael Lotz dprintf("io-apic %u has range %u-%u, %u entries, version 0x%08lx, " 12163475256SMichael Lotz "apic-id %u\n", ioapic.number, ioapic.global_interrupt_base, 12263475256SMichael Lotz ioapic.global_interrupt_last, ioapic.max_redirection_entry + 1, 12363475256SMichael Lotz ioapic.version, ioapic.apic_id); 12463475256SMichael Lotz } 12563475256SMichael Lotz 12663475256SMichael Lotz 1275d01e61aSMichael Lotz static inline struct ioapic* 1285d01e61aSMichael Lotz find_ioapic(int32 gsi) 129dc14d97bSMichael Lotz { 1305d01e61aSMichael Lotz if (gsi < 0) 1315d01e61aSMichael Lotz return NULL; 1325d01e61aSMichael Lotz 1335d01e61aSMichael Lotz struct ioapic* current = &sIOAPICs; 1345d01e61aSMichael Lotz while (current != NULL) { 1355d01e61aSMichael Lotz if (gsi >= current->global_interrupt_base 1365d01e61aSMichael Lotz && gsi <= current->global_interrupt_last) { 1375d01e61aSMichael Lotz return current; 1385d01e61aSMichael Lotz } 1395d01e61aSMichael Lotz 1405d01e61aSMichael Lotz current = current->next; 1415d01e61aSMichael Lotz } 1425d01e61aSMichael Lotz 1435d01e61aSMichael Lotz return NULL; 1445d01e61aSMichael Lotz } 1455d01e61aSMichael Lotz 1465d01e61aSMichael Lotz 1475d01e61aSMichael Lotz static inline uint32 1485d01e61aSMichael Lotz ioapic_read_32(struct ioapic& ioapic, uint8 registerSelect) 1495d01e61aSMichael Lotz { 1505d01e61aSMichael Lotz ioapic.registers->io_register_select = registerSelect; 1515d01e61aSMichael Lotz return ioapic.registers->io_window_register; 152dc14d97bSMichael Lotz } 153dc14d97bSMichael Lotz 154dc14d97bSMichael Lotz 155dc14d97bSMichael Lotz static inline void 1565d01e61aSMichael Lotz ioapic_write_32(struct ioapic& ioapic, uint8 registerSelect, uint32 value) 157dc14d97bSMichael Lotz { 1585d01e61aSMichael Lotz ioapic.registers->io_register_select = registerSelect; 1595d01e61aSMichael Lotz ioapic.registers->io_window_register = value; 160dc14d97bSMichael Lotz } 161dc14d97bSMichael Lotz 162dc14d97bSMichael Lotz 163dc14d97bSMichael Lotz static inline uint64 1645d01e61aSMichael Lotz ioapic_read_64(struct ioapic& ioapic, uint8 registerSelect) 165dc14d97bSMichael Lotz { 1665d01e61aSMichael Lotz ioapic.registers->io_register_select = registerSelect + 1; 1675d01e61aSMichael Lotz uint64 result = ioapic.registers->io_window_register; 168dc14d97bSMichael Lotz result <<= 32; 1695d01e61aSMichael Lotz ioapic.registers->io_register_select = registerSelect; 1705d01e61aSMichael Lotz result |= ioapic.registers->io_window_register; 171dc14d97bSMichael Lotz return result; 172dc14d97bSMichael Lotz } 173dc14d97bSMichael Lotz 174dc14d97bSMichael Lotz 175dc14d97bSMichael Lotz static inline void 1765d01e61aSMichael Lotz ioapic_write_64(struct ioapic& ioapic, uint8 registerSelect, uint64 value) 177dc14d97bSMichael Lotz { 1785d01e61aSMichael Lotz ioapic.registers->io_register_select = registerSelect; 1795d01e61aSMichael Lotz ioapic.registers->io_window_register = (uint32)value; 1805d01e61aSMichael Lotz ioapic.registers->io_register_select = registerSelect + 1; 1815d01e61aSMichael Lotz ioapic.registers->io_window_register = (uint32)(value >> 32); 182dc14d97bSMichael Lotz } 183dc14d97bSMichael Lotz 184dc14d97bSMichael Lotz 185dc14d97bSMichael Lotz static bool 186eda74390SMichael Lotz ioapic_is_spurious_interrupt(int32 gsi) 187dc14d97bSMichael Lotz { 188dc14d97bSMichael Lotz // the spurious interrupt vector is initialized to the max value in smp 189eda74390SMichael Lotz return gsi == 0xff - ARCH_INTERRUPT_BASE; 190dc14d97bSMichael Lotz } 191dc14d97bSMichael Lotz 192dc14d97bSMichael Lotz 193dc14d97bSMichael Lotz static bool 1945d01e61aSMichael Lotz ioapic_is_level_triggered_interrupt(int32 gsi) 195dc14d97bSMichael Lotz { 1965d01e61aSMichael Lotz struct ioapic* ioapic = find_ioapic(gsi); 1975d01e61aSMichael Lotz if (ioapic == NULL) 198dc14d97bSMichael Lotz return false; 199dc14d97bSMichael Lotz 2005d01e61aSMichael Lotz uint8 pin = gsi - ioapic->global_interrupt_base; 2015d01e61aSMichael Lotz return (ioapic->level_triggered_mask & (1 << pin)) != 0; 202dc14d97bSMichael Lotz } 203dc14d97bSMichael Lotz 204dc14d97bSMichael Lotz 205dc14d97bSMichael Lotz static bool 206dc14d97bSMichael Lotz ioapic_end_of_interrupt(int32 num) 207dc14d97bSMichael Lotz { 208dc14d97bSMichael Lotz apic_end_of_interrupt(); 209dc14d97bSMichael Lotz return true; 210dc14d97bSMichael Lotz } 211dc14d97bSMichael Lotz 212dc14d97bSMichael Lotz 213dc14d97bSMichael Lotz static void 2145d01e61aSMichael Lotz ioapic_enable_io_interrupt(int32 gsi) 215dc14d97bSMichael Lotz { 2165d01e61aSMichael Lotz struct ioapic* ioapic = find_ioapic(gsi); 2175d01e61aSMichael Lotz if (ioapic == NULL) 218dc14d97bSMichael Lotz return; 219dc14d97bSMichael Lotz 2205d01e61aSMichael Lotz uint8 pin = gsi - ioapic->global_interrupt_base; 2215d01e61aSMichael Lotz TRACE(("ioapic_enable_io_interrupt: gsi %ld -> io-apic %u pin %u\n", 2225d01e61aSMichael Lotz gsi, ioapic->number, pin)); 223dc14d97bSMichael Lotz 2245d01e61aSMichael Lotz uint64 entry = ioapic_read_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2); 225dc14d97bSMichael Lotz entry &= ~(1 << IO_APIC_INTERRUPT_MASK_SHIFT); 226dc14d97bSMichael Lotz entry |= IO_APIC_INTERRUPT_UNMASKED << IO_APIC_INTERRUPT_MASK_SHIFT; 2275d01e61aSMichael Lotz ioapic_write_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2, entry); 2285d01e61aSMichael Lotz // TODO: Take writing order into account! We must not unmask the entry 2295d01e61aSMichael Lotz // before the other half is valid. 230dc14d97bSMichael Lotz } 231dc14d97bSMichael Lotz 232dc14d97bSMichael Lotz 233dc14d97bSMichael Lotz static void 2345d01e61aSMichael Lotz ioapic_disable_io_interrupt(int32 gsi) 235dc14d97bSMichael Lotz { 2365d01e61aSMichael Lotz struct ioapic* ioapic = find_ioapic(gsi); 2375d01e61aSMichael Lotz if (ioapic == NULL) 238dc14d97bSMichael Lotz return; 239dc14d97bSMichael Lotz 2405d01e61aSMichael Lotz uint8 pin = gsi - ioapic->global_interrupt_base; 2415d01e61aSMichael Lotz TRACE(("ioapic_disable_io_interrupt: gsi %ld -> io-apic %u pin %u\n", 2425d01e61aSMichael Lotz gsi, ioapic->number, pin)); 243dc14d97bSMichael Lotz 2445d01e61aSMichael Lotz uint64 entry = ioapic_read_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2); 245dc14d97bSMichael Lotz entry &= ~(1 << IO_APIC_INTERRUPT_MASK_SHIFT); 246dc14d97bSMichael Lotz entry |= IO_APIC_INTERRUPT_MASKED << IO_APIC_INTERRUPT_MASK_SHIFT; 2475d01e61aSMichael Lotz ioapic_write_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2, entry); 2485d01e61aSMichael Lotz // TODO: Take writing order into account! We must not modify the entry 2495d01e61aSMichael Lotz // before it is masked. 250dc14d97bSMichael Lotz } 251dc14d97bSMichael Lotz 252dc14d97bSMichael Lotz 253dc14d97bSMichael Lotz static void 2545d01e61aSMichael Lotz ioapic_configure_io_interrupt(int32 gsi, uint32 config) 255dc14d97bSMichael Lotz { 2565d01e61aSMichael Lotz struct ioapic* ioapic = find_ioapic(gsi); 2575d01e61aSMichael Lotz if (ioapic == NULL) 258dc14d97bSMichael Lotz return; 259dc14d97bSMichael Lotz 2605d01e61aSMichael Lotz uint8 pin = gsi - ioapic->global_interrupt_base; 2615d01e61aSMichael Lotz TRACE(("ioapic_configure_io_interrupt: gsi %ld -> io-apic %u pin %u; " 2625d01e61aSMichael Lotz "config 0x%08lx\n", gsi, ioapic->number, pin, config)); 263dc14d97bSMichael Lotz 2645d01e61aSMichael Lotz uint64 entry = ioapic_read_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2); 265dc14d97bSMichael Lotz entry &= ~((1 << IO_APIC_TRIGGER_MODE_SHIFT) 266dc14d97bSMichael Lotz | (1 << IO_APIC_PIN_POLARITY_SHIFT) 267dc14d97bSMichael Lotz | (IO_APIC_INTERRUPT_VECTOR_MASK << IO_APIC_INTERRUPT_VECTOR_SHIFT)); 268dc14d97bSMichael Lotz 269dc14d97bSMichael Lotz if (config & B_LEVEL_TRIGGERED) { 270dc14d97bSMichael Lotz entry |= (IO_APIC_TRIGGER_MODE_LEVEL << IO_APIC_TRIGGER_MODE_SHIFT); 2715d01e61aSMichael Lotz ioapic->level_triggered_mask |= (1 << pin); 272dc14d97bSMichael Lotz } else { 273dc14d97bSMichael Lotz entry |= (IO_APIC_TRIGGER_MODE_EDGE << IO_APIC_TRIGGER_MODE_SHIFT); 2745d01e61aSMichael Lotz ioapic->level_triggered_mask &= ~(1 << pin); 275dc14d97bSMichael Lotz } 276dc14d97bSMichael Lotz 277dc14d97bSMichael Lotz if (config & B_LOW_ACTIVE_POLARITY) 278dc14d97bSMichael Lotz entry |= (IO_APIC_PIN_POLARITY_LOW_ACTIVE << IO_APIC_PIN_POLARITY_SHIFT); 279dc14d97bSMichael Lotz else 280dc14d97bSMichael Lotz entry |= (IO_APIC_PIN_POLARITY_HIGH_ACTIVE << IO_APIC_PIN_POLARITY_SHIFT); 281dc14d97bSMichael Lotz 282dc14d97bSMichael Lotz entry |= (pin + ARCH_INTERRUPT_BASE) << IO_APIC_INTERRUPT_VECTOR_SHIFT; 2835d01e61aSMichael Lotz ioapic_write_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2, entry); 2845d01e61aSMichael Lotz } 2855d01e61aSMichael Lotz 2865d01e61aSMichael Lotz 2875d01e61aSMichael Lotz static status_t 2885d01e61aSMichael Lotz ioapic_map_ioapic(struct ioapic& ioapic, phys_addr_t physicalAddress) 2895d01e61aSMichael Lotz { 2905d01e61aSMichael Lotz ioapic.register_area = vm_map_physical_memory(B_SYSTEM_TEAM, "io-apic", 2915d01e61aSMichael Lotz (void**)&ioapic.registers, ioapic.registers != NULL ? B_EXACT_ADDRESS 2925d01e61aSMichael Lotz : B_ANY_KERNEL_ADDRESS, B_PAGE_SIZE, B_KERNEL_READ_AREA 293d11e32a8SMichael Lotz | B_KERNEL_WRITE_AREA, physicalAddress, ioapic.registers != NULL); 2945d01e61aSMichael Lotz if (ioapic.register_area < 0) { 2955d01e61aSMichael Lotz panic("mapping io-apic %u failed", ioapic.number); 2965d01e61aSMichael Lotz return ioapic.register_area; 2975d01e61aSMichael Lotz } 2985d01e61aSMichael Lotz 299d11e32a8SMichael Lotz dprintf("mapped io-apic %u to %p\n", ioapic.number, ioapic.registers); 300d11e32a8SMichael Lotz 30163475256SMichael Lotz ioapic.version = ioapic_read_32(ioapic, IO_APIC_VERSION); 30263475256SMichael Lotz if (ioapic.version == 0xffffffff) { 3035d01e61aSMichael Lotz dprintf("io-apic %u seems inaccessible, not using it\n", 3045d01e61aSMichael Lotz ioapic.number); 3055d01e61aSMichael Lotz vm_delete_area(B_SYSTEM_TEAM, ioapic.register_area, true); 3065d01e61aSMichael Lotz ioapic.register_area = -1; 3075d01e61aSMichael Lotz ioapic.registers = NULL; 3085d01e61aSMichael Lotz return B_ERROR; 3095d01e61aSMichael Lotz } 3105d01e61aSMichael Lotz 3115d01e61aSMichael Lotz ioapic.max_redirection_entry 31263475256SMichael Lotz = ((ioapic.version >> IO_APIC_MAX_REDIRECTION_ENTRY_SHIFT) 3135d01e61aSMichael Lotz & IO_APIC_MAX_REDIRECTION_ENTRY_MASK); 3145d01e61aSMichael Lotz 3155d01e61aSMichael Lotz ioapic.global_interrupt_last 3165d01e61aSMichael Lotz = ioapic.global_interrupt_base + ioapic.max_redirection_entry; 3175d01e61aSMichael Lotz 3185d01e61aSMichael Lotz return B_OK; 3195d01e61aSMichael Lotz } 3205d01e61aSMichael Lotz 3215d01e61aSMichael Lotz 3225d01e61aSMichael Lotz static status_t 32363475256SMichael Lotz ioapic_initialize_ioapic(struct ioapic& ioapic, uint8 targetAPIC) 3245d01e61aSMichael Lotz { 32563475256SMichael Lotz // program the APIC ID 32663475256SMichael Lotz ioapic_write_32(ioapic, IO_APIC_ID, ioapic.apic_id << IO_APIC_ID_SHIFT); 32763475256SMichael Lotz 3285d01e61aSMichael Lotz // program the interrupt vectors of the io-apic 3295d01e61aSMichael Lotz ioapic.level_triggered_mask = 0; 330e9d55697SMichael Lotz uint8 gsi = ioapic.global_interrupt_base; 331e9d55697SMichael Lotz for (uint8 i = 0; i <= ioapic.max_redirection_entry; i++, gsi++) { 3325d01e61aSMichael Lotz // initialize everything to deliver to the boot CPU in physical mode 3335d01e61aSMichael Lotz // and masked until explicitly enabled through enable_io_interrupt() 33463475256SMichael Lotz uint64 entry = ((uint64)targetAPIC << IO_APIC_DESTINATION_FIELD_SHIFT) 3355d01e61aSMichael Lotz | (IO_APIC_INTERRUPT_MASKED << IO_APIC_INTERRUPT_MASK_SHIFT) 3365d01e61aSMichael Lotz | (IO_APIC_DESTINATION_MODE_PHYSICAL << IO_APIC_DESTINATION_MODE_SHIFT) 3377a8ce431SMichael Lotz | ((gsi + ARCH_INTERRUPT_BASE) << IO_APIC_INTERRUPT_VECTOR_SHIFT); 3385d01e61aSMichael Lotz 339e9d55697SMichael Lotz if (gsi == 0) { 340e9d55697SMichael Lotz // make GSI 0 into an external interrupt 3415d01e61aSMichael Lotz entry |= (IO_APIC_TRIGGER_MODE_EDGE << IO_APIC_TRIGGER_MODE_SHIFT) 3425d01e61aSMichael Lotz | (IO_APIC_PIN_POLARITY_HIGH_ACTIVE << IO_APIC_PIN_POLARITY_SHIFT) 3435d01e61aSMichael Lotz | (IO_APIC_DELIVERY_MODE_EXT_INT << IO_APIC_DELIVERY_MODE_SHIFT); 344e9d55697SMichael Lotz } else if (gsi < 16) { 345e9d55697SMichael Lotz // make GSIs 1-15 ISA interrupts 3465d01e61aSMichael Lotz entry |= (IO_APIC_TRIGGER_MODE_EDGE << IO_APIC_TRIGGER_MODE_SHIFT) 3475d01e61aSMichael Lotz | (IO_APIC_PIN_POLARITY_HIGH_ACTIVE << IO_APIC_PIN_POLARITY_SHIFT) 3485d01e61aSMichael Lotz | (IO_APIC_DELIVERY_MODE_FIXED << IO_APIC_DELIVERY_MODE_SHIFT); 3495d01e61aSMichael Lotz } else { 3505d01e61aSMichael Lotz // and the rest are PCI interrupts 3515d01e61aSMichael Lotz entry |= (IO_APIC_TRIGGER_MODE_LEVEL << IO_APIC_TRIGGER_MODE_SHIFT) 3525d01e61aSMichael Lotz | (IO_APIC_PIN_POLARITY_LOW_ACTIVE << IO_APIC_PIN_POLARITY_SHIFT) 3535d01e61aSMichael Lotz | (IO_APIC_DELIVERY_MODE_FIXED << IO_APIC_DELIVERY_MODE_SHIFT); 3545d01e61aSMichael Lotz ioapic.level_triggered_mask |= (1 << i); 3555d01e61aSMichael Lotz } 3565d01e61aSMichael Lotz 3575d01e61aSMichael Lotz ioapic_write_64(ioapic, IO_APIC_REDIRECTION_TABLE + 2 * i, entry); 3585d01e61aSMichael Lotz } 3595d01e61aSMichael Lotz 3605d01e61aSMichael Lotz return B_OK; 361dc14d97bSMichael Lotz } 362dc14d97bSMichael Lotz 363dc14d97bSMichael Lotz 364dc14d97bSMichael Lotz static status_t 365d11e32a8SMichael Lotz acpi_enumerate_ioapics(acpi_module_info* acpi) 366d11e32a8SMichael Lotz { 367d11e32a8SMichael Lotz acpi_table_madt* madt = NULL; 368d11e32a8SMichael Lotz if (acpi->get_table(ACPI_SIG_MADT, 0, (void**)&madt) != B_OK) { 369d11e32a8SMichael Lotz dprintf("failed to get MADT from ACPI, not configuring io-apics\n"); 370d11e32a8SMichael Lotz return B_ERROR; 371d11e32a8SMichael Lotz } 372d11e32a8SMichael Lotz 373d11e32a8SMichael Lotz struct ioapic* lastIOAPIC = &sIOAPICs; 374d11e32a8SMichael Lotz 375d11e32a8SMichael Lotz acpi_subtable_header* apicEntry 376d11e32a8SMichael Lotz = (acpi_subtable_header*)((uint8*)madt + sizeof(acpi_table_madt)); 377d11e32a8SMichael Lotz void* end = ((uint8*)madt + madt->Header.Length); 378d11e32a8SMichael Lotz while (apicEntry < end) { 379d11e32a8SMichael Lotz switch (apicEntry->Type) { 380d11e32a8SMichael Lotz case ACPI_MADT_TYPE_IO_APIC: 381d11e32a8SMichael Lotz { 382d11e32a8SMichael Lotz acpi_madt_io_apic* info = (acpi_madt_io_apic*)apicEntry; 38363475256SMichael Lotz dprintf("found io-apic with address 0x%08lx, global " 38463475256SMichael Lotz "interrupt base %lu, apic-id %u\n", (uint32)info->Address, 38563475256SMichael Lotz (uint32)info->GlobalIrqBase, info->Id); 386d11e32a8SMichael Lotz 387cdb247a3SMichael Lotz struct ioapic* alreadyMapped 388cdb247a3SMichael Lotz = find_ioapic((int32)info->GlobalIrqBase); 389cdb247a3SMichael Lotz if (alreadyMapped != NULL) { 39063475256SMichael Lotz // We've already mapped this IO-APIC (at boot), but we 39163475256SMichael Lotz // need to fill in the APIC ID. TODO: We might not 39263475256SMichael Lotz // actually get the IO-APIC with base 0 as first entry! 39363475256SMichael Lotz alreadyMapped->apic_id = info->Id; 39463475256SMichael Lotz alreadyMapped->global_interrupt_base = info->GlobalIrqBase; 39563475256SMichael Lotz alreadyMapped->global_interrupt_last = info->GlobalIrqBase 39663475256SMichael Lotz + alreadyMapped->max_redirection_entry; 39763475256SMichael Lotz print_ioapic(*alreadyMapped); 398d11e32a8SMichael Lotz break; 399d11e32a8SMichael Lotz } 400d11e32a8SMichael Lotz 401d11e32a8SMichael Lotz struct ioapic* ioapic 402d11e32a8SMichael Lotz = (struct ioapic*)malloc(sizeof(struct ioapic)); 403d11e32a8SMichael Lotz if (ioapic == NULL) { 404d11e32a8SMichael Lotz dprintf("ran out of memory while allocating io-apic " 405d11e32a8SMichael Lotz "structure\n"); 406d11e32a8SMichael Lotz return B_NO_MEMORY; 407d11e32a8SMichael Lotz } 408d11e32a8SMichael Lotz 409d11e32a8SMichael Lotz ioapic->number = lastIOAPIC->number + 1; 41063475256SMichael Lotz ioapic->apic_id = info->Id; 411d11e32a8SMichael Lotz ioapic->global_interrupt_base = info->GlobalIrqBase; 412d11e32a8SMichael Lotz ioapic->registers = NULL; 413d11e32a8SMichael Lotz ioapic->next = NULL; 414d11e32a8SMichael Lotz 415d11e32a8SMichael Lotz dprintf("mapping io-apic %u at physical address %p\n", 416d11e32a8SMichael Lotz ioapic->number, (void*)info->Address); 417d11e32a8SMichael Lotz status_t status = ioapic_map_ioapic(*ioapic, info->Address); 41863475256SMichael Lotz if (status != B_OK) { 41963475256SMichael Lotz free(ioapic); 42063475256SMichael Lotz break; 42163475256SMichael Lotz } 42263475256SMichael Lotz 42363475256SMichael Lotz print_ioapic(*ioapic); 424d11e32a8SMichael Lotz lastIOAPIC->next = ioapic; 425d11e32a8SMichael Lotz lastIOAPIC = ioapic; 426d11e32a8SMichael Lotz break; 427d11e32a8SMichael Lotz } 428*0414a203SMichael Lotz 429*0414a203SMichael Lotz #ifdef TRACE_IOAPIC 430*0414a203SMichael Lotz case ACPI_MADT_TYPE_LOCAL_APIC: 431*0414a203SMichael Lotz { 432*0414a203SMichael Lotz // purely informational 433*0414a203SMichael Lotz acpi_madt_local_apic* info = (acpi_madt_local_apic*)apicEntry; 434*0414a203SMichael Lotz dprintf("found local apic with id %u, processor id %u, " 435*0414a203SMichael Lotz "flags 0x%08lx\n", info->Id, info->ProcessorId, 436*0414a203SMichael Lotz (uint32)info->LapicFlags); 437*0414a203SMichael Lotz break; 438*0414a203SMichael Lotz } 439*0414a203SMichael Lotz 440*0414a203SMichael Lotz case ACPI_MADT_TYPE_INTERRUPT_OVERRIDE: 441*0414a203SMichael Lotz { 442*0414a203SMichael Lotz // TODO: take these into account 443*0414a203SMichael Lotz acpi_madt_interrupt_override* info 444*0414a203SMichael Lotz = (acpi_madt_interrupt_override*)apicEntry; 445*0414a203SMichael Lotz dprintf("found interrupt override for bus %u, source irq %u, " 446*0414a203SMichael Lotz "global irq %lu, flags 0x%08lx\n", info->Bus, 447*0414a203SMichael Lotz info->SourceIrq, (uint32)info->GlobalIrq, 448*0414a203SMichael Lotz (uint32)info->IntiFlags); 449*0414a203SMichael Lotz break; 450*0414a203SMichael Lotz } 451*0414a203SMichael Lotz 452*0414a203SMichael Lotz case ACPI_MADT_TYPE_NMI_SOURCE: 453*0414a203SMichael Lotz { 454*0414a203SMichael Lotz // TODO: take these into account 455*0414a203SMichael Lotz acpi_madt_nmi_source* info 456*0414a203SMichael Lotz = (acpi_madt_nmi_source*)apicEntry; 457*0414a203SMichael Lotz dprintf("found nmi source global irq %lu, flags 0x%04x\n", 458*0414a203SMichael Lotz (uint32)info->GlobalIrq, (uint16)info->IntiFlags); 459*0414a203SMichael Lotz break; 460*0414a203SMichael Lotz } 461*0414a203SMichael Lotz 462*0414a203SMichael Lotz case ACPI_MADT_TYPE_LOCAL_APIC_NMI: 463*0414a203SMichael Lotz { 464*0414a203SMichael Lotz // TODO: take these into account, but at apic.cpp 465*0414a203SMichael Lotz acpi_madt_local_apic_nmi* info 466*0414a203SMichael Lotz = (acpi_madt_local_apic_nmi*)apicEntry; 467*0414a203SMichael Lotz dprintf("found local apic nmi source for processor %u, " 468*0414a203SMichael Lotz "flags 0x%04x, local int %u\n", info->ProcessorId, 469*0414a203SMichael Lotz (uint16)info->IntiFlags, info->Lint); 470*0414a203SMichael Lotz break; 471*0414a203SMichael Lotz } 472*0414a203SMichael Lotz 473*0414a203SMichael Lotz case ACPI_MADT_TYPE_LOCAL_APIC_OVERRIDE: 474*0414a203SMichael Lotz { 475*0414a203SMichael Lotz // TODO: take these into account, but at apic.cpp 476*0414a203SMichael Lotz acpi_madt_local_apic_override* info 477*0414a203SMichael Lotz = (acpi_madt_local_apic_override*)apicEntry; 478*0414a203SMichael Lotz dprintf("found local apic override with address 0x%016llx\n", 479*0414a203SMichael Lotz (uint64)info->Address); 480*0414a203SMichael Lotz break; 481*0414a203SMichael Lotz } 482*0414a203SMichael Lotz 483*0414a203SMichael Lotz default: 484*0414a203SMichael Lotz dprintf("found unhandled subtable of type %u length %u\n", 485*0414a203SMichael Lotz apicEntry->Type, apicEntry->Length); 486*0414a203SMichael Lotz break; 487*0414a203SMichael Lotz #endif 488d11e32a8SMichael Lotz } 489d11e32a8SMichael Lotz 490d11e32a8SMichael Lotz apicEntry 491d11e32a8SMichael Lotz = (acpi_subtable_header*)((uint8*)apicEntry + apicEntry->Length); 492d11e32a8SMichael Lotz } 493d11e32a8SMichael Lotz 494d11e32a8SMichael Lotz return B_OK; 495d11e32a8SMichael Lotz } 496d11e32a8SMichael Lotz 497d11e32a8SMichael Lotz 498d11e32a8SMichael Lotz static status_t 499dc14d97bSMichael Lotz acpi_set_interrupt_model(acpi_module_info* acpiModule, uint32 interruptModel) 500dc14d97bSMichael Lotz { 501dc14d97bSMichael Lotz acpi_object_type model; 502dc14d97bSMichael Lotz model.object_type = ACPI_TYPE_INTEGER; 503dc14d97bSMichael Lotz model.data.integer = interruptModel; 504dc14d97bSMichael Lotz 505dc14d97bSMichael Lotz acpi_objects parameter; 506dc14d97bSMichael Lotz parameter.count = 1; 507dc14d97bSMichael Lotz parameter.pointer = &model; 508dc14d97bSMichael Lotz 509dc14d97bSMichael Lotz dprintf("setting ACPI interrupt model to %s\n", 510dc14d97bSMichael Lotz interruptModel == 0 ? "PIC" 511dc14d97bSMichael Lotz : (interruptModel == 1 ? "APIC" 512dc14d97bSMichael Lotz : (interruptModel == 2 ? "SAPIC" 513dc14d97bSMichael Lotz : "unknown"))); 514dc14d97bSMichael Lotz 515dc14d97bSMichael Lotz return acpiModule->evaluate_method(NULL, "\\_PIC", ¶meter, NULL); 516dc14d97bSMichael Lotz } 517dc14d97bSMichael Lotz 518dc14d97bSMichael Lotz 519a56cbb2aSMichael Lotz bool 520a56cbb2aSMichael Lotz ioapic_is_interrupt_available(int32 gsi) 521a56cbb2aSMichael Lotz { 522a56cbb2aSMichael Lotz return find_ioapic(gsi) != NULL; 523a56cbb2aSMichael Lotz } 524a56cbb2aSMichael Lotz 525a56cbb2aSMichael Lotz 526dc14d97bSMichael Lotz void 527dc14d97bSMichael Lotz ioapic_map(kernel_args* args) 528dc14d97bSMichael Lotz { 529dc14d97bSMichael Lotz if (args->arch_args.apic == NULL) { 530dc14d97bSMichael Lotz dprintf("no local apic available\n"); 531dc14d97bSMichael Lotz return; 532dc14d97bSMichael Lotz } 533dc14d97bSMichael Lotz 534dc14d97bSMichael Lotz if (args->arch_args.ioapic == NULL) { 5355d01e61aSMichael Lotz dprintf("no io-apic available, not using io-apics for interrupt " 5365d01e61aSMichael Lotz "routing\n"); 537dc14d97bSMichael Lotz return; 538dc14d97bSMichael Lotz } 539dc14d97bSMichael Lotz 5405d01e61aSMichael Lotz // map in the first IO-APIC 5415d01e61aSMichael Lotz sIOAPICs.registers = (ioapic_registers*)args->arch_args.ioapic; 5425d01e61aSMichael Lotz ioapic_map_ioapic(sIOAPICs, args->arch_args.ioapic_phys); 543dc14d97bSMichael Lotz } 544dc14d97bSMichael Lotz 545dc14d97bSMichael Lotz 546dc14d97bSMichael Lotz void 547dc14d97bSMichael Lotz ioapic_init(kernel_args* args) 548dc14d97bSMichael Lotz { 549dc14d97bSMichael Lotz static const interrupt_controller ioapicController = { 550dc14d97bSMichael Lotz "82093AA IOAPIC", 551dc14d97bSMichael Lotz &ioapic_enable_io_interrupt, 552dc14d97bSMichael Lotz &ioapic_disable_io_interrupt, 553dc14d97bSMichael Lotz &ioapic_configure_io_interrupt, 554dc14d97bSMichael Lotz &ioapic_is_spurious_interrupt, 555dc14d97bSMichael Lotz &ioapic_is_level_triggered_interrupt, 556dc14d97bSMichael Lotz &ioapic_end_of_interrupt 557dc14d97bSMichael Lotz }; 558dc14d97bSMichael Lotz 5595d01e61aSMichael Lotz if (sIOAPICs.register_area < 0 || sIOAPICs.registers == NULL) 560dc14d97bSMichael Lotz return; 561dc14d97bSMichael Lotz 562dc14d97bSMichael Lotz #if 0 563dc14d97bSMichael Lotz if (get_safemode_boolean(B_SAFEMODE_DISABLE_IOAPIC, false)) { 5645d01e61aSMichael Lotz dprintf("io-apics explicitly disabled, not using io-apics for " 5655d01e61aSMichael Lotz "interrupt routing\n"); 566dc14d97bSMichael Lotz return; 567dc14d97bSMichael Lotz } 568dc14d97bSMichael Lotz #else 569dc14d97bSMichael Lotz // TODO: This can be removed once IO-APIC code is broadly tested 570dc14d97bSMichael Lotz if (!get_safemode_boolean(B_SAFEMODE_ENABLE_IOAPIC, false)) { 5715d01e61aSMichael Lotz dprintf("io-apics not enabled, not using io-apics for interrupt " 572dc14d97bSMichael Lotz "routing\n"); 573dc14d97bSMichael Lotz return; 574dc14d97bSMichael Lotz } 575dc14d97bSMichael Lotz #endif 576dc14d97bSMichael Lotz 577dc14d97bSMichael Lotz // load acpi module 578dc14d97bSMichael Lotz status_t status; 579dc14d97bSMichael Lotz acpi_module_info* acpiModule; 580dc14d97bSMichael Lotz status = get_module(B_ACPI_MODULE_NAME, (module_info**)&acpiModule); 581dc14d97bSMichael Lotz if (status != B_OK) { 5825d01e61aSMichael Lotz dprintf("acpi module not available, not configuring io-apics\n"); 583dc14d97bSMichael Lotz return; 584dc14d97bSMichael Lotz } 585dc14d97bSMichael Lotz BPrivate::CObjectDeleter<const char, status_t> 586dc14d97bSMichael Lotz acpiModulePutter(B_ACPI_MODULE_NAME, put_module); 587dc14d97bSMichael Lotz 588d11e32a8SMichael Lotz status = acpi_enumerate_ioapics(acpiModule); 589d11e32a8SMichael Lotz if (status != B_OK) { 590ca67ddb3SMichael Lotz // We don't treat this case as fatal just yet. If we are able to 591ca67ddb3SMichael Lotz // route everything with the available IO-APICs we're fine, if not 592ca67ddb3SMichael Lotz // we will fail at the routing preparation stage further down. 593ca67ddb3SMichael Lotz dprintf("failed to enumerate all io-apics, working with what we got\n"); 594d11e32a8SMichael Lotz } 595d11e32a8SMichael Lotz 596d11e32a8SMichael Lotz // switch to the APIC interrupt model before retrieving the IRQ routing 597dc14d97bSMichael Lotz // table as it will return different settings depending on the model 598dc14d97bSMichael Lotz status = acpi_set_interrupt_model(acpiModule, ACPI_INTERRUPT_MODEL_APIC); 599dc14d97bSMichael Lotz if (status != B_OK) { 600dc14d97bSMichael Lotz dprintf("failed to put ACPI into APIC interrupt model, ignoring\n"); 601dc14d97bSMichael Lotz // don't abort, as the _PIC method is optional and as long as there 602dc14d97bSMichael Lotz // aren't different routings based on it this is non-fatal 603dc14d97bSMichael Lotz } 604dc14d97bSMichael Lotz 605dc14d97bSMichael Lotz IRQRoutingTable table; 606dc14d97bSMichael Lotz status = prepare_irq_routing(acpiModule, table, 607eda74390SMichael Lotz &ioapic_is_interrupt_available); 608dc14d97bSMichael Lotz if (status != B_OK) { 6095d01e61aSMichael Lotz dprintf("IRQ routing preparation failed, not configuring io-apics\n"); 610dc14d97bSMichael Lotz acpi_set_interrupt_model(acpiModule, ACPI_INTERRUPT_MODEL_PIC); 611dc14d97bSMichael Lotz // revert to PIC interrupt model just in case 612dc14d97bSMichael Lotz return; 613dc14d97bSMichael Lotz } 614dc14d97bSMichael Lotz 615dc14d97bSMichael Lotz print_irq_routing_table(table); 616dc14d97bSMichael Lotz 6175d01e61aSMichael Lotz // use the boot CPU as the target for all interrupts 61863475256SMichael Lotz uint8 targetAPIC = args->arch_args.cpu_apic_id[0]; 6195d01e61aSMichael Lotz 6205d01e61aSMichael Lotz struct ioapic* current = &sIOAPICs; 6215d01e61aSMichael Lotz while (current != NULL) { 6225d01e61aSMichael Lotz status = ioapic_initialize_ioapic(*current, targetAPIC); 6235d01e61aSMichael Lotz if (status != B_OK) { 6245d01e61aSMichael Lotz panic("failed to initialize io-apic %u", current->number); 6255d01e61aSMichael Lotz acpi_set_interrupt_model(acpiModule, ACPI_INTERRUPT_MODEL_PIC); 6265d01e61aSMichael Lotz return; 6275d01e61aSMichael Lotz } 6285d01e61aSMichael Lotz 6295d01e61aSMichael Lotz current = current->next; 6305d01e61aSMichael Lotz } 6315d01e61aSMichael Lotz 632dc14d97bSMichael Lotz status = enable_irq_routing(acpiModule, table); 633dc14d97bSMichael Lotz if (status != B_OK) { 634dc14d97bSMichael Lotz panic("failed to enable IRQ routing"); 635dc14d97bSMichael Lotz // if it failed early on it might still work in PIC mode 636dc14d97bSMichael Lotz acpi_set_interrupt_model(acpiModule, ACPI_INTERRUPT_MODEL_PIC); 637dc14d97bSMichael Lotz return; 638dc14d97bSMichael Lotz } 639dc14d97bSMichael Lotz 6405d01e61aSMichael Lotz // configure IO-APIC interrupts from PCI routing table 641dc14d97bSMichael Lotz for (int i = 0; i < table.Count(); i++) { 642dc14d97bSMichael Lotz irq_routing_entry& entry = table.ElementAt(i); 643dc14d97bSMichael Lotz ioapic_configure_io_interrupt(entry.irq, 644dc14d97bSMichael Lotz entry.polarity | entry.trigger_mode); 645dc14d97bSMichael Lotz } 646dc14d97bSMichael Lotz 647a56cbb2aSMichael Lotz // kill the local ints on the local APIC 648a56cbb2aSMichael Lotz apic_disable_local_ints(); 649a56cbb2aSMichael Lotz // TODO: This uses the assumption that our init is running on the 650a56cbb2aSMichael Lotz // boot CPU and only the boot CPU has the local ints configured 651a56cbb2aSMichael Lotz // because it was running in legacy PIC mode. Possibly the other 652a56cbb2aSMichael Lotz // local APICs of the other CPUs have them configured as well. It 653a56cbb2aSMichael Lotz // shouldn't really harm, but should eventually be corrected. 654a56cbb2aSMichael Lotz 655dc14d97bSMichael Lotz // disable the legacy PIC 656dc14d97bSMichael Lotz pic_disable(); 657dc14d97bSMichael Lotz 658dc14d97bSMichael Lotz // prefer the ioapic over the normal pic 6595d01e61aSMichael Lotz dprintf("using io-apics for interrupt routing\n"); 660dc14d97bSMichael Lotz arch_int_set_interrupt_controller(ioapicController); 661dc14d97bSMichael Lotz } 662