1 /*
2 * Copyright (c) 2017 Intel Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/kernel.h>
8 #include <zephyr/ztest.h>
9 #include <zephyr/irq.h>
10 #include <zephyr/tc_util.h>
11 #include <zephyr/sw_isr_table.h>
12 #include <zephyr/interrupt_util.h>
13 #include <zephyr/sys/barrier.h>
14
15 extern uint32_t _irq_vector_table[];
16
17 #if defined(ARCH_IRQ_DIRECT_CONNECT) && defined(CONFIG_GEN_IRQ_VECTOR_TABLE)
18 #define HAS_DIRECT_IRQS
19 #endif
20
21 #if defined(CONFIG_RISCV)
22 #if defined(CONFIG_NRFX_CLIC)
23
24 #if defined(CONFIG_SOC_SERIES_NRF54LX) && defined(CONFIG_RISCV_CORE_NORDIC_VPR)
25 #define ISR1_OFFSET 16
26 #define ISR3_OFFSET 17
27 #define ISR5_OFFSET 18
28 #define TRIG_CHECK_SIZE 19
29 #elif defined(CONFIG_SOC_NRF54H20_CPUPPR) || defined(CONFIG_SOC_NRF54H20_CPUFLPR)
30 #define ISR1_OFFSET 14
31 #define ISR3_OFFSET 15
32 #define ISR5_OFFSET 16
33 #define TRIG_CHECK_SIZE 17
34 #else
35 #error "Target not supported"
36 #endif
37
38 #elif defined(CONFIG_RISCV_HAS_CLIC)
39 #define ISR1_OFFSET 3
40 #define ISR3_OFFSET 17
41 #define ISR5_OFFSET 18
42 #define TRIG_CHECK_SIZE 19
43 #else
44
45 /* RISC-V has very few IRQ lines which can be triggered from software */
46 #define ISR3_OFFSET 1
47
48 /* Since we have so few lines we have to share the same line for two different
49 * tests
50 */
51 #ifdef HAS_DIRECT_IRQS
52 #define ISR1_OFFSET 5
53 #else
54 #define ISR5_OFFSET 5
55 #endif
56 #define TRIG_CHECK_SIZE 6
57 #endif
58
59 #define IRQ_LINE(offset) offset
60 #if defined(CONFIG_RISCV_RESERVED_IRQ_ISR_TABLES_OFFSET)
61 #define TABLE_INDEX(offset) offset + CONFIG_RISCV_RESERVED_IRQ_ISR_TABLES_OFFSET
62 #else
63 #define TABLE_INDEX(offset) offset
64 #endif
65
66 #else
67 #define ISR1_OFFSET 0
68 #define ISR2_OFFSET 1
69 #define ISR3_OFFSET 2
70 #define ISR4_OFFSET 3
71 #define ISR5_OFFSET 4
72 #define ISR6_OFFSET 5
73
74 #if defined(CONFIG_SOC_ARC_EMSDP)
75 /* ARC EMSDP' console will use irq 108 / irq 107, will conflict
76 * with isr used here, so add a workaround
77 */
78 #define TEST_NUM_IRQS 105
79 #elif defined(CONFIG_SOC_NRF5340_CPUAPP) || defined(CONFIG_SOC_SERIES_NRF91X)
80 /* In the application core of nRF5340 and nRF9 series, not all interrupts with highest
81 * numbers are implemented. Thus, limit the number of interrupts reported to
82 * the test, so that it does not try to use some unavailable ones.
83 */
84 #define TEST_NUM_IRQS 33
85 #elif defined(CONFIG_SOC_STM32G071XX)
86 /* In STM32G071XX limit the number of interrupts reported to
87 * the test, so that it does not try to use some of the IRQs
88 * at the end of the vector table that are already used by
89 * the board.
90 */
91 #define TEST_NUM_IRQS 26
92 #elif defined(CONFIG_SOC_SERIES_NPCX7) || defined(CONFIG_SOC_SERIES_NPCX9)
93 /* Both NPCX7 and NPCX9 series use the IRQs at the end of the vector table, for
94 * example, the IRQ 60 and 61 used for Multi-Input Wake-Up Unit (MIWU) devices
95 * by default, and conflicts with ISR used for testing. Move IRQs for this
96 * test suite to solve the issue.
97 */
98 #define TEST_NUM_IRQS 44
99 #elif defined(CONFIG_SOC_LPC55S16)
100 /* IRQ 57 is reserved in the NXP LPC55S16 SoC. Thus, limit the number
101 * of interrupts reported to the test, so that it does not try to use
102 * it.
103 */
104 #define TEST_NUM_IRQS 57
105 #else
106 #define TEST_NUM_IRQS CONFIG_NUM_IRQS
107 #endif
108
109 #define TEST_IRQ_TABLE_SIZE (IRQ_TABLE_SIZE - \
110 (CONFIG_NUM_IRQS - TEST_NUM_IRQS))
111 #define IRQ_LINE(offset) (TEST_NUM_IRQS - ((offset) + 1))
112 #define TABLE_INDEX(offset) (TEST_IRQ_TABLE_SIZE - ((offset) + 1))
113 #define TRIG_CHECK_SIZE 6
114 #endif
115
116 #define ISR3_ARG 0xb01dface
117 #define ISR4_ARG 0xca55e77e
118 #define ISR5_ARG 0xf0ccac1a
119 #define ISR6_ARG 0xba5eba11
120
121 #if defined(CONFIG_RISCV_HAS_CLIC)
122 #define IRQ_FLAGS 1 /* rising edge */
123 #else
124 #define IRQ_FLAGS 0
125 #endif
126
127 static volatile int trigger_check[TRIG_CHECK_SIZE];
128
129 #ifdef HAS_DIRECT_IRQS
130 #ifdef ISR1_OFFSET
ISR_DIRECT_DECLARE(isr1)131 ISR_DIRECT_DECLARE(isr1)
132 {
133 printk("isr1 ran\n");
134 trigger_check[ISR1_OFFSET]++;
135 return 0;
136 }
137 #endif
138
139 #ifdef ISR2_OFFSET
ISR_DIRECT_DECLARE(isr2)140 ISR_DIRECT_DECLARE(isr2)
141 {
142 printk("isr2 ran\n");
143 trigger_check[ISR2_OFFSET]++;
144 return 1;
145 }
146 #endif
147 #endif
148
149 #ifdef ISR3_OFFSET
isr3(const void * param)150 void isr3(const void *param)
151 {
152 printk("%s ran with parameter %p\n", __func__, param);
153 trigger_check[ISR3_OFFSET]++;
154 }
155 #endif
156
157 #ifdef ISR4_OFFSET
isr4(const void * param)158 void isr4(const void *param)
159 {
160 printk("%s ran with parameter %p\n", __func__, param);
161 trigger_check[ISR4_OFFSET]++;
162 }
163 #endif
164
165 #ifdef ISR5_OFFSET
isr5(const void * param)166 void isr5(const void *param)
167 {
168 printk("%s ran with parameter %p\n", __func__, param);
169 trigger_check[ISR5_OFFSET]++;
170 }
171 #endif
172
173 #ifdef ISR6_OFFSET
isr6(const void * param)174 void isr6(const void *param)
175 {
176 printk("%s ran with parameter %p\n", __func__, param);
177 trigger_check[ISR6_OFFSET]++;
178 }
179 #endif
180
181 #ifndef CONFIG_CPU_CORTEX_M
182 /* Need to turn optimization off. Otherwise compiler may generate incorrect
183 * code, not knowing that trigger_irq() affects the value of trigger_check,
184 * even if declared volatile.
185 *
186 * A memory barrier does not help, we need an 'instruction barrier' but GCC
187 * doesn't support this; we need to tell the compiler not to reorder memory
188 * accesses to trigger_check around calls to trigger_irq.
189 */
190 __no_optimization
191 #endif
test_irq(int offset)192 int test_irq(int offset)
193 {
194 #ifndef NO_TRIGGER_FROM_SW
195 TC_PRINT("triggering irq %d\n", IRQ_LINE(offset));
196 trigger_irq(IRQ_LINE(offset));
197 #ifdef CONFIG_CPU_CORTEX_M
198 barrier_dsync_fence_full();
199 barrier_isync_fence_full();
200 #endif
201 if (trigger_check[offset] != 1) {
202 TC_PRINT("interrupt %d didn't run once, ran %d times\n",
203 IRQ_LINE(offset),
204 trigger_check[offset]);
205 return -1;
206 }
207 #else
208 /* This arch doesn't support triggering interrupts from software */
209 ARG_UNUSED(offset);
210 #endif
211 return 0;
212 }
213
214
215
216 #ifdef HAS_DIRECT_IRQS
check_vector(void * isr,int offset)217 static int check_vector(void *isr, int offset)
218 {
219 /*
220 * The problem with an IRQ table where the entries are jump opcodes is that it
221 * the destination address is encoded in the opcode and strictly depending on
222 * the address of the instruction itself (and very much architecture
223 * dependent). For the sake of simplicity just skip the checks.
224 */
225 #ifndef CONFIG_IRQ_VECTOR_TABLE_JUMP_BY_CODE
226 TC_PRINT("Checking _irq_vector_table entry %d for irq %d\n",
227 TABLE_INDEX(offset), IRQ_LINE(offset));
228
229 if (_irq_vector_table[TABLE_INDEX(offset)] != (uint32_t)isr) {
230 TC_PRINT("bad entry %d in vector table\n", TABLE_INDEX(offset));
231 return -1;
232 }
233 #endif /* !CONFIG_IRQ_VECTOR_TABLE_JUMP_BY_CODE */
234
235 if (test_irq(offset)) {
236 return -1;
237 }
238
239 return 0;
240 }
241 #endif
242
243 #ifdef CONFIG_GEN_SW_ISR_TABLE
check_sw_isr(void * isr,uintptr_t arg,int offset)244 static int check_sw_isr(void *isr, uintptr_t arg, int offset)
245 {
246 struct _isr_table_entry *e = &_sw_isr_table[TABLE_INDEX(offset)];
247
248 TC_PRINT("Checking _sw_isr_table entry %d for irq %d\n",
249 TABLE_INDEX(offset), IRQ_LINE(offset));
250
251 if (e->arg != (void *)arg) {
252 TC_PRINT("bad argument in SW isr table\n");
253 TC_PRINT("expected %p got %p\n", (void *)arg, e->arg);
254 return -1;
255 }
256 if (e->isr != isr) {
257 TC_PRINT("Bad ISR in SW isr table\n");
258 TC_PRINT("expected %p got %p\n", (void *)isr, e->isr);
259 return -1;
260 }
261 #if defined(CONFIG_GEN_IRQ_VECTOR_TABLE) && !defined(CONFIG_IRQ_VECTOR_TABLE_JUMP_BY_CODE)
262 void *v = (void *)_irq_vector_table[TABLE_INDEX(offset)];
263 if (v != _isr_wrapper) {
264 TC_PRINT("Vector does not point to _isr_wrapper\n");
265 TC_PRINT("expected %p got %p\n", _isr_wrapper, v);
266 return -1;
267 }
268 #endif /* CONFIG_GEN_IRQ_VECTOR_TABLE && !CONFIG_IRQ_VECTOR_TABLE_JUMP_BY_CODE */
269
270 if (test_irq(offset)) {
271 return -1;
272 }
273 return 0;
274 }
275 #endif
276
277 /**
278 * @ingroup kernel_interrupt_tests
279 * @brief test to validate direct interrupt
280 *
281 * @details initialize two direct interrupt handler using IRQ_DIRECT_CONNECT
282 * api at build time. For ‘direct’ interrupts, address of handler function will
283 * be placed in the irq vector table. And each entry contains the pointer to
284 * isr and the corresponding parameters.
285 *
286 * At the end according to architecture, we manually trigger the interrupt.
287 * And all irq handler should get called.
288 *
289 * @see IRQ_DIRECT_CONNECT(), irq_enable()
290 *
291 */
ZTEST(gen_isr_table,test_build_time_direct_interrupt)292 ZTEST(gen_isr_table, test_build_time_direct_interrupt)
293 {
294 #ifndef HAS_DIRECT_IRQS
295 ztest_test_skip();
296 #else
297
298 #ifdef ISR1_OFFSET
299 IRQ_DIRECT_CONNECT(IRQ_LINE(ISR1_OFFSET), 0, isr1, IRQ_FLAGS);
300 irq_enable(IRQ_LINE(ISR1_OFFSET));
301 TC_PRINT("isr1 isr=%p irq=%d\n", isr1, IRQ_LINE(ISR1_OFFSET));
302 zassert_ok(check_vector(isr1, ISR1_OFFSET),
303 "check direct interrpt isr1 failed");
304 #endif
305
306 #ifdef ISR2_OFFSET
307 IRQ_DIRECT_CONNECT(IRQ_LINE(ISR2_OFFSET), 0, isr2, IRQ_FLAGS);
308 irq_enable(IRQ_LINE(ISR2_OFFSET));
309 TC_PRINT("isr2 isr=%p irq=%d\n", isr2, IRQ_LINE(ISR2_OFFSET));
310
311
312 zassert_ok(check_vector(isr2, ISR2_OFFSET),
313 "check direct interrpt isr2 failed");
314 #endif
315 #endif
316 }
317
318 /**
319 * @ingroup kernel_interrupt_tests
320 * @brief test to validate gen_isr_table and interrupt
321 *
322 * @details initialize two normal interrupt handler using IRQ_CONNECT api at
323 * build time. For 'regular' interrupts, the address of the common software isr
324 * table is placed in the irq vector table, and software ISR table is an array
325 * of struct _isr_table_entry. And each entry contains the pointer to isr and
326 * the corresponding parameters.
327 *
328 * At the end according to architecture, we manually trigger the interrupt.
329 * And all irq handler should get called.
330 *
331 * @see IRQ_CONNECT(), irq_enable()
332 *
333 */
ZTEST(gen_isr_table,test_build_time_interrupt)334 ZTEST(gen_isr_table, test_build_time_interrupt)
335 {
336 #ifndef CONFIG_GEN_SW_ISR_TABLE
337 ztest_test_skip();
338 #else
339 TC_PRINT("_sw_isr_table at location %p\n", _sw_isr_table);
340
341 #ifdef ISR3_OFFSET
342 IRQ_CONNECT(IRQ_LINE(ISR3_OFFSET), 1, isr3, ISR3_ARG, IRQ_FLAGS);
343 irq_enable(IRQ_LINE(ISR3_OFFSET));
344 TC_PRINT("isr3 isr=%p irq=%d param=%p\n", isr3, IRQ_LINE(ISR3_OFFSET),
345 (void *)ISR3_ARG);
346
347 zassert_ok(check_sw_isr(isr3, ISR3_ARG, ISR3_OFFSET),
348 "check interrupt isr3 failed");
349 #endif
350
351 #ifdef ISR4_OFFSET
352 IRQ_CONNECT(IRQ_LINE(ISR4_OFFSET), 1, isr4, ISR4_ARG, IRQ_FLAGS);
353 irq_enable(IRQ_LINE(ISR4_OFFSET));
354 TC_PRINT("isr4 isr=%p irq=%d param=%p\n", isr4, IRQ_LINE(ISR4_OFFSET),
355 (void *)ISR4_ARG);
356
357 zassert_ok(check_sw_isr(isr4, ISR4_ARG, ISR4_OFFSET),
358 "check interrupt isr4 failed");
359 #endif
360 #endif
361 }
362
363 /**
364 * @ingroup kernel_interrupt_tests
365 * @brief test to validate gen_isr_table and dynamic interrupt
366 *
367 * @details initialize two dynamic interrupt handler using irq_connect_dynamic
368 * api at run time. For dynamic interrupts, the address of the common software
369 * isr table is also placed in the irq vector table. Software ISR table is an
370 * array of struct _isr_table_entry. And each entry contains the pointer to isr
371 * and the corresponding parameters.
372 *
373 * At the end according to architecture, we manually trigger the interrupt.
374 * And all irq handler should get called.
375 *
376 * @see irq_connect_dynamic(), irq_enable()
377 *
378 */
ZTEST(gen_isr_table,test_run_time_interrupt)379 ZTEST(gen_isr_table, test_run_time_interrupt)
380 {
381
382 #ifndef CONFIG_GEN_SW_ISR_TABLE
383 ztest_test_skip();
384 #else
385
386 #ifdef ISR5_OFFSET
387 irq_connect_dynamic(IRQ_LINE(ISR5_OFFSET), 1, isr5,
388 (const void *)ISR5_ARG, IRQ_FLAGS);
389 irq_enable(IRQ_LINE(ISR5_OFFSET));
390 TC_PRINT("isr5 isr=%p irq=%d param=%p\n", isr5, IRQ_LINE(ISR5_OFFSET),
391 (void *)ISR5_ARG);
392 zassert_ok(check_sw_isr(isr5, ISR5_ARG, ISR5_OFFSET),
393 "test dynamic interrupt isr5 failed");
394 #endif
395
396 #ifdef ISR6_OFFSET
397 irq_connect_dynamic(IRQ_LINE(ISR6_OFFSET), 1, isr6,
398 (const void *)ISR6_ARG, IRQ_FLAGS);
399 irq_enable(IRQ_LINE(ISR6_OFFSET));
400 TC_PRINT("isr6 isr=%p irq=%d param=%p\n", isr6, IRQ_LINE(ISR6_OFFSET),
401 (void *)ISR6_ARG);
402
403 zassert_ok(check_sw_isr(isr6, ISR6_ARG, ISR6_OFFSET),
404 "check dynamic interrupt isr6 failed");
405 #endif
406 #endif
407 }
408
gen_isr_table_setup(void)409 static void *gen_isr_table_setup(void)
410 {
411 TC_PRINT("IRQ configuration (total lines %d):\n", CONFIG_NUM_IRQS);
412
413 return NULL;
414 }
415
416 ZTEST_SUITE(gen_isr_table, NULL, gen_isr_table_setup, NULL, NULL, NULL);
417