// vi:ft=cpp /** * \file * \brief IPC stream */ /* * (c) 2008-2009 Adam Lackorzynski , * Alexander Warg , * Torsten Frenzel * economic rights: Technische Universität Dresden (Germany) * * This file is part of TUD:OS and distributed under the terms of the * GNU General Public License 2. * Please see the COPYING-GPL-2 file for details. * * As a special exception, you may use this file as part of a free software * library without restriction. Specifically, if other files instantiate * templates or use macros or inline functions from this file, or you compile * this file and link it with other files to produce an executable, this * file does not by itself cause the resulting executable to be covered by * the GNU General Public License. This exception does not however * invalidate any other reasons why the executable file might be covered by * the GNU General Public License. */ #pragma once #include #include #include #include #define L4_CXX_IPC_BACKWARD_COMPAT namespace L4 { typedef int Opcode; namespace Ipc { class Ostream; class Istream; namespace Internal { /** * \brief Abstraction for inserting an array into an Ipc::Ostream. * \ingroup ipc_fw * \internal * * An object of Buf_cp_out can be used to insert an array of arbitrary values, * that can be inserted into an Ipc::Ostream individually. * The array is therefore copied to the message buffer, in contrast to * data handled with Msg_out_buffer or Msg_io_buffer. * * On insertion into the Ipc::Ostream exactly the given number of elements * of type T are copied to the message buffer, this means the source buffer * is no longer referenced after insertion into the stream. * * You should use buf_cp_out() to create instances of Buf_cp_out. * * The counterpart is either Buf_cp_in (buf_cp_in()) or Buf_in (buf_in()). */ template< typename T > class Buf_cp_out { public: /** * \brief Create a buffer object for the given array. * \param v The pointer to the array with size elements of type T. * \param size the number of elements in the array. */ Buf_cp_out(T const *v, unsigned long size) : _v(v), _s(size) {} /** * \brief Get the number of elements in the array. * \note This function is usually used by the Ipc::Ostream itself. */ unsigned long size() const { return _s; } /** * \brief Get the pointer to the array. * \note This function is usually used by the Ipc::Ostream itself. */ T const *buf() const { return _v; } private: friend class Ostream; T const *_v; unsigned long _s; }; } /** * \brief Insert an array into an Ipc::Ostream. * \ingroup ipc_fw * * \param v Pointer to the array that shall be inserted into an Ipc::Ostream. * \param size Number of elements in the array. * * This function inserts an array (e.g. a string) into an Ipc::Ostream. * The data is copied to the stream. On insertion into the Ipc::Ostream * exactly the given number of elements of type T are copied to the message * buffer, this means the source buffer is no longer referenced after * insertion into the stream. * * \see The counterpart is either buf_cp_in() or buf_in(). */ template< typename T > Internal::Buf_cp_out buf_cp_out(T const *v, unsigned long size) { return Internal::Buf_cp_out(v, size); } namespace Internal { /** * \brief Abstraction for extracting array from an Ipc::Istream. * \ingroup ipc_fw * \internal * * An instance of Buf_cp_in can be used to extract an array from * an Ipc::Istream. This is the counterpart to the Buf_cp_out abstraction. * The data from the received message is thereby copied to the given buffer * and size is set to the number of elements found in the stream. * To avoid the copy operation Buf_in may be used instead. * * \see buf_cp_in(), Buf_in, buf_in(), Buf_cp_out, and buf_cp_out(). */ template< typename T > class Buf_cp_in { public: /** * \brief Create a buffer for extracting an array from an Ipc::Istream. * \param v The buffer for array (copy in). * \param size Input: the number of elements the array can take at most
* Output: the number of elements found in the stream. */ Buf_cp_in(T *v, unsigned long &size) : _v(v), _s(&size) {} unsigned long &size() const { return *_s; } T *buf() const { return _v; } private: friend class Istream; T *_v; unsigned long *_s; }; } /** * \brief Extract an array from an Ipc::Istream. * \ingroup ipc_fw * * \param v Pointer to the array that shall receive the values from the * Ipc::Istream. * \param size Input: the number of elements the array can take at most
* Output: the number of elements found in the stream. * * buf_cp_in() can be used to extract an array from an Ipc::Istream. This is * the counterpart buf_cp_out(). The data from the received message is * thereby copied to the given buffer and size is set to the number of * elements found in the stream. To avoid the copy operation buf_in() may be * used instead. * * \see buf_in() and buf_cp_out(). */ template< typename T > Internal::Buf_cp_in buf_cp_in(T *v, unsigned long &size) { return Internal::Buf_cp_in(v, size); } /** * \brief Abstraction for extracting a zero-termintaed string from * an Ipc::Istream. * \ingroup ipc_fw * * An instance of Str_cp_in can be used to extract a zero-terminated string * an Ipc::Istream. The data from the received message is thereby copied to the * given buffer and size is set to the number of characters found in the * stream. The string is zero terminated in any circumstances. When the given * buffer is smaller than the received string the last byte in the buffer will * be the zero terminator. In the case the received string is shorter than the * given buffer the zero termination will be placed behind the received data. * This provides a zero-terminated result even in cases where the sender did * not provide proper termination or in cases of too small receiver buffers. * * \see str_cp_in(). */ template< typename T > class Str_cp_in { public: /** * \brief Create a buffer for extracting an array from an Ipc::Istream. * \param v The buffer for string. * \param[in] size the number of bytes available in \a v
* \param[out] size the number of bytes received (including the terminator). */ Str_cp_in(T *v, unsigned long &size) : _v(v), _s(&size) {} unsigned long &size() const { return *_s; } T *buf() const { return _v; } private: friend class Istream; T *_v; unsigned long *_s; }; /** * \brief Create a Str_cp_in for the given values. * \ingroup ipc_fw * * This function makes it more convenient to extract arrays from an * Ipc::Istream (\see Str_cp_in.) * * \param v Pointer to the array that shall receive the values from the * Ipc::Istream. * \param size Input: the number of elements the array can take at most
* Output: the number of elements found in the stream. */ template< typename T > Str_cp_in str_cp_in(T *v, unsigned long &size) { return Str_cp_in(v, size); } /** * \brief Pointer to an element of type T in an Ipc::Istream. * \ingroup ipc_fw * * This wrapper can be used to extract an element of type T from an * Ipc::Istream, whereas the data is not copied out, but a pointer into * the message buffer itself is returned. With is mechanism it is possible * to avoid an extra copy of large data structures from a received IPC * message, instead the returned pointer gives direct access to the data * in the message. * * See msg_ptr(). */ template< typename T > class Msg_ptr { private: T **_p; public: /** * \brief Create a Msg_ptr object that set pointer p to point into the * message buffer. * \param p The pointer that is adjusted to point into the message buffer. */ explicit Msg_ptr(T *&p) : _p(&p) {} void set(T *p) const { *_p = p; } }; /** * \brief Create an Msg_ptr to adjust the given pointer. * \ingroup ipc_fw * * This function makes it more convenient to extract pointers to data in the * message buffer itself from an Ipc::Istream. * This may be used to avoid copy out of large data structures. * (See Msg_ptr.) */ template< typename T > Msg_ptr msg_ptr(T *&p) { return Msg_ptr(p); } namespace Internal { /** * \brief Abstraction to extract an array from an Ipc::Istream. * \ingroup ipc_fw * \internal * * This wrapper provides a possibility to extract an array from an * Ipc::Istream, without extra copy overhead. In contrast to Buf_cp_in * the data is not copied to a buffer, but a pointer to the array is returned. * * The mechanism is comparable to that of Msg_ptr, however it handles arrays * inserted with Buf_cp_out. * * See buf_in(), Buf_cp_out, buf_cp_out(), Buf_cp_in, and buf_cp_in(). */ template< typename T > class Buf_in { public: /** * \brief Create an Buf_in to adjust a pointer to the array and the size * of the array. * \param v The pointer to adjust to the first element of the array. * \param size The number of elements found in the stream. */ Buf_in(T *&v, unsigned long &size) : _v(&v), _s(&size) {} void set_size(unsigned long s) const { *_s = s; } T *&buf() const { return *_v; } private: friend class Istream; T **_v; unsigned long *_s; }; } /** * \brief Return a pointer to stream array data. * \ingroup ipc_fw * * \param v Output: pointer to the array within the Ipc::Istream. * \param size Output: the number of elements found in the stream. * * This routine provdes a possibility to extract an array from an * Ipc::Istream, without extra copy overhead. In contrast to buf_cp_in() * the data is not copied to a buffer, but a pointer to the array is returned. * The user must make sure the UTCB is not used for other purposes while the * returned pointer is still in use. * * The mechanism is comparable to that of Msg_ptr, however it handles arrays * inserted with buf_cp_out(). * * \see buf_cp_in() and buf_cp_out(). */ template< typename T > Internal::Buf_in buf_in(T *&v, unsigned long &size) { return Internal::Buf_in(v, size); } /** * \brief A receive item for receiving a single capability. * * This class is the main abstraction for receiving capabilities * via Ipc::Istream. To receive a capability an instance of Small_buf * that refers to an empty capability slot must be inserted into the * Ipc::Istream before the receive operation. */ class Small_buf { public: explicit Small_buf(L4::Cap cap, unsigned long flags = 0) : _data(cap.cap() | L4_RCV_ITEM_SINGLE_CAP | flags) {} explicit Small_buf(l4_cap_idx_t idx, unsigned long flags = 0) : _data(idx | L4_RCV_ITEM_SINGLE_CAP | flags) {} private: l4_umword_t _data; }; class Snd_item { public: Snd_item(l4_umword_t base, l4_umword_t data) : _base(base), _data(data) {} protected: l4_umword_t _base; l4_umword_t _data; }; class Buf_item { public: Buf_item(l4_umword_t base, l4_umword_t data) : _base(base), _data(data) {} protected: l4_umword_t _base; l4_umword_t _data; }; template< typename T > class Gen_fpage : public T { public: enum Type { Special = L4_FPAGE_SPECIAL << 4, Memory = L4_FPAGE_MEMORY << 4, Io = L4_FPAGE_IO << 4, Obj = L4_FPAGE_OBJ << 4 }; enum Map_type { Map = L4_MAP_ITEM_MAP, Grant = L4_MAP_ITEM_GRANT, }; enum Cacheopt { None = 0, Cached = L4_FPAGE_CACHEABLE << 4, Buffered = L4_FPAGE_BUFFERABLE << 4, Uncached = L4_FPAGE_UNCACHEABLE << 4 }; enum Continue { Single = 0, Last = 0, More = L4_ITEM_CONT, Compound = L4_ITEM_CONT, }; private: Gen_fpage(Type type, l4_addr_t base, int order, unsigned char rights, l4_addr_t snd_base, Map_type map_type, Cacheopt cache, Continue cont) : T(L4_ITEM_MAP | (snd_base & (~0UL << 10)) | l4_umword_t(map_type) | l4_umword_t(cache) | l4_umword_t(cont), base | l4_umword_t(type) | rights | (l4_umword_t(order) << 6)) {} public: Gen_fpage() : T(0, 0) {} Gen_fpage(l4_fpage_t const &fp, l4_addr_t snd_base = 0, Map_type map_type = Map, Cacheopt cache = None, Continue cont = Last) : T(L4_ITEM_MAP | (snd_base & (~0UL << 10)) | l4_umword_t(map_type) | l4_umword_t(cache) | l4_umword_t(cont), fp.raw) {} Gen_fpage(L4::Cap cap, unsigned rights) : T(L4_ITEM_MAP | Map | (rights & 0xf0), cap.fpage(rights).raw) {} static Gen_fpage obj(l4_addr_t base, int order, unsigned char rights, l4_addr_t snd_base = 0, Map_type map_type = Map, Continue cont = Last) { return Gen_fpage(Obj, base << 12, order, rights, snd_base, map_type, None, cont); } static Gen_fpage mem(l4_addr_t base, int order, unsigned char rights, l4_addr_t snd_base = 0, Map_type map_type = Map, Cacheopt cache = None, Continue cont = Last) { return Gen_fpage(Memory, base, order, rights, snd_base, map_type, cache, cont); } static Gen_fpage io(l4_addr_t base, int order, unsigned char rights, l4_addr_t snd_base = 0, Map_type map_type = Map, Continue cont = Last) { return Gen_fpage(Io, base << 12, order, rights, snd_base, map_type, None, cont); } unsigned order() const { return (T::_data >> 6) & 0x3f; } unsigned snd_order() const { return (T::_data >> 6) & 0x3f; } unsigned rcv_order() const { return (T::_base >> 6) & 0x3f; } l4_addr_t base() const { return T::_data & (~0UL << 12); } l4_addr_t snd_base() const { return T::_base & (~0UL << 10); } void snd_base(l4_addr_t b) { T::_base = (T::_base & ~(~0UL << 10)) | (b & (~0UL << 10)); } bool is_valid() const { return T::_base & L4_ITEM_MAP; } // A cap has been mapped bool cap_received() const { return (T::_base & 0x3e) == 0x38; } // the label out of an IPC gate has been received, L4_RCV_ITEM_LOCAL_ID // must be specified, and the IPC gate must be local to the receiver // (i.e., the target thread of the IPC gate is in the same task as the // receiving thread) bool id_received() const { return (T::_base & 0x3e) == 0x3c; } // Sender and receiver are in the same task, set only iff // L4_RCV_ITEM_LOCAL_ID has been specified by the receiver, the value // delivered is the cap-index (in the task) bool local_id_received() const { return (T::_base & 0x3e) == 0x3e; } l4_umword_t data() const { return T::_data; } l4_umword_t base_x() const { return T::_base; } }; typedef Gen_fpage Snd_fpage; typedef Gen_fpage Rcv_fpage; #ifdef L4_CXX_IPC_SUPPORT_STRINGS template class Gen_string : public T { public: Gen_string() : T(0, 0) {} Gen_string(B buf, unsigned long size) : T(size << 10, l4_umword_t(buf)) {} unsigned long len() const { return T::_base >> 10; } }; typedef Gen_string Snd_string; typedef Gen_string Rcv_string; #endif template< typename T, template class B > struct Generic_va_type : public B { enum { Id = B::Id }; typedef B ID; typedef T const &Ret_value; typedef T Value; static Ret_value value(void const *d) { return *reinterpret_cast(d); } static void const *addr_of(Value const &v) { return &v; } static unsigned size(void const *) { return sizeof(T); } static L4_varg_type unsigned_id() { return (L4_varg_type)(Id & ~L4_VARG_TYPE_SIGN); } static L4_varg_type signed_id() { return (L4_varg_type)(Id | L4_VARG_TYPE_SIGN); } static L4_varg_type id() { return (L4_varg_type)Id; } }; template< typename T > struct Va_type_id; template<> struct Va_type_id { enum { Id = L4_VARG_TYPE_UMWORD }; }; template<> struct Va_type_id { enum { Id = L4_VARG_TYPE_MWORD }; }; template<> struct Va_type_id { enum { Id = L4_VARG_TYPE_FPAGE }; }; template<> struct Va_type_id { enum { Id = L4_VARG_TYPE_NIL }; }; template<> struct Va_type_id { enum { Id = L4_VARG_TYPE_STRING }; }; template< typename T > struct Va_type; template<> struct Va_type : public Generic_va_type {}; template<> struct Va_type : public Generic_va_type {}; template<> struct Va_type : public Generic_va_type {}; template<> struct Va_type { typedef void Ret_value; typedef void Value; static void const *addr_of(void) { return 0; } static void value(void const *) {} static L4_varg_type id() { return L4_VARG_TYPE_NIL; } static unsigned size(void const *) { return 0; } }; template<> struct Va_type { typedef char const *Ret_value; typedef char const *Value; static void const *addr_of(Value v) { return v; } static L4_varg_type id() { return L4_VARG_TYPE_STRING; } static unsigned size(void const *s) { char const *_s = reinterpret_cast(s); int l = 1; while (*_s) { ++_s; ++l; } return l; } static Ret_value value(void const *d) { return (char const *)d; } }; class Varg { private: l4_umword_t _tag; char const *_d; public: typedef l4_umword_t Tag; L4_varg_type type() const { return (L4_varg_type)(_tag & 0xff); } int length() const { return _tag >> 16; } void tag(Tag tag) { _tag = tag; } Tag tag() const { return _tag; } void data(char const *d) { _d = d; } char const *data() const { return _d; } char const *&data() { return _d; } Varg() : _tag(0), _d(0) {} Varg(L4_varg_type t, void const *v, int len) : _tag(t | ((l4_mword_t)len << 16)), _d((char const *)v) {} template< typename V > typename Va_type::Ret_value value() const { return Va_type::value(_d); } template< typename T > bool is_of() const { return Va_type::id() == type(); } bool is_nil() const { return is_of(); } bool is_of_int() const { return (type() & ~L4_VARG_TYPE_SIGN) == L4_VARG_TYPE_UMWORD; } template< typename T > bool get_value(typename Va_type::Value *v) const { if (!is_of()) return false; *v = Va_type::value(_d); return true; } template< typename T > void set_value(void const *d) { typedef Va_type Vt; _tag = Vt::id() | (Vt::size(d) << 16); _d = (char const *)d; } }; template class Varg_t : public Varg { public: typedef typename Va_type::Value Value; explicit Varg_t(Value v) : Varg() { _data = v; set_value(Va_type::addr_of(_data)); } private: Value _data; }; namespace Utcb_stream_check { static bool check_utcb_data_offset(unsigned sz) { return sz > sizeof(l4_umword_t) * L4_UTCB_GENERIC_DATA_SIZE; } } /** * \brief Input stream for IPC unmarshalling. * \ingroup ipc_fw * * Ipc::Istream is part of the dynamic IPC marshalling infrastructure, as well * as Ipc::Ostream and Ipc::Iostream. * * Ipc::Istream is an input stream supporting extraction of values from an * IPC message buffer. A received IPC message can be unmarshalled using the * usual extraction operator (>>). * * There exist some special wrapper classes to extract arrays (see * Ipc_buf_cp_in and Ipc_buf_in) and indirect strings (see Msg_in_buffer and * Msg_io_buffer). */ class Istream { public: /** * \brief Create an input stream for the given message buffer. * * The given message buffer is used for IPC operations wait()/receive() * and received data can be extracted using the >> operator afterwards. * In the case of indirect message parts a buffer of type Msg_in_buffer * must be inserted into the stream before the IPC operation and contains * received data afterwards. * * \param msg The message buffer to receive IPC messages. */ Istream(l4_utcb_t *utcb) : _tag(), _utcb(utcb), _current_msg(reinterpret_cast(l4_utcb_mr_u(utcb)->mr)), _pos(0), _current_buf(0) {} /** * \brief Reset the stream to empty, and ready for receive()/wait(). * The stream is reset to the same state as on its creation. */ void reset() { _pos = 0; _current_buf = 0; _current_msg = reinterpret_cast(l4_utcb_mr_u(_utcb)->mr); } /** * \brief Check whether a value of type T can be obtained from the stream. */ template< typename T > bool has_more() { unsigned apos = cxx::Type_traits::align(_pos); return apos + sizeof(T) <= _tag.words() * sizeof(l4_umword_t); } /** * \name Get/Put Functions. * These functions are basically used to implement the extraction operators * (>>) and should not be called directly. * * See \link ipc_stream IPC stream operators \endlink. */ //@{ /** * \brief Copy out an array of type \a T with \a size elements. * * \param buf Pointer to a buffer for size elements of type T. * \param size number of elements of type T to copy out. * * See \link ipc_stream IPC stream operators \endlink. */ template< typename T > unsigned long get(T *buf, unsigned long elems) { unsigned long size = elems * sizeof(T); _pos = cxx::Type_traits::align(_pos); if (Utcb_stream_check::check_utcb_data_offset(_pos + size)) return 0; __builtin_memcpy(buf, _current_msg + _pos, size); _pos += size; return elems; } /** * \brief Skip size elements of type T in the stream. * \param size number of elements to skip. */ template< typename T > void skip(unsigned long size) { size *= sizeof(T); _pos = cxx::Type_traits::align(_pos); if (Utcb_stream_check::check_utcb_data_offset(_pos + size)) return; _pos += size; } /** * \brief Read one size elements of type T from the stream and return * a pointer. * * In contrast to a normal get, this version does actually not copy the data * but returns a pointer to the data. * * \param buf a Msg_ptr that is actually set to point to the element in the * stream. * \param elems number of elements to extract (default is 1). * * See \link ipc_stream IPC stream operators \endlink. */ template< typename T > unsigned long get(Msg_ptr const &buf, unsigned long elems = 1) { unsigned long size = elems * sizeof(T); _pos = cxx::Type_traits::align(_pos); if (Utcb_stream_check::check_utcb_data_offset(_pos + size)) return 0; buf.set(reinterpret_cast(_current_msg + _pos)); _pos += size; return elems; } /** * \brief Extract a single element of type T from the stream. * \param v Output: the element. * * See \link ipc_stream IPC stream operators \endlink. */ template< typename T > void get(T &v) { _pos = cxx::Type_traits::align(_pos); if (Utcb_stream_check::check_utcb_data_offset(_pos + sizeof(T))) { v = T(); return; } v = *(reinterpret_cast(_current_msg + _pos)); _pos += sizeof(T); } bool get(Ipc::Varg *va) { Ipc::Varg::Tag t; if (!has_more()) { va->tag(0); return 0; } get(t); va->tag(t); get(msg_ptr(va->data()), va->length()); return 1; } /** * \brief Get the message tag of a received IPC. * \return The L4 message tag for the received IPC. * * This is in particular useful for handling page faults or exceptions. * * See \link ipc_stream IPC stream operators \endlink. */ l4_msgtag_t tag() const { return _tag; } /** * \brief Get the message tag of a received IPC. * \return A reference to the L4 message tag for the received IPC. * * This is in particular useful for handling page faults or exceptions. * * See \link ipc_stream IPC stream operators \endlink. */ l4_msgtag_t &tag() { return _tag; } //@} /** * \internal * \brief Put a receive item into the stream's buffer registers. */ inline bool put(Buf_item const &); /** * \internal * \brief Put a small receive item into the stream's buffer registers. */ inline bool put(Small_buf const &); /** * \name IPC operations. */ //@{ /** * \brief Wait for an incoming message from any sender. * \param src contains the sender after a successful IPC operation. * \return The IPC result dope (l4_msgtag_t). * * This wait is actually known as 'open wait'. */ inline l4_msgtag_t wait(l4_umword_t *src) { return wait(src, L4_IPC_NEVER); } /** * \brief Wait for an incoming message from any sender. * \param src contains the sender after a successful IPC operation. * \param timeout Timeout used for IPC. * \return The IPC result dope (l4_msgtag_t). * * This wait is actually known as 'open wait'. */ inline l4_msgtag_t wait(l4_umword_t *src, l4_timeout_t timeout); /** * \brief Wait for a message from the specified sender. * \param src The sender id to receive from. * \return The IPC result dope (l4_msgtag_t). * * This is commonly known as 'closed wait'. */ inline l4_msgtag_t receive(l4_cap_idx_t src) { return receive(src, L4_IPC_NEVER); } inline l4_msgtag_t receive(l4_cap_idx_t src, l4_timeout_t timeout); //@} /** * \brief Return utcb pointer. */ inline l4_utcb_t *utcb() const { return _utcb; } protected: l4_msgtag_t _tag; l4_utcb_t *_utcb; char *_current_msg; unsigned _pos; unsigned char _current_buf; }; class Istream_copy : public Istream { private: l4_msg_regs_t _mrs; public: Istream_copy(Istream const &o) : Istream(o), _mrs(*l4_utcb_mr_u(o.utcb())) { // do some reverse mr to utcb trickery _utcb = (l4_utcb_t*)((l4_addr_t)&_mrs - (l4_addr_t)l4_utcb_mr_u((l4_utcb_t*)0)); _current_msg = reinterpret_cast(l4_utcb_mr_u(_utcb)->mr); } }; /** * \brief Read a value out of a stream. * * \param s An Istream. * \return The value of type T. * * The stream position is progressed accordingly. */ template< typename T > inline T read(Istream &s) { T t; s >> t; return t; } /** * \brief Output stream for IPC marshalling. * \ingroup ipc_fw * * Ipc::Ostream is part of the dynamic IPC marshalling infrastructure, as well * as Ipc::Istream and Ipc::Iostream. * * Ipc::Ostream is an output stream supporting insertion of values into an * IPC message buffer. A IPC message can be marshalled using the * usual insertion operator <<, see \link ipc_stream IPC stream operators * \endlink. * * There exist some special wrapper classes to insert arrays (see * Ipc::Buf_cp_out) and indirect strings (see Msg_out_buffer and * Msg_io_buffer). */ class Ostream { public: /** * \brief Create an IPC output stream using the given message buffer \a msg. */ Ostream(l4_utcb_t *utcb) : _tag(), _utcb(utcb), _current_msg(reinterpret_cast(l4_utcb_mr_u(_utcb)->mr)), _pos(0), _current_item(0) {} /** * \brief Reset the stream to empty, same state as a newly created stream. */ void reset() { _pos = 0; _current_item = 0; _current_msg = reinterpret_cast(l4_utcb_mr_u(_utcb)->mr); } /** * \name Get/Put functions. * * These functions are basically used to implement the insertion operators * (<<) and should not be called directly. * * See \link ipc_stream IPC stream operators \endlink. */ //@{ /** * \brief Put an array with \a size elements of type \a T into the stream. * \param buf A pointer to the array to insert into the buffer. * \param size The number of elements in the array. */ template< typename T > bool put(T *buf, unsigned long size) { size *= sizeof(T); _pos = cxx::Type_traits::align(_pos); if (Utcb_stream_check::check_utcb_data_offset(_pos + size)) return false; __builtin_memcpy(_current_msg + _pos, buf, size); _pos += size; return true; } /** * \brief Insert an element of type \a T into the stream. * \param v The element to insert. */ template< typename T > bool put(T const &v) { _pos = cxx::Type_traits::align(_pos); if (Utcb_stream_check::check_utcb_data_offset(_pos + sizeof(T))) return false; *(reinterpret_cast(_current_msg + _pos)) = v; _pos += sizeof(T); return true; } int put(Varg const &va) { put(va.tag()); put(va.data(), va.length()); return 0; } template< typename T > int put(Varg_t const &va) { return put(static_cast(va)); } /** * \brief Extract the L4 message tag from the stream. * \return the extracted L4 message tag. */ l4_msgtag_t tag() const { return _tag; } /** * \brief Extract a reference to the L4 message tag from the stream. * \return A reference to the L4 message tag. */ l4_msgtag_t &tag() { return _tag; } //@} /** * \internal * \brief Put a send item into the stream's message buffer. */ inline bool put_snd_item(Snd_item const &); /** * \name IPC operations. */ //@{ /** * \brief Send the message via IPC to the given receiver. * \param dst The destination for the message. */ inline l4_msgtag_t send(l4_cap_idx_t dst, long proto = 0, unsigned flags = 0); //@} /** * \brief Return utcb pointer. */ inline l4_utcb_t *utcb() const { return _utcb; } #if 0 /** * \brief Get the currently used bytes in the stream. */ unsigned long tell() const { register unsigned w = (_pos + sizeof(l4_umword_t)-1) / sizeof(l4_umword_t); w -= _current_item * 2; _tag = l4_msgtag(0, w, _current_item, 0); } #endif public: l4_msgtag_t prepare_ipc(long proto = 0, unsigned flags = 0) { register unsigned w = (_pos + sizeof(l4_umword_t)-1) / sizeof(l4_umword_t); w -= _current_item * 2; return l4_msgtag(proto, w, _current_item, flags); } protected: l4_msgtag_t _tag; l4_utcb_t *_utcb; char *_current_msg; unsigned _pos; unsigned char _current_item; }; /** * \brief Input/Output stream for IPC [un]marshalling. * \ingroup ipc_fw * * The Ipc::Iostream is part of the AW Env IPC framework as well as * Ipc::Istream and Ipc::Ostream. * In particular an Ipc::Iostream is a combination of an Ipc::Istream and an * Ipc::Ostream. It can use either a single message buffer for receiving and * sending messages or a pair of a receive and a send buffer. The stream also * supports combined IPC operations such as call() and reply_and_wait(), which * can be used to implement RPC functionality. */ class Iostream : public Istream, public Ostream { public: /** * \brief Create an IPC IO stream with a single message buffer. * \param msg The message buffer used as backing store. * * The created IO stream uses the same message buffer for sending and * receiving IPC messages. */ explicit Iostream(l4_utcb_t *utcb) : Istream(utcb), Ostream(utcb) {} /** * \brief Reset the stream to its initial state. * * Input as well as the output stream are reset. */ void reset() { Istream::reset(); Ostream::reset(); } /** * \name Get/Put functions. * * These functions are basically used to implement the insertion operators * (<<) and should not be called directly. * * See \link ipc_stream IPC stream operators \endlink. */ //@{ using Istream::get; using Istream::put; using Ostream::put; //@} /** * \name IPC operations. */ //@{ /** * \brief Do an IPC call using the message in the output stream and * receiving to the input stream. * \param dst The destination L4 UID (thread) to call. * \param timeout The IPC timeout for the call. * \param proto The protocol value to use in the message tag. * \return the result dope of the IPC operation. * * This is a combined IPC operation consisting of a send and a receive * to/from the given destination \a dst. * * A call is usually used by clients for RPCs to a server. * */ inline l4_msgtag_t call(l4_cap_idx_t dst, l4_timeout_t timeout, long proto = 0); inline l4_msgtag_t call(l4_cap_idx_t dst, long proto = 0); /** * \brief Do an IPC reply and wait. * \param src_dst Input: the destination for the send operation.
* Output: the source of the received message. * \return the result dope of the IPC operation. * * This is a combined IPC operation consisting of a send operation and * an open wait for any message. * * A reply and wait is usually used by servers that reply to a client * and wait for the next request by any other client. */ inline l4_msgtag_t reply_and_wait(l4_umword_t *src_dst, long proto = 0) { return reply_and_wait(src_dst, L4_IPC_SEND_TIMEOUT_0, proto); } inline l4_msgtag_t send_and_wait(l4_cap_idx_t dest, l4_umword_t *src, long proto = 0) { return send_and_wait(dest, src, L4_IPC_SEND_TIMEOUT_0, proto); } /** * \brief Do an IPC reply and wait. * \param src_dst Input: the destination for the send operation.
* Output: the source of the received message. * \param timeout Timeout used for IPC. * \return the result dope of the IPC operation. * * This is a combined IPC operation consisting of a send operation and * an open wait for any message. * * A reply and wait is usually used by servers that reply to a client * and wait for the next request by any other client. */ inline l4_msgtag_t reply_and_wait(l4_umword_t *src_dst, l4_timeout_t timeout, long proto = 0); inline l4_msgtag_t send_and_wait(l4_cap_idx_t dest, l4_umword_t *src, l4_timeout_t timeout, long proto = 0); inline l4_msgtag_t reply(l4_timeout_t timeout, long proto = 0); inline l4_msgtag_t reply(long proto = 0) { return reply(L4_IPC_SEND_TIMEOUT_0, proto); } //@} }; inline bool Ostream::put_snd_item(Snd_item const &v) { typedef Snd_item T; _pos = cxx::Type_traits::align(_pos); if (Utcb_stream_check::check_utcb_data_offset(_pos + sizeof(T))) return false; *(reinterpret_cast(_current_msg + _pos)) = v; _pos += sizeof(T); ++_current_item; return true; } inline bool Istream::put(Buf_item const &item) { if (_current_buf >= L4_UTCB_GENERIC_BUFFERS_SIZE - 3) return false; l4_utcb_br_u(_utcb)->bdr &= ~L4_BDR_OFFSET_MASK; reinterpret_cast(l4_utcb_br_u(_utcb)->br[_current_buf]) = item; _current_buf += 2; return true; } inline bool Istream::put(Small_buf const &item) { if (_current_buf >= L4_UTCB_GENERIC_BUFFERS_SIZE - 2) return false; l4_utcb_br_u(_utcb)->bdr &= ~L4_BDR_OFFSET_MASK; reinterpret_cast(l4_utcb_br_u(_utcb)->br[_current_buf]) = item; _current_buf += 1; return true; } inline l4_msgtag_t Ostream::send(l4_cap_idx_t dst, long proto, unsigned flags) { l4_msgtag_t tag = prepare_ipc(proto, L4_MSGTAG_FLAGS & flags); return l4_ipc_send(dst, _utcb, tag, L4_IPC_NEVER); } inline l4_msgtag_t Iostream::call(l4_cap_idx_t dst, l4_timeout_t timeout, long label) { l4_msgtag_t tag = prepare_ipc(label); tag = l4_ipc_call(dst, Ostream::_utcb, tag, timeout); Istream::tag() = tag; Istream::_pos = 0; return tag; } inline l4_msgtag_t Iostream::call(l4_cap_idx_t dst, long label) { return call(dst, L4_IPC_NEVER, label); } inline l4_msgtag_t Iostream::reply_and_wait(l4_umword_t *src_dst, l4_timeout_t timeout, long proto) { l4_msgtag_t tag = prepare_ipc(proto); tag = l4_ipc_reply_and_wait(Ostream::_utcb, tag, src_dst, timeout); Istream::tag() = tag; Istream::_pos = 0; return tag; } inline l4_msgtag_t Iostream::send_and_wait(l4_cap_idx_t dest, l4_umword_t *src, l4_timeout_t timeout, long proto) { l4_msgtag_t tag = prepare_ipc(proto); tag = l4_ipc_send_and_wait(dest, Ostream::_utcb, tag, src, timeout); Istream::tag() = tag; Istream::_pos = 0; return tag; } inline l4_msgtag_t Iostream::reply(l4_timeout_t timeout, long proto) { l4_msgtag_t tag = prepare_ipc(proto); tag = l4_ipc_send(L4_INVALID_CAP | L4_SYSF_REPLY, Ostream::_utcb, tag, timeout); Istream::tag() = tag; Istream::_pos = 0; return tag; } inline l4_msgtag_t Istream::wait(l4_umword_t *src, l4_timeout_t timeout) { l4_msgtag_t res; res = l4_ipc_wait(_utcb, src, timeout); tag() = res; _pos = 0; return res; } inline l4_msgtag_t Istream::receive(l4_cap_idx_t src, l4_timeout_t timeout) { l4_msgtag_t res; res = l4_ipc_receive(src, _utcb, timeout); tag() = res; _pos = 0; return res; } } // namespace Ipc } // namespace L4 /** * \brief Extract one element of type \a T from the stream \a s. * \ingroup ipc_stream * \param s The stream to extract from. * \param v Output: extracted value. * \return the stream \a s. */ inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, bool &v) { s.get(v); return s; } inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, int &v) { s.get(v); return s; } inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, long int &v) { s.get(v); return s; } inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, long long int &v) { s.get(v); return s; } inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, unsigned int &v) { s.get(v); return s; } inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, unsigned long int &v) { s.get(v); return s; } inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, unsigned long long int &v) { s.get(v); return s; } inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, short int &v) { s.get(v); return s; } inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, unsigned short int &v) { s.get(v); return s; } inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, char &v) { s.get(v); return s; } inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, unsigned char &v) { s.get(v); return s; } inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, signed char &v) { s.get(v); return s; } inline L4::Ipc::Istream &operator << (L4::Ipc::Istream &s, L4::Ipc::Buf_item const &v) { s.put(v); return s; } inline L4::Ipc::Istream &operator << (L4::Ipc::Istream &s, L4::Ipc::Small_buf const &v) { s.put(v); return s; } inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, L4::Ipc::Snd_item &v) { l4_umword_t b, d; s >> b >> d; v = L4::Ipc::Snd_item(b, d); return s; } inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, L4::Ipc::Varg &v) { s.get(&v); return s; } /** * \brief Extract the L4 message tag from the stream \a s. * \ingroup ipc_stream * \param s The stream to extract from. * \param v Output: the extracted tag. * \return the stream \a s. */ inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, l4_msgtag_t &v) { v = s.tag(); return s; } /** * \brief Extract an array of \a T elements from the stream \a s. * \ingroup ipc_stream * * This operator actually does not copy out the data in the array, but * returns a pointer into the message buffer itself. This means that the * data is only valid as long as there is no new data inserted into the stream. * * See Ipc::Buf_in, Ipc::Buf_cp_in, and Ipc::Buf_cp_out. * * \param s The stream to extract from. * \param v Output: pointer to the extracted array (ipc_buf_in()). * \return the stream \a s. */ template< typename T > inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, L4::Ipc::Internal::Buf_in const &v) { unsigned long si; s.get(si); v.set_size(s.get(L4::Ipc::Msg_ptr(v.buf()), si)); return s; } /** * \brief Extract an element of type \a T from the stream \a s. * \ingroup ipc_stream * * This operator actually does not copy out the data, but * returns a pointer into the message buffer itself. This means that the * data is only valid as long as there is no new data inserted into the stream. * * See Msg_ptr. * * \param s The stream to extract from. * \param v Output: pointer to the extracted element. * \return the stream \a s. */ template< typename T > inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, L4::Ipc::Msg_ptr const &v) { s.get(v); return s; } /** * \brief Extract an array of \a T elements from the stream \a s. * \ingroup ipc_stream * * This operator does a copy out of the data into the given buffer. * * See Ipc::Buf_in, Ipc::Buf_cp_in, and Ipc::Buf_cp_out. * * \param s The stream to extract from. * \param v buffer description to copy the array to (Ipc::Buf_cp_out()). * \return the stream \a s. */ template< typename T > inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, L4::Ipc::Internal::Buf_cp_in const &v) { unsigned long sz; s.get(sz); v.size() = s.get(v.buf(), cxx::min(v.size(), sz)); return s; } /** * \brief Extract a zero-terminated string from the stream. * \ingroup ipc_stream * * This operator does a copy out of the data into the given buffer. * * \param s The stream to extract from. * \param v buffer description to copy the array to (Ipc::Str_cp_out()). * \return the stream \a s. */ template< typename T > inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, L4::Ipc::Str_cp_in const &v) { unsigned long sz; s.get(sz); unsigned long rsz = s.get(v.buf(), cxx::min(v.size(), sz)); if (rsz < v.size() && v.buf()[rsz - 1]) ++rsz; // add the zero termination behind the received data if (rsz != 0) v.buf()[rsz - 1] = 0; v.size() = rsz; return s; } /** * \brief Insert an element to type \a T into the stream \a s. * \ingroup ipc_stream * * \param s The stream to insert the element \a v. * \param v The element to insert. * \return the stream \a s. */ inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, bool v) { s.put(v); return s; } inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, int v) { s.put(v); return s; } inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, long int v) { s.put(v); return s; } inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, long long int v) { s.put(v); return s; } inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, unsigned int v) { s.put(v); return s; } inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, unsigned long int v) { s.put(v); return s; } inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, unsigned long long int v) { s.put(v); return s; } inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, short int v) { s.put(v); return s; } inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, unsigned short int v) { s.put(v); return s; } inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, char v) { s.put(v); return s; } inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, unsigned char v) { s.put(v); return s; } inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, signed char v) { s.put(v); return s; } inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, L4::Ipc::Snd_item const &v) { s.put_snd_item(v); return s; } template< typename T > inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, L4::Cap const &v) { s << L4::Ipc::Snd_fpage(v.fpage()); return s; } inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, L4::Ipc::Varg const &v) { s.put(v); return s; } template< typename T > inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, L4::Ipc::Varg_t const &v) { s.put(v); return s; } /** * \brief Insert the L4 message tag into the stream \a s. * \ingroup ipc_stream * * \note Only one message tag can be inserted into a stream. Multiple * insertions simply overwrite previous insertions. * \param s The stream to insert the tag \a v. * \param v The L4 message tag to insert. * \return the stream \a s. */ inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, l4_msgtag_t const &v) { s.tag() = v; return s; } /** * \brief Insert an array with elements of type \a T into the stream \a s. * \ingroup ipc_stream * * \param s The stream to insert the array \a v. * \param v The array to insert (see Ipc::Buf_cp_out()). * \return the stream \a s. */ template< typename T > inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, L4::Ipc::Internal::Buf_cp_out const &v) { s.put(v.size()); s.put(v.buf(), v.size()); return s; } /** * \brief Insert a zero terminated character string into the stream \a s. * \ingroup ipc_stream * * \param s The stream to insert the string \a v. * \param v The string to insert. * \return the stream \a s. * * This operator produces basically the same content as the array insertion, * however the length of the array is calculated using strlen(\a v) + 1 * . The string is copied into the message including the trailing zero. */ inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, char const *v) { unsigned long l = __builtin_strlen(v) + 1; s.put(l); s.put(v, l); return s; } #ifdef L4_CXX_IPC_BACKWARD_COMPAT namespace L4 { #if 0 template< typename T > class Ipc_buf_cp_out : public Ipc::Buf_cp_out {}; template< typename T > class Ipc_buf_cp_in : public Ipc::Buf_cp_in {}; template< typename T > class Ipc_buf_in : public Ipc::Buf_in {}; template< typename T > class Msg_ptr : public Ipc::Msg_ptr {}; #endif template< typename T > Ipc::Internal::Buf_cp_out ipc_buf_cp_out(T *v, unsigned long size) L4_DEPRECATED("Use L4::Ipc::buf_cp_out() now"); template< typename T > Ipc::Internal::Buf_cp_out ipc_buf_cp_out(T *v, unsigned long size) { return Ipc::Internal::Buf_cp_out(v, size); } template< typename T > Ipc::Internal::Buf_cp_in ipc_buf_cp_in(T *v, unsigned long &size) L4_DEPRECATED("Use L4::Ipc::buf_cp_in() now"); template< typename T > Ipc::Internal::Buf_cp_in ipc_buf_cp_in(T *v, unsigned long &size) { return Ipc::Internal::Buf_cp_in(v, size); } template< typename T > Ipc::Internal::Buf_in ipc_buf_in(T *&v, unsigned long &size) L4_DEPRECATED("Use L4::Ipc::buf_in() now"); template< typename T > Ipc::Internal::Buf_in ipc_buf_in(T *&v, unsigned long &size) { return Ipc::Internal::Buf_in(v, size); } template< typename T > Ipc::Msg_ptr msg_ptr(T *&p) L4_DEPRECATED("Use L4::Ipc::msg_ptr() now"); template< typename T > Ipc::Msg_ptr msg_ptr(T *&p) { return Ipc::Msg_ptr(p); } typedef Ipc::Istream Ipc_istream L4_DEPRECATED("Use L4::Ipc::Istream now"); typedef Ipc::Ostream Ipc_ostream L4_DEPRECATED("Use L4::Ipc::Ostream now");; typedef Ipc::Iostream Ipc_iostream L4_DEPRECATED("Use L4::Ipc::Iostream now");; typedef Ipc::Snd_fpage Snd_fpage L4_DEPRECATED("Use L4::Ipc::Snd_fpage now");; typedef Ipc::Rcv_fpage Rcv_fpage L4_DEPRECATED("Use L4::Ipc::Rcv_fpage now");; typedef Ipc::Small_buf Small_buf L4_DEPRECATED("Use L4::Ipc::Small_buf now");; namespace Ipc { template< typename T > class Buf_cp_in : public Internal::Buf_cp_in { public: Buf_cp_in(T *v, unsigned long &size) : Internal::Buf_cp_in(v, size) {} }; template< typename T > class Buf_cp_out : public Internal::Buf_cp_out { public: Buf_cp_out(T const *v, unsigned long size) : Internal::Buf_cp_out(v, size) {} }; template< typename T > class Buf_in : public Internal::Buf_in { public: Buf_in(T *&v, unsigned long &size) : Internal::Buf_in(v, size) {} }; } // namespace Ipc } // namespace L4 template< typename T > inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, L4::Ipc::Buf_cp_in const &v) L4_DEPRECATED("Use L4::Ipc::buf_cp_in() now"); template< typename T > inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, L4::Ipc::Buf_cp_in const &v) { return operator>>(s, static_cast >(v)); } template< typename T > inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, L4::Ipc::Buf_in const &v) L4_DEPRECATED("Use L4::Ipc::buf_in() now"); template< typename T > inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, L4::Ipc::Buf_in const &v) { return operator>>(s, static_cast >(v)); } template< typename T > inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, L4::Ipc::Buf_cp_out const &v) L4_DEPRECATED("Use L4::Ipc::buf_cp_out() now"); template< typename T > inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, L4::Ipc::Buf_cp_out const &v) { return operator<<(s, static_cast >(v)); } #endif