diff options
| author | Ada Christine <adachristine18@gmail.com> | 2025-02-02 15:20:52 +0000 |
|---|---|---|
| committer | Ada Christine <adachristine18@gmail.com> | 2025-02-02 15:20:52 +0000 |
| commit | 9bc0738101979b626233ee226ece3cc8cae053ec (patch) | |
| tree | 33f608f59a3cfc062db0363c208f3b0ea05c62e3 | |
| parent | 3d806c9e2fcb6226915982b6514919a3b9dac4dc (diff) | |
full flow implemented
| -rw-r--r-- | api/kjarna/syscall.h | 12 | ||||
| -rw-r--r-- | api/posix/unistd.h | 2 | ||||
| -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 | ||||
| -rw-r--r-- | lldbscript | 2 | ||||
| -rw-r--r-- | service/kjarna.c | 4 | ||||
| -rw-r--r-- | service/lib/posix.c | 113 |
11 files changed, 386 insertions, 215 deletions
diff --git a/api/kjarna/syscall.h b/api/kjarna/syscall.h new file mode 100644 index 0000000..8af162a --- /dev/null +++ b/api/kjarna/syscall.h @@ -0,0 +1,12 @@ +#define SYS_NOOP 0 +#define SYS_OPEN 1 +#define SYS_CLOSE 2 +#define SYS_LSEEK 3 +#define SYS_READ 4 +#define SYS_WRITE 5 +#define SYS_MMAP 6 +#define SYS_MUNMAP 7 +#define SYS_EXIT 8 + +#define NR_SYSCALLS (SYS_EXIT + 1) + diff --git a/api/posix/unistd.h b/api/posix/unistd.h index 4964689..6139660 100644 --- a/api/posix/unistd.h +++ b/api/posix/unistd.h @@ -2,6 +2,7 @@ #include <stddef.h> #include "sys/types.h" +#include <stdnoreturn.h> #define STDIN_FILENO 0 #define STDOUT_FILENO 1 @@ -18,4 +19,5 @@ ssize_t read(int fd, void *buf, size_t length); ssize_t write(int fd, const void *buf, size_t length); off_t lseek(int fd, off_t position, int whence); int close(int fd); +noreturn void _exit(int status); 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); } @@ -3,5 +3,5 @@ target modules add service/kjarna.os gdb-remote localhost:1234 target modules load --file kjarna.efi .text 0x6184000 .data 0x6189000 target modules load --file kjarna.os --slide 0x6178000 -b fake_syscall_handler +b syscall_delegate_exit diff --git a/service/kjarna.c b/service/kjarna.c index ce9ec54..80adae0 100644 --- a/service/kjarna.c +++ b/service/kjarna.c @@ -1,4 +1,5 @@ #include <kjarna/interface.h> +#include <unistd.h> struct kjarna_entry_params *entry_params; @@ -13,6 +14,7 @@ int kjarna_entry() while(wait); - return main(0, nullptr); + int result = main(0, nullptr); + _exit(result); } diff --git a/service/lib/posix.c b/service/lib/posix.c index 9a3e70f..abc73bf 100644 --- a/service/lib/posix.c +++ b/service/lib/posix.c @@ -1,19 +1,110 @@ #include <unistd.h> +#include <sys/mman.h> +#include <kjarna/syscall.h> +#include <stdnoreturn.h> -ssize_t write(int fd, const void *buffer, size_t length) +__attribute__((always_inline)) +static inline int64_t syscall1(int syscall_index, uint64_t p1) { - ssize_t result; + int64_t result; + __asm__ ( + "movq %1, %%rax\n" + "movq %2, %%rdi\n" + "syscall\n" + : "=a"(result) + : "g"(syscall_index), "g"(p1) + : "rdi"); + return result; +} - __asm__ ( - "movq %1, %%rdi\n" - "movq %2, %%rsi\n" - "movq %3, %%rdx\n" - "movq %0, %%rax\n" - "syscall" - : "=a"(result) - : "g"(fd), "g"(buffer), "g"(length) - : "rdi", "rsi","rdx"); +__attribute__((always_inline)) +static inline int64_t syscall2(int syscall_index, uint64_t p1, uint64_t p2) +{ + int64_t result; + __asm__ ( + "movq %1, %%rax\n" + "movq %2, %%rdi\n" + "movq %3, %%rsi\n" + "syscall\n" + : "=a"(result) + : "g"(syscall_index), "g"(p1), "g"(p2) + : "rdi", "rsi"); + return result; +} + +__attribute__((always_inline)) +static inline int64_t syscall3(int syscall_index, uint64_t p1, uint64_t p2, uint64_t p3) +{ + int64_t result; + __asm__ ( + "movq %1, %%rax\n" + "movq %2, %%rdi\n" + "movq %3, %%rsi\n" + "movq %4, %%rdx\n" + "syscall\n" + : "=a"(result) + : "g"(syscall_index), "g"(p1), "g"(p2), "g"(p3) + : "rdi", "rsi","rdx"); + return result; +} +__attribute__((always_inline)) +static inline int64_t syscall6(int syscall_index, uint64_t p1, uint64_t p2, uint64_t p3, uint64_t p4, uint64_t p5, uint64_t p6) +{ + int64_t result; + __asm__ ( + "movq %1, %%rax\n" + "movq %2, %%rdi\n" + "movq %3, %%rsi\n" + "movq %4, %%rdx\n" + "movq %5, %%r10\n" + "movq %6, %%r8\n" + "movq %7, %%r9\n" + "syscall\n" + : "=a"(result) + : "g"(syscall_index), "g"(p1), "g"(p2), "g"(p3), "g"(p4), "g"(p5), "g"(p6) + : "rdi", "rsi","rdx", "r10", "r8", "r9"); return result; } +int open(const char *path, int flags, int mode) +{ + return (int)syscall3(SYS_OPEN, (uintptr_t)path, flags, mode); +} + +int close(int fd) +{ + return (int)syscall1(SYS_CLOSE, fd); +} + +off_t lseek(int fd, off_t offset, int whence) +{ + return syscall3(SYS_LSEEK, fd, offset, whence); +} + +ssize_t read(int fd, void *buffer, size_t length) +{ + return syscall3(SYS_READ, fd, (uintptr_t)buffer, length); +} + +ssize_t write(int fd, const void *buffer, size_t length) +{ + return syscall3(SYS_WRITE, fd, (uintptr_t)buffer, length); +} + +void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) +{ + return (void *)syscall6(SYS_MMAP, (uintptr_t)addr, length, prot, flags, fd, offset); +} + +int munmap(void *addr, size_t length) +{ + return syscall2(SYS_MUNMAP, (uintptr_t)addr, length); +} + +noreturn void _exit(int status) +{ + syscall1(SYS_EXIT, status); + while(true); +} + |
