#include <stdbool.h>
#include <stddef.h>
#include "esp_err.h"
+#include "esp_flash.h"
#include "esp_spi_flash.h"
+
#ifdef __cplusplus
extern "C" {
#endif
* 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 */
*/
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_*). 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_*)
+ * @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_NOT_SUPPORTED if CONFIG_CONFIG_SPI_FLASH_USE_LEGACY_IMPL is enabled
+ * - 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
#include <sys/lock.h>
#include "esp_flash_partitions.h"
#include "esp_attr.h"
+#include "esp_flash.h"
#include "esp_spi_flash.h"
#include "esp_partition.h"
#include "esp_flash_encrypt.h"
#include "esp_log.h"
#include "bootloader_common.h"
+#include "bootloader_util.h"
#include "esp_ota_ops.h"
#define HASH_LEN 32 /* SHA-256 digest length */
#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;
break;
}
// allocate new linked list item and populate it with data from partition table
- partition_list_item_t* item = (partition_list_item_t*) malloc(sizeof(partition_list_item_t));
+ 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 = it->pos.offset;
item->info.size = it->pos.size;
item->info.type = it->type;
item->info.subtype = it->subtype;
item->info.encrypted = it->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 */
last = item;
}
spi_flash_munmap(handle);
- return ESP_OK;
+ return err;
}
void esp_partition_iterator_release(esp_partition_iterator_t iterator)
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)
+{
+ if (out_partition != NULL) {
+ *out_partition = NULL;
+ }
+#ifdef CONFIG_SPI_FLASH_USE_LEGACY_IMPL
+ return ESP_ERR_NOT_SUPPORTED;
+#endif
+
+ if (offset + size > flash_chip->size) {
+ return ESP_ERR_INVALID_SIZE;
+ }
+
+ 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, *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;
+}
+
const esp_partition_t *esp_partition_verify(const esp_partition_t *partition)
{
assert(partition != NULL);
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->address == partition->address
+ if (p->flash_chip == partition->flash_chip
+ && p->address == partition->address
&& partition->size == p->size
&& partition->encrypted == p->encrypted) {
esp_partition_iterator_release(it);
}
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_SECURE_FLASH_ENC_ENABLED
+ 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;
}
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_SECURE_FLASH_ENC_ENABLED
+ if (partition->flash_chip != esp_flash_default_chip) {
+ return ESP_ERR_NOT_SUPPORTED;
+ }
return spi_flash_write_encrypted(dst_offset, src, size);
#else
return ESP_ERR_NOT_SUPPORTED;
if (start_addr % 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 + start_addr, size);
+#else
return spi_flash_erase_range(partition->address + start_addr, size);
+#endif // CONFIG_SPI_FLASH_USE_LEGACY_IMPL
}
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;
--- /dev/null
+#include "esp_flash.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));
+}