summaryrefslogtreecommitdiffstats
path: root/arch/x86_64/usermode.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/x86_64/usermode.c')
-rw-r--r--arch/x86_64/usermode.c184
1 files changed, 184 insertions, 0 deletions
diff --git a/arch/x86_64/usermode.c b/arch/x86_64/usermode.c
new file mode 100644
index 0000000..9ce5ed3
--- /dev/null
+++ b/arch/x86_64/usermode.c
@@ -0,0 +1,184 @@
+#include "arch/x86_64/object.h"
+#include "object.h"
+#include "memory.h"
+#include "bootargs.h"
+#include "device/initrd.h"
+#include "object.h"
+#include "panic.h"
+#include "string.h"
+#include "print.h"
+#include "device/processor.h"
+#include "arch/x86_64/page.h"
+
+static tcb_t s_init_tcb;
+
+//Dynamic allocation hell!!!
+static uintptr_t
+s_new_mapping(objdir_t *untyped_dir)
+{
+ objdir_entry_t *untyped_entry = NULL;
+ for(int i = 1; i < OBJECT_DIRECTORY_MAX_ENTRIES; i++) {
+ untyped_entry = &untyped_dir->entries[i];
+ if(untyped_entry->type != KO_MEMORY_UNTYPED) continue;
+ uintptr_t mapping = 0;
+ int err = untyped_retype_page(untyped_entry, (void**)&mapping);
+ if(err != 0) continue;
+ return mapping;
+ }
+ return 0;
+}
+
+static uintptr_t
+s_new_kstack(objdir_t *untyped_dir, objdir_entry_t *dest_entry)
+{
+ objdir_entry_t *untyped_entry = NULL;
+ for(int i = 1; i < OBJECT_DIRECTORY_MAX_ENTRIES; i++) {
+ untyped_entry = &untyped_dir->entries[i];
+ if(untyped_entry->type != KO_MEMORY_UNTYPED) continue;
+
+ int err = untyped_retype_kernel_stack(untyped_entry, dest_entry);
+ if(err != 0) continue;
+
+ uintptr_t ptr = dest_entry->data;
+ return (uintptr_t)vmem_phys_tovirt(ptr);
+ }
+ return 0;
+}
+
+static pmle_t*
+s_ensure_mapping_layer(pmle_t *pml, objdir_t *untyped_dir, uintptr_t addr, uint8_t layer)
+{
+ pmli_t pmli = PML_I_FOR_LAYER(addr, layer);
+ if(pml[pmli].p)
+ return pmle_get_page(pml[pmli]);
+
+ uintptr_t table_phys = s_new_mapping(untyped_dir);
+ pmle_t *table = vmem_phys_tovirt(table_phys);
+ memset(table, 0, 0x1000);
+
+ pml[pmli] = (pmle_t) {
+ .p = 1,
+ .rw = 1,
+ .us = 1,
+ .paddr = table_phys >> 12
+ };
+ __asm__ volatile("invlpg (%0)":: "r"(table_phys));
+ return vmem_phys_tovirt(table_phys);
+}
+
+static void
+s_map_page(pmle_t *pml4, objdir_t *untyped_dir, uintptr_t addr)
+{
+ pmle_t *pml3 = s_ensure_mapping_layer(pml4, untyped_dir, addr, 4);
+ pmle_t *pml2 = s_ensure_mapping_layer(pml3, untyped_dir, addr, 3);
+ pmle_t *pml1 = s_ensure_mapping_layer(pml2, untyped_dir, addr, 2);
+
+ pmli_t pml1i = PML_I_FOR_LAYER(addr, 1);
+ pmle_t *pmle = &pml1[pml1i];
+
+ uintptr_t pptr = s_new_mapping(untyped_dir);
+
+ *pmle = (pmle_t) {
+ .p = 1,
+ .rw = 1,
+ .us = 1,
+ .paddr = pptr >> 12
+ };
+ __asm__ volatile("invlpg (%0)":: "r"(addr));
+}
+
+__attribute__((noreturn))
+static void
+s_enter_usermode(void *ip, void *sp)
+{
+ __asm__ volatile("mov %0, %%rsp; \
+ movq %1, %%rcx; \
+ movq $0x202, %%r11; \
+ cli; \
+ swapgs; \
+ sysretq"::
+ "r"(sp), "r"(ip): "memory");
+ for(;;);
+}
+
+void
+init_load(void)
+{
+ const char *init_filename = bootargs_getarg("init");
+ if(init_filename == NULL) {
+ kpanic("Missing boot argument \"init\"");
+ }
+ tar_header_t *init_header = initrd_find_file(init_filename);
+ if(init_header == NULL) {
+ kpanic("Init file not found in initrd. (expected \"%s\")", init_filename);
+ }
+
+ size_t tcb_diri = _initDirectory.self.data++;
+ size_t kstack_diri = _initDirectory.self.data++;
+ size_t message_diri = _initDirectory.self.data++;
+
+ _initData.tcb_object = tcb_diri;
+ _initData.kernel_stack_object = kstack_diri;
+ _initData.message_object = message_diri;
+
+ _initDirectory.entries[tcb_diri] = (objdir_entry_t) {
+ .type = KO_TCB,
+ .data = (uintptr_t)&s_init_tcb
+ };
+
+ size_t untyped_diri = _initData.untyped_data_dir;
+ objdir_t *untyped_dir = (objdir_t*)_initDirectory.entries[untyped_diri].data;
+
+ size_t pml4_diri = _initData.pm_object;
+ pmle_t *pml4 = vmem_phys_tovirt(_initDirectory.entries[pml4_diri].data);
+
+ //Reserve and map pages for init binary
+ size_t init_size = initrd_file_size(init_header);
+
+ size_t init_pages = (init_size >> 12);
+ uintptr_t init_base = 0x1000;
+
+ for(size_t i = 0; i < init_pages + 1; i++) {
+ s_map_page(pml4, untyped_dir, init_base + (i * 0x1000));
+ }
+ //Copy over init data
+ memcpy((void*)init_base, (&((tar_block_t*)init_header)[1])->data, init_size);
+
+ //Create a user stack
+ uintptr_t stack_base = 0x00007FFFFFFFF000;
+ s_map_page(pml4, untyped_dir, stack_base);
+ uintptr_t sp = stack_base + 0xFF0;
+
+ //Create a kernel stack for the init TCB
+ uintptr_t ksp_base = (uintptr_t)s_new_kstack(untyped_dir, &_initDirectory.entries[kstack_diri]);
+ _initDirectory.entries[kstack_diri] = (objdir_entry_t) {
+ .type = KO_KERNEL_STACK,
+ .data = ksp_base
+ };
+ s_init_tcb.ksp = ksp_base + 0xFF0;
+
+ processor_t *proc = (processor_t*)processor_current();
+ proc->tss.rsp[0] = ksp_base;
+
+ //Create a message object for init
+ uintptr_t message_base = init_base + (init_pages << 12);
+ message_base += ((~(message_base & 0xFFF)) & 0xFFF) + 1;
+
+ s_map_page(pml4, untyped_dir, message_base);
+ _initDirectory.entries[message_diri] = (objdir_entry_t) {
+ .type = KO_MESSAGE,
+ .data = message_base
+ };
+ _initData.message_object_address = message_base;
+
+ //Write init data to user stack.
+ sp -= sizeof(init_data_t);
+ memcpy((void*)sp, &_initData, sizeof(init_data_t));
+ sp -= sizeof(uintptr_t);
+ *((uintptr_t*)sp) = sp + sizeof(uintptr_t);
+
+ //Setup usermode and jump
+ proc->tcb = &s_init_tcb;
+ proc->tss.rsp[0] = s_init_tcb.ksp;
+ s_enter_usermode((void*)init_base, (void*)sp);
+}