genode/base-hw/include/kernel/syscalls.h

585 lines
17 KiB
C++

/*
* \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 <base/syscall_types.h>
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_ */