summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xsrc/kernel/proc.c27
-rw-r--r--src/kernel/proc.h5
-rw-r--r--src/kernel/syscalls.c12
-rw-r--r--src/shared/include/camellia/syscalls.h4
-rw-r--r--src/shared/include/camellia/types.h5
-rw-r--r--src/user/app/ext2fs/main.c3
-rw-r--r--src/user/app/init/driver/initctl.c4
-rw-r--r--src/user/app/init/init.c22
-rw-r--r--src/user/app/shell/shell.c1
-rw-r--r--src/user/lib/_start2.c4
-rw-r--r--src/user/lib/include/unistd.h3
-rw-r--r--src/user/lib/intr.s20
-rw-r--r--src/user/lib/syscall.c8
-rw-r--r--src/user/lib/unistd.c11
14 files changed, 123 insertions, 6 deletions
diff --git a/src/kernel/proc.c b/src/kernel/proc.c
index d105f66..d8c7e57 100755
--- a/src/kernel/proc.c
+++ b/src/kernel/proc.c
@@ -3,6 +3,7 @@
#include <kernel/arch/generic.h>
#include <kernel/execbuf.h>
#include <kernel/mem/alloc.h>
+#include <kernel/mem/virt.h>
#include <kernel/panic.h>
#include <kernel/proc.h>
#include <kernel/vfs/mount.h>
@@ -65,6 +66,7 @@ struct process *process_fork(struct process *parent, int flags) {
child->regs = parent->regs;
child->state = parent->state;
assert(child->state == PS_RUNNING); // not copying the state union
+ child->intr_fn = parent->intr_fn;
child->noreap = (flags & FORK_NOREAP) > 0;
@@ -188,12 +190,15 @@ void process_kill(struct process *p, int ret) {
}
if (p->state == PS_DYING) {
- if (p->parent && proc_alive(p->parent) && !p->noreap) {
+ if (p->parent && proc_alive(p->parent)) {
process_transition(p, PS_TOREAP);
} else {
process_transition(p, PS_TOMBSTONE);
}
}
+ if (p == process_first) {
+ assert(!p->child);
+ }
process_tryreap(p);
if (p == process_first) {
@@ -271,6 +276,21 @@ void process_tryreap(struct process *dead) {
}
}
+void process_intr(struct process *p) {
+ if (!p->intr_fn) return;
+
+ /* save old rsp,rip */
+ struct intr_data d;
+ void __user *sp = p->regs.rsp - 128 - sizeof(d);
+ d.ip = (void __user *)p->regs.rcx;
+ d.sp = p->regs.rsp;
+ virt_cpy_to(p->pages, sp, &d, sizeof(d));
+
+ /* switch to intr handler */
+ p->regs.rcx = (uint64_t)p->intr_fn;
+ p->regs.rsp = sp;
+}
+
/** Removes a process from the process tree. */
static void process_forget(struct process *p) {
assert(p->parent);
@@ -447,6 +467,11 @@ void process_transition(struct process *p, enum process_state state) {
assert(p->state == PS_TOREAP || p->state == PS_DYING);
} else if (state == PS_TOREAP) {
assert(p->state == PS_DYING);
+
+ assert(!p->parent || proc_alive(p->parent));
+ for (struct process *it = p->child; it; it = it->sibling) {
+ assert(p->state != PS_TOREAP);
+ }
} else if (state != PS_RUNNING && state != PS_DYING) {
assert(p->state == PS_RUNNING);
}
diff --git a/src/kernel/proc.h b/src/kernel/proc.h
index b95e901..041dbd0 100644
--- a/src/kernel/proc.h
+++ b/src/kernel/proc.h
@@ -81,6 +81,9 @@ struct process {
/* vfs_backend controlled (not exclusively) by this process */
struct vfs_backend *controlled;
+ /* interrupt handler */
+ void __user *intr_fn;
+
struct {
void *buf;
size_t len;
@@ -100,6 +103,8 @@ void process_filicide(struct process *proc, int ret);
/** Tries to reap a dead process / free a tombstone. */
void process_tryreap(struct process *dead);
+void process_intr(struct process *proc);
+
/** Switches execution to any running process. */
_Noreturn void process_switch_any(void);
diff --git a/src/kernel/syscalls.c b/src/kernel/syscalls.c
index 3ebda61..8327b68 100644
--- a/src/kernel/syscalls.c
+++ b/src/kernel/syscalls.c
@@ -378,6 +378,16 @@ void _syscall_filicide(void) {
process_filicide(process_current, -1);
}
+void _syscall_intr(void) {
+ for (struct process *p = process_current->child; p; p = process_next(p, process_current)) {
+ process_intr(p);
+ }
+}
+
+void _syscall_intr_set(void __user *ip) {
+ process_current->intr_fn = ip;
+}
+
long _syscall_execbuf(void __user *ubuf, size_t len) {
if (len == 0) SYSCALL_RETURN(0);
if (len > EXECBUF_MAX_LEN)
@@ -428,6 +438,8 @@ long _syscall(long num, long a, long b, long c, long d, long e) {
break; case _SYSCALL_PIPE: _syscall_pipe((userptr_t)a, b);
break; case _SYSCALL_SLEEP: _syscall_sleep(a);
break; case _SYSCALL_FILICIDE: _syscall_filicide();
+ break; case _SYSCALL_INTR: _syscall_intr();
+ break; case _SYSCALL_INTR_SET: _syscall_intr_set((userptr_t)a);
break; case _SYSCALL_EXECBUF: _syscall_execbuf((userptr_t)a, b);
break; case _SYSCALL_DEBUG_KLOG: _syscall_debug_klog((userptr_t)a, b);
break;
diff --git a/src/shared/include/camellia/syscalls.h b/src/shared/include/camellia/syscalls.h
index 690e928..1de5368 100644
--- a/src/shared/include/camellia/syscalls.h
+++ b/src/shared/include/camellia/syscalls.h
@@ -17,6 +17,8 @@
#define _SYSCALL_PIPE 14
#define _SYSCALL_SLEEP 15
#define _SYSCALL_FILICIDE 16
+#define _SYSCALL_INTR 17
+#define _SYSCALL_INTR_SET 18
#define _SYSCALL_EXECBUF 100
#define _SYSCALL_DEBUG_KLOG 101
@@ -74,6 +76,8 @@ long _syscall_pipe(handle_t __user user_ends[2], int flags);
void _syscall_sleep(long ms);
void _syscall_filicide(void);
+void _syscall_intr(void);
+void _syscall_intr_set(void __user *ip);
/* see shared/execbuf.h */
long _syscall_execbuf(void __user *buf, size_t len);
diff --git a/src/shared/include/camellia/types.h b/src/shared/include/camellia/types.h
index be34783..bd17e51 100644
--- a/src/shared/include/camellia/types.h
+++ b/src/shared/include/camellia/types.h
@@ -30,3 +30,8 @@ struct ufs_request {
long offset;
int flags;
};
+
+struct intr_data {
+ void __user *ip;
+ void __user *sp; /* last for pop %rsp */
+};
diff --git a/src/user/app/ext2fs/main.c b/src/user/app/ext2fs/main.c
index f6d65cc..65e7460 100644
--- a/src/user/app/ext2fs/main.c
+++ b/src/user/app/ext2fs/main.c
@@ -12,6 +12,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <unistd.h>
struct handle {
uint32_t n;
@@ -191,6 +192,8 @@ err:
int
main(int argc, char **argv)
{
+ intr_set(NULL);
+
if (argc < 2) errx(1, "bad usage");
// TODO pread/pwrite for normal handles
FILE *disk = fopen(argv[1], "r+");
diff --git a/src/user/app/init/driver/initctl.c b/src/user/app/init/driver/initctl.c
index 76d1b1b..67c8ade 100644
--- a/src/user/app/init/driver/initctl.c
+++ b/src/user/app/init/driver/initctl.c
@@ -29,7 +29,9 @@ void initctl_drv(handle_t killswitch) {
}
if (!strcmp(buf, "halt")) {
_syscall_write(killswitch, "halt", 4, 0, 0);
- exit(1);
+ }
+ if (!strcmp(buf, "intr")) {
+ _syscall_write(killswitch, "intr", 4, 0, 0);
}
c0_fs_respond(NULL, res.len, 0);
break;
diff --git a/src/user/app/init/init.c b/src/user/app/init/init.c
index 350d9a0..0114ac5 100644
--- a/src/user/app/init/init.c
+++ b/src/user/app/init/init.c
@@ -3,6 +3,7 @@
#include <camellia/syscalls.h>
#include <stdint.h>
#include <stdio.h>
+#include <string.h>
#include <unistd.h>
#include <camellia/fs/misc.h>
@@ -29,7 +30,7 @@ void redirect(const char *exe, const char *out, const char *in) {
exit(1);
}
_syscall_await();
- _syscall_filicide();
+ _syscall_intr();
}
}
}
@@ -39,7 +40,7 @@ int main(void) {
freopen("/kdev/com1", "a+", stdout);
freopen("/kdev/com1", "a+", stderr);
- printf("in init (stage 2), main at %p\n", &main);
+ printf("[init] stage 2, main at %p\n", &main);
MOUNT_AT("/keyboard") {
MOUNT_AT("/") { fs_whitelist((const char*[]){"/kdev/ps2/kb", NULL}); }
@@ -104,7 +105,22 @@ int main(void) {
exit(1);
}
- _syscall_read(killswitch_pipe[0], NULL, 0, 0);
+ char buf[128];
+ for (;;) {
+ if (_syscall_read(killswitch_pipe[0], buf, 128, 0) != 4) {
+ break;
+ }
+ if (memcmp(buf, "intr", 4) == 0) {
+ _syscall_intr();
+ } else if (memcmp(buf, "halt", 4) == 0) {
+ break;
+ }
+ }
+ printf("[init] intr\n");
+ _syscall_intr();
+ _syscall_sleep(1000);
+ printf("[init] filicide\n");
_syscall_filicide();
+ printf("[init] goodbye\n");
return 0;
}
diff --git a/src/user/app/shell/shell.c b/src/user/app/shell/shell.c
index 3dad1bd..564daa8 100644
--- a/src/user/app/shell/shell.c
+++ b/src/user/app/shell/shell.c
@@ -144,7 +144,6 @@ static void run(char *cmd) {
}
}
-
int main(int argc, char **argv) {
static char buf[256];
FILE *f = stdin;
diff --git a/src/user/lib/_start2.c b/src/user/lib/_start2.c
index ba3b6b0..495f046 100644
--- a/src/user/lib/_start2.c
+++ b/src/user/lib/_start2.c
@@ -17,9 +17,13 @@ const char *shortname(const char *path) {
return path;
}
+void intr_trampoline(void); /* intr.s */
+
_Noreturn void _start2(struct execdata *ed) {
const char *progname;
elf_selfreloc();
+ _syscall_intr_set(intr_trampoline);
+ intr_set(intr_default);
__setinitialcwd(ed->cwd);
progname = shortname(ed->argv[0]);
diff --git a/src/user/lib/include/unistd.h b/src/user/lib/include/unistd.h
index f49c337..d13767b 100644
--- a/src/user/lib/include/unistd.h
+++ b/src/user/lib/include/unistd.h
@@ -26,3 +26,6 @@ size_t absolutepath(char *out, const char *in, size_t size);
// TODO put in an internal libc header
void __setinitialcwd(const char *c);
+
+void intr_set(void (*fn)(void));
+void intr_default(void);
diff --git a/src/user/lib/intr.s b/src/user/lib/intr.s
new file mode 100644
index 0000000..008387d
--- /dev/null
+++ b/src/user/lib/intr.s
@@ -0,0 +1,20 @@
+.section .text
+.global intr_trampoline
+.type intr_trampoline, @function
+intr_trampoline:
+ push %rax
+ push %rdx
+ call *_intr(%rip)
+ pop %rdx
+ pop %rax
+ pop tmprip(%rip)
+ pop %rsp
+ jmp *tmprip(%rip)
+
+.section .bss
+tmprip:
+ .skip 8
+
+.global _intr
+_intr:
+ .skip 8
diff --git a/src/user/lib/syscall.c b/src/user/lib/syscall.c
index d6ed0af..d42c2ee 100644
--- a/src/user/lib/syscall.c
+++ b/src/user/lib/syscall.c
@@ -74,6 +74,14 @@ void _syscall_filicide(void) {
return (void)_syscall(_SYSCALL_FILICIDE, 0, 0, 0, 0, 0);
}
+void _syscall_intr(void) {
+ return (void)_syscall(_SYSCALL_INTR, 0, 0, 0, 0, 0);
+}
+
+void _syscall_intr_set(void __user *ip) {
+ return (void)_syscall(_SYSCALL_INTR_SET, (long)ip, 0, 0, 0, 0);
+}
+
long _syscall_execbuf(void __user *buf, size_t len) {
return _syscall(_SYSCALL_EXECBUF, (long)buf, (long)len, 0, 0, 0);
}
diff --git a/src/user/lib/unistd.c b/src/user/lib/unistd.c
index 01aa94f..cb862f8 100644
--- a/src/user/lib/unistd.c
+++ b/src/user/lib/unistd.c
@@ -161,3 +161,14 @@ size_t absolutepath(char *out, const char *in, size_t size) {
void __setinitialcwd(const char *s) {
__initialcwd = s;
}
+
+static void intr_null(void) { }
+
+extern void (*volatile _intr)(void);
+void intr_set(void (*fn)(void)) {
+ _intr = fn ? fn : intr_null;
+}
+
+void intr_default(void) {
+ exit(-1);
+}