summaryrefslogtreecommitdiff
path: root/boot/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'boot/main.c')
-rw-r--r--boot/main.c159
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);
}
+