]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
posix: devfs with /dev/null and /dev/tty
authorTulio A M Mendes <[email protected]>
Sun, 8 Feb 2026 02:55:41 +0000 (23:55 -0300)
committerTulio A M Mendes <[email protected]>
Sun, 8 Feb 2026 02:55:41 +0000 (23:55 -0300)
Add a minimal devfs mounted at /dev providing /dev/null and /dev/tty char devices. Implement tty_read_kbuf/tty_write_kbuf to support VFS-backed tty IO and allow open/read/write through FS_CHARDEVICE nodes. Add userland smoke tests.

include/devfs.h [new file with mode: 0644]
include/tty.h
src/kernel/devfs.c [new file with mode: 0644]
src/kernel/init.c
src/kernel/syscall.c
src/kernel/tty.c
user/init.c

diff --git a/include/devfs.h b/include/devfs.h
new file mode 100644 (file)
index 0000000..ba6256d
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef DEVFS_H
+#define DEVFS_H
+
+#include "fs.h"
+
+fs_node_t* devfs_create_root(void);
+
+#endif
index c3cb1365e41fc3febf3619e357e0840c8efe2452..4d39686a3f35c5e77b9f99c1fa7199de59ebaacd 100644 (file)
@@ -9,6 +9,9 @@ void tty_init(void);
 int tty_read(void* user_buf, uint32_t len);
 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);
+
 void tty_input_char(char c);
 
 #endif
diff --git a/src/kernel/devfs.c b/src/kernel/devfs.c
new file mode 100644 (file)
index 0000000..884cc28
--- /dev/null
@@ -0,0 +1,97 @@
+#include "devfs.h"
+
+#include "errno.h"
+#include "tty.h"
+#include "utils.h"
+
+struct devfs_root {
+    fs_node_t vfs;
+};
+
+static struct devfs_root g_dev_root;
+static fs_node_t g_dev_null;
+static fs_node_t g_dev_tty;
+static uint32_t g_devfs_inited = 0;
+
+static uint32_t dev_null_read(fs_node_t* node, uint32_t offset, uint32_t size, uint8_t* buffer) {
+    (void)node;
+    (void)offset;
+    (void)size;
+    (void)buffer;
+    return 0;
+}
+
+static uint32_t dev_null_write(fs_node_t* node, uint32_t offset, uint32_t size, uint8_t* buffer) {
+    (void)node;
+    (void)offset;
+    (void)buffer;
+    return size;
+}
+
+static uint32_t dev_tty_read(fs_node_t* node, uint32_t offset, uint32_t size, uint8_t* buffer) {
+    (void)node;
+    (void)offset;
+    int rc = tty_read_kbuf(buffer, size);
+    if (rc < 0) return 0;
+    return (uint32_t)rc;
+}
+
+static uint32_t dev_tty_write(fs_node_t* node, uint32_t offset, uint32_t size, uint8_t* buffer) {
+    (void)node;
+    (void)offset;
+    int rc = tty_write_kbuf(buffer, size);
+    if (rc < 0) return 0;
+    return (uint32_t)rc;
+}
+
+static struct fs_node* devfs_finddir_impl(struct fs_node* node, char* name) {
+    (void)node;
+    if (!name || name[0] == 0) return 0;
+
+    if (strcmp(name, "null") == 0) return &g_dev_null;
+    if (strcmp(name, "tty") == 0) return &g_dev_tty;
+    return 0;
+}
+
+static void devfs_init_once(void) {
+    if (g_devfs_inited) return;
+    g_devfs_inited = 1;
+
+    memset(&g_dev_root, 0, sizeof(g_dev_root));
+    strcpy(g_dev_root.vfs.name, "dev");
+    g_dev_root.vfs.flags = FS_DIRECTORY;
+    g_dev_root.vfs.inode = 1;
+    g_dev_root.vfs.length = 0;
+    g_dev_root.vfs.read = 0;
+    g_dev_root.vfs.write = 0;
+    g_dev_root.vfs.open = 0;
+    g_dev_root.vfs.close = 0;
+    g_dev_root.vfs.finddir = &devfs_finddir_impl;
+
+    memset(&g_dev_null, 0, sizeof(g_dev_null));
+    strcpy(g_dev_null.name, "null");
+    g_dev_null.flags = FS_CHARDEVICE;
+    g_dev_null.inode = 2;
+    g_dev_null.length = 0;
+    g_dev_null.read = &dev_null_read;
+    g_dev_null.write = &dev_null_write;
+    g_dev_null.open = 0;
+    g_dev_null.close = 0;
+    g_dev_null.finddir = 0;
+
+    memset(&g_dev_tty, 0, sizeof(g_dev_tty));
+    strcpy(g_dev_tty.name, "tty");
+    g_dev_tty.flags = FS_CHARDEVICE;
+    g_dev_tty.inode = 3;
+    g_dev_tty.length = 0;
+    g_dev_tty.read = &dev_tty_read;
+    g_dev_tty.write = &dev_tty_write;
+    g_dev_tty.open = 0;
+    g_dev_tty.close = 0;
+    g_dev_tty.finddir = 0;
+}
+
+fs_node_t* devfs_create_root(void) {
+    devfs_init_once();
+    return &g_dev_root.vfs;
+}
index c0361acd12b88039cfdfc4e65d5ebfb495a04085..afb5749f23500bf56ab1144e154ee53cc60a2077 100644 (file)
@@ -6,6 +6,7 @@
 #include "initrd.h"
 #include "overlayfs.h"
 #include "tmpfs.h"
+#include "devfs.h"
 #include "tty.h"
 #include "uart_console.h"
 
@@ -63,6 +64,11 @@ int init_start(const struct boot_info* bi) {
 
     tty_init();
 
+    fs_node_t* dev = devfs_create_root();
+    if (dev) {
+        (void)vfs_mount("/dev", dev);
+    }
+
     int user_ret = arch_platform_start_userspace(bi);
 
     if (bi && cmdline_has_token(bi->cmdline, "ring3")) {
index 691a331e0e1300b2ad160fc0ed14aaccef7af4e0..9176f7ad1849e1c4170f1c119e15158c20d1a607 100644 (file)
@@ -580,6 +580,26 @@ static int syscall_read_impl(int fd, void* user_buf, uint32_t len) {
     struct file* f = fd_get(fd);
     if (!f || !f->node) return -EBADF;
 
+    if (f->node->flags == FS_CHARDEVICE) {
+        uint8_t kbuf[256];
+        uint32_t total = 0;
+        while (total < len) {
+            uint32_t chunk = len - total;
+            if (chunk > sizeof(kbuf)) chunk = (uint32_t)sizeof(kbuf);
+
+            uint32_t rd = vfs_read(f->node, 0, chunk, kbuf);
+            if (rd == 0) break;
+
+            if (copy_to_user((uint8_t*)user_buf + total, kbuf, rd) < 0) {
+                return -EFAULT;
+            }
+
+            total += rd;
+            if (rd < chunk) break;
+        }
+        return (int)total;
+    }
+
     uint8_t kbuf[256];
     uint32_t total = 0;
     while (total < len) {
@@ -593,7 +613,9 @@ static int syscall_read_impl(int fd, void* user_buf, uint32_t len) {
             return -EFAULT;
         }
 
-        f->offset += rd;
+        if (f->node->flags == FS_FILE) {
+            f->offset += rd;
+        }
         total += rd;
 
         if (rd < chunk) break;
@@ -614,7 +636,7 @@ static int syscall_write_impl(int fd, const void* user_buf, uint32_t len) {
 
     struct file* f = fd_get(fd);
     if (!f || !f->node) return -EBADF;
-    if (f->node->flags != FS_FILE) return -ESPIPE;
+    if (f->node->flags != FS_FILE && f->node->flags != FS_CHARDEVICE) return -ESPIPE;
     if (!f->node->write) return -ESPIPE;
 
     uint8_t kbuf[256];
@@ -627,9 +649,9 @@ static int syscall_write_impl(int fd, const void* user_buf, uint32_t len) {
             return -EFAULT;
         }
 
-        uint32_t wr = vfs_write(f->node, f->offset, chunk, kbuf);
+        uint32_t wr = vfs_write(f->node, (f->node->flags == FS_FILE) ? f->offset : 0, chunk, kbuf);
         if (wr == 0) break;
-        f->offset += wr;
+        if (f->node->flags == FS_FILE) f->offset += wr;
         total += wr;
         if (wr < chunk) break;
     }
index c561964ec82b98c58861087d9831c736ed8a75a7..60189ec995b0ca136ae753538f5fc4defe27ac0c 100644 (file)
@@ -30,6 +30,60 @@ static int canon_empty(void) {
     return canon_head == canon_tail;
 }
 
+static uint32_t canon_count(void);
+static int waitq_push(struct process* p);
+
+int tty_write_kbuf(const void* kbuf, uint32_t len) {
+    if (!kbuf) return -EFAULT;
+    if (len > 1024 * 1024) return -EINVAL;
+
+    const char* p = (const char*)kbuf;
+    for (uint32_t i = 0; i < len; i++) {
+        uart_put_char(p[i]);
+    }
+    return (int)len;
+}
+
+int tty_read_kbuf(void* kbuf, uint32_t len) {
+    if (!kbuf) return -EFAULT;
+    if (len > 1024 * 1024) return -EINVAL;
+    if (!current_process) return -ECHILD;
+
+    while (1) {
+        uintptr_t flags = spin_lock_irqsave(&tty_lock);
+
+        if (!canon_empty()) {
+            uint32_t avail = canon_count();
+            uint32_t to_read = len;
+            if (to_read > avail) to_read = avail;
+
+            uint32_t total = 0;
+            while (total < to_read) {
+                uint32_t chunk = to_read - total;
+                if (chunk > 256U) chunk = 256U;
+
+                for (uint32_t i = 0; i < chunk; i++) {
+                    ((char*)kbuf)[total + i] = canon_buf[canon_tail];
+                    canon_tail = (canon_tail + 1U) % TTY_CANON_BUF;
+                }
+                total += chunk;
+            }
+
+            spin_unlock_irqrestore(&tty_lock, flags);
+            return (int)to_read;
+        }
+
+        if (waitq_push(current_process) == 0) {
+            current_process->state = PROCESS_BLOCKED;
+        }
+
+        spin_unlock_irqrestore(&tty_lock, flags);
+
+        hal_cpu_enable_interrupts();
+        schedule();
+    }
+}
+
 static uint32_t canon_count(void) {
     if (canon_head >= canon_tail) return canon_head - canon_tail;
     return (TTY_CANON_BUF - canon_tail) + canon_head;
index 9f067e4b31d7175daf4668fdb9fff40efcc33c2a..5fe3db982f111224f34ccfe0036e9110b6495e35 100644 (file)
@@ -591,6 +591,41 @@ void _start(void) {
 
     sys_write(1, "[init] tmpfs/mount OK\n", (uint32_t)(sizeof("[init] tmpfs/mount OK\n") - 1));
 
+    {
+        int fd = sys_open("/dev/null", 0);
+        if (fd < 0) {
+            sys_write(1, "[init] /dev/null open failed\n",
+                      (uint32_t)(sizeof("[init] /dev/null open failed\n") - 1));
+            sys_exit(1);
+        }
+        static const char z[] = "discard me";
+        int wr = sys_write(fd, z, (uint32_t)(sizeof(z) - 1));
+        if (wr != (int)(sizeof(z) - 1)) {
+            sys_write(1, "[init] /dev/null write failed\n",
+                      (uint32_t)(sizeof("[init] /dev/null write failed\n") - 1));
+            sys_exit(1);
+        }
+        (void)sys_close(fd);
+        sys_write(1, "[init] /dev/null OK\n", (uint32_t)(sizeof("[init] /dev/null OK\n") - 1));
+    }
+
+    {
+        int fd = sys_open("/dev/tty", 0);
+        if (fd < 0) {
+            sys_write(1, "[init] /dev/tty open failed\n",
+                      (uint32_t)(sizeof("[init] /dev/tty open failed\n") - 1));
+            sys_exit(1);
+        }
+        static const char m[] = "[init] /dev/tty write OK\n";
+        int wr = sys_write(fd, m, (uint32_t)(sizeof(m) - 1));
+        if (wr != (int)(sizeof(m) - 1)) {
+            sys_write(1, "[init] /dev/tty write failed\n",
+                      (uint32_t)(sizeof("[init] /dev/tty write failed\n") - 1));
+            sys_exit(1);
+        }
+        (void)sys_close(fd);
+    }
+
     enum { NCHILD = 100 };
     int children[NCHILD];
     for (int i = 0; i < NCHILD; i++) {