diff options
author | Freya Murphy <freya@freyacat.org> | 2025-04-24 13:42:14 -0400 |
---|---|---|
committer | Freya Murphy <freya@freyacat.org> | 2025-04-24 13:42:14 -0400 |
commit | 3a0fbbe6c75c06f1a38b5ef7e3bc8c4b47ccd4b0 (patch) | |
tree | 7888d97acd86e600a4a774455e8217e8de18ea61 | |
parent | fmt (diff) | |
download | comus-3a0fbbe6c75c06f1a38b5ef7e3bc8c4b47ccd4b0.tar.gz comus-3a0fbbe6c75c06f1a38b5ef7e3bc8c4b47ccd4b0.tar.bz2 comus-3a0fbbe6c75c06f1a38b5ef7e3bc8c4b47ccd4b0.zip |
user.c for loading userspace elfs into memoryuser
-rw-r--r-- | kernel/include/comus/limits.h | 3 | ||||
-rw-r--r-- | kernel/include/comus/procs.h | 6 | ||||
-rw-r--r-- | kernel/include/comus/user.h | 25 | ||||
-rw-r--r-- | kernel/user.c | 124 |
4 files changed, 158 insertions, 0 deletions
diff --git a/kernel/include/comus/limits.h b/kernel/include/comus/limits.h index 675df47..cadfc93 100644 --- a/kernel/include/comus/limits.h +++ b/kernel/include/comus/limits.h @@ -22,6 +22,9 @@ #define N_FILE_NAME 256 #define N_DISKS 8 +/// elf limits +#define N_ELF_SEGMENTS 16 + /// length of terminal buffer #define TERM_MAX_WIDTH 1920 #define TERM_MAX_HEIGHT 1080 diff --git a/kernel/include/comus/procs.h b/kernel/include/comus/procs.h index d92bc5d..d105867 100644 --- a/kernel/include/comus/procs.h +++ b/kernel/include/comus/procs.h @@ -13,6 +13,7 @@ #include <comus/limits.h> #include <comus/memory.h> #include <lib.h> +#include <elf.h> #define PCB_REG(pcb, x) ((pcb)->regs->x) #define PCB_RET(pcb) ((pcb)->regs->rax) @@ -56,6 +57,11 @@ struct pcb { size_t priority; size_t ticks; + // elf metadata + Elf64_Ehdr elf_header; + Elf64_Phdr elf_segments[N_ELF_SEGMENTS]; + Elf64_Half n_elf_segments; + // queue linkage struct pcb *next; // next PDB in queue diff --git a/kernel/include/comus/user.h b/kernel/include/comus/user.h new file mode 100644 index 0000000..29c978c --- /dev/null +++ b/kernel/include/comus/user.h @@ -0,0 +1,25 @@ +/** + * @file user.h + * + * @author Freya Murphy <freya@freyacat.org> + * + * Userland functions + */ + +#ifndef USER_H_ +#define USER_H_ + +#include <comus/procs.h> +#include <comus/fs.h> + +/** + * Load a user elf program from a file into a pcb + */ +int user_load(struct pcb *pcb, struct disk *disk); + +/** + * Clean up all loaded userland data from a pcb + */ +void user_cleanup(struct pcb *pcb); + +#endif /* user.h */ diff --git a/kernel/user.c b/kernel/user.c index 0a237e9..592b35b 100644 --- a/kernel/user.c +++ b/kernel/user.c @@ -1,5 +1,129 @@ +#include "lib/kio.h" +#include <comus/fs.h> #include <comus/procs.h> #include <comus/memory.h> +#include <comus/user.h> +#include <elf.h> + +/// FIXME: the following code is using direct +/// disk access instead of file access, this is +/// because filesystems are not yet implemented. +/// This MUST be changed once we have files. +/// - Freya + +#define USER_STACK_TOP 0x800000000000 +#define USER_STACK_LEN (4 * PAGE_SIZE) + +static int user_load_segment(struct pcb *pcb, struct disk *disk, int idx) +{ + uint8_t buf[PAGE_SIZE]; + Elf64_Phdr hdr; + size_t npages; + int ret = 0; + + hdr = pcb->elf_segments[idx]; + npages = (hdr.p_filesz + PAGE_SIZE - 1) / PAGE_SIZE; + + // allocate memory in user process + if (mem_alloc_pages_at(pcb->memctx, npages, (void *)hdr.p_vaddr, + F_WRITEABLE | F_UNPRIVILEGED) == NULL) + return 1; + + // load data + for (size_t i = 0; i < npages; i++) { + mem_ctx_switch(kernel_mem_ctx); // disk_read is kernel internal + ret = disk_read(disk, hdr.p_offset + i * PAGE_SIZE, PAGE_SIZE, buf); + if (ret < 0) + break; + mem_ctx_switch(pcb->memctx); + memcpy((char *)hdr.p_vaddr + i * PAGE_SIZE, buf, PAGE_SIZE); + } + + mem_ctx_switch(kernel_mem_ctx); + return ret; +} + +static int user_load_segments(struct pcb *pcb, struct disk *disk) +{ + int ret = 0; + + for (int i = 0; i < pcb->n_elf_segments; i++) + if ((ret = user_load_segment(pcb, disk, i))) + return ret; + return 0; +} + +static int user_load_elf(struct pcb *pcb, struct disk *disk) +{ + int ret = 0; + + ret = disk_read(disk, 0, sizeof(Elf64_Ehdr), &pcb->elf_header); + if (ret < 0) + return 1; + + if (pcb->elf_header.e_phnum > N_ELF_SEGMENTS) + return 1; + + pcb->n_elf_segments = pcb->elf_header.e_phnum; + ret = disk_read(disk, pcb->elf_header.e_phoff, + sizeof(Elf64_Phdr) * pcb->elf_header.e_phnum, + &pcb->elf_segments); + if (ret < 0) + return 1; + + return 0; +} + +static int user_setup_stack(struct pcb *pcb) +{ + // allocate stack + if (mem_alloc_pages_at(pcb->memctx, USER_STACK_LEN / PAGE_SIZE, + (void *)(USER_STACK_TOP - USER_STACK_LEN), + F_WRITEABLE | F_UNPRIVILEGED) == NULL) + return 1; + + // setup initial context save area + pcb->regs = (struct cpu_regs *)(USER_STACK_TOP - sizeof(struct cpu_regs)); + mem_ctx_switch(pcb->memctx); + memset(pcb->regs, 0, sizeof(struct cpu_regs)); + pcb->regs->rip = pcb->elf_header.e_entry; + pcb->regs->cs = 0x18 | 3; + pcb->regs->rflags = (1 << 9); + pcb->regs->rsp = USER_STACK_TOP; + pcb->regs->ss = 0x20 | 3; + mem_ctx_switch(kernel_mem_ctx); + + return 0; +} + +int user_load(struct pcb *pcb, struct disk *disk) +{ + pcb->regs = NULL; + + // allocate memory context + pcb->memctx = mem_ctx_alloc(); + if (pcb->memctx == NULL) + goto fail; + + // load elf information + if (user_load_elf(pcb, disk)) + goto fail; + + // load segments into memory + if (user_load_segments(pcb, disk)) + goto fail; + + // setup process stack + if (user_setup_stack(pcb)) + goto fail; + + // success + return 0; + +fail: + user_cleanup(pcb); + return 1; +} void user_cleanup(struct pcb *pcb) { |