1 /*
2  * Copyright (c) 2018 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <ztest.h>
8 #include <irq_offload.h>
9 #include <debug/stack.h>
10 
11 #define STACKSIZE (256 + CONFIG_TEST_EXTRA_STACKSIZE)
12 
13 static K_THREAD_STACK_DEFINE(dyn_thread_stack, STACKSIZE);
14 static K_SEM_DEFINE(start_sem, 0, 1);
15 static K_SEM_DEFINE(end_sem, 0, 1);
16 static ZTEST_BMEM struct k_thread *dyn_thread;
17 
k_sys_fatal_error_handler(unsigned int reason,const z_arch_esf_t * esf)18 void k_sys_fatal_error_handler(unsigned int reason, const z_arch_esf_t *esf)
19 {
20 	if (reason != K_ERR_KERNEL_OOPS) {
21 		printk("wrong error reason\n");
22 		k_fatal_halt(reason);
23 	}
24 	if (k_current_get() != dyn_thread) {
25 		printk("wrong thread crashed\n");
26 		k_fatal_halt(reason);
27 	}
28 }
29 
dyn_thread_entry(void * p1,void * p2,void * p3)30 static void dyn_thread_entry(void *p1, void *p2, void *p3)
31 {
32 	k_sem_take(&start_sem, K_FOREVER);
33 
34 	k_sem_give(&end_sem);
35 }
36 
prep(void)37 static void prep(void)
38 {
39 	k_thread_access_grant(k_current_get(), dyn_thread_stack,
40 			      &start_sem, &end_sem);
41 }
42 
create_dynamic_thread(void)43 static void create_dynamic_thread(void)
44 {
45 	k_tid_t tid;
46 
47 	dyn_thread = k_object_alloc(K_OBJ_THREAD);
48 
49 	zassert_not_null(dyn_thread, "Cannot allocate thread k_object!");
50 
51 	tid = k_thread_create(dyn_thread, dyn_thread_stack, STACKSIZE,
52 			      dyn_thread_entry, NULL, NULL, NULL,
53 			      K_PRIO_PREEMPT(0), K_USER, K_FOREVER);
54 
55 	k_object_access_grant(&start_sem, tid);
56 	k_object_access_grant(&end_sem, tid);
57 
58 	k_thread_start(tid);
59 
60 	k_sem_give(&start_sem);
61 
62 	zassert_true(k_sem_take(&end_sem, K_SECONDS(1)) == 0,
63 		     "k_sem_take(end_sem) failed");
64 
65 	k_thread_abort(tid);
66 
67 	k_object_release(dyn_thread);
68 }
69 
permission_test(void)70 static void permission_test(void)
71 {
72 	struct k_thread *dyn_thread;
73 	k_tid_t tid;
74 
75 	dyn_thread = k_object_alloc(K_OBJ_THREAD);
76 
77 	zassert_not_null(dyn_thread, "Cannot allocate thread k_object!");
78 
79 	tid = k_thread_create(dyn_thread, dyn_thread_stack, STACKSIZE,
80 			      dyn_thread_entry, NULL, NULL, NULL,
81 			      K_PRIO_PREEMPT(0), K_USER, K_FOREVER);
82 
83 	k_object_access_grant(&start_sem, tid);
84 
85 	k_thread_start(tid);
86 
87 	/*
88 	 * Notice dyn_thread will not have permission to access
89 	 * end_sem, which will cause kernel oops.
90 	 */
91 
92 	k_sem_give(&start_sem);
93 
94 	/*
95 	 * If dyn_thread has permission to access end_sem,
96 	 * k_sem_take() would be able to take the semaphore.
97 	 */
98 	zassert_true(k_sem_take(&end_sem, K_SECONDS(1)) != 0,
99 		     "Semaphore end_sem has incorrect permission");
100 
101 	k_thread_abort(tid);
102 
103 	k_object_release(dyn_thread);
104 }
105 
106 /**
107  * @ingroup kernel_thread_tests
108  * @brief Test object permission on dynamic user thread when index is reused
109  *
110  * @details This creates one dynamic thread with permissions to both
111  * semaphores so there is no fault. Then a new thread is created and will be
112  * re-using the thread index in first pass. Except the second thread does
113  * not have permission to one of the semaphore. If permissions are cleared
114  * correctly when thread is destroyed, the second should raise kernel oops.
115  */
test_dyn_thread_perms(void)116 static void test_dyn_thread_perms(void)
117 {
118 	permission_test();
119 
120 	TC_PRINT("===== must have access denied on k_sem %p\n", &end_sem);
121 }
122 
123 static struct k_thread *dynamic_threads[CONFIG_MAX_THREAD_BYTES * 8];
124 
test_thread_index_management(void)125 static void test_thread_index_management(void)
126 {
127 	int i, ctr = 0;
128 
129 	/* Create thread objects until we run out of ids */
130 	while (true) {
131 		struct k_thread *t = k_object_alloc(K_OBJ_THREAD);
132 
133 		if (t == NULL) {
134 			break;
135 		}
136 
137 		dynamic_threads[ctr] = t;
138 		ctr++;
139 	}
140 
141 	zassert_true(ctr != 0, "unable to create any thread objects");
142 
143 	TC_PRINT("created %d thread objects\n", ctr);
144 
145 	/* Show that the above NULL return value wasn't because we ran out of
146 	 * heap space/
147 	 */
148 	void *blob = k_malloc(256);
149 	zassert_true(blob != NULL, "out of heap memory");
150 
151 	/* Free one of the threads... */
152 	k_object_free(dynamic_threads[0]);
153 
154 	/* And show that we can now create another one, the freed thread's
155 	 * index should have been garbage collected.
156 	 */
157 	dynamic_threads[0] = k_object_alloc(K_OBJ_THREAD);
158 	zassert_true(dynamic_threads[0] != NULL,
159 		     "couldn't create thread object\n");
160 
161 	/* TODO: Implement a test that shows that thread IDs are properly
162 	 * recycled when a thread object is garbage collected due to references
163 	 * dropping to zero. For example, we ought to be able to exit here
164 	 * without calling k_object_free() on any of the threads we created
165 	 * here; their references would drop to zero and they would be
166 	 * automatically freed. However, it is known that the thread IDs are
167 	 * not properly recycled when this happens, see #17023.
168 	 */
169 	for (i = 0; i < ctr; i++) {
170 		k_object_free(dynamic_threads[i]);
171 	}
172 }
173 
174 /**
175  * @ingroup kernel_thread_tests
176  * @brief Test creation of dynamic user thread under kernel thread
177  *
178  * @details This is a simple test to create a user thread
179  * dynamically via k_object_alloc() under a kernel thread.
180  */
test_kernel_create_dyn_user_thread(void)181 static void test_kernel_create_dyn_user_thread(void)
182 {
183 	create_dynamic_thread();
184 }
185 
186 /**
187  * @ingroup kernel_thread_tests
188  * @brief Test creation of dynamic user thread under user thread
189  *
190  * @details This is a simple test to create a user thread
191  * dynamically via k_object_alloc() under a user thread.
192  */
test_user_create_dyn_user_thread(void)193 static void test_user_create_dyn_user_thread(void)
194 {
195 	create_dynamic_thread();
196 }
197 
198 /* test case main entry */
test_main(void)199 void test_main(void)
200 {
201 	k_thread_system_pool_assign(k_current_get());
202 
203 	prep();
204 
205 	ztest_test_suite(thread_dynamic,
206 			 ztest_unit_test(test_kernel_create_dyn_user_thread),
207 			 ztest_user_unit_test(test_user_create_dyn_user_thread),
208 			 ztest_unit_test(test_dyn_thread_perms),
209 			 ztest_unit_test(test_thread_index_management)
210 			 );
211 	ztest_run_test_suite(thread_dynamic);
212 }
213