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
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();
|