// vi:set ft=cpp: -*- Mode: C++ -*-
/*
 * (c) 2008-2009 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
 *               Alexander Warg <warg@os.inf.tu-dresden.de>
 *     economic rights: Technische Universität Dresden (Germany)
 *
 * License: see LICENSE.spdx (in this directory or the directories above)
 */

#pragma once

#include <l4/re/util/cap_alloc>
#include <l4/re/util/unique_cap>
#include <l4/re/consts>
#include <l4/re/env>

#include <l4/sys/cxx/ipc_server_loop>
#include <l4/sys/factory>
#include <l4/sys/task>
#include <l4/sys/thread>
#include <l4/sys/ipc_gate>

#include <l4/cxx/exceptions>

namespace L4Re { namespace Util {

/**
 * A registry that manages server objects and their attached IPC gates for
 * a single server loop for a specific thread.
 *
 * This class manages most of the setup of a server object. If necessary,
 * an IPC gate is created, the specified thread is bound to the IPC gate.
 * Incoming IPC is dispatched to the server object based on
 * the label of the IPC gate.
 *
 * The object registry is also able to manage IRQ endpoints. They require a
 * different method for the object creation. Otherwise they are handled in
 * the same way as IPC gates: a server object is responsible to process
 * the incoming interrupts.
 */
class Object_registry :
  public L4::Basic_registry,
  public L4::Registry_iface
{
  /**
   * Handler class for stale requests from servers that have been
   * deregistered.
   */
  struct Null_handler : L4::Epiface_t<Null_handler, L4::Kobject>
  {};

protected:
  L4::Cap<L4::Thread> _server;
  L4::Cap<L4::Factory> _factory;
  L4::Ipc_svr::Server_iface *_sif;

private:
  Null_handler _null_handler;

public:
  /**
   * Create a registry for the main thread of the task using the default factory.
   *
   * \param sif   Server loop interface.
   */
  explicit
  Object_registry(L4::Ipc_svr::Server_iface *sif)
  : _server(L4Re::Env::env()->main_thread()),
    _factory(L4Re::Env::env()->factory()),
    _sif(sif)
  {}

  /**
   * Create a registry for arbitrary threads.
   *
   * \param sif     Server loop interface.
   * \param server  Capability to the thread that executes the server objects.
   * \param factory Capability to a factory object capable of creating new
   *                IPC gates.
   */
  Object_registry(L4::Ipc_svr::Server_iface *sif,
                  L4::Cap<L4::Thread> server,
                  L4::Cap<L4::Factory> factory)
  : _server(server), _factory(factory), _sif(sif)
  {}

private:
  typedef L4::Ipc_svr::Server_iface Server_iface;
  typedef Server_iface::Demand Demand;

  L4::Cap<L4::Rcv_endpoint>
  _register_ep(L4::Epiface *o, L4::Cap<L4::Rcv_endpoint> ep,
               Demand const &demand)
  {
    int err = _sif->alloc_buffer_demand(demand);
    if (err < 0)
      return L4::Cap<L4::Rcv_endpoint>(err | L4_INVALID_CAP_BIT);

    err = o->set_server(_sif, ep);
    if (err < 0)
      return L4::Cap<L4::Rcv_endpoint>(err | L4_INVALID_CAP_BIT);

    l4_umword_t id = l4_umword_t(o);
    err = l4_error(ep->bind_thread(_server, id));
    if (err < 0)
      return L4::Cap<L4::Rcv_endpoint>(err | L4_INVALID_CAP_BIT);

    return ep;
  }

  L4::Cap<void> _register_ep(L4::Epiface *o, char const *service,
                             Demand const &demand)
  {
    L4::Cap<L4::Rcv_endpoint> cap = L4Re::Env::env()->get_cap<L4::Rcv_endpoint>(service);
    if (!cap.is_valid())
      return cap;

    return _register_ep(o, cap, demand);
  }

  L4::Cap<void> _register_gate(L4::Epiface *o, Demand const &demand)
  {
    int err = _sif->alloc_buffer_demand(demand);
    if (err < 0)
      return L4::Cap<void>(err | L4_INVALID_CAP_BIT);

   auto cap = L4Re::Util::make_unique_cap<L4::Kobject>();

    if (!cap.is_valid())
      return cap.get();

    l4_umword_t id = l4_umword_t(o);
    err = l4_error(_factory->create_gate(cap.get(), _server, id));
    if (err < 0)
      return L4::Cap<void>(err | L4_INVALID_CAP_BIT);

    err = o->set_server(_sif, cap.get(), true);
    if (err < 0)
      return L4::Cap<void>(err | L4_INVALID_CAP_BIT);

    return cap.release();
  }

  L4::Cap<L4::Irq> _register_irq(L4::Epiface *o,
                                 Demand const &demand)
  {
    int err = _sif->alloc_buffer_demand(demand);
    if (err < 0)
      return L4::Cap<L4::Irq>(err | L4_INVALID_CAP_BIT);

    auto cap = L4Re::Util::make_unique_cap<L4::Irq>();

    if (!cap.is_valid())
      return cap.get();

    l4_umword_t id = l4_umword_t(o);
    err = l4_error(_factory->create(cap.get()));
    if (err < 0)
      return L4::Cap<L4::Irq>(err | L4_INVALID_CAP_BIT);

    err = o->set_server(_sif, cap.get(), true);
    if (err < 0)
      return L4::Cap<L4::Irq>(err | L4_INVALID_CAP_BIT);

    err = l4_error(cap->bind_thread(_server, id));
    if (err < 0)
      return L4::Cap<L4::Irq>(err | L4_INVALID_CAP_BIT);

    return cap.release();
  }

  static Demand _get_buffer_demand(L4::Epiface *o)
  { return o->get_buffer_demand(); }

  template<typename T>
  static Demand _get_buffer_demand(T *,
      typename L4::Kobject_typeid<typename T::Interface>::Demand
        d = typename L4::Kobject_typeid<typename T::Interface>::Demand())
  { return d; }

public:
  /**
   * Register a new server object to a pre-allocated receive endpoint.
   *
   * \param o       Server object that handles IPC requests.
   * \param service Name of a pre-allocated receive endpoint.
   *
   * \retval L4::Cap<void> The capability known as `service` on success.
   * \retval L4::Cap<void>::Invalid No capability with the given name found.
   *
   * The interface must be freed with unregister_obj() by the caller
   * to unbind the thread from the capability.
   */
  L4::Cap<void> register_obj(L4::Epiface *o, char const *service) override
  {
    return _register_ep(o, service, _get_buffer_demand(o));
  }

  /**
   * Register a new server object on a newly allocated capability.
   *
   * \param o  Server object that handles IPC requests.
   *
   * \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.
   *
   * The IPC gate will be allocated using the registry's factory. The
   * caller must call unregister_obj() to free all resources.
   */
  L4::Cap<void> register_obj(L4::Epiface *o) override
  {
    return _register_gate(o, _get_buffer_demand(o));
  }

  /**
   * Register a handler for an interrupt.
   *
   * \param o  Server object that handles 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.
   *
   * The IRQ will be newly allocated using the registry's factory object. The
   * caller must call unregister_obj() to free all resources.
   */
  L4::Cap<L4::Irq> register_irq_obj(L4::Epiface *o) override
  {
    return _register_irq(o, _get_buffer_demand(o));
  }

  /**
   * Register a handler for an already existing interrupt.
   *
   * \param o    Server object that handles the IPC.
   * \param ep   Capability to a receive endpoint, may be a hardware or
   *             software interrupt or an IPC gate.
   *
   * \retval L4::Cap<L4::Rcv_endpoint>           Capability `ep` on success.
   * \retval L4::Cap<L4::Rcv_endpoint>::Invalid  The IRQ attach operation has failed.
   *
   * The interface must be freed with unregister_obj() by the caller
   * to unbind the thread from the capability.
   */
  L4::Cap<L4::Rcv_endpoint>
  register_obj(L4::Epiface *o, L4::Cap<L4::Rcv_endpoint> ep) override
  {
    return _register_ep(o, ep, _get_buffer_demand(o));
  }


  /**
   * Remove a server object from the handler list.
   *
   * \param o      Server object to unbind.
   * \param unmap  Specifies if the object capability shall be unmapped (true)
   *               or not. The default (true) is to unmap the capability.
   *
   * The capability used by the server object will be unmapped if `unmap` is
   * true.
   */
  void unregister_obj(L4::Epiface *o, bool unmap = true) override
  {
    L4::Epiface::Stored_cap c;

    if (!o || !o->obj_cap().is_valid())
      return;

    c = o->obj_cap();

    if (unmap)
      L4::Cap<L4::Task>(L4Re::This_task)->unmap(c.fpage(), L4_FP_ALL_SPACES);

    // make sure unhandled ipc ends up with the null handler
    L4::Thread::Modify_senders todo;
    todo.add(~3UL, reinterpret_cast<l4_umword_t>(o),
             ~0UL, reinterpret_cast<l4_umword_t>
                   (static_cast<L4::Epiface *>(&_null_handler)));
    _server->modify_senders(todo);

    // we use bit 4 to indicated an internally allocated cap
    if (c.managed())
      cap_alloc.free(c);

    o->set_server(0, L4::Cap<void>::Invalid);
  }
};

/**
 * A server loop object which has a Object_registry included.
 */
template< typename LOOP_HOOKS = L4::Ipc_svr::Default_loop_hooks >
class Registry_server : public L4::Server<LOOP_HOOKS>
{
private:
  typedef L4::Server<LOOP_HOOKS> Base;
  Object_registry _registry;

public:
  /**
   * Create a new server loop object for the main thread of the task.
   *
   * \pre Must be called from the main thread or behaviour is undefined.
   */
  Registry_server() : _registry(this)
  {}

  /**
   * Create a new server loop object for an arbitrary thread and factory.
   *
   * \param server  Capability to thread running the server loop.
   * \param factory Capability to factory object used to create new IPC gates.
   *
   * \deprecated Note that this variant of the constructor is deprecated,
   * please do not supply the UTCB pointer, it's not used.
   */
  Registry_server(l4_utcb_t *, L4::Cap<L4::Thread> server,
                  L4::Cap<L4::Factory> factory) L4_DEPRECATED("Omit UTCB pointer argument")
  : _registry(this, server, factory)
  {}

  /**
   * Create a new server loop object for an arbitrary thread and factory.
   *
   * \param server  Capability to thread running the server loop.
   * \param factory Capability to factory object used to create new IPC gates.
   */
  Registry_server(L4::Cap<L4::Thread> server,
                  L4::Cap<L4::Factory> factory)
  : _registry(this, server, factory)
  {}

  /** Return registry of this server loop. */
  Object_registry const *registry() const { return &_registry; }
  /** Return registry of this server loop. */
  Object_registry *registry() { return &_registry; }

  /**
   * Start the server loop.
   *
   * \param utcb  The UTCB of the thread running the server loop, defaults to
   *              l4_utcb().
   */
  void L4_NORETURN loop(l4_utcb_t *utcb = l4_utcb())
  { Base::template loop<L4::Runtime_error, Object_registry &>(_registry, utcb); }

  /**
   * Start the server loop with error printing.
   *
   * \tparam Printer  The printer type.
   * \param printer   The printer object on which printf() is called.
   * \param utcb      The UTCB of the thread running the server loop, defaults
   *                  to l4_utcb().
   */
  template <typename Printer>
  void L4_NORETURN loop_dbg(Printer printer, l4_utcb_t *utcb = l4_utcb())
  {
    Base::template loop_dbg<L4::Runtime_error, Object_registry &, Printer>
      (_registry, printer, utcb);
  }
};
}}
