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 the platforms that use the ARM GIC, use the SGI (software generated
49 * interrupt) lines 14 and 15 for testing.
50 */
51 #define IRQ0_LINE 14
52 #define IRQ1_LINE 15
53
54 /*
55 * Choose lower prio for IRQ0 and higher priority for IRQ1
56 * Minimum legal value of GICC BPR is '3' ie <gggg.ssss>
57 * Hence choosing default priority and highest possible priority
58 * '0x0' as the priorities so that the preemption rule applies
59 * generically to all GIC versions and security states.
60 */
61 #define IRQ0_PRIO IRQ_DEFAULT_PRIORITY
62 #define IRQ1_PRIO 0x0
63 #elif (defined(CONFIG_SOC_SERIES_NRF54LX) || defined(CONFIG_SOC_NRF54H20_CPUFLPR)) && \
64 defined(CONFIG_RISCV_CORE_NORDIC_VPR)
65 #define IRQ0_LINE 16
66 #define IRQ1_LINE 17
67
68 #define IRQ0_PRIO 1
69 #define IRQ1_PRIO 2
70 #elif defined(CONFIG_SOC_SERIES_NRF54HX) && defined(CONFIG_RISCV_CORE_NORDIC_VPR)
71 #define IRQ0_LINE 14
72 #define IRQ1_LINE 15
73
74 #define IRQ0_PRIO 1
75 #define IRQ1_PRIO 2
76 #elif defined(CONFIG_SOC_NRF9280_CPUPPR)
77 #define IRQ0_LINE 14
78 #define IRQ1_LINE 15
79
80 #define IRQ0_PRIO 1
81 #define IRQ1_PRIO 2
82 #else
83 /*
84 * For all the other platforms, use the last two available IRQ lines for
85 * testing.
86 */
87 #define IRQ0_LINE (CONFIG_NUM_IRQS - 1)
88 #define IRQ1_LINE (CONFIG_NUM_IRQS - 2)
89
90 #define IRQ0_PRIO 1
91 #define IRQ1_PRIO 0
92 #endif
93
94 #ifdef TEST_NESTED_ISR
95 static uint32_t irq_line_0;
96 static uint32_t irq_line_1;
97
98 static uint32_t isr0_result;
99 static uint32_t isr1_result;
100
isr1(const void * param)101 void isr1(const void *param)
102 {
103 ARG_UNUSED(param);
104
105 k_str_out_count("ISR1: Enter\n");
106
107 /* Set verification token */
108 isr1_result = ISR1_TOKEN;
109
110 k_str_out_count("ISR1: Leave\n");
111 }
112
isr0(const void * param)113 void isr0(const void *param)
114 {
115 ARG_UNUSED(param);
116
117 k_str_out_count("ISR0: Enter\n");
118
119 /* Set verification token */
120 isr0_result = ISR0_TOKEN;
121
122 /* Trigger nested IRQ 1 */
123 trigger_irq(irq_line_1);
124
125 /* Wait for interrupt */
126 k_busy_wait(DURATION * USEC_PER_MSEC);
127
128 /* Validate nested ISR result token */
129 zassert_equal(isr1_result, ISR1_TOKEN, "isr1 did not execute");
130
131 k_str_out_count("ISR0: Leave\n");
132 }
133
134 /**
135 * @brief Test interrupt nesting
136 *
137 * @ingroup kernel_interrupt_tests
138 *
139 * This routine tests the interrupt nesting feature, which allows an ISR to be
140 * preempted in mid-execution if a higher priority interrupt is signaled. The
141 * lower priority ISR resumes execution once the higher priority ISR has
142 * completed its processing.
143 *
144 * The expected control flow for this test is as follows:
145 *
146 * 1. [thread] Trigger IRQ 0 (lower priority)
147 * 2. [isr0] Set ISR 0 result token and trigger IRQ 1 (higher priority)
148 * 3. [isr1] Set ISR 1 result token and return
149 * 4. [isr0] Validate ISR 1 result token and return
150 * 5. [thread] Validate ISR 0 result token
151 */
ZTEST(interrupt_feature,test_nested_isr)152 ZTEST(interrupt_feature, test_nested_isr)
153 {
154 /* Resolve test IRQ line numbers */
155 #if defined(IRQ0_LINE) && defined(IRQ1_LINE)
156 irq_line_0 = IRQ0_LINE;
157 irq_line_1 = IRQ1_LINE;
158 #elif defined(CONFIG_CPU_CORTEX_M) && defined(CONFIG_DYNAMIC_INTERRUPTS)
159 irq_line_0 = get_available_nvic_line(CONFIG_NUM_IRQS);
160 irq_line_1 = get_available_nvic_line(irq_line_0);
161 #else
162 ztest_test_skip();
163 #endif
164
165 /* Connect and enable test IRQs */
166 #if defined(IRQ0_LINE) && defined(IRQ1_LINE)
167 IRQ_CONNECT(IRQ0_LINE, IRQ0_PRIO, isr0, 0, 0);
168 IRQ_CONNECT(IRQ1_LINE, IRQ1_PRIO, isr1, 0, 0);
169 #else
170 arch_irq_connect_dynamic(irq_line_0, IRQ0_PRIO, isr0, NULL, 0);
171 arch_irq_connect_dynamic(irq_line_1, IRQ1_PRIO, isr1, NULL, 0);
172 #endif
173
174 irq_enable(irq_line_0);
175 irq_enable(irq_line_1);
176
177 /* Trigger test IRQ 0 */
178 trigger_irq(irq_line_0);
179
180 /* Wait for interrupt */
181 k_busy_wait(DURATION * USEC_PER_MSEC);
182
183 /* Validate ISR result token */
184 zassert_equal(isr0_result, ISR0_TOKEN, "isr0 did not execute");
185 }
186 #else
ZTEST(interrupt_feature,test_nested_isr)187 ZTEST(interrupt_feature, test_nested_isr)
188 {
189 ztest_test_skip();
190 }
191 #endif /* TEST_NESTED_ISR */
192