1 /*
2  * Copyright (c) 2020 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/ztest.h>
8 #include <zephyr/kernel.h>
9 #include <zephyr/kernel_structs.h>
10 #include <zephyr/app_memory/app_memdomain.h>
11 #include <zephyr/sys/libc-hooks.h>
12 #include <zephyr/sys/util.h>
13 
14 #define NUM_THREADS	3
15 #define STACK_SIZE	(512 + CONFIG_TEST_EXTRA_STACK_SIZE)
16 
17 #define STATIC_DATA8	0x7FU
18 #define STATIC_DATA32	0xABCDEF00U
19 #define STATIC_DATA64	0x1122334455667788UL
20 
21 #define PREFIX_8	0x30U
22 #define PREFIX_32	0x44668800U
23 #define PREFIX_64	0xFFEEDDCC00000000UL
24 
25 #ifdef CONFIG_USERSPACE
26 K_APPMEM_PARTITION_DEFINE(part_common);
27 struct k_mem_domain dom_common;
28 #endif /* CONFIG_USERSPACE */
29 
30 enum test_result {
31 	TEST_OK,
32 
33 	/* When thread_data* != STATIC_DATA at thread entry */
34 	ERR_BAD_STATIC_DATA,
35 
36 	/* When thread_bss* != 0 at thread entry */
37 	ERR_BSS_NOT_ZERO,
38 
39 	/* If data/bss is changed by other threads */
40 	ERR_DATA_CHANGED_BY_OTHERS,
41 	ERR_BSS_CHANGED_BY_OTHERS,
42 
43 	TEST_NOT_STARTED,
44 };
45 
46 static K_THREAD_STACK_ARRAY_DEFINE(tls_stack, NUM_THREADS, STACK_SIZE);
47 
48 static struct k_thread tls_thread[NUM_THREADS];
49 
50 K_APP_BMEM(part_common) static k_tid_t tls_tid[NUM_THREADS];
51 K_APP_BMEM(part_common) static enum test_result tls_result[NUM_THREADS];
52 
53 /* Thread data with initialized values */
54 static uint8_t Z_THREAD_LOCAL thread_data8 = STATIC_DATA8;
55 static uint32_t Z_THREAD_LOCAL thread_data32 = STATIC_DATA32;
56 static uint64_t Z_THREAD_LOCAL thread_data64 = STATIC_DATA64;
57 
58 /* Zeroed thread data */
59 static uint8_t Z_THREAD_LOCAL thread_bss8;
60 static uint32_t Z_THREAD_LOCAL thread_bss32;
61 static uint64_t Z_THREAD_LOCAL thread_bss64;
62 
tls_thread_entry(void * p1,void * p2,void * p3)63 static void tls_thread_entry(void *p1, void *p2, void *p3)
64 {
65 	uint32_t idx;
66 
67 	idx = (uint32_t)POINTER_TO_UINT(p1);
68 
69 	/* Check if TLS area in stack is initialized correctly */
70 	if (thread_data8 != STATIC_DATA8) {
71 		tls_result[idx] = ERR_BAD_STATIC_DATA;
72 		goto out;
73 	}
74 
75 	if (thread_data32 != STATIC_DATA32) {
76 		tls_result[idx] = ERR_BAD_STATIC_DATA;
77 		goto out;
78 	}
79 
80 	if (thread_data64 != STATIC_DATA64) {
81 		tls_result[idx] = ERR_BAD_STATIC_DATA;
82 		goto out;
83 	}
84 
85 	if (thread_bss8 != 0) {
86 		tls_result[idx] = ERR_BSS_NOT_ZERO;
87 		goto out;
88 	}
89 
90 	if (thread_bss32 != 0) {
91 		tls_result[idx] = ERR_BSS_NOT_ZERO;
92 		goto out;
93 	}
94 
95 	if (thread_bss64 != 0) {
96 		tls_result[idx] = ERR_BSS_NOT_ZERO;
97 		goto out;
98 	}
99 
100 	/* Set thread data and see if they remain unchanged */
101 	thread_data8 = STATIC_DATA8 + idx;
102 	thread_bss8 = PREFIX_8 + idx;
103 
104 	thread_data32 = STATIC_DATA32 + idx;
105 	thread_bss32 = PREFIX_32 + idx;
106 
107 	thread_data64 = STATIC_DATA64 + idx;
108 	thread_bss64 = PREFIX_64 + idx;
109 
110 	/* Let other threads run */
111 	k_sleep(K_MSEC(100));
112 
113 	if (thread_data8 != (STATIC_DATA8 + idx)) {
114 		tls_result[idx] = ERR_DATA_CHANGED_BY_OTHERS;
115 		goto out;
116 	}
117 
118 	if (thread_data32 != (STATIC_DATA32 + idx)) {
119 		tls_result[idx] = ERR_DATA_CHANGED_BY_OTHERS;
120 		goto out;
121 	}
122 
123 	if (thread_data64 != (STATIC_DATA64 + idx)) {
124 		tls_result[idx] = ERR_DATA_CHANGED_BY_OTHERS;
125 		goto out;
126 	}
127 
128 	if (thread_bss8 != (PREFIX_8 + idx)) {
129 		tls_result[idx] = ERR_BSS_CHANGED_BY_OTHERS;
130 		goto out;
131 	}
132 
133 	if (thread_bss32 != (PREFIX_32 + idx)) {
134 		tls_result[idx] = ERR_BSS_CHANGED_BY_OTHERS;
135 		goto out;
136 	}
137 
138 	if (thread_bss64 != (PREFIX_64 + idx)) {
139 		tls_result[idx] = ERR_BSS_CHANGED_BY_OTHERS;
140 		goto out;
141 	}
142 
143 	/* Values are all expected. Test passed */
144 	tls_result[idx] = TEST_OK;
145 
146 out:
147 	return;
148 }
149 
start_tls_test(uint32_t thread_options)150 static void start_tls_test(uint32_t thread_options)
151 {
152 	unsigned int i;
153 	bool passed;
154 
155 	/* Create threads */
156 	for (i = 0; i < NUM_THREADS; i++) {
157 		tls_result[i] = TEST_NOT_STARTED;
158 		tls_tid[i] = k_thread_create(&tls_thread[i], tls_stack[i],
159 					     STACK_SIZE, tls_thread_entry,
160 					     UINT_TO_POINTER(i), NULL, NULL,
161 					     0, thread_options, K_NO_WAIT);
162 	}
163 
164 	/* Wait for all threads to run */
165 	k_sleep(K_MSEC(500));
166 
167 	/* Stop all threads */
168 	for (i = 0; i < NUM_THREADS; i++) {
169 		k_thread_abort(tls_tid[i]);
170 		k_thread_join(&tls_thread[i], K_FOREVER);
171 	}
172 
173 	/* Check test results */
174 	passed = true;
175 	for (i = 0; i < NUM_THREADS; i++) {
176 		TC_PRINT("thread %d: result %d (expecting %d)\n",
177 			 i, tls_result[i], TEST_OK);
178 		if (tls_result[i] != TEST_OK) {
179 			passed = false;
180 		}
181 	}
182 
183 	zassert_true(passed, "Test failed");
184 }
185 
ZTEST(thread_tls,test_tls)186 ZTEST(thread_tls, test_tls)
187 {
188 	if (IS_ENABLED(CONFIG_USERSPACE)) {
189 		ztest_test_skip();
190 	}
191 
192 	/* TLS test in supervisor mode */
193 	start_tls_test(0);
194 }
195 
ZTEST_USER(thread_tls,test_tls_userspace)196 ZTEST_USER(thread_tls, test_tls_userspace)
197 {
198 	/* TLS test in supervisor mode */
199 	start_tls_test(K_USER | K_INHERIT_PERMS);
200 }
201 
thread_tls_setup(void)202 void *thread_tls_setup(void)
203 {
204 #ifdef CONFIG_USERSPACE
205 	int ret;
206 	unsigned int i;
207 
208 	struct k_mem_partition *parts[] = {
209 		&part_common,
210 #if Z_LIBC_PARTITION_EXISTS
211 		&z_libc_partition,
212 #endif
213 		&ztest_mem_partition,
214 	};
215 
216 	parts[0] = &part_common;
217 
218 	ret = k_mem_domain_init(&dom_common, ARRAY_SIZE(parts), parts);
219 	__ASSERT(ret == 0, "k_mem_domain_init() failed %d", ret);
220 	ARG_UNUSED(ret);
221 
222 	k_mem_domain_add_thread(&dom_common, k_current_get());
223 
224 	for (i = 0; i < NUM_THREADS; i++) {
225 		k_thread_access_grant(k_current_get(),
226 				      &tls_thread[i], &tls_stack[i]);
227 	}
228 #endif /* CONFIG_USERSPACE */
229 
230 	return NULL;
231 }
232 
233 ZTEST_SUITE(thread_tls, NULL, thread_tls_setup, NULL, NULL, NULL);
234