diff options
93 files changed, 3412 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6b6772d --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.o +*.a +*.elf +.ccls* diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6697fc6 --- /dev/null +++ b/Makefile @@ -0,0 +1,39 @@ +include config.mk + +CFILES := $(wildcard *.c) +CFILES += $(wildcard */*.c) +CFILES += $(wildcard boot/$(TARGET_BOOTLOADER)/*.c) +CFILES += $(wildcard arch/$(TARGET_MACHINE)/*.c) +CFILES += $(wildcard arch/$(TARGET_MACHINE)/*/*.c) + +SFILES := $(wildcard *.S) +SFILES += $(wildcard */*.S) +SFILES += $(wildcard arch/$(TARGET_MACHINE)/*.S) +SFILES += $(wildcard arch/$(TARGET_MACHINE)/*/*.S) + +OBJFILES := $(patsubst %.c,%.o,$(CFILES)) +OBJFILES += $(patsubst %.S,%.o,$(SFILES)) + +DFILES := $(wildcard *.d) +DFILES += $(wildcard */*.d) +DFILES += $(wildcard arch/$(TARGET_MACHINE)/*.d) +DFILES += $(wildcard arch/$(TARGET_MACHINE)/*/*.d) + +BIN := jove.elf + +all: $(BIN) + +.PHONY: clean +clean: + rm $(OBJFILES) + rm $(DFILES) + rm $(BIN) + +$(BIN): ${OBJFILES} + $(LD) $(LDFLAGS) ${OBJFILES} -o $(BIN) + +%.o:%.c + $(CC) $(CFLAGS) -c $< -o $@ + +%.o:%.S + $(AS) -g -c $< -o $@ diff --git a/abi/permission.h b/abi/permission.h new file mode 100644 index 0000000..5ca56e0 --- /dev/null +++ b/abi/permission.h @@ -0,0 +1,6 @@ +#ifndef JOVE_ABI_PERMISSION_H +#define JOVE_ABI_PERMISSION_H 1 + +#define PERM_MEMSRV 0 + +#endif diff --git a/abi/syscall.h b/abi/syscall.h new file mode 100644 index 0000000..95cd4a5 --- /dev/null +++ b/abi/syscall.h @@ -0,0 +1,29 @@ +#ifndef JOVE_ABI_SYSCALL_H +#define JOVE_ABI_SYSCALL_H 1 + +typedef struct syscall { + int id; +} syscall_t; + +struct syscall_log { + syscall_t syscall; + const char *message; +}; + +enum +{ + SYSCALL_LOG = 0, + SYSCALL_COUNT +}; + +#define _SYSCALL(data) __asm__ volatile("movq %0, %%rdi\nsyscall":: "r"(data)) + +void _syscall_log(const char *message) { + struct syscall_log syscall_data = { + .syscall = (syscall_t){ .id = SYSCALL_LOG }, + .message = message + }; + _SYSCALL(&syscall_data); +} + +#endif diff --git a/arch/arch.h b/arch/arch.h new file mode 100644 index 0000000..6e569fd --- /dev/null +++ b/arch/arch.h @@ -0,0 +1,8 @@ +#ifndef JOVE_ARCH_H +#define JOVE_ARCH_H 1 + +extern void serial_setup(void); + +extern void arch_tables_setup(void); + +#endif diff --git a/arch/x86_64/cpu.h b/arch/x86_64/cpu.h new file mode 100644 index 0000000..942c8d9 --- /dev/null +++ b/arch/x86_64/cpu.h @@ -0,0 +1,13 @@ +#ifndef ARCH_x86_64_CPU_H +#define ARCH_x86_64_CPU_H 1 + +#include <stdint.h> + +struct Registers +{ + uint64_t r15, r14, r13, r12, r11, r10, r9, r8; + uint64_t bp, di, si, dx, cx, bx, ax; + uint64_t ip, cs, flags, sp, ss; +}; + +#endif diff --git a/arch/x86_64/elf.c b/arch/x86_64/elf.c new file mode 100644 index 0000000..60b8f13 --- /dev/null +++ b/arch/x86_64/elf.c @@ -0,0 +1,47 @@ +#include "elf.h" +#include "lib/string.h" +#include "usr/elf.h" +#include "io/log.h" +#include "mem/memory.h" + +void* +elf_load(const void *data, size_t len) +{ + if(data == NULL) return NULL; + if(len < 4) return NULL; + + struct ELF_header *ehdr = (struct ELF_header*)data; + if(ehdr->e_ident[EI_MAG0] != E_IDENT_MAG0 && + ehdr->e_ident[EI_MAG1] != E_IDENT_MAG1 && + ehdr->e_ident[EI_MAG2] != E_IDENT_MAG2 && + ehdr->e_ident[EI_MAG3] != E_IDENT_MAG3) + return NULL; + + if(ehdr->e_ident[EI_CLASS] != E_IDENT_CLASS64) { + klogf("Jove does not support ELF files of class CLASS32\n"); + return NULL; + } + + if(ehdr->e_type != ET_EXEC) { + klogf("Jove does not support ELF files other than ET_EXEC\n"); + return NULL; + } + + uint64_t entry = ehdr->e_entry; + + size_t phdrc = ehdr->e_phnum; + struct ELF_phdr *phdrs = (struct ELF_phdr*)((uintptr_t)data + ehdr->e_phoff); + + for(size_t phdri = 0; phdri < phdrc; phdri++) + { + struct ELF_phdr *phdr = &phdrs[phdri]; + void *pdata = (void*)phdr->p_vaddr; + + mem_ensure_range(phdr->p_vaddr, phdr->p_vaddr + phdr->p_memsz, true, true); + if(phdr->p_type == PT_LOAD) + { + memcpy(pdata, (void*)((uintptr_t)data + phdr->p_offset), phdr->p_filesz); + } + } + return (void*)entry; +} diff --git a/arch/x86_64/elf.d b/arch/x86_64/elf.d new file mode 100644 index 0000000..f57bbe6 --- /dev/null +++ b/arch/x86_64/elf.d @@ -0,0 +1,2 @@ +arch/x86_64/elf.o: arch/x86_64/elf.c arch/x86_64/elf.h lib/string.h \ + usr/elf.h io/log.h mem/memory.h mem/slab.h diff --git a/arch/x86_64/elf.h b/arch/x86_64/elf.h new file mode 100644 index 0000000..ebcd228 --- /dev/null +++ b/arch/x86_64/elf.h @@ -0,0 +1,78 @@ +#ifndef JOVE_ARCH_x86_64_ELF_H +#define JOVE_ARCH_x86_64_ELF_H 1 + +#include <stdint.h> + +#define EI_MAG0 0 +#define EI_MAG1 1 +#define EI_MAG2 2 +#define EI_MAG3 3 +#define EI_CLASS 4 +#define EI_DATA 5 +#define EI_VERSION 6 +#define EI_OSABI 7 +#define EI_ABIVERSION 8 +#define EI_PAD 9 + +#define E_IDENT_MAG0 0x7F +#define E_IDENT_MAG1 'E' +#define E_IDENT_MAG2 'L' +#define E_IDENT_MAG3 'F' + +#define E_IDENT_CLASS32 1 +#define E_IDENT_CLASS64 2 + +#define ET_EXEC 0x02 + +struct ELF_header +{ + uint8_t e_ident[15]; + uint16_t e_type; + uint16_t e_machine; + uint32_t e_version; + uint64_t e_entry; + uint64_t e_phoff; + uint64_t e_shoff; + uint32_t e_flags; + uint16_t e_ehsize; + uint16_t e_phentsize; + uint16_t e_phnum; + uint16_t e_shentsize; + uint16_t e_shnum; + uint16_t e_shstrndx; +}; + +#define PT_NULL 0 +#define PT_LOAD 1 + +#define PF_X 1 +#define PF_W 2 +#define PF_R 4 + +struct ELF_phdr +{ + uint32_t p_type; + uint32_t p_flags; + uint64_t p_offset; + uint64_t p_vaddr; + uint64_t p_paddr; + uint64_t p_filesz; + uint64_t p_memsz; + uint64_t p_align; +}; + +struct ELF_shdr +{ + uint32_t sh_name; + uint32_t sh_type; + uint64_t sh_flags; + uint64_t sh_addr; + uint64_t sh_offset; + uint64_t sh_size; + uint32_t sh_link; + uint32_t sh_info; + uint64_t sh_addralign; + uint64_t sh_entsize; +}; + +#endif diff --git a/arch/x86_64/gdt.c b/arch/x86_64/gdt.c new file mode 100644 index 0000000..d996dcb --- /dev/null +++ b/arch/x86_64/gdt.c @@ -0,0 +1,125 @@ +#include "tables.h" +#include "tss.h" + +enum +{ + GDT_SEGMENT_KERNEL_NULL = 0, + GDT_SEGMENT_KERNEL_CODE, + GDT_SEGMENT_KERNEL_DATA, + + GDT_SEGMENT_USER_NULL, + GDT_SEGMENT_USER_DATA, + GDT_SEGMENT_USER_CODE, + + GDT_SEGMENT_TSS_LOW, + GDT_SEGMENT_TSS_HIGH, + + GDT_SEGMENT_COUNT +}; + +__attribute__((aligned(0x1000))) +static struct SegmentDescriptor s_gdtd[GDT_SEGMENT_COUNT] = +{ + { 0 }, /* Kernel NULL */ + { /* Kernel Code (64-bit RO EX DPL0) */ + .limit_0_15 = 0xFFFF, + .base_0_15 = 0, + .base_16_23 = 0, + .type = CD_SEGMENT_TYPE_CODE | CD_SEGMENT_TYPE_CODE_CONFORMING, + .s = 1, + .dpl = 0, + .p = 1, + .limit_16_19 = 0xF, + .l = 1, + .d_b = 0, + .g = 0, + .base_24_31 = 0 + }, + { /* Kernel Data (64-bit RW DPL0) */ + .limit_0_15 = 0xFFFF, + .base_0_15 = 0, + .base_16_23 = 0, + .type = CD_SEGMENT_TYPE_WRITEABLE, + .s = 1, + .dpl = 0, + .p = 1, + .limit_16_19 = 0xF, + .l = 1, + .d_b = 0, + .g = 0, + .base_24_31 = 0 + }, + { 0 }, /* User NULL */ + { /* User Data (64-bit RO EX DPL3)*/ + .limit_0_15 = 0xFFFF, + .base_0_15 = 0, + .base_16_23 = 0, + .type = CD_SEGMENT_TYPE_WRITEABLE, + .s = 1, + .dpl = 3, + .p = 1, + .limit_16_19 = 0xF, + .l = 1, + .d_b = 0, + .g = 0, + .base_24_31 = 0, + }, + { /* User Code (64-bit RO EX DPL3)*/ + .limit_0_15 = 0xFFFF, + .base_0_15 = 0, + .base_16_23 = 0, + .type = CD_SEGMENT_TYPE_CODE | CD_SEGMENT_TYPE_CODE_CONFORMING, + .s = 1, + .dpl = 3, + .p = 1, + .limit_16_19 = 0xF, + .l = 1, + .d_b = 0, + .g = 0, + .base_24_31 = 0, + }, + { /* TSS Low */ + .limit_0_15 = sizeof(struct TSS), + .base_0_15 = 0, + .base_16_23 = 0, + .type = S_SEGMENT_TYPE_TSS_AVAIL, + .avl = 1, + .s = 0, + .dpl = 0, + .p = 1, + .limit_16_19 = 0, + .l = 1, + .d_b = 0, + .g = 0, + .base_24_31 = 0, + }, + { 0 } +}; +static struct XDTR s_gdtr = { + .length = sizeof(s_gdtd) - 1, + .address = (uintptr_t)&s_gdtd +}; + +static struct TSS s_tss = { + +}; + +extern void x86_64_lgdt(struct XDTR *gdtr); +extern void x86_64_flush_tss(void); +void +x86_64_load_gdt(void) +{ + { + struct SegmentDescriptor *tss_lo = &s_gdtd[GDT_SEGMENT_TSS_LOW]; + struct SegmentDescriptor *tss_hi = &s_gdtd[GDT_SEGMENT_TSS_HIGH]; + uintptr_t tssb = (uintptr_t)&s_tss; + tss_lo->base_0_15 = tssb & 0xFFFF; + tss_lo->base_16_23 = (tssb >> 16) & 0xFF; + tss_lo->base_24_31 = (tssb >> 24) & 0xFF; + tss_hi->limit_0_15 = (tssb >> 32) & 0xFFFF; + tss_hi->base_0_15 = (tssb >> 48) & 0xFFFF; + } + + x86_64_lgdt(&s_gdtr); + x86_64_flush_tss(); +} diff --git a/arch/x86_64/gdt.d b/arch/x86_64/gdt.d new file mode 100644 index 0000000..7b43bdb --- /dev/null +++ b/arch/x86_64/gdt.d @@ -0,0 +1,2 @@ +arch/x86_64/gdt.o: arch/x86_64/gdt.c arch/x86_64/tables.h \ + arch/x86_64/tss.h diff --git a/arch/x86_64/idt.c b/arch/x86_64/idt.c new file mode 100644 index 0000000..567dead --- /dev/null +++ b/arch/x86_64/idt.c @@ -0,0 +1,70 @@ +#include "tables.h" +#include "cpu.h" +#include "lib/jove.h" +#include "io/log.h" + +PAGEALIGN +static struct InterruptTrapGate s_idtd[48]; +static struct InterruptState *(*s_int_handlers[48])(struct Registers*); +static struct XDTR s_idtr = { + .length = sizeof(s_idtd) - 1, + .address = (uintptr_t)&s_idtd +}; + +uint64_t __isr_err; +uint64_t __isr_num; + +struct Registers* +irq_handle(struct Registers *state) +{ + if(__isr_num < 48) { + if(s_int_handlers[__isr_num] != NULL) { + s_int_handlers[__isr_num](state); + return state; + } + } + klogf("Interrupt %i\nerror code %#016X\n", __isr_num, __isr_err); + klogf("IP: %#016X\n", state->ip); + klogf("AX: %#016X\n", state->ax); + klogf("BX: %#016X\n", state->bx); + klogf("CX: %#016X\n", state->cx); + klogf("DX: %#016X\n", state->dx); + klogf("SI: %#016X\n", state->si); + klogf("DI: %#016X\n", state->di); + klogf("BP: %#016X\n", state->bp); + klogf("R8: %#016X\n", state->r8); + klogf("R9: %#016X\n", state->r9); + klogf("R10: %#016X\n", state->r10); + klogf("R11: %#016X\n", state->r11); + klogf("R12: %#016X\n", state->r12); + klogf("R13: %#016X\n", state->r13); + klogf("R14: %#016X\n", state->r14); + klogf("R15: %#016X\n", state->r15); + kpanic("Unhandled exception\n"); + return state; +} + +extern void x86_64_lidt(struct XDTR *idtr); +void +x86_64_load_idt(void) +{ + extern uintptr_t __isr_stubs[22]; + for(int i = 0; i < 22; i++) + { + uintptr_t base = __isr_stubs[i]; + klogf("INT %2i : %#016X\n", i, base); + s_idtd[i] = (struct InterruptTrapGate){ + .base_0_15 = (base & 0xFFFF), + .segment_selector = 0x10, + .ist = 0, + .zero_0 = 0, + .type = 0xE, + .zero_1 = 0, + .dpl = 0, + .p = 1, + .base_16_31 = (base >> 16) & 0xFFFF, + .base_32_63 = (base >> 32) & 0xFFFFFFFF + }; + } + x86_64_lidt(&s_idtr); +} diff --git a/arch/x86_64/idt.d b/arch/x86_64/idt.d new file mode 100644 index 0000000..7e976e7 --- /dev/null +++ b/arch/x86_64/idt.d @@ -0,0 +1,2 @@ +arch/x86_64/idt.o: arch/x86_64/idt.c arch/x86_64/tables.h \ + arch/x86_64/cpu.h lib/jove.h io/log.h diff --git a/arch/x86_64/int.S b/arch/x86_64/int.S new file mode 100644 index 0000000..96c62d9 --- /dev/null +++ b/arch/x86_64/int.S @@ -0,0 +1,131 @@ +.section .text +.global x86_64_lidt +.type x86_64_lidt @function +x86_64_lidt: + lidt (%rdi) + retq + +.macro irq_preserve + pushq %rax + pushq %rbx + pushq %rcx + pushq %rdx + pushq %rsi + pushq %rdi + pushq %rbp + pushq %r8 + pushq %r9 + pushq %r10 + pushq %r11 + pushq %r12 + pushq %r13 + pushq %r14 + pushq %r15 +.endm + +.macro irq_restore + popq %r15 + popq %r14 + popq %r13 + popq %r12 + popq %r11 + popq %r10 + popq %r9 + popq %r8 + popq %rbp + popq %rdi + popq %rsi + popq %rdx + popq %rcx + popq %rbx + popq %rax +.endm + +.global irq_stub +.type irq_stub @function +irq_stub: + irq_preserve + + movq %rsp, %rdi + cld + + .extern irq_handle + call irq_handle + movq %rax, %rsp + + irq_restore + addq $128, %rsp + iretq + +.extern __isr_err +.extern __isr_num + +.macro isr_error num:req +.type __isr\num @function +isr\num: + subq $128, %rsp + pushq %rbx + movq 8(%rsp), %rbx + movq %rbx, __isr_err + popq %rbx + addq $8, %rsp + movq $\num, __isr_num + jmp irq_stub +.endm + +.macro isr_noerr num:req +.type __isr\num @function +isr\num: + movq $\num, __isr_num + jmp irq_stub +.endm + +isr_noerr 0 +isr_noerr 1 +isr_noerr 2 +isr_noerr 3 +isr_noerr 4 +isr_noerr 5 +isr_noerr 6 +isr_noerr 7 +isr_error 8 +isr_noerr 9 +isr_error 10 +isr_error 11 +isr_error 12 +isr_error 13 +isr_error 14 +isr_noerr 15 +isr_noerr 16 +isr_error 17 +isr_noerr 18 +isr_noerr 19 +isr_noerr 20 +isr_error 21 + +.section .data +.global __isr_stubs +.type __isr_stubs @object +__isr_stubs: + .quad isr0 + .quad isr1 + .quad isr2 + .quad isr3 + .quad isr4 + .quad isr5 + .quad isr6 + .quad isr7 + .quad isr8 + .quad isr9 + .quad isr10 + .quad isr11 + .quad isr12 + .quad isr13 + .quad isr14 + .quad isr15 + .quad isr16 + .quad isr17 + .quad isr18 + .quad isr19 + .quad isr20 + .quad isr21 diff --git a/arch/x86_64/loadgdt.S b/arch/x86_64/loadgdt.S new file mode 100644 index 0000000..e183850 --- /dev/null +++ b/arch/x86_64/loadgdt.S @@ -0,0 +1,24 @@ +.global x86_64_lgdt +.type x86_64_lgdt @function +x86_64_lgdt: + lgdt (%rdi) +.reload_segments: + pushq $0x8 + leaq .reload_cs, %rax + pushq %rax + retfq +.reload_cs: + movw $0x10, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + movw %ax, %ss + retq + +.global x86_64_flush_tss +.type x86_64_flush_tss @function +x86_64_flush_tss: + movw $0x30, %ax + ltr %ax + retq diff --git a/arch/x86_64/paging.c b/arch/x86_64/paging.c new file mode 100644 index 0000000..76b5735 --- /dev/null +++ b/arch/x86_64/paging.c @@ -0,0 +1,202 @@ +#include "paging.h" +#include <stddef.h> +#include "lib/jove.h" +#include "io/log.h" +#include "lib/string.h" +#include "boot/boot.h" + +extern void *_kernel_end; + +PAGEALIGN static uint64_t s_kernel_initial_pml4[512]; +PAGEALIGN static uint64_t s_kernel_initial_pml3[2][512]; +PAGEALIGN static uint64_t s_kernel_initial_pml2[2][512]; +PAGEALIGN static uint64_t s_kernel_initial_pml1[2][512]; +static struct PageDirectory s_kernel_initial_pd; + +struct PageDirectory *mem_current_pd; + +physptr_t +mem_linear_tophys_koffset(uintptr_t virt) +{ + return (virt - boot_kernel_virtual_base) + boot_kernel_physical_address; +} + +uintptr_t +mem_phys_tolinear(physptr_t phys) +{ + return (uintptr_t)(phys + 0xFFFF800000000000ULL); +} + +static size_t +s_paging_pmle(size_t l, uintptr_t addr) +{ + size_t shift = (12 + (9 * l)); + return (addr & (0x1FFULL << shift)) >> shift; +} + +static union PageEntry* +s_paging_fetch_table(union PageEntry *pt, size_t l, uintptr_t virt) +{ + size_t pmle = s_paging_pmle(l, virt); + union PageEntry entry = pt[pmle]; + bool entry_new = false; + if(!entry.p) { + entry_new = true; + entry.value = mem_phys_take4k(); + entry.p = 1; + entry.rw = 1; + entry.us = 1; + pt[pmle] = entry; + } + union PageEntry *table = (union PageEntry*)(mem_phys_tolinear(entry.paddr << 12)); + if(entry_new) memset(table, 0, PAGESIZE); + return table; +} + +static union PageEntry* +s_paging_get_table(union PageEntry *pt, size_t l, uintptr_t virt) +{ + if(pt == NULL) return NULL; + size_t pmle = s_paging_pmle(l, virt); + union PageEntry entry = pt[pmle]; + if(!entry.p) return NULL; + return (union PageEntry*)(mem_phys_tolinear(entry.paddr << 12)); +} + +physptr_t +mem_linear_tophys(uintptr_t virt) +{ + struct PageDirectory *pd = mem_current_pd; + union PageEntry *pml3 = s_paging_get_table(pd->pml4_vaddr, 3, virt); + union PageEntry *pml2 = s_paging_get_table(pd->pml4_vaddr, 3, virt); + union PageEntry *pml1 = s_paging_get_table(pd->pml4_vaddr, 3, virt); + if(pml1 == NULL) return 0; + + size_t pml1i = s_paging_pmle(0, virt); + + if(!pml1[pml1i].p) return 0; + return pml1[pml1i].paddr << 12; +} + +bool +mem_check_ptr(const void *ptr) +{ + if(ptr == NULL) return false; + return mem_linear_tophys((uintptr_t)ptr) != 0; +} + +void +mem_paging_map4k(struct PageDirectory *pd, physptr_t phys, uintptr_t virt, uint8_t flg) +{ + union PageEntry *pml3 = s_paging_fetch_table(pd->pml4_vaddr, 3, virt); + union PageEntry *pml2 = s_paging_fetch_table(pml3, 2, virt); + union PageEntry *pml1 = s_paging_fetch_table(pml2, 1, virt); + size_t pml1e = s_paging_pmle(0, virt); + + pml1[pml1e] = (union PageEntry) { + .p = (flg & 1) > 0, + .rw = (flg & 2) > 0, + .us = (flg & 4) > 0, + .paddr = phys >> 12 + }; +} + +union PageEntry +mem_paging_fetch4k(struct PageDirectory *pd, uintptr_t virt) +{ + union PageEntry *pml3 = s_paging_fetch_table(pd->pml4_vaddr, 3, virt); + union PageEntry *pml2 = s_paging_fetch_table(pml3, 2, virt); + union PageEntry *pml1 = s_paging_fetch_table(pml2, 1, virt); + return pml1[s_paging_pmle(0, virt)]; +} + +void +mem_pd_ensure_4k(struct PageDirectory *pd, uintptr_t virt, uint8_t flg) +{ + union PageEntry pml1e = mem_paging_fetch4k(pd, virt); + if(!pml1e.p) { + uintptr_t phys = mem_phys_take4k(); + mem_paging_map4k(pd, phys, virt, flg); + } +} + +void +mem_pd_ensure_range(struct PageDirectory *pd, uintptr_t from, uintptr_t to, uint8_t flg) +{ + from &= ~0xFFF; + for(; from < to; from += PAGESIZE) + mem_pd_ensure_4k(pd, from, flg); +} + +void +mem_ensure_range(uintptr_t from, uintptr_t to, bool rw, bool user) +{ + mem_pd_ensure_range(mem_current_pd, from, to, 1 | (rw << 1) | (user << 2)); +} + +void mem_pd_new(struct PageDirectory *pd) +{ + physptr_t pml4p = mem_phys_take4k(); + union PageEntry *pml4 = (union PageEntry*)mem_phys_tolinear(pml4p); + memset(pml4, 0, PAGESIZE); + memcpy(&pml4[256], &pd->pml4_vaddr[256], PAGESIZE / 2); + + *pd = (struct PageDirectory){ + .pml4_vaddr = pml4, + .pml4_paddr = pml4p, + .references = 1 + }; +} + +void mem_pd_clone(struct PageDirectory *pd, struct PageDirectory *parent) +{ + mem_pd_new(pd); + for(size_t i = 0; i < 256; i++) { + + } +} + +void +mem_paging_setup(void) +{ + memset(s_kernel_initial_pml4, 0, PAGESIZE); + memset(s_kernel_initial_pml3, 0, 2 * PAGESIZE); + memset(s_kernel_initial_pml2, 0, 2 * PAGESIZE); + memset(s_kernel_initial_pml1, 0, PAGESIZE); + s_kernel_initial_pd = (struct PageDirectory){ + .pml4_vaddr = (union PageEntry*)&s_kernel_initial_pml4, + .pml4_paddr = mem_linear_tophys_koffset((uintptr_t)&s_kernel_initial_pml4), + .references = 1 + }; + mem_current_pd = &s_kernel_initial_pd; + + /* Map first few GiBs */ + s_kernel_initial_pml4[256] = + mem_linear_tophys_koffset((uintptr_t)&s_kernel_initial_pml3[0]) + | 3; + s_kernel_initial_pml3[0][0] = + mem_linear_tophys_koffset((uintptr_t)&s_kernel_initial_pml2[0]) + | 3; + for(int i = 0; i < 512; i++) { + s_kernel_initial_pml2[0][i] = (i * (PAGESIZE * 512)) | 0x80 | 3; + } + + size_t kernel_pml4e = (boot_kernel_virtual_base >> (39)); + size_t kernel_pml3e = (boot_kernel_virtual_base >> (30)) % 512; + size_t kernel_pml2e = (boot_kernel_virtual_base >> (21)) % 512; + size_t kernel_npages = ((((uintptr_t)&_kernel_end) - boot_kernel_virtual_base) >> 12) + 1; + klogf("Kernel has %i pages\n", kernel_npages); + + /* Map kernel pages */ + s_kernel_initial_pml4[511] = + mem_linear_tophys_koffset((uintptr_t)&s_kernel_initial_pml3[1]) | 3; + s_kernel_initial_pml3[1][kernel_pml3e] = + mem_linear_tophys_koffset((uintptr_t)&s_kernel_initial_pml2[1]) | 3; + s_kernel_initial_pml2[1][kernel_pml2e] = + mem_linear_tophys_koffset((uintptr_t)&s_kernel_initial_pml1[0]) | 3; + for(int i = 0; i < kernel_npages; i++) { + s_kernel_initial_pml1[0][i] = (i * PAGESIZE) + boot_kernel_physical_address | 3; + } + + __asm__ volatile("mov %0, %%cr3":: "r"(s_kernel_initial_pd.pml4_paddr)); +} diff --git a/arch/x86_64/paging.d b/arch/x86_64/paging.d new file mode 100644 index 0000000..bb3ea83 --- /dev/null +++ b/arch/x86_64/paging.d @@ -0,0 +1,2 @@ +arch/x86_64/paging.o: arch/x86_64/paging.c arch/x86_64/paging.h \ + mem/memory.h mem/slab.h lib/jove.h io/log.h lib/string.h boot/boot.h diff --git a/arch/x86_64/paging.h b/arch/x86_64/paging.h new file mode 100644 index 0000000..1e88a0b --- /dev/null +++ b/arch/x86_64/paging.h @@ -0,0 +1,44 @@ +#ifndef JOVE_ARCH_x86_64_PAGING_H +#define JOVE_ARCH_x86_64_PAGING_H 1 + +#include <stdint.h> +#include "mem/memory.h" + +union PageEntry +{ + struct { + uint8_t p : 1; /* Present */ + uint8_t rw : 1; /* Read/write. 0 for RO.*/ + uint8_t us : 1; /* User/supervisor. 0 for DPL3 forbid */ + uint8_t pwt : 1; + uint8_t pcd : 1; + uint8_t a : 1; /* Accessed */ + uint8_t d : 1; /* Dirty */ + uint8_t ps_pat : 1; + uint8_t g : 1; /* Global */ + uint8_t _r0 : 2; + uint8_t r : 1; + uint64_t paddr : 35; + uint8_t _r1; + uint8_t pk : 4; + uint8_t xd : 1; + }__attribute__((packed)); + uint64_t value; +}__attribute__((packed)); + +struct PageDirectory +{ + union PageEntry *pml4_vaddr; + physptr_t pml4_paddr; + size_t references; +}; + +extern struct PageDirectory *mem_current_pd; + +void mem_pd_new(struct PageDirectory *pd); +void mem_pd_clone(struct PageDirectory *pd, struct PageDirectory *parent); + +void mem_pd_ensure_4k(struct PageDirectory *pd, uintptr_t virt, uint8_t flg); +void mem_pd_ensure_range(struct PageDirectory *pd, uintptr_t from, uintptr_t to, uint8_t flg); + +#endif diff --git a/arch/x86_64/savestate.S b/arch/x86_64/savestate.S new file mode 100644 index 0000000..c89933b --- /dev/null +++ b/arch/x86_64/savestate.S @@ -0,0 +1,39 @@ +.global saveregs +.type saveregs @function +saveregs: + pushq %rax + pushq %rbx + pushq %rcx + pushq %rdx + pushq %rsi + pushq %rdi + pushq %rbp + pushq %r8 + pushq %r9 + pushq %r10 + pushq %r11 + pushq %r12 + pushq %r13 + pushq %r14 + pushq %r15 + retq + +.global loadregs +.type loadregs @function +loadregs: + popq %r15 + popq %r14 + popq %r13 + popq %r12 + popq %r11 + popq %r10 + popq %r9 + popq %r8 + popq %rbp + popq %rdi + popq %rsi + popq %rdx + popq %rcx + popq %rbx + popq %rax + diff --git a/arch/x86_64/serial.c b/arch/x86_64/serial.c new file mode 100644 index 0000000..1b49e3f --- /dev/null +++ b/arch/x86_64/serial.c @@ -0,0 +1,62 @@ +#include "serial.h" +#include "uart.h" +#include "io/log.h" + +static struct LogDevice s_serial_logdev = + { .out = serial_out, .chain = NULL }; + +bool serial_supported = true; + +void +serial_setup(void) +{ + /* Disable interrupts. */ + poutb(SERIAL_UART_COM_IER(SERIAL_UART_COM1), 0x0); + /* Enable DLAB. */ + poutb(SERIAL_UART_COM_LCR(SERIAL_UART_COM1), 0x80); + /* Set divisor to 3 (38400 baud)*/ + poutb(SERIAL_UART_COM_DLAB_DLL(SERIAL_UART_COM1), 3); + poutb(SERIAL_UART_COM_DLAB_DLH(SERIAL_UART_COM1), 0); + /* Set flags for LCR (8 bits, no parity, one stop)*/ + poutb(SERIAL_UART_COM_LCR(SERIAL_UART_COM1), 1 | 2); + /* Enable & clear FIFO, 14-byte threshold */ + poutb(SERIAL_UART_COM_FCR(SERIAL_UART_COM1), 1 | 2 | 4 | 0xC0); + /* Enable interrupts, set RTS/DSR. */ + poutb(SERIAL_UART_COM_MCR(SERIAL_UART_COM1), 1 | 2 | 8); + /* Set loopback mode for testing. */ + poutb(SERIAL_UART_COM_MCR(SERIAL_UART_COM1), 2 | 4 | 8 | 0x10); + /* Test serial output. */ + poutb(SERIAL_UART_COM1, 0xAE); + if(pinb(SERIAL_UART_COM1) != 0xAE) { + serial_supported = false; + return; + } + + /* Serial is not faulty. + * No loopback, enable output 1 & 2.*/ + poutb(SERIAL_UART_COM_MCR(SERIAL_UART_COM1), 1 | 2 | 4 | 8); + + klog_newdev(&s_serial_logdev); +} + +static ALWAYS_INLINE bool +serial_transmit_empty(uint16_t com) +{ + return pinb(SERIAL_UART_COM_LSR(com) & 0x20); +} + +static inline void +serial_outb(uint16_t com, uint8_t b) +{ + if(b == '\n') serial_outb(com, '\r'); + while(!serial_transmit_empty(SERIAL_UART_COM1)); + poutb(com, b); +} + +void +serial_out(const char *s, size_t len) +{ + if(!serial_supported) return; + for(; len > 0; len--) + serial_outb(SERIAL_UART_COM1, *(s++)); +} diff --git a/arch/x86_64/serial.d b/arch/x86_64/serial.d new file mode 100644 index 0000000..a0b0756 --- /dev/null +++ b/arch/x86_64/serial.d @@ -0,0 +1,2 @@ +arch/x86_64/serial.o: arch/x86_64/serial.c arch/x86_64/serial.h \ + arch/x86_64/uart.h lib/jove.h io/log.h diff --git a/arch/x86_64/serial.h b/arch/x86_64/serial.h new file mode 100644 index 0000000..ecb896d --- /dev/null +++ b/arch/x86_64/serial.h @@ -0,0 +1,27 @@ +#ifndef JOVE_KERNEL_ARCH_x86_64_SERIAL_H +#define JOVE_KERNEL_ARCH_x86_64_SERIAL_H 1 + +#include <stdbool.h> +#include <stddef.h> + +#define SERIAL_UART_COM1 0x3F8 +#define SERIAL_UART_COM2 0x2F8 + +#define SERIAL_UART_COM_THR(COM) COM +#define SERIAL_UART_COM_RBR(COM) COM +#define SERIAL_UART_COM_DLAB_DLL(COM) COM +#define SERIAL_UART_COM_IER(COM) (COM + 1) +#define SERIAL_UART_COM_DLAB_DLH(COM) (COM + 1) +#define SERIAL_UART_COM_IIR(COM) (COM + 2) +#define SERIAL_UART_COM_FCR(COM) (COM + 2) +#define SERIAL_UART_COM_LCR(COM) (COM + 3) +#define SERIAL_UART_COM_MCR(COM) (COM + 4) +#define SERIAL_UART_COM_LSR(COM) (COM + 5) +#define SERIAL_UART_COM_MSR(COM) (COM + 6) +#define SERIAL_UART_COM_SR(COM) (COM + 7) + +extern bool serial_supported; + +void serial_out(const char *s, size_t len); + +#endif diff --git a/arch/x86_64/syscall.c b/arch/x86_64/syscall.c new file mode 100644 index 0000000..0398f11 --- /dev/null +++ b/arch/x86_64/syscall.c @@ -0,0 +1 @@ +#include "usr/umode.h" diff --git a/arch/x86_64/syscall.d b/arch/x86_64/syscall.d new file mode 100644 index 0000000..bb8f166 --- /dev/null +++ b/arch/x86_64/syscall.d @@ -0,0 +1 @@ +arch/x86_64/syscall.o: arch/x86_64/syscall.c usr/umode.h diff --git a/arch/x86_64/syscall_setup.S b/arch/x86_64/syscall_setup.S new file mode 100644 index 0000000..cbd3220 --- /dev/null +++ b/arch/x86_64/syscall_setup.S @@ -0,0 +1,37 @@ +.extern _kernel_thread_sp +.extern syscall_handler + +.global syscall_entry +.type syscall_entry @function +syscall_entry: + movq %rsp, %rax + movq (_kernel_thread_bp), %rsp + pushq %rax + pushq %rbp + pushq %rcx + pushq %r11 + movq %rsp, %rbp + call syscall_handler + popq %r11 + popq %rcx + popq %rbp + popq %rsp + sysretq + +.global syscall_setup_syscall +.type syscall_setup_syscall @function +syscall_setup_syscall: + movq $0xc0000082, %rcx + leaq syscall_entry, %rdx + mov %edx, %eax + shr $32, %rdx + wrmsr + movq $0xc0000080, %rcx + rdmsr + or $1, %eax + wrmsr + movq $0xc0000081, %rcx + rdmsr + mov $0x00180008, %edx + wrmsr + retq diff --git a/arch/x86_64/tables.c b/arch/x86_64/tables.c new file mode 100644 index 0000000..f530089 --- /dev/null +++ b/arch/x86_64/tables.c @@ -0,0 +1,8 @@ +#include "tables.h" + +void +arch_tables_setup(void) +{ + x86_64_load_gdt(); + x86_64_load_idt(); +} diff --git a/arch/x86_64/tables.d b/arch/x86_64/tables.d new file mode 100644 index 0000000..6b10ebe --- /dev/null +++ b/arch/x86_64/tables.d @@ -0,0 +1 @@ +arch/x86_64/tables.o: arch/x86_64/tables.c arch/x86_64/tables.h diff --git a/arch/x86_64/tables.h b/arch/x86_64/tables.h new file mode 100644 index 0000000..8e43bec --- /dev/null +++ b/arch/x86_64/tables.h @@ -0,0 +1,60 @@ +#ifndef JOVE_ARCH_X86_64_TABLES_H +#define JOVE_ARCH_X86_64_TABLES_H 1 + +#include <stdint.h> + +#define CD_SEGMENT_TYPE_ACCESSED 1 +#define CD_SEGMENT_TYPE_WRITEABLE 2 +#define CD_SEGMENT_TYPE_DATA_EXPAND_DOWN 4 +#define CD_SEGMENT_TYPE_CODE_CONFORMING 4 +#define CD_SEGMENT_TYPE_CODE 8 + +#define S_SEGMENT_TYPE_LDT 2 +#define S_SEGMENT_TYPE_TSS_AVAIL 9 +#define S_SEGMENT_TYPE_TSS_BUSY 11 +#define S_SEGMENT_TYPE_CALLGATE 12 +#define S_SEGMENT_TYPE_INT_GATE 14 +#define S_SEGMENT_TYPE_TRAP_GATE 15 + +struct SegmentDescriptor +{ + uint16_t limit_0_15; /* Segment limit. */ + uint16_t base_0_15; /* Segment base. */ + uint8_t base_16_23; + uint8_t type : 4; /* Segment type. */ + uint8_t s : 1; /* Descriptor type (0 = system, 1 = code/data)*/ + uint8_t dpl : 2; /* Descriptor privilege level. */ + uint8_t p : 1; ; /* Present. */ + uint8_t limit_16_19 : 4; + uint8_t avl : 1; /* Available for use by system software. */ + uint8_t l : 1; /* 64-bit segment (Ext). */ + uint8_t d_b : 1; /* Default operation size (0 = 16-bit, 1 = 32-bit)*/ + uint8_t g : 1; /* Granularity. */ + uint8_t base_24_31; +}__attribute__((packed)); + +struct InterruptTrapGate +{ + uint16_t base_0_15; + uint16_t segment_selector; + uint8_t ist : 3; + uint8_t zero_0 : 5; + uint8_t type : 4; + uint8_t zero_1 : 1; + uint8_t dpl : 2; + uint8_t p : 1; + uint16_t base_16_31; + uint32_t base_32_63; + +}__attribute__((packed)); + +struct XDTR /* Generic table descriptor struct */ +{ + uint16_t length; + uint64_t address; +}__attribute__((packed)); + +void x86_64_load_gdt(void); +void x86_64_load_idt(void); + +#endif diff --git a/arch/x86_64/thread.c b/arch/x86_64/thread.c new file mode 100644 index 0000000..da20067 --- /dev/null +++ b/arch/x86_64/thread.c @@ -0,0 +1,85 @@ +#include "tsk/tasking.h" +#include "mem/memory.h" +#include "io/log.h" +#include "lib/hashtable.h" +#include "lib/string.h" +#include "paging.h" +#include "cpu.h" + +struct ThreadBody { + struct Thread base; + struct PageDirectory pd; + struct Registers state; +}; + +struct Thread *thread_current; +uintptr_t _kernel_thread_bp = 0; + +//static struct SLinkedList s_threads; +static struct HashTable s_threads; + +static struct SlabCache s_thread_cache; +static struct SlabCache s_kbp_cache; + +static tid_t s_thread_id_next = 1; + +static size_t +s_thread_hash_func(const void* tid, size_t _) +{ + return (tid_t)tid; +} + +void +tasking_setup(void) +{ + hashtable_new(&s_threads, struct ThreadBody*, tid_t); + s_threads.hash_function = s_thread_hash_func; + + mem_slabcache_new(&s_thread_cache, "threads", sizeof(struct ThreadBody)); + mem_slabcache_new(&s_kbp_cache, "kernel stacks", 4096); + + struct ThreadBody *kthread = mem_slab_alloc(&s_thread_cache); + *kthread = (struct ThreadBody){ + .base.id = s_thread_id_next++, + .base.kbp = ((uintptr_t)mem_slab_alloc(&s_kbp_cache)) + 0xFFF, + .base.perm = (size_t)-1, + .pd = *mem_current_pd + }; + hashtable_insert(&s_threads, 0, kthread); + + thread_current = (struct Thread*)kthread; + _kernel_thread_bp = thread_current->kbp; +} + +struct Thread* +thread_new(struct Thread *parent) +{ + struct ThreadBody *new = mem_slab_alloc(&s_thread_cache); + memcpy(new, parent, sizeof(struct ThreadBody)); + new->base.id = s_thread_id_next++; + + uintptr_t ksp_base = (uintptr_t)mem_slab_alloc(&s_kbp_cache); + new->base.kbp = ksp_base + 0xFFF; + new->base.perm = parent->perm; + + hashtable_insert(&s_threads, (void*)new->base.id, new); + return (struct Thread*)new; +} + +struct Thread* +thread_get(tid_t id) +{ + struct Thread **thread = hashtable_get(&s_threads, (void*)id, struct Thread*); + if(thread == NULL) return NULL; + return *thread; +} + +void +thread_free(struct Thread *thread) +{ + struct ThreadBody *body = (struct ThreadBody*)thread; + body->pd.references--; + thread->kbp -= 0xFFF; + mem_slab_free(&s_kbp_cache, (void*)(thread->kbp)); + klogf("Need impl for thread_free\n"); +} diff --git a/arch/x86_64/thread.d b/arch/x86_64/thread.d new file mode 100644 index 0000000..c78e4a9 --- /dev/null +++ b/arch/x86_64/thread.d @@ -0,0 +1,3 @@ +arch/x86_64/thread.o: arch/x86_64/thread.c tsk/tasking.h mem/memory.h \ + mem/slab.h io/log.h lib/hashtable.h lib/linkedlist.h lib/string.h \ + arch/x86_64/paging.h arch/x86_64/cpu.h diff --git a/arch/x86_64/tss.h b/arch/x86_64/tss.h new file mode 100644 index 0000000..34ec444 --- /dev/null +++ b/arch/x86_64/tss.h @@ -0,0 +1,18 @@ +#ifndef JOVE_ARCH_x86_64_TSS_H +#define JOVE_ARCH_x86_64_TSS_H 1 + +#include <stddef.h> +#include <stdint.h> + +struct TSS +{ + uint32_t resv0; + uint64_t rsp[3]; + uint64_t resv1; + uint64_t ist[8]; + uint64_t resv2; + uint16_t resv3; + uint16_t iobp; +}; + +#endif diff --git a/arch/x86_64/uart.h b/arch/x86_64/uart.h new file mode 100644 index 0000000..8386eb7 --- /dev/null +++ b/arch/x86_64/uart.h @@ -0,0 +1,19 @@ +#ifndef JOVE_KERNEL_ARCH_x86_64_UART_H +#define JOVE_KERNEL_ARCH_x86_64_UART_H 1 + +#include "lib/jove.h" +#include <stdint.h> + +ALWAYS_INLINE uint8_t pinb(uint16_t port) +{ + uint8_t v; + __asm__ volatile("inb %1, %0": "=a"(v): "Nd"(port)); + return v; +} + +ALWAYS_INLINE void poutb(uint16_t port, uint8_t b) +{ + __asm__ volatile("outb %0, %1":: "a"(b), "Nd"(port)); +} + +#endif diff --git a/arch/x86_64/umode_enter.S b/arch/x86_64/umode_enter.S new file mode 100644 index 0000000..29aa4e3 --- /dev/null +++ b/arch/x86_64/umode_enter.S @@ -0,0 +1,8 @@ +.global umode_enter +.type umode_enter @function +umode_enter: + movq %rdi, %rcx + movq %rsi, %rsp + movq $0x0202, %r11 + cli + sysretq diff --git a/boot/boot.h b/boot/boot.h new file mode 100644 index 0000000..d7571d8 --- /dev/null +++ b/boot/boot.h @@ -0,0 +1,40 @@ +#ifndef JOVE_BOOT_H +#define JOVE_BOOT_H 1 + +#include <stdint.h> +#include <stddef.h> +#include <stdbool.h> + +struct MemoryMapEntry { + uintptr_t base; + size_t length; + bool usable; +}; + +#define MEMORY_MAP_MAX_ENTRIES 64 +struct MemoryMap { + size_t count; + struct MemoryMapEntry entries[MEMORY_MAP_MAX_ENTRIES]; +}; + +#define BOOT_MODULE_MAX_ENTRIES 4 +struct BootModule { + char *path; + char *cmdline; + size_t size; + uintptr_t addr; +}; + +#define KERNEL_INITIAL_STACK_WIDTH (0x1000 * 4) +extern void *boot_kernel_initial_stack_base; +extern uintptr_t boot_kernel_physical_address; +extern const char *boot_kernel_cmdline; + +extern struct MemoryMap boot_memorymap; + +extern struct BootModule boot_modules[BOOT_MODULE_MAX_ENTRIES]; +extern size_t boot_module_count; + +#define boot_kernel_virtual_base 0xFFFFFFFF80000000ULL + +#endif diff --git a/boot/cmdline.c b/boot/cmdline.c new file mode 100644 index 0000000..aa6aa05 --- /dev/null +++ b/boot/cmdline.c @@ -0,0 +1,50 @@ +#include "cmdline.h" +#include "boot.h" +#include "mem/memory.h" +#include "lib/string.h" +#include "io/log.h" + +static struct HashTable s_cmdline_kernel; + +const char* +cmdline_get(const char *key) +{ + const char** value = hashtable_get(&s_cmdline_kernel, key, const char*); + if(value == NULL) return NULL; + return *value; +} + +void +cmdline_kernel_setup(void) +{ + hashtable_news(&s_cmdline_kernel, const char*); + size_t cmdi = 0; + + const char *cmdline = boot_kernel_cmdline; + if(cmdline == 0) return; + + while(cmdline[cmdi] != 0) + { + size_t keyi = cmdi; + while(cmdline[keyi] != '=' && cmdline[keyi] != 0) keyi++; + if(cmdline[keyi] == 0 || keyi == 0) { + klogf("kernel cmdline is empty or malformed; skipping.\n"); + break; + } + + size_t keylen = keyi - cmdi; + char *key = mem_alloc(keylen); + memcpy(key, &cmdline[cmdi], keylen); + + size_t valuei = keyi + 1; + while(cmdline[valuei] != ' ' && cmdline[valuei] != 0) valuei++; + + size_t valuelen = (valuei - keyi) - 1; + char *value = mem_alloc(valuelen); + if(valuelen != 0) memcpy(value, &cmdline[keyi + 1], valuelen); + + klogf("%s = %s\n", key, value); + hashtable_insert(&s_cmdline_kernel, key, &value); + cmdi = valuei + 1; + } +} diff --git a/boot/cmdline.d b/boot/cmdline.d new file mode 100644 index 0000000..f3ba401 --- /dev/null +++ b/boot/cmdline.d @@ -0,0 +1,3 @@ +boot/cmdline.o: boot/cmdline.c boot/cmdline.h lib/hashtable.h \ + lib/linkedlist.h boot/boot.h mem/memory.h mem/slab.h lib/string.h \ + io/log.h diff --git a/boot/cmdline.h b/boot/cmdline.h new file mode 100644 index 0000000..b1162b7 --- /dev/null +++ b/boot/cmdline.h @@ -0,0 +1,10 @@ +#ifndef JOVE_BOOT_CMDLINE_H +#define JOVE_BOOT_CMDLINE_H 1 + +#include "lib/hashtable.h" + +const char *cmdline_get(const char *key); + +void cmdline_kernel_setup(void); + +#endif diff --git a/boot/limine/limine.c b/boot/limine/limine.c new file mode 100644 index 0000000..7cc50d0 --- /dev/null +++ b/boot/limine/limine.c @@ -0,0 +1,89 @@ +#include "limine.h" +#include "boot/boot.h" + +static char s_kernel_stack[KERNEL_INITIAL_STACK_WIDTH]; + +struct MemoryMap boot_memorymap; + +struct BootModule boot_modules[BOOT_MODULE_MAX_ENTRIES]; +struct BootModule boot_kernel_file; +size_t boot_module_count = 0; + +uintptr_t boot_kernel_physical_address; +void *boot_kernel_initial_stack_base = &s_kernel_stack; +const char *boot_kernel_cmdline = NULL; + +struct limine_memmap_request s_mmap_request = { + .id = LIMINE_MEMMAP_REQUEST +}; +struct limine_kernel_address_request s_kaddr_request = { + .id = LIMINE_KERNEL_ADDRESS_REQUEST +}; +struct limine_kernel_file_request s_kfile_request = { + .id = LIMINE_KERNEL_FILE_REQUEST +}; +struct limine_module_request s_module_request = { + .id = LIMINE_MODULE_REQUEST +}; + +static void +s_load_memorymap(void) +{ + struct limine_memmap_response *response = s_mmap_request.response; + if(response == NULL) return; + boot_memorymap.count = response->entry_count; + for(size_t i = 0; i < response->entry_count; i++) { + struct limine_memmap_entry *e = response->entries[i]; + struct MemoryMapEntry *bme = &boot_memorymap.entries[i]; + + bme->base = e->base; + bme->length = e->length; + bme->usable = e->type == LIMINE_MEMMAP_USABLE || + e->type == LIMINE_MEMMAP_ACPI_RECLAIMABLE || + e->type == LIMINE_MEMMAP_BOOTLOADER_RECLAIMABLE; + } +} + +static void +s_load_modules(void) +{ + struct limine_module_response *response = s_module_request.response; + if(response->module_count > 4) response->module_count = 4; + boot_module_count = response->module_count; + for(size_t i = 0; i < response->module_count; i++) { + boot_modules[i] = (struct BootModule){ + .path = response->modules[i]->path, + .cmdline = response->modules[i]->cmdline, + .size = response->modules[i]->size, + .addr = (uintptr_t)response->modules[i]->address + }; + } +} + +static void +s_load_kernel_file(void) +{ + struct limine_kernel_file_response *response = s_kfile_request.response; + boot_kernel_file = (struct BootModule) { + .path = response->kernel_file->path, + .cmdline = response->kernel_file->cmdline, + .size = response->kernel_file->size, + .addr = (uintptr_t)response->kernel_file->address + }; + boot_kernel_cmdline = response->kernel_file->cmdline; +} + +void _start(void) +{ + boot_kernel_physical_address = s_kaddr_request.response->physical_base; + s_load_memorymap(); + + s_load_kernel_file(); + s_load_modules(); + + char *kernel_stack_entry = s_kernel_stack + (KERNEL_INITIAL_STACK_WIDTH - 1); + __asm__ volatile("movq %0, %%rsp":: "r"(kernel_stack_entry)); + + extern void kernel_main(void); + kernel_main(); +} diff --git a/boot/limine/limine.d b/boot/limine/limine.d new file mode 100644 index 0000000..1c946a9 --- /dev/null +++ b/boot/limine/limine.d @@ -0,0 +1,2 @@ +boot/limine/limine.o: boot/limine/limine.c boot/limine/limine.h \ + boot/boot.h diff --git a/boot/limine/limine.h b/boot/limine/limine.h new file mode 100644 index 0000000..186f3b1 --- /dev/null +++ b/boot/limine/limine.h @@ -0,0 +1,579 @@ +/* BSD Zero Clause License */ + +/* Copyright (C) 2022-2023 mintsuki and contributors. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _LIMINE_H +#define _LIMINE_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdint.h> + +/* Misc */ + +#ifdef LIMINE_NO_POINTERS +# define LIMINE_PTR(TYPE) uint64_t +#else +# define LIMINE_PTR(TYPE) TYPE +#endif + +#ifdef __GNUC__ +# define LIMINE_DEPRECATED __attribute__((__deprecated__)) +# define LIMINE_DEPRECATED_IGNORE_START \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +# define LIMINE_DEPRECATED_IGNORE_END \ + _Pragma("GCC diagnostic pop") +#else +# define LIMINE_DEPRECATED +# define LIMINE_DEPRECATED_IGNORE_START +# define LIMINE_DEPRECATED_IGNORE_END +#endif + +#define LIMINE_BASE_REVISION(N) \ + uint64_t limine_base_revision[3] = { 0xf9562b2d5c95a6c8, 0x6a7b384944536bdc, (N) }; + +#define LIMINE_BASE_REVISION_SUPPORTED (limine_base_revision[2] == 0) + +#define LIMINE_COMMON_MAGIC 0xc7b1dd30df4c8b88, 0x0a82e883a194f07b + +struct limine_uuid { + uint32_t a; + uint16_t b; + uint16_t c; + uint8_t d[8]; +}; + +#define LIMINE_MEDIA_TYPE_GENERIC 0 +#define LIMINE_MEDIA_TYPE_OPTICAL 1 +#define LIMINE_MEDIA_TYPE_TFTP 2 + +struct limine_file { + uint64_t revision; + LIMINE_PTR(void *) address; + uint64_t size; + LIMINE_PTR(char *) path; + LIMINE_PTR(char *) cmdline; + uint32_t media_type; + uint32_t unused; + uint32_t tftp_ip; + uint32_t tftp_port; + uint32_t partition_index; + uint32_t mbr_disk_id; + struct limine_uuid gpt_disk_uuid; + struct limine_uuid gpt_part_uuid; + struct limine_uuid part_uuid; +}; + +/* Boot info */ + +#define LIMINE_BOOTLOADER_INFO_REQUEST { LIMINE_COMMON_MAGIC, 0xf55038d8e2a1202f, 0x279426fcf5f59740 } + +struct limine_bootloader_info_response { + uint64_t revision; + LIMINE_PTR(char *) name; + LIMINE_PTR(char *) version; +}; + +struct limine_bootloader_info_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_bootloader_info_response *) response; +}; + +/* Stack size */ + +#define LIMINE_STACK_SIZE_REQUEST { LIMINE_COMMON_MAGIC, 0x224ef0460a8e8926, 0xe1cb0fc25f46ea3d } + +struct limine_stack_size_response { + uint64_t revision; +}; + +struct limine_stack_size_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_stack_size_response *) response; + uint64_t stack_size; +}; + +/* HHDM */ + +#define LIMINE_HHDM_REQUEST { LIMINE_COMMON_MAGIC, 0x48dcf1cb8ad2b852, 0x63984e959a98244b } + +struct limine_hhdm_response { + uint64_t revision; + uint64_t offset; +}; + +struct limine_hhdm_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_hhdm_response *) response; +}; + +/* Framebuffer */ + +#define LIMINE_FRAMEBUFFER_REQUEST { LIMINE_COMMON_MAGIC, 0x9d5827dcd881dd75, 0xa3148604f6fab11b } + +#define LIMINE_FRAMEBUFFER_RGB 1 + +struct limine_video_mode { + uint64_t pitch; + uint64_t width; + uint64_t height; + uint16_t bpp; + uint8_t memory_model; + uint8_t red_mask_size; + uint8_t red_mask_shift; + uint8_t green_mask_size; + uint8_t green_mask_shift; + uint8_t blue_mask_size; + uint8_t blue_mask_shift; +}; + +struct limine_framebuffer { + LIMINE_PTR(void *) address; + uint64_t width; + uint64_t height; + uint64_t pitch; + uint16_t bpp; + uint8_t memory_model; + uint8_t red_mask_size; + uint8_t red_mask_shift; + uint8_t green_mask_size; + uint8_t green_mask_shift; + uint8_t blue_mask_size; + uint8_t blue_mask_shift; + uint8_t unused[7]; + uint64_t edid_size; + LIMINE_PTR(void *) edid; + /* Response revision 1 */ + uint64_t mode_count; + LIMINE_PTR(struct limine_video_mode **) modes; +}; + +struct limine_framebuffer_response { + uint64_t revision; + uint64_t framebuffer_count; + LIMINE_PTR(struct limine_framebuffer **) framebuffers; +}; + +struct limine_framebuffer_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_framebuffer_response *) response; +}; + +/* Terminal */ + +#define LIMINE_TERMINAL_REQUEST { LIMINE_COMMON_MAGIC, 0xc8ac59310c2b0844, 0xa68d0c7265d38878 } + +#define LIMINE_TERMINAL_CB_DEC 10 +#define LIMINE_TERMINAL_CB_BELL 20 +#define LIMINE_TERMINAL_CB_PRIVATE_ID 30 +#define LIMINE_TERMINAL_CB_STATUS_REPORT 40 +#define LIMINE_TERMINAL_CB_POS_REPORT 50 +#define LIMINE_TERMINAL_CB_KBD_LEDS 60 +#define LIMINE_TERMINAL_CB_MODE 70 +#define LIMINE_TERMINAL_CB_LINUX 80 + +#define LIMINE_TERMINAL_CTX_SIZE ((uint64_t)(-1)) +#define LIMINE_TERMINAL_CTX_SAVE ((uint64_t)(-2)) +#define LIMINE_TERMINAL_CTX_RESTORE ((uint64_t)(-3)) +#define LIMINE_TERMINAL_FULL_REFRESH ((uint64_t)(-4)) + +/* Response revision 1 */ +#define LIMINE_TERMINAL_OOB_OUTPUT_GET ((uint64_t)(-10)) +#define LIMINE_TERMINAL_OOB_OUTPUT_SET ((uint64_t)(-11)) + +#define LIMINE_TERMINAL_OOB_OUTPUT_OCRNL (1 << 0) +#define LIMINE_TERMINAL_OOB_OUTPUT_OFDEL (1 << 1) +#define LIMINE_TERMINAL_OOB_OUTPUT_OFILL (1 << 2) +#define LIMINE_TERMINAL_OOB_OUTPUT_OLCUC (1 << 3) +#define LIMINE_TERMINAL_OOB_OUTPUT_ONLCR (1 << 4) +#define LIMINE_TERMINAL_OOB_OUTPUT_ONLRET (1 << 5) +#define LIMINE_TERMINAL_OOB_OUTPUT_ONOCR (1 << 6) +#define LIMINE_TERMINAL_OOB_OUTPUT_OPOST (1 << 7) + +LIMINE_DEPRECATED_IGNORE_START + +struct LIMINE_DEPRECATED limine_terminal; + +typedef void (*limine_terminal_write)(struct limine_terminal *, const char *, uint64_t); +typedef void (*limine_terminal_callback)(struct limine_terminal *, uint64_t, uint64_t, uint64_t, uint64_t); + +struct LIMINE_DEPRECATED limine_terminal { + uint64_t columns; + uint64_t rows; + LIMINE_PTR(struct limine_framebuffer *) framebuffer; +}; + +struct LIMINE_DEPRECATED limine_terminal_response { + uint64_t revision; + uint64_t terminal_count; + LIMINE_PTR(struct limine_terminal **) terminals; + LIMINE_PTR(limine_terminal_write) write; +}; + +struct LIMINE_DEPRECATED limine_terminal_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_terminal_response *) response; + LIMINE_PTR(limine_terminal_callback) callback; +}; + +LIMINE_DEPRECATED_IGNORE_END + +/* Paging mode */ + +#define LIMINE_PAGING_MODE_REQUEST { LIMINE_COMMON_MAGIC, 0x95c1a0edab0944cb, 0xa4e5cb3842f7488a } + +#if defined (__x86_64__) || defined (__i386__) +#define LIMINE_PAGING_MODE_X86_64_4LVL 0 +#define LIMINE_PAGING_MODE_X86_64_5LVL 1 +#define LIMINE_PAGING_MODE_MAX LIMINE_PAGING_MODE_X86_64_5LVL +#define LIMINE_PAGING_MODE_DEFAULT LIMINE_PAGING_MODE_X86_64_4LVL +#elif defined (__aarch64__) +#define LIMINE_PAGING_MODE_AARCH64_4LVL 0 +#define LIMINE_PAGING_MODE_AARCH64_5LVL 1 +#define LIMINE_PAGING_MODE_MAX LIMINE_PAGING_MODE_AARCH64_5LVL +#define LIMINE_PAGING_MODE_DEFAULT LIMINE_PAGING_MODE_AARCH64_4LVL +#elif defined (__riscv) && (__riscv_xlen == 64) +#define LIMINE_PAGING_MODE_RISCV_SV39 0 +#define LIMINE_PAGING_MODE_RISCV_SV48 1 +#define LIMINE_PAGING_MODE_RISCV_SV57 2 +#define LIMINE_PAGING_MODE_MAX LIMINE_PAGING_MODE_RISCV_SV57 +#define LIMINE_PAGING_MODE_DEFAULT LIMINE_PAGING_MODE_RISCV_SV48 +#else +#error Unknown architecture +#endif + +struct limine_paging_mode_response { + uint64_t revision; + uint64_t mode; + uint64_t flags; +}; + +struct limine_paging_mode_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_paging_mode_response *) response; + uint64_t mode; + uint64_t flags; +}; + +/* 5-level paging */ + +#define LIMINE_5_LEVEL_PAGING_REQUEST { LIMINE_COMMON_MAGIC, 0x94469551da9b3192, 0xebe5e86db7382888 } + +LIMINE_DEPRECATED_IGNORE_START + +struct LIMINE_DEPRECATED limine_5_level_paging_response { + uint64_t revision; +}; + +struct LIMINE_DEPRECATED limine_5_level_paging_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_5_level_paging_response *) response; +}; + +LIMINE_DEPRECATED_IGNORE_END + +/* SMP */ + +#define LIMINE_SMP_REQUEST { LIMINE_COMMON_MAGIC, 0x95a67b819a1b857e, 0xa0b61b723b6a73e0 } + +struct limine_smp_info; + +typedef void (*limine_goto_address)(struct limine_smp_info *); + +#if defined (__x86_64__) || defined (__i386__) + +#define LIMINE_SMP_X2APIC (1 << 0) + +struct limine_smp_info { + uint32_t processor_id; + uint32_t lapic_id; + uint64_t reserved; + LIMINE_PTR(limine_goto_address) goto_address; + uint64_t extra_argument; +}; + +struct limine_smp_response { + uint64_t revision; + uint32_t flags; + uint32_t bsp_lapic_id; + uint64_t cpu_count; + LIMINE_PTR(struct limine_smp_info **) cpus; +}; + +#elif defined (__aarch64__) + +struct limine_smp_info { + uint32_t processor_id; + uint32_t gic_iface_no; + uint64_t mpidr; + uint64_t reserved; + LIMINE_PTR(limine_goto_address) goto_address; + uint64_t extra_argument; +}; + +struct limine_smp_response { + uint64_t revision; + uint64_t flags; + uint64_t bsp_mpidr; + uint64_t cpu_count; + LIMINE_PTR(struct limine_smp_info **) cpus; +}; + +#elif defined (__riscv) && (__riscv_xlen == 64) + +struct limine_smp_info { + uint64_t processor_id; + uint64_t hartid; + uint64_t reserved; + LIMINE_PTR(limine_goto_address) goto_address; + uint64_t extra_argument; +}; + +struct limine_smp_response { + uint64_t revision; + uint64_t flags; + uint64_t bsp_hartid; + uint64_t cpu_count; + LIMINE_PTR(struct limine_smp_info **) cpus; +}; + +#else +#error Unknown architecture +#endif + +struct limine_smp_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_smp_response *) response; + uint64_t flags; +}; + +/* Memory map */ + +#define LIMINE_MEMMAP_REQUEST { LIMINE_COMMON_MAGIC, 0x67cf3d9d378a806f, 0xe304acdfc50c3c62 } + +#define LIMINE_MEMMAP_USABLE 0 +#define LIMINE_MEMMAP_RESERVED 1 +#define LIMINE_MEMMAP_ACPI_RECLAIMABLE 2 +#define LIMINE_MEMMAP_ACPI_NVS 3 +#define LIMINE_MEMMAP_BAD_MEMORY 4 +#define LIMINE_MEMMAP_BOOTLOADER_RECLAIMABLE 5 +#define LIMINE_MEMMAP_KERNEL_AND_MODULES 6 +#define LIMINE_MEMMAP_FRAMEBUFFER 7 + +struct limine_memmap_entry { + uint64_t base; + uint64_t length; + uint64_t type; +}; + +struct limine_memmap_response { + uint64_t revision; + uint64_t entry_count; + LIMINE_PTR(struct limine_memmap_entry **) entries; +}; + +struct limine_memmap_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_memmap_response *) response; +}; + +/* Entry point */ + +#define LIMINE_ENTRY_POINT_REQUEST { LIMINE_COMMON_MAGIC, 0x13d86c035a1cd3e1, 0x2b0caa89d8f3026a } + +typedef void (*limine_entry_point)(void); + +struct limine_entry_point_response { + uint64_t revision; +}; + +struct limine_entry_point_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_entry_point_response *) response; + LIMINE_PTR(limine_entry_point) entry; +}; + +/* Kernel File */ + +#define LIMINE_KERNEL_FILE_REQUEST { LIMINE_COMMON_MAGIC, 0xad97e90e83f1ed67, 0x31eb5d1c5ff23b69 } + +struct limine_kernel_file_response { + uint64_t revision; + LIMINE_PTR(struct limine_file *) kernel_file; +}; + +struct limine_kernel_file_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_kernel_file_response *) response; +}; + +/* Module */ + +#define LIMINE_MODULE_REQUEST { LIMINE_COMMON_MAGIC, 0x3e7e279702be32af, 0xca1c4f3bd1280cee } + +#define LIMINE_INTERNAL_MODULE_REQUIRED (1 << 0) + +struct limine_internal_module { + LIMINE_PTR(const char *) path; + LIMINE_PTR(const char *) cmdline; + uint64_t flags; +}; + +struct limine_module_response { + uint64_t revision; + uint64_t module_count; + LIMINE_PTR(struct limine_file **) modules; +}; + +struct limine_module_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_module_response *) response; + + /* Request revision 1 */ + uint64_t internal_module_count; + LIMINE_PTR(struct limine_internal_module **) internal_modules; +}; + +/* RSDP */ + +#define LIMINE_RSDP_REQUEST { LIMINE_COMMON_MAGIC, 0xc5e77b6b397e7b43, 0x27637845accdcf3c } + +struct limine_rsdp_response { + uint64_t revision; + LIMINE_PTR(void *) address; +}; + +struct limine_rsdp_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_rsdp_response *) response; +}; + +/* SMBIOS */ + +#define LIMINE_SMBIOS_REQUEST { LIMINE_COMMON_MAGIC, 0x9e9046f11e095391, 0xaa4a520fefbde5ee } + +struct limine_smbios_response { + uint64_t revision; + LIMINE_PTR(void *) entry_32; + LIMINE_PTR(void *) entry_64; +}; + +struct limine_smbios_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_smbios_response *) response; +}; + +/* EFI system table */ + +#define LIMINE_EFI_SYSTEM_TABLE_REQUEST { LIMINE_COMMON_MAGIC, 0x5ceba5163eaaf6d6, 0x0a6981610cf65fcc } + +struct limine_efi_system_table_response { + uint64_t revision; + LIMINE_PTR(void *) address; +}; + +struct limine_efi_system_table_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_efi_system_table_response *) response; +}; + +/* EFI memory map */ + +#define LIMINE_EFI_MEMMAP_REQUEST { LIMINE_COMMON_MAGIC, 0x7df62a431d6872d5, 0xa4fcdfb3e57306c8 } + +struct limine_efi_memmap_response { + uint64_t revision; + LIMINE_PTR(void *) memmap; + uint64_t memmap_size; + uint64_t desc_size; + uint64_t desc_version; +}; + +struct limine_efi_memmap_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_efi_memmap_response *) response; +}; + +/* Boot time */ + +#define LIMINE_BOOT_TIME_REQUEST { LIMINE_COMMON_MAGIC, 0x502746e184c088aa, 0xfbc5ec83e6327893 } + +struct limine_boot_time_response { + uint64_t revision; + int64_t boot_time; +}; + +struct limine_boot_time_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_boot_time_response *) response; +}; + +/* Kernel address */ + +#define LIMINE_KERNEL_ADDRESS_REQUEST { LIMINE_COMMON_MAGIC, 0x71ba76863cc55f63, 0xb2644a48c516a487 } + +struct limine_kernel_address_response { + uint64_t revision; + uint64_t physical_base; + uint64_t virtual_base; +}; + +struct limine_kernel_address_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_kernel_address_response *) response; +}; + +/* Device Tree Blob */ + +#define LIMINE_DTB_REQUEST { LIMINE_COMMON_MAGIC, 0xb40ddb48fb54bac7, 0x545081493f81ffb7 } + +struct limine_dtb_response { + uint64_t revision; + LIMINE_PTR(void *) dtb_ptr; +}; + +struct limine_dtb_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_dtb_response *) response; +}; + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/compile_flags.txt b/compile_flags.txt new file mode 100644 index 0000000..ab1f054 --- /dev/null +++ b/compile_flags.txt @@ -0,0 +1,3 @@ +-I. +-ffreestanding +-nostdlib diff --git a/config.mk b/config.mk new file mode 100644 index 0000000..11ce9f4 --- /dev/null +++ b/config.mk @@ -0,0 +1,24 @@ +TARGET_MACHINE = x86_64 +TARGET_OS = jove +TARGET_TRIPLET = $(TARGET_MACHINE)-$(TARGET_OS) +TARGET_BOOTLOADER = limine + +CFLAGS = \ + -ffreestanding \ + -mno-sse \ + -nostdlib \ + -fno-pie \ + -fno-pic \ + -g \ + -D$(TARGET_MACHINE) \ + -I. + +LDFLAGS = -nostdlib \ + -z max-page-size=0x1000 \ + -T link/$(TARGET_TRIPLET).ld + +ifeq "$(TARGET_MACHINE)" "x86_64" + CFLAGS += -mno-red-zone \ + -mcmodel=kernel \ + -MMD +endif diff --git a/io/interrupt.h b/io/interrupt.h new file mode 100644 index 0000000..444ff01 --- /dev/null +++ b/io/interrupt.h @@ -0,0 +1,6 @@ +#ifndef JOVE_IO_INTERRUPT_H +#define JOVE_IO_INTERRUPT_H 1 + + + +#endif diff --git a/io/log.c b/io/log.c new file mode 100644 index 0000000..14397a7 --- /dev/null +++ b/io/log.c @@ -0,0 +1,68 @@ +#include "log.h" +#include "lib/jove.h" +#include "lib/string.h" +#include "lib/format.h" + +static struct LogDevice *s_first_logdev = NULL; + +void +klog_newdev(struct LogDevice *dev) +{ + if(s_first_logdev == NULL) { + s_first_logdev = dev; + }else{ + dev->chain = s_first_logdev; + s_first_logdev = dev; + } +} + +static void +s_klogc(struct LogDevice *dev, char c) +{ + dev->out(&c, 1); + if(dev->chain != NULL) + s_klogc(dev->chain, c); +} + +void +klogc(char c) +{ + s_klogc(s_first_logdev, c); +} + +static void +s_klogs(struct LogDevice *dev, const char *s, size_t slen) +{ + dev->out(s, slen); + if(dev->chain != NULL) + s_klogs(dev->chain, s, slen); +} + +void +klogs(const char *s) +{ + size_t slen = strlen(s); + s_klogs(s_first_logdev, s, slen); +} + +void +klogsn(const char *s, size_t len) +{ + s_klogs(s_first_logdev, s, len); +} + +void +kvlogf(const char *fmt, va_list ap) +{ + char buffer[256]; + svfmt(buffer, 256, fmt, ap); + klogs(buffer); +} + +void klogf(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + kvlogf(fmt, ap); + va_end(ap); +} diff --git a/io/log.d b/io/log.d new file mode 100644 index 0000000..b8609ce --- /dev/null +++ b/io/log.d @@ -0,0 +1 @@ +io/log.o: io/log.c io/log.h lib/jove.h lib/string.h lib/format.h diff --git a/io/log.h b/io/log.h new file mode 100644 index 0000000..ad1526d --- /dev/null +++ b/io/log.h @@ -0,0 +1,22 @@ +#ifndef JOVE_IO_LOG_H +#define JOVE_IO_LOG_H 1 + +#include <stddef.h> + +struct LogDevice +{ + void (*out)(const char*, size_t); + struct LogDevice *chain; +}; + +void klog_newdev(struct LogDevice *dev); + +void klogc(char c); +void klogs(const char *s); +void klogsn(const char *s, size_t len); + +#include <stdarg.h> +void kvlogf(const char *fmt, va_list ap); +void klogf(const char *fmt, ...); + +#endif diff --git a/ird/initrd.c b/ird/initrd.c new file mode 100644 index 0000000..f219243 --- /dev/null +++ b/ird/initrd.c @@ -0,0 +1,88 @@ +#include "initrd.h" +#include "lib/string.h" +#include "io/log.h" +#include "boot/boot.h" +#include "boot/cmdline.h" +#include "mem/memory.h" + +struct SlabCache s_initrd_cache; +struct SLinkedList s_initrd_files; + +static size_t +s_tar_oct_dec(const char *oct) +{ + size_t value = 0; + while(*oct != 0) { + value *= 8; + value += (*oct) - '0'; + oct++; + } + return value; +} + +static void +s_initrd_parse(struct BootModule *module) +{ + struct TARSector *sector = (struct TARSector*)module->addr; + while(true) { + struct TARHeader *header = (struct TARHeader*)sector; + if(header->name[0] == 0) break; + + struct InitrdFile *file = mem_slab_alloc(&s_initrd_cache); + *file = (struct InitrdFile){ + .header = header, + .name = header->name, + .size = s_tar_oct_dec(header->size), + .data = §or[1] + }; + klogf("File %s size %i\n", file->name, file->size); + sll_push(&s_initrd_files, file); + + sector = §or[(file->size / 512) + 1]; + if(file->size % 512 > 0) sector = §or[1]; + } +} + +struct InitrdFile * +ird_getfile(const char *path) +{ + for(struct SLLNode *node = s_initrd_files.head; node != NULL; node = node->next) { + struct InitrdFile *file = (struct InitrdFile*)node; + if(strcmp(file->name, path) == 0) return file; + } + return NULL; +} + +struct SLinkedList * +ird_getfiles(void) +{ + return &s_initrd_files; +} + +void +initrd_setup(void) +{ + const char *initrd_path = cmdline_get("initrd"); + if(initrd_path == 0) { + klogf("No initrd loaded!\n"); + return; + } + klogf("Initrd located in module path %s\n", initrd_path); + struct BootModule *initrd_module = NULL; + for(size_t i = 0; i < boot_module_count; i++) { + struct BootModule *module = &boot_modules[i]; + if(strcmp(module->path, initrd_path) == 0) { + initrd_module = module; + break; + } + } + + if(initrd_module == NULL) { + klogf("Initrd not found in modules!\n"); + return; + } + + sll_new(&s_initrd_files, sizeof(struct InitrdFile)); + mem_slabcache_new(&s_initrd_cache, "initrd files", sizeof(struct InitrdFile)); + s_initrd_parse(initrd_module); +} diff --git a/ird/initrd.d b/ird/initrd.d new file mode 100644 index 0000000..b2b0e40 --- /dev/null +++ b/ird/initrd.d @@ -0,0 +1,3 @@ +ird/initrd.o: ird/initrd.c ird/initrd.h ird/tar.h lib/linkedlist.h \ + lib/string.h io/log.h boot/boot.h boot/cmdline.h lib/hashtable.h \ + lib/linkedlist.h mem/memory.h mem/slab.h diff --git a/ird/initrd.h b/ird/initrd.h new file mode 100644 index 0000000..f236fa1 --- /dev/null +++ b/ird/initrd.h @@ -0,0 +1,21 @@ +#ifndef JOVE_INITRD_H +#define JOVE_INITRD_H 1 + +#include <stddef.h> +#include "tar.h" +#include "lib/linkedlist.h" + +struct InitrdFile { + struct InitrdFile *next; + struct TARHeader *header; + const char *name; + size_t size; + const void *data; +}; + +void initrd_setup(void); + +struct InitrdFile *ird_getfile(const char *path); +struct SLinkedList *ird_getfiles(void); + +#endif diff --git a/ird/tar.h b/ird/tar.h new file mode 100644 index 0000000..5c72620 --- /dev/null +++ b/ird/tar.h @@ -0,0 +1,18 @@ +#ifndef JOVE_INITRD_TAR_H +#define JOVE_INITRD_TAR_H 1 + +struct TARSector { char data[512]; }; + +struct TARHeader { + char name[100]; + char mode[8]; + char owner[8]; + char group[8]; + char size[12]; + char modified[12]; + char checksum[8]; + char link; + char linkname[100]; +}; + +#endif diff --git a/lib/format.h b/lib/format.h new file mode 100644 index 0000000..5a2b75c --- /dev/null +++ b/lib/format.h @@ -0,0 +1,13 @@ +#ifndef JOVE_LIB_FORMAT_H +#define JOVE_LIB_FORMAT_H 1 + +#include <stddef.h> +#include <stdbool.h> + +size_t ltostr(char *s, size_t limit, unsigned long l, bool sgn, int radix); + +char *sfmt(char *s, size_t limit, const char *fmt, ...); +#include <stdarg.h> +char *svfmt(char *s, size_t limit, const char *fmt, va_list ap); + +#endif diff --git a/lib/hashtable.c b/lib/hashtable.c new file mode 100644 index 0000000..17f1e74 --- /dev/null +++ b/lib/hashtable.c @@ -0,0 +1,109 @@ +#include "hashtable.h" +#include "string.h" +#include "mem/memory.h" + +static size_t +s_hash_function_mul(const void *data, size_t len) +{ + size_t hash = 1; + const char *cdata = (const char*)data; + for(size_t i = 0; i < len; i++) { + hash *= ((size_t)cdata[i]) + 1; + } + return hash; +} + +static size_t +s_hash_function_mul_s(const void *data, size_t _) +{ + return s_hash_function_mul(data, strlen(data)); +} + +void +_hashtable_new(struct HashTable *table, size_t obj_size, size_t key_size) +{ + *table = (struct HashTable){ + .buckets = NULL, + .bucket_count = 0, + .bucket_capacity = 2, + .obj_size = obj_size, + .key_size = key_size, + .hash_function = s_hash_function_mul + }; + table->buckets = mem_alloc(sizeof(struct SLinkedList) * 2); +} + +void +_hashtable_news(struct HashTable *table, size_t obj_size) +{ + *table = (struct HashTable){ + .buckets = NULL, + .bucket_count = 0, + .bucket_capacity = 2, + .obj_size = obj_size, + .key_size = -1, + .hash_function = s_hash_function_mul_s + }; + table->buckets = mem_alloc(sizeof(struct SLinkedList) * 2); +} + +void +hashtable_insert(struct HashTable *table, const void *key, void *data) +{ + size_t hash = table->hash_function(key, table->key_size); + if(table->bucket_capacity == table->bucket_count) { + struct SLinkedList *old_buckets = table->buckets; + size_t old_buckets_count = table->bucket_count; + + table->bucket_capacity *= 2; + table->bucket_count = 0; + table->buckets = mem_alloc(sizeof(struct SLinkedList) * table->bucket_capacity); + for(size_t i = 0; i < old_buckets_count; i++) { + for(struct SLLNode *node = old_buckets[i].head; node != NULL; node = node->next) { + struct HashTableValue *value = (struct HashTableValue*)(&node->data); + hashtable_insert(table, value->key, value->value); + mem_free(node); + } + } + } + + size_t index = hash % table->bucket_capacity; + struct SLinkedList *bucket = &table->buckets[index]; + struct SLLNode *node = mem_alloc(sizeof(struct SLLNode) + sizeof(struct HashTableValue) + table->obj_size); + struct HashTableValue *value = (struct HashTableValue*)(node->data); + + value->key = key; + memcpy(value->value, data, table->obj_size); + + table->bucket_count++; + if(bucket->count == 0) { + sll_new(bucket, table->obj_size); + sll_push(bucket, node); + return; + } + for(struct SLLNode *onode = bucket->head; onode != NULL; onode = onode->next) + { + struct HashTableValue *ovalue = (struct HashTableValue*)onode->data; + size_t keylen = table->key_size > 0 ? table->key_size : strlen((const char*)key) + 1; + if(memcmp(ovalue->key, value->key, keylen) == 0) { + memcpy(ovalue->value, value->value, table->obj_size); + return; + } + } + sll_push(bucket, node); +} + +void* +_hashtable_get(struct HashTable *table, const void *key) +{ + size_t hash = table->hash_function(key, table->key_size); + size_t index = hash % table->bucket_capacity; + struct SLinkedList *bucket = &table->buckets[index]; + for(struct SLLNode *node = bucket->head; node != NULL; node = node->next) + { + struct HashTableValue *value = (struct HashTableValue*)node->data; + size_t keylen = table->key_size > 0 ? table->key_size : strlen((const char*)value->key); + if(memcmp(key, value->key, keylen) == 0) return value->value; + } + return NULL; +} diff --git a/lib/hashtable.d b/lib/hashtable.d new file mode 100644 index 0000000..791f457 --- /dev/null +++ b/lib/hashtable.d @@ -0,0 +1,2 @@ +lib/hashtable.o: lib/hashtable.c lib/hashtable.h lib/linkedlist.h \ + lib/string.h mem/memory.h mem/slab.h diff --git a/lib/hashtable.h b/lib/hashtable.h new file mode 100644 index 0000000..fe6f5c3 --- /dev/null +++ b/lib/hashtable.h @@ -0,0 +1,34 @@ +#ifndef JOVE_LIB_HASHTABLE_H +#define JOVE_LIB_HASHTABLE_H 1 + +#include <stddef.h> +#include <stdint.h> +#include "linkedlist.h" + +struct HashTableValue +{ + const void *key; + char value[]; +}; + +struct HashTable { + struct SLinkedList *buckets; + size_t bucket_count; + size_t bucket_capacity; + + size_t obj_size; + int key_size; + size_t (*hash_function)(const void*, size_t); +}; + +void _hashtable_new(struct HashTable *table, size_t obj_size, size_t key_size); +#define hashtable_new(table, type, keytype) _hashtable_new(table, sizeof(type), sizeof(keytype)) + +void _hashtable_news(struct HashTable *table, size_t obj_size); +#define hashtable_news(table, type) _hashtable_news(table, sizeof(type)) + +void hashtable_insert(struct HashTable *table, const void *key, void *data); +void *_hashtable_get(struct HashTable *table, const void *key); +#define hashtable_get(table, key, type) (type*)_hashtable_get(table, key) + +#endif diff --git a/lib/jove.h b/lib/jove.h new file mode 100644 index 0000000..4aa60ed --- /dev/null +++ b/lib/jove.h @@ -0,0 +1,15 @@ +#ifndef JOVE_LIB_JOVE_H +#define JOVE_LIB_JOVE_H 1 + +#define ALWAYS_INLINE inline __attribute__((always_inline)) +#define PAGEALIGN __attribute__((aligned(0x1000))) + +//#define LOG2(n) (__builtin_clz(n) ^ 31) + +extern void *_kernel_start; +extern void *_kernel_end; + +__attribute__((noreturn)) void _kpanic(const char *file, int line, const char *fmt, ...); +#define kpanic(...) _kpanic(__FILE__, __LINE__, __VA_ARGS__) + +#endif diff --git a/lib/kpanic.c b/lib/kpanic.c new file mode 100644 index 0000000..97e42eb --- /dev/null +++ b/lib/kpanic.c @@ -0,0 +1,15 @@ +#include "jove.h" +#include "io/log.h" + +#include <stdarg.h> + +__attribute__((noreturn)) +void _kpanic(const char *file, int line, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + klogf("!!! PANIC !!!\n%s:%i\n", file, line); + kvlogf(fmt, ap); + va_end(ap); + for(;;) __asm__ volatile("hlt"); +} diff --git a/lib/kpanic.d b/lib/kpanic.d new file mode 100644 index 0000000..bf4ed9e --- /dev/null +++ b/lib/kpanic.d @@ -0,0 +1 @@ +lib/kpanic.o: lib/kpanic.c lib/jove.h io/log.h diff --git a/lib/linkedlist.c b/lib/linkedlist.c new file mode 100644 index 0000000..6cc6403 --- /dev/null +++ b/lib/linkedlist.c @@ -0,0 +1,34 @@ +#include "linkedlist.h" + +void +sll_new(struct SLinkedList *list, size_t obj_size) +{ + list->obj_size = obj_size; + list->count = 0; + list->head = list->tail = NULL; +}; + +void +sll_push(struct SLinkedList *list, void *data) +{ + struct SLLNode *node = (struct SLLNode*)data; + if(list->tail != NULL) { + list->tail->next = node; + } + if(list->head == NULL) + list->head = node; + list->tail = node; + list->count++; +} + +void* +sll_get(struct SLinkedList *list, size_t index) +{ + struct SLLNode *node = list->head; + if(node == NULL) return NULL; + if(index > list->count) return list->tail; + for(size_t i = 0; i < index; i++) { + node = node->next; + } + return node; +} diff --git a/lib/linkedlist.d b/lib/linkedlist.d new file mode 100644 index 0000000..a06a9df --- /dev/null +++ b/lib/linkedlist.d @@ -0,0 +1 @@ +lib/linkedlist.o: lib/linkedlist.c lib/linkedlist.h diff --git a/lib/linkedlist.h b/lib/linkedlist.h new file mode 100644 index 0000000..26c148e --- /dev/null +++ b/lib/linkedlist.h @@ -0,0 +1,26 @@ +#ifndef JOVE_LIB_LINKEDLIST_H +#define JOVE_LIB_LINKEDLIST_H 1 + +#include <stdint.h> +#include <stddef.h> + +struct SLLNode { + struct SLLNode *next; + char data[]; +}; + +/*Singly Linked List*/ +struct SLinkedList +{ + struct SLLNode *head; + struct SLLNode *tail; + + size_t obj_size; + size_t count; +}; + +void sll_new(struct SLinkedList *list, size_t obj_size); +void sll_push(struct SLinkedList *list, void *node); +void *sll_get(struct SLinkedList *list, size_t index); + +#endif diff --git a/lib/ltostr.c b/lib/ltostr.c new file mode 100644 index 0000000..e28be31 --- /dev/null +++ b/lib/ltostr.c @@ -0,0 +1,26 @@ +#include "format.h" + +size_t +ltostr(char *s, size_t limit, unsigned long l, bool sgn, int radix) +{ + size_t si = 0; + size_t digits = 0; + if((long)l < 0 && sgn) { + l = -((long)l); + s[0] = '-'; + } + for(unsigned long lv = l; lv != 0; lv /= radix) + digits++; + digits = digits > limit ? limit : digits; + + if(digits-- == 0) + s[si++] = '0'; + for(unsigned long lv = l; lv != 0; lv /= radix) + { + if(si >= limit) return si; + int digit = lv % radix; + s[(digits - si)] = (digit >= 10 ? (digit + 'a' - 10) : digit + '0'); + si++; + } + return si; +} diff --git a/lib/ltostr.d b/lib/ltostr.d new file mode 100644 index 0000000..fecc225 --- /dev/null +++ b/lib/ltostr.d @@ -0,0 +1 @@ +lib/ltostr.o: lib/ltostr.c lib/format.h diff --git a/lib/mem.c b/lib/mem.c new file mode 100644 index 0000000..b60fbbd --- /dev/null +++ b/lib/mem.c @@ -0,0 +1,52 @@ +#include "string.h" +#include "mem/memory.h" + +void* +memset(void *dest, int c, size_t n) +{ + char *destc = (char*)dest; + for(size_t i = 0; i < n; i++) + destc[i] = c; + return dest; +} + +void* +memcpy(void *dest, const void *src, size_t n) +{ + char *destc = (char*)dest; + const char *srcc = (const char*)src; + for(size_t i = 0; i < n; i++) + destc[i] = srcc[i]; + return dest; +} + +void* +memmove(void *dest, const void *src, size_t n) +{ + char *destc = (char*)dest; + const char *srcc = (const char*)src; + if(destc + n < srcc) return memcpy(dest, src, n); + char buffer[n]; + memcpy(buffer, src, n); + return memcpy(destc, buffer, n); +} + +int +memcmp(const void *a, const void *b, size_t n) +{ + const char *ac = (const char*)a; + const char *bc = (const char*)b; + for(size_t i = 0; i < n; i++) { + if(ac[i] != bc[i]) return ac[i] - bc[i]; + } + return 0; +} + +char* +strdup(const char *s) +{ + size_t slen = strlen(s); + char *ret = mem_alloc(slen); + memcpy(ret, s, slen); + return ret; +} diff --git a/lib/mem.d b/lib/mem.d new file mode 100644 index 0000000..232ecd5 --- /dev/null +++ b/lib/mem.d @@ -0,0 +1 @@ +lib/mem.o: lib/mem.c lib/string.h mem/memory.h mem/slab.h diff --git a/lib/sfmt.c b/lib/sfmt.c new file mode 100644 index 0000000..f903cbf --- /dev/null +++ b/lib/sfmt.c @@ -0,0 +1,112 @@ +#include "string.h" +#include "format.h" + +char* +sfmt(char *s, size_t limit, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + + s = svfmt(s, limit, fmt, ap); + + va_end(ap); + return s; +} + +char* +svfmt(char *s, size_t limit, const char *fmt, va_list ap) +{ + + size_t fmtlen = strlen(fmt); + size_t si = 0; + for(size_t fmti = 0; fmti < fmtlen; fmti++) + { + if(si >= limit) break; + if(fmt[fmti] != '%') { + s[si++] = fmt[fmti]; + continue; + } + // TODO: Format flags + fmti++; + + bool alt = false; + bool zpad = false; + for(;;fmti++){ + if(fmt[fmti] == '#') { alt = true; continue; } + if(fmt[fmti] == '0') { zpad = true; continue; } + break; + } + int fwidth = 0; + if(fmt[fmti] >= '1' && fmt[fmti] <= '9') + { + for(;fmt[fmti] >= '0' && fmt[fmti] <= '9';fmti++) { + fwidth *= 10; + fwidth += fmt[fmti] - '0'; + } + } + int precision = 0; + if(fmt[fmti] == '.') + { + fmti++; + for(;fmt[fmti] >= '0' && fmt[fmti] <= '9';fmti++) { + precision *= 10; + precision += fmt[fmti] - '0'; + } + } + + bool sgn = true; + bool upper = false; + int radix = 10; + switch(fmt[fmti]) + { + case '%': + s[si++] = '%'; + break; + case 'b': + radix = 2; + case 'X': + upper = true; + case 'x': + if(radix == 10) + radix = 16; + case 'o': + if(radix == 10) + radix = 8; + case 'u': + sgn = false; + case 'i': { + if((radix == 8 || radix == 16) && alt) { + s[si++] = '0'; + if(radix == 16) { + s[si++] = 'x'; + } + if(radix == 2) { + s[si++] = 'b'; + } + } + size_t osi = si; + size_t nlen = ltostr(&s[si], limit - si, va_arg(ap, long), sgn, radix); + if(upper) sntoupper(&s[si], nlen); + si += nlen; + + int lpad = fwidth - (int)nlen; + if(lpad > 0) + { + if(lpad + osi >= limit) lpad = (limit - osi - 1); + memmove(&s[osi + lpad], &s[osi], nlen); + memset(&s[osi], zpad ? '0' : ' ', lpad); + si += lpad; + } + } break; + case 's': { + const char *str = va_arg(ap, char*); + size_t slen = strlen(str); + size_t wlen = slen > limit - si ? limit - si : slen; + for(size_t i = 0; i < wlen; i++) + s[si++] = str[i]; + } break; + } + } + s[si] = 0; + return s; +} diff --git a/lib/sfmt.d b/lib/sfmt.d new file mode 100644 index 0000000..f462cea --- /dev/null +++ b/lib/sfmt.d @@ -0,0 +1 @@ +lib/sfmt.o: lib/sfmt.c lib/string.h lib/format.h diff --git a/lib/string.c b/lib/string.c new file mode 100644 index 0000000..4bb1bad --- /dev/null +++ b/lib/string.c @@ -0,0 +1,16 @@ +#include "string.h" + +size_t strlen(const char *s) { + size_t l = 0; + for(; *s != 0; s++, l++); + return l; +} + +int strcmp(const char *a, const char *b) +{ + size_t i = 0; + for(; a[i] != 0 && b[i] != 0; i++) { + if(a[i] != b[i]) return a[i] - b[i]; + } + return a[i] - b[i]; +} diff --git a/lib/string.d b/lib/string.d new file mode 100644 index 0000000..dae7158 --- /dev/null +++ b/lib/string.d @@ -0,0 +1 @@ +lib/string.o: lib/string.c lib/string.h diff --git a/lib/string.h b/lib/string.h new file mode 100644 index 0000000..a91ec94 --- /dev/null +++ b/lib/string.h @@ -0,0 +1,18 @@ +#ifndef JOVE_LIB_STRING_H +#define JOVE_LIB_STRING_H 1 + +#include <stddef.h> + +int toupper(int c); +char *stoupper(char *s); +char *sntoupper(char *s, size_t limit); + +size_t strlen(const char *s); + +void *memset(void *dest, int c, size_t n); +void *memcpy(void *dest, const void *src, size_t n); +void *memmove(void *dest, const void *src, size_t n); +int memcmp(const void *a, const void *b, size_t n); +int strcmp(const char *a, const char *b); + +#endif diff --git a/lib/toupper.c b/lib/toupper.c new file mode 100644 index 0000000..807eb38 --- /dev/null +++ b/lib/toupper.c @@ -0,0 +1,26 @@ +#include "string.h" + +int +toupper(int c) +{ + if(c >= 'a' && c <= 'z') + c -= ('a' - 'A'); + return c; +} + +char* +stoupper(char *s) +{ + char *o = s; + for(; *s != 0; s++) + *s = toupper(*s); + return o; +} + +char* +sntoupper(char *s, size_t limit) +{ + for(size_t i = 0; i < limit; i++) + s[i] = toupper(s[i]); + return s; +} diff --git a/lib/toupper.d b/lib/toupper.d new file mode 100644 index 0000000..72e73f9 --- /dev/null +++ b/lib/toupper.d @@ -0,0 +1 @@ +lib/toupper.o: lib/toupper.c lib/string.h diff --git a/link/x86_64-jove.ld b/link/x86_64-jove.ld new file mode 100644 index 0000000..0686617 --- /dev/null +++ b/link/x86_64-jove.ld @@ -0,0 +1,52 @@ +OUTPUT_FORMAT(elf64-x86-64) +OUTPUT_ARCH(i386:x86-64) + +ENTRY(_start) + +PAGESIZE = CONSTANT(MAXPAGESIZE); + +PHDRS +{ + text PT_LOAD FLAGS((1 << 0) | (1 << 2)) ; /* RO EX*/ + rodata PT_LOAD FLAGS((1 << 2)) ; /* RO*/ + data PT_LOAD FLAGS((1 << 1) | (1 << 2)); /* RW*/ + bss PT_LOAD FLAGS((1 << 1) | (1 << 2)); /* RW*/ +} + +SECTIONS +{ + . = 0xFFFFFFFF80000000; + _kernel_start = .; + + .text BLOCK(PAGESIZE) : ALIGN(PAGESIZE) { + *(.init) + *(.text .text.*) + + . = ALIGN(8); + _kernel_ctors_start = .; + *(.ctors) + _kernel_ctors_end = .; + + *(.fini) + } :text + + .rodata BLOCK(PAGESIZE) : ALIGN(PAGESIZE) { + *(.rodata .rodata.*) + } :rodata + + .data BLOCK(PAGESIZE) : ALIGN(PAGESIZE) { + *(.data .data.*) + } :data + + .bss BLOCK(PAGESIZE) : ALIGN(PAGESIZE) { + *(COMMON) + *(.bss .bss.*) + } :bss + + /DISCARD/ : { + *(.note .note.*) + } + + _kernel_end = .; +} + @@ -0,0 +1,29 @@ +#include "arch/arch.h" +#include "io/log.h" +#include "mem/buddymap.h" +#include "mem/memory.h" +#include "boot/cmdline.h" +#include "tsk/tasking.h" +#include "ird/initrd.h" +#include "usr/umode.h" +#include "lib/jove.h" + +void +kernel_main(void) +{ + serial_setup(); + arch_tables_setup(); + + mem_buddy_setup(); + mem_paging_setup(); + mem_slab_setup(); + + cmdline_kernel_setup(); + + tasking_setup(); + + initrd_setup(); + umode_setup(); + + kpanic("Reached end of kernel main\n"); +} @@ -0,0 +1,4 @@ +main.o: main.c arch/arch.h io/log.h mem/buddymap.h mem/memory.h \ + mem/slab.h mem/memory.h boot/cmdline.h lib/hashtable.h lib/linkedlist.h \ + tsk/tasking.h ird/initrd.h ird/tar.h lib/linkedlist.h usr/umode.h \ + lib/jove.h diff --git a/mem/buddymap.c b/mem/buddymap.c new file mode 100644 index 0000000..5165876 --- /dev/null +++ b/mem/buddymap.c @@ -0,0 +1,151 @@ +#include "buddymap.h" +#include "lib/string.h" +#include "boot/boot.h" +#include "io/log.h" +#include <stdbool.h> + +#define ENTRY_BITS (sizeof(uintmax_t) * 8) +#define ENTRY_SIZE (ENTRY_BITS * PAGESIZE) +#define ENTRY_COUNT (MEMMAP_BUDDY_LIMIT / ENTRY_SIZE) +#define BUDDY_LAYERS 4 + +uintmax_t s_buddy_l0[ENTRY_COUNT]; +uintmax_t s_buddy_l1[ENTRY_COUNT << 1]; +uintmax_t s_buddy_l2[ENTRY_COUNT << 2]; +uintmax_t s_buddy_l3[ENTRY_COUNT << 3]; +uintmax_t *s_buddies[BUDDY_LAYERS] = { + s_buddy_l0, + s_buddy_l1, + s_buddy_l2, + s_buddy_l3 +}; +size_t s_buddies_lastfree[BUDDY_LAYERS] = { 1, 1, 1, 1 }; + +static bool +s_buddy_test(size_t l, size_t i) +{ + return (s_buddies[l][i / (ENTRY_BITS)] & (1ULL << (i % (ENTRY_BITS)))) > 0; +} + +static void +s_buddy_set(size_t l, size_t i) +{ + size_t j = i << l; + size_t w = 1 << l; + for(int layer = 0; layer < BUDDY_LAYERS; layer++) + { + if(w == 0) w = 1; + for(size_t bit = 0; bit < w; bit++) { + size_t entry = (j + bit) / ENTRY_BITS; + s_buddies[layer][entry] |= (1ULL << (j + bit % ENTRY_BITS)); + } + j >>= 1; + w >>= 1; + } +} + +static void +s_buddy_unset(size_t l, size_t i) +{ + size_t j = i << l; + size_t w = 1 << l; + bool free_upper = false; + for(int layer = 0; layer < BUDDY_LAYERS; layer++) + { + if(w == 0) { + size_t lower = (j << 1) % ENTRY_BITS; + size_t other = (lower + 1); + size_t entry = (j << 1) / ENTRY_BITS; + if((s_buddies[layer-1][entry] & (1ULL << lower)) > 0 && + (s_buddies[layer-1][entry] & (1ULL << other)) > 0) + s_buddies[layer][entry >> 1] &= ~(1ULL << (j % ENTRY_BITS)); + } + + for(size_t bit = 0; bit < w; bit++) { + size_t entry = j / ENTRY_BITS; + s_buddies[layer][entry] |= (1ULL << bit); + } + j >>= 1; + w >>= 1; + } +} + +void +mem_buddy_set_range(uintptr_t base, size_t length) +{ + for(int l = 0; l < BUDDY_LAYERS; l++) { + size_t bits = (length / PAGESIZE) >> l; + size_t biti = (base / PAGESIZE) >> l; + + if(bits == 0) bits = 1; + for(size_t i = 0; i < bits; i++) { + size_t entry = (biti + i) / ENTRY_BITS; + s_buddies[l][entry] |= 1ULL << ((biti + i) % ENTRY_BITS); + } + } +} + +void +mem_buddy_free_range(uintptr_t base, size_t length) +{ + for(int l = 0; l < BUDDY_LAYERS; l++) { + size_t bits = (length / PAGESIZE) >> l; + size_t biti = (base / PAGESIZE) >> l; + size_t bitbase = (biti * PAGESIZE) << l; + + if(bits == 0) continue; + for(size_t i = 0; i < bits; i++, bitbase += (PAGESIZE << l)) { + if(bitbase < base) continue; + size_t entry = (biti + i) / ENTRY_BITS; + s_buddies[l][entry] &= ~(1ULL << ((biti+ i) % ENTRY_BITS)); + } + } +} + + +uintptr_t +mem_buddy_takefree(size_t l) +{ + uintmax_t *layer = s_buddies[l]; + size_t lastfree = s_buddies_lastfree[l]; + if(s_buddy_test(l, lastfree)) lastfree = 0; + size_t entries = ENTRY_COUNT >> l; + for(size_t i = lastfree / ENTRY_BITS; i < entries; i++) { + uintmax_t entry = layer[i]; + if(entry == (uintmax_t)-1LL) continue; + for(size_t j = 0; j < ENTRY_BITS; j++) { + if((entry & (1ULL << j)) == 0) { + size_t bit = (i * ENTRY_BITS) + j; + s_buddies_lastfree[l] = bit + 1; + s_buddy_set(l, bit); + return bit * (PAGESIZE << l); + } + } + } + return 0; +} + +void +mem_buddy_setup() +{ + memset(s_buddy_l0, 0xFF, sizeof(s_buddy_l0)); + memset(s_buddy_l1, 0xFF, sizeof(s_buddy_l1)); + memset(s_buddy_l2, 0xFF, sizeof(s_buddy_l2)); + memset(s_buddy_l3, 0xFF, sizeof(s_buddy_l3)); + + for(int i = 0; i < boot_memorymap.count; i++) { + struct MemoryMapEntry *entry = &boot_memorymap.entries[i]; + klogf("%2i\t%#016X -> %#016X (%i)\n", + i, entry->base, entry->base + entry->length, entry->usable); + if(entry->base > MEMMAP_BUDDY_LIMIT) continue; + size_t length = entry->length; + if(entry->base + length > MEMMAP_BUDDY_LIMIT) length = MEMMAP_BUDDY_LIMIT - (entry->base + length); + if(entry->usable) + mem_buddy_free_range(entry->base, entry->length); + } + + s_buddies[0][0] |= 1; + s_buddies[1][0] |= 1; + s_buddies[2][0] |= 1; + s_buddies[3][0] |= 1; +} diff --git a/mem/buddymap.d b/mem/buddymap.d new file mode 100644 index 0000000..7132cfe --- /dev/null +++ b/mem/buddymap.d @@ -0,0 +1,2 @@ +mem/buddymap.o: mem/buddymap.c mem/buddymap.h mem/memory.h mem/slab.h \ + lib/string.h boot/boot.h io/log.h diff --git a/mem/buddymap.h b/mem/buddymap.h new file mode 100644 index 0000000..2f4f5dc --- /dev/null +++ b/mem/buddymap.h @@ -0,0 +1,21 @@ +#ifndef JOVE_MEMORY_BUDDYMAP_H +#define JOVE_MEMORY_BUDDYMAP_H 1 + +#include "memory.h" +#include <stdint.h> +#include <stddef.h> + +#define MEMMAP_BUDDY_LIMIT (4 * GiB) + +void mem_buddy_set_range(uintptr_t base, size_t length); +void mem_buddy_free_range(uintptr_t base, size_t length); +uintptr_t mem_buddy_takefree(size_t layer); + +#define mem_buddy_takefree_4k() mem_buddy_takefree(0) +#define mem_buddy_takefree_8k() mem_buddy_takefree(1) +#define mem_buddy_takefree_16k() mem_buddy_takefree(2) +#define mem_buddy_takefree_32k() mem_buddy_takefree(3) + +void mem_buddy_setup(void); + +#endif diff --git a/mem/memory.h b/mem/memory.h new file mode 100644 index 0000000..eb30217 --- /dev/null +++ b/mem/memory.h @@ -0,0 +1,49 @@ +#ifndef JOVE_MEM_H +#define JOVE_MEM_H 1 + +#define PAGESIZE 4096ULL +#define KiB 1024ULL +#define MiB (KiB * KiB) +#define GiB (MiB * KiB) +#define TiB (GiB * KiB) + +#include <stddef.h> +#include <stdint.h> +#include <stdbool.h> +typedef uintptr_t physptr_t; + +#include "slab.h" + +/*Linear*/ +void mem_paging_setup(void); + +physptr_t mem_linear_tophys(uintptr_t virt); + +/**Check if pointer is within valid memory. + * @param ptr pointer to check. + * @return if the pointer is invalid.*/ +bool mem_check_ptr(const void *ptr); + +/**Make sure the range indicated is available in memory. + * If necessary, allocate new pages using the passed flags + * @param from start of the range. + * @param to end of the range. + * @param rw flag to mark page is writeable. + * @param user flag to mark page as user accessable*/ +void mem_ensure_range(uintptr_t from, uintptr_t to, bool rw, bool user); + +void mem_slab_setup(void); +void mem_slabcache_new(struct SlabCache *cache, char *name, size_t objsize); + +void* mem_slab_alloc(struct SlabCache *cache); +void mem_slab_free(struct SlabCache *cache, void *ptr); + +void* mem_alloc(size_t width); +void mem_free(void *ptr); + +/*Physical*/ + +physptr_t mem_phys_take4k(void); +void mem_phys_reserve(physptr_t start, size_t len); + +#endif diff --git a/mem/phys.c b/mem/phys.c new file mode 100644 index 0000000..e56a4d6 --- /dev/null +++ b/mem/phys.c @@ -0,0 +1,14 @@ +#include "memory.h" +#include "buddymap.h" + +physptr_t +mem_phys_take4k(void) +{ + return mem_buddy_takefree_4k(); +} + +void +mem_phys_reserve(physptr_t start, size_t len) +{ + mem_buddy_set_range(start, len); +} diff --git a/mem/phys.d b/mem/phys.d new file mode 100644 index 0000000..192b714 --- /dev/null +++ b/mem/phys.d @@ -0,0 +1 @@ +mem/phys.o: mem/phys.c mem/memory.h mem/slab.h mem/buddymap.h diff --git a/mem/slab.c b/mem/slab.c new file mode 100644 index 0000000..75b8302 --- /dev/null +++ b/mem/slab.c @@ -0,0 +1,201 @@ +#include "slab.h" +#include "memory.h" +#include "lib/format.h" +#include "lib/string.h" +#include "lib/jove.h" +#include "io/log.h" + +extern void *_kernel_end; + +static uintptr_t s_addr_next_free; + +#define GENERIC_CACHEC 8 +static struct SlabCache s_generic_caches[GENERIC_CACHEC]; + +static uintptr_t +s_next_free(size_t width) +{ + uintptr_t ret = s_addr_next_free; + s_addr_next_free += width; + mem_ensure_range(ret, s_addr_next_free, true, false); + return ret; +} + +static int +s_get_free_listw(size_t slabw, size_t objw) +{ + int freelistc = 1; + while(freelistc < 256) { + int maxobjc = (slabw - (freelistc * sizeof(uintptr_t))) / objw; + if(maxobjc <= freelistc) return maxobjc; + freelistc++; + } + return freelistc; +} + +static struct SlabDescriptor +*s_slab_new(struct SlabCache *cache, struct SlabDescriptor *last) +{ + size_t slab_width = (cache->slab_pages * PAGESIZE); + uintptr_t descr_base = s_next_free(slab_width); + struct SlabDescriptor *descr = (struct SlabDescriptor*)descr_base; + + size_t free_listc = s_get_free_listw( + slab_width - sizeof(struct SlabDescriptor), + cache->obj_size); + size_t descriptor_width = sizeof(struct SlabDescriptor) + + (free_listc * sizeof(uintptr_t)); + uintptr_t obj_base = descr_base + descriptor_width; + + if(free_listc < 8) { + free_listc = ((slab_width - sizeof(struct SlabDescriptor)) / cache->obj_size); + descr = mem_alloc(sizeof(struct SlabDescriptor) + (free_listc * sizeof(uintptr_t))); + obj_base = descr_base; + } + + *descr = (struct SlabDescriptor) { + .prev = last, + .next = (last == NULL ? NULL : last->next), + .slab_base = (void*)descr_base, + .obj_base = (void*)obj_base, + .free_count = free_listc, + .free_index = free_listc - 1 + }; + for(size_t i = 0; i < free_listc; i++) { + descr->free[i] = obj_base + (i * cache->obj_size); + } + + return descr; +} + +void +mem_slabcache_new(struct SlabCache *cache, char *name, size_t objsize) +{ + if(objsize % 8 > 0) objsize += (8 - (objsize % 8)); + size_t pages = objsize > 512 ? (objsize >> 9) : 1; + *cache = (struct SlabCache){ + .obj_size = objsize, + .slab_pages = pages, + .list_free = NULL, + .list_partial = NULL, + .list_full = NULL + }; + size_t namelen = strlen(name); + namelen = namelen > 32 ? 32 : namelen; + memcpy(cache->name, name, namelen); + + //Allocate the first slab + cache->list_free = s_slab_new(cache, NULL); +} + +void* +mem_slab_alloc(struct SlabCache *cache) +{ + // Get a free slab + struct SlabDescriptor *slab = NULL; + if(cache->list_partial != NULL) slab = cache->list_partial; + if(slab == NULL && cache->list_free != NULL) { + slab = cache->list_free; + cache->list_free = slab->next; + } + if(slab == NULL) slab = s_slab_new(cache, cache->list_free); + cache->list_partial = slab; + + // Take an object from the slab. + uintptr_t objaddr = slab->free[slab->free_index]; + slab->free_index -= 1; + + if(slab->free_index < 0) { + slab->next = cache->list_full; + cache->list_full = slab; + } + return (void*)objaddr; +} + +void +mem_slab_free(struct SlabCache *cache, void *ptr) +{ + uintptr_t addr = (uintptr_t)ptr; + //Look for the pointer in the bounds of every slab + for(struct SlabDescriptor *slab = cache->list_full; + slab != NULL; slab = slab->next) + { + uintptr_t base = (uintptr_t)slab->obj_base; + uintptr_t limit = ((uintptr_t)slab->slab_base) + + (cache->slab_pages * PAGESIZE); + if(addr > limit || addr < base) continue; + if((addr - base) % cache->obj_size != 0) { + klogf("Tried to free offset pointer %#016X in slab %s\n", + addr, cache->name); + return; + } + slab->free_index++; + slab->free[slab->free_index] = addr; + + cache->list_full = slab->next; + slab->next = cache->list_partial; + cache->list_partial = slab; + return; + } + for(struct SlabDescriptor *slab = cache->list_partial; + slab != NULL; slab = slab->next) + { + uintptr_t base = (uintptr_t)slab->obj_base; + uintptr_t limit = ((uintptr_t)slab->slab_base) + + (cache->slab_pages * PAGESIZE); + if(addr > limit || addr < base) continue; + if((addr - base) % cache->obj_size != 0) { + klogf("Tried to free offset pointer %#016X in slab %s\n", + addr, cache->name); + return; + } + slab->free_index++; + slab->free[slab->free_index] = addr; + + if(slab->free_index == (slab->free_count - 1)) { + cache->list_partial = slab->next; + slab->next = cache->list_free; + cache->list_free = slab; + } + return; + } +} + +void* +mem_alloc(size_t width) +{ + size_t width_log2 = (__builtin_clz(width) ^ 31) + 1; + if(width_log2 < 6) width_log2 = 6; + width_log2 -= 6; + if(width_log2 >= GENERIC_CACHEC) { + klogf("Allocation size %i too big for generic caches!\n", width); + return NULL; + } + + struct SlabCache *generic_cache = &s_generic_caches[width_log2]; + return mem_slab_alloc(generic_cache); +} + +void +mem_free(void *ptr) +{ + for(int i = 0; i < GENERIC_CACHEC; i++) { + mem_slab_free(&s_generic_caches[i], ptr); + } +} + +void +mem_slab_setup(void) +{ + s_addr_next_free = (uintptr_t)&_kernel_end; + s_addr_next_free = ((s_addr_next_free >> 12) + 1) << 12; + s_get_free_listw(PAGESIZE - sizeof(struct SlabDescriptor), 32); + + for(int i = 0; i < GENERIC_CACHEC; i++) + { + size_t objsize = 1 << (i + 6); + char slab_name[SLABCACHE_NAME_LIMIT]; + sfmt(slab_name, SLABCACHE_NAME_LIMIT, "generic_%i", 1 << (i + 6)); + mem_slabcache_new(&s_generic_caches[i], slab_name, objsize); + } +} diff --git a/mem/slab.d b/mem/slab.d new file mode 100644 index 0000000..5cffd4c --- /dev/null +++ b/mem/slab.d @@ -0,0 +1,2 @@ +mem/slab.o: mem/slab.c mem/slab.h mem/memory.h lib/format.h lib/string.h \ + lib/jove.h io/log.h diff --git a/mem/slab.h b/mem/slab.h new file mode 100644 index 0000000..074d278 --- /dev/null +++ b/mem/slab.h @@ -0,0 +1,33 @@ +#ifndef JOVE_MEMORY_SLAB_H +#define JOVE_MEMORY_SLAB_H 1 + +#include <stdint.h> +#include <stddef.h> +#include <stdbool.h> + +#define SLABCACHE_NAME_LIMIT 32 +struct SlabCache +{ + char name[SLABCACHE_NAME_LIMIT]; + + struct SlabDescriptor *list_free; + struct SlabDescriptor *list_partial; + struct SlabDescriptor *list_full; + + size_t obj_size; + size_t slab_pages; +}; + +struct SlabDescriptor +{ + struct SlabDescriptor *prev; + struct SlabDescriptor *next; + void *slab_base; + void *obj_base; + + size_t free_count; + int free_index; + uintptr_t free[]; +}; + +#endif diff --git a/tsk/tasking.h b/tsk/tasking.h new file mode 100644 index 0000000..d217171 --- /dev/null +++ b/tsk/tasking.h @@ -0,0 +1,25 @@ +#ifndef JOVE_TASKING_H +#define JOVE_TASKING_H 1 + +#include <stddef.h> +#include <stdint.h> + +typedef size_t tid_t; + +struct Thread +{ + struct Thread *next; + tid_t id; + uintptr_t kbp; + size_t perm; +}; + +extern struct Thread *thread_current; + +void tasking_setup(void); + +struct Thread *thread_new(struct Thread *parent); +struct Thread *thread_get(tid_t id); +void thread_perm_release(struct Thread *thread, size_t mask); + +#endif diff --git a/tsk/thread.c b/tsk/thread.c new file mode 100644 index 0000000..7fbafa6 --- /dev/null +++ b/tsk/thread.c @@ -0,0 +1,7 @@ +#include "tasking.h" + +void +thread_perm_release(struct Thread *thread, size_t mask) +{ + thread->perm &= ~mask; +} diff --git a/tsk/thread.d b/tsk/thread.d new file mode 100644 index 0000000..1869275 --- /dev/null +++ b/tsk/thread.d @@ -0,0 +1 @@ +tsk/thread.o: tsk/thread.c tsk/tasking.h diff --git a/usr/elf.h b/usr/elf.h new file mode 100644 index 0000000..d805e55 --- /dev/null +++ b/usr/elf.h @@ -0,0 +1,12 @@ +#ifndef JOVE_USER_ELF_H +#define JOVE_USER_ELF_H 1 + +#include <stddef.h> + +/**Load an ELF file into usermode memory. + * @param data pointer to ELF file data buffer + * @param len length of ELF file data buffer + * @return entry point for loaded ELF exec*/ +void *elf_load(const void *data, size_t len); + +#endif diff --git a/usr/syscall.c b/usr/syscall.c new file mode 100644 index 0000000..0ea5700 --- /dev/null +++ b/usr/syscall.c @@ -0,0 +1,23 @@ +#include "syscall.h" +#include "mem/memory.h" +#include "io/log.h" + +int _syscall_handler_log(struct syscall_log *req) +{ + klogf("Message ptr %#016X\n", req->message); + if(!mem_check_ptr(req->message)) return -1; + klogf("%s", req->message); + return 0; +} + +void *_syscall_handlers[SYSCALL_COUNT] = { + _syscall_handler_log +}; + +int +syscall_handler(syscall_t *req) +{ + if(!mem_check_ptr(req)) return -1; + if(req->id >= SYSCALL_COUNT) return -1; + return ((syscall_handler_t)(_syscall_handlers[req->id]))(req); +} diff --git a/usr/syscall.d b/usr/syscall.d new file mode 100644 index 0000000..9d5acbf --- /dev/null +++ b/usr/syscall.d @@ -0,0 +1,2 @@ +usr/syscall.o: usr/syscall.c usr/syscall.h abi/syscall.h mem/memory.h \ + mem/slab.h io/log.h diff --git a/usr/syscall.h b/usr/syscall.h new file mode 100644 index 0000000..49beb85 --- /dev/null +++ b/usr/syscall.h @@ -0,0 +1,10 @@ +#ifndef JOVE_USER_SYSCALL_H +#define JOVE_USER_SYSCALL_H 1 + +#include "abi/syscall.h" + +typedef int (*syscall_handler_t)(syscall_t*); + +int _syscall_handler_log(struct syscall_log *req); + +#endif diff --git a/usr/umode.c b/usr/umode.c new file mode 100644 index 0000000..4ef5306 --- /dev/null +++ b/usr/umode.c @@ -0,0 +1,31 @@ +#include "umode.h" +#include "elf.h" +#include "boot/cmdline.h" +#include "lib/jove.h" +#include "ird/initrd.h" +#include "mem/memory.h" + +void +umode_setup(void) +{ + extern void syscall_setup_syscall(void); + syscall_setup_syscall(); + + const char *init_path = cmdline_get("init"); + if(init_path == NULL) + kpanic("Missing path to init ELF file / binary\n"); + + struct InitrdFile *init_file = ird_getfile(init_path); + if(init_file == NULL) + kpanic("Missing init file %s in initrd\n", init_path); + + void (*entry_point)(void) = elf_load(init_file->data, init_file->size); + if(entry_point == NULL) + kpanic("Init file %s is incorrectly formatted (want ELF64)\n", init_path); + + void *user_stack = (void*)(0x00007FFFFFFFFFFF); + mem_ensure_range((uintptr_t)user_stack & ~0xFFF, (uintptr_t)user_stack, true, true); + + klogf("User entry point %#016X\n", entry_point); + umode_enter(entry_point, user_stack); +} diff --git a/usr/umode.d b/usr/umode.d new file mode 100644 index 0000000..54c004b --- /dev/null +++ b/usr/umode.d @@ -0,0 +1,3 @@ +usr/umode.o: usr/umode.c usr/umode.h usr/elf.h boot/cmdline.h \ + lib/hashtable.h lib/linkedlist.h lib/jove.h ird/initrd.h ird/tar.h \ + lib/linkedlist.h mem/memory.h mem/slab.h diff --git a/usr/umode.h b/usr/umode.h new file mode 100644 index 0000000..2755abe --- /dev/null +++ b/usr/umode.h @@ -0,0 +1,8 @@ +#ifndef JOVE_UMODE_H +#define JOVE_UMODE_H 1 + +void umode_setup(void); + +void umode_enter(void (*entry)(void), void *stack); + +#endif |