summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--Makefile39
-rw-r--r--abi/permission.h6
-rw-r--r--abi/syscall.h29
-rw-r--r--arch/arch.h8
-rw-r--r--arch/x86_64/cpu.h13
-rw-r--r--arch/x86_64/elf.c47
-rw-r--r--arch/x86_64/elf.d2
-rw-r--r--arch/x86_64/elf.h78
-rw-r--r--arch/x86_64/gdt.c125
-rw-r--r--arch/x86_64/gdt.d2
-rw-r--r--arch/x86_64/idt.c70
-rw-r--r--arch/x86_64/idt.d2
-rw-r--r--arch/x86_64/int.S131
-rw-r--r--arch/x86_64/loadgdt.S24
-rw-r--r--arch/x86_64/paging.c202
-rw-r--r--arch/x86_64/paging.d2
-rw-r--r--arch/x86_64/paging.h44
-rw-r--r--arch/x86_64/savestate.S39
-rw-r--r--arch/x86_64/serial.c62
-rw-r--r--arch/x86_64/serial.d2
-rw-r--r--arch/x86_64/serial.h27
-rw-r--r--arch/x86_64/syscall.c1
-rw-r--r--arch/x86_64/syscall.d1
-rw-r--r--arch/x86_64/syscall_setup.S37
-rw-r--r--arch/x86_64/tables.c8
-rw-r--r--arch/x86_64/tables.d1
-rw-r--r--arch/x86_64/tables.h60
-rw-r--r--arch/x86_64/thread.c85
-rw-r--r--arch/x86_64/thread.d3
-rw-r--r--arch/x86_64/tss.h18
-rw-r--r--arch/x86_64/uart.h19
-rw-r--r--arch/x86_64/umode_enter.S8
-rw-r--r--boot/boot.h40
-rw-r--r--boot/cmdline.c50
-rw-r--r--boot/cmdline.d3
-rw-r--r--boot/cmdline.h10
-rw-r--r--boot/limine/limine.c89
-rw-r--r--boot/limine/limine.d2
-rw-r--r--boot/limine/limine.h579
-rw-r--r--compile_flags.txt3
-rw-r--r--config.mk24
-rw-r--r--io/interrupt.h6
-rw-r--r--io/log.c68
-rw-r--r--io/log.d1
-rw-r--r--io/log.h22
-rw-r--r--ird/initrd.c88
-rw-r--r--ird/initrd.d3
-rw-r--r--ird/initrd.h21
-rw-r--r--ird/tar.h18
-rw-r--r--lib/format.h13
-rw-r--r--lib/hashtable.c109
-rw-r--r--lib/hashtable.d2
-rw-r--r--lib/hashtable.h34
-rw-r--r--lib/jove.h15
-rw-r--r--lib/kpanic.c15
-rw-r--r--lib/kpanic.d1
-rw-r--r--lib/linkedlist.c34
-rw-r--r--lib/linkedlist.d1
-rw-r--r--lib/linkedlist.h26
-rw-r--r--lib/ltostr.c26
-rw-r--r--lib/ltostr.d1
-rw-r--r--lib/mem.c52
-rw-r--r--lib/mem.d1
-rw-r--r--lib/sfmt.c112
-rw-r--r--lib/sfmt.d1
-rw-r--r--lib/string.c16
-rw-r--r--lib/string.d1
-rw-r--r--lib/string.h18
-rw-r--r--lib/toupper.c26
-rw-r--r--lib/toupper.d1
-rw-r--r--link/x86_64-jove.ld52
-rw-r--r--main.c29
-rw-r--r--main.d4
-rw-r--r--mem/buddymap.c151
-rw-r--r--mem/buddymap.d2
-rw-r--r--mem/buddymap.h21
-rw-r--r--mem/memory.h49
-rw-r--r--mem/phys.c14
-rw-r--r--mem/phys.d1
-rw-r--r--mem/slab.c201
-rw-r--r--mem/slab.d2
-rw-r--r--mem/slab.h33
-rw-r--r--tsk/tasking.h25
-rw-r--r--tsk/thread.c7
-rw-r--r--tsk/thread.d1
-rw-r--r--usr/elf.h12
-rw-r--r--usr/syscall.c23
-rw-r--r--usr/syscall.d2
-rw-r--r--usr/syscall.h10
-rw-r--r--usr/umode.c31
-rw-r--r--usr/umode.d3
-rw-r--r--usr/umode.h8
93 files changed, 3412 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..6b6772d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+*.o
+*.a
+*.elf
+.ccls*
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..6697fc6
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,39 @@
+include config.mk
+
+CFILES := $(wildcard *.c)
+CFILES += $(wildcard */*.c)
+CFILES += $(wildcard boot/$(TARGET_BOOTLOADER)/*.c)
+CFILES += $(wildcard arch/$(TARGET_MACHINE)/*.c)
+CFILES += $(wildcard arch/$(TARGET_MACHINE)/*/*.c)
+
+SFILES := $(wildcard *.S)
+SFILES += $(wildcard */*.S)
+SFILES += $(wildcard arch/$(TARGET_MACHINE)/*.S)
+SFILES += $(wildcard arch/$(TARGET_MACHINE)/*/*.S)
+
+OBJFILES := $(patsubst %.c,%.o,$(CFILES))
+OBJFILES += $(patsubst %.S,%.o,$(SFILES))
+
+DFILES := $(wildcard *.d)
+DFILES += $(wildcard */*.d)
+DFILES += $(wildcard arch/$(TARGET_MACHINE)/*.d)
+DFILES += $(wildcard arch/$(TARGET_MACHINE)/*/*.d)
+
+BIN := jove.elf
+
+all: $(BIN)
+
+.PHONY: clean
+clean:
+ rm $(OBJFILES)
+ rm $(DFILES)
+ rm $(BIN)
+
+$(BIN): ${OBJFILES}
+ $(LD) $(LDFLAGS) ${OBJFILES} -o $(BIN)
+
+%.o:%.c
+ $(CC) $(CFLAGS) -c $< -o $@
+
+%.o:%.S
+ $(AS) -g -c $< -o $@
diff --git a/abi/permission.h b/abi/permission.h
new file mode 100644
index 0000000..5ca56e0
--- /dev/null
+++ b/abi/permission.h
@@ -0,0 +1,6 @@
+#ifndef JOVE_ABI_PERMISSION_H
+#define JOVE_ABI_PERMISSION_H 1
+
+#define PERM_MEMSRV 0
+
+#endif
diff --git a/abi/syscall.h b/abi/syscall.h
new file mode 100644
index 0000000..95cd4a5
--- /dev/null
+++ b/abi/syscall.h
@@ -0,0 +1,29 @@
+#ifndef JOVE_ABI_SYSCALL_H
+#define JOVE_ABI_SYSCALL_H 1
+
+typedef struct syscall {
+ int id;
+} syscall_t;
+
+struct syscall_log {
+ syscall_t syscall;
+ const char *message;
+};
+
+enum
+{
+ SYSCALL_LOG = 0,
+ SYSCALL_COUNT
+};
+
+#define _SYSCALL(data) __asm__ volatile("movq %0, %%rdi\nsyscall":: "r"(data))
+
+void _syscall_log(const char *message) {
+ struct syscall_log syscall_data = {
+ .syscall = (syscall_t){ .id = SYSCALL_LOG },
+ .message = message
+ };
+ _SYSCALL(&syscall_data);
+}
+
+#endif
diff --git a/arch/arch.h b/arch/arch.h
new file mode 100644
index 0000000..6e569fd
--- /dev/null
+++ b/arch/arch.h
@@ -0,0 +1,8 @@
+#ifndef JOVE_ARCH_H
+#define JOVE_ARCH_H 1
+
+extern void serial_setup(void);
+
+extern void arch_tables_setup(void);
+
+#endif
diff --git a/arch/x86_64/cpu.h b/arch/x86_64/cpu.h
new file mode 100644
index 0000000..942c8d9
--- /dev/null
+++ b/arch/x86_64/cpu.h
@@ -0,0 +1,13 @@
+#ifndef ARCH_x86_64_CPU_H
+#define ARCH_x86_64_CPU_H 1
+
+#include <stdint.h>
+
+struct Registers
+{
+ uint64_t r15, r14, r13, r12, r11, r10, r9, r8;
+ uint64_t bp, di, si, dx, cx, bx, ax;
+ uint64_t ip, cs, flags, sp, ss;
+};
+
+#endif
diff --git a/arch/x86_64/elf.c b/arch/x86_64/elf.c
new file mode 100644
index 0000000..60b8f13
--- /dev/null
+++ b/arch/x86_64/elf.c
@@ -0,0 +1,47 @@
+#include "elf.h"
+#include "lib/string.h"
+#include "usr/elf.h"
+#include "io/log.h"
+#include "mem/memory.h"
+
+void*
+elf_load(const void *data, size_t len)
+{
+ if(data == NULL) return NULL;
+ if(len < 4) return NULL;
+
+ struct ELF_header *ehdr = (struct ELF_header*)data;
+ if(ehdr->e_ident[EI_MAG0] != E_IDENT_MAG0 &&
+ ehdr->e_ident[EI_MAG1] != E_IDENT_MAG1 &&
+ ehdr->e_ident[EI_MAG2] != E_IDENT_MAG2 &&
+ ehdr->e_ident[EI_MAG3] != E_IDENT_MAG3)
+ return NULL;
+
+ if(ehdr->e_ident[EI_CLASS] != E_IDENT_CLASS64) {
+ klogf("Jove does not support ELF files of class CLASS32\n");
+ return NULL;
+ }
+
+ if(ehdr->e_type != ET_EXEC) {
+ klogf("Jove does not support ELF files other than ET_EXEC\n");
+ return NULL;
+ }
+
+ uint64_t entry = ehdr->e_entry;
+
+ size_t phdrc = ehdr->e_phnum;
+ struct ELF_phdr *phdrs = (struct ELF_phdr*)((uintptr_t)data + ehdr->e_phoff);
+
+ for(size_t phdri = 0; phdri < phdrc; phdri++)
+ {
+ struct ELF_phdr *phdr = &phdrs[phdri];
+ void *pdata = (void*)phdr->p_vaddr;
+
+ mem_ensure_range(phdr->p_vaddr, phdr->p_vaddr + phdr->p_memsz, true, true);
+ if(phdr->p_type == PT_LOAD)
+ {
+ memcpy(pdata, (void*)((uintptr_t)data + phdr->p_offset), phdr->p_filesz);
+ }
+ }
+ return (void*)entry;
+}
diff --git a/arch/x86_64/elf.d b/arch/x86_64/elf.d
new file mode 100644
index 0000000..f57bbe6
--- /dev/null
+++ b/arch/x86_64/elf.d
@@ -0,0 +1,2 @@
+arch/x86_64/elf.o: arch/x86_64/elf.c arch/x86_64/elf.h lib/string.h \
+ usr/elf.h io/log.h mem/memory.h mem/slab.h
diff --git a/arch/x86_64/elf.h b/arch/x86_64/elf.h
new file mode 100644
index 0000000..ebcd228
--- /dev/null
+++ b/arch/x86_64/elf.h
@@ -0,0 +1,78 @@
+#ifndef JOVE_ARCH_x86_64_ELF_H
+#define JOVE_ARCH_x86_64_ELF_H 1
+
+#include <stdint.h>
+
+#define EI_MAG0 0
+#define EI_MAG1 1
+#define EI_MAG2 2
+#define EI_MAG3 3
+#define EI_CLASS 4
+#define EI_DATA 5
+#define EI_VERSION 6
+#define EI_OSABI 7
+#define EI_ABIVERSION 8
+#define EI_PAD 9
+
+#define E_IDENT_MAG0 0x7F
+#define E_IDENT_MAG1 'E'
+#define E_IDENT_MAG2 'L'
+#define E_IDENT_MAG3 'F'
+
+#define E_IDENT_CLASS32 1
+#define E_IDENT_CLASS64 2
+
+#define ET_EXEC 0x02
+
+struct ELF_header
+{
+ uint8_t e_ident[15];
+ uint16_t e_type;
+ uint16_t e_machine;
+ uint32_t e_version;
+ uint64_t e_entry;
+ uint64_t e_phoff;
+ uint64_t e_shoff;
+ uint32_t e_flags;
+ uint16_t e_ehsize;
+ uint16_t e_phentsize;
+ uint16_t e_phnum;
+ uint16_t e_shentsize;
+ uint16_t e_shnum;
+ uint16_t e_shstrndx;
+};
+
+#define PT_NULL 0
+#define PT_LOAD 1
+
+#define PF_X 1
+#define PF_W 2
+#define PF_R 4
+
+struct ELF_phdr
+{
+ uint32_t p_type;
+ uint32_t p_flags;
+ uint64_t p_offset;
+ uint64_t p_vaddr;
+ uint64_t p_paddr;
+ uint64_t p_filesz;
+ uint64_t p_memsz;
+ uint64_t p_align;
+};
+
+struct ELF_shdr
+{
+ uint32_t sh_name;
+ uint32_t sh_type;
+ uint64_t sh_flags;
+ uint64_t sh_addr;
+ uint64_t sh_offset;
+ uint64_t sh_size;
+ uint32_t sh_link;
+ uint32_t sh_info;
+ uint64_t sh_addralign;
+ uint64_t sh_entsize;
+};
+
+#endif
diff --git a/arch/x86_64/gdt.c b/arch/x86_64/gdt.c
new file mode 100644
index 0000000..d996dcb
--- /dev/null
+++ b/arch/x86_64/gdt.c
@@ -0,0 +1,125 @@
+#include "tables.h"
+#include "tss.h"
+
+enum
+{
+ GDT_SEGMENT_KERNEL_NULL = 0,
+ GDT_SEGMENT_KERNEL_CODE,
+ GDT_SEGMENT_KERNEL_DATA,
+
+ GDT_SEGMENT_USER_NULL,
+ GDT_SEGMENT_USER_DATA,
+ GDT_SEGMENT_USER_CODE,
+
+ GDT_SEGMENT_TSS_LOW,
+ GDT_SEGMENT_TSS_HIGH,
+
+ GDT_SEGMENT_COUNT
+};
+
+__attribute__((aligned(0x1000)))
+static struct SegmentDescriptor s_gdtd[GDT_SEGMENT_COUNT] =
+{
+ { 0 }, /* Kernel NULL */
+ { /* Kernel Code (64-bit RO EX DPL0) */
+ .limit_0_15 = 0xFFFF,
+ .base_0_15 = 0,
+ .base_16_23 = 0,
+ .type = CD_SEGMENT_TYPE_CODE | CD_SEGMENT_TYPE_CODE_CONFORMING,
+ .s = 1,
+ .dpl = 0,
+ .p = 1,
+ .limit_16_19 = 0xF,
+ .l = 1,
+ .d_b = 0,
+ .g = 0,
+ .base_24_31 = 0
+ },
+ { /* Kernel Data (64-bit RW DPL0) */
+ .limit_0_15 = 0xFFFF,
+ .base_0_15 = 0,
+ .base_16_23 = 0,
+ .type = CD_SEGMENT_TYPE_WRITEABLE,
+ .s = 1,
+ .dpl = 0,
+ .p = 1,
+ .limit_16_19 = 0xF,
+ .l = 1,
+ .d_b = 0,
+ .g = 0,
+ .base_24_31 = 0
+ },
+ { 0 }, /* User NULL */
+ { /* User Data (64-bit RO EX DPL3)*/
+ .limit_0_15 = 0xFFFF,
+ .base_0_15 = 0,
+ .base_16_23 = 0,
+ .type = CD_SEGMENT_TYPE_WRITEABLE,
+ .s = 1,
+ .dpl = 3,
+ .p = 1,
+ .limit_16_19 = 0xF,
+ .l = 1,
+ .d_b = 0,
+ .g = 0,
+ .base_24_31 = 0,
+ },
+ { /* User Code (64-bit RO EX DPL3)*/
+ .limit_0_15 = 0xFFFF,
+ .base_0_15 = 0,
+ .base_16_23 = 0,
+ .type = CD_SEGMENT_TYPE_CODE | CD_SEGMENT_TYPE_CODE_CONFORMING,
+ .s = 1,
+ .dpl = 3,
+ .p = 1,
+ .limit_16_19 = 0xF,
+ .l = 1,
+ .d_b = 0,
+ .g = 0,
+ .base_24_31 = 0,
+ },
+ { /* TSS Low */
+ .limit_0_15 = sizeof(struct TSS),
+ .base_0_15 = 0,
+ .base_16_23 = 0,
+ .type = S_SEGMENT_TYPE_TSS_AVAIL,
+ .avl = 1,
+ .s = 0,
+ .dpl = 0,
+ .p = 1,
+ .limit_16_19 = 0,
+ .l = 1,
+ .d_b = 0,
+ .g = 0,
+ .base_24_31 = 0,
+ },
+ { 0 }
+};
+static struct XDTR s_gdtr = {
+ .length = sizeof(s_gdtd) - 1,
+ .address = (uintptr_t)&s_gdtd
+};
+
+static struct TSS s_tss = {
+
+};
+
+extern void x86_64_lgdt(struct XDTR *gdtr);
+extern void x86_64_flush_tss(void);
+void
+x86_64_load_gdt(void)
+{
+ {
+ struct SegmentDescriptor *tss_lo = &s_gdtd[GDT_SEGMENT_TSS_LOW];
+ struct SegmentDescriptor *tss_hi = &s_gdtd[GDT_SEGMENT_TSS_HIGH];
+ uintptr_t tssb = (uintptr_t)&s_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;
+ }
+
+ x86_64_lgdt(&s_gdtr);
+ x86_64_flush_tss();
+}
diff --git a/arch/x86_64/gdt.d b/arch/x86_64/gdt.d
new file mode 100644
index 0000000..7b43bdb
--- /dev/null
+++ b/arch/x86_64/gdt.d
@@ -0,0 +1,2 @@
+arch/x86_64/gdt.o: arch/x86_64/gdt.c arch/x86_64/tables.h \
+ arch/x86_64/tss.h
diff --git a/arch/x86_64/idt.c b/arch/x86_64/idt.c
new file mode 100644
index 0000000..567dead
--- /dev/null
+++ b/arch/x86_64/idt.c
@@ -0,0 +1,70 @@
+#include "tables.h"
+#include "cpu.h"
+#include "lib/jove.h"
+#include "io/log.h"
+
+PAGEALIGN
+static struct InterruptTrapGate s_idtd[48];
+static struct InterruptState *(*s_int_handlers[48])(struct Registers*);
+static struct XDTR s_idtr = {
+ .length = sizeof(s_idtd) - 1,
+ .address = (uintptr_t)&s_idtd
+};
+
+uint64_t __isr_err;
+uint64_t __isr_num;
+
+struct Registers*
+irq_handle(struct Registers *state)
+{
+ if(__isr_num < 48) {
+ if(s_int_handlers[__isr_num] != NULL) {
+ s_int_handlers[__isr_num](state);
+ return state;
+ }
+ }
+ klogf("Interrupt %i\nerror code %#016X\n", __isr_num, __isr_err);
+ klogf("IP: %#016X\n", state->ip);
+ klogf("AX: %#016X\n", state->ax);
+ klogf("BX: %#016X\n", state->bx);
+ klogf("CX: %#016X\n", state->cx);
+ klogf("DX: %#016X\n", state->dx);
+ klogf("SI: %#016X\n", state->si);
+ klogf("DI: %#016X\n", state->di);
+ klogf("BP: %#016X\n", state->bp);
+ klogf("R8: %#016X\n", state->r8);
+ klogf("R9: %#016X\n", state->r9);
+ klogf("R10: %#016X\n", state->r10);
+ klogf("R11: %#016X\n", state->r11);
+ klogf("R12: %#016X\n", state->r12);
+ klogf("R13: %#016X\n", state->r13);
+ klogf("R14: %#016X\n", state->r14);
+ klogf("R15: %#016X\n", state->r15);
+ kpanic("Unhandled exception\n");
+ return state;
+}
+
+extern void x86_64_lidt(struct XDTR *idtr);
+void
+x86_64_load_idt(void)
+{
+ extern uintptr_t __isr_stubs[22];
+ for(int i = 0; i < 22; i++)
+ {
+ uintptr_t base = __isr_stubs[i];
+ klogf("INT %2i : %#016X\n", i, base);
+ s_idtd[i] = (struct InterruptTrapGate){
+ .base_0_15 = (base & 0xFFFF),
+ .segment_selector = 0x10,
+ .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
+ };
+ }
+ x86_64_lidt(&s_idtr);
+}
diff --git a/arch/x86_64/idt.d b/arch/x86_64/idt.d
new file mode 100644
index 0000000..7e976e7
--- /dev/null
+++ b/arch/x86_64/idt.d
@@ -0,0 +1,2 @@
+arch/x86_64/idt.o: arch/x86_64/idt.c arch/x86_64/tables.h \
+ arch/x86_64/cpu.h lib/jove.h io/log.h
diff --git a/arch/x86_64/int.S b/arch/x86_64/int.S
new file mode 100644
index 0000000..96c62d9
--- /dev/null
+++ b/arch/x86_64/int.S
@@ -0,0 +1,131 @@
+.section .text
+.global x86_64_lidt
+.type x86_64_lidt @function
+x86_64_lidt:
+ lidt (%rdi)
+ retq
+
+.macro irq_preserve
+ 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 irq_restore
+ 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
+
+.global irq_stub
+.type irq_stub @function
+irq_stub:
+ irq_preserve
+
+ movq %rsp, %rdi
+ cld
+
+ .extern irq_handle
+ call irq_handle
+ movq %rax, %rsp
+
+ irq_restore
+ addq $128, %rsp
+ iretq
+
+.extern __isr_err
+.extern __isr_num
+
+.macro isr_error num:req
+.type __isr\num @function
+isr\num:
+ subq $128, %rsp
+ pushq %rbx
+ movq 8(%rsp), %rbx
+ movq %rbx, __isr_err
+ popq %rbx
+ addq $8, %rsp
+ movq $\num, __isr_num
+ jmp irq_stub
+.endm
+
+.macro isr_noerr num:req
+.type __isr\num @function
+isr\num:
+ movq $\num, __isr_num
+ jmp irq_stub
+.endm
+
+isr_noerr 0
+isr_noerr 1
+isr_noerr 2
+isr_noerr 3
+isr_noerr 4
+isr_noerr 5
+isr_noerr 6
+isr_noerr 7
+isr_error 8
+isr_noerr 9
+isr_error 10
+isr_error 11
+isr_error 12
+isr_error 13
+isr_error 14
+isr_noerr 15
+isr_noerr 16
+isr_error 17
+isr_noerr 18
+isr_noerr 19
+isr_noerr 20
+isr_error 21
+
+.section .data
+.global __isr_stubs
+.type __isr_stubs @object
+__isr_stubs:
+ .quad isr0
+ .quad isr1
+ .quad isr2
+ .quad isr3
+ .quad isr4
+ .quad isr5
+ .quad isr6
+ .quad isr7
+ .quad isr8
+ .quad isr9
+ .quad isr10
+ .quad isr11
+ .quad isr12
+ .quad isr13
+ .quad isr14
+ .quad isr15
+ .quad isr16
+ .quad isr17
+ .quad isr18
+ .quad isr19
+ .quad isr20
+ .quad isr21
diff --git a/arch/x86_64/loadgdt.S b/arch/x86_64/loadgdt.S
new file mode 100644
index 0000000..e183850
--- /dev/null
+++ b/arch/x86_64/loadgdt.S
@@ -0,0 +1,24 @@
+.global x86_64_lgdt
+.type x86_64_lgdt @function
+x86_64_lgdt:
+ lgdt (%rdi)
+.reload_segments:
+ pushq $0x8
+ leaq .reload_cs, %rax
+ pushq %rax
+ retfq
+.reload_cs:
+ movw $0x10, %ax
+ movw %ax, %ds
+ movw %ax, %es
+ movw %ax, %fs
+ movw %ax, %gs
+ movw %ax, %ss
+ retq
+
+.global x86_64_flush_tss
+.type x86_64_flush_tss @function
+x86_64_flush_tss:
+ movw $0x30, %ax
+ ltr %ax
+ retq
diff --git a/arch/x86_64/paging.c b/arch/x86_64/paging.c
new file mode 100644
index 0000000..76b5735
--- /dev/null
+++ b/arch/x86_64/paging.c
@@ -0,0 +1,202 @@
+#include "paging.h"
+#include <stddef.h>
+#include "lib/jove.h"
+#include "io/log.h"
+#include "lib/string.h"
+#include "boot/boot.h"
+
+extern void *_kernel_end;
+
+PAGEALIGN static uint64_t s_kernel_initial_pml4[512];
+PAGEALIGN static uint64_t s_kernel_initial_pml3[2][512];
+PAGEALIGN static uint64_t s_kernel_initial_pml2[2][512];
+PAGEALIGN static uint64_t s_kernel_initial_pml1[2][512];
+static struct PageDirectory s_kernel_initial_pd;
+
+struct PageDirectory *mem_current_pd;
+
+physptr_t
+mem_linear_tophys_koffset(uintptr_t virt)
+{
+ return (virt - boot_kernel_virtual_base) + boot_kernel_physical_address;
+}
+
+uintptr_t
+mem_phys_tolinear(physptr_t phys)
+{
+ return (uintptr_t)(phys + 0xFFFF800000000000ULL);
+}
+
+static size_t
+s_paging_pmle(size_t l, uintptr_t addr)
+{
+ size_t shift = (12 + (9 * l));
+ return (addr & (0x1FFULL << shift)) >> shift;
+}
+
+static union PageEntry*
+s_paging_fetch_table(union PageEntry *pt, size_t l, uintptr_t virt)
+{
+ size_t pmle = s_paging_pmle(l, virt);
+ union PageEntry entry = pt[pmle];
+ bool entry_new = false;
+ if(!entry.p) {
+ entry_new = true;
+ entry.value = mem_phys_take4k();
+ entry.p = 1;
+ entry.rw = 1;
+ entry.us = 1;
+ pt[pmle] = entry;
+ }
+ union PageEntry *table = (union PageEntry*)(mem_phys_tolinear(entry.paddr << 12));
+ if(entry_new) memset(table, 0, PAGESIZE);
+ return table;
+}
+
+static union PageEntry*
+s_paging_get_table(union PageEntry *pt, size_t l, uintptr_t virt)
+{
+ if(pt == NULL) return NULL;
+ size_t pmle = s_paging_pmle(l, virt);
+ union PageEntry entry = pt[pmle];
+ if(!entry.p) return NULL;
+ return (union PageEntry*)(mem_phys_tolinear(entry.paddr << 12));
+}
+
+physptr_t
+mem_linear_tophys(uintptr_t virt)
+{
+ struct PageDirectory *pd = mem_current_pd;
+ union PageEntry *pml3 = s_paging_get_table(pd->pml4_vaddr, 3, virt);
+ union PageEntry *pml2 = s_paging_get_table(pd->pml4_vaddr, 3, virt);
+ union PageEntry *pml1 = s_paging_get_table(pd->pml4_vaddr, 3, virt);
+ if(pml1 == NULL) return 0;
+
+ size_t pml1i = s_paging_pmle(0, virt);
+
+ if(!pml1[pml1i].p) return 0;
+ return pml1[pml1i].paddr << 12;
+}
+
+bool
+mem_check_ptr(const void *ptr)
+{
+ if(ptr == NULL) return false;
+ return mem_linear_tophys((uintptr_t)ptr) != 0;
+}
+
+void
+mem_paging_map4k(struct PageDirectory *pd, physptr_t phys, uintptr_t virt, uint8_t flg)
+{
+ union PageEntry *pml3 = s_paging_fetch_table(pd->pml4_vaddr, 3, virt);
+ union PageEntry *pml2 = s_paging_fetch_table(pml3, 2, virt);
+ union PageEntry *pml1 = s_paging_fetch_table(pml2, 1, virt);
+ size_t pml1e = s_paging_pmle(0, virt);
+
+ pml1[pml1e] = (union PageEntry) {
+ .p = (flg & 1) > 0,
+ .rw = (flg & 2) > 0,
+ .us = (flg & 4) > 0,
+ .paddr = phys >> 12
+ };
+}
+
+union PageEntry
+mem_paging_fetch4k(struct PageDirectory *pd, uintptr_t virt)
+{
+ union PageEntry *pml3 = s_paging_fetch_table(pd->pml4_vaddr, 3, virt);
+ union PageEntry *pml2 = s_paging_fetch_table(pml3, 2, virt);
+ union PageEntry *pml1 = s_paging_fetch_table(pml2, 1, virt);
+ return pml1[s_paging_pmle(0, virt)];
+}
+
+void
+mem_pd_ensure_4k(struct PageDirectory *pd, uintptr_t virt, uint8_t flg)
+{
+ union PageEntry pml1e = mem_paging_fetch4k(pd, virt);
+ if(!pml1e.p) {
+ uintptr_t phys = mem_phys_take4k();
+ mem_paging_map4k(pd, phys, virt, flg);
+ }
+}
+
+void
+mem_pd_ensure_range(struct PageDirectory *pd, uintptr_t from, uintptr_t to, uint8_t flg)
+{
+ from &= ~0xFFF;
+ for(; from < to; from += PAGESIZE)
+ mem_pd_ensure_4k(pd, from, flg);
+}
+
+void
+mem_ensure_range(uintptr_t from, uintptr_t to, bool rw, bool user)
+{
+ mem_pd_ensure_range(mem_current_pd, from, to, 1 | (rw << 1) | (user << 2));
+}
+
+void mem_pd_new(struct PageDirectory *pd)
+{
+ physptr_t pml4p = mem_phys_take4k();
+ union PageEntry *pml4 = (union PageEntry*)mem_phys_tolinear(pml4p);
+ memset(pml4, 0, PAGESIZE);
+ memcpy(&pml4[256], &pd->pml4_vaddr[256], PAGESIZE / 2);
+
+ *pd = (struct PageDirectory){
+ .pml4_vaddr = pml4,
+ .pml4_paddr = pml4p,
+ .references = 1
+ };
+}
+
+void mem_pd_clone(struct PageDirectory *pd, struct PageDirectory *parent)
+{
+ mem_pd_new(pd);
+ for(size_t i = 0; i < 256; i++) {
+
+ }
+}
+
+void
+mem_paging_setup(void)
+{
+ memset(s_kernel_initial_pml4, 0, PAGESIZE);
+ memset(s_kernel_initial_pml3, 0, 2 * PAGESIZE);
+ memset(s_kernel_initial_pml2, 0, 2 * PAGESIZE);
+ memset(s_kernel_initial_pml1, 0, PAGESIZE);
+ s_kernel_initial_pd = (struct PageDirectory){
+ .pml4_vaddr = (union PageEntry*)&s_kernel_initial_pml4,
+ .pml4_paddr = mem_linear_tophys_koffset((uintptr_t)&s_kernel_initial_pml4),
+ .references = 1
+ };
+ mem_current_pd = &s_kernel_initial_pd;
+
+ /* Map first few GiBs */
+ s_kernel_initial_pml4[256] =
+ mem_linear_tophys_koffset((uintptr_t)&s_kernel_initial_pml3[0])
+ | 3;
+ s_kernel_initial_pml3[0][0] =
+ mem_linear_tophys_koffset((uintptr_t)&s_kernel_initial_pml2[0])
+ | 3;
+ for(int i = 0; i < 512; i++) {
+ s_kernel_initial_pml2[0][i] = (i * (PAGESIZE * 512)) | 0x80 | 3;
+ }
+
+ size_t kernel_pml4e = (boot_kernel_virtual_base >> (39));
+ size_t kernel_pml3e = (boot_kernel_virtual_base >> (30)) % 512;
+ size_t kernel_pml2e = (boot_kernel_virtual_base >> (21)) % 512;
+ size_t kernel_npages = ((((uintptr_t)&_kernel_end) - boot_kernel_virtual_base) >> 12) + 1;
+ klogf("Kernel has %i pages\n", kernel_npages);
+
+ /* Map kernel pages */
+ s_kernel_initial_pml4[511] =
+ mem_linear_tophys_koffset((uintptr_t)&s_kernel_initial_pml3[1]) | 3;
+ s_kernel_initial_pml3[1][kernel_pml3e] =
+ mem_linear_tophys_koffset((uintptr_t)&s_kernel_initial_pml2[1]) | 3;
+ s_kernel_initial_pml2[1][kernel_pml2e] =
+ mem_linear_tophys_koffset((uintptr_t)&s_kernel_initial_pml1[0]) | 3;
+ for(int i = 0; i < kernel_npages; i++) {
+ s_kernel_initial_pml1[0][i] = (i * PAGESIZE) + boot_kernel_physical_address | 3;
+ }
+
+ __asm__ volatile("mov %0, %%cr3":: "r"(s_kernel_initial_pd.pml4_paddr));
+}
diff --git a/arch/x86_64/paging.d b/arch/x86_64/paging.d
new file mode 100644
index 0000000..bb3ea83
--- /dev/null
+++ b/arch/x86_64/paging.d
@@ -0,0 +1,2 @@
+arch/x86_64/paging.o: arch/x86_64/paging.c arch/x86_64/paging.h \
+ mem/memory.h mem/slab.h lib/jove.h io/log.h lib/string.h boot/boot.h
diff --git a/arch/x86_64/paging.h b/arch/x86_64/paging.h
new file mode 100644
index 0000000..1e88a0b
--- /dev/null
+++ b/arch/x86_64/paging.h
@@ -0,0 +1,44 @@
+#ifndef JOVE_ARCH_x86_64_PAGING_H
+#define JOVE_ARCH_x86_64_PAGING_H 1
+
+#include <stdint.h>
+#include "mem/memory.h"
+
+union PageEntry
+{
+ 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));
+
+struct PageDirectory
+{
+ union PageEntry *pml4_vaddr;
+ physptr_t pml4_paddr;
+ size_t references;
+};
+
+extern struct PageDirectory *mem_current_pd;
+
+void mem_pd_new(struct PageDirectory *pd);
+void mem_pd_clone(struct PageDirectory *pd, struct PageDirectory *parent);
+
+void mem_pd_ensure_4k(struct PageDirectory *pd, uintptr_t virt, uint8_t flg);
+void mem_pd_ensure_range(struct PageDirectory *pd, uintptr_t from, uintptr_t to, uint8_t flg);
+
+#endif
diff --git a/arch/x86_64/savestate.S b/arch/x86_64/savestate.S
new file mode 100644
index 0000000..c89933b
--- /dev/null
+++ b/arch/x86_64/savestate.S
@@ -0,0 +1,39 @@
+.global saveregs
+.type saveregs @function
+saveregs:
+ 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
+ retq
+
+.global loadregs
+.type loadregs @function
+loadregs:
+ 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
+
diff --git a/arch/x86_64/serial.c b/arch/x86_64/serial.c
new file mode 100644
index 0000000..1b49e3f
--- /dev/null
+++ b/arch/x86_64/serial.c
@@ -0,0 +1,62 @@
+#include "serial.h"
+#include "uart.h"
+#include "io/log.h"
+
+static struct LogDevice s_serial_logdev =
+ { .out = serial_out, .chain = NULL };
+
+bool serial_supported = true;
+
+void
+serial_setup(void)
+{
+ /* Disable interrupts. */
+ poutb(SERIAL_UART_COM_IER(SERIAL_UART_COM1), 0x0);
+ /* Enable DLAB. */
+ poutb(SERIAL_UART_COM_LCR(SERIAL_UART_COM1), 0x80);
+ /* Set divisor to 3 (38400 baud)*/
+ poutb(SERIAL_UART_COM_DLAB_DLL(SERIAL_UART_COM1), 3);
+ poutb(SERIAL_UART_COM_DLAB_DLH(SERIAL_UART_COM1), 0);
+ /* Set flags for LCR (8 bits, no parity, one stop)*/
+ poutb(SERIAL_UART_COM_LCR(SERIAL_UART_COM1), 1 | 2);
+ /* Enable & clear FIFO, 14-byte threshold */
+ poutb(SERIAL_UART_COM_FCR(SERIAL_UART_COM1), 1 | 2 | 4 | 0xC0);
+ /* Enable interrupts, set RTS/DSR. */
+ poutb(SERIAL_UART_COM_MCR(SERIAL_UART_COM1), 1 | 2 | 8);
+ /* Set loopback mode for testing. */
+ poutb(SERIAL_UART_COM_MCR(SERIAL_UART_COM1), 2 | 4 | 8 | 0x10);
+ /* Test serial output. */
+ poutb(SERIAL_UART_COM1, 0xAE);
+ if(pinb(SERIAL_UART_COM1) != 0xAE) {
+ serial_supported = false;
+ return;
+ }
+
+ /* Serial is not faulty.
+ * No loopback, enable output 1 & 2.*/
+ poutb(SERIAL_UART_COM_MCR(SERIAL_UART_COM1), 1 | 2 | 4 | 8);
+
+ klog_newdev(&s_serial_logdev);
+}
+
+static ALWAYS_INLINE bool
+serial_transmit_empty(uint16_t com)
+{
+ return pinb(SERIAL_UART_COM_LSR(com) & 0x20);
+}
+
+static inline void
+serial_outb(uint16_t com, uint8_t b)
+{
+ if(b == '\n') serial_outb(com, '\r');
+ while(!serial_transmit_empty(SERIAL_UART_COM1));
+ poutb(com, b);
+}
+
+void
+serial_out(const char *s, size_t len)
+{
+ if(!serial_supported) return;
+ for(; len > 0; len--)
+ serial_outb(SERIAL_UART_COM1, *(s++));
+}
diff --git a/arch/x86_64/serial.d b/arch/x86_64/serial.d
new file mode 100644
index 0000000..a0b0756
--- /dev/null
+++ b/arch/x86_64/serial.d
@@ -0,0 +1,2 @@
+arch/x86_64/serial.o: arch/x86_64/serial.c arch/x86_64/serial.h \
+ arch/x86_64/uart.h lib/jove.h io/log.h
diff --git a/arch/x86_64/serial.h b/arch/x86_64/serial.h
new file mode 100644
index 0000000..ecb896d
--- /dev/null
+++ b/arch/x86_64/serial.h
@@ -0,0 +1,27 @@
+#ifndef JOVE_KERNEL_ARCH_x86_64_SERIAL_H
+#define JOVE_KERNEL_ARCH_x86_64_SERIAL_H 1
+
+#include <stdbool.h>
+#include <stddef.h>
+
+#define SERIAL_UART_COM1 0x3F8
+#define SERIAL_UART_COM2 0x2F8
+
+#define SERIAL_UART_COM_THR(COM) COM
+#define SERIAL_UART_COM_RBR(COM) COM
+#define SERIAL_UART_COM_DLAB_DLL(COM) COM
+#define SERIAL_UART_COM_IER(COM) (COM + 1)
+#define SERIAL_UART_COM_DLAB_DLH(COM) (COM + 1)
+#define SERIAL_UART_COM_IIR(COM) (COM + 2)
+#define SERIAL_UART_COM_FCR(COM) (COM + 2)
+#define SERIAL_UART_COM_LCR(COM) (COM + 3)
+#define SERIAL_UART_COM_MCR(COM) (COM + 4)
+#define SERIAL_UART_COM_LSR(COM) (COM + 5)
+#define SERIAL_UART_COM_MSR(COM) (COM + 6)
+#define SERIAL_UART_COM_SR(COM) (COM + 7)
+
+extern bool serial_supported;
+
+void serial_out(const char *s, size_t len);
+
+#endif
diff --git a/arch/x86_64/syscall.c b/arch/x86_64/syscall.c
new file mode 100644
index 0000000..0398f11
--- /dev/null
+++ b/arch/x86_64/syscall.c
@@ -0,0 +1 @@
+#include "usr/umode.h"
diff --git a/arch/x86_64/syscall.d b/arch/x86_64/syscall.d
new file mode 100644
index 0000000..bb8f166
--- /dev/null
+++ b/arch/x86_64/syscall.d
@@ -0,0 +1 @@
+arch/x86_64/syscall.o: arch/x86_64/syscall.c usr/umode.h
diff --git a/arch/x86_64/syscall_setup.S b/arch/x86_64/syscall_setup.S
new file mode 100644
index 0000000..cbd3220
--- /dev/null
+++ b/arch/x86_64/syscall_setup.S
@@ -0,0 +1,37 @@
+.extern _kernel_thread_sp
+.extern syscall_handler
+
+.global syscall_entry
+.type syscall_entry @function
+syscall_entry:
+ movq %rsp, %rax
+ movq (_kernel_thread_bp), %rsp
+ pushq %rax
+ pushq %rbp
+ pushq %rcx
+ pushq %r11
+ movq %rsp, %rbp
+ call syscall_handler
+ popq %r11
+ popq %rcx
+ popq %rbp
+ popq %rsp
+ sysretq
+
+.global syscall_setup_syscall
+.type syscall_setup_syscall @function
+syscall_setup_syscall:
+ movq $0xc0000082, %rcx
+ leaq syscall_entry, %rdx
+ mov %edx, %eax
+ shr $32, %rdx
+ wrmsr
+ movq $0xc0000080, %rcx
+ rdmsr
+ or $1, %eax
+ wrmsr
+ movq $0xc0000081, %rcx
+ rdmsr
+ mov $0x00180008, %edx
+ wrmsr
+ retq
diff --git a/arch/x86_64/tables.c b/arch/x86_64/tables.c
new file mode 100644
index 0000000..f530089
--- /dev/null
+++ b/arch/x86_64/tables.c
@@ -0,0 +1,8 @@
+#include "tables.h"
+
+void
+arch_tables_setup(void)
+{
+ x86_64_load_gdt();
+ x86_64_load_idt();
+}
diff --git a/arch/x86_64/tables.d b/arch/x86_64/tables.d
new file mode 100644
index 0000000..6b10ebe
--- /dev/null
+++ b/arch/x86_64/tables.d
@@ -0,0 +1 @@
+arch/x86_64/tables.o: arch/x86_64/tables.c arch/x86_64/tables.h
diff --git a/arch/x86_64/tables.h b/arch/x86_64/tables.h
new file mode 100644
index 0000000..8e43bec
--- /dev/null
+++ b/arch/x86_64/tables.h
@@ -0,0 +1,60 @@
+#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
+
+struct 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));
+
+struct InterruptTrapGate
+{
+ 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;
+
+}__attribute__((packed));
+
+struct XDTR /* Generic table descriptor struct */
+{
+ uint16_t length;
+ uint64_t address;
+}__attribute__((packed));
+
+void x86_64_load_gdt(void);
+void x86_64_load_idt(void);
+
+#endif
diff --git a/arch/x86_64/thread.c b/arch/x86_64/thread.c
new file mode 100644
index 0000000..da20067
--- /dev/null
+++ b/arch/x86_64/thread.c
@@ -0,0 +1,85 @@
+#include "tsk/tasking.h"
+#include "mem/memory.h"
+#include "io/log.h"
+#include "lib/hashtable.h"
+#include "lib/string.h"
+#include "paging.h"
+#include "cpu.h"
+
+struct ThreadBody {
+ struct Thread base;
+ struct PageDirectory pd;
+ struct Registers state;
+};
+
+struct Thread *thread_current;
+uintptr_t _kernel_thread_bp = 0;
+
+//static struct SLinkedList s_threads;
+static struct HashTable s_threads;
+
+static struct SlabCache s_thread_cache;
+static struct SlabCache s_kbp_cache;
+
+static tid_t s_thread_id_next = 1;
+
+static size_t
+s_thread_hash_func(const void* tid, size_t _)
+{
+ return (tid_t)tid;
+}
+
+void
+tasking_setup(void)
+{
+ hashtable_new(&s_threads, struct ThreadBody*, tid_t);
+ s_threads.hash_function = s_thread_hash_func;
+
+ mem_slabcache_new(&s_thread_cache, "threads", sizeof(struct ThreadBody));
+ mem_slabcache_new(&s_kbp_cache, "kernel stacks", 4096);
+
+ struct ThreadBody *kthread = mem_slab_alloc(&s_thread_cache);
+ *kthread = (struct ThreadBody){
+ .base.id = s_thread_id_next++,
+ .base.kbp = ((uintptr_t)mem_slab_alloc(&s_kbp_cache)) + 0xFFF,
+ .base.perm = (size_t)-1,
+ .pd = *mem_current_pd
+ };
+ hashtable_insert(&s_threads, 0, kthread);
+
+ thread_current = (struct Thread*)kthread;
+ _kernel_thread_bp = thread_current->kbp;
+}
+
+struct Thread*
+thread_new(struct Thread *parent)
+{
+ struct ThreadBody *new = mem_slab_alloc(&s_thread_cache);
+ memcpy(new, parent, sizeof(struct ThreadBody));
+ new->base.id = s_thread_id_next++;
+
+ uintptr_t ksp_base = (uintptr_t)mem_slab_alloc(&s_kbp_cache);
+ new->base.kbp = ksp_base + 0xFFF;
+ new->base.perm = parent->perm;
+
+ hashtable_insert(&s_threads, (void*)new->base.id, new);
+ return (struct Thread*)new;
+}
+
+struct Thread*
+thread_get(tid_t id)
+{
+ struct Thread **thread = hashtable_get(&s_threads, (void*)id, struct Thread*);
+ if(thread == NULL) return NULL;
+ return *thread;
+}
+
+void
+thread_free(struct Thread *thread)
+{
+ struct ThreadBody *body = (struct ThreadBody*)thread;
+ body->pd.references--;
+ thread->kbp -= 0xFFF;
+ mem_slab_free(&s_kbp_cache, (void*)(thread->kbp));
+ klogf("Need impl for thread_free\n");
+}
diff --git a/arch/x86_64/thread.d b/arch/x86_64/thread.d
new file mode 100644
index 0000000..c78e4a9
--- /dev/null
+++ b/arch/x86_64/thread.d
@@ -0,0 +1,3 @@
+arch/x86_64/thread.o: arch/x86_64/thread.c tsk/tasking.h mem/memory.h \
+ mem/slab.h io/log.h lib/hashtable.h lib/linkedlist.h lib/string.h \
+ arch/x86_64/paging.h arch/x86_64/cpu.h
diff --git a/arch/x86_64/tss.h b/arch/x86_64/tss.h
new file mode 100644
index 0000000..34ec444
--- /dev/null
+++ b/arch/x86_64/tss.h
@@ -0,0 +1,18 @@
+#ifndef JOVE_ARCH_x86_64_TSS_H
+#define JOVE_ARCH_x86_64_TSS_H 1
+
+#include <stddef.h>
+#include <stdint.h>
+
+struct TSS
+{
+ uint32_t resv0;
+ uint64_t rsp[3];
+ uint64_t resv1;
+ uint64_t ist[8];
+ uint64_t resv2;
+ uint16_t resv3;
+ uint16_t iobp;
+};
+
+#endif
diff --git a/arch/x86_64/uart.h b/arch/x86_64/uart.h
new file mode 100644
index 0000000..8386eb7
--- /dev/null
+++ b/arch/x86_64/uart.h
@@ -0,0 +1,19 @@
+#ifndef JOVE_KERNEL_ARCH_x86_64_UART_H
+#define JOVE_KERNEL_ARCH_x86_64_UART_H 1
+
+#include "lib/jove.h"
+#include <stdint.h>
+
+ALWAYS_INLINE uint8_t pinb(uint16_t port)
+{
+ uint8_t v;
+ __asm__ volatile("inb %1, %0": "=a"(v): "Nd"(port));
+ return v;
+}
+
+ALWAYS_INLINE void poutb(uint16_t port, uint8_t b)
+{
+ __asm__ volatile("outb %0, %1":: "a"(b), "Nd"(port));
+}
+
+#endif
diff --git a/arch/x86_64/umode_enter.S b/arch/x86_64/umode_enter.S
new file mode 100644
index 0000000..29aa4e3
--- /dev/null
+++ b/arch/x86_64/umode_enter.S
@@ -0,0 +1,8 @@
+.global umode_enter
+.type umode_enter @function
+umode_enter:
+ movq %rdi, %rcx
+ movq %rsi, %rsp
+ movq $0x0202, %r11
+ cli
+ sysretq
diff --git a/boot/boot.h b/boot/boot.h
new file mode 100644
index 0000000..d7571d8
--- /dev/null
+++ b/boot/boot.h
@@ -0,0 +1,40 @@
+#ifndef JOVE_BOOT_H
+#define JOVE_BOOT_H 1
+
+#include <stdint.h>
+#include <stddef.h>
+#include <stdbool.h>
+
+struct MemoryMapEntry {
+ uintptr_t base;
+ size_t length;
+ bool usable;
+};
+
+#define MEMORY_MAP_MAX_ENTRIES 64
+struct MemoryMap {
+ size_t count;
+ struct MemoryMapEntry entries[MEMORY_MAP_MAX_ENTRIES];
+};
+
+#define BOOT_MODULE_MAX_ENTRIES 4
+struct BootModule {
+ char *path;
+ char *cmdline;
+ size_t size;
+ uintptr_t addr;
+};
+
+#define KERNEL_INITIAL_STACK_WIDTH (0x1000 * 4)
+extern void *boot_kernel_initial_stack_base;
+extern uintptr_t boot_kernel_physical_address;
+extern const char *boot_kernel_cmdline;
+
+extern struct MemoryMap boot_memorymap;
+
+extern struct BootModule boot_modules[BOOT_MODULE_MAX_ENTRIES];
+extern size_t boot_module_count;
+
+#define boot_kernel_virtual_base 0xFFFFFFFF80000000ULL
+
+#endif
diff --git a/boot/cmdline.c b/boot/cmdline.c
new file mode 100644
index 0000000..aa6aa05
--- /dev/null
+++ b/boot/cmdline.c
@@ -0,0 +1,50 @@
+#include "cmdline.h"
+#include "boot.h"
+#include "mem/memory.h"
+#include "lib/string.h"
+#include "io/log.h"
+
+static struct HashTable s_cmdline_kernel;
+
+const char*
+cmdline_get(const char *key)
+{
+ const char** value = hashtable_get(&s_cmdline_kernel, key, const char*);
+ if(value == NULL) return NULL;
+ return *value;
+}
+
+void
+cmdline_kernel_setup(void)
+{
+ hashtable_news(&s_cmdline_kernel, const char*);
+ size_t cmdi = 0;
+
+ const char *cmdline = boot_kernel_cmdline;
+ if(cmdline == 0) return;
+
+ while(cmdline[cmdi] != 0)
+ {
+ size_t keyi = cmdi;
+ while(cmdline[keyi] != '=' && cmdline[keyi] != 0) keyi++;
+ if(cmdline[keyi] == 0 || keyi == 0) {
+ klogf("kernel cmdline is empty or malformed; skipping.\n");
+ break;
+ }
+
+ size_t keylen = keyi - cmdi;
+ char *key = mem_alloc(keylen);
+ memcpy(key, &cmdline[cmdi], keylen);
+
+ size_t valuei = keyi + 1;
+ while(cmdline[valuei] != ' ' && cmdline[valuei] != 0) valuei++;
+
+ size_t valuelen = (valuei - keyi) - 1;
+ char *value = mem_alloc(valuelen);
+ if(valuelen != 0) memcpy(value, &cmdline[keyi + 1], valuelen);
+
+ klogf("%s = %s\n", key, value);
+ hashtable_insert(&s_cmdline_kernel, key, &value);
+ cmdi = valuei + 1;
+ }
+}
diff --git a/boot/cmdline.d b/boot/cmdline.d
new file mode 100644
index 0000000..f3ba401
--- /dev/null
+++ b/boot/cmdline.d
@@ -0,0 +1,3 @@
+boot/cmdline.o: boot/cmdline.c boot/cmdline.h lib/hashtable.h \
+ lib/linkedlist.h boot/boot.h mem/memory.h mem/slab.h lib/string.h \
+ io/log.h
diff --git a/boot/cmdline.h b/boot/cmdline.h
new file mode 100644
index 0000000..b1162b7
--- /dev/null
+++ b/boot/cmdline.h
@@ -0,0 +1,10 @@
+#ifndef JOVE_BOOT_CMDLINE_H
+#define JOVE_BOOT_CMDLINE_H 1
+
+#include "lib/hashtable.h"
+
+const char *cmdline_get(const char *key);
+
+void cmdline_kernel_setup(void);
+
+#endif
diff --git a/boot/limine/limine.c b/boot/limine/limine.c
new file mode 100644
index 0000000..7cc50d0
--- /dev/null
+++ b/boot/limine/limine.c
@@ -0,0 +1,89 @@
+#include "limine.h"
+#include "boot/boot.h"
+
+static char s_kernel_stack[KERNEL_INITIAL_STACK_WIDTH];
+
+struct MemoryMap boot_memorymap;
+
+struct BootModule boot_modules[BOOT_MODULE_MAX_ENTRIES];
+struct BootModule boot_kernel_file;
+size_t boot_module_count = 0;
+
+uintptr_t boot_kernel_physical_address;
+void *boot_kernel_initial_stack_base = &s_kernel_stack;
+const char *boot_kernel_cmdline = NULL;
+
+struct limine_memmap_request s_mmap_request = {
+ .id = LIMINE_MEMMAP_REQUEST
+};
+struct limine_kernel_address_request s_kaddr_request = {
+ .id = LIMINE_KERNEL_ADDRESS_REQUEST
+};
+struct limine_kernel_file_request s_kfile_request = {
+ .id = LIMINE_KERNEL_FILE_REQUEST
+};
+struct limine_module_request s_module_request = {
+ .id = LIMINE_MODULE_REQUEST
+};
+
+static void
+s_load_memorymap(void)
+{
+ struct limine_memmap_response *response = s_mmap_request.response;
+ if(response == NULL) return;
+ boot_memorymap.count = response->entry_count;
+ for(size_t i = 0; i < response->entry_count; i++) {
+ struct limine_memmap_entry *e = response->entries[i];
+ struct MemoryMapEntry *bme = &boot_memorymap.entries[i];
+
+ bme->base = e->base;
+ bme->length = e->length;
+ bme->usable = e->type == LIMINE_MEMMAP_USABLE ||
+ e->type == LIMINE_MEMMAP_ACPI_RECLAIMABLE ||
+ e->type == LIMINE_MEMMAP_BOOTLOADER_RECLAIMABLE;
+ }
+}
+
+static void
+s_load_modules(void)
+{
+ struct limine_module_response *response = s_module_request.response;
+ if(response->module_count > 4) response->module_count = 4;
+ boot_module_count = response->module_count;
+ for(size_t i = 0; i < response->module_count; i++) {
+ boot_modules[i] = (struct BootModule){
+ .path = response->modules[i]->path,
+ .cmdline = response->modules[i]->cmdline,
+ .size = response->modules[i]->size,
+ .addr = (uintptr_t)response->modules[i]->address
+ };
+ }
+}
+
+static void
+s_load_kernel_file(void)
+{
+ struct limine_kernel_file_response *response = s_kfile_request.response;
+ boot_kernel_file = (struct BootModule) {
+ .path = response->kernel_file->path,
+ .cmdline = response->kernel_file->cmdline,
+ .size = response->kernel_file->size,
+ .addr = (uintptr_t)response->kernel_file->address
+ };
+ boot_kernel_cmdline = response->kernel_file->cmdline;
+}
+
+void _start(void)
+{
+ boot_kernel_physical_address = s_kaddr_request.response->physical_base;
+ s_load_memorymap();
+
+ s_load_kernel_file();
+ s_load_modules();
+
+ char *kernel_stack_entry = s_kernel_stack + (KERNEL_INITIAL_STACK_WIDTH - 1);
+ __asm__ volatile("movq %0, %%rsp":: "r"(kernel_stack_entry));
+
+ extern void kernel_main(void);
+ kernel_main();
+}
diff --git a/boot/limine/limine.d b/boot/limine/limine.d
new file mode 100644
index 0000000..1c946a9
--- /dev/null
+++ b/boot/limine/limine.d
@@ -0,0 +1,2 @@
+boot/limine/limine.o: boot/limine/limine.c boot/limine/limine.h \
+ boot/boot.h
diff --git a/boot/limine/limine.h b/boot/limine/limine.h
new file mode 100644
index 0000000..186f3b1
--- /dev/null
+++ b/boot/limine/limine.h
@@ -0,0 +1,579 @@
+/* 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/compile_flags.txt b/compile_flags.txt
new file mode 100644
index 0000000..ab1f054
--- /dev/null
+++ b/compile_flags.txt
@@ -0,0 +1,3 @@
+-I.
+-ffreestanding
+-nostdlib
diff --git a/config.mk b/config.mk
new file mode 100644
index 0000000..11ce9f4
--- /dev/null
+++ b/config.mk
@@ -0,0 +1,24 @@
+TARGET_MACHINE = x86_64
+TARGET_OS = jove
+TARGET_TRIPLET = $(TARGET_MACHINE)-$(TARGET_OS)
+TARGET_BOOTLOADER = limine
+
+CFLAGS = \
+ -ffreestanding \
+ -mno-sse \
+ -nostdlib \
+ -fno-pie \
+ -fno-pic \
+ -g \
+ -D$(TARGET_MACHINE) \
+ -I.
+
+LDFLAGS = -nostdlib \
+ -z max-page-size=0x1000 \
+ -T link/$(TARGET_TRIPLET).ld
+
+ifeq "$(TARGET_MACHINE)" "x86_64"
+ CFLAGS += -mno-red-zone \
+ -mcmodel=kernel \
+ -MMD
+endif
diff --git a/io/interrupt.h b/io/interrupt.h
new file mode 100644
index 0000000..444ff01
--- /dev/null
+++ b/io/interrupt.h
@@ -0,0 +1,6 @@
+#ifndef JOVE_IO_INTERRUPT_H
+#define JOVE_IO_INTERRUPT_H 1
+
+
+
+#endif
diff --git a/io/log.c b/io/log.c
new file mode 100644
index 0000000..14397a7
--- /dev/null
+++ b/io/log.c
@@ -0,0 +1,68 @@
+#include "log.h"
+#include "lib/jove.h"
+#include "lib/string.h"
+#include "lib/format.h"
+
+static struct LogDevice *s_first_logdev = NULL;
+
+void
+klog_newdev(struct LogDevice *dev)
+{
+ if(s_first_logdev == NULL) {
+ s_first_logdev = dev;
+ }else{
+ dev->chain = s_first_logdev;
+ s_first_logdev = dev;
+ }
+}
+
+static void
+s_klogc(struct LogDevice *dev, char c)
+{
+ dev->out(&c, 1);
+ if(dev->chain != NULL)
+ s_klogc(dev->chain, c);
+}
+
+void
+klogc(char c)
+{
+ s_klogc(s_first_logdev, c);
+}
+
+static void
+s_klogs(struct LogDevice *dev, const char *s, size_t slen)
+{
+ dev->out(s, slen);
+ if(dev->chain != NULL)
+ s_klogs(dev->chain, s, slen);
+}
+
+void
+klogs(const char *s)
+{
+ size_t slen = strlen(s);
+ s_klogs(s_first_logdev, s, slen);
+}
+
+void
+klogsn(const char *s, size_t len)
+{
+ s_klogs(s_first_logdev, s, len);
+}
+
+void
+kvlogf(const char *fmt, va_list ap)
+{
+ char buffer[256];
+ svfmt(buffer, 256, fmt, ap);
+ klogs(buffer);
+}
+
+void klogf(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ kvlogf(fmt, ap);
+ va_end(ap);
+}
diff --git a/io/log.d b/io/log.d
new file mode 100644
index 0000000..b8609ce
--- /dev/null
+++ b/io/log.d
@@ -0,0 +1 @@
+io/log.o: io/log.c io/log.h lib/jove.h lib/string.h lib/format.h
diff --git a/io/log.h b/io/log.h
new file mode 100644
index 0000000..ad1526d
--- /dev/null
+++ b/io/log.h
@@ -0,0 +1,22 @@
+#ifndef JOVE_IO_LOG_H
+#define JOVE_IO_LOG_H 1
+
+#include <stddef.h>
+
+struct LogDevice
+{
+ void (*out)(const char*, size_t);
+ struct LogDevice *chain;
+};
+
+void klog_newdev(struct LogDevice *dev);
+
+void klogc(char c);
+void klogs(const char *s);
+void klogsn(const char *s, size_t len);
+
+#include <stdarg.h>
+void kvlogf(const char *fmt, va_list ap);
+void klogf(const char *fmt, ...);
+
+#endif
diff --git a/ird/initrd.c b/ird/initrd.c
new file mode 100644
index 0000000..f219243
--- /dev/null
+++ b/ird/initrd.c
@@ -0,0 +1,88 @@
+#include "initrd.h"
+#include "lib/string.h"
+#include "io/log.h"
+#include "boot/boot.h"
+#include "boot/cmdline.h"
+#include "mem/memory.h"
+
+struct SlabCache s_initrd_cache;
+struct SLinkedList s_initrd_files;
+
+static size_t
+s_tar_oct_dec(const char *oct)
+{
+ size_t value = 0;
+ while(*oct != 0) {
+ value *= 8;
+ value += (*oct) - '0';
+ oct++;
+ }
+ return value;
+}
+
+static void
+s_initrd_parse(struct BootModule *module)
+{
+ struct TARSector *sector = (struct TARSector*)module->addr;
+ while(true) {
+ struct TARHeader *header = (struct TARHeader*)sector;
+ if(header->name[0] == 0) break;
+
+ struct InitrdFile *file = mem_slab_alloc(&s_initrd_cache);
+ *file = (struct InitrdFile){
+ .header = header,
+ .name = header->name,
+ .size = s_tar_oct_dec(header->size),
+ .data = &sector[1]
+ };
+ klogf("File %s size %i\n", file->name, file->size);
+ sll_push(&s_initrd_files, file);
+
+ sector = &sector[(file->size / 512) + 1];
+ if(file->size % 512 > 0) sector = &sector[1];
+ }
+}
+
+struct InitrdFile *
+ird_getfile(const char *path)
+{
+ for(struct SLLNode *node = s_initrd_files.head; node != NULL; node = node->next) {
+ struct InitrdFile *file = (struct InitrdFile*)node;
+ if(strcmp(file->name, path) == 0) return file;
+ }
+ return NULL;
+}
+
+struct SLinkedList *
+ird_getfiles(void)
+{
+ return &s_initrd_files;
+}
+
+void
+initrd_setup(void)
+{
+ const char *initrd_path = cmdline_get("initrd");
+ if(initrd_path == 0) {
+ klogf("No initrd loaded!\n");
+ return;
+ }
+ klogf("Initrd located in module path %s\n", initrd_path);
+ struct BootModule *initrd_module = NULL;
+ for(size_t i = 0; i < boot_module_count; i++) {
+ struct BootModule *module = &boot_modules[i];
+ if(strcmp(module->path, initrd_path) == 0) {
+ initrd_module = module;
+ break;
+ }
+ }
+
+ if(initrd_module == NULL) {
+ klogf("Initrd not found in modules!\n");
+ return;
+ }
+
+ sll_new(&s_initrd_files, sizeof(struct InitrdFile));
+ mem_slabcache_new(&s_initrd_cache, "initrd files", sizeof(struct InitrdFile));
+ s_initrd_parse(initrd_module);
+}
diff --git a/ird/initrd.d b/ird/initrd.d
new file mode 100644
index 0000000..b2b0e40
--- /dev/null
+++ b/ird/initrd.d
@@ -0,0 +1,3 @@
+ird/initrd.o: ird/initrd.c ird/initrd.h ird/tar.h lib/linkedlist.h \
+ lib/string.h io/log.h boot/boot.h boot/cmdline.h lib/hashtable.h \
+ lib/linkedlist.h mem/memory.h mem/slab.h
diff --git a/ird/initrd.h b/ird/initrd.h
new file mode 100644
index 0000000..f236fa1
--- /dev/null
+++ b/ird/initrd.h
@@ -0,0 +1,21 @@
+#ifndef JOVE_INITRD_H
+#define JOVE_INITRD_H 1
+
+#include <stddef.h>
+#include "tar.h"
+#include "lib/linkedlist.h"
+
+struct InitrdFile {
+ struct InitrdFile *next;
+ struct TARHeader *header;
+ const char *name;
+ size_t size;
+ const void *data;
+};
+
+void initrd_setup(void);
+
+struct InitrdFile *ird_getfile(const char *path);
+struct SLinkedList *ird_getfiles(void);
+
+#endif
diff --git a/ird/tar.h b/ird/tar.h
new file mode 100644
index 0000000..5c72620
--- /dev/null
+++ b/ird/tar.h
@@ -0,0 +1,18 @@
+#ifndef JOVE_INITRD_TAR_H
+#define JOVE_INITRD_TAR_H 1
+
+struct TARSector { char data[512]; };
+
+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];
+};
+
+#endif
diff --git a/lib/format.h b/lib/format.h
new file mode 100644
index 0000000..5a2b75c
--- /dev/null
+++ b/lib/format.h
@@ -0,0 +1,13 @@
+#ifndef JOVE_LIB_FORMAT_H
+#define JOVE_LIB_FORMAT_H 1
+
+#include <stddef.h>
+#include <stdbool.h>
+
+size_t ltostr(char *s, size_t limit, unsigned long l, bool sgn, int radix);
+
+char *sfmt(char *s, size_t limit, const char *fmt, ...);
+#include <stdarg.h>
+char *svfmt(char *s, size_t limit, const char *fmt, va_list ap);
+
+#endif
diff --git a/lib/hashtable.c b/lib/hashtable.c
new file mode 100644
index 0000000..17f1e74
--- /dev/null
+++ b/lib/hashtable.c
@@ -0,0 +1,109 @@
+#include "hashtable.h"
+#include "string.h"
+#include "mem/memory.h"
+
+static size_t
+s_hash_function_mul(const void *data, size_t len)
+{
+ size_t hash = 1;
+ const char *cdata = (const char*)data;
+ for(size_t i = 0; i < len; i++) {
+ hash *= ((size_t)cdata[i]) + 1;
+ }
+ return hash;
+}
+
+static size_t
+s_hash_function_mul_s(const void *data, size_t _)
+{
+ return s_hash_function_mul(data, strlen(data));
+}
+
+void
+_hashtable_new(struct HashTable *table, size_t obj_size, size_t key_size)
+{
+ *table = (struct HashTable){
+ .buckets = NULL,
+ .bucket_count = 0,
+ .bucket_capacity = 2,
+ .obj_size = obj_size,
+ .key_size = key_size,
+ .hash_function = s_hash_function_mul
+ };
+ table->buckets = mem_alloc(sizeof(struct SLinkedList) * 2);
+}
+
+void
+_hashtable_news(struct HashTable *table, size_t obj_size)
+{
+ *table = (struct HashTable){
+ .buckets = NULL,
+ .bucket_count = 0,
+ .bucket_capacity = 2,
+ .obj_size = obj_size,
+ .key_size = -1,
+ .hash_function = s_hash_function_mul_s
+ };
+ table->buckets = mem_alloc(sizeof(struct SLinkedList) * 2);
+}
+
+void
+hashtable_insert(struct HashTable *table, const void *key, void *data)
+{
+ size_t hash = table->hash_function(key, table->key_size);
+ if(table->bucket_capacity == table->bucket_count) {
+ struct SLinkedList *old_buckets = table->buckets;
+ size_t old_buckets_count = table->bucket_count;
+
+ table->bucket_capacity *= 2;
+ table->bucket_count = 0;
+ table->buckets = mem_alloc(sizeof(struct SLinkedList) * table->bucket_capacity);
+ for(size_t i = 0; i < old_buckets_count; i++) {
+ for(struct SLLNode *node = old_buckets[i].head; node != NULL; node = node->next) {
+ struct HashTableValue *value = (struct HashTableValue*)(&node->data);
+ hashtable_insert(table, value->key, value->value);
+ mem_free(node);
+ }
+ }
+ }
+
+ size_t index = hash % table->bucket_capacity;
+ struct SLinkedList *bucket = &table->buckets[index];
+ struct SLLNode *node = mem_alloc(sizeof(struct SLLNode) + sizeof(struct HashTableValue) + table->obj_size);
+ struct HashTableValue *value = (struct HashTableValue*)(node->data);
+
+ value->key = key;
+ memcpy(value->value, data, table->obj_size);
+
+ table->bucket_count++;
+ if(bucket->count == 0) {
+ sll_new(bucket, table->obj_size);
+ sll_push(bucket, node);
+ return;
+ }
+ for(struct SLLNode *onode = bucket->head; onode != NULL; onode = onode->next)
+ {
+ struct HashTableValue *ovalue = (struct HashTableValue*)onode->data;
+ size_t keylen = table->key_size > 0 ? table->key_size : strlen((const char*)key) + 1;
+ if(memcmp(ovalue->key, value->key, keylen) == 0) {
+ memcpy(ovalue->value, value->value, table->obj_size);
+ return;
+ }
+ }
+ sll_push(bucket, node);
+}
+
+void*
+_hashtable_get(struct HashTable *table, const void *key)
+{
+ size_t hash = table->hash_function(key, table->key_size);
+ size_t index = hash % table->bucket_capacity;
+ struct SLinkedList *bucket = &table->buckets[index];
+ for(struct SLLNode *node = bucket->head; node != NULL; node = node->next)
+ {
+ struct HashTableValue *value = (struct HashTableValue*)node->data;
+ size_t keylen = table->key_size > 0 ? table->key_size : strlen((const char*)value->key);
+ if(memcmp(key, value->key, keylen) == 0) return value->value;
+ }
+ return NULL;
+}
diff --git a/lib/hashtable.d b/lib/hashtable.d
new file mode 100644
index 0000000..791f457
--- /dev/null
+++ b/lib/hashtable.d
@@ -0,0 +1,2 @@
+lib/hashtable.o: lib/hashtable.c lib/hashtable.h lib/linkedlist.h \
+ lib/string.h mem/memory.h mem/slab.h
diff --git a/lib/hashtable.h b/lib/hashtable.h
new file mode 100644
index 0000000..fe6f5c3
--- /dev/null
+++ b/lib/hashtable.h
@@ -0,0 +1,34 @@
+#ifndef JOVE_LIB_HASHTABLE_H
+#define JOVE_LIB_HASHTABLE_H 1
+
+#include <stddef.h>
+#include <stdint.h>
+#include "linkedlist.h"
+
+struct HashTableValue
+{
+ const void *key;
+ char value[];
+};
+
+struct HashTable {
+ struct SLinkedList *buckets;
+ size_t bucket_count;
+ size_t bucket_capacity;
+
+ size_t obj_size;
+ int key_size;
+ size_t (*hash_function)(const void*, size_t);
+};
+
+void _hashtable_new(struct HashTable *table, size_t obj_size, size_t key_size);
+#define hashtable_new(table, type, keytype) _hashtable_new(table, sizeof(type), sizeof(keytype))
+
+void _hashtable_news(struct HashTable *table, size_t obj_size);
+#define hashtable_news(table, type) _hashtable_news(table, sizeof(type))
+
+void hashtable_insert(struct HashTable *table, const void *key, void *data);
+void *_hashtable_get(struct HashTable *table, const void *key);
+#define hashtable_get(table, key, type) (type*)_hashtable_get(table, key)
+
+#endif
diff --git a/lib/jove.h b/lib/jove.h
new file mode 100644
index 0000000..4aa60ed
--- /dev/null
+++ b/lib/jove.h
@@ -0,0 +1,15 @@
+#ifndef JOVE_LIB_JOVE_H
+#define JOVE_LIB_JOVE_H 1
+
+#define ALWAYS_INLINE inline __attribute__((always_inline))
+#define PAGEALIGN __attribute__((aligned(0x1000)))
+
+//#define LOG2(n) (__builtin_clz(n) ^ 31)
+
+extern void *_kernel_start;
+extern void *_kernel_end;
+
+__attribute__((noreturn)) void _kpanic(const char *file, int line, const char *fmt, ...);
+#define kpanic(...) _kpanic(__FILE__, __LINE__, __VA_ARGS__)
+
+#endif
diff --git a/lib/kpanic.c b/lib/kpanic.c
new file mode 100644
index 0000000..97e42eb
--- /dev/null
+++ b/lib/kpanic.c
@@ -0,0 +1,15 @@
+#include "jove.h"
+#include "io/log.h"
+
+#include <stdarg.h>
+
+__attribute__((noreturn))
+void _kpanic(const char *file, int line, const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ klogf("!!! PANIC !!!\n%s:%i\n", file, line);
+ kvlogf(fmt, ap);
+ va_end(ap);
+ for(;;) __asm__ volatile("hlt");
+}
diff --git a/lib/kpanic.d b/lib/kpanic.d
new file mode 100644
index 0000000..bf4ed9e
--- /dev/null
+++ b/lib/kpanic.d
@@ -0,0 +1 @@
+lib/kpanic.o: lib/kpanic.c lib/jove.h io/log.h
diff --git a/lib/linkedlist.c b/lib/linkedlist.c
new file mode 100644
index 0000000..6cc6403
--- /dev/null
+++ b/lib/linkedlist.c
@@ -0,0 +1,34 @@
+#include "linkedlist.h"
+
+void
+sll_new(struct SLinkedList *list, size_t obj_size)
+{
+ list->obj_size = obj_size;
+ list->count = 0;
+ list->head = list->tail = NULL;
+};
+
+void
+sll_push(struct SLinkedList *list, void *data)
+{
+ struct SLLNode *node = (struct SLLNode*)data;
+ if(list->tail != NULL) {
+ list->tail->next = node;
+ }
+ if(list->head == NULL)
+ list->head = node;
+ list->tail = node;
+ list->count++;
+}
+
+void*
+sll_get(struct SLinkedList *list, size_t index)
+{
+ struct SLLNode *node = list->head;
+ if(node == NULL) return NULL;
+ if(index > list->count) return list->tail;
+ for(size_t i = 0; i < index; i++) {
+ node = node->next;
+ }
+ return node;
+}
diff --git a/lib/linkedlist.d b/lib/linkedlist.d
new file mode 100644
index 0000000..a06a9df
--- /dev/null
+++ b/lib/linkedlist.d
@@ -0,0 +1 @@
+lib/linkedlist.o: lib/linkedlist.c lib/linkedlist.h
diff --git a/lib/linkedlist.h b/lib/linkedlist.h
new file mode 100644
index 0000000..26c148e
--- /dev/null
+++ b/lib/linkedlist.h
@@ -0,0 +1,26 @@
+#ifndef JOVE_LIB_LINKEDLIST_H
+#define JOVE_LIB_LINKEDLIST_H 1
+
+#include <stdint.h>
+#include <stddef.h>
+
+struct SLLNode {
+ struct SLLNode *next;
+ char data[];
+};
+
+/*Singly Linked List*/
+struct SLinkedList
+{
+ struct SLLNode *head;
+ struct SLLNode *tail;
+
+ size_t obj_size;
+ size_t count;
+};
+
+void sll_new(struct SLinkedList *list, size_t obj_size);
+void sll_push(struct SLinkedList *list, void *node);
+void *sll_get(struct SLinkedList *list, size_t index);
+
+#endif
diff --git a/lib/ltostr.c b/lib/ltostr.c
new file mode 100644
index 0000000..e28be31
--- /dev/null
+++ b/lib/ltostr.c
@@ -0,0 +1,26 @@
+#include "format.h"
+
+size_t
+ltostr(char *s, size_t limit, unsigned long l, bool sgn, int radix)
+{
+ size_t si = 0;
+ size_t digits = 0;
+ if((long)l < 0 && sgn) {
+ l = -((long)l);
+ s[0] = '-';
+ }
+ for(unsigned long lv = l; lv != 0; lv /= radix)
+ digits++;
+ digits = digits > limit ? limit : digits;
+
+ if(digits-- == 0)
+ s[si++] = '0';
+ for(unsigned long lv = l; lv != 0; lv /= radix)
+ {
+ if(si >= limit) return si;
+ int digit = lv % radix;
+ s[(digits - si)] = (digit >= 10 ? (digit + 'a' - 10) : digit + '0');
+ si++;
+ }
+ return si;
+}
diff --git a/lib/ltostr.d b/lib/ltostr.d
new file mode 100644
index 0000000..fecc225
--- /dev/null
+++ b/lib/ltostr.d
@@ -0,0 +1 @@
+lib/ltostr.o: lib/ltostr.c lib/format.h
diff --git a/lib/mem.c b/lib/mem.c
new file mode 100644
index 0000000..b60fbbd
--- /dev/null
+++ b/lib/mem.c
@@ -0,0 +1,52 @@
+#include "string.h"
+#include "mem/memory.h"
+
+void*
+memset(void *dest, int c, size_t n)
+{
+ char *destc = (char*)dest;
+ for(size_t i = 0; i < n; i++)
+ destc[i] = c;
+ return dest;
+}
+
+void*
+memcpy(void *dest, const void *src, size_t n)
+{
+ char *destc = (char*)dest;
+ const char *srcc = (const char*)src;
+ for(size_t i = 0; i < n; i++)
+ destc[i] = srcc[i];
+ return dest;
+}
+
+void*
+memmove(void *dest, const void *src, size_t n)
+{
+ char *destc = (char*)dest;
+ const char *srcc = (const char*)src;
+ if(destc + n < srcc) return memcpy(dest, src, n);
+ char buffer[n];
+ memcpy(buffer, src, n);
+ return memcpy(destc, buffer, n);
+}
+
+int
+memcmp(const void *a, const void *b, size_t n)
+{
+ const char *ac = (const char*)a;
+ const char *bc = (const char*)b;
+ for(size_t i = 0; i < n; i++) {
+ if(ac[i] != bc[i]) return ac[i] - bc[i];
+ }
+ return 0;
+}
+
+char*
+strdup(const char *s)
+{
+ size_t slen = strlen(s);
+ char *ret = mem_alloc(slen);
+ memcpy(ret, s, slen);
+ return ret;
+}
diff --git a/lib/mem.d b/lib/mem.d
new file mode 100644
index 0000000..232ecd5
--- /dev/null
+++ b/lib/mem.d
@@ -0,0 +1 @@
+lib/mem.o: lib/mem.c lib/string.h mem/memory.h mem/slab.h
diff --git a/lib/sfmt.c b/lib/sfmt.c
new file mode 100644
index 0000000..f903cbf
--- /dev/null
+++ b/lib/sfmt.c
@@ -0,0 +1,112 @@
+#include "string.h"
+#include "format.h"
+
+char*
+sfmt(char *s, size_t limit, const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+
+ s = svfmt(s, limit, fmt, ap);
+
+ va_end(ap);
+ return s;
+}
+
+char*
+svfmt(char *s, size_t limit, const char *fmt, va_list ap)
+{
+
+ size_t fmtlen = strlen(fmt);
+ size_t si = 0;
+ for(size_t fmti = 0; fmti < fmtlen; fmti++)
+ {
+ if(si >= limit) break;
+ if(fmt[fmti] != '%') {
+ s[si++] = fmt[fmti];
+ continue;
+ }
+ // TODO: Format flags
+ fmti++;
+
+ bool alt = false;
+ bool zpad = false;
+ for(;;fmti++){
+ if(fmt[fmti] == '#') { alt = true; continue; }
+ if(fmt[fmti] == '0') { zpad = true; continue; }
+ break;
+ }
+ int fwidth = 0;
+ if(fmt[fmti] >= '1' && fmt[fmti] <= '9')
+ {
+ for(;fmt[fmti] >= '0' && fmt[fmti] <= '9';fmti++) {
+ fwidth *= 10;
+ fwidth += fmt[fmti] - '0';
+ }
+ }
+ int precision = 0;
+ if(fmt[fmti] == '.')
+ {
+ fmti++;
+ for(;fmt[fmti] >= '0' && fmt[fmti] <= '9';fmti++) {
+ precision *= 10;
+ precision += fmt[fmti] - '0';
+ }
+ }
+
+ bool sgn = true;
+ bool upper = false;
+ int radix = 10;
+ switch(fmt[fmti])
+ {
+ case '%':
+ s[si++] = '%';
+ break;
+ case 'b':
+ radix = 2;
+ case 'X':
+ upper = true;
+ case 'x':
+ if(radix == 10)
+ radix = 16;
+ case 'o':
+ if(radix == 10)
+ radix = 8;
+ case 'u':
+ sgn = false;
+ case 'i': {
+ if((radix == 8 || radix == 16) && alt) {
+ s[si++] = '0';
+ if(radix == 16) {
+ s[si++] = 'x';
+ }
+ if(radix == 2) {
+ s[si++] = 'b';
+ }
+ }
+ size_t osi = si;
+ size_t nlen = ltostr(&s[si], limit - si, va_arg(ap, long), sgn, radix);
+ if(upper) sntoupper(&s[si], nlen);
+ si += nlen;
+
+ int lpad = fwidth - (int)nlen;
+ if(lpad > 0)
+ {
+ if(lpad + osi >= limit) lpad = (limit - osi - 1);
+ memmove(&s[osi + lpad], &s[osi], nlen);
+ memset(&s[osi], zpad ? '0' : ' ', lpad);
+ si += lpad;
+ }
+ } break;
+ case 's': {
+ const char *str = va_arg(ap, char*);
+ size_t slen = strlen(str);
+ size_t wlen = slen > limit - si ? limit - si : slen;
+ for(size_t i = 0; i < wlen; i++)
+ s[si++] = str[i];
+ } break;
+ }
+ }
+ s[si] = 0;
+ return s;
+}
diff --git a/lib/sfmt.d b/lib/sfmt.d
new file mode 100644
index 0000000..f462cea
--- /dev/null
+++ b/lib/sfmt.d
@@ -0,0 +1 @@
+lib/sfmt.o: lib/sfmt.c lib/string.h lib/format.h
diff --git a/lib/string.c b/lib/string.c
new file mode 100644
index 0000000..4bb1bad
--- /dev/null
+++ b/lib/string.c
@@ -0,0 +1,16 @@
+#include "string.h"
+
+size_t strlen(const char *s) {
+ size_t l = 0;
+ for(; *s != 0; s++, l++);
+ return l;
+}
+
+int strcmp(const char *a, const char *b)
+{
+ size_t i = 0;
+ for(; a[i] != 0 && b[i] != 0; i++) {
+ if(a[i] != b[i]) return a[i] - b[i];
+ }
+ return a[i] - b[i];
+}
diff --git a/lib/string.d b/lib/string.d
new file mode 100644
index 0000000..dae7158
--- /dev/null
+++ b/lib/string.d
@@ -0,0 +1 @@
+lib/string.o: lib/string.c lib/string.h
diff --git a/lib/string.h b/lib/string.h
new file mode 100644
index 0000000..a91ec94
--- /dev/null
+++ b/lib/string.h
@@ -0,0 +1,18 @@
+#ifndef JOVE_LIB_STRING_H
+#define JOVE_LIB_STRING_H 1
+
+#include <stddef.h>
+
+int toupper(int c);
+char *stoupper(char *s);
+char *sntoupper(char *s, size_t limit);
+
+size_t strlen(const char *s);
+
+void *memset(void *dest, int c, size_t n);
+void *memcpy(void *dest, const void *src, size_t n);
+void *memmove(void *dest, const void *src, size_t n);
+int memcmp(const void *a, const void *b, size_t n);
+int strcmp(const char *a, const char *b);
+
+#endif
diff --git a/lib/toupper.c b/lib/toupper.c
new file mode 100644
index 0000000..807eb38
--- /dev/null
+++ b/lib/toupper.c
@@ -0,0 +1,26 @@
+#include "string.h"
+
+int
+toupper(int c)
+{
+ if(c >= 'a' && c <= 'z')
+ c -= ('a' - 'A');
+ return c;
+}
+
+char*
+stoupper(char *s)
+{
+ char *o = s;
+ for(; *s != 0; s++)
+ *s = toupper(*s);
+ return o;
+}
+
+char*
+sntoupper(char *s, size_t limit)
+{
+ for(size_t i = 0; i < limit; i++)
+ s[i] = toupper(s[i]);
+ return s;
+}
diff --git a/lib/toupper.d b/lib/toupper.d
new file mode 100644
index 0000000..72e73f9
--- /dev/null
+++ b/lib/toupper.d
@@ -0,0 +1 @@
+lib/toupper.o: lib/toupper.c lib/string.h
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..e74d8df
--- /dev/null
+++ b/main.c
@@ -0,0 +1,29 @@
+#include "arch/arch.h"
+#include "io/log.h"
+#include "mem/buddymap.h"
+#include "mem/memory.h"
+#include "boot/cmdline.h"
+#include "tsk/tasking.h"
+#include "ird/initrd.h"
+#include "usr/umode.h"
+#include "lib/jove.h"
+
+void
+kernel_main(void)
+{
+ serial_setup();
+ arch_tables_setup();
+
+ mem_buddy_setup();
+ mem_paging_setup();
+ mem_slab_setup();
+
+ cmdline_kernel_setup();
+
+ tasking_setup();
+
+ initrd_setup();
+ umode_setup();
+
+ kpanic("Reached end of kernel main\n");
+}
diff --git a/main.d b/main.d
new file mode 100644
index 0000000..66487e8
--- /dev/null
+++ b/main.d
@@ -0,0 +1,4 @@
+main.o: main.c arch/arch.h io/log.h mem/buddymap.h mem/memory.h \
+ mem/slab.h mem/memory.h boot/cmdline.h lib/hashtable.h lib/linkedlist.h \
+ tsk/tasking.h ird/initrd.h ird/tar.h lib/linkedlist.h usr/umode.h \
+ lib/jove.h
diff --git a/mem/buddymap.c b/mem/buddymap.c
new file mode 100644
index 0000000..5165876
--- /dev/null
+++ b/mem/buddymap.c
@@ -0,0 +1,151 @@
+#include "buddymap.h"
+#include "lib/string.h"
+#include "boot/boot.h"
+#include "io/log.h"
+#include <stdbool.h>
+
+#define ENTRY_BITS (sizeof(uintmax_t) * 8)
+#define ENTRY_SIZE (ENTRY_BITS * PAGESIZE)
+#define ENTRY_COUNT (MEMMAP_BUDDY_LIMIT / ENTRY_SIZE)
+#define BUDDY_LAYERS 4
+
+uintmax_t s_buddy_l0[ENTRY_COUNT];
+uintmax_t s_buddy_l1[ENTRY_COUNT << 1];
+uintmax_t s_buddy_l2[ENTRY_COUNT << 2];
+uintmax_t s_buddy_l3[ENTRY_COUNT << 3];
+uintmax_t *s_buddies[BUDDY_LAYERS] = {
+ s_buddy_l0,
+ s_buddy_l1,
+ s_buddy_l2,
+ s_buddy_l3
+};
+size_t s_buddies_lastfree[BUDDY_LAYERS] = { 1, 1, 1, 1 };
+
+static bool
+s_buddy_test(size_t l, size_t i)
+{
+ return (s_buddies[l][i / (ENTRY_BITS)] & (1ULL << (i % (ENTRY_BITS)))) > 0;
+}
+
+static void
+s_buddy_set(size_t l, size_t i)
+{
+ size_t j = i << l;
+ size_t w = 1 << l;
+ for(int layer = 0; layer < BUDDY_LAYERS; layer++)
+ {
+ if(w == 0) w = 1;
+ for(size_t bit = 0; bit < w; bit++) {
+ size_t entry = (j + bit) / ENTRY_BITS;
+ s_buddies[layer][entry] |= (1ULL << (j + bit % ENTRY_BITS));
+ }
+ j >>= 1;
+ w >>= 1;
+ }
+}
+
+static void
+s_buddy_unset(size_t l, size_t i)
+{
+ size_t j = i << l;
+ size_t w = 1 << l;
+ bool free_upper = false;
+ for(int layer = 0; layer < BUDDY_LAYERS; layer++)
+ {
+ if(w == 0) {
+ size_t lower = (j << 1) % ENTRY_BITS;
+ size_t other = (lower + 1);
+ size_t entry = (j << 1) / ENTRY_BITS;
+ if((s_buddies[layer-1][entry] & (1ULL << lower)) > 0 &&
+ (s_buddies[layer-1][entry] & (1ULL << other)) > 0)
+ s_buddies[layer][entry >> 1] &= ~(1ULL << (j % ENTRY_BITS));
+ }
+
+ for(size_t bit = 0; bit < w; bit++) {
+ size_t entry = j / ENTRY_BITS;
+ s_buddies[layer][entry] |= (1ULL << bit);
+ }
+ j >>= 1;
+ w >>= 1;
+ }
+}
+
+void
+mem_buddy_set_range(uintptr_t base, size_t length)
+{
+ for(int l = 0; l < BUDDY_LAYERS; l++) {
+ size_t bits = (length / PAGESIZE) >> l;
+ size_t biti = (base / PAGESIZE) >> l;
+
+ if(bits == 0) bits = 1;
+ for(size_t i = 0; i < bits; i++) {
+ size_t entry = (biti + i) / ENTRY_BITS;
+ s_buddies[l][entry] |= 1ULL << ((biti + i) % ENTRY_BITS);
+ }
+ }
+}
+
+void
+mem_buddy_free_range(uintptr_t base, size_t length)
+{
+ for(int l = 0; l < BUDDY_LAYERS; l++) {
+ size_t bits = (length / PAGESIZE) >> l;
+ size_t biti = (base / PAGESIZE) >> l;
+ size_t bitbase = (biti * PAGESIZE) << l;
+
+ if(bits == 0) continue;
+ for(size_t i = 0; i < bits; i++, bitbase += (PAGESIZE << l)) {
+ if(bitbase < base) continue;
+ size_t entry = (biti + i) / ENTRY_BITS;
+ s_buddies[l][entry] &= ~(1ULL << ((biti+ i) % ENTRY_BITS));
+ }
+ }
+}
+
+
+uintptr_t
+mem_buddy_takefree(size_t l)
+{
+ uintmax_t *layer = s_buddies[l];
+ size_t lastfree = s_buddies_lastfree[l];
+ if(s_buddy_test(l, lastfree)) lastfree = 0;
+ size_t entries = ENTRY_COUNT >> l;
+ for(size_t i = lastfree / ENTRY_BITS; i < entries; i++) {
+ uintmax_t entry = layer[i];
+ if(entry == (uintmax_t)-1LL) continue;
+ for(size_t j = 0; j < ENTRY_BITS; j++) {
+ if((entry & (1ULL << j)) == 0) {
+ size_t bit = (i * ENTRY_BITS) + j;
+ s_buddies_lastfree[l] = bit + 1;
+ s_buddy_set(l, bit);
+ return bit * (PAGESIZE << l);
+ }
+ }
+ }
+ return 0;
+}
+
+void
+mem_buddy_setup()
+{
+ memset(s_buddy_l0, 0xFF, sizeof(s_buddy_l0));
+ memset(s_buddy_l1, 0xFF, sizeof(s_buddy_l1));
+ memset(s_buddy_l2, 0xFF, sizeof(s_buddy_l2));
+ memset(s_buddy_l3, 0xFF, sizeof(s_buddy_l3));
+
+ for(int i = 0; i < boot_memorymap.count; i++) {
+ struct MemoryMapEntry *entry = &boot_memorymap.entries[i];
+ klogf("%2i\t%#016X -> %#016X (%i)\n",
+ i, entry->base, entry->base + entry->length, entry->usable);
+ if(entry->base > MEMMAP_BUDDY_LIMIT) continue;
+ size_t length = entry->length;
+ if(entry->base + length > MEMMAP_BUDDY_LIMIT) length = MEMMAP_BUDDY_LIMIT - (entry->base + length);
+ if(entry->usable)
+ mem_buddy_free_range(entry->base, entry->length);
+ }
+
+ s_buddies[0][0] |= 1;
+ s_buddies[1][0] |= 1;
+ s_buddies[2][0] |= 1;
+ s_buddies[3][0] |= 1;
+}
diff --git a/mem/buddymap.d b/mem/buddymap.d
new file mode 100644
index 0000000..7132cfe
--- /dev/null
+++ b/mem/buddymap.d
@@ -0,0 +1,2 @@
+mem/buddymap.o: mem/buddymap.c mem/buddymap.h mem/memory.h mem/slab.h \
+ lib/string.h boot/boot.h io/log.h
diff --git a/mem/buddymap.h b/mem/buddymap.h
new file mode 100644
index 0000000..2f4f5dc
--- /dev/null
+++ b/mem/buddymap.h
@@ -0,0 +1,21 @@
+#ifndef JOVE_MEMORY_BUDDYMAP_H
+#define JOVE_MEMORY_BUDDYMAP_H 1
+
+#include "memory.h"
+#include <stdint.h>
+#include <stddef.h>
+
+#define MEMMAP_BUDDY_LIMIT (4 * GiB)
+
+void mem_buddy_set_range(uintptr_t base, size_t length);
+void mem_buddy_free_range(uintptr_t base, size_t length);
+uintptr_t mem_buddy_takefree(size_t layer);
+
+#define mem_buddy_takefree_4k() mem_buddy_takefree(0)
+#define mem_buddy_takefree_8k() mem_buddy_takefree(1)
+#define mem_buddy_takefree_16k() mem_buddy_takefree(2)
+#define mem_buddy_takefree_32k() mem_buddy_takefree(3)
+
+void mem_buddy_setup(void);
+
+#endif
diff --git a/mem/memory.h b/mem/memory.h
new file mode 100644
index 0000000..eb30217
--- /dev/null
+++ b/mem/memory.h
@@ -0,0 +1,49 @@
+#ifndef JOVE_MEM_H
+#define JOVE_MEM_H 1
+
+#define PAGESIZE 4096ULL
+#define KiB 1024ULL
+#define MiB (KiB * KiB)
+#define GiB (MiB * KiB)
+#define TiB (GiB * KiB)
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+typedef uintptr_t physptr_t;
+
+#include "slab.h"
+
+/*Linear*/
+void mem_paging_setup(void);
+
+physptr_t mem_linear_tophys(uintptr_t virt);
+
+/**Check if pointer is within valid memory.
+ * @param ptr pointer to check.
+ * @return if the pointer is invalid.*/
+bool mem_check_ptr(const void *ptr);
+
+/**Make sure the range indicated is available in memory.
+ * If necessary, allocate new pages using the passed flags
+ * @param from start of the range.
+ * @param to end of the range.
+ * @param rw flag to mark page is writeable.
+ * @param user flag to mark page as user accessable*/
+void mem_ensure_range(uintptr_t from, uintptr_t to, bool rw, bool user);
+
+void mem_slab_setup(void);
+void mem_slabcache_new(struct SlabCache *cache, char *name, size_t objsize);
+
+void* mem_slab_alloc(struct SlabCache *cache);
+void mem_slab_free(struct SlabCache *cache, void *ptr);
+
+void* mem_alloc(size_t width);
+void mem_free(void *ptr);
+
+/*Physical*/
+
+physptr_t mem_phys_take4k(void);
+void mem_phys_reserve(physptr_t start, size_t len);
+
+#endif
diff --git a/mem/phys.c b/mem/phys.c
new file mode 100644
index 0000000..e56a4d6
--- /dev/null
+++ b/mem/phys.c
@@ -0,0 +1,14 @@
+#include "memory.h"
+#include "buddymap.h"
+
+physptr_t
+mem_phys_take4k(void)
+{
+ return mem_buddy_takefree_4k();
+}
+
+void
+mem_phys_reserve(physptr_t start, size_t len)
+{
+ mem_buddy_set_range(start, len);
+}
diff --git a/mem/phys.d b/mem/phys.d
new file mode 100644
index 0000000..192b714
--- /dev/null
+++ b/mem/phys.d
@@ -0,0 +1 @@
+mem/phys.o: mem/phys.c mem/memory.h mem/slab.h mem/buddymap.h
diff --git a/mem/slab.c b/mem/slab.c
new file mode 100644
index 0000000..75b8302
--- /dev/null
+++ b/mem/slab.c
@@ -0,0 +1,201 @@
+#include "slab.h"
+#include "memory.h"
+#include "lib/format.h"
+#include "lib/string.h"
+#include "lib/jove.h"
+#include "io/log.h"
+
+extern void *_kernel_end;
+
+static uintptr_t s_addr_next_free;
+
+#define GENERIC_CACHEC 8
+static struct SlabCache s_generic_caches[GENERIC_CACHEC];
+
+static uintptr_t
+s_next_free(size_t width)
+{
+ uintptr_t ret = s_addr_next_free;
+ s_addr_next_free += width;
+ mem_ensure_range(ret, s_addr_next_free, true, false);
+ return ret;
+}
+
+static int
+s_get_free_listw(size_t slabw, size_t objw)
+{
+ int freelistc = 1;
+ while(freelistc < 256) {
+ int maxobjc = (slabw - (freelistc * sizeof(uintptr_t))) / objw;
+ if(maxobjc <= freelistc) return maxobjc;
+ freelistc++;
+ }
+ return freelistc;
+}
+
+static struct SlabDescriptor
+*s_slab_new(struct SlabCache *cache, struct SlabDescriptor *last)
+{
+ size_t slab_width = (cache->slab_pages * PAGESIZE);
+ uintptr_t descr_base = s_next_free(slab_width);
+ struct SlabDescriptor *descr = (struct SlabDescriptor*)descr_base;
+
+ size_t free_listc = s_get_free_listw(
+ slab_width - sizeof(struct SlabDescriptor),
+ cache->obj_size);
+ size_t descriptor_width = sizeof(struct SlabDescriptor)
+ + (free_listc * sizeof(uintptr_t));
+ uintptr_t obj_base = descr_base + descriptor_width;
+
+ if(free_listc < 8) {
+ free_listc = ((slab_width - sizeof(struct SlabDescriptor)) / cache->obj_size);
+ descr = mem_alloc(sizeof(struct SlabDescriptor) + (free_listc * sizeof(uintptr_t)));
+ obj_base = descr_base;
+ }
+
+ *descr = (struct SlabDescriptor) {
+ .prev = last,
+ .next = (last == NULL ? NULL : last->next),
+ .slab_base = (void*)descr_base,
+ .obj_base = (void*)obj_base,
+ .free_count = free_listc,
+ .free_index = free_listc - 1
+ };
+ for(size_t i = 0; i < free_listc; i++) {
+ descr->free[i] = obj_base + (i * cache->obj_size);
+ }
+
+ return descr;
+}
+
+void
+mem_slabcache_new(struct SlabCache *cache, char *name, size_t objsize)
+{
+ if(objsize % 8 > 0) objsize += (8 - (objsize % 8));
+ size_t pages = objsize > 512 ? (objsize >> 9) : 1;
+ *cache = (struct SlabCache){
+ .obj_size = objsize,
+ .slab_pages = pages,
+ .list_free = NULL,
+ .list_partial = NULL,
+ .list_full = NULL
+ };
+ size_t namelen = strlen(name);
+ namelen = namelen > 32 ? 32 : namelen;
+ memcpy(cache->name, name, namelen);
+
+ //Allocate the first slab
+ cache->list_free = s_slab_new(cache, NULL);
+}
+
+void*
+mem_slab_alloc(struct SlabCache *cache)
+{
+ // Get a free slab
+ struct SlabDescriptor *slab = NULL;
+ if(cache->list_partial != NULL) slab = cache->list_partial;
+ if(slab == NULL && cache->list_free != NULL) {
+ slab = cache->list_free;
+ cache->list_free = slab->next;
+ }
+ if(slab == NULL) slab = s_slab_new(cache, cache->list_free);
+ cache->list_partial = slab;
+
+ // Take an object from the slab.
+ uintptr_t objaddr = slab->free[slab->free_index];
+ slab->free_index -= 1;
+
+ if(slab->free_index < 0) {
+ slab->next = cache->list_full;
+ cache->list_full = slab;
+ }
+ return (void*)objaddr;
+}
+
+void
+mem_slab_free(struct SlabCache *cache, void *ptr)
+{
+ uintptr_t addr = (uintptr_t)ptr;
+ //Look for the pointer in the bounds of every slab
+ for(struct SlabDescriptor *slab = cache->list_full;
+ slab != NULL; slab = slab->next)
+ {
+ uintptr_t base = (uintptr_t)slab->obj_base;
+ uintptr_t limit = ((uintptr_t)slab->slab_base)
+ + (cache->slab_pages * PAGESIZE);
+ if(addr > limit || addr < base) continue;
+ if((addr - base) % cache->obj_size != 0) {
+ klogf("Tried to free offset pointer %#016X in slab %s\n",
+ addr, cache->name);
+ return;
+ }
+ slab->free_index++;
+ slab->free[slab->free_index] = addr;
+
+ cache->list_full = slab->next;
+ slab->next = cache->list_partial;
+ cache->list_partial = slab;
+ return;
+ }
+ for(struct SlabDescriptor *slab = cache->list_partial;
+ slab != NULL; slab = slab->next)
+ {
+ uintptr_t base = (uintptr_t)slab->obj_base;
+ uintptr_t limit = ((uintptr_t)slab->slab_base)
+ + (cache->slab_pages * PAGESIZE);
+ if(addr > limit || addr < base) continue;
+ if((addr - base) % cache->obj_size != 0) {
+ klogf("Tried to free offset pointer %#016X in slab %s\n",
+ addr, cache->name);
+ return;
+ }
+ slab->free_index++;
+ slab->free[slab->free_index] = addr;
+
+ if(slab->free_index == (slab->free_count - 1)) {
+ cache->list_partial = slab->next;
+ slab->next = cache->list_free;
+ cache->list_free = slab;
+ }
+ return;
+ }
+}
+
+void*
+mem_alloc(size_t width)
+{
+ size_t width_log2 = (__builtin_clz(width) ^ 31) + 1;
+ if(width_log2 < 6) width_log2 = 6;
+ width_log2 -= 6;
+ if(width_log2 >= GENERIC_CACHEC) {
+ klogf("Allocation size %i too big for generic caches!\n", width);
+ return NULL;
+ }
+
+ struct SlabCache *generic_cache = &s_generic_caches[width_log2];
+ return mem_slab_alloc(generic_cache);
+}
+
+void
+mem_free(void *ptr)
+{
+ for(int i = 0; i < GENERIC_CACHEC; i++) {
+ mem_slab_free(&s_generic_caches[i], ptr);
+ }
+}
+
+void
+mem_slab_setup(void)
+{
+ s_addr_next_free = (uintptr_t)&_kernel_end;
+ s_addr_next_free = ((s_addr_next_free >> 12) + 1) << 12;
+ s_get_free_listw(PAGESIZE - sizeof(struct SlabDescriptor), 32);
+
+ for(int i = 0; i < GENERIC_CACHEC; i++)
+ {
+ size_t objsize = 1 << (i + 6);
+ char slab_name[SLABCACHE_NAME_LIMIT];
+ sfmt(slab_name, SLABCACHE_NAME_LIMIT, "generic_%i", 1 << (i + 6));
+ mem_slabcache_new(&s_generic_caches[i], slab_name, objsize);
+ }
+}
diff --git a/mem/slab.d b/mem/slab.d
new file mode 100644
index 0000000..5cffd4c
--- /dev/null
+++ b/mem/slab.d
@@ -0,0 +1,2 @@
+mem/slab.o: mem/slab.c mem/slab.h mem/memory.h lib/format.h lib/string.h \
+ lib/jove.h io/log.h
diff --git a/mem/slab.h b/mem/slab.h
new file mode 100644
index 0000000..074d278
--- /dev/null
+++ b/mem/slab.h
@@ -0,0 +1,33 @@
+#ifndef JOVE_MEMORY_SLAB_H
+#define JOVE_MEMORY_SLAB_H 1
+
+#include <stdint.h>
+#include <stddef.h>
+#include <stdbool.h>
+
+#define SLABCACHE_NAME_LIMIT 32
+struct SlabCache
+{
+ char name[SLABCACHE_NAME_LIMIT];
+
+ struct SlabDescriptor *list_free;
+ struct SlabDescriptor *list_partial;
+ struct SlabDescriptor *list_full;
+
+ size_t obj_size;
+ size_t slab_pages;
+};
+
+struct SlabDescriptor
+{
+ struct SlabDescriptor *prev;
+ struct SlabDescriptor *next;
+ void *slab_base;
+ void *obj_base;
+
+ size_t free_count;
+ int free_index;
+ uintptr_t free[];
+};
+
+#endif
diff --git a/tsk/tasking.h b/tsk/tasking.h
new file mode 100644
index 0000000..d217171
--- /dev/null
+++ b/tsk/tasking.h
@@ -0,0 +1,25 @@
+#ifndef JOVE_TASKING_H
+#define JOVE_TASKING_H 1
+
+#include <stddef.h>
+#include <stdint.h>
+
+typedef size_t tid_t;
+
+struct Thread
+{
+ struct Thread *next;
+ tid_t id;
+ uintptr_t kbp;
+ size_t perm;
+};
+
+extern struct Thread *thread_current;
+
+void tasking_setup(void);
+
+struct Thread *thread_new(struct Thread *parent);
+struct Thread *thread_get(tid_t id);
+void thread_perm_release(struct Thread *thread, size_t mask);
+
+#endif
diff --git a/tsk/thread.c b/tsk/thread.c
new file mode 100644
index 0000000..7fbafa6
--- /dev/null
+++ b/tsk/thread.c
@@ -0,0 +1,7 @@
+#include "tasking.h"
+
+void
+thread_perm_release(struct Thread *thread, size_t mask)
+{
+ thread->perm &= ~mask;
+}
diff --git a/tsk/thread.d b/tsk/thread.d
new file mode 100644
index 0000000..1869275
--- /dev/null
+++ b/tsk/thread.d
@@ -0,0 +1 @@
+tsk/thread.o: tsk/thread.c tsk/tasking.h
diff --git a/usr/elf.h b/usr/elf.h
new file mode 100644
index 0000000..d805e55
--- /dev/null
+++ b/usr/elf.h
@@ -0,0 +1,12 @@
+#ifndef JOVE_USER_ELF_H
+#define JOVE_USER_ELF_H 1
+
+#include <stddef.h>
+
+/**Load an ELF file into usermode memory.
+ * @param data pointer to ELF file data buffer
+ * @param len length of ELF file data buffer
+ * @return entry point for loaded ELF exec*/
+void *elf_load(const void *data, size_t len);
+
+#endif
diff --git a/usr/syscall.c b/usr/syscall.c
new file mode 100644
index 0000000..0ea5700
--- /dev/null
+++ b/usr/syscall.c
@@ -0,0 +1,23 @@
+#include "syscall.h"
+#include "mem/memory.h"
+#include "io/log.h"
+
+int _syscall_handler_log(struct syscall_log *req)
+{
+ klogf("Message ptr %#016X\n", req->message);
+ if(!mem_check_ptr(req->message)) return -1;
+ klogf("%s", req->message);
+ return 0;
+}
+
+void *_syscall_handlers[SYSCALL_COUNT] = {
+ _syscall_handler_log
+};
+
+int
+syscall_handler(syscall_t *req)
+{
+ if(!mem_check_ptr(req)) return -1;
+ if(req->id >= SYSCALL_COUNT) return -1;
+ return ((syscall_handler_t)(_syscall_handlers[req->id]))(req);
+}
diff --git a/usr/syscall.d b/usr/syscall.d
new file mode 100644
index 0000000..9d5acbf
--- /dev/null
+++ b/usr/syscall.d
@@ -0,0 +1,2 @@
+usr/syscall.o: usr/syscall.c usr/syscall.h abi/syscall.h mem/memory.h \
+ mem/slab.h io/log.h
diff --git a/usr/syscall.h b/usr/syscall.h
new file mode 100644
index 0000000..49beb85
--- /dev/null
+++ b/usr/syscall.h
@@ -0,0 +1,10 @@
+#ifndef JOVE_USER_SYSCALL_H
+#define JOVE_USER_SYSCALL_H 1
+
+#include "abi/syscall.h"
+
+typedef int (*syscall_handler_t)(syscall_t*);
+
+int _syscall_handler_log(struct syscall_log *req);
+
+#endif
diff --git a/usr/umode.c b/usr/umode.c
new file mode 100644
index 0000000..4ef5306
--- /dev/null
+++ b/usr/umode.c
@@ -0,0 +1,31 @@
+#include "umode.h"
+#include "elf.h"
+#include "boot/cmdline.h"
+#include "lib/jove.h"
+#include "ird/initrd.h"
+#include "mem/memory.h"
+
+void
+umode_setup(void)
+{
+ extern void syscall_setup_syscall(void);
+ syscall_setup_syscall();
+
+ const char *init_path = cmdline_get("init");
+ if(init_path == NULL)
+ kpanic("Missing path to init ELF file / binary\n");
+
+ struct InitrdFile *init_file = ird_getfile(init_path);
+ if(init_file == NULL)
+ kpanic("Missing init file %s in initrd\n", init_path);
+
+ void (*entry_point)(void) = elf_load(init_file->data, init_file->size);
+ if(entry_point == NULL)
+ kpanic("Init file %s is incorrectly formatted (want ELF64)\n", init_path);
+
+ void *user_stack = (void*)(0x00007FFFFFFFFFFF);
+ mem_ensure_range((uintptr_t)user_stack & ~0xFFF, (uintptr_t)user_stack, true, true);
+
+ klogf("User entry point %#016X\n", entry_point);
+ umode_enter(entry_point, user_stack);
+}
diff --git a/usr/umode.d b/usr/umode.d
new file mode 100644
index 0000000..54c004b
--- /dev/null
+++ b/usr/umode.d
@@ -0,0 +1,3 @@
+usr/umode.o: usr/umode.c usr/umode.h usr/elf.h boot/cmdline.h \
+ lib/hashtable.h lib/linkedlist.h lib/jove.h ird/initrd.h ird/tar.h \
+ lib/linkedlist.h mem/memory.h mem/slab.h
diff --git a/usr/umode.h b/usr/umode.h
new file mode 100644
index 0000000..2755abe
--- /dev/null
+++ b/usr/umode.h
@@ -0,0 +1,8 @@
+#ifndef JOVE_UMODE_H
+#define JOVE_UMODE_H 1
+
+void umode_setup(void);
+
+void umode_enter(void (*entry)(void), void *stack);
+
+#endif