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 /src/kernel/proc.c | |
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).
Diffstat (limited to 'src/kernel/proc.c')
-rw-r--r-- | src/kernel/proc.c | 73 |
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); |