aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKimplul <kimi.h.kuparinen@gmail.com>2024-07-18 17:39:15 +0300
committerKimplul <kimi.h.kuparinen@gmail.com>2024-07-18 17:39:15 +0300
commitf9e23e6e41068d63d42f950941c3d2fc155ef21c (patch)
treef548be5eab98389108dbd52b69eae413800405da
parentf952391ee86f42d7606b2d3791931aba746a7aca (diff)
downloadkmi-f9e23e6e41068d63d42f950941c3d2fc155ef21c.tar.gz
kmi-f9e23e6e41068d63d42f950941c3d2fc155ef21c.zip
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
-rw-r--r--include/kmi/syscalls.h8
-rw-r--r--include/kmi/uapi.h19
-rw-r--r--src/bits.c24
-rw-r--r--src/uapi/dispatch.c1
-rw-r--r--src/uapi/ipc.c72
-rw-r--r--tests/common/test.h6
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
@@ -513,9 +513,24 @@ 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)