diff options
Diffstat (limited to 'boot/main.c')
| -rw-r--r-- | boot/main.c | 159 |
1 files changed, 158 insertions, 1 deletions
diff --git a/boot/main.c b/boot/main.c index 38c913f..570b703 100644 --- a/boot/main.c +++ b/boot/main.c @@ -1,9 +1,166 @@ #include "kjarna.h" +#include <stdnoreturn.h> + +/* + * Fake syscall mechanism - + * + * We have a GDT with entries for supervisory mode. These serve as the + * entries to satisfy the requirements of the SYSCALL instruction, as + * for some reason (possibly intentionally) the OVMF GDT is not laid out + * in a way to make use of the SYSCALL instruction possible. + * + * This causes us to have to work inside of constraints during loading time + * - All "user" mode execution entirely blocks interrupts processing. That + * means that user mode code must not execute "hlt", or the system will + * be locked. + * - When (if?) user input is required, it is always buffered. It is possible + * to simulate unbuffered input, at the cost of one syscall per transfer + * from "kernel" side to "user" side. This will cause high input lag. + * We will not be running Quake in this environment. + * + * These constraints are probably fine, as the loading process only needs to + * open files, map memory, etc. + */ + + +struct dtr64 +{ + uint16_t limit; + uint64_t base; +} __attribute__((packed, aligned(16))); + +struct segment_descriptor +{ + uint16_t limit0; + uint16_t base0; + uint8_t base16; + uint8_t access; + uint8_t flags_limit16; + uint8_t base24; +} __attribute__((packed, aligned(8))); + +union msr_star +{ + struct + { + uint32_t target_eip; + uint16_t syscall_selector_base; + uint16_t sysret_selector_base; + } + fields; + uint64_t value; +}; + +union msr_lstar +{ + void (*syscall_handler)(void); + uint64_t value; +}; + +void *stack_alloc(void **stack_pointer, size_t alloc_size) +{ + void *block = *(char **)stack_pointer -= alloc_size; + + // maintain alignment + *(char **)stack_pointer -= alloc_size % sizeof(size_t); + + return block; +} + +static void msr_write(uint32_t index, uint64_t value) +{ + uint32_t low = value & 0xffffffff; + uint32_t high = value >> 32; + + __asm__ volatile + ( + "wrmsr\n\t" + :: "a"(low), "d"(high), "c"(index) + ); +} + +uint64_t msr_read(uint32_t index) +{ + uint32_t low; + uint32_t high; + + __asm__ volatile + ( + "rdmsr\n\t" + : "=a"(low), "=d"(high) + : "c"(index) + ); + + return ((uint64_t)high << 32)|low; +} + +static struct segment_descriptor const fake_syscall_gdt[] = +{ + { 0 }, + { 0xffff, 0, 0, 0x9a, 0xaf, 0}, + { 0xffff, 0, 0, 0x92, 0xcf, 0} +}; + +static noreturn void fake_syscall_entry(void) +{ + while (true); +} + +SYSV_ABI void fake_syscall_return(void *target_rsp); + +static void install_syscall_handler(void) +{ + union msr_lstar lstar = { fake_syscall_entry }; + union msr_star star = { { 0, 1 << 3, 1 << 3 | 3 } }; + + msr_write(0xc0000082, lstar.value); + msr_write(0xc0000081, star.value); + + uint64_t efer = msr_read(0xc0000080); + efer |= 1; + msr_write(0xc0000080, efer); +} + +struct context_stack_frame +{ + struct dtr64 lret_gdtr; + uint64_t lret_ds; + uint64_t lret_rip; + uint64_t lret_cs; +}; + +static noreturn void enter_boot_image(struct kjarna_boot_image *image) +{ + size_t boot_stack_size = 0x10000; + char *boot_image_stack = calloc(1, boot_stack_size); + void *boot_image_stack_head = boot_image_stack + boot_stack_size; + + struct context_stack_frame *stack_frame = stack_alloc(&boot_image_stack_head, sizeof(*stack_frame)); + + stack_frame->lret_cs = 8; + stack_frame->lret_ds = 16; + stack_frame->lret_rip = (uintptr_t)image->entry; + + stack_frame->lret_gdtr.limit = sizeof(fake_syscall_gdt) - 1; + stack_frame->lret_gdtr.base = (uintptr_t)fake_syscall_gdt; + + fake_syscall_return(boot_image_stack_head); + + while (true); +} + +#include <libc/stdio.h> int main(int argc, char **argv) { (void)argc; (void)argv; - return image_start(); + + printf("DEBUG: alignof(struct dtr64) = %zu", alignof(struct dtr64)); + printf("DEBUG: sizeof(struct dtr64) = %zu", sizeof(struct dtr64)); + struct kjarna_boot_image boot_image = get_boot_image(); + install_syscall_handler(); + enter_boot_image(&boot_image); } + |
