2b161b7aff
Kernel patch: Introduce a transfer item type to express that a cap should be translated and if this fails to map it instead. It would be possible without this combined transfer item type however with additional overhead. In this case Genode/NOVA would have to map and translate all caps used as parameter in IPC. It would look like this: * If the map and translation succeed, the cap at the new cap index would have to be revoked. Then the translated cap index can be used. * If the map succeeds and the translation fails then the mapped cap index can be used. * It would become complicated when multiple caps are mapped and translated and only some of the translation succeed. In such cases Genode would have to figure out the right relation of translated/mapped and not translated/mapped caps. It would require to make some assumption about the order how translated/mapped caps are reported at the UTCB by the kernel. All the points above lead to the decision to create a separate transfer item type for that. Genode: Most the times the translation succeeds, mapping of caps happens either seldom. This takes now a bit the pressure of not enough aligned receive cap windows as described in issue #247. The patch mainly adds adjustments to handle the translated and mapped caps correctly especially during freeing of the receive window (don't free translated cap indexes). Fixes #268
209 lines
5.0 KiB
C++
209 lines
5.0 KiB
C++
/*
|
|
* \brief Implementation of the IPC API for NOVA
|
|
* \author Norman Feske
|
|
* \date 2009-10-02
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2009-2012 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/thread.h>
|
|
|
|
#include <base/printf.h>
|
|
|
|
/* NOVA includes */
|
|
#include <nova/syscalls.h>
|
|
|
|
using namespace Genode;
|
|
using namespace Nova;
|
|
|
|
|
|
/***************
|
|
** Utilities **
|
|
***************/
|
|
|
|
/**
|
|
* Copy message registers from UTCB to destination message buffer
|
|
*/
|
|
static void copy_utcb_to_msgbuf(Nova::Utcb *utcb, Msgbuf_base *rcv_msg)
|
|
{
|
|
size_t num_msg_words = utcb->msg_words();
|
|
if (num_msg_words == 0) return;
|
|
|
|
/* look up and validate destination message buffer to receive the payload */
|
|
mword_t *msg_buf = (mword_t *)rcv_msg->buf;
|
|
if (num_msg_words*sizeof(mword_t) > rcv_msg->size()) {
|
|
PERR("receive message buffer too small msg size=%zx, buf size=%zd",
|
|
num_msg_words*sizeof(mword_t), rcv_msg->size());
|
|
num_msg_words = rcv_msg->size()/sizeof(mword_t);
|
|
}
|
|
|
|
/* read message payload into destination message buffer */
|
|
mword_t *src = (mword_t *)(void *)(&utcb->msg[0]);
|
|
mword_t *dst = (mword_t *)&msg_buf[0];
|
|
for (unsigned i = 0; i < num_msg_words; i++)
|
|
*dst++ = *src++;
|
|
|
|
rcv_msg->rcv_reset();
|
|
}
|
|
|
|
|
|
/**
|
|
* Copy message payload to UTCB message registers
|
|
*/
|
|
static bool copy_msgbuf_to_utcb(Nova::Utcb *utcb, Msgbuf_base *snd_msg,
|
|
unsigned num_msg_words, mword_t local_name)
|
|
{
|
|
/* look up address and size of message payload */
|
|
mword_t *msg_buf = (mword_t *)snd_msg->buf;
|
|
|
|
/*
|
|
* XXX determine correct number of message registers
|
|
*/
|
|
enum { NUM_MSG_REGS = 256 };
|
|
if (num_msg_words > NUM_MSG_REGS) {
|
|
PERR("Message does not fit into UTCB message registers\n");
|
|
num_msg_words = NUM_MSG_REGS;
|
|
}
|
|
|
|
msg_buf[0] = local_name;
|
|
|
|
/* store message into UTCB message registers */
|
|
mword_t *src = (mword_t *)&msg_buf[0];
|
|
mword_t *dst = (mword_t *)(void *)&utcb->msg[0];
|
|
for (unsigned i = 0; i < num_msg_words; i++)
|
|
*dst++ = *src++;
|
|
|
|
utcb->set_msg_word(num_msg_words);
|
|
|
|
/* append portal capability selectors */
|
|
for (unsigned i = 0; i < snd_msg->snd_pt_sel_cnt(); i++) {
|
|
int pt_sel = snd_msg->snd_pt_sel(i);
|
|
if (pt_sel < 0) continue;
|
|
|
|
if (!utcb->append_item(Nova::Obj_crd(pt_sel, 0), i))
|
|
return false;
|
|
}
|
|
|
|
/* we have consumed portal capability selectors, reset message buffer */
|
|
snd_msg->snd_reset();
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/*****************
|
|
** Ipc_ostream **
|
|
*****************/
|
|
|
|
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(mword_t);
|
|
}
|
|
|
|
|
|
/*****************
|
|
** Ipc_istream **
|
|
*****************/
|
|
|
|
void Ipc_istream::_wait() { }
|
|
|
|
|
|
Ipc_istream::Ipc_istream(Msgbuf_base *rcv_msg)
|
|
: Ipc_unmarshaller(&rcv_msg->buf[0], rcv_msg->size()), _rcv_msg(rcv_msg)
|
|
{
|
|
_read_offset = sizeof(mword_t);
|
|
}
|
|
|
|
|
|
Ipc_istream::~Ipc_istream() { }
|
|
|
|
|
|
/****************
|
|
** Ipc_client **
|
|
****************/
|
|
|
|
void Ipc_client::_call()
|
|
{
|
|
Nova::Utcb *utcb = (Nova::Utcb *)Thread_base::myself()->utcb();
|
|
|
|
if (!copy_msgbuf_to_utcb(utcb, _snd_msg, _write_offset/sizeof(mword_t),
|
|
Ipc_ostream::_dst.local_name())) {
|
|
PERR("could not setup IPC");
|
|
return;
|
|
}
|
|
_rcv_msg->rcv_prepare_pt_sel_window(utcb);
|
|
|
|
/* establish the mapping via a portal traversal */
|
|
uint8_t res = Nova::call(Ipc_ostream::_dst.dst());
|
|
if (res) {
|
|
/* If an error occurred, reset word&item count (not done by kernel). */
|
|
utcb->set_msg_word(0);
|
|
PERR("call returned %u", res);
|
|
}
|
|
|
|
copy_utcb_to_msgbuf(utcb, _rcv_msg);
|
|
_snd_msg->snd_reset();
|
|
|
|
_write_offset = _read_offset = sizeof(mword_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::_wait()
|
|
{
|
|
/*
|
|
* This function is only called by the portal dispatcher of server
|
|
* entrypoint'. When the dispatcher is called, the incoming message already
|
|
* arrived so that we do not need to block. The only remaining thing to do
|
|
* is unmarshalling the arguments.
|
|
*/
|
|
|
|
Nova::Utcb *utcb = (Nova::Utcb *)Thread_base::myself()->utcb();
|
|
|
|
_rcv_msg->post_ipc(utcb);
|
|
copy_utcb_to_msgbuf(utcb, _rcv_msg);
|
|
|
|
/* reset unmarshaller */
|
|
_read_offset = sizeof(mword_t);
|
|
_write_offset = 2*sizeof(mword_t); /* leave space for the return value */
|
|
}
|
|
|
|
|
|
void Ipc_server::_reply()
|
|
{
|
|
Nova::Utcb *utcb = (Nova::Utcb *)Thread_base::myself()->utcb();
|
|
|
|
copy_msgbuf_to_utcb(utcb, _snd_msg, _write_offset/sizeof(mword_t),
|
|
Ipc_ostream::_dst.local_name());
|
|
|
|
Nova::reply(Thread_base::myself()->stack_top());
|
|
}
|
|
|
|
|
|
void Ipc_server::_reply_wait() { }
|
|
|
|
|
|
Ipc_server::Ipc_server(Msgbuf_base *snd_msg,
|
|
Msgbuf_base *rcv_msg)
|
|
: Ipc_istream(rcv_msg), Ipc_ostream(Native_capability(), snd_msg)
|
|
{ }
|