summaryrefslogtreecommitdiff
path: root/src/kernel/proc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/kernel/proc.c')
-rw-r--r--src/kernel/proc.c73
1 files changed, 61 insertions, 12 deletions
diff --git a/src/kernel/proc.c b/src/kernel/proc.c
index a22f632..350f3ee 100644
--- a/src/kernel/proc.c
+++ b/src/kernel/proc.c
@@ -259,6 +259,11 @@ void proc_kill(Proc *p, int ret) {
it = sibling;
}
+ if (p->queuedintr) {
+ kfree(p->queuedintr);
+ p->queuedintr = NULL;
+ }
+
if (p->execbuf.buf) {
kfree(p->execbuf.buf);
p->execbuf.buf = NULL;
@@ -352,14 +357,12 @@ void proc_tryreap(Proc *dead) {
}
}
-void proc_tryintr(Proc *p) {
- if (p->state == PS_WAITS4TIMER) {
- timer_deschedule(p);
- }
-}
-
void proc_intr(Proc *p, const char *buf, size_t len) {
- assert(buf != NULL || len == 0);
+ /* ignore attempts to interrupt with no string. they sometimes occur due
+ * to e.g. fopen trying to truncate the /intr file */
+ // TODO check callsites
+ if (len == 0) return;
+ assert(buf != NULL);
if (4 <= len && memcmp(buf, "kill", 4) == 0) {
proc_kill(p, EINTR);
@@ -367,18 +370,63 @@ void proc_intr(Proc *p, const char *buf, size_t len) {
}
if (!p->intr_fn) return;
- proc_tryintr(p);
+ if (p->state == PS_WAITS4TIMER) {
+ timer_deschedule(p);
+ }
- /* save old rsp,rip */
+ if (p->queuedintr != NULL) {
+ // TODO multiple queued interrupts. should be easy
+ panic_unimplemented();
+ }
+ p->queuedintr = kmalloc(sizeof(Intr) + len, TagIntr);
+ p->queuedintr->len = len;
+ memcpy(p->queuedintr->buf, buf, len);
+}
+
+static void proc_intr_deliver(Proc *p) {
+ assert(p->state == PS_RUNNING);
+ Intr *intr = p->queuedintr;
+
+ /* interrupts are held off until a handler is registered */
+ if (intr == NULL || p->intr_fn == NULL) {
+ return;
+ }
+
+ /* save old registers */
+ // TODO separate types for normal registers and sse?
+ // then i could reuse the normal register type here
struct intr_data d;
- void __user *sp = p->regs.rsp - 128 - sizeof(d);
- d.ip = (void __user *)p->regs.rip;
- d.sp = p->regs.rsp;
+ d.r15 = p->regs.r15;
+ d.r14 = p->regs.r14;
+ d.r13 = p->regs.r13;
+ d.r12 = p->regs.r12;
+ d.r11 = p->regs.r11;
+ d.r10 = p->regs.r10;
+ d.r9 = p->regs.r9;
+ d.r8 = p->regs.r8;
+ d.rdi = p->regs.rdi;
+ d.rsi = p->regs.rsi;
+ d.rbp = p->regs.rbp;
+ d.rsp = p->regs.rsp;
+ d.rbx = p->regs.rbx;
+ d.rdx = p->regs.rdx;
+ d.rcx = p->regs.rcx;
+ d.rax = p->regs.rax;
+ d.rip = p->regs.rip;
+
+ // one more because the buffer itself isn't NUL terminated
+ // TODO ensure NUL termination
+ void __user *sp = p->regs.rsp - 128 - sizeof(d) - intr->len - 1;
pcpy_to(p, sp, &d, sizeof(d));
+ pcpy_to(p, sp + sizeof(d), intr->buf, intr->len);
+ pcpy_to(p, sp + sizeof(d) + intr->len, "", 1);
/* switch to intr handler */
p->regs.rip = (uint64_t)p->intr_fn;
p->regs.rsp = sp;
+
+ kfree(p->queuedintr);
+ p->queuedintr = NULL;
}
static void proc_prune_leaves(void) {
@@ -432,6 +480,7 @@ _Noreturn void proc_switch_any(void) {
if (p) {
assert(p->state == PS_RUNNING);
proc_cur = p;
+ proc_intr_deliver(p);
pagedir_switch(p->pages);
if (p->execbuf.buf) {
execbuf_run(p);