diff --git a/headers/private/kernel/int.h b/headers/private/kernel/int.h index 8fe8fd59d7..b3642c4d80 100644 --- a/headers/private/kernel/int.h +++ b/headers/private/kernel/int.h @@ -53,4 +53,8 @@ are_interrupts_enabled(void) #define restore_interrupts(status) arch_int_restore_interrupts(status) +status_t reserve_io_interrupt_vectors(long count, long startVector); +status_t allocate_io_interrupt_vectors(long count, long *startVector); +void free_io_interrupt_vectors(long count, long startVector); + #endif /* _KERNEL_INT_H */ diff --git a/src/system/kernel/arch/x86/arch_int.cpp b/src/system/kernel/arch/x86/arch_int.cpp index fe746b342b..123f575bb3 100644 --- a/src/system/kernel/arch/x86/arch_int.cpp +++ b/src/system/kernel/arch/x86/arch_int.cpp @@ -84,6 +84,7 @@ static desc_table* sIDTs[B_MAX_CPU_COUNT]; // table with functions handling respective interrupts typedef void interrupt_handler_function(struct iframe* frame); + #define INTERRUPT_HANDLER_TABLE_SIZE 256 interrupt_handler_function* gInterruptHandlerTable[ INTERRUPT_HANDLER_TABLE_SIZE]; @@ -653,6 +654,8 @@ arch_int_init(struct kernel_args *args) set_trap_gate(0, 98, &trap98); // for performance testing only set_trap_gate(0, 99, &trap99); // syscall interrupt + reserve_io_interrupt_vectors(2, 98); + // configurable msi or msi-x interrupts set_interrupt_gate(0, 100, &trap100); set_interrupt_gate(0, 101, &trap101); diff --git a/src/system/kernel/arch/x86/arch_smp.cpp b/src/system/kernel/arch/x86/arch_smp.cpp index 1b585eada6..dd67bea52c 100644 --- a/src/system/kernel/arch/x86/arch_smp.cpp +++ b/src/system/kernel/arch/x86/arch_smp.cpp @@ -90,6 +90,7 @@ arch_smp_init(kernel_args *args) if (args->num_cpus > 1) { // I/O interrupts start at ARCH_INTERRUPT_BASE, so all interrupts are shifted + reserve_io_interrupt_vectors(3, 0xfd - ARCH_INTERRUPT_BASE); 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); diff --git a/src/system/kernel/arch/x86/ioapic.cpp b/src/system/kernel/arch/x86/ioapic.cpp index 5b5e52f910..78f6129718 100644 --- a/src/system/kernel/arch/x86/ioapic.cpp +++ b/src/system/kernel/arch/x86/ioapic.cpp @@ -767,6 +767,14 @@ ioapic_init(kernel_args* args) ioapic_enable_io_interrupt(i); } + // mark the interrupt vectors reserved so they aren't used for other stuff + current = sIOAPICs; + while (current != NULL) { + reserve_io_interrupt_vectors(current->max_redirection_entry + 1, + current->global_interrupt_base); + current = current->next; + } + // prefer the ioapic over the normal pic dprintf("using io-apics for interrupt routing\n"); arch_int_set_interrupt_controller(ioapicController); diff --git a/src/system/kernel/arch/x86/msi.cpp b/src/system/kernel/arch/x86/msi.cpp index 2972de74c0..a859a9a07c 100644 --- a/src/system/kernel/arch/x86/msi.cpp +++ b/src/system/kernel/arch/x86/msi.cpp @@ -4,20 +4,14 @@ */ #include -#include -#include #include #include +#include #include -static const uint32 kVectorCount = 256 - ARCH_INTERRUPT_BASE; -static const uint8 kNumISAVectors = 16; - static bool sMSISupported = false; -static bool sAllocatedVectors[kVectorCount]; -static mutex sMSIAllocationLock = MUTEX_INITIALIZER("msi_allocation"); void @@ -28,27 +22,6 @@ msi_init() return; } - // TODO: less hardcoding! - - // the first 16 vectors are legacy ISA in all cases - for (uint16 i = 0; i < kNumISAVectors; i++) - sAllocatedVectors[i] = true; - - for (uint16 i = kNumISAVectors; i < kVectorCount; i++) { - // if ioapics aren't in use this will always return false, leaving - // the vectors free for us; otherwise we'll avoid any vector that - // can be addressed by an IO-APIC - sAllocatedVectors[i] = ioapic_is_interrupt_available(i); - } - - // performance testing and syscall interrupts - sAllocatedVectors[98 - ARCH_INTERRUPT_BASE] = true; - sAllocatedVectors[99 - ARCH_INTERRUPT_BASE] = true; - - // the upper range is used by apic local (timer) and smp interrupts (ipi) - for (uint16 i = 251; i < 256; i++) - sAllocatedVectors[i - ARCH_INTERRUPT_BASE] = true; - dprintf("msi support enabled\n"); sMSISupported = true; } @@ -68,46 +41,24 @@ msi_allocate_vectors(uint8 count, uint8 *startVector, uint64 *address, if (!sMSISupported) return B_UNSUPPORTED; - mutex_lock(&sMSIAllocationLock); + long vector; + status_t result = allocate_io_interrupt_vectors(count, &vector); + if (result != B_OK) + return result; - uint8 vector = 0; - bool runFound = true; - for (uint16 i = 0; i < kVectorCount - (count - 1); i++) { - if (!sAllocatedVectors[i]) { - vector = i; - runFound = true; - for (uint16 j = 1; j < count; j++) { - if (sAllocatedVectors[i + j]) { - runFound = false; - i += j; - break; - } - } - - if (runFound) - break; - } - } - - if (!runFound) { - mutex_unlock(&sMSIAllocationLock); - dprintf("found no free vectors to allocate %u msi messages\n", count); + if (vector >= 256) { + free_io_interrupt_vectors(count, vector); return B_NO_MEMORY; } - for (uint16 i = 0; i < count; i++) - sAllocatedVectors[i + vector] = true; - - mutex_unlock(&sMSIAllocationLock); - - *startVector = vector; + *startVector = (uint8)vector; *address = MSI_ADDRESS_BASE | (0 << MSI_DESTINATION_ID_SHIFT) | MSI_NO_REDIRECTION | MSI_DESTINATION_MODE_PHYSICAL; *data = MSI_TRIGGER_MODE_EDGE | MSI_DELIVERY_MODE_FIXED - | (vector + ARCH_INTERRUPT_BASE); + | ((uint16)vector + ARCH_INTERRUPT_BASE); dprintf("msi_allocate_vectors: allocated %u vectors starting from %u\n", - count, vector); + count, *startVector); return B_OK; } @@ -120,21 +71,8 @@ msi_free_vectors(uint8 count, uint8 startVector) return; } - if ((uint32)startVector + count > kVectorCount) { - panic("invalid start vector %u or count %u supplied to " - "msi_free_vectors\n", startVector, count); - } - dprintf("msi_free_vectors: freeing %u vectors starting from %u\n", count, startVector); - mutex_lock(&sMSIAllocationLock); - for (uint16 i = 0; i < count; i++) { - if (!sAllocatedVectors[i + startVector]) - panic("msi vector %u was not allocated\n", i + startVector); - - sAllocatedVectors[i + startVector] = false; - } - - mutex_unlock(&sMSIAllocationLock); + free_io_interrupt_vectors(count, startVector); } diff --git a/src/system/kernel/arch/x86/pic.cpp b/src/system/kernel/arch/x86/pic.cpp index 2f5916b671..7e086312c2 100644 --- a/src/system/kernel/arch/x86/pic.cpp +++ b/src/system/kernel/arch/x86/pic.cpp @@ -13,6 +13,8 @@ #include +#include + //#define TRACE_PIC #ifdef TRACE_PIC @@ -229,6 +231,8 @@ pic_init() TRACE(("PIC level trigger mode: 0x%08lx\n", sLevelTriggeredInterrupts)); + reserve_io_interrupt_vectors(16, 0); + // make the pic controller the current one arch_int_set_interrupt_controller(picController); } @@ -243,4 +247,6 @@ pic_disable(uint16& enabledInterrupts) // Mask off all interrupts on master and slave out8(0xff, PIC_MASTER_MASK); out8(0xff, PIC_SLAVE_MASK); + + free_io_interrupt_vectors(16, 0); } diff --git a/src/system/kernel/arch/x86/timers/x86_apic.cpp b/src/system/kernel/arch/x86/timers/x86_apic.cpp index 05636775b0..7c9b3f0133 100644 --- a/src/system/kernel/arch/x86/timers/x86_apic.cpp +++ b/src/system/kernel/arch/x86/timers/x86_apic.cpp @@ -105,6 +105,8 @@ apic_timer_init(struct kernel_args *args) return B_ERROR; sApicTicsPerSec = args->arch_args.apic_time_cv_factor; + + reserve_io_interrupt_vectors(1, 0xfb - ARCH_INTERRUPT_BASE); install_io_interrupt_handler(0xfb - ARCH_INTERRUPT_BASE, &apic_timer_interrupt, NULL, B_NO_LOCK_VECTOR); diff --git a/src/system/kernel/int.cpp b/src/system/kernel/int.cpp index 8ac9d6a90c..3818db3761 100644 --- a/src/system/kernel/int.cpp +++ b/src/system/kernel/int.cpp @@ -1,4 +1,7 @@ /* + * Copyright 2011, Michael Lotz, mmlr@mlotz.ch. + * Distributed under the terms of the MIT License. + * * Copyright 2002-2010, Axel Dörfler, axeld@pinc-software.de. * Distributed under the terms of the MIT License. * @@ -17,6 +20,7 @@ #include #include #include +#include #include #include @@ -56,6 +60,9 @@ struct io_vector { }; static struct io_vector sVectors[NUM_IO_VECTORS]; +static bool sAllocatedIOInterruptVectors[NUM_IO_VECTORS]; +static mutex sIOInterruptVectorAllocationLock + = MUTEX_INITIALIZER("io_interrupt_vector_allocation"); #if DEBUG_INTERRUPTS @@ -421,3 +428,102 @@ remove_io_interrupt_handler(long vector, interrupt_handler handler, void *data) return status; } + +/* Mark \a count contigous interrupts starting at \a startVector as in use. + This will prevent them from being allocated by others. Only use this when + the reserved range is hardwired to the given vector, otherwise allocate + vectors using allocate_io_interrupt_vectors() instead. +*/ +status_t +reserve_io_interrupt_vectors(long count, long startVector) +{ + MutexLocker locker(&sIOInterruptVectorAllocationLock); + + for (long i = 0; i < count; i++) { + if (sAllocatedIOInterruptVectors[startVector + i]) { + panic("reserved interrupt vector range %ld-%ld overlaps already " + "allocated vector %ld", startVector, startVector + count - 1, + startVector + i); + free_io_interrupt_vectors(i, startVector); + return B_BUSY; + } + + sAllocatedIOInterruptVectors[startVector + i] = true; + } + + dprintf("reserve_io_interrupt_vectors: reserved %ld vectors starting " + "from %ld\n", count, startVector); + return B_OK; +} + + +/*! Allocate \a count contigous interrupt vectors. The vectors are allocated + as available so that they do not overlap with any other reserved vector. + The first vector to be used is returned in \a startVector on success. +*/ +status_t +allocate_io_interrupt_vectors(long count, long *startVector) +{ + MutexLocker locker(&sIOInterruptVectorAllocationLock); + + long vector = 0; + bool runFound = true; + for (long i = 0; i < NUM_IO_VECTORS - (count - 1); i++) { + if (sAllocatedIOInterruptVectors[i]) + continue; + + vector = i; + runFound = true; + for (uint16 j = 1; j < count; j++) { + if (sAllocatedIOInterruptVectors[i + j]) { + runFound = false; + i += j; + break; + } + } + + if (runFound) + break; + } + + if (!runFound) { + dprintf("found no free vectors to allocate %ld io interrupts\n", count); + return B_NO_MEMORY; + } + + for (long i = 0; i < count; i++) + sAllocatedIOInterruptVectors[vector + i] = true; + + *startVector = vector; + dprintf("allocate_io_interrupt_vectors: allocated %ld vectors starting " + "from %ld\n", count, vector); + return B_OK; +} + + +/*! Free/unreserve interrupt vectors previously allocated with the + {reserve|allocate}_io_interrupt_vectors() functions. The \a count and + \a startVector can be adjusted from the allocation calls to partially free + a vector range. +*/ +void +free_io_interrupt_vectors(long count, long startVector) +{ + if (startVector + count > NUM_IO_VECTORS) { + panic("invalid start vector %ld or count %ld supplied to " + "free_io_interrupt_vectors\n", startVector, count); + } + + dprintf("free_io_interrupt_vectors: freeing %ld vectors starting " + "from %ld\n", count, startVector); + + MutexLocker locker(sIOInterruptVectorAllocationLock); + for (long i = 0; i < count; i++) { + if (!sAllocatedIOInterruptVectors[startVector + i]) { + panic("io interrupt vector %ld was not allocated\n", + startVector + i); + } + + sAllocatedIOInterruptVectors[startVector + i] = false; + } +}