mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
Merge branch 'bugfix/window_spill_a0_corruption_v4.1' into 'release/v4.1'
freertos: don't clobber a4 while spilling register windows (backport v4.1) See merge request espressif/esp-idf!10306
This commit is contained in:
commit
573f5de99a
@ -245,6 +245,60 @@ TEST_CASE("c++ exceptions emergency pool", "[cxx] [exceptions] [ignore] [leaks=3
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#define TIMEOUT 19
|
||||
|
||||
#define RECURSION 19
|
||||
|
||||
static esp_timer_handle_t crash_timer;
|
||||
|
||||
static uint32_t result = 0;
|
||||
|
||||
uint32_t calc_fac(uint32_t n) {
|
||||
if (n == 1 || n == 0) {
|
||||
return 1;
|
||||
} else {
|
||||
return n * calc_fac(n - 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void timer_cb(void *arg) {
|
||||
result = calc_fac(RECURSION);
|
||||
}
|
||||
|
||||
// TODO: Not a unit test, refactor to integration test/system test, etc.
|
||||
TEST_CASE("frequent interrupts don't interfere with c++ exceptions", "[cxx] [exceptions] [leaks=800]")
|
||||
{// if exception workaround is disabled, this is almost guaranteed to fail
|
||||
const esp_timer_create_args_t timer_args {
|
||||
timer_cb,
|
||||
NULL,
|
||||
ESP_TIMER_TASK,
|
||||
"crash_timer"
|
||||
};
|
||||
|
||||
TEST_ESP_OK(esp_timer_create(&timer_args, &crash_timer));
|
||||
TEST_ESP_OK(esp_timer_start_periodic(crash_timer, TIMEOUT));
|
||||
|
||||
for (int i = 0; i < 500; i++) {
|
||||
bool thrown_value = false;
|
||||
try {
|
||||
throw true;
|
||||
} catch (bool e) {
|
||||
thrown_value = e;
|
||||
}
|
||||
|
||||
if (thrown_value) {
|
||||
printf("ex thrown %d\n", i);
|
||||
} else {
|
||||
printf("ex not thrown\n");
|
||||
TEST_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_ESP_OK(esp_timer_stop(crash_timer));
|
||||
TEST_ESP_OK(esp_timer_delete(crash_timer));
|
||||
}
|
||||
|
||||
#else // !CONFIG_COMPILER_CXX_EXCEPTIONS
|
||||
|
||||
TEST_CASE("std::out_of_range exception when -fno-exceptions", "[cxx][reset=abort,SW_CPU_RESET]")
|
||||
|
43
components/freertos/test/test_context_save_clobber.c
Normal file
43
components/freertos/test/test_context_save_clobber.c
Normal file
@ -0,0 +1,43 @@
|
||||
#include "unity.h"
|
||||
#include "esp_intr_alloc.h"
|
||||
|
||||
#if defined(__XTENSA__)
|
||||
#include "xtensa/config/core-isa.h"
|
||||
#include "xtensa/hal.h"
|
||||
#if defined(XCHAL_HAVE_WINDOWED)
|
||||
/* Regression test for a0 register being corrupted in _xt_context_save.
|
||||
*
|
||||
* The idea in this test is to have a function which recursively calls itself
|
||||
* with call4, eventually filling up all the register windows. At that point,
|
||||
* it does some lengthy operation. If an interrupt occurs at that point, and
|
||||
* corrupts a0 register of one of the windows, this will cause an exception
|
||||
* when the recursive function returns.
|
||||
*/
|
||||
|
||||
|
||||
/* See test_context_save_clober_func.S */
|
||||
extern void test_context_save_clober_func(void);
|
||||
|
||||
static void int_timer_handler(void *arg)
|
||||
{
|
||||
xthal_set_ccompare(1, xthal_get_ccount() + 10000);
|
||||
(*(int*) arg)++;
|
||||
}
|
||||
|
||||
TEST_CASE("context save doesn't corrupt return address register", "[freertos]")
|
||||
{
|
||||
/* set up an interrupt */
|
||||
intr_handle_t ih;
|
||||
int int_triggered = 0;
|
||||
TEST_ESP_OK(esp_intr_alloc(ETS_INTERNAL_TIMER1_INTR_SOURCE, 0, int_timer_handler, &int_triggered, &ih));
|
||||
xthal_set_ccompare(1, xthal_get_ccount() + 10000);
|
||||
|
||||
/* fill all the windows and delay a bit, waiting for an interrupt to happen */
|
||||
test_context_save_clober_func();
|
||||
|
||||
esp_intr_free(ih);
|
||||
TEST_ASSERT_GREATER_THAN(0, int_triggered);
|
||||
}
|
||||
|
||||
#endif // XCHAL_HAVE_WINDOWED
|
||||
#endif // __XTENSA__
|
55
components/freertos/test/test_context_save_clobber_asm.S
Normal file
55
components/freertos/test/test_context_save_clobber_asm.S
Normal file
@ -0,0 +1,55 @@
|
||||
/* Helper function for the test case in test_context_save_clobber.c */
|
||||
|
||||
#if defined(__XTENSA__)
|
||||
#include "xtensa/config/core-isa.h"
|
||||
#if defined(XCHAL_HAVE_WINDOWED)
|
||||
|
||||
.data
|
||||
recursion_count:
|
||||
.word 0
|
||||
|
||||
.text
|
||||
.global test_context_save_clober_func
|
||||
.type test_context_save_clober_func,@function
|
||||
.align 4
|
||||
|
||||
/* This function recursively calls itself via call4, making sure each frame
|
||||
* uses only 4 registers. For this reason, recursion count can not be
|
||||
* a function argument (it would have to be in a6) and is placed into .data
|
||||
* section above.
|
||||
*/
|
||||
test_context_save_clober_func:
|
||||
entry a1, 16
|
||||
|
||||
/* load recursion count from memory */
|
||||
movi a3, recursion_count
|
||||
l32i a2, a3, 0
|
||||
|
||||
/* if it is zero, initialize it to 16 (=64 physical registers / 4 registers per call) */
|
||||
bnez a2, 1f
|
||||
movi a2, 16
|
||||
1:
|
||||
|
||||
/* decrement the counter and write it back */
|
||||
addi a2, a2, -1
|
||||
s32i a2, a3, 0
|
||||
|
||||
/* counter not zero? do a recursive call */
|
||||
beqz a2, wait
|
||||
call4 test_context_save_clober_func
|
||||
j end
|
||||
|
||||
wait:
|
||||
/* Counter has reached zero, and we have 16 frames on the stack.
|
||||
* Delay for a few seconds, expecting in interrupt to happen.
|
||||
*/
|
||||
movi a3, 100000000
|
||||
1:
|
||||
addi a3, a3, -1
|
||||
bnez a3, 1b
|
||||
|
||||
end:
|
||||
retw
|
||||
|
||||
#endif // XCHAL_HAVE_WINDOWED
|
||||
#endif // __XTENSA__
|
@ -156,7 +156,7 @@ _xt_context_save:
|
||||
movi a3, -XCHAL_EXTRA_SA_ALIGN
|
||||
and a2, a2, a3 /* align dynamically >16 bytes */
|
||||
# endif
|
||||
call0 xthal_save_extra_nw /* destroys a0,2,3,4,5 */
|
||||
call0 xthal_save_extra_nw /* destroys a0,2,3 */
|
||||
#endif
|
||||
|
||||
#ifndef __XTENSA_CALL0_ABI__
|
||||
@ -173,17 +173,23 @@ _xt_context_save:
|
||||
* 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.
|
||||
* Note: even though a4-a15 are saved into the exception frame, we should not
|
||||
* clobber them until after SPILL_ALL_WINDOWS. This is because these registers
|
||||
* may contain live windows belonging to previous frames in the call stack.
|
||||
* These frames will be spilled by SPILL_ALL_WINDOWS, and if the register was
|
||||
* used as a temporary by this code, the temporary value would get stored
|
||||
* onto the stack, instead of the real value.
|
||||
*/
|
||||
rsr a2, PS /* to be restored after SPILL_ALL_WINDOWS */
|
||||
movi a4, PS_INTLEVEL_MASK
|
||||
and a3, a2, a4 /* get the current INTLEVEL */
|
||||
movi a0, PS_INTLEVEL_MASK
|
||||
and a3, a2, a0 /* 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
|
||||
movi a0, PS_UM | PS_WOE /* clear EXCM, enable window overflow, set new INTLEVEL */
|
||||
or a3, a3, a0
|
||||
wsr a3, ps
|
||||
rsr a4, EPC1 /* to be restored after SPILL_ALL_WINDOWS */
|
||||
rsr a0, 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 */
|
||||
@ -191,7 +197,7 @@ _xt_context_save:
|
||||
|
||||
wsr a2, PS /* restore to the value at entry */
|
||||
rsync
|
||||
wsr a4, EPC1 /* likewise */
|
||||
wsr a0, EPC1 /* likewise */
|
||||
|
||||
#endif /* __XTENSA_CALL0_ABI__ */
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user