Skip to content

Commit eb5752c

Browse files
committed
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.
1 parent f11ad0c commit eb5752c

File tree

3 files changed

+36
-19
lines changed

3 files changed

+36
-19
lines changed

components/esp32/system_api.c

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -266,15 +266,10 @@ void IRAM_ATTR esp_restart(void)
266266
*/
267267
void IRAM_ATTR esp_restart_noos()
268268
{
269-
const uint32_t core_id = xPortGetCoreID();
270-
const uint32_t other_core_id = core_id == 0 ? 1 : 0;
271-
esp_cpu_stall(other_core_id);
272-
273-
// other core is now stalled, can access DPORT registers directly
274-
esp_dport_access_int_pause();
269+
// Disable interrupts
270+
xt_ints_off(0xFFFFFFFF);
275271

276-
// We need to disable TG0/TG1 watchdogs
277-
// First enable RTC watchdog for 1 second
272+
// Enable RTC watchdog for 1 second
278273
REG_WRITE(RTC_CNTL_WDTWPROTECT_REG, RTC_CNTL_WDT_WKEY_VALUE);
279274
REG_WRITE(RTC_CNTL_WDTCONFIG0_REG,
280275
RTC_CNTL_WDT_FLASHBOOT_MOD_EN_M |
@@ -284,6 +279,18 @@ void IRAM_ATTR esp_restart_noos()
284279
(1 << RTC_CNTL_WDT_CPU_RESET_LENGTH_S) );
285280
REG_WRITE(RTC_CNTL_WDTCONFIG1_REG, rtc_clk_slow_freq_get_hz() * 1);
286281

282+
// Reset and stall the other CPU.
283+
// CPU must be reset before stalling, in case it was running a s32c1i
284+
// instruction. This would cause memory pool to be locked by arbiter
285+
// to the stalled CPU, preventing current CPU from accessing this pool.
286+
const uint32_t core_id = xPortGetCoreID();
287+
const uint32_t other_core_id = (core_id == 0) ? 1 : 0;
288+
esp_cpu_reset(other_core_id);
289+
esp_cpu_stall(other_core_id);
290+
291+
// Other core is now stalled, can access DPORT registers directly
292+
esp_dport_access_int_abort();
293+
287294
// Disable TG0/TG1 watchdogs
288295
TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
289296
TIMERG0.wdt_config0.en = 0;
@@ -292,8 +299,10 @@ void IRAM_ATTR esp_restart_noos()
292299
TIMERG1.wdt_config0.en = 0;
293300
TIMERG1.wdt_wprotect=0;
294301

295-
// Disable all interrupts
296-
xt_ints_off(0xFFFFFFFF);
302+
// Flush any data left in UART FIFOs
303+
uart_tx_wait_idle(0);
304+
uart_tx_wait_idle(1);
305+
uart_tx_wait_idle(2);
297306

298307
// Disable cache
299308
Cache_Read_Disable(0);
@@ -310,11 +319,6 @@ void IRAM_ATTR esp_restart_noos()
310319
WRITE_PERI_REG(GPIO_FUNC5_IN_SEL_CFG_REG, 0x30);
311320
#endif
312321

313-
// Flush any data left in UART FIFOs
314-
uart_tx_wait_idle(0);
315-
uart_tx_wait_idle(1);
316-
uart_tx_wait_idle(2);
317-
318322
// Reset wifi/bluetooth/ethernet/sdio (bb/mac)
319323
DPORT_SET_PERI_REG_MASK(DPORT_CORE_RST_EN_REG,
320324
DPORT_BB_RST | DPORT_FE_RST | DPORT_MAC_RST |
@@ -337,14 +341,14 @@ void IRAM_ATTR esp_restart_noos()
337341
// Reset CPUs
338342
if (core_id == 0) {
339343
// Running on PRO CPU: APP CPU is stalled. Can reset both CPUs.
340-
SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG,
341-
RTC_CNTL_SW_PROCPU_RST_M | RTC_CNTL_SW_APPCPU_RST_M);
344+
esp_cpu_reset(1);
345+
esp_cpu_reset(0);
342346
} else {
343347
// Running on APP CPU: need to reset PRO CPU and unstall it,
344348
// then reset APP CPU
345-
SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_SW_PROCPU_RST_M);
349+
esp_cpu_reset(0);
346350
esp_cpu_unstall(0);
347-
SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_SW_APPCPU_RST_M);
351+
esp_cpu_reset(1);
348352
}
349353
while(true) {
350354
;

components/soc/esp32/cpu_util.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@ void IRAM_ATTR esp_cpu_unstall(int cpu_id)
4444
}
4545
}
4646

47+
void IRAM_ATTR esp_cpu_reset(int cpu_id)
48+
{
49+
SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG,
50+
cpu_id == 0 ? RTC_CNTL_SW_PROCPU_RST_M : RTC_CNTL_SW_APPCPU_RST_M);
51+
}
52+
4753
bool IRAM_ATTR esp_cpu_in_ocd_debug_mode()
4854
{
4955
#if CONFIG_ESP32_DEBUG_OCDAWARE

components/soc/esp32/include/soc/cpu.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,13 @@ void esp_cpu_stall(int cpu_id);
8585
*/
8686
void esp_cpu_unstall(int cpu_id);
8787

88+
/**
89+
* @brief Reset CPU using RTC controller
90+
* @param cpu_id ID of the CPU to reset (0 = PRO, 1 = APP)
91+
*/
92+
void esp_cpu_reset(int cpu_id);
93+
94+
8895
/**
8996
* @brief Returns true if a JTAG debugger is attached to CPU
9097
* OCD (on chip debug) port.

0 commit comments

Comments
 (0)