summaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorJon Santmyer <jon@jonsantmyer.com>2025-07-30 14:32:01 -0400
committerJon Santmyer <jon@jonsantmyer.com>2025-07-30 14:32:01 -0400
commitb905869a35f062a4e5072f10bec3a2ba3db0e365 (patch)
tree0666691804878857b4bb07daca8a54f5ddb8ae0b /arch
downloadjove-kernel-b905869a35f062a4e5072f10bec3a2ba3db0e365.tar.gz
jove-kernel-b905869a35f062a4e5072f10bec3a2ba3db0e365.tar.bz2
jove-kernel-b905869a35f062a4e5072f10bec3a2ba3db0e365.zip
working userland with some invoke syscalls
Diffstat (limited to 'arch')
-rw-r--r--arch/x86_64/gdt.c111
-rw-r--r--arch/x86_64/idt.c46
-rw-r--r--arch/x86_64/ivt.s91
-rw-r--r--arch/x86_64/lgdt.s26
-rw-r--r--arch/x86_64/page_directory.c114
-rw-r--r--arch/x86_64/panic.c36
-rw-r--r--arch/x86_64/processor.c126
-rw-r--r--arch/x86_64/savestate.s35
-rw-r--r--arch/x86_64/syscall.c25
-rw-r--r--arch/x86_64/untyped_memory.c54
-rw-r--r--arch/x86_64/untyped_retype_page.c51
-rw-r--r--arch/x86_64/usermode.c184
12 files changed, 899 insertions, 0 deletions
diff --git a/arch/x86_64/gdt.c b/arch/x86_64/gdt.c
new file mode 100644
index 0000000..e8ca912
--- /dev/null
+++ b/arch/x86_64/gdt.c
@@ -0,0 +1,111 @@
+#include "arch/x86_64/tables.h"
+#include "arch/x86_64/processor.h"
+#include "string.h"
+
+static segment_descriptor_t s_gdt[] = {
+ { 0 }, //Kernel NULL
+ { //Kernel Code
+ .limit_0_15 = 0xFFFF,
+ .base_0_15 = 0x0,
+ .type = CD_SEGMENT_TYPE_CODE | CD_SEGMENT_TYPE_WRITEABLE,
+ .s = 1,
+ .dpl = 0,
+ .p = 1,
+ .limit_16_19 = 0xF,
+ .avl = 0,
+ .l = 1,
+ .d_b = 0,
+ .g = 1,
+ .base_24_31 = 0x0
+ },
+ { //Kernel Data
+ .limit_0_15 = 0xFFFF,
+ .base_0_15 = 0x0,
+ .type = CD_SEGMENT_TYPE_WRITEABLE,
+ .s = 1,
+ .dpl = 0,
+ .p = 1,
+ .limit_16_19 = 0xF,
+ .avl = 0,
+ .l = 0,
+ .d_b = 1,
+ .g = 1,
+ .base_24_31 = 0x0
+ },
+ { 0 }, //User NULL
+ { //User Data
+ .limit_0_15 = 0xFFFF,
+ .base_0_15 = 0x0,
+ .type = CD_SEGMENT_TYPE_WRITEABLE,
+ .s = 1,
+ .dpl = 3,
+ .p = 1,
+ .limit_16_19 = 0xF,
+ .avl = 0,
+ .l = 0,
+ .d_b = 1,
+ .g = 1,
+ .base_24_31 = 0x0
+ },
+ { //User Code
+ .limit_0_15 = 0xFFFF,
+ .base_0_15 = 0x0,
+ .type = CD_SEGMENT_TYPE_CODE | CD_SEGMENT_TYPE_WRITEABLE,
+ .s = 1,
+ .dpl = 3,
+ .p = 1,
+ .limit_16_19 = 0xF,
+ .avl = 0,
+ .l = 1,
+ .d_b = 0,
+ .g = 1,
+ .base_24_31 = 0x0
+ },
+ { //TSS Low
+ .limit_0_15 = 0,
+ .base_0_15 = 0,
+ .base_16_23 = 0,
+ .type = S_SEGMENT_TYPE_TSS_AVAIL,
+ .avl = 0,
+ .s = 0,
+ .dpl = 0,
+ .p = 1,
+ .limit_16_19 = 0,
+ .l = 0,
+ .d_b = 0,
+ .g = 0,
+ .base_24_31 = 0
+ },
+ { 0 } //TSS High
+};
+
+void
+gdt_setup(processor_t *processor)
+{
+ memcpy(processor->gdt, s_gdt, sizeof(s_gdt));
+
+ processor->gdtr.base = (uintptr_t)&s_gdt;
+ processor->gdtr.length = sizeof(s_gdt) - 1;
+
+ segment_descriptor_t *tss_lo = &processor->gdt[GDT_ENTRY_TSS_LOW];
+ segment_descriptor_t *tss_hi = &processor->gdt[GDT_ENTRY_TSS_HIGH];
+
+ uintptr_t tssb = (uintptr_t)&processor->tss;
+ tss_lo->base_0_15 = tssb & 0xFFFF;
+ tss_lo->base_16_23 = (tssb >> 16) & 0xFF;
+ tss_lo->base_24_31 = (tssb >> 24) & 0xFF;
+ tss_hi->limit_0_15 = (tssb >> 32) & 0xFFFF;
+ tss_hi->base_0_15 = (tssb >> 48) & 0xFFFF;
+
+ size_t tssl = sizeof(tss_t);
+ tss_lo->limit_0_15 = tssl & 0xFFFF;
+ tss_lo->limit_16_19 = (tssl >> 16) & 0xF;
+
+ processor->tss.iopb = tssl;
+
+ extern void gdt_load(void*);
+ gdt_load(&processor->gdtr);
+
+ extern void tss_flush(uint16_t);
+ tss_flush(GDT_ENTRY_TSS_LOW * sizeof(segment_descriptor_t));
+}
diff --git a/arch/x86_64/idt.c b/arch/x86_64/idt.c
new file mode 100644
index 0000000..99e1da1
--- /dev/null
+++ b/arch/x86_64/idt.c
@@ -0,0 +1,46 @@
+#include "arch/x86_64/tables.h"
+#include "arch/x86_64/idt.h"
+
+__attribute__((aligned(4096)))
+interrupt_gate_t s_idtd[256];
+
+int64_t __isr_err;
+int64_t __isr_num;
+
+void
+isr_handle(ivt_state_t* state)
+{
+ kpanic_state(state, "Unhandled interrupt %i", __isr_num);
+}
+
+void
+ivt_setup(void)
+{
+ extern uintptr_t __ivt[256];
+ for(int i = 0; i < 256; i++) {
+ uintptr_t base = __ivt[i];
+ s_idtd[i] = (interrupt_gate_t) {
+ .base_0_15 = (base & 0xFFFF),
+ .segment_selector = 0x8,
+ .ist = 0,
+ .zero_0 = 0,
+ .type = 0xE,
+ .zero_1 = 0,
+ .dpl = 0,
+ .p = 1,
+ .base_16_31 = (base >> 16) & 0xFFFF,
+ .base_32_63 = (base >> 32) & 0xFFFFFFFF,
+ .resv = 0
+ };
+ }
+}
+
+void
+idt_setup(processor_t *processor)
+{
+ processor->idtr.base = (uintptr_t)&s_idtd;
+ processor->idtr.length = sizeof(s_idtd) - 1;
+
+ extern void idt_load(void* idtr);
+ idt_load(&processor->idtr);
+}
diff --git a/arch/x86_64/ivt.s b/arch/x86_64/ivt.s
new file mode 100644
index 0000000..d2d504b
--- /dev/null
+++ b/arch/x86_64/ivt.s
@@ -0,0 +1,91 @@
+.section .text
+.global idt_load
+.type idt_load @function
+idt_load:
+ lidt (%rdi)
+ sti
+ retq
+.size idt_load, . - idt_load
+
+.include "arch/x86_64/savestate.s"
+
+.extern isr_handle
+.type __isr_head @function
+__isr_head:
+ pushall
+ movq %rsp, %rdi
+ cld
+
+ call isr_handle
+ popall
+ iretq
+
+.extern __isr_err
+.extern __isr_num
+
+.macro defn_isr_err num:req
+.type __isr\num @function
+__isr\num:
+ pushq %rbx
+ movq 8(%rsp), %rbx
+ movq %rbx, __isr_err
+ popq %rbx
+ addq $8, %rsp
+ movq $\num, __isr_num
+ jmp __isr_head
+.size __isr\num, . - __isr\num
+.endm
+
+.macro defn_isr num:req
+.type __isr\num @function
+__isr\num:
+ movq $\num, __isr_num
+ jmp __isr_head
+.size __isr\num, . - __isr\num
+.endm
+
+.macro decl_isr num:req
+.quad __isr\num
+.endm
+
+defn_isr 0
+defn_isr 1
+defn_isr 2
+defn_isr 3
+defn_isr 4
+defn_isr 5
+defn_isr 6
+defn_isr 7
+defn_isr_err 8
+defn_isr 9
+defn_isr_err 10
+defn_isr_err 11
+defn_isr_err 12
+defn_isr_err 13
+defn_isr_err 14
+defn_isr 15
+defn_isr 16
+defn_isr_err 17
+defn_isr 18
+defn_isr 19
+defn_isr 20
+defn_isr_err 21
+
+.altmacro
+.set i, 22
+.rept 256-22
+ defn_isr %i
+ .set i, i+1
+.endr
+
+.section .data
+
+.global __ivt
+.type __ivt @object
+__ivt:
+ .set i, 0
+ .rept 256
+ decl_isr %i
+ .set i, i+1
+ .endr
+.size __ivt, . - __ivt
diff --git a/arch/x86_64/lgdt.s b/arch/x86_64/lgdt.s
new file mode 100644
index 0000000..7122ef9
--- /dev/null
+++ b/arch/x86_64/lgdt.s
@@ -0,0 +1,26 @@
+.global gdt_load
+.type gdt_load @function
+gdt_load:
+ lgdt (%rdi)
+.reload_segments:
+ pushq $0x8
+ leaq .reload_cs, %rax
+ pushq %rax
+ lretq
+.reload_cs:
+ movw $0x10, %ax
+ movw %ax, %ds
+ movw %ax, %es
+ movw %ax, %fs
+ movw %ax, %gs
+ movw %ax, %ss
+ retq
+.size gdt_load, . - gdt_load
+
+.global tss_flush
+.type tss_flush @function
+tss_flush:
+ movw %di, %ax
+ ltr %ax
+ retq
+.size tss_flush, . - tss_flush
diff --git a/arch/x86_64/page_directory.c b/arch/x86_64/page_directory.c
new file mode 100644
index 0000000..0506703
--- /dev/null
+++ b/arch/x86_64/page_directory.c
@@ -0,0 +1,114 @@
+#include "arch/x86_64/page.h"
+#include "arch/x86_64/object.h"
+#include "device/processor.h"
+#include "print.h"
+#include "memory.h"
+#include "object.h"
+#include "string.h"
+#include "jove.h"
+
+physptr_t s_kpbase(void);
+
+#if defined(__limine__)
+
+#include "boot/limine/limine.h"
+static struct limine_kernel_address_request s_kaddr_req = {
+ .id = LIMINE_KERNEL_ADDRESS_REQUEST
+};
+
+physptr_t s_kpbase(void) { return s_kaddr_req.response->physical_base; }
+
+#endif
+
+physptr_t
+vmem_tophys_koff(virtptr_t v)
+{
+ return v - (physptr_t)&_kernel_start + s_kpbase();
+}
+
+#define IDENTITY_BASE 0xFFFF800000000000
+void*
+vmem_phys_tovirt(physptr_t p)
+{
+ return (void*)(p + IDENTITY_BASE);
+}
+
+uintptr_t vmem_ident_tophys(void *vptr)
+{
+ return ((uintptr_t)vptr) - IDENTITY_BASE;
+}
+
+void*
+pmle_get_page(pmle_t entry)
+{
+ uintptr_t pptr = entry.paddr << 12;
+ return vmem_phys_tovirt(pptr);
+}
+
+__attribute__((aligned(0x1000))) pmle_t s_kernel_pml4[512]; // Page L4
+__attribute__((aligned(0x1000))) pmle_t s_kernel_pml3[512]; // Page L3
+__attribute__((aligned(0x1000))) pmle_t s_kernel_pml2[512]; // Page directory
+__attribute__((aligned(0x1000))) pmle_t s_kernel_pml1[512]; // Page table
+
+__attribute__((aligned(0x1000))) pmle_t s_idmap_pml3[512];
+__attribute__((aligned(0x1000))) pmle_t s_idmap_pml2[512];
+
+void
+vmem_setup(void)
+{
+ memset(s_kernel_pml4, 0, 0x1000);
+ memset(s_kernel_pml3, 0, 0x1000);
+ memset(s_kernel_pml2, 0, 0x1000);
+ memset(s_kernel_pml1, 0, 0x1000);
+
+ memset(s_idmap_pml3, 0, 0x1000);
+ memset(s_idmap_pml2, 0, 0x1000);
+
+ virtptr_t kernel_start = (virtptr_t)&_kernel_start;
+ virtptr_t kernel_end = (virtptr_t)&_kernel_end;
+
+ size_t kernel_size = kernel_end - kernel_start;
+ size_t kernel_size_pages = (kernel_size / 0x1000) + 1;
+
+ physptr_t kernel_pml4_base = vmem_tophys_koff((virtptr_t)&s_kernel_pml4);
+ physptr_t kernel_pml3_base = vmem_tophys_koff((virtptr_t)&s_kernel_pml3);
+ physptr_t kernel_pml2_base = vmem_tophys_koff((virtptr_t)&s_kernel_pml2);
+ physptr_t kernel_pml1_base = vmem_tophys_koff((virtptr_t)&s_kernel_pml1);
+
+ physptr_t idmap_pml3_base = vmem_tophys_koff((virtptr_t)&s_idmap_pml3);
+ physptr_t idmap_pml2_base = vmem_tophys_koff((virtptr_t)&s_idmap_pml2);
+
+ processor_t *processor = processor_current();
+ processor->pdir = kernel_pml4_base;
+
+ //Map memory identity pages.
+ s_kernel_pml4[256].value = idmap_pml3_base | 2 | 1;
+ s_idmap_pml3[0].value = idmap_pml2_base | 2 | 1;
+ for(int i = 0; i < 512; i++) {
+ s_idmap_pml2[i].value = (i * 0x1000 * 512) | 0x80 | 2 | 1;
+ }
+
+ //Map the kernel to himem.
+
+ pmli_t kernel_pml3_i = PML_I_FOR_LAYER(kernel_start, 3);
+ pmli_t kernel_pml2_i = PML_I_FOR_LAYER(kernel_start, 2);
+ pmli_t kernel_pml1_ib = PML_I_FOR_LAYER(kernel_start, 1);
+ pmli_t kernel_pml1_ie = PML_I_FOR_LAYER(kernel_end, 1) + 1;
+
+ s_kernel_pml4[511].value = kernel_pml3_base | 2 | 1;
+ s_kernel_pml3[kernel_pml3_i].value = kernel_pml2_base | 2 | 1;
+ s_kernel_pml2[kernel_pml2_i].value = kernel_pml1_base | 2 | 1;
+ for(pmli_t i = kernel_pml1_ib; i < kernel_pml1_ie; i++) {
+ s_kernel_pml1[i].value = ((i * 0x1000) + s_kpbase()) | 3;
+ }
+
+ __asm__ volatile("mov %0, %%cr3":: "r"(kernel_pml4_base));
+
+ //Add page mapping object to init directory.
+ uint8_t pm_i = _initDirectory.self.data++;
+ _initDirectory.entries[pm_i] = (objdir_entry_t) {
+ .type = KO_MEMORY_MAPPING_PAGE,
+ .data = kernel_pml4_base
+ };
+ _initData.pm_object = pm_i;
+}
diff --git a/arch/x86_64/panic.c b/arch/x86_64/panic.c
new file mode 100644
index 0000000..2e34500
--- /dev/null
+++ b/arch/x86_64/panic.c
@@ -0,0 +1,36 @@
+#include "jove.h"
+#include "arch/x86_64/idt.h"
+#include "print.h"
+
+#include <stddef.h>
+
+struct stackFrame {
+ struct stackFrame *ebp;
+ uintptr_t eip;
+};
+
+void
+kpanic_state(ivt_state_t *state, const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ kprintf("Kernel Panic!\n");
+
+ kvprintf(fmt, ap);
+ kprintf("\n\nCore dump:\n");
+
+ kprintf("RAX %p | RBX %p | RCX %p | RDX %p\n", state->rax, state->rbx, state->rcx, state->rdx);
+ kprintf("RSI %p | RDI %p | RSP %p | RBP %p\n", state->rsi, state->rdi, state->rsp, state->rbp);
+ kprintf("R8 %p | R9 %p | R10 %p | R11 %p\n", state->r8, state->r9, state->r10, state->r11);
+ kprintf("R12 %p | R13 %p | R14 %p | R15 %p\n", state->r12, state->r13, state->r14, state->r15);
+ kprintf("RIP %p\n", state->rip);
+
+ kprintf("\nStack trace:\n");
+ struct stackFrame *frame = (struct stackFrame*)state->rbp;
+ for(size_t framei = 0; frame && framei < 64; ++framei) {
+ kprintf("%i %p : %p\n", framei, frame, frame->eip);
+ frame = frame->ebp;
+ }
+
+ hcf();
+}
diff --git a/arch/x86_64/processor.c b/arch/x86_64/processor.c
new file mode 100644
index 0000000..b39ba63
--- /dev/null
+++ b/arch/x86_64/processor.c
@@ -0,0 +1,126 @@
+#include "device/processor.h"
+#include "arch/x86_64/tables.h"
+#include "include/arch/x86_64/idt.h"
+#include "include/arch/x86_64/object.h"
+#include "jove.h"
+
+processor_t s_bsp = {
+ .odir = &_initDirectory
+};
+
+struct jove_ObjectDirectory s_processor_dir = {
+ .entries = {
+ [0] = {
+ .type = KO_OBJECT_DIRECTORY,
+ .data = 2
+ },
+ [1] = {
+ .type = KO_DEV_PROCESSOR,
+ .data = (uintptr_t)&s_bsp
+ }
+ }
+};
+
+char s_initial_response_buffer[256] = { 0 };
+
+typedef union msr_efer
+{
+ struct {
+ uint8_t sce : 1;
+ uint8_t resv : 7;
+ uint8_t lme : 1;
+ uint8_t unk0 : 1;
+ uint8_t lma : 1;
+ uint8_t nxe : 1;
+ uint8_t svme : 1;
+ uint8_t lmsle : 1;
+ uint8_t ffxsr : 1;
+ uint8_t tce : 1;
+ };
+ uint32_t v[2];
+} msr_efer_t;
+
+typedef union msr_star
+{
+ struct {
+ uint32_t eip;
+ uint16_t kcs;
+ uint16_t ucs;
+ };
+ uint32_t v[2];
+} msr_star_t;
+
+typedef union msr_lstar
+{
+ uint32_t v[2];
+ uintptr_t ip;
+} msr_lstar_t;
+
+static void
+s_enable_sce(void)
+{
+ msr_efer_t feat;
+ rdmsr(MSR_EFER, &feat.v[0], &feat.v[1]);
+
+ feat.sce = 1;
+ wrmsr(MSR_EFER, feat.v[0], feat.v[1]);
+
+ msr_star_t star;
+ star.kcs = GDT_ENTRY_KERNEL_CODE * sizeof(segment_descriptor_t);
+ star.ucs = GDT_ENTRY_USER_CODE * sizeof(segment_descriptor_t);
+ wrmsr(MSR_STAR, star.v[0], star.v[1]);
+
+ extern void _syscall_entry(void);
+ msr_lstar_t lstar;
+ lstar.ip = (uintptr_t)_syscall_entry;
+ wrmsr(MSR_LSTAR, lstar.v[0], lstar.v[1]);
+}
+
+void
+processor_setup(void *_processor)
+{
+ processor_t *processor = (processor_t*)_processor;
+ gdt_setup(processor);
+ idt_setup(processor);
+
+ wrmsr(MSR_GS_BASE,
+ (uint32_t)((uintptr_t)_processor & 0xFFFFFFFF),
+ (uint32_t)((uintptr_t)_processor >> 32));
+}
+
+void
+bsp_setup(void)
+{
+ uint64_t pd_i = _initDirectory.self.data++;
+
+ _initDirectory.entries[pd_i] = (objdir_entry_t) {
+ .type = KO_OBJECT_DIRECTORY,
+ .data = (uintptr_t)(&s_processor_dir)
+ };
+ _initData.processor_dir = pd_i;
+
+ ivt_setup();
+ s_enable_sce();
+
+ processor_setup(&s_bsp);
+}
+
+void
+rdmsr(uint32_t msr, uint32_t *lo, uint32_t *hi)
+{
+ __asm__ volatile("rdmsr": "=a"(*lo), "=d"(*hi): "c"(msr));
+}
+
+void
+wrmsr(uint32_t msr, uint32_t lo, uint32_t hi)
+{
+ __asm__ volatile("wrmsr":: "a"(lo), "d"(hi), "c"(msr));
+}
+
+void*
+processor_current(void)
+{
+ uint64_t r = 0;
+ rdmsr(MSR_GS_BASE, (uint32_t*)&r, ((uint32_t*)&r) + 1);
+ return (void*)r;
+}
diff --git a/arch/x86_64/savestate.s b/arch/x86_64/savestate.s
new file mode 100644
index 0000000..f48b62c
--- /dev/null
+++ b/arch/x86_64/savestate.s
@@ -0,0 +1,35 @@
+.macro pushall
+ pushq %rax
+ pushq %rbx
+ pushq %rcx
+ pushq %rdx
+ pushq %rsi
+ pushq %rdi
+ pushq %rbp
+ pushq %r8
+ pushq %r9
+ pushq %r10
+ pushq %r11
+ pushq %r12
+ pushq %r13
+ pushq %r14
+ pushq %r15
+.endm
+
+.macro popall
+ popq %r15
+ popq %r14
+ popq %r13
+ popq %r12
+ popq %r11
+ popq %r10
+ popq %r9
+ popq %r8
+ popq %rbp
+ popq %rdi
+ popq %rsi
+ popq %rdx
+ popq %rcx
+ popq %rbx
+ popq %rax
+.endm
diff --git a/arch/x86_64/syscall.c b/arch/x86_64/syscall.c
new file mode 100644
index 0000000..a563525
--- /dev/null
+++ b/arch/x86_64/syscall.c
@@ -0,0 +1,25 @@
+#include "arch/x86_64/processor.h"
+#include <stddef.h>
+
+__attribute__((naked))
+void _syscall_entry(void)
+{
+ __asm__ volatile(" \
+ pushq %%r11; \
+ pushq %%rcx; \
+ swapgs; \
+ movq %%gs:%c[tcb], %%rax; \
+ movq %%rsp, %c[sp](%%rax); \
+ movq %c[ksp](%%rax), %%rsp; \
+ pushq %c[sp](%%rax); \
+ callq _syscall_handler; \
+ swapgs; \
+ popq %%rsp; \
+ popq %%rcx; \
+ popq %%r11; \
+ sysretq;"
+ ::
+ [tcb] "i"(offsetof(processor_t, tcb)),
+ [sp] "i"(offsetof(tcb_t, sp)),
+ [ksp] "i"(offsetof(tcb_t, ksp)));
+}
diff --git a/arch/x86_64/untyped_memory.c b/arch/x86_64/untyped_memory.c
new file mode 100644
index 0000000..699cdbf
--- /dev/null
+++ b/arch/x86_64/untyped_memory.c
@@ -0,0 +1,54 @@
+#include "memory.h"
+#include "jove.h"
+#include "print.h"
+#include "arch/x86_64/object.h"
+#include "arch/x86_64/page.h"
+#include <stddef.h>
+
+static struct jove_ObjectDirectory s_untyped_dir = {
+ .self = { .type = KO_OBJECT_DIRECTORY, .data = 1 },
+};
+
+static void s_populate_tables();
+
+#if defined(__limine__)
+
+#include "boot/limine/limine.h"
+static volatile struct limine_memmap_request s_memmap_req = {
+ .id = LIMINE_MEMMAP_REQUEST,
+ .response = NULL
+};
+
+void
+pmem_setup(void)
+{
+ size_t diri = _initDirectory.self.data++;
+ _initDirectory.entries[diri] = (objdir_entry_t) {
+ .type = KO_OBJECT_DIRECTORY,
+ .data = (uintptr_t)&s_untyped_dir
+ };
+ _initData.untyped_data_dir = diri;
+
+ if(s_memmap_req.response == NULL) {
+ klogf("Failed to load physical memory map");
+ hcf();
+ }
+ struct limine_memmap_response *response = s_memmap_req.response;
+ for(size_t i = 0; i < response->entry_count; i++) {
+ struct limine_memmap_entry *entry = response->entries[i];
+ if(entry->type != LIMINE_MEMMAP_USABLE) continue;
+
+ size_t table_index = s_untyped_dir.self.data++;
+ uintmax_t *untyped_data = vmem_phys_tovirt(entry->base);
+ objdir_entry_t *table_entry = &s_untyped_dir.entries[table_index];
+
+ table_entry->type = KO_MEMORY_UNTYPED;
+ table_entry->data = (uintptr_t)entry->base;
+ *untyped_data = entry->length;
+
+ klogf("New untyped block %i at %p:%p\n",
+ table_index, entry->base, entry->length);
+ }
+}
+
+#endif
diff --git a/arch/x86_64/untyped_retype_page.c b/arch/x86_64/untyped_retype_page.c
new file mode 100644
index 0000000..216d88d
--- /dev/null
+++ b/arch/x86_64/untyped_retype_page.c
@@ -0,0 +1,51 @@
+#include "memory.h"
+#include "arch/x86_64/page.h"
+#include "print.h"
+#include <stddef.h>
+
+static int
+s_untyped_retype(
+ objdir_entry_t *untyped_entry,
+ size_t size,
+ size_t align,
+ void **dest)
+{
+ if(untyped_entry->type != KO_MEMORY_UNTYPED) return -1;
+ if((untyped_entry->data & (align - 1)) != 0) return -2; //EALIGN
+
+ uintptr_t *untyped_data = vmem_phys_tovirt(untyped_entry->data);
+ uintmax_t untyped_size = *untyped_data;
+
+ if(untyped_size <= size) {
+ return -3;
+ }
+
+ *untyped_data -= size;
+ *dest = (void*)(untyped_entry->data + untyped_size - size);
+
+ return 0;
+}
+
+int untyped_retype_page(objdir_entry_t *untyped_entry, void **dest)
+{
+ int r = s_untyped_retype(untyped_entry, 0x1000, 0x1000, dest);
+ if(r != 0) return r;
+
+ #ifdef DBG_MEM
+ klogf("Untyped block %p retyped to page at %p\n", untyped_entry->data, *dest);
+ #endif
+ return r;
+}
+
+int
+untyped_retype_kernel_stack(objdir_entry_t *untyped_entry, objdir_entry_t *dest_entry)
+{
+ int r = s_untyped_retype(untyped_entry, KERNEL_STACK_SIZE, 1, (void**)(&dest_entry->data));
+ if(r != 0) return r;
+
+ dest_entry->type = KO_KERNEL_STACK;
+ #ifdef DBG_MEM
+ klogf("Untyped block %p retyped to kernel stack at %p\n", untyped_entry->data, dest_entry->data);
+ #endif
+ return r;
+}
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);
+}