diff --git a/components/app_update/esp_ota_ops.c b/components/app_update/esp_ota_ops.c index 439117b596..c702a203e2 100644 --- a/components/app_update/esp_ota_ops.c +++ b/components/app_update/esp_ota_ops.c @@ -27,6 +27,7 @@ #include "esp_spi_flash.h" #include "esp_image_format.h" #include "esp_secure_boot.h" +#include "esp_flash_encrypt.h" #include "sdkconfig.h" #include "esp_ota_ops.h" @@ -44,6 +45,10 @@ typedef struct ota_ops_entry_ { esp_partition_t part; uint32_t erased_size; uint32_t wrote_size; +#ifdef CONFIG_FLASH_ENCRYPTION_ENABLED + uint8_t partial_bytes; + uint8_t partial_data[16]; +#endif LIST_ENTRY(ota_ops_entry_) entries; } ota_ops_entry_t; @@ -106,6 +111,7 @@ esp_err_t esp_ota_begin(const esp_partition_t *partition, size_t image_size, esp esp_err_t esp_ota_write(esp_ota_handle_t handle, const void *data, size_t size) { + const uint8_t *data_bytes = (const uint8_t *)data; esp_err_t ret; ota_ops_entry_t *it; @@ -119,7 +125,47 @@ esp_err_t esp_ota_write(esp_ota_handle_t handle, const void *data, size_t size) if (it->handle == handle) { // must erase the partition before writing to it assert(it->erased_size > 0 && "must erase the partition before writing to it"); - ret = esp_partition_write(&it->part, it->wrote_size, data, size); + + if(it->wrote_size == 0 && size > 0 && data_bytes[0] != 0xE9) { + ESP_LOGE(TAG, "OTA image has invalid magic byte (expected 0xE9, saw 0x%02x", data_bytes[0]); + return ESP_ERR_OTA_VALIDATE_FAILED; + } + +#ifdef CONFIG_FLASH_ENCRYPTION_ENABLED + if (esp_flash_encryption_enabled()) { + /* Can only write 16 byte blocks to flash, so need to cache anything else */ + size_t copy_len; + + /* check if we have partially written data from earlier */ + if (it->partial_bytes != 0) { + copy_len = OTA_MIN(16 - it->partial_bytes, size); + memcpy(it->partial_data + it->partial_bytes, data_bytes, copy_len); + it->partial_bytes += copy_len; + if (it->partial_bytes != 16) { + return ESP_OK; /* nothing to write yet, just filling buffer */ + } + /* write 16 byte to partition */ + ret = esp_partition_write(&it->part, it->wrote_size, it->partial_data, 16); + if (ret != ESP_OK) { + return ret; + } + it->partial_bytes = 0; + memset(it->partial_data, 0xFF, 16); + it->wrote_size += 16; + data_bytes += copy_len; + size -= copy_len; + } + + /* check if we need to save trailing data that we're about to write */ + it->partial_bytes = size % 16; + if (it->partial_bytes != 0) { + size -= it->partial_bytes; + memcpy(it->partial_data, data_bytes + size, it->partial_bytes); + } + } +#endif + + ret = esp_partition_write(&it->part, it->wrote_size, data_bytes, size); if(ret == ESP_OK){ it->wrote_size += size; } @@ -135,26 +181,11 @@ esp_err_t esp_ota_write(esp_ota_handle_t handle, const void *data, size_t size) esp_err_t esp_ota_end(esp_ota_handle_t handle) { ota_ops_entry_t *it; + size_t image_size; + esp_err_t ret = ESP_OK; + for (it = LIST_FIRST(&s_ota_ops_entries_head); it != NULL; it = LIST_NEXT(it, entries)) { if (it->handle == handle) { - // an ota handle need to be ended after erased and wrote data in it - if ((it->erased_size == 0) || (it->wrote_size == 0)) { - return ESP_ERR_INVALID_ARG; - } - -#ifdef CONFIG_SECUREBOOTLOADER - esp_err_t ret; - size_t image_size; - if (esp_image_basic_verify(it->part.address, &image_size) != ESP_OK) { - return ESP_ERR_OTA_VALIDATE_FAILED; - } - ret = esp_secure_boot_verify_signature(it->part.address, image_size); - if (ret != ESP_OK) { - return ESP_ERR_OTA_VALIDATE_FAILED; - } -#endif - - LIST_REMOVE(it, entries); break; } } @@ -163,8 +194,44 @@ esp_err_t esp_ota_end(esp_ota_handle_t handle) return ESP_ERR_NOT_FOUND; } + /* 'it' holds the ota_ops_entry_t for 'handle' */ + + // esp_ota_end() is only valid if some data was written to this handle + if ((it->erased_size == 0) || (it->wrote_size == 0)) { + ret = ESP_ERR_INVALID_ARG; + goto cleanup; + } + +#ifdef CONFIG_FLASH_ENCRYPTION_ENABLED + if (it->partial_bytes > 0 && esp_flash_encryption_enabled()) { + /* Write out last 16 bytes, if necessary */ + ret = esp_partition_write(&it->part, it->wrote_size, it->partial_data, 16); + if (ret != ESP_OK) { + ret = ESP_ERR_INVALID_STATE; + goto cleanup; + } + it->wrote_size += 16; + it->partial_bytes = 0; + } +#endif + + if (esp_image_basic_verify(it->part.address, true, &image_size) != ESP_OK) { + ret = ESP_ERR_OTA_VALIDATE_FAILED; + goto cleanup; + } + +#ifdef CONFIG_SECURE_BOOT_ENABLED + ret = esp_secure_boot_verify_signature(it->part.address, image_size); + if (ret != ESP_OK) { + ret = ESP_ERR_OTA_VALIDATE_FAILED; + goto cleanup; + } +#endif + + cleanup: + LIST_REMOVE(it, entries); free(it); - return ESP_OK; + return ret; } static uint32_t ota_select_crc(const ota_select *s) @@ -271,11 +338,9 @@ static esp_err_t esp_rewrite_ota_data(esp_partition_subtype_t subtype) } return rewrite_ota_seq((SUB_TYPE_ID(subtype) + 1) % ota_app_count + i * ota_app_count, 0, find_partition); - } else if (s_ota_select[0].ota_seq == 0xFFFFFFFF && s_ota_select[1].ota_seq == 0xFFFFFFFF) { - return rewrite_ota_seq(SUB_TYPE_ID(subtype) + 1, 0, find_partition); - } else { - return ESP_ERR_OTA_SELECT_INFO_INVALID; + /* Both OTA slots are invalid, probably because unformatted... */ + return rewrite_ota_seq(SUB_TYPE_ID(subtype) + 1, 0, find_partition); } } else { @@ -285,17 +350,18 @@ static esp_err_t esp_rewrite_ota_data(esp_partition_subtype_t subtype) esp_err_t esp_ota_set_boot_partition(const esp_partition_t *partition) { + size_t image_size; const esp_partition_t *find_partition = NULL; if (partition == NULL) { return ESP_ERR_INVALID_ARG; } -#ifdef CONFIG_SECUREBOOTLOADER - size_t image_size; - if (esp_image_basic_verify(partition->address, &image_size) != ESP_OK) { + if (esp_image_basic_verify(partition->address, true, &image_size) != ESP_OK) { return ESP_ERR_OTA_VALIDATE_FAILED; } - ret = esp_secure_boot_verify_signature(partition->address, image_size); + +#ifdef CONFIG_SECURE_BOOT_ENABLED + esp_err_t ret = esp_secure_boot_verify_signature(partition->address, image_size); if (ret != ESP_OK) { return ESP_ERR_OTA_VALIDATE_FAILED; } @@ -349,33 +415,34 @@ const esp_partition_t *esp_ota_get_boot_partition(void) } ota_app_count = get_ota_partition_count(); - ESP_LOGD(TAG, "found ota bin max = %d", ota_app_count); + ESP_LOGD(TAG, "found ota app max = %d", ota_app_count); + if (s_ota_select[0].ota_seq == 0xFFFFFFFF && s_ota_select[1].ota_seq == 0xFFFFFFFF) { - ESP_LOGD(TAG, "finding factory bin......"); + ESP_LOGD(TAG, "finding factory app......"); return esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_FACTORY, NULL); } else if (ota_select_valid(&s_ota_select[0]) && ota_select_valid(&s_ota_select[1])) { - ESP_LOGD(TAG, "finding ota_%d bin......", \ + ESP_LOGD(TAG, "finding ota_%d app......", \ ESP_PARTITION_SUBTYPE_APP_OTA_MIN + ((OTA_MAX(s_ota_select[0].ota_seq, s_ota_select[1].ota_seq) - 1) % ota_app_count)); return esp_partition_find_first(ESP_PARTITION_TYPE_APP, \ ESP_PARTITION_SUBTYPE_APP_OTA_MIN + ((OTA_MAX(s_ota_select[0].ota_seq, s_ota_select[1].ota_seq) - 1) % ota_app_count), NULL); } else if (ota_select_valid(&s_ota_select[0])) { - ESP_LOGD(TAG, "finding ota_%d bin......", \ + ESP_LOGD(TAG, "finding ota_%d app......", \ ESP_PARTITION_SUBTYPE_APP_OTA_MIN + (s_ota_select[0].ota_seq - 1) % ota_app_count); return esp_partition_find_first(ESP_PARTITION_TYPE_APP, \ ESP_PARTITION_SUBTYPE_APP_OTA_MIN + (s_ota_select[0].ota_seq - 1) % ota_app_count, NULL); } else if (ota_select_valid(&s_ota_select[1])) { - ESP_LOGD(TAG, "finding ota_%d bin......", \ + ESP_LOGD(TAG, "finding ota_%d app......", \ ESP_PARTITION_SUBTYPE_APP_OTA_MIN + (s_ota_select[1].ota_seq - 1) % ota_app_count); return esp_partition_find_first(ESP_PARTITION_TYPE_APP, \ ESP_PARTITION_SUBTYPE_APP_OTA_MIN + (s_ota_select[1].ota_seq - 1) % ota_app_count, NULL); } else { - ESP_LOGE(TAG, "not found current bin"); - return NULL; + ESP_LOGE(TAG, "ota data invalid, no current app. Assuming factory"); + return esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_FACTORY, NULL); } } diff --git a/components/app_update/include/esp_ota_ops.h b/components/app_update/include/esp_ota_ops.h index 846aa2b2cf..fe3307763f 100755 --- a/components/app_update/include/esp_ota_ops.h +++ b/components/app_update/include/esp_ota_ops.h @@ -73,11 +73,16 @@ esp_err_t esp_ota_write(esp_ota_handle_t handle, const void* data, size_t size); /** * @brief Finish the update and validate written data * - * @param handle Handle obtained from esp_ota_begin + * @param handle Handle obtained from esp_ota_begin. * - * @return: - * - ESP_OK: if validate ota image pass - * - ESP_ERR_OTA_VALIDATE_FAILED: validate the ota image is invalid + * @note After calling esp_ota_end(), the handle is no longer valid and any memory associated with it is freed (regardless of result). + * + * @return: + * - ESP_OK: Newly written OTA app image is valid. + * - ESP_ERR_NOT_FOUND: OTA handle was not found. + * - ESP_ERR_INVALID_ARG: Handle was never written to. + * - ESP_ERR_OTA_VALIDATE_FAILED: OTA image is invalid (either not a valid app image, or - if secure boot is enabled - signature failed to verify.) + * - ESP_ERR_INVALID_STATE: If flash encryption is enabled, this result indicates an internal error writing the final encrypted bytes to flash. */ esp_err_t esp_ota_end(esp_ota_handle_t handle); diff --git a/components/bootloader/src/main/bootloader_start.c b/components/bootloader/src/main/bootloader_start.c index a374c4306d..43066aa3d7 100644 --- a/components/bootloader/src/main/bootloader_start.c +++ b/components/bootloader/src/main/bootloader_start.c @@ -323,12 +323,15 @@ void bootloader_main() } else { if(ota_select_valid(&sa) && ota_select_valid(&sb)) { load_part_pos = bs.ota[(((sa.ota_seq > sb.ota_seq)?sa.ota_seq:sb.ota_seq) - 1)%bs.app_count]; - }else if(ota_select_valid(&sa)) { + } else if(ota_select_valid(&sa)) { load_part_pos = bs.ota[(sa.ota_seq - 1) % bs.app_count]; - }else if(ota_select_valid(&sb)) { + } else if(ota_select_valid(&sb)) { load_part_pos = bs.ota[(sb.ota_seq - 1) % bs.app_count]; - }else { - ESP_LOGE(TAG, "ota data partition info error"); + } else if (bs.factory.offset != 0) { + ESP_LOGE(TAG, "ota data partition invalid, falling back to factory"); + load_part_pos = bs.factory; + } else { + ESP_LOGE(TAG, "ota data partition invalid and no factory, can't boot"); return; } } diff --git a/components/bootloader_support/include/esp_flash_encrypt.h b/components/bootloader_support/include/esp_flash_encrypt.h index 015dea030a..ff1a5f3306 100644 --- a/components/bootloader_support/include/esp_flash_encrypt.h +++ b/components/bootloader_support/include/esp_flash_encrypt.h @@ -15,7 +15,8 @@ #define __ESP32_FLASH_ENCRYPT_H #include -#include +#include "esp_attr.h" +#include "esp_err.h" #include "esp_spi_flash.h" #include "soc/efuse_reg.h" @@ -30,9 +31,17 @@ * * @return true if flash encryption is enabled. */ -static inline bool esp_flash_encryption_enabled(void) { +static inline IRAM_ATTR bool esp_flash_encryption_enabled(void) { uint32_t flash_crypt_cnt = REG_GET_FIELD(EFUSE_BLK0_RDATA0_REG, EFUSE_RD_FLASH_CRYPT_CNT); - return __builtin_parity(flash_crypt_cnt) == 1; + /* __builtin_parity is in flash, so we calculate parity inline */ + bool enabled = false; + while(flash_crypt_cnt) { + if (flash_crypt_cnt & 1) { + enabled = !enabled; + } + flash_crypt_cnt >>= 1; + } + return enabled; } /* @brief Update on-device flash encryption diff --git a/components/bootloader_support/src/bootloader_flash.c b/components/bootloader_support/src/bootloader_flash.c index 405da732b3..da77b1dc28 100644 --- a/components/bootloader_support/src/bootloader_flash.c +++ b/components/bootloader_support/src/bootloader_flash.c @@ -16,6 +16,7 @@ #include #include #include /* including in bootloader for error values */ +#include #ifndef BOOTLOADER_BUILD /* Normal app version maps to esp_spi_flash.h operations... @@ -48,7 +49,11 @@ void bootloader_munmap(const void *mapping) esp_err_t bootloader_flash_read(size_t src, void *dest, size_t size, bool allow_decrypt) { - return spi_flash_read(src, dest, size); + if (allow_decrypt && esp_flash_encryption_enabled()) { + return spi_flash_read_encrypted(src, dest, size); + } else { + return spi_flash_read(src, dest, size); + } } esp_err_t bootloader_flash_write(size_t dest_addr, void *src, size_t size, bool write_encrypted) diff --git a/components/bootloader_support/src/flash_encrypt.c b/components/bootloader_support/src/flash_encrypt.c index 8b9ca60cd1..8ba068d03b 100644 --- a/components/bootloader_support/src/flash_encrypt.c +++ b/components/bootloader_support/src/flash_encrypt.c @@ -285,6 +285,9 @@ static esp_err_t encrypt_partition(int index, const esp_partition_info_t *partit } else { should_encrypt = false; } + } else if (partition->type == PART_TYPE_DATA && partition->subtype == PART_SUBTYPE_DATA_OTA) { + /* check if we have ota data partition and the partition should be encrypted unconditionally */ + should_encrypt = true; } if (!should_encrypt) { diff --git a/components/esp32/crosscore_int.c b/components/esp32/crosscore_int.c index 1e131eeef7..f75f0eba7d 100644 --- a/components/esp32/crosscore_int.c +++ b/components/esp32/crosscore_int.c @@ -82,7 +82,7 @@ void esp_crosscore_int_init() { assert(err == ESP_OK); } -void esp_crosscore_int_send_yield(int coreId) { +void IRAM_ATTR esp_crosscore_int_send_yield(int coreId) { assert(coreId= 256) { - return; /* invalid address */ + return false; /* invalid address */ } int idx = page / 32; @@ -277,6 +300,8 @@ static inline IRAM_ATTR void update_written_pages(size_t start_addr, size_t leng Cache_Flush(1); #endif bzero(written_pages, sizeof(written_pages)); + return true; } } + return false; } diff --git a/components/spi_flash/partition.c b/components/spi_flash/partition.c index 45f66d696b..76036b305e 100644 --- a/components/spi_flash/partition.c +++ b/components/spi_flash/partition.c @@ -166,10 +166,14 @@ static esp_err_t load_partitions() item->info.type = it->type; item->info.subtype = it->subtype; item->info.encrypted = it->flags & PART_FLAG_ENCRYPTED; - if (esp_flash_encryption_enabled() && it->type == PART_TYPE_APP) { - /* All app partitions are encrypted if encryption is turned on */ + if (esp_flash_encryption_enabled() && ( + it->type == PART_TYPE_APP + || (it->type == PART_TYPE_DATA && it->subtype == PART_SUBTYPE_DATA_OTA))) { + /* If encryption is turned on, all app partitions and OTA data + are always encrypted */ item->info.encrypted = true; } + // it->label may not be zero-terminated strncpy(item->info.label, (const char*) it->label, sizeof(it->label)); item->info.label[sizeof(it->label)] = 0; diff --git a/examples/system/ota/main/ota.c b/examples/system/ota/main/ota.c index 4956129e5d..2dfda1c828 100644 --- a/examples/system/ota/main/ota.c +++ b/examples/system/ota/main/ota.c @@ -95,7 +95,7 @@ static void initialise_wifi(void) } /*read buffer by byte still delim ,return read bytes counts*/ -int read_until(char *buffer, char delim, int len) +static int read_until(char *buffer, char delim, int len) { // /*TODO: delim check,buffer check,further: do an buffer length limited*/ int i = 0; @@ -109,7 +109,7 @@ int read_until(char *buffer, char delim, int len) * return true if packet including \r\n\r\n that means http packet header finished,start to receive packet body * otherwise return false * */ -bool resolve_pkg(char text[], int total_len, esp_ota_handle_t out_handle) +static bool read_past_http_header(char text[], int total_len, esp_ota_handle_t out_handle) { /* i means current position */ int i = 0, i_read_len = 0; @@ -121,17 +121,10 @@ bool resolve_pkg(char text[], int total_len, esp_ota_handle_t out_handle) memset(ota_write_data, 0, BUFFSIZE); /*copy first http packet body to write buffer*/ memcpy(ota_write_data, &(text[i + 2]), i_write_len); - /*check write packet header first byte:0xE9 second byte:0x09 */ - if (ota_write_data[0] == 0xE9 && i_write_len >= 2 && ota_write_data[1] == 0x09) { - ESP_LOGI(TAG, "OTA Write Header format Check OK. first byte is %02x ,second byte is %02x", ota_write_data[0], ota_write_data[1]); - } else { - ESP_LOGE(TAG, "OTA Write Header format Check Failed! first byte is %02x ,second byte is %02x", ota_write_data[0], ota_write_data[1]); - return false; - } esp_err_t err = esp_ota_write( out_handle, (const void *)ota_write_data, i_write_len); if (err != ESP_OK) { - ESP_LOGE(TAG, "Error: esp_ota_write failed! err=%x", err); + ESP_LOGE(TAG, "Error: esp_ota_write failed! err=0x%x", err); return false; } else { ESP_LOGI(TAG, "esp_ota_write header OK"); @@ -266,7 +259,7 @@ void main_task(void *pvParameter) task_fatal_error(); } - bool pkg_body_start = false, flag = true; + bool resp_body_start = false, flag = true; /*deal with all receive packet*/ while (flag) { memset(text, 0, TEXT_BUFFSIZE); @@ -275,14 +268,14 @@ void main_task(void *pvParameter) if (buff_len < 0) { /*receive error*/ ESP_LOGE(TAG, "Error: receive data error! errno=%d", errno); task_fatal_error(); - } else if (buff_len > 0 && !pkg_body_start) { /*deal with packet header*/ + } else if (buff_len > 0 && !resp_body_start) { /*deal with response header*/ memcpy(ota_write_data, text, buff_len); - pkg_body_start = resolve_pkg(text, buff_len, out_handle); - } else if (buff_len > 0 && pkg_body_start) { /*deal with packet body*/ + resp_body_start = read_past_http_header(text, buff_len, out_handle); + } else if (buff_len > 0 && resp_body_start) { /*deal with response body*/ memcpy(ota_write_data, text, buff_len); err = esp_ota_write( out_handle, (const void *)ota_write_data, buff_len); if (err != ESP_OK) { - ESP_LOGE(TAG, "Error: esp_ota_write failed! err=%x", err); + ESP_LOGE(TAG, "Error: esp_ota_write failed! err=0x%x", err); task_fatal_error(); } binary_file_length += buff_len;