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

423 lines
10 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/ipc.h>
#include <base/printf.h>
#include <base/blocking.h>
#include <base/thread.h>
#include <util/misc_math.h>
/* base-internal includes */
#include <internal/capability_space_sel4.h>
#include <internal/kernel_debugger.h>
/* seL4 includes */
#include <sel4/sel4.h>
using namespace Genode;
/**
* Message-register definitions
*/
enum {
MR_IDX_NUM_CAPS = 0,
MR_IDX_CAPS = 1,
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_base::myself()->tid() because the Thread_base
* object of the main thread does not exist yet. During this phase, we
* return a reference to the 'main_rcv_sel' variable.
*/
if (Thread_base::myself()) {
return Thread_base::myself()->tid().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
* \param data_length size of message data in bytes
*/
static seL4_MessageInfo_t new_seL4_message(Msgbuf_base &msg,
size_t const data_length)
{
/*
* 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 &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(data_length) / 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(umword_t badge,
seL4_MessageInfo_t const &msg_info,
Msgbuf_base &dst_msg)
{
/*
* Extract Genode capabilities from seL4 IPC message
*/
dst_msg.reset_caps();
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.append_cap(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 const arg_badge =
seL4_CapData_Badge_get_Badge(seL4_GetBadge(curr_sel4_cap_idx));
if (arg_badge != rpc_obj_key.value()) {
PWRN("argument badge (%d) != RPC object key (%d)",
arg_badge, rpc_obj_key.value());
}
Native_capability arg_cap = Capability_space::lookup(rpc_obj_key);
dst_msg.append_cap(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.append_cap(arg_cap);
} else {
Capability_space::Ipc_cap_data const
ipc_cap_data(rpc_obj_key, rcv_sel());
dst_msg.append_cap(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
*/
umword_t *dst = (umword_t *)dst_msg.data();
for (size_t i = 0; i < seL4_MessageInfo_get_length(msg_info); i++)
*dst++ = seL4_GetMR(MR_IDX_DATA + i);
/*
* Store RPC object key of invoked object to be picked up by server.cc
*/
*(long *)dst_msg.data() = badge;
}
/*****************************
** IPC marshalling support **
*****************************/
void Ipc_ostream::_marshal_capability(Native_capability const &cap)
{
_snd_msg->append_cap(cap);
}
void Ipc_istream::_unmarshal_capability(Native_capability &cap)
{
cap = _rcv_msg->extract_cap();
}
/*****************
** Ipc_ostream **
*****************/
void Ipc_ostream::_send()
{
ASSERT(false);
_write_offset = sizeof(umword_t);
}
Ipc_ostream::Ipc_ostream(Native_capability dst, Msgbuf_base *snd_msg)
:
Ipc_marshaller((char *)snd_msg->data(), snd_msg->size()),
_snd_msg(snd_msg), _dst(dst)
{
_write_offset = sizeof(umword_t);
}
/*****************
** Ipc_istream **
*****************/
void Ipc_istream::_wait()
{
ASSERT(false);
}
Ipc_istream::Ipc_istream(Msgbuf_base *rcv_msg)
:
Ipc_unmarshaller((char *)rcv_msg->data(), rcv_msg->size()),
_rcv_msg(rcv_msg)
{
_read_offset = sizeof(umword_t);
}
Ipc_istream::~Ipc_istream() { }
/****************
** Ipc_client **
****************/
void Ipc_client::_call()
{
if (!Ipc_ostream::_dst.valid()) {
PERR("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, _write_offset);
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(0, reply_msg_info, *_rcv_msg);
_write_offset = _read_offset = sizeof(umword_t);
}
Ipc_client::Ipc_client(Native_capability const &srv, Msgbuf_base *snd_msg,
Msgbuf_base *rcv_msg, unsigned short)
: Ipc_istream(rcv_msg), Ipc_ostream(srv, snd_msg), _result(0)
{ }
/****************
** Ipc_server **
****************/
void Ipc_server::_prepare_next_reply_wait()
{
/* now we have a request to reply */
_reply_needed = true;
/* leave space for return value at the beginning of the msgbuf */
_write_offset = 2*sizeof(umword_t);
/* receive buffer offset */
_read_offset = sizeof(umword_t);
_rcv_msg->reset_read_cap_index();
_snd_msg->reset_caps();
}
void Ipc_server::_wait()
{
seL4_Word badge = Rpc_obj_key::INVALID;
seL4_MessageInfo_t const msg_info =
seL4_Recv(Thread_base::myself()->tid().ep_sel, &badge);
decode_seL4_message(badge, msg_info, *_rcv_msg);
_prepare_next_reply_wait();
}
void Ipc_server::_reply()
{
ASSERT(false);
}
void Ipc_server::_reply_wait()
{
if (!_reply_needed) {
_wait();
} else {
seL4_Word badge = Rpc_obj_key::INVALID;
seL4_MessageInfo_t const reply_msg_info =
new_seL4_message(*_snd_msg, _write_offset);
seL4_MessageInfo_t const request_msg_info =
seL4_ReplyRecv(Thread_base::myself()->tid().ep_sel,
reply_msg_info, &badge);
decode_seL4_message(badge, request_msg_info, *_rcv_msg);
}
_prepare_next_reply_wait();
}
Ipc_server::Ipc_server(Msgbuf_base *snd_msg,
Msgbuf_base *rcv_msg)
:
Ipc_istream(rcv_msg), Ipc_ostream(Native_capability(), snd_msg),
_reply_needed(false)
{
*static_cast<Native_capability *>(this) =
Native_capability(Capability_space::create_ep_cap(*Thread_base::myself()));
}