diff options
56 files changed, 2856 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8965c2e --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +*.o +*.a +*.d +*.elf +.ccls* diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1d26c59 --- /dev/null +++ b/Makefile @@ -0,0 +1,37 @@ +include $(CONFIG) +include config.mk + +CDIRS := boot +CDIRS += boot/$(TARGET_BOOTLOADER) +CDIRS += device lib syscall + +CFILES := $(wildcard *.c) +CFILES += $(wildcard arch/$(TARGET_MACHINE)/*.c) +CFILES += $(foreach dir, $(CDIRS), $(wildcard $(dir)/*.c)) +CFILES += $(foreach dir, $(CDIRS), $(wildcard arch/$(TARGET_MACHINE)/$(dir)/*.c)) + +SFILES := $(wildcard *.s) +SFILES += $(wildcard arch/$(TARGET_MACHINE)/*.s) +SFILES += $(foreach dir, $(CDIRS), $(wildcard $(dir)/*.s)) +SFILES += $(foreach dir, $(CDIRS), $(wildcard arch/$(TARGET_MACHINE)/$(dir)/*.s)) + +OBJFILES := $(patsubst %.c,%.o,$(CFILES)) +OBJFILES += $(patsubst %.s,%.o,$(SFILES)) + +BIN := jove.elf + +all: $(BIN) + +.PHONY: clean +clean: + rm $(OBJFILES) + rm $(BIN) + +$(BIN): ${OBJFILES} + $(LD) $(LDFLAGS) ${OBJFILES} -o $(BIN) + +%.o:%.c + $(CC) $(CFLAGS) -c $< -o $@ + +%.o:%.S + $(AS) -g -c $< -o $@ diff --git a/arch/x86_64/gdt.c b/arch/x86_64/gdt.c new file mode 100644 index 0000000..e8ca912 --- /dev/null +++ b/arch/x86_64/gdt.c @@ -0,0 +1,111 @@ +#include "arch/x86_64/tables.h" +#include "arch/x86_64/processor.h" +#include "string.h" + +static segment_descriptor_t s_gdt[] = { + { 0 }, //Kernel NULL + { //Kernel Code + .limit_0_15 = 0xFFFF, + .base_0_15 = 0x0, + .type = CD_SEGMENT_TYPE_CODE | CD_SEGMENT_TYPE_WRITEABLE, + .s = 1, + .dpl = 0, + .p = 1, + .limit_16_19 = 0xF, + .avl = 0, + .l = 1, + .d_b = 0, + .g = 1, + .base_24_31 = 0x0 + }, + { //Kernel Data + .limit_0_15 = 0xFFFF, + .base_0_15 = 0x0, + .type = CD_SEGMENT_TYPE_WRITEABLE, + .s = 1, + .dpl = 0, + .p = 1, + .limit_16_19 = 0xF, + .avl = 0, + .l = 0, + .d_b = 1, + .g = 1, + .base_24_31 = 0x0 + }, + { 0 }, //User NULL + { //User Data + .limit_0_15 = 0xFFFF, + .base_0_15 = 0x0, + .type = CD_SEGMENT_TYPE_WRITEABLE, + .s = 1, + .dpl = 3, + .p = 1, + .limit_16_19 = 0xF, + .avl = 0, + .l = 0, + .d_b = 1, + .g = 1, + .base_24_31 = 0x0 + }, + { //User Code + .limit_0_15 = 0xFFFF, + .base_0_15 = 0x0, + .type = CD_SEGMENT_TYPE_CODE | CD_SEGMENT_TYPE_WRITEABLE, + .s = 1, + .dpl = 3, + .p = 1, + .limit_16_19 = 0xF, + .avl = 0, + .l = 1, + .d_b = 0, + .g = 1, + .base_24_31 = 0x0 + }, + { //TSS Low + .limit_0_15 = 0, + .base_0_15 = 0, + .base_16_23 = 0, + .type = S_SEGMENT_TYPE_TSS_AVAIL, + .avl = 0, + .s = 0, + .dpl = 0, + .p = 1, + .limit_16_19 = 0, + .l = 0, + .d_b = 0, + .g = 0, + .base_24_31 = 0 + }, + { 0 } //TSS High +}; + +void +gdt_setup(processor_t *processor) +{ + memcpy(processor->gdt, s_gdt, sizeof(s_gdt)); + + processor->gdtr.base = (uintptr_t)&s_gdt; + processor->gdtr.length = sizeof(s_gdt) - 1; + + segment_descriptor_t *tss_lo = &processor->gdt[GDT_ENTRY_TSS_LOW]; + segment_descriptor_t *tss_hi = &processor->gdt[GDT_ENTRY_TSS_HIGH]; + + uintptr_t tssb = (uintptr_t)&processor->tss; + tss_lo->base_0_15 = tssb & 0xFFFF; + tss_lo->base_16_23 = (tssb >> 16) & 0xFF; + tss_lo->base_24_31 = (tssb >> 24) & 0xFF; + tss_hi->limit_0_15 = (tssb >> 32) & 0xFFFF; + tss_hi->base_0_15 = (tssb >> 48) & 0xFFFF; + + size_t tssl = sizeof(tss_t); + tss_lo->limit_0_15 = tssl & 0xFFFF; + tss_lo->limit_16_19 = (tssl >> 16) & 0xF; + + processor->tss.iopb = tssl; + + extern void gdt_load(void*); + gdt_load(&processor->gdtr); + + extern void tss_flush(uint16_t); + tss_flush(GDT_ENTRY_TSS_LOW * sizeof(segment_descriptor_t)); +} diff --git a/arch/x86_64/idt.c b/arch/x86_64/idt.c new file mode 100644 index 0000000..99e1da1 --- /dev/null +++ b/arch/x86_64/idt.c @@ -0,0 +1,46 @@ +#include "arch/x86_64/tables.h" +#include "arch/x86_64/idt.h" + +__attribute__((aligned(4096))) +interrupt_gate_t s_idtd[256]; + +int64_t __isr_err; +int64_t __isr_num; + +void +isr_handle(ivt_state_t* state) +{ + kpanic_state(state, "Unhandled interrupt %i", __isr_num); +} + +void +ivt_setup(void) +{ + extern uintptr_t __ivt[256]; + for(int i = 0; i < 256; i++) { + uintptr_t base = __ivt[i]; + s_idtd[i] = (interrupt_gate_t) { + .base_0_15 = (base & 0xFFFF), + .segment_selector = 0x8, + .ist = 0, + .zero_0 = 0, + .type = 0xE, + .zero_1 = 0, + .dpl = 0, + .p = 1, + .base_16_31 = (base >> 16) & 0xFFFF, + .base_32_63 = (base >> 32) & 0xFFFFFFFF, + .resv = 0 + }; + } +} + +void +idt_setup(processor_t *processor) +{ + processor->idtr.base = (uintptr_t)&s_idtd; + processor->idtr.length = sizeof(s_idtd) - 1; + + extern void idt_load(void* idtr); + idt_load(&processor->idtr); +} diff --git a/arch/x86_64/ivt.s b/arch/x86_64/ivt.s new file mode 100644 index 0000000..d2d504b --- /dev/null +++ b/arch/x86_64/ivt.s @@ -0,0 +1,91 @@ +.section .text +.global idt_load +.type idt_load @function +idt_load: + lidt (%rdi) + sti + retq +.size idt_load, . - idt_load + +.include "arch/x86_64/savestate.s" + +.extern isr_handle +.type __isr_head @function +__isr_head: + pushall + movq %rsp, %rdi + cld + + call isr_handle + popall + iretq + +.extern __isr_err +.extern __isr_num + +.macro defn_isr_err num:req +.type __isr\num @function +__isr\num: + pushq %rbx + movq 8(%rsp), %rbx + movq %rbx, __isr_err + popq %rbx + addq $8, %rsp + movq $\num, __isr_num + jmp __isr_head +.size __isr\num, . - __isr\num +.endm + +.macro defn_isr num:req +.type __isr\num @function +__isr\num: + movq $\num, __isr_num + jmp __isr_head +.size __isr\num, . - __isr\num +.endm + +.macro decl_isr num:req +.quad __isr\num +.endm + +defn_isr 0 +defn_isr 1 +defn_isr 2 +defn_isr 3 +defn_isr 4 +defn_isr 5 +defn_isr 6 +defn_isr 7 +defn_isr_err 8 +defn_isr 9 +defn_isr_err 10 +defn_isr_err 11 +defn_isr_err 12 +defn_isr_err 13 +defn_isr_err 14 +defn_isr 15 +defn_isr 16 +defn_isr_err 17 +defn_isr 18 +defn_isr 19 +defn_isr 20 +defn_isr_err 21 + +.altmacro +.set i, 22 +.rept 256-22 + defn_isr %i + .set i, i+1 +.endr + +.section .data + +.global __ivt +.type __ivt @object +__ivt: + .set i, 0 + .rept 256 + decl_isr %i + .set i, i+1 + .endr +.size __ivt, . - __ivt diff --git a/arch/x86_64/lgdt.s b/arch/x86_64/lgdt.s new file mode 100644 index 0000000..7122ef9 --- /dev/null +++ b/arch/x86_64/lgdt.s @@ -0,0 +1,26 @@ +.global gdt_load +.type gdt_load @function +gdt_load: + lgdt (%rdi) +.reload_segments: + pushq $0x8 + leaq .reload_cs, %rax + pushq %rax + lretq +.reload_cs: + movw $0x10, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + movw %ax, %ss + retq +.size gdt_load, . - gdt_load + +.global tss_flush +.type tss_flush @function +tss_flush: + movw %di, %ax + ltr %ax + retq +.size tss_flush, . - tss_flush diff --git a/arch/x86_64/page_directory.c b/arch/x86_64/page_directory.c new file mode 100644 index 0000000..0506703 --- /dev/null +++ b/arch/x86_64/page_directory.c @@ -0,0 +1,114 @@ +#include "arch/x86_64/page.h" +#include "arch/x86_64/object.h" +#include "device/processor.h" +#include "print.h" +#include "memory.h" +#include "object.h" +#include "string.h" +#include "jove.h" + +physptr_t s_kpbase(void); + +#if defined(__limine__) + +#include "boot/limine/limine.h" +static struct limine_kernel_address_request s_kaddr_req = { + .id = LIMINE_KERNEL_ADDRESS_REQUEST +}; + +physptr_t s_kpbase(void) { return s_kaddr_req.response->physical_base; } + +#endif + +physptr_t +vmem_tophys_koff(virtptr_t v) +{ + return v - (physptr_t)&_kernel_start + s_kpbase(); +} + +#define IDENTITY_BASE 0xFFFF800000000000 +void* +vmem_phys_tovirt(physptr_t p) +{ + return (void*)(p + IDENTITY_BASE); +} + +uintptr_t vmem_ident_tophys(void *vptr) +{ + return ((uintptr_t)vptr) - IDENTITY_BASE; +} + +void* +pmle_get_page(pmle_t entry) +{ + uintptr_t pptr = entry.paddr << 12; + return vmem_phys_tovirt(pptr); +} + +__attribute__((aligned(0x1000))) pmle_t s_kernel_pml4[512]; // Page L4 +__attribute__((aligned(0x1000))) pmle_t s_kernel_pml3[512]; // Page L3 +__attribute__((aligned(0x1000))) pmle_t s_kernel_pml2[512]; // Page directory +__attribute__((aligned(0x1000))) pmle_t s_kernel_pml1[512]; // Page table + +__attribute__((aligned(0x1000))) pmle_t s_idmap_pml3[512]; +__attribute__((aligned(0x1000))) pmle_t s_idmap_pml2[512]; + +void +vmem_setup(void) +{ + memset(s_kernel_pml4, 0, 0x1000); + memset(s_kernel_pml3, 0, 0x1000); + memset(s_kernel_pml2, 0, 0x1000); + memset(s_kernel_pml1, 0, 0x1000); + + memset(s_idmap_pml3, 0, 0x1000); + memset(s_idmap_pml2, 0, 0x1000); + + virtptr_t kernel_start = (virtptr_t)&_kernel_start; + virtptr_t kernel_end = (virtptr_t)&_kernel_end; + + size_t kernel_size = kernel_end - kernel_start; + size_t kernel_size_pages = (kernel_size / 0x1000) + 1; + + physptr_t kernel_pml4_base = vmem_tophys_koff((virtptr_t)&s_kernel_pml4); + physptr_t kernel_pml3_base = vmem_tophys_koff((virtptr_t)&s_kernel_pml3); + physptr_t kernel_pml2_base = vmem_tophys_koff((virtptr_t)&s_kernel_pml2); + physptr_t kernel_pml1_base = vmem_tophys_koff((virtptr_t)&s_kernel_pml1); + + physptr_t idmap_pml3_base = vmem_tophys_koff((virtptr_t)&s_idmap_pml3); + physptr_t idmap_pml2_base = vmem_tophys_koff((virtptr_t)&s_idmap_pml2); + + processor_t *processor = processor_current(); + processor->pdir = kernel_pml4_base; + + //Map memory identity pages. + s_kernel_pml4[256].value = idmap_pml3_base | 2 | 1; + s_idmap_pml3[0].value = idmap_pml2_base | 2 | 1; + for(int i = 0; i < 512; i++) { + s_idmap_pml2[i].value = (i * 0x1000 * 512) | 0x80 | 2 | 1; + } + + //Map the kernel to himem. + + pmli_t kernel_pml3_i = PML_I_FOR_LAYER(kernel_start, 3); + pmli_t kernel_pml2_i = PML_I_FOR_LAYER(kernel_start, 2); + pmli_t kernel_pml1_ib = PML_I_FOR_LAYER(kernel_start, 1); + pmli_t kernel_pml1_ie = PML_I_FOR_LAYER(kernel_end, 1) + 1; + + s_kernel_pml4[511].value = kernel_pml3_base | 2 | 1; + s_kernel_pml3[kernel_pml3_i].value = kernel_pml2_base | 2 | 1; + s_kernel_pml2[kernel_pml2_i].value = kernel_pml1_base | 2 | 1; + for(pmli_t i = kernel_pml1_ib; i < kernel_pml1_ie; i++) { + s_kernel_pml1[i].value = ((i * 0x1000) + s_kpbase()) | 3; + } + + __asm__ volatile("mov %0, %%cr3":: "r"(kernel_pml4_base)); + + //Add page mapping object to init directory. + uint8_t pm_i = _initDirectory.self.data++; + _initDirectory.entries[pm_i] = (objdir_entry_t) { + .type = KO_MEMORY_MAPPING_PAGE, + .data = kernel_pml4_base + }; + _initData.pm_object = pm_i; +} diff --git a/arch/x86_64/panic.c b/arch/x86_64/panic.c new file mode 100644 index 0000000..2e34500 --- /dev/null +++ b/arch/x86_64/panic.c @@ -0,0 +1,36 @@ +#include "jove.h" +#include "arch/x86_64/idt.h" +#include "print.h" + +#include <stddef.h> + +struct stackFrame { + struct stackFrame *ebp; + uintptr_t eip; +}; + +void +kpanic_state(ivt_state_t *state, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + kprintf("Kernel Panic!\n"); + + kvprintf(fmt, ap); + kprintf("\n\nCore dump:\n"); + + kprintf("RAX %p | RBX %p | RCX %p | RDX %p\n", state->rax, state->rbx, state->rcx, state->rdx); + kprintf("RSI %p | RDI %p | RSP %p | RBP %p\n", state->rsi, state->rdi, state->rsp, state->rbp); + kprintf("R8 %p | R9 %p | R10 %p | R11 %p\n", state->r8, state->r9, state->r10, state->r11); + kprintf("R12 %p | R13 %p | R14 %p | R15 %p\n", state->r12, state->r13, state->r14, state->r15); + kprintf("RIP %p\n", state->rip); + + kprintf("\nStack trace:\n"); + struct stackFrame *frame = (struct stackFrame*)state->rbp; + for(size_t framei = 0; frame && framei < 64; ++framei) { + kprintf("%i %p : %p\n", framei, frame, frame->eip); + frame = frame->ebp; + } + + hcf(); +} diff --git a/arch/x86_64/processor.c b/arch/x86_64/processor.c new file mode 100644 index 0000000..b39ba63 --- /dev/null +++ b/arch/x86_64/processor.c @@ -0,0 +1,126 @@ +#include "device/processor.h" +#include "arch/x86_64/tables.h" +#include "include/arch/x86_64/idt.h" +#include "include/arch/x86_64/object.h" +#include "jove.h" + +processor_t s_bsp = { + .odir = &_initDirectory +}; + +struct jove_ObjectDirectory s_processor_dir = { + .entries = { + [0] = { + .type = KO_OBJECT_DIRECTORY, + .data = 2 + }, + [1] = { + .type = KO_DEV_PROCESSOR, + .data = (uintptr_t)&s_bsp + } + } +}; + +char s_initial_response_buffer[256] = { 0 }; + +typedef union msr_efer +{ + struct { + uint8_t sce : 1; + uint8_t resv : 7; + uint8_t lme : 1; + uint8_t unk0 : 1; + uint8_t lma : 1; + uint8_t nxe : 1; + uint8_t svme : 1; + uint8_t lmsle : 1; + uint8_t ffxsr : 1; + uint8_t tce : 1; + }; + uint32_t v[2]; +} msr_efer_t; + +typedef union msr_star +{ + struct { + uint32_t eip; + uint16_t kcs; + uint16_t ucs; + }; + uint32_t v[2]; +} msr_star_t; + +typedef union msr_lstar +{ + uint32_t v[2]; + uintptr_t ip; +} msr_lstar_t; + +static void +s_enable_sce(void) +{ + msr_efer_t feat; + rdmsr(MSR_EFER, &feat.v[0], &feat.v[1]); + + feat.sce = 1; + wrmsr(MSR_EFER, feat.v[0], feat.v[1]); + + msr_star_t star; + star.kcs = GDT_ENTRY_KERNEL_CODE * sizeof(segment_descriptor_t); + star.ucs = GDT_ENTRY_USER_CODE * sizeof(segment_descriptor_t); + wrmsr(MSR_STAR, star.v[0], star.v[1]); + + extern void _syscall_entry(void); + msr_lstar_t lstar; + lstar.ip = (uintptr_t)_syscall_entry; + wrmsr(MSR_LSTAR, lstar.v[0], lstar.v[1]); +} + +void +processor_setup(void *_processor) +{ + processor_t *processor = (processor_t*)_processor; + gdt_setup(processor); + idt_setup(processor); + + wrmsr(MSR_GS_BASE, + (uint32_t)((uintptr_t)_processor & 0xFFFFFFFF), + (uint32_t)((uintptr_t)_processor >> 32)); +} + +void +bsp_setup(void) +{ + uint64_t pd_i = _initDirectory.self.data++; + + _initDirectory.entries[pd_i] = (objdir_entry_t) { + .type = KO_OBJECT_DIRECTORY, + .data = (uintptr_t)(&s_processor_dir) + }; + _initData.processor_dir = pd_i; + + ivt_setup(); + s_enable_sce(); + + processor_setup(&s_bsp); +} + +void +rdmsr(uint32_t msr, uint32_t *lo, uint32_t *hi) +{ + __asm__ volatile("rdmsr": "=a"(*lo), "=d"(*hi): "c"(msr)); +} + +void +wrmsr(uint32_t msr, uint32_t lo, uint32_t hi) +{ + __asm__ volatile("wrmsr":: "a"(lo), "d"(hi), "c"(msr)); +} + +void* +processor_current(void) +{ + uint64_t r = 0; + rdmsr(MSR_GS_BASE, (uint32_t*)&r, ((uint32_t*)&r) + 1); + return (void*)r; +} diff --git a/arch/x86_64/savestate.s b/arch/x86_64/savestate.s new file mode 100644 index 0000000..f48b62c --- /dev/null +++ b/arch/x86_64/savestate.s @@ -0,0 +1,35 @@ +.macro pushall + pushq %rax + pushq %rbx + pushq %rcx + pushq %rdx + pushq %rsi + pushq %rdi + pushq %rbp + pushq %r8 + pushq %r9 + pushq %r10 + pushq %r11 + pushq %r12 + pushq %r13 + pushq %r14 + pushq %r15 +.endm + +.macro popall + popq %r15 + popq %r14 + popq %r13 + popq %r12 + popq %r11 + popq %r10 + popq %r9 + popq %r8 + popq %rbp + popq %rdi + popq %rsi + popq %rdx + popq %rcx + popq %rbx + popq %rax +.endm diff --git a/arch/x86_64/syscall.c b/arch/x86_64/syscall.c new file mode 100644 index 0000000..a563525 --- /dev/null +++ b/arch/x86_64/syscall.c @@ -0,0 +1,25 @@ +#include "arch/x86_64/processor.h" +#include <stddef.h> + +__attribute__((naked)) +void _syscall_entry(void) +{ + __asm__ volatile(" \ + pushq %%r11; \ + pushq %%rcx; \ + swapgs; \ + movq %%gs:%c[tcb], %%rax; \ + movq %%rsp, %c[sp](%%rax); \ + movq %c[ksp](%%rax), %%rsp; \ + pushq %c[sp](%%rax); \ + callq _syscall_handler; \ + swapgs; \ + popq %%rsp; \ + popq %%rcx; \ + popq %%r11; \ + sysretq;" + :: + [tcb] "i"(offsetof(processor_t, tcb)), + [sp] "i"(offsetof(tcb_t, sp)), + [ksp] "i"(offsetof(tcb_t, ksp))); +} diff --git a/arch/x86_64/untyped_memory.c b/arch/x86_64/untyped_memory.c new file mode 100644 index 0000000..699cdbf --- /dev/null +++ b/arch/x86_64/untyped_memory.c @@ -0,0 +1,54 @@ +#include "memory.h" +#include "jove.h" +#include "print.h" +#include "arch/x86_64/object.h" +#include "arch/x86_64/page.h" +#include <stddef.h> + +static struct jove_ObjectDirectory s_untyped_dir = { + .self = { .type = KO_OBJECT_DIRECTORY, .data = 1 }, +}; + +static void s_populate_tables(); + +#if defined(__limine__) + +#include "boot/limine/limine.h" +static volatile struct limine_memmap_request s_memmap_req = { + .id = LIMINE_MEMMAP_REQUEST, + .response = NULL +}; + +void +pmem_setup(void) +{ + size_t diri = _initDirectory.self.data++; + _initDirectory.entries[diri] = (objdir_entry_t) { + .type = KO_OBJECT_DIRECTORY, + .data = (uintptr_t)&s_untyped_dir + }; + _initData.untyped_data_dir = diri; + + if(s_memmap_req.response == NULL) { + klogf("Failed to load physical memory map"); + hcf(); + } + struct limine_memmap_response *response = s_memmap_req.response; + for(size_t i = 0; i < response->entry_count; i++) { + struct limine_memmap_entry *entry = response->entries[i]; + if(entry->type != LIMINE_MEMMAP_USABLE) continue; + + size_t table_index = s_untyped_dir.self.data++; + uintmax_t *untyped_data = vmem_phys_tovirt(entry->base); + objdir_entry_t *table_entry = &s_untyped_dir.entries[table_index]; + + table_entry->type = KO_MEMORY_UNTYPED; + table_entry->data = (uintptr_t)entry->base; + *untyped_data = entry->length; + + klogf("New untyped block %i at %p:%p\n", + table_index, entry->base, entry->length); + } +} + +#endif diff --git a/arch/x86_64/untyped_retype_page.c b/arch/x86_64/untyped_retype_page.c new file mode 100644 index 0000000..216d88d --- /dev/null +++ b/arch/x86_64/untyped_retype_page.c @@ -0,0 +1,51 @@ +#include "memory.h" +#include "arch/x86_64/page.h" +#include "print.h" +#include <stddef.h> + +static int +s_untyped_retype( + objdir_entry_t *untyped_entry, + size_t size, + size_t align, + void **dest) +{ + if(untyped_entry->type != KO_MEMORY_UNTYPED) return -1; + if((untyped_entry->data & (align - 1)) != 0) return -2; //EALIGN + + uintptr_t *untyped_data = vmem_phys_tovirt(untyped_entry->data); + uintmax_t untyped_size = *untyped_data; + + if(untyped_size <= size) { + return -3; + } + + *untyped_data -= size; + *dest = (void*)(untyped_entry->data + untyped_size - size); + + return 0; +} + +int untyped_retype_page(objdir_entry_t *untyped_entry, void **dest) +{ + int r = s_untyped_retype(untyped_entry, 0x1000, 0x1000, dest); + if(r != 0) return r; + + #ifdef DBG_MEM + klogf("Untyped block %p retyped to page at %p\n", untyped_entry->data, *dest); + #endif + return r; +} + +int +untyped_retype_kernel_stack(objdir_entry_t *untyped_entry, objdir_entry_t *dest_entry) +{ + int r = s_untyped_retype(untyped_entry, KERNEL_STACK_SIZE, 1, (void**)(&dest_entry->data)); + if(r != 0) return r; + + dest_entry->type = KO_KERNEL_STACK; + #ifdef DBG_MEM + klogf("Untyped block %p retyped to kernel stack at %p\n", untyped_entry->data, dest_entry->data); + #endif + return r; +} diff --git a/arch/x86_64/usermode.c b/arch/x86_64/usermode.c new file mode 100644 index 0000000..9ce5ed3 --- /dev/null +++ b/arch/x86_64/usermode.c @@ -0,0 +1,184 @@ +#include "arch/x86_64/object.h" +#include "object.h" +#include "memory.h" +#include "bootargs.h" +#include "device/initrd.h" +#include "object.h" +#include "panic.h" +#include "string.h" +#include "print.h" +#include "device/processor.h" +#include "arch/x86_64/page.h" + +static tcb_t s_init_tcb; + +//Dynamic allocation hell!!! +static uintptr_t +s_new_mapping(objdir_t *untyped_dir) +{ + objdir_entry_t *untyped_entry = NULL; + for(int i = 1; i < OBJECT_DIRECTORY_MAX_ENTRIES; i++) { + untyped_entry = &untyped_dir->entries[i]; + if(untyped_entry->type != KO_MEMORY_UNTYPED) continue; + uintptr_t mapping = 0; + int err = untyped_retype_page(untyped_entry, (void**)&mapping); + if(err != 0) continue; + return mapping; + } + return 0; +} + +static uintptr_t +s_new_kstack(objdir_t *untyped_dir, objdir_entry_t *dest_entry) +{ + objdir_entry_t *untyped_entry = NULL; + for(int i = 1; i < OBJECT_DIRECTORY_MAX_ENTRIES; i++) { + untyped_entry = &untyped_dir->entries[i]; + if(untyped_entry->type != KO_MEMORY_UNTYPED) continue; + + int err = untyped_retype_kernel_stack(untyped_entry, dest_entry); + if(err != 0) continue; + + uintptr_t ptr = dest_entry->data; + return (uintptr_t)vmem_phys_tovirt(ptr); + } + return 0; +} + +static pmle_t* +s_ensure_mapping_layer(pmle_t *pml, objdir_t *untyped_dir, uintptr_t addr, uint8_t layer) +{ + pmli_t pmli = PML_I_FOR_LAYER(addr, layer); + if(pml[pmli].p) + return pmle_get_page(pml[pmli]); + + uintptr_t table_phys = s_new_mapping(untyped_dir); + pmle_t *table = vmem_phys_tovirt(table_phys); + memset(table, 0, 0x1000); + + pml[pmli] = (pmle_t) { + .p = 1, + .rw = 1, + .us = 1, + .paddr = table_phys >> 12 + }; + __asm__ volatile("invlpg (%0)":: "r"(table_phys)); + return vmem_phys_tovirt(table_phys); +} + +static void +s_map_page(pmle_t *pml4, objdir_t *untyped_dir, uintptr_t addr) +{ + pmle_t *pml3 = s_ensure_mapping_layer(pml4, untyped_dir, addr, 4); + pmle_t *pml2 = s_ensure_mapping_layer(pml3, untyped_dir, addr, 3); + pmle_t *pml1 = s_ensure_mapping_layer(pml2, untyped_dir, addr, 2); + + pmli_t pml1i = PML_I_FOR_LAYER(addr, 1); + pmle_t *pmle = &pml1[pml1i]; + + uintptr_t pptr = s_new_mapping(untyped_dir); + + *pmle = (pmle_t) { + .p = 1, + .rw = 1, + .us = 1, + .paddr = pptr >> 12 + }; + __asm__ volatile("invlpg (%0)":: "r"(addr)); +} + +__attribute__((noreturn)) +static void +s_enter_usermode(void *ip, void *sp) +{ + __asm__ volatile("mov %0, %%rsp; \ + movq %1, %%rcx; \ + movq $0x202, %%r11; \ + cli; \ + swapgs; \ + sysretq":: + "r"(sp), "r"(ip): "memory"); + for(;;); +} + +void +init_load(void) +{ + const char *init_filename = bootargs_getarg("init"); + if(init_filename == NULL) { + kpanic("Missing boot argument \"init\""); + } + tar_header_t *init_header = initrd_find_file(init_filename); + if(init_header == NULL) { + kpanic("Init file not found in initrd. (expected \"%s\")", init_filename); + } + + size_t tcb_diri = _initDirectory.self.data++; + size_t kstack_diri = _initDirectory.self.data++; + size_t message_diri = _initDirectory.self.data++; + + _initData.tcb_object = tcb_diri; + _initData.kernel_stack_object = kstack_diri; + _initData.message_object = message_diri; + + _initDirectory.entries[tcb_diri] = (objdir_entry_t) { + .type = KO_TCB, + .data = (uintptr_t)&s_init_tcb + }; + + size_t untyped_diri = _initData.untyped_data_dir; + objdir_t *untyped_dir = (objdir_t*)_initDirectory.entries[untyped_diri].data; + + size_t pml4_diri = _initData.pm_object; + pmle_t *pml4 = vmem_phys_tovirt(_initDirectory.entries[pml4_diri].data); + + //Reserve and map pages for init binary + size_t init_size = initrd_file_size(init_header); + + size_t init_pages = (init_size >> 12); + uintptr_t init_base = 0x1000; + + for(size_t i = 0; i < init_pages + 1; i++) { + s_map_page(pml4, untyped_dir, init_base + (i * 0x1000)); + } + //Copy over init data + memcpy((void*)init_base, (&((tar_block_t*)init_header)[1])->data, init_size); + + //Create a user stack + uintptr_t stack_base = 0x00007FFFFFFFF000; + s_map_page(pml4, untyped_dir, stack_base); + uintptr_t sp = stack_base + 0xFF0; + + //Create a kernel stack for the init TCB + uintptr_t ksp_base = (uintptr_t)s_new_kstack(untyped_dir, &_initDirectory.entries[kstack_diri]); + _initDirectory.entries[kstack_diri] = (objdir_entry_t) { + .type = KO_KERNEL_STACK, + .data = ksp_base + }; + s_init_tcb.ksp = ksp_base + 0xFF0; + + processor_t *proc = (processor_t*)processor_current(); + proc->tss.rsp[0] = ksp_base; + + //Create a message object for init + uintptr_t message_base = init_base + (init_pages << 12); + message_base += ((~(message_base & 0xFFF)) & 0xFFF) + 1; + + s_map_page(pml4, untyped_dir, message_base); + _initDirectory.entries[message_diri] = (objdir_entry_t) { + .type = KO_MESSAGE, + .data = message_base + }; + _initData.message_object_address = message_base; + + //Write init data to user stack. + sp -= sizeof(init_data_t); + memcpy((void*)sp, &_initData, sizeof(init_data_t)); + sp -= sizeof(uintptr_t); + *((uintptr_t*)sp) = sp + sizeof(uintptr_t); + + //Setup usermode and jump + proc->tcb = &s_init_tcb; + proc->tss.rsp[0] = s_init_tcb.ksp; + s_enter_usermode((void*)init_base, (void*)sp); +} diff --git a/boot/bootargs.c b/boot/bootargs.c new file mode 100644 index 0000000..81ba57d --- /dev/null +++ b/boot/bootargs.c @@ -0,0 +1,25 @@ +#include "bootargs.h" +#include "boot.h" +#include "string.h" +#include <stdint.h> +#include "print.h" + +char* +bootargs_getarg(const char *key) +{ + size_t keylen = strlen(key); + char *arg = jove_bootargs; + for(; (uintptr_t)arg < (uintptr_t)jove_bootargs + jove_bootargs_len; arg += strlen(arg) + 1) { + size_t arglen = strlen(arg); + if(arglen < keylen) continue; + + for(size_t i = 0; i < keylen; i++) { + if(arg[i] != key[i]) goto eol; + } + if(arg[keylen] != '=') continue; + return arg + keylen + 1; +eol: + continue; + } + return NULL; +} diff --git a/boot/limine/limine.c b/boot/limine/limine.c new file mode 100644 index 0000000..6a1bb43 --- /dev/null +++ b/boot/limine/limine.c @@ -0,0 +1,38 @@ +#include "limine.h" +#include "boot.h" +#include "string.h" + +char s_kernel_stack_initial[8192]; + +char *jove_bootargs; +int jove_bootargs_len; + +struct limine_kernel_file_request s_kernel_file_req = { + .id = LIMINE_KERNEL_FILE_REQUEST +}; + +void +_start(void) +{ + __asm__ volatile("movq %0, %%rsp":: "r"((uintptr_t)&s_kernel_stack_initial + 8191)); + + struct limine_kernel_file_response *kernel_file_response = s_kernel_file_req.response; + jove_bootargs = kernel_file_response->kernel_file->cmdline; + jove_bootargs_len = strlen(jove_bootargs); + + //Zero all spaces in bootargs not enclosed with quotes. + for(char *c = jove_bootargs; *c; c++) { + if(*c == '"') { + *c = 0; + for(++c; *c && *c != '"'; c++); + *(c++) = 0; + continue; + } + if(*c == ' ') *c = 0; + } + + extern void _jove_main(void); + + __asm__ volatile("movq $0, %%rbp"::); + _jove_main(); +} diff --git a/boot/limine/limine.h b/boot/limine/limine.h new file mode 100644 index 0000000..b7baa79 --- /dev/null +++ b/boot/limine/limine.h @@ -0,0 +1,578 @@ +/* BSD Zero Clause License */ + +/* Copyright (C) 2022-2023 mintsuki and contributors. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _LIMINE_H +#define _LIMINE_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdint.h> + +/* Misc */ + +#ifdef LIMINE_NO_POINTERS +# define LIMINE_PTR(TYPE) uint64_t +#else +# define LIMINE_PTR(TYPE) TYPE +#endif + +#ifdef __GNUC__ +# define LIMINE_DEPRECATED __attribute__((__deprecated__)) +# define LIMINE_DEPRECATED_IGNORE_START \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +# define LIMINE_DEPRECATED_IGNORE_END \ + _Pragma("GCC diagnostic pop") +#else +# define LIMINE_DEPRECATED +# define LIMINE_DEPRECATED_IGNORE_START +# define LIMINE_DEPRECATED_IGNORE_END +#endif + +#define LIMINE_BASE_REVISION(N) \ + uint64_t limine_base_revision[3] = { 0xf9562b2d5c95a6c8, 0x6a7b384944536bdc, (N) }; + +#define LIMINE_BASE_REVISION_SUPPORTED (limine_base_revision[2] == 0) + +#define LIMINE_COMMON_MAGIC 0xc7b1dd30df4c8b88, 0x0a82e883a194f07b + +struct limine_uuid { + uint32_t a; + uint16_t b; + uint16_t c; + uint8_t d[8]; +}; + +#define LIMINE_MEDIA_TYPE_GENERIC 0 +#define LIMINE_MEDIA_TYPE_OPTICAL 1 +#define LIMINE_MEDIA_TYPE_TFTP 2 + +struct limine_file { + uint64_t revision; + LIMINE_PTR(void *) address; + uint64_t size; + LIMINE_PTR(char *) path; + LIMINE_PTR(char *) cmdline; + uint32_t media_type; + uint32_t unused; + uint32_t tftp_ip; + uint32_t tftp_port; + uint32_t partition_index; + uint32_t mbr_disk_id; + struct limine_uuid gpt_disk_uuid; + struct limine_uuid gpt_part_uuid; + struct limine_uuid part_uuid; +}; + +/* Boot info */ + +#define LIMINE_BOOTLOADER_INFO_REQUEST { LIMINE_COMMON_MAGIC, 0xf55038d8e2a1202f, 0x279426fcf5f59740 } + +struct limine_bootloader_info_response { + uint64_t revision; + LIMINE_PTR(char *) name; + LIMINE_PTR(char *) version; +}; + +struct limine_bootloader_info_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_bootloader_info_response *) response; +}; + +/* Stack size */ + +#define LIMINE_STACK_SIZE_REQUEST { LIMINE_COMMON_MAGIC, 0x224ef0460a8e8926, 0xe1cb0fc25f46ea3d } + +struct limine_stack_size_response { + uint64_t revision; +}; + +struct limine_stack_size_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_stack_size_response *) response; + uint64_t stack_size; +}; + +/* HHDM */ + +#define LIMINE_HHDM_REQUEST { LIMINE_COMMON_MAGIC, 0x48dcf1cb8ad2b852, 0x63984e959a98244b } + +struct limine_hhdm_response { + uint64_t revision; + uint64_t offset; +}; + +struct limine_hhdm_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_hhdm_response *) response; +}; + +/* Framebuffer */ + +#define LIMINE_FRAMEBUFFER_REQUEST { LIMINE_COMMON_MAGIC, 0x9d5827dcd881dd75, 0xa3148604f6fab11b } + +#define LIMINE_FRAMEBUFFER_RGB 1 + +struct limine_video_mode { + uint64_t pitch; + uint64_t width; + uint64_t height; + uint16_t bpp; + uint8_t memory_model; + uint8_t red_mask_size; + uint8_t red_mask_shift; + uint8_t green_mask_size; + uint8_t green_mask_shift; + uint8_t blue_mask_size; + uint8_t blue_mask_shift; +}; + +struct limine_framebuffer { + LIMINE_PTR(void *) address; + uint64_t width; + uint64_t height; + uint64_t pitch; + uint16_t bpp; + uint8_t memory_model; + uint8_t red_mask_size; + uint8_t red_mask_shift; + uint8_t green_mask_size; + uint8_t green_mask_shift; + uint8_t blue_mask_size; + uint8_t blue_mask_shift; + uint8_t unused[7]; + uint64_t edid_size; + LIMINE_PTR(void *) edid; + /* Response revision 1 */ + uint64_t mode_count; + LIMINE_PTR(struct limine_video_mode **) modes; +}; + +struct limine_framebuffer_response { + uint64_t revision; + uint64_t framebuffer_count; + LIMINE_PTR(struct limine_framebuffer **) framebuffers; +}; + +struct limine_framebuffer_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_framebuffer_response *) response; +}; + +/* Terminal */ + +#define LIMINE_TERMINAL_REQUEST { LIMINE_COMMON_MAGIC, 0xc8ac59310c2b0844, 0xa68d0c7265d38878 } + +#define LIMINE_TERMINAL_CB_DEC 10 +#define LIMINE_TERMINAL_CB_BELL 20 +#define LIMINE_TERMINAL_CB_PRIVATE_ID 30 +#define LIMINE_TERMINAL_CB_STATUS_REPORT 40 +#define LIMINE_TERMINAL_CB_POS_REPORT 50 +#define LIMINE_TERMINAL_CB_KBD_LEDS 60 +#define LIMINE_TERMINAL_CB_MODE 70 +#define LIMINE_TERMINAL_CB_LINUX 80 + +#define LIMINE_TERMINAL_CTX_SIZE ((uint64_t)(-1)) +#define LIMINE_TERMINAL_CTX_SAVE ((uint64_t)(-2)) +#define LIMINE_TERMINAL_CTX_RESTORE ((uint64_t)(-3)) +#define LIMINE_TERMINAL_FULL_REFRESH ((uint64_t)(-4)) + +/* Response revision 1 */ +#define LIMINE_TERMINAL_OOB_OUTPUT_GET ((uint64_t)(-10)) +#define LIMINE_TERMINAL_OOB_OUTPUT_SET ((uint64_t)(-11)) + +#define LIMINE_TERMINAL_OOB_OUTPUT_OCRNL (1 << 0) +#define LIMINE_TERMINAL_OOB_OUTPUT_OFDEL (1 << 1) +#define LIMINE_TERMINAL_OOB_OUTPUT_OFILL (1 << 2) +#define LIMINE_TERMINAL_OOB_OUTPUT_OLCUC (1 << 3) +#define LIMINE_TERMINAL_OOB_OUTPUT_ONLCR (1 << 4) +#define LIMINE_TERMINAL_OOB_OUTPUT_ONLRET (1 << 5) +#define LIMINE_TERMINAL_OOB_OUTPUT_ONOCR (1 << 6) +#define LIMINE_TERMINAL_OOB_OUTPUT_OPOST (1 << 7) + +LIMINE_DEPRECATED_IGNORE_START + +struct LIMINE_DEPRECATED limine_terminal; + +typedef void (*limine_terminal_write)(struct limine_terminal *, const char *, uint64_t); +typedef void (*limine_terminal_callback)(struct limine_terminal *, uint64_t, uint64_t, uint64_t, uint64_t); + +struct LIMINE_DEPRECATED limine_terminal { + uint64_t columns; + uint64_t rows; + LIMINE_PTR(struct limine_framebuffer *) framebuffer; +}; + +struct LIMINE_DEPRECATED limine_terminal_response { + uint64_t revision; + uint64_t terminal_count; + LIMINE_PTR(struct limine_terminal **) terminals; + LIMINE_PTR(limine_terminal_write) write; +}; + +struct LIMINE_DEPRECATED limine_terminal_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_terminal_response *) response; + LIMINE_PTR(limine_terminal_callback) callback; +}; + +LIMINE_DEPRECATED_IGNORE_END + +/* Paging mode */ + +#define LIMINE_PAGING_MODE_REQUEST { LIMINE_COMMON_MAGIC, 0x95c1a0edab0944cb, 0xa4e5cb3842f7488a } + +#if defined (__x86_64__) || defined (__i386__) +#define LIMINE_PAGING_MODE_X86_64_4LVL 0 +#define LIMINE_PAGING_MODE_X86_64_5LVL 1 +#define LIMINE_PAGING_MODE_MAX LIMINE_PAGING_MODE_X86_64_5LVL +#define LIMINE_PAGING_MODE_DEFAULT LIMINE_PAGING_MODE_X86_64_4LVL +#elif defined (__aarch64__) +#define LIMINE_PAGING_MODE_AARCH64_4LVL 0 +#define LIMINE_PAGING_MODE_AARCH64_5LVL 1 +#define LIMINE_PAGING_MODE_MAX LIMINE_PAGING_MODE_AARCH64_5LVL +#define LIMINE_PAGING_MODE_DEFAULT LIMINE_PAGING_MODE_AARCH64_4LVL +#elif defined (__riscv) && (__riscv_xlen == 64) +#define LIMINE_PAGING_MODE_RISCV_SV39 0 +#define LIMINE_PAGING_MODE_RISCV_SV48 1 +#define LIMINE_PAGING_MODE_RISCV_SV57 2 +#define LIMINE_PAGING_MODE_MAX LIMINE_PAGING_MODE_RISCV_SV57 +#define LIMINE_PAGING_MODE_DEFAULT LIMINE_PAGING_MODE_RISCV_SV48 +#else +#error Unknown architecture +#endif + +struct limine_paging_mode_response { + uint64_t revision; + uint64_t mode; + uint64_t flags; +}; + +struct limine_paging_mode_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_paging_mode_response *) response; + uint64_t mode; + uint64_t flags; +}; + +/* 5-level paging */ + +#define LIMINE_5_LEVEL_PAGING_REQUEST { LIMINE_COMMON_MAGIC, 0x94469551da9b3192, 0xebe5e86db7382888 } + +LIMINE_DEPRECATED_IGNORE_START + +struct LIMINE_DEPRECATED limine_5_level_paging_response { + uint64_t revision; +}; + +struct LIMINE_DEPRECATED limine_5_level_paging_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_5_level_paging_response *) response; +}; + +LIMINE_DEPRECATED_IGNORE_END + +/* SMP */ + +#define LIMINE_SMP_REQUEST { LIMINE_COMMON_MAGIC, 0x95a67b819a1b857e, 0xa0b61b723b6a73e0 } + +struct limine_smp_info; + +typedef void (*limine_goto_address)(struct limine_smp_info *); + +#if defined (__x86_64__) || defined (__i386__) + +#define LIMINE_SMP_X2APIC (1 << 0) + +struct limine_smp_info { + uint32_t processor_id; + uint32_t lapic_id; + uint64_t reserved; + LIMINE_PTR(limine_goto_address) goto_address; + uint64_t extra_argument; +}; + +struct limine_smp_response { + uint64_t revision; + uint32_t flags; + uint32_t bsp_lapic_id; + uint64_t cpu_count; + LIMINE_PTR(struct limine_smp_info **) cpus; +}; + +#elif defined (__aarch64__) + +struct limine_smp_info { + uint32_t processor_id; + uint32_t gic_iface_no; + uint64_t mpidr; + uint64_t reserved; + LIMINE_PTR(limine_goto_address) goto_address; + uint64_t extra_argument; +}; + +struct limine_smp_response { + uint64_t revision; + uint64_t flags; + uint64_t bsp_mpidr; + uint64_t cpu_count; + LIMINE_PTR(struct limine_smp_info **) cpus; +}; + +#elif defined (__riscv) && (__riscv_xlen == 64) + +struct limine_smp_info { + uint64_t processor_id; + uint64_t hartid; + uint64_t reserved; + LIMINE_PTR(limine_goto_address) goto_address; + uint64_t extra_argument; +}; + +struct limine_smp_response { + uint64_t revision; + uint64_t flags; + uint64_t bsp_hartid; + uint64_t cpu_count; + LIMINE_PTR(struct limine_smp_info **) cpus; +}; + +#else +#error Unknown architecture +#endif + +struct limine_smp_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_smp_response *) response; + uint64_t flags; +}; + +/* Memory map */ + +#define LIMINE_MEMMAP_REQUEST { LIMINE_COMMON_MAGIC, 0x67cf3d9d378a806f, 0xe304acdfc50c3c62 } + +#define LIMINE_MEMMAP_USABLE 0 +#define LIMINE_MEMMAP_RESERVED 1 +#define LIMINE_MEMMAP_ACPI_RECLAIMABLE 2 +#define LIMINE_MEMMAP_ACPI_NVS 3 +#define LIMINE_MEMMAP_BAD_MEMORY 4 +#define LIMINE_MEMMAP_BOOTLOADER_RECLAIMABLE 5 +#define LIMINE_MEMMAP_KERNEL_AND_MODULES 6 +#define LIMINE_MEMMAP_FRAMEBUFFER 7 + +struct limine_memmap_entry { + uint64_t base; + uint64_t length; + uint64_t type; +}; + +struct limine_memmap_response { + uint64_t revision; + uint64_t entry_count; + LIMINE_PTR(struct limine_memmap_entry **) entries; +}; + +struct limine_memmap_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_memmap_response *) response; +}; + +/* Entry point */ + +#define LIMINE_ENTRY_POINT_REQUEST { LIMINE_COMMON_MAGIC, 0x13d86c035a1cd3e1, 0x2b0caa89d8f3026a } + +typedef void (*limine_entry_point)(void); + +struct limine_entry_point_response { + uint64_t revision; +}; + +struct limine_entry_point_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_entry_point_response *) response; + LIMINE_PTR(limine_entry_point) entry; +}; + +/* Kernel File */ + +#define LIMINE_KERNEL_FILE_REQUEST { LIMINE_COMMON_MAGIC, 0xad97e90e83f1ed67, 0x31eb5d1c5ff23b69 } + +struct limine_kernel_file_response { + uint64_t revision; + LIMINE_PTR(struct limine_file *) kernel_file; +}; + +struct limine_kernel_file_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_kernel_file_response *) response; +}; + +/* Module */ + +#define LIMINE_MODULE_REQUEST { LIMINE_COMMON_MAGIC, 0x3e7e279702be32af, 0xca1c4f3bd1280cee } + +#define LIMINE_INTERNAL_MODULE_REQUIRED (1 << 0) + +struct limine_internal_module { + LIMINE_PTR(const char *) path; + LIMINE_PTR(const char *) cmdline; + uint64_t flags; +}; + +struct limine_module_response { + uint64_t revision; + uint64_t module_count; + LIMINE_PTR(struct limine_file **) modules; +}; + +struct limine_module_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_module_response *) response; + + /* Request revision 1 */ + uint64_t internal_module_count; + LIMINE_PTR(struct limine_internal_module **) internal_modules; +}; + +/* RSDP */ + +#define LIMINE_RSDP_REQUEST { LIMINE_COMMON_MAGIC, 0xc5e77b6b397e7b43, 0x27637845accdcf3c } + +struct limine_rsdp_response { + uint64_t revision; + LIMINE_PTR(void *) address; +}; + +struct limine_rsdp_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_rsdp_response *) response; +}; + +/* SMBIOS */ + +#define LIMINE_SMBIOS_REQUEST { LIMINE_COMMON_MAGIC, 0x9e9046f11e095391, 0xaa4a520fefbde5ee } + +struct limine_smbios_response { + uint64_t revision; + LIMINE_PTR(void *) entry_32; + LIMINE_PTR(void *) entry_64; +}; + +struct limine_smbios_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_smbios_response *) response; +}; + +/* EFI system table */ + +#define LIMINE_EFI_SYSTEM_TABLE_REQUEST { LIMINE_COMMON_MAGIC, 0x5ceba5163eaaf6d6, 0x0a6981610cf65fcc } + +struct limine_efi_system_table_response { + uint64_t revision; + LIMINE_PTR(void *) address; +}; + +struct limine_efi_system_table_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_efi_system_table_response *) response; +}; + +/* EFI memory map */ + +#define LIMINE_EFI_MEMMAP_REQUEST { LIMINE_COMMON_MAGIC, 0x7df62a431d6872d5, 0xa4fcdfb3e57306c8 } + +struct limine_efi_memmap_response { + uint64_t revision; + LIMINE_PTR(void *) memmap; + uint64_t memmap_size; + uint64_t desc_size; + uint64_t desc_version; +}; + +struct limine_efi_memmap_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_efi_memmap_response *) response; +}; + +/* Boot time */ + +#define LIMINE_BOOT_TIME_REQUEST { LIMINE_COMMON_MAGIC, 0x502746e184c088aa, 0xfbc5ec83e6327893 } + +struct limine_boot_time_response { + uint64_t revision; + int64_t boot_time; +}; + +struct limine_boot_time_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_boot_time_response *) response; +}; + +/* Kernel address */ + +#define LIMINE_KERNEL_ADDRESS_REQUEST { LIMINE_COMMON_MAGIC, 0x71ba76863cc55f63, 0xb2644a48c516a487 } + +struct limine_kernel_address_response { + uint64_t revision; + uint64_t physical_base; + uint64_t virtual_base; +}; + +struct limine_kernel_address_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_kernel_address_response *) response; +}; + +/* Device Tree Blob */ + +#define LIMINE_DTB_REQUEST { LIMINE_COMMON_MAGIC, 0xb40ddb48fb54bac7, 0x545081493f81ffb7 } + +struct limine_dtb_response { + uint64_t revision; + LIMINE_PTR(void *) dtb_ptr; +}; + +struct limine_dtb_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_dtb_response *) response; +}; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/config.mk b/config.mk new file mode 100644 index 0000000..4b940b5 --- /dev/null +++ b/config.mk @@ -0,0 +1,44 @@ +PROCESSORS_MAX=8 +ENABLE_UART = y +ENABLE_PORTIO_UART = y +ENABLE_INITRD = y + +DBG_MEM = y + +CFLAGS = \ + -ffreestanding \ + -mno-sse \ + -nostdlib \ + -fno-pie \ + -fno-pic \ + -g \ + -D__$(TARGET_MACHINE)__ \ + -D__$(TARGET_BOOTLOADER)__ \ + -DPROCESSORS_MAX=$(PROCESSORS_MAX) \ + -Iinclude \ + -I. + +LDFLAGS = -nostdlib \ + -z max-page-size=0x1000 \ + -T link/$(TARGET_TRIPLET).ld + +ifeq "$(TARGET_MACHINE)" "x86_64" + CFLAGS += -m64 \ + -mno-red-zone \ + -mcmodel=kernel \ + -mfsgsbase +endif + +ifeq "$(ENABLE_UART)" "y" +CFLAGS += -DENABLE_UART +endif +ifeq "$(ENABLE_PORTIO_UART)" "y" +CFLAGS += -DENABLE_PORTIO_UART +endif +ifeq "$(ENABLE_INITRD)" "y" +CFLAGS += -DENABLE_INITRD +endif + +ifeq "$(DBG_MEM)" "y" +CFLAGS += -DDBG_MEM +endif diff --git a/device/initrd.c b/device/initrd.c new file mode 100644 index 0000000..36eefc6 --- /dev/null +++ b/device/initrd.c @@ -0,0 +1,116 @@ +#if defined(ENABLE_INITRD) + +#include <stddef.h> + +#include "device/initrd.h" +#include "device/processor.h" +#include "object.h" +#include "jove.h" +#include "bootargs.h" +#include "print.h" +#include "string.h" +#include "panic.h" + +struct jove_ObjectDirectory s_initrd_dir = { + .entries = { + [0] = (objdir_entry_t){ + .type = KO_OBJECT_DIRECTORY, + .data = 1 + } + } +}; + +static uintptr_t s_initrd_getaddr(char *path); + +#if defined(__limine__) + +#include "boot/limine/limine.h" +static struct limine_module_request s_module_request = { + .id = LIMINE_MODULE_REQUEST +}; + +static uintptr_t +s_initrd_getaddr(char *path) +{ + struct limine_module_response *response = s_module_request.response; + for(size_t i = 0; i < response->module_count; i++) { + struct limine_file *module = response->modules[i]; + if(strcmp(path, module->path) == 0) + return (uintptr_t)module->address; + } + kpanic("Failed to load module at path %s\n", path); +} + + +#endif + +static size_t +s_tar_oct_dec(const char *oct) +{ + size_t r = 0; + for(; *oct; oct++) r = (r * 8) + (*oct - '0'); + return r; +} + +void +tar_parse(tar_block_t *block) +{ + while(true) { + tar_header_t *header = &block->header; + if(*header->name == 0) return; + if(s_initrd_dir.self.data == OBJECT_DIRECTORY_MAX_ENTRIES) { + klogf("More than %i files in initrd! Skipping the rest.\n", OBJECT_DIRECTORY_MAX_ENTRIES - 1); + } + + size_t file_size = s_tar_oct_dec(header->size); + s_initrd_dir.entries[s_initrd_dir.self.data++] = (objdir_entry_t) { + .type = KO_INITRD_FILE, + .data = (uintptr_t)block + }; + + klogf("Load file '%s' at %p size %x\n", block->header.name, block + 1, file_size); + + block = &block[(file_size / 512) + 1]; + if(file_size % 512) block++; + } +} + +void +initrd_setup(void) +{ + char *initrd_path = bootargs_getarg("initrd"); + if(initrd_path == 0) { + kpanic("Missing kernel commandline argument for initrd."); + } + + uintptr_t initrd_addr = s_initrd_getaddr(initrd_path); + klogf("Initrd found at %p\n", initrd_addr); + tar_parse((tar_block_t*)initrd_addr); + + size_t initrd_diri = _initDirectory.self.data++; + _initDirectory.entries[initrd_diri] = (objdir_entry_t) { + .type = KO_OBJECT_DIRECTORY, + .data = (uintptr_t)&s_initrd_dir + }; + //Add initrd dir to init object + ((init_data_t*)_initDirectory.entries[1].data)->initrd_dir = initrd_diri; +} + +int +initrd_file_size(tar_header_t *file) +{ + return s_tar_oct_dec(file->size); +} + +tar_header_t* +initrd_find_file(const char *filename) +{ + for(size_t i = 0; i < s_initrd_dir.self.data - 1; i++) { + objdir_entry_t *entry = &s_initrd_dir.entries[i+1]; + tar_header_t *file = (tar_header_t*)entry->data; + if(strcmp(file->name + 1, filename) == 0) return file; + } + return NULL; +} + +#endif diff --git a/device/portio_uart.c b/device/portio_uart.c new file mode 100644 index 0000000..00800b5 --- /dev/null +++ b/device/portio_uart.c @@ -0,0 +1,68 @@ +#ifdef ENABLE_PORTIO_UART +#include "jove.h" +#include "device/portio_uart.h" +#include "device/portio.h" + +static void +s_set_int(ioport_t dev, uint8_t enable) +{ + port_outb(PORTIO_UART_COM_IER(dev), enable); +} + +static void +s_set_baud(ioport_t dev, uint16_t baud) +{ + /* Enable DLAB */ + port_outb(PORTIO_UART_COM_LCR(dev), PORTIO_UART_COM_LCR(dev) | 0x80); + /* Write low & high. */ + port_outb(PORTIO_UART_COM_DLAB_DLL(dev), (uint8_t)(baud & 0xFF)); + port_outb(PORTIO_UART_COM_DLAB_DLH(dev), (uint8_t)((baud >> 8) & 0xFF)); + /* Disable DLAB */ + port_outb(PORTIO_UART_COM_LCR(dev), PORTIO_UART_COM_LCR(dev) & 0x7F); +} + +static void +s_set_fcr(ioport_t dev, uint8_t flg) +{ + port_outb(PORTIO_UART_COM_FCR(dev), flg); +} + +static void +s_set_lcr(ioport_t dev, uint8_t flg) +{ + port_outb(PORTIO_UART_COM_LCR(dev), flg); +} + +static void +s_set_mcr(ioport_t dev, uint8_t flg) +{ + port_outb(PORTIO_UART_COM_MCR(dev), flg); +} + +void +portio_uart_setup(void) +{ + s_set_int(PORTIO_UART_COM1, 0); + s_set_baud(PORTIO_UART_COM1, 3); + s_set_lcr(PORTIO_UART_COM1, 1 | 2); + s_set_fcr(PORTIO_UART_COM1, 1 | 2 | 4 | 0xC0); + s_set_fcr(PORTIO_UART_COM1, 1 | 2 | 8); + s_set_mcr(PORTIO_UART_COM1, 1 | 2 | 4 | 8); + + uint64_t serial_i = _initDirectory.self.data++; + _initDirectory.entries[serial_i] = (struct jove_ObjectDirectoryEntry) { + .type = KO_DEV_UART, + .data = PORTIO_UART_COM1 + }; + _initData.log_object = serial_i; +} + +void +portio_uart_write(uint64_t dev, const char *s, int n) +{ + for(int i = 0; i < n; i++) { + port_outb(dev, s[i]); + } +} + +#endif diff --git a/device/uart.c b/device/uart.c new file mode 100644 index 0000000..6728ab1 --- /dev/null +++ b/device/uart.c @@ -0,0 +1,13 @@ +#include "device/uart.h" +#include "device/portio_uart.h" + +void +uart_write(objdir_t *dir, uint64_t entryi, const char *s, size_t w) +{ + objdir_entry_t *entry = objdir_seek(dir, entryi); + if(entry == NULL || entry->type != KO_DEV_UART) return; + +#ifdef ENABLE_PORTIO_UART + portio_uart_write(entry->data, s, w); +#endif +} diff --git a/include/arch/x86_64/idt.h b/include/arch/x86_64/idt.h new file mode 100644 index 0000000..90ce48b --- /dev/null +++ b/include/arch/x86_64/idt.h @@ -0,0 +1,18 @@ +#ifndef _JOVE_x86_64_IDT_H +#define _JOVE_x86_64_IDT_H 1 + +#include "processor.h" + +typedef struct jove_IVTState +{ + uint64_t r15, r14, r13, r12, r11, r10, r9, r8; + uint64_t rbp, rdi, rsi, rdx, rcx, rbx, rax; + uint64_t rip, cs, rflags, rsp, ss; +} ivt_state_t; + +void kpanic_state(ivt_state_t *state, const char *fmt, ...); + +void ivt_setup(void); +void idt_setup(processor_t *processor); + +#endif diff --git a/include/arch/x86_64/object.h b/include/arch/x86_64/object.h new file mode 100644 index 0000000..8d5af33 --- /dev/null +++ b/include/arch/x86_64/object.h @@ -0,0 +1,26 @@ +#ifndef _JOVE_x86_64_OBJECT_H +#define _JOVE_x86_64_OBJECT_H 1 + +#include <stdint.h> + +typedef struct jove_InitData +{ + uint8_t log_object; + uint8_t untyped_data_dir; + uint8_t processor_dir; + uint8_t pm_object; //Page mapping object. + uint8_t initrd_dir; //Init ramdisk files directory. + uint8_t tcb_object; + uint8_t kernel_stack_object; + uint8_t message_object; + uintptr_t message_object_address; +} init_data_t; + +typedef struct jove_ThreadControlBlock +{ + void *stack; + uintptr_t sp, ksp; + void *pml4; +} tcb_t; + +#endif diff --git a/include/arch/x86_64/page.h b/include/arch/x86_64/page.h new file mode 100644 index 0000000..99bc691 --- /dev/null +++ b/include/arch/x86_64/page.h @@ -0,0 +1,41 @@ +#ifndef _JOVE_ARCH_x86_64_PAGE_H +#define _JOVE_ARCH_x86_64_PAGE_H 1 + +#include <stdint.h> +#include "include/object.h" + +typedef union jove_PageMapLevelEntry +{ + struct { + uint8_t p : 1; /* Present */ + uint8_t rw : 1; /* Read/write. 0 for RO.*/ + uint8_t us : 1; /* User/supervisor. 0 for DPL3 forbid */ + uint8_t pwt : 1; + uint8_t pcd : 1; + uint8_t a : 1; /* Accessed */ + uint8_t d : 1; /* Dirty */ + uint8_t ps_pat : 1; + uint8_t g : 1; /* Global */ + uint8_t _r0 : 2; + uint8_t r : 1; + uint64_t paddr : 35; + uint8_t _r1; + uint8_t pk : 4; + uint8_t xd : 1; + }__attribute__((packed)); + uint64_t value; +} __attribute__((packed)) pmle_t; + +typedef uint16_t pmli_t; + +#define PML_SHL(l) ((l * 9) + 3) +#define PML_I_FOR_LAYER(v, l) ((v >> PML_SHL(l)) % 512) + +uintptr_t vmem_ident_tophys(void *vptr); +void *vmem_phys_tovirt(uintptr_t pptr); + +void *pmle_get_page(pmle_t entry); + +int untyped_retype_page(objdir_entry_t *untyped_entry, void **dest_ptr); + +#endif diff --git a/include/arch/x86_64/processor.h b/include/arch/x86_64/processor.h new file mode 100644 index 0000000..f8a93ce --- /dev/null +++ b/include/arch/x86_64/processor.h @@ -0,0 +1,66 @@ +#ifndef _JOVE_ARCH_x86_64_PROCESSOR_H +#define _JOVE_ARCH_x86_64_PROCESSOR_H 1 + +#include "memory.h" +#include "tables.h" +#include "object.h" +#include <stdint.h> + +#define MSR_FS_BASE 0xC0000100 +#define MSR_GS_BASE 0xC0000101 +#define MSR_KGS_BASE 0xC0000102 + +#define MSR_EFER 0xC0000080 +#define MSR_STAR 0xC0000081 +#define MSR_LSTAR 0xC0000082 +#define MSR_SFMASK 0xC0000084 + +typedef struct jove_TSS +{ + uint32_t resv0; + uint64_t rsp[3]; + uint32_t resv1; + uint64_t ist[8]; + uint32_t resv2[2]; + uint16_t resv3; + uint16_t iopb; +} tss_t; + +enum +{ + GDT_ENTRY_KERNEL_NULL = 0, + GDT_ENTRY_KERNEL_CODE, + GDT_ENTRY_KERNEL_DATA, + GDT_ENTRY_USER_NULL, + GDT_ENTRY_USER_DATA, + GDT_ENTRY_USER_CODE, + GDT_ENTRY_TSS_LOW, + GDT_ENTRY_TSS_HIGH, + GDT_ENTRY_COUNT +}; + +typedef struct jove_Processor +{ + physptr_t pdir; + struct jove_ObjectDirectory *odir; + + segment_descriptor_t gdt[GDT_ENTRY_COUNT]; + struct { + uint16_t length; + uint64_t base; + } __attribute__((packed)) gdtr; + struct { + uint16_t length; + uint64_t base; + } __attribute__((packed)) idtr; + + tss_t tss; + tcb_t *tcb; +} processor_t; + +void gdt_setup(processor_t *processor); + +void rdmsr(uint32_t msr, uint32_t *lo, uint32_t *hi); +void wrmsr(uint32_t msr, uint32_t lo, uint32_t hi); + +#endif diff --git a/include/arch/x86_64/tables.h b/include/arch/x86_64/tables.h new file mode 100644 index 0000000..42651a1 --- /dev/null +++ b/include/arch/x86_64/tables.h @@ -0,0 +1,51 @@ +#ifndef _JOVE_ARCH_x86_64_TABLES_H +#define _JOVE_ARCH_x86_64_TABLES_H 1 + +#include <stdint.h> + +#define CD_SEGMENT_TYPE_ACCESSED 1 +#define CD_SEGMENT_TYPE_WRITEABLE 2 +#define CD_SEGMENT_TYPE_DATA_EXPAND_DOWN 4 +#define CD_SEGMENT_TYPE_CODE_CONFORMING 4 +#define CD_SEGMENT_TYPE_CODE 8 + +#define S_SEGMENT_TYPE_LDT 2 +#define S_SEGMENT_TYPE_TSS_AVAIL 9 +#define S_SEGMENT_TYPE_TSS_BUSY 11 +#define S_SEGMENT_TYPE_CALLGATE 12 +#define S_SEGMENT_TYPE_INT_GATE 14 +#define S_SEGMENT_TYPE_TRAP_GATE 15 + +typedef struct jove_SegmentDescriptor +{ + uint16_t limit_0_15; /* Segment limit. */ + uint16_t base_0_15; /* Segment base. */ + uint8_t base_16_23; + uint8_t type : 4; /* Segment type. */ + uint8_t s : 1; /* Descriptor type (0 = system, 1 = code/data)*/ + uint8_t dpl : 2; /* Descriptor privilege level. */ + uint8_t p : 1; /* Present. */ + uint8_t limit_16_19 : 4; + uint8_t avl : 1; /* Available for use by system software. */ + uint8_t l : 1; /* 64-bit segment (Ext). */ + uint8_t d_b : 1; /* Default operation size (0 = 16-bit, 1 = 32-bit)*/ + uint8_t g : 1; /* Granularity. */ + uint8_t base_24_31; +}__attribute__((packed)) segment_descriptor_t; + +typedef struct jove_InterruptGate +{ + uint16_t base_0_15; + uint16_t segment_selector; + uint8_t ist : 3; + uint8_t zero_0 : 5; + uint8_t type : 4; + uint8_t zero_1 : 1; + uint8_t dpl : 2; + uint8_t p : 1; + uint16_t base_16_31; + uint32_t base_32_63; + uint32_t resv; +}__attribute__((packed)) interrupt_gate_t; + +#endif diff --git a/include/boot.h b/include/boot.h new file mode 100644 index 0000000..989b4b9 --- /dev/null +++ b/include/boot.h @@ -0,0 +1,7 @@ +#ifndef _JOVE_BOOT_H +#define _JOVE_BOOT_H 1 + +extern char *jove_bootargs; +extern int jove_bootargs_len; + +#endif diff --git a/include/bootargs.h b/include/bootargs.h new file mode 100644 index 0000000..94910f4 --- /dev/null +++ b/include/bootargs.h @@ -0,0 +1,13 @@ +#ifndef _JOVE_BOOTARGS_H +#define _JOVE_BOOTARGS_H 1 + +/*Layout: + * [KEY]=[VALUE] + * All values are seperated by spaces.*/ + +/**@FUNC Get the value associated with the given key. + * @PARAM key string to search for. + * @RETURN value associated with key. */ +char *bootargs_getarg(const char *key); + +#endif diff --git a/include/device/initrd.h b/include/device/initrd.h new file mode 100644 index 0000000..eaf3157 --- /dev/null +++ b/include/device/initrd.h @@ -0,0 +1,29 @@ +#ifndef _JOVE_DEV_INITRD_H +#define _JOVE_DEV_INITRD_H 1 + +#include <stddef.h> + +typedef struct tarHeader +{ + char name[100]; + char mode[8]; + char owner[8]; + char group[8]; + char size[12]; + char modified[12]; + char checksum[8]; + char link; + char linkname[100]; +} tar_header_t; + +typedef union tarBlock { + tar_header_t header; + char data[512]; +} tar_block_t; + +void initrd_setup(void); + +tar_header_t *initrd_find_file(const char *filename); +int initrd_file_size(tar_header_t *header); + +#endif diff --git a/include/device/portio.h b/include/device/portio.h new file mode 100644 index 0000000..76b28c4 --- /dev/null +++ b/include/device/portio.h @@ -0,0 +1,26 @@ +#ifndef _JOVE_PORTIO_H +#define _JOVE_PORTIO_H 1 + +#include <stdint.h> + +typedef uint16_t ioport_t; + +static inline uint8_t port_inb(ioport_t port) +{ uint8_t r; __asm__ volatile("inb %w1, %b0": "=a"(r): "Nd"(port): "memory"); return r; } + +static inline uint16_t port_inw(ioport_t port) +{ uint16_t r; __asm__ volatile("inw %w1, %w0": "=a"(r): "Nd"(port): "memory"); return r; } + +static inline uint32_t port_inl(ioport_t port) +{ uint32_t r; __asm__ volatile("inl %d1, %l0": "=a"(r): "Nd"(port): "memory"); return r; } + +static inline void port_outb(ioport_t port, uint8_t v) +{ __asm__ volatile("outb %b0, %w1":: "a"(v), "Nd"(port): "memory"); } + +static inline void port_outw(ioport_t port, uint16_t v) +{ __asm__ volatile("outb %w0, %w1":: "a"(v), "Nd"(port): "memory"); } + +static inline void port_outl(ioport_t port, uint32_t v) +{ __asm__ volatile("outb %d0, %w1":: "a"(v), "Nd"(port): "memory"); } + +#endif diff --git a/include/device/portio_uart.h b/include/device/portio_uart.h new file mode 100644 index 0000000..1b521a1 --- /dev/null +++ b/include/device/portio_uart.h @@ -0,0 +1,27 @@ +#ifndef _JOVE_DEVICE_PORTIO_UART +#define _JOVE_DEVICE_PORTIO_UART 1 + +#include <stdint.h> + +#define PORTIO_UART_COM1 0x3F8 + +#define PORTIO_UART_COM_THR(COM) COM +#define PORTIO_UART_COM_RBR(COM) COM +#define PORTIO_UART_COM_DLAB_DLL(COM) COM +#define PORTIO_UART_COM_IER(COM) (COM + 1) +#define PORTIO_UART_COM_DLAB_DLH(COM) (COM + 1) +#define PORTIO_UART_COM_IIR(COM) (COM + 2) +#define PORTIO_UART_COM_FCR(COM) (COM + 2) +#define PORTIO_UART_COM_LCR(COM) (COM + 3) +#define PORTIO_UART_COM_MCR(COM) (COM + 4) +#define PORTIO_UART_COM_LSR(COM) (COM + 5) +#define PORTIO_UART_COM_MSR(COM) (COM + 6) +#define PORTIO_UART_COM_SR(COM) (COM + 7) + +void +portio_uart_setup(void); + +void +portio_uart_write(uint64_t dev, const char *s, int n); + +#endif diff --git a/include/device/processor.h b/include/device/processor.h new file mode 100644 index 0000000..33deed4 --- /dev/null +++ b/include/device/processor.h @@ -0,0 +1,22 @@ +#ifndef _JOVE_DEVICE_PROCESSOR_H +#define _JOVE_DEVICE_PROCESSOR_H 1 + +#if defined(__x86_64__) +#include "arch/x86_64/processor.h" +#endif + +/**@FUNC Initialize the bootstrap processor.*/ +void bsp_setup(void); +/**@FUNC Initialize the given processor with kernel-specific values. + * Generically: + * Instantiates a new kernel object representing the passed processor. + * For x86_64: + * Loads and uses the generic GDT and IDT. + * @PARAM processor processor to initialize.*/ +void processor_setup(void* processor); + +/**@FUNC Returns the processor struct this function is called by. + * @RET pointer to current processor.*/ +void *processor_current(void); + +#endif diff --git a/include/device/uart.h b/include/device/uart.h new file mode 100644 index 0000000..3d481a2 --- /dev/null +++ b/include/device/uart.h @@ -0,0 +1,11 @@ +#ifndef _JOVE_DEVICE_UART_H +#define _JOVE_DEVICE_UART_H 1 + +#include <stdint.h> +#include <stddef.h> + +#include "object.h" + +void uart_write(objdir_t *dir, uint64_t entryi, const char *s, size_t w); + +#endif diff --git a/include/error.h b/include/error.h new file mode 100644 index 0000000..8aa087c --- /dev/null +++ b/include/error.h @@ -0,0 +1,8 @@ +#ifndef _JOVE_ERROR_H +#define _JOVE_ERROR_H 1 + +#define E_OK 0 +#define E_BADOBJ 1 +#define E_ARGUMENT 2 + +#endif diff --git a/include/init.h b/include/init.h new file mode 100644 index 0000000..aa722b1 --- /dev/null +++ b/include/init.h @@ -0,0 +1,10 @@ +#ifndef _JOVE_INIT_H +#define _JOVE_INIT_H 1 + +#ifdef __x86_64__ + +#endif + +void init_load(void); + +#endif diff --git a/include/jove.h b/include/jove.h new file mode 100644 index 0000000..cbe1a77 --- /dev/null +++ b/include/jove.h @@ -0,0 +1,15 @@ +#ifndef _JOVE_H +#define _JOVE_H 1 + +#define NORETURN __attribute__((noreturn)) + +extern void (*_kernel_start)(void); +extern void (*_kernel_end)(void); + +#include "object.h" +extern objdir_t _initDirectory; +extern init_data_t _initData; + +NORETURN void hcf(void); + +#endif diff --git a/include/memory.h b/include/memory.h new file mode 100644 index 0000000..e29c41b --- /dev/null +++ b/include/memory.h @@ -0,0 +1,17 @@ +#ifndef _JOVE_MEMORY_H +#define _JOVE_MEMORY_H 1 + +#include <stdint.h> +#include "object.h" + +typedef uintptr_t physptr_t; +typedef uintptr_t virtptr_t; + +#define KERNEL_STACK_SIZE 0x1000 + +void pmem_setup(void); +void vmem_setup(void); + +int untyped_retype_kernel_stack(objdir_entry_t *untyped_entry, objdir_entry_t *dest_entry); + +#endif diff --git a/include/object.h b/include/object.h new file mode 100644 index 0000000..0a71b5f --- /dev/null +++ b/include/object.h @@ -0,0 +1,61 @@ +#ifndef _JOVE_OBJECT_H +#define _JOVE_OBJECT_H + +#include <stdint.h> + +#if defined(__x86_64__) +#include "arch/x86_64/object.h" +#endif + +enum +{ + /* Generic objects */ + KO_NONE = 0, + KO_OBJECT_DIRECTORY, + KO_INIT_DATA, + KO_MEMORY_UNTYPED, + KO_MEMORY_MAPPED_PAGE, //4KiB Fixed Width + KO_MEMORY_MAPPING_PAGE, //4Kib Fixed Width + KO_INITRD_FILE, + KO_TCB, + KO_KERNEL_STACK, + KO_MESSAGE, + /* Device objects*/ + KO_DEV_INVALID = 0x100, + KO_DEV_PROCESSOR, + KO_DEV_UART +}; + +typedef uintmax_t obj_path_t; +typedef uint16_t obj_type_t; + +typedef struct jove_ObjectDirectoryEntry +{ + obj_type_t type; + union { + struct { + char lock : 1; + char u0 : 7; + char u1; +}; + unsigned short flg; + }; + uintmax_t data; +} objdir_entry_t; + +#define OBJECT_DIRECTORY_MAX_ENTRIES 256 + +//The first entry always represents itself, which allows the kernel to ignore +//some checks. +//The data variable also contains the next free index for this directory. +typedef struct jove_ObjectDirectory +{ + union { + objdir_entry_t self; + objdir_entry_t entries[OBJECT_DIRECTORY_MAX_ENTRIES]; + }; +} objdir_t; + +objdir_entry_t *objdir_seek(objdir_t *dir, uintmax_t index); + +#endif diff --git a/include/panic.h b/include/panic.h new file mode 100644 index 0000000..076a425 --- /dev/null +++ b/include/panic.h @@ -0,0 +1,9 @@ +#ifndef _JOVE_PANIC_H +#define _JOVE_PANIC_H 1 + +#include "jove.h" + +#define kpanic(...) _kpanic(__FILE__, __LINE__, __VA_ARGS__) +NORETURN void _kpanic(const char *file, int line, const char *fmt, ...); + +#endif diff --git a/include/print.h b/include/print.h new file mode 100644 index 0000000..f4c942c --- /dev/null +++ b/include/print.h @@ -0,0 +1,17 @@ +#ifndef _JOVE_LIB_PRINT_H +#define _JOVE_LIB_PRINT_H 1 + +#include <stdarg.h> + +void _klogf(const char *file, const char *func, int line, const char *fmt, ...); +#define klogf(...) _klogf(__FILE__, __FUNCTION__, __LINE__, __VA_ARGS__) + +int kprintf(const char *fmt, ...); +int ksprintf(char *s, const char *fmt, ...); +int ksnprintf(char *s, int size, const char *fmt, ...); + +int kvprintf(const char *fmt, va_list ap); +int kvsprintf(char *s, const char *fmt, va_list ap); +int kvsnprintf(char *s, int size, const char *fmt, va_list ap); + +#endif diff --git a/include/string.h b/include/string.h new file mode 100644 index 0000000..a821f1c --- /dev/null +++ b/include/string.h @@ -0,0 +1,25 @@ +#ifndef _JOVE_STRING_H +#define _JOVE_STRING_H 1 + +#include <stdbool.h> +#include <stddef.h> + +/**@FUNC Writes the given integer to the string using a custom base. + * If the integer is larger than size, s will stop being written to at s[size-1]. + * Returns the number of characters written, or would have been written. + * @PARAM s buffer to write to. + * @PARAM size size of buffer to write to. + * @PARAM l integer to write. + * @PARAM sign whether the integer is signed or unsigned. + * @PARAM radix base to write at. + * @RETURN number of characters in number.*/ +int ltostr(char *s, int size, unsigned long l, bool sign, int radix); + +size_t strlen(const char *s); +int strcmp(const char *s1, const char *s2); + +void *memset(void *dest, char c, size_t n); +void *memcpy(void *dest, const void *src, size_t n); +void *memmove(void *dest, const void *src, size_t n); + +#endif diff --git a/include/syscall.h b/include/syscall.h new file mode 100644 index 0000000..c9c26d8 --- /dev/null +++ b/include/syscall.h @@ -0,0 +1,46 @@ +#ifndef _JOVE_SYSCALL_H +#define _JOVE_SYSCALL_H 1 + +#include <stdint.h> +#include <stddef.h> +#include "object.h" + +enum +{ + SYSCALL_NONE = 0, + SYSCALL_INVOKE, + SYSCALL_SEND, + SYSCALL_RECV, + + SYSCALL_DEBUG_PUTC, + SYSCALL_DEBUG_IDENTIFY +}; + +typedef uint16_t invokeid_t; + +struct syscallInvokeHeader +{ + obj_path_t target_path; + invokeid_t func_id; +}; + +enum +{ + INVOKE_OBJDIR_NMEMB = 0, + INVOKE_OBJDIR_GETMEMB +}; + +struct syscallInvoke_objdir_nmemb +{ + struct syscallInvokeHeader header; + size_t value; +}; + +struct syscallInvoke_objdir_getmemb +{ + struct syscallInvokeHeader header; + uint8_t member; + obj_type_t value; +}; + +#endif diff --git a/include/tcb.h b/include/tcb.h new file mode 100644 index 0000000..6545083 --- /dev/null +++ b/include/tcb.h @@ -0,0 +1,18 @@ +#ifndef _JOVE_TCB_H +#define _JOVE_TCB_H 1 + +#include <stddef.h> +#include <stdint.h> + +typedef struct jove_ThreadControlBlock +{ + uintmax_t id; + struct jove_ThreadControlBlock *children; + struct jove_ThreadControlBlock *next; + + void *stack; + uintptr_t sp, ksp; + void *pml4; +} tcb_t; + +#endif diff --git a/lib/itoa.c b/lib/itoa.c new file mode 100644 index 0000000..57f0c22 --- /dev/null +++ b/lib/itoa.c @@ -0,0 +1,32 @@ +#include "string.h" + +int +ltostr(char *s, int size, unsigned long l, bool sign, int radix) +{ + unsigned wsize = 0; + unsigned digits = 0; + if((long)l < 0 && sign) { + l = -((long)l); + if(size > wsize && s != 0) s[wsize] = '-'; + wsize++; + } + + for(unsigned long lv = l; lv != 0; lv /= radix) { + digits++; + } + + if(digits-- == 0) { + if(size > wsize && s != 0) s[wsize] = '0'; + wsize++; + } + + for(unsigned long lv = l; lv != 0; lv /= radix) { + int digit = lv % radix; + if(size > digits - wsize && s != 0) { + s[digits - wsize] = (digit >= 10 ? (digit + 'a' - 10) : digit + '0'); + } + wsize++; + } + if(size > wsize && s != 0) s[wsize] = 0; + return ++wsize; +} diff --git a/lib/log.c b/lib/log.c new file mode 100644 index 0000000..6bc770d --- /dev/null +++ b/lib/log.c @@ -0,0 +1,14 @@ +#include "print.h" +#include <stdarg.h> + +void +_klogf(const char *file, const char *func, int line, const char *fmt, ...) +{ + kprintf("%s:%i ", func, line); + + va_list ap; + va_start(ap, fmt); + kvprintf(fmt, ap); + va_end(ap); + +} diff --git a/lib/mem.c b/lib/mem.c new file mode 100644 index 0000000..2ce67f3 --- /dev/null +++ b/lib/mem.c @@ -0,0 +1,27 @@ +#include "string.h" + +void* +memset(void *dest, char c, size_t n) +{ + char *a = dest; + for(size_t i = 0; i < n; i++) a[i] = c; + return dest; +} + +void* +memcpy(void *dest, const void *src, size_t n) +{ + char *a = dest; + const char *b = src; + for(size_t i = 0; i < n; i++) a[i] = b[i]; + return dest; +} + +void* +memmove(void *dest, const void *src, size_t n) +{ + char buffer[n]; + memcpy(buffer, src, n); + memcpy(dest, buffer, n); + return dest; +} diff --git a/lib/objdir.c b/lib/objdir.c new file mode 100644 index 0000000..665da4c --- /dev/null +++ b/lib/objdir.c @@ -0,0 +1,15 @@ +#include "object.h" + +objdir_entry_t* +objdir_seek(objdir_t *dir, uintmax_t index) +{ + uint8_t tli = index & 0xFF; + if(tli == 0) return &dir->self; + + objdir_entry_t *entry = &dir->entries[tli]; + if(entry->type == KO_OBJECT_DIRECTORY) { + if(tli == index) return entry; + return objdir_seek((objdir_t*)entry->data, index >> 8); + } + return entry; +} diff --git a/lib/panic.c b/lib/panic.c new file mode 100644 index 0000000..bfe80e8 --- /dev/null +++ b/lib/panic.c @@ -0,0 +1,15 @@ +#include "panic.h" +#include "print.h" + +NORETURN void +_kpanic(const char *file, int line, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + + kprintf("KERNEL PANIC:%s:%i: \n", file, line); + kvprintf(fmt, ap); + + va_end(ap); + hcf(); +} diff --git a/lib/print.c b/lib/print.c new file mode 100644 index 0000000..45c7671 --- /dev/null +++ b/lib/print.c @@ -0,0 +1,146 @@ +#include "print.h" +#include "device/uart.h" +#include "jove.h" +#include "string.h" +#include <stddef.h> +#include <stdbool.h> + +int +kprintf(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + int r = kvprintf(fmt, ap); + va_end(ap); + return r; +} + +int +ksprintf(char *s, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + int r = kvsprintf(s, fmt, ap); + va_end(ap); + return r; +} + +int +ksnprintf(char *s, int size, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + int r = kvsnprintf(s, size, fmt, ap); + va_end(ap); + return r; +} + +int kvprintf(const char *fmt, va_list ap) +{ + va_list ap_dup; + va_copy(ap_dup, ap); + int size = kvsnprintf(0, 0, fmt, ap); + char buffer[size]; + kvsnprintf(buffer, size, fmt, ap_dup); + +#ifdef ENABLE_UART + uart_write(&_initDirectory, _initData.log_object, buffer, size - 1); +#endif + return size; +} + +int +kvsprintf(char *s, const char *fmt, va_list ap) +{ + return kvsnprintf(s, 65536, fmt, ap); +} + +int +kvsnprintf(char *s, int size, const char *fmt, va_list ap) +{ + const char *fmtc = fmt; + int wsize = 0; + do{ + if(*fmtc != '%') { + if(s != NULL && wsize < size) s[wsize] = *fmtc; + wsize++; + continue; + } + + bool sign = true; + bool caps = true; + int radix = 10; + int lpad = 0; + int paramw = sizeof(int); + + if(*(++fmtc) == 0) goto done; + switch(*fmtc) { + case 'c': + if(s != NULL && wsize < size) s[wsize] = va_arg(ap, int); + wsize++; + break; + case 'p': //fallthrough; + paramw = sizeof(uintptr_t); + lpad = paramw * 2; + case 'X': //fallthrough + case 'x': //fallthrough + sign = false; + radix = 16; + goto printint; + break; + case 'u': //fallthrough + sign = false; + goto printint; + break; + case 'i': + goto printint; + break; + case 's': + goto printstr; + break; + default: + break; + } + continue; +printstr: + { + char *arg_s = va_arg(ap, char*); + if(arg_s == 0) arg_s = "(NULL)"; + size_t ssize = strlen(arg_s); + for(char *c = arg_s; *c; c++) { + if(s != NULL && wsize < size) s[wsize] = *c; + wsize++; + } + } + continue; +printint: + { + int isize = 1; + void *iptr = NULL; + if(s != NULL) { + iptr = &s[wsize]; + } + switch(paramw) { + case sizeof(int): isize = ltostr(iptr, size - wsize, va_arg(ap, int), sign, radix); break; + case sizeof(uintptr_t): isize = ltostr(iptr, size - wsize, va_arg(ap, long), sign, radix); break; + default: break; + } + if(lpad) { + if(isize < lpad) { + lpad -= isize - 1; + if(s != 0) { + memmove(&s[wsize + lpad], &s[wsize], isize); + memset(&s[wsize], '0', lpad); + } + isize += lpad; + + } + } + wsize += isize; + continue; + } + }while(*(fmtc++) != 0); +done: + if(s != NULL && wsize < size) s[wsize++] = 0; + return wsize; +} diff --git a/lib/strcmp.c b/lib/strcmp.c new file mode 100644 index 0000000..da6a51a --- /dev/null +++ b/lib/strcmp.c @@ -0,0 +1,8 @@ +#include "string.h" + +int +strcmp(const char *s1, const char *s2) +{ + while(*s1 && *s2 && *(s1++) == *(s2++)); + return *s2 - *s1; +} diff --git a/lib/strlen.c b/lib/strlen.c new file mode 100644 index 0000000..da34697 --- /dev/null +++ b/lib/strlen.c @@ -0,0 +1,9 @@ +#include "string.h" + +size_t +strlen(const char *s) +{ + size_t l = 0; + for(; s[l]; l++); + return l; +} diff --git a/link/x86_64-jove.ld b/link/x86_64-jove.ld new file mode 100644 index 0000000..0686617 --- /dev/null +++ b/link/x86_64-jove.ld @@ -0,0 +1,52 @@ +OUTPUT_FORMAT(elf64-x86-64) +OUTPUT_ARCH(i386:x86-64) + +ENTRY(_start) + +PAGESIZE = CONSTANT(MAXPAGESIZE); + +PHDRS +{ + text PT_LOAD FLAGS((1 << 0) | (1 << 2)) ; /* RO EX*/ + rodata PT_LOAD FLAGS((1 << 2)) ; /* RO*/ + data PT_LOAD FLAGS((1 << 1) | (1 << 2)); /* RW*/ + bss PT_LOAD FLAGS((1 << 1) | (1 << 2)); /* RW*/ +} + +SECTIONS +{ + . = 0xFFFFFFFF80000000; + _kernel_start = .; + + .text BLOCK(PAGESIZE) : ALIGN(PAGESIZE) { + *(.init) + *(.text .text.*) + + . = ALIGN(8); + _kernel_ctors_start = .; + *(.ctors) + _kernel_ctors_end = .; + + *(.fini) + } :text + + .rodata BLOCK(PAGESIZE) : ALIGN(PAGESIZE) { + *(.rodata .rodata.*) + } :rodata + + .data BLOCK(PAGESIZE) : ALIGN(PAGESIZE) { + *(.data .data.*) + } :data + + .bss BLOCK(PAGESIZE) : ALIGN(PAGESIZE) { + *(COMMON) + *(.bss .bss.*) + } :bss + + /DISCARD/ : { + *(.note .note.*) + } + + _kernel_end = .; +} + @@ -0,0 +1,44 @@ +#include "jove.h" +#include "object.h" +#include "memory.h" +#include "print.h" +#include "init.h" +#include "device/portio_uart.h" +#include "device/processor.h" +#include "device/initrd.h" + +struct jove_ObjectDirectory _initDirectory = { + + .entries = { + [0] = { + .type = KO_OBJECT_DIRECTORY, + .data = 1 + }, + } +}; +init_data_t _initData; + +void +_jove_main(void) +{ + bsp_setup(); +#ifdef ENABLE_PORTIO_UART + portio_uart_setup(); +#endif + + pmem_setup(); + vmem_setup(); +#ifdef ENABLE_INITRD + initrd_setup(); +#endif + + init_load(); + kprintf("Reached end of kernel main!\n"); + hcf(); +} + +NORETURN void +hcf(void) +{ + for(;;) __asm__ volatile("hlt"); +} diff --git a/syscall/handler.c b/syscall/handler.c new file mode 100644 index 0000000..efab179 --- /dev/null +++ b/syscall/handler.c @@ -0,0 +1,66 @@ +#include "syscall.h" +#include "object.h" +#include "handles.h" +#include "device/processor.h" +#include "print.h" +#include <stdint.h> +#include <stddef.h> + +static int +s_syscall_handle_invoke(objdir_t *root_dir, void *payload) +{ + struct syscallInvokeHeader *invokeHeader = payload; + uintmax_t target_path = invokeHeader->target_path; + objdir_entry_t *target_entry = objdir_seek(root_dir, target_path); + + klogf("target entry %x:%p func %i\n", target_path, target_entry->data, invokeHeader->func_id); + + if(target_entry == NULL) return -1; + switch(target_entry->type) { + case KO_NONE: + return -1; + case KO_OBJECT_DIRECTORY: + return syscall_handle_invoke_objdir(root_dir, (objdir_t*)target_entry->data, payload); + default: + klogf("Missing implementation of invoke for type %i\n", target_entry->type); + return -1; + } +} + +int +_syscall_handler(uintmax_t argsi, int calli) +{ + objdir_t *objdir = ((processor_t*)processor_current())->odir; + objdir_entry_t *payload_entry = objdir_seek(objdir, argsi); + if(payload_entry == NULL) { + klogf("Missing object for syscall %i (%x)\n", calli, argsi); + return -1; + } + if(payload_entry->type != KO_MESSAGE) { + klogf("Tried to invoke syscall %i with invalid object %x\n", calli, argsi); + return -1; + } + + uintmax_t *payload = (void*)payload_entry->data; + + switch(calli) { + case SYSCALL_INVOKE: return s_syscall_handle_invoke(objdir, payload); + case SYSCALL_DEBUG_PUTC: + kprintf("%c", (char)payload[0]); + return 0; + case SYSCALL_DEBUG_IDENTIFY: + { + objdir_entry_t *target_entry = objdir_seek(objdir, payload[0]); + if(payload_entry == NULL) { + payload[0] = KO_NONE; + } else { + payload[0] = target_entry->type; + } + return 0; + } + default: + klogf("Invalid syscall %i caught! Failing.\n", calli); + return -1; + } + return -1; +} diff --git a/syscall/handles.h b/syscall/handles.h new file mode 100644 index 0000000..f9fd79c --- /dev/null +++ b/syscall/handles.h @@ -0,0 +1,8 @@ +#ifndef _JOVE_SYSCALL_HANDLES_H +#define _JOVE_SYSCALL_HANDLES_H 1 + +#include "object.h" + +int syscall_handle_invoke_objdir(objdir_t *root_dir, objdir_t *target_dir, void *payload); + +#endif diff --git a/syscall/invoke_objdir.c b/syscall/invoke_objdir.c new file mode 100644 index 0000000..4698f34 --- /dev/null +++ b/syscall/invoke_objdir.c @@ -0,0 +1,34 @@ +#include "handles.h" +#include "syscall.h" + +static int +s_handle_invoke_objdir_nmemb(objdir_t *root_dir, objdir_t *target_dir, void *payload) +{ + struct syscallInvoke_objdir_nmemb *payloadStruct = payload; + payloadStruct->value = target_dir->self.data; + return 0; +} + +static int +s_handle_invoke_objdir_getmemb(objdir_t *root_dir, objdir_t *target_dir, void *payload) +{ + struct syscallInvoke_objdir_getmemb *payloadStruct = payload; + payloadStruct->value = target_dir->entries[payloadStruct->member].type; + return 0; +} + +static int (*s_invoke_handles[])(objdir_t*, objdir_t*, void*) = { + [INVOKE_OBJDIR_NMEMB] = s_handle_invoke_objdir_nmemb, + [INVOKE_OBJDIR_GETMEMB] = s_handle_invoke_objdir_getmemb +}; +static size_t s_invoke_handles_count = sizeof(s_invoke_handles) / sizeof(void*); + +int +syscall_handle_invoke_objdir(objdir_t *root_dir, objdir_t *target_dir, void *payload) +{ + struct syscallInvokeHeader *invokeHeader = payload; + size_t funcid = invokeHeader->func_id; + + if(funcid >= s_invoke_handles_count) return -1; + return s_invoke_handles[funcid](root_dir, target_dir, payload); +} |