]> granicus.if.org Git - esp-idf/commitdiff
bootloader: Add support of anti-rollback
authorKonstantin Kondrashov <konstantin@espressif.com>
Wed, 13 Feb 2019 09:32:23 +0000 (17:32 +0800)
committerKonstantin Kondrashov <konstantin@espressif.com>
Thu, 14 Feb 2019 10:51:43 +0000 (18:51 +0800)
Added:
* set a secure version in app/bootloader.
* description anti-rollback to ota part
* emulate the secure_version write and read operations
* efuse_em partition.
* a description about a rollback for native_ota_example.

Closes: TW26335
22 files changed:
components/app_update/esp_ota_ops.c
components/app_update/include/esp_ota_ops.h
components/app_update/test/test_switch_ota.c
components/bootloader/Kconfig.projbuild
components/bootloader_support/include/bootloader_common.h
components/bootloader_support/include/esp_efuse.h
components/bootloader_support/src/bootloader_common.c
components/bootloader_support/src/bootloader_utility.c
components/bootloader_support/src/efuse.c
components/esp32/cpu_start.c
components/esp32/esp_err_to_name.c
components/esp32/include/esp_flash_data_types.h
components/partition_table/CMakeLists.txt
components/partition_table/Makefile.projbuild
components/partition_table/gen_esp32part.py
components/partition_table/project_include.cmake
components/spi_flash/include/esp_partition.h
docs/en/api-reference/system/ota.rst
examples/system/ota/README.md
examples/system/ota/native_ota_example/main/Kconfig.projbuild
examples/system/ota/native_ota_example/main/native_ota_example.c
examples/system/ota/native_ota_example/version.txt [new file with mode: 0644]

index 22b13f53019190a2fdc002632b04342b5d1ad6d3..02eab8eb6bb81a128e9a8291e4d848201debc40b 100644 (file)
@@ -40,6 +40,8 @@
 #include "bootloader_common.h"
 #include "sys/param.h"
 #include "esp_system.h"
+#include "esp_efuse.h"
+
 
 #define SUB_TYPE_ID(i) (i & 0x0F) 
 
@@ -144,10 +146,21 @@ esp_err_t esp_ota_begin(const esp_partition_t *partition, size_t image_size, esp
         return ESP_ERR_INVALID_ARG;
     }
 
-    if (partition == esp_ota_get_running_partition()) {
+    const esp_partition_t* running_partition = esp_ota_get_running_partition();
+    if (partition == running_partition) {
         return ESP_ERR_OTA_PARTITION_CONFLICT;
     }
 
+#ifdef CONFIG_APP_ROLLBACK_ENABLE
+    esp_ota_img_states_t ota_state_running_part;
+    if (esp_ota_get_state_partition(running_partition, &ota_state_running_part) == ESP_OK) {
+        if (ota_state_running_part == ESP_OTA_IMG_PENDING_VERIFY) {
+            ESP_LOGE(TAG, "Running app has not confirmed state (ESP_OTA_IMG_PENDING_VERIFY)");
+            return ESP_ERR_OTA_ROLLBACK_INVALID_STATE;
+        }
+    }
+#endif
+
     // If input image size is 0 or OTA_SIZE_UNKNOWN, erase entire partition
     if ((image_size == 0) || (image_size == OTA_SIZE_UNKNOWN)) {
         ret = esp_partition_erase_range(partition, 0, partition->size);
@@ -388,6 +401,22 @@ esp_err_t esp_ota_set_boot_partition(const esp_partition_t *partition)
                 return ESP_ERR_NOT_FOUND;
             }
         } else {
+#ifdef CONFIG_APP_ANTI_ROLLBACK
+            esp_app_desc_t partition_app_desc;
+            esp_err_t err = esp_ota_get_partition_description(partition, &partition_app_desc);
+            if (err != ESP_OK) {
+                return err;
+            }
+
+            if (esp_efuse_check_secure_version(partition_app_desc.secure_version) == false) {
+                ESP_LOGE(TAG, "This a new partition can not be booted due to a secure version is lower than stored in efuse. Partition will be erased.");
+                esp_err_t err = esp_partition_erase_range(partition, 0, partition->size);
+                if (err != ESP_OK) {
+                    return err;
+                }
+                return ESP_ERR_OTA_SMALL_SEC_VER;
+            }
+#endif
             return esp_rewrite_ota_data(partition->subtype);
         }
     } else {
@@ -560,6 +589,69 @@ esp_err_t esp_ota_get_partition_description(const esp_partition_t *partition, es
     return ESP_OK;
 }
 
+#ifdef CONFIG_APP_ANTI_ROLLBACK
+static esp_err_t esp_ota_set_anti_rollback(void) {
+    const esp_app_desc_t *app_desc = esp_ota_get_app_description();
+    return esp_efuse_update_secure_version(app_desc->secure_version);
+}
+#endif
+
+// Checks applications on the slots which can be booted in case of rollback.
+// Returns true if the slots have at least one app (except the running app).
+bool esp_ota_check_rollback_is_possible(void)
+{
+    esp_ota_select_entry_t otadata[2];
+    if (read_otadata(otadata) == NULL) {
+        return false;
+    }
+
+    int ota_app_count = get_ota_partition_count();
+    if (ota_app_count == 0) {
+        return false;
+    }
+
+    bool valid_otadata[2];
+    valid_otadata[0] = bootloader_common_ota_select_valid(&otadata[0]);
+    valid_otadata[1] = bootloader_common_ota_select_valid(&otadata[1]);
+
+    int active_ota = bootloader_common_select_otadata(otadata, valid_otadata, true);
+    if (active_ota == -1) {
+        return false;
+    }
+    int last_active_ota = (~active_ota)&1;
+
+    const esp_partition_t *partition = NULL;
+#ifndef CONFIG_APP_ANTI_ROLLBACK
+    if (valid_otadata[last_active_ota] == false) {
+        partition = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_FACTORY, NULL);
+        if (partition != NULL) {
+            if(image_validate(partition, ESP_IMAGE_VERIFY_SILENT) == ESP_OK) {
+                return true;
+            }
+        }
+    }
+#endif
+
+    if (valid_otadata[last_active_ota] == true) {
+        int slot = (otadata[last_active_ota].ota_seq - 1) % ota_app_count;
+        partition = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_OTA_MIN + slot, NULL);
+        if (partition != NULL) {
+            if(image_validate(partition, ESP_IMAGE_VERIFY_SILENT) == ESP_OK) {
+#ifdef CONFIG_APP_ANTI_ROLLBACK
+                esp_app_desc_t app_desc;
+                if (esp_ota_get_partition_description(partition, &app_desc) == ESP_OK &&
+                    esp_efuse_check_secure_version(app_desc.secure_version) == true) {
+                    return true;
+                }
+#else
+                return true;
+#endif
+            }
+        }
+    }
+    return false;
+}
+
 // if valid == false - will done rollback with reboot. After reboot will boot previous OTA[x] or Factory partition.
 // if valid == true  - it confirm that current OTA[x] is workable. Reboot will not happen.
 static esp_err_t esp_ota_current_ota_is_workable(bool valid)
@@ -575,8 +667,18 @@ static esp_err_t esp_ota_current_ota_is_workable(bool valid)
         if (valid == true && otadata[active_otadata].ota_state != ESP_OTA_IMG_VALID) {
             otadata[active_otadata].ota_state = ESP_OTA_IMG_VALID;
             ESP_LOGD(TAG, "OTA[current] partition is marked as VALID");
-            return rewrite_ota_seq(otadata, otadata[active_otadata].ota_seq, active_otadata, otadata_partition);
+            esp_err_t err = rewrite_ota_seq(otadata, otadata[active_otadata].ota_seq, active_otadata, otadata_partition);
+#ifdef CONFIG_APP_ANTI_ROLLBACK
+            if (err == ESP_OK) {
+                return esp_ota_set_anti_rollback();
+            }
+#endif
+            return err;
         } else if (valid == false) {
+            if (esp_ota_check_rollback_is_possible() == false) {
+                ESP_LOGE(TAG, "Rollback is not possible, do not have any suitable apps in slots");
+                return ESP_ERR_OTA_ROLLBACK_FAILED;
+            }
             ESP_LOGD(TAG, "OTA[current] partition is marked as INVALID");
             otadata[active_otadata].ota_state = ESP_OTA_IMG_INVALID;
             esp_err_t err = rewrite_ota_seq(otadata, otadata[active_otadata].ota_seq, active_otadata, otadata_partition);
@@ -612,26 +714,11 @@ static bool check_invalid_otadata (const esp_ota_select_entry_t *s) {
 
 static int get_last_invalid_otadata(const esp_ota_select_entry_t *two_otadata)
 {
-    int num_invalid_otadata = -1;
 
     bool invalid_otadata[2];
     invalid_otadata[0] = check_invalid_otadata(&two_otadata[0]);
     invalid_otadata[1] = check_invalid_otadata(&two_otadata[1]);
-    if (invalid_otadata[0] == true && invalid_otadata[1] == true) {
-        if (MIN(two_otadata[0].ota_seq, two_otadata[1].ota_seq) == two_otadata[0].ota_seq) {
-            num_invalid_otadata = 0;
-        } else {
-            num_invalid_otadata = 1;
-        }
-    } else {
-        for (int i = 0; i < 2; ++i) {
-            if(invalid_otadata[i]) {
-                num_invalid_otadata = i;
-                break;
-            }
-        }
-    }
-
+    int num_invalid_otadata = bootloader_common_select_otadata(two_otadata, invalid_otadata, false);
     ESP_LOGD(TAG, "Invalid otadata[%d]", num_invalid_otadata);
     return num_invalid_otadata;
 }
index ff07cec5a0bae3836b3cc85bf16a43506ceabfdc..94a34b4f98ded4205cda40f54648770f2b689b6f 100644 (file)
@@ -34,6 +34,10 @@ extern "C"
 #define ESP_ERR_OTA_PARTITION_CONFLICT           (ESP_ERR_OTA_BASE + 0x01)  /*!< Error if request was to write or erase the current running partition */
 #define ESP_ERR_OTA_SELECT_INFO_INVALID          (ESP_ERR_OTA_BASE + 0x02)  /*!< Error if OTA data partition contains invalid content */
 #define ESP_ERR_OTA_VALIDATE_FAILED              (ESP_ERR_OTA_BASE + 0x03)  /*!< Error if OTA app image is invalid */
+#define ESP_ERR_OTA_SMALL_SEC_VER                (ESP_ERR_OTA_BASE + 0x04)  /*!< Error if the firmware has a secure version less than the running firmware. */
+#define ESP_ERR_OTA_ROLLBACK_FAILED              (ESP_ERR_OTA_BASE + 0x05)  /*!< Error if flash does not have valid firmware in passive partition and hence rollback is not possible */
+#define ESP_ERR_OTA_ROLLBACK_INVALID_STATE       (ESP_ERR_OTA_BASE + 0x06)  /*!< Error if current active firmware is still marked in pending validation state (ESP_OTA_IMG_PENDING_VERIFY), essentially first boot of firmware image post upgrade and hence firmware upgrade is not possible */
+
 
 /**
  * @brief Opaque handle for an application OTA update
@@ -62,6 +66,10 @@ const esp_app_desc_t *esp_ota_get_app_description(void);
  * On success, this function allocates memory that remains in use
  * until esp_ota_end() is called with the returned handle.
  *
+ * Note: If the rollback option is enabled and the running application has the ESP_OTA_IMG_PENDING_VERIFY state then
+ * it will lead to the ESP_ERR_OTA_ROLLBACK_INVALID_STATE error. Confirm the running app before to run download a new app,
+ * use esp_ota_mark_app_valid_cancel_rollback() function for it (this should be done as early as possible when you first download a new application).
+ *
  * @param partition Pointer to info for partition which will receive the OTA update. Required.
  * @param image_size Size of new OTA app image. Partition will be erased in order to receive this size of image. If 0 or OTA_SIZE_UNKNOWN, the entire partition is erased.
  * @param out_handle On success, returns a handle which should be used for subsequent esp_ota_write() and esp_ota_end() calls.
@@ -75,6 +83,7 @@ const esp_app_desc_t *esp_ota_get_app_description(void);
  *    - ESP_ERR_OTA_SELECT_INFO_INVALID: The OTA data partition contains invalid data.
  *    - ESP_ERR_INVALID_SIZE: Partition doesn't fit in configured flash size.
  *    - ESP_ERR_FLASH_OP_TIMEOUT or ESP_ERR_FLASH_OP_FAIL: Flash write failed.
+ *    - ESP_ERR_OTA_ROLLBACK_INVALID_STATE: If the running app has not confirmed state. Before performing an update, the application must be valid.
  */
 esp_err_t esp_ota_begin(const esp_partition_t* partition, size_t image_size, esp_ota_handle_t* out_handle);
 
@@ -208,8 +217,11 @@ esp_err_t esp_ota_mark_app_valid_cancel_rollback();
  * @brief This function is called to roll back to the previously workable app with reboot.
  *
  * If rollback is successful then device will reset else API will return with error code.
+ * Checks applications on a flash drive that can be booted in case of rollback.
+ * If the flash does not have at least one app (except the running app) then rollback is not possible.
  * @return
  *  - ESP_FAIL: if not successful.
+ *  - ESP_ERR_OTA_ROLLBACK_FAILED: The rollback is not possible due to flash does not have any apps.
  */
 esp_err_t esp_ota_mark_app_invalid_rollback_and_reboot();
 
@@ -242,6 +254,18 @@ esp_err_t esp_ota_get_state_partition(const esp_partition_t *partition, esp_ota_
  */
 esp_err_t esp_ota_erase_last_boot_app_partition(void);
 
+/**
+ * @brief Checks applications on the slots which can be booted in case of rollback.
+ *
+ * These applications should be valid (marked in otadata as not UNDEFINED, INVALID or ABORTED and crc is good) and be able booted,
+ * and secure_version of app >= secure_version of efuse (if anti-rollback is enabled).
+ *
+ * @return
+ *        - True: Returns true if the slots have at least one app (except the running app).
+ *        - False: The rollback is not possible.
+ */
+bool esp_ota_check_rollback_is_possible(void);
+
 #ifdef __cplusplus
 }
 #endif
index a3cb113c6a71769e5deb1416a45612f2dc542ef1..f2adb2f799b9521a2081d77b6438cd4c7f01eca7 100644 (file)
@@ -237,6 +237,12 @@ static void reset_output_pin(uint32_t num_pin)
 }
 #endif
 
+static void mark_app_valid(void)
+{
+#ifdef CONFIG_APP_ROLLBACK_ENABLE
+    TEST_ESP_OK(esp_ota_mark_app_valid_cancel_rollback());
+#endif
+}
 
 /* @brief Checks and prepares the partition so that the factory app is launched after that.
  */
@@ -262,15 +268,18 @@ static void test_flow1(void)
         case 3:
             ESP_LOGI(TAG, "OTA0");
             TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_OTA_0, cur_app->subtype);
+            mark_app_valid();
             copy_current_app_to_next_part_and_reboot(cur_app);
             break;
         case 4:
             ESP_LOGI(TAG, "OTA1");
             TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_OTA_1, cur_app->subtype);
+            mark_app_valid();
             copy_current_app_to_next_part_and_reboot(cur_app);
             break;
         case 5:
             ESP_LOGI(TAG, "OTA0");
+            mark_app_valid();
             TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_OTA_0, cur_app->subtype);
             erase_ota_data();
             break;
@@ -302,6 +311,7 @@ static void test_flow2(void)
         case 3:
             ESP_LOGI(TAG, "OTA0");
             TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_OTA_0, cur_app->subtype);
+            mark_app_valid();
             copy_current_app_to_next_part(cur_app, get_next_update_partition());
             corrupt_ota_data(CORR_CRC_1_SECTOR_OTA_DATA);
             reboot_as_deep_sleep();
@@ -338,11 +348,13 @@ static void test_flow3(void)
         case 3:
             ESP_LOGI(TAG, "OTA0");
             TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_OTA_0, cur_app->subtype);
+            mark_app_valid();
             copy_current_app_to_next_part_and_reboot(cur_app);
             break;
         case 4:
             ESP_LOGI(TAG, "OTA1");
             TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_OTA_1, cur_app->subtype);
+            mark_app_valid();
             copy_current_app_to_next_part(cur_app, get_next_update_partition());
             corrupt_ota_data(CORR_CRC_2_SECTOR_OTA_DATA);
             reboot_as_deep_sleep();
@@ -394,7 +406,7 @@ static void test_flow4(void)
         case 3:
             ESP_LOGI(TAG, "OTA0");
             TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_OTA_0, cur_app->subtype);
-
+            mark_app_valid();
             TEST_ESP_OK(nvs_flash_init());
             TEST_ESP_OK(nvs_open(STORAGE_NAMESPACE, NVS_READWRITE, &handle));
             TEST_ESP_OK(nvs_get_i32(handle, "boot_count", &boot_count_nvs));
@@ -523,7 +535,7 @@ static void test_rollback1(void)
 #else
             TEST_ASSERT_EQUAL(ESP_OTA_IMG_PENDING_VERIFY, ota_state);
 #endif
-            esp_ota_mark_app_valid_cancel_rollback();
+            TEST_ESP_OK(esp_ota_mark_app_valid_cancel_rollback());
             TEST_ESP_OK(esp_ota_get_state_partition(cur_app, &ota_state));
             TEST_ASSERT_EQUAL(ESP_OTA_IMG_VALID, ota_state);
             reboot_as_deep_sleep();
@@ -602,7 +614,7 @@ static void test_rollback2(void)
 #else
             TEST_ASSERT_EQUAL(ESP_OTA_IMG_PENDING_VERIFY, ota_state);
 #endif
-            esp_ota_mark_app_valid_cancel_rollback();
+            TEST_ESP_OK(esp_ota_mark_app_valid_cancel_rollback());
             TEST_ASSERT_NULL(esp_ota_get_last_invalid_partition());
             TEST_ESP_OK(esp_ota_get_state_partition(cur_app, &ota_state));
             TEST_ASSERT_EQUAL(ESP_OTA_IMG_VALID, ota_state);
@@ -683,6 +695,7 @@ static void test_erase_last_app_flow(void)
         case 3:
             ESP_LOGI(TAG, "OTA0");
             TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_OTA_0, cur_app->subtype);
+            mark_app_valid();
             app_update();
             reboot_as_deep_sleep();
             break;
index bdf84fa7b9a1a4302632744011b5b6db023d6f55..a51ff6f0254fbe9222ac4819c0d38cfbfd2a18ea 100644 (file)
@@ -173,6 +173,51 @@ menu "Bootloader config"
             If the app is working, then it is marked as valid. Otherwise, it is marked as not valid and rolls back to
             the previous working app. A reboot is performed, and the app is booted before the software update.
             Note: If during the first boot a new app the power goes out or the WDT works, then roll back will happen.
+            Rollback is possible only between the apps with the same security versions.
+
+    config APP_ANTI_ROLLBACK
+        bool "Enable app anti-rollback support"
+        depends on APP_ROLLBACK_ENABLE
+        default n
+        help
+            This option prevents rollback to previous firmware/application image with lower security version.
+
+    config APP_SECURE_VERSION
+        int "eFuse secure version of app"
+        depends on APP_ANTI_ROLLBACK
+        default 0
+        help
+            The secure version is the sequence number stored in the header of each firmware.
+            The security version is set in the bootloader, version is recorded in the eFuse field
+            as the number of set ones. The allocated number of bits in the efuse field
+            for storing the security version is limited (see APP_SECURE_VERSION_SIZE_EFUSE_FIELD option).
+
+            Bootloader: When bootloader selects an app to boot, an app is selected that has
+            a security version greater or equal that recorded in eFuse field.
+            The app is booted with a higher (or equal) secure version.
+
+            The security version is worth increasing if in previous versions there is
+            a significant vulnerability and their use is not acceptable.
+
+            Your partition table should has a scheme with ota_0 + ota_1 (without factory).
+
+    config APP_SECURE_VERSION_SIZE_EFUSE_FIELD
+        int "Size of the efuse secure version field"
+        depends on APP_ANTI_ROLLBACK
+        range 1 32
+        default 32
+        help
+            The size of the efuse secure version field. Its length is limited to 32 bits.
+            This determines how many times the security version can be increased.
+
+    config EFUSE_SECURE_VERSION_EMULATE
+        bool "Emulate operations with efuse secure version(only test)"
+        default n
+        depends on APP_ANTI_ROLLBACK
+        help
+            This option allow emulate read/write operations with efuse secure version.
+            It allow to test anti-rollback implemention without permanent write eFuse bits.
+            In partition table should be exist this partition `emul_efuse, data, 5, , 0x2000`.
 
 endmenu  # Bootloader
 
index f57b4b8a9af53c984dfdbb8ae1558c64e5c179dc..d18b97ceb3e6b43fa9c14b4365de125e56d29c56 100644 (file)
@@ -111,6 +111,18 @@ esp_err_t bootloader_common_get_sha256_of_partition(uint32_t address, uint32_t s
  */
 int bootloader_common_get_active_otadata(esp_ota_select_entry_t *two_otadata);
 
+/**
+ * @brief Returns the number of active otadata.
+ *
+ * @param[in] two_otadata       Pointer on array from two otadata structures.
+ * @param[in] valid_two_otadata Pointer on array from two bools. True means select.
+ * @param[in] max               True - will select the maximum ota_seq number, otherwise the minimum.
+ *
+ * @return The number of active otadata (0 or 1).
+ *        - -1: If it does not have active otadata.
+ */
+int bootloader_common_select_otadata(const esp_ota_select_entry_t *two_otadata, bool *valid_two_otadata, bool max);
+
 /**
  * @brief Returns esp_app_desc structure for app partition. This structure includes app version.
  * 
index c094a6ab445a461244dd0d2c399f188c8b75ff35..3f446cb939d873c0732a3aa9dd60cdc6ffd1c3c5 100644 (file)
@@ -16,6 +16,7 @@
 
 #include "soc/efuse_reg.h"
 #include "esp_err.h"
+#include "stdbool.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -91,6 +92,42 @@ esp_err_t esp_efuse_apply_34_encoding(const uint8_t *in_bytes, uint32_t *out_wor
  */
 void esp_efuse_write_random_key(uint32_t blk_wdata0_reg);
 
+/* @brief Return secure_version from efuse field.
+ * @return Secure version from efuse field
+ */
+uint32_t esp_efuse_read_secure_version();
+
+/* @brief Check secure_version from app and secure_version and from efuse field.
+ *
+ * @param secure_version Secure version from app.
+ * @return
+ *          - True: If version of app is equal or more then secure_version from efuse.
+ */
+bool esp_efuse_check_secure_version(uint32_t secure_version);
+
+/* @brief Write efuse field by secure_version value.
+ *
+ * Update the secure_version value is available if the coding scheme is None.
+ * Note: Do not use this function in your applications. This function is called as part of the other API.
+ *
+ * @param[in] secure_version Secure version from app.
+ * @return
+ *          - ESP_OK: Successful.
+ *          - ESP_FAIL: secure version of app cannot be set to efuse field.
+ *          - ESP_ERR_NOT_SUPPORTED: Anti rollback is not supported with the 3/4 and Repeat coding scheme.
+ */
+esp_err_t esp_efuse_update_secure_version(uint32_t secure_version);
+
+/* @brief Initializes variables: offset and size to simulate the work of an eFuse.
+ *
+ * Note: To simulate the work of an eFuse need to set CONFIG_EFUSE_SECURE_VERSION_EMULATE option
+ * and to add in the partition.csv file a line `efuse_em, data, efuse,   ,   0x2000,`.
+ *
+ * @param[in] offset The starting address of the partition where the eFuse data will be located.
+ * @param[in] size The size of the partition.
+ */
+void esp_efuse_init(uint32_t offset, uint32_t size);
+
 #ifdef __cplusplus
 }
 #endif
index 7486164164b5f393c27c6527675d925bcbcb59cc..29e591d1af12523dff89af4b48391e3132c075bc 100644 (file)
@@ -199,15 +199,15 @@ esp_err_t bootloader_common_get_sha256_of_partition (uint32_t address, uint32_t
     return ESP_OK;
 }
 
-int bootloader_common_get_active_otadata(esp_ota_select_entry_t *two_otadata)
+int bootloader_common_select_otadata(const esp_ota_select_entry_t *two_otadata, bool *valid_two_otadata, bool max)
 {
+    if (two_otadata == NULL || valid_two_otadata == NULL) {
+        return -1;
+    }
     int active_otadata = -1;
-
-    bool valid_otadata[2];
-    valid_otadata[0] = bootloader_common_ota_select_valid(&two_otadata[0]);
-    valid_otadata[1] = bootloader_common_ota_select_valid(&two_otadata[1]);
-    if (valid_otadata[0] && valid_otadata[1]) {
-        if (MAX(two_otadata[0].ota_seq, two_otadata[1].ota_seq) == two_otadata[0].ota_seq) {
+    if (valid_two_otadata[0] && valid_two_otadata[1]) {
+        int condition = (max == true) ? MAX(two_otadata[0].ota_seq, two_otadata[1].ota_seq) : MIN(two_otadata[0].ota_seq, two_otadata[1].ota_seq);
+        if (condition == two_otadata[0].ota_seq) {
             active_otadata = 0;
         } else {
             active_otadata = 1;
@@ -215,7 +215,7 @@ int bootloader_common_get_active_otadata(esp_ota_select_entry_t *two_otadata)
         ESP_LOGD(TAG, "Both OTA copies are valid");
     } else {
         for (int i = 0; i < 2; ++i) {
-            if (valid_otadata[i]) {
+            if (valid_two_otadata[i]) {
                 active_otadata = i;
                 ESP_LOGD(TAG, "Only otadata[%d] is valid", i);
                 break;
@@ -225,6 +225,17 @@ int bootloader_common_get_active_otadata(esp_ota_select_entry_t *two_otadata)
     return active_otadata;
 }
 
+int bootloader_common_get_active_otadata(esp_ota_select_entry_t *two_otadata)
+{
+    if (two_otadata == NULL) {
+        return -1;
+    }
+    bool valid_two_otadata[2];
+    valid_two_otadata[0] = bootloader_common_ota_select_valid(&two_otadata[0]);
+    valid_two_otadata[1] = bootloader_common_ota_select_valid(&two_otadata[1]);
+    return bootloader_common_select_otadata(two_otadata, valid_two_otadata, true);
+}
+
 esp_err_t bootloader_common_get_partition_description(const esp_partition_pos_t *partition, esp_app_desc_t *app_desc)
 {
     if (partition == NULL || app_desc == NULL || partition->offset == 0) {
index 80cac89fd66c5d29b420cfe928b13a10bde1c623..720294fa48a259ce66e853f61c6d1933a28c8f27 100644 (file)
@@ -51,6 +51,7 @@
 #include "bootloader_common.h"
 #include "bootloader_utility.h"
 #include "bootloader_sha.h"
+#include "esp_efuse.h"
 
 static const char* TAG = "boot";
 
@@ -166,6 +167,12 @@ bool bootloader_utility_load_partition_table(bootloader_state_t* bs)
             case PART_SUBTYPE_DATA_NVS_KEYS:
                 partition_usage = "NVS keys";
                 break;
+            case PART_SUBTYPE_DATA_EFUSE_EM:
+                partition_usage = "efuse";
+#ifdef CONFIG_EFUSE_SECURE_VERSION_EMULATE
+                esp_efuse_init(partition->pos.offset, partition->pos.size);
+#endif
+                break;
             default:
                 partition_usage = "Unknown data";
                 break;
@@ -234,6 +241,54 @@ static esp_err_t write_otadata(esp_ota_select_entry_t *otadata, uint32_t offset,
     return err;
 }
 
+static bool check_anti_rollback(const esp_partition_pos_t *partition)
+{
+#ifdef CONFIG_APP_ANTI_ROLLBACK
+    esp_app_desc_t app_desc;
+    esp_err_t err = bootloader_common_get_partition_description(partition, &app_desc);
+    return err == ESP_OK && esp_efuse_check_secure_version(app_desc.secure_version) == true;
+#else
+    return true;
+#endif
+}
+
+#ifdef CONFIG_APP_ANTI_ROLLBACK
+static void update_anti_rollback(const esp_partition_pos_t *partition)
+{
+    esp_app_desc_t app_desc;
+    esp_err_t err = bootloader_common_get_partition_description(partition, &app_desc);
+    if (err == ESP_OK) {
+        esp_efuse_update_secure_version(app_desc.secure_version);
+    }
+}
+
+static int get_active_otadata_with_check_anti_rollback(const bootloader_state_t *bs, esp_ota_select_entry_t *two_otadata)
+{
+    uint32_t ota_seq;
+    uint32_t ota_slot;
+    bool valid_otadata[2];
+
+    valid_otadata[0] = bootloader_common_ota_select_valid(&two_otadata[0]);
+    valid_otadata[1] = bootloader_common_ota_select_valid(&two_otadata[1]);
+
+    bool sec_ver_valid_otadata[2] = { 0 };
+    for (int i = 0; i < 2; ++i) {
+        if (valid_otadata[i] == true) {
+            ota_seq = two_otadata[i].ota_seq - 1; // Raw OTA sequence number. May be more than # of OTA slots
+            ota_slot = ota_seq % bs->app_count; // Actual OTA partition selection
+            if (check_anti_rollback(&bs->ota[ota_slot]) == false) {
+                // invalid. This otadata[i] will not be selected as active.
+                ESP_LOGD(TAG, "OTA slot %d has an app with secure_version, this version is smaller than in the device. This OTA slot will not be selected.", ota_slot);
+            } else {
+                sec_ver_valid_otadata[i] = true;
+            }
+        }
+    }
+
+    return bootloader_common_select_otadata(two_otadata, sec_ver_valid_otadata, true);
+}
+#endif
+
 int bootloader_utility_get_selected_boot_partition(const bootloader_state_t *bs)
 {
     esp_ota_select_entry_t otadata[2];
@@ -250,6 +305,7 @@ int bootloader_utility_get_selected_boot_partition(const bootloader_state_t *bs)
 
     ESP_LOGD(TAG, "otadata[0]: sequence values 0x%08x", otadata[0].ota_seq);
     ESP_LOGD(TAG, "otadata[1]: sequence values 0x%08x", otadata[1].ota_seq);
+
 #ifdef CONFIG_APP_ROLLBACK_ENABLE
     bool write_encrypted = esp_flash_encryption_enabled();
     for (int i = 0; i < 2; ++i) {
@@ -260,6 +316,8 @@ int bootloader_utility_get_selected_boot_partition(const bootloader_state_t *bs)
         }
     }
 #endif
+
+#ifndef CONFIG_APP_ANTI_ROLLBACK
     if ((bootloader_common_ota_select_invalid(&otadata[0]) &&
          bootloader_common_ota_select_invalid(&otadata[1])) ||
          bs->app_count == 0) {
@@ -280,18 +338,38 @@ int bootloader_utility_get_selected_boot_partition(const bootloader_state_t *bs)
         }
     } else {
         int active_otadata = bootloader_common_get_active_otadata(otadata);
+#else
+    ESP_LOGI(TAG, "Enabled a check secure version of app for anti rollback");
+    ESP_LOGI(TAG, "Secure version (from eFuse) = %d", esp_efuse_read_secure_version());
+    // When CONFIG_APP_ANTI_ROLLBACK is enabled factory partition should not be in partition table, only two ota_app are there.
+    if ((otadata[0].ota_seq == UINT32_MAX || otadata[0].crc != bootloader_common_ota_select_crc(&otadata[0])) &&
+        (otadata[1].ota_seq == UINT32_MAX || otadata[1].crc != bootloader_common_ota_select_crc(&otadata[1]))) {
+        ESP_LOGI(TAG, "otadata[0..1] in initial state");
+        // both otadata are initial(0xFFFFFFFF) or incorrect crc.
+        // will set correct ota_seq.
+        ota_has_initial_contents = true;
+    } else {
+        int active_otadata = get_active_otadata_with_check_anti_rollback(bs, otadata);
+#endif
         if (active_otadata != -1) {
             ESP_LOGD(TAG, "Active otadata[%d]", active_otadata);
+            uint32_t ota_seq = otadata[active_otadata].ota_seq - 1; // Raw OTA sequence number. May be more than # of OTA slots
+            boot_index = ota_seq % bs->app_count; // Actual OTA partition selection
+            ESP_LOGD(TAG, "Mapping seq %d -> OTA slot %d", ota_seq, boot_index);
 #ifdef CONFIG_APP_ROLLBACK_ENABLE
             if (otadata[active_otadata].ota_state == ESP_OTA_IMG_NEW) {
                 ESP_LOGD(TAG, "otadata[%d] is selected as new and marked PENDING_VERIFY state", active_otadata);
                 otadata[active_otadata].ota_state = ESP_OTA_IMG_PENDING_VERIFY;
                 write_otadata(&otadata[active_otadata], bs->ota_info.offset + FLASH_SECTOR_SIZE * active_otadata, write_encrypted);
             }
-#endif
-            uint32_t ota_seq = otadata[active_otadata].ota_seq - 1; // Raw OTA sequence number. May be more than # of OTA slots
-            boot_index = ota_seq % bs->app_count; // Actual OTA partition selection
-            ESP_LOGD(TAG, "Mapping seq %d -> OTA slot %d", ota_seq, boot_index);
+#endif // CONFIG_APP_ROLLBACK_ENABLE
+
+#ifdef CONFIG_APP_ANTI_ROLLBACK
+            if(otadata[active_otadata].ota_state == ESP_OTA_IMG_VALID) {
+                update_anti_rollback(&bs->ota[boot_index]);
+            }
+#endif // CONFIG_APP_ANTI_ROLLBACK
+
         } else if (bs->factory.offset != 0) {
             ESP_LOGE(TAG, "ota data partition invalid, falling back to factory");
             boot_index = FACTORY_INDEX;
@@ -326,15 +404,19 @@ static bool try_load_partition(const esp_partition_pos_t *partition, esp_image_m
 // otadata has initial content(0xFFFFFFFF), then set actual ota_seq.
 static void set_actual_ota_seq(const bootloader_state_t *bs, int index)
 {
-    if (index >= 0 && ota_has_initial_contents == true) {
+    if (index > FACTORY_INDEX && ota_has_initial_contents == true) {
         esp_ota_select_entry_t otadata;
+        memset(&otadata, 0xFF, sizeof(otadata));
         otadata.ota_seq = index + 1;
         otadata.ota_state = ESP_OTA_IMG_VALID;
         otadata.crc = bootloader_common_ota_select_crc(&otadata);
 
         bool write_encrypted = esp_flash_encryption_enabled();
         write_otadata(&otadata, bs->ota_info.offset + FLASH_SECTOR_SIZE * 0, write_encrypted);
-        ESP_LOGD(TAG, "Set actual ota_seq=%d in otadata[0]", otadata.ota_seq);
+        ESP_LOGI(TAG, "Set actual ota_seq=%d in otadata[0]", otadata.ota_seq);
+#ifdef CONFIG_APP_ANTI_ROLLBACK
+        update_anti_rollback(&bs->ota[index]);
+#endif
     }
 }
 
@@ -362,7 +444,7 @@ void bootloader_utility_load_boot_image(const bootloader_state_t *bs, int start_
             continue;
         }
         ESP_LOGD(TAG, TRY_LOG_FORMAT, index, part.offset, part.size);
-        if (try_load_partition(&part, &image_data)) {
+        if (check_anti_rollback(&part) && try_load_partition(&part, &image_data)) {
             set_actual_ota_seq(bs, index);
             load_image(&image_data);
         }
@@ -376,7 +458,7 @@ void bootloader_utility_load_boot_image(const bootloader_state_t *bs, int start_
             continue;
         }
         ESP_LOGD(TAG, TRY_LOG_FORMAT, index, part.offset, part.size);
-        if (try_load_partition(&part, &image_data)) {
+        if (check_anti_rollback(&part) && try_load_partition(&part, &image_data)) {
             set_actual_ota_seq(bs, index);
             load_image(&image_data);
         }
index 35d0a64e19822ddce6629e41d90c06f281d6831d..637d6e49477eb6453a7fad00610c7f5cd8cf2ac1 100644 (file)
@@ -112,3 +112,124 @@ void esp_efuse_write_random_key(uint32_t blk_wdata0_reg)
     bzero(buf, sizeof(buf));
     bzero(raw, sizeof(raw));
 }
+
+
+#ifdef CONFIG_EFUSE_SECURE_VERSION_EMULATE
+
+#include "bootloader_flash.h"
+#include "esp_flash_encrypt.h"
+
+static uint32_t esp_efuse_flash_offset = 0;
+static uint32_t esp_efuse_flash_size = 0;
+void esp_efuse_init(uint32_t offset, uint32_t size)
+{
+    esp_efuse_flash_offset = offset;
+    esp_efuse_flash_size = size;
+}
+
+static uint32_t emulate_secure_version_read()
+{
+    uint32_t secure_version;
+    uint32_t offset = esp_efuse_flash_offset;
+    if (offset == 0) {
+        ESP_LOGE(TAG, "emulate secure_version can not be used");
+        return 0;
+    }
+    const uint32_t *efuse_place_in_flash = bootloader_mmap(offset, esp_efuse_flash_size);
+    if (!efuse_place_in_flash) {
+        ESP_LOGE(TAG, "secure_version can not be read from (0x%x, 0x%x) flash", offset, esp_efuse_flash_size);
+        return 0;
+    }
+    memcpy(&secure_version, efuse_place_in_flash, sizeof(uint32_t));
+    bootloader_munmap(efuse_place_in_flash);
+    secure_version = ~secure_version;
+    ESP_LOGV(TAG, "Read 0x%08x secure_version from flash", secure_version);
+    return secure_version;
+}
+
+static void emulate_secure_version_write(uint32_t secure_version)
+{
+    uint32_t secure_version_wr = ~secure_version;
+    uint32_t offset = esp_efuse_flash_offset;
+    if (offset == 0) {
+        ESP_LOGE(TAG, "emulate secure_version can not be used");
+        return;
+    }
+    esp_err_t err = bootloader_flash_write(offset, &secure_version_wr, sizeof(secure_version_wr), false);
+    if (err != ESP_OK) {
+        ESP_LOGE(TAG, "secure_version can not be written to flash. err = 0x%x", err);
+    }
+    ESP_LOGV(TAG, "Write 0x%08x secure_version into flash", secure_version);
+}
+#endif
+
+// This efuse register is used whole for secure version (32 bits).
+#define EFUSE_BLK_RD_ANTI_ROLLBACK EFUSE_BLK3_RDATA4_REG
+#define EFUSE_BLK_WR_ANTI_ROLLBACK EFUSE_BLK3_WDATA4_REG
+
+uint32_t esp_efuse_read_secure_version()
+{
+#ifdef CONFIG_APP_ANTI_ROLLBACK
+    uint32_t secure_version;
+
+#ifdef CONFIG_EFUSE_SECURE_VERSION_EMULATE
+    secure_version = emulate_secure_version_read();
+#else
+    secure_version = REG_READ(EFUSE_BLK_RD_ANTI_ROLLBACK);
+#endif // CONFIG_EFUSE_SECURE_VERSION_EMULATE
+
+    return __builtin_popcount(secure_version & ((1ULL << CONFIG_APP_SECURE_VERSION_SIZE_EFUSE_FIELD) - 1));
+#else
+    return 0;
+#endif
+}
+
+#ifdef CONFIG_APP_ANTI_ROLLBACK
+static void write_anti_rollback(uint32_t new_bits)
+{
+#ifdef CONFIG_EFUSE_SECURE_VERSION_EMULATE
+    emulate_secure_version_write(new_bits);
+#else
+    esp_efuse_reset();
+    REG_WRITE(EFUSE_BLK_WR_ANTI_ROLLBACK, new_bits);
+    esp_efuse_burn_new_values();
+#endif
+}
+#endif
+
+bool esp_efuse_check_secure_version(uint32_t secure_version)
+{
+    uint32_t sec_ver_hw = esp_efuse_read_secure_version();
+    return secure_version >= sec_ver_hw;
+}
+
+esp_err_t esp_efuse_update_secure_version(uint32_t secure_version)
+{
+#ifdef CONFIG_APP_ANTI_ROLLBACK
+    if (CONFIG_APP_SECURE_VERSION_SIZE_EFUSE_FIELD < secure_version) {
+        ESP_LOGE(TAG, "Max secure version is %d. Given %d version can not be written.", CONFIG_APP_SECURE_VERSION_SIZE_EFUSE_FIELD, secure_version);
+        return ESP_ERR_INVALID_ARG;
+    }
+#ifndef CONFIG_EFUSE_SECURE_VERSION_EMULATE
+    uint32_t coding_scheme = REG_READ(EFUSE_BLK0_RDATA6_REG) & EFUSE_CODING_SCHEME_M;
+    if (coding_scheme != EFUSE_CODING_SCHEME_VAL_NONE) {
+        ESP_LOGE(TAG, "Anti rollback is not supported with a 3/4 coding scheme.");
+        return ESP_ERR_NOT_SUPPORTED;
+    }
+#endif
+    uint32_t sec_ver_hw = esp_efuse_read_secure_version();
+    // If secure_version is the same as in eFuse field than it is ok just go out.
+    if (sec_ver_hw < secure_version) {
+        uint32_t num_bit_hw = (1ULL << sec_ver_hw) - 1;
+        uint32_t num_bit_app = (1ULL << secure_version) - 1;
+        // Repeated programming of programmed bits is strictly forbidden
+        uint32_t new_bits = num_bit_app - num_bit_hw; // get only new bits
+        write_anti_rollback(new_bits);
+        ESP_LOGI(TAG, "Anti-rollback is set. eFuse field is updated(%d).", secure_version);
+    } else if (sec_ver_hw > secure_version) {
+        ESP_LOGE(TAG, "Anti-rollback is not set. secure_version of app is lower that eFuse field(%d).", sec_ver_hw);
+        return ESP_FAIL;
+    }
+#endif
+    return ESP_OK;
+}
index 7dacf38af173cb1dba52675b9c24da548809d618..d2f7a6317706536ffbe809e0a09ce7b3d010ec7e 100644 (file)
@@ -186,7 +186,7 @@ void IRAM_ATTR call_start_cpu0()
         ESP_EARLY_LOGI(TAG, "App version:      %s", app_desc->version);
 #endif
 #ifdef CONFIG_APP_SECURE_VERSION
-        ESP_EARLY_LOGI(TAG, "Secure version:   %x", app_desc->secure_version);
+        ESP_EARLY_LOGI(TAG, "Secure version:   %d", app_desc->secure_version);
 #endif
 #ifdef CONFIG_APP_COMPILE_TIME_DATE
         ESP_EARLY_LOGI(TAG, "Compile time:     %s", app_desc->time);
@@ -514,6 +514,12 @@ static void main_task(void* args)
     // Now that the application is about to start, disable boot watchdog
 #ifndef CONFIG_BOOTLOADER_WDT_DISABLE_IN_USER_CODE
     rtc_wdt_disable();
+#endif
+#ifdef CONFIG_EFUSE_SECURE_VERSION_EMULATE
+    const esp_partition_t *efuse_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_EFUSE_EM, NULL);
+    if (efuse_partition) {
+        esp_efuse_init(efuse_partition->address, efuse_partition->size);
+    }
 #endif
     app_main();
     vTaskDelete(NULL);
index dd1b81614c667b5ab9a18152cfedde14d0e25b55..ba09a439f8f0eaa5636849bd65a1b35f5f42ba2e 100644 (file)
@@ -224,6 +224,22 @@ static const esp_err_msg_t esp_err_msg_table[] = {
 #   endif
 #   ifdef      ESP_ERR_OTA_VALIDATE_FAILED
     ERR_TBL_IT(ESP_ERR_OTA_VALIDATE_FAILED),                /*  5379 0x1503 Error if OTA app image is invalid */
+#   endif
+#   ifdef      ESP_ERR_OTA_SMALL_SEC_VER
+    ERR_TBL_IT(ESP_ERR_OTA_SMALL_SEC_VER),                  /*  5380 0x1504 Error if the firmware has a secure version
+                                                                            less than the running firmware. */
+#   endif
+#   ifdef      ESP_ERR_OTA_ROLLBACK_FAILED
+    ERR_TBL_IT(ESP_ERR_OTA_ROLLBACK_FAILED),                /*  5381 0x1505 Error if flash does not have valid firmware
+                                                                            in passive partition and hence rollback is
+                                                                            not possible */
+#   endif
+#   ifdef      ESP_ERR_OTA_ROLLBACK_INVALID_STATE
+    ERR_TBL_IT(ESP_ERR_OTA_ROLLBACK_INVALID_STATE),         /*  5382 0x1506 Error if current active firmware is still
+                                                                            marked in pending validation state
+                                                                            (ESP_OTA_IMG_PENDING_VERIFY), essentially
+                                                                            first boot of firmware image post upgrade
+                                                                            and hence firmware upgrade is not possible */
 #   endif
     // components/bootloader_support/include/esp_image_format.h
 #   ifdef      ESP_ERR_IMAGE_BASE
index d0f6ff32b819c60748a401de4557464359a58564..998e522f06897301206134f9daa36f382863a003 100644 (file)
@@ -72,6 +72,7 @@ typedef struct {
 #define PART_SUBTYPE_DATA_RF  0x01
 #define PART_SUBTYPE_DATA_WIFI 0x02
 #define PART_SUBTYPE_DATA_NVS_KEYS 0x04
+#define PART_SUBTYPE_DATA_EFUSE_EM 0x05
 
 #define PART_TYPE_END 0xff
 #define PART_SUBTYPE_END 0xff
index 6020820fe001156239e92e14bd504686f0464a30..492dfb810700b0b67dd80d565665c536c351ffe5 100644 (file)
@@ -69,6 +69,15 @@ if(CONFIG_SECURE_BOOT_ENABLED AND
         VERBATIM)
 endif()
 
+# If anti-rollback option is set then factory partition should not be in Partition Table.
+# In this case, should be used the partition table with two ota app without the factory.
+if(CONFIG_APP_ANTI_ROLLBACK AND FACTORY_OFFSET)
+    fail_at_build_time(check_table_contents
+        "ERROR: Anti-rollback option is enabled. Partition table should consist of two ota app without factory partition.")
+add_dependencies(bootloader check_table_contents)
+add_dependencies(app check_table_contents)
+endif()
+
 add_dependencies(bootloader partition_table)
 add_dependencies(app partition_table)
 
index 7128e6ab6f865e9b106702020b64f1a1f44b5a02..57aeca2b9311d703c21d6de08233bfeda9549beb 100644 (file)
@@ -60,7 +60,7 @@ $(PARTITION_TABLE_BIN_UNSIGNED): $(PARTITION_TABLE_CSV_PATH) $(SDKCONFIG_MAKEFIL
        @echo "Building partitions from $(PARTITION_TABLE_CSV_PATH)..."
        $(GEN_ESP32PART) $< $@
 
-all_binaries: $(PARTITION_TABLE_BIN) partition_table_get_info
+all_binaries: $(PARTITION_TABLE_BIN) partition_table_get_info check_table_contents
 
 partition_table_get_info: $(PARTITION_TABLE_BIN)
        $(eval PHY_DATA_OFFSET:=$(shell $(GET_PART_INFO) --partition-type data --partition-subtype phy \
@@ -71,12 +71,19 @@ partition_table_get_info: $(PARTITION_TABLE_BIN)
                                                        --partition-table-file $(PARTITION_TABLE_BIN) get_partition_info --info offset))
        $(eval OTA_DATA_SIZE:=$(shell $(GET_PART_INFO) --partition-type data --partition-subtype ota \
                                                        --partition-table-file $(PARTITION_TABLE_BIN) get_partition_info --info size))
+       $(eval FACTORY_OFFSET:=$(shell $(GET_PART_INFO) --partition-type app --partition-subtype factory \
+                                                       --partition-table-file $(PARTITION_TABLE_BIN) get_partition_info --info offset))
 
 export APP_OFFSET
 export PHY_DATA_OFFSET
 export OTA_DATA_OFFSET
 export OTA_DATA_SIZE
 
+# If anti-rollback option is set then factory partition should not be in Partition Table.
+# In this case, should be used the partition table with two ota app without the factory.
+check_table_contents: partition_table_get_info
+       @echo $(if $(CONFIG_APP_ANTI_ROLLBACK), $(if $(FACTORY_OFFSET), $(error "ERROR: Anti-rollback option is enabled. Partition table should consist of two ota app without factory partition."), ""), "")
+
 PARTITION_TABLE_FLASH_CMD = $(ESPTOOLPY_SERIAL) write_flash $(PARTITION_TABLE_OFFSET) $(PARTITION_TABLE_BIN)
 ESPTOOL_ALL_FLASH_ARGS += $(PARTITION_TABLE_OFFSET) $(PARTITION_TABLE_BIN)
 
index c7b8d502144606d6fd6db7bb07ba7296dcbd9620..17075074034322b4f3659f3def26cb002c2a9e54 100755 (executable)
@@ -60,6 +60,7 @@ SUBTYPES = {
         "nvs": 0x02,
         "coredump": 0x03,
         "nvs_keys": 0x04,
+        "efuse": 0x05,
         "esphttpd": 0x80,
         "fat": 0x81,
         "spiffs": 0x82,
index 9d564ca51fda0d404bc443118e01a7999856c065..03ee63b3fd75a4319ed330b064418d5acbf9149b 100644 (file)
@@ -60,6 +60,9 @@ get_partition_info(OTADATA_PARTITION_OFFSET
 get_partition_info(OTADATA_PARTITION_SIZE
                 "--partition-type data --partition-subtype ota" "size")
 
+get_partition_info(FACTORY_OFFSET
+                "--partition-type app --partition-subtype factory" "offset")
+
 endif()
 
 set(BOOTLOADER_OFFSET 0x1000)
index b1203696b8464ec0aa589354d3dae4b27a9b9e9e..6537967eb7ef70a82e3bc74a10484274f9548a8d 100644 (file)
@@ -71,6 +71,7 @@ typedef enum {
     ESP_PARTITION_SUBTYPE_DATA_NVS = 0x02,                                    //!< NVS partition
     ESP_PARTITION_SUBTYPE_DATA_COREDUMP = 0x03,                               //!< COREDUMP partition
     ESP_PARTITION_SUBTYPE_DATA_NVS_KEYS = 0x04,                               //!< Partition for NVS keys
+    ESP_PARTITION_SUBTYPE_DATA_EFUSE_EM = 0x05,                               //!< Partition for emulate eFuse bits
 
     ESP_PARTITION_SUBTYPE_DATA_ESPHTTPD = 0x80,                               //!< ESPHTTPD partition
     ESP_PARTITION_SUBTYPE_DATA_FAT = 0x81,                                    //!< FAT partition
index 500b9512d254131a55fcb9587083e8f3ca9904f2..95b0b32a0b3926fd3f7e32700b10b551a486c00b 100644 (file)
@@ -121,6 +121,74 @@ A brief description of where the states are set:
 * ``ESP_OTA_IMG_ABORTED`` state is set if there was no confirmation of the application operability and occurs reboots (if :ref:`CONFIG_APP_ROLLBACK_ENABLE` option is enabled).
 * ``ESP_OTA_IMG_PENDING_VERIFY`` state is set in a bootloader if :ref:`CONFIG_APP_ROLLBACK_ENABLE` option is enabled and selected app has ``ESP_OTA_IMG_NEW`` state.
 
+.. _anti-rollback:
+
+Anti-rollback
+-------------
+
+Anti-rollback prevents rollback to application with security version lower than one programmed in eFuse of chip.
+
+This function works if set :ref:`CONFIG_APP_ANTI_ROLLBACK` option. In the bootloader, when selecting a bootable application, an additional security version check is added which is on the chip and in the application image. The version in the bootable firmware must be greater than or equal to the version in the chip.
+
+
+:ref:`CONFIG_APP_ANTI_ROLLBACK` and :ref:`CONFIG_APP_ROLLBACK_ENABLE` options are used together. In this case, rollback is possible only on the security version which is equal or higher than the version in the chip.
+
+
+A typical anti-rollback scheme is
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+- New firmware released with the elimination of vulnerabilities with the previous version of security. 
+- After the developer makes sure that this firmware is working. He can increase the security version and release a new firmware.
+- Download new application.
+- To make it bootable, run the function :cpp:func:`esp_ota_set_boot_partition`. If the security version of the new application is smaller than the version in the chip, the new application will be erased. Update to new firmware is not possible.
+- Reboot.
+- In the bootloader, an application with a security version greater than or equal to the version in the chip will be selected. If otadata is in the initial state, and one firmware was loaded via a serial channel, whose secure version is higher than the chip, then the secure version of efuse will be immediately updated in the bootloader.
+- New application booted. Then the application should perform diagnostics of the operation and if it is completed successfully, you should call :cpp:func:`esp_ota_mark_app_valid_cancel_rollback` function to mark the running application with the ``ESP_OTA_IMG_VALID`` state and update the secure version on chip. Note that if was called :cpp:func:`esp_ota_mark_app_invalid_rollback_and_reboot` function a rollback may not happend due to the device may not have any bootable apps then it will return ``ESP_ERR_OTA_ROLLBACK_FAILED`` error and stay in the ``ESP_OTA_IMG_PENDING_VERIFY`` state.
+- The next update of app is possible if a running app is in the ``ESP_OTA_IMG_VALID`` state.
+
+Recommendation: 
+
+If you want to avoid the download/erase overhead in case of the app from the server has security version lower then running app you have to get ``new_app_info.secure_version`` from the first package of an image and compare it with the secure version of efuse. Use ``esp_efuse_check_secure_version(new_app_info.secure_version)`` function if it is true then continue downloading otherwise abort.
+
+.. code-block:: c
+
+    ....
+    bool image_header_was_checked = false;
+    while (1) {
+        int data_read = esp_http_client_read(client, ota_write_data, BUFFSIZE);
+        ...
+        if (data_read > 0) {
+            if (image_header_was_checked == false) {
+                esp_app_desc_t new_app_info;
+                if (data_read > sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t) + sizeof(esp_app_desc_t)) {
+                    // check current version with downloading
+                    if (esp_efuse_check_secure_version(new_app_info.secure_version) == false) {
+                       ESP_LOGE(TAG, "This a new app can not be downloaded due to a secure version is lower than stored in efuse.");
+                       http_cleanup(client);
+                       task_fatal_error();
+                    }
+                    
+                    image_header_was_checked = true;
+
+                    esp_ota_begin(update_partition, OTA_SIZE_UNKNOWN, &update_handle);
+                }
+            }
+            esp_ota_write( update_handle, (const void *)ota_write_data, data_read);
+        }
+    }
+    ...
+
+Restrictions:
+
+- The number of bits in the ``secure_version`` field is limited to 32 bits. This means that only 32 times you can do an anti-rollback. You can reduce the length of this efuse field use :ref:`CONFIG_APP_SECURE_VERSION_SIZE_EFUSE_FIELD` option.
+- Anti-rollback only works if the encoding scheme for efuse is set to ``NONE``.
+- The partition table should not have a factory partition, only two of the app.
+
+``security_version``:
+
+- In application image it is stored in ``esp_app_desc`` structure. The number is set :ref:`CONFIG_APP_SECURE_VERSION`.
+- In ESP32 it is stored in efuse ``EFUSE_BLK3_RDATA4_REG``. (when a eFuse bit is programmed to 1, it can never be reverted to 0). The number of bits set in this register is the ``security_version`` from app.
+
 .. _secure-ota-updates:
 
 Secure OTA Updates Without Secure boot
index a5b77ea03d28c699c3dd3239e1bbdbddb2dbf57b..ced9391944fb448b6f0c7b706ce7905e543a516e 100644 (file)
@@ -109,6 +109,25 @@ When the example starts up, it will print "Starting OTA example..." then:
 3. Write the image to flash, and configure the next boot from this image.
 4. Reboot
 
+## Support the rollback
+
+This feature allows you to roll back to the previous firmware if the app is not operable. Option :ref:`CONFIG_APP_ROLLBACK_ENABLE` allows you to track the first boot of the application (see the``Over The Air Updates (OTA)`` article). 
+For ``native_ota_example``, added a bit of code to demonstrate how a rollback works. To use it, you need enable the :ref:`CONFIG_APP_ROLLBACK_ENABLE` option in Kconfig and under the "Example Configuration" submenu to set "Number of the GPIO input for diagnostic" to manage the rollback process.
+
+To trigger a rollback, this GPIO must be pulled low while the message `Diagnostics (5 sec)...` which will be on first boot.
+If GPIO is not pulled low then the operable of the app will be confirmed.
+
+## Support the version of application
+
+For ``native_ota_example``, code has been added to demonstrate how to check the version of the application and prevent infinite firmware updates. Only the application with the new version can be downloaded. Version checking is performed after the very first firmware image package has been received, which contains data about the firmware version. The application version can be taken from three places:
+
+1. If ``PROJECT_VER`` variable set in project Cmake/Makefile file, its value will be used.
+2. Else, if the ``$PROJECT_PATH/version.txt`` exists, its contents will be used as ``PROJECT_VER``.
+3. Else, if the project is located inside a Git repository, the output of ``git describe`` will be used.
+4. Otherwise, ``PROJECT_VER`` will be "1".
+
+In ``native_ota_example``, ``$PROJECT_PATH/version.txt`` is used to define the version of app. Change the version in the file to compile the new firmware.
+
 ## Troubleshooting
 
 * Check your PC can ping the ESP32 at its IP, and that the IP, AP and other configuration settings are correct in menuconfig.
index 628dfda5d3f3d7f6cbd49018dc506c14a46234e9..ebb9995729bd2b1df9b3f88b54e63c9e85ceccc1 100644 (file)
@@ -22,4 +22,15 @@ menu "Example Configuration"
 
             See example README.md for details.
 
+    config GPIO_DIAGNOSTIC
+        int "Number of the GPIO input for diagnostic"
+        range 0 39
+        default 4
+        help
+            Used to demonstrate how a rollback works.
+            The selected GPIO will be configured as an input with internal pull-up enabled.
+            To trigger a rollback, this GPIO must be pulled low while the message
+            `Diagnostics (5 sec)...` which will be on first boot.
+            If GPIO is not pulled low then the operable of the app will be confirmed.
+
 endmenu
index 0263b0447d09fc36884be529ea1b64858897653f..854e77fac49bedcedd6c66a1cd9a9dc2ff4e6eda 100644 (file)
@@ -22,6 +22,7 @@
 
 #include "nvs.h"
 #include "nvs_flash.h"
+#include "driver/gpio.h"
 
 #define EXAMPLE_WIFI_SSID CONFIG_WIFI_SSID
 #define EXAMPLE_WIFI_PASS CONFIG_WIFI_PASSWORD
@@ -260,6 +261,25 @@ static void ota_example_task(void *pvParameter)
     return ;
 }
 
+static bool diagnostic(void)
+{
+    gpio_config_t io_conf;
+    io_conf.intr_type    = GPIO_PIN_INTR_DISABLE;
+    io_conf.mode         = GPIO_MODE_INPUT;
+    io_conf.pin_bit_mask = (1ULL << CONFIG_GPIO_DIAGNOSTIC);
+    io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
+    io_conf.pull_up_en   = GPIO_PULLUP_ENABLE;
+    gpio_config(&io_conf);
+
+    ESP_LOGI(TAG, "Diagnostics (5 sec)...");
+    vTaskDelay(5000 / portTICK_PERIOD_MS);
+
+    bool diagnostic_is_ok = gpio_get_level(CONFIG_GPIO_DIAGNOSTIC);
+
+    gpio_reset_pin(CONFIG_GPIO_DIAGNOSTIC);
+    return diagnostic_is_ok;
+}
+
 void app_main()
 {
     uint8_t sha_256[HASH_LEN] = { 0 };
@@ -288,7 +308,7 @@ void app_main()
     if (esp_ota_get_state_partition(running, &ota_state) == ESP_OK) {
         if (ota_state == ESP_OTA_IMG_PENDING_VERIFY) {
             // run diagnostic function ...
-            bool diagnostic_is_ok = true;
+            bool diagnostic_is_ok = diagnostic();
             if (diagnostic_is_ok) {
                 ESP_LOGI(TAG, "Diagnostics completed successfully! Continuing execution ...");
                 esp_ota_mark_app_valid_cancel_rollback();
diff --git a/examples/system/ota/native_ota_example/version.txt b/examples/system/ota/native_ota_example/version.txt
new file mode 100644 (file)
index 0000000..56a6051
--- /dev/null
@@ -0,0 +1 @@
+1
\ No newline at end of file