383 lines
9.4 KiB
C++
383 lines
9.4 KiB
C++
/*
|
|
* \brief Implementation of the IPC API for Fiasco.OC
|
|
* \author Stefan Kalkowski
|
|
* \date 2009-12-03
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2009-2013 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.
|
|
*/
|
|
|
|
/*
|
|
* l4_msgtag_t (size == 1 mword) format:
|
|
*
|
|
* --------------------------------------------------------------
|
|
* | label | 4 Bit flags | 6 Bit items | 6 Bit word count |
|
|
* --------------------------------------------------------------
|
|
*/
|
|
|
|
|
|
/* Genode includes */
|
|
#include <base/blocking.h>
|
|
#include <base/ipc.h>
|
|
#include <base/ipc_msgbuf.h>
|
|
#include <base/thread.h>
|
|
#include <util/assert.h>
|
|
|
|
/* base-foc/src/base/lock */
|
|
#include <lock_helper.h> /* for 'thread_get_my_native_id()' */
|
|
|
|
/* Fiasco.OC includes */
|
|
namespace Fiasco {
|
|
#include <l4/sys/consts.h>
|
|
#include <l4/sys/ipc.h>
|
|
#include <l4/sys/types.h>
|
|
#include <l4/sys/utcb.h>
|
|
#include <l4/sys/kdebug.h>
|
|
}
|
|
|
|
using namespace Genode;
|
|
using namespace Fiasco;
|
|
|
|
|
|
/*****************************
|
|
** IPC marshalling support **
|
|
*****************************/
|
|
|
|
void Ipc_ostream::_marshal_capability(Native_capability const &cap)
|
|
{
|
|
/* first transfer local capability value */
|
|
_write_to_buf(cap.local());
|
|
|
|
/* if it's a local capability we're done */
|
|
if (cap.local())
|
|
return;
|
|
|
|
if (cap.valid()) {
|
|
if (!l4_msgtag_label(l4_task_cap_valid(L4_BASE_TASK_CAP, cap.dst()))) {
|
|
_write_to_buf(0);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* transfer capability id */
|
|
_write_to_buf(cap.local_name());
|
|
|
|
/* only transfer kernel-capability if it's a valid one */
|
|
if (cap.valid())
|
|
_snd_msg->snd_append_cap_sel(cap.dst());
|
|
|
|
ASSERT(!cap.valid() ||
|
|
l4_msgtag_label(l4_task_cap_valid(L4_BASE_TASK_CAP, cap.dst())),
|
|
"Send invalid cap");
|
|
}
|
|
|
|
|
|
void Ipc_istream::_unmarshal_capability(Native_capability &cap)
|
|
{
|
|
long value = 0;
|
|
|
|
/* get local capability pointer from message buffer */
|
|
_read_from_buf(value);
|
|
|
|
/* if it's a local capability, the pointer is marshalled in the id */
|
|
if (value) {
|
|
cap = Capability<Native_capability>::local_cap((Native_capability*)value);
|
|
return;
|
|
}
|
|
|
|
/* extract capability id from message buffer */
|
|
_read_from_buf(value);
|
|
|
|
/* if id is zero an invalid capability was transfered */
|
|
if (!value) {
|
|
cap = Native_capability();
|
|
return;
|
|
}
|
|
|
|
/* try to insert received capability in the map and return it */
|
|
cap = Native_capability(cap_map()->insert_map(value, _rcv_msg->rcv_cap_sel()));
|
|
}
|
|
|
|
|
|
/***************
|
|
** Utilities **
|
|
***************/
|
|
|
|
enum Debug {
|
|
DEBUG_MSG = 0,
|
|
HALT_ON_ERROR = 0
|
|
};
|
|
|
|
|
|
static inline bool ipc_error(l4_msgtag_t tag, bool print)
|
|
{
|
|
int ipc_error = l4_ipc_error(tag, l4_utcb());
|
|
if (ipc_error) {
|
|
if (print) {
|
|
outstring("Ipc error: ");
|
|
outhex32(ipc_error);
|
|
outstring(" occurred!\n");
|
|
}
|
|
if (HALT_ON_ERROR)
|
|
enter_kdebug("Ipc error");
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* Copy message registers from UTCB to destination message buffer
|
|
*/
|
|
static void copy_utcb_to_msgbuf(l4_msgtag_t tag, Msgbuf_base *rcv_msg)
|
|
{
|
|
unsigned num_msg_words = l4_msgtag_words(tag);
|
|
unsigned num_cap_sel = l4_msgtag_items(tag);
|
|
if (num_msg_words == 0 && num_cap_sel == 0)
|
|
return;
|
|
|
|
/* look up and validate destination message buffer to receive the payload */
|
|
l4_mword_t *msg_buf = (l4_mword_t *)rcv_msg->buf;
|
|
if (num_msg_words*sizeof(l4_mword_t) > rcv_msg->size()) {
|
|
if (DEBUG_MSG)
|
|
outstring("receive message buffer too small");
|
|
num_msg_words = rcv_msg->size()/sizeof(l4_mword_t);
|
|
}
|
|
|
|
/* read message payload into destination message buffer */
|
|
l4_mword_t *src = (l4_mword_t *)l4_utcb_mr();
|
|
l4_mword_t *dst = (l4_mword_t *)&msg_buf[0];
|
|
for (unsigned i = 0; i < num_msg_words; i++)
|
|
*dst++ = *src++;
|
|
|
|
rcv_msg->rcv_reset();
|
|
}
|
|
|
|
|
|
/**
|
|
* Copy message registers from message buffer to UTCB and create message tag.
|
|
*/
|
|
static l4_msgtag_t copy_msgbuf_to_utcb(Msgbuf_base *snd_msg, unsigned offset,
|
|
Native_capability dst)
|
|
{
|
|
l4_mword_t *msg_buf = (l4_mword_t *)snd_msg->buf;
|
|
unsigned num_msg_words = offset/sizeof(l4_mword_t);
|
|
unsigned num_cap_sel = snd_msg->snd_cap_sel_cnt();
|
|
|
|
if (num_msg_words + 2 * num_cap_sel > L4_UTCB_GENERIC_DATA_SIZE) {
|
|
if (DEBUG_MSG)
|
|
outstring("receive message buffer too small");
|
|
throw Ipc_error();
|
|
}
|
|
|
|
/* first copy target label to message buffer */
|
|
msg_buf[0] = dst.local_name();
|
|
|
|
/* store message into UTCB message registers */
|
|
for (unsigned i = 0; i < num_msg_words; i++)
|
|
l4_utcb_mr()->mr[i] = msg_buf[i];
|
|
|
|
/* setup flexpages of capabilities to send */
|
|
for (unsigned i = 0; i < num_cap_sel; i++) {
|
|
unsigned idx = num_msg_words + 2*i;
|
|
l4_utcb_mr()->mr[idx] = L4_ITEM_MAP/* | L4_ITEM_CONT*/;
|
|
l4_utcb_mr()->mr[idx + 1] = l4_obj_fpage(snd_msg->snd_cap_sel(i),
|
|
0, L4_FPAGE_RWX).raw;
|
|
}
|
|
|
|
/* we have consumed capability selectors, reset message buffer */
|
|
snd_msg->snd_reset();
|
|
|
|
return l4_msgtag(0, num_msg_words, num_cap_sel, 0);
|
|
}
|
|
|
|
|
|
/*****************
|
|
** Ipc_ostream **
|
|
*****************/
|
|
|
|
void Ipc_ostream::_send()
|
|
{
|
|
l4_msgtag_t tag = copy_msgbuf_to_utcb(_snd_msg, _write_offset, _dst);
|
|
tag = l4_ipc_send(_dst.dst(), l4_utcb(), tag, L4_IPC_NEVER);
|
|
if (ipc_error(tag, DEBUG_MSG))
|
|
throw Ipc_error();
|
|
|
|
_write_offset = sizeof(l4_mword_t);
|
|
}
|
|
|
|
|
|
Ipc_ostream::Ipc_ostream(Native_capability dst, Msgbuf_base *snd_msg)
|
|
:
|
|
Ipc_marshaller(&snd_msg->buf[0], snd_msg->size()),
|
|
_snd_msg(snd_msg), _dst(dst)
|
|
{
|
|
_write_offset = sizeof(l4_mword_t);
|
|
}
|
|
|
|
|
|
/*****************
|
|
** Ipc_istream **
|
|
*****************/
|
|
|
|
void Ipc_istream::_wait()
|
|
{
|
|
l4_umword_t label = 0;
|
|
addr_t rcv_cap_sel = _rcv_msg->rcv_cap_sel_base();
|
|
for (int i = 0; i < Msgbuf_base::MAX_CAP_ARGS; i++) {
|
|
l4_utcb_br()->br[i] = rcv_cap_sel | L4_RCV_ITEM_SINGLE_CAP;
|
|
rcv_cap_sel += L4_CAP_SIZE;
|
|
}
|
|
l4_utcb_br()->bdr = 0;
|
|
|
|
l4_msgtag_t tag;
|
|
do {
|
|
tag = l4_ipc_wait(l4_utcb(), &label, L4_IPC_NEVER);
|
|
} while (ipc_error(tag, DEBUG_MSG));
|
|
|
|
/* copy received label into message buffer */
|
|
_rcv_msg->label(label);
|
|
|
|
/* copy message from the UTCBs message registers to the receive buffer */
|
|
copy_utcb_to_msgbuf(tag, _rcv_msg);
|
|
|
|
/* reset unmarshaller */
|
|
_read_offset = sizeof(l4_mword_t);
|
|
}
|
|
|
|
|
|
Ipc_istream::Ipc_istream(Msgbuf_base *rcv_msg)
|
|
:
|
|
Ipc_unmarshaller(&rcv_msg->buf[0], rcv_msg->size()),
|
|
Native_capability((Cap_index*)Fiasco::l4_utcb_tcr()->user[Fiasco::UTCB_TCR_BADGE]),
|
|
_rcv_msg(rcv_msg)
|
|
{
|
|
_read_offset = sizeof(l4_mword_t);
|
|
}
|
|
|
|
|
|
Ipc_istream::~Ipc_istream() { }
|
|
|
|
|
|
/****************
|
|
** Ipc_client **
|
|
****************/
|
|
|
|
void Ipc_client::_call()
|
|
{
|
|
/* copy call message to the UTCBs message registers */
|
|
l4_msgtag_t tag = copy_msgbuf_to_utcb(_snd_msg, _write_offset, _dst);
|
|
|
|
addr_t rcv_cap_sel = _rcv_msg->rcv_cap_sel_base();
|
|
for (int i = 0; i < Msgbuf_base::MAX_CAP_ARGS; i++) {
|
|
l4_utcb_br()->br[i] = rcv_cap_sel | L4_RCV_ITEM_SINGLE_CAP;
|
|
rcv_cap_sel += L4_CAP_SIZE;
|
|
}
|
|
|
|
tag = l4_ipc_call(_dst.dst(), l4_utcb(), tag, L4_IPC_NEVER);
|
|
if (l4_ipc_error(tag, l4_utcb()) == L4_IPC_RECANCELED)
|
|
throw Genode::Blocking_canceled();
|
|
if (ipc_error(tag, DEBUG_MSG))
|
|
throw Genode::Ipc_error();
|
|
|
|
/* copy request message from the UTCBs message registers */
|
|
copy_utcb_to_msgbuf(tag, _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)
|
|
: 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);
|
|
}
|
|
|
|
|
|
void Ipc_server::_wait()
|
|
{
|
|
/* wait for new server request */
|
|
try { Ipc_istream::_wait(); } catch (Blocking_canceled) { }
|
|
|
|
/* we only have an unknown implicit reply capability */
|
|
/* _dst = ???; */
|
|
|
|
_prepare_next_reply_wait();
|
|
}
|
|
|
|
|
|
void Ipc_server::_reply()
|
|
{
|
|
l4_msgtag_t tag = copy_msgbuf_to_utcb(_snd_msg, _write_offset, _dst);
|
|
tag = l4_ipc_send(L4_SYSF_REPLY, l4_utcb(), tag, L4_IPC_SEND_TIMEOUT_0);
|
|
ipc_error(tag, DEBUG_MSG);
|
|
}
|
|
|
|
|
|
void Ipc_server::_reply_wait()
|
|
{
|
|
if (_reply_needed) {
|
|
l4_umword_t label;
|
|
addr_t rcv_cap_sel = _rcv_msg->rcv_cap_sel_base();
|
|
for (int i = 0; i < Msgbuf_base::MAX_CAP_ARGS; i++) {
|
|
l4_utcb_br()->br[i] = rcv_cap_sel | L4_RCV_ITEM_SINGLE_CAP;
|
|
rcv_cap_sel += L4_CAP_SIZE;
|
|
}
|
|
|
|
l4_utcb_br()->bdr &= ~L4_BDR_OFFSET_MASK;
|
|
|
|
l4_msgtag_t tag = copy_msgbuf_to_utcb(_snd_msg, _write_offset, _dst);
|
|
tag = l4_ipc_reply_and_wait(l4_utcb(), tag, &label, L4_IPC_SEND_TIMEOUT_0);
|
|
if (ipc_error(tag, false)) {
|
|
/*
|
|
* The error conditions could be a message cut (which
|
|
* we want to ignore on the server side) or a reply failure
|
|
* (for example, if the caller went dead during the call.
|
|
* In both cases, we do not reflect the error condition to
|
|
* the user but want to wait for the next proper incoming
|
|
* message. So let's just wait now.
|
|
*/
|
|
_wait();
|
|
} else {
|
|
|
|
/* copy received label into message buffer */
|
|
_rcv_msg->label(label);
|
|
|
|
/* copy request message from the UTCBs message registers */
|
|
copy_utcb_to_msgbuf(tag, _rcv_msg);
|
|
}
|
|
} else
|
|
_wait();
|
|
|
|
/* reply capability is implicit in fiasco.oc and unknown to us */
|
|
/* _dst = ???; */
|
|
_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)
|
|
{ }
|