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