PatchworkOS  19e446b
A non-POSIX operating system.
Loading...
Searching...
No Matches
thread.c
Go to the documentation of this file.
3
4#include <kernel/cpu/cpu.h>
5#include <kernel/cpu/gdt.h>
6#include <kernel/init/init.h>
7#include <kernel/log/log.h>
8#include <kernel/log/panic.h>
9#include <kernel/mem/cache.h>
10#include <kernel/mem/vmm.h>
11#include <kernel/proc/process.h>
12#include <kernel/sched/sched.h>
13#include <kernel/sched/timer.h>
14#include <kernel/sched/wait.h>
15#include <kernel/sync/lock.h>
16
17#include <stdlib.h>
18#include <string.h>
19#include <sys/list.h>
20#include <sys/math.h>
21
22static void thread_ctor(void* ptr)
23{
24 thread_t* thread = (thread_t*)ptr;
25
26 thread->process = NULL;
27 thread->id = 0;
29 atomic_init(&thread->state, THREAD_PARKED);
30 thread->error = 0;
31 thread->kernelStack = (stack_pointer_t){0};
32 thread->userStack = (stack_pointer_t){0};
33 thread->wait = (wait_client_t){0};
34 thread->simd = (simd_ctx_t){0};
35 thread->notes = (note_queue_t){0};
36 thread->syscall = (syscall_ctx_t){0};
37 thread->perf = (perf_thread_ctx_t){0};
38 thread->rcu = (rcu_entry_t){0};
39 thread->fsBase = 0;
40 memset_s(&thread->frame, sizeof(interrupt_frame_t), 0, sizeof(interrupt_frame_t));
41}
42
43static cache_t cache = CACHE_CREATE(cache, "thread", sizeof(thread_t), CACHE_LINE, NULL, NULL);
44
49
51{
52 if (process == NULL)
53 {
54 errno = EINVAL;
55 return NULL;
56 }
57
58 thread_t* thread = cache_alloc(&cache);
59 if (thread == NULL)
60 {
61 errno = ENOMEM;
62 return NULL;
63 }
64
65 if (atomic_load(&process->flags) & PROCESS_DYING)
66 {
67 cache_free(thread);
68 errno = EINVAL;
69 return NULL;
70 }
71
72 thread->process = process;
73 thread->id = atomic_fetch_add_explicit(&process->threads.newTid, 1, memory_order_relaxed);
74 sched_client_init(&thread->sched);
75 atomic_store(&thread->state, THREAD_PARKED);
76 thread->error = 0;
80 {
81 cache_free(thread);
82 return NULL;
83 }
84 if (stack_pointer_init(&thread->userStack,
87 {
88 cache_free(thread);
89 return NULL;
90 }
91 wait_client_init(&thread->wait);
92 if (simd_ctx_init(&thread->simd) == ERR)
93 {
94 cache_free(thread);
95 return NULL;
96 }
97 note_queue_init(&thread->notes);
98 syscall_ctx_init(&thread->syscall, &thread->kernelStack);
99 perf_thread_ctx_init(&thread->perf);
100
101 thread->fsBase = 0;
102
103 REF(process);
104 lock_acquire(&process->threads.lock);
105 process->threads.count++;
106 list_push_back_rcu(&process->threads.list, &thread->processEntry);
107 lock_release(&process->threads.lock);
108 return thread;
109}
110
112{
113 lock_acquire(&thread->process->threads.lock);
114 thread->process->threads.count--;
116 lock_release(&thread->process->threads.lock);
117
118 UNREF(thread->process);
119 thread->process = NULL;
120
121 simd_ctx_deinit(&thread->simd);
122
123 rcu_call(&thread->rcu, rcu_call_cache_free, thread);
124}
125
127{
128 if (entry == NULL)
129 {
130 errno = EINVAL;
131 return ERR;
132 }
133
135 if (thread == NULL)
136 {
137 return ERR;
138 }
139
140 thread->frame.rip = (uintptr_t)entry;
141 thread->frame.rdi = (uintptr_t)arg;
142 thread->frame.rbp = thread->kernelStack.top;
143 thread->frame.rsp = thread->kernelStack.top;
144 thread->frame.cs = GDT_CS_RING0;
145 thread->frame.ss = GDT_SS_RING0;
147
148 tid_t volatile tid = thread->id;
149 sched_submit(thread);
150 return tid;
151}
152
153void thread_save(thread_t* thread, const interrupt_frame_t* frame)
154{
155 simd_ctx_save(&thread->simd);
156
157 thread->frame = *frame;
158}
159
161{
162 *frame = thread->frame;
163
164 vmm_load(&thread->process->space);
165 simd_ctx_load(&thread->simd);
166 syscall_ctx_load(&thread->syscall);
167
168 msr_write(MSR_FS_BASE, thread->fsBase);
169}
170
172{
173 return note_amount(&thread->notes) != 0;
174}
175
176uint64_t thread_send_note(thread_t* thread, const char* string)
177{
178 if (note_send(&thread->notes, string) == ERR)
179 {
180 return ERR;
181 }
182
184 if (atomic_compare_exchange_strong(&thread->state, &expected, THREAD_UNBLOCKING))
185 {
186 wait_unblock_thread(thread, EINTR);
187 }
188
189 return 0;
190}
191
193{
194 return errno;
195}
196
201
202uint64_t thread_copy_from_user(thread_t* thread, void* dest, const void* userSrc, uint64_t length)
203{
204 if (thread == NULL || dest == NULL || userSrc == NULL || length == 0)
205 {
206 errno = EINVAL;
207 return ERR;
208 }
209
210 if (space_pin(&thread->process->space, userSrc, length, &thread->userStack) == ERR)
211 {
212 return ERR;
213 }
214
215 memcpy(dest, userSrc, length);
216 space_unpin(&thread->process->space, userSrc, length);
217 return 0;
218}
219
220uint64_t thread_copy_to_user(thread_t* thread, void* userDest, const void* src, uint64_t length)
221{
222 if (thread == NULL || userDest == NULL || src == NULL || length == 0)
223 {
224 errno = EINVAL;
225 return ERR;
226 }
227
228 if (space_pin(&thread->process->space, userDest, length, &thread->userStack) == ERR)
229 {
230 return ERR;
231 }
232
233 memcpy(userDest, src, length);
234 space_unpin(&thread->process->space, userDest, length);
235 return 0;
236}
237
238uint64_t thread_copy_from_user_terminated(thread_t* thread, const void* userArray, const void* terminator,
239 uint8_t objectSize, uint64_t maxCount, void** outArray, uint64_t* outCount)
240{
241 if (thread == NULL || userArray == NULL || terminator == NULL || objectSize == 0 || maxCount == 0 ||
242 outArray == NULL)
243 {
244 errno = EINVAL;
245 return ERR;
246 }
247
248 uint64_t arraySize =
249 space_pin_terminated(&thread->process->space, userArray, terminator, objectSize, maxCount, &thread->userStack);
250 if (arraySize == ERR)
251 {
252 return ERR;
253 }
254
255 uint64_t elementCount = arraySize / objectSize;
256 uint64_t allocSize = arraySize;
257
258 void* kernelArray = malloc(allocSize);
259 if (kernelArray == NULL)
260 {
261 space_unpin(&thread->process->space, userArray, arraySize);
262 errno = ENOMEM;
263 return ERR;
264 }
265
266 memcpy(kernelArray, userArray, arraySize);
267 space_unpin(&thread->process->space, userArray, arraySize);
268
269 *outArray = kernelArray;
270 if (outCount != NULL)
271 {
272 *outCount = elementCount - 1;
273 }
274
275 return 0;
276}
277
278uint64_t thread_copy_from_user_string(thread_t* thread, char* dest, const char* userSrc, uint64_t size)
279{
280 if (thread == NULL || dest == NULL || userSrc == NULL || size <= 1)
281 {
282 errno = EINVAL;
283 return ERR;
284 }
285
286 char terminator = '\0';
287 uint64_t strLength =
288 space_pin_terminated(&thread->process->space, userSrc, &terminator, sizeof(char), size - 1, &thread->userStack);
289 if (strLength == ERR)
290 {
291 return ERR;
292 }
293 dest[size - 1] = '\0';
294
295 memcpy(dest, userSrc, strLength);
296 space_unpin(&thread->process->space, userSrc, strLength);
297 return 0;
298}
299
300uint64_t thread_copy_from_user_pathname(thread_t* thread, pathname_t* pathname, const char* userPath)
301{
302 if (thread == NULL || pathname == NULL || userPath == NULL)
303 {
304 errno = EINVAL;
305 return ERR;
306 }
307
308 char terminator = '\0';
309 uint64_t pathLength = space_pin_terminated(&thread->process->space, userPath, &terminator, sizeof(char), MAX_PATH,
310 &thread->userStack);
311 if (pathLength == ERR)
312 {
313 return ERR;
314 }
315
316 char copy[MAX_PATH];
317 memcpy(copy, userPath, pathLength);
318 space_unpin(&thread->process->space, userPath, pathLength);
319
320 if (pathname_init(pathname, copy) == ERR)
321 {
322 return ERR;
323 }
324
325 return 0;
326}
327
328uint64_t thread_copy_from_user_string_array(thread_t* thread, const char** user, char*** out, uint64_t* outAmount)
329{
330 if (thread == NULL || user == NULL || out == NULL)
331 {
332 errno = EINVAL;
333 return ERR;
334 }
335
336 char** copy;
337 uint64_t amount;
338 char* terminator = NULL;
339 if (thread_copy_from_user_terminated(thread, (void*)user, (void*)&terminator, sizeof(char*), CONFIG_MAX_ARGC,
340 (void**)&copy, &amount) == ERR)
341 {
342 return ERR;
343 }
344
345 for (uint64_t i = 0; i < amount; i++)
346 {
347 char* strCopy;
348 uint64_t strLen;
349 char strTerminator = '\0';
350 if (thread_copy_from_user_terminated(thread, copy[i], &strTerminator, sizeof(char), MAX_PATH, (void**)&strCopy,
351 &strLen) == ERR)
352 {
353 for (uint64_t j = 0; j < i; j++)
354 {
355 free(copy[j]);
356 }
357 free((void*)copy);
358 return ERR;
359 }
360
361 copy[i] = strCopy;
362 }
363
364 *out = copy;
365 if (outAmount != NULL)
366 {
367 *outAmount = amount;
368 }
369
370 return 0;
371}
372
373uint64_t thread_load_atomic_from_user(thread_t* thread, atomic_uint64_t* userObj, uint64_t* outValue)
374{
375 if (thread == NULL || userObj == NULL || outValue == NULL)
376 {
377 errno = EINVAL;
378 return ERR;
379 }
380
381 if (space_pin(&thread->process->space, userObj, sizeof(atomic_uint64_t), &thread->userStack) == ERR)
382 {
383 return ERR;
384 }
385
386 *outValue = atomic_load(userObj);
387 space_unpin(&thread->process->space, userObj, sizeof(atomic_uint64_t));
388 return 0;
389}
390
392{
393 thread_t* thread = thread_current();
394
395 switch (op)
396 {
397 case ARCH_SET_FS:
398 thread->fsBase = addr;
399 msr_write(MSR_FS_BASE, addr);
400 return 0;
401 case ARCH_GET_FS:
402 if (thread_copy_to_user(thread, (void*)addr, &thread->fsBase, sizeof(uintptr_t)) == ERR)
403 {
404 return ERR;
405 }
406 return 0;
407 default:
408 errno = EINVAL;
409 return ERR;
410 }
411}
#define MAX_PATH
Maximum length of filepaths.
Definition MAX_PATH.h:11
size_t maxPages
Definition main.c:238
int errno_t
Definition errno_t.h:4
#define CONFIG_MAX_ARGC
Maximum argument vector configuration.
Definition config.h:79
#define CONFIG_MAX_USER_STACK_PAGES
User stack configuration.
Definition config.h:42
#define CONFIG_MAX_KERNEL_STACK_PAGES
Kernel stack configuration.
Definition config.h:32
#define GDT_SS_RING0
Value to load into the SS register for kernel data.
Definition gdt.h:36
#define GDT_CS_RING0
Value to load into the CS register for kernel code.
Definition gdt.h:35
void simd_ctx_save(simd_ctx_t *ctx)
Definition simd.c:142
void simd_ctx_load(simd_ctx_t *ctx)
Definition simd.c:157
uint64_t simd_ctx_init(simd_ctx_t *ctx)
Definition simd.c:123
void simd_ctx_deinit(simd_ctx_t *ctx)
Definition simd.c:136
#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:28
#define SYSCALL_DEFINE(num, returnType,...)
Macro to define a syscall.
Definition syscall.h:172
void syscall_ctx_load(syscall_ctx_t *ctx)
Load the syscall context into the MSR_KERNEL_GS_BASE MSR.
Definition syscall.c:36
@ SYS_GETTID
Definition syscall.h:71
@ SYS_ERRNO
Definition syscall.h:69
@ SYS_ARCH_PRCTL
Definition syscall.h:104
void perf_thread_ctx_init(perf_thread_ctx_t *ctx)
Initializes a per-thread performance context.
Definition perf.c:144
uint64_t pathname_init(pathname_t *pathname, const char *string)
Initialize a pathname.
Definition path.c:116
uint64_t note_send(note_queue_t *queue, const char *string)
Write a note to a note queue.
Definition note.c:46
uint64_t note_amount(note_queue_t *queue)
The amount of pending notes in a note queue, including special notes.
Definition note.c:35
void note_queue_init(note_queue_t *queue)
Initialize a note queue.
Definition note.c:25
void cache_free(void *obj)
Free an object back to its cache.
Definition cache.c:205
#define CACHE_LINE
Cache line size in bytes.
Definition cache.h:75
void * cache_alloc(cache_t *cache)
Allocate an object from the cache.
Definition cache.c:109
#define CACHE_CREATE(_cache, _name, _size, _alignment, _ctor, _dtor)
Macro to create a cache initializer.
Definition cache.h:148
uint64_t space_pin_terminated(space_t *space, const void *address, const void *terminator, size_t objectSize, size_t maxCount, stack_pointer_t *userStack)
Pins a region of memory terminated by a terminator value.
Definition space.c:330
void space_unpin(space_t *space, const void *address, size_t length)
Unpins pages in a region previously pinned with space_pin() or space_pin_string().
Definition space.c:418
uint64_t space_pin(space_t *space, const void *address, size_t length, stack_pointer_t *userStack)
Pins pages within a region of the address space.
Definition space.c:281
#define VMM_KERNEL_STACKS_MAX
The maximum address for kernel stacks.
Definition vmm.h:65
void vmm_load(space_t *space)
Loads a virtual address space.
Definition vmm.c:442
#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:374
@ PROCESS_DYING
Definition process.h:40
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:328
bool thread_is_note_pending(thread_t *thread)
Check if a thread has a note pending.
Definition thread.c:171
void thread_free(thread_t *thread)
Frees a thread structure.
Definition thread.c:111
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:202
void thread_load(thread_t *thread, interrupt_frame_t *frame)
Load state from a thread.
Definition thread.c:160
static thread_t * thread_current(void)
Retrieves the currently running thread.
Definition thread.h:126
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:373
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:238
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:300
uint64_t thread_copy_from_user_string(thread_t *thread, char *dest, const char *userSrc, uint64_t size)
Safely copy a string from user space.
Definition thread.c:278
thread_t * thread_new(process_t *process)
Creates a new thread structure.
Definition thread.c:50
uint64_t thread_copy_to_user(thread_t *thread, void *userDest, const void *src, uint64_t length)
Safely copy data to user space.
Definition thread.c:220
void thread_save(thread_t *thread, const interrupt_frame_t *frame)
Save state to a thread.
Definition thread.c:153
thread_state_t
Thread state enum.
Definition thread.h:35
void(* thread_kernel_entry_t)(void *arg)
Kernel thread entry point function type.
Definition thread.h:110
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:126
uint64_t thread_send_note(thread_t *thread, const char *string)
Send a note to a thread.
Definition thread.c:176
@ THREAD_UNBLOCKING
Has started unblocking, used to prevent the same thread being unblocked multiple times.
Definition thread.h:40
@ THREAD_BLOCKED
Is blocking and waiting in one or multiple wait queues.
Definition thread.h:39
@ THREAD_PARKED
Is doing nothing, not in a queue, not blocking, think of it as "other".
Definition thread.h:36
void wait_client_init(wait_client_t *client)
Initialize a threads wait client.
Definition wait.c:67
void wait_unblock_thread(thread_t *thread, errno_t err)
Unblock a specific thread.
Definition wait.c:295
void sched_client_init(sched_client_t *client)
Initialize the scheduler context for a thread.
Definition sched.c:132
void sched_submit(thread_t *thread)
Submits a thread to the scheduler.
Definition sched.c:378
static void lock_release(lock_t *lock)
Releases a lock.
Definition lock.h:175
static void lock_acquire(lock_t *lock)
Acquires a lock, blocking until it is available.
Definition lock.h:96
void rcu_call_cache_free(void *arg)
Helper callback to free a cache object.
Definition rcu.c:193
void rcu_call(rcu_entry_t *entry, rcu_callback_t func, void *arg)
Add a callback to be executed after a grace period.
Definition rcu.c:72
#define REF(ptr)
Increment reference count.
Definition ref.h:82
#define UNREF(ptr)
Decrement reference count.
Definition ref.h:109
#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_remove_rcu(list_entry_t *entry)
Removes a list entry from its current list in a RCU-safe manner.
Definition list.h:307
static void list_entry_init(list_entry_t *entry)
Initializes a list entry.
Definition list.h:173
static void list_push_back_rcu(list_t *list, list_entry_t *entry)
Pushes an entry to the end of the list in a RCU-safe manner.
Definition list.h:337
arch_prctl_t
Architecture specific thread data codes.
Definition proc.h:347
@ ARCH_GET_FS
Get the FS base address.
Definition proc.h:348
@ ARCH_SET_FS
Set the FS base address.
Definition proc.h:349
#define NULL
Pointer error value.
Definition NULL.h:25
#define ERR
Integer error value.
Definition ERR.h:17
#define PAGE_SIZE
The size of a memory page in bytes.
Definition PAGE_SIZE.h:8
__UINT64_TYPE__ tid_t
Thread Identifier.
Definition tid_t.h:12
errno_t memset_s(void *s, rsize_t smax, int c, rsize_t n)
Definition memset_s.c:9
#define RFLAGS_INTERRUPT_ENABLE
Definition regs.h:34
static void msr_write(uint32_t msr, uint64_t value)
Definition regs.h:73
#define RFLAGS_ALWAYS_SET
Definition regs.h:26
#define MSR_FS_BASE
Definition regs.h:19
#define atomic_store(object, desired)
Definition stdatomic.h:289
@ memory_order_relaxed
Definition stdatomic.h:116
#define atomic_fetch_add_explicit(object, operand, order)
Definition stdatomic.h:259
#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
Cache structure.
Definition cache.h:123
Trap Frame Structure.
Definition interrupt.h:195
Per-thread note queue.
Definition note.h:141
Pathname structure.
Definition path.h:139
Per-Thread performance context.
Definition perf.h:64
Process structure.
Definition process.h:76
space_t space
Definition process.h:84
process_threads_t threads
Definition process.h:96
uint64_t count
Definition process.h:52
list_t list
Reads are RCU protected, writes require the lock.
Definition process.h:51
Intrusive RCU head structure.
Definition rcu.h:65
Structure to define a stack in memory.
uintptr_t top
The top of the stack, this address is not inclusive.
Per thread syscall context.
Definition syscall.h:133
Thread of execution structure.
Definition thread.h:61
perf_thread_ctx_t perf
Definition thread.h:80
list_entry_t processEntry
The entry for the parent process.
Definition thread.h:63
process_t * process
The parent process that the thread executes within.
Definition thread.h:62
interrupt_frame_t frame
Definition thread.h:87
rcu_entry_t rcu
Definition thread.h:81
stack_pointer_t kernelStack
The kernel stack of the thread.
Definition thread.h:73
uintptr_t fsBase
The FS base address for the thread.
Definition thread.h:82
errno_t error
Definition thread.h:72
note_queue_t notes
Definition thread.h:78
syscall_ctx_t syscall
Definition thread.h:79
stack_pointer_t userStack
The user stack of the thread.
Definition thread.h:74
wait_client_t wait
Definition thread.h:76
tid_t id
The thread id, unique within a process_t.
Definition thread.h:64
sched_client_t sched
Definition thread.h:75
simd_ctx_t simd
Definition thread.h:77
Represents a thread in the waiting subsystem.
Definition wait.h:198
static void thread_ctor(void *ptr)
Definition thread.c:22
static cache_t cache
Definition thread.c:43
static uintptr_t thread_id_to_offset(tid_t tid, uint64_t maxPages)
Definition thread.c:45