]> granicus.if.org Git - esp-idf/commitdiff
spi_flash: support for partitions in external flash
authorIvan Grokhotkov <ivan@espressif.com>
Tue, 18 Jun 2019 17:31:43 +0000 (01:31 +0800)
committerIvan Grokhotkov <ivan@espressif.com>
Tue, 2 Jul 2019 08:26:06 +0000 (10:26 +0200)
components/spi_flash/esp_flash_api.c
components/spi_flash/include/esp_partition.h
components/spi_flash/partition.c
components/spi_flash/sim/flash_mock.cpp
components/spi_flash/test/test_partition_ext.c [new file with mode: 0644]

index f86b9c749ea5d6200da5150e5c6aac38f6873e0c..1ce52e47b2f769fa5e9c95ebd31d497608f73be5 100644 (file)
@@ -621,6 +621,8 @@ inline static IRAM_ATTR bool regions_overlap(uint32_t a_start, uint32_t a_len,ui
     Adapter layer to original api before IDF v4.0
 ------------------------------------------------------------------------------*/
 
+#ifndef CONFIG_SPI_FLASH_USE_LEGACY_IMPL
+
 static esp_err_t spi_flash_translate_rc(esp_err_t err)
 {
     switch (err) {
@@ -644,7 +646,6 @@ static esp_err_t spi_flash_translate_rc(esp_err_t err)
     return ESP_OK;
 }
 
-#ifndef CONFIG_SPI_FLASH_USE_LEGACY_IMPL
 esp_err_t spi_flash_erase_range(uint32_t start_addr, uint32_t size)
 {
     esp_err_t err = esp_flash_erase_region(NULL, start_addr, size);
@@ -655,8 +656,6 @@ esp_err_t spi_flash_write(size_t dst, const void *srcv, size_t size)
 {
     esp_err_t err = esp_flash_write(NULL, srcv, dst, size);
     return spi_flash_translate_rc(err);
-
-    //CHECK_WRITE_ADDRESS(dst, size);
 }
 
 esp_err_t spi_flash_read(size_t src, void *dstv, size_t size)
@@ -671,4 +670,4 @@ esp_err_t spi_flash_unlock()
     return spi_flash_translate_rc(err);
 }
 
-#endif
+#endif // CONFIG_SPI_FLASH_USE_LEGACY_IMPL
index 6537967eb7ef70a82e3bc74a10484274f9548a8d..d9ec1e3a52da533335ecac900e2dc4d4f7b9deb0 100644 (file)
 #include <stdbool.h>
 #include <stddef.h>
 #include "esp_err.h"
+#include "esp_flash.h"
 #include "esp_spi_flash.h"
 
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -98,6 +100,7 @@ typedef struct esp_partition_iterator_opaque_* esp_partition_iterator_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 */
@@ -320,6 +323,43 @@ esp_err_t esp_partition_get_sha256(const esp_partition_t *partition, uint8_t *sh
  */
 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
index 5da204ae5647534f77f15b60b334f744893bfb79..6876ee71ad687962ce32dc41ffc83100680f5a61 100644 (file)
 #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;
 
@@ -163,12 +167,18 @@ static esp_err_t load_partitions()
             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 */
@@ -193,7 +203,7 @@ static esp_err_t load_partitions()
         last = item;
     }
     spi_flash_munmap(handle);
-    return ESP_OK;
+    return err;
 }
 
 void esp_partition_iterator_release(esp_partition_iterator_t iterator)
@@ -208,6 +218,80 @@ const esp_partition_t* esp_partition_get(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);
@@ -218,7 +302,8 @@ const esp_partition_t *esp_partition_verify(const esp_partition_t *partition)
     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);
@@ -242,9 +327,17 @@ esp_err_t esp_partition_read(const esp_partition_t* partition,
     }
 
     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;
@@ -276,9 +369,16 @@ esp_err_t esp_partition_write(const esp_partition_t* partition,
     }
     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;
@@ -302,7 +402,11 @@ esp_err_t esp_partition_erase_range(const esp_partition_t* partition,
     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
 
 }
 
@@ -325,6 +429,9 @@ esp_err_t esp_partition_mmap(const esp_partition_t* partition, uint32_t offset,
     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;
index 9535bf29b16df6a8092904dd2e2479057b2ec3bb..1a275d5180d59ba36a73f7ca2817faf94d2a67a3 100644 (file)
@@ -109,3 +109,5 @@ void *heap_caps_malloc( size_t size, uint32_t caps )
 {
     return NULL;
 }
+
+esp_flash_t* esp_flash_default_chip = NULL;
diff --git a/components/spi_flash/test/test_partition_ext.c b/components/spi_flash/test/test_partition_ext.c
new file mode 100644 (file)
index 0000000..ead5508
--- /dev/null
@@ -0,0 +1,47 @@
+#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));
+}