diff options
author | dzwdz | 2024-07-25 22:17:27 +0200 |
---|---|---|
committer | dzwdz | 2024-07-25 22:17:27 +0200 |
commit | 69fd0dd9fda47aa52cccdbef6ca388cea38e693b (patch) | |
tree | 9e0e80e0380f2f39dea8f3a76ecb629918ff187a | |
parent | 24934406d5d39e013e22a9e6f4138c4169460d71 (diff) |
kernel: pass more information to user on interrupt
This is meant to facilitate a syscall for returning from interrupts, which
will actually work in the general case as opposed to the current hack, which
only works if the interrupt occured during a syscall (which is correct... for
now).
-rw-r--r-- | src/cmd/init/driver/termcook.c | 2 | ||||
-rw-r--r-- | src/cmd/init/init.c | 7 | ||||
-rw-r--r-- | src/cmd/shell/shell.c | 6 | ||||
-rw-r--r-- | src/kernel/malloc.c | 1 | ||||
-rw-r--r-- | src/kernel/malloc.h | 1 | ||||
-rw-r--r-- | src/kernel/proc.c | 73 | ||||
-rw-r--r-- | src/kernel/proc.h | 10 | ||||
-rw-r--r-- | src/kernel/types.h | 1 | ||||
-rw-r--r-- | src/libc/_start2.c | 2 | ||||
-rw-r--r-- | src/libc/include/camellia/intr.h | 5 | ||||
-rw-r--r-- | src/libc/intr.c | 9 | ||||
-rw-r--r-- | src/libc/intr.s | 13 | ||||
-rw-r--r-- | src/libk/include/camellia/types.h | 13 | ||||
-rw-r--r-- | sysroot/bin/sh/halt | 2 |
14 files changed, 105 insertions, 40 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 { diff --git a/sysroot/bin/sh/halt b/sysroot/bin/sh/halt index 7304155..d02109f 100644 --- a/sysroot/bin/sh/halt +++ b/sysroot/bin/sh/halt @@ -1,2 +1,2 @@ #!/bin/shell -echo "Die monster. You don't belong in this world!" > /proc/1/intr +echo -n "Die monster. You don't belong in this world!" > /proc/1/intr |