/* * \brief Kernels syscall frontend * \author Martin stein * \date 2011-11-30 */ /* * Copyright (C) 2011-2013 Genode Labs GmbH * * This file is part of the Genode OS framework, which is distributed * under the terms of the GNU General Public License version 2. */ #ifndef _INCLUDE__KERNEL__SYSCALLS_H_ #define _INCLUDE__KERNEL__SYSCALLS_H_ /* Genode includes */ #include namespace Genode { class Platform_thread; class Platform_pd; class Tlb; } namespace Kernel { typedef Genode::Tlb Tlb; typedef Genode::addr_t addr_t; typedef Genode::size_t size_t; typedef Genode::Platform_thread Platform_thread; typedef Genode::Platform_pd Platform_pd; /** * Unique opcodes of all syscalls supported by the kernel */ enum Syscall_type { INVALID_SYSCALL = 0, /* execution control */ NEW_THREAD = 1, DELETE_THREAD = 26, START_THREAD = 2, PAUSE_THREAD = 3, RESUME_THREAD = 4, RESUME_FAULTER = 28, GET_THREAD = 5, CURRENT_THREAD_ID = 6, YIELD_THREAD = 7, READ_THREAD_STATE = 18, WRITE_THREAD_STATE = 19, /* interprocess communication */ REQUEST_AND_WAIT = 8, REPLY = 9, WAIT_FOR_REQUEST = 10, /* management of resource protection-domains */ SET_PAGER = 11, UPDATE_PD = 12, UPDATE_REGION = 32, NEW_PD = 13, /* interrupt handling */ ALLOCATE_IRQ = 14, AWAIT_IRQ = 15, FREE_IRQ = 16, /* debugging */ PRINT_CHAR = 17, /* asynchronous signalling */ NEW_SIGNAL_RECEIVER = 20, NEW_SIGNAL_CONTEXT = 21, KILL_SIGNAL_CONTEXT = 30, AWAIT_SIGNAL = 22, SUBMIT_SIGNAL = 23, SIGNAL_PENDING = 27, ACK_SIGNAL = 29, /* vm specific */ NEW_VM = 24, RUN_VM = 25, PAUSE_VM = 31, }; /***************************************************************** ** Syscall with 1 to 6 arguments ** ** ** ** These functions must not be inline to ensure that objects, ** ** wich are referenced by arguments, are tagged as "used" even ** ** though only the pointer gets handled in here. ** *****************************************************************/ Syscall_ret syscall(Syscall_arg arg_0); Syscall_ret syscall(Syscall_arg arg_0, Syscall_arg arg_1); Syscall_ret syscall(Syscall_arg arg_0, Syscall_arg arg_1, Syscall_arg arg_2); Syscall_ret syscall(Syscall_arg arg_0, Syscall_arg arg_1, Syscall_arg arg_2, Syscall_arg arg_3); Syscall_ret syscall(Syscall_arg arg_0, Syscall_arg arg_1, Syscall_arg arg_2, Syscall_arg arg_3, Syscall_arg arg_4); Syscall_ret syscall(Syscall_arg arg_0, Syscall_arg arg_1, Syscall_arg arg_2, Syscall_arg arg_3, Syscall_arg arg_4, Syscall_arg arg_5); /** * Virtual range of the mode transition region in every PD */ addr_t mode_transition_virt_base(); size_t mode_transition_size(); /** * Get sizes of the kernel objects */ size_t thread_size(); size_t pd_size(); size_t signal_context_size(); size_t signal_receiver_size(); size_t vm_size(); /** * Get alignment constraints of the kernel objects */ unsigned kernel_pd_alignm_log2(); /** * Create a new PD * * \param dst physical base of an appropriate portion of memory * that is thereupon allocated to the kernel * \param pd core local Platform_pd object * * \retval >0 ID of the new PD * \retval 0 if no new PD was created * * Restricted to core threads. Regaining of the supplied memory is not * supported by now. */ inline int new_pd(void * const dst, Platform_pd * const pd) { return syscall(NEW_PD, (Syscall_arg)dst, (Syscall_arg)pd); } /** * Propagate changes in PD configuration * * \param pd_id ID of the PD that has been configured * * It might be, that the kernel and/or the hardware caches parts of PD * configurations such as virtual address translations. This syscall * ensures that the current configuration of the targeted PD gets fully * applied from the moment it returns to the userland. This syscall is * inappropriate in case that a PD wants to change its own configuration. * There's no need for this syscall after a configuration change that * can't affect the kernel- and/or hardware-caches. * * Restricted to core threads. */ inline void update_pd(unsigned const pd_id) { syscall(UPDATE_PD, (Syscall_arg)pd_id); } /** * Propagate memory-updates within a given virtual region * * \param base virtual base of the region * \param size size of the region * * If one updates a memory region and must ensure that the update * gets visible directly to other address spaces, this syscall does * the job. * * Restricted to core threads. */ inline void update_region(addr_t base, size_t size) { syscall(UPDATE_REGION, (Syscall_arg)base, (Syscall_arg)size); } /** * Create a new thread that is stopped initially * * \param dst physical base of an appropriate portion of memory * that is thereupon allocated to the kernel * \param pt assigned platform thread * * \retval >0 ID of the new thread * \retval 0 if no new thread was created * * Restricted to core threads. Regaining of the supplied memory can be done * through 'delete_thread'. */ inline int new_thread(void * const dst, Platform_thread * const pt) { return syscall(NEW_THREAD, (Syscall_arg)dst, (Syscall_arg)pt); } /** * Delete an existing thread * * \param id kernel name of the targeted thread * * Restricted to core threads. After calling this, the memory that was * granted beforehand by 'new_thread' to kernel for managing this thread * is freed again. */ inline void delete_thread(unsigned thread_id) { syscall(DELETE_THREAD, (Syscall_arg)thread_id); } /** * Start thread with a given context and let it participate in CPU scheduling * * \param id ID of targeted thread * \param ip initial instruction pointer * \param sp initial stack pointer * * \retval >0 success, return value is the TLB of the thread * \retval 0 the targeted thread wasn't started or was already started * when this gets called (in both cases it remains untouched) * * Restricted to core threads. */ inline Tlb * start_thread(Platform_thread * const phys_pt, void * ip, void * sp, unsigned cpu_no) { return (Tlb *)syscall(START_THREAD, (Syscall_arg)phys_pt, (Syscall_arg)ip, (Syscall_arg)sp, (Syscall_arg)cpu_no); } /** * Prevent thread from participating in CPU scheduling * * \param id ID of the targeted thread. If not set * this will target the current thread. * * \retval 0 syscall was successful * \retval <0 if the targeted thread does not exist or still participates * in CPU scheduling after * * If the caller doesn't target itself, this is restricted to core threads. */ inline int pause_thread(unsigned const id = 0) { return syscall(PAUSE_THREAD, id); } /** * Let an already started thread participate in CPU scheduling * * \param id ID of the targeted thread * * \retval 0 if syscall was successful and thread were paused beforehand * \retval >0 if syscall was successful and thread were already active * \retval <0 if targeted thread doesn't participate in CPU * scheduling after * * If the targeted thread blocks for any event except a 'start_thread' * call this call cancels the blocking. */ inline int resume_thread(unsigned const id = 0) { return syscall(RESUME_THREAD, id); } /** * Continue thread after a pagefault that could be resolved * * \param id ID of the targeted thread */ inline void resume_faulter(unsigned const id = 0) { syscall(RESUME_FAULTER, id); } /** * Let the current thread give up its remaining timeslice * * \param id if this thread ID is set and valid this will resume the * targeted thread additionally */ inline void yield_thread(unsigned const id = 0) { syscall(YIELD_THREAD, id); } /** * Get the thread ID of the current thread */ inline int current_thread_id() { return syscall(CURRENT_THREAD_ID); } /** * Get platform thread by ID or 0 if target is "core main" or "idle" * * \param id ID of the targeted thread or 0 if caller targets itself * * Restricted to core threads. */ inline Platform_thread * get_thread(unsigned const id = 0) { return (Platform_thread *)syscall(GET_THREAD, id); } /** * Send IPC request and wait for reply * * \param id ID of the receiver thread * \param size request size (beginning with the callers UTCB base) * * \return size of received reply (beginning with the callers UTCB base) * * If the receiver exists, this blocks execution until a dedicated reply * message has been send by the receiver. The receiver may never do so. */ inline size_t request_and_wait(unsigned const id, size_t const size) { return (size_t)syscall(REQUEST_AND_WAIT, id, size); } /** * Wait for next IPC request, discard current request * * \return size of received request (beginning with the callers UTCB base) */ inline size_t wait_for_request() { return (size_t)syscall(WAIT_FOR_REQUEST); } /** * Reply to last IPC request * * \param size reply size (beginning with the callers UTCB base) * \param await_request if the call shall await and fetch next request * * \return request size (beginning with the callers UTCB base) * if await_request was set */ inline size_t reply(size_t const size, bool const await_request) { return (size_t)syscall(REPLY, size, await_request); } /** * Set a thread that gets informed about pagefaults of another thread * * \param pager_id ID of the thread that shall get informed. * Subsequently this thread gets an IPC message, * wich contains an according 'Pagefault' object for * every pagefault the faulter throws. * \param faulter_id ID of the thread that throws the pagefaults * wich shall be notified. After every pagefault this * thread remains paused to be reactivated by * 'resume_thread'. * * Restricted to core threads. */ inline void set_pager(unsigned const pager_id, unsigned const faulter_id) { syscall(SET_PAGER, pager_id, faulter_id); } /** * Print a char 'c' to the kernels serial ouput */ inline void print_char(char const c) { syscall(PRINT_CHAR, (Syscall_arg)c); } /** * Allocate an IRQ to the caller if the IRQ is not allocated already * * \param id ID of the targeted IRQ * * \return wether the IRQ has been allocated to this thread or not * * Restricted to core threads. */ inline bool allocate_irq(unsigned const id) { return syscall(ALLOCATE_IRQ, (Syscall_arg)id); } /** * Free an IRQ from allocation if it is allocated by the caller * * \param id ID of the targeted IRQ * * \return wether the IRQ has been freed or not * * Restricted to core threads. */ inline bool free_irq(unsigned const id) { return syscall(FREE_IRQ, (Syscall_arg)id); } /** * Block caller for the occurence of its IRQ * * Restricted to core threads. Blocks the caller forever * if he has not allocated any IRQ. */ inline void await_irq() { syscall(AWAIT_IRQ); } /** * Copy the current state of a thread to the callers UTCB * * \param thread_id ID of the targeted thread * * Restricted to core threads. One can also read from its own context, * or any thread that is active in the meantime. In these cases * be aware of the fact, that the result reflects the thread * state that were backed at the last kernel entry of the thread. * The copy might be incoherent when this function returns because * the caller might get scheduled away before then. */ inline void read_thread_state(unsigned const thread_id) { syscall(READ_THREAD_STATE, (Syscall_arg)thread_id); } /** * Override the state of a thread with the callers UTCB content * * \param thread_id ID of the targeted thread * * Restricted to core threads. One can also write to its own context, or * to that of a thread that is active in the meantime. */ inline void write_thread_state(unsigned const thread_id) { syscall(WRITE_THREAD_STATE, (Syscall_arg)thread_id); } /** * Create a kernel object that acts as receiver for asynchronous signals * * \param dst physical base of an appropriate portion of memory * that is thereupon allocated to the kernel * * \return ID of the new kernel object * * Restricted to core threads. Regaining of the supplied memory is not * supported by now. */ inline unsigned new_signal_receiver(void * dst) { return syscall(NEW_SIGNAL_RECEIVER, (Syscall_arg)dst); } /** * Create a kernel object that acts as a distinct signal type at a receiver * * \param dst physical base of an appropriate portion of memory * that is thereupon allocated to the kernel * \param receiver_id ID of the receiver kernel-object that shall * provide the new signal context * \param imprint Every signal, one receives at the new context, * will hold this imprint. This enables the receiver * to interrelate signals with the context. * * \return ID of the new kernel object * * Core-only syscall. Regaining of the supplied memory is not * supported by now. */ inline unsigned new_signal_context(void * dst, unsigned receiver_id, unsigned imprint) { return syscall(NEW_SIGNAL_CONTEXT, (Syscall_arg)dst, (Syscall_arg)receiver_id, (Syscall_arg)imprint); } /** * Wait for occurence of at least one signal at any context of a receiver * * \param receiver_id ID of the targeted receiver kernel-object * * When this call returns, an instance of 'Signal::Data' is located at the * base of the callers UTCB. It's granted that every occurence of a signal * is provided through this function, exactly till it gets delivered through * this function. If multiple threads listen at the same receiver, and/or * multiple contexts of the receiver trigger simultanously, there is no * assertion about wich thread receives, and from wich context. But * deliveries belonging to the same context are serialized through * 'ack_signal', to enable synchronization in 'kill_signal'. */ inline void await_signal(unsigned receiver_id) { syscall(AWAIT_SIGNAL, (Syscall_arg)receiver_id); } /** * Get summarized state of all contexts of a signal receiver * * \param receiver_id ID of the targeted receiver kernel-object */ inline bool signal_pending(unsigned receiver_id) { return syscall(SIGNAL_PENDING, (Syscall_arg)receiver_id); } /** * Trigger a specific signal context * * \param context_id ID of the targeted context kernel-object * \param num how often the context shall be triggered by this call */ inline void submit_signal(unsigned context_id, int num) { syscall(SUBMIT_SIGNAL, (Syscall_arg)context_id, (Syscall_arg)num); } /** * Acknowledge the processing of the last signal of a signal context * * \param context_id kernel name of the targeted signal context * * Should be called after all signal objects, that reference the targeted * signal context in userland are destructed. The signal context wont * deliver a new signal until the old signal is acknowledged. */ inline void ack_signal(unsigned context_id) { syscall(ACK_SIGNAL, (Syscall_arg)context_id); } /** * Destruct a signal context * * \param context_id kernel name of the targeted signal context * * \return wether the context could be destructed * * Blocks the caller until the last delivered signal of the targeted * context is acknowledged. Then the context gets destructed, losing * all submits that were not delivered when this syscall occured. */ inline bool kill_signal_context(unsigned context_id) { return syscall(KILL_SIGNAL_CONTEXT, (Syscall_arg)context_id); } /** * Create a new virtual-machine that is stopped initially * * \param dst physical base of an appropriate portion of memory * that is thereupon allocated to the kernel * \param state location of the CPU state of the VM * \param context_id ID of the targeted signal context * * \retval >0 ID of the new VM * \retval 0 if no new VM was created * * Restricted to core threads. Regaining of the supplied memory is not * supported by now. */ inline int new_vm(void * const dst, void * const state, unsigned context_id) { return syscall(NEW_VM, (Syscall_arg)dst, (Syscall_arg)state, (Syscall_arg)context_id); } /** * Execute a virtual-machine (again) * * \param id ID of the targeted VM * * Restricted to core threads. */ inline void run_vm(unsigned const id) { syscall(RUN_VM, (Syscall_arg)id); } /** * Stop execution of a virtual-machine * * \param id ID of the targeted VM * * Restricted to core threads. */ inline void pause_vm(unsigned const id) { syscall(PAUSE_VM, (Syscall_arg)id); } } #endif /* _INCLUDE__KERNEL__SYSCALLS_H_ */