summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFreya Murphy <freya@freyacat.org>2025-04-24 13:42:14 -0400
committerFreya Murphy <freya@freyacat.org>2025-04-24 13:42:14 -0400
commit3a0fbbe6c75c06f1a38b5ef7e3bc8c4b47ccd4b0 (patch)
tree7888d97acd86e600a4a774455e8217e8de18ea61
parentfmt (diff)
downloadcomus-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.h3
-rw-r--r--kernel/include/comus/procs.h6
-rw-r--r--kernel/include/comus/user.h25
-rw-r--r--kernel/user.c124
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)
{