diff options
Diffstat (limited to 'src/uapi')
| -rw-r--r-- | src/uapi/conf.c | 1 | ||||
| -rw-r--r-- | src/uapi/dispatch.c | 3 | ||||
| -rw-r--r-- | src/uapi/ipc.c | 40 | ||||
| -rw-r--r-- | src/uapi/proc.c | 100 |
4 files changed, 105 insertions, 39 deletions
diff --git a/src/uapi/conf.c b/src/uapi/conf.c index 242239c..685d294 100644 --- a/src/uapi/conf.c +++ b/src/uapi/conf.c @@ -12,6 +12,7 @@ #include <kmi/sizes.h> #include <kmi/uapi.h> #include <kmi/conf.h> +#include <arch/irq.h> #include <arch/proc.h> diff --git a/src/uapi/dispatch.c b/src/uapi/dispatch.c index f0ff724..7cfce4c 100644 --- a/src/uapi/dispatch.c +++ b/src/uapi/dispatch.c @@ -66,7 +66,7 @@ void handle_syscall(sys_arg_t syscall, sys_arg_t a, sys_arg_t b, case SYS_IPC_KICK: sys_ipc_kick(t, a, b, c, d, e); break; case SYS_IPC_RESP: sys_ipc_resp(t, a, b, c, d, e); break; case SYS_IPC_GHOST: sys_ipc_ghost(t, a, b, c, d, e); break; - case SYS_IPC_NOTIFY: sys_ipc_notify(t, a, b, c, d, e); break; + case SYS_NOTIFY: sys_notify(t, a, b, c, d, e); break; case SYS_CREATE: sys_create(t, a, b, c, d, e); break; case SYS_FORK: sys_fork(t, a, b, c, d, e); break; case SYS_EXEC: sys_exec(t, a, b, c, d, e); break; @@ -81,6 +81,7 @@ void handle_syscall(sys_arg_t syscall, sys_arg_t a, sys_arg_t b, case SYS_POWEROFF: sys_poweroff(t, a, b, c, d, e); break; case SYS_SLEEP: sys_sleep(t, a, b, c, d, e); break; case SYS_IRQ_REQ: sys_irq_req(t, a, b, c, d, e); break; + case SYS_EXIT: sys_exit(t, a, b, c, d, e); break; default: error("Syscall %zu outside allowed range [0 - %i]\n", syscall, SYS_NUM - 1); diff --git a/src/uapi/ipc.c b/src/uapi/ipc.c index 069ca59..f3896f5 100644 --- a/src/uapi/ipc.c +++ b/src/uapi/ipc.c @@ -6,6 +6,7 @@ * Interprocess communication syscall implementations. */ +#include <kmi/orphanage.h> #include <kmi/debug.h> #include <kmi/uapi.h> #include <kmi/tcb.h> @@ -29,9 +30,6 @@ struct call_ctx { /** Current process ID. */ id_t pid; - - /** Whether this context should be skipped when responding. */ - bool kick; }; /** @@ -83,7 +81,9 @@ static void finalize_rpc(struct tcb *t, struct tcb *r, vm_t s) static vm_t enter_rpc(struct tcb *t, struct sys_ret a, enum ipc_kind kind) { - vm_t rpc_stack = rpc_position(t); + /* reuse current rpc stack location if we're being kicked */ + vm_t rpc_stack = (kind == IPC_KICK && + is_rpc(t)) ? t->rpc_stack :rpc_position(t); struct call_ctx *ctx = (struct call_ctx *)(rpc_stack) - 1; ctx->regs = t->regs; @@ -98,9 +98,6 @@ static vm_t enter_rpc(struct tcb *t, struct sys_ret a, ctx->eid = t->eid; ctx->rpc_stack = rpc_stack; - /* only rpcs can be kicked forward */ - ctx->kick = kind == IPC_KICK && is_rpc(t); - /** @todo if we run out of rpc_stack space we should just stop, likely * return a status? except it shouldn't happen after we've run * enough_rpc_stack(). */ @@ -223,17 +220,25 @@ static void leave_rpc(struct tcb *t, struct sys_ret a) struct call_ctx *ctx = (struct call_ctx *)(rpc_stack) - 1; vm_t top = ctx->rpc_stack; - /* find first instance of not kicked context */ - while (ctx->kick) { - rpc_stack = ctx->rpc_stack + BASE_PAGE_SIZE; - ctx = (struct call_ctx *)(rpc_stack) - 1; - unreference_proc(get_tcb(ctx->pid)); - } - t->regs = ctx->regs; /* again, get rid of args as fast as possible */ set_args(t, 6, a); + struct tcb *r = get_tcb(ctx->pid); + while (!r || r->dead) { + /* we unwound back to our root process which is apparently dead, + * we're orphaned :( */ + if (ctx->pid == t->rid) { + orphanize(t); + return; + } + + rpc_stack = ctx->rpc_stack + BASE_PAGE_SIZE; + ctx = (struct call_ctx *)(rpc_stack) - 1; + + r = get_tcb(ctx->pid); + } + set_return(t, ctx->exec); /* if we're returning from a failed rpc, this should essentially be a * no-op */ @@ -301,13 +306,12 @@ static void do_ipc(struct tcb *t, vm_t s = enter_rpc(t, SYS_RET6(t->eid, t->tid, d0, d1, d2, d3), kind); struct tcb *r = get_tcb(pid); - if (unlikely(!r)) { + if (unlikely(!r || !is_proc(r))) { leave_rpc(t, SYS_RET1(ERR_INVAL)); return; } - r = get_rproc(r); - if (unlikely(r->dead)) { + if (unlikely(zombie(r))) { leave_rpc(t, SYS_RET1(ERR_INVAL)); return; } @@ -430,7 +434,7 @@ SYSCALL_DEFINE0(ipc_ghost)(struct tcb *t) * @param tid Thread ID to notify. * @return \ref OK and 0. */ -SYSCALL_DEFINE1(ipc_notify)(struct tcb *t, sys_arg_t tid){ +SYSCALL_DEFINE1(notify)(struct tcb *t, sys_arg_t tid){ if (t->tid != tid && !has_cap(t->caps, CAP_NOTIFY)) return_args1(t, ERR_PERM); diff --git a/src/uapi/proc.c b/src/uapi/proc.c index 1b672ce..3ce808f 100644 --- a/src/uapi/proc.c +++ b/src/uapi/proc.c @@ -10,7 +10,9 @@ #include <kmi/uapi.h> #include <kmi/proc.h> #include <kmi/bits.h> +#include <kmi/power.h> #include <kmi/notify.h> +#include <kmi/orphanage.h> #include <kmi/mem_regions.h> #include <arch/irq.h> @@ -145,24 +147,94 @@ SYSCALL_DEFINE2(spawn)(struct tcb *t, sys_arg_t bin, sys_arg_t interp) * Kill syscall handler. * * @param t Current tcb. - * @param tid Thread to kill. + * @param pid Process to kill. * \todo Implement. * * @return ERR_PERM if not capable to kill, otherwise OK. */ -SYSCALL_DEFINE1(kill)(struct tcb *t, sys_arg_t tid) +SYSCALL_DEFINE1(kill)(struct tcb *t, sys_arg_t pid) { struct tcb *c = get_cproc(t); if (!(has_cap(c->caps, CAP_PROC))) return_args1(t, ERR_PERM); - /** @todo implement */ - /** @todo remember to unregister IRQ handlers */ + struct tcb *r = get_tcb(pid); + if (is_proc(r)) + return_args1(t, ERR_INVAL); + destroy_proc(r); return_args1(t, OK); } /** + * Actual worker of swapping between threads. + * Assumes that both \p t and \p s exist and that \p s isn't a zombie or + * currently running. + * + * @param t Current tcb. + * @param s Thread to swap to. + */ +static void swap(struct tcb *t, struct tcb *s) +{ + /* switch over to new thread */ + use_tcb(s); + + /* if an irq handler is directly swapping to some other thread, + * interpret it as the thread being finished with its critical section */ + enable_irqs(); + + if (!is_rpc(s) && orphan(s)) { + orphanize(s); + return; + } + + /* set return value for current thread */ + set_args1(t, OK); + + /* handle possible queued notification */ + if (s->notify_flags) + notify(s, 0); + + /* no notifications, so get register state for new thread */ + return_args(s, get_args(s)); +} + +/** + * Syscall handler for exit syscall. + * + * @param t Thread that is exiting. + * @param tid Thread to swap to. + * + * @return \ref OK or \ref ERR_INVAL is \p tid is not a thread + * or \ref ERR_NF if \p tid is a zombie + * or \ref ERR_EXT if \p tid is currently running. + */ +SYSCALL_DEFINE1(exit)(struct tcb *t, sys_arg_t tid) +{ + if (tid != 0) { + struct tcb *s = get_tcb(tid); + if (!s) + return_args1(t, ERR_INVAL); + + if (zombie(s)) + return_args1(t, ERR_NF); + + if (running(s)) + return_args1(t, ERR_EXT); + + + swap(t, s); + } + + destroy_thread(t); + + if (tid == 0) { + enable_irqs(); + sleep(); + } +} + +/** * Swap syscall handler. * * \todo Implement. @@ -183,23 +255,11 @@ SYSCALL_DEFINE1(swap)(struct tcb *t, sys_arg_t tid){ if (!s) return_args1(t, ERR_INVAL); + if (zombie(s)) + return_args1(t, ERR_NF); + if (running(s)) return_args1(t, ERR_EXT); - /* switch over to new thread */ - use_tcb(s); - - /* set return value for current thread */ - set_args1(t, OK); - - /* if an irq handler is directly swapping to some other thread, - * interpret it as the thread being finished with its critical section */ - enable_irqs(); - - /* handle possible queued notification */ - if (s->notify_flags) - notify(s, 0); - - /* no notifications, so get register state for new thread */ - return_args(s, get_args(s)); + return swap(t, s); } |
