mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
Merge branch 'bugfix/replace_setjmp_rom' into 'master'
[system]: Made longjmp save for context switch Closes IDFGH-3232 See merge request espressif/esp-idf!13119
This commit is contained in:
commit
83e68ee19e
@ -247,7 +247,7 @@ missing-member-hint-distance=1
|
||||
missing-member-max-choices=1
|
||||
|
||||
# List of decorators that change the signature of a decorated function.
|
||||
signature-mutators=
|
||||
signature-mutators=ttfw_idf.idf_example_test,ttfw_idf.idf_unit_test,ttfw_idf.idf_custom_test,ttfw_idf.idf_component_unit_test
|
||||
|
||||
|
||||
[SPELLING]
|
||||
|
@ -8,7 +8,7 @@ endif
|
||||
|
||||
PROJECT_NAME := bootloader
|
||||
|
||||
COMPONENTS := esp_hw_support esptool_py bootloader_support log spi_flash micro-ecc soc main efuse esp_rom hal
|
||||
COMPONENTS := esp_hw_support esptool_py bootloader_support log spi_flash micro-ecc soc main efuse esp_rom hal xtensa
|
||||
|
||||
# Clear C and CXX from top level project
|
||||
CFLAGS =
|
||||
|
@ -1,8 +1,14 @@
|
||||
idf_build_get_property(target IDF_TARGET)
|
||||
|
||||
idf_component_register(SRCS "patches/esp_rom_crc.c"
|
||||
"patches/esp_rom_sys.c"
|
||||
"patches/esp_rom_uart.c"
|
||||
set(sources "patches/esp_rom_crc.c"
|
||||
"patches/esp_rom_sys.c"
|
||||
"patches/esp_rom_uart.c")
|
||||
|
||||
if(CONFIG_IDF_TARGET_ARCH_XTENSA)
|
||||
list(APPEND sources "patches/esp_rom_longjmp.S")
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS ${sources}
|
||||
INCLUDE_DIRS include "${target}"
|
||||
PRIV_REQUIRES soc hal)
|
||||
|
||||
@ -96,6 +102,9 @@ else() # Regular app build
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(CONFIG_IDF_TARGET_ARCH_XTENSA)
|
||||
target_link_libraries(${COMPONENT_LIB} INTERFACE "-Wl,--wrap=longjmp")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(target STREQUAL "esp32s2")
|
||||
|
@ -39,5 +39,6 @@ endif
|
||||
|
||||
COMPONENT_ADD_LDFLAGS += -L $(COMPONENT_PATH)/esp32/ld \
|
||||
$(addprefix -T ,$(LINKER_SCRIPTS)) \
|
||||
-l$(COMPONENT_NAME) -Wl,--wrap=longjmp \
|
||||
|
||||
COMPONENT_ADD_LINKER_DEPS += $(addprefix esp32/ld/, $(LINKER_SCRIPTS))
|
||||
|
71
components/esp_rom/patches/esp_rom_longjmp.S
Normal file
71
components/esp_rom/patches/esp_rom_longjmp.S
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
Copyright (c) 2001-2006 by Tensilica Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
This file contains a modified version of the original Xtensa longjmp implementation.
|
||||
In this modified version, setting WINDOWSTART = 1 << WINDOWBASE is done inside a critical section.
|
||||
This is necessary because after a FreeRTOS context switch in IDF, the values of WINDOWBASE and WINDOWSTART
|
||||
are not guaranteed to be the same as before the context switch.
|
||||
*/
|
||||
|
||||
#include <xtensa/corebits.h>
|
||||
|
||||
/*
|
||||
Replacement of the first instructions of void longjmp (jmp_buf env, int val)
|
||||
*/
|
||||
|
||||
.align 4
|
||||
.literal_position
|
||||
.global __wrap_longjmp
|
||||
.type __wrap_longjmp, @function
|
||||
__wrap_longjmp:
|
||||
entry sp, 16
|
||||
|
||||
/* Deactivate interrupts in order to modify WINDOWBASE and WINDOWSTART. */
|
||||
rsr a7, PS /* to be restored after SPILL_ALL_WINDOWS */
|
||||
movi a5, PS_EXCM /* PS_INTLEVEL_MASK */
|
||||
or a5, a7, a5 /* get the current INTLEVEL */
|
||||
wsr a5, PS
|
||||
|
||||
/* Invalidate all but the current window;
|
||||
set WindowStart to (1 << WindowBase). */
|
||||
rsr a5, WINDOWBASE
|
||||
movi a4, 1
|
||||
ssl a5
|
||||
sll a4, a4
|
||||
wsr a4, WINDOWSTART
|
||||
rsync
|
||||
|
||||
/* Activate interrupts again after modifying WINDOWBASE and WINDOWSTART. */
|
||||
wsr a7, PS
|
||||
|
||||
/* Jump back to original longjmp implementation.
|
||||
The jump target is the instrucion
|
||||
l32i a0, a2, 64
|
||||
of the original code. Hence, the original code's entry instruction and windowstart modification are left
|
||||
out.
|
||||
*/
|
||||
movi a0, __real_longjmp + 20
|
||||
jx a0
|
||||
|
||||
.size __wrap_longjmp, . - __wrap_longjmp
|
@ -5,3 +5,7 @@ COMPONENT_ADD_LDFLAGS += $(COMPONENT_PATH)/esp32/libxt_hal.a
|
||||
COMPONENT_ADD_LDFRAGMENTS += linker.lf
|
||||
|
||||
COMPONENT_SRCDIRS := . esp32
|
||||
|
||||
ifdef IS_BOOTLOADER_BUILD
|
||||
COMPONENT_OBJEXCLUDE := xtensa_intr.o xtensa_intr_asm.o
|
||||
endif
|
||||
|
@ -267,6 +267,7 @@ tools/test_apps/protocols/mqtt/publish_connect_test/app_test.py
|
||||
tools/test_apps/protocols/openssl/app_test.py
|
||||
tools/test_apps/protocols/pppos/app_test.py
|
||||
tools/test_apps/system/gdb_loadable_elf/app_test.py
|
||||
tools/test_apps/system/longjmp_test/app_test.py
|
||||
tools/test_apps/system/memprot/app_test.py
|
||||
tools/test_apps/system/monitor_ide_integration/app_test.py
|
||||
tools/test_apps/system/panic/app_test.py
|
||||
|
6
tools/test_apps/system/longjmp_test/CMakeLists.txt
Normal file
6
tools/test_apps/system/longjmp_test/CMakeLists.txt
Normal file
@ -0,0 +1,6 @@
|
||||
# The following lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(hello-world)
|
8
tools/test_apps/system/longjmp_test/Makefile
Normal file
8
tools/test_apps/system/longjmp_test/Makefile
Normal file
@ -0,0 +1,8 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := longjmp_test
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
13
tools/test_apps/system/longjmp_test/README.md
Normal file
13
tools/test_apps/system/longjmp_test/README.md
Normal file
@ -0,0 +1,13 @@
|
||||
| Supported Targets | ESP32 | ESP32-S2 | ESP32-S3 |
|
||||
| ----------------- | ----- | -------- | -------- |
|
||||
|
||||
# Building
|
||||
Example building for ESP32:
|
||||
```
|
||||
idf.py set-target esp32
|
||||
cp sdkconfig.defaults sdkconfig
|
||||
idf.py build
|
||||
```
|
||||
|
||||
# Running
|
||||
All the setup needs to be done as described in the [test apps README](../../README.md).
|
18
tools/test_apps/system/longjmp_test/app_test.py
Normal file
18
tools/test_apps/system/longjmp_test/app_test.py
Normal file
@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import ttfw_idf
|
||||
from tiny_test_fw import Utility
|
||||
|
||||
|
||||
@ttfw_idf.idf_custom_test(env_tag='Example_GENERIC', target=['esp32', 'esp32s2'], group='test-apps')
|
||||
def test_longjmp(env, _):
|
||||
|
||||
dut = env.get_dut('longjmp_test', 'tools/test_apps/system/longjmp_test')
|
||||
dut.start_app()
|
||||
dut.expect('Test successful', 15)
|
||||
|
||||
Utility.console_log('longjmp test done.')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_longjmp()
|
2
tools/test_apps/system/longjmp_test/main/CMakeLists.txt
Normal file
2
tools/test_apps/system/longjmp_test/main/CMakeLists.txt
Normal file
@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "hello_world_main.c"
|
||||
INCLUDE_DIRS "")
|
118
tools/test_apps/system/longjmp_test/main/hello_world_main.c
Normal file
118
tools/test_apps/system/longjmp_test/main/hello_world_main.c
Normal file
@ -0,0 +1,118 @@
|
||||
/* test longjmp
|
||||
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_spi_flash.h"
|
||||
#include <esp_task.h>
|
||||
|
||||
#include <setjmp.h>
|
||||
|
||||
#define LUAI_NOIPA __attribute__((__noipa__))
|
||||
#define LUAI_THROW(c) longjmp((c)->b, 1)
|
||||
#define LUAI_TRY(c,a) if (setjmp((c)->b) == 0) { a }
|
||||
|
||||
#define TIMEOUT 50
|
||||
|
||||
#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);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
jmp_buf b;
|
||||
} jmp_ctx;
|
||||
|
||||
LUAI_NOIPA
|
||||
static void pret(jmp_ctx *jc) {
|
||||
LUAI_THROW(jc);
|
||||
}
|
||||
|
||||
LUAI_NOIPA
|
||||
static void precurse(jmp_ctx *jc, int n) {
|
||||
if (n) precurse(jc, n - 1);
|
||||
else pret(jc);
|
||||
}
|
||||
|
||||
LUAI_NOIPA
|
||||
static void ptest(jmp_ctx *jc) {
|
||||
precurse(jc, 64);
|
||||
}
|
||||
|
||||
LUAI_NOIPA
|
||||
void pcall(void (*func)(jmp_ctx *ctx)) {
|
||||
jmp_ctx jc;
|
||||
LUAI_TRY(&jc,
|
||||
ptest(&jc);
|
||||
);
|
||||
}
|
||||
|
||||
static void sjlj_task(void *ctx) {
|
||||
uint32_t start = xTaskGetTickCount();
|
||||
for (;;) {
|
||||
pcall(ptest);
|
||||
uint32_t end = xTaskGetTickCount();
|
||||
|
||||
uint32_t dt = end - start;
|
||||
if (dt >= 1000) {
|
||||
start = end;
|
||||
|
||||
printf("[%u] sjlj tick %d\n", end, (int)ctx);
|
||||
}
|
||||
|
||||
if (end > 9800) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
const esp_timer_create_args_t timer_args = {
|
||||
timer_cb,
|
||||
NULL,
|
||||
ESP_TIMER_TASK,
|
||||
"crash_timer",
|
||||
true,
|
||||
};
|
||||
|
||||
esp_timer_create(&timer_args, &crash_timer);
|
||||
esp_timer_start_periodic(crash_timer, TIMEOUT);
|
||||
|
||||
printf("Hello world!\n");
|
||||
printf("Free heap: %d\n", esp_get_free_heap_size());
|
||||
|
||||
for (size_t i = 0; i < 16; i++) {
|
||||
xTaskCreate(sjlj_task, "sjlj_task", 4096, (void *) i, tskIDLE_PRIORITY + 0, NULL);
|
||||
}
|
||||
|
||||
vTaskDelay(10000);
|
||||
printf("stopping timers...\n");
|
||||
esp_timer_stop(crash_timer);
|
||||
esp_timer_delete(crash_timer);
|
||||
|
||||
printf("Test successful\n");
|
||||
}
|
11
tools/test_apps/system/longjmp_test/sdkconfig.defaults
Normal file
11
tools/test_apps/system/longjmp_test/sdkconfig.defaults
Normal file
@ -0,0 +1,11 @@
|
||||
CONFIG_COMPILER_OPTIMIZATION_PERF=y
|
||||
|
||||
CONFIG_ESP_INT_WDT=n
|
||||
CONFIG_ESP_TASK_WDT=n
|
||||
|
||||
CONFIG_ESP_SYSTEM_PANIC_PRINT_REBOOT=y
|
||||
|
||||
CONFIG_ESP_TIMER_TASK_STACK_SIZE=3584
|
||||
|
||||
CONFIG_FREERTOS_UNICORE=y
|
||||
CONFIG_FREERTOS_HZ=1000
|
Loading…
Reference in New Issue
Block a user