/*! \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();