genode/base-nova/include/base/ipc_msgbuf.h

397 lines
9.6 KiB
C
Raw Normal View History

2011-12-22 15:19:25 +00:00
/*
* \brief IPC message buffer layout for NOVA
* \author Norman Feske
* \author Alexander Boettcher
2011-12-22 15:19:25 +00:00
* \date 2009-10-02
*
* On NOVA, we use IPC to transmit plain data and for capability delegation
* and capability translation.
* Therefore the message buffer contains three categories of payload. The
2011-12-22 15:19:25 +00:00
* capability-specific part are the members '_snd_pt*' (sending capability
* selectors) and '_rcv_pt*' (receiving capability selectors).
*/
/*
2013-01-10 20:44:47 +00:00
* Copyright (C) 2009-2013 Genode Labs GmbH
2011-12-22 15:19:25 +00:00
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _INCLUDE__BASE__IPC_MSGBUF_H_
#define _INCLUDE__BASE__IPC_MSGBUF_H_
/* Genode includes */
#include <base/cap_sel_alloc.h>
/* NOVA includes */
#include <nova/syscalls.h>
#include <nova/util.h>
2011-12-22 15:19:25 +00:00
namespace Genode {
class Msgbuf_base
{
public:
enum {
MAX_CAP_ARGS_LOG2 = 2,
MAX_CAP_ARGS = 1 << MAX_CAP_ARGS_LOG2
};
2011-12-22 15:19:25 +00:00
protected:
size_t _size;
/**
* Number of portal-capability selectors to send
*/
size_t _snd_pt_sel_cnt;
/**
* Portal capability selectors to delegate
*/
struct {
addr_t sel;
unsigned rights;
bool trans_map;
} _snd_pt_sel [MAX_CAP_ARGS];
2011-12-22 15:19:25 +00:00
/**
* Base of portal receive window
*/
addr_t _rcv_pt_base;
2011-12-22 15:19:25 +00:00
struct {
addr_t sel;
bool del;
} _rcv_pt_sel [MAX_CAP_ARGS];
enum { FREE_SEL, UNUSED_CAP, USED_CAP } _rcv_pt_cap_free [MAX_CAP_ARGS];
2011-12-22 15:19:25 +00:00
/**
* Read counter for unmarshalling portal capability
* selectors
2011-12-22 15:19:25 +00:00
*/
unsigned _rcv_pt_sel_cnt;
unsigned _rcv_pt_sel_max;
2011-12-22 15:19:25 +00:00
/**
* Number of capabilities which has been received,
* reported by the kernel.
2011-12-22 15:19:25 +00:00
*/
unsigned _rcv_items;
2011-12-22 15:19:25 +00:00
char _msg_start[]; /* symbol marks start of message */
public:
enum { INVALID_INDEX = ~0UL };
2011-12-22 15:19:25 +00:00
/**
* Constructor
*/
Msgbuf_base()
: _rcv_pt_base(INVALID_INDEX), _rcv_items(0)
2011-12-22 15:19:25 +00:00
{
rcv_reset();
snd_reset();
}
~Msgbuf_base()
{
rcv_reset();
}
2011-12-22 15:19:25 +00:00
/*
* Begin of actual message buffer
*/
char buf[];
/**
* Return size of message buffer
*/
inline size_t size() const { return _size; }
/**
* Return address of message buffer
*/
inline void *addr() { return &_msg_start[0]; }
/**
* Reset portal capability selector payload
*/
inline void snd_reset() { _snd_pt_sel_cnt = 0; }
/**
* Append portal capability selector to message buffer
*/
inline bool snd_append_pt_sel(addr_t pt_sel, unsigned rights,
bool trans_map)
2011-12-22 15:19:25 +00:00
{
if (_snd_pt_sel_cnt >= MAX_CAP_ARGS - 1)
return false;
_snd_pt_sel[_snd_pt_sel_cnt ].sel = pt_sel;
_snd_pt_sel[_snd_pt_sel_cnt ].rights = rights;
_snd_pt_sel[_snd_pt_sel_cnt++].trans_map = trans_map;
2011-12-22 15:19:25 +00:00
return true;
}
/**
* Return number of marshalled portal-capability
* selectors
2011-12-22 15:19:25 +00:00
*/
inline size_t snd_pt_sel_cnt() const
{
return _snd_pt_sel_cnt;
}
2011-12-22 15:19:25 +00:00
/**
* Return portal capability selector
*
* \param i index (0 ... 'pt_sel_cnt()' - 1)
* \return portal-capability range descriptor
*
* The returned object could be a null cap. Use
* is_null method to check for it.
2011-12-22 15:19:25 +00:00
*/
Nova::Obj_crd snd_pt_sel(addr_t i, bool &trans_map) const
{
if (i >= _snd_pt_sel_cnt)
return Nova::Obj_crd();
trans_map = _snd_pt_sel[i].trans_map;
return Nova::Obj_crd(_snd_pt_sel[i].sel, 0,
_snd_pt_sel[i].rights);
}
2011-12-22 15:19:25 +00:00
/**
* Request current portal-receive window
*/
addr_t rcv_pt_base() const { return _rcv_pt_base; }
2011-12-22 15:19:25 +00:00
/**
* Reset portal-capability receive window
*/
void rcv_reset()
{
if (!rcv_invalid()) { rcv_cleanup(false); }
_rcv_pt_sel_cnt = 0;
_rcv_pt_sel_max = 0;
_rcv_pt_base = INVALID_INDEX;
}
2011-12-22 15:19:25 +00:00
/**
* Return received portal-capability selector
*/
addr_t rcv_pt_sel()
2011-12-22 15:19:25 +00:00
{
/* return only received or translated caps */
if (_rcv_pt_sel_cnt < _rcv_pt_sel_max)
return _rcv_pt_sel[_rcv_pt_sel_cnt++].sel;
else
return INVALID_INDEX;
2011-12-22 15:19:25 +00:00
}
/**
* Return true if receive window must be re-initialized
*/
bool rcv_invalid() const
{
return _rcv_pt_base == INVALID_INDEX;
}
2011-12-22 15:19:25 +00:00
/**
* Return true if receive window must be re-initialized
*
* After reading portal selectors from the message
* buffer using 'rcv_pt_sel()', we assume that the IDC
* call populated the current receive window with one
* or more portal capabilities.
* To enable the reception of portal capability
* selectors for the next IDC, we need a fresh receive
* window.
*
* \param keep 'true' - Try to keep receive window if
* it's clean.
* 'false' - Free caps of receive window
* because object is freed
* afterwards.
*
* \result 'true' - receive window must be re-initialized
* 'false' - portal selectors has been kept
2011-12-22 15:19:25 +00:00
*/
bool rcv_cleanup(bool keep)
{
/* mark used mapped capabilities as used to prevent freeing */
bool used = false;
for (unsigned i = 0; i < _rcv_pt_sel_cnt; i++) {
if (!_rcv_pt_sel[i].del)
continue;
/* should never happen */
if (_rcv_pt_sel[i].sel < rcv_pt_base() ||
(_rcv_pt_sel[i].sel >= rcv_pt_base() + MAX_CAP_ARGS))
nova_die();
_rcv_pt_cap_free [_rcv_pt_sel[i].sel - rcv_pt_base()] = USED_CAP;
used = true;
}
/* revoke received caps which are unused */
for (unsigned i = 0; i < MAX_CAP_ARGS; i++) {
if (_rcv_pt_cap_free[i] != UNUSED_CAP)
continue;
Nova::revoke(Nova::Obj_crd(rcv_pt_base() + i, 0), true);
}
_rcv_pt_sel_cnt = 0;
_rcv_pt_sel_max = 0;
/* we can keep the cap selectors if none was used */
if (keep && !used)
return false;
/* keep used selectors, free up rest */
for (unsigned i = 0; i < MAX_CAP_ARGS; i++) {
if (_rcv_pt_cap_free[i] == USED_CAP)
continue;
cap_selector_allocator()->free(rcv_pt_base() + i, 0);
}
return true;
}
2011-12-22 15:19:25 +00:00
/**
* Initialize receive window for portal capability
* selectors
2011-12-22 15:19:25 +00:00
*
* \param utcb - UTCB of designated receiver
* thread
* \param rcv_window - If specified - receive exactly
* one capability at the specified
* index of rcv_window
2011-12-22 15:19:25 +00:00
*
* Depending on the 'rcv_invalid', 'rcv_cleanup(true)'
* state of the message buffer and the specified
* rcv_window parameter, this function allocates a
* fresh receive window and clears 'rcv_invalid'.
2011-12-22 15:19:25 +00:00
*/
bool rcv_prepare_pt_sel_window(Nova::Utcb *utcb,
addr_t rcv_window = INVALID_INDEX)
2011-12-22 15:19:25 +00:00
{
try {
/*
* If a rcv_window was specified use solely
* the selector specified by rcv_window.
*/
if (rcv_window != INVALID_INDEX) {
/* cleanup if this msgbuf was already used */
if (!rcv_invalid()) rcv_cleanup(false);
_rcv_pt_base = rcv_window;
} else {
if (rcv_invalid() || rcv_cleanup(true))
_rcv_pt_base = cap_selector_allocator()->alloc(MAX_CAP_ARGS_LOG2);
}
addr_t max = 0;
if (rcv_window == INVALID_INDEX)
max = MAX_CAP_ARGS_LOG2;
using namespace Nova;
/* setup receive window */
utcb->crd_rcv = Obj_crd(rcv_pt_base(), max);
/* open maximal translate window */
utcb->crd_xlt = Obj_crd(0, ~0UL);
return true;
} catch (Bit_array_out_of_indexes) {
using namespace Nova;
/* setup receive window */
utcb->crd_rcv = Obj_crd();
/* open maximal translate window */
utcb->crd_xlt = Obj_crd(0, ~0UL);
return false;
}
}
/**
* Post IPC processing.
*
* Remember where and which caps have been received
* respectively have been translated.
* The information is required to correctly free
* cap indexes and to revoke unused received caps.
*
* \param utcb UTCB of designated receiver thread
*/
void post_ipc(Nova::Utcb *utcb)
{
using namespace Nova;
_rcv_items = (utcb->items >> 16) & 0xffffu;
_rcv_pt_sel_max = 0;
_rcv_pt_sel_cnt = 0;
for (unsigned i = 0; i < MAX_CAP_ARGS; i++)
_rcv_pt_cap_free [i] = FREE_SEL;
addr_t max = 1UL << utcb->crd_rcv.order();
for (unsigned i = 0; i < _rcv_items; i++) {
Utcb::Item * item = utcb->get_item(i);
if (!item)
break;
Crd cap = Crd(item->crd);
/* track which items we got mapped */
if (!cap.is_null() && item->is_del()) {
/* should never happen */
if (cap.base() < rcv_pt_base() ||
(cap.base() >= rcv_pt_base() + MAX_CAP_ARGS))
nova_die();
_rcv_pt_cap_free [cap.base() - rcv_pt_base()] = UNUSED_CAP;
}
if (_rcv_pt_sel_max >= max) continue;
/* track the order of mapped and translated items */
if (cap.is_null()) {
_rcv_pt_sel[_rcv_pt_sel_max].sel = INVALID_INDEX;
_rcv_pt_sel[_rcv_pt_sel_max++].del = false;
} else {
_rcv_pt_sel[_rcv_pt_sel_max].sel = cap.base();
_rcv_pt_sel[_rcv_pt_sel_max++].del = item->is_del();
}
}
/*
* If a specific rcv_window has been specified,
* (see rcv_prepare_pt_sel_window) then the
* caller want to take care about freeing the
* selector. Make the _rcv_pt_base invalid so
* that it is not cleanup twice.
*/
if (max != MAX_CAP_ARGS)
_rcv_pt_base = INVALID_INDEX;
2011-12-22 15:19:25 +00:00
}
};
template <unsigned BUF_SIZE>
class Msgbuf : public Msgbuf_base
{
public:
char buf[BUF_SIZE];
Msgbuf() { _size = BUF_SIZE; }
};
}
#endif /* _INCLUDE__BASE__IPC_MSGBUF_H_ */