From: Tulio A M Mendes Date: Sun, 8 Feb 2026 01:16:45 +0000 (-0300) Subject: D3: dup/pipe/execve syscalls X-Git-Url: https://projects.tadryanom.me/sitemap.xml?a=commitdiff_plain;h=d72821be67a427ee309e090cd39cf594331da5a1;p=AdrOS.git D3: dup/pipe/execve syscalls 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. --- diff --git a/Makefile b/Makefile index b4e28cc..0238493 100644 --- 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 diff --git a/include/errno.h b/include/errno.h index ead6079..c4de0d5 100644 --- a/include/errno.h +++ b/include/errno.h @@ -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 diff --git a/include/syscall.h b/include/syscall.h index b716ac1..fada6d3 100644 --- a/include/syscall.h +++ b/include/syscall.h @@ -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 diff --git a/src/kernel/syscall.c b/src/kernel/syscall.c index 93f02df..dfa9d8d 100644 --- a/src/kernel/syscall.c +++ b/src/kernel/syscall.c @@ -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 index 0000000..2e7a739 --- /dev/null +++ b/user/echo.c @@ -0,0 +1,35 @@ +#include + +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); +} diff --git a/user/init.c b/user/init.c index aa3c1db..b79d0e5 100644 --- a/user/init.c +++ b/user/init.c @@ -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); }