diff --git a/headers/private/kernel/arch/int.h b/headers/private/kernel/arch/int.h index 0cab718079..ecbe5c8554 100644 --- a/headers/private/kernel/arch/int.h +++ b/headers/private/kernel/arch/int.h @@ -8,17 +8,22 @@ #ifndef KERNEL_ARCH_INT_H #define KERNEL_ARCH_INT_H - -#include #include +// config flags for arch_int_configure_io_interrupt() +#define B_EDGE_TRIGGERED 1 +#define B_LEVEL_TRIGGERED 2 +#define B_LOW_ACTIVE_POLARITY 4 +#define B_HIGH_ACTIVE_POLARITY 8 #ifdef __cplusplus extern "C" { #endif -status_t arch_int_init(kernel_args *args); -status_t arch_int_init_post_vm(kernel_args *args); +struct kernel_args; + +status_t arch_int_init(struct kernel_args *args); +status_t arch_int_init_post_vm(struct kernel_args *args); status_t arch_int_init_post_device_manager(struct kernel_args *args); void arch_int_enable_interrupts(void); @@ -26,6 +31,7 @@ int arch_int_disable_interrupts(void); void arch_int_restore_interrupts(int oldstate); void arch_int_enable_io_interrupt(int irq); void arch_int_disable_io_interrupt(int irq); +void arch_int_configure_io_interrupt(int irq, uint32 config); bool arch_int_are_interrupts_enabled(void); #ifdef __cplusplus diff --git a/headers/private/system/safemode_defs.h b/headers/private/system/safemode_defs.h index 060eaeccf8..1b16a35b35 100644 --- a/headers/private/system/safemode_defs.h +++ b/headers/private/system/safemode_defs.h @@ -11,6 +11,7 @@ #define B_SAFEMODE_DISABLE_USER_ADD_ONS "disableuseraddons" #define B_SAFEMODE_DISABLE_IDE_DMA "disableidedma" +#define B_SAFEMODE_DISABLE_IOAPIC "disable_ioapic" #define B_SAFEMODE_DISABLE_ACPI "disable_acpi" #define B_SAFEMODE_DISABLE_APM "disable_apm" #define B_SAFEMODE_DISABLE_SMP "disable_smp" diff --git a/src/add-ons/kernel/bus_managers/pci/Jamfile b/src/add-ons/kernel/bus_managers/pci/Jamfile index d94b91d876..bc98798e2c 100644 --- a/src/add-ons/kernel/bus_managers/pci/Jamfile +++ b/src/add-ons/kernel/bus_managers/pci/Jamfile @@ -1,6 +1,7 @@ SubDir HAIKU_TOP src add-ons kernel bus_managers pci ; -UsePrivateHeaders kernel shared ; +UsePrivateKernelHeaders ; +UsePrivateHeaders shared ; UsePrivateHeaders [ FDirName kernel util ] ; KernelAddon pci : diff --git a/src/add-ons/kernel/bus_managers/pci/pci.cpp b/src/add-ons/kernel/bus_managers/pci/pci.cpp index d6b7d64db9..3ac346bce3 100644 --- a/src/add-ons/kernel/bus_managers/pci/pci.cpp +++ b/src/add-ons/kernel/bus_managers/pci/pci.cpp @@ -16,6 +16,9 @@ #include "pci_private.h" #include "pci.h" +// private header for configuring io interrupts to level triggered +#include + #define TRACE_CAP(x...) dprintf(x) #define FLOW(x...) //#define FLOW(x...) dprintf(x) @@ -1005,6 +1008,12 @@ PCI::_ReadHeaderInfo(PCIDev *dev) dev->device, dev->function, PCI_min_grant, 1); dev->info.u.h0.max_latency = ReadConfig(dev->domain, dev->bus, dev->device, dev->function, PCI_max_latency, 1); + + if (dev->info.u.h0.interrupt_line != 0 + && dev->info.u.h0.interrupt_line != 0xff) { + arch_int_configure_io_interrupt(dev->info.u.h0.interrupt_line, + B_LEVEL_TRIGGERED | B_LOW_ACTIVE_POLARITY); + } break; } diff --git a/src/system/boot/platform/bios_ia32/smp.cpp b/src/system/boot/platform/bios_ia32/smp.cpp index dbd35b8cc9..2437a67de7 100644 --- a/src/system/boot/platform/bios_ia32/smp.cpp +++ b/src/system/boot/platform/bios_ia32/smp.cpp @@ -421,18 +421,20 @@ smp_init_other_cpus(void) unload_driver_settings(handle); } - if (gKernelArgs.num_cpus < 2) + if (gKernelArgs.arch_args.apic_phys == 0) return; - TRACE(("smp: found %ld cpus\n", gKernelArgs.num_cpus)); + TRACE(("smp: found %ld cpu%s\n", gKernelArgs.num_cpus, gKernelArgs.num_cpus != 1 ? "s" : "")); TRACE(("smp: apic_phys = %p\n", (void *)gKernelArgs.arch_args.apic_phys)); TRACE(("smp: ioapic_phys = %p\n", (void *)gKernelArgs.arch_args.ioapic_phys)); - // map in the apic & ioapic + // map in the apic & ioapic (if available) gKernelArgs.arch_args.apic = (uint32 *)mmu_map_physical_memory( gKernelArgs.arch_args.apic_phys, B_PAGE_SIZE, kDefaultPageFlags); - gKernelArgs.arch_args.ioapic = (uint32 *)mmu_map_physical_memory( - gKernelArgs.arch_args.ioapic_phys, B_PAGE_SIZE, kDefaultPageFlags); + if (gKernelArgs.arch_args.ioapic_phys != 0) { + gKernelArgs.arch_args.ioapic = (uint32 *)mmu_map_physical_memory( + gKernelArgs.arch_args.ioapic_phys, B_PAGE_SIZE, kDefaultPageFlags); + } TRACE(("smp: apic = %p\n", gKernelArgs.arch_args.apic)); TRACE(("smp: ioapic = %p\n", gKernelArgs.arch_args.ioapic)); @@ -440,6 +442,9 @@ smp_init_other_cpus(void) // calculate how fast the apic timer is calculate_apic_timer_conversion_factor(); + if (gKernelArgs.num_cpus < 2) + return; + for (uint32 i = 1; i < gKernelArgs.num_cpus; i++) { // create a final stack the trampoline code will put the ap processor on gKernelArgs.cpu_kstack[i].start = (addr_t)mmu_allocate(NULL, KERNEL_STACK_SIZE); diff --git a/src/system/kernel/arch/x86/arch_int.c b/src/system/kernel/arch/x86/arch_int.c index aebe32bc18..4743351208 100644 --- a/src/system/kernel/arch/x86/arch_int.c +++ b/src/system/kernel/arch/x86/arch_int.c @@ -22,11 +22,13 @@ #include #include +#include #include #include #include "interrupts.h" +#include #include #include @@ -70,10 +72,78 @@ #define PIC_NON_SPECIFIC_EOI 0x20 -#define PIC_INT_BASE ARCH_INTERRUPT_BASE -#define PIC_SLAVE_INT_BASE (ARCH_INTERRUPT_BASE + 8) +#define PIC_SLAVE_INT_BASE 8 #define PIC_NUM_INTS 0x0f + +// Definitions for a 82093AA IO APIC controller +#define IO_APIC_IDENTIFICATION 0x00 +#define IO_APIC_VERSION 0x01 +#define IO_APIC_ARBITRATION 0x02 +#define IO_APIC_REDIRECTION_TABLE 0x10 // entry = base + 2 * index + +// Fields for the version register +#define IO_APIC_VERSION_SHIFT 0 +#define IO_APIC_VERSION_MASK 0xff +#define IO_APIC_MAX_REDIRECTION_ENTRY_SHIFT 16 +#define IO_APIC_MAX_REDIRECTION_ENTRY_MASK 0xff + +// Fields of each redirection table entry +#define IO_APIC_DESTINATION_FIELD_SHIFT 56 +#define IO_APIC_DESTINATION_FIELD_MASK 0x0f +#define IO_APIC_INTERRUPT_MASK_SHIFT 16 +#define IO_APIC_INTERRUPT_MASKED 1 +#define IO_APIC_INTERRUPT_UNMASKED 0 +#define IO_APIC_TRIGGER_MODE_SHIFT 15 +#define IO_APIC_TRIGGER_MODE_EDGE 0 +#define IO_APIC_TRIGGER_MODE_LEVEL 1 +#define IO_APIC_REMOTE_IRR_SHIFT 14 +#define IO_APIC_PIN_POLARITY_SHIFT 13 +#define IO_APIC_PIN_POLARITY_HIGH_ACTIVE 0 +#define IO_APIC_PIN_POLARITY_LOW_ACTIVE 1 +#define IO_APIC_DELIVERY_STATUS_SHIFT 12 +#define IO_APIC_DELIVERY_STATUS_IDLE 0 +#define IO_APIC_DELIVERY_STATUS_PENDING 1 +#define IO_APIC_DESTINATION_MODE_SHIFT 11 +#define IO_APIC_DESTINATION_MODE_PHYSICAL 0 +#define IO_APIC_DESTINATION_MODE_LOGICAL 1 +#define IO_APIC_DELIVERY_MODE_SHIFT 8 +#define IO_APIC_DELIVERY_MODE_MASK 0x07 +#define IO_APIC_DELIVERY_MODE_FIXED 0 +#define IO_APIC_DELIVERY_MODE_LOWEST_PRIO 1 +#define IO_APIC_DELIVERY_MODE_SMI 2 +#define IO_APIC_DELIVERY_MODE_NMI 4 +#define IO_APIC_DELIVERY_MODE_INIT 5 +#define IO_APIC_DELIVERY_MODE_EXT_INT 7 +#define IO_APIC_INTERRUPT_VECTOR_SHIFT 0 +#define IO_APIC_INTERRUPT_VECTOR_MASK 0xff + +typedef struct ioapic_s { + volatile uint32 io_register_select; + uint32 reserved[3]; + volatile uint32 io_window_register; +} ioapic _PACKED; + +static ioapic *sIOAPIC = NULL; +static uint32 sIOAPICMaxRedirectionEntry = 23; +static void *sLocalAPIC = NULL; + +static uint32 sIRQToIOAPICPin[256]; + +bool gUsingIOAPIC = false; + +typedef struct interrupt_controller_s { + const char *name; + void (*enable_io_interrupt)(int32 num); + void (*disable_io_interrupt)(int32 num); + void (*configure_io_interrupt)(int32 num, uint32 config); + bool (*is_spurious_interrupt)(int32 num); + void (*end_of_interrupt)(int32 num); +} interrupt_controller; + +static interrupt_controller *sCurrentPIC = NULL; + + static const char *kInterruptNames[] = { /* 0 */ "Divide Error Exception", /* 1 */ "Debug Exception", @@ -105,7 +175,7 @@ typedef struct { } desc_table; static desc_table *sIDT = NULL; -static uint16 sLevelTriggeredInterrupts; +static uint32 sLevelTriggeredInterrupts = 0; // binary mask: 1 level, 0 edge // table with functions handling respective interrupts @@ -156,7 +226,7 @@ x86_set_task_gate(int32 n, int32 segment) * it must assume it's a spurious interrupt. */ -static inline bool +static bool pic_is_spurious_interrupt(int32 num) { int32 isr; @@ -184,34 +254,107 @@ pic_is_spurious_interrupt(int32 num) static void pic_end_of_interrupt(int32 num) { - if (num >= PIC_INT_BASE && num <= PIC_INT_BASE + PIC_NUM_INTS) { - // PIC 8259 controlled interrupt - if (num >= PIC_SLAVE_INT_BASE) - out8(PIC_NON_SPECIFIC_EOI, PIC_SLAVE_CONTROL); + if (num < 0 || num > PIC_NUM_INTS) + return; - // we always need to acknowledge the master PIC - out8(PIC_NON_SPECIFIC_EOI, PIC_MASTER_CONTROL); - } + // PIC 8259 controlled interrupt + if (num >= PIC_SLAVE_INT_BASE) + out8(PIC_NON_SPECIFIC_EOI, PIC_SLAVE_CONTROL); + + // we always need to acknowledge the master PIC + out8(PIC_NON_SPECIFIC_EOI, PIC_MASTER_CONTROL); } -static inline bool -pic_is_level_triggered(int32 num) +static void +pic_enable_io_interrupt(int32 num) { - return sLevelTriggeredInterrupts & (1U << (num - PIC_INT_BASE)); + // interrupt is specified "normalized" + if (num < 0 || num > PIC_NUM_INTS) + return; + + // enable PIC 8259 controlled interrupt + + TRACE(("pic_enable_io_interrupt: irq %ld\n", num)); + + if (num < PIC_SLAVE_INT_BASE) + out8(in8(PIC_MASTER_MASK) & ~(1 << num), PIC_MASTER_MASK); + else + out8(in8(PIC_SLAVE_MASK) & ~(1 << (num - PIC_SLAVE_INT_BASE)), PIC_SLAVE_MASK); +} + + +static void +pic_disable_io_interrupt(int32 num) +{ + // interrupt is specified "normalized" + // never disable slave pic line IRQ 2 + if (num < 0 || num > PIC_NUM_INTS || num == 2) + return; + + // disable PIC 8259 controlled interrupt + + TRACE(("pic_disable_io_interrupt: irq %ld\n", num)); + + if (num < PIC_SLAVE_INT_BASE) + out8(in8(PIC_MASTER_MASK) | (1 << num), PIC_MASTER_MASK); + else + out8(in8(PIC_SLAVE_MASK) | (1 << (num - PIC_SLAVE_INT_BASE)), PIC_SLAVE_MASK); +} + + +static void +pic_configure_io_interrupt(int32 num, uint32 config) +{ + uint8 value; + int32 localBit; + if (num < 0 || num > PIC_NUM_INTS || num == 2) + return; + + TRACE(("pic_configure_io_interrupt: irq %ld; config 0x%08lx\n", num, config)); + + if (num < PIC_SLAVE_INT_BASE) { + value = in8(PIC_MASTER_TRIGGER_MODE); + localBit = num; + } else { + value = in8(PIC_SLAVE_TRIGGER_MODE); + localBit = num - PIC_SLAVE_INT_BASE; + } + + if (config & B_LEVEL_TRIGGERED) + value |= 1 << localBit; + else + value &= ~(1 << localBit); + + if (num < PIC_SLAVE_INT_BASE) + out8(value, PIC_MASTER_TRIGGER_MODE); + else + out8(value, PIC_SLAVE_TRIGGER_MODE); + + sLevelTriggeredInterrupts = in8(PIC_MASTER_TRIGGER_MODE) + | (in8(PIC_SLAVE_TRIGGER_MODE) << 8); } static void pic_init(void) { + static interrupt_controller picController = { + "8259 PIC", + &pic_enable_io_interrupt, + &pic_disable_io_interrupt, + &pic_configure_io_interrupt, + &pic_is_spurious_interrupt, + &pic_end_of_interrupt + }; + // Start initialization sequence for the master and slave PICs out8(PIC_INIT1 | PIC_INIT1_SEND_INIT4, PIC_MASTER_INIT1); out8(PIC_INIT1 | PIC_INIT1_SEND_INIT4, PIC_SLAVE_INIT1); // Set start of interrupts to 0x20 for master, 0x28 for slave - out8(PIC_INT_BASE, PIC_MASTER_INIT2); - out8(PIC_SLAVE_INT_BASE, PIC_SLAVE_INIT2); + out8(ARCH_INTERRUPT_BASE, PIC_MASTER_INIT2); + out8(ARCH_INTERRUPT_BASE + PIC_SLAVE_INT_BASE, PIC_SLAVE_INIT2); // Specify cascading through interrupt 2 out8(PIC_INIT3_IR2_IS_SLAVE, PIC_MASTER_INIT3); @@ -228,49 +371,277 @@ pic_init(void) #if 0 // should set everything possible to level triggered - out8(PIC_MASTER_TRIGGER_MODE, 0xf8); - out8(PIC_SLAVE_TRIGGER_MODE, 0xde); + out8(0xf8, PIC_MASTER_TRIGGER_MODE); + out8(0xde, PIC_SLAVE_TRIGGER_MODE); #endif sLevelTriggeredInterrupts = in8(PIC_MASTER_TRIGGER_MODE) | (in8(PIC_SLAVE_TRIGGER_MODE) << 8); - TRACE(("PIC level trigger mode: %04x\n", sLevelTriggeredInterrupts)); + TRACE(("PIC level trigger mode: 0x%08lx\n", sLevelTriggeredInterrupts)); + + // make the pic controller the current one + sCurrentPIC = &picController; + gUsingIOAPIC = false; +} + + +static inline uint32 +ioapic_read_32(uint8 registerSelect) +{ + sIOAPIC->io_register_select = registerSelect; + return sIOAPIC->io_window_register; +} + + +static inline void +ioapic_write_32(uint8 registerSelect, uint32 value) +{ + sIOAPIC->io_register_select = registerSelect; + sIOAPIC->io_window_register = value; +} + + +static inline uint64 +ioapic_read_64(uint8 registerSelect) +{ + uint64 result; + sIOAPIC->io_register_select = registerSelect + 1; + result = sIOAPIC->io_window_register; + result <<= 32; + sIOAPIC->io_register_select = registerSelect; + result |= sIOAPIC->io_window_register; + return result; +} + + +static inline void +ioapic_write_64(uint8 registerSelect, uint64 value) +{ + sIOAPIC->io_register_select = registerSelect; + sIOAPIC->io_window_register = (uint32)value; + sIOAPIC->io_register_select = registerSelect + 1; + sIOAPIC->io_window_register = (uint32)(value >> 32); +} + + +static bool +ioapic_is_spurious_interrupt(int32 num) +{ + // the spurious interrupt vector is initialized to the max value in smp + return num == 0xff - ARCH_INTERRUPT_BASE; +} + + +static void +ioapic_end_of_interrupt(int32 num) +{ + *(volatile uint32 *)((char *)sLocalAPIC + APIC_EOI) = 0; +} + + +static void +ioapic_enable_io_interrupt(int32 num) +{ + uint64 entry; + int32 pin = sIRQToIOAPICPin[num]; + if (pin < 0 || pin > sIOAPICMaxRedirectionEntry) + return; + + TRACE(("ioapic_enable_io_interrupt: IRQ %ld -> pin %ld\n", num, pin)); + + entry = ioapic_read_64(IO_APIC_REDIRECTION_TABLE + pin * 2); + entry &= ~(1 << IO_APIC_INTERRUPT_MASK_SHIFT); + entry |= IO_APIC_INTERRUPT_UNMASKED << IO_APIC_INTERRUPT_MASK_SHIFT; + ioapic_write_64(IO_APIC_REDIRECTION_TABLE + pin * 2, entry); +} + + +static void +ioapic_disable_io_interrupt(int32 num) +{ + uint64 entry; + int32 pin = sIRQToIOAPICPin[num]; + if (pin < 0 || pin > sIOAPICMaxRedirectionEntry) + return; + + TRACE(("ioapic_disable_io_interrupt: IRQ %ld -> pin %ld\n", num, pin)); + + entry = ioapic_read_64(IO_APIC_REDIRECTION_TABLE + pin * 2); + entry &= ~(1 << IO_APIC_INTERRUPT_MASK_SHIFT); + entry |= IO_APIC_INTERRUPT_MASKED << IO_APIC_INTERRUPT_MASK_SHIFT; + ioapic_write_64(IO_APIC_REDIRECTION_TABLE + pin * 2, entry); +} + + +static void +ioapic_configure_io_interrupt(int32 num, uint32 config) +{ + uint64 entry; + int32 pin = sIRQToIOAPICPin[num]; + if (pin < 0 || pin > sIOAPICMaxRedirectionEntry) + return; + + TRACE(("ioapic_configure_io_interrupt: IRQ %ld -> pin %ld; config 0x%08lx\n", + num, pin, config)); + + entry = ioapic_read_64(IO_APIC_REDIRECTION_TABLE + pin * 2); + entry &= ~((1 << IO_APIC_TRIGGER_MODE_SHIFT) + | (1 << IO_APIC_PIN_POLARITY_SHIFT) + | (IO_APIC_INTERRUPT_VECTOR_MASK << IO_APIC_INTERRUPT_VECTOR_SHIFT)); + + if (config & B_LEVEL_TRIGGERED) { + entry |= (IO_APIC_TRIGGER_MODE_LEVEL << IO_APIC_TRIGGER_MODE_SHIFT); + sLevelTriggeredInterrupts |= (1 << num); + } else { + entry |= (IO_APIC_TRIGGER_MODE_EDGE << IO_APIC_TRIGGER_MODE_SHIFT); + sLevelTriggeredInterrupts &= ~(1 << num); + } + + if (config & B_LOW_ACTIVE_POLARITY) + entry |= (IO_APIC_PIN_POLARITY_LOW_ACTIVE << IO_APIC_PIN_POLARITY_SHIFT); + else + entry |= (IO_APIC_PIN_POLARITY_HIGH_ACTIVE << IO_APIC_PIN_POLARITY_SHIFT); + + entry |= (num + ARCH_INTERRUPT_BASE) << IO_APIC_INTERRUPT_VECTOR_SHIFT; + ioapic_write_64(IO_APIC_REDIRECTION_TABLE + pin * 2, entry); +} + + +static void +ioapic_init(kernel_args *args) +{ + uint32 i; + uint32 version; + uint64 targetAPIC; + void *settings; + + static interrupt_controller ioapicController = { + "82093AA IOAPIC", + &ioapic_enable_io_interrupt, + &ioapic_disable_io_interrupt, + &ioapic_configure_io_interrupt, + &ioapic_is_spurious_interrupt, + &ioapic_end_of_interrupt + }; + + if (args->arch_args.apic == NULL) { + dprintf("no local apic availabe\n"); + return; + } + + // always map the local apic as it can be used for timers even if we + // don't end up using the io apic + sLocalAPIC = args->arch_args.apic; + if (map_physical_memory("local apic", (void *)args->arch_args.apic_phys, + B_PAGE_SIZE, B_EXACT_ADDRESS, B_KERNEL_READ_AREA + | B_KERNEL_WRITE_AREA, &sLocalAPIC) < B_OK) { + panic("mapping the local apic failed"); + return; + } + + if (args->arch_args.ioapic == NULL) { + dprintf("no ioapic available, not using ioapics for interrupt routing\n"); + return; + } + + settings = load_driver_settings(B_SAFEMODE_DRIVER_SETTINGS); + if (settings != NULL && get_driver_boolean_parameter(settings, + B_SAFEMODE_DISABLE_IOAPIC, false, false)) { + dprintf("ioapic explicitly disabled, not using ioapics for interrupt routing\n"); + unload_driver_settings(settings); + return; + } else if (settings != NULL) + unload_driver_settings(settings); + + // TODO: remove when the PCI IRQ routing through ACPI is available below + return; + + // map in the ioapic + sIOAPIC = (ioapic *)args->arch_args.ioapic; + if (map_physical_memory("ioapic", (void *)args->arch_args.ioapic_phys, + B_PAGE_SIZE, B_EXACT_ADDRESS, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, + (void **)&sIOAPIC) < B_OK) { + panic("mapping the ioapic failed"); + return; + } + + version = ioapic_read_32(IO_APIC_VERSION); + if (version == 0xffffffff) { + dprintf("ioapic seems inaccessible, not using it\n"); + return; + } + + sLevelTriggeredInterrupts = 0; + sIOAPICMaxRedirectionEntry + = ((version >> IO_APIC_MAX_REDIRECTION_ENTRY_SHIFT) + & IO_APIC_MAX_REDIRECTION_ENTRY_MASK); + + // use the boot CPU as the target for all interrupts + targetAPIC = args->arch_args.cpu_apic_id[0]; + + // program the interrupt vectors of the ioapic + for (i = 0; i <= sIOAPICMaxRedirectionEntry; i++) { + // initialize everything to deliver to the boot CPU in physical mode + // and masked until explicitly enabled through enable_io_interrupt() + uint64 entry = (targetAPIC << IO_APIC_DESTINATION_FIELD_SHIFT) + | (IO_APIC_INTERRUPT_MASKED << IO_APIC_INTERRUPT_MASK_SHIFT) + | (IO_APIC_DESTINATION_MODE_PHYSICAL << IO_APIC_DESTINATION_MODE_SHIFT) + | ((i + ARCH_INTERRUPT_BASE) << IO_APIC_INTERRUPT_VECTOR_SHIFT); + + if (i == 0) { + // make redirection entry 0 into an external interrupt + entry |= (IO_APIC_TRIGGER_MODE_EDGE << IO_APIC_TRIGGER_MODE_SHIFT) + | (IO_APIC_PIN_POLARITY_HIGH_ACTIVE << IO_APIC_PIN_POLARITY_SHIFT) + | (IO_APIC_DELIVERY_MODE_EXT_INT << IO_APIC_DELIVERY_MODE_SHIFT); + } else if (i < 16) { + // make 1-15 ISA interrupts + entry |= (IO_APIC_TRIGGER_MODE_EDGE << IO_APIC_TRIGGER_MODE_SHIFT) + | (IO_APIC_PIN_POLARITY_HIGH_ACTIVE << IO_APIC_PIN_POLARITY_SHIFT) + | (IO_APIC_DELIVERY_MODE_FIXED << IO_APIC_DELIVERY_MODE_SHIFT); + } else { + // and the rest are PCI interrupts + entry |= (IO_APIC_TRIGGER_MODE_LEVEL << IO_APIC_TRIGGER_MODE_SHIFT) + | (IO_APIC_PIN_POLARITY_LOW_ACTIVE << IO_APIC_PIN_POLARITY_SHIFT) + | (IO_APIC_DELIVERY_MODE_FIXED << IO_APIC_DELIVERY_MODE_SHIFT); + sLevelTriggeredInterrupts |= (1 << i); + } + + ioapic_write_64(IO_APIC_REDIRECTION_TABLE + 2 * i, entry); + } + + // setup default 1:1 mapping + for (i = 0; i < 256; i++) + sIRQToIOAPICPin[i] = i; + + // TODO: here ACPI needs to be used to properly set up the PCI IRQ + // routing. + + // prefer the ioapic over the normal pic + dprintf("using ioapic for interrupt routing\n"); + sCurrentPIC = &ioapicController; + gUsingIOAPIC = true; } void arch_int_enable_io_interrupt(int irq) { - // interrupt is specified "normalized" - if (irq < 0 || irq > PIC_NUM_INTS) - return; - - // enable PIC 8259 controlled interrupt - - TRACE(("arch_int_enable_io_interrupt: irq %d\n", irq)); - - if (irq < 8) - out8(in8(PIC_MASTER_MASK) & ~(1 << irq), PIC_MASTER_MASK); - else - out8(in8(PIC_SLAVE_MASK) & ~(1 << (irq - 8)), PIC_SLAVE_MASK); + sCurrentPIC->enable_io_interrupt(irq); } void arch_int_disable_io_interrupt(int irq) { - // interrupt is specified "normalized" - // never disable slave pic line IRQ 2 - if (irq < 0 || irq > PIC_NUM_INTS || irq == 2) - return; + sCurrentPIC->disable_io_interrupt(irq); +} - // disable PIC 8259 controlled interrupt - if (irq < 8) - out8(in8(PIC_MASTER_MASK) | (1 << irq), PIC_MASTER_MASK); - else - out8(in8(PIC_SLAVE_MASK) | (1 << (irq - 8)), PIC_SLAVE_MASK); +void +arch_int_configure_io_interrupt(int irq, uint32 config) +{ + sCurrentPIC->configure_io_interrupt(irq, config); } @@ -504,24 +875,25 @@ page_fault_exception(struct iframe* frame) static void hardware_interrupt(struct iframe* frame) { - bool levelTriggered = pic_is_level_triggered(frame->vector); + int32 vector = frame->vector - ARCH_INTERRUPT_BASE; + bool levelTriggered = false; int ret; - // This is a workaround for spurious assertions of interrupts 7/15 - // which seems to be an often seen problem on the PC platform - if (pic_is_spurious_interrupt(frame->vector - ARCH_INTERRUPT_BASE)) { - TRACE(("got spurious interrupt at vector %ld\n", frame->vector)); + if (sCurrentPIC->is_spurious_interrupt(vector)) { + TRACE(("got spurious interrupt at vector %ld\n", vector)); return; } - if (!levelTriggered) - pic_end_of_interrupt(frame->vector); + if (vector < 32) + levelTriggered = (sLevelTriggeredInterrupts & (1 << vector)) != 0; - ret = int_io_interrupt_handler(frame->vector - ARCH_INTERRUPT_BASE, - levelTriggered); + if (!levelTriggered) + sCurrentPIC->end_of_interrupt(vector); + + ret = int_io_interrupt_handler(vector, levelTriggered); if (levelTriggered) - pic_end_of_interrupt(frame->vector); + sCurrentPIC->end_of_interrupt(vector); if (ret == B_INVOKE_SCHEDULER) { cpu_status state = disable_interrupts(); @@ -536,7 +908,7 @@ hardware_interrupt(struct iframe* frame) status_t -arch_int_init(kernel_args *args) +arch_int_init(struct kernel_args *args) { int i; interrupt_handler_function** table; @@ -544,7 +916,7 @@ arch_int_init(kernel_args *args) // set the global sIDT variable sIDT = (desc_table *)args->arch_args.vir_idt; - // setup the interrupt controller + // setup the standard programmable interrupt controller pic_init(); set_intr_gate(0, &trap0); @@ -584,6 +956,14 @@ arch_int_init(kernel_args *args) set_intr_gate(45, &trap45); set_intr_gate(46, &trap46); set_intr_gate(47, &trap47); + set_intr_gate(48, &trap48); + set_intr_gate(49, &trap49); + set_intr_gate(50, &trap50); + set_intr_gate(51, &trap51); + set_intr_gate(52, &trap52); + set_intr_gate(53, &trap53); + set_intr_gate(54, &trap54); + set_intr_gate(55, &trap55); set_system_gate(98, &trap98); // for performance testing only set_system_gate(99, &trap99); @@ -628,10 +1008,12 @@ arch_int_init(kernel_args *args) status_t -arch_int_init_post_vm(kernel_args *args) +arch_int_init_post_vm(struct kernel_args *args) { area_id area; + ioapic_init(args); + sIDT = (desc_table *)args->arch_args.vir_idt; area = create_area("idt", (void *)&sIDT, B_EXACT_ADDRESS, B_PAGE_SIZE, B_ALREADY_WIRED, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA); diff --git a/src/system/kernel/arch/x86/arch_interrupts.S b/src/system/kernel/arch/x86/arch_interrupts.S index 7906bc0f9c..634555126f 100644 --- a/src/system/kernel/arch/x86/arch_interrupts.S +++ b/src/system/kernel/arch/x86/arch_interrupts.S @@ -231,6 +231,14 @@ TRAP(trap44, 44) TRAP(trap45, 45) TRAP(trap46, 46) TRAP(trap47, 47) +TRAP(trap48, 48) +TRAP(trap49, 49) +TRAP(trap50, 50) +TRAP(trap51, 51) +TRAP(trap52, 52) +TRAP(trap53, 53) +TRAP(trap54, 54) +TRAP(trap55, 55) TRAP(trap251, 251) TRAP(trap252, 252) diff --git a/src/system/kernel/arch/x86/arch_smp.c b/src/system/kernel/arch/x86/arch_smp.c index 7fa6c75733..08b347f2df 100644 --- a/src/system/kernel/arch/x86/arch_smp.c +++ b/src/system/kernel/arch/x86/arch_smp.c @@ -34,65 +34,24 @@ # define TRACE(x) ; #endif -//#define TRACE_ARCH_SMP_TIMER -#ifdef TRACE_ARCH_SMP_TIMER -# define TRACE_TIMER(x) dprintf x -#else -# define TRACE_TIMER(x) ; -#endif - -extern timer_info gAPICTimer; - -static void *apic = NULL; -static uint32 cpu_apic_id[B_MAX_CPU_COUNT] = {0, 0}; -static uint32 cpu_os_id[B_MAX_CPU_COUNT] = {0, 0}; -static uint32 cpu_apic_version[B_MAX_CPU_COUNT] = {0, 0}; -static void *ioapic = NULL; - -static int32 -i386_ici_interrupt(void *data) -{ - // genuine inter-cpu interrupt - TRACE(("inter-cpu interrupt on cpu %ld\n", smp_get_current_cpu())); - arch_smp_ack_interrupt(); - - return smp_intercpu_int_handler(); -} - - -static int32 -i386_spurious_interrupt(void *data) -{ - // spurious interrupt - TRACE(("spurious interrupt on cpu %ld\n", smp_get_current_cpu())); - arch_smp_ack_interrupt(); - - return B_HANDLED_INTERRUPT; -} - - -static int32 -i386_smp_error_interrupt(void *data) -{ - // smp error interrupt - TRACE(("smp error interrupt on cpu %ld\n", smp_get_current_cpu())); - arch_smp_ack_interrupt(); - - return B_HANDLED_INTERRUPT; -} +static void *sLocalAPIC = NULL; +static uint32 sCPUAPICIds[B_MAX_CPU_COUNT]; +static uint32 sCPUOSIds[B_MAX_CPU_COUNT]; +static uint32 sAPICVersions[B_MAX_CPU_COUNT]; +extern bool gUsingIOAPIC; static uint32 apic_read(uint32 offset) { - return *(volatile uint32 *)((char *)apic + offset); + return *(volatile uint32 *)((char *)sLocalAPIC + offset); } static void apic_write(uint32 offset, uint32 data) { - *(volatile uint32 *)((char *)apic + offset) = data; + *(volatile uint32 *)((char *)sLocalAPIC + offset) = data; } @@ -156,33 +115,74 @@ setup_apic(kernel_args *args, int32 cpu) } +static int32 +i386_ici_interrupt(void *data) +{ + // genuine inter-cpu interrupt + TRACE(("inter-cpu interrupt on cpu %ld\n", smp_get_current_cpu())); + + // if we are not using the IO APIC we need to acknowledge the + // interrupt ourselfs + if (!gUsingIOAPIC) + apic_write(APIC_EOI, 0); + + return smp_intercpu_int_handler(); +} + + +static int32 +i386_spurious_interrupt(void *data) +{ + // spurious interrupt + TRACE(("spurious interrupt on cpu %ld\n", smp_get_current_cpu())); + + // spurious interrupts must not be acknowledged as it does not expect + // a end of interrupt - if we still do it we would loose the next best + // interrupt + return B_HANDLED_INTERRUPT; +} + + +static int32 +i386_smp_error_interrupt(void *data) +{ + // smp error interrupt + TRACE(("smp error interrupt on cpu %ld\n", smp_get_current_cpu())); + + // if we are not using the IO APIC we need to acknowledge the + // interrupt ourselfs + if (!gUsingIOAPIC) + apic_write(APIC_EOI, 0); + + return B_HANDLED_INTERRUPT; +} + + status_t arch_smp_init(kernel_args *args) { TRACE(("arch_smp_init: entry\n")); + if (args->arch_args.apic == NULL) + return B_OK; + + sLocalAPIC = args->arch_args.apic; + + // setup some globals + memcpy(sCPUAPICIds, args->arch_args.cpu_apic_id, sizeof(args->arch_args.cpu_apic_id)); + memcpy(sCPUOSIds, args->arch_args.cpu_os_id, sizeof(args->arch_args.cpu_os_id)); + memcpy(sAPICVersions, args->arch_args.cpu_apic_version, sizeof(args->arch_args.cpu_apic_version)); + + // set up the local apic on the boot cpu + arch_smp_per_cpu_init(args, 0); + if (args->num_cpus > 1) { - // setup some globals - apic = (void *)args->arch_args.apic; - ioapic = (void *)args->arch_args.ioapic; - memcpy(cpu_apic_id, args->arch_args.cpu_apic_id, sizeof(args->arch_args.cpu_apic_id)); - memcpy(cpu_os_id, args->arch_args.cpu_os_id, sizeof(args->arch_args.cpu_os_id)); - memcpy(cpu_apic_version, args->arch_args.cpu_apic_version, sizeof(args->arch_args.cpu_apic_version)); - - // setup regions that represent the apic & ioapic - map_physical_memory("local apic", (void *)args->arch_args.apic_phys, B_PAGE_SIZE, - B_EXACT_ADDRESS, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, &apic); - map_physical_memory("ioapic", (void *)args->arch_args.ioapic_phys, B_PAGE_SIZE, - B_EXACT_ADDRESS, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, &ioapic); - - // set up the local apic on the boot cpu - arch_smp_per_cpu_init(args, 0); - // I/O interrupts start at ARCH_INTERRUPT_BASE, so all interrupts are shifted install_io_interrupt_handler(0xfd - ARCH_INTERRUPT_BASE, &i386_ici_interrupt, NULL, B_NO_LOCK_VECTOR); install_io_interrupt_handler(0xfe - ARCH_INTERRUPT_BASE, &i386_smp_error_interrupt, NULL, B_NO_LOCK_VECTOR); install_io_interrupt_handler(0xff - ARCH_INTERRUPT_BASE, &i386_spurious_interrupt, NULL, B_NO_LOCK_VECTOR); } + return B_OK; } @@ -224,7 +224,7 @@ arch_smp_send_ici(int32 target_cpu) state = disable_interrupts(); config = apic_read(APIC_INTR_COMMAND_2) & APIC_INTR_COMMAND_2_MASK; - apic_write(APIC_INTR_COMMAND_2, config | cpu_apic_id[target_cpu] << 24); + apic_write(APIC_INTR_COMMAND_2, config | sCPUAPICIds[target_cpu] << 24); config = apic_read(APIC_INTR_COMMAND_1) & APIC_INTR_COMMAND_1_MASK; apic_write(APIC_INTR_COMMAND_1, config | 0xfd | APIC_DELIVERY_MODE_FIXED @@ -242,10 +242,3 @@ arch_smp_send_ici(int32 target_cpu) restore_interrupts(state); } - - -void -arch_smp_ack_interrupt(void) -{ - apic_write(APIC_EOI, 0); -} diff --git a/src/system/kernel/arch/x86/interrupts.h b/src/system/kernel/arch/x86/interrupts.h index aae9784ee5..fe064f1baf 100644 --- a/src/system/kernel/arch/x86/interrupts.h +++ b/src/system/kernel/arch/x86/interrupts.h @@ -19,7 +19,8 @@ void trap12();void trap13();void trap14();void trap16();void trap17();void trap1 void trap19(); void trap32();void trap33();void trap34();void trap35();void trap36();void trap37(); void trap38();void trap39();void trap40();void trap41();void trap42();void trap43(); -void trap44();void trap45();void trap46();void trap47(); +void trap44();void trap45();void trap46();void trap47();void trap48();void trap49(); +void trap50();void trap51();void trap52();void trap53();void trap54();void trap55(); void double_fault(); // int 8 diff --git a/src/system/kernel/arch/x86/timers/x86_apic.c b/src/system/kernel/arch/x86/timers/x86_apic.c index a8b8af11f4..fe9d0d8a7b 100644 --- a/src/system/kernel/arch/x86/timers/x86_apic.c +++ b/src/system/kernel/arch/x86/timers/x86_apic.c @@ -21,6 +21,8 @@ static void *sApicPtr = NULL; static uint32 sApicTicsPerSec = 0; +extern bool gUsingIOAPIC; + struct timer_info gAPICTimer = { "APIC", &apic_get_prio, @@ -51,17 +53,14 @@ _apic_write(uint32 offset, uint32 data) } -static void -_apic_acknowledge_interrupt(void) -{ - _apic_write(APIC_EOI, 0); -} - - static int32 apic_timer_interrupt(void *data) { - _apic_acknowledge_interrupt(); + // if we are not using the IO APIC we need to acknowledge the + // interrupt ourselfs + if (!gUsingIOAPIC) + _apic_write(APIC_EOI, 0); + return timer_interrupt(); }