// vi:set ft=cpp: -*- Mode: C++ -*- /** * \file * Common thread related definitions. */ /* * (c) 2008-2009 Adam Lackorzynski , * Alexander Warg * economic rights: Technische Universität Dresden (Germany) * * 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 #include #include namespace L4 { /** * C++ L4 kernel thread 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. * * \includefile{l4/sys/thread} * * For the C interface see the \ref l4_thread_api API. */ class Thread : public Kobject_t > { 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. * \param utcb UTCB to use for this operation. * * \return System call return tag * * This method allows to manipulate 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`). * * 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. * \param utcb UTCB to use for this operation. * * \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`). */ 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_commit(). * * This class is responsible for initializing various attributes of a * thread in a UTCB for the control_commit() 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_commit() * function. Usually this is the UTCB of the calling thread. */ 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 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 pager() noexcept { return Cap(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 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 exc_handler() noexcept { return Cap(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. * * 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 const &task) noexcept { l4_thread_control_bind_u(thread_utcb, task.cap(), _u); } /** * Set the thread to alien mode. */ void alien(int on) noexcept { l4_thread_control_alien_u(_u, on); } /** * Allow host system calls on Fiasco-UX. * * \pre Running on Fiasco-UX. */ void ux_host_syscall(int on) noexcept { l4_thread_control_ux_host_syscall_u(_u, on); } }; /** * Commit the given thread-attributes object. * * \param attr the attribute object to commit to the thread. */ l4_msgtag_t control(Attr const &attr) noexcept { return l4_thread_control_commit_u(cap(), attr._u); } /** * Switch execution to this thread. * * \param utcb the UTCB of the current thread. * * \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. * \param utcb UTCB of the current thread. * * \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. * * 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. * * 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 or disable the vCPU feature for the thread. * * \param vcpu_state A virtual address pointing to a l4_vcpu_state_t or 0. * If not 0, it must be a valid kernel-user-memory address * (see L4::Task::add_ku_mem()). * \param utcb UTCB to use for this operation. * * \return Syscall return tag. * * This function enables the vCPU feature of `this` thread if `vcpu_state` * is set to a valid kernel-user-memory address, or disables the vCPU * feature if `vcpu_state` is 0. (Disable: optional, currently unsupported.) * * The asynchronous IPC handling is described at \ref l4_vcpu_api. */ 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 or disable 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()). * \param utcb UTCB to use for this operation. * * \return Syscall return tag. * * The extended vCPU feature allows the use of hardware-virtualization * features such as Intel's VT or AMD's SVM. * * This function enables the extended vCPU feature of `this` thread * if `ext_vcpu_state` is set to a valid kernel-user-memory address, or * disables the vCPU feature if `ext_vcpu_state` is 0. * * \note The extended vCPU mode includes the normal vCPU mode. */ 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{u} * * \return System call return tag containing the return code. * * List of deletion events: * * deletion of an IPC gate bound to this thread. * * \see l4_thread_register_del_irq */ l4_msgtag_t register_del_irq(Cap 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(). */ 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 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); } }; }