genode/repos/base/include/base/ipc.h

639 lines
13 KiB
C++

/*
* \brief Generic IPC infrastructure
* \author Norman Feske
* \date 2006-06-12
*
* Most of the marshalling and unmarshallung code is generic for IPC
* implementations among different platforms. In addition to the generic
* marshalling items, platform-specific marshalling items can be realized
* via specialized stream operators defined in the platform-specific
* 'base/ipc.h'. Hence, this header file is never to be included directly.
* It should only be included by a platform-specific 'base/ipc.h' file.
*/
/*
* Copyright (C) 2006-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.
*/
#ifndef _INCLUDE__BASE__IPC_GENERIC_H_
#define _INCLUDE__BASE__IPC_GENERIC_H_
#include <util/misc_math.h>
#include <util/string.h>
#include <base/exception.h>
#include <base/capability.h>
#include <base/ipc_msgbuf.h>
#include <base/rpc_args.h>
#include <base/printf.h>
namespace Genode {
enum Ipc_ostream_send { IPC_SEND };
enum Ipc_istream_wait { IPC_WAIT };
enum Ipc_client_call { IPC_CALL };
enum Ipc_server_reply { IPC_REPLY };
enum Ipc_server_reply_wait { IPC_REPLY_WAIT };
class Ipc_error;
class Ipc_marshaller;
class Ipc_unmarshaller;
class Ipc_ostream;
class Ipc_istream;
class Ipc_client;
class Ipc_server;
}
/**
* Exception type
*/
class Genode::Ipc_error : public Exception { };
/**
* Marshal arguments into send message buffer
*/
class Genode::Ipc_marshaller
{
protected:
char *_sndbuf;
size_t _sndbuf_size;
unsigned _write_offset;
protected:
/**
* Write value to send buffer
*/
template <typename T>
void _write_to_buf(T const &value)
{
/* check buffer range */
if (_write_offset + sizeof(T) >= _sndbuf_size) return;
/* write integer to buffer */
*reinterpret_cast<T *>(&_sndbuf[_write_offset]) = value;
/* increment write pointer to next dword-aligned value */
_write_offset += align_natural(sizeof(T));
}
/**
* Write bytes to send buffer
*/
void _write_to_buf(char const *src_addr, unsigned num_bytes)
{
/* check buffer range */
if (_write_offset + num_bytes >= _sndbuf_size) return;
/* copy buffer */
memcpy(&_sndbuf[_write_offset], src_addr, num_bytes);
/* increment write pointer to next dword-aligned value */
_write_offset += align_natural(num_bytes);
}
/**
* Write 'Rpc_in_buffer' to send buffer
*/
void _write_buffer_to_buf(Rpc_in_buffer_base const &b)
{
size_t size = b.size();
_write_to_buf(size);
_write_to_buf(b.base(), size);
}
/**
* Write array to send buffer
*/
template <typename T, size_t N>
void _write_to_buf(T const (&array)[N])
{
/* check buffer range */
if (_write_offset + sizeof(array) >= _sndbuf_size)
PERR("send buffer overrun");
memcpy(&_sndbuf[_write_offset], array, sizeof(array));
_write_offset += align_natural(sizeof(array));
}
public:
Ipc_marshaller(char *sndbuf, size_t sndbuf_size)
: _sndbuf(sndbuf), _sndbuf_size(sndbuf_size), _write_offset(0) { }
};
/**
* Unmarshal arguments from receive buffer
*/
class Genode::Ipc_unmarshaller
{
protected:
char *_rcvbuf;
size_t _rcvbuf_size;
unsigned _read_offset;
protected:
/**
* Read value of type T from buffer
*/
template <typename T>
void _read_from_buf(T &value)
{
/* check receive buffer range */
if (_read_offset + sizeof(T) >= _rcvbuf_size) return;
/* return value from receive buffer */
value = *reinterpret_cast<T *>(&_rcvbuf[_read_offset]);
/* increment read pointer to next dword-aligned value */
_read_offset += align_natural(sizeof(T));
}
/**
* Read 'Rpc_in_buffer' from receive buffer
*/
void _read_bytebuf_from_buf(Rpc_in_buffer_base &b)
{
size_t size = 0;
_read_from_buf(size);
b = Rpc_in_buffer_base(0, 0);
/*
* Check receive buffer range
*
* Note: The addr of the Rpc_in_buffer_base is a null pointer when this
* condition triggers.
*/
if (_read_offset + size >= _rcvbuf_size) {
PERR("message buffer overrun");
return;
}
b = Rpc_in_buffer_base(&_rcvbuf[_read_offset], size);
_read_offset += align_natural(size);
}
/**
* Read array from receive buffer
*/
template <typename T, size_t N>
void _read_from_buf(T (&array)[N])
{
if (_read_offset + sizeof(array) >= _rcvbuf_size) {
PERR("receive buffer overrun");
return;
}
memcpy(array, &_rcvbuf[_read_offset], sizeof(array));
_read_offset += align_natural(sizeof(array));
}
/**
* Read long value at specified byte index of receive buffer
*/
long _long_at_idx(int idx) { return *(long *)(&_rcvbuf[idx]); }
public:
Ipc_unmarshaller(char *rcvbuf, size_t rcvbuf_size)
: _rcvbuf(rcvbuf), _rcvbuf_size(rcvbuf_size), _read_offset(0) { }
};
/**
* Stream for sending information via a capability to an endpoint
*/
class Genode::Ipc_ostream : public Ipc_marshaller
{
protected:
Msgbuf_base *_snd_msg; /* send message buffer */
Native_capability _dst;
/**
* Reset marshaller and write badge at the beginning of the message
*/
void _prepare_next_send();
/**
* Send message in _snd_msg to _dst
*/
void _send();
/**
* Insert capability to message buffer
*/
void _marshal_capability(Native_capability const &cap);
public:
/**
* Constructor
*/
Ipc_ostream(Native_capability dst, Msgbuf_base *snd_msg);
/**
* Return true if Ipc_ostream is ready for send
*/
bool ready_for_send() const { return _dst.valid(); }
/**
* Insert value into send buffer
*/
template <typename T>
Ipc_ostream &operator << (T const &value)
{
_write_to_buf(value);
return *this;
}
/**
* Insert byte buffer to send buffer
*/
Ipc_ostream &operator << (Rpc_in_buffer_base const &b)
{
_write_buffer_to_buf(b);
return *this;
}
/**
* Insert capability to send buffer
*/
Ipc_ostream &operator << (Native_capability const &cap)
{
_marshal_capability(cap);
return *this;
}
/**
* Insert typed capability to send buffer
*/
template <typename IT>
Ipc_ostream &operator << (Capability<IT> const &typed_cap)
{
_marshal_capability(typed_cap);
return *this;
}
/**
* Issue the sending of the message buffer
*/
Ipc_ostream &operator << (Ipc_ostream_send)
{
_send();
return *this;
}
/**
* Return current 'IPC_SEND' destination
*
* This function is typically needed by a server than sends replies
* in a different order as the incoming calls.
*/
Native_capability dst() const { return _dst; }
/**
* Set destination for the next 'IPC_SEND'
*/
void dst(Native_capability const &dst) { _dst = dst; }
};
/**
* Stream for receiving information
*/
class Genode::Ipc_istream : public Ipc_unmarshaller, public Native_capability
{
private:
/**
* Prevent 'Ipc_istream' objects from being copied
*
* Copying an 'Ipc_istream' object would result in a duplicated
* (and possibly inconsistent) connection state both the original
* and the copied object.
*/
Ipc_istream(const Ipc_istream &);
protected:
Msgbuf_base *_rcv_msg;
Native_connection_state _rcv_cs;
/**
* Obtain capability from message buffer
*/
void _unmarshal_capability(Native_capability &cap);
protected:
/**
* Reset unmarshaller
*/
void _prepare_next_receive();
/**
* Wait for incoming message to be received in _rcv_msg
*/
void _wait();
public:
explicit Ipc_istream(Msgbuf_base *rcv_msg);
~Ipc_istream();
/**
* Read badge that was supplied with the message
*/
long badge() { return _long_at_idx(0); }
/**
* Block for an incoming message filling the receive buffer
*/
Ipc_istream &operator >> (Ipc_istream_wait)
{
_wait();
return *this;
}
/**
* Read values from receive buffer
*/
template <typename T>
Ipc_istream &operator >> (T &value)
{
_read_from_buf(value);
return *this;
}
/**
* Read byte buffer from receive buffer
*/
Ipc_istream &operator >> (Rpc_in_buffer_base &b)
{
_read_bytebuf_from_buf(b);
return *this;
}
/**
* Read byte buffer from receive buffer
*/
template <size_t MAX_BUFFER_SIZE>
Ipc_istream &operator >> (Rpc_in_buffer<MAX_BUFFER_SIZE> &b)
{
_read_bytebuf_from_buf(b);
return *this;
}
/**
* Read capability from receive buffer
*/
Ipc_istream &operator >> (Native_capability &cap)
{
_unmarshal_capability(cap);
return *this;
}
/**
* Read typed capability from receive buffer
*/
template <typename IT>
Ipc_istream &operator >> (Capability<IT> &typed_cap)
{
_unmarshal_capability(typed_cap);
return *this;
}
};
class Genode::Ipc_client: public Ipc_istream, public Ipc_ostream
{
protected:
int _result; /* result of most recent call */
void _prepare_next_call();
/**
* Send RPC message and wait for result
*/
void _call();
/**
* Set return value if call to server failed
*/
void ret(int retval)
{
*reinterpret_cast<int *>(&_rcvbuf[sizeof(umword_t)]) = retval;
}
public:
enum { ERR_INVALID_OBJECT = -70000, };
/**
* Constructor
*/
Ipc_client(Native_capability const &srv, Msgbuf_base *snd_msg,
Msgbuf_base *rcv_msg, unsigned short rcv_caps = ~0);
/**
* Operator that issues an IPC call
*
* \throw Ipc_error
* \throw Blocking_canceled
*/
Ipc_client &operator << (Ipc_client_call)
{
_call();
_read_from_buf(_result);
if (_result == ERR_INVALID_OBJECT)
throw Ipc_error();
return *this;
}
template <typename T>
Ipc_client &operator << (T const &value)
{
_write_to_buf(value);
return *this;
}
Ipc_client &operator << (Rpc_in_buffer_base const &b)
{
_write_buffer_to_buf(b);
return *this;
}
template <size_t MAX_BUFFER_SIZE>
Ipc_client &operator << (Rpc_in_buffer<MAX_BUFFER_SIZE> const &b)
{
_write_buffer_to_buf(b);
return *this;
}
Ipc_client &operator << (Native_capability const &cap)
{
_marshal_capability(cap);
return *this;
}
template <typename IT>
Ipc_client &operator << (Capability<IT> const &typed_cap)
{
_marshal_capability(typed_cap);
return *this;
}
Ipc_client &operator >> (Native_capability &cap)
{
_unmarshal_capability(cap);
return *this;
}
template <typename IT>
Ipc_client &operator >> (Capability<IT> &typed_cap)
{
_unmarshal_capability(typed_cap);
return *this;
}
template <typename T>
Ipc_client &operator >> (T &value)
{
_read_from_buf(value);
return *this;
}
Ipc_client &operator >> (Rpc_in_buffer_base &b)
{
_read_bytebuf_from_buf(b);
return *this;
}
int result() const { return _result; }
};
class Genode::Ipc_server : public Ipc_istream, public Ipc_ostream
{
protected:
bool _reply_needed; /* false for the first reply_wait */
void _prepare_next_reply_wait();
/**
* Wait for incoming call
*
* In constrast to 'Ipc_istream::_wait()', this function stores the
* next reply destination from into 'dst' of the 'Ipc_ostream'.
*/
void _wait();
/**
* Send reply to destination
*
* In contrast to 'Ipc_ostream::_send()', this function prepares
* the 'Ipc_server' to send another subsequent reply without the
* calling '_wait()' in between. This is needed when a server
* answers calls out of order.
*/
void _reply();
/**
* Send result of previous RPC request and wait for new one
*/
void _reply_wait();
public:
/**
* Constructor
*/
Ipc_server(Msgbuf_base *snd_msg, Msgbuf_base *rcv_msg);
/**
* Set return value of server call
*/
void ret(int retval)
{
*reinterpret_cast<int *>(&_sndbuf[sizeof(umword_t)]) = retval;
}
/**
* Set reply destination
*/
void dst(Native_capability const &reply_dst)
{
Ipc_ostream::dst(reply_dst);
_reply_needed = reply_dst.valid();
}
using Ipc_ostream::dst;
/**
* Block for an incoming message filling the receive buffer
*/
Ipc_server &operator >> (Ipc_istream_wait)
{
_wait();
return *this;
}
/**
* Issue the sending of the message buffer
*/
Ipc_server &operator << (Ipc_server_reply)
{
_reply();
return *this;
}
/**
* Reply current request and wait for a new one
*/
Ipc_server &operator >> (Ipc_server_reply_wait)
{
_reply_wait();
return *this;
}
/**
* Write value to send buffer
*
* This operator is only used by test programs
*/
template <typename T>
Ipc_server &operator << (T const &value)
{
_write_to_buf(value);
return *this;
}
/**
* Read value from receive buffer
*
* This operator should only be used by the server framework for
* reading the function offset. The server-side processing of the
* payload is done using 'Ipc_istream' and 'Ipc_ostream'.
*/
template <typename T>
Ipc_server &operator >> (T &value)
{
_read_from_buf(value);
return *this;
}
};
#endif /* _INCLUDE__BASE__IPC_GENERIC_H_ */