diff --git a/src/include/kernel/sched.h b/src/include/kernel/sched.h index 56d4daa..b6331d6 100644 --- a/src/include/kernel/sched.h +++ b/src/include/kernel/sched.h @@ -40,6 +40,12 @@ #define WNOHANG 0x02 #define WUNTRACED 0x04 +typedef struct SignalQueue { + struct SignalQueue *next; + int signum; + struct Thread *sender; +} SignalQueue; + typedef struct Thread { int status, cpu, priority; pid_t pid, tid; // pid == tid for the main thread @@ -47,7 +53,14 @@ typedef struct Thread { lock_t lock; bool normalExit; // true when the thread ends by exit() and is not forcefully killed - bool clean; // true when the exit status has been read by waitpid() + bool clean; // true when the exit status has been read by waitpid() + bool handlingSignal; // true inside a signal handler + + void *signals; + SignalQueue *signalQueue; + uintptr_t signalTrampoline; + uintptr_t siginfo; + uintptr_t signalUserContext; SyscallRequest syscall; // for when the thread is blocked int exitStatus; // for zombie threads @@ -56,6 +69,7 @@ typedef struct Thread { struct Thread *next; void *context; // platform-specific (page tables, registers, etc) + void *signalContext; uintptr_t highest; } Thread; diff --git a/src/include/kernel/signal.h b/src/include/kernel/signal.h new file mode 100644 index 0000000..87ff905 --- /dev/null +++ b/src/include/kernel/signal.h @@ -0,0 +1,110 @@ +/* + * lux - a lightweight unix-like operating system + * Omar Elghoul, 2024 + * + * Core Microkernel + */ + +#pragma once + +#include +#include + +/* Signal Handler Macros */ +#define SIG_DFL (void (*)(int))0 +#define SIG_ERR (void (*)(int))1 +#define SIG_HOLD (void (*)(int))2 +#define SIG_IGN (void (*)(int))3 + +#define SIG_T 1 /* terminate */ +#define SIG_A 2 /* abort */ +#define SIG_C 3 /* continue */ +#define SIG_S 4 /* stop */ +#define SIG_I 5 /* ignore */ + +/* ISO C Signals */ +#define SIGABRT 1 +#define SIGFPE 2 +#define SIGILL 3 +#define SIGINT 4 +#define SIGSEGV 5 +#define SIGTERM 6 + +/* POSIX Extension Signals */ +#define SIGALRM 7 +#define SIGBUS 8 +#define SIGCHLD 9 +#define SIGCONT 10 +#define SIGHUP 11 +#define SIGKILL 12 +#define SIGPIPE 13 +#define SIGQUIT 14 +#define SIGSTOP 15 +#define SIGTSTP 16 +#define SIGTTIN 17 +#define SIGTTOU 18 +#define SIGUSR1 19 +#define SIGUSR2 20 +#define SIGPOLL 21 +#define SIGSYS 22 +#define SIGTRAP 23 +#define SIGURG 24 +#define SIGVTALRM 25 +#define SIGXCPU 26 +#define SIGXFSZ 27 + +#define MAX_SIGNAL 27 + +/* Signal Flags */ +#define SA_NOCLDSTOP 0x0001 +#define SA_ONSTACK 0x0002 +#define SA_RESETHAND 0x0004 +#define SA_RESTART 0x0008 +#define SA_SIGINFO 0x0010 +#define SA_NOCLDWAIT 0x0020 +#define SA_NODEFER 0x0040 + +#define SIG_BLOCK 0x0001 +#define SIG_UNBLOCK 0x0002 + +#define SS_ONSTACK 0x0001 +#define SS_DISABLE 0x0002 + +#define MINSIGSTKSZ 4096 +#define SIGSTKSZ 16384 + +typedef volatile uint32_t sig_atomic_t; +typedef uint64_t sigset_t; + +typedef struct { + int si_signo, si_code, si_errno, si_status; + pid_t si_pid; + uid_t si_uid; + void *si_addr; + long si_band; +} siginfo_t; + +struct sigaction { + union { + void (*sa_handler)(int); + void (*sa_sigaction)(int, siginfo_t *, void *); + }; + + sigset_t sa_mask; + int sa_flags; +}; + +int sigemptyset(sigset_t *); +int sigfillset(sigset_t *); +int sigaddset(sigset_t *, int); +int sigdelset(sigset_t *, int); +int sigismember(sigset_t *, int); + +void *signalDefaults(); +int signalDefaultHandler(int); +void *signalClone(const void *); +void signalHandle(Thread *); + +int kill(Thread *, pid_t, int); +int sigaction(Thread *, int, const struct sigaction *, struct sigaction *); +void sigreturn(Thread *); \ No newline at end of file diff --git a/src/include/kernel/syscalls.h b/src/include/kernel/syscalls.h index eb9df2d..75f959d 100644 --- a/src/include/kernel/syscalls.h +++ b/src/include/kernel/syscalls.h @@ -11,10 +11,11 @@ #include #include -#define MAX_SYSCALL 56 +#define MAX_SYSCALL 58 /* IPC syscall indexes, this range will be used for immediate handling without * waiting for the kernel thread to dispatch the syscall */ +#define SYSCALL_ACCEPT 44 // accept() #define SYSCALL_IPC_START 42 // bind() #define SYSCALL_IPC_END 46 // send() diff --git a/src/include/platform/platform.h b/src/include/platform/platform.h index 2707d00..a4aac77 100644 --- a/src/include/platform/platform.h +++ b/src/include/platform/platform.h @@ -61,4 +61,6 @@ int platformGetMaxIRQ(); // maximum interrupt implemented by hardware int platformConfigureIRQ(Thread *, int, IRQHandler *); // configure an IRQ pin IRQCommand *platformGetIRQCommand(); // per-CPU IRQ command structure void platformIdle(); // to be called when the CPU is idle -void platformCleanThread(void *, uintptr_t); // garbage collector after thread is killed or replaced by exec() \ No newline at end of file +void platformCleanThread(void *, uintptr_t); // garbage collector after thread is killed or replaced by exec() +int platformSendSignal(Thread *, Thread *, int, uintptr_t); +void platformSigreturn(Thread *); \ No newline at end of file diff --git a/src/include/stdlib.h b/src/include/stdlib.h index f344244..47922a7 100644 --- a/src/include/stdlib.h +++ b/src/include/stdlib.h @@ -19,10 +19,13 @@ char *itoa(int, char *, int); int atoi(const char *); char *ltoa(long, char *, int); long atol(const char *); -void *mallocUC(size_t); void *malloc(size_t); void *calloc(size_t, size_t); void *realloc(void *, size_t); +void *umalloc(size_t); +void *uxmalloc(size_t); +void *ucalloc(size_t, size_t); +void *urealloc(void *, size_t); void free(void *); int rand(); void srand(unsigned int); diff --git a/src/ipc/signal.c b/src/ipc/signal.c new file mode 100644 index 0000000..e376c7f --- /dev/null +++ b/src/ipc/signal.c @@ -0,0 +1,339 @@ +/* + * lux - a lightweight unix-like operating system + * Omar Elghoul, 2024 + * + * Core Microkernel + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Implementation of ISO C and POSIX Signals */ + +/* sigemptyset(): clears a set of signals + * params: set - set of signals + * returns: zero + */ + +int sigemptyset(sigset_t *set) { + *set = 0; + return 0; +} + +/* sigfillset(): fills a set of signals with all supported signals + * params: set - set of signals + * returns: zero + */ + +int sigfillset(sigset_t *set) { + *set = 0; + + for(int i = 0; i < MAX_SIGNAL; i++) + *set |= (1 << i); + + return 0; +} + +/* sigaddset(): adds a signal to a set of signals + * params: set - set of signals + * params: signum - signal to add + * returns: zero on success + */ + +int sigaddset(sigset_t *set, int signum) { + if(signum <= 0 || signum > MAX_SIGNAL) + return -EINVAL; + + *set |= (1 << signum); + return 0; +} + +/* sigdelset(): removes a signal from a set of signals + * params: set - set of signals + * params: signum - signal to be removed + * returns: zero on success + */ + +int sigdelset(sigset_t *set, int signum) { + if(signum <= 0 || signum > MAX_SIGNAL) + return -EINVAL; + + *set &= ~(1 << signum); + return 0; +} + +/* sigismember(): tests if a signal is in a set of signals + * params: set - set of signals + * params: signum - signal to be checked + * returns: zero if absent, one if present, negative error code on fail + */ + +int sigismember(sigset_t *set, int signum) { + if(signum <= 0 || signum > MAX_SIGNAL) + return -EINVAL; + + if(*set & (1 << signum)) return 1; + else return 0; +} + +/* signalDefaults(): sets up the default signal handlers for a thread + * params: none + * returns: pointer to the signal handler array, NULL on fail + */ + +void *signalDefaults() { + struct sigaction *ptr = malloc((MAX_SIGNAL+1) * sizeof(struct sigaction)); + if(!ptr) return NULL; + + for(int i = 0; i < MAX_SIGNAL; i++) + ptr[i].sa_handler = SIG_DFL; // default + + return (void *) ptr; +} + +/* signalDefaultHandler(): returns the default handler for a signal + * params: signum - signal number + * returns: default handler macro, zero on fail + */ + +int signalDefaultHandler(int signum) { + switch(signum) { + case SIGABRT: + case SIGBUS: + case SIGFPE: + case SIGILL: + case SIGQUIT: + case SIGSEGV: + case SIGSYS: + case SIGTRAP: + case SIGXCPU: + case SIGXFSZ: + return SIG_A; + + case SIGALRM: + case SIGHUP: + case SIGINT: + case SIGKILL: + case SIGPIPE: + case SIGTERM: + case SIGUSR1: + case SIGUSR2: + case SIGPOLL: + case SIGVTALRM: + return SIG_T; + + case SIGCHLD: + case SIGURG: + return SIG_I; + + case SIGCONT: + return SIG_C; + + default: + return 0; + } +} + +/* signalClone(): clones the signal handlers of a thread + * params: h - template signal handlers, NULL to use defaults + * returns: pointer to the signal handler array, NULL on fail + */ + +void *signalClone(const void *h) { + if(!h) return signalDefaults(); + + void *new = malloc((MAX_SIGNAL+1) * sizeof(struct sigaction)); + if(!new) return NULL; + + return memcpy(new, h, (MAX_SIGNAL+1) * sizeof(struct sigaction)); +} + +/* kill(): sends a signal to a process or thread + * params: t - calling thread + * params: pid - pid of the process/thread to send a signal to + * pid > 0 refers to the process/thread whose PID/TID is exactly pid + * pid == 0 refers to processes whose process group ID is that of the sender + * pid == -1 refers to all processes that may receive the signal + * pid < -1 refers to the process group whose ID is the abs(pid) + * params: sig - signal to be sent, zero to test PID validitiy + * returns: zero on success, negative error code on fail + */ + +int kill(Thread *t, pid_t pid, int sig) { + if(sig < 0 || sig > MAX_SIGNAL) return -EINVAL; + + pid_t group; + Process *parent; + Thread *dest; + + if(pid == -1) return -EPERM; /* placeholder */ + + if(pid < -1) group = -1 * pid; + else if(!pid) group = t->pid; + else group = 0; + + if(group) { + parent = getProcess(group); + if(!parent) return -ESRCH; + } else { + dest = getThread(pid); + if(!dest) return -ESRCH; + } + + if(!sig) return 0; // null signal verifies that pid is valid + + if(group) { + // send the signal to all threads of the parent as well as all threads + // of all the children + if(!parent->threads || !parent->threadCount) return 0; + + for(int i = 0; i < parent->threadCount; i++) { + dest = parent->threads[i]; + if(dest) { + int status = kill(t, dest->tid, sig); + if(status) return status; + } + } + + if(!parent->children || !parent->childrenCount) return 0; + + Process *child; + for(int i = 0; i < parent->childrenCount; i++) { + child = parent->children[i]; + if(!child || !child->threads || !child->threadCount) + continue; + + for(int j = 0; j < child->threadCount; j++) { + dest = child->threads[j]; + if(dest) { + int status = kill(t, dest->tid, sig); + if(status) return status; + } + } + } + } else { + // send the signal to the exact thread specified by pid + SignalQueue *s = calloc(1, sizeof(SignalQueue)); + if(!s) return -ENOMEM; + + s->signum = sig; + s->sender = t; + s->next = NULL; + + acquireLockBlocking(&dest->lock); + + SignalQueue *q = dest->signalQueue; + if(!q) { + dest->signalQueue = s; + } else { + while(q->next) q = q->next; + q->next = s; + } + + releaseLock(&dest->lock); + } + + return 0; +} + +/* signalHandle(): checks the signal queue and invokes a signal handler + * params: t - thread to check + * returns: nothing + */ + +void signalHandle(Thread *t) { + if(t->handlingSignal || !t->signalQueue) return; + + acquireLockBlocking(&t->lock); + + // linked list structure + SignalQueue *s = t->signalQueue; + t->signalQueue = s->next; + + releaseLock(&t->lock); + + int signum = s->signum - 1; // change to zero-based + struct sigaction *handlers = (struct sigaction *) t->signals; + uintptr_t handler = (uintptr_t) handlers[signum].sa_handler; + Thread *sender = s->sender; + int def = 0; + + free(s); + + switch(handler) { + case (uintptr_t) SIG_IGN: + case (uintptr_t) SIG_HOLD: + return; + case (uintptr_t) SIG_DFL: + def = signalDefaultHandler(signum + 1); + break; + } + + switch(def) { + case SIG_I: + case SIG_C: + return; + case SIG_T: + case SIG_A: + case SIG_S: + terminateThread(t, -1, true); + break; + default: + t->handlingSignal = true; + platformSendSignal(sender, t, signum + 1, handler); + } +} + +/* sigaction(): manipulate a signal handler + * params: t - calling thread + * params: sig - signal number + * params: act - new signal handler, NULL to query the current signal handler + * params: oact - old signal handler, NULL if not requested + * returns: zero on success, negative errno on fail + */ + +int sigaction(Thread *t, int sig, const struct sigaction *act, struct sigaction *oact) { + if(sig <= 0 || sig > MAX_SIGNAL) return -EINVAL; + + struct sigaction *handlers = (struct sigaction *) t->signals; + + if(!act) { + // query signal handler + if(!oact) return 0; + memcpy(oact, &handlers[sig-1], sizeof(struct sigaction)); + return 0; + } + + uintptr_t handler = (uintptr_t) act->sa_handler; + if(handler != (uintptr_t) SIG_DFL && handler != (uintptr_t) SIG_IGN && + handler < USER_BASE_ADDRESS) + return -EINVAL; + + acquireLockBlocking(&t->lock); + + // save the old signal handler if necessary + if(oact) + memcpy(oact, &handlers[sig-1], sizeof(struct sigaction)); + + memcpy(&handlers[sig-1], act, sizeof(struct sigaction)); + releaseLock(&t->lock); + return 0; +} + +/* sigreturn(): returns from a signal handler and restores previous state + * params: t - calling thread + * returns: nothing + */ + +void sigreturn(Thread *t) { + if(!t->handlingSignal) return; + platformSigreturn(t); + t->handlingSignal = false; +} \ No newline at end of file diff --git a/src/libc/stdlib.c b/src/libc/stdlib.c index 951769e..3290762 100644 --- a/src/libc/stdlib.c +++ b/src/libc/stdlib.c @@ -122,14 +122,15 @@ void *malloc(size_t size) { return (void *)((uintptr_t)ptr + sizeof(struct mallocHeader)); } -void *mallocUC(size_t size) { - /* special case for malloc that uses uncacheable memory */ +void *umalloc(size_t size) { + /* this is the exact same as malloc() but allocates in the user space to + * be used for signal structures */ if(!size) return NULL; size_t pageSize = (size + sizeof(struct mallocHeader) + PAGE_SIZE - 1) / PAGE_SIZE; - + acquireLockBlocking(&lock); - uintptr_t ptr = vmmAllocate(KERNEL_HEAP_BASE, KERNEL_HEAP_LIMIT, pageSize, VMM_WRITE | VMM_NO_CACHE); + uintptr_t ptr = vmmAllocate(USER_HEAP_BASE, USER_HEAP_LIMIT, pageSize, VMM_WRITE | VMM_USER); if(!ptr) { releaseLock(&lock); return NULL; @@ -139,6 +140,37 @@ void *mallocUC(size_t size) { header->byteSize = size; header->pageSize = pageSize; + // allocate a guard page as well + uintptr_t guard = ptr + (pageSize*PAGE_SIZE); + platformMapPage(guard, 0, 0); + + releaseLock(&lock); + + return (void *)((uintptr_t)ptr + sizeof(struct mallocHeader)); +} + +void *uxmalloc(size_t size) { + /* again exactly the same umalloc() but with execute permissions, this will + * be used for installing platform-specific signal trampoline code */ + if(!size) return NULL; + size_t pageSize = (size + sizeof(struct mallocHeader) + PAGE_SIZE - 1) / PAGE_SIZE; + + acquireLockBlocking(&lock); + + uintptr_t ptr = vmmAllocate(USER_HEAP_BASE, USER_HEAP_LIMIT, pageSize, VMM_WRITE | VMM_USER | VMM_EXEC); + if(!ptr) { + releaseLock(&lock); + return NULL; + } + + struct mallocHeader *header = (struct mallocHeader *)ptr; + header->byteSize = size; + header->pageSize = pageSize; + + // allocate a guard page as well + uintptr_t guard = ptr + (pageSize*PAGE_SIZE); + platformMapPage(guard, 0, 0); + releaseLock(&lock); return (void *)((uintptr_t)ptr + sizeof(struct mallocHeader)); @@ -151,6 +183,13 @@ void *calloc(size_t num, size_t size) { return ptr; } +void *ucalloc(size_t num, size_t size) { + void *ptr = umalloc(num * size); + if(!ptr) return NULL; + memset(ptr, 0, num * size); + return ptr; +} + void *realloc(void *ptr, size_t newSize) { if(!newSize) return NULL; if(!ptr) return malloc(newSize); @@ -174,6 +213,29 @@ void *realloc(void *ptr, size_t newSize) { return newPtr; } +void *urealloc(void *ptr, size_t newSize) { + if(!newSize) return NULL; + if(!ptr) return umalloc(newSize); + + void *newPtr = umalloc(newSize); + if(!newPtr) return NULL; + + uintptr_t oldBase = (uintptr_t)ptr; + oldBase &= ~(PAGE_SIZE-1); + struct mallocHeader *header = (struct mallocHeader *)oldBase; + size_t oldSize = header->byteSize; + + if(oldSize > newSize) { + // we're shrinking the memory, copy the new size only + memcpy(newPtr, ptr, newSize); + } else { + memcpy(newPtr, ptr, oldSize); + } + + free(ptr); + return newPtr; +} + void free(void *ptr) { if(!ptr) return; uintptr_t base = (uintptr_t)ptr; diff --git a/src/platform/x86_64/include/platform/context.h b/src/platform/x86_64/include/platform/context.h index 5fae4a4..8f545c0 100644 --- a/src/platform/x86_64/include/platform/context.h +++ b/src/platform/x86_64/include/platform/context.h @@ -50,6 +50,7 @@ typedef struct { void *platformCreateContext(void *, int, uintptr_t, uintptr_t); int platformSetContext(Thread *, uintptr_t, uintptr_t, const char **, const char **); +int platformSignalSetup(Thread *); #define PLATFORM_CONTEXT_SIZE sizeof(ThreadContext) diff --git a/src/platform/x86_64/include/platform/mmap.h b/src/platform/x86_64/include/platform/mmap.h index e1c6ea4..d19e984 100644 --- a/src/platform/x86_64/include/platform/mmap.h +++ b/src/platform/x86_64/include/platform/mmap.h @@ -19,5 +19,7 @@ #define KERNEL_HEAP_LIMIT (uintptr_t)0xFFFF8FFFFFFFFFFF #define KERNEL_MMIO_LIMIT ((uint64_t)KERNEL_BASE_MAPPED << 30) #define USER_BASE_ADDRESS 0x400000 // 4 MB, user programs will be loaded here +#define USER_HEAP_BASE (uintptr_t)0x00006FFF80000000 // for signal structures +#define USER_HEAP_LIMIT (uintptr_t)0x00006FFFFFFFFFFF // 2 GB of space #define USER_MMIO_BASE (uintptr_t)0x0000700000000000 // for mmap() and similar syscalls #define USER_LIMIT_ADDRESS (KERNEL_BASE_ADDRESS-1) // maximum limit for the lower half diff --git a/src/platform/x86_64/ipc/signal.c b/src/platform/x86_64/ipc/signal.c new file mode 100644 index 0000000..1b6e697 --- /dev/null +++ b/src/platform/x86_64/ipc/signal.c @@ -0,0 +1,108 @@ +/* + * lux - a lightweight unix-like operating system + * Omar Elghoul, 2024 + * + * Platform-Specific Code for x86_64 + */ + +#include +#include +#include +#include +#include +#include + +extern size_t sigstubSize; +extern uint8_t sigstub[]; + +/* platformSignalSetup(): sets up the signal trampoline code in a user process + * params: t - thread structure + * returns: zero on success + */ + +int platformSignalSetup(Thread *t) { + void *trampoline = uxmalloc(sigstubSize); + if(!trampoline) return -1; + + void *siginfo = umalloc(sizeof(siginfo_t)); + if(!siginfo) { + free(trampoline); + return -1; + } + + void *sigctx = umalloc(PLATFORM_CONTEXT_SIZE); + if(!sigctx) { + free(trampoline); + free(siginfo); + return -1; + } + + memcpy(trampoline, sigstub, sigstubSize); + t->signalTrampoline = (uintptr_t) trampoline; + t->siginfo = (uintptr_t) siginfo; + t->signalUserContext = (uintptr_t) sigctx; + return 0; +} + +/* platformSendSignal(): dispatches a signal to a thread + * params: sender - thread sending the signal + * params: dest - thread receiving the signal + * params: signum - signal number + * params: handler - function to dispatch + * returns: zero on success + */ + +int platformSendSignal(Thread *sender, Thread *dest, int signum, uintptr_t handler) { + memcpy(dest->signalContext, dest->context, PLATFORM_CONTEXT_SIZE); + + ThreadContext *ctx = (ThreadContext *) dest->context; + platformUseContext(ctx); + + Process *p = NULL; + if(sender) p = getProcess(sender->pid); + + siginfo_t *siginfo = (siginfo_t *) dest->siginfo; + siginfo->si_signo = signum; + if(sender) siginfo->si_pid = sender->tid; + else siginfo->si_pid = 0; // we will use pid 0 for the kernel + + if(p) siginfo->si_uid = p->user; + else siginfo->si_uid = 0; + + siginfo->si_code = 0; // TODO + + ThreadContext *uctx = (ThreadContext *) dest->signalUserContext; + memcpy(uctx, dest->context, PLATFORM_CONTEXT_SIZE); + + // signal entry point + // func(int sig, siginfo_t *info, void *ctx) + // https://pubs.opengroup.org/onlinepubs/007904875/functions/sigaction.html + + ctx->regs.rip = handler; + ctx->regs.rdi = signum; // int sig + ctx->regs.rsi = (uint64_t) siginfo; // siginfo_t *info + ctx->regs.rdx = (uint64_t) uctx; // void *ctx + + ctx->regs.rflags = 0x202; + ctx->regs.rsp -= 128; // downwards of the red zone + + // ensure stack is 16-byte aligned on entry + while(ctx->regs.rsp & 0x0F) + ctx->regs.rsp--; + ctx->regs.rbp = ctx->regs.rsp; + + // inject signal trampoline + uint64_t *stack = (uint64_t *) ctx->regs.rsp; + *stack = dest->signalTrampoline; + + return 0; +} + +/* platformSigreturn(): restores context before a signal handler was invoked + * params: t - thread to restore + * returns: nothing + */ + +void platformSigreturn(Thread *t) { + memcpy(t->context, t->signalContext, PLATFORM_CONTEXT_SIZE); +} \ No newline at end of file diff --git a/src/platform/x86_64/ipc/sigstub.asm b/src/platform/x86_64/ipc/sigstub.asm new file mode 100644 index 0000000..4449bca --- /dev/null +++ b/src/platform/x86_64/ipc/sigstub.asm @@ -0,0 +1,25 @@ + +; lux - a lightweight unix-like operating system +; Omar Elghoul, 2024 + +[bits 64] + +; Signal Trampoline Code +; this simply invokes the sigreturn() system call to allow the kernel to +; restore the state of the thread before the signal was raised + +SYSCALL_SIGRETURN equ 49 + +section .data + +global sigstub +align 16 +sigstub: + mov rax, SYSCALL_SIGRETURN + syscall + +sigstubEnd: + +global sigstubSize +align 16 +sigstubSize: dq sigstubEnd - sigstub diff --git a/src/platform/x86_64/sched/context.c b/src/platform/x86_64/sched/context.c index 35b7e1e..0cc5ff0 100644 --- a/src/platform/x86_64/sched/context.c +++ b/src/platform/x86_64/sched/context.c @@ -135,6 +135,8 @@ int platformSetContext(Thread *t, uintptr_t entry, uintptr_t highest, const char /* this sets up an entry point for the thread that's something like * void _start(const char **argv, const char **envp) */ + if(platformSignalSetup(t)) return -1; + ThreadContext *ctx = (ThreadContext *)t->context; ctx->regs.rip = entry; ctx->regs.rdi = (uint64_t)argv; diff --git a/src/platform/x86_64/string.asm b/src/platform/x86_64/string.asm index 8d31693..81786a3 100644 --- a/src/platform/x86_64/string.asm +++ b/src/platform/x86_64/string.asm @@ -68,7 +68,7 @@ memset: mov ax, si ; 32 bits mov esi, eax shr rax, 32 - mov eax, esi ; full 64 bits + or rax, rsi ; full 64 bits mov rcx, rdx shr rcx, 3 ; div 8 diff --git a/src/sched/exec.c b/src/sched/exec.c index 54b7b92..45c1593 100644 --- a/src/sched/exec.c +++ b/src/sched/exec.c @@ -14,6 +14,7 @@ #include #include #include +#include int execmve(Thread *, void *, const char **, const char **); @@ -323,6 +324,9 @@ int execmve(Thread *t, void *image, const char **argv, const char **envp) { } } + // set up default signal handlers + t->signals = signalDefaults(); + // TODO: here we've successfully loaded the new program, but we also need // to free up memory used by the original program platformCleanThread(oldctx, oldHighest); diff --git a/src/sched/fork.c b/src/sched/fork.c index ceb6877..1afc7eb 100644 --- a/src/sched/fork.c +++ b/src/sched/fork.c @@ -11,6 +11,7 @@ #include #include #include +#include /* fork(): forks the running thread * params: t - pointer to thread structure @@ -52,6 +53,7 @@ pid_t fork(Thread *t) { p->threads[0]->pid = pid; p->threads[0]->tid = pid; p->threads[0]->context = calloc(1, PLATFORM_CONTEXT_SIZE); + p->threads[0]->signalContext = calloc(1, PLATFORM_CONTEXT_SIZE); p->threads[0]->highest = t->highest; p->threads[0]->pages = t->pages; @@ -59,7 +61,7 @@ pid_t fork(Thread *t) { // entire process memory, but just the calling thread p->pages = t->pages; - if(!p->threads[0]->context) { + if(!p->threads[0]->context || !p->threads[0]->signalContext) { free(p->threads[0]); free(p->threads); free(p); @@ -77,6 +79,9 @@ pid_t fork(Thread *t) { return -1; } + // clone signal handlers + p->threads[0]->signals = signalClone(t->signals); + // clone I/O descriptors Process *parent = getProcess(t->pid); if(parent) { diff --git a/src/sched/sched.c b/src/sched/sched.c index 8cdefd7..2025718 100644 --- a/src/sched/sched.c +++ b/src/sched/sched.c @@ -12,6 +12,7 @@ #include #include #include +#include #include static bool scheduling = false; @@ -353,11 +354,16 @@ void schedule() { if(current && (current->status == THREAD_RUNNING)) current->status = THREAD_QUEUED; - t->status = THREAD_RUNNING; - t->time = schedTimeslice(t, t->priority); - t->cpu = cpu; - releaseLock(&lock); - platformSwitchContext(t); + signalHandle(t); + + if(t->status == THREAD_QUEUED) { + // check status again because the signal handler may terminate a thread + t->status = THREAD_RUNNING; + t->time = schedTimeslice(t, t->priority); + t->cpu = cpu; + releaseLock(&lock); + platformSwitchContext(t); + } } t = t->next; @@ -513,12 +519,8 @@ void unblockThread(Thread *t) { */ int yield(Thread *t) { - acquireLockBlocking(&lock); - t->status = THREAD_QUEUED; t->time = schedTimeslice(t, t->priority); - - releaseLock(&lock); return 0; } diff --git a/src/syscalls/dispatch.c b/src/syscalls/dispatch.c index 0d94a27..cfe1bf8 100644 --- a/src/syscalls/dispatch.c +++ b/src/syscalls/dispatch.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -478,6 +479,24 @@ void syscallDispatchSend(SyscallRequest *req) { } } +void syscallDispatchKill(SyscallRequest *req) { + req->ret = kill(req->thread, req->params[0], req->params[1]); + req->unblock = true; +} + +void syscallDispatchSigAction(SyscallRequest *req) { + if((!req->params[1] || syscallVerifyPointer(req, req->params[1], sizeof(struct sigaction))) && + (!req->params[2] || syscallVerifyPointer(req, req->params[2], sizeof(struct sigaction)))) { + req->ret = sigaction(req->thread, req->params[0], (const struct sigaction *) req->params[1], (struct sigaction *) req->params[2]); + req->unblock = true; + } +} + +void syscallDispatchSigreturn(SyscallRequest *req) { + sigreturn(req->thread); + req->unblock = true; +} + /* Group 4: Memory Management */ void syscallDispatchSBrk(SyscallRequest *req) { @@ -583,18 +602,20 @@ void (*syscallDispatchTable[])(SyscallRequest *) = { syscallDispatchAccept, // 44 - accept() syscallDispatchRecv, // 45 - recv() syscallDispatchSend, // 46 - send() - NULL, // 47 - kill() + syscallDispatchKill, // 47 - kill() + syscallDispatchSigAction, // 48 - sigaction() + syscallDispatchSigreturn, // 49 - sigreturn() /* group 4: memory management */ - syscallDispatchSBrk, // 48 - sbrk() - NULL, // 49 - mmap() - NULL, // 50 - munmap() + syscallDispatchSBrk, // 50 - sbrk() + NULL, // 51 - mmap() + NULL, // 52 - munmap() /* group 5: driver I/O functions */ - syscallDispatchIoperm, // 51 - ioperm() - syscallDispatchIRQ, // 52 - irq() - syscallDispatchIoctl, // 53 - ioctl() - syscallDispatchMMIO, // 54 - mmio() - syscallDispatchPContig, // 55 - pcontig() - syscallDispatchVToP, // 56 - vtop() + syscallDispatchIoperm, // 53 - ioperm() + syscallDispatchIRQ, // 54 - irq() + syscallDispatchIoctl, // 55 - ioctl() + syscallDispatchMMIO, // 56 - mmio() + syscallDispatchPContig, // 57 - pcontig() + syscallDispatchVToP, // 58 - vtop() }; diff --git a/src/syscalls/queue.c b/src/syscalls/queue.c index be13683..4a7df54 100644 --- a/src/syscalls/queue.c +++ b/src/syscalls/queue.c @@ -9,6 +9,7 @@ #include #include #include +#include #include static SyscallRequest *requests = NULL; // sort of a linked list in a sense @@ -28,7 +29,8 @@ void syscallHandle(void *ctx) { // allow immediate handling of IPC syscalls without going through the // syscall queue for performance if((req->function >= SYSCALL_IPC_START && req->function <= SYSCALL_IPC_END) || - (req->function >= SYSCALL_RW_START && req->function <= SYSCALL_RW_END)) { + (req->function >= SYSCALL_RW_START && req->function <= SYSCALL_RW_END) || + (req->function == SYSCALL_ACCEPT)) { setLocalSched(false); syscallDispatchTable[req->function](req); @@ -105,6 +107,7 @@ int syscallProcess() { if(!requests) return 0; SyscallRequest *syscall = syscallDequeue(); if(!syscall) return 0; + if(syscall->thread->status != THREAD_BLOCKED) return 0; setLocalSched(false); @@ -116,10 +119,17 @@ int syscallProcess() { terminateThread(syscall->thread, -1, false); schedRelease(); } else { - threadUseContext(syscall->thread->tid); - syscallDispatchTable[syscall->function](syscall); - platformSetContextStatus(syscall->thread->context, syscall->ret); - //threadUseContext(getTid()); + signalHandle(syscall->thread); + if(syscall->thread->status == THREAD_ZOMBIE) { + setLocalSched(true); + return 1; + } else if(syscall->thread->status == THREAD_QUEUED) { + syscallEnqueue(syscall); + } else if(syscall->thread->status == THREAD_BLOCKED) { + threadUseContext(syscall->thread->tid); + syscallDispatchTable[syscall->function](syscall); + platformSetContextStatus(syscall->thread->context, syscall->ret); + } } if((syscall->thread->status == THREAD_BLOCKED) && syscall->unblock) {