From ccb6ca89f11d35b5b1d5ff44b4fc77dcfa5ce1c3 Mon Sep 17 00:00:00 2001 From: xamidev Date: Mon, 4 May 2026 20:24:18 +0200 Subject: [PATCH 1/2] Load TAR archive + run raw user program --- Makefile | 7 ++-- include/fs/initfs.h | 14 ++++++++ include/kernel.h | 3 +- include/sched/process.h | 1 + include/string/string.h | 1 + limine.conf | 3 +- src/fs/initfs.c | 76 +++++++++++++++++++++++++++++++++++++++++ src/kmain.c | 13 +++---- src/sched/process.c | 48 +++++++++++++++++++++++--- src/string/string.c | 8 +++++ 10 files changed, 157 insertions(+), 17 deletions(-) create mode 100644 include/fs/initfs.h create mode 100644 src/fs/initfs.c diff --git a/Makefile b/Makefile index ffefdfc..a5fb9a4 100644 --- a/Makefile +++ b/Makefile @@ -23,6 +23,8 @@ LD := x86_64-elf-ld $(ELFFILE): $(BUILDDIR) $(OBJFILES) nasm -f bin user/hello.S -o $(BUILDDIR)/hello nasm -f bin user/pedicel.S -o $(BUILDDIR)/pedicel + tar cvf $(BUILDDIR)/initfs.tar -C $(BUILDDIR) hello pedicel + nasm -f elf64 src/arch/x86/idt.S -o $(BUILDDIR)/idt_stub.o $(LD) -o $(ELFFILE) -T linker.ld $(OBJFILES) $(BUILDDIR)/idt_stub.o # Get the symbols for debugging @@ -43,14 +45,15 @@ limine/limine: git clone https://github.com/limine-bootloader/limine.git --branch=v9.x-binary --depth=1 $(MAKE) -C limine +initfs: + build-iso: limine/limine $(ELFFILE) rm -rf iso_root mkdir -p iso_root/boot cp -v $(ELFFILE) iso_root/boot mkdir -p iso_root/boot/limine cp -v limine.conf iso_root/boot/limine - cp $(BUILDDIR)/hello iso_root/boot/ - cp $(BUILDDIR)/pedicel iso_root/boot/ + cp $(BUILDDIR)/initfs.tar iso_root/boot/ mkdir -p iso_root/EFI/BOOT cp -v limine/limine-bios.sys limine/limine-bios-cd.bin limine/limine-uefi-cd.bin iso_root/boot/limine/ cp -v limine/BOOTX64.EFI iso_root/EFI/BOOT/ diff --git a/include/fs/initfs.h b/include/fs/initfs.h new file mode 100644 index 0000000..11c9857 --- /dev/null +++ b/include/fs/initfs.h @@ -0,0 +1,14 @@ +/* + * @author xamidev + * @brief PS/2 Keyboard driver + * @license GPL-3.0-only + */ + +#ifndef INITFS_H +#define INITFS_H + +#include + +int initfs_init(struct limine_file* tar_file); + +#endif \ No newline at end of file diff --git a/include/kernel.h b/include/kernel.h index 7a9712b..f15579a 100644 --- a/include/kernel.h +++ b/include/kernel.h @@ -10,7 +10,8 @@ #include "limine.h" enum ErrorCodes { ENOMEM, - EIO + EIO, + ENOENT }; #define CLEAR_INTERRUPTS __asm__ volatile("cli") diff --git a/include/sched/process.h b/include/sched/process.h index 13fe331..af36cb6 100644 --- a/include/sched/process.h +++ b/include/sched/process.h @@ -39,5 +39,6 @@ void process_exit(void); void process_display_list(struct process* processes_list); void process_create_user(struct limine_file* file, char* name); +void process_create_user_raw(char* file, int size, char* name); #endif diff --git a/include/string/string.h b/include/string/string.h index 78859ec..f01dbb9 100644 --- a/include/string/string.h +++ b/include/string/string.h @@ -13,5 +13,6 @@ char *strcpy(char *dest, const char *src); char *strcat(char *dest, const char *src); void strncpy(char* dst, const char* src, size_t n); int strncmp(const char* s1, const char* s2, size_t n); +size_t strlen(const char* str); #endif \ No newline at end of file diff --git a/limine.conf b/limine.conf index 7988d0b..c39c423 100644 --- a/limine.conf +++ b/limine.conf @@ -6,5 +6,4 @@ interface_branding: Welcome to the PepperOS disk! comment: Default configuration (warning: spicy) path: boot():/boot/pepperk - module_path: boot():/boot/hello - module_path: boot():/boot/pedicel \ No newline at end of file + module_path: boot():/boot/initfs.tar \ No newline at end of file diff --git a/src/fs/initfs.c b/src/fs/initfs.c new file mode 100644 index 0000000..ddeff2b --- /dev/null +++ b/src/fs/initfs.c @@ -0,0 +1,76 @@ +/* + * @author xamidev + * @brief Initial TAR filesystem (read-only) + * @license GPL-3.0-only + */ + +#include +#include +#include +#include +#include +#include + +void* archive_start_addr; +uint64_t archive_size; + +/* + * tar_oct2bin - convert octal size string to an integer + * @str: octal size string + * @size: size of string + * + * Return: + * $n - file size as an integer + */ +int tar_oct2bin(unsigned char* str, int size) +{ + int n = 0; + unsigned char* c = str; + while (size-- > 0) { + n *= 8; + n += *c - '0'; + c++; + } + return n; +} + +/* + * tar_lookup - lookup a file in the TAR file + * @archive: pointer to beginning of the archive + * @filename: file to lookup (absolute path) + * @out: where to store file data if found + * + * Return: + * $filesize - size of the file, if found + * $0 - file not found + */ +int tar_lookup(unsigned char* archive, char* filename, char** out) +{ + unsigned char *ptr = archive; + + while (!memcmp(ptr + 257, "ustar", 5)) { + int filesize = tar_oct2bin(ptr + 0x7c, 11); + if (!memcmp(ptr, filename, strlen(filename) + 1)) { + *out = (char*)(ptr + 512); + return filesize; + } + ptr += (((filesize + 511) / 512) + 1) * 512; + } + return 0; +} + +/* + * initfs_init - initialize the TAR initial filesystem + * @tar_file: pointer to the Limine-loaded archive + * + * Return: + * $0 - on success + */ +int initfs_init(struct limine_file* tar_file) +{ + archive_start_addr = tar_file->address; + archive_size = tar_file->size; + + DEBUG("Loaded TAR initial filesystem (initfs.tar)"); + return 0; +} \ No newline at end of file diff --git a/src/kmain.c b/src/kmain.c index 31ca493..c0ec88b 100644 --- a/src/kmain.c +++ b/src/kmain.c @@ -25,6 +25,7 @@ #include #include #include +#include // Limine version used __attribute__((used, section(".limine_requests"))) @@ -120,16 +121,12 @@ void kmain() idle_proc = process_create("idle", (void*)idle_main, 0); if (!boot_ctx.module) { - panic(NULL, "could not load 'hello' executable :("); + panic(NULL, "could not load initfs.tar :("); } - if (boot_ctx.module->module_count == 2) { - file = boot_ctx.module->modules[0]; - DEBUG("file: addr=%p size=%u", file->address, file->size); - process_create_user(file, "hello"); - - file = boot_ctx.module->modules[1]; - process_create_user(file, "pedicel"); + if (boot_ctx.module->module_count == 1) { + initfs_init(boot_ctx.module->modules[0]); } + process_create("kshell", (void*)pedicel_main, 0); scheduler_init(); diff --git a/src/sched/process.c b/src/sched/process.c index 0f16df2..e7d72d6 100644 --- a/src/sched/process.c +++ b/src/sched/process.c @@ -222,10 +222,6 @@ void process_jump_to_user(uintptr_t stack_top, uintptr_t user_code) " :: "r"(stack_top), "r"(user_code)); } -// Kernel stack used for interrupts from userland process. -// Should be set in TSS.RSP0 when switching to userland process. -uint8_t interrupt_stack[0x8000]; - extern struct tss tss; /* @@ -276,6 +272,50 @@ void process_create_user(struct limine_file* file, char* name) memcpy((uint64_t*)code, exec_addr, exec_size); load_cr3(VIRT_TO_PHYS((uint64_t)kernel_pml4)); + process_add(&processes_list, proc); + DEBUG("user process '%s' (pid=%u) enqueued for scheduling", name, proc->pid); + SET_INTERRUPTS; +} + +// Same as above but for a raw data pointer (pointing to raw binary, no ELF) +void process_create_user_raw(char* file, int size, char* name) +{ + CLEAR_INTERRUPTS; + struct process* proc = (struct process*)kmalloc(sizeof(struct process)); + struct cpu_status* ctx = (struct cpu_status*)kmalloc(sizeof(struct cpu_status)); + + if (!proc || !ctx) panic(NULL, "out of memory while creating user process"); + + strncpy(proc->name, name, PROCESS_NAME_MAX); + memset(ctx, 0, sizeof(struct cpu_status)); // set GP registers to zero + proc->pid = next_free_pid++; + proc->status = READY; + proc->next = 0; + proc->context = ctx; + proc->context->iret_ss = USER_DATA_SEGMENT | 3; + proc->context->iret_cs = USER_CODE_SEGMENT | 3; + proc->context->iret_flags = 0x202; // Interrupt Flag set + + void* exec_addr = (void*)file; + uint64_t exec_size = size; + + uint64_t* user_pml4 = vmm_create_address_space(); + if (!user_pml4) panic(NULL, "failed to create user address space"); + proc->root_page_table = user_pml4; + + uintptr_t stack_top = vmm_alloc_user_stack(user_pml4); + uint64_t code = vmm_alloc_user_code(user_pml4, exec_addr, exec_size); + + proc->context->iret_rsp = stack_top; + proc->context->iret_rip = code; + proc->kernel_stack = kalloc_stack(); + if (!proc->kernel_stack) panic(NULL, "failed to allocate kernel stack"); + + // Copy code into user pages; for that we need to temporarily switch to the user pml4 + load_cr3(VIRT_TO_PHYS((uint64_t)user_pml4)); + memcpy((uint64_t*)code, exec_addr, exec_size); + load_cr3(VIRT_TO_PHYS((uint64_t)kernel_pml4)); + process_add(&processes_list, proc); DEBUG("user process '%s' (pid=%u) enqueued for scheduling", name, proc->pid); SET_INTERRUPTS; diff --git a/src/string/string.c b/src/string/string.c index 6b51c58..a42d0e6 100644 --- a/src/string/string.c +++ b/src/string/string.c @@ -98,4 +98,12 @@ int strncmp(const char* s1, const char* s2, size_t n) else { return ( *(unsigned char *)s1 - *(unsigned char *)s2 ); } +} + +// BSD implementation +size_t strlen(const char* str) +{ + const char* s; + for (s = str; *s; ++s); + return (s - str); } \ No newline at end of file -- 2.52.0 From c00a247ead82ce8455d98ee7c3e403018bc39970 Mon Sep 17 00:00:00 2001 From: xamidev Date: Mon, 4 May 2026 20:38:10 +0200 Subject: [PATCH 2/2] Kshell: load executable command --- include/fs/initfs.h | 1 + include/kernel.h | 2 ++ src/kapps/kshell.c | 8 +++++++- src/kapps/loader.c | 32 ++++++++++++++++++++++++++++++++ 4 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 src/kapps/loader.c diff --git a/include/fs/initfs.h b/include/fs/initfs.h index 11c9857..c47d517 100644 --- a/include/fs/initfs.h +++ b/include/fs/initfs.h @@ -10,5 +10,6 @@ #include int initfs_init(struct limine_file* tar_file); +int tar_lookup(unsigned char* archive, char* filename, char** out); #endif \ No newline at end of file diff --git a/include/kernel.h b/include/kernel.h index f15579a..01cfb42 100644 --- a/include/kernel.h +++ b/include/kernel.h @@ -47,6 +47,8 @@ void debug_stack_trace(unsigned int max_frames); const char* debug_find_symbol(uintptr_t rip, uintptr_t* offset); void boot_mem_display(void); +int loader_load_raw(); + #define assert(check) do { if(!(check)) hcf(); } while(0) struct boot_context { diff --git a/src/kapps/kshell.c b/src/kapps/kshell.c index f4a6852..46cd6dc 100644 --- a/src/kapps/kshell.c +++ b/src/kapps/kshell.c @@ -51,7 +51,8 @@ void pedicel_main(void* arg) "pf - trigger a page fault\r\n" "now - get current date\r\n" "smash - smash the stack\r\n" - "mem - get used heap info\r\n"); + "mem - get used heap info\r\n" + "load - load an user executable\r\n"); continue; } @@ -96,6 +97,11 @@ void pedicel_main(void* arg) continue; } + if (strncmp(input_buf, "load", 4) == 0) { + loader_load_raw(); + continue; + } + printf("%s: command not found\r\n", input_buf); } } \ No newline at end of file diff --git a/src/kapps/loader.c b/src/kapps/loader.c new file mode 100644 index 0000000..4ee510d --- /dev/null +++ b/src/kapps/loader.c @@ -0,0 +1,32 @@ +/* + * @author xamidev + * @brief Executable loader + * @license GPL-3.0-only + */ + +#include +#include +#include +#include +#include +#include + +extern void* archive_start_addr; + +int loader_load_raw() +{ + char input_buf[PEDICEL_INPUT_SIZE] = {0}; + do { + printf("file> "); + keyboard_getline(input_buf, PEDICEL_INPUT_SIZE); + } while (strncmp(input_buf, "", 1) == 0); + + char* data = NULL; + int sz = tar_lookup(archive_start_addr, input_buf,&data); + if (sz > 0) { + process_create_user_raw(data, sz, input_buf); + return 0; // TODO: should return something else on error + } + printf("Couldn't load file '%s'\r\n", input_buf); + return 1; +} \ No newline at end of file -- 2.52.0