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

344 lines
9.0 KiB
C++

/*
* \brief seL4 implementation of the IPC API
* \author Norman Feske
* \date 2014-10-14
*/
/*
* Copyright (C) 2014 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* 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;
}
/**
* 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);
/*
* Allocate and define receive selector
*/
if (!rcv_sel())
rcv_sel() = Capability_space::alloc_rcv_sel();
/*
* 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)
{
/*
* Extract Genode capabilities from seL4 IPC message
*/
dst_msg.reset();
size_t const num_caps = seL4_GetMR(MR_IDX_NUM_CAPS);
size_t curr_sel4_cap_idx = 0;
for (size_t i = 0; i < num_caps; i++) {
Rpc_obj_key const rpc_obj_key(seL4_GetMR(MR_IDX_CAPS + 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() && seL4_MessageInfo_get_extraCaps(msg_info) == 0) {
dst_msg.insert(Native_capability());
continue;
}
/*
* RPC object key as contained in the message data is valid.
*/
unsigned const unwrapped =
seL4_MessageInfo_get_capsUnwrapped(msg_info) &
(1 << 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.
*/
unsigned long const arg_badge =
seL4_CapData_Badge_get_Badge(seL4_GetBadge(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 = seL4_MessageInfo_get_extraCaps(msg_info);
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++;
}
/*
* Extract message data payload
*/
size_t const num_msg_words = seL4_MessageInfo_get_length(msg_info);
/* detect malformed message with too small header */
if (num_msg_words < MR_IDX_DATA)
return;
/* 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));
}
/****************
** 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");
}
if (!rcv_sel())
rcv_sel() = Capability_space::alloc_rcv_sel();
seL4_MessageInfo_t const request_msg_info = new_seL4_message(snd_msg);
unsigned const dst_sel = Capability_space::ipc_cap_data(dst).sel.value();
seL4_MessageInfo_t const reply_msg_info =
seL4_Call(dst_sel, request_msg_info);
decode_seL4_message(reply_msg_info, rcv_msg);
return Rpc_exception_code(seL4_GetMR(MR_IDX_EXC_CODE));
}
/****************
** IPC server **
****************/
void Genode::ipc_reply(Native_capability caller, Rpc_exception_code exc,
Msgbuf_base &snd_msg)
{
ASSERT(false);
}
Genode::Rpc_request Genode::ipc_reply_wait(Reply_capability const &last_caller,
Rpc_exception_code exc,
Msgbuf_base &reply_msg,
Msgbuf_base &request_msg)
{
seL4_Word badge = 0;
if (exc.value == Rpc_exception_code::INVALID_OBJECT) {
seL4_MessageInfo_t const request_msg_info =
seL4_Recv(Thread::myself()->native_thread().ep_sel, &badge);
decode_seL4_message(request_msg_info, request_msg);
} else {
seL4_MessageInfo_t const reply_msg_info = new_seL4_message(reply_msg);
seL4_SetMR(MR_IDX_EXC_CODE, exc.value);
seL4_MessageInfo_t const request_msg_info =
seL4_ReplyRecv(Thread::myself()->native_thread().ep_sel,
reply_msg_info, &badge);
decode_seL4_message(request_msg_info, 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() { }