summaryrefslogtreecommitdiff
path: root/src/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'src/kernel')
-rw-r--r--src/kernel/malloc.c1
-rw-r--r--src/kernel/malloc.h1
-rw-r--r--src/kernel/proc.c73
-rw-r--r--src/kernel/proc.h10
-rw-r--r--src/kernel/types.h1
5 files changed, 71 insertions, 15 deletions
diff --git a/src/kernel/malloc.c b/src/kernel/malloc.c
index 9f87274..2fe4792 100644
--- a/src/kernel/malloc.c
+++ b/src/kernel/malloc.c
@@ -27,6 +27,7 @@ static const char *tagnames[] = {
"DevTime", /* TagDevTime */
"RootCache", /* TagRootCache */
"PageRefcount", /* TagPageRefcount */
+ "Intr", /* TagIntr */
};
static_assert(sizeof(tagnames) == sizeof(const char *) * TagLast);
diff --git a/src/kernel/malloc.h b/src/kernel/malloc.h
index a38d49b..af033f5 100644
--- a/src/kernel/malloc.h
+++ b/src/kernel/malloc.h
@@ -29,6 +29,7 @@ enum MallocTag {
TagDevTime,
TagRootCache,
TagPageRefcount,
+ TagIntr,
TagLast,
};
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);
diff --git a/src/kernel/proc.h b/src/kernel/proc.h
index 99eac94..96cd8ba 100644
--- a/src/kernel/proc.h
+++ b/src/kernel/proc.h
@@ -88,6 +88,7 @@ struct Proc {
/* interrupt handler */
void __user *intr_fn;
+ Intr *queuedintr;
struct {
void *buf;
@@ -99,6 +100,12 @@ struct Proc {
uint64_t basetime;
};
+/* an interrupt queued for delivery */
+struct Intr {
+ size_t len;
+ char buf[];
+};
+
extern Proc *proc_cur;
/** Creates the root process. */
@@ -116,9 +123,6 @@ void proc_kill(Proc *proc, int ret);
/** Tries to reap a dead process / free a tombstone. */
void proc_tryreap(Proc *dead);
-/** Try to interrupt whatever the process is doing instead of PS_RUNNING. */
-void proc_tryintr(Proc *p);
-
/** Send an interupt to a process. */
void proc_intr(Proc *p, const char *buf, size_t len);
diff --git a/src/kernel/types.h b/src/kernel/types.h
index b8308a5..0286763 100644
--- a/src/kernel/types.h
+++ b/src/kernel/types.h
@@ -11,6 +11,7 @@ FORWARD_STRUCT(VfsBackend)
FORWARD_STRUCT(VfsMount)
FORWARD_STRUCT(VfsReq)
FORWARD_STRUCT(ReqQueue)
+FORWARD_STRUCT(Intr)
/* arch-specific stuff */
FORWARD_STRUCT(GfxInfo)