diff options
Diffstat (limited to 'boot')
| -rw-r--r-- | boot/Makefile | 4 | ||||
| -rw-r--r-- | boot/efi/image.c | 48 | ||||
| -rw-r--r-- | boot/efi/start.c | 10 | ||||
| -rw-r--r-- | boot/fake_syscall_entry.S | 25 | ||||
| -rw-r--r-- | boot/kjarna.h | 21 | ||||
| -rw-r--r-- | boot/main.c | 159 |
6 files changed, 249 insertions, 18 deletions
diff --git a/boot/Makefile b/boot/Makefile index 7f4be6b..3c47336 100644 --- a/boot/Makefile +++ b/boot/Makefile @@ -14,6 +14,8 @@ CFLAGS += --target=$(ARCH)-unknown-windows -fshort-wchar -mno-avx \ -mno-sse -mno-mmx -funsigned-char -Wno-pointer-sign -ggdb \ -fno-ms-extensions +ASFLAGS += --target=$(ARCH)-unknown-windows -ggdb + LDFLAGS := --target=$(ARCH)-unknown-windows -nostdlib -ggdb \ -Wl,-entry:_start,-subsystem:efi_application -fuse-ld=lld-link @@ -22,7 +24,7 @@ all: $(IMAGE) CRT_OBJS := start.o bind.o LIB_OBJS := memcmp.o memset.o memcpy.o memmove.o string.o elf64.o printf.o stdio.o EFI_OBJS := file.o memory.o protocol.o image.o -APP_OBJS := main.o +APP_OBJS := main.o fake_syscall_entry.o OBJS := $(CRT_OBJS) $(LIB_OBJS) $(EFI_OBJS) $(APP_OBJS) DEPS := $(OBJS:.o=.d) diff --git a/boot/efi/image.c b/boot/efi/image.c index 25e1911..b9b4891 100644 --- a/boot/efi/image.c +++ b/boot/efi/image.c @@ -14,12 +14,6 @@ #include <efi/error.h> -struct image_buffer -{ - char *base; - size_t length; -}; - static int create_image_buffer(int fd, struct image_buffer *buffer) { if ((buffer->length = elf64_size_fd(fd)) == 0) @@ -89,9 +83,9 @@ kjarna_image_entry_func *image_entry_addr(struct image_buffer *buffer) return (kjarna_image_entry_func *)(buffer->base + ehdr->e_entry); } -int image_start(void) +struct kjarna_interface get_boot_interface(void) { - struct kjarna_interface interface = + struct kjarna_interface result = { efi_open, efi_close, @@ -102,6 +96,30 @@ int image_start(void) efi_munmap }; + return result; +} + +struct kjarna_boot_image get_boot_image(void) +{ + struct kjarna_boot_image image = + { + { nullptr, 0 }, + nullptr + }; + + image.buffer = load_image(); + + if (image.buffer.base != nullptr) + { + image.entry = image_entry_addr(&image.buffer); + } + + return image; +} + +int image_start(void) +{ + struct kjarna_interface interface = get_boot_interface(); struct kjarna_entry_params params = { &interface, @@ -110,21 +128,19 @@ int image_start(void) nullptr }; - struct image_buffer buffer = load_image(); - - kjarna_image_entry_func *image_entry = nullptr; + struct kjarna_boot_image image = get_boot_image(); - if (buffer.base != nullptr) + if (image.buffer.base != nullptr) { - image_entry = image_entry_addr(&buffer); + image.entry = image_entry_addr(&image.buffer); } - if (image_entry != nullptr) + if (image.entry != nullptr) { - image_entry(¶ms); + image.entry(¶ms); } - if (munmap(buffer.base, buffer.length) == -1) + if (munmap(image.buffer.base, image.buffer.length) == -1) { write(STDOUT_FILENO, "failed unmap\r\n", -1); diff --git a/boot/efi/start.c b/boot/efi/start.c index f1f4ec7..fc44377 100644 --- a/boot/efi/start.c +++ b/boot/efi/start.c @@ -28,8 +28,18 @@ EFI_STATUS _start(EFI_HANDLE handle, EFI_SYSTEM_TABLE *system_table) EFI_GUID lip_guid = EFI_LOADED_IMAGE_PROTOCOL_GUID; EFI_LOADED_IMAGE_PROTOCOL *lip = efi_get_protocol(efi_context->handle, &lip_guid); + uint64_t text_base; + uint64_t data_base; + printf("DEBUG: EFI_LOADED_IMAGE_PROTOCOL { .ImageBase = %p }\n", lip->ImageBase); __asm__ volatile ("movq %0, %%rax" :: "r"(lip->ImageBase) : "rax"); + __asm__ volatile ( + "lea .text, %0\n" + "lea .data, %1\n" + : "=r"(text_base), "=r"(data_base) + : + ); + printf("DEBUG: .text %p .data %p\n", text_base, data_base); bool wait = true; while(wait) diff --git a/boot/fake_syscall_entry.S b/boot/fake_syscall_entry.S new file mode 100644 index 0000000..ff2428d --- /dev/null +++ b/boot/fake_syscall_entry.S @@ -0,0 +1,25 @@ +.section .text + +.extern fake_syscall_handler + +.global fake_syscall_entry +fake_syscall_entry: + hlt + +.global fake_syscall_return +fake_syscall_return: + cli + mov %rdi, %rsp + lgdt (%rsp) + add $16, %rsp + + movw (%rsp), %ds + movw (%rsp), %es + movw (%rsp), %fs + movw (%rsp), %gs + movw (%rsp), %ss + + add $8, %rsp + + lretq + diff --git a/boot/kjarna.h b/boot/kjarna.h index 81bb79b..38a550f 100644 --- a/boot/kjarna.h +++ b/boot/kjarna.h @@ -1,7 +1,28 @@ #pragma once +#pragma once + #include <kjarna/interface.h> int main(int argc, char **argv); int image_start(void); +void *malloc(size_t size); +void *calloc(size_t count, size_t size); +void free(void *block); + +struct image_buffer +{ + char *base; + size_t length; +}; + +struct kjarna_boot_image +{ + struct image_buffer buffer; + kjarna_image_entry_func *entry; +}; + +struct kjarna_interface get_boot_interface(void); +struct kjarna_boot_image get_boot_image(void); + 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); } + |
