From 9c715d7946a9595bad53307cf0a141d4226d0a5a Mon Sep 17 00:00:00 2001 From: Konstantin Kondrashov Date: Thu, 26 Jul 2018 17:07:36 +0800 Subject: [PATCH] bootloader_support: Fix enable rtc_wdt for resolve issue with varying supply Eliminates the issue with the lock up in the bootloader due to a power drawdown during its operation. Closes https://github.com/espressif/esp-idf/issues/1814 --- components/bootloader/Kconfig.projbuild | 32 +++++++++++++++++++ .../include/esp_flash_encrypt.h | 3 ++ .../bootloader_support/src/bootloader_init.c | 13 ++++++++ .../bootloader_support/src/flash_encrypt.c | 2 ++ components/esp32/clk.c | 21 ++++++++++++ components/esp32/cpu_start.c | 9 ++++-- components/soc/esp32/rtc_wdt.c | 21 ++++++++++++ components/soc/include/soc/rtc_wdt.h | 12 +++++++ 8 files changed, 110 insertions(+), 3 deletions(-) diff --git a/components/bootloader/Kconfig.projbuild b/components/bootloader/Kconfig.projbuild index 75dae508ae..bca976e102 100644 --- a/components/bootloader/Kconfig.projbuild +++ b/components/bootloader/Kconfig.projbuild @@ -127,6 +127,38 @@ config BOOTLOADER_HOLD_TIME_GPIO The GPIO must be held low continuously for this period of time after reset before a factory reset or test partition boot (as applicable) is performed. +config BOOTLOADER_WDT_ENABLE + bool "Use RTC watchdog in start code" + default y + help + Tracks the execution time of startup code. + If the execution time is exceeded, the RTC_WDT will restart system. + It is also useful to prevent a lock up in start code caused by an unstable power source. + NOTE: Tracks the execution time starts from the bootloader code - re-set timeout, while selecting the source for slow_clk - and ends calling app_main. + Re-set timeout is needed due to WDT uses a SLOW_CLK clock source. After changing a frequency slow_clk a time of WDT needs to re-set for new frequency. + slow_clk depends on ESP32_RTC_CLOCK_SOURCE (INTERNAL_RC or EXTERNAL_CRYSTAL). + +config BOOTLOADER_WDT_DISABLE_IN_USER_CODE + bool "Allows RTC watchdog disable in user code" + depends on BOOTLOADER_WDT_ENABLE + default n + help + If it is set, the client must itself reset or disable rtc_wdt in their code (app_main()). + Otherwise rtc_wdt will be disabled before calling app_main function. + Use function rtc_wdt_feed() for resetting counter of rtc_wdt. + Use function rtc_wdt_disable() for disabling rtc_wdt. + +config BOOTLOADER_WDT_TIME_MS + int "Timeout for RTC watchdog (ms)" + depends on BOOTLOADER_WDT_ENABLE + default 9000 + range 0 120000 + help + Verify that this parameter is correct and more then the execution time. + Pay attention to options such as reset to factory, trigger test partition and encryption on boot + - these options can increase the execution time. + Note: RTC_WDT will reset while encryption operations will be performed. + endmenu # Bootloader diff --git a/components/bootloader_support/include/esp_flash_encrypt.h b/components/bootloader_support/include/esp_flash_encrypt.h index 2f4679b2a4..ab22f67d4d 100644 --- a/components/bootloader_support/include/esp_flash_encrypt.h +++ b/components/bootloader_support/include/esp_flash_encrypt.h @@ -83,6 +83,8 @@ static inline /** @cond */ IRAM_ATTR /** @endcond */ bool esp_flash_encryption_e * @note Take care not to power off the device while this function * is running, or the partition currently being encrypted will be lost. * + * @note RTC_WDT will reset while encryption operations will be performed (if RTC_WDT is configured). + * * @return ESP_OK if all operations succeeded, ESP_ERR_INVALID_STATE * if a fatal error occured during encryption of all partitions. */ @@ -91,6 +93,7 @@ esp_err_t esp_flash_encrypt_check_and_update(void); /** @brief Encrypt-in-place a block of flash sectors * + * @note This function resets RTC_WDT between operations with sectors. * @param src_addr Source offset in flash. Should be multiple of 4096 bytes. * @param data_length Length of data to encrypt in bytes. Will be rounded up to next multiple of 4096 bytes. * diff --git a/components/bootloader_support/src/bootloader_init.c b/components/bootloader_support/src/bootloader_init.c index fe0d756fb5..68e2101ef8 100644 --- a/components/bootloader_support/src/bootloader_init.c +++ b/components/bootloader_support/src/bootloader_init.c @@ -143,8 +143,21 @@ static esp_err_t bootloader_main() ESP_LOGI(TAG, "compile time " __TIME__ ); ets_set_appcpu_boot_addr(0); +#ifdef CONFIG_BOOTLOADER_WDT_ENABLE + ESP_LOGD(TAG, "Enabling RTCWDT(%d ms)", CONFIG_BOOTLOADER_WDT_TIME_MS); + rtc_wdt_protect_off(); + rtc_wdt_disable(); + rtc_wdt_set_length_of_reset_signal(RTC_WDT_SYS_RESET_SIG, RTC_WDT_LENGTH_3_2us); + rtc_wdt_set_length_of_reset_signal(RTC_WDT_CPU_RESET_SIG, RTC_WDT_LENGTH_3_2us); + rtc_wdt_set_stage(RTC_WDT_STAGE0, RTC_WDT_STAGE_ACTION_RESET_RTC); + rtc_wdt_set_time(RTC_WDT_STAGE0, CONFIG_BOOTLOADER_WDT_TIME_MS); + rtc_wdt_enable(); + rtc_wdt_protect_on(); +#else /* disable watch dog here */ rtc_wdt_disable(); +#endif + REG_SET_FIELD(TIMG_WDTWPROTECT_REG(0), TIMG_WDT_WKEY, TIMG_WDT_WKEY_VALUE); REG_CLR_BIT( TIMG_WDTCONFIG0_REG(0), TIMG_WDT_FLASHBOOT_MOD_EN ); #ifndef CONFIG_SPI_FLASH_ROM_DRIVER_PATCH diff --git a/components/bootloader_support/src/flash_encrypt.c b/components/bootloader_support/src/flash_encrypt.c index 7b17dd4517..e04945daec 100644 --- a/components/bootloader_support/src/flash_encrypt.c +++ b/components/bootloader_support/src/flash_encrypt.c @@ -24,6 +24,7 @@ #include "esp_efuse.h" #include "esp_log.h" #include "rom/secure_boot.h" +#include "soc/rtc_wdt.h" #include "rom/cache.h" #include "rom/spi_flash.h" /* TODO: Remove this */ @@ -317,6 +318,7 @@ esp_err_t esp_flash_encrypt_region(uint32_t src_addr, size_t data_length) } for (size_t i = 0; i < data_length; i += FLASH_SECTOR_SIZE) { + rtc_wdt_feed(); uint32_t sec_start = i + src_addr; err = bootloader_flash_read(sec_start, buf, FLASH_SECTOR_SIZE, false); if (err != ESP_OK) { diff --git a/components/esp32/clk.c b/components/esp32/clk.c index 17afaa1b34..54abc18b72 100644 --- a/components/esp32/clk.c +++ b/components/esp32/clk.c @@ -26,6 +26,7 @@ #include "rom/rtc.h" #include "soc/soc.h" #include "soc/rtc.h" +#include "soc/rtc_wdt.h" #include "soc/rtc_cntl_reg.h" #include "soc/i2s_reg.h" #include "driver/periph_ctrl.h" @@ -87,6 +88,18 @@ void esp_clk_init(void) rtc_clk_fast_freq_set(RTC_FAST_FREQ_8M); +#ifdef CONFIG_BOOTLOADER_WDT_ENABLE + // WDT uses a SLOW_CLK clock source. After a function select_rtc_slow_clk a frequency of this source can changed. + // If the frequency changes from 150kHz to 32kHz, then the timeout set for the WDT will increase 4.6 times. + // Therefore, for the time of frequency change, set a new lower timeout value (1.6 sec). + // This prevents excessive delay before resetting in case the supply voltage is drawdown. + // (If frequency is changed from 150kHz to 32kHz then WDT timeout will increased to 1.6sec * 150/32 = 7.5 sec). + rtc_wdt_protect_off(); + rtc_wdt_feed(); + rtc_wdt_set_time(RTC_WDT_STAGE0, 1600); + rtc_wdt_protect_on(); +#endif + #if defined(CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL) select_rtc_slow_clk(SLOW_CLK_32K_XTAL); #elif defined(CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_OSC) @@ -97,6 +110,14 @@ void esp_clk_init(void) select_rtc_slow_clk(RTC_SLOW_FREQ_RTC); #endif +#ifdef CONFIG_BOOTLOADER_WDT_ENABLE + // After changing a frequency WDT timeout needs to be set for new frequency. + rtc_wdt_protect_off(); + rtc_wdt_feed(); + rtc_wdt_set_time(RTC_WDT_STAGE0, CONFIG_BOOTLOADER_WDT_TIME_MS); + rtc_wdt_protect_on(); +#endif + rtc_cpu_freq_config_t old_config, new_config; rtc_clk_cpu_freq_get_config(&old_config); const uint32_t old_freq_mhz = old_config.freq_mhz; diff --git a/components/esp32/cpu_start.c b/components/esp32/cpu_start.c index bf044bd7b2..cfff63a2c5 100644 --- a/components/esp32/cpu_start.c +++ b/components/esp32/cpu_start.c @@ -137,7 +137,9 @@ void IRAM_ATTR call_start_cpu0() || rst_reas[1] == RTCWDT_SYS_RESET || rst_reas[1] == TG0WDT_SYS_RESET #endif ) { +#ifndef CONFIG_BOOTLOADER_WDT_ENABLE rtc_wdt_disable(); +#endif } //Clear BSS. Please do not attempt to do any complex stuff (like early logging) before this. @@ -427,9 +429,6 @@ static void do_global_ctors(void) static void main_task(void* args) { - // Now that the application is about to start, disable boot watchdogs - REG_CLR_BIT(TIMG_WDTCONFIG0_REG(0), TIMG_WDT_FLASHBOOT_MOD_EN_S); - rtc_wdt_disable(); #if !CONFIG_FREERTOS_UNICORE // Wait for FreeRTOS initialization to finish on APP CPU, before replacing its startup stack while (port_xSchedulerRunning[1] == 0) { @@ -470,6 +469,10 @@ static void main_task(void* args) } #endif + // Now that the application is about to start, disable boot watchdog +#ifndef CONFIG_BOOTLOADER_WDT_DISABLE_IN_USER_CODE + rtc_wdt_disable(); +#endif app_main(); vTaskDelete(NULL); } diff --git a/components/soc/esp32/rtc_wdt.c b/components/soc/esp32/rtc_wdt.c index 5fea2fd55c..67770709bc 100644 --- a/components/soc/esp32/rtc_wdt.c +++ b/components/soc/esp32/rtc_wdt.c @@ -87,6 +87,27 @@ esp_err_t rtc_wdt_set_time(rtc_wdt_stage_t stage, unsigned int timeout_ms) return ESP_OK; } +esp_err_t rtc_wdt_get_timeout(rtc_wdt_stage_t stage, unsigned int* timeout_ms) +{ + if (stage > 3) { + return ESP_ERR_INVALID_ARG; + } + uint32_t time_tick; + if (stage == RTC_WDT_STAGE0) { + time_tick = READ_PERI_REG(RTC_CNTL_WDTCONFIG1_REG); + } else if (stage == RTC_WDT_STAGE1) { + time_tick = READ_PERI_REG(RTC_CNTL_WDTCONFIG2_REG); + } else if (stage == RTC_WDT_STAGE2) { + time_tick = READ_PERI_REG(RTC_CNTL_WDTCONFIG3_REG); + } else { + time_tick = READ_PERI_REG(RTC_CNTL_WDTCONFIG4_REG); + } + + *timeout_ms = time_tick * 1000 / rtc_clk_slow_freq_get_hz(); + + return ESP_OK; +} + esp_err_t rtc_wdt_set_stage(rtc_wdt_stage_t stage, rtc_wdt_stage_action_t stage_sel) { if (stage > 3 || stage_sel > 4) { diff --git a/components/soc/include/soc/rtc_wdt.h b/components/soc/include/soc/rtc_wdt.h index 9094a305a0..ec7175a002 100644 --- a/components/soc/include/soc/rtc_wdt.h +++ b/components/soc/include/soc/rtc_wdt.h @@ -142,6 +142,18 @@ void rtc_wdt_feed(); */ esp_err_t rtc_wdt_set_time(rtc_wdt_stage_t stage, unsigned int timeout_ms); +/** + * @brief Get the timeout set for the required stage. + * + * @param[in] stage Stage of rtc_wdt. + * @param[out] timeout_ms Timeout set for this stage. (not elapsed time). + * + * @return + * - ESP_OK In case of success + * - ESP_ERR_INVALID_ARG If stage has invalid value + */ +esp_err_t rtc_wdt_get_timeout(rtc_wdt_stage_t stage, unsigned int* timeout_ms); + /** * @brief Set an action for required stage. *