From e324707cc884485e908504bb8288481e61ceee3b Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Thu, 26 Oct 2017 19:11:47 +0800 Subject: [PATCH] esp_restart: fix possible race while stalling other CPU, enable WDT early Previously esp_restart would stall the other CPU before enabling RTC_WDT. If the other CPU was executing an s32c1i instruction, the lock signal from CPU to the arbiter would still be held after CPU was stalled. If the CPU running esp_restart would then try to access the same locked memory pool, it would be stuck, because lock signal would never be released. With this change, esp_restart resets the other CPU before stalling it. Ideally, we would want to reset the CPU and keep it in reset, but the hardware doesn't have such feature for PRO_CPU (it is possible to hold APP_CPU in reset using DPORT register). Given that ROM code will not use s32c1i in the first few hundred cycles, doing reset and then stall seems to be safe. In addition to than, RTC_WDT initialization is moved to the beginning of the function, to prevent possible lock-up if CPU stalling still has any issue. --- components/esp32/dport_access.c | 9 +++++ components/esp32/include/esp_dport_access.h | 1 + components/esp32/system_api.c | 35 +++++++++++-------- components/soc/esp32/cpu_util.c | 6 ++++ components/soc/esp32/include/soc/cpu.h | 7 ++++ .../soc/esp32/include/soc/rtc_cntl_reg.h | 1 + 6 files changed, 45 insertions(+), 14 deletions(-) diff --git a/components/esp32/dport_access.c b/components/esp32/dport_access.c index 473c43325..03ea68236 100644 --- a/components/esp32/dport_access.c +++ b/components/esp32/dport_access.c @@ -185,3 +185,12 @@ void esp_dport_access_int_deinit(void) #endif portEXIT_CRITICAL_ISR(&g_dport_mux); } + + +void esp_dport_access_int_abort(void) +{ + dport_core_state[0] = DPORT_CORE_STATE_IDLE; +#ifndef CONFIG_FREERTOS_UNICORE + dport_core_state[1] = DPORT_CORE_STATE_IDLE; +#endif +} diff --git a/components/esp32/include/esp_dport_access.h b/components/esp32/include/esp_dport_access.h index 8b081c5ae..220516309 100644 --- a/components/esp32/include/esp_dport_access.h +++ b/components/esp32/include/esp_dport_access.h @@ -23,6 +23,7 @@ void esp_dport_access_stall_other_cpu_start(void); void esp_dport_access_stall_other_cpu_end(void); void esp_dport_access_int_init(void); void esp_dport_access_int_deinit(void); +void esp_dport_access_int_abort(void); #if defined(BOOTLOADER_BUILD) || defined(CONFIG_FREERTOS_UNICORE) || !defined(ESP_PLATFORM) #define DPORT_STALL_OTHER_CPU_START() diff --git a/components/esp32/system_api.c b/components/esp32/system_api.c index b3c54b12b..4d7beb55e 100644 --- a/components/esp32/system_api.c +++ b/components/esp32/system_api.c @@ -256,22 +256,31 @@ void IRAM_ATTR esp_restart(void) */ void IRAM_ATTR esp_restart_noos() { - const uint32_t core_id = xPortGetCoreID(); - const uint32_t other_core_id = core_id == 0 ? 1 : 0; - esp_cpu_stall(other_core_id); + // Disable interrupts + xt_ints_off(0xFFFFFFFF); - // other core is now stalled, can access DPORT registers directly - esp_dport_access_int_deinit(); - - // We need to disable TG0/TG1 watchdogs - // First enable RTC watchdog to be on the safe side + // Enable RTC watchdog for 1 second REG_WRITE(RTC_CNTL_WDTWPROTECT_REG, RTC_CNTL_WDT_WKEY_VALUE); REG_WRITE(RTC_CNTL_WDTCONFIG0_REG, RTC_CNTL_WDT_FLASHBOOT_MOD_EN_M | + (RTC_WDT_STG_SEL_RESET_SYSTEM << RTC_CNTL_WDT_STG0_S) | + (RTC_WDT_STG_SEL_RESET_RTC << RTC_CNTL_WDT_STG1_S) | (1 << RTC_CNTL_WDT_SYS_RESET_LENGTH_S) | (1 << RTC_CNTL_WDT_CPU_RESET_LENGTH_S) ); REG_WRITE(RTC_CNTL_WDTCONFIG1_REG, 128000); + // Reset and stall the other CPU. + // CPU must be reset before stalling, in case it was running a s32c1i + // instruction. This would cause memory pool to be locked by arbiter + // to the stalled CPU, preventing current CPU from accessing this pool. + const uint32_t core_id = xPortGetCoreID(); + const uint32_t other_core_id = (core_id == 0) ? 1 : 0; + esp_cpu_reset(other_core_id); + esp_cpu_stall(other_core_id); + + // Other core is now stalled, can access DPORT registers directly + esp_dport_access_int_abort(); + // Disable TG0/TG1 watchdogs TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; TIMERG0.wdt_config0.en = 0; @@ -280,8 +289,6 @@ void IRAM_ATTR esp_restart_noos() TIMERG1.wdt_config0.en = 0; TIMERG1.wdt_wprotect=0; - // Disable all interrupts - xt_ints_off(0xFFFFFFFF); // Disable cache Cache_Read_Disable(0); @@ -322,14 +329,14 @@ void IRAM_ATTR esp_restart_noos() // Reset CPUs if (core_id == 0) { // Running on PRO CPU: APP CPU is stalled. Can reset both CPUs. - SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, - RTC_CNTL_SW_PROCPU_RST_M | RTC_CNTL_SW_APPCPU_RST_M); + esp_cpu_reset(1); + esp_cpu_reset(0); } else { // Running on APP CPU: need to reset PRO CPU and unstall it, // then reset APP CPU - SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_SW_PROCPU_RST_M); + esp_cpu_reset(0); esp_cpu_unstall(0); - SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_SW_APPCPU_RST_M); + esp_cpu_reset(1); } while(true) { ; diff --git a/components/soc/esp32/cpu_util.c b/components/soc/esp32/cpu_util.c index ecfcab4ba..bc052af98 100644 --- a/components/soc/esp32/cpu_util.c +++ b/components/soc/esp32/cpu_util.c @@ -44,6 +44,12 @@ void IRAM_ATTR esp_cpu_unstall(int cpu_id) } } +void IRAM_ATTR esp_cpu_reset(int cpu_id) +{ + SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, + cpu_id == 0 ? RTC_CNTL_SW_PROCPU_RST_M : RTC_CNTL_SW_APPCPU_RST_M); +} + bool IRAM_ATTR esp_cpu_in_ocd_debug_mode() { #if CONFIG_ESP32_DEBUG_OCDAWARE diff --git a/components/soc/esp32/include/soc/cpu.h b/components/soc/esp32/include/soc/cpu.h index b56fb3dc8..05ec91776 100644 --- a/components/soc/esp32/include/soc/cpu.h +++ b/components/soc/esp32/include/soc/cpu.h @@ -85,6 +85,13 @@ void esp_cpu_stall(int cpu_id); */ void esp_cpu_unstall(int cpu_id); +/** + * @brief Reset CPU using RTC controller + * @param cpu_id ID of the CPU to reset (0 = PRO, 1 = APP) + */ +void esp_cpu_reset(int cpu_id); + + /** * @brief Returns true if a JTAG debugger is attached to CPU * OCD (on chip debug) port. diff --git a/components/soc/esp32/include/soc/rtc_cntl_reg.h b/components/soc/esp32/include/soc/rtc_cntl_reg.h index 02f8dff2c..ffcbb3c03 100644 --- a/components/soc/esp32/include/soc/rtc_cntl_reg.h +++ b/components/soc/esp32/include/soc/rtc_cntl_reg.h @@ -1718,6 +1718,7 @@ #define RTC_WDT_STG_SEL_INT 1 #define RTC_WDT_STG_SEL_RESET_CPU 2 #define RTC_WDT_STG_SEL_RESET_SYSTEM 3 +#define RTC_WDT_STG_SEL_RESET_RTC 4 #define RTC_CNTL_WDTCONFIG1_REG (DR_REG_RTCCNTL_BASE + 0x90) /* RTC_CNTL_WDT_STG0_HOLD : R/W ;bitpos:[31:0] ;default: 32'd128000 ; */