PatchworkOS  19e446b
A non-POSIX operating system.
Loading...
Searching...
No Matches
loader.c
Go to the documentation of this file.
1#include <kernel/cpu/gdt.h>
2#include <kernel/fs/dentry.h>
5#include <kernel/fs/path.h>
6#include <kernel/fs/vfs.h>
7#include <kernel/log/log.h>
8#include <kernel/mem/vmm.h>
10#include <kernel/sched/loader.h>
11#include <kernel/sched/sched.h>
12#include <kernel/sched/thread.h>
13
14#include <errno.h>
15#include <kernel/sched/wait.h>
16#include <stdlib.h>
17#include <string.h>
18#include <sys/elf.h>
19#include <sys/math.h>
20#include <sys/proc.h>
21
22static void loader_strv_free(char** array, uint64_t amount)
23{
24 if (array == NULL)
25 {
26 return;
27 }
28
29 for (uint64_t i = 0; i < amount; i++)
30 {
31 if (array[i] == NULL)
32 {
33 continue;
34 }
35 free(array[i]);
36 }
37 free((void*)array);
38}
39
40void loader_exec(void)
41{
42 thread_t* thread = thread_current();
43 process_t* process = thread->process;
44
45 file_t* file = NULL;
46 void* fileData = NULL;
47
48 uintptr_t* addrs = NULL;
49
50 pathname_t pathname;
51 if (pathname_init(&pathname, process->argv[0]) == ERR)
52 {
53 goto cleanup;
54 }
55
56 file = vfs_open(&pathname, process);
57 if (file == NULL)
58 {
59 goto cleanup;
60 }
61
62 if (!(file->mode & MODE_EXECUTE))
63 {
64 errno = EACCES;
65 goto cleanup;
66 }
67
68 size_t fileSize = vfs_seek(file, 0, SEEK_END);
69 vfs_seek(file, 0, SEEK_SET);
70
71 fileData = malloc(fileSize);
72 if (fileData == NULL)
73 {
74 errno = ENOMEM;
75 goto cleanup;
76 }
77
78 size_t readSize = vfs_read(file, fileData, fileSize);
79 if (readSize != fileSize)
80 {
81 goto cleanup;
82 }
83
84 Elf64_File elf;
85 if (elf64_validate(&elf, fileData, fileSize) != 0)
86 {
87 errno = ENOEXEC;
88 goto cleanup;
89 }
90
91 Elf64_Addr minAddr = UINT64_MAX;
92 Elf64_Addr maxAddr = 0;
93 elf64_get_loadable_bounds(&elf, &minAddr, &maxAddr);
94 uint64_t loadSize = maxAddr - minAddr;
95
96 if (vmm_alloc(&process->space, (void*)minAddr, loadSize, PAGE_SIZE, PML_USER | PML_WRITE | PML_PRESENT,
98 {
99 goto cleanup;
100 }
101
102 elf64_load_segments(&elf, 0, 0);
103
104 char* rsp = (char*)thread->userStack.top;
105
106 addrs = malloc(sizeof(uintptr_t) * process->argc);
107 if (addrs == NULL)
108 {
109 errno = ENOMEM;
110 goto cleanup;
111 }
112
113 for (int64_t i = (int64_t)process->argc - 1; i >= 0; i--)
114 {
115 size_t len = strlen(process->argv[i]) + 1;
116 rsp -= len;
117 memcpy(rsp, process->argv[i], len);
118 addrs[i] = (uintptr_t)rsp;
119 }
120
121 rsp = (char*)ROUND_DOWN((uintptr_t)rsp, 8);
122
123 rsp -= (process->argc + 1) * sizeof(char*);
124 uintptr_t* argvStack = (uintptr_t*)rsp;
125 for (uint64_t i = 0; i < process->argc; i++)
126 {
127 argvStack[i] = addrs[i];
128 }
129 argvStack[process->argc] = 0;
130
131 // Disable interrupts, they will be enabled when we jump to user space.
132 ASM("cli");
133
134 memset(&thread->frame, 0, sizeof(interrupt_frame_t));
135 thread->frame.rsp = ROUND_DOWN((uintptr_t)rsp - sizeof(uint64_t), 16);
136 thread->frame.rip = elf.header->e_entry;
137 thread->frame.rdi = process->argc;
138 thread->frame.rsi = (uintptr_t)rsp;
139 thread->frame.cs = GDT_CS_RING3;
140 thread->frame.ss = GDT_SS_RING3;
142
143 errno = EOK;
144cleanup:
145 if (file != NULL)
146 {
147 UNREF(file);
148 }
149 if (fileData != NULL)
150 {
151 free(fileData);
152 }
153 if (addrs != NULL)
154 {
155 free(addrs);
156 }
157 pid_t pid = process->id;
158 if (errno == EOK)
159 {
160 thread_jump(thread);
161 }
162 LOG_DEBUG("exec failed due to %s pid=%llu\n", strerror(errno), pid);
163 sched_exits("exec failed");
164}
165
166static void loader_entry(void)
167{
168 thread_t* thread = thread_current();
169
170 WAIT_BLOCK(&thread->process->suspendQueue, !(atomic_load(&thread->process->flags) & PROCESS_SUSPENDED));
171
173
174 loader_exec();
175}
176
178{
179 if (argv == NULL)
180 {
181 errno = EINVAL;
182 return ERR;
183 }
184
185 thread_t* thread = thread_current();
186 assert(thread != NULL);
187 process_t* process = thread->process;
188 assert(process != NULL);
189
190 namespace_t* ns = process_get_ns(process);
191 if (ns == NULL)
192 {
193 return ERR;
194 }
195 UNREF_DEFER(ns);
196
197 namespace_t* childNs;
199 {
200 childNs = namespace_new(ns);
201 if (childNs == NULL)
202 {
203 return ERR;
204 }
205
206 if (!(flags & SPAWN_EMPTY_NS))
207 {
208 if (namespace_copy(childNs, ns) == ERR)
209 {
210 UNREF(childNs);
211 return ERR;
212 }
213 }
214 }
215 else
216 {
217 childNs = REF(ns);
218 }
219 UNREF_DEFER(childNs);
220
221 process_t* child =
222 process_new(atomic_load(&process->priority), flags & SPAWN_EMPTY_GROUP ? NULL : &process->group, childNs);
223 if (child == NULL)
224 {
225 return ERR;
226 }
227 UNREF_DEFER(child);
228
229 thread_t* childThread = thread_new(child);
230 if (childThread == NULL)
231 {
232 return ERR;
233 }
234
235 char** argvCopy = NULL;
236 uint64_t argc = 0;
237 if (thread_copy_from_user_string_array(thread, argv, &argvCopy, &argc) == ERR)
238 {
239 return ERR;
240 }
241
242 if (argc == 0 || argvCopy[0] == NULL)
243 {
244 loader_strv_free(argvCopy, argc);
245 errno = EINVAL;
246 return ERR;
247 }
248
249 if (process_set_cmdline(child, argvCopy, argc) == ERR)
250 {
251 loader_strv_free(argvCopy, argc);
252 return ERR;
253 }
254
255 if (flags & SPAWN_SUSPEND)
256 {
257 atomic_fetch_or(&child->flags, PROCESS_SUSPENDED);
258 }
259
260 if (!(flags & SPAWN_EMPTY_FDS))
261 {
263 {
264 if (file_table_copy(&child->fileTable, &process->fileTable, 0, 3) == ERR)
265 {
266 loader_strv_free(argvCopy, argc);
267 return ERR;
268 }
269 }
270 else
271 {
272 if (file_table_copy(&child->fileTable, &process->fileTable, 0, CONFIG_MAX_FD) == ERR)
273 {
274 loader_strv_free(argvCopy, argc);
275 return ERR;
276 }
277 }
278 }
279
280 if (!(flags & SPAWN_EMPTY_ENV))
281 {
282 if (env_copy(&child->env, &process->env) == ERR)
283 {
284 loader_strv_free(argvCopy, argc);
285 return ERR;
286 }
287 }
288
289 if (!(flags & SPAWN_EMPTY_CWD))
290 {
291 path_t cwd = cwd_get(&process->cwd, ns);
292 cwd_set(&child->cwd, &cwd);
293 path_put(&cwd);
294 }
295
296 // Call loader_exec()
297 memset(&thread->frame, 0, sizeof(interrupt_frame_t));
298 childThread->frame.rip = (uintptr_t)loader_entry;
299 childThread->frame.cs = GDT_CS_RING0;
300 childThread->frame.ss = GDT_SS_RING0;
301 childThread->frame.rsp = childThread->kernelStack.top;
303
304 sched_submit(childThread);
305 return child->id;
306}
307
308SYSCALL_DEFINE(SYS_THREAD_CREATE, tid_t, void* entry, void* arg)
309{
310 thread_t* thread = thread_current();
311 process_t* process = thread->process;
312 space_t* space = &process->space;
313
314 if (space_check_access(space, entry, sizeof(uint64_t)) == ERR)
315 {
316 return ERR;
317 }
318
319 // Dont check arg user space can use it however it wants
320
321 thread_t* newThread = thread_new(process);
322 if (newThread == NULL)
323 {
324 return ERR;
325 }
326
327 memset(&thread->frame, 0, sizeof(interrupt_frame_t));
328 newThread->frame.rip = (uint64_t)entry;
329 newThread->frame.rsp = newThread->userStack.top;
330 newThread->frame.rbp = newThread->userStack.top;
331 newThread->frame.rdi = (uint64_t)arg;
332 newThread->frame.cs = GDT_CS_RING3;
333 newThread->frame.ss = GDT_SS_RING3;
335
336 tid_t volatile result = newThread->id; // Important to not deref after pushing the thread
337 sched_submit(newThread);
338 return result;
339}
#define SEEK_SET
Definition SEEK.h:4
#define SEEK_END
Definition SEEK.h:6
#define assert(expression)
Definition assert.h:29
#define CONFIG_MAX_FD
Maximum file descriptor configuration.
Definition config.h:51
#define GDT_SS_RING3
Value to load into the SS register for user data.
Definition gdt.h:39
#define GDT_CS_RING3
Value to load into the CS register for user code.
Definition gdt.h:38
#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
#define SYSCALL_DEFINE(num, returnType,...)
Macro to define a syscall.
Definition syscall.h:172
@ SYS_THREAD_CREATE
Definition syscall.h:87
@ SYS_SPAWN
Definition syscall.h:67
void cwd_set(cwd_t *cwd, const path_t *newPath)
Set the current working directory.
Definition cwd.c:39
path_t cwd_get(cwd_t *cwd, namespace_t *ns)
Get the current working directory.
Definition cwd.c:19
void file_table_close_mode(file_table_t *table, mode_t mode)
Close all files in the file table with the specified mode.
Definition file_table.c:116
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:246
uint64_t namespace_copy(namespace_t *dest, namespace_t *src)
Copy mounts from one namespace to another.
Definition namespace.c:287
namespace_t * namespace_new(namespace_t *parent)
Create a new namespace.
Definition namespace.c:253
void path_put(path_t *path)
Put a path.
Definition path.c:273
uint64_t pathname_init(pathname_t *pathname, const char *string)
Initialize a pathname.
Definition path.c:116
@ MODE_PRIVATE
Definition path.h:93
@ MODE_EXECUTE
Definition path.h:83
#define LOG_DEBUG(format,...)
Definition log.h:85
@ PML_USER
@ PML_PRESENT
@ PML_WRITE
uint64_t space_check_access(space_t *space, const void *addr, size_t length)
Checks if a virtual memory region is within the allowed address range of the space.
Definition space.c:444
void * vmm_alloc(space_t *space, void *virtAddr, size_t length, size_t alignment, pml_flags_t pmlFlags, vmm_alloc_flags_t allocFlags)
Allocates and maps virtual memory in a given address space.
Definition vmm.c:153
@ VMM_ALLOC_OVERWRITE
If any page is already mapped, overwrite the mapping.
Definition vmm.h:122
uint64_t env_copy(env_t *dest, env_t *src)
Copy environment variables from one environment to another.
Definition env.c:26
process_t * process_new(priority_t priority, group_member_t *group, namespace_t *ns)
Allocates and initializes a new process.
Definition process.c:125
uint64_t process_set_cmdline(process_t *process, char **argv, uint64_t argc)
Sets the command line arguments for a process.
Definition process.c:294
namespace_t * process_get_ns(process_t *process)
Gets the namespace of a process.
Definition process.c:206
@ PROCESS_SUSPENDED
Definition process.h:41
void loader_exec(void)
Causes the currently running thread to load and execute a new program.
Definition loader.c: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
static thread_t * thread_current(void)
Retrieves the currently running thread.
Definition thread.h:126
_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:50
#define WAIT_BLOCK(queue, condition)
Blocks until the condition is true, will test the condition on every wakeup.
Definition wait.h:51
_NORETURN void sched_exits(const char *status)
Terminates the currently executing process and all it's threads.
Definition sched.c:654
void sched_submit(thread_t *thread)
Submits a thread to the scheduler.
Definition sched.c:378
#define UNREF_DEFER(ptr)
RAII-style cleanup for scoped references.
Definition ref.h:122
#define REF(ptr)
Increment reference count.
Definition ref.h:82
#define UNREF(ptr)
Decrement reference count.
Definition ref.h:109
size_t vfs_seek(file_t *file, ssize_t offset, seek_origin_t origin)
Seek in a file.
Definition vfs.c:355
size_t vfs_read(file_t *file, void *buffer, size_t count)
Read from a file.
Definition vfs.c:279
file_t * vfs_open(const pathname_t *pathname, process_t *process)
Open a file.
Definition vfs.c:156
#define EINVAL
Invalid argument.
Definition errno.h:142
#define ENOEXEC
Exec format error.
Definition errno.h:72
#define ENOMEM
Out of memory.
Definition errno.h:92
#define errno
Error number variable.
Definition errno.h:27
#define EACCES
Permission denied.
Definition errno.h:97
#define EOK
No error.
Definition errno.h:32
#define ASM(...)
Inline assembly macro.
Definition defs.h:160
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:23
spawn_flags_t
Spawn behaviour flags.
Definition proc.h:52
@ SPAWN_EMPTY_GROUP
Don't inherit the parent's process group, instead create a new group.
Definition proc.h:66
@ SPAWN_EMPTY_FDS
Dont inherit the file descriptors of the parent process.
Definition proc.h:62
@ SPAWN_STDIO_FDS
Only inherit stdin, stdout and stderr from the parent process.
Definition proc.h:63
@ SPAWN_EMPTY_NS
Create a new empty namespace, the new namespace will not contain any mountpoints or even a root.
Definition proc.h:68
@ SPAWN_SUSPEND
Definition proc.h:61
@ SPAWN_COPY_NS
Don't share the parent's namespace, instead create a new copy of it.
Definition proc.h:67
@ SPAWN_EMPTY_ENV
Don't inherit the parent's environment variables.
Definition proc.h:64
@ SPAWN_EMPTY_CWD
Don't inherit the parent's current working directory, starts at root (/).
Definition proc.h:65
#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
__UINT64_TYPE__ pid_t
Process Identifier.
Definition pid_t.h:11
static void loader_entry(void)
Definition loader.c:166
static void loader_strv_free(char **array, uint64_t amount)
Definition loader.c:22
static const path_flag_t flags[]
Definition path.c:47
#define RFLAGS_INTERRUPT_ENABLE
Definition regs.h:34
#define RFLAGS_ALWAYS_SET
Definition regs.h:26
#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
_PUBLIC char * strerror(int errnum)
Definition strerror.c:6
_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
mode_t mode
Definition file.h:42
Trap Frame Structure.
Definition interrupt.h:195
Namespace structure.
Definition namespace.h:57
Path structure.
Definition path.h:127
Pathname structure.
Definition path.h:139
Process structure.
Definition process.h:76
file_table_t fileTable
Definition process.h:88
group_member_t group
Definition process.h:100
uint64_t argc
Definition process.h:99
env_t env
Definition process.h:97
char ** argv
Definition process.h:98
wait_queue_t suspendQueue
Definition process.h:93
space_t space
Definition process.h:84
pid_t id
Definition process.h:81
cwd_t cwd
Definition process.h:87
Virtual address space structure.
Definition space.h:78
uintptr_t top
The top of the stack, this address is not inclusive.
Thread of execution structure.
Definition thread.h:61
process_t * process
The parent process that the thread executes within.
Definition thread.h:62
interrupt_frame_t frame
Definition thread.h:87
stack_pointer_t kernelStack
The kernel stack of the thread.
Definition thread.h:73
stack_pointer_t userStack
The user stack of the thread.
Definition thread.h:74
tid_t id
The thread id, unique within a process_t.
Definition thread.h:64