# 在引导程序中使用 NVS

[English]

## 概述

本指南概述了可用于自定义引导加载程序代码的 NVS（非易失性存储）功能及其限制。

由于引导加载程序运行环境的限制，自定义引导加载程序代码无法直接使用完整的 NVS API。为此，NVS 提供了一个简化 API，仅支持只读访问 NVS 数据。

该 API 支持读取除 blob 之外的所有 NVS 数据类型。一次 API 调用可以同时读取多个 NVS 条目。可以从同一 NVS 分区的不同命名空间读取值，读取结果存储在输入/输出结构数组中，每个数据项的固定大小最多为 8 字节。

由于引导加载程序中的堆内存分配限制，读取字符串条目时，API 要求调用者提供缓冲区及其大小。

### 读取加密的 NVS 分区

如果 NVS 分区使用 NVS 加密 指南中的加密方案，该 API 还支持解密 NVS 数据。

应用程序应按照以下步骤使用 `nvs_bootloader_read()` API 读取加密 NVS 分区数据：

> 1. 根据所选的 NVS 加密方案填充 NVS 安全配置结构 `nvs_sec_cfg_t` （详情请参阅 NVS 加密）。

> 2. 使用 `nvs_bootloader_read_security_cfg()` API 从指定的安全方案中读取 NVS 安全配置。

> 3. 获取安全配置后，使用 `nvs_bootloader_secure_init()` API 初始化 NVS flash 分区。

> 4. 使用 `nvs_bootloader_read()` API 执行 NVS 读取操作。

> 5. 使用 `nvs_bootloader_secure_deinit()` API 反初始化 NVS flash 分区，并清除安全配置。

备注

在使用基于 HMAC 的方案进行上述流程时，可以直接调用 `nvs_flash_secure_init()` API 对默认和自定义 NVS 分区进行加密，而无需启用 NVS 加密相关配置选项（如 CONFIG_NVS_ENCRYPTION， CONFIG_NVS_SEC_KEY_PROTECTION_SCHEME -> `CONFIG_NVS_SEC_KEY_PROTECT_USING_HMAC`， CONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID）。

## 应用示例

代码示例请参阅 ESP-IDF 示例 storage/nvs 目录下的 storage/nvs/nvs_bootloader。

本节演示了如何在输入/输出结构中准备数据，以支持不同的数据类型、命名空间和键。此外，还包含从 NVS 读取字符串数据的示例。

示例还演示了如何检查读取操作是否成功，数据是否存在不一致，或是否在 NVS 中找不到某些值的情形。该示例能够将 API 返回的值（或错误码）打印到控制台。

## API 参考

### Header File

- components/nvs_flash/include/nvs_bootloader.h

- This header file can be included with:

  >     #include "nvs_bootloader.h"

- This header file is a part of the API provided by the `nvs_flash` component. To declare that your component depends on `nvs_flash`, add the following to your CMakeLists.txt:

  >     REQUIRES nvs_flash

  > or

  >     PRIV_REQUIRES nvs_flash

### Functions

esp_err_t nvs_bootloader_read(const char *partition_name, const size_t read_list_count, nvs_bootloader_read_list_t read_list[])

  Reads data specified from the specified NVS partition.

  This function reads data from the NVS partition specified by `partition_name`. Multiple NVS entries can be read in a single call. The list of entries to read is specified in the `read_list` array. Function indicates overall success or failure by its return value. In case it is ESP_OK or ESP_ERR_INVALID_ARG, result of validation / reading of individual entry is returned in the `result_code` member of each element of the `read_list` array.

  参数:

    - partition_name -- The name of the NVS partition to read from.

    - read_list_count -- The number of elements in the `read_list` array.

    - read_list -- An array of `nvs_bootloader_read_list_t` structures specifying the keys and buffers for reading data.

  返回:

    The return value of the function in this file can be one of the following:

    - `ESP_OK`: The function successfully checked all input parameters and executed successfully. The individual `result_code` in `read_list` indicates the result of the lookup for a particular requested key.

    - `ESP_ERR_INVALID_ARG`: The validity of all `read_list` input parameters was checked and failed for at least one of the parameters. The individual `result_code` in `read_list` provides the detailed reason. This error code is also returned when read_list is null or read_list_count is 0.

    - `ESP_ERR_NVS_INVALID_NAME`: The partition name specified is too long or is null.

    - `ESP_ERR_NVS_PART_NOT_FOUND`: The partition was not found in the partition table.

    - `ESP_ERR_NVS_CORRUPT_KEY_PART`: Encryption-related problems.

    - `ESP_ERR_NVS_WRONG_ENCRYPTION`: Encryption-related problems.

    - `ESP_ERR_INVALID_STATE`: NVS partition or pages related errors - wrong size of partition, header inconsistent / entries inconsistencies, multiple active pages, page in state INVALID.

    - `ESP_ERR_NO_MEM`: Cannot allocate memory required to perform the function.

    - Technical errors in underlying storage.

esp_err_t nvs_bootloader_secure_init(const nvs_sec_cfg_t *sec_cfg)

  Initialize internal NVS security context, thus, enabling the NVS bootloader read API to decrypt encrypted NVS partitions.

  备注

  Once `nvs_bootloader_secure_init()` is performed, `nvs_bootloader_read()` can correctly read only those NVS partitions that are encrypted using the given `nvs_sec_cfg_t` security config, until `nvs_bootloader_secure_deinit()` clears the internal NVS security context.

  参数:

    sec_cfg -- NVS security key that would be used for decrypting the NVS partition

  返回:

    ESP_OK if security initialization is successful

void nvs_bootloader_secure_deinit(void)

  Clear the internal NVS security context.

esp_err_t nvs_bootloader_read_security_cfg(nvs_sec_scheme_t *scheme_cfg, nvs_sec_cfg_t *cfg)

  Reads NVS bootloader security configuration set by the specified security scheme.

  参数:

    - scheme_cfg -- [in] Security scheme specific configuration

    - cfg -- [out] Security configuration (encryption keys)

  返回:

    - ESP_OK, if cfg was read successfully;

    - ESP_ERR_INVALID_ARG, if scheme_cfg or cfg is NULL;

    - ESP_FAIL, if the key reading process fails

### Unions

union nvs_bootloader_value_placeholder_t
  #include <nvs_bootloader.h>

  Union of value placeholders for all nvs_type_t supported by bootloader code.

  Public Members

  uint8_t u8_val

    Placeholder for unsigned 8 bit integer variable

  int8_t i8_val

    Placeholder for signed 8 bit integer variable

  uint16_t u16_val

    Placeholder for unsigned 16 bit integer variable

  int16_t i16_val

    Placeholder for signed 16 bit integer variable

  uint32_t u32_val

    Placeholder for unsigned 32 bit integer variable

  int32_t i32_val

    Placeholder for signed 32 bit integer variable

  uint64_t u64_val

    Placeholder for unsigned 64 bit integer variable

  int64_t i64_val

    Placeholder for signed 64 bit integer variable

  nvs_bootloader_str_value_placeholder_t str_val

    Placeholder for string buffer information

### Structures

struct nvs_bootloader_str_value_placeholder_t

  Placeholders for buffer pointer and length of string type.

  Public Members

  char *buff_ptr

    Pointer to the buffer where string and terminating zero character will be read

  size_t buff_len

    Buffer length in bytes

struct nvs_bootloader_read_list_t

  Structure representing one NVS bootloader entry.

  This structure serves as read operation input parameters and result value and status placeholder. Before calling the `nvs_bootloader_read` function, populate the namespace_name, key_name and value_type members. If string value has to be read, provide also buffer and its length in the `value.str_val` member.

  The result_code member will be populated by the function with the result of the read operation. There are 2 possible situations and interpretations of the result_code: If the return value of the `nvs_bootloader_read` was ESP_OK, the result_code will be one of the following:

  - `ESP_OK`: Entry found, value member contains the data. This is the only case when the value member is populated.

  - `ESP_ERR_NVS_TYPE_MISMATCH`: Entry was found, but requested datatype doesn't match datatype found in NVS

  - `ESP_ERR_NVS_NOT_FOUND`: Data was not found.

  - `ESP_ERR_INVALID_SIZE`: the value found for string is longer than the space provided in placeholder (str_val.buff_len) If the return value of the function was ESP_ERR_INVALID_ARG, the result_code will be one of the following:

  - `ESP_ERR_NVS_NOT_FOUND`: Check of this parameters was successful.

  - `ESP_ERR_NVS_INVALID_NAME`: namespace_name is NULL or too long

  - `ESP_ERR_NVS_KEY_TOO_LONG`: key_name NULL or too long

  - `ESP_ERR_INVALID_SIZE`: the size of the buffer provided for NVS_TYPE_STR in placeholder (str_val.buff_len) is zero or exceeds maximum value NVS_CONST_STR_LEN_MAX_SIZE

  - `ESP_ERR_INVALID_ARG`: Invalid datatype requested

  Public Members

  const char *namespace_name

    Namespace of the entry

  const char *key_name

    Key of the entry

  nvs_type_t value_type

    Expected datatype to be read, can be any of NVS_TYPE_U*, NVS_TYPE_I* or NVS_TYPE_STR

  esp_err_t result_code

    Result code of this entry. Explanation is in general description of the struct nvs_bootloader_read_list_t

  nvs_bootloader_value_placeholder_t value

    Placeholder for value read

  uint8_t namespace_index

    Index of the namespace (internal variable, do not use)

此文档对您有帮助吗？

- 反馈已收到，谢谢！
- 如果您有其他意见，欢迎填写乐鑫文档反馈表。

- 我们重视您的反馈。
- 您可以填写乐鑫文档反馈表告诉我们如何改进该文档。