]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
D3: dup/pipe/execve syscalls
authorTulio A M Mendes <[email protected]>
Sun, 8 Feb 2026 01:16:45 +0000 (22:16 -0300)
committerTulio A M Mendes <[email protected]>
Sun, 8 Feb 2026 01:16:45 +0000 (22:16 -0300)
Add refcounted dup/dup2, pipe() ring buffer, and execve() to replace the current process image. Include userland wrappers, smoke tests, and a second user binary (echo.elf) packaged into initrd.

Makefile
include/errno.h
include/syscall.h
src/kernel/syscall.c
user/echo.c [new file with mode: 0644]
user/init.c

index b4e28cc5fcaa84f69659ee82c25ab6347044f3c5..0238493bf09ec1744ed826337754a047ece05e29 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -45,6 +45,7 @@ ifeq ($(ARCH),x86)
     C_SOURCES += $(wildcard $(SRC_DIR)/arch/x86/*.c)
 
     USER_ELF := user/init.elf
+    ECHO_ELF := user/echo.elf
     INITRD_IMG := initrd.img
     MKINITRD := tools/mkinitrd
 endif
@@ -120,8 +121,11 @@ $(MKINITRD): tools/mkinitrd.c
 $(USER_ELF): user/init.c user/linker.ld
        @i686-elf-gcc -m32 -ffreestanding -fno-pie -no-pie -nostdlib -Wl,-T,user/linker.ld -o $(USER_ELF) user/init.c
 
-$(INITRD_IMG): $(MKINITRD) $(USER_ELF)
-       @./$(MKINITRD) $(INITRD_IMG) $(USER_ELF):bin/init.elf
+$(ECHO_ELF): user/echo.c user/linker.ld
+       @i686-elf-gcc -m32 -ffreestanding -fno-pie -no-pie -nostdlib -Wl,-T,user/linker.ld -o $(ECHO_ELF) user/echo.c
+
+$(INITRD_IMG): $(MKINITRD) $(USER_ELF) $(ECHO_ELF)
+       @./$(MKINITRD) $(INITRD_IMG) $(USER_ELF):bin/init.elf $(ECHO_ELF):bin/echo.elf
 
 run: iso
        @rm -f serial.log qemu.log
index ead6079060ce6f4e01162bd4383c8004c6d96932..c4de0d5d4e9baa0892d3a815db5a4e7d6ae0502b 100644 (file)
@@ -6,7 +6,10 @@
 #define EIO 5
 #define EBADF 9
 #define EFAULT 14
+#define ENOMEM 12
 #define EINVAL 22
+#define EPIPE 32
+#define EMFILE 24
 #define ESPIPE 29
 
 #endif
index b716ac16f5eb301f6b9ceebb63b4f29ca623d4aa..fada6d35cb9266ba238439262525770fad517c67 100644 (file)
@@ -22,6 +22,11 @@ enum {
     SYSCALL_LSEEK = 9,
     SYSCALL_FSTAT = 10,
     SYSCALL_STAT = 11,
+
+    SYSCALL_DUP = 12,
+    SYSCALL_DUP2 = 13,
+    SYSCALL_PIPE = 14,
+    SYSCALL_EXECVE = 15,
 };
 
 #endif
index 93f02df73a7a313558673395825e1702b92f5b28..dfa9d8d92a4c02b7f5923dd18c3244657ccebf93 100644 (file)
@@ -6,9 +6,12 @@
 #include "process.h"
 #include "uart_console.h"
 #include "uaccess.h"
+#include "utils.h"
 
 #include "errno.h"
+#include "elf.h"
 #include "stat.h"
+#include "vmm.h"
 
 #include "hal/cpu.h"
 
@@ -18,8 +21,192 @@ struct file {
     fs_node_t* node;
     uint32_t offset;
     uint32_t flags;
+    uint32_t refcount;
 };
 
+static int fd_alloc(struct file* f);
+static int fd_close(int fd);
+
+struct pipe_state {
+    uint8_t* buf;
+    uint32_t cap;
+    uint32_t rpos;
+    uint32_t wpos;
+    uint32_t count;
+    uint32_t readers;
+    uint32_t writers;
+};
+
+struct pipe_node {
+    fs_node_t node;
+    struct pipe_state* ps;
+    uint32_t is_read_end;
+};
+
+static uint32_t pipe_read(fs_node_t* n, uint32_t offset, uint32_t size, uint8_t* buffer) {
+    (void)offset;
+    struct pipe_node* pn = (struct pipe_node*)n;
+    if (!pn || !pn->ps || !buffer) return 0;
+    if (!pn->is_read_end) return 0;
+
+    struct pipe_state* ps = pn->ps;
+    if (size == 0) return 0;
+
+    uint32_t to_read = size;
+    if (to_read > ps->count) to_read = ps->count;
+
+    for (uint32_t i = 0; i < to_read; i++) {
+        buffer[i] = ps->buf[ps->rpos];
+        ps->rpos++;
+        if (ps->rpos == ps->cap) ps->rpos = 0;
+    }
+    ps->count -= to_read;
+    return to_read;
+}
+
+static uint32_t pipe_write(fs_node_t* n, uint32_t offset, uint32_t size, uint8_t* buffer) {
+    (void)offset;
+    struct pipe_node* pn = (struct pipe_node*)n;
+    if (!pn || !pn->ps || !buffer) return 0;
+    if (pn->is_read_end) return 0;
+
+    struct pipe_state* ps = pn->ps;
+    if (size == 0) return 0;
+    if (ps->readers == 0) return 0;
+
+    uint32_t free = ps->cap - ps->count;
+    uint32_t to_write = size;
+    if (to_write > free) to_write = free;
+
+    for (uint32_t i = 0; i < to_write; i++) {
+        ps->buf[ps->wpos] = buffer[i];
+        ps->wpos++;
+        if (ps->wpos == ps->cap) ps->wpos = 0;
+    }
+    ps->count += to_write;
+    return to_write;
+}
+
+static void pipe_close(fs_node_t* n) {
+    struct pipe_node* pn = (struct pipe_node*)n;
+    if (!pn || !pn->ps) {
+        if (pn) kfree(pn);
+        return;
+    }
+
+    if (pn->is_read_end) {
+        if (pn->ps->readers) pn->ps->readers--;
+    } else {
+        if (pn->ps->writers) pn->ps->writers--;
+    }
+
+    struct pipe_state* ps = pn->ps;
+    kfree(pn);
+
+    if (ps->readers == 0 && ps->writers == 0) {
+        if (ps->buf) kfree(ps->buf);
+        kfree(ps);
+    }
+}
+
+static int pipe_node_create(struct pipe_state* ps, int is_read_end, fs_node_t** out_node) {
+    if (!ps || !out_node) return -1;
+    struct pipe_node* pn = (struct pipe_node*)kmalloc(sizeof(*pn));
+    if (!pn) return -1;
+    memset(pn, 0, sizeof(*pn));
+
+    pn->ps = ps;
+    pn->is_read_end = is_read_end ? 1U : 0U;
+    pn->node.flags = FS_FILE;
+    pn->node.length = 0;
+    pn->node.open = NULL;
+    pn->node.finddir = NULL;
+    pn->node.close = pipe_close;
+    if (pn->is_read_end) {
+        strcpy(pn->node.name, "pipe:r");
+        pn->node.read = pipe_read;
+        pn->node.write = NULL;
+        ps->readers++;
+    } else {
+        strcpy(pn->node.name, "pipe:w");
+        pn->node.read = NULL;
+        pn->node.write = pipe_write;
+        ps->writers++;
+    }
+
+    *out_node = &pn->node;
+    return 0;
+}
+
+static int syscall_pipe_impl(int* user_fds) {
+    if (!user_fds) return -EFAULT;
+    if (user_range_ok(user_fds, sizeof(int) * 2) == 0) return -EFAULT;
+
+    struct pipe_state* ps = (struct pipe_state*)kmalloc(sizeof(*ps));
+    if (!ps) return -ENOMEM;
+    memset(ps, 0, sizeof(*ps));
+    ps->cap = 512;
+    ps->buf = (uint8_t*)kmalloc(ps->cap);
+    if (!ps->buf) {
+        kfree(ps);
+        return -ENOMEM;
+    }
+
+    fs_node_t* rnode = NULL;
+    fs_node_t* wnode = NULL;
+    if (pipe_node_create(ps, 1, &rnode) < 0 || pipe_node_create(ps, 0, &wnode) < 0) {
+        if (rnode) vfs_close(rnode);
+        if (wnode) vfs_close(wnode);
+        if (ps->buf) kfree(ps->buf);
+        kfree(ps);
+        return -ENOMEM;
+    }
+
+    struct file* rf = (struct file*)kmalloc(sizeof(*rf));
+    struct file* wf = (struct file*)kmalloc(sizeof(*wf));
+    if (!rf || !wf) {
+        if (rf) kfree(rf);
+        if (wf) kfree(wf);
+        vfs_close(rnode);
+        vfs_close(wnode);
+        return -ENOMEM;
+    }
+    memset(rf, 0, sizeof(*rf));
+    memset(wf, 0, sizeof(*wf));
+    rf->node = rnode;
+    rf->refcount = 1;
+    wf->node = wnode;
+    wf->refcount = 1;
+
+    int rfd = fd_alloc(rf);
+    if (rfd < 0) {
+        kfree(rf);
+        kfree(wf);
+        vfs_close(rnode);
+        vfs_close(wnode);
+        return -EMFILE;
+    }
+
+    int wfd = fd_alloc(wf);
+    if (wfd < 0) {
+        (void)fd_close(rfd);
+        kfree(wf);
+        vfs_close(wnode);
+        return -EMFILE;
+    }
+
+    int kfds[2];
+    kfds[0] = rfd;
+    kfds[1] = wfd;
+    if (copy_to_user(user_fds, kfds, sizeof(kfds)) < 0) {
+        (void)fd_close(rfd);
+        (void)fd_close(wfd);
+        return -EFAULT;
+    }
+
+    return 0;
+}
+
 static int stat_from_node(const fs_node_t* node, struct stat* st) {
     if (!node || !st) return -EFAULT;
 
@@ -35,6 +222,20 @@ static int stat_from_node(const fs_node_t* node, struct stat* st) {
     return 0;
 }
 
+static int fd_alloc_from(int start_fd, struct file* f) {
+    if (!current_process || !f) return -1;
+    if (start_fd < 0) start_fd = 0;
+    if (start_fd >= PROCESS_MAX_FILES) return -1;
+
+    for (int fd = start_fd; fd < PROCESS_MAX_FILES; fd++) {
+        if (current_process->files[fd] == NULL) {
+            current_process->files[fd] = f;
+            return fd;
+        }
+    }
+    return -1;
+}
+
 static int fd_alloc(struct file* f) {
     if (!current_process || !f) return -1;
 
@@ -60,10 +261,89 @@ static int fd_close(int fd) {
     struct file* f = current_process->files[fd];
     if (!f) return -1;
     current_process->files[fd] = NULL;
-    kfree(f);
+
+    if (f->refcount > 0) {
+        f->refcount--;
+    }
+    if (f->refcount == 0) {
+        if (f->node) {
+            vfs_close(f->node);
+        }
+        kfree(f);
+    }
     return 0;
 }
 
+static int syscall_dup_impl(int oldfd) {
+    struct file* f = fd_get(oldfd);
+    if (!f) return -EBADF;
+    f->refcount++;
+    int newfd = fd_alloc_from(0, f);
+    if (newfd < 0) {
+        f->refcount--;
+        return -EMFILE;
+    }
+    return newfd;
+}
+
+static int syscall_execve_impl(struct registers* regs, const char* user_path, const char* const* user_argv, const char* const* user_envp) {
+    (void)user_argv;
+    (void)user_envp;
+    if (!regs || !user_path) return -EFAULT;
+
+    char path[128];
+    for (size_t i = 0; i < sizeof(path); i++) {
+        if (copy_from_user(&path[i], &user_path[i], 1) < 0) {
+            return -EFAULT;
+        }
+        if (path[i] == 0) break;
+        if (i + 1 == sizeof(path)) {
+            path[sizeof(path) - 1] = 0;
+            break;
+        }
+    }
+
+    uintptr_t entry = 0;
+    uintptr_t user_sp = 0;
+    uintptr_t new_as = 0;
+    if (elf32_load_user_from_initrd(path, &entry, &user_sp, &new_as) != 0) {
+        return -ENOENT;
+    }
+
+    uintptr_t old_as = current_process ? current_process->addr_space : 0;
+    if (!current_process) {
+        vmm_as_destroy(new_as);
+        return -EINVAL;
+    }
+
+    current_process->addr_space = new_as;
+    vmm_as_activate(new_as);
+
+    if (old_as && old_as != new_as) {
+        vmm_as_destroy(old_as);
+    }
+
+    regs->eip = (uint32_t)entry;
+    regs->useresp = (uint32_t)user_sp;
+    regs->eax = 0;
+    return 0;
+}
+
+static int syscall_dup2_impl(int oldfd, int newfd) {
+    if (newfd < 0 || newfd >= PROCESS_MAX_FILES) return -EBADF;
+    struct file* f = fd_get(oldfd);
+    if (!f) return -EBADF;
+    if (oldfd == newfd) return newfd;
+
+    if (current_process && current_process->files[newfd]) {
+        (void)fd_close(newfd);
+    }
+
+    f->refcount++;
+    current_process->files[newfd] = f;
+    return newfd;
+}
+
 static int syscall_stat_impl(const char* user_path, struct stat* user_st) {
     if (!user_path || !user_st) return -EFAULT;
     if (user_range_ok(user_st, sizeof(*user_st)) == 0) return -EFAULT;
@@ -155,6 +435,7 @@ static int syscall_open_impl(const char* user_path, uint32_t flags) {
     f->node = node;
     f->offset = 0;
     f->flags = 0;
+    f->refcount = 1;
 
     int fd = fd_alloc(f);
     if (fd < 0) {
@@ -168,11 +449,11 @@ static int syscall_read_impl(int fd, void* user_buf, uint32_t len) {
     if (len > 1024 * 1024) return -1;
     if (user_range_ok(user_buf, (size_t)len) == 0) return -1;
 
-    if (fd == 0) {
+    if (fd == 0 && (!current_process || !current_process->files[0])) {
         return tty_read(user_buf, len);
     }
 
-    if (fd == 1 || fd == 2) return -1;
+    if ((fd == 1 || fd == 2) && (!current_process || !current_process->files[fd])) return -1;
 
     struct file* f = fd_get(fd);
     if (!f || !f->node) return -1;
@@ -203,7 +484,7 @@ static int syscall_write_impl(int fd, const void* user_buf, uint32_t len) {
     if (len > 1024 * 1024) return -EINVAL;
     if (user_range_ok(user_buf, (size_t)len) == 0) return -EFAULT;
 
-    if (fd == 1 || fd == 2) {
+    if ((fd == 1 || fd == 2) && (!current_process || !current_process->files[fd])) {
         return tty_write((const char*)user_buf, len);
     }
 
@@ -275,7 +556,7 @@ static void syscall_handler(struct registers* regs) {
     if (syscall_no == SYSCALL_EXIT) {
         int status = (int)regs->ebx;
 
-        for (int fd = 3; fd < PROCESS_MAX_FILES; fd++) {
+        for (int fd = 0; fd < PROCESS_MAX_FILES; fd++) {
             if (current_process && current_process->files[fd]) {
                 (void)fd_close(fd);
             }
@@ -347,6 +628,33 @@ static void syscall_handler(struct registers* regs) {
         return;
     }
 
+    if (syscall_no == SYSCALL_DUP) {
+        int oldfd = (int)regs->ebx;
+        regs->eax = (uint32_t)syscall_dup_impl(oldfd);
+        return;
+    }
+
+    if (syscall_no == SYSCALL_DUP2) {
+        int oldfd = (int)regs->ebx;
+        int newfd = (int)regs->ecx;
+        regs->eax = (uint32_t)syscall_dup2_impl(oldfd, newfd);
+        return;
+    }
+
+    if (syscall_no == SYSCALL_PIPE) {
+        int* user_fds = (int*)regs->ebx;
+        regs->eax = (uint32_t)syscall_pipe_impl(user_fds);
+        return;
+    }
+
+    if (syscall_no == SYSCALL_EXECVE) {
+        const char* path = (const char*)regs->ebx;
+        const char* const* argv = (const char* const*)regs->ecx;
+        const char* const* envp = (const char* const*)regs->edx;
+        regs->eax = (uint32_t)syscall_execve_impl(regs, path, argv, envp);
+        return;
+    }
+
     regs->eax = (uint32_t)-1;
 }
 
diff --git a/user/echo.c b/user/echo.c
new file mode 100644 (file)
index 0000000..2e7a739
--- /dev/null
@@ -0,0 +1,35 @@
+#include <stdint.h>
+
+enum {
+    SYSCALL_WRITE = 1,
+    SYSCALL_EXIT  = 2,
+};
+
+static int sys_write(int fd, const void* buf, uint32_t len) {
+    int ret;
+    __asm__ volatile(
+        "int $0x80"
+        : "=a"(ret)
+        : "a"(SYSCALL_WRITE), "b"(fd), "c"(buf), "d"(len)
+        : "memory"
+    );
+    return ret;
+}
+
+static __attribute__((noreturn)) void sys_exit(int status) {
+    __asm__ volatile(
+        "int $0x80"
+        :
+        : "a"(SYSCALL_EXIT), "b"(status)
+        : "memory"
+    );
+    for (;;) {
+        __asm__ volatile("hlt");
+    }
+}
+
+void _start(void) {
+    static const char m[] = "[echo] hello from echo.elf\n";
+    (void)sys_write(1, m, (uint32_t)(sizeof(m) - 1));
+    sys_exit(0);
+}
index aa3c1db5baf5c56139bbf78386ed4c2445a99f17..b79d0e576b92cd407658a24a5d96eac8d8fa6e40 100644 (file)
@@ -11,6 +11,11 @@ enum {
     SYSCALL_LSEEK = 9,
     SYSCALL_FSTAT = 10,
     SYSCALL_STAT = 11,
+
+    SYSCALL_DUP = 12,
+    SYSCALL_DUP2 = 13,
+    SYSCALL_PIPE = 14,
+    SYSCALL_EXECVE = 15,
 };
 
 enum {
@@ -40,6 +45,50 @@ static int sys_write(int fd, const void* buf, uint32_t len) {
     return ret;
 }
 
+static int sys_execve(const char* path, const char* const* argv, const char* const* envp) {
+    int ret;
+    __asm__ volatile(
+        "int $0x80"
+        : "=a"(ret)
+        : "a"(SYSCALL_EXECVE), "b"(path), "c"(argv), "d"(envp)
+        : "memory"
+    );
+    return ret;
+}
+
+static int sys_pipe(int fds[2]) {
+    int ret;
+    __asm__ volatile(
+        "int $0x80"
+        : "=a"(ret)
+        : "a"(SYSCALL_PIPE), "b"(fds)
+        : "memory"
+    );
+    return ret;
+}
+
+static int sys_dup(int oldfd) {
+    int ret;
+    __asm__ volatile(
+        "int $0x80"
+        : "=a"(ret)
+        : "a"(SYSCALL_DUP), "b"(oldfd)
+        : "memory"
+    );
+    return ret;
+}
+
+static int sys_dup2(int oldfd, int newfd) {
+    int ret;
+    __asm__ volatile(
+        "int $0x80"
+        : "=a"(ret)
+        : "a"(SYSCALL_DUP2), "b"(oldfd), "c"(newfd)
+        : "memory"
+    );
+    return ret;
+}
+
 static int sys_waitpid(int pid, int* status, uint32_t options) {
     int ret;
     __asm__ volatile(
@@ -288,6 +337,94 @@ void _start(void) {
         sys_exit(1);
     }
 
+    if (sys_lseek(fd, 0, SEEK_END) < 0) {
+        sys_write(1, "[init] dup2 prep lseek failed\n",
+                  (uint32_t)(sizeof("[init] dup2 prep lseek failed\n") - 1));
+        sys_exit(1);
+    }
+
+    if (sys_dup2(fd, 1) != 1) {
+        sys_write(1, "[init] dup2 failed\n", (uint32_t)(sizeof("[init] dup2 failed\n") - 1));
+        sys_exit(1);
+    }
+
+    (void)sys_close(fd);
+
+    {
+        static const char m[] = "[init] dup2 stdout->file OK\n";
+        if (sys_write(1, m, (uint32_t)(sizeof(m) - 1)) != (int)(sizeof(m) - 1)) {
+            sys_exit(1);
+        }
+    }
+
+    (void)sys_close(1);
+    sys_write(1, "[init] dup2 restore tty OK\n",
+              (uint32_t)(sizeof("[init] dup2 restore tty OK\n") - 1));
+
+    {
+        int pfds[2];
+        if (sys_pipe(pfds) < 0) {
+            sys_write(1, "[init] pipe failed\n", (uint32_t)(sizeof("[init] pipe failed\n") - 1));
+            sys_exit(1);
+        }
+
+        static const char pmsg[] = "pipe-test";
+        if (sys_write(pfds[1], pmsg, (uint32_t)(sizeof(pmsg) - 1)) != (int)(sizeof(pmsg) - 1)) {
+            sys_write(1, "[init] pipe write failed\n",
+                      (uint32_t)(sizeof("[init] pipe write failed\n") - 1));
+            sys_exit(1);
+        }
+
+        char rbuf[16];
+        int prd = sys_read(pfds[0], rbuf, (uint32_t)(sizeof(pmsg) - 1));
+        if (prd != (int)(sizeof(pmsg) - 1)) {
+            sys_write(1, "[init] pipe read failed\n",
+                      (uint32_t)(sizeof("[init] pipe read failed\n") - 1));
+            sys_exit(1);
+        }
+
+        int ok = 1;
+        for (uint32_t i = 0; i < (uint32_t)(sizeof(pmsg) - 1); i++) {
+            if ((uint8_t)rbuf[i] != (uint8_t)pmsg[i]) ok = 0;
+        }
+        if (!ok) {
+            sys_write(1, "[init] pipe mismatch\n",
+                      (uint32_t)(sizeof("[init] pipe mismatch\n") - 1));
+            sys_exit(1);
+        }
+
+        if (sys_dup2(pfds[1], 1) != 1) {
+            sys_write(1, "[init] pipe dup2 failed\n",
+                      (uint32_t)(sizeof("[init] pipe dup2 failed\n") - 1));
+            sys_exit(1);
+        }
+
+        static const char p2[] = "dup2-pipe";
+        if (sys_write(1, p2, (uint32_t)(sizeof(p2) - 1)) != (int)(sizeof(p2) - 1)) {
+            sys_exit(1);
+        }
+
+        int prd2 = sys_read(pfds[0], rbuf, (uint32_t)(sizeof(p2) - 1));
+        if (prd2 != (int)(sizeof(p2) - 1)) {
+            sys_write(1, "[init] pipe dup2 read failed\n",
+                      (uint32_t)(sizeof("[init] pipe dup2 read failed\n") - 1));
+            sys_exit(1);
+        }
+
+        (void)sys_close(1);
+        (void)sys_close(pfds[0]);
+        (void)sys_close(pfds[1]);
+
+        sys_write(1, "[init] pipe OK\n", (uint32_t)(sizeof("[init] pipe OK\n") - 1));
+    }
+
+    fd = sys_open("/tmp/hello.txt", 0);
+    if (fd < 0) {
+        sys_write(1, "[init] tmpfs open2 failed\n",
+                  (uint32_t)(sizeof("[init] tmpfs open2 failed\n") - 1));
+        sys_exit(1);
+    }
+
     if (sys_stat("/tmp/hello.txt", &st) < 0) {
         sys_write(1, "[init] tmpfs stat failed\n",
                   (uint32_t)(sizeof("[init] tmpfs stat failed\n") - 1));
@@ -454,5 +591,12 @@ void _start(void) {
         static const char wbad[] = "[init] waitpid failed (100 children, explicit)\n";
         (void)sys_write(1, wbad, (uint32_t)(sizeof(wbad) - 1));
     }
+
+    (void)sys_write(1, "[init] execve(/bin/echo.elf)\n",
+                    (uint32_t)(sizeof("[init] execve(/bin/echo.elf)\n") - 1));
+    (void)sys_execve("/bin/echo.elf", (const char* const*)0, (const char* const*)0);
+    (void)sys_write(1, "[init] execve returned (unexpected)\n",
+                    (uint32_t)(sizeof("[init] execve returned (unexpected)\n") - 1));
+    sys_exit(1);
     sys_exit(0);
 }