PatchworkOS
Loading...
Searching...
No Matches
loader.c
Go to the documentation of this file.
2
3#include <kernel/cpu/gdt.h>
4#include <kernel/fs/vfs.h>
5#include <kernel/log/log.h>
6#include <kernel/mem/vmm.h>
9
10#include <errno.h>
11#include <stdlib.h>
12#include <string.h>
13#include <sys/elf.h>
14#include <sys/math.h>
15
16static void* loader_load_program(thread_t* thread)
17{
18 process_t* process = thread->process;
19 space_t* space = &process->space;
20
21 const char* executable = process->argv.buffer[0];
22 if (executable == NULL)
23 {
24 return NULL;
25 }
26
27 file_t* file = vfs_open(PATHNAME(executable), process);
28 if (file == NULL)
29 {
30 return NULL;
31 }
33
34 elf_hdr_t header;
35 if (vfs_read(file, &header, sizeof(elf_hdr_t)) != sizeof(elf_hdr_t))
36 {
37 return NULL;
38 }
39 if (!ELF_IS_VALID(&header))
40 {
41 return NULL;
42 }
43
44 uint64_t min = UINT64_MAX;
45 uint64_t max = 0;
46 for (uint64_t i = 0; i < header.phdrAmount; i++)
47 {
48 uint64_t offset = sizeof(elf_hdr_t) + header.phdrSize * i;
49 if (vfs_seek(file, offset, SEEK_SET) != offset)
50 {
51 return NULL;
52 }
53
54 elf_phdr_t phdr;
55 if (vfs_read(file, &phdr, sizeof(elf_phdr_t)) != sizeof(elf_phdr_t))
56 {
57 return NULL;
58 }
59
60 switch (phdr.type)
61 {
63 {
64 min = MIN(min, phdr.virtAddr);
65 max = MAX(max, phdr.virtAddr + phdr.memorySize);
66 if (phdr.memorySize < phdr.fileSize)
67 {
68 return NULL;
69 }
70
71 if (vmm_alloc(space, (void*)phdr.virtAddr, phdr.memorySize, PML_PRESENT | PML_WRITE | PML_USER,
73 {
74 return NULL;
75 }
76 memset((void*)phdr.virtAddr, 0, phdr.memorySize);
77
78 if (vfs_seek(file, phdr.offset, SEEK_SET) != phdr.offset)
79 {
80 return NULL;
81 }
82 if (vfs_read(file, (void*)phdr.virtAddr, phdr.fileSize) != phdr.fileSize)
83 {
84 return NULL;
85 }
86
87 if (!(phdr.flags & ELF_PHDR_FLAGS_WRITE))
88 {
89 if (vmm_protect(&thread->process->space, (void*)phdr.virtAddr, phdr.memorySize,
91 {
92 return NULL;
93 }
94 }
95 }
96 break;
97 }
98 }
99
100 return (void*)header.entry;
101}
102
103static char** loader_setup_argv(thread_t* thread)
104{
105 if (thread->process->argv.size >= PAGE_SIZE)
106 {
107 return NULL;
108 }
109
110 char** argv = memcpy((void*)(thread->userStack.top - sizeof(uint64_t) - thread->process->argv.size),
111 thread->process->argv.buffer, thread->process->argv.size);
112
113 for (uint64_t i = 0; i < thread->process->argv.amount; i++)
114 {
115 argv[i] = (void*)((uint64_t)argv[i] - (uint64_t)thread->process->argv.buffer + (uint64_t)argv);
116 }
117
118 return argv;
119}
120
121static void loader_process_entry(void)
122{
123 thread_t* thread = sched_thread();
124
125 void* rip = loader_load_program(thread);
126 if (rip == NULL)
127 {
129 }
130
131 char** argv = loader_setup_argv(thread);
132 if (argv == NULL)
133 {
135 }
136
137 // Disable interrupts, they will be enabled by the IRETQ instruction.
138 asm volatile("cli");
139
140 memset(&thread->frame, 0, sizeof(interrupt_frame_t));
141 thread->frame.rdi = thread->process->argv.amount;
142 thread->frame.rsi = (uintptr_t)argv;
143 thread->frame.rsp = (uintptr_t)ROUND_DOWN((uint64_t)argv - 1, 16);
144 thread->frame.rip = (uintptr_t)rip;
145 thread->frame.cs = GDT_CS_RING3;
146 thread->frame.ss = GDT_SS_RING3;
148
149 LOG_DEBUG("jump to user space path=%s pid=%d rsp=%p rip=%p\n", thread->process->argv.buffer[0], thread->process->id,
150 (void*)thread->frame.rsp, (void*)thread->frame.rip);
152}
153
154thread_t* loader_spawn(const char** argv, priority_t priority, const path_t* cwd)
155{
156 process_t* process = sched_process();
157 assert(process != NULL);
158
159 if (argv == NULL || argv[0] == NULL)
160 {
161 errno = EINVAL;
162 return NULL;
163 }
164
165 pathname_t executable;
166 if (pathname_init(&executable, argv[0]) == ERR)
167 {
168 return NULL;
169 }
170
171 stat_t info;
172 if (vfs_stat(&executable, &info, process) == ERR)
173 {
174 return NULL;
175 }
176
177 if (info.type != INODE_FILE)
178 {
179 errno = EISDIR;
180 return NULL;
181 }
182
183 process_t* child = process_new(process, argv, cwd, priority);
184 if (child == NULL)
185 {
186 return NULL;
187 }
188 DEREF_DEFER(child);
189
190 thread_t* childThread = thread_new(child);
191 if (childThread == NULL)
192 {
193 return NULL;
194 }
195
196 childThread->frame.rip = (uintptr_t)loader_process_entry;
197 childThread->frame.rsp = childThread->kernelStack.top;
198 childThread->frame.cs = GDT_CS_RING0;
199 childThread->frame.ss = GDT_SS_RING0;
201
202 LOG_INFO("spawn path=%s pid=%d\n", argv[0], child->id);
203 return childThread;
204}
205
206SYSCALL_DEFINE(SYS_SPAWN, pid_t, const char** argv, const spawn_fd_t* fds, const char* cwdString, spawn_attr_t* attr)
207{
208 thread_t* thread = sched_thread();
209 process_t* process = thread->process;
210
211 priority_t priority;
212 if (attr == NULL || attr->flags & SPAWN_INHERIT_PRIORITY)
213 {
214 priority = atomic_load(&process->priority);
215 }
216 else
217 {
218 priority = attr->priority;
219 }
220
221 if (priority >= PRIORITY_MAX_USER)
222 {
223 errno = EACCES;
224 return ERR;
225 }
226
227 char** argvCopy;
228 uint64_t argc;
229 char* argvTerminator = NULL;
230 if (thread_copy_from_user_terminated(thread, argv, &argvTerminator, sizeof(char*), CONFIG_MAX_ARGC,
231 (void**)&argvCopy, &argc) == ERR)
232 {
233 return ERR;
234 }
235
236 for (uint64_t i = 0; i < argc; i++)
237 {
238 char* userSpacePtr = argvCopy[i];
239 char* kernelStringCopy;
240 uint64_t stringLen;
241 char terminator = '\0';
242
243 if (thread_copy_from_user_terminated(thread, userSpacePtr, &terminator, sizeof(char), MAX_PATH,
244 (void**)&kernelStringCopy, &stringLen) == ERR)
245 {
246 for (uint64_t j = 0; j < i; j++)
247 {
248 free(argvCopy[j]);
249 }
250 free(argvCopy);
251 return ERR;
252 }
253
254 argvCopy[i] = kernelStringCopy;
255 }
256
257 path_t cwdPath = PATH_EMPTY;
258 if (cwdString != NULL)
259 {
260 pathname_t cwdPathname;
261 if (thread_copy_from_user_pathname(thread, &cwdPathname, cwdString) == ERR)
262 {
263 goto cleanup_argv;
264 }
265
266 if (vfs_walk(&cwdPath, &cwdPathname, WALK_NONE, process) == ERR)
267 {
268 goto cleanup_argv;
269 }
270 }
271
272 thread_t* child = loader_spawn((const char**)argvCopy, priority, cwdString == NULL ? NULL : &cwdPath);
273
274 for (uint64_t i = 0; i < argc; i++)
275 {
276 free(argvCopy[i]);
277 }
278 free(argvCopy);
279
280 if (cwdString != NULL)
281 {
282 path_put(&cwdPath);
283 }
284
285 if (child == NULL)
286 {
287 return ERR;
288 }
289
290 if (fds != NULL)
291 {
292 spawn_fd_t* fdsCopy;
293 uint64_t fdAmount;
294 spawn_fd_t fdsTerminator = SPAWN_FD_END;
295
296 if (thread_copy_from_user_terminated(thread, fds, &fdsTerminator, sizeof(spawn_fd_t), CONFIG_MAX_FD,
297 (void**)&fdsCopy, &fdAmount) == ERR)
298 {
299 thread_free(child);
300 return ERR;
301 }
302
303 vfs_ctx_t* parentVfsCtx = &process->vfsCtx;
304 vfs_ctx_t* childVfsCtx = &child->process->vfsCtx;
305
306 for (uint64_t i = 0; i < fdAmount; i++)
307 {
308 file_t* file = vfs_ctx_get_file(parentVfsCtx, fdsCopy[i].parent);
309 if (file == NULL)
310 {
311 free(fdsCopy);
312 thread_free(child);
313 errno = EBADF;
314 return ERR;
315 }
316
317 if (vfs_ctx_set_fd(childVfsCtx, fdsCopy[i].child, file) == ERR)
318 {
319 DEREF(file);
320 free(fdsCopy);
321 thread_free(child);
322 errno = EBADF;
323 return ERR;
324 }
325 DEREF(file);
326 }
327 free(fdsCopy);
328 }
329
330 pid_t childPid = child->process->id; // Important to not deref after pushing the thread
331 sched_push_new_thread(child, thread);
332 return childPid;
333
334cleanup_argv:
335 for (uint64_t i = 0; i < argc; i++)
336 {
337 free(argvCopy[i]);
338 }
339 free(argvCopy);
340 return ERR;
341}
342
343thread_t* loader_thread_create(process_t* parent, void* entry, void* arg)
344{
345 thread_t* child = thread_new(parent);
346 if (child == NULL)
347 {
348 return NULL;
349 }
350
351 child->frame.rip = (uint64_t)entry;
352 child->frame.rsp = child->userStack.top;
353 child->frame.rdi = (uint64_t)arg;
354 child->frame.cs = GDT_CS_RING3;
355 child->frame.ss = GDT_SS_RING3;
357 return child;
358}
359
360SYSCALL_DEFINE(SYS_THREAD_CREATE, tid_t, void* entry, void* arg)
361{
362 thread_t* thread = sched_thread();
363 process_t* process = thread->process;
364 space_t* space = &process->space;
365
366 if (space_check_access(space, entry, sizeof(uint64_t)) == ERR)
367 {
368 return ERR;
369 }
370
371 // Dont check arg user space can use it however it wants
372
373 thread_t* newThread = loader_thread_create(process, entry, arg);
374 if (newThread == NULL)
375 {
376 return ERR;
377 }
378
379 sched_push_new_thread(newThread, thread);
380 return newThread->id;
381}
#define MAX_PATH
Maximum length of filepaths.
Definition MAX_PATH.h:11
#define SEEK_SET
Definition SEEK.h:4
#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 SYS_SPAWN
Definition syscalls.h:24
#define SYSCALL_DEFINE(num, returnType,...)
Macro to define a syscall.
Definition syscalls.h:100
#define SYS_THREAD_CREATE
Definition syscalls.h:45
void path_put(path_t *path)
Put a path.
Definition path.c:257
#define PATHNAME(string)
Helper to create a pathname.
Definition path.h:156
uint64_t pathname_init(pathname_t *pathname, const char *string)
Initialize a pathname.
Definition path.c:100
#define PATH_EMPTY
Helper to create an empty path.
Definition path.h:182
@ WALK_NONE
No flags.
Definition path.h:101
fd_t vfs_ctx_set_fd(vfs_ctx_t *ctx, fd_t fd, file_t *file)
Allocate a specific file descriptor in a VFS context.
Definition vfs_ctx.c:164
file_t * vfs_ctx_get_file(vfs_ctx_t *ctx, fd_t fd)
Get a file from a VFS context.
Definition vfs_ctx.c:48
uint64_t vfs_seek(file_t *file, int64_t offset, seek_origin_t origin)
Seek in a file.
Definition vfs.c:807
file_t * vfs_open(const pathname_t *pathname, process_t *process)
Open a file.
Definition vfs.c:531
uint64_t vfs_stat(const pathname_t *pathname, stat_t *buffer, process_t *process)
Get file information.
Definition vfs.c:1222
uint64_t vfs_read(file_t *file, void *buffer, uint64_t count)
Read from a file.
Definition vfs.c:694
uint64_t vfs_walk(path_t *outPath, const pathname_t *pathname, walk_flags_t flags, process_t *process)
Walk a pathname to a path, starting from the current process's working directory.
Definition vfs.c:341
#define LOG_INFO(format,...)
Definition log.h:87
#define LOG_DEBUG(format,...)
Definition log.h:81
@ 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:474
uint64_t vmm_protect(space_t *space, void *virtAddr, uint64_t length, pml_flags_t flags)
Changes memory protection flags for a virtual memory region in a given address space.
Definition vmm.c:401
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
@ VMM_ALLOC_NONE
Definition vmm.h:123
process_t * process_new(process_t *parent, const char **argv, const path_t *cwd, priority_t priority)
Allocates and initializes a new process.
Definition process.c:458
thread_t * loader_spawn(const char **argv, priority_t priority, const path_t *cwd)
Spawns a child process from an executable file.
Definition loader.c:154
NORETURN void loader_jump_to_user_space(thread_t *thread)
Performs the initial jump to userspace.
thread_t * loader_thread_create(process_t *parent, void *entry, void *arg)
Creates a new thread within an existing process.
Definition loader.c:343
void thread_free(thread_t *thread)
Frees a thread structure.
Definition thread.c:89
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
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
void sched_push_new_thread(thread_t *thread, thread_t *parent)
Pushes a newly created thread onto the scheduling queue.
Definition sched.c:385
process_t * sched_process(void)
Retrieves the process of the currently running thread.
Definition sched.c:164
thread_t * sched_thread(void)
Retrieves the currently running thread.
Definition sched.c:157
_NORETURN void sched_process_exit(uint64_t status)
Exits the current process.
Definition sched.c:191
#define DEREF_DEFER(ptr)
RAII-style cleanup for scoped references.
Definition ref.h:54
#define DEREF(ptr)
Decrement reference count.
Definition ref.h:80
#define CONFIG_MAX_ARGC
Maximum argument vector configuration.
Definition config.h:79
#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 EACCES
Permission denied.
Definition errno.h:97
#define EBADF
Bad file number.
Definition errno.h:77
#define EISDIR
Is a directory.
Definition errno.h:137
#define ELF_PHDR_FLAGS_WRITE
Definition elf.h:128
#define ELF_IS_VALID(hdr)
Checks the validity of an ELF header.
Definition elf.h:263
#define ELF_PHDR_TYPE_LOAD
Definition elf.h:108
@ INODE_FILE
Is a file.
Definition io.h:345
#define MIN(x, y)
Definition math.h:16
#define ROUND_DOWN(number, multiple)
Definition math.h:21
#define MAX(x, y)
Definition math.h:15
#define SPAWN_FD_END
Spawn fds termination constant.
Definition proc.h:82
#define SPAWN_INHERIT_PRIORITY
Definition proc.h:76
#define PRIORITY_MAX_USER
Definition proc.h:43
#define PAGE_SIZE
Memory page size.
Definition proc.h:140
uint8_t priority_t
Priority type.
Definition proc.h:41
#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 fb_info_t info
Definition gop.c:41
static void * loader_load_program(thread_t *thread)
Definition loader.c:16
static char ** loader_setup_argv(thread_t *thread)
Definition loader.c:103
static void loader_process_entry(void)
Definition loader.c:121
static dentry_t * file
Definition log_file.c:17
#define RFLAGS_INTERRUPT_ENABLE
Definition regs.h:32
#define RFLAGS_ALWAYS_SET
Definition regs.h:24
#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
_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
char ** buffer
Definition argv.h:24
uint64_t size
!< Used to avoid allocations for empty argv
Definition argv.h:26
uint64_t amount
!< Size of the buffer in bytes.
Definition argv.h:27
ELF file header.
Definition elf.h:276
uint16_t phdrAmount
Definition elf.h:287
uint64_t entry
Definition elf.h:281
uint16_t phdrSize
Definition elf.h:286
ELF program header.
Definition elf.h:299
uint64_t fileSize
Definition elf.h:305
uint64_t offset
Definition elf.h:302
uint64_t memorySize
Definition elf.h:306
uint64_t virtAddr
Definition elf.h:303
elf_phdr_flags_t flags
Definition elf.h:301
elf_phdr_type_t type
Definition elf.h:300
File structure.
Definition file.h:37
Trap Frame Structure.
Definition interrupt.h:42
uint64_t rflags
Definition interrupt.h:64
Path structure.
Definition path.h:110
Pathname structure.
Definition path.h:122
Process structure.
Definition process.h:53
argv_t argv
Definition process.h:58
space_t space
Definition process.h:59
pid_t id
Definition process.h:55
vfs_ctx_t vfsCtx
Definition process.h:61
Virtual address space structure.
Definition space.h:79
priority_t priority
Definition proc.h:91
spawn_flags_t flags
Definition proc.h:90
Stucture used to duplicate fds in spawn().
Definition proc.h:55
uintptr_t top
The top of the stack, this address is not inclusive.
Stat type.
Definition io.h:360
Thread of execution structure.
Definition thread.h:55
process_t * process
The parent process that the thread executes within.
Definition thread.h:57
interrupt_frame_t frame
Definition thread.h:79
stack_pointer_t kernelStack
The kernel stack of the thread.
Definition thread.h:68
stack_pointer_t userStack
The user stack of the thread.
Definition thread.h:69
tid_t id
The thread id, unique within a process_t.
Definition thread.h:59
VFS context structure.
Definition vfs_ctx.h:31