From f6f23141b35dd3fbcf7fe4522766e732ea92df61 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Thu, 8 Sep 2016 19:18:03 +0800 Subject: [PATCH 01/13] components/spi_flash: add high level partition api header TW6701 --- components/spi_flash/include/esp_partition.h | 211 +++++++++++++++++++ 1 file changed, 211 insertions(+) create mode 100644 components/spi_flash/include/esp_partition.h diff --git a/components/spi_flash/include/esp_partition.h b/components/spi_flash/include/esp_partition.h new file mode 100644 index 000000000..fd1223da5 --- /dev/null +++ b/components/spi_flash/include/esp_partition.h @@ -0,0 +1,211 @@ +// 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. + +#ifndef __ESP_PARTITION_H__ +#define __ESP_PARTITION_H__ + +#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; + +/** + * @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. + * @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, const char* label); + + +/** + * @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. + * + * @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. + * + */ +esp_partition_iterator_t esp_partition_next(esp_partition_iterator_t iterator); + +/** + * @brief Get partition type + * + * @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. + */ +esp_partition_type_t esp_partition_type(esp_partition_iterator_t iterator); + +/** + * @brief Get partition size + * + * @param iterator Iterator obtained using esp_partition_find. Must be non-NULL. + * + * @return partition size, in bytes + */ +uint32_t esp_partition_size(esp_partition_iterator_t iterator); + +/** + * @brief Get partition address + * + * @param iterator Iterator obtained using esp_partition_find. Must be non-NULL. + * + * @return flash address of partition start + */ +uint32_t esp_partition_address(esp_partition_iterator_t iterator); + +/** + * @brief Get partition label + * + * @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. + */ +const char* esp_partition_label(esp_partition_iterator_t iterator); + +/** + * @brief Read data from the partition + * + * @param iterator Iterator obtained using esp_partition_find. Must be non-NULL. + * @param src_offset Address of the data to be read, relative to the + * beginning of the partition. + * @param dst Pointer to the buffer where data should be stored. + * Must be non-NULL and at least 'size' bytes long. + * @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; + * or one of error codes from lower-level flash driver. + */ +esp_err_t esp_partition_read(esp_partition_iterator_t iterator, + uint32_t src_offset, uint8_t* dst, uint32_t size); + +/** + * @brief Write data to the partition + * + * @param iterator Iterator obtained using esp_partition_find. Must be non-NULL. + * @param src Pointer to the source buffer. Must be non-NULL and + * at least 'size' bytes long. + * @param dst_offset Address where the data should be written, relative to the + * beginning of the partition. + * @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_INVALID_ARG, if iterator or dst are NULL; + * ESP_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, + const uint8_t* src, uint32_t dst_offset, uint32_t size); + +/** + * @brief Erase part of the partition + * + * @param iterator Iterator obtained using esp_partition_find. Must be non-NULL. + * @param start_addr Address where erase operation should start. Must be aligned + * to 4 kilobytes. + * @param size Size of the range which should be erased, in bytes. + * 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; + * 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 Release partition iterator + * + * Any pointers obtained using esp_partition_label function will be invalid + * after this call. + * + * @param iterator Iterator obtained using esp_partition_find. Must be non-NULL. + * + */ +void esp_partition_iterator_release(esp_partition_iterator_t iterator); + + +#ifdef __cplusplus +} +#endif + + +#endif /* __ESP_PARTITION_H__ */ From 628bde2080a9b99023e3214a8d55aae5962d9eeb Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 19 Oct 2016 17:05:37 +0800 Subject: [PATCH 02/13] bootloader: move useful structures to esp32 component --- .../bootloader/src/main/bootloader_config.h | 80 +---------- .../bootloader/src/main/bootloader_start.c | 124 ++++++++---------- .../bootloader/src/main/flash_encrypt.c | 2 +- .../esp32/include/esp_flash_data_types.h | 102 ++++++++++++++ 4 files changed, 162 insertions(+), 146 deletions(-) create mode 100644 components/esp32/include/esp_flash_data_types.h diff --git a/components/bootloader/src/main/bootloader_config.h b/components/bootloader/src/main/bootloader_config.h index f99a1c94e..8a837693c 100644 --- a/components/bootloader/src/main/bootloader_config.h +++ b/components/bootloader/src/main/bootloader_config.h @@ -20,12 +20,13 @@ extern "C" { #endif + +#include "esp_flash_data_types.h" + #define BOOT_VERSION "V0.1" #define SPI_SEC_SIZE 0x1000 #define MEM_CACHE(offset) (uint8_t *)(0x3f400000 + (offset)) #define CACHE_READ_32(offset) ((uint32_t *)(0x3f400000 + (offset))) -#define PARTITION_ADD 0x4000 -#define PARTITION_MAGIC 0x50AA #define IROM_LOW 0x400D0000 #define IROM_HIGH 0x40400000 #define DROM_LOW 0x3F400000 @@ -35,73 +36,6 @@ extern "C" #define RTC_DATA_LOW 0x50000000 #define RTC_DATA_HIGH 0x50002000 -/*spi mode,saved in third byte in flash */ -enum { - SPI_MODE_QIO, - SPI_MODE_QOUT, - SPI_MODE_DIO, - SPI_MODE_DOUT, - SPI_MODE_FAST_READ, - SPI_MODE_SLOW_READ -}; -/* spi speed*/ -enum { - SPI_SPEED_40M, - SPI_SPEED_26M, - SPI_SPEED_20M, - SPI_SPEED_80M = 0xF -}; -/*supported flash sizes*/ -enum { - SPI_SIZE_1MB = 0, - SPI_SIZE_2MB, - SPI_SIZE_4MB, - SPI_SIZE_8MB, - SPI_SIZE_16MB, - SPI_SIZE_MAX -}; - - -struct flash_hdr { - char magic; - char blocks; - char spi_mode; /* flag of flash read mode in unpackage and usage in future */ - char spi_speed: 4; /* low bit */ - char spi_size: 4; - unsigned int entry_addr; - uint8_t encrypt_flag; /* encrypt flag */ - uint8_t secury_boot_flag; /* secury boot flag */ - char extra_header[14]; /* ESP32 additional header, unused by second bootloader */ -}; - -/* each header of flash bin block */ -struct block_hdr { - unsigned int load_addr; - unsigned int data_len; -}; - -/* OTA selection structure (two copies in the OTA data partition.) - - Size of 32 bytes is friendly to flash encryption */ -typedef struct { - uint32_t ota_seq; - uint8_t seq_label[24]; - uint32_t crc; /* CRC32 of ota_seq field only */ -} ota_select; - -typedef struct { - uint32_t offset; - uint32_t size; -} partition_pos_t; - -typedef struct { - uint16_t magic; - uint8_t type; /* partition Type */ - uint8_t subtype; /* part_subtype */ - partition_pos_t pos; - uint8_t label[16]; /* label for the partition */ - uint8_t reserved[4]; /* reserved */ -} partition_info_t; #define PART_TYPE_APP 0x00 #define PART_SUBTYPE_FACTORY 0x00 @@ -120,10 +54,10 @@ typedef struct { #define SPI_ERROR_LOG "spi flash error" typedef struct { - partition_pos_t ota_info; - partition_pos_t factory; - partition_pos_t test; - partition_pos_t ota[16]; + esp_partition_pos_t ota_info; + esp_partition_pos_t factory; + esp_partition_pos_t test; + esp_partition_pos_t ota[16]; uint32_t app_count; uint32_t selected_subtype; } bootloader_state_t; diff --git a/components/bootloader/src/main/bootloader_start.c b/components/bootloader/src/main/bootloader_start.c index e87f579f4..5b1e15207 100644 --- a/components/bootloader/src/main/bootloader_start.c +++ b/components/bootloader/src/main/bootloader_start.c @@ -3,7 +3,7 @@ // 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 @@ -49,8 +49,8 @@ flash cache is down and the app CPU is in reset. We do have a stack, so we can d extern void Cache_Flush(int); void bootloader_main(); -void unpack_load_app(const partition_pos_t *app_node); -void print_flash_info(struct flash_hdr* pfhdr); +void unpack_load_app(const esp_partition_pos_t *app_node); +void print_flash_info(const esp_image_header_t* pfhdr); void IRAM_ATTR set_cache_and_start_app(uint32_t drom_addr, uint32_t drom_load_addr, uint32_t drom_size, @@ -58,7 +58,7 @@ void IRAM_ATTR set_cache_and_start_app(uint32_t drom_addr, uint32_t irom_load_addr, uint32_t irom_size, uint32_t entry_addr); -static void update_flash_config(struct flash_hdr* pfhdr); +static void update_flash_config(const esp_image_header_t* pfhdr); void IRAM_ATTR call_start_cpu0() @@ -154,7 +154,7 @@ void boot_cache_redirect( uint32_t pos, size_t size ) */ bool load_partition_table(bootloader_state_t* bs, uint32_t addr) { - partition_info_t partition; + esp_partition_info_t partition; uint32_t end = addr + 0x1000; int index = 0; char *partition_usage; @@ -168,7 +168,7 @@ bool load_partition_table(bootloader_state_t* bs, uint32_t addr) ESP_LOGD(TAG, "type=%x subtype=%x", partition.type, partition.subtype); partition_usage = "unknown"; - if (partition.magic == PARTITION_MAGIC) { /* valid partition definition */ + if (partition.magic == ESP_PARTITION_MAGIC) { /* valid partition definition */ switch(partition.type) { case PART_TYPE_APP: /* app partition */ switch(partition.subtype) { @@ -231,12 +231,12 @@ bool load_partition_table(bootloader_state_t* bs, uint32_t addr) return true; } -static uint32_t ota_select_crc(const ota_select *s) +static uint32_t ota_select_crc(const esp_ota_select_entry_t *s) { return crc32_le(UINT32_MAX, (uint8_t*)&s->ota_seq, 4); } -static bool ota_select_valid(const ota_select *s) +static bool ota_select_valid(const esp_ota_select_entry_t *s) { return s->ota_seq != UINT32_MAX && s->crc == ota_select_crc(s); } @@ -252,10 +252,10 @@ void bootloader_main() { ESP_LOGI(TAG, "Espressif ESP32 2nd stage bootloader v. %s", BOOT_VERSION); - struct flash_hdr fhdr; + esp_image_header_t fhdr; bootloader_state_t bs; SpiFlashOpResult spiRet1,spiRet2; - ota_select sa,sb; + esp_ota_select_entry_t sa,sb; memset(&bs, 0, sizeof(bs)); ESP_LOGI(TAG, "compile time " __TIME__ ); @@ -266,18 +266,18 @@ void bootloader_main() /*register first sector in drom0 page 0 */ boot_cache_redirect( 0, 0x5000 ); - memcpy((unsigned int *) &fhdr, MEM_CACHE(0x1000), sizeof(struct flash_hdr) ); + memcpy((unsigned int *) &fhdr, MEM_CACHE(0x1000), sizeof(esp_image_header_t) ); print_flash_info(&fhdr); update_flash_config(&fhdr); - if (!load_partition_table(&bs, PARTITION_ADD)) { + if (!load_partition_table(&bs, ESP_PARTITION_TABLE_ADDR)) { ESP_LOGE(TAG, "load partition table error!"); return; } - partition_pos_t load_part_pos; + esp_partition_pos_t load_part_pos; if (bs.ota_info.offset != 0) { // check if partition table has OTA info partition //ESP_LOGE("OTA info sector handling is not implemented"); @@ -293,14 +293,14 @@ void bootloader_main() sb.crc = ota_select_crc(&sb); Cache_Read_Disable(0); - spiRet1 = SPIEraseSector(bs.ota_info.offset/0x1000); - spiRet2 = SPIEraseSector(bs.ota_info.offset/0x1000+1); + spiRet1 = SPIEraseSector(bs.ota_info.offset/0x1000); + spiRet2 = SPIEraseSector(bs.ota_info.offset/0x1000+1); if (spiRet1 != SPI_FLASH_RESULT_OK || spiRet2 != SPI_FLASH_RESULT_OK ) { ESP_LOGE(TAG, SPI_ERROR_LOG); return; } - spiRet1 = SPIWrite(bs.ota_info.offset,(uint32_t *)&sa,sizeof(ota_select)); - spiRet2 = SPIWrite(bs.ota_info.offset + 0x1000,(uint32_t *)&sb,sizeof(ota_select)); + spiRet1 = SPIWrite(bs.ota_info.offset,(uint32_t *)&sa,sizeof(esp_ota_select_entry_t)); + spiRet2 = SPIWrite(bs.ota_info.offset + 0x1000,(uint32_t *)&sb,sizeof(esp_ota_select_entry_t)); if (spiRet1 != SPI_FLASH_RESULT_OK || spiRet2 != SPI_FLASH_RESULT_OK ) { ESP_LOGE(TAG, SPI_ERROR_LOG); return; @@ -329,7 +329,7 @@ void bootloader_main() } ESP_LOGI(TAG, "Loading app partition at offset %08x", load_part_pos); - if(fhdr.secury_boot_flag == 0x01) { + if(fhdr.secure_boot_flag == 0x01) { /* protect the 2nd_boot */ if(false == secure_boot()){ ESP_LOGE(TAG, "secure boot failed"); @@ -350,12 +350,12 @@ void bootloader_main() } -void unpack_load_app(const partition_pos_t* partition) +void unpack_load_app(const esp_partition_pos_t* partition) { boot_cache_redirect(partition->offset, partition->size); uint32_t pos = 0; - struct flash_hdr image_header; + esp_image_header_t image_header; memcpy(&image_header, MEM_CACHE(pos), sizeof(image_header)); pos += sizeof(image_header); @@ -379,7 +379,7 @@ void unpack_load_app(const partition_pos_t* partition) for (uint32_t section_index = 0; section_index < image_header.blocks; ++section_index) { - struct block_hdr section_header = {0}; + esp_image_section_header_t section_header = {0}; memcpy(§ion_header, MEM_CACHE(pos), sizeof(section_header)); pos += sizeof(section_header); @@ -485,23 +485,23 @@ void IRAM_ATTR set_cache_and_start_app( (*entry)(); } -static void update_flash_config(struct flash_hdr* pfhdr) +static void update_flash_config(const esp_image_header_t* pfhdr) { uint32_t size; switch(pfhdr->spi_size) { - case SPI_SIZE_1MB: + case ESP_IMAGE_FLASH_SIZE_1MB: size = 1; break; - case SPI_SIZE_2MB: + case ESP_IMAGE_FLASH_SIZE_2MB: size = 2; break; - case SPI_SIZE_4MB: + case ESP_IMAGE_FLASH_SIZE_4MB: size = 4; break; - case SPI_SIZE_8MB: + case ESP_IMAGE_FLASH_SIZE_8MB: size = 8; break; - case SPI_SIZE_16MB: + case ESP_IMAGE_FLASH_SIZE_16MB: size = 16; break; default: @@ -516,66 +516,53 @@ static void update_flash_config(struct flash_hdr* pfhdr) Cache_Read_Enable( 0 ); } -void print_flash_info(struct flash_hdr* pfhdr) +void print_flash_info(const esp_image_header_t* phdr) { #if (BOOT_LOG_LEVEL >= BOOT_LOG_LEVEL_NOTICE) - struct flash_hdr fhdr = *pfhdr; - - ESP_LOGD(TAG, "magic %02x", fhdr.magic ); - ESP_LOGD(TAG, "blocks %02x", fhdr.blocks ); - ESP_LOGD(TAG, "spi_mode %02x", fhdr.spi_mode ); - ESP_LOGD(TAG, "spi_speed %02x", fhdr.spi_speed ); - ESP_LOGD(TAG, "spi_size %02x", fhdr.spi_size ); + ESP_LOGD(TAG, "magic %02x", phdr->magic ); + ESP_LOGD(TAG, "blocks %02x", phdr->blocks ); + ESP_LOGD(TAG, "spi_mode %02x", phdr->spi_mode ); + ESP_LOGD(TAG, "spi_speed %02x", phdr->spi_speed ); + ESP_LOGD(TAG, "spi_size %02x", phdr->spi_size ); const char* str; - switch ( fhdr.spi_speed ) { - case SPI_SPEED_40M: + switch ( phdr->spi_speed ) { + case ESP_IMAGE_SPI_SPEED_40M: str = "40MHz"; break; - - case SPI_SPEED_26M: + case ESP_IMAGE_SPI_SPEED_26M: str = "26.7MHz"; break; - - case SPI_SPEED_20M: + case ESP_IMAGE_SPI_SPEED_20M: str = "20MHz"; break; - - case SPI_SPEED_80M: + case ESP_IMAGE_SPI_SPEED_80M: str = "80MHz"; break; - default: str = "20MHz"; break; } ESP_LOGI(TAG, "SPI Speed : %s", str ); - - - switch ( fhdr.spi_mode ) { - case SPI_MODE_QIO: + switch ( phdr->spi_mode ) { + case ESP_IMAGE_SPI_MODE_QIO: str = "QIO"; break; - - case SPI_MODE_QOUT: + case ESP_IMAGE_SPI_MODE_QOUT: str = "QOUT"; break; - - case SPI_MODE_DIO: + case ESP_IMAGE_SPI_MODE_DIO: str = "DIO"; break; - - case SPI_MODE_DOUT: + case ESP_IMAGE_SPI_MODE_DOUT: str = "DOUT"; break; - - case SPI_MODE_FAST_READ: + case ESP_IMAGE_SPI_MODE_FAST_READ: str = "FAST READ"; break; - - case SPI_MODE_SLOW_READ: + case ESP_IMAGE_SPI_MODE_SLOW_READ: str = "SLOW READ"; break; default: @@ -584,31 +571,24 @@ void print_flash_info(struct flash_hdr* pfhdr) } ESP_LOGI(TAG, "SPI Mode : %s", str ); - - - switch ( fhdr.spi_size ) { - case SPI_SIZE_1MB: + switch ( phdr->spi_size ) { + case ESP_IMAGE_FLASH_SIZE_1MB: str = "1MB"; break; - - case SPI_SIZE_2MB: + case ESP_IMAGE_FLASH_SIZE_2MB: str = "2MB"; break; - - case SPI_SIZE_4MB: + case ESP_IMAGE_FLASH_SIZE_4MB: str = "4MB"; break; - - case SPI_SIZE_8MB: + case ESP_IMAGE_FLASH_SIZE_8MB: str = "8MB"; break; - - case SPI_SIZE_16MB: + case ESP_IMAGE_FLASH_SIZE_16MB: str = "16MB"; break; - default: - str = "1MB"; + str = "2MB"; break; } ESP_LOGI(TAG, "SPI Flash Size : %s", str ); diff --git a/components/bootloader/src/main/flash_encrypt.c b/components/bootloader/src/main/flash_encrypt.c index 26e66aa03..2fb57a987 100644 --- a/components/bootloader/src/main/flash_encrypt.c +++ b/components/bootloader/src/main/flash_encrypt.c @@ -128,7 +128,7 @@ bool flash_encrypt(bootloader_state_t *bs) return false; } /* encrypt partition table */ - if (false == flash_encrypt_write(PARTITION_ADD, SPI_SEC_SIZE)) { + if (false == flash_encrypt_write(ESP_PARTITION_TABLE_ADDR, SPI_SEC_SIZE)) { ESP_LOGE(TAG, "encrypt partition table error"); return false; } diff --git a/components/esp32/include/esp_flash_data_types.h b/components/esp32/include/esp_flash_data_types.h new file mode 100644 index 000000000..b16ee59f5 --- /dev/null +++ b/components/esp32/include/esp_flash_data_types.h @@ -0,0 +1,102 @@ +// 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. +#ifndef __ESP_BIN_TYPES_H__ +#define __ESP_BIN_TYPES_H__ + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define ESP_PARTITION_TABLE_ADDR 0x4000 +#define ESP_PARTITION_MAGIC 0x50AA + +/*spi mode,saved in third byte in flash */ +typedef enum { + ESP_IMAGE_SPI_MODE_QIO, + ESP_IMAGE_SPI_MODE_QOUT, + ESP_IMAGE_SPI_MODE_DIO, + ESP_IMAGE_SPI_MODE_DOUT, + ESP_IMAGE_SPI_MODE_FAST_READ, + ESP_IMAGE_SPI_MODE_SLOW_READ +} esp_image_spi_mode_t; + +/* spi speed*/ +enum { + ESP_IMAGE_SPI_SPEED_40M, + ESP_IMAGE_SPI_SPEED_26M, + ESP_IMAGE_SPI_SPEED_20M, + ESP_IMAGE_SPI_SPEED_80M = 0xF +} esp_image_spi_freq_t; + +/*supported flash sizes*/ +typedef enum { + ESP_IMAGE_FLASH_SIZE_1MB = 0, + ESP_IMAGE_FLASH_SIZE_2MB, + ESP_IMAGE_FLASH_SIZE_4MB, + ESP_IMAGE_FLASH_SIZE_8MB, + ESP_IMAGE_FLASH_SIZE_16MB, + ESP_IMAGE_FLASH_SIZE_MAX +} esp_image_flash_size_t; + +typedef struct { + char magic; + char blocks; + char spi_mode; /* flag of flash read mode in unpackage and usage in future */ + char spi_speed: 4; /* low bit */ + char spi_size: 4; + unsigned int entry_addr; + uint8_t encrypt_flag; /* encrypt flag */ + uint8_t secure_boot_flag; /* secure boot flag */ + char extra_header[14]; /* ESP32 additional header, unused by second bootloader */ +} esp_image_header_t; + +/* each header of flash bin block */ +typedef struct { + unsigned int load_addr; + unsigned int data_len; +} esp_image_section_header_t; + + +/* OTA selection structure (two copies in the OTA data partition.) + Size of 32 bytes is friendly to flash encryption */ +typedef struct { + uint32_t ota_seq; + uint8_t seq_label[24]; + uint32_t crc; /* CRC32 of ota_seq field only */ +} esp_ota_select_entry_t; + + +typedef struct { + uint32_t offset; + uint32_t size; +} esp_partition_pos_t; + +typedef struct { + uint16_t magic; + uint8_t type; /* partition Type */ + uint8_t subtype; /* part_subtype */ + esp_partition_pos_t pos; + uint8_t label[16]; /* label for the partition */ + uint8_t reserved[4]; /* reserved */ +} esp_partition_info_t; + + +#ifdef __cplusplus +} +#endif + +#endif //__ESP_BIN_TYPES_H__ From 54ca573ce465b4a12cad9cc804c6c80063dcab35 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 19 Oct 2016 17:08:05 +0800 Subject: [PATCH 03/13] spi_flash: move cache operations into separate file --- components/spi_flash/README.rst | 33 +++ .../{esp_spi_flash.c => cache_utils.c} | 201 ++++-------------- components/spi_flash/cache_utils.h | 44 ++++ components/spi_flash/flash_ops.c | 158 ++++++++++++++ 4 files changed, 274 insertions(+), 162 deletions(-) create mode 100644 components/spi_flash/README.rst rename components/spi_flash/{esp_spi_flash.c => cache_utils.c} (53%) create mode 100644 components/spi_flash/cache_utils.h create mode 100644 components/spi_flash/flash_ops.c diff --git a/components/spi_flash/README.rst b/components/spi_flash/README.rst new file mode 100644 index 000000000..22f98cf02 --- /dev/null +++ b/components/spi_flash/README.rst @@ -0,0 +1,33 @@ +Driver for SPI flash read/write/erase operations +================================================ + +Implementation notes +-------------------- + +In order to perform some flash operations, we need to make sure both CPUs +are not running any code from flash for the duration of the flash operation. +In a single-core setup this is easy: we disable interrupts/scheduler and do +the flash operation. In the dual-core setup this is slightly more complicated. +We need to make sure that the other CPU doesn't run any code from flash. + + +When SPI flash API is called on CPU A (can be PRO or APP), we start +spi_flash_op_block_func function on CPU B using esp_ipc_call API. This API +wakes up high priority task on CPU B and tells it to execute given function, +in this case spi_flash_op_block_func. This function disables cache on CPU B and +signals that cache is disabled by setting s_flash_op_can_start flag. +Then the task on CPU A disables cache as well, and proceeds to execute flash +operation. + +While flash operation is running, interrupts can still run on CPU B. +We assume that all interrupt code is placed into RAM. + +Once flash operation is complete, function on CPU A sets another flag, +s_flash_op_complete, to let the task on CPU B know that it can re-enable +cache and release the CPU. Then the function on CPU A re-enables the cache on +CPU A as well and returns control to the calling code. + +Additionally, all API functions are protected with a mutex (s_flash_op_mutex). + +In a single core environment (CONFIG_FREERTOS_UNICORE enabled), we simply +disable both caches, no inter-CPU communication takes place. diff --git a/components/spi_flash/esp_spi_flash.c b/components/spi_flash/cache_utils.c similarity index 53% rename from components/spi_flash/esp_spi_flash.c rename to components/spi_flash/cache_utils.c index d702f3b81..6ae47bdb3 100644 --- a/components/spi_flash/esp_spi_flash.c +++ b/components/spi_flash/cache_utils.c @@ -3,7 +3,7 @@ // 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 @@ -30,39 +30,7 @@ #include "esp_spi_flash.h" #include "esp_log.h" -/* - Driver for SPI flash read/write/erase operations - In order to perform some flash operations, we need to make sure both CPUs - are not running any code from flash for the duration of the flash operation. - In a single-core setup this is easy: we disable interrupts/scheduler and do - the flash operation. In the dual-core setup this is slightly more complicated. - We need to make sure that the other CPU doesn't run any code from flash. - - - When SPI flash API is called on CPU A (can be PRO or APP), we start - spi_flash_op_block_func function on CPU B using esp_ipc_call API. This API - wakes up high priority task on CPU B and tells it to execute given function, - in this case spi_flash_op_block_func. This function disables cache on CPU B and - signals that cache is disabled by setting s_flash_op_can_start flag. - Then the task on CPU A disables cache as well, and proceeds to execute flash - operation. - - While flash operation is running, interrupts can still run on CPU B. - We assume that all interrupt code is placed into RAM. - - Once flash operation is complete, function on CPU A sets another flag, - s_flash_op_complete, to let the task on CPU B know that it can re-enable - cache and release the CPU. Then the function on CPU A re-enables the cache on - CPU A as well and returns control to the calling code. - - Additionally, all API functions are protected with a mutex (s_flash_op_mutex). - - In a single core environment (CONFIG_FREERTOS_UNICORE enabled), we simply - disable both caches, no inter-CPU communication takes place. -*/ - -static esp_err_t spi_flash_translate_rc(SpiFlashOpResult rc); static void IRAM_ATTR spi_flash_disable_cache(uint32_t cpuid, uint32_t* saved_state); static void IRAM_ATTR spi_flash_restore_cache(uint32_t cpuid, uint32_t saved_state); @@ -72,25 +40,23 @@ static uint32_t s_flash_op_cache_state[2]; static SemaphoreHandle_t s_flash_op_mutex; static bool s_flash_op_can_start = false; static bool s_flash_op_complete = false; -#endif //CONFIG_FREERTOS_UNICORE -#if CONFIG_SPI_FLASH_ENABLE_COUNTERS -static const char* TAG = "spi_flash"; -static spi_flash_counters_t s_flash_stats; +void spi_flash_init_lock() +{ + s_flash_op_mutex = xSemaphoreCreateMutex(); +} -#define COUNTER_START() uint32_t ts_begin = xthal_get_ccount() -#define COUNTER_STOP(counter) do{ s_flash_stats.counter.count++; s_flash_stats.counter.time += (xthal_get_ccount() - ts_begin) / (XT_CLOCK_FREQ / 1000000); } while(0) -#define COUNTER_ADD_BYTES(counter, size) do { s_flash_stats.counter.bytes += size; } while (0) -#else -#define COUNTER_START() -#define COUNTER_STOP(counter) -#define COUNTER_ADD_BYTES(counter, size) +void spi_flash_op_lock() +{ + xSemaphoreTake(s_flash_op_mutex, portMAX_DELAY); +} -#endif //CONFIG_SPI_FLASH_ENABLE_COUNTERS +void spi_flash_op_unlock() +{ + xSemaphoreGive(s_flash_op_mutex); +} -#ifndef CONFIG_FREERTOS_UNICORE - -static void IRAM_ATTR spi_flash_op_block_func(void* arg) +void IRAM_ATTR spi_flash_op_block_func(void* arg) { // Disable scheduler on this CPU vTaskSuspendAll(); @@ -108,19 +74,9 @@ static void IRAM_ATTR spi_flash_op_block_func(void* arg) xTaskResumeAll(); } -void spi_flash_init() +void IRAM_ATTR spi_flash_disable_interrupts_caches_and_other_cpu() { - s_flash_op_mutex = xSemaphoreCreateMutex(); - -#if CONFIG_SPI_FLASH_ENABLE_COUNTERS - spi_flash_reset_counters(); -#endif -} - -static void IRAM_ATTR spi_flash_disable_interrupts_caches_and_other_cpu() -{ - // Take the API lock - xSemaphoreTake(s_flash_op_mutex, portMAX_DELAY); + spi_flash_op_lock(); const uint32_t cpuid = xPortGetCoreID(); const uint32_t other_cpuid = (cpuid == 0) ? 1 : 0; @@ -152,7 +108,7 @@ static void IRAM_ATTR spi_flash_disable_interrupts_caches_and_other_cpu() spi_flash_disable_cache(cpuid, &s_flash_op_cache_state[cpuid]); } -static void IRAM_ATTR spi_flash_enable_interrupts_caches_and_other_cpu() +void IRAM_ATTR spi_flash_enable_interrupts_caches_and_other_cpu() { const uint32_t cpuid = xPortGetCoreID(); const uint32_t other_cpuid = (cpuid == 0) ? 1 : 0; @@ -173,98 +129,45 @@ static void IRAM_ATTR spi_flash_enable_interrupts_caches_and_other_cpu() xTaskResumeAll(); } // Release API lock - xSemaphoreGive(s_flash_op_mutex); + spi_flash_op_unlock(); } -#else // CONFIG_FREERTOS_UNICORE +#else // CONFIG_FREERTOS_UNICORE -void spi_flash_init() +void spi_flash_init_lock() { -#if CONFIG_SPI_FLASH_ENABLE_COUNTERS - spi_flash_reset_counters(); -#endif } -static void IRAM_ATTR spi_flash_disable_interrupts_caches_and_other_cpu() +void spi_flash_op_lock() { vTaskSuspendAll(); +} + +void spi_flash_op_unlock() +{ + xTaskResumeAll(); +} + + +void IRAM_ATTR spi_flash_disable_interrupts_caches_and_other_cpu() +{ + spi_flash_op_lock(); spi_flash_disable_cache(0, &s_flash_op_cache_state[0]); } -static void IRAM_ATTR spi_flash_enable_interrupts_caches_and_other_cpu() +void IRAM_ATTR spi_flash_enable_interrupts_caches_and_other_cpu() { spi_flash_restore_cache(0, s_flash_op_cache_state[0]); - xTaskResumeAll(); + spi_flash_op_unlock(); } #endif // CONFIG_FREERTOS_UNICORE - -SpiFlashOpResult IRAM_ATTR spi_flash_unlock() -{ - static bool unlocked = false; - if (!unlocked) { - SpiFlashOpResult rc = SPIUnlock(); - if (rc != SPI_FLASH_RESULT_OK) { - return rc; - } - unlocked = true; - } - return SPI_FLASH_RESULT_OK; -} - -esp_err_t IRAM_ATTR spi_flash_erase_sector(uint16_t sec) -{ - COUNTER_START(); - spi_flash_disable_interrupts_caches_and_other_cpu(); - SpiFlashOpResult rc; - rc = spi_flash_unlock(); - if (rc == SPI_FLASH_RESULT_OK) { - rc = SPIEraseSector(sec); - } - spi_flash_enable_interrupts_caches_and_other_cpu(); - COUNTER_STOP(erase); - return spi_flash_translate_rc(rc); -} - -esp_err_t IRAM_ATTR spi_flash_write(uint32_t dest_addr, const uint32_t *src, uint32_t size) -{ - COUNTER_START(); - spi_flash_disable_interrupts_caches_and_other_cpu(); - SpiFlashOpResult rc; - rc = spi_flash_unlock(); - if (rc == SPI_FLASH_RESULT_OK) { - rc = SPIWrite(dest_addr, src, (int32_t) size); - COUNTER_ADD_BYTES(write, size); - } - spi_flash_enable_interrupts_caches_and_other_cpu(); - COUNTER_STOP(write); - return spi_flash_translate_rc(rc); -} - -esp_err_t IRAM_ATTR spi_flash_read(uint32_t src_addr, uint32_t *dest, uint32_t size) -{ - COUNTER_START(); - spi_flash_disable_interrupts_caches_and_other_cpu(); - SpiFlashOpResult rc = SPIRead(src_addr, dest, (int32_t) size); - COUNTER_ADD_BYTES(read, size); - spi_flash_enable_interrupts_caches_and_other_cpu(); - COUNTER_STOP(read); - return spi_flash_translate_rc(rc); -} - -static esp_err_t spi_flash_translate_rc(SpiFlashOpResult rc) -{ - switch (rc) { - case SPI_FLASH_RESULT_OK: - return ESP_OK; - case SPI_FLASH_RESULT_TIMEOUT: - return ESP_ERR_FLASH_OP_TIMEOUT; - case SPI_FLASH_RESULT_ERR: - default: - return ESP_ERR_FLASH_OP_FAIL; - } -} +/** + * The following two functions are replacements for Cache_Read_Disable and Cache_Read_Enable + * function in ROM. They are used to work around a bug where Cache_Read_Disable requires a call to + * Cache_Flush before Cache_Read_Enable, even if cached data was not modified. + */ static const uint32_t cache_mask = DPORT_APP_CACHE_MASK_OPSDRAM | DPORT_APP_CACHE_MASK_DROM0 | DPORT_APP_CACHE_MASK_DRAM1 | DPORT_APP_CACHE_MASK_IROM0 | @@ -300,29 +203,3 @@ static void IRAM_ATTR spi_flash_restore_cache(uint32_t cpuid, uint32_t saved_sta } } -#if CONFIG_SPI_FLASH_ENABLE_COUNTERS - -static inline void dump_counter(spi_flash_counter_t* counter, const char* name) -{ - ESP_LOGI(TAG, "%s count=%8d time=%8dms bytes=%8d\n", name, - counter->count, counter->time, counter->bytes); -} - -const spi_flash_counters_t* spi_flash_get_counters() -{ - return &s_flash_stats; -} - -void spi_flash_reset_counters() -{ - memset(&s_flash_stats, 0, sizeof(s_flash_stats)); -} - -void spi_flash_dump_counters() -{ - dump_counter(&s_flash_stats.read, "read "); - dump_counter(&s_flash_stats.write, "write"); - dump_counter(&s_flash_stats.erase, "erase"); -} - -#endif //CONFIG_SPI_FLASH_ENABLE_COUNTERS diff --git a/components/spi_flash/cache_utils.h b/components/spi_flash/cache_utils.h new file mode 100644 index 000000000..899a31c65 --- /dev/null +++ b/components/spi_flash/cache_utils.h @@ -0,0 +1,44 @@ +// 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. + +#ifndef ESP_SPI_FLASH_CACHE_UTILS_H +#define ESP_SPI_FLASH_CACHE_UTILS_H + +/** + * This header file contains declarations of cache manipulation functions + * used both in flash_ops.c and flash_mmap.c. + * + * These functions are considered internal and are not designed to be called from applications. + */ + +// Init mutex protecting access to spi_flash_* APIs +void spi_flash_init_lock(); + +// Take mutex protecting access to spi_flash_* APIs +void spi_flash_op_lock(); + +// Release said mutex +void spi_flash_op_unlock(); + +// Suspend the scheduler on both CPUs, disable cache. +// Contrary to its name this doesn't do anything with interrupts, yet. +// Interrupt disabling capability will be added once we implement +// interrupt allocation API. +void spi_flash_disable_interrupts_caches_and_other_cpu(); + +// Enable cache, enable interrupts (to be added in future), resume scheduler +void spi_flash_enable_interrupts_caches_and_other_cpu(); + + +#endif //ESP_SPI_FLASH_CACHE_UTILS_H diff --git a/components/spi_flash/flash_ops.c b/components/spi_flash/flash_ops.c new file mode 100644 index 000000000..99e8ef77f --- /dev/null +++ b/components/spi_flash/flash_ops.c @@ -0,0 +1,158 @@ +// 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 +#include +#include +#include +#include +#include +#include "sdkconfig.h" +#include "esp_ipc.h" +#include "esp_attr.h" +#include "esp_spi_flash.h" +#include "esp_log.h" +#include "cache_utils.h" + +#if CONFIG_SPI_FLASH_ENABLE_COUNTERS +static const char* TAG = "spi_flash"; +static spi_flash_counters_t s_flash_stats; + +#define COUNTER_START() uint32_t ts_begin = xthal_get_ccount() +#define COUNTER_STOP(counter) \ + do{ \ + s_flash_stats.counter.count++; \ + s_flash_stats.counter.time += (xthal_get_ccount() - ts_begin) / (XT_CLOCK_FREQ / 1000000); \\ + } while(0) + +#define COUNTER_ADD_BYTES(counter, size) \ + do { \ + s_flash_stats.counter.bytes += size; \ + } while (0) + +#else +#define COUNTER_START() +#define COUNTER_STOP(counter) +#define COUNTER_ADD_BYTES(counter, size) + +#endif //CONFIG_SPI_FLASH_ENABLE_COUNTERS + +static esp_err_t spi_flash_translate_rc(SpiFlashOpResult rc); + +void spi_flash_init() +{ + spi_flash_init_lock(); +#if CONFIG_SPI_FLASH_ENABLE_COUNTERS + spi_flash_reset_counters(); +#endif +} + +SpiFlashOpResult IRAM_ATTR spi_flash_unlock() +{ + static bool unlocked = false; + if (!unlocked) { + SpiFlashOpResult rc = SPIUnlock(); + if (rc != SPI_FLASH_RESULT_OK) { + return rc; + } + unlocked = true; + } + return SPI_FLASH_RESULT_OK; +} + +esp_err_t IRAM_ATTR spi_flash_erase_sector(uint16_t sec) +{ + COUNTER_START(); + spi_flash_disable_interrupts_caches_and_other_cpu(); + SpiFlashOpResult rc; + rc = spi_flash_unlock(); + if (rc == SPI_FLASH_RESULT_OK) { + rc = SPIEraseSector(sec); + } + spi_flash_enable_interrupts_caches_and_other_cpu(); + COUNTER_STOP(erase); + return spi_flash_translate_rc(rc); +} + +esp_err_t IRAM_ATTR spi_flash_write(uint32_t dest_addr, const uint32_t *src, uint32_t size) +{ + COUNTER_START(); + spi_flash_disable_interrupts_caches_and_other_cpu(); + SpiFlashOpResult rc; + rc = spi_flash_unlock(); + if (rc == SPI_FLASH_RESULT_OK) { + rc = SPIWrite(dest_addr, src, (int32_t) size); + COUNTER_ADD_BYTES(write, size); + } + spi_flash_enable_interrupts_caches_and_other_cpu(); + COUNTER_STOP(write); + return spi_flash_translate_rc(rc); +} + +esp_err_t IRAM_ATTR spi_flash_read(uint32_t src_addr, uint32_t *dest, uint32_t size) +{ + COUNTER_START(); + spi_flash_disable_interrupts_caches_and_other_cpu(); + SpiFlashOpResult rc = SPIRead(src_addr, dest, (int32_t) size); + COUNTER_ADD_BYTES(read, size); + spi_flash_enable_interrupts_caches_and_other_cpu(); + COUNTER_STOP(read); + return spi_flash_translate_rc(rc); +} + +static esp_err_t spi_flash_translate_rc(SpiFlashOpResult rc) +{ + switch (rc) { + case SPI_FLASH_RESULT_OK: + return ESP_OK; + case SPI_FLASH_RESULT_TIMEOUT: + return ESP_ERR_FLASH_OP_TIMEOUT; + case SPI_FLASH_RESULT_ERR: + default: + return ESP_ERR_FLASH_OP_FAIL; + } +} + +#if CONFIG_SPI_FLASH_ENABLE_COUNTERS + +static inline void dump_counter(spi_flash_counter_t* counter, const char* name) +{ + ESP_LOGI(TAG, "%s count=%8d time=%8dms bytes=%8d\n", name, + counter->count, counter->time, counter->bytes); +} + +const spi_flash_counters_t* spi_flash_get_counters() +{ + return &s_flash_stats; +} + +void spi_flash_reset_counters() +{ + memset(&s_flash_stats, 0, sizeof(s_flash_stats)); +} + +void spi_flash_dump_counters() +{ + dump_counter(&s_flash_stats.read, "read "); + dump_counter(&s_flash_stats.write, "write"); + dump_counter(&s_flash_stats.erase, "erase"); +} + +#endif //CONFIG_SPI_FLASH_ENABLE_COUNTERS From 42068c3b36cb4b5898ea0f9b353057dd85af33ab Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 19 Oct 2016 17:17:24 +0800 Subject: [PATCH 04/13] spi_flash: implement mmap/munmap --- components/esp32/include/esp_err.h | 2 + components/esp32/include/soc/dport_reg.h | 5 + components/spi_flash/flash_mmap.c | 211 +++++++++++++++++++ components/spi_flash/include/esp_spi_flash.h | 57 +++++ 4 files changed, 275 insertions(+) create mode 100644 components/spi_flash/flash_mmap.c diff --git a/components/esp32/include/esp_err.h b/components/esp32/include/esp_err.h index 4f013f91a..af9d2ec33 100644 --- a/components/esp32/include/esp_err.h +++ b/components/esp32/include/esp_err.h @@ -31,6 +31,8 @@ typedef int32_t esp_err_t; #define ESP_ERR_NO_MEM 0x101 #define ESP_ERR_INVALID_ARG 0x102 #define ESP_ERR_INVALID_STATE 0x103 +#define ESP_ERR_INVALID_SIZE 0x104 +#define ESP_ERR_NOT_FOUND 0x105 /** * Macro which can be used to check the error code, diff --git a/components/esp32/include/soc/dport_reg.h b/components/esp32/include/soc/dport_reg.h index d65d9edbc..0c43c0874 100644 --- a/components/esp32/include/soc/dport_reg.h +++ b/components/esp32/include/soc/dport_reg.h @@ -3830,6 +3830,11 @@ #define DPORT_DATE_S 0 #define DPORT_DPORT_DATE_VERSION 0x1605190 +/* Flash MMU table for PRO CPU */ +#define DPORT_PRO_FLASH_MMU_TABLE ((volatile uint32_t*) 0x3FF10000) + +/* Flash MMU table for APP CPU */ +#define DPORT_APP_FLASH_MMU_TABLE ((volatile uint32_t*) 0x3FF12000) diff --git a/components/spi_flash/flash_mmap.c b/components/spi_flash/flash_mmap.c new file mode 100644 index 000000000..8636a2605 --- /dev/null +++ b/components/spi_flash/flash_mmap.c @@ -0,0 +1,211 @@ +// 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 +#include +#include +#include +#include +#include +#include "sdkconfig.h" +#include "esp_ipc.h" +#include "esp_attr.h" +#include "esp_spi_flash.h" +#include "esp_log.h" +#include "cache_utils.h" + +#ifndef NDEBUG +// Enable built-in checks in queue.h in debug builds +#define INVARIANTS +#endif +#include "rom/queue.h" + +#define REGIONS_COUNT 4 +#define PAGES_PER_REGION 64 +#define FLASH_PAGE_SIZE 0x10000 +#define INVALID_ENTRY_VAL 0x100 +#define VADDR0_START_ADDR 0x3F400000 +#define VADDR1_START_ADDR 0x40000000 +#define VADDR1_FIRST_USABLE_ADDR 0x400D0000 +#define PRO_IRAM0_FIRST_USABLE_PAGE ((VADDR1_FIRST_USABLE_ADDR - VADDR1_START_ADDR) / FLASH_PAGE_SIZE + 64) + + +typedef struct mmap_entry_{ + uint32_t handle; + int page; + int count; + LIST_ENTRY(mmap_entry_) entries; +} mmap_entry_t; + + +static LIST_HEAD(mmap_entries_head, mmap_entry_) s_mmap_entries_head = + LIST_HEAD_INITIALIZER(s_mmap_entries_head); +static uint8_t s_mmap_page_refcnt[REGIONS_COUNT * PAGES_PER_REGION] = {0}; +static uint32_t s_mmap_last_handle = 0; + + +static void IRAM_ATTR spi_flash_mmap_init() +{ + for (int i = 0; i < REGIONS_COUNT * PAGES_PER_REGION; ++i) { + uint32_t entry_pro = DPORT_PRO_FLASH_MMU_TABLE[i]; + uint32_t entry_app = DPORT_APP_FLASH_MMU_TABLE[i]; + if (entry_pro != entry_app) { + // clean up entries used by boot loader + entry_pro = 0; + DPORT_PRO_FLASH_MMU_TABLE[i] = 0; + } + if ((entry_pro & 0x100) == 0 && (i == 0 || i == PRO_IRAM0_FIRST_USABLE_PAGE || entry_pro != 0)) { + s_mmap_page_refcnt[i] = 1; + } + } +} + +esp_err_t IRAM_ATTR spi_flash_mmap(uint32_t src_addr, size_t size, spi_flash_mmap_memory_t memory, + const void** out_ptr, spi_flash_mmap_handle_t* out_handle) +{ + esp_err_t ret; + mmap_entry_t* new_entry = (mmap_entry_t*) malloc(sizeof(mmap_entry_t)); + if (new_entry == 0) { + return ESP_ERR_NO_MEM; + } + if (src_addr & 0xffff) { + return ESP_ERR_INVALID_ARG; + } + spi_flash_disable_interrupts_caches_and_other_cpu(); + if (s_mmap_page_refcnt[0] == 0) { + spi_flash_mmap_init(); + } + // figure out the memory region where we should look for pages + int region_begin; // first page to check + int region_size; // number of pages to check + uint32_t region_addr; // base address of memory region + if (memory == SPI_FLASH_MMAP_DATA) { + // Vaddr0 + region_begin = 0; + region_size = 64; + region_addr = VADDR0_START_ADDR; + } else { + // only part of VAddr1 is usable, so adjust for that + region_begin = VADDR1_FIRST_USABLE_ADDR; + region_size = 3 * 64 - region_begin; + region_addr = VADDR1_FIRST_USABLE_ADDR; + } + // region which should be mapped + int phys_page = src_addr / FLASH_PAGE_SIZE; + int page_count = (size + FLASH_PAGE_SIZE - 1) / FLASH_PAGE_SIZE; + // The following part searches for a range of MMU entries which can be used. + // Algorithm is essentially naïve strstr algorithm, except that unused MMU + // entries are treated as wildcards. + int start; + int end = region_begin + region_size - page_count; + for (start = region_begin; start < end; ++start) { + int page = phys_page; + int pos; + for (pos = start; pos < start + page_count; ++pos, ++page) { + int table_val = (int) DPORT_PRO_FLASH_MMU_TABLE[pos]; + uint8_t refcnt = s_mmap_page_refcnt[pos]; + if (refcnt != 0 && table_val != page) { + break; + } + } + // whole mapping range matched, bail out + if (pos - start == page_count) { + break; + } + } + // checked all the region(s) and haven't found anything? + if (start == end) { + *out_handle = 0; + *out_ptr = NULL; + ret = ESP_ERR_NO_MEM; + } else { + // set up mapping using pages [start, start + page_count) + uint32_t entry_val = (uint32_t) phys_page; + for (int i = start; i != start + page_count; ++i, ++entry_val) { + // sanity check: we won't reconfigure entries with non-zero reference count + assert(s_mmap_page_refcnt[i] == 0 || + (DPORT_PRO_FLASH_MMU_TABLE[i] == entry_val && + DPORT_APP_FLASH_MMU_TABLE[i] == entry_val)); + if (s_mmap_page_refcnt[i] == 0) { + DPORT_PRO_FLASH_MMU_TABLE[i] = entry_val; + DPORT_APP_FLASH_MMU_TABLE[i] = entry_val; + } + ++s_mmap_page_refcnt[i]; + } + + LIST_INSERT_HEAD(&s_mmap_entries_head, new_entry, entries); + new_entry->page = start; + new_entry->count = page_count; + new_entry->handle = ++s_mmap_last_handle; + *out_handle = new_entry->handle; + *out_ptr = (void*) (region_addr + start * FLASH_PAGE_SIZE); + ret = ESP_OK; + } + spi_flash_enable_interrupts_caches_and_other_cpu(); + if (*out_ptr == NULL) { + free(new_entry); + } + return ret; +} + +void IRAM_ATTR spi_flash_munmap(spi_flash_mmap_handle_t handle) +{ + spi_flash_disable_interrupts_caches_and_other_cpu(); + mmap_entry_t* it; + // look for handle in linked list + for (it = LIST_FIRST(&s_mmap_entries_head); it != NULL; it = LIST_NEXT(it, entries)) { + if (it->handle == handle) { + // for each page, decrement reference counter + // if reference count is zero, disable MMU table entry to + // facilitate debugging of use-after-free conditions + for (int i = it->page; i < it->page + it->count; ++i) { + assert(s_mmap_page_refcnt[i] > 0); + if (--s_mmap_page_refcnt[i] == 0) { + DPORT_PRO_FLASH_MMU_TABLE[i] = INVALID_ENTRY_VAL; + DPORT_APP_FLASH_MMU_TABLE[i] = INVALID_ENTRY_VAL; + } + } + LIST_REMOVE(it, entries); + break; + } + } + spi_flash_enable_interrupts_caches_and_other_cpu(); + if (it == NULL) { + assert(0 && "invalid handle, or handle already unmapped"); + } + free(it); +} + +void spi_flash_mmap_dump() +{ + if (s_mmap_page_refcnt[0] == 0) { + spi_flash_mmap_init(); + } + mmap_entry_t* it; + for (it = LIST_FIRST(&s_mmap_entries_head); it != NULL; it = LIST_NEXT(it, entries)) { + printf("handle=%d page=%d count=%d\n", it->handle, it->page, it->count); + } + for (int i = 0; i < REGIONS_COUNT * PAGES_PER_REGION; ++i) { + if (s_mmap_page_refcnt[i] != 0) { + printf("page %d: refcnt=%d paddr=%d\n", + i, (int) s_mmap_page_refcnt[i], DPORT_PRO_FLASH_MMU_TABLE[i]); + } + } +} diff --git a/components/spi_flash/include/esp_spi_flash.h b/components/spi_flash/include/esp_spi_flash.h index 6d635880e..597e41857 100644 --- a/components/spi_flash/include/esp_spi_flash.h +++ b/components/spi_flash/include/esp_spi_flash.h @@ -70,6 +70,63 @@ esp_err_t spi_flash_write(uint32_t des_addr, const uint32_t *src_addr, uint32_t esp_err_t spi_flash_read(uint32_t src_addr, uint32_t *des_addr, uint32_t size); +/** + * @brief Enumeration which specifies memory space requested in an mmap call + */ +typedef enum { + SPI_FLASH_MMAP_DATA, /**< map to data memory (Vaddr0), allows byte-aligned access, 4 MB total */ + SPI_FLASH_MMAP_INST, /**< map to instruction memory (Vaddr1-3), allows only 4-byte-aligned access, 11 MB total */ +} spi_flash_mmap_memory_t; + +/** + * @brief Opaque handle for memory region obtained from spi_flash_mmap. + */ +typedef uint32_t spi_flash_mmap_handle_t; + +/** + * @brief Map region of flash memory into data or instruction address space + * + * This function allocates sufficient number of 64k MMU pages and configures + * them to map request region of flash memory into data address space or into + * instruction address space. It may reuse MMU pages which already provide + * required mapping. As with any allocator, there is possibility of fragmentation + * of address space if mmap/munmap are heavily used. To troubleshoot issues with + * page allocation, use spi_flash_mmap_dump function. + * + * @param src_addr Physical address in flash where requested region starts. + * This address *must* be aligned to 64kB boundary. + * @param size Size of region which has to be mapped. This size will be rounded + * up to a 64k boundary. + * @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 spi_flash_munmap call + * + * @return ESP_OK on success, ESP_ERR_NO_MEM if pages can not be allocated + */ +esp_err_t spi_flash_mmap(uint32_t src_addr, size_t size, spi_flash_mmap_memory_t memory, + const void** out_ptr, spi_flash_mmap_handle_t* out_handle); + +/** + * @brief Release region previously obtained using spi_flash_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 spi_flash_munmap(spi_flash_mmap_handle_t handle); + +/** + * @brief Display information about mapped regions + * + * This function lists handles obtained using spi_flash_mmap, along with range + * of pages allocated to each handle. It also lists all non-zero entries of + * MMU table and corresponding reference counts. + */ +void spi_flash_mmap_dump(); + #if CONFIG_SPI_FLASH_ENABLE_COUNTERS /** From 999e6d4e8f9035f8de54410a8e037ec7ada3e917 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 19 Oct 2016 17:19:02 +0800 Subject: [PATCH 05/13] freertos: move panic handler data to DRAM Fixes https://ezredmine.espressif.com/issues/7817 --- components/esp32/ld/esp32.common.ld | 1 + 1 file changed, 1 insertion(+) diff --git a/components/esp32/ld/esp32.common.ld b/components/esp32/ld/esp32.common.ld index a3c636784..2226e9882 100644 --- a/components/esp32/ld/esp32.common.ld +++ b/components/esp32/ld/esp32.common.ld @@ -92,6 +92,7 @@ SECTIONS KEEP(*(.gnu.linkonce.s2.*)) KEEP(*(.jcr)) *(.dram1 .dram1.*) + *libfreertos.a:panic.o(.rodata .rodata.*) _data_end = ABSOLUTE(.); . = ALIGN(4); _heap_start = ABSOLUTE(.); From 079d9ea018729c95a9aea0ac759f40849cb13f5e Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 19 Oct 2016 18:04:25 +0800 Subject: [PATCH 06/13] spi_flash: implement partition API --- components/spi_flash/include/esp_partition.h | 156 ++++++++----- components/spi_flash/partition.c | 221 +++++++++++++++++++ 2 files changed, 326 insertions(+), 51 deletions(-) create mode 100644 components/spi_flash/partition.c diff --git a/components/spi_flash/include/esp_partition.h b/components/spi_flash/include/esp_partition.h index fd1223da5..6bdba149f 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, +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, - 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, + 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, - PT_FILESYSTEM_MASK = 0x0200, - PT_FILESYSTEM_ESPHTTPD = 0x0200, - PT_FILESYSTEM_FAT = 0x0201, - PT_FILESYSTEM_SPIFFS = 0x0202, - PT_FILESYSTEM_ANY = 0x20ff, + 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, - PT_END = 0xffff -}; + ESP_PARTITION_END = 0xffff +} esp_partition_type_t; -#define PT_APP_OTA(i) ((esp_partition_type_t)(PT_APP_OTA_MIN + ((i) & 0xf))) +#define ESP_PARTITION_APP_OTA(i) ((esp_partition_type_t)(ESP_PARTITION_APP_OTA_MIN + ((i) & 0xf))) -typedef struct esp_partition_iterator_opaque_t* esp_partition_iterator_t; +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 000000000..381a1624b --- /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; +} From 2c5340d47e2a482c2fc58ae02178332775c3a699 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 21 Oct 2016 17:28:50 +0800 Subject: [PATCH 07/13] spi_flash: change argument types spi_flash_read and spi_flash_write currently have a limitation that source and destination must be word-aligned. This can be fixed by adding code paths for various unaligned scenarios, but function signatures also need to be adjusted. As a first step (since we are pre-1.0 and can still change function signatures) alignment checks are added, and pointer types are relaxed to uint8_t. Later we will add handling of unaligned operations. This change also introduces spi_flash_erase_range and spi_flash_get_chip_size functions. We probably need something like spi_flash_chip_size_detect which will detect actual chip size. This is to allow single application binary to be used on a variety of boards and modules. --- components/nvs_flash/src/nvs_page.cpp | 27 ++++--- .../nvs_flash/test/spi_flash_emulation.cpp | 10 +-- .../nvs_flash/test/spi_flash_emulation.h | 6 +- .../test/test_spi_flash_emulation.cpp | 20 ++--- components/spi_flash/flash_ops.c | 81 +++++++++++++++++-- components/spi_flash/include/esp_spi_flash.h | 50 +++++++++--- 6 files changed, 146 insertions(+), 48 deletions(-) diff --git a/components/nvs_flash/src/nvs_page.cpp b/components/nvs_flash/src/nvs_page.cpp index f4fc5430c..9ba478e21 100644 --- a/components/nvs_flash/src/nvs_page.cpp +++ b/components/nvs_flash/src/nvs_page.cpp @@ -37,7 +37,7 @@ esp_err_t Page::load(uint32_t sectorNumber) mErasedEntryCount = 0; Header header; - auto rc = spi_flash_read(mBaseAddress, reinterpret_cast(&header), sizeof(header)); + auto rc = spi_flash_read(mBaseAddress, reinterpret_cast(&header), sizeof(header)); if (rc != ESP_OK) { mState = PageState::INVALID; return rc; @@ -48,7 +48,7 @@ esp_err_t Page::load(uint32_t sectorNumber) // reading the whole page takes ~40 times less than erasing it uint32_t line[8]; for (uint32_t i = 0; i < SPI_FLASH_SEC_SIZE; i += sizeof(line)) { - rc = spi_flash_read(mBaseAddress + i, line, sizeof(line)); + rc = spi_flash_read(mBaseAddress + i, reinterpret_cast(line), sizeof(line)); if (rc != ESP_OK) { mState = PageState::INVALID; return rc; @@ -86,7 +86,7 @@ esp_err_t Page::load(uint32_t sectorNumber) esp_err_t Page::writeEntry(const Item& item) { - auto rc = spi_flash_write(getEntryAddress(mNextFreeEntry), reinterpret_cast(&item), sizeof(item)); + auto rc = spi_flash_write(getEntryAddress(mNextFreeEntry), reinterpret_cast(&item), sizeof(item)); if (rc != ESP_OK) { mState = PageState::INVALID; return rc; @@ -114,7 +114,7 @@ esp_err_t Page::writeEntryData(const uint8_t* data, size_t size) assert(mFirstUsedEntry != INVALID_ENTRY); const uint16_t count = size / ENTRY_SIZE; - auto rc = spi_flash_write(getEntryAddress(mNextFreeEntry), reinterpret_cast(data), static_cast(size)); + auto rc = spi_flash_write(getEntryAddress(mNextFreeEntry), data, size); if (rc != ESP_OK) { mState = PageState::INVALID; return rc; @@ -396,8 +396,8 @@ esp_err_t Page::mLoadEntryTable() if (mState == PageState::ACTIVE || mState == PageState::FULL || mState == PageState::FREEING) { - auto rc = spi_flash_read(mBaseAddress + ENTRY_TABLE_OFFSET, mEntryTable.data(), - static_cast(mEntryTable.byteSize())); + auto rc = spi_flash_read(mBaseAddress + ENTRY_TABLE_OFFSET, reinterpret_cast(mEntryTable.data()), + mEntryTable.byteSize()); if (rc != ESP_OK) { mState = PageState::INVALID; return rc; @@ -435,7 +435,7 @@ esp_err_t Page::mLoadEntryTable() while (mNextFreeEntry < ENTRY_COUNT) { uint32_t entryAddress = getEntryAddress(mNextFreeEntry); uint32_t header; - auto rc = spi_flash_read(entryAddress, &header, sizeof(header)); + auto rc = spi_flash_read(entryAddress, reinterpret_cast(&header), sizeof(header)); if (rc != ESP_OK) { mState = PageState::INVALID; return rc; @@ -559,7 +559,7 @@ esp_err_t Page::initialize() header.mSeqNumber = mSeqNumber; header.mCrc32 = header.calculateCrc32(); - auto rc = spi_flash_write(mBaseAddress, reinterpret_cast(&header), sizeof(header)); + auto rc = spi_flash_write(mBaseAddress, reinterpret_cast(&header), sizeof(header)); if (rc != ESP_OK) { mState = PageState::INVALID; return rc; @@ -577,7 +577,8 @@ esp_err_t Page::alterEntryState(size_t index, EntryState state) mEntryTable.set(index, state); size_t wordToWrite = mEntryTable.getWordIndex(index); uint32_t word = mEntryTable.data()[wordToWrite]; - auto rc = spi_flash_write(mBaseAddress + ENTRY_TABLE_OFFSET + static_cast(wordToWrite) * 4, &word, 4); + auto rc = spi_flash_write(mBaseAddress + ENTRY_TABLE_OFFSET + static_cast(wordToWrite) * 4, + reinterpret_cast(&word), sizeof(word)); if (rc != ESP_OK) { mState = PageState::INVALID; return rc; @@ -600,7 +601,8 @@ esp_err_t Page::alterEntryRangeState(size_t begin, size_t end, EntryState state) } if (nextWordIndex != wordIndex) { uint32_t word = mEntryTable.data()[wordIndex]; - auto rc = spi_flash_write(mBaseAddress + ENTRY_TABLE_OFFSET + static_cast(wordIndex) * 4, &word, 4); + auto rc = spi_flash_write(mBaseAddress + ENTRY_TABLE_OFFSET + static_cast(wordIndex) * 4, + reinterpret_cast(&word), 4); if (rc != ESP_OK) { return rc; } @@ -612,7 +614,8 @@ esp_err_t Page::alterEntryRangeState(size_t begin, size_t end, EntryState state) esp_err_t Page::alterPageState(PageState state) { - auto rc = spi_flash_write(mBaseAddress, reinterpret_cast(&state), sizeof(state)); + uint32_t state_val = static_cast(state); + auto rc = spi_flash_write(mBaseAddress, reinterpret_cast(&state_val), sizeof(state)); if (rc != ESP_OK) { mState = PageState::INVALID; return rc; @@ -623,7 +626,7 @@ esp_err_t Page::alterPageState(PageState state) esp_err_t Page::readEntry(size_t index, Item& dst) const { - auto rc = spi_flash_read(getEntryAddress(index), reinterpret_cast(&dst), sizeof(dst)); + auto rc = spi_flash_read(getEntryAddress(index), reinterpret_cast(&dst), sizeof(dst)); if (rc != ESP_OK) { return rc; } diff --git a/components/nvs_flash/test/spi_flash_emulation.cpp b/components/nvs_flash/test/spi_flash_emulation.cpp index 5185bd34c..bd1482268 100644 --- a/components/nvs_flash/test/spi_flash_emulation.cpp +++ b/components/nvs_flash/test/spi_flash_emulation.cpp @@ -22,7 +22,7 @@ void spi_flash_emulator_set(SpiFlashEmulator* e) s_emulator = e; } -esp_err_t spi_flash_erase_sector(uint16_t sec) +esp_err_t spi_flash_erase_sector(size_t sec) { if (!s_emulator) { return ESP_ERR_FLASH_OP_TIMEOUT; @@ -35,26 +35,26 @@ esp_err_t spi_flash_erase_sector(uint16_t sec) return ESP_OK; } -esp_err_t spi_flash_write(uint32_t des_addr, const uint32_t *src_addr, uint32_t size) +esp_err_t spi_flash_write(size_t des_addr, const uint8_t *src_addr, size_t size) { if (!s_emulator) { return ESP_ERR_FLASH_OP_TIMEOUT; } - if (!s_emulator->write(des_addr, src_addr, size)) { + if (!s_emulator->write(des_addr, reinterpret_cast(src_addr), size)) { return ESP_ERR_FLASH_OP_FAIL; } return ESP_OK; } -esp_err_t spi_flash_read(uint32_t src_addr, uint32_t *des_addr, uint32_t size) +esp_err_t spi_flash_read(size_t src_addr, uint8_t *des_addr, size_t size) { if (!s_emulator) { return ESP_ERR_FLASH_OP_TIMEOUT; } - if (!s_emulator->read(des_addr, src_addr, size)) { + if (!s_emulator->read(reinterpret_cast(des_addr), src_addr, size)) { return ESP_ERR_FLASH_OP_FAIL; } diff --git a/components/nvs_flash/test/spi_flash_emulation.h b/components/nvs_flash/test/spi_flash_emulation.h index d5a242b24..ba50c4f9e 100644 --- a/components/nvs_flash/test/spi_flash_emulation.h +++ b/components/nvs_flash/test/spi_flash_emulation.h @@ -44,7 +44,7 @@ public: spi_flash_emulator_set(nullptr); } - bool read(uint32_t* dest, uint32_t srcAddr, size_t size) const + bool read(uint32_t* dest, size_t srcAddr, size_t size) const { if (srcAddr % 4 != 0 || size % 4 != 0 || @@ -60,7 +60,7 @@ public: return true; } - bool write(uint32_t dstAddr, const uint32_t* src, size_t size) + bool write(size_t dstAddr, const uint32_t* src, size_t size) { uint32_t sectorNumber = dstAddr/SPI_FLASH_SEC_SIZE; if (sectorNumber < mLowerSectorBound || sectorNumber >= mUpperSectorBound) { @@ -96,7 +96,7 @@ public: return true; } - bool erase(uint32_t sectorNumber) + bool erase(size_t sectorNumber) { size_t offset = sectorNumber * SPI_FLASH_SEC_SIZE / 4; if (offset > mData.size()) { diff --git a/components/nvs_flash/test/test_spi_flash_emulation.cpp b/components/nvs_flash/test/test_spi_flash_emulation.cpp index ea233da61..329e721ce 100644 --- a/components/nvs_flash/test/test_spi_flash_emulation.cpp +++ b/components/nvs_flash/test/test_spi_flash_emulation.cpp @@ -30,7 +30,7 @@ TEST_CASE("flash starts with all bytes == 0xff", "[spi_flash_emu]") uint8_t sector[SPI_FLASH_SEC_SIZE]; for (int i = 0; i < 4; ++i) { - CHECK(spi_flash_read(0, reinterpret_cast(sector), sizeof(sector)) == ESP_OK); + CHECK(spi_flash_read(0, sector, sizeof(sector)) == ESP_OK); for (auto v: sector) { CHECK(v == 0xff); } @@ -42,9 +42,9 @@ TEST_CASE("invalid writes are checked", "[spi_flash_emu]") SpiFlashEmulator emu(1); uint32_t val = 0; - CHECK(spi_flash_write(0, &val, 4) == ESP_OK); + CHECK(spi_flash_write(0, reinterpret_cast(&val), 4) == ESP_OK); val = 1; - CHECK(spi_flash_write(0, &val, 4) == ESP_ERR_FLASH_OP_FAIL); + CHECK(spi_flash_write(0, reinterpret_cast(&val), 4) == ESP_ERR_FLASH_OP_FAIL); } @@ -53,11 +53,11 @@ TEST_CASE("out of bounds writes fail", "[spi_flash_emu]") SpiFlashEmulator emu(4); uint32_t vals[8]; std::fill_n(vals, 8, 0); - CHECK(spi_flash_write(0, vals, sizeof(vals)) == ESP_OK); + CHECK(spi_flash_write(0, reinterpret_cast(vals), sizeof(vals)) == ESP_OK); - CHECK(spi_flash_write(4*4096 - sizeof(vals), vals, sizeof(vals)) == ESP_OK); + CHECK(spi_flash_write(4*4096 - sizeof(vals), reinterpret_cast(vals), sizeof(vals)) == ESP_OK); - CHECK(spi_flash_write(4*4096 - sizeof(vals) + 4, vals, sizeof(vals)) == ESP_ERR_FLASH_OP_FAIL); + CHECK(spi_flash_write(4*4096 - sizeof(vals) + 4, reinterpret_cast(vals), sizeof(vals)) == ESP_ERR_FLASH_OP_FAIL); } @@ -65,9 +65,9 @@ TEST_CASE("after erase the sector is set to 0xff", "[spi_flash_emu]") { SpiFlashEmulator emu(4); uint32_t val1 = 0xab00cd12; - CHECK(spi_flash_write(0, &val1, sizeof(val1)) == ESP_OK); + CHECK(spi_flash_write(0, reinterpret_cast(&val1), sizeof(val1)) == ESP_OK); uint32_t val2 = 0x5678efab; - CHECK(spi_flash_write(4096 - 4, &val2, sizeof(val2)) == ESP_OK); + CHECK(spi_flash_write(4096 - 4, reinterpret_cast(&val2), sizeof(val2)) == ESP_OK); CHECK(emu.words()[0] == val1); CHECK(range_empty_n(emu.words() + 1, 4096 / 4 - 2)); @@ -83,7 +83,7 @@ TEST_CASE("after erase the sector is set to 0xff", "[spi_flash_emu]") TEST_CASE("read/write/erase operation times are calculated correctly", "[spi_flash_emu]") { SpiFlashEmulator emu(1); - uint32_t data[128]; + uint8_t data[512]; spi_flash_read(0, data, 4); CHECK(emu.getTotalTime() == 7); CHECK(emu.getReadOps() == 1); @@ -141,7 +141,7 @@ TEST_CASE("read/write/erase operation times are calculated correctly", "[spi_fla CHECK(emu.getTotalTime() == 37142); } -TEST_CASE("data is randomized predicatbly", "[spi_flash_emu]") +TEST_CASE("data is randomized predictably", "[spi_flash_emu]") { SpiFlashEmulator emu1(3); emu1.randomize(0x12345678); diff --git a/components/spi_flash/flash_ops.c b/components/spi_flash/flash_ops.c index 99e8ef77f..512f6d20d 100644 --- a/components/spi_flash/flash_ops.c +++ b/components/spi_flash/flash_ops.c @@ -64,6 +64,11 @@ void spi_flash_init() #endif } +size_t spi_flash_get_chip_size() +{ + return g_rom_flashchip.chip_size; +} + SpiFlashOpResult IRAM_ATTR spi_flash_unlock() { static bool unlocked = false; @@ -77,28 +82,74 @@ SpiFlashOpResult IRAM_ATTR spi_flash_unlock() return SPI_FLASH_RESULT_OK; } -esp_err_t IRAM_ATTR spi_flash_erase_sector(uint16_t sec) +esp_err_t IRAM_ATTR spi_flash_erase_sector(size_t sec) { + return spi_flash_erase_range(sec * SPI_FLASH_SEC_SIZE, SPI_FLASH_SEC_SIZE); +} + +esp_err_t IRAM_ATTR spi_flash_erase_range(uint32_t start_addr, uint32_t size) +{ + if (start_addr % SPI_FLASH_SEC_SIZE != 0) { + return ESP_ERR_INVALID_ARG; + } + if (size % SPI_FLASH_SEC_SIZE != 0) { + return ESP_ERR_INVALID_SIZE; + } + if (size + start_addr > spi_flash_get_chip_size()) { + return ESP_ERR_INVALID_SIZE; + } + size_t start = start_addr / SPI_FLASH_SEC_SIZE; + size_t end = start + size / SPI_FLASH_SEC_SIZE; + const size_t sectors_per_block = 16; COUNTER_START(); spi_flash_disable_interrupts_caches_and_other_cpu(); SpiFlashOpResult rc; rc = spi_flash_unlock(); if (rc == SPI_FLASH_RESULT_OK) { - rc = SPIEraseSector(sec); + for (size_t sector = start; sector != end && rc == SPI_FLASH_RESULT_OK; ) { + if (sector % sectors_per_block == 0 && end - sector > sectors_per_block) { + rc = SPIEraseBlock(sector / sectors_per_block); + sector += sectors_per_block; + COUNTER_ADD_BYTES(erase, sectors_per_block * SPI_FLASH_SEC_SIZE); + } + else { + rc = SPIEraseSector(sector); + ++sector; + COUNTER_ADD_BYTES(erase, SPI_FLASH_SEC_SIZE); + } + } } spi_flash_enable_interrupts_caches_and_other_cpu(); COUNTER_STOP(erase); return spi_flash_translate_rc(rc); } -esp_err_t IRAM_ATTR spi_flash_write(uint32_t dest_addr, const uint32_t *src, uint32_t size) +esp_err_t IRAM_ATTR spi_flash_write(size_t dest_addr, const uint8_t *src, size_t size) { + // TODO: replace this check with code which deals with unaligned sources + if (((ptrdiff_t) src) % 4 != 0) { + return ESP_ERR_INVALID_ARG; + } + // Destination alignment is also checked in ROM code, but we can give + // better error code here + // TODO: add handling of unaligned destinations + if (dest_addr % 4 != 0) { + return ESP_ERR_INVALID_ARG; + } + if (size % 4 != 0) { + return ESP_ERR_INVALID_SIZE; + } + // Out of bound writes are checked in ROM code, but we can give better + // error code here + if (dest_addr + size > g_rom_flashchip.chip_size) { + return ESP_ERR_INVALID_SIZE; + } COUNTER_START(); spi_flash_disable_interrupts_caches_and_other_cpu(); SpiFlashOpResult rc; rc = spi_flash_unlock(); if (rc == SPI_FLASH_RESULT_OK) { - rc = SPIWrite(dest_addr, src, (int32_t) size); + rc = SPIWrite((uint32_t) dest_addr, (const uint32_t*) src, (int32_t) size); COUNTER_ADD_BYTES(write, size); } spi_flash_enable_interrupts_caches_and_other_cpu(); @@ -106,11 +157,29 @@ esp_err_t IRAM_ATTR spi_flash_write(uint32_t dest_addr, const uint32_t *src, uin return spi_flash_translate_rc(rc); } -esp_err_t IRAM_ATTR spi_flash_read(uint32_t src_addr, uint32_t *dest, uint32_t size) +esp_err_t IRAM_ATTR spi_flash_read(size_t src_addr, uint8_t *dest, size_t size) { + // TODO: replace this check with code which deals with unaligned destinations + if (((ptrdiff_t) dest) % 4 != 0) { + return ESP_ERR_INVALID_ARG; + } + // Source alignment is also checked in ROM code, but we can give + // better error code here + // TODO: add handling of unaligned destinations + if (src_addr % 4 != 0) { + return ESP_ERR_INVALID_ARG; + } + if (size % 4 != 0) { + return ESP_ERR_INVALID_SIZE; + } + // Out of bound reads are checked in ROM code, but we can give better + // error code here + if (src_addr + size > g_rom_flashchip.chip_size) { + return ESP_ERR_INVALID_SIZE; + } COUNTER_START(); spi_flash_disable_interrupts_caches_and_other_cpu(); - SpiFlashOpResult rc = SPIRead(src_addr, dest, (int32_t) size); + SpiFlashOpResult rc = SPIRead((uint32_t) src_addr, (uint32_t*) dest, (int32_t) size); COUNTER_ADD_BYTES(read, size); spi_flash_enable_interrupts_caches_and_other_cpu(); COUNTER_STOP(read); diff --git a/components/spi_flash/include/esp_spi_flash.h b/components/spi_flash/include/esp_spi_flash.h index 597e41857..ab66a4204 100644 --- a/components/spi_flash/include/esp_spi_flash.h +++ b/components/spi_flash/include/esp_spi_flash.h @@ -38,37 +38,63 @@ extern "C" { */ void spi_flash_init(); +/** + * @brief Get flash chip size, as set in binary image header + * + * @note This value does not necessarily match real flash size. + * + * @return size of flash chip, in bytes + */ +size_t spi_flash_get_chip_size(); + /** * @brief Erase the Flash sector. * - * @param uint16 sec : Sector number, the count starts at sector 0, 4KB per sector. + * @param sector Sector number, the count starts at sector 0, 4KB per sector. * * @return esp_err_t */ -esp_err_t spi_flash_erase_sector(uint16_t sec); +esp_err_t spi_flash_erase_sector(size_t sector); + +/** + * @brief Erase a range of flash sectors + * + * @param uint32_t start_address : Address where erase operation has to start. + * Must be 4kB-aligned + * @param uint32_t size : Size of erased range, in bytes. Must be divisible by 4kB. + * + * @return esp_err_t + */ +esp_err_t spi_flash_erase_range(size_t start_addr, size_t size); + /** * @brief Write data to Flash. * - * @param uint32 des_addr : destination address in Flash. - * @param uint32 *src_addr : source address of the data. - * @param uint32 size : length of data + * @note Both des_addr and src_addr have to be 4-byte aligned. + * This is a temporary limitation which will be removed. + * + * @param des_addr destination address in Flash + * @param src_addr source address of the data + * @param size length of data, in bytes * * @return esp_err_t */ -esp_err_t spi_flash_write(uint32_t des_addr, const uint32_t *src_addr, uint32_t size); +esp_err_t spi_flash_write(size_t des_addr, const uint8_t *src_addr, size_t size); /** * @brief Read data from Flash. * - * @param uint32 src_addr : source address of the data in Flash. - * @param uint32 *des_addr : destination address. - * @param uint32 size : length of data + * @note Both des_addr and src_addr have to be 4-byte aligned. + * This is a temporary limitation which will be removed. + * + * @param src_addr source address of the data in Flash. + * @param des_addr destination address + * @param size length of data * * @return esp_err_t */ -esp_err_t spi_flash_read(uint32_t src_addr, uint32_t *des_addr, uint32_t size); - +esp_err_t spi_flash_read(size_t src_addr, uint8_t *des_addr, size_t size); /** * @brief Enumeration which specifies memory space requested in an mmap call @@ -135,7 +161,7 @@ void spi_flash_mmap_dump(); typedef struct { uint32_t count; // number of times operation was executed uint32_t time; // total time taken, in microseconds - uint32_t bytes; // total number of bytes, for read and write operations + uint32_t bytes; // total number of bytes } spi_flash_counter_t; typedef struct { From b6693225c188ab627a31bbe8e881bfa183f21c22 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 21 Oct 2016 18:44:57 +0800 Subject: [PATCH 08/13] spi_flash: implement partition API, drop trivial wrappers This implements esp_partition_read, esp_partition_write, esp_partition_erase_range, esp_partition_mmap. Also removed getters which didn't add much sugar after all. --- components/spi_flash/include/esp_partition.h | 213 +++++++++---------- components/spi_flash/include/esp_spi_flash.h | 1 + components/spi_flash/partition.c | 121 ++++++++--- 3 files changed, 186 insertions(+), 149 deletions(-) diff --git a/components/spi_flash/include/esp_partition.h b/components/spi_flash/include/esp_partition.h index 6bdba149f..89e523f9a 100644 --- a/components/spi_flash/include/esp_partition.h +++ b/components/spi_flash/include/esp_partition.h @@ -17,7 +17,9 @@ #include #include +#include #include "esp_err.h" +#include "esp_spi_flash.h" #ifdef __cplusplus extern "C" { @@ -128,123 +130,6 @@ const esp_partition_t* esp_partition_get(esp_partition_iterator_t iterator); */ 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. - */ -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 - */ -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 - */ -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 for the lifetime of the application. - */ -const char* esp_partition_label(esp_partition_iterator_t iterator); - -/** - * @brief Read data from the partition - * - * @param iterator Iterator obtained using esp_partition_find. Must be non-NULL. - * @param src_offset Address of the data to be read, relative to the - * beginning of the partition. - * @param dst Pointer to the buffer where data should be stored. - * Must be non-NULL and at least 'size' bytes long. - * @param size Size of data to be read, in bytes. - * - * @return ESP_OK, if data was read successfully; - * 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, - uint32_t src_offset, uint8_t* dst, uint32_t size); - -/** - * @brief Write data to the partition - * - * @param iterator Iterator obtained using esp_partition_find. Must be non-NULL. - * @param src Pointer to the source buffer. Must be non-NULL and - * at least 'size' bytes long. - * @param dst_offset Address where the data should be written, relative to the - * beginning of the partition. - * @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 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, - const uint8_t* src, uint32_t dst_offset, uint32_t size); - -/** - * @brief Erase part of the partition - * - * @param iterator Iterator obtained using esp_partition_find. Must be non-NULL. - * @param start_addr Address where erase operation should start. Must be aligned - * to 4 kilobytes. - * @param size Size of the range which should be erased, in bytes. - * Must be divisible by 4 kilobytes. - * - * @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(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 * @@ -256,6 +141,100 @@ void* esp_partition_mmap(esp_partition_iterator_t iterator, uint32_t offset, uin */ void esp_partition_iterator_release(esp_partition_iterator_t iterator); +/** + * @brief Read data from the partition + * + * @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 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(const esp_partition_t* partition, + size_t src_offset, uint8_t* 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. + * + * @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 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(const esp_partition_t* partition, + size_t dst_offset, const uint8_t* 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 start_addr Address where erase operation should start. Must be aligned + * to 4 kilobytes. + * @param size Size of the range which should be erased, in bytes. + * Must be divisible by 4 kilobytes. + * + * @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, + uint32_t start_addr, uint32_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 + * spi_flash_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 spi_flash_munmap call + * + * @return ESP_OK, if successful + */ +esp_err_t esp_partition_mmap(const esp_partition_t* partition, uint32_t offset, uint32_t size, + spi_flash_mmap_memory_t memory, + const void** out_ptr, spi_flash_mmap_handle_t* out_handle); + #ifdef __cplusplus } diff --git a/components/spi_flash/include/esp_spi_flash.h b/components/spi_flash/include/esp_spi_flash.h index ab66a4204..2aa92d9a5 100644 --- a/components/spi_flash/include/esp_spi_flash.h +++ b/components/spi_flash/include/esp_spi_flash.h @@ -16,6 +16,7 @@ #define ESP_SPI_FLASH_H #include +#include #include "esp_err.h" #include "sdkconfig.h" diff --git a/components/spi_flash/partition.c b/components/spi_flash/partition.c index 381a1624b..d138f09b5 100644 --- a/components/spi_flash/partition.c +++ b/components/spi_flash/partition.c @@ -135,38 +135,6 @@ const esp_partition_t* esp_partition_find_first(esp_partition_type_t type, const 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 = @@ -219,3 +187,92 @@ static esp_err_t load_partitions() spi_flash_munmap(handle); return ESP_OK; } + +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_err_t esp_partition_read(const esp_partition_t* partition, + size_t src_offset, uint8_t* 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 spi_flash_read(partition->address + src_offset, dst, size); +} + +esp_err_t esp_partition_write(const esp_partition_t* partition, + size_t dst_offset, const uint8_t* 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; + } + return spi_flash_write(partition->address + dst_offset, src, size); +} + +esp_err_t esp_partition_erase_range(const esp_partition_t* partition, + size_t start_addr, size_t size) +{ + assert(partition != NULL); + if (start_addr > partition->size) { + return ESP_ERR_INVALID_ARG; + } + if (start_addr + size > partition->size) { + return ESP_ERR_INVALID_SIZE; + } + if (size % SPI_FLASH_SEC_SIZE != 0) { + return ESP_ERR_INVALID_SIZE; + } + if (start_addr % SPI_FLASH_SEC_SIZE != 0) { + return ESP_ERR_INVALID_ARG; + } + return spi_flash_erase_range(partition->address + start_addr, 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, uint32_t offset, uint32_t size, + spi_flash_mmap_memory_t memory, + const void** out_ptr, spi_flash_mmap_handle_t* out_handle) +{ + assert(partition != NULL); + if (offset > partition->size) { + return ESP_ERR_INVALID_ARG; + } + if (offset + size > partition->size) { + return ESP_ERR_INVALID_SIZE; + } + size_t phys_addr = partition->address + offset; + // offset within 64kB block + size_t region_offset = phys_addr & 0xffff; + size_t mmap_addr = phys_addr & 0xffff0000; + esp_err_t rc = spi_flash_mmap(mmap_addr, size, memory, out_ptr, out_handle); + // adjust returned pointer to point to the correct offset + if (rc == ESP_OK) { + *out_ptr = (void*) (((ptrdiff_t) *out_ptr) + region_offset); + } + return rc; +} From e229ec0340ba2a921223eec51169200cc10c8329 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 21 Oct 2016 19:33:42 +0800 Subject: [PATCH 09/13] spi_flash: check physical address in mmap against flash chip size --- components/spi_flash/flash_mmap.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/components/spi_flash/flash_mmap.c b/components/spi_flash/flash_mmap.c index 8636a2605..2165a784d 100644 --- a/components/spi_flash/flash_mmap.c +++ b/components/spi_flash/flash_mmap.c @@ -88,6 +88,9 @@ esp_err_t IRAM_ATTR spi_flash_mmap(uint32_t src_addr, size_t size, spi_flash_mma if (src_addr & 0xffff) { return ESP_ERR_INVALID_ARG; } + if (src_addr + size > g_rom_flashchip.chip_size) { + return ESP_ERR_INVALID_ARG; + } spi_flash_disable_interrupts_caches_and_other_cpu(); if (s_mmap_page_refcnt[0] == 0) { spi_flash_mmap_init(); From e03b45eb0a9eb07eb1e78c7869af18a7faa386d5 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Tue, 25 Oct 2016 11:43:00 +0800 Subject: [PATCH 10/13] spi_flash: improve documentation --- .../esp32/include/esp_flash_data_types.h | 38 ++--- components/spi_flash/README.rst | 133 +++++++++++++++++- components/spi_flash/include/esp_spi_flash.h | 2 + 3 files changed, 152 insertions(+), 21 deletions(-) diff --git a/components/esp32/include/esp_flash_data_types.h b/components/esp32/include/esp_flash_data_types.h index b16ee59f5..4bf886c84 100644 --- a/components/esp32/include/esp_flash_data_types.h +++ b/components/esp32/include/esp_flash_data_types.h @@ -24,7 +24,7 @@ extern "C" #define ESP_PARTITION_TABLE_ADDR 0x4000 #define ESP_PARTITION_MAGIC 0x50AA -/*spi mode,saved in third byte in flash */ +/* SPI flash mode, used in esp_image_header_t */ typedef enum { ESP_IMAGE_SPI_MODE_QIO, ESP_IMAGE_SPI_MODE_QOUT, @@ -34,7 +34,7 @@ typedef enum { ESP_IMAGE_SPI_MODE_SLOW_READ } esp_image_spi_mode_t; -/* spi speed*/ +/* SPI flash clock frequency */ enum { ESP_IMAGE_SPI_SPEED_40M, ESP_IMAGE_SPI_SPEED_26M, @@ -42,7 +42,7 @@ enum { ESP_IMAGE_SPI_SPEED_80M = 0xF } esp_image_spi_freq_t; -/*supported flash sizes*/ +/* Supported SPI flash sizes */ typedef enum { ESP_IMAGE_FLASH_SIZE_1MB = 0, ESP_IMAGE_FLASH_SIZE_2MB, @@ -52,22 +52,23 @@ typedef enum { ESP_IMAGE_FLASH_SIZE_MAX } esp_image_flash_size_t; +/* Main header of binary image */ typedef struct { - char magic; - char blocks; - char spi_mode; /* flag of flash read mode in unpackage and usage in future */ - char spi_speed: 4; /* low bit */ - char spi_size: 4; - unsigned int entry_addr; + uint8_t magic; + uint8_t blocks; + uint8_t spi_mode; /* flash read mode (esp_image_spi_mode_t as uint8_t) */ + uint8_t spi_speed: 4; /* flash frequency (esp_image_spi_freq_t as uint8_t) */ + uint8_t spi_size: 4; /* flash chip size (esp_image_flash_size_t as uint8_t) */ + uint32_t entry_addr; uint8_t encrypt_flag; /* encrypt flag */ uint8_t secure_boot_flag; /* secure boot flag */ - char extra_header[14]; /* ESP32 additional header, unused by second bootloader */ + uint8_t extra_header[14]; /* ESP32 additional header, unused by second bootloader */ } esp_image_header_t; -/* each header of flash bin block */ +/* Header of binary image segment */ typedef struct { - unsigned int load_addr; - unsigned int data_len; + uint32_t load_addr; + uint32_t data_len; } esp_image_section_header_t; @@ -85,13 +86,16 @@ typedef struct { uint32_t size; } esp_partition_pos_t; +/* Structure which describes the layout of partition table entry. + * See docs/partition_tables.rst for more information about individual fields. + */ typedef struct { uint16_t magic; - uint8_t type; /* partition Type */ - uint8_t subtype; /* part_subtype */ + uint8_t type; + uint8_t subtype; esp_partition_pos_t pos; - uint8_t label[16]; /* label for the partition */ - uint8_t reserved[4]; /* reserved */ + uint8_t label[16]; + uint8_t reserved[4]; } esp_partition_info_t; diff --git a/components/spi_flash/README.rst b/components/spi_flash/README.rst index 22f98cf02..b479c3b0e 100644 --- a/components/spi_flash/README.rst +++ b/components/spi_flash/README.rst @@ -1,5 +1,128 @@ -Driver for SPI flash read/write/erase operations -================================================ +SPI flash related APIs +====================== + +Overview +-------- +Spi_flash component contains APIs related to reading, writing, erasing, +memory mapping data in the external SPI flash. It also has higher-level +APIs which work with partition table and partitions. + +Note that all the functionality is limited to the "main" flash chip, +i.e. the flash chip from which program runs. For ``spi_flash_*`` functions, +this is software limitation. Underlying ROM functions which work with SPI flash +do not have provisions for working with flash chips attached to SPI peripherals +other than SPI0. + +SPI flash access APIs +--------------------- + +This is the set of APIs for working with data in flash: + +- ``spi_flash_read`` used to read data from flash to RAM +- ``spi_flash_write`` used to write data from RAM to flash +- ``spi_flash_erase_sector`` used to erase individual sectors of flash +- ``spi_flash_erase_range`` used to erase range of addresses in flash +- ``spi_flash_get_chip_size`` returns flash chip size, in bytes, as configured in menuconfig + +There are some data alignment limitations which need to be considered when using +spi_flash_read/spi_flash_write functions: + +- buffer in RAM must be 4-byte aligned +- size must be 4-byte aligned +- address in flash must be 4-byte aligned + +These alignment limitations are purely software, and should be removed in future +versions. + +It is assumed that correct SPI flash chip size is set at compile time using +menuconfig. While run-time detection of SPI flash chip size is possible, it is +not implemented yet. Applications which need this (e.g. to provide one firmware +binary for different flash sizes) can do flash chip size detection and set +the correct flash chip size in ``chip_size`` member of ``g_rom_flashchip`` +structure. This size is used by ``spi_flash_*`` functions for bounds checking. + +SPI flash APIs disable instruction and data caches while reading/writing/erasing. +See implementation notes below on details how this happens. For application +this means that at some periods of time, code can not be run from flash, +and constant data can not be fetched from flash by the CPU. This is not an +issue for normal code which runs in a task, because SPI flash APIs prevent +other tasks from running while caches are disabled. This is an issue for +interrupt handlers, which can still be called while flash operation is in +progress. If the interrupt handler is not placed into IRAM, there is a +possibility that interrupt will happen at the time when caches are disabled, +which will cause an illegal instruction exception. + +To prevent this, make sure that all ISR code, and all functions called from ISR +code are placed into IRAM, or are located in ROM. Most useful C library +functions are located in ROM, so they can be called from ISR. + +To place a function into IRAM, use ``IRAM_ATTR`` attribute, e.g.:: + + #include "esp_attr.h" + + void IRAM_ATTR gpio_isr_handler(void* arg) + { + // ... + } + +When flash encryption is enabled, ``spi_flash_read`` will read data as it is +stored in flash (without decryption), and ``spi_flash_write`` will write data +in plain text. In other words, ``spi_flash_read/write`` APIs don't have +provisions to deal with encrypted data. + + +Partition table APIs +-------------------- + +ESP-IDF uses partition table to maintain information about various regions of +SPI flash memory (bootloader, various application binaries, data, filesystems). +More information about partition tables can be found in docs/partition_tables.rst. + +This component provides APIs to enumerate partitions found in the partition table +and perform operations on them. These functions are declared in ``esp_partition.h``: + +- ``esp_partition_find`` used to search partition table for entries with specific type, returns an opaque iterator +- ``esp_partition_get`` returns a structure describing the partition, for the given iterator +- ``esp_partition_next`` advances iterator to the next partition found +- ``esp_partition_iterator_release`` releases iterator returned by ``esp_partition_find`` +- ``esp_partition_find_first`` is a convenience function which returns structure describing the first partition found by esp_partition_find +- ``esp_partition_read``, ``esp_partition_write``, ``esp_partition_erase_range`` are equivalent to ``spi_flash_read``, ``spi_flash_write``, ``spi_flash_erase_range``, but operate within partition boundaries + +Most application code should use ``esp_partition_*`` APIs instead of lower level +``spi_flash_*`` APIs. Partition APIs do bounds checking and calculate correct +offsets in flash based on data stored in partition table. + +Memory mapping APIs +------------------- + +ESP32 features memory hardware which allows regions of flash memory to be mapped +into instruction and data address spaces. This mapping works only for read operations, +it is not possible to modify contents of flash memory by writing to mapped memory +region. Mapping happens in 64KB pages. Memory mapping hardware can map up to +4 megabytes of flash into data address space, and up to 16 megabytes of flash into +instruction address space. See the technical reference manual for more details +about memory mapping hardware. + +Note that some number of 64KB pages is used to map the application +itself into memory, so the actual number of available 64KB pages may be less. + +Reading data from flash using a memory mapped region is the only way to decrypt +contents of flash when flash encryption is enabled. Decryption is performed at +hardware level. + +Memory mapping APIs are declared in ``esp_spi_flash.h`` and ``esp_partition.h``: + +- ``spi_flash_mmap`` maps a region of physical flash addresses into instruction space or data space of the CPU +- ``spi_flash_munmap`` unmaps previously mapped region +- ``esp_partition_mmap`` maps part of a partition into the instruction space or data space of the CPU + +Differences between ``spi_flash_mmap`` and ``esp_partition_mmap`` are as follows: + +- ``spi_flash_mmap`` must be given a 64KB aligned physical address +- ``esp_partition_mmap`` may be given an arbitrary offset within the partition, it will adjust returned pointer to mapped memory as necessary + +Note that because memory mapping happens in 64KB blocks, it may be possible to +read data outside of the partition provided to ``esp_partition_mmap``. Implementation notes -------------------- @@ -19,8 +142,10 @@ signals that cache is disabled by setting s_flash_op_can_start flag. Then the task on CPU A disables cache as well, and proceeds to execute flash operation. -While flash operation is running, interrupts can still run on CPU B. -We assume that all interrupt code is placed into RAM. +While flash operation is running, interrupts can still run on CPUs A and B. +We assume that all interrupt code is placed into RAM. Once interrupt allocation +API is added, we should add a flag to request interrupt to be disabled for +the duration of flash operations. Once flash operation is complete, function on CPU A sets another flag, s_flash_op_complete, to let the task on CPU B know that it can re-enable diff --git a/components/spi_flash/include/esp_spi_flash.h b/components/spi_flash/include/esp_spi_flash.h index 2aa92d9a5..16b50a718 100644 --- a/components/spi_flash/include/esp_spi_flash.h +++ b/components/spi_flash/include/esp_spi_flash.h @@ -35,6 +35,8 @@ extern "C" { * * This function must be called exactly once, before any other * spi_flash_* functions are called. + * Currently this function is called from startup code. There is + * no need to call it from application code. * */ void spi_flash_init(); From 9f0f05d5201aeed9390f94b50f5cf65403040729 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Thu, 27 Oct 2016 00:46:33 +0800 Subject: [PATCH 11/13] spi_flash: change pointer type to void* --- components/nvs_flash/src/nvs_page.cpp | 20 +++++++++---------- .../nvs_flash/test/spi_flash_emulation.cpp | 4 ++-- .../test/test_spi_flash_emulation.cpp | 14 ++++++------- components/spi_flash/flash_ops.c | 4 ++-- components/spi_flash/include/esp_partition.h | 4 ++-- components/spi_flash/include/esp_spi_flash.h | 4 ++-- 6 files changed, 25 insertions(+), 25 deletions(-) diff --git a/components/nvs_flash/src/nvs_page.cpp b/components/nvs_flash/src/nvs_page.cpp index 9ba478e21..a5e058758 100644 --- a/components/nvs_flash/src/nvs_page.cpp +++ b/components/nvs_flash/src/nvs_page.cpp @@ -37,7 +37,7 @@ esp_err_t Page::load(uint32_t sectorNumber) mErasedEntryCount = 0; Header header; - auto rc = spi_flash_read(mBaseAddress, reinterpret_cast(&header), sizeof(header)); + auto rc = spi_flash_read(mBaseAddress, &header, sizeof(header)); if (rc != ESP_OK) { mState = PageState::INVALID; return rc; @@ -48,7 +48,7 @@ esp_err_t Page::load(uint32_t sectorNumber) // reading the whole page takes ~40 times less than erasing it uint32_t line[8]; for (uint32_t i = 0; i < SPI_FLASH_SEC_SIZE; i += sizeof(line)) { - rc = spi_flash_read(mBaseAddress + i, reinterpret_cast(line), sizeof(line)); + rc = spi_flash_read(mBaseAddress + i, line, sizeof(line)); if (rc != ESP_OK) { mState = PageState::INVALID; return rc; @@ -86,7 +86,7 @@ esp_err_t Page::load(uint32_t sectorNumber) esp_err_t Page::writeEntry(const Item& item) { - auto rc = spi_flash_write(getEntryAddress(mNextFreeEntry), reinterpret_cast(&item), sizeof(item)); + auto rc = spi_flash_write(getEntryAddress(mNextFreeEntry), &item, sizeof(item)); if (rc != ESP_OK) { mState = PageState::INVALID; return rc; @@ -396,7 +396,7 @@ esp_err_t Page::mLoadEntryTable() if (mState == PageState::ACTIVE || mState == PageState::FULL || mState == PageState::FREEING) { - auto rc = spi_flash_read(mBaseAddress + ENTRY_TABLE_OFFSET, reinterpret_cast(mEntryTable.data()), + auto rc = spi_flash_read(mBaseAddress + ENTRY_TABLE_OFFSET, mEntryTable.data(), mEntryTable.byteSize()); if (rc != ESP_OK) { mState = PageState::INVALID; @@ -435,7 +435,7 @@ esp_err_t Page::mLoadEntryTable() while (mNextFreeEntry < ENTRY_COUNT) { uint32_t entryAddress = getEntryAddress(mNextFreeEntry); uint32_t header; - auto rc = spi_flash_read(entryAddress, reinterpret_cast(&header), sizeof(header)); + auto rc = spi_flash_read(entryAddress, &header, sizeof(header)); if (rc != ESP_OK) { mState = PageState::INVALID; return rc; @@ -559,7 +559,7 @@ esp_err_t Page::initialize() header.mSeqNumber = mSeqNumber; header.mCrc32 = header.calculateCrc32(); - auto rc = spi_flash_write(mBaseAddress, reinterpret_cast(&header), sizeof(header)); + auto rc = spi_flash_write(mBaseAddress, &header, sizeof(header)); if (rc != ESP_OK) { mState = PageState::INVALID; return rc; @@ -578,7 +578,7 @@ esp_err_t Page::alterEntryState(size_t index, EntryState state) size_t wordToWrite = mEntryTable.getWordIndex(index); uint32_t word = mEntryTable.data()[wordToWrite]; auto rc = spi_flash_write(mBaseAddress + ENTRY_TABLE_OFFSET + static_cast(wordToWrite) * 4, - reinterpret_cast(&word), sizeof(word)); + &word, sizeof(word)); if (rc != ESP_OK) { mState = PageState::INVALID; return rc; @@ -602,7 +602,7 @@ esp_err_t Page::alterEntryRangeState(size_t begin, size_t end, EntryState state) if (nextWordIndex != wordIndex) { uint32_t word = mEntryTable.data()[wordIndex]; auto rc = spi_flash_write(mBaseAddress + ENTRY_TABLE_OFFSET + static_cast(wordIndex) * 4, - reinterpret_cast(&word), 4); + &word, 4); if (rc != ESP_OK) { return rc; } @@ -615,7 +615,7 @@ esp_err_t Page::alterEntryRangeState(size_t begin, size_t end, EntryState state) esp_err_t Page::alterPageState(PageState state) { uint32_t state_val = static_cast(state); - auto rc = spi_flash_write(mBaseAddress, reinterpret_cast(&state_val), sizeof(state)); + auto rc = spi_flash_write(mBaseAddress, &state_val, sizeof(state)); if (rc != ESP_OK) { mState = PageState::INVALID; return rc; @@ -626,7 +626,7 @@ esp_err_t Page::alterPageState(PageState state) esp_err_t Page::readEntry(size_t index, Item& dst) const { - auto rc = spi_flash_read(getEntryAddress(index), reinterpret_cast(&dst), sizeof(dst)); + auto rc = spi_flash_read(getEntryAddress(index), &dst, sizeof(dst)); if (rc != ESP_OK) { return rc; } diff --git a/components/nvs_flash/test/spi_flash_emulation.cpp b/components/nvs_flash/test/spi_flash_emulation.cpp index bd1482268..914efc145 100644 --- a/components/nvs_flash/test/spi_flash_emulation.cpp +++ b/components/nvs_flash/test/spi_flash_emulation.cpp @@ -35,7 +35,7 @@ esp_err_t spi_flash_erase_sector(size_t sec) return ESP_OK; } -esp_err_t spi_flash_write(size_t des_addr, const uint8_t *src_addr, size_t size) +esp_err_t spi_flash_write(size_t des_addr, const void *src_addr, size_t size) { if (!s_emulator) { return ESP_ERR_FLASH_OP_TIMEOUT; @@ -48,7 +48,7 @@ esp_err_t spi_flash_write(size_t des_addr, const uint8_t *src_addr, size_t size) return ESP_OK; } -esp_err_t spi_flash_read(size_t src_addr, uint8_t *des_addr, size_t size) +esp_err_t spi_flash_read(size_t src_addr, void *des_addr, size_t size) { if (!s_emulator) { return ESP_ERR_FLASH_OP_TIMEOUT; diff --git a/components/nvs_flash/test/test_spi_flash_emulation.cpp b/components/nvs_flash/test/test_spi_flash_emulation.cpp index 329e721ce..0c77aa966 100644 --- a/components/nvs_flash/test/test_spi_flash_emulation.cpp +++ b/components/nvs_flash/test/test_spi_flash_emulation.cpp @@ -42,9 +42,9 @@ TEST_CASE("invalid writes are checked", "[spi_flash_emu]") SpiFlashEmulator emu(1); uint32_t val = 0; - CHECK(spi_flash_write(0, reinterpret_cast(&val), 4) == ESP_OK); + CHECK(spi_flash_write(0, &val, 4) == ESP_OK); val = 1; - CHECK(spi_flash_write(0, reinterpret_cast(&val), 4) == ESP_ERR_FLASH_OP_FAIL); + CHECK(spi_flash_write(0, &val, 4) == ESP_ERR_FLASH_OP_FAIL); } @@ -53,11 +53,11 @@ TEST_CASE("out of bounds writes fail", "[spi_flash_emu]") SpiFlashEmulator emu(4); uint32_t vals[8]; std::fill_n(vals, 8, 0); - CHECK(spi_flash_write(0, reinterpret_cast(vals), sizeof(vals)) == ESP_OK); + CHECK(spi_flash_write(0, vals, sizeof(vals)) == ESP_OK); - CHECK(spi_flash_write(4*4096 - sizeof(vals), reinterpret_cast(vals), sizeof(vals)) == ESP_OK); + CHECK(spi_flash_write(4*4096 - sizeof(vals), vals, sizeof(vals)) == ESP_OK); - CHECK(spi_flash_write(4*4096 - sizeof(vals) + 4, reinterpret_cast(vals), sizeof(vals)) == ESP_ERR_FLASH_OP_FAIL); + CHECK(spi_flash_write(4*4096 - sizeof(vals) + 4, vals, sizeof(vals)) == ESP_ERR_FLASH_OP_FAIL); } @@ -65,9 +65,9 @@ TEST_CASE("after erase the sector is set to 0xff", "[spi_flash_emu]") { SpiFlashEmulator emu(4); uint32_t val1 = 0xab00cd12; - CHECK(spi_flash_write(0, reinterpret_cast(&val1), sizeof(val1)) == ESP_OK); + CHECK(spi_flash_write(0, &val1, sizeof(val1)) == ESP_OK); uint32_t val2 = 0x5678efab; - CHECK(spi_flash_write(4096 - 4, reinterpret_cast(&val2), sizeof(val2)) == ESP_OK); + CHECK(spi_flash_write(4096 - 4, &val2, sizeof(val2)) == ESP_OK); CHECK(emu.words()[0] == val1); CHECK(range_empty_n(emu.words() + 1, 4096 / 4 - 2)); diff --git a/components/spi_flash/flash_ops.c b/components/spi_flash/flash_ops.c index 512f6d20d..ae72568aa 100644 --- a/components/spi_flash/flash_ops.c +++ b/components/spi_flash/flash_ops.c @@ -124,7 +124,7 @@ esp_err_t IRAM_ATTR spi_flash_erase_range(uint32_t start_addr, uint32_t size) return spi_flash_translate_rc(rc); } -esp_err_t IRAM_ATTR spi_flash_write(size_t dest_addr, const uint8_t *src, size_t size) +esp_err_t IRAM_ATTR spi_flash_write(size_t dest_addr, const void *src, size_t size) { // TODO: replace this check with code which deals with unaligned sources if (((ptrdiff_t) src) % 4 != 0) { @@ -157,7 +157,7 @@ esp_err_t IRAM_ATTR spi_flash_write(size_t dest_addr, const uint8_t *src, size_t return spi_flash_translate_rc(rc); } -esp_err_t IRAM_ATTR spi_flash_read(size_t src_addr, uint8_t *dest, size_t size) +esp_err_t IRAM_ATTR spi_flash_read(size_t src_addr, void *dest, size_t size) { // TODO: replace this check with code which deals with unaligned destinations if (((ptrdiff_t) dest) % 4 != 0) { diff --git a/components/spi_flash/include/esp_partition.h b/components/spi_flash/include/esp_partition.h index 89e523f9a..432575f19 100644 --- a/components/spi_flash/include/esp_partition.h +++ b/components/spi_flash/include/esp_partition.h @@ -159,7 +159,7 @@ void esp_partition_iterator_release(esp_partition_iterator_t iterator); * 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, uint8_t* dst, size_t size); + size_t src_offset, void* dst, size_t size); /** * @brief Write data to the partition @@ -185,7 +185,7 @@ esp_err_t esp_partition_read(const esp_partition_t* 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 uint8_t* src, size_t size); + size_t dst_offset, const void* src, size_t size); /** * @brief Erase part of the partition diff --git a/components/spi_flash/include/esp_spi_flash.h b/components/spi_flash/include/esp_spi_flash.h index 16b50a718..df0a8decc 100644 --- a/components/spi_flash/include/esp_spi_flash.h +++ b/components/spi_flash/include/esp_spi_flash.h @@ -83,7 +83,7 @@ esp_err_t spi_flash_erase_range(size_t start_addr, size_t size); * * @return esp_err_t */ -esp_err_t spi_flash_write(size_t des_addr, const uint8_t *src_addr, size_t size); +esp_err_t spi_flash_write(size_t dest, const void *src, size_t size); /** * @brief Read data from Flash. @@ -97,7 +97,7 @@ esp_err_t spi_flash_write(size_t des_addr, const uint8_t *src_addr, size_t size) * * @return esp_err_t */ -esp_err_t spi_flash_read(size_t src_addr, uint8_t *des_addr, size_t size); +esp_err_t spi_flash_read(size_t src, void *dest, size_t size); /** * @brief Enumeration which specifies memory space requested in an mmap call From c581229e1d47da7b531433905896e4820ccdf54e Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Thu, 27 Oct 2016 00:48:10 +0800 Subject: [PATCH 12/13] partition API: separate type and subtype into two enums --- components/spi_flash/include/esp_partition.h | 91 ++++++++++---------- components/spi_flash/partition.c | 41 ++++----- 2 files changed, 62 insertions(+), 70 deletions(-) diff --git a/components/spi_flash/include/esp_partition.h b/components/spi_flash/include/esp_partition.h index 432575f19..e2f289eb7 100644 --- a/components/spi_flash/include/esp_partition.h +++ b/components/spi_flash/include/esp_partition.h @@ -26,51 +26,52 @@ extern "C" { #endif 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_APP = 0x00, + ESP_PARTITION_TYPE_DATA = 0x01, + ESP_PARTITION_TYPE_FILESYSTEM = 0x02, } esp_partition_type_t; -#define ESP_PARTITION_APP_OTA(i) ((esp_partition_type_t)(ESP_PARTITION_APP_OTA_MIN + ((i) & 0xf))) +typedef enum { + ESP_PARTITION_SUBTYPE_APP_FACTORY = 0x00, + ESP_PARTITION_SUBTYPE_APP_OTA_MIN = 0x10, + ESP_PARTITION_SUBTYPE_APP_OTA_0 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 0, + ESP_PARTITION_SUBTYPE_APP_OTA_1 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 1, + ESP_PARTITION_SUBTYPE_APP_OTA_2 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 2, + ESP_PARTITION_SUBTYPE_APP_OTA_3 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 3, + ESP_PARTITION_SUBTYPE_APP_OTA_4 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 4, + ESP_PARTITION_SUBTYPE_APP_OTA_5 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 5, + ESP_PARTITION_SUBTYPE_APP_OTA_6 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 6, + ESP_PARTITION_SUBTYPE_APP_OTA_7 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 7, + ESP_PARTITION_SUBTYPE_APP_OTA_8 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 8, + ESP_PARTITION_SUBTYPE_APP_OTA_9 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 9, + ESP_PARTITION_SUBTYPE_APP_OTA_10 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 10, + ESP_PARTITION_SUBTYPE_APP_OTA_11 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 11, + ESP_PARTITION_SUBTYPE_APP_OTA_12 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 12, + ESP_PARTITION_SUBTYPE_APP_OTA_13 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 13, + ESP_PARTITION_SUBTYPE_APP_OTA_14 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 14, + ESP_PARTITION_SUBTYPE_APP_OTA_15 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 15, + ESP_PARTITION_SUBTYPE_APP_OTA_MAX = 15, + ESP_PARTITION_SUBTYPE_APP_TEST = 0x20, + + ESP_PARTITION_SUBTYPE_DATA_OTA = 0x00, + ESP_PARTITION_SUBTYPE_DATA_RF = 0x01, + ESP_PARTITION_SUBTYPE_DATA_NVS = 0x02, + + ESP_PARTITION_SUBTYPE_FILESYSTEM_ESPHTTPD = 0x00, + ESP_PARTITION_SUBTYPE_FILESYSTEM_FAT = 0x01, + ESP_PARTITION_SUBTYPE_FILESYSTEM_SPIFFS = 0x02, + + ESP_PARTITION_SUBTYPE_ANY = 0xff, +} esp_partition_subtype_t; + +#define ESP_PARTITION_SUBTYPE_OTA(i) ((esp_partition_subtype_t)(ESP_PARTITION_SUBTYPE_APP_OTA_MIN + ((i) & 0xf))) typedef struct esp_partition_iterator_opaque_* esp_partition_iterator_t; typedef struct { esp_partition_type_t type; + esp_partition_subtype_t subtype; uint32_t address; uint32_t size; char label[17]; @@ -81,9 +82,9 @@ typedef struct { * @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 ESP_PARTITION_APP_ANY or ESP_PARTITION_FILESYSTEM_ANY, - * respectively. + * @param subtype Partition subtype, of esp_partition_subtype_t values. + * 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. * @@ -92,22 +93,22 @@ typedef struct { * 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, const char* label); +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 - * To find all app partitions or all filesystem partitions, - * use ESP_PARTITION_APP_ANY or ESP_PARTITION_FILESYSTEM_ANY, - * respectively. + * @param subtype Partition subtype, of esp_partition_subtype_t values. + * 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 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); +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 diff --git a/components/spi_flash/partition.c b/components/spi_flash/partition.c index d138f09b5..21013d96f 100644 --- a/components/spi_flash/partition.c +++ b/components/spi_flash/partition.c @@ -39,13 +39,14 @@ typedef struct partition_list_item_ { 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 esp_partition_iterator_opaque_t* iterator_create(esp_partition_type_t type, const char* label); +static esp_partition_iterator_opaque_t* iterator_create(esp_partition_type_t type, esp_partition_subtype_t subtype, const char* label); static esp_err_t load_partitions(); @@ -54,18 +55,8 @@ static SLIST_HEAD(partition_list_head_, partition_list_item_) 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) + esp_partition_subtype_t subtype, const char* label) { if (SLIST_EMPTY(&s_partition_list)) { // only lock if list is empty (and check again after acquiring lock) @@ -81,7 +72,7 @@ esp_partition_iterator_t esp_partition_find(esp_partition_type_t type, } // 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); + esp_partition_iterator_t it = iterator_create(type, subtype, 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 @@ -95,17 +86,13 @@ esp_partition_iterator_t esp_partition_next(esp_partition_iterator_t it) 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) { + if (it->type != p->type) { continue; } - if (requested_minor_type != 0xff && requested_minor_type != it_minor_type) { + if (it->subtype != 0xff && it->subtype != p->subtype) { continue; } if (it->label != NULL && strcmp(it->label, p->label) != 0) { @@ -124,9 +111,10 @@ esp_partition_iterator_t esp_partition_next(esp_partition_iterator_t it) return it; } -const esp_partition_t* esp_partition_find_first(esp_partition_type_t type, const char* label) +const esp_partition_t* esp_partition_find_first(esp_partition_type_t type, + esp_partition_subtype_t subtype, const char* label) { - esp_partition_iterator_t it = esp_partition_find(type, label); + esp_partition_iterator_t it = esp_partition_find(type, subtype, label); if (it == NULL) { return NULL; } @@ -135,11 +123,13 @@ const esp_partition_t* esp_partition_find_first(esp_partition_type_t type, const return res; } -static esp_partition_iterator_opaque_t* iterator_create(esp_partition_type_t type, const char* label) +static esp_partition_iterator_opaque_t* iterator_create(esp_partition_type_t type, + esp_partition_subtype_t subtype, const char* label) { esp_partition_iterator_opaque_t* it = (esp_partition_iterator_opaque_t*) malloc(sizeof(esp_partition_iterator_opaque_t)); it->type = type; + it->subtype = subtype; it->label = label; it->next_item = SLIST_FIRST(&s_partition_list); it->info = NULL; @@ -172,7 +162,8 @@ static esp_err_t load_partitions() 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.type = it->type; + item->info.subtype = it->subtype; item->info.encrypted = false; // it->label may not be zero-terminated strncpy(item->info.label, (const char*) it->label, sizeof(it->label)); @@ -201,7 +192,7 @@ const esp_partition_t* esp_partition_get(esp_partition_iterator_t iterator) } esp_err_t esp_partition_read(const esp_partition_t* partition, - size_t src_offset, uint8_t* dst, size_t size) + size_t src_offset, void* dst, size_t size) { assert(partition != NULL); if (src_offset > partition->size) { @@ -214,7 +205,7 @@ esp_err_t esp_partition_read(const esp_partition_t* partition, } esp_err_t esp_partition_write(const esp_partition_t* partition, - size_t dst_offset, const uint8_t* src, size_t size) + size_t dst_offset, const void* src, size_t size) { assert(partition != NULL); if (dst_offset > partition->size) { From dc2b5d0c96ff018ebd5b988939f44793dbed3f9d Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Thu, 27 Oct 2016 10:16:42 +0800 Subject: [PATCH 13/13] spi_flash: update comment blocks --- components/spi_flash/include/esp_partition.h | 21 +++++++++----------- components/spi_flash/include/esp_spi_flash.h | 8 ++++---- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/components/spi_flash/include/esp_partition.h b/components/spi_flash/include/esp_partition.h index e2f289eb7..ae0185dcd 100644 --- a/components/spi_flash/include/esp_partition.h +++ b/components/spi_flash/include/esp_partition.h @@ -82,9 +82,9 @@ typedef struct { * @brief Find partition based on one or more parameters * * @param type Partition type, one of esp_partition_type_t values - * @param subtype Partition subtype, of esp_partition_subtype_t values. - * To find all partitions of given type, use - * ESP_PARTITION_SUBTYPE_ANY. + * @param subtype Partition subtype, one of esp_partition_subtype_t values. + * 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. * @@ -99,13 +99,13 @@ esp_partition_iterator_t esp_partition_find(esp_partition_type_t type, esp_parti * @brief Find first partition based on one or more parameters * * @param type Partition type, one of esp_partition_type_t values - * @param subtype Partition subtype, of esp_partition_subtype_t values. - * To find all partitions of given type, use - * ESP_PARTITION_SUBTYPE_ANY. + * @param subtype Partition subtype, one of esp_partition_subtype_t values. + * 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 parition is found. + * @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); @@ -134,9 +134,6 @@ esp_partition_iterator_t esp_partition_next(esp_partition_iterator_t iterator); /** * @brief Release partition iterator * - * Any pointers obtained using esp_partition_label function will be invalid - * after this call. - * * @param iterator Iterator obtained using esp_partition_find. Must be non-NULL. * */ @@ -155,7 +152,7 @@ void esp_partition_iterator_release(esp_partition_iterator_t iterator); * @param size Size of data to be read, in bytes. * * @return ESP_OK, if data was read successfully; - * ESP_ERR_INVALID_ARG, if iterator or src are NULL; + * 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. */ @@ -181,7 +178,7 @@ esp_err_t esp_partition_read(const esp_partition_t* partition, * esp_partition_erase_range call. * * @return ESP_OK, if data was written successfully; - * ESP_ERR_INVALID_ARG, if iterator or dst are NULL; + * 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. */ diff --git a/components/spi_flash/include/esp_spi_flash.h b/components/spi_flash/include/esp_spi_flash.h index df0a8decc..c65eaa583 100644 --- a/components/spi_flash/include/esp_spi_flash.h +++ b/components/spi_flash/include/esp_spi_flash.h @@ -77,8 +77,8 @@ esp_err_t spi_flash_erase_range(size_t start_addr, size_t size); * @note Both des_addr and src_addr have to be 4-byte aligned. * This is a temporary limitation which will be removed. * - * @param des_addr destination address in Flash - * @param src_addr source address of the data + * @param dest destination address in Flash + * @param src pointer to the source buffer * @param size length of data, in bytes * * @return esp_err_t @@ -91,8 +91,8 @@ esp_err_t spi_flash_write(size_t dest, const void *src, size_t size); * @note Both des_addr and src_addr have to be 4-byte aligned. * This is a temporary limitation which will be removed. * - * @param src_addr source address of the data in Flash. - * @param des_addr destination address + * @param src source address of the data in Flash. + * @param dest pointer to the destination buffer * @param size length of data * * @return esp_err_t