summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/cmd/init/driver/termcook.c2
-rw-r--r--src/cmd/init/init.c7
-rw-r--r--src/cmd/shell/shell.c6
-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
-rw-r--r--src/libc/_start2.c2
-rw-r--r--src/libc/include/camellia/intr.h5
-rw-r--r--src/libc/intr.c9
-rw-r--r--src/libc/intr.s13
-rw-r--r--src/libk/include/camellia/types.h13
13 files changed, 104 insertions, 39 deletions
diff --git a/src/cmd/init/driver/termcook.c b/src/cmd/init/driver/termcook.c
index e0edcf5..ed851e3 100644
--- a/src/cmd/init/driver/termcook.c
+++ b/src/cmd/init/driver/termcook.c
@@ -77,7 +77,7 @@ static void line_editor(void *) {
}
break;
case 3: /* C-c */
- send_intr("");
+ send_intr("C-c");
break;
case 0x1c: /* C-\ */
send_intr("kill");
diff --git a/src/cmd/init/init.c b/src/cmd/init/init.c
index 0a64054..9caa283 100644
--- a/src/cmd/init/init.c
+++ b/src/cmd/init/init.c
@@ -68,9 +68,10 @@ void redirect(const char *exe, const char *out, const char *in) {
}
}
-void shutdown(void) {
- printf("[init] intr\n");
- _sys_intr(NULL, 0);
+void shutdown(struct intr_data *data) {
+ const char *msg = "impending shutdown";
+ printf("[init] shutdown with message: %s\n", data->msg);
+ _sys_intr(msg, strlen(msg));
_sys_sleep(1000);
printf("[init] filicide\n");
_sys_filicide();
diff --git a/src/cmd/shell/shell.c b/src/cmd/shell/shell.c
index 9afdb5c..15cb6ed 100644
--- a/src/cmd/shell/shell.c
+++ b/src/cmd/shell/shell.c
@@ -139,10 +139,14 @@ static void run(char *cmd) {
}
}
+static void intr(struct intr_data *intr) {
+ fprintf(stderr, "%s: got interrupt: %s\n", getprogname(), intr->msg);
+}
+
int main(int argc, char **argv) {
static char buf[256];
FILE *f = stdin;
- intr_set(NULL);
+ intr_set(intr);
if (argc > 1) {
f = fopen(argv[1], "r");
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)
diff --git a/src/libc/_start2.c b/src/libc/_start2.c
index 2c6b64d..e6809b9 100644
--- a/src/libc/_start2.c
+++ b/src/libc/_start2.c
@@ -35,8 +35,8 @@ _Noreturn void _start2(struct execdata *ed) {
_klogf("_start2 %s %p", progname, __executable_start);
- _sys_intr_set(intr_trampoline);
intr_set(intr_default);
+ _sys_intr_set(intr_trampoline); /* can cause queued interrupts to arrive */
__initialcwd = ed->cwd;
exit(main(ed->argc, ed->argv, ed->envp));
diff --git a/src/libc/include/camellia/intr.h b/src/libc/include/camellia/intr.h
index b9390fd..c6d0310 100644
--- a/src/libc/include/camellia/intr.h
+++ b/src/libc/include/camellia/intr.h
@@ -1,4 +1,5 @@
#pragma once
+#include <camellia/types.h>
-void intr_set(void (*fn)(void));
-void intr_default(void);
+void intr_set(void (*fn)(struct intr_data *));
+void intr_default(struct intr_data *);
diff --git a/src/libc/intr.c b/src/libc/intr.c
index cdefc2f..c44a90e 100644
--- a/src/libc/intr.c
+++ b/src/libc/intr.c
@@ -1,13 +1,14 @@
#include <camellia/intr.h>
+#include <stdio.h>
#include <stdlib.h>
-static void intr_null(void) { }
+static void intr_null(struct intr_data *) { }
-extern void (*volatile _intr)(void);
-void intr_set(void (*fn)(void)) {
+extern void (*volatile _intr)(struct intr_data *);
+void intr_set(void (*fn)(struct intr_data *)) {
_intr = fn ? fn : intr_null;
}
-void intr_default(void) {
+void intr_default(struct intr_data *intr) {
exit(-1);
}
diff --git a/src/libc/intr.s b/src/libc/intr.s
index 008387d..dcc61fe 100644
--- a/src/libc/intr.s
+++ b/src/libc/intr.s
@@ -2,19 +2,12 @@
.global intr_trampoline
.type intr_trampoline, @function
intr_trampoline:
- push %rax
- push %rdx
+ mov %rsp, %rdi /* pass intr_data as the argument */
+ and $~0xF, %rsp
call *_intr(%rip)
- pop %rdx
- pop %rax
- pop tmprip(%rip)
- pop %rsp
- jmp *tmprip(%rip)
+ int3 /* you weren't supposed to return */
.section .bss
-tmprip:
- .skip 8
-
.global _intr
_intr:
.skip 8
diff --git a/src/libk/include/camellia/types.h b/src/libk/include/camellia/types.h
index 3117877..9cbd386 100644
--- a/src/libk/include/camellia/types.h
+++ b/src/libk/include/camellia/types.h
@@ -33,8 +33,17 @@ struct ufs_request {
};
struct intr_data {
- void __user *ip;
- void __user *sp; /* last for pop %rsp */
+ /* same order as CpuRegs.
+ * notable omissions:
+ * - SSE registers (usually there's no need to save them, userland can do
+ * that itself if it wants)
+ * - FLAGS (so the kernel doesn't need to care about sanitizing it) */
+ uint64_t r15, r14, r13, r12, r11, r10, r9, r8;
+ uint64_t rdi, rsi;
+ userptr_t rbp, rsp;
+ uint64_t rbx, rdx, rcx, rax, rip;
+
+ char msg[]; /* variable size, NUL terminated */
};
struct sys_wait2 {