// vi:set ft=cpp: -*- Mode: C++ -*- /* * (c) 2014 Alexander Warg * * 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 #pragma GCC system_header #include #include #include /** * \file l4/sys/cxx/ipc_iface Interface Definition Language * \sa L4_RPC, L4_INLINE_RPC, L4::Ipc::Call L4::Ipc::Send_only, * L4::Ipc::Msg::Rpc_call, L4::Ipc::Msg::Rpc_inline_call * * */ /** * \page l4_cxx_ipc_iface Interface Definition Language * * An interface definition in L4Re is normally declared as a class * derived from L4::Kobject_t. For example, the simple calculator * example declares its interface like that: * * ~~~{.cpp} * struct Calc : L4::Kobject_t * { * L4_INLINE_RPC(int, sub, (l4_uint32_t a, l4_uint32_t b, l4_uint32_t *res)); * L4_INLINE_RPC(int, neg, (l4_uint32_t a, l4_uint32_t *res)); * * typedef L4::Typeid::Rpcs Rpcs; * }; * ~~~ * * * The signature of each function is first declared using one of the RPC * macros (see below) and then all the functions need to be listed in the * Rpcs type. * * Clients invoke these functions with the name given to the RPC macros, `sub` * and `neg` above. Servers implement them by defining functions with an `op_` * prepended, `op_sub` and `op_neg`. The types of the parameters in the macro * definition, on the server side, and on the client side are not the same. * The following section describes how they are related to each other. * * \section l4_cxx_ipc_iface_types Parameter types for RPC * * Generally all value parameters, const reference parameters, * and const pointer parameters to an RPC interface are considered as input * parameters for the RPC and are transmitted from the client to the server. * \note This means that `char const *` is treated as an input `char` and not * as a zero terminated string value, for strings see L4::Ipc::String<>. * * Parameters that are non-const references or non-const pointers are treated * as output parameters going from the server to the client. * * There are special data types that appear on only one side (client or server) * when used, see the following table for details. * * ~~~~~~{.cpp} * L4_RPC(long, test, (int arg1, char const *arg2, unsigned *ret1)); * ~~~~~~ * * The example shows the declaration of a method called `test` with `long` * as return type, `arg1` is an `int` input, `arg2` a `char` input, and * `ret1` an `unsigned` output parameter. * * | Type | Direction | Client-Type | Server-Type | * |:----------------------------|:----------|:---------------------|:---------------------| * | `T` | Input | `T` | `T` | * | `T const &` | Input | `T const &` | `T const &` | * | `T const *` | Input | `T const *` | `T const &` | * | `T &` | Output | `T &` | `T &` | * | `T *` | Output | `T *` | `T &` | * | `L4::Ipc::In_out` | In/Out | `T &` | `T &` | * | `L4::Ipc::In_out` | In/Out | `T *` | `T &` | * | `L4::Ipc::Cap` | Input | `L4::Ipc::Cap` | `L4::Ipc::Snd_fpage` | * | `L4::Ipc::Out >` | Output | `L4::Cap` | `L4::Ipc::Cap &` | * | `L4::Ipc::Rcv_fpage` | Input | `L4::Ipc::Rcv_fpage` | `void` | * | `L4::Ipc::Small_buf` | Input | `L4::Ipc::Small_buf` | `void` | * * Array types can be used to transmit arrays of variable length. They can * either be stored in a client-provided buffer (L4::Ipc::Array), copied into * a server-provided buffer (L4::Ipc::Array_in_buf) or directly read and written * into the UTCB (L4::Ipc::Array_ref). For latter type, the start * position of an array type needs to be known in advance. That implies that * only one such array can be transmitted per direction and it must be the * last part in the message. * * | Type | Direction | Client-Type | Server-Type | * |:--------------------------|:----------|:--------------------------|:----------------------------------| * | `L4::Ipc::Array` | Input | `L4::Ipc::Array` | `L4::Ipc::Array_ref` | * | `L4::Ipc::Array` | Input | `L4::Ipc::Array` | `L4::Ipc::Array_in_buf const &`| * | `L4::Ipc::Array &` | Output | `L4::Ipc::Array &` | `L4::Ipc::Array_ref &` | * | `L4::Ipc::Array_ref &` | Output | `L4::Ipc::Array_ref &` | `L4::Ipc::Array_ref &` | * * * Finally, there are some optional types where the sender can choose * if the parameter should be included in the message. These types are for * the implementation of some legacy message formats and should generally * not be needed for the definition of ordinary interfaces. * * | Type | Direction | Client-Type | Server-Type | * |:-------------------------------|:----------|:-------------------------|:-------------------------------| * | `L4::Ipc::Opt` | Input | `L4::Ipc::Opt` | `T` | * | `L4::Ipc::Opt` | Input | `L4::Ipc::Opt` | `T` | * | `L4::Ipc::Opt` | Output | `T &` | `L4::Ipc::Opt &` | * | `L4::Ipc::Opt` | Output | `T *` | `L4::Ipc::Opt &` | * | `L4::Ipc::Opt &>` | Output | `Array_ref &` | `L4::Ipc::Opt> &` | * * \section l4_cxx_ipc_iface_return_types RPC Return Types * * On the server side, the return type of an RPC handling function is always * `long`. The return value is transmitted via the label field in l4_msgtag_t * and is therefore restricted to its length. Per convention, a negative * return value is interpreted as an error condition. If the return value * is negative, output parameters are not transmitted back to the client. * * \attention The client must never rely on the content of output parameters * when the return value is negative. * * On the client-side, the return value of the RPC is set as defined in * the RPC macro. If `l4_msgtag_t` is given, then the client has access * to the full message tag, otherwise the return type should be `long`. * Note that the client might not only receive the server return value in * response but also an IPC error code. * * \section l4_cxx_ipc_iface_members RPC Method Declaration * * RPC member functions can be declared using one of the following C++ macros. * * For inline RPC stubs, where the RPC stub code itself is `inline`: * * * ~~~{.cpp} * L4_INLINE_RPC(res, name, (args...), flags) * ~~~ * \copybrief #L4_INLINE_RPC * * * ~~~{.cpp} * L4_INLINE_RPC_OP(op, res, name, (args...), flags) * ~~~ * \copybrief #L4_INLINE_RPC_OP * * * ~~~{.cpp} * L4_INLINE_RPC_NF(res, name, (args...), flags) * ~~~ * \copybrief #L4_INLINE_RPC_NF * * * ~~~{.cpp} * L4_INLINE_RPC_NF_OP(opcode, Ret_type, func_name, (args...), flags) * ~~~ * \copybrief #L4_INLINE_RPC_NF_OP * * For external RPC stubs, where the RPC stub code must be defined in a * separate compile unit (usually a `.cc` file): * * * ~~~{.cpp} * L4_RPC(Ret_type, func_name, (args...), flags) * ~~~ * \copybrief #L4_RPC * * * * ~~~{.cpp} * L4_RPC_OP(opcode, Ret_type, func_name, (args...), flags) * ~~~ * \copybrief #L4_RPC_OP * * * ~~~{.cpp} * L4_RPC_NF(Ret_type, func_name, (args...), flags) * ~~~ * \copybrief #L4_RPC_NF * * * ~~~{.cpp} * L4_RPC_NF_OP(opcode, Ret_type, func_name, (args...), flags) * ~~~ * \copybrief #L4_RPC_NF_OP * * To generate the implementation of an external RPC stub: * * * ~~~{.cpp} * L4_RPC_DEF(class_name::func_name) * ~~~ * \copybrief #L4_RPC_DEF * * The `NF` versions of the macro generally do not generate a callable * member function named `` but do only generate the type `_t`. * This data type can be used to call the RPC stub explicitly using * `_t::call(L4::Cap cap, args...)`. * */ // TODO: add some more documentation namespace L4 { namespace Ipc { /** * RPC attribute for a standard RPC call. * * This is the default for the \a FLAGS parameter for L4::Ipc::Msg::Rpc_call * L4::Ipc::Msg::Rpc_inline_call templates and declares the RPC to have default * call semantics and timeouts. * * Examples: * ~~~~~~~~~~~{.cpp} * L4_RPC(long, send, (unsigned value), L4::Ipc::Call); * ~~~~~~~~~~~ * which is equivalent to: * ~~~~~~~~~~~{.cpp} * L4_RPC(long, send, (unsigned value)); * ~~~~~~~~~~~ */ struct L4_EXPORT Call { enum { Is_call = true }; enum { Rights = 0 }; static l4_timeout_t timeout() { return L4_IPC_NEVER; } }; /** * RPC attribute for an RPC call, with zero send timeout. */ struct L4_EXPORT Call_zero_send_timeout : Call { static l4_timeout_t timeout() { return L4_IPC_SEND_TIMEOUT_0; } }; /** * RPC attribute for an RPC call with required rights. * \tparam RIGHTS The capability rights required for this call. * #L4_CAP_FPAGE_W and #L4_CAP_FPAGE_S are checked within the * server (and -#L4_EPERM shall be returned if the caller has * insufficient rights). #L4_CAP_FPAGE_R is always on but * might be specified for documentation purposes. Other rights * cannot be used in this context, because they cannot be * checked at the server side. * * Examples: * ~~~~~~~~~~~{.cpp} * L4_RPC(long, func, (unsigned value), L4::Ipc::Call_t); * ~~~~~~~~~~~ */ template struct L4_EXPORT Call_t : Call { enum { Rights = RIGHTS }; }; /** * RPC attribute for a send-only RPC. * * This class can be used as FLAGS parameter to L4::Ipc::Msg::Rpc_call and * L4::Ipc::Msg::Rpc_inline_call templates and declares the RPC to use send-only * semantics and timeouts. * * Examples: * ~~~~~~~~~~~{.cpp} * L4_RPC(long, send, (unsigned value), L4::Ipc::Send_only); * ~~~~~~~~~~~ */ struct L4_EXPORT Send_only { enum { Is_call = false }; enum { Rights = 0 }; static l4_timeout_t timeout() { return L4_IPC_NEVER; } }; namespace Msg { /** * \internal * Callable data type for the given RPC signature \a SIG. * \tparam OP The type for the specific operation, usually this type * inherits from Rpc_call<>. * \tparam CLASS The class that contains this callable. The class is * used to determine the protocol ID for the IPC and * \a CLASS plus \a OP are used to determine the op-code. * \tparam SIG The function signature for the RPC. */ template struct L4_EXPORT Rpc_inline_call; /** * \internal * The implementation for SIG == R (ARGS...) */ template struct L4_EXPORT Rpc_inline_call { template struct Result { typedef T result_type; }; enum { Return_tag = L4::Types::Same::value }; /// Our type typedef Rpc_inline_call type; /// The type used to uniquely identify the operation typedef OP op_type; /// The class that contains this operation typedef CLASS class_type; /// The result type of the callable typedef typename Result::result_type result_type; /// The original signature given to Rpc_call typedef R ipc_type (ARGS...); /// The signature of the client callable typedef result_type func_type (typename _Elem::arg_type...); /// The RPC flags type (specifying e.g. send-only or call operation) typedef FLAGS flags_type; template static typename L4::Types::Enable_if< Return_tag, RES >::type return_err(long err) noexcept { return l4_msgtag(err, 0, 0, 0); } template static typename L4::Types::Enable_if< Return_tag, RES >::type return_ipc_err(l4_msgtag_t tag, l4_utcb_t const *) noexcept { return tag; } template static typename L4::Types::Enable_if< Return_tag, RES >::type return_code(l4_msgtag_t tag) noexcept { return tag; } template static typename L4::Types::Enable_if< !Return_tag, RES >::type return_err(long err) noexcept { return err; } template static typename L4::Types::Enable_if< !Return_tag, RES >::type return_ipc_err(l4_msgtag_t, l4_utcb_t *utcb) noexcept { return l4_ipc_to_errno(l4_ipc_error_code(utcb)); } template static typename L4::Types::Enable_if< !Return_tag, RES >::type return_code(l4_msgtag_t tag) noexcept { return tag.label(); } static R call(L4::Cap cap, typename _Elem::arg_type ...a, l4_utcb_t *utcb = l4_utcb()) noexcept; }; /** * \internal * non-inline version of Rpc_call */ template struct L4_EXPORT Rpc_call; /** * \internal * Helper to define a callable member of an RPC interface. * * \tparam IPC Some type derived from Rpc_call or Rpc_inline_call * \tparam SIG signature of the method */ template struct _Call; /// \internal template struct _Call { public: typedef typename IPC::class_type class_type; typedef typename IPC::result_type result_type; private: L4::Cap cap() const noexcept { return L4::Cap(reinterpret_cast(this) & L4_CAP_MASK); } public: /// The implementation of this callable result_type operator () (ARGS ...a, l4_utcb_t *utcb = l4_utcb()) const noexcept { return IPC::call(cap(), a..., utcb); } }; /** * \internal * Helper to define a callable member of an RPC interface. * \tparam IPC The type defining the RPC method, usually derived from * Rpc_inline_call. */ template struct Call : _Call {}; /** * \internal * non-inline version of Rpc_call (for SIG == R (ARGS...)) */ template struct L4_EXPORT Rpc_call : Rpc_inline_call { static R call(L4::Cap cap, typename _Elem::arg_type ...a, l4_utcb_t *utcb = l4_utcb()) noexcept; }; #define L4_INLINE_RPC_SRV_FORWARD(name) \ template struct fwd \ { \ OBJ *o; \ fwd(OBJ *o) noexcept : o(o) {} \ template long call(ARGS ...a) noexcept(noexcept(o->op_##name(a...))) \ { return o->op_##name(a...); } \ } /** * Define an inline RPC call type (the type only, no callable). * \param res The result type of the RPC call * \param name The name of the function (`name`_t is used for the type.) * \param args The argument list of the RPC function, and RPC attributes * (L4::Ipc::Call, L4::Ipc::Call_t etc.). * * Stubs generated by this macro can be used explicitly in custom wrapper * methods that need to use the underlying RPC code and provide some higher * level abstraction, for example with default arguments or extra argument * conversion. */ #define L4_INLINE_RPC_NF(res, name, args...) \ struct name##_t : L4::Ipc::Msg::Rpc_inline_call \ { \ typedef L4::Ipc::Msg::Rpc_inline_call type; \ L4_INLINE_RPC_SRV_FORWARD(name); \ } /** * \brief Define an inline RPC call type with specific opcode (the type only, * no callable). * \param op The opcode number for this function * \copydetails #L4_INLINE_RPC_NF */ #define L4_INLINE_RPC_NF_OP(op, res, name, args...) \ struct name##_t : L4::Ipc::Msg::Rpc_inline_call \ { \ typedef L4::Ipc::Msg::Rpc_inline_call type; \ enum { Opcode = (op) }; \ L4_INLINE_RPC_SRV_FORWARD(name); \ } #ifdef DOXYGEN /** * Define an inline RPC call (type and callable). * \param res The result type of the RPC call * \param name The name of the function (`name`_t is used for the type.) * \param args The argument list of the RPC function. * \param attr Optional RPC attributes (L4::Ipc::Call, L4::Ipc::Call_t etc.). */ #define L4_INLINE_RPC(res, name, args, attr...) res name args #else #define L4_INLINE_RPC(res, name, args...) \ L4_INLINE_RPC_NF(res, name, args); L4::Ipc::Msg::Call name #endif #ifdef DOXYGEN /** * Define an inline RPC call with specific opcode (type and callable). * \param op The opcode number for this function * \param res The result type of the RPC call * \param name The name of the function (`name`_t is used for the type.) * \param args The argument list of the RPC function. * \param attr Optional RPC attributes (L4::Ipc::Call, L4::Ipc::Call_t etc.). */ #define L4_INLINE_RPC_OP(op, res, name, args, attr...) res name args #else #define L4_INLINE_RPC_OP(op, res, name, args...) \ L4_INLINE_RPC_NF_OP(op, res, name, args); L4::Ipc::Msg::Call name #endif /** * Define an RPC call type (the type only, no callable). * \param res The result type of the RPC call * \param name The name of the function (`name`_t is used for the type.) * \param args The argument list of the RPC function, and RPC attributes * (L4::Ipc::Call, L4::Ipc::Call_t etc.). */ #define L4_RPC_NF(res, name, args...) \ struct name##_t : L4::Ipc::Msg::Rpc_call \ { \ typedef L4::Ipc::Msg::Rpc_call type; \ L4_INLINE_RPC_SRV_FORWARD(name); \ } /** * Define an RPC call type with specific opcode (the type only, no callable). * \param op The opcode number for this function * \param res The result type of the RPC call * \param name The name of the function (`name`_t is used for the type.) * \param args The argument list of the RPC function, and RPC attributes * (L4::Ipc::Call, L4::Ipc::Call_t etc.). */ #define L4_RPC_NF_OP(op, res, name, args...) \ struct name##_t : L4::Ipc::Msg::Rpc_call \ { \ typedef L4::Ipc::Msg::Rpc_call type; \ enum { Opcode = (op) }; \ L4_INLINE_RPC_SRV_FORWARD(name); \ } #ifdef DOXYGEN /** * Define an RPC call (type and callable). * \param res The result type of the RPC call * \param name The name of the function (`name`_t is used for the type.) * \param args The argument list of the RPC function. * \param attr Optional RPC attributes (L4::Ipc::Call, L4::Ipc::Call_t etc.). */ #define L4_RPC(res, name, args, attr...) res name args #else #define L4_RPC(res, name, args...) \ L4_RPC_NF(res, name, args); L4::Ipc::Msg::Call name #endif #ifdef DOXYGEN /** * Define an RPC call with specific opcode (type and callable). * \param op The opcode number for this function * \param res The result type of the RPC call * \param name The name of the function (`name`_t is used for the type.) * \param args The argument list of the RPC function. * \param attr Optional RPC attributes (L4::Ipc::Call, L4::Ipc::Call_t etc.). */ #define L4_RPC_OP(op, res, name, args, attr...) res name args #else #define L4_RPC_OP(op, res, name, args...) \ L4_RPC_NF_OP(op, res, name, args); L4::Ipc::Msg::Call name #endif /** * \internal * Implementation details */ namespace Detail { /** * \tparam ARGS The list of arguments used for the RPC function. */ template struct Buf { public: template static constexpr int write(char *, int offset, int) noexcept { return offset; } template static constexpr int read(char *, int offset, int, long) noexcept { return offset; } typedef void Base; }; template struct Buf : Buf { typedef Buf Base; typedef Clnt_xmit xmit; typedef typename _Elem::arg_type arg_type; typedef Detail::_Plain plain; template static int write(char *base, int offset, int limit, arg_type a, typename _Elem::arg_type ...m) noexcept { offset = xmit::to_msg(base, offset, limit, plain::deref(a), typename DIR::dir(), typename DIR::cls()); return Base::template write(base, offset, limit, m...); } template static int read(char *base, int offset, int limit, long ret, arg_type a, typename _Elem::arg_type ...m) noexcept { int r = xmit::from_msg(base, offset, limit, ret, plain::deref(a), typename DIR::dir(), typename DIR::cls()); if (L4_LIKELY(r >= 0)) return Base::template read(base, r, limit, ret, m...); if (_Elem::Is_optional) return Base::template read(base, offset, limit, ret, m...); return r; } }; template struct _Part { /// The buffer type with op-code. typedef Buf Data; template static int write(void *b, int offset, int limit, typename _Elem::arg_type ...m) noexcept { int r = Data::template write((char *)b, offset, limit, m...); if (L4_LIKELY(r >= offset)) return r - offset; return r; } template static int read(void *b, int offset, int limit, long ret, typename _Elem::arg_type ...m) noexcept { int r = Data::template read((char *)b, offset, limit, ret, m...); if (L4_LIKELY(r >= offset)) return r - offset; return r; } }; /** * template declaration for message parts in message registers * \tparam IPC_TYPE The function signature intended for marshalling * \tparam OPCODE The opcode data type for the messages (use void for * protocols without opcodes) */ template struct Part; // The version without an op-code template struct Part : _Part { /// The buffer type with op-code. typedef Buf Data; // write arguments, skipping the dummy opcode template static int write_op(void *b, int offset, int limit, int /*placeholder for op*/, typename _Elem::arg_type ...m) noexcept { int r = Data::template write((char *)b, offset, limit, m...); if (L4_LIKELY(r >= offset)) return r - offset; return r; } }; // Message part with additional opcode template struct Part : _Part { typedef OPCODE opcode_type; /// The buffer type with op-code. typedef Buf Data; // write arguments, including the opcode template static int write_op(void *b, int offset, int limit, opcode_type op, typename _Elem::arg_type ...m) noexcept { int r = Data::template write((char *)b, offset, limit, op, m...); if (L4_LIKELY(r >= offset)) return r - offset; return r; } }; } // namespace Detail //---------------------------------------------------- // Implementation of the RPC call // TODO: Add support for timeout via special RPC argument // TODO: Add support for passing the UTCB pointer as argument // template inline R Rpc_inline_call:: call(L4::Cap cap, typename _Elem::arg_type ...a, l4_utcb_t *utcb) noexcept { using namespace Ipc::Msg; typedef typename Kobject_typeid::Iface::Rpcs Rpcs; typedef typename Rpcs::template Rpc Opt; typedef Detail::Part Args; l4_msg_regs_t *mrs = l4_utcb_mr_u(utcb); // handle in-data part of the arguments int send_bytes = Args::template write_op(mrs->mr, 0, Mr_bytes, Opt::Opcode, a...); if (L4_UNLIKELY(send_bytes < 0)) return return_err(send_bytes); send_bytes = align_to(send_bytes); int const send_words = send_bytes / Word_bytes; // write the in-items part of the message if there is one int item_bytes = Args::template write(&mrs->mr[send_words], 0, Mr_bytes - send_bytes, a...); if (L4_UNLIKELY(item_bytes < 0)) return return_err(item_bytes); int send_items = item_bytes / Item_bytes; { // setup the receive buffers for the RPC call l4_buf_regs_t *brs = l4_utcb_br_u(utcb); // XXX: we currently support only one type of receive buffers per call brs->bdr = 0; // we always start at br[0] // the limit leaves us at least one register for the zero terminator // add the buffers given as arguments to the buffer registers int bytes = Args::template write(brs->br, 0, Br_bytes - Word_bytes, a...); if (L4_UNLIKELY(bytes < 0)) return return_err(bytes); brs->br[bytes / Word_bytes] = 0; } // here we do the actual IPC --------------------------------- l4_msgtag_t t; t = l4_msgtag(CLASS::Protocol, send_words, send_items, 0); // do the call (Q: do we need support for timeouts?) if (flags_type::Is_call) t = l4_ipc_call(cap.cap(), utcb, t, flags_type::timeout()); else { t = l4_ipc_send(cap.cap(), utcb, t, flags_type::timeout()); if (L4_UNLIKELY(t.has_error())) return return_ipc_err(t, utcb); return return_code(l4_msgtag(0, 0, 0, t.flags())); } // unmarshalling starts here --------------------------------- // bail out early in the case of an IPC error if (L4_UNLIKELY(t.has_error())) return return_ipc_err(t, utcb); // take the label as return value long r = t.label(); // bail out on negative error codes too if (L4_UNLIKELY(r < 0)) return return_err(r); int const rcv_bytes = t.words() * Word_bytes; // read the static out-data values to the arguments int err = Args::template read(mrs->mr, 0, rcv_bytes, r, a...); int const item_limit = t.items() * Item_bytes; if (L4_UNLIKELY(err < 0 || item_limit > Mr_bytes)) return return_err(-L4_EMSGTOOSHORT); // read the static out-items to the arguments err = Args::template read(&mrs->mr[t.words()], 0, item_limit, r, a...); if (L4_UNLIKELY(err < 0)) return return_err(-L4_EMSGTOOSHORT); return return_code(t); } } // namespace Msg } // namespace Ipc } // namespace L4