/* * \brief IPC implementation for Pistachio * \author Julian Stecklina * \author Norman Feske * \date 2008-01-28 */ /* * Copyright (C) 2008-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 #include namespace Pistachio { #include #include #include } using namespace Genode; using namespace Pistachio; #define VERBOSE_IPC 0 #if VERBOSE_IPC /* Just a printf wrapper for now. */ #define IPCDEBUG(msg, ...) { \ if (L4_Myself().raw == 0xf4001) { \ (void)printf("IPC (thread = 0x%x) " msg, \ L4_ThreadNo(Pistachio::L4_Myself()) \ , ##__VA_ARGS__); \ } else {} } #else #define IPCDEBUG(...) #endif /***************** ** Ipc_ostream ** *****************/ void Ipc_ostream::_send() { IPCDEBUG("_send to 0x%08lx.\n", Ipc_ostream::_dst.dst().raw); L4_Msg_t msg; L4_StringItem_t sitem = L4_StringItem(_write_offset, _snd_msg->buf); L4_Word_t local_name = Ipc_ostream::_dst.local_name(); L4_Clear(&msg); L4_Append(&msg, local_name); L4_Append(&msg, sitem); L4_Load(&msg); L4_MsgTag_t result = L4_Send(Ipc_ostream::_dst.dst()); /* * Error indicator * TODO Check what happened and print a nicer error message. */ if (L4_IpcFailed(result)) { PERR("ipc error in _send."); throw Genode::Ipc_error(); } IPCDEBUG("_send successful\n"); _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); IPCDEBUG("Ipc_ostream constructed.\n"); } /***************** ** Ipc_istream ** *****************/ /** * Assert that we got 1 untyped word and 2 typed words */ static inline void check_ipc_result(L4_MsgTag_t result, L4_Word_t error_code) { /* * Test for IPC cancellation via Core's cancel-blocking mechanism */ enum { ERROR_MASK = 0xe, ERROR_CANCELED = 3 << 1 }; if (L4_IpcFailed(result) && ((L4_ErrorCode() & ERROR_MASK) == ERROR_CANCELED)) throw Genode::Blocking_canceled(); /* * Provide diagnostic information on unexpected conditions */ if (L4_IpcFailed(result)) { PERR("Error in thread %08lx. IPC failed.", L4_Myself().raw); throw Genode::Ipc_error(); } if (L4_UntypedWords(result) != 1) { PERR("Error in thread %08lx. Expected one untyped word (local_name), but got %lu.\n", L4_Myself().raw, L4_UntypedWords(result)); PERR("This should not happen. Inspect!"); throw Genode::Ipc_error(); } if (L4_TypedWords(result) != 2) { PERR("Error. Expected two typed words (a string item). but got %lu.\n", L4_TypedWords(result)); PERR("This should not happen. Inspect!"); throw Genode::Ipc_error(); } } void Ipc_istream::_wait() { L4_MsgTag_t result; L4_MsgBuffer_t msgbuf; IPCDEBUG("_wait.\n"); retry: IPCDEBUG("_wait loop start (more than once means IpcError)\n"); L4_Clear (&msgbuf); L4_Append (&msgbuf, L4_StringItem (_rcv_msg->size(), _rcv_msg->buf)); L4_Accept(L4_UntypedWordsAcceptor); L4_Accept(L4_StringItemsAcceptor, &msgbuf); // Wait for message. result = L4_Wait(&_rcv_cs); if (L4_IpcFailed(result)) goto retry; IPCDEBUG("Got something from 0x%x.\n", _rcv_cs); L4_Msg_t msg; L4_Store(result, &msg); check_ipc_result(result, L4_ErrorCode()); /* get the local name */ L4_Word_t local_name = L4_Get(&msg,0); /* * Store local_name where badge() looks for it. * XXX Check this... */ *((long *)_rcv_msg->buf) = local_name; _read_offset = sizeof(umword_t); IPCDEBUG("_wait successful\n"); } Ipc_istream::Ipc_istream(Msgbuf_base *rcv_msg) : Ipc_unmarshaller(&rcv_msg->buf[0], rcv_msg->size()), Native_capability(Pistachio::L4_Myself(), 0), _rcv_msg(rcv_msg) { IPCDEBUG("Ipc_istream constructed.\n"); _rcv_cs = L4_nilthread; _read_offset = sizeof(umword_t); } Ipc_istream::~Ipc_istream() { } /**************** ** Ipc_client ** ****************/ void Ipc_client::_call() { IPCDEBUG("Starting to _call (with %u bytes of data).\n", _write_offset); L4_Msg_t msg; L4_StringItem_t sitem = L4_StringItem(_write_offset, _snd_msg->buf); L4_Word_t local_name = Ipc_ostream::_dst.local_name(); IPCDEBUG("Destination local_name = 0x%x\n", local_name); L4_MsgBuffer_t msgbuf; /* prepare message buffer */ L4_Clear (&msgbuf); L4_Append (&msgbuf, L4_StringItem (_rcv_msg->size(), _rcv_msg->buf)); L4_Accept(L4_UntypedWordsAcceptor); L4_Accept(L4_StringItemsAcceptor, &msgbuf); /* prepare sending parameters */ L4_Clear(&msg); L4_Append(&msg, local_name); L4_Append(&msg, sitem); L4_Load(&msg); L4_MsgTag_t result = L4_Call(Ipc_ostream::_dst.dst()); _write_offset = _read_offset = sizeof(umword_t); check_ipc_result(result, L4_ErrorCode()); IPCDEBUG("Call done.\n"); } 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) { IPCDEBUG("Ipc_client constructed.\n"); } /**************** ** 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 */ Ipc_ostream::_dst = Native_capability(_rcv_cs, badge()); _prepare_next_reply_wait(); } void Ipc_server::_reply() { L4_Msg_t msg; L4_StringItem_t sitem = L4_StringItem(_write_offset, _snd_msg->buf); L4_Word_t local_name = Ipc_ostream::_dst.local_name(); L4_Clear(&msg); L4_Append(&msg, local_name); L4_Append(&msg, sitem); L4_Load(&msg); L4_MsgTag_t result = L4_Reply(Ipc_ostream::_dst.dst()); if (L4_IpcFailed(result)) PERR("ipc error in _reply, ignored"); _prepare_next_reply_wait(); } void Ipc_server::_reply_wait() { IPCDEBUG("Starting to _reply_wait. (with %u bytes of data)\n", _reply_needed ? _write_offset : 0); if (_reply_needed) { /* prepare massage */ L4_Msg_t msg; L4_StringItem_t sitem = L4_StringItem(_write_offset, _snd_msg->buf); L4_Word_t local_name = Ipc_ostream::_dst.local_name(); L4_Clear(&msg); L4_Append(&msg, local_name); L4_Append(&msg, sitem); L4_Load(&msg); /* Prepare message buffer */ L4_MsgBuffer_t msgbuf; L4_Clear(&msgbuf); L4_Append(&msgbuf, L4_StringItem (_rcv_msg->size(), _rcv_msg->buf)); L4_Accept(L4_UntypedWordsAcceptor); L4_Accept(L4_StringItemsAcceptor, &msgbuf); L4_MsgTag_t result = L4_Ipc(Ipc_ostream::_dst.dst(), L4_anythread, L4_Timeouts(L4_ZeroTime, L4_Never), &_rcv_cs); IPCDEBUG("Got something from 0x%x.\n", L4_ThreadNo(L4_GlobalId(_rcv_cs))); /* error handling - check whether send or receive failed */ if (L4_IpcFailed(result)) { L4_Word_t errcode = L4_ErrorCode(); L4_Word_t phase = errcode & 1; L4_Word_t error = (errcode & 0xF) >> 1; PERR("IPC %s error %02lx, offset %08lx -> _wait() instead.", phase ? "receive" : "send", error, errcode >> 4); _wait(); return; } L4_Clear(&msg); L4_Store(result, &msg); try { check_ipc_result(result, L4_ErrorCode()); } catch (...) { /* * If something went wrong, just call _wait instead of relaying * the error to the user. */ IPCDEBUG("Bad IPC content -> _wait() instead.\n"); _wait(); return; } /* get the local name */ local_name = L4_Get(&msg, 0); /* * Store local_name where badge() looks for it. * XXX Check this... */ *((long *)_rcv_msg->buf) = local_name; IPCDEBUG("local_name = 0x%lx\n", badge()); /* define destination of next reply */ Ipc_ostream::_dst = Native_capability(_rcv_cs, badge()); _prepare_next_reply_wait(); } else _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) { }