summaryrefslogtreecommitdiff
path: root/src/kernel
diff options
context:
space:
mode:
authordzwdz2023-01-19 23:36:11 +0100
committerdzwdz2023-01-19 23:36:11 +0100
commita2f9ae9d4ab678fa66a2ec5d1072ea22a36a18a1 (patch)
treec048e9165b27d27075d2c17ab943ac52b46a6a40 /src/kernel
parentda546a0822b1995efe1832c9cc57aab62ccdcf65 (diff)
kernel: user interrupts
Diffstat (limited to 'src/kernel')
-rwxr-xr-xsrc/kernel/proc.c27
-rw-r--r--src/kernel/proc.h5
-rw-r--r--src/kernel/syscalls.c12
3 files changed, 43 insertions, 1 deletions
diff --git a/src/kernel/proc.c b/src/kernel/proc.c
index d105f66..d8c7e57 100755
--- a/src/kernel/proc.c
+++ b/src/kernel/proc.c
@@ -3,6 +3,7 @@
#include <kernel/arch/generic.h>
#include <kernel/execbuf.h>
#include <kernel/mem/alloc.h>
+#include <kernel/mem/virt.h>
#include <kernel/panic.h>
#include <kernel/proc.h>
#include <kernel/vfs/mount.h>
@@ -65,6 +66,7 @@ struct process *process_fork(struct process *parent, int flags) {
child->regs = parent->regs;
child->state = parent->state;
assert(child->state == PS_RUNNING); // not copying the state union
+ child->intr_fn = parent->intr_fn;
child->noreap = (flags & FORK_NOREAP) > 0;
@@ -188,12 +190,15 @@ void process_kill(struct process *p, int ret) {
}
if (p->state == PS_DYING) {
- if (p->parent && proc_alive(p->parent) && !p->noreap) {
+ if (p->parent && proc_alive(p->parent)) {
process_transition(p, PS_TOREAP);
} else {
process_transition(p, PS_TOMBSTONE);
}
}
+ if (p == process_first) {
+ assert(!p->child);
+ }
process_tryreap(p);
if (p == process_first) {
@@ -271,6 +276,21 @@ void process_tryreap(struct process *dead) {
}
}
+void process_intr(struct process *p) {
+ if (!p->intr_fn) return;
+
+ /* save old rsp,rip */
+ struct intr_data d;
+ void __user *sp = p->regs.rsp - 128 - sizeof(d);
+ d.ip = (void __user *)p->regs.rcx;
+ d.sp = p->regs.rsp;
+ virt_cpy_to(p->pages, sp, &d, sizeof(d));
+
+ /* switch to intr handler */
+ p->regs.rcx = (uint64_t)p->intr_fn;
+ p->regs.rsp = sp;
+}
+
/** Removes a process from the process tree. */
static void process_forget(struct process *p) {
assert(p->parent);
@@ -447,6 +467,11 @@ void process_transition(struct process *p, enum process_state state) {
assert(p->state == PS_TOREAP || p->state == PS_DYING);
} else if (state == PS_TOREAP) {
assert(p->state == PS_DYING);
+
+ assert(!p->parent || proc_alive(p->parent));
+ for (struct process *it = p->child; it; it = it->sibling) {
+ assert(p->state != PS_TOREAP);
+ }
} else if (state != PS_RUNNING && state != PS_DYING) {
assert(p->state == PS_RUNNING);
}
diff --git a/src/kernel/proc.h b/src/kernel/proc.h
index b95e901..041dbd0 100644
--- a/src/kernel/proc.h
+++ b/src/kernel/proc.h
@@ -81,6 +81,9 @@ struct process {
/* vfs_backend controlled (not exclusively) by this process */
struct vfs_backend *controlled;
+ /* interrupt handler */
+ void __user *intr_fn;
+
struct {
void *buf;
size_t len;
@@ -100,6 +103,8 @@ void process_filicide(struct process *proc, int ret);
/** Tries to reap a dead process / free a tombstone. */
void process_tryreap(struct process *dead);
+void process_intr(struct process *proc);
+
/** Switches execution to any running process. */
_Noreturn void process_switch_any(void);
diff --git a/src/kernel/syscalls.c b/src/kernel/syscalls.c
index 3ebda61..8327b68 100644
--- a/src/kernel/syscalls.c
+++ b/src/kernel/syscalls.c
@@ -378,6 +378,16 @@ void _syscall_filicide(void) {
process_filicide(process_current, -1);
}
+void _syscall_intr(void) {
+ for (struct process *p = process_current->child; p; p = process_next(p, process_current)) {
+ process_intr(p);
+ }
+}
+
+void _syscall_intr_set(void __user *ip) {
+ process_current->intr_fn = ip;
+}
+
long _syscall_execbuf(void __user *ubuf, size_t len) {
if (len == 0) SYSCALL_RETURN(0);
if (len > EXECBUF_MAX_LEN)
@@ -428,6 +438,8 @@ long _syscall(long num, long a, long b, long c, long d, long e) {
break; case _SYSCALL_PIPE: _syscall_pipe((userptr_t)a, b);
break; case _SYSCALL_SLEEP: _syscall_sleep(a);
break; case _SYSCALL_FILICIDE: _syscall_filicide();
+ break; case _SYSCALL_INTR: _syscall_intr();
+ break; case _SYSCALL_INTR_SET: _syscall_intr_set((userptr_t)a);
break; case _SYSCALL_EXECBUF: _syscall_execbuf((userptr_t)a, b);
break; case _SYSCALL_DEBUG_KLOG: _syscall_debug_klog((userptr_t)a, b);
break;