From f9e23e6e41068d63d42f950941c3d2fc155ef21c Mon Sep 17 00:00:00 2001 From: Kimplul Date: Thu, 18 Jul 2024 17:39:15 +0300 Subject: add ipc_tail() + ipc_kick() does a forward and a tail at the same time The idea with ipc_tail() is to allow 'trusted' calls, so for example a process is not allowed to willy-nilly open a file, as it has to go via init(), making the eid 1. This way the receiver can know that the request has gone through init() and can open up a new connection (file, whatever) after which requests from that pid/tid are allowed without init() intervention --- include/kmi/syscalls.h | 8 ++++-- include/kmi/uapi.h | 19 +++++++++++-- src/bits.c | 24 ++++++++++++++--- src/uapi/dispatch.c | 1 + src/uapi/ipc.c | 72 +++++++++++++++++++++++++++++++++++--------------- tests/common/test.h | 6 +++++ 6 files changed, 102 insertions(+), 28 deletions(-) diff --git a/include/kmi/syscalls.h b/include/kmi/syscalls.h index 4319812..2701bbc 100644 --- a/include/kmi/syscalls.h +++ b/include/kmi/syscalls.h @@ -87,12 +87,16 @@ enum sys_code { /** @name IPC. */ /** @{ */ /** Send IPC request as client. */ - SYS_IPC_REQ, /* IPC request to server */ + SYS_IPC_REQ, /** Forward IPC request from client. */ SYS_IPC_FWD, - /** Kick request handling to someone else. */ + /** Tail call, i.e. 'do this for me and then return to whoever called + * me" */ + SYS_IPC_TAIL, + + /** Kick request handling to someone else. Forwards AND does a tailcall. */ SYS_IPC_KICK, /** IPC response from server. */ diff --git a/include/kmi/uapi.h b/include/kmi/uapi.h index 2805a04..fa4aeae 100644 --- a/include/kmi/uapi.h +++ b/include/kmi/uapi.h @@ -512,10 +512,25 @@ SYSCALL_DECLARE5(ipc_req, pid, d0, d1, d2, d3); */ SYSCALL_DECLARE5(ipc_fwd, pid, d0, d1, d2, d3); +/** + * Tail syscall. + * + * Requests that the current process doesn't get returned to in an rpc stack. + * + * @param t Current tcb. + * @param pid Tail target process. + * @param d0 First argument. + * @param d1 Second argument. + * @param d2 Third argument. + * @param d3 Fourth argument. + */ +SYSCALL_DECLARE5(ipc_tail, pid, d0, d1, d2, d3); + /** * Kicking syscall. - * Kicks the handling of an IPC req/fwd to someone else, jumping over the - * current process at ipc_resp(). + * Kicks the handling of an IPC to someone else, jumping over the + * current process at ipc_resp(). Effectively does a forward and a tail at the + * same time. * * I'm imagining that this is useful in cases where an init process connects a * client to another server, and kicks the actual request handling to the diff --git a/src/bits.c b/src/bits.c index 6bc3032..d5968f1 100644 --- a/src/bits.c +++ b/src/bits.c @@ -17,7 +17,13 @@ __weak uint16_t __bswap16(const uint16_t u) return (u & 0xff00) >> 8 | (u & 0x00ff) << 8; } -uint16_t __bswaphi2(uint16_t u) +/** + * Wrapper for gcc builtins, if arch doesn't have it. + * + * @param u Value to byteswap. + * @return \p u byteswapped. + */ +__used uint16_t __bswaphi2(uint16_t u) { return __bswap16(u); } @@ -29,7 +35,13 @@ __weak uint32_t __bswap32(const uint32_t u) (u & 0x0000ff00) << 8 | (u & 0x000000ff) << 24; } -uint32_t __bswapsi2(uint32_t u) +/** + * Wrapper for gcc builtins, if arch doesn't have it. + * + * @param u Value to byteswap. + * @return \p u byteswapped. + */ +__used uint32_t __bswapsi2(uint32_t u) { return __bswap32(u); } @@ -47,7 +59,13 @@ __weak uint64_t __bswap64(const uint64_t u) (u & 0x00000000000000ffULL) << 56; } -uint64_t __bswapdi2(uint64_t u) +/** + * Wrapper for gcc builtins, if arch doesn't have it. + * + * @param u Value to byteswap. + * @return \p u byteswapped. + */ +__used uint64_t __bswapdi2(uint64_t u) { return __bswap64(u); } diff --git a/src/uapi/dispatch.c b/src/uapi/dispatch.c index bbebd9c..5e29922 100644 --- a/src/uapi/dispatch.c +++ b/src/uapi/dispatch.c @@ -62,6 +62,7 @@ void handle_syscall(sys_arg_t syscall, sys_arg_t a, sys_arg_t b, case SYS_FREE_TIMER: sys_free_timer(t, a, b, c, d, e); break; case SYS_IPC_REQ: sys_ipc_req(t, a, b, c, d, e); break; case SYS_IPC_FWD: sys_ipc_fwd(t, a, b, c, d, e); break; + case SYS_IPC_TAIL: sys_ipc_tail(t, a, b, c, d, e); break; 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; diff --git a/src/uapi/ipc.c b/src/uapi/ipc.c index 3a28afa..c24f8cf 100644 --- a/src/uapi/ipc.c +++ b/src/uapi/ipc.c @@ -45,8 +45,13 @@ struct stack_diff { }; /** Enumerator for IPC kind. Used by do_ipc(). */ -enum ipc_kind { - IPC_REQ, IPC_FWD, IPC_KICK +enum ipc_flags { + /** Reuse current rpc stack location. Effectively also means that the + * caller gets a response from whoever is called instead of the current + * process. */ + IPC_TAIL = (1 << 0), + /** Don't update the effective ID. */ + IPC_FORWARD = (1 << 1) }; /** @@ -76,14 +81,14 @@ static void finalize_rpc(struct tcb *t, struct tcb *r, vm_t s) * * @param t Thread to migrate. * @param a RPC arguments. - * @param kind Kind of IPC we're doing. Essentially toggles kick boolean. + * @param flags Kind of IPC we're doing. * @return RPC stack difference that should be passed to finalize_rpc(). */ static vm_t enter_rpc(struct tcb *t, struct sys_ret a, - enum ipc_kind kind) + enum ipc_flags flags) { /* reuse current rpc stack location if we're being kicked */ - vm_t rpc_stack = (kind == IPC_KICK && + vm_t rpc_stack = (is_set(flags, IPC_TAIL) && is_rpc(t)) ? t->rpc_stack : rpc_position(t); struct call_ctx *ctx = (struct call_ctx *)(rpc_stack) - 1; @@ -138,6 +143,7 @@ static bool __enough_rpc_stack(struct tcb *t) /** * Actually run notification handler, no ifs or buts. + * Always enables irqs. * * @param t Current thread. * @param r Process where notification handler is. @@ -167,20 +173,21 @@ static __noreturn void __run_notify(struct tcb *t, struct tcb *r) * ("pid 0"), and we are notifying the current thread */ vm_t s = enter_rpc(t, SYS_RET5(0, t->tid, code, flags, t->eid), - IPC_REQ); + 0); finalize_rpc(t, r, s); clear_bits(t->notify_flags, flags); + enable_irqs(); bkl_unlock(); ret_userspace_fast(); unreachable(); } -void notify(struct tcb *t, enum notify_flag flag) +void notify(struct tcb *t, enum notify_flag flags) { - set_bits(t->notify_flags, flag); + set_bits(t->notify_flags, flags); if (!t->notify_flags) return; @@ -217,6 +224,10 @@ void notify(struct tcb *t, enum notify_flag flag) /** * Jump back to process where rpc came from, assuming such a thing exists. * If we're queued for a notification, jump to it instead. + + * Note that leave_rpc() doesn't enable irqs, as it might be used to + * cancel a halfway started rpc in do_ipc(), in which case we want to + * return back to the process with irqs in the same state as before. * * @param t Thread to do return migration on. * @param a Arguments to pass along. @@ -279,13 +290,16 @@ static void leave_rpc(struct tcb *t, struct sys_ret a) * @param d1 IPC argument 1. * @param d2 IPC argument 2. * @param d3 IPC argument 3. - * @param kind Which kind of IPC to perform. + * @param flags Which kind of IPC to perform. * * Returns \ref ERR_OOMEM if there isn't enough IPC stack left, \ref ERR_INVAL * if the the target process doesn't exist, \ref ERR_NOINIT if the target * process hasn't defined a callback. Otherwise \ref OK and whatever the target * process sends back. * + * Enables irqs if the ipc is succesful, otherwise leaves them in the state they + * were when entering. + * * @todo should all static functions have double underscores? I seem to be * inconsistent. */ @@ -295,12 +309,12 @@ static void do_ipc(struct tcb *t, sys_arg_t d1, sys_arg_t d2, sys_arg_t d3, - enum ipc_kind kind) + enum ipc_flags flags) { if (unlikely(!__enough_rpc_stack(t))) return_args1(t, ERR_OOMEM); - vm_t s = enter_rpc(t, SYS_RET6(t->eid, t->tid, d0, d1, d2, d3), kind); + vm_t s = enter_rpc(t, SYS_RET6(t->eid, t->tid, d0, d1, d2, d3), flags); struct tcb *r = get_tcb(pid); if (unlikely(!r || !is_proc(r))) { @@ -318,13 +332,14 @@ static void do_ipc(struct tcb *t, return; } - if (kind != IPC_REQ) + if (!is_set(flags, IPC_FORWARD)) t->eid = t->pid; finalize_rpc(t, r, s); /* I tested out passing the return values as arguments to * ret_userspace_fast, but apparently that causes enough stack shuffling * to be slower overall. */ + enable_irqs(); bkl_unlock(); ret_userspace_fast(); } @@ -337,13 +352,12 @@ static void do_ipc(struct tcb *t, * @param d1 IPC argument 1. * @param d2 IPC argument 2. * @param d3 IPC argument 3. - * @return When succesful: OK, thread id of the caller and the arguments as-is. + * @return When succesful: OK, thread id of the handler and the arguments as-is. */ SYSCALL_DEFINE5(ipc_req)(struct tcb *t, sys_arg_t pid, sys_arg_t d0, sys_arg_t d1, sys_arg_t d2, sys_arg_t d3) { - enable_irqs(); - do_ipc(t, pid, d0, d1, d2, d3, IPC_REQ); + do_ipc(t, pid, d0, d1, d2, d3, 0); } /** @@ -355,13 +369,30 @@ SYSCALL_DEFINE5(ipc_req)(struct tcb *t, sys_arg_t pid, * @param d1 IPC argument 1. * @param d2 IPC argument 2. * @param d3 IPC argument 3. - * @return When succesful: OK, thread id of the caller and the arguments as-is. + * @return When succesful: OK, thread id of the handler and the arguments as-is. */ SYSCALL_DEFINE5(ipc_fwd)(struct tcb *t, sys_arg_t pid, sys_arg_t d0, sys_arg_t d1, sys_arg_t d2, sys_arg_t d3) { - enable_irqs(); - do_ipc(t, pid, d0, d1, d2, d3, IPC_FWD); + do_ipc(t, pid, d0, d1, d2, d3, IPC_FORWARD); +} + +/** + * IPC tail call syscall handler. + * + * @param t Current tcb. + * @param pid Process to request RPC to. + * @param d0 IPC argument 0. + * @param d1 IPC argument 1. + * @param d2 IPC argument 2. + * @param d3 IPC argument 3. + * @return When succesful: OK, thread ID of the handler and the arguments as-is. + */ +SYSCALL_DEFINE5(ipc_tail)(struct tcb *t, sys_arg_t pid, + sys_arg_t d0, sys_arg_t d1, sys_arg_t d2, + sys_arg_t d3) +{ + do_ipc(t, pid, d0, d1, d2, d3, IPC_TAIL); } /** @@ -373,14 +404,14 @@ SYSCALL_DEFINE5(ipc_fwd)(struct tcb *t, sys_arg_t pid, * @param d1 IPC argument 1. * @param d2 IPC argument 2. * @param d3 IPC argument 3. - * @return When succesful: OK, thread id of the caller and the arguments as-is. + * @return When succesful: OK, thread id of the handler and the arguments as-is. */ SYSCALL_DEFINE5(ipc_kick)(struct tcb *t, sys_arg_t pid, sys_arg_t d0, sys_arg_t d1, sys_arg_t d2, sys_arg_t d3) { enable_irqs(); - do_ipc(t, pid, d0, d1, d2, d3, IPC_KICK); + do_ipc(t, pid, d0, d1, d2, d3, IPC_FORWARD | IPC_TAIL); } /** @@ -443,6 +474,5 @@ SYSCALL_DEFINE1(notify)(struct tcb *t, sys_arg_t tid){ /* set args, if notify swaps us out we pick them up the next time this * thread is scheduled */ set_args1(t, OK); - enable_irqs(); notify(r, NOTIFY_SIGNAL); } diff --git a/tests/common/test.h b/tests/common/test.h index 3b591e0..40e3622 100644 --- a/tests/common/test.h +++ b/tests/common/test.h @@ -165,6 +165,12 @@ static inline id_t sys_req_abs_timer(uint64_t ticks) #define sys_ipc_fwd3(pid, d0, d1, d2) syscall4(SYS_IPC_FWD, pid, d0, d1, d2) #define sys_ipc_fwd4(pid, d0, d1, d2, d3) syscall5(SYS_IPC_FWD, pid, d0, d1, d2, d3) +#define sys_ipc_tail0(pid) syscall1(SYS_IPC_TAIL, pid) +#define sys_ipc_tail1(pid, d0) syscall2(SYS_IPC_TAIL, pid, d0) +#define sys_ipc_tail2(pid, d0, d1) syscall3(SYS_IPC_TAIL, pid, d0, d1) +#define sys_ipc_tail3(pid, d0, d1, d2) syscall4(SYS_IPC_TAIL, pid, d0, d1, d2) +#define sys_ipc_tail4(pid, d0, d1, d2, d3) syscall5(SYS_IPC_TAIL, pid, d0, d1, d2, d3) + #define sys_ipc_kick0(pid) syscall1(SYS_IPC_KICK, pid) #define sys_ipc_kick1(pid, d0) syscall2(SYS_IPC_KICK, pid, d0) #define sys_ipc_kick2(pid, d0, d1) syscall3(SYS_IPC_KICK, pid, d0, d1) -- cgit v1.3