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/irq_multilevel.h>
11 #include <zephyr/tc_util.h>
12 #include <zephyr/sw_isr_table.h>
13 #include <zephyr/interrupt_util.h>
14 #include <zephyr/sys/barrier.h>
15 
16 extern uint32_t _irq_vector_table[];
17 
18 #if defined(ARCH_IRQ_DIRECT_CONNECT) && defined(CONFIG_GEN_IRQ_VECTOR_TABLE)
19 #define HAS_DIRECT_IRQS
20 #endif
21 
22 #if defined(CONFIG_RISCV)
23 #if defined(CONFIG_NRFX_CLIC)
24 #define ISR1_OFFSET	15
25 #define ISR3_OFFSET	16
26 #define ISR5_OFFSET	17
27 #define TRIG_CHECK_SIZE	18
28 #elif defined(CONFIG_RISCV_HAS_CLIC)
29 #define ISR1_OFFSET	3
30 #define ISR3_OFFSET	17
31 #define ISR5_OFFSET	18
32 #define TRIG_CHECK_SIZE	19
33 #else
34 
35 /* RISC-V has very few IRQ lines which can be triggered from software */
36 #define ISR3_OFFSET	1
37 
38 /* Since we have so few lines we have to share the same line for two different
39  * tests
40  */
41 #ifdef HAS_DIRECT_IRQS
42 #define ISR1_OFFSET	5
43 #else
44 #define ISR5_OFFSET	5
45 #endif
46 #define TRIG_CHECK_SIZE	6
47 #endif
48 
49 #define IRQ_LINE(offset)        offset
50 #if defined(CONFIG_RISCV_RESERVED_IRQ_ISR_TABLES_OFFSET)
51 #define TABLE_INDEX(offset)     offset + CONFIG_RISCV_RESERVED_IRQ_ISR_TABLES_OFFSET
52 #else
53 #define TABLE_INDEX(offset)     offset
54 #endif
55 
56 #else
57 #define ISR1_OFFSET	0
58 #define ISR2_OFFSET	1
59 #define ISR3_OFFSET	2
60 #define ISR4_OFFSET	3
61 #define ISR5_OFFSET	4
62 #define ISR6_OFFSET	5
63 
64 #if defined(CONFIG_SOC_ARC_EMSDP)
65 /* ARC EMSDP' console will use irq 108 / irq 107, will conflict
66  * with isr used here, so add a workaround
67  */
68 #define TEST_NUM_IRQS	105
69 #elif defined(CONFIG_SOC_NRF5340_CPUAPP) || defined(CONFIG_SOC_NRF9160)
70 /* In nRF9160 and application core in nRF5340, not all interrupts with highest
71  * numbers are implemented. Thus, limit the number of interrupts reported to
72  * the test, so that it does not try to use some unavailable ones.
73  */
74 #define TEST_NUM_IRQS	33
75 #elif defined(CONFIG_SOC_STM32G071XX)
76 /* In STM32G071XX limit the number of interrupts reported to
77  * the test, so that it does not try to use some of the IRQs
78  * at the end of the vector table that are already used by
79  * the board.
80  */
81 #define TEST_NUM_IRQS	26
82 #elif defined(CONFIG_SOC_SERIES_NPCX7) || defined(CONFIG_SOC_SERIES_NPCX9)
83 /* Both NPCX7 and NPCX9 series use the IRQs at the end of the vector table, for
84  * example, the IRQ 60 and 61 used for Multi-Input Wake-Up Unit (MIWU) devices
85  * by default, and conflicts with ISR used for testing. Move IRQs for this
86  * test suite to solve the issue.
87  */
88 #define TEST_NUM_IRQS	44
89 #elif defined(CONFIG_SOC_LPC55S16)
90 /* IRQ 57 is reserved in the NXP LPC55S16 SoC. Thus, limit the number
91  * of interrupts reported to the test, so that it does not try to use
92  * it.
93  */
94 #define TEST_NUM_IRQS	57
95 #else
96 #define TEST_NUM_IRQS	CONFIG_NUM_IRQS
97 #endif
98 
99 #define TEST_IRQ_TABLE_SIZE	(IRQ_TABLE_SIZE - \
100 				 (CONFIG_NUM_IRQS - TEST_NUM_IRQS))
101 #define IRQ_LINE(offset)	(TEST_NUM_IRQS - ((offset) + 1))
102 #define TABLE_INDEX(offset)	(TEST_IRQ_TABLE_SIZE - ((offset) + 1))
103 #define TRIG_CHECK_SIZE		6
104 #endif
105 
106 #define ISR3_ARG	0xb01dface
107 #define ISR4_ARG	0xca55e77e
108 #define ISR5_ARG	0xf0ccac1a
109 #define ISR6_ARG	0xba5eba11
110 
111 #if defined(CONFIG_RISCV_HAS_CLIC)
112 #define IRQ_FLAGS 1 /* rising edge */
113 #else
114 #define IRQ_FLAGS 0
115 #endif
116 
117 static volatile int trigger_check[TRIG_CHECK_SIZE];
118 
119 #ifdef HAS_DIRECT_IRQS
120 #ifdef ISR1_OFFSET
ISR_DIRECT_DECLARE(isr1)121 ISR_DIRECT_DECLARE(isr1)
122 {
123 	printk("isr1 ran\n");
124 	trigger_check[ISR1_OFFSET]++;
125 	return 0;
126 }
127 #endif
128 
129 #ifdef ISR2_OFFSET
ISR_DIRECT_DECLARE(isr2)130 ISR_DIRECT_DECLARE(isr2)
131 {
132 	printk("isr2 ran\n");
133 	trigger_check[ISR2_OFFSET]++;
134 	return 1;
135 }
136 #endif
137 #endif
138 
139 #ifdef ISR3_OFFSET
isr3(const void * param)140 void isr3(const void *param)
141 {
142 	printk("%s ran with parameter %p\n", __func__, param);
143 	trigger_check[ISR3_OFFSET]++;
144 }
145 #endif
146 
147 #ifdef ISR4_OFFSET
isr4(const void * param)148 void isr4(const void *param)
149 {
150 	printk("%s ran with parameter %p\n", __func__, param);
151 	trigger_check[ISR4_OFFSET]++;
152 }
153 #endif
154 
155 #ifdef ISR5_OFFSET
isr5(const void * param)156 void isr5(const void *param)
157 {
158 	printk("%s ran with parameter %p\n", __func__, param);
159 	trigger_check[ISR5_OFFSET]++;
160 }
161 #endif
162 
163 #ifdef ISR6_OFFSET
isr6(const void * param)164 void isr6(const void *param)
165 {
166 	printk("%s ran with parameter %p\n", __func__, param);
167 	trigger_check[ISR6_OFFSET]++;
168 }
169 #endif
170 
171 #ifndef CONFIG_CPU_CORTEX_M
172 /* Need to turn optimization off. Otherwise compiler may generate incorrect
173  * code, not knowing that trigger_irq() affects the value of trigger_check,
174  * even if declared volatile.
175  *
176  * A memory barrier does not help, we need an 'instruction barrier' but GCC
177  * doesn't support this; we need to tell the compiler not to reorder memory
178  * accesses to trigger_check around calls to trigger_irq.
179  */
180 __no_optimization
181 #endif
test_irq(int offset)182 int test_irq(int offset)
183 {
184 #ifndef NO_TRIGGER_FROM_SW
185 	TC_PRINT("triggering irq %d\n", IRQ_LINE(offset));
186 	trigger_irq(IRQ_LINE(offset));
187 #ifdef CONFIG_CPU_CORTEX_M
188 	barrier_dsync_fence_full();
189 	barrier_isync_fence_full();
190 #endif
191 	if (trigger_check[offset] != 1) {
192 		TC_PRINT("interrupt %d didn't run once, ran %d times\n",
193 			 IRQ_LINE(offset),
194 			 trigger_check[offset]);
195 		return -1;
196 	}
197 #else
198 	/* This arch doesn't support triggering interrupts from software */
199 	ARG_UNUSED(offset);
200 #endif
201 	return 0;
202 }
203 
204 
205 
206 #ifdef HAS_DIRECT_IRQS
check_vector(void * isr,int offset)207 static int check_vector(void *isr, int offset)
208 {
209 /*
210  * The problem with an IRQ table where the entries are jump opcodes is that it
211  * the destination address is encoded in the opcode and strictly depending on
212  * the address of the instruction itself (and very much architecture
213  * dependent). For the sake of simplicity just skip the checks.
214  */
215 #ifndef CONFIG_IRQ_VECTOR_TABLE_JUMP_BY_CODE
216 	TC_PRINT("Checking _irq_vector_table entry %d for irq %d\n",
217 		 TABLE_INDEX(offset), IRQ_LINE(offset));
218 
219 	if (_irq_vector_table[TABLE_INDEX(offset)] != (uint32_t)isr) {
220 		TC_PRINT("bad entry %d in vector table\n", TABLE_INDEX(offset));
221 		return -1;
222 	}
223 #endif /* !CONFIG_IRQ_VECTOR_TABLE_JUMP_BY_CODE */
224 
225 	if (test_irq(offset)) {
226 		return -1;
227 	}
228 
229 	return 0;
230 }
231 #endif
232 
233 #ifdef CONFIG_GEN_SW_ISR_TABLE
check_sw_isr(void * isr,uintptr_t arg,int offset)234 static int check_sw_isr(void *isr, uintptr_t arg, int offset)
235 {
236 	struct _isr_table_entry *e = &_sw_isr_table[TABLE_INDEX(offset)];
237 
238 	TC_PRINT("Checking _sw_isr_table entry %d for irq %d\n",
239 		 TABLE_INDEX(offset), IRQ_LINE(offset));
240 
241 	if (e->arg != (void *)arg) {
242 		TC_PRINT("bad argument in SW isr table\n");
243 		TC_PRINT("expected %p got %p\n", (void *)arg, e->arg);
244 		return -1;
245 	}
246 	if (e->isr != isr) {
247 		TC_PRINT("Bad ISR in SW isr table\n");
248 		TC_PRINT("expected %p got %p\n", (void *)isr, e->isr);
249 		return -1;
250 	}
251 #if defined(CONFIG_GEN_IRQ_VECTOR_TABLE) && !defined(CONFIG_IRQ_VECTOR_TABLE_JUMP_BY_CODE)
252 	void *v = (void *)_irq_vector_table[TABLE_INDEX(offset)];
253 	if (v != _isr_wrapper) {
254 		TC_PRINT("Vector does not point to _isr_wrapper\n");
255 		TC_PRINT("expected %p got %p\n", _isr_wrapper, v);
256 		return -1;
257 	}
258 #endif /* CONFIG_GEN_IRQ_VECTOR_TABLE && !CONFIG_IRQ_VECTOR_TABLE_JUMP_BY_CODE */
259 
260 	if (test_irq(offset)) {
261 		return -1;
262 	}
263 	return 0;
264 }
265 #endif
266 
267 /**
268  * @ingroup kernel_interrupt_tests
269  * @brief test to validate direct interrupt
270  *
271  * @details initialize two direct interrupt handler using IRQ_DIRECT_CONNECT
272  * api at build time. For ‘direct’ interrupts, address of handler function will
273  * be placed in the irq vector table. And each entry contains the pointer to
274  * isr and the corresponding parameters.
275  *
276  * At the end according to architecture, we manually trigger the interrupt.
277  * And all irq handler should get called.
278  *
279  * @see IRQ_DIRECT_CONNECT(), irq_enable()
280  *
281  */
ZTEST(gen_isr_table,test_build_time_direct_interrupt)282 ZTEST(gen_isr_table, test_build_time_direct_interrupt)
283 {
284 #ifndef HAS_DIRECT_IRQS
285 	ztest_test_skip();
286 #else
287 
288 #ifdef ISR1_OFFSET
289 	IRQ_DIRECT_CONNECT(IRQ_LINE(ISR1_OFFSET), 0, isr1, IRQ_FLAGS);
290 	irq_enable(IRQ_LINE(ISR1_OFFSET));
291 	TC_PRINT("isr1 isr=%p irq=%d\n", isr1, IRQ_LINE(ISR1_OFFSET));
292 	zassert_ok(check_vector(isr1, ISR1_OFFSET),
293 			"check direct interrpt isr1 failed");
294 #endif
295 
296 #ifdef ISR2_OFFSET
297 	IRQ_DIRECT_CONNECT(IRQ_LINE(ISR2_OFFSET), 0, isr2, IRQ_FLAGS);
298 	irq_enable(IRQ_LINE(ISR2_OFFSET));
299 	TC_PRINT("isr2 isr=%p irq=%d\n", isr2, IRQ_LINE(ISR2_OFFSET));
300 
301 
302 	zassert_ok(check_vector(isr2, ISR2_OFFSET),
303 			"check direct interrpt isr2 failed");
304 #endif
305 #endif
306 }
307 
308 /**
309  * @ingroup kernel_interrupt_tests
310  * @brief test to validate gen_isr_table and interrupt
311  *
312  * @details initialize two normal interrupt handler using IRQ_CONNECT api at
313  * build time. For 'regular' interrupts, the address of the common software isr
314  * table is placed in the irq vector table, and software ISR table is an array
315  * of struct _isr_table_entry. And each entry contains the pointer to isr and
316  * the corresponding parameters.
317  *
318  * At the end according to architecture, we manually trigger the interrupt.
319  * And all irq handler should get called.
320  *
321  * @see IRQ_CONNECT(), irq_enable()
322  *
323  */
ZTEST(gen_isr_table,test_build_time_interrupt)324 ZTEST(gen_isr_table, test_build_time_interrupt)
325 {
326 #ifndef CONFIG_GEN_SW_ISR_TABLE
327 	ztest_test_skip();
328 #else
329 	TC_PRINT("_sw_isr_table at location %p\n", _sw_isr_table);
330 
331 #ifdef ISR3_OFFSET
332 	IRQ_CONNECT(IRQ_LINE(ISR3_OFFSET), 1, isr3, ISR3_ARG, IRQ_FLAGS);
333 	irq_enable(IRQ_LINE(ISR3_OFFSET));
334 	TC_PRINT("isr3 isr=%p irq=%d param=%p\n", isr3, IRQ_LINE(ISR3_OFFSET),
335 		 (void *)ISR3_ARG);
336 
337 	zassert_ok(check_sw_isr(isr3, ISR3_ARG, ISR3_OFFSET),
338 			"check interrupt isr3 failed");
339 #endif
340 
341 #ifdef ISR4_OFFSET
342 	IRQ_CONNECT(IRQ_LINE(ISR4_OFFSET), 1, isr4, ISR4_ARG, IRQ_FLAGS);
343 	irq_enable(IRQ_LINE(ISR4_OFFSET));
344 	TC_PRINT("isr4 isr=%p irq=%d param=%p\n", isr4, IRQ_LINE(ISR4_OFFSET),
345 		 (void *)ISR4_ARG);
346 
347 	zassert_ok(check_sw_isr(isr4, ISR4_ARG, ISR4_OFFSET),
348 			"check interrupt isr4 failed");
349 #endif
350 #endif
351 }
352 
353 /**
354  * @ingroup kernel_interrupt_tests
355  * @brief test to validate gen_isr_table and dynamic interrupt
356  *
357  * @details initialize two dynamic interrupt handler using irq_connect_dynamic
358  * api at run time. For dynamic interrupts, the address of the common software
359  * isr table is also placed in the irq vector table. Software ISR table is an
360  * array of struct _isr_table_entry. And each entry contains the pointer to isr
361  * and the corresponding parameters.
362  *
363  * At the end according to architecture, we manually trigger the interrupt.
364  * And all irq handler should get called.
365  *
366  * @see irq_connect_dynamic(), irq_enable()
367  *
368  */
ZTEST(gen_isr_table,test_run_time_interrupt)369 ZTEST(gen_isr_table, test_run_time_interrupt)
370 {
371 
372 #ifndef CONFIG_GEN_SW_ISR_TABLE
373 	ztest_test_skip();
374 #else
375 
376 #ifdef ISR5_OFFSET
377 	irq_connect_dynamic(IRQ_LINE(ISR5_OFFSET), 1, isr5,
378 			    (const void *)ISR5_ARG, IRQ_FLAGS);
379 	irq_enable(IRQ_LINE(ISR5_OFFSET));
380 	TC_PRINT("isr5 isr=%p irq=%d param=%p\n", isr5, IRQ_LINE(ISR5_OFFSET),
381 		 (void *)ISR5_ARG);
382 	zassert_ok(check_sw_isr(isr5, ISR5_ARG, ISR5_OFFSET),
383 			"test dynamic interrupt isr5 failed");
384 #endif
385 
386 #ifdef ISR6_OFFSET
387 	irq_connect_dynamic(IRQ_LINE(ISR6_OFFSET), 1, isr6,
388 			    (const void *)ISR6_ARG, IRQ_FLAGS);
389 	irq_enable(IRQ_LINE(ISR6_OFFSET));
390 	TC_PRINT("isr6 isr=%p irq=%d param=%p\n", isr6, IRQ_LINE(ISR6_OFFSET),
391 		 (void *)ISR6_ARG);
392 
393 	zassert_ok(check_sw_isr(isr6, ISR6_ARG, ISR6_OFFSET),
394 			"check dynamic interrupt isr6 failed");
395 #endif
396 #endif
397 }
398 
gen_isr_table_setup(void)399 static void *gen_isr_table_setup(void)
400 {
401 	TC_PRINT("IRQ configuration (total lines %d):\n", CONFIG_NUM_IRQS);
402 
403 #pragma GCC diagnostic push
404 #pragma GCC diagnostic ignored "-Wunused-label"
405 
406 	return NULL;
407 }
408 
409 #ifdef CONFIG_MULTI_LEVEL_INTERRUPTS
test_multi_level_bit_masks_fn(uint32_t irq1,uint32_t irq2,uint32_t irq3)410 static void test_multi_level_bit_masks_fn(uint32_t irq1, uint32_t irq2, uint32_t irq3)
411 {
412 	const uint32_t l2_shift = CONFIG_1ST_LEVEL_INTERRUPT_BITS;
413 	const uint32_t l3_shift = CONFIG_1ST_LEVEL_INTERRUPT_BITS + CONFIG_2ND_LEVEL_INTERRUPT_BITS;
414 	const uint32_t hwirq1 = irq1;
415 	const uint32_t hwirq2 = irq2 - 1;
416 	const uint32_t hwirq3 = irq3 - 1;
417 	const bool has_l3 = irq3 > 0;
418 	const bool has_l2 = irq2 > 0;
419 	const uint32_t level = has_l3 ? 3 : has_l2 ? 2 : 1;
420 	const uint32_t irqn_l1 = irq1;
421 	const uint32_t irqn_l2 = (irq2 << l2_shift) | irqn_l1;
422 	const uint32_t irqn = (irq3 << l3_shift) | irqn_l2;
423 
424 	zassert_equal(level, irq_get_level(irqn));
425 
426 	if (has_l2) {
427 		zassert_equal(hwirq2, irq_from_level_2(irqn));
428 		zassert_equal(hwirq2, irq_from_level(irqn, 2));
429 		zassert_equal((hwirq2 + 1) << l2_shift, irq_to_level_2(hwirq2));
430 		zassert_equal((hwirq2 + 1) << l2_shift, irq_to_level(hwirq2, 2));
431 		zassert_equal(hwirq1, irq_parent_level_2(irqn));
432 		zassert_equal(hwirq1, irq_parent_level(irqn, 2));
433 	}
434 
435 	if (has_l3) {
436 		zassert_equal(hwirq3, irq_from_level_3(irqn));
437 		zassert_equal(hwirq3, irq_from_level(irqn, 3));
438 		zassert_equal((hwirq3 + 1) << l3_shift, irq_to_level_3(hwirq3));
439 		zassert_equal((hwirq3 + 1) << l3_shift, irq_to_level(hwirq3, 3));
440 		zassert_equal(hwirq2 + 1, irq_parent_level_3(irqn));
441 		zassert_equal(hwirq2 + 1, irq_parent_level(irqn, 3));
442 	}
443 
444 	if (has_l3) {
445 		zassert_equal(irqn_l2, irq_get_intc_irq(irqn));
446 	} else if (has_l2) {
447 		zassert_equal(irqn_l1, irq_get_intc_irq(irqn));
448 	} else {
449 		/* degenerate cases */
450 		if (false) {
451 			zassert_equal(irqn, irq_get_intc_irq(irqn));
452 		}
453 	}
454 }
455 
ZTEST(gen_isr_table,test_multi_level_bit_masks_l1)456 ZTEST(gen_isr_table, test_multi_level_bit_masks_l1)
457 {
458 	uint32_t irq1;
459 
460 	/* First IRQ of level 1 */
461 	irq1 = 0;
462 	test_multi_level_bit_masks_fn(irq1, 0, 0);
463 
464 	/* Somewhere in-between */
465 	irq1 = BIT_MASK(CONFIG_1ST_LEVEL_INTERRUPT_BITS) >> 1;
466 	test_multi_level_bit_masks_fn(irq1, 0, 0);
467 
468 	/* Last IRQ of level 1 */
469 	irq1 = BIT_MASK(CONFIG_1ST_LEVEL_INTERRUPT_BITS);
470 	test_multi_level_bit_masks_fn(irq1, 0, 0);
471 }
472 
ZTEST(gen_isr_table,test_multi_level_bit_masks_l2)473 ZTEST(gen_isr_table, test_multi_level_bit_masks_l2)
474 {
475 	if (!IS_ENABLED(CONFIG_2ND_LEVEL_INTERRUPTS)) {
476 		ztest_test_skip();
477 	}
478 
479 	uint32_t irq1, irq2;
480 
481 	/* First IRQ of level 2 */
482 	irq1 = 0;
483 	/* First irq of level 2 and onwards is 1, as 0 means that the irq is not present */
484 	irq2 = 1;
485 	test_multi_level_bit_masks_fn(irq1, irq2, 0);
486 
487 	/* Somewhere in-between */
488 	irq1 = BIT_MASK(CONFIG_1ST_LEVEL_INTERRUPT_BITS) >> 1;
489 	irq2 = BIT_MASK(CONFIG_2ND_LEVEL_INTERRUPT_BITS) >> 1;
490 	test_multi_level_bit_masks_fn(irq1, irq2, 0);
491 
492 	/* Last IRQ of level 2 */
493 	irq1 = BIT_MASK(CONFIG_1ST_LEVEL_INTERRUPT_BITS);
494 	irq2 = BIT_MASK(CONFIG_2ND_LEVEL_INTERRUPT_BITS);
495 	test_multi_level_bit_masks_fn(irq1, irq2, 0);
496 }
497 
ZTEST(gen_isr_table,test_multi_level_bit_masks_l3)498 ZTEST(gen_isr_table, test_multi_level_bit_masks_l3)
499 {
500 	if (!IS_ENABLED(CONFIG_3RD_LEVEL_INTERRUPTS)) {
501 		ztest_test_skip();
502 	}
503 
504 	uint32_t irq1, irq2, irq3;
505 
506 	/* First IRQ of level 3 */
507 	irq1 = 0;
508 	/* First irq of level 2 and onwards is 1, as 0 means that the irq is not present */
509 	irq2 = 1;
510 	irq3 = 1;
511 	test_multi_level_bit_masks_fn(irq1, irq2, irq3);
512 
513 	/* Somewhere in-between */
514 	irq1 = BIT_MASK(CONFIG_1ST_LEVEL_INTERRUPT_BITS) >> 1;
515 	irq2 = BIT_MASK(CONFIG_2ND_LEVEL_INTERRUPT_BITS) >> 1;
516 	irq3 = BIT_MASK(CONFIG_3RD_LEVEL_INTERRUPT_BITS) >> 1;
517 	test_multi_level_bit_masks_fn(irq1, irq2, irq3);
518 
519 	/* Last IRQ of level 3 */
520 	irq1 = BIT_MASK(CONFIG_1ST_LEVEL_INTERRUPT_BITS);
521 	irq2 = BIT_MASK(CONFIG_2ND_LEVEL_INTERRUPT_BITS);
522 	irq3 = BIT_MASK(CONFIG_3RD_LEVEL_INTERRUPT_BITS);
523 	test_multi_level_bit_masks_fn(irq1, irq2, irq3);
524 }
525 
526 #endif /* CONFIG_MULTI_LEVEL_INTERRUPTS */
527 
528 ZTEST_SUITE(gen_isr_table, NULL, gen_isr_table_setup, NULL, NULL, NULL);
529