genode/repos/base-sel4/src/lib/base/ipc.cc

401 lines
11 KiB
C++

/*
* \brief seL4 implementation of the IPC API
* \author Norman Feske
* \date 2014-10-14
*/
/*
* Copyright (C) 2014-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
/* Genode includes */
#include <base/blocking.h>
#include <base/ipc.h>
#include <base/log.h>
#include <base/thread.h>
#include <util/misc_math.h>
/* base-internal includes */
#include <base/internal/capability_space_sel4.h>
#include <base/internal/kernel_debugger.h>
#include <base/internal/ipc_server.h>
/* seL4 includes */
#include <sel4/sel4.h>
using namespace Genode;
/**
* Message-register definitions
*/
enum {
MR_IDX_EXC_CODE = 0,
MR_IDX_NUM_CAPS = 1,
MR_IDX_CAPS = 2,
MR_IDX_DATA = MR_IDX_CAPS + Msgbuf_base::MAX_CAPS_PER_MSG,
};
/**
* Return reference to receive selector of the calling thread
*/
static unsigned &rcv_sel()
{
/*
* When the function is called at the very early initialization phase, we
* cannot access Thread::myself()->native_thread() because the
* Thread object of the main thread does not exist yet. During this
* phase, we return a reference to the 'main_rcv_sel' variable.
*/
if (Thread::myself()) {
return Thread::myself()->native_thread().rcv_sel;
}
static unsigned main_rcv_sel = Capability_space::alloc_rcv_sel();
return main_rcv_sel;
}
/*****************************
** Startup library support **
*****************************/
void prepare_reinit_main_thread()
{
/**
* Reset selector to invalid, so that a new fresh will be allocated.
* The IPC buffer of the thread must be configured to point to the
* receive selector which is done by Capability_space::alloc_rcv_sel(),
* which finally calls seL4_SetCapReceivePath();
*/
rcv_sel() = 0;
}
/**
* Convert Genode::Msgbuf_base content into seL4 message
*
* \param msg source message buffer
*/
static seL4_MessageInfo_t new_seL4_message(Msgbuf_base const &msg)
{
/*
* Supply capabilities to kernel IPC message
*/
seL4_SetMR(MR_IDX_NUM_CAPS, msg.used_caps());
size_t sel4_sel_cnt = 0;
for (size_t i = 0; i < msg.used_caps(); i++) {
Native_capability const &cap = msg.cap(i);
if (cap.valid()) {
Capability_space::Ipc_cap_data const ipc_cap_data =
Capability_space::ipc_cap_data(cap);
seL4_SetMR(MR_IDX_CAPS + i, ipc_cap_data.rpc_obj_key.value());
seL4_SetCap(sel4_sel_cnt++, ipc_cap_data.sel.value());
} else {
seL4_SetMR(MR_IDX_CAPS + i, Rpc_obj_key::INVALID);
}
}
/*
* Pad unused capability slots with invalid capabilities to avoid
* leakage of any information that happens to be in the IPC buffer.
*/
for (size_t i = msg.used_caps(); i < Msgbuf_base::MAX_CAPS_PER_MSG; i++)
seL4_SetMR(MR_IDX_CAPS + i, Rpc_obj_key::INVALID);
/*
* Supply data payload
*/
size_t const num_data_mwords =
align_natural(msg.data_size()) / sizeof(umword_t);
umword_t const *src = (umword_t const *)msg.data();
for (size_t i = 0; i < num_data_mwords; i++)
seL4_SetMR(MR_IDX_DATA + i, *src++);
seL4_MessageInfo_t const msg_info =
seL4_MessageInfo_new(0, 0, sel4_sel_cnt,
MR_IDX_DATA + num_data_mwords);
return msg_info;
}
/**
* Convert seL4 message into Genode::Msgbuf_base
*/
static void decode_seL4_message(seL4_MessageInfo_t const &msg_info,
Msgbuf_base &dst_msg)
{
/**
* Read all required data from seL4 IPC message
*
* You must not use any Genode primitives which may corrupt the IPCBuffer
* during this step, e.g. Lock or RPC for output !!!
*/
size_t const num_caps = min(seL4_GetMR(MR_IDX_NUM_CAPS),
(addr_t)Msgbuf_base::MAX_CAPS_PER_MSG);
uint32_t const caps_extra = seL4_MessageInfo_get_extraCaps(msg_info);
uint32_t const caps_unwrapped = seL4_MessageInfo_get_capsUnwrapped(msg_info);
uint32_t const num_msg_words = seL4_MessageInfo_get_length(msg_info);
Rpc_obj_key rpc_obj_keys[Msgbuf_base::MAX_CAPS_PER_MSG];
unsigned long arg_badges[Msgbuf_base::MAX_CAPS_PER_MSG];
for (size_t i = 0; i < num_caps; i++) {
rpc_obj_keys[i] = Rpc_obj_key(seL4_GetMR(MR_IDX_CAPS + i));
if (!rpc_obj_keys[i].valid())
/*
* If rpc_obj_key is invalid, avoid calling
* seL4_CapData_Badge_get_Badge. It may trigger a assertion if
* the lowest bit is set by the garbage badge value we got.
*/
arg_badges[i] = Rpc_obj_key::INVALID;
else
arg_badges[i] = seL4_GetBadge(i);
}
/**
* Extract message data payload
*/
/* detect malformed message with too small header */
if (num_msg_words >= MR_IDX_DATA) {
/* copy data payload */
size_t const max_words = dst_msg.capacity()/sizeof(umword_t);
size_t const num_data_words = min(num_msg_words - MR_IDX_DATA, max_words);
umword_t *dst = (umword_t *)dst_msg.data();
for (size_t i = 0; i < num_data_words; i++)
*dst++ = seL4_GetMR(MR_IDX_DATA + i);
dst_msg.data_size(num_data_words*sizeof(umword_t));
}
/**
* Now we got all data from the IPCBuffer, we may use Native_capability
*/
/**
* Construct Genode capabilities from read seL4 IPC message stored in
* rpc_opj_keys and arg_badges.
*/
size_t curr_sel4_cap_idx = 0;
for (size_t i = 0; i < num_caps; i++) {
Rpc_obj_key const rpc_obj_key = rpc_obj_keys[i];
/*
* Detect passing of invalid capabilities as arguments
*
* The second condition of the check handles the case where a non-RPC
* object capability as passed as RPC argument as done by the
* 'Cap_session::alloc' RPC function. Here, the entrypoint capability
* is not an RPC-object capability but a raw seL4 endpoint selector.
*
* XXX Technically, a message may contain one invalid capability
* followed by a valid one. This check would still wrongly regard
* the first capability as a valid one. A better approach would
* be to introduce another state to Rpc_obj_key, which would
* denote a valid capability that is not an RPC-object capability.
* Hence it is meaningless as a key.
*/
if (!rpc_obj_key.valid() && caps_extra == 0) {
dst_msg.insert(Native_capability());
continue;
}
/*
* RPC object key as contained in the message data is valid.
*/
bool const unwrapped = caps_unwrapped & (1U << curr_sel4_cap_idx);
/* distinguish unwrapped from delegated cap */
if (unwrapped) {
/*
* Received unwrapped capability
*
* This means that the capability argument belongs to our endpoint.
* So it is already present within the capability space.
*/
ASSERT(curr_sel4_cap_idx < Msgbuf_base::MAX_CAPS_PER_MSG);
unsigned long const arg_badge = arg_badges[curr_sel4_cap_idx];
if (arg_badge != rpc_obj_key.value()) {
warning("argument badge (", arg_badge, ") != RPC object key (",
rpc_obj_key.value(), ")");
}
Native_capability arg_cap = Capability_space::lookup(rpc_obj_key);
dst_msg.insert(arg_cap);
} else {
/*
* Received delegated capability
*
* We have either received a capability that is foreign to us,
* or an alias for a capability that we already posses. The
* latter can happen in the following circumstances:
*
* - We forwarded a selector that was created by another
* component. We cannot re-identify such a capability when
* handed back because seL4's badge mechanism works only for
* capabilities belonging to the IPC destination endpoint.
*
* - We received a selector on the IPC reply path, where seL4's
* badge mechanism is not in effect.
*/
bool const delegated = caps_extra;
ASSERT(delegated);
Native_capability arg_cap = Capability_space::lookup(rpc_obj_key);
if (arg_cap.valid()) {
/*
* Discard the received selector and keep using the already
* present one.
*
* XXX We'd need to find out if both the received and the
* looked-up selector refer to the same endpoint.
* Unfortunaltely, seL4 lacks such a comparison operation.
*/
Capability_space::reset_sel(rcv_sel());
dst_msg.insert(arg_cap);
} else {
Capability_space::Ipc_cap_data const
ipc_cap_data(rpc_obj_key, rcv_sel());
dst_msg.insert(Capability_space::import(ipc_cap_data));
/*
* Since we keep using the received selector, we need to
* allocate a fresh one for the next incoming delegation.
*/
rcv_sel() = Capability_space::alloc_rcv_sel();
}
}
curr_sel4_cap_idx++;
}
}
/****************
** IPC client **
****************/
Rpc_exception_code Genode::ipc_call(Native_capability dst,
Msgbuf_base &snd_msg, Msgbuf_base &rcv_msg,
size_t)
{
if (!dst.valid()) {
error("Trying to invoke an invalid capability, stop.");
kernel_debugger_panic("IPC destination is invalid");
}
/* allocate and define receive selector */
if (!rcv_sel())
rcv_sel() = Capability_space::alloc_rcv_sel();
rcv_msg.reset();
unsigned const dst_sel = Capability_space::ipc_cap_data(dst).sel.value();
/**
* Do not use Genode primitives after this point until the return which may
* alter the content of the IPCBuffer, e.g. Lock or RPC.
*/
seL4_MessageInfo_t const request = new_seL4_message(snd_msg);
seL4_MessageInfo_t const reply_msg_info = seL4_Call(dst_sel, request);
Rpc_exception_code const exc_code(seL4_GetMR(MR_IDX_EXC_CODE));
decode_seL4_message(reply_msg_info, rcv_msg);
return exc_code;
}
/****************
** IPC server **
****************/
void Genode::ipc_reply(Native_capability, Rpc_exception_code exc,
Msgbuf_base &snd_msg)
{
/* allocate and define receive selector */
if (!rcv_sel())
rcv_sel() = Capability_space::alloc_rcv_sel();
/**
* Do not use Genode primitives after this point until the return which may
* alter the content of the IPCBuffer, e.g. Lock or RPC.
*/
/* called when entrypoint thread leaves entry loop and exits */
seL4_MessageInfo_t const reply_msg_info = new_seL4_message(snd_msg);
seL4_SetMR(MR_IDX_EXC_CODE, exc.value);
seL4_Reply(reply_msg_info);
}
Genode::Rpc_request Genode::ipc_reply_wait(Reply_capability const &,
Rpc_exception_code exc,
Msgbuf_base &reply_msg,
Msgbuf_base &request_msg)
{
/* allocate and define receive selector */
if (!rcv_sel())
rcv_sel() = Capability_space::alloc_rcv_sel();
seL4_CPtr const dest = Thread::myself()->native_thread().ep_sel;
seL4_Word badge = 0;
if (exc.value == Rpc_exception_code::INVALID_OBJECT)
reply_msg.reset();
request_msg.reset();
/**
* Do not use Genode primitives after this point until the return which may
* alter the content of the IPCBuffer, e.g. Lock or RPC.
*/
seL4_MessageInfo_t const reply_msg_info = new_seL4_message(reply_msg);
seL4_SetMR(MR_IDX_EXC_CODE, exc.value);
seL4_MessageInfo_t const req = seL4_ReplyRecv(dest, reply_msg_info, &badge);
decode_seL4_message(req, request_msg);
return Rpc_request(Native_capability(), badge);
}
Ipc_server::Ipc_server()
:
Native_capability(Capability_space::create_ep_cap(*Thread::myself()))
{ }
Ipc_server::~Ipc_server() { }