]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
posix: poll syscall (pipes + devfs tty/null)
authorTulio A M Mendes <[email protected]>
Sun, 8 Feb 2026 03:31:57 +0000 (00:31 -0300)
committerTulio A M Mendes <[email protected]>
Sun, 8 Feb 2026 03:31:57 +0000 (00:31 -0300)
Add a minimal poll() syscall (POLLIN/POLLOUT) with timeout semantics (0 non-blocking, <0 blocking). Support pipe endpoints and devfs char devices (/dev/null,/dev/tty) and add userland smoke tests.

include/syscall.h
include/tty.h
src/kernel/syscall.c
src/kernel/tty.c
user/init.c

index 47eda77ee1203f940bbd65fd05048a789601a537..6c22752a0ccf31f76272f239e7003c717c80880d 100644 (file)
@@ -26,6 +26,7 @@ enum {
     SYSCALL_EXECVE = 15,
     SYSCALL_FORK = 16,
     SYSCALL_GETPPID = 17,
+    SYSCALL_POLL = 18,
 };
 
 #endif
index 4d39686a3f35c5e77b9f99c1fa7199de59ebaacd..31f13704ad93fe7d2e9a9a72bf6a92a911109fe4 100644 (file)
@@ -12,6 +12,9 @@ int tty_write(const void* user_buf, uint32_t len);
 int tty_read_kbuf(void* kbuf, uint32_t len);
 int tty_write_kbuf(const void* kbuf, uint32_t len);
 
+int tty_can_read(void);
+int tty_can_write(void);
+
 void tty_input_char(char c);
 
 #endif
index 510c7abf410c63829e1a81df478bbca5b44db7b0..b85be6799c8448f42ec54c2834fc52641e08b8d3 100644 (file)
 
 static int fd_alloc(struct file* f);
 static int fd_close(int fd);
+static struct file* fd_get(int fd);
+
+struct pollfd {
+    int fd;
+    int16_t events;
+    int16_t revents;
+};
+
+enum {
+    POLLIN = 0x0001,
+    POLLOUT = 0x0004,
+    POLLERR = 0x0008,
+    POLLHUP = 0x0010,
+};
 
 static int execve_copy_user_str(char* out, size_t out_sz, const char* user_s) {
     if (!out || out_sz == 0 || !user_s) return -EFAULT;
@@ -84,6 +98,91 @@ struct pipe_node {
     uint32_t is_read_end;
 };
 
+static int syscall_poll_impl(struct pollfd* user_fds, uint32_t nfds, int32_t timeout) {
+    if (!user_fds) return -EFAULT;
+    if (nfds > 64U) return -EINVAL;
+    if (user_range_ok(user_fds, sizeof(struct pollfd) * (size_t)nfds) == 0) return -EFAULT;
+
+    // timeout semantics (minimal):
+    //  - timeout == 0 : non-blocking
+    //  - timeout  < 0 : block forever
+    //  - timeout  > 0 : treated as "ticks" (best-effort)
+    extern uint32_t get_tick_count(void);
+    uint32_t start_tick = get_tick_count();
+
+    struct pollfd kfds[64];
+
+    for (;;) {
+        if (copy_from_user(kfds, user_fds, sizeof(struct pollfd) * (size_t)nfds) < 0) return -EFAULT;
+
+        int ready = 0;
+        for (uint32_t i = 0; i < nfds; i++) {
+            kfds[i].revents = 0;
+            int fd = kfds[i].fd;
+            if (fd < 0) continue;
+
+            struct file* f = fd_get(fd);
+            if (!f || !f->node) {
+                kfds[i].revents |= POLLERR;
+                continue;
+            }
+
+            fs_node_t* n = f->node;
+
+            // Pipes (identified by node name prefix).
+            if (n->name[0] == 'p' && n->name[1] == 'i' && n->name[2] == 'p' && n->name[3] == 'e' && n->name[4] == ':') {
+                struct pipe_node* pn = (struct pipe_node*)n;
+                struct pipe_state* ps = pn->ps;
+                if (!ps) {
+                    kfds[i].revents |= POLLERR;
+                } else if (pn->is_read_end) {
+                    if ((kfds[i].events & POLLIN) && (ps->count > 0 || ps->writers == 0)) {
+                        kfds[i].revents |= POLLIN;
+                        if (ps->writers == 0) kfds[i].revents |= POLLHUP;
+                    }
+                } else {
+                    if (ps->readers == 0) {
+                        if (kfds[i].events & POLLOUT) kfds[i].revents |= POLLERR;
+                    } else {
+                        uint32_t free = ps->cap - ps->count;
+                        if ((kfds[i].events & POLLOUT) && free > 0) {
+                            kfds[i].revents |= POLLOUT;
+                        }
+                    }
+                }
+            } else if (n->flags == FS_CHARDEVICE) {
+                // devfs devices: inode 2=/dev/null, 3=/dev/tty
+                if (n->inode == 2) {
+                    if (kfds[i].events & POLLIN) kfds[i].revents |= POLLIN | POLLHUP;
+                    if (kfds[i].events & POLLOUT) kfds[i].revents |= POLLOUT;
+                } else if (n->inode == 3) {
+                    if ((kfds[i].events & POLLIN) && tty_can_read()) kfds[i].revents |= POLLIN;
+                    if ((kfds[i].events & POLLOUT) && tty_can_write()) kfds[i].revents |= POLLOUT;
+                }
+            } else {
+                // Regular files are always readable/writable (best-effort).
+                if (kfds[i].events & POLLIN) kfds[i].revents |= POLLIN;
+                if (kfds[i].events & POLLOUT) kfds[i].revents |= POLLOUT;
+            }
+
+            if (kfds[i].revents) ready++;
+        }
+
+        if (copy_to_user(user_fds, kfds, sizeof(struct pollfd) * (size_t)nfds) < 0) return -EFAULT;
+
+        if (ready) return ready;
+        if (timeout == 0) return 0;
+
+        if (timeout > 0) {
+            uint32_t now = get_tick_count();
+            uint32_t elapsed = now - start_tick;
+            if (elapsed >= (uint32_t)timeout) return 0;
+        }
+
+        process_sleep(1);
+    }
+}
+
 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;
@@ -807,6 +906,14 @@ static void syscall_handler(struct registers* regs) {
         return;
     }
 
+    if (syscall_no == SYSCALL_POLL) {
+        struct pollfd* fds = (struct pollfd*)regs->ebx;
+        uint32_t nfds = regs->ecx;
+        int32_t timeout = (int32_t)regs->edx;
+        regs->eax = (uint32_t)syscall_poll_impl(fds, nfds, timeout);
+        return;
+    }
+
     regs->eax = (uint32_t)-ENOSYS;
 }
 
index 60189ec995b0ca136ae753538f5fc4defe27ac0c..d24039257f1efc1e77521ebfde32b6a8e84328c2 100644 (file)
@@ -89,6 +89,17 @@ static uint32_t canon_count(void) {
     return (TTY_CANON_BUF - canon_tail) + canon_head;
 }
 
+int tty_can_read(void) {
+    uintptr_t flags = spin_lock_irqsave(&tty_lock);
+    int ready = canon_empty() ? 0 : 1;
+    spin_unlock_irqrestore(&tty_lock, flags);
+    return ready;
+}
+
+int tty_can_write(void) {
+    return 1;
+}
+
 static void canon_push(char c) {
     uint32_t next = (canon_head + 1U) % TTY_CANON_BUF;
     if (next == canon_tail) {
index 5fe3db982f111224f34ccfe0036e9110b6495e35..defaead565527a4bb214836cf06e8a0695a5ed97 100644 (file)
@@ -18,6 +18,18 @@ enum {
     SYSCALL_EXECVE = 15,
     SYSCALL_FORK = 16,
     SYSCALL_GETPPID = 17,
+    SYSCALL_POLL = 18,
+};
+
+struct pollfd {
+    int fd;
+    int16_t events;
+    int16_t revents;
+};
+
+enum {
+    POLLIN = 0x0001,
+    POLLOUT = 0x0004,
 };
 
 enum {
@@ -51,6 +63,17 @@ static int sys_write(int fd, const void* buf, uint32_t len) {
     return ret;
 }
 
+static int sys_poll(struct pollfd* fds, uint32_t nfds, int32_t timeout) {
+    int ret;
+    __asm__ volatile(
+        "int $0x80"
+        : "=a"(ret)
+        : "a"(SYSCALL_POLL), "b"(fds), "c"(nfds), "d"(timeout)
+        : "memory"
+    );
+    return ret;
+}
+
 static int sys_getpid(void) {
     int ret;
     __asm__ volatile(
@@ -444,6 +467,67 @@ void _start(void) {
         (void)sys_close(pfds[1]);
 
         sys_write(1, "[init] pipe OK\n", (uint32_t)(sizeof("[init] pipe OK\n") - 1));
+
+    {
+        int fds[2];
+        if (sys_pipe(fds) < 0) {
+            sys_write(1, "[init] poll pipe setup failed\n",
+                      (uint32_t)(sizeof("[init] poll pipe setup failed\n") - 1));
+            sys_exit(1);
+        }
+
+        struct pollfd p;
+        p.fd = fds[0];
+        p.events = POLLIN;
+        p.revents = 0;
+        int rc = sys_poll(&p, 1, 0);
+        if (rc != 0) {
+            sys_write(1, "[init] poll(pipe) expected 0\n",
+                      (uint32_t)(sizeof("[init] poll(pipe) expected 0\n") - 1));
+            sys_exit(1);
+        }
+
+        static const char a = 'A';
+        if (sys_write(fds[1], &a, 1) != 1) {
+            sys_write(1, "[init] poll pipe write failed\n",
+                      (uint32_t)(sizeof("[init] poll pipe write failed\n") - 1));
+            sys_exit(1);
+        }
+
+        p.revents = 0;
+        rc = sys_poll(&p, 1, 0);
+        if (rc != 1 || (p.revents & POLLIN) == 0) {
+            sys_write(1, "[init] poll(pipe) expected POLLIN\n",
+                      (uint32_t)(sizeof("[init] poll(pipe) expected POLLIN\n") - 1));
+            sys_exit(1);
+        }
+
+        (void)sys_close(fds[0]);
+        (void)sys_close(fds[1]);
+        sys_write(1, "[init] poll(pipe) OK\n", (uint32_t)(sizeof("[init] poll(pipe) OK\n") - 1));
+    }
+
+    {
+        int fd = sys_open("/dev/null", 0);
+        if (fd < 0) {
+            sys_write(1, "[init] poll(/dev/null) open failed\n",
+                      (uint32_t)(sizeof("[init] poll(/dev/null) open failed\n") - 1));
+            sys_exit(1);
+        }
+        struct pollfd p;
+        p.fd = fd;
+        p.events = POLLOUT;
+        p.revents = 0;
+        int rc = sys_poll(&p, 1, 0);
+        if (rc != 1 || (p.revents & POLLOUT) == 0) {
+            sys_write(1, "[init] poll(/dev/null) expected POLLOUT\n",
+                      (uint32_t)(sizeof("[init] poll(/dev/null) expected POLLOUT\n") - 1));
+            sys_exit(1);
+        }
+        (void)sys_close(fd);
+        sys_write(1, "[init] poll(/dev/null) OK\n",
+                  (uint32_t)(sizeof("[init] poll(/dev/null) OK\n") - 1));
+    }
     }
 
     fd = sys_open("/tmp/hello.txt", 0);