| /* |
| * Copyright (c) 2010-2014 Wind River Systems, Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /** |
| * @file |
| * @brief Interrupt management support for IA-32 arch |
| * |
| * This module provides routines to manage asynchronous interrupts |
| * on the IA-32 architecture. |
| * |
| * INTERNAL |
| * The _idt_base_address symbol is used to determine the base address of the IDT. |
| * (It is generated by the linker script, and doesn't correspond to an actual |
| * global variable.) |
| * |
| * Dynamic interrupts are handled by a set of dynamic interrupt stubs defined |
| * in intstub.S. Each one pushes a "stub id" onto the stack and calls |
| * common_dynamic_handler, which uses the stub id to pull the details |
| * about what to do with the dynamic IRQ out of the dyn_irq_list array. |
| * This array is populated by calls to irq_connect_dynamic(), which also |
| * installs the associated dynamic stub in the IDT. |
| */ |
| |
| |
| #include <nanokernel.h> |
| #include <arch/cpu.h> |
| #include <nano_private.h> |
| #include <misc/__assert.h> |
| #include <idtEnt.h> |
| #include <misc/printk.h> |
| #include <irq.h> |
| |
| extern void _SpuriousIntHandler(void *); |
| extern void _SpuriousIntNoErrCodeHandler(void *); |
| |
| /* |
| * These 'dummy' variables are used in nanoArchInit() to force the inclusion of |
| * the spurious interrupt handlers. They *must* be declared in a module other |
| * than the one they are used in to get around garbage collection issues and |
| * warnings issued some compilers that they aren't used. Therefore care must |
| * be taken if they are to be moved. See nano_private.h for more information. |
| */ |
| void *_dummy_spurious_interrupt; |
| void *_dummy_exception_vector_stub; |
| |
| /* |
| * Place the addresses of the spurious interrupt handlers into the intList |
| * section. The genIdt tool can then populate any unused vectors with |
| * these routines. |
| */ |
| void *__attribute__((section(".spurIsr"))) MK_ISR_NAME(_SpuriousIntHandler) = |
| &_SpuriousIntHandler; |
| void *__attribute__((section(".spurNoErrIsr"))) |
| MK_ISR_NAME(_SpuriousIntNoErrCodeHandler) = |
| &_SpuriousIntNoErrCodeHandler; |
| |
| #if CONFIG_DEBUG_IRQS |
| /** |
| * |
| * @brief Dump out the IDT for debugging purposes |
| * |
| * The IDT has a strange structure which confounds direct examination in |
| * a debugger. This function will print out its contents in human-readable |
| * form. If unused, gc-sections will strip this function from the binary. |
| */ |
| void irq_debug_dump_idt(void) |
| { |
| int i; |
| IDT_ENTRY *idt = (IDT_ENTRY *)_idt_base_address; |
| |
| printk("Installed interrupt handlers (spurious omitted):\n"); |
| for (i = 0; i < CONFIG_IDT_NUM_VECTORS; i++) { |
| uint32_t addr = idt[i].offset_low + (idt[i].offset_high << 16); |
| |
| if ((void *)addr == &_SpuriousIntNoErrCodeHandler || |
| (void *)addr == &_SpuriousIntHandler) { |
| continue; |
| } |
| |
| printk("IDT 0x%x: CS=0x%x ADDR=0x%x DPL=0x%x ", |
| i, idt[i].segment_selector, addr, |
| idt[i].dpl); |
| if (idt[i].present) { |
| printk("present "); |
| } |
| |
| if (idt[i].gate_size) { |
| printk("32-bit "); |
| } else { |
| printk("16-bit "); |
| } |
| |
| switch (idt[i].type) { |
| case 0x5: |
| printk("task gate"); |
| break; |
| case 0x6: |
| printk("IRQ gate"); |
| break; |
| case 0x7: |
| printk("trap gate"); |
| break; |
| default: |
| printk("Garbage type (0x%x)", idt[i].type); |
| break; |
| } |
| printk("\n"); |
| } |
| } |
| #endif |
| |
| /** |
| * |
| * @brief Connect a routine to an interrupt vector |
| * |
| * @param vector interrupt vector: 0 to 255 on IA-32 |
| * @param routine a function pointer to the interrupt routine |
| * @param dpl priv level for interrupt-gate descriptor |
| * |
| * This routine "connects" the specified <routine> to the specified interrupt |
| * <vector>. On the IA-32 architecture, an interrupt vector is a value from |
| * 0 to 255. This routine merely fills in the appropriate interrupt |
| * descriptor table (IDT) with an interrupt-gate descriptor such that <routine> |
| * is invoked when interrupt <vector> is asserted. The <dpl> argument specifies |
| * the privilege level for the interrupt-gate descriptor; (hardware) interrupts |
| * and exceptions should specify a level of 0, whereas handlers for user-mode |
| * software generated interrupts should specify 3. |
| * |
| * @return N/A |
| * |
| * INTERNAL |
| * Unlike nanoCpuExcConnect() and irq_connect_dynamic(), the _IntVecSet() routine |
| * is a very basic API that simply updates the appropriate entry in Interrupt |
| * Descriptor Table (IDT) such that the specified routine is invoked when the |
| * specified interrupt vector is asserted. |
| * |
| */ |
| |
| void _IntVecSet(unsigned int vector, void (*routine)(void *), unsigned int dpl) |
| { |
| unsigned long long *pIdtEntry; |
| unsigned int key; |
| |
| /* |
| * The <vector> parameter must be less than the value of the |
| * CONFIG_IDT_NUM_VECTORS configuration parameter, however, |
| * explicit validation will not be performed in this primitive. |
| */ |
| |
| pIdtEntry = (unsigned long long *)(_idt_base_address + (vector << 3)); |
| |
| /* |
| * Lock interrupts to protect the IDT entry to which _IdtEntryCreate() |
| * will write. They must be locked here because the _IdtEntryCreate() |
| * code is shared with the 'gen_idt' host tool. |
| */ |
| |
| key = irq_lock(); |
| _IdtEntCreate(pIdtEntry, routine, dpl); |
| |
| #ifdef CONFIG_MVIC |
| /* Some nonstandard interrupt controllers may be doing some IDT |
| * caching for performance reasons and need the IDT reloaded if |
| * any changes are made to it |
| */ |
| __asm__ volatile ("lidt _Idt"); |
| #endif |
| |
| irq_unlock(key); |
| } |
| |
| #if ALL_DYN_IRQ_STUBS > 0 |
| /* |
| * _interrupt_vectors_allocated[] is generated by the 'gen_idt' tool. It is |
| * initialized to identify which interrupts have been statically connected |
| * and which interrupts are available to be dynamically connected at run time. |
| * The variable itself is defined in the linker file. |
| */ |
| extern unsigned int _interrupt_vectors_allocated[]; |
| |
| /* |
| * Guard against situations when ALL_DYN_IRQ_STUBS is left equal to 0, |
| * but irq_connect_dynamic is still used, which causes system failure. |
| * If ALL_DYN_IRQ_STUBS is left 0, but irq_connect_dynamic is used, linker |
| * generates an error |
| */ |
| struct dyn_irq_info { |
| /** IRQ handler */ |
| void (*handler)(void *param); |
| /** Parameter to pass to the handler */ |
| void *param; |
| }; |
| |
| /* |
| * Instead of creating a large sparse table mapping all possible IDT vectors |
| * to dyn_irq_info, the dynamic stubs push a "stub id" onto the stack |
| * which is used by common_dynamic_handler() to fetch the appropriate |
| * information out of this much smaller table |
| */ |
| static struct dyn_irq_info dyn_irq_list[ALL_DYN_IRQ_STUBS]; |
| static unsigned int next_irq_stub; |
| |
| /* Memory address pointing to where in ROM the code for the dynamic stubs are */ |
| extern void *_DynIntStubsBegin; |
| |
| /** |
| * |
| * @brief Connect a C routine to a hardware interrupt |
| * |
| * @param irq virtualized IRQ to connect to |
| * @param priority requested priority of interrupt |
| * @param routine the C interrupt handler |
| * @param parameter parameter passed to C routine |
| * @param flags IRQ flags |
| * |
| * This routine connects an interrupt service routine (ISR) coded in C to |
| * the specified hardware <irq>. An interrupt vector will be allocated to |
| * satisfy the specified <priority>. |
| * |
| * The specified <irq> represents a virtualized IRQ, i.e. it does not |
| * necessarily represent a specific IRQ line on a given interrupt controller |
| * device. The platform presents a virtualized set of IRQs from 0 to N, where |
| * N is the total number of IRQs supported by all the interrupt controller |
| * devices on the board. See the platform's documentation for the mapping of |
| * virtualized IRQ to physical IRQ. |
| * |
| * When the device asserts an interrupt on the specified <irq>, a switch to |
| * the interrupt stack is performed (if not already executing on the interrupt |
| * stack), followed by saving the integer (i.e. non-floating point) thread of |
| * the currently executing task, fiber, or ISR. The ISR specified by <routine> |
| * will then be invoked with the single <parameter>. When the ISR returns, a |
| * context switch may occur. |
| * |
| * On some platforms <flags> parameter needs to be specified to indicate if |
| * the irq is triggered by low or high level or by rising or falling edge. |
| * |
| * The routine searches for the first available element in the dynamic_stubs |
| * array and uses it for the stub. |
| * |
| * @return the allocated interrupt vector |
| * |
| * WARNINGS |
| * This routine does not perform range checking on the requested <priority> |
| * and thus, depending on the underlying interrupt controller, may result |
| * in the assignment of an interrupt vector located in the reserved range of |
| * the processor. |
| * |
| * INTERNAL |
| * For debug kernels, this routine shall return -1 when there are no |
| * vectors remaining in the specified <priority> level. |
| */ |
| |
| int _arch_irq_connect_dynamic(unsigned int irq, unsigned int priority, |
| void (*routine)(void *parameter), void *parameter, |
| uint32_t flags) |
| { |
| int vector; |
| int stub_idx; |
| |
| /* |
| * Invoke the interrupt controller routine _SysIntVecAlloc() which will: |
| * a) allocate a vector satisfying the requested priority, |
| * b) create a new entry in the dynamic stub array |
| * c) program the underlying interrupt controller device such that |
| * when <irq> is asserted, the allocated interrupt vector will be |
| * presented to the CPU. |
| * |
| * The _SysIntVecAlloc() routine will use the "utility" routine |
| * _IntVecAlloc() provided in this module to scan the |
| * _interrupt_vectors_allocated[] array for a suitable vector. |
| */ |
| |
| vector = _SysIntVecAlloc(irq, priority, flags); |
| __ASSERT(vector != -1, "Unable to request a vector for irq %d with priority %d", |
| irq, priority); |
| |
| stub_idx = _stub_alloc(&next_irq_stub, ALL_DYN_IRQ_STUBS); |
| __ASSERT(stub_idx != -1, "No available interrupt stubs found"); |
| |
| dyn_irq_list[stub_idx].handler = routine; |
| dyn_irq_list[stub_idx].param = parameter; |
| _IntVecSet(vector, _get_dynamic_stub(stub_idx, &_DynIntStubsBegin), 0); |
| |
| return vector; |
| } |
| |
| |
| /** |
| * @brief Common dynamic IRQ handler function |
| * |
| * This gets called by _DynStubCommon with the stub index supplied as |
| * an argument. Look up the required information in dyn_irq_list and |
| * execute it. |
| * |
| * @param stub_idx Index into the dyn_irq_list array |
| */ |
| void _common_dynamic_irq_handler(uint32_t stub_idx) |
| { |
| dyn_irq_list[stub_idx].handler(dyn_irq_list[stub_idx].param); |
| } |
| |
| /** |
| * @internal |
| * |
| * @brief Set the handler in an already connected stub |
| * |
| * This routine is used to modify an already fully constructed interrupt stub |
| * to specify a new <routine> and/or <parameter>. This only works with |
| * dynamic interrupt stubs. |
| */ |
| void _irq_handler_set(unsigned int vector, void (*routine)(void *parameter), |
| void *parameter) |
| { |
| int key; |
| uint8_t stub_idx; |
| |
| /* |
| * Disable IRQs so we can ensure that the associated interrupt |
| * doesn't run in an inconsistent state while we're doing this |
| */ |
| key = irq_lock(); |
| |
| stub_idx = _stub_idx_from_vector(vector); |
| |
| __ASSERT(stub_idx < ALL_DYN_IRQ_STUBS, "Bad stub index"); |
| |
| dyn_irq_list[stub_idx].handler = routine; |
| dyn_irq_list[stub_idx].param = parameter; |
| irq_unlock(key); |
| } |
| |
| |
| /** |
| * |
| * @brief Allocate a free interrupt vector given <priority> |
| * |
| * This routine scans the _interrupt_vectors_allocated[] array for a free vector |
| * that satisfies the specified <priority>. It is a utility function for use |
| * only by the interrupt controller's _SysIntVecAlloc() routine. |
| * |
| * This routine assumes that the relationship between interrupt priority and |
| * interrupt vector is : |
| * |
| * priority = (vector / 16) - 2; |
| * |
| * Vectors 0 to 31 are reserved for CPU exceptions and do NOT fall under |
| * the priority scheme. The first vector used for priority level 0 will be 32. |
| * |
| * Each interrupt priority level contains 16 vectors, and the prioritization |
| * of interrupts within a priority level is determined by the vector number; |
| * the higher the vector number, the higher the priority within that priority |
| * level. |
| * |
| * It is also assumed that the interrupt controllers are capable of managing |
| * interrupt requests on a per-vector level as opposed to a per-priority level. |
| * For example, the local APIC on Pentium4 and later processors, the in-service |
| * register (ISR) and the interrupt request register (IRR) are 256 bits wide. |
| * |
| * @return allocated interrupt vector |
| * |
| * INTERNAL |
| * For debug kernels, this routine shall return -1 when there are no |
| * vectors remaining in the specified <priority> level. |
| */ |
| |
| int _IntVecAlloc(unsigned int requested_priority) |
| { |
| unsigned int key; |
| unsigned int entryToScan; |
| unsigned int fsb; /* first set bit in entry */ |
| unsigned int search_set; |
| int vector_block; |
| int vector; |
| |
| static unsigned int mask[2] = {0x0000ffff, 0xffff0000}; |
| |
| vector_block = requested_priority + 2; |
| |
| __ASSERT(((vector_block << 4) + 15) <= CONFIG_IDT_NUM_VECTORS, |
| "IDT too small (%d entries) to use priority %d", |
| CONFIG_IDT_NUM_VECTORS, requested_priority); |
| |
| /* |
| * Atomically allocate a vector from the _interrupt_vectors_allocated[] |
| * array to prevent race conditions with other tasks/fibers attempting |
| * to allocate an interrupt vector. |
| * |
| * Note: As _interrupt_vectors_allocated[] is initialized by the 'gen_idt' |
| * tool, it is critical that this routine use the same algorithm as the |
| * 'gen_idt' tool for allocating interrupt vectors. |
| */ |
| |
| entryToScan = vector_block >> 1; |
| |
| /* |
| * The _interrupt_vectors_allocated[] entry indexed by 'entryToScan' is a |
| * 32-bit quantity and thus represents the vectors for a pair of priority |
| * levels. Mask out the unwanted priority level and then use find_lsb_set() |
| * to scan for an available vector of the requested priority. |
| * |
| * Note that find_lsb_set() returns bit position from 1 to 32, |
| * or 0 if the argument is zero. |
| */ |
| |
| key = irq_lock(); |
| |
| search_set = mask[vector_block & 1] & _interrupt_vectors_allocated[entryToScan]; |
| fsb = find_lsb_set(search_set); |
| |
| __ASSERT(fsb != 0, "No remaning vectors for priority level %d", |
| requested_priority); |
| |
| /* |
| * An available vector of the requested priority was found. |
| * Mark it as allocated. |
| */ |
| |
| --fsb; |
| _interrupt_vectors_allocated[entryToScan] &= ~(1 << fsb); |
| |
| irq_unlock(key); |
| |
| /* compute vector given allocated bit within the priority level */ |
| |
| vector = (entryToScan << 5) + fsb; |
| |
| return vector; |
| } |
| |
| /** |
| * |
| * @brief Mark interrupt vector as allocated |
| * |
| * This routine is used to "reserve" an interrupt vector that is allocated |
| * or assigned by any means other than _IntVecAllocate(). This marks the vector |
| * as allocated so that any future invocations of _IntVecAllocate() will not |
| * return that vector. |
| * |
| * @return N/A |
| * |
| */ |
| |
| void _IntVecMarkAllocated(unsigned int vector) |
| { |
| unsigned int entryToSet = vector / 32; |
| unsigned int bitToSet = vector % 32; |
| unsigned int imask; |
| |
| imask = irq_lock(); |
| _interrupt_vectors_allocated[entryToSet] &= ~(1 << bitToSet); |
| irq_unlock(imask); |
| } |
| |
| /** |
| * |
| * @brief Mark interrupt vector as free |
| * |
| * This routine is used to "free" an interrupt vector that is allocated |
| * or assigned using _IntVecAllocate() or _IntVecMarkAllocated(). This marks the |
| * vector as available so that any future allocations can return that vector. |
| * |
| */ |
| |
| void _IntVecMarkFree(unsigned int vector) |
| { |
| unsigned int entryToSet = vector / 32; |
| unsigned int bitToSet = vector % 32; |
| unsigned int imask; |
| |
| imask = irq_lock(); |
| _interrupt_vectors_allocated[entryToSet] |= (1 << bitToSet); |
| irq_unlock(imask); |
| } |
| #endif /* ALL_DYN_IRQ_STUBS > 0 */ |