فهرست منبع

bootloader_support: Adds API to detect Factory reset has happened

Closes https://github.com/espressif/esp-idf/issues/10753
KonstantinKondrashov 2 سال پیش
والد
کامیت
efbafb873b

+ 7 - 0
components/app_update/test_apps/main/test_switch_ota.c

@@ -486,6 +486,9 @@ static void test_flow4(void)
             break;
         case 3:
             ESP_LOGI(TAG, "OTA0");
+#ifdef BOOTLOADER_RESERVE_RTC_MEM
+            TEST_ASSERT_FALSE(bootloader_common_get_rtc_retain_mem_factory_reset_state());
+#endif
             TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_OTA_0, cur_app->subtype);
             mark_app_valid();
             set_output_pin(CONFIG_BOOTLOADER_NUM_PIN_FACTORY_RESET);
@@ -494,6 +497,10 @@ static void test_flow4(void)
         case 4:
             reset_output_pin(CONFIG_BOOTLOADER_NUM_PIN_FACTORY_RESET);
             ESP_LOGI(TAG, "Factory");
+#ifdef BOOTLOADER_RESERVE_RTC_MEM
+            TEST_ASSERT_TRUE(bootloader_common_get_rtc_retain_mem_factory_reset_state());
+            TEST_ASSERT_FALSE(bootloader_common_get_rtc_retain_mem_factory_reset_state());
+#endif
             TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_FACTORY, cur_app->subtype);
             erase_ota_data();
             break;

+ 13 - 1
components/bootloader/Kconfig.projbuild

@@ -114,6 +114,7 @@ menu "Bootloader config"
     config BOOTLOADER_FACTORY_RESET
         bool "GPIO triggers factory reset"
         default N
+        select BOOTLOADER_RESERVE_RTC_MEM if SOC_RTC_FAST_MEM_SUPPORTED
         help
             Allows to reset the device to factory settings:
             - clear one or more data partitions;
@@ -326,6 +327,7 @@ menu "Bootloader config"
         # but - as noted in help - it invalidates the integrity of Secure Boot checks
         depends on SOC_RTC_FAST_MEM_SUPPORTED && ((SECURE_BOOT && SECURE_BOOT_INSECURE) || !SECURE_BOOT)
         default n
+        select BOOTLOADER_RESERVE_RTC_MEM
         help
             This option disables the normal validation of an image coming out of
             deep sleep (checksums, SHA256, and signature). This is a trade-off
@@ -381,7 +383,7 @@ menu "Bootloader config"
     config BOOTLOADER_RESERVE_RTC_SIZE
         hex
         depends on SOC_RTC_FAST_MEM_SUPPORTED
-        default 0x10 if BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP || BOOTLOADER_CUSTOM_RESERVE_RTC
+        default 0x10 if BOOTLOADER_RESERVE_RTC_MEM
         default 0
         help
             Reserve RTC FAST memory for Skip image validation. This option in bytes.
@@ -393,6 +395,7 @@ menu "Bootloader config"
     config BOOTLOADER_CUSTOM_RESERVE_RTC
         bool "Reserve RTC FAST memory for custom purposes"
         depends on SOC_RTC_FAST_MEM_SUPPORTED
+        select BOOTLOADER_RESERVE_RTC_MEM
         default n
         help
             This option allows the customer to place data in the RTC FAST memory,
@@ -412,6 +415,15 @@ menu "Bootloader config"
             in this area of memory, you can increase it. It must be a multiple of 4 bytes.
             This area (rtc_retain_mem_t) is reserved and has access from the bootloader and an application.
 
+    config BOOTLOADER_RESERVE_RTC_MEM
+        bool
+        depends on SOC_RTC_FAST_MEM_SUPPORTED
+        help
+            This option reserves an area in RTC FAST memory for the following features:
+            - "Skip image validation when exiting deep sleep"
+            - "Reserve RTC FAST memory for custom purposes"
+            - "GPIO triggers factory reset"
+
     config BOOTLOADER_FLASH_XMC_SUPPORT
         bool "Enable the support for flash chips of XMC (READ HELP FIRST)"
         default y

+ 5 - 2
components/bootloader/subproject/main/bootloader_start.c

@@ -98,9 +98,12 @@ static int selected_boot_partition(const bootloader_state_t *bs)
             if (bootloader_common_erase_part_type_data(list_erase, ota_data_erase) == false) {
                 ESP_LOGE(TAG, "Not all partitions were erased");
             }
+#ifdef CONFIG_BOOTLOADER_RESERVE_RTC_MEM
+            bootloader_common_set_rtc_retain_mem_factory_reset_state();
+#endif
             return bootloader_utility_get_selected_boot_partition(bs);
         }
-#endif
+#endif // CONFIG_BOOTLOADER_FACTORY_RESET
         // TEST firmware.
 #ifdef CONFIG_BOOTLOADER_APP_TEST
         bool app_test_level = false;
@@ -117,7 +120,7 @@ static int selected_boot_partition(const bootloader_state_t *bs)
                 return INVALID_INDEX;
             }
         }
-#endif
+#endif // CONFIG_BOOTLOADER_APP_TEST
         // Customer implementation.
         // if (gpio_pin_1 == true && ...){
         //     boot_index = required_boot_partition;

+ 17 - 2
components/bootloader_support/include/bootloader_common.h

@@ -173,7 +173,7 @@ esp_err_t bootloader_common_check_chip_validity(const esp_image_header_t* img_hd
  */
 void bootloader_common_vddsdio_configure(void);
 
-#if defined( CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP ) || defined( CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC )
+#if CONFIG_BOOTLOADER_RESERVE_RTC_MEM
 /**
  * @brief Returns partition from rtc_retain_mem
  *
@@ -223,6 +223,21 @@ void bootloader_common_reset_rtc_retain_mem(void);
  */
 uint16_t bootloader_common_get_rtc_retain_mem_reboot_counter(void);
 
+/**
+ * @brief Returns True if Factory reset has happened
+ *
+ * Reset the status after reading it.
+ *
+ * @return True: Factory reset has happened
+ *         False: No Factory reset
+ */
+bool bootloader_common_get_rtc_retain_mem_factory_reset_state(void);
+
+/**
+ * @brief Sets Factory reset status
+ */
+void bootloader_common_set_rtc_retain_mem_factory_reset_state(void);
+
 /**
  * @brief Returns rtc_retain_mem
  *
@@ -233,7 +248,7 @@ uint16_t bootloader_common_get_rtc_retain_mem_reboot_counter(void);
  */
 rtc_retain_mem_t* bootloader_common_get_rtc_retain_mem(void);
 
-#endif
+#endif // CONFIG_BOOTLOADER_RESERVE_RTC_MEM
 
 #ifdef __cplusplus
 }

+ 12 - 6
components/bootloader_support/include/esp_image_format.h

@@ -47,7 +47,14 @@ typedef enum {
 typedef struct {
     esp_partition_pos_t partition;  /*!< Partition of application which worked before goes to the deep sleep. */
     uint16_t reboot_counter;        /*!< Reboot counter. Reset only when power is off. */
-    uint16_t reserve;               /*!< Reserve */
+    union {
+        struct {
+            uint8_t factory_reset_state         : 1;  /* True when Factory reset has occurred */
+            uint8_t reserve                     : 7;  /* Reserve */
+        };
+        uint8_t val;
+    } flags;
+    uint8_t reserve;                /*!< Reserve */
 #ifdef CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC
     uint8_t custom[CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC_SIZE]; /*!< Reserve for custom propose */
 #endif
@@ -57,6 +64,8 @@ typedef struct {
 
 ESP_STATIC_ASSERT(offsetof(rtc_retain_mem_t, crc) == sizeof(rtc_retain_mem_t) - sizeof(uint32_t), "CRC field must be the last field of rtc_retain_mem_t structure");
 
+#ifdef CONFIG_BOOTLOADER_RESERVE_RTC_MEM
+
 #ifdef CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC
 ESP_STATIC_ASSERT(CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC_SIZE % 4 == 0, "CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC_SIZE must be a multiple of 4 bytes");
 /* The custom field must be the penultimate field */
@@ -64,19 +73,16 @@ ESP_STATIC_ASSERT(offsetof(rtc_retain_mem_t, custom) == sizeof(rtc_retain_mem_t)
                "custom field in rtc_retain_mem_t structure must be the field before the CRC one");
 #endif
 
-#if defined(CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP) || defined(CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC)
 ESP_STATIC_ASSERT(CONFIG_BOOTLOADER_RESERVE_RTC_SIZE % 4 == 0, "CONFIG_BOOTLOADER_RESERVE_RTC_SIZE must be a multiple of 4 bytes");
-#endif
 
 #ifdef CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC
 #define ESP_BOOTLOADER_RESERVE_RTC (CONFIG_BOOTLOADER_RESERVE_RTC_SIZE + CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC_SIZE)
-#elif defined(CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP)
+#else
 #define ESP_BOOTLOADER_RESERVE_RTC (CONFIG_BOOTLOADER_RESERVE_RTC_SIZE)
 #endif
 
-#if defined(CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP) || defined(CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC)
 ESP_STATIC_ASSERT(sizeof(rtc_retain_mem_t) <= ESP_BOOTLOADER_RESERVE_RTC, "Reserved RTC area must exceed size of rtc_retain_mem_t");
-#endif
+#endif // CONFIG_BOOTLOADER_RESERVE_RTC_MEM
 
 /**
  * @brief Verify an app image.

+ 29 - 6
components/bootloader_support/src/bootloader_common_loader.c

@@ -119,7 +119,7 @@ int bootloader_common_select_otadata(const esp_ota_select_entry_t *two_otadata,
     return active_otadata;
 }
 
-#if defined( CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP ) || defined( CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC )
+#if CONFIG_BOOTLOADER_RESERVE_RTC_MEM
 
 #define RTC_RETAIN_MEM_ADDR (SOC_RTC_DRAM_HIGH - sizeof(rtc_retain_mem_t))
 
@@ -148,7 +148,7 @@ static uint32_t rtc_retain_mem_size(void) {
 #endif
 }
 
-static bool check_rtc_retain_mem(void)
+static bool is_retain_mem_valid(void)
 {
     return esp_rom_crc32_le(UINT32_MAX, (uint8_t*)rtc_retain_mem, rtc_retain_mem_size()) == rtc_retain_mem->crc && rtc_retain_mem->crc != UINT32_MAX;
 }
@@ -165,15 +165,37 @@ NOINLINE_ATTR void bootloader_common_reset_rtc_retain_mem(void)
 
 uint16_t bootloader_common_get_rtc_retain_mem_reboot_counter(void)
 {
-    if (check_rtc_retain_mem()) {
+    if (is_retain_mem_valid()) {
         return rtc_retain_mem->reboot_counter;
     }
     return 0;
 }
 
+void bootloader_common_set_rtc_retain_mem_factory_reset_state(void)
+{
+    if (!is_retain_mem_valid()) {
+        bootloader_common_reset_rtc_retain_mem();
+    }
+    rtc_retain_mem->flags.factory_reset_state = true;
+    update_rtc_retain_mem_crc();
+}
+
+bool bootloader_common_get_rtc_retain_mem_factory_reset_state(void)
+{
+    if (is_retain_mem_valid()) {
+        bool factory_reset_state = rtc_retain_mem->flags.factory_reset_state;
+        if (factory_reset_state == true) {
+            rtc_retain_mem->flags.factory_reset_state = false;
+            update_rtc_retain_mem_crc();
+        }
+        return factory_reset_state;
+    }
+    return false;
+}
+
 esp_partition_pos_t* bootloader_common_get_rtc_retain_mem_partition(void)
 {
-    if (check_rtc_retain_mem()) {
+    if (is_retain_mem_valid()) {
         return &rtc_retain_mem->partition;
     }
     return NULL;
@@ -182,7 +204,7 @@ esp_partition_pos_t* bootloader_common_get_rtc_retain_mem_partition(void)
 void bootloader_common_update_rtc_retain_mem(esp_partition_pos_t* partition, bool reboot_counter)
 {
     if (reboot_counter) {
-        if (!check_rtc_retain_mem()) {
+        if (!is_retain_mem_valid()) {
             bootloader_common_reset_rtc_retain_mem();
         }
         if (++rtc_retain_mem->reboot_counter == 0) {
@@ -204,4 +226,5 @@ rtc_retain_mem_t* bootloader_common_get_rtc_retain_mem(void)
 {
     return rtc_retain_mem;
 }
-#endif // defined( CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP ) || defined( CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC )
+
+#endif // CONFIG_BOOTLOADER_RESERVE_RTC_MEM

+ 5 - 1
components/bootloader_support/src/bootloader_utility.c

@@ -487,10 +487,14 @@ static void set_actual_ota_seq(const bootloader_state_t *bs, int index)
         update_anti_rollback(&bs->ota[index]);
 #endif
     }
-#if defined( CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP ) || defined( CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC )
+#if CONFIG_BOOTLOADER_RESERVE_RTC_MEM
+#ifdef CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP
     esp_partition_pos_t partition = index_to_partition(bs, index);
     bootloader_common_update_rtc_retain_mem(&partition, true);
+#else
+    bootloader_common_update_rtc_retain_mem(NULL, true);
 #endif
+#endif // CONFIG_BOOTLOADER_RESERVE_RTC_MEM
 }
 
 #ifdef CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP

+ 4 - 2
components/esp_system/ld/esp32/memory.ld.in

@@ -27,13 +27,15 @@
 #define CONFIG_BTDM_RESERVE_DRAM 0
 #endif
 
+#if CONFIG_BOOTLOADER_RESERVE_RTC_MEM
 #ifdef CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC
 #define ESP_BOOTLOADER_RESERVE_RTC (CONFIG_BOOTLOADER_RESERVE_RTC_SIZE + CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC_SIZE)
-#elif defined(CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP)
+#else
 #define ESP_BOOTLOADER_RESERVE_RTC (CONFIG_BOOTLOADER_RESERVE_RTC_SIZE)
+#endif // not CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC
 #else
 #define ESP_BOOTLOADER_RESERVE_RTC 0
-#endif
+#endif // not CONFIG_BOOTLOADER_RESERVE_RTC_MEM
 
 #if defined(CONFIG_ESP32_USE_FIXED_STATIC_RAM_SIZE)
 

+ 4 - 2
components/esp_system/ld/esp32c3/memory.ld.in

@@ -15,13 +15,15 @@
 #include "sdkconfig.h"
 #include "ld.common"
 
+#if CONFIG_BOOTLOADER_RESERVE_RTC_MEM
 #ifdef CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC
 #define ESP_BOOTLOADER_RESERVE_RTC (CONFIG_BOOTLOADER_RESERVE_RTC_SIZE + CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC_SIZE)
-#elif defined(CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP)
+#else
 #define ESP_BOOTLOADER_RESERVE_RTC (CONFIG_BOOTLOADER_RESERVE_RTC_SIZE)
+#endif // not CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC
 #else
 #define ESP_BOOTLOADER_RESERVE_RTC 0
-#endif
+#endif // not CONFIG_BOOTLOADER_RESERVE_RTC_MEM
 
 /**
  * physical memory is mapped twice to the vritual address (IRAM and DRAM).

+ 4 - 2
components/esp_system/ld/esp32c6/memory.ld.in

@@ -15,13 +15,15 @@
 #include "sdkconfig.h"
 #include "ld.common"
 
+#if CONFIG_BOOTLOADER_RESERVE_RTC_MEM
 #ifdef CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC
 #define ESP_BOOTLOADER_RESERVE_RTC (CONFIG_BOOTLOADER_RESERVE_RTC_SIZE + CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC_SIZE)
-#elif defined(CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP)
+#else
 #define ESP_BOOTLOADER_RESERVE_RTC (CONFIG_BOOTLOADER_RESERVE_RTC_SIZE)
+#endif // not CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC
 #else
 #define ESP_BOOTLOADER_RESERVE_RTC 0
-#endif
+#endif // not CONFIG_BOOTLOADER_RESERVE_RTC_MEM
 
 /**
  * physical memory is mapped twice to the vritual address (IRAM and DRAM).

+ 4 - 2
components/esp_system/ld/esp32h2/memory.ld.in

@@ -15,13 +15,15 @@
 #include "sdkconfig.h"
 #include "ld.common"
 
+#if CONFIG_BOOTLOADER_RESERVE_RTC_MEM
 #ifdef CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC
 #define ESP_BOOTLOADER_RESERVE_RTC (CONFIG_BOOTLOADER_RESERVE_RTC_SIZE + CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC_SIZE)
-#elif defined(CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP)
+#else
 #define ESP_BOOTLOADER_RESERVE_RTC (CONFIG_BOOTLOADER_RESERVE_RTC_SIZE)
+#endif // not CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC
 #else
 #define ESP_BOOTLOADER_RESERVE_RTC 0
-#endif
+#endif // not CONFIG_BOOTLOADER_RESERVE_RTC_MEM
 
 /**
  * physical memory is mapped twice to the vritual address (IRAM and DRAM).

+ 4 - 2
components/esp_system/ld/esp32h4/memory.ld.in

@@ -15,13 +15,15 @@
 #include "sdkconfig.h"
 #include "ld.common"
 
+#if CONFIG_BOOTLOADER_RESERVE_RTC_MEM
 #ifdef CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC
 #define ESP_BOOTLOADER_RESERVE_RTC (CONFIG_BOOTLOADER_RESERVE_RTC_SIZE + CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC_SIZE)
-#elif defined(CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP)
+#else
 #define ESP_BOOTLOADER_RESERVE_RTC (CONFIG_BOOTLOADER_RESERVE_RTC_SIZE)
+#endif // not CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC
 #else
 #define ESP_BOOTLOADER_RESERVE_RTC 0
-#endif
+#endif // not CONFIG_BOOTLOADER_RESERVE_RTC_MEM
 
 #define SRAM_IRAM_START     0x4037C000
 #define SRAM_DRAM_START     0x3FC7C000

+ 4 - 2
components/esp_system/ld/esp32s2/memory.ld.in

@@ -15,13 +15,15 @@
 #include "sdkconfig.h"
 #include "ld.common"
 
+#if CONFIG_BOOTLOADER_RESERVE_RTC_MEM
 #ifdef CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC
 #define ESP_BOOTLOADER_RESERVE_RTC (CONFIG_BOOTLOADER_RESERVE_RTC_SIZE + CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC_SIZE)
-#elif defined(CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP)
+#else
 #define ESP_BOOTLOADER_RESERVE_RTC (CONFIG_BOOTLOADER_RESERVE_RTC_SIZE)
+#endif // not CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC
 #else
 #define ESP_BOOTLOADER_RESERVE_RTC 0
-#endif
+#endif // not CONFIG_BOOTLOADER_RESERVE_RTC_MEM
 
 #ifdef CONFIG_ESP32S2_INSTRUCTION_CACHE_8KB
 #define CONFIG_ESP32S2_INSTRUCTION_CACHE_SIZE 0x2000

+ 4 - 2
components/esp_system/ld/esp32s3/memory.ld.in

@@ -14,13 +14,15 @@
 #include "sdkconfig.h"
 #include "ld.common"
 
+#if CONFIG_BOOTLOADER_RESERVE_RTC_MEM
 #ifdef CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC
 #define ESP_BOOTLOADER_RESERVE_RTC (CONFIG_BOOTLOADER_RESERVE_RTC_SIZE + CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC_SIZE)
-#elif defined(CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP)
+#else
 #define ESP_BOOTLOADER_RESERVE_RTC (CONFIG_BOOTLOADER_RESERVE_RTC_SIZE)
+#endif // not CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC
 #else
 #define ESP_BOOTLOADER_RESERVE_RTC 0
-#endif
+#endif // not CONFIG_BOOTLOADER_RESERVE_RTC_MEM
 
 /*
  * 40370000 <- IRAM/Icache -> 40378000 <- D/IRAM (I) -> 403E0000

+ 8 - 0
docs/en/api-guides/bootloader.rst

@@ -86,6 +86,14 @@ In addition, the following configuration options control the reset condition:
 
 - :ref:`CONFIG_BOOTLOADER_FACTORY_RESET_PIN_LEVEL` - configure whether a factory reset should trigger on a high or low level of the GPIO. If the GPIO has an internal pullup then this is enabled before the pin is sampled, consult the {IDF_TARGET_NAME} datasheet for details on pin internal pullups.
 
+.. only:: SOC_RTC_FAST_MEM_SUPPORTED
+
+    Sometimes an application needs to know if the Factory Reset has occurred. For this purpose, there is a function :cpp:func:`bootloader_common_get_rtc_retain_mem_factory_reset_state`, which returns its status, after reading the status is reset to false. This feature reserves some RTC FAST memory (the same size as the :ref:`CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP` feature).
+
+.. only:: not SOC_RTC_FAST_MEM_SUPPORTED
+
+    Sometimes an application needs to know if the Factory Reset has occurred. The {IDF_TARGET_NAME} chip does not have RTC FAST memory, so there is no API to detect it. Instead, there is a workaround: you need an NVS partition that will be erased by bootloader while Factory Reset (add this partition to :ref:`CONFIG_BOOTLOADER_DATA_FACTORY_RESET`). In this NVS partition, create a "factory_reset_state" token that will be increased in the application. If the "factory_reset_state" is 0 then the factory reset has occurred.
+
 .. _bootloader_boot_from_test_firmware:
 
 Boot from Test Firmware