diff --git a/components/esp32/test/CMakeLists.txt b/components/esp32/test/CMakeLists.txt index 6aed5f74f2..af8794813c 100644 --- a/components/esp32/test/CMakeLists.txt +++ b/components/esp32/test/CMakeLists.txt @@ -1,7 +1,7 @@ if(IDF_TARGET STREQUAL "esp32") idf_component_register(SRC_DIRS . PRIV_INCLUDE_DIRS . - PRIV_REQUIRES cmock test_utils nvs_flash ulp esp_common + PRIV_REQUIRES cmock test_utils mbedtls ulp esp_common ) target_link_libraries(${COMPONENT_LIB} INTERFACE "-u ld_include_test_dport_xt_highint5") endif() diff --git a/components/esp32s2/test/CMakeLists.txt b/components/esp32s2/test/CMakeLists.txt index da0006730e..733ed02730 100644 --- a/components/esp32s2/test/CMakeLists.txt +++ b/components/esp32s2/test/CMakeLists.txt @@ -1,7 +1,7 @@ if(IDF_TARGET STREQUAL "esp32s2") idf_component_register(SRC_DIRS . PRIV_INCLUDE_DIRS . - PRIV_REQUIRES cmock test_utils nvs_flash ulp esp_common + PRIV_REQUIRES cmock test_utils nvs_flash mbedtls ulp esp_common ) endif() diff --git a/components/esp_common/src/esp_err_to_name.c b/components/esp_common/src/esp_err_to_name.c index a45bbe323a..5de66c4c2e 100644 --- a/components/esp_common/src/esp_err_to_name.c +++ b/components/esp_common/src/esp_err_to_name.c @@ -212,6 +212,12 @@ static const esp_err_msg_t esp_err_msg_table[] = { ERR_TBL_IT(ESP_ERR_NVS_CONTENT_DIFFERS), /* 4376 0x1118 Internal error; never returned by nvs API functions. NVS key is different in comparison */ +# endif +# ifdef ESP_ERR_NVS_WRONG_ENCRYPTION + ERR_TBL_IT(ESP_ERR_NVS_WRONG_ENCRYPTION), /* 4377 0x1119 NVS partition is marked as encrypted + with generic flash encryption. This is + forbidden since the NVS encryption works + differently. */ # endif // components/ulp/include/ulp_common.h # ifdef ESP_ERR_ULP_BASE diff --git a/components/nvs_flash/CMakeLists.txt b/components/nvs_flash/CMakeLists.txt index d450d8077a..41fe7d8006 100644 --- a/components/nvs_flash/CMakeLists.txt +++ b/components/nvs_flash/CMakeLists.txt @@ -1,16 +1,18 @@ set(srcs "src/nvs_api.cpp" "src/nvs_cxx_api.cpp" "src/nvs_item_hash_list.cpp" - "src/nvs_ops.cpp" "src/nvs_page.cpp" "src/nvs_pagemanager.cpp" "src/nvs_storage.cpp" "src/nvs_handle_simple.cpp" "src/nvs_handle_locked.cpp" + "src/nvs_partition.cpp" + "src/nvs_partition_lookup.cpp" "src/nvs_partition_manager.cpp" "src/nvs_types.cpp") + if(CONFIG_NVS_ENCRYPTION) - list(APPEND srcs "src/nvs_encr.cpp") + list(APPEND srcs "src/nvs_encrypted_partition.cpp") endif() idf_component_register(SRCS "${srcs}" diff --git a/components/nvs_flash/include/nvs.h b/components/nvs_flash/include/nvs.h index 3e877a6520..a73f531947 100644 --- a/components/nvs_flash/include/nvs.h +++ b/components/nvs_flash/include/nvs.h @@ -59,6 +59,7 @@ typedef nvs_handle_t nvs_handle IDF_DEPRECATED("Replace with nvs_handle_t"); #define ESP_ERR_NVS_ENCR_NOT_SUPPORTED (ESP_ERR_NVS_BASE + 0x15) /*!< NVS encryption is not supported in this version */ #define ESP_ERR_NVS_KEYS_NOT_INITIALIZED (ESP_ERR_NVS_BASE + 0x16) /*!< NVS key partition is uninitialized */ #define ESP_ERR_NVS_CORRUPT_KEY_PART (ESP_ERR_NVS_BASE + 0x17) /*!< NVS key partition is corrupt */ +#define ESP_ERR_NVS_WRONG_ENCRYPTION (ESP_ERR_NVS_BASE + 0x19) /*!< NVS partition is marked as encrypted with generic flash encryption. This is forbidden since the NVS encryption works differently. */ #define ESP_ERR_NVS_CONTENT_DIFFERS (ESP_ERR_NVS_BASE + 0x18) /*!< Internal error; never returned by nvs API functions. NVS key is different in comparison */ diff --git a/components/nvs_flash/src/nvs_api.cpp b/components/nvs_flash/src/nvs_api.cpp index 06d638f4c5..8ce0d07ba5 100644 --- a/components/nvs_flash/src/nvs_api.cpp +++ b/components/nvs_flash/src/nvs_api.cpp @@ -20,9 +20,6 @@ #include "esp_partition.h" #include "sdkconfig.h" #include "nvs_handle_simple.hpp" -#ifdef CONFIG_NVS_ENCRYPTION -#include "nvs_encr.hpp" -#endif #ifdef ESP_PLATFORM #include @@ -57,14 +54,9 @@ private: uint32_t NVSHandleEntry::s_nvs_next_handle; extern "C" void nvs_dump(const char *partName); -extern "C" esp_err_t nvs_flash_init_custom(const char *partName, uint32_t baseSector, uint32_t sectorCount); - -#ifdef CONFIG_NVS_ENCRYPTION -extern "C" esp_err_t nvs_flash_secure_init_custom(const char *partName, uint32_t baseSector, uint32_t sectorCount, nvs_sec_cfg_t* cfg); -#endif #ifdef ESP_PLATFORM -SemaphoreHandle_t nvs::Lock::mSemaphore = NULL; +SemaphoreHandle_t nvs::Lock::mSemaphore = nullptr; #endif using namespace std; @@ -83,41 +75,13 @@ extern "C" void nvs_dump(const char *partName) nvs::Storage* pStorage; pStorage = lookup_storage_from_name(partName); - if (pStorage == NULL) { + if (pStorage == nullptr) { return; } pStorage->debugDump(); - return; } -extern "C" esp_err_t nvs_flash_init_custom(const char *partName, uint32_t baseSector, uint32_t sectorCount) -{ - ESP_LOGD(TAG, "nvs_flash_init_custom partition=%s start=%d count=%d", partName, baseSector, sectorCount); - - return NVSPartitionManager::get_instance()->init_custom(partName, baseSector, sectorCount); -} - -#ifdef CONFIG_NVS_ENCRYPTION -extern "C" esp_err_t nvs_flash_secure_init_custom(const char *partName, uint32_t baseSector, uint32_t sectorCount, nvs_sec_cfg_t* cfg) -{ - ESP_LOGD(TAG, "nvs_flash_secure_init_custom partition=%s start=%d count=%d", partName, baseSector, sectorCount); - - if(cfg) { - auto encrMgr = EncrMgr::getInstance(); - - if (!encrMgr) return ESP_ERR_NO_MEM; - - auto err = encrMgr->setSecurityContext(baseSector, sectorCount, cfg); - if(err != ESP_OK) { - return err; - } - } - - return nvs_flash_init_custom(partName, baseSector, sectorCount); -} -#endif - static esp_err_t close_handles_and_deinit(const char* part_name) { // Delete all corresponding open handles @@ -132,13 +96,24 @@ extern "C" esp_err_t nvs_flash_init_partition_ptr(const esp_partition_t *partiti Lock::init(); Lock lock; - if (!partition) { + if (partition == nullptr) { return ESP_ERR_INVALID_ARG; } - return nvs_flash_init_custom(partition->label, - partition->address / SPI_FLASH_SEC_SIZE, - partition->size / SPI_FLASH_SEC_SIZE); + NVSPartition *part = new (std::nothrow) NVSPartition(partition); + if (part == nullptr) { + return ESP_ERR_NO_MEM; + } + + esp_err_t init_res = NVSPartitionManager::get_instance()->init_custom(part, + partition->address / SPI_FLASH_SEC_SIZE, + partition->size / SPI_FLASH_SEC_SIZE); + + if (init_res != ESP_OK) { + delete part; + } + + return init_res; } #ifdef ESP_PLATFORM @@ -160,21 +135,8 @@ extern "C" esp_err_t nvs_flash_secure_init_partition(const char *part_name, nvs_ { Lock::init(); Lock lock; - nvs::Storage* mStorage; - mStorage = lookup_storage_from_name(part_name); - if (mStorage) { - return ESP_OK; - } - - const esp_partition_t* partition = esp_partition_find_first( - ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, part_name); - if (partition == NULL) { - return ESP_ERR_NOT_FOUND; - } - - return nvs_flash_secure_init_custom(part_name, partition->address / SPI_FLASH_SEC_SIZE, - partition->size / SPI_FLASH_SEC_SIZE, cfg); + return NVSPartitionManager::get_instance()->secure_init_partition(part_name, cfg); } extern "C" esp_err_t nvs_flash_secure_init(nvs_sec_cfg_t* cfg) @@ -200,7 +162,7 @@ extern "C" esp_err_t nvs_flash_erase_partition(const char *part_name) const esp_partition_t* partition = esp_partition_find_first( ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, part_name); - if (partition == NULL) { + if (partition == nullptr) { return ESP_ERR_NOT_FOUND; } @@ -212,7 +174,7 @@ extern "C" esp_err_t nvs_flash_erase_partition_ptr(const esp_partition_t *partit Lock::init(); Lock lock; - if (!partition) { + if (partition == nullptr) { return ESP_ERR_INVALID_ARG; } @@ -515,7 +477,7 @@ extern "C" esp_err_t nvs_get_stats(const char* part_name, nvs_stats_t* nvs_stats Lock lock; nvs::Storage* pStorage; - if (nvs_stats == NULL) { + if (nvs_stats == nullptr) { return ESP_ERR_INVALID_ARG; } nvs_stats->used_entries = 0; @@ -523,8 +485,8 @@ extern "C" esp_err_t nvs_get_stats(const char* part_name, nvs_stats_t* nvs_stats nvs_stats->total_entries = 0; nvs_stats->namespace_count = 0; - pStorage = lookup_storage_from_name((part_name == NULL) ? NVS_DEFAULT_PART_NAME : part_name); - if (pStorage == NULL) { + pStorage = lookup_storage_from_name((part_name == nullptr) ? NVS_DEFAULT_PART_NAME : part_name); + if (pStorage == nullptr) { return ESP_ERR_NVS_NOT_INITIALIZED; } @@ -538,7 +500,7 @@ extern "C" esp_err_t nvs_get_stats(const char* part_name, nvs_stats_t* nvs_stats extern "C" esp_err_t nvs_get_used_entry_count(nvs_handle_t c_handle, size_t* used_entries) { Lock lock; - if(used_entries == NULL){ + if(used_entries == nullptr){ return ESP_ERR_INVALID_ARG; } *used_entries = 0; @@ -571,12 +533,12 @@ extern "C" esp_err_t nvs_flash_generate_keys(const esp_partition_t* partition, n cfg->tky[cnt] = 0xee; } - err = spi_flash_write(partition->address, cfg->eky, NVS_KEY_SIZE); + err = esp_partition_write(partition, 0, cfg->eky, NVS_KEY_SIZE); if(err != ESP_OK) { return err; } - err = spi_flash_write(partition->address + NVS_KEY_SIZE, cfg->tky, NVS_KEY_SIZE); + err = esp_partition_write(partition, NVS_KEY_SIZE, cfg->tky, NVS_KEY_SIZE); if(err != ESP_OK) { return err; } @@ -622,17 +584,17 @@ extern "C" esp_err_t nvs_flash_read_security_cfg(const esp_partition_t* partitio return true; }; - auto err = spi_flash_read(partition->address, eky_raw, NVS_KEY_SIZE); + auto err = esp_partition_read_raw(partition, 0, eky_raw, NVS_KEY_SIZE); if(err != ESP_OK) { return err; } - err = spi_flash_read(partition->address + NVS_KEY_SIZE, tky_raw, NVS_KEY_SIZE); + err = esp_partition_read_raw(partition, NVS_KEY_SIZE, tky_raw, NVS_KEY_SIZE); if(err != ESP_OK) { return err; } - err = spi_flash_read(partition->address + 2 * NVS_KEY_SIZE, &crc_raw, 4); + err = esp_partition_read_raw(partition, 2 * NVS_KEY_SIZE, &crc_raw, 4); if(err != ESP_OK) { return err; } @@ -679,8 +641,8 @@ extern "C" esp_err_t nvs_flash_read_security_cfg(const esp_partition_t* partitio static nvs_iterator_t create_iterator(nvs::Storage *storage, nvs_type_t type) { nvs_iterator_t it = (nvs_iterator_t)calloc(1, sizeof(nvs_opaque_iterator_t)); - if (it == NULL) { - return NULL; + if (it == nullptr) { + return nullptr; } it->storage = storage; @@ -695,19 +657,19 @@ extern "C" nvs_iterator_t nvs_entry_find(const char *part_name, const char *name nvs::Storage *pStorage; pStorage = lookup_storage_from_name(part_name); - if (pStorage == NULL) { - return NULL; + if (pStorage == nullptr) { + return nullptr; } nvs_iterator_t it = create_iterator(pStorage, type); - if (it == NULL) { - return NULL; + if (it == nullptr) { + return nullptr; } bool entryFound = pStorage->findEntry(it, namespace_name); if (!entryFound) { free(it); - return NULL; + return nullptr; } return it; @@ -721,7 +683,7 @@ extern "C" nvs_iterator_t nvs_entry_next(nvs_iterator_t it) bool entryFound = it->storage->nextEntry(it); if (!entryFound) { free(it); - return NULL; + return nullptr; } return it; diff --git a/components/nvs_flash/src/nvs_encr.cpp b/components/nvs_flash/src/nvs_encr.cpp deleted file mode 100644 index c33a595475..0000000000 --- a/components/nvs_flash/src/nvs_encr.cpp +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright 2015-2018 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 "nvs_encr.hpp" -#include "nvs_types.hpp" -#include - -namespace nvs -{ - - bool EncrMgr::isActive = false; - EncrMgr* EncrMgr::instance = nullptr; - - - EncrMgr* EncrMgr::getInstance() - { - if(!isActive) - { - instance = new (std::nothrow) EncrMgr(); - if (instance) { - isActive = true; - } - } - return instance; - } - - void EncrMgr::resetInstance() - { - if(isActive) { - delete instance; - instance = nullptr; - isActive = false; - } - } - - bool EncrMgr::isEncrActive() { - return isActive; - } - - XtsCtxt* EncrMgr::findXtsCtxtFromAddr(uint32_t addr) { - - auto it = find_if(std::begin(xtsCtxtList), std::end(xtsCtxtList), [=](XtsCtxt& ctx) -> bool - { return (ctx.baseSector * SPI_FLASH_SEC_SIZE <= addr) - && (addr < (ctx.baseSector + ctx.sectorCount) * SPI_FLASH_SEC_SIZE); }); - - if (it == std::end(xtsCtxtList)) { - return nullptr; - } - return it; - } - - esp_err_t EncrMgr::setSecurityContext(uint32_t baseSector, uint32_t sectorCount, nvs_sec_cfg_t* cfg) { - - uint8_t* eky = reinterpret_cast(cfg); - - auto ctxt = new (std::nothrow) XtsCtxt(); - - if (!ctxt) return ESP_ERR_NO_MEM; - - ctxt->baseSector = baseSector; - ctxt->sectorCount = sectorCount; - - mbedtls_aes_xts_init(ctxt->ectxt); - mbedtls_aes_xts_init(ctxt->dctxt); - - if(mbedtls_aes_xts_setkey_enc(ctxt->ectxt, eky, 2 * NVS_KEY_SIZE * 8)) { - return ESP_ERR_NVS_XTS_CFG_FAILED; - } - - if(mbedtls_aes_xts_setkey_dec(ctxt->dctxt, eky, 2 * NVS_KEY_SIZE * 8)) { - return ESP_ERR_NVS_XTS_CFG_FAILED; - } - - xtsCtxtList.push_back(ctxt); - - return ESP_OK; - } - - esp_err_t EncrMgr::removeSecurityContext(uint32_t baseSector) { - auto xtsCtxt = findXtsCtxtFromAddr(baseSector * SPI_FLASH_SEC_SIZE); - if(!xtsCtxt) { - return ESP_ERR_NVS_XTS_CFG_NOT_FOUND; - } - xtsCtxtList.erase(xtsCtxt); - delete xtsCtxt; - - if(!xtsCtxtList.size()) { - resetInstance(); - } - return ESP_OK; - } - - - esp_err_t EncrMgr::encryptNvsData(uint8_t* ptxt, uint32_t addr, uint32_t ptxtLen, XtsCtxt* xtsCtxt) { - - uint8_t entrySize = sizeof(Item); - - //sector num required as an arr by mbedtls. Should have been just uint64/32. - uint8_t data_unit[16]; - - assert(ptxtLen % entrySize == 0); - - /* Use relative address instead of absolute address (relocatable), so that host-generated - * encrypted nvs images can be used*/ - uint32_t relAddr = addr - (xtsCtxt->baseSector * SPI_FLASH_SEC_SIZE); - - memset(data_unit, 0, sizeof(data_unit)); - - for(uint8_t entry = 0; entry < (ptxtLen/entrySize); entry++) - { - uint32_t offset = entry * entrySize; - uint32_t *addr_loc = (uint32_t*) &data_unit[0]; - - *addr_loc = relAddr + offset; - if(mbedtls_aes_crypt_xts(xtsCtxt->ectxt, MBEDTLS_AES_ENCRYPT, entrySize, data_unit, ptxt + offset, ptxt + offset)) { - return ESP_ERR_NVS_XTS_ENCR_FAILED; - } - } - return ESP_OK; - } - - esp_err_t EncrMgr::decryptNvsData(uint8_t* ctxt, uint32_t addr, uint32_t ctxtLen, XtsCtxt* xtsCtxt) { - - //sector num required as an arr by mbedtls. Should have been just uint64/32. - uint8_t data_unit[16]; - - - /** Currently upper layer of NVS reads entries one by one even for variable size - * multi-entry data types. So length should always be equal to size of an entry.*/ - assert(ctxtLen == sizeof(Item)); - - uint32_t relAddr = addr - (xtsCtxt->baseSector * SPI_FLASH_SEC_SIZE); - - memset(data_unit, 0, sizeof(data_unit)); - - memcpy(data_unit, &relAddr, sizeof(relAddr)); - - if(mbedtls_aes_crypt_xts(xtsCtxt->dctxt, MBEDTLS_AES_DECRYPT, ctxtLen, data_unit, ctxt, ctxt)) { - return ESP_ERR_NVS_XTS_DECR_FAILED; - } - return ESP_OK; - } - -} // namespace nvs diff --git a/components/nvs_flash/src/nvs_encr.hpp b/components/nvs_flash/src/nvs_encr.hpp deleted file mode 100644 index 3c136d376f..0000000000 --- a/components/nvs_flash/src/nvs_encr.hpp +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2015-2018 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 nvs_encr_hpp -#define nvs_encr_hpp - -#include "esp_err.h" -#include "mbedtls/aes.h" -#include "intrusive_list.h" -#include "nvs_flash.h" - -namespace nvs -{ - -struct XtsCtxt : public intrusive_list_node { - public: - mbedtls_aes_xts_context ectxt[1]; - mbedtls_aes_xts_context dctxt[1]; - uint32_t baseSector; - uint32_t sectorCount; -}; - - -/* A singleton class for managing nvs encryption*/ -class EncrMgr -{ - public: - static EncrMgr* getInstance(); - static void resetInstance(); - static bool isEncrActive(); - esp_err_t setSecurityContext(uint32_t baseSector, uint32_t sectorCount, nvs_sec_cfg_t* cfg); - esp_err_t removeSecurityContext(uint32_t baseSector); - esp_err_t encryptNvsData(uint8_t* ptxt, uint32_t addr, uint32_t ptxtLen, XtsCtxt* xtsCtxt); - esp_err_t decryptNvsData(uint8_t* ctxt, uint32_t addr, uint32_t ctxtLen, XtsCtxt* xtsCtxt); - XtsCtxt* findXtsCtxtFromAddr(uint32_t addr); - ~EncrMgr() {} - - protected: - static bool isActive; - static EncrMgr* instance; - intrusive_list xtsCtxtList; - EncrMgr() {} - -}; // class EncrMgr - -esp_err_t nvs_flash_write(size_t destAddr, const void *srcAddr, size_t size); -esp_err_t nvs_flash_read(size_t srcAddr, void *destAddr, size_t size); - - -} // namespace nvs - - -#endif /* nvs_encr_hpp */ diff --git a/components/nvs_flash/src/nvs_encrypted_partition.cpp b/components/nvs_flash/src/nvs_encrypted_partition.cpp new file mode 100644 index 0000000000..4256c6a012 --- /dev/null +++ b/components/nvs_flash/src/nvs_encrypted_partition.cpp @@ -0,0 +1,122 @@ +// Copyright 2019 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 "nvs_encrypted_partition.hpp" +#include "nvs_types.hpp" + +namespace nvs { + +NVSEncryptedPartition::NVSEncryptedPartition(const esp_partition_t *partition) + : NVSPartition(partition) { } + +esp_err_t NVSEncryptedPartition::init(nvs_sec_cfg_t* cfg) +{ + uint8_t* eky = reinterpret_cast(cfg); + + mbedtls_aes_xts_init(&mEctxt); + mbedtls_aes_xts_init(&mDctxt); + + if (mbedtls_aes_xts_setkey_enc(&mEctxt, eky, 2 * NVS_KEY_SIZE * 8) != 0) { + return ESP_ERR_NVS_XTS_CFG_FAILED; + } + + if (mbedtls_aes_xts_setkey_dec(&mDctxt, eky, 2 * NVS_KEY_SIZE * 8) != 0) { + return ESP_ERR_NVS_XTS_CFG_FAILED; + } + + return ESP_OK; +} + +esp_err_t NVSEncryptedPartition::read(size_t src_offset, void* dst, size_t size) +{ + /** Currently upper layer of NVS reads entries one by one even for variable size + * multi-entry data types. So length should always be equal to size of an entry.*/ + if (size != sizeof(Item)) return ESP_ERR_INVALID_SIZE; + + // read data + esp_err_t read_result = esp_partition_read(mESPPartition, src_offset, dst, size); + if (read_result != ESP_OK) { + return read_result; + } + + // decrypt data + //sector num required as an arr by mbedtls. Should have been just uint64/32. + uint8_t data_unit[16]; + + uint32_t relAddr = src_offset; + + memset(data_unit, 0, sizeof(data_unit)); + + memcpy(data_unit, &relAddr, sizeof(relAddr)); + + uint8_t *destination = reinterpret_cast(dst); + + if (mbedtls_aes_crypt_xts(&mDctxt, MBEDTLS_AES_DECRYPT, size, data_unit, destination, destination) != 0) { + return ESP_ERR_NVS_XTS_DECR_FAILED; + } + + return ESP_OK; +} + +esp_err_t NVSEncryptedPartition::write(size_t addr, const void* src, size_t size) +{ + if (size % ESP_ENCRYPT_BLOCK_SIZE != 0) return ESP_ERR_INVALID_SIZE; + + // copy data to buffer for encryption + uint8_t* buf = new (std::nothrow) uint8_t [size]; + + if (!buf) return ESP_ERR_NO_MEM; + + memcpy(buf, src, size); + + // encrypt data + uint8_t entrySize = sizeof(Item); + + //sector num required as an arr by mbedtls. Should have been just uint64/32. + uint8_t data_unit[16]; + + /* Use relative address instead of absolute address (relocatable), so that host-generated + * encrypted nvs images can be used*/ + uint32_t relAddr = addr; + + memset(data_unit, 0, sizeof(data_unit)); + + for(uint8_t entry = 0; entry < (size/entrySize); entry++) + { + uint32_t offset = entry * entrySize; + uint32_t *addr_loc = (uint32_t*) &data_unit[0]; + + *addr_loc = relAddr + offset; + if (mbedtls_aes_crypt_xts(&mEctxt, + MBEDTLS_AES_ENCRYPT, + entrySize, + data_unit, + buf + offset, + buf + offset) != 0) { + delete buf; + return ESP_ERR_NVS_XTS_ENCR_FAILED; + } + } + + // write data + esp_err_t result = esp_partition_write(mESPPartition, addr, buf, size); + + delete buf; + + return result; +} + +} // nvs + diff --git a/components/nvs_flash/src/nvs_encrypted_partition.hpp b/components/nvs_flash/src/nvs_encrypted_partition.hpp new file mode 100644 index 0000000000..7c93d136df --- /dev/null +++ b/components/nvs_flash/src/nvs_encrypted_partition.hpp @@ -0,0 +1,44 @@ +// Copyright 2019 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 NVS_ENCRYPTED_PARTITION_HPP_ +#define NVS_ENCRYPTED_PARTITION_HPP_ + +#include "mbedtls/aes.h" +#include "nvs_flash.h" +#include "nvs_partition.hpp" + +namespace nvs { + +class NVSEncryptedPartition : public NVSPartition { +public: + NVSEncryptedPartition(const esp_partition_t *partition); + + virtual ~NVSEncryptedPartition() { } + + esp_err_t init(nvs_sec_cfg_t* cfg); + + esp_err_t read(size_t src_offset, void* dst, size_t size) override; + + esp_err_t write(size_t dst_offset, const void* src, size_t size) override; + +protected: + mbedtls_aes_xts_context mEctxt; + mbedtls_aes_xts_context mDctxt; +}; + +} // nvs + +#endif // NVS_ENCRYPTED_PARTITION_HPP_ + diff --git a/components/nvs_flash/src/nvs_ops.cpp b/components/nvs_flash/src/nvs_ops.cpp deleted file mode 100644 index 4dfcc9c111..0000000000 --- a/components/nvs_flash/src/nvs_ops.cpp +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2015-2018 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 "esp_spi_flash.h" -#include "nvs_ops.hpp" -#ifdef CONFIG_NVS_ENCRYPTION -#include "nvs_encr.hpp" -#include -#endif - -namespace nvs -{ -#ifdef CONFIG_NVS_ENCRYPTION -esp_err_t nvs_flash_write(size_t destAddr, const void *srcAddr, size_t size) { - - if(EncrMgr::isEncrActive()) { - auto encrMgr = EncrMgr::getInstance(); - - if (!encrMgr) return ESP_ERR_NO_MEM; - - auto xtsCtxt = encrMgr->findXtsCtxtFromAddr(destAddr); - - if(xtsCtxt) { - uint8_t* buf = static_cast(malloc(size)); - memcpy(buf, srcAddr, size); - auto err = encrMgr->encryptNvsData(buf, destAddr, size, xtsCtxt); - if( err != ESP_OK) { - return err; - } - err = spi_flash_write(destAddr, buf, size); - delete buf; - return err; - } - } - return spi_flash_write(destAddr, srcAddr, size); -} - -esp_err_t nvs_flash_read(size_t srcAddr, void *destAddr, size_t size) { - - auto err = spi_flash_read(srcAddr, destAddr, size); - - if(err != ESP_OK) { - return err; - } - - if(EncrMgr::isEncrActive()) { - auto encrMgr = EncrMgr::getInstance(); - - if (!encrMgr) return ESP_ERR_NO_MEM; - - auto xtsCtxt = encrMgr->findXtsCtxtFromAddr(srcAddr); - if(xtsCtxt) { - return encrMgr->decryptNvsData(static_cast(destAddr), - srcAddr, size, xtsCtxt); - } - } - return ESP_OK; -} -#else -esp_err_t nvs_flash_write(size_t destAddr, const void *srcAddr, size_t size) { - return spi_flash_write(destAddr, srcAddr, size); -} - -esp_err_t nvs_flash_read(size_t srcAddr, void *destAddr, size_t size) { - return spi_flash_read(srcAddr, destAddr, size); -} -#endif -} diff --git a/components/nvs_flash/src/nvs_ops.hpp b/components/nvs_flash/src/nvs_ops.hpp deleted file mode 100644 index 20a9d140a0..0000000000 --- a/components/nvs_flash/src/nvs_ops.hpp +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2015-2018 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 nvs_ops_hpp -#define nvs_ops_hpp - -#include "esp_err.h" - -namespace nvs -{ - esp_err_t nvs_flash_write(size_t destAddr, const void *srcAddr, size_t size); - esp_err_t nvs_flash_read(size_t srcAddr, void *destAddr, size_t size); - -} // namespace nvs - - -#endif /* nvs_ops_hpp */ diff --git a/components/nvs_flash/src/nvs_page.cpp b/components/nvs_flash/src/nvs_page.cpp index 350cc2821d..0563eca2b8 100644 --- a/components/nvs_flash/src/nvs_page.cpp +++ b/components/nvs_flash/src/nvs_page.cpp @@ -20,11 +20,11 @@ #include #include -#include "nvs_ops.hpp" - namespace nvs { +Page::Page() : mPartition(nullptr) { } + uint32_t Page::Header::calculateCrc32() { return crc32_le(0xffffffff, @@ -32,14 +32,19 @@ uint32_t Page::Header::calculateCrc32() offsetof(Header, mCrc32) - offsetof(Header, mSeqNumber)); } -esp_err_t Page::load(uint32_t sectorNumber) +esp_err_t Page::load(Partition *partition, uint32_t sectorNumber) { + if (partition == nullptr) { + return ESP_ERR_INVALID_ARG; + } + + mPartition = partition; mBaseAddress = sectorNumber * SEC_SIZE; mUsedEntryCount = 0; mErasedEntryCount = 0; Header header; - auto rc = spi_flash_read(mBaseAddress, &header, sizeof(header)); + auto rc = mPartition->read_raw(mBaseAddress, &header, sizeof(header)); if (rc != ESP_OK) { mState = PageState::INVALID; return rc; @@ -54,7 +59,7 @@ esp_err_t Page::load(uint32_t sectorNumber) if (!block) return ESP_ERR_NO_MEM; for (uint32_t i = 0; i < SPI_FLASH_SEC_SIZE; i += 4 * BLOCK_SIZE) { - rc = spi_flash_read(mBaseAddress + i, block, 4 * BLOCK_SIZE); + rc = mPartition->read_raw(mBaseAddress + i, block, 4 * BLOCK_SIZE); if (rc != ESP_OK) { mState = PageState::INVALID; delete[] block; @@ -101,7 +106,7 @@ esp_err_t Page::writeEntry(const Item& item) { esp_err_t err; - err = nvs_flash_write(getEntryAddress(mNextFreeEntry), &item, sizeof(item)); + err = mPartition->write(getEntryAddress(mNextFreeEntry), &item, sizeof(item)); if (err != ESP_OK) { mState = PageState::INVALID; @@ -133,6 +138,7 @@ esp_err_t Page::writeEntryData(const uint8_t* data, size_t size) const uint8_t* buf = data; #ifdef ESP_PLATFORM + // TODO: check whether still necessary with esp_partition* API /* On the ESP32, data can come from DROM, which is not accessible by spi_flash_write * function. To work around this, we copy the data to heap if it came from DROM. * Hopefully this won't happen very often in practice. For data from DRAM, we should @@ -149,7 +155,7 @@ esp_err_t Page::writeEntryData(const uint8_t* data, size_t size) } #endif //ESP_PLATFORM - auto rc = nvs_flash_write(getEntryAddress(mNextFreeEntry), buf, size); + auto rc = mPartition->write(getEntryAddress(mNextFreeEntry), buf, size); #ifdef ESP_PLATFORM if (buf != data) { @@ -518,7 +524,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, mEntryTable.data(), + auto rc = mPartition->read_raw(mBaseAddress + ENTRY_TABLE_OFFSET, mEntryTable.data(), mEntryTable.byteSize()); if (rc != ESP_OK) { mState = PageState::INVALID; @@ -557,7 +563,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 = mPartition->read_raw(entryAddress, &header, sizeof(header)); if (rc != ESP_OK) { mState = PageState::INVALID; return rc; @@ -722,7 +728,7 @@ esp_err_t Page::initialize() header.mVersion = mVersion; header.mCrc32 = header.calculateCrc32(); - auto rc = spi_flash_write(mBaseAddress, &header, sizeof(header)); + auto rc = mPartition->write_raw(mBaseAddress, &header, sizeof(header)); if (rc != ESP_OK) { mState = PageState::INVALID; return rc; @@ -739,7 +745,7 @@ 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, + auto rc = mPartition->write_raw(mBaseAddress + ENTRY_TABLE_OFFSET + static_cast(wordToWrite) * 4, &word, sizeof(word)); if (rc != ESP_OK) { mState = PageState::INVALID; @@ -763,7 +769,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, + auto rc = mPartition->write_raw(mBaseAddress + ENTRY_TABLE_OFFSET + static_cast(wordIndex) * 4, &word, 4); if (rc != ESP_OK) { return rc; @@ -777,7 +783,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, &state_val, sizeof(state)); + auto rc = mPartition->write_raw(mBaseAddress, &state_val, sizeof(state)); if (rc != ESP_OK) { mState = PageState::INVALID; return rc; @@ -788,7 +794,7 @@ esp_err_t Page::alterPageState(PageState state) esp_err_t Page::readEntry(size_t index, Item& dst) const { - auto rc = nvs_flash_read(getEntryAddress(index), &dst, sizeof(dst)); + auto rc = mPartition->read(getEntryAddress(index), &dst, sizeof(dst)); if (rc != ESP_OK) { return rc; } @@ -925,8 +931,7 @@ esp_err_t Page::setVersion(uint8_t ver) esp_err_t Page::erase() { - auto sector = mBaseAddress / SPI_FLASH_SEC_SIZE; - auto rc = spi_flash_erase_sector(sector); + auto rc = mPartition->erase_range(mBaseAddress, SPI_FLASH_SEC_SIZE); if (rc != ESP_OK) { mState = PageState::INVALID; return rc; diff --git a/components/nvs_flash/src/nvs_page.hpp b/components/nvs_flash/src/nvs_page.hpp index a491752406..bb6a0dca62 100644 --- a/components/nvs_flash/src/nvs_page.hpp +++ b/components/nvs_flash/src/nvs_page.hpp @@ -24,6 +24,7 @@ #include "compressed_enum_table.hpp" #include "intrusive_list.h" #include "nvs_item_hash_list.hpp" +#include "partition.hpp" namespace nvs { @@ -77,17 +78,19 @@ public: INVALID = 0 }; + Page(); + PageState state() const { return mState; } - esp_err_t load(uint32_t sectorNumber); + esp_err_t load(Partition *partition, uint32_t sectorNumber); esp_err_t getSeqNumber(uint32_t& seqNumber) const; esp_err_t setSeqNumber(uint32_t seqNumber); - + esp_err_t setVersion(uint8_t version); esp_err_t writeItem(uint8_t nsIndex, ItemType datatype, const char* key, const void* data, size_t dataSize, uint8_t chunkIdx = CHUNK_ANY); @@ -188,7 +191,7 @@ protected: esp_err_t readEntry(size_t index, Item& dst) const; esp_err_t writeEntry(const Item& item); - + esp_err_t writeEntryData(const uint8_t* data, size_t size); esp_err_t eraseEntryAndSpan(size_t index); @@ -205,7 +208,7 @@ protected: assert(entry < ENTRY_COUNT); return mBaseAddress + ENTRY_DATA_OFFSET + static_cast(entry) * ENTRY_SIZE; } - + static const char* pageStateToName(PageState ps); @@ -223,6 +226,8 @@ protected: HashList mHashList; + Partition *mPartition; + static const uint32_t HEADER_OFFSET = 0; static const uint32_t ENTRY_TABLE_OFFSET = HEADER_OFFSET + 32; static const uint32_t ENTRY_DATA_OFFSET = ENTRY_TABLE_OFFSET + 32; diff --git a/components/nvs_flash/src/nvs_pagemanager.cpp b/components/nvs_flash/src/nvs_pagemanager.cpp index f9dc078839..88a830bd0f 100644 --- a/components/nvs_flash/src/nvs_pagemanager.cpp +++ b/components/nvs_flash/src/nvs_pagemanager.cpp @@ -15,8 +15,12 @@ namespace nvs { -esp_err_t PageManager::load(uint32_t baseSector, uint32_t sectorCount) +esp_err_t PageManager::load(Partition *partition, uint32_t baseSector, uint32_t sectorCount) { + if (partition == nullptr) { + return ESP_ERR_INVALID_ARG; + } + mBaseSector = baseSector; mPageCount = sectorCount; mPageList.clear(); @@ -26,7 +30,7 @@ esp_err_t PageManager::load(uint32_t baseSector, uint32_t sectorCount) if (!mPages) return ESP_ERR_NO_MEM; for (uint32_t i = 0; i < sectorCount; ++i) { - auto err = mPages[i].load(baseSector + i); + auto err = mPages[i].load(partition, baseSector + i); if (err != ESP_OK) { return err; } @@ -126,7 +130,7 @@ esp_err_t PageManager::load(uint32_t baseSector, uint32_t sectorCount) } // partition should have at least one free page - if (mFreePageList.size() == 0) { + if (mFreePageList.empty()) { return ESP_ERR_NVS_NO_FREE_PAGES; } diff --git a/components/nvs_flash/src/nvs_pagemanager.hpp b/components/nvs_flash/src/nvs_pagemanager.hpp index 8cc9f90427..4a1c19f7ec 100644 --- a/components/nvs_flash/src/nvs_pagemanager.hpp +++ b/components/nvs_flash/src/nvs_pagemanager.hpp @@ -18,7 +18,7 @@ #include #include "nvs_types.hpp" #include "nvs_page.hpp" -#include "nvs_pagemanager.hpp" +#include "partition.hpp" #include "intrusive_list.h" namespace nvs @@ -31,7 +31,7 @@ public: PageManager() {} - esp_err_t load(uint32_t baseSector, uint32_t sectorCount); + esp_err_t load(Partition *partition, uint32_t baseSector, uint32_t sectorCount); TPageListIterator begin() { diff --git a/components/nvs_flash/src/nvs_partition.cpp b/components/nvs_flash/src/nvs_partition.cpp new file mode 100644 index 0000000000..313c1697ae --- /dev/null +++ b/components/nvs_flash/src/nvs_partition.cpp @@ -0,0 +1,78 @@ +// Copyright 2019 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 "string.h" +#include "nvs_partition.hpp" + +namespace nvs { + +NVSPartition::NVSPartition(const esp_partition_t* partition) + : mESPPartition(partition) +{ + // ensure the class is in a valid state + if (partition == nullptr) { + abort(); + } +} + +const char *NVSPartition::get_partition_name() +{ + return mESPPartition->label; +} + +esp_err_t NVSPartition::read_raw(size_t src_offset, void* dst, size_t size) +{ + return esp_partition_read_raw(mESPPartition, src_offset, dst, size); +} + +esp_err_t NVSPartition::read(size_t src_offset, void* dst, size_t size) +{ + if (size % ESP_ENCRYPT_BLOCK_SIZE != 0) { + return ESP_ERR_INVALID_ARG; + } + + return esp_partition_read(mESPPartition, src_offset, dst, size); +} + +esp_err_t NVSPartition::write_raw(size_t dst_offset, const void* src, size_t size) +{ + return esp_partition_write_raw(mESPPartition, dst_offset, src, size); +} + +esp_err_t NVSPartition::write(size_t dst_offset, const void* src, size_t size) +{ + if (size % ESP_ENCRYPT_BLOCK_SIZE != 0) { + return ESP_ERR_INVALID_ARG; + } + + return esp_partition_write(mESPPartition, dst_offset, src, size); +} + +esp_err_t NVSPartition::erase_range(size_t dst_offset, size_t size) +{ + return esp_partition_erase_range(mESPPartition, dst_offset, size); +} + +uint32_t NVSPartition::get_address() +{ + return mESPPartition->address; +} + +uint32_t NVSPartition::get_size() +{ + return mESPPartition->size; +} + +} // nvs + diff --git a/components/nvs_flash/src/nvs_partition.hpp b/components/nvs_flash/src/nvs_partition.hpp new file mode 100644 index 0000000000..c469763c95 --- /dev/null +++ b/components/nvs_flash/src/nvs_partition.hpp @@ -0,0 +1,116 @@ +// Copyright 2019 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_HPP_ +#define ESP_PARTITION_HPP_ + +#include "esp_partition.h" +#include "intrusive_list.h" +#include "partition.hpp" + +#define ESP_ENCRYPT_BLOCK_SIZE 16 + +#define PART_NAME_MAX_SIZE 16 /*!< maximum length of partition name (excluding null terminator) */ + +namespace nvs { + +/** + * Implementation of Partition for NVS. + * + * It is implemented as an intrusive_list_node to easily store instances of it. NVSStorage and NVSPage take pointer + * references of this class to abstract their partition operations. + */ +class NVSPartition : public Partition, public intrusive_list_node { +public: + /** + * Copy partition_name to mPartitionName and initialize mESPPartition. + * + * @param partition_name the name of the partition as in the partition table, must be non-NULL! + * @param partition an already initialized partition structure + */ + NVSPartition(const esp_partition_t* partition); + + /** + * No need to de-initialize mESPPartition here, if you used esp_partition_find_first. + * Otherwise, the user is responsible for de-initializing it. + */ + virtual ~NVSPartition() { } + + const char *get_partition_name() override; + + /** + * Look into \c esp_partition_read_raw for more details. + * + * @return + * - ESP_OK on success + * - other error codes from the esp_partition API + */ + esp_err_t read_raw(size_t src_offset, void* dst, size_t size) override; + + /** + * Look into \c esp_partition_read for more details. + * + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_ARG if size isn't a multiple of ESP_ENCRYPT_BLOCK_SIZE + * - other error codes from the esp_partition API + */ + esp_err_t read(size_t src_offset, void* dst, size_t size) override; + + /** + * Look into \c esp_partition_write_raw for more details. + * + * @return + * - ESP_OK on success + * - error codes from the esp_partition API + */ + esp_err_t write_raw(size_t dst_offset, const void* src, size_t size) override; + + /** + * Look into \c esp_partition_write for more details. + * + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_ARG if size isn't a multiple of ESP_ENCRYPT_BLOCK_SIZE + * - other error codes from the esp_partition API + */ + esp_err_t write(size_t dst_offset, const void* src, size_t size) override; + + /** + * Look into \c esp_partition_erase_range for more details. + * + * @return + * - ESP_OK on success + * - error codes from the esp_partition API + */ + esp_err_t erase_range(size_t dst_offset, size_t size) override; + + /** + * @return the base address of the partition. + */ + uint32_t get_address() override; + + /** + * @return the size of the partition in bytes. + */ + uint32_t get_size() override; + +protected: + const esp_partition_t* mESPPartition; +}; + +} // nvs + +#endif // ESP_PARTITION_HPP_ + diff --git a/components/nvs_flash/src/nvs_partition_lookup.cpp b/components/nvs_flash/src/nvs_partition_lookup.cpp new file mode 100644 index 0000000000..579d8948bb --- /dev/null +++ b/components/nvs_flash/src/nvs_partition_lookup.cpp @@ -0,0 +1,65 @@ +#include "esp_partition.h" +#include "nvs_partition_lookup.hpp" + +#ifdef CONFIG_NVS_ENCRYPTION +#include "nvs_encrypted_partition.hpp" +#endif // CONFIG_NVS_ENCRYPTION + +namespace nvs { + +esp_err_t lookup_nvs_partition(const char* label, NVSPartition **p) +{ + const esp_partition_t* esp_partition = esp_partition_find_first( + ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, label); + + if (esp_partition == nullptr) { + return ESP_ERR_NOT_FOUND; + } + + if (esp_partition->encrypted) { + return ESP_ERR_NVS_WRONG_ENCRYPTION; + } + + NVSPartition *partition = new (std::nothrow) NVSPartition(esp_partition); + if (partition == nullptr) { + return ESP_ERR_NO_MEM; + } + + *p = partition; + + return ESP_OK; +} + +#ifdef CONFIG_NVS_ENCRYPTION +esp_err_t lookup_nvs_encrypted_partition(const char* label, nvs_sec_cfg_t* cfg, NVSPartition **p) +{ + const esp_partition_t* esp_partition = esp_partition_find_first( + ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, label); + + if (esp_partition == nullptr) { + return ESP_ERR_NOT_FOUND; + } + + if (esp_partition->encrypted) { + return ESP_ERR_NVS_WRONG_ENCRYPTION; + } + + NVSEncryptedPartition *enc_p = new (std::nothrow) NVSEncryptedPartition(esp_partition); + if (enc_p == nullptr) { + return ESP_ERR_NO_MEM; + } + + esp_err_t result = enc_p->init(cfg); + if (result != ESP_OK) { + delete enc_p; + return result; + } + + *p = enc_p; + + return ESP_OK; +} + +#endif // CONFIG_NVS_ENCRYPTION + +} // nvs diff --git a/components/nvs_flash/src/nvs_partition_lookup.hpp b/components/nvs_flash/src/nvs_partition_lookup.hpp new file mode 100644 index 0000000000..3d9c1249a3 --- /dev/null +++ b/components/nvs_flash/src/nvs_partition_lookup.hpp @@ -0,0 +1,18 @@ +#include "esp_err.h" +#include "nvs_partition.hpp" +#include "nvs_flash.h" + +#ifndef NVS_PARTITION_LOOKUP_HPP_ +#define NVS_PARTITION_LOOKUP_HPP_ + +namespace nvs { + +esp_err_t lookup_nvs_partition(const char* label, NVSPartition **p); + +#ifdef CONFIG_NVS_ENCRYPTION +esp_err_t lookup_nvs_encrypted_partition(const char* label, nvs_sec_cfg_t* cfg, NVSPartition **p); +#endif // CONFIG_NVS_ENCRYPTION + +} // nvs + +#endif // NVS_PARTITION_LOOKUP_HPP_ diff --git a/components/nvs_flash/src/nvs_partition_manager.cpp b/components/nvs_flash/src/nvs_partition_manager.cpp index 31108b486e..3bf14ea33f 100644 --- a/components/nvs_flash/src/nvs_partition_manager.cpp +++ b/components/nvs_flash/src/nvs_partition_manager.cpp @@ -13,6 +13,11 @@ // limitations under the License. #include "esp_partition.h" #include "nvs_partition_manager.hpp" +#include "nvs_partition_lookup.hpp" + +#ifdef CONFIG_NVS_ENCRYPTION +#include "nvs_encrypted_partition.hpp" +#endif // CONFIG_NVS_ENCRYPTION namespace nvs { @@ -30,6 +35,11 @@ NVSPartitionManager* NVSPartitionManager::get_instance() #ifdef ESP_PLATFORM esp_err_t NVSPartitionManager::init_partition(const char *partition_label) { + if (strlen(partition_label) > NVS_PART_NAME_MAX_SIZE) { + return ESP_ERR_INVALID_ARG; + } + + uint32_t size; Storage* mStorage; mStorage = lookup_storage_from_name(partition_label); @@ -39,33 +49,55 @@ esp_err_t NVSPartitionManager::init_partition(const char *partition_label) assert(SPI_FLASH_SEC_SIZE != 0); - const esp_partition_t* partition = esp_partition_find_first( - ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, partition_label); - if (partition == nullptr) { - return ESP_ERR_NOT_FOUND; + NVSPartition *p = nullptr; + esp_err_t result = lookup_nvs_partition(partition_label, &p); + + if (result != ESP_OK) { + goto error; } - return init_custom(partition_label, partition->address / SPI_FLASH_SEC_SIZE, - partition->size / SPI_FLASH_SEC_SIZE); + size = p->get_size(); + + result = init_custom(p, 0, size / SPI_FLASH_SEC_SIZE); + if (result != ESP_OK) { + goto error; + } + + nvs_partition_list.push_back(p); + + return ESP_OK; + +error: + delete p; + return result; } #endif // ESP_PLATFORM -esp_err_t NVSPartitionManager::init_custom(const char *partName, uint32_t baseSector, uint32_t sectorCount) +esp_err_t NVSPartitionManager::init_custom(Partition *partition, uint32_t baseSector, uint32_t sectorCount) { - if (strlen(partName) > NVS_PART_NAME_MAX_SIZE) return ESP_ERR_INVALID_ARG; + Storage* new_storage = nullptr; + Storage* storage = lookup_storage_from_name(partition->get_partition_name()); + if (storage == nullptr) { + new_storage = new (std::nothrow) Storage(partition); - Storage* new_storage = NULL; - Storage* storage = lookup_storage_from_name(partName); - if (storage == NULL) { - new_storage = new (std::nothrow) Storage((const char *)partName); - - if (!new_storage) return ESP_ERR_NO_MEM; + if (new_storage == nullptr) { + return ESP_ERR_NO_MEM; + } storage = new_storage; + } else { + // if storage was initialized already, we don't need partition and hence delete it + for (auto it = nvs_partition_list.begin(); it != nvs_partition_list.end(); ++it) { + if (partition == it) { + nvs_partition_list.erase(it); + delete partition; + break; + } + } } esp_err_t err = storage->init(baseSector, sectorCount); - if (new_storage != NULL) { + if (new_storage != nullptr) { if (err == ESP_OK) { nvs_storage_list.push_back(new_storage); } else { @@ -75,41 +107,47 @@ esp_err_t NVSPartitionManager::init_custom(const char *partName, uint32_t baseSe return err; } -#ifdef ESP_PLATFORM #ifdef CONFIG_NVS_ENCRYPTION +#ifdef ESP_PLATFORM esp_err_t NVSPartitionManager::secure_init_partition(const char *part_name, nvs_sec_cfg_t* cfg) { + if (strlen(part_name) > NVS_PART_NAME_MAX_SIZE) { + return ESP_ERR_INVALID_ARG; + } + Storage* mStorage; mStorage = lookup_storage_from_name(part_name); - if (mStorage) { + if (mStorage != nullptr) { return ESP_OK; } - const esp_partition_t* partition = esp_partition_find_first( - ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, part_name); - if (partition == NULL) { - return ESP_ERR_NOT_FOUND; + NVSPartition *p; + esp_err_t result; + if (cfg != nullptr) { + result = lookup_nvs_encrypted_partition(part_name, cfg, &p); + } else { + result = lookup_nvs_partition(part_name, &p); } - return secure_init_custom(part_name, partition->address / SPI_FLASH_SEC_SIZE, - partition->size / SPI_FLASH_SEC_SIZE, cfg); -} - -esp_err_t NVSPartitionManager::secure_init_custom(const char *partName, uint32_t baseSector, uint32_t sectorCount, nvs_sec_cfg_t* cfg) -{ - if(cfg) { - auto encrMgr = EncrMgr::getInstance(); - auto err = encrMgr->setSecurityContext(baseSector, sectorCount, cfg); - if(err != ESP_OK) { - return err; - } + if (result != ESP_OK) { + return result; } - return init_custom(partName, baseSector, sectorCount); + uint32_t size = p->get_size(); + + result = init_custom(p, 0, size / SPI_FLASH_SEC_SIZE); + if (result != ESP_OK) { + delete p; + return result; + } + + nvs_partition_list.push_back(p); + + return ESP_OK; } -#endif // CONFIG_NVS_ENCRYPTION #endif // ESP_PLATFORM +#endif // CONFIG_NVS_ENCRYPTION esp_err_t NVSPartitionManager::deinit_partition(const char *partition_label) { @@ -118,13 +156,6 @@ esp_err_t NVSPartitionManager::deinit_partition(const char *partition_label) return ESP_ERR_NVS_NOT_INITIALIZED; } -#ifdef CONFIG_NVS_ENCRYPTION - if(EncrMgr::isEncrActive()) { - auto encrMgr = EncrMgr::getInstance(); - encrMgr->removeSecurityContext(storage->getBaseSector()); - } -#endif - /* Clean up handles related to the storage being deinitialized */ for (auto it = nvs_handles.begin(); it != nvs_handles.end(); ++it) { if (it->mStoragePtr == storage) { @@ -133,10 +164,19 @@ esp_err_t NVSPartitionManager::deinit_partition(const char *partition_label) } } - /* Finally delete the storage itself */ + /* Finally delete the storage and its partition */ nvs_storage_list.erase(storage); delete storage; + for (auto it = nvs_partition_list.begin(); it != nvs_partition_list.end(); ++it) { + if (strcmp(it->get_partition_name(), partition_label) == 0) { + NVSPartition *p = it; + nvs_partition_list.erase(it); + delete p; + break; + } + } + return ESP_OK; } @@ -148,12 +188,12 @@ esp_err_t NVSPartitionManager::open_handle(const char *part_name, uint8_t nsIndex; Storage* sHandle; - if (nvs_storage_list.size() == 0) { + if (nvs_storage_list.empty()) { return ESP_ERR_NVS_NOT_INITIALIZED; } sHandle = lookup_storage_from_name(part_name); - if (sHandle == NULL) { + if (sHandle == nullptr) { return ESP_ERR_NVS_PART_NOT_FOUND; } @@ -164,7 +204,9 @@ esp_err_t NVSPartitionManager::open_handle(const char *part_name, *handle = new (std::nothrow) NVSHandleSimple(open_mode==NVS_READONLY, nsIndex, sHandle); - if (!handle) return ESP_ERR_NO_MEM; + if (handle == nullptr) { + return ESP_ERR_NO_MEM; + } nvs_handles.push_back(*handle); @@ -194,7 +236,7 @@ Storage* NVSPartitionManager::lookup_storage_from_name(const char* name) }); if (it == end(nvs_storage_list)) { - return NULL; + return nullptr; } return it; } diff --git a/components/nvs_flash/src/nvs_partition_manager.hpp b/components/nvs_flash/src/nvs_partition_manager.hpp index baa0c09d10..48f8f0f12f 100644 --- a/components/nvs_flash/src/nvs_partition_manager.hpp +++ b/components/nvs_flash/src/nvs_partition_manager.hpp @@ -16,10 +16,8 @@ #include "nvs_handle_simple.hpp" #include "nvs_storage.hpp" - -#ifdef CONFIG_NVS_ENCRYPTION -#include "nvs_encr.hpp" -#endif +#include "nvs_partition.hpp" +#include "nvs_flash.h" namespace nvs { @@ -31,12 +29,10 @@ public: esp_err_t init_partition(const char *partition_label); - esp_err_t init_custom(const char *partName, uint32_t baseSector, uint32_t sectorCount); + esp_err_t init_custom(Partition *partition, uint32_t baseSector, uint32_t sectorCount); #ifdef CONFIG_NVS_ENCRYPTION esp_err_t secure_init_partition(const char *part_name, nvs_sec_cfg_t* cfg); - - esp_err_t secure_init_custom(const char *partName, uint32_t baseSector, uint32_t sectorCount, nvs_sec_cfg_t* cfg); #endif esp_err_t deinit_partition(const char *partition_label); @@ -57,6 +53,8 @@ protected: intrusive_list nvs_handles; intrusive_list nvs_storage_list; + + intrusive_list nvs_partition_list; }; } // nvs diff --git a/components/nvs_flash/src/nvs_storage.cpp b/components/nvs_flash/src/nvs_storage.cpp index 42e8e2316e..88176c1045 100644 --- a/components/nvs_flash/src/nvs_storage.cpp +++ b/components/nvs_flash/src/nvs_storage.cpp @@ -90,7 +90,7 @@ void Storage::eraseOrphanDataBlobs(TBlobIndexList& blobIdxList) esp_err_t Storage::init(uint32_t baseSector, uint32_t sectorCount) { - auto err = mPageManager.load(baseSector, sectorCount); + auto err = mPageManager.load(mPartition, baseSector, sectorCount); if (err != ESP_OK) { mState = StorageState::INVALID; return err; diff --git a/components/nvs_flash/src/nvs_storage.hpp b/components/nvs_flash/src/nvs_storage.hpp index 9e17c7179c..fd6eb1cc9f 100644 --- a/components/nvs_flash/src/nvs_storage.hpp +++ b/components/nvs_flash/src/nvs_storage.hpp @@ -20,6 +20,7 @@ #include "nvs_types.hpp" #include "nvs_page.hpp" #include "nvs_pagemanager.hpp" +#include "partition.hpp" //extern void dumpBytes(const uint8_t* data, size_t count); @@ -60,9 +61,10 @@ class Storage : public intrusive_list_node public: ~Storage(); - Storage(const char *pName = NVS_DEFAULT_PART_NAME) - { - strncpy(mPartitionName, pName, NVS_PART_NAME_MAX_SIZE); + Storage(Partition *partition) : mPartition(partition) { + if (partition == nullptr) { + abort(); + } }; esp_err_t init(uint32_t baseSector, uint32_t sectorCount); @@ -98,10 +100,16 @@ public: esp_err_t eraseNamespace(uint8_t nsIndex); + const Partition *getPart() const + { + return mPartition; + } + const char *getPartName() const { - return mPartitionName; + return mPartition->get_partition_name(); } + uint32_t getBaseSector() { return mPageManager.getBaseSector(); @@ -145,7 +153,7 @@ protected: esp_err_t findItem(uint8_t nsIndex, ItemType datatype, const char* key, Page* &page, Item& item, uint8_t chunkIdx = Page::CHUNK_ANY, VerOffset chunkStart = VerOffset::VER_ANY); protected: - char mPartitionName [NVS_PART_NAME_MAX_SIZE + 1]; + Partition *mPartition; size_t mPageCount; PageManager mPageManager; TNamespaces mNamespaces; diff --git a/components/nvs_flash/src/partition.hpp b/components/nvs_flash/src/partition.hpp new file mode 100644 index 0000000000..9947c9eda1 --- /dev/null +++ b/components/nvs_flash/src/partition.hpp @@ -0,0 +1,60 @@ +// Copyright 2019 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 PARTITION_HPP_ +#define PARTITION_HPP_ + +#include "esp_err.h" + +namespace nvs { + +/** + * @brief Abstract interface for partition related operations, currently in NVS. + * + * It resembles the main operations according to esp_partition.h. + */ +class Partition { +public: + virtual ~Partition() { } + + /** + * Return the partition name as in the partition table. + */ + virtual const char *get_partition_name() = 0; + + virtual esp_err_t read_raw(size_t src_offset, void* dst, size_t size) = 0; + + virtual esp_err_t read(size_t src_offset, void* dst, size_t size) = 0; + + virtual esp_err_t write_raw(size_t dst_offset, const void* src, size_t size) = 0; + + virtual esp_err_t write(size_t dst_offset, const void* src, size_t size) = 0; + + virtual esp_err_t erase_range(size_t dst_offset, size_t size) = 0; + + /** + * Return the address of the beginning of the partition. + */ + virtual uint32_t get_address() = 0; + + /** + * Return the partition size in bytes. + */ + virtual uint32_t get_size() = 0; +}; + +} // nvs + +#endif // PARTITION_HPP_ + diff --git a/components/nvs_flash/test/CMakeLists.txt b/components/nvs_flash/test/CMakeLists.txt index a354cf59cf..f69580398b 100644 --- a/components/nvs_flash/test/CMakeLists.txt +++ b/components/nvs_flash/test/CMakeLists.txt @@ -1,3 +1,4 @@ idf_component_register(SRC_DIRS "." PRIV_INCLUDE_DIRS "." - PRIV_REQUIRES cmock test_utils nvs_flash bootloader_support) + PRIV_REQUIRES cmock test_utils nvs_flash bootloader_support + EMBED_TXTFILES encryption_keys.bin partition_encrypted.bin sample.bin) diff --git a/components/nvs_flash/test/test_nvs.c b/components/nvs_flash/test/test_nvs.c index 529147e497..b32fa93baf 100644 --- a/components/nvs_flash/test/test_nvs.c +++ b/components/nvs_flash/test/test_nvs.c @@ -18,6 +18,15 @@ static const char* TAG = "test_nvs"; +TEST_CASE("Partition name no longer than 16 characters", "[nvs]") +{ + const char *TOO_LONG_NAME = "0123456789abcdefg"; + + TEST_ESP_ERR(ESP_ERR_INVALID_ARG, nvs_flash_init_partition(TOO_LONG_NAME)); + + nvs_flash_deinit_partition(TOO_LONG_NAME); // just in case +} + TEST_CASE("flash erase deinitializes initialized partition", "[nvs]") { nvs_handle_t handle; @@ -26,7 +35,7 @@ TEST_CASE("flash erase deinitializes initialized partition", "[nvs]") nvs_flash_erase(); err = nvs_flash_init(); } - ESP_ERROR_CHECK( err ); + TEST_ESP_OK( err ); TEST_ESP_OK(nvs_flash_init()); TEST_ESP_OK(nvs_open("uninit_ns", NVS_READWRITE, &handle)); @@ -468,7 +477,7 @@ TEST_CASE("test nvs apis for nvs partition generator utility with encryption ena } for (int i = 0; i < nvs_part->size; i+= SPI_FLASH_SEC_SIZE) { - ESP_ERROR_CHECK( spi_flash_write(nvs_part->address + i, nvs_data_start + i, SPI_FLASH_SEC_SIZE) ); + ESP_ERROR_CHECK( esp_partition_write(nvs_part, i, nvs_data_start + i, SPI_FLASH_SEC_SIZE) ); } esp_err_t err = nvs_flash_read_security_cfg(key_part, &xts_cfg); diff --git a/components/nvs_flash/test_nvs_host/Makefile b/components/nvs_flash/test_nvs_host/Makefile index 430eecdc83..c04ae3a424 100644 --- a/components/nvs_flash/test_nvs_host/Makefile +++ b/components/nvs_flash/test_nvs_host/Makefile @@ -10,11 +10,11 @@ SOURCE_FILES = \ nvs_pagemanager.cpp \ nvs_storage.cpp \ nvs_item_hash_list.cpp \ - nvs_encr.cpp \ - nvs_ops.cpp \ nvs_handle_simple.cpp \ nvs_handle_locked.cpp \ nvs_partition_manager.cpp \ + nvs_partition.cpp \ + nvs_encrypted_partition.cpp \ nvs_cxx_api.cpp \ ) \ spi_flash_emulation.cpp \ @@ -25,6 +25,7 @@ SOURCE_FILES = \ test_partition_manager.cpp \ test_nvs_handle.cpp \ test_nvs_storage.cpp \ + test_nvs_partition.cpp \ test_nvs_cxx_api.cpp \ test_nvs_initialization.cpp \ crc.cpp \ @@ -36,7 +37,7 @@ else COMPILER := gcc endif -CPPFLAGS += -I../include -I../src -I./ -I../../esp_common/include -I../../esp32/include -I ../../mbedtls/mbedtls/include -I ../../spi_flash/include -I ../../hal/include -I ../../xtensa/include -I ../../../tools/catch -fprofile-arcs -ftest-coverage +CPPFLAGS += -I../include -I../src -I./ -I../../esp_common/include -I../../esp32/include -I ../../mbedtls/mbedtls/include -I ../../spi_flash/include -I ../../hal/include -I ../../xtensa/include -I ../../../tools/catch -fprofile-arcs -ftest-coverage -g2 -ggdb CFLAGS += -fprofile-arcs -ftest-coverage CXXFLAGS += -std=c++11 -Wall -Werror LDFLAGS += -lstdc++ -Wall -fprofile-arcs -ftest-coverage @@ -89,6 +90,8 @@ clean: clean-coverage rm -f ../nvs_partition_generator/partition_encrypted.bin rm -f ../nvs_partition_generator/partition_encrypted_using_keygen.bin rm -f ../nvs_partition_generator/partition_encrypted_using_keyfile.bin + rm -f ../nvs_partition_generator/partition_decrypted.bin + rm -f ../nvs_partition_generator/partition_encoded.bin rm -f ../nvs_partition_generator/Test-1-partition-encrypted.bin rm -f ../nvs_partition_generator/Test-1-partition.bin rm -f ../../../tools/mass_mfg/samples/sample_values_multipage_blob_created.csv diff --git a/components/nvs_flash/test_nvs_host/spi_flash_emulation.cpp b/components/nvs_flash/test_nvs_host/spi_flash_emulation.cpp index 9881203083..581e35e1e9 100644 --- a/components/nvs_flash/test_nvs_host/spi_flash_emulation.cpp +++ b/components/nvs_flash/test_nvs_host/spi_flash_emulation.cpp @@ -11,7 +11,7 @@ // 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 "esp_spi_flash.h" +#include "esp_partition.h" #include "spi_flash_emulation.h" @@ -22,39 +22,82 @@ void spi_flash_emulator_set(SpiFlashEmulator* e) s_emulator = e; } -esp_err_t spi_flash_erase_sector(size_t sec) +esp_err_t esp_partition_erase_range(const esp_partition_t* partition, + size_t offset, size_t size) { if (!s_emulator) { return ESP_ERR_FLASH_OP_TIMEOUT; } - if (!s_emulator->erase(sec)) { + if (size % SPI_FLASH_SEC_SIZE != 0) { + return ESP_ERR_INVALID_SIZE; + } + + if (offset % SPI_FLASH_SEC_SIZE != 0) { + return ESP_ERR_INVALID_ARG; + } + + size_t start_sector = offset / SPI_FLASH_SEC_SIZE; + size_t num_sectors = size / SPI_FLASH_SEC_SIZE; + for (size_t sector = start_sector; sector < (start_sector + num_sectors); sector++) { + if (!s_emulator->erase(sector)) { + return ESP_ERR_FLASH_OP_FAIL; + } + } + + return ESP_OK; +} + +esp_err_t esp_partition_read(const esp_partition_t* partition, + size_t src_offset, void* dst, size_t size) +{ + if (!s_emulator) { + return ESP_ERR_FLASH_OP_TIMEOUT; + } + + if (!s_emulator->read(reinterpret_cast(dst), src_offset, size)) { return ESP_ERR_FLASH_OP_FAIL; } return ESP_OK; } -esp_err_t spi_flash_write(size_t des_addr, const void *src_addr, size_t size) +esp_err_t esp_partition_read_raw(const esp_partition_t* partition, + size_t src_offset, void* dst, size_t size) { if (!s_emulator) { return ESP_ERR_FLASH_OP_TIMEOUT; } - if (!s_emulator->write(des_addr, reinterpret_cast(src_addr), size)) { + if (!s_emulator->read(reinterpret_cast(dst), src_offset, size)) { return ESP_ERR_FLASH_OP_FAIL; } return ESP_OK; } -esp_err_t spi_flash_read(size_t src_addr, void *des_addr, size_t size) +esp_err_t esp_partition_write(const esp_partition_t* partition, + size_t dst_offset, const void* src, size_t size) { if (!s_emulator) { return ESP_ERR_FLASH_OP_TIMEOUT; } - if (!s_emulator->read(reinterpret_cast(des_addr), src_addr, size)) { + if (!s_emulator->write(dst_offset, reinterpret_cast(src), size)) { + return ESP_ERR_FLASH_OP_FAIL; + } + + return ESP_OK; +} + +esp_err_t esp_partition_write_raw(const esp_partition_t* partition, + size_t dst_offset, const void* src, size_t size) +{ + if (!s_emulator) { + return ESP_ERR_FLASH_OP_TIMEOUT; + } + + if (!s_emulator->write(dst_offset, reinterpret_cast(src), size)) { return ESP_ERR_FLASH_OP_FAIL; } diff --git a/components/nvs_flash/test_nvs_host/test_fixtures.hpp b/components/nvs_flash/test_nvs_host/test_fixtures.hpp new file mode 100644 index 0000000000..473f1da973 --- /dev/null +++ b/components/nvs_flash/test_nvs_host/test_fixtures.hpp @@ -0,0 +1,149 @@ +// 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 "nvs_partition.hpp" +#include "nvs_encrypted_partition.hpp" +#include "spi_flash_emulation.h" +#include "nvs.h" + +class PartitionEmulation : public nvs::Partition { +public: + PartitionEmulation(SpiFlashEmulator *spi_flash_emulator, + uint32_t address, + uint32_t size, + const char *partition_name = NVS_DEFAULT_PART_NAME) + : partition_name(partition_name), flash_emu(spi_flash_emulator), address(address), size(size) + { + assert(partition_name); + assert(flash_emu); + assert(size); + } + + const char *get_partition_name() override + { + return partition_name; + } + + esp_err_t read_raw(size_t src_offset, void* dst, size_t size) override + { + if (!flash_emu->read(reinterpret_cast(dst), src_offset, size)) { + return ESP_ERR_FLASH_OP_FAIL; + } + + return ESP_OK; + } + + esp_err_t read(size_t src_offset, void* dst, size_t size) override + { + if (!flash_emu->read(reinterpret_cast(dst), src_offset, size)) { + return ESP_ERR_FLASH_OP_FAIL; + } + + return ESP_OK; + } + + esp_err_t write_raw(size_t dst_offset, const void* src, size_t size) override + { + if (!flash_emu->write(dst_offset, reinterpret_cast(src), size)) { + return ESP_ERR_FLASH_OP_FAIL; + } + + return ESP_OK; + } + + esp_err_t write(size_t dst_offset, const void* src, size_t size) override + { + if (!flash_emu->write(dst_offset, reinterpret_cast(src), size)) { + return ESP_ERR_FLASH_OP_FAIL; + } + + return ESP_OK; + } + + esp_err_t erase_range(size_t dst_offset, size_t size) override + { + if (size % SPI_FLASH_SEC_SIZE != 0) { + return ESP_ERR_INVALID_SIZE; + } + + if (dst_offset % SPI_FLASH_SEC_SIZE != 0) { + return ESP_ERR_INVALID_ARG; + } + + size_t start_sector = dst_offset / SPI_FLASH_SEC_SIZE; + size_t num_sectors = size / SPI_FLASH_SEC_SIZE; + for (size_t sector = start_sector; sector < (start_sector + num_sectors); sector++) { + if (!flash_emu->erase(sector)) { + return ESP_ERR_FLASH_OP_FAIL; + } + } + + return ESP_OK; + } + + uint32_t get_address() override + { + return address; + } + + uint32_t get_size() override + { + return size; + } + +private: + const char *partition_name; + + SpiFlashEmulator *flash_emu; + + uint32_t address; + + uint32_t size; +}; + +struct PartitionEmulationFixture { + PartitionEmulationFixture(uint32_t start_sector = 0, + uint32_t sector_size = 1, + const char *partition_name = NVS_DEFAULT_PART_NAME) + : emu(start_sector + sector_size), + part(&emu, start_sector * SPI_FLASH_SEC_SIZE, sector_size * SPI_FLASH_SEC_SIZE, partition_name) { + } + + ~PartitionEmulationFixture() { } + + SpiFlashEmulator emu; + + PartitionEmulation part; +}; + +struct EncryptedPartitionFixture { + EncryptedPartitionFixture(nvs_sec_cfg_t *cfg, + uint32_t start_sector = 0, + uint32_t sector_size = 1, + const char *partition_name = NVS_DEFAULT_PART_NAME) + : esp_partition(), emu(start_sector + sector_size), + part(&esp_partition) { + esp_partition.address = start_sector * SPI_FLASH_SEC_SIZE; + esp_partition.size = sector_size * SPI_FLASH_SEC_SIZE; + strncpy(esp_partition.label, partition_name, PART_NAME_MAX_SIZE); + assert(part.init(cfg) == ESP_OK); + } + + ~EncryptedPartitionFixture() { } + + esp_partition_t esp_partition; + + SpiFlashEmulator emu; + + nvs::NVSEncryptedPartition part; +}; diff --git a/components/nvs_flash/test_nvs_host/test_nvs.cpp b/components/nvs_flash/test_nvs_host/test_nvs.cpp index 279ecd2961..778f05eef7 100644 --- a/components/nvs_flash/test_nvs_host/test_nvs.cpp +++ b/components/nvs_flash/test_nvs_host/test_nvs.cpp @@ -15,10 +15,10 @@ #include "nvs.hpp" #include "nvs_test_api.h" #include "sdkconfig.h" -#ifdef CONFIG_NVS_ENCRYPTION -#include "nvs_encr.hpp" -#endif #include "spi_flash_emulation.h" +#include "nvs_partition_manager.hpp" +#include "nvs_partition.hpp" +#include "mbedtls/aes.h" #include #include #include @@ -28,6 +28,8 @@ #include #include +#include "test_fixtures.hpp" + #define TEST_ESP_ERR(rc, res) CHECK((rc) == (res)) #define TEST_ESP_OK(rc) CHECK((rc) == ESP_OK) @@ -79,20 +81,20 @@ TEST_CASE("crc32 behaves as expected", "[nvs]") CHECK(crc32_1 != item2.calculateCrc32()); } -TEST_CASE("starting with empty flash, page is in uninitialized state", "[nvs]") +TEST_CASE("Page starting with empty flash is in uninitialized state", "[nvs]") { - SpiFlashEmulator emu(1); + PartitionEmulationFixture f; Page page; CHECK(page.state() == Page::PageState::INVALID); - CHECK(page.load(0) == ESP_OK); + CHECK(page.load(&f.part, 0) == ESP_OK); CHECK(page.state() == Page::PageState::UNINITIALIZED); } -TEST_CASE("can distinguish namespaces", "[nvs]") +TEST_CASE("Page can distinguish namespaces", "[nvs]") { - SpiFlashEmulator emu(1); + PartitionEmulationFixture f; Page page; - CHECK(page.load(0) == ESP_OK); + CHECK(page.load(&f.part, 0) == ESP_OK); int32_t val1 = 0x12345678; CHECK(page.writeItem(1, ItemType::I32, "intval1", &val1, sizeof(val1)) == ESP_OK); int32_t val2 = 0x23456789; @@ -104,32 +106,32 @@ TEST_CASE("can distinguish namespaces", "[nvs]") } -TEST_CASE("reading with different type causes type mismatch error", "[nvs]") +TEST_CASE("Page reading with different type causes type mismatch error", "[nvs]") { - SpiFlashEmulator emu(1); + PartitionEmulationFixture f; Page page; - CHECK(page.load(0) == ESP_OK); + CHECK(page.load(&f.part, 0) == ESP_OK); int32_t val = 0x12345678; CHECK(page.writeItem(1, ItemType::I32, "intval1", &val, sizeof(val)) == ESP_OK); CHECK(page.readItem(1, ItemType::U32, "intval1", &val, sizeof(val)) == ESP_ERR_NVS_TYPE_MISMATCH); } -TEST_CASE("when page is erased, it's state becomes UNITIALIZED", "[nvs]") +TEST_CASE("Page when erased, it's state becomes UNITIALIZED", "[nvs]") { - SpiFlashEmulator emu(1); + PartitionEmulationFixture f; Page page; - CHECK(page.load(0) == ESP_OK); + CHECK(page.load(&f.part, 0) == ESP_OK); int32_t val = 0x12345678; CHECK(page.writeItem(1, ItemType::I32, "intval1", &val, sizeof(val)) == ESP_OK); CHECK(page.erase() == ESP_OK); CHECK(page.state() == Page::PageState::UNINITIALIZED); } -TEST_CASE("when writing and erasing, used/erased counts are updated correctly", "[nvs]") +TEST_CASE("Page when writing and erasing, used/erased counts are updated correctly", "[nvs]") { - SpiFlashEmulator emu(1); + PartitionEmulationFixture f; Page page; - CHECK(page.load(0) == ESP_OK); + CHECK(page.load(&f.part, 0) == ESP_OK); CHECK(page.getUsedEntryCount() == 0); CHECK(page.getErasedEntryCount() == 0); uint32_t foo1 = 0; @@ -156,11 +158,11 @@ TEST_CASE("when writing and erasing, used/erased counts are updated correctly", CHECK(page.getErasedEntryCount() == Page::ENTRY_COUNT - 1); } -TEST_CASE("when page is full, adding an element fails", "[nvs]") +TEST_CASE("Page when page is full, adding an element fails", "[nvs]") { - SpiFlashEmulator emu(1); + PartitionEmulationFixture f; Page page; - CHECK(page.load(0) == ESP_OK); + CHECK(page.load(&f.part, 0) == ESP_OK); for (size_t i = 0; i < Page::ENTRY_COUNT; ++i) { char name[16]; snprintf(name, sizeof(name), "i%ld", (long int)i); @@ -169,30 +171,30 @@ TEST_CASE("when page is full, adding an element fails", "[nvs]") CHECK(page.writeItem(1, "foo", 64UL) == ESP_ERR_NVS_PAGE_FULL); } -TEST_CASE("page maintains its seq number") +TEST_CASE("Page maintains its seq number") { - SpiFlashEmulator emu(1); + PartitionEmulationFixture f; { Page page; - CHECK(page.load(0) == ESP_OK); + CHECK(page.load(&f.part, 0) == ESP_OK); CHECK(page.setSeqNumber(123) == ESP_OK); int32_t val = 42; CHECK(page.writeItem(1, ItemType::I32, "dummy", &val, sizeof(val)) == ESP_OK); } { Page page; - CHECK(page.load(0) == ESP_OK); + CHECK(page.load(&f.part, 0) == ESP_OK); uint32_t seqno; CHECK(page.getSeqNumber(seqno) == ESP_OK); CHECK(seqno == 123); } } -TEST_CASE("can write and read variable length data", "[nvs]") +TEST_CASE("Page can write and read variable length data", "[nvs]") { - SpiFlashEmulator emu(1); + PartitionEmulationFixture f; Page page; - CHECK(page.load(0) == ESP_OK); + CHECK(page.load(&f.part, 0) == ESP_OK); const char str[] = "foobar1234foobar1234foobar1234foobar1234foobar1234foobar1234foobar1234foobar1234"; size_t len = strlen(str); CHECK(page.writeItem(1, "stuff1", 42) == ESP_OK); @@ -222,11 +224,11 @@ TEST_CASE("can write and read variable length data", "[nvs]") CHECK(memcmp(buf, str, strlen(str)) == 0); } -TEST_CASE("different key names are distinguished even if the pointer is the same", "[nvs]") +TEST_CASE("Page different key names are distinguished even if the pointer is the same", "[nvs]") { - SpiFlashEmulator emu(1); + PartitionEmulationFixture f; Page page; - TEST_ESP_OK(page.load(0)); + TEST_ESP_OK(page.load(&f.part, 0)); TEST_ESP_OK(page.writeItem(1, "i1", 1)); TEST_ESP_OK(page.writeItem(1, "i2", 2)); int32_t value; @@ -243,9 +245,9 @@ TEST_CASE("different key names are distinguished even if the pointer is the same TEST_CASE("Page validates key size", "[nvs]") { - SpiFlashEmulator emu(4); + PartitionEmulationFixture f(0, 4); Page page; - TEST_ESP_OK(page.load(0)); + TEST_ESP_OK(page.load(&f.part, 0)); // 16-character key fails TEST_ESP_ERR(page.writeItem(1, "0123456789123456", 1), ESP_ERR_NVS_KEY_TOO_LONG); // 15-character key is okay @@ -254,9 +256,9 @@ TEST_CASE("Page validates key size", "[nvs]") TEST_CASE("Page validates blob size", "[nvs]") { - SpiFlashEmulator emu(4); + PartitionEmulationFixture f(0, 4); Page page; - TEST_ESP_OK(page.load(0)); + TEST_ESP_OK(page.load(&f.part, 0)); char buf[4096] = { 0 }; // There are two potential errors here: @@ -271,20 +273,20 @@ TEST_CASE("Page validates blob size", "[nvs]") TEST_CASE("Page handles invalid CRC of variable length items", "[nvs][cur]") { - SpiFlashEmulator emu(4); + PartitionEmulationFixture f(0, 4); { Page page; - TEST_ESP_OK(page.load(0)); + TEST_ESP_OK(page.load(&f.part, 0)); char buf[128] = {0}; TEST_ESP_OK(page.writeItem(1, ItemType::BLOB, "1", buf, sizeof(buf))); } // corrupt header of the item (64 is the offset of the first item in page) uint32_t overwrite_buf = 0; - emu.write(64, &overwrite_buf, 4); + f.emu.write(64, &overwrite_buf, 4); // load page again { Page page; - TEST_ESP_OK(page.load(0)); + TEST_ESP_OK(page.load(&f.part, 0)); } } @@ -331,20 +333,20 @@ TEST_CASE("HashList is cleaned up as soon as items are erased", "[nvs]") TEST_CASE("can init PageManager in empty flash", "[nvs]") { - SpiFlashEmulator emu(4); + PartitionEmulationFixture f(0, 4); PageManager pm; - CHECK(pm.load(0, 4) == ESP_OK); + CHECK(pm.load(&f.part, 0, 4) == ESP_OK); } TEST_CASE("PageManager adds page in the correct order", "[nvs]") { const size_t pageCount = 8; - SpiFlashEmulator emu(pageCount); + PartitionEmulationFixture f(0, pageCount); uint32_t pageNo[pageCount] = { -1U, 50, 11, -1U, 23, 22, 24, 49}; for (uint32_t i = 0; i < pageCount; ++i) { Page p; - p.load(i); + p.load(&f.part, i); if (pageNo[i] != -1U) { p.setSeqNumber(pageNo[i]); p.writeItem(1, "foo", 10U); @@ -352,7 +354,7 @@ TEST_CASE("PageManager adds page in the correct order", "[nvs]") } PageManager pageManager; - CHECK(pageManager.load(0, pageCount) == ESP_OK); + CHECK(pageManager.load(&f.part, 0, pageCount) == ESP_OK); uint32_t lastSeqNo = 0; for (auto it = std::begin(pageManager); it != std::end(pageManager); ++it) { @@ -364,46 +366,47 @@ TEST_CASE("PageManager adds page in the correct order", "[nvs]") TEST_CASE("can init storage in empty flash", "[nvs]") { - SpiFlashEmulator emu(8); - Storage storage; - emu.setBounds(4, 8); + PartitionEmulationFixture f(0, 8); + Storage storage(&f.part); + f.emu.setBounds(4, 8); + cout << "before check" << endl; CHECK(storage.init(4, 4) == ESP_OK); - s_perf << "Time to init empty storage (4 sectors): " << emu.getTotalTime() << " us" << std::endl; + s_perf << "Time to init empty storage (4 sectors): " << f.emu.getTotalTime() << " us" << std::endl; } TEST_CASE("storage doesn't add duplicates within one page", "[nvs]") { - SpiFlashEmulator emu(8); - Storage storage; - emu.setBounds(4, 8); + PartitionEmulationFixture f(0, 8); + Storage storage(&f.part); + f.emu.setBounds(4, 8); CHECK(storage.init(4, 4) == ESP_OK); int bar = 0; CHECK(storage.writeItem(1, "bar", ++bar) == ESP_OK); CHECK(storage.writeItem(1, "bar", ++bar) == ESP_OK); Page page; - page.load(4); + page.load(&f.part, 4); CHECK(page.getUsedEntryCount() == 1); CHECK(page.getErasedEntryCount() == 1); } TEST_CASE("can write one item a thousand times", "[nvs]") { - SpiFlashEmulator emu(8); - Storage storage; - emu.setBounds(4, 8); + PartitionEmulationFixture f(0, 8); + Storage storage(&f.part); + f.emu.setBounds(4, 8); CHECK(storage.init(4, 4) == ESP_OK); for (size_t i = 0; i < Page::ENTRY_COUNT * 4 * 2; ++i) { REQUIRE(storage.writeItem(1, "i", static_cast(i)) == ESP_OK); } - s_perf << "Time to write one item a thousand times: " << emu.getTotalTime() << " us (" << emu.getEraseOps() << " " << emu.getWriteOps() << " " << emu.getReadOps() << " " << emu.getWriteBytes() << " " << emu.getReadBytes() << ")" << std::endl; + s_perf << "Time to write one item a thousand times: " << f.emu.getTotalTime() << " us (" << f.emu.getEraseOps() << " " << f.emu.getWriteOps() << " " << f.emu.getReadOps() << " " << f.emu.getWriteBytes() << " " << f.emu.getReadBytes() << ")" << std::endl; } TEST_CASE("storage doesn't add duplicates within multiple pages", "[nvs]") { - SpiFlashEmulator emu(8); - Storage storage; - emu.setBounds(4, 8); + PartitionEmulationFixture f(0, 8); + Storage storage(&f.part); + f.emu.setBounds(4, 8); CHECK(storage.init(4, 4) == ESP_OK); int bar = 0; CHECK(storage.writeItem(1, "bar", ++bar) == ESP_OK); @@ -413,16 +416,16 @@ TEST_CASE("storage doesn't add duplicates within multiple pages", "[nvs]") CHECK(storage.writeItem(1, "bar", ++bar) == ESP_OK); Page page; - page.load(4); + page.load(&f.part, 4); CHECK(page.findItem(1, itemTypeOf(), "bar") == ESP_ERR_NVS_NOT_FOUND); - page.load(5); + page.load(&f.part, 5); CHECK(page.findItem(1, itemTypeOf(), "bar") == ESP_OK); } TEST_CASE("storage can find items on second page if first is not fully written and has cached search data", "[nvs]") { - SpiFlashEmulator emu(3); - Storage storage; + PartitionEmulationFixture f(0, 3); + Storage storage(&f.part); CHECK(storage.init(0, 3) == ESP_OK); int bar = 0; uint8_t bigdata[(Page::CHUNK_MAX_SIZE - Page::ENTRY_SIZE)/2] = {0}; @@ -444,9 +447,9 @@ TEST_CASE("storage can find items on second page if first is not fully written a TEST_CASE("can write and read variable length data lots of times", "[nvs]") { - SpiFlashEmulator emu(8); - Storage storage; - emu.setBounds(4, 8); + PartitionEmulationFixture f(0, 8); + Storage storage(&f.part); + f.emu.setBounds(4, 8); CHECK(storage.init(4, 4) == ESP_OK); const char str[] = "foobar1234foobar1234foobar1234foobar1234foobar1234foobar1234foobar1234foobar1234"; char buf[sizeof(str) + 16]; @@ -464,16 +467,16 @@ TEST_CASE("can write and read variable length data lots of times", "[nvs]") CHECK(storage.readItem(1, ItemType::SZ, "foobaar", buf, sizeof(buf)) == ESP_OK); CHECK(memcmp(buf, str, strlen(str) + 1) == 0); } - s_perf << "Time to write one string and one integer a thousand times: " << emu.getTotalTime() << " us (" << emu.getEraseOps() << " " << emu.getWriteOps() << " " << emu.getReadOps() << " " << emu.getWriteBytes() << " " << emu.getReadBytes() << ")" << std::endl; + s_perf << "Time to write one string and one integer a thousand times: " << f.emu.getTotalTime() << " us (" << f.emu.getEraseOps() << " " << f.emu.getWriteOps() << " " << f.emu.getReadOps() << " " << f.emu.getWriteBytes() << " " << f.emu.getReadBytes() << ")" << std::endl; } TEST_CASE("can get length of variable length data", "[nvs]") { - SpiFlashEmulator emu(8); - emu.randomize(200); - Storage storage; - emu.setBounds(4, 8); + PartitionEmulationFixture f(0, 8); + f.emu.randomize(200); + Storage storage(&f.part); + f.emu.setBounds(4, 8); CHECK(storage.init(4, 4) == ESP_OK); const char str[] = "foobar1234foobar1234foobar1234foobar1234foobar1234foobar1234foobar1234foobar1234"; size_t len = strlen(str); @@ -490,24 +493,24 @@ TEST_CASE("can get length of variable length data", "[nvs]") TEST_CASE("can create namespaces", "[nvs]") { - SpiFlashEmulator emu(8); - Storage storage; - emu.setBounds(4, 8); + PartitionEmulationFixture f(0, 8); + Storage storage(&f.part); + f.emu.setBounds(4, 8); CHECK(storage.init(4, 4) == ESP_OK); uint8_t nsi; CHECK(storage.createOrOpenNamespace("wifi", false, nsi) == ESP_ERR_NVS_NOT_FOUND); CHECK(storage.createOrOpenNamespace("wifi", true, nsi) == ESP_OK); Page page; - page.load(4); + page.load(&f.part, 4); CHECK(page.findItem(Page::NS_INDEX, ItemType::U8, "wifi") == ESP_OK); } TEST_CASE("storage may become full", "[nvs]") { - SpiFlashEmulator emu(8); - Storage storage; - emu.setBounds(4, 8); + PartitionEmulationFixture f(0, 8); + Storage storage(&f.part); + f.emu.setBounds(4, 8); CHECK(storage.init(4, 4) == ESP_OK); for (size_t i = 0; i < Page::ENTRY_COUNT * 3; ++i) { char name[Item::MAX_KEY_LENGTH + 1]; @@ -519,8 +522,8 @@ TEST_CASE("storage may become full", "[nvs]") TEST_CASE("can modify an item on a page which will be erased", "[nvs]") { - SpiFlashEmulator emu(2); - Storage storage; + PartitionEmulationFixture f(0, 8); + Storage storage(&f.part); CHECK(storage.init(0, 2) == ESP_OK); for (size_t i = 0; i < Page::ENTRY_COUNT * 3 + 1; ++i) { REQUIRE(storage.writeItem(1, "foo", 42U) == ESP_OK); @@ -530,10 +533,10 @@ TEST_CASE("can modify an item on a page which will be erased", "[nvs]") TEST_CASE("erase operations are distributed among sectors", "[nvs]") { const size_t sectors = 6; - SpiFlashEmulator emu(sectors); - Storage storage; + PartitionEmulationFixture f(0, sectors); + Storage storage(&f.part); CHECK(storage.init(0, sectors) == ESP_OK); - + /* Fill some part of storage with static values */ const size_t static_sectors = 2; for (size_t i = 0; i < static_sectors * Page::ENTRY_COUNT; ++i) { @@ -551,7 +554,7 @@ TEST_CASE("erase operations are distributed among sectors", "[nvs]") /* Check that erase counts are distributed between the remaining sectors */ const size_t max_erase_cnt = write_ops / Page::ENTRY_COUNT / (sectors - static_sectors) + 1; for (size_t i = 0; i < sectors; ++i) { - auto erase_cnt = emu.getSectorEraseCount(i); + auto erase_cnt = f.emu.getSectorEraseCount(i); INFO("Sector " << i << " erased " << erase_cnt); CHECK(erase_cnt <= max_erase_cnt); } @@ -559,8 +562,8 @@ TEST_CASE("erase operations are distributed among sectors", "[nvs]") TEST_CASE("can erase items", "[nvs]") { - SpiFlashEmulator emu(3); - Storage storage; + PartitionEmulationFixture f(0, 8); + Storage storage(&f.part); CHECK(storage.init(0, 3) == ESP_OK); for (size_t i = 0; i < Page::ENTRY_COUNT * 2 - 3; ++i) { char name[Item::MAX_KEY_LENGTH + 1]; @@ -578,35 +581,8 @@ TEST_CASE("can erase items", "[nvs]") CHECK(storage.readItem(3, "key00222", val) == ESP_ERR_NVS_NOT_FOUND); } -TEST_CASE("partition name is deep copy", "[nvs]") -{ - SpiFlashEmulator emu(10); - char partition_name[16]; - strcpy(partition_name, "const_name"); - - nvs_handle_t handle_1; - nvs_handle_t handle_2; - const uint32_t NVS_FLASH_SECTOR = 6; - const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; - emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); - - TEST_ESP_OK(nvs_flash_init_custom(partition_name, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)); - - strcpy(partition_name, "just_kidding"); - - TEST_ESP_OK(nvs_open_from_partition("const_name", "test", NVS_READWRITE, &handle_1)); - CHECK(nvs_open_from_partition("just_kidding", "test", NVS_READWRITE, &handle_2) == ESP_ERR_NVS_PART_NOT_FOUND); - - nvs_close(handle_1); - nvs_close(handle_2); - - nvs_flash_deinit_partition("const_name"); - nvs_flash_deinit_partition("just_kidding"); // just in case, try not to affect other tests -} - TEST_CASE("namespace name is deep copy", "[nvs]") { - SpiFlashEmulator emu(10); char ns_name[16]; strcpy(ns_name, "const_name"); @@ -614,9 +590,13 @@ TEST_CASE("namespace name is deep copy", "[nvs]") nvs_handle_t handle_2; const uint32_t NVS_FLASH_SECTOR = 6; const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; - emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); + PartitionEmulationFixture f(NVS_FLASH_SECTOR, + NVS_FLASH_SECTOR_COUNT_MIN); + f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); - TEST_ESP_OK(nvs_flash_init_custom(NVS_DEFAULT_PART_NAME, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)); + TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, + NVS_FLASH_SECTOR, + NVS_FLASH_SECTOR_COUNT_MIN)); TEST_ESP_OK(nvs_open("const_name", NVS_READWRITE, &handle_1)); strcpy(ns_name, "just_kidding"); @@ -629,33 +609,20 @@ TEST_CASE("namespace name is deep copy", "[nvs]") nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME); } -TEST_CASE("Partition name no longer than 16 characters", "[nvs]") -{ - SpiFlashEmulator emu(10); - const char *TOO_LONG_NAME = "0123456789abcdefg"; - - const uint32_t NVS_FLASH_SECTOR = 6; - const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; - emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); - - CHECK(nvs_flash_init_custom(TOO_LONG_NAME, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) - == ESP_ERR_INVALID_ARG); - - nvs_flash_deinit_partition(TOO_LONG_NAME); // just in case -} - TEST_CASE("readonly handle fails on writing", "[nvs]") { - SpiFlashEmulator emu(10); + PartitionEmulationFixture f(0, 10); const char* str = "value 0123456789abcdef0123456789abcdef"; const uint8_t blob[8] = {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7}; nvs_handle_t handle_1; const uint32_t NVS_FLASH_SECTOR = 6; const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; - emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); + f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); - TEST_ESP_OK(nvs_flash_init_custom(NVS_DEFAULT_PART_NAME, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)); + TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, + NVS_FLASH_SECTOR, + NVS_FLASH_SECTOR_COUNT_MIN)); // first, creating namespace... TEST_ESP_OK(nvs_open("ro_ns", NVS_READWRITE, &handle_1)); @@ -674,20 +641,22 @@ TEST_CASE("readonly handle fails on writing", "[nvs]") TEST_CASE("nvs api tests", "[nvs]") { - SpiFlashEmulator emu(10); - emu.randomize(100); + PartitionEmulationFixture f(0, 10); + f.emu.randomize(100); nvs_handle_t handle_1; const uint32_t NVS_FLASH_SECTOR = 6; const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; - emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); + f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); TEST_ESP_ERR(nvs_open("namespace1", NVS_READWRITE, &handle_1), ESP_ERR_NVS_NOT_INITIALIZED); for (uint16_t i = NVS_FLASH_SECTOR; i init_custom(&f.part, + NVS_FLASH_SECTOR, + NVS_FLASH_SECTOR_COUNT_MIN)); TEST_ESP_ERR(nvs_open("namespace1", NVS_READONLY, &handle_1), ESP_ERR_NVS_NOT_FOUND); @@ -732,21 +701,25 @@ TEST_CASE("nvs api tests", "[nvs]") CHECK(0 == strcmp(buf, str)); nvs_close(handle_1); nvs_close(handle_2); + + TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); } TEST_CASE("nvs iterators tests", "[nvs]") { - SpiFlashEmulator emu(5); + PartitionEmulationFixture f(0, 5); const uint32_t NVS_FLASH_SECTOR = 0; const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 5; - emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); + f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); for (uint16_t i = NVS_FLASH_SECTOR; i < NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN; ++i) { - spi_flash_erase_sector(i); + f.emu.erase(i); } - TEST_ESP_OK(nvs_flash_init_custom(NVS_DEFAULT_PART_NAME, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)); + TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, + NVS_FLASH_SECTOR, + NVS_FLASH_SECTOR_COUNT_MIN)); nvs_iterator_t it; nvs_entry_info_t info; @@ -893,23 +866,27 @@ TEST_CASE("nvs iterators tests", "[nvs]") nvs_close(handle_1); nvs_close(handle_2); + + TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); } TEST_CASE("Iterator with not matching type iterates correctly", "[nvs]") { - SpiFlashEmulator emu(5); + PartitionEmulationFixture f(0, 5); nvs_iterator_t it; nvs_handle_t my_handle; const char* NAMESPACE = "test_ns_4"; const uint32_t NVS_FLASH_SECTOR = 0; const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 5; - emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); + f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); for (uint16_t i = NVS_FLASH_SECTOR; i < NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN; ++i) { - spi_flash_erase_sector(i); + f.emu.erase(i); } - TEST_ESP_OK(nvs_flash_init_custom(NVS_DEFAULT_PART_NAME, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)); + TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, + NVS_FLASH_SECTOR, + NVS_FLASH_SECTOR_COUNT_MIN)); // writing string to namespace (a type which spans multiple entries) TEST_ESP_OK(nvs_open(NAMESPACE, NVS_READWRITE, &my_handle)); @@ -922,23 +899,30 @@ TEST_CASE("Iterator with not matching type iterates correctly", "[nvs]") // re-init to trigger cleaning up of broken items -> a corrupted string will be erased nvs_flash_deinit(); - TEST_ESP_OK(nvs_flash_init_custom(NVS_DEFAULT_PART_NAME, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)); + TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, + NVS_FLASH_SECTOR, + NVS_FLASH_SECTOR_COUNT_MIN)); it = nvs_entry_find(NVS_DEFAULT_PART_NAME, NAMESPACE, NVS_TYPE_STR); CHECK(it != NULL); nvs_release_iterator(it); + + // without deinit it affects "nvs api tests" + TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); } TEST_CASE("wifi test", "[nvs]") { - SpiFlashEmulator emu(10); - emu.randomize(10); + PartitionEmulationFixture f(0, 10); + f.emu.randomize(10); const uint32_t NVS_FLASH_SECTOR = 5; const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; - emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); - TEST_ESP_OK(nvs_flash_init_custom(NVS_DEFAULT_PART_NAME, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)); + f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); + TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, + NVS_FLASH_SECTOR, + NVS_FLASH_SECTOR_COUNT_MIN)); nvs_handle_t misc_handle; TEST_ESP_OK(nvs_open("nvs.net80211", NVS_READWRITE, &misc_handle)); @@ -1066,73 +1050,80 @@ TEST_CASE("wifi test", "[nvs]") TEST_ESP_ERR(nvs_get_u8(net80211_handle, "bcn_interval", &bcn_interval), ESP_ERR_NVS_NOT_FOUND); TEST_ESP_OK(nvs_set_u8(net80211_handle, "bcn_interval", bcn_interval)); - s_perf << "Time to simulate nvs init with wifi libs: " << emu.getTotalTime() << " us (" << emu.getEraseOps() << "E " << emu.getWriteOps() << "W " << emu.getReadOps() << "R " << emu.getWriteBytes() << "Wb " << emu.getReadBytes() << "Rb)" << std::endl; + s_perf << "Time to simulate nvs init with wifi libs: " << f.emu.getTotalTime() << " us (" << f.emu.getEraseOps() << "E " << f.emu.getWriteOps() << "W " << f.emu.getReadOps() << "R " << f.emu.getWriteBytes() << "Wb " << f.emu.getReadBytes() << "Rb)" << std::endl; + TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); } TEST_CASE("writing the identical content does not write or erase", "[nvs]") { - SpiFlashEmulator emu(20); + PartitionEmulationFixture f(0, 20); const uint32_t NVS_FLASH_SECTOR = 5; const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 10; - emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); - TEST_ESP_OK(nvs_flash_init_custom(NVS_DEFAULT_PART_NAME, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)); + f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); + TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, + NVS_FLASH_SECTOR, + NVS_FLASH_SECTOR_COUNT_MIN)); nvs_handle misc_handle; TEST_ESP_OK(nvs_open("test", NVS_READWRITE, &misc_handle)); // Test writing a u8 twice, then changing it nvs_set_u8(misc_handle, "test_u8", 8); - emu.clearStats(); + f.emu.clearStats(); nvs_set_u8(misc_handle, "test_u8", 8); - CHECK(emu.getWriteOps() == 0); - CHECK(emu.getEraseOps() == 0); - CHECK(emu.getReadOps() != 0); - emu.clearStats(); + CHECK(f.emu.getWriteOps() == 0); + CHECK(f.emu.getEraseOps() == 0); + CHECK(f.emu.getReadOps() != 0); + f.emu.clearStats(); nvs_set_u8(misc_handle, "test_u8", 9); - CHECK(emu.getWriteOps() != 0); - CHECK(emu.getReadOps() != 0); + CHECK(f.emu.getWriteOps() != 0); + CHECK(f.emu.getReadOps() != 0); // Test writing a string twice, then changing it static const char *test[2] = {"Hello world.", "Hello world!"}; nvs_set_str(misc_handle, "test_str", test[0]); - emu.clearStats(); + f.emu.clearStats(); nvs_set_str(misc_handle, "test_str", test[0]); - CHECK(emu.getWriteOps() == 0); - CHECK(emu.getEraseOps() == 0); - CHECK(emu.getReadOps() != 0); - emu.clearStats(); + CHECK(f.emu.getWriteOps() == 0); + CHECK(f.emu.getEraseOps() == 0); + CHECK(f.emu.getReadOps() != 0); + f.emu.clearStats(); nvs_set_str(misc_handle, "test_str", test[1]); - CHECK(emu.getWriteOps() != 0); - CHECK(emu.getReadOps() != 0); + CHECK(f.emu.getWriteOps() != 0); + CHECK(f.emu.getReadOps() != 0); // Test writing a multi-page blob, then changing it uint8_t blob[Page::CHUNK_MAX_SIZE * 3] = {0}; memset(blob, 1, sizeof(blob)); nvs_set_blob(misc_handle, "test_blob", blob, sizeof(blob)); - emu.clearStats(); + f.emu.clearStats(); nvs_set_blob(misc_handle, "test_blob", blob, sizeof(blob)); - CHECK(emu.getWriteOps() == 0); - CHECK(emu.getEraseOps() == 0); - CHECK(emu.getReadOps() != 0); + CHECK(f.emu.getWriteOps() == 0); + CHECK(f.emu.getEraseOps() == 0); + CHECK(f.emu.getReadOps() != 0); blob[sizeof(blob) - 1]++; - emu.clearStats(); + f.emu.clearStats(); nvs_set_blob(misc_handle, "test_blob", blob, sizeof(blob)); - CHECK(emu.getWriteOps() != 0); - CHECK(emu.getReadOps() != 0); + CHECK(f.emu.getWriteOps() != 0); + CHECK(f.emu.getReadOps() != 0); + + TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); } TEST_CASE("can init storage from flash with random contents", "[nvs]") { - SpiFlashEmulator emu(10); - emu.randomize(42); + PartitionEmulationFixture f(0, 10); + f.emu.randomize(42); nvs_handle_t handle; const uint32_t NVS_FLASH_SECTOR = 5; const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; - emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); - TEST_ESP_OK(nvs_flash_init_custom(NVS_DEFAULT_PART_NAME, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)); + f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); + TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, + NVS_FLASH_SECTOR, + NVS_FLASH_SECTOR_COUNT_MIN)); TEST_ESP_OK(nvs_open("nvs.net80211", NVS_READWRITE, &handle)); @@ -1140,6 +1131,8 @@ TEST_CASE("can init storage from flash with random contents", "[nvs]") if (nvs_get_u8(handle, "wifi.opmode", &opmode) != ESP_OK) { TEST_ESP_OK(nvs_set_u8(handle, "wifi.opmode", opmode)); } + + TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); } @@ -1153,14 +1146,16 @@ TEST_CASE("nvs api tests, starting with random data in flash", "[nvs][long]") lastPercent = percentDone; printf("%d%%\n", percentDone); } - SpiFlashEmulator emu(10); - emu.randomize(static_cast(count)); + PartitionEmulationFixture f(0, 10); + f.emu.randomize(static_cast(count)); const uint32_t NVS_FLASH_SECTOR = 6; const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; - emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); + f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); - TEST_ESP_OK(nvs_flash_init_custom(NVS_DEFAULT_PART_NAME, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)); + TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, + NVS_FLASH_SECTOR, + NVS_FLASH_SECTOR_COUNT_MIN)); nvs_handle_t handle_1; TEST_ESP_ERR(nvs_open("namespace1", NVS_READONLY, &handle_1), ESP_ERR_NVS_NOT_FOUND); @@ -1195,6 +1190,8 @@ TEST_CASE("nvs api tests, starting with random data in flash", "[nvs][long]") } nvs_close(handle_1); } + + TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); } extern "C" void nvs_dump(const char *partName); @@ -1460,15 +1457,17 @@ TEST_CASE("monkey test", "[nvs][monkey]") uint32_t seed = 3; gen.seed(seed); - SpiFlashEmulator emu(10); - emu.randomize(seed); - emu.clearStats(); + PartitionEmulationFixture f(0, 10); + f.emu.randomize(seed); + f.emu.clearStats(); const uint32_t NVS_FLASH_SECTOR = 2; const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 8; - emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); + f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); - TEST_ESP_OK(nvs_flash_init_custom(NVS_DEFAULT_PART_NAME, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)); + TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, + NVS_FLASH_SECTOR, + NVS_FLASH_SECTOR_COUNT_MIN)); nvs_handle_t handle; TEST_ESP_OK(nvs_open("namespace1", NVS_READWRITE, &handle)); @@ -1476,7 +1475,9 @@ TEST_CASE("monkey test", "[nvs][monkey]") size_t count = 1000; CHECK(test.doRandomThings(handle, gen, count) == ESP_OK); - s_perf << "Monkey test: nErase=" << emu.getEraseOps() << " nWrite=" << emu.getWriteOps() << std::endl; + s_perf << "Monkey test: nErase=" << f.emu.getEraseOps() << " nWrite=" << f.emu.getWriteOps() << std::endl; + + TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); } TEST_CASE("test recovery from sudden poweroff", "[long][nvs][recovery][monkey]") @@ -1487,20 +1488,20 @@ TEST_CASE("test recovery from sudden poweroff", "[long][nvs][recovery][monkey]") gen.seed(seed); const size_t iter_count = 2000; - SpiFlashEmulator emu(10); + PartitionEmulationFixture f(0, 10); const uint32_t NVS_FLASH_SECTOR = 2; const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 8; - emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); + f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); size_t totalOps = 0; int lastPercent = -1; for (uint32_t errDelay = 0; ; ++errDelay) { INFO(errDelay); - emu.randomize(seed); - emu.clearStats(); - emu.failAfter(errDelay); + f.emu.randomize(seed); + f.emu.clearStats(); + f.emu.failAfter(errDelay); RandomTest test; if (totalOps != 0) { @@ -1515,7 +1516,9 @@ TEST_CASE("test recovery from sudden poweroff", "[long][nvs][recovery][monkey]") nvs_handle_t handle; size_t count = iter_count; - if (nvs_flash_init_custom(NVS_DEFAULT_PART_NAME, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK) { + if (NVSPartitionManager::get_instance()->init_custom(&f.part, + NVS_FLASH_SECTOR, + NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK) { if (nvs_open("namespace1", NVS_READWRITE, &handle) == ESP_OK) { if(test.doRandomThings(handle, gen, count) != ESP_ERR_FLASH_OP_FAIL) { nvs_close(handle); @@ -1523,9 +1526,12 @@ TEST_CASE("test recovery from sudden poweroff", "[long][nvs][recovery][monkey]") } nvs_close(handle); } + TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); } - TEST_ESP_OK(nvs_flash_init_custom(NVS_DEFAULT_PART_NAME, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)); + TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, + NVS_FLASH_SECTOR, + NVS_FLASH_SECTOR_COUNT_MIN)); TEST_ESP_OK(nvs_open("namespace1", NVS_READWRITE, &handle)); auto res = test.doRandomThings(handle, gen, count); if (res != ESP_OK) { @@ -1533,16 +1539,20 @@ TEST_CASE("test recovery from sudden poweroff", "[long][nvs][recovery][monkey]") CHECK(0); } nvs_close(handle); - totalOps = emu.getEraseOps() + emu.getWriteBytes() / 4; + totalOps = f.emu.getEraseOps() + f.emu.getWriteBytes() / 4; + + TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); } } TEST_CASE("test for memory leaks in open/set", "[leaks]") { - SpiFlashEmulator emu(10); + PartitionEmulationFixture f(0, 10); const uint32_t NVS_FLASH_SECTOR = 6; const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; - emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); - TEST_ESP_OK(nvs_flash_init_custom(NVS_DEFAULT_PART_NAME, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)); + f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); + TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, + NVS_FLASH_SECTOR, + NVS_FLASH_SECTOR_COUNT_MIN)); for (int i = 0; i < 100000; ++i) { nvs_handle_t light_handle = 0; @@ -1552,15 +1562,17 @@ TEST_CASE("test for memory leaks in open/set", "[leaks]") TEST_ESP_OK(nvs_commit(light_handle)); nvs_close(light_handle); } + + TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); } TEST_CASE("duplicate items are removed", "[nvs][dupes]") { - SpiFlashEmulator emu(3); + PartitionEmulationFixture f(0, 3); { // create one item nvs::Page p; - p.load(0); + p.load(&f.part, 0); p.writeItem(1, "opmode", 3); } { @@ -1568,14 +1580,14 @@ TEST_CASE("duplicate items are removed", "[nvs][dupes]") nvs::Item item(1, ItemType::U8, 1, "opmode"); item.data[0] = 2; item.crc32 = item.calculateCrc32(); - emu.write(3 * 32, reinterpret_cast(&item), sizeof(item)); - emu.write(4 * 32, reinterpret_cast(&item), sizeof(item)); + f.emu.write(3 * 32, reinterpret_cast(&item), sizeof(item)); + f.emu.write(4 * 32, reinterpret_cast(&item), sizeof(item)); uint32_t mask = 0xFFFFFFEA; - emu.write(32, &mask, 4); + f.emu.write(32, &mask, 4); } { // load page and check that second item persists - nvs::Storage s; + nvs::Storage s(&f.part); s.init(0, 3); uint8_t val; ESP_ERROR_CHECK(s.readItem(1, "opmode", val)); @@ -1583,7 +1595,7 @@ TEST_CASE("duplicate items are removed", "[nvs][dupes]") } { Page p; - p.load(0); + p.load(&f.part, 0); CHECK(p.getErasedEntryCount() == 2); CHECK(p.getUsedEntryCount() == 1); } @@ -1591,13 +1603,13 @@ TEST_CASE("duplicate items are removed", "[nvs][dupes]") TEST_CASE("recovery after failure to write data", "[nvs]") { - SpiFlashEmulator emu(3); + PartitionEmulationFixture f(0, 3); const char str[] = "value 0123456789abcdef012345678value 0123456789abcdef012345678"; // make flash write fail exactly in Page::writeEntryData - emu.failAfter(17); + f.emu.failAfter(17); { - Storage storage; + Storage storage(&f.part); TEST_ESP_OK(storage.init(0, 3)); TEST_ESP_ERR(storage.writeItem(1, ItemType::SZ, "key", str, strlen(str)), ESP_ERR_FLASH_OP_FAIL); @@ -1611,7 +1623,7 @@ TEST_CASE("recovery after failure to write data", "[nvs]") { // load page and check that data was erased Page p; - p.load(0); + p.load(&f.part, 0); CHECK(p.getErasedEntryCount() == 3); CHECK(p.getUsedEntryCount() == 0); @@ -1622,8 +1634,8 @@ TEST_CASE("recovery after failure to write data", "[nvs]") TEST_CASE("crc errors in item header are handled", "[nvs]") { - SpiFlashEmulator emu(3); - Storage storage; + PartitionEmulationFixture f(0, 3); + Storage storage(&f.part); // prepare some data TEST_ESP_OK(storage.init(0, 3)); TEST_ESP_OK(storage.writeItem(0, "ns1", static_cast(1))); @@ -1632,7 +1644,7 @@ TEST_CASE("crc errors in item header are handled", "[nvs]") // corrupt item header uint32_t val = 0; - emu.write(32 * 3, &val, 4); + f.emu.write(32 * 3, &val, 4); // check that storage can recover TEST_ESP_OK(storage.init(0, 3)); @@ -1650,7 +1662,7 @@ TEST_CASE("crc errors in item header are handled", "[nvs]") // corrupt another item on the full page val = 0; - emu.write(32 * 4, &val, 4); + f.emu.write(32 * 4, &val, 4); // check that storage can recover TEST_ESP_OK(storage.init(0, 3)); @@ -1660,13 +1672,13 @@ TEST_CASE("crc errors in item header are handled", "[nvs]") TEST_CASE("crc error in variable length item is handled", "[nvs]") { - SpiFlashEmulator emu(3); + PartitionEmulationFixture f(0, 3); const uint64_t before_val = 0xbef04e; const uint64_t after_val = 0xaf7e4; // write some data { Page p; - p.load(0); + p.load(&f.part, 0); TEST_ESP_OK(p.writeItem(0, "before", before_val)); const char* str = "foobar"; TEST_ESP_OK(p.writeItem(0, ItemType::SZ, "key", str, strlen(str))); @@ -1674,13 +1686,13 @@ TEST_CASE("crc error in variable length item is handled", "[nvs]") } // corrupt some data uint32_t w; - CHECK(emu.read(&w, 32 * 3 + 8, sizeof(w))); + CHECK(f.emu.read(&w, 32 * 3 + 8, sizeof(w))); w &= 0xf000000f; - CHECK(emu.write(32 * 3 + 8, &w, sizeof(w))); + CHECK(f.emu.write(32 * 3 + 8, &w, sizeof(w))); // load and check { Page p; - p.load(0); + p.load(&f.part, 0); CHECK(p.getUsedEntryCount() == 2); CHECK(p.getErasedEntryCount() == 2); @@ -1696,8 +1708,8 @@ TEST_CASE("crc error in variable length item is handled", "[nvs]") TEST_CASE("read/write failure (TW8406)", "[nvs]") { - SpiFlashEmulator emu(3); - nvs_flash_init_custom(NVS_DEFAULT_PART_NAME, 0, 3); + PartitionEmulationFixture f(0, 3); + NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 3); for (int attempts = 0; attempts < 3; ++attempts) { int i = 0; nvs_handle_t light_handle = 0; @@ -1724,14 +1736,16 @@ TEST_CASE("read/write failure (TW8406)", "[nvs]") } nvs_close(light_handle); } + + TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); } TEST_CASE("nvs_flash_init checks for an empty page", "[nvs]") { const size_t blob_size = Page::CHUNK_MAX_SIZE; uint8_t blob[blob_size] = {0}; - SpiFlashEmulator emu(5); - TEST_ESP_OK( nvs_flash_init_custom(NVS_DEFAULT_PART_NAME, 0, 5) ); + PartitionEmulationFixture f(0, 8); + TEST_ESP_OK( NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 5) ); nvs_handle_t handle; TEST_ESP_OK( nvs_open("test", NVS_READWRITE, &handle) ); // Fill first page @@ -1742,16 +1756,23 @@ TEST_CASE("nvs_flash_init checks for an empty page", "[nvs]") TEST_ESP_OK( nvs_set_blob(handle, "3a", blob, blob_size) ); TEST_ESP_OK( nvs_commit(handle) ); nvs_close(handle); + TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); // first two pages are now full, third one is writable, last two are empty // init should fail - TEST_ESP_ERR( nvs_flash_init_custom(NVS_DEFAULT_PART_NAME, 0, 3), ESP_ERR_NVS_NO_FREE_PAGES ); + TEST_ESP_ERR( NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 3), + ESP_ERR_NVS_NO_FREE_PAGES ); + + // in case this test fails, to not affect other tests + nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME); } TEST_CASE("multiple partitions access check", "[nvs]") { SpiFlashEmulator emu(10); - TEST_ESP_OK( nvs_flash_init_custom("nvs1", 0, 5) ); - TEST_ESP_OK( nvs_flash_init_custom("nvs2", 5, 5) ); + PartitionEmulation p0(&emu, 0 * SPI_FLASH_SEC_SIZE, 5 * SPI_FLASH_SEC_SIZE, "nvs1"); + PartitionEmulation p1(&emu, 5 * SPI_FLASH_SEC_SIZE, 5 * SPI_FLASH_SEC_SIZE, "nvs2"); + TEST_ESP_OK( NVSPartitionManager::get_instance()->init_custom(&p0, 0, 5) ); + TEST_ESP_OK( NVSPartitionManager::get_instance()->init_custom(&p1, 5, 5) ); nvs_handle_t handle1, handle2; TEST_ESP_OK( nvs_open_from_partition("nvs1", "test", NVS_READWRITE, &handle1) ); TEST_ESP_OK( nvs_open_from_partition("nvs2", "test", NVS_READWRITE, &handle2) ); @@ -1762,14 +1783,17 @@ TEST_CASE("multiple partitions access check", "[nvs]") TEST_ESP_OK( nvs_get_i32(handle2, "foo", &v2)); CHECK(v1 == 0xdeadbeef); CHECK(v2 == 0xcafebabe); + + TEST_ESP_OK(nvs_flash_deinit_partition(p0.get_partition_name())); + TEST_ESP_OK(nvs_flash_deinit_partition(p1.get_partition_name())); } TEST_CASE("nvs page selection takes into account free entries also not just erased entries", "[nvs]") { const size_t blob_size = Page::CHUNK_MAX_SIZE/2; uint8_t blob[blob_size] = {0}; - SpiFlashEmulator emu(3); - TEST_ESP_OK( nvs_flash_init_custom(NVS_DEFAULT_PART_NAME, 0, 3) ); + PartitionEmulationFixture f(0, 3); + TEST_ESP_OK( NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 3) ); nvs_handle_t handle; TEST_ESP_OK( nvs_open("test", NVS_READWRITE, &handle) ); // Fill first page @@ -1783,11 +1807,13 @@ TEST_CASE("nvs page selection takes into account free entries also not just eras TEST_ESP_OK( nvs_set_blob(handle, "3a", blob, 4) ); TEST_ESP_OK( nvs_commit(handle) ); nvs_close(handle); + + TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name())); } TEST_CASE("calculate used and free space", "[nvs]") { - SpiFlashEmulator emu(6); + PartitionEmulationFixture f(0, 6); nvs_flash_deinit(); TEST_ESP_ERR(nvs_get_stats(NULL, NULL), ESP_ERR_INVALID_ARG); nvs_stats_t stat1; @@ -1804,7 +1830,7 @@ TEST_CASE("calculate used and free space", "[nvs]") CHECK(h_count_entries == 0); // init nvs - TEST_ESP_OK(nvs_flash_init_custom(NVS_DEFAULT_PART_NAME, 0, 6)); + TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 6)); TEST_ESP_ERR(nvs_get_used_entry_count(handle, &h_count_entries), ESP_ERR_NVS_INVALID_HANDLE); CHECK(h_count_entries == 0); @@ -1918,26 +1944,29 @@ TEST_CASE("calculate used and free space", "[nvs]") CHECK(stat1.used_entries == (h1_count_entries + h2_count_entries + h3_count_entries + stat1.namespace_count)); nvs_close(handle_3); + + TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name())); } +// TODO: leaks memory TEST_CASE("Recovery from power-off when the entry being erased is not on active page", "[nvs]") { const size_t blob_size = Page::CHUNK_MAX_SIZE/2 ; size_t read_size = blob_size; uint8_t blob[blob_size] = {0x11}; - SpiFlashEmulator emu(3); - TEST_ESP_OK( nvs_flash_init_custom(NVS_DEFAULT_PART_NAME, 0, 3) ); + PartitionEmulationFixture f(0, 3); + TEST_ESP_OK( NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 3) ); nvs_handle_t handle; TEST_ESP_OK( nvs_open("test", NVS_READWRITE, &handle) ); - emu.clearStats(); - emu.failAfter(Page::CHUNK_MAX_SIZE/4 + 75); + f.emu.clearStats(); + f.emu.failAfter(Page::CHUNK_MAX_SIZE/4 + 75); TEST_ESP_OK( nvs_set_blob(handle, "1a", blob, blob_size) ); TEST_ESP_OK( nvs_set_blob(handle, "1b", blob, blob_size) ); TEST_ESP_ERR( nvs_erase_key(handle, "1a"), ESP_ERR_FLASH_OP_FAIL ); - TEST_ESP_OK( nvs_flash_init_custom(NVS_DEFAULT_PART_NAME, 0, 3) ); + TEST_ESP_OK( NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 3) ); /* Check 1a is erased fully*/ TEST_ESP_ERR( nvs_get_blob(handle, "1a", blob, &read_size), ESP_ERR_NVS_NOT_FOUND); @@ -1946,15 +1975,18 @@ TEST_CASE("Recovery from power-off when the entry being erased is not on active TEST_ESP_OK( nvs_get_blob(handle, "1b", blob, &read_size)); nvs_close(handle); + + TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name())); } +// TODO: leaks memory TEST_CASE("Recovery from power-off when page is being freed.", "[nvs]") { const size_t blob_size = (Page::ENTRY_COUNT-3) * Page::ENTRY_SIZE; size_t read_size = blob_size/2; uint8_t blob[blob_size] = {0}; - SpiFlashEmulator emu(3); - TEST_ESP_OK(nvs_flash_init_custom(NVS_DEFAULT_PART_NAME, 0, 3)); + PartitionEmulationFixture f(0, 3); + TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 3)); nvs_handle_t handle; TEST_ESP_OK(nvs_open("test", NVS_READWRITE, &handle)); // Fill first page @@ -1967,11 +1999,11 @@ TEST_CASE("Recovery from power-off when page is being freed.", "[nvs]") TEST_ESP_OK(nvs_erase_key(handle, "1c")); - emu.clearStats(); - emu.failAfter(6 * Page::ENTRY_COUNT); + f.emu.clearStats(); + f.emu.failAfter(6 * Page::ENTRY_COUNT); TEST_ESP_ERR(nvs_set_blob(handle, "1d", blob, blob_size/4), ESP_ERR_FLASH_OP_FAIL); - TEST_ESP_OK(nvs_flash_init_custom(NVS_DEFAULT_PART_NAME, 0, 3)); + TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 3)); read_size = blob_size/3; TEST_ESP_OK( nvs_get_blob(handle, "1a", blob, &read_size)); @@ -1987,33 +2019,39 @@ TEST_CASE("Recovery from power-off when page is being freed.", "[nvs]") TEST_ESP_OK(nvs_commit(handle)); nvs_close(handle); + + TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name())); } TEST_CASE("Multi-page blobs are supported", "[nvs]") { const size_t blob_size = Page::CHUNK_MAX_SIZE *2; uint8_t blob[blob_size] = {0}; - SpiFlashEmulator emu(5); - TEST_ESP_OK(nvs_flash_init_custom(NVS_DEFAULT_PART_NAME, 0, 5)); + PartitionEmulationFixture f(0, 5); + TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 5)); nvs_handle_t handle; TEST_ESP_OK(nvs_open("test", NVS_READWRITE, &handle)); TEST_ESP_OK(nvs_set_blob(handle, "abc", blob, blob_size)); TEST_ESP_OK(nvs_commit(handle)); nvs_close(handle); + + TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name())); } TEST_CASE("Failures are handled while storing multi-page blobs", "[nvs]") { const size_t blob_size = Page::CHUNK_MAX_SIZE *7; uint8_t blob[blob_size] = {0}; - SpiFlashEmulator emu(5); - TEST_ESP_OK(nvs_flash_init_custom(NVS_DEFAULT_PART_NAME, 0, 5)); + PartitionEmulationFixture f(0, 5); + TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 5)); nvs_handle_t handle; TEST_ESP_OK(nvs_open("test", NVS_READWRITE, &handle)); TEST_ESP_ERR(nvs_set_blob(handle, "abc", blob, blob_size), ESP_ERR_NVS_VALUE_TOO_LONG); TEST_ESP_OK(nvs_set_blob(handle, "abc", blob, Page::CHUNK_MAX_SIZE*2)); TEST_ESP_OK(nvs_commit(handle)); nvs_close(handle); + + TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name())); } TEST_CASE("Reading multi-page blobs", "[nvs]") @@ -2022,8 +2060,8 @@ TEST_CASE("Reading multi-page blobs", "[nvs]") uint8_t blob[blob_size]; uint8_t blob_read[blob_size]; size_t read_size = blob_size; - SpiFlashEmulator emu(5); - TEST_ESP_OK(nvs_flash_init_custom(NVS_DEFAULT_PART_NAME, 0, 5)); + PartitionEmulationFixture f(0, 5); + TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 5)); nvs_handle_t handle; memset(blob, 0x11, blob_size); memset(blob_read, 0xee, blob_size); @@ -2033,6 +2071,8 @@ TEST_CASE("Reading multi-page blobs", "[nvs]") CHECK(memcmp(blob, blob_read, blob_size) == 0); TEST_ESP_OK(nvs_commit(handle)); nvs_close(handle); + + TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name())); } TEST_CASE("Modification of values for Multi-page blobs are supported", "[nvs]") @@ -2044,8 +2084,8 @@ TEST_CASE("Modification of values for Multi-page blobs are supported", "[nvs]") uint8_t blob3[blob_size] = {0x22}; uint8_t blob4[blob_size] ={ 0x33}; size_t read_size = blob_size; - SpiFlashEmulator emu(6); - TEST_ESP_OK( nvs_flash_init_custom(NVS_DEFAULT_PART_NAME, 0, 6) ); + PartitionEmulationFixture f(0, 6); + TEST_ESP_OK( NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 6) ); nvs_handle_t handle; memset(blob, 0x11, blob_size); memset(blob2, 0x22, blob_size); @@ -2061,6 +2101,8 @@ TEST_CASE("Modification of values for Multi-page blobs are supported", "[nvs]") CHECK(memcmp(blob4, blob_read, blob_size) == 0); TEST_ESP_OK( nvs_commit(handle) ); nvs_close(handle); + + TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name())); } TEST_CASE("Modification from single page blob to multi-page", "[nvs]") @@ -2069,8 +2111,8 @@ TEST_CASE("Modification from single page blob to multi-page", "[nvs]") uint8_t blob[blob_size] = {0}; uint8_t blob_read[blob_size] = {0xff}; size_t read_size = blob_size; - SpiFlashEmulator emu(5); - TEST_ESP_OK( nvs_flash_init_custom(NVS_DEFAULT_PART_NAME, 0, 5) ); + PartitionEmulationFixture f(0, 5); + TEST_ESP_OK( NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 5) ); nvs_handle_t handle; TEST_ESP_OK(nvs_open("Test", NVS_READWRITE, &handle) ); TEST_ESP_OK(nvs_set_blob(handle, "abc", blob, Page::CHUNK_MAX_SIZE/2)); @@ -2079,6 +2121,8 @@ TEST_CASE("Modification from single page blob to multi-page", "[nvs]") CHECK(memcmp(blob, blob_read, blob_size) == 0); TEST_ESP_OK(nvs_commit(handle) ); nvs_close(handle); + + TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name())); } TEST_CASE("Modification from multi-page to single page", "[nvs]") @@ -2087,8 +2131,8 @@ TEST_CASE("Modification from multi-page to single page", "[nvs]") uint8_t blob[blob_size] = {0}; uint8_t blob_read[blob_size] = {0xff}; size_t read_size = blob_size; - SpiFlashEmulator emu(5); - TEST_ESP_OK(nvs_flash_init_custom(NVS_DEFAULT_PART_NAME, 0, 5) ); + PartitionEmulationFixture f(0, 5); + TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 5) ); nvs_handle_t handle; TEST_ESP_OK(nvs_open("Test", NVS_READWRITE, &handle) ); TEST_ESP_OK(nvs_set_blob(handle, "abc", blob, blob_size)); @@ -2098,6 +2142,8 @@ TEST_CASE("Modification from multi-page to single page", "[nvs]") CHECK(memcmp(blob, blob_read, Page::CHUNK_MAX_SIZE) == 0); TEST_ESP_OK(nvs_commit(handle) ); nvs_close(handle); + + TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name())); } TEST_CASE("Multi-page blob erased using nvs_erase_key should not be found when probed for just length", "[nvs]") @@ -2105,8 +2151,8 @@ TEST_CASE("Multi-page blob erased using nvs_erase_key should not be found when p const size_t blob_size = Page::CHUNK_MAX_SIZE *3; uint8_t blob[blob_size] = {0}; size_t read_size = blob_size; - SpiFlashEmulator emu(5); - TEST_ESP_OK(nvs_flash_init_custom(NVS_DEFAULT_PART_NAME, 0, 5)); + PartitionEmulationFixture f(0, 5); + TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 5)); nvs_handle handle; TEST_ESP_OK(nvs_open("Test", NVS_READWRITE, &handle)); TEST_ESP_OK(nvs_set_blob(handle, "abc", blob, blob_size)); @@ -2114,6 +2160,8 @@ TEST_CASE("Multi-page blob erased using nvs_erase_key should not be found when p TEST_ESP_ERR(nvs_get_blob(handle, "abc", NULL, &read_size), ESP_ERR_NVS_NOT_FOUND); TEST_ESP_OK(nvs_commit(handle)); nvs_close(handle); + + TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name())); } @@ -2123,8 +2171,8 @@ TEST_CASE("Check that orphaned blobs are erased during init", "[nvs]") uint8_t blob[blob_size] = {0x11}; uint8_t blob2[blob_size] = {0x22}; uint8_t blob3[blob_size] = {0x33}; - SpiFlashEmulator emu(5); - Storage storage; + PartitionEmulationFixture f(0, 5); + Storage storage(&f.part); TEST_ESP_OK(storage.init(0, 5)); @@ -2138,7 +2186,7 @@ TEST_CASE("Check that orphaned blobs are erased during init", "[nvs]") TEST_ESP_ERR(storage.writeItem(1, ItemType::BLOB, "key2", blob, sizeof(blob)), ESP_ERR_NVS_NOT_ENOUGH_SPACE); Page p; - p.load(3); // This is where index will be placed. + p.load(&f.part, 3); // This is where index will be placed. p.erase(); TEST_ESP_OK(storage.init(0, 5)); @@ -2149,8 +2197,8 @@ TEST_CASE("Check that orphaned blobs are erased during init", "[nvs]") TEST_CASE("nvs blob fragmentation test", "[nvs]") { - SpiFlashEmulator emu(4); - TEST_ESP_OK(nvs_flash_init_custom(NVS_DEFAULT_PART_NAME, 0, 4) ); + PartitionEmulationFixture f(0, 4); + TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 4) ); const size_t BLOB_SIZE = 3500; uint8_t *blob = (uint8_t*) malloc(BLOB_SIZE); CHECK(blob != NULL); @@ -2167,14 +2215,16 @@ TEST_CASE("nvs blob fragmentation test", "[nvs]") TEST_ESP_OK( nvs_set_u32(h, seq_buf, i) ); } free(blob); + + TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name())); } TEST_CASE("nvs code handles errors properly when partition is near to full", "[nvs]") { const size_t blob_size = Page::CHUNK_MAX_SIZE * 0.3 ; uint8_t blob[blob_size] = {0x11}; - SpiFlashEmulator emu(5); - Storage storage; + PartitionEmulationFixture f(0, 5); + Storage storage(&f.part); char nvs_key[16] = ""; TEST_ESP_OK(storage.init(0, 5)); @@ -2193,23 +2243,28 @@ TEST_CASE("nvs code handles errors properly when partition is near to full", "[n TEST_CASE("Check for nvs version incompatibility", "[nvs]") { - SpiFlashEmulator emu(3); + PartitionEmulationFixture f(0, 3); int32_t val1 = 0x12345678; Page p; - p.load(0); + p.load(&f.part, 0); TEST_ESP_OK(p.setVersion(Page::NVS_VERSION - 1)); TEST_ESP_OK(p.writeItem(1, ItemType::I32, "foo", &val1, sizeof(val1))); - TEST_ESP_ERR(nvs_flash_init_custom(NVS_DEFAULT_PART_NAME, 0, 3), ESP_ERR_NVS_NEW_VERSION_FOUND); + TEST_ESP_ERR(NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 3), + ESP_ERR_NVS_NEW_VERSION_FOUND); + + // if something went wrong, clean up + nvs_flash_deinit_partition(f.part.get_partition_name()); } TEST_CASE("Check that NVS supports old blob format without blob index", "[nvs]") { SpiFlashEmulator emu("../nvs_partition_generator/part_old_blob_format.bin"); + PartitionEmulation part(&emu, 0, 2 * SPI_FLASH_SEC_SIZE, "test"); nvs_handle_t handle; - TEST_ESP_OK( nvs_flash_init_custom("test", 0, 2) ); + TEST_ESP_OK( NVSPartitionManager::get_instance()->init_custom(&part, 0, 2) ); TEST_ESP_OK( nvs_open_from_partition("test", "dummyNamespace", NVS_READWRITE, &handle)); char buf[64] = {0}; @@ -2224,7 +2279,7 @@ TEST_CASE("Check that NVS supports old blob format without blob index", "[nvs]") CHECK(memcmp(buf, base64data, buflen) == 0); Page p; - p.load(0); + p.load(&part, 0); /* Check that item is stored in old format without blob index*/ TEST_ESP_OK(p.findItem(1, ItemType::BLOB, "dummyHex2BinKey")); @@ -2234,7 +2289,7 @@ TEST_CASE("Check that NVS supports old blob format without blob index", "[nvs]") TEST_ESP_OK(nvs_set_blob(handle, "dummyHex2BinKey", hexdata, sizeof(hexdata))); Page p2; - p2.load(0); + p2.load(&part, 0); /* Check the type of the blob. Expect type mismatch since the blob is stored in new format*/ TEST_ESP_ERR(p2.findItem(1, ItemType::BLOB, "dummyHex2BinKey"), ESP_ERR_NVS_TYPE_MISMATCH); @@ -2247,8 +2302,10 @@ TEST_CASE("Check that NVS supports old blob format without blob index", "[nvs]") TEST_ESP_OK( nvs_get_blob(handle, "dummyBase64Key", buf, &buflen)); CHECK(memcmp(buf, base64data, buflen) == 0); + TEST_ESP_OK(nvs_flash_deinit_partition(part.get_partition_name())); } +// TODO: leaks memory TEST_CASE("monkey test with old-format blob present", "[nvs][monkey]") { std::random_device rd; @@ -2256,17 +2313,19 @@ TEST_CASE("monkey test with old-format blob present", "[nvs][monkey]") uint32_t seed = 3; gen.seed(seed); - SpiFlashEmulator emu(10); - emu.randomize(seed); - emu.clearStats(); + PartitionEmulationFixture f(0, 10); + f.emu.randomize(seed); + f.emu.clearStats(); const uint32_t NVS_FLASH_SECTOR = 2; const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 8; static const size_t smallBlobLen = Page::CHUNK_MAX_SIZE / 3; - emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); + f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); - TEST_ESP_OK(nvs_flash_init_custom(NVS_DEFAULT_PART_NAME, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)); + TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, + NVS_FLASH_SECTOR, + NVS_FLASH_SECTOR_COUNT_MIN)); nvs_handle_t handle; TEST_ESP_OK(nvs_open("namespace1", NVS_READWRITE, &handle)); @@ -2278,7 +2337,7 @@ TEST_CASE("monkey test with old-format blob present", "[nvs][monkey]") /* Erase index and chunks for the blob with "singlepage" key */ for (uint8_t num = NVS_FLASH_SECTOR; num < NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN; num++) { Page p; - p.load(num); + p.load(&f.part, num); p.eraseItem(1, ItemType::BLOB, "singlepage", Item::CHUNK_ANY, VerOffset::VER_ANY); p.eraseItem(1, ItemType::BLOB_IDX, "singlepage", Item::CHUNK_ANY, VerOffset::VER_ANY); p.eraseItem(1, ItemType::BLOB_DATA, "singlepage", Item::CHUNK_ANY, VerOffset::VER_ANY); @@ -2287,7 +2346,7 @@ TEST_CASE("monkey test with old-format blob present", "[nvs][monkey]") /* Now write "singlepage" blob in old format*/ for (uint8_t num = NVS_FLASH_SECTOR; num < NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN; num++) { Page p; - p.load(num); + p.load(&f.part, num); if (p.state() == Page::PageState::ACTIVE) { uint8_t buf[smallBlobLen]; size_t blobLen = gen() % smallBlobLen; @@ -2307,8 +2366,12 @@ TEST_CASE("monkey test with old-format blob present", "[nvs][monkey]") break; } } + + TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name())); /* Initialize again */ - TEST_ESP_OK(nvs_flash_init_custom(NVS_DEFAULT_PART_NAME, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)); + TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, + NVS_FLASH_SECTOR, + NVS_FLASH_SECTOR_COUNT_MIN)); TEST_ESP_OK(nvs_open("namespace1", NVS_READWRITE, &handle)); /* Perform random things */ @@ -2324,7 +2387,7 @@ TEST_CASE("monkey test with old-format blob present", "[nvs][monkey]") for (uint8_t num = NVS_FLASH_SECTOR; num < NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN; num++) { Page p; - p.load(num); + p.load(&f.part, num); if(!oldVerPresent && p.findItem(1, ItemType::BLOB, "singlepage", Item::CHUNK_ANY, VerOffset::VER_ANY) == ESP_OK) { oldVerPresent = true; } @@ -2336,7 +2399,9 @@ TEST_CASE("monkey test with old-format blob present", "[nvs][monkey]") CHECK(oldVerPresent != newVerPresent); } - s_perf << "Monkey test: nErase=" << emu.getEraseOps() << " nWrite=" << emu.getWriteOps() << std::endl; + + TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name())); + s_perf << "Monkey test: nErase=" << f.emu.getEraseOps() << " nWrite=" << f.emu.getWriteOps() << std::endl; } TEST_CASE("Recovery from power-off during modification of blob present in old-format (same page)", "[nvs]") @@ -2346,10 +2411,10 @@ TEST_CASE("Recovery from power-off during modification of blob present in old-fo uint32_t seed = 3; gen.seed(seed); - SpiFlashEmulator emu(3); - emu.clearStats(); + PartitionEmulationFixture f(0, 3); + f.emu.clearStats(); - TEST_ESP_OK(nvs_flash_init_custom(NVS_DEFAULT_PART_NAME, 0, 3)); + TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 3)); nvs_handle_t handle; TEST_ESP_OK(nvs_open("namespace1", NVS_READWRITE, &handle)); @@ -2362,7 +2427,7 @@ TEST_CASE("Recovery from power-off during modification of blob present in old-fo /* Power-off when blob was being written on the same page where its old version in old format * was present*/ Page p; - p.load(0); + p.load(&f.part, 0); /* Write blob in old-format*/ TEST_ESP_OK(p.writeItem(1, ItemType::BLOB, "singlepage", hexdata_old, sizeof(hexdata_old))); @@ -2378,17 +2443,19 @@ TEST_CASE("Recovery from power-off during modification of blob present in old-fo TEST_ESP_OK(p.findItem(1, ItemType::BLOB, "singlepage")); + TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name())); /* Initialize again */ - TEST_ESP_OK(nvs_flash_init_custom(NVS_DEFAULT_PART_NAME, 0, 3)); + TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 3)); TEST_ESP_OK(nvs_open("namespace1", NVS_READWRITE, &handle)); TEST_ESP_OK( nvs_get_blob(handle, "singlepage", buf, &buflen)); CHECK(memcmp(buf, hexdata, buflen) == 0); Page p2; - p2.load(0); + p2.load(&f.part, 0); TEST_ESP_ERR(p2.findItem(1, ItemType::BLOB, "singlepage"), ESP_ERR_NVS_TYPE_MISMATCH); + TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name())); } TEST_CASE("Recovery from power-off during modification of blob present in old-format (different page)", "[nvs]") @@ -2398,10 +2465,10 @@ TEST_CASE("Recovery from power-off during modification of blob present in old-fo uint32_t seed = 3; gen.seed(seed); - SpiFlashEmulator emu(3); - emu.clearStats(); + PartitionEmulationFixture f(0, 3); + f.emu.clearStats(); - TEST_ESP_OK(nvs_flash_init_custom(NVS_DEFAULT_PART_NAME, 0, 3)); + TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 3)); nvs_handle_t handle; TEST_ESP_OK(nvs_open("namespace1", NVS_READWRITE, &handle)); @@ -2415,7 +2482,7 @@ TEST_CASE("Recovery from power-off during modification of blob present in old-fo /* Power-off when blob was being written on the different page where its old version in old format * was present*/ Page p; - p.load(0); + p.load(&f.part, 0); /* Write blob in old-format*/ TEST_ESP_OK(p.writeItem(1, ItemType::BLOB, "singlepage", hexdata_old, sizeof(hexdata_old))); @@ -2428,33 +2495,53 @@ TEST_CASE("Recovery from power-off during modification of blob present in old-fo item.blobIndex.chunkStart = VerOffset::VER_0_OFFSET; p.markFull(); Page p2; - p2.load(1); + p2.load(&f.part, 1); p2.setSeqNumber(1); TEST_ESP_OK(p2.writeItem(1, ItemType::BLOB_IDX, "singlepage", item.data, sizeof(item.data))); TEST_ESP_OK(p.findItem(1, ItemType::BLOB, "singlepage")); + TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name())); /* Initialize again */ - TEST_ESP_OK(nvs_flash_init_custom(NVS_DEFAULT_PART_NAME, 0, 3)); + TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 3)); TEST_ESP_OK(nvs_open("namespace1", NVS_READWRITE, &handle)); TEST_ESP_OK( nvs_get_blob(handle, "singlepage", buf, &buflen)); CHECK(memcmp(buf, hexdata, buflen) == 0); Page p3; - p3.load(0); + p3.load(&f.part, 0); TEST_ESP_ERR(p3.findItem(1, ItemType::BLOB, "singlepage"), ESP_ERR_NVS_NOT_FOUND); + + TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name())); } -static void check_nvs_part_gen_args(char const *part_name, int size, char const *filename, bool is_encr, nvs_sec_cfg_t* xts_cfg) +static void check_nvs_part_gen_args(SpiFlashEmulator *spi_flash_emulator, + char const *part_name, + int size, + char const *filename, + bool is_encr, + nvs_sec_cfg_t* xts_cfg) { nvs_handle_t handle; - if (is_encr) - TEST_ESP_OK(nvs_flash_secure_init_custom(part_name, 0, size, xts_cfg)); - else - TEST_ESP_OK( nvs_flash_init_custom(part_name, 0, size) ); + esp_partition_t esp_part; + esp_part.encrypted = false; // we're not testing generic flash encryption here, only the legacy NVS encryption + esp_part.address = 0; + esp_part.size = size * SPI_FLASH_SEC_SIZE; + strncpy(esp_part.label, part_name, PART_NAME_MAX_SIZE); + shared_ptr part; + + if (is_encr) { + NVSEncryptedPartition *enc_part = new NVSEncryptedPartition(&esp_part); + TEST_ESP_OK(enc_part->init(xts_cfg)); + part.reset(enc_part); + } else { + part.reset(new PartitionEmulation(spi_flash_emulator, 0, size, part_name)); + } + + TEST_ESP_OK( NVSPartitionManager::get_instance()->init_custom(part.get(), 0, size) ); TEST_ESP_OK( nvs_open_from_partition(part_name, "dummyNamespace", NVS_READONLY, &handle)); uint8_t u8v; @@ -2510,6 +2597,8 @@ static void check_nvs_part_gen_args(char const *part_name, int size, char const file.close(); nvs_close(handle); + + TEST_ESP_OK(nvs_flash_deinit_partition(part_name)); } @@ -2550,9 +2639,7 @@ TEST_CASE("check and read data from partition generated via partition generation SpiFlashEmulator emu("../nvs_partition_generator/partition_single_page.bin"); - TEST_ESP_OK(nvs_flash_deinit()); - - check_nvs_part_gen_args("test", 3, "../nvs_partition_generator/testdata/sample_singlepage_blob.bin", false, NULL); + check_nvs_part_gen_args(&emu, "test", 3, "../nvs_partition_generator/testdata/sample_singlepage_blob.bin", false, NULL); childpid = fork(); if (childpid == 0) { @@ -2603,7 +2690,7 @@ TEST_CASE("check and read data from partition generated via partition generation SpiFlashEmulator emu("../nvs_partition_generator/partition_multipage_blob.bin"); - check_nvs_part_gen_args("test", 4, "../nvs_partition_generator/testdata/sample_multipage_blob.bin",false,NULL); + check_nvs_part_gen_args(&emu, "test", 4, "../nvs_partition_generator/testdata/sample_multipage_blob.bin",false,NULL); childpid = fork(); if (childpid == 0) { @@ -2677,10 +2764,10 @@ TEST_CASE("check and read data from partition generated via manufacturing utilit } SpiFlashEmulator emu1("../../../tools/mass_mfg/host_test/bin/Test-1.bin"); - check_nvs_part_gen_args("test", 3, "mfg_testdata/sample_singlepage_blob.bin", false, NULL); + check_nvs_part_gen_args(&emu1, "test", 3, "mfg_testdata/sample_singlepage_blob.bin", false, NULL); SpiFlashEmulator emu2("../nvs_partition_generator/Test-1-partition.bin"); - check_nvs_part_gen_args("test", 3, "testdata/sample_singlepage_blob.bin", false, NULL); + check_nvs_part_gen_args(&emu2, "test", 3, "testdata/sample_singlepage_blob.bin", false, NULL); childpid = fork(); @@ -2758,10 +2845,10 @@ TEST_CASE("check and read data from partition generated via manufacturing utilit } SpiFlashEmulator emu1("../../../tools/mass_mfg/host_test/bin/Test-1.bin"); - check_nvs_part_gen_args("test", 4, "mfg_testdata/sample_multipage_blob.bin", false, NULL); + check_nvs_part_gen_args(&emu1, "test", 4, "mfg_testdata/sample_multipage_blob.bin", false, NULL); SpiFlashEmulator emu2("../nvs_partition_generator/Test-1-partition.bin"); - check_nvs_part_gen_args("test", 4, "testdata/sample_multipage_blob.bin", false, NULL); + check_nvs_part_gen_args(&emu2, "test", 4, "testdata/sample_multipage_blob.bin", false, NULL); childpid = fork(); if (childpid == 0) { @@ -2854,28 +2941,27 @@ TEST_CASE("check underlying xts code for 32-byte size sector encryption", "[nvs] TEST_CASE("test nvs apis with encryption enabled", "[nvs]") { - SpiFlashEmulator emu(10); - emu.randomize(100); - nvs_handle_t handle_1; const uint32_t NVS_FLASH_SECTOR = 6; const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; - emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); nvs_sec_cfg_t xts_cfg; for(int count = 0; count < NVS_KEY_SIZE; count++) { xts_cfg.eky[count] = 0x11; xts_cfg.tky[count] = 0x22; } + EncryptedPartitionFixture fixture(&xts_cfg, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN); + fixture.emu.randomize(100); + fixture.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); for (uint16_t i = NVS_FLASH_SECTOR; i + init_custom(&fixture.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)); TEST_ESP_ERR(nvs_open("namespace1", NVS_READONLY, &handle_1), ESP_ERR_NVS_NOT_FOUND); - TEST_ESP_OK(nvs_open("namespace1", NVS_READWRITE, &handle_1)); TEST_ESP_OK(nvs_set_i32(handle_1, "foo", 0x12345678)); TEST_ESP_OK(nvs_set_i32(handle_1, "foo", 0x23456789)); @@ -2960,7 +3046,7 @@ TEST_CASE("test nvs apis for nvs partition generator utility with encryption ena cfg.tky[count] = 0x22; } - check_nvs_part_gen_args(NVS_DEFAULT_PART_NAME, 4, "../nvs_partition_generator/testdata/sample_multipage_blob.bin", true, &cfg); + check_nvs_part_gen_args(&emu, NVS_DEFAULT_PART_NAME, 4, "../nvs_partition_generator/testdata/sample_multipage_blob.bin", true, &cfg); childpid = fork(); if (childpid == 0) { @@ -3086,7 +3172,6 @@ TEST_CASE("test nvs apis for nvs partition generator utility with encryption ena fclose(fp); - TEST_ESP_OK(nvs_flash_deinit()); nvs_sec_cfg_t cfg; for(int count = 0; count < NVS_KEY_SIZE; count++) { @@ -3094,7 +3179,7 @@ TEST_CASE("test nvs apis for nvs partition generator utility with encryption ena cfg.tky[count] = buffer[count+32] & 255; } - check_nvs_part_gen_args(NVS_DEFAULT_PART_NAME, 4, "../nvs_partition_generator/testdata/sample_multipage_blob.bin", true, &cfg); + check_nvs_part_gen_args(&emu, NVS_DEFAULT_PART_NAME, 4, "../nvs_partition_generator/testdata/sample_multipage_blob.bin", true, &cfg); } @@ -3154,7 +3239,6 @@ TEST_CASE("test nvs apis for nvs partition generator utility with encryption ena fclose(fp); - TEST_ESP_OK(nvs_flash_deinit()); nvs_sec_cfg_t cfg; for(int count = 0; count < NVS_KEY_SIZE; count++) { @@ -3162,7 +3246,7 @@ TEST_CASE("test nvs apis for nvs partition generator utility with encryption ena cfg.tky[count] = buffer[count+32] & 255; } - check_nvs_part_gen_args(NVS_DEFAULT_PART_NAME, 4, "../nvs_partition_generator/testdata/sample_multipage_blob.bin", true, &cfg); + check_nvs_part_gen_args(&emu, NVS_DEFAULT_PART_NAME, 4, "../nvs_partition_generator/testdata/sample_multipage_blob.bin", true, &cfg); childpid = fork(); if (childpid == 0) { @@ -3253,21 +3337,17 @@ TEST_CASE("check and read data from partition generated via manufacturing utilit SpiFlashEmulator emu1("../../../tools/mass_mfg/host_test/bin/Test-1.bin"); - TEST_ESP_OK(nvs_flash_deinit()); - nvs_sec_cfg_t cfg; for(int count = 0; count < NVS_KEY_SIZE; count++) { cfg.eky[count] = 0x11; cfg.tky[count] = 0x22; } - check_nvs_part_gen_args(NVS_DEFAULT_PART_NAME, 4, "mfg_testdata/sample_multipage_blob.bin", true, &cfg); + check_nvs_part_gen_args(&emu1, NVS_DEFAULT_PART_NAME, 4, "mfg_testdata/sample_multipage_blob.bin", true, &cfg); SpiFlashEmulator emu2("../nvs_partition_generator/Test-1-partition-encrypted.bin"); - TEST_ESP_OK(nvs_flash_deinit()); - - check_nvs_part_gen_args(NVS_DEFAULT_PART_NAME, 4, "testdata/sample_multipage_blob.bin", true, &cfg); + check_nvs_part_gen_args(&emu2, NVS_DEFAULT_PART_NAME, 4, "testdata/sample_multipage_blob.bin", true, &cfg); childpid = fork(); @@ -3376,8 +3456,6 @@ TEST_CASE("check and read data from partition generated via manufacturing utilit fclose(fp); - TEST_ESP_OK(nvs_flash_deinit()); - nvs_sec_cfg_t cfg; for(int count = 0; count < NVS_KEY_SIZE; count++) { @@ -3385,13 +3463,11 @@ TEST_CASE("check and read data from partition generated via manufacturing utilit cfg.tky[count] = buffer[count+32] & 255; } - check_nvs_part_gen_args(NVS_DEFAULT_PART_NAME, 4, "mfg_testdata/sample_multipage_blob.bin", true, &cfg); + check_nvs_part_gen_args(&emu1, NVS_DEFAULT_PART_NAME, 4, "mfg_testdata/sample_multipage_blob.bin", true, &cfg); SpiFlashEmulator emu2("../nvs_partition_generator/Test-1-partition-encrypted.bin"); - TEST_ESP_OK(nvs_flash_deinit()); - - check_nvs_part_gen_args(NVS_DEFAULT_PART_NAME, 4, "testdata/sample_multipage_blob.bin", true, &cfg); + check_nvs_part_gen_args(&emu2, NVS_DEFAULT_PART_NAME, 4, "testdata/sample_multipage_blob.bin", true, &cfg); childpid = fork(); if (childpid == 0) { diff --git a/components/nvs_flash/test_nvs_host/test_nvs_cxx_api.cpp b/components/nvs_flash/test_nvs_host/test_nvs_cxx_api.cpp index c4329c97db..da1d006be2 100644 --- a/components/nvs_flash/test_nvs_host/test_nvs_cxx_api.cpp +++ b/components/nvs_flash/test_nvs_host/test_nvs_cxx_api.cpp @@ -19,6 +19,8 @@ #include "nvs_partition_manager.hpp" #include "spi_flash_emulation.h" +#include "test_fixtures.hpp" + #include using namespace std; @@ -27,12 +29,12 @@ TEST_CASE("NVSHandleSimple CXX api open invalid arguments", "[nvs cxx]") { const uint32_t NVS_FLASH_SECTOR = 6; const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; - SpiFlashEmulator emu(10); + PartitionEmulationFixture f(0, 10, "test"); esp_err_t result; shared_ptr handle; REQUIRE(nvs::NVSPartitionManager::get_instance()-> - init_custom("test", NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK); + init_custom(&f.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK); handle = nvs::open_nvs_handle_from_partition(nullptr, "ns_1", NVS_READWRITE, &result); CHECK(result == ESP_ERR_INVALID_ARG); @@ -61,11 +63,11 @@ TEST_CASE("NVSHandleSimple CXX api open successful", "[nvs cxx]") { const uint32_t NVS_FLASH_SECTOR = 6; const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; - SpiFlashEmulator emu(10); + PartitionEmulationFixture f(0, 10, "test"); esp_err_t result; shared_ptr handle; - REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom("test", NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) + REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom(&f.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK); CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0); @@ -87,11 +89,11 @@ TEST_CASE("NVSHandleSimple CXX api open default part successful", "[nvs cxx]") { const uint32_t NVS_FLASH_SECTOR = 6; const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; - SpiFlashEmulator emu(10); + PartitionEmulationFixture f(0, 10); esp_err_t result; shared_ptr handle; - REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom("nvs", NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) + REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom(&f.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK); CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0); @@ -113,11 +115,11 @@ TEST_CASE("NVSHandleSimple CXX api open default part ns NULL", "[nvs cxx]") { const uint32_t NVS_FLASH_SECTOR = 6; const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; - SpiFlashEmulator emu(10); + PartitionEmulationFixture f(0, 10); esp_err_t result; shared_ptr handle; - REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom("nvs", NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) + REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom(&f.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK); CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0); @@ -135,12 +137,12 @@ TEST_CASE("NVSHandleSimple CXX api read/write string", "[nvs cxx]") { const uint32_t NVS_FLASH_SECTOR = 6; const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; - SpiFlashEmulator emu(10); + PartitionEmulationFixture f(0, 10); char read_buffer [256]; esp_err_t result; shared_ptr handle; - REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom("nvs", NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) + REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom(&f.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK); CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0); @@ -164,13 +166,13 @@ TEST_CASE("NVSHandleSimple CXX api read/write blob", "[nvs cxx]") { const uint32_t NVS_FLASH_SECTOR = 6; const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; - SpiFlashEmulator emu(10); + PartitionEmulationFixture f(0, 10); const char blob [6] = {15, 16, 17, 18, 19}; char read_blob[6] = {0}; esp_err_t result; shared_ptr handle; - REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom("nvs", NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) + REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom(&f.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK); CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0); diff --git a/components/nvs_flash/test_nvs_host/test_nvs_handle.cpp b/components/nvs_flash/test_nvs_host/test_nvs_handle.cpp index 3374d25192..d04c67dc11 100644 --- a/components/nvs_flash/test_nvs_host/test_nvs_handle.cpp +++ b/components/nvs_flash/test_nvs_host/test_nvs_handle.cpp @@ -19,6 +19,8 @@ #include "nvs_partition_manager.hpp" #include "spi_flash_emulation.h" +#include "test_fixtures.hpp" + #include #include @@ -29,9 +31,9 @@ TEST_CASE("NVSHandleSimple closes its reference in PartitionManager", "[partitio { const uint32_t NVS_FLASH_SECTOR = 6; const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; - SpiFlashEmulator emu(10); + PartitionEmulationFixture f(0, 10, "test"); - REQUIRE(NVSPartitionManager::get_instance()->init_custom("test", NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) + REQUIRE(NVSPartitionManager::get_instance()->init_custom(&f.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK); CHECK(NVSPartitionManager::get_instance()->open_handles_size() == 0); @@ -53,9 +55,9 @@ TEST_CASE("NVSHandleSimple multiple open and closes with PartitionManager", "[pa { const uint32_t NVS_FLASH_SECTOR = 6; const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; - SpiFlashEmulator emu(10); + PartitionEmulationFixture f(0, 10, "test"); - REQUIRE(NVSPartitionManager::get_instance()->init_custom("test", NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) + REQUIRE(NVSPartitionManager::get_instance()->init_custom(&f.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK); CHECK(NVSPartitionManager::get_instance()->open_handles_size() == 0); @@ -83,18 +85,18 @@ TEST_CASE("NVSHandleSimple multiple open and closes with PartitionManager", "[pa } -TEST_CASE("nvshandle readonly fails", "[partition_mgr]") +TEST_CASE("NVSHandleSimple readonly fails", "[partition_mgr]") { - SpiFlashEmulator emu(10); + PartitionEmulationFixture f(0, 10); NVSPartitionManager::get_instance()->deinit_partition(NVS_DEFAULT_PART_NAME); NVSHandleSimple *handle_1; NVSHandleSimple *handle_2; const uint32_t NVS_FLASH_SECTOR = 6; const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; - emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); + f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); - CHECK(NVSPartitionManager::get_instance()->init_custom(NVS_DEFAULT_PART_NAME, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK); + CHECK(NVSPartitionManager::get_instance()->init_custom(&f.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK); CHECK(NVSPartitionManager::get_instance()->open_handles_size() == 0); // first, creating namespace... @@ -123,13 +125,13 @@ TEST_CASE("NVSHandleSimple set/get char", "[partition_mgr]") BAR }; - SpiFlashEmulator emu(10); + PartitionEmulationFixture f(0, 10); const uint32_t NVS_FLASH_SECTOR = 6; const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; - emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); + f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); - REQUIRE(NVSPartitionManager::get_instance()->init_custom(NVS_DEFAULT_PART_NAME, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) + REQUIRE(NVSPartitionManager::get_instance()->init_custom(&f.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK); NVSHandleSimple *handle; @@ -155,13 +157,13 @@ TEST_CASE("NVSHandleSimple correctly sets/gets int enum", "[partition_mgr]") BAR }; - SpiFlashEmulator emu(10); + PartitionEmulationFixture f(0, 10); const uint32_t NVS_FLASH_SECTOR = 6; const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; - emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); + f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); - REQUIRE(NVSPartitionManager::get_instance()->init_custom(NVS_DEFAULT_PART_NAME, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) + REQUIRE(NVSPartitionManager::get_instance()->init_custom(&f.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK); NVSHandleSimple *handle; @@ -188,13 +190,13 @@ TEST_CASE("NVSHandleSimple correctly sets/gets int enum with negative values", " BAR }; - SpiFlashEmulator emu(10); + PartitionEmulationFixture f(0, 10); const uint32_t NVS_FLASH_SECTOR = 6; const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; - emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); + f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); - REQUIRE(NVSPartitionManager::get_instance()->init_custom(NVS_DEFAULT_PART_NAME, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) + REQUIRE(NVSPartitionManager::get_instance()->init_custom(&f.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK); NVSHandleSimple *handle; @@ -220,13 +222,13 @@ TEST_CASE("NVSHandleSimple correctly sets/gets uint8_t enum", "[partition_mgr]") BAR }; - SpiFlashEmulator emu(10); + PartitionEmulationFixture f(0, 10); const uint32_t NVS_FLASH_SECTOR = 6; const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; - emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); + f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); - REQUIRE(NVSPartitionManager::get_instance()->init_custom(NVS_DEFAULT_PART_NAME, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) + REQUIRE(NVSPartitionManager::get_instance()->init_custom(&f.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK); NVSHandleSimple *handle; @@ -253,13 +255,13 @@ TEST_CASE("NVSHandleSimple correctly sets/gets char enum", "[partition_mgr]") BAR }; - SpiFlashEmulator emu(10); + PartitionEmulationFixture f(0, 10); const uint32_t NVS_FLASH_SECTOR = 6; const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; - emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); + f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); - REQUIRE(NVSPartitionManager::get_instance()->init_custom(NVS_DEFAULT_PART_NAME, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) + REQUIRE(NVSPartitionManager::get_instance()->init_custom(&f.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK); NVSHandleSimple *handle; diff --git a/components/nvs_flash/test_nvs_host/test_nvs_partition.cpp b/components/nvs_flash/test_nvs_host/test_nvs_partition.cpp new file mode 100644 index 0000000000..4385b9a6dc --- /dev/null +++ b/components/nvs_flash/test_nvs_host/test_nvs_partition.cpp @@ -0,0 +1,55 @@ +// 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 "catch.hpp" +#include +#include +#include "nvs_test_api.h" +#include "nvs_handle_simple.hpp" +#include "nvs_partition.hpp" +#include "spi_flash_emulation.h" + +#include "test_fixtures.hpp" + +#include + +using namespace std; +using namespace nvs; + +TEST_CASE("encrypted partition read size must be item size", "[nvs]") +{ + char foo [32] = { }; + nvs_sec_cfg_t xts_cfg; + for(int count = 0; count < NVS_KEY_SIZE; count++) { + xts_cfg.eky[count] = 0x11; + xts_cfg.tky[count] = 0x22; + } + EncryptedPartitionFixture fix(&xts_cfg); + + CHECK(fix.part.read(0, foo, sizeof (foo) -1) == ESP_ERR_INVALID_SIZE); +} + +TEST_CASE("encrypted partition write size must be mod item size", "[nvs]") +{ + char foo [64] = { }; + nvs_sec_cfg_t xts_cfg; + for(int count = 0; count < NVS_KEY_SIZE; count++) { + xts_cfg.eky[count] = 0x11; + xts_cfg.tky[count] = 0x22; + } + EncryptedPartitionFixture fix(&xts_cfg); + + CHECK(fix.part.write(0, foo, sizeof (foo) -1) == ESP_ERR_INVALID_SIZE); + CHECK(fix.part.write(0, foo, sizeof (foo)) == ESP_OK); + CHECK(fix.part.write(0, foo, sizeof (foo) * 2) == ESP_OK); +} diff --git a/components/nvs_flash/test_nvs_host/test_nvs_storage.cpp b/components/nvs_flash/test_nvs_host/test_nvs_storage.cpp index 805434dad4..9d3dbc57bd 100644 --- a/components/nvs_flash/test_nvs_host/test_nvs_storage.cpp +++ b/components/nvs_flash/test_nvs_host/test_nvs_storage.cpp @@ -18,6 +18,8 @@ #include "nvs_partition_manager.hpp" #include "spi_flash_emulation.h" +#include "test_fixtures.hpp" + #include using namespace std; @@ -27,9 +29,9 @@ TEST_CASE("Storage iterator recognizes blob with VerOffset::VER_1_OFFSET", "[nvs { const uint32_t NVS_FLASH_SECTOR = 6; const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; - SpiFlashEmulator emu(10); + PartitionEmulationFixture f(0, 10, "test"); - REQUIRE(NVSPartitionManager::get_instance()->init_custom("test", NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) + REQUIRE(NVSPartitionManager::get_instance()->init_custom(&f.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK); uint8_t blob [] = {0x0, 0x1, 0x2, 0x3}; diff --git a/components/nvs_flash/test_nvs_host/test_partition_manager.cpp b/components/nvs_flash/test_nvs_host/test_partition_manager.cpp index e6326b4036..3051187784 100644 --- a/components/nvs_flash/test_nvs_host/test_partition_manager.cpp +++ b/components/nvs_flash/test_nvs_host/test_partition_manager.cpp @@ -20,25 +20,28 @@ #include "spi_flash_emulation.h" #include "nvs_test_api.h" +#include "test_fixtures.hpp" + using namespace nvs; TEST_CASE("Partition manager initializes storage", "[partition_mgr]") { const uint32_t NVS_FLASH_SECTOR = 6; const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; - SpiFlashEmulator emu(10); + PartitionEmulationFixture f(0, 10, "test"); - REQUIRE(NVSPartitionManager::get_instance()->init_custom("test", NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK); + REQUIRE(NVSPartitionManager::get_instance()->init_custom(&f.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK); CHECK(NVSPartitionManager::get_instance()->lookup_storage_from_name("test") != nullptr); + REQUIRE(NVSPartitionManager::get_instance()->deinit_partition(f.part.get_partition_name()) == ESP_OK); } TEST_CASE("Partition manager de-initializes storage", "[partition_mgr]") { const uint32_t NVS_FLASH_SECTOR = 6; const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; - SpiFlashEmulator emu(10); + PartitionEmulationFixture f(0, 10, "test"); - REQUIRE(NVSPartitionManager::get_instance()->init_custom("test", NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK); + REQUIRE(NVSPartitionManager::get_instance()->init_custom(&f.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK); CHECK(NVSPartitionManager::get_instance()->lookup_storage_from_name("test") != nullptr); CHECK(NVSPartitionManager::get_instance()->deinit_partition("test") == ESP_OK); CHECK(NVSPartitionManager::get_instance()->lookup_storage_from_name("test") == nullptr); @@ -49,11 +52,13 @@ TEST_CASE("Partition manager initializes multiple partitions", "[partition_mgr]" const uint32_t NVS_FLASH_SECTOR = 6; const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; SpiFlashEmulator emu(10); + PartitionEmulation part_0(&emu, NVS_FLASH_SECTOR * SPI_FLASH_SEC_SIZE, NVS_FLASH_SECTOR_COUNT_MIN * SPI_FLASH_SEC_SIZE, "test1"); + PartitionEmulation part_1(&emu, NVS_FLASH_SECTOR * SPI_FLASH_SEC_SIZE, NVS_FLASH_SECTOR_COUNT_MIN * SPI_FLASH_SEC_SIZE, "test2"); - REQUIRE(NVSPartitionManager::get_instance()->init_custom("test1", NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) + REQUIRE(NVSPartitionManager::get_instance()->init_custom(&part_0, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK); // TODO: why does this work, actually? same sectors used as above - REQUIRE(NVSPartitionManager::get_instance()->init_custom("test2", NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) + REQUIRE(NVSPartitionManager::get_instance()->init_custom(&part_1, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK); Storage *storage1 = NVSPartitionManager::get_instance()->lookup_storage_from_name("test1"); REQUIRE(storage1 != nullptr); @@ -61,15 +66,17 @@ TEST_CASE("Partition manager initializes multiple partitions", "[partition_mgr]" REQUIRE(storage2 != nullptr); CHECK(storage1 != storage2); + REQUIRE(NVSPartitionManager::get_instance()->deinit_partition(part_0.get_partition_name()) == ESP_OK); + REQUIRE(NVSPartitionManager::get_instance()->deinit_partition(part_1.get_partition_name()) == ESP_OK); } TEST_CASE("Partition manager invalidates handle on partition de-init", "[partition_mgr]") { const uint32_t NVS_FLASH_SECTOR = 6; const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; - SpiFlashEmulator emu(10); + PartitionEmulationFixture f(0, 10, "test"); - REQUIRE(NVSPartitionManager::get_instance()->init_custom("test", NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) + REQUIRE(NVSPartitionManager::get_instance()->init_custom(&f.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK); NVSHandleSimple *handle; diff --git a/components/nvs_flash/test_nvs_host/test_spi_flash_emulation.cpp b/components/nvs_flash/test_nvs_host/test_spi_flash_emulation.cpp index 2913a7ed7b..22afbe9b96 100644 --- a/components/nvs_flash/test_nvs_host/test_spi_flash_emulation.cpp +++ b/components/nvs_flash/test_nvs_host/test_spi_flash_emulation.cpp @@ -11,9 +11,9 @@ // 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 "catch.hpp" #include "esp_spi_flash.h" +#include "esp_partition.h" #include "spi_flash_emulation.h" #include @@ -25,14 +25,21 @@ bool range_empty_n(Tit it_begin, size_t n) return all_of(it_begin, it_begin + n, bind(equal_to(), placeholders::_1, 0xffffffff)); } +struct FlashEmuFixture { + FlashEmuFixture(size_t sectors) : esp_part(), emu(sectors) { } + + esp_partition_t esp_part; + SpiFlashEmulator emu; +}; + TEST_CASE("flash starts with all bytes == 0xff", "[spi_flash_emu]") { - SpiFlashEmulator emu(4); + FlashEmuFixture f(4); uint8_t sector[SPI_FLASH_SEC_SIZE]; for (int i = 0; i < 4; ++i) { - CHECK(spi_flash_read(0, sector, sizeof(sector)) == ESP_OK); + CHECK(esp_partition_read(&f.esp_part, 0, sector, sizeof(sector)) == ESP_OK); for (auto v: sector) { CHECK(v == 0xff); } @@ -41,116 +48,139 @@ TEST_CASE("flash starts with all bytes == 0xff", "[spi_flash_emu]") TEST_CASE("invalid writes are checked", "[spi_flash_emu]") { - SpiFlashEmulator emu(1); + FlashEmuFixture f(1); uint32_t val = 0; - CHECK(spi_flash_write(0, &val, 4) == ESP_OK); + CHECK(esp_partition_write(&f.esp_part, 0, &val, 4) == ESP_OK); val = 1; - CHECK(spi_flash_write(0, &val, 4) == ESP_ERR_FLASH_OP_FAIL); + CHECK(esp_partition_write(&f.esp_part, 0, &val, 4) == ESP_ERR_FLASH_OP_FAIL); } TEST_CASE("out of bounds writes fail", "[spi_flash_emu]") { - SpiFlashEmulator emu(4); + FlashEmuFixture f(4); uint32_t vals[8]; std::fill_n(vals, 8, 0); - CHECK(spi_flash_write(0, vals, sizeof(vals)) == ESP_OK); + CHECK(esp_partition_write(&f.esp_part, 0, &vals, sizeof(vals)) == ESP_OK); - CHECK(spi_flash_write(4*4096 - sizeof(vals), vals, sizeof(vals)) == ESP_OK); + CHECK(esp_partition_write(&f.esp_part, 4*4096 - sizeof(vals), &vals, sizeof(vals)) == ESP_OK); - CHECK(spi_flash_write(4*4096 - sizeof(vals) + 4, vals, sizeof(vals)) == ESP_ERR_FLASH_OP_FAIL); + CHECK(esp_partition_write(&f.esp_part, 4*4096 - sizeof(vals) + 4, &vals, sizeof(vals)) == ESP_ERR_FLASH_OP_FAIL); } - TEST_CASE("after erase the sector is set to 0xff", "[spi_flash_emu]") { - SpiFlashEmulator emu(4); + FlashEmuFixture f(4); uint32_t val1 = 0xab00cd12; - CHECK(spi_flash_write(0, &val1, sizeof(val1)) == ESP_OK); + CHECK(esp_partition_write(&f.esp_part, 0, &val1, sizeof(val1)) == ESP_OK); uint32_t val2 = 0x5678efab; - CHECK(spi_flash_write(4096 - 4, &val2, sizeof(val2)) == ESP_OK); + CHECK(esp_partition_write(&f.esp_part, 4096 - 4, &val2, sizeof(val2)) == ESP_OK); - CHECK(emu.words()[0] == val1); - CHECK(range_empty_n(emu.words() + 1, 4096 / 4 - 2)); - CHECK(emu.words()[4096 / 4 - 1] == val2); + CHECK(f.emu.words()[0] == val1); + CHECK(range_empty_n(f.emu.words() + 1, 4096 / 4 - 2)); + CHECK(f.emu.words()[4096 / 4 - 1] == val2); - CHECK(spi_flash_erase_sector(0) == ESP_OK); + CHECK(esp_partition_erase_range(&f.esp_part, 0, SPI_FLASH_SEC_SIZE) == ESP_OK); - CHECK(emu.words()[0] == 0xffffffff); - CHECK(range_empty_n(emu.words() + 1, 4096 / 4 - 2)); - CHECK(emu.words()[4096 / 4 - 1] == 0xffffffff); + CHECK(f.emu.words()[0] == 0xffffffff); + CHECK(range_empty_n(f.emu.words() + 1, 4096 / 4 - 2)); + CHECK(f.emu.words()[4096 / 4 - 1] == 0xffffffff); +} + +TEST_CASE("EMU raw read function works", "[spi_flash_emu]") +{ + FlashEmuFixture f(4); + uint32_t value = 0xdeadbeef; + uint32_t read_value = 0; + CHECK(esp_partition_write(&f.esp_part, 0, &value, sizeof(value)) == ESP_OK); + + CHECK(esp_partition_read_raw(&f.esp_part, 0, &read_value, sizeof(&read_value)) == ESP_OK); + + CHECK(read_value == 0xdeadbeef); +} + +TEST_CASE("EMU raw write function works", "[spi_flash_emu]") +{ + FlashEmuFixture f(4); + uint32_t value = 0xdeadbeef; + uint32_t read_value = 0; + CHECK(esp_partition_write_raw(&f.esp_part, 0, &value, sizeof(value)) == ESP_OK); + + CHECK(esp_partition_read(&f.esp_part, 0, &read_value, sizeof(&read_value)) == ESP_OK); + + CHECK(read_value == 0xdeadbeef); } TEST_CASE("read/write/erase operation times are calculated correctly", "[spi_flash_emu]") { - SpiFlashEmulator emu(1); + FlashEmuFixture f(1); uint8_t data[512]; - spi_flash_read(0, data, 4); - CHECK(emu.getTotalTime() == 7); - CHECK(emu.getReadOps() == 1); - CHECK(emu.getReadBytes() == 4); - emu.clearStats(); - spi_flash_read(0, data, 8); - CHECK(emu.getTotalTime() == 5); - CHECK(emu.getReadOps() == 1); - CHECK(emu.getReadBytes() == 8); - emu.clearStats(); - spi_flash_read(0, data, 16); - CHECK(emu.getTotalTime() == 6); - CHECK(emu.getReadOps() == 1); - CHECK(emu.getReadBytes() == 16); - emu.clearStats(); - spi_flash_read(0, data, 128); - CHECK(emu.getTotalTime() == 18); - CHECK(emu.getReadOps() == 1); - CHECK(emu.getReadBytes() == 128); - emu.clearStats(); - spi_flash_read(0, data, 256); - CHECK(emu.getTotalTime() == 32); - emu.clearStats(); - spi_flash_read(0, data, (128+256)/2); - CHECK(emu.getTotalTime() == (18+32)/2); - emu.clearStats(); + esp_partition_read(&f.esp_part, 0, data, 4); + CHECK(f.emu.getTotalTime() == 7); + CHECK(f.emu.getReadOps() == 1); + CHECK(f.emu.getReadBytes() == 4); + f.emu.clearStats(); + esp_partition_read(&f.esp_part, 0, data, 8); + CHECK(f.emu.getTotalTime() == 5); + CHECK(f.emu.getReadOps() == 1); + CHECK(f.emu.getReadBytes() == 8); + f.emu.clearStats(); + esp_partition_read(&f.esp_part, 0, data, 16); + CHECK(f.emu.getTotalTime() == 6); + CHECK(f.emu.getReadOps() == 1); + CHECK(f.emu.getReadBytes() == 16); + f.emu.clearStats(); + esp_partition_read(&f.esp_part, 0, data, 128); + CHECK(f.emu.getTotalTime() == 18); + CHECK(f.emu.getReadOps() == 1); + CHECK(f.emu.getReadBytes() == 128); + f.emu.clearStats(); + esp_partition_read(&f.esp_part, 0, data, 256); + CHECK(f.emu.getTotalTime() == 32); + f.emu.clearStats(); + esp_partition_read(&f.esp_part, 0, data, (128+256)/2); + CHECK(f.emu.getTotalTime() == (18+32)/2); + f.emu.clearStats(); - spi_flash_write(0, data, 4); - CHECK(emu.getTotalTime() == 19); - CHECK(emu.getWriteOps() == 1); - CHECK(emu.getWriteBytes() == 4); - emu.clearStats(); - CHECK(emu.getWriteOps() == 0); - CHECK(emu.getWriteBytes() == 0); - spi_flash_write(0, data, 8); - CHECK(emu.getTotalTime() == 23); - emu.clearStats(); - spi_flash_write(0, data, 16); - CHECK(emu.getTotalTime() == 35); - CHECK(emu.getWriteOps() == 1); - CHECK(emu.getWriteBytes() == 16); - emu.clearStats(); - spi_flash_write(0, data, 128); - CHECK(emu.getTotalTime() == 205); - emu.clearStats(); - spi_flash_write(0, data, 256); - CHECK(emu.getTotalTime() == 417); - emu.clearStats(); - spi_flash_write(0, data, (128+256)/2); - CHECK(emu.getTotalTime() == (205+417)/2); - emu.clearStats(); + esp_partition_write(&f.esp_part, 0, data, 4); + CHECK(f.emu.getTotalTime() == 19); + CHECK(f.emu.getWriteOps() == 1); + CHECK(f.emu.getWriteBytes() == 4); + f.emu.clearStats(); + CHECK(f.emu.getWriteOps() == 0); + CHECK(f.emu.getWriteBytes() == 0); + esp_partition_write(&f.esp_part, 0, data, 8); + CHECK(f.emu.getTotalTime() == 23); + f.emu.clearStats(); + esp_partition_write(&f.esp_part, 0, data, 16); + CHECK(f.emu.getTotalTime() == 35); + CHECK(f.emu.getWriteOps() == 1); + CHECK(f.emu.getWriteBytes() == 16); + f.emu.clearStats(); + esp_partition_write(&f.esp_part, 0, data, 128); + CHECK(f.emu.getTotalTime() == 205); + f.emu.clearStats(); + esp_partition_write(&f.esp_part, 0, data, 256); + CHECK(f.emu.getTotalTime() == 417); + f.emu.clearStats(); + esp_partition_write(&f.esp_part, 0, data, (128+256)/2); + CHECK(f.emu.getTotalTime() == (205+417)/2); + f.emu.clearStats(); - spi_flash_erase_sector(0); - CHECK(emu.getEraseOps() == 1); - CHECK(emu.getTotalTime() == 37142); + esp_partition_erase_range(&f.esp_part, 0, SPI_FLASH_SEC_SIZE); + CHECK(f.emu.getEraseOps() == 1); + CHECK(f.emu.getTotalTime() == 37142); } TEST_CASE("data is randomized predictably", "[spi_flash_emu]") { SpiFlashEmulator emu1(3); emu1.randomize(0x12345678); - + SpiFlashEmulator emu2(3); emu2.randomize(0x12345678); - + CHECK(std::equal(emu1.bytes(), emu1.bytes() + emu1.size(), emu2.bytes())); } diff --git a/components/partition_table/test/test_partition.c b/components/partition_table/test/test_partition.c index e023132d71..15088728bb 100644 --- a/components/partition_table/test/test_partition.c +++ b/components/partition_table/test/test_partition.c @@ -10,7 +10,7 @@ TEST_CASE("Can read partition table", "[partition]") const esp_partition_t *p = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_ANY, NULL); TEST_ASSERT_NOT_NULL(p); - TEST_ASSERT_EQUAL(0x10000, p->address); + TEST_ASSERT_EQUAL(0x20000, p->address); TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_FACTORY, p->subtype); esp_partition_iterator_t it = esp_partition_find(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, NULL); diff --git a/components/spi_flash/include/esp_partition.h b/components/spi_flash/include/esp_partition.h index 72c543bd4b..930f7d69aa 100644 --- a/components/spi_flash/include/esp_partition.h +++ b/components/spi_flash/include/esp_partition.h @@ -202,6 +202,9 @@ const esp_partition_t *esp_partition_verify(const esp_partition_t *partition); /** * @brief Read data from the partition * + * Partitions marked with an encryption flag will automatically be + * be read and decrypted via a cache mapping. + * * @param partition Pointer to partition structure obtained using * esp_partition_find_first or esp_partition_get. * Must be non-NULL. @@ -250,7 +253,59 @@ 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 void* src, size_t size); + size_t dst_offset, const void* src, size_t size); + +/** + * @brief Read data from the partition + * + * @note This function is essentially the same as \c esp_partition_write() above. + * It just never decrypts data but returns it as is. + * + * @param partition Pointer to partition structure obtained using + * esp_partition_find_first or esp_partition_get. + * Must be non-NULL. + * @param dst Pointer to the buffer where data should be stored. + * Pointer must be non-NULL and buffer must be at least 'size' bytes long. + * @param src_offset Address of the data to be read, relative to the + * beginning of the partition. + * @param size Size of data to be read, in bytes. + * + * @return ESP_OK, if data was read successfully; + * ESP_ERR_INVALID_ARG, if src_offset exceeds partition size; + * ESP_ERR_INVALID_SIZE, if read would go out of bounds of the partition; + * or one of error codes from lower-level flash driver. + */ +esp_err_t esp_partition_read_raw(const esp_partition_t* partition, + size_t src_offset, void* dst, size_t size); + +/** + * @brief Write data to the partition without any transformation/encryption. + * + * @note This function is essentially the same as \c esp_partition_write() above. + * It just never encrypts data but writes it as is. + * + * Before writing data to flash, corresponding region of flash needs to be erased. + * This can be done using esp_partition_erase_range function. + * + * @param partition Pointer to partition structure obtained using + * esp_partition_find_first or esp_partition_get. + * Must be non-NULL. + * @param dst_offset Address where the data should be written, relative to the + * beginning of the partition. + * @param src Pointer to the source buffer. Pointer must be non-NULL and + * buffer must be at least 'size' bytes long. + * @param size Size of data to be written, in bytes. + * + * @note Prior to writing to flash memory, make sure it has been erased with + * esp_partition_erase_range call. + * + * @return ESP_OK, if data was written successfully; + * ESP_ERR_INVALID_ARG, if dst_offset exceeds partition size; + * ESP_ERR_INVALID_SIZE, if write would go out of bounds of the partition; + * or one of the error codes from lower-level flash driver. + */ +esp_err_t esp_partition_write_raw(const esp_partition_t* partition, + size_t dst_offset, const void* src, size_t size); /** * @brief Erase part of the partition diff --git a/components/spi_flash/partition.c b/components/spi_flash/partition.c index 4457d63360..27844f38a1 100644 --- a/components/spi_flash/partition.c +++ b/components/spi_flash/partition.c @@ -402,6 +402,43 @@ esp_err_t esp_partition_write(const esp_partition_t* partition, } } +esp_err_t esp_partition_read_raw(const esp_partition_t* partition, + size_t src_offset, void* dst, size_t size) +{ + assert(partition != NULL); + if (src_offset > partition->size) { + return ESP_ERR_INVALID_ARG; + } + if (src_offset + size > partition->size) { + return ESP_ERR_INVALID_SIZE; + } + +#ifndef CONFIG_SPI_FLASH_USE_LEGACY_IMPL + return esp_flash_read(partition->flash_chip, dst, partition->address + src_offset, size); +#else + return spi_flash_read(partition->address + src_offset, dst, size); +#endif // CONFIG_SPI_FLASH_USE_LEGACY_IMPL +} + +esp_err_t esp_partition_write_raw(const esp_partition_t* partition, + size_t dst_offset, const void* src, size_t size) +{ + assert(partition != NULL); + if (dst_offset > partition->size) { + return ESP_ERR_INVALID_ARG; + } + if (dst_offset + size > partition->size) { + return ESP_ERR_INVALID_SIZE; + } + dst_offset = partition->address + dst_offset; + +#ifndef CONFIG_SPI_FLASH_USE_LEGACY_IMPL + return esp_flash_write(partition->flash_chip, src, dst_offset, size); +#else + return spi_flash_write(dst_offset, src, size); +#endif // CONFIG_SPI_FLASH_USE_LEGACY_IMPL +} + esp_err_t esp_partition_erase_range(const esp_partition_t* partition, size_t offset, size_t size) { diff --git a/tools/unit-test-app/partition_table_unit_test_app.csv b/tools/unit-test-app/partition_table_unit_test_app.csv index 948f00c5c1..4b2bdeeacb 100644 --- a/tools/unit-test-app/partition_table_unit_test_app.csv +++ b/tools/unit-test-app/partition_table_unit_test_app.csv @@ -2,10 +2,10 @@ # # Name, Type, SubType, Offset, Size, Flags # Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap -nvs, data, nvs, 0x9000, 0x4000 -otadata, data, ota, 0xd000, 0x2000 -phy_init, data, phy, 0xf000, 0x1000 -factory, 0, 0, 0x10000, 0x260000 +nvs, data, nvs, 0xb000, 0x5000 +otadata, data, ota, 0x10000, 0x2000 +phy_init, data, phy, 0x12000, 0x1000 +factory, 0, 0, 0x20000, 0x260000 # these OTA partitions are used for tests, but can't fit real OTA apps in them # (done this way to reduce total flash usage.) ota_0, 0, ota_0, , 64K