summaryrefslogtreecommitdiff
path: root/kc
diff options
context:
space:
mode:
authorAda Christine <adachristine18@gmail.com>2021-12-19 22:41:15 +0000
committerAda Christine <adachristine18@gmail.com>2021-12-19 22:41:15 +0000
commite303a03e7c96bc5bf7fa93f7b46ce63196893d7a (patch)
treef187b374feec85015d6df1f94db618a3f5cc893f /kc
parentbff63a4337eb3388617d230970c5eb2684d6a215 (diff)
moving files around
Diffstat (limited to 'kc')
-rw-r--r--kc/boot/Makefile37
-rw-r--r--kc/boot/kc_main.c73
-rw-r--r--kc/component.ld75
-rw-r--r--kc/core/Makefile39
-rw-r--r--kc/core/cpu.c225
-rw-r--r--kc/core/cpu.h9
-rw-r--r--kc/core/exceptions.S54
-rw-r--r--kc/core/exceptions.h7
-rw-r--r--kc/core/kc_main.c69
-rw-r--r--kc/core/kprint.c18
-rw-r--r--kc/core/kprint.h5
-rw-r--r--kc/core/main.c4
-rw-r--r--kc/core/memory.c534
-rw-r--r--kc/core/memory.h53
-rw-r--r--kc/core/panic.c25
-rw-r--r--kc/core/panic.h13
-rw-r--r--kc/core/port.c14
-rw-r--r--kc/core/port.h5
-rw-r--r--kc/core/serial.c41
-rw-r--r--kc/core/serial.h5
-rw-r--r--kc/entry_x86_64.S18
-rw-r--r--kc/reloc_x86_64.c69
22 files changed, 1392 insertions, 0 deletions
diff --git a/kc/boot/Makefile b/kc/boot/Makefile
new file mode 100644
index 0000000..76bd4d4
--- /dev/null
+++ b/kc/boot/Makefile
@@ -0,0 +1,37 @@
+include ../../defaults.mk
+
+VPATH := ../../lib ../
+
+TARGET := efi.os
+.DEFAULT: $(TARGET)
+
+# EFI API for temporary things
+
+EFIAPIDIR := /usr/local/include/efi
+
+CPPFLAGS += -I../../api -I$(EFIAPIDIR) -I$(EFIAPIDIR)/$(ARCH) -I$(EFIAPIDIR)/protocol
+CFLAGS += -Wno-unused-const-variable -Wno-unused-function -fvisibility=hidden \
+ -g -fshort-wchar -mgeneral-regs-only
+
+LIBDIRS += -L$(dir $(shell $(CC) -print-libgcc-file-name))
+LDSCRIPT := ../component.ld
+LDFLAGS += -z max-page-size=4096 --export-dynamic -pic --no-dynamic-linker
+LOADLIBES := -lgcc
+
+OBJS := entry_x86_64.o reloc_x86_64.o kc_main.o elf64.o memcmp.o memcpy.o \
+ memmove.o memset.o
+$(OBJS): CFLAGS += -fPIC
+
+DEPS := $(patsubst %.o,%.d,$(OBJS))
+
+$(TARGET): $(LDSCRIPT) $(MAKEFILE_LIST)
+$(TARGET): $(OBJS)
+
+CLEANLIST := $(wildcard $(OBJS) $(TARGET))
+DCLEANLIST := $(wildcard $(DEPS))
+
+include ../../rules.mk
+-include $(DEPS)
+
+.DEFAULT: $(TARGET)
+
diff --git a/kc/boot/kc_main.c b/kc/boot/kc_main.c
new file mode 100644
index 0000000..1829616
--- /dev/null
+++ b/kc/boot/kc_main.c
@@ -0,0 +1,73 @@
+#include <loader/efi/shim.h>
+#include <kernel/memory/range.h>
+
+#include <stdbool.h>
+
+#include "../lib/elf.h"
+
+struct efi_memory_map
+{
+ struct memory_range buffer;
+ UINTN size;
+ UINTN key;
+ UINTN descsize;
+ UINT32 descver;
+};
+
+struct efi_video_data
+{
+ struct memory_range buffer;
+ UINT32 width;
+ UINT32 height;
+ UINT32 pitch;
+ EFI_GRAPHICS_PIXEL_FORMAT format;
+ EFI_PIXEL_BITMASK mask;
+};
+
+struct efi_boot_data
+{
+ struct efi_memory_map memory_map;
+ struct efi_video_data video_data;
+ void *acpi_rsdp;
+ unsigned long acpi_version;
+};
+
+struct efi_loader_image kernel_image =
+{
+ .path = L"\\adasoft\\sophia\\kernel.os"
+};
+
+static struct efi_boot_data boot_data;
+
+// TODO: split relocation code out into library
+
+EFI_STATUS kc_main(struct efi_loader_interface *interface)
+{
+ // 1. load kernel and boot modules into memory
+ // 2. generate page tables for kernel and boot modules
+ // 2. gather boot data, store as type SystemMemoryType
+ // 3. exit boot services
+ // 5. ???????????
+
+ EFI_STATUS status;
+
+ if (!interface)
+ {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!EFI_ERROR((status = interface->image_open(&kernel_image))) &&
+ !EFI_ERROR((status = interface->image_alloc(&kernel_image))) &&
+ !EFI_ERROR((status = interface->image_load(&kernel_image))))
+ {
+ (void)boot_data;
+ }
+
+ __asm__ volatile (
+ "cli\n\t"
+ "hlt\n\t"
+ );
+
+ return status;
+}
+
diff --git a/kc/component.ld b/kc/component.ld
new file mode 100644
index 0000000..3a48ae8
--- /dev/null
+++ b/kc/component.ld
@@ -0,0 +1,75 @@
+ENTRY(kc_entry)
+
+SECTIONS
+{
+ PROVIDE(kc_image_base = .);
+
+ . = sizeof_headers;
+
+ .rel :
+ {
+ *(.rel)
+ *(.rel.*)
+ }
+
+ .rela :
+ {
+ *(.rel)
+ *(.rela.*)
+ }
+
+ .hash :
+ {
+ *(.hash)
+ }
+
+ .dynsym :
+ {
+ *(.dynsym)
+ }
+
+ .dynstr :
+ {
+ *(.dynstr)
+ }
+
+ .text : ALIGN(4K)
+ {
+ PROVIDE(kc_text_begin = .);
+ *(.text.start)
+ *(.text)
+ . = ALIGN(4K);
+ PROVIDE(kc_text_end = .);
+ } =0xcc
+
+ .dynamic : ALIGN(4K)
+ {
+ *(.dynamic)
+ }
+
+ .data :
+ {
+ PROVIDE(kc_data_begin = .);
+ *(.rodata)
+ *(.data)
+ }
+
+ .data.rel :
+ {
+ *(.data.rel)
+ *(.data.rel.*)
+ }
+
+ .bss :
+ {
+ *(.bss)
+ PROVIDE(kc_data_end = .);
+ }
+
+ /DISCARD/ :
+ {
+ *(.eh_frame)
+ *(.comment)
+ }
+}
+
diff --git a/kc/core/Makefile b/kc/core/Makefile
new file mode 100644
index 0000000..2893b42
--- /dev/null
+++ b/kc/core/Makefile
@@ -0,0 +1,39 @@
+include ../../defaults.mk
+
+VPATH := ../../lib ../
+
+TARGET := kernel.os
+.DEFAULT: $(TARGET)
+
+CPPFLAGS += -I../../api
+CFLAGS += -Wno-unused-const-variable -Wno-unused-function -fvisibility=hidden -g
+
+LIBDIRS += -L$(dir $(shell $(CC) -print-libgcc-file-name))
+LDSCRIPT := ../component.ld
+LDFLAGS += -z max-page-size=4096 --export-dynamic -pic --no-dynamic-linker
+LOADLIBES := -lgcc
+
+SOBJS :=
+GOBJS := entry_x86_64.o reloc_x86_64.o kprint.o serial.o port.o \
+ kc_main.o memory.o memset.o memcpy.o memmove.o memcmp.o cpu.o \
+ exceptions.o panic.o
+IOBJS :=
+
+# __attribute__((interrupt)) requires -mgeneral-regs-only
+$(IOBJS): CFLAGS += -mgeneral-regs-only
+$(IOBJS) $(GOBJS): CFLAGS += -mcmodel=small -fPIC
+
+OBJS := $(SOBJS) $(GOBJS) $(IOBJS)
+DEPS := $(patsubst %.o,%.d,$(OBJS))
+
+$(TARGET): $(LDSCRIPT) $(MAKEFILE_LIST)
+$(TARGET): $(OBJS)
+
+CLEANLIST := $(wildcard $(OBJS) $(TARGET))
+DCLEANLIST := $(wildcard $(DEPS))
+
+include ../../rules.mk
+-include $(DEPS)
+
+.DEFAULT: $(TARGET)
+
diff --git a/kc/core/cpu.c b/kc/core/cpu.c
new file mode 100644
index 0000000..ed1a04e
--- /dev/null
+++ b/kc/core/cpu.c
@@ -0,0 +1,225 @@
+#include "cpu.h"
+#include "exceptions.h"
+
+#include <stdint.h>
+
+#define GDT_ENTRIES 16
+#define IDT_ENTRIES 256
+
+#define CODE_SEG_INDEX 1
+#define DATA_SEG_INDEX 2
+#define TASK_SEG_INDEX 3
+
+struct dtr64
+{
+ uint16_t limit;
+ uint64_t base;
+} __attribute__((packed));
+
+struct segment_descriptor
+{
+ uint16_t limit0;
+ uint16_t base0;
+ uint8_t base16;
+ uint8_t access;
+ uint8_t flags_limit16;
+ uint8_t base24;
+} __attribute__((packed));
+
+enum segment_descriptor_type
+{
+ CODE64_SEG,
+ DATA_SEG,
+ TASK64_SEG,
+};
+
+struct gate_descriptor
+{
+ uint16_t offset0;
+ uint16_t selector;
+ uint8_t ist;
+ uint8_t access;
+ uint16_t offset16;
+ uint64_t offset32;
+} __attribute__((packed));
+
+enum gate_descriptor_type
+{
+ INT64_GATE,
+ TRAP64_GATE,
+};
+
+struct task64_segment
+{
+ uint32_t reserved0;
+ uint64_t rsp0;
+ uint64_t rsp1;
+ uint64_t rsp2;
+ uint64_t reserved1;
+ uint64_t ist[7];
+ uint16_t reserved2;
+ uint16_t iomap_base;
+};
+
+static struct segment_descriptor gdt[GDT_ENTRIES];
+static struct gate_descriptor idt[IDT_ENTRIES];
+static struct task64_segment tss;
+
+static void set_gdt(int index,
+ uint64_t base,
+ uint64_t limit,
+ enum segment_descriptor_type type)
+{
+ gdt[index].limit0 = limit & 0xffff;
+ gdt[index].base0 = base & 0xffff;
+ gdt[index].base16 = (base >> 16) & 0xff;
+ gdt[index].flags_limit16 = (limit >> 16) & 0xf;
+ gdt[index].base24 = (base >> 24) & 0xff;
+
+ switch (type)
+ {
+ case CODE64_SEG:
+ gdt[index].access = 0x9a;
+ gdt[index].flags_limit16 |= 0xa0;
+ break;
+ case DATA_SEG:
+ gdt[index].access = 0x92;
+ gdt[index].flags_limit16 |= 0xc0;
+ break;
+ case TASK64_SEG:
+ gdt[index].access = 0x89;
+ //TODO: make this cleaner i don't like it
+ gdt[index+1].limit0 = (base >> 32) & 0xffff;
+ gdt[index+1].base0 = (base >> 48) & 0xffff;
+ break;
+ };
+}
+
+static void set_idt(int vec,
+ uint16_t selector,
+ uint64_t offset,
+ enum gate_descriptor_type type)
+{
+ idt[vec].offset0 = offset & 0xffff;
+ idt[vec].selector = selector;
+ idt[vec].offset16 = (offset >> 16) & 0xffff;
+ idt[vec].offset32 = (offset >> 32) & 0xffffffff;
+
+ switch (type)
+ {
+ case INT64_GATE:
+ idt[vec].access = 0x8e;
+ break;
+ case TRAP64_GATE:
+ idt[vec].access = 0x8f;
+ break;
+ }
+}
+
+void set_ist(int vec, int ist_index)
+{
+ // non-destructively set the ist index.
+ idt[vec].access |= ist_index & 0x7;
+}
+
+static void gdt_init(void)
+{
+ set_gdt(CODE_SEG_INDEX, 0, (uint64_t)-1, CODE64_SEG);
+ set_gdt(DATA_SEG_INDEX, 0, (uint64_t)-1, DATA_SEG);
+ set_gdt(TASK_SEG_INDEX, (uint64_t)&tss, sizeof(tss) - 1, TASK64_SEG);
+
+ struct dtr64 gdtr = {sizeof(gdt) - 1, (uint64_t)&gdt};
+
+ __asm__ volatile
+ (
+ // can't have interrupts during GDT reload
+ // but don't re-enable interrupts if they were already disabled
+ "pushf\n"
+ "cli\n"
+ "lgdt %0\n"
+ "mov %w2, %%ds\n"
+ "mov %w2, %%es\n"
+ "mov %w2, %%fs\n"
+ "mov %w2, %%gs\n"
+ "mov %w2, %%ss\n"
+ // make a far return frame on the stack
+ // [ss] @rsp+8
+ // [rip] @rsp
+ "pushq %1\n"
+ "lea .Lflush(%%rip), %%rax\n"
+ "push %%rax\n"
+ // far return, now we're in the code segment
+ // defined here.
+ "lretq\n"
+ ".Lflush:\n"
+ "ltr %w3\n"
+ "popf\n"
+ // dependency on pre-boot GDT is now gone
+ :
+ : "m"(gdtr),
+ "i"(CODE_SEG_INDEX << 3),
+ "r"(DATA_SEG_INDEX << 3),
+ "r"(TASK_SEG_INDEX << 3)
+ );
+}
+
+static void idt_init(void)
+{
+ struct dtr64 idtr = {sizeof(idt) - 1, (uint64_t)&idt};
+
+ __asm__ volatile
+ (
+ // can't have interrupts enabled during IDT reload--obviously.
+ // as with GDT don't re-enable interrupts if they were already
+ // disabled.
+ "pushf\n"
+ "cli\n"
+ "lidt %0\n"
+ "popf\n"
+ // much more straightforward than the gdt code.
+ :: "m"(idtr)
+ );
+}
+
+void cpu_init(void)
+{
+ gdt_init();
+ idt_init();
+ exceptions_init();
+}
+
+void exceptions_init(void)
+{
+ isr_install(PAGE_FAULT_VECTOR, &page_fault_isr, 0, 0);
+}
+
+void isr_install(int vec, void (*isr)(void), int trap, int ist)
+{
+ enum gate_descriptor_type type = INT64_GATE;
+
+ if (trap)
+ {
+ type = TRAP64_GATE;
+ }
+
+ set_idt(vec, CODE_SEG_INDEX << 3, (uint64_t)isr, type);
+ if (ist)
+ {
+ set_ist(vec, ist);
+ }
+}
+
+void ist_install(int ist, void *stack)
+{
+ tss.ist[ist] = (uint64_t)stack;
+}
+
+void int_enable(void)
+{
+ __asm__ ("sti");
+}
+
+void int_disable(void)
+{
+ __asm__ ("cli");
+}
diff --git a/kc/core/cpu.h b/kc/core/cpu.h
new file mode 100644
index 0000000..56a4073
--- /dev/null
+++ b/kc/core/cpu.h
@@ -0,0 +1,9 @@
+#pragma once
+
+void cpu_init(void);
+
+void isr_install(int vec, void (*isr)(void), int ist, int trap);
+void ist_install(int ist, void *stack);
+
+void int_enable(void);
+void int_disable(void);
diff --git a/kc/core/exceptions.S b/kc/core/exceptions.S
new file mode 100644
index 0000000..80d49ee
--- /dev/null
+++ b/kc/core/exceptions.S
@@ -0,0 +1,54 @@
+.extern page_fault_handler
+
+.global page_fault_isr
+.type page_fault_isr, @function
+page_fault_isr:
+ /* just in case something tried to be clever before */
+ cld
+ /* push param registers */
+ push %rdi
+ push %rsi
+ push %rdx
+ push %rcx
+ push %r8
+ push %r9
+ /* collect parameters for page fault handler */
+ mov 48(%rsp), %rdi /* error code is first parameter */
+ mov %cr2, %rsi /* PFLA is next parameter */
+ /* caller-saved registers */
+ push %rax
+ push %r10
+ push %r11
+ /* callee-saved registers */
+ push %rbx
+ push %rbp
+ push %r12
+ push %r13
+ push %r14
+ push %r15
+ /* stack has to be aligned */
+ mov %rsp, %rbp
+ and $-16, %rsp
+ call page_fault_handler
+ mov %rbp, %rsp
+ /* TODO: test for error code here and panic() */
+ pop %r15
+ pop %r14
+ pop %r13
+ pop %r12
+ pop %rbp
+ pop %rbx
+ pop %r11
+ pop %r10
+ pop %rax
+ pop %r9
+ pop %r8
+ pop %rcx
+ pop %rdx
+ pop %rsi
+ pop %rdi
+ /* unwind the error code from the stack */
+ add $8, %rsp
+ /* leave the isr */
+ iretq
+.size page_fault_isr, . - page_fault_isr
diff --git a/kc/core/exceptions.h b/kc/core/exceptions.h
new file mode 100644
index 0000000..5d22284
--- /dev/null
+++ b/kc/core/exceptions.h
@@ -0,0 +1,7 @@
+#pragma once
+
+extern void page_fault_isr(void);
+
+#define PAGE_FAULT_VECTOR 0xe
+
+void exceptions_init(void);
diff --git a/kc/core/kc_main.c b/kc/core/kc_main.c
new file mode 100644
index 0000000..5eecaf2
--- /dev/null
+++ b/kc/core/kc_main.c
@@ -0,0 +1,69 @@
+#include "cpu.h"
+#include "serial.h"
+#include "kprint.h"
+#include "memory.h"
+#include "panic.h"
+
+#include <loader/data_efi.h>
+
+#include <kernel/entry.h>
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdnoreturn.h>
+
+static void efi_memory_init(struct efi_memory_map_data *map)
+{
+ // FIXME: bad girl shit: overwriting on aliased pointers.
+ // this is a temporary solution to shortcomings with
+ // loader boot data. it's sort-of OK because the memory_range struct
+ // is smaller than EFI_MEMORY_DESCRIPTOR. it's still not good.
+ struct memory_range *ranges = (struct memory_range *)map->data;
+ int entries = map->size / map->descsize;
+
+ for (int i = 0; i < entries; i++)
+ {
+ EFI_MEMORY_DESCRIPTOR *desc;
+ desc = (EFI_MEMORY_DESCRIPTOR *)((char *)map->data + i * map->descsize);
+
+ enum memory_range_type type;
+ phys_addr_t base;
+ size_t size;
+
+ base = desc->PhysicalStart;
+ size = desc->NumberOfPages * EFI_PAGE_SIZE;
+
+ switch (desc->Type)
+ {
+ case EfiConventionalMemory:
+ case EfiBootServicesData:
+ case EfiBootServicesCode:
+ case EfiLoaderData:
+ case EfiLoaderCode:
+ type = AVAILABLE_MEMORY;
+ break;
+ case SystemMemoryType:
+ type = SYSTEM_MEMORY;
+ break;
+ default:
+ type = RESERVED_MEMORY;
+ }
+
+ ranges[i].type = type;
+ ranges[i].base = base;
+ ranges[i].size = size;
+ }
+
+ memory_init(ranges, entries);
+}
+
+noreturn void kernel_entry(struct efi_boot_data *data)
+{
+ (void)data;
+ cpu_init();
+ serial_init();
+ kputs("<hacker voice> i'm in\r\n");
+ efi_memory_init(data->memory_map);
+ halt();
+}
diff --git a/kc/core/kprint.c b/kc/core/kprint.c
new file mode 100644
index 0000000..0a15efd
--- /dev/null
+++ b/kc/core/kprint.c
@@ -0,0 +1,18 @@
+#include "kprint.h"
+#include "serial.h"
+
+int kputchar(int c)
+{
+ return serial_putchar(c);
+}
+
+int kputs(const char *s)
+{
+ while (*s)
+ {
+ kputchar(*s++);
+ }
+
+ return 0;
+}
+
diff --git a/kc/core/kprint.h b/kc/core/kprint.h
new file mode 100644
index 0000000..5b307a2
--- /dev/null
+++ b/kc/core/kprint.h
@@ -0,0 +1,5 @@
+#pragma once
+
+int kputchar(int c);
+int kputs(const char *s);
+
diff --git a/kc/core/main.c b/kc/core/main.c
new file mode 100644
index 0000000..2fb2e3e
--- /dev/null
+++ b/kc/core/main.c
@@ -0,0 +1,4 @@
+void kernel_main(void)
+{
+}
+
diff --git a/kc/core/memory.c b/kc/core/memory.c
new file mode 100644
index 0000000..62f3a38
--- /dev/null
+++ b/kc/core/memory.c
@@ -0,0 +1,534 @@
+#include "memory.h"
+#include "kprint.h"
+#include "panic.h"
+
+#include <stdint.h>
+
+#include <kernel/entry.h>
+#include <kernel/memory/paging.h>
+
+/* kernel virtual space guarantees
+ *
+ * the loader must set up the address space as follows
+ * 1. kernel virtual space is 2GiB in size on 2GiB alignment.
+ * 2. mappings begin at +0x7fc00000. this mapping space is sparse and its
+ * own mapping tables begin at +0x7fffe000.
+ */
+
+#define kpm1_index(v) (((uint64_t)v >> pte_index_bits(1)) & 0x7ffff)
+#define kpm2_index(v) (((uint64_t)v >> pte_index_bits(2)) & 0x3ff)
+
+#define align_next(v, a) (((uint64_t)v + a - 1) & ~(a - 1))
+
+// a conservative 128MB for kernel heap
+#define KERNEL_HEAP_SPACE_SIZE (128 << 20)
+
+typedef int (*page_fault_handler_func)(uint32_t code, void *address);
+
+enum memory_space_flags
+{
+ SYSTEM_MEMORY_SPACE, // memory that cannot fault
+ ANONYMOUS_MEMORY_SPACE, // memory that can fault
+};
+
+struct memory_space
+{
+ enum memory_space_flags flags;
+ page_fault_handler_func handler;
+ void *base;
+ void *head;
+ struct memory_space *next;
+ struct memory_space *prev;
+};
+
+struct page
+{
+ uint32_t next: 31;
+ uint32_t used: 1;
+};
+
+extern char k_virt_base;
+extern char k_text_begin;
+extern char k_text_end;
+extern char k_data_begin;
+extern char k_data_end;
+
+static struct page *const page_array = (struct page *)0xffffffd800000000;
+static int first_free_page_index = -1;
+static size_t page_array_entries = 0;
+static size_t free_pages = 0;
+
+// the temporary mapping place. never use this permanently.
+static void *const temp = (void *)0xffffffffffa00000;
+static uint64_t *const kernel_pm1 = (uint64_t *)0xffffffffffc00000;
+static uint64_t *const kernel_pm2 = (uint64_t *)0xffffffffffffe000;
+
+static uint64_t *get_kernel_pm1e(void *vaddr);
+static uint64_t *get_kernel_pm2e(void *vaddr);
+static void *get_virtual_page(enum page_map_flags flags);
+static void *page_map_at(void *vaddr, phys_addr_t paddr, enum page_map_flags flags);
+
+// handlers for memory space types
+int anonymous_page_handler(uint32_t code, void *address);
+
+// the system memory_space's that are always present
+static struct memory_space kernel_image_space;
+static struct memory_space kernel_stack_space;
+static struct memory_space kernel_pagemap_space;
+// the kernel's own heap space
+static struct memory_space kernel_heap_space;
+
+// NULL if the system address spaces are not set up
+static struct memory_space *root_memory_space;
+
+static struct memory_range init_grab_pages(struct memory_range *ranges,
+ int count,
+ size_t size)
+{
+ struct memory_range request = {SYSTEM_MEMORY,
+ 0,
+ align_next(size, PAGE_SIZE)};
+
+ for (int i = 0; i < count; i++)
+ {
+ if (ranges[i].type != AVAILABLE_MEMORY ||
+ ranges[i].base < (1 << 20)) // leave pages below 1MiB alone.
+ {
+ continue;
+ }
+ else if (ranges[i].size > request.size)
+ {
+ request.base = ranges[i].base;
+ ranges[i].base += request.size;
+ ranges[i].size -= request.size;
+ return request;
+ }
+ }
+
+ request.type = INVALID_MEMORY;
+ return request;
+}
+
+static size_t init_get_max_paddr(struct memory_range *ranges, int count)
+{
+ phys_addr_t max_paddr = 0;
+
+ // get the highest usable physical address in all memory ranges.
+ for (int i = 0; i < count; i++)
+ {
+ if (ranges[i].type != AVAILABLE_MEMORY)
+ {
+ continue;
+ }
+
+ if ((ranges[i].base + ranges[i].size - 1) > max_paddr)
+ {
+ max_paddr = ranges[i].base + ranges[i].size - 1;
+ }
+ }
+
+ return max_paddr;
+}
+
+static phys_addr_t get_kernel_pm4_phys(void)
+{
+ phys_addr_t pm4_phys;
+ __asm__ (
+ "mov %%cr3, %0\n\t"
+ : "=r"(pm4_phys)
+ );
+
+ return page_address(pm4_phys, 1);
+}
+
+static phys_addr_t get_kernel_pm3_phys(void)
+{
+ phys_addr_t pm3_phys;
+ // make a temporary mapping to read pm4
+ uint64_t *pm4 = page_map_at(temp,
+ get_kernel_pm4_phys(),
+ CONTENT_RODATA|SIZE_2M);
+
+ // pm3_phys is in pm4.
+ pm3_phys = page_address(pm4[pte_index(&k_virt_base, 4)], 1);
+ // never leave a temporary mapping
+ page_unmap(pm4);
+
+ return pm3_phys;
+}
+
+static void init_map_page_array_pm2(struct memory_range *pm2_pages)
+{
+ // now we need to write the physical address of each pm2 page for the
+ // page_array in the kernel's pm3.
+ uint64_t *pm3 = page_map_at(temp,
+ get_kernel_pm3_phys(),
+ CONTENT_RWDATA|SIZE_2M);
+
+ // the first index is NOT zero!
+ for (size_t i = 0; i < (pm2_pages->size / PAGE_SIZE); i++)
+ {
+ pm3[pte_index(page_array, 3) + i] = (pm2_pages->base + i *
+ PAGE_SIZE)|PAGE_NX|PAGE_WR|PAGE_PR;
+ }
+ // never forget to unmap temporary mappings.
+ page_unmap(pm3);
+}
+
+static void init_create_page_array_map(struct memory_range *pages,
+ struct memory_range *maps)
+{
+ // indices for page_array always start at 0
+ size_t entry_count = pages->size / PAGE_SIZE;
+
+ // every 512 entries we need to re-map and clean.
+ for (size_t i = 0; i < entry_count; i += PAGE_TABLE_INDEX_MASK)
+ {
+ uint64_t *pm1 = page_map_at(temp,
+ maps->base + i * PAGE_SIZE,
+ CONTENT_RWDATA|SIZE_2M);
+ memset(pm1, 0, PAGE_SIZE);
+ for (size_t j = 0;
+ (j < PAGE_TABLE_INDEX_MASK) && ((j + i) < entry_count);
+ j++)
+ {
+ pm1[j] = (pages->base + (i + j) * PAGE_SIZE)|
+ PAGE_NX|PAGE_WR|PAGE_PR;
+ }
+
+ page_unmap(pm1);
+ }
+}
+
+static void init_set_memory_range(struct memory_range *range)
+{
+ for (size_t i = 0; i < range->size / PAGE_SIZE; i++)
+ {
+ if (range->base / PAGE_SIZE + i > page_array_entries)
+ {
+ return;
+ }
+
+ if (range->type == AVAILABLE_MEMORY)
+ {
+ page_free(range->base + PAGE_SIZE * i);
+ }
+ else
+ {
+ struct page *page = &page_array[range->base / PAGE_SIZE + i];
+ page->used = 1;
+ }
+ }
+}
+
+static void init_populate_page_array(struct memory_range *ranges, int count)
+{
+ for (int i = 0; i < count; i++)
+ {
+ init_set_memory_range(&ranges[i]);
+ }
+}
+
+static void init_create_page_array(struct memory_range *ranges, int count)
+{
+ // the highest actual physical memory address.
+ size_t max_paddr = init_get_max_paddr(ranges, count);
+ page_array_entries = max_paddr / page_size(1);
+ size_t page_array_size = page_array_entries * sizeof(struct page);
+
+ // the physical pages that will contain page_array
+ struct memory_range pa_pages = init_grab_pages(ranges, count, page_array_size);
+
+ // the number of page tables needed to map pa_pages
+ // this will be 1 on systems with less than 2GB of memory.
+ size_t pm1_count = page_count(pa_pages.size, 2);
+ struct memory_range pm1_pages = init_grab_pages(ranges,
+ count,
+ pm1_count * page_size(1));
+
+ init_create_page_array_map(&pa_pages, &pm1_pages);
+
+ // the number of page directories needed to map pa_pages
+ // this will be 1 on systems with less than 1TB of memory.
+ // who the fuck has 1TB of memory lol
+ size_t pm2_count = page_count(pa_pages.size, 3);
+ struct memory_range pm2_pages = init_grab_pages(ranges,
+ count,
+ pm2_count * page_size(1));
+
+ init_create_page_array_map(&pm1_pages, &pm2_pages);
+
+ // ok here goes
+ init_map_page_array_pm2(&pm2_pages);
+
+ memset(page_array, 0, page_array_size);
+
+ init_populate_page_array(ranges, count);
+ init_populate_page_array(&pa_pages, 1);
+ init_populate_page_array(&pm1_pages, 1);
+ init_populate_page_array(&pm2_pages, 1);
+}
+
+static struct memory_space init_system_space(void *base, void *head)
+{
+ struct memory_space space = {SYSTEM_MEMORY_SPACE, NULL, base, head, NULL, NULL};
+ return space;
+}
+
+static struct memory_space init_anonymous_space(void *base, void *head)
+{
+ struct memory_space space = {ANONYMOUS_MEMORY_SPACE,
+ &anonymous_page_handler,
+ base,
+ head,
+ NULL,
+ NULL};
+
+ return space;
+}
+
+void memory_init(struct memory_range *ranges, int count)
+{
+ init_create_page_array(ranges, count);
+ // initialize always-present memory spaces for the vmm
+ kernel_image_space = init_system_space((void *)&k_text_begin,
+ (void *)align_next(&k_data_end, PAGE_SIZE));
+ kernel_stack_space = init_system_space((void *)KERNEL_ENTRY_STACK_BASE,
+ (void *)KERNEL_ENTRY_STACK_HEAD);
+ kernel_pagemap_space = init_system_space(kernel_pm1, (void *)-1LL);
+
+ // initialize the kernel heap space
+ kernel_heap_space = init_anonymous_space(kernel_image_space.head,
+ (char *)kernel_image_space.head +
+ KERNEL_HEAP_SPACE_SIZE);
+
+ // set up the list links
+ kernel_image_space.next = &kernel_heap_space;
+ kernel_heap_space.prev = &kernel_image_space;
+ kernel_heap_space.next = &kernel_stack_space;
+ kernel_stack_space.prev = &kernel_heap_space;
+ kernel_stack_space.next = &kernel_pagemap_space;
+ kernel_pagemap_space.prev = &kernel_stack_space;
+
+ root_memory_space = &kernel_image_space;
+}
+
+static void *page_map_at(void *vaddr,
+ phys_addr_t paddr,
+ enum page_map_flags flags)
+{
+ uint64_t entry = PAGE_PR;
+
+ switch (flags & CONTENT_MASK)
+ {
+ case CONTENT_RODATA:
+ entry |= PAGE_NX;
+ break;
+ case CONTENT_RWDATA:
+ entry |= PAGE_NX|PAGE_WR;
+ break;
+ default:
+ break;
+ }
+
+ size_t offset;
+ uint64_t *pte;
+
+ switch (flags & SIZE_MASK)
+ {
+ case SIZE_2M:
+ entry |= page_address(paddr, 2)|PAGE_LG;
+ offset = page_offset(paddr, 2);
+ pte = get_kernel_pm2e(vaddr);
+ break;
+ case SIZE_4K:
+ entry |= page_address(paddr, 1);
+ offset = page_offset(paddr, 1);
+ pte = get_kernel_pm1e(vaddr);
+ break;
+ default:
+ offset = 0;
+ pte = NULL;
+ }
+
+ if (!pte)
+ {
+ return NULL;
+ }
+
+ *pte = entry;
+ return (char *)vaddr + offset;
+}
+
+void *page_map(phys_addr_t paddr, enum page_map_flags flags)
+{
+ return page_map_at(get_virtual_page(flags), paddr, flags);
+}
+
+void page_unmap(void *vaddr)
+{
+ uint64_t *pte = get_kernel_pm2e(vaddr);
+
+ if (pte && !(*pte & PAGE_LG))
+ {
+ pte = get_kernel_pm1e(vaddr);
+ }
+
+ if (pte)
+ {
+ *pte = 0;
+ }
+ __asm__ ("invlpg (%0)" :: "r"(vaddr));
+}
+
+phys_addr_t page_alloc(void)
+{
+ int index = first_free_page_index;
+
+ if (index > 0)
+ {
+ page_array[index].used = 1;
+ first_free_page_index = page_array[index].next;
+ free_pages--;
+ }
+
+ return (phys_addr_t)index * page_size(1);
+}
+
+void page_free(phys_addr_t paddr)
+{
+ int index = paddr / page_size(1);
+
+ if ((size_t)index > page_array_entries)
+ {
+ return;
+ }
+
+ page_array[index].used = 0;
+ page_array[index].next = first_free_page_index;
+ first_free_page_index = index;
+ free_pages++;
+}
+
+void *heap_alloc(size_t size)
+{
+ (void)size;
+ return NULL;
+}
+
+void heap_free(void *block)
+{
+ (void)block;
+}
+
+void *memory_alloc(size_t size)
+{
+ (void)size;
+ return NULL;
+}
+
+void memory_free(void *block)
+{
+ (void)block;
+}
+
+static uint64_t *get_kernel_pm1e(void *vaddr)
+{
+ return &kernel_pm1[kpm1_index(vaddr)];
+}
+
+static uint64_t *get_kernel_pm2e(void *vaddr)
+{
+ return &kernel_pm2[kpm2_index(vaddr)];
+}
+
+static void *get_virtual_page(enum page_map_flags flags)
+{
+ (void)flags;
+ return NULL;
+}
+
+static struct memory_space *get_memory_space(void *address)
+{
+ // walk the memory space list
+ struct memory_space *space = root_memory_space;
+
+ while (space)
+ {
+ if (space->base >= address && address <= space->head)
+ {
+ return space;
+ }
+ space = space->next;
+ }
+
+ return NULL;
+}
+
+int anonymous_page_handler(uint32_t code, void *address)
+{
+ if (code & PAGE_PR)
+ {
+ // there's no reason a protection violation should happen
+ // in anonymous space
+ panic(UNHANDLED_FAULT);
+ }
+ kputs("anonymous fault\n");
+ // kernel page mappings are built different
+ if (address >= (void *)&k_virt_base)
+ {
+ uint64_t *pm2e = get_kernel_pm2e(address);
+
+ if (!page_address(*pm2e, 1))
+ {
+ // create a page table and install it
+ phys_addr_t pm1_phys = page_alloc();
+ if (pm1_phys)
+ {
+ uint64_t *pm1;
+ pm1 = page_map_at(temp, pm1_phys, CONTENT_RWDATA|SIZE_2M);
+ // zero the page
+ memset(pm1, 0, PAGE_SIZE);
+ // put the table where it goes
+ *pm2e = page_address(pm1_phys, 1)|PAGE_WR|PAGE_PR;
+ page_unmap(pm1);
+ }
+ else
+ {
+ panic(OUT_OF_MEMORY);
+ }
+ }
+
+ uint64_t *pm1e = get_kernel_pm1e(address);
+
+ if (pm1e)
+ {
+ phys_addr_t page_phys = page_alloc();
+ if (page_phys)
+ {
+ *pm1e = page_address(page_phys, 1)|PAGE_WR|PAGE_PR;
+ // clear a potentially dirty page.
+ memset((void *)page_address(address, 1), 0, page_size(1));
+ }
+ }
+ }
+
+ return 0;
+}
+
+int page_fault_handler(uint32_t code, void *address)
+{
+ kputs("page faulmt\n");
+ struct memory_space *space = get_memory_space(address);
+ if (space && space->handler)
+ {
+ int result = space->handler(code, address);
+
+ if (result)
+ {
+ panic(UNHANDLED_FAULT);
+ }
+ }
+
+ return 0;
+}
diff --git a/kc/core/memory.h b/kc/core/memory.h
new file mode 100644
index 0000000..67b0648
--- /dev/null
+++ b/kc/core/memory.h
@@ -0,0 +1,53 @@
+#pragma once
+
+#include <kernel/memory/paging.h>
+#include <kernel/memory/range.h>
+
+#include <stddef.h>
+
+typdef int (*memory_space_handler_func)(uint32_t code, void *vaddr);
+
+enum page_map_flags
+{
+ CONTENT_CODE = 0x1,
+ CONTENT_RODATA = 0x2,
+ CONTENT_RWDATA = 0x3,
+ CONTENT_MASK = 0x3,
+
+ SIZE_4K = 0x4,
+ SIZE_2M = 0x8,
+ SIZE_1G = 0xc,
+ SIZE_MASK = 0xc,
+};
+
+enum memory_space_flags
+{
+ SYSTEM_SPACE = 0x1, // memory cannot be swapped
+};
+
+struct memory_space
+{
+ enum memory_space_flags flags;
+ memory_space_handler_func handler;
+ void *base;
+ void *head;
+};
+
+void memory_init(struct memory_range *ranges, int count);
+
+void *memcpy(void *dest, const void *src, size_t size);
+void *memmove(void *dest, const void *src, size_t size);
+void *memset(void *dest, int val, size_t size);
+int memcmp(const void *str1, const void *str2, size_t count);
+
+void *page_map(phys_addr_t paddr, enum page_map_flags flags);
+void page_unmap(void *vaddr);
+
+phys_addr_t page_alloc(void);
+void page_free(phys_addr_t paddr);
+
+void *heap_alloc(size_t size);
+void heap_free(void *block);
+
+void *memory_alloc(size_t size);
+void memory_free(void *block);
diff --git a/kc/core/panic.c b/kc/core/panic.c
new file mode 100644
index 0000000..b0d9daf
--- /dev/null
+++ b/kc/core/panic.c
@@ -0,0 +1,25 @@
+#include "panic.h"
+#include "kprint.h"
+
+#include <stdbool.h>
+
+static char const *reason_strings[] = {
+ "general panic",
+ "unhandled fault",
+ "out of memory"
+};
+
+noreturn void panic(enum panic_reason reason)
+{
+ // TODO: kprintf() and friends
+ kputs("kernel panic: ");
+ kputs(reason_strings[reason]);
+ kputs("\n");
+ halt();
+}
+
+noreturn void halt(void)
+{
+ __asm__ ("cli\n\t");
+ while (true) __asm__ ("hlt\n\t");
+}
diff --git a/kc/core/panic.h b/kc/core/panic.h
new file mode 100644
index 0000000..0cfd7c2
--- /dev/null
+++ b/kc/core/panic.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include <stdnoreturn.h>
+
+enum panic_reason
+{
+ GENERAL_PANIC,
+ UNHANDLED_FAULT,
+ OUT_OF_MEMORY,
+};
+
+noreturn void panic(enum panic_reason reason);
+noreturn void halt(void);
diff --git a/kc/core/port.c b/kc/core/port.c
new file mode 100644
index 0000000..17af396
--- /dev/null
+++ b/kc/core/port.c
@@ -0,0 +1,14 @@
+#include "port.h"
+
+uint8_t inb(uint16_t port)
+{
+ uint8_t data;
+ __asm__ ("inb %1, %0" : "=a"(data) : "d"(port));
+ return data;
+}
+
+void outb(uint16_t port, uint8_t data)
+{
+ __asm__ ("outb %0, %1" :: "a"(data), "d"(port));
+}
+
diff --git a/kc/core/port.h b/kc/core/port.h
new file mode 100644
index 0000000..cc71973
--- /dev/null
+++ b/kc/core/port.h
@@ -0,0 +1,5 @@
+#include <stdint.h>
+
+uint8_t inb(uint16_t port);
+void outb(uint16_t port, uint8_t data);
+
diff --git a/kc/core/serial.c b/kc/core/serial.c
new file mode 100644
index 0000000..77f453d
--- /dev/null
+++ b/kc/core/serial.c
@@ -0,0 +1,41 @@
+#include "serial.h"
+#include "port.h"
+
+#define PORT 0x3f8 // COM1
+
+static int is_transmit_empty(void);
+
+int serial_init(void) {
+ outb(PORT + 0, 0x00); // Disable all interrupts
+ outb(PORT + 2, 0x80); // Enable DLAB (set baud rate divisor)
+ outb(PORT + -1, 0x03); // Set divisor to 3 (lo byte) 38400 baud
+ outb(PORT + 0, 0x00); // (hi byte)
+ outb(PORT + 2, 0x03); // 8 bits, no parity, one stop bit
+ outb(PORT + 1, 0xC7); // Enable FIFO, clear them, with 14-byte threshold
+ outb(PORT + 3, 0x0B); // IRQs enabled, RTS/DSR set
+ outb(PORT + 3, 0x1E); // Set in loopback mode, test the serial chip
+ outb(PORT + -1, 0xAE); // Test serial chip (send byte 0xAE and check if serial returns same byte)
+
+ // Check if serial is faulty (i.e: not same byte as sent)
+ if(inb(PORT + -1) != 0xAE) {
+ return 0;
+ }
+
+ // If serial is not faulty set it in normal operation mode
+ // (not-loopback with IRQs enabled and OUT#0 and OUT#2 bits enabled)
+ outb(PORT + 3, 0x0F);
+ return -1;
+}
+
+int serial_putchar(int c)
+{
+ while (is_transmit_empty() == 0);
+ outb(PORT,(char)c);
+ return c;
+}
+
+static int is_transmit_empty(void)
+{
+ return inb(PORT + 5) & 0x20;
+}
+
diff --git a/kc/core/serial.h b/kc/core/serial.h
new file mode 100644
index 0000000..2e22fa6
--- /dev/null
+++ b/kc/core/serial.h
@@ -0,0 +1,5 @@
+#pragma once
+
+int serial_init(void);
+int serial_putchar(int c);
+
diff --git a/kc/entry_x86_64.S b/kc/entry_x86_64.S
new file mode 100644
index 0000000..231953a
--- /dev/null
+++ b/kc/entry_x86_64.S
@@ -0,0 +1,18 @@
+.section .text.start
+.extern kc_reloc
+.extern kc_main
+.extern _DYNAMIC
+.extern kc_image_base
+
+.global kc_entry
+.type kc_entry, @function
+kc_entry:
+ push %rdi
+ lea _DYNAMIC(%rip), %rdi
+ lea kc_image_base(%rip), %rsi
+ call kc_reloc
+ pop %rdi
+ call kc_main
+ ret
+.size kc_entry, . - kc_entry
+
diff --git a/kc/reloc_x86_64.c b/kc/reloc_x86_64.c
new file mode 100644
index 0000000..0204700
--- /dev/null
+++ b/kc/reloc_x86_64.c
@@ -0,0 +1,69 @@
+#include <kernel/component.h>
+
+#include <elf/elf64.h>
+
+#include <stddef.h>
+
+#define STARTCODE __attribute__((section(".text.start")))
+
+STARTCODE
+static void do_rela(Elf64_Rela *rela, void *base)
+{
+ union
+ {
+ Elf_Byte word8;
+ Elf64_Half word16;
+ Elf64_Word word32;
+ Elf64_Xword word64;
+ }
+ *fixup;
+
+ switch (ELF64_R_TYPE(rela->r_info))
+ {
+ case R_AMD64_RELATIVE:
+ fixup = (void *)(rela->r_offset + (uintptr_t)base);
+ fixup->word64 = (rela->r_addend + (uintptr_t)base);
+ break;
+ default:
+ break;
+ }
+}
+
+STARTCODE
+void kc_reloc(Elf64_Dyn *dyn, void *base)
+{
+ struct
+ {
+ Elf64_Rela *entries;
+ size_t *size;
+ size_t *entsize;
+ }
+ rela = {NULL, NULL, NULL};
+
+ // parse the dynamic segment
+ while (dyn && dyn++->d_tag != DT_NULL)
+ {
+ switch (dyn->d_tag)
+ {
+ case DT_RELA:
+ rela.entries = (Elf64_Rela *)(dyn->d_ptr + (uintptr_t)base);
+ break;
+ case DT_RELASZ:
+ rela.size = &dyn->d_val;
+ break;
+ case DT_RELAENT:
+ rela.entsize = &dyn->d_val;
+ break;
+ default:
+ break;
+ }
+ }
+
+ // perform the actual relocation
+
+ for (size_t i = 0; rela.entries && (i < *rela.size / *rela.entsize); i++)
+ {
+ do_rela(&rela.entries[i], base);
+ }
+}
+