summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAda Christine <adachristine18@gmail.com>2025-02-02 15:20:52 +0000
committerAda Christine <adachristine18@gmail.com>2025-02-02 15:20:52 +0000
commit9bc0738101979b626233ee226ece3cc8cae053ec (patch)
tree33f608f59a3cfc062db0363c208f3b0ea05c62e3
parent3d806c9e2fcb6226915982b6514919a3b9dac4dc (diff)
full flow implemented
-rw-r--r--api/kjarna/syscall.h12
-rw-r--r--api/posix/unistd.h2
-rw-r--r--boot/Makefile2
-rw-r--r--boot/fake_syscall.c245
-rw-r--r--boot/fake_syscall.h8
-rw-r--r--boot/fake_syscall_entry.S12
-rw-r--r--boot/kjarna.h3
-rw-r--r--boot/main.c198
-rw-r--r--lldbscript2
-rw-r--r--service/kjarna.c4
-rw-r--r--service/lib/posix.c113
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);
}
diff --git a/lldbscript b/lldbscript
index 5f89867..00cb556 100644
--- a/lldbscript
+++ b/lldbscript
@@ -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);
+}
+