#define EPERM 1
#define ENOENT 2
#define EIO 5
+#define EINTR 4
#define EBADF 9
#define ECHILD 10
#define EFAULT 14
#define PROCESS_MAX_FILES 16
+#define PROCESS_MAX_SIG 32
+
struct process {
uint32_t pid;
uint32_t parent_pid;
int has_user_regs;
struct registers user_regs;
+ // Minimal signals: handler pointers, blocked mask and pending mask.
+ // handlers[i] == 0 => default
+ // handlers[i] == 1 => ignore
+ // handlers[i] >= 2 => user handler address
+ uintptr_t sig_handlers[PROCESS_MAX_SIG];
+ uint32_t sig_blocked_mask;
+ uint32_t sig_pending_mask;
+
int waiting;
int wait_pid;
int wait_result_pid;
SYSCALL_SETSID = 22,
SYSCALL_SETPGID = 23,
SYSCALL_GETPGRP = 24,
+
+ SYSCALL_SIGACTION = 25,
+ SYSCALL_SIGPROCMASK = 26,
};
#endif
#include "uart_console.h"
#include "process.h"
#include "spinlock.h"
+#include "uaccess.h"
#include <stddef.h>
#define IDT_ENTRIES 256
idt[num].flags = flags; // Present(0x80) | DPL(00) | Type(0xE = 32bit Int Gate) -> 0x8E
}
+static void deliver_signals_to_usermode(struct registers* regs) {
+ if (!regs) return;
+ if (!current_process) return;
+ if ((regs->cs & 3U) != 3U) return;
+
+ const uint32_t pending = current_process->sig_pending_mask;
+ const uint32_t blocked = current_process->sig_blocked_mask;
+ uint32_t ready = pending & ~blocked;
+ if (!ready) return;
+
+ int sig = -1;
+ for (int i = 1; i < PROCESS_MAX_SIG; i++) {
+ if ((ready & (1U << (uint32_t)i)) != 0U) {
+ sig = i;
+ break;
+ }
+ }
+ if (sig < 0) return;
+
+ current_process->sig_pending_mask &= ~(1U << (uint32_t)sig);
+
+ const uintptr_t h = current_process->sig_handlers[sig];
+ if (h == 1) {
+ return;
+ }
+
+ if (h == 0) {
+ if (sig == 11) {
+ process_exit_notify(128 + sig);
+ __asm__ volatile("sti");
+ schedule();
+ for (;;) __asm__ volatile("hlt");
+ }
+ return;
+ }
+
+ uint32_t frame[2];
+ frame[0] = regs->eip;
+ frame[1] = (uint32_t)sig;
+
+ const uint32_t new_esp = regs->useresp - (uint32_t)sizeof(frame);
+ if (copy_to_user((void*)(uintptr_t)new_esp, frame, sizeof(frame)) < 0) {
+ const int SIG_SEGV = 11;
+ process_exit_notify(128 + SIG_SEGV);
+ __asm__ volatile("sti");
+ schedule();
+ for (;;) __asm__ volatile("hlt");
+ }
+
+ regs->useresp = new_esp;
+ regs->eip = (uint32_t)h;
+}
+
/* Reconfigure the PIC to remap IRQs from 0-15 to 32-47 */
void pic_remap(void) {
uint8_t a1, a2;
}
outb(0x20, 0x20); // Master EOI
}
+
+ deliver_signals_to_usermode(regs);
}
}
int process_kill(uint32_t pid, int sig) {
- // Minimal: support SIGKILL only.
const int SIG_KILL = 9;
if (pid == 0) return -EINVAL;
- if (sig != SIG_KILL) return -EINVAL;
- // Killing self: just exit via existing path.
- if (current_process && current_process->pid == pid) {
+ if (sig <= 0 || sig >= PROCESS_MAX_SIG) return -EINVAL;
+
+ if (current_process && current_process->pid == pid && sig == SIG_KILL) {
process_exit_notify(128 + sig);
hal_cpu_enable_interrupts();
schedule();
return 0;
}
- process_close_all_files_locked(p);
- p->exit_status = 128 + sig;
- p->state = PROCESS_ZOMBIE;
+ if (sig == SIG_KILL) {
+ process_close_all_files_locked(p);
+ p->exit_status = 128 + sig;
+ p->state = PROCESS_ZOMBIE;
- if (p->pid != 0) {
- struct process* parent = process_find_locked(p->parent_pid);
- if (parent && parent->state == PROCESS_BLOCKED && parent->waiting) {
- if (parent->wait_pid == -1 || parent->wait_pid == (int)p->pid) {
- parent->wait_result_pid = (int)p->pid;
- parent->wait_result_status = p->exit_status;
- parent->state = PROCESS_READY;
+ if (p->pid != 0) {
+ struct process* parent = process_find_locked(p->parent_pid);
+ if (parent && parent->state == PROCESS_BLOCKED && parent->waiting) {
+ if (parent->wait_pid == -1 || parent->wait_pid == (int)p->pid) {
+ parent->wait_result_pid = (int)p->pid;
+ parent->wait_result_status = p->exit_status;
+ parent->state = PROCESS_READY;
+ }
}
}
+ } else {
+ p->sig_pending_mask |= (1U << (uint32_t)sig);
+ if (p->state == PROCESS_BLOCKED || p->state == PROCESS_SLEEPING) {
+ p->state = PROCESS_READY;
+ }
}
spin_unlock_irqrestore(&sched_lock, flags);
fs_node_t* node = vfs_lookup(path);
if (!node) return -ENOENT;
- if (node->flags != FS_FILE && node->flags != FS_CHARDEVICE) return -EINVAL;
struct file* f = (struct file*)kmalloc(sizeof(*f));
if (!f) return -ENOMEM;
return (int)total;
}
- if (f->node->flags != FS_FILE) return -ESPIPE;
if (!f->node->read) return -ESPIPE;
uint8_t kbuf[256];
struct file* f = fd_get(fd);
if (!f || !f->node) return -EBADF;
- if (f->node->flags != FS_FILE && f->node->flags != FS_CHARDEVICE) return -ESPIPE;
if (!f->node->write) return -ESPIPE;
+ if (((f->node->flags & FS_FILE) == 0) && f->node->flags != FS_CHARDEVICE) return -ESPIPE;
uint8_t kbuf[256];
uint32_t total = 0;
return -EFAULT;
}
- uint32_t wr = vfs_write(f->node, (f->node->flags == FS_FILE) ? f->offset : 0, chunk, kbuf);
+ uint32_t wr = vfs_write(f->node, ((f->node->flags & FS_FILE) != 0) ? f->offset : 0, chunk, kbuf);
if (wr == 0) break;
- if (f->node->flags == FS_FILE) f->offset += wr;
+ if ((f->node->flags & FS_FILE) != 0) f->offset += wr;
total += wr;
if (wr < chunk) break;
}
return (int)current_process->pgrp_id;
}
+static int syscall_sigaction_impl(int sig, uintptr_t handler, uintptr_t* old_out) {
+ if (!current_process) return -EINVAL;
+ if (sig <= 0 || sig >= PROCESS_MAX_SIG) return -EINVAL;
+
+ if (old_out) {
+ if (user_range_ok(old_out, sizeof(*old_out)) == 0) return -EFAULT;
+ uintptr_t oldh = current_process->sig_handlers[sig];
+ if (copy_to_user(old_out, &oldh, sizeof(oldh)) < 0) return -EFAULT;
+ }
+
+ current_process->sig_handlers[sig] = handler;
+ return 0;
+}
+
+static int syscall_sigprocmask_impl(uint32_t how, uint32_t mask, uint32_t* old_out) {
+ if (!current_process) return -EINVAL;
+
+ if (old_out) {
+ if (user_range_ok(old_out, sizeof(*old_out)) == 0) return -EFAULT;
+ uint32_t old = current_process->sig_blocked_mask;
+ if (copy_to_user(old_out, &old, sizeof(old)) < 0) return -EFAULT;
+ }
+
+ if (how == 0U) {
+ current_process->sig_blocked_mask = mask;
+ return 0;
+ }
+ if (how == 1U) {
+ current_process->sig_blocked_mask |= mask;
+ return 0;
+ }
+ if (how == 2U) {
+ current_process->sig_blocked_mask &= ~mask;
+ return 0;
+ }
+ return -EINVAL;
+}
+
static void syscall_handler(struct registers* regs) {
uint32_t syscall_no = regs->eax;
return;
}
+ if (syscall_no == SYSCALL_SIGACTION) {
+ int sig = (int)regs->ebx;
+ uintptr_t handler = (uintptr_t)regs->ecx;
+ uintptr_t* old_out = (uintptr_t*)regs->edx;
+ regs->eax = (uint32_t)syscall_sigaction_impl(sig, handler, old_out);
+ return;
+ }
+
+ if (syscall_no == SYSCALL_SIGPROCMASK) {
+ uint32_t how = regs->ebx;
+ uint32_t mask = regs->ecx;
+ uint32_t* old_out = (uint32_t*)regs->edx;
+ regs->eax = (uint32_t)syscall_sigprocmask_impl(how, mask, old_out);
+ return;
+ }
+
regs->eax = (uint32_t)-ENOSYS;
}
static uint32_t tty_lflag = TTY_ICANON | TTY_ECHO;
+static uint32_t tty_session_id = 0;
+static uint32_t tty_fg_pgrp = 0;
+
+enum {
+ SIGTSTP = 20,
+ SIGTTIN = 21,
+ SIGTTOU = 22,
+};
+
static int canon_empty(void) {
return canon_head == canon_tail;
}
if (!kbuf) return -EFAULT;
if (len > 1024 * 1024) return -EINVAL;
+ // Job control: background writes to controlling TTY generate SIGTTOU.
+ if (current_process && tty_session_id != 0 && current_process->session_id == tty_session_id &&
+ tty_fg_pgrp != 0 && current_process->pgrp_id != tty_fg_pgrp) {
+ (void)process_kill(current_process->pid, SIGTTOU);
+ return -EINTR;
+ }
+
const char* p = (const char*)kbuf;
for (uint32_t i = 0; i < len; i++) {
uart_put_char(p[i]);
if (len > 1024 * 1024) return -EINVAL;
if (!current_process) return -ECHILD;
+ // Job control: background reads from controlling TTY generate SIGTTIN.
+ if (tty_session_id != 0 && current_process->session_id == tty_session_id &&
+ tty_fg_pgrp != 0 && current_process->pgrp_id != tty_fg_pgrp) {
+ (void)process_kill(current_process->pid, SIGTTIN);
+ return -EINTR;
+ }
+
while (1) {
uintptr_t flags = spin_lock_irqsave(&tty_lock);
int tty_ioctl(uint32_t cmd, void* user_arg) {
if (!user_arg) return -EFAULT;
+ if (current_process && tty_session_id == 0 && current_process->session_id != 0) {
+ tty_session_id = current_process->session_id;
+ tty_fg_pgrp = current_process->pgrp_id;
+ }
+
if (cmd == TTY_TIOCGPGRP) {
if (user_range_ok(user_arg, sizeof(int)) == 0) return -EFAULT;
- int fg = 0;
+ int fg = (int)tty_fg_pgrp;
if (copy_to_user(user_arg, &fg, sizeof(fg)) < 0) return -EFAULT;
return 0;
}
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 (fg != 0) return -EINVAL;
+ if (!current_process) return -EINVAL;
+
+ // If there is no controlling session yet, only allow setting fg=0.
+ // This matches early-boot semantics used by userland smoke tests.
+ if (tty_session_id == 0) {
+ if (fg != 0) return -EPERM;
+ tty_fg_pgrp = 0;
+ return 0;
+ }
+
+ if (current_process->session_id != tty_session_id) return -EPERM;
+ if (fg < 0) return -EINVAL;
+ tty_fg_pgrp = (uint32_t)fg;
return 0;
}
line_len = 0;
canon_head = canon_tail = 0;
waitq_head = waitq_tail = 0;
+ tty_session_id = 0;
+ tty_fg_pgrp = 0;
keyboard_set_callback(tty_keyboard_cb);
}
if (len > 1024 * 1024) return -EINVAL;
if (user_range_ok(user_buf, (size_t)len) == 0) return -EFAULT;
+ // Job control: background writes to controlling TTY generate SIGTTOU.
+ if (current_process && tty_session_id != 0 && current_process->session_id == tty_session_id &&
+ tty_fg_pgrp != 0 && current_process->pgrp_id != tty_fg_pgrp) {
+ (void)process_kill(current_process->pid, SIGTTOU);
+ return -EINTR;
+ }
+
char kbuf[256];
uint32_t remaining = len;
uintptr_t up = (uintptr_t)user_buf;
if (user_range_ok(user_buf, (size_t)len) == 0) return -EFAULT;
if (!current_process) return -ECHILD;
+ // Job control: background reads from controlling TTY generate SIGTTIN.
+ if (tty_session_id != 0 && current_process->session_id == tty_session_id &&
+ tty_fg_pgrp != 0 && current_process->pgrp_id != tty_fg_pgrp) {
+ (void)process_kill(current_process->pid, SIGTTIN);
+ return -EINTR;
+ }
+
while (1) {
uintptr_t flags = spin_lock_irqsave(&tty_lock);
SYSCALL_SETSID = 22,
SYSCALL_SETPGID = 23,
SYSCALL_GETPGRP = 24,
+
+ SYSCALL_SIGACTION = 25,
+ SYSCALL_SIGPROCMASK = 26,
};
enum {
enum {
SIGKILL = 9,
+ SIGUSR1 = 10,
SIGSEGV = 11,
+ SIGTTIN = 21,
+ SIGTTOU = 22,
};
enum {
return ret;
}
+static void write_int_dec(int v) {
+ char buf[16];
+ int i = 0;
+ if (v == 0) {
+ buf[i++] = '0';
+ } else {
+ int neg = 0;
+ if (v < 0) {
+ neg = 1;
+ v = -v;
+ }
+ while (v > 0 && i < (int)sizeof(buf)) {
+ buf[i++] = (char)('0' + (v % 10));
+ v /= 10;
+ }
+ if (neg && i < (int)sizeof(buf)) {
+ buf[i++] = '-';
+ }
+ for (int j = 0; j < i / 2; j++) {
+ char t = buf[j];
+ buf[j] = buf[i - 1 - j];
+ buf[i - 1 - j] = t;
+ }
+ }
+ (void)sys_write(1, buf, (uint32_t)i);
+}
+
+static void write_hex8(uint8_t v) {
+ static const char hex[] = "0123456789ABCDEF";
+ char b[2];
+ b[0] = hex[(v >> 4) & 0xF];
+ b[1] = hex[v & 0xF];
+ (void)sys_write(1, b, 2);
+}
+
+static void write_hex32(uint32_t v) {
+ static const char hex[] = "0123456789ABCDEF";
+ char b[8];
+ for (int i = 0; i < 8; i++) {
+ uint32_t shift = (uint32_t)(28 - 4 * i);
+ b[i] = hex[(v >> shift) & 0xFU];
+ }
+ (void)sys_write(1, b, 8);
+}
+
+static int sys_sigaction(int sig, void (*handler)(int), uintptr_t* old_out) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_SIGACTION), "b"(sig), "c"(handler), "d"(old_out)
+ : "memory"
+ );
+ return ret;
+}
+
static int sys_select(uint32_t nfds, uint64_t* readfds, uint64_t* writefds, uint64_t* exceptfds, int32_t timeout) {
int ret;
__asm__ volatile(
__builtin_unreachable();
}
+static volatile int got_usr1 = 0;
+static volatile int got_ttin = 0;
+static volatile int got_ttou = 0;
+
+static void usr1_handler(int sig) {
+ (void)sig;
+ got_usr1 = 1;
+ sys_write(1, "[init] SIGUSR1 handler OK\n",
+ (uint32_t)(sizeof("[init] SIGUSR1 handler OK\n") - 1));
+}
+
+static void ttin_handler(int sig) {
+ (void)sig;
+ got_ttin = 1;
+}
+
+static void ttou_handler(int sig) {
+ (void)sig;
+ got_ttou = 1;
+}
+
void _start(void) {
__asm__ volatile(
"mov $0x23, %ax\n"
(void)sys_write(1, msg, (uint32_t)(sizeof(msg) - 1));
static const char path[] = "/bin/init.elf";
+
int fd = sys_open(path, 0);
if (fd < 0) {
- sys_write(1, "[init] open failed\n", (uint32_t)(sizeof("[init] open failed\n") - 1));
+ sys_write(1, "[init] open failed fd=", (uint32_t)(sizeof("[init] open failed fd=") - 1));
+ write_int_dec(fd);
+ sys_write(1, "\n", 1);
sys_exit(1);
}
uint8_t hdr[4];
- int rd = sys_read(fd, hdr, (uint32_t)sizeof(hdr));
- if (sys_close(fd) < 0) {
- sys_write(1, "[init] close failed\n", (uint32_t)(sizeof("[init] close failed\n") - 1));
- sys_exit(1);
- }
-
+ int rd = sys_read(fd, hdr, 4);
+ (void)sys_close(fd);
if (rd == 4 && hdr[0] == 0x7F && hdr[1] == 'E' && hdr[2] == 'L' && hdr[3] == 'F') {
sys_write(1, "[init] open/read/close OK (ELF magic)\n",
(uint32_t)(sizeof("[init] open/read/close OK (ELF magic)\n") - 1));
} else {
- sys_write(1, "[init] read failed or bad header\n",
- (uint32_t)(sizeof("[init] read failed or bad header\n") - 1));
+ sys_write(1, "[init] read failed or bad header rd=", (uint32_t)(sizeof("[init] read failed or bad header rd=") - 1));
+ write_int_dec(rd);
+ sys_write(1, " hdr=", (uint32_t)(sizeof(" hdr=") - 1));
+ for (int i = 0; i < 4; i++) {
+ write_hex8(hdr[i]);
+ }
+ sys_write(1, "\n", 1);
sys_exit(1);
}
}
(void)sys_close(tfd);
+ }
+
{
int pid = sys_fork();
if (pid < 0) {
(uint32_t)(sizeof("[init] ioctl(/dev/tty) OK\n") - 1));
}
+ // A2: basic job control. A background pgrp read/write on controlling TTY should raise SIGTTIN/SIGTTOU.
+ {
+ int leader = sys_fork();
+ if (leader < 0) {
+ sys_write(1, "[init] fork(job control leader) failed\n",
+ (uint32_t)(sizeof("[init] fork(job control leader) failed\n") - 1));
+ sys_exit(1);
+ }
+ if (leader == 0) {
+ int me = sys_getpid();
+ int sid = sys_setsid();
+ if (sid != me) {
+ sys_write(1, "[init] setsid(job control) failed\n",
+ (uint32_t)(sizeof("[init] setsid(job control) failed\n") - 1));
+ sys_exit(1);
+ }
+
+ int tfd = sys_open("/dev/tty", 0);
+ if (tfd < 0) {
+ sys_write(1, "[init] open(/dev/tty) for job control failed\n",
+ (uint32_t)(sizeof("[init] open(/dev/tty) for job control failed\n") - 1));
+ sys_exit(1);
+ }
+
+ // Touch ioctl to make kernel acquire controlling session/pgrp.
+ int fg = 0;
+ (void)sys_ioctl(tfd, TIOCGPGRP, &fg);
+
+ fg = me;
+ if (sys_ioctl(tfd, TIOCSPGRP, &fg) < 0) {
+ sys_write(1, "[init] ioctl TIOCSPGRP(job control) failed\n",
+ (uint32_t)(sizeof("[init] ioctl TIOCSPGRP(job control) failed\n") - 1));
+ sys_exit(1);
+ }
+
+ int bg = sys_fork();
+ if (bg < 0) {
+ sys_write(1, "[init] fork(job control bg) failed\n",
+ (uint32_t)(sizeof("[init] fork(job control bg) failed\n") - 1));
+ sys_exit(1);
+ }
+ if (bg == 0) {
+ (void)sys_setpgid(0, me + 1);
+
+ (void)sys_sigaction(SIGTTIN, ttin_handler, 0);
+ (void)sys_sigaction(SIGTTOU, ttou_handler, 0);
+
+ uint8_t b = 0;
+ (void)sys_read(tfd, &b, 1);
+ if (!got_ttin) {
+ sys_write(1, "[init] SIGTTIN job control failed\n",
+ (uint32_t)(sizeof("[init] SIGTTIN job control failed\n") - 1));
+ sys_exit(1);
+ }
+
+ const char msg2[] = "x";
+ (void)sys_write(tfd, msg2, 1);
+ if (!got_ttou) {
+ sys_write(1, "[init] SIGTTOU job control failed\n",
+ (uint32_t)(sizeof("[init] SIGTTOU job control failed\n") - 1));
+ sys_exit(1);
+ }
+
+ sys_exit(0);
+ }
+
+ int st2 = 0;
+ int wp2 = sys_waitpid(bg, &st2, 0);
+ if (wp2 != bg || st2 != 0) {
+ sys_write(1, "[init] waitpid(job control bg) failed wp=", (uint32_t)(sizeof("[init] waitpid(job control bg) failed wp=") - 1));
+ write_int_dec(wp2);
+ sys_write(1, " st=", (uint32_t)(sizeof(" st=") - 1));
+ write_int_dec(st2);
+ sys_write(1, "\n", 1);
+ sys_exit(1);
+ }
+
+ (void)sys_close(tfd);
+ sys_exit(0);
+ }
+
+ int stL = 0;
+ int wpL = sys_waitpid(leader, &stL, 0);
+ if (wpL != leader || stL != 0) {
+ sys_write(1, "[init] waitpid(job control leader) failed wp=", (uint32_t)(sizeof("[init] waitpid(job control leader) failed wp=") - 1));
+ write_int_dec(wpL);
+ sys_write(1, " st=", (uint32_t)(sizeof(" st=") - 1));
+ write_int_dec(stL);
+ sys_write(1, "\n", 1);
+ sys_exit(1);
+ }
+
+ sys_write(1, "[init] job control (SIGTTIN/SIGTTOU) OK\n",
+ (uint32_t)(sizeof("[init] job control (SIGTTIN/SIGTTOU) OK\n") - 1));
+ }
+
{
int fd = sys_open("/dev/null", 0);
if (fd < 0) {
sys_write(1, "[init] setsid/setpgid/getpgrp OK\n",
(uint32_t)(sizeof("[init] setsid/setpgid/getpgrp OK\n") - 1));
}
+
+ {
+ uintptr_t oldh = 0;
+ if (sys_sigaction(SIGUSR1, usr1_handler, &oldh) < 0) {
+ sys_write(1, "[init] sigaction failed\n",
+ (uint32_t)(sizeof("[init] sigaction failed\n") - 1));
+ sys_exit(1);
+ }
+
+ int me = sys_getpid();
+ if (sys_kill(me, SIGUSR1) < 0) {
+ sys_write(1, "[init] kill(SIGUSR1) failed\n",
+ (uint32_t)(sizeof("[init] kill(SIGUSR1) failed\n") - 1));
+ sys_exit(1);
+ }
+
+ for (uint32_t i = 0; i < 2000000U; i++) {
+ if (got_usr1) break;
+ }
+
+ if (!got_usr1) {
+ sys_write(1, "[init] SIGUSR1 not delivered\n",
+ (uint32_t)(sizeof("[init] SIGUSR1 not delivered\n") - 1));
+ sys_exit(1);
+ }
+
+ sys_write(1, "[init] sigaction/kill(SIGUSR1) OK\n",
+ (uint32_t)(sizeof("[init] sigaction/kill(SIGUSR1) OK\n") - 1));
}
fd = sys_open("/tmp/hello.txt", 0);
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)) {
+ if (sys_write(fd, z, (uint32_t)(sizeof(z) - 1)) != (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);