You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

119 lines
4.3 KiB
C

/*! \file
* \brief Functionality required for \ref context_switch "context switching"
*/
/*! \defgroup context Context Switch
* \brief Low-Level functionality required for context switching
*/
#pragma once
#include "../types.h"
/*! \brief Structure for saving the CPU context when switching coroutines.
* \ingroup context
*/
struct Context {
uintptr_t rbx; ///< RBX of the thread
uintptr_t rbp; ///< RBP of the thread
uintptr_t r12; ///< R12 of the thread
uintptr_t r13; ///< R13 of the thread
uintptr_t r14; ///< R14 of the thread
uintptr_t r15; ///< R15 of the thread
void* rsp; ///< Current stack pointer of the thread
} __attribute__((packed));
/*! \brief Prepares a context for its first activation.
*
* \ingroup context
*
* On first activation (during *some* context switch), the execution of a
* thread should start at its entry point (typically an implementation of \ref
* Thread::kickoff).
*
* For this, we have to prepare the thread context such that \ref
* context_switch and \ref context_launch can work with it.
*
* Just pushing the entry point onto the stack as a return address is not
* sufficient, however.
* \ref Thread::kickoff requires a pointer to the current thread as a
* parameter, which we also have to transfer. According to the 64 bit systemv
* calling convention, parameters are passed via the volatile registers `rdi,
* rsi, rcx, rdx, r8, r9`. But theses are never set during the intial context
* switch (why?). Therefore we pass the parameter using the non-volatile
* register `r15` and use a trampoline function as the actual entry point. See
* \ref fake_systemv_abi for details.
*
* `prepareContext()` can be implemented in the high-level programming language
* C++ (in file `context.cc`).
*
* \param tos Pointer to the top of stack (= address of first byte beyond
* the memory reserved for the stack)
* \param context Reference to the Context structure to be filled
* \param kickoff Pointer to the \ref Thread::kickoff function
* \param param1 first parameter for \ref Thread::kickoff function
*/
/*!
* \todo(14) Implement Function (and helper functions, if required)
*/
void prepareContext(void* tos, Context& context, void (*kickoff)(void*),
void* param1 = nullptr);
/*! \brief Executes the context switch.
*
* \ingroup context
*
* For a clean context switch, the current register values must be stored in
* the given context struct. Subsequently, these values must be restored
* accordingly from the `next` context struct.
*
* This function must be implemented in assembler in the file `context.asm`
* (why?). It must be declared as `extern "C"`, so assembler functions are not
* C++ name mangled.
*
* \param next Pointer to the structure that the next context will be read
* from
* \param current Pointer to the structure that the current context will be
* stored in
*
* \todo(14) Implement Method
*/
extern "C" void context_switch(Context* next, Context* current);
/*! \brief Launch context switching.
*
* To start context switching, the current context (from the boot-routines) is
* thrown away and the prepared register values within the given `next` context
* replace it.
*
* This function must be implemented in assembler in the file `context.asm`
* (why?). It must be declared as `extern "C"`, so assembler functions are not
* C++ name mangled.
*
* \ingroup context
*
* \param next Pointer to the structure that the next context will be read
* from
*
* \todo(14) Implement Method
*/
extern "C" void context_launch(Context* next);
/*! \brief Fakes a systemv abi call.
*
* When a thread is first started, only non-volatile registers are "restored"
* from our prepared context (which is where we stored our \ref Thread::kickoff
* parameters). However, the 64 bit calling convention (systemv) dictates that
* parameters are passed via the volatile registers `rdi, rsi, rcx, rdx, r8,
* r9`. In order to call a C++ function, we have to transfer our parameters from
* the non-volatile registers (e.g. `r15, ...`) to the correct volatile ones
* (`rdi, ...`).
*
* This function must be implemented in assembler in the file `context.asm`
* (why?). It must be declared as `extern "C"`, so assembler functions are not
* C++ name mangled.
* \ingroup context
*
* \todo(14) Implement Method
*/
extern "C" void fake_systemv_abi();