diff options
Diffstat (limited to 'boot')
| -rw-r--r-- | boot/Makefile | 2 | ||||
| -rw-r--r-- | boot/fake_syscall.c | 245 | ||||
| -rw-r--r-- | boot/fake_syscall.h | 8 | ||||
| -rw-r--r-- | boot/fake_syscall_entry.S | 12 | ||||
| -rw-r--r-- | boot/kjarna.h | 3 | ||||
| -rw-r--r-- | boot/main.c | 198 |
6 files changed, 266 insertions, 202 deletions
diff --git a/boot/Makefile b/boot/Makefile index 49b6639..29151cd 100644 --- a/boot/Makefile +++ b/boot/Makefile @@ -24,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 msr.o EFI_OBJS := file.o memory.o protocol.o image.o -APP_OBJS := main.o fake_syscall_entry.o +APP_OBJS := main.o fake_syscall_entry.o fake_syscall.o OBJS := $(CRT_OBJS) $(LIB_OBJS) $(EFI_OBJS) $(APP_OBJS) DEPS := $(OBJS:.o=.d) diff --git a/boot/fake_syscall.c b/boot/fake_syscall.c new file mode 100644 index 0000000..e2aaeca --- /dev/null +++ b/boot/fake_syscall.c @@ -0,0 +1,245 @@ +#include <kjarna/syscall.h> +#include "fake_syscall.h" +#include "kjarna.h" +#include <bits/x86_64/descriptor.h> +#include <kjarna/interface.h> +#include <posix/unistd.h> +#include <asm/x86_64/msr.h> +#include <bits/x86_64/msr.h> +#include <posix/sys/mman.h> +#include <posix/fcntl.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 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 *exit_fake_syscall_stack; + +SYSV_ABI void fake_syscall_entry(void); +SYSV_ABI int64_t fake_syscall_begin(void *stack, void **return_stack_save); +SYSV_ABI int64_t fake_syscall_end(int status, void *stack); + +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; +} + +struct syscall_context_return_state +{ + uint64_t rflags; + struct descriptor_table_register_long gdtr; + uint64_t return_ds; + uintptr_t return_rip; + uint64_t return_cs; +}; + +struct syscall_context +{ + uint64_t pad; + uint64_t syscall_index; + syscall_delegate_parameters parameters; + struct syscall_context_return_state state; +}; + +static struct +{ + uint16_t cs; + uint16_t ds; + uint64_t rflags; + struct descriptor_table_register_long gdtr; +} +system_context; + +static void save_system_context() +{ + __asm__ volatile ( + "movw %%cs, %0\n" + "movw %%ds, %1\n" + "pushfq\n" + "popq %3\n" + "sgdt %2\n" + : + "=m"(system_context.cs), + "=m"(system_context.ds), + "=m"(system_context.gdtr), + "=g"(system_context.rflags) + : + ); +} + +static void restore_system_context() +{ + __asm__ volatile ( + "lgdt %0\n" + "mov %w1, %%ds\n" + "mov %w1, %%es\n" + "mov %w1, %%fs\n" + "mov %w1, %%gs\n" + "mov %w1, %%gs\n" + "mov %w1, %%ss\n" + "push %q2\n" + "lea .Lflush(%%rip), %%rax\n" + "push %%rax\n" + "lretq\n" + ".Lflush:\n" + "push %q3\n" + "popfq\n" + : + : + "m"(system_context.gdtr), + "m"(system_context.ds), + "m"(system_context.cs), + "g"(system_context.rflags) + : + "rax" + ); + +} + +static struct segment_descriptor const fake_syscall_gdt[] = +{ + { 0 }, + { 0xffff, 0, 0, 0x9a, 0xaf, 0}, + { 0xffff, 0, 0, 0x92, 0xcf, 0} +}; + +static int64_t syscall_delegate_open(syscall_delegate_parameters params) +{ + return open((char *)params[0], (int)params[1], (int)params[2]); +} + +static int64_t syscall_delegate_close(syscall_delegate_parameters params) +{ + return close((int)params[0]); +} + +static int64_t syscall_delegate_lseek(syscall_delegate_parameters params) +{ + return lseek((int)params[0], (off_t)params[1], (int)params[2]); +} + +static int64_t syscall_delegate_read(syscall_delegate_parameters params) +{ + return read((int)params[0], (void *)params[1], (size_t)params[2]); +} + +static int64_t syscall_delegate_write(syscall_delegate_parameters params) +{ + return write((int)params[0], (void *)params[1], (size_t)params[2]); +} + +static int64_t syscall_delegate_mmap(syscall_delegate_parameters params) +{ + return (int64_t) mmap((void *)params[0], (size_t)params[1], (int)params[2], (int)params[3], (int)params[4], (off_t)params[5]); +} + +static int64_t syscall_delegate_munmap(syscall_delegate_parameters params) +{ + return munmap((void *)params[0], (size_t)params[1]); +} + +static int64_t syscall_delegate_exit(syscall_delegate_parameters params) +{ + fake_syscall_end((int)params[0], exit_fake_syscall_stack); + return -1; +} + +static syscall_delegate *syscall_handler_delegates[NR_SYSCALLS] = +{ + [SYS_OPEN] = syscall_delegate_open, + [SYS_CLOSE] = syscall_delegate_close, + [SYS_LSEEK] = syscall_delegate_lseek, + [SYS_READ] = syscall_delegate_read, + [SYS_WRITE] = syscall_delegate_write, + [SYS_MMAP] = syscall_delegate_mmap, + [SYS_MUNMAP] = syscall_delegate_munmap, + [SYS_EXIT] = syscall_delegate_exit +}; + +SYSV_ABI int64_t fake_syscall_handler(struct syscall_context *context) +{ + int index = (int)context->syscall_index; + if (index > NR_SYSCALLS) + { + // TODO: ENOSYS + return -1; + } + + syscall_delegate *delegate = syscall_handler_delegates[index]; + + if (delegate == nullptr) + { + // TODO: ENOSYS + return -1; + } + + restore_system_context(); + int64_t result = delegate(context->parameters); + save_system_context(); + + return result; +} + +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); +} + +static void *create_fake_syscall_stack(void *entry_addr) +{ + constexpr size_t fake_syscall_stack_size = 0x20000; // 128KiB stack + char *stack_pointer_base = mmap(nullptr, fake_syscall_stack_size, 0, 0, -1, 0); + void *stack_pointer_head = stack_pointer_base + fake_syscall_stack_size; + + struct syscall_context *context = stack_alloc(&stack_pointer_head, sizeof(*context)); + + context->state.rflags = 0; + context->state.gdtr.base = (uintptr_t)&fake_syscall_gdt; + context->state.gdtr.limit = sizeof(fake_syscall_gdt) - 1; + context->state.return_ds = 16; + context->state.return_cs = 8; + context->state.return_rip = (uintptr_t)entry_addr; + + return stack_pointer_head; +} + +int64_t fake_syscall_start(struct kjarna_boot_image *image) +{ + install_syscall_handler(); + void *fake_stack = create_fake_syscall_stack((void *)image->entry); + save_system_context(); + int64_t result = fake_syscall_begin(fake_stack, &exit_fake_syscall_stack); + return result; + while(true); +} + diff --git a/boot/fake_syscall.h b/boot/fake_syscall.h new file mode 100644 index 0000000..f3da647 --- /dev/null +++ b/boot/fake_syscall.h @@ -0,0 +1,8 @@ +#include <stdint.h> +#include "kjarna.h" + +typedef uint64_t syscall_delegate_parameters[6]; +typedef int64_t (syscall_delegate)(syscall_delegate_parameters params); + +int64_t fake_syscall_start(struct kjarna_boot_image *image); + diff --git a/boot/fake_syscall_entry.S b/boot/fake_syscall_entry.S index f75c2d7..2ac3c11 100644 --- a/boot/fake_syscall_entry.S +++ b/boot/fake_syscall_entry.S @@ -1,8 +1,8 @@ .section .text .global fake_syscall_entry -.global fake_syscall_start - +.global fake_syscall_begin +.global fake_syscall_end .extern fake_syscall_handler /* @@ -24,8 +24,14 @@ 104 state.return_cs */ -fake_syscall_start: +fake_syscall_end: + mov %rdi, %rax + mov %rsi, %rsp + ret + +fake_syscall_begin: cli + mov %rsp, (%rsi) mov %rdi, %rsp jmp .L_fake_syscall_return diff --git a/boot/kjarna.h b/boot/kjarna.h index 38a550f..be77a4b 100644 --- a/boot/kjarna.h +++ b/boot/kjarna.h @@ -5,7 +5,6 @@ #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); @@ -23,6 +22,6 @@ struct kjarna_boot_image 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 ed18578..55eab00 100644 --- a/boot/main.c +++ b/boot/main.c @@ -1,196 +1,5 @@ #include "kjarna.h" -#include <stdnoreturn.h> -#include <asm/x86_64/msr.h> -#include <bits/x86_64/msr.h> -#include <bits/x86_64/descriptor.h> -#include <libc/stdio.h> -#include <posix/sys/mman.h> -#include <posix/unistd.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 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} -}; - -typedef uint64_t syscall_delegate_parameters[6]; - -static struct -{ - uint16_t cs; - uint16_t ds; - uint64_t rflags; - struct descriptor_table_register_long gdtr; -} -uefi_context; - -static void save_uefi_context() -{ - __asm__ volatile ( - "movw %%cs, %0\n" - "movw %%ds, %1\n" - "pushfq\n" - "popq %3\n" - "sgdt %2\n" - : - "=m"(uefi_context.cs), - "=m"(uefi_context.ds), - "=m"(uefi_context.gdtr), - "=g"(uefi_context.rflags) - : - ); -} - -static void restore_uefi_context() -{ - __asm__ volatile ( - "lgdt %0\n" - "mov %w1, %%ds\n" - "mov %w1, %%es\n" - "mov %w1, %%fs\n" - "mov %w1, %%gs\n" - "mov %w1, %%gs\n" - "mov %w1, %%ss\n" - "push %q2\n" - "lea .Lflush(%%rip), %%rax\n" - "push %%rax\n" - "lretq\n" - ".Lflush:\n" - "push %q3\n" - "popfq\n" - : - : - "m"(uefi_context.gdtr), - "m"(uefi_context.ds), - "m"(uefi_context.cs), - "g"(uefi_context.rflags) - : - "rax" - ); - -} - -struct syscall_context_return_state -{ - uint64_t rflags; - struct descriptor_table_register_long gdtr; - uint64_t return_ds; - uintptr_t return_rip; - uint64_t return_cs; -}; - -struct syscall_context -{ - uint64_t pad; - uint64_t syscall_index; - syscall_delegate_parameters parameters; - struct syscall_context_return_state state; -}; - - -typedef int64_t (syscall_delegate)(syscall_delegate_parameters params); - -int64_t syscall_delegate_write(syscall_delegate_parameters params) -{ - return write((int)params[0], (void *)params[1], (size_t)params[2]); -} - -constexpr int NR_SYSCALLS = 10; -static syscall_delegate *syscall_handler_delegates[NR_SYSCALLS] = -{ - nullptr, - syscall_delegate_write -}; - -SYSV_ABI int64_t fake_syscall_handler(struct syscall_context *context) -{ - if (context->syscall_index > NR_SYSCALLS) - { - // TODO: ENOSYS - return -1; - } - - syscall_delegate *delegate = syscall_handler_delegates[context->syscall_index]; - - if (delegate == nullptr) - { - // TODO: ENOSYS - return -1; - } - - restore_uefi_context(); - int64_t result = delegate(context->parameters); - save_uefi_context(); - - return result; -} - -SYSV_ABI void fake_syscall_entry(void); -SYSV_ABI void fake_syscall_start(void *stack); - -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); -} - -static void *create_fake_syscall_stack(void *entry_addr) -{ - constexpr size_t fake_syscall_stack_size = 0x20000; // 128KiB stack - char *stack_pointer_base = mmap(nullptr, fake_syscall_stack_size, 0, 0, -1, 0); - void *stack_pointer_head = stack_pointer_base + fake_syscall_stack_size; - - struct syscall_context *context = stack_alloc(&stack_pointer_head, sizeof(*context)); - - context->state.rflags = 0; - context->state.gdtr.base = (uintptr_t)&fake_syscall_gdt; - context->state.gdtr.limit = sizeof(fake_syscall_gdt) - 1; - context->state.return_ds = 16; - context->state.return_cs = 8; - context->state.return_rip = (uintptr_t)entry_addr; - - return stack_pointer_head; -} +#include "fake_syscall.h" int main(int argc, char **argv) { @@ -198,9 +7,6 @@ int main(int argc, char **argv) (void)argv; struct kjarna_boot_image boot_image = get_boot_image(); - install_syscall_handler(); - void *fake_syscall_stack = create_fake_syscall_stack((void *)boot_image.entry); - save_uefi_context(); - fake_syscall_start(fake_syscall_stack); + fake_syscall_start(&boot_image); } |
