freertos: save/restore PS and EPC1 around window spilling
Since in b0491307
, which has introduced the optimized window spill
procedure, _xt_context_save did not work correctly when called from
_xt_syscall_exc. This was because unlike _xt_lowint1, _xt_syscall_exc
does not save PS and EPC1. The new version of _xt_context_save
modified PS (on purpose) and EPC1 (accidentally, due to window
overflow exceptions), which resulted in a crash upon 'rfi' from the
syscall.
This commit adds restoring of PS and EPC1 in _xt_context_save. It also
slightly reduces the number of instructions used to prepare PS for
window spill.
Unit test for setjmp/longjmp (which were broken by this regression)
is added.
Closes https://github.com/espressif/esp-idf/issues/4541
This commit is contained in:
parent
b5b30736de
commit
891eb3b020
2 changed files with 59 additions and 17 deletions
|
@ -166,27 +166,34 @@ _xt_context_save:
|
|||
s32i a9, sp, XT_STK_OVLY /* save overlay state */
|
||||
#endif
|
||||
|
||||
rsr a2, PS /* We need to enable window exceptions to */
|
||||
movi a3, PS_INTLEVEL_MASK /* perform spill registers*/
|
||||
and a2, a2, a3
|
||||
bnez a2, _not_l1
|
||||
rsr a2, PS
|
||||
movi a3, PS_INTLEVEL(1) /* For some curious reason the level 1 interrupts */
|
||||
or a2, a2, a3 /* dont set the intlevel correctly on PS, we need to */
|
||||
wsr a2, PS /* do this manually */
|
||||
rsync
|
||||
_not_l1:
|
||||
rsr a2, PS /* finally umask the window exceptions */
|
||||
movi a3, ~(PS_EXCM_MASK)
|
||||
and a2, a2, a3
|
||||
wsr a2, PS
|
||||
rsync
|
||||
/* SPILL_ALL_WINDOWS macro requires window overflow exceptions to be enabled,
|
||||
* i.e. PS.EXCM cleared and PS.WOE set.
|
||||
* Since we are going to clear PS.EXCM, we also need to increase INTLEVEL
|
||||
* at least to XCHAL_EXCM_LEVEL. This matches that value of effective INTLEVEL
|
||||
* at entry (CINTLEVEL=max(PS.INTLEVEL, XCHAL_EXCM_LEVEL) when PS.EXCM is set.
|
||||
* Since WindowOverflow exceptions will trigger inside SPILL_ALL_WINDOWS,
|
||||
* need to save/restore EPC1 as well.
|
||||
*/
|
||||
rsr a2, PS /* to be restored after SPILL_ALL_WINDOWS */
|
||||
movi a4, PS_INTLEVEL_MASK
|
||||
and a3, a2, a4 /* get the current INTLEVEL */
|
||||
bgeui a3, XCHAL_EXCM_LEVEL, 1f /* calculate max(INTLEVEL, XCHAL_EXCM_LEVEL) */
|
||||
movi a3, XCHAL_EXCM_LEVEL
|
||||
1:
|
||||
movi a4, PS_UM | PS_WOE /* clear EXCM, enable window overflow, set new INTLEVEL */
|
||||
or a3, a3, a4
|
||||
wsr a3, ps
|
||||
rsr a4, EPC1 /* to be restored after SPILL_ALL_WINDOWS */
|
||||
|
||||
addi sp, sp, XT_STK_FRMSZ /* go back to spill register region */
|
||||
SPILL_ALL_WINDOWS /* place the live register windows there */
|
||||
addi sp, sp, -XT_STK_FRMSZ /* return the current stack pointer and proceed with context save*/
|
||||
|
||||
#endif
|
||||
wsr a2, PS /* restore to the value at entry */
|
||||
rsync
|
||||
wsr a4, EPC1 /* likewise */
|
||||
|
||||
#endif /* __XTENSA_CALL0_ABI__ */
|
||||
|
||||
l32i a12, sp, XT_STK_TMP0 /* restore the temp saved registers */
|
||||
l32i a13, sp, XT_STK_TMP1 /* our return address is there */
|
||||
|
|
35
components/newlib/test/test_setjmp.c
Normal file
35
components/newlib/test/test_setjmp.c
Normal file
|
@ -0,0 +1,35 @@
|
|||
#include <setjmp.h>
|
||||
#include <stdio.h>
|
||||
#include "unity.h"
|
||||
#include "esp_system.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
jmp_buf jmp_env;
|
||||
uint32_t retval;
|
||||
volatile bool inner_called;
|
||||
} setjmp_test_ctx_t;
|
||||
|
||||
static __attribute__((noreturn)) void inner(setjmp_test_ctx_t *ctx)
|
||||
{
|
||||
printf("inner, retval=0x%x\n", ctx->retval);
|
||||
ctx->inner_called = true;
|
||||
longjmp(ctx->jmp_env, ctx->retval);
|
||||
TEST_FAIL_MESSAGE("Should not reach here");
|
||||
}
|
||||
|
||||
TEST_CASE("setjmp and longjmp", "[newlib]")
|
||||
{
|
||||
const uint32_t expected = 0x12345678;
|
||||
setjmp_test_ctx_t ctx = {
|
||||
.retval = expected
|
||||
};
|
||||
uint32_t ret = setjmp(ctx.jmp_env);
|
||||
if (!ctx.inner_called) {
|
||||
TEST_ASSERT_EQUAL(0, ret);
|
||||
inner(&ctx);
|
||||
} else {
|
||||
TEST_ASSERT_EQUAL(expected, ret);
|
||||
}
|
||||
TEST_ASSERT(ctx.inner_called);
|
||||
}
|
Loading…
Reference in a new issue