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

namespace L4 {

/**
 * C++ L4 kernel thread interface, see \ref l4_thread_api for the C interface.
 *
 * The Thread class defines a thread of execution in the L4 context.
 * Usually user-level and kernel threads are mapped 1:1 to each other.
 * Thread kernel objects are created using a factory, see the L4::Factory API
 * (L4::Factory::create()).
 *
 * Amongst other things an L4::Thread encapsulates:
 * - CPU state
 *   - General-purpose registers
 *   - Program counter
 *   - Stack pointer
 * - FPU state
 * - Scheduling parameters, see the L4::Scheduler API
 * - Execution state
 *   - Blocked, Runnable, Running
 *
 * Thread objects provide an API for
 * - Thread configuration and manipulation
 * - Thread switching.
 *
 * On ARM newly created threads run in EL0 by default and the exception level
 * can be changed there with ex_regs().
 *
 * \includefile{l4/sys/thread}
 *
 * For the C interface see the \ref l4_thread_api API. For more elaborated
 * documentation on the vCPU feature see \ref l4_vcpu_api.
 */
class Thread :
  public Kobject_t<Thread, Snd_destination, L4_PROTO_THREAD,
                   Type_info::Demand_t<1> >
{
public:
  /**
   * Exchange basic thread registers.
   *
   * \param ip     New instruction pointer, use ~0UL to leave the
   *               instruction pointer unchanged.
   * \param sp     New stack pointer, use ~0UL to leave the stack
   *               pointer unchanged.
   * \param flags  Ex-regs flags, see #L4_thread_ex_regs_flags.
   * \utcb_def{utcb}
   *
   * \return System call return tag.
   *
   * This method allows to manipulate and start a thread. The basic
   * functionality is to set the instruction pointer and the stack pointer of a
   * thread. Additionally, this method allows also to cancel ongoing IPC
   * operations and to force the thread to raise an artificial exception (see
   * `flags`). If the thread is in an IPC operation or if
   * #L4_THREAD_EX_REGS_TRIGGER_EXCEPTION forces an IPC then changes in IP and
   * SP take effect directly after returning from this IPC. On ARM this method
   * allows to change the execption level, see #L4_thread_ex_regs_flags_arm and
   * #L4_thread_ex_regs_flags_arm64.
   *
   * The thread is started using L4::Scheduler::run_thread(). However, if at
   * the time L4::Scheduler::run_thread() is called, the instruction pointer of
   * the thread is invalid, a later call to ex_regs() with a valid instruction
   * pointer might start the thread.
   */
  l4_msgtag_t ex_regs(l4_addr_t ip, l4_addr_t sp,
                      l4_umword_t flags,
                      l4_utcb_t *utcb = l4_utcb()) noexcept
  { return l4_thread_ex_regs_u(cap(), ip, sp, flags, utcb); }

  /**
   * Exchange basic thread registers and return previous values.
   *
   * \param[in,out] ip     New instruction pointer, use ~0UL to leave the
   *                       instruction pointer unchanged, return previous
   *                       instruction pointer.
   * \param[in,out] sp     New stack pointer, use ~0UL to leave the stack
   *                       pointer unchanged, returns previous stack pointer.
   * \param[in,out] flags  Ex-regs flags, see #L4_thread_ex_regs_flags, return
   *                       previous CPU flags of the thread.
   * \utcb_def{utcb}
   *
   * \return System call return tag. [out] parameters are only valid if the
   *         function returns successfully. Use l4_error() to check.
   *
   * This method allows to manipulate and start a thread. The basic
   * functionality is to set the instruction pointer and the stack pointer of a
   * thread. Additionally, this method allows also to cancel ongoing IPC
   * operations and to force the thread to raise an artificial exception (see
   * `flags`). If the thread is in an IPC operation or if
   * #L4_THREAD_EX_REGS_TRIGGER_EXCEPTION forces an IPC then changes in IP and
   * SP take effect directly after returning from this IPC. On ARM this method
   * allows to change the execption level, see #L4_thread_ex_regs_flags_arm and
   * #L4_thread_ex_regs_flags_arm64.
   *
   * The thread is started using L4::Scheduler::run_thread(). However, if at
   * the time L4::Scheduler::run_thread() is called, the instruction pointer of
   * the thread is invalid, a later call to ex_regs() with a valid instruction
   * pointer might start the thread.
   */
  l4_msgtag_t ex_regs(l4_addr_t *ip, l4_addr_t *sp,
                      l4_umword_t *flags,
                      l4_utcb_t *utcb = l4_utcb()) noexcept
  { return l4_thread_ex_regs_ret_u(cap(), ip, sp, flags, utcb); }


  /**
   * Thread attributes used for control().
   *
   * This class is responsible for initializing various attributes of a
   * thread in a UTCB for the control() method.
   *
   * \note Instantiation of this class starts the preparation of the UTCB. Do
   *       not invoke any non-Attr functions between the instantiation and the
   *       call to L4::Thread::control().
   *
   * \see \ref l4_thread_control_api for some more details.
   */
  class Attr
  {
  private:
     friend class L4::Thread;
     l4_utcb_t *_u;

   public:
     /**
      * Create a thread-attribute object with the given UTCB.
      *
      * \param utcb  The UTCB to use for the later L4::Thread::control()
      *              function. Usually this is the UTCB of the calling thread.
      *              See #l4_utcb().
      */
     explicit Attr(l4_utcb_t *utcb = l4_utcb()) noexcept : _u(utcb)
     { l4_thread_control_start_u(utcb); }

     /**
      * Set the pager capability selector.
      *
      * \param pager  The capability selector that shall be used for page-fault
      *               messages. This capability selector must be valid within
      *               the task the thread is bound to.
      */
     void pager(Cap<void> const &pager) noexcept
     { l4_thread_control_pager_u(pager.cap(), _u); }

     /**
      * Get the capability selector used for page-fault messages.
      *
      * \return The capability selector used to send page-fault messages. The
      *         selector is valid in the task the thread is bound to.
      */
     Cap<void> pager() noexcept
     { return Cap<void>(l4_utcb_mr_u(_u)->mr[1]); }

     /**
      * Set the exception-handler capability selector.
      *
      * \param exc_handler  The capability selector that shall be used for
      *                     exception messages. This capability selector must
      *                     be valid within the task the thread is bound to.
      */
     void exc_handler(Cap<void> const &exc_handler) noexcept
     { l4_thread_control_exc_handler_u(exc_handler.cap(), _u); }

     /**
      * Get the capability selector used for exception messages.
      *
      * \return The capability selector used to send exception messages. The
      *         selector is valid in the task the thread is bound to.
      */
     Cap<void> exc_handler() noexcept
     { return Cap<void>(l4_utcb_mr_u(_u)->mr[2]); }

     /**
      * Bind the thread to a task.
      *
      * \param thread_utcb  The thread’s UTCB address within the task it shall
      *                     be bound to. The address must be aligned
      *                     (architecture dependent; at least word aligned) and
      *                     it must point to at least #L4_UTCB_OFFSET bytes of
      *                     kernel-user memory.
      * \param task         The task the thread shall be bound to.
      *
      * \pre The thread must not be bound to a task yet.
      *
      * \pre The capability `task` must have the permission #L4_CAP_FPAGE_S,
      *      otherwise the later call to #L4::Thread::control() with this #Attr
      *      object will fail with #L4_EPERM.
      *
      * A thread may execute code in the context of a task if and only if the
      * thread is bound to the task. To actually start execution,
      * L4::Thread::ex_regs() needs to be used. Execution in the context of the
      * task means that the code has access to all the task’s resources (and
      * only those). The executed code itself must be one of those resources. A
      * thread can be bound at most once to a task.
      *
      * \note The UTCBs of different threads in the same task should not overlap
      *       in order to prevent data corruption.
      */
     void bind(l4_utcb_t *thread_utcb, Cap<Task> const &task) noexcept
     { l4_thread_control_bind_u(thread_utcb, task.cap(), _u); }

     /**
      * \copydoc l4_thread_control_alien
      */
     void alien(int on) noexcept
     { l4_thread_control_alien_u(_u, on); }
   };

  /**
   * Commit the given thread-attributes object.
   *
   * \param attr the attribute object to commit to the thread.
   *
   * \return Syscall return tag containing one of the following return codes.
   *
   * \retval L4_EOK      Operation successful.
   * \retval -L4_EPERM   Insufficient permissions; see precondition.
   * \retval -L4_EINVAL  Malformed thread-attributes.
   *
   * \pre The invoked Thread capability must have the permission
   *      #L4_CAP_FPAGE_S. When using #Attr::bind(), also the respective Task
   *      capability must have the permission #L4_CAP_FPAGE_S.
   */
  l4_msgtag_t control(Attr const &attr) noexcept
  { return l4_thread_control_commit_u(cap(), attr._u); }

  /**
   * Switch execution to this thread.
   *
   * \utcb_def{utcb}
   *
   * \note The current time slice is inherited to this thread.
   */
  l4_msgtag_t switch_to(l4_utcb_t *utcb = l4_utcb()) noexcept
  { return l4_thread_switch_u(cap(), utcb); }

  /**
   * Get consumed time of thread in us.
   *
   * \param[out] us    Consumed time in µs.
   * \utcb_def{utcb}
   *
   * \return Syscall return tag.
   */
  l4_msgtag_t stats_time(l4_kernel_clock_t *us,
                         l4_utcb_t *utcb = l4_utcb()) noexcept
  { return l4_thread_stats_time_u(cap(), us, utcb); }

  /**
   * Resume from vCPU asynchronous IPC handler, start.
   *
   * \utcb_def{utcb}
   *
   * \return Message tag to be used for l4_sndfpage_add() and
   *         l4_thread_vcpu_resume_commit()
   *
   * The vCPU resume functionality is split in multiple functions to allow the
   * specification of additional send-flexpages using l4_sndfpage_add().
   *
   * The asynchronous IPC handling is described at \ref l4_vcpu_api.
   *
   * \see l4_thread_vcpu_resume_start
   */
  l4_msgtag_t vcpu_resume_start(l4_utcb_t *utcb = l4_utcb()) noexcept
  { return l4_thread_vcpu_resume_start_u(utcb); }

  /**
   * Resume from vCPU asynchronous IPC handler, commit.
   *
   * \param tag   Tag to use, returned by l4_thread_vcpu_resume_start().
   * \utcb_def{utcb}
   *
   * \return Syscall return tag containing one of the following return codes.
   *
   * \retval 0           Indicates a VM exit, provided that `thread` is in
   *                     extended vCPU mode with virtual interrupts cleared.
   * \retval 1           Indicates an incoming IPC message, provided that the
   *                     `thread` is in extended vCPU mode with virtual
   *                     interrupts cleared.
   * \retval -L4_EPERM   The user task capability set in the vCPU state is
   *                     missing the #L4_CAP_FPAGE_S right. On Intel's VT-x
   *                     (VMX): The vCPU context capability set in the extended
   *                     vCPU state is missing the #L4_CAP_FPAGE_S right.
   * \retval -L4_ENOENT  The user task capability set in the vCPU state is
   *                     invalid.
   * \retval -L4_EINVAL  `thread` is not the current running thread, or does not
   *                     have the vCPU feature enabled. On Intel's VT-x (VMX):
   *                     No vCPU context associated with the extended vCPU
   *                     state.
   * \retval -L4_EBUSY   On Intel's VT-x (VMX): The vCPU context associated
   *                     with the extended vCPU state is already active on a
   *                     different CPU.
   * \retval -L4_ENODEV  On Intel's VT-x (VMX): The vCPU context associated
   *                     with the extended vCPU state cannot be initialized or
   *                     activated.
   * \retval <0          A supplied mapping failed.
   *
   * All flexpages in the UTCB (added with l4_sndfpage_add() after
   * l4_thread_vcpu_resume_start()) are unconditionally mapped into the
   * user task configured in the vCPU state.
   *
   * To resume into another address space, the capability to the target \ref
   * l4_task_api (or L4::Vm) must be set in l4_vcpu_state_t::user_task together
   * with #L4_VCPU_F_USER_MODE. The capability selector must have all lower bits
   * clear (see #L4_CAP_MASK). The kernel adds the #L4_SYSF_SEND flag there to
   * indicate that the capability has been referenced in the kernel. Consecutive
   * resumes will not reference the task capability again until all lower bits
   * are cleared again. To release a task use a different task capability or use
   * an invalid capability with the #L4_SYSF_REPLY flag set.
   *
   * The asynchronous IPC handling is described at \ref l4_vcpu_api.
   *
   * \see l4_thread_vcpu_resume_commit
   */
  l4_msgtag_t vcpu_resume_commit(l4_msgtag_t tag,
                                 l4_utcb_t *utcb = l4_utcb()) noexcept
  { return l4_thread_vcpu_resume_commit_u(cap(), tag, utcb); }

   /**
    * Enable the vCPU feature for the thread.
    *
    * \param vcpu_state  A virtual address pointing to a l4_vcpu_state_t. It
    *                    must be a valid kernel-user-memory address (see
    *                    L4::Task::add_ku_mem()).
    * \utcb_def{utcb}
    *
    * \return Syscall return tag.
    *
    * This function enables the vCPU feature of `this` thread
    *
    * The kernel-user memory starting at `vcpu_state` must be at least 128-byte
    * aligned and must cover the size of l4_vcpu_state_t.
    *
    * The asynchronous IPC handling is described at \ref l4_vcpu_api.
    *
    * \note Disabling of the vCPU feature is optional and currently not
    *       supported.
    */
   l4_msgtag_t vcpu_control(l4_addr_t vcpu_state, l4_utcb_t *utcb = l4_utcb())
     noexcept
   { return l4_thread_vcpu_control_u(cap(), vcpu_state, utcb); }

   /**
    * Enable the extended vCPU feature for the thread.
    *
    * \param ext_vcpu_state  The virtual address where the kernel shall store
    *                        the vCPU state in case of vCPU exits. The address
    *                        must be a valid kernel-user-memory address (see
    *                        L4::Task::add_ku_mem()).
    * \utcb_def{utcb}
    *
    * \return Syscall return tag.
    *
    * The extended vCPU feature allows the use of hardware-virtualization
    * features such as Intel's VT-x (VMX) or AMD's AMD-V (SVM).
    *
    * This function enables the extended vCPU feature of `this` thread.
    * Enabling the extended vCPU feature also enables the vCPU feature.
    *
    * The kernel-user memory area starting at `ext_vcpu_state` must be at least
    * 4 KiB aligned and must cover a size of L4_PAGESIZE. It includes the data
    * of l4_vcpu_state_t at offset 0, the extended vCPU state at offset
    * L4_VCPU_OFFSET_EXT_STATE, and, on some platforms, the extended vCPU
    * information at offset L4_VCPU_OFFSET_EXT_INFOS.
    *
    * On Intel's VT-x (VMX), the extended vCPU state is #l4_vm_vmx_vcpu_vmcs_t
    * and the extended vCPU information is #l4_vm_vmx_vcpu_infos_t.
    * Furthermore, the extended vCPU state needs to be associated with a vCPU
    * context (see #l4_vm_vmx_set_hw_vmcs()).
    *
    * On AMD's AMD-V (SVM), the extended vCPU state is #l4_vm_svm_vmcb_t.
    *
    * \note Enabling the extended vCPU feature for a thread running on a
    *       different CPU core is currently not supported.
    * \note Disabling of the extended vCPU feature is currently not supported.
    * \note Upgrading from non-extended vCPU feature to extended vCPU feature
    *       is currently not supported.
    */
   l4_msgtag_t vcpu_control_ext(l4_addr_t ext_vcpu_state,
                                l4_utcb_t *utcb = l4_utcb()) noexcept
   { return l4_thread_vcpu_control_ext_u(cap(), ext_vcpu_state, utcb); }

  /**
   * Register an IRQ that will trigger upon deletion events.
   *
   * \param irq  Capability selector for the IRQ object to be triggered.
   * \utcb_def{u}
   *
   * \return System call return tag containing the return code.
   *
   * \retval -L4_BUSY   A deletion IRQ is already bound to this thread.
   * \retval -L4_EPERM  Insufficient permissions; see precondition.
   *
   * \pre The capability `irq` must have the permission #L4_CAP_FPAGE_W.
   *
   * In case the `irq` is already bound to an interrupt source, it is unbound
   * first. When `irq` is deleted, it will be deregistered first. A registered
   * deletion Irq can only be deregistered by deleting the Irq or the thread.
   *
   * List of deletion events:
   *   * deletion of one or several IPC gates bound to this thread.
   *
   * When the deletion event is delivered, there is no indication about which
   * IPC gate was deleted.
   *
   * \see l4_thread_register_del_irq
   */
  l4_msgtag_t register_del_irq(Cap<Irq> irq, l4_utcb_t *u = l4_utcb()) noexcept
  { return l4_thread_register_del_irq_u(cap(), irq.cap(), u); }

  /**
   * Class wrapping a list of rules which modify the sender label of IPC
   * messages inbound to this thread.
   *
   * Use the add() function to add modification rules, and use
   * modify_senders() to commit. Do not use the UTCB in between as it is
   * used by add() and modify_senders().
   *
   * This mechanism shall be used to change the source object labels of every
   * pending IPC of an IPC gate or an IRQ if the labels in such pending IPC
   * become invalid for the receiving thread, potentially because:
   *  - an IPC gate / IRQ was unbound from a thread, or
   *  - an IPC gate / IRQ was removed, or
   *  - the label of an IPC gate / IRQ bound to a thread was changed.
   *
   * It is not required to perform the modify_sender mechanism after an IPC
   * gate or an IRQ was bound to a thread for the first time.
   */
  class Modify_senders
  {
  private:
    friend class Thread;
    l4_utcb_t *utcb;
    unsigned cnt;

  public:
    explicit Modify_senders(l4_utcb_t *u = l4_utcb()) noexcept
    : utcb(u), cnt(1)
    {
      l4_utcb_mr_u(utcb)->mr[0] = L4_THREAD_MODIFY_SENDER_OP;
    }

    /**
     * Add a rule.
     *
     * \param match_mask Bitmask of bits to match the label.
     * \param match      Bitmask that must be equal to the label after applying
     *                   match_mask.
     * \param del_bits   Bits to be deleted from the label.
     * \param add_bits   Bits to be added to the label.
     *
     * \return 0 on success, <0 on error
     *
     * In pseudo code:
     *   if ((sender_label & match_mask) == match)
     *     { sender_label = (sender_label & ~del_bits) | add_bits; }
     *
     * Only the first match is applied.
     *
     * \see l4_thread_modify_sender_add()
     */
    int add(l4_umword_t match_mask, l4_umword_t match,
            l4_umword_t del_bits, l4_umword_t add_bits) noexcept
    {
      l4_msg_regs_t *m = l4_utcb_mr_u(utcb);
      if (cnt >= L4_UTCB_GENERIC_DATA_SIZE - 4)
        return -L4_ENOMEM;
      m->mr[cnt++] = match_mask;
      m->mr[cnt++] = match;
      m->mr[cnt++] = del_bits;
      m->mr[cnt++] = add_bits;
      return 0;
    }
  };

  /**
   * Apply sender modification rules.
   *
   * \param todo  Prepared sender modification rules.
   *
   * \return System call return tag.
   *
   * The modification rules are applied to all IPCs to the thread (whether
   * directly or by IPC gate) that are already in flight, that is that the
   * sender is already blocking on.
   *
   * See Modify_senders for a detailed description when applying sender
   * modification rules is required.
   *
   * \note Modifying the senders of a thread running on a different CPU core is
   *       not supported.
   *
   * \note To ensure that no in-flight senders are missed, either the
   *       thread itself must execute modify_senders, or the thread executing
   *       the modify_senders must synchronize with the target thread. This
   *       synchronization must ensure the following:
   *       1. Before modify_senders is executed the target thread must execute
   *          at least shortly (so that pending DRQs are handled).
   *       2. The target thread must pause its IPC dispatch, until
   *          modify_senders is completed. In other words, the target thread must
   *          not be receive ready, because otherwise an IPC message with an
   *          unmodified label can be transferred to its UTCB or vCPU state.
   *
   * \see l4_thread_modify_sender_commit()
   */
  l4_msgtag_t modify_senders(Modify_senders const &todo) noexcept
  {
    return l4_ipc_call(cap(), todo.utcb, l4_msgtag(L4_PROTO_THREAD, todo.cnt, 0, 0), L4_IPC_NEVER);
  }

  /**
   * Register an IRQ that will trigger when a forwarded virtual interrupt is
   * pending.
   *
   * \param irq  Capability selector for the IRQ object to be triggered.
   * \utcb_def{u}
   *
   * \return System call return tag containing the return code.
   *
   * \retval -L4_BUSY   A doorbell IRQ is already bound to this thread.
   * \retval -L4_EPERM  Insufficient permissions; see precondition.
   *
   * \pre The capability `irq` must have the permission #L4_CAP_FPAGE_W.
   *
   * See Irq::bind_vcpu() for more details about how interrupts can be
   * forwarded directly by the kernel to extended vCPU user mode.
   *
   * In case the `irq` is already bound to an interrupt source, it is unbound
   * first. When `irq` is deleted, it will be deregistered first. A registered
   * doorbell Irq can only be deregistered by deleting the Irq or the thread.
   *
   * \see l4_thread_register_doorbell_irq
   */
  l4_msgtag_t register_doorbell_irq(Cap<Irq> irq, l4_utcb_t *u = l4_utcb()) noexcept
  { return l4_thread_register_doorbell_irq_u(cap(), irq.cap(), u); }
};
}
