diff --git a/components/partition_table/Kconfig.projbuild b/components/partition_table/Kconfig.projbuild index 208b75d9eb..57e0cd2624 100644 --- a/components/partition_table/Kconfig.projbuild +++ b/components/partition_table/Kconfig.projbuild @@ -121,7 +121,7 @@ menu "Partition Table" config PARTITION_TABLE_MD5 bool "Generate an MD5 checksum for the partition table" default y - depends on !ESP32_COMPATIBLE_PRE_V3_1_BOOTLOADERS + depends on !ESP32_COMPATIBLE_PRE_V3_1_BOOTLOADERS && !IDF_TARGET_LINUX help Generate an MD5 checksum for the partition table for protecting the integrity of the table. The generation should be turned off for legacy diff --git a/components/spi_flash/CMakeLists.txt b/components/spi_flash/CMakeLists.txt index 364c35f610..b89e907557 100644 --- a/components/spi_flash/CMakeLists.txt +++ b/components/spi_flash/CMakeLists.txt @@ -1,4 +1,17 @@ idf_build_get_property(target IDF_TARGET) +if(${target} STREQUAL "linux") + set(srcs "partition.c" + "partition_linux.c") + idf_component_get_property(hal_dir hal COMPONENT_DIR) + idf_component_get_property(bootloader_support_dir bootloader_support COMPONENT_DIR) + + idf_component_register(SRCS "${srcs}" + INCLUDE_DIRS include ${hal_dir}/include ${bootloader_support_dir}/include + PRIV_INCLUDE_DIRS include/spi_flash + PRIV_REQUIRES linux partition_table) + return() +endif() + if(BOOTLOADER_BUILD) set(cache_srcs "") set(priv_requires bootloader_support soc) @@ -11,6 +24,7 @@ else() ) set(srcs "partition.c" + "partition_target.c" ) if(CONFIG_ESPTOOLPY_OCT_FLASH) diff --git a/components/spi_flash/host_test/partition_api_test/CMakeLists.txt b/components/spi_flash/host_test/partition_api_test/CMakeLists.txt new file mode 100644 index 0000000000..e69f345495 --- /dev/null +++ b/components/spi_flash/host_test/partition_api_test/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.5) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +set(COMPONENTS main) +# Freertos is included via common components, however, currently only the mock component is compatible with linux +# target. +list(APPEND EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/mocks/freertos/") + +project(partition_api_test) diff --git a/components/spi_flash/host_test/partition_api_test/README.md b/components/spi_flash/host_test/partition_api_test/README.md new file mode 100644 index 0000000000..8e2e38e920 --- /dev/null +++ b/components/spi_flash/host_test/partition_api_test/README.md @@ -0,0 +1,18 @@ +| Supported Targets | Linux | +| ----------------- | ----- | + +This is a test project for partition-related APIs on Linux target (CONFIG_IDF_TARGET_LINUX). + +# Build +Source the IDF environment as usual. + +Once this is done, build the application: +```bash +idf.py build partition-table +``` +Note that for the time being, `partition-table` target needs to be built manually. + +# Run +```bash +`build/partition_api_test.elf` +``` diff --git a/components/spi_flash/host_test/partition_api_test/main/CMakeLists.txt b/components/spi_flash/host_test/partition_api_test/main/CMakeLists.txt new file mode 100644 index 0000000000..408a6111e1 --- /dev/null +++ b/components/spi_flash/host_test/partition_api_test/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "partition_api_test.cpp" + REQUIRES spi_flash) diff --git a/components/spi_flash/host_test/partition_api_test/main/partition_api_test.cpp b/components/spi_flash/host_test/partition_api_test/main/partition_api_test.cpp new file mode 100644 index 0000000000..4ee8849496 --- /dev/null +++ b/components/spi_flash/host_test/partition_api_test/main/partition_api_test.cpp @@ -0,0 +1,106 @@ +/* + * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + * + * Linux host partition API test + */ + +#include +#include "esp_err.h" +#include "esp_partition.h" +#include "esp_private/partition_linux.h" + +int main(int argc, char **argv) +{ + printf("Partition API Linux emulation test: "); + + //////////////////////////////////////// + //PARTITION LOOKUP: + + //1. esp_partition_find (label=STORAGE) + esp_partition_iterator_t iter = esp_partition_find(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "storage"); + assert(iter); + + //2. esp_partition_get (label=STORAGE) + const esp_partition_t *part = esp_partition_get(iter); + assert(part); + + //3. esp_partition_iterator_release (label STORAGE iter): assumed OK + esp_partition_iterator_release(iter); + + //////////////////////////////////////// + //ITERATORS, PARTITION PROPERTIES: + + //4. esp_partition_find_first (type=APP, subtype=ANY) + const esp_partition_t *partition_app = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_ANY, NULL); + assert(partition_app); + + //5. enumerate all APP partitions + iter = esp_partition_find(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_ANY, NULL); + assert(iter); + size_t counter = 0; + + while (iter != NULL) { + const esp_partition_t *part_data = esp_partition_get(iter); + counter++; + assert(part_data); + iter = esp_partition_next(iter); + } + esp_partition_iterator_release(iter); + + //6. enumerate all DATA partitions and print details for each + iter = esp_partition_find(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, NULL); + assert(iter); + counter = 0; + + while (iter != NULL) { + const esp_partition_t *part_data = esp_partition_get(iter); + counter++; + assert(part_data); + iter = esp_partition_next(iter); + } + esp_partition_iterator_release(iter); + + //7. esp_partition_find_first (type=DATA, label=STORAGE) + const esp_partition_t *partition_data = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "storage"); + assert(partition_data); + + ///////////////////////////////////// + //OPERATIONS + + uint8_t buff[] = "ABCDEFGHIJKLMNOP"; + size_t bufsize = sizeof(buff); + size_t off = 0x100; + + //8. esp_partition_write/raw + esp_err_t err = esp_partition_write(partition_data, off, (const void *)buff, bufsize); + assert(err == ESP_OK); + + //9. esp_partition_read/raw + uint8_t buffout[32] = {0}; + err = esp_partition_read(partition_data, off, (void *)buffout, bufsize); + assert(err == ESP_OK); + + //10. esp_partition_erase_range + uint8_t buferase[bufsize]; + memset(buferase, 0xFF, bufsize); + memset(buffout, 0, sizeof(buffout)); + size_t sector_off = 0; //erase works per whole sector - offset must be aligned to 4kB boundaries + + err = esp_partition_erase_range(partition_data, sector_off, SPI_FLASH_SEC_SIZE); + assert(esp_partition_read(partition_data, off, (void *)buffout, bufsize) == ESP_OK); + assert(err == ESP_OK && memcmp(buffout, buferase, bufsize) == 0); + + //11. esp_partition_verify (partition_data) + const esp_partition_t *verified_partition = esp_partition_verify(partition_data); + assert(verified_partition != NULL); + + //12. release SPI FLASH emulation block from memory + err = esp_partition_file_munmap(); + assert(err == ESP_OK); + + printf("OK\n"); + + return 0; +} diff --git a/components/spi_flash/host_test/partition_api_test/partition_table.csv b/components/spi_flash/host_test/partition_api_test/partition_table.csv new file mode 100644 index 0000000000..a70efe6a75 --- /dev/null +++ b/components/spi_flash/host_test/partition_api_test/partition_table.csv @@ -0,0 +1,6 @@ +# Name, Type, SubType, Offset, Size, Flags +# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap +nvs, data, nvs, 0x9000, 0x6000, +phy_init, data, phy, 0xf000, 0x1000, +factory, app, factory, 0x10000, 1M, +storage, data, , , 0x40000, diff --git a/components/spi_flash/host_test/partition_api_test/sdkconfig.defaults b/components/spi_flash/host_test/partition_api_test/sdkconfig.defaults new file mode 100644 index 0000000000..47666febd9 --- /dev/null +++ b/components/spi_flash/host_test/partition_api_test/sdkconfig.defaults @@ -0,0 +1,7 @@ +CONFIG_IDF_TARGET="linux" +CONFIG_COMPILER_CXX_EXCEPTIONS=y +CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=n +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partition_table.csv" +CONFIG_ESPTOOLPY_FLASHSIZE="4MB" +CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y diff --git a/components/spi_flash/include/esp_partition.h b/components/spi_flash/include/esp_partition.h index 550bf6b6b2..792a31e363 100644 --- a/components/spi_flash/include/esp_partition.h +++ b/components/spi_flash/include/esp_partition.h @@ -1,16 +1,8 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ #ifndef __ESP_PARTITION_H__ #define __ESP_PARTITION_H__ @@ -22,7 +14,6 @@ #include "esp_flash.h" #include "esp_spi_flash.h" - #ifdef __cplusplus extern "C" { #endif @@ -206,7 +197,7 @@ void esp_partition_iterator_release(esp_partition_iterator_t iterator); * - If partition not found, returns NULL. * - If found, returns a pointer to the esp_partition_t structure in flash. This pointer is always valid for the lifetime of the application. */ -const esp_partition_t *esp_partition_verify(const esp_partition_t *partition); +const esp_partition_t* esp_partition_verify(const esp_partition_t* partition); /** * @brief Read data from the partition @@ -382,7 +373,7 @@ esp_err_t esp_partition_mmap(const esp_partition_t* partition, size_t offset, si * - ESP_ERR_IMAGE_INVALID: App partition doesn't contain a valid app image. * - ESP_FAIL: An allocation error occurred. */ -esp_err_t esp_partition_get_sha256(const esp_partition_t *partition, uint8_t *sha_256); +esp_err_t esp_partition_get_sha256(const esp_partition_t* partition, uint8_t* sha_256); /** * @brief Check for the identity of two partitions by SHA-256 digest. @@ -394,7 +385,7 @@ esp_err_t esp_partition_get_sha256(const esp_partition_t *partition, uint8_t *sh * - True: In case of the two firmware is equal. * - False: Otherwise */ -bool esp_partition_check_identity(const esp_partition_t *partition_1, const esp_partition_t *partition_2); +bool esp_partition_check_identity(const esp_partition_t* partition_1, const esp_partition_t* partition_2); /** * @brief Register a partition on an external flash chip @@ -418,8 +409,8 @@ bool esp_partition_check_identity(const esp_partition_t *partition_1, const esp_ * - ESP_ERR_INVALID_SIZE if the partition doesn't fit into the flash chip size */ esp_err_t esp_partition_register_external(esp_flash_t* flash_chip, size_t offset, size_t size, - const char* label, esp_partition_type_t type, esp_partition_subtype_t subtype, - const esp_partition_t** out_partition); + const char* label, esp_partition_type_t type, esp_partition_subtype_t subtype, + const esp_partition_t** out_partition); /** * @brief Deregister the partition previously registered using esp_partition_register_external @@ -437,5 +428,4 @@ esp_err_t esp_partition_deregister_external(const esp_partition_t* partition); } #endif - #endif /* __ESP_PARTITION_H__ */ diff --git a/components/spi_flash/include/esp_private/partition_linux.h b/components/spi_flash/include/esp_private/partition_linux.h new file mode 100644 index 0000000000..5da5738d78 --- /dev/null +++ b/components/spi_flash/include/esp_private/partition_linux.h @@ -0,0 +1,92 @@ +/* + * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file partition_linux.h + * + * @brief Private API functions used for Linux-target emulation of the Partition APIs (host-side testing) + */ + +/** + * @brief Partition type to string conversion routine + * + * @param type Partition type, see esp_partition_type_t + * + * @return string equivalent of given partition type or "unknown" on mismatch + */ +const char* esp_partition_type_to_str(const uint32_t type); + +/** + * @brief Partition subtype to string conversion routine + * + * @param type Partition type, see esp_partition_type_t + * @param subtype Partition subtype, see esp_partition_subtype_t + * + * @return string equivalent of given partition subtype or "unknown" on mismatch + */ +const char *esp_partition_subtype_to_str(const uint32_t type, const uint32_t subtype); + +/** + * @brief Creates memory emulation of SPI FLASH device (Linux host) + * + * The function creates a memory buffer to emulate SPI FLASH device and provides a pointer to its beginning, in order + * to allow relevant Partition APIs run in host-emulated environment without any code change. + * + * The emulation buffer is actually a disk file mapped to the host memory, current version implements the following: + * 1. create temporary file /tmp/idf-partition-XXXXXX (fixed size 4MB) + * 2. mmap() whole file to the memory and set its contents to all 1s (SPI NOR flash default) + * 3. upload build/partition_table/partition-table.bin (hard-wired path for now) to ESP_PARTITION_TABLE_OFFSET + * (from the beginning of the memory buffer, ie to the same offset as in real SPI FLASH) + * 4. [optional: iterate through the partitions uploaded and print esp_partition_info_t details for each] + * 5. set part_desc_addr_start[out] parameter to the memory buffer starting address + * + * The pointer returned in part_desc_addr_start is then used as it was regular SPI FLASH address. + * + * NOTES: + * 1. the temporary file generated is not deleted automatically - the cleanup happens during the next host system reset + * 2. the mmapped() section remains active until esp_partition_file_unmmap() is called + * 3. mmap() is called with MAP_SHARED so the emulated SPI FLASH can be shared among processes + * + * @param[out] part_desc_addr_start output pointer to receive memory SPI FLASH buffer address + * + * @return + * - ESP_OK: Operation successful + * - ESP_ERR_NOT_FINISHED: Failed to generate temporary file + * - ESP_ERR_INVALID_SIZE (one of the following): + * - Failed to resize temporary file to required value + * - Failed to set filepointer in partition-table.bin + * - ESP_ERR_NO_MEM: Failed to mmap() the temporary file into the memory + * - ESP_ERR_NOT_FOUND: Couldn't open the partition_table.bin file + * - ESP_ERR_INVALID_STATE: Failed to upload partition_table into the memory + */ +esp_err_t esp_partition_file_mmap(const uint8_t **part_desc_addr_start); + +/** + * @brief Releases the memory of emulated SPI FLASH device (Linux host) + * + * The function releases the memory block previously allocated by esp_partition_file_mmap(). + * The buffer is freed by calling munmap() with emulated_buffer, buffer_size + * + * @return + * - ESP_OK: Operation successful + * - ESP_ERR_NO_MEM: The memory buffer was not allocated + * - ESP_ERR_INVALID_SIZE: The buffer size was 0 + * - ESP_ERR_INVALID_RESPONSE: Failed to munmap() the emulation file from memory + */ +esp_err_t esp_partition_file_munmap(void); + +#ifdef __cplusplus +} +#endif diff --git a/components/spi_flash/partition.c b/components/spi_flash/partition.c index 4b6e492c7f..7ad342c57e 100644 --- a/components/spi_flash/partition.c +++ b/components/spi_flash/partition.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -9,19 +9,26 @@ #include #include #include +#include "sdkconfig.h" #include "esp_flash_partitions.h" #include "esp_attr.h" #include "esp_flash.h" -#include "esp_spi_flash.h" #include "esp_partition.h" + +#if !CONFIG_IDF_TARGET_LINUX #include "esp_flash_encrypt.h" +#endif + #include "esp_log.h" #include "esp_rom_md5.h" -#include "bootloader_common.h" #include "bootloader_util.h" -#include "esp_ota_ops.h" -#define HASH_LEN 32 /* SHA-256 digest length */ +#if CONFIG_IDF_TARGET_LINUX +#if __has_include() +#include +#endif +#include "esp_private/partition_linux.h" +#endif #ifndef NDEBUG // Enable built-in checks in queue.h in debug builds @@ -36,24 +43,173 @@ typedef struct partition_list_item_ { } partition_list_item_t; typedef struct esp_partition_iterator_opaque_ { - esp_partition_type_t type; // requested type - esp_partition_subtype_t subtype; // requested subtype - const char* label; // requested label (can be NULL) - partition_list_item_t* next_item; // next item to iterate to - esp_partition_t* info; // pointer to info (it is redundant, but makes code more readable) + esp_partition_type_t type; // requested type + esp_partition_subtype_t subtype; // requested subtype + const char *label; // requested label (can be NULL) + partition_list_item_t *next_item; // next item to iterate to + esp_partition_t *info; // pointer to info (it is redundant, but makes code more readable) } esp_partition_iterator_opaque_t; - -static esp_partition_iterator_opaque_t* iterator_create(esp_partition_type_t type, esp_partition_subtype_t subtype, const char* label); -static esp_err_t load_partitions(void); -static esp_err_t ensure_partitions_loaded(void); - - -static const char* TAG = "partition"; -static SLIST_HEAD(partition_list_head_, partition_list_item_) s_partition_list = - SLIST_HEAD_INITIALIZER(s_partition_list); +static SLIST_HEAD(partition_list_head_, partition_list_item_) s_partition_list = SLIST_HEAD_INITIALIZER(s_partition_list); static _lock_t s_partition_list_lock; +static const char *TAG = "partition"; + +// Create linked list of partition_list_item_t structures. +// This function is called only once, with s_partition_list_lock taken. +static esp_err_t load_partitions(void) +{ + const uint8_t *p_start; + const uint8_t *p_end; +#if !CONFIG_IDF_TARGET_LINUX + spi_flash_mmap_handle_t handle; +#endif + + // Temporary list of loaded partitions, if valid then we copy this to s_partition_list + typeof(s_partition_list) new_partitions_list = SLIST_HEAD_INITIALIZER(s_partition_list); + partition_list_item_t *last = NULL; + +#if CONFIG_PARTITION_TABLE_MD5 + const uint8_t *md5_part = NULL; + const uint8_t *stored_md5; + uint8_t calc_md5[ESP_ROM_MD5_DIGEST_LEN]; + md5_context_t context; + + esp_rom_md5_init(&context); +#endif + + uint32_t partition_align_pg_size = (ESP_PARTITION_TABLE_OFFSET) & ~(0x10000 - 1); + uint32_t partition_pad = ESP_PARTITION_TABLE_OFFSET - partition_align_pg_size; + +#if CONFIG_IDF_TARGET_LINUX + esp_err_t err = esp_partition_file_mmap(&p_start); +#else + esp_err_t err = spi_flash_mmap(partition_align_pg_size, + SPI_FLASH_SEC_SIZE, SPI_FLASH_MMAP_DATA, (const void **)&p_start, &handle); +#endif + + if (err != ESP_OK) { + return err; + } + + // calculate partition address within mmap-ed region + p_start += partition_pad; + p_end = p_start + SPI_FLASH_SEC_SIZE; + + for (const uint8_t *p_entry = p_start; p_entry < p_end; p_entry += sizeof(esp_partition_info_t)) { + esp_partition_info_t entry; + // copying to RAM instead of using pointer to flash to avoid any chance of TOCTOU due to cache miss + // when flash encryption is used + memcpy(&entry, p_entry, sizeof(entry)); + +#if CONFIG_PARTITION_TABLE_MD5 + if (entry.magic == ESP_PARTITION_MAGIC_MD5) { + md5_part = p_entry; + break; + } +#endif + if (entry.magic != ESP_PARTITION_MAGIC) { + break; + } + +#if CONFIG_PARTITION_TABLE_MD5 + esp_rom_md5_update(&context, &entry, sizeof(entry)); +#endif + + // allocate new linked list item and populate it with data from partition table + partition_list_item_t *item = (partition_list_item_t *) calloc(sizeof(partition_list_item_t), 1); + if (item == NULL) { + err = ESP_ERR_NO_MEM; + break; + } +#if CONFIG_IDF_TARGET_LINUX + item->info.flash_chip = NULL; +#else + item->info.flash_chip = esp_flash_default_chip; +#endif + item->info.address = entry.pos.offset; + item->info.size = entry.pos.size; + item->info.type = entry.type; + item->info.subtype = entry.subtype; + item->info.encrypted = entry.flags & PART_FLAG_ENCRYPTED; + item->user_registered = false; + +#if CONFIG_IDF_TARGET_LINUX + item->info.encrypted = false; +#else + if (!esp_flash_encryption_enabled()) { + /* If flash encryption is not turned on, no partitions should be treated as encrypted */ + item->info.encrypted = false; + } else if (entry.type == ESP_PARTITION_TYPE_APP + || (entry.type == ESP_PARTITION_TYPE_DATA && entry.subtype == ESP_PARTITION_SUBTYPE_DATA_OTA) + || (entry.type == ESP_PARTITION_TYPE_DATA && entry.subtype == ESP_PARTITION_SUBTYPE_DATA_NVS_KEYS)) { + /* If encryption is turned on, all app partitions and OTA data + are always encrypted */ + item->info.encrypted = true; + } +#endif + +#if CONFIG_NVS_COMPATIBLE_PRE_V4_3_ENCRYPTION_FLAG + if (entry.type == ESP_PARTITION_TYPE_DATA && + entry.subtype == ESP_PARTITION_SUBTYPE_DATA_NVS && + (entry.flags & PART_FLAG_ENCRYPTED)) { + ESP_LOGI(TAG, "Ignoring encrypted flag for \"%s\" partition", entry.label); + item->info.encrypted = false; + } +#endif + // item->info.label is initialized by calloc, so resulting string will be null terminated + strncpy(item->info.label, (const char *) entry.label, sizeof(item->info.label) - 1); + + // add it to the list + if (last == NULL) { + SLIST_INSERT_HEAD(&new_partitions_list, item, next); + } else { + SLIST_INSERT_AFTER(last, item, next); + } + last = item; + } + +#if CONFIG_PARTITION_TABLE_MD5 + if (md5_part == NULL) { + ESP_LOGE(TAG, "No MD5 found in partition table"); + err = ESP_ERR_NOT_FOUND; + } else { + stored_md5 = md5_part + ESP_PARTITION_MD5_OFFSET; + esp_rom_md5_final(calc_md5, &context); + +#if !CONFIG_IDF_TARGET_LINUX + ESP_LOG_BUFFER_HEXDUMP("calculated md5", calc_md5, ESP_ROM_MD5_DIGEST_LEN, ESP_LOG_VERBOSE); + ESP_LOG_BUFFER_HEXDUMP("stored md5", stored_md5, ESP_ROM_MD5_DIGEST_LEN, ESP_LOG_VERBOSE); +#endif + + if (memcmp(calc_md5, stored_md5, ESP_ROM_MD5_DIGEST_LEN) != 0) { + ESP_LOGE(TAG, "Partition table MD5 mismatch"); + err = ESP_ERR_INVALID_STATE; + } else { + ESP_LOGV(TAG, "Partition table MD5 verified"); + } + } +#endif + + if (err == ESP_OK) { + /* Don't copy the list to the static variable unless it's verified */ + s_partition_list = new_partitions_list; + } else { + /* Otherwise, free all the memory we just allocated */ + partition_list_item_t *it = new_partitions_list.slh_first; + while (it) { + partition_list_item_t *next = it->next.sle_next; + free(it); + it = next; + } + } + +#if !CONFIG_IDF_TARGET_LINUX + spi_flash_munmap(handle); +#endif + + return err; +} static esp_err_t ensure_partitions_loaded(void) { @@ -62,7 +218,7 @@ static esp_err_t ensure_partitions_loaded(void) // only lock if list is empty (and check again after acquiring lock) _lock_acquire(&s_partition_list_lock); if (SLIST_EMPTY(&s_partition_list)) { - ESP_LOGD(TAG, "Loading the partition table"); + ESP_LOGV(TAG, "Loading the partition table"); err = load_partitions(); if (err != ESP_OK) { ESP_LOGE(TAG, "load_partitions returned 0x%x", err); @@ -73,8 +229,21 @@ static esp_err_t ensure_partitions_loaded(void) return err; } +static esp_partition_iterator_opaque_t *iterator_create(esp_partition_type_t type, + esp_partition_subtype_t subtype, const char *label) +{ + esp_partition_iterator_opaque_t *it = + (esp_partition_iterator_opaque_t *) malloc(sizeof(esp_partition_iterator_opaque_t)); + it->type = type; + it->subtype = subtype; + it->label = label; + it->next_item = SLIST_FIRST(&s_partition_list); + it->info = NULL; + return it; +} + esp_partition_iterator_t esp_partition_find(esp_partition_type_t type, - esp_partition_subtype_t subtype, const char* label) + esp_partition_subtype_t subtype, const char *label) { if (ensure_partitions_loaded() != ESP_OK) { return NULL; @@ -103,7 +272,7 @@ esp_partition_iterator_t esp_partition_next(esp_partition_iterator_t it) } _lock_acquire(&s_partition_list_lock); for (; it->next_item != NULL; it->next_item = SLIST_NEXT(it->next_item, next)) { - esp_partition_t* p = &it->next_item->info; + esp_partition_t *p = &it->next_item->info; if (it->type != ESP_PARTITION_TYPE_ANY && it->type != p->type) { continue; } @@ -126,168 +295,18 @@ esp_partition_iterator_t esp_partition_next(esp_partition_iterator_t it) return it; } -const esp_partition_t* esp_partition_find_first(esp_partition_type_t type, - esp_partition_subtype_t subtype, const char* label) +const esp_partition_t *esp_partition_find_first(esp_partition_type_t type, + esp_partition_subtype_t subtype, const char *label) { esp_partition_iterator_t it = esp_partition_find(type, subtype, label); if (it == NULL) { return NULL; } - const esp_partition_t* res = esp_partition_get(it); + const esp_partition_t *res = esp_partition_get(it); esp_partition_iterator_release(it); return res; } -static esp_partition_iterator_opaque_t* iterator_create(esp_partition_type_t type, - esp_partition_subtype_t subtype, const char* label) -{ - esp_partition_iterator_opaque_t* it = - (esp_partition_iterator_opaque_t*) malloc(sizeof(esp_partition_iterator_opaque_t)); - it->type = type; - it->subtype = subtype; - it->label = label; - it->next_item = SLIST_FIRST(&s_partition_list); - it->info = NULL; - return it; -} - -// Create linked list of partition_list_item_t structures. -// This function is called only once, with s_partition_list_lock taken. -static esp_err_t load_partitions(void) -{ - const uint8_t *p_start; - const uint8_t *p_end; - spi_flash_mmap_handle_t handle; - - // Temporary list of loaded partitions, if valid then we copy this to s_partition_list - typeof(s_partition_list) new_partitions_list = SLIST_HEAD_INITIALIZER(s_partition_list); - partition_list_item_t* last = NULL; - -#if CONFIG_PARTITION_TABLE_MD5 - const uint8_t *md5_part = NULL; - const uint8_t *stored_md5; - uint8_t calc_md5[ESP_ROM_MD5_DIGEST_LEN]; - md5_context_t context; - - esp_rom_md5_init(&context); -#endif - - // map 64kB block where partition table is located - uint32_t partition_align_pg_size = (ESP_PARTITION_TABLE_OFFSET) & ~(0x10000 - 1); - uint32_t partition_pad = ESP_PARTITION_TABLE_OFFSET - partition_align_pg_size; - esp_err_t err = spi_flash_mmap(partition_align_pg_size, - SPI_FLASH_SEC_SIZE, SPI_FLASH_MMAP_DATA, (const void **)&p_start, &handle); - if (err != ESP_OK) { - return err; - } - // calculate partition address within mmap-ed region - p_start += partition_pad; - p_end = p_start + SPI_FLASH_SEC_SIZE; - - for(const uint8_t *p_entry = p_start; p_entry < p_end; p_entry += sizeof(esp_partition_info_t)) { - esp_partition_info_t entry; - // copying to RAM instead of using pointer to flash to avoid any chance of TOCTOU due to cache miss - // when flash encryption is used - memcpy(&entry, p_entry, sizeof(entry)); - -#if CONFIG_PARTITION_TABLE_MD5 - if (entry.magic == ESP_PARTITION_MAGIC_MD5) { - md5_part = p_entry; - break; - } -#endif - if (entry.magic != ESP_PARTITION_MAGIC) { - break; - } - -#if CONFIG_PARTITION_TABLE_MD5 - esp_rom_md5_update(&context, &entry, sizeof(entry)); -#endif - - // allocate new linked list item and populate it with data from partition table - partition_list_item_t* item = (partition_list_item_t*) calloc(sizeof(partition_list_item_t), 1); - if (item == NULL) { - err = ESP_ERR_NO_MEM; - break; - } - item->info.flash_chip = esp_flash_default_chip; - item->info.address = entry.pos.offset; - item->info.size = entry.pos.size; - item->info.type = entry.type; - item->info.subtype = entry.subtype; - item->info.encrypted = entry.flags & PART_FLAG_ENCRYPTED; - item->user_registered = false; - - if (!esp_flash_encryption_enabled()) { - /* If flash encryption is not turned on, no partitions should be treated as encrypted */ - item->info.encrypted = false; - } else if (entry.type == ESP_PARTITION_TYPE_APP - || (entry.type == ESP_PARTITION_TYPE_DATA && entry.subtype == ESP_PARTITION_SUBTYPE_DATA_OTA) - || (entry.type == ESP_PARTITION_TYPE_DATA && entry.subtype == ESP_PARTITION_SUBTYPE_DATA_NVS_KEYS)) { - /* If encryption is turned on, all app partitions and OTA data - are always encrypted */ - item->info.encrypted = true; - } - -#if CONFIG_NVS_COMPATIBLE_PRE_V4_3_ENCRYPTION_FLAG - if (entry.type == ESP_PARTITION_TYPE_DATA && - entry.subtype == ESP_PARTITION_SUBTYPE_DATA_NVS && - (entry.flags & PART_FLAG_ENCRYPTED)) { - ESP_LOGI(TAG, "Ignoring encrypted flag for \"%s\" partition", entry.label); - item->info.encrypted = false; - } -#endif - - // item->info.label is initialized by calloc, so resulting string will be null terminated - strncpy(item->info.label, (const char*) entry.label, sizeof(item->info.label) - 1); - - // add it to the list - if (last == NULL) { - SLIST_INSERT_HEAD(&new_partitions_list, item, next); - } else { - SLIST_INSERT_AFTER(last, item, next); - } - last = item; - } - -#if CONFIG_PARTITION_TABLE_MD5 - if (md5_part == NULL) { - ESP_LOGE(TAG, "No MD5 found in partition table"); - err = ESP_ERR_NOT_FOUND; - } else { - stored_md5 = md5_part + ESP_PARTITION_MD5_OFFSET; - - esp_rom_md5_final(calc_md5, &context); - - - ESP_LOG_BUFFER_HEXDUMP("calculated md5", calc_md5, ESP_ROM_MD5_DIGEST_LEN, ESP_LOG_VERBOSE); - ESP_LOG_BUFFER_HEXDUMP("stored md5", stored_md5, ESP_ROM_MD5_DIGEST_LEN, ESP_LOG_VERBOSE); - - if (memcmp(calc_md5, stored_md5, ESP_ROM_MD5_DIGEST_LEN) != 0) { - ESP_LOGE(TAG, "Partition table MD5 mismatch"); - err = ESP_ERR_INVALID_STATE; - } else { - ESP_LOGD(TAG, "Partition table MD5 verified"); - } - } -#endif - - if (err == ESP_OK) { - /* Don't copy the list to the static variable unless it's verified */ - s_partition_list = new_partitions_list; - } else { - /* Otherwise, free all the memory we just allocated */ - partition_list_item_t *it = new_partitions_list.slh_first; - while (it) { - partition_list_item_t *next = it->next.sle_next; - free(it); - it = next; - } - } - - spi_flash_munmap(handle); - return err; -} void esp_partition_iterator_release(esp_partition_iterator_t iterator) { @@ -295,15 +314,37 @@ void esp_partition_iterator_release(esp_partition_iterator_t iterator) free(iterator); } -const esp_partition_t* esp_partition_get(esp_partition_iterator_t iterator) +const esp_partition_t *esp_partition_get(esp_partition_iterator_t iterator) { assert(iterator != NULL); return iterator->info; } -esp_err_t esp_partition_register_external(esp_flash_t* flash_chip, size_t offset, size_t size, - const char* label, esp_partition_type_t type, esp_partition_subtype_t subtype, - const esp_partition_t** out_partition) +const esp_partition_t *esp_partition_verify(const esp_partition_t *partition) +{ + assert(partition != NULL); + const char *label = (strlen(partition->label) > 0) ? partition->label : NULL; + esp_partition_iterator_t it = esp_partition_find(partition->type, + partition->subtype, + label); + while (it != NULL) { + const esp_partition_t *p = esp_partition_get(it); + /* Can't memcmp() whole structure here as padding contents may be different */ + if (p->flash_chip == partition->flash_chip + && p->address == partition->address + && partition->size == p->size + && partition->encrypted == p->encrypted) { + esp_partition_iterator_release(it); + return p; + } + it = esp_partition_next(it); + } + esp_partition_iterator_release(it); + return NULL; +} +esp_err_t esp_partition_register_external(esp_flash_t *flash_chip, size_t offset, size_t size, + const char *label, esp_partition_type_t type, esp_partition_subtype_t subtype, + const esp_partition_t **out_partition) { if (out_partition != NULL) { *out_partition = NULL; @@ -321,7 +362,7 @@ esp_err_t esp_partition_register_external(esp_flash_t* flash_chip, size_t offset return err; } - partition_list_item_t* item = (partition_list_item_t*) calloc(sizeof(partition_list_item_t), 1); + partition_list_item_t *item = (partition_list_item_t *) calloc(sizeof(partition_list_item_t), 1); if (item == NULL) { return ESP_ERR_NO_MEM; } @@ -335,7 +376,8 @@ esp_err_t esp_partition_register_external(esp_flash_t* flash_chip, size_t offset strlcpy(item->info.label, label, sizeof(item->info.label)); _lock_acquire(&s_partition_list_lock); - partition_list_item_t *it, *last = NULL; + partition_list_item_t *it = NULL; + partition_list_item_t *last = NULL; SLIST_FOREACH(it, &s_partition_list, next) { /* Check if the new partition overlaps an existing one */ if (it->info.flash_chip == flash_chip && @@ -359,7 +401,7 @@ esp_err_t esp_partition_register_external(esp_flash_t* flash_chip, size_t offset return ESP_OK; } -esp_err_t esp_partition_deregister_external(const esp_partition_t* partition) +esp_err_t esp_partition_deregister_external(const esp_partition_t *partition) { esp_err_t result = ESP_ERR_NOT_FOUND; _lock_acquire(&s_partition_list_lock); @@ -379,231 +421,3 @@ esp_err_t esp_partition_deregister_external(const esp_partition_t* partition) _lock_release(&s_partition_list_lock); return result; } - -const esp_partition_t *esp_partition_verify(const esp_partition_t *partition) -{ - assert(partition != NULL); - const char *label = (strlen(partition->label) > 0) ? partition->label : NULL; - esp_partition_iterator_t it = esp_partition_find(partition->type, - partition->subtype, - label); - while (it != NULL) { - const esp_partition_t *p = esp_partition_get(it); - /* Can't memcmp() whole structure here as padding contents may be different */ - if (p->flash_chip == partition->flash_chip - && p->address == partition->address - && partition->size == p->size - && partition->encrypted == p->encrypted) { - esp_partition_iterator_release(it); - return p; - } - it = esp_partition_next(it); - } - esp_partition_iterator_release(it); - return NULL; -} - -esp_err_t esp_partition_read(const esp_partition_t* partition, - size_t src_offset, void* dst, size_t size) -{ - assert(partition != NULL); - if (src_offset > partition->size) { - return ESP_ERR_INVALID_ARG; - } - if (src_offset + size > partition->size) { - return ESP_ERR_INVALID_SIZE; - } - - if (!partition->encrypted) { -#ifndef CONFIG_SPI_FLASH_USE_LEGACY_IMPL - return esp_flash_read(partition->flash_chip, dst, partition->address + src_offset, size); -#else - return spi_flash_read(partition->address + src_offset, dst, size); -#endif // CONFIG_SPI_FLASH_USE_LEGACY_IMPL - } else { -#if CONFIG_SPI_FLASH_ENABLE_ENCRYPTED_READ_WRITE - if (partition->flash_chip != esp_flash_default_chip) { - return ESP_ERR_NOT_SUPPORTED; - } - - /* Encrypted partitions need to be read via a cache mapping */ - const void *buf; - spi_flash_mmap_handle_t handle; - esp_err_t err; - - err = esp_partition_mmap(partition, src_offset, size, - SPI_FLASH_MMAP_DATA, &buf, &handle); - if (err != ESP_OK) { - return err; - } - memcpy(dst, buf, size); - spi_flash_munmap(handle); - return ESP_OK; -#else - return ESP_ERR_NOT_SUPPORTED; -#endif // CONFIG_SPI_FLASH_ENABLE_ENCRYPTED_READ_WRITE - } -} - -esp_err_t esp_partition_write(const esp_partition_t* partition, - size_t dst_offset, const void* src, size_t size) -{ - assert(partition != NULL); - if (dst_offset > partition->size) { - return ESP_ERR_INVALID_ARG; - } - if (dst_offset + size > partition->size) { - return ESP_ERR_INVALID_SIZE; - } - dst_offset = partition->address + dst_offset; - if (!partition->encrypted) { -#ifndef CONFIG_SPI_FLASH_USE_LEGACY_IMPL - return esp_flash_write(partition->flash_chip, src, dst_offset, size); -#else - return spi_flash_write(dst_offset, src, size); -#endif // CONFIG_SPI_FLASH_USE_LEGACY_IMPL - } else { -#if CONFIG_SPI_FLASH_ENABLE_ENCRYPTED_READ_WRITE - if (partition->flash_chip != esp_flash_default_chip) { - return ESP_ERR_NOT_SUPPORTED; - } -#ifndef CONFIG_SPI_FLASH_USE_LEGACY_IMPL - return esp_flash_write_encrypted(partition->flash_chip, dst_offset, src, size); -#else - return spi_flash_write_encrypted(dst_offset, src, size); -#endif // CONFIG_SPI_FLASH_USE_LEGACY_IMPL -#else - return ESP_ERR_NOT_SUPPORTED; -#endif // CONFIG_SPI_FLASH_ENABLE_ENCRYPTED_READ_WRITE - } -} - -esp_err_t esp_partition_read_raw(const esp_partition_t* partition, - size_t src_offset, void* dst, size_t size) -{ - assert(partition != NULL); - if (src_offset > partition->size) { - return ESP_ERR_INVALID_ARG; - } - if (src_offset + size > partition->size) { - return ESP_ERR_INVALID_SIZE; - } - -#ifndef CONFIG_SPI_FLASH_USE_LEGACY_IMPL - return esp_flash_read(partition->flash_chip, dst, partition->address + src_offset, size); -#else - return spi_flash_read(partition->address + src_offset, dst, size); -#endif // CONFIG_SPI_FLASH_USE_LEGACY_IMPL -} - -esp_err_t esp_partition_write_raw(const esp_partition_t* partition, - size_t dst_offset, const void* src, size_t size) -{ - assert(partition != NULL); - if (dst_offset > partition->size) { - return ESP_ERR_INVALID_ARG; - } - if (dst_offset + size > partition->size) { - return ESP_ERR_INVALID_SIZE; - } - dst_offset = partition->address + dst_offset; - -#ifndef CONFIG_SPI_FLASH_USE_LEGACY_IMPL - return esp_flash_write(partition->flash_chip, src, dst_offset, size); -#else - return spi_flash_write(dst_offset, src, size); -#endif // CONFIG_SPI_FLASH_USE_LEGACY_IMPL -} - -esp_err_t esp_partition_erase_range(const esp_partition_t* partition, - size_t offset, size_t size) -{ - assert(partition != NULL); - if (offset > partition->size) { - return ESP_ERR_INVALID_ARG; - } - if (offset + size > partition->size) { - return ESP_ERR_INVALID_SIZE; - } - if (size % SPI_FLASH_SEC_SIZE != 0) { - return ESP_ERR_INVALID_SIZE; - } - if (offset % SPI_FLASH_SEC_SIZE != 0) { - return ESP_ERR_INVALID_ARG; - } -#ifndef CONFIG_SPI_FLASH_USE_LEGACY_IMPL - return esp_flash_erase_region(partition->flash_chip, partition->address + offset, size); -#else - return spi_flash_erase_range(partition->address + offset, size); -#endif // CONFIG_SPI_FLASH_USE_LEGACY_IMPL -} - -/* - * Note: current implementation ignores the possibility of multiple regions in the same partition being - * mapped. Reference counting and address space re-use is delegated to spi_flash_mmap. - * - * If this becomes a performance issue (i.e. if we need to map multiple regions within the partition), - * we can add esp_partition_mmapv which will accept an array of offsets and sizes, and return array of - * mmaped pointers, and a single handle for all these regions. - */ -esp_err_t esp_partition_mmap(const esp_partition_t* partition, size_t offset, size_t size, - spi_flash_mmap_memory_t memory, - const void** out_ptr, spi_flash_mmap_handle_t* out_handle) -{ - assert(partition != NULL); - if (offset > partition->size) { - return ESP_ERR_INVALID_ARG; - } - if (offset + size > partition->size) { - return ESP_ERR_INVALID_SIZE; - } - if (partition->flash_chip != esp_flash_default_chip) { - return ESP_ERR_NOT_SUPPORTED; - } - size_t phys_addr = partition->address + offset; - // offset within 64kB block - size_t region_offset = phys_addr & 0xffff; - size_t mmap_addr = phys_addr & 0xffff0000; - esp_err_t rc = spi_flash_mmap(mmap_addr, size+region_offset, memory, out_ptr, out_handle); - // adjust returned pointer to point to the correct offset - if (rc == ESP_OK) { - *out_ptr = (void*) (((ptrdiff_t) *out_ptr) + region_offset); - } - return rc; -} - -esp_err_t esp_partition_get_sha256(const esp_partition_t *partition, uint8_t *sha_256) -{ - return bootloader_common_get_sha256_of_partition(partition->address, partition->size, partition->type, sha_256); -} - -bool esp_partition_check_identity(const esp_partition_t *partition_1, const esp_partition_t *partition_2) -{ - uint8_t sha_256[2][HASH_LEN] = { 0 }; - - if (esp_partition_get_sha256(partition_1, sha_256[0]) == ESP_OK && - esp_partition_get_sha256(partition_2, sha_256[1]) == ESP_OK) { - - if (memcmp(sha_256[0], sha_256[1], HASH_LEN) == 0) { - // The partitions are identity - return true; - } - } - return false; -} - -bool esp_partition_main_flash_region_safe(size_t addr, size_t size) -{ - bool result = true; - if (addr <= ESP_PARTITION_TABLE_OFFSET + ESP_PARTITION_TABLE_MAX_LEN) { - return false; - } - const esp_partition_t *p = esp_ota_get_running_partition(); - if (addr >= p->address && addr < p->address + p->size) { - return false; - } - if (addr < p->address && addr + size > p->address) { - return false; - } - return result; -} diff --git a/components/spi_flash/partition_linux.c b/components/spi_flash/partition_linux.c new file mode 100644 index 0000000000..784b0d1c11 --- /dev/null +++ b/components/spi_flash/partition_linux.c @@ -0,0 +1,255 @@ +/* + * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "sdkconfig.h" +#include "esp_partition.h" +#include "esp_flash_partitions.h" +#include "esp_private/partition_linux.h" +#include "esp_log.h" + +static const char *TAG = "linux_spiflash"; +static void *s_spiflash_mem_file_buf = NULL; +static uint32_t s_spiflash_mem_file_size = 0x400000; //4MB fixed + +const char *esp_partition_type_to_str(const uint32_t type) +{ + switch (type) { + case PART_TYPE_APP: return "app"; + case PART_TYPE_DATA: return "data"; + default: return "unknown"; + } +} + +const char *esp_partition_subtype_to_str(const uint32_t type, const uint32_t subtype) +{ + switch (type) { + case PART_TYPE_APP: + switch (subtype) { + case PART_SUBTYPE_FACTORY: return "factory"; + case PART_SUBTYPE_OTA_FLAG: return "ota_flag"; + case PART_SUBTYPE_OTA_MASK: return "ota_mask"; + case PART_SUBTYPE_TEST: return "test"; + default: return "unknown"; + } + case PART_TYPE_DATA: + switch (subtype) { + case PART_SUBTYPE_DATA_OTA: return "data_ota"; + case PART_SUBTYPE_DATA_RF: return "data_rf"; + case PART_SUBTYPE_DATA_WIFI: return "data_wifi"; + case PART_SUBTYPE_DATA_NVS_KEYS: return "nvs_keys"; + case PART_SUBTYPE_DATA_EFUSE_EM: return "efuse_em"; + default: return "unknown"; + } + default: return "unknown"; + } +} + +esp_err_t esp_partition_file_mmap(const uint8_t **part_desc_addr_start) +{ + //create temporary file to hold complete SPIFLASH size + char temp_spiflash_mem_file_name[PATH_MAX] = {"/tmp/idf-partition-XXXXXX"}; + int spiflash_mem_file_fd = mkstemp(temp_spiflash_mem_file_name); + + if (spiflash_mem_file_fd == -1) { + ESP_LOGE(TAG, "Failed to create SPI FLASH emulation file %s: %s", temp_spiflash_mem_file_name, strerror(errno)); + return ESP_ERR_NOT_FINISHED; + } + + if (ftruncate(spiflash_mem_file_fd, s_spiflash_mem_file_size) != 0) { + ESP_LOGE(TAG, "Failed to set size of SPI FLASH memory emulation file %s: %s", temp_spiflash_mem_file_name, strerror(errno)); + return ESP_ERR_INVALID_SIZE; + } + + ESP_LOGV(TAG, "SPIFLASH memory emulation file created: %s (size: %d B)", temp_spiflash_mem_file_name, s_spiflash_mem_file_size); + + //create memory-mapping for the partitions holder file + if ((s_spiflash_mem_file_buf = mmap(NULL, s_spiflash_mem_file_size, PROT_READ | PROT_WRITE, MAP_SHARED, spiflash_mem_file_fd, 0)) == MAP_FAILED) { + ESP_LOGE(TAG, "Failed to mmap() SPI FLASH memory emulation file: %s", strerror(errno)); + return ESP_ERR_NO_MEM; + } + + //initialize whole range with bit-1 (NOR FLASH default) + memset(s_spiflash_mem_file_buf, 0xFF, s_spiflash_mem_file_size); + + //upload partition table to the mmap file at real offset as in SPIFLASH + const char *partition_table_file_name = "build/partition_table/partition-table.bin"; + + FILE *f_partition_table = fopen(partition_table_file_name, "r+"); + if (f_partition_table == NULL) { + ESP_LOGE(TAG, "Failed to open partition table file %s: %s", partition_table_file_name, strerror(errno)); + return ESP_ERR_NOT_FOUND; + } + + if (fseek(f_partition_table, 0L, SEEK_END) != 0) { + ESP_LOGE(TAG, "Failed to seek in partition table file %s: %s", partition_table_file_name, strerror(errno)); + return ESP_ERR_INVALID_SIZE; + } + + int partition_table_file_size = ftell(f_partition_table); + ESP_LOGV(TAG, "Using partition table file %s (size: %d B):", partition_table_file_name, partition_table_file_size); + + uint8_t *part_table_in_spiflash = s_spiflash_mem_file_buf + ESP_PARTITION_TABLE_OFFSET; + + //copy partition table from the file to emulated SPIFLASH memory space + if (fseek(f_partition_table, 0L, SEEK_SET) != 0) { + ESP_LOGE(TAG, "Failed to seek in partition table file %s: %s", partition_table_file_name, strerror(errno)); + return ESP_ERR_INVALID_SIZE; + } + + size_t res = fread(part_table_in_spiflash, 1, partition_table_file_size, f_partition_table); + fclose(f_partition_table); + if (res != partition_table_file_size) { + ESP_LOGE(TAG, "Failed to read partition table file %s", partition_table_file_name); + return ESP_ERR_INVALID_STATE; + } + +#ifdef CONFIG_LOG_DEFAULT_LEVEL_VERBOSE + uint8_t *part_ptr = part_table_in_spiflash; + uint8_t *part_end_ptr = part_table_in_spiflash + partition_table_file_size; + + ESP_LOGV(TAG, ""); + ESP_LOGV(TAG, "Partition table sucessfully imported, partitions found:"); + + while (part_ptr < part_end_ptr) { + esp_partition_info_t *p_part_item = (esp_partition_info_t *)part_ptr; + if (p_part_item->magic != ESP_PARTITION_MAGIC ) { + break; + } + + ESP_LOGV(TAG, " --------------"); + ESP_LOGV(TAG, " label: %s", p_part_item->label); + ESP_LOGV(TAG, " type: %s", esp_partition_type_to_str(p_part_item->type)); + ESP_LOGV(TAG, " subtype: %s", esp_partition_subtype_to_str(p_part_item->type, p_part_item->subtype)); + ESP_LOGV(TAG, " offset: 0x%08X", p_part_item->pos.offset); + ESP_LOGV(TAG, " size: %d", p_part_item->pos.size); + ESP_LOGV(TAG, " flags: %d", p_part_item->flags); + + part_ptr += sizeof(esp_partition_info_t); + } + + ESP_LOGV(TAG, ""); +#endif + + //return mmapped file starting address + *part_desc_addr_start = s_spiflash_mem_file_buf; + + return ESP_OK; +} + +esp_err_t esp_partition_file_munmap() +{ + if (s_spiflash_mem_file_buf == NULL) { + return ESP_ERR_NO_MEM; + } + if (s_spiflash_mem_file_size == 0) { + return ESP_ERR_INVALID_SIZE; + } + + if (munmap(s_spiflash_mem_file_buf, s_spiflash_mem_file_size) != 0) { + ESP_LOGE(TAG, "Failed to munmap() SPI FLASH memory emulation file: %s", strerror(errno)); + return ESP_ERR_INVALID_RESPONSE; + } + + s_spiflash_mem_file_buf = NULL; + + return ESP_OK; +} + +esp_err_t esp_partition_write(const esp_partition_t *partition, size_t dst_offset, const void *src, size_t size) +{ + assert(partition != NULL); + + if (partition->encrypted) { + return ESP_ERR_NOT_SUPPORTED; + } + if (dst_offset > partition->size) { + return ESP_ERR_INVALID_ARG; + } + if (dst_offset + size > partition->size) { + return ESP_ERR_INVALID_SIZE; + } + + uint8_t *write_buf = malloc(size); + if (write_buf == NULL) { + return ESP_ERR_NO_MEM; + } + + void *dst_addr = s_spiflash_mem_file_buf + partition->address + dst_offset; + ESP_LOGV(TAG, "esp_partition_write(): partition=%s dst_offset=%zu src=%p size=%zu (real dst address: %p)", partition->label, dst_offset, src, size, dst_addr); + + //read the contents first, AND with the write buffer (to emulate real NOR FLASH behavior) + memcpy(write_buf, dst_addr, size); + for (size_t x = 0; x < size; x++) { + write_buf[x] &= ((uint8_t *)src)[x]; + } + memcpy(dst_addr, write_buf, size); + free(write_buf); + + return ESP_OK; +} + +esp_err_t esp_partition_read(const esp_partition_t *partition, size_t src_offset, void *dst, size_t size) +{ + assert(partition != NULL); + + if (partition->encrypted) { + return ESP_ERR_NOT_SUPPORTED; + } + if (src_offset > partition->size) { + return ESP_ERR_INVALID_ARG; + } + if (src_offset + size > partition->size) { + return ESP_ERR_INVALID_SIZE; + } + + void *src_addr = s_spiflash_mem_file_buf + partition->address + src_offset; + + ESP_LOGV(TAG, "esp_partition_read(): partition=%s src_offset=%zu dst=%p size=%zu (real src address: %p)", partition->label, src_offset, dst, size, src_addr); + + memcpy(dst, src_addr, size); + + return ESP_OK; +} + +esp_err_t esp_partition_read_raw(const esp_partition_t *partition, size_t src_offset, void *dst, size_t size) +{ + ESP_LOGV(TAG, "esp_partition_read_raw(): calling esp_partition_read()"); + return esp_partition_read(partition, src_offset, dst, size); +} + +esp_err_t esp_partition_write_raw(const esp_partition_t *partition, size_t dst_offset, const void *src, size_t size) +{ + ESP_LOGV(TAG, "esp_partition_write_raw(): calling esp_partition_write()"); + return esp_partition_write(partition, dst_offset, src, size); +} + +esp_err_t esp_partition_erase_range(const esp_partition_t *partition, size_t offset, size_t size) +{ + assert(partition != NULL); + + if (offset > partition->size || offset % SPI_FLASH_SEC_SIZE != 0) { + return ESP_ERR_INVALID_ARG; + } + if (offset + size > partition->size || size % SPI_FLASH_SEC_SIZE != 0) { + return ESP_ERR_INVALID_SIZE; + } + + void *target_addr = s_spiflash_mem_file_buf + partition->address + offset; + ESP_LOGV(TAG, "esp_partition_erase_range(): partition=%s offset=%zu size=%zu (real target address: %p)", partition->label, offset, size, target_addr); + + //set all bits to 1 (NOR FLASH default) + memset(target_addr, 0xFF, size); + + return ESP_OK; +} diff --git a/components/spi_flash/partition_target.c b/components/spi_flash/partition_target.c new file mode 100644 index 0000000000..c0c626cdd8 --- /dev/null +++ b/components/spi_flash/partition_target.c @@ -0,0 +1,229 @@ +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include "sdkconfig.h" +#include "esp_flash_partitions.h" +#include "esp_attr.h" +#include "esp_flash.h" +#include "esp_partition.h" +#include "esp_flash_encrypt.h" +#include "esp_log.h" +#include "esp_rom_md5.h" +#include "esp_spi_flash.h" +#include "bootloader_common.h" +#include "esp_ota_ops.h" + +#define HASH_LEN 32 /* SHA-256 digest length */ + +esp_err_t esp_partition_read(const esp_partition_t *partition, + size_t src_offset, void *dst, size_t size) +{ + assert(partition != NULL); + if (src_offset > partition->size) { + return ESP_ERR_INVALID_ARG; + } + if (src_offset + size > partition->size) { + return ESP_ERR_INVALID_SIZE; + } + + if (!partition->encrypted) { +#ifndef CONFIG_SPI_FLASH_USE_LEGACY_IMPL + return esp_flash_read(partition->flash_chip, dst, partition->address + src_offset, size); +#else + return spi_flash_read(partition->address + src_offset, dst, size); +#endif // CONFIG_SPI_FLASH_USE_LEGACY_IMPL + } + +#if CONFIG_SPI_FLASH_ENABLE_ENCRYPTED_READ_WRITE + if (partition->flash_chip != esp_flash_default_chip) { + return ESP_ERR_NOT_SUPPORTED; + } + + /* Encrypted partitions need to be read via a cache mapping */ + const void *buf; + spi_flash_mmap_handle_t handle; + + esp_err_t err = esp_partition_mmap(partition, src_offset, size, + SPI_FLASH_MMAP_DATA, &buf, &handle); + if (err != ESP_OK) { + return err; + } + memcpy(dst, buf, size); + spi_flash_munmap(handle); + return ESP_OK; +#else + return ESP_ERR_NOT_SUPPORTED; +#endif // CONFIG_SPI_FLASH_ENABLE_ENCRYPTED_READ_WRITE +} + +esp_err_t esp_partition_write(const esp_partition_t *partition, + size_t dst_offset, const void *src, size_t size) +{ + assert(partition != NULL); + if (dst_offset > partition->size) { + return ESP_ERR_INVALID_ARG; + } + if (dst_offset + size > partition->size) { + return ESP_ERR_INVALID_SIZE; + } + dst_offset = partition->address + dst_offset; + if (!partition->encrypted) { +#ifndef CONFIG_SPI_FLASH_USE_LEGACY_IMPL + return esp_flash_write(partition->flash_chip, src, dst_offset, size); +#else + return spi_flash_write(dst_offset, src, size); +#endif // CONFIG_SPI_FLASH_USE_LEGACY_IMPL + } + +#if CONFIG_SPI_FLASH_ENABLE_ENCRYPTED_READ_WRITE + if (partition->flash_chip != esp_flash_default_chip) { + return ESP_ERR_NOT_SUPPORTED; + } + +#ifndef CONFIG_SPI_FLASH_USE_LEGACY_IMPL + return esp_flash_write_encrypted(partition->flash_chip, dst_offset, src, size); +#else + return spi_flash_write_encrypted(dst_offset, src, size); +#endif // CONFIG_SPI_FLASH_USE_LEGACY_IMPL +#else + return ESP_ERR_NOT_SUPPORTED; +#endif // CONFIG_SPI_FLASH_ENABLE_ENCRYPTED_READ_WRITE +} + +esp_err_t esp_partition_read_raw(const esp_partition_t *partition, + size_t src_offset, void *dst, size_t size) +{ + assert(partition != NULL); + if (src_offset > partition->size) { + return ESP_ERR_INVALID_ARG; + } + if (src_offset + size > partition->size) { + return ESP_ERR_INVALID_SIZE; + } + +#ifndef CONFIG_SPI_FLASH_USE_LEGACY_IMPL + return esp_flash_read(partition->flash_chip, dst, partition->address + src_offset, size); +#else + return spi_flash_read(partition->address + src_offset, dst, size); +#endif // CONFIG_SPI_FLASH_USE_LEGACY_IMPL +} + +esp_err_t esp_partition_write_raw(const esp_partition_t *partition, + size_t dst_offset, const void *src, size_t size) +{ + assert(partition != NULL); + if (dst_offset > partition->size) { + return ESP_ERR_INVALID_ARG; + } + if (dst_offset + size > partition->size) { + return ESP_ERR_INVALID_SIZE; + } + dst_offset = partition->address + dst_offset; + +#ifndef CONFIG_SPI_FLASH_USE_LEGACY_IMPL + return esp_flash_write(partition->flash_chip, src, dst_offset, size); +#else + return spi_flash_write(dst_offset, src, size); +#endif // CONFIG_SPI_FLASH_USE_LEGACY_IMPL +} + +esp_err_t esp_partition_erase_range(const esp_partition_t *partition, + size_t offset, size_t size) +{ + assert(partition != NULL); + if (offset > partition->size) { + return ESP_ERR_INVALID_ARG; + } + if (offset + size > partition->size) { + return ESP_ERR_INVALID_SIZE; + } + if (size % SPI_FLASH_SEC_SIZE != 0) { + return ESP_ERR_INVALID_SIZE; + } + if (offset % SPI_FLASH_SEC_SIZE != 0) { + return ESP_ERR_INVALID_ARG; + } +#ifndef CONFIG_SPI_FLASH_USE_LEGACY_IMPL + return esp_flash_erase_region(partition->flash_chip, partition->address + offset, size); +#else + return spi_flash_erase_range(partition->address + offset, size); +#endif // CONFIG_SPI_FLASH_USE_LEGACY_IMPL +} + +/* + * Note: current implementation ignores the possibility of multiple regions in the same partition being + * mapped. Reference counting and address space re-use is delegated to spi_flash_mmap. + * + * If this becomes a performance issue (i.e. if we need to map multiple regions within the partition), + * we can add esp_partition_mmapv which will accept an array of offsets and sizes, and return array of + * mmaped pointers, and a single handle for all these regions. + */ +esp_err_t esp_partition_mmap(const esp_partition_t *partition, size_t offset, size_t size, + spi_flash_mmap_memory_t memory, + const void **out_ptr, spi_flash_mmap_handle_t *out_handle) +{ + assert(partition != NULL); + if (offset > partition->size) { + return ESP_ERR_INVALID_ARG; + } + if (offset + size > partition->size) { + return ESP_ERR_INVALID_SIZE; + } + if (partition->flash_chip != esp_flash_default_chip) { + return ESP_ERR_NOT_SUPPORTED; + } + size_t phys_addr = partition->address + offset; + // offset within 64kB block + size_t region_offset = phys_addr & 0xffff; + size_t mmap_addr = phys_addr & 0xffff0000; + esp_err_t rc = spi_flash_mmap(mmap_addr, size + region_offset, memory, out_ptr, out_handle); + // adjust returned pointer to point to the correct offset + if (rc == ESP_OK) { + *out_ptr = (void *) (((ptrdiff_t) * out_ptr) + region_offset); + } + return rc; +} + +esp_err_t esp_partition_get_sha256(const esp_partition_t *partition, uint8_t *sha_256) +{ + return bootloader_common_get_sha256_of_partition(partition->address, partition->size, partition->type, sha_256); +} + +bool esp_partition_check_identity(const esp_partition_t *partition_1, const esp_partition_t *partition_2) +{ + uint8_t sha_256[2][HASH_LEN] = { 0 }; + + if (esp_partition_get_sha256(partition_1, sha_256[0]) == ESP_OK && + esp_partition_get_sha256(partition_2, sha_256[1]) == ESP_OK) { + + if (memcmp(sha_256[0], sha_256[1], HASH_LEN) == 0) { + // The partitions are identity + return true; + } + } + return false; +} + +bool esp_partition_main_flash_region_safe(size_t addr, size_t size) +{ + bool result = true; + if (addr <= ESP_PARTITION_TABLE_OFFSET + ESP_PARTITION_TABLE_MAX_LEN) { + return false; + } + const esp_partition_t *p = esp_ota_get_running_partition(); + if (addr >= p->address && addr < p->address + p->size) { + return false; + } + if (addr < p->address && addr + size > p->address) { + return false; + } + return result; +} diff --git a/components/spi_flash/sim/Makefile.files b/components/spi_flash/sim/Makefile.files index d9c7ae62e2..0fdb0c759d 100644 --- a/components/spi_flash/sim/Makefile.files +++ b/components/spi_flash/sim/Makefile.files @@ -4,6 +4,7 @@ SOURCE_FILES := \ flash_mock_util.c \ $(addprefix ../, \ partition.c \ + ../spi_flash/partition_target.c \ flash_ops.c \ esp32/flash_ops_esp32.c \ ) \ diff --git a/components/spi_flash/sim/stubs/log/include/esp_log.h b/components/spi_flash/sim/stubs/log/include/esp_log.h index ab9a6799ae..103e7703fd 100644 --- a/components/spi_flash/sim/stubs/log/include/esp_log.h +++ b/components/spi_flash/sim/stubs/log/include/esp_log.h @@ -61,9 +61,7 @@ void esp_log_write(esp_log_level_t level, const char* tag, const char* format, . #define ESP_LOGV( tag, format, ... ) if (LOG_LOCAL_LEVEL >= ESP_LOG_VERBOSE) { esp_log_write(ESP_LOG_VERBOSE, tag, LOG_FORMAT(V, format), esp_log_timestamp(), tag, ##__VA_ARGS__); } -// Assume that flash encryption is not enabled. Put here since in partition.c -// esp_log.h is included later than esp_flash_encrypt.h. -#define esp_flash_encryption_enabled() false +#define esp_flash_encryption_enabled() false #ifdef __cplusplus } diff --git a/tools/ci/check_copyright_ignore.txt b/tools/ci/check_copyright_ignore.txt index 8f4a0a8b91..ae52fb4d65 100644 --- a/tools/ci/check_copyright_ignore.txt +++ b/tools/ci/check_copyright_ignore.txt @@ -1680,7 +1680,6 @@ components/spi_flash/cache_utils.h components/spi_flash/include/esp_flash.h components/spi_flash/include/esp_flash_internal.h components/spi_flash/include/esp_flash_spi_init.h -components/spi_flash/include/esp_partition.h components/spi_flash/include/esp_spi_flash.h components/spi_flash/include/esp_spi_flash_counters.h components/spi_flash/include/memspi_host_driver.h