/* * \brief IPC implementation for Fiasco * \author Norman Feske * \date 2006-06-13 */ /* * Copyright (C) 2006-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. */ #include #include #include namespace Fiasco { #include #include #include } using namespace Genode; /***************** ** Ipc_ostream ** *****************/ void Ipc_ostream::_send() { using namespace Fiasco; _snd_msg->send_dope = L4_IPC_DOPE((_write_offset + sizeof(umword_t) - 1)>>2, 0); l4_msgdope_t result; l4_ipc_send(_dst.dst(), _snd_msg->addr(), _dst.local_name(), *reinterpret_cast(&_snd_msg->buf[sizeof(umword_t)]), L4_IPC_NEVER, &result); if (L4_IPC_IS_ERROR(result)) { PERR("Ipc error %lx", L4_IPC_ERROR(result)); throw Genode::Ipc_error(); } _write_offset = sizeof(umword_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(umword_t); } /***************** ** Ipc_istream ** *****************/ void Ipc_istream::_wait() { using namespace Fiasco; l4_msgdope_t result; /* * Wait until we get a proper message and thereby * ignore receive message cuts on the server-side. * This error condition should be handled by the * client. The server does not bother. */ do { _rcv_msg->size_dope = L4_IPC_DOPE(_rcv_msg->size()>>2, 0); l4_ipc_wait(&_rcv_cs, _rcv_msg->addr(), reinterpret_cast(&_rcv_msg->buf[0]), reinterpret_cast(&_rcv_msg->buf[sizeof(umword_t)]), L4_IPC_NEVER, &result); if (L4_IPC_IS_ERROR(result)) PERR("Ipc error %lx", L4_IPC_ERROR(result)); } while (L4_IPC_IS_ERROR(result)); /* reset buffer read offset */ _read_offset = sizeof(umword_t); } Ipc_istream::Ipc_istream(Msgbuf_base *rcv_msg): Ipc_unmarshaller(&rcv_msg->buf[0], rcv_msg->size()), Native_capability(Fiasco::l4_myself(), 0), _rcv_msg(rcv_msg) { using namespace Fiasco; _rcv_cs = L4_INVALID_ID; _read_offset = sizeof(umword_t); } Ipc_istream::~Ipc_istream() { } /**************** ** Ipc_client ** ****************/ void Ipc_client::_call() { using namespace Fiasco; l4_msgdope_t ipc_result; long rec_badge; _snd_msg->send_dope = L4_IPC_DOPE((_write_offset + 2*sizeof(umword_t) - 1)>>2, 0); _rcv_msg->size_dope = L4_IPC_DOPE(_rcv_msg->size()>>2, 0); l4_ipc_call(_dst.dst(), _write_offset <= 2*sizeof(umword_t) ? L4_IPC_SHORT_MSG : _snd_msg->addr(), _dst.local_name(), *reinterpret_cast(&_snd_msg->buf[sizeof(umword_t)]), _rcv_msg->addr(), reinterpret_cast(&rec_badge), reinterpret_cast(&_rcv_msg->buf[sizeof(umword_t)]), L4_IPC_NEVER, &ipc_result); if (L4_IPC_IS_ERROR(ipc_result)) { if (L4_IPC_ERROR(ipc_result) == L4_IPC_RECANCELED) throw Genode::Blocking_canceled(); PERR("Ipc error %lx", L4_IPC_ERROR(ipc_result)); throw Genode::Ipc_error(); } /* * Reset buffer read and write offsets. We shadow the first mword of the * send message buffer (filled via '_write_offset') with the local name of * the invoked remote object. We shadow the first mword of the receive * buffer (retrieved via '_read_offset') with the local name of the reply * capability ('rec_badge'), which is bogus in the L4/Fiasco case. In both * cases, we skip the shadowed message mword when reading/writing the * message payload. */ _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) { } /* define destination of next reply */ _dst = Native_capability(_rcv_cs, badge()); _prepare_next_reply_wait(); } void Ipc_server::_reply() { using namespace Fiasco; _snd_msg->send_dope = L4_IPC_DOPE((_write_offset + sizeof(umword_t) - 1)>>2, 0); l4_msgdope_t result; l4_ipc_send(_dst.dst(), _snd_msg->addr(), _dst.local_name(), *reinterpret_cast(&_snd_msg->buf[sizeof(umword_t)]), L4_IPC_SEND_TIMEOUT_0, &result); if (L4_IPC_IS_ERROR(result)) PERR("Ipc error %lx, ignored", L4_IPC_ERROR(result)); _prepare_next_reply_wait(); } void Ipc_server::_reply_wait() { using namespace Fiasco; if (_reply_needed) { l4_msgdope_t ipc_result; _snd_msg->send_dope = L4_IPC_DOPE((_write_offset + sizeof(umword_t) - 1)>>2, 0); _rcv_msg->size_dope = L4_IPC_DOPE(_rcv_msg->size()>>2, 0); /* * Use short IPC for reply if possible. * This is the common case of returning * an integer as RPC result. */ l4_ipc_reply_and_wait( _dst.dst(), _write_offset <= 2*sizeof(umword_t) ? L4_IPC_SHORT_MSG : _snd_msg->addr(), _dst.local_name(), *reinterpret_cast(&_snd_msg->buf[sizeof(umword_t)]), &_rcv_cs, _rcv_msg->addr(), reinterpret_cast(&_rcv_msg->buf[0]), reinterpret_cast(&_rcv_msg->buf[sizeof(umword_t)]), L4_IPC_SEND_TIMEOUT_0, &ipc_result); if (L4_IPC_IS_ERROR(ipc_result)) { PERR("Ipc error %lx", L4_IPC_ERROR(ipc_result)); /* * 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 _wait(); /* define destination of next reply */ _dst = Native_capability(_rcv_cs, badge()); _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) { }