// 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 #include namespace L4 { namespace Ipc { namespace Msg { namespace Detail { template struct Sizeof { enum { size = sizeof(T) }; }; template<> struct Sizeof { enum { size = 0 }; }; /** * Argument data structure for server-function arguments. */ template struct Arg_pack { template unsigned get(char *, unsigned offset, unsigned) { return offset; } template unsigned set(char *, unsigned offset, unsigned, long) { return offset; } template long call(F f, ARGS ...args) { return f(args...); } template long obj_call(O *o, ARGS ...args) { typedef typename FUNC::template fwd Fwd; return Fwd(o).template call(args...); //return o->op_dispatch(args...); } }; /** * Data member for server-function argument T. */ template struct Svr_arg : Svr_xmit, Arg_pack { typedef Arg_pack Base; typedef SVR_TYPE svr_type; typedef typename _Elem::svr_arg_type svr_arg_type; svr_type v; template int get(char *msg, unsigned offset, unsigned limit) { typedef Svr_xmit ct; int r = ct::to_svr(msg, offset, limit, this->v, typename DIR::dir(), typename DIR::cls()); if (L4_LIKELY(r >= 0)) return Base::template get(msg, r, limit); if (_Elem::Is_optional) { v = svr_type(); return Base::template get(msg, offset, limit); } return r; } template int set(char *msg, unsigned offset, unsigned limit, long ret) { typedef Svr_xmit ct; int r = ct::from_svr(msg, offset, limit, ret, this->v, typename DIR::dir(), typename DIR::cls()); if (L4_UNLIKELY(r < 0)) return r; return Base::template set(msg, r, limit, ret); } template long call(F f, ARGS ...args) { //As_arg check; return Base::template call(f, args..., this->v); } template long obj_call(O *o, ARGS ...args) { //As_arg check; return Base::template obj_call(o, args..., this->v); } }; template struct Svr_arg : Arg_pack { typedef Arg_pack Base; template int get(char *msg, unsigned offset, unsigned limit) { return Base::template get(msg, offset, limit); } template int set(char *msg, unsigned offset, unsigned limit, long ret) { return Base::template set(msg, offset, limit, ret); } template long call(F f, ARGS ...args) { return Base::template call(f, args...); } template long obj_call(O *o, ARGS ...args) { return Base::template obj_call(o, args...); } }; template struct Arg_pack : Svr_arg::svr_type, M...> {}; } // namespace Detail //--------------------------------------------------------------------- /** * Server-side RPC arguments data structure used to provide arguments * to the server-side implementation of an RPC function. */ template struct Svr_arg_pack; template struct Svr_arg_pack : Detail::Arg_pack { typedef Detail::Arg_pack Base; template int get(void *msg, unsigned offset, unsigned limit) { return Base::template get((char *)msg, offset, limit); } template int set(void *msg, unsigned offset, unsigned limit, long ret) { return Base::template set((char *)msg, offset, limit, ret); } }; /** * Handle an incoming RPC call and forward it to o->op_dispatch(). */ template static l4_msgtag_t handle_svr_obj_call(O *o, l4_utcb_t *utcb, l4_msgtag_t tag, ARGS ...args) { typedef Svr_arg_pack Pack; enum { Do_reply = IPC_TYPE::rpc::flags_type::Is_call, Short_err = Do_reply ? -L4_EMSGTOOSHORT : -L4_ENOREPLY, }; // XXX: send a reply or just do not reply in case of a cheating client if (L4_UNLIKELY(tag.words() + tag.items() * Item_words > Mr_words)) return l4_msgtag(Short_err, 0, 0, 0); // our whole arguments data structure Pack pack; l4_msg_regs_t *mrs = l4_utcb_mr_u(utcb); int in_pos = Detail::Sizeof::size; unsigned const in_bytes = tag.words() * Word_bytes; in_pos = pack.template get(&mrs->mr[0], in_pos, in_bytes); if (L4_UNLIKELY(in_pos < 0)) return l4_msgtag(Short_err, 0, 0, 0); if (L4_UNLIKELY(pack.template get(mrs->mr, 0, Mr_bytes) < 0)) return l4_msgtag(Short_err, 0, 0, 0); in_pos = pack.template get(&mrs->mr[tag.words()], 0, tag.items() * Item_bytes); if (L4_UNLIKELY(in_pos < 0)) return l4_msgtag(Short_err, 0, 0, 0); asm volatile ("" : "=m" (mrs->mr)); // call the server function long ret = pack.template obj_call(o, args...); if (!Do_reply) return l4_msgtag(-L4_ENOREPLY, 0, 0, 0); // our convention says that negative return value means no // reply data if (L4_UNLIKELY(ret < 0)) return l4_msgtag(ret, 0, 0, 0); // reply with the reply data from the server function int bytes = pack.template set(mrs->mr, 0, Mr_bytes, ret); if (L4_UNLIKELY(bytes < 0)) return l4_msgtag(-L4_EMSGTOOLONG, 0, 0, 0); unsigned words = (bytes + Word_bytes - 1) / Word_bytes; bytes = pack.template set(&mrs->mr[words], 0, Mr_bytes - words * Word_bytes, ret); if (L4_UNLIKELY(bytes < 0)) return l4_msgtag(-L4_EMSGTOOLONG, 0, 0, 0); unsigned const items = bytes / Item_bytes; return l4_msgtag(ret, words, items, 0); } //------------------------------------------------------------------------- template struct Dispatch_call; template struct Dispatch_call, void> { template static l4_msgtag_t call(OBJ *o, l4_utcb_t *utcb, l4_msgtag_t tag, ARGS ...a) { return o->op_dispatch(utcb, tag, a...); } }; template struct Dispatch_call { constexpr static unsigned rmask() { return RPCS::rpc::flags_type::Rights & 3UL; } template static l4_msgtag_t call(OBJ *o, l4_utcb_t *utcb, l4_msgtag_t tag, unsigned rights, ARGS ...a) { if ((rights & rmask()) != rmask()) return l4_msgtag(-L4_EPERM, 0, 0, 0); typedef L4::Typeid::Rights Rights; return handle_svr_obj_call(o, utcb, tag, Rights(rights), a...); } }; template struct Dispatch_call { constexpr static unsigned rmask() { return RPCS::rpc::flags_type::Rights & 3UL; } template static l4_msgtag_t _call(OBJ *o, l4_utcb_t *utcb, l4_msgtag_t tag, unsigned rights, OPCODE_TYPE op, ARGS ...a) { if (L4::Types::Same::value || RPCS::Opcode == op) { if ((rights & rmask()) != rmask()) return l4_msgtag(-L4_EPERM, 0, 0, 0); typedef L4::Typeid::Rights Rights; return handle_svr_obj_call(o, utcb, tag, Rights(rights), a...); } return Dispatch_call::template _call(o, utcb, tag, rights, op, a...); } template static l4_msgtag_t call(OBJ *o, l4_utcb_t *utcb, l4_msgtag_t tag, unsigned rights, ARGS ...a) { OPCODE_TYPE op; unsigned limit = tag.words() * Word_bytes; typedef Svr_xmit S; int err = S::to_svr((char *)l4_utcb_mr_u(utcb)->mr, 0, limit, op, Dir_in(), Cls_data()); if (L4_UNLIKELY(err < 0)) return l4_msgtag(-L4_EMSGTOOSHORT, 0, 0, 0); return _call(o, utcb, tag, rights, op, a...); } }; template<> struct Dispatch_call { template static l4_msgtag_t _call(OBJ *, l4_utcb_t *, l4_msgtag_t, unsigned, int, ARGS ...) { return l4_msgtag(-L4_ENOSYS, 0, 0, 0); } template static l4_msgtag_t call(OBJ *, l4_utcb_t *, l4_msgtag_t, unsigned, ARGS ...) { return l4_msgtag(-L4_ENOSYS, 0, 0, 0); } }; template struct Dispatch_call : Dispatch_call {}; template static l4_msgtag_t dispatch_call(OBJ *o, l4_utcb_t *utcb, l4_msgtag_t tag, unsigned rights, ARGS ...a) { return Dispatch_call::template call(o, utcb, tag, rights, a...); } } // namespace Msg } // namesapce Ipc } // namespace L4