summaryrefslogtreecommitdiffstats
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
downloadjove-kernel-b905869a35f062a4e5072f10bec3a2ba3db0e365.tar.gz
jove-kernel-b905869a35f062a4e5072f10bec3a2ba3db0e365.tar.bz2
jove-kernel-b905869a35f062a4e5072f10bec3a2ba3db0e365.zip
working userland with some invoke syscalls
-rw-r--r--.gitignore5
-rw-r--r--Makefile37
-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
-rw-r--r--boot/bootargs.c25
-rw-r--r--boot/limine/limine.c38
-rw-r--r--boot/limine/limine.h578
-rw-r--r--config.mk44
-rw-r--r--device/initrd.c116
-rw-r--r--device/portio_uart.c68
-rw-r--r--device/uart.c13
-rw-r--r--include/arch/x86_64/idt.h18
-rw-r--r--include/arch/x86_64/object.h26
-rw-r--r--include/arch/x86_64/page.h41
-rw-r--r--include/arch/x86_64/processor.h66
-rw-r--r--include/arch/x86_64/tables.h51
-rw-r--r--include/boot.h7
-rw-r--r--include/bootargs.h13
-rw-r--r--include/device/initrd.h29
-rw-r--r--include/device/portio.h26
-rw-r--r--include/device/portio_uart.h27
-rw-r--r--include/device/processor.h22
-rw-r--r--include/device/uart.h11
-rw-r--r--include/error.h8
-rw-r--r--include/init.h10
-rw-r--r--include/jove.h15
-rw-r--r--include/memory.h17
-rw-r--r--include/object.h61
-rw-r--r--include/panic.h9
-rw-r--r--include/print.h17
-rw-r--r--include/string.h25
-rw-r--r--include/syscall.h46
-rw-r--r--include/tcb.h18
-rw-r--r--lib/itoa.c32
-rw-r--r--lib/log.c14
-rw-r--r--lib/mem.c27
-rw-r--r--lib/objdir.c15
-rw-r--r--lib/panic.c15
-rw-r--r--lib/print.c146
-rw-r--r--lib/strcmp.c8
-rw-r--r--lib/strlen.c9
-rw-r--r--link/x86_64-jove.ld52
-rw-r--r--main.c44
-rw-r--r--syscall/handler.c66
-rw-r--r--syscall/handles.h8
-rw-r--r--syscall/invoke_objdir.c34
56 files changed, 2856 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..8965c2e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+*.o
+*.a
+*.d
+*.elf
+.ccls*
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..1d26c59
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,37 @@
+include $(CONFIG)
+include config.mk
+
+CDIRS := boot
+CDIRS += boot/$(TARGET_BOOTLOADER)
+CDIRS += device lib syscall
+
+CFILES := $(wildcard *.c)
+CFILES += $(wildcard arch/$(TARGET_MACHINE)/*.c)
+CFILES += $(foreach dir, $(CDIRS), $(wildcard $(dir)/*.c))
+CFILES += $(foreach dir, $(CDIRS), $(wildcard arch/$(TARGET_MACHINE)/$(dir)/*.c))
+
+SFILES := $(wildcard *.s)
+SFILES += $(wildcard arch/$(TARGET_MACHINE)/*.s)
+SFILES += $(foreach dir, $(CDIRS), $(wildcard $(dir)/*.s))
+SFILES += $(foreach dir, $(CDIRS), $(wildcard arch/$(TARGET_MACHINE)/$(dir)/*.s))
+
+OBJFILES := $(patsubst %.c,%.o,$(CFILES))
+OBJFILES += $(patsubst %.s,%.o,$(SFILES))
+
+BIN := jove.elf
+
+all: $(BIN)
+
+.PHONY: clean
+clean:
+ rm $(OBJFILES)
+ rm $(BIN)
+
+$(BIN): ${OBJFILES}
+ $(LD) $(LDFLAGS) ${OBJFILES} -o $(BIN)
+
+%.o:%.c
+ $(CC) $(CFLAGS) -c $< -o $@
+
+%.o:%.S
+ $(AS) -g -c $< -o $@
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);
+}
diff --git a/boot/bootargs.c b/boot/bootargs.c
new file mode 100644
index 0000000..81ba57d
--- /dev/null
+++ b/boot/bootargs.c
@@ -0,0 +1,25 @@
+#include "bootargs.h"
+#include "boot.h"
+#include "string.h"
+#include <stdint.h>
+#include "print.h"
+
+char*
+bootargs_getarg(const char *key)
+{
+ size_t keylen = strlen(key);
+ char *arg = jove_bootargs;
+ for(; (uintptr_t)arg < (uintptr_t)jove_bootargs + jove_bootargs_len; arg += strlen(arg) + 1) {
+ size_t arglen = strlen(arg);
+ if(arglen < keylen) continue;
+
+ for(size_t i = 0; i < keylen; i++) {
+ if(arg[i] != key[i]) goto eol;
+ }
+ if(arg[keylen] != '=') continue;
+ return arg + keylen + 1;
+eol:
+ continue;
+ }
+ return NULL;
+}
diff --git a/boot/limine/limine.c b/boot/limine/limine.c
new file mode 100644
index 0000000..6a1bb43
--- /dev/null
+++ b/boot/limine/limine.c
@@ -0,0 +1,38 @@
+#include "limine.h"
+#include "boot.h"
+#include "string.h"
+
+char s_kernel_stack_initial[8192];
+
+char *jove_bootargs;
+int jove_bootargs_len;
+
+struct limine_kernel_file_request s_kernel_file_req = {
+ .id = LIMINE_KERNEL_FILE_REQUEST
+};
+
+void
+_start(void)
+{
+ __asm__ volatile("movq %0, %%rsp":: "r"((uintptr_t)&s_kernel_stack_initial + 8191));
+
+ struct limine_kernel_file_response *kernel_file_response = s_kernel_file_req.response;
+ jove_bootargs = kernel_file_response->kernel_file->cmdline;
+ jove_bootargs_len = strlen(jove_bootargs);
+
+ //Zero all spaces in bootargs not enclosed with quotes.
+ for(char *c = jove_bootargs; *c; c++) {
+ if(*c == '"') {
+ *c = 0;
+ for(++c; *c && *c != '"'; c++);
+ *(c++) = 0;
+ continue;
+ }
+ if(*c == ' ') *c = 0;
+ }
+
+ extern void _jove_main(void);
+
+ __asm__ volatile("movq $0, %%rbp"::);
+ _jove_main();
+}
diff --git a/boot/limine/limine.h b/boot/limine/limine.h
new file mode 100644
index 0000000..b7baa79
--- /dev/null
+++ b/boot/limine/limine.h
@@ -0,0 +1,578 @@
+/* BSD Zero Clause License */
+
+/* Copyright (C) 2022-2023 mintsuki and contributors.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _LIMINE_H
+#define _LIMINE_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+/* Misc */
+
+#ifdef LIMINE_NO_POINTERS
+# define LIMINE_PTR(TYPE) uint64_t
+#else
+# define LIMINE_PTR(TYPE) TYPE
+#endif
+
+#ifdef __GNUC__
+# define LIMINE_DEPRECATED __attribute__((__deprecated__))
+# define LIMINE_DEPRECATED_IGNORE_START \
+ _Pragma("GCC diagnostic push") \
+ _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"")
+# define LIMINE_DEPRECATED_IGNORE_END \
+ _Pragma("GCC diagnostic pop")
+#else
+# define LIMINE_DEPRECATED
+# define LIMINE_DEPRECATED_IGNORE_START
+# define LIMINE_DEPRECATED_IGNORE_END
+#endif
+
+#define LIMINE_BASE_REVISION(N) \
+ uint64_t limine_base_revision[3] = { 0xf9562b2d5c95a6c8, 0x6a7b384944536bdc, (N) };
+
+#define LIMINE_BASE_REVISION_SUPPORTED (limine_base_revision[2] == 0)
+
+#define LIMINE_COMMON_MAGIC 0xc7b1dd30df4c8b88, 0x0a82e883a194f07b
+
+struct limine_uuid {
+ uint32_t a;
+ uint16_t b;
+ uint16_t c;
+ uint8_t d[8];
+};
+
+#define LIMINE_MEDIA_TYPE_GENERIC 0
+#define LIMINE_MEDIA_TYPE_OPTICAL 1
+#define LIMINE_MEDIA_TYPE_TFTP 2
+
+struct limine_file {
+ uint64_t revision;
+ LIMINE_PTR(void *) address;
+ uint64_t size;
+ LIMINE_PTR(char *) path;
+ LIMINE_PTR(char *) cmdline;
+ uint32_t media_type;
+ uint32_t unused;
+ uint32_t tftp_ip;
+ uint32_t tftp_port;
+ uint32_t partition_index;
+ uint32_t mbr_disk_id;
+ struct limine_uuid gpt_disk_uuid;
+ struct limine_uuid gpt_part_uuid;
+ struct limine_uuid part_uuid;
+};
+
+/* Boot info */
+
+#define LIMINE_BOOTLOADER_INFO_REQUEST { LIMINE_COMMON_MAGIC, 0xf55038d8e2a1202f, 0x279426fcf5f59740 }
+
+struct limine_bootloader_info_response {
+ uint64_t revision;
+ LIMINE_PTR(char *) name;
+ LIMINE_PTR(char *) version;
+};
+
+struct limine_bootloader_info_request {
+ uint64_t id[4];
+ uint64_t revision;
+ LIMINE_PTR(struct limine_bootloader_info_response *) response;
+};
+
+/* Stack size */
+
+#define LIMINE_STACK_SIZE_REQUEST { LIMINE_COMMON_MAGIC, 0x224ef0460a8e8926, 0xe1cb0fc25f46ea3d }
+
+struct limine_stack_size_response {
+ uint64_t revision;
+};
+
+struct limine_stack_size_request {
+ uint64_t id[4];
+ uint64_t revision;
+ LIMINE_PTR(struct limine_stack_size_response *) response;
+ uint64_t stack_size;
+};
+
+/* HHDM */
+
+#define LIMINE_HHDM_REQUEST { LIMINE_COMMON_MAGIC, 0x48dcf1cb8ad2b852, 0x63984e959a98244b }
+
+struct limine_hhdm_response {
+ uint64_t revision;
+ uint64_t offset;
+};
+
+struct limine_hhdm_request {
+ uint64_t id[4];
+ uint64_t revision;
+ LIMINE_PTR(struct limine_hhdm_response *) response;
+};
+
+/* Framebuffer */
+
+#define LIMINE_FRAMEBUFFER_REQUEST { LIMINE_COMMON_MAGIC, 0x9d5827dcd881dd75, 0xa3148604f6fab11b }
+
+#define LIMINE_FRAMEBUFFER_RGB 1
+
+struct limine_video_mode {
+ uint64_t pitch;
+ uint64_t width;
+ uint64_t height;
+ uint16_t bpp;
+ uint8_t memory_model;
+ uint8_t red_mask_size;
+ uint8_t red_mask_shift;
+ uint8_t green_mask_size;
+ uint8_t green_mask_shift;
+ uint8_t blue_mask_size;
+ uint8_t blue_mask_shift;
+};
+
+struct limine_framebuffer {
+ LIMINE_PTR(void *) address;
+ uint64_t width;
+ uint64_t height;
+ uint64_t pitch;
+ uint16_t bpp;
+ uint8_t memory_model;
+ uint8_t red_mask_size;
+ uint8_t red_mask_shift;
+ uint8_t green_mask_size;
+ uint8_t green_mask_shift;
+ uint8_t blue_mask_size;
+ uint8_t blue_mask_shift;
+ uint8_t unused[7];
+ uint64_t edid_size;
+ LIMINE_PTR(void *) edid;
+ /* Response revision 1 */
+ uint64_t mode_count;
+ LIMINE_PTR(struct limine_video_mode **) modes;
+};
+
+struct limine_framebuffer_response {
+ uint64_t revision;
+ uint64_t framebuffer_count;
+ LIMINE_PTR(struct limine_framebuffer **) framebuffers;
+};
+
+struct limine_framebuffer_request {
+ uint64_t id[4];
+ uint64_t revision;
+ LIMINE_PTR(struct limine_framebuffer_response *) response;
+};
+
+/* Terminal */
+
+#define LIMINE_TERMINAL_REQUEST { LIMINE_COMMON_MAGIC, 0xc8ac59310c2b0844, 0xa68d0c7265d38878 }
+
+#define LIMINE_TERMINAL_CB_DEC 10
+#define LIMINE_TERMINAL_CB_BELL 20
+#define LIMINE_TERMINAL_CB_PRIVATE_ID 30
+#define LIMINE_TERMINAL_CB_STATUS_REPORT 40
+#define LIMINE_TERMINAL_CB_POS_REPORT 50
+#define LIMINE_TERMINAL_CB_KBD_LEDS 60
+#define LIMINE_TERMINAL_CB_MODE 70
+#define LIMINE_TERMINAL_CB_LINUX 80
+
+#define LIMINE_TERMINAL_CTX_SIZE ((uint64_t)(-1))
+#define LIMINE_TERMINAL_CTX_SAVE ((uint64_t)(-2))
+#define LIMINE_TERMINAL_CTX_RESTORE ((uint64_t)(-3))
+#define LIMINE_TERMINAL_FULL_REFRESH ((uint64_t)(-4))
+
+/* Response revision 1 */
+#define LIMINE_TERMINAL_OOB_OUTPUT_GET ((uint64_t)(-10))
+#define LIMINE_TERMINAL_OOB_OUTPUT_SET ((uint64_t)(-11))
+
+#define LIMINE_TERMINAL_OOB_OUTPUT_OCRNL (1 << 0)
+#define LIMINE_TERMINAL_OOB_OUTPUT_OFDEL (1 << 1)
+#define LIMINE_TERMINAL_OOB_OUTPUT_OFILL (1 << 2)
+#define LIMINE_TERMINAL_OOB_OUTPUT_OLCUC (1 << 3)
+#define LIMINE_TERMINAL_OOB_OUTPUT_ONLCR (1 << 4)
+#define LIMINE_TERMINAL_OOB_OUTPUT_ONLRET (1 << 5)
+#define LIMINE_TERMINAL_OOB_OUTPUT_ONOCR (1 << 6)
+#define LIMINE_TERMINAL_OOB_OUTPUT_OPOST (1 << 7)
+
+LIMINE_DEPRECATED_IGNORE_START
+
+struct LIMINE_DEPRECATED limine_terminal;
+
+typedef void (*limine_terminal_write)(struct limine_terminal *, const char *, uint64_t);
+typedef void (*limine_terminal_callback)(struct limine_terminal *, uint64_t, uint64_t, uint64_t, uint64_t);
+
+struct LIMINE_DEPRECATED limine_terminal {
+ uint64_t columns;
+ uint64_t rows;
+ LIMINE_PTR(struct limine_framebuffer *) framebuffer;
+};
+
+struct LIMINE_DEPRECATED limine_terminal_response {
+ uint64_t revision;
+ uint64_t terminal_count;
+ LIMINE_PTR(struct limine_terminal **) terminals;
+ LIMINE_PTR(limine_terminal_write) write;
+};
+
+struct LIMINE_DEPRECATED limine_terminal_request {
+ uint64_t id[4];
+ uint64_t revision;
+ LIMINE_PTR(struct limine_terminal_response *) response;
+ LIMINE_PTR(limine_terminal_callback) callback;
+};
+
+LIMINE_DEPRECATED_IGNORE_END
+
+/* Paging mode */
+
+#define LIMINE_PAGING_MODE_REQUEST { LIMINE_COMMON_MAGIC, 0x95c1a0edab0944cb, 0xa4e5cb3842f7488a }
+
+#if defined (__x86_64__) || defined (__i386__)
+#define LIMINE_PAGING_MODE_X86_64_4LVL 0
+#define LIMINE_PAGING_MODE_X86_64_5LVL 1
+#define LIMINE_PAGING_MODE_MAX LIMINE_PAGING_MODE_X86_64_5LVL
+#define LIMINE_PAGING_MODE_DEFAULT LIMINE_PAGING_MODE_X86_64_4LVL
+#elif defined (__aarch64__)
+#define LIMINE_PAGING_MODE_AARCH64_4LVL 0
+#define LIMINE_PAGING_MODE_AARCH64_5LVL 1
+#define LIMINE_PAGING_MODE_MAX LIMINE_PAGING_MODE_AARCH64_5LVL
+#define LIMINE_PAGING_MODE_DEFAULT LIMINE_PAGING_MODE_AARCH64_4LVL
+#elif defined (__riscv) && (__riscv_xlen == 64)
+#define LIMINE_PAGING_MODE_RISCV_SV39 0
+#define LIMINE_PAGING_MODE_RISCV_SV48 1
+#define LIMINE_PAGING_MODE_RISCV_SV57 2
+#define LIMINE_PAGING_MODE_MAX LIMINE_PAGING_MODE_RISCV_SV57
+#define LIMINE_PAGING_MODE_DEFAULT LIMINE_PAGING_MODE_RISCV_SV48
+#else
+#error Unknown architecture
+#endif
+
+struct limine_paging_mode_response {
+ uint64_t revision;
+ uint64_t mode;
+ uint64_t flags;
+};
+
+struct limine_paging_mode_request {
+ uint64_t id[4];
+ uint64_t revision;
+ LIMINE_PTR(struct limine_paging_mode_response *) response;
+ uint64_t mode;
+ uint64_t flags;
+};
+
+/* 5-level paging */
+
+#define LIMINE_5_LEVEL_PAGING_REQUEST { LIMINE_COMMON_MAGIC, 0x94469551da9b3192, 0xebe5e86db7382888 }
+
+LIMINE_DEPRECATED_IGNORE_START
+
+struct LIMINE_DEPRECATED limine_5_level_paging_response {
+ uint64_t revision;
+};
+
+struct LIMINE_DEPRECATED limine_5_level_paging_request {
+ uint64_t id[4];
+ uint64_t revision;
+ LIMINE_PTR(struct limine_5_level_paging_response *) response;
+};
+
+LIMINE_DEPRECATED_IGNORE_END
+
+/* SMP */
+
+#define LIMINE_SMP_REQUEST { LIMINE_COMMON_MAGIC, 0x95a67b819a1b857e, 0xa0b61b723b6a73e0 }
+
+struct limine_smp_info;
+
+typedef void (*limine_goto_address)(struct limine_smp_info *);
+
+#if defined (__x86_64__) || defined (__i386__)
+
+#define LIMINE_SMP_X2APIC (1 << 0)
+
+struct limine_smp_info {
+ uint32_t processor_id;
+ uint32_t lapic_id;
+ uint64_t reserved;
+ LIMINE_PTR(limine_goto_address) goto_address;
+ uint64_t extra_argument;
+};
+
+struct limine_smp_response {
+ uint64_t revision;
+ uint32_t flags;
+ uint32_t bsp_lapic_id;
+ uint64_t cpu_count;
+ LIMINE_PTR(struct limine_smp_info **) cpus;
+};
+
+#elif defined (__aarch64__)
+
+struct limine_smp_info {
+ uint32_t processor_id;
+ uint32_t gic_iface_no;
+ uint64_t mpidr;
+ uint64_t reserved;
+ LIMINE_PTR(limine_goto_address) goto_address;
+ uint64_t extra_argument;
+};
+
+struct limine_smp_response {
+ uint64_t revision;
+ uint64_t flags;
+ uint64_t bsp_mpidr;
+ uint64_t cpu_count;
+ LIMINE_PTR(struct limine_smp_info **) cpus;
+};
+
+#elif defined (__riscv) && (__riscv_xlen == 64)
+
+struct limine_smp_info {
+ uint64_t processor_id;
+ uint64_t hartid;
+ uint64_t reserved;
+ LIMINE_PTR(limine_goto_address) goto_address;
+ uint64_t extra_argument;
+};
+
+struct limine_smp_response {
+ uint64_t revision;
+ uint64_t flags;
+ uint64_t bsp_hartid;
+ uint64_t cpu_count;
+ LIMINE_PTR(struct limine_smp_info **) cpus;
+};
+
+#else
+#error Unknown architecture
+#endif
+
+struct limine_smp_request {
+ uint64_t id[4];
+ uint64_t revision;
+ LIMINE_PTR(struct limine_smp_response *) response;
+ uint64_t flags;
+};
+
+/* Memory map */
+
+#define LIMINE_MEMMAP_REQUEST { LIMINE_COMMON_MAGIC, 0x67cf3d9d378a806f, 0xe304acdfc50c3c62 }
+
+#define LIMINE_MEMMAP_USABLE 0
+#define LIMINE_MEMMAP_RESERVED 1
+#define LIMINE_MEMMAP_ACPI_RECLAIMABLE 2
+#define LIMINE_MEMMAP_ACPI_NVS 3
+#define LIMINE_MEMMAP_BAD_MEMORY 4
+#define LIMINE_MEMMAP_BOOTLOADER_RECLAIMABLE 5
+#define LIMINE_MEMMAP_KERNEL_AND_MODULES 6
+#define LIMINE_MEMMAP_FRAMEBUFFER 7
+
+struct limine_memmap_entry {
+ uint64_t base;
+ uint64_t length;
+ uint64_t type;
+};
+
+struct limine_memmap_response {
+ uint64_t revision;
+ uint64_t entry_count;
+ LIMINE_PTR(struct limine_memmap_entry **) entries;
+};
+
+struct limine_memmap_request {
+ uint64_t id[4];
+ uint64_t revision;
+ LIMINE_PTR(struct limine_memmap_response *) response;
+};
+
+/* Entry point */
+
+#define LIMINE_ENTRY_POINT_REQUEST { LIMINE_COMMON_MAGIC, 0x13d86c035a1cd3e1, 0x2b0caa89d8f3026a }
+
+typedef void (*limine_entry_point)(void);
+
+struct limine_entry_point_response {
+ uint64_t revision;
+};
+
+struct limine_entry_point_request {
+ uint64_t id[4];
+ uint64_t revision;
+ LIMINE_PTR(struct limine_entry_point_response *) response;
+ LIMINE_PTR(limine_entry_point) entry;
+};
+
+/* Kernel File */
+
+#define LIMINE_KERNEL_FILE_REQUEST { LIMINE_COMMON_MAGIC, 0xad97e90e83f1ed67, 0x31eb5d1c5ff23b69 }
+
+struct limine_kernel_file_response {
+ uint64_t revision;
+ LIMINE_PTR(struct limine_file *) kernel_file;
+};
+
+struct limine_kernel_file_request {
+ uint64_t id[4];
+ uint64_t revision;
+ LIMINE_PTR(struct limine_kernel_file_response *) response;
+};
+
+/* Module */
+
+#define LIMINE_MODULE_REQUEST { LIMINE_COMMON_MAGIC, 0x3e7e279702be32af, 0xca1c4f3bd1280cee }
+
+#define LIMINE_INTERNAL_MODULE_REQUIRED (1 << 0)
+
+struct limine_internal_module {
+ LIMINE_PTR(const char *) path;
+ LIMINE_PTR(const char *) cmdline;
+ uint64_t flags;
+};
+
+struct limine_module_response {
+ uint64_t revision;
+ uint64_t module_count;
+ LIMINE_PTR(struct limine_file **) modules;
+};
+
+struct limine_module_request {
+ uint64_t id[4];
+ uint64_t revision;
+ LIMINE_PTR(struct limine_module_response *) response;
+
+ /* Request revision 1 */
+ uint64_t internal_module_count;
+ LIMINE_PTR(struct limine_internal_module **) internal_modules;
+};
+
+/* RSDP */
+
+#define LIMINE_RSDP_REQUEST { LIMINE_COMMON_MAGIC, 0xc5e77b6b397e7b43, 0x27637845accdcf3c }
+
+struct limine_rsdp_response {
+ uint64_t revision;
+ LIMINE_PTR(void *) address;
+};
+
+struct limine_rsdp_request {
+ uint64_t id[4];
+ uint64_t revision;
+ LIMINE_PTR(struct limine_rsdp_response *) response;
+};
+
+/* SMBIOS */
+
+#define LIMINE_SMBIOS_REQUEST { LIMINE_COMMON_MAGIC, 0x9e9046f11e095391, 0xaa4a520fefbde5ee }
+
+struct limine_smbios_response {
+ uint64_t revision;
+ LIMINE_PTR(void *) entry_32;
+ LIMINE_PTR(void *) entry_64;
+};
+
+struct limine_smbios_request {
+ uint64_t id[4];
+ uint64_t revision;
+ LIMINE_PTR(struct limine_smbios_response *) response;
+};
+
+/* EFI system table */
+
+#define LIMINE_EFI_SYSTEM_TABLE_REQUEST { LIMINE_COMMON_MAGIC, 0x5ceba5163eaaf6d6, 0x0a6981610cf65fcc }
+
+struct limine_efi_system_table_response {
+ uint64_t revision;
+ LIMINE_PTR(void *) address;
+};
+
+struct limine_efi_system_table_request {
+ uint64_t id[4];
+ uint64_t revision;
+ LIMINE_PTR(struct limine_efi_system_table_response *) response;
+};
+
+/* EFI memory map */
+
+#define LIMINE_EFI_MEMMAP_REQUEST { LIMINE_COMMON_MAGIC, 0x7df62a431d6872d5, 0xa4fcdfb3e57306c8 }
+
+struct limine_efi_memmap_response {
+ uint64_t revision;
+ LIMINE_PTR(void *) memmap;
+ uint64_t memmap_size;
+ uint64_t desc_size;
+ uint64_t desc_version;
+};
+
+struct limine_efi_memmap_request {
+ uint64_t id[4];
+ uint64_t revision;
+ LIMINE_PTR(struct limine_efi_memmap_response *) response;
+};
+
+/* Boot time */
+
+#define LIMINE_BOOT_TIME_REQUEST { LIMINE_COMMON_MAGIC, 0x502746e184c088aa, 0xfbc5ec83e6327893 }
+
+struct limine_boot_time_response {
+ uint64_t revision;
+ int64_t boot_time;
+};
+
+struct limine_boot_time_request {
+ uint64_t id[4];
+ uint64_t revision;
+ LIMINE_PTR(struct limine_boot_time_response *) response;
+};
+
+/* Kernel address */
+
+#define LIMINE_KERNEL_ADDRESS_REQUEST { LIMINE_COMMON_MAGIC, 0x71ba76863cc55f63, 0xb2644a48c516a487 }
+
+struct limine_kernel_address_response {
+ uint64_t revision;
+ uint64_t physical_base;
+ uint64_t virtual_base;
+};
+
+struct limine_kernel_address_request {
+ uint64_t id[4];
+ uint64_t revision;
+ LIMINE_PTR(struct limine_kernel_address_response *) response;
+};
+
+/* Device Tree Blob */
+
+#define LIMINE_DTB_REQUEST { LIMINE_COMMON_MAGIC, 0xb40ddb48fb54bac7, 0x545081493f81ffb7 }
+
+struct limine_dtb_response {
+ uint64_t revision;
+ LIMINE_PTR(void *) dtb_ptr;
+};
+
+struct limine_dtb_request {
+ uint64_t id[4];
+ uint64_t revision;
+ LIMINE_PTR(struct limine_dtb_response *) response;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/config.mk b/config.mk
new file mode 100644
index 0000000..4b940b5
--- /dev/null
+++ b/config.mk
@@ -0,0 +1,44 @@
+PROCESSORS_MAX=8
+ENABLE_UART = y
+ENABLE_PORTIO_UART = y
+ENABLE_INITRD = y
+
+DBG_MEM = y
+
+CFLAGS = \
+ -ffreestanding \
+ -mno-sse \
+ -nostdlib \
+ -fno-pie \
+ -fno-pic \
+ -g \
+ -D__$(TARGET_MACHINE)__ \
+ -D__$(TARGET_BOOTLOADER)__ \
+ -DPROCESSORS_MAX=$(PROCESSORS_MAX) \
+ -Iinclude \
+ -I.
+
+LDFLAGS = -nostdlib \
+ -z max-page-size=0x1000 \
+ -T link/$(TARGET_TRIPLET).ld
+
+ifeq "$(TARGET_MACHINE)" "x86_64"
+ CFLAGS += -m64 \
+ -mno-red-zone \
+ -mcmodel=kernel \
+ -mfsgsbase
+endif
+
+ifeq "$(ENABLE_UART)" "y"
+CFLAGS += -DENABLE_UART
+endif
+ifeq "$(ENABLE_PORTIO_UART)" "y"
+CFLAGS += -DENABLE_PORTIO_UART
+endif
+ifeq "$(ENABLE_INITRD)" "y"
+CFLAGS += -DENABLE_INITRD
+endif
+
+ifeq "$(DBG_MEM)" "y"
+CFLAGS += -DDBG_MEM
+endif
diff --git a/device/initrd.c b/device/initrd.c
new file mode 100644
index 0000000..36eefc6
--- /dev/null
+++ b/device/initrd.c
@@ -0,0 +1,116 @@
+#if defined(ENABLE_INITRD)
+
+#include <stddef.h>
+
+#include "device/initrd.h"
+#include "device/processor.h"
+#include "object.h"
+#include "jove.h"
+#include "bootargs.h"
+#include "print.h"
+#include "string.h"
+#include "panic.h"
+
+struct jove_ObjectDirectory s_initrd_dir = {
+ .entries = {
+ [0] = (objdir_entry_t){
+ .type = KO_OBJECT_DIRECTORY,
+ .data = 1
+ }
+ }
+};
+
+static uintptr_t s_initrd_getaddr(char *path);
+
+#if defined(__limine__)
+
+#include "boot/limine/limine.h"
+static struct limine_module_request s_module_request = {
+ .id = LIMINE_MODULE_REQUEST
+};
+
+static uintptr_t
+s_initrd_getaddr(char *path)
+{
+ struct limine_module_response *response = s_module_request.response;
+ for(size_t i = 0; i < response->module_count; i++) {
+ struct limine_file *module = response->modules[i];
+ if(strcmp(path, module->path) == 0)
+ return (uintptr_t)module->address;
+ }
+ kpanic("Failed to load module at path %s\n", path);
+}
+
+
+#endif
+
+static size_t
+s_tar_oct_dec(const char *oct)
+{
+ size_t r = 0;
+ for(; *oct; oct++) r = (r * 8) + (*oct - '0');
+ return r;
+}
+
+void
+tar_parse(tar_block_t *block)
+{
+ while(true) {
+ tar_header_t *header = &block->header;
+ if(*header->name == 0) return;
+ if(s_initrd_dir.self.data == OBJECT_DIRECTORY_MAX_ENTRIES) {
+ klogf("More than %i files in initrd! Skipping the rest.\n", OBJECT_DIRECTORY_MAX_ENTRIES - 1);
+ }
+
+ size_t file_size = s_tar_oct_dec(header->size);
+ s_initrd_dir.entries[s_initrd_dir.self.data++] = (objdir_entry_t) {
+ .type = KO_INITRD_FILE,
+ .data = (uintptr_t)block
+ };
+
+ klogf("Load file '%s' at %p size %x\n", block->header.name, block + 1, file_size);
+
+ block = &block[(file_size / 512) + 1];
+ if(file_size % 512) block++;
+ }
+}
+
+void
+initrd_setup(void)
+{
+ char *initrd_path = bootargs_getarg("initrd");
+ if(initrd_path == 0) {
+ kpanic("Missing kernel commandline argument for initrd.");
+ }
+
+ uintptr_t initrd_addr = s_initrd_getaddr(initrd_path);
+ klogf("Initrd found at %p\n", initrd_addr);
+ tar_parse((tar_block_t*)initrd_addr);
+
+ size_t initrd_diri = _initDirectory.self.data++;
+ _initDirectory.entries[initrd_diri] = (objdir_entry_t) {
+ .type = KO_OBJECT_DIRECTORY,
+ .data = (uintptr_t)&s_initrd_dir
+ };
+ //Add initrd dir to init object
+ ((init_data_t*)_initDirectory.entries[1].data)->initrd_dir = initrd_diri;
+}
+
+int
+initrd_file_size(tar_header_t *file)
+{
+ return s_tar_oct_dec(file->size);
+}
+
+tar_header_t*
+initrd_find_file(const char *filename)
+{
+ for(size_t i = 0; i < s_initrd_dir.self.data - 1; i++) {
+ objdir_entry_t *entry = &s_initrd_dir.entries[i+1];
+ tar_header_t *file = (tar_header_t*)entry->data;
+ if(strcmp(file->name + 1, filename) == 0) return file;
+ }
+ return NULL;
+}
+
+#endif
diff --git a/device/portio_uart.c b/device/portio_uart.c
new file mode 100644
index 0000000..00800b5
--- /dev/null
+++ b/device/portio_uart.c
@@ -0,0 +1,68 @@
+#ifdef ENABLE_PORTIO_UART
+#include "jove.h"
+#include "device/portio_uart.h"
+#include "device/portio.h"
+
+static void
+s_set_int(ioport_t dev, uint8_t enable)
+{
+ port_outb(PORTIO_UART_COM_IER(dev), enable);
+}
+
+static void
+s_set_baud(ioport_t dev, uint16_t baud)
+{
+ /* Enable DLAB */
+ port_outb(PORTIO_UART_COM_LCR(dev), PORTIO_UART_COM_LCR(dev) | 0x80);
+ /* Write low & high. */
+ port_outb(PORTIO_UART_COM_DLAB_DLL(dev), (uint8_t)(baud & 0xFF));
+ port_outb(PORTIO_UART_COM_DLAB_DLH(dev), (uint8_t)((baud >> 8) & 0xFF));
+ /* Disable DLAB */
+ port_outb(PORTIO_UART_COM_LCR(dev), PORTIO_UART_COM_LCR(dev) & 0x7F);
+}
+
+static void
+s_set_fcr(ioport_t dev, uint8_t flg)
+{
+ port_outb(PORTIO_UART_COM_FCR(dev), flg);
+}
+
+static void
+s_set_lcr(ioport_t dev, uint8_t flg)
+{
+ port_outb(PORTIO_UART_COM_LCR(dev), flg);
+}
+
+static void
+s_set_mcr(ioport_t dev, uint8_t flg)
+{
+ port_outb(PORTIO_UART_COM_MCR(dev), flg);
+}
+
+void
+portio_uart_setup(void)
+{
+ s_set_int(PORTIO_UART_COM1, 0);
+ s_set_baud(PORTIO_UART_COM1, 3);
+ s_set_lcr(PORTIO_UART_COM1, 1 | 2);
+ s_set_fcr(PORTIO_UART_COM1, 1 | 2 | 4 | 0xC0);
+ s_set_fcr(PORTIO_UART_COM1, 1 | 2 | 8);
+ s_set_mcr(PORTIO_UART_COM1, 1 | 2 | 4 | 8);
+
+ uint64_t serial_i = _initDirectory.self.data++;
+ _initDirectory.entries[serial_i] = (struct jove_ObjectDirectoryEntry) {
+ .type = KO_DEV_UART,
+ .data = PORTIO_UART_COM1
+ };
+ _initData.log_object = serial_i;
+}
+
+void
+portio_uart_write(uint64_t dev, const char *s, int n)
+{
+ for(int i = 0; i < n; i++) {
+ port_outb(dev, s[i]);
+ }
+}
+
+#endif
diff --git a/device/uart.c b/device/uart.c
new file mode 100644
index 0000000..6728ab1
--- /dev/null
+++ b/device/uart.c
@@ -0,0 +1,13 @@
+#include "device/uart.h"
+#include "device/portio_uart.h"
+
+void
+uart_write(objdir_t *dir, uint64_t entryi, const char *s, size_t w)
+{
+ objdir_entry_t *entry = objdir_seek(dir, entryi);
+ if(entry == NULL || entry->type != KO_DEV_UART) return;
+
+#ifdef ENABLE_PORTIO_UART
+ portio_uart_write(entry->data, s, w);
+#endif
+}
diff --git a/include/arch/x86_64/idt.h b/include/arch/x86_64/idt.h
new file mode 100644
index 0000000..90ce48b
--- /dev/null
+++ b/include/arch/x86_64/idt.h
@@ -0,0 +1,18 @@
+#ifndef _JOVE_x86_64_IDT_H
+#define _JOVE_x86_64_IDT_H 1
+
+#include "processor.h"
+
+typedef struct jove_IVTState
+{
+ uint64_t r15, r14, r13, r12, r11, r10, r9, r8;
+ uint64_t rbp, rdi, rsi, rdx, rcx, rbx, rax;
+ uint64_t rip, cs, rflags, rsp, ss;
+} ivt_state_t;
+
+void kpanic_state(ivt_state_t *state, const char *fmt, ...);
+
+void ivt_setup(void);
+void idt_setup(processor_t *processor);
+
+#endif
diff --git a/include/arch/x86_64/object.h b/include/arch/x86_64/object.h
new file mode 100644
index 0000000..8d5af33
--- /dev/null
+++ b/include/arch/x86_64/object.h
@@ -0,0 +1,26 @@
+#ifndef _JOVE_x86_64_OBJECT_H
+#define _JOVE_x86_64_OBJECT_H 1
+
+#include <stdint.h>
+
+typedef struct jove_InitData
+{
+ uint8_t log_object;
+ uint8_t untyped_data_dir;
+ uint8_t processor_dir;
+ uint8_t pm_object; //Page mapping object.
+ uint8_t initrd_dir; //Init ramdisk files directory.
+ uint8_t tcb_object;
+ uint8_t kernel_stack_object;
+ uint8_t message_object;
+ uintptr_t message_object_address;
+} init_data_t;
+
+typedef struct jove_ThreadControlBlock
+{
+ void *stack;
+ uintptr_t sp, ksp;
+ void *pml4;
+} tcb_t;
+
+#endif
diff --git a/include/arch/x86_64/page.h b/include/arch/x86_64/page.h
new file mode 100644
index 0000000..99bc691
--- /dev/null
+++ b/include/arch/x86_64/page.h
@@ -0,0 +1,41 @@
+#ifndef _JOVE_ARCH_x86_64_PAGE_H
+#define _JOVE_ARCH_x86_64_PAGE_H 1
+
+#include <stdint.h>
+#include "include/object.h"
+
+typedef union jove_PageMapLevelEntry
+{
+ struct {
+ uint8_t p : 1; /* Present */
+ uint8_t rw : 1; /* Read/write. 0 for RO.*/
+ uint8_t us : 1; /* User/supervisor. 0 for DPL3 forbid */
+ uint8_t pwt : 1;
+ uint8_t pcd : 1;
+ uint8_t a : 1; /* Accessed */
+ uint8_t d : 1; /* Dirty */
+ uint8_t ps_pat : 1;
+ uint8_t g : 1; /* Global */
+ uint8_t _r0 : 2;
+ uint8_t r : 1;
+ uint64_t paddr : 35;
+ uint8_t _r1;
+ uint8_t pk : 4;
+ uint8_t xd : 1;
+ }__attribute__((packed));
+ uint64_t value;
+} __attribute__((packed)) pmle_t;
+
+typedef uint16_t pmli_t;
+
+#define PML_SHL(l) ((l * 9) + 3)
+#define PML_I_FOR_LAYER(v, l) ((v >> PML_SHL(l)) % 512)
+
+uintptr_t vmem_ident_tophys(void *vptr);
+void *vmem_phys_tovirt(uintptr_t pptr);
+
+void *pmle_get_page(pmle_t entry);
+
+int untyped_retype_page(objdir_entry_t *untyped_entry, void **dest_ptr);
+
+#endif
diff --git a/include/arch/x86_64/processor.h b/include/arch/x86_64/processor.h
new file mode 100644
index 0000000..f8a93ce
--- /dev/null
+++ b/include/arch/x86_64/processor.h
@@ -0,0 +1,66 @@
+#ifndef _JOVE_ARCH_x86_64_PROCESSOR_H
+#define _JOVE_ARCH_x86_64_PROCESSOR_H 1
+
+#include "memory.h"
+#include "tables.h"
+#include "object.h"
+#include <stdint.h>
+
+#define MSR_FS_BASE 0xC0000100
+#define MSR_GS_BASE 0xC0000101
+#define MSR_KGS_BASE 0xC0000102
+
+#define MSR_EFER 0xC0000080
+#define MSR_STAR 0xC0000081
+#define MSR_LSTAR 0xC0000082
+#define MSR_SFMASK 0xC0000084
+
+typedef struct jove_TSS
+{
+ uint32_t resv0;
+ uint64_t rsp[3];
+ uint32_t resv1;
+ uint64_t ist[8];
+ uint32_t resv2[2];
+ uint16_t resv3;
+ uint16_t iopb;
+} tss_t;
+
+enum
+{
+ GDT_ENTRY_KERNEL_NULL = 0,
+ GDT_ENTRY_KERNEL_CODE,
+ GDT_ENTRY_KERNEL_DATA,
+ GDT_ENTRY_USER_NULL,
+ GDT_ENTRY_USER_DATA,
+ GDT_ENTRY_USER_CODE,
+ GDT_ENTRY_TSS_LOW,
+ GDT_ENTRY_TSS_HIGH,
+ GDT_ENTRY_COUNT
+};
+
+typedef struct jove_Processor
+{
+ physptr_t pdir;
+ struct jove_ObjectDirectory *odir;
+
+ segment_descriptor_t gdt[GDT_ENTRY_COUNT];
+ struct {
+ uint16_t length;
+ uint64_t base;
+ } __attribute__((packed)) gdtr;
+ struct {
+ uint16_t length;
+ uint64_t base;
+ } __attribute__((packed)) idtr;
+
+ tss_t tss;
+ tcb_t *tcb;
+} processor_t;
+
+void gdt_setup(processor_t *processor);
+
+void rdmsr(uint32_t msr, uint32_t *lo, uint32_t *hi);
+void wrmsr(uint32_t msr, uint32_t lo, uint32_t hi);
+
+#endif
diff --git a/include/arch/x86_64/tables.h b/include/arch/x86_64/tables.h
new file mode 100644
index 0000000..42651a1
--- /dev/null
+++ b/include/arch/x86_64/tables.h
@@ -0,0 +1,51 @@
+#ifndef _JOVE_ARCH_x86_64_TABLES_H
+#define _JOVE_ARCH_x86_64_TABLES_H 1
+
+#include <stdint.h>
+
+#define CD_SEGMENT_TYPE_ACCESSED 1
+#define CD_SEGMENT_TYPE_WRITEABLE 2
+#define CD_SEGMENT_TYPE_DATA_EXPAND_DOWN 4
+#define CD_SEGMENT_TYPE_CODE_CONFORMING 4
+#define CD_SEGMENT_TYPE_CODE 8
+
+#define S_SEGMENT_TYPE_LDT 2
+#define S_SEGMENT_TYPE_TSS_AVAIL 9
+#define S_SEGMENT_TYPE_TSS_BUSY 11
+#define S_SEGMENT_TYPE_CALLGATE 12
+#define S_SEGMENT_TYPE_INT_GATE 14
+#define S_SEGMENT_TYPE_TRAP_GATE 15
+
+typedef struct jove_SegmentDescriptor
+{
+ uint16_t limit_0_15; /* Segment limit. */
+ uint16_t base_0_15; /* Segment base. */
+ uint8_t base_16_23;
+ uint8_t type : 4; /* Segment type. */
+ uint8_t s : 1; /* Descriptor type (0 = system, 1 = code/data)*/
+ uint8_t dpl : 2; /* Descriptor privilege level. */
+ uint8_t p : 1; /* Present. */
+ uint8_t limit_16_19 : 4;
+ uint8_t avl : 1; /* Available for use by system software. */
+ uint8_t l : 1; /* 64-bit segment (Ext). */
+ uint8_t d_b : 1; /* Default operation size (0 = 16-bit, 1 = 32-bit)*/
+ uint8_t g : 1; /* Granularity. */
+ uint8_t base_24_31;
+}__attribute__((packed)) segment_descriptor_t;
+
+typedef struct jove_InterruptGate
+{
+ uint16_t base_0_15;
+ uint16_t segment_selector;
+ uint8_t ist : 3;
+ uint8_t zero_0 : 5;
+ uint8_t type : 4;
+ uint8_t zero_1 : 1;
+ uint8_t dpl : 2;
+ uint8_t p : 1;
+ uint16_t base_16_31;
+ uint32_t base_32_63;
+ uint32_t resv;
+}__attribute__((packed)) interrupt_gate_t;
+
+#endif
diff --git a/include/boot.h b/include/boot.h
new file mode 100644
index 0000000..989b4b9
--- /dev/null
+++ b/include/boot.h
@@ -0,0 +1,7 @@
+#ifndef _JOVE_BOOT_H
+#define _JOVE_BOOT_H 1
+
+extern char *jove_bootargs;
+extern int jove_bootargs_len;
+
+#endif
diff --git a/include/bootargs.h b/include/bootargs.h
new file mode 100644
index 0000000..94910f4
--- /dev/null
+++ b/include/bootargs.h
@@ -0,0 +1,13 @@
+#ifndef _JOVE_BOOTARGS_H
+#define _JOVE_BOOTARGS_H 1
+
+/*Layout:
+ * [KEY]=[VALUE]
+ * All values are seperated by spaces.*/
+
+/**@FUNC Get the value associated with the given key.
+ * @PARAM key string to search for.
+ * @RETURN value associated with key. */
+char *bootargs_getarg(const char *key);
+
+#endif
diff --git a/include/device/initrd.h b/include/device/initrd.h
new file mode 100644
index 0000000..eaf3157
--- /dev/null
+++ b/include/device/initrd.h
@@ -0,0 +1,29 @@
+#ifndef _JOVE_DEV_INITRD_H
+#define _JOVE_DEV_INITRD_H 1
+
+#include <stddef.h>
+
+typedef struct tarHeader
+{
+ char name[100];
+ char mode[8];
+ char owner[8];
+ char group[8];
+ char size[12];
+ char modified[12];
+ char checksum[8];
+ char link;
+ char linkname[100];
+} tar_header_t;
+
+typedef union tarBlock {
+ tar_header_t header;
+ char data[512];
+} tar_block_t;
+
+void initrd_setup(void);
+
+tar_header_t *initrd_find_file(const char *filename);
+int initrd_file_size(tar_header_t *header);
+
+#endif
diff --git a/include/device/portio.h b/include/device/portio.h
new file mode 100644
index 0000000..76b28c4
--- /dev/null
+++ b/include/device/portio.h
@@ -0,0 +1,26 @@
+#ifndef _JOVE_PORTIO_H
+#define _JOVE_PORTIO_H 1
+
+#include <stdint.h>
+
+typedef uint16_t ioport_t;
+
+static inline uint8_t port_inb(ioport_t port)
+{ uint8_t r; __asm__ volatile("inb %w1, %b0": "=a"(r): "Nd"(port): "memory"); return r; }
+
+static inline uint16_t port_inw(ioport_t port)
+{ uint16_t r; __asm__ volatile("inw %w1, %w0": "=a"(r): "Nd"(port): "memory"); return r; }
+
+static inline uint32_t port_inl(ioport_t port)
+{ uint32_t r; __asm__ volatile("inl %d1, %l0": "=a"(r): "Nd"(port): "memory"); return r; }
+
+static inline void port_outb(ioport_t port, uint8_t v)
+{ __asm__ volatile("outb %b0, %w1":: "a"(v), "Nd"(port): "memory"); }
+
+static inline void port_outw(ioport_t port, uint16_t v)
+{ __asm__ volatile("outb %w0, %w1":: "a"(v), "Nd"(port): "memory"); }
+
+static inline void port_outl(ioport_t port, uint32_t v)
+{ __asm__ volatile("outb %d0, %w1":: "a"(v), "Nd"(port): "memory"); }
+
+#endif
diff --git a/include/device/portio_uart.h b/include/device/portio_uart.h
new file mode 100644
index 0000000..1b521a1
--- /dev/null
+++ b/include/device/portio_uart.h
@@ -0,0 +1,27 @@
+#ifndef _JOVE_DEVICE_PORTIO_UART
+#define _JOVE_DEVICE_PORTIO_UART 1
+
+#include <stdint.h>
+
+#define PORTIO_UART_COM1 0x3F8
+
+#define PORTIO_UART_COM_THR(COM) COM
+#define PORTIO_UART_COM_RBR(COM) COM
+#define PORTIO_UART_COM_DLAB_DLL(COM) COM
+#define PORTIO_UART_COM_IER(COM) (COM + 1)
+#define PORTIO_UART_COM_DLAB_DLH(COM) (COM + 1)
+#define PORTIO_UART_COM_IIR(COM) (COM + 2)
+#define PORTIO_UART_COM_FCR(COM) (COM + 2)
+#define PORTIO_UART_COM_LCR(COM) (COM + 3)
+#define PORTIO_UART_COM_MCR(COM) (COM + 4)
+#define PORTIO_UART_COM_LSR(COM) (COM + 5)
+#define PORTIO_UART_COM_MSR(COM) (COM + 6)
+#define PORTIO_UART_COM_SR(COM) (COM + 7)
+
+void
+portio_uart_setup(void);
+
+void
+portio_uart_write(uint64_t dev, const char *s, int n);
+
+#endif
diff --git a/include/device/processor.h b/include/device/processor.h
new file mode 100644
index 0000000..33deed4
--- /dev/null
+++ b/include/device/processor.h
@@ -0,0 +1,22 @@
+#ifndef _JOVE_DEVICE_PROCESSOR_H
+#define _JOVE_DEVICE_PROCESSOR_H 1
+
+#if defined(__x86_64__)
+#include "arch/x86_64/processor.h"
+#endif
+
+/**@FUNC Initialize the bootstrap processor.*/
+void bsp_setup(void);
+/**@FUNC Initialize the given processor with kernel-specific values.
+ * Generically:
+ * Instantiates a new kernel object representing the passed processor.
+ * For x86_64:
+ * Loads and uses the generic GDT and IDT.
+ * @PARAM processor processor to initialize.*/
+void processor_setup(void* processor);
+
+/**@FUNC Returns the processor struct this function is called by.
+ * @RET pointer to current processor.*/
+void *processor_current(void);
+
+#endif
diff --git a/include/device/uart.h b/include/device/uart.h
new file mode 100644
index 0000000..3d481a2
--- /dev/null
+++ b/include/device/uart.h
@@ -0,0 +1,11 @@
+#ifndef _JOVE_DEVICE_UART_H
+#define _JOVE_DEVICE_UART_H 1
+
+#include <stdint.h>
+#include <stddef.h>
+
+#include "object.h"
+
+void uart_write(objdir_t *dir, uint64_t entryi, const char *s, size_t w);
+
+#endif
diff --git a/include/error.h b/include/error.h
new file mode 100644
index 0000000..8aa087c
--- /dev/null
+++ b/include/error.h
@@ -0,0 +1,8 @@
+#ifndef _JOVE_ERROR_H
+#define _JOVE_ERROR_H 1
+
+#define E_OK 0
+#define E_BADOBJ 1
+#define E_ARGUMENT 2
+
+#endif
diff --git a/include/init.h b/include/init.h
new file mode 100644
index 0000000..aa722b1
--- /dev/null
+++ b/include/init.h
@@ -0,0 +1,10 @@
+#ifndef _JOVE_INIT_H
+#define _JOVE_INIT_H 1
+
+#ifdef __x86_64__
+
+#endif
+
+void init_load(void);
+
+#endif
diff --git a/include/jove.h b/include/jove.h
new file mode 100644
index 0000000..cbe1a77
--- /dev/null
+++ b/include/jove.h
@@ -0,0 +1,15 @@
+#ifndef _JOVE_H
+#define _JOVE_H 1
+
+#define NORETURN __attribute__((noreturn))
+
+extern void (*_kernel_start)(void);
+extern void (*_kernel_end)(void);
+
+#include "object.h"
+extern objdir_t _initDirectory;
+extern init_data_t _initData;
+
+NORETURN void hcf(void);
+
+#endif
diff --git a/include/memory.h b/include/memory.h
new file mode 100644
index 0000000..e29c41b
--- /dev/null
+++ b/include/memory.h
@@ -0,0 +1,17 @@
+#ifndef _JOVE_MEMORY_H
+#define _JOVE_MEMORY_H 1
+
+#include <stdint.h>
+#include "object.h"
+
+typedef uintptr_t physptr_t;
+typedef uintptr_t virtptr_t;
+
+#define KERNEL_STACK_SIZE 0x1000
+
+void pmem_setup(void);
+void vmem_setup(void);
+
+int untyped_retype_kernel_stack(objdir_entry_t *untyped_entry, objdir_entry_t *dest_entry);
+
+#endif
diff --git a/include/object.h b/include/object.h
new file mode 100644
index 0000000..0a71b5f
--- /dev/null
+++ b/include/object.h
@@ -0,0 +1,61 @@
+#ifndef _JOVE_OBJECT_H
+#define _JOVE_OBJECT_H
+
+#include <stdint.h>
+
+#if defined(__x86_64__)
+#include "arch/x86_64/object.h"
+#endif
+
+enum
+{
+ /* Generic objects */
+ KO_NONE = 0,
+ KO_OBJECT_DIRECTORY,
+ KO_INIT_DATA,
+ KO_MEMORY_UNTYPED,
+ KO_MEMORY_MAPPED_PAGE, //4KiB Fixed Width
+ KO_MEMORY_MAPPING_PAGE, //4Kib Fixed Width
+ KO_INITRD_FILE,
+ KO_TCB,
+ KO_KERNEL_STACK,
+ KO_MESSAGE,
+ /* Device objects*/
+ KO_DEV_INVALID = 0x100,
+ KO_DEV_PROCESSOR,
+ KO_DEV_UART
+};
+
+typedef uintmax_t obj_path_t;
+typedef uint16_t obj_type_t;
+
+typedef struct jove_ObjectDirectoryEntry
+{
+ obj_type_t type;
+ union {
+ struct {
+ char lock : 1;
+ char u0 : 7;
+ char u1;
+};
+ unsigned short flg;
+ };
+ uintmax_t data;
+} objdir_entry_t;
+
+#define OBJECT_DIRECTORY_MAX_ENTRIES 256
+
+//The first entry always represents itself, which allows the kernel to ignore
+//some checks.
+//The data variable also contains the next free index for this directory.
+typedef struct jove_ObjectDirectory
+{
+ union {
+ objdir_entry_t self;
+ objdir_entry_t entries[OBJECT_DIRECTORY_MAX_ENTRIES];
+ };
+} objdir_t;
+
+objdir_entry_t *objdir_seek(objdir_t *dir, uintmax_t index);
+
+#endif
diff --git a/include/panic.h b/include/panic.h
new file mode 100644
index 0000000..076a425
--- /dev/null
+++ b/include/panic.h
@@ -0,0 +1,9 @@
+#ifndef _JOVE_PANIC_H
+#define _JOVE_PANIC_H 1
+
+#include "jove.h"
+
+#define kpanic(...) _kpanic(__FILE__, __LINE__, __VA_ARGS__)
+NORETURN void _kpanic(const char *file, int line, const char *fmt, ...);
+
+#endif
diff --git a/include/print.h b/include/print.h
new file mode 100644
index 0000000..f4c942c
--- /dev/null
+++ b/include/print.h
@@ -0,0 +1,17 @@
+#ifndef _JOVE_LIB_PRINT_H
+#define _JOVE_LIB_PRINT_H 1
+
+#include <stdarg.h>
+
+void _klogf(const char *file, const char *func, int line, const char *fmt, ...);
+#define klogf(...) _klogf(__FILE__, __FUNCTION__, __LINE__, __VA_ARGS__)
+
+int kprintf(const char *fmt, ...);
+int ksprintf(char *s, const char *fmt, ...);
+int ksnprintf(char *s, int size, const char *fmt, ...);
+
+int kvprintf(const char *fmt, va_list ap);
+int kvsprintf(char *s, const char *fmt, va_list ap);
+int kvsnprintf(char *s, int size, const char *fmt, va_list ap);
+
+#endif
diff --git a/include/string.h b/include/string.h
new file mode 100644
index 0000000..a821f1c
--- /dev/null
+++ b/include/string.h
@@ -0,0 +1,25 @@
+#ifndef _JOVE_STRING_H
+#define _JOVE_STRING_H 1
+
+#include <stdbool.h>
+#include <stddef.h>
+
+/**@FUNC Writes the given integer to the string using a custom base.
+ * If the integer is larger than size, s will stop being written to at s[size-1].
+ * Returns the number of characters written, or would have been written.
+ * @PARAM s buffer to write to.
+ * @PARAM size size of buffer to write to.
+ * @PARAM l integer to write.
+ * @PARAM sign whether the integer is signed or unsigned.
+ * @PARAM radix base to write at.
+ * @RETURN number of characters in number.*/
+int ltostr(char *s, int size, unsigned long l, bool sign, int radix);
+
+size_t strlen(const char *s);
+int strcmp(const char *s1, const char *s2);
+
+void *memset(void *dest, char c, size_t n);
+void *memcpy(void *dest, const void *src, size_t n);
+void *memmove(void *dest, const void *src, size_t n);
+
+#endif
diff --git a/include/syscall.h b/include/syscall.h
new file mode 100644
index 0000000..c9c26d8
--- /dev/null
+++ b/include/syscall.h
@@ -0,0 +1,46 @@
+#ifndef _JOVE_SYSCALL_H
+#define _JOVE_SYSCALL_H 1
+
+#include <stdint.h>
+#include <stddef.h>
+#include "object.h"
+
+enum
+{
+ SYSCALL_NONE = 0,
+ SYSCALL_INVOKE,
+ SYSCALL_SEND,
+ SYSCALL_RECV,
+
+ SYSCALL_DEBUG_PUTC,
+ SYSCALL_DEBUG_IDENTIFY
+};
+
+typedef uint16_t invokeid_t;
+
+struct syscallInvokeHeader
+{
+ obj_path_t target_path;
+ invokeid_t func_id;
+};
+
+enum
+{
+ INVOKE_OBJDIR_NMEMB = 0,
+ INVOKE_OBJDIR_GETMEMB
+};
+
+struct syscallInvoke_objdir_nmemb
+{
+ struct syscallInvokeHeader header;
+ size_t value;
+};
+
+struct syscallInvoke_objdir_getmemb
+{
+ struct syscallInvokeHeader header;
+ uint8_t member;
+ obj_type_t value;
+};
+
+#endif
diff --git a/include/tcb.h b/include/tcb.h
new file mode 100644
index 0000000..6545083
--- /dev/null
+++ b/include/tcb.h
@@ -0,0 +1,18 @@
+#ifndef _JOVE_TCB_H
+#define _JOVE_TCB_H 1
+
+#include <stddef.h>
+#include <stdint.h>
+
+typedef struct jove_ThreadControlBlock
+{
+ uintmax_t id;
+ struct jove_ThreadControlBlock *children;
+ struct jove_ThreadControlBlock *next;
+
+ void *stack;
+ uintptr_t sp, ksp;
+ void *pml4;
+} tcb_t;
+
+#endif
diff --git a/lib/itoa.c b/lib/itoa.c
new file mode 100644
index 0000000..57f0c22
--- /dev/null
+++ b/lib/itoa.c
@@ -0,0 +1,32 @@
+#include "string.h"
+
+int
+ltostr(char *s, int size, unsigned long l, bool sign, int radix)
+{
+ unsigned wsize = 0;
+ unsigned digits = 0;
+ if((long)l < 0 && sign) {
+ l = -((long)l);
+ if(size > wsize && s != 0) s[wsize] = '-';
+ wsize++;
+ }
+
+ for(unsigned long lv = l; lv != 0; lv /= radix) {
+ digits++;
+ }
+
+ if(digits-- == 0) {
+ if(size > wsize && s != 0) s[wsize] = '0';
+ wsize++;
+ }
+
+ for(unsigned long lv = l; lv != 0; lv /= radix) {
+ int digit = lv % radix;
+ if(size > digits - wsize && s != 0) {
+ s[digits - wsize] = (digit >= 10 ? (digit + 'a' - 10) : digit + '0');
+ }
+ wsize++;
+ }
+ if(size > wsize && s != 0) s[wsize] = 0;
+ return ++wsize;
+}
diff --git a/lib/log.c b/lib/log.c
new file mode 100644
index 0000000..6bc770d
--- /dev/null
+++ b/lib/log.c
@@ -0,0 +1,14 @@
+#include "print.h"
+#include <stdarg.h>
+
+void
+_klogf(const char *file, const char *func, int line, const char *fmt, ...)
+{
+ kprintf("%s:%i ", func, line);
+
+ va_list ap;
+ va_start(ap, fmt);
+ kvprintf(fmt, ap);
+ va_end(ap);
+
+}
diff --git a/lib/mem.c b/lib/mem.c
new file mode 100644
index 0000000..2ce67f3
--- /dev/null
+++ b/lib/mem.c
@@ -0,0 +1,27 @@
+#include "string.h"
+
+void*
+memset(void *dest, char c, size_t n)
+{
+ char *a = dest;
+ for(size_t i = 0; i < n; i++) a[i] = c;
+ return dest;
+}
+
+void*
+memcpy(void *dest, const void *src, size_t n)
+{
+ char *a = dest;
+ const char *b = src;
+ for(size_t i = 0; i < n; i++) a[i] = b[i];
+ return dest;
+}
+
+void*
+memmove(void *dest, const void *src, size_t n)
+{
+ char buffer[n];
+ memcpy(buffer, src, n);
+ memcpy(dest, buffer, n);
+ return dest;
+}
diff --git a/lib/objdir.c b/lib/objdir.c
new file mode 100644
index 0000000..665da4c
--- /dev/null
+++ b/lib/objdir.c
@@ -0,0 +1,15 @@
+#include "object.h"
+
+objdir_entry_t*
+objdir_seek(objdir_t *dir, uintmax_t index)
+{
+ uint8_t tli = index & 0xFF;
+ if(tli == 0) return &dir->self;
+
+ objdir_entry_t *entry = &dir->entries[tli];
+ if(entry->type == KO_OBJECT_DIRECTORY) {
+ if(tli == index) return entry;
+ return objdir_seek((objdir_t*)entry->data, index >> 8);
+ }
+ return entry;
+}
diff --git a/lib/panic.c b/lib/panic.c
new file mode 100644
index 0000000..bfe80e8
--- /dev/null
+++ b/lib/panic.c
@@ -0,0 +1,15 @@
+#include "panic.h"
+#include "print.h"
+
+NORETURN void
+_kpanic(const char *file, int line, const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+
+ kprintf("KERNEL PANIC:%s:%i: \n", file, line);
+ kvprintf(fmt, ap);
+
+ va_end(ap);
+ hcf();
+}
diff --git a/lib/print.c b/lib/print.c
new file mode 100644
index 0000000..45c7671
--- /dev/null
+++ b/lib/print.c
@@ -0,0 +1,146 @@
+#include "print.h"
+#include "device/uart.h"
+#include "jove.h"
+#include "string.h"
+#include <stddef.h>
+#include <stdbool.h>
+
+int
+kprintf(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ int r = kvprintf(fmt, ap);
+ va_end(ap);
+ return r;
+}
+
+int
+ksprintf(char *s, const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ int r = kvsprintf(s, fmt, ap);
+ va_end(ap);
+ return r;
+}
+
+int
+ksnprintf(char *s, int size, const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ int r = kvsnprintf(s, size, fmt, ap);
+ va_end(ap);
+ return r;
+}
+
+int kvprintf(const char *fmt, va_list ap)
+{
+ va_list ap_dup;
+ va_copy(ap_dup, ap);
+ int size = kvsnprintf(0, 0, fmt, ap);
+ char buffer[size];
+ kvsnprintf(buffer, size, fmt, ap_dup);
+
+#ifdef ENABLE_UART
+ uart_write(&_initDirectory, _initData.log_object, buffer, size - 1);
+#endif
+ return size;
+}
+
+int
+kvsprintf(char *s, const char *fmt, va_list ap)
+{
+ return kvsnprintf(s, 65536, fmt, ap);
+}
+
+int
+kvsnprintf(char *s, int size, const char *fmt, va_list ap)
+{
+ const char *fmtc = fmt;
+ int wsize = 0;
+ do{
+ if(*fmtc != '%') {
+ if(s != NULL && wsize < size) s[wsize] = *fmtc;
+ wsize++;
+ continue;
+ }
+
+ bool sign = true;
+ bool caps = true;
+ int radix = 10;
+ int lpad = 0;
+ int paramw = sizeof(int);
+
+ if(*(++fmtc) == 0) goto done;
+ switch(*fmtc) {
+ case 'c':
+ if(s != NULL && wsize < size) s[wsize] = va_arg(ap, int);
+ wsize++;
+ break;
+ case 'p': //fallthrough;
+ paramw = sizeof(uintptr_t);
+ lpad = paramw * 2;
+ case 'X': //fallthrough
+ case 'x': //fallthrough
+ sign = false;
+ radix = 16;
+ goto printint;
+ break;
+ case 'u': //fallthrough
+ sign = false;
+ goto printint;
+ break;
+ case 'i':
+ goto printint;
+ break;
+ case 's':
+ goto printstr;
+ break;
+ default:
+ break;
+ }
+ continue;
+printstr:
+ {
+ char *arg_s = va_arg(ap, char*);
+ if(arg_s == 0) arg_s = "(NULL)";
+ size_t ssize = strlen(arg_s);
+ for(char *c = arg_s; *c; c++) {
+ if(s != NULL && wsize < size) s[wsize] = *c;
+ wsize++;
+ }
+ }
+ continue;
+printint:
+ {
+ int isize = 1;
+ void *iptr = NULL;
+ if(s != NULL) {
+ iptr = &s[wsize];
+ }
+ switch(paramw) {
+ case sizeof(int): isize = ltostr(iptr, size - wsize, va_arg(ap, int), sign, radix); break;
+ case sizeof(uintptr_t): isize = ltostr(iptr, size - wsize, va_arg(ap, long), sign, radix); break;
+ default: break;
+ }
+ if(lpad) {
+ if(isize < lpad) {
+ lpad -= isize - 1;
+ if(s != 0) {
+ memmove(&s[wsize + lpad], &s[wsize], isize);
+ memset(&s[wsize], '0', lpad);
+ }
+ isize += lpad;
+
+ }
+ }
+ wsize += isize;
+ continue;
+ }
+ }while(*(fmtc++) != 0);
+done:
+ if(s != NULL && wsize < size) s[wsize++] = 0;
+ return wsize;
+}
diff --git a/lib/strcmp.c b/lib/strcmp.c
new file mode 100644
index 0000000..da6a51a
--- /dev/null
+++ b/lib/strcmp.c
@@ -0,0 +1,8 @@
+#include "string.h"
+
+int
+strcmp(const char *s1, const char *s2)
+{
+ while(*s1 && *s2 && *(s1++) == *(s2++));
+ return *s2 - *s1;
+}
diff --git a/lib/strlen.c b/lib/strlen.c
new file mode 100644
index 0000000..da34697
--- /dev/null
+++ b/lib/strlen.c
@@ -0,0 +1,9 @@
+#include "string.h"
+
+size_t
+strlen(const char *s)
+{
+ size_t l = 0;
+ for(; s[l]; l++);
+ return l;
+}
diff --git a/link/x86_64-jove.ld b/link/x86_64-jove.ld
new file mode 100644
index 0000000..0686617
--- /dev/null
+++ b/link/x86_64-jove.ld
@@ -0,0 +1,52 @@
+OUTPUT_FORMAT(elf64-x86-64)
+OUTPUT_ARCH(i386:x86-64)
+
+ENTRY(_start)
+
+PAGESIZE = CONSTANT(MAXPAGESIZE);
+
+PHDRS
+{
+ text PT_LOAD FLAGS((1 << 0) | (1 << 2)) ; /* RO EX*/
+ rodata PT_LOAD FLAGS((1 << 2)) ; /* RO*/
+ data PT_LOAD FLAGS((1 << 1) | (1 << 2)); /* RW*/
+ bss PT_LOAD FLAGS((1 << 1) | (1 << 2)); /* RW*/
+}
+
+SECTIONS
+{
+ . = 0xFFFFFFFF80000000;
+ _kernel_start = .;
+
+ .text BLOCK(PAGESIZE) : ALIGN(PAGESIZE) {
+ *(.init)
+ *(.text .text.*)
+
+ . = ALIGN(8);
+ _kernel_ctors_start = .;
+ *(.ctors)
+ _kernel_ctors_end = .;
+
+ *(.fini)
+ } :text
+
+ .rodata BLOCK(PAGESIZE) : ALIGN(PAGESIZE) {
+ *(.rodata .rodata.*)
+ } :rodata
+
+ .data BLOCK(PAGESIZE) : ALIGN(PAGESIZE) {
+ *(.data .data.*)
+ } :data
+
+ .bss BLOCK(PAGESIZE) : ALIGN(PAGESIZE) {
+ *(COMMON)
+ *(.bss .bss.*)
+ } :bss
+
+ /DISCARD/ : {
+ *(.note .note.*)
+ }
+
+ _kernel_end = .;
+}
+
diff --git a/main.c b/main.c
new file mode 100644
index 0000000..4416bba
--- /dev/null
+++ b/main.c
@@ -0,0 +1,44 @@
+#include "jove.h"
+#include "object.h"
+#include "memory.h"
+#include "print.h"
+#include "init.h"
+#include "device/portio_uart.h"
+#include "device/processor.h"
+#include "device/initrd.h"
+
+struct jove_ObjectDirectory _initDirectory = {
+
+ .entries = {
+ [0] = {
+ .type = KO_OBJECT_DIRECTORY,
+ .data = 1
+ },
+ }
+};
+init_data_t _initData;
+
+void
+_jove_main(void)
+{
+ bsp_setup();
+#ifdef ENABLE_PORTIO_UART
+ portio_uart_setup();
+#endif
+
+ pmem_setup();
+ vmem_setup();
+#ifdef ENABLE_INITRD
+ initrd_setup();
+#endif
+
+ init_load();
+ kprintf("Reached end of kernel main!\n");
+ hcf();
+}
+
+NORETURN void
+hcf(void)
+{
+ for(;;) __asm__ volatile("hlt");
+}
diff --git a/syscall/handler.c b/syscall/handler.c
new file mode 100644
index 0000000..efab179
--- /dev/null
+++ b/syscall/handler.c
@@ -0,0 +1,66 @@
+#include "syscall.h"
+#include "object.h"
+#include "handles.h"
+#include "device/processor.h"
+#include "print.h"
+#include <stdint.h>
+#include <stddef.h>
+
+static int
+s_syscall_handle_invoke(objdir_t *root_dir, void *payload)
+{
+ struct syscallInvokeHeader *invokeHeader = payload;
+ uintmax_t target_path = invokeHeader->target_path;
+ objdir_entry_t *target_entry = objdir_seek(root_dir, target_path);
+
+ klogf("target entry %x:%p func %i\n", target_path, target_entry->data, invokeHeader->func_id);
+
+ if(target_entry == NULL) return -1;
+ switch(target_entry->type) {
+ case KO_NONE:
+ return -1;
+ case KO_OBJECT_DIRECTORY:
+ return syscall_handle_invoke_objdir(root_dir, (objdir_t*)target_entry->data, payload);
+ default:
+ klogf("Missing implementation of invoke for type %i\n", target_entry->type);
+ return -1;
+ }
+}
+
+int
+_syscall_handler(uintmax_t argsi, int calli)
+{
+ objdir_t *objdir = ((processor_t*)processor_current())->odir;
+ objdir_entry_t *payload_entry = objdir_seek(objdir, argsi);
+ if(payload_entry == NULL) {
+ klogf("Missing object for syscall %i (%x)\n", calli, argsi);
+ return -1;
+ }
+ if(payload_entry->type != KO_MESSAGE) {
+ klogf("Tried to invoke syscall %i with invalid object %x\n", calli, argsi);
+ return -1;
+ }
+
+ uintmax_t *payload = (void*)payload_entry->data;
+
+ switch(calli) {
+ case SYSCALL_INVOKE: return s_syscall_handle_invoke(objdir, payload);
+ case SYSCALL_DEBUG_PUTC:
+ kprintf("%c", (char)payload[0]);
+ return 0;
+ case SYSCALL_DEBUG_IDENTIFY:
+ {
+ objdir_entry_t *target_entry = objdir_seek(objdir, payload[0]);
+ if(payload_entry == NULL) {
+ payload[0] = KO_NONE;
+ } else {
+ payload[0] = target_entry->type;
+ }
+ return 0;
+ }
+ default:
+ klogf("Invalid syscall %i caught! Failing.\n", calli);
+ return -1;
+ }
+ return -1;
+}
diff --git a/syscall/handles.h b/syscall/handles.h
new file mode 100644
index 0000000..f9fd79c
--- /dev/null
+++ b/syscall/handles.h
@@ -0,0 +1,8 @@
+#ifndef _JOVE_SYSCALL_HANDLES_H
+#define _JOVE_SYSCALL_HANDLES_H 1
+
+#include "object.h"
+
+int syscall_handle_invoke_objdir(objdir_t *root_dir, objdir_t *target_dir, void *payload);
+
+#endif
diff --git a/syscall/invoke_objdir.c b/syscall/invoke_objdir.c
new file mode 100644
index 0000000..4698f34
--- /dev/null
+++ b/syscall/invoke_objdir.c
@@ -0,0 +1,34 @@
+#include "handles.h"
+#include "syscall.h"
+
+static int
+s_handle_invoke_objdir_nmemb(objdir_t *root_dir, objdir_t *target_dir, void *payload)
+{
+ struct syscallInvoke_objdir_nmemb *payloadStruct = payload;
+ payloadStruct->value = target_dir->self.data;
+ return 0;
+}
+
+static int
+s_handle_invoke_objdir_getmemb(objdir_t *root_dir, objdir_t *target_dir, void *payload)
+{
+ struct syscallInvoke_objdir_getmemb *payloadStruct = payload;
+ payloadStruct->value = target_dir->entries[payloadStruct->member].type;
+ return 0;
+}
+
+static int (*s_invoke_handles[])(objdir_t*, objdir_t*, void*) = {
+ [INVOKE_OBJDIR_NMEMB] = s_handle_invoke_objdir_nmemb,
+ [INVOKE_OBJDIR_GETMEMB] = s_handle_invoke_objdir_getmemb
+};
+static size_t s_invoke_handles_count = sizeof(s_invoke_handles) / sizeof(void*);
+
+int
+syscall_handle_invoke_objdir(objdir_t *root_dir, objdir_t *target_dir, void *payload)
+{
+ struct syscallInvokeHeader *invokeHeader = payload;
+ size_t funcid = invokeHeader->func_id;
+
+ if(funcid >= s_invoke_handles_count) return -1;
+ return s_invoke_handles[funcid](root_dir, target_dir, payload);
+}