From d3c768375047a98a3f934e71eed0084e07665d96 Mon Sep 17 00:00:00 2001 From: Tulio A M Mendes Date: Fri, 6 Feb 2026 09:14:46 -0300 Subject: [PATCH] x86/usermode: add ring3 syscall smoke test --- .gitignore | 4 +- include/syscall.h | 2 + iso/boot/grub/grub.cfg | 11 ++++++ src/arch/x86/usermode.c | 86 +++++++++++++++++++++++++++++++++++++++++ src/arch/x86/vmm.c | 8 +++- src/kernel/main.c | 31 +++++++++++++++ src/kernel/shell.c | 9 +++++ src/kernel/syscall.c | 12 ++++++ 8 files changed, 161 insertions(+), 2 deletions(-) create mode 100644 iso/boot/grub/grub.cfg create mode 100644 src/arch/x86/usermode.c diff --git a/.gitignore b/.gitignore index aeb4c3c..05ef27b 100644 --- a/.gitignore +++ b/.gitignore @@ -6,10 +6,12 @@ build/ *.img *.log +# ISO staging: keep config, ignore generated kernel binaries +iso/boot/*.bin + # Temporary folders iso_root/ isodir/ -iso/ # Editor/OS clutter .DS_Store diff --git a/include/syscall.h b/include/syscall.h index 863f179..b5c63ea 100644 --- a/include/syscall.h +++ b/include/syscall.h @@ -7,6 +7,8 @@ void syscall_init(void); enum { SYSCALL_WRITE = 1, + SYSCALL_EXIT = 2, + SYSCALL_GETPID = 3, }; #endif diff --git a/iso/boot/grub/grub.cfg b/iso/boot/grub/grub.cfg new file mode 100644 index 0000000..ca6a033 --- /dev/null +++ b/iso/boot/grub/grub.cfg @@ -0,0 +1,11 @@ +set timeout=5 +set default=0 + +#serial --unit=0 --speed=115200 --word=8 --parity=no --stop=1 +#terminal_input serial +#terminal_output serial + +menuentry "AdrOS (x86)" { + multiboot2 /boot/adros-x86.bin ring3 + boot +} diff --git a/src/arch/x86/usermode.c b/src/arch/x86/usermode.c new file mode 100644 index 0000000..4d7c835 --- /dev/null +++ b/src/arch/x86/usermode.c @@ -0,0 +1,86 @@ +#include +#include + +#include "pmm.h" +#include "vmm.h" +#include "uart_console.h" +#include "utils.h" + +#if defined(__i386__) + +static void* pmm_alloc_page_low_16mb(void) { + for (int tries = 0; tries < 4096; tries++) { + void* p = pmm_alloc_page(); + if (!p) return NULL; + if ((uintptr_t)p < 0x01000000U) { + return p; + } + pmm_free_page(p); + } + return NULL; +} + +__attribute__((noreturn)) static void enter_usermode(uintptr_t user_eip, uintptr_t user_esp) { + __asm__ volatile( + "cli\n" + "mov $0x23, %%ax\n" + "mov %%ax, %%ds\n" + "mov %%ax, %%es\n" + "mov %%ax, %%fs\n" + "mov %%ax, %%gs\n" + "pushl $0x23\n" /* ss */ + "pushl %[esp]\n" /* esp */ + "pushfl\n" + "popl %%eax\n" + "orl $0x200, %%eax\n" /* IF=1 */ + "pushl %%eax\n" + "pushl $0x1B\n" /* cs */ + "pushl %[eip]\n" /* eip */ + "iret\n" + : + : [eip] "r"(user_eip), [esp] "r"(user_esp) + : "eax", "memory" + ); + + __builtin_unreachable(); +} + +void x86_usermode_test_start(void) { + uart_print("[USER] Starting ring3 test...\n"); + + const uintptr_t user_code_vaddr = 0x00400000U; + const uintptr_t user_stack_vaddr = 0x00800000U; + + void* code_phys = pmm_alloc_page_low_16mb(); + void* stack_phys = pmm_alloc_page_low_16mb(); + if (!code_phys || !stack_phys) { + uart_print("[USER] OOM allocating user pages.\n"); + return; + } + + vmm_map_page((uint64_t)(uintptr_t)code_phys, (uint64_t)user_code_vaddr, + VMM_FLAG_PRESENT | VMM_FLAG_RW | VMM_FLAG_USER); + vmm_map_page((uint64_t)(uintptr_t)stack_phys, (uint64_t)user_stack_vaddr, + VMM_FLAG_PRESENT | VMM_FLAG_RW | VMM_FLAG_USER); + + static const uint8_t user_prog[] = { + 0xB8, 0x01, 0x00, 0x00, 0x00, /* mov eax, 1 (SYSCALL_WRITE) */ + 0xBB, 0x01, 0x00, 0x00, 0x00, /* mov ebx, 1 (stdout) */ + 0xB9, 0x40, 0x00, 0x40, 0x00, /* mov ecx, 0x00400040 */ + 0xBA, 0x12, 0x00, 0x00, 0x00, /* mov edx, 0x12 */ + 0xCD, 0x80, /* int 0x80 */ + 0xB8, 0x02, 0x00, 0x00, 0x00, /* mov eax, 2 (SYSCALL_EXIT) */ + 0xCD, 0x80, /* int 0x80 */ + 0xEB, 0xFE /* jmp $ (loop) */ + }; + + static const char msg[] = "Hello from ring3!\n"; + + memcpy((void*)(uintptr_t)code_phys, user_prog, sizeof(user_prog)); + memcpy((void*)((uintptr_t)code_phys + 0x40), msg, sizeof(msg) - 1); + + uintptr_t user_esp = user_stack_vaddr + 4096; + enter_usermode(user_code_vaddr, user_esp); +} + +#endif diff --git a/src/arch/x86/vmm.c b/src/arch/x86/vmm.c index 9649395..365ea6f 100644 --- a/src/arch/x86/vmm.c +++ b/src/arch/x86/vmm.c @@ -59,7 +59,13 @@ void vmm_map_page(uint64_t phys, uint64_t virt, uint32_t flags) { for(int i=0; i<1024; i++) pt_virt[i] = 0; // Add to Directory - boot_pd[pd_index] = pt_phys | X86_PTE_PRESENT | X86_PTE_RW; + uint32_t pde_flags = X86_PTE_PRESENT | X86_PTE_RW; + if (flags & VMM_FLAG_USER) pde_flags |= X86_PTE_USER; + boot_pd[pd_index] = pt_phys | pde_flags; + } + + if ((flags & VMM_FLAG_USER) && !(boot_pd[pd_index] & X86_PTE_USER)) { + boot_pd[pd_index] |= X86_PTE_USER; } // Get table address from Directory diff --git a/src/kernel/main.c b/src/kernel/main.c index d2f1cfe..08b8f37 100644 --- a/src/kernel/main.c +++ b/src/kernel/main.c @@ -1,4 +1,5 @@ #include +#include #include "vga_console.h" #include "uart_console.h" #include "pmm.h" @@ -22,6 +23,30 @@ #include "hal/cpu.h" +#if defined(__i386__) +extern void x86_usermode_test_start(void); + +static int cmdline_has_token(const char* cmdline, const char* token) { + if (!cmdline || !token) return 0; + + for (size_t i = 0; cmdline[i] != 0; i++) { + size_t j = 0; + while (token[j] != 0 && cmdline[i + j] == token[j]) { + j++; + } + if (token[j] == 0) { + char before = (i == 0) ? ' ' : cmdline[i - 1]; + char after = cmdline[i + j]; + int before_ok = (before == ' ' || before == '\t'); + int after_ok = (after == 0 || after == ' ' || after == '\t'); + if (before_ok && after_ok) return 1; + } + } + + return 0; +} +#endif + /* Check if the compiler thinks we are targeting the wrong operating system. */ #if defined(__linux__) #warning "You are not using a cross-compiler, you may run into trouble" @@ -65,6 +90,12 @@ void kernel_main(const struct boot_info* bi) { hal_cpu_enable_interrupts(); +#if defined(__i386__) + if (bi && cmdline_has_token(bi->cmdline, "ring3")) { + x86_usermode_test_start(); + } +#endif + // 9. Load InitRD (if available) if (bi && bi->initrd_start) { const uintptr_t initrd_virt_base = 0xE0000000U; diff --git a/src/kernel/shell.c b/src/kernel/shell.c index f00050b..1539161 100644 --- a/src/kernel/shell.c +++ b/src/kernel/shell.c @@ -30,6 +30,7 @@ void execute_command(char* cmd) { uart_print(" cat - Read file content\n"); uart_print(" mem - Show memory stats\n"); uart_print(" panic - Trigger kernel panic\n"); + uart_print(" ring3 - Run x86 ring3 syscall test\n"); uart_print(" reboot - Restart system\n"); uart_print(" sleep - Sleep for N ticks\n"); } @@ -87,6 +88,14 @@ void execute_command(char* cmd) { for(;;) { hal_cpu_idle(); } +#endif + } + else if (strcmp(cmd, "ring3") == 0) { +#if defined(__i386__) + extern void x86_usermode_test_start(void); + x86_usermode_test_start(); +#else + uart_print("ring3 test only available on x86.\n"); #endif } else if (strcmp(cmd, "reboot") == 0) { diff --git a/src/kernel/syscall.c b/src/kernel/syscall.c index e42d4a3..d657ea2 100644 --- a/src/kernel/syscall.c +++ b/src/kernel/syscall.c @@ -24,6 +24,18 @@ static void syscall_handler(struct registers* regs) { return; } + if (syscall_no == SYSCALL_GETPID) { + regs->eax = 0; + return; + } + + if (syscall_no == SYSCALL_EXIT) { + uart_print("[USER] exit()\n"); + for(;;) { + __asm__ volatile("cli; hlt"); + } + } + regs->eax = (uint32_t)-1; } -- 2.43.0