// vi:set ft=cpp: -*- Mode: C++ -*-
/*
 * (c) 2014-2015 Alexander Warg <alexander.warg@kernkonzept.com>
 *
 * 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 "capability.h"
#include "ipc_server"
#include "ipc_string"
#include <l4/sys/types.h>
#include <l4/sys/utcb.h>
#include <l4/sys/__typeinfo.h>
#include <l4/sys/meta>
#include <l4/cxx/type_traits>

namespace L4 {

// forward for Irqep_t
class Irq;
class Rcv_endpoint;

namespace Ipc_svr {

class Timeout;

/**
 * \ingroup cxx_ipc_server
 * \brief Interface for server-loop related functions.
 *
 * This interface provides access to high-level server-loop related functions,
 * such as management of receive buffers and timeouts.
 */
class Server_iface
{
private:
  Server_iface(Server_iface const &);
  Server_iface const &operator = (Server_iface const &);

public:
  /// Data type expressing server-side demand for receive buffers.
  typedef L4::Type_info::Demand Demand;

  /// Make a server interface
  Server_iface() {}

  // Destroy the server interface
  virtual ~Server_iface() = 0;

  /**
   * \brief Tells the server to allocate buffers for the given demand.
   * \param demand  The total server-side demand of receive buffers needed for
   *                a given interface, see Demand.
   *
   * This function is not called by user applications directly. Usually the
   * server implementation or the registry implementation calls this function
   * whenever a new object is registered at the server.
   */
  virtual int alloc_buffer_demand(Demand const &demand) = 0;

  /**
   * \brief Get capability slot allocated to the given receive buffer.
   * \param index  The receive buffer index of the expected capability
   *               argument (0 <= \c index < \c caps registered with
   *               alloc_buffer_demand()).
   * \pre 0 <= \c index < \c caps registered with alloc_buffer_demand()
   * \return Capability slot currently allocated to the given receive buffer.
   */
  virtual L4::Cap<void> get_rcv_cap(int index) const = 0;

  /**
   * \brief Allocate a new capability for the given receive buffer.
   * \param index  The receive buffer index of the expected capability
   *               argument (0 <= \c index < \c caps registered with
   *               alloc_buffer_demand()).
   * \pre 0 <= \c index < \c caps registered with alloc_buffer_demand()
   * \return 0 on success, < 0 on error.
   */
  virtual int realloc_rcv_cap(int index) = 0;

  /**
   * \brief Add a timeout to the server internal timeout queue.
   * \param timeout  The timeout object to register.
   * \param time     The time (absolute) at which the timeout shall expire.
   * \pre timeout must not be in any queue.
   * \return 0 on success, 1 if timeout is already expired, < 0 on error.
   */
  virtual int add_timeout(Timeout *timeout, l4_kernel_clock_t time) = 0;

  /**
   * \brief Remove the given timeout from the timer queue.
   * \param timeout  The timout object to remove.
   * \return 0 on success, < 0 on error.
   */
  virtual int remove_timeout(Timeout *timeout) = 0;

  /**
   * \brief Get given receive buffer as typed capability.
   * \see get_rcv_cap()
   * \param index  The receive buffer index of the expected capability
   *               argument. (0 <= \c index < \c caps registered with
   *               alloc_buffer_demand().)
   * \pre 0 <= \c index < \c caps registered with alloc_buffer_demand()
   * \return Capability slot currently allocated to the given receive buffer.
   * \note This is a convenience wrapper for get_rcv_cap() to avoid
   *       L4::cap_cast<>().
   */
  template<typename T>
  L4::Cap<T> rcv_cap(int index) const
  { return L4::cap_cast<T>(get_rcv_cap(index)); }

  /**
   * \brief Get receive cap with the given index as generic (void) type.
   * \param index  The index of the cap receive buffer of the expected
   *               capability. (0 <= \c index < \c caps registered with
   *               alloc_buffer_demand().)
   * \return Capability slot currently allocated to the given capability
   *         buffer.
   * \note This is a convenience wrapper for get_rcv_cap().
   */
  L4::Cap<void> rcv_cap(int index) const
  { return get_rcv_cap(index); }
};

inline Server_iface::~Server_iface() {}

} // namespace Ipc_svr

/**
 * Base class for interface implementations.
 *
 * An Epiface is the base interface of objects registered in the server loop.
 * Incomming IPC gets dispatched to the appropriate Epiface object where
 * the call is then handled appropriately.
 *
 * \note Server loops are allowed to internally keep raw pointers to Epiface
 *              objects for dispatching calls. Instances must therefore never
 *              be copied or moved.
 */
struct Epiface
{
  Epiface(Epiface const &) = delete;
  Epiface &operator = (Epiface const &) = delete;

  /// Type for abstract server interface.
  typedef Ipc_svr::Server_iface Server_iface;
  /// Type for server-side receive buffer demand.
  typedef Ipc_svr::Server_iface::Demand Demand;

  class Stored_cap : public Cap<void>
  {
  private:
    enum { Managed = 0x10 };

  public:
    Stored_cap() = default;
    Stored_cap(Cap<void> const &c, bool managed = false)
    : Cap<void>((c.cap() & L4_CAP_MASK) | (managed ? Managed : 0))
    {
      static_assert (!(L4_CAP_MASK & Managed), "conflicting bits used...");
    }

    bool managed() const { return cap() & Managed; }
  };

  /// Make a server object
  Epiface() : _data(0) {}

  /**
   * The abstract handler for client requests to the object.
   * \param tag The message tag for this invocation.
   * \param rights The rights bits in the invoked capability.
   * \param utcb The UTCB used for the invocation.
   * \retval -L4_ENOREPLY  No reply message is send.
   * \retval <0            Error, reply with error code.
   * \retval >=0           Success, reply with return value.
   *
   * This function must be implemented by application specific server
   * objects.
   */
  virtual l4_msgtag_t dispatch(l4_msgtag_t tag, unsigned rights,
                               l4_utcb_t *utcb) = 0;

  /**
   * Get the server-side receive buffer demand for this object.
   * \note This function is usually not implemented directly, but by using
   *       Server_object_t template with an IPC interface definition.
   * \return The needed server-side receive buffers for this object
   */
  virtual Demand get_buffer_demand() const = 0; //{ return Demand(0); }

  /// Destroy the object
  virtual ~Epiface() = 0;

  /**
   * Get the capability to the kernel object belonging to this object.
   * \return Capability for the kernel object behind the server.
   *
   * This is usually either an Ipc_gate or an Irq.
   */
  Stored_cap obj_cap() const { return _cap; }

  /**
   * Get pointer to server interface at which the object is currently registered.
   * \return Pointer to the server at which the object is currently registered,
   *         NULL if the object is not registered at any server.
   */
  Server_iface *server_iface() const { return _data; }

  /**
   * Set server registration info for the object.
   * \param srv      The server to register at
   * \param cap      The capability that connects the object.
   * \param managed  Mark the capability as managed or unmanaged. Typical
   *                 server implementations use this flag to remember whether
   *                 the capability was internally allocated or not.
   * \return 0 on success, -L4_EINVAL if the srv and cap are not consistent.
   */
  int set_server(Server_iface *srv, Cap<void> cap, bool managed = false)
  {
    if ((srv && cap) || (!srv && !cap))
      {
        _data = srv;
        _cap  = Stored_cap(cap, managed);
        return 0;
      }

    return -L4_EINVAL;
  }

  /**
   * Deprecated server registration function.
   */
  void set_obj_cap(Cap<void> const &cap) { _cap = cap; }

private:
  Server_iface *_data;
  Stored_cap _cap;
};

inline Epiface::~Epiface() {}

/**
 * Epiface mixin for generic Kobject-based interfaces.
 *
 * \tparam RPC_IFACE  Data type of the IPC interface definition.
 * \tparam BASE       Base Epiface class.
 *
 */
template<typename RPC_IFACE, typename BASE = Epiface>
struct Epiface_t0 : BASE
{
  /// Data type of the IPC interface definition
  typedef RPC_IFACE Interface;

  /// Get the server-side buffer demand based in \a IFACE.
  typename Type_info::Demand get_buffer_demand() const
  { return typename Kobject_typeid<RPC_IFACE>::Demand(); }

  /**
   * Get the (typed) capability to this object.
   * \return Capability for the kernel object behind the server.
   */
  Cap<RPC_IFACE> obj_cap() const
  { return L4::cap_cast<RPC_IFACE>(BASE::obj_cap()); }
};

/**
 * Epiface implementation for interrupt handlers.
 *
 * \tparam Derived  Irq handler implementation class.
 *                  The class must provide a single function handle_irq().
 * \tparam BASE     Base Epiface class.
 */
template<typename Derived, typename BASE = Epiface,
         bool = cxx::is_polymorphic<BASE>::value>
struct Irqep_t : Epiface_t0<void, BASE>
{
  l4_msgtag_t dispatch(l4_msgtag_t, unsigned, l4_utcb_t *) final
  {
    static_cast<Derived*>(this)->handle_irq();
    return l4_msgtag(-L4_ENOREPLY, 0, 0, 0);
  }

  /**
   * Get the (typed) capability to this object.
   * \return Irq capability for the kernel object behind the server.
   */
  Cap<L4::Irq> obj_cap() const
  { return L4::cap_cast<L4::Irq>(BASE::obj_cap()); }
};

template<typename Derived, typename BASE>
struct Irqep_t<Derived, BASE, false> : Epiface_t0<void, BASE>
{
  l4_msgtag_t dispatch(l4_msgtag_t, unsigned, l4_utcb_t *)
  {
    static_cast<Derived*>(this)->handle_irq();
    return l4_msgtag(-L4_ENOREPLY, 0, 0, 0);
  }

  /**
   * Get the (typed) capability to this object.
   * \return Irq capability for the kernel object behind the server.
   */
  Cap<L4::Irq> obj_cap() const
  { return L4::cap_cast<L4::Irq>(BASE::obj_cap()); }
};

/**
 * Abstract interface for object registries.
 *
 * An object registry allows to register L4::Epiface objects at a server
 * loop either for synchronous RPC messages or for asynchronous IRQ
 * messages.
 */
class Registry_iface
{
public:
  virtual ~Registry_iface() = 0;

  /**
   * Register an L4::Epiface for an IPC gate available in the applications
   * environment under the name `service`.
   * \param o        Pointer to an Epiface object that shall be registered.
   * \param service  Name of the capability that shall be used to connect
   *                 `o` to as a server-side object.
   * \retval L4::Cap<void> The capability known as `service` on success.
   * \retval L4::Cap<void>::Invalid No capability with the given name found.
   *
   * After a successful call to this function `o->obj_cap()` is equal
   * to the capability in the environment with the name given by `service`.
   */
  virtual L4::Cap<void>
  register_obj(L4::Epiface *o, char const *service) = 0;

  /**
   * Register `o` as server-side object for synchronous RPC.
   * \param o  Pointer to an Epiface object that shall be registered as
   *           server-side object for RPC.
   * \retval L4::Cap<void> A valid capability to a new IPC gate.
   * \retval L4::Cap<void>::Invalid  The allocation of the IPC gate
   *                                 has failed.
   *
   * After successful registration `o->obj_cap()` will be the capability
   * of the allocated IPC gate.
   *
   * The function may allocate a capability slot for the object. In that
   * case unregister_obj() is responsible for freeing the slot as well.
   */
  virtual L4::Cap<void>
  register_obj(L4::Epiface *o) = 0;

  /**
   * Register `o` as server-side object for asynchronous IRQs.
   * \param o  Pointer to an Epiface object that shall be registered as
   *           server-side object for IRQs.
   * \retval L4::Cap<L4::Irq>            Capability to a new IRQ object on success.
   * \retval L4::Cap<L4::Irq>::Invalid  The allocation of the IRQ has failed.
   *
   * After successful registration `o->obj_cap()` will be the capability
   * of the allocated IRQ object.
   *
   * The function may allocate a capability slot for the object. In that
   * case unregister_obj() is responsible for freeing the slot as well.
   */
  virtual L4::Cap<L4::Irq> register_irq_obj(L4::Epiface *o) = 0;

  /**
   * Register `o` as server-side object for a pre-allocated capability.
   * \param o    Pointer to an Epiface object that shall be registered as
   *             server-side object.
   * \param ep   Capability to an already allocated capability where `o`
   *             shall be attached as server-side handler. The capability
   *             may point to an IPC gate or an IRQ.
   * \retval L4::Cap<L4::Rcv_endpoint>           Capability `ep` on success.
   * \retval L4::Cap<L4::Rcv_endpoint>::Invalid  The IRQ attach operation has failed.
   *
   * After successful registration `o->obj_cap()` will be equal to `ep`.
   */
  virtual L4::Cap<L4::Rcv_endpoint>
  register_obj(L4::Epiface *o, L4::Cap<L4::Rcv_endpoint> ep) = 0;

  /**
   * Unregister the given object `o` from the server.
   * \param o      Pointer to the Epiface object that shall be unregistered.
   *               The object must have been registered with any of the
   *               register methods if Registry_iface.
   * \param unmap  If true the capability `o->obj_cap()` shall be unmapped
   *               from the local object space.
   *
   * The function always unmaps and frees the capability if it was
   * allocated by either Registry_iface::register_irq_obj(L4::Epiface *),
   * or by Registry_iface::register_obj(L4::Epiface *).
   */
  virtual void
  unregister_obj(L4::Epiface *o, bool unmap = true) = 0;
};

inline Registry_iface::~Registry_iface() {}

namespace Ipc {
namespace Detail {

using namespace L4::Typeid;

template<typename IFACE>
struct Meta_svr
{
  long op_num_interfaces(L4::Meta::Rights)
  { return 1; }

  long op_interface(L4::Meta::Rights, l4_umword_t ifx, long &proto, L4::Ipc::String<char> &name)
  {
    if (ifx > 0)
      return -L4_ERANGE;
    proto = L4::kobject_typeid<IFACE>()->proto();
    if (auto *n = L4::kobject_typeid<IFACE>()->name())
      name.copy_in(n);

    return 0;
  }

  long op_supports(L4::Meta::Rights, l4_mword_t proto)
  { return L4::kobject_typeid<IFACE>()->has_proto(proto); }
};

template<typename IFACE, typename LIST>
struct _Dispatch;

// No match dispatcher found
template<typename IFACE>
struct _Dispatch<IFACE, Iface_list_end>
{
  template< typename THIS, typename A1, typename A2 >
  static l4_msgtag_t f(THIS *, l4_msgtag_t, A1, A2 &)
  { return l4_msgtag(-L4_EBADPROTO, 0, 0, 0); }
};

// call matching p_dispatch() function
template<typename IFACE, typename I, typename LIST >
struct _Dispatch<IFACE, Iface_list<I, LIST> >
{
  // special handling for the meta protocol, to avoid 'using' murx
  template< typename THIS >
  static l4_msgtag_t _f(THIS *, l4_msgtag_t tag, unsigned r,
                        l4_utcb_t *utcb, True::type)
  {
    using L4::Ipc::Msg::dispatch_call;
    typedef L4::Meta::Rpcs Meta;
    typedef Meta_svr<IFACE> Msvr;
    return dispatch_call<Meta>((Msvr *)0, utcb, tag, r);
  }

  // normal dispatch to the op_<func> methods of \a self.
  template< typename THIS >
  static l4_msgtag_t _f(THIS *self, l4_msgtag_t t, unsigned r,
                        l4_utcb_t *utcb, False::type)
  {
    using L4::Ipc::Msg::dispatch_call;
    return dispatch_call<typename I::iface_type::Rpcs>(self, utcb, t, r);
  }

  // dispatch function with switch for meta protocol
  template< typename THIS >
  static l4_msgtag_t f(THIS *self, l4_msgtag_t tag, unsigned r,
                       l4_utcb_t *utcb)
  {
    if (I::Proto == tag.label())
      return _f(self, tag, r, utcb, Bool<I::Proto == (long)L4_PROTO_META>());

    return _Dispatch<IFACE, typename LIST::type>::f(self, tag, r, utcb);
  }
};

template<typename IFACE>
struct Dispatch :
  _Dispatch<IFACE, typename L4::Kobject_typeid<IFACE>::Iface_list::type>
{};

} // namespace Detail

template<typename EPIFACE>
struct Dispatch : Detail::Dispatch<typename EPIFACE::Interface>
{};

} // namespace Ipc

/**
 * Epiface implementation for Kobject-based interface implementations.
 *
 * \tparam Derived  Class providing the interface implementations.
 * \tparam BASE     Epiface base class.
 */
template<typename Derived, typename IFACE, typename BASE = L4::Epiface,
         bool = cxx::is_polymorphic<BASE>::value>
struct Epiface_t : Epiface_t0<IFACE, BASE>
{
  l4_msgtag_t
  dispatch(l4_msgtag_t tag, unsigned rights, l4_utcb_t *utcb) final
  {
    typedef Ipc::Dispatch<Derived> Dispatch;
    return Dispatch::f(static_cast<Derived*>(this), tag, rights, utcb);
  }
};

template<typename Derived, typename IFACE, typename BASE>
struct Epiface_t<Derived, IFACE, BASE, false> : Epiface_t0<IFACE, BASE>
{
  l4_msgtag_t
  dispatch(l4_msgtag_t tag, unsigned rights, l4_utcb_t *utcb)
  {
    typedef Ipc::Dispatch<Derived> Dispatch;
    return Dispatch::f(static_cast<Derived*>(this), tag, rights, utcb);
  }
};

/**
 * \ingroup cxx_ipc_server
 * \brief This registry returns the corresponding server object
 *        based on the label of an Ipc_gate.
 */
class Basic_registry
{
public:
  typedef Epiface Value;
  /**
   * \brief Get the server object for an Ipc_gate label.
   * \param label The label usually stored in an Ipc_gate.
   * \return A pointer to the Epiface identified by the given label.
   */
  static Value *find(l4_umword_t label)
  { return reinterpret_cast<Value*>(label & ~3UL); }

  /**
   * \brief The dispatch function called by the server loop.
   *
   * This function forwards the message to the server object identified by the
   * given \a label.
   *
   * \param tag The message tag used for the invocation.
   * \param label The label used to find the object including the rights bits
   *              of the invoked capability.
   * \param utcb The UTCB used for the invocation.
   * \return The return code from the object's dispatch function or -L4_ENOENT
   *         if the object does not exist.
   */
  static l4_msgtag_t dispatch(l4_msgtag_t tag, l4_umword_t label,
                              l4_utcb_t *utcb)
  {
    return find(label)->dispatch(tag, label, utcb);
  }
};


} // namespace L4