// vi:set ft=cpp: -*- Mode: C++ -*-
/**
 * \file
 * Common task related definitions.
 */
/*
 * (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/sys/task.h>
#include <l4/sys/capability>

namespace L4 {

/**
 * C++ interface of the Task kernel object, see \ref l4_task_api for
 * the C interface.
 *
 * The L4::Task class represents a combination of the address spaces provided
 * by the L4Re micro kernel. A task consists of at least a memory address space
 * and an object address space. On IA32 there is also an IO-port address space
 * associated with an L4::Task.
 *
 * L4::Task objects are created using the L4::Factory interface.
 * \includefile{l4/sys/task}
 */
class Task :
  public Kobject_t<Task, Kobject, L4_PROTO_TASK,
                   Type_info::Demand_t<2> >
{
public:
  /**
   * Map resources available in the source task to a destination task.
   *
   * \param src_task   Capability selector of the source task.
   * \param snd_fpage  Send flexpage that describes an area in the address
   *                   space or object space of the source task.
   * \param snd_base   Send base that describes an offset in the receive window
   *                   of the destination task. The lower bits contain additional
   *                   map control flags (see #l4_fpage_cacheability_opt_t for
   *                   memory mappings, #L4_obj_fpage_ctl for object mappings,
   *                   and #L4_MAP_ITEM_GRANT; also see l4_map_control() and
   *                   l4_map_obj_control()).
   * \utcb_def{utcb}
   *
   * \return Syscall return tag. The function l4_error() shall be used to test
   *         if the map operation was successful.
   *
   * \retval L4_EOK      Operation successful (but see notes below).
   * \retval -L4_EPERM   Insufficient permissions; see precondition.
   * \retval -L4_EINVAL  Invalid source task capability.
   * \retval -L4_IPC_SEMAPFAILED The map operation failed due to limited quota.
   *
   * \pre The invoked Task capability must have the permission #L4_CAP_FPAGE_W.
   *
   * This method allows for asynchronous transfer of capabilities, memory
   * mappings, and IO-port mappings (on IA32) from one task to another.
   * The destination task is the task referenced by the capability on which the
   * map is invoked, and the receive window is the whole address space of that
   * task. By specifying proper rights in the `snd_fpage` and `snd_base`, it is
   * possible to remove rights during transfer.
   *
   * \note If the send flexpage is of type #L4_FPAGE_OBJ, the #L4_CAP_FPAGE_S
   *       right is removed from the transferred capability unless both the
   *       source and destination task capabilities possess the #L4_CAP_FPAGE_S
   *       right themselves.
   *
   * \note Even with l4_error() returning L4_EOK there might be cases where not
   *       all pages of the send flexpage were mapped respectively granted to
   *       the destination task, for instance, if the corresponding mapping in
   *       the destination task does already exist.
   *
   * For more information on spaces and mappings, see
   * \ref l4re_concepts_mapping. The flexpage API is described in more detail at
   * \ref l4_fpage_api.
   *
   * \note For peculiarities when using grant, see #L4_MAP_ITEM_GRANT.
   */
  l4_msgtag_t map(Cap<Task> const &src_task,
                  l4_fpage_t const &snd_fpage, l4_umword_t snd_base,
                  l4_utcb_t *utcb = l4_utcb()) noexcept
  { return l4_task_map_u(cap(), src_task.cap(), snd_fpage, snd_base, utcb); }

  /**
   * Revoke rights from the task.
   *
   * \param fpage     Flexpage that describes an area in one capability space
   *                  of `this` task and the rights to revoke.
   * \param map_mask  Unmap mask, see #l4_unmap_flags_t
   *
   * \utcb_def{utcb}
   *
   * \return Syscall return tag
   *
   * This method allows to revoke rights from the destination task. The rights
   * to revoke are specified in the flexpage, see l4_fpage_rights(). For a
   * flexpage describing IO ports or memory, it also revokes rights from all the
   * tasks that got the rights delegated from the destination task (i.e., this
   * operation does a recursive rights revocation). The capability is unmapped
   * if certain rights are specified, see below for details. It is guaranteed
   * that the rights revocation is completed before this function returns.
   *
   * Note that this function cannot be used to revoke the reference counting
   * permission (see #L4_FPAGE_C_REF_CNT) or the IPC-gate server permission
   * (see #L4_FPAGE_C_IPCGATE_SVR) from object capabilities.
   *
   * It depends on the platform and the object type which rights need to be
   * specified in the `rights` field of `fpage` to unmap a capability:
   *  - An object capability is unmapped if and only if the #L4_CAP_FPAGE_R
   *    right bit is set.
   *  - An IO port is unmapped if and only if any right bit is set.
   *  - Memory is unmapped if and only if the #L4_FPAGE_RO right bit is set.
   *
   * \note Depending on the page-table features supported by the hardware,
   *       revocation of certain rights from a memory capability can be a no-op
   *       (i.e., the rights are not revoked). Further, revocation of certain
   *       rights may grant other rights which were not present before. For
   *       instance, on an architecture without support for NX, revoking X does
   *       nothing. For another example, revoking only X from an execute-only
   *       page grants read permission (because the mapping remains present in
   *       the page table).
   *
   * \note If the reference counter of a kernel object referenced in `fpage`
   *       goes down to zero (as a result of deleting capabilities), the
   *       deletion of the object is initiated. Objects are not destroyed until
   *       all other kernel objects holding a reference to it drop the
   *       reference.
   */
  l4_msgtag_t unmap(l4_fpage_t const &fpage,
                    l4_umword_t map_mask,
                    l4_utcb_t *utcb = l4_utcb()) noexcept
  { return l4_task_unmap_u(cap(), fpage, map_mask, utcb); }

  /**
   * Revoke rights from a task.
   *
   * \param fpages      An array of flexpages. Each item describes an area in
   *                    one capability space of `this` task.
   * \param num_fpages  Number of fpages in the `fpages` array.
   * \param map_mask    Unmap mask, see #l4_unmap_flags_t.
   * \utcb_def{utcb}
   *
   * Revoke rights for an array of flexpages, see #unmap for details.
   *
   * \pre The caller needs to take care that `num_fpages` is not bigger
   *      than L4_UTCB_GENERIC_DATA_SIZE - 2.
   */
  l4_msgtag_t unmap_batch(l4_fpage_t const *fpages,
                          unsigned num_fpages,
                          l4_umword_t map_mask,
                          l4_utcb_t *utcb = l4_utcb()) noexcept
  { return l4_task_unmap_batch_u(cap(), fpages, num_fpages, map_mask, utcb); }

  /**
   * Release capability and delete object.
   *
   * \param obj   Capability index of the object to delete.
   * \utcb_def{utcb}
   *
   * \return Syscall return tag
   *
   * If `obj` has the delete permission, initiates the deletion of the object.
   * This implies that all capabilities for that object are gone afterwards.
   * However, kernel-internally, objects are not destroyed until all other
   * kernel objects holding a reference to it drop the reference. Hence, quota
   * used by that object might not be freed immediately.
   *
   * If `obj` does not have the delete permission, no error will be reported and
   * only the capability `obj` is removed. (Note that, depending on the object’s
   * reference counter, this might still imply initiation of deletion.)
   *
   * This operation is equivalent to unmap() with #L4_FP_DELETE_OBJ flag.
   */
  l4_msgtag_t delete_obj(L4::Cap<void> obj,
                         l4_utcb_t *utcb = l4_utcb()) noexcept
  { return l4_task_delete_obj_u(cap(), obj.cap(), utcb); }

  /**
   * Release object capability.
   *
   * \param cap   Capability selector of the object to release.
   * \utcb_def{utcb}
   *
   * \return Syscall return tag.
   *
   * This operation unmaps the capability from `this` task. This operation is
   * equivalent to unmapping a single object capability by specifying all object
   * rights as unmap mask.
   *
   * \note If the reference counter of the kernel object referenced by `cap`
   *       goes down to zero, the deletion of the object is initiated. Objects
   *       are not destroyed until all other kernel objects holding a reference
   *       to it drop the reference.
   */
  l4_msgtag_t release_cap(L4::Cap<void> cap,
                          l4_utcb_t *utcb = l4_utcb()) noexcept
  { return l4_task_release_cap_u(this->cap(), cap.cap(), utcb); }

  /**
   * Check whether a capability is present (refers to an object).
   *
   * \param cap   Valid capability to check for presence.
   * \utcb_def{utcb}
   *
   * \retval "l4_msgtag_t::label() > 0"   Capability is present (refers to an
   *                                      object).
   * \retval "l4_msgtag_t::label() == 0"  No capability present (void object).
   *
   * A capability is considered present when it refers to an existing
   * kernel object.
   *
   * \pre `cap` must be a valid capability (i.e. `cap.is_valid() == true`).
   *      If you are unsure about the validity of your capability use
   *      L4::Cap.validate() instead.
   */
  l4_msgtag_t cap_valid(Cap<void> const &cap,
                        l4_utcb_t *utcb = l4_utcb()) noexcept
  { return l4_task_cap_valid_u(this->cap(), cap.cap(), utcb); }

  /**
   * Test whether two capabilities point to the same object with the same
   * permissions (only considering selected permissions).
   *
   * \param cap_a  Capability selector for the first capability to compare.
   * \param cap_b  Capability selector for the second capability to compare.
   * \utcb_def{utcb}
   *
   * \retval "l4_msgtag_t::label() = 1"  The compared capabilities point to the
   *                                     same object with same considered
   *                                     permission.
   * \retval "l4_msgtag_t::label() = 0"  The compared capabilities do **not**
   *                                     point to the same object or differ in
   *                                     the considered permission.
   *
   * - For L4::Ipc_gate objects, only the permissions #L4_CAP_FPAGE_W,
   *   #L4_CAP_FPAGE_S, and #L4_FPAGE_C_OBJ_RIGHT1 are considered for the
   *   comparison. Differences in other permissions are ignored.
   * - For other objects, only the permissions #L4_CAP_FPAGE_W and
   *   #L4_CAP_FPAGE_S are considered for the comparison. Differences in other
   *   permissions are ignored.
   *
   * Note that having the #L4_CAP_FPAGE_R permission is implicit in possessing
   * the capability.
   */
  l4_msgtag_t cap_equal(Cap<void> const &cap_a,
                        Cap<void> const &cap_b,
                        l4_utcb_t *utcb = l4_utcb()) noexcept
  { return l4_task_cap_equal_u(cap(), cap_a.cap(), cap_b.cap(), utcb); }

  /**
   * Add kernel-user memory.
   *
   * \param[in,out] fpage  Flexpage describing the virtual area the memory goes
   *                       to. On systems without MMU, the flexpage is adjusted
   *                       to reflect the actually allocated physical address.
   * \utcb_def{utcb}
   *
   * \return Syscall return tag
   *
   * Kernel-user memory (ku_mem) is memory that is shared between the kernel and
   * user-space. It is needed for the UTCB area of threads (see
   * L4::Thread::Attr::bind()) and for (extended) vCPU state. Note that existing
   * kernel-user memory cannot be unmapped or mapped somewhere else.
   *
   * \note The amount of kernel-user memory that can be allocated at once is
   *       limited by the used kernel implementation. The minimum allocatable
   *       amount is one page (`L4_PAGESIZE`). A portable implementation should
   *       not depend on allocations greater than 16KiB to succeed.
   *
   * \note This function is only guaranteed to work on L4::Task objects. It
   *       might or might not work on L4::Vm objects or on L4Re::Dma_space
   *       objects but there is no practical use for adding kernel-user memory
   *       to L4::Vm objects or to L4Re::Dma_space objects.
   */
  l4_msgtag_t add_ku_mem(l4_fpage_t *fpage,
                         l4_utcb_t *utcb = l4_utcb()) noexcept
  { return l4_task_add_ku_mem_u(cap(), fpage, utcb); }

};
}


