genode/base-nova/src/base/ipc/ipc.cc

210 lines
5.1 KiB
C++
Raw Normal View History

2011-12-22 16:19:25 +01:00
/*
* \brief Implementation of the IPC API for NOVA
* \author Norman Feske
* \date 2009-10-02
*/
/*
2013-01-10 21:44:47 +01:00
* Copyright (C) 2009-2013 Genode Labs GmbH
2011-12-22 16:19:25 +01:00
*
* 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",
2011-12-22 16:19:25 +01:00
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++;
}
/**
* Copy message payload to UTCB message registers
*/
static bool copy_msgbuf_to_utcb(Nova::Utcb *utcb, Msgbuf_base *snd_msg,
2011-12-22 16:19:25 +01:00
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++) {
bool trans_map = true;
Nova::Obj_crd crd = snd_msg->snd_pt_sel(i, trans_map);
if (crd.base() == ~0UL) continue;
2011-12-22 16:19:25 +01:00
if (!utcb->append_item(crd, i, false, false, trans_map))
return false;
2011-12-22 16:19:25 +01:00
}
/* we have consumed portal capability selectors, reset message buffer */
snd_msg->snd_reset();
return true;
2011-12-22 16:19:25 +01:00
}
/*****************
** 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, Ipc_ostream::_dst.rcv_window());
2011-12-22 16:19:25 +01:00
/* establish the mapping via a portal traversal */
uint8_t res = Nova::call(Ipc_ostream::_dst.local_name());
if (res != Nova::NOVA_OK) {
/* If an error occurred, reset word&item count (not done by kernel). */
utcb->set_msg_word(0);
/* set return value for ipc_generic part if call failed */
ret(ERR_INVALID_OBJECT);
}
2011-12-22 16:19:25 +01:00
_rcv_msg->post_ipc(utcb);
2011-12-22 16:19:25 +01:00
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);
2011-12-22 16:19:25 +01:00
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());
2011-12-22 16:19:25 +01:00
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)
{ }