mirror of
https://github.com/espressif/esp-idf.git
synced 2026-05-28 16:46:31 +03:00
feat(app_update): add API for checking the spi mode compatibility
New API to check the SPI flash mode from the incoming firmware image during OTA updates could prevent bootloader/app incompatibility of DIO vs QIO flash modes. More information: - https://github.com/espressif/esp-hosted-mcu/issues/143#issuecomment-3741753788 - https://github.com/espressif/esp-idf/issues/9674#issuecomment-1232533757 - https://github.com/espressif/esp-idf/issues/9542#issuecomment-1211317354
This commit is contained in:
@@ -1012,6 +1012,52 @@ bool esp_ota_check_rollback_is_possible(void)
|
||||
return false;
|
||||
}
|
||||
|
||||
esp_err_t esp_ota_check_image_validity(esp_partition_type_t part_type,
|
||||
const esp_image_header_t *img_hdr,
|
||||
const esp_app_desc_t *app_desc)
|
||||
{
|
||||
if (img_hdr == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
// Map partition type to image type for bootloader_common API
|
||||
esp_image_type img_type;
|
||||
if (part_type == ESP_PARTITION_TYPE_APP) {
|
||||
img_type = ESP_IMAGE_APPLICATION;
|
||||
} else if (part_type == ESP_PARTITION_TYPE_BOOTLOADER) {
|
||||
img_type = ESP_IMAGE_BOOTLOADER;
|
||||
} else {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
// Check chip ID and chip revision validity
|
||||
esp_err_t err = bootloader_common_check_chip_validity(img_hdr, img_type);
|
||||
if (err != ESP_OK) {
|
||||
return ESP_ERR_INVALID_VERSION;
|
||||
}
|
||||
|
||||
// Check SPI flash mode if app descriptor is provided
|
||||
if (part_type == ESP_PARTITION_TYPE_APP && app_desc != NULL) {
|
||||
// Get the running app's descriptor
|
||||
const esp_app_desc_t *running_app_desc = esp_app_get_description();
|
||||
|
||||
if (running_app_desc->spi_flash_mode == 0 || app_desc->spi_flash_mode == 0) {
|
||||
// Older image format, CONFIG_ESPTOOLPY_FLASHMODE_VAL not stored in the app descriptor
|
||||
ESP_LOGD(TAG, "Older image format and hence no SPI flash mode info is available");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// Compare SPI flash modes as stored in app descriptor (CONFIG_ESPTOOLPY_FLASHMODE_VAL)
|
||||
if (app_desc->spi_flash_mode != running_app_desc->spi_flash_mode) {
|
||||
ESP_LOGE(TAG, "SPI flash mode mismatch: running app has mode %d, new app has mode %d",
|
||||
running_app_desc->spi_flash_mode, app_desc->spi_flash_mode);
|
||||
return ESP_ERR_OTA_SPI_MODE_MISMATCH;
|
||||
}
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "esp_err.h"
|
||||
#include "esp_partition.h"
|
||||
#include "esp_app_desc.h"
|
||||
#include "esp_app_format.h"
|
||||
#include "esp_bootloader_desc.h"
|
||||
#include "esp_flash_partitions.h"
|
||||
#include "soc/soc_caps.h"
|
||||
@@ -32,7 +33,8 @@ extern "C"
|
||||
#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 */
|
||||
#define ESP_ERR_OTA_ALREADY_IN_PROGRESS (ESP_ERR_OTA_BASE + 0x07) /*!< Error if another OTA operation is already in progress on the same partition */
|
||||
#define ESP_ERR_OTA_ALREADY_IN_PROGRESS (ESP_ERR_OTA_BASE + 0x07) /*!< Error if another OTA operation is already in progress on the same partition */
|
||||
#define ESP_ERR_OTA_SPI_MODE_MISMATCH (ESP_ERR_OTA_BASE + 0x08) /*!< Error if the firmware's SPI flash mode doesn't match the running firmware */
|
||||
|
||||
|
||||
/**
|
||||
@@ -420,6 +422,31 @@ esp_err_t esp_ota_erase_last_boot_app_partition(void);
|
||||
*/
|
||||
bool esp_ota_check_rollback_is_possible(void);
|
||||
|
||||
/**
|
||||
* @brief Check image validity including chip ID, chip revision, and optionally SPI flash mode.
|
||||
*
|
||||
* This function performs comprehensive validation of an OTA image:
|
||||
* - Verifies the chip ID matches the current chip
|
||||
* - Verifies the chip revision meets the image requirements
|
||||
* - Optionally verifies the SPI flash mode matches (if app_desc is provided)
|
||||
*
|
||||
* For bootloader partitions (ESP_PARTITION_TYPE_BOOTLOADER), the maximum chip revision check is skipped.
|
||||
* For application partitions (ESP_PARTITION_TYPE_APP), both minimum and maximum chip revision are checked.
|
||||
*
|
||||
* @param[in] part_type Partition type (ESP_PARTITION_TYPE_APP or ESP_PARTITION_TYPE_BOOTLOADER)
|
||||
* @param[in] img_hdr Pointer to the image header for chip ID and revision checks
|
||||
* @param[in] app_desc Pointer to the app descriptor for SPI mode check (can be NULL to skip SPI mode verification)
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Image is valid for this chip
|
||||
* - ESP_ERR_INVALID_ARG: img_hdr is NULL or part_type is not APP or BOOTLOADER
|
||||
* - ESP_ERR_INVALID_VERSION: Chip ID or chip revision mismatch
|
||||
* - ESP_ERR_OTA_SPI_MODE_MISMATCH: SPI flash mode does not match (only when app_desc is provided)
|
||||
*/
|
||||
esp_err_t esp_ota_check_image_validity(esp_partition_type_t part_type,
|
||||
const esp_image_header_t *img_hdr,
|
||||
const esp_app_desc_t *app_desc);
|
||||
|
||||
#if SOC_EFUSE_SECURE_BOOT_KEY_DIGESTS > 1 && (CONFIG_SECURE_BOOT_V2_ENABLED || __DOXYGEN__)
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
@@ -60,6 +60,9 @@ const __attribute__((weak)) __attribute__((section(".rodata_desc"))) esp_app_de
|
||||
.min_efuse_blk_rev_full = CONFIG_ESP_EFUSE_BLOCK_REV_MIN_FULL,
|
||||
.max_efuse_blk_rev_full = CONFIG_ESP_EFUSE_BLOCK_REV_MAX_FULL,
|
||||
.mmu_page_size = 31 - __builtin_clz(CONFIG_MMU_PAGE_SIZE),
|
||||
#if !(CONFIG_IDF_TARGET_LINUX || CONFIG_APP_BUILD_TYPE_PURE_RAM_APP)
|
||||
.spi_flash_mode = CONFIG_ESPTOOLPY_FLASHMODE_VAL,
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifndef CONFIG_APP_EXCLUDE_PROJECT_VER_VAR
|
||||
|
||||
@@ -36,7 +36,8 @@ typedef struct {
|
||||
uint16_t min_efuse_blk_rev_full; /*!< Minimal eFuse block revision supported by image, in format: major * 100 + minor */
|
||||
uint16_t max_efuse_blk_rev_full; /*!< Maximal eFuse block revision supported by image, in format: major * 100 + minor */
|
||||
uint8_t mmu_page_size; /*!< MMU page size in log base 2 format */
|
||||
uint8_t reserv3[3]; /*!< reserv3 */
|
||||
uint8_t spi_flash_mode; /*!< SPI flash mode as per CONFIG_ESPTOOLPY_FLASHMODE_VAL for compatibility check during OTA */
|
||||
uint8_t reserv3[2]; /*!< reserv3 */
|
||||
uint32_t reserv2[18]; /*!< reserv2 */
|
||||
} esp_app_desc_t;
|
||||
|
||||
|
||||
@@ -294,6 +294,10 @@ static const esp_err_msg_t esp_err_msg_table[] = {
|
||||
ERR_TBL_IT(ESP_ERR_OTA_ALREADY_IN_PROGRESS), /* 5383 0x1507 Error if another OTA operation is
|
||||
already in progress on the same
|
||||
partition */
|
||||
# endif
|
||||
# ifdef ESP_ERR_OTA_SPI_MODE_MISMATCH
|
||||
ERR_TBL_IT(ESP_ERR_OTA_SPI_MODE_MISMATCH), /* 5384 0x1508 Error if the firmware's SPI flash mode
|
||||
doesn't match the running firmware */
|
||||
# endif
|
||||
// components/efuse/include/esp_efuse.h
|
||||
# ifdef ESP_ERR_EFUSE
|
||||
|
||||
@@ -33,4 +33,13 @@ menu "ESP HTTPS OTA"
|
||||
This enables use of range header in esp_https_ota component.
|
||||
The firmware image will be downloaded over multiple HTTP requests.
|
||||
|
||||
config ESP_HTTPS_OTA_VERIFY_SPI_MODE
|
||||
bool "Verify SPI flash mode compatibility for application during OTA"
|
||||
default y
|
||||
help
|
||||
When enabled, the OTA process will verify that the SPI flash mode of the new
|
||||
application image is compatible with the currently running firmware. This helps
|
||||
prevent flashing firmware with incompatible SPI mode settings which could
|
||||
cause flash write failures.
|
||||
|
||||
endmenu
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2017-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2017-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -681,27 +681,24 @@ esp_err_t esp_https_ota_get_bootloader_img_desc(esp_https_ota_handle_t https_ota
|
||||
return get_description_from_image(https_ota_handle, new_img_info);
|
||||
}
|
||||
|
||||
static esp_err_t esp_ota_verify_chip_id(const void *arg)
|
||||
static const esp_app_desc_t *esp_https_ota_get_app_desc(const void *data_buf)
|
||||
{
|
||||
esp_image_header_t *data = (esp_image_header_t *)(arg);
|
||||
esp_https_ota_dispatch_event(ESP_HTTPS_OTA_VERIFY_CHIP_ID, (void *)(&data->chip_id), sizeof(esp_chip_id_t));
|
||||
|
||||
if (data->chip_id != CONFIG_IDF_FIRMWARE_CHIP_ID) {
|
||||
ESP_LOGE(TAG, "Mismatch chip id, expected %d, found %d", CONFIG_IDF_FIRMWARE_CHIP_ID, data->chip_id);
|
||||
return ESP_ERR_INVALID_VERSION;
|
||||
}
|
||||
return ESP_OK;
|
||||
return (const esp_app_desc_t *)((const uint8_t *)data_buf +
|
||||
sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t));
|
||||
}
|
||||
|
||||
static esp_err_t esp_ota_verify_chip_revision(const void *arg)
|
||||
static esp_err_t esp_https_ota_verify_image(const void *data_buf, esp_partition_type_t part_type, bool verify_spi_mode)
|
||||
{
|
||||
esp_image_header_t *data = (esp_image_header_t *)(arg);
|
||||
esp_https_ota_dispatch_event(ESP_HTTPS_OTA_VERIFY_CHIP_REVISION, (void *)(&data->min_chip_rev_full), sizeof(uint16_t));
|
||||
const esp_image_header_t *img_hdr = (const esp_image_header_t *) data_buf;
|
||||
|
||||
if (!bootloader_common_check_chip_revision_validity(data, true)) {
|
||||
return ESP_ERR_INVALID_VERSION;
|
||||
}
|
||||
return ESP_OK;
|
||||
// Dispatch verification events
|
||||
esp_https_ota_dispatch_event(ESP_HTTPS_OTA_VERIFY_CHIP_ID, (void *)(&img_hdr->chip_id), sizeof(esp_chip_id_t));
|
||||
esp_https_ota_dispatch_event(ESP_HTTPS_OTA_VERIFY_CHIP_REVISION, (void *)(&img_hdr->min_chip_rev_full), sizeof(uint16_t));
|
||||
|
||||
// Get app descriptor only if SPI mode verification is needed
|
||||
const esp_app_desc_t *app_desc = verify_spi_mode ? esp_https_ota_get_app_desc(data_buf) : NULL;
|
||||
|
||||
return esp_ota_check_image_validity(part_type, img_hdr, app_desc);
|
||||
}
|
||||
|
||||
esp_err_t esp_https_ota_perform(esp_https_ota_handle_t https_ota_handle)
|
||||
@@ -761,12 +758,11 @@ esp_err_t esp_https_ota_perform(esp_https_ota_handle_t https_ota_handle)
|
||||
}
|
||||
#endif // CONFIG_ESP_HTTPS_OTA_DECRYPT_CB
|
||||
if (handle->partition.final->type == ESP_PARTITION_TYPE_APP || handle->partition.final->type == ESP_PARTITION_TYPE_BOOTLOADER) {
|
||||
err = esp_ota_verify_chip_id(data_buf);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
err = esp_ota_verify_chip_revision(data_buf);
|
||||
bool verify_spi_mode = false;
|
||||
#if CONFIG_ESP_HTTPS_OTA_VERIFY_SPI_MODE
|
||||
verify_spi_mode = (handle->partition.final->type == ESP_PARTITION_TYPE_APP);
|
||||
#endif
|
||||
err = esp_https_ota_verify_image(data_buf, handle->partition.final->type, verify_spi_mode);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -88,6 +88,17 @@ menu "Serial flasher config"
|
||||
# information get from efuse, so don't care this dout choice.
|
||||
default "dout" if ESPTOOLPY_FLASHMODE_OPI
|
||||
|
||||
# Hidden config providing the numeric value of flash mode
|
||||
# This value will be stored in the esp_app_desc_t structure in the binary
|
||||
# Note: legacy images will have this field set to 0 and hence using 1 to start the count
|
||||
config ESPTOOLPY_FLASHMODE_VAL
|
||||
int
|
||||
default 1 if ESPTOOLPY_FLASHMODE_QIO
|
||||
default 2 if ESPTOOLPY_FLASHMODE_QOUT
|
||||
default 3 if ESPTOOLPY_FLASHMODE_DIO
|
||||
default 4 if ESPTOOLPY_FLASHMODE_DOUT
|
||||
default 5 if ESPTOOLPY_FLASHMODE_OPI
|
||||
|
||||
orsource "../spi_flash/$IDF_TARGET/Kconfig.flash_freq"
|
||||
|
||||
config ESPTOOLPY_FLASHFREQ
|
||||
|
||||
@@ -125,6 +125,10 @@ The ``DROM`` segment of the application binary starts with the :cpp:type:`esp_ap
|
||||
* ``time`` and ``date``: compile time and date
|
||||
* ``idf_ver``: version of ESP-IDF [#f1]_
|
||||
* ``app_elf_sha256``: contains SHA256 hash for the application ELF file
|
||||
* ``min_efuse_blk_rev_full``: minimal eFuse block revision supported by the image, in format: major * 100 + minor
|
||||
* ``max_efuse_blk_rev_full``: maximal eFuse block revision supported by the image, in format: major * 100 + minor
|
||||
* ``mmu_page_size``: MMU page size in log base 2 format
|
||||
* ``spi_flash_mode``: SPI flash mode as per ``CONFIG_ESPTOOLPY_FLASHMODE_VAL`` for compatibility check during OTA
|
||||
|
||||
.. [#f1] The maximum length is 32 characters, including null-termination character. For example, if the length of ``PROJECT_NAME`` exceeds 31 characters, the excess characters will be disregarded.
|
||||
|
||||
|
||||
@@ -125,6 +125,10 @@
|
||||
* ``time`` 和 ``date``:编译时间和日期
|
||||
* ``idf_ver``:ESP-IDF 的版本 [#f1]_
|
||||
* ``app_elf_sha256``:包含应用程序 ELF 文件的 sha256 哈希
|
||||
* ``min_efuse_blk_rev_full``:镜像支持的最小 eFuse 块版本,格式为:major * 100 + minor
|
||||
* ``max_efuse_blk_rev_full``:镜像支持的最大 eFuse 块版本,格式为:major * 100 + minor
|
||||
* ``mmu_page_size``:MMU 页大小,以 log2 格式表示
|
||||
* ``spi_flash_mode``:SPI flash 模式,取自 ``CONFIG_ESPTOOLPY_FLASHMODE_VAL``,用于 OTA 过程中的兼容性检查
|
||||
|
||||
.. [#f1] 最大长度为 32 个字符,其中包括 null 终止符。也就是说,如果 ``PROJECT_NAME`` 的长度超过 31 个字符,超出的字符将被忽略。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user