feat(nvs_flash): Added support for Block Data Layer storage provider

- BDL provider can be enabled vor NVS in the menuconfig option NVS_BDL_STACK
- Hierarchy of Partition class tree was adjusted and all operations were documented
- Class Partition is now derived from intrusive_list_node and ExceptionlessAllocable
- Class NVSPartition implements only the dual support for esp_partition and BDL
- Class NVSEncryptedPartition implements only encryption related extensions
This commit is contained in:
radek.tandler
2025-12-03 11:06:08 +01:00
parent 152e8768e1
commit 45c642e6d1
16 changed files with 595 additions and 133 deletions
+57 -30
View File
@@ -17,6 +17,7 @@
#include "esp_err.h"
#include <esp_rom_crc.h>
#include "nvs_internal.h"
#include "nvs_partition_lookup.hpp"
// Uncomment this line to force output from this module
// #define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
@@ -91,6 +92,7 @@ static esp_err_t close_handles_and_deinit(const char* part_name)
return NVSPartitionManager::get_instance()->deinit_partition(part_name);
}
#ifndef CONFIG_NVS_BDL_STACK
extern "C" esp_err_t nvs_flash_init_partition_ptr(const esp_partition_t *partition)
{
esp_err_t lock_result = Lock::init();
@@ -108,10 +110,12 @@ extern "C" esp_err_t nvs_flash_init_partition_ptr(const esp_partition_t *partiti
return ESP_ERR_NO_MEM;
}
const uint32_t sec_size = esp_partition_get_main_flash_sector_size();
uint32_t sec_size = NVS_CONST_PAGE_SIZE;
uint32_t size = part->get_size();
esp_err_t init_res = NVSPartitionManager::get_instance()->init_custom(part,
0,
partition->size / sec_size);
size / sec_size);
if (init_res != ESP_OK) {
delete part;
@@ -119,6 +123,46 @@ extern "C" esp_err_t nvs_flash_init_partition_ptr(const esp_partition_t *partiti
return init_res;
}
#else
extern "C" esp_err_t nvs_flash_init_partition_bdl(const char* partition_label, esp_blockdev_handle_t bdl)
{
esp_err_t lock_result = Lock::init();
if (lock_result != ESP_OK) {
return lock_result;
}
Lock lock;
if (partition_label == nullptr) {
return ESP_ERR_INVALID_ARG;
}
if (bdl == nullptr) {
return ESP_ERR_INVALID_ARG;
}
if (!NVSPartition::is_bdl_nvs_compliant(partition_label, bdl)) {
return ESP_ERR_NOT_SUPPORTED;
}
NVSPartition *part = new (std::nothrow) NVSPartition(partition_label, bdl, false);
if (part == nullptr) {
return ESP_ERR_NO_MEM;
}
uint32_t sec_size = NVS_CONST_PAGE_SIZE;
uint32_t size = part->get_size();
esp_err_t init_res = NVSPartitionManager::get_instance()->init_custom(part,
0,
size / sec_size);
if (init_res != ESP_OK) {
delete part;
}
return init_res;
}
#endif
#ifndef LINUX_HOST_LEGACY_TEST
extern "C" esp_err_t nvs_flash_init_partition(const char *part_name)
@@ -129,7 +173,6 @@ extern "C" esp_err_t nvs_flash_init_partition(const char *part_name)
}
Lock lock;
assert(nvs::Page::SEC_SIZE == esp_partition_get_main_flash_sector_size());
return NVSPartitionManager::get_instance()->init_partition(part_name);
}
@@ -170,7 +213,6 @@ extern "C" esp_err_t nvs_flash_secure_init_partition(const char *part_name, nvs_
}
Lock lock;
assert(nvs::Page::SEC_SIZE == esp_partition_get_main_flash_sector_size());
return NVSPartitionManager::get_instance()->secure_init_partition(part_name, cfg);
}
@@ -197,38 +239,23 @@ 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 == nullptr) {
return ESP_ERR_NOT_FOUND;
// reuse the partition lookup code to find the NVS partition to streamline the BDL and non-BDL code paths
nvs::Partition* part = nullptr;
esp_err_t err = nvs::partition_lookup::lookup_nvs_partition(part_name, &part);
if (err != ESP_OK || part == nullptr) {
return err;
}
return esp_partition_erase_range(partition, 0, partition->size);
// erase the partition
err = part->erase_range(0, part->get_size());
// No need to delete the partition here, as it is managed by the NVSPartitionManager.
return err;
}
extern "C" esp_err_t nvs_flash_erase_partition_ptr(const esp_partition_t *partition)
{
esp_err_t lock_result = Lock::init();
if (lock_result != ESP_OK) {
return lock_result;
}
Lock lock;
if (partition == nullptr) {
return ESP_ERR_INVALID_ARG;
}
// if the partition is initialized, uninitialize it first
if (NVSPartitionManager::get_instance()->lookup_storage_from_name(partition->label)) {
const esp_err_t err = close_handles_and_deinit(partition->label);
// only hypothetical/future case, deinit_partition() only fails if partition is uninitialized
if (err != ESP_OK) {
return err;
}
}
return esp_partition_erase_range(partition, 0, partition->size);
return nvs_flash_erase_partition(partition->label);
}
extern "C" esp_err_t nvs_flash_erase(void)
@@ -3,15 +3,20 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <cstring>
#include "nvs_encrypted_partition.hpp"
#include "nvs_types.hpp"
#include "nvs_constants.h"
namespace nvs {
#ifdef CONFIG_NVS_BDL_STACK
NVSEncryptedPartition::NVSEncryptedPartition(const char* label, const esp_blockdev_handle_t bdl, const bool managed_bdl)
: NVSPartition(label, bdl, managed_bdl) { }
#else
NVSEncryptedPartition::NVSEncryptedPartition(const esp_partition_t *partition)
: NVSPartition(partition) { }
#endif // CONFIG_NVS_BDL_STACK
esp_err_t NVSEncryptedPartition::init(nvs_sec_cfg_t* cfg)
{
@@ -33,19 +38,20 @@ esp_err_t NVSEncryptedPartition::init(nvs_sec_cfg_t* cfg)
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;
// 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.
// here we make sure that the size is really compliant with the minimal encryption block size.
if (size % NVS_ENCRYPT_BLOCK_SIZE != 0) return ESP_ERR_INVALID_SIZE;
// read data
esp_err_t read_result = esp_partition_read(mESPPartition, src_offset, dst, size);
esp_err_t read_result = NVSPartition::read(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];
uint8_t data_unit[NVS_ENCRYPT_BLOCK_SIZE];
uint32_t relAddr = src_offset;
@@ -64,7 +70,7 @@ esp_err_t NVSEncryptedPartition::read(size_t src_offset, void* dst, size_t size)
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;
if (size % NVS_ENCRYPT_BLOCK_SIZE != 0) return ESP_ERR_INVALID_SIZE;
// copy data to buffer for encryption
uint8_t* buf = new (std::nothrow) uint8_t [size];
@@ -77,7 +83,7 @@ esp_err_t NVSEncryptedPartition::write(size_t addr, const void* src, size_t size
uint8_t entrySize = sizeof(Item);
//sector num required as an arr by mbedtls. Should have been just uint64/32.
uint8_t data_unit[16];
uint8_t data_unit[NVS_ENCRYPT_BLOCK_SIZE];
/* Use relative address instead of absolute address (relocatable), so that host-generated
* encrypted nvs images can be used*/
@@ -103,7 +109,7 @@ esp_err_t NVSEncryptedPartition::write(size_t addr, const void* src, size_t size
}
// write data
esp_err_t result = esp_partition_write(mESPPartition, addr, buf, size);
esp_err_t result = NVSPartition::write(addr, buf, size);
delete [] buf;
@@ -5,27 +5,70 @@
*/
#pragma once
#include "sdkconfig.h" // For CONFIG_NVS_BDL_STACK
#include "mbedtls/aes.h" // For mbedtls_aes_xts_context
#include "nvs_flash.h" // For nvs_sec_cfg_t
#include "nvs_partition.hpp"
namespace nvs {
/**
* Implementation of encrypted esp_partition or bdl storage wrapper for NVS.
*/
class NVSEncryptedPartition : public NVSPartition {
public:
#ifdef CONFIG_NVS_BDL_STACK
NVSEncryptedPartition(const char* label, const esp_blockdev_handle_t bdl, const bool managed_bdl = true);
#else
NVSEncryptedPartition(const esp_partition_t *partition);
#endif
virtual ~NVSEncryptedPartition() { }
/**
* Initializes the AES encryption components with the provided configuration.
*
* @param cfg the configuration for the AES encryption
*
* @return
* - ESP_OK on success
* - other error codes from the AES component initialization
* - ESP_ERR_NVS_XTS_CFG_FAILED if the AES key configuration is invalid
*/
esp_err_t init(nvs_sec_cfg_t* cfg);
/**
* Reads and decrypts the data from the storage
* This function is intended for reading data aligned to the size of NVS entry.
*
* @param src_offset the offset in the storage to read from
* @param dst an already allocated buffer to read data into
* @param size the size of the data to read in bytes
*
* @return
* - ESP_OK on success
* - other error codes from the esp_partition or BDL API
*/
esp_err_t read(size_t src_offset, void* dst, size_t size) override;
/**
* Encrypts the data from the src buffer and writes it to the storage.
* This function is intended for writing data aligned to the size of NVS entry.
* Repeated writes to the same address are not expected to be called.
*
* @param dst_offset the offset in the storage to write to
* @param src pointer to the buffer to write data from
* @param size the size of the data to write in bytes
*
* @return
* - ESP_OK on success
* - other error codes from the esp_partition or BDL API
*/
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;
mbedtls_aes_xts_context mEctxt; // AES context for encryption
mbedtls_aes_xts_context mDctxt; // AES context for decryption
};
} // nvs
+152 -10
View File
@@ -3,12 +3,20 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <cstdlib>
#include "nvs_partition.hpp"
#include <cstdlib>
#include "esp_log.h"
#include "nvs_constants.h" // For NVS_CONST_PAGE_SIZE
#ifdef CONFIG_NVS_BDL_STACK
#include "esp_partition.h" // For esp_blockdev_handle_t
#endif // CONFIG_NVS_BDL_STACK
#define TAG "NVSPartition"
namespace nvs {
#ifndef CONFIG_NVS_BDL_STACK
NVSPartition::NVSPartition(const esp_partition_t* partition)
: mESPPartition(partition)
{
@@ -18,6 +26,11 @@ NVSPartition::NVSPartition(const esp_partition_t* partition)
}
}
NVSPartition::~NVSPartition()
{
// No need to de-initialize mESPPartition here, if you used esp_partition_find_first.
}
const char *NVSPartition::get_partition_name()
{
return mESPPartition->label;
@@ -30,10 +43,6 @@ esp_err_t NVSPartition::read_raw(size_t src_offset, void* dst, size_t 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);
}
@@ -44,10 +53,6 @@ esp_err_t NVSPartition::write_raw(size_t dst_offset, const void* src, size_t siz
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);
}
@@ -71,4 +76,141 @@ bool NVSPartition::get_readonly()
return mESPPartition->readonly;
}
#else // CONFIG_NVS_BDL_STACK
// BDL implementation of NVSPartition
NVSPartition::NVSPartition(const char* label, const esp_blockdev_handle_t bdl, const bool managed_bdl)
: mBDL(bdl), mManagedBDLbyInstance(managed_bdl)
{
if (bdl == nullptr || label == nullptr) {
std::abort();
}
strlcpy(mLabel, label, NVS_PART_NAME_MAX_SIZE);
mLabel[NVS_PART_NAME_MAX_SIZE] = '\0';
}
NVSPartition::~NVSPartition()
{
if(mManagedBDLbyInstance) {
// If the BDL was managed by this instance, we need to de-initialize it.
if (mBDL != nullptr) {
mBDL->ops->release(mBDL);
}
}
}
const char* NVSPartition::get_partition_name()
{
return mLabel;
}
esp_err_t NVSPartition::read_raw(size_t src_offset, void* dst, size_t size)
{
return mBDL->ops->read(mBDL, (uint8_t*) dst, size, src_offset, size);
}
esp_err_t NVSPartition::read(size_t src_offset, void* dst, size_t size)
{
return mBDL->ops->read(mBDL, (uint8_t*) dst, size, src_offset, size);
}
esp_err_t NVSPartition::write_raw(size_t dst_offset, const void* src, size_t size)
{
return mBDL->ops->write(mBDL, (const uint8_t*) src, dst_offset, size);
}
esp_err_t NVSPartition::write(size_t dst_offset, const void* src, size_t size)
{
return mBDL->ops->write(mBDL, (const uint8_t*) src, dst_offset, size);
}
esp_err_t NVSPartition::erase_range(size_t dst_offset, size_t size)
{
return mBDL->ops->erase(mBDL, dst_offset, size);
}
uint32_t NVSPartition::get_address()
{
// BDL cannot be used directly to get the address, as it is not a partition in the traditional sense.
ESP_LOGE(TAG, "get_address() is not supported");
std::abort();
return 0;
}
uint32_t NVSPartition::get_size()
{
return mBDL->geometry.disk_size;
}
bool NVSPartition::get_readonly()
{
return mBDL->device_flags.read_only;
}
bool NVSPartition::is_bdl_nvs_compliant(const char* label, const esp_blockdev_handle_t bdl)
{
if (bdl == nullptr) {
ESP_LOGV(TAG, "Block device layer handle is null for label %s", label);
return false;
}
bool is_compliant = true;
if(label == nullptr) {
ESP_LOGV(TAG, "Label is null, cannot check compliance");
return false;
}
if(strlen(label) >= NVS_PART_NAME_MAX_SIZE) {
ESP_LOGV(TAG, "Label %s is too long, it must be less than %d characters", label, NVS_PART_NAME_MAX_SIZE);
is_compliant = false;
}
if(bdl->geometry.read_size != 1){
ESP_LOGV(TAG, "Block device read size is not 1, it is %zu", bdl->geometry.read_size);
is_compliant = false;
}
if(bdl->geometry.write_size != 1) {
ESP_LOGV(TAG, "Block device write size is not 1, it is %zu", bdl->geometry.write_size);
is_compliant = false;
}
if(bdl->geometry.erase_size % NVS_CONST_PAGE_SIZE != 0) {
ESP_LOGV(TAG, "Block device erase size is not a multiple of %d, it is %zu", NVS_CONST_PAGE_SIZE, bdl->geometry.erase_size);
is_compliant = false;
}
if(bdl->geometry.disk_size % NVS_CONST_PAGE_SIZE != 0) {
ESP_LOGV(TAG, "Block device size is not a multiple of %d, it is %zu", NVS_CONST_PAGE_SIZE, bdl->geometry.disk_size);
is_compliant = false;
}
if(bdl->ops->read == nullptr) {
ESP_LOGV(TAG, "Block device read operation is not present");
is_compliant = false;
}
if(bdl->ops->write == nullptr) {
ESP_LOGV(TAG, "Block device write operation is not present");
is_compliant = false;
}
if(bdl->ops->erase == nullptr) {
ESP_LOGV(TAG, "Block device erase operation is not present");
is_compliant = false;
}
if(bdl->device_flags.default_val_after_erase != 1) {
ESP_LOGV(TAG, "Block device default value after erase has to be 1, it is %d", bdl->device_flags.default_val_after_erase);
is_compliant = false;
}
if(bdl->device_flags.encrypted) {
ESP_LOGV(TAG, "Block device is encrypted, NVS does not support encrypted BDLs");
is_compliant = false;
}
return is_compliant;
}
#endif // CONFIG_NVS_BDL_STACK
} // nvs
+116 -34
View File
@@ -5,106 +5,188 @@
*/
#pragma once
#include "esp_partition.h"
#include "intrusive_list.h"
#include "sdkconfig.h" // For CONFIG_NVS_BDL_STACK
#include "partition.hpp"
#include "nvs_memory_management.hpp"
#define ESP_ENCRYPT_BLOCK_SIZE 16
#define PART_NAME_MAX_SIZE 16 /*!< maximum length of partition name (excluding null terminator) */
#ifdef CONFIG_NVS_BDL_STACK
#include "esp_blockdev.h" // For esp_blockdev_handle_t
#include "nvs.h" // For NVS_PART_NAME_MAX_SIZE
#else
#include "esp_partition.h"
#endif // CONFIG_NVS_BDL_STACK
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.
* Implementation of non-encrypted esp_partition or bdl storage wrapper for NVS.
*/
class NVSPartition : public Partition, public intrusive_list_node<NVSPartition>, public ExceptionlessAllocatable {
class NVSPartition : public Partition {
public:
#ifdef CONFIG_NVS_BDL_STACK
/**
* Copy partition_name to mPartitionName and initialize mESPPartition.
* Constructor copies storage name to mLabel and initializes BDL reference.
* See function is_bdl_nvs_compliant for the requirements on the BDL.
*
* @param partition_name the name of the partition as in the partition table, must be non-NULL!
* @param partition an already initialized partition structure
* @param label the label of the partition corresponding to the referenced BDL handle.
* @param bdl an already initialized bdl handle
* @param managed_bdl if true, the BDL handle will be released in the destructor of this class.
*/
NVSPartition(const char* label, const esp_blockdev_handle_t bdl, const bool managed_bdl = true);
/**
* Function allowing to check if the block device is compliant with NVS requirements.
* This static function shall be called prior to creating an instance of NVSPartition.
* Following checks are performed:
* 1. The block device handle must not be null
* 2. The block device label must not be null
* 3. The block device label must fit to the statically allocated buffer (NVS_PART_NAME_MAX_SIZE)
* 4. The block device read block size must be 1
* 5. The block device write block size must be 1
* 6. The block device erase block size must be a multiple of NVS_CONST_PAGE_SIZE (NVS_CONST_PAGE_SIZE is 4kB)
* 7. The size of the block device must be a multiple of the NVS_CONST_PAGE_SIZE (NVS_CONST_PAGE_SIZE is 4kB)
* 8. BDL operations read / write / erase must be supported
* 9. The block device write operation type must be at least NOR flash compliant. Regular write, or write setting to zero are supported.
* 10. The block device must not be encrypted
*
* @param label the label of the storage corresponding to the referenced BDL handle.
* @param bdl an already initialized bdl handle
*
* @return true if the block device is NVS compliant and can be used for NVS operations, false otherwise.
*/
static bool is_bdl_nvs_compliant(const char* label, const esp_blockdev_handle_t bdl);
#else
/**
* Constructor initializes the mPartition.
*
* @param partition an already initialized esp_partition structure
*/
NVSPartition(const esp_partition_t* partition);
#endif
/**
* No need to de-initialize mESPPartition here, if you used esp_partition_find_first.
* Otherwise, the user is responsible for de-initializing it.
* Destructor cleans up the wrapper.
* If BDL is used and constructor was called with managed_bdl = true,
* the BDL handle will be released.
*/
virtual ~NVSPartition() { }
virtual ~NVSPartition();
/**
* Return the name (label) of the storage.
* In esp_partition implementation it returns the partition label.
* In BDL implementation it returns the BDL label handed over to the constructor.
*
* @return
* - the label of the storage
*/
const char *get_partition_name() override;
/**
* Look into \c esp_partition_read_raw for more details.
* Reads data from the storage.
* Raw read is used for reading data aligned down to the size of 1 byte
*
* @param src_offset the offset in the storage to read from
* @param dst an already allocated buffer to read data into
* @param size the size of the data to read in bytes
*
* @return
* - ESP_OK on success
* - other error codes from the esp_partition API
* - other error codes from the esp_partition or BDL 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.
* Reads data from the storage
* This function is intended for reading data aligned to the size of NVS entry.
* This, not encrypted, implementation does not require the size to be aligned to the NVS entry size though.
*
* @param src_offset the offset in the storage to read from
* @param dst an already allocated buffer to read data into
* @param size the size of the data to read in bytes
*
* @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
* - other error codes from the esp_partition or BDL 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.
* Writes data from the src buffer to the storage.
* Raw write is used for writing data aligned down to the size of 1 byte
* Even repeated writes to the same address can be called.
*
* @param dst_offset the offset in the storage to write to
* @param src pointer to the buffer to write data from
* @param size the size of the data to write in bytes
*
* @return
* - ESP_OK on success
* - error codes from the esp_partition API
* - other error codes from the esp_partition or BDL 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.
* Writes data from the src buffer to the storage.
* This function is intended for writing data aligned to the size of NVS entry.
* This, not encrypted, implementation does not require the size to be aligned to the NVS entry size though.
* Repeated writes to the same address are not expected to be called.
*
* @param dst_offset the offset in the storage to write to
* @param src pointer to the buffer to write data from
* @param size the size of the data to write in bytes
*
* @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
* - other error codes from the esp_partition or BDL 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.
* Erases the address range in the storage.
* Erased data is set to 0xFF.
*
* @param dst_offset the offset in the storage to erase
* @param size the size of the data to erase in bytes
*
* @return
* - ESP_OK on success
* - error codes from the esp_partition API
* - other error codes from the esp_partition or BDL API
*/
esp_err_t erase_range(size_t dst_offset, size_t size) override;
/**
* @return the base address of the partition.
* Returns the RAM address of the beginning of the memory mapped storage.
* Not available if the block device layer is enabled.
*
* @return
* - ESP_OK on success
* - other error codes from the esp_partition or BDL API
*/
uint32_t get_address() override;
/**
* @return the size of the partition in bytes.
* Returns total size of the storage in bytes.
*
* @return size of the storage in bytes.
*/
uint32_t get_size() override;
/**
* @return true if the partition is read-only.
* Returns the read-only status of the storage.
*
* @return true if the storage is read-only.
*/
bool get_readonly() override;
protected:
const esp_partition_t* mESPPartition;
#ifdef CONFIG_NVS_BDL_STACK
esp_blockdev_handle_t mBDL; // Block Device Layer handle for the storage
char mLabel[NVS_PART_NAME_MAX_SIZE + 1]; // Label of the storage, used for matching in NVS open operations
bool mManagedBDLbyInstance; // True if the BDL is managed by this instance, false if it is managed externally
#else
const esp_partition_t* mESPPartition; // Pointer to the ESP partition structure for the storage
#endif // CONFIG_NVS_BDL_STACK
};
} // nvs
@@ -11,18 +11,17 @@
#endif // ! LINUX_TARGET
namespace nvs {
namespace partition_lookup {
esp_err_t lookup_nvs_partition(const char* label, NVSPartition **p)
esp_err_t lookup_nvs_partition(const char* label, Partition **p)
{
#ifndef CONFIG_NVS_BDL_STACK
const esp_partition_t* esp_partition = esp_partition_find_first(
ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, label);
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;
}
@@ -33,15 +32,36 @@ esp_err_t lookup_nvs_partition(const char* label, NVSPartition **p)
}
*p = partition;
return ESP_OK;
#else
esp_blockdev_handle_t bdl = NULL;
esp_err_t err = esp_partition_get_blockdev(
ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, label, &bdl);
if (err != ESP_OK) {
return ESP_ERR_NOT_FOUND;
}
if (!NVSPartition::is_bdl_nvs_compliant(label, bdl)) {
if(bdl->ops->release) bdl->ops->release(bdl);
return ESP_ERR_NVS_PART_NOT_FOUND;
}
NVSPartition *partition = new (std::nothrow) NVSPartition(label, bdl);
if (partition == nullptr) {
return ESP_ERR_NO_MEM;
}
*p = partition;
return ESP_OK;
#endif
}
#ifndef LINUX_TARGET
esp_err_t lookup_nvs_encrypted_partition(const char* label, nvs_sec_cfg_t* cfg, NVSPartition **p)
esp_err_t lookup_nvs_encrypted_partition(const char* label, nvs_sec_cfg_t* cfg, Partition **p)
{
#ifndef CONFIG_NVS_BDL_STACK
const esp_partition_t* esp_partition = esp_partition_find_first(
ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, label);
ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, label);
if (esp_partition == nullptr) {
return ESP_ERR_NOT_FOUND;
@@ -51,23 +71,47 @@ esp_err_t lookup_nvs_encrypted_partition(const char* label, nvs_sec_cfg_t* cfg,
return ESP_ERR_NVS_WRONG_ENCRYPTION;
}
NVSEncryptedPartition *enc_p = new (std::nothrow) NVSEncryptedPartition(esp_partition);
if (enc_p == nullptr) {
NVSEncryptedPartition *partition = new (std::nothrow) NVSEncryptedPartition(esp_partition);
if (partition == nullptr) {
return ESP_ERR_NO_MEM;
}
esp_err_t result = enc_p->init(cfg);
esp_err_t result = partition->init(cfg);
if (result != ESP_OK) {
delete enc_p;
delete partition;
return result;
}
*p = enc_p;
*p = partition;
return ESP_OK;
#else
esp_blockdev_handle_t bdl = NULL;
esp_err_t err = esp_partition_get_blockdev(
ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, label, &bdl);
if (err != ESP_OK) {
return ESP_ERR_NOT_FOUND;
}
if (!NVSPartition::is_bdl_nvs_compliant(label, bdl)) {
if(bdl->ops->release) bdl->ops->release(bdl);
return ESP_ERR_NVS_PART_NOT_FOUND;
}
NVSEncryptedPartition *partition = new (std::nothrow) NVSEncryptedPartition(label, bdl);
if (partition == nullptr) {
return ESP_ERR_NO_MEM;
}
esp_err_t result = partition->init(cfg);
if (result != ESP_OK) {
delete partition;
return result;
}
*p = partition;
return ESP_OK;
#endif
}
#endif // ! LINUX_TARGET
#endif // !LINUX_TARGET
} // partition_lookup
} // nvs
} // namespace partition_lookup
} // namespace nvs
@@ -13,9 +13,9 @@ namespace nvs {
namespace partition_lookup {
esp_err_t lookup_nvs_partition(const char* label, NVSPartition **p);
esp_err_t lookup_nvs_partition(const char* label, Partition **p);
esp_err_t lookup_nvs_encrypted_partition(const char* label, nvs_sec_cfg_t* cfg, NVSPartition **p);
esp_err_t lookup_nvs_encrypted_partition(const char* label, nvs_sec_cfg_t* cfg, Partition **p);
} // partition_lookup
@@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_partition.h"
#include "nvs_partition_manager.hpp"
#include "nvs_partition_lookup.hpp"
#include "nvs_internal.h"
@@ -27,15 +27,12 @@ NVSPartitionManager* NVSPartitionManager::get_instance()
return 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;
const uint32_t sec_size = esp_partition_get_main_flash_sector_size();
Storage* mStorage;
mStorage = lookup_storage_from_name(partition_label);
@@ -43,17 +40,20 @@ esp_err_t NVSPartitionManager::init_partition(const char *partition_label)
return ESP_OK;
}
NVS_ASSERT_OR_RETURN(sec_size != 0, ESP_FAIL);
NVSPartition *p = nullptr;
Partition *p = nullptr;
esp_err_t result = partition_lookup::lookup_nvs_partition(partition_label, &p);
if (result != ESP_OK) {
return result;
}
uint32_t size = p->get_size();
uint32_t sec_size = NVS_CONST_PAGE_SIZE;
if (result != ESP_OK) {
goto error;
}
size = p->get_size();
result = init_custom(p, 0, size / sec_size);
if (result != ESP_OK) {
goto error;
@@ -67,7 +67,6 @@ error:
delete p;
return result;
}
#endif // ESP_PLATFORM
esp_err_t NVSPartitionManager::init_custom(Partition *partition, uint32_t baseSector, uint32_t sectorCount)
{
@@ -117,7 +116,7 @@ esp_err_t NVSPartitionManager::secure_init_partition(const char *part_name, nvs_
return ESP_OK;
}
NVSPartition *p;
Partition *p;
esp_err_t result;
if (cfg != nullptr) {
result = partition_lookup::lookup_nvs_encrypted_partition(part_name, cfg, &p);
@@ -130,7 +129,7 @@ esp_err_t NVSPartitionManager::secure_init_partition(const char *part_name, nvs_
}
uint32_t size = p->get_size();
const uint32_t sec_size = esp_partition_get_main_flash_sector_size();
uint32_t sec_size = NVS_CONST_PAGE_SIZE;
result = init_custom(p, 0, size / sec_size);
if (result != ESP_OK) {
@@ -165,7 +164,7 @@ esp_err_t NVSPartitionManager::deinit_partition(const char *partition_label)
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;
Partition *p = it;
nvs_partition_list.erase(it);
delete p;
break;
@@ -6,9 +6,7 @@
#pragma once
#include "nvs_handle_simple.hpp"
#include "nvs_storage.hpp"
#include "nvs_partition.hpp"
#include "nvs_memory_management.hpp"
#include "partition.hpp"
#include "nvs_flash.h"
namespace nvs {
@@ -44,7 +42,7 @@ protected:
intrusive_list<nvs::Storage> nvs_storage_list;
intrusive_list<nvs::NVSPartition> nvs_partition_list;
intrusive_list<nvs::Partition> nvs_partition_list;
};
} // nvs
+89 -7
View File
@@ -6,45 +6,127 @@
#pragma once
#include "esp_err.h"
#include "intrusive_list.h"
#include "nvs_memory_management.hpp"
namespace nvs {
/**
* @brief Abstract interface for partition related operations, currently in NVS.
* @brief Abstract interface for permanent storage (partition) related operations, currently in NVS.
*
* It resembles the main operations according to esp_partition.h.
* It resembles the main operations required by NVS to operate on permanent storage.
* It is implemented as an intrusive_list_node to easily store instances of it.
* It also allows to create instances of this class without throwing exceptions using custom heap allocator
*/
class Partition {
class Partition : public intrusive_list_node<Partition>, public ExceptionlessAllocatable {
public:
virtual ~Partition() { }
/**
* Return the partition name as in the partition table.
* Return the name (label) of the storage.
*
* @return
* - the name of the storage, e.g. "nvs".
*/
virtual const char *get_partition_name() = 0;
/**
* Reads data from the storage.
* Raw read is used for reading data aligned down to the size of 1 byte
* No decryption is applied.
*
* @param src_offset the offset in the storage to read from
* @param dst an already allocated buffer to read data into
* @param size the size of the data to read in bytes
*
* @return
* - ESP_OK on success
* - other error codes from the implementation of the storage
*/
virtual esp_err_t read_raw(size_t src_offset, void* dst, size_t size) = 0;
/**
* Reads data from the storage
* This function is intended for reading data aligned to the size of NVS entry.
* Implementations of this function may leverage the minimal size to read efficiently.
* For implementation with encryption, this function will decrypt the data.
*
* @param src_offset the offset in the storage to read from
* @param dst an already allocated buffer to read data into
* @param size the size of the data to read in bytes
*
* @return
* - ESP_OK on success
* - other error codes from the implementation of the storage
*/
virtual esp_err_t read(size_t src_offset, void* dst, size_t size) = 0;
/**
* Writes data from the src buffer to the storage.
* Raw write is used for writing data aligned down to the size of 1 byte
* Repeated writes to the same address can be called thus no encryption is applied.
*
* @param dst_offset the offset in the storage to write to
* @param src pointer to the buffer to write data from
* @param size the size of the data to write in bytes
*
* @return
* - ESP_OK on success
* - other error codes from the implementation of the storage
*/
virtual esp_err_t write_raw(size_t dst_offset, const void* src, size_t size) = 0;
/**
* Writes data from the src buffer to the storage.
* This function is intended for writing data aligned to the size of NVS entry.
* This, not encrypted, implementation does not require the size to be aligned to the NVS entry size though.
* Repeated writes to the same address are not expected to be called.
* For implementation with encryption, this function will encrypt the data.
*
* @param dst_offset the offset in the storage to write to
* @param src pointer to the buffer to write data from
* @param size the size of the data to write in bytes
*
* @return
* - ESP_OK on success
* - other error codes from the implementation of the storage
*/
virtual esp_err_t write(size_t dst_offset, const void* src, size_t size) = 0;
/**
* Erases the address range in the storage.
* Erased data is set to 0xFF.
*
* @param dst_offset the offset in the storage to erase
* @param size the size of the data to erase in bytes
*
* @return
* - ESP_OK on success
* - other error codes from the implementation of the storage
*/
virtual esp_err_t erase_range(size_t dst_offset, size_t size) = 0;
/**
* Return the address of the beginning of the partition.
* Returns the RAM address of the beginning of the memory mapped storage.
* Not available if the block device layer is enabled.
*
* @return
* - ESP_OK on success
* - other error codes from the implementation of the storage
*/
virtual uint32_t get_address() = 0;
/**
* Return the partition size in bytes.
* Returns total size of the storage in bytes.
*
* @return size of the storage in bytes.
*/
virtual uint32_t get_size() = 0;
/**
* Return true if the partition is read-only.
* Returns the read-only status of the storage.
*
* @return true if the storage is read-only.
*/
virtual bool get_readonly() = 0;
};