PatchworkOS  da8a090
A non-POSIX operating system.
Loading...
Searching...
No Matches
thread.c
Go to the documentation of this file.
2
3#include <kernel/cpu/cpu.h>
4#include <kernel/cpu/gdt.h>
5#include <kernel/init/init.h>
6#include <kernel/log/log.h>
7#include <kernel/log/panic.h>
8#include <kernel/mem/vmm.h>
10#include <kernel/sched/timer.h>
11#include <kernel/sched/wait.h>
12#include <kernel/sync/lock.h>
13
14#include <stdlib.h>
15#include <string.h>
16#include <sys/list.h>
17#include <sys/math.h>
18
23
24static uint64_t thread_init(thread_t* thread, process_t* process)
25{
26 if (process == NULL)
27 {
28 return ERR;
29 }
30
31 thread->process = REF(process);
33 thread->id = thread->process->threads.newTid++;
34 sched_client_init(&thread->sched);
35 atomic_init(&thread->state, THREAD_PARKED);
36 thread->error = 0;
40 {
41 UNREF(process);
42 return ERR;
43 }
44 if (stack_pointer_init(&thread->userStack,
47 {
48 UNREF(process);
49 return ERR;
50 }
51 wait_client_init(&thread->wait);
52 if (simd_ctx_init(&thread->simd) == ERR)
53 {
54 UNREF(process);
55 return ERR;
56 }
57 note_queue_init(&thread->notes);
58 syscall_ctx_init(&thread->syscall, &thread->kernelStack);
59 perf_thread_ctx_init(&thread->perf);
60
61 memset(&thread->frame, 0, sizeof(interrupt_frame_t));
62
63 lock_acquire(&process->threads.lock);
64 list_push_back(&process->threads.list, &thread->processEntry);
65 lock_release(&process->threads.lock);
66
67 return 0;
68}
69
71{
72 if (process == NULL)
73 {
74 errno = EINVAL;
75 return NULL;
76 }
77
78 if (atomic_load(&process->flags) & PROCESS_DYING)
79 {
80 errno = EINVAL;
81 return NULL;
82 }
83
84 thread_t* thread = malloc(sizeof(thread_t));
85 if (thread == NULL)
86 {
87 return NULL;
88 }
89 if (thread_init(thread, process) == ERR)
90 {
91 free(thread);
92 return NULL;
93 }
94 return thread;
95}
96
98{
99 if (entry == NULL)
100 {
101 errno = EINVAL;
102 return ERR;
103 }
104
106 if (thread == NULL)
107 {
108 return ERR;
109 }
110
111 thread->frame.rip = (uintptr_t)entry;
112 thread->frame.rdi = (uintptr_t)arg;
113 thread->frame.rbp = thread->kernelStack.top;
114 thread->frame.rsp = thread->kernelStack.top;
115 thread->frame.cs = GDT_CS_RING0;
116 thread->frame.ss = GDT_SS_RING0;
118
119 tid_t volatile tid = thread->id;
120 sched_submit(thread);
121 return tid;
122}
123
125{
126 process_t* process = thread->process;
127 assert(process != NULL);
128
129 lock_acquire(&process->threads.lock);
130 list_remove(&process->threads.list, &thread->processEntry);
131 lock_release(&process->threads.lock);
132
133 UNREF(process);
134 thread->process = NULL;
135
136 simd_ctx_deinit(&thread->simd);
137 free(thread);
138}
139
140void thread_save(thread_t* thread, const interrupt_frame_t* frame)
141{
142 simd_ctx_save(&thread->simd);
143
144 thread->frame = *frame;
145}
146
148{
149 *frame = thread->frame;
150
151 space_load(&thread->process->space);
152 simd_ctx_load(&thread->simd);
153 syscall_ctx_load(&thread->syscall);
154}
155
157{
158 return note_queue_length(&thread->notes) != 0;
159}
160
162{
163 if (note_queue_write(&thread->notes, buffer, count) == ERR)
164 {
165 return ERR;
166 }
167
169 if (atomic_compare_exchange_strong(&thread->state, &expected, THREAD_UNBLOCKING))
170 {
171 LOG_DEBUG("notifying thread tid=%d pid=%d of pending note\n", thread->id, thread->process->id);
172 wait_unblock_thread(thread, EINTR);
173 }
174
175 return 0;
176}
177
179{
180 return errno;
181}
182
184{
185 return sched_thread()->id;
186}
187
188uint64_t thread_copy_from_user(thread_t* thread, void* dest, const void* userSrc, uint64_t length)
189{
190 if (thread == NULL || dest == NULL || userSrc == NULL || length == 0)
191 {
192 errno = EINVAL;
193 return ERR;
194 }
195
196 if (space_pin(&thread->process->space, userSrc, length, &thread->userStack) == ERR)
197 {
198 return ERR;
199 }
200
201 memcpy(dest, userSrc, length);
202 space_unpin(&thread->process->space, userSrc, length);
203 return 0;
204}
205
206uint64_t thread_copy_to_user(thread_t* thread, void* dest, const void* userSrc, uint64_t length)
207{
208 if (thread == NULL || dest == NULL || userSrc == NULL || length == 0)
209 {
210 errno = EINVAL;
211 return ERR;
212 }
213
214 if (space_pin(&thread->process->space, dest, length, &thread->userStack) == ERR)
215 {
216 return ERR;
217 }
218
219 memcpy(dest, userSrc, length);
220 space_unpin(&thread->process->space, dest, length);
221 return 0;
222}
223
224uint64_t thread_copy_from_user_terminated(thread_t* thread, const void* userArray, const void* terminator,
225 uint8_t objectSize, uint64_t maxCount, void** outArray, uint64_t* outCount)
226{
227 if (thread == NULL || userArray == NULL || terminator == NULL || objectSize == 0 || maxCount == 0 ||
228 outArray == NULL)
229 {
230 errno = EINVAL;
231 return ERR;
232 }
233
234 uint64_t arraySize =
235 space_pin_terminated(&thread->process->space, userArray, terminator, objectSize, maxCount, &thread->userStack);
236 if (arraySize == ERR)
237 {
238 return ERR;
239 }
240
241 uint64_t elementCount = arraySize / objectSize;
242 uint64_t allocSize = (elementCount + 1) * objectSize; // +1 for terminator
243
244 void* kernelArray = malloc(allocSize);
245 if (kernelArray == NULL)
246 {
247 space_unpin(&thread->process->space, userArray, arraySize);
248 errno = ENOMEM;
249 return ERR;
250 }
251
252 memcpy(kernelArray, userArray, arraySize);
253 memcpy((uint8_t*)kernelArray + arraySize, terminator, objectSize); // Add terminator
254 space_unpin(&thread->process->space, userArray, arraySize);
255
256 *outArray = kernelArray;
257 if (outCount != NULL)
258 {
259 *outCount = elementCount;
260 }
261
262 return 0;
263}
264
265uint64_t thread_copy_from_user_pathname(thread_t* thread, pathname_t* pathname, const char* userPath)
266{
267 if (thread == NULL || pathname == NULL || userPath == NULL)
268 {
269 errno = EINVAL;
270 return ERR;
271 }
272
273 char terminator = '\0';
274 uint64_t pathLength = space_pin_terminated(&thread->process->space, userPath, &terminator, sizeof(char), MAX_PATH,
275 &thread->userStack);
276 if (pathLength == ERR)
277 {
278 return ERR;
279 }
280
281 char copy[MAX_PATH];
282 memcpy(copy, userPath, pathLength);
283 copy[pathLength] = '\0';
284 space_unpin(&thread->process->space, userPath, pathLength);
285
286 if (pathname_init(pathname, copy) == ERR)
287 {
288 return ERR;
289 }
290
291 return 0;
292}
293
294uint64_t thread_copy_from_user_string_array(thread_t* thread, const char** user, char*** out, uint64_t* outAmount)
295{
296 char** copy;
297 uint64_t amount;
298 char* terminator = NULL;
299 if (thread_copy_from_user_terminated(thread, (void*)user, (void*)&terminator, sizeof(char*), CONFIG_MAX_ARGC,
300 (void**)&copy, &amount) == ERR)
301 {
302 return ERR;
303 }
304
305 for (uint64_t i = 0; i < amount; i++)
306 {
307 char* strCopy;
308 uint64_t strLen;
309 char strTerminator = '\0';
310 if (thread_copy_from_user_terminated(thread, copy[i], &strTerminator, sizeof(char), MAX_PATH, (void**)&strCopy,
311 &strLen) == ERR)
312 {
313 for (uint64_t j = 0; j < i; j++)
314 {
315 free(copy[j]);
316 }
317 free((void*)copy);
318 return ERR;
319 }
320
321 copy[i] = strCopy;
322 }
323
324 *out = copy;
325 if (outAmount != NULL)
326 {
327 *outAmount = amount;
328 }
329
330 return 0;
331}
332
333uint64_t thread_load_atomic_from_user(thread_t* thread, atomic_uint64_t* userObj, uint64_t* outValue)
334{
335 if (thread == NULL || userObj == NULL || outValue == NULL)
336 {
337 errno = EINVAL;
338 return ERR;
339 }
340
341 if (space_pin(&thread->process->space, userObj, sizeof(atomic_uint64_t), &thread->userStack) == ERR)
342 {
343 return ERR;
344 }
345
346 *outValue = atomic_load(userObj);
347 space_unpin(&thread->process->space, userObj, sizeof(atomic_uint64_t));
348 return 0;
349}
#define MAX_PATH
Maximum length of filepaths.
Definition MAX_PATH.h:11
#define assert(expression)
Definition assert.h:29
int errno_t
Definition errno_t.h:4
@ GDT_CS_RING0
Value to load into the CS register for kernel code.
Definition gdt.h:45
@ GDT_SS_RING0
Value to load into the SS register for kernel data.
Definition gdt.h:46
void simd_ctx_save(simd_ctx_t *ctx)
Definition simd.c:139
void simd_ctx_load(simd_ctx_t *ctx)
Definition simd.c:154
uint64_t simd_ctx_init(simd_ctx_t *ctx)
Definition simd.c:121
void simd_ctx_deinit(simd_ctx_t *ctx)
Definition simd.c:134
#define STACK_POINTER_GUARD_PAGES
The amount of guard pages to use for stacks.
uint64_t stack_pointer_init(stack_pointer_t *stack, uintptr_t maxAddress, uint64_t maxPages)
Initializes a stack pointer structure, does not allocate or map any memory.
void syscall_ctx_init(syscall_ctx_t *ctx, const stack_pointer_t *syscallStack)
Initialize a syscall context.
Definition syscall.c:16
#define SYSCALL_DEFINE(num, returnType,...)
Macro to define a syscall.
Definition syscall.h:144
void syscall_ctx_load(syscall_ctx_t *ctx)
Load the syscall context into the MSR_KERNEL_GS_BASE MSR.
Definition syscall.c:21
@ SYS_GETTID
Definition syscall.h:71
@ SYS_ERRNO
Definition syscall.h:69
void perf_thread_ctx_init(perf_thread_ctx_t *ctx)
Initializes a per-thread performance context.
Definition perf.c:130
uint64_t pathname_init(pathname_t *pathname, const char *string)
Initialize a pathname.
Definition path.c:89
uint64_t note_queue_length(note_queue_t *queue)
Get the length of a note queue.
Definition note.c:31
uint64_t note_queue_write(note_queue_t *queue, const void *buffer, uint64_t count)
Write a note to a note queue.
Definition note.c:42
void note_queue_init(note_queue_t *queue)
Initialize a note queue.
Definition note.c:22
#define LOG_DEBUG(format,...)
Definition log.h:100
void space_unpin(space_t *space, const void *address, uint64_t length)
Unpins pages in a region previously pinned with space_pin() or space_pin_string().
Definition space.c:452
uint64_t space_pin_terminated(space_t *space, const void *address, const void *terminator, uint8_t objectSize, uint64_t maxCount, stack_pointer_t *userStack)
Pins a region of memory terminated by a terminator value.
Definition space.c:379
uint64_t space_pin(space_t *space, const void *address, uint64_t length, stack_pointer_t *userStack)
Pins pages within a region of the address space.
Definition space.c:330
void space_load(space_t *space)
Loads a virtual address space.
Definition space.c:159
#define VMM_KERNEL_STACKS_MAX
The maximum address for kernel stacks.
Definition vmm.h:65
#define VMM_USER_SPACE_MAX
The maximum address for user space.
Definition vmm.h:74
process_t * process_get_kernel(void)
Gets the kernel process.
Definition process.c:943
@ PROCESS_DYING
Definition process.h:149
uint64_t thread_copy_from_user_string_array(thread_t *thread, const char **user, char ***out, uint64_t *outAmount)
Safely copy a null-terminated array of strings and their contents from user space into a string vecto...
Definition thread.c:294
bool thread_is_note_pending(thread_t *thread)
Check if a thread has a note pending.
Definition thread.c:156
void thread_free(thread_t *thread)
Frees a thread structure.
Definition thread.c:124
uint64_t thread_send_note(thread_t *thread, const void *buffer, uint64_t count)
Send a note to a thread.
Definition thread.c:161
uint64_t thread_copy_from_user(thread_t *thread, void *dest, const void *userSrc, uint64_t length)
Safely copy data from user space.
Definition thread.c:188
void thread_load(thread_t *thread, interrupt_frame_t *frame)
Load state from a thread.
Definition thread.c:147
uint64_t thread_copy_to_user(thread_t *thread, void *dest, const void *userSrc, uint64_t length)
Safely copy data to user space.
Definition thread.c:206
uint64_t thread_load_atomic_from_user(thread_t *thread, atomic_uint64_t *userObj, uint64_t *outValue)
Atomically load a 64-bit value from a user-space atomic variable.
Definition thread.c:333
uint64_t thread_copy_from_user_terminated(thread_t *thread, const void *userArray, const void *terminator, uint8_t objectSize, uint64_t maxCount, void **outArray, uint64_t *outCount)
Safely copy a null-terminated array of objects from user space.
Definition thread.c:224
uint64_t thread_copy_from_user_pathname(thread_t *thread, pathname_t *pathname, const char *userPath)
Safely copy a string from user space and use it to initialize a pathname.
Definition thread.c:265
thread_t * thread_new(process_t *process)
Creates a new thread structure.
Definition thread.c:70
void thread_save(thread_t *thread, const interrupt_frame_t *frame)
Save state to a thread.
Definition thread.c:140
thread_state_t
Thread state enum.
Definition thread.h:30
void(* thread_kernel_entry_t)(void *arg)
Kernel thread entry point function type.
Definition thread.h:103
tid_t thread_kernel_create(thread_kernel_entry_t entry, void *arg)
Creates a new thread that runs in kernel mode and submits it to the scheduler.
Definition thread.c:97
@ THREAD_UNBLOCKING
Has started unblocking, used to prevent the same thread being unblocked multiple times.
Definition thread.h:35
@ THREAD_BLOCKED
Is blocking and waiting in one or multiple wait queues.
Definition thread.h:34
@ THREAD_PARKED
Is doing nothing, not in a queue, not blocking, think of it as "other".
Definition thread.h:31
void wait_client_init(wait_client_t *client)
Initialize a threads wait client.
Definition wait.c:54
void wait_unblock_thread(thread_t *thread, errno_t err)
Unblock a specific thread.
Definition wait.c:284
void sched_client_init(sched_client_t *client)
Initialize the scheduler context for a thread.
Definition sched.c:102
thread_t * sched_thread(void)
Retrieves the currently running thread.
Definition sched.c:612
void sched_submit(thread_t *thread)
Submits a thread to the scheduler.
Definition sched.c:375
static void lock_release(lock_t *lock)
Releases a lock.
Definition lock.h:146
static void lock_acquire(lock_t *lock)
Acquires a lock, blocking until it is available.
Definition lock.h:103
#define REF(ptr)
Increment reference count.
Definition ref.h:65
#define UNREF(ptr)
Decrement reference count.
Definition ref.h:80
#define CONFIG_MAX_ARGC
Maximum argument vector configuration.
Definition config.h:79
#define CONFIG_MAX_USER_STACK_PAGES
User stack configuration.
Definition config.h:37
#define CONFIG_MAX_KERNEL_STACK_PAGES
Kernel stack configuration.
Definition config.h:26
#define EINVAL
Invalid argument.
Definition errno.h:142
#define EINTR
Interrupted system call.
Definition errno.h:52
#define ENOMEM
Out of memory.
Definition errno.h:92
#define errno
Error number variable.
Definition errno.h:27
static void list_push_back(list_t *list, list_entry_t *entry)
Pushes an entry to the end of the list.
Definition list.h:343
static void list_remove(list_t *list, list_entry_t *entry)
Removes a list entry from its current list.
Definition list.h:315
static void list_entry_init(list_entry_t *entry)
Initializes a list entry.
Definition list.h:182
#define PAGE_SIZE
The size of a memory page in bytes.
Definition proc.h:102
#define NULL
Pointer error value.
Definition NULL.h:23
#define ERR
Integer error value.
Definition ERR.h:17
__UINT64_TYPE__ tid_t
Thread Identifier.
Definition tid_t.h:12
static uint64_t thread_init(thread_t *thread, process_t *process)
Definition thread.c:24
static uintptr_t thread_id_to_offset(tid_t tid, uint64_t maxPages)
Definition thread.c:19
uint64_t maxPages
Definition mem.c:16
EFI_PHYSICAL_ADDRESS buffer
Definition mem.c:15
static atomic_long count
Definition main.c:10
#define RFLAGS_INTERRUPT_ENABLE
Definition regs.h:32
#define RFLAGS_ALWAYS_SET
Definition regs.h:24
#define atomic_compare_exchange_strong(object, expected, desired)
Definition stdatomic.h:278
#define atomic_load(object)
Definition stdatomic.h:288
#define atomic_init(obj, value)
Definition stdatomic.h:75
__UINT64_TYPE__ uint64_t
Definition stdint.h:17
__UINT8_TYPE__ uint8_t
Definition stdint.h:11
__UINTPTR_TYPE__ uintptr_t
Definition stdint.h:43
_PUBLIC void * malloc(size_t size)
Definition malloc.c:5
_PUBLIC void free(void *ptr)
Definition free.c:11
_PUBLIC void * memcpy(void *_RESTRICT s1, const void *_RESTRICT s2, size_t n)
Definition memcpy.c:61
_PUBLIC void * memset(void *s, int c, size_t n)
Definition memset.c:4
Trap Frame Structure.
Definition interrupt.h:143
Pathname structure.
Definition path.h:137
Process structure.
Definition process.h:158
space_t space
Definition process.h:163
pid_t id
Definition process.h:160
process_threads_t threads
Definition process.h:172
uintptr_t top
The top of the stack, this address is not inclusive.
Thread of execution structure.
Definition thread.h:63
perf_thread_ctx_t perf
Definition thread.h:82
list_entry_t processEntry
The entry for the parent process.
Definition thread.h:65
process_t * process
The parent process that the thread executes within.
Definition thread.h:64
interrupt_frame_t frame
Definition thread.h:87
stack_pointer_t kernelStack
The kernel stack of the thread.
Definition thread.h:75
errno_t error
Definition thread.h:74
note_queue_t notes
Definition thread.h:80
syscall_ctx_t syscall
Definition thread.h:81
stack_pointer_t userStack
The user stack of the thread.
Definition thread.h:76
wait_client_t wait
Definition thread.h:78
tid_t id
The thread id, unique within a process_t.
Definition thread.h:66
sched_client_t sched
Definition thread.h:77
simd_ctx_t simd
Definition thread.h:79