// -*- Mode: C++ -*-
// vim:ft=cpp
/**
 * \file
 * Environment interface
 */
/*
 * (c) 2008-2009 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
 *               Alexander Warg <warg@os.inf.tu-dresden.de>,
 *               Björn Döbel <doebel@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/sys/types.h>

#include <l4/re/rm>
#include <l4/re/parent>
#include <l4/re/mem_alloc>
#include <l4/re/log>
#include <l4/re/consts>

#include <l4/re/env.h>

namespace L4 {
class Scheduler;
}

/**
 * L4 Runtime Environment.
 */
namespace L4Re
{
  class Itas;
  struct Dbg_events;

  /**
   * C++ interface of the initial environment that is provided to an L4 task.
   *
   * The initial environment is provided to each L4 task that is started
   * by an L4Re conform loader, such as the Moe root task. The initial
   * environment provides access to a set of initial capabilities and
   * some additional information about the available resources, such as
   * free UTCBs (see \link l4_utcb_api Virtual Registers \endlink) and
   * available entries in capability table (provided by the micro kernel).
   *
   * Each of the initial capabilities is stored at a fixed index in the
   * task's capability table and the L4 runtime environment provides
   * convenience functions to retrieve the capabilities. See the table below
   * for an comprehensive overview.
   *
   * Name         | Object Type     | Convenience Function
   * ------------ | --------------- | --------------------
   * parent       | L4Re::Parent    | L4Re::Env::parent()
   * user_factory | L4::Factory     | L4Re::Env::user_factory()
   * log          | L4Re::Log       | L4Re::Env::log()
   * main_thread  | L4::Thread      | L4Re::Env::main_thread()
   * rm           | L4Re::Rm        | L4Re::Env::rm()
   * factory      | L4::Factory     | L4Re::Env::factory()
   * task         | L4::Task        | L4Re::Env::task()
   * scheduler    | L4::Scheduler   | L4Re::Env::scheduler()
   * itas         | L4Re::Itas      | L4Re::Env::itas()
   *
   * Additional information found in the initial environment is:
   * - First free entry in capability table
   * - The \link l4_utcb_api UTCB \endlink area (as flexpage)
   * - First free UTCB (address in the UTCB area)
   *
   * \includefile{l4/re/env}
   *
   * For an explanation of the default task capabilites see \ref
   * l4_default_caps_t.
   *
   * For the C interface refer to \ref api_l4re_env.
   */
  class L4_EXPORT Env
  {
  private:
    l4re_env_t _env;
  public:

    /**
     * C++ type for an entry in the initial objects array.
     */
    typedef l4re_env_cap_entry_t Cap_entry;

    /**
     * Returns the initial environment for the current task.
     *
     * \return Pointer to the initial environment class.
     *
     * A typical use of this function is L4Re::Env::env()->\<member\>()
     */
    static Env const *env() noexcept
    { return reinterpret_cast<Env*>(l4re_global_env); }

    /**
     * Object-capability to the parent.
     * \return Parent object-capability
     */
    L4::Cap<Parent> parent() const noexcept
    { return L4::Cap<Parent>(_env.parent); }
    /**
     * Object-capability to the memory allocator.
     * \return Memory allocator object-capability
     */
    L4::Cap<Mem_alloc> mem_alloc() const noexcept
    { return L4::Cap<Mem_alloc>(_env.mem_alloc); }
    /**
     * Object-capability to the user-level object factory.
     */
    L4::Cap<L4::Factory> user_factory() const noexcept
    { return L4::Cap<L4::Factory>(_env.mem_alloc); }
    /**
     * Object-capability to the region map.
     * \return Region map object-capability
     */
    L4::Cap<Rm> rm() const noexcept
    { return L4::Cap<Rm>(_env.rm); }
    /**
     * Object-capability to the logging service.
     * \return Log object-capability
     */
    L4::Cap<Log> log() const noexcept
    { return L4::Cap<Log>(_env.log); }
    /**
     * Object-capability of the first user thread.
     * \return Object-capability of the first user thread.
     */
    L4::Cap<L4::Thread> main_thread() const noexcept
    { return L4::Cap<L4::Thread>(_env.main_thread); }
    /**
     * Object-capability of the user task.
     * \return Object-capability of the user task.
     */
    L4::Cap<L4::Task> task() const noexcept
    { return L4::Cap<L4::Task>(L4RE_THIS_TASK_CAP); }
    /**
     * Object-capability to the factory object available to the task.
     * \return Factory object-capability
     */
    L4::Cap<L4::Factory> factory() const noexcept
    { return L4::Cap<L4::Factory>(_env.factory); }
    /**
     * First available capability selector.
     * \return First capability selector.
     *
     * First capability selector available for use for in the application.
     */
    l4_cap_idx_t first_free_cap() const noexcept
    { return _env.first_free_cap; }
    /**
     * UTCB area of the task.
     * \return UTCB area
     */
    l4_fpage_t utcb_area() const noexcept
    { return _env.utcb_area; }
    /**
     * First free UTCB.
     * \return object-capability
     *
     * First free UTCB within the UTCB area available for the application to
     * use.
     */
    l4_addr_t first_free_utcb() const noexcept
    { return _env.first_free_utcb; }

    /**
     * Get a pointer to the first entry in the initial objects array.
     * \return A pointer to the first entry in the initial objects array.
     */
    Cap_entry const *initial_caps() const noexcept
    { return _env.caps; }

    /**
     * Get the Cap_entry for the object named \a name.
     * \param name is the name of the object.
     * \param l is the length of the name, thus \a name might not be
     *          zero terminated.
     * \return A pointer to the Cap_entry for the object named \a name,
     *         or NULL if no such object was found.
     */
    Cap_entry const *get(char const *name, unsigned l) const noexcept
    { return l4re_env_get_cap_l(name, l, &_env); }

    /**
     * Get the capability selector for the object named \a name.
     * \param name is the name of the object.
     * \param l is the length of the name, thus \a name might not be
     *          zero terminated.
     * \return A capability selector for the object named \a name,
     *         or an invalid capability selector if no such object was found.
     */
    template< typename T >
    L4::Cap<T> get_cap(char const *name, unsigned l) const noexcept
    {
      if (Cap_entry const *e = get(name, l))
	return L4::Cap<T>(e->cap);

      return L4::Cap<T>(-L4_ENOENT);
    }

    /**
     * Get the capability selector for the object named \a name.
     * \param name is the name of the object (zero terminated).
     * \return A capability selector for the object named \a name,
     *         or an invalid capability selector if no such object was found.
     */
    template< typename T >
    L4::Cap<T> get_cap(char const *name) const noexcept
    { return get_cap<T>(name, __builtin_strlen(name)); }

    /**
     * Set parent object-capability.
     * \param c  Parent object-capability
     */
    void parent(L4::Cap<Parent> const &c) noexcept
    { _env.parent = c.cap(); }
    /**
     * Set memory allocator object-capability.
     * \param c  Memory allocator object-capability
     */
    void mem_alloc(L4::Cap<Mem_alloc> const &c) noexcept
    { _env.mem_alloc = c.cap(); }
    /**
     * Set region map object-capability.
     * \param c  Region map object-capability
     */
    void rm(L4::Cap<Rm> const &c) noexcept
    { _env.rm = c.cap(); }
    /**
     * Set log object-capability.
     * \param c  Log object-capability
     */
    void log(L4::Cap<Log> const &c) noexcept
    { _env.log = c.cap(); }
    /**
     * Set object-capability of first user thread.
     * \param c  First thread's object-capability
     */
    void main_thread(L4::Cap<L4::Thread> const &c) noexcept
    { _env.main_thread = c.cap(); }
    /**
     * Set factory object-capability.
     * \param c  Factory object-capability
     */
    void factory(L4::Cap<L4::Factory> const &c) noexcept
    { _env.factory = c.cap(); }
    /**
     * Set first available capability selector.
     * \param c First capability selector available to the application.
     */
    void first_free_cap(l4_cap_idx_t c) noexcept
    { _env.first_free_cap = c; }
    /**
     * Set UTCB area of the task.
     * \param utcbs  UTCB area
     */
    void utcb_area(l4_fpage_t utcbs) noexcept
    { _env.utcb_area = utcbs; }
    /**
     * Set first free UTCB.
     * \param u First UTCB available for the application to use.
     */
    void first_free_utcb(l4_addr_t u) noexcept
    { _env.first_free_utcb = u; }

    /**
     * Get the scheduler capability for the task.
     * \return The capability selector for the default scheduler used for this
     *         task.
     */
    L4::Cap<L4::Scheduler> scheduler() const noexcept
    { return L4::Cap<L4::Scheduler>(_env.scheduler); }

    /**
     * Set the scheduler capability.
     * \param c is the capability to be set as scheduler.
     */
    void scheduler(L4::Cap<L4::Scheduler> const &c) noexcept
    { _env.scheduler = c.cap(); }

    /**
     * Object-capability to the ITAS services.
     * \return ITAS object-capability
     *
     * Attention: this capability might be invalid, depending on the system
     * configuration. Regular applications must not use it directly as it is
     * an implementation detail of the L4Re libc that is subject to change
     * without notice!
     */
    L4::Cap<Itas> itas() const noexcept
    { return L4::Cap<Itas>(_env.itas); }

    /**
     * Set the ITAS capability.
     * \param c is the capability to be set as ITAS.
     */
    void itas(L4::Cap<Itas> const &c) noexcept
    { _env.itas = c.cap(); }

    /**
     * Object-capability to a debugger events service.
     * \return Dbg_events object-capability
     *
     * This capability can be invalid.
     */
    L4::Cap<Dbg_events> dbg_events() const noexcept
    { return L4::Cap<Dbg_events>(_env.dbg_events); }

    /**
     * Set the dbg_events capability.
     * \param dbg_events is the capability to be set
     *                   for the debug events service.
     *
     * Note that the capability can be invalid.
     */
    void dbg_events(L4::Cap<Dbg_events> const &dbg_events) noexcept
    { _env.dbg_events = dbg_events.cap(); }

    /**
     * Set the pointer to the first Cap_entry in the initial objects array.
     * \param first is the first element in the array.
     */
    void initial_caps(Cap_entry *first) noexcept
    { _env.caps = first; }
  };
};
