mirror of
https://github.com/espressif/esp-idf
synced 2025-03-09 17:19:09 -04:00
feat(partition_table): Support primary subtypes partitions
This commit is contained in:
parent
7d179ccd3e
commit
8c4f576f99
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -86,12 +86,12 @@ ESP_STATIC_ASSERT(sizeof(rtc_retain_mem_t) <= ESP_BOOTLOADER_RESERVE_RTC, "Reser
|
||||
#endif // CONFIG_BOOTLOADER_RESERVE_RTC_MEM
|
||||
|
||||
/**
|
||||
* @brief Verify an app image.
|
||||
* @brief Verify an app/bootloader image.
|
||||
*
|
||||
* If encryption is enabled, data will be transparently decrypted.
|
||||
*
|
||||
* @param mode Mode of operation (verify, silent verify, or load).
|
||||
* @param part Partition to load the app from.
|
||||
* @param part Partition to load the app/bootloader from.
|
||||
* @param[inout] data Pointer to the image metadata structure which is be filled in by this function.
|
||||
* 'start_addr' member should be set (to the start address of the image.)
|
||||
* Other fields will all be initialised by this function.
|
||||
@ -113,11 +113,11 @@ ESP_STATIC_ASSERT(sizeof(rtc_retain_mem_t) <= ESP_BOOTLOADER_RESERVE_RTC, "Reser
|
||||
esp_err_t esp_image_verify(esp_image_load_mode_t mode, const esp_partition_pos_t *part, esp_image_metadata_t *data);
|
||||
|
||||
/**
|
||||
* @brief Get metadata of app
|
||||
* @brief Get metadata of app/bootloader
|
||||
*
|
||||
* If encryption is enabled, data will be transparently decrypted.
|
||||
*
|
||||
* @param part Partition to load the app from.
|
||||
* @param part Partition to load the app/bootloader from.
|
||||
* @param[out] metadata Pointer to the image metadata structure which is be filled in by this function.
|
||||
* Fields will all be initialised by this function.
|
||||
*
|
||||
@ -171,7 +171,7 @@ esp_err_t bootloader_load_image(const esp_partition_pos_t *part, esp_image_metad
|
||||
esp_err_t bootloader_load_image_no_verify(const esp_partition_pos_t *part, esp_image_metadata_t *data);
|
||||
|
||||
/**
|
||||
* @brief Verify the bootloader image.
|
||||
* @brief Verify the PRIMARY bootloader image.
|
||||
*
|
||||
* @param[out] If result is ESP_OK and this pointer is non-NULL, it
|
||||
* will be set to the length of the bootloader image.
|
||||
@ -181,7 +181,7 @@ esp_err_t bootloader_load_image_no_verify(const esp_partition_pos_t *part, esp_i
|
||||
esp_err_t esp_image_verify_bootloader(uint32_t *length);
|
||||
|
||||
/**
|
||||
* @brief Verify the bootloader image.
|
||||
* @brief Verify the PRIMARY bootloader image.
|
||||
*
|
||||
* @param[out] Metadata for the image. Only valid if result is ESP_OK.
|
||||
*
|
||||
@ -197,6 +197,25 @@ esp_err_t esp_image_verify_bootloader_data(esp_image_metadata_t *data);
|
||||
*/
|
||||
int esp_image_get_flash_size(esp_image_flash_size_t app_flash_size);
|
||||
|
||||
/**
|
||||
* @brief Get the ota bootloader offset
|
||||
*
|
||||
* The esp_image_verify functions use the offset to distinguish between application and bootloader verifications.
|
||||
* The application must set the OTA bootloader offset before running any verification functions for the OTA bootloader partition.
|
||||
*
|
||||
* @return ota Bootloader offset. UINT32_MAX - not set.
|
||||
*/
|
||||
uint32_t esp_image_bootloader_offset_get(void);
|
||||
|
||||
/**
|
||||
* @brief Set the ota bootloader offset
|
||||
*
|
||||
* The esp_image_verify functions use the offset to distinguish between application and bootloader verifications.
|
||||
* The application must set the OTA bootloader offset before running any verification functions for the OTA bootloader partition.
|
||||
*
|
||||
* @param offset ota Bootloader offset
|
||||
*/
|
||||
void esp_image_bootloader_offset_set(const uint32_t offset);
|
||||
|
||||
typedef struct {
|
||||
uint32_t drom_addr;
|
||||
|
@ -140,13 +140,13 @@ bool bootloader_common_erase_part_type_data(const char *list_erase, bool ota_dat
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t bootloader_common_get_sha256_of_partition (uint32_t address, uint32_t size, int type, uint8_t *out_sha_256)
|
||||
esp_err_t bootloader_common_get_sha256_of_partition(uint32_t address, uint32_t size, int type, uint8_t *out_sha_256)
|
||||
{
|
||||
if (out_sha_256 == NULL || size == 0) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (type == PART_TYPE_APP) {
|
||||
if (type == PART_TYPE_APP || type == PART_TYPE_BOOTLOADER) {
|
||||
const esp_partition_pos_t partition_pos = {
|
||||
.offset = address,
|
||||
.size = size,
|
||||
|
@ -102,6 +102,24 @@ static esp_err_t process_checksum(bootloader_sha256_handle_t sha_handle, uint32_
|
||||
static esp_err_t __attribute__((unused)) verify_secure_boot_signature(bootloader_sha256_handle_t sha_handle, esp_image_metadata_t *data, uint8_t *image_digest, uint8_t *verified_digest);
|
||||
static esp_err_t __attribute__((unused)) verify_simple_hash(bootloader_sha256_handle_t sha_handle, esp_image_metadata_t *data);
|
||||
|
||||
static uint32_t s_bootloader_partition_offset = ESP_PRIMARY_BOOTLOADER_OFFSET;
|
||||
|
||||
uint32_t esp_image_bootloader_offset_get(void)
|
||||
{
|
||||
return s_bootloader_partition_offset;
|
||||
}
|
||||
|
||||
void esp_image_bootloader_offset_set(const uint32_t offset)
|
||||
{
|
||||
s_bootloader_partition_offset = offset;
|
||||
ESP_LOGI(TAG, "Bootloader offsets for PRIMARY: 0x%x, Secondary: 0x%" PRIx32, ESP_PRIMARY_BOOTLOADER_OFFSET, s_bootloader_partition_offset);
|
||||
}
|
||||
|
||||
static bool is_bootloader(uint32_t offset)
|
||||
{
|
||||
return ((offset == ESP_PRIMARY_BOOTLOADER_OFFSET) || (offset == s_bootloader_partition_offset));
|
||||
}
|
||||
|
||||
static esp_err_t image_load(esp_image_load_mode_t mode, const esp_partition_pos_t *part, esp_image_metadata_t *data)
|
||||
{
|
||||
#ifdef BOOTLOADER_BUILD
|
||||
@ -135,7 +153,7 @@ static esp_err_t image_load(esp_image_load_mode_t mode, const esp_partition_pos_
|
||||
// For secure boot V1 on ESP32, we don't calculate SHA or verify signature on bootloaders.
|
||||
// (For non-secure boot, we don't verify any SHA-256 hash appended to the bootloader because
|
||||
// esptool.py may have rewritten the header - rely on esptool.py having verified the bootloader at flashing time, instead.)
|
||||
verify_sha = (part->offset != ESP_BOOTLOADER_OFFSET) && do_verify;
|
||||
verify_sha = !is_bootloader(part->offset) && do_verify;
|
||||
#endif
|
||||
|
||||
if (part->size > SIXTEEN_MB) {
|
||||
@ -199,7 +217,7 @@ static esp_err_t image_load(esp_image_load_mode_t mode, const esp_partition_pos_
|
||||
#if CONFIG_SECURE_BOOT_V2_ENABLED
|
||||
ESP_FAULT_ASSERT(!esp_secure_boot_enabled() || memcmp(image_digest, verified_digest, HASH_LEN) == 0);
|
||||
#else // Secure Boot V1 on ESP32, only verify signatures for apps not bootloaders
|
||||
ESP_FAULT_ASSERT(data->start_addr == ESP_BOOTLOADER_OFFSET || memcmp(image_digest, verified_digest, HASH_LEN) == 0);
|
||||
ESP_FAULT_ASSERT(is_bootloader(data->start_addr) || memcmp(image_digest, verified_digest, HASH_LEN) == 0);
|
||||
#endif
|
||||
|
||||
#endif // SECURE_BOOT_CHECK_SIGNATURE
|
||||
@ -332,7 +350,8 @@ static esp_err_t verify_image_header(uint32_t src_addr, const esp_image_header_t
|
||||
// Checking the chip revision header *will* print a bunch of other info
|
||||
// regardless of silent setting as this may be important, but don't bother checking it
|
||||
// if it looks like the app partition is erased or otherwise garbage
|
||||
CHECK_ERR(bootloader_common_check_chip_validity(image, ESP_IMAGE_APPLICATION));
|
||||
esp_image_type image_type = is_bootloader(src_addr) ? ESP_IMAGE_BOOTLOADER : ESP_IMAGE_APPLICATION;
|
||||
CHECK_ERR(bootloader_common_check_chip_validity(image, image_type));
|
||||
|
||||
if (image->segment_count > ESP_IMAGE_MAX_SEGMENTS) {
|
||||
FAIL_LOAD("image at 0x%"PRIx32" segment count %d exceeds max %d", src_addr, image->segment_count, ESP_IMAGE_MAX_SEGMENTS);
|
||||
@ -695,7 +714,7 @@ static esp_err_t process_segment_data(int segment, intptr_t load_addr, uint32_t
|
||||
// Case II: Bootloader verifying bootloader
|
||||
// The esp_app_desc_t structure is located in DROM and is always in segment #0.
|
||||
// Anti-rollback check and efuse block version check should handle only Case I from above.
|
||||
if (segment == 0 && metadata->start_addr != ESP_BOOTLOADER_OFFSET) {
|
||||
if (segment == 0 && !is_bootloader(metadata->start_addr)) {
|
||||
/* ESP32 doesn't have more memory and more efuse bits for block major version. */
|
||||
#if !CONFIG_IDF_TARGET_ESP32
|
||||
const esp_app_desc_t *app_desc = (const esp_app_desc_t *)src;
|
||||
@ -847,8 +866,8 @@ esp_err_t esp_image_verify_bootloader_data(esp_image_metadata_t *data)
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
const esp_partition_pos_t bootloader_part = {
|
||||
.offset = ESP_BOOTLOADER_OFFSET,
|
||||
.size = ESP_PARTITION_TABLE_OFFSET - ESP_BOOTLOADER_OFFSET,
|
||||
.offset = ESP_PRIMARY_BOOTLOADER_OFFSET,
|
||||
.size = ESP_BOOTLOADER_SIZE,
|
||||
};
|
||||
return esp_image_verify(ESP_IMAGE_VERIFY,
|
||||
&bootloader_part,
|
||||
@ -871,7 +890,7 @@ static esp_err_t process_appended_hash_and_sig(esp_image_metadata_t *data, uint3
|
||||
#if CONFIG_SECURE_BOOT || CONFIG_SECURE_SIGNED_APPS_NO_SECURE_BOOT
|
||||
|
||||
// Case I: Bootloader part
|
||||
if (part_offset == ESP_BOOTLOADER_OFFSET) {
|
||||
if (is_bootloader(part_offset)) {
|
||||
// For bootloader with secure boot v1, signature stays in an independent flash
|
||||
// sector (offset 0x0) and does not get appended to the image.
|
||||
#if CONFIG_SECURE_BOOT_V2_ENABLED
|
||||
@ -1005,7 +1024,7 @@ static esp_err_t verify_secure_boot_signature(bootloader_sha256_handle_t sha_han
|
||||
#if CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME || CONFIG_SECURE_SIGNED_APPS_ECDSA_V2_SCHEME
|
||||
data->image_len = end - data->start_addr + sizeof(ets_secure_boot_signature_t);
|
||||
#elif defined(CONFIG_SECURE_SIGNED_APPS_ECDSA_SCHEME)
|
||||
if (data->start_addr != ESP_BOOTLOADER_OFFSET) {
|
||||
if (!is_bootloader(data->start_addr)) {
|
||||
data->image_len = end - data->start_addr + sizeof(esp_secure_boot_sig_block_t);
|
||||
}
|
||||
#endif
|
||||
|
@ -43,7 +43,7 @@ static const char *TAG = "flash_encrypt";
|
||||
|
||||
/* Static functions for stages of flash encryption */
|
||||
static esp_err_t encrypt_bootloader(void);
|
||||
static esp_err_t encrypt_and_load_partition_table(esp_partition_info_t *partition_table, int *num_partitions);
|
||||
static esp_err_t encrypt_and_load_partition_table(uint32_t offset, esp_partition_info_t *partition_table, int *num_partitions);
|
||||
static esp_err_t encrypt_partition(int index, const esp_partition_info_t *partition);
|
||||
static size_t get_flash_encrypt_cnt_value(void);
|
||||
|
||||
@ -262,12 +262,12 @@ esp_err_t esp_flash_encrypt_contents(void)
|
||||
esp_flash_encryption_enable_key_mgr();
|
||||
#endif
|
||||
|
||||
err = encrypt_bootloader();
|
||||
err = encrypt_bootloader(); // PART_SUBTYPE_BOOTLOADER_PRIMARY
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
err = encrypt_and_load_partition_table(partition_table, &num_partitions);
|
||||
err = encrypt_and_load_partition_table(ESP_PRIMARY_PARTITION_TABLE_OFFSET, partition_table, &num_partitions); // PART_SUBTYPE_PARTITION_TABLE_PRIMARY
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
@ -277,6 +277,14 @@ esp_err_t esp_flash_encrypt_contents(void)
|
||||
|
||||
/* Go through each partition and encrypt if necessary */
|
||||
for (int i = 0; i < num_partitions; i++) {
|
||||
if ((partition_table[i].type == PART_TYPE_BOOTLOADER && partition_table[i].subtype == PART_SUBTYPE_BOOTLOADER_PRIMARY)
|
||||
|| (partition_table[i].type == PART_TYPE_PARTITION_TABLE && partition_table[i].subtype == PART_SUBTYPE_PARTITION_TABLE_PRIMARY)) {
|
||||
/* Skip encryption of PRIMARY partitions for bootloader and partition table.
|
||||
* PRIMARY partitions have already been encrypted above.
|
||||
* We allow to encrypt partitions that are not PRIMARY.
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
err = encrypt_partition(i, &partition_table[i]);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
@ -337,13 +345,13 @@ static esp_err_t encrypt_bootloader(void)
|
||||
|
||||
#if CONFIG_SECURE_BOOT_V2_ENABLED
|
||||
/* The image length obtained from esp_image_verify_bootloader includes the sector boundary padding and the signature block lengths */
|
||||
if (ESP_BOOTLOADER_OFFSET + image_length > ESP_PARTITION_TABLE_OFFSET) {
|
||||
ESP_LOGE(TAG, "Bootloader is too large to fit Secure Boot V2 signature sector and partition table (configured offset 0x%x)", ESP_PARTITION_TABLE_OFFSET);
|
||||
if (image_length > ESP_BOOTLOADER_SIZE) {
|
||||
ESP_LOGE(TAG, "Bootloader is too large to fit Secure Boot V2 signature sector and partition table (configured offset 0x%x)", ESP_PRIMARY_PARTITION_TABLE_OFFSET);
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
}
|
||||
#endif // CONFIG_SECURE_BOOT_V2_ENABLED
|
||||
|
||||
err = esp_flash_encrypt_region(ESP_BOOTLOADER_OFFSET, image_length);
|
||||
err = esp_flash_encrypt_region(ESP_PRIMARY_BOOTLOADER_OFFSET, image_length);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to encrypt bootloader in place: 0x%x", err);
|
||||
return err;
|
||||
@ -368,33 +376,37 @@ static esp_err_t encrypt_bootloader(void)
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t encrypt_and_load_partition_table(esp_partition_info_t *partition_table, int *num_partitions)
|
||||
static esp_err_t read_and_verify_partition_table(uint32_t offset, esp_partition_info_t *partition_table, int *num_partitions)
|
||||
{
|
||||
esp_err_t err;
|
||||
/* Check for plaintext partition table */
|
||||
err = bootloader_flash_read(ESP_PARTITION_TABLE_OFFSET, partition_table, ESP_PARTITION_TABLE_MAX_LEN, false);
|
||||
err = bootloader_flash_read(offset, partition_table, ESP_PARTITION_TABLE_MAX_LEN, false);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to read partition table data");
|
||||
ESP_LOGE(TAG, "Failed to read partition table data at 0x%" PRIx32, offset);
|
||||
return err;
|
||||
}
|
||||
if (esp_partition_table_verify(partition_table, false, num_partitions) == ESP_OK) {
|
||||
ESP_LOGD(TAG, "partition table is plaintext. Encrypting...");
|
||||
esp_err_t err = esp_flash_encrypt_region(ESP_PARTITION_TABLE_OFFSET,
|
||||
FLASH_SECTOR_SIZE);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to encrypt partition table in place. %x", err);
|
||||
return err;
|
||||
}
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to read partition table data - not plaintext?");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
err = esp_partition_table_verify(partition_table, false, num_partitions);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to read partition table data - not plaintext or empty?");
|
||||
}
|
||||
|
||||
/* Valid partition table loaded */
|
||||
ESP_LOGI(TAG, "partition table encrypted and loaded successfully");
|
||||
return ESP_OK;
|
||||
return err;
|
||||
}
|
||||
|
||||
static esp_err_t encrypt_and_load_partition_table(uint32_t offset, esp_partition_info_t *partition_table, int *num_partitions)
|
||||
{
|
||||
esp_err_t err = read_and_verify_partition_table(offset, partition_table, num_partitions);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
ESP_LOGD(TAG, "partition table is plaintext. Encrypting...");
|
||||
err = esp_flash_encrypt_region(offset, FLASH_SECTOR_SIZE);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to encrypt partition table in place. %x", err);
|
||||
return err;
|
||||
}
|
||||
ESP_LOGI(TAG, "partition table encrypted and loaded successfully");
|
||||
return err;
|
||||
}
|
||||
|
||||
static esp_err_t encrypt_partition(int index, const esp_partition_info_t *partition)
|
||||
{
|
||||
@ -402,19 +414,26 @@ static esp_err_t encrypt_partition(int index, const esp_partition_info_t *partit
|
||||
bool should_encrypt = (partition->flags & PART_FLAG_ENCRYPTED);
|
||||
uint32_t size = partition->pos.size;
|
||||
|
||||
if (partition->type == PART_TYPE_APP) {
|
||||
/* check if the partition holds a valid unencrypted app */
|
||||
if (partition->type == PART_TYPE_APP || partition->type == PART_TYPE_BOOTLOADER) {
|
||||
/* check if the partition holds a valid unencrypted app/bootloader */
|
||||
esp_image_metadata_t image_data = {};
|
||||
err = esp_image_verify(ESP_IMAGE_VERIFY,
|
||||
&partition->pos,
|
||||
&image_data);
|
||||
if (partition->type == PART_TYPE_BOOTLOADER) {
|
||||
esp_image_bootloader_offset_set(partition->pos.offset);
|
||||
}
|
||||
err = esp_image_verify(ESP_IMAGE_VERIFY, &partition->pos, &image_data);
|
||||
should_encrypt = (err == ESP_OK);
|
||||
#ifdef CONFIG_SECURE_FLASH_ENCRYPT_ONLY_IMAGE_LEN_IN_APP_PART
|
||||
if (should_encrypt) {
|
||||
if (partition->type == PART_TYPE_APP && should_encrypt) {
|
||||
// Encrypt only the app image instead of encrypting the whole partition
|
||||
size = image_data.image_len;
|
||||
}
|
||||
#endif
|
||||
} else if (partition->type == PART_TYPE_PARTITION_TABLE) {
|
||||
/* check if the partition holds a valid unencrypted partition table */
|
||||
esp_partition_info_t partition_table[ESP_PARTITION_TABLE_MAX_ENTRIES];
|
||||
int num_partitions;
|
||||
err = read_and_verify_partition_table(partition->pos.offset, partition_table, &num_partitions);
|
||||
should_encrypt = (err == ESP_OK && num_partitions != 0);
|
||||
} else if ((partition->type == PART_TYPE_DATA && partition->subtype == PART_SUBTYPE_DATA_OTA)
|
||||
|| (partition->type == PART_TYPE_DATA && partition->subtype == PART_SUBTYPE_DATA_NVS_KEYS)) {
|
||||
/* check if we have ota data partition and the partition should be encrypted unconditionally */
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -52,10 +52,11 @@ esp_err_t esp_partition_table_verify(const esp_partition_info_t *partition_table
|
||||
}
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
//MD5 checksum matches and we continue with the next interation in
|
||||
//MD5 checksum matches and we continue with the next iteration in
|
||||
//order to detect the end of the partition table
|
||||
md5_found = 1;
|
||||
} else if (part->magic == 0xFFFF
|
||||
} else if (num_parts != 0 // the first record cannot be empty, otherwise the whole table is empty
|
||||
&& part->magic == 0xFFFF
|
||||
&& part->type == PART_TYPE_END
|
||||
&& part->subtype == PART_SUBTYPE_END) {
|
||||
ESP_LOGD(TAG, "partition table verified, %d entries", num_parts);
|
||||
|
@ -6,6 +6,14 @@ if(NOT ${target} STREQUAL "linux")
|
||||
list(APPEND priv_req esptool_py)
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED BOOTLOADER_OFFSET) # For Linux target
|
||||
if(DEFINED CONFIG_BOOTLOADER_OFFSET_IN_FLASH)
|
||||
set(BOOTLOADER_OFFSET ${CONFIG_BOOTLOADER_OFFSET_IN_FLASH})
|
||||
else()
|
||||
set(BOOTLOADER_OFFSET 0)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
idf_component_register(PRIV_REQUIRES ${priv_req})
|
||||
|
||||
if(non_os_build)
|
||||
@ -55,8 +63,12 @@ idf_build_get_property(build_dir BUILD_DIR)
|
||||
idf_build_get_property(python PYTHON)
|
||||
idf_build_get_property(extra_subtypes EXTRA_PARTITION_SUBTYPES)
|
||||
|
||||
set(gen_partition_table "${python}" "${CMAKE_CURRENT_SOURCE_DIR}/gen_esp32part.py" "-q"
|
||||
"--offset" "${PARTITION_TABLE_OFFSET}" "${md5_opt}" "${flashsize_opt}"
|
||||
set(gen_partition_table "${python}" "${CMAKE_CURRENT_SOURCE_DIR}/gen_esp32part.py"
|
||||
"-q"
|
||||
"--offset" "${PARTITION_TABLE_OFFSET}"
|
||||
"--primary-bootloader-offset" "${BOOTLOADER_OFFSET}"
|
||||
"${md5_opt}"
|
||||
"${flashsize_opt}"
|
||||
"${partition_secure_opt}" ${extra_partition_subtypes} "--")
|
||||
|
||||
set(partition_table_display
|
||||
|
@ -58,11 +58,11 @@ def get_ptype_as_int(ptype):
|
||||
# Keep this map in sync with esp_partition_subtype_t enum in esp_partition.h
|
||||
SUBTYPES = {
|
||||
BOOTLOADER_TYPE: {
|
||||
# 'primary': 0x00, # The tool does not allow to define this partition yet.
|
||||
'primary': 0x00,
|
||||
'ota': 0x01,
|
||||
},
|
||||
PARTITION_TABLE_TYPE: {
|
||||
# 'primary': 0x00, # The tool does not allow to define this partition yet.
|
||||
'primary': 0x00,
|
||||
'ota': 0x01,
|
||||
},
|
||||
APP_TYPE: {
|
||||
@ -153,6 +153,7 @@ quiet = False
|
||||
md5sum = True
|
||||
secure = SECURE_NONE
|
||||
offset_part_table = 0
|
||||
primary_bootloader_offset = None
|
||||
|
||||
|
||||
def status(msg):
|
||||
@ -210,6 +211,11 @@ class PartitionTable(list):
|
||||
# fix up missing offsets & negative sizes
|
||||
last_end = offset_part_table + PARTITION_TABLE_SIZE # first offset after partition table
|
||||
for e in res:
|
||||
is_primary_bootloader = (e.type == BOOTLOADER_TYPE and e.subtype == SUBTYPES[e.type]['primary'])
|
||||
is_primary_partition_table = (e.type == PARTITION_TABLE_TYPE and e.subtype == SUBTYPES[e.type]['primary'])
|
||||
if is_primary_bootloader or is_primary_partition_table:
|
||||
# They do not participate in the restoration of missing offsets
|
||||
continue
|
||||
if e.offset is not None and e.offset < last_end:
|
||||
if e == res[0]:
|
||||
raise InputError('CSV Error at line %d: Partitions overlap. Partition sets offset 0x%x. '
|
||||
@ -280,7 +286,10 @@ class PartitionTable(list):
|
||||
last = None
|
||||
for p in sorted(self, key=lambda x:x.offset):
|
||||
if p.offset < offset_part_table + PARTITION_TABLE_SIZE:
|
||||
raise InputError('Partition offset 0x%x is below 0x%x' % (p.offset, offset_part_table + PARTITION_TABLE_SIZE))
|
||||
is_primary_bootloader = (p.type == BOOTLOADER_TYPE and p.subtype == SUBTYPES[p.type]['primary'])
|
||||
is_primary_partition_table = (p.type == PARTITION_TABLE_TYPE and p.subtype == SUBTYPES[p.type]['primary'])
|
||||
if not (is_primary_bootloader or is_primary_partition_table):
|
||||
raise InputError('Partition offset 0x%x is below 0x%x' % (p.offset, offset_part_table + PARTITION_TABLE_SIZE))
|
||||
if last is not None and p.offset < last.offset + last.size:
|
||||
raise InputError('Partition at 0x%x overlaps 0x%x-0x%x' % (p.offset, last.offset, last.offset + last.size - 1))
|
||||
last = p
|
||||
@ -388,8 +397,8 @@ class PartitionDefinition(object):
|
||||
res.name = fields[0]
|
||||
res.type = res.parse_type(fields[1])
|
||||
res.subtype = res.parse_subtype(fields[2])
|
||||
res.offset = res.parse_address(fields[3])
|
||||
res.size = res.parse_address(fields[4])
|
||||
res.offset = res.parse_address(fields[3], res.type, res.subtype)
|
||||
res.size = res.parse_size(fields[4], res.type)
|
||||
if res.size is None:
|
||||
raise InputError("Size field can't be empty")
|
||||
|
||||
@ -443,7 +452,24 @@ class PartitionDefinition(object):
|
||||
return SUBTYPES[DATA_TYPE]['undefined']
|
||||
return parse_int(strval, SUBTYPES.get(self.type, {}))
|
||||
|
||||
def parse_address(self, strval):
|
||||
def parse_size(self, strval, ptype):
|
||||
if ptype == BOOTLOADER_TYPE:
|
||||
if primary_bootloader_offset is None:
|
||||
raise InputError(f'Primary bootloader offset is not defined. Please use --primary-bootloader-offset')
|
||||
return offset_part_table - primary_bootloader_offset
|
||||
if ptype == PARTITION_TABLE_TYPE:
|
||||
return PARTITION_TABLE_SIZE
|
||||
if strval == '':
|
||||
return None # PartitionTable will fill in default
|
||||
return parse_int(strval)
|
||||
|
||||
def parse_address(self, strval, ptype, psubtype):
|
||||
if ptype == BOOTLOADER_TYPE and psubtype == SUBTYPES[ptype]['primary']:
|
||||
if primary_bootloader_offset is None:
|
||||
raise InputError(f'Primary bootloader offset is not defined. Please use --primary-bootloader-offset')
|
||||
return primary_bootloader_offset
|
||||
if ptype == PARTITION_TABLE_TYPE and psubtype == SUBTYPES[ptype]['primary']:
|
||||
return offset_part_table
|
||||
if strval == '':
|
||||
return None # PartitionTable will fill in default
|
||||
return parse_int(strval)
|
||||
@ -563,6 +589,7 @@ def main():
|
||||
global md5sum
|
||||
global offset_part_table
|
||||
global secure
|
||||
global primary_bootloader_offset
|
||||
parser = argparse.ArgumentParser(description='ESP32 partition table utility')
|
||||
|
||||
parser.add_argument('--flash-size', help='Optional flash size limit, checks partition table fits in flash',
|
||||
@ -573,6 +600,7 @@ def main():
|
||||
'enabled by default and this flag does nothing.', action='store_true')
|
||||
parser.add_argument('--quiet', '-q', help="Don't print non-critical status messages to stderr", action='store_true')
|
||||
parser.add_argument('--offset', '-o', help='Set offset partition table', default='0x8000')
|
||||
parser.add_argument('--primary-bootloader-offset', help='Set primary bootloader offset', default=None)
|
||||
parser.add_argument('--secure', help='Require app partitions to be suitable for secure boot', nargs='?', const=SECURE_V1, choices=[SECURE_V1, SECURE_V2])
|
||||
parser.add_argument('--extra-partition-subtypes', help='Extra partition subtype entries', nargs='*')
|
||||
parser.add_argument('input', help='Path to CSV or binary file to parse.', type=argparse.FileType('rb'))
|
||||
@ -585,6 +613,13 @@ def main():
|
||||
md5sum = not args.disable_md5sum
|
||||
secure = args.secure
|
||||
offset_part_table = int(args.offset, 0)
|
||||
if args.primary_bootloader_offset is not None:
|
||||
primary_bootloader_offset = int(args.primary_bootloader_offset, 0)
|
||||
if primary_bootloader_offset >= offset_part_table:
|
||||
raise InputError(
|
||||
f'Unsupported configuration. Primary bootloader must be below partition table. '
|
||||
f'Check --primary-bootloader-offset={primary_bootloader_offset:#x} and --offset={offset_part_table:#x}'
|
||||
)
|
||||
if args.extra_partition_subtypes:
|
||||
add_extra_subtypes(args.extra_partition_subtypes)
|
||||
|
||||
|
@ -56,12 +56,13 @@ PARTITION_BOOT_DEFAULT = _PartitionId()
|
||||
|
||||
class ParttoolTarget():
|
||||
|
||||
def __init__(self, port=None, baud=None, partition_table_offset=PARTITION_TABLE_OFFSET, partition_table_file=None,
|
||||
def __init__(self, port=None, baud=None, partition_table_offset=PARTITION_TABLE_OFFSET, primary_bootloader_offset=None, partition_table_file=None,
|
||||
esptool_args=[], esptool_write_args=[], esptool_read_args=[], esptool_erase_args=[]):
|
||||
self.port = port
|
||||
self.baud = baud
|
||||
|
||||
gen.offset_part_table = partition_table_offset
|
||||
gen.primary_bootloader_offset = primary_bootloader_offset
|
||||
|
||||
def parse_esptool_args(esptool_args):
|
||||
results = list()
|
||||
@ -239,6 +240,7 @@ def main():
|
||||
parser.add_argument('--baud', '-b', help='baudrate to use', type=int)
|
||||
|
||||
parser.add_argument('--partition-table-offset', '-o', help='offset to read the partition table from', type=str)
|
||||
parser.add_argument('--primary-bootloader-offset', help='offset for primary bootloader', type=str)
|
||||
parser.add_argument('--partition-table-file', '-f', help='file (CSV/binary) to read the partition table from; \
|
||||
overrides device attached to specified port as the partition table source when defined')
|
||||
|
||||
@ -313,6 +315,9 @@ def main():
|
||||
if args.partition_table_offset:
|
||||
target_args['partition_table_offset'] = int(args.partition_table_offset, 0)
|
||||
|
||||
if args.primary_bootloader_offset:
|
||||
target_args['primary_bootloader_offset'] = int(args.primary_bootloader_offset, 0)
|
||||
|
||||
if args.esptool_args:
|
||||
target_args['esptool_args'] = args.esptool_args
|
||||
|
||||
|
@ -1,4 +1,11 @@
|
||||
set(PARTITION_TABLE_OFFSET ${CONFIG_PARTITION_TABLE_OFFSET})
|
||||
if(NOT DEFINED BOOTLOADER_OFFSET) # For Linux target
|
||||
if(DEFINED CONFIG_BOOTLOADER_OFFSET_IN_FLASH)
|
||||
set(BOOTLOADER_OFFSET ${CONFIG_BOOTLOADER_OFFSET_IN_FLASH})
|
||||
else()
|
||||
set(BOOTLOADER_OFFSET 0)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(PARTITION_TABLE_CHECK_SIZES_TOOL_PATH "${CMAKE_CURRENT_LIST_DIR}/check_sizes.py")
|
||||
|
||||
@ -58,6 +65,7 @@ function(partition_table_get_partition_info result get_part_info_args part_info)
|
||||
execute_process(COMMAND ${python}
|
||||
${idf_path}/components/partition_table/parttool.py -q
|
||||
--partition-table-offset ${PARTITION_TABLE_OFFSET}
|
||||
--primary-bootloader-offset ${BOOTLOADER_OFFSET}
|
||||
--partition-table-file ${PARTITION_CSV_PATH}
|
||||
get_partition_info ${get_part_info_args} --info ${part_info}
|
||||
${extra_partition_subtypes}
|
||||
|
@ -1,8 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
from __future__ import division, print_function
|
||||
|
||||
import csv
|
||||
import io
|
||||
import os
|
||||
@ -64,6 +62,9 @@ def _strip_trailing_ffs(binary_table):
|
||||
|
||||
|
||||
class CSVParserTests(Py23TestCase):
|
||||
def tearDown(self):
|
||||
gen_esp32part.primary_bootloader_offset = None
|
||||
gen_esp32part.offset_part_table = 0
|
||||
|
||||
def test_simple_partition(self):
|
||||
table = gen_esp32part.PartitionTable.from_csv(SIMPLE_CSV)
|
||||
@ -182,6 +183,42 @@ first, app, ota_0, 0x200000, 1M
|
||||
t = gen_esp32part.PartitionTable.from_csv(csv)
|
||||
t.verify()
|
||||
|
||||
def test_bootloader_and_part_table_partitions_no_sdkconfig(self):
|
||||
csv = """
|
||||
bootloader, bootloader, primary, N/A, N/A
|
||||
"""
|
||||
with self.assertRaisesRegex(gen_esp32part.InputError, 'Primary bootloader offset is not defined. Please use --primary-bootloader-offset'):
|
||||
gen_esp32part.PartitionTable.from_csv(csv)
|
||||
|
||||
def test_bootloader_and_part_table_partitions(self):
|
||||
csv = """
|
||||
bootloader, bootloader, primary, N/A, N/A
|
||||
partition_table, partition_table, primary, N/A, N/A
|
||||
FactoryApp, app, factory, , 1M
|
||||
OtaBTLDR, bootloader, ota, , N/A
|
||||
OtaPrtTable, partition_table, ota, , N/A
|
||||
"""
|
||||
gen_esp32part.primary_bootloader_offset = 0x1000
|
||||
gen_esp32part.offset_part_table = 0x9000
|
||||
part_table_size = 0x1000
|
||||
bootloader_size = gen_esp32part.offset_part_table - gen_esp32part.primary_bootloader_offset
|
||||
t = gen_esp32part.PartitionTable.from_csv(csv)
|
||||
t.verify()
|
||||
# bootloader
|
||||
self.assertEqual(t[0].offset, gen_esp32part.primary_bootloader_offset)
|
||||
self.assertEqual(t[0].size, bootloader_size)
|
||||
# partition_table
|
||||
self.assertEqual(t[1].offset, gen_esp32part.offset_part_table)
|
||||
self.assertEqual(t[1].size, part_table_size)
|
||||
# FactoryApp
|
||||
self.assertEqual(t[2].offset, 0x10000)
|
||||
# OtaBTLDR
|
||||
self.assertEqual(t[3].offset, 0x110000)
|
||||
self.assertEqual(t[3].size, bootloader_size)
|
||||
# OtaPrtTable
|
||||
self.assertEqual(t[4].offset, 0x118000)
|
||||
self.assertEqual(t[4].size, part_table_size)
|
||||
|
||||
|
||||
class BinaryOutputTests(Py23TestCase):
|
||||
def test_binary_entry(self):
|
||||
@ -369,7 +406,7 @@ class CommandLineTests(Py23TestCase):
|
||||
from_csv = gen_esp32part.PartitionTable.from_csv(f.read())
|
||||
self.assertEqual(_strip_trailing_ffs(from_csv.to_binary()), LONGER_BINARY_TABLE)
|
||||
|
||||
# run gen_esp32part.py to conver the CSV to binary again
|
||||
# run gen_esp32part.py to convert the CSV to binary again
|
||||
output = subprocess.check_output([sys.executable, '../gen_esp32part.py',
|
||||
csvpath, binpath], stderr=subprocess.STDOUT)
|
||||
self.assertNotIn(b'WARNING', output)
|
||||
|
@ -73,6 +73,20 @@ The CSV format is the same format as printed in the summaries shown above. Howev
|
||||
* Each non-comment line in the CSV file is a partition definition.
|
||||
* The ``Offset`` field for each partition is empty. The ``gen_esp32part.py`` tool fills in each blank offset, starting after the partition table and making sure each partition is aligned correctly.
|
||||
|
||||
Here is an example of a CSV partition table that includes bootloader and partition table partitions:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
# ESP-IDF Partition Table
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
bootloader, bootloader, primary, N/A, N/A,
|
||||
partition_table, partition_table, primary, N/A, N/A,
|
||||
nvs, data, nvs, , 0x6000,
|
||||
phy_init, data, phy, , 0x1000,
|
||||
factory, app, factory, , 1M,
|
||||
|
||||
The ``gen_esp32part.py`` tool will replace each ``N/A`` with appropriate values based on the selected Kconfig options: {IDF_TARGET_CONFIG_BOOTLOADER_OFFSET_IN_FLASH} for the bootloader offset and :ref:`CONFIG_PARTITION_TABLE_OFFSET` for the partition table offset.
|
||||
|
||||
Name Field
|
||||
~~~~~~~~~~
|
||||
|
||||
@ -83,10 +97,10 @@ Type Field
|
||||
|
||||
Partition type field can be specified as a name or a number 0-254 (or as hex 0x00-0xFE). Types 0x00-0x3F are reserved for ESP-IDF core functions.
|
||||
|
||||
- ``app`` (0x00).
|
||||
- ``data`` (0x01).
|
||||
- ``bootloader`` (0x02). By default, this partition is not included in any CSV partition table files because it is not required and does not impact the system's functionality. It is only useful for the bootloader OTA update. Even if this partition is not present in the CSV file, it is still possible to perform the OTA. Please note that if you specify this partition in the CSV file, its address and size must match Kconfigs.
|
||||
- ``partition_table`` (0x03).
|
||||
- ``app`` (0x00),
|
||||
- ``data`` (0x01),
|
||||
- ``bootloader`` (0x02). By default, this partition is not included in any CSV partition table files in ESP-IDF because it is not required and does not impact the system's functionality. It is only useful for the bootloader OTA updates and flash partitioning. Even if this partition is not present in the CSV file, it is still possible to perform the OTA.
|
||||
- ``partition_table`` (0x03). By default, this partition also is not included in any CSV partition table files in ESP-IDF.
|
||||
- 0x40-0xFE are reserved for **custom partition types**. If your app needs to store data in a format not already supported by ESP-IDF, then use a value from this range.
|
||||
|
||||
See :cpp:type:`esp_partition_type_t` for the enum definitions for ``app`` and ``data`` partitions.
|
||||
@ -120,13 +134,17 @@ See enum :cpp:type:`esp_partition_subtype_t` for the full list of subtypes defin
|
||||
|
||||
* When type is ``bootloader``, the SubType field can be specified as:
|
||||
|
||||
- ``primary`` (0x00). It is the so-called second stage bootloader, which is placed at the {IDF_TARGET_CONFIG_BOOTLOADER_OFFSET_IN_FLASH} address in the flash. The ``gen_esp32part.py`` does not allow to have this partition in the CSV file for now.
|
||||
- ``ota`` (0x01). It is a temporary bootloader partition used by the bootloader OTA update functionality for downloading a new image.
|
||||
- ``primary`` (0x00). This is the 2nd stage bootloader, located at the {IDF_TARGET_CONFIG_BOOTLOADER_OFFSET_IN_FLASH} address in flash memory. The tool automatically determines the appropriate size and offset for this subtype, so any size or offset specified for this subtype will be ignored. You can either leave these fields blank or use ``N/A`` as a placeholder.
|
||||
- ``ota`` (0x01). This is a temporary bootloader partition used by the bootloader OTA update functionality to download a new image. The tool ignores the size for this subtype, allowing you to leave it blank or use ``N/A``. You can only specify an offset, or leave it blank to have the tool calculate it based on the offsets of previously used partitions.
|
||||
|
||||
The size of the bootloader type is calculated by the ``gen_esp32part.py`` tool based on the specified ``--offset`` (the partition table offset) and ``--primary-partition-offset`` arguments. Specifically, the bootloader size is defined as (:ref:`CONFIG_PARTITION_TABLE_OFFSET` - {IDF_TARGET_CONFIG_BOOTLOADER_OFFSET_IN_FLASH}). This calculated size applies to all subtypes of the bootloader.
|
||||
|
||||
* When type is ``partition_table``, the SubType field can be specified as:
|
||||
|
||||
- ``primary`` (0x00). It is the primary partition table, which is placed at the :ref:`CONFIG_PARTITION_TABLE_OFFSET` address in the flash. The ``gen_esp32part.py`` does not allow to have this partition in the CSV file for now.
|
||||
- ``ota`` (0x01). It is a temporary partition table partition used by the partition table OTA update functionality for downloading a new image.
|
||||
- ``primary`` (0x00). This is the primary partition table, located at the :ref:`CONFIG_PARTITION_TABLE_OFFSET` address in flash memory. The tool automatically determines the appropriate size and offset for this subtype, so any size or offset specified for this subtype will be ignored. You can either leave these fields blank or use ``N/A`` as a placeholder.
|
||||
- ``ota`` (0x01). It is a temporary partition table partition used by the partition table OTA update functionality for downloading a new image. The tool ignores the size for this subtype, allowing you to leave it blank or use ``N/A``. You can specify an offset, or leave it blank, in which case the tool will calculate it based on the offsets of previously allocated partitions.
|
||||
|
||||
The size for the ``partition_table`` type is fixed at ``0x1000`` and applies uniformly across all subtypes of ``partition_table``.
|
||||
|
||||
* When type is ``data``, the subtype field can be specified as ``ota`` (0x00), ``phy`` (0x01), ``nvs`` (0x02), nvs_keys (0x04), or a range of other component-specific subtypes (see :cpp:type:`subtype enum <esp_partition_subtype_t>`).
|
||||
|
||||
@ -185,7 +203,9 @@ Offset & Size
|
||||
- Partitions of type ``app`` have to be placed at offsets aligned to 0x10000 (64 KB). If you leave the offset field blank, ``gen_esp32part.py`` will automatically align the partition. If you specify an unaligned offset for an ``app`` partition, the tool will return an error.
|
||||
- Partitions of type ``app`` should have the size aligned to the flash sector size (4 KB). If you specify an unaligned size for an ``app`` partition, the tool will return an error.
|
||||
:SOC_SECURE_BOOT_V1: - If Secure Boot V1 is enabled, then the partition of type ``app`` needs to have size aligned to 0x10000 (64 KB) boundary.
|
||||
:SOC_SECURE_BOOT_V1: - The ``bootloader`` offset and size are not affected by the Secure Boot V1 option. Whether Secure Boot V1 is enabled or not, the bootloader remains the same size and does not include the secure digest, which is flashed at offset 0x0 in the flash and occupies one sector (4096 bytes).
|
||||
- Sizes and offsets can be specified as decimal numbers, hex numbers with the prefix 0x, or size multipliers K or M (1024 and 1024*1024 bytes).
|
||||
- For ``bootloader`` and ``partition_table`` types, specifying ``N/A`` for size and offset in the CSV file means that these values are automatically determined by the tool and cannot be manually defined. This requires setting the ``--offset`` and ``--primary-partition-offset`` arguments of ``gen_esp32part.py``.
|
||||
|
||||
If you want the partitions in the partition table to work relative to any placement (:ref:`CONFIG_PARTITION_TABLE_OFFSET`) of the table itself, leave the offset field (in CSV file) for all partitions blank. Similarly, if changing the partition table offset then be aware that all blank partition offsets may change to match, and that any fixed offsets may now collide with the partition table (causing an error).
|
||||
|
||||
@ -198,7 +218,15 @@ Two flags are currently supported, ``encrypted`` and ``readonly``:
|
||||
|
||||
.. note::
|
||||
|
||||
``app`` type partitions will always be encrypted, regardless of whether this flag is set or not.
|
||||
The following type partitions will always be encrypted, regardless of whether this flag is set or not:
|
||||
|
||||
.. list::
|
||||
|
||||
- ``app``,
|
||||
- ``bootloader``,
|
||||
- ``partition_table``,
|
||||
- type ``data`` and subtype ``ota``,
|
||||
- type ``data`` and subtype ``nvs_keys``.
|
||||
|
||||
- If ``readonly`` flag is set, the partition will be read-only. This flag is only supported for ``data`` type partitions except ``ota``` and ``coredump``` subtypes. This flag can help to protect against accidental writes to a partition that contains critical device-specific configuration data, e.g., factory data partition.
|
||||
|
||||
|
@ -1,7 +1,9 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, , 0x4000,
|
||||
phy_init, data, phy, , 0x1000,
|
||||
storage, data, 0xff, , 0x1000, encrypted
|
||||
nvs_key, data, nvs_keys, , 0x1000, encrypted,
|
||||
emul_efuse, data, efuse, , 0x2000,
|
||||
factory, app, factory, , 1M,
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
bootloader, bootloader, primary, N/A, N/A,
|
||||
partition_table, partition_table, primary, N/A, N/A,
|
||||
nvs, data, nvs, , 0x4000,
|
||||
phy_init, data, phy, , 0x1000,
|
||||
storage, data, 0xff, , 0x1000, encrypted
|
||||
nvs_key, data, nvs_keys, , 0x1000, encrypted,
|
||||
emul_efuse, data, efuse, , 0x2000,
|
||||
factory, app, factory, , 1M,
|
||||
|
|
Loading…
x
Reference in New Issue
Block a user