summaryrefslogtreecommitdiffstats
path: root/arch/x86_64
diff options
context:
space:
mode:
authorJon Santmyer <jon@jonsantmyer.com>2024-03-11 21:30:31 -0400
committerJon Santmyer <jon@jonsantmyer.com>2024-03-11 21:30:31 -0400
commitd1ff7bcc91886626dc9060ec5fb67ee102ab7c1d (patch)
tree8f0b5cd8aad31089131785dc6e37b659490f9955 /arch/x86_64
downloadjove-kernel-d1ff7bcc91886626dc9060ec5fb67ee102ab7c1d.tar.gz
jove-kernel-d1ff7bcc91886626dc9060ec5fb67ee102ab7c1d.tar.bz2
jove-kernel-d1ff7bcc91886626dc9060ec5fb67ee102ab7c1d.zip
usermode capable kernel with logging syscall
Diffstat (limited to 'arch/x86_64')
-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
28 files changed, 1113 insertions, 0 deletions
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