PatchworkOS
Loading...
Searching...
No Matches
thread.c
Go to the documentation of this file.
2
3#include <kernel/cpu/gdt.h>
4#include <kernel/cpu/smp.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
20static bool bootThreadInitalized = false;
21
26
27static uint64_t thread_init(thread_t* thread, process_t* process)
28{
29 list_entry_init(&thread->entry);
30 thread->process = REF(process);
32 thread->id = thread->process->threads.newTid++;
34 atomic_init(&thread->state, THREAD_PARKED);
35 thread->error = 0;
39 {
40 DEREF(process);
41 return ERR;
42 }
43 if (stack_pointer_init(&thread->userStack,
46 {
47 DEREF(process);
48 return ERR;
49 }
50 wait_thread_ctx_init(&thread->wait);
51 if (simd_ctx_init(&thread->simd) == ERR)
52 {
53 DEREF(process);
54 return ERR;
55 }
56 note_queue_init(&thread->notes);
57 syscall_ctx_init(&thread->syscall, &thread->kernelStack);
58
59 memset(&thread->frame, 0, sizeof(interrupt_frame_t));
60
61 lock_acquire(&process->threads.lock);
62 list_push(&process->threads.list, &thread->processEntry);
63 lock_release(&process->threads.lock);
64
65 LOG_DEBUG("created tid=%d pid=%d\n", thread->id, process->id);
66 return 0;
67}
68
70{
71 if (atomic_load(&process->isDying))
72 {
73 return NULL;
74 }
75
76 thread_t* thread = malloc(sizeof(thread_t));
77 if (thread == NULL)
78 {
79 return NULL;
80 }
81 if (thread_init(thread, process) == ERR)
82 {
83 free(thread);
84 return NULL;
85 }
86 return thread;
87}
88
89void thread_free(thread_t* thread)
90{
91 LOG_DEBUG("freeing tid=%d pid=%d\n", thread->id, thread->process->id);
92
93 process_t* process = thread->process;
94 assert(process != NULL);
95
96 lock_acquire(&process->threads.lock);
97 list_remove(&process->threads.list, &thread->processEntry);
98 lock_release(&process->threads.lock);
99
100 DEREF(process);
101 thread->process = NULL;
102
103 simd_ctx_deinit(&thread->simd);
104 free(thread);
105}
106
107void thread_save(thread_t* thread, const interrupt_frame_t* frame)
108{
109 simd_ctx_save(&thread->simd);
110 thread->frame = *frame;
111}
112
114{
115 *frame = thread->frame;
116
117 space_load(&thread->process->space);
118 simd_ctx_load(&thread->simd);
119 syscall_ctx_load(&thread->syscall);
120}
121
123{
124 *frame = thread->frame;
125}
126
128{
129 return note_queue_length(&thread->notes) != 0;
130}
131
133{
134 if (note_queue_write(&thread->notes, buffer, count) == ERR)
135 {
136 return ERR;
137 }
138
140 if (atomic_compare_exchange_strong(&thread->state, &expected, THREAD_UNBLOCKING))
141 {
142 LOG_DEBUG("notifying thread tid=%d pid=%d of pending note\n", thread->id, thread->process->id);
143 wait_unblock_thread(thread, EINTR);
144 }
145
146 return 0;
147}
148
150{
151 return errno;
152}
153
155{
156 return sched_thread()->id;
157}
158
160{
162 {
164 {
165 panic(NULL, "Failed to initialize boot thread");
166 }
167 LOG_INFO("boot thread initialized with pid=%d tid=%d\n", bootThread.process->id, bootThread.id);
169 }
170 return &bootThread;
171}
172
174{
175 thread_t* thread = sched_thread_unsafe();
176 if (thread == NULL)
177 {
178 return ERR;
179 }
180 uintptr_t faultAddr = (uintptr_t)cr2_read();
181
182 if (frame->errorCode & PAGE_FAULT_PRESENT)
183 {
184 errno = EFAULT;
185 return ERR;
186 }
187
188 uintptr_t alignedFaultAddr = ROUND_DOWN(faultAddr, PAGE_SIZE);
189 if (stack_pointer_is_in_stack(&thread->userStack, alignedFaultAddr, 1))
190 {
191 if (vmm_alloc(&thread->process->space, (void*)alignedFaultAddr, PAGE_SIZE, PML_WRITE | PML_PRESENT | PML_USER,
193 {
194 if (errno == EEXIST) // Race condition, another CPU mapped the page.
195 {
196 return 0;
197 }
198
199 return ERR;
200 }
201 memset((void*)alignedFaultAddr, 0, PAGE_SIZE);
202 return 0;
203 }
204
206 {
207 errno = EFAULT;
208 return ERR;
209 }
210
211 if (stack_pointer_is_in_stack(&thread->kernelStack, alignedFaultAddr, 1))
212 {
213 if (vmm_alloc(&thread->process->space, (void*)alignedFaultAddr, PAGE_SIZE, PML_WRITE | PML_PRESENT,
215 {
216 if (errno == EEXIST) // Race condition, another CPU mapped the page.
217 {
218 return 0;
219 }
220 return ERR;
221 }
222 memset((void*)alignedFaultAddr, 0, PAGE_SIZE);
223 return 0;
224 }
225
226 errno = EFAULT;
227 return ERR;
228}
229
230uint64_t thread_copy_from_user(thread_t* thread, void* dest, const void* userSrc, uint64_t length)
231{
232 if (thread == NULL || dest == NULL || userSrc == NULL || length == 0)
233 {
234 errno = EINVAL;
235 return ERR;
236 }
237
238 if (space_pin(&thread->process->space, userSrc, length, &thread->userStack) == ERR)
239 {
240 return ERR;
241 }
242
243 memcpy(dest, userSrc, length);
244 space_unpin(&thread->process->space, userSrc, length);
245 return 0;
246}
247
248uint64_t thread_copy_to_user(thread_t* thread, void* dest, const void* userSrc, uint64_t length)
249{
250 if (thread == NULL || dest == NULL || userSrc == NULL || length == 0)
251 {
252 errno = EINVAL;
253 return ERR;
254 }
255
256 if (space_pin(&thread->process->space, dest, length, &thread->userStack) == ERR)
257 {
258 return ERR;
259 }
260
261 memcpy(dest, userSrc, length);
262 space_unpin(&thread->process->space, dest, length);
263 return 0;
264}
265
266uint64_t thread_copy_from_user_terminated(thread_t* thread, const void* userArray, const void* terminator,
267 uint8_t objectSize, uint64_t maxCount, void** outArray, uint64_t* outCount)
268{
269 if (thread == NULL || userArray == NULL || terminator == NULL || objectSize == 0 || maxCount == 0 ||
270 outArray == NULL)
271 {
272 errno = EINVAL;
273 return ERR;
274 }
275
276 uint64_t arraySize =
277 space_pin_terminated(&thread->process->space, userArray, terminator, objectSize, maxCount, &thread->userStack);
278 if (arraySize == ERR)
279 {
280 return ERR;
281 }
282
283 uint64_t elementCount = arraySize / objectSize;
284 uint64_t allocSize = (elementCount + 1) * objectSize; // +1 for terminator
285
286 void* kernelArray = malloc(allocSize);
287 if (kernelArray == NULL)
288 {
289 space_unpin(&thread->process->space, userArray, arraySize);
290 errno = ENOMEM;
291 return ERR;
292 }
293
294 memcpy(kernelArray, userArray, arraySize);
295 memcpy((uint8_t*)kernelArray + arraySize, terminator, objectSize); // Add terminator
296 space_unpin(&thread->process->space, userArray, arraySize);
297
298 *outArray = kernelArray;
299 if (outCount != NULL)
300 {
301 *outCount = elementCount;
302 }
303
304 return 0;
305}
306
307uint64_t thread_copy_from_user_pathname(thread_t* thread, pathname_t* pathname, const char* userPath)
308{
309 if (thread == NULL || pathname == NULL || userPath == NULL)
310 {
311 errno = EINVAL;
312 return ERR;
313 }
314
315 char terminator = '\0';
316 uint64_t pathLength = space_pin_terminated(&thread->process->space, userPath, &terminator, sizeof(char), MAX_PATH,
317 &thread->userStack);
318 if (pathLength == ERR)
319 {
320 return ERR;
321 }
322
323 char copy[MAX_PATH];
324 memcpy(copy, userPath, pathLength);
325 copy[pathLength] = '\0';
326 space_unpin(&thread->process->space, userPath, pathLength);
327
328 if (pathname_init(pathname, copy) == ERR)
329 {
330 return ERR;
331 }
332
333 return 0;
334}
335
336uint64_t thread_load_atomic_from_user(thread_t* thread, atomic_uint64_t* userObj, uint64_t* outValue)
337{
338 if (thread == NULL || userObj == NULL || outValue == NULL)
339 {
340 errno = EINVAL;
341 return ERR;
342 }
343
344 if (space_pin(&thread->process->space, userObj, sizeof(atomic_uint64_t), &thread->userStack) == ERR)
345 {
346 return ERR;
347 }
348
349 *outValue = atomic_load(userObj);
350 space_unpin(&thread->process->space, userObj, sizeof(atomic_uint64_t));
351 return 0;
352}
#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
#define INTERRUPT_FRAME_IN_USER_SPACE(frame)
Checks if a interrupt frame is from user space.
Definition interrupt.h:76
@ PAGE_FAULT_PRESENT
Definition interrupt.h:24
void simd_ctx_save(simd_ctx_t *ctx)
Definition simd.c:104
void simd_ctx_load(simd_ctx_t *ctx)
Definition simd.c:119
uint64_t simd_ctx_init(simd_ctx_t *ctx)
Definition simd.c:86
void simd_ctx_deinit(simd_ctx_t *ctx)
Definition simd.c:99
#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.
bool stack_pointer_is_in_stack(stack_pointer_t *stack, uintptr_t addr, uint64_t length)
Check if an region is within the stack.
#define SYS_ERRNO
Definition syscalls.h:26
#define SYSCALL_DEFINE(num, returnType,...)
Macro to define a syscall.
Definition syscalls.h:100
void syscall_ctx_init(syscall_ctx_t *ctx, stack_pointer_t *kernelStack)
Initialize a per-thread syscall context.
Definition syscalls.c:50
void syscall_ctx_load(syscall_ctx_t *ctx)
Load a syscall context into the CPU.
Definition syscalls.c:56
#define SYS_GETTID
Definition syscalls.h:28
uint64_t pathname_init(pathname_t *pathname, const char *string)
Initialize a pathname.
Definition path.c:100
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:37
void note_queue_init(note_queue_t *queue)
Initialize a note queue.
Definition note.c:22
NORETURN void panic(const interrupt_frame_t *frame, const char *format,...)
Panic the kernel, printing a message and halting.
Definition panic.c:362
#define LOG_INFO(format,...)
Definition log.h:87
#define LOG_DEBUG(format,...)
Definition log.h:81
@ PML_USER
@ PML_PRESENT
@ PML_WRITE
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:448
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:382
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:334
void space_load(space_t *space)
Loads a virtual address space.
Definition space.c:161
#define VMM_KERNEL_STACKS_MAX
The maximum address for kernel stacks.
Definition vmm.h:65
void * vmm_alloc(space_t *space, void *virtAddr, uint64_t length, pml_flags_t pmlFlags, vmm_alloc_flags_t allocFlags)
Allocates and maps virtual memory in a given address space.
Definition vmm.c:168
#define VMM_USER_SPACE_MAX
The maximum address for user space.
Definition vmm.h:74
@ VMM_ALLOC_FAIL_IF_MAPPED
If set and any page is already mapped, fail and set errno to EEXIST.
Definition vmm.h:124
process_t * process_get_kernel(void)
Gets the kernel process.
Definition process.c:594
bool thread_is_note_pending(thread_t *thread)
Check if a thread has a note pending.
Definition thread.c:127
void thread_free(thread_t *thread)
Frees a thread structure.
Definition thread.c:89
uint64_t thread_send_note(thread_t *thread, const void *buffer, uint64_t count)
Send a note to a thread.
Definition thread.c:132
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:230
void thread_load(thread_t *thread, interrupt_frame_t *frame)
Load state from a thread.
Definition thread.c:113
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:248
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:336
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:266
thread_t * thread_get_boot(void)
Retrieves the boot thread.
Definition thread.c:159
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:307
thread_t * thread_new(process_t *process)
Creates a new thread structure.
Definition thread.c:69
uint64_t thread_handle_page_fault(const interrupt_frame_t *frame)
Handles a page fault that occurred in the currently running thread.
Definition thread.c:173
void thread_save(thread_t *thread, const interrupt_frame_t *frame)
Save state to a thread.
Definition thread.c:107
thread_state_t
Thread state enum.
Definition thread.h:29
void thread_get_interrupt_frame(thread_t *thread, interrupt_frame_t *frame)
Retrieve the interrupt frame from a thread.
Definition thread.c:122
@ 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:30
void wait_thread_ctx_init(wait_thread_ctx_t *wait)
Initialize per-thread wait context.
Definition wait.c:87
void wait_unblock_thread(thread_t *thread, errno_t err)
Unblock a specific thread.
Definition wait.c:156
thread_t * sched_thread(void)
Retrieves the currently running thread.
Definition sched.c:157
void sched_thread_ctx_init(sched_thread_ctx_t *ctx)
Initializes a thread's scheduling context.
Definition sched.c:69
thread_t * sched_thread_unsafe(void)
Retrieves the currently running thread without disabling interrupts.
Definition sched.c:175
static void lock_release(lock_t *lock)
Releases a lock.
Definition lock.h:140
static void lock_acquire(lock_t *lock)
Acquires a lock, blocking until it is available.
Definition lock.h:97
#define REF(ptr)
Increment reference count.
Definition ref.h:65
#define DEREF(ptr)
Decrement reference count.
Definition ref.h:80
#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 EEXIST
File exists.
Definition errno.h:117
#define EINVAL
Invalid argument.
Definition errno.h:142
#define EFAULT
Bad address.
Definition errno.h:102
#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(list_t *list, list_entry_t *entry)
Removes a list entry from its current list.
Definition list.h:317
static void list_push(list_t *list, list_entry_t *entry)
Pushes an entry to the end of the list.
Definition list.h:345
static void list_entry_init(list_entry_t *entry)
Initializes a list entry.
Definition list.h:184
#define ROUND_DOWN(number, multiple)
Definition math.h:21
#define PAGE_SIZE
Memory page size.
Definition proc.h:140
#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 bool bootThreadInitalized
Definition thread.c:20
static uint64_t thread_init(thread_t *thread, process_t *process)
Definition thread.c:27
static uintptr_t thread_id_to_offset(tid_t tid, uint64_t maxPages)
Definition thread.c:22
static thread_t bootThread
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:9
static uint64_t cr2_read()
Definition regs.h:114
#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:4
_PUBLIC void * memset(void *s, int c, size_t n)
Definition memset.c:4
Trap Frame Structure.
Definition interrupt.h:42
uint64_t errorCode
Definition interrupt.h:60
Pathname structure.
Definition path.h:122
Process structure.
Definition process.h:53
space_t space
Definition process.h:59
pid_t id
Definition process.h:55
process_threads_t threads
Definition process.h:65
atomic_bool isDying
Definition process.h:64
Thread of execution structure.
Definition thread.h:55
list_entry_t processEntry
The entry for the parent process.
Definition thread.h:58
sched_thread_ctx_t sched
Definition thread.h:70
process_t * process
The parent process that the thread executes within.
Definition thread.h:57
interrupt_frame_t frame
Definition thread.h:79
list_entry_t entry
The entry for the scheduler and wait system.
Definition thread.h:56
stack_pointer_t kernelStack
The kernel stack of the thread.
Definition thread.h:68
errno_t error
Definition thread.h:67
note_queue_t notes
Definition thread.h:73
syscall_ctx_t syscall
Definition thread.h:74
stack_pointer_t userStack
The user stack of the thread.
Definition thread.h:69
wait_thread_ctx_t wait
Definition thread.h:71
tid_t id
The thread id, unique within a process_t.
Definition thread.h:59
simd_ctx_t simd
Definition thread.h:72