| /* |
| * Copyright (c) 2010-2014 Wind River Systems, Inc. |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| /** |
| * @file |
| * @brief Interrupt support for IA-32 arch |
| * |
| * 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.) |
| */ |
| |
| #include <zephyr/kernel.h> |
| #include <zephyr/arch/cpu.h> |
| #include <zephyr/kernel_structs.h> |
| #include <zephyr/sys/__assert.h> |
| #include <zephyr/irq.h> |
| #include <zephyr/tracing/tracing.h> |
| #include <kswap.h> |
| #include <zephyr/arch/x86/ia32/segmentation.h> |
| |
| extern void z_SpuriousIntHandler(void *handler); |
| extern void z_SpuriousIntNoErrCodeHandler(void *handler); |
| |
| /* |
| * 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(z_SpuriousIntHandler) = |
| &z_SpuriousIntHandler; |
| void *__attribute__((section(".spurNoErrIsr"))) |
| MK_ISR_NAME(z_SpuriousIntNoErrCodeHandler) = |
| &z_SpuriousIntNoErrCodeHandler; |
| |
| __pinned_func |
| void arch_isr_direct_footer_swap(unsigned int key) |
| { |
| (void)z_swap_irqlock(key); |
| } |
| |
| #if CONFIG_X86_DYNAMIC_IRQ_STUBS > 0 |
| |
| /* |
| * z_interrupt_vectors_allocated[] bitfield 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, with a 1 bit indicating a free vector. The variable itself is |
| * defined in the linker file. |
| */ |
| extern unsigned int z_interrupt_vectors_allocated[]; |
| |
| struct dyn_irq_info { |
| /** IRQ handler */ |
| void (*handler)(const void *param); |
| /** Parameter to pass to the handler */ |
| const 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 |
| */ |
| __pinned_bss |
| static struct dyn_irq_info dyn_irq_list[CONFIG_X86_DYNAMIC_IRQ_STUBS]; |
| |
| __pinned_bss |
| static unsigned int next_irq_stub; |
| |
| /* Memory address pointing to where in ROM the code for the dynamic stubs are. |
| * Linker symbol. |
| */ |
| extern char z_dynamic_stubs_begin[]; |
| |
| /** |
| * @brief Allocate a free interrupt vector given <priority> |
| * |
| * This routine scans the z_interrupt_vectors_allocated[] array for a free vector |
| * that satisfies the specified <priority>. |
| * |
| * 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. |
| * |
| * 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 |
| */ |
| |
| static unsigned int priority_to_free_vector(unsigned int requested_priority) |
| { |
| unsigned int entry; |
| unsigned int fsb; /* first set bit in entry */ |
| unsigned int search_set; |
| unsigned int vector_block; |
| unsigned int vector; |
| |
| static unsigned int mask[2] = {0x0000ffffU, 0xffff0000U}; |
| |
| 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 |
| * z_interrupt_vectors_allocated[] array to prevent race conditions |
| * with other threads attempting to allocate an interrupt |
| * vector. |
| * |
| * Note: As z_interrupt_vectors_allocated[] is initialized by the |
| * 'gen_idt.py' tool, it is critical that this routine use the same |
| * algorithm as the 'gen_idt.py' tool for allocating interrupt vectors. |
| */ |
| |
| entry = vector_block >> 1; |
| |
| /* |
| * The z_interrupt_vectors_allocated[] entry indexed by 'entry' |
| * 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. |
| */ |
| search_set = mask[vector_block & 1] & |
| z_interrupt_vectors_allocated[entry]; |
| fsb = find_lsb_set(search_set); |
| |
| __ASSERT(fsb != 0U, "No remaning vectors for priority level %d", |
| requested_priority); |
| |
| /* |
| * An available vector of the requested priority was found. |
| * Mark it as allocated by clearing the bit. |
| */ |
| --fsb; |
| z_interrupt_vectors_allocated[entry] &= ~BIT(fsb); |
| |
| /* compute vector given allocated bit within the priority level */ |
| vector = (entry << 5) + fsb; |
| |
| return vector; |
| } |
| |
| /** |
| * @brief Get the memory address of an unused dynamic IRQ or exception stub |
| * |
| * We generate at build time a set of dynamic stubs which push |
| * a stub index onto the stack for use as an argument by |
| * common handling code. |
| * |
| * @param stub_idx Stub number to fetch the corresponding stub function |
| * @return Pointer to the stub code to install into the IDT |
| */ |
| __pinned_func |
| static void *get_dynamic_stub(int stub_idx) |
| { |
| uint32_t offset; |
| |
| /* |
| * Because we want the sizes of the stubs to be consistent and minimized, |
| * stubs are grouped into blocks, each containing a push and subsequent |
| * 2-byte jump instruction to the end of the block, which then contains |
| * a larger jump instruction to common dynamic IRQ handling code |
| */ |
| offset = (stub_idx * Z_DYN_STUB_SIZE) + |
| ((stub_idx / Z_DYN_STUB_PER_BLOCK) * |
| Z_DYN_STUB_LONG_JMP_EXTRA_SIZE); |
| |
| return (void *)((uint32_t)&z_dynamic_stubs_begin + offset); |
| } |
| |
| extern const struct pseudo_descriptor z_x86_idt; |
| |
| static void idt_vector_install(int vector, void *irq_handler) |
| { |
| unsigned int key; |
| |
| key = irq_lock(); |
| z_init_irq_gate(&z_x86_idt.entries[vector], CODE_SEG, |
| (uint32_t)irq_handler, 0); |
| irq_unlock(key); |
| } |
| |
| int arch_irq_connect_dynamic(unsigned int irq, unsigned int priority, |
| void (*routine)(const void *parameter), |
| const void *parameter, uint32_t flags) |
| { |
| int vector, stub_idx, key; |
| |
| key = irq_lock(); |
| |
| vector = priority_to_free_vector(priority); |
| /* 0 indicates not used, vectors for interrupts start at 32 */ |
| __ASSERT(_irq_to_interrupt_vector[irq] == 0U, |
| "IRQ %d already configured", irq); |
| _irq_to_interrupt_vector[irq] = vector; |
| z_irq_controller_irq_config(vector, irq, flags); |
| |
| stub_idx = next_irq_stub++; |
| __ASSERT(stub_idx < CONFIG_X86_DYNAMIC_IRQ_STUBS, |
| "No available interrupt stubs found"); |
| |
| dyn_irq_list[stub_idx].handler = routine; |
| dyn_irq_list[stub_idx].param = parameter; |
| idt_vector_install(vector, get_dynamic_stub(stub_idx)); |
| |
| irq_unlock(key); |
| |
| return vector; |
| } |
| |
| /** |
| * @brief Common dynamic IRQ handler function |
| * |
| * This gets called by the IRQ entry asm code 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 |
| */ |
| __pinned_func |
| void z_x86_dynamic_irq_handler(uint8_t stub_idx) |
| { |
| dyn_irq_list[stub_idx].handler(dyn_irq_list[stub_idx].param); |
| } |
| #endif /* CONFIG_X86_DYNAMIC_IRQ_STUBS > 0 */ |