1 /*
2 * Copyright (c) 2019 Nordic Semiconductor ASA.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/ztest.h>
8 #include <zephyr/sys/barrier.h>
9
10 #define STACKSIZE 1024
11
12 /* Priority level of the threads used in this test.
13 * The priority level, itself, is arbitrary; we only
14 * want to ensure they are cooperative threads.
15 */
16 #define PRIORITY K_PRIO_COOP(0)
17
18 #if defined(CONFIG_X86) && defined(CONFIG_X86_SSE)
19 #define K_FP_OPTS (K_FP_REGS | K_SSE_REGS)
20 #elif defined(CONFIG_X86) || defined(CONFIG_ARM) || defined(CONFIG_SPARC)
21 #define K_FP_OPTS K_FP_REGS
22 #else
23 #error "Architecture not supported for this test"
24 #endif
25
26 struct k_thread usr_fp_thread;
27 K_THREAD_STACK_DEFINE(usr_fp_thread_stack, STACKSIZE);
28
29 ZTEST_BMEM static volatile int test_ret = TC_PASS;
30
usr_fp_thread_entry_1(void * p1,void * p2,void * p3)31 static void usr_fp_thread_entry_1(void *p1, void *p2, void *p3)
32 {
33 ARG_UNUSED(p1);
34 ARG_UNUSED(p2);
35 ARG_UNUSED(p3);
36
37 k_yield();
38 }
39
40 #if defined(CONFIG_ARM) || (defined(CONFIG_X86) && defined(CONFIG_LAZY_FPU_SHARING))
41 #define K_FLOAT_DISABLE_SYSCALL_RETVAL 0
42 #else
43 #define K_FLOAT_DISABLE_SYSCALL_RETVAL -ENOTSUP
44 #endif
45
usr_fp_thread_entry_2(void * p1,void * p2,void * p3)46 static void usr_fp_thread_entry_2(void *p1, void *p2, void *p3)
47 {
48 ARG_UNUSED(p1);
49 ARG_UNUSED(p2);
50 ARG_UNUSED(p3);
51
52 k_yield();
53
54 /* System call to disable FP mode */
55 if (k_float_disable(k_current_get()) != K_FLOAT_DISABLE_SYSCALL_RETVAL) {
56
57 TC_ERROR("k_float_disable() fail - should never see this\n");
58
59 test_ret = TC_FAIL;
60 }
61 }
62
ZTEST(k_float_disable,test_k_float_disable_common)63 ZTEST(k_float_disable, test_k_float_disable_common)
64 {
65 test_ret = TC_PASS;
66
67 /* Set thread priority level to the one used
68 * in this test suite for cooperative threads.
69 */
70 k_thread_priority_set(k_current_get(), PRIORITY);
71
72 /* Create an FP-capable User thread with the same cooperative
73 * priority as the current thread.
74 */
75 k_thread_create(&usr_fp_thread, usr_fp_thread_stack, STACKSIZE,
76 usr_fp_thread_entry_1, NULL, NULL, NULL,
77 PRIORITY, K_USER | K_FP_OPTS,
78 K_NO_WAIT);
79
80 /* Yield will swap-in usr_fp_thread */
81 k_yield();
82
83 /* Verify K_FP_OPTS are set properly */
84 zassert_true(
85 (usr_fp_thread.base.user_options & K_FP_OPTS) != 0,
86 "usr_fp_thread FP options not set (0x%0x)",
87 usr_fp_thread.base.user_options);
88
89 #if defined(CONFIG_ARM)
90 /* Verify FP mode can only be disabled for current thread */
91 zassert_true((k_float_disable(&usr_fp_thread) == -EINVAL),
92 "k_float_disable() successful on thread other than current!");
93
94 /* Verify K_FP_OPTS are still set */
95 zassert_true(
96 (usr_fp_thread.base.user_options & K_FP_OPTS) != 0,
97 "usr_fp_thread FP options cleared");
98 #elif defined(CONFIG_X86) && defined(CONFIG_LAZY_FPU_SHARING)
99 zassert_true((k_float_disable(&usr_fp_thread) == 0),
100 "k_float_disable() failure");
101
102 /* Verify K_FP_OPTS are now cleared */
103 zassert_true(
104 (usr_fp_thread.base.user_options & K_FP_OPTS) == 0,
105 "usr_fp_thread FP options not clear (0x%0x)",
106 usr_fp_thread.base.user_options);
107 #else
108 /* Verify k_float_disable() is not supported */
109 zassert_true((k_float_disable(&usr_fp_thread) == -ENOTSUP),
110 "k_float_disable() successful when not supported");
111 #endif
112 }
113
ZTEST(k_float_disable,test_k_float_disable_syscall)114 ZTEST(k_float_disable, test_k_float_disable_syscall)
115 {
116 test_ret = TC_PASS;
117
118 k_thread_priority_set(k_current_get(), PRIORITY);
119
120 /* Create an FP-capable User thread with the same cooperative
121 * priority as the current thread. The thread will disable its
122 * FP mode.
123 */
124 k_thread_create(&usr_fp_thread, usr_fp_thread_stack, STACKSIZE,
125 usr_fp_thread_entry_2, NULL, NULL, NULL,
126 PRIORITY, K_INHERIT_PERMS | K_USER | K_FP_OPTS,
127 K_NO_WAIT);
128
129 /* Yield will swap-in usr_fp_thread */
130 k_yield();
131
132 /* Verify K_FP_OPTS are set properly */
133 zassert_true(
134 (usr_fp_thread.base.user_options & K_FP_OPTS) != 0,
135 "usr_fp_thread FP options not set (0x%0x)",
136 usr_fp_thread.base.user_options);
137
138 /* Yield will swap-in usr_fp_thread */
139 k_yield();
140
141 #if defined(CONFIG_ARM) || (defined(CONFIG_X86) && defined(CONFIG_LAZY_FPU_SHARING))
142
143 /* Verify K_FP_OPTS are now cleared by the user thread itself */
144 zassert_true(
145 (usr_fp_thread.base.user_options & K_FP_OPTS) == 0,
146 "usr_fp_thread FP options not clear (0x%0x)",
147 usr_fp_thread.base.user_options);
148
149 /* test_ret is volatile, static analysis says we can't use in assert */
150 bool ok = test_ret == TC_PASS;
151
152 zassert_true(ok, "");
153 #else
154 /* Check skipped for x86 without support for Lazy FP Sharing */
155 #endif
156 }
157
158 #if defined(CONFIG_ARM) && defined(CONFIG_DYNAMIC_INTERRUPTS)
159
160 #include <zephyr/arch/cpu.h>
161 #if defined(CONFIG_CPU_CORTEX_M)
162 #include <cmsis_core.h>
163 #else
164 #include <zephyr/interrupt_util.h>
165 #endif
166
167 struct k_thread sup_fp_thread;
168 K_THREAD_STACK_DEFINE(sup_fp_thread_stack, STACKSIZE);
169
arm_test_isr_handler(const void * args)170 void arm_test_isr_handler(const void *args)
171 {
172 ARG_UNUSED(args);
173
174 if (k_float_disable(&sup_fp_thread) != -EINVAL) {
175
176 TC_ERROR("k_float_disable() successful in ISR\n");
177
178 test_ret = TC_FAIL;
179 }
180 }
181
sup_fp_thread_entry(void * p1,void * p2,void * p3)182 static void sup_fp_thread_entry(void *p1, void *p2, void *p3)
183 {
184 ARG_UNUSED(p1);
185 ARG_UNUSED(p2);
186 ARG_UNUSED(p3);
187
188 /* Verify K_FP_REGS flag is set */
189 if ((sup_fp_thread.base.user_options & K_FP_REGS) == 0) {
190
191 TC_ERROR("sup_fp_thread FP options cleared\n");
192 test_ret = TC_FAIL;
193 }
194
195 /* Determine an NVIC IRQ line that is not currently in use. */
196 int i;
197
198 #if defined(CONFIG_CPU_CORTEX_M)
199 for (i = CONFIG_NUM_IRQS - 1; i >= 0; i--) {
200 if (NVIC_GetEnableIRQ(i) == 0) {
201 /*
202 * Interrupts configured statically with IRQ_CONNECT(.)
203 * are automatically enabled. NVIC_GetEnableIRQ()
204 * returning false, here, implies that the IRQ line is
205 * not enabled, thus, currently not in use by Zephyr.
206 */
207 break;
208 }
209 }
210 #else
211 /*
212 * SGIs are always enabled by default, so choose the last one
213 * for testing.
214 */
215 i = GIC_PPI_INT_BASE - 1;
216 #endif
217
218 zassert_true(i >= 0,
219 "No available IRQ line to use in the test\n");
220
221 TC_PRINT("Available IRQ line: %u\n", i);
222
223 arch_irq_connect_dynamic(i,
224 0,
225 arm_test_isr_handler,
226 NULL,
227 0);
228
229 #if defined(CONFIG_CPU_CORTEX_M)
230 NVIC_ClearPendingIRQ(i);
231 NVIC_EnableIRQ(i);
232 NVIC_SetPendingIRQ(i);
233 #else
234 arch_irq_enable(i);
235 trigger_irq(i);
236 #endif
237
238 /*
239 * Instruction barriers to make sure the NVIC IRQ is
240 * set to pending state before program proceeds.
241 */
242 barrier_dsync_fence_full();
243 barrier_isync_fence_full();
244
245 /* Verify K_FP_REGS flag is still set */
246 if ((sup_fp_thread.base.user_options & K_FP_REGS) == 0) {
247
248 TC_ERROR("sup_fp_thread FP options cleared\n");
249 test_ret = TC_FAIL;
250 }
251 }
252
ZTEST(k_float_disable,test_k_float_disable_irq)253 ZTEST(k_float_disable, test_k_float_disable_irq)
254 {
255 test_ret = TC_PASS;
256
257 k_thread_priority_set(k_current_get(), PRIORITY);
258
259
260 /* Create an FP-capable Supervisor thread with the same cooperative
261 * priority as the current thread.
262 */
263 k_thread_create(&sup_fp_thread, sup_fp_thread_stack, STACKSIZE,
264 sup_fp_thread_entry, NULL, NULL, NULL,
265 PRIORITY, K_FP_REGS,
266 K_NO_WAIT);
267
268 /* Yield will swap-in sup_fp_thread */
269 k_yield();
270
271 /* test_ret is volatile, static analysis says we can't use in assert */
272 bool ok = test_ret == TC_PASS;
273
274 zassert_true(ok, "");
275 }
276 #else
ZTEST(k_float_disable,test_k_float_disable_irq)277 ZTEST(k_float_disable, test_k_float_disable_irq)
278 {
279 TC_PRINT("This is not an ARM system with DYNAMIC_INTERRUPTS.\n");
280 ztest_test_skip();
281 }
282 #endif /* CONFIG_ARM && CONFIG_DYNAMIC_INTERRUPTS */
283
284 ZTEST_SUITE(k_float_disable, NULL, NULL, NULL, NULL, NULL);
285