1 /*
2 * Copyright (c) 2010-2014 Wind River Systems, Inc.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 /**
8 * @file
9 * @brief Interrupt support for IA-32 arch
10 *
11 * INTERNAL
12 * The _idt_base_address symbol is used to determine the base address of the IDT.
13 * (It is generated by the linker script, and doesn't correspond to an actual
14 * global variable.)
15 */
16
17 #include <zephyr/kernel.h>
18 #include <zephyr/arch/cpu.h>
19 #include <zephyr/kernel_structs.h>
20 #include <zephyr/sys/__assert.h>
21 #include <zephyr/irq.h>
22 #include <zephyr/tracing/tracing.h>
23 #include <kswap.h>
24 #include <zephyr/arch/x86/ia32/segmentation.h>
25
26 extern void z_SpuriousIntHandler(void *handler);
27 extern void z_SpuriousIntNoErrCodeHandler(void *handler);
28
29 /*
30 * Place the addresses of the spurious interrupt handlers into the intList
31 * section. The genIdt tool can then populate any unused vectors with
32 * these routines.
33 */
34 void *__attribute__((section(".spurIsr"))) MK_ISR_NAME(z_SpuriousIntHandler) =
35 &z_SpuriousIntHandler;
36 void *__attribute__((section(".spurNoErrIsr")))
37 MK_ISR_NAME(z_SpuriousIntNoErrCodeHandler) =
38 &z_SpuriousIntNoErrCodeHandler;
39
40 __pinned_func
arch_isr_direct_footer_swap(unsigned int key)41 void arch_isr_direct_footer_swap(unsigned int key)
42 {
43 (void)z_swap_irqlock(key);
44 }
45
46 #if CONFIG_X86_DYNAMIC_IRQ_STUBS > 0
47
48 /*
49 * z_interrupt_vectors_allocated[] bitfield is generated by the 'gen_idt' tool.
50 * It is initialized to identify which interrupts have been statically
51 * connected and which interrupts are available to be dynamically connected at
52 * run time, with a 1 bit indicating a free vector. The variable itself is
53 * defined in the linker file.
54 */
55 extern unsigned int z_interrupt_vectors_allocated[];
56
57 struct dyn_irq_info {
58 /** IRQ handler */
59 void (*handler)(const void *param);
60 /** Parameter to pass to the handler */
61 const void *param;
62 };
63
64 /*
65 * Instead of creating a large sparse table mapping all possible IDT vectors
66 * to dyn_irq_info, the dynamic stubs push a "stub id" onto the stack
67 * which is used by common_dynamic_handler() to fetch the appropriate
68 * information out of this much smaller table
69 */
70 __pinned_bss
71 static struct dyn_irq_info dyn_irq_list[CONFIG_X86_DYNAMIC_IRQ_STUBS];
72
73 __pinned_bss
74 static unsigned int next_irq_stub;
75
76 /* Memory address pointing to where in ROM the code for the dynamic stubs are.
77 * Linker symbol.
78 */
79 extern char z_dynamic_stubs_begin[];
80
81 /**
82 * @brief Allocate a free interrupt vector given <priority>
83 *
84 * This routine scans the z_interrupt_vectors_allocated[] array for a free vector
85 * that satisfies the specified <priority>.
86 *
87 * This routine assumes that the relationship between interrupt priority and
88 * interrupt vector is :
89 *
90 * priority = (vector / 16) - 2;
91 *
92 * Vectors 0 to 31 are reserved for CPU exceptions and do NOT fall under
93 * the priority scheme. The first vector used for priority level 0 will be 32.
94 * Each interrupt priority level contains 16 vectors.
95 *
96 * It is also assumed that the interrupt controllers are capable of managing
97 * interrupt requests on a per-vector level as opposed to a per-priority level.
98 * For example, the local APIC on Pentium4 and later processors, the in-service
99 * register (ISR) and the interrupt request register (IRR) are 256 bits wide.
100 *
101 * @return allocated interrupt vector
102 */
103
priority_to_free_vector(unsigned int requested_priority)104 static unsigned int priority_to_free_vector(unsigned int requested_priority)
105 {
106 unsigned int entry;
107 unsigned int fsb; /* first set bit in entry */
108 unsigned int search_set;
109 unsigned int vector_block;
110 unsigned int vector;
111
112 static unsigned int mask[2] = {0x0000ffffU, 0xffff0000U};
113
114 vector_block = requested_priority + 2;
115
116 __ASSERT(((vector_block << 4) + 15) <= CONFIG_IDT_NUM_VECTORS,
117 "IDT too small (%d entries) to use priority %d",
118 CONFIG_IDT_NUM_VECTORS, requested_priority);
119
120 /*
121 * Atomically allocate a vector from the
122 * z_interrupt_vectors_allocated[] array to prevent race conditions
123 * with other threads attempting to allocate an interrupt
124 * vector.
125 *
126 * Note: As z_interrupt_vectors_allocated[] is initialized by the
127 * 'gen_idt.py' tool, it is critical that this routine use the same
128 * algorithm as the 'gen_idt.py' tool for allocating interrupt vectors.
129 */
130
131 entry = vector_block >> 1;
132
133 /*
134 * The z_interrupt_vectors_allocated[] entry indexed by 'entry'
135 * is a 32-bit quantity and thus represents the vectors for a pair of
136 * priority levels. Mask out the unwanted priority level and then use
137 * find_lsb_set() to scan for an available vector of the requested
138 * priority.
139 *
140 * Note that find_lsb_set() returns bit position from 1 to 32, or 0 if
141 * the argument is zero.
142 */
143 search_set = mask[vector_block & 1] &
144 z_interrupt_vectors_allocated[entry];
145 fsb = find_lsb_set(search_set);
146
147 __ASSERT(fsb != 0U, "No remaning vectors for priority level %d",
148 requested_priority);
149
150 /*
151 * An available vector of the requested priority was found.
152 * Mark it as allocated by clearing the bit.
153 */
154 --fsb;
155 z_interrupt_vectors_allocated[entry] &= ~BIT(fsb);
156
157 /* compute vector given allocated bit within the priority level */
158 vector = (entry << 5) + fsb;
159
160 return vector;
161 }
162
163 /**
164 * @brief Get the memory address of an unused dynamic IRQ or exception stub
165 *
166 * We generate at build time a set of dynamic stubs which push
167 * a stub index onto the stack for use as an argument by
168 * common handling code.
169 *
170 * @param stub_idx Stub number to fetch the corresponding stub function
171 * @return Pointer to the stub code to install into the IDT
172 */
173 __pinned_func
get_dynamic_stub(int stub_idx)174 static void *get_dynamic_stub(int stub_idx)
175 {
176 uint32_t offset;
177
178 /*
179 * Because we want the sizes of the stubs to be consistent and minimized,
180 * stubs are grouped into blocks, each containing a push and subsequent
181 * 2-byte jump instruction to the end of the block, which then contains
182 * a larger jump instruction to common dynamic IRQ handling code
183 */
184 offset = (stub_idx * Z_DYN_STUB_SIZE) +
185 ((stub_idx / Z_DYN_STUB_PER_BLOCK) *
186 Z_DYN_STUB_LONG_JMP_EXTRA_SIZE);
187
188 return (void *)((uint32_t)&z_dynamic_stubs_begin + offset);
189 }
190
191 extern const struct pseudo_descriptor z_x86_idt;
192
idt_vector_install(int vector,void * irq_handler)193 static void idt_vector_install(int vector, void *irq_handler)
194 {
195 unsigned int key;
196
197 key = irq_lock();
198 z_init_irq_gate(&z_x86_idt.entries[vector], CODE_SEG,
199 (uint32_t)irq_handler, 0);
200 irq_unlock(key);
201 }
202
arch_irq_connect_dynamic(unsigned int irq,unsigned int priority,void (* routine)(const void * parameter),const void * parameter,uint32_t flags)203 int arch_irq_connect_dynamic(unsigned int irq, unsigned int priority,
204 void (*routine)(const void *parameter),
205 const void *parameter, uint32_t flags)
206 {
207 int vector, stub_idx, key;
208
209 key = irq_lock();
210
211 vector = priority_to_free_vector(priority);
212 /* 0 indicates not used, vectors for interrupts start at 32 */
213 __ASSERT(_irq_to_interrupt_vector[irq] == 0U,
214 "IRQ %d already configured", irq);
215 _irq_to_interrupt_vector[irq] = vector;
216 z_irq_controller_irq_config(vector, irq, flags);
217
218 stub_idx = next_irq_stub++;
219 __ASSERT(stub_idx < CONFIG_X86_DYNAMIC_IRQ_STUBS,
220 "No available interrupt stubs found");
221
222 dyn_irq_list[stub_idx].handler = routine;
223 dyn_irq_list[stub_idx].param = parameter;
224 idt_vector_install(vector, get_dynamic_stub(stub_idx));
225
226 irq_unlock(key);
227
228 return vector;
229 }
230
231 /**
232 * @brief Common dynamic IRQ handler function
233 *
234 * This gets called by the IRQ entry asm code with the stub index supplied as
235 * an argument. Look up the required information in dyn_irq_list and
236 * execute it.
237 *
238 * @param stub_idx Index into the dyn_irq_list array
239 */
240 __pinned_func
z_x86_dynamic_irq_handler(uint8_t stub_idx)241 void z_x86_dynamic_irq_handler(uint8_t stub_idx)
242 {
243 dyn_irq_list[stub_idx].handler(dyn_irq_list[stub_idx].param);
244 }
245 #endif /* CONFIG_X86_DYNAMIC_IRQ_STUBS > 0 */
246