1 /*
2  * Copyright (c) 2022, Meta
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include "kernel_internal.h"
8 
9 #include <zephyr/kernel.h>
10 #include <ksched.h>
11 #include <zephyr/kernel/thread_stack.h>
12 #include <zephyr/logging/log.h>
13 #include <zephyr/sys/bitarray.h>
14 #include <zephyr/sys/kobject.h>
15 #include <zephyr/internal/syscall_handler.h>
16 
17 LOG_MODULE_DECLARE(os, CONFIG_KERNEL_LOG_LEVEL);
18 
19 #if CONFIG_DYNAMIC_THREAD_POOL_SIZE > 0
20 #define BA_SIZE CONFIG_DYNAMIC_THREAD_POOL_SIZE
21 #else
22 #define BA_SIZE 1
23 #endif /* CONFIG_DYNAMIC_THREAD_POOL_SIZE > 0 */
24 
25 struct dyn_cb_data {
26 	k_tid_t tid;
27 	k_thread_stack_t *stack;
28 };
29 
30 static K_THREAD_STACK_ARRAY_DEFINE(dynamic_stack, CONFIG_DYNAMIC_THREAD_POOL_SIZE,
31 				   CONFIG_DYNAMIC_THREAD_STACK_SIZE);
32 SYS_BITARRAY_DEFINE_STATIC(dynamic_ba, BA_SIZE);
33 
z_thread_stack_alloc_dyn(size_t align,size_t size)34 static k_thread_stack_t *z_thread_stack_alloc_dyn(size_t align, size_t size)
35 {
36 	return z_thread_aligned_alloc(align, size);
37 }
38 
z_thread_stack_alloc_pool(size_t size)39 static k_thread_stack_t *z_thread_stack_alloc_pool(size_t size)
40 {
41 	int rv;
42 	size_t offset;
43 	k_thread_stack_t *stack;
44 
45 	if (size > CONFIG_DYNAMIC_THREAD_STACK_SIZE) {
46 		LOG_DBG("stack size %zu is > pool stack size %d", size,
47 			CONFIG_DYNAMIC_THREAD_STACK_SIZE);
48 		return NULL;
49 	}
50 
51 	rv = sys_bitarray_alloc(&dynamic_ba, 1, &offset);
52 	if (rv < 0) {
53 		LOG_DBG("unable to allocate stack from pool");
54 		return NULL;
55 	}
56 
57 	__ASSERT_NO_MSG(offset < CONFIG_DYNAMIC_THREAD_POOL_SIZE);
58 
59 	stack = (k_thread_stack_t *)&dynamic_stack[offset];
60 
61 	return stack;
62 }
63 
stack_alloc_dyn(size_t size,int flags)64 static k_thread_stack_t *stack_alloc_dyn(size_t size, int flags)
65 {
66 	if ((flags & K_USER) == K_USER) {
67 #ifdef CONFIG_DYNAMIC_OBJECTS
68 		return k_object_alloc_size(K_OBJ_THREAD_STACK_ELEMENT, size);
69 #else
70 		/* Dynamic user stack needs a kobject, so if this option is not
71 		 * enabled we can't proceed.
72 		 */
73 		return NULL;
74 #endif /* CONFIG_DYNAMIC_OBJECTS */
75 	}
76 
77 	return z_thread_stack_alloc_dyn(Z_KERNEL_STACK_OBJ_ALIGN,
78 					K_KERNEL_STACK_LEN(size));
79 }
80 
z_impl_k_thread_stack_alloc(size_t size,int flags)81 k_thread_stack_t *z_impl_k_thread_stack_alloc(size_t size, int flags)
82 {
83 	k_thread_stack_t *stack = NULL;
84 
85 	if (IS_ENABLED(CONFIG_DYNAMIC_THREAD_PREFER_ALLOC)) {
86 		stack = stack_alloc_dyn(size, flags);
87 		if (stack == NULL && CONFIG_DYNAMIC_THREAD_POOL_SIZE > 0) {
88 			stack = z_thread_stack_alloc_pool(size);
89 		}
90 	} else if (IS_ENABLED(CONFIG_DYNAMIC_THREAD_PREFER_POOL)) {
91 		if (CONFIG_DYNAMIC_THREAD_POOL_SIZE > 0) {
92 			stack = z_thread_stack_alloc_pool(size);
93 		}
94 
95 		if ((stack == NULL) && IS_ENABLED(CONFIG_DYNAMIC_THREAD_ALLOC)) {
96 			stack = stack_alloc_dyn(size, flags);
97 		}
98 	}
99 
100 	return stack;
101 }
102 
103 #ifdef CONFIG_USERSPACE
z_vrfy_k_thread_stack_alloc(size_t size,int flags)104 static inline k_thread_stack_t *z_vrfy_k_thread_stack_alloc(size_t size, int flags)
105 {
106 	return z_impl_k_thread_stack_alloc(size, flags);
107 }
108 #include <zephyr/syscalls/k_thread_stack_alloc_mrsh.c>
109 #endif /* CONFIG_USERSPACE */
110 
dyn_cb(const struct k_thread * thread,void * user_data)111 static void dyn_cb(const struct k_thread *thread, void *user_data)
112 {
113 	struct dyn_cb_data *const data = (struct dyn_cb_data *)user_data;
114 
115 	if (data->stack == (k_thread_stack_t *)thread->stack_info.start) {
116 		__ASSERT(data->tid == NULL, "stack %p is associated with more than one thread!",
117 			 (void *)thread->stack_info.start);
118 		data->tid = (k_tid_t)thread;
119 	}
120 }
121 
z_impl_k_thread_stack_free(k_thread_stack_t * stack)122 int z_impl_k_thread_stack_free(k_thread_stack_t *stack)
123 {
124 	struct dyn_cb_data data = {.stack = stack};
125 
126 	/* Get a possible tid associated with stack */
127 	k_thread_foreach(dyn_cb, &data);
128 
129 	if (data.tid != NULL) {
130 		if (!(z_is_thread_state_set(data.tid, _THREAD_DUMMY) ||
131 		      z_is_thread_state_set(data.tid, _THREAD_DEAD))) {
132 			LOG_ERR("tid %p is in use!", data.tid);
133 			return -EBUSY;
134 		}
135 	}
136 
137 	if (CONFIG_DYNAMIC_THREAD_POOL_SIZE > 0) {
138 		if (IS_ARRAY_ELEMENT(dynamic_stack, stack)) {
139 			if (sys_bitarray_free(&dynamic_ba, 1, ARRAY_INDEX(dynamic_stack, stack))) {
140 				LOG_ERR("stack %p is not allocated!", stack);
141 				return -EINVAL;
142 			}
143 
144 			return 0;
145 		}
146 	}
147 
148 	if (IS_ENABLED(CONFIG_DYNAMIC_THREAD_ALLOC)) {
149 #ifdef CONFIG_USERSPACE
150 		if (k_object_find(stack)) {
151 			k_object_free(stack);
152 		} else {
153 			k_free(stack);
154 		}
155 #else
156 		k_free(stack);
157 #endif /* CONFIG_USERSPACE */
158 	} else {
159 		LOG_DBG("Invalid stack %p", stack);
160 		return -EINVAL;
161 	}
162 
163 	return 0;
164 }
165 
166 #ifdef CONFIG_USERSPACE
z_vrfy_k_thread_stack_free(k_thread_stack_t * stack)167 static inline int z_vrfy_k_thread_stack_free(k_thread_stack_t *stack)
168 {
169 	/* The thread stack object must not be in initialized state.
170 	 *
171 	 * Thread stack objects are initialized when the thread is created
172 	 * and de-initialized when the thread is destroyed. Since we can't
173 	 * free a stack that is in use, we have to check that the caller
174 	 * has access to the object but that it is not in use anymore.
175 	 */
176 	K_OOPS(K_SYSCALL_OBJ_NEVER_INIT(stack, K_OBJ_THREAD_STACK_ELEMENT));
177 
178 	return z_impl_k_thread_stack_free(stack);
179 }
180 #include <zephyr/syscalls/k_thread_stack_free_mrsh.c>
181 #endif /* CONFIG_USERSPACE */
182