From f2a0ff093b814c8baf416324368fb15c677b37a7 Mon Sep 17 00:00:00 2001 From: Tulio A M Mendes Date: Sat, 7 Feb 2026 23:55:41 -0300 Subject: [PATCH] posix: devfs with /dev/null and /dev/tty 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 | 8 ++++ include/tty.h | 3 ++ src/kernel/devfs.c | 97 ++++++++++++++++++++++++++++++++++++++++++++ src/kernel/init.c | 6 +++ src/kernel/syscall.c | 30 ++++++++++++-- src/kernel/tty.c | 54 ++++++++++++++++++++++++ user/init.c | 35 ++++++++++++++++ 7 files changed, 229 insertions(+), 4 deletions(-) create mode 100644 include/devfs.h create mode 100644 src/kernel/devfs.c diff --git a/include/devfs.h b/include/devfs.h new file mode 100644 index 0000000..ba6256d --- /dev/null +++ b/include/devfs.h @@ -0,0 +1,8 @@ +#ifndef DEVFS_H +#define DEVFS_H + +#include "fs.h" + +fs_node_t* devfs_create_root(void); + +#endif diff --git a/include/tty.h b/include/tty.h index c3cb136..4d39686 100644 --- a/include/tty.h +++ b/include/tty.h @@ -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 index 0000000..884cc28 --- /dev/null +++ b/src/kernel/devfs.c @@ -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; +} diff --git a/src/kernel/init.c b/src/kernel/init.c index c0361ac..afb5749 100644 --- a/src/kernel/init.c +++ b/src/kernel/init.c @@ -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")) { diff --git a/src/kernel/syscall.c b/src/kernel/syscall.c index 691a331..9176f7a 100644 --- a/src/kernel/syscall.c +++ b/src/kernel/syscall.c @@ -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; } diff --git a/src/kernel/tty.c b/src/kernel/tty.c index c561964..60189ec 100644 --- a/src/kernel/tty.c +++ b/src/kernel/tty.c @@ -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; diff --git a/user/init.c b/user/init.c index 9f067e4..5fe3db9 100644 --- a/user/init.c +++ b/user/init.c @@ -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++) { -- 2.43.0