From: Ivan Grokhotkov Date: Wed, 19 Oct 2016 10:04:25 +0000 (+0800) Subject: spi_flash: implement partition API X-Git-Tag: v1.0~98^2~7 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=079d9ea018729c95a9aea0ac759f40849cb13f5e;p=esp-idf spi_flash: implement partition API --- diff --git a/components/spi_flash/include/esp_partition.h b/components/spi_flash/include/esp_partition.h index fd1223da51..6bdba149f1 100644 --- a/components/spi_flash/include/esp_partition.h +++ b/components/spi_flash/include/esp_partition.h @@ -15,62 +15,73 @@ #ifndef __ESP_PARTITION_H__ #define __ESP_PARTITION_H__ +#include +#include #include "esp_err.h" #ifdef __cplusplus extern "C" { #endif -enum esp_partition_type_t { - PT_APP_MASK = 0x0000, - PT_APP_FACTORY = PT_APP_MASK | 0x00, - PT_APP_OTA_MIN = PT_APP_MASK | 0x10, - PT_APP_OTA_0 = PT_APP_OTA_MIN + 0, - PT_APP_OTA_1 = PT_APP_OTA_MIN + 1, - PT_APP_OTA_2 = PT_APP_OTA_MIN + 2, - PT_APP_OTA_3 = PT_APP_OTA_MIN + 3, - PT_APP_OTA_4 = PT_APP_OTA_MIN + 4, - PT_APP_OTA_5 = PT_APP_OTA_MIN + 5, - PT_APP_OTA_6 = PT_APP_OTA_MIN + 6, - PT_APP_OTA_7 = PT_APP_OTA_MIN + 7, - PT_APP_OTA_8 = PT_APP_OTA_MIN + 8, - PT_APP_OTA_9 = PT_APP_OTA_MIN + 9, - PT_APP_OTA_10 = PT_APP_OTA_MIN + 10, - PT_APP_OTA_11 = PT_APP_OTA_MIN + 11, - PT_APP_OTA_12 = PT_APP_OTA_MIN + 12, - PT_APP_OTA_13 = PT_APP_OTA_MIN + 13, - PT_APP_OTA_14 = PT_APP_OTA_MIN + 14, - PT_APP_OTA_15 = PT_APP_OTA_MIN + 15, - PT_APP_OTA_MAX = PT_APP_MASK | 0x1f, - PT_APP_TEST = PT_APP_MASK | 0x20, - PT_APP_ANY = PT_APP_MASK | 0xff, - - PT_DATA_MASK = 0x0100, - PT_DATA_OTA = PT_DATA_MASK | 0x00, - PT_DATA_RF = PT_DATA_MASK | 0x01, - PT_DATA_WIFI = PT_DATA_MASK | 0x02, - PT_DATA_ANY = PT_DATA_MASK | 0xff, - - PT_FILESYSTEM_MASK = 0x0200, - PT_FILESYSTEM_ESPHTTPD = 0x0200, - PT_FILESYSTEM_FAT = 0x0201, - PT_FILESYSTEM_SPIFFS = 0x0202, - PT_FILESYSTEM_ANY = 0x20ff, - - PT_END = 0xffff -}; - -#define PT_APP_OTA(i) ((esp_partition_type_t)(PT_APP_OTA_MIN + ((i) & 0xf))) - - -typedef struct esp_partition_iterator_opaque_t* esp_partition_iterator_t; +typedef enum { + ESP_PARTITION_APP_MASK = 0x0000, + ESP_PARTITION_APP_FACTORY = ESP_PARTITION_APP_MASK | 0x00, + ESP_PARTITION_APP_OTA_MIN = ESP_PARTITION_APP_MASK | 0x10, + ESP_PARTITION_APP_OTA_0 = ESP_PARTITION_APP_OTA_MIN + 0, + ESP_PARTITION_APP_OTA_1 = ESP_PARTITION_APP_OTA_MIN + 1, + ESP_PARTITION_APP_OTA_2 = ESP_PARTITION_APP_OTA_MIN + 2, + ESP_PARTITION_APP_OTA_3 = ESP_PARTITION_APP_OTA_MIN + 3, + ESP_PARTITION_APP_OTA_4 = ESP_PARTITION_APP_OTA_MIN + 4, + ESP_PARTITION_APP_OTA_5 = ESP_PARTITION_APP_OTA_MIN + 5, + ESP_PARTITION_APP_OTA_6 = ESP_PARTITION_APP_OTA_MIN + 6, + ESP_PARTITION_APP_OTA_7 = ESP_PARTITION_APP_OTA_MIN + 7, + ESP_PARTITION_APP_OTA_8 = ESP_PARTITION_APP_OTA_MIN + 8, + ESP_PARTITION_APP_OTA_9 = ESP_PARTITION_APP_OTA_MIN + 9, + ESP_PARTITION_APP_OTA_10 = ESP_PARTITION_APP_OTA_MIN + 10, + ESP_PARTITION_APP_OTA_11 = ESP_PARTITION_APP_OTA_MIN + 11, + ESP_PARTITION_APP_OTA_12 = ESP_PARTITION_APP_OTA_MIN + 12, + ESP_PARTITION_APP_OTA_13 = ESP_PARTITION_APP_OTA_MIN + 13, + ESP_PARTITION_APP_OTA_14 = ESP_PARTITION_APP_OTA_MIN + 14, + ESP_PARTITION_APP_OTA_15 = ESP_PARTITION_APP_OTA_MIN + 15, + ESP_PARTITION_APP_OTA_MAX = ESP_PARTITION_APP_MASK | 0x1f, + ESP_PARTITION_APP_TEST = ESP_PARTITION_APP_MASK | 0x20, + ESP_PARTITION_APP_ANY = ESP_PARTITION_APP_MASK | 0xff, + + ESP_PARTITION_DATA_MASK = 0x0100, + ESP_PARTITION_DATA_OTA = ESP_PARTITION_DATA_MASK | 0x00, + ESP_PARTITION_DATA_RF = ESP_PARTITION_DATA_MASK | 0x01, + ESP_PARTITION_DATA_WIFI = ESP_PARTITION_DATA_MASK | 0x02, + ESP_PARTITION_DATA_ANY = ESP_PARTITION_DATA_MASK | 0xff, + + ESP_PARTITION_FILESYSTEM_MASK = 0x0200, + ESP_PARTITION_FILESYSTEM_ESPHTTPD = 0x0200, + ESP_PARTITION_FILESYSTEM_FAT = 0x0201, + ESP_PARTITION_FILESYSTEM_SPIFFS = 0x0202, + ESP_PARTITION_FILESYSTEM_ANY = 0x20ff, + + ESP_PARTITION_END = 0xffff +} esp_partition_type_t; + +#define ESP_PARTITION_APP_OTA(i) ((esp_partition_type_t)(ESP_PARTITION_APP_OTA_MIN + ((i) & 0xf))) + + +typedef struct esp_partition_iterator_opaque_* esp_partition_iterator_t; + +typedef struct { + esp_partition_type_t type; + uint32_t address; + uint32_t size; + char label[17]; + bool encrypted; +} esp_partition_t; /** * @brief Find partition based on one or more parameters * * @param type Partition type, one of esp_partition_type_t values * To find all app partitions or all filesystem partitions, - * use PT_APP_ANY or PT_FILESYSTEM_ANY, respectively. + * use ESP_PARTITION_APP_ANY or ESP_PARTITION_FILESYSTEM_ANY, + * respectively. * @param label (optional) Partition label. Set this value if looking * for partition with a specific name. Pass NULL otherwise. * @@ -81,24 +92,47 @@ typedef struct esp_partition_iterator_opaque_t* esp_partition_iterator_t; */ esp_partition_iterator_t esp_partition_find(esp_partition_type_t type, const char* label); +/** + * @brief Find first partition based on one or more parameters + * + * @param type Partition type, one of esp_partition_type_t values + * To find all app partitions or all filesystem partitions, + * use ESP_PARTITION_APP_ANY or ESP_PARTITION_FILESYSTEM_ANY, + * respectively. + * @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 parition 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, 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 pointers obtained using esp_partition_label function for this iterator - * will be invalid after this call. + * Any copies of the iterator will be invalid after this call. * * @param iterator Iterator obtained using esp_partition_find. Must be non-NULL. * - * @return iterator pointing to the next partition found, or NULL if no more - * partitions were found. - * + * @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 Get partition type * + * @note This is a helper function built around esp_partition_get. + * * @param iterator Iterator obtained using esp_partition_find. Must be non-NULL. * * @return esp_partition_type_t value for partition pointed to by the iterator. @@ -108,6 +142,8 @@ esp_partition_type_t esp_partition_type(esp_partition_iterator_t iterator); /** * @brief Get partition size * + * @note This is a helper function built around esp_partition_get. + * * @param iterator Iterator obtained using esp_partition_find. Must be non-NULL. * * @return partition size, in bytes @@ -117,6 +153,8 @@ uint32_t esp_partition_size(esp_partition_iterator_t iterator); /** * @brief Get partition address * + * @note This is a helper function built around esp_partition_get. + * * @param iterator Iterator obtained using esp_partition_find. Must be non-NULL. * * @return flash address of partition start @@ -126,11 +164,12 @@ uint32_t esp_partition_address(esp_partition_iterator_t iterator); /** * @brief Get partition label * + * @note This is a helper function built around esp_partition_get. + * * @param iterator Iterator obtained using esp_partition_find. Must be non-NULL. * * @return pointer to a zero-terminated string with partition label. - * The pointer is valid until the call to esp_partition_next or - * esp_partition_iterator_release for the given iterator. + * The pointer is valid for the lifetime of the application. */ const char* esp_partition_label(esp_partition_iterator_t iterator); @@ -145,8 +184,8 @@ const char* esp_partition_label(esp_partition_iterator_t iterator); * @param size Size of data to be read, in bytes. * * @return ESP_OK, if data was read successfully; - * ESP_INVALID_ARG, if iterator or src are NULL; - * ESP_INVALID_SIZE, if read would go out of bounds of the partition; + * ESP_ERR_INVALID_ARG, if iterator or src are NULL; + * 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(esp_partition_iterator_t iterator, @@ -166,8 +205,8 @@ esp_err_t esp_partition_read(esp_partition_iterator_t iterator, * esp_partition_erase_range call. * * @return ESP_OK, if data was written successfully; - * ESP_INVALID_ARG, if iterator or dst are NULL; - * ESP_INVALID_SIZE, if write would go out of bounds of the partition; + * ESP_ERR_INVALID_ARG, if iterator or dst are NULL; + * 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(esp_partition_iterator_t iterator, @@ -183,13 +222,28 @@ esp_err_t esp_partition_write(esp_partition_iterator_t iterator, * Must be divisible by 4 kilobytes. * * @return ESP_OK, if the range was erased successfully; - * ESP_INVALID_ARG, if iterator or dst are NULL; - * ESP_INVALID_SIZE, if erase would go out of bounds of the partition; + * 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(esp_partition_iterator_t iterator, uint32_t start_addr, uint32_t size); +/** + * @brief Configure MMU to map partition into data memory + * + * @param iterator Iterator obtained using esp_partition_find. Must be non-NULL. + * + * @param offset Offset from the beginning of partition where mapping should start. + * Must be aligned to 64k. + * + * @param size Size of the area to be mapped. + * + * @return pointer to mapped memory, if successful + * NULL, if memory can not be mapped for any reason + */ +void* esp_partition_mmap(esp_partition_iterator_t iterator, uint32_t offset, uint32_t size); + /** * @brief Release partition iterator diff --git a/components/spi_flash/partition.c b/components/spi_flash/partition.c new file mode 100644 index 0000000000..381a1624b5 --- /dev/null +++ b/components/spi_flash/partition.c @@ -0,0 +1,221 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include + +#include "esp_attr.h" +#include "esp_flash_data_types.h" +#include "esp_spi_flash.h" +#include "esp_partition.h" +#include "esp_log.h" + + +#ifndef NDEBUG +// Enable built-in checks in queue.h in debug builds +#define INVARIANTS +#endif +#include "rom/queue.h" + + +typedef struct partition_list_item_ { + esp_partition_t info; + SLIST_ENTRY(partition_list_item_) next; +} partition_list_item_t; + +typedef struct esp_partition_iterator_opaque_ { + esp_partition_type_t type; // requested type + const char* label; // requested label (can be NULL) + partition_list_item_t* next_item; // next item to iterate to + esp_partition_t* info; // pointer to info (it is redundant, but makes code more readable) +} esp_partition_iterator_opaque_t; + + +static esp_partition_iterator_opaque_t* iterator_create(esp_partition_type_t type, const char* label); +static esp_err_t load_partitions(); + + +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 uint32_t get_major_type(esp_partition_type_t type) +{ + return (type >> 8) & 0xff; +} + +static uint32_t get_minor_type(esp_partition_type_t type) +{ + return type & 0xff; +} + +esp_partition_iterator_t esp_partition_find(esp_partition_type_t type, + const char* label) +{ + if (SLIST_EMPTY(&s_partition_list)) { + // only lock if list is empty (and check again after acquiring lock) + _lock_acquire(&s_partition_list_lock); + esp_err_t err = ESP_OK; + if (SLIST_EMPTY(&s_partition_list)) { + err = load_partitions(); + } + _lock_release(&s_partition_list_lock); + if (err != ESP_OK) { + 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, label); + // 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) { + return NULL; + } + uint32_t requested_major_type = get_major_type(it->type); + uint32_t requested_minor_type = get_minor_type(it->type); + _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; + uint32_t it_major_type = get_major_type(p->type); + uint32_t it_minor_type = get_minor_type(p->type); + if (requested_major_type != it_major_type) { + continue; + } + if (requested_minor_type != 0xff && requested_minor_type != it_minor_type) { + 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, const char* label) +{ + esp_partition_iterator_t it = esp_partition_find(type, 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; +} + +esp_partition_type_t esp_partition_type(esp_partition_iterator_t iterator) +{ + return esp_partition_get(iterator)->type; +} + +uint32_t esp_partition_size(esp_partition_iterator_t iterator) +{ + return esp_partition_get(iterator)->size; +} + +uint32_t esp_partition_address(esp_partition_iterator_t iterator) +{ + return esp_partition_get(iterator)->address; +} + +const char* esp_partition_label(esp_partition_iterator_t iterator) +{ + return esp_partition_get(iterator)->label; +} + +static esp_partition_iterator_opaque_t* iterator_create(esp_partition_type_t type, const char* label) +{ + esp_partition_iterator_opaque_t* it = + (esp_partition_iterator_opaque_t*) malloc(sizeof(esp_partition_iterator_opaque_t)); + it->type = type; + it->label = label; + it->next_item = SLIST_FIRST(&s_partition_list); + it->info = NULL; + return it; +} + +// Create linked list of partition_list_item_t structures. +// This function is called only once, with s_partition_list_lock taken. +static esp_err_t load_partitions() +{ + const uint32_t* ptr; + spi_flash_mmap_handle_t handle; + // map 64kB block where partition table is located + esp_err_t err = spi_flash_mmap(ESP_PARTITION_TABLE_ADDR & 0xffff0000, + SPI_FLASH_SEC_SIZE, SPI_FLASH_MMAP_DATA, (const void**) &ptr, &handle); + if (err != ESP_OK) { + return err; + } + // calculate partition address within mmap-ed region + const esp_partition_info_t* it = (const esp_partition_info_t*) + (ptr + (ESP_PARTITION_TABLE_ADDR & 0xffff) / sizeof(*ptr)); + const esp_partition_info_t* end = it + SPI_FLASH_SEC_SIZE / sizeof(*it); + // tail of the linked list of partitions + partition_list_item_t* last = NULL; + for (; it != end; ++it) { + if (it->magic != ESP_PARTITION_MAGIC) { + 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)); + item->info.address = it->pos.offset; + item->info.size = it->pos.size; + item->info.type = (it->type << 8) | it->subtype; + item->info.encrypted = false; + // it->label may not be zero-terminated + strncpy(item->info.label, (const char*) it->label, sizeof(it->label)); + item->info.label[sizeof(it->label)] = 0; + // add it to the list + if (last == NULL) { + SLIST_INSERT_HEAD(&s_partition_list, item, next); + } else { + SLIST_INSERT_AFTER(last, item, next); + } + } + spi_flash_munmap(handle); + return ESP_OK; +}