]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
x86/usermode: add ring3 syscall smoke test
authorTulio A M Mendes <[email protected]>
Fri, 6 Feb 2026 12:14:46 +0000 (09:14 -0300)
committerTulio A M Mendes <[email protected]>
Fri, 6 Feb 2026 12:14:46 +0000 (09:14 -0300)
.gitignore
include/syscall.h
iso/boot/grub/grub.cfg [new file with mode: 0644]
src/arch/x86/usermode.c [new file with mode: 0644]
src/arch/x86/vmm.c
src/kernel/main.c
src/kernel/shell.c
src/kernel/syscall.c

index aeb4c3c35be6974850b782be3d54c7b7ec0438d7..05ef27b56d8278847559e5a0ce8758afd16c592d 100644 (file)
@@ -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
index 863f17906244eeb0efd2ec78ded32d953d1ea789..b5c63ea42f4c66e6ef33ce3f9f0cb721ac1b4da1 100644 (file)
@@ -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 (file)
index 0000000..ca6a033
--- /dev/null
@@ -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 (file)
index 0000000..4d7c835
--- /dev/null
@@ -0,0 +1,86 @@
+#include <stdint.h>
+#include <stddef.h>
+
+#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
index 9649395e06748c38026a1ee553b932024369a511..365ea6f8b4d9df62c87a720d27f9a3c3edb3867d 100644 (file)
@@ -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
index d2f1cfe31253c0c7032056436e950208e43530e2..08b8f374aac1aa81ae4387d389b294e448b3f13e 100644 (file)
@@ -1,4 +1,5 @@
 #include <stdint.h>
+#include <stddef.h>
 #include "vga_console.h"
 #include "uart_console.h"
 #include "pmm.h"
 
 #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;
index f00050bd8935bd632814eb7a42af314dcf768e3c..153916148132783bc7ba43bb74792ee73f3d2de9 100644 (file)
@@ -30,6 +30,7 @@ void execute_command(char* cmd) {
         uart_print("  cat <file>  - 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 <num> - 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) {
index e42d4a3d9651200e84768fc7db78a98f588c0e07..d657ea2f52474edc5ff9dcd73aa4f07c5a509f2c 100644 (file)
@@ -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;
 }