NVS: using esp_partition API

* partition api changed from spi_flash* API to
  esp_partition* API and is abstracted as a C++
  interface.
* The old nvs encryption is still possible
* changed default unit test app partition table
* Partitions coming from esp_partition API are
  checked for generic flash encryption. If yes,
  an error is returned since generic flash
  encryption isn't compatible with nvs
  encryption
* esp32, esp32s2 tests don't require nvs_flash
  but mbedtls now

Closes IDF-1340
Closes IDF-858
This commit is contained in:
Jakob Hasse 2020-04-27 08:51:31 +08:00
parent 153c2e7406
commit aca9ec28b3
41 changed files with 1641 additions and 958 deletions

View File

@ -1,7 +1,7 @@
if(IDF_TARGET STREQUAL "esp32") if(IDF_TARGET STREQUAL "esp32")
idf_component_register(SRC_DIRS . idf_component_register(SRC_DIRS .
PRIV_INCLUDE_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") target_link_libraries(${COMPONENT_LIB} INTERFACE "-u ld_include_test_dport_xt_highint5")
endif() endif()

View File

@ -1,7 +1,7 @@
if(IDF_TARGET STREQUAL "esp32s2") if(IDF_TARGET STREQUAL "esp32s2")
idf_component_register(SRC_DIRS . idf_component_register(SRC_DIRS .
PRIV_INCLUDE_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() endif()

View File

@ -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 ERR_TBL_IT(ESP_ERR_NVS_CONTENT_DIFFERS), /* 4376 0x1118 Internal error; never returned by nvs
API functions. NVS key is different in API functions. NVS key is different in
comparison */ 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 # endif
// components/ulp/include/ulp_common.h // components/ulp/include/ulp_common.h
# ifdef ESP_ERR_ULP_BASE # ifdef ESP_ERR_ULP_BASE

View File

@ -1,16 +1,18 @@
set(srcs "src/nvs_api.cpp" set(srcs "src/nvs_api.cpp"
"src/nvs_cxx_api.cpp" "src/nvs_cxx_api.cpp"
"src/nvs_item_hash_list.cpp" "src/nvs_item_hash_list.cpp"
"src/nvs_ops.cpp"
"src/nvs_page.cpp" "src/nvs_page.cpp"
"src/nvs_pagemanager.cpp" "src/nvs_pagemanager.cpp"
"src/nvs_storage.cpp" "src/nvs_storage.cpp"
"src/nvs_handle_simple.cpp" "src/nvs_handle_simple.cpp"
"src/nvs_handle_locked.cpp" "src/nvs_handle_locked.cpp"
"src/nvs_partition.cpp"
"src/nvs_partition_lookup.cpp"
"src/nvs_partition_manager.cpp" "src/nvs_partition_manager.cpp"
"src/nvs_types.cpp") "src/nvs_types.cpp")
if(CONFIG_NVS_ENCRYPTION) if(CONFIG_NVS_ENCRYPTION)
list(APPEND srcs "src/nvs_encr.cpp") list(APPEND srcs "src/nvs_encrypted_partition.cpp")
endif() endif()
idf_component_register(SRCS "${srcs}" idf_component_register(SRCS "${srcs}"

View File

@ -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_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_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_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 */ #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 */

View File

@ -20,9 +20,6 @@
#include "esp_partition.h" #include "esp_partition.h"
#include "sdkconfig.h" #include "sdkconfig.h"
#include "nvs_handle_simple.hpp" #include "nvs_handle_simple.hpp"
#ifdef CONFIG_NVS_ENCRYPTION
#include "nvs_encr.hpp"
#endif
#ifdef ESP_PLATFORM #ifdef ESP_PLATFORM
#include <esp32/rom/crc.h> #include <esp32/rom/crc.h>
@ -57,14 +54,9 @@ private:
uint32_t NVSHandleEntry::s_nvs_next_handle; uint32_t NVSHandleEntry::s_nvs_next_handle;
extern "C" void nvs_dump(const char *partName); 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 #ifdef ESP_PLATFORM
SemaphoreHandle_t nvs::Lock::mSemaphore = NULL; SemaphoreHandle_t nvs::Lock::mSemaphore = nullptr;
#endif #endif
using namespace std; using namespace std;
@ -83,41 +75,13 @@ extern "C" void nvs_dump(const char *partName)
nvs::Storage* pStorage; nvs::Storage* pStorage;
pStorage = lookup_storage_from_name(partName); pStorage = lookup_storage_from_name(partName);
if (pStorage == NULL) { if (pStorage == nullptr) {
return; return;
} }
pStorage->debugDump(); 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) static esp_err_t close_handles_and_deinit(const char* part_name)
{ {
// Delete all corresponding open handles // 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::init();
Lock lock; Lock lock;
if (!partition) { if (partition == nullptr) {
return ESP_ERR_INVALID_ARG; return ESP_ERR_INVALID_ARG;
} }
return nvs_flash_init_custom(partition->label, NVSPartition *part = new (std::nothrow) NVSPartition(partition);
partition->address / SPI_FLASH_SEC_SIZE, if (part == nullptr) {
partition->size / SPI_FLASH_SEC_SIZE); 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 #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::init();
Lock lock; Lock lock;
nvs::Storage* mStorage;
mStorage = lookup_storage_from_name(part_name); return NVSPartitionManager::get_instance()->secure_init_partition(part_name, cfg);
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);
} }
extern "C" esp_err_t nvs_flash_secure_init(nvs_sec_cfg_t* 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( const esp_partition_t* partition = esp_partition_find_first(
ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, part_name); ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, part_name);
if (partition == NULL) { if (partition == nullptr) {
return ESP_ERR_NOT_FOUND; 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::init();
Lock lock; Lock lock;
if (!partition) { if (partition == nullptr) {
return ESP_ERR_INVALID_ARG; 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; Lock lock;
nvs::Storage* pStorage; nvs::Storage* pStorage;
if (nvs_stats == NULL) { if (nvs_stats == nullptr) {
return ESP_ERR_INVALID_ARG; return ESP_ERR_INVALID_ARG;
} }
nvs_stats->used_entries = 0; 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->total_entries = 0;
nvs_stats->namespace_count = 0; nvs_stats->namespace_count = 0;
pStorage = lookup_storage_from_name((part_name == NULL) ? NVS_DEFAULT_PART_NAME : part_name); pStorage = lookup_storage_from_name((part_name == nullptr) ? NVS_DEFAULT_PART_NAME : part_name);
if (pStorage == NULL) { if (pStorage == nullptr) {
return ESP_ERR_NVS_NOT_INITIALIZED; 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) extern "C" esp_err_t nvs_get_used_entry_count(nvs_handle_t c_handle, size_t* used_entries)
{ {
Lock lock; Lock lock;
if(used_entries == NULL){ if(used_entries == nullptr){
return ESP_ERR_INVALID_ARG; return ESP_ERR_INVALID_ARG;
} }
*used_entries = 0; *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; 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) { if(err != ESP_OK) {
return err; 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) { if(err != ESP_OK) {
return err; return err;
} }
@ -622,17 +584,17 @@ extern "C" esp_err_t nvs_flash_read_security_cfg(const esp_partition_t* partitio
return true; 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) { if(err != ESP_OK) {
return err; 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) { if(err != ESP_OK) {
return err; 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) { if(err != ESP_OK) {
return err; 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) 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)); nvs_iterator_t it = (nvs_iterator_t)calloc(1, sizeof(nvs_opaque_iterator_t));
if (it == NULL) { if (it == nullptr) {
return NULL; return nullptr;
} }
it->storage = storage; 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; nvs::Storage *pStorage;
pStorage = lookup_storage_from_name(part_name); pStorage = lookup_storage_from_name(part_name);
if (pStorage == NULL) { if (pStorage == nullptr) {
return NULL; return nullptr;
} }
nvs_iterator_t it = create_iterator(pStorage, type); nvs_iterator_t it = create_iterator(pStorage, type);
if (it == NULL) { if (it == nullptr) {
return NULL; return nullptr;
} }
bool entryFound = pStorage->findEntry(it, namespace_name); bool entryFound = pStorage->findEntry(it, namespace_name);
if (!entryFound) { if (!entryFound) {
free(it); free(it);
return NULL; return nullptr;
} }
return it; return it;
@ -721,7 +683,7 @@ extern "C" nvs_iterator_t nvs_entry_next(nvs_iterator_t it)
bool entryFound = it->storage->nextEntry(it); bool entryFound = it->storage->nextEntry(it);
if (!entryFound) { if (!entryFound) {
free(it); free(it);
return NULL; return nullptr;
} }
return it; return it;

View File

@ -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 <string.h>
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<uint8_t*>(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

View File

@ -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<XtsCtxt> {
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<XtsCtxt> 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 */

View File

@ -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 <cstring>
#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<uint8_t*>(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<uint8_t*>(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

View File

@ -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_

View File

@ -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 <string.h>
#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<uint8_t*>(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<uint8_t*>(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
}

View File

@ -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 */

View File

@ -20,11 +20,11 @@
#include <cstdio> #include <cstdio>
#include <cstring> #include <cstring>
#include "nvs_ops.hpp"
namespace nvs namespace nvs
{ {
Page::Page() : mPartition(nullptr) { }
uint32_t Page::Header::calculateCrc32() uint32_t Page::Header::calculateCrc32()
{ {
return crc32_le(0xffffffff, return crc32_le(0xffffffff,
@ -32,14 +32,19 @@ uint32_t Page::Header::calculateCrc32()
offsetof(Header, mCrc32) - offsetof(Header, mSeqNumber)); 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; mBaseAddress = sectorNumber * SEC_SIZE;
mUsedEntryCount = 0; mUsedEntryCount = 0;
mErasedEntryCount = 0; mErasedEntryCount = 0;
Header header; Header header;
auto rc = spi_flash_read(mBaseAddress, &header, sizeof(header)); auto rc = mPartition->read_raw(mBaseAddress, &header, sizeof(header));
if (rc != ESP_OK) { if (rc != ESP_OK) {
mState = PageState::INVALID; mState = PageState::INVALID;
return rc; return rc;
@ -54,7 +59,7 @@ esp_err_t Page::load(uint32_t sectorNumber)
if (!block) return ESP_ERR_NO_MEM; if (!block) return ESP_ERR_NO_MEM;
for (uint32_t i = 0; i < SPI_FLASH_SEC_SIZE; i += 4 * BLOCK_SIZE) { 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) { if (rc != ESP_OK) {
mState = PageState::INVALID; mState = PageState::INVALID;
delete[] block; delete[] block;
@ -101,7 +106,7 @@ esp_err_t Page::writeEntry(const Item& item)
{ {
esp_err_t err; 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) { if (err != ESP_OK) {
mState = PageState::INVALID; mState = PageState::INVALID;
@ -133,6 +138,7 @@ esp_err_t Page::writeEntryData(const uint8_t* data, size_t size)
const uint8_t* buf = data; const uint8_t* buf = data;
#ifdef ESP_PLATFORM #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 /* 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. * 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 * 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 #endif //ESP_PLATFORM
auto rc = nvs_flash_write(getEntryAddress(mNextFreeEntry), buf, size); auto rc = mPartition->write(getEntryAddress(mNextFreeEntry), buf, size);
#ifdef ESP_PLATFORM #ifdef ESP_PLATFORM
if (buf != data) { if (buf != data) {
@ -518,7 +524,7 @@ esp_err_t Page::mLoadEntryTable()
if (mState == PageState::ACTIVE || if (mState == PageState::ACTIVE ||
mState == PageState::FULL || mState == PageState::FULL ||
mState == PageState::FREEING) { 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()); mEntryTable.byteSize());
if (rc != ESP_OK) { if (rc != ESP_OK) {
mState = PageState::INVALID; mState = PageState::INVALID;
@ -557,7 +563,7 @@ esp_err_t Page::mLoadEntryTable()
while (mNextFreeEntry < ENTRY_COUNT) { while (mNextFreeEntry < ENTRY_COUNT) {
uint32_t entryAddress = getEntryAddress(mNextFreeEntry); uint32_t entryAddress = getEntryAddress(mNextFreeEntry);
uint32_t header; 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) { if (rc != ESP_OK) {
mState = PageState::INVALID; mState = PageState::INVALID;
return rc; return rc;
@ -722,7 +728,7 @@ esp_err_t Page::initialize()
header.mVersion = mVersion; header.mVersion = mVersion;
header.mCrc32 = header.calculateCrc32(); 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) { if (rc != ESP_OK) {
mState = PageState::INVALID; mState = PageState::INVALID;
return rc; return rc;
@ -739,7 +745,7 @@ esp_err_t Page::alterEntryState(size_t index, EntryState state)
mEntryTable.set(index, state); mEntryTable.set(index, state);
size_t wordToWrite = mEntryTable.getWordIndex(index); size_t wordToWrite = mEntryTable.getWordIndex(index);
uint32_t word = mEntryTable.data()[wordToWrite]; uint32_t word = mEntryTable.data()[wordToWrite];
auto rc = spi_flash_write(mBaseAddress + ENTRY_TABLE_OFFSET + static_cast<uint32_t>(wordToWrite) * 4, auto rc = mPartition->write_raw(mBaseAddress + ENTRY_TABLE_OFFSET + static_cast<uint32_t>(wordToWrite) * 4,
&word, sizeof(word)); &word, sizeof(word));
if (rc != ESP_OK) { if (rc != ESP_OK) {
mState = PageState::INVALID; mState = PageState::INVALID;
@ -763,7 +769,7 @@ esp_err_t Page::alterEntryRangeState(size_t begin, size_t end, EntryState state)
} }
if (nextWordIndex != wordIndex) { if (nextWordIndex != wordIndex) {
uint32_t word = mEntryTable.data()[wordIndex]; uint32_t word = mEntryTable.data()[wordIndex];
auto rc = spi_flash_write(mBaseAddress + ENTRY_TABLE_OFFSET + static_cast<uint32_t>(wordIndex) * 4, auto rc = mPartition->write_raw(mBaseAddress + ENTRY_TABLE_OFFSET + static_cast<uint32_t>(wordIndex) * 4,
&word, 4); &word, 4);
if (rc != ESP_OK) { if (rc != ESP_OK) {
return rc; 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) esp_err_t Page::alterPageState(PageState state)
{ {
uint32_t state_val = static_cast<uint32_t>(state); uint32_t state_val = static_cast<uint32_t>(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) { if (rc != ESP_OK) {
mState = PageState::INVALID; mState = PageState::INVALID;
return rc; return rc;
@ -788,7 +794,7 @@ esp_err_t Page::alterPageState(PageState state)
esp_err_t Page::readEntry(size_t index, Item& dst) const 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) { if (rc != ESP_OK) {
return rc; return rc;
} }
@ -925,8 +931,7 @@ esp_err_t Page::setVersion(uint8_t ver)
esp_err_t Page::erase() esp_err_t Page::erase()
{ {
auto sector = mBaseAddress / SPI_FLASH_SEC_SIZE; auto rc = mPartition->erase_range(mBaseAddress, SPI_FLASH_SEC_SIZE);
auto rc = spi_flash_erase_sector(sector);
if (rc != ESP_OK) { if (rc != ESP_OK) {
mState = PageState::INVALID; mState = PageState::INVALID;
return rc; return rc;

View File

@ -24,6 +24,7 @@
#include "compressed_enum_table.hpp" #include "compressed_enum_table.hpp"
#include "intrusive_list.h" #include "intrusive_list.h"
#include "nvs_item_hash_list.hpp" #include "nvs_item_hash_list.hpp"
#include "partition.hpp"
namespace nvs namespace nvs
{ {
@ -77,12 +78,14 @@ public:
INVALID = 0 INVALID = 0
}; };
Page();
PageState state() const PageState state() const
{ {
return mState; 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 getSeqNumber(uint32_t& seqNumber) const;
@ -223,6 +226,8 @@ protected:
HashList mHashList; HashList mHashList;
Partition *mPartition;
static const uint32_t HEADER_OFFSET = 0; static const uint32_t HEADER_OFFSET = 0;
static const uint32_t ENTRY_TABLE_OFFSET = HEADER_OFFSET + 32; static const uint32_t ENTRY_TABLE_OFFSET = HEADER_OFFSET + 32;
static const uint32_t ENTRY_DATA_OFFSET = ENTRY_TABLE_OFFSET + 32; static const uint32_t ENTRY_DATA_OFFSET = ENTRY_TABLE_OFFSET + 32;

View File

@ -15,8 +15,12 @@
namespace nvs 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; mBaseSector = baseSector;
mPageCount = sectorCount; mPageCount = sectorCount;
mPageList.clear(); mPageList.clear();
@ -26,7 +30,7 @@ esp_err_t PageManager::load(uint32_t baseSector, uint32_t sectorCount)
if (!mPages) return ESP_ERR_NO_MEM; if (!mPages) return ESP_ERR_NO_MEM;
for (uint32_t i = 0; i < sectorCount; ++i) { 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) { if (err != ESP_OK) {
return err; 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 // partition should have at least one free page
if (mFreePageList.size() == 0) { if (mFreePageList.empty()) {
return ESP_ERR_NVS_NO_FREE_PAGES; return ESP_ERR_NVS_NO_FREE_PAGES;
} }

View File

@ -18,7 +18,7 @@
#include <list> #include <list>
#include "nvs_types.hpp" #include "nvs_types.hpp"
#include "nvs_page.hpp" #include "nvs_page.hpp"
#include "nvs_pagemanager.hpp" #include "partition.hpp"
#include "intrusive_list.h" #include "intrusive_list.h"
namespace nvs namespace nvs
@ -31,7 +31,7 @@ public:
PageManager() {} 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() TPageListIterator begin()
{ {

View File

@ -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

View File

@ -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<NVSPartition> {
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_

View File

@ -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

View File

@ -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_

View File

@ -13,6 +13,11 @@
// limitations under the License. // limitations under the License.
#include "esp_partition.h" #include "esp_partition.h"
#include "nvs_partition_manager.hpp" #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 { namespace nvs {
@ -30,6 +35,11 @@ NVSPartitionManager* NVSPartitionManager::get_instance()
#ifdef ESP_PLATFORM #ifdef ESP_PLATFORM
esp_err_t NVSPartitionManager::init_partition(const char *partition_label) 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; Storage* mStorage;
mStorage = lookup_storage_from_name(partition_label); 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); assert(SPI_FLASH_SEC_SIZE != 0);
const esp_partition_t* partition = esp_partition_find_first( NVSPartition *p = nullptr;
ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, partition_label); esp_err_t result = lookup_nvs_partition(partition_label, &p);
if (partition == nullptr) {
return ESP_ERR_NOT_FOUND; if (result != ESP_OK) {
goto error;
} }
return init_custom(partition_label, partition->address / SPI_FLASH_SEC_SIZE, size = p->get_size();
partition->size / SPI_FLASH_SEC_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 #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; if (new_storage == nullptr) {
Storage* storage = lookup_storage_from_name(partName); return ESP_ERR_NO_MEM;
if (storage == NULL) { }
new_storage = new (std::nothrow) Storage((const char *)partName);
if (!new_storage) return ESP_ERR_NO_MEM;
storage = new_storage; 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); esp_err_t err = storage->init(baseSector, sectorCount);
if (new_storage != NULL) { if (new_storage != nullptr) {
if (err == ESP_OK) { if (err == ESP_OK) {
nvs_storage_list.push_back(new_storage); nvs_storage_list.push_back(new_storage);
} else { } else {
@ -75,41 +107,47 @@ esp_err_t NVSPartitionManager::init_custom(const char *partName, uint32_t baseSe
return err; return err;
} }
#ifdef ESP_PLATFORM
#ifdef CONFIG_NVS_ENCRYPTION #ifdef CONFIG_NVS_ENCRYPTION
#ifdef ESP_PLATFORM
esp_err_t NVSPartitionManager::secure_init_partition(const char *part_name, nvs_sec_cfg_t* cfg) 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; Storage* mStorage;
mStorage = lookup_storage_from_name(part_name); mStorage = lookup_storage_from_name(part_name);
if (mStorage) { if (mStorage != nullptr) {
return ESP_OK; return ESP_OK;
} }
const esp_partition_t* partition = esp_partition_find_first( NVSPartition *p;
ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, part_name); esp_err_t result;
if (partition == NULL) { if (cfg != nullptr) {
return ESP_ERR_NOT_FOUND; 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, if (result != ESP_OK) {
partition->size / SPI_FLASH_SEC_SIZE, cfg); return result;
}
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;
}
} }
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 // ESP_PLATFORM
#endif // CONFIG_NVS_ENCRYPTION
esp_err_t NVSPartitionManager::deinit_partition(const char *partition_label) 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; 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 */ /* Clean up handles related to the storage being deinitialized */
for (auto it = nvs_handles.begin(); it != nvs_handles.end(); ++it) { for (auto it = nvs_handles.begin(); it != nvs_handles.end(); ++it) {
if (it->mStoragePtr == storage) { 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); nvs_storage_list.erase(storage);
delete 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; return ESP_OK;
} }
@ -148,12 +188,12 @@ esp_err_t NVSPartitionManager::open_handle(const char *part_name,
uint8_t nsIndex; uint8_t nsIndex;
Storage* sHandle; Storage* sHandle;
if (nvs_storage_list.size() == 0) { if (nvs_storage_list.empty()) {
return ESP_ERR_NVS_NOT_INITIALIZED; return ESP_ERR_NVS_NOT_INITIALIZED;
} }
sHandle = lookup_storage_from_name(part_name); sHandle = lookup_storage_from_name(part_name);
if (sHandle == NULL) { if (sHandle == nullptr) {
return ESP_ERR_NVS_PART_NOT_FOUND; 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); *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); nvs_handles.push_back(*handle);
@ -194,7 +236,7 @@ Storage* NVSPartitionManager::lookup_storage_from_name(const char* name)
}); });
if (it == end(nvs_storage_list)) { if (it == end(nvs_storage_list)) {
return NULL; return nullptr;
} }
return it; return it;
} }

View File

@ -16,10 +16,8 @@
#include "nvs_handle_simple.hpp" #include "nvs_handle_simple.hpp"
#include "nvs_storage.hpp" #include "nvs_storage.hpp"
#include "nvs_partition.hpp"
#ifdef CONFIG_NVS_ENCRYPTION #include "nvs_flash.h"
#include "nvs_encr.hpp"
#endif
namespace nvs { namespace nvs {
@ -31,12 +29,10 @@ public:
esp_err_t init_partition(const char *partition_label); 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 #ifdef CONFIG_NVS_ENCRYPTION
esp_err_t secure_init_partition(const char *part_name, nvs_sec_cfg_t* cfg); 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 #endif
esp_err_t deinit_partition(const char *partition_label); esp_err_t deinit_partition(const char *partition_label);
@ -57,6 +53,8 @@ protected:
intrusive_list<NVSHandleSimple> nvs_handles; intrusive_list<NVSHandleSimple> nvs_handles;
intrusive_list<nvs::Storage> nvs_storage_list; intrusive_list<nvs::Storage> nvs_storage_list;
intrusive_list<nvs::NVSPartition> nvs_partition_list;
}; };
} // nvs } // nvs

View File

@ -90,7 +90,7 @@ void Storage::eraseOrphanDataBlobs(TBlobIndexList& blobIdxList)
esp_err_t Storage::init(uint32_t baseSector, uint32_t sectorCount) 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) { if (err != ESP_OK) {
mState = StorageState::INVALID; mState = StorageState::INVALID;
return err; return err;

View File

@ -20,6 +20,7 @@
#include "nvs_types.hpp" #include "nvs_types.hpp"
#include "nvs_page.hpp" #include "nvs_page.hpp"
#include "nvs_pagemanager.hpp" #include "nvs_pagemanager.hpp"
#include "partition.hpp"
//extern void dumpBytes(const uint8_t* data, size_t count); //extern void dumpBytes(const uint8_t* data, size_t count);
@ -60,9 +61,10 @@ class Storage : public intrusive_list_node<Storage>
public: public:
~Storage(); ~Storage();
Storage(const char *pName = NVS_DEFAULT_PART_NAME) Storage(Partition *partition) : mPartition(partition) {
{ if (partition == nullptr) {
strncpy(mPartitionName, pName, NVS_PART_NAME_MAX_SIZE); abort();
}
}; };
esp_err_t init(uint32_t baseSector, uint32_t sectorCount); esp_err_t init(uint32_t baseSector, uint32_t sectorCount);
@ -98,10 +100,16 @@ public:
esp_err_t eraseNamespace(uint8_t nsIndex); esp_err_t eraseNamespace(uint8_t nsIndex);
const Partition *getPart() const
{
return mPartition;
}
const char *getPartName() const const char *getPartName() const
{ {
return mPartitionName; return mPartition->get_partition_name();
} }
uint32_t getBaseSector() uint32_t getBaseSector()
{ {
return mPageManager.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); 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: protected:
char mPartitionName [NVS_PART_NAME_MAX_SIZE + 1]; Partition *mPartition;
size_t mPageCount; size_t mPageCount;
PageManager mPageManager; PageManager mPageManager;
TNamespaces mNamespaces; TNamespaces mNamespaces;

View File

@ -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_

View File

@ -1,3 +1,4 @@
idf_component_register(SRC_DIRS "." idf_component_register(SRC_DIRS "."
PRIV_INCLUDE_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)

View File

@ -18,6 +18,15 @@
static const char* TAG = "test_nvs"; 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]") TEST_CASE("flash erase deinitializes initialized partition", "[nvs]")
{ {
nvs_handle_t handle; nvs_handle_t handle;
@ -26,7 +35,7 @@ TEST_CASE("flash erase deinitializes initialized partition", "[nvs]")
nvs_flash_erase(); nvs_flash_erase();
err = nvs_flash_init(); err = nvs_flash_init();
} }
ESP_ERROR_CHECK( err ); TEST_ESP_OK( err );
TEST_ESP_OK(nvs_flash_init()); TEST_ESP_OK(nvs_flash_init());
TEST_ESP_OK(nvs_open("uninit_ns", NVS_READWRITE, &handle)); 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) { 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); esp_err_t err = nvs_flash_read_security_cfg(key_part, &xts_cfg);

View File

@ -10,11 +10,11 @@ SOURCE_FILES = \
nvs_pagemanager.cpp \ nvs_pagemanager.cpp \
nvs_storage.cpp \ nvs_storage.cpp \
nvs_item_hash_list.cpp \ nvs_item_hash_list.cpp \
nvs_encr.cpp \
nvs_ops.cpp \
nvs_handle_simple.cpp \ nvs_handle_simple.cpp \
nvs_handle_locked.cpp \ nvs_handle_locked.cpp \
nvs_partition_manager.cpp \ nvs_partition_manager.cpp \
nvs_partition.cpp \
nvs_encrypted_partition.cpp \
nvs_cxx_api.cpp \ nvs_cxx_api.cpp \
) \ ) \
spi_flash_emulation.cpp \ spi_flash_emulation.cpp \
@ -25,6 +25,7 @@ SOURCE_FILES = \
test_partition_manager.cpp \ test_partition_manager.cpp \
test_nvs_handle.cpp \ test_nvs_handle.cpp \
test_nvs_storage.cpp \ test_nvs_storage.cpp \
test_nvs_partition.cpp \
test_nvs_cxx_api.cpp \ test_nvs_cxx_api.cpp \
test_nvs_initialization.cpp \ test_nvs_initialization.cpp \
crc.cpp \ crc.cpp \
@ -36,7 +37,7 @@ else
COMPILER := gcc COMPILER := gcc
endif 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 CFLAGS += -fprofile-arcs -ftest-coverage
CXXFLAGS += -std=c++11 -Wall -Werror CXXFLAGS += -std=c++11 -Wall -Werror
LDFLAGS += -lstdc++ -Wall -fprofile-arcs -ftest-coverage 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.bin
rm -f ../nvs_partition_generator/partition_encrypted_using_keygen.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_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-encrypted.bin
rm -f ../nvs_partition_generator/Test-1-partition.bin rm -f ../nvs_partition_generator/Test-1-partition.bin
rm -f ../../../tools/mass_mfg/samples/sample_values_multipage_blob_created.csv rm -f ../../../tools/mass_mfg/samples/sample_values_multipage_blob_created.csv

View File

@ -11,7 +11,7 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
#include "esp_spi_flash.h" #include "esp_partition.h"
#include "spi_flash_emulation.h" #include "spi_flash_emulation.h"
@ -22,39 +22,82 @@ void spi_flash_emulator_set(SpiFlashEmulator* e)
s_emulator = 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) { if (!s_emulator) {
return ESP_ERR_FLASH_OP_TIMEOUT; 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<uint32_t*>(dst), src_offset, size)) {
return ESP_ERR_FLASH_OP_FAIL; return ESP_ERR_FLASH_OP_FAIL;
} }
return ESP_OK; 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) { if (!s_emulator) {
return ESP_ERR_FLASH_OP_TIMEOUT; return ESP_ERR_FLASH_OP_TIMEOUT;
} }
if (!s_emulator->write(des_addr, reinterpret_cast<const uint32_t*>(src_addr), size)) { if (!s_emulator->read(reinterpret_cast<uint32_t*>(dst), src_offset, size)) {
return ESP_ERR_FLASH_OP_FAIL; return ESP_ERR_FLASH_OP_FAIL;
} }
return ESP_OK; 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) { if (!s_emulator) {
return ESP_ERR_FLASH_OP_TIMEOUT; return ESP_ERR_FLASH_OP_TIMEOUT;
} }
if (!s_emulator->read(reinterpret_cast<uint32_t*>(des_addr), src_addr, size)) { if (!s_emulator->write(dst_offset, reinterpret_cast<const uint32_t*>(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<const uint32_t*>(src), size)) {
return ESP_ERR_FLASH_OP_FAIL; return ESP_ERR_FLASH_OP_FAIL;
} }

View File

@ -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<uint32_t*>(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<uint32_t*>(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<const uint32_t*>(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<const uint32_t*>(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;
};

File diff suppressed because it is too large Load Diff

View File

@ -19,6 +19,8 @@
#include "nvs_partition_manager.hpp" #include "nvs_partition_manager.hpp"
#include "spi_flash_emulation.h" #include "spi_flash_emulation.h"
#include "test_fixtures.hpp"
#include <iostream> #include <iostream>
using namespace std; 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 = 6;
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
SpiFlashEmulator emu(10); PartitionEmulationFixture f(0, 10, "test");
esp_err_t result; esp_err_t result;
shared_ptr<nvs::NVSHandle> handle; shared_ptr<nvs::NVSHandle> handle;
REQUIRE(nvs::NVSPartitionManager::get_instance()-> 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); handle = nvs::open_nvs_handle_from_partition(nullptr, "ns_1", NVS_READWRITE, &result);
CHECK(result == ESP_ERR_INVALID_ARG); 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 = 6;
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
SpiFlashEmulator emu(10); PartitionEmulationFixture f(0, 10, "test");
esp_err_t result; esp_err_t result;
shared_ptr<nvs::NVSHandle> handle; shared_ptr<nvs::NVSHandle> 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); == ESP_OK);
CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0); 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 = 6;
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
SpiFlashEmulator emu(10); PartitionEmulationFixture f(0, 10);
esp_err_t result; esp_err_t result;
shared_ptr<nvs::NVSHandle> handle; shared_ptr<nvs::NVSHandle> 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); == ESP_OK);
CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0); 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 = 6;
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
SpiFlashEmulator emu(10); PartitionEmulationFixture f(0, 10);
esp_err_t result; esp_err_t result;
shared_ptr<nvs::NVSHandle> handle; shared_ptr<nvs::NVSHandle> 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); == ESP_OK);
CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0); 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 = 6;
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
SpiFlashEmulator emu(10); PartitionEmulationFixture f(0, 10);
char read_buffer [256]; char read_buffer [256];
esp_err_t result; esp_err_t result;
shared_ptr<nvs::NVSHandle> handle; shared_ptr<nvs::NVSHandle> 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); == ESP_OK);
CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0); 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 = 6;
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; 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}; const char blob [6] = {15, 16, 17, 18, 19};
char read_blob[6] = {0}; char read_blob[6] = {0};
esp_err_t result; esp_err_t result;
shared_ptr<nvs::NVSHandle> handle; shared_ptr<nvs::NVSHandle> 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); == ESP_OK);
CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0); CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0);

View File

@ -19,6 +19,8 @@
#include "nvs_partition_manager.hpp" #include "nvs_partition_manager.hpp"
#include "spi_flash_emulation.h" #include "spi_flash_emulation.h"
#include "test_fixtures.hpp"
#include <iostream> #include <iostream>
#include <string> #include <string>
@ -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 = 6;
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; 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); == ESP_OK);
CHECK(NVSPartitionManager::get_instance()->open_handles_size() == 0); 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 = 6;
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; 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); == ESP_OK);
CHECK(NVSPartitionManager::get_instance()->open_handles_size() == 0); 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); NVSPartitionManager::get_instance()->deinit_partition(NVS_DEFAULT_PART_NAME);
NVSHandleSimple *handle_1; NVSHandleSimple *handle_1;
NVSHandleSimple *handle_2; NVSHandleSimple *handle_2;
const uint32_t NVS_FLASH_SECTOR = 6; const uint32_t NVS_FLASH_SECTOR = 6;
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; 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); CHECK(NVSPartitionManager::get_instance()->open_handles_size() == 0);
// first, creating namespace... // first, creating namespace...
@ -123,13 +125,13 @@ TEST_CASE("NVSHandleSimple set/get char", "[partition_mgr]")
BAR BAR
}; };
SpiFlashEmulator emu(10); PartitionEmulationFixture f(0, 10);
const uint32_t NVS_FLASH_SECTOR = 6; const uint32_t NVS_FLASH_SECTOR = 6;
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; 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); == ESP_OK);
NVSHandleSimple *handle; NVSHandleSimple *handle;
@ -155,13 +157,13 @@ TEST_CASE("NVSHandleSimple correctly sets/gets int enum", "[partition_mgr]")
BAR BAR
}; };
SpiFlashEmulator emu(10); PartitionEmulationFixture f(0, 10);
const uint32_t NVS_FLASH_SECTOR = 6; const uint32_t NVS_FLASH_SECTOR = 6;
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; 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); == ESP_OK);
NVSHandleSimple *handle; NVSHandleSimple *handle;
@ -188,13 +190,13 @@ TEST_CASE("NVSHandleSimple correctly sets/gets int enum with negative values", "
BAR BAR
}; };
SpiFlashEmulator emu(10); PartitionEmulationFixture f(0, 10);
const uint32_t NVS_FLASH_SECTOR = 6; const uint32_t NVS_FLASH_SECTOR = 6;
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; 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); == ESP_OK);
NVSHandleSimple *handle; NVSHandleSimple *handle;
@ -220,13 +222,13 @@ TEST_CASE("NVSHandleSimple correctly sets/gets uint8_t enum", "[partition_mgr]")
BAR BAR
}; };
SpiFlashEmulator emu(10); PartitionEmulationFixture f(0, 10);
const uint32_t NVS_FLASH_SECTOR = 6; const uint32_t NVS_FLASH_SECTOR = 6;
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; 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); == ESP_OK);
NVSHandleSimple *handle; NVSHandleSimple *handle;
@ -253,13 +255,13 @@ TEST_CASE("NVSHandleSimple correctly sets/gets char enum", "[partition_mgr]")
BAR BAR
}; };
SpiFlashEmulator emu(10); PartitionEmulationFixture f(0, 10);
const uint32_t NVS_FLASH_SECTOR = 6; const uint32_t NVS_FLASH_SECTOR = 6;
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; 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); == ESP_OK);
NVSHandleSimple *handle; NVSHandleSimple *handle;

View File

@ -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 <algorithm>
#include <cstring>
#include "nvs_test_api.h"
#include "nvs_handle_simple.hpp"
#include "nvs_partition.hpp"
#include "spi_flash_emulation.h"
#include "test_fixtures.hpp"
#include <iostream>
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);
}

View File

@ -18,6 +18,8 @@
#include "nvs_partition_manager.hpp" #include "nvs_partition_manager.hpp"
#include "spi_flash_emulation.h" #include "spi_flash_emulation.h"
#include "test_fixtures.hpp"
#include <iostream> #include <iostream>
using namespace std; 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 = 6;
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; 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); == ESP_OK);
uint8_t blob [] = {0x0, 0x1, 0x2, 0x3}; uint8_t blob [] = {0x0, 0x1, 0x2, 0x3};

View File

@ -20,25 +20,28 @@
#include "spi_flash_emulation.h" #include "spi_flash_emulation.h"
#include "nvs_test_api.h" #include "nvs_test_api.h"
#include "test_fixtures.hpp"
using namespace nvs; using namespace nvs;
TEST_CASE("Partition manager initializes storage", "[partition_mgr]") TEST_CASE("Partition manager initializes storage", "[partition_mgr]")
{ {
const uint32_t NVS_FLASH_SECTOR = 6; const uint32_t NVS_FLASH_SECTOR = 6;
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; 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()->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]") TEST_CASE("Partition manager de-initializes storage", "[partition_mgr]")
{ {
const uint32_t NVS_FLASH_SECTOR = 6; const uint32_t NVS_FLASH_SECTOR = 6;
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; 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()->lookup_storage_from_name("test") != nullptr);
CHECK(NVSPartitionManager::get_instance()->deinit_partition("test") == ESP_OK); CHECK(NVSPartitionManager::get_instance()->deinit_partition("test") == ESP_OK);
CHECK(NVSPartitionManager::get_instance()->lookup_storage_from_name("test") == nullptr); 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 = 6;
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
SpiFlashEmulator emu(10); 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); == ESP_OK);
// TODO: why does this work, actually? same sectors used as above // 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); == ESP_OK);
Storage *storage1 = NVSPartitionManager::get_instance()->lookup_storage_from_name("test1"); Storage *storage1 = NVSPartitionManager::get_instance()->lookup_storage_from_name("test1");
REQUIRE(storage1 != nullptr); REQUIRE(storage1 != nullptr);
@ -61,15 +66,17 @@ TEST_CASE("Partition manager initializes multiple partitions", "[partition_mgr]"
REQUIRE(storage2 != nullptr); REQUIRE(storage2 != nullptr);
CHECK(storage1 != storage2); 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]") 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 = 6;
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; 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); == ESP_OK);
NVSHandleSimple *handle; NVSHandleSimple *handle;

View File

@ -11,9 +11,9 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
#include <functional>
#include "catch.hpp" #include "catch.hpp"
#include "esp_spi_flash.h" #include "esp_spi_flash.h"
#include "esp_partition.h"
#include "spi_flash_emulation.h" #include "spi_flash_emulation.h"
#include <functional> #include <functional>
@ -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<uint32_t>(), placeholders::_1, 0xffffffff)); return all_of(it_begin, it_begin + n, bind(equal_to<uint32_t>(), 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]") TEST_CASE("flash starts with all bytes == 0xff", "[spi_flash_emu]")
{ {
SpiFlashEmulator emu(4); FlashEmuFixture f(4);
uint8_t sector[SPI_FLASH_SEC_SIZE]; uint8_t sector[SPI_FLASH_SEC_SIZE];
for (int i = 0; i < 4; ++i) { 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) { for (auto v: sector) {
CHECK(v == 0xff); CHECK(v == 0xff);
} }
@ -41,106 +48,129 @@ TEST_CASE("flash starts with all bytes == 0xff", "[spi_flash_emu]")
TEST_CASE("invalid writes are checked", "[spi_flash_emu]") TEST_CASE("invalid writes are checked", "[spi_flash_emu]")
{ {
SpiFlashEmulator emu(1); FlashEmuFixture f(1);
uint32_t val = 0; 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; 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]") TEST_CASE("out of bounds writes fail", "[spi_flash_emu]")
{ {
SpiFlashEmulator emu(4); FlashEmuFixture f(4);
uint32_t vals[8]; uint32_t vals[8];
std::fill_n(vals, 8, 0); 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]") TEST_CASE("after erase the sector is set to 0xff", "[spi_flash_emu]")
{ {
SpiFlashEmulator emu(4); FlashEmuFixture f(4);
uint32_t val1 = 0xab00cd12; 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; 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(f.emu.words()[0] == val1);
CHECK(range_empty_n(emu.words() + 1, 4096 / 4 - 2)); CHECK(range_empty_n(f.emu.words() + 1, 4096 / 4 - 2));
CHECK(emu.words()[4096 / 4 - 1] == val2); 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(f.emu.words()[0] == 0xffffffff);
CHECK(range_empty_n(emu.words() + 1, 4096 / 4 - 2)); CHECK(range_empty_n(f.emu.words() + 1, 4096 / 4 - 2));
CHECK(emu.words()[4096 / 4 - 1] == 0xffffffff); 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]") TEST_CASE("read/write/erase operation times are calculated correctly", "[spi_flash_emu]")
{ {
SpiFlashEmulator emu(1); FlashEmuFixture f(1);
uint8_t data[512]; uint8_t data[512];
spi_flash_read(0, data, 4); esp_partition_read(&f.esp_part, 0, data, 4);
CHECK(emu.getTotalTime() == 7); CHECK(f.emu.getTotalTime() == 7);
CHECK(emu.getReadOps() == 1); CHECK(f.emu.getReadOps() == 1);
CHECK(emu.getReadBytes() == 4); CHECK(f.emu.getReadBytes() == 4);
emu.clearStats(); f.emu.clearStats();
spi_flash_read(0, data, 8); esp_partition_read(&f.esp_part, 0, data, 8);
CHECK(emu.getTotalTime() == 5); CHECK(f.emu.getTotalTime() == 5);
CHECK(emu.getReadOps() == 1); CHECK(f.emu.getReadOps() == 1);
CHECK(emu.getReadBytes() == 8); CHECK(f.emu.getReadBytes() == 8);
emu.clearStats(); f.emu.clearStats();
spi_flash_read(0, data, 16); esp_partition_read(&f.esp_part, 0, data, 16);
CHECK(emu.getTotalTime() == 6); CHECK(f.emu.getTotalTime() == 6);
CHECK(emu.getReadOps() == 1); CHECK(f.emu.getReadOps() == 1);
CHECK(emu.getReadBytes() == 16); CHECK(f.emu.getReadBytes() == 16);
emu.clearStats(); f.emu.clearStats();
spi_flash_read(0, data, 128); esp_partition_read(&f.esp_part, 0, data, 128);
CHECK(emu.getTotalTime() == 18); CHECK(f.emu.getTotalTime() == 18);
CHECK(emu.getReadOps() == 1); CHECK(f.emu.getReadOps() == 1);
CHECK(emu.getReadBytes() == 128); CHECK(f.emu.getReadBytes() == 128);
emu.clearStats(); f.emu.clearStats();
spi_flash_read(0, data, 256); esp_partition_read(&f.esp_part, 0, data, 256);
CHECK(emu.getTotalTime() == 32); CHECK(f.emu.getTotalTime() == 32);
emu.clearStats(); f.emu.clearStats();
spi_flash_read(0, data, (128+256)/2); esp_partition_read(&f.esp_part, 0, data, (128+256)/2);
CHECK(emu.getTotalTime() == (18+32)/2); CHECK(f.emu.getTotalTime() == (18+32)/2);
emu.clearStats(); f.emu.clearStats();
spi_flash_write(0, data, 4); esp_partition_write(&f.esp_part, 0, data, 4);
CHECK(emu.getTotalTime() == 19); CHECK(f.emu.getTotalTime() == 19);
CHECK(emu.getWriteOps() == 1); CHECK(f.emu.getWriteOps() == 1);
CHECK(emu.getWriteBytes() == 4); CHECK(f.emu.getWriteBytes() == 4);
emu.clearStats(); f.emu.clearStats();
CHECK(emu.getWriteOps() == 0); CHECK(f.emu.getWriteOps() == 0);
CHECK(emu.getWriteBytes() == 0); CHECK(f.emu.getWriteBytes() == 0);
spi_flash_write(0, data, 8); esp_partition_write(&f.esp_part, 0, data, 8);
CHECK(emu.getTotalTime() == 23); CHECK(f.emu.getTotalTime() == 23);
emu.clearStats(); f.emu.clearStats();
spi_flash_write(0, data, 16); esp_partition_write(&f.esp_part, 0, data, 16);
CHECK(emu.getTotalTime() == 35); CHECK(f.emu.getTotalTime() == 35);
CHECK(emu.getWriteOps() == 1); CHECK(f.emu.getWriteOps() == 1);
CHECK(emu.getWriteBytes() == 16); CHECK(f.emu.getWriteBytes() == 16);
emu.clearStats(); f.emu.clearStats();
spi_flash_write(0, data, 128); esp_partition_write(&f.esp_part, 0, data, 128);
CHECK(emu.getTotalTime() == 205); CHECK(f.emu.getTotalTime() == 205);
emu.clearStats(); f.emu.clearStats();
spi_flash_write(0, data, 256); esp_partition_write(&f.esp_part, 0, data, 256);
CHECK(emu.getTotalTime() == 417); CHECK(f.emu.getTotalTime() == 417);
emu.clearStats(); f.emu.clearStats();
spi_flash_write(0, data, (128+256)/2); esp_partition_write(&f.esp_part, 0, data, (128+256)/2);
CHECK(emu.getTotalTime() == (205+417)/2); CHECK(f.emu.getTotalTime() == (205+417)/2);
emu.clearStats(); f.emu.clearStats();
spi_flash_erase_sector(0); esp_partition_erase_range(&f.esp_part, 0, SPI_FLASH_SEC_SIZE);
CHECK(emu.getEraseOps() == 1); CHECK(f.emu.getEraseOps() == 1);
CHECK(emu.getTotalTime() == 37142); CHECK(f.emu.getTotalTime() == 37142);
} }
TEST_CASE("data is randomized predictably", "[spi_flash_emu]") TEST_CASE("data is randomized predictably", "[spi_flash_emu]")

View File

@ -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); 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_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); 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); esp_partition_iterator_t it = esp_partition_find(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, NULL);

View File

@ -202,6 +202,9 @@ const esp_partition_t *esp_partition_verify(const esp_partition_t *partition);
/** /**
* @brief Read data from the 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 * @param partition Pointer to partition structure obtained using
* esp_partition_find_first or esp_partition_get. * esp_partition_find_first or esp_partition_get.
* Must be non-NULL. * 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. * or one of error codes from lower-level flash driver.
*/ */
esp_err_t esp_partition_write(const esp_partition_t* partition, 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 * @brief Erase part of the partition

View File

@ -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, esp_err_t esp_partition_erase_range(const esp_partition_t* partition,
size_t offset, size_t size) size_t offset, size_t size)
{ {

View File

@ -2,10 +2,10 @@
# #
# Name, Type, SubType, Offset, Size, Flags # Name, Type, SubType, Offset, Size, Flags
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap # Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
nvs, data, nvs, 0x9000, 0x4000 nvs, data, nvs, 0xb000, 0x5000
otadata, data, ota, 0xd000, 0x2000 otadata, data, ota, 0x10000, 0x2000
phy_init, data, phy, 0xf000, 0x1000 phy_init, data, phy, 0x12000, 0x1000
factory, 0, 0, 0x10000, 0x260000 factory, 0, 0, 0x20000, 0x260000
# these OTA partitions are used for tests, but can't fit real OTA apps in them # these OTA partitions are used for tests, but can't fit real OTA apps in them
# (done this way to reduce total flash usage.) # (done this way to reduce total flash usage.)
ota_0, 0, ota_0, , 64K ota_0, 0, ota_0, , 64K

1 # Special partition table for unit test app
2 #
3 # Name, Type, SubType, Offset, Size, Flags
4 # Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
5 nvs, data, nvs, 0x9000, 0x4000 nvs, data, nvs, 0xb000, 0x5000
6 otadata, data, ota, 0xd000, 0x2000 otadata, data, ota, 0x10000, 0x2000
7 phy_init, data, phy, 0xf000, 0x1000 phy_init, data, phy, 0x12000, 0x1000
8 factory, 0, 0, 0x10000, 0x260000 factory, 0, 0, 0x20000, 0x260000
9 # these OTA partitions are used for tests, but can't fit real OTA apps in them
10 # (done this way to reduce total flash usage.)
11 ota_0, 0, ota_0, , 64K