blob: 651da7e02e717ae43df481c1f5ae0c52885e498a [file] [log] [blame]
/*
* Copyright (c) 2017, Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <xtensa-asm2-s.h>
/*
* spill_reg_windows
*
* Globally visible symbol to do register spills. Useful for unit
* testing, or maybe as part of a debug/watchdog/error handler. Not a
* C function, call this via CALL0 (so you probably have to save off
* A0, but no other registers need to be spilled). On return, all
* registers not part of the current function will be spilled to
* memory.
*/
.global spill_reg_windows
.align 4
spill_reg_windows:
SPILL_ALL_WINDOWS
ret
/* Takes two arguments, a function pointer in A2 and a count in A3.
* Decrements the count, and if non-zero calls itself
* recursively. Otherwise calls the function.
*/
.align 4
_one_quad:
entry a1, 16
addi a3, a3, -1
beqz a3, _call_fn
mov a6, a2
mov a7, a3
call4 _one_quad
retw
_call_fn:
callx4 a2
retw
/* Takes a function pointer as its single argument (in A2 as per ABI)
* and invokes it having "filled" the register window with CALL4
* frames.
*/
.global fill_window
.align 4
fill_window:
entry a1, 16
mov a6, a2
movi a7, 16
call4 _one_quad
retw
/* The operation of the specific tests is to put some known values
* into a particular subset of high registers. Doing this will cause
* the window exception to spill wrapped-around frames to make space,
* which should be detected by the save code and cause it to write
* only the specific registers needed.
*/
.global test_highreg_0
.align 4
test_highreg_0:
entry a1, 16
j _test_highreg_end
.global test_highreg_4
.align 4
test_highreg_4:
entry a1, 16
movi a4, 4
movi a5, 5
movi a6, 6
movi a7, 7
j _test_highreg_end
.global test_highreg_8
.align 4
test_highreg_8:
entry a1, 16
movi a4, 4
movi a5, 5
movi a6, 6
movi a7, 7
movi a8, 8
movi a9, 9
movi a10, 10
movi a11, 11
j _test_highreg_end
.global test_highreg_12
.align 4
test_highreg_12:
entry a1, 16
movi a4, 4
movi a5, 5
movi a6, 6
movi a7, 7
movi a8, 8
movi a9, 9
movi a10, 10
movi a11, 11
movi a12, 12
movi a13, 13
movi a14, 14
movi a15, 15
j _test_highreg_end
/* Loads a pointer into A1 to serve as a "save stack" that can be
* inspected by the caller, does the save, then restores and returns,
* placing the output stack pointer "test_highreg_handle" for
* inspection.
*/
.align 4
_test_highreg_end:
movi a2, _test_highreg_a0_save
s32i a0, a2, 0
movi a2, _test_highreg_sp_save
s32i a1, a2, 0
/* Do it once just to make sure the restore code works */
call0 xtensa_save_high_regs
movi a2, 22
movi a3, 33
call0 xtensa_restore_high_regs
movi a2, test_highreg_sp_top
l32i a1, a2, 0
call0 xtensa_save_high_regs
movi a2, test_highreg_handle
s32i a1, a2, 0
movi a2, _test_highreg_sp_save
l32i a1, a2, 0
movi a2, _test_highreg_a0_save
l32i a0, a2, 0
retw
.global testfw
.align 4
testfw:
entry a1, 16
movi a2, testfw_wb
rsr.WINDOWBASE a3
s32i a3, a2, 0
movi a2, testfw_ws
rsr.WINDOWSTART a3
s32i a3, a2, 0
retw
/* Does a "jump" to a symbol named "rfi_jump_c" using RFI. */
.global rfi_jump
.align 4
rfi_jump:
#if 1
movi a2, rfi_jump_c
wsr.EPC6 a2
rsr.PS a2
wsr.EPS6 a2
rsync
rfi 6
#else
movi a2, rfi_jump_c
jx a2
#endif
.global do_xstack_call
.align 4
do_xstack_call:
entry a1, 16
mov a3, a2 /* a3 == "new sp" (this function's 1st argument) */
movi a2, xstack_top /* a2 == cross-stack callee/handler */
/* Fake a save frame, CROSS_STACK_CALL just wants the old SP
* from it, we don't need to fill it. Only one available
* register, so it uses the bottom slot of the "fake BSA" as
* scratch.
*/
addi a1, a1, -BASE_SAVE_AREA_SIZE
s32i a1, a1, 0
addi a1, a1, -4
l32i a1, a1, 4
s32i a1, a1, 0
CROSS_STACK_CALL
/* Restore the stack */
l32i a1, a1, 0
addi a1, a1, BASE_SAVE_AREA_SIZE
retw
/* Define our exception handler. Offsets written to assume:
* struct { int nest; void *stack_top; }
*/
.align 4
_handle_excint:
EXCINT_HANDLER MISC0, 0, 4
/* And a single vector at level 5 to point to it and call our C
* handler. There is a timer on most cores (qemu and LX6/ESP-32 at
* least) that can be used for unit testing.
*/
DEF_EXCINT 5, _handle_excint, handle_int5_c