#include "errno.h"
#include "tty.h"
+#include "pty.h"
#include "utils.h"
struct devfs_root {
static struct devfs_root g_dev_root;
static fs_node_t g_dev_null;
static fs_node_t g_dev_tty;
+static fs_node_t g_dev_ptmx;
+static fs_node_t g_dev_pts_dir;
+static fs_node_t g_dev_pts0;
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) {
return (uint32_t)rc;
}
+static uint32_t dev_ptmx_read(fs_node_t* node, uint32_t offset, uint32_t size, uint8_t* buffer) {
+ (void)node;
+ (void)offset;
+ int rc = pty_master_read_kbuf(buffer, size);
+ if (rc < 0) return 0;
+ return (uint32_t)rc;
+}
+
+static uint32_t dev_ptmx_write(fs_node_t* node, uint32_t offset, uint32_t size, const uint8_t* buffer) {
+ (void)node;
+ (void)offset;
+ int rc = pty_master_write_kbuf(buffer, size);
+ if (rc < 0) return 0;
+ return (uint32_t)rc;
+}
+
+static uint32_t dev_pts0_read(fs_node_t* node, uint32_t offset, uint32_t size, uint8_t* buffer) {
+ (void)node;
+ (void)offset;
+ int rc = pty_slave_read_kbuf(buffer, size);
+ if (rc < 0) return 0;
+ return (uint32_t)rc;
+}
+
+static uint32_t dev_pts0_write(fs_node_t* node, uint32_t offset, uint32_t size, const uint8_t* buffer) {
+ (void)node;
+ (void)offset;
+ int rc = pty_slave_write_kbuf(buffer, size);
+ if (rc < 0) return 0;
+ return (uint32_t)rc;
+}
+
static struct fs_node* devfs_finddir_impl(struct fs_node* node, const 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;
+ if (strcmp(name, "ptmx") == 0) return &g_dev_ptmx;
+ if (strcmp(name, "pts") == 0) return &g_dev_pts_dir;
+ return 0;
+}
+
+static struct fs_node* devfs_pts_finddir_impl(struct fs_node* node, const char* name) {
+ (void)node;
+ if (!name || name[0] == 0) return 0;
+ if (strcmp(name, "0") == 0) return &g_dev_pts0;
return 0;
}
g_dev_tty.open = 0;
g_dev_tty.close = 0;
g_dev_tty.finddir = 0;
+
+ memset(&g_dev_ptmx, 0, sizeof(g_dev_ptmx));
+ strcpy(g_dev_ptmx.name, "ptmx");
+ g_dev_ptmx.flags = FS_CHARDEVICE;
+ g_dev_ptmx.inode = 4;
+ g_dev_ptmx.length = 0;
+ g_dev_ptmx.read = &dev_ptmx_read;
+ g_dev_ptmx.write = &dev_ptmx_write;
+ g_dev_ptmx.open = 0;
+ g_dev_ptmx.close = 0;
+ g_dev_ptmx.finddir = 0;
+
+ memset(&g_dev_pts_dir, 0, sizeof(g_dev_pts_dir));
+ strcpy(g_dev_pts_dir.name, "pts");
+ g_dev_pts_dir.flags = FS_DIRECTORY;
+ g_dev_pts_dir.inode = 5;
+ g_dev_pts_dir.length = 0;
+ g_dev_pts_dir.read = 0;
+ g_dev_pts_dir.write = 0;
+ g_dev_pts_dir.open = 0;
+ g_dev_pts_dir.close = 0;
+ g_dev_pts_dir.finddir = &devfs_pts_finddir_impl;
+
+ memset(&g_dev_pts0, 0, sizeof(g_dev_pts0));
+ strcpy(g_dev_pts0.name, "0");
+ g_dev_pts0.flags = FS_CHARDEVICE;
+ g_dev_pts0.inode = 6;
+ g_dev_pts0.length = 0;
+ g_dev_pts0.read = &dev_pts0_read;
+ g_dev_pts0.write = &dev_pts0_write;
+ g_dev_pts0.open = 0;
+ g_dev_pts0.close = 0;
+ g_dev_pts0.finddir = 0;
}
fs_node_t* devfs_create_root(void) {
--- /dev/null
+#include "pty.h"
+
+#include "errno.h"
+#include "process.h"
+#include "spinlock.h"
+#include "uaccess.h"
+
+#include "hal/cpu.h"
+
+#include <stddef.h>
+
+#define PTY_BUF_CAP 1024
+#define PTY_WAITQ_MAX 16
+
+static spinlock_t pty_lock = {0};
+
+static uint8_t m2s_buf[PTY_BUF_CAP];
+static uint32_t m2s_head = 0;
+static uint32_t m2s_tail = 0;
+
+static uint8_t s2m_buf[PTY_BUF_CAP];
+static uint32_t s2m_head = 0;
+static uint32_t s2m_tail = 0;
+
+static struct process* m2s_waitq[PTY_WAITQ_MAX];
+static uint32_t m2s_wq_head = 0;
+static uint32_t m2s_wq_tail = 0;
+
+static struct process* s2m_waitq[PTY_WAITQ_MAX];
+static uint32_t s2m_wq_head = 0;
+static uint32_t s2m_wq_tail = 0;
+
+static uint32_t pty_session_id = 0;
+static uint32_t pty_fg_pgrp = 0;
+
+enum {
+ SIGTTIN = 21,
+ SIGTTOU = 22,
+};
+
+enum {
+ TTY_TIOCGPGRP = 0x540F,
+ TTY_TIOCSPGRP = 0x5410,
+};
+
+static uint32_t rb_count(uint32_t head, uint32_t tail) {
+ if (head >= tail) return head - tail;
+ return (PTY_BUF_CAP - tail) + head;
+}
+
+static uint32_t rb_free(uint32_t head, uint32_t tail) {
+ return (PTY_BUF_CAP - 1U) - rb_count(head, tail);
+}
+
+static void rb_push(uint8_t* buf, uint32_t* head, uint32_t* tail, uint8_t c) {
+ uint32_t next = (*head + 1U) % PTY_BUF_CAP;
+ if (next == *tail) {
+ *tail = (*tail + 1U) % PTY_BUF_CAP;
+ }
+ buf[*head] = c;
+ *head = next;
+}
+
+static int rb_pop(uint8_t* buf, uint32_t* head, uint32_t* tail, uint8_t* out) {
+ if (*head == *tail) return 0;
+ *out = buf[*tail];
+ *tail = (*tail + 1U) % PTY_BUF_CAP;
+ return 1;
+}
+
+static int waitq_push(struct process** q, uint32_t* head, uint32_t* tail, struct process* p) {
+ uint32_t next = (*head + 1U) % PTY_WAITQ_MAX;
+ if (next == *tail) return -1;
+ q[*head] = p;
+ *head = next;
+ return 0;
+}
+
+static struct process* waitq_pop(struct process** q, uint32_t* head, uint32_t* tail) {
+ if (*head == *tail) return NULL;
+ struct process* p = q[*tail];
+ *tail = (*tail + 1U) % PTY_WAITQ_MAX;
+ return p;
+}
+
+static void waitq_wake_one(struct process** q, uint32_t* head, uint32_t* tail) {
+ struct process* p = waitq_pop(q, head, tail);
+ if (p && p->state == PROCESS_BLOCKED) {
+ p->state = PROCESS_READY;
+ }
+}
+
+void pty_init(void) {
+ spinlock_init(&pty_lock);
+ m2s_head = m2s_tail = 0;
+ s2m_head = s2m_tail = 0;
+ m2s_wq_head = m2s_wq_tail = 0;
+ s2m_wq_head = s2m_wq_tail = 0;
+ pty_session_id = 0;
+ pty_fg_pgrp = 0;
+}
+
+static int pty_jobctl_write_check(void) {
+ if (current_process && pty_session_id != 0 && current_process->session_id == pty_session_id &&
+ pty_fg_pgrp != 0 && current_process->pgrp_id != pty_fg_pgrp) {
+ (void)process_kill(current_process->pid, SIGTTOU);
+ return -EINTR;
+ }
+ return 0;
+}
+
+static int pty_jobctl_read_check(void) {
+ if (!current_process) return -ECHILD;
+ if (pty_session_id != 0 && current_process->session_id == pty_session_id &&
+ pty_fg_pgrp != 0 && current_process->pgrp_id != pty_fg_pgrp) {
+ (void)process_kill(current_process->pid, SIGTTIN);
+ return -EINTR;
+ }
+ return 0;
+}
+
+int pty_master_can_read(void) {
+ uintptr_t flags = spin_lock_irqsave(&pty_lock);
+ int ready = (rb_count(s2m_head, s2m_tail) != 0U) ? 1 : 0;
+ spin_unlock_irqrestore(&pty_lock, flags);
+ return ready;
+}
+
+int pty_master_can_write(void) {
+ uintptr_t flags = spin_lock_irqsave(&pty_lock);
+ int ready = (rb_free(m2s_head, m2s_tail) != 0U) ? 1 : 0;
+ spin_unlock_irqrestore(&pty_lock, flags);
+ return ready;
+}
+
+int pty_slave_can_read(void) {
+ uintptr_t flags = spin_lock_irqsave(&pty_lock);
+ int ready = (rb_count(m2s_head, m2s_tail) != 0U) ? 1 : 0;
+ spin_unlock_irqrestore(&pty_lock, flags);
+ return ready;
+}
+
+int pty_slave_can_write(void) {
+ uintptr_t flags = spin_lock_irqsave(&pty_lock);
+ int ready = (rb_free(s2m_head, s2m_tail) != 0U) ? 1 : 0;
+ spin_unlock_irqrestore(&pty_lock, flags);
+ return ready;
+}
+
+int pty_master_read_kbuf(void* kbuf, uint32_t len) {
+ if (!kbuf) return -EFAULT;
+ if (len > 1024 * 1024) return -EINVAL;
+
+ int jc = pty_jobctl_read_check();
+ if (jc < 0) return jc;
+
+ while (1) {
+ uintptr_t flags = spin_lock_irqsave(&pty_lock);
+ uint32_t avail = rb_count(s2m_head, s2m_tail);
+ if (avail != 0U) {
+ uint32_t to_read = len;
+ if (to_read > avail) to_read = avail;
+ for (uint32_t i = 0; i < to_read; i++) {
+ uint8_t c = 0;
+ (void)rb_pop(s2m_buf, &s2m_head, &s2m_tail, &c);
+ ((uint8_t*)kbuf)[i] = c;
+ }
+ spin_unlock_irqrestore(&pty_lock, flags);
+ return (int)to_read;
+ }
+
+ if (current_process) {
+ if (waitq_push(s2m_waitq, &s2m_wq_head, &s2m_wq_tail, current_process) == 0) {
+ current_process->state = PROCESS_BLOCKED;
+ }
+ }
+
+ spin_unlock_irqrestore(&pty_lock, flags);
+ hal_cpu_enable_interrupts();
+ schedule();
+ }
+}
+
+int pty_master_write_kbuf(const void* kbuf, uint32_t len) {
+ if (!kbuf) return -EFAULT;
+ if (len > 1024 * 1024) return -EINVAL;
+
+ int jc = pty_jobctl_write_check();
+ if (jc < 0) return jc;
+
+ uintptr_t flags = spin_lock_irqsave(&pty_lock);
+ uint32_t free = rb_free(m2s_head, m2s_tail);
+ uint32_t to_write = len;
+ if (to_write > free) to_write = free;
+
+ for (uint32_t i = 0; i < to_write; i++) {
+ rb_push(m2s_buf, &m2s_head, &m2s_tail, ((const uint8_t*)kbuf)[i]);
+ }
+
+ if (to_write) {
+ waitq_wake_one(m2s_waitq, &m2s_wq_head, &m2s_wq_tail);
+ }
+
+ spin_unlock_irqrestore(&pty_lock, flags);
+ return (int)to_write;
+}
+
+int pty_slave_read_kbuf(void* kbuf, uint32_t len) {
+ if (!kbuf) return -EFAULT;
+ if (len > 1024 * 1024) return -EINVAL;
+
+ int jc = pty_jobctl_read_check();
+ if (jc < 0) return jc;
+
+ while (1) {
+ uintptr_t flags = spin_lock_irqsave(&pty_lock);
+ uint32_t avail = rb_count(m2s_head, m2s_tail);
+ if (avail != 0U) {
+ uint32_t to_read = len;
+ if (to_read > avail) to_read = avail;
+ for (uint32_t i = 0; i < to_read; i++) {
+ uint8_t c = 0;
+ (void)rb_pop(m2s_buf, &m2s_head, &m2s_tail, &c);
+ ((uint8_t*)kbuf)[i] = c;
+ }
+ spin_unlock_irqrestore(&pty_lock, flags);
+ return (int)to_read;
+ }
+
+ if (current_process) {
+ if (waitq_push(m2s_waitq, &m2s_wq_head, &m2s_wq_tail, current_process) == 0) {
+ current_process->state = PROCESS_BLOCKED;
+ }
+ }
+
+ spin_unlock_irqrestore(&pty_lock, flags);
+ hal_cpu_enable_interrupts();
+ schedule();
+ }
+}
+
+int pty_slave_write_kbuf(const void* kbuf, uint32_t len) {
+ if (!kbuf) return -EFAULT;
+ if (len > 1024 * 1024) return -EINVAL;
+
+ int jc = pty_jobctl_write_check();
+ if (jc < 0) return jc;
+
+ uintptr_t flags = spin_lock_irqsave(&pty_lock);
+ uint32_t free = rb_free(s2m_head, s2m_tail);
+ uint32_t to_write = len;
+ if (to_write > free) to_write = free;
+
+ for (uint32_t i = 0; i < to_write; i++) {
+ rb_push(s2m_buf, &s2m_head, &s2m_tail, ((const uint8_t*)kbuf)[i]);
+ }
+
+ if (to_write) {
+ waitq_wake_one(s2m_waitq, &s2m_wq_head, &s2m_wq_tail);
+ }
+
+ spin_unlock_irqrestore(&pty_lock, flags);
+ return (int)to_write;
+}
+
+int pty_slave_ioctl(uint32_t cmd, void* user_arg) {
+ if (!user_arg) return -EFAULT;
+
+ if (current_process && pty_session_id == 0 && current_process->session_id != 0) {
+ pty_session_id = current_process->session_id;
+ pty_fg_pgrp = current_process->pgrp_id;
+ }
+
+ if (cmd == TTY_TIOCGPGRP) {
+ if (user_range_ok(user_arg, sizeof(int)) == 0) return -EFAULT;
+ int fg = (int)pty_fg_pgrp;
+ if (copy_to_user(user_arg, &fg, sizeof(fg)) < 0) return -EFAULT;
+ return 0;
+ }
+
+ if (cmd == TTY_TIOCSPGRP) {
+ if (user_range_ok(user_arg, sizeof(int)) == 0) return -EFAULT;
+ int fg = 0;
+ if (copy_from_user(&fg, user_arg, sizeof(fg)) < 0) return -EFAULT;
+ if (!current_process) return -EINVAL;
+
+ if (pty_session_id == 0) {
+ if (fg != 0) return -EPERM;
+ pty_fg_pgrp = 0;
+ return 0;
+ }
+
+ if (current_process->session_id != pty_session_id) return -EPERM;
+ if (fg < 0) return -EINVAL;
+ pty_fg_pgrp = (uint32_t)fg;
+ return 0;
+ }
+
+ return -EINVAL;
+}
(void)sys_write(1, b, 8);
}
+static int memeq(const void* a, const void* b, uint32_t n) {
+ const uint8_t* pa = (const uint8_t*)a;
+ const uint8_t* pb = (const uint8_t*)b;
+ for (uint32_t i = 0; i < n; i++) {
+ if (pa[i] != pb[i]) return 0;
+ }
+ return 1;
+}
+
static int sys_sigaction2(int sig, const struct sigaction* act, struct sigaction* oldact) {
int ret;
__asm__ volatile(
(uint32_t)(sizeof("[init] poll(/dev/null) OK\n") - 1));
}
+ {
+ int mfd = sys_open("/dev/ptmx", 0);
+ int sfd = sys_open("/dev/pts/0", 0);
+ if (mfd < 0 || sfd < 0) {
+ sys_write(1, "[init] pty open failed\n",
+ (uint32_t)(sizeof("[init] pty open failed\n") - 1));
+ sys_exit(1);
+ }
+
+ static const char m2s[] = "m2s";
+ if (sys_write(mfd, m2s, (uint32_t)(sizeof(m2s) - 1)) != (int)(sizeof(m2s) - 1)) {
+ sys_write(1, "[init] pty write master failed\n",
+ (uint32_t)(sizeof("[init] pty write master failed\n") - 1));
+ sys_exit(1);
+ }
+
+ struct pollfd p;
+ p.fd = sfd;
+ p.events = POLLIN;
+ p.revents = 0;
+ int rc = sys_poll(&p, 1, 50);
+ if (rc != 1 || (p.revents & POLLIN) == 0) {
+ sys_write(1, "[init] pty poll slave failed\n",
+ (uint32_t)(sizeof("[init] pty poll slave failed\n") - 1));
+ sys_exit(1);
+ }
+
+ char buf[8];
+ int rd = sys_read(sfd, buf, (uint32_t)(sizeof(m2s) - 1));
+ if (rd != (int)(sizeof(m2s) - 1) || !memeq(buf, m2s, (uint32_t)(sizeof(m2s) - 1))) {
+ sys_write(1, "[init] pty read slave failed\n",
+ (uint32_t)(sizeof("[init] pty read slave failed\n") - 1));
+ sys_exit(1);
+ }
+
+ static const char s2m[] = "s2m";
+ if (sys_write(sfd, s2m, (uint32_t)(sizeof(s2m) - 1)) != (int)(sizeof(s2m) - 1)) {
+ sys_write(1, "[init] pty write slave failed\n",
+ (uint32_t)(sizeof("[init] pty write slave failed\n") - 1));
+ sys_exit(1);
+ }
+
+ p.fd = mfd;
+ p.events = POLLIN;
+ p.revents = 0;
+ rc = sys_poll(&p, 1, 50);
+ if (rc != 1 || (p.revents & POLLIN) == 0) {
+ sys_write(1, "[init] pty poll master failed\n",
+ (uint32_t)(sizeof("[init] pty poll master failed\n") - 1));
+ sys_exit(1);
+ }
+
+ rd = sys_read(mfd, buf, (uint32_t)(sizeof(s2m) - 1));
+ if (rd != (int)(sizeof(s2m) - 1) || !memeq(buf, s2m, (uint32_t)(sizeof(s2m) - 1))) {
+ sys_write(1, "[init] pty read master failed\n",
+ (uint32_t)(sizeof("[init] pty read master failed\n") - 1));
+ sys_exit(1);
+ }
+
+ (void)sys_close(mfd);
+ (void)sys_close(sfd);
+ sys_write(1, "[init] pty OK\n", (uint32_t)(sizeof("[init] pty OK\n") - 1));
+ }
+
{
sys_write(1, "[init] setsid test: before fork\n",
(uint32_t)(sizeof("[init] setsid test: before fork\n") - 1));