70 lines
2.3 KiB
C
70 lines
2.3 KiB
C
|
/*
|
||
|
* this is the internal transfer function.
|
||
|
*
|
||
|
* HISTORY
|
||
|
* 07-Sep-16 Add clang support using x register naming. Fredrik Fornwall
|
||
|
* 13-Apr-13 Add support for strange GCC caller-save decisions
|
||
|
* 08-Apr-13 File creation. Michael Matz
|
||
|
*
|
||
|
* NOTES
|
||
|
*
|
||
|
* Simply save all callee saved registers
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#define STACK_REFPLUS 1
|
||
|
|
||
|
#ifdef SLP_EVAL
|
||
|
#define STACK_MAGIC 0
|
||
|
#define REGS_TO_SAVE "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", \
|
||
|
"x27", "x28", "x30" /* aka lr */, \
|
||
|
"v8", "v9", "v10", "v11", \
|
||
|
"v12", "v13", "v14", "v15"
|
||
|
|
||
|
static int
|
||
|
slp_switch(void)
|
||
|
{
|
||
|
int err;
|
||
|
void *fp;
|
||
|
register long *stackref, stsizediff;
|
||
|
__asm__ volatile ("" : : : REGS_TO_SAVE);
|
||
|
__asm__ volatile ("str x29, %0" : "=m"(fp) : : );
|
||
|
__asm__ ("mov %0, sp" : "=r" (stackref));
|
||
|
{
|
||
|
SLP_SAVE_STATE(stackref, stsizediff);
|
||
|
__asm__ volatile (
|
||
|
"add sp,sp,%0\n"
|
||
|
"add x29,x29,%0\n"
|
||
|
:
|
||
|
: "r" (stsizediff)
|
||
|
);
|
||
|
SLP_RESTORE_STATE();
|
||
|
/* SLP_SAVE_STATE macro contains some return statements
|
||
|
(of -1 and 1). It falls through only when
|
||
|
the return value of slp_save_state() is zero, which
|
||
|
is placed in x0.
|
||
|
In that case we (slp_switch) also want to return zero
|
||
|
(also in x0 of course).
|
||
|
Now, some GCC versions (seen with 4.8) think it's a
|
||
|
good idea to save/restore x0 around the call to
|
||
|
slp_restore_state(), instead of simply zeroing it
|
||
|
at the return below. But slp_restore_state
|
||
|
writes random values to the stack slot used for this
|
||
|
save/restore (from when it once was saved above in
|
||
|
SLP_SAVE_STATE, when it was still uninitialized), so
|
||
|
"restoring" that precious zero actually makes us
|
||
|
return random values. There are some ways to make
|
||
|
GCC not use that zero value in the normal return path
|
||
|
(e.g. making err volatile, but that costs a little
|
||
|
stack space), and the simplest is to call a function
|
||
|
that returns an unknown value (which happens to be zero),
|
||
|
so the saved/restored value is unused. */
|
||
|
__asm__ volatile ("mov %0, #0" : "=r" (err));
|
||
|
}
|
||
|
__asm__ volatile ("ldr x29, %0" : : "m" (fp) :);
|
||
|
__asm__ volatile ("" : : : REGS_TO_SAVE);
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
#endif
|