sel4: IPC implementation

This commit is contained in:
Norman Feske 2015-05-11 08:43:43 +02:00 committed by Christian Helmuth
parent ff46d02c48
commit 6ffba0e473
8 changed files with 484 additions and 146 deletions

View File

@ -1,11 +1,11 @@
/*
* \brief seL4-specific layout of IPC message buffer
* \brief IPC message buffer layout
* \author Norman Feske
* \date 2014-10-14
* \date 2015-05-10
*/
/*
* Copyright (C) 2014 Genode Labs GmbH
* Copyright (C) 2015 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.
@ -14,35 +14,45 @@
#ifndef _INCLUDE__BASE__IPC_MSGBUF_H_
#define _INCLUDE__BASE__IPC_MSGBUF_H_
#include <base/native_types.h>
namespace Genode {
/**
* IPC message buffer layout
*/
class Msgbuf_base
class Msgbuf_base;
template <unsigned> struct Msgbuf;
}
class Genode::Msgbuf_base
{
public:
enum { MAX_CAPS_PER_MSG = 8 };
enum { MAX_CAPS_PER_MSG = 3 };
protected:
/*
* Resolve ambiguity if the header is included from a libc-using
* program.
*/
typedef Genode::size_t size_t;
/*
* Capabilities to be transferred
*/
int _caps[MAX_CAPS_PER_MSG];
Genode::size_t _used_caps;
Genode::size_t _read_cap_index;
Native_capability _caps[MAX_CAPS_PER_MSG];
size_t _used_caps = 0;
size_t _read_cap_index = 0;
/**
* Maximum size of plain-data message payload
*/
Genode::size_t _size;
size_t const _size;
/**
* Actual size of plain-data message payload
*/
Genode::size_t _used_size;
size_t _used_size = 0;
char _msg_start[]; /* symbol marks start of message buffer data */
@ -50,63 +60,87 @@ namespace Genode {
* No member variables are allowed beyond this point!
*/
Msgbuf_base(size_t size) : _size(size) { }
public:
char buf[];
Msgbuf_base() { reset_caps(); }
/**
* Return size of message buffer
*/
inline Genode::size_t size() const { return _size; };
size_t size() const { return _size; };
/**
* Return address of message buffer
*/
inline void *addr() { return &_msg_start[0]; };
void reset_caps() { _used_caps = 0; _read_cap_index = 0; }
bool append_cap(int cap)
void reset_caps()
{
if (_used_caps == MAX_CAPS_PER_MSG)
return false;
for (Genode::size_t i = 0; i < _used_caps; i++)
_caps[i] = Native_capability();
_caps[_used_caps++] = cap;
return true;
_used_caps = 0;
}
int read_cap()
void reset_read_cap_index()
{
_read_cap_index = 0;
}
/**
* Return pointer to start of message-buffer content
*/
void const *data() const { return &_msg_start[0]; };
void *data() { return &_msg_start[0]; };
/**
* Exception type
*/
class Too_many_caps : public Exception { };
/**
* Called from '_marshal_capability'
*/
void append_cap(Native_capability const &cap)
{
if (_used_caps == MAX_CAPS_PER_MSG)
throw Too_many_caps();
_caps[_used_caps++] = cap;
}
/**
* Called from '_unmarshal_capability'
*/
Native_capability extract_cap()
{
if (_read_cap_index == _used_caps)
return -1;
return Native_capability();
return _caps[_read_cap_index++];
}
/**
* Return number of marshalled capabilities
*/
size_t used_caps() const { return _used_caps; }
int cap(unsigned index) const
Native_capability &cap(unsigned index)
{
return index < _used_caps ? _caps[index] : -1;
return _caps[index];
}
};
template <unsigned BUF_SIZE>
struct Genode::Msgbuf : Msgbuf_base
{
/**
* Pump up IPC message buffer to specified buffer size
*
* XXX remove padding of 16
*/
template <unsigned BUF_SIZE>
class Msgbuf : public Msgbuf_base
{
public:
char buf[BUF_SIZE + 16];
char buf[BUF_SIZE];
Msgbuf() { _size = BUF_SIZE; }
Msgbuf() : Msgbuf_base(BUF_SIZE) { }
};
}
#endif /* _INCLUDE__BASE__IPC_MSGBUF_H_ */

View File

@ -82,3 +82,38 @@ Rpc_obj_key Capability_space::rpc_obj_key(Native_capability::Data const &data)
return local_capability_space().rpc_obj_key(data);
}
Capability_space::Ipc_cap_data Capability_space::ipc_cap_data(Native_capability const &cap)
{
return local_capability_space().ipc_cap_data(*cap.data());
}
Native_capability Capability_space::lookup(Rpc_obj_key rpc_obj_key)
{
Native_capability::Data *data = local_capability_space().lookup(rpc_obj_key);
return data ? Native_capability(*data) : Native_capability();
}
unsigned Capability_space::alloc_rcv_sel()
{
PDBG("not implemented");
for (;;);
return 0;
}
void Capability_space::reset_sel(unsigned sel)
{
PDBG("not implemented");
}
Native_capability Capability_space::import(Ipc_cap_data ipc_cap_data)
{
PDBG("not implemented");
return Native_capability();
}

View File

@ -22,7 +22,7 @@
#include <internal/capability_space.h>
#include <internal/assert.h>
namespace Genode { template <unsigned, typename> class Capability_space_sel4; }
namespace Genode { template <unsigned, unsigned, typename> class Capability_space_sel4; }
/**
@ -37,6 +37,9 @@ namespace Genode { namespace Capability_space {
{
Rpc_obj_key rpc_obj_key;
unsigned sel;
Ipc_cap_data(Rpc_obj_key rpc_obj_key, unsigned sel)
: rpc_obj_key(rpc_obj_key), sel(sel) { }
};
/**
@ -49,6 +52,23 @@ namespace Genode { namespace Capability_space {
*/
unsigned alloc_rcv_sel();
/**
* Delete selector but retain allocation
*
* This function is used when a delegated capability selector is replaced
* with an already known selector. The delegated selector is discarded.
*/
void reset_sel(unsigned sel);
/**
* Lookup capability by its RPC object key
*/
Native_capability lookup(Rpc_obj_key key);
/**
* Import capability into the component's capability space
*/
Native_capability import(Ipc_cap_data ipc_cap_data);
} }
@ -65,7 +85,7 @@ namespace Genode { namespace Capability_space {
* freeing capabilities allocated from another component. This information
* is part of the core-specific 'Native_capability::Data' structure.
*/
template <unsigned NUM_CAPS, typename CAP_DATA>
template <unsigned NUM_CAPS, unsigned _NUM_CORE_MANAGED_CAPS, typename CAP_DATA>
class Genode::Capability_space_sel4
{
public:
@ -73,10 +93,10 @@ class Genode::Capability_space_sel4
/*
* The capability space consists of two parts. The lower part is
* populated with statically-defined capabilities whereas the upper
* part is dynamically managed by the component. The 'NUM_STATIC_CAPS'
* defines the size of the first part.
* part is dynamically managed by the component. The
* 'NUM_CORE_MANAGED_CAPS' defines the size of the first part.
*/
enum { NUM_STATIC_CAPS = 1024 };
enum { NUM_CORE_MANAGED_CAPS = _NUM_CORE_MANAGED_CAPS };
private:
@ -111,7 +131,7 @@ class Genode::Capability_space_sel4
Tree_managed_data _caps_data[NUM_CAPS];
Avl_tree<Tree_managed_data> _tree;
Lock _lock;
Lock mutable _lock;
/**
* Calculate index into _caps_data for capability data object
@ -125,9 +145,9 @@ class Genode::Capability_space_sel4
/**
* Return true if capability is locally managed by the component
*/
bool _is_dynamic(Data &data) const
bool _is_core_managed(Data &data) const
{
return _index(data) >= NUM_STATIC_CAPS;
return _index(data) < NUM_CORE_MANAGED_CAPS;
}
void _remove(Native_capability::Data &data)
@ -180,7 +200,7 @@ class Genode::Capability_space_sel4
{
Lock::Guard guard(_lock);
if (_is_dynamic(data) && !data.dec_ref()) {
if (!_is_core_managed(data) && !data.dec_ref()) {
PDBG("remove cap");
_remove(data);
}
@ -190,9 +210,10 @@ class Genode::Capability_space_sel4
{
Lock::Guard guard(_lock);
if (_is_dynamic(data))
if (!_is_core_managed(data)) {
data.inc_ref();
}
}
Rpc_obj_key rpc_obj_key(Data const &data) const
{
@ -203,6 +224,16 @@ class Genode::Capability_space_sel4
{
return { rpc_obj_key(data), sel(data) };
}
Data *lookup(Rpc_obj_key key) const
{
Lock::Guard guard(_lock);
if (!_tree.first())
return nullptr;
return _tree.first()->find_by_key(key);
}
};

View File

@ -19,7 +19,8 @@
#include <util/misc_math.h>
/* base-internal includes */
#include <internal/capability_space.h>
#include <internal/capability_space_sel4.h>
#include <internal/kernel_debugger.h>
/* seL4 includes */
#include <sel4/interfaces/sel4_client.h>
@ -27,19 +28,216 @@
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,
};
/**
* 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);
} 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
*/
Native_thread &thread = Thread_base::myself()->tid();
if (!thread.rcv_sel)
thread.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));
if (!rpc_obj_key.valid()) {
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_thread &thread = Thread_base::myself()->tid();
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(thread.rcv_sel);
dst_msg.append_cap(arg_cap);
} else {
Capability_space::Ipc_cap_data const
ipc_cap_data(rpc_obj_key, thread.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.
*/
thread.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)
{
PDBG("not implemented");
_snd_msg->append_cap(cap);
}
void Ipc_istream::_unmarshal_capability(Native_capability &cap)
{
PDBG("not implemented");
cap = _rcv_msg->extract_cap();
}
@ -49,16 +247,15 @@ void Ipc_istream::_unmarshal_capability(Native_capability &cap)
void Ipc_ostream::_send()
{
PDBG("not implemented");
ASSERT(false);
_write_offset = sizeof(umword_t);
}
Ipc_ostream::Ipc_ostream(Native_capability dst, Msgbuf_base *snd_msg)
:
Ipc_marshaller((char *)snd_msg->addr(), snd_msg->size()),
Ipc_marshaller((char *)snd_msg->data(), snd_msg->size()),
_snd_msg(snd_msg), _dst(dst)
{
_write_offset = sizeof(umword_t);
@ -71,19 +268,13 @@ Ipc_ostream::Ipc_ostream(Native_capability dst, Msgbuf_base *snd_msg)
void Ipc_istream::_wait()
{
PDBG("not implemented");
for (;;)
seL4_Yield();
_read_offset = sizeof(umword_t);
ASSERT(false);
}
Ipc_istream::Ipc_istream(Msgbuf_base *rcv_msg)
:
Ipc_unmarshaller((char *)rcv_msg->addr(), rcv_msg->size()),
Ipc_unmarshaller((char *)rcv_msg->data(), rcv_msg->size()),
_rcv_msg(rcv_msg)
{
_read_offset = sizeof(umword_t);
@ -99,7 +290,20 @@ Ipc_istream::~Ipc_istream() { }
void Ipc_client::_call()
{
PDBG("not implemented");
if (!Ipc_ostream::_dst.valid()) {
PERR("Trying to invoke an invalid capability, stop.");
kernel_debugger_panic("IPC destination is invalid");
}
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;
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);
}
@ -125,29 +329,19 @@ void Ipc_server::_prepare_next_reply_wait()
/* receive buffer offset */
_read_offset = sizeof(umword_t);
_rcv_msg->reset_read_cap_index();
_snd_msg->reset_caps();
}
void Ipc_server::_wait()
{
/* wait for new server request */
PDBG("called, do seL4_Wait");
seL4_Word badge = Rpc_obj_key::INVALID;
seL4_MessageInfo_t const msg_info =
seL4_Wait(Thread_base::myself()->tid().ep_sel, nullptr);
PDBG("returned from seL4_Wait, call seL4_Reply");
seL4_Wait(Thread_base::myself()->tid().ep_sel, &badge);
PDBG("msg_info: got unwrapped %d", seL4_MessageInfo_get_capsUnwrapped(msg_info));
PDBG(" got extra caps %d", seL4_MessageInfo_get_extraCaps(msg_info));
PDBG(" label %d", seL4_MessageInfo_get_label(msg_info));
if (seL4_MessageInfo_get_capsUnwrapped(msg_info)) {
PDBG(" badge %d", seL4_CapData_Badge_get_Badge(seL4_GetBadge(0)));
}
for (;;)
seL4_Yield();
// try { Ipc_istream::_wait(); } catch (Blocking_canceled) { }
decode_seL4_message(badge, msg_info, *_rcv_msg);
_prepare_next_reply_wait();
}
@ -155,18 +349,30 @@ void Ipc_server::_wait()
void Ipc_server::_reply()
{
try { _send(); } catch (Ipc_error) { }
_prepare_next_reply_wait();
ASSERT(false);
}
void Ipc_server::_reply_wait()
{
if (_reply_needed)
_reply();
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_ReplyWait(Thread_base::myself()->tid().ep_sel,
reply_msg_info, &badge);
decode_seL4_message(badge, request_msg_info, *_rcv_msg);
}
_prepare_next_reply_wait();
}
@ -176,5 +382,6 @@ Ipc_server::Ipc_server(Msgbuf_base *snd_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()));
*static_cast<Native_capability *>(this) =
Native_capability(Capability_space::create_ep_cap(*Thread_base::myself()));
}

View File

@ -64,9 +64,6 @@ void Rpc_entrypoint::entry()
/* set default return value */
srv.ret(Ipc_client::ERR_INVALID_OBJECT);
/* check whether capability's label fits global id */
PDBG("not implemented");
/* atomically lookup and lock referenced object */
Object_pool<Rpc_object_base>::Guard curr_obj(lookup_and_lock(srv.badge()));
if (!curr_obj)

View File

@ -61,7 +61,7 @@ namespace {
struct Local_capability_space
:
Capability_space_sel4<1UL << Core_cspace::NUM_CORE_SEL_LOG2,
Capability_space_sel4<1UL << Core_cspace::NUM_CORE_SEL_LOG2, 0UL,
Native_capability::Data>
{ };
@ -163,7 +163,34 @@ Capability_space::Ipc_cap_data Capability_space::ipc_cap_data(Native_capability
}
Native_capability Capability_space::lookup(Rpc_obj_key rpc_obj_key)
{
Native_capability::Data *data = local_capability_space().lookup(rpc_obj_key);
return data ? Native_capability(*data) : Native_capability();
}
unsigned Capability_space::alloc_rcv_sel()
{
return platform_specific()->alloc_core_rcv_sel();
}
void Capability_space::reset_sel(unsigned sel)
{
return platform_specific()->reset_sel(sel);
}
Native_capability Capability_space::import(Ipc_cap_data ipc_cap_data)
{
/* imported capabilities are not associated with a CAP session */
Cap_session const *cap_session = nullptr;
Native_capability::Data &data =
local_capability_space().create_capability(ipc_cap_data.sel, cap_session,
ipc_cap_data.rpc_obj_key);
return Native_capability(data);
}

View File

@ -138,6 +138,7 @@ class Genode::Platform : public Platform_generic
unsigned alloc_core_sel();
unsigned alloc_core_rcv_sel();
void reset_sel(unsigned sel);
void free_core_sel(unsigned sel);
void wait_for_exit();

View File

@ -272,12 +272,18 @@ unsigned Platform::alloc_core_rcv_sel()
{
unsigned rcv_sel = alloc_core_sel();
seL4_SetCapReceivePath(_core_cnode.sel(), rcv_sel, 0);
seL4_SetCapReceivePath(_core_cnode.sel(), rcv_sel, _core_cnode.size_log2());
return rcv_sel;
}
void Platform::reset_sel(unsigned sel)
{
_core_cnode.remove(sel);
}
void Platform::free_core_sel(unsigned sel)
{
Lock::Guard guard(_core_sel_alloc_lock);