1 /*
2  * Copyright (c) 2020 Stephanos Ioannidis <root@stephanos.io>
3  * Copyright (c) 2018 Intel Corporation
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #include <zephyr/ztest.h>
9 #include <zephyr/interrupt_util.h>
10 
11 /*
12  * Run the nested interrupt test for the supported platforms only.
13  */
14 #if defined(CONFIG_CPU_CORTEX_M) || defined(CONFIG_ARC) || \
15 	defined(CONFIG_GIC) || defined(CONFIG_NRFX_CLIC)
16 #define TEST_NESTED_ISR
17 #endif
18 
19 #define DURATION	5
20 
21 #define ISR0_TOKEN	0xDEADBEEF
22 #define ISR1_TOKEN	0xCAFEBABE
23 
24 /*
25  * This test uses two IRQ lines selected within the range of available IRQs on
26  * the target SoC.  These IRQs are platform and interrupt controller-specific,
27  * and must be specified for every supported platform.
28  *
29  * In terms of priority, the IRQ1 is triggered from the ISR of the IRQ0;
30  * therefore, the priority of IRQ1 must be greater than that of the IRQ0.
31  */
32 #if defined(CONFIG_CPU_CORTEX_M)
33 /*
34  * For Cortex-M NVIC, unused and available IRQs are automatically detected when
35  * the test is run.
36  *
37  * The IRQ priorities start at 1 because the priority 0 is reserved for the
38  * SVCall exception and Zero-Latency IRQs (see `_EXCEPTION_RESERVED_PRIO`).
39  */
40 #define IRQ0_PRIO	2
41 #define IRQ1_PRIO	1
42 #ifdef CONFIG_BOARD_QEMU_CORTEX_M3
43 #define IRQ0_LINE	42
44 #define IRQ1_LINE	41
45 #endif
46 #elif defined(CONFIG_GIC)
47 /*
48  * For platforms that use Arm's GIC, use the SGI (software generated
49  * interrupt) lines 6 and 7 for testing.
50  * SGI 0-2 are used by Zephyr for SMP IPIs.
51  * SGI 8-15 are unaccessible from Non-Secure state.
52  */
53 #define IRQ0_LINE	6
54 #define IRQ1_LINE	7
55 
56 /*
57  * Choose lower prio for IRQ0 and higher priority for IRQ1
58  * Minimum legal value of GICC BPR is '3' ie  <gggg.ssss>
59  * Hence choosing default priority and highest possible priority
60  * '0x0' as the priorities so that the preemption rule applies
61  * generically to all GIC versions and security states.
62  */
63 #define IRQ0_PRIO	IRQ_DEFAULT_PRIORITY
64 #define IRQ1_PRIO	0x0
65 #elif (defined(CONFIG_SOC_SERIES_NRF54LX) || defined(CONFIG_SOC_NRF54H20_CPUFLPR)) && \
66 	defined(CONFIG_RISCV_CORE_NORDIC_VPR)
67 #define IRQ0_LINE	16
68 #define IRQ1_LINE	17
69 
70 #define IRQ0_PRIO	1
71 #define IRQ1_PRIO	2
72 #elif defined(CONFIG_SOC_SERIES_NRF54HX) && defined(CONFIG_RISCV_CORE_NORDIC_VPR)
73 #define IRQ0_LINE	14
74 #define IRQ1_LINE	15
75 
76 #define IRQ0_PRIO	1
77 #define IRQ1_PRIO	2
78 #elif defined(CONFIG_SOC_NRF9280_CPUPPR)
79 #define IRQ0_LINE	14
80 #define IRQ1_LINE	15
81 
82 #define IRQ0_PRIO	1
83 #define IRQ1_PRIO	2
84 #else
85 /*
86  * For all the other platforms, use the last two available IRQ lines for
87  * testing.
88  */
89 #define IRQ0_LINE	(CONFIG_NUM_IRQS - 1)
90 #define IRQ1_LINE	(CONFIG_NUM_IRQS - 2)
91 
92 #define IRQ0_PRIO	1
93 #define IRQ1_PRIO	0
94 #endif
95 
96 #ifdef TEST_NESTED_ISR
97 static uint32_t irq_line_0;
98 static uint32_t irq_line_1;
99 
100 static uint32_t isr0_result;
101 static uint32_t isr1_result;
102 
isr1(const void * param)103 void isr1(const void *param)
104 {
105 	ARG_UNUSED(param);
106 
107 	k_str_out_count("ISR1: Enter\n");
108 
109 	/* Set verification token */
110 	isr1_result = ISR1_TOKEN;
111 
112 	k_str_out_count("ISR1: Leave\n");
113 }
114 
isr0(const void * param)115 void isr0(const void *param)
116 {
117 	ARG_UNUSED(param);
118 
119 	k_str_out_count("ISR0: Enter\n");
120 
121 	/* Set verification token */
122 	isr0_result = ISR0_TOKEN;
123 
124 	/* Trigger nested IRQ 1 */
125 	trigger_irq(irq_line_1);
126 
127 	/* Wait for interrupt */
128 	k_busy_wait(DURATION * USEC_PER_MSEC);
129 
130 	/* Validate nested ISR result token */
131 	zassert_equal(isr1_result, ISR1_TOKEN, "isr1 did not execute");
132 
133 	k_str_out_count("ISR0: Leave\n");
134 }
135 
136 /**
137  * @brief Test interrupt nesting
138  *
139  * @ingroup kernel_interrupt_tests
140  *
141  * This routine tests the interrupt nesting feature, which allows an ISR to be
142  * preempted in mid-execution if a higher priority interrupt is signaled. The
143  * lower priority ISR resumes execution once the higher priority ISR has
144  * completed its processing.
145  *
146  * The expected control flow for this test is as follows:
147  *
148  * 1. [thread] Trigger IRQ 0 (lower priority)
149  * 2. [isr0] Set ISR 0 result token and trigger IRQ 1 (higher priority)
150  * 3. [isr1] Set ISR 1 result token and return
151  * 4. [isr0] Validate ISR 1 result token and return
152  * 5. [thread] Validate ISR 0 result token
153  */
ZTEST(interrupt_feature,test_nested_isr)154 ZTEST(interrupt_feature, test_nested_isr)
155 {
156 	/* Resolve test IRQ line numbers */
157 #if defined(IRQ0_LINE) && defined(IRQ1_LINE)
158 	irq_line_0 = IRQ0_LINE;
159 	irq_line_1 = IRQ1_LINE;
160 #elif defined(CONFIG_CPU_CORTEX_M) && defined(CONFIG_DYNAMIC_INTERRUPTS)
161 	irq_line_0 = get_available_nvic_line(CONFIG_NUM_IRQS);
162 	irq_line_1 = get_available_nvic_line(irq_line_0);
163 #else
164 	ztest_test_skip();
165 #endif
166 
167 	/* Connect and enable test IRQs */
168 #if defined(IRQ0_LINE) && defined(IRQ1_LINE)
169 	IRQ_CONNECT(IRQ0_LINE, IRQ0_PRIO, isr0, 0, 0);
170 	IRQ_CONNECT(IRQ1_LINE, IRQ1_PRIO, isr1, 0, 0);
171 #else
172 	arch_irq_connect_dynamic(irq_line_0, IRQ0_PRIO, isr0, NULL, 0);
173 	arch_irq_connect_dynamic(irq_line_1, IRQ1_PRIO, isr1, NULL, 0);
174 #endif
175 
176 	irq_enable(irq_line_0);
177 	irq_enable(irq_line_1);
178 
179 	/* Trigger test IRQ 0 */
180 	trigger_irq(irq_line_0);
181 
182 	/* Wait for interrupt */
183 	k_busy_wait(DURATION * USEC_PER_MSEC);
184 
185 	/* Validate ISR result token */
186 	zassert_equal(isr0_result, ISR0_TOKEN, "isr0 did not execute");
187 }
188 #else
ZTEST(interrupt_feature,test_nested_isr)189 ZTEST(interrupt_feature, test_nested_isr)
190 {
191 	ztest_test_skip();
192 }
193 #endif /* TEST_NESTED_ISR */
194