#pragma once
#ifndef STACKMAN_SWITCH_H
#define STACKMAN_SWITCH_H
/* Must include the platform here to know if the switch function
* is implemented in assembler or nt
*/
#include "platforms/platform.h"
/* we can request that C functions be implemented with static linkage
* when including in other libraries
*/
#if defined(STACKMAN_LINKAGE_STATIC)
#if !defined(STACKMAN_ASSEMBLY_SRC) && !STACKMAN_SWITCH_C
#define STACKMAN_LINKAGE_SWITCH static
#endif
#endif
#if !defined(STACKMAN_LINKAGE_SWITCH)
/* then it is extern */
#if defined(__cplusplus)
#define STACKMAN_LINKAGE_SWITCH extern "C"
#else
#define STACKMAN_LINKAGE_SWITCH extern
#endif
#endif
/* do we need to have indirection for stackman_switch?
* we do, if the implementation is inline and we are not
* building a library, because an optimizing compiler may
* decide to inline functions that contain in-line assembler
*/
#if (!defined(STACKMAN_ASSEMBLY_SRC) && !defined(STACKMAN_BUILD_LIB))
#define STACKMAN_SWITCH_NEED_INDIRECT 1
#else
#define STACKMAN_SWITCH_NEED_INDIRECT 0
#endif
#if STACKMAN_SWITCH_NEED_INDIRECT
#define STACKMAN_SWITCH_INASM_NAME _stackman_switch_inasm
#define STACKMAN_LINKAGE_SWITCH_INASM static
#else
#define STACKMAN_SWITCH_INASM_NAME stackman_switch
#define STACKMAN_LINKAGE_SWITCH_INASM STACKMAN_LINKAGE_SWITCH
#endif
/* raw stack switching function. The caller supplies a callback
* and context to perform determine the new stack pointer and
* perform cleanup of old stack and initialization of the new
* stack.
* The platform specific implementation of the function is responsible
* for saving and restoring any machine registers that are assumed
* to be unchanged across function calls and to change the actual
* stack pointer.
*
* The implementation must simply:
* 1) store all platform and cpu state on the stack (callee-stored
* registers, exception state, etc)
* 2) call the callback with the context, opcode STACKMAN_OP_SAVE and
* the current stack pointer. This allows the application to do additional
* cleanup and determine the new stack pointer.
* 3) replace the stack pointer with the returned value from the callback.
* 4) call the callback again with opcode STACKMAN_OP_RESTORE and the new
* stack pointer. This allows the application to initialize its state
after the switch.
* 5) Pop the platform state back from stack.
* 6) return the result from the second call of the callback.
*
* Note that the callback and context are _not_ guaranteed
* to be stack local. That is, even if the stack pointer is modified
* according to the first call to the callback, the same callback and
* context may be used during the second callback, in the new stack.
* Therefore, the same callback and context pointers should always be used
* when switching between stacks. This
* restriction could be lifted by pushing both values onto the stack
* before switching but the added complexity of platform implementations
* doesn't seem to warrant that for such an exotic use case.
*
* an appropriate implementation is included by
* platforms/platform.h
* A template implementation is available in
* platforms/switch_template.h
*/
/* Opcodes for the callback function */
typedef enum stackman_op_t {
/* The callback receives the current stack pointer and should
* save any state that it wishes, for example the contents of the
* stack, before returning a new stack pointer.
*/
STACKMAN_OP_SAVE = 0,
/* The callback receives the new stack pointer and should restore
* any state for it, e.g. fillint the stack with the correct data.
* what it returns will be the return value from stackman_switch().
*/
STACKMAN_OP_RESTORE = 1,
/* The callback is being invoked from a stackman_call() invokation.
* Stack_pointer is the pointer that was provided to stackman_call()
*/
STACKMAN_OP_CALL = 2,
} stackman_op_t;
/* The switch callback. It receives the context passed to
* stackman_switch(), an opcode (above) and a stack pointer.
* The opcode is passed as an int, to avoid portability problems
* with the ambiguous size of an enum.
*/
typedef void *(*stackman_cb_t)(
void *context, int opcode, void *stack_pointer);
/* The actual stack switching function.
* It saves state, switches stack pointer, and restores state
*/
STACKMAN_LINKAGE_SWITCH
void *stackman_switch(stackman_cb_t callback, void *context);
/* A function to call a function with a different stack location.
* Instead of a normal function call, where the return address and arguments
* are pushed on the current stack, first the stack pointer is changed to
* a new location. This is useful for example to move the stack into heap memory.
* 'callback' will be invoked with the
* stack already set to the new position as specified by 'stack'. The third
* argument to the callback is the *previous* stack pointer.
* The callback can then proceed to 'save' the new stack for future switching.
* Returning from the callback will work normally.
*/
STACKMAN_LINKAGE_SWITCH
void *stackman_call(stackman_cb_t callback, void *context, void *stack);
#endif /* STACKMAN_SWITCH_H */