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