Merge branch 'feature/anti_rollback_efuse' into 'master'

Support anti-rollback

See merge request idf/esp-idf!3682
This commit is contained in:
Angus Gratton 2019-02-20 16:11:00 +08:00
commit 40a55823a9
22 changed files with 635 additions and 40 deletions

View 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;
}

View 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

View 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;

View 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

View 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.
*

View 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

View 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) {

View 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);
}

View 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;
}

View 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);

View 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

View 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

View 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)

View 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)

View File

@ -60,6 +60,7 @@ SUBTYPES = {
"nvs": 0x02,
"coredump": 0x03,
"nvs_keys": 0x04,
"efuse": 0x05,
"esphttpd": 0x80,
"fat": 0x81,
"spiffs": 0x82,

View 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)

View 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

View 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

View 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.

View 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

View 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();

View File

@ -0,0 +1 @@
1