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