mirror of
https://github.com/espressif/esp-idf.git
synced 2026-05-03 19:41:55 +02:00
Storage: Partition APIs moved to the new component 'esp_partition'
All the partition handling API functions and data-types were moved from the 'spi_flash' component to the new one named 'esp_partition'. See Storage 5.x migration guide for more details
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
# Documentation: .gitlab/ci/README.md#manifest-file-to-control-the-buildtest-apps
|
||||
|
||||
components/esp_partition/host_test/partition_api_test:
|
||||
enable:
|
||||
- if: IDF_TARGET == "linux"
|
||||
reason: only test on linux
|
||||
@@ -0,0 +1,30 @@
|
||||
set(srcs "partition.c")
|
||||
set(priv_reqs esp_system bootloader_support spi_flash app_update partition_table)
|
||||
set(reqs)
|
||||
set(include_dirs "include")
|
||||
|
||||
idf_build_get_property(target IDF_TARGET)
|
||||
if(${target} STREQUAL "linux")
|
||||
list(APPEND srcs "partition_linux.c")
|
||||
set(priv_reqs partition_table)
|
||||
|
||||
# Steal some include directories from bootloader_support and hal components:
|
||||
idf_component_get_property(hal_dir hal COMPONENT_DIR)
|
||||
idf_component_get_property(bootloader_support_dir bootloader_support COMPONENT_DIR)
|
||||
list(APPEND include_dirs include ${hal_dir}/include ${bootloader_support_dir}/include)
|
||||
else()
|
||||
list(APPEND srcs "partition_target.c")
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS "${srcs}"
|
||||
INCLUDE_DIRS ${include_dirs}
|
||||
REQUIRES ${reqs}
|
||||
PRIV_REQUIRES ${priv_reqs})
|
||||
|
||||
if(CMAKE_C_COMPILER_ID MATCHES "GNU")
|
||||
# These flags are GCC specific
|
||||
set_property(SOURCE ${cache_srcs} APPEND_STRING PROPERTY COMPILE_FLAGS
|
||||
" -fno-inline-small-functions -fno-inline-functions-called-once")
|
||||
endif()
|
||||
|
||||
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")
|
||||
@@ -0,0 +1,11 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
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)
|
||||
|
||||
add_dependencies(partition_api_test.elf partition-table)
|
||||
@@ -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`
|
||||
```
|
||||
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "partition_api_test.c"
|
||||
REQUIRES esp_partition unity)
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Linux host partition API test
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "esp_err.h"
|
||||
#include "esp_partition.h"
|
||||
#include "esp_private/partition_linux.h"
|
||||
#include "unity.h"
|
||||
#include "unity_fixture.h"
|
||||
|
||||
|
||||
TEST_GROUP(partition_api);
|
||||
|
||||
TEST_SETUP(partition_api)
|
||||
{
|
||||
}
|
||||
|
||||
TEST_TEAR_DOWN(partition_api)
|
||||
{
|
||||
}
|
||||
|
||||
TEST(partition_api, test_partition_find_basic)
|
||||
{
|
||||
esp_partition_iterator_t iter = esp_partition_find(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "storage");
|
||||
TEST_ASSERT_NOT_NULL(iter);
|
||||
|
||||
const esp_partition_t *part = esp_partition_get(iter);
|
||||
TEST_ASSERT_NOT_NULL(part);
|
||||
|
||||
esp_partition_iterator_release(iter);
|
||||
}
|
||||
|
||||
TEST(partition_api, test_partition_find_app)
|
||||
{
|
||||
esp_partition_iterator_t iter = esp_partition_find(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_ANY, NULL);
|
||||
TEST_ASSERT_NOT_NULL(iter);
|
||||
size_t counter = 0;
|
||||
|
||||
while (iter != NULL) {
|
||||
const esp_partition_t *part_data = esp_partition_get(iter);
|
||||
counter++;
|
||||
TEST_ASSERT_NOT_NULL(part_data);
|
||||
iter = esp_partition_next(iter);
|
||||
}
|
||||
esp_partition_iterator_release(iter);
|
||||
}
|
||||
|
||||
TEST(partition_api, test_partition_find_data)
|
||||
{
|
||||
esp_partition_iterator_t iter = esp_partition_find(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, NULL);
|
||||
TEST_ASSERT_NOT_NULL(iter);
|
||||
size_t counter = 0;
|
||||
|
||||
while (iter != NULL) {
|
||||
const esp_partition_t *part_data = esp_partition_get(iter);
|
||||
counter++;
|
||||
TEST_ASSERT_NOT_NULL(part_data);
|
||||
iter = esp_partition_next(iter);
|
||||
}
|
||||
esp_partition_iterator_release(iter);
|
||||
}
|
||||
|
||||
TEST(partition_api, test_partition_find_first)
|
||||
{
|
||||
const esp_partition_t *partition_app = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_ANY, NULL);
|
||||
TEST_ASSERT_NOT_NULL(partition_app);
|
||||
|
||||
const esp_partition_t *partition_data = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "storage");
|
||||
TEST_ASSERT_NOT_NULL(partition_data);
|
||||
}
|
||||
|
||||
TEST(partition_api, test_partition_ops)
|
||||
{
|
||||
const esp_partition_t *partition_data = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "storage");
|
||||
TEST_ASSERT_NOT_NULL(partition_data);
|
||||
|
||||
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);
|
||||
TEST_ESP_OK(err);
|
||||
|
||||
//9. esp_partition_read/raw
|
||||
uint8_t buffout[32] = {0};
|
||||
err = esp_partition_read(partition_data, off, (void *)buffout, bufsize);
|
||||
TEST_ESP_OK(err);
|
||||
|
||||
//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, partition_data->erase_size);
|
||||
assert(esp_partition_read(partition_data, off, (void *)buffout, bufsize) == ESP_OK);
|
||||
TEST_ESP_OK(err);
|
||||
TEST_ASSERT_EQUAL(0, memcmp(buffout, buferase, bufsize));
|
||||
|
||||
//11. esp_partition_verify (partition_data)
|
||||
const esp_partition_t *verified_partition = esp_partition_verify(partition_data);
|
||||
TEST_ASSERT_NOT_NULL(verified_partition);
|
||||
}
|
||||
|
||||
TEST_GROUP_RUNNER(partition_api)
|
||||
{
|
||||
RUN_TEST_CASE(partition_api, test_partition_find_basic);
|
||||
RUN_TEST_CASE(partition_api, test_partition_find_app);
|
||||
RUN_TEST_CASE(partition_api, test_partition_find_data);
|
||||
RUN_TEST_CASE(partition_api, test_partition_find_first);
|
||||
RUN_TEST_CASE(partition_api, test_partition_ops);
|
||||
}
|
||||
|
||||
static void run_all_tests(void)
|
||||
{
|
||||
RUN_TEST_GROUP(partition_api);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
UNITY_MAIN_FUNC(run_all_tests);
|
||||
return 0;
|
||||
}
|
||||
@@ -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,
|
||||
|
@@ -0,0 +1,8 @@
|
||||
CONFIG_IDF_TARGET="linux"
|
||||
CONFIG_COMPILER_CXX_EXCEPTIONS=y
|
||||
CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=n
|
||||
CONFIG_UNITY_ENABLE_FIXTURE=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partition_table.csv"
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE="4MB"
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
|
||||
@@ -0,0 +1,461 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef __ESP_PARTITION_H__
|
||||
#define __ESP_PARTITION_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include "esp_err.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @file esp_partition.h
|
||||
* @brief Partition APIs
|
||||
*/
|
||||
|
||||
/** @cond */
|
||||
typedef struct esp_flash_t esp_flash_t;
|
||||
/** @endcond */
|
||||
|
||||
/**
|
||||
* @brief Enumeration which specifies memory space requested in an mmap call
|
||||
*/
|
||||
typedef enum {
|
||||
ESP_PARTITION_MMAP_DATA, /**< map to data memory (Vaddr0), allows byte-aligned access, 4 MB total */
|
||||
ESP_PARTITION_MMAP_INST, /**< map to instruction memory (Vaddr1-3), allows only 4-byte-aligned access, 11 MB total */
|
||||
} esp_partition_mmap_memory_t;
|
||||
|
||||
/**
|
||||
* @brief Opaque handle for memory region obtained from esp_partition_mmap.
|
||||
*/
|
||||
typedef uint32_t esp_partition_mmap_handle_t;
|
||||
|
||||
/**
|
||||
* @brief Partition type
|
||||
*
|
||||
* @note Partition types with integer value 0x00-0x3F are reserved for partition types defined by ESP-IDF.
|
||||
* Any other integer value 0x40-0xFE can be used by individual applications, without restriction.
|
||||
*
|
||||
* @internal Keep this enum in sync with PartitionDefinition class gen_esp32part.py @endinternal
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
ESP_PARTITION_TYPE_APP = 0x00, //!< Application partition type
|
||||
ESP_PARTITION_TYPE_DATA = 0x01, //!< Data partition type
|
||||
|
||||
ESP_PARTITION_TYPE_ANY = 0xff, //!< Used to search for partitions with any type
|
||||
} esp_partition_type_t;
|
||||
|
||||
/**
|
||||
* @brief Partition subtype
|
||||
*
|
||||
* @note These ESP-IDF-defined partition subtypes apply to partitions of type ESP_PARTITION_TYPE_APP
|
||||
* and ESP_PARTITION_TYPE_DATA.
|
||||
*
|
||||
* Application-defined partition types (0x40-0xFE) can set any numeric subtype value.
|
||||
*
|
||||
* @internal Keep this enum in sync with PartitionDefinition class gen_esp32part.py @endinternal
|
||||
*/
|
||||
typedef enum {
|
||||
ESP_PARTITION_SUBTYPE_APP_FACTORY = 0x00, //!< Factory application partition
|
||||
ESP_PARTITION_SUBTYPE_APP_OTA_MIN = 0x10, //!< Base for OTA partition subtypes
|
||||
ESP_PARTITION_SUBTYPE_APP_OTA_0 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 0, //!< OTA partition 0
|
||||
ESP_PARTITION_SUBTYPE_APP_OTA_1 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 1, //!< OTA partition 1
|
||||
ESP_PARTITION_SUBTYPE_APP_OTA_2 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 2, //!< OTA partition 2
|
||||
ESP_PARTITION_SUBTYPE_APP_OTA_3 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 3, //!< OTA partition 3
|
||||
ESP_PARTITION_SUBTYPE_APP_OTA_4 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 4, //!< OTA partition 4
|
||||
ESP_PARTITION_SUBTYPE_APP_OTA_5 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 5, //!< OTA partition 5
|
||||
ESP_PARTITION_SUBTYPE_APP_OTA_6 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 6, //!< OTA partition 6
|
||||
ESP_PARTITION_SUBTYPE_APP_OTA_7 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 7, //!< OTA partition 7
|
||||
ESP_PARTITION_SUBTYPE_APP_OTA_8 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 8, //!< OTA partition 8
|
||||
ESP_PARTITION_SUBTYPE_APP_OTA_9 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 9, //!< OTA partition 9
|
||||
ESP_PARTITION_SUBTYPE_APP_OTA_10 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 10,//!< OTA partition 10
|
||||
ESP_PARTITION_SUBTYPE_APP_OTA_11 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 11,//!< OTA partition 11
|
||||
ESP_PARTITION_SUBTYPE_APP_OTA_12 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 12,//!< OTA partition 12
|
||||
ESP_PARTITION_SUBTYPE_APP_OTA_13 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 13,//!< OTA partition 13
|
||||
ESP_PARTITION_SUBTYPE_APP_OTA_14 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 14,//!< OTA partition 14
|
||||
ESP_PARTITION_SUBTYPE_APP_OTA_15 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 15,//!< OTA partition 15
|
||||
ESP_PARTITION_SUBTYPE_APP_OTA_MAX = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 16,//!< Max subtype of OTA partition
|
||||
ESP_PARTITION_SUBTYPE_APP_TEST = 0x20, //!< Test application partition
|
||||
|
||||
ESP_PARTITION_SUBTYPE_DATA_OTA = 0x00, //!< OTA selection partition
|
||||
ESP_PARTITION_SUBTYPE_DATA_PHY = 0x01, //!< PHY init data partition
|
||||
ESP_PARTITION_SUBTYPE_DATA_NVS = 0x02, //!< NVS partition
|
||||
ESP_PARTITION_SUBTYPE_DATA_COREDUMP = 0x03, //!< COREDUMP partition
|
||||
ESP_PARTITION_SUBTYPE_DATA_NVS_KEYS = 0x04, //!< Partition for NVS keys
|
||||
ESP_PARTITION_SUBTYPE_DATA_EFUSE_EM = 0x05, //!< Partition for emulate eFuse bits
|
||||
ESP_PARTITION_SUBTYPE_DATA_UNDEFINED = 0x06, //!< Undefined (or unspecified) data partition
|
||||
|
||||
ESP_PARTITION_SUBTYPE_DATA_ESPHTTPD = 0x80, //!< ESPHTTPD partition
|
||||
ESP_PARTITION_SUBTYPE_DATA_FAT = 0x81, //!< FAT partition
|
||||
ESP_PARTITION_SUBTYPE_DATA_SPIFFS = 0x82, //!< SPIFFS partition
|
||||
|
||||
#if __has_include("extra_partition_subtypes.inc")
|
||||
#include "extra_partition_subtypes.inc"
|
||||
#endif
|
||||
|
||||
ESP_PARTITION_SUBTYPE_ANY = 0xff, //!< Used to search for partitions with any subtype
|
||||
} esp_partition_subtype_t;
|
||||
|
||||
/**
|
||||
* @brief Convenience macro to get esp_partition_subtype_t value for the i-th OTA partition
|
||||
*/
|
||||
#define ESP_PARTITION_SUBTYPE_OTA(i) ((esp_partition_subtype_t)(ESP_PARTITION_SUBTYPE_APP_OTA_MIN + ((i) & 0xf)))
|
||||
|
||||
/**
|
||||
* @brief Opaque partition iterator type
|
||||
*/
|
||||
typedef struct esp_partition_iterator_opaque_* esp_partition_iterator_t;
|
||||
|
||||
/**
|
||||
* @brief partition information structure
|
||||
*
|
||||
* This is not the format in flash, that format is esp_partition_info_t.
|
||||
*
|
||||
* However, this is the format used by this API.
|
||||
*/
|
||||
typedef struct {
|
||||
esp_flash_t* flash_chip; /*!< SPI flash chip on which the partition resides */
|
||||
esp_partition_type_t type; /*!< partition type (app/data) */
|
||||
esp_partition_subtype_t subtype; /*!< partition subtype */
|
||||
uint32_t address; /*!< starting address of the partition in flash */
|
||||
uint32_t size; /*!< size of the partition, in bytes */
|
||||
uint32_t erase_size; /*!< size the erase operation should be aligned to */
|
||||
char label[17]; /*!< partition label, zero-terminated ASCII string */
|
||||
bool encrypted; /*!< flag is set to true if partition is encrypted */
|
||||
} esp_partition_t;
|
||||
|
||||
/**
|
||||
* @brief Find partition based on one or more parameters
|
||||
*
|
||||
* @param type Partition type, one of esp_partition_type_t values or an 8-bit unsigned integer.
|
||||
* To find all partitions, no matter the type, use ESP_PARTITION_TYPE_ANY, and set
|
||||
* subtype argument to ESP_PARTITION_SUBTYPE_ANY.
|
||||
* @param subtype Partition subtype, one of esp_partition_subtype_t values or an 8-bit unsigned integer.
|
||||
* To find all partitions of given type, use ESP_PARTITION_SUBTYPE_ANY.
|
||||
* @param label (optional) Partition label. Set this value if looking
|
||||
* for partition with a specific name. Pass NULL otherwise.
|
||||
*
|
||||
* @return iterator which can be used to enumerate all the partitions found,
|
||||
* or NULL if no partitions were found.
|
||||
* Iterator obtained through this function has to be released
|
||||
* using esp_partition_iterator_release when not used any more.
|
||||
*/
|
||||
esp_partition_iterator_t esp_partition_find(esp_partition_type_t type, esp_partition_subtype_t subtype, const char* label);
|
||||
|
||||
/**
|
||||
* @brief Find first partition based on one or more parameters
|
||||
*
|
||||
* @param type Partition type, one of esp_partition_type_t values or an 8-bit unsigned integer.
|
||||
* To find all partitions, no matter the type, use ESP_PARTITION_TYPE_ANY, and set
|
||||
* subtype argument to ESP_PARTITION_SUBTYPE_ANY.
|
||||
* @param subtype Partition subtype, one of esp_partition_subtype_t values or an 8-bit unsigned integer
|
||||
* To find all partitions of given type, use ESP_PARTITION_SUBTYPE_ANY.
|
||||
* @param label (optional) Partition label. Set this value if looking
|
||||
* for partition with a specific name. Pass NULL otherwise.
|
||||
*
|
||||
* @return pointer to esp_partition_t structure, or NULL if no partition is found.
|
||||
* This pointer is valid for the lifetime of the application.
|
||||
*/
|
||||
const esp_partition_t* esp_partition_find_first(esp_partition_type_t type, esp_partition_subtype_t subtype, const char* label);
|
||||
|
||||
/**
|
||||
* @brief Get esp_partition_t structure for given partition
|
||||
*
|
||||
* @param iterator Iterator obtained using esp_partition_find. Must be non-NULL.
|
||||
*
|
||||
* @return pointer to esp_partition_t structure. This pointer is valid for the lifetime
|
||||
* of the application.
|
||||
*/
|
||||
const esp_partition_t* esp_partition_get(esp_partition_iterator_t iterator);
|
||||
|
||||
/**
|
||||
* @brief Move partition iterator to the next partition found
|
||||
*
|
||||
* Any copies of the iterator will be invalid after this call.
|
||||
*
|
||||
* @param iterator Iterator obtained using esp_partition_find. Must be non-NULL.
|
||||
*
|
||||
* @return NULL if no partition was found, valid esp_partition_iterator_t otherwise.
|
||||
*/
|
||||
esp_partition_iterator_t esp_partition_next(esp_partition_iterator_t iterator);
|
||||
|
||||
/**
|
||||
* @brief Release partition iterator
|
||||
*
|
||||
* @param iterator Iterator obtained using esp_partition_find.
|
||||
* The iterator is allowed to be NULL, so it is not necessary to check its value
|
||||
* before calling this function.
|
||||
*
|
||||
*/
|
||||
void esp_partition_iterator_release(esp_partition_iterator_t iterator);
|
||||
|
||||
/**
|
||||
* @brief Verify partition data
|
||||
*
|
||||
* Given a pointer to partition data, verify this partition exists in the partition table (all fields match.)
|
||||
*
|
||||
* This function is also useful to take partition data which may be in a RAM buffer and convert it to a pointer to the
|
||||
* permanent partition data stored in flash.
|
||||
*
|
||||
* Pointers returned from this function can be compared directly to the address of any pointer returned from
|
||||
* esp_partition_get(), as a test for equality.
|
||||
*
|
||||
* @param partition Pointer to partition data to verify. Must be non-NULL. All fields of this structure must match the
|
||||
* partition table entry in flash for this function to return a successful match.
|
||||
*
|
||||
* @return
|
||||
* - 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);
|
||||
|
||||
/**
|
||||
* @brief Read data from the partition
|
||||
*
|
||||
* Partitions marked with an encryption flag will automatically be
|
||||
* be read and decrypted via a cache mapping.
|
||||
*
|
||||
* @param partition Pointer to partition structure obtained using
|
||||
* esp_partition_find_first or esp_partition_get.
|
||||
* Must be non-NULL.
|
||||
* @param dst Pointer to the buffer where data should be stored.
|
||||
* Pointer must be non-NULL and buffer must be at least 'size' bytes long.
|
||||
* @param src_offset Address of the data to be read, relative to the
|
||||
* beginning of the partition.
|
||||
* @param size Size of data to be read, in bytes.
|
||||
*
|
||||
* @return ESP_OK, if data was read successfully;
|
||||
* ESP_ERR_INVALID_ARG, if src_offset exceeds partition size;
|
||||
* ESP_ERR_INVALID_SIZE, if read would go out of bounds of the partition;
|
||||
* or one of error codes from lower-level flash driver.
|
||||
*/
|
||||
esp_err_t esp_partition_read(const esp_partition_t* partition,
|
||||
size_t src_offset, void* dst, size_t size);
|
||||
|
||||
/**
|
||||
* @brief Write data to the partition
|
||||
*
|
||||
* Before writing data to flash, corresponding region of flash needs to be erased.
|
||||
* This can be done using esp_partition_erase_range function.
|
||||
*
|
||||
* Partitions marked with an encryption flag will automatically be
|
||||
* written via the esp_flash_write_encrypted() function. If writing to
|
||||
* an encrypted partition, all write offsets and lengths must be
|
||||
* multiples of 16 bytes. See the esp_flash_write_encrypted() function
|
||||
* for more details. Unencrypted partitions do not have this
|
||||
* restriction.
|
||||
*
|
||||
* @param partition Pointer to partition structure obtained using
|
||||
* esp_partition_find_first or esp_partition_get.
|
||||
* Must be non-NULL.
|
||||
* @param dst_offset Address where the data should be written, relative to the
|
||||
* beginning of the partition.
|
||||
* @param src Pointer to the source buffer. Pointer must be non-NULL and
|
||||
* buffer must be at least 'size' bytes long.
|
||||
* @param size Size of data to be written, in bytes.
|
||||
*
|
||||
* @note Prior to writing to flash memory, make sure it has been erased with
|
||||
* esp_partition_erase_range call.
|
||||
*
|
||||
* @return ESP_OK, if data was written successfully;
|
||||
* ESP_ERR_INVALID_ARG, if dst_offset exceeds partition size;
|
||||
* ESP_ERR_INVALID_SIZE, if write would go out of bounds of the partition;
|
||||
* or one of error codes from lower-level flash driver.
|
||||
*/
|
||||
esp_err_t esp_partition_write(const esp_partition_t* partition,
|
||||
size_t dst_offset, const void* src, size_t size);
|
||||
|
||||
/**
|
||||
* @brief Read data from the partition without any transformation/decryption.
|
||||
*
|
||||
* @note This function is essentially the same as \c esp_partition_read() above.
|
||||
* It just never decrypts data but returns it as is.
|
||||
*
|
||||
* @param partition Pointer to partition structure obtained using
|
||||
* esp_partition_find_first or esp_partition_get.
|
||||
* Must be non-NULL.
|
||||
* @param dst Pointer to the buffer where data should be stored.
|
||||
* Pointer must be non-NULL and buffer must be at least 'size' bytes long.
|
||||
* @param src_offset Address of the data to be read, relative to the
|
||||
* beginning of the partition.
|
||||
* @param size Size of data to be read, in bytes.
|
||||
*
|
||||
* @return ESP_OK, if data was read successfully;
|
||||
* ESP_ERR_INVALID_ARG, if src_offset exceeds partition size;
|
||||
* ESP_ERR_INVALID_SIZE, if read would go out of bounds of the partition;
|
||||
* or one of error codes from lower-level flash driver.
|
||||
*/
|
||||
esp_err_t esp_partition_read_raw(const esp_partition_t* partition,
|
||||
size_t src_offset, void* dst, size_t size);
|
||||
|
||||
/**
|
||||
* @brief Write data to the partition without any transformation/encryption.
|
||||
*
|
||||
* @note This function is essentially the same as \c esp_partition_write() above.
|
||||
* It just never encrypts data but writes it as is.
|
||||
*
|
||||
* Before writing data to flash, corresponding region of flash needs to be erased.
|
||||
* This can be done using esp_partition_erase_range function.
|
||||
*
|
||||
* @param partition Pointer to partition structure obtained using
|
||||
* esp_partition_find_first or esp_partition_get.
|
||||
* Must be non-NULL.
|
||||
* @param dst_offset Address where the data should be written, relative to the
|
||||
* beginning of the partition.
|
||||
* @param src Pointer to the source buffer. Pointer must be non-NULL and
|
||||
* buffer must be at least 'size' bytes long.
|
||||
* @param size Size of data to be written, in bytes.
|
||||
*
|
||||
* @note Prior to writing to flash memory, make sure it has been erased with
|
||||
* esp_partition_erase_range call.
|
||||
*
|
||||
* @return ESP_OK, if data was written successfully;
|
||||
* ESP_ERR_INVALID_ARG, if dst_offset exceeds partition size;
|
||||
* ESP_ERR_INVALID_SIZE, if write would go out of bounds of the partition;
|
||||
* or one of the error codes from lower-level flash driver.
|
||||
*/
|
||||
esp_err_t esp_partition_write_raw(const esp_partition_t* partition,
|
||||
size_t dst_offset, const void* src, size_t size);
|
||||
|
||||
/**
|
||||
* @brief Erase part of the partition
|
||||
*
|
||||
* @param partition Pointer to partition structure obtained using
|
||||
* esp_partition_find_first or esp_partition_get.
|
||||
* Must be non-NULL.
|
||||
* @param offset Offset from the beginning of partition where erase operation
|
||||
* should start. Must be aligned to partition->erase_size.
|
||||
* @param size Size of the range which should be erased, in bytes.
|
||||
* Must be divisible by partition->erase_size.
|
||||
*
|
||||
* @return ESP_OK, if the range was erased successfully;
|
||||
* ESP_ERR_INVALID_ARG, if iterator or dst are NULL;
|
||||
* ESP_ERR_INVALID_SIZE, if erase would go out of bounds of the partition;
|
||||
* or one of error codes from lower-level flash driver.
|
||||
*/
|
||||
esp_err_t esp_partition_erase_range(const esp_partition_t* partition,
|
||||
size_t offset, size_t size);
|
||||
|
||||
/**
|
||||
* @brief Configure MMU to map partition into data memory
|
||||
*
|
||||
* Unlike spi_flash_mmap function, which requires a 64kB aligned base address,
|
||||
* this function doesn't impose such a requirement.
|
||||
* If offset results in a flash address which is not aligned to 64kB boundary,
|
||||
* address will be rounded to the lower 64kB boundary, so that mapped region
|
||||
* includes requested range.
|
||||
* Pointer returned via out_ptr argument will be adjusted to point to the
|
||||
* requested offset (not necessarily to the beginning of mmap-ed region).
|
||||
*
|
||||
* To release mapped memory, pass handle returned via out_handle argument to
|
||||
* esp_partition_munmap function.
|
||||
*
|
||||
* @param partition Pointer to partition structure obtained using
|
||||
* esp_partition_find_first or esp_partition_get.
|
||||
* Must be non-NULL.
|
||||
* @param offset Offset from the beginning of partition where mapping should start.
|
||||
* @param size Size of the area to be mapped.
|
||||
* @param memory Memory space where the region should be mapped
|
||||
* @param out_ptr Output, pointer to the mapped memory region
|
||||
* @param out_handle Output, handle which should be used for esp_partition_munmap call
|
||||
*
|
||||
* @return ESP_OK, if successful
|
||||
*/
|
||||
esp_err_t esp_partition_mmap(const esp_partition_t* partition, size_t offset, size_t size,
|
||||
esp_partition_mmap_memory_t memory,
|
||||
const void** out_ptr, esp_partition_mmap_handle_t* out_handle);
|
||||
|
||||
/**
|
||||
* @brief Release region previously obtained using esp_partition_mmap
|
||||
*
|
||||
* @note Calling this function will not necessarily unmap memory region.
|
||||
* Region will only be unmapped when there are no other handles which
|
||||
* reference this region. In case of partially overlapping regions
|
||||
* it is possible that memory will be unmapped partially.
|
||||
*
|
||||
* @param handle Handle obtained from spi_flash_mmap
|
||||
*/
|
||||
void esp_partition_munmap(esp_partition_mmap_handle_t handle);
|
||||
|
||||
/**
|
||||
* @brief Get SHA-256 digest for required partition.
|
||||
*
|
||||
* For apps with SHA-256 appended to the app image, the result is the appended SHA-256 value for the app image content.
|
||||
* The hash is verified before returning, if app content is invalid then the function returns ESP_ERR_IMAGE_INVALID.
|
||||
* For apps without SHA-256 appended to the image, the result is the SHA-256 of all bytes in the app image.
|
||||
* For other partition types, the result is the SHA-256 of the entire partition.
|
||||
*
|
||||
* @param[in] partition Pointer to info for partition containing app or data. (fields: address, size and type, are required to be filled).
|
||||
* @param[out] sha_256 Returned SHA-256 digest for a given partition.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: In case of successful operation.
|
||||
* - ESP_ERR_INVALID_ARG: The size was 0 or the sha_256 was NULL.
|
||||
* - ESP_ERR_NO_MEM: Cannot allocate memory for sha256 operation.
|
||||
* - 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);
|
||||
|
||||
/**
|
||||
* @brief Check for the identity of two partitions by SHA-256 digest.
|
||||
*
|
||||
* @param[in] partition_1 Pointer to info for partition 1 containing app or data. (fields: address, size and type, are required to be filled).
|
||||
* @param[in] partition_2 Pointer to info for partition 2 containing app or data. (fields: address, size and type, are required to be filled).
|
||||
*
|
||||
* @return
|
||||
* - 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);
|
||||
|
||||
/**
|
||||
* @brief Register a partition on an external flash chip
|
||||
*
|
||||
* This API allows designating certain areas of external flash chips (identified by the esp_flash_t structure)
|
||||
* as partitions. This allows using them with components which access SPI flash through the esp_partition API.
|
||||
*
|
||||
* @param flash_chip Pointer to the structure identifying the flash chip
|
||||
* @param offset Address in bytes, where the partition starts
|
||||
* @param size Size of the partition in bytes
|
||||
* @param label Partition name
|
||||
* @param type One of the partition types (ESP_PARTITION_TYPE_*), or an integer. Note that applications can not be booted from external flash
|
||||
* chips, so using ESP_PARTITION_TYPE_APP is not supported.
|
||||
* @param subtype One of the partition subtypes (ESP_PARTITION_SUBTYPE_*), or an integer.
|
||||
* @param[out] out_partition Output, if non-NULL, receives the pointer to the resulting esp_partition_t structure
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_NO_MEM if memory allocation has failed
|
||||
* - ESP_ERR_INVALID_ARG if the new partition overlaps another partition on the same flash chip
|
||||
* - 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);
|
||||
|
||||
/**
|
||||
* @brief Deregister the partition previously registered using esp_partition_register_external
|
||||
* @param partition pointer to the partition structure obtained from esp_partition_register_external,
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_NOT_FOUND if the partition pointer is not found
|
||||
* - ESP_ERR_INVALID_ARG if the partition comes from the partition table
|
||||
* - ESP_ERR_INVALID_ARG if the partition was not registered using
|
||||
* esp_partition_register_external function.
|
||||
*/
|
||||
esp_err_t esp_partition_deregister_external(const esp_partition_t* partition);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __ESP_PARTITION_H__ */
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#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 emulated sector size for the partition API on Linux */
|
||||
#define ESP_PARTITION_EMULATED_SECTOR_SIZE 0x1000
|
||||
|
||||
/**
|
||||
* @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
|
||||
@@ -0,0 +1,445 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/lock.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_flash_partitions.h"
|
||||
#include "esp_attr.h"
|
||||
#include "esp_partition.h"
|
||||
|
||||
#if !CONFIG_IDF_TARGET_LINUX
|
||||
#include "esp_flash.h"
|
||||
#include "esp_flash_encrypt.h"
|
||||
#endif
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_rom_md5.h"
|
||||
#include "bootloader_util.h"
|
||||
|
||||
#if CONFIG_IDF_TARGET_LINUX
|
||||
#if __has_include(<bsd/string.h>)
|
||||
#include <bsd/string.h>
|
||||
#endif
|
||||
#include "esp_private/partition_linux.h"
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_IDF_TARGET_LINUX
|
||||
#define MMU_PAGE_SIZE CONFIG_MMU_PAGE_SIZE
|
||||
#else
|
||||
// No relation to the page size on Linux; assume the same value as on ESP32
|
||||
#define MMU_PAGE_SIZE 65536
|
||||
#endif // CONFIG_MMU_PAGE_SIZE
|
||||
|
||||
#ifndef NDEBUG
|
||||
// Enable built-in checks in queue.h in debug builds
|
||||
#define INVARIANTS
|
||||
#endif
|
||||
#include "sys/queue.h"
|
||||
|
||||
typedef struct partition_list_item_ {
|
||||
esp_partition_t info;
|
||||
bool user_registered;
|
||||
SLIST_ENTRY(partition_list_item_) next;
|
||||
} 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_iterator_opaque_t;
|
||||
|
||||
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) & ~(MMU_PAGE_SIZE - 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);
|
||||
size_t mapped_size = ESP_PARTITION_EMULATED_SECTOR_SIZE;
|
||||
#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);
|
||||
size_t mapped_size = SPI_FLASH_SEC_SIZE;
|
||||
#endif
|
||||
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// calculate partition address within mmap-ed region
|
||||
p_start += partition_pad;
|
||||
p_end = p_start + mapped_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;
|
||||
#if CONFIG_IDF_TARGET_LINUX
|
||||
item->info.erase_size = ESP_PARTITION_EMULATED_SECTOR_SIZE;
|
||||
#else
|
||||
item->info.erase_size = SPI_FLASH_SEC_SIZE;
|
||||
#endif
|
||||
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)
|
||||
{
|
||||
esp_err_t err = ESP_OK;
|
||||
if (SLIST_EMPTY(&s_partition_list)) {
|
||||
// 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_LOGV(TAG, "Loading the partition table");
|
||||
err = load_partitions();
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "load_partitions returned 0x%x", err);
|
||||
}
|
||||
}
|
||||
_lock_release(&s_partition_list_lock);
|
||||
}
|
||||
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));
|
||||
if (it == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
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)
|
||||
{
|
||||
if (ensure_partitions_loaded() != ESP_OK) {
|
||||
return NULL;
|
||||
}
|
||||
// Searching for a specific subtype without specifying the type doesn't make
|
||||
// sense, and is likely a usage error.
|
||||
if (type == ESP_PARTITION_TYPE_ANY && subtype != ESP_PARTITION_SUBTYPE_ANY) {
|
||||
return NULL;
|
||||
}
|
||||
// create an iterator pointing to the start of the list
|
||||
// (next item will be the first one)
|
||||
esp_partition_iterator_t it = iterator_create(type, subtype, label);
|
||||
if (it == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
// advance iterator to the next item which matches constraints
|
||||
it = esp_partition_next(it);
|
||||
// if nothing found, it == NULL and iterator has been released
|
||||
return it;
|
||||
}
|
||||
|
||||
esp_partition_iterator_t esp_partition_next(esp_partition_iterator_t it)
|
||||
{
|
||||
assert(it);
|
||||
// iterator reached the end of linked list?
|
||||
if (it->next_item == NULL) {
|
||||
esp_partition_iterator_release(it);
|
||||
return NULL;
|
||||
}
|
||||
_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;
|
||||
if (it->type != ESP_PARTITION_TYPE_ANY && it->type != p->type) {
|
||||
continue;
|
||||
}
|
||||
if (it->subtype != ESP_PARTITION_SUBTYPE_ANY && it->subtype != p->subtype) {
|
||||
continue;
|
||||
}
|
||||
if (it->label != NULL && strcmp(it->label, p->label) != 0) {
|
||||
continue;
|
||||
}
|
||||
// all constraints match, bail out
|
||||
break;
|
||||
}
|
||||
_lock_release(&s_partition_list_lock);
|
||||
if (it->next_item == NULL) {
|
||||
esp_partition_iterator_release(it);
|
||||
return NULL;
|
||||
}
|
||||
it->info = &it->next_item->info;
|
||||
it->next_item = SLIST_NEXT(it->next_item, next);
|
||||
return it;
|
||||
}
|
||||
|
||||
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);
|
||||
esp_partition_iterator_release(it);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
void esp_partition_iterator_release(esp_partition_iterator_t iterator)
|
||||
{
|
||||
// iterator == NULL is okay
|
||||
free(iterator);
|
||||
}
|
||||
|
||||
const esp_partition_t *esp_partition_get(esp_partition_iterator_t iterator)
|
||||
{
|
||||
assert(iterator != NULL);
|
||||
return iterator->info;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#if CONFIG_IDF_TARGET_LINUX
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
|
||||
#else
|
||||
if (offset + size > flash_chip->size) {
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
}
|
||||
#endif // CONFIG_IDF_TARGET_LINUX
|
||||
|
||||
esp_err_t err = ensure_partitions_loaded();
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
partition_list_item_t *item = (partition_list_item_t *) calloc(sizeof(partition_list_item_t), 1);
|
||||
if (item == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
item->info.flash_chip = flash_chip;
|
||||
item->info.address = offset;
|
||||
item->info.size = size;
|
||||
item->info.type = type;
|
||||
item->info.subtype = subtype;
|
||||
item->info.encrypted = false;
|
||||
item->user_registered = true;
|
||||
strlcpy(item->info.label, label, sizeof(item->info.label));
|
||||
|
||||
_lock_acquire(&s_partition_list_lock);
|
||||
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 &&
|
||||
bootloader_util_regions_overlap(offset, offset + size,
|
||||
it->info.address, it->info.address + it->info.size)) {
|
||||
_lock_release(&s_partition_list_lock);
|
||||
free(item);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
last = it;
|
||||
}
|
||||
if (last == NULL) {
|
||||
SLIST_INSERT_HEAD(&s_partition_list, item, next);
|
||||
} else {
|
||||
SLIST_INSERT_AFTER(last, item, next);
|
||||
}
|
||||
_lock_release(&s_partition_list_lock);
|
||||
if (out_partition != NULL) {
|
||||
*out_partition = &item->info;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
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);
|
||||
partition_list_item_t *it;
|
||||
SLIST_FOREACH(it, &s_partition_list, next) {
|
||||
if (&it->info == partition) {
|
||||
if (!it->user_registered) {
|
||||
result = ESP_ERR_INVALID_ARG;
|
||||
break;
|
||||
}
|
||||
SLIST_REMOVE(&s_partition_list, it, partition_list_item_, next);
|
||||
free(it);
|
||||
result = ESP_OK;
|
||||
break;
|
||||
}
|
||||
}
|
||||
_lock_release(&s_partition_list_lock);
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,255 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#include <errno.h>
|
||||
#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 % partition->erase_size != 0) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (offset + size > partition->size || size % partition->erase_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;
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/lock.h>
|
||||
#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 "spi_flash_mmap.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) {
|
||||
return esp_flash_read(partition->flash_chip, dst, partition->address + src_offset, size);
|
||||
}
|
||||
|
||||
#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) {
|
||||
return esp_flash_write(partition->flash_chip, src, dst_offset, size);
|
||||
}
|
||||
|
||||
#if CONFIG_SPI_FLASH_ENABLE_ENCRYPTED_READ_WRITE
|
||||
if (partition->flash_chip != esp_flash_default_chip) {
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
return esp_flash_write_encrypted(partition->flash_chip, dst_offset, src, size);
|
||||
#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;
|
||||
}
|
||||
|
||||
return esp_flash_read(partition->flash_chip, dst, partition->address + src_offset, size);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
return esp_flash_write(partition->flash_chip, src, dst_offset, 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) {
|
||||
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;
|
||||
}
|
||||
|
||||
return esp_flash_erase_region(partition->flash_chip, partition->address + offset, size);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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,
|
||||
esp_partition_mmap_memory_t memory,
|
||||
const void **out_ptr, esp_partition_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 mmu page size block
|
||||
size_t region_offset = phys_addr & (CONFIG_MMU_PAGE_SIZE - 1);
|
||||
size_t mmap_addr = phys_addr & ~(CONFIG_MMU_PAGE_SIZE - 1);
|
||||
esp_err_t rc = spi_flash_mmap(mmap_addr, size + region_offset, (spi_flash_mmap_memory_t) memory, out_ptr, (spi_flash_mmap_handle_t*) 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;
|
||||
}
|
||||
|
||||
void esp_partition_munmap(esp_partition_mmap_handle_t handle)
|
||||
{
|
||||
spi_flash_munmap((spi_flash_mmap_handle_t) handle);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
idf_component_register(SRC_DIRS "."
|
||||
PRIV_INCLUDE_DIRS "."
|
||||
PRIV_REQUIRES test_utils esp_partition esp_system app_update bootloader_support spi_flash)
|
||||
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "unity.h"
|
||||
#include "test_utils.h"
|
||||
#include "esp_partition.h"
|
||||
|
||||
|
||||
TEST_CASE("Can read partition table", "[partition]")
|
||||
{
|
||||
|
||||
const esp_partition_t *p = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_ANY, NULL);
|
||||
TEST_ASSERT_NOT_NULL(p);
|
||||
TEST_ASSERT_EQUAL(0x20000, p->address);
|
||||
TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_FACTORY, p->subtype);
|
||||
|
||||
esp_partition_iterator_t it = esp_partition_find(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, NULL);
|
||||
TEST_ASSERT_NOT_NULL(it);
|
||||
int count = 0;
|
||||
const esp_partition_t* prev = NULL;
|
||||
for (; it != NULL; it = esp_partition_next(it)) {
|
||||
const esp_partition_t *p = esp_partition_get(it);
|
||||
TEST_ASSERT_NOT_NULL(p);
|
||||
if (prev) {
|
||||
TEST_ASSERT_TRUE_MESSAGE(prev->address < p->address, "incorrect partition order");
|
||||
}
|
||||
prev = p;
|
||||
++count;
|
||||
}
|
||||
esp_partition_iterator_release(it);
|
||||
TEST_ASSERT_EQUAL(5, count);
|
||||
|
||||
it = esp_partition_find(ESP_PARTITION_TYPE_ANY, ESP_PARTITION_SUBTYPE_ANY, NULL);
|
||||
TEST_ASSERT_NOT_NULL(it);
|
||||
count = 0;
|
||||
for (; it != NULL; it = esp_partition_next(it)) {
|
||||
++count;
|
||||
}
|
||||
esp_partition_iterator_release(it);
|
||||
TEST_ASSERT_EQUAL(8, count);
|
||||
}
|
||||
|
||||
TEST_CASE("Can write, read, mmap partition", "[partition][ignore]")
|
||||
{
|
||||
const esp_partition_t *p = get_test_data_partition();
|
||||
printf("Using partition %s at 0x%x, size 0x%x\n", p->label, p->address, p->size);
|
||||
TEST_ASSERT_NOT_NULL(p);
|
||||
const size_t max_size = 2 * p->erase_size;
|
||||
uint8_t *data = (uint8_t *) malloc(max_size);
|
||||
TEST_ASSERT_NOT_NULL(data);
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_partition_erase_range(p, 0, p->size));
|
||||
|
||||
srand(0);
|
||||
size_t block_size;
|
||||
for (size_t offset = 0; offset < p->size; offset += block_size) {
|
||||
block_size = ((rand() + 4) % max_size) & (~0x3);
|
||||
size_t left = p->size - offset;
|
||||
if (block_size > left) {
|
||||
block_size = left;
|
||||
}
|
||||
for (size_t i = 0; i < block_size / 4; ++i) {
|
||||
((uint32_t *) (data))[i] = rand();
|
||||
}
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_partition_write(p, offset, data, block_size));
|
||||
}
|
||||
|
||||
srand(0);
|
||||
for (size_t offset = 0; offset < p->size; offset += block_size) {
|
||||
block_size = ((rand() + 4) % max_size) & (~0x3);
|
||||
size_t left = p->size - offset;
|
||||
if (block_size > left) {
|
||||
block_size = left;
|
||||
}
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_partition_read(p, offset, data, block_size));
|
||||
for (size_t i = 0; i < block_size / 4; ++i) {
|
||||
TEST_ASSERT_EQUAL(rand(), ((uint32_t *) data)[i]);
|
||||
}
|
||||
}
|
||||
|
||||
free(data);
|
||||
|
||||
const uint32_t *mmap_data;
|
||||
esp_partition_mmap_handle_t mmap_handle;
|
||||
size_t begin = 3000;
|
||||
size_t size = 64000; //chosen so size is smaller than 64K but the mmap straddles 2 MMU blocks
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_partition_mmap(p, begin, size, ESP_PARTITION_MMAP_DATA,
|
||||
(const void **)&mmap_data, &mmap_handle));
|
||||
srand(0);
|
||||
for (size_t offset = 0; offset < p->size; offset += block_size) {
|
||||
block_size = ((rand() + 4) % max_size) & (~0x3);
|
||||
size_t left = p->size - offset;
|
||||
if (block_size > left) {
|
||||
block_size = left;
|
||||
}
|
||||
for (size_t i = 0; i < block_size / 4; ++i) {
|
||||
size_t pos = offset + i * 4;
|
||||
uint32_t expected = rand();
|
||||
if (pos < begin || pos >= (begin + size)) {
|
||||
continue;
|
||||
}
|
||||
TEST_ASSERT_EQUAL(expected, mmap_data[(pos - begin) / 4]);
|
||||
}
|
||||
}
|
||||
|
||||
esp_partition_munmap(mmap_handle);
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#include "esp_flash.h"
|
||||
#include "spi_flash_mmap.h"
|
||||
#include "esp_partition.h"
|
||||
#include "unity.h"
|
||||
|
||||
TEST_CASE("Basic handling of a partition in external flash", "[partition]")
|
||||
{
|
||||
esp_flash_t flash = {
|
||||
.size = 1 * 1024 * 1024,
|
||||
};
|
||||
|
||||
const esp_partition_type_t t = ESP_PARTITION_TYPE_DATA;
|
||||
const esp_partition_subtype_t st = ESP_PARTITION_SUBTYPE_DATA_FAT;
|
||||
const char* label = "ext_fat";
|
||||
|
||||
/* can register */
|
||||
const esp_partition_t* ext_partition;
|
||||
TEST_ESP_OK(esp_partition_register_external(&flash, 0, flash.size, label, t, st, &ext_partition));
|
||||
|
||||
/* can find the registered partition */
|
||||
const esp_partition_t* found = esp_partition_find_first(t, st, label);
|
||||
TEST_ASSERT_EQUAL_HEX(ext_partition, found);
|
||||
TEST_ASSERT_EQUAL(found->size, flash.size);
|
||||
TEST_ASSERT_EQUAL(found->address, 0);
|
||||
|
||||
/* can deregister */
|
||||
TEST_ESP_OK(esp_partition_deregister_external(ext_partition));
|
||||
|
||||
/* can not deregister one of the partitions on the main flash chip */
|
||||
const esp_partition_t* nvs_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, NULL);
|
||||
TEST_ASSERT_NOT_NULL(nvs_partition);
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_partition_deregister_external(nvs_partition));
|
||||
|
||||
/* can not deregister an unknown partition */
|
||||
esp_partition_t dummy_partition = {};
|
||||
TEST_ESP_ERR(ESP_ERR_NOT_FOUND, esp_partition_deregister_external(&dummy_partition));
|
||||
|
||||
/* can not register a partition larger than the flash size */
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_SIZE,
|
||||
esp_partition_register_external(&flash, 0, 2 * flash.size, "ext_fat", t, st, &ext_partition));
|
||||
|
||||
/* can not register an overlapping partition */
|
||||
TEST_ESP_OK(esp_partition_register_external(&flash, 0, 2 * SPI_FLASH_SEC_SIZE, "p1", t, st, &ext_partition));
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_partition_register_external(&flash, SPI_FLASH_SEC_SIZE, 2 * SPI_FLASH_SEC_SIZE,
|
||||
"p2", t, st, NULL));
|
||||
TEST_ESP_OK(esp_partition_deregister_external(ext_partition));
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2010-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
// Test for spi_flash_{read,write}.
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
#include <unity.h>
|
||||
#include <test_utils.h>
|
||||
#include <esp_image_format.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_partition.h>
|
||||
#include <esp_attr.h>
|
||||
#include "esp_flash.h"
|
||||
#include "spi_flash_mmap.h"
|
||||
|
||||
TEST_CASE("Test erase partition", "[spi_flash][esp_flash]")
|
||||
{
|
||||
const esp_partition_t *part = get_test_data_partition();
|
||||
|
||||
#if CONFIG_SPI_FLASH_ENABLE_COUNTERS
|
||||
spi_flash_reset_counters();
|
||||
#endif
|
||||
|
||||
// erase whole partition
|
||||
ESP_ERROR_CHECK( esp_partition_erase_range(part, 0, part->size) );
|
||||
|
||||
#if CONFIG_SPI_FLASH_ENABLE_COUNTERS
|
||||
spi_flash_dump_counters();
|
||||
#endif
|
||||
|
||||
// put some dummy data on sector boundaries
|
||||
const static DRAM_ATTR char some_data[] = "abcdefghijklmn";
|
||||
for (int i = 0; i < part->size; i+= 4096) {
|
||||
ESP_ERROR_CHECK( esp_partition_write(part, i, some_data, strlen(some_data)) );
|
||||
}
|
||||
|
||||
// check it's there!
|
||||
char buf[strlen(some_data)];
|
||||
for (int i = 0; i < part->size; i+= 4096) {
|
||||
memset(buf, 0x00, sizeof(buf));
|
||||
ESP_ERROR_CHECK( esp_partition_read(part, i, buf, sizeof(buf)) );
|
||||
TEST_ASSERT_EQUAL_INT(0, strncmp(buf, some_data, sizeof(buf)));
|
||||
}
|
||||
|
||||
// erase the whole thing again
|
||||
ESP_ERROR_CHECK( esp_partition_erase_range(part, 0, part->size) );
|
||||
|
||||
// check it's gone
|
||||
for (int i = 0; i < part->size; i+= 4096) {
|
||||
memset(buf, 0x00, sizeof(buf));
|
||||
ESP_ERROR_CHECK( esp_partition_read(part, i, buf, sizeof(buf)) );
|
||||
for (int i = 0; i < sizeof(buf); i++) {
|
||||
TEST_ASSERT_EQUAL_HEX8(0xFF, buf[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool s_test_nonzero_sha_of_partition(const esp_partition_t *part, bool allow_invalid_image)
|
||||
{
|
||||
uint8_t sha256[32] = { 0 };
|
||||
|
||||
TEST_ASSERT_NOT_NULL(part);
|
||||
|
||||
esp_err_t err = esp_partition_get_sha256(part, sha256);
|
||||
|
||||
if (allow_invalid_image && err == ESP_ERR_IMAGE_INVALID) {
|
||||
printf("App partition at 0x%x doesn't hold a valid app\n", part->address);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Otherwise, err should be ESP_OK
|
||||
ESP_ERROR_CHECK(err);
|
||||
|
||||
ESP_LOG_BUFFER_HEX("sha", sha256, sizeof(sha256));
|
||||
|
||||
for (int i = 0; i < sizeof(sha256); i++) {
|
||||
if (sha256[i] != 0) {
|
||||
return true; // At least one non-zero byte!
|
||||
}
|
||||
}
|
||||
TEST_FAIL_MESSAGE("SHA-256 of data partition should not be all zeroes");
|
||||
abort(); // unreachable
|
||||
}
|
||||
|
||||
TEST_CASE("Test esp_partition_get_sha256() with data", "[spi_flash]")
|
||||
{
|
||||
const esp_partition_t *part = get_test_data_partition();
|
||||
s_test_nonzero_sha_of_partition(part, false);
|
||||
}
|
||||
|
||||
TEST_CASE("Test esp_partition_get_sha256() with app", "[spi_flash]")
|
||||
{
|
||||
bool found_valid_app = false;
|
||||
esp_partition_iterator_t it = esp_partition_find(ESP_PARTITION_TYPE_APP,
|
||||
ESP_PARTITION_SUBTYPE_ANY,
|
||||
NULL);
|
||||
TEST_ASSERT_NOT_NULL(it); /* has to be at least one app partition */
|
||||
|
||||
while (it != NULL) {
|
||||
const esp_partition_t *part = esp_partition_get(it);
|
||||
printf("Hashing app partition at 0x%x\n", part->address);
|
||||
bool valid = s_test_nonzero_sha_of_partition(part, true);
|
||||
found_valid_app |= valid;
|
||||
|
||||
it = esp_partition_next(it);
|
||||
}
|
||||
|
||||
TEST_ASSERT_MESSAGE(found_valid_app, "At least one app partition should be a valid app partition");
|
||||
}
|
||||
|
||||
TEST_CASE("Test esp_partition_get_sha256() that it can handle a big partition", "[spi_flash]")
|
||||
{
|
||||
esp_partition_t partition;
|
||||
const void *ptr;
|
||||
spi_flash_mmap_handle_t handle;
|
||||
|
||||
uint8_t sha256[32] = { 0 };
|
||||
uint32_t size_flash_chip;
|
||||
esp_flash_get_size(NULL, &size_flash_chip);
|
||||
|
||||
printf("size_flash_chip = %d bytes\n", size_flash_chip);
|
||||
|
||||
ESP_ERROR_CHECK(spi_flash_mmap(0x00000000, size_flash_chip * 7 / 10, SPI_FLASH_MMAP_DATA, &ptr, &handle));
|
||||
TEST_ASSERT_NOT_NULL(ptr);
|
||||
|
||||
partition.address = 0x00000000;
|
||||
partition.size = size_flash_chip;
|
||||
partition.type = ESP_PARTITION_TYPE_DATA;
|
||||
|
||||
ESP_ERROR_CHECK(esp_partition_get_sha256(&partition, sha256));
|
||||
ESP_LOG_BUFFER_HEX("sha", sha256, sizeof(sha256));
|
||||
|
||||
spi_flash_munmap(handle);
|
||||
}
|
||||
Reference in New Issue
Block a user