Loading...
Searching...
No Matches
ipc_iface
Go to the documentation of this file.
// vi:set ft=cpp: -*- Mode: C++ -*-
/*
* (c) 2014 Alexander Warg <alexander.warg@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#pragma GCC system_header
#include <l4/sys/cxx/ipc_basics>
#include <l4/sys/cxx/ipc_types>
#include <l4/sys/__typeinfo.h>
// TODO: add some more documentation
namespace L4 { namespace Ipc {
struct L4_EXPORT Call
{
enum { Is_call = true };
enum { Rights = 0 };
static l4_timeout_t timeout() { return L4_IPC_NEVER; }
};
struct L4_EXPORT Call_zero_send_timeout : Call
{
static l4_timeout_t timeout() { return L4_IPC_SEND_TIMEOUT_0; }
};
template<unsigned RIGHTS>
struct L4_EXPORT Call_t : Call
{
enum { Rights = RIGHTS };
};
struct L4_EXPORT Send_only
{
enum { Is_call = false };
enum { Rights = 0 };
static l4_timeout_t timeout() { return L4_IPC_NEVER; }
};
namespace Msg {
template<typename OP, typename CLASS, typename SIG, typename FLAGS = Call>
struct L4_EXPORT Rpc_inline_call;
template<typename OP, typename CLASS, typename FLAGS, typename R,
typename ...ARGS>
struct L4_EXPORT Rpc_inline_call<OP, CLASS, R (ARGS...), FLAGS>
{
template<typename T> struct Result { typedef T result_type; };
enum
{
Return_tag = L4::Types::Same<R, l4_msgtag_t>::value
};
typedef Rpc_inline_call type;
typedef OP op_type;
typedef CLASS class_type;
typedef typename Result<R>::result_type result_type;
typedef R ipc_type (ARGS...);
typedef result_type func_type (typename _Elem<ARGS>::arg_type...);
typedef FLAGS flags_type;
template<typename RES>
static typename L4::Types::Enable_if< Return_tag, RES >::type
return_err(long err) noexcept { return l4_msgtag(err, 0, 0, 0); }
template<typename RES>
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<typename RES>
static typename L4::Types::Enable_if< Return_tag, RES >::type
return_code(l4_msgtag_t tag) noexcept { return tag; }
template<typename RES>
static typename L4::Types::Enable_if< !Return_tag, RES >::type
return_err(long err) noexcept { return err; }
template<typename RES>
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<typename RES>
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<class_type> cap,
typename _Elem<ARGS>::arg_type ...a,
l4_utcb_t *utcb = l4_utcb()) noexcept;
};
template<typename OP, typename CLASS, typename SIG, typename FLAGS = Call>
struct L4_EXPORT Rpc_call;
template<typename IPC, typename SIG> struct _Call;
template<typename IPC, typename R, typename ...ARGS>
struct _Call<IPC, R (ARGS...)>
{
public:
typedef typename IPC::class_type class_type;
typedef typename IPC::result_type result_type;
private:
L4::Cap<class_type> cap() const noexcept
{
return L4::Cap<class_type>(reinterpret_cast< l4_cap_idx_t >(this)
& L4_CAP_MASK);
}
public:
result_type operator () (ARGS ...a, l4_utcb_t *utcb = l4_utcb()) const noexcept
{ return IPC::call(cap(), a..., utcb); }
};
template<typename IPC> struct Call : _Call<IPC, typename IPC::func_type> {};
template<typename OP,
typename CLASS,
typename FLAGS,
typename R,
typename ...ARGS>
struct L4_EXPORT Rpc_call<OP, CLASS, R (ARGS...), FLAGS> :
Rpc_inline_call<OP, CLASS, R (ARGS...), FLAGS>
{
static R call(L4::Cap<CLASS> cap,
typename _Elem<ARGS>::arg_type ...a,
l4_utcb_t *utcb = l4_utcb()) noexcept;
};
#define L4_INLINE_RPC_SRV_FORWARD(name) \
template<typename OBJ> struct fwd \
{ \
OBJ *o; \
fwd(OBJ *o) noexcept : o(o) {} \
template<typename ...ARGS> long call(ARGS ...a) noexcept(noexcept(o->op_##name(a...))) \
{ return o->op_##name(a...); } \
}
#define L4_INLINE_RPC_NF(res, name, args...) \
struct name##_t : L4::Ipc::Msg::Rpc_inline_call<name##_t, Class, res args> \
{ \
typedef L4::Ipc::Msg::Rpc_inline_call<name##_t, Class, res args> type; \
L4_INLINE_RPC_SRV_FORWARD(name); \
}
#define L4_INLINE_RPC_NF_OP(op, res, name, args...) \
struct name##_t : L4::Ipc::Msg::Rpc_inline_call<name##_t, Class, res args> \
{ \
typedef L4::Ipc::Msg::Rpc_inline_call<name##_t, Class, res args> type; \
enum { Opcode = (op) }; \
L4_INLINE_RPC_SRV_FORWARD(name); \
}
#ifdef DOXYGEN
#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##_t> name
#endif
#ifdef DOXYGEN
#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##_t> name
#endif
#define L4_RPC_NF(res, name, args...) \
struct name##_t : L4::Ipc::Msg::Rpc_call<name##_t, Class, res args> \
{ \
typedef L4::Ipc::Msg::Rpc_call<name##_t, Class, res args> type; \
L4_INLINE_RPC_SRV_FORWARD(name); \
}
#define L4_RPC_NF_OP(op, res, name, args...) \
struct name##_t : L4::Ipc::Msg::Rpc_call<name##_t, Class, res args> \
{ \
typedef L4::Ipc::Msg::Rpc_call<name##_t, Class, res args> type; \
enum { Opcode = (op) }; \
L4_INLINE_RPC_SRV_FORWARD(name); \
}
#ifdef DOXYGEN
#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##_t> name
#endif
#ifdef DOXYGEN
#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##_t> name
#endif
namespace Detail {
template<typename ...ARGS>
struct Buf
{
public:
template<typename DIR>
static constexpr int write(char *, int offset, int) noexcept
{ return offset; }
template<typename DIR>
static constexpr int read(char *, int offset, int, long) noexcept
{ return offset; }
typedef void Base;
};
template<typename A, typename ...M>
struct Buf<A, M...> : Buf<M...>
{
typedef Buf<M...> Base;
typedef Clnt_xmit<A> xmit;
typedef typename _Elem<A>::arg_type arg_type;
typedef Detail::_Plain<arg_type> plain;
template<typename DIR>
static int
write(char *base, int offset, int limit,
arg_type a, typename _Elem<M>::arg_type ...m) noexcept
{
offset = xmit::to_msg(base, offset, limit, plain::deref(a),
typename DIR::dir(), typename DIR::cls());
return Base::template write<DIR>(base, offset, limit, m...);
}
template<typename DIR>
static int
read(char *base, int offset, int limit, long ret,
arg_type a, typename _Elem<M>::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<DIR>(base, r, limit, ret, m...);
if (_Elem<A>::Is_optional)
return Base::template read<DIR>(base, offset, limit, ret, m...);
return r;
}
};
template <typename ...ARGS> struct _Part
{
typedef Buf<ARGS...> Data;
template<typename DIR>
static int write(void *b, int offset, int limit,
typename _Elem<ARGS>::arg_type ...m) noexcept
{
char *buf = static_cast<char *>(b);
int r = Data::template write<DIR>(buf, offset, limit, m...);
if (L4_LIKELY(r >= offset))
return r - offset;
return r;
}
template<typename DIR>
static int read(void *b, int offset, int limit, long ret,
typename _Elem<ARGS>::arg_type ...m) noexcept
{
char *buf = static_cast<char *>(b);
int r = Data::template read<DIR>(buf, offset, limit, ret, m...);
if (L4_LIKELY(r >= offset))
return r - offset;
return r;
}
};
template<typename IPC_TYPE, typename OPCODE = void>
struct Part;
// The version without an op-code
template<typename R, typename ...ARGS>
struct Part<R (ARGS...), void> : _Part<ARGS...>
{
typedef Buf<ARGS...> Data;
// write arguments, skipping the dummy opcode
template<typename DIR>
static int write_op(void *b, int offset, int limit,
int /*placeholder for op*/,
typename _Elem<ARGS>::arg_type ...m) noexcept
{
char *buf = static_cast<char *>(b);
int r = Data::template write<DIR>(buf, offset, limit, m...);
if (L4_LIKELY(r >= offset))
return r - offset;
return r;
}
};
// Message part with additional opcode
template<typename OPCODE, typename R, typename ...ARGS>
struct Part<R (ARGS...), OPCODE> : _Part<ARGS...>
{
typedef OPCODE opcode_type;
typedef Buf<opcode_type, ARGS...> Data;
// write arguments, including the opcode
template<typename DIR>
static int write_op(void *b, int offset, int limit,
opcode_type op, typename _Elem<ARGS>::arg_type ...m) noexcept
{
char *buf = static_cast<char *>(b);
int r = Data::template write<DIR>(buf, 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<typename OP, typename CLASS, typename FLAGS, typename R,
typename ...ARGS>
inline R
Rpc_inline_call<OP, CLASS, R (ARGS...), FLAGS>::
call(L4::Cap<CLASS> cap,
typename _Elem<ARGS>::arg_type ...a,
l4_utcb_t *utcb) noexcept
{
using namespace Ipc::Msg;
typedef typename Kobject_typeid<CLASS>::Iface::Rpcs Rpcs;
typedef typename Rpcs::template Rpc<OP> Opt;
typedef Detail::Part<ipc_type, typename Rpcs::opcode_type> 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<Do_in_data>(mrs->mr, 0, Mr_bytes,
Opt::Opcode, a...);
if (L4_UNLIKELY(send_bytes < 0))
return return_err<R>(send_bytes);
send_bytes = align_to<l4_umword_t>(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<Do_in_items>(&mrs->mr[send_words], 0,
Mr_bytes - send_bytes, a...);
if (L4_UNLIKELY(item_bytes < 0))
return return_err<R>(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<Do_rcv_buffers>(brs->br, 0, Br_bytes - Word_bytes,
a...);
if (L4_UNLIKELY(bytes < 0))
return return_err<R>(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<R>(t, utcb);
return return_code<R>(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<R>(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>(r);
int const rcv_bytes = t.words() * Word_bytes;
// read the static out-data values to the arguments
int err = Args::template read<Do_out_data>(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<R>(-L4_EMSGTOOSHORT);
// read the static out-items to the arguments
err = Args::template read<Do_out_items>(&mrs->mr[t.words()], 0, item_limit,
r, a...);
if (L4_UNLIKELY(err < 0))
return return_err<R>(-L4_EMSGTOOSHORT);
return return_code<R>(t);
}
} // namespace Msg
} // namespace Ipc
} // namespace L4
__typeinfo.h
Type information handling.
L4::Cap_base::cap
l4_cap_idx_t cap() const noexcept
Return capability selector.
Definition capability.h:49
l4_cap_idx_t
unsigned long l4_cap_idx_t
Capability selector type.
Definition types.h:336
L4_CAP_MASK
@ L4_CAP_MASK
Mask to get only the relevant bits of an l4_cap_idx_t.
Definition consts.h:151
L4_EMSGTOOSHORT
@ L4_EMSGTOOSHORT
Message too short.
Definition err.h:56
l4_ipc_send
l4_msgtag_t l4_ipc_send(l4_cap_idx_t dest, l4_utcb_t *utcb, l4_msgtag_t tag, l4_timeout_t timeout) L4_NOTHROW
Send a message to an object (do not wait for a reply).
Definition ipc.h:586
l4_ipc_call
l4_msgtag_t l4_ipc_call(l4_cap_idx_t object, l4_utcb_t *utcb, l4_msgtag_t tag, l4_timeout_t timeout) L4_NOTHROW
Object call (usual invocation).
Definition ipc.h:565
l4_ipc_error_code
int l4_ipc_error_code(l4_utcb_t *utcb) L4_NOTHROW
Get the error condition of the last invocation from the TCR.
Definition ipc.h:658
l4_msgtag
l4_msgtag_t l4_msgtag(long label, unsigned words, unsigned items, unsigned flags) L4_NOTHROW
Create a message tag from the specified values.
Definition types.h:405
l4_msgtag_t
struct l4_msgtag_t l4_msgtag_t
Message tag data structure.
L4_IPC_SEND_TIMEOUT_0
#define L4_IPC_SEND_TIMEOUT_0
0 send timeout
Definition __timeout.h:78
L4_IPC_NEVER
#define L4_IPC_NEVER
never timeout
Definition __timeout.h:76
l4_utcb_t
struct l4_utcb_t l4_utcb_t
Opaque type for the UTCB.
Definition utcb.h:56
l4_utcb
l4_utcb_t * l4_utcb(void) L4_NOTHROW L4_PURE
Get the UTCB address.
Definition utcb.h:346
l4_buf_regs_t
struct l4_buf_regs_t l4_buf_regs_t
Encapsulation of the buffer-registers block in the UTCB.
l4_msg_regs_t
union l4_msg_regs_t l4_msg_regs_t
Encapsulation of the message-register block in the UTCB.
L4_UNLIKELY
#define L4_UNLIKELY(x)
Expression is unlikely to execute.
Definition compiler.h:284
L4_EXPORT
#define L4_EXPORT
Attribute to mark functions, variables, and data types as being exported from a library.
Definition compiler.h:214
L4_LIKELY
#define L4_LIKELY(x)
Expression is likely to execute.
Definition compiler.h:283
ipc_types
l4_ipc_to_errno
long l4_ipc_to_errno(unsigned long ipc_error_code) L4_NOTHROW
Get a negative error code for the given IPC error code.
Definition ipc.h:561
L4::Ipc::Msg
IPC Message related functionality.
Definition ipc_array:154
L4::Ipc::Msg::Item_bytes
@ Item_bytes
number of bytes for one message item
Definition ipc_basics:92
L4::Ipc::Msg::Word_bytes
@ Word_bytes
number of bytes for one message word
Definition ipc_basics:88
L4::Ipc
IPC related functionality.
Definition ipc_array:13
L4
L4 low-level kernel interface.
Definition l4sys-l4f-groups.dox:50
L4::Ipc::Call_t
RPC attribute for an RPC call with required rights.
Definition ipc_iface:271
L4::Ipc::Call_zero_send_timeout
RPC attribute for an RPC call, with zero send timeout.
Definition ipc_iface:250
L4::Ipc::Call
RPC attribute for a standard RPC call.
Definition ipc_iface:240
L4::Ipc::Send_only
RPC attribute for a send-only RPC.
Definition ipc_iface:288
l4_buf_regs_t::br
l4_umword_t br[L4_UTCB_GENERIC_BUFFERS_SIZE]
Buffer registers.
Definition utcb.h:88
l4_buf_regs_t::bdr
l4_umword_t bdr
Buffer descriptor.
Definition utcb.h:85
l4_msgtag_t::label
long label() const L4_NOTHROW
Get the protocol value.
Definition types.h:158
l4_msgtag_t::has_error
bool has_error() const L4_NOTHROW
Test if flags indicate an error.
Definition types.h:191
l4_msgtag_t::words
unsigned words() const L4_NOTHROW
Get the number of untyped words.
Definition types.h:169
l4_msgtag_t::items
unsigned items() const L4_NOTHROW
Get the number of typed items.
Definition types.h:171
l4_msgtag_t::flags
unsigned flags() const L4_NOTHROW
Get the flags value.
Definition types.h:178
l4_msg_regs_t::mr
l4_umword_t mr[L4_UTCB_GENERIC_DATA_SIZE]
Message registers.
Definition utcb.h:69
l4_timeout_t
Timeout pair.
Definition __timeout.h:53