PatchworkOS  da8a090
A non-POSIX operating system.
Loading...
Searching...
No Matches
loader.c
Go to the documentation of this file.
1#include <kernel/fs/dentry.h>
3#include <kernel/fs/path.h>
5#include <kernel/cpu/gdt.h>
6#include <kernel/fs/vfs.h>
7#include <kernel/log/log.h>
8#include <kernel/mem/vmm.h>
10#include <kernel/sched/sched.h>
11#include <kernel/sched/thread.h>
12
13#include <errno.h>
14#include <kernel/sched/wait.h>
15#include <stdlib.h>
16#include <string.h>
17#include <sys/elf.h>
18#include <sys/math.h>
19#include <sys/proc.h>
20
21static void loader_strv_free(char** array, uint64_t amount)
22{
23 if (array == NULL)
24 {
25 return;
26 }
27
28 for (uint64_t i = 0; i < amount; i++)
29 {
30 if (array[i] == NULL)
31 {
32 continue;
33 }
34 free(array[i]);
35 }
36 free((void*)array);
37}
38
39void loader_exec(const char* executable, char** argv, uint64_t argc)
40{
41 assert(executable != NULL);
42 assert((argv != NULL && argc > 0) || ((argv == NULL || argv[0] == NULL) && argc == 0));
43
44 // Generic error if a lower function fails without setting errno
46
47 thread_t* thread = sched_thread();
48 process_t* process = thread->process;
49
50 file_t* file = NULL;
51 void* fileData = NULL;
52
53 file = vfs_open(PATHNAME(executable), process);
54 if (file == NULL)
55 {
56 goto cleanup;
57 }
58
59 uint64_t fileSize = vfs_seek(file, 0, SEEK_END);
61
62 fileData = malloc(fileSize);
63 if (fileData == NULL)
64 {
65 goto cleanup;
66 }
67
68 uint64_t readSize = vfs_read(file, fileData, fileSize);
69 if (readSize != fileSize)
70 {
71 goto cleanup;
72 }
73
74 Elf64_File elf;
75 if (elf64_validate(&elf, fileData, fileSize) != 0)
76 {
77 goto cleanup;
78 }
79
80 Elf64_Addr minAddr = UINT64_MAX;
81 Elf64_Addr maxAddr = 0;
82 elf64_get_loadable_bounds(&elf, &minAddr, &maxAddr);
83 uint64_t loadSize = maxAddr - minAddr;
84
85 if (vmm_alloc(&process->space, (void*)minAddr, loadSize, PML_USER | PML_WRITE | PML_PRESENT, VMM_ALLOC_OVERWRITE) ==
86 NULL)
87 {
88 goto cleanup;
89 }
90
91 elf64_load_segments(&elf, 0, 0);
92
93 if (process_set_cmdline(process, argv, argc) == ERR)
94 {
95 goto cleanup;
96 }
97
98 char* rsp = (char*)thread->userStack.top;
99 for (int64_t i = argc - 1; i >= 0; i--)
100 {
101 size_t len = strlen(argv[i]) + 1;
102 rsp -= len;
103 memcpy(rsp, argv[i], len);
104
105 free(argv[i]);
106 argv[i] = rsp;
107 }
108 rsp -= sizeof(char*);
109 *((char**)rsp) = NULL;
110 for (int64_t i = argc - 1; i >= 0; i--)
111 {
112 rsp -= sizeof(char*);
113 *((char**)rsp) = argv[i];
114 argv[i] = NULL;
115 }
116
117 // Disable interrupts, they will be enabled when we jump to user space.
118 asm volatile("cli");
119
120 memset(&thread->frame, 0, sizeof(interrupt_frame_t));
121 thread->frame.rsp = ROUND_DOWN((uintptr_t)rsp - sizeof(uint64_t), 16);
122 thread->frame.rip = elf.header->e_entry;
123 thread->frame.rdi = argc;
124 thread->frame.rsi = (uintptr_t)rsp;
125 thread->frame.cs = GDT_CS_RING3;
126 thread->frame.ss = GDT_SS_RING3;
128
129 errno = EOK;
130cleanup:
131 if (file != NULL)
132 {
133 UNREF(file);
134 }
135 if (fileData != NULL)
136 {
137 free(fileData);
138 }
139 free((void*)executable);
140 loader_strv_free(argv, argc);
141 if (errno == EOK)
142 {
143 thread_jump(thread);
144 }
146}
147
148static void loader_entry(const char* executable, char** argv, uint64_t argc)
149{
150 thread_t* thread = sched_thread();
151
152 WAIT_BLOCK(&thread->process->suspendQueue, !(atomic_load(&thread->process->flags) & PROCESS_SUSPENDED));
153
154 loader_exec(executable, argv, argc);
155}
156
158{
159 process_t* child = NULL;
160 thread_t* childThread = NULL;
161
162 uint64_t argc = 0;
163 char** argvCopy = NULL;
164 char* executable = NULL;
165
166 if (argv == NULL)
167 {
168 errno = EINVAL;
169 goto error;
170 }
171
172 thread_t* thread = sched_thread();
173 process_t* process = thread->process;
174
175 child = process_new(atomic_load(&process->priority));
176 if (child == NULL)
177 {
178 goto error;
179 }
180
181 childThread = thread_new(child);
182 if (childThread == NULL)
183 {
184 goto error;
185 }
186
187 if (thread_copy_from_user_string_array(thread, argv, &argvCopy, &argc) == ERR)
188 {
189 goto error;
190 }
191
192 if (argc == 0 || argvCopy[0] == NULL)
193 {
194 goto error;
195 }
196
197 executable = strdup(argvCopy[0]);
198 if (executable == NULL)
199 {
200 goto error;
201 }
202
203 if (flags & SPAWN_SUSPEND)
204 {
205 atomic_fetch_or(&child->flags, PROCESS_SUSPENDED);
206 }
207
208 if (!(flags & SPAWN_EMPTY_FDS))
209 {
211 {
212 if (file_table_copy(&child->fileTable, &process->fileTable, 0, 3) == ERR)
213 {
214 goto error;
215 }
216 }
217 else
218 {
219 if (file_table_copy(&child->fileTable, &process->fileTable, 0, CONFIG_MAX_FD) == ERR)
220 {
221 goto error;
222 }
223 }
224 }
225
226 if (!(flags & SPAWN_EMPTY_NS))
227 {
228 if (namespace_set_parent(&child->ns, &process->ns) == ERR)
229 {
230 goto error;
231 }
232 }
233
234 if (!(flags & SPAWN_EMPTY_ENV))
235 {
236 if (process_copy_env(child, process) == ERR)
237 {
238 goto error;
239 }
240 }
241
242 if (!(flags & SPAWN_EMPTY_CWD))
243 {
244 path_t cwd = cwd_get(&process->cwd);
245 cwd_set(&child->cwd, &cwd);
246 path_put(&cwd);
247 }
248
249 // Call loader_exec(executable, argvCopy, argc)
250 memset(&thread->frame, 0, sizeof(interrupt_frame_t));
251 childThread->frame.rip = (uintptr_t)loader_entry;
252 childThread->frame.rdi = (uintptr_t)executable;
253 childThread->frame.rsi = (uintptr_t)argvCopy;
254 childThread->frame.rdx = argc;
255
256 childThread->frame.cs = GDT_CS_RING0;
257 childThread->frame.ss = GDT_SS_RING0;
258 childThread->frame.rsp = childThread->kernelStack.top;
260
261 pid_t volatile result = child->id; // Important to not deref after pushing the thread
262 sched_submit(childThread);
263 return result;
264
265error:
266 if (childThread != NULL)
267 {
268 thread_free(childThread);
269 }
270 if (child != NULL)
271 {
272 process_kill(child, errno);
273 }
274
275 free((void*)executable);
276 loader_strv_free(argvCopy, argc);
277 return ERR;
278}
279
280SYSCALL_DEFINE(SYS_THREAD_CREATE, tid_t, void* entry, void* arg)
281{
282 thread_t* thread = sched_thread();
283 process_t* process = thread->process;
284 space_t* space = &process->space;
285
286 if (space_check_access(space, entry, sizeof(uint64_t)) == ERR)
287 {
288 return ERR;
289 }
290
291 // Dont check arg user space can use it however it wants
292
293 thread_t* newThread = thread_new(process);
294 if (newThread == NULL)
295 {
296 return ERR;
297 }
298
299 memset(&thread->frame, 0, sizeof(interrupt_frame_t));
300 newThread->frame.rip = (uint64_t)entry;
301 newThread->frame.rsp = newThread->userStack.top;
302 newThread->frame.rbp = newThread->userStack.top;
303 newThread->frame.rdi = (uint64_t)arg;
304 newThread->frame.cs = GDT_CS_RING3;
305 newThread->frame.ss = GDT_SS_RING3;
307
308 tid_t volatile result = newThread->id; // Important to not deref after pushing the thread
309 sched_submit(newThread);
310 return result;
311}
#define SEEK_SET
Definition SEEK.h:4
#define SEEK_END
Definition SEEK.h:6
#define assert(expression)
Definition assert.h:29
@ GDT_CS_RING3
Value to load into the CS register for user code.
Definition gdt.h:48
@ GDT_CS_RING0
Value to load into the CS register for kernel code.
Definition gdt.h:45
@ GDT_SS_RING3
Value to load into the SS register for user data.
Definition gdt.h:49
@ GDT_SS_RING0
Value to load into the SS register for kernel data.
Definition gdt.h:46
#define SYSCALL_DEFINE(num, returnType,...)
Macro to define a syscall.
Definition syscall.h:144
@ SYS_THREAD_CREATE
Definition syscall.h:88
@ SYS_SPAWN
Definition syscall.h:67
void cwd_set(cwd_t *cwd, const path_t *newPath)
Set the current working directory.
Definition cwd.c:44
path_t cwd_get(cwd_t *cwd)
Get the current working directory.
Definition cwd.c:18
uint64_t file_table_copy(file_table_t *dest, file_table_t *src, fd_t min, fd_t max)
Copy a file table, closing any overlapping file descriptors.
Definition file_table.c:204
uint64_t namespace_set_parent(namespace_t *ns, namespace_t *parent)
Sets the parent of a namespace and inherits all mounts from the parent.
Definition namespace.c:123
void path_put(path_t *path)
Put a path.
Definition path.c:246
#define PATHNAME(string)
Helper to create a pathname.
Definition path.h:166
@ PML_USER
@ PML_PRESENT
@ PML_WRITE
uint64_t space_check_access(space_t *space, const void *addr, uint64_t length)
Checks if a virtual memory region is within the allowed address range of the space.
Definition space.c:478
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:163
@ VMM_ALLOC_OVERWRITE
If any page is already mapped, overwrite the mapping.
Definition vmm.h:123
void loader_exec(const char *executable, char **argv, uint64_t argc)
Causes the currently running thread to load and execute a new program.
Definition loader.c:39
process_t * process_new(priority_t priority)
Allocates and initializes a new process.
Definition process.c:718
uint64_t process_set_cmdline(process_t *process, char **argv, uint64_t argc)
Sets the command line arguments for a process.
Definition process.c:869
uint64_t process_copy_env(process_t *dest, process_t *src)
Copies the environment variables from one process to another.
Definition process.c:817
void process_kill(process_t *process, int32_t status)
Kills a process.
Definition process.c:776
@ PROCESS_SUSPENDED
Definition process.h:150
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
void thread_free(thread_t *thread)
Frees a thread structure.
Definition thread.c:124
_NORETURN void thread_jump(thread_t *thread)
Jump to a thread by calling thread_load() and then loading its interrupt frame.
thread_t * thread_new(process_t *process)
Creates a new thread structure.
Definition thread.c:70
#define WAIT_BLOCK(queue, condition)
Blocks until the condition is true, will test the condition on every wakeup.
Definition wait.h:48
_NORETURN void sched_process_exit(int32_t status)
Terminates the currently executing process and all it's threads.
Definition sched.c:648
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
#define UNREF(ptr)
Decrement reference count.
Definition ref.h:80
uint64_t vfs_seek(file_t *file, int64_t offset, seek_origin_t origin)
Seek in a file.
Definition vfs.c:286
file_t * vfs_open(const pathname_t *pathname, process_t *process)
Open a file.
Definition vfs.c:94
uint64_t vfs_read(file_t *file, void *buffer, uint64_t count)
Read from a file.
Definition vfs.c:200
#define CONFIG_MAX_FD
Maximum file descriptor configuration.
Definition config.h:47
#define EINVAL
Invalid argument.
Definition errno.h:142
#define ESPAWNFAIL
Process spawn failed.
Definition errno.h:722
#define errno
Error number variable.
Definition errno.h:27
#define EOK
No error.
Definition errno.h:32
void elf64_load_segments(const Elf64_File *elf, Elf64_Addr base, Elf64_Off offset)
Load all loadable segments of an ELF file into memory.
uint64_t elf64_validate(Elf64_File *elf, void *data, uint64_t size)
Validate a files content and initalize a ELF64_File structure using it.
void elf64_get_loadable_bounds(const Elf64_File *elf, Elf64_Addr *minAddr, Elf64_Addr *maxAddr)
Get the loadable virtual memory bounds of an ELF file.
uint64_t Elf64_Addr
ELF64 Unsigned program address.
Definition elf.h:30
#define ROUND_DOWN(number, multiple)
Definition math.h:21
spawn_flags_t
Spawn behaviour flags.
Definition proc.h:57
@ SPAWN_EMPTY_FDS
Dont inherit the file descriptors of the parent process.
Definition proc.h:67
@ SPAWN_STDIO_FDS
Only inherit stdin, stdout and stderr from the parent process.
Definition proc.h:68
@ SPAWN_EMPTY_NS
Dont inherit the mountpoints of the parent's namespace.
Definition proc.h:69
@ SPAWN_SUSPEND
Definition proc.h:66
@ SPAWN_EMPTY_ENV
Don't inherit the parent's environment variables.
Definition proc.h:70
@ SPAWN_EMPTY_CWD
Don't inherit the parent's current working directory, starts at root (/).
Definition proc.h:71
#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
__UINT64_TYPE__ pid_t
Process Identifier.
Definition pid_t.h:11
static void loader_strv_free(char **array, uint64_t amount)
Definition loader.c:21
static void loader_entry(const char *executable, char **argv, uint64_t argc)
Definition loader.c:148
static dentry_t * file
Definition log_file.c:22
static const path_flag_t flags[]
Definition path.c:42
#define RFLAGS_INTERRUPT_ENABLE
Definition regs.h:32
#define RFLAGS_ALWAYS_SET
Definition regs.h:24
#define atomic_fetch_or(object, operand)
Definition stdatomic.h:285
#define atomic_load(object)
Definition stdatomic.h:288
__UINT64_TYPE__ uint64_t
Definition stdint.h:17
#define UINT64_MAX
Definition stdint.h:74
__UINTPTR_TYPE__ uintptr_t
Definition stdint.h:43
__INT64_TYPE__ int64_t
Definition stdint.h:16
_PUBLIC void * malloc(size_t size)
Definition malloc.c:5
_PUBLIC void free(void *ptr)
Definition free.c:11
char * strdup(const char *src)
Definition strdup.c:5
_PUBLIC void * memcpy(void *_RESTRICT s1, const void *_RESTRICT s2, size_t n)
Definition memcpy.c:61
_PUBLIC size_t strlen(const char *s)
Definition strlen.c:3
_PUBLIC void * memset(void *s, int c, size_t n)
Definition memset.c:4
Elf64_Addr e_entry
Entry point virtual address.
Definition elf.h:100
ELF File Helper structure.
Definition elf.h:781
Elf64_Ehdr * header
The data in the file, pointed to the start of the ELF header.
Definition elf.h:782
File structure.
Definition file.h:39
Trap Frame Structure.
Definition interrupt.h:143
Path structure.
Definition path.h:125
Process structure.
Definition process.h:158
file_table_t fileTable
Definition process.h:166
namespace_t ns
Definition process.h:164
wait_queue_t suspendQueue
Definition process.h:169
space_t space
Definition process.h:163
pid_t id
Definition process.h:160
cwd_t cwd
Definition process.h:165
Virtual address space structure.
Definition space.h:79
uintptr_t top
The top of the stack, this address is not inclusive.
Thread of execution structure.
Definition thread.h:63
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
stack_pointer_t userStack
The user stack of the thread.
Definition thread.h:76
tid_t id
The thread id, unique within a process_t.
Definition thread.h:66