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")
idf_component_register(SRC_DIRS .
PRIV_INCLUDE_DIRS .
PRIV_REQUIRES cmock test_utils nvs_flash ulp esp_common
PRIV_REQUIRES cmock test_utils mbedtls ulp esp_common
)
target_link_libraries(${COMPONENT_LIB} INTERFACE "-u ld_include_test_dport_xt_highint5")
endif()

View File

@ -1,7 +1,7 @@
if(IDF_TARGET STREQUAL "esp32s2")
idf_component_register(SRC_DIRS .
PRIV_INCLUDE_DIRS .
PRIV_REQUIRES cmock test_utils nvs_flash ulp esp_common
PRIV_REQUIRES cmock test_utils nvs_flash mbedtls ulp esp_common
)
endif()

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
API functions. NVS key is different in
comparison */
# endif
# ifdef ESP_ERR_NVS_WRONG_ENCRYPTION
ERR_TBL_IT(ESP_ERR_NVS_WRONG_ENCRYPTION), /* 4377 0x1119 NVS partition is marked as encrypted
with generic flash encryption. This is
forbidden since the NVS encryption works
differently. */
# endif
// components/ulp/include/ulp_common.h
# ifdef ESP_ERR_ULP_BASE

View File

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

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_KEYS_NOT_INITIALIZED (ESP_ERR_NVS_BASE + 0x16) /*!< NVS key partition is uninitialized */
#define ESP_ERR_NVS_CORRUPT_KEY_PART (ESP_ERR_NVS_BASE + 0x17) /*!< NVS key partition is corrupt */
#define ESP_ERR_NVS_WRONG_ENCRYPTION (ESP_ERR_NVS_BASE + 0x19) /*!< NVS partition is marked as encrypted with generic flash encryption. This is forbidden since the NVS encryption works differently. */
#define ESP_ERR_NVS_CONTENT_DIFFERS (ESP_ERR_NVS_BASE + 0x18) /*!< Internal error; never returned by nvs API functions. NVS key is different in comparison */

View File

@ -20,9 +20,6 @@
#include "esp_partition.h"
#include "sdkconfig.h"
#include "nvs_handle_simple.hpp"
#ifdef CONFIG_NVS_ENCRYPTION
#include "nvs_encr.hpp"
#endif
#ifdef ESP_PLATFORM
#include <esp32/rom/crc.h>
@ -57,14 +54,9 @@ private:
uint32_t NVSHandleEntry::s_nvs_next_handle;
extern "C" void nvs_dump(const char *partName);
extern "C" esp_err_t nvs_flash_init_custom(const char *partName, uint32_t baseSector, uint32_t sectorCount);
#ifdef CONFIG_NVS_ENCRYPTION
extern "C" esp_err_t nvs_flash_secure_init_custom(const char *partName, uint32_t baseSector, uint32_t sectorCount, nvs_sec_cfg_t* cfg);
#endif
#ifdef ESP_PLATFORM
SemaphoreHandle_t nvs::Lock::mSemaphore = NULL;
SemaphoreHandle_t nvs::Lock::mSemaphore = nullptr;
#endif
using namespace std;
@ -83,41 +75,13 @@ extern "C" void nvs_dump(const char *partName)
nvs::Storage* pStorage;
pStorage = lookup_storage_from_name(partName);
if (pStorage == NULL) {
if (pStorage == nullptr) {
return;
}
pStorage->debugDump();
return;
}
extern "C" esp_err_t nvs_flash_init_custom(const char *partName, uint32_t baseSector, uint32_t sectorCount)
{
ESP_LOGD(TAG, "nvs_flash_init_custom partition=%s start=%d count=%d", partName, baseSector, sectorCount);
return NVSPartitionManager::get_instance()->init_custom(partName, baseSector, sectorCount);
}
#ifdef CONFIG_NVS_ENCRYPTION
extern "C" esp_err_t nvs_flash_secure_init_custom(const char *partName, uint32_t baseSector, uint32_t sectorCount, nvs_sec_cfg_t* cfg)
{
ESP_LOGD(TAG, "nvs_flash_secure_init_custom partition=%s start=%d count=%d", partName, baseSector, sectorCount);
if(cfg) {
auto encrMgr = EncrMgr::getInstance();
if (!encrMgr) return ESP_ERR_NO_MEM;
auto err = encrMgr->setSecurityContext(baseSector, sectorCount, cfg);
if(err != ESP_OK) {
return err;
}
}
return nvs_flash_init_custom(partName, baseSector, sectorCount);
}
#endif
static esp_err_t close_handles_and_deinit(const char* part_name)
{
// Delete all corresponding open handles
@ -132,13 +96,24 @@ extern "C" esp_err_t nvs_flash_init_partition_ptr(const esp_partition_t *partiti
Lock::init();
Lock lock;
if (!partition) {
if (partition == nullptr) {
return ESP_ERR_INVALID_ARG;
}
return nvs_flash_init_custom(partition->label,
partition->address / SPI_FLASH_SEC_SIZE,
partition->size / SPI_FLASH_SEC_SIZE);
NVSPartition *part = new (std::nothrow) NVSPartition(partition);
if (part == nullptr) {
return ESP_ERR_NO_MEM;
}
esp_err_t init_res = NVSPartitionManager::get_instance()->init_custom(part,
partition->address / SPI_FLASH_SEC_SIZE,
partition->size / SPI_FLASH_SEC_SIZE);
if (init_res != ESP_OK) {
delete part;
}
return init_res;
}
#ifdef ESP_PLATFORM
@ -160,21 +135,8 @@ extern "C" esp_err_t nvs_flash_secure_init_partition(const char *part_name, nvs_
{
Lock::init();
Lock lock;
nvs::Storage* mStorage;
mStorage = lookup_storage_from_name(part_name);
if (mStorage) {
return ESP_OK;
}
const esp_partition_t* partition = esp_partition_find_first(
ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, part_name);
if (partition == NULL) {
return ESP_ERR_NOT_FOUND;
}
return nvs_flash_secure_init_custom(part_name, partition->address / SPI_FLASH_SEC_SIZE,
partition->size / SPI_FLASH_SEC_SIZE, cfg);
return NVSPartitionManager::get_instance()->secure_init_partition(part_name, cfg);
}
extern "C" esp_err_t nvs_flash_secure_init(nvs_sec_cfg_t* cfg)
@ -200,7 +162,7 @@ extern "C" esp_err_t nvs_flash_erase_partition(const char *part_name)
const esp_partition_t* partition = esp_partition_find_first(
ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, part_name);
if (partition == NULL) {
if (partition == nullptr) {
return ESP_ERR_NOT_FOUND;
}
@ -212,7 +174,7 @@ extern "C" esp_err_t nvs_flash_erase_partition_ptr(const esp_partition_t *partit
Lock::init();
Lock lock;
if (!partition) {
if (partition == nullptr) {
return ESP_ERR_INVALID_ARG;
}
@ -515,7 +477,7 @@ extern "C" esp_err_t nvs_get_stats(const char* part_name, nvs_stats_t* nvs_stats
Lock lock;
nvs::Storage* pStorage;
if (nvs_stats == NULL) {
if (nvs_stats == nullptr) {
return ESP_ERR_INVALID_ARG;
}
nvs_stats->used_entries = 0;
@ -523,8 +485,8 @@ extern "C" esp_err_t nvs_get_stats(const char* part_name, nvs_stats_t* nvs_stats
nvs_stats->total_entries = 0;
nvs_stats->namespace_count = 0;
pStorage = lookup_storage_from_name((part_name == NULL) ? NVS_DEFAULT_PART_NAME : part_name);
if (pStorage == NULL) {
pStorage = lookup_storage_from_name((part_name == nullptr) ? NVS_DEFAULT_PART_NAME : part_name);
if (pStorage == nullptr) {
return ESP_ERR_NVS_NOT_INITIALIZED;
}
@ -538,7 +500,7 @@ extern "C" esp_err_t nvs_get_stats(const char* part_name, nvs_stats_t* nvs_stats
extern "C" esp_err_t nvs_get_used_entry_count(nvs_handle_t c_handle, size_t* used_entries)
{
Lock lock;
if(used_entries == NULL){
if(used_entries == nullptr){
return ESP_ERR_INVALID_ARG;
}
*used_entries = 0;
@ -571,12 +533,12 @@ extern "C" esp_err_t nvs_flash_generate_keys(const esp_partition_t* partition, n
cfg->tky[cnt] = 0xee;
}
err = spi_flash_write(partition->address, cfg->eky, NVS_KEY_SIZE);
err = esp_partition_write(partition, 0, cfg->eky, NVS_KEY_SIZE);
if(err != ESP_OK) {
return err;
}
err = spi_flash_write(partition->address + NVS_KEY_SIZE, cfg->tky, NVS_KEY_SIZE);
err = esp_partition_write(partition, NVS_KEY_SIZE, cfg->tky, NVS_KEY_SIZE);
if(err != ESP_OK) {
return err;
}
@ -622,17 +584,17 @@ extern "C" esp_err_t nvs_flash_read_security_cfg(const esp_partition_t* partitio
return true;
};
auto err = spi_flash_read(partition->address, eky_raw, NVS_KEY_SIZE);
auto err = esp_partition_read_raw(partition, 0, eky_raw, NVS_KEY_SIZE);
if(err != ESP_OK) {
return err;
}
err = spi_flash_read(partition->address + NVS_KEY_SIZE, tky_raw, NVS_KEY_SIZE);
err = esp_partition_read_raw(partition, NVS_KEY_SIZE, tky_raw, NVS_KEY_SIZE);
if(err != ESP_OK) {
return err;
}
err = spi_flash_read(partition->address + 2 * NVS_KEY_SIZE, &crc_raw, 4);
err = esp_partition_read_raw(partition, 2 * NVS_KEY_SIZE, &crc_raw, 4);
if(err != ESP_OK) {
return err;
}
@ -679,8 +641,8 @@ extern "C" esp_err_t nvs_flash_read_security_cfg(const esp_partition_t* partitio
static nvs_iterator_t create_iterator(nvs::Storage *storage, nvs_type_t type)
{
nvs_iterator_t it = (nvs_iterator_t)calloc(1, sizeof(nvs_opaque_iterator_t));
if (it == NULL) {
return NULL;
if (it == nullptr) {
return nullptr;
}
it->storage = storage;
@ -695,19 +657,19 @@ extern "C" nvs_iterator_t nvs_entry_find(const char *part_name, const char *name
nvs::Storage *pStorage;
pStorage = lookup_storage_from_name(part_name);
if (pStorage == NULL) {
return NULL;
if (pStorage == nullptr) {
return nullptr;
}
nvs_iterator_t it = create_iterator(pStorage, type);
if (it == NULL) {
return NULL;
if (it == nullptr) {
return nullptr;
}
bool entryFound = pStorage->findEntry(it, namespace_name);
if (!entryFound) {
free(it);
return NULL;
return nullptr;
}
return it;
@ -721,7 +683,7 @@ extern "C" nvs_iterator_t nvs_entry_next(nvs_iterator_t it)
bool entryFound = it->storage->nextEntry(it);
if (!entryFound) {
free(it);
return NULL;
return nullptr;
}
return it;

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 <cstring>
#include "nvs_ops.hpp"
namespace nvs
{
Page::Page() : mPartition(nullptr) { }
uint32_t Page::Header::calculateCrc32()
{
return crc32_le(0xffffffff,
@ -32,14 +32,19 @@ uint32_t Page::Header::calculateCrc32()
offsetof(Header, mCrc32) - offsetof(Header, mSeqNumber));
}
esp_err_t Page::load(uint32_t sectorNumber)
esp_err_t Page::load(Partition *partition, uint32_t sectorNumber)
{
if (partition == nullptr) {
return ESP_ERR_INVALID_ARG;
}
mPartition = partition;
mBaseAddress = sectorNumber * SEC_SIZE;
mUsedEntryCount = 0;
mErasedEntryCount = 0;
Header header;
auto rc = spi_flash_read(mBaseAddress, &header, sizeof(header));
auto rc = mPartition->read_raw(mBaseAddress, &header, sizeof(header));
if (rc != ESP_OK) {
mState = PageState::INVALID;
return rc;
@ -54,7 +59,7 @@ esp_err_t Page::load(uint32_t sectorNumber)
if (!block) return ESP_ERR_NO_MEM;
for (uint32_t i = 0; i < SPI_FLASH_SEC_SIZE; i += 4 * BLOCK_SIZE) {
rc = spi_flash_read(mBaseAddress + i, block, 4 * BLOCK_SIZE);
rc = mPartition->read_raw(mBaseAddress + i, block, 4 * BLOCK_SIZE);
if (rc != ESP_OK) {
mState = PageState::INVALID;
delete[] block;
@ -101,7 +106,7 @@ esp_err_t Page::writeEntry(const Item& item)
{
esp_err_t err;
err = nvs_flash_write(getEntryAddress(mNextFreeEntry), &item, sizeof(item));
err = mPartition->write(getEntryAddress(mNextFreeEntry), &item, sizeof(item));
if (err != ESP_OK) {
mState = PageState::INVALID;
@ -133,6 +138,7 @@ esp_err_t Page::writeEntryData(const uint8_t* data, size_t size)
const uint8_t* buf = data;
#ifdef ESP_PLATFORM
// TODO: check whether still necessary with esp_partition* API
/* On the ESP32, data can come from DROM, which is not accessible by spi_flash_write
* function. To work around this, we copy the data to heap if it came from DROM.
* Hopefully this won't happen very often in practice. For data from DRAM, we should
@ -149,7 +155,7 @@ esp_err_t Page::writeEntryData(const uint8_t* data, size_t size)
}
#endif //ESP_PLATFORM
auto rc = nvs_flash_write(getEntryAddress(mNextFreeEntry), buf, size);
auto rc = mPartition->write(getEntryAddress(mNextFreeEntry), buf, size);
#ifdef ESP_PLATFORM
if (buf != data) {
@ -518,7 +524,7 @@ esp_err_t Page::mLoadEntryTable()
if (mState == PageState::ACTIVE ||
mState == PageState::FULL ||
mState == PageState::FREEING) {
auto rc = spi_flash_read(mBaseAddress + ENTRY_TABLE_OFFSET, mEntryTable.data(),
auto rc = mPartition->read_raw(mBaseAddress + ENTRY_TABLE_OFFSET, mEntryTable.data(),
mEntryTable.byteSize());
if (rc != ESP_OK) {
mState = PageState::INVALID;
@ -557,7 +563,7 @@ esp_err_t Page::mLoadEntryTable()
while (mNextFreeEntry < ENTRY_COUNT) {
uint32_t entryAddress = getEntryAddress(mNextFreeEntry);
uint32_t header;
auto rc = spi_flash_read(entryAddress, &header, sizeof(header));
auto rc = mPartition->read_raw(entryAddress, &header, sizeof(header));
if (rc != ESP_OK) {
mState = PageState::INVALID;
return rc;
@ -722,7 +728,7 @@ esp_err_t Page::initialize()
header.mVersion = mVersion;
header.mCrc32 = header.calculateCrc32();
auto rc = spi_flash_write(mBaseAddress, &header, sizeof(header));
auto rc = mPartition->write_raw(mBaseAddress, &header, sizeof(header));
if (rc != ESP_OK) {
mState = PageState::INVALID;
return rc;
@ -739,7 +745,7 @@ esp_err_t Page::alterEntryState(size_t index, EntryState state)
mEntryTable.set(index, state);
size_t wordToWrite = mEntryTable.getWordIndex(index);
uint32_t word = mEntryTable.data()[wordToWrite];
auto rc = spi_flash_write(mBaseAddress + ENTRY_TABLE_OFFSET + static_cast<uint32_t>(wordToWrite) * 4,
auto rc = mPartition->write_raw(mBaseAddress + ENTRY_TABLE_OFFSET + static_cast<uint32_t>(wordToWrite) * 4,
&word, sizeof(word));
if (rc != ESP_OK) {
mState = PageState::INVALID;
@ -763,7 +769,7 @@ esp_err_t Page::alterEntryRangeState(size_t begin, size_t end, EntryState state)
}
if (nextWordIndex != wordIndex) {
uint32_t word = mEntryTable.data()[wordIndex];
auto rc = spi_flash_write(mBaseAddress + ENTRY_TABLE_OFFSET + static_cast<uint32_t>(wordIndex) * 4,
auto rc = mPartition->write_raw(mBaseAddress + ENTRY_TABLE_OFFSET + static_cast<uint32_t>(wordIndex) * 4,
&word, 4);
if (rc != ESP_OK) {
return rc;
@ -777,7 +783,7 @@ esp_err_t Page::alterEntryRangeState(size_t begin, size_t end, EntryState state)
esp_err_t Page::alterPageState(PageState state)
{
uint32_t state_val = static_cast<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) {
mState = PageState::INVALID;
return rc;
@ -788,7 +794,7 @@ esp_err_t Page::alterPageState(PageState state)
esp_err_t Page::readEntry(size_t index, Item& dst) const
{
auto rc = nvs_flash_read(getEntryAddress(index), &dst, sizeof(dst));
auto rc = mPartition->read(getEntryAddress(index), &dst, sizeof(dst));
if (rc != ESP_OK) {
return rc;
}
@ -925,8 +931,7 @@ esp_err_t Page::setVersion(uint8_t ver)
esp_err_t Page::erase()
{
auto sector = mBaseAddress / SPI_FLASH_SEC_SIZE;
auto rc = spi_flash_erase_sector(sector);
auto rc = mPartition->erase_range(mBaseAddress, SPI_FLASH_SEC_SIZE);
if (rc != ESP_OK) {
mState = PageState::INVALID;
return rc;

View File

@ -24,6 +24,7 @@
#include "compressed_enum_table.hpp"
#include "intrusive_list.h"
#include "nvs_item_hash_list.hpp"
#include "partition.hpp"
namespace nvs
{
@ -77,17 +78,19 @@ public:
INVALID = 0
};
Page();
PageState state() const
{
return mState;
}
esp_err_t load(uint32_t sectorNumber);
esp_err_t load(Partition *partition, uint32_t sectorNumber);
esp_err_t getSeqNumber(uint32_t& seqNumber) const;
esp_err_t setSeqNumber(uint32_t seqNumber);
esp_err_t setVersion(uint8_t version);
esp_err_t writeItem(uint8_t nsIndex, ItemType datatype, const char* key, const void* data, size_t dataSize, uint8_t chunkIdx = CHUNK_ANY);
@ -188,7 +191,7 @@ protected:
esp_err_t readEntry(size_t index, Item& dst) const;
esp_err_t writeEntry(const Item& item);
esp_err_t writeEntryData(const uint8_t* data, size_t size);
esp_err_t eraseEntryAndSpan(size_t index);
@ -205,7 +208,7 @@ protected:
assert(entry < ENTRY_COUNT);
return mBaseAddress + ENTRY_DATA_OFFSET + static_cast<uint32_t>(entry) * ENTRY_SIZE;
}
static const char* pageStateToName(PageState ps);
@ -223,6 +226,8 @@ protected:
HashList mHashList;
Partition *mPartition;
static const uint32_t HEADER_OFFSET = 0;
static const uint32_t ENTRY_TABLE_OFFSET = HEADER_OFFSET + 32;
static const uint32_t ENTRY_DATA_OFFSET = ENTRY_TABLE_OFFSET + 32;

View File

@ -15,8 +15,12 @@
namespace nvs
{
esp_err_t PageManager::load(uint32_t baseSector, uint32_t sectorCount)
esp_err_t PageManager::load(Partition *partition, uint32_t baseSector, uint32_t sectorCount)
{
if (partition == nullptr) {
return ESP_ERR_INVALID_ARG;
}
mBaseSector = baseSector;
mPageCount = sectorCount;
mPageList.clear();
@ -26,7 +30,7 @@ esp_err_t PageManager::load(uint32_t baseSector, uint32_t sectorCount)
if (!mPages) return ESP_ERR_NO_MEM;
for (uint32_t i = 0; i < sectorCount; ++i) {
auto err = mPages[i].load(baseSector + i);
auto err = mPages[i].load(partition, baseSector + i);
if (err != ESP_OK) {
return err;
}
@ -126,7 +130,7 @@ esp_err_t PageManager::load(uint32_t baseSector, uint32_t sectorCount)
}
// partition should have at least one free page
if (mFreePageList.size() == 0) {
if (mFreePageList.empty()) {
return ESP_ERR_NVS_NO_FREE_PAGES;
}

View File

@ -18,7 +18,7 @@
#include <list>
#include "nvs_types.hpp"
#include "nvs_page.hpp"
#include "nvs_pagemanager.hpp"
#include "partition.hpp"
#include "intrusive_list.h"
namespace nvs
@ -31,7 +31,7 @@ public:
PageManager() {}
esp_err_t load(uint32_t baseSector, uint32_t sectorCount);
esp_err_t load(Partition *partition, uint32_t baseSector, uint32_t sectorCount);
TPageListIterator begin()
{

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.
#include "esp_partition.h"
#include "nvs_partition_manager.hpp"
#include "nvs_partition_lookup.hpp"
#ifdef CONFIG_NVS_ENCRYPTION
#include "nvs_encrypted_partition.hpp"
#endif // CONFIG_NVS_ENCRYPTION
namespace nvs {
@ -30,6 +35,11 @@ NVSPartitionManager* NVSPartitionManager::get_instance()
#ifdef ESP_PLATFORM
esp_err_t NVSPartitionManager::init_partition(const char *partition_label)
{
if (strlen(partition_label) > NVS_PART_NAME_MAX_SIZE) {
return ESP_ERR_INVALID_ARG;
}
uint32_t size;
Storage* mStorage;
mStorage = lookup_storage_from_name(partition_label);
@ -39,33 +49,55 @@ esp_err_t NVSPartitionManager::init_partition(const char *partition_label)
assert(SPI_FLASH_SEC_SIZE != 0);
const esp_partition_t* partition = esp_partition_find_first(
ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, partition_label);
if (partition == nullptr) {
return ESP_ERR_NOT_FOUND;
NVSPartition *p = nullptr;
esp_err_t result = lookup_nvs_partition(partition_label, &p);
if (result != ESP_OK) {
goto error;
}
return init_custom(partition_label, partition->address / SPI_FLASH_SEC_SIZE,
partition->size / SPI_FLASH_SEC_SIZE);
size = p->get_size();
result = init_custom(p, 0, size / SPI_FLASH_SEC_SIZE);
if (result != ESP_OK) {
goto error;
}
nvs_partition_list.push_back(p);
return ESP_OK;
error:
delete p;
return result;
}
#endif // ESP_PLATFORM
esp_err_t NVSPartitionManager::init_custom(const char *partName, uint32_t baseSector, uint32_t sectorCount)
esp_err_t NVSPartitionManager::init_custom(Partition *partition, uint32_t baseSector, uint32_t sectorCount)
{
if (strlen(partName) > NVS_PART_NAME_MAX_SIZE) return ESP_ERR_INVALID_ARG;
Storage* new_storage = nullptr;
Storage* storage = lookup_storage_from_name(partition->get_partition_name());
if (storage == nullptr) {
new_storage = new (std::nothrow) Storage(partition);
Storage* new_storage = NULL;
Storage* storage = lookup_storage_from_name(partName);
if (storage == NULL) {
new_storage = new (std::nothrow) Storage((const char *)partName);
if (!new_storage) return ESP_ERR_NO_MEM;
if (new_storage == nullptr) {
return ESP_ERR_NO_MEM;
}
storage = new_storage;
} else {
// if storage was initialized already, we don't need partition and hence delete it
for (auto it = nvs_partition_list.begin(); it != nvs_partition_list.end(); ++it) {
if (partition == it) {
nvs_partition_list.erase(it);
delete partition;
break;
}
}
}
esp_err_t err = storage->init(baseSector, sectorCount);
if (new_storage != NULL) {
if (new_storage != nullptr) {
if (err == ESP_OK) {
nvs_storage_list.push_back(new_storage);
} else {
@ -75,41 +107,47 @@ esp_err_t NVSPartitionManager::init_custom(const char *partName, uint32_t baseSe
return err;
}
#ifdef ESP_PLATFORM
#ifdef CONFIG_NVS_ENCRYPTION
#ifdef ESP_PLATFORM
esp_err_t NVSPartitionManager::secure_init_partition(const char *part_name, nvs_sec_cfg_t* cfg)
{
if (strlen(part_name) > NVS_PART_NAME_MAX_SIZE) {
return ESP_ERR_INVALID_ARG;
}
Storage* mStorage;
mStorage = lookup_storage_from_name(part_name);
if (mStorage) {
if (mStorage != nullptr) {
return ESP_OK;
}
const esp_partition_t* partition = esp_partition_find_first(
ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, part_name);
if (partition == NULL) {
return ESP_ERR_NOT_FOUND;
NVSPartition *p;
esp_err_t result;
if (cfg != nullptr) {
result = lookup_nvs_encrypted_partition(part_name, cfg, &p);
} else {
result = lookup_nvs_partition(part_name, &p);
}
return secure_init_custom(part_name, partition->address / SPI_FLASH_SEC_SIZE,
partition->size / SPI_FLASH_SEC_SIZE, cfg);
}
esp_err_t NVSPartitionManager::secure_init_custom(const char *partName, uint32_t baseSector, uint32_t sectorCount, nvs_sec_cfg_t* cfg)
{
if(cfg) {
auto encrMgr = EncrMgr::getInstance();
auto err = encrMgr->setSecurityContext(baseSector, sectorCount, cfg);
if(err != ESP_OK) {
return err;
}
if (result != ESP_OK) {
return result;
}
return init_custom(partName, baseSector, sectorCount);
uint32_t size = p->get_size();
result = init_custom(p, 0, size / SPI_FLASH_SEC_SIZE);
if (result != ESP_OK) {
delete p;
return result;
}
nvs_partition_list.push_back(p);
return ESP_OK;
}
#endif // CONFIG_NVS_ENCRYPTION
#endif // ESP_PLATFORM
#endif // CONFIG_NVS_ENCRYPTION
esp_err_t NVSPartitionManager::deinit_partition(const char *partition_label)
{
@ -118,13 +156,6 @@ esp_err_t NVSPartitionManager::deinit_partition(const char *partition_label)
return ESP_ERR_NVS_NOT_INITIALIZED;
}
#ifdef CONFIG_NVS_ENCRYPTION
if(EncrMgr::isEncrActive()) {
auto encrMgr = EncrMgr::getInstance();
encrMgr->removeSecurityContext(storage->getBaseSector());
}
#endif
/* Clean up handles related to the storage being deinitialized */
for (auto it = nvs_handles.begin(); it != nvs_handles.end(); ++it) {
if (it->mStoragePtr == storage) {
@ -133,10 +164,19 @@ esp_err_t NVSPartitionManager::deinit_partition(const char *partition_label)
}
}
/* Finally delete the storage itself */
/* Finally delete the storage and its partition */
nvs_storage_list.erase(storage);
delete storage;
for (auto it = nvs_partition_list.begin(); it != nvs_partition_list.end(); ++it) {
if (strcmp(it->get_partition_name(), partition_label) == 0) {
NVSPartition *p = it;
nvs_partition_list.erase(it);
delete p;
break;
}
}
return ESP_OK;
}
@ -148,12 +188,12 @@ esp_err_t NVSPartitionManager::open_handle(const char *part_name,
uint8_t nsIndex;
Storage* sHandle;
if (nvs_storage_list.size() == 0) {
if (nvs_storage_list.empty()) {
return ESP_ERR_NVS_NOT_INITIALIZED;
}
sHandle = lookup_storage_from_name(part_name);
if (sHandle == NULL) {
if (sHandle == nullptr) {
return ESP_ERR_NVS_PART_NOT_FOUND;
}
@ -164,7 +204,9 @@ esp_err_t NVSPartitionManager::open_handle(const char *part_name,
*handle = new (std::nothrow) NVSHandleSimple(open_mode==NVS_READONLY, nsIndex, sHandle);
if (!handle) return ESP_ERR_NO_MEM;
if (handle == nullptr) {
return ESP_ERR_NO_MEM;
}
nvs_handles.push_back(*handle);
@ -194,7 +236,7 @@ Storage* NVSPartitionManager::lookup_storage_from_name(const char* name)
});
if (it == end(nvs_storage_list)) {
return NULL;
return nullptr;
}
return it;
}

View File

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

View File

@ -90,7 +90,7 @@ void Storage::eraseOrphanDataBlobs(TBlobIndexList& blobIdxList)
esp_err_t Storage::init(uint32_t baseSector, uint32_t sectorCount)
{
auto err = mPageManager.load(baseSector, sectorCount);
auto err = mPageManager.load(mPartition, baseSector, sectorCount);
if (err != ESP_OK) {
mState = StorageState::INVALID;
return err;

View File

@ -20,6 +20,7 @@
#include "nvs_types.hpp"
#include "nvs_page.hpp"
#include "nvs_pagemanager.hpp"
#include "partition.hpp"
//extern void dumpBytes(const uint8_t* data, size_t count);
@ -60,9 +61,10 @@ class Storage : public intrusive_list_node<Storage>
public:
~Storage();
Storage(const char *pName = NVS_DEFAULT_PART_NAME)
{
strncpy(mPartitionName, pName, NVS_PART_NAME_MAX_SIZE);
Storage(Partition *partition) : mPartition(partition) {
if (partition == nullptr) {
abort();
}
};
esp_err_t init(uint32_t baseSector, uint32_t sectorCount);
@ -98,10 +100,16 @@ public:
esp_err_t eraseNamespace(uint8_t nsIndex);
const Partition *getPart() const
{
return mPartition;
}
const char *getPartName() const
{
return mPartitionName;
return mPartition->get_partition_name();
}
uint32_t getBaseSector()
{
return mPageManager.getBaseSector();
@ -145,7 +153,7 @@ protected:
esp_err_t findItem(uint8_t nsIndex, ItemType datatype, const char* key, Page* &page, Item& item, uint8_t chunkIdx = Page::CHUNK_ANY, VerOffset chunkStart = VerOffset::VER_ANY);
protected:
char mPartitionName [NVS_PART_NAME_MAX_SIZE + 1];
Partition *mPartition;
size_t mPageCount;
PageManager mPageManager;
TNamespaces mNamespaces;

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 "."
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";
TEST_CASE("Partition name no longer than 16 characters", "[nvs]")
{
const char *TOO_LONG_NAME = "0123456789abcdefg";
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, nvs_flash_init_partition(TOO_LONG_NAME));
nvs_flash_deinit_partition(TOO_LONG_NAME); // just in case
}
TEST_CASE("flash erase deinitializes initialized partition", "[nvs]")
{
nvs_handle_t handle;
@ -26,7 +35,7 @@ TEST_CASE("flash erase deinitializes initialized partition", "[nvs]")
nvs_flash_erase();
err = nvs_flash_init();
}
ESP_ERROR_CHECK( err );
TEST_ESP_OK( err );
TEST_ESP_OK(nvs_flash_init());
TEST_ESP_OK(nvs_open("uninit_ns", NVS_READWRITE, &handle));
@ -468,7 +477,7 @@ TEST_CASE("test nvs apis for nvs partition generator utility with encryption ena
}
for (int i = 0; i < nvs_part->size; i+= SPI_FLASH_SEC_SIZE) {
ESP_ERROR_CHECK( spi_flash_write(nvs_part->address + i, nvs_data_start + i, SPI_FLASH_SEC_SIZE) );
ESP_ERROR_CHECK( esp_partition_write(nvs_part, i, nvs_data_start + i, SPI_FLASH_SEC_SIZE) );
}
esp_err_t err = nvs_flash_read_security_cfg(key_part, &xts_cfg);

View File

@ -10,11 +10,11 @@ SOURCE_FILES = \
nvs_pagemanager.cpp \
nvs_storage.cpp \
nvs_item_hash_list.cpp \
nvs_encr.cpp \
nvs_ops.cpp \
nvs_handle_simple.cpp \
nvs_handle_locked.cpp \
nvs_partition_manager.cpp \
nvs_partition.cpp \
nvs_encrypted_partition.cpp \
nvs_cxx_api.cpp \
) \
spi_flash_emulation.cpp \
@ -25,6 +25,7 @@ SOURCE_FILES = \
test_partition_manager.cpp \
test_nvs_handle.cpp \
test_nvs_storage.cpp \
test_nvs_partition.cpp \
test_nvs_cxx_api.cpp \
test_nvs_initialization.cpp \
crc.cpp \
@ -36,7 +37,7 @@ else
COMPILER := gcc
endif
CPPFLAGS += -I../include -I../src -I./ -I../../esp_common/include -I../../esp32/include -I ../../mbedtls/mbedtls/include -I ../../spi_flash/include -I ../../hal/include -I ../../xtensa/include -I ../../../tools/catch -fprofile-arcs -ftest-coverage
CPPFLAGS += -I../include -I../src -I./ -I../../esp_common/include -I../../esp32/include -I ../../mbedtls/mbedtls/include -I ../../spi_flash/include -I ../../hal/include -I ../../xtensa/include -I ../../../tools/catch -fprofile-arcs -ftest-coverage -g2 -ggdb
CFLAGS += -fprofile-arcs -ftest-coverage
CXXFLAGS += -std=c++11 -Wall -Werror
LDFLAGS += -lstdc++ -Wall -fprofile-arcs -ftest-coverage
@ -89,6 +90,8 @@ clean: clean-coverage
rm -f ../nvs_partition_generator/partition_encrypted.bin
rm -f ../nvs_partition_generator/partition_encrypted_using_keygen.bin
rm -f ../nvs_partition_generator/partition_encrypted_using_keyfile.bin
rm -f ../nvs_partition_generator/partition_decrypted.bin
rm -f ../nvs_partition_generator/partition_encoded.bin
rm -f ../nvs_partition_generator/Test-1-partition-encrypted.bin
rm -f ../nvs_partition_generator/Test-1-partition.bin
rm -f ../../../tools/mass_mfg/samples/sample_values_multipage_blob_created.csv

View File

@ -11,7 +11,7 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "esp_spi_flash.h"
#include "esp_partition.h"
#include "spi_flash_emulation.h"
@ -22,39 +22,82 @@ void spi_flash_emulator_set(SpiFlashEmulator* e)
s_emulator = e;
}
esp_err_t spi_flash_erase_sector(size_t sec)
esp_err_t esp_partition_erase_range(const esp_partition_t* partition,
size_t offset, size_t size)
{
if (!s_emulator) {
return ESP_ERR_FLASH_OP_TIMEOUT;
}
if (!s_emulator->erase(sec)) {
if (size % SPI_FLASH_SEC_SIZE != 0) {
return ESP_ERR_INVALID_SIZE;
}
if (offset % SPI_FLASH_SEC_SIZE != 0) {
return ESP_ERR_INVALID_ARG;
}
size_t start_sector = offset / SPI_FLASH_SEC_SIZE;
size_t num_sectors = size / SPI_FLASH_SEC_SIZE;
for (size_t sector = start_sector; sector < (start_sector + num_sectors); sector++) {
if (!s_emulator->erase(sector)) {
return ESP_ERR_FLASH_OP_FAIL;
}
}
return ESP_OK;
}
esp_err_t esp_partition_read(const esp_partition_t* partition,
size_t src_offset, void* dst, size_t size)
{
if (!s_emulator) {
return ESP_ERR_FLASH_OP_TIMEOUT;
}
if (!s_emulator->read(reinterpret_cast<uint32_t*>(dst), src_offset, size)) {
return ESP_ERR_FLASH_OP_FAIL;
}
return ESP_OK;
}
esp_err_t spi_flash_write(size_t des_addr, const void *src_addr, size_t size)
esp_err_t esp_partition_read_raw(const esp_partition_t* partition,
size_t src_offset, void* dst, size_t size)
{
if (!s_emulator) {
return ESP_ERR_FLASH_OP_TIMEOUT;
}
if (!s_emulator->write(des_addr, reinterpret_cast<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_OK;
}
esp_err_t spi_flash_read(size_t src_addr, void *des_addr, size_t size)
esp_err_t esp_partition_write(const esp_partition_t* partition,
size_t dst_offset, const void* src, size_t size)
{
if (!s_emulator) {
return ESP_ERR_FLASH_OP_TIMEOUT;
}
if (!s_emulator->read(reinterpret_cast<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;
}

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 "spi_flash_emulation.h"
#include "test_fixtures.hpp"
#include <iostream>
using namespace std;
@ -27,12 +29,12 @@ TEST_CASE("NVSHandleSimple CXX api open invalid arguments", "[nvs cxx]")
{
const uint32_t NVS_FLASH_SECTOR = 6;
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
SpiFlashEmulator emu(10);
PartitionEmulationFixture f(0, 10, "test");
esp_err_t result;
shared_ptr<nvs::NVSHandle> handle;
REQUIRE(nvs::NVSPartitionManager::get_instance()->
init_custom("test", NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK);
init_custom(&f.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK);
handle = nvs::open_nvs_handle_from_partition(nullptr, "ns_1", NVS_READWRITE, &result);
CHECK(result == ESP_ERR_INVALID_ARG);
@ -61,11 +63,11 @@ TEST_CASE("NVSHandleSimple CXX api open successful", "[nvs cxx]")
{
const uint32_t NVS_FLASH_SECTOR = 6;
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
SpiFlashEmulator emu(10);
PartitionEmulationFixture f(0, 10, "test");
esp_err_t result;
shared_ptr<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);
CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0);
@ -87,11 +89,11 @@ TEST_CASE("NVSHandleSimple CXX api open default part successful", "[nvs cxx]")
{
const uint32_t NVS_FLASH_SECTOR = 6;
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
SpiFlashEmulator emu(10);
PartitionEmulationFixture f(0, 10);
esp_err_t result;
shared_ptr<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);
CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0);
@ -113,11 +115,11 @@ TEST_CASE("NVSHandleSimple CXX api open default part ns NULL", "[nvs cxx]")
{
const uint32_t NVS_FLASH_SECTOR = 6;
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
SpiFlashEmulator emu(10);
PartitionEmulationFixture f(0, 10);
esp_err_t result;
shared_ptr<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);
CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0);
@ -135,12 +137,12 @@ TEST_CASE("NVSHandleSimple CXX api read/write string", "[nvs cxx]")
{
const uint32_t NVS_FLASH_SECTOR = 6;
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
SpiFlashEmulator emu(10);
PartitionEmulationFixture f(0, 10);
char read_buffer [256];
esp_err_t result;
shared_ptr<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);
CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0);
@ -164,13 +166,13 @@ TEST_CASE("NVSHandleSimple CXX api read/write blob", "[nvs cxx]")
{
const uint32_t NVS_FLASH_SECTOR = 6;
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
SpiFlashEmulator emu(10);
PartitionEmulationFixture f(0, 10);
const char blob [6] = {15, 16, 17, 18, 19};
char read_blob[6] = {0};
esp_err_t result;
shared_ptr<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);
CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0);

View File

@ -19,6 +19,8 @@
#include "nvs_partition_manager.hpp"
#include "spi_flash_emulation.h"
#include "test_fixtures.hpp"
#include <iostream>
#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_COUNT_MIN = 3;
SpiFlashEmulator emu(10);
PartitionEmulationFixture f(0, 10, "test");
REQUIRE(NVSPartitionManager::get_instance()->init_custom("test", NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)
REQUIRE(NVSPartitionManager::get_instance()->init_custom(&f.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)
== ESP_OK);
CHECK(NVSPartitionManager::get_instance()->open_handles_size() == 0);
@ -53,9 +55,9 @@ TEST_CASE("NVSHandleSimple multiple open and closes with PartitionManager", "[pa
{
const uint32_t NVS_FLASH_SECTOR = 6;
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
SpiFlashEmulator emu(10);
PartitionEmulationFixture f(0, 10, "test");
REQUIRE(NVSPartitionManager::get_instance()->init_custom("test", NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)
REQUIRE(NVSPartitionManager::get_instance()->init_custom(&f.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)
== ESP_OK);
CHECK(NVSPartitionManager::get_instance()->open_handles_size() == 0);
@ -83,18 +85,18 @@ TEST_CASE("NVSHandleSimple multiple open and closes with PartitionManager", "[pa
}
TEST_CASE("nvshandle readonly fails", "[partition_mgr]")
TEST_CASE("NVSHandleSimple readonly fails", "[partition_mgr]")
{
SpiFlashEmulator emu(10);
PartitionEmulationFixture f(0, 10);
NVSPartitionManager::get_instance()->deinit_partition(NVS_DEFAULT_PART_NAME);
NVSHandleSimple *handle_1;
NVSHandleSimple *handle_2;
const uint32_t NVS_FLASH_SECTOR = 6;
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);
f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);
CHECK(NVSPartitionManager::get_instance()->init_custom(NVS_DEFAULT_PART_NAME, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK);
CHECK(NVSPartitionManager::get_instance()->init_custom(&f.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK);
CHECK(NVSPartitionManager::get_instance()->open_handles_size() == 0);
// first, creating namespace...
@ -123,13 +125,13 @@ TEST_CASE("NVSHandleSimple set/get char", "[partition_mgr]")
BAR
};
SpiFlashEmulator emu(10);
PartitionEmulationFixture f(0, 10);
const uint32_t NVS_FLASH_SECTOR = 6;
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);
f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);
REQUIRE(NVSPartitionManager::get_instance()->init_custom(NVS_DEFAULT_PART_NAME, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)
REQUIRE(NVSPartitionManager::get_instance()->init_custom(&f.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)
== ESP_OK);
NVSHandleSimple *handle;
@ -155,13 +157,13 @@ TEST_CASE("NVSHandleSimple correctly sets/gets int enum", "[partition_mgr]")
BAR
};
SpiFlashEmulator emu(10);
PartitionEmulationFixture f(0, 10);
const uint32_t NVS_FLASH_SECTOR = 6;
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);
f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);
REQUIRE(NVSPartitionManager::get_instance()->init_custom(NVS_DEFAULT_PART_NAME, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)
REQUIRE(NVSPartitionManager::get_instance()->init_custom(&f.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)
== ESP_OK);
NVSHandleSimple *handle;
@ -188,13 +190,13 @@ TEST_CASE("NVSHandleSimple correctly sets/gets int enum with negative values", "
BAR
};
SpiFlashEmulator emu(10);
PartitionEmulationFixture f(0, 10);
const uint32_t NVS_FLASH_SECTOR = 6;
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);
f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);
REQUIRE(NVSPartitionManager::get_instance()->init_custom(NVS_DEFAULT_PART_NAME, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)
REQUIRE(NVSPartitionManager::get_instance()->init_custom(&f.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)
== ESP_OK);
NVSHandleSimple *handle;
@ -220,13 +222,13 @@ TEST_CASE("NVSHandleSimple correctly sets/gets uint8_t enum", "[partition_mgr]")
BAR
};
SpiFlashEmulator emu(10);
PartitionEmulationFixture f(0, 10);
const uint32_t NVS_FLASH_SECTOR = 6;
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);
f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);
REQUIRE(NVSPartitionManager::get_instance()->init_custom(NVS_DEFAULT_PART_NAME, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)
REQUIRE(NVSPartitionManager::get_instance()->init_custom(&f.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)
== ESP_OK);
NVSHandleSimple *handle;
@ -253,13 +255,13 @@ TEST_CASE("NVSHandleSimple correctly sets/gets char enum", "[partition_mgr]")
BAR
};
SpiFlashEmulator emu(10);
PartitionEmulationFixture f(0, 10);
const uint32_t NVS_FLASH_SECTOR = 6;
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);
f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);
REQUIRE(NVSPartitionManager::get_instance()->init_custom(NVS_DEFAULT_PART_NAME, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)
REQUIRE(NVSPartitionManager::get_instance()->init_custom(&f.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)
== ESP_OK);
NVSHandleSimple *handle;

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 "spi_flash_emulation.h"
#include "test_fixtures.hpp"
#include <iostream>
using namespace std;
@ -27,9 +29,9 @@ TEST_CASE("Storage iterator recognizes blob with VerOffset::VER_1_OFFSET", "[nvs
{
const uint32_t NVS_FLASH_SECTOR = 6;
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
SpiFlashEmulator emu(10);
PartitionEmulationFixture f(0, 10, "test");
REQUIRE(NVSPartitionManager::get_instance()->init_custom("test", NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)
REQUIRE(NVSPartitionManager::get_instance()->init_custom(&f.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)
== ESP_OK);
uint8_t blob [] = {0x0, 0x1, 0x2, 0x3};

View File

@ -20,25 +20,28 @@
#include "spi_flash_emulation.h"
#include "nvs_test_api.h"
#include "test_fixtures.hpp"
using namespace nvs;
TEST_CASE("Partition manager initializes storage", "[partition_mgr]")
{
const uint32_t NVS_FLASH_SECTOR = 6;
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
SpiFlashEmulator emu(10);
PartitionEmulationFixture f(0, 10, "test");
REQUIRE(NVSPartitionManager::get_instance()->init_custom("test", NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK);
REQUIRE(NVSPartitionManager::get_instance()->init_custom(&f.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK);
CHECK(NVSPartitionManager::get_instance()->lookup_storage_from_name("test") != nullptr);
REQUIRE(NVSPartitionManager::get_instance()->deinit_partition(f.part.get_partition_name()) == ESP_OK);
}
TEST_CASE("Partition manager de-initializes storage", "[partition_mgr]")
{
const uint32_t NVS_FLASH_SECTOR = 6;
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
SpiFlashEmulator emu(10);
PartitionEmulationFixture f(0, 10, "test");
REQUIRE(NVSPartitionManager::get_instance()->init_custom("test", NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK);
REQUIRE(NVSPartitionManager::get_instance()->init_custom(&f.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK);
CHECK(NVSPartitionManager::get_instance()->lookup_storage_from_name("test") != nullptr);
CHECK(NVSPartitionManager::get_instance()->deinit_partition("test") == ESP_OK);
CHECK(NVSPartitionManager::get_instance()->lookup_storage_from_name("test") == nullptr);
@ -49,11 +52,13 @@ TEST_CASE("Partition manager initializes multiple partitions", "[partition_mgr]"
const uint32_t NVS_FLASH_SECTOR = 6;
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
SpiFlashEmulator emu(10);
PartitionEmulation part_0(&emu, NVS_FLASH_SECTOR * SPI_FLASH_SEC_SIZE, NVS_FLASH_SECTOR_COUNT_MIN * SPI_FLASH_SEC_SIZE, "test1");
PartitionEmulation part_1(&emu, NVS_FLASH_SECTOR * SPI_FLASH_SEC_SIZE, NVS_FLASH_SECTOR_COUNT_MIN * SPI_FLASH_SEC_SIZE, "test2");
REQUIRE(NVSPartitionManager::get_instance()->init_custom("test1", NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)
REQUIRE(NVSPartitionManager::get_instance()->init_custom(&part_0, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)
== ESP_OK);
// TODO: why does this work, actually? same sectors used as above
REQUIRE(NVSPartitionManager::get_instance()->init_custom("test2", NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)
REQUIRE(NVSPartitionManager::get_instance()->init_custom(&part_1, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)
== ESP_OK);
Storage *storage1 = NVSPartitionManager::get_instance()->lookup_storage_from_name("test1");
REQUIRE(storage1 != nullptr);
@ -61,15 +66,17 @@ TEST_CASE("Partition manager initializes multiple partitions", "[partition_mgr]"
REQUIRE(storage2 != nullptr);
CHECK(storage1 != storage2);
REQUIRE(NVSPartitionManager::get_instance()->deinit_partition(part_0.get_partition_name()) == ESP_OK);
REQUIRE(NVSPartitionManager::get_instance()->deinit_partition(part_1.get_partition_name()) == ESP_OK);
}
TEST_CASE("Partition manager invalidates handle on partition de-init", "[partition_mgr]")
{
const uint32_t NVS_FLASH_SECTOR = 6;
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
SpiFlashEmulator emu(10);
PartitionEmulationFixture f(0, 10, "test");
REQUIRE(NVSPartitionManager::get_instance()->init_custom("test", NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)
REQUIRE(NVSPartitionManager::get_instance()->init_custom(&f.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)
== ESP_OK);
NVSHandleSimple *handle;

View File

@ -11,9 +11,9 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <functional>
#include "catch.hpp"
#include "esp_spi_flash.h"
#include "esp_partition.h"
#include "spi_flash_emulation.h"
#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));
}
struct FlashEmuFixture {
FlashEmuFixture(size_t sectors) : esp_part(), emu(sectors) { }
esp_partition_t esp_part;
SpiFlashEmulator emu;
};
TEST_CASE("flash starts with all bytes == 0xff", "[spi_flash_emu]")
{
SpiFlashEmulator emu(4);
FlashEmuFixture f(4);
uint8_t sector[SPI_FLASH_SEC_SIZE];
for (int i = 0; i < 4; ++i) {
CHECK(spi_flash_read(0, sector, sizeof(sector)) == ESP_OK);
CHECK(esp_partition_read(&f.esp_part, 0, sector, sizeof(sector)) == ESP_OK);
for (auto v: sector) {
CHECK(v == 0xff);
}
@ -41,116 +48,139 @@ TEST_CASE("flash starts with all bytes == 0xff", "[spi_flash_emu]")
TEST_CASE("invalid writes are checked", "[spi_flash_emu]")
{
SpiFlashEmulator emu(1);
FlashEmuFixture f(1);
uint32_t val = 0;
CHECK(spi_flash_write(0, &val, 4) == ESP_OK);
CHECK(esp_partition_write(&f.esp_part, 0, &val, 4) == ESP_OK);
val = 1;
CHECK(spi_flash_write(0, &val, 4) == ESP_ERR_FLASH_OP_FAIL);
CHECK(esp_partition_write(&f.esp_part, 0, &val, 4) == ESP_ERR_FLASH_OP_FAIL);
}
TEST_CASE("out of bounds writes fail", "[spi_flash_emu]")
{
SpiFlashEmulator emu(4);
FlashEmuFixture f(4);
uint32_t vals[8];
std::fill_n(vals, 8, 0);
CHECK(spi_flash_write(0, vals, sizeof(vals)) == ESP_OK);
CHECK(esp_partition_write(&f.esp_part, 0, &vals, sizeof(vals)) == ESP_OK);
CHECK(spi_flash_write(4*4096 - sizeof(vals), vals, sizeof(vals)) == ESP_OK);
CHECK(esp_partition_write(&f.esp_part, 4*4096 - sizeof(vals), &vals, sizeof(vals)) == ESP_OK);
CHECK(spi_flash_write(4*4096 - sizeof(vals) + 4, vals, sizeof(vals)) == ESP_ERR_FLASH_OP_FAIL);
CHECK(esp_partition_write(&f.esp_part, 4*4096 - sizeof(vals) + 4, &vals, sizeof(vals)) == ESP_ERR_FLASH_OP_FAIL);
}
TEST_CASE("after erase the sector is set to 0xff", "[spi_flash_emu]")
{
SpiFlashEmulator emu(4);
FlashEmuFixture f(4);
uint32_t val1 = 0xab00cd12;
CHECK(spi_flash_write(0, &val1, sizeof(val1)) == ESP_OK);
CHECK(esp_partition_write(&f.esp_part, 0, &val1, sizeof(val1)) == ESP_OK);
uint32_t val2 = 0x5678efab;
CHECK(spi_flash_write(4096 - 4, &val2, sizeof(val2)) == ESP_OK);
CHECK(esp_partition_write(&f.esp_part, 4096 - 4, &val2, sizeof(val2)) == ESP_OK);
CHECK(emu.words()[0] == val1);
CHECK(range_empty_n(emu.words() + 1, 4096 / 4 - 2));
CHECK(emu.words()[4096 / 4 - 1] == val2);
CHECK(f.emu.words()[0] == val1);
CHECK(range_empty_n(f.emu.words() + 1, 4096 / 4 - 2));
CHECK(f.emu.words()[4096 / 4 - 1] == val2);
CHECK(spi_flash_erase_sector(0) == ESP_OK);
CHECK(esp_partition_erase_range(&f.esp_part, 0, SPI_FLASH_SEC_SIZE) == ESP_OK);
CHECK(emu.words()[0] == 0xffffffff);
CHECK(range_empty_n(emu.words() + 1, 4096 / 4 - 2));
CHECK(emu.words()[4096 / 4 - 1] == 0xffffffff);
CHECK(f.emu.words()[0] == 0xffffffff);
CHECK(range_empty_n(f.emu.words() + 1, 4096 / 4 - 2));
CHECK(f.emu.words()[4096 / 4 - 1] == 0xffffffff);
}
TEST_CASE("EMU raw read function works", "[spi_flash_emu]")
{
FlashEmuFixture f(4);
uint32_t value = 0xdeadbeef;
uint32_t read_value = 0;
CHECK(esp_partition_write(&f.esp_part, 0, &value, sizeof(value)) == ESP_OK);
CHECK(esp_partition_read_raw(&f.esp_part, 0, &read_value, sizeof(&read_value)) == ESP_OK);
CHECK(read_value == 0xdeadbeef);
}
TEST_CASE("EMU raw write function works", "[spi_flash_emu]")
{
FlashEmuFixture f(4);
uint32_t value = 0xdeadbeef;
uint32_t read_value = 0;
CHECK(esp_partition_write_raw(&f.esp_part, 0, &value, sizeof(value)) == ESP_OK);
CHECK(esp_partition_read(&f.esp_part, 0, &read_value, sizeof(&read_value)) == ESP_OK);
CHECK(read_value == 0xdeadbeef);
}
TEST_CASE("read/write/erase operation times are calculated correctly", "[spi_flash_emu]")
{
SpiFlashEmulator emu(1);
FlashEmuFixture f(1);
uint8_t data[512];
spi_flash_read(0, data, 4);
CHECK(emu.getTotalTime() == 7);
CHECK(emu.getReadOps() == 1);
CHECK(emu.getReadBytes() == 4);
emu.clearStats();
spi_flash_read(0, data, 8);
CHECK(emu.getTotalTime() == 5);
CHECK(emu.getReadOps() == 1);
CHECK(emu.getReadBytes() == 8);
emu.clearStats();
spi_flash_read(0, data, 16);
CHECK(emu.getTotalTime() == 6);
CHECK(emu.getReadOps() == 1);
CHECK(emu.getReadBytes() == 16);
emu.clearStats();
spi_flash_read(0, data, 128);
CHECK(emu.getTotalTime() == 18);
CHECK(emu.getReadOps() == 1);
CHECK(emu.getReadBytes() == 128);
emu.clearStats();
spi_flash_read(0, data, 256);
CHECK(emu.getTotalTime() == 32);
emu.clearStats();
spi_flash_read(0, data, (128+256)/2);
CHECK(emu.getTotalTime() == (18+32)/2);
emu.clearStats();
esp_partition_read(&f.esp_part, 0, data, 4);
CHECK(f.emu.getTotalTime() == 7);
CHECK(f.emu.getReadOps() == 1);
CHECK(f.emu.getReadBytes() == 4);
f.emu.clearStats();
esp_partition_read(&f.esp_part, 0, data, 8);
CHECK(f.emu.getTotalTime() == 5);
CHECK(f.emu.getReadOps() == 1);
CHECK(f.emu.getReadBytes() == 8);
f.emu.clearStats();
esp_partition_read(&f.esp_part, 0, data, 16);
CHECK(f.emu.getTotalTime() == 6);
CHECK(f.emu.getReadOps() == 1);
CHECK(f.emu.getReadBytes() == 16);
f.emu.clearStats();
esp_partition_read(&f.esp_part, 0, data, 128);
CHECK(f.emu.getTotalTime() == 18);
CHECK(f.emu.getReadOps() == 1);
CHECK(f.emu.getReadBytes() == 128);
f.emu.clearStats();
esp_partition_read(&f.esp_part, 0, data, 256);
CHECK(f.emu.getTotalTime() == 32);
f.emu.clearStats();
esp_partition_read(&f.esp_part, 0, data, (128+256)/2);
CHECK(f.emu.getTotalTime() == (18+32)/2);
f.emu.clearStats();
spi_flash_write(0, data, 4);
CHECK(emu.getTotalTime() == 19);
CHECK(emu.getWriteOps() == 1);
CHECK(emu.getWriteBytes() == 4);
emu.clearStats();
CHECK(emu.getWriteOps() == 0);
CHECK(emu.getWriteBytes() == 0);
spi_flash_write(0, data, 8);
CHECK(emu.getTotalTime() == 23);
emu.clearStats();
spi_flash_write(0, data, 16);
CHECK(emu.getTotalTime() == 35);
CHECK(emu.getWriteOps() == 1);
CHECK(emu.getWriteBytes() == 16);
emu.clearStats();
spi_flash_write(0, data, 128);
CHECK(emu.getTotalTime() == 205);
emu.clearStats();
spi_flash_write(0, data, 256);
CHECK(emu.getTotalTime() == 417);
emu.clearStats();
spi_flash_write(0, data, (128+256)/2);
CHECK(emu.getTotalTime() == (205+417)/2);
emu.clearStats();
esp_partition_write(&f.esp_part, 0, data, 4);
CHECK(f.emu.getTotalTime() == 19);
CHECK(f.emu.getWriteOps() == 1);
CHECK(f.emu.getWriteBytes() == 4);
f.emu.clearStats();
CHECK(f.emu.getWriteOps() == 0);
CHECK(f.emu.getWriteBytes() == 0);
esp_partition_write(&f.esp_part, 0, data, 8);
CHECK(f.emu.getTotalTime() == 23);
f.emu.clearStats();
esp_partition_write(&f.esp_part, 0, data, 16);
CHECK(f.emu.getTotalTime() == 35);
CHECK(f.emu.getWriteOps() == 1);
CHECK(f.emu.getWriteBytes() == 16);
f.emu.clearStats();
esp_partition_write(&f.esp_part, 0, data, 128);
CHECK(f.emu.getTotalTime() == 205);
f.emu.clearStats();
esp_partition_write(&f.esp_part, 0, data, 256);
CHECK(f.emu.getTotalTime() == 417);
f.emu.clearStats();
esp_partition_write(&f.esp_part, 0, data, (128+256)/2);
CHECK(f.emu.getTotalTime() == (205+417)/2);
f.emu.clearStats();
spi_flash_erase_sector(0);
CHECK(emu.getEraseOps() == 1);
CHECK(emu.getTotalTime() == 37142);
esp_partition_erase_range(&f.esp_part, 0, SPI_FLASH_SEC_SIZE);
CHECK(f.emu.getEraseOps() == 1);
CHECK(f.emu.getTotalTime() == 37142);
}
TEST_CASE("data is randomized predictably", "[spi_flash_emu]")
{
SpiFlashEmulator emu1(3);
emu1.randomize(0x12345678);
SpiFlashEmulator emu2(3);
emu2.randomize(0x12345678);
CHECK(std::equal(emu1.bytes(), emu1.bytes() + emu1.size(), emu2.bytes()));
}

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);
TEST_ASSERT_NOT_NULL(p);
TEST_ASSERT_EQUAL(0x10000, p->address);
TEST_ASSERT_EQUAL(0x20000, p->address);
TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_FACTORY, p->subtype);
esp_partition_iterator_t it = esp_partition_find(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, NULL);

View File

@ -202,6 +202,9 @@ const esp_partition_t *esp_partition_verify(const esp_partition_t *partition);
/**
* @brief Read data from the partition
*
* Partitions marked with an encryption flag will automatically be
* be read and decrypted via a cache mapping.
*
* @param partition Pointer to partition structure obtained using
* esp_partition_find_first or esp_partition_get.
* Must be non-NULL.
@ -250,7 +253,59 @@ esp_err_t esp_partition_read(const esp_partition_t* partition,
* or one of error codes from lower-level flash driver.
*/
esp_err_t esp_partition_write(const esp_partition_t* partition,
size_t dst_offset, const void* src, size_t size);
size_t dst_offset, const void* src, size_t size);
/**
* @brief Read data from the partition
*
* @note This function is essentially the same as \c esp_partition_write() above.
* It just never decrypts data but returns it as is.
*
* @param partition Pointer to partition structure obtained using
* esp_partition_find_first or esp_partition_get.
* Must be non-NULL.
* @param dst Pointer to the buffer where data should be stored.
* Pointer must be non-NULL and buffer must be at least 'size' bytes long.
* @param src_offset Address of the data to be read, relative to the
* beginning of the partition.
* @param size Size of data to be read, in bytes.
*
* @return ESP_OK, if data was read successfully;
* ESP_ERR_INVALID_ARG, if src_offset exceeds partition size;
* ESP_ERR_INVALID_SIZE, if read would go out of bounds of the partition;
* or one of error codes from lower-level flash driver.
*/
esp_err_t esp_partition_read_raw(const esp_partition_t* partition,
size_t src_offset, void* dst, size_t size);
/**
* @brief Write data to the partition without any transformation/encryption.
*
* @note This function is essentially the same as \c esp_partition_write() above.
* It just never encrypts data but writes it as is.
*
* Before writing data to flash, corresponding region of flash needs to be erased.
* This can be done using esp_partition_erase_range function.
*
* @param partition Pointer to partition structure obtained using
* esp_partition_find_first or esp_partition_get.
* Must be non-NULL.
* @param dst_offset Address where the data should be written, relative to the
* beginning of the partition.
* @param src Pointer to the source buffer. Pointer must be non-NULL and
* buffer must be at least 'size' bytes long.
* @param size Size of data to be written, in bytes.
*
* @note Prior to writing to flash memory, make sure it has been erased with
* esp_partition_erase_range call.
*
* @return ESP_OK, if data was written successfully;
* ESP_ERR_INVALID_ARG, if dst_offset exceeds partition size;
* ESP_ERR_INVALID_SIZE, if write would go out of bounds of the partition;
* or one of the error codes from lower-level flash driver.
*/
esp_err_t esp_partition_write_raw(const esp_partition_t* partition,
size_t dst_offset, const void* src, size_t size);
/**
* @brief Erase part of the partition

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,
size_t offset, size_t size)
{

View File

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

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