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