#include "kjarna.h" #include #include #include #include /* * 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 interrupt 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 latency. * 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. */ 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 struct segment_descriptor const fake_syscall_gdt[] = { { 0 }, { 0xffff, 0, 0, 0x9a, 0xaf, 0}, { 0xffff, 0, 0, 0x92, 0xcf, 0} }; SYSV_ABI static void fake_syscall_handler(void) { while (true); } static void *return_rsp; SYSV_ABI void fake_syscall_entry(void); SYSV_ABI void fake_syscall_return(void *target_rsp, void **return_rsp, SYSV_ABI void (*callback)()); static void install_syscall_handler(void) { union msr_lstar lstar = { (uintptr_t)fake_syscall_entry }; union msr_star star = { { 0, 1 << 3, 1 << 3 | 3 } }; msr_write(MSR_INDEX_LSTAR, lstar.value); msr_write(MSR_INDEX_STAR, star.value); uint64_t efer = msr_read(MSR_INDEX_EFER); efer |= 1; msr_write(MSR_INDEX_EFER, efer); } struct context_stack_frame { struct descriptor_table_register_long lret_gdtr; uint64_t lret_ds; uint64_t lret_rip; uint64_t lret_cs; }; static 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, &return_rsp, fake_syscall_handler); while (true); } #include int main(int argc, char **argv) { (void)argc; (void)argv; struct kjarna_boot_image boot_image = get_boot_image(); install_syscall_handler(); enter_boot_image(&boot_image); }