uint32_t zh_set_sn(uint8_t *buf_sn, uint32_t len_sn)
{
-
+ errcode_t nv_ret_value=0;
#ifdef SUPPORT_NV
if( buf_sn == NULL || len_sn == 0)
return ERRCODE_FAIL;
+ uint8_t *test_nv_value; /* 要写入的NV value保存在test_nv_value中 */
+
+ uint16_t key_len= sizeof(custom_key_sn_t);
+ uint8_t *write_value = malloc(key_len);
+ custom_key_sn_t *p_sn = (custom_key_sn_t*)write_value;
uint16_t len = 0;
- custom_key_sn_t sn = {0};
- if (len_sn > sizeof(sn.sn))
+ if (len_sn > sizeof(p_sn->sn))
{
- len = sizeof(sn.sn);
+ len = sizeof(p_sn->sn);
}else{
len = len_sn;
}
- sn.size = len;
- memcpy_s(sn.sn,sizeof(sn.sn),buf_sn,len);
- errcode_t nvRet = uapi_nv_write(NV_ID_CUSTOM_KEY_SERIAL_NUM, &sn, sizeof(custom_key_sn_t));
- //PRINT("write sn len %d, ret %d , %s\r\n",len,nvRet,sn.sn);
+ p_sn->size = len;
+ memcpy_s(p_sn->sn,sizeof(p_sn->sn),buf_sn,len);
+ nv_ret_value = uapi_nv_write(NV_ID_CUSTOM_KEY_SERIAL_NUM, write_value, key_len);
+ if (nv_ret_value != ERRCODE_SUCC) {
+ /* ERROR PROCESS */
+ free(write_value);
+ return ERRCODE_FAIL;
+ }
+ /* APP PROCESS */
+ free(write_value);
+
#endif
- return nvRet;
+ return nv_ret_value;
+
}
// extern "C" {
uint32_t mcu_os_sys_reset(uint8_t *para, uint32_t para_len);
}
uint8_t *sn_buf = (uint8_t*)argv[0];
uint16_t len = strlen((const char*)sn_buf);
- PRINT("set sn = %s \r\n",sn_buf);
uint32_t rret = zh_set_sn(sn_buf,len);
+ PRINT("set sn = %s , len = %d, ret 0x%X\r\n",sn_buf,len, rret);
+
return 0;
}
{
UNUSED(strCommonCmd);
PRINT("custom_start_ota \r\n");
+
return uaip_cmd_update_request();
}
static uint32_t custom_utils_getsn(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
uint8_t sn_buf[32] = {0};
uint32_t len = 0;
uint32_t ret = zh_get_sn(sn_buf,&len);
- PRINT("get sn len %d , %s \r\n",len,sn_buf);
+ PRINT("get sn len %d , %s , ret = 0x%X\r\n",len,sn_buf,ret);
return 0;
}
static const at_cmd_table_t g_custom_utils_cmd[] = {
-#===============================================================================\r
-# @brief cmake file\r
-# Copyright (c) @CompanyNameMagicTag 2022-2022. All rights reserved.\r
-#===============================================================================\r
-set(COMPONENT_NAME "nv")\r
-\r
-set(SOURCES\r
- ${CMAKE_CURRENT_SOURCE_DIR}/nv_storage_app/soc_nv_storage_handle.c\r
- ${CMAKE_CURRENT_SOURCE_DIR}/nv_storage_app/soc_nv_storage.c\r
- ${CMAKE_CURRENT_SOURCE_DIR}/nv_storage_lib/nv_key.c\r
- ${CMAKE_CURRENT_SOURCE_DIR}/nv_storage_lib/nv_notify.c\r
- ${CMAKE_CURRENT_SOURCE_DIR}/nv_storage_lib/nv_nvregion.c\r
- ${CMAKE_CURRENT_SOURCE_DIR}/nv_storage_lib/nv_page.c\r
- ${CMAKE_CURRENT_SOURCE_DIR}/nv_storage_lib/nv_reset.c\r
- ${CMAKE_CURRENT_SOURCE_DIR}/nv_storage_lib/nv_rpc.c\r
- ${CMAKE_CURRENT_SOURCE_DIR}/nv_storage_lib/nv_store.c\r
- ${CMAKE_CURRENT_SOURCE_DIR}/nv_storage_lib/nv_update.c\r
- ${CMAKE_CURRENT_SOURCE_DIR}/nv_storage_lib/nv_upg.c\r
- ${CMAKE_CURRENT_SOURCE_DIR}/nv_storage_lib/nv_debug.c\r
- ${CMAKE_CURRENT_SOURCE_DIR}/nv_storage_lib/nv_async_store.c\r
-)\r
-\r
-set(PUBLIC_HEADER\r
- ${CMAKE_SOURCE_DIR}/include/middleware/utils\r
- ${CMAKE_CURRENT_SOURCE_DIR}/nv_storage_lib\r
- ${CMAKE_CURRENT_SOURCE_DIR}/nv_storage_app\r
-)\r
-\r
-set(PRIVATE_HEADER\r
- ${CMAKE_SOURCE_DIR}/middleware/chips/${chip}/nv/include\r
- ${CMAKE_SOURCE_DIR}/middleware/utils/update/inner_include\r
-)\r
-\r
-set(PRIVATE_DEFINES\r
-)\r
-\r
-set(PUBLIC_DEFINES\r
-)\r
-\r
-# use this when you want to add ccflags like -include xxx\r
-set(COMPONENT_PUBLIC_CCFLAGS\r
-)\r
-\r
-set(COMPONENT_CCFLAGS\r
-)\r
-\r
-set(WHOLE_LINK\r
- true\r
-)\r
-\r
-set(MAIN_COMPONENT\r
- false\r
-)\r
+#===============================================================================
+# @brief cmake file
+# Copyright (c) @CompanyNameMagicTag 2022-2022. All rights reserved.
+#===============================================================================
+set(COMPONENT_NAME "nv")
+
+set(SOURCES
+ ${CMAKE_CURRENT_SOURCE_DIR}/nv_storage_app/soc_nv_storage_handle.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/nv_storage_app/soc_nv_storage.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/nv_storage_lib/nv_key.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/nv_storage_lib/nv_notify.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/nv_storage_lib/nv_nvregion.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/nv_storage_lib/nv_page.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/nv_storage_lib/nv_reset.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/nv_storage_lib/nv_rpc.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/nv_storage_lib/nv_store.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/nv_storage_lib/nv_update.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/nv_storage_lib/nv_upg.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/nv_storage_lib/nv_debug.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/nv_storage_lib/nv_async_store.c
+)
+
+set(PUBLIC_HEADER
+ ${CMAKE_SOURCE_DIR}/include/middleware/utils
+ ${CMAKE_CURRENT_SOURCE_DIR}/nv_storage_lib
+ ${CMAKE_CURRENT_SOURCE_DIR}/nv_storage_app
+)
+
+set(PRIVATE_HEADER
+ ${CMAKE_SOURCE_DIR}/middleware/chips/${chip}/nv/include
+ ${CMAKE_SOURCE_DIR}/middleware/utils/update/inner_include
+)
+
+set(PRIVATE_DEFINES
+)
+
+set(PUBLIC_DEFINES
+)
+
+# use this when you want to add ccflags like -include xxx
+set(COMPONENT_PUBLIC_CCFLAGS
+)
+
+set(COMPONENT_CCFLAGS
+)
+
+set(WHOLE_LINK
+ true
+)
+
+set(MAIN_COMPONENT
+ false
+)
build_component()
\ No newline at end of file
-/*\r
- * Copyright (c) @CompanyNameMagicTag 2021-2022. All rights reserved.\r
- * Description: KEY VALUE STORAGE INTERNAL INTERFACE\r
- */\r
-\r
-#ifndef NV_STORAGE_H\r
-#define NV_STORAGE_H\r
-\r
-#include "nv_notify.h"\r
-#include "nv.h"\r
-\r
-#include "errcode.h"\r
-\r
-#ifdef __cplusplus\r
-#if __cplusplus\r
-extern "C" {\r
-#endif /* __cplusplus */\r
-#endif /* __cplusplus */\r
-\r
-/**\r
- * KV key attribute flags\r
- * A key with a normal or encrypted attribute can be set to a permanent attribute again.\r
- * A key with a permanent attribute cannot be set to another attribute.\r
- * Attribute 3 is considered to be both encrypted and permanent.\r
- */\r
-typedef enum {\r
- NV_ATTRIBUTE_NORMAL = 0x0, /* Default type, no special attribute */\r
- NV_ATTRIBUTE_PERMANENT = 0x1, /* Key is permanent and can't be deleted or modified */\r
- NV_ATTRIBUTE_ENCRYPTED = 0x2, /* Key is encrypted in flash, The key can be encrypted permanently */\r
- NV_ATTRIBUTE_NON_UPGRADE = 0x4\r
-} nv_attributes_t;\r
-\r
-nv_direct_ctrl_t *nv_direct_get_nv_ctrl(void);\r
-\r
-/**\r
- * Force store a key value pair, even if the key has been set to permanent.\r
- * @param key key to associate kvalue to\r
- * @param kvalue value to store\r
- * @param kvalue_length length in bytes of kvalue\r
- * @return SOC_ERR_SUCCESS or an error code\r
- */\r
-errcode_t uapi_nv_write_force(uint16_t key, const uint8_t *kvalue, uint16_t kvalue_length);\r
-\r
-/**\r
- * Erase an stored value given its key\r
- * @param key key of the key to erase\r
- * @return SOC_ERR_SUCCESS or an error code\r
- */\r
-errcode_t uapi_nv_delete_key(uint16_t key);\r
-\r
-/**\r
- * Update the attributes of an existing key.\r
- * @param key key to associate make permanent\r
- * @param attr Sets the storage attributes of a key.\r
- * @return SOC_ERR_SUCCESS or an error code\r
- * @note This key can not then be deleted or overwitten, and there is no mechanism to undo this\r
- */\r
-errcode_t uapi_nv_update_key_attr(uint16_t key, nv_key_attr_t *attr, nv_storage_completed_callback func);\r
-\r
-/**\r
- * Get key information associated with a specific key\r
- * @param key key of the value to get\r
- * @param length length of the current key\r
- * @param attr The storage attributes of a key.\r
- * @return SOC_ERR_SUCCESS or an error code\r
- */\r
-errcode_t uapi_nv_get_key_attr(uint16_t key, uint16_t *length, nv_key_attr_t *attr);\r
-\r
- /**\r
- * @ingroup iot_nv\r
- * @brief compare a value associated with a specific key.\r
- * CNcomment:检验特定NV的值 CNend\r
- *\r
- * @par 描述:\r
- * Check whether NV data has been stored. If the stored data is the same\r
- * as the input parameter data,a success message is returned.\r
- * CNcomment:检查NV数据是否已存储。如果存储的数据与入参数据相同,则返回成功 CNend\r
- * @attention None.\r
- *\r
- * @param key [IN] The key to which the kvalue is associated.\r
- * CNcomment:要检验的NV ID CNend\r
- * @param kvalue_length [IN] Length of kvalue in bytes.\r
- * CNcomment:要检验的NV长度 CNend\r
- * @param kvalue [IN] The value to store.CNcomment:待进行对比的NV值 CNend\r
- *\r
- * @retval #true Success\r
- * @retval #false Failure\r
- *\r
- * @par 依赖:\r
- * @li nv\r
- * @li nv.h\r
- * @see None\r
- */\r
-bool uapi_nv_is_stored(uint16_t key, uint16_t kvalue_length, const uint8_t *kvalue);\r
-\r
-#ifdef __cplusplus\r
-#if __cplusplus\r
-}\r
-#endif /* __cplusplus */\r
-#endif /* __cplusplus */\r
-\r
-#endif /* NV_STORAGE_H */\r
+/*
+ * Copyright (c) @CompanyNameMagicTag 2021-2022. All rights reserved.
+ * Description: KEY VALUE STORAGE INTERNAL INTERFACE
+ */
+
+#ifndef NV_STORAGE_H
+#define NV_STORAGE_H
+
+#include "nv_notify.h"
+#include "nv.h"
+
+#include "errcode.h"
+
+#ifdef __cplusplus
+#if __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+#endif /* __cplusplus */
+
+/**
+ * KV key attribute flags
+ * A key with a normal or encrypted attribute can be set to a permanent attribute again.
+ * A key with a permanent attribute cannot be set to another attribute.
+ * Attribute 3 is considered to be both encrypted and permanent.
+ */
+typedef enum {
+ NV_ATTRIBUTE_NORMAL = 0x0, /* Default type, no special attribute */
+ NV_ATTRIBUTE_PERMANENT = 0x1, /* Key is permanent and can't be deleted or modified */
+ NV_ATTRIBUTE_ENCRYPTED = 0x2, /* Key is encrypted in flash, The key can be encrypted permanently */
+ NV_ATTRIBUTE_NON_UPGRADE = 0x4
+} nv_attributes_t;
+
+nv_direct_ctrl_t *nv_direct_get_nv_ctrl(void);
+
+/**
+ * Force store a key value pair, even if the key has been set to permanent.
+ * @param key key to associate kvalue to
+ * @param kvalue value to store
+ * @param kvalue_length length in bytes of kvalue
+ * @return SOC_ERR_SUCCESS or an error code
+ */
+errcode_t uapi_nv_write_force(uint16_t key, const uint8_t *kvalue, uint16_t kvalue_length);
+
+/**
+ * Erase an stored value given its key
+ * @param key key of the key to erase
+ * @return SOC_ERR_SUCCESS or an error code
+ */
+errcode_t uapi_nv_delete_key(uint16_t key);
+
+/**
+ * Update the attributes of an existing key.
+ * @param key key to associate make permanent
+ * @param attr Sets the storage attributes of a key.
+ * @return SOC_ERR_SUCCESS or an error code
+ * @note This key can not then be deleted or overwitten, and there is no mechanism to undo this
+ */
+errcode_t uapi_nv_update_key_attr(uint16_t key, nv_key_attr_t *attr, nv_storage_completed_callback func);
+
+/**
+ * Get key information associated with a specific key
+ * @param key key of the value to get
+ * @param length length of the current key
+ * @param attr The storage attributes of a key.
+ * @return SOC_ERR_SUCCESS or an error code
+ */
+errcode_t uapi_nv_get_key_attr(uint16_t key, uint16_t *length, nv_key_attr_t *attr);
+
+ /**
+ * @ingroup iot_nv
+ * @brief compare a value associated with a specific key.
+ * CNcomment:检验特定NV的值 CNend
+ *
+ * @par 描述:
+ * Check whether NV data has been stored. If the stored data is the same
+ * as the input parameter data,a success message is returned.
+ * CNcomment:检查NV数据是否已存储。如果存储的数据与入参数据相同,则返回成功 CNend
+ * @attention None.
+ *
+ * @param key [IN] The key to which the kvalue is associated.
+ * CNcomment:要检验的NV ID CNend
+ * @param kvalue_length [IN] Length of kvalue in bytes.
+ * CNcomment:要检验的NV长度 CNend
+ * @param kvalue [IN] The value to store.CNcomment:待进行对比的NV值 CNend
+ *
+ * @retval #true Success
+ * @retval #false Failure
+ *
+ * @par 依赖:
+ * @li nv
+ * @li nv.h
+ * @see None
+ */
+bool uapi_nv_is_stored(uint16_t key, uint16_t kvalue_length, const uint8_t *kvalue);
+
+#ifdef __cplusplus
+#if __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* __cplusplus */
+
+#endif /* NV_STORAGE_H */
-/*\r
- * Copyright (c) @CompanyNameMagicTag 2016-2022. All rights reserved.\r
- * Description: SOC KEY VALUE STORAGE IMPLEMENTATION\r
- */\r
-\r
-#include "nv.h"\r
-#include "nv_storage.h"\r
-#include "nv_store.h"\r
-#include "nv_reset.h"\r
-#include "securec.h"\r
-#include "nv_porting.h"\r
-#include "common_def.h"\r
-#include "nv_config.h"\r
-#include "systick.h"\r
-#include "nv_async_store.h"\r
-#include "nv_update.h"\r
-#include "nv_notify.h"\r
-#include "nv_task_adapt.h"\r
-#include "nv_storage_handle.h"\r
-\r
-void uapi_nv_init(void)\r
-{\r
- (void)nv_direct_ctrl_init();\r
-}\r
-\r
-errcode_t uapi_nv_write_with_attr(uint16_t key, const uint8_t *kvalue, uint16_t kvalue_length, nv_key_attr_t *attr,\r
- nv_storage_completed_callback func)\r
-{\r
- errcode_t ret;\r
-\r
- if (kvalue == NULL || kvalue_length == 0) {\r
- return ERRCODE_NV_INVALID_PARAMS;\r
- }\r
-\r
-#if (CONFIG_NV_SUPPORT_ENCRYPT != NV_YES)\r
- if ((attr != NULL && attr->encrypted == true)) {\r
- nv_log_err("[NV] nv_direct_write_with_attr: encryption not support!\r\n");\r
- return ERRCODE_NV_ILLEGAL_OPERATION;\r
- }\r
-#endif\r
-\r
- uint64_t start_time = uapi_systick_get_ms();\r
-\r
-#if (CONFIG_NV_SUPPORT_ASYNCHRONOUS_STORE == NV_YES)\r
- ret = nv_async_write_with_attr(key, kvalue, kvalue_length, attr, func);\r
-#else\r
- ret = nv_direct_write_with_attr(key, kvalue, kvalue_length, attr, func);\r
-#endif\r
- uint64_t end_time = uapi_systick_get_ms();\r
- nv_log_debug("[NV] write key (key_id = 0x%x len = %d) ret = %d. takes %lld ms\r\n",\r
- key, kvalue_length, ret, (end_time - start_time));\r
- unused(start_time);\r
- unused(end_time);\r
- return ret;\r
-}\r
-\r
-errcode_t uapi_nv_write(uint16_t key, const uint8_t *kvalue, uint16_t kvalue_length)\r
-{\r
- return uapi_nv_write_with_attr(key, kvalue, kvalue_length, NULL, NULL);\r
-}\r
-\r
-errcode_t uapi_nv_write_force(uint16_t key, const uint8_t *kvalue, uint16_t kvalue_length)\r
-{\r
- if (kvalue == NULL || kvalue_length == 0) {\r
- return ERRCODE_NV_INVALID_PARAMS;\r
- }\r
-#ifdef CONFIG_NV_SUPPORT_WRITE_FORCE\r
- return nv_direct_write_force(key, kvalue, kvalue_length);\r
-#endif\r
- unused(key);\r
- return ERRCODE_NOT_SUPPORT;\r
-}\r
-\r
-errcode_t uapi_nv_update_key_attr(uint16_t key, nv_key_attr_t *attr, nv_storage_completed_callback func)\r
-{\r
- if (attr == NULL) {\r
- return ERRCODE_NV_INVALID_PARAMS;\r
- }\r
-#ifdef CONFIG_NV_SUPPORT_UPDATE_ATTR\r
- return nv_direct_update_key_attr(key, attr, func);\r
-#endif\r
- unused(key);\r
- unused(func);\r
- return ERRCODE_NOT_SUPPORT;\r
-}\r
-\r
-errcode_t uapi_nv_get_key_attr(uint16_t key, uint16_t *length, nv_key_attr_t *attr)\r
-{\r
- if (attr == NULL) {\r
- return ERRCODE_NV_INVALID_PARAMS;\r
- }\r
-\r
- return nv_direct_get_key_attr(key, length, attr);\r
-}\r
-\r
-errcode_t uapi_nv_read_with_attr(uint16_t key, uint16_t kvalue_max_length, uint16_t *kvalue_length, uint8_t *kvalue,\r
- nv_key_attr_t *attr)\r
-{\r
- errcode_t ret;\r
-\r
- if (kvalue_length == NULL || kvalue == NULL || attr == NULL) {\r
- return ERRCODE_NV_INVALID_PARAMS;\r
- }\r
-\r
- uint64_t start_time = uapi_systick_get_ms();\r
-\r
-#if (CONFIG_NV_SUPPORT_ASYNCHRONOUS_STORE == NV_YES)\r
- ret = nv_async_read_with_attr(key, kvalue_max_length, kvalue_length, kvalue, attr);\r
-#else\r
- ret = nv_direct_get_key_data(key, kvalue_max_length, kvalue_length, kvalue, attr);\r
-#endif\r
-\r
- uint64_t end_time = uapi_systick_get_ms();\r
- nv_log_debug("[NV] read key (key_id = 0x%x len = %d) ret = 0x%x. takes %lld ms\r\n",\r
- key, *kvalue_length, ret, (end_time - start_time));\r
- unused(start_time);\r
- unused(end_time);\r
- return ret;\r
-}\r
-\r
-errcode_t uapi_nv_read(uint16_t key, uint16_t kvalue_max_length, uint16_t *kvalue_length, uint8_t *kvalue)\r
-{\r
- if (kvalue_length == NULL || kvalue == NULL) {\r
- return ERRCODE_NV_INVALID_PARAMS;\r
- }\r
- nv_key_attr_t attr;\r
- return uapi_nv_read_with_attr(key, kvalue_max_length, kvalue_length, kvalue, &attr);\r
-}\r
-\r
-errcode_t uapi_nv_delete_key(uint16_t key)\r
-{\r
- unused(key);\r
- return ERRCODE_NOT_SUPPORT;\r
-}\r
-\r
-bool uapi_nv_is_stored(uint16_t key, uint16_t kvalue_length, const uint8_t *kvalue)\r
-{\r
- if (kvalue_length == 0 || kvalue == NULL) {\r
- return false;\r
- }\r
-\r
- errcode_t ret = nv_direct_stored(key, kvalue_length, kvalue);\r
- if (ret == ERRCODE_SUCC) {\r
- return true;\r
- } else {\r
- return false;\r
- }\r
-}\r
-\r
-errcode_t uapi_nv_get_store_status(nv_store_status_t *status)\r
-{\r
- if (status == NULL) {\r
- return ERRCODE_NV_INVALID_PARAMS;\r
- }\r
-\r
- uint64_t start_time = uapi_systick_get_ms();\r
- errcode_t ret = nv_direct_get_store_status(status);\r
- uint64_t end_time = uapi_systick_get_ms();\r
- nv_log_debug("[NV] get store status take %lld ms\r\n", (end_time - start_time));\r
- unused(start_time);\r
- unused(end_time);\r
- return ret;\r
-}\r
-\r
-errcode_t uapi_nv_backup(const nv_backup_mode_t *backup_mode)\r
-{\r
-#if (CONFIG_NV_SUPPORT_BACKUP_RESTORE == NV_YES)\r
- if (backup_mode == NULL) {\r
- return ERRCODE_NV_INVALID_PARAMS;\r
- }\r
-\r
- return nv_direct_backup_keys(backup_mode);\r
-#else\r
- unused(backup_mode);\r
- return ERRCODE_NOT_SUPPORT;\r
-#endif\r
-}\r
-\r
-errcode_t uapi_nv_set_restore_mode_all(void)\r
-{\r
-#if (CONFIG_NV_SUPPORT_BACKUP_RESTORE == NV_YES)\r
-\r
- return nv_direct_set_restore_flag_all();\r
-#else\r
- return ERRCODE_NOT_SUPPORT;\r
-#endif\r
-}\r
-\r
-errcode_t uapi_nv_set_restore_mode_partitial(const nv_restore_mode_t *restore_mode)\r
-{\r
-#if (CONFIG_NV_SUPPORT_BACKUP_RESTORE == NV_YES)\r
- return nv_direct_set_restore_flag_partitial(restore_mode);\r
-#else\r
- unused(restore_mode);\r
- return ERRCODE_NOT_SUPPORT;\r
-#endif\r
-}\r
-\r
-errcode_t uapi_nv_flush(void)\r
-{\r
-#if (CONFIG_NV_SUPPORT_ASYNCHRONOUS_STORE == NV_YES)\r
- return nv_async_flush();\r
-#else\r
- return ERRCODE_NOT_SUPPORT;\r
-#endif\r
-}\r
-\r
-errcode_t uapi_nv_register_change_notify_proc(uint16_t min_key, uint16_t max_key, nv_changed_notify_func func)\r
-{\r
- if ((min_key > max_key) || (func == NULL)) {\r
- return ERRCODE_NV_INVALID_PARAMS;\r
- }\r
-\r
- if (nv_direct_get_nv_ctrl()->notify_regitser_max_nums == 0) {\r
- return ERRCODE_NV_INVALID_PARAMS;\r
- }\r
-#if (CONFIG_NV_SUPPORT_CHANGE_NOTIFY == NV_YES)\r
- return nv_direct_add_func_to_notify_list(min_key, max_key, func);\r
-#endif\r
- return ERRCODE_NOT_SUPPORT;\r
-}\r
+/*
+ * Copyright (c) @CompanyNameMagicTag 2016-2022. All rights reserved.
+ * Description: SOC KEY VALUE STORAGE IMPLEMENTATION
+ */
+
+#include "nv.h"
+#include "nv_storage.h"
+#include "nv_store.h"
+#include "nv_reset.h"
+#include "securec.h"
+#include "nv_porting.h"
+#include "common_def.h"
+#include "nv_config.h"
+#include "systick.h"
+#include "nv_async_store.h"
+#include "nv_update.h"
+#include "nv_notify.h"
+#include "nv_task_adapt.h"
+#include "nv_storage_handle.h"
+
+void uapi_nv_init(void)
+{
+ (void)nv_direct_ctrl_init();
+}
+
+errcode_t uapi_nv_write_with_attr(uint16_t key, const uint8_t *kvalue, uint16_t kvalue_length, nv_key_attr_t *attr,
+ nv_storage_completed_callback func)
+{
+ errcode_t ret;
+
+ if (kvalue == NULL || kvalue_length == 0) {
+ return ERRCODE_NV_INVALID_PARAMS;
+ }
+
+#if (CONFIG_NV_SUPPORT_ENCRYPT != NV_YES)
+ if ((attr != NULL && attr->encrypted == true)) {
+ nv_log_err("[NV] nv_direct_write_with_attr: encryption not support!\r\n");
+ return ERRCODE_NV_ILLEGAL_OPERATION;
+ }
+#endif
+
+ uint64_t start_time = uapi_systick_get_ms();
+
+#if (CONFIG_NV_SUPPORT_ASYNCHRONOUS_STORE == NV_YES)
+ ret = nv_async_write_with_attr(key, kvalue, kvalue_length, attr, func);
+#else
+ ret = nv_direct_write_with_attr(key, kvalue, kvalue_length, attr, func);
+#endif
+ uint64_t end_time = uapi_systick_get_ms();
+ nv_log_debug("[NV] write key (key_id = 0x%x len = %d) ret = %d. takes %lld ms\r\n",
+ key, kvalue_length, ret, (end_time - start_time));
+ unused(start_time);
+ unused(end_time);
+ return ret;
+}
+
+errcode_t uapi_nv_write(uint16_t key, const uint8_t *kvalue, uint16_t kvalue_length)
+{
+ nv_key_attr_t kattrib = {0};
+ kattrib.encrypted = false;
+ kattrib.non_upgrade = false;
+ kattrib.permanent = false;
+ return uapi_nv_write_with_attr(key, kvalue, kvalue_length, &kattrib, NULL);
+}
+
+errcode_t uapi_nv_write_force(uint16_t key, const uint8_t *kvalue, uint16_t kvalue_length)
+{
+ if (kvalue == NULL || kvalue_length == 0) {
+ return ERRCODE_NV_INVALID_PARAMS;
+ }
+#ifdef CONFIG_NV_SUPPORT_WRITE_FORCE
+ return nv_direct_write_force(key, kvalue, kvalue_length);
+#endif
+ unused(key);
+ return ERRCODE_NOT_SUPPORT;
+}
+
+errcode_t uapi_nv_update_key_attr(uint16_t key, nv_key_attr_t *attr, nv_storage_completed_callback func)
+{
+ if (attr == NULL) {
+ return ERRCODE_NV_INVALID_PARAMS;
+ }
+#ifdef CONFIG_NV_SUPPORT_UPDATE_ATTR
+ return nv_direct_update_key_attr(key, attr, func);
+#endif
+ unused(key);
+ unused(func);
+ return ERRCODE_NOT_SUPPORT;
+}
+
+errcode_t uapi_nv_get_key_attr(uint16_t key, uint16_t *length, nv_key_attr_t *attr)
+{
+ if (attr == NULL) {
+ return ERRCODE_NV_INVALID_PARAMS;
+ }
+
+ return nv_direct_get_key_attr(key, length, attr);
+}
+
+errcode_t uapi_nv_read_with_attr(uint16_t key, uint16_t kvalue_max_length, uint16_t *kvalue_length, uint8_t *kvalue,
+ nv_key_attr_t *attr)
+{
+ errcode_t ret;
+
+ if (kvalue_length == NULL || kvalue == NULL || attr == NULL) {
+ return ERRCODE_NV_INVALID_PARAMS;
+ }
+
+ uint64_t start_time = uapi_systick_get_ms();
+
+#if (CONFIG_NV_SUPPORT_ASYNCHRONOUS_STORE == NV_YES)
+ ret = nv_async_read_with_attr(key, kvalue_max_length, kvalue_length, kvalue, attr);
+#else
+ ret = nv_direct_get_key_data(key, kvalue_max_length, kvalue_length, kvalue, attr);
+#endif
+
+ uint64_t end_time = uapi_systick_get_ms();
+ nv_log_debug("[NV] read key (key_id = 0x%x len = %d) ret = 0x%x. takes %lld ms\r\n",
+ key, *kvalue_length, ret, (end_time - start_time));
+ unused(start_time);
+ unused(end_time);
+ return ret;
+}
+
+errcode_t uapi_nv_read(uint16_t key, uint16_t kvalue_max_length, uint16_t *kvalue_length, uint8_t *kvalue)
+{
+ if (kvalue_length == NULL || kvalue == NULL) {
+ return ERRCODE_NV_INVALID_PARAMS;
+ }
+ nv_key_attr_t attr;
+ return uapi_nv_read_with_attr(key, kvalue_max_length, kvalue_length, kvalue, &attr);
+}
+
+errcode_t uapi_nv_delete_key(uint16_t key)
+{
+ unused(key);
+ return ERRCODE_NOT_SUPPORT;
+}
+
+bool uapi_nv_is_stored(uint16_t key, uint16_t kvalue_length, const uint8_t *kvalue)
+{
+ if (kvalue_length == 0 || kvalue == NULL) {
+ return false;
+ }
+
+ errcode_t ret = nv_direct_stored(key, kvalue_length, kvalue);
+ if (ret == ERRCODE_SUCC) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+errcode_t uapi_nv_get_store_status(nv_store_status_t *status)
+{
+ if (status == NULL) {
+ return ERRCODE_NV_INVALID_PARAMS;
+ }
+
+ uint64_t start_time = uapi_systick_get_ms();
+ errcode_t ret = nv_direct_get_store_status(status);
+ uint64_t end_time = uapi_systick_get_ms();
+ nv_log_debug("[NV] get store status take %lld ms\r\n", (end_time - start_time));
+ unused(start_time);
+ unused(end_time);
+ return ret;
+}
+
+errcode_t uapi_nv_backup(const nv_backup_mode_t *backup_mode)
+{
+#if (CONFIG_NV_SUPPORT_BACKUP_RESTORE == NV_YES)
+ if (backup_mode == NULL) {
+ return ERRCODE_NV_INVALID_PARAMS;
+ }
+
+ return nv_direct_backup_keys(backup_mode);
+#else
+ unused(backup_mode);
+ return ERRCODE_NOT_SUPPORT;
+#endif
+}
+
+errcode_t uapi_nv_set_restore_mode_all(void)
+{
+#if (CONFIG_NV_SUPPORT_BACKUP_RESTORE == NV_YES)
+
+ return nv_direct_set_restore_flag_all();
+#else
+ return ERRCODE_NOT_SUPPORT;
+#endif
+}
+
+errcode_t uapi_nv_set_restore_mode_partitial(const nv_restore_mode_t *restore_mode)
+{
+#if (CONFIG_NV_SUPPORT_BACKUP_RESTORE == NV_YES)
+ return nv_direct_set_restore_flag_partitial(restore_mode);
+#else
+ unused(restore_mode);
+ return ERRCODE_NOT_SUPPORT;
+#endif
+}
+
+errcode_t uapi_nv_flush(void)
+{
+#if (CONFIG_NV_SUPPORT_ASYNCHRONOUS_STORE == NV_YES)
+ return nv_async_flush();
+#else
+ return ERRCODE_NOT_SUPPORT;
+#endif
+}
+
+errcode_t uapi_nv_register_change_notify_proc(uint16_t min_key, uint16_t max_key, nv_changed_notify_func func)
+{
+ if ((min_key > max_key) || (func == NULL)) {
+ return ERRCODE_NV_INVALID_PARAMS;
+ }
+
+ if (nv_direct_get_nv_ctrl()->notify_regitser_max_nums == 0) {
+ return ERRCODE_NV_INVALID_PARAMS;
+ }
+#if (CONFIG_NV_SUPPORT_CHANGE_NOTIFY == NV_YES)
+ return nv_direct_add_func_to_notify_list(min_key, max_key, func);
+#endif
+ return ERRCODE_NOT_SUPPORT;
+}
-/*\r
- * Copyright (c) @CompanyNameMagicTag 2016-2022. All rights reserved.\r
- * Description: SOC KEY VALUE STORAGE IMPLEMENTATION\r
- */\r
-\r
-#include "nv.h"\r
-#include "nv_storage.h"\r
-#include "nv_store.h"\r
-#include "nv_upg.h"\r
-#include "securec.h"\r
-#include "nv_porting.h"\r
-#include "common_def.h"\r
-#include "nv_reset.h"\r
-#include "nv_async_store.h"\r
-#include "osal_semaphore.h"\r
-#include "nv_update.h"\r
-#include "nv_notify.h"\r
-#include "nv_task_adapt.h"\r
-#include "nv_storage_handle.h"\r
-\r
-osal_semaphore nv_sem;\r
-#define MAX_BINARY_VAL 1\r
-\r
-STATIC nv_attributes_t nv_helper_convert_key_attr(const nv_key_attr_t *attr)\r
-{\r
- nv_attributes_t attribute = NV_ATTRIBUTE_NORMAL;\r
- bool is_support_crypt = false;\r
- if (attr == NULL) {\r
- return attribute;\r
- }\r
-\r
-#if (CONFIG_NV_SUPPORT_ENCRYPT == NV_YES)\r
- is_support_crypt = attr->encrypted;\r
-#endif\r
-\r
- if (attr->permanent && is_support_crypt) {\r
- attribute = NV_ATTRIBUTE_ENCRYPTED | NV_ATTRIBUTE_PERMANENT | NV_ATTRIBUTE_NON_UPGRADE;\r
- } else if (attr->permanent) {\r
- attribute = NV_ATTRIBUTE_PERMANENT | NV_ATTRIBUTE_NON_UPGRADE;\r
- } else if (is_support_crypt) {\r
- attribute = NV_ATTRIBUTE_ENCRYPTED | NV_ATTRIBUTE_NON_UPGRADE;\r
- } else if (attr->non_upgrade) {\r
- attribute = NV_ATTRIBUTE_NON_UPGRADE;\r
- }\r
- return attribute;\r
-}\r
-\r
-static uint16_t nv_storage_max_key_space(const nv_key_attr_t *attr)\r
-{\r
- if (attr == NULL) {\r
- return NV_NORMAL_KVALUE_MAX_LEN;\r
- }\r
- return (attr->encrypted ? NV_ENCRYPTED_KVALUE_MAX_LEN : NV_NORMAL_KVALUE_MAX_LEN);\r
-}\r
-\r
-nv_direct_ctrl_t g_nv_direct_ctrl;\r
-nv_direct_ctrl_t *nv_direct_get_nv_ctrl(void)\r
-{\r
- return &g_nv_direct_ctrl;\r
-}\r
-\r
-errcode_t nv_direct_get_key_attr(uint16_t key_id, uint16_t *len, nv_key_attr_t *attr)\r
-{\r
- kv_attributes_t get_attribute = 0;\r
- errcode_t ret = ERRCODE_SUCC;\r
-\r
- if (len == NULL || attr == NULL) {\r
- return ERRCODE_NV_INVALID_PARAMS;\r
- }\r
-\r
- if (osal_sem_down_timeout(&nv_sem, 0xFFFFFFFF) != ERRCODE_SUCC) {\r
- return ERRCODE_NV_SEM_WAIT_ERR;\r
- }\r
-\r
- *len = 0;\r
- memset_s(attr, sizeof(nv_key_attr_t), 0, sizeof(nv_key_attr_t));\r
- ret = kv_store_get_key_attr(KV_STORE_APPLICATION, key_id, len, &get_attribute);\r
- if (ret == ERRCODE_SUCC) {\r
- if (((uint32_t)get_attribute & NV_ATTRIBUTE_PERMANENT) != 0) {\r
- attr->permanent = true;\r
- attr->non_upgrade = true;\r
- }\r
- if (((uint32_t)get_attribute & NV_ATTRIBUTE_ENCRYPTED) != 0) {\r
- attr->encrypted = true;\r
- attr->non_upgrade = true;\r
- }\r
- if (((uint32_t)get_attribute & NV_ATTRIBUTE_NON_UPGRADE) != 0) {\r
- attr->non_upgrade = true;\r
- }\r
- }\r
- osal_sem_up(&nv_sem);\r
- return ret;\r
-}\r
-\r
-errcode_t nv_direct_write_with_attr(uint16_t key_id, const uint8_t *kvalue, uint16_t kvalue_length, nv_key_attr_t *attr,\r
- nv_storage_completed_callback func)\r
-{\r
- errcode_t ret;\r
- flash_task_node task_node = {0};\r
- uint16_t len;\r
- kv_attributes_t attribute = (kv_attributes_t)0;\r
- unused(func);\r
-\r
- if (kvalue_length > nv_storage_max_key_space(attr)) {\r
- nv_log_err("[NV] nv_direct_write_with_attr: len exceeds the max size. key_id = 0x%x\r\n", key_id);\r
- return ERRCODE_NV_NO_ENOUGH_SPACE;\r
- }\r
-\r
- if (osal_sem_down_timeout(&nv_sem, 0xFFFFFFFF) != ERRCODE_SUCC) {\r
- nv_log_err("[NV] nv_direct_write_with_attr: semaphore error!\r\n");\r
- return ERRCODE_NV_SEM_WAIT_ERR;\r
- }\r
-\r
- ret = kv_store_get_key_attr(KV_STORE_APPLICATION, key_id, &len, &attribute);\r
- if (ret == ERRCODE_SUCC) {\r
- if ((((uint32_t)attribute & NV_ATTRIBUTE_PERMANENT) != 0) ||\r
- ((((uint32_t)attribute & NV_ATTRIBUTE_ENCRYPTED) != 0) && (attr != NULL && attr->encrypted == false))\r
- || ((((uint32_t)attribute & NV_ATTRIBUTE_NON_UPGRADE) != 0) && (attr == NULL))) {\r
- /* When old key is permanent, not permit to write */\r
- /* When old key is encrypted, new key is non-encrypted, not permit to write */\r
- /* When old key is non upgrade, new key is write by ordinary write, not permit to write */\r
- nv_log_err("[NV] nv_direct_write_with_attr: operation not allowed! key_id = 0x%x\r\n", key_id);\r
- osal_sem_up(&nv_sem);\r
- return ERRCODE_NV_ILLEGAL_OPERATION;\r
- }\r
- }\r
- if (attr != NULL) {\r
- attribute = (uint8_t)nv_helper_convert_key_attr(attr);\r
- }\r
- task_node.state_code = FLASH_TASK_READY;\r
- task_node.data.kv.key = key_id;\r
- task_node.data.kv.kvalue = kvalue;\r
- task_node.data.kv.kvalue_length = kvalue_length;\r
- task_node.data.kv.force_write = false;\r
- task_node.data.kv.attribute = attribute;\r
- ret = kv_update_write_key(KV_STORE_APPLICATION, &task_node);\r
- osal_sem_up(&nv_sem);\r
- if (ret != ERRCODE_SUCC) {\r
- nv_log_err("[NV] nv_direct_write_with_attr failed. key_id = 0x%x, ret = 0x%x\r\n", key_id, ret);\r
- return ret;\r
- }\r
-#if (CONFIG_NV_SUPPORT_CHANGE_NOTIFY == NV_YES)\r
- nv_change_notify(key_id);\r
-#endif\r
- return ERRCODE_SUCC;\r
-}\r
-\r
-#ifdef CONFIG_NV_SUPPORT_WRITE_FORCE\r
-errcode_t nv_direct_write_force(uint16_t key_id, const uint8_t *kvalue, uint16_t kvalue_length)\r
-{\r
- errcode_t ret;\r
- flash_task_node task_node = {0};\r
- uint16_t len;\r
- kv_attributes_t attribute = 0;\r
-\r
- if (kvalue_length > NV_NORMAL_KVALUE_MAX_LEN) {\r
- return ERRCODE_NV_NO_ENOUGH_SPACE;\r
- }\r
-\r
- if (osal_sem_down_timeout(&nv_sem, 0xFFFFFFFF) != ERRCODE_SUCC) {\r
- return ERRCODE_NV_SEM_WAIT_ERR;\r
- }\r
-\r
- ret = kv_store_get_key_attr(KV_STORE_APPLICATION, key_id, &len, &attribute);\r
- if (ret == ERRCODE_SUCC) {\r
- if (((uint32_t)attribute & NV_ATTRIBUTE_ENCRYPTED) != 0) {\r
- /* When old key is encrypted, not permit to write */\r
- osal_sem_up(&nv_sem);\r
- return ERRCODE_NV_ILLEGAL_OPERATION;\r
- }\r
- }\r
-\r
- task_node.state_code = FLASH_TASK_READY;\r
- task_node.data.kv.key = key_id;\r
- task_node.data.kv.kvalue = kvalue;\r
- task_node.data.kv.kvalue_length = kvalue_length;\r
- task_node.data.kv.force_write = true;\r
- task_node.data.kv.attribute = (uint8_t)NV_ATTRIBUTE_NORMAL;\r
- ret = kv_update_write_key(KV_STORE_APPLICATION, &task_node);\r
- osal_sem_up(&nv_sem);\r
- if (ret != ERRCODE_SUCC) {\r
- return ret;\r
- }\r
-#if (CONFIG_NV_SUPPORT_CHANGE_NOTIFY == NV_YES)\r
- nv_change_notify(key_id);\r
-#endif\r
- return ERRCODE_SUCC;\r
-}\r
-#endif\r
-\r
-errcode_t nv_direct_get_key_data(\r
- uint16_t key_id, uint16_t kvalue_max_length, uint16_t *kvalue_length, uint8_t *kvalue, nv_key_attr_t *attr)\r
-{\r
- kv_attributes_t data_attribute = 0;\r
-\r
- if (osal_sem_down_timeout(&nv_sem, 0xFFFFFFFF) != ERRCODE_SUCC) {\r
- return ERRCODE_NV_SEM_WAIT_ERR;\r
- }\r
-\r
- kv_store_key_data_t key_data = {kvalue_max_length, 0, kvalue};\r
- errcode_t ret_val = kv_store_get_key(KV_STORE_APPLICATION, key_id, &key_data, &data_attribute);\r
- /* 如果在工作区没有找到该数据,可能存在数据被破坏情况,去备份区去找该数据 */\r
- if (ret_val != ERRCODE_SUCC) {\r
- ret_val = kv_store_read_backup_key(key_id, &key_data, &data_attribute);\r
- }\r
-\r
- if (ret_val == ERRCODE_SUCC) {\r
- if (((uint32_t)data_attribute & NV_ATTRIBUTE_PERMANENT) != 0) {\r
- attr->permanent = true;\r
- attr->non_upgrade = true;\r
- }\r
- if (((uint32_t)data_attribute & NV_ATTRIBUTE_ENCRYPTED) != 0) {\r
- attr->encrypted = true;\r
- attr->non_upgrade = true;\r
- }\r
- if (((uint32_t)data_attribute & NV_ATTRIBUTE_NON_UPGRADE) != 0) {\r
- attr->non_upgrade = true;\r
- }\r
- }\r
-\r
- *kvalue_length = key_data.kvalue_actual_length;\r
- osal_sem_up(&nv_sem);\r
- return ret_val;\r
-}\r
-\r
-#ifdef CONFIG_NV_SUPPORT_UPDATE_ATTR\r
-errcode_t nv_direct_update_key_attr(uint16_t key_id, nv_key_attr_t *attr, nv_storage_completed_callback func)\r
-{\r
- errcode_t ret_val;\r
- flash_task_node task_node = {0};\r
- uint16_t len;\r
- kv_attributes_t attribute = 0;\r
- nv_key_attr_t new_attr = {0};\r
- unused(func);\r
-\r
- memcpy_s(&new_attr, sizeof(nv_key_attr_t), attr, sizeof(nv_key_attr_t));\r
- if (osal_sem_down_timeout(&nv_sem, 0xFFFFFFFF) != ERRCODE_SUCC) {\r
- return ERRCODE_NV_SEM_WAIT_ERR;\r
- }\r
-\r
- ret_val = kv_store_get_key_attr(KV_STORE_APPLICATION, key_id, &len, &attribute);\r
- if (ret_val != ERRCODE_SUCC) {\r
- osal_sem_up(&nv_sem);\r
- return ret_val;\r
- }\r
-\r
- if (((uint32_t)attribute & NV_ATTRIBUTE_PERMANENT) != 0) {\r
- osal_sem_up(&nv_sem);\r
- return ERRCODE_SUCC;\r
- }\r
-\r
- if (((uint32_t)attribute & NV_ATTRIBUTE_ENCRYPTED) != 0) {\r
- /* if old key is encrypted, the new key must be encrypted too */\r
- new_attr.encrypted = true;\r
- }\r
-\r
- task_node.state_code = FLASH_TASK_READY;\r
- task_node.data.kv_attribute.key = key_id;\r
- task_node.data.kv_attribute.attribute = (uint8_t)nv_helper_convert_key_attr(&new_attr);\r
- ret_val = kv_update_modify_attribute(KV_STORE_APPLICATION, &task_node);\r
- osal_sem_up(&nv_sem);\r
- return ret_val;\r
-}\r
-#endif\r
-\r
-#ifdef CONFIG_NV_SUPPORT_DELETE_KEY\r
-errcode_t nv_direct_erase(uint16_t key_id)\r
-{\r
- errcode_t ret_val;\r
- uint16_t len = 0;\r
- kv_attributes_t attribute = 0;\r
- flash_task_node task_node = {0};\r
- if (osal_sem_down_timeout(&nv_sem, 0xFFFFFFFF) != ERRCODE_SUCC) {\r
- return ERRCODE_NV_SEM_WAIT_ERR;\r
- }\r
-\r
- ret_val = kv_store_get_key_attr(KV_STORE_APPLICATION, key_id, &len, &attribute);\r
- if (ret_val != ERRCODE_SUCC) {\r
- osal_sem_up(&nv_sem);\r
- return ret_val;\r
- }\r
-\r
- if (((uint32_t)attribute & KV_ATTRIBUTE_PERMANENT) != 0) {\r
- osal_sem_up(&nv_sem);\r
- return ERRCODE_NV_ILLEGAL_OPERATION;\r
- }\r
-\r
- task_node.data.kv_erase.key = key_id;\r
- ret_val = kv_update_erase_key(KV_STORE_APPLICATION, &task_node);\r
- osal_sem_up(&nv_sem);\r
- return ret_val;\r
-}\r
-#endif\r
-\r
-errcode_t nv_direct_get_store_status(nv_store_status_t *status)\r
-{\r
- errcode_t ret_val = ERRCODE_FAIL;\r
- if (osal_sem_down_timeout(&nv_sem, 0xFFFFFFFF) != ERRCODE_SUCC) {\r
- return ERRCODE_NV_SEM_WAIT_ERR;\r
- }\r
-\r
- memset_s(status, sizeof(nv_store_status_t), 0, sizeof(nv_store_status_t));\r
- ret_val = kv_store_get_status(KV_STORE_APPLICATION, status);\r
- osal_sem_up(&nv_sem);\r
- return ret_val;\r
-}\r
-\r
-errcode_t nv_direct_stored(uint16_t key_id, uint16_t kvalue_length, const uint8_t *kvalue)\r
-{\r
- errcode_t ret = ERRCODE_SUCC;\r
- uint8_t *stored_kvalue = NULL;\r
- kv_attributes_t attribute = 0;\r
-\r
- if (kvalue_length > NV_NORMAL_KVALUE_MAX_LEN) {\r
- return ERRCODE_NV_NO_ENOUGH_SPACE;\r
- }\r
-\r
- stored_kvalue = (uint8_t *)(uintptr_t)kv_malloc(kvalue_length);\r
- if (stored_kvalue == NULL) {\r
- return ERRCODE_MALLOC;\r
- }\r
-\r
- if (osal_sem_down_timeout(&nv_sem, 0xFFFFFFFF) != ERRCODE_SUCC) {\r
- kv_free(stored_kvalue);\r
- return ERRCODE_NV_SEM_WAIT_ERR;\r
- }\r
-\r
- kv_store_key_data_t key_data = {kvalue_length, 0, stored_kvalue};\r
- if (kv_store_get_key(KV_STORE_APPLICATION, key_id, &key_data, &attribute) != ERRCODE_SUCC) {\r
- ret = ERRCODE_FAIL;\r
- } else if ((key_data.kvalue_actual_length != kvalue_length) ||\r
- (memcmp((void *)stored_kvalue, (void *)kvalue, kvalue_length) != 0)) {\r
- ret = ERRCODE_FAIL;\r
- }\r
- osal_sem_up(&nv_sem);\r
- kv_free(stored_kvalue);\r
- return ret;\r
-}\r
-\r
-errcode_t nv_direct_backup_keys(const nv_backup_mode_t *backup_mode)\r
-{\r
-#if (CONFIG_NV_SUPPORT_BACKUP_RESTORE == NV_YES)\r
- errcode_t ret;\r
-\r
-#if (CONFIG_NV_SUPPORT_ASYNCHRONOUS_STORE == NV_YES)\r
- /* 保证备份前,NV消息队列中数据写入flash */\r
- ret = uapi_nv_flush();\r
- if (ret != ERRCODE_SUCC) {\r
- return ret;\r
- }\r
-#endif\r
-\r
- if (osal_sem_down_timeout(&nv_sem, 0xFFFFFFFF) != ERRCODE_SUCC) {\r
- return ERRCODE_NV_SEM_WAIT_ERR;\r
- }\r
-\r
- ret = kv_restore_set_region_flag(backup_mode->region_mode);\r
- if (ret != ERRCODE_SUCC) {\r
- osal_sem_up(&nv_sem);\r
- return ret;\r
- }\r
- ret = kv_backup_delete_repeat_key();\r
- if (ret != ERRCODE_SUCC) {\r
- osal_sem_up(&nv_sem);\r
- return ret;\r
- }\r
- ret = kv_backup_write_key();\r
- osal_sem_up(&nv_sem);\r
- return ret;\r
-#else\r
- unused(backup_mode);\r
- return ERRCODE_NOT_SUPPORT;\r
-#endif\r
-}\r
-\r
-errcode_t nv_direct_set_restore_flag_all(void)\r
-{\r
-#if (CONFIG_NV_SUPPORT_BACKUP_RESTORE == NV_YES)\r
- nv_reset_mode_t nv_reset_mode = {0};\r
- nv_reset_mode.mode = RESET_MODE_A;\r
- return kv_enable_restore_flag(&nv_reset_mode);\r
-\r
-#else\r
- return ERRCODE_NOT_SUPPORT;\r
-#endif\r
-}\r
-\r
-errcode_t nv_direct_set_restore_flag_partitial(const nv_restore_mode_t *nv_restore_mode)\r
-{\r
-#if (CONFIG_NV_SUPPORT_BACKUP_RESTORE == NV_YES)\r
- if (nv_restore_mode == NULL) {\r
- return ERRCODE_NV_INVALID_PARAMS;\r
- }\r
- nv_reset_mode_t nv_reset_mode = {0};\r
- if (memcpy_s(nv_reset_mode.region_flag, KEY_ID_REGION_MAX_NUM, nv_restore_mode, KEY_ID_REGION_MAX_NUM) != EOK) {\r
- return ERRCODE_MEMCPY;\r
- }\r
- nv_reset_mode.mode = RESET_MODE_B;\r
- return kv_enable_restore_flag(&nv_reset_mode);\r
-#else\r
- unused(nv_restore_mode);\r
- return ERRCODE_NOT_SUPPORT;\r
-#endif\r
-}\r
-\r
-#if (CONFIG_NV_SUPPORT_CHANGE_NOTIFY == NV_YES)\r
-errcode_t nv_direct_add_func_to_notify_list(uint16_t min_key, uint16_t max_key, nv_changed_notify_func func)\r
-{\r
- nv_direct_ctrl_t *nv_ctrl = nv_direct_get_nv_ctrl();\r
- if (osal_sem_down_timeout(&nv_sem, 0xFFFFFFFF) != ERRCODE_SUCC) {\r
- return ERRCODE_NV_SEM_WAIT_ERR;\r
- }\r
-\r
- nv_changed_proc_t *notify_list = nv_ctrl->nv_change_notify_list;\r
- if (notify_list == NULL) {\r
- osal_sem_up(&nv_sem);\r
- return ERRCODE_NV_NOT_INITIALISED;\r
- }\r
-\r
- if (nv_ctrl->notify_registered_nums >= nv_ctrl->notify_regitser_max_nums) {\r
- osal_sem_up(&nv_sem);\r
- return ERRCODE_NV_NOTIFY_LIST_FULL;\r
- }\r
-\r
- if (!nv_change_notify_segment_is_valid(nv_ctrl, min_key, max_key)) {\r
- osal_sem_up(&nv_sem);\r
- return ERRCODE_NV_NOTIFY_SEGMENT_ERR;\r
- }\r
-\r
- uint8_t new_index = nv_ctrl->notify_registered_nums;\r
- notify_list[new_index].min_key = min_key;\r
- notify_list[new_index].max_key = max_key;\r
- notify_list[new_index].func = func;\r
- nv_ctrl->notify_registered_nums++;\r
- osal_sem_up(&nv_sem);\r
- return ERRCODE_SUCC;\r
-}\r
-#endif\r
-\r
-void nv_direct_ctrl_init(void)\r
-{\r
- (void)kv_update_init((cores_t)KV_STORE_APPLICATION);\r
- nv_direct_ctrl_t *nv_ctrl = nv_direct_get_nv_ctrl();\r
- memset_s(nv_ctrl, sizeof(nv_direct_ctrl_t), 0, sizeof(nv_direct_ctrl_t));\r
- if (osal_sem_binary_sem_init(&nv_sem, MAX_BINARY_VAL) != ERRCODE_SUCC) {\r
- return;\r
- }\r
-#if (CONFIG_NV_SUPPORT_CHANGE_NOTIFY == NV_YES)\r
- (void)nv_direct_notify_list_init();\r
-#endif\r
-#if (CONFIG_NV_SUPPORT_BACKUP_RESTORE == NV_YES)\r
- (void)kv_update_backup_init();\r
-#endif\r
-#if (CONFIG_NV_SUPPORT_BACKUP_RESTORE == NV_YES)\r
- (void)kv_restore_all_keys();\r
-#endif\r
-#if (CONFIG_NV_SUPPORT_ASYNCHRONOUS_STORE == NV_YES)\r
- (void)nv_async_init(KV_STORE_APPLICATION, kv_store_get_page_count(KV_STORE_APPLICATION));\r
-#endif\r
- nv_log_info("nv init success!\r\n");\r
-}\r
+/*
+ * Copyright (c) @CompanyNameMagicTag 2016-2022. All rights reserved.
+ * Description: SOC KEY VALUE STORAGE IMPLEMENTATION
+ */
+
+#include "nv.h"
+#include "nv_storage.h"
+#include "nv_store.h"
+#include "nv_upg.h"
+#include "securec.h"
+#include "nv_porting.h"
+#include "common_def.h"
+#include "nv_reset.h"
+#include "nv_async_store.h"
+#include "osal_semaphore.h"
+#include "nv_update.h"
+#include "nv_notify.h"
+#include "nv_task_adapt.h"
+#include "nv_storage_handle.h"
+
+osal_semaphore nv_sem;
+#define MAX_BINARY_VAL 1
+
+STATIC nv_attributes_t nv_helper_convert_key_attr(const nv_key_attr_t *attr)
+{
+ nv_attributes_t attribute = NV_ATTRIBUTE_NORMAL;
+ bool is_support_crypt = false;
+ if (attr == NULL) {
+ return attribute;
+ }
+
+#if (CONFIG_NV_SUPPORT_ENCRYPT == NV_YES)
+ is_support_crypt = attr->encrypted;
+#endif
+
+ if (attr->permanent && is_support_crypt) {
+ attribute = NV_ATTRIBUTE_ENCRYPTED | NV_ATTRIBUTE_PERMANENT | NV_ATTRIBUTE_NON_UPGRADE;
+ } else if (attr->permanent) {
+ attribute = NV_ATTRIBUTE_PERMANENT | NV_ATTRIBUTE_NON_UPGRADE;
+ } else if (is_support_crypt) {
+ attribute = NV_ATTRIBUTE_ENCRYPTED | NV_ATTRIBUTE_NON_UPGRADE;
+ } else if (attr->non_upgrade) {
+ attribute = NV_ATTRIBUTE_NON_UPGRADE;
+ }
+ return attribute;
+}
+
+static uint16_t nv_storage_max_key_space(const nv_key_attr_t *attr)
+{
+ if (attr == NULL) {
+ return NV_NORMAL_KVALUE_MAX_LEN;
+ }
+ return (attr->encrypted ? NV_ENCRYPTED_KVALUE_MAX_LEN : NV_NORMAL_KVALUE_MAX_LEN);
+}
+
+nv_direct_ctrl_t g_nv_direct_ctrl;
+nv_direct_ctrl_t *nv_direct_get_nv_ctrl(void)
+{
+ return &g_nv_direct_ctrl;
+}
+
+errcode_t nv_direct_get_key_attr(uint16_t key_id, uint16_t *len, nv_key_attr_t *attr)
+{
+ kv_attributes_t get_attribute = 0;
+ errcode_t ret = ERRCODE_SUCC;
+
+ if (len == NULL || attr == NULL) {
+ return ERRCODE_NV_INVALID_PARAMS;
+ }
+
+ if (osal_sem_down_timeout(&nv_sem, 0xFFFFFFFF) != ERRCODE_SUCC) {
+ return ERRCODE_NV_SEM_WAIT_ERR;
+ }
+
+ *len = 0;
+ memset_s(attr, sizeof(nv_key_attr_t), 0, sizeof(nv_key_attr_t));
+ ret = kv_store_get_key_attr(KV_STORE_APPLICATION, key_id, len, &get_attribute);
+ if (ret == ERRCODE_SUCC) {
+ if (((uint32_t)get_attribute & NV_ATTRIBUTE_PERMANENT) != 0) {
+ attr->permanent = true;
+ attr->non_upgrade = true;
+ }
+ if (((uint32_t)get_attribute & NV_ATTRIBUTE_ENCRYPTED) != 0) {
+ attr->encrypted = true;
+ attr->non_upgrade = true;
+ }
+ if (((uint32_t)get_attribute & NV_ATTRIBUTE_NON_UPGRADE) != 0) {
+ attr->non_upgrade = true;
+ }
+ }
+ osal_sem_up(&nv_sem);
+ return ret;
+}
+
+errcode_t nv_direct_write_with_attr(uint16_t key_id, const uint8_t *kvalue, uint16_t kvalue_length, nv_key_attr_t *attr,
+ nv_storage_completed_callback func)
+{
+ errcode_t ret;
+ flash_task_node task_node = {0};
+ uint16_t len;
+ kv_attributes_t attribute = (kv_attributes_t)0;
+ unused(func);
+
+ if (kvalue_length > nv_storage_max_key_space(attr)) {
+ nv_log_err("[NV] nv_direct_write_with_attr: len exceeds the max size. key_id = 0x%x\r\n", key_id);
+ return ERRCODE_NV_NO_ENOUGH_SPACE;
+ }
+
+ if (osal_sem_down_timeout(&nv_sem, 0xFFFFFFFF) != ERRCODE_SUCC) {
+ nv_log_err("[NV] nv_direct_write_with_attr: semaphore error!\r\n");
+ return ERRCODE_NV_SEM_WAIT_ERR;
+ }
+
+ ret = kv_store_get_key_attr(KV_STORE_APPLICATION, key_id, &len, &attribute);
+ if (ret == ERRCODE_SUCC) {
+ if ((((uint32_t)attribute & NV_ATTRIBUTE_PERMANENT) != 0) ||
+ ((((uint32_t)attribute & NV_ATTRIBUTE_ENCRYPTED) != 0) && (attr != NULL && attr->encrypted == false))
+ || ((((uint32_t)attribute & NV_ATTRIBUTE_NON_UPGRADE) != 0) && (attr == NULL))) {
+ /* When old key is permanent, not permit to write */
+ /* When old key is encrypted, new key is non-encrypted, not permit to write */
+ /* When old key is non upgrade, new key is write by ordinary write, not permit to write */
+ nv_log_err("[NV] nv_direct_write_with_attr: operation not allowed! key_id = 0x%x\r\n", key_id);
+ osal_sem_up(&nv_sem);
+ return ERRCODE_NV_ILLEGAL_OPERATION;
+ }
+ }
+ if (attr != NULL) {
+ attribute = (uint8_t)nv_helper_convert_key_attr(attr);
+ }
+ task_node.state_code = FLASH_TASK_READY;
+ task_node.data.kv.key = key_id;
+ task_node.data.kv.kvalue = kvalue;
+ task_node.data.kv.kvalue_length = kvalue_length;
+ task_node.data.kv.force_write = false;
+ task_node.data.kv.attribute = attribute;
+ ret = kv_update_write_key(KV_STORE_APPLICATION, &task_node);
+ osal_sem_up(&nv_sem);
+ if (ret != ERRCODE_SUCC) {
+ nv_log_err("[NV] nv_direct_write_with_attr failed. key_id = 0x%x, ret = 0x%x\r\n", key_id, ret);
+ return ret;
+ }
+#if (CONFIG_NV_SUPPORT_CHANGE_NOTIFY == NV_YES)
+ nv_change_notify(key_id);
+#endif
+ return ERRCODE_SUCC;
+}
+
+#ifdef CONFIG_NV_SUPPORT_WRITE_FORCE
+errcode_t nv_direct_write_force(uint16_t key_id, const uint8_t *kvalue, uint16_t kvalue_length)
+{
+ errcode_t ret;
+ flash_task_node task_node = {0};
+ uint16_t len;
+ kv_attributes_t attribute = 0;
+
+ if (kvalue_length > NV_NORMAL_KVALUE_MAX_LEN) {
+ return ERRCODE_NV_NO_ENOUGH_SPACE;
+ }
+
+ if (osal_sem_down_timeout(&nv_sem, 0xFFFFFFFF) != ERRCODE_SUCC) {
+ return ERRCODE_NV_SEM_WAIT_ERR;
+ }
+
+ ret = kv_store_get_key_attr(KV_STORE_APPLICATION, key_id, &len, &attribute);
+ if (ret == ERRCODE_SUCC) {
+ if (((uint32_t)attribute & NV_ATTRIBUTE_ENCRYPTED) != 0) {
+ /* When old key is encrypted, not permit to write */
+ osal_sem_up(&nv_sem);
+ return ERRCODE_NV_ILLEGAL_OPERATION;
+ }
+ }
+
+ task_node.state_code = FLASH_TASK_READY;
+ task_node.data.kv.key = key_id;
+ task_node.data.kv.kvalue = kvalue;
+ task_node.data.kv.kvalue_length = kvalue_length;
+ task_node.data.kv.force_write = true;
+ task_node.data.kv.attribute = (uint8_t)NV_ATTRIBUTE_NORMAL;
+ ret = kv_update_write_key(KV_STORE_APPLICATION, &task_node);
+ osal_sem_up(&nv_sem);
+ if (ret != ERRCODE_SUCC) {
+ return ret;
+ }
+#if (CONFIG_NV_SUPPORT_CHANGE_NOTIFY == NV_YES)
+ nv_change_notify(key_id);
+#endif
+ return ERRCODE_SUCC;
+}
+#endif
+
+errcode_t nv_direct_get_key_data(
+ uint16_t key_id, uint16_t kvalue_max_length, uint16_t *kvalue_length, uint8_t *kvalue, nv_key_attr_t *attr)
+{
+ kv_attributes_t data_attribute = 0;
+
+ if (osal_sem_down_timeout(&nv_sem, 0xFFFFFFFF) != ERRCODE_SUCC) {
+ return ERRCODE_NV_SEM_WAIT_ERR;
+ }
+
+ kv_store_key_data_t key_data = {kvalue_max_length, 0, kvalue};
+ errcode_t ret_val = kv_store_get_key(KV_STORE_APPLICATION, key_id, &key_data, &data_attribute);
+ /* 如果在工作区没有找到该数据,可能存在数据被破坏情况,去备份区去找该数据 */
+ if (ret_val != ERRCODE_SUCC) {
+ ret_val = kv_store_read_backup_key(key_id, &key_data, &data_attribute);
+ }
+
+ if (ret_val == ERRCODE_SUCC) {
+ if (((uint32_t)data_attribute & NV_ATTRIBUTE_PERMANENT) != 0) {
+ attr->permanent = true;
+ attr->non_upgrade = true;
+ }
+ if (((uint32_t)data_attribute & NV_ATTRIBUTE_ENCRYPTED) != 0) {
+ attr->encrypted = true;
+ attr->non_upgrade = true;
+ }
+ if (((uint32_t)data_attribute & NV_ATTRIBUTE_NON_UPGRADE) != 0) {
+ attr->non_upgrade = true;
+ }
+ }
+
+ *kvalue_length = key_data.kvalue_actual_length;
+ osal_sem_up(&nv_sem);
+ return ret_val;
+}
+
+#ifdef CONFIG_NV_SUPPORT_UPDATE_ATTR
+errcode_t nv_direct_update_key_attr(uint16_t key_id, nv_key_attr_t *attr, nv_storage_completed_callback func)
+{
+ errcode_t ret_val;
+ flash_task_node task_node = {0};
+ uint16_t len;
+ kv_attributes_t attribute = 0;
+ nv_key_attr_t new_attr = {0};
+ unused(func);
+
+ memcpy_s(&new_attr, sizeof(nv_key_attr_t), attr, sizeof(nv_key_attr_t));
+ if (osal_sem_down_timeout(&nv_sem, 0xFFFFFFFF) != ERRCODE_SUCC) {
+ return ERRCODE_NV_SEM_WAIT_ERR;
+ }
+
+ ret_val = kv_store_get_key_attr(KV_STORE_APPLICATION, key_id, &len, &attribute);
+ if (ret_val != ERRCODE_SUCC) {
+ osal_sem_up(&nv_sem);
+ return ret_val;
+ }
+
+ if (((uint32_t)attribute & NV_ATTRIBUTE_PERMANENT) != 0) {
+ osal_sem_up(&nv_sem);
+ return ERRCODE_SUCC;
+ }
+
+ if (((uint32_t)attribute & NV_ATTRIBUTE_ENCRYPTED) != 0) {
+ /* if old key is encrypted, the new key must be encrypted too */
+ new_attr.encrypted = true;
+ }
+
+ task_node.state_code = FLASH_TASK_READY;
+ task_node.data.kv_attribute.key = key_id;
+ task_node.data.kv_attribute.attribute = (uint8_t)nv_helper_convert_key_attr(&new_attr);
+ ret_val = kv_update_modify_attribute(KV_STORE_APPLICATION, &task_node);
+ osal_sem_up(&nv_sem);
+ return ret_val;
+}
+#endif
+
+#ifdef CONFIG_NV_SUPPORT_DELETE_KEY
+errcode_t nv_direct_erase(uint16_t key_id)
+{
+ errcode_t ret_val;
+ uint16_t len = 0;
+ kv_attributes_t attribute = 0;
+ flash_task_node task_node = {0};
+ if (osal_sem_down_timeout(&nv_sem, 0xFFFFFFFF) != ERRCODE_SUCC) {
+ return ERRCODE_NV_SEM_WAIT_ERR;
+ }
+
+ ret_val = kv_store_get_key_attr(KV_STORE_APPLICATION, key_id, &len, &attribute);
+ if (ret_val != ERRCODE_SUCC) {
+ osal_sem_up(&nv_sem);
+ return ret_val;
+ }
+
+ if (((uint32_t)attribute & KV_ATTRIBUTE_PERMANENT) != 0) {
+ osal_sem_up(&nv_sem);
+ return ERRCODE_NV_ILLEGAL_OPERATION;
+ }
+
+ task_node.data.kv_erase.key = key_id;
+ ret_val = kv_update_erase_key(KV_STORE_APPLICATION, &task_node);
+ osal_sem_up(&nv_sem);
+ return ret_val;
+}
+#endif
+
+errcode_t nv_direct_get_store_status(nv_store_status_t *status)
+{
+ errcode_t ret_val = ERRCODE_FAIL;
+ if (osal_sem_down_timeout(&nv_sem, 0xFFFFFFFF) != ERRCODE_SUCC) {
+ return ERRCODE_NV_SEM_WAIT_ERR;
+ }
+
+ memset_s(status, sizeof(nv_store_status_t), 0, sizeof(nv_store_status_t));
+ ret_val = kv_store_get_status(KV_STORE_APPLICATION, status);
+ osal_sem_up(&nv_sem);
+ return ret_val;
+}
+
+errcode_t nv_direct_stored(uint16_t key_id, uint16_t kvalue_length, const uint8_t *kvalue)
+{
+ errcode_t ret = ERRCODE_SUCC;
+ uint8_t *stored_kvalue = NULL;
+ kv_attributes_t attribute = 0;
+
+ if (kvalue_length > NV_NORMAL_KVALUE_MAX_LEN) {
+ return ERRCODE_NV_NO_ENOUGH_SPACE;
+ }
+
+ stored_kvalue = (uint8_t *)(uintptr_t)kv_malloc(kvalue_length);
+ if (stored_kvalue == NULL) {
+ return ERRCODE_MALLOC;
+ }
+
+ if (osal_sem_down_timeout(&nv_sem, 0xFFFFFFFF) != ERRCODE_SUCC) {
+ kv_free(stored_kvalue);
+ return ERRCODE_NV_SEM_WAIT_ERR;
+ }
+
+ kv_store_key_data_t key_data = {kvalue_length, 0, stored_kvalue};
+ if (kv_store_get_key(KV_STORE_APPLICATION, key_id, &key_data, &attribute) != ERRCODE_SUCC) {
+ ret = ERRCODE_FAIL;
+ } else if ((key_data.kvalue_actual_length != kvalue_length) ||
+ (memcmp((void *)stored_kvalue, (void *)kvalue, kvalue_length) != 0)) {
+ ret = ERRCODE_FAIL;
+ }
+ osal_sem_up(&nv_sem);
+ kv_free(stored_kvalue);
+ return ret;
+}
+
+errcode_t nv_direct_backup_keys(const nv_backup_mode_t *backup_mode)
+{
+#if (CONFIG_NV_SUPPORT_BACKUP_RESTORE == NV_YES)
+ errcode_t ret;
+
+#if (CONFIG_NV_SUPPORT_ASYNCHRONOUS_STORE == NV_YES)
+ /* 保证备份前,NV消息队列中数据写入flash */
+ ret = uapi_nv_flush();
+ if (ret != ERRCODE_SUCC) {
+ return ret;
+ }
+#endif
+
+ if (osal_sem_down_timeout(&nv_sem, 0xFFFFFFFF) != ERRCODE_SUCC) {
+ return ERRCODE_NV_SEM_WAIT_ERR;
+ }
+
+ ret = kv_restore_set_region_flag(backup_mode->region_mode);
+ if (ret != ERRCODE_SUCC) {
+ osal_sem_up(&nv_sem);
+ return ret;
+ }
+ ret = kv_backup_delete_repeat_key();
+ if (ret != ERRCODE_SUCC) {
+ osal_sem_up(&nv_sem);
+ return ret;
+ }
+ ret = kv_backup_write_key();
+ osal_sem_up(&nv_sem);
+ return ret;
+#else
+ unused(backup_mode);
+ return ERRCODE_NOT_SUPPORT;
+#endif
+}
+
+errcode_t nv_direct_set_restore_flag_all(void)
+{
+#if (CONFIG_NV_SUPPORT_BACKUP_RESTORE == NV_YES)
+ nv_reset_mode_t nv_reset_mode = {0};
+ nv_reset_mode.mode = RESET_MODE_A;
+ return kv_enable_restore_flag(&nv_reset_mode);
+
+#else
+ return ERRCODE_NOT_SUPPORT;
+#endif
+}
+
+errcode_t nv_direct_set_restore_flag_partitial(const nv_restore_mode_t *nv_restore_mode)
+{
+#if (CONFIG_NV_SUPPORT_BACKUP_RESTORE == NV_YES)
+ if (nv_restore_mode == NULL) {
+ return ERRCODE_NV_INVALID_PARAMS;
+ }
+ nv_reset_mode_t nv_reset_mode = {0};
+ if (memcpy_s(nv_reset_mode.region_flag, KEY_ID_REGION_MAX_NUM, nv_restore_mode, KEY_ID_REGION_MAX_NUM) != EOK) {
+ return ERRCODE_MEMCPY;
+ }
+ nv_reset_mode.mode = RESET_MODE_B;
+ return kv_enable_restore_flag(&nv_reset_mode);
+#else
+ unused(nv_restore_mode);
+ return ERRCODE_NOT_SUPPORT;
+#endif
+}
+
+#if (CONFIG_NV_SUPPORT_CHANGE_NOTIFY == NV_YES)
+errcode_t nv_direct_add_func_to_notify_list(uint16_t min_key, uint16_t max_key, nv_changed_notify_func func)
+{
+ nv_direct_ctrl_t *nv_ctrl = nv_direct_get_nv_ctrl();
+ if (osal_sem_down_timeout(&nv_sem, 0xFFFFFFFF) != ERRCODE_SUCC) {
+ return ERRCODE_NV_SEM_WAIT_ERR;
+ }
+
+ nv_changed_proc_t *notify_list = nv_ctrl->nv_change_notify_list;
+ if (notify_list == NULL) {
+ osal_sem_up(&nv_sem);
+ return ERRCODE_NV_NOT_INITIALISED;
+ }
+
+ if (nv_ctrl->notify_registered_nums >= nv_ctrl->notify_regitser_max_nums) {
+ osal_sem_up(&nv_sem);
+ return ERRCODE_NV_NOTIFY_LIST_FULL;
+ }
+
+ if (!nv_change_notify_segment_is_valid(nv_ctrl, min_key, max_key)) {
+ osal_sem_up(&nv_sem);
+ return ERRCODE_NV_NOTIFY_SEGMENT_ERR;
+ }
+
+ uint8_t new_index = nv_ctrl->notify_registered_nums;
+ notify_list[new_index].min_key = min_key;
+ notify_list[new_index].max_key = max_key;
+ notify_list[new_index].func = func;
+ nv_ctrl->notify_registered_nums++;
+ osal_sem_up(&nv_sem);
+ return ERRCODE_SUCC;
+}
+#endif
+
+void nv_direct_ctrl_init(void)
+{
+ (void)kv_update_init((cores_t)KV_STORE_APPLICATION);
+ nv_direct_ctrl_t *nv_ctrl = nv_direct_get_nv_ctrl();
+ memset_s(nv_ctrl, sizeof(nv_direct_ctrl_t), 0, sizeof(nv_direct_ctrl_t));
+ if (osal_sem_binary_sem_init(&nv_sem, MAX_BINARY_VAL) != ERRCODE_SUCC) {
+ return;
+ }
+#if (CONFIG_NV_SUPPORT_CHANGE_NOTIFY == NV_YES)
+ (void)nv_direct_notify_list_init();
+#endif
+#if (CONFIG_NV_SUPPORT_BACKUP_RESTORE == NV_YES)
+ (void)kv_update_backup_init();
+#endif
+#if (CONFIG_NV_SUPPORT_BACKUP_RESTORE == NV_YES)
+ (void)kv_restore_all_keys();
+#endif
+#if (CONFIG_NV_SUPPORT_ASYNCHRONOUS_STORE == NV_YES)
+ (void)nv_async_init(KV_STORE_APPLICATION, kv_store_get_page_count(KV_STORE_APPLICATION));
+#endif
+ nv_log_info("nv init success!\r\n");
+}
-/*\r
- * Copyright (c) @CompanyNameMagicTag 2023-2023. All rights reserved.\r
- * Description: NV Storage Library debug\r
- */\r
-\r
-#include "nv_debug.h"\r
-#include "nv_porting.h"\r
-#include "common_def.h"\r
-\r
-STATIC uint32_t g_nv_debug_level = NV_LOG_LEVEL_INFO;\r
-\r
-uint32_t nv_get_debug_level(void)\r
-{\r
- return g_nv_debug_level;\r
-}\r
-\r
-void nv_set_debug_level(uint32_t level)\r
-{\r
- g_nv_debug_level = level;\r
-}\r
+/*
+ * Copyright (c) @CompanyNameMagicTag 2023-2023. All rights reserved.
+ * Description: NV Storage Library debug
+ */
+
+#include "nv_debug.h"
+#include "nv_porting.h"
+#include "common_def.h"
+
+STATIC uint32_t g_nv_debug_level = NV_LOG_LEVEL_DBG;//NV_LOG_LEVEL_INFO;
+
+uint32_t nv_get_debug_level(void)
+{
+ return g_nv_debug_level;
+}
+
+void nv_set_debug_level(uint32_t level)
+{
+ g_nv_debug_level = level;
+}
-/*\r
- * Copyright (c) @CompanyNameMagicTag 2019-2023. All rights reserved.\r
- * Description: NV Storage Library debug\r
- */\r
-\r
-#ifndef NV_DEBUG_H\r
-#define NV_DEBUG_H\r
-\r
-#include "stdint.h"\r
-#include "errcode.h"\r
-\r
-#ifdef __cplusplus\r
-#if __cplusplus\r
-extern "C" {\r
-#endif /* __cplusplus */\r
-#endif /* __cplusplus */\r
-\r
-#define NV_LOG_LEVEL_ALERT 0\r
-#define NV_LOG_LEVEL_FATAL 1\r
-#define NV_LOG_LEVEL_ERROR 2\r
-#define NV_LOG_LEVEL_WARN 3\r
-#define NV_LOG_LEVEL_NOTICE 4\r
-#define NV_LOG_LEVEL_INFO 5\r
-#define NV_LOG_LEVEL_DBG 6\r
-#define NV_LOG_LEVEL_TRACE 7\r
-\r
-uint32_t nv_get_debug_level(void);\r
-\r
-void nv_set_debug_level(uint32_t level);\r
-\r
-#ifdef __cplusplus\r
-#if __cplusplus\r
-}\r
-#endif /* __cplusplus */\r
-#endif /* __cplusplus */\r
-\r
-#endif /* NV_DEBUG_H */\r
+/*
+ * Copyright (c) @CompanyNameMagicTag 2019-2023. All rights reserved.
+ * Description: NV Storage Library debug
+ */
+
+#ifndef NV_DEBUG_H
+#define NV_DEBUG_H
+
+#include "stdint.h"
+#include "errcode.h"
+
+#ifdef __cplusplus
+#if __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+#endif /* __cplusplus */
+
+#define NV_LOG_LEVEL_ALERT 0
+#define NV_LOG_LEVEL_FATAL 1
+#define NV_LOG_LEVEL_ERROR 2
+#define NV_LOG_LEVEL_WARN 3
+#define NV_LOG_LEVEL_NOTICE 4
+#define NV_LOG_LEVEL_INFO 5
+#define NV_LOG_LEVEL_DBG 6
+#define NV_LOG_LEVEL_TRACE 7
+
+uint32_t nv_get_debug_level(void);
+
+void nv_set_debug_level(uint32_t level);
+
+#ifdef __cplusplus
+#if __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* __cplusplus */
+
+#endif /* NV_DEBUG_H */
-/*\r
- * Copyright (c) @CompanyNameMagicTag 2019-2022. All rights reserved.\r
- * Description: KV Storage Library key access module implementation\r
- */\r
-\r
-#include "nv_key.h"\r
-#include "string.h"\r
-#include "nv_porting.h"\r
-#include "uapi_crc.h"\r
-#include "nv_nvregion.h"\r
-#include "common_def.h"\r
-\r
-#define KV_CLEARTEXT_PADDING_SIZE (1UL << 2) /* Currently these both need to be a power of 2... */\r
-#define KV_CIPHERTEXT_PADDING_SIZE (1UL << 4) /* ...due to implementation of kv_padded_length */\r
-#define kv_padded_length(d, s) (((d) + ((s) - 1)) & ~((s) - 1))\r
-#define kv_padded_cleartext_length(d) kv_padded_length(d, KV_CLEARTEXT_PADDING_SIZE)\r
-#define kv_padded_ciphertext_length(d) kv_padded_length(d, KV_CIPHERTEXT_PADDING_SIZE)\r
-#define KV_DMA_COPY_SIZE_ALIGN 32\r
-\r
-uint8_t g_nv_header_magic = KV_KEY_MAGIC;\r
-\r
-/* IOT-16672 Use ROM Lib function instead (enhanced to support KV requirements, as here) */\r
-errcode_t kv_key_helper_copy_flash(uint32_t dest_location, uint32_t src_location, uint16_t length)\r
-{\r
- if (length == 0) {\r
- return ERRCODE_NV_ZERO_LENGTH_COPY;\r
- }\r
- uint32_t src_addr = src_location - FLASH_PHYSICAL_ADDR_START;\r
- uint16_t length_rounded = length & ~(KV_DMA_COPY_SIZE_ALIGN - 1);\r
- uint16_t length_remainder = length & (KV_DMA_COPY_SIZE_ALIGN - 1);\r
- uint16_t dest_offset = 0;\r
- while (length_rounded > 0) {\r
- if (kv_flash_read((uint32_t)(uintptr_t)src_addr, KV_DMA_COPY_SIZE_ALIGN,\r
- (uint8_t *)(uintptr_t)(dest_location + dest_offset)) != ERRCODE_SUCC) {\r
- return ERRCODE_NV_READ_FLASH_ERR;\r
- }\r
- dest_offset += KV_DMA_COPY_SIZE_ALIGN;\r
- src_addr += KV_DMA_COPY_SIZE_ALIGN;\r
- length_rounded -= KV_DMA_COPY_SIZE_ALIGN;\r
- }\r
-\r
- if (length_remainder > 0) {\r
- if (kv_flash_read((uint32_t)(uintptr_t)src_addr, length_remainder,\r
- (uint8_t *)(uintptr_t)(dest_location + dest_offset)) != ERRCODE_SUCC) {\r
- return ERRCODE_NV_READ_FLASH_ERR;\r
- }\r
- }\r
- return ERRCODE_SUCC;\r
-}\r
-\r
-#ifdef CONFIG_NV_SUPPORT_SINGLE_CORE_SYSTEM\r
-errcode_t kv_key_direct_write_flash(uint32_t dest, uint32_t length, const uint8_t *src)\r
-{\r
- uint32_t dest_w = dest;\r
- if ((dest >= FLASH_PHYSICAL_ADDR_START) &&\r
- ((dest + length) <= FLASH_PHYSICAL_ADDR_END)) {\r
- dest_w -= FLASH_PHYSICAL_ADDR_START;\r
- } else {\r
- return ERRCODE_FLASH_INVALID_PARAM_BEYOND_ADDR;\r
- }\r
-\r
- if (kv_flash_write(dest_w, length, src, false) != ERRCODE_SUCC) {\r
- return ERRCODE_NV_READ_FLASH_ERR;\r
- }\r
-\r
- return ERRCODE_SUCC;\r
-}\r
-\r
-errcode_t kv_key_direct_erase_flash(uint32_t dest, const uint32_t size)\r
-{\r
- uint32_t dest_w = dest;\r
- if ((dest >= FLASH_PHYSICAL_ADDR_START) &&\r
- ((dest + size) <= FLASH_PHYSICAL_ADDR_END)) {\r
- dest_w -= FLASH_PHYSICAL_ADDR_START;\r
- } else {\r
- return ERRCODE_FLASH_INVALID_PARAM_BEYOND_ADDR;\r
- }\r
-\r
- if (kv_flash_erase(dest_w, size) != ERRCODE_SUCC) {\r
- return ERRCODE_FAIL;\r
- }\r
-\r
- return ERRCODE_SUCC;\r
-}\r
-#endif\r
-\r
-errcode_t kv_key_write_flash(uint32_t dest, uint32_t length, const uint8_t *src)\r
-{\r
-#ifdef CONFIG_NV_SUPPORT_SINGLE_CORE_SYSTEM\r
- return kv_key_direct_write_flash(dest, length, src);\r
-#else\r
- return flash_task_drv_write(dest, length, src);\r
-#endif\r
-}\r
-\r
-errcode_t kv_key_erase_flash(uint32_t dest, const uint32_t size)\r
-{\r
-#ifdef CONFIG_NV_SUPPORT_SINGLE_CORE_SYSTEM\r
- return kv_key_direct_erase_flash(dest, size);\r
-#else\r
- return flash_task_drv_erase(dest, size);\r
-#endif\r
-}\r
-\r
-#if (CONFIG_NV_SUPPORT_ENCRYPT == NV_YES)\r
-STATIC errcode_t kv_key_decode_flash(uintptr_t dest, const uintptr_t src, uint32_t length, uint32_t crypto_handle)\r
-{\r
- errcode_t ret;\r
- uint8_t *kv_padded_data = NULL;\r
-\r
- uint32_t padded_length = kv_key_padded_data_length(KV_ATTRIBUTE_ENCRYPTED, (uint16_t)length);\r
- if (padded_length == length) {\r
- ret = kv_key_helper_copy_flash((uint32_t)dest, (uint32_t)src, (uint16_t)length);\r
- if (ret == ERRCODE_SUCC) {\r
- return nv_crypto_decode(crypto_handle, dest, dest, length);\r
- } else {\r
- return ret;\r
- }\r
- } else {\r
- kv_padded_data = (uint8_t *)kv_malloc(padded_length);\r
- if (kv_padded_data == NULL) {\r
- return ERRCODE_MALLOC;\r
- }\r
- ret = kv_key_helper_copy_flash((uint32_t)(uintptr_t)kv_padded_data, (uint32_t)(uintptr_t)src,\r
- (uint16_t)padded_length);\r
- if (ret != ERRCODE_SUCC) {\r
- goto end;\r
- }\r
- ret = nv_crypto_decode(crypto_handle, (uintptr_t)kv_padded_data, (uintptr_t)kv_padded_data, padded_length);\r
- if (ret != ERRCODE_SUCC) {\r
- goto end;\r
- }\r
- if (memcpy_s((void *)dest, length, kv_padded_data, length) != EOK) {\r
- ret = ERRCODE_MEMCPY;\r
- goto end;\r
- }\r
- }\r
-end:\r
- kv_free(kv_padded_data);\r
- return ret;\r
-}\r
-#endif\r
-\r
-STATIC errcode_t kv_key_read_data_from_flash(uintptr_t dest, const uintptr_t src, uint32_t length,\r
- uint32_t crypto_handle)\r
-{\r
- if (crypto_handle == INVAILD_CRYPTO_HANDLE) {\r
- /* Decrypt not required, just return */\r
- return kv_key_helper_copy_flash((uint32_t)dest, (uint32_t)src, (uint16_t)length);\r
- } else {\r
- /* Data needs decrypting */\r
-#if (CONFIG_NV_SUPPORT_ENCRYPT == NV_YES)\r
- return kv_key_decode_flash(dest, src, length, crypto_handle);\r
-#else\r
- return ERRCODE_FAIL;\r
-#endif\r
- }\r
-}\r
-\r
-errcode_t kv_key_read_data(kv_key_handle_t *key, uint8_t *dest_location)\r
-{\r
- errcode_t res = ERRCODE_SUCC;\r
- uint32_t crypto_handle = INVAILD_CRYPTO_HANDLE;\r
- uint32_t key_data_offset = 0;\r
- uint32_t key_data_location = (uint32_t)(uintptr_t)key->key_location + (uint32_t)sizeof(kv_key_header_t);\r
- uint32_t decryptable_data_len = key->header.length;\r
- /* Check whether data is encrypted */\r
-#if (CONFIG_NV_SUPPORT_ENCRYPT == NV_YES)\r
- if (key->header.enc_key == AES_KDFKEY_SDRK_TYPE) {\r
- /* Read key data, using AES engine if encrypted */\r
- res = nv_crypto_claim_aes(&crypto_handle, &(key->header));\r
- if (res != ERRCODE_SUCC) {\r
- nv_crypto_release_aes(crypto_handle);\r
- return res;\r
- }\r
- }\r
-#endif\r
-\r
- /* Read key data, using AES engine if encrypted */\r
- while (key_data_offset < decryptable_data_len) {\r
- uint32_t chunk_len = uapi_min(NV_KEY_DATA_CHUNK_LEN, decryptable_data_len - key_data_offset);\r
-\r
- res = kv_key_read_data_from_flash((uintptr_t)(dest_location + key_data_offset),\r
- (uintptr_t)key_data_location + key_data_offset,\r
- chunk_len, crypto_handle);\r
- if (res != ERRCODE_SUCC) {\r
- break;\r
- }\r
- key_data_offset += chunk_len;\r
- }\r
-#if (CONFIG_NV_SUPPORT_ENCRYPT == NV_YES)\r
- if (crypto_handle != INVAILD_CRYPTO_HANDLE) {\r
- nv_crypto_release_aes(crypto_handle);\r
- }\r
-#endif\r
- return res;\r
-}\r
-\r
-STATIC errcode_t kv_helper_compare_key_data_chunks(const kv_key_handle_t *key, const uint8_t *compare_data,\r
- kv_helper_compare_key_data_info_t *info)\r
-{\r
- errcode_t res = ERRCODE_SUCC;\r
-\r
- uintptr_t key_data_location = (uintptr_t)key->key_location + (uintptr_t)sizeof(kv_key_header_t);\r
- uint32_t key_data_offset = 0;\r
- while (key_data_offset < key->header.length) {\r
- const uint16_t chunk_len = (uint16_t)uapi_min(NV_KEY_DATA_CHUNK_LEN, key->header.length - key_data_offset);\r
- res = kv_key_read_data_from_flash((uintptr_t)info->key_data_chunk, (key_data_location + key_data_offset),\r
- chunk_len, info->crypto_handle);\r
- if (res != ERRCODE_SUCC) {\r
- break;\r
- }\r
-\r
- uint32_t compare_length = uapi_min(chunk_len, key->header.length - key_data_offset);\r
- if (info->compare_data_chunk != NULL) {\r
- /* Compare data in flash */\r
- res = kv_key_helper_copy_flash((uint32_t)(uintptr_t)info->compare_data_chunk,\r
- (uint32_t)(uintptr_t)compare_data + key_data_offset,\r
- (uint16_t)compare_length);\r
- if (res != ERRCODE_SUCC) {\r
- break;\r
- }\r
- if (memcmp(info->key_data_chunk, info->compare_data_chunk, compare_length) != 0) {\r
- return ERRCODE_NV_DATA_MISMATCH;\r
- }\r
- } else {\r
- /* Compare data in RAM */\r
- if (memcmp(info->key_data_chunk, compare_data + key_data_offset, compare_length) != 0) {\r
- return ERRCODE_NV_DATA_MISMATCH;\r
- }\r
- }\r
- key_data_offset += chunk_len;\r
- }\r
-\r
- return res;\r
-}\r
-\r
-errcode_t kv_helper_compare_key_data(kv_key_handle_t *key, const uint8_t *compare_data, uint16_t compare_length)\r
-{\r
- kv_helper_compare_key_data_info_t info = {INVAILD_CRYPTO_HANDLE, NULL, NULL};\r
- errcode_t res;\r
-\r
- if (key->header.length != compare_length) {\r
- return ERRCODE_NV_LENGTH_MISMATCH;\r
- }\r
-\r
- /* Key data *will* be in flash and could be encrypted */\r
- info.key_data_chunk = (uint8_t *)kv_malloc(NV_KEY_DATA_CHUNK_LEN);\r
- if (info.key_data_chunk == NULL) {\r
- return ERRCODE_MALLOC;\r
- }\r
-\r
- /* Compare data *could* be in flash and will not be encrypted */\r
- if (\r
-#ifdef CONFIG_NV_SUPPORT_SINGLE_CORE_SYSTEM\r
- ((uintptr_t)(compare_data + compare_length) >= FLASH_PHYSICAL_ADDR_START) &&\r
-#endif\r
- (uintptr_t)(compare_data + compare_length) <= FLASH_PHYSICAL_ADDR_END) {\r
- info.compare_data_chunk = (uint8_t *)kv_malloc(NV_KEY_DATA_CHUNK_LEN);\r
- if (info.compare_data_chunk == NULL) {\r
- res = ERRCODE_MALLOC;\r
- /* Ensure there is not a memory leak */\r
- goto ret_free;\r
- }\r
- }\r
-\r
-#if (CONFIG_NV_SUPPORT_ENCRYPT == NV_YES)\r
- if (key->header.enc_key == AES_KDFKEY_SDRK_TYPE) {\r
- /* Read key data, using AES engine if encrypted */\r
- res = nv_crypto_claim_aes(&(info.crypto_handle), &(key->header));\r
- if (res != ERRCODE_SUCC) {\r
- goto ret_free;\r
- }\r
- }\r
-#endif\r
-\r
- res = kv_helper_compare_key_data_chunks(key, compare_data, &info);\r
-\r
-ret_free:\r
-#if (CONFIG_NV_SUPPORT_ENCRYPT == NV_YES)\r
- if (info.crypto_handle != INVAILD_CRYPTO_HANDLE) {\r
- nv_crypto_release_aes(info.crypto_handle);\r
- }\r
-#endif\r
- kv_free(info.compare_data_chunk);\r
- kv_free(info.key_data_chunk);\r
- return res;\r
-}\r
-\r
-bool kv_key_is_erased(const kv_key_handle_t *key)\r
-{\r
- if ((key->header.magic != g_nv_header_magic) ||\r
- (key->header.valid != KV_KEY_VALID) ||\r
- (key->header.length == 0xFFFF) ||\r
- (key->header.length == 0)) {\r
- return true;\r
- }\r
-\r
- return false;\r
-}\r
-\r
-bool kv_key_is_valid(kv_key_handle_t *key)\r
-{\r
- bool res = kv_key_is_erased(key);\r
- if (res == true) {\r
- return false;\r
- }\r
-\r
- return (kv_key_validation(key, false) == ERRCODE_SUCC);\r
-}\r
-\r
-/**\r
- * Attempt to match pertinent bits in a key with a search_pattern\r
- * @param key_handle Handle of a valid key\r
- * @param search_pattern The pattern of bits to match in the key\r
- * @param search_mask A mask to select the pertinent bits in the key for matching\r
- * @return true if pattern matches, false otherwise\r
- */\r
-errcode_t kv_key_does_pattern_match(const kv_key_handle_t *key, kv_key_id search_pattern, kv_key_id search_mask)\r
-{\r
- /* Match pertinent bits in the key with the search_pattern */\r
- if ((key->header.key_id & search_mask) != search_pattern) {\r
- return ERRCODE_NV_SEARCH_PATTERN_MISMATCH;\r
- }\r
- return ERRCODE_SUCC;\r
-}\r
-\r
-errcode_t kv_key_does_filter_match(kv_key_handle_t *key, kv_key_filter_t *search_filter)\r
-{\r
- /* If no search filter is given then match every key */\r
- if (search_filter != NULL) {\r
- errcode_t res;\r
- res = kv_key_does_pattern_match(key, search_filter->pattern, search_filter->mask);\r
- if (res != ERRCODE_SUCC) {\r
- return res;\r
- }\r
-\r
- if (((uint32_t)search_filter->type & (uint32_t)KV_KEY_FILTER_TYPE_ANY) != (uint32_t)KV_KEY_FILTER_TYPE_ANY) {\r
- /* Determine key type */\r
- kv_key_filter_type_t key_type;\r
- if ((((uint32_t)kv_key_attributes(key)) & ((uint32_t)KV_ATTRIBUTE_PERMANENT)) != 0) {\r
- key_type = KV_KEY_FILTER_TYPE_PERMANENT;\r
- } else {\r
- key_type = KV_KEY_FILTER_TYPE_NORMAL;\r
- }\r
- if (((uint32_t)search_filter->type & (uint32_t)key_type) == 0) {\r
- return ERRCODE_NV_SEARCH_KEY_TYPE_MISMATCH;\r
- }\r
- }\r
-\r
- if (((uint32_t)search_filter->state & (uint32_t)KV_KEY_FILTER_STATE_ANY) != (uint32_t)KV_KEY_FILTER_STATE_ANY) {\r
- /* Determine key state */\r
- kv_key_filter_state_t key_state;\r
- if (kv_key_is_valid(key)) {\r
- key_state = KV_KEY_FILTER_STATE_VALID;\r
- } else {\r
- key_state = KV_KEY_FILTER_STATE_INVALID;\r
- }\r
- if (((uint32_t)search_filter->state & (uint32_t)key_state) == 0) {\r
- return ERRCODE_NV_SEARCH_KEY_STATE_MISMATCH;\r
- }\r
- }\r
- }\r
-\r
- return ERRCODE_SUCC;\r
-}\r
-\r
-STATIC bool kv_key_header_is_full_ff(kv_key_header_t *key_header, uint8_t len)\r
-{\r
- kv_key_header_t header_cmp;\r
- uint8_t cmp_len = (len > (uint8_t)sizeof(kv_key_header_t)) ? ((uint8_t)sizeof(kv_key_header_t)) : len;\r
-\r
- (void)memset_s(&header_cmp, sizeof(kv_key_header_t), 0xFF, sizeof(kv_key_header_t)); /* 0xFF is initial value */\r
-\r
- if (memcmp(key_header, &header_cmp, cmp_len) == 0) {\r
- return true;\r
- }\r
- return false;\r
-}\r
-\r
-errcode_t kv_key_get_handle_from_location(kv_key_location key_location, kv_key_handle_t *key)\r
-{\r
- key->header.length = 0xFFFF; /* Would not expect a flash read to fail, but just in case... */\r
- errcode_t res = kv_key_helper_copy_flash((uint32_t)(uintptr_t)&key->header, (uint32_t)(uintptr_t)key_location,\r
- sizeof(kv_key_header_t));\r
- if (res != ERRCODE_SUCC) {\r
- return res;\r
- }\r
-\r
- key->key_location = key_location;\r
-\r
- if (kv_key_header_is_full_ff(&key->header, (uint8_t)sizeof(kv_key_header_t))) {\r
- /* Last key has been read */\r
- return ERRCODE_NV_KEY_NOT_FOUND;\r
- }\r
-\r
- if (g_nv_header_magic == KV_KEY_MAGIC && key->header.magic != KV_KEY_MAGIC) {\r
- /* Wrong key has been read */\r
- return ERRCODE_NV_INVALID_KEY_HEADER;\r
- }\r
- return ERRCODE_SUCC;\r
-}\r
-\r
-errcode_t kv_key_locations_in_same_page(kv_key_location first_key_location, kv_key_location second_key_location)\r
-{\r
- uintptr_t first_page_location = (uintptr_t)first_key_location & ~(KV_PAGE_SIZE - 1);\r
- uintptr_t second_page_location = ((uintptr_t)second_key_location +\r
- (uintptr_t)sizeof(kv_key_header_t)) & ~(KV_PAGE_SIZE - 1);\r
- if (first_page_location != second_page_location) {\r
- return ERRCODE_FAIL;\r
- }\r
- return ERRCODE_SUCC;\r
-}\r
-\r
-#if (CONFIG_NV_SUPPORT_SKIP_CORRUPT_KEY == NV_YES)\r
-errcode_t kv_key_get_next_magic_position(kv_key_location key_location, kv_key_handle_t *key)\r
-{\r
- uint32_t src = (uint32_t)(uintptr_t)key_location;\r
- kv_nvregion_area_t* nvregion_area = nv_get_region_area();\r
- uint32_t start_page_loc = nvregion_area->nv_data_addr - FLASH_PHYSICAL_ADDR_START;\r
- uint32_t next_page_loc = ((((src - start_page_loc) / KV_PAGE_SIZE) + 1) * KV_PAGE_SIZE) + start_page_loc;\r
-\r
- /* 从传入位置的下一位开始读 */\r
- src = src + 1;\r
- bool found_ending = false;\r
- kv_key_location tmp_location;\r
-\r
- for (; src < next_page_loc - sizeof(kv_key_header_t); src++) {\r
- (void)memset_s(&(key->header), sizeof(kv_key_header_t), 0, sizeof(kv_key_header_t));\r
- if (kv_key_helper_copy_flash((uint32_t)(uintptr_t)&key->header, src, sizeof(kv_key_header_t)) != ERRCODE_SUCC) {\r
- return ERRCODE_NV_READ_FLASH_ERR;\r
- }\r
-\r
- if (key->header.magic == KV_KEY_MAGIC &&\r
- (key->header.valid == KV_KEY_INVALID || key->header.valid == KV_KEY_VALID)) {\r
- key->key_location = (kv_key_location)(uintptr_t)src;\r
- /* 找到有效的Key */\r
- if (kv_key_validate_integrity(key, true) == ERRCODE_SUCC) {\r
- nv_log_debug("[NV][magic] Get valid next key (location = 0x%x).\r\n", key->key_location);\r
- return ERRCODE_SUCC;\r
- }\r
- }\r
-\r
- if (kv_key_header_is_full_ff(&key->header, (uint8_t)sizeof(kv_key_header_t))) {\r
- /* 找到一块全F区域,暂时标记为数据结尾 */\r
- if (!found_ending) {\r
- found_ending = true;\r
- tmp_location = (kv_key_location)(uintptr_t)src;\r
- }\r
- /* 若此段为全F,直接跳到下一段进行读取,不以1的步长行进 */\r
- src += (uint32_t)sizeof(kv_key_header_t) - 1;\r
- } else if (found_ending) {\r
- /* 如又找到一块非全F区域,则上次标记的位置不是数据结尾,取消标记 */\r
- found_ending = false;\r
- }\r
- }\r
- if (found_ending) {\r
- key->key_location = tmp_location;\r
- } else {\r
- /* 如未找到数据结尾, 则在最后继续查找更短一些的全FF数据 */\r
- for (src = next_page_loc - (uint32_t)sizeof(kv_key_header_t); src < next_page_loc; src++) {\r
- uint32_t read_len = next_page_loc - src;\r
- if (kv_key_helper_copy_flash((uint32_t)(uintptr_t)&key->header, src, (uint16_t)read_len) != ERRCODE_SUCC) {\r
- return ERRCODE_NV_READ_FLASH_ERR;\r
- }\r
- if (kv_key_header_is_full_ff(&key->header, (uint8_t)read_len)) {\r
- key->key_location = (kv_key_location)(uintptr_t)src;\r
- nv_log_debug("[NV][magic] Get next key on end 2 (location = 0x%x).\r\n", key->key_location);\r
- return ERRCODE_NV_KEY_NOT_FOUND;\r
- }\r
- }\r
- key->key_location = (kv_key_location)(uintptr_t)src;\r
- }\r
-\r
- return ERRCODE_NV_KEY_NOT_FOUND;\r
-}\r
-\r
-STATIC errcode_t kv_key_get_next_key_from_location(kv_key_location cur_key_loc, kv_key_location next_key_loc,\r
- kv_key_handle_t *key)\r
-{\r
- if (kv_key_locations_in_same_page(cur_key_loc, next_key_loc) != ERRCODE_SUCC) {\r
- /* Reached the end of the KV page */\r
- return ERRCODE_NV_KEY_NOT_FOUND;\r
- }\r
-\r
- return kv_key_get_handle_from_location(next_key_loc, key);\r
-}\r
-\r
-STATIC errcode_t kv_key_get_next_handle_with_corrupt_key(kv_key_handle_t *key, kv_key_validate_status_t key_status)\r
-{\r
- kv_key_handle_t cur_key;\r
- errcode_t res = ERRCODE_SUCC;\r
-\r
- /* 如果当前KEY是错误Key,直接需要通过magic找到下一个key */\r
- if (key_status == KV_KEY_VALIDATE_WRONG) {\r
- return kv_key_get_next_magic_position(key->key_location, key);\r
- }\r
-\r
- if (kv_key_header_is_full_ff(&key->header, (uint8_t)sizeof(kv_key_header_t))) {\r
- /* Last key has been read */\r
- return ERRCODE_NV_KEY_NOT_FOUND;\r
- }\r
-\r
- if (key->header.magic != KV_KEY_MAGIC) {\r
- /* corrupt key has been read, then get next key by magic */\r
- nv_log_info("[NV] header magic error(location = 0x%x)! Get next key by magic.\r\n", key->key_location);\r
- return kv_key_get_next_magic_position(key->key_location, key);\r
- }\r
-\r
- uint16_t offset = kv_key_flash_size(key);\r
- kv_key_location next_key_location = (kv_key_location)((uintptr_t)key->key_location + offset);\r
-\r
- (void)memcpy_s(&cur_key, sizeof(kv_key_handle_t), key, sizeof(kv_key_handle_t));\r
-\r
- /* 出现下面两种情况之一时:\r
- 1. 下一个Key的位置与当前Key不在一个page内\r
- 2. 下一个key的位置指向数据结尾\r
- 首先验证当前key,如果当前KEY校验成功,则说明key length是可信的,说明确实找到了数据结尾,返回ERRCODE_NV_KEY_NOT_FOUND;\r
- 如果当前KEY校验不成功,则说明key length可能是不正确的,则需要通过magic找到下一个key。\r
- */\r
- res = kv_key_get_next_key_from_location(key->key_location, next_key_location, key);\r
- if (res != ERRCODE_SUCC) {\r
- if (key_status == KV_KEY_VALIDATE_CORRECT || (kv_key_validate_integrity(&cur_key, true) == ERRCODE_SUCC)) {\r
- key->key_location = next_key_location;\r
- if (res == ERRCODE_NV_INVALID_KEY_HEADER) {\r
- res = ERRCODE_SUCC;\r
- }\r
- return res;\r
- } else {\r
- /* 如果当前KEY校验不成功,则说明key length可能是不正确的,则需要通过magic找到下一个KEY */\r
- nv_log_info("[NV] validate failed (location = 0x%x)! Get next key by magic.\r\n", cur_key.key_location);\r
- return kv_key_get_next_magic_position(cur_key.key_location, key);\r
- }\r
- }\r
-\r
- return res;\r
-}\r
-#endif /* #ifdef CONFIG_NV_SUPPORT_SKIP_CORRUPT_KEY */\r
-\r
-STATIC errcode_t kv_key_get_next_handle_direct(kv_key_handle_t *key)\r
-{\r
- if (key->header.length == 0xFFFF) {\r
- /* Last key has been read */\r
- return ERRCODE_NV_KEY_NOT_FOUND;\r
- }\r
-\r
- uint16_t offset = kv_key_flash_size(key);\r
- kv_key_location new_key_location = (kv_key_location)((uintptr_t)key->key_location + offset);\r
- if (kv_key_locations_in_same_page(key->key_location, new_key_location) != ERRCODE_SUCC) {\r
- /* Reached the end of the KV page */\r
- return ERRCODE_NV_KEY_NOT_FOUND;\r
- }\r
-\r
- return kv_key_get_handle_from_location(new_key_location, key);\r
-}\r
-\r
-errcode_t kv_key_get_next_handle(kv_key_handle_t *key, kv_key_validate_status_t key_status)\r
-{\r
- unused(key_status);\r
-#if (CONFIG_NV_SUPPORT_SKIP_CORRUPT_KEY == NV_YES)\r
- if (g_nv_header_magic == KV_KEY_MAGIC) {\r
- return kv_key_get_next_handle_with_corrupt_key(key, key_status);\r
- } else {\r
- return kv_key_get_next_handle_direct(key);\r
- }\r
-#else\r
- return kv_key_get_next_handle_direct(key);\r
-#endif\r
-}\r
-\r
-STATIC void kv_key_fix_header_for_validate(kv_key_header_t *key_header, bool ignor_invalid)\r
-{\r
- if (ignor_invalid && key_header->valid == KV_KEY_INVALID) {\r
- key_header->valid = KV_KEY_VALID;\r
- if (g_nv_header_magic == KV_KEY_NO_MAGIC) {\r
- key_header->magic = g_nv_header_magic;\r
- }\r
- }\r
-}\r
-\r
-STATIC void kv_key_hash_crc_tag_start(kv_key_handle_t *key, uint32_t *crc_ret)\r
-{\r
- *crc_ret = 0;\r
- if (key->header.enc_key != AES_KDFKEY_SDRK_TYPE) {\r
- return;\r
- }\r
-\r
-#if ((CONFIG_NV_SUPPORT_ENCRYPT == NV_YES) && (CONFIG_NV_SUPPORT_HASH_FOR_CRYPT == NV_YES))\r
- (void)nv_crypto_start_hash();\r
-#endif\r
-}\r
-\r
-STATIC void kv_key_hash_crc_tag_update(kv_key_handle_t *key, uint8_t *data_chunk, uint32_t data_len, uint32_t *crc_ret)\r
-{\r
- *crc_ret = uapi_crc32(*crc_ret, data_chunk, data_len);\r
- if (key->header.enc_key != AES_KDFKEY_SDRK_TYPE) {\r
- return;\r
- }\r
-\r
-#if ((CONFIG_NV_SUPPORT_ENCRYPT == NV_YES) && (CONFIG_NV_SUPPORT_HASH_FOR_CRYPT == NV_YES))\r
- nv_crypto_update_hash(data_chunk, data_len);\r
-#endif\r
-}\r
-\r
-STATIC void kv_key_hash_crc_tag_finish(kv_key_handle_t *key, uint8_t *calculated_hash, uint32_t crc_value,\r
- uint32_t crypto_handle)\r
-{\r
- unused(crypto_handle);\r
- uint32_t crc_ret = kv_crc32_swap(crc_value);\r
-\r
- if (key->header.enc_key != AES_KDFKEY_SDRK_TYPE) {\r
- (void)memcpy_s((void *)calculated_hash, KV_CRYPTO_CRC_SIZE, (const void *)&crc_ret, KV_CRYPTO_CRC_SIZE);\r
- return;\r
- }\r
-\r
-#if (CONFIG_NV_SUPPORT_ENCRYPT == NV_YES)\r
-#if (CONFIG_NV_SUPPORT_HASH_FOR_CRYPT == NV_YES)\r
- nv_crypto_complete_hash(calculated_hash);\r
- (void)memcpy_s((void *)(calculated_hash + KV_CRYPTO_HASH_SIZE - KV_CRYPTO_CRC_SIZE), KV_CRYPTO_CRC_SIZE,\r
- (const void *)&crc_ret, KV_CRYPTO_CRC_SIZE);\r
-#else\r
- (void)memcpy_s((void *)(calculated_hash + KV_CRYPTO_HASH_SIZE - KV_CRYPTO_CRC_SIZE), KV_CRYPTO_CRC_SIZE,\r
- (const void *)&crc_ret, KV_CRYPTO_CRC_SIZE);\r
-#endif /* (CONFIG_NV_SUPPORT_HASH_FOR_CRYPT == NV_YES) */\r
-#endif /* #if (CONFIG_NV_SUPPORT_ENCRYPT == NV_YES) */\r
-}\r
-\r
-STATIC errcode_t kv_key_hash_crc_tag_verify(kv_key_handle_t *key, uint8_t *read_hash_crc, uint8_t *cal_hash_crc,\r
- uint32_t crypto_handle, bool only_crc)\r
-{\r
- unused(crypto_handle);\r
- unused(only_crc);\r
- errcode_t res = ERRCODE_SUCC;\r
-\r
- if (key->header.enc_key != AES_KDFKEY_SDRK_TYPE) {\r
- if (memcmp(cal_hash_crc, read_hash_crc, KV_CRYPTO_CRC_SIZE) != 0) {\r
- res = ERRCODE_NV_HASH_MISMATCH;\r
- }\r
- return res;\r
- }\r
-\r
-#if (CONFIG_NV_SUPPORT_ENCRYPT != NV_YES)\r
- /* 不支持加密的情况下直接返回失败 */\r
- return ERRCODE_NV_HASH_MISMATCH;\r
-#else\r
- if (only_crc) {\r
- /* 只校验CRC的场景,校验最后4个字节的CRC(密文的CRC) */\r
- if (memcmp(cal_hash_crc + KV_CRYPTO_HASH_SIZE - KV_CRYPTO_CRC_SIZE,\r
- read_hash_crc + KV_CRYPTO_HASH_SIZE - KV_CRYPTO_CRC_SIZE, KV_CRYPTO_CRC_SIZE) != 0) {\r
- res = ERRCODE_NV_HASH_MISMATCH;\r
- }\r
- return res;\r
- }\r
-#if (CONFIG_NV_SUPPORT_HASH_FOR_CRYPT == NV_YES)\r
- /* 使用HASH校验的场景,校验HASH的前28个字节 */\r
- if (memcmp(cal_hash_crc, read_hash_crc, KV_CRYPTO_HASH_SIZE - KV_CRYPTO_CRC_SIZE) != 0) {\r
- res = ERRCODE_NV_HASH_MISMATCH;\r
- }\r
- return res;\r
-#else\r
- /* 使用GCM TAG的场景,直接校验16字节的TAG */\r
- return nv_crypto_validate_tag(crypto_handle);\r
-#endif /* (CONFIG_NV_SUPPORT_HASH_FOR_CRYPT == NV_YES)) */\r
-#endif /* #if (CONFIG_NV_SUPPORT_ENCRYPT != NV_YES) */\r
-}\r
-\r
-STATIC errcode_t kv_key_validate_hash_chunks(kv_key_handle_t *key, uint8_t *read_data_chunk, uint8_t *cal_hash_crc,\r
- uint32_t crypto_handle, bool ignor_invalid)\r
-{\r
- errcode_t res;\r
- kv_key_header_t *key_header = (kv_key_header_t *)read_data_chunk;\r
- uintptr_t key_location = (uintptr_t)key->key_location;\r
- uint32_t crc_ret = 0;\r
- uint32_t hash_crc_len = ((key->header.enc_key == AES_KDFKEY_SDRK_TYPE) ? KV_CRYPTO_HASH_SIZE : KV_CRYPTO_CRC_SIZE);\r
-\r
- kv_key_hash_crc_tag_start(key, &crc_ret);\r
-\r
- /* Read header first, which is never encrypted */\r
- res = kv_key_read_data_from_flash((uintptr_t)read_data_chunk, key_location,\r
- sizeof(kv_key_header_t), INVAILD_CRYPTO_HANDLE);\r
- if (res != ERRCODE_SUCC) {\r
- return res;\r
- }\r
-\r
- kv_key_fix_header_for_validate(key_header, ignor_invalid);\r
-\r
- kv_key_hash_crc_tag_update(key, read_data_chunk, sizeof(kv_key_header_t), &crc_ret);\r
-\r
- const kv_attributes_t attributes = kv_key_attributes(key);\r
- uint32_t decryptable_data_len = kv_key_padded_data_length(attributes, key->header.length);\r
-#if (CONFIG_NV_SUPPORT_HASH_FOR_CRYPT == NV_NO)\r
- if (crypto_handle != INVAILD_CRYPTO_HANDLE) {\r
- /* 在使用GCM TAG时,先读取一次TAG并设置给解密模块 */\r
- /* Read key hash, which could be unencrypted */\r
- uintptr_t hash_location = key_location + (uintptr_t)sizeof(kv_key_header_t) + (uintptr_t)decryptable_data_len;\r
- (void)kv_key_read_data_from_flash((uintptr_t)read_data_chunk, hash_location,\r
- KV_CRYPTO_HASH_SIZE, INVAILD_CRYPTO_HANDLE);\r
- (void)nv_crypto_set_tag(crypto_handle, read_data_chunk, NV_AES_GCM_TAG_LENGTH);\r
- }\r
-#endif\r
-\r
- /* Read rest of key data, which could be encrypted */\r
- uint32_t key_data_offset = 0;\r
- key_location += (uintptr_t)sizeof(kv_key_header_t);\r
- while (key_data_offset < decryptable_data_len) {\r
- uint32_t chunk_len = uapi_min(NV_KEY_DATA_CHUNK_LEN, decryptable_data_len - key_data_offset);\r
-\r
- res = kv_key_read_data_from_flash((uintptr_t)read_data_chunk, (key_location + key_data_offset),\r
- chunk_len, crypto_handle);\r
- if (res != ERRCODE_SUCC) {\r
- return res;\r
- }\r
-\r
- kv_key_hash_crc_tag_update(key, read_data_chunk, chunk_len, &crc_ret);\r
- key_data_offset += chunk_len;\r
- }\r
-\r
- /* Read key hash, which could be unencrypted */\r
- res = kv_key_read_data_from_flash((uintptr_t)read_data_chunk, (key_location + key_data_offset),\r
- hash_crc_len, INVAILD_CRYPTO_HANDLE);\r
-\r
- kv_key_hash_crc_tag_finish(key, cal_hash_crc, crc_ret, crypto_handle);\r
- return res;\r
-}\r
-\r
-/*\r
- * 校验KEY的完整性。\r
- * 非加密KEY, 校验CRC;\r
- * 加密KEY, 如CONFIG_NV_SUPPORT_HASH_FOR_CRYPT为YES,校验明文HASH;\r
- * 如CONFIG_NV_SUPPORT_HASH_FOR_CRYPT为NO,校验密文的CRC(CRC存放在16字节Tag之后)。\r
- */\r
-errcode_t kv_key_validate_integrity(kv_key_handle_t *key, bool ignor_invalid)\r
-{\r
- uint32_t crypto_handle = INVAILD_CRYPTO_HANDLE;\r
-\r
- if (key->header.length > NV_NORMAL_KVALUE_MAX_LEN) {\r
- return ERRCODE_NV_INVALID_KEY_HEADER;\r
- }\r
-\r
- uint8_t *calculated_hash = (uint8_t *)kv_malloc(KV_CRYPTO_HASH_SIZE);\r
- if (calculated_hash == NULL) {\r
- return ERRCODE_MALLOC;\r
- }\r
-\r
- uint8_t *read_data_chunk = (uint8_t *)kv_malloc(NV_KEY_DATA_CHUNK_LEN);\r
- if (read_data_chunk == NULL) {\r
- kv_free(calculated_hash);\r
- return ERRCODE_MALLOC;\r
- }\r
-\r
- errcode_t res = kv_key_validate_hash_chunks(key, read_data_chunk, calculated_hash, crypto_handle, ignor_invalid);\r
- if (res != ERRCODE_SUCC) {\r
- goto err;\r
- }\r
-\r
- res = kv_key_hash_crc_tag_verify(key, read_data_chunk, calculated_hash, crypto_handle, true);\r
-\r
-err:\r
- kv_free(read_data_chunk);\r
- kv_free(calculated_hash);\r
- return res;\r
-}\r
-\r
-/*\r
- * 校验KEY的正确性:\r
- * 非加密KEY校验CRC\r
- * 加密KEY, 如CONFIG_NV_SUPPORT_HASH_FOR_CRYPT为YES,校验明文HASH;\r
- * 如CONFIG_NV_SUPPORT_HASH_FOR_CRYPT为NO,则校验GCM Tag。\r
- */\r
-errcode_t kv_key_validation(kv_key_handle_t *key, bool ignor_invalid)\r
-{\r
- errcode_t res;\r
- uint32_t crypto_handle = INVAILD_CRYPTO_HANDLE;\r
-\r
- if (key->header.length > NV_NORMAL_KVALUE_MAX_LEN) {\r
- return ERRCODE_NV_INVALID_KEY_HEADER;\r
- }\r
-\r
- uint8_t *calculated_hash = (uint8_t *)kv_malloc(KV_CRYPTO_HASH_SIZE);\r
- if (calculated_hash == NULL) {\r
- return ERRCODE_MALLOC;\r
- }\r
- (void)memset_s(calculated_hash, KV_CRYPTO_HASH_SIZE, 0, KV_CRYPTO_HASH_SIZE);\r
-\r
- uint8_t *read_data_chunk = (uint8_t *)kv_malloc(NV_KEY_DATA_CHUNK_LEN);\r
- if (read_data_chunk == NULL) {\r
- kv_free(calculated_hash);\r
- return ERRCODE_MALLOC;\r
- }\r
-\r
- (void)memset_s(read_data_chunk, NV_KEY_DATA_CHUNK_LEN, 0, NV_KEY_DATA_CHUNK_LEN);\r
-\r
-#if (CONFIG_NV_SUPPORT_ENCRYPT == NV_YES)\r
- if (key->header.enc_key != 0) {\r
- /* Read key data, using AES engine if encrypted */\r
- res = nv_crypto_claim_aes(&crypto_handle, &(key->header));\r
- if (res != ERRCODE_SUCC) {\r
- goto err;\r
- }\r
- }\r
-#endif\r
-\r
- res = kv_key_validate_hash_chunks(key, read_data_chunk, calculated_hash, crypto_handle, ignor_invalid);\r
- if (res != ERRCODE_SUCC) {\r
- goto err;\r
- }\r
-\r
- res = kv_key_hash_crc_tag_verify(key, read_data_chunk, calculated_hash, crypto_handle, false);\r
-\r
-err:\r
- kv_free(read_data_chunk);\r
- kv_free(calculated_hash);\r
-\r
-#if (CONFIG_NV_SUPPORT_ENCRYPT == NV_YES)\r
- if (crypto_handle != INVAILD_CRYPTO_HANDLE) {\r
- nv_crypto_release_aes(crypto_handle);\r
- }\r
-#endif\r
- return res;\r
-}\r
-\r
-kv_attributes_t kv_key_attributes(const kv_key_handle_t *key)\r
-{\r
- kv_attributes_t attributes = 0;\r
-\r
- if (key->header.type != KV_KEY_TYPE_NORMAL) {\r
- attributes = (kv_attributes_t)((uint32_t)attributes | (uint32_t)KV_ATTRIBUTE_PERMANENT);\r
- }\r
- if (key->header.enc_key != 0) {\r
- attributes = (kv_attributes_t)((uint32_t)attributes | (uint32_t)KV_ATTRIBUTE_ENCRYPTED);\r
- }\r
- if (key->header.upgrade != KV_KEY_TYPE_NORMAL) {\r
- attributes = (kv_attributes_t)((uint32_t)attributes | (uint32_t)KV_ATTRIBUTE_NON_UPGRADE);\r
- }\r
- return attributes;\r
-}\r
-\r
-uint16_t kv_key_padded_data_length(kv_attributes_t attributes, uint16_t unpadded_length)\r
-{\r
- if (((uint32_t)attributes & (uint32_t)KV_ATTRIBUTE_ENCRYPTED) != 0) {\r
- return kv_padded_ciphertext_length(unpadded_length);\r
- } else {\r
- return kv_padded_cleartext_length(unpadded_length);\r
- }\r
-}\r
-\r
-uint16_t kv_key_flash_size(kv_key_handle_t *key)\r
-{\r
- uint16_t size;\r
- kv_attributes_t attributes;\r
- uint16_t hash_crc_size;\r
-\r
- if (key->header.enc_key == AES_KDFKEY_SDRK_TYPE) {\r
- hash_crc_size = KV_CRYPTO_HASH_SIZE;\r
- } else {\r
- hash_crc_size = KV_CRYPTO_CRC_SIZE;\r
- }\r
-\r
- size = (uint16_t)sizeof(kv_key_header_t) + hash_crc_size;\r
-\r
- attributes = kv_key_attributes(key);\r
- size += kv_key_padded_data_length(attributes, key->header.length);\r
-\r
- return size;\r
-}\r
-\r
-void kv_key_build_from_new(kv_key_handle_t *key, const kv_key_details_t *new_key, kv_key_location key_location)\r
-{\r
- key->header.key_id = new_key->key_id;\r
- key->header.length = (uint16_t)new_key->kvalue_length;\r
- key->header.magic = g_nv_header_magic;\r
- key->header.valid = KV_KEY_VALID;\r
- key->header.type = KV_KEY_TYPE_NORMAL;\r
- if (((uint32_t)new_key->attributes & (uint32_t)KV_ATTRIBUTE_PERMANENT) != 0) {\r
- key->header.type = KV_KEY_TYPE_PERMANENT;\r
- }\r
- key->header.upgrade = KV_KEY_TYPE_NORMAL;\r
- if (((uint32_t)new_key->attributes & (uint32_t)KV_ATTRIBUTE_NON_UPGRADE) != 0) {\r
- key->header.upgrade = 0;\r
- key->header.version = key->header.version + 1;\r
- }\r
- key->header.enc_key = 0;\r
- if (((uint32_t)new_key->attributes & (uint32_t)KV_ATTRIBUTE_ENCRYPTED) != 0) {\r
- key->header.enc_key = AES_KDFKEY_SDRK_TYPE;\r
-#if (CONFIG_NV_SUPPORT_ENCRYPT == NV_YES)\r
- nv_crypto_generate_random(&key->header.rnd);\r
-#endif\r
- key->header.version = key->header.version + 1;\r
- }\r
-\r
- key->key_location = key_location;\r
-}\r
-\r
-uint32_t kv_crc32_swap(uint32_t crc)\r
-{\r
- return ((crc >> CRC_SWAP_SIZE24) | ((crc >> CRC_SWAP_SIZE8) & 0xFF00) |\r
- ((crc << CRC_SWAP_SIZE8) & 0xFF0000) | (crc << CRC_SWAP_SIZE24));\r
+/*
+ * Copyright (c) @CompanyNameMagicTag 2019-2022. All rights reserved.
+ * Description: KV Storage Library key access module implementation
+ */
+
+#include "nv_key.h"
+#include "string.h"
+#include "nv_porting.h"
+#include "uapi_crc.h"
+#include "nv_nvregion.h"
+#include "common_def.h"
+
+#define KV_CLEARTEXT_PADDING_SIZE (1UL << 2) /* Currently these both need to be a power of 2... */
+#define KV_CIPHERTEXT_PADDING_SIZE (1UL << 4) /* ...due to implementation of kv_padded_length */
+#define kv_padded_length(d, s) (((d) + ((s) - 1)) & ~((s) - 1))
+#define kv_padded_cleartext_length(d) kv_padded_length(d, KV_CLEARTEXT_PADDING_SIZE)
+#define kv_padded_ciphertext_length(d) kv_padded_length(d, KV_CIPHERTEXT_PADDING_SIZE)
+#define KV_DMA_COPY_SIZE_ALIGN 32
+
+uint8_t g_nv_header_magic = KV_KEY_MAGIC;
+
+/* IOT-16672 Use ROM Lib function instead (enhanced to support KV requirements, as here) */
+errcode_t kv_key_helper_copy_flash(uint32_t dest_location, uint32_t src_location, uint16_t length)
+{
+ if (length == 0) {
+ return ERRCODE_NV_ZERO_LENGTH_COPY;
+ }
+ uint32_t src_addr = src_location - FLASH_PHYSICAL_ADDR_START;
+ uint16_t length_rounded = length & ~(KV_DMA_COPY_SIZE_ALIGN - 1);
+ uint16_t length_remainder = length & (KV_DMA_COPY_SIZE_ALIGN - 1);
+ uint16_t dest_offset = 0;
+ while (length_rounded > 0) {
+ if (kv_flash_read((uint32_t)(uintptr_t)src_addr, KV_DMA_COPY_SIZE_ALIGN,
+ (uint8_t *)(uintptr_t)(dest_location + dest_offset)) != ERRCODE_SUCC) {
+ return ERRCODE_NV_READ_FLASH_ERR;
+ }
+ dest_offset += KV_DMA_COPY_SIZE_ALIGN;
+ src_addr += KV_DMA_COPY_SIZE_ALIGN;
+ length_rounded -= KV_DMA_COPY_SIZE_ALIGN;
+ }
+
+ if (length_remainder > 0) {
+ if (kv_flash_read((uint32_t)(uintptr_t)src_addr, length_remainder,
+ (uint8_t *)(uintptr_t)(dest_location + dest_offset)) != ERRCODE_SUCC) {
+ return ERRCODE_NV_READ_FLASH_ERR;
+ }
+ }
+ return ERRCODE_SUCC;
+}
+
+#ifdef CONFIG_NV_SUPPORT_SINGLE_CORE_SYSTEM
+errcode_t kv_key_direct_write_flash(uint32_t dest, uint32_t length, const uint8_t *src)
+{
+ uint32_t dest_w = dest;
+ if ((dest >= FLASH_PHYSICAL_ADDR_START) &&
+ ((dest + length) <= FLASH_PHYSICAL_ADDR_END)) {
+ dest_w -= FLASH_PHYSICAL_ADDR_START;
+ } else {
+ return ERRCODE_FLASH_INVALID_PARAM_BEYOND_ADDR;
+ }
+
+ if (kv_flash_write(dest_w, length, src, false) != ERRCODE_SUCC) {
+ return ERRCODE_NV_READ_FLASH_ERR;
+ }
+
+ return ERRCODE_SUCC;
+}
+
+errcode_t kv_key_direct_erase_flash(uint32_t dest, const uint32_t size)
+{
+ uint32_t dest_w = dest;
+ if ((dest >= FLASH_PHYSICAL_ADDR_START) &&
+ ((dest + size) <= FLASH_PHYSICAL_ADDR_END)) {
+ dest_w -= FLASH_PHYSICAL_ADDR_START;
+ } else {
+ return ERRCODE_FLASH_INVALID_PARAM_BEYOND_ADDR;
+ }
+
+ if (kv_flash_erase(dest_w, size) != ERRCODE_SUCC) {
+ return ERRCODE_FAIL;
+ }
+
+ return ERRCODE_SUCC;
+}
+#endif
+
+errcode_t kv_key_write_flash(uint32_t dest, uint32_t length, const uint8_t *src)
+{
+#ifdef CONFIG_NV_SUPPORT_SINGLE_CORE_SYSTEM
+ return kv_key_direct_write_flash(dest, length, src);
+#else
+ return flash_task_drv_write(dest, length, src);
+#endif
+}
+
+errcode_t kv_key_erase_flash(uint32_t dest, const uint32_t size)
+{
+#ifdef CONFIG_NV_SUPPORT_SINGLE_CORE_SYSTEM
+ return kv_key_direct_erase_flash(dest, size);
+#else
+ return flash_task_drv_erase(dest, size);
+#endif
+}
+
+#if (CONFIG_NV_SUPPORT_ENCRYPT == NV_YES)
+STATIC errcode_t kv_key_decode_flash(uintptr_t dest, const uintptr_t src, uint32_t length, uint32_t crypto_handle)
+{
+ errcode_t ret;
+ uint8_t *kv_padded_data = NULL;
+
+ uint32_t padded_length = kv_key_padded_data_length(KV_ATTRIBUTE_ENCRYPTED, (uint16_t)length);
+ if (padded_length == length) {
+ ret = kv_key_helper_copy_flash((uint32_t)dest, (uint32_t)src, (uint16_t)length);
+ if (ret == ERRCODE_SUCC) {
+ return nv_crypto_decode(crypto_handle, dest, dest, length);
+ } else {
+ return ret;
+ }
+ } else {
+ kv_padded_data = (uint8_t *)kv_malloc(padded_length);
+ if (kv_padded_data == NULL) {
+ return ERRCODE_MALLOC;
+ }
+ ret = kv_key_helper_copy_flash((uint32_t)(uintptr_t)kv_padded_data, (uint32_t)(uintptr_t)src,
+ (uint16_t)padded_length);
+ if (ret != ERRCODE_SUCC) {
+ goto end;
+ }
+ ret = nv_crypto_decode(crypto_handle, (uintptr_t)kv_padded_data, (uintptr_t)kv_padded_data, padded_length);
+ if (ret != ERRCODE_SUCC) {
+ goto end;
+ }
+ if (memcpy_s((void *)dest, length, kv_padded_data, length) != EOK) {
+ ret = ERRCODE_MEMCPY;
+ goto end;
+ }
+ }
+end:
+ kv_free(kv_padded_data);
+ return ret;
+}
+#endif
+
+STATIC errcode_t kv_key_read_data_from_flash(uintptr_t dest, const uintptr_t src, uint32_t length,
+ uint32_t crypto_handle)
+{
+ if (crypto_handle == INVAILD_CRYPTO_HANDLE) {
+ /* Decrypt not required, just return */
+ return kv_key_helper_copy_flash((uint32_t)dest, (uint32_t)src, (uint16_t)length);
+ } else {
+ /* Data needs decrypting */
+#if (CONFIG_NV_SUPPORT_ENCRYPT == NV_YES)
+ return kv_key_decode_flash(dest, src, length, crypto_handle);
+#else
+ return ERRCODE_FAIL;
+#endif
+ }
+}
+
+errcode_t kv_key_read_data(kv_key_handle_t *key, uint8_t *dest_location)
+{
+ errcode_t res = ERRCODE_SUCC;
+ uint32_t crypto_handle = INVAILD_CRYPTO_HANDLE;
+ uint32_t key_data_offset = 0;
+ uint32_t key_data_location = (uint32_t)(uintptr_t)key->key_location + (uint32_t)sizeof(kv_key_header_t);
+ uint32_t decryptable_data_len = key->header.length;
+ /* Check whether data is encrypted */
+#if (CONFIG_NV_SUPPORT_ENCRYPT == NV_YES)
+ if (key->header.enc_key == AES_KDFKEY_SDRK_TYPE) {
+ /* Read key data, using AES engine if encrypted */
+ res = nv_crypto_claim_aes(&crypto_handle, &(key->header));
+ if (res != ERRCODE_SUCC) {
+ nv_crypto_release_aes(crypto_handle);
+ return res;
+ }
+ }
+#endif
+
+ /* Read key data, using AES engine if encrypted */
+ while (key_data_offset < decryptable_data_len) {
+ uint32_t chunk_len = uapi_min(NV_KEY_DATA_CHUNK_LEN, decryptable_data_len - key_data_offset);
+
+ res = kv_key_read_data_from_flash((uintptr_t)(dest_location + key_data_offset),
+ (uintptr_t)key_data_location + key_data_offset,
+ chunk_len, crypto_handle);
+ if (res != ERRCODE_SUCC) {
+ break;
+ }
+ key_data_offset += chunk_len;
+ }
+#if (CONFIG_NV_SUPPORT_ENCRYPT == NV_YES)
+ if (crypto_handle != INVAILD_CRYPTO_HANDLE) {
+ nv_crypto_release_aes(crypto_handle);
+ }
+#endif
+ return res;
+}
+
+STATIC errcode_t kv_helper_compare_key_data_chunks(const kv_key_handle_t *key, const uint8_t *compare_data,
+ kv_helper_compare_key_data_info_t *info)
+{
+ errcode_t res = ERRCODE_SUCC;
+
+ uintptr_t key_data_location = (uintptr_t)key->key_location + (uintptr_t)sizeof(kv_key_header_t);
+ uint32_t key_data_offset = 0;
+ while (key_data_offset < key->header.length) {
+ const uint16_t chunk_len = (uint16_t)uapi_min(NV_KEY_DATA_CHUNK_LEN, key->header.length - key_data_offset);
+ res = kv_key_read_data_from_flash((uintptr_t)info->key_data_chunk, (key_data_location + key_data_offset),
+ chunk_len, info->crypto_handle);
+ if (res != ERRCODE_SUCC) {
+ break;
+ }
+
+ uint32_t compare_length = uapi_min(chunk_len, key->header.length - key_data_offset);
+ if (info->compare_data_chunk != NULL) {
+ /* Compare data in flash */
+ res = kv_key_helper_copy_flash((uint32_t)(uintptr_t)info->compare_data_chunk,
+ (uint32_t)(uintptr_t)compare_data + key_data_offset,
+ (uint16_t)compare_length);
+ if (res != ERRCODE_SUCC) {
+ break;
+ }
+ if (memcmp(info->key_data_chunk, info->compare_data_chunk, compare_length) != 0) {
+ return ERRCODE_NV_DATA_MISMATCH;
+ }
+ } else {
+ /* Compare data in RAM */
+ if (memcmp(info->key_data_chunk, compare_data + key_data_offset, compare_length) != 0) {
+ return ERRCODE_NV_DATA_MISMATCH;
+ }
+ }
+ key_data_offset += chunk_len;
+ }
+
+ return res;
+}
+
+errcode_t kv_helper_compare_key_data(kv_key_handle_t *key, const uint8_t *compare_data, uint16_t compare_length)
+{
+ kv_helper_compare_key_data_info_t info = {INVAILD_CRYPTO_HANDLE, NULL, NULL};
+ errcode_t res;
+
+ if (key->header.length != compare_length) {
+ return ERRCODE_NV_LENGTH_MISMATCH;
+ }
+
+ /* Key data *will* be in flash and could be encrypted */
+ info.key_data_chunk = (uint8_t *)kv_malloc(NV_KEY_DATA_CHUNK_LEN);
+ if (info.key_data_chunk == NULL) {
+ return ERRCODE_MALLOC;
+ }
+
+ /* Compare data *could* be in flash and will not be encrypted */
+ if (
+#ifdef CONFIG_NV_SUPPORT_SINGLE_CORE_SYSTEM
+ ((uintptr_t)(compare_data + compare_length) >= FLASH_PHYSICAL_ADDR_START) &&
+#endif
+ (uintptr_t)(compare_data + compare_length) <= FLASH_PHYSICAL_ADDR_END) {
+ info.compare_data_chunk = (uint8_t *)kv_malloc(NV_KEY_DATA_CHUNK_LEN);
+ if (info.compare_data_chunk == NULL) {
+ res = ERRCODE_MALLOC;
+ /* Ensure there is not a memory leak */
+ goto ret_free;
+ }
+ }
+
+#if (CONFIG_NV_SUPPORT_ENCRYPT == NV_YES)
+ if (key->header.enc_key == AES_KDFKEY_SDRK_TYPE) {
+ /* Read key data, using AES engine if encrypted */
+ res = nv_crypto_claim_aes(&(info.crypto_handle), &(key->header));
+ if (res != ERRCODE_SUCC) {
+ goto ret_free;
+ }
+ }
+#endif
+
+ res = kv_helper_compare_key_data_chunks(key, compare_data, &info);
+
+ret_free:
+#if (CONFIG_NV_SUPPORT_ENCRYPT == NV_YES)
+ if (info.crypto_handle != INVAILD_CRYPTO_HANDLE) {
+ nv_crypto_release_aes(info.crypto_handle);
+ }
+#endif
+ kv_free(info.compare_data_chunk);
+ kv_free(info.key_data_chunk);
+ return res;
+}
+
+bool kv_key_is_erased(const kv_key_handle_t *key)
+{
+ if ((key->header.magic != g_nv_header_magic) ||
+ (key->header.valid != KV_KEY_VALID) ||
+ (key->header.length == 0xFFFF) ||
+ (key->header.length == 0)) {
+ return true;
+ }
+
+ return false;
+}
+
+bool kv_key_is_valid(kv_key_handle_t *key)
+{
+ bool res = kv_key_is_erased(key);
+ if (res == true) {
+ return false;
+ }
+
+ return (kv_key_validation(key, false) == ERRCODE_SUCC);
+}
+
+/**
+ * Attempt to match pertinent bits in a key with a search_pattern
+ * @param key_handle Handle of a valid key
+ * @param search_pattern The pattern of bits to match in the key
+ * @param search_mask A mask to select the pertinent bits in the key for matching
+ * @return true if pattern matches, false otherwise
+ */
+errcode_t kv_key_does_pattern_match(const kv_key_handle_t *key, kv_key_id search_pattern, kv_key_id search_mask)
+{
+ /* Match pertinent bits in the key with the search_pattern */
+ if ((key->header.key_id & search_mask) != search_pattern) {
+ return ERRCODE_NV_SEARCH_PATTERN_MISMATCH;
+ }
+ return ERRCODE_SUCC;
+}
+
+errcode_t kv_key_does_filter_match(kv_key_handle_t *key, kv_key_filter_t *search_filter)
+{
+ /* If no search filter is given then match every key */
+ if (search_filter != NULL) {
+ errcode_t res;
+ res = kv_key_does_pattern_match(key, search_filter->pattern, search_filter->mask);
+ if (res != ERRCODE_SUCC) {
+ return res;
+ }
+
+ if (((uint32_t)search_filter->type & (uint32_t)KV_KEY_FILTER_TYPE_ANY) != (uint32_t)KV_KEY_FILTER_TYPE_ANY) {
+ /* Determine key type */
+ kv_key_filter_type_t key_type;
+ if ((((uint32_t)kv_key_attributes(key)) & ((uint32_t)KV_ATTRIBUTE_PERMANENT)) != 0) {
+ key_type = KV_KEY_FILTER_TYPE_PERMANENT;
+ } else {
+ key_type = KV_KEY_FILTER_TYPE_NORMAL;
+ }
+ if (((uint32_t)search_filter->type & (uint32_t)key_type) == 0) {
+ return ERRCODE_NV_SEARCH_KEY_TYPE_MISMATCH;
+ }
+ }
+
+ if (((uint32_t)search_filter->state & (uint32_t)KV_KEY_FILTER_STATE_ANY) != (uint32_t)KV_KEY_FILTER_STATE_ANY) {
+ /* Determine key state */
+ kv_key_filter_state_t key_state;
+ if (kv_key_is_valid(key)) {
+ key_state = KV_KEY_FILTER_STATE_VALID;
+ } else {
+ key_state = KV_KEY_FILTER_STATE_INVALID;
+ }
+ if (((uint32_t)search_filter->state & (uint32_t)key_state) == 0) {
+ return ERRCODE_NV_SEARCH_KEY_STATE_MISMATCH;
+ }
+ }
+ }
+
+ return ERRCODE_SUCC;
+}
+
+STATIC bool kv_key_header_is_full_ff(kv_key_header_t *key_header, uint8_t len)
+{
+ kv_key_header_t header_cmp;
+ uint8_t cmp_len = (len > (uint8_t)sizeof(kv_key_header_t)) ? ((uint8_t)sizeof(kv_key_header_t)) : len;
+
+ (void)memset_s(&header_cmp, sizeof(kv_key_header_t), 0xFF, sizeof(kv_key_header_t)); /* 0xFF is initial value */
+
+ if (memcmp(key_header, &header_cmp, cmp_len) == 0) {
+ return true;
+ }
+ return false;
+}
+
+errcode_t kv_key_get_handle_from_location(kv_key_location key_location, kv_key_handle_t *key)
+{
+ key->header.length = 0xFFFF; /* Would not expect a flash read to fail, but just in case... */
+ errcode_t res = kv_key_helper_copy_flash((uint32_t)(uintptr_t)&key->header, (uint32_t)(uintptr_t)key_location,
+ sizeof(kv_key_header_t));
+ if (res != ERRCODE_SUCC) {
+ return res;
+ }
+
+ key->key_location = key_location;
+
+ if (kv_key_header_is_full_ff(&key->header, (uint8_t)sizeof(kv_key_header_t))) {
+ /* Last key has been read */
+ return ERRCODE_NV_KEY_NOT_FOUND;
+ }
+
+ if (g_nv_header_magic == KV_KEY_MAGIC && key->header.magic != KV_KEY_MAGIC) {
+ /* Wrong key has been read */
+ return ERRCODE_NV_INVALID_KEY_HEADER;
+ }
+ return ERRCODE_SUCC;
+}
+
+errcode_t kv_key_locations_in_same_page(kv_key_location first_key_location, kv_key_location second_key_location)
+{
+ uintptr_t first_page_location = (uintptr_t)first_key_location & ~(KV_PAGE_SIZE - 1);
+ uintptr_t second_page_location = ((uintptr_t)second_key_location +
+ (uintptr_t)sizeof(kv_key_header_t)) & ~(KV_PAGE_SIZE - 1);
+ if (first_page_location != second_page_location) {
+ return ERRCODE_FAIL;
+ }
+ return ERRCODE_SUCC;
+}
+
+#if (CONFIG_NV_SUPPORT_SKIP_CORRUPT_KEY == NV_YES)
+errcode_t kv_key_get_next_magic_position(kv_key_location key_location, kv_key_handle_t *key)
+{
+ uint32_t src = (uint32_t)(uintptr_t)key_location;
+ kv_nvregion_area_t* nvregion_area = nv_get_region_area();
+ uint32_t start_page_loc = nvregion_area->nv_data_addr - FLASH_PHYSICAL_ADDR_START;
+ uint32_t next_page_loc = ((((src - start_page_loc) / KV_PAGE_SIZE) + 1) * KV_PAGE_SIZE) + start_page_loc;
+
+ /* 从传入位置的下一位开始读 */
+ src = src + 1;
+ bool found_ending = false;
+ kv_key_location tmp_location;
+
+ for (; src < next_page_loc - sizeof(kv_key_header_t); src++) {
+ (void)memset_s(&(key->header), sizeof(kv_key_header_t), 0, sizeof(kv_key_header_t));
+ if (kv_key_helper_copy_flash((uint32_t)(uintptr_t)&key->header, src, sizeof(kv_key_header_t)) != ERRCODE_SUCC) {
+ return ERRCODE_NV_READ_FLASH_ERR;
+ }
+
+ if (key->header.magic == KV_KEY_MAGIC &&
+ (key->header.valid == KV_KEY_INVALID || key->header.valid == KV_KEY_VALID)) {
+ key->key_location = (kv_key_location)(uintptr_t)src;
+ /* 找到有效的Key */
+ if (kv_key_validate_integrity(key, true) == ERRCODE_SUCC) {
+ nv_log_debug("[NV][magic] Get valid next key (location = 0x%x).\r\n", key->key_location);
+ return ERRCODE_SUCC;
+ }
+ }
+
+ if (kv_key_header_is_full_ff(&key->header, (uint8_t)sizeof(kv_key_header_t))) {
+ /* 找到一块全F区域,暂时标记为数据结尾 */
+ if (!found_ending) {
+ found_ending = true;
+ tmp_location = (kv_key_location)(uintptr_t)src;
+ }
+ /* 若此段为全F,直接跳到下一段进行读取,不以1的步长行进 */
+ src += (uint32_t)sizeof(kv_key_header_t) - 1;
+ } else if (found_ending) {
+ /* 如又找到一块非全F区域,则上次标记的位置不是数据结尾,取消标记 */
+ found_ending = false;
+ }
+ }
+ if (found_ending) {
+ key->key_location = tmp_location;
+ } else {
+ /* 如未找到数据结尾, 则在最后继续查找更短一些的全FF数据 */
+ for (src = next_page_loc - (uint32_t)sizeof(kv_key_header_t); src < next_page_loc; src++) {
+ uint32_t read_len = next_page_loc - src;
+ if (kv_key_helper_copy_flash((uint32_t)(uintptr_t)&key->header, src, (uint16_t)read_len) != ERRCODE_SUCC) {
+ return ERRCODE_NV_READ_FLASH_ERR;
+ }
+ if (kv_key_header_is_full_ff(&key->header, (uint8_t)read_len)) {
+ key->key_location = (kv_key_location)(uintptr_t)src;
+ nv_log_debug("[NV][magic] Get next key on end 2 (location = 0x%x).\r\n", key->key_location);
+ return ERRCODE_NV_KEY_NOT_FOUND;
+ }
+ }
+ key->key_location = (kv_key_location)(uintptr_t)src;
+ }
+
+ return ERRCODE_NV_KEY_NOT_FOUND;
+}
+
+STATIC errcode_t kv_key_get_next_key_from_location(kv_key_location cur_key_loc, kv_key_location next_key_loc,
+ kv_key_handle_t *key)
+{
+ if (kv_key_locations_in_same_page(cur_key_loc, next_key_loc) != ERRCODE_SUCC) {
+ /* Reached the end of the KV page */
+ return ERRCODE_NV_KEY_NOT_FOUND;
+ }
+
+ return kv_key_get_handle_from_location(next_key_loc, key);
+}
+
+STATIC errcode_t kv_key_get_next_handle_with_corrupt_key(kv_key_handle_t *key, kv_key_validate_status_t key_status)
+{
+ kv_key_handle_t cur_key;
+ errcode_t res = ERRCODE_SUCC;
+
+ /* 如果当前KEY是错误Key,直接需要通过magic找到下一个key */
+ if (key_status == KV_KEY_VALIDATE_WRONG) {
+ return kv_key_get_next_magic_position(key->key_location, key);
+ }
+
+ if (kv_key_header_is_full_ff(&key->header, (uint8_t)sizeof(kv_key_header_t))) {
+ /* Last key has been read */
+ return ERRCODE_NV_KEY_NOT_FOUND;
+ }
+
+ if (key->header.magic != KV_KEY_MAGIC) {
+ /* corrupt key has been read, then get next key by magic */
+ nv_log_info("[NV] header magic error(location = 0x%x)! Get next key by magic.\r\n", key->key_location);
+ return kv_key_get_next_magic_position(key->key_location, key);
+ }
+
+ uint16_t offset = kv_key_flash_size(key);
+ kv_key_location next_key_location = (kv_key_location)((uintptr_t)key->key_location + offset);
+
+ (void)memcpy_s(&cur_key, sizeof(kv_key_handle_t), key, sizeof(kv_key_handle_t));
+
+ /* 出现下面两种情况之一时:
+ 1. 下一个Key的位置与当前Key不在一个page内
+ 2. 下一个key的位置指向数据结尾
+ 首先验证当前key,如果当前KEY校验成功,则说明key length是可信的,说明确实找到了数据结尾,返回ERRCODE_NV_KEY_NOT_FOUND;
+ 如果当前KEY校验不成功,则说明key length可能是不正确的,则需要通过magic找到下一个key。
+ */
+ res = kv_key_get_next_key_from_location(key->key_location, next_key_location, key);
+ if (res != ERRCODE_SUCC) {
+ if (key_status == KV_KEY_VALIDATE_CORRECT || (kv_key_validate_integrity(&cur_key, true) == ERRCODE_SUCC)) {
+ key->key_location = next_key_location;
+ if (res == ERRCODE_NV_INVALID_KEY_HEADER) {
+ res = ERRCODE_SUCC;
+ }
+ return res;
+ } else {
+ /* 如果当前KEY校验不成功,则说明key length可能是不正确的,则需要通过magic找到下一个KEY */
+ nv_log_info("[NV] validate failed (location = 0x%x)! Get next key by magic.\r\n", cur_key.key_location);
+ return kv_key_get_next_magic_position(cur_key.key_location, key);
+ }
+ }
+
+ return res;
+}
+#endif /* #ifdef CONFIG_NV_SUPPORT_SKIP_CORRUPT_KEY */
+
+STATIC errcode_t kv_key_get_next_handle_direct(kv_key_handle_t *key)
+{
+ if (key->header.length == 0xFFFF) {
+ /* Last key has been read */
+ return ERRCODE_NV_KEY_NOT_FOUND;
+ }
+
+ uint16_t offset = kv_key_flash_size(key);
+ kv_key_location new_key_location = (kv_key_location)((uintptr_t)key->key_location + offset);
+ if (kv_key_locations_in_same_page(key->key_location, new_key_location) != ERRCODE_SUCC) {
+ /* Reached the end of the KV page */
+ return ERRCODE_NV_KEY_NOT_FOUND;
+ }
+
+ return kv_key_get_handle_from_location(new_key_location, key);
+}
+
+errcode_t kv_key_get_next_handle(kv_key_handle_t *key, kv_key_validate_status_t key_status)
+{
+ unused(key_status);
+#if (CONFIG_NV_SUPPORT_SKIP_CORRUPT_KEY == NV_YES)
+ if (g_nv_header_magic == KV_KEY_MAGIC) {
+ return kv_key_get_next_handle_with_corrupt_key(key, key_status);
+ } else {
+ return kv_key_get_next_handle_direct(key);
+ }
+#else
+ return kv_key_get_next_handle_direct(key);
+#endif
+}
+
+STATIC void kv_key_fix_header_for_validate(kv_key_header_t *key_header, bool ignor_invalid)
+{
+ if (ignor_invalid && key_header->valid == KV_KEY_INVALID) {
+ key_header->valid = KV_KEY_VALID;
+ if (g_nv_header_magic == KV_KEY_NO_MAGIC) {
+ key_header->magic = g_nv_header_magic;
+ }
+ }
+}
+
+STATIC void kv_key_hash_crc_tag_start(kv_key_handle_t *key, uint32_t *crc_ret)
+{
+ *crc_ret = 0;
+ if (key->header.enc_key != AES_KDFKEY_SDRK_TYPE) {
+ return;
+ }
+
+#if ((CONFIG_NV_SUPPORT_ENCRYPT == NV_YES) && (CONFIG_NV_SUPPORT_HASH_FOR_CRYPT == NV_YES))
+ (void)nv_crypto_start_hash();
+#endif
+}
+
+STATIC void kv_key_hash_crc_tag_update(kv_key_handle_t *key, uint8_t *data_chunk, uint32_t data_len, uint32_t *crc_ret)
+{
+ *crc_ret = uapi_crc32(*crc_ret, data_chunk, data_len);
+ if (key->header.enc_key != AES_KDFKEY_SDRK_TYPE) {
+ return;
+ }
+
+#if ((CONFIG_NV_SUPPORT_ENCRYPT == NV_YES) && (CONFIG_NV_SUPPORT_HASH_FOR_CRYPT == NV_YES))
+ nv_crypto_update_hash(data_chunk, data_len);
+#endif
+}
+
+STATIC void kv_key_hash_crc_tag_finish(kv_key_handle_t *key, uint8_t *calculated_hash, uint32_t crc_value,
+ uint32_t crypto_handle)
+{
+ unused(crypto_handle);
+ uint32_t crc_ret = kv_crc32_swap(crc_value);
+
+ if (key->header.enc_key != AES_KDFKEY_SDRK_TYPE) {
+ (void)memcpy_s((void *)calculated_hash, KV_CRYPTO_CRC_SIZE, (const void *)&crc_ret, KV_CRYPTO_CRC_SIZE);
+ return;
+ }
+
+#if (CONFIG_NV_SUPPORT_ENCRYPT == NV_YES)
+#if (CONFIG_NV_SUPPORT_HASH_FOR_CRYPT == NV_YES)
+ nv_crypto_complete_hash(calculated_hash);
+ (void)memcpy_s((void *)(calculated_hash + KV_CRYPTO_HASH_SIZE - KV_CRYPTO_CRC_SIZE), KV_CRYPTO_CRC_SIZE,
+ (const void *)&crc_ret, KV_CRYPTO_CRC_SIZE);
+#else
+ (void)memcpy_s((void *)(calculated_hash + KV_CRYPTO_HASH_SIZE - KV_CRYPTO_CRC_SIZE), KV_CRYPTO_CRC_SIZE,
+ (const void *)&crc_ret, KV_CRYPTO_CRC_SIZE);
+#endif /* (CONFIG_NV_SUPPORT_HASH_FOR_CRYPT == NV_YES) */
+#endif /* #if (CONFIG_NV_SUPPORT_ENCRYPT == NV_YES) */
+}
+
+STATIC errcode_t kv_key_hash_crc_tag_verify(kv_key_handle_t *key, uint8_t *read_hash_crc, uint8_t *cal_hash_crc,
+ uint32_t crypto_handle, bool only_crc)
+{
+ unused(crypto_handle);
+ unused(only_crc);
+ errcode_t res = ERRCODE_SUCC;
+
+ if (key->header.enc_key != AES_KDFKEY_SDRK_TYPE) {
+ if (memcmp(cal_hash_crc, read_hash_crc, KV_CRYPTO_CRC_SIZE) != 0) {
+ res = ERRCODE_NV_HASH_MISMATCH;
+ }
+ return res;
+ }
+
+#if (CONFIG_NV_SUPPORT_ENCRYPT != NV_YES)
+ /* 不支持加密的情况下直接返回失败 */
+ return ERRCODE_NV_HASH_MISMATCH;
+#else
+ if (only_crc) {
+ /* 只校验CRC的场景,校验最后4个字节的CRC(密文的CRC) */
+ if (memcmp(cal_hash_crc + KV_CRYPTO_HASH_SIZE - KV_CRYPTO_CRC_SIZE,
+ read_hash_crc + KV_CRYPTO_HASH_SIZE - KV_CRYPTO_CRC_SIZE, KV_CRYPTO_CRC_SIZE) != 0) {
+ res = ERRCODE_NV_HASH_MISMATCH;
+ }
+ return res;
+ }
+#if (CONFIG_NV_SUPPORT_HASH_FOR_CRYPT == NV_YES)
+ /* 使用HASH校验的场景,校验HASH的前28个字节 */
+ if (memcmp(cal_hash_crc, read_hash_crc, KV_CRYPTO_HASH_SIZE - KV_CRYPTO_CRC_SIZE) != 0) {
+ res = ERRCODE_NV_HASH_MISMATCH;
+ }
+ return res;
+#else
+ /* 使用GCM TAG的场景,直接校验16字节的TAG */
+ return nv_crypto_validate_tag(crypto_handle);
+#endif /* (CONFIG_NV_SUPPORT_HASH_FOR_CRYPT == NV_YES)) */
+#endif /* #if (CONFIG_NV_SUPPORT_ENCRYPT != NV_YES) */
+}
+
+STATIC errcode_t kv_key_validate_hash_chunks(kv_key_handle_t *key, uint8_t *read_data_chunk, uint8_t *cal_hash_crc,
+ uint32_t crypto_handle, bool ignor_invalid)
+{
+ errcode_t res;
+ kv_key_header_t *key_header = (kv_key_header_t *)read_data_chunk;
+ uintptr_t key_location = (uintptr_t)key->key_location;
+ uint32_t crc_ret = 0;
+ uint32_t hash_crc_len = ((key->header.enc_key == AES_KDFKEY_SDRK_TYPE) ? KV_CRYPTO_HASH_SIZE : KV_CRYPTO_CRC_SIZE);
+
+ kv_key_hash_crc_tag_start(key, &crc_ret);
+
+ /* Read header first, which is never encrypted */
+ res = kv_key_read_data_from_flash((uintptr_t)read_data_chunk, key_location,
+ sizeof(kv_key_header_t), INVAILD_CRYPTO_HANDLE);
+ if (res != ERRCODE_SUCC) {
+ return res;
+ }
+
+ kv_key_fix_header_for_validate(key_header, ignor_invalid);
+
+ kv_key_hash_crc_tag_update(key, read_data_chunk, sizeof(kv_key_header_t), &crc_ret);
+
+ const kv_attributes_t attributes = kv_key_attributes(key);
+ uint32_t decryptable_data_len = kv_key_padded_data_length(attributes, key->header.length);
+#if (CONFIG_NV_SUPPORT_HASH_FOR_CRYPT == NV_NO)
+ if (crypto_handle != INVAILD_CRYPTO_HANDLE) {
+ /* 在使用GCM TAG时,先读取一次TAG并设置给解密模块 */
+ /* Read key hash, which could be unencrypted */
+ uintptr_t hash_location = key_location + (uintptr_t)sizeof(kv_key_header_t) + (uintptr_t)decryptable_data_len;
+ (void)kv_key_read_data_from_flash((uintptr_t)read_data_chunk, hash_location,
+ KV_CRYPTO_HASH_SIZE, INVAILD_CRYPTO_HANDLE);
+ (void)nv_crypto_set_tag(crypto_handle, read_data_chunk, NV_AES_GCM_TAG_LENGTH);
+ }
+#endif
+
+ /* Read rest of key data, which could be encrypted */
+ uint32_t key_data_offset = 0;
+ key_location += (uintptr_t)sizeof(kv_key_header_t);
+ while (key_data_offset < decryptable_data_len) {
+ uint32_t chunk_len = uapi_min(NV_KEY_DATA_CHUNK_LEN, decryptable_data_len - key_data_offset);
+
+ res = kv_key_read_data_from_flash((uintptr_t)read_data_chunk, (key_location + key_data_offset),
+ chunk_len, crypto_handle);
+ if (res != ERRCODE_SUCC) {
+ return res;
+ }
+
+ kv_key_hash_crc_tag_update(key, read_data_chunk, chunk_len, &crc_ret);
+ key_data_offset += chunk_len;
+ }
+
+ /* Read key hash, which could be unencrypted */
+ res = kv_key_read_data_from_flash((uintptr_t)read_data_chunk, (key_location + key_data_offset),
+ hash_crc_len, INVAILD_CRYPTO_HANDLE);
+
+ kv_key_hash_crc_tag_finish(key, cal_hash_crc, crc_ret, crypto_handle);
+ return res;
+}
+
+/*
+ * 校验KEY的完整性。
+ * 非加密KEY, 校验CRC;
+ * 加密KEY, 如CONFIG_NV_SUPPORT_HASH_FOR_CRYPT为YES,校验明文HASH;
+ * 如CONFIG_NV_SUPPORT_HASH_FOR_CRYPT为NO,校验密文的CRC(CRC存放在16字节Tag之后)。
+ */
+errcode_t kv_key_validate_integrity(kv_key_handle_t *key, bool ignor_invalid)
+{
+ uint32_t crypto_handle = INVAILD_CRYPTO_HANDLE;
+
+ if (key->header.length > NV_NORMAL_KVALUE_MAX_LEN) {
+ return ERRCODE_NV_INVALID_KEY_HEADER;
+ }
+
+ uint8_t *calculated_hash = (uint8_t *)kv_malloc(KV_CRYPTO_HASH_SIZE);
+ if (calculated_hash == NULL) {
+ return ERRCODE_MALLOC;
+ }
+
+ uint8_t *read_data_chunk = (uint8_t *)kv_malloc(NV_KEY_DATA_CHUNK_LEN);
+ if (read_data_chunk == NULL) {
+ kv_free(calculated_hash);
+ return ERRCODE_MALLOC;
+ }
+
+ errcode_t res = kv_key_validate_hash_chunks(key, read_data_chunk, calculated_hash, crypto_handle, ignor_invalid);
+ if (res != ERRCODE_SUCC) {
+ goto err;
+ }
+
+ res = kv_key_hash_crc_tag_verify(key, read_data_chunk, calculated_hash, crypto_handle, true);
+
+err:
+ kv_free(read_data_chunk);
+ kv_free(calculated_hash);
+ return res;
+}
+
+/*
+ * 校验KEY的正确性:
+ * 非加密KEY校验CRC
+ * 加密KEY, 如CONFIG_NV_SUPPORT_HASH_FOR_CRYPT为YES,校验明文HASH;
+ * 如CONFIG_NV_SUPPORT_HASH_FOR_CRYPT为NO,则校验GCM Tag。
+ */
+errcode_t kv_key_validation(kv_key_handle_t *key, bool ignor_invalid)
+{
+ errcode_t res;
+ uint32_t crypto_handle = INVAILD_CRYPTO_HANDLE;
+
+ if (key->header.length > NV_NORMAL_KVALUE_MAX_LEN) {
+ return ERRCODE_NV_INVALID_KEY_HEADER;
+ }
+
+ uint8_t *calculated_hash = (uint8_t *)kv_malloc(KV_CRYPTO_HASH_SIZE);
+ if (calculated_hash == NULL) {
+ return ERRCODE_MALLOC;
+ }
+ (void)memset_s(calculated_hash, KV_CRYPTO_HASH_SIZE, 0, KV_CRYPTO_HASH_SIZE);
+
+ uint8_t *read_data_chunk = (uint8_t *)kv_malloc(NV_KEY_DATA_CHUNK_LEN);
+ if (read_data_chunk == NULL) {
+ kv_free(calculated_hash);
+ return ERRCODE_MALLOC;
+ }
+
+ (void)memset_s(read_data_chunk, NV_KEY_DATA_CHUNK_LEN, 0, NV_KEY_DATA_CHUNK_LEN);
+
+#if (CONFIG_NV_SUPPORT_ENCRYPT == NV_YES)
+ if (key->header.enc_key != 0) {
+ /* Read key data, using AES engine if encrypted */
+ res = nv_crypto_claim_aes(&crypto_handle, &(key->header));
+ if (res != ERRCODE_SUCC) {
+ goto err;
+ }
+ }
+#endif
+
+ res = kv_key_validate_hash_chunks(key, read_data_chunk, calculated_hash, crypto_handle, ignor_invalid);
+ if (res != ERRCODE_SUCC) {
+ goto err;
+ }
+
+ res = kv_key_hash_crc_tag_verify(key, read_data_chunk, calculated_hash, crypto_handle, false);
+
+err:
+ kv_free(read_data_chunk);
+ kv_free(calculated_hash);
+
+#if (CONFIG_NV_SUPPORT_ENCRYPT == NV_YES)
+ if (crypto_handle != INVAILD_CRYPTO_HANDLE) {
+ nv_crypto_release_aes(crypto_handle);
+ }
+#endif
+ return res;
+}
+
+kv_attributes_t kv_key_attributes(const kv_key_handle_t *key)
+{
+ kv_attributes_t attributes = 0;
+
+ if (key->header.type != KV_KEY_TYPE_NORMAL) {
+ attributes = (kv_attributes_t)((uint32_t)attributes | (uint32_t)KV_ATTRIBUTE_PERMANENT);
+ }
+ if (key->header.enc_key != 0) {
+ attributes = (kv_attributes_t)((uint32_t)attributes | (uint32_t)KV_ATTRIBUTE_ENCRYPTED);
+ }
+ if (key->header.upgrade != KV_KEY_TYPE_NORMAL) {
+ attributes = (kv_attributes_t)((uint32_t)attributes | (uint32_t)KV_ATTRIBUTE_NON_UPGRADE);
+ }
+ return attributes;
+}
+
+uint16_t kv_key_padded_data_length(kv_attributes_t attributes, uint16_t unpadded_length)
+{
+ if (((uint32_t)attributes & (uint32_t)KV_ATTRIBUTE_ENCRYPTED) != 0) {
+ return kv_padded_ciphertext_length(unpadded_length);
+ } else {
+ return kv_padded_cleartext_length(unpadded_length);
+ }
+}
+
+uint16_t kv_key_flash_size(kv_key_handle_t *key)
+{
+ uint16_t size;
+ kv_attributes_t attributes;
+ uint16_t hash_crc_size;
+
+ if (key->header.enc_key == AES_KDFKEY_SDRK_TYPE) {
+ hash_crc_size = KV_CRYPTO_HASH_SIZE;
+ } else {
+ hash_crc_size = KV_CRYPTO_CRC_SIZE;
+ }
+
+ size = (uint16_t)sizeof(kv_key_header_t) + hash_crc_size;
+
+ attributes = kv_key_attributes(key);
+ size += kv_key_padded_data_length(attributes, key->header.length);
+
+ return size;
+}
+
+void kv_key_build_from_new(kv_key_handle_t *key, const kv_key_details_t *new_key, kv_key_location key_location)
+{
+ key->header.key_id = new_key->key_id;
+ key->header.length = (uint16_t)new_key->kvalue_length;
+ key->header.magic = g_nv_header_magic;
+ key->header.valid = KV_KEY_VALID;
+ key->header.type = KV_KEY_TYPE_NORMAL;
+ if (((uint32_t)new_key->attributes & (uint32_t)KV_ATTRIBUTE_PERMANENT) != 0) {
+ key->header.type = KV_KEY_TYPE_PERMANENT;
+ }
+ key->header.upgrade = KV_KEY_TYPE_NORMAL;
+ if (((uint32_t)new_key->attributes & (uint32_t)KV_ATTRIBUTE_NON_UPGRADE) != 0) {
+ key->header.upgrade = 0;
+ key->header.version = key->header.version + 1;
+ }
+ key->header.enc_key = 0;
+ if (((uint32_t)new_key->attributes & (uint32_t)KV_ATTRIBUTE_ENCRYPTED) != 0) {
+ key->header.enc_key = AES_KDFKEY_SDRK_TYPE;
+#if (CONFIG_NV_SUPPORT_ENCRYPT == NV_YES)
+ nv_crypto_generate_random(&key->header.rnd);
+#endif
+ key->header.version = key->header.version + 1;
+ }
+
+ key->key_location = key_location;
+}
+
+uint32_t kv_crc32_swap(uint32_t crc)
+{
+ return ((crc >> CRC_SWAP_SIZE24) | ((crc >> CRC_SWAP_SIZE8) & 0xFF00) |
+ ((crc << CRC_SWAP_SIZE8) & 0xFF0000) | (crc << CRC_SWAP_SIZE24));
}
\ No newline at end of file
-/*\r
- * Copyright (c) @CompanyNameMagicTag 2019-2022. All rights reserved.\r
- * Description: KV Storage Library key access interface\r
- */\r
-\r
-#ifndef NV_KEY_H\r
-#define NV_KEY_H\r
-\r
-#include "stdbool.h"\r
-#include "errcode.h"\r
-\r
-#ifdef __cplusplus\r
-#if __cplusplus\r
-extern "C" {\r
-#endif /* __cplusplus */\r
-#endif /* __cplusplus */\r
-\r
-#define KV_KEY_MAGIC 0xA9\r
-#define KV_KEY_VALID 0xFF\r
-#define KV_KEY_INVALID 0\r
-#define KV_KEY_NO_MAGIC 0xFF\r
-\r
-#define KV_KEY_TYPE_NORMAL 0xFF\r
-#define KV_KEY_TYPE_PERMANENT 0\r
-\r
-#define KV_KEY_FORCE_UPDATE 0xf0\r
-\r
-#define KV_CRYPTO_HASH_SIZE 32\r
-#define KV_CRYPTO_CRC_SIZE 4\r
-#define KV_CRYPTO_CRC32_SIZE 32\r
-#define AES_BLOCK_SIZE 16\r
-#define NV_AES_GCM_TAG_LENGTH 16\r
-#define MIN_AES_DECRYPT_LEN (2 * AES_BLOCK_SIZE)\r
-#define AES_KDFKEY_SDRK_TYPE 0x52\r
-#define CRC_SWAP_SIZE8 8\r
-#define CRC_SWAP_SIZE24 24\r
-#define INVAILD_CRYPTO_HANDLE 0xFFFFFFFF\r
-\r
-typedef enum {\r
- KV_KEY_FILTER_STATE_VALID = 0x01,\r
- KV_KEY_FILTER_STATE_INVALID = 0x02,\r
- KV_KEY_FILTER_STATE_ANY = 0x03\r
-} kv_key_filter_state_t;\r
-\r
-typedef enum {\r
- KV_KEY_FILTER_TYPE_NORMAL = 0x01,\r
- KV_KEY_FILTER_TYPE_PERMANENT = 0x02,\r
- KV_KEY_FILTER_TYPE_ANY = 0x03\r
-} kv_key_filter_type_t;\r
-\r
-/**\r
- * KV key attribute flags\r
- */\r
-typedef enum {\r
- KV_ATTRIBUTE_PERMANENT = 0x1, /* Key is permanent and can't be deleted or modified */\r
- KV_ATTRIBUTE_ENCRYPTED = 0x2, /* Key is encrypted in flash */\r
- KV_ATTRIBUTE_NON_UPGRADE = 0x4 /* key cannot be modified during the upgrade. */\r
-} kv_attributes_t;\r
-\r
-typedef enum {\r
- KV_KEY_NOT_VALIDATE,\r
- KV_KEY_VALIDATE_CORRECT,\r
- KV_KEY_VALIDATE_WRONG\r
-} kv_key_validate_status_t;\r
-\r
-typedef uint16_t kv_key_id;\r
-\r
-/** Represents the location in flash of a KV key. Provided in such a way that it can't be dereferenced,\r
- * as flash is not directly addressable */\r
-typedef const void *kv_key_location;\r
-\r
-/** Defines a set of conditions to match when searching through stores for keys */\r
-typedef struct {\r
- kv_key_location location; /* (retained) location is out of context here */\r
- kv_key_id pattern; /* Pattern of bits matched against a key_id, after mask has been applied */\r
- kv_key_id mask; /* Mask is applied to a key id before matching pattern */\r
- kv_key_filter_state_t state; /* Include only keys of the specified state in search results */\r
- kv_key_filter_type_t type; /* Include only keys of the specified type in search results */\r
-} kv_key_filter_t;\r
-\r
-/** KV store key header */\r
-typedef struct {\r
- uint8_t magic; /* Magic number to indicate the start of the item */\r
- uint8_t valid; /* flag to indicate whether the value is valid. */\r
- uint16_t length; /* Length of the key_data field in bytes */\r
- uint8_t type; /* Normal (0xFF) or permanent (0x00) */\r
- uint8_t upgrade; /* Indicating whether upgradeable(not.0x00) */\r
- uint16_t key_id; /* The Key ID */\r
- uint16_t enc_key; /* Allows some customisation of the data AES key used, */\r
- /* 0x0 - key_data is plaintext, Others - key_data is encrypted */\r
- uint16_t version; /* Version of the key */\r
- uint32_t rnd; /* align the header to be 16bytes, random number */\r
-} kv_key_header_t;\r
-\r
-/** A KV key handle, which provides both key header information and the location of key data in flash */\r
-typedef struct {\r
- kv_key_header_t header;\r
- kv_key_location key_location; /* Location in flash of the KV key */\r
-} kv_key_handle_t;\r
-\r
-typedef struct {\r
- kv_key_id key_id;\r
- bool focre_write;\r
- const uint8_t *kvalue;\r
- uint32_t kvalue_length;\r
- kv_attributes_t attributes;\r
-} kv_key_details_t;\r
-typedef struct {\r
- uint32_t crypto_handle;\r
- uint8_t *key_data_chunk;\r
- uint8_t *compare_data_chunk;\r
-} kv_helper_compare_key_data_info_t;\r
-\r
-extern uint8_t g_nv_header_magic;\r
-\r
-errcode_t kv_key_helper_copy_flash(uint32_t dest_location, uint32_t src_location, uint16_t length);\r
-errcode_t kv_key_direct_write_flash(uint32_t dest, uint32_t length, const uint8_t *src);\r
-errcode_t kv_key_direct_erase_flash(uint32_t dest, const uint32_t size);\r
-errcode_t kv_key_write_flash(uint32_t dest, uint32_t length, const uint8_t *src);\r
-errcode_t kv_key_erase_flash(uint32_t dest, const uint32_t size);\r
-bool kv_key_is_erased(const kv_key_handle_t *key);\r
-errcode_t kv_key_validate_integrity(kv_key_handle_t *key, bool ignor_invalid);\r
-errcode_t kv_key_validation(kv_key_handle_t *key, bool ignor_invalid);\r
-bool kv_key_is_valid(kv_key_handle_t *key);\r
-errcode_t kv_key_does_pattern_match(const kv_key_handle_t *key, kv_key_id search_pattern, kv_key_id search_mask);\r
-errcode_t kv_key_does_filter_match(kv_key_handle_t *key, kv_key_filter_t *search_filter);\r
-errcode_t kv_key_get_handle_from_location(kv_key_location key_location, kv_key_handle_t *key);\r
-errcode_t kv_key_locations_in_same_page(kv_key_location first_key_location, kv_key_location second_key_location);\r
-errcode_t kv_key_get_next_handle(kv_key_handle_t *key, kv_key_validate_status_t key_status);\r
-kv_attributes_t kv_key_attributes(const kv_key_handle_t *key);\r
-uint16_t kv_key_padded_data_length(kv_attributes_t attributes, uint16_t unpadded_length);\r
-uint16_t kv_key_flash_size(kv_key_handle_t *key);\r
-errcode_t kv_key_read_data(kv_key_handle_t *key, uint8_t *dest_location);\r
-errcode_t kv_helper_compare_key_data(kv_key_handle_t *key, const uint8_t *compare_data, uint16_t compare_length);\r
-void kv_key_build_from_new(kv_key_handle_t *key, const kv_key_details_t *new_key, kv_key_location key_location);\r
-errcode_t kv_key_get_next_magic_position(kv_key_location key_location, kv_key_handle_t *key);\r
-\r
-uint32_t kv_crc32_swap(uint32_t crc);\r
-\r
-#ifdef __cplusplus\r
-#if __cplusplus\r
-}\r
-#endif /* __cplusplus */\r
-#endif /* __cplusplus */\r
-\r
-#endif /* NV_KEY_H */\r
+/*
+ * Copyright (c) @CompanyNameMagicTag 2019-2022. All rights reserved.
+ * Description: KV Storage Library key access interface
+ */
+
+#ifndef NV_KEY_H
+#define NV_KEY_H
+
+#include "stdbool.h"
+#include "errcode.h"
+
+#ifdef __cplusplus
+#if __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+#endif /* __cplusplus */
+
+#define KV_KEY_MAGIC 0xA9
+#define KV_KEY_VALID 0xFF
+#define KV_KEY_INVALID 0
+#define KV_KEY_NO_MAGIC 0xFF
+
+#define KV_KEY_TYPE_NORMAL 0xFF
+#define KV_KEY_TYPE_PERMANENT 0
+
+#define KV_KEY_FORCE_UPDATE 0xf0
+
+#define KV_CRYPTO_HASH_SIZE 32
+#define KV_CRYPTO_CRC_SIZE 4
+#define KV_CRYPTO_CRC32_SIZE 32
+#define AES_BLOCK_SIZE 16
+#define NV_AES_GCM_TAG_LENGTH 16
+#define MIN_AES_DECRYPT_LEN (2 * AES_BLOCK_SIZE)
+#define AES_KDFKEY_SDRK_TYPE 0x52
+#define CRC_SWAP_SIZE8 8
+#define CRC_SWAP_SIZE24 24
+#define INVAILD_CRYPTO_HANDLE 0xFFFFFFFF
+
+typedef enum {
+ KV_KEY_FILTER_STATE_VALID = 0x01,
+ KV_KEY_FILTER_STATE_INVALID = 0x02,
+ KV_KEY_FILTER_STATE_ANY = 0x03
+} kv_key_filter_state_t;
+
+typedef enum {
+ KV_KEY_FILTER_TYPE_NORMAL = 0x01,
+ KV_KEY_FILTER_TYPE_PERMANENT = 0x02,
+ KV_KEY_FILTER_TYPE_ANY = 0x03
+} kv_key_filter_type_t;
+
+/**
+ * KV key attribute flags
+ */
+typedef enum {
+ KV_ATTRIBUTE_PERMANENT = 0x1, /* Key is permanent and can't be deleted or modified */
+ KV_ATTRIBUTE_ENCRYPTED = 0x2, /* Key is encrypted in flash */
+ KV_ATTRIBUTE_NON_UPGRADE = 0x4 /* key cannot be modified during the upgrade. */
+} kv_attributes_t;
+
+typedef enum {
+ KV_KEY_NOT_VALIDATE,
+ KV_KEY_VALIDATE_CORRECT,
+ KV_KEY_VALIDATE_WRONG
+} kv_key_validate_status_t;
+
+typedef uint16_t kv_key_id;
+
+/** Represents the location in flash of a KV key. Provided in such a way that it can't be dereferenced,
+ * as flash is not directly addressable */
+typedef const void *kv_key_location;
+
+/** Defines a set of conditions to match when searching through stores for keys */
+typedef struct {
+ kv_key_location location; /* (retained) location is out of context here */
+ kv_key_id pattern; /* Pattern of bits matched against a key_id, after mask has been applied */
+ kv_key_id mask; /* Mask is applied to a key id before matching pattern */
+ kv_key_filter_state_t state; /* Include only keys of the specified state in search results */
+ kv_key_filter_type_t type; /* Include only keys of the specified type in search results */
+} kv_key_filter_t;
+
+/** KV store key header */
+typedef struct {
+ uint8_t magic; /* Magic number to indicate the start of the item */
+ uint8_t valid; /* flag to indicate whether the value is valid. */
+ uint16_t length; /* Length of the key_data field in bytes */
+ uint8_t type; /* Normal (0xFF) or permanent (0x00) */
+ uint8_t upgrade; /* Indicating whether upgradeable(not.0x00) */
+ uint16_t key_id; /* The Key ID */
+ uint16_t enc_key; /* Allows some customisation of the data AES key used, */
+ /* 0x0 - key_data is plaintext, Others - key_data is encrypted */
+ uint16_t version; /* Version of the key */
+ uint32_t rnd; /* align the header to be 16bytes, random number */
+} kv_key_header_t;
+
+/** A KV key handle, which provides both key header information and the location of key data in flash */
+typedef struct {
+ kv_key_header_t header;
+ kv_key_location key_location; /* Location in flash of the KV key */
+} kv_key_handle_t;
+
+typedef struct {
+ kv_key_id key_id;
+ bool focre_write;
+ const uint8_t *kvalue;
+ uint32_t kvalue_length;
+ kv_attributes_t attributes;
+} kv_key_details_t;
+typedef struct {
+ uint32_t crypto_handle;
+ uint8_t *key_data_chunk;
+ uint8_t *compare_data_chunk;
+} kv_helper_compare_key_data_info_t;
+
+extern uint8_t g_nv_header_magic;
+
+errcode_t kv_key_helper_copy_flash(uint32_t dest_location, uint32_t src_location, uint16_t length);
+errcode_t kv_key_direct_write_flash(uint32_t dest, uint32_t length, const uint8_t *src);
+errcode_t kv_key_direct_erase_flash(uint32_t dest, const uint32_t size);
+errcode_t kv_key_write_flash(uint32_t dest, uint32_t length, const uint8_t *src);
+errcode_t kv_key_erase_flash(uint32_t dest, const uint32_t size);
+bool kv_key_is_erased(const kv_key_handle_t *key);
+errcode_t kv_key_validate_integrity(kv_key_handle_t *key, bool ignor_invalid);
+errcode_t kv_key_validation(kv_key_handle_t *key, bool ignor_invalid);
+bool kv_key_is_valid(kv_key_handle_t *key);
+errcode_t kv_key_does_pattern_match(const kv_key_handle_t *key, kv_key_id search_pattern, kv_key_id search_mask);
+errcode_t kv_key_does_filter_match(kv_key_handle_t *key, kv_key_filter_t *search_filter);
+errcode_t kv_key_get_handle_from_location(kv_key_location key_location, kv_key_handle_t *key);
+errcode_t kv_key_locations_in_same_page(kv_key_location first_key_location, kv_key_location second_key_location);
+errcode_t kv_key_get_next_handle(kv_key_handle_t *key, kv_key_validate_status_t key_status);
+kv_attributes_t kv_key_attributes(const kv_key_handle_t *key);
+uint16_t kv_key_padded_data_length(kv_attributes_t attributes, uint16_t unpadded_length);
+uint16_t kv_key_flash_size(kv_key_handle_t *key);
+errcode_t kv_key_read_data(kv_key_handle_t *key, uint8_t *dest_location);
+errcode_t kv_helper_compare_key_data(kv_key_handle_t *key, const uint8_t *compare_data, uint16_t compare_length);
+void kv_key_build_from_new(kv_key_handle_t *key, const kv_key_details_t *new_key, kv_key_location key_location);
+errcode_t kv_key_get_next_magic_position(kv_key_location key_location, kv_key_handle_t *key);
+
+uint32_t kv_crc32_swap(uint32_t crc);
+
+#ifdef __cplusplus
+#if __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* __cplusplus */
+
+#endif /* NV_KEY_H */
-/*\r
- * Copyright (c) @CompanyNameMagicTag 2019-2022. All rights reserved.\r
- * Description: KV Storage Library non-volatile region access module implementation\r
- */\r
-\r
-#include "nv_nvregion.h"\r
-#include "nv_page.h"\r
-#include "nv_store.h"\r
-#include "nv_porting.h"\r
-#include "nv_config.h"\r
-#include "common_def.h"\r
-#ifndef CONFIG_NV_SUPPORT_SINGLE_CORE_SYSTEM\r
-#include "flash_task_mutex.h"\r
-#include "flash_task_adapt.h"\r
-#endif\r
-\r
-static kv_nvregion_map_t g_kv_nvregion_map = {NULL, NULL, 0};\r
-static kv_nvregion_area_t g_kv_nvregion_area = {KV_STORE_START_ADDR, KV_STORE_DATA_SIZE,\r
- KV_BACKUP_START_ADDR, KV_BACKUP_DATA_SIZE};\r
-uint32_t g_kv_nvregion_use_times[KV_STORE_DATA_SIZE / KV_PAGE_SIZE] = {0};\r
-\r
-kv_nvregion_map_t* kv_nvregion_get_map(void)\r
-{\r
- return &g_kv_nvregion_map;\r
-}\r
-\r
-uint32_t kv_nvregion_get_use_times(kv_page_location page_location)\r
-{\r
- uint32_t page_number = kv_nvregion_get_page_number(page_location);\r
- if (page_number >= g_kv_nvregion_map.num_entries) {\r
- return (uint32_t)-1;\r
- }\r
-\r
- return g_kv_nvregion_map.entries[page_number].sequence_number;\r
-}\r
-\r
-static void kv_nvregion_clear_map_entry(kv_page_header_t *map_entry)\r
-{\r
- (void)memset_s(map_entry, sizeof(kv_page_header_t), 0, sizeof(kv_page_header_t));\r
-}\r
-\r
-static errcode_t kv_nvregion_is_valid_map_entry(const kv_page_header_t *map_entry)\r
-{\r
- if ((map_entry->details.store_id != 0) && (map_entry->details.store_id != 0xFFFF) &&\r
- (~map_entry->inverted_details_word == *(uint32_t *)(uintptr_t)&map_entry->details) &&\r
- (~map_entry->inverted_sequence_number == map_entry->sequence_number)) {\r
- return ERRCODE_SUCC;\r
- }\r
- return ERRCODE_FAIL;\r
-}\r
-\r
-static void kv_nvregion_filter_duplicate_map_entries(kv_page_header_t *new_entry)\r
-{\r
- kv_page_header_t *existing_entry = g_kv_nvregion_map.entries;\r
- while (existing_entry < new_entry) {\r
- if ((existing_entry->details.store_id == new_entry->details.store_id) &&\r
- (existing_entry->details.page_index == new_entry->details.page_index)) {\r
- if (existing_entry->sequence_number <= new_entry->sequence_number) {\r
- /* An existing map entry is older than the new one, use the new one */\r
- /* Two entries have the same sequence number. This is unexpected. */\r
- /* For the moment use the new entry, but it could be better to try and determine based on content */\r
- /* e.g. amount of free space, number of erased keys, corrupted keys */\r
- kv_nvregion_clear_map_entry(existing_entry);\r
- } else {\r
- /* An existing map entry is more recent than the new one */\r
- kv_nvregion_clear_map_entry(new_entry);\r
- break;\r
- }\r
- }\r
- existing_entry++;\r
- }\r
-}\r
-\r
-STATIC void kv_nvregion_free_resource(void)\r
-{\r
- if (g_kv_nvregion_map.entries != NULL) {\r
- kv_free(g_kv_nvregion_map.entries);\r
- g_kv_nvregion_map.entries = NULL;\r
- }\r
- if (g_kv_nvregion_map.page_status_map != NULL) {\r
- kv_free(g_kv_nvregion_map.page_status_map);\r
- g_kv_nvregion_map.page_status_map = NULL;\r
- }\r
-}\r
-\r
-STATIC errcode_t kv_nvregion_init_map(void)\r
-{\r
- /* Create blank NV region map */\r
- uint32_t num_entries = g_kv_nvregion_area.nv_data_size / KV_PAGE_SIZE;\r
- if (num_entries == 0) {\r
- return ERRCODE_NV_INVALID_PARAMS;\r
- }\r
-\r
- /* Obtain details of NV memory region */\r
- if (((g_kv_nvregion_area.nv_data_addr & (KV_PAGE_SIZE - 1)) != 0) ||\r
- ((g_kv_nvregion_area.nv_data_size & (KV_PAGE_SIZE - 1)) != 0) ||\r
- ((g_kv_nvregion_area.nv_data_size & ~(KV_PAGE_SIZE - 1)) == 0)) {\r
- /* NV Data address or size not aligned to a page boundry */\r
- return ERRCODE_FAIL;\r
- }\r
-\r
- /* Free up any existing NV region map */\r
- if (num_entries != g_kv_nvregion_map.num_entries) {\r
- kv_nvregion_free_resource();\r
- }\r
-\r
- if (g_kv_nvregion_map.entries == NULL) {\r
- g_kv_nvregion_map.entries = (kv_page_header_t *)kv_malloc(num_entries * sizeof(kv_page_header_t));\r
- }\r
-\r
- if (g_kv_nvregion_map.page_status_map == NULL) {\r
- g_kv_nvregion_map.page_status_map =\r
- (nv_page_status_map_t *)kv_malloc(num_entries * sizeof(nv_page_status_map_t));\r
- }\r
-\r
- if ((g_kv_nvregion_map.entries == NULL) || (g_kv_nvregion_map.page_status_map == NULL)) {\r
- kv_nvregion_free_resource();\r
- return ERRCODE_MALLOC;\r
- }\r
-\r
- (void)memset_s(g_kv_nvregion_map.entries, num_entries * sizeof(kv_page_header_t), 0,\r
- num_entries * sizeof(kv_page_header_t));\r
- (void)memset_s(g_kv_nvregion_map.page_status_map, num_entries * sizeof(nv_page_status_map_t), 0,\r
- num_entries * sizeof(nv_page_status_map_t));\r
- g_kv_nvregion_map.num_entries = num_entries;\r
- return ERRCODE_SUCC;\r
-}\r
-\r
-static errcode_t kv_nvregion_build_map(void)\r
-{\r
- errcode_t ret;\r
- unused(ret);\r
- uint32_t nv_page = g_kv_nvregion_area.nv_data_addr;\r
-\r
- for (uint32_t i = 0; i < g_kv_nvregion_map.num_entries; i++) {\r
- kv_page_header_t *new_entry = &g_kv_nvregion_map.entries[i];\r
- ret = kv_key_helper_copy_flash((uint32_t)(uintptr_t)new_entry, (uint32_t)nv_page, sizeof(kv_page_header_t));\r
-\r
- if (kv_nvregion_is_valid_map_entry(new_entry) == ERRCODE_SUCC\r
- && new_entry->details.store_id != KV_STORE_ID_BACKUP) {\r
- if (new_entry->details.ver == 0) {\r
- g_nv_header_magic = KV_KEY_NO_MAGIC;\r
- }\r
- kv_nvregion_filter_duplicate_map_entries(new_entry);\r
- kv_page_handle_t page;\r
- page.page_location = (kv_page_location)(uintptr_t)nv_page;\r
- kv_page_read_status_to_map(&page, &(g_kv_nvregion_map.page_status_map[i]));\r
- } else {\r
- kv_nvregion_clear_map_entry(new_entry);\r
- }\r
-\r
- nv_page += KV_PAGE_SIZE;\r
- }\r
- nv_log_debug("[NV] g_nv_header_magic = 0x%x.\r\n", g_nv_header_magic);\r
- return ERRCODE_SUCC;\r
-}\r
-\r
-void kv_nvregion_init(uint32_t nv_data_addr, uint32_t nv_data_size, uint32_t nv_backup_addr, uint32_t nv_backup_size)\r
-{\r
- g_kv_nvregion_area.nv_data_addr = nv_data_addr;\r
- g_kv_nvregion_area.nv_data_size = nv_data_size;\r
- g_kv_nvregion_area.nv_backup_addr = nv_backup_addr;\r
- g_kv_nvregion_area.nv_backup_size = nv_backup_size;\r
-}\r
-\r
-kv_nvregion_area_t* nv_get_region_area(void)\r
-{\r
- return &g_kv_nvregion_area;\r
-}\r
-\r
-errcode_t kv_nvregion_scan(void)\r
-{\r
- errcode_t res;\r
- res = kv_nvregion_init_map();\r
- if (res != ERRCODE_SUCC) {\r
- return res;\r
- }\r
- return kv_nvregion_build_map();\r
-}\r
-\r
-errcode_t kv_nvregion_create_page(uint16_t store_id, uint8_t page_index)\r
-{\r
- errcode_t res;\r
- kv_page_location page_location;\r
-\r
- if ((g_kv_nvregion_map.entries == NULL) || (g_kv_nvregion_map.num_entries == 0)) {\r
- return ERRCODE_NV_NOT_INITIALISED;\r
- }\r
- if (store_id == 0) {\r
- return ERRCODE_INVALID_PARAM;\r
- }\r
-\r
- res = kv_nvregion_find_unused_page(&page_location);\r
- if (res != ERRCODE_SUCC) {\r
- return res;\r
- }\r
-\r
- res = kv_nvregion_erase_page(page_location);\r
- if (res != ERRCODE_SUCC) {\r
- return res;\r
- }\r
-\r
- res = kv_nvregion_write_page(page_location, store_id, page_index);\r
- if (res != ERRCODE_SUCC) {\r
- return res;\r
- }\r
-\r
- return kv_nvregion_scan();\r
-}\r
-\r
-errcode_t kv_nvregion_find_page(uint16_t store_id, uint8_t page_index,\r
- kv_page_location *location, kv_page_header_t *header)\r
-{\r
- if ((g_kv_nvregion_map.entries == NULL) || (g_kv_nvregion_map.num_entries == 0)) {\r
- return ERRCODE_NV_NOT_INITIALISED;\r
- }\r
- if (store_id == 0) {\r
- return ERRCODE_INVALID_PARAM;\r
- }\r
- for (uint32_t i = 0; i < g_kv_nvregion_map.num_entries; i++) {\r
- kv_page_header_t *map_entry = &g_kv_nvregion_map.entries[i];\r
- if ((map_entry->details.store_id == store_id) && (map_entry->details.page_index == page_index)) {\r
- if (location != NULL) {\r
- uint32_t nv_page = g_kv_nvregion_area.nv_data_addr;\r
- nv_page += KV_PAGE_SIZE * i;\r
- *location = (kv_page_location)(uintptr_t)nv_page;\r
- }\r
- if (header != NULL) {\r
- (void)memcpy_s(header, sizeof(kv_page_header_t), map_entry, sizeof(kv_page_header_t));\r
- }\r
- return ERRCODE_SUCC;\r
- }\r
- }\r
- return ERRCODE_NV_PAGE_NOT_FOUND;\r
-}\r
-\r
-errcode_t kv_nvregion_find_unused_page(kv_page_location *location)\r
-{\r
- if ((g_kv_nvregion_map.entries == NULL) || (g_kv_nvregion_map.num_entries == 0)) {\r
- return ERRCODE_NV_NOT_INITIALISED;\r
- }\r
-\r
- for (uint32_t i = 0; i < g_kv_nvregion_map.num_entries; i++) {\r
- kv_page_header_t *map_entry = &g_kv_nvregion_map.entries[i];\r
-\r
- if ((map_entry->details.store_id == 0) && (map_entry->details.page_index == 0) &&\r
- (map_entry->inverted_details_word == 0) &&\r
- (map_entry->sequence_number == 0) && (map_entry->inverted_sequence_number == 0)) {\r
- if (location != NULL) {\r
- uint32_t nv_page = g_kv_nvregion_area.nv_data_addr;\r
- nv_page += KV_PAGE_SIZE * i;\r
- *location = (kv_page_location)(uintptr_t)nv_page;\r
- }\r
- return ERRCODE_SUCC;\r
- }\r
- }\r
- return ERRCODE_NV_PAGE_NOT_FOUND;\r
-}\r
-\r
-errcode_t kv_nvregion_erase_page(kv_page_location page_location)\r
-{\r
- uint32_t nv_page_addr = (uint32_t)(uintptr_t)page_location;\r
- errcode_t ret;\r
-\r
- if ((nv_page_addr < g_kv_nvregion_area.nv_data_addr) ||\r
- (nv_page_addr >= (g_kv_nvregion_area.nv_data_addr + g_kv_nvregion_area.nv_data_size))) {\r
- return ERRCODE_INVALID_PARAM;\r
- }\r
-\r
- ret = kv_key_erase_flash(nv_page_addr, KV_PAGE_SIZE);\r
- return ret;\r
-}\r
-\r
-errcode_t kv_nvregion_write_page(kv_page_location page_location, uint16_t store_id, uint8_t page_index)\r
-{\r
- uint32_t written;\r
- kv_page_header_t page_header;\r
- unused(written);\r
-\r
- /* Build header for KV page */\r
- page_header.details.store_id = store_id;\r
- page_header.details.page_index = page_index;\r
- page_header.details.ver = (g_nv_header_magic == KV_KEY_MAGIC) ? 1 : 0;\r
- page_header.inverted_details_word = ~*(uint32_t *)(uintptr_t)&page_header.details;\r
- page_header.sequence_number = 0;\r
- page_header.inverted_sequence_number = ~page_header.sequence_number;\r
-\r
- return kv_key_write_flash((uint32_t)(uintptr_t)page_location, sizeof(kv_page_header_t),\r
- (uint8_t *)&page_header);\r
-}\r
-\r
-uint32_t kv_nvregion_get_page_number(kv_page_location page_location)\r
-{\r
- uint32_t page_number = ((uint32_t)(uintptr_t)page_location - g_kv_nvregion_area.nv_data_addr) / KV_PAGE_SIZE;\r
- if (page_number >= g_kv_nvregion_map.num_entries) {\r
- return (uint32_t)-1;\r
- }\r
- return page_number;\r
+/*
+ * Copyright (c) @CompanyNameMagicTag 2019-2022. All rights reserved.
+ * Description: KV Storage Library non-volatile region access module implementation
+ */
+
+#include "nv_nvregion.h"
+#include "nv_page.h"
+#include "nv_store.h"
+#include "nv_porting.h"
+#include "nv_config.h"
+#include "common_def.h"
+#ifndef CONFIG_NV_SUPPORT_SINGLE_CORE_SYSTEM
+#include "flash_task_mutex.h"
+#include "flash_task_adapt.h"
+#endif
+
+static kv_nvregion_map_t g_kv_nvregion_map = {NULL, NULL, 0};
+static kv_nvregion_area_t g_kv_nvregion_area = {KV_STORE_START_ADDR, KV_STORE_DATA_SIZE,
+ KV_BACKUP_START_ADDR, KV_BACKUP_DATA_SIZE};
+uint32_t g_kv_nvregion_use_times[KV_STORE_DATA_SIZE / KV_PAGE_SIZE] = {0};
+
+kv_nvregion_map_t* kv_nvregion_get_map(void)
+{
+ return &g_kv_nvregion_map;
+}
+
+uint32_t kv_nvregion_get_use_times(kv_page_location page_location)
+{
+ uint32_t page_number = kv_nvregion_get_page_number(page_location);
+ if (page_number >= g_kv_nvregion_map.num_entries) {
+ return (uint32_t)-1;
+ }
+
+ return g_kv_nvregion_map.entries[page_number].sequence_number;
+}
+
+static void kv_nvregion_clear_map_entry(kv_page_header_t *map_entry)
+{
+ (void)memset_s(map_entry, sizeof(kv_page_header_t), 0, sizeof(kv_page_header_t));
+}
+
+static errcode_t kv_nvregion_is_valid_map_entry(const kv_page_header_t *map_entry)
+{
+ if ((map_entry->details.store_id != 0) && (map_entry->details.store_id != 0xFFFF) &&
+ (~map_entry->inverted_details_word == *(uint32_t *)(uintptr_t)&map_entry->details) &&
+ (~map_entry->inverted_sequence_number == map_entry->sequence_number)) {
+ return ERRCODE_SUCC;
+ }
+ return ERRCODE_FAIL;
+}
+
+static void kv_nvregion_filter_duplicate_map_entries(kv_page_header_t *new_entry)
+{
+ kv_page_header_t *existing_entry = g_kv_nvregion_map.entries;
+ while (existing_entry < new_entry) {
+ if ((existing_entry->details.store_id == new_entry->details.store_id) &&
+ (existing_entry->details.page_index == new_entry->details.page_index)) {
+ if (existing_entry->sequence_number <= new_entry->sequence_number) {
+ /* An existing map entry is older than the new one, use the new one */
+ /* Two entries have the same sequence number. This is unexpected. */
+ /* For the moment use the new entry, but it could be better to try and determine based on content */
+ /* e.g. amount of free space, number of erased keys, corrupted keys */
+ kv_nvregion_clear_map_entry(existing_entry);
+ } else {
+ /* An existing map entry is more recent than the new one */
+ kv_nvregion_clear_map_entry(new_entry);
+ break;
+ }
+ }
+ existing_entry++;
+ }
+}
+
+STATIC void kv_nvregion_free_resource(void)
+{
+ if (g_kv_nvregion_map.entries != NULL) {
+ kv_free(g_kv_nvregion_map.entries);
+ g_kv_nvregion_map.entries = NULL;
+ }
+ if (g_kv_nvregion_map.page_status_map != NULL) {
+ kv_free(g_kv_nvregion_map.page_status_map);
+ g_kv_nvregion_map.page_status_map = NULL;
+ }
+}
+
+STATIC errcode_t kv_nvregion_init_map(void)
+{
+ /* Create blank NV region map */
+ uint32_t num_entries = g_kv_nvregion_area.nv_data_size / KV_PAGE_SIZE;
+ if (num_entries == 0) {
+ return ERRCODE_NV_INVALID_PARAMS;
+ }
+
+ /* Obtain details of NV memory region */
+ if (((g_kv_nvregion_area.nv_data_addr & (KV_PAGE_SIZE - 1)) != 0) ||
+ ((g_kv_nvregion_area.nv_data_size & (KV_PAGE_SIZE - 1)) != 0) ||
+ ((g_kv_nvregion_area.nv_data_size & ~(KV_PAGE_SIZE - 1)) == 0)) {
+ /* NV Data address or size not aligned to a page boundry */
+ return ERRCODE_FAIL;
+ }
+
+ /* Free up any existing NV region map */
+ if (num_entries != g_kv_nvregion_map.num_entries) {
+ kv_nvregion_free_resource();
+ }
+
+ if (g_kv_nvregion_map.entries == NULL) {
+ g_kv_nvregion_map.entries = (kv_page_header_t *)kv_malloc(num_entries * sizeof(kv_page_header_t));
+ }
+
+ if (g_kv_nvregion_map.page_status_map == NULL) {
+ g_kv_nvregion_map.page_status_map =
+ (nv_page_status_map_t *)kv_malloc(num_entries * sizeof(nv_page_status_map_t));
+ }
+
+ if ((g_kv_nvregion_map.entries == NULL) || (g_kv_nvregion_map.page_status_map == NULL)) {
+ kv_nvregion_free_resource();
+ return ERRCODE_MALLOC;
+ }
+
+ (void)memset_s(g_kv_nvregion_map.entries, num_entries * sizeof(kv_page_header_t), 0,
+ num_entries * sizeof(kv_page_header_t));
+ (void)memset_s(g_kv_nvregion_map.page_status_map, num_entries * sizeof(nv_page_status_map_t), 0,
+ num_entries * sizeof(nv_page_status_map_t));
+ g_kv_nvregion_map.num_entries = num_entries;
+ return ERRCODE_SUCC;
+}
+
+static errcode_t kv_nvregion_build_map(void)
+{
+ errcode_t ret;
+ unused(ret);
+ uint32_t nv_page = g_kv_nvregion_area.nv_data_addr;
+
+ for (uint32_t i = 0; i < g_kv_nvregion_map.num_entries; i++) {
+ kv_page_header_t *new_entry = &g_kv_nvregion_map.entries[i];
+ ret = kv_key_helper_copy_flash((uint32_t)(uintptr_t)new_entry, (uint32_t)nv_page, sizeof(kv_page_header_t));
+
+ if (kv_nvregion_is_valid_map_entry(new_entry) == ERRCODE_SUCC
+ && new_entry->details.store_id != KV_STORE_ID_BACKUP) {
+ if (new_entry->details.ver == 0) {
+ g_nv_header_magic = KV_KEY_NO_MAGIC;
+ }
+ kv_nvregion_filter_duplicate_map_entries(new_entry);
+ kv_page_handle_t page;
+ page.page_location = (kv_page_location)(uintptr_t)nv_page;
+ kv_page_read_status_to_map(&page, &(g_kv_nvregion_map.page_status_map[i]));
+ } else {
+ kv_nvregion_clear_map_entry(new_entry);
+ }
+
+ nv_page += KV_PAGE_SIZE;
+ }
+ nv_log_debug("[NV] g_nv_header_magic = 0x%x.\r\n", g_nv_header_magic);
+ return ERRCODE_SUCC;
+}
+
+void kv_nvregion_init(uint32_t nv_data_addr, uint32_t nv_data_size, uint32_t nv_backup_addr, uint32_t nv_backup_size)
+{
+ g_kv_nvregion_area.nv_data_addr = nv_data_addr;
+ g_kv_nvregion_area.nv_data_size = nv_data_size;
+ g_kv_nvregion_area.nv_backup_addr = nv_backup_addr;
+ g_kv_nvregion_area.nv_backup_size = nv_backup_size;
+}
+
+kv_nvregion_area_t* nv_get_region_area(void)
+{
+ return &g_kv_nvregion_area;
+}
+
+errcode_t kv_nvregion_scan(void)
+{
+ errcode_t res;
+ res = kv_nvregion_init_map();
+ if (res != ERRCODE_SUCC) {
+ return res;
+ }
+ return kv_nvregion_build_map();
+}
+
+errcode_t kv_nvregion_create_page(uint16_t store_id, uint8_t page_index)
+{
+ errcode_t res;
+ kv_page_location page_location;
+
+ if ((g_kv_nvregion_map.entries == NULL) || (g_kv_nvregion_map.num_entries == 0)) {
+ return ERRCODE_NV_NOT_INITIALISED;
+ }
+ if (store_id == 0) {
+ return ERRCODE_INVALID_PARAM;
+ }
+
+ res = kv_nvregion_find_unused_page(&page_location);
+ if (res != ERRCODE_SUCC) {
+ return res;
+ }
+
+ res = kv_nvregion_erase_page(page_location);
+ if (res != ERRCODE_SUCC) {
+ return res;
+ }
+
+ res = kv_nvregion_write_page(page_location, store_id, page_index);
+ if (res != ERRCODE_SUCC) {
+ return res;
+ }
+
+ return kv_nvregion_scan();
+}
+
+errcode_t kv_nvregion_find_page(uint16_t store_id, uint8_t page_index,
+ kv_page_location *location, kv_page_header_t *header)
+{
+ if ((g_kv_nvregion_map.entries == NULL) || (g_kv_nvregion_map.num_entries == 0)) {
+ return ERRCODE_NV_NOT_INITIALISED;
+ }
+ if (store_id == 0) {
+ return ERRCODE_INVALID_PARAM;
+ }
+ for (uint32_t i = 0; i < g_kv_nvregion_map.num_entries; i++) {
+ kv_page_header_t *map_entry = &g_kv_nvregion_map.entries[i];
+ if ((map_entry->details.store_id == store_id) && (map_entry->details.page_index == page_index)) {
+ if (location != NULL) {
+ uint32_t nv_page = g_kv_nvregion_area.nv_data_addr;
+ nv_page += KV_PAGE_SIZE * i;
+ *location = (kv_page_location)(uintptr_t)nv_page;
+ }
+ if (header != NULL) {
+ (void)memcpy_s(header, sizeof(kv_page_header_t), map_entry, sizeof(kv_page_header_t));
+ }
+ return ERRCODE_SUCC;
+ }
+ }
+ return ERRCODE_NV_PAGE_NOT_FOUND;
+}
+
+errcode_t kv_nvregion_find_unused_page(kv_page_location *location)
+{
+ if ((g_kv_nvregion_map.entries == NULL) || (g_kv_nvregion_map.num_entries == 0)) {
+ return ERRCODE_NV_NOT_INITIALISED;
+ }
+
+ for (uint32_t i = 0; i < g_kv_nvregion_map.num_entries; i++) {
+ kv_page_header_t *map_entry = &g_kv_nvregion_map.entries[i];
+
+ if ((map_entry->details.store_id == 0) && (map_entry->details.page_index == 0) &&
+ (map_entry->inverted_details_word == 0) &&
+ (map_entry->sequence_number == 0) && (map_entry->inverted_sequence_number == 0)) {
+ if (location != NULL) {
+ uint32_t nv_page = g_kv_nvregion_area.nv_data_addr;
+ nv_page += KV_PAGE_SIZE * i;
+ *location = (kv_page_location)(uintptr_t)nv_page;
+ }
+ return ERRCODE_SUCC;
+ }
+ }
+ return ERRCODE_NV_PAGE_NOT_FOUND;
+}
+
+errcode_t kv_nvregion_erase_page(kv_page_location page_location)
+{
+ uint32_t nv_page_addr = (uint32_t)(uintptr_t)page_location;
+ errcode_t ret;
+
+ if ((nv_page_addr < g_kv_nvregion_area.nv_data_addr) ||
+ (nv_page_addr >= (g_kv_nvregion_area.nv_data_addr + g_kv_nvregion_area.nv_data_size))) {
+ return ERRCODE_INVALID_PARAM;
+ }
+
+ ret = kv_key_erase_flash(nv_page_addr, KV_PAGE_SIZE);
+ return ret;
+}
+
+errcode_t kv_nvregion_write_page(kv_page_location page_location, uint16_t store_id, uint8_t page_index)
+{
+ uint32_t written;
+ kv_page_header_t page_header;
+ unused(written);
+
+ /* Build header for KV page */
+ page_header.details.store_id = store_id;
+ page_header.details.page_index = page_index;
+ page_header.details.ver = (g_nv_header_magic == KV_KEY_MAGIC) ? 1 : 0;
+ page_header.inverted_details_word = ~*(uint32_t *)(uintptr_t)&page_header.details;
+ page_header.sequence_number = 0;
+ page_header.inverted_sequence_number = ~page_header.sequence_number;
+
+ return kv_key_write_flash((uint32_t)(uintptr_t)page_location, sizeof(kv_page_header_t),
+ (uint8_t *)&page_header);
+}
+
+uint32_t kv_nvregion_get_page_number(kv_page_location page_location)
+{
+ uint32_t page_number = ((uint32_t)(uintptr_t)page_location - g_kv_nvregion_area.nv_data_addr) / KV_PAGE_SIZE;
+ if (page_number >= g_kv_nvregion_map.num_entries) {
+ return (uint32_t)-1;
+ }
+ return page_number;
}
\ No newline at end of file
-/*\r
- * Copyright (c) @CompanyNameMagicTag 2019-2022. All rights reserved.\r
- * Description: KV Storage Library non-volatile region access interface\r
- */\r
-\r
-#ifndef NV_NVREGION_H\r
-#define NV_NVREGION_H\r
-\r
-#include "nv_page.h"\r
-#include "errcode.h"\r
-\r
-#ifdef __cplusplus\r
-#if __cplusplus\r
-extern "C" {\r
-#endif /* __cplusplus */\r
-#endif /* __cplusplus */\r
-\r
-typedef struct {\r
- uint16_t key_id; /* Key ID */\r
- uint16_t key_len : 13; /* Key length */\r
- uint16_t permanent : 1; /* 是否是永久Key */\r
- uint16_t encrypted : 1; /* 是否是加密Key */\r
- uint16_t upgrade : 1; /* 是否可以升级 */\r
- uint16_t key_offset : 13; /* Key的位置(相对页首地址的偏移) */\r
- uint16_t valid : 1; /* Key是否有效 */\r
- uint16_t corrupted : 1; /* Key是否被破坏 */\r
- uint16_t reserved : 1; /* Key预留字段 */\r
-} nv_key_index_map_t;\r
-\r
-typedef struct {\r
- kv_page_header_t *entries;\r
- nv_page_status_map_t *page_status_map;\r
- uint32_t num_entries;\r
-} kv_nvregion_map_t;\r
-\r
-typedef struct {\r
- uint32_t nv_data_addr;\r
- uint32_t nv_data_size;\r
- uint32_t nv_backup_addr;\r
- uint32_t nv_backup_size;\r
-} kv_nvregion_area_t;\r
-\r
-void kv_nvregion_init(uint32_t nv_data_addr, uint32_t nv_data_size, uint32_t nv_backup_addr, uint32_t nv_backup_size);\r
-\r
-errcode_t kv_nvregion_scan(void);\r
-errcode_t kv_nvregion_find_page(uint16_t store_id, uint8_t page_index,\r
- kv_page_location *location, kv_page_header_t *header);\r
-errcode_t kv_nvregion_find_unused_page(kv_page_location *location);\r
-errcode_t kv_nvregion_erase_page(kv_page_location page_location);\r
-errcode_t kv_nvregion_create_page(uint16_t store_id, uint8_t page_index);\r
-errcode_t kv_nvregion_write_page(kv_page_location page_location, uint16_t store_id, uint8_t page_index);\r
-\r
-kv_nvregion_map_t* kv_nvregion_get_map(void);\r
-kv_nvregion_area_t* nv_get_region_area(void);\r
-\r
-uint32_t kv_nvregion_get_page_number(kv_page_location page_location);\r
-uint32_t kv_nvregion_get_use_times(kv_page_location page_location);\r
-\r
-#ifdef __cplusplus\r
-#if __cplusplus\r
-}\r
-#endif /* __cplusplus */\r
-#endif /* __cplusplus */\r
-\r
-#endif /* NV_NVREGION_H */\r
+/*
+ * Copyright (c) @CompanyNameMagicTag 2019-2022. All rights reserved.
+ * Description: KV Storage Library non-volatile region access interface
+ */
+
+#ifndef NV_NVREGION_H
+#define NV_NVREGION_H
+
+#include "nv_page.h"
+#include "errcode.h"
+
+#ifdef __cplusplus
+#if __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+#endif /* __cplusplus */
+
+typedef struct {
+ uint16_t key_id; /* Key ID */
+ uint16_t key_len : 13; /* Key length */
+ uint16_t permanent : 1; /* 是否是永久Key */
+ uint16_t encrypted : 1; /* 是否是加密Key */
+ uint16_t upgrade : 1; /* 是否可以升级 */
+ uint16_t key_offset : 13; /* Key的位置(相对页首地址的偏移) */
+ uint16_t valid : 1; /* Key是否有效 */
+ uint16_t corrupted : 1; /* Key是否被破坏 */
+ uint16_t reserved : 1; /* Key预留字段 */
+} nv_key_index_map_t;
+
+typedef struct {
+ kv_page_header_t *entries;
+ nv_page_status_map_t *page_status_map;
+ uint32_t num_entries;
+} kv_nvregion_map_t;
+
+typedef struct {
+ uint32_t nv_data_addr;
+ uint32_t nv_data_size;
+ uint32_t nv_backup_addr;
+ uint32_t nv_backup_size;
+} kv_nvregion_area_t;
+
+void kv_nvregion_init(uint32_t nv_data_addr, uint32_t nv_data_size, uint32_t nv_backup_addr, uint32_t nv_backup_size);
+
+errcode_t kv_nvregion_scan(void);
+errcode_t kv_nvregion_find_page(uint16_t store_id, uint8_t page_index,
+ kv_page_location *location, kv_page_header_t *header);
+errcode_t kv_nvregion_find_unused_page(kv_page_location *location);
+errcode_t kv_nvregion_erase_page(kv_page_location page_location);
+errcode_t kv_nvregion_create_page(uint16_t store_id, uint8_t page_index);
+errcode_t kv_nvregion_write_page(kv_page_location page_location, uint16_t store_id, uint8_t page_index);
+
+kv_nvregion_map_t* kv_nvregion_get_map(void);
+kv_nvregion_area_t* nv_get_region_area(void);
+
+uint32_t kv_nvregion_get_page_number(kv_page_location page_location);
+uint32_t kv_nvregion_get_use_times(kv_page_location page_location);
+
+#ifdef __cplusplus
+#if __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* __cplusplus */
+
+#endif /* NV_NVREGION_H */
-/*\r
- * Copyright (c) @CompanyNameMagicTag 2019-2022. All rights reserved.\r
- * Description: KV Storage Library page access module implementation\r
- */\r
-\r
-#include "nv_page.h"\r
-#include "nv_key.h"\r
-#include "nv_nvregion.h"\r
-#include "nv_porting.h"\r
-#include "common_def.h"\r
-\r
-#define assert_p(x) ((void)0)\r
-\r
-errcode_t kv_page_get_index(kv_page_handle_t *page, uint32_t *page_index)\r
-{\r
- if ((page->page_location != (kv_page_location)NULL) &&\r
- (~page->page_header.inverted_details_word == *(uint32_t *)(uintptr_t)&page->page_header.details) &&\r
- (~page->page_header.inverted_sequence_number == page->page_header.sequence_number)) {\r
- *page_index = page->page_header.details.page_index;\r
- return ERRCODE_SUCC;\r
- }\r
- return ERRCODE_NV_INVALID_PAGE;\r
-}\r
-\r
-errcode_t kv_page_find_first_key(const kv_page_handle_t *page, kv_key_filter_t *search_filter,\r
- kv_key_handle_t *key)\r
-{\r
- kv_key_location key_location = (kv_key_location)((uintptr_t)page->page_location + sizeof(kv_page_header_t));\r
- errcode_t res = kv_key_get_handle_from_location(key_location, key);\r
-#if (CONFIG_NV_SUPPORT_SKIP_CORRUPT_KEY == NV_YES)\r
- if (res == ERRCODE_NV_INVALID_KEY_HEADER) {\r
- nv_log_debug("[NV] kv_page_find_first_key: key magic err (location = 0x%x)!\r\n", key_location);\r
- res = kv_key_get_next_magic_position(key_location, key);\r
- }\r
-#endif\r
- while (res == ERRCODE_SUCC) {\r
- res = kv_key_does_filter_match(key, search_filter);\r
- if (res == ERRCODE_SUCC) {\r
- return ERRCODE_SUCC;\r
- }\r
- res = kv_key_get_next_handle(key, KV_KEY_NOT_VALIDATE);\r
- }\r
- return res;\r
-}\r
-\r
-errcode_t kv_page_find_next_key(const kv_page_handle_t *page, kv_key_filter_t *search_filter,\r
- kv_key_handle_t *key)\r
-{\r
- errcode_t res = kv_key_get_next_handle(key, KV_KEY_NOT_VALIDATE);\r
- unused(page);\r
- while (res == ERRCODE_SUCC) {\r
- res = kv_key_does_filter_match(key, search_filter);\r
- if (res == ERRCODE_SUCC) {\r
- return ERRCODE_SUCC;\r
- }\r
- res = kv_key_get_next_handle(key, KV_KEY_NOT_VALIDATE);\r
- }\r
- return res;\r
-}\r
-\r
-STATIC void kv_page_copy_status_map(kv_page_status_t *page_status, nv_page_status_map_t *page_status_map)\r
-{\r
- page_status->total_space = KV_PAGE_SIZE - (uint32_t)sizeof(kv_page_header_t);\r
- page_status->used_space = page_status_map->used_space;\r
- page_status->reclaimable_space = page_status_map->reclaimable_space;\r
- page_status->corrupted_space = page_status_map->corrupted_space;\r
- page_status->max_key_space = page_status->total_space - page_status->used_space + page_status->reclaimable_space;\r
- page_status->first_writable_location = page_status_map->page_location + page_status_map->first_writable_offset;\r
-}\r
-\r
-void kv_page_read_status_to_map(kv_page_handle_t *page, nv_page_status_map_t *status_map)\r
-{\r
- kv_key_handle_t key;\r
- uint32_t page_data_loc = (uint32_t)(uintptr_t)page->page_location + (uint32_t)sizeof(kv_page_header_t);\r
-\r
- (void)memset_s(status_map, sizeof(nv_page_status_map_t), 0, sizeof(nv_page_status_map_t));\r
-\r
- status_map->page_location = (uint32_t)page->page_location;\r
-\r
- errcode_t key_found = kv_page_find_first_key(page, NULL, &key);\r
- uint32_t first_key_loc = (uint32_t)(uintptr_t)key.key_location;\r
-\r
- /* 如果第一个KEY不在page起始位置,说明page起始位置处的KEY被破坏 */\r
- if (first_key_loc > page_data_loc) {\r
- status_map->corrupted_space += (uint16_t)(first_key_loc - page_data_loc);\r
- status_map->reclaimable_space += (uint16_t)(first_key_loc - page_data_loc);\r
- status_map->used_space += (uint16_t)(first_key_loc - page_data_loc);\r
- }\r
-\r
- while (key_found == ERRCODE_SUCC) {\r
- uint32_t key_size;\r
- uint32_t last_key_location;\r
-\r
- key_size = kv_key_flash_size(&key);\r
-\r
- errcode_t ret = kv_key_validate_integrity(&key, true);\r
- if (ret != ERRCODE_SUCC) {\r
- /* Key校验失败,则key length不可信,通过获取下一个Key的位置,与当前key的位置差重新计算key_size */\r
- nv_log_err("[NV] key validate integrity. key id = 0x%x location = 0x%x. ret = 0x%x\r\n",\r
- key.header.key_id, key.key_location, ret);\r
- last_key_location = (uint32_t)(uintptr_t)key.key_location;\r
- key_found = kv_key_get_next_handle(&key, KV_KEY_VALIDATE_WRONG);\r
- if ((key_found == ERRCODE_SUCC) || (key_found == ERRCODE_NV_KEY_NOT_FOUND)) {\r
- key_size = (uint32_t)(uintptr_t)key.key_location - last_key_location;\r
- } else {\r
- break;\r
- }\r
-\r
- status_map->corrupted_space += (uint16_t)key_size;\r
- status_map->reclaimable_space += (uint16_t)key_size;\r
- } else {\r
- if (kv_key_is_erased(&key)) {\r
- status_map->reclaimable_space += (uint16_t)key_size;\r
- }\r
- key_found = kv_key_get_next_handle(&key, KV_KEY_VALIDATE_CORRECT);\r
- }\r
- status_map->used_space += (uint16_t)key_size;\r
- assert_p(status_map->used_space <= (KV_PAGE_SIZE - sizeof(kv_page_header_t)));\r
- }\r
-\r
- status_map->first_writable_offset = (uint16_t)sizeof(kv_page_header_t) + status_map->used_space;\r
-}\r
-\r
-void kv_page_get_status(kv_page_handle_t *page, kv_page_status_t *page_status)\r
-{\r
- nv_page_status_map_t page_status_map;\r
- kv_page_read_status_to_map(page, &page_status_map);\r
- kv_page_copy_status_map(page_status, &page_status_map);\r
-}\r
-\r
-void kv_page_get_status_from_map(kv_page_handle_t *page, kv_page_status_t *page_status)\r
-{\r
- kv_nvregion_map_t *nv_map = kv_nvregion_get_map();\r
-\r
- uint32_t page_number = kv_nvregion_get_page_number(page->page_location);\r
- if (page_number >= nv_map->num_entries) {\r
- return;\r
- }\r
-\r
- kv_page_copy_status_map(page_status, &(nv_map->page_status_map[page_number]));\r
- return;\r
-}\r
+/*
+ * Copyright (c) @CompanyNameMagicTag 2019-2022. All rights reserved.
+ * Description: KV Storage Library page access module implementation
+ */
+
+#include "nv_page.h"
+#include "nv_key.h"
+#include "nv_nvregion.h"
+#include "nv_porting.h"
+#include "common_def.h"
+
+#define assert_p(x) ((void)0)
+
+errcode_t kv_page_get_index(kv_page_handle_t *page, uint32_t *page_index)
+{
+ if ((page->page_location != (kv_page_location)NULL) &&
+ (~page->page_header.inverted_details_word == *(uint32_t *)(uintptr_t)&page->page_header.details) &&
+ (~page->page_header.inverted_sequence_number == page->page_header.sequence_number)) {
+ *page_index = page->page_header.details.page_index;
+ return ERRCODE_SUCC;
+ }
+ return ERRCODE_NV_INVALID_PAGE;
+}
+
+errcode_t kv_page_find_first_key(const kv_page_handle_t *page, kv_key_filter_t *search_filter,
+ kv_key_handle_t *key)
+{
+ kv_key_location key_location = (kv_key_location)((uintptr_t)page->page_location + sizeof(kv_page_header_t));
+ errcode_t res = kv_key_get_handle_from_location(key_location, key);
+#if (CONFIG_NV_SUPPORT_SKIP_CORRUPT_KEY == NV_YES)
+ if (res == ERRCODE_NV_INVALID_KEY_HEADER) {
+ nv_log_debug("[NV] kv_page_find_first_key: key magic err (location = 0x%x)!\r\n", key_location);
+ res = kv_key_get_next_magic_position(key_location, key);
+ }
+#endif
+ while (res == ERRCODE_SUCC) {
+ res = kv_key_does_filter_match(key, search_filter);
+ if (res == ERRCODE_SUCC) {
+ return ERRCODE_SUCC;
+ }
+ res = kv_key_get_next_handle(key, KV_KEY_NOT_VALIDATE);
+ }
+ return res;
+}
+
+errcode_t kv_page_find_next_key(const kv_page_handle_t *page, kv_key_filter_t *search_filter,
+ kv_key_handle_t *key)
+{
+ errcode_t res = kv_key_get_next_handle(key, KV_KEY_NOT_VALIDATE);
+ unused(page);
+ while (res == ERRCODE_SUCC) {
+ res = kv_key_does_filter_match(key, search_filter);
+ if (res == ERRCODE_SUCC) {
+ return ERRCODE_SUCC;
+ }
+ res = kv_key_get_next_handle(key, KV_KEY_NOT_VALIDATE);
+ }
+ return res;
+}
+
+STATIC void kv_page_copy_status_map(kv_page_status_t *page_status, nv_page_status_map_t *page_status_map)
+{
+ page_status->total_space = KV_PAGE_SIZE - (uint32_t)sizeof(kv_page_header_t);
+ page_status->used_space = page_status_map->used_space;
+ page_status->reclaimable_space = page_status_map->reclaimable_space;
+ page_status->corrupted_space = page_status_map->corrupted_space;
+ page_status->max_key_space = page_status->total_space - page_status->used_space + page_status->reclaimable_space;
+ page_status->first_writable_location = page_status_map->page_location + page_status_map->first_writable_offset;
+}
+
+void kv_page_read_status_to_map(kv_page_handle_t *page, nv_page_status_map_t *status_map)
+{
+ kv_key_handle_t key;
+ uint32_t page_data_loc = (uint32_t)(uintptr_t)page->page_location + (uint32_t)sizeof(kv_page_header_t);
+
+ (void)memset_s(status_map, sizeof(nv_page_status_map_t), 0, sizeof(nv_page_status_map_t));
+
+ status_map->page_location = (uint32_t)page->page_location;
+
+ errcode_t key_found = kv_page_find_first_key(page, NULL, &key);
+ uint32_t first_key_loc = (uint32_t)(uintptr_t)key.key_location;
+
+ /* 如果第一个KEY不在page起始位置,说明page起始位置处的KEY被破坏 */
+ if (first_key_loc > page_data_loc) {
+ status_map->corrupted_space += (uint16_t)(first_key_loc - page_data_loc);
+ status_map->reclaimable_space += (uint16_t)(first_key_loc - page_data_loc);
+ status_map->used_space += (uint16_t)(first_key_loc - page_data_loc);
+ }
+
+ while (key_found == ERRCODE_SUCC) {
+ uint32_t key_size;
+ uint32_t last_key_location;
+
+ key_size = kv_key_flash_size(&key);
+
+ errcode_t ret = kv_key_validate_integrity(&key, true);
+ if (ret != ERRCODE_SUCC) {
+ /* Key校验失败,则key length不可信,通过获取下一个Key的位置,与当前key的位置差重新计算key_size */
+ nv_log_err("[NV] key validate integrity. key id = 0x%x location = 0x%x. ret = 0x%x\r\n",
+ key.header.key_id, key.key_location, ret);
+ last_key_location = (uint32_t)(uintptr_t)key.key_location;
+ key_found = kv_key_get_next_handle(&key, KV_KEY_VALIDATE_WRONG);
+ if ((key_found == ERRCODE_SUCC) || (key_found == ERRCODE_NV_KEY_NOT_FOUND)) {
+ key_size = (uint32_t)(uintptr_t)key.key_location - last_key_location;
+ } else {
+ break;
+ }
+
+ status_map->corrupted_space += (uint16_t)key_size;
+ status_map->reclaimable_space += (uint16_t)key_size;
+ } else {
+ if (kv_key_is_erased(&key)) {
+ status_map->reclaimable_space += (uint16_t)key_size;
+ }
+ key_found = kv_key_get_next_handle(&key, KV_KEY_VALIDATE_CORRECT);
+ }
+ status_map->used_space += (uint16_t)key_size;
+ assert_p(status_map->used_space <= (KV_PAGE_SIZE - sizeof(kv_page_header_t)));
+ }
+
+ status_map->first_writable_offset = (uint16_t)sizeof(kv_page_header_t) + status_map->used_space;
+}
+
+void kv_page_get_status(kv_page_handle_t *page, kv_page_status_t *page_status)
+{
+ nv_page_status_map_t page_status_map;
+ kv_page_read_status_to_map(page, &page_status_map);
+ kv_page_copy_status_map(page_status, &page_status_map);
+}
+
+void kv_page_get_status_from_map(kv_page_handle_t *page, kv_page_status_t *page_status)
+{
+ kv_nvregion_map_t *nv_map = kv_nvregion_get_map();
+
+ uint32_t page_number = kv_nvregion_get_page_number(page->page_location);
+ if (page_number >= nv_map->num_entries) {
+ return;
+ }
+
+ kv_page_copy_status_map(page_status, &(nv_map->page_status_map[page_number]));
+ return;
+}
-/*\r
- * Copyright (c) @CompanyNameMagicTag 2019-2022. All rights reserved.\r
- * Description: KV Storage Library page access interface\r
- */\r
-\r
-#ifndef NV_PAGE_H\r
-#define NV_PAGE_H\r
-\r
-#include "nv_key.h"\r
-#include "errcode.h"\r
-\r
-#ifdef __cplusplus\r
-#if __cplusplus\r
-extern "C" {\r
-#endif /* __cplusplus */\r
-#endif /* __cplusplus */\r
-\r
-/** Represents the location in flash of a KV page. Provided in such a way that it can't be dereferenced,\r
- * as flash is not directly addressable */\r
-typedef const void *kv_page_location;\r
-\r
-typedef struct {\r
- uint16_t store_id; /* Identifies the page as belonging to a certain store */\r
- uint8_t ver;\r
- uint8_t page_index; /* Uniquely identifies a page within a store */\r
-} kv_page_details_t;\r
-\r
-typedef struct {\r
- kv_page_details_t details;\r
- uint32_t inverted_details_word;\r
- uint32_t sequence_number;\r
- uint32_t inverted_sequence_number;\r
-} kv_page_header_t;\r
-\r
-/* A KV page handle, which provides both the page header information and the location in flash */\r
-typedef struct {\r
- kv_page_header_t page_header;\r
- kv_page_location page_location; /* Location in flash of the KV page */\r
-} kv_page_handle_t;\r
-\r
-typedef struct {\r
- uint32_t total_space; /* 本页最大空间(不含页头) */\r
- uint32_t used_space; /* 本页已使用空间 */\r
- uint32_t reclaimable_space; /* 本页可回收空间(包含已损坏空间) */\r
- uint32_t corrupted_space; /* 本页已损坏空间 */\r
- uint32_t max_key_space; /* 本页可写的最大空间(未使用+可回收) */\r
- uint32_t first_writable_location; /* 本页第一个可写入位置 */\r
-} kv_page_status_t;\r
-\r
-typedef struct {\r
- uint32_t page_location; /* 本页首地址(包含页头) */\r
- uint16_t used_space; /* 本页已使用空间 */\r
- uint16_t reclaimable_space; /* 本页可回收空间(包含已损坏空间) */\r
- uint16_t corrupted_space; /* 本页已损坏空间 */\r
- uint16_t first_writable_offset; /* 本页第一个可写入位置(相对页首地址的偏移) */\r
-} nv_page_status_map_t;\r
-\r
-errcode_t kv_page_get_index(kv_page_handle_t *page, uint32_t *page_index);\r
-errcode_t kv_page_find_first_key(const kv_page_handle_t *page, kv_key_filter_t *search_filter,\r
- kv_key_handle_t *key);\r
-errcode_t kv_page_find_next_key(const kv_page_handle_t *page, kv_key_filter_t *search_filter,\r
- kv_key_handle_t *key);\r
-void kv_page_get_status(kv_page_handle_t *page, kv_page_status_t *page_status);\r
-void kv_page_get_status_from_map(kv_page_handle_t *page, kv_page_status_t *page_status);\r
-void kv_page_read_status_to_map(kv_page_handle_t *page, nv_page_status_map_t *status_map);\r
-\r
-#ifdef __cplusplus\r
-#if __cplusplus\r
-}\r
-#endif /* __cplusplus */\r
-#endif /* __cplusplus */\r
-\r
-#endif /* NV_PAGE_H_ */\r
+/*
+ * Copyright (c) @CompanyNameMagicTag 2019-2022. All rights reserved.
+ * Description: KV Storage Library page access interface
+ */
+
+#ifndef NV_PAGE_H
+#define NV_PAGE_H
+
+#include "nv_key.h"
+#include "errcode.h"
+
+#ifdef __cplusplus
+#if __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+#endif /* __cplusplus */
+
+/** Represents the location in flash of a KV page. Provided in such a way that it can't be dereferenced,
+ * as flash is not directly addressable */
+typedef const void *kv_page_location;
+
+typedef struct {
+ uint16_t store_id; /* Identifies the page as belonging to a certain store */
+ uint8_t ver;
+ uint8_t page_index; /* Uniquely identifies a page within a store */
+} kv_page_details_t;
+
+typedef struct {
+ kv_page_details_t details;
+ uint32_t inverted_details_word;
+ uint32_t sequence_number;
+ uint32_t inverted_sequence_number;
+} kv_page_header_t;
+
+/* A KV page handle, which provides both the page header information and the location in flash */
+typedef struct {
+ kv_page_header_t page_header;
+ kv_page_location page_location; /* Location in flash of the KV page */
+} kv_page_handle_t;
+
+typedef struct {
+ uint32_t total_space; /* 本页最大空间(不含页头) */
+ uint32_t used_space; /* 本页已使用空间 */
+ uint32_t reclaimable_space; /* 本页可回收空间(包含已损坏空间) */
+ uint32_t corrupted_space; /* 本页已损坏空间 */
+ uint32_t max_key_space; /* 本页可写的最大空间(未使用+可回收) */
+ uint32_t first_writable_location; /* 本页第一个可写入位置 */
+} kv_page_status_t;
+
+typedef struct {
+ uint32_t page_location; /* 本页首地址(包含页头) */
+ uint16_t used_space; /* 本页已使用空间 */
+ uint16_t reclaimable_space; /* 本页可回收空间(包含已损坏空间) */
+ uint16_t corrupted_space; /* 本页已损坏空间 */
+ uint16_t first_writable_offset; /* 本页第一个可写入位置(相对页首地址的偏移) */
+} nv_page_status_map_t;
+
+errcode_t kv_page_get_index(kv_page_handle_t *page, uint32_t *page_index);
+errcode_t kv_page_find_first_key(const kv_page_handle_t *page, kv_key_filter_t *search_filter,
+ kv_key_handle_t *key);
+errcode_t kv_page_find_next_key(const kv_page_handle_t *page, kv_key_filter_t *search_filter,
+ kv_key_handle_t *key);
+void kv_page_get_status(kv_page_handle_t *page, kv_page_status_t *page_status);
+void kv_page_get_status_from_map(kv_page_handle_t *page, kv_page_status_t *page_status);
+void kv_page_read_status_to_map(kv_page_handle_t *page, nv_page_status_map_t *status_map);
+
+#ifdef __cplusplus
+#if __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* __cplusplus */
+
+#endif /* NV_PAGE_H_ */
-/*\r
- * Copyright (c) @CompanyNameMagicTag 2023-2023. All rights reserved.\r
- * Description: NV Storage Library Restore factory settings interface\r
- */\r
-#include "nv.h"\r
-#include "nv_store.h"\r
-#include "nv_page.h"\r
-#include "nv_storage.h"\r
-#include "nv_nvregion.h"\r
-#include "nv_porting.h"\r
-#include "nv_notify.h"\r
-#include "nv_task_adapt.h"\r
-#include "nv_update.h"\r
-#include "common_def.h"\r
-#include "nv_storage_handle.h"\r
-#include "nv_reset.h"\r
-\r
-#if (CONFIG_NV_SUPPORT_BACKUP_RESTORE == NV_YES)\r
-\r
-bool g_reset_region_flag[KEY_ID_REGION_MAX_NUM] = {0};\r
-STATIC retain_key_list_handle_t* g_retain_key_list = NULL;\r
-\r
-#define RESET_MAX_BINARY_VAL 1\r
-#define assert_r(x) ((void)0)\r
-#define NV_RESTORE_MAX_KEY 4060\r
-\r
-STATIC errcode_t kv_region_is_enable(uint16_t key_id)\r
-{\r
- uint16_t region_id = key_id / REGION_KEY_NUMS;\r
- if (region_id < KEY_ID_REGION_MAX_NUM && g_reset_region_flag[region_id]) {\r
- return ERRCODE_SUCC;\r
- }\r
- return ERRCODE_FAIL;\r
-}\r
-\r
-STATIC void retain_key_list_free(void)\r
-{\r
- retain_key_t *current_key = g_retain_key_list->next;\r
- retain_key_t *temp_key = NULL;\r
- while (current_key != NULL) {\r
- temp_key = current_key;\r
- current_key = current_key->next;\r
- if (temp_key->kvalue != NULL) {\r
- kv_free(temp_key->kvalue);\r
- }\r
- kv_free(temp_key);\r
- g_retain_key_list->list_length--;\r
- }\r
- kv_free(g_retain_key_list);\r
- g_retain_key_list = NULL;\r
-}\r
-\r
-STATIC errcode_t retain_key_list_init(void)\r
-{\r
- if (g_retain_key_list != NULL) {\r
- return ERRCODE_SUCC;\r
- }\r
- g_retain_key_list = (retain_key_list_handle_t *)(uintptr_t)kv_malloc(sizeof(retain_key_list_handle_t));\r
- if (g_retain_key_list == NULL) {\r
- return ERRCODE_MALLOC;\r
- }\r
-\r
- g_retain_key_list->list_length = 0;\r
- g_retain_key_list->tail = NULL;\r
- g_retain_key_list->next = NULL;\r
- return ERRCODE_SUCC;\r
-}\r
-\r
-STATIC void retain_key_list_add(retain_key_t* cur_key)\r
-{\r
- if (g_retain_key_list->list_length == 0) {\r
- g_retain_key_list->next = cur_key;\r
- g_retain_key_list->tail = cur_key;\r
- } else {\r
- g_retain_key_list->tail->next = cur_key;\r
- g_retain_key_list->tail = cur_key;\r
- }\r
- g_retain_key_list->list_length++;\r
-}\r
-\r
-STATIC errcode_t kv_reset_backup_retain_keys_helper(kv_page_handle_t *page)\r
-{\r
- errcode_t res;\r
- kv_key_handle_t key;\r
- retain_key_t *cur_key = NULL;\r
- res = retain_key_list_init();\r
- if (res != ERRCODE_SUCC) {\r
- return res;\r
- }\r
-\r
- errcode_t key_found = kv_page_find_first_key(page, NULL, &key);\r
- while (key_found == ERRCODE_SUCC) {\r
- if ((kv_key_is_valid(&key)) && (kv_region_is_enable(key.header.key_id) == ERRCODE_FAIL)) {\r
- cur_key = (retain_key_t *)kv_malloc(sizeof(retain_key_t));\r
- if (cur_key == NULL) {\r
- return ERRCODE_MALLOC;\r
- }\r
- (void)memset_s(cur_key, sizeof(retain_key_t), 0, sizeof(retain_key_t));\r
- cur_key->key = key.header.key_id;\r
- cur_key->kvalue_length = key.header.length;\r
- cur_key->kvalue = (uint8_t *)kv_malloc(cur_key->kvalue_length);\r
- if (cur_key->kvalue == NULL) {\r
- return ERRCODE_MALLOC;\r
- }\r
- cur_key->next = NULL;\r
- res = kv_key_read_data(&key, cur_key->kvalue);\r
- if (res != ERRCODE_SUCC) {\r
- return ERRCODE_FAIL;\r
- }\r
- nv_log_debug("[NV] backup_retain_keys_helper key_id: %d\r\n", cur_key->key);\r
- (void)retain_key_list_add(cur_key);\r
- }\r
- key_found = kv_page_find_next_key(page, NULL, &key);\r
- }\r
- return ERRCODE_SUCC;\r
-}\r
-\r
-STATIC errcode_t kv_reset_backup_retain_keys(kv_store_t store, uint8_t num_pages)\r
-{\r
- errcode_t ret;\r
- kv_page_handle_t kv_page;\r
- for (uint8_t page_index = 0; page_index < num_pages; page_index++) {\r
- ret = kv_store_get_page_handle(store, page_index, &kv_page);\r
- if (ret != ERRCODE_SUCC) {\r
- return ret;\r
- }\r
- ret = kv_reset_backup_retain_keys_helper(&kv_page);\r
- if (ret != ERRCODE_SUCC) {\r
- retain_key_list_free();\r
- return ret;\r
- }\r
- }\r
- return ERRCODE_SUCC;\r
-}\r
-\r
-STATIC errcode_t restore_retain_keys(void)\r
-{\r
- if (g_retain_key_list == NULL) {\r
- return ERRCODE_FAIL;\r
- }\r
- retain_key_t *cur_key = g_retain_key_list->next;\r
- while (cur_key != NULL && cur_key->kvalue != NULL) {\r
- nv_log_debug("[NV] restore_retain_keys_b key_id: 0x%x\r\n", cur_key->key);\r
- (void)nv_direct_write_with_attr(cur_key->key, cur_key->kvalue, cur_key->kvalue_length, NULL, NULL);\r
- cur_key = cur_key->next;\r
- }\r
- retain_key_list_free();\r
- return ERRCODE_SUCC;\r
-}\r
-\r
-STATIC errcode_t kv_backup_invalid_key(const kv_key_handle_t *key)\r
-{\r
- /* 计算valid相对于key起始的位置,即要写入数据在flash中相对于key_location的偏移量 */\r
- uint32_t offset = ((uint32_t)(uintptr_t)&key->header.valid) - ((uint32_t)(uintptr_t)key);\r
- uint32_t write_position = (uint32_t)(uintptr_t)key->key_location + offset - FLASH_PHYSICAL_ADDR_START;\r
- uint32_t write_length = sizeof(key->header.valid);\r
- uint8_t *write_data = (uint8_t *)kv_malloc(write_length);\r
- if (write_data == NULL) {\r
- return ERRCODE_MALLOC;\r
- }\r
- (void)memset_s(write_data, write_length, 0, write_length);\r
- errcode_t ret = kv_flash_write(write_position, write_length, write_data, false);\r
- kv_free(write_data);\r
- return ret;\r
-}\r
-\r
-STATIC errcode_t kv_backup_delete_one_page_repeat_key(kv_page_handle_t *page)\r
-{\r
- errcode_t ret;\r
- kv_key_handle_t key;\r
- errcode_t key_found = kv_page_find_first_key(page, NULL, &key);\r
- while (key_found == ERRCODE_SUCC) {\r
- if ((kv_key_is_valid(&key)) && (kv_region_is_enable(key.header.key_id) == ERRCODE_SUCC)) {\r
- ret = kv_backup_invalid_key(&key);\r
- if (ret != ERRCODE_SUCC) {\r
- return ret;\r
- }\r
- }\r
- key_found = kv_page_find_next_key(page, NULL, &key);\r
- }\r
- return ERRCODE_SUCC;\r
-}\r
-\r
-errcode_t kv_backup_delete_repeat_key(void)\r
-{\r
- errcode_t ret;\r
- kv_page_handle_t page;\r
- kv_nvregion_area_t* nvregion_area = nv_get_region_area();\r
- if (nvregion_area == NULL) {\r
- return ERRCODE_FAIL;\r
- }\r
- uint32_t page_start = nvregion_area->nv_backup_addr;\r
- /* 遍历备份区,将每个要被重复备份的key标志位置为无效 */\r
- for (uint32_t page_index = 0; page_index < KV_BACKUP_PAGE_NUM; page_index++) {\r
- page.page_location = (kv_page_location)(uintptr_t)(page_start + page_index * KV_PAGE_SIZE);\r
- ret = kv_backup_delete_one_page_repeat_key(&page);\r
- if (ret != ERRCODE_SUCC) {\r
- return ret;\r
- }\r
- }\r
-\r
- return ERRCODE_SUCC;\r
-}\r
-\r
-STATIC errcode_t kv_backup_dragpage_switch_unused_page(kv_page_handle_t dragpage, kv_page_handle_t unused_page)\r
-{\r
- /* 将换页页内容转移到unused page */\r
- errcode_t ret;\r
- uint32_t page_head_size = (uint32_t)sizeof(kv_page_header_t);\r
-\r
- /* 将换页页的页头转移到unused page */\r
- kv_page_header_t head_buffer;\r
- ret = kv_key_helper_copy_flash((uint32_t)(uintptr_t)&head_buffer,\r
- (uint32_t)dragpage.page_location, page_head_size);\r
- if (ret != ERRCODE_SUCC) {\r
- return ret;\r
- }\r
- ret = kv_key_write_flash((uint32_t)unused_page.page_location, page_head_size, (uint8_t *)&head_buffer);\r
- if (ret != ERRCODE_SUCC) {\r
- return ret;\r
- }\r
-\r
- kv_key_handle_t key;\r
- uint16_t key_size;\r
- uint32_t write_position = (uint32_t)((uintptr_t)unused_page.page_location + page_head_size);\r
- /* 将换页页的有效的key转移到unused page */\r
- errcode_t key_found = kv_page_find_first_key(&dragpage, NULL, &key);\r
- while (key_found == ERRCODE_SUCC) {\r
- if (kv_key_is_valid(&key)) {\r
- key_size = kv_key_flash_size(&key);\r
- uint8_t *kv_buffer = (uint8_t *)kv_malloc(key_size);\r
- if (kv_buffer == NULL) {\r
- return ERRCODE_MALLOC;\r
- }\r
- (void)memset_s(kv_buffer, key_size, 0, key_size);\r
- ret = kv_key_helper_copy_flash((uint32_t)(uintptr_t)kv_buffer, (uint32_t)(uintptr_t)key.key_location,\r
- key_size);\r
- if (ret != ERRCODE_SUCC) {\r
- kv_free(kv_buffer);\r
- return ret;\r
- }\r
- ret = kv_key_write_flash(write_position, key_size, kv_buffer);\r
- if (ret != ERRCODE_SUCC) {\r
- kv_free(kv_buffer);\r
- return ret;\r
- }\r
- kv_free(kv_buffer);\r
- write_position += key_size;\r
- }\r
- key_found = kv_page_find_next_key(&dragpage, NULL, &key);\r
- }\r
-\r
- return ERRCODE_SUCC;\r
-}\r
-\r
-errcode_t kv_backup_copy_unused_page_to_dragpage(uint32_t dragpage_location, uint32_t unused_page_location)\r
-{\r
- errcode_t ret;\r
-\r
- /* 首先将工作区用来换页的页的key内容全部转移到备份区原来的页上 */\r
- uint8_t *page_buffer = (uint8_t *)kv_malloc(KV_PAGE_SIZE - sizeof(kv_page_header_t));\r
- if (page_buffer == NULL) {\r
- return ERRCODE_MALLOC;\r
- }\r
- ret = kv_key_helper_copy_flash((uint32_t)(uintptr_t)page_buffer,\r
- unused_page_location + sizeof(kv_page_header_t), KV_PAGE_SIZE - sizeof(kv_page_header_t));\r
- if (ret != ERRCODE_SUCC) {\r
- kv_free(page_buffer);\r
- return ret;\r
- }\r
- ret = kv_key_write_flash(dragpage_location + sizeof(kv_page_header_t),\r
- KV_PAGE_SIZE - sizeof(kv_page_header_t), page_buffer);\r
- if (ret != ERRCODE_SUCC) {\r
- kv_free(page_buffer);\r
- return ret;\r
- }\r
- kv_free(page_buffer);\r
-\r
- /* 其次拷贝工作区换页页的页头内容:先拷贝页内容,后拷贝页头是为了更好的处理换页时掉电异常场景 */\r
- kv_page_header_t head_buffer;\r
- ret = kv_key_helper_copy_flash((uint32_t)(uintptr_t)&head_buffer,\r
- unused_page_location, sizeof(kv_page_header_t));\r
- if (ret != ERRCODE_SUCC) {\r
- return ret;\r
- }\r
- ret = kv_key_write_flash(dragpage_location, sizeof(kv_page_header_t), (uint8_t *)&head_buffer);\r
- if (ret != ERRCODE_SUCC) {\r
- return ret;\r
- }\r
-\r
- return ERRCODE_SUCC;\r
-}\r
-\r
-/* 备份区换页处理机制 */\r
-STATIC errcode_t kv_backup_dragpage_process(kv_page_handle_t dragpage)\r
-{\r
- errcode_t ret;\r
- kv_page_handle_t unused_page;\r
- kv_page_location unused_page_location = NULL;\r
- /* 1、去找工作区可以换页的页,然后将可以换页的页地址给传出 */\r
- ret = kv_nvregion_find_unused_page(&unused_page_location);\r
- if (ret != ERRCODE_SUCC) {\r
- return ret;\r
- }\r
- /* 2、将工作区内可以换页的页的内容擦除 */\r
- ret = kv_nvregion_erase_page(unused_page_location);\r
- if (ret != ERRCODE_SUCC) {\r
- return ret;\r
- }\r
- unused_page.page_location = unused_page_location;\r
- /* 3、将备份区要换页的内容拷贝到工作区可以换页的页上 */\r
- ret = kv_backup_dragpage_switch_unused_page(dragpage, unused_page);\r
- if (ret != ERRCODE_SUCC) {\r
- return ret;\r
- }\r
- /* 4、将备份区要换页的页擦除内容 */\r
- ret = kv_key_erase_flash((uint32_t)(uintptr_t)dragpage.page_location, KV_PAGE_SIZE);\r
- if (ret != ERRCODE_SUCC) {\r
- return ret;\r
- }\r
- /* 5、将换页页的内容拷贝到备份区被擦除的页上 */\r
- ret = kv_backup_copy_unused_page_to_dragpage((uint32_t)dragpage.page_location,\r
- (uint32_t)unused_page.page_location);\r
- if (ret != ERRCODE_SUCC) {\r
- return ret;\r
- }\r
- /* 6、擦除工作区换页页 */\r
- ret = kv_nvregion_erase_page(unused_page_location);\r
- if (ret != ERRCODE_SUCC) {\r
- return ret;\r
- }\r
-\r
- return ERRCODE_SUCC;\r
-}\r
-\r
-STATIC errcode_t kv_backup_find_write_position(uint16_t required_space, uint32_t *write_position)\r
-{\r
- errcode_t ret;\r
- kv_page_handle_t page;\r
- kv_page_status_t page_status;\r
- kv_page_handle_t dragpage;\r
- kv_nvregion_area_t* nvregion_area = nv_get_region_area();\r
- uint32_t page_start = nvregion_area->nv_backup_addr;\r
- for (uint32_t page_index = 0; page_index < KV_BACKUP_PAGE_NUM; page_index++) {\r
- page.page_location = (kv_page_location)(uintptr_t)(page_start + page_index * KV_PAGE_SIZE);\r
- kv_page_get_status(&page, &page_status);\r
- if ((page_status.total_space - page_status.used_space) >= required_space) {\r
- *write_position = page_status.first_writable_location;\r
- return ERRCODE_SUCC;\r
- }\r
-\r
- /* 找可换页页 */\r
- if (page_status.max_key_space >= required_space) {\r
- dragpage = page;\r
- }\r
- }\r
-\r
- /* 进行备份区换页 */\r
- ret = kv_backup_dragpage_process(dragpage);\r
- if (ret != ERRCODE_SUCC) {\r
- return ret;\r
- }\r
-\r
- /* 将备份区整理好的页可以写的位置传出 */\r
- kv_page_get_status(&dragpage, &page_status);\r
- if ((page_status.total_space - page_status.used_space) >= required_space) {\r
- *write_position = page_status.first_writable_location;\r
- return ERRCODE_SUCC;\r
- }\r
- return ERRCODE_NV_NO_ENOUGH_SPACE;\r
-}\r
-\r
-STATIC errcode_t kv_backup_write_one_page_key(kv_page_handle_t *page)\r
-{\r
- errcode_t ret;\r
- kv_key_handle_t key;\r
- uint16_t key_size;\r
- uint32_t write_position = 0;\r
- errcode_t key_found = kv_page_find_first_key(page, NULL, &key);\r
- while (key_found == ERRCODE_SUCC) {\r
- if ((kv_key_is_valid(&key)) &&\r
- (kv_region_is_enable(key.header.key_id) == ERRCODE_SUCC)) {\r
- key_size = kv_key_flash_size(&key);\r
- uint8_t *kv_buffer = (uint8_t *)kv_malloc(key_size);\r
- if (kv_buffer == NULL) {\r
- return ERRCODE_MALLOC;\r
- }\r
- ret = kv_key_helper_copy_flash((uint32_t)(uintptr_t)kv_buffer, (uint32_t)(uintptr_t)key.key_location,\r
- key_size);\r
- if (ret != ERRCODE_SUCC) {\r
- kv_free(kv_buffer);\r
- return ret;\r
- }\r
- ret = kv_backup_find_write_position(key_size, &write_position);\r
- if (ret != ERRCODE_SUCC) {\r
- kv_free(kv_buffer);\r
- return ret;\r
- }\r
- ret = kv_key_write_flash(write_position, key_size, kv_buffer);\r
- if (ret != ERRCODE_SUCC) {\r
- kv_free(kv_buffer);\r
- return ret;\r
- }\r
- kv_free(kv_buffer);\r
- }\r
- key_found = kv_page_find_next_key(page, NULL, &key);\r
- }\r
- return ERRCODE_SUCC;\r
-}\r
-\r
-errcode_t kv_backup_write_key(void)\r
-{\r
- errcode_t ret;\r
- kv_page_handle_t page;\r
-\r
- for (uint32_t page_index = 0; page_index < KV_STORE_PAGES_ACPU; page_index++) {\r
- ret = kv_store_get_page_handle(KV_STORE_APPLICATION, page_index, &page);\r
- if (ret != ERRCODE_SUCC) {\r
- return ret;\r
- }\r
- ret = kv_backup_write_one_page_key(&page);\r
- if (ret != ERRCODE_SUCC) {\r
- return ret;\r
- }\r
- }\r
-\r
- return ERRCODE_SUCC;\r
-}\r
-\r
-STATIC errcode_t kv_read_backup_key(uint16_t key_id, uint16_t *kvalue_length, uint8_t *kvalue, nv_key_attr_t *attr,\r
- kv_page_location page_location)\r
-{\r
- kv_attributes_t backup_attribute = 0;\r
- if (kvalue_length == NULL || kvalue == NULL || attr == NULL) {\r
- return ERRCODE_NV_INVALID_PARAMS;\r
- }\r
-\r
- kv_store_key_data_t key_data = {*kvalue_length, 0, kvalue};\r
- errcode_t ret_val = kv_store_get_backup_key(key_id, &key_data, &backup_attribute, page_location);\r
- if (ret_val == ERRCODE_SUCC) {\r
- memset_s(attr, sizeof(nv_key_attr_t), 0, sizeof(nv_key_attr_t));\r
- if (((uint32_t)backup_attribute & NV_ATTRIBUTE_PERMANENT) != 0) {\r
- attr->permanent = true;\r
- attr->non_upgrade = true;\r
- }\r
- if (((uint32_t)backup_attribute & NV_ATTRIBUTE_ENCRYPTED) != 0) {\r
- attr->encrypted = true;\r
- attr->non_upgrade = true;\r
- }\r
- if (((uint32_t)backup_attribute & NV_ATTRIBUTE_NON_UPGRADE) != 0) {\r
- attr->non_upgrade = true;\r
- }\r
- }\r
- *kvalue_length = key_data.kvalue_actual_length;\r
- return ret_val;\r
-}\r
-\r
-STATIC errcode_t kv_start_restore_all_keys(void)\r
-{\r
- errcode_t ret = ERRCODE_SUCC;\r
- uint16_t klength = 0;\r
- uint8_t *kvalue;\r
- nv_key_attr_t attr;\r
- kv_key_handle_t key;\r
- kv_page_handle_t page;\r
- errcode_t key_found;\r
- kv_nvregion_area_t* nvregion_area = nv_get_region_area();\r
- if (nvregion_area == NULL) {\r
- return ERRCODE_FAIL;\r
- }\r
- for (uint32_t page_num = 0; page_num < KV_BACKUP_PAGE_NUM; page_num++) {\r
- page.page_location = (kv_page_location)(uintptr_t)(nvregion_area->nv_backup_addr + page_num * KV_PAGE_SIZE);\r
- key_found = kv_page_find_first_key(&page, NULL, &key);\r
- while (key_found == ERRCODE_SUCC) {\r
- if (kv_region_is_enable(key.header.key_id) == ERRCODE_SUCC) {\r
- kvalue = (uint8_t *)kv_malloc(key.header.length);\r
- klength = key.header.length;\r
- (void)kv_read_backup_key(key.header.key_id, &klength, kvalue, &attr, page.page_location);\r
- ret = nv_direct_write_with_attr(key.header.key_id, kvalue, klength, &attr, NULL);\r
- kv_free(kvalue);\r
- }\r
- key_found = kv_page_find_next_key(&page, NULL, &key);\r
- }\r
- }\r
- return ret;\r
-}\r
-\r
-errcode_t kv_enable_restore_flag(const nv_reset_mode_t *nv_reset_mode)\r
-{\r
- nv_key_attr_t attr = {0};\r
- attr.non_upgrade = true;\r
- errcode_t ret = nv_direct_write_with_attr(NV_ID_RESTORE_ENABLE,\r
- (uint8_t *)(uintptr_t)nv_reset_mode, sizeof(nv_reset_mode_t), &attr, NULL);\r
- return ret;\r
-}\r
-\r
-static errcode_t kv_disable_restore_flag(void)\r
-{\r
- nv_reset_mode_t nv_reset_mode = {0};\r
- nv_key_attr_t attr = {0};\r
- attr.non_upgrade = true;\r
- errcode_t ret = nv_direct_write_with_attr(NV_ID_RESTORE_ENABLE,\r
- (uint8_t *)(uintptr_t)nv_reset_mode, sizeof(nv_reset_mode_t), &attr, NULL);\r
- return ret;\r
-}\r
-\r
-errcode_t kv_restore_set_region_flag(const bool *flag)\r
-{\r
- if (flag == NULL) {\r
- if (memset_s(g_reset_region_flag, sizeof(g_reset_region_flag), true, sizeof(g_reset_region_flag)) != EOK) {\r
- return ERRCODE_MEMSET;\r
- }\r
- } else {\r
- if (memcpy_s(g_reset_region_flag, sizeof(g_reset_region_flag), flag, sizeof(g_reset_region_flag)) != EOK) {\r
- return ERRCODE_MEMCPY;\r
- }\r
- }\r
- return ERRCODE_SUCC;\r
-}\r
-\r
-STATIC errcode_t kv_restore_clear_work_area(void)\r
-{\r
- errcode_t ret;\r
- kv_nvregion_area_t* nvregion_area = nv_get_region_area();\r
- if (nvregion_area == NULL) {\r
- return ERRCODE_FAIL;\r
- }\r
- ret = kv_key_erase_flash(nvregion_area->nv_data_addr, KV_STORE_DATA_SIZE);\r
- if (ret != ERRCODE_SUCC) {\r
- return ret;\r
- }\r
- return kv_update_init((cores_t)KV_STORE_APPLICATION);\r
-}\r
-\r
-STATIC errcode_t kv_restore_mode_a(void)\r
-{\r
- errcode_t ret;\r
- ret = kv_restore_clear_work_area();\r
- if (ret != ERRCODE_SUCC) {\r
- return ret;\r
- }\r
-\r
- ret = kv_restore_set_region_flag(NULL);\r
- if (ret != ERRCODE_SUCC) {\r
- return ret;\r
- }\r
-\r
- return kv_start_restore_all_keys();\r
-}\r
-\r
-STATIC errcode_t kv_restore_mode_b(const bool *flag)\r
-{\r
- if (flag == NULL) {\r
- return ERRCODE_NV_ILLEGAL_OPERATION;\r
- }\r
-\r
- errcode_t ret;\r
- ret = kv_restore_set_region_flag(flag);\r
- if (ret != ERRCODE_SUCC) {\r
- return ret;\r
- }\r
-\r
- ret = kv_reset_backup_retain_keys(KV_STORE_APPLICATION, kv_store_get_page_count(KV_STORE_APPLICATION));\r
- if (ret != ERRCODE_SUCC) {\r
- return ret;\r
- }\r
-\r
- ret = kv_restore_clear_work_area();\r
- if (ret != ERRCODE_SUCC) {\r
- return ret;\r
- }\r
-\r
- ret = kv_restore_set_region_flag(NULL);\r
- if (ret != ERRCODE_SUCC) {\r
- return ret;\r
- }\r
-\r
- ret = kv_start_restore_all_keys();\r
- if (ret != ERRCODE_SUCC) {\r
- return ret;\r
- }\r
-\r
- return restore_retain_keys();\r
-}\r
-\r
-errcode_t kv_backup_keys(const nv_backup_mode_t *backup_flag)\r
-{\r
- errcode_t ret;\r
- ret = kv_restore_set_region_flag(backup_flag->region_mode);\r
- if (ret != ERRCODE_SUCC) {\r
- return ret;\r
- }\r
- ret = kv_backup_delete_repeat_key();\r
- if (ret != ERRCODE_SUCC) {\r
- return ret;\r
- }\r
- return kv_backup_write_key();\r
-}\r
-\r
-errcode_t kv_restore_all_keys(void)\r
-{\r
- errcode_t ret;\r
- nv_reset_mode_t nv_reset_mode;\r
- uint16_t flag_length;\r
- nv_key_attr_t attr;\r
- ret = nv_direct_get_key_data(NV_ID_RESTORE_ENABLE, sizeof(nv_reset_mode_t), &flag_length,\r
- (uint8_t *)(uintptr_t)&nv_reset_mode, &attr);\r
- if (ret != ERRCODE_SUCC) {\r
- return ret;\r
- }\r
- (void)kv_disable_restore_flag();\r
-\r
- if (nv_reset_mode.mode == RESET_MODE_A) {\r
- return kv_restore_mode_a();\r
- } else if (nv_reset_mode.mode == RESET_MODE_B) {\r
- return kv_restore_mode_b(nv_reset_mode.region_flag);\r
- } else {\r
- return ERRCODE_NV_ILLEGAL_OPERATION;\r
- }\r
-}\r
-\r
+/*
+ * Copyright (c) @CompanyNameMagicTag 2023-2023. All rights reserved.
+ * Description: NV Storage Library Restore factory settings interface
+ */
+#include "nv.h"
+#include "nv_store.h"
+#include "nv_page.h"
+#include "nv_storage.h"
+#include "nv_nvregion.h"
+#include "nv_porting.h"
+#include "nv_notify.h"
+#include "nv_task_adapt.h"
+#include "nv_update.h"
+#include "common_def.h"
+#include "nv_storage_handle.h"
+#include "nv_reset.h"
+
+#if (CONFIG_NV_SUPPORT_BACKUP_RESTORE == NV_YES)
+
+bool g_reset_region_flag[KEY_ID_REGION_MAX_NUM] = {0};
+STATIC retain_key_list_handle_t* g_retain_key_list = NULL;
+
+#define RESET_MAX_BINARY_VAL 1
+#define assert_r(x) ((void)0)
+#define NV_RESTORE_MAX_KEY 4060
+
+STATIC errcode_t kv_region_is_enable(uint16_t key_id)
+{
+ uint16_t region_id = key_id / REGION_KEY_NUMS;
+ if (region_id < KEY_ID_REGION_MAX_NUM && g_reset_region_flag[region_id]) {
+ return ERRCODE_SUCC;
+ }
+ return ERRCODE_FAIL;
+}
+
+STATIC void retain_key_list_free(void)
+{
+ retain_key_t *current_key = g_retain_key_list->next;
+ retain_key_t *temp_key = NULL;
+ while (current_key != NULL) {
+ temp_key = current_key;
+ current_key = current_key->next;
+ if (temp_key->kvalue != NULL) {
+ kv_free(temp_key->kvalue);
+ }
+ kv_free(temp_key);
+ g_retain_key_list->list_length--;
+ }
+ kv_free(g_retain_key_list);
+ g_retain_key_list = NULL;
+}
+
+STATIC errcode_t retain_key_list_init(void)
+{
+ if (g_retain_key_list != NULL) {
+ return ERRCODE_SUCC;
+ }
+ g_retain_key_list = (retain_key_list_handle_t *)(uintptr_t)kv_malloc(sizeof(retain_key_list_handle_t));
+ if (g_retain_key_list == NULL) {
+ return ERRCODE_MALLOC;
+ }
+
+ g_retain_key_list->list_length = 0;
+ g_retain_key_list->tail = NULL;
+ g_retain_key_list->next = NULL;
+ return ERRCODE_SUCC;
+}
+
+STATIC void retain_key_list_add(retain_key_t* cur_key)
+{
+ if (g_retain_key_list->list_length == 0) {
+ g_retain_key_list->next = cur_key;
+ g_retain_key_list->tail = cur_key;
+ } else {
+ g_retain_key_list->tail->next = cur_key;
+ g_retain_key_list->tail = cur_key;
+ }
+ g_retain_key_list->list_length++;
+}
+
+STATIC errcode_t kv_reset_backup_retain_keys_helper(kv_page_handle_t *page)
+{
+ errcode_t res;
+ kv_key_handle_t key;
+ retain_key_t *cur_key = NULL;
+ res = retain_key_list_init();
+ if (res != ERRCODE_SUCC) {
+ return res;
+ }
+
+ errcode_t key_found = kv_page_find_first_key(page, NULL, &key);
+ while (key_found == ERRCODE_SUCC) {
+ if ((kv_key_is_valid(&key)) && (kv_region_is_enable(key.header.key_id) == ERRCODE_FAIL)) {
+ cur_key = (retain_key_t *)kv_malloc(sizeof(retain_key_t));
+ if (cur_key == NULL) {
+ return ERRCODE_MALLOC;
+ }
+ (void)memset_s(cur_key, sizeof(retain_key_t), 0, sizeof(retain_key_t));
+ cur_key->key = key.header.key_id;
+ cur_key->kvalue_length = key.header.length;
+ cur_key->kvalue = (uint8_t *)kv_malloc(cur_key->kvalue_length);
+ if (cur_key->kvalue == NULL) {
+ return ERRCODE_MALLOC;
+ }
+ cur_key->next = NULL;
+ res = kv_key_read_data(&key, cur_key->kvalue);
+ if (res != ERRCODE_SUCC) {
+ return ERRCODE_FAIL;
+ }
+ nv_log_debug("[NV] backup_retain_keys_helper key_id: %d\r\n", cur_key->key);
+ (void)retain_key_list_add(cur_key);
+ }
+ key_found = kv_page_find_next_key(page, NULL, &key);
+ }
+ return ERRCODE_SUCC;
+}
+
+STATIC errcode_t kv_reset_backup_retain_keys(kv_store_t store, uint8_t num_pages)
+{
+ errcode_t ret;
+ kv_page_handle_t kv_page;
+ for (uint8_t page_index = 0; page_index < num_pages; page_index++) {
+ ret = kv_store_get_page_handle(store, page_index, &kv_page);
+ if (ret != ERRCODE_SUCC) {
+ return ret;
+ }
+ ret = kv_reset_backup_retain_keys_helper(&kv_page);
+ if (ret != ERRCODE_SUCC) {
+ retain_key_list_free();
+ return ret;
+ }
+ }
+ return ERRCODE_SUCC;
+}
+
+STATIC errcode_t restore_retain_keys(void)
+{
+ if (g_retain_key_list == NULL) {
+ return ERRCODE_FAIL;
+ }
+ retain_key_t *cur_key = g_retain_key_list->next;
+ while (cur_key != NULL && cur_key->kvalue != NULL) {
+ nv_log_debug("[NV] restore_retain_keys_b key_id: 0x%x\r\n", cur_key->key);
+ (void)nv_direct_write_with_attr(cur_key->key, cur_key->kvalue, cur_key->kvalue_length, NULL, NULL);
+ cur_key = cur_key->next;
+ }
+ retain_key_list_free();
+ return ERRCODE_SUCC;
+}
+
+STATIC errcode_t kv_backup_invalid_key(const kv_key_handle_t *key)
+{
+ /* 计算valid相对于key起始的位置,即要写入数据在flash中相对于key_location的偏移量 */
+ uint32_t offset = ((uint32_t)(uintptr_t)&key->header.valid) - ((uint32_t)(uintptr_t)key);
+ uint32_t write_position = (uint32_t)(uintptr_t)key->key_location + offset - FLASH_PHYSICAL_ADDR_START;
+ uint32_t write_length = sizeof(key->header.valid);
+ uint8_t *write_data = (uint8_t *)kv_malloc(write_length);
+ if (write_data == NULL) {
+ return ERRCODE_MALLOC;
+ }
+ (void)memset_s(write_data, write_length, 0, write_length);
+ errcode_t ret = kv_flash_write(write_position, write_length, write_data, false);
+ kv_free(write_data);
+ return ret;
+}
+
+STATIC errcode_t kv_backup_delete_one_page_repeat_key(kv_page_handle_t *page)
+{
+ errcode_t ret;
+ kv_key_handle_t key;
+ errcode_t key_found = kv_page_find_first_key(page, NULL, &key);
+ while (key_found == ERRCODE_SUCC) {
+ if ((kv_key_is_valid(&key)) && (kv_region_is_enable(key.header.key_id) == ERRCODE_SUCC)) {
+ ret = kv_backup_invalid_key(&key);
+ if (ret != ERRCODE_SUCC) {
+ return ret;
+ }
+ }
+ key_found = kv_page_find_next_key(page, NULL, &key);
+ }
+ return ERRCODE_SUCC;
+}
+
+errcode_t kv_backup_delete_repeat_key(void)
+{
+ errcode_t ret;
+ kv_page_handle_t page;
+ kv_nvregion_area_t* nvregion_area = nv_get_region_area();
+ if (nvregion_area == NULL) {
+ return ERRCODE_FAIL;
+ }
+ uint32_t page_start = nvregion_area->nv_backup_addr;
+ /* 遍历备份区,将每个要被重复备份的key标志位置为无效 */
+ for (uint32_t page_index = 0; page_index < KV_BACKUP_PAGE_NUM; page_index++) {
+ page.page_location = (kv_page_location)(uintptr_t)(page_start + page_index * KV_PAGE_SIZE);
+ ret = kv_backup_delete_one_page_repeat_key(&page);
+ if (ret != ERRCODE_SUCC) {
+ return ret;
+ }
+ }
+
+ return ERRCODE_SUCC;
+}
+
+STATIC errcode_t kv_backup_dragpage_switch_unused_page(kv_page_handle_t dragpage, kv_page_handle_t unused_page)
+{
+ /* 将换页页内容转移到unused page */
+ errcode_t ret;
+ uint32_t page_head_size = (uint32_t)sizeof(kv_page_header_t);
+
+ /* 将换页页的页头转移到unused page */
+ kv_page_header_t head_buffer;
+ ret = kv_key_helper_copy_flash((uint32_t)(uintptr_t)&head_buffer,
+ (uint32_t)dragpage.page_location, page_head_size);
+ if (ret != ERRCODE_SUCC) {
+ return ret;
+ }
+ ret = kv_key_write_flash((uint32_t)unused_page.page_location, page_head_size, (uint8_t *)&head_buffer);
+ if (ret != ERRCODE_SUCC) {
+ return ret;
+ }
+
+ kv_key_handle_t key;
+ uint16_t key_size;
+ uint32_t write_position = (uint32_t)((uintptr_t)unused_page.page_location + page_head_size);
+ /* 将换页页的有效的key转移到unused page */
+ errcode_t key_found = kv_page_find_first_key(&dragpage, NULL, &key);
+ while (key_found == ERRCODE_SUCC) {
+ if (kv_key_is_valid(&key)) {
+ key_size = kv_key_flash_size(&key);
+ uint8_t *kv_buffer = (uint8_t *)kv_malloc(key_size);
+ if (kv_buffer == NULL) {
+ return ERRCODE_MALLOC;
+ }
+ (void)memset_s(kv_buffer, key_size, 0, key_size);
+ ret = kv_key_helper_copy_flash((uint32_t)(uintptr_t)kv_buffer, (uint32_t)(uintptr_t)key.key_location,
+ key_size);
+ if (ret != ERRCODE_SUCC) {
+ kv_free(kv_buffer);
+ return ret;
+ }
+ ret = kv_key_write_flash(write_position, key_size, kv_buffer);
+ if (ret != ERRCODE_SUCC) {
+ kv_free(kv_buffer);
+ return ret;
+ }
+ kv_free(kv_buffer);
+ write_position += key_size;
+ }
+ key_found = kv_page_find_next_key(&dragpage, NULL, &key);
+ }
+
+ return ERRCODE_SUCC;
+}
+
+errcode_t kv_backup_copy_unused_page_to_dragpage(uint32_t dragpage_location, uint32_t unused_page_location)
+{
+ errcode_t ret;
+
+ /* 首先将工作区用来换页的页的key内容全部转移到备份区原来的页上 */
+ uint8_t *page_buffer = (uint8_t *)kv_malloc(KV_PAGE_SIZE - sizeof(kv_page_header_t));
+ if (page_buffer == NULL) {
+ return ERRCODE_MALLOC;
+ }
+ ret = kv_key_helper_copy_flash((uint32_t)(uintptr_t)page_buffer,
+ unused_page_location + sizeof(kv_page_header_t), KV_PAGE_SIZE - sizeof(kv_page_header_t));
+ if (ret != ERRCODE_SUCC) {
+ kv_free(page_buffer);
+ return ret;
+ }
+ ret = kv_key_write_flash(dragpage_location + sizeof(kv_page_header_t),
+ KV_PAGE_SIZE - sizeof(kv_page_header_t), page_buffer);
+ if (ret != ERRCODE_SUCC) {
+ kv_free(page_buffer);
+ return ret;
+ }
+ kv_free(page_buffer);
+
+ /* 其次拷贝工作区换页页的页头内容:先拷贝页内容,后拷贝页头是为了更好的处理换页时掉电异常场景 */
+ kv_page_header_t head_buffer;
+ ret = kv_key_helper_copy_flash((uint32_t)(uintptr_t)&head_buffer,
+ unused_page_location, sizeof(kv_page_header_t));
+ if (ret != ERRCODE_SUCC) {
+ return ret;
+ }
+ ret = kv_key_write_flash(dragpage_location, sizeof(kv_page_header_t), (uint8_t *)&head_buffer);
+ if (ret != ERRCODE_SUCC) {
+ return ret;
+ }
+
+ return ERRCODE_SUCC;
+}
+
+/* 备份区换页处理机制 */
+STATIC errcode_t kv_backup_dragpage_process(kv_page_handle_t dragpage)
+{
+ errcode_t ret;
+ kv_page_handle_t unused_page;
+ kv_page_location unused_page_location = NULL;
+ /* 1、去找工作区可以换页的页,然后将可以换页的页地址给传出 */
+ ret = kv_nvregion_find_unused_page(&unused_page_location);
+ if (ret != ERRCODE_SUCC) {
+ return ret;
+ }
+ /* 2、将工作区内可以换页的页的内容擦除 */
+ ret = kv_nvregion_erase_page(unused_page_location);
+ if (ret != ERRCODE_SUCC) {
+ return ret;
+ }
+ unused_page.page_location = unused_page_location;
+ /* 3、将备份区要换页的内容拷贝到工作区可以换页的页上 */
+ ret = kv_backup_dragpage_switch_unused_page(dragpage, unused_page);
+ if (ret != ERRCODE_SUCC) {
+ return ret;
+ }
+ /* 4、将备份区要换页的页擦除内容 */
+ ret = kv_key_erase_flash((uint32_t)(uintptr_t)dragpage.page_location, KV_PAGE_SIZE);
+ if (ret != ERRCODE_SUCC) {
+ return ret;
+ }
+ /* 5、将换页页的内容拷贝到备份区被擦除的页上 */
+ ret = kv_backup_copy_unused_page_to_dragpage((uint32_t)dragpage.page_location,
+ (uint32_t)unused_page.page_location);
+ if (ret != ERRCODE_SUCC) {
+ return ret;
+ }
+ /* 6、擦除工作区换页页 */
+ ret = kv_nvregion_erase_page(unused_page_location);
+ if (ret != ERRCODE_SUCC) {
+ return ret;
+ }
+
+ return ERRCODE_SUCC;
+}
+
+STATIC errcode_t kv_backup_find_write_position(uint16_t required_space, uint32_t *write_position)
+{
+ errcode_t ret;
+ kv_page_handle_t page;
+ kv_page_status_t page_status;
+ kv_page_handle_t dragpage;
+ kv_nvregion_area_t* nvregion_area = nv_get_region_area();
+ uint32_t page_start = nvregion_area->nv_backup_addr;
+ for (uint32_t page_index = 0; page_index < KV_BACKUP_PAGE_NUM; page_index++) {
+ page.page_location = (kv_page_location)(uintptr_t)(page_start + page_index * KV_PAGE_SIZE);
+ kv_page_get_status(&page, &page_status);
+ if ((page_status.total_space - page_status.used_space) >= required_space) {
+ *write_position = page_status.first_writable_location;
+ return ERRCODE_SUCC;
+ }
+
+ /* 找可换页页 */
+ if (page_status.max_key_space >= required_space) {
+ dragpage = page;
+ }
+ }
+
+ /* 进行备份区换页 */
+ ret = kv_backup_dragpage_process(dragpage);
+ if (ret != ERRCODE_SUCC) {
+ return ret;
+ }
+
+ /* 将备份区整理好的页可以写的位置传出 */
+ kv_page_get_status(&dragpage, &page_status);
+ if ((page_status.total_space - page_status.used_space) >= required_space) {
+ *write_position = page_status.first_writable_location;
+ return ERRCODE_SUCC;
+ }
+ return ERRCODE_NV_NO_ENOUGH_SPACE;
+}
+
+STATIC errcode_t kv_backup_write_one_page_key(kv_page_handle_t *page)
+{
+ errcode_t ret;
+ kv_key_handle_t key;
+ uint16_t key_size;
+ uint32_t write_position = 0;
+ errcode_t key_found = kv_page_find_first_key(page, NULL, &key);
+ while (key_found == ERRCODE_SUCC) {
+ if ((kv_key_is_valid(&key)) &&
+ (kv_region_is_enable(key.header.key_id) == ERRCODE_SUCC)) {
+ key_size = kv_key_flash_size(&key);
+ uint8_t *kv_buffer = (uint8_t *)kv_malloc(key_size);
+ if (kv_buffer == NULL) {
+ return ERRCODE_MALLOC;
+ }
+ ret = kv_key_helper_copy_flash((uint32_t)(uintptr_t)kv_buffer, (uint32_t)(uintptr_t)key.key_location,
+ key_size);
+ if (ret != ERRCODE_SUCC) {
+ kv_free(kv_buffer);
+ return ret;
+ }
+ ret = kv_backup_find_write_position(key_size, &write_position);
+ if (ret != ERRCODE_SUCC) {
+ kv_free(kv_buffer);
+ return ret;
+ }
+ ret = kv_key_write_flash(write_position, key_size, kv_buffer);
+ if (ret != ERRCODE_SUCC) {
+ kv_free(kv_buffer);
+ return ret;
+ }
+ kv_free(kv_buffer);
+ }
+ key_found = kv_page_find_next_key(page, NULL, &key);
+ }
+ return ERRCODE_SUCC;
+}
+
+errcode_t kv_backup_write_key(void)
+{
+ errcode_t ret;
+ kv_page_handle_t page;
+
+ for (uint32_t page_index = 0; page_index < KV_STORE_PAGES_ACPU; page_index++) {
+ ret = kv_store_get_page_handle(KV_STORE_APPLICATION, page_index, &page);
+ if (ret != ERRCODE_SUCC) {
+ return ret;
+ }
+ ret = kv_backup_write_one_page_key(&page);
+ if (ret != ERRCODE_SUCC) {
+ return ret;
+ }
+ }
+
+ return ERRCODE_SUCC;
+}
+
+STATIC errcode_t kv_read_backup_key(uint16_t key_id, uint16_t *kvalue_length, uint8_t *kvalue, nv_key_attr_t *attr,
+ kv_page_location page_location)
+{
+ kv_attributes_t backup_attribute = 0;
+ if (kvalue_length == NULL || kvalue == NULL || attr == NULL) {
+ return ERRCODE_NV_INVALID_PARAMS;
+ }
+
+ kv_store_key_data_t key_data = {*kvalue_length, 0, kvalue};
+ errcode_t ret_val = kv_store_get_backup_key(key_id, &key_data, &backup_attribute, page_location);
+ if (ret_val == ERRCODE_SUCC) {
+ memset_s(attr, sizeof(nv_key_attr_t), 0, sizeof(nv_key_attr_t));
+ if (((uint32_t)backup_attribute & NV_ATTRIBUTE_PERMANENT) != 0) {
+ attr->permanent = true;
+ attr->non_upgrade = true;
+ }
+ if (((uint32_t)backup_attribute & NV_ATTRIBUTE_ENCRYPTED) != 0) {
+ attr->encrypted = true;
+ attr->non_upgrade = true;
+ }
+ if (((uint32_t)backup_attribute & NV_ATTRIBUTE_NON_UPGRADE) != 0) {
+ attr->non_upgrade = true;
+ }
+ }
+ *kvalue_length = key_data.kvalue_actual_length;
+ return ret_val;
+}
+
+STATIC errcode_t kv_start_restore_all_keys(void)
+{
+ errcode_t ret = ERRCODE_SUCC;
+ uint16_t klength = 0;
+ uint8_t *kvalue;
+ nv_key_attr_t attr;
+ kv_key_handle_t key;
+ kv_page_handle_t page;
+ errcode_t key_found;
+ kv_nvregion_area_t* nvregion_area = nv_get_region_area();
+ if (nvregion_area == NULL) {
+ return ERRCODE_FAIL;
+ }
+ for (uint32_t page_num = 0; page_num < KV_BACKUP_PAGE_NUM; page_num++) {
+ page.page_location = (kv_page_location)(uintptr_t)(nvregion_area->nv_backup_addr + page_num * KV_PAGE_SIZE);
+ key_found = kv_page_find_first_key(&page, NULL, &key);
+ while (key_found == ERRCODE_SUCC) {
+ if (kv_region_is_enable(key.header.key_id) == ERRCODE_SUCC) {
+ kvalue = (uint8_t *)kv_malloc(key.header.length);
+ klength = key.header.length;
+ (void)kv_read_backup_key(key.header.key_id, &klength, kvalue, &attr, page.page_location);
+ ret = nv_direct_write_with_attr(key.header.key_id, kvalue, klength, &attr, NULL);
+ kv_free(kvalue);
+ }
+ key_found = kv_page_find_next_key(&page, NULL, &key);
+ }
+ }
+ return ret;
+}
+
+errcode_t kv_enable_restore_flag(const nv_reset_mode_t *nv_reset_mode)
+{
+ nv_key_attr_t attr = {0};
+ attr.non_upgrade = true;
+ errcode_t ret = nv_direct_write_with_attr(NV_ID_RESTORE_ENABLE,
+ (uint8_t *)(uintptr_t)nv_reset_mode, sizeof(nv_reset_mode_t), &attr, NULL);
+ return ret;
+}
+
+static errcode_t kv_disable_restore_flag(void)
+{
+ nv_reset_mode_t nv_reset_mode = {0};
+ nv_key_attr_t attr = {0};
+ attr.non_upgrade = true;
+ errcode_t ret = nv_direct_write_with_attr(NV_ID_RESTORE_ENABLE,
+ (uint8_t *)(uintptr_t)nv_reset_mode, sizeof(nv_reset_mode_t), &attr, NULL);
+ return ret;
+}
+
+errcode_t kv_restore_set_region_flag(const bool *flag)
+{
+ if (flag == NULL) {
+ if (memset_s(g_reset_region_flag, sizeof(g_reset_region_flag), true, sizeof(g_reset_region_flag)) != EOK) {
+ return ERRCODE_MEMSET;
+ }
+ } else {
+ if (memcpy_s(g_reset_region_flag, sizeof(g_reset_region_flag), flag, sizeof(g_reset_region_flag)) != EOK) {
+ return ERRCODE_MEMCPY;
+ }
+ }
+ return ERRCODE_SUCC;
+}
+
+STATIC errcode_t kv_restore_clear_work_area(void)
+{
+ errcode_t ret;
+ kv_nvregion_area_t* nvregion_area = nv_get_region_area();
+ if (nvregion_area == NULL) {
+ return ERRCODE_FAIL;
+ }
+ ret = kv_key_erase_flash(nvregion_area->nv_data_addr, KV_STORE_DATA_SIZE);
+ if (ret != ERRCODE_SUCC) {
+ return ret;
+ }
+ return kv_update_init((cores_t)KV_STORE_APPLICATION);
+}
+
+STATIC errcode_t kv_restore_mode_a(void)
+{
+ errcode_t ret;
+ ret = kv_restore_clear_work_area();
+ if (ret != ERRCODE_SUCC) {
+ return ret;
+ }
+
+ ret = kv_restore_set_region_flag(NULL);
+ if (ret != ERRCODE_SUCC) {
+ return ret;
+ }
+
+ return kv_start_restore_all_keys();
+}
+
+STATIC errcode_t kv_restore_mode_b(const bool *flag)
+{
+ if (flag == NULL) {
+ return ERRCODE_NV_ILLEGAL_OPERATION;
+ }
+
+ errcode_t ret;
+ ret = kv_restore_set_region_flag(flag);
+ if (ret != ERRCODE_SUCC) {
+ return ret;
+ }
+
+ ret = kv_reset_backup_retain_keys(KV_STORE_APPLICATION, kv_store_get_page_count(KV_STORE_APPLICATION));
+ if (ret != ERRCODE_SUCC) {
+ return ret;
+ }
+
+ ret = kv_restore_clear_work_area();
+ if (ret != ERRCODE_SUCC) {
+ return ret;
+ }
+
+ ret = kv_restore_set_region_flag(NULL);
+ if (ret != ERRCODE_SUCC) {
+ return ret;
+ }
+
+ ret = kv_start_restore_all_keys();
+ if (ret != ERRCODE_SUCC) {
+ return ret;
+ }
+
+ return restore_retain_keys();
+}
+
+errcode_t kv_backup_keys(const nv_backup_mode_t *backup_flag)
+{
+ errcode_t ret;
+ ret = kv_restore_set_region_flag(backup_flag->region_mode);
+ if (ret != ERRCODE_SUCC) {
+ return ret;
+ }
+ ret = kv_backup_delete_repeat_key();
+ if (ret != ERRCODE_SUCC) {
+ return ret;
+ }
+ return kv_backup_write_key();
+}
+
+errcode_t kv_restore_all_keys(void)
+{
+ errcode_t ret;
+ nv_reset_mode_t nv_reset_mode;
+ uint16_t flag_length;
+ nv_key_attr_t attr;
+ ret = nv_direct_get_key_data(NV_ID_RESTORE_ENABLE, sizeof(nv_reset_mode_t), &flag_length,
+ (uint8_t *)(uintptr_t)&nv_reset_mode, &attr);
+ if (ret != ERRCODE_SUCC) {
+ return ret;
+ }
+ (void)kv_disable_restore_flag();
+
+ if (nv_reset_mode.mode == RESET_MODE_A) {
+ return kv_restore_mode_a();
+ } else if (nv_reset_mode.mode == RESET_MODE_B) {
+ return kv_restore_mode_b(nv_reset_mode.region_flag);
+ } else {
+ return ERRCODE_NV_ILLEGAL_OPERATION;
+ }
+}
+
#endif /* #if (CONFIG_NV_SUPPORT_BACKUP_RESTORE == NV_YES) */
\ No newline at end of file
-/*\r
- * Copyright (c) @CompanyNameMagicTag 2023-2023. All rights reserved.\r
- * Description: NV Storage Library Restore factory settings interface\r
- */\r
-\r
-#ifndef NV_RESET_H_\r
-#define NV_RESET_H_\r
-\r
-#include "nv.h"\r
-#include "errcode.h"\r
-\r
-#ifdef __cplusplus\r
-#if __cplusplus\r
-extern "C" {\r
-#endif /* __cplusplus */\r
-#endif /* __cplusplus */\r
-\r
-#define RESET_MODE_A 1\r
-#define RESET_MODE_B 2\r
-\r
-typedef struct {\r
- uint8_t mode;\r
- bool region_flag[KEY_ID_REGION_MAX_NUM];\r
-} nv_reset_mode_t;\r
-\r
-typedef struct retain_key {\r
- uint16_t key;\r
- uint16_t kvalue_length;\r
- uint8_t *kvalue;\r
- struct retain_key* next;\r
-} retain_key_t;\r
-\r
-typedef struct {\r
- uint8_t list_length;\r
- retain_key_t* tail;\r
- retain_key_t* next;\r
-} retain_key_list_handle_t;\r
-\r
-errcode_t kv_backup_keys(const nv_backup_mode_t *backup_flag);\r
-errcode_t kv_restore_all_keys(void);\r
-errcode_t kv_enable_restore_flag(const nv_reset_mode_t *nv_reset_mode);\r
-errcode_t kv_backup_copy_unused_page_to_dragpage(uint32_t dragpage_location, uint32_t unused_page_location);\r
-\r
-errcode_t kv_restore_set_region_flag(const bool *flag);\r
-errcode_t kv_backup_delete_repeat_key(void);\r
-errcode_t kv_backup_write_key(void);\r
-\r
-#ifdef __cplusplus\r
-#if __cplusplus\r
-}\r
-#endif /* __cplusplus */\r
-#endif /* __cplusplus */\r
-\r
-#endif /* NV_RESET_H_ */\r
-\r
+/*
+ * Copyright (c) @CompanyNameMagicTag 2023-2023. All rights reserved.
+ * Description: NV Storage Library Restore factory settings interface
+ */
+
+#ifndef NV_RESET_H_
+#define NV_RESET_H_
+
+#include "nv.h"
+#include "errcode.h"
+
+#ifdef __cplusplus
+#if __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+#endif /* __cplusplus */
+
+#define RESET_MODE_A 1
+#define RESET_MODE_B 2
+
+typedef struct {
+ uint8_t mode;
+ bool region_flag[KEY_ID_REGION_MAX_NUM];
+} nv_reset_mode_t;
+
+typedef struct retain_key {
+ uint16_t key;
+ uint16_t kvalue_length;
+ uint8_t *kvalue;
+ struct retain_key* next;
+} retain_key_t;
+
+typedef struct {
+ uint8_t list_length;
+ retain_key_t* tail;
+ retain_key_t* next;
+} retain_key_list_handle_t;
+
+errcode_t kv_backup_keys(const nv_backup_mode_t *backup_flag);
+errcode_t kv_restore_all_keys(void);
+errcode_t kv_enable_restore_flag(const nv_reset_mode_t *nv_reset_mode);
+errcode_t kv_backup_copy_unused_page_to_dragpage(uint32_t dragpage_location, uint32_t unused_page_location);
+
+errcode_t kv_restore_set_region_flag(const bool *flag);
+errcode_t kv_backup_delete_repeat_key(void);
+errcode_t kv_backup_write_key(void);
+
+#ifdef __cplusplus
+#if __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* __cplusplus */
+
+#endif /* NV_RESET_H_ */
+
-/*\r
- * Copyright (c) @CompanyNameMagicTag 2019-2022. All rights reserved.\r
- * Description: KV Storage Library remote RPC handler implementations\r
- */\r
-#ifndef CONFIG_NV_SUPPORT_SINGLE_CORE_SYSTEM\r
-#include "nv_rpc.h"\r
-#include "rpc_auto_generated.h"\r
-#include "rpc_interface.h"\r
-#include "nv_store.h"\r
-#include "nv_porting.h"\r
-\r
-errcode_t uapi_rpc_remote_cmd_kv_store_get_status(cores core, uint16_t *store_status_length,\r
- rpc_auto_generated_release_func *store_status_free_mem_callback,\r
- uint8_t **store_status)\r
-{\r
- errcode_t call_err;\r
- kv_store_t store;\r
- unused(call_err);\r
- /* Initialise pass-back values in case we fail early */\r
- *store_status = NULL;\r
- *store_status_length = 0;\r
-\r
- uint32_t status_size = sizeof(nv_store_status_t);\r
- *store_status = osal_kmalloc(sizeof(nv_store_status_t), OSAL_GFP_KERNEL);\r
- if (*store_status == NULL) {\r
- return ERRCODE_MALLOC;\r
- }\r
- *store_status_length = status_size;\r
- *store_status_free_mem_callback = osal_kfree;\r
- store = kv_store_from_core(core);\r
- return kv_store_get_status(store, *store_status);\r
-}\r
-\r
-errcode_t uapi_rpc_remote_cmd_kv_store_get_key(cores core, uint16_t key_id, uint16_t *kvalue_length,\r
- rpc_auto_generated_release_func *kvalue_free_mem_callback,\r
- uint8_t **kvalue)\r
-{\r
- errcode_t call_err;\r
- kv_store_t store;\r
- kv_key_handle_t key;\r
-\r
- /* Initialise pass-back values in case we fail early */\r
- *kvalue = NULL;\r
- *kvalue_length = 0;\r
-\r
- store = kv_store_from_core(core);\r
- call_err = kv_store_find_valid_key(store, key_id, &key);\r
- if (call_err != ERRCODE_SUCC) {\r
- return call_err;\r
- }\r
-\r
- /* Attempt to obtain key data from store */\r
- *kvalue = osal_kmalloc(key.header.length, OSAL_GFP_KERNEL);\r
- if (*kvalue == NULL) {\r
- return ERRCODE_MALLOC;\r
- }\r
- *kvalue_length = key.header.length;\r
- *kvalue_free_mem_callback = osal_kfree;\r
-\r
- return kv_key_read_data(&key, *kvalue);\r
-}\r
-\r
-errcode_t uapi_rpc_remote_cmd_kv_store_get_key_by_addr(cores core,\r
- uint16_t key_id,\r
- uint32_t kvalue_addr,\r
- uint16_t length)\r
-{\r
- errcode_t call_err;\r
- kv_store_t store;\r
- kv_key_handle_t key;\r
-\r
- store = kv_store_from_core(core);\r
- call_err = kv_store_find_valid_key(store, key_id, &key);\r
- if (call_err != ERRCODE_SUCC) {\r
- return call_err;\r
- }\r
-\r
- /* Attempt to obtain key data from store */\r
- if (length < key.header.length) {\r
- return ERRCODE_NV_GET_BUFFER_TOO_SMALL;\r
- }\r
- return kv_key_read_data(&key, (uint8_t *)(uintptr_t)kvalue_addr);\r
-}\r
-\r
-errcode_t uapi_rpc_remote_cmd_kv_store_get_key_attr(cores core, uint16_t key_id, uint16_t *len, uint8_t *attributes)\r
-{\r
- kv_store_t store;\r
-\r
- /* Initialise pass-back value in case we fail early */\r
- *attributes = 0;\r
- *len = 0;\r
-\r
- store = kv_store_from_core(core);\r
- return kv_store_get_key_attr(store, (kv_key_id)key_id, len, (kv_attributes_t *)attributes);\r
-}\r
-#endif\r
+/*
+ * Copyright (c) @CompanyNameMagicTag 2019-2022. All rights reserved.
+ * Description: KV Storage Library remote RPC handler implementations
+ */
+#ifndef CONFIG_NV_SUPPORT_SINGLE_CORE_SYSTEM
+#include "nv_rpc.h"
+#include "rpc_auto_generated.h"
+#include "rpc_interface.h"
+#include "nv_store.h"
+#include "nv_porting.h"
+
+errcode_t uapi_rpc_remote_cmd_kv_store_get_status(cores core, uint16_t *store_status_length,
+ rpc_auto_generated_release_func *store_status_free_mem_callback,
+ uint8_t **store_status)
+{
+ errcode_t call_err;
+ kv_store_t store;
+ unused(call_err);
+ /* Initialise pass-back values in case we fail early */
+ *store_status = NULL;
+ *store_status_length = 0;
+
+ uint32_t status_size = sizeof(nv_store_status_t);
+ *store_status = osal_kmalloc(sizeof(nv_store_status_t), OSAL_GFP_KERNEL);
+ if (*store_status == NULL) {
+ return ERRCODE_MALLOC;
+ }
+ *store_status_length = status_size;
+ *store_status_free_mem_callback = osal_kfree;
+ store = kv_store_from_core(core);
+ return kv_store_get_status(store, *store_status);
+}
+
+errcode_t uapi_rpc_remote_cmd_kv_store_get_key(cores core, uint16_t key_id, uint16_t *kvalue_length,
+ rpc_auto_generated_release_func *kvalue_free_mem_callback,
+ uint8_t **kvalue)
+{
+ errcode_t call_err;
+ kv_store_t store;
+ kv_key_handle_t key;
+
+ /* Initialise pass-back values in case we fail early */
+ *kvalue = NULL;
+ *kvalue_length = 0;
+
+ store = kv_store_from_core(core);
+ call_err = kv_store_find_valid_key(store, key_id, &key);
+ if (call_err != ERRCODE_SUCC) {
+ return call_err;
+ }
+
+ /* Attempt to obtain key data from store */
+ *kvalue = osal_kmalloc(key.header.length, OSAL_GFP_KERNEL);
+ if (*kvalue == NULL) {
+ return ERRCODE_MALLOC;
+ }
+ *kvalue_length = key.header.length;
+ *kvalue_free_mem_callback = osal_kfree;
+
+ return kv_key_read_data(&key, *kvalue);
+}
+
+errcode_t uapi_rpc_remote_cmd_kv_store_get_key_by_addr(cores core,
+ uint16_t key_id,
+ uint32_t kvalue_addr,
+ uint16_t length)
+{
+ errcode_t call_err;
+ kv_store_t store;
+ kv_key_handle_t key;
+
+ store = kv_store_from_core(core);
+ call_err = kv_store_find_valid_key(store, key_id, &key);
+ if (call_err != ERRCODE_SUCC) {
+ return call_err;
+ }
+
+ /* Attempt to obtain key data from store */
+ if (length < key.header.length) {
+ return ERRCODE_NV_GET_BUFFER_TOO_SMALL;
+ }
+ return kv_key_read_data(&key, (uint8_t *)(uintptr_t)kvalue_addr);
+}
+
+errcode_t uapi_rpc_remote_cmd_kv_store_get_key_attr(cores core, uint16_t key_id, uint16_t *len, uint8_t *attributes)
+{
+ kv_store_t store;
+
+ /* Initialise pass-back value in case we fail early */
+ *attributes = 0;
+ *len = 0;
+
+ store = kv_store_from_core(core);
+ return kv_store_get_key_attr(store, (kv_key_id)key_id, len, (kv_attributes_t *)attributes);
+}
+#endif
-/*\r
- * Copyright (c) @CompanyNameMagicTag 2019-2022. All rights reserved.\r
- * Description: KV Storage Library remote RPC handler implementations\r
- */\r
-\r
-#include "errcode.h"\r
-\r
-#ifndef SRC_LIB_KV_STORAGE_PUBLIC_NV_RPC_H_\r
-#define SRC_LIB_KV_STORAGE_PUBLIC_NV_RPC_H_\r
-\r
-#endif /* SRC_LIB_KV_STORAGE_PUBLIC_NV_RPC_H_ */\r
+/*
+ * Copyright (c) @CompanyNameMagicTag 2019-2022. All rights reserved.
+ * Description: KV Storage Library remote RPC handler implementations
+ */
+
+#include "errcode.h"
+
+#ifndef SRC_LIB_KV_STORAGE_PUBLIC_NV_RPC_H_
+#define SRC_LIB_KV_STORAGE_PUBLIC_NV_RPC_H_
+
+#endif /* SRC_LIB_KV_STORAGE_PUBLIC_NV_RPC_H_ */
-/*\r
- * Copyright (c) @CompanyNameMagicTag 2019-2022. All rights reserved.\r
- * Description: KV Storage Library store access interface\r
- */\r
-\r
-#ifndef NV_STORE_H\r
-#define NV_STORE_H\r
-\r
-#include "nv_page.h"\r
-#include "platform_types.h"\r
-#include "nv.h"\r
-#include "errcode.h"\r
-\r
-#ifdef __cplusplus\r
-#if __cplusplus\r
-extern "C" {\r
-#endif /* __cplusplus */\r
-#endif /* __cplusplus */\r
-\r
-/** Store ids for each core, with a minimum hamming distance of 10 between each value */\r
-#define KV_STORE_ID_SCPU 0xCB7E\r
-#define KV_STORE_ID_ACPU 0x254D\r
-#define KV_STORE_ID_BACKUP 0x34B2\r
-\r
-typedef enum {\r
- KV_STORE_APPLICATION,\r
- KV_STORE_MAX_NUM,\r
-} kv_store_t;\r
-\r
-typedef struct {\r
- uint16_t kvalue_max_length;\r
- uint16_t kvalue_actual_length;\r
- uint8_t *kvalue;\r
-} kv_store_key_data_t;\r
-\r
-cores_t kv_store_to_core(kv_store_t store);\r
-kv_store_t kv_store_from_core(cores_t core);\r
-uint16_t kv_store_get_id(kv_store_t store);\r
-uint8_t kv_store_get_page_count(kv_store_t store);\r
-errcode_t kv_store_get_page_handle(kv_store_t store, uint32_t page_index, kv_page_handle_t *page);\r
-errcode_t kv_store_find_valid_key(kv_store_t store, kv_key_id key_id, kv_key_handle_t *key);\r
-errcode_t kv_store_get_backup_key(kv_key_id key_id, kv_store_key_data_t *key_data, kv_attributes_t *attributes,\r
- kv_page_location page_location);\r
-errcode_t kv_store_get_key_attr(kv_store_t store, kv_key_id key_id, uint16_t *len, kv_attributes_t *attributes);\r
-errcode_t kv_store_get_key(kv_store_t store, kv_key_id key_id, kv_store_key_data_t *key_data,\r
- kv_attributes_t *attributes);\r
-errcode_t kv_store_get_status(kv_store_t store, nv_store_status_t *store_status);\r
-errcode_t kv_store_find_write_page(kv_store_t store, uint32_t required_space, kv_page_handle_t *page,\r
- kv_page_status_t *page_status);\r
-errcode_t kv_store_read_backup_key(kv_key_id key_id, kv_store_key_data_t *key_data, kv_attributes_t *attributes);\r
-\r
-#ifdef __cplusplus\r
-#if __cplusplus\r
-}\r
-#endif /* __cplusplus */\r
-#endif /* __cplusplus */\r
-\r
-#endif /* NV_STORE_H_ */\r
+/*
+ * Copyright (c) @CompanyNameMagicTag 2019-2022. All rights reserved.
+ * Description: KV Storage Library store access interface
+ */
+
+#ifndef NV_STORE_H
+#define NV_STORE_H
+
+#include "nv_page.h"
+#include "platform_types.h"
+#include "nv.h"
+#include "errcode.h"
+
+#ifdef __cplusplus
+#if __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+#endif /* __cplusplus */
+
+/** Store ids for each core, with a minimum hamming distance of 10 between each value */
+#define KV_STORE_ID_SCPU 0xCB7E
+#define KV_STORE_ID_ACPU 0x254D
+#define KV_STORE_ID_BACKUP 0x34B2
+
+typedef enum {
+ KV_STORE_APPLICATION,
+ KV_STORE_MAX_NUM,
+} kv_store_t;
+
+typedef struct {
+ uint16_t kvalue_max_length;
+ uint16_t kvalue_actual_length;
+ uint8_t *kvalue;
+} kv_store_key_data_t;
+
+cores_t kv_store_to_core(kv_store_t store);
+kv_store_t kv_store_from_core(cores_t core);
+uint16_t kv_store_get_id(kv_store_t store);
+uint8_t kv_store_get_page_count(kv_store_t store);
+errcode_t kv_store_get_page_handle(kv_store_t store, uint32_t page_index, kv_page_handle_t *page);
+errcode_t kv_store_find_valid_key(kv_store_t store, kv_key_id key_id, kv_key_handle_t *key);
+errcode_t kv_store_get_backup_key(kv_key_id key_id, kv_store_key_data_t *key_data, kv_attributes_t *attributes,
+ kv_page_location page_location);
+errcode_t kv_store_get_key_attr(kv_store_t store, kv_key_id key_id, uint16_t *len, kv_attributes_t *attributes);
+errcode_t kv_store_get_key(kv_store_t store, kv_key_id key_id, kv_store_key_data_t *key_data,
+ kv_attributes_t *attributes);
+errcode_t kv_store_get_status(kv_store_t store, nv_store_status_t *store_status);
+errcode_t kv_store_find_write_page(kv_store_t store, uint32_t required_space, kv_page_handle_t *page,
+ kv_page_status_t *page_status);
+errcode_t kv_store_read_backup_key(kv_key_id key_id, kv_store_key_data_t *key_data, kv_attributes_t *attributes);
+
+#ifdef __cplusplus
+#if __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* __cplusplus */
+
+#endif /* NV_STORE_H_ */
-/*\r
- * Copyright (c) @CompanyNameMagicTag 2021-2022. All rights reserved.\r
- * Description: FLASH task shared definitions between the SCPU and A/PCPU.\r
- */\r
-\r
-#ifndef FLASH_TASK_ETYPES_H\r
-#define FLASH_TASK_ETYPES_H\r
-\r
-#include "stdbool.h"\r
-#include "errcode.h"\r
-\r
-#ifdef __cplusplus\r
-#if __cplusplus\r
-extern "C" {\r
-#endif /* __cplusplus */\r
-#endif /* __cplusplus */\r
-\r
-/** Identifier to check that a section of memory is indeed a flash_task_node */\r
-#define FLASH_TASK_NODE_IDENTIFIER 0xd3a71f85\r
-\r
-/* Normal values of the flash_task_node.result field; anything else is an error being reported - must remain in sync\r
- * with the SCPU soc_errno. The A/PCPU can use these defines in case they have a different set of error codes */\r
-#define FLASH_TASK_DISCARD 0x80002040\r
-#define FLASH_TASK_DISCARDED 0x80002041\r
-#define FLASH_TASK_READY 0x80002042\r
-#define FLASH_TASK_BEING_PROCESSED 0x80002043\r
-#define FLASH_TASK_COMPLETED 0x80003130\r
-\r
-#define FLASH_TASK_ERASE_ALL_MAGIC_VALUE_LEN 10\r
-\r
-typedef enum {\r
- /* Create KV storage in flash memory */\r
- FLASH_TASK_KV_PAGE_INIT = 0,\r
- /* A new or modification to an existing key-value */\r
- FLASH_TASK_KV_DATA,\r
- /* Modify the attribute on an existing key-value */\r
- FLASH_TASK_KV_ATTRIBUTE,\r
- /* Erase a key-value */\r
- FLASH_TASK_KV_ERASE,\r
- /* Write / overwrite some FOTA patch data in FLASH */\r
- FLASH_TASK_FOTA_DATA,\r
- /* Erase some FOTA patch data in FLASH and re-write the FOTA header */\r
- FLASH_TASK_FOTA_ERASE,\r
- /* Write some data to ACPU storage in FLASH */\r
- FLASH_TASK_ACPU_DATA,\r
- /* Erase some ACPU storage in FLASH */\r
- FLASH_TASK_ACPU_ERASE,\r
- /* Number of task types */\r
- FLASH_TASK_NUM_TYPES,\r
-} flash_task_type_t;\r
-\r
-\r
-/** @brief Key type */\r
-typedef uint16_t kv_storage_key;\r
-\r
-typedef struct {\r
- uint32_t kv_addr;\r
- uint32_t kv_size;\r
-} flash_task_kv_init_t;\r
-\r
-typedef struct {\r
- const uint8_t *kvalue;\r
- kv_storage_key key;\r
- uint16_t kvalue_length;\r
- uint8_t attribute;\r
- bool force_write;\r
-} flash_task_kv_data_t;\r
-\r
-typedef struct {\r
- kv_storage_key key;\r
- uint8_t attribute;\r
-} flash_task_kv_attribute_t;\r
-\r
-typedef struct {\r
- kv_storage_key key;\r
-} flash_task_kv_erase_t;\r
-\r
-typedef struct {\r
- kv_storage_key key;\r
-} flash_task_kv_backup_t;\r
-\r
-typedef struct {\r
- /* Used to ensure erase all is not called by mistake. */\r
- uint8_t magic_value[FLASH_TASK_ERASE_ALL_MAGIC_VALUE_LEN];\r
-} kv_erase_all_t;\r
-\r
-typedef struct {\r
- uint8_t *src; /* Mapped address in calling core memory map, can in RAM or FLASH. */\r
- uint8_t *dst; /* Mapped address in calling core memory map FOTA FLASH region\r
- * - must not include the FOTA header. */\r
- uint16_t length;\r
-} flash_task_flash_data_t;\r
-\r
-typedef struct {\r
- uint32_t start_address; /* Core mapped address in FOTA FLASH region; must be at the start of a flash page. */\r
- uint32_t length; /* Must be a multiple of the page size. */\r
-} flash_task_flash_erase_t;\r
-\r
-typedef union {\r
- flash_task_kv_init_t kv_region;\r
- flash_task_kv_data_t kv;\r
- flash_task_kv_attribute_t kv_attribute;\r
- flash_task_kv_erase_t kv_erase;\r
- flash_task_flash_data_t flash;\r
- flash_task_flash_erase_t flash_erase;\r
-} task_node_data_t;\r
-\r
-typedef struct owner_callback_data_t owner_callback;\r
-\r
-/**\r
- * @brief Flash task local callback from the flash_task_process_completed_task(...) function.\r
- *\r
- * @param result is the result of the flash task execution on the SCPU.\r
- * @param callback is the callback struct containing any information that was prepared for use during the callback.\r
- * @return soc_errno value SOC_ERR_SUCCESS or another error code indicating that the callback has failed; this\r
- * will be returned by the flash_task_process_completed_task(...) function. */\r
-typedef errcode_t (*flash_task_completed_callback)(errcode_t result, owner_callback *callback);\r
-\r
-struct owner_callback_data_t {\r
- /** Callback function to call if not EXT_NULL. */\r
- flash_task_completed_callback func;\r
- /* Include any owner data to be available to the callback function upon execution. */\r
-};\r
-\r
-typedef struct flash_task_description_t flash_task_node;\r
-struct flash_task_description_t {\r
- /** A flash_task identifier to maximise the chance that a bug is detected quickly. */\r
- uint32_t identifier;\r
- /** Next task in the queue or EXT_NULL if nothing. */\r
- volatile flash_task_node *next;\r
- /** FOR THE TASK PRODUCER: This is private to the core that owns the cache and will not be examined by the SCPU task\r
- * handlers. It will nominally be used by the owning core as the call-back it should call when it discovers that\r
- * this task has been completed combined with a pointer to a struct with any state information.\r
- *\r
- * NB: IN THE SCPU FLASH TASK HANDLER DURING EXECUTION ONLY: this will be nulled out in the copy initially provided\r
- * to the SCPU task handler and can be used to store task state while the task is in progress, even if the task\r
- * must be temporarily suspended due to an A/PCPU wake-up. */\r
- owner_callback *owner_callback;\r
- /** The state_code is a soc_errno and is used as a lock so is volatile so that it can be read/written from each core\r
- * properly.\r
- * FLASH_TASK_DISCARD - Set by the owning core so that the SCPU will stop checking this memory\r
- * (and will acknowledge this by setting this to DISCARDED).\r
- * FLASH_TASK_DISCARDED - Set by the SCPU to acknowledge that this task has not been processed and\r
- * was discarded by request of the owning core.\r
- * (the SCPU is no longer examining this memory which can be modified/freed).\r
- * FLASH_TASK_READY - Set by the owning core so that the SCPU will process this task\r
- * (and will update it when finished) so the owning core cannot change\r
- * anything other than the owner_callback once this has been set.\r
- * FLASH_TASK_BEING_PROCESSED - Set by the SCPU to indicate that this task is being processed;\r
- * this may take some time if the SCPU has to wait for other cores to sleep.\r
- * The owning core must not change anything other than the owner_callback.\r
- * FLASH_TASK_COMPLETED - Set by the SCPU to indicate that this task completed successfully\r
- * (the SCPU is no longer examining this memory which can be modified/freed).\r
- * any other value - Provides an error code; contact customer support to obtain it's meaning\r
- * (the SCPU is no longer examining this memory which can be modified/freed).\r
- */\r
- volatile uint32_t state_code;\r
- /** The flash task to be performed. */\r
- flash_task_type_t task;\r
- /** The unique per task data contents. */\r
- task_node_data_t data;\r
- /** A checksum formed by xoring the flash write task information (task and data) and is used to spot errors. */\r
- uint32_t checksum;\r
-};\r
-\r
-#ifdef __cplusplus\r
-#if __cplusplus\r
-}\r
-#endif /* __cplusplus */\r
-#endif /* __cplusplus */\r
-\r
-#endif /* _EXTERNAL_ETYPES_5GNBRBIV100_SHARED_FLASH_TASK_ETYPES_H */\r
-\r
+/*
+ * Copyright (c) @CompanyNameMagicTag 2021-2022. All rights reserved.
+ * Description: FLASH task shared definitions between the SCPU and A/PCPU.
+ */
+
+#ifndef FLASH_TASK_ETYPES_H
+#define FLASH_TASK_ETYPES_H
+
+#include "stdbool.h"
+#include "errcode.h"
+
+#ifdef __cplusplus
+#if __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+#endif /* __cplusplus */
+
+/** Identifier to check that a section of memory is indeed a flash_task_node */
+#define FLASH_TASK_NODE_IDENTIFIER 0xd3a71f85
+
+/* Normal values of the flash_task_node.result field; anything else is an error being reported - must remain in sync
+ * with the SCPU soc_errno. The A/PCPU can use these defines in case they have a different set of error codes */
+#define FLASH_TASK_DISCARD 0x80002040
+#define FLASH_TASK_DISCARDED 0x80002041
+#define FLASH_TASK_READY 0x80002042
+#define FLASH_TASK_BEING_PROCESSED 0x80002043
+#define FLASH_TASK_COMPLETED 0x80003130
+
+#define FLASH_TASK_ERASE_ALL_MAGIC_VALUE_LEN 10
+
+typedef enum {
+ /* Create KV storage in flash memory */
+ FLASH_TASK_KV_PAGE_INIT = 0,
+ /* A new or modification to an existing key-value */
+ FLASH_TASK_KV_DATA,
+ /* Modify the attribute on an existing key-value */
+ FLASH_TASK_KV_ATTRIBUTE,
+ /* Erase a key-value */
+ FLASH_TASK_KV_ERASE,
+ /* Write / overwrite some FOTA patch data in FLASH */
+ FLASH_TASK_FOTA_DATA,
+ /* Erase some FOTA patch data in FLASH and re-write the FOTA header */
+ FLASH_TASK_FOTA_ERASE,
+ /* Write some data to ACPU storage in FLASH */
+ FLASH_TASK_ACPU_DATA,
+ /* Erase some ACPU storage in FLASH */
+ FLASH_TASK_ACPU_ERASE,
+ /* Number of task types */
+ FLASH_TASK_NUM_TYPES,
+} flash_task_type_t;
+
+
+/** @brief Key type */
+typedef uint16_t kv_storage_key;
+
+typedef struct {
+ uint32_t kv_addr;
+ uint32_t kv_size;
+} flash_task_kv_init_t;
+
+typedef struct {
+ const uint8_t *kvalue;
+ kv_storage_key key;
+ uint16_t kvalue_length;
+ uint8_t attribute;
+ bool force_write;
+} flash_task_kv_data_t;
+
+typedef struct {
+ kv_storage_key key;
+ uint8_t attribute;
+} flash_task_kv_attribute_t;
+
+typedef struct {
+ kv_storage_key key;
+} flash_task_kv_erase_t;
+
+typedef struct {
+ kv_storage_key key;
+} flash_task_kv_backup_t;
+
+typedef struct {
+ /* Used to ensure erase all is not called by mistake. */
+ uint8_t magic_value[FLASH_TASK_ERASE_ALL_MAGIC_VALUE_LEN];
+} kv_erase_all_t;
+
+typedef struct {
+ uint8_t *src; /* Mapped address in calling core memory map, can in RAM or FLASH. */
+ uint8_t *dst; /* Mapped address in calling core memory map FOTA FLASH region
+ * - must not include the FOTA header. */
+ uint16_t length;
+} flash_task_flash_data_t;
+
+typedef struct {
+ uint32_t start_address; /* Core mapped address in FOTA FLASH region; must be at the start of a flash page. */
+ uint32_t length; /* Must be a multiple of the page size. */
+} flash_task_flash_erase_t;
+
+typedef union {
+ flash_task_kv_init_t kv_region;
+ flash_task_kv_data_t kv;
+ flash_task_kv_attribute_t kv_attribute;
+ flash_task_kv_erase_t kv_erase;
+ flash_task_flash_data_t flash;
+ flash_task_flash_erase_t flash_erase;
+} task_node_data_t;
+
+typedef struct owner_callback_data_t owner_callback;
+
+/**
+ * @brief Flash task local callback from the flash_task_process_completed_task(...) function.
+ *
+ * @param result is the result of the flash task execution on the SCPU.
+ * @param callback is the callback struct containing any information that was prepared for use during the callback.
+ * @return soc_errno value SOC_ERR_SUCCESS or another error code indicating that the callback has failed; this
+ * will be returned by the flash_task_process_completed_task(...) function. */
+typedef errcode_t (*flash_task_completed_callback)(errcode_t result, owner_callback *callback);
+
+struct owner_callback_data_t {
+ /** Callback function to call if not EXT_NULL. */
+ flash_task_completed_callback func;
+ /* Include any owner data to be available to the callback function upon execution. */
+};
+
+typedef struct flash_task_description_t flash_task_node;
+struct flash_task_description_t {
+ /** A flash_task identifier to maximise the chance that a bug is detected quickly. */
+ uint32_t identifier;
+ /** Next task in the queue or EXT_NULL if nothing. */
+ volatile flash_task_node *next;
+ /** FOR THE TASK PRODUCER: This is private to the core that owns the cache and will not be examined by the SCPU task
+ * handlers. It will nominally be used by the owning core as the call-back it should call when it discovers that
+ * this task has been completed combined with a pointer to a struct with any state information.
+ *
+ * NB: IN THE SCPU FLASH TASK HANDLER DURING EXECUTION ONLY: this will be nulled out in the copy initially provided
+ * to the SCPU task handler and can be used to store task state while the task is in progress, even if the task
+ * must be temporarily suspended due to an A/PCPU wake-up. */
+ owner_callback *owner_callback;
+ /** The state_code is a soc_errno and is used as a lock so is volatile so that it can be read/written from each core
+ * properly.
+ * FLASH_TASK_DISCARD - Set by the owning core so that the SCPU will stop checking this memory
+ * (and will acknowledge this by setting this to DISCARDED).
+ * FLASH_TASK_DISCARDED - Set by the SCPU to acknowledge that this task has not been processed and
+ * was discarded by request of the owning core.
+ * (the SCPU is no longer examining this memory which can be modified/freed).
+ * FLASH_TASK_READY - Set by the owning core so that the SCPU will process this task
+ * (and will update it when finished) so the owning core cannot change
+ * anything other than the owner_callback once this has been set.
+ * FLASH_TASK_BEING_PROCESSED - Set by the SCPU to indicate that this task is being processed;
+ * this may take some time if the SCPU has to wait for other cores to sleep.
+ * The owning core must not change anything other than the owner_callback.
+ * FLASH_TASK_COMPLETED - Set by the SCPU to indicate that this task completed successfully
+ * (the SCPU is no longer examining this memory which can be modified/freed).
+ * any other value - Provides an error code; contact customer support to obtain it's meaning
+ * (the SCPU is no longer examining this memory which can be modified/freed).
+ */
+ volatile uint32_t state_code;
+ /** The flash task to be performed. */
+ flash_task_type_t task;
+ /** The unique per task data contents. */
+ task_node_data_t data;
+ /** A checksum formed by xoring the flash write task information (task and data) and is used to spot errors. */
+ uint32_t checksum;
+};
+
+#ifdef __cplusplus
+#if __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* __cplusplus */
+
+#endif /* _EXTERNAL_ETYPES_5GNBRBIV100_SHARED_FLASH_TASK_ETYPES_H */
+
-/*\r
- * Copyright (c) @CompanyNameMagicTag 2019-2022. All rights reserved.\r
- * Description: KV Storage Library flash updating module implementation\r
- */\r
-\r
-#include "nv_update.h"\r
-#include "nv_store.h"\r
-#include "nv_page.h"\r
-#include "nv_key.h"\r
-#include "nv_porting.h"\r
-#include "nv_config.h"\r
-#include "nv_reset.h"\r
-#include "nv_nvregion.h"\r
-#if defined(CONFIG_PARTITION_FEATURE_SUPPORT)\r
-#include "partition.h"\r
-#endif\r
-#include "assert.h"\r
-#include "common_def.h"\r
-#include "uapi_crc.h"\r
-#ifndef CONFIG_NV_SUPPORT_SINGLE_CORE_SYSTEM\r
-#include "flash_task_mutex.h"\r
-#include "flash_task_adapt.h"\r
-#endif\r
-\r
-#define KV_WRITE_KEY_BUFFER_SIZE NV_KEY_DATA_CHUNK_LEN\r
-#define KV_WRITE_CHUNK_SIZE 32\r
-\r
-#define assert__(x) ((void)0)\r
-\r
-/* Pointer to current state machine being processed.\r
- * Will point to the last instance in a list of kv_active_state_machine_t, or NULL if\r
- * no state machine is being processed */\r
-STATIC kv_active_state_machine_t *g_current_state_machine = NULL;\r
-\r
-/* Holds a search filter being used by searching operations performed by state machines */\r
-STATIC kv_key_filter_t g_search_filter;\r
-\r
-/* Holds details of key data to be written to a store */\r
-STATIC kv_key_details_t g_new_key_details;\r
-\r
-/* The key within a KV store begin accessed by a state machine */\r
-STATIC kv_key_handle_t g_current_key;\r
-\r
-/* The flash page within a KV store begin accessed by a state machine */\r
-STATIC kv_page_handle_t g_current_page;\r
-\r
-/* The KV store begin accessed by a state machine */\r
-STATIC kv_store_t g_current_store;\r
-\r
-/* The position in a flash page where a new key will be written */\r
-STATIC kv_key_location g_current_store_write_location;\r
-\r
-/* Memory address of flash page being used to defrag an existing page into */\r
-STATIC kv_page_location g_defrag_page_location = NULL;\r
-\r
-\r
-STATIC errcode_t kv_create_write_buffer(uint32_t write_location, uint32_t size);\r
-STATIC void kv_remove_write_buffer(void);\r
-STATIC kv_managed_source_buffer_t* kv_update_source_buffer(uint8_t *data, uint32_t data_length);\r
-STATIC void kv_remove_source_buffer(void);\r
-STATIC void kv_release_crypto(void);\r
-STATIC errcode_t begin_state_machine(const kv_state_machine_t *machine);\r
-STATIC void end_state_machine(void);\r
-\r
-/* State machine action functions */\r
-STATIC kv_update_event_t kv_update_action_store_write_buffer(void);\r
-STATIC kv_update_event_t kv_update_action_prime_write_buffer(void);\r
-\r
-STATIC kv_update_event_t kv_update_action_select_first_page(void);\r
-STATIC kv_update_event_t kv_update_action_select_next_page(void);\r
-STATIC kv_update_event_t kv_update_action_find_first_key(void);\r
-STATIC kv_update_event_t kv_update_action_find_next_key(void);\r
-STATIC kv_update_event_t kv_update_action_prepare_delete_key(void);\r
-\r
-STATIC kv_update_event_t kv_update_action_find_defrag_page(void);\r
-STATIC kv_update_event_t kv_update_action_erase_defrag_page(void);\r
-STATIC kv_update_event_t kv_update_action_prepare_copy_key(void);\r
-\r
-STATIC kv_update_event_t kv_update_action_prepare_defrag_page(void);\r
-STATIC kv_update_event_t kv_update_action_copy_all_keys(void);\r
-STATIC kv_update_event_t kv_update_action_prepare_page_header(void);\r
-STATIC kv_update_event_t kv_update_action_find_write_position(void);\r
-STATIC kv_update_event_t kv_update_action_defrag_current_page(void);\r
-STATIC kv_update_event_t kv_update_action_update_nvregion_map(void);\r
-\r
-STATIC kv_update_event_t kv_update_action_prepare_store(void);\r
-STATIC kv_update_event_t kv_update_action_prepare_write_key(void);\r
-STATIC kv_update_event_t kv_update_action_claim_crypto(void);\r
-STATIC kv_update_event_t kv_update_action_erase_old_keys(void);\r
-\r
-STATIC kv_update_event_t kv_update_action_update_nvmap_for_erase_key(void);\r
-STATIC kv_update_event_t kv_update_action_update_nvmap_for_new_key(void);\r
-\r
-\r
-/* State machine to iterate through keys in a store marking all keys as erased\r
- * - Uses a pre-configured filter to determine which keys to select for erasing\r
- */\r
-static const kv_update_transition_t g_erase_keys_transitions[] = {\r
- {.state = STATE_SELECT_FIRST_PAGE, .event = EVENT_PAGE_SELECTED, .next_state = STATE_FIND_FIRST_KEY },\r
- {.state = STATE_SELECT_FIRST_PAGE, .event = EVENT_PAGE_NOT_SELECTED, .next_state = STATE_EXIT },\r
- {.state = STATE_SELECT_NEXT_PAGE, .event = EVENT_PAGE_SELECTED, .next_state = STATE_FIND_FIRST_KEY },\r
- {.state = STATE_SELECT_NEXT_PAGE, .event = EVENT_PAGE_NOT_SELECTED, .next_state = STATE_EXIT },\r
- {.state = STATE_FIND_FIRST_KEY, .event = EVENT_KEY_FOUND, .next_state = STATE_PREP_DELETE_KEY },\r
- {.state = STATE_FIND_FIRST_KEY, .event = EVENT_KEY_NOT_FOUND, .next_state = STATE_SELECT_NEXT_PAGE},\r
- {.state = STATE_FIND_NEXT_KEY, .event = EVENT_KEY_FOUND, .next_state = STATE_PREP_DELETE_KEY },\r
- {.state = STATE_FIND_NEXT_KEY, .event = EVENT_KEY_NOT_FOUND, .next_state = STATE_SELECT_NEXT_PAGE},\r
- {.state = STATE_PREP_DELETE_KEY, .event = EVENT_WRITE_BUFFER_PRIMED, .next_state = STATE_PERFORM_WRITE },\r
- {.state = STATE_PERFORM_WRITE, .event = EVENT_WRITE_BUFFER_STORED, .next_state = STATE_UPDATE_MAP_FOR_OLD },\r
- {.state = STATE_UPDATE_MAP_FOR_OLD, .event = EVENT_PAGE_MAP_UPDATED, .next_state = STATE_FIND_NEXT_KEY },\r
- {.state = STATE_PERFORM_WRITE, .event = EVENT_SUSPEND, .next_state = STATE_SUSPENDED },\r
- {.state = STATE_EXIT, .event = EVENT_NONE, .next_state = STATE_EXIT }\r
-};\r
-static const kv_update_action_t g_erase_keys_actions[] = {\r
- {.state = STATE_SELECT_FIRST_PAGE, .action = kv_update_action_select_first_page },\r
- {.state = STATE_SELECT_NEXT_PAGE, .action = kv_update_action_select_next_page },\r
- {.state = STATE_FIND_FIRST_KEY, .action = kv_update_action_find_first_key },\r
- {.state = STATE_FIND_NEXT_KEY, .action = kv_update_action_find_next_key },\r
- {.state = STATE_PREP_DELETE_KEY, .action = kv_update_action_prepare_delete_key},\r
- {.state = STATE_PERFORM_WRITE, .action = kv_update_action_store_write_buffer},\r
- {.state = STATE_UPDATE_MAP_FOR_OLD, .action = kv_update_action_update_nvmap_for_erase_key},\r
- {.state = STATE_EXIT, .action = NULL }\r
-};\r
-static const kv_state_machine_t g_erase_keys_machine = {\r
- .initial_state = STATE_SELECT_FIRST_PAGE,\r
- .resume_state = STATE_PERFORM_WRITE,\r
- .exit_event = EVENT_KEYS_ERASED,\r
- .transition_table = g_erase_keys_transitions,\r
- .action_table = g_erase_keys_actions,\r
- .write_buffer_size = sizeof(uint32_t)\r
-};\r
-\r
-/*\r
- * State machine to locate a defrag page\r
- * - Scans NV Region for an unused page of flash\r
- * - Sets g_defrag_page_location to point the unused flash page, thus declaring it to be the current defrag page\r
- * - Ensures defrag page is erased\r
- */\r
-static const kv_update_transition_t g_prep_defrag_page_transitions[] = {\r
- {.state = STATE_FIND_DEFRAG, .event = EVENT_DEFRAG_FOUND, .next_state = STATE_ERASE_DEFRAG},\r
- {.state = STATE_ERASE_DEFRAG, .event = EVENT_DEFRAG_ERASED, .next_state = STATE_EXIT },\r
- {.state = STATE_ERASE_DEFRAG, .event = EVENT_SUSPEND, .next_state = STATE_SUSPENDED },\r
- {.state = STATE_EXIT, .event = EVENT_NONE, .next_state = STATE_EXIT }\r
-};\r
-static const kv_update_action_t g_prep_defrag_page_actions[] = {\r
- {.state = STATE_FIND_DEFRAG, .action = kv_update_action_find_defrag_page },\r
- {.state = STATE_ERASE_DEFRAG, .action = kv_update_action_erase_defrag_page},\r
- {.state = STATE_EXIT, .action = NULL }\r
-};\r
-static const kv_state_machine_t g_prep_defrag_page_machine = {\r
- .initial_state = STATE_FIND_DEFRAG,\r
- .resume_state = STATE_ERASE_DEFRAG,\r
- .exit_event = EVENT_DEFRAG_PREPARED,\r
- .transition_table = g_prep_defrag_page_transitions,\r
- .action_table = g_prep_defrag_page_actions,\r
- .write_buffer_size = 0\r
-};\r
-\r
-/*\r
- * State machine to copy keys from the current page to the defrag page.\r
- * Keys to copy are selected by a pre-configured filter, contained in g_search_filter\r
- * Current page identified by g_current_page\r
- * Defrag page identified by g_defrag_page_location\r
- */\r
-static const kv_update_transition_t g_copy_all_keys_transitions[] = {\r
- {.state = STATE_FIND_FIRST_KEY, .event = EVENT_KEY_FOUND, .next_state = STATE_PREP_COPY_KEY },\r
- {.state = STATE_FIND_FIRST_KEY, .event = EVENT_KEY_NOT_FOUND, .next_state = STATE_EXIT },\r
- {.state = STATE_FIND_NEXT_KEY, .event = EVENT_KEY_FOUND, .next_state = STATE_PREP_COPY_KEY },\r
- {.state = STATE_FIND_NEXT_KEY, .event = EVENT_KEY_NOT_FOUND, .next_state = STATE_EXIT },\r
- {.state = STATE_PREP_COPY_KEY, .event = EVENT_COPY_KEY_READY, .next_state = STATE_PRIME_WRITE },\r
- {.state = STATE_PRIME_WRITE, .event = EVENT_WRITE_BUFFER_PRIMED, .next_state = STATE_PERFORM_WRITE },\r
- {.state = STATE_PRIME_WRITE, .event = EVENT_WRITE_DATA_EXHAUSTED, .next_state = STATE_FIND_NEXT_KEY },\r
- {.state = STATE_PERFORM_WRITE, .event = EVENT_WRITE_BUFFER_STORED, .next_state = STATE_PRIME_WRITE },\r
- {.state = STATE_PERFORM_WRITE, .event = EVENT_SUSPEND, .next_state = STATE_SUSPENDED },\r
- {.state = STATE_EXIT, .event = EVENT_NONE, .next_state = STATE_EXIT }\r
-};\r
-static const kv_update_action_t g_copy_all_keys_actions[] = {\r
- {.state = STATE_FIND_FIRST_KEY, .action = kv_update_action_find_first_key },\r
- {.state = STATE_FIND_NEXT_KEY, .action = kv_update_action_find_next_key },\r
- {.state = STATE_PREP_COPY_KEY, .action = kv_update_action_prepare_copy_key },\r
- {.state = STATE_PRIME_WRITE, .action = kv_update_action_prime_write_buffer},\r
- {.state = STATE_PERFORM_WRITE, .action = kv_update_action_store_write_buffer},\r
- {.state = STATE_EXIT, .action = NULL }\r
-};\r
-static const kv_state_machine_t g_copy_all_keys_machine = {\r
- .initial_state = STATE_FIND_FIRST_KEY,\r
- .resume_state = STATE_PERFORM_WRITE,\r
- .exit_event = EVENT_ALL_KEYS_COPIED,\r
- .transition_table = g_copy_all_keys_transitions,\r
- .action_table = g_copy_all_keys_actions,\r
- .write_buffer_size = KV_WRITE_KEY_BUFFER_SIZE\r
-};\r
-\r
-/*\r
- * State machine to defrag a store page\r
- * Makes use of the g_prep_defrag_page_machine state machine\r
- * Makes use of the g_copy_all_keys_machine state machine\r
- * - Locates an unused flash page in the NV Region and erased it\r
- * - Copies valid keys from page being defragged to the new page\r
- * - Writes an updated page header to top of the new page, so that it supercedes the recently defragged page\r
- */\r
-static const kv_update_transition_t g_defrag_page_transitions[] = {\r
- {.state = STATE_PREP_DEFRAG, .event = EVENT_DEFRAG_PREPARED, .next_state = STATE_COPY_ALL_KEYS },\r
- {.state = STATE_COPY_ALL_KEYS, .event = EVENT_ALL_KEYS_COPIED, .next_state = STATE_PREP_PAGE_HEADER },\r
- {.state = STATE_PREP_PAGE_HEADER, .event = EVENT_WRITE_BUFFER_PRIMED, .next_state = STATE_WRITE_PAGE_HEADER },\r
- {.state = STATE_WRITE_PAGE_HEADER, .event = EVENT_WRITE_BUFFER_STORED, .next_state = STATE_UPDATE_NVREGION_MAP},\r
- {.state = STATE_WRITE_PAGE_HEADER, .event = EVENT_SUSPEND, .next_state = STATE_SUSPENDED },\r
- {.state = STATE_UPDATE_NVREGION_MAP, .event = EVENT_NVREGION_MAP_UPDATED, .next_state = STATE_EXIT },\r
- {.state = STATE_EXIT, .event = EVENT_NONE, .next_state = STATE_EXIT }\r
-};\r
-static const kv_update_action_t g_defrag_page_actions[] = {\r
- {.state = STATE_PREP_DEFRAG, .action = kv_update_action_prepare_defrag_page},\r
- {.state = STATE_COPY_ALL_KEYS, .action = kv_update_action_copy_all_keys },\r
- {.state = STATE_PREP_PAGE_HEADER, .action = kv_update_action_prepare_page_header},\r
- {.state = STATE_WRITE_PAGE_HEADER, .action = kv_update_action_store_write_buffer },\r
- {.state = STATE_UPDATE_NVREGION_MAP, .action = kv_update_action_update_nvregion_map},\r
- {.state = STATE_EXIT, .action = NULL }\r
-};\r
-static const kv_state_machine_t g_defrag_page_machine = {\r
- .initial_state = STATE_PREP_DEFRAG,\r
- .resume_state = STATE_WRITE_PAGE_HEADER,\r
- .exit_event = EVENT_DEFRAG_COMPLETE,\r
- .transition_table = g_defrag_page_transitions,\r
- .action_table = g_defrag_page_actions,\r
- .write_buffer_size = sizeof(kv_page_header_t)\r
-};\r
-\r
-/*\r
- * State machine to prepare a store for writing a key\r
- * May make use of the g_defrag_page_machine state machine\r
- * - Selects a suitable page in a store for receiving a key\r
- * - Kicks off a defrag of the selected page if necessary\r
- */\r
-static const kv_update_transition_t g_prepare_store_transitions[] = {\r
- {.state = STATE_FIND_WRITE_POS, .event = EVENT_DEFRAG_REQUIRED, .next_state = STATE_DEFRAG_PAGE },\r
- {.state = STATE_FIND_WRITE_POS, .event = EVENT_WRITE_POS_FOUND, .next_state = STATE_EXIT },\r
- {.state = STATE_DEFRAG_PAGE, .event = EVENT_DEFRAG_COMPLETE, .next_state = STATE_FIND_WRITE_POS},\r
- {.state = STATE_EXIT, .event = EVENT_NONE, .next_state = STATE_EXIT }\r
-};\r
-static const kv_update_action_t g_prepare_store_actions[] = {\r
- {.state = STATE_FIND_WRITE_POS, .action = kv_update_action_find_write_position},\r
- {.state = STATE_DEFRAG_PAGE, .action = kv_update_action_defrag_current_page},\r
- {.state = STATE_EXIT, .action = NULL }\r
-};\r
-STATIC const kv_state_machine_t g_prepare_store_machine = {\r
- .initial_state = STATE_FIND_WRITE_POS,\r
- .resume_state = STATE_INVALID,\r
- .exit_event = EVENT_STORE_READY,\r
- .transition_table = g_prepare_store_transitions,\r
- .action_table = g_prepare_store_actions,\r
- .write_buffer_size = 0\r
-};\r
-\r
-/*\r
- * State machine to write a key to a store\r
- * Makes use of the g_prepare_store_machine state machine\r
- */\r
-static const kv_update_transition_t g_write_key_transitions[] = {\r
- {.state = STATE_PREP_STORE, .event = EVENT_STORE_READY, .next_state = STATE_PREP_KEY_DATA },\r
- {.state = STATE_PREP_KEY_DATA, .event = EVENT_KEY_UPDATE_NOT_NEEDED, .next_state = STATE_EXIT },\r
- {.state = STATE_PREP_KEY_DATA, .event = EVENT_KEY_DATA_READY, .next_state = STATE_CLAIM_CRYPTO },\r
- {.state = STATE_CLAIM_CRYPTO, .event = EVENT_CRYPTO_CLAIMED, .next_state = STATE_PRIME_WRITE },\r
- {.state = STATE_PRIME_WRITE, .event = EVENT_WRITE_BUFFER_PRIMED, .next_state = STATE_PERFORM_WRITE },\r
- {.state = STATE_PRIME_WRITE, .event = EVENT_WRITE_DATA_EXHAUSTED, .next_state = STATE_UPDATE_MAP_FOR_NEW},\r
- {.state = STATE_UPDATE_MAP_FOR_NEW, .event = EVENT_PAGE_MAP_UPDATED, .next_state = STATE_ERASE_OLD_KEYS },\r
- {.state = STATE_PERFORM_WRITE, .event = EVENT_WRITE_BUFFER_STORED, .next_state = STATE_PRIME_WRITE },\r
- {.state = STATE_PERFORM_WRITE, .event = EVENT_SUSPEND, .next_state = STATE_SUSPENDED },\r
- {.state = STATE_ERASE_OLD_KEYS, .event = EVENT_KEYS_ERASED, .next_state = STATE_EXIT },\r
- {.state = STATE_EXIT, .event = EVENT_NONE, .next_state = STATE_EXIT }\r
-};\r
-static const kv_update_action_t g_write_key_actions[] = {\r
- {.state = STATE_PREP_STORE, .action = kv_update_action_prepare_store },\r
- {.state = STATE_PREP_KEY_DATA, .action = kv_update_action_prepare_write_key },\r
- {.state = STATE_CLAIM_CRYPTO, .action = kv_update_action_claim_crypto },\r
- {.state = STATE_PRIME_WRITE, .action = kv_update_action_prime_write_buffer},\r
- {.state = STATE_PERFORM_WRITE, .action = kv_update_action_store_write_buffer},\r
- {.state = STATE_UPDATE_MAP_FOR_NEW, .action = kv_update_action_update_nvmap_for_new_key},\r
- {.state = STATE_ERASE_OLD_KEYS, .action = kv_update_action_erase_old_keys },\r
- {.state = STATE_EXIT, .action = NULL }\r
-};\r
-static const kv_state_machine_t g_write_key_machine = {\r
- .initial_state = STATE_PREP_STORE,\r
- .resume_state = STATE_CLAIM_CRYPTO,\r
- .exit_event = EVENT_WRITE_COMPLETE,\r
- .transition_table = g_write_key_transitions,\r
- .action_table = g_write_key_actions,\r
- .write_buffer_size = KV_WRITE_KEY_BUFFER_SIZE\r
-};\r
-\r
-/* Attempts to write all data in a write buffer to flash */\r
-STATIC kv_update_event_t kv_update_action_store_write_buffer(void)\r
-{\r
- errcode_t ret;\r
- uint32_t read_location;\r
- uint32_t write_length;\r
- uint32_t written;\r
-\r
- kv_managed_write_buffer_t *write_buffer = g_current_state_machine->write_buffer;\r
-\r
- /* Skip over section of write buffer that has already been written, due to being suspended */\r
- if (write_buffer->write_location < write_buffer->resume_location) {\r
- uint32_t data_already_written = write_buffer->resume_location - write_buffer->write_location;\r
- if (data_already_written > write_buffer->data_length) {\r
- data_already_written = write_buffer->data_length;\r
- }\r
- write_buffer->data_consumed += (uint16_t)data_already_written;\r
- write_buffer->write_location += data_already_written;\r
- }\r
-\r
- /* Attempt to write chunks of data from the write_buffer to flash */\r
- while (write_buffer->data_consumed < write_buffer->data_length) {\r
- /* Determine size and position of chunk to write */\r
- read_location = (uint32_t)(uintptr_t)write_buffer->data + write_buffer->data_consumed;\r
- write_length = uapi_min(KV_WRITE_CHUNK_SIZE, write_buffer->data_length - write_buffer->data_consumed);\r
-\r
- /* Attempt to write chunk */\r
- ret = kv_key_write_flash(write_buffer->write_location, write_length, (uint8_t *)(uintptr_t)read_location);\r
- if (ret == ERRCODE_SUCC) {\r
- written = write_length;\r
- } else {\r
- written = 0;\r
- }\r
- write_buffer->data_consumed += (uint16_t)written;\r
- /* Update write position, automatically allowing for subsequent writes to occur sequentially in flash */\r
- write_buffer->write_location += written;\r
- if (written < write_length) {\r
- /* Write was aborted early */\r
- write_buffer->resume_location = write_buffer->write_location;\r
- return EVENT_SUSPEND;\r
- }\r
- }\r
- /* Write completed */\r
- return EVENT_WRITE_BUFFER_STORED;\r
-}\r
-STATIC errcode_t kv_update_hash_encrypt_chunk(kv_managed_write_buffer_t *write_buffer,\r
- const kv_managed_source_buffer_t *source_buffer, uint32_t chunk_dest, uint32_t chunk_len)\r
-{\r
- /* Need to cal CRC? */\r
- errcode_t ret = ERRCODE_SUCC;\r
- if (source_buffer->crc_data) {\r
- if (!write_buffer->crc_claimed) {\r
- return ERRCODE_NV_HASH_UNAVAILABLE;\r
- }\r
- write_buffer->crc_ret = uapi_crc32(write_buffer->crc_ret, (const uint8_t *)(uintptr_t)chunk_dest,\r
- chunk_len);\r
- }\r
-\r
-#if (CONFIG_NV_SUPPORT_ENCRYPT == NV_YES)\r
- /* Need to hash data? */\r
- if (source_buffer->hash_data) {\r
- if (!write_buffer->hash_claimed) {\r
- return ERRCODE_NV_HASH_UNAVAILABLE;\r
- }\r
- nv_crypto_update_hash((const uint8_t *)(uintptr_t)chunk_dest, chunk_len);\r
- }\r
-\r
- /* Need to encrypt data? */\r
- if (source_buffer->encrypt_data) {\r
- if (!write_buffer->encrypt_claimed) {\r
- return ERRCODE_NV_AES_UNAVAILABLE;\r
- }\r
-\r
- ret = nv_crypto_encode(write_buffer->crypto_handle, (uintptr_t)chunk_dest, (uintptr_t)chunk_dest, chunk_len);\r
- }\r
-\r
- if (source_buffer->gcm_tag_data || source_buffer->hash_data) {\r
- if ((source_buffer->gcm_tag_data && !write_buffer->gcm_tag_claimed) ||\r
- (source_buffer->hash_data && !write_buffer->hash_claimed)) {\r
- return ERRCODE_NV_HASH_UNAVAILABLE;\r
- }\r
- write_buffer->crc_ret = uapi_crc32(write_buffer->crc_ret, (const uint8_t *)(uintptr_t)chunk_dest,\r
- chunk_len);\r
- }\r
-#endif\r
- return ret;\r
-}\r
-\r
-/* Copy data from the selected source buffer to the write_buffer, hashing and encrypting the data as necessary */\r
-STATIC errcode_t prime_next_chunk(void)\r
-{\r
- kv_managed_source_buffer_t *source_buffer = g_current_state_machine->current_source;\r
- kv_managed_write_buffer_t *write_buffer = g_current_state_machine->write_buffer;\r
-\r
- uint32_t remaining_dest_space = write_buffer->size - write_buffer->data_length;\r
- uint32_t remaining_src_data = source_buffer->data_length - source_buffer->data_consumed;\r
- uint16_t chunk_len = (uint16_t)uapi_min(remaining_dest_space, remaining_src_data);\r
-\r
- uint32_t chunk_dest = (uint32_t)(uintptr_t)write_buffer->data + write_buffer->data_length;\r
- uint32_t chunk_src = (uint32_t)(uintptr_t)source_buffer->data + source_buffer->data_consumed;\r
-\r
- if (source_buffer->encrypt_data) {\r
- /* For encrypted data, the length of each operation is the same as that of NV reading.\r
- * CNCommont: 对于加密数据,每次操作的数据长度与读NV时保持一致(KEY_DATA_CHUNK_LEN),因此当write_buffer剩余空间\r
- * 小于KEY_DATA_CHUNK_LEN时,暂不处理.\r
- */\r
- chunk_len = (uint16_t)uapi_min(chunk_len, NV_KEY_DATA_CHUNK_LEN);\r
- if (remaining_dest_space < NV_KEY_DATA_CHUNK_LEN) {\r
- return ERRCODE_NV_BUFFER_PRIMED_PREMATURELY;\r
- }\r
- }\r
-\r
- kv_attributes_t attributes = (source_buffer->encrypt_data) ? KV_ATTRIBUTE_ENCRYPTED : 0;\r
- if (remaining_dest_space < kv_key_padded_data_length(attributes, chunk_len)) {\r
- /* Not enough space for padded data, exit early and write what we have */\r
- return ERRCODE_NV_BUFFER_PRIMED_PREMATURELY;\r
- }\r
-\r
- /* Copy a chunk of source_buffer data to write buffer */\r
- if ((chunk_src >= FLASH_PHYSICAL_ADDR_START) &&\r
- (chunk_src + chunk_len) <= FLASH_PHYSICAL_ADDR_END) {\r
- /* Source buffer is referencing data in Flash */\r
- errcode_t res = kv_key_helper_copy_flash(chunk_dest, chunk_src, chunk_len);\r
- if (res != ERRCODE_SUCC) {\r
- return res;\r
- }\r
- } else {\r
- int32_t result;\r
- /* Source buffer is referencing data in RAM */\r
- result = memcpy_s((void *)(uintptr_t)chunk_dest, remaining_dest_space,\r
- (const void *)(uintptr_t)chunk_src, chunk_len);\r
- if (result != (int32_t)EOK) {\r
- assert__(false);\r
- }\r
- }\r
-\r
- /* Round chunk_len up to a multiple of 4 or 16 bytes, depending upon the encryption requirement */\r
- /* We have already checked there is enough space in the write buffer */\r
- chunk_len = kv_key_padded_data_length(attributes, chunk_len);\r
-\r
- errcode_t hash_crypt_ret;\r
- hash_crypt_ret = kv_update_hash_encrypt_chunk(write_buffer, source_buffer, chunk_dest, chunk_len);\r
- if (hash_crypt_ret != ERRCODE_SUCC) {\r
- return hash_crypt_ret;\r
- }\r
-\r
- write_buffer->data_length += chunk_len;\r
- source_buffer->data_consumed += chunk_len;\r
- return ERRCODE_SUCC;\r
-}\r
-\r
-/* Select next source buffer, filling it with a hash if required */\r
-STATIC errcode_t select_next_source_buffer(void)\r
-{\r
- kv_managed_source_buffer_t *source_buffer = g_current_state_machine->current_source->next;\r
- kv_managed_write_buffer_t *write_buffer = g_current_state_machine->write_buffer;\r
-\r
- g_current_state_machine->current_source = source_buffer;\r
-\r
- /* Has the next source_buffer buffer been designated to receive the hash calculation */\r
- if ((source_buffer != NULL) && (source_buffer->receive_hash)) {\r
- if (write_buffer->crc_claimed) {\r
- if (source_buffer->data_length < KV_CRYPTO_CRC_SIZE) {\r
- return ERRCODE_NV_KEY_HASH_BUFFER_TOO_SMALL;\r
- }\r
-\r
- write_buffer->crc_ret = kv_crc32_swap(write_buffer->crc_ret);\r
- (void)memcpy_s((void *)source_buffer->data, KV_CRYPTO_CRC_SIZE, (const void *)&write_buffer->crc_ret,\r
- KV_CRYPTO_CRC_SIZE);\r
- write_buffer->crc_claimed = false;\r
- source_buffer->data_length = KV_CRYPTO_CRC_SIZE;\r
- }\r
-#if (CONFIG_NV_SUPPORT_ENCRYPT == NV_YES)\r
- else if (write_buffer->hash_claimed) {\r
- if (source_buffer->data_length < KV_CRYPTO_HASH_SIZE) {\r
- return ERRCODE_NV_KEY_HASH_BUFFER_TOO_SMALL;\r
- }\r
- (void)memset_s(source_buffer->data, source_buffer->data_length, 0, source_buffer->data_length);\r
- nv_crypto_complete_hash(source_buffer->data);\r
-\r
- write_buffer->crc_ret = kv_crc32_swap(write_buffer->crc_ret);\r
- (void)memcpy_s((void *)(source_buffer->data + KV_CRYPTO_HASH_SIZE - KV_CRYPTO_CRC_SIZE),\r
- KV_CRYPTO_CRC_SIZE, (const void *)&write_buffer->crc_ret, KV_CRYPTO_CRC_SIZE);\r
-\r
- write_buffer->hash_claimed = false;\r
- source_buffer->data_length = KV_CRYPTO_HASH_SIZE;\r
- } else if (write_buffer->gcm_tag_claimed) {\r
- uint32_t tag_len = NV_AES_GCM_TAG_LENGTH;\r
- if (source_buffer->data_length < KV_CRYPTO_HASH_SIZE) {\r
- return ERRCODE_NV_KEY_HASH_BUFFER_TOO_SMALL;\r
- }\r
-\r
- write_buffer->crc_ret = kv_crc32_swap(write_buffer->crc_ret);\r
- memset_s(source_buffer->data, source_buffer->data_length, 0, source_buffer->data_length);\r
-\r
- nv_crypto_get_tag(write_buffer->crypto_handle, source_buffer->data, &tag_len);\r
-\r
- (void)memcpy_s((void *)(source_buffer->data + KV_CRYPTO_HASH_SIZE - KV_CRYPTO_CRC_SIZE),\r
- KV_CRYPTO_CRC_SIZE, (const void *)&write_buffer->crc_ret, KV_CRYPTO_CRC_SIZE);\r
-\r
- write_buffer->gcm_tag_claimed = false;\r
- source_buffer->data_length = KV_CRYPTO_HASH_SIZE;\r
- }\r
-#endif\r
- else {\r
- return ERRCODE_NV_HASH_UNAVAILABLE;\r
- }\r
- }\r
-\r
- return ERRCODE_SUCC;\r
-}\r
-\r
-/* Gather data from one or more source buffers to fill write_buffer with more data ready for writing to flash */\r
-STATIC kv_update_event_t kv_update_action_prime_write_buffer(void)\r
-{\r
- errcode_t res;\r
-\r
- kv_managed_source_buffer_t *source_buffer = g_current_state_machine->current_source;\r
- kv_managed_write_buffer_t *write_buffer = g_current_state_machine->write_buffer;\r
-\r
- (void)memset_s(write_buffer->data, write_buffer->size, 0, write_buffer->size);\r
-\r
- write_buffer->data_length = 0;\r
- write_buffer->data_consumed = 0;\r
-\r
- while ((source_buffer != NULL) && (write_buffer->data_length < write_buffer->size)) {\r
- if (source_buffer->data_consumed < source_buffer->data_length) {\r
- res = prime_next_chunk();\r
- if (res == ERRCODE_NV_BUFFER_PRIMED_PREMATURELY) {\r
- /* Not enough space for padded data so just write what we have for now */\r
- break;\r
- }\r
- if (res != ERRCODE_SUCC) {\r
- g_current_state_machine->error_code = res;\r
- return EVENT_ERROR;\r
- }\r
- } else {\r
- /* Select next source buffer */\r
- res = select_next_source_buffer();\r
- if (res != ERRCODE_SUCC) {\r
- g_current_state_machine->error_code = res;\r
- return EVENT_ERROR;\r
- }\r
- source_buffer = g_current_state_machine->current_source;\r
- }\r
- }\r
-\r
- /* Did we manage to place any data in the write buffer? */\r
- if (write_buffer->data_length == 0) {\r
- kv_release_crypto();\r
- return EVENT_WRITE_DATA_EXHAUSTED;\r
- } else {\r
- return EVENT_WRITE_BUFFER_PRIMED;\r
- }\r
-}\r
-\r
-/*\r
- * Attempts to obtain a handle to the first page of the g_current_store\r
- * - g_current_page will be populated if a page is obtained\r
- */\r
-STATIC kv_update_event_t kv_update_action_select_first_page(void)\r
-{\r
- errcode_t res;\r
-\r
- res = kv_store_get_page_handle(g_current_store, 0, &g_current_page);\r
- if (res == ERRCODE_SUCC) {\r
- return EVENT_PAGE_SELECTED;\r
- }\r
- return EVENT_PAGE_NOT_SELECTED;\r
-}\r
-\r
-/*\r
- * Attempts to obtain a handle to the next page of the g_current_store, based on g_current_page\r
- * - g_current_page will be updated if a page is obtained\r
- */\r
-STATIC kv_update_event_t kv_update_action_select_next_page(void)\r
-{\r
- errcode_t res;\r
- uint32_t page_index;\r
- if (kv_page_get_index(&g_current_page, &page_index) != ERRCODE_SUCC) {\r
- return EVENT_PAGE_NOT_SELECTED;\r
- }\r
- page_index++;\r
- if (page_index < kv_store_get_page_count(g_current_store)) {\r
- res = kv_store_get_page_handle(g_current_store, page_index, &g_current_page);\r
- if (res == ERRCODE_SUCC) {\r
- return EVENT_PAGE_SELECTED;\r
- }\r
- }\r
-\r
- return EVENT_PAGE_NOT_SELECTED;\r
-}\r
-\r
-/*\r
- * Attempts to obtain the first key in the g_current_page, that conforms to g_search_filter\r
- * - g_current_key will be populated if a key is obtained\r
- */\r
-STATIC kv_update_event_t kv_update_action_find_first_key(void)\r
-{\r
- errcode_t res;\r
-\r
- res = kv_page_find_first_key(&g_current_page, &g_search_filter, &g_current_key);\r
- while (res == ERRCODE_SUCC) {\r
- if (g_current_key.key_location != g_search_filter.location) {\r
- return EVENT_KEY_FOUND;\r
- }\r
- res = kv_page_find_next_key(&g_current_page, &g_search_filter, &g_current_key);\r
- }\r
- return EVENT_KEY_NOT_FOUND;\r
-}\r
-\r
-/*\r
- * Attempts to obtain the next key in the g_current_page, based on g_current_key and that conforms to g_search_filter\r
- * - g_current_key will be updated if a key is obtained\r
- */\r
-STATIC kv_update_event_t kv_update_action_find_next_key(void)\r
-{\r
- errcode_t res;\r
-\r
- res = kv_page_find_next_key(&g_current_page, &g_search_filter, &g_current_key);\r
- while (res == ERRCODE_SUCC) {\r
- if (g_current_key.key_location != g_search_filter.location) {\r
- return EVENT_KEY_FOUND;\r
- }\r
- res = kv_page_find_next_key(&g_current_page, &g_search_filter, &g_current_key);\r
- }\r
- return EVENT_KEY_NOT_FOUND;\r
-}\r
-\r
-/*\r
- * Prepares the g_current_state_machine->write_buffer to mark g_current_key as invalid (and hence erased)\r
- * Will not mark keys with the attribute KV_ATTRIBUTE_PERMANENT as invalid\r
- */\r
-STATIC kv_update_event_t kv_update_action_prepare_delete_key(void)\r
-{\r
- kv_managed_write_buffer_t *write_buffer = NULL;\r
- kv_attributes_t attributes = kv_key_attributes(&g_current_key);\r
- if (((uint32_t)attributes & (uint32_t)KV_ATTRIBUTE_PERMANENT) != 0 &&\r
- (g_new_key_details.focre_write != true)) {\r
- g_current_state_machine->error_code = ERRCODE_NV_TRYING_TO_MODIFY_A_PERMANENT_KEY;\r
- return EVENT_ERROR;\r
- }\r
-\r
- write_buffer = g_current_state_machine->write_buffer;\r
- if (write_buffer == NULL) {\r
- g_current_state_machine->error_code = ERRCODE_NV_WRITE_BUFFER_NOT_ALLOCATED;\r
- return EVENT_ERROR;\r
- }\r
- if (write_buffer->size < sizeof(uint32_t)) {\r
- g_current_state_machine->error_code = ERRCODE_NV_WRITE_BUFFER_TOO_SMALL;\r
- return EVENT_ERROR;\r
- }\r
-\r
- /* As only 4 bytes are being written, directly prime write buffer rather that setting up a source buffer */\r
- *(uint32_t *)write_buffer->data = KV_KEY_INVALID;\r
- write_buffer->data_consumed = 0;\r
-\r
- if (g_nv_header_magic == KV_KEY_MAGIC) {\r
- write_buffer->data_length = (uint16_t)sizeof(uint8_t);\r
- /* Need to manually configure the write location for each key being deleted */\r
- /* as they will not follow in an ordered sequence */\r
- write_buffer->write_location = (uint32_t)(uintptr_t)((uint8_t *)g_current_key.key_location + sizeof(uint8_t));\r
- } else {\r
- write_buffer->data_length = (uint16_t)sizeof(uint16_t);\r
- write_buffer->write_location = (uint32_t)(uintptr_t)g_current_key.key_location;\r
- }\r
-\r
- return EVENT_WRITE_BUFFER_PRIMED;\r
-}\r
-\r
-STATIC kv_update_event_t kv_update_action_prepare_copy_key(void)\r
-{\r
- kv_managed_write_buffer_t *write_buffer = g_current_state_machine->write_buffer;\r
- if (write_buffer == NULL) {\r
- g_current_state_machine->error_code = ERRCODE_NV_WRITE_BUFFER_NOT_ALLOCATED;\r
- return EVENT_ERROR;\r
- }\r
- if (write_buffer->write_location == 0) {\r
- write_buffer->write_location = (uint32_t)(uintptr_t)g_defrag_page_location + (uint32_t)sizeof(kv_page_header_t);\r
- }\r
-\r
- /* Use a source buffer to manage copy progress of key data */\r
- uint8_t *source = (uint8_t *)g_current_key.key_location;\r
- uint16_t length = kv_key_flash_size(&g_current_key);\r
- kv_managed_source_buffer_t *source_buffer = kv_update_source_buffer(source, length);\r
- g_current_state_machine->current_source = source_buffer; /* Point current source buffer at first buffer */\r
- if (source_buffer == NULL) {\r
- g_current_state_machine->error_code = ERRCODE_MALLOC;\r
- return EVENT_ERROR;\r
- }\r
-\r
- return EVENT_COPY_KEY_READY;\r
-}\r
-\r
-/*\r
- * Attempts to locate an unused page of flash, in the KV NV flash region, to use as the current defrag page\r
- * g_defrag_page_location will be set to point to the unused flash page\r
- */\r
-STATIC kv_update_event_t kv_update_action_find_defrag_page(void)\r
-{\r
- errcode_t res;\r
- g_defrag_page_location = NULL;\r
- res = kv_nvregion_find_unused_page(&g_defrag_page_location);\r
- if (res == ERRCODE_SUCC) {\r
- nv_log_debug("[NV] found defrag_page. loc = 0x%x\r\n", g_defrag_page_location);\r
- return EVENT_DEFRAG_FOUND;\r
- } else {\r
- g_current_state_machine->error_code = res;\r
- return EVENT_ERROR;\r
- }\r
-}\r
-\r
-/*\r
- * Attempts to erase the current defrag page, as defined by g_defrag_page_location\r
- */\r
-STATIC kv_update_event_t kv_update_action_erase_defrag_page(void)\r
-{\r
- errcode_t res;\r
- res = kv_nvregion_erase_page(g_defrag_page_location);\r
- if (res == ERRCODE_SUCC) {\r
- return EVENT_DEFRAG_ERASED;\r
- } else if (res == ERRCODE_NV_WRITE_VETOED) {\r
- return EVENT_SUSPEND;\r
- } else {\r
- g_current_state_machine->error_code = res;\r
- return EVENT_ERROR;\r
- }\r
-}\r
-\r
-/*\r
- * Attempts to start the g_prep_defrag_page_machine state machine\r
- */\r
-STATIC kv_update_event_t kv_update_action_prepare_defrag_page(void)\r
-{\r
- errcode_t res = begin_state_machine(&g_prep_defrag_page_machine);\r
- if (res == ERRCODE_SUCC) {\r
- return EVENT_NONE;\r
- } else {\r
- g_current_state_machine->error_code = res;\r
- return EVENT_ERROR;\r
- }\r
-}\r
-\r
-/*\r
- * Attempts to start the g_copy_all_keys_machine state machine\r
- * Configures g_search_filter to find all valid (un-erased) keys\r
- */\r
-STATIC kv_update_event_t kv_update_action_copy_all_keys(void)\r
-{\r
- /* Configure search filter to copy all valid keys */\r
- g_search_filter.pattern = 0;\r
- g_search_filter.mask = 0;\r
- g_search_filter.state = KV_KEY_FILTER_STATE_VALID;\r
- g_search_filter.type = KV_KEY_FILTER_TYPE_ANY;\r
- g_search_filter.location = 0;\r
-\r
- errcode_t res = begin_state_machine(&g_copy_all_keys_machine);\r
- if (res == ERRCODE_SUCC) {\r
- return EVENT_NONE;\r
- } else {\r
- g_current_state_machine->error_code = res;\r
- return EVENT_ERROR;\r
- }\r
-}\r
-\r
-/*\r
- * Prepares g_current_state_machine->write_buffer with information to construct a page header for the\r
- * current defrag page, as defined by g_defrag_page_location\r
- * All information is obtained from g_current_page\r
- * The page sequence_number is updated to indicate that this page will supersede g_current_page\r
- */\r
-STATIC kv_update_event_t kv_update_action_prepare_page_header(void)\r
-{\r
- kv_managed_write_buffer_t *write_buffer = g_current_state_machine->write_buffer;\r
- if (write_buffer == NULL) {\r
- g_current_state_machine->error_code = ERRCODE_NV_WRITE_BUFFER_NOT_ALLOCATED;\r
- return EVENT_ERROR;\r
- }\r
- write_buffer->write_location = (uint32_t)(uintptr_t)g_defrag_page_location;\r
-\r
- if (memcpy_s(write_buffer->data, sizeof(kv_page_header_t), &g_current_page, sizeof(kv_page_header_t)) != EOK) {\r
- return EVENT_ERROR;\r
- }\r
- write_buffer->data_length = (uint16_t)sizeof(kv_page_header_t);\r
- write_buffer->data_consumed = 0;\r
-\r
- /* Too specific, move to kv_page? */\r
- kv_page_header_t *defrag_page_header = (kv_page_header_t *)write_buffer->data;\r
- defrag_page_header->sequence_number++;\r
- defrag_page_header->inverted_sequence_number = ~defrag_page_header->sequence_number;\r
-\r
- return EVENT_WRITE_BUFFER_PRIMED;\r
-}\r
-\r
-/*\r
- * Forces a re-scan of the KV NV flash region to update a map of KV pages\r
- */\r
-STATIC kv_update_event_t kv_update_action_update_nvregion_map(void)\r
-{\r
- errcode_t res;\r
-\r
- res = kv_nvregion_scan();\r
- if (res != ERRCODE_SUCC) {\r
- g_current_state_machine->error_code = res;\r
- return EVENT_ERROR;\r
- }\r
- return EVENT_NVREGION_MAP_UPDATED;\r
-}\r
-\r
-/*\r
- * Attempts to find a suitable write location in g_current_store for g_new_key_details\r
- * Will set g_current_store_write_location if enough space is found in g_current_store\r
- * Will indicate that a defrag is required if there is not currently enough free space in g_current_store\r
- * Will return an error if a defrag would not free up enough space for the new key\r
- */\r
-STATIC kv_update_event_t kv_update_action_find_write_position(void)\r
-{\r
- errcode_t res;\r
- uint16_t required_space;\r
- kv_key_handle_t new_key;\r
- kv_page_status_t page_status;\r
-\r
- /* Work out space needed for new key */\r
- kv_key_build_from_new(&new_key, &g_new_key_details, 0);\r
- required_space = kv_key_flash_size(&new_key);\r
-\r
- res = kv_store_find_write_page(g_current_store, required_space, &g_current_page, &page_status);\r
- if (res == ERRCODE_SUCC) {\r
- g_current_store_write_location = (kv_key_location)(uintptr_t)page_status.first_writable_location;\r
- return EVENT_WRITE_POS_FOUND;\r
- } else if (res == ERRCODE_NV_DEFRAGMENTATION_NEEDED) {\r
- return EVENT_DEFRAG_REQUIRED;\r
- } else {\r
- g_current_state_machine->error_code = res;\r
- return EVENT_ERROR;\r
- }\r
-}\r
-\r
-/*\r
- * Attempts to start the g_defrag_page_machine state machine\r
- */\r
-STATIC kv_update_event_t kv_update_action_defrag_current_page(void)\r
-{\r
- errcode_t res = begin_state_machine(&g_defrag_page_machine);\r
- if (res == ERRCODE_SUCC) {\r
- return EVENT_NONE;\r
- } else {\r
- g_current_state_machine->error_code = res;\r
- return EVENT_ERROR;\r
- }\r
-}\r
-\r
-/*\r
- * Attempts to start the g_prepare_store_machine state machine\r
- */\r
-STATIC kv_update_event_t kv_update_action_prepare_store(void)\r
-{\r
- errcode_t res = begin_state_machine(&g_prepare_store_machine);\r
- if (res == ERRCODE_SUCC) {\r
- return EVENT_NONE;\r
- } else {\r
- g_current_state_machine->error_code = res;\r
- return EVENT_ERROR;\r
- }\r
-}\r
-\r
-/*\r
- * Configure write location for g_current_state_machine->write_buffer\r
- * Attempt to allocate memory for AES Control data structure, if key data is to be encrypted\r
- */\r
-STATIC kv_update_event_t kv_update_helper_prepare_write_buffer(void)\r
-{\r
- kv_managed_write_buffer_t *write_buffer = g_current_state_machine->write_buffer;\r
-\r
- if (write_buffer == NULL) {\r
- g_current_state_machine->error_code = ERRCODE_NV_WRITE_BUFFER_NOT_ALLOCATED;\r
- return EVENT_ERROR;\r
- }\r
- write_buffer->write_location = (uint32_t)(uintptr_t)g_current_store_write_location;\r
- return EVENT_KEY_DATA_READY;\r
-}\r
-\r
-/*\r
- * Attempt to allocate a kv_managed_source_buffer_t for a key header\r
- */\r
-STATIC kv_update_event_t kv_update_helper_setup_key_header_source_buffer(void)\r
-{\r
- kv_managed_source_buffer_t *source_buffer = NULL;\r
-\r
- /* Setup source buffers for key header */\r
- source_buffer = kv_update_source_buffer((uint8_t *)&g_current_key.header, sizeof(kv_key_header_t));\r
- if (source_buffer == NULL) {\r
- g_current_state_machine->error_code = ERRCODE_NV_KEY_HEADER_BUFFER_NOT_ALLOCATED;\r
- return EVENT_ERROR;\r
- }\r
-\r
- if (((uint32_t)g_new_key_details.attributes & (uint32_t)KV_ATTRIBUTE_ENCRYPTED) != 0) {\r
-#if (CONFIG_NV_SUPPORT_HASH_FOR_CRYPT == NV_YES)\r
- source_buffer->hash_data = true;\r
-#else\r
- source_buffer->gcm_tag_data = true;\r
-#endif\r
- } else {\r
- source_buffer->crc_data = true;\r
- }\r
- source_buffer->encrypt_data = false;\r
- return EVENT_KEY_DATA_READY;\r
-}\r
-\r
-/*\r
- * Attempt to allocate a kv_managed_source_buffer_t for key data\r
- */\r
-STATIC kv_update_event_t kv_update_helper_setup_key_data_source_buffer(bool release_key_data)\r
-{\r
- kv_managed_source_buffer_t *source_buffer = NULL;\r
-\r
- /* Setup source buffer for key data */\r
- source_buffer = kv_update_source_buffer((uint8_t *)g_new_key_details.kvalue, g_new_key_details.kvalue_length);\r
- if (source_buffer == NULL) {\r
- g_current_state_machine->error_code = ERRCODE_NV_KEY_DATA_BUFFER_NOT_ALLOCATED;\r
- return EVENT_ERROR;\r
- }\r
- source_buffer->release_data = release_key_data;\r
-\r
- if (((uint32_t)g_new_key_details.attributes & (uint32_t)KV_ATTRIBUTE_ENCRYPTED) != 0) {\r
-#if (CONFIG_NV_SUPPORT_HASH_FOR_CRYPT == NV_YES)\r
- source_buffer->hash_data = true;\r
-#else\r
- source_buffer->gcm_tag_data = true;\r
-#endif\r
- } else {\r
- source_buffer->crc_data = true;\r
- }\r
- source_buffer->encrypt_data = (((uint32_t)g_new_key_details.attributes & (uint32_t)KV_ATTRIBUTE_ENCRYPTED) != 0);\r
- return EVENT_KEY_DATA_READY;\r
-}\r
-\r
-/*\r
- * Attempt to allocate a kv_managed_source_buffer_t for a key hash\r
- * Will also attempt to allocate a buffer to contain the calculated hash\r
- */\r
-STATIC kv_update_event_t kv_update_helper_setup_key_hash_source_buffer(void)\r
-{\r
- uint8_t *key_hash = NULL;\r
- kv_managed_source_buffer_t *source_buffer = NULL;\r
- uint32_t hash_crc_len = 0;\r
- if (((uint32_t)g_new_key_details.attributes & (uint32_t)KV_ATTRIBUTE_ENCRYPTED) != 0) {\r
- hash_crc_len = KV_CRYPTO_HASH_SIZE;\r
- } else {\r
- hash_crc_len = KV_CRYPTO_CRC_SIZE;\r
- }\r
-\r
- /* Setup source buffer for key hash */\r
- /* The hash is calculated while data is written to flash then placed into this buffer for writing */\r
- key_hash = (uint8_t *)kv_malloc(hash_crc_len);\r
- if (key_hash == NULL) {\r
- g_current_state_machine->error_code = ERRCODE_MALLOC;\r
- return EVENT_ERROR;\r
- }\r
- source_buffer = kv_update_source_buffer(key_hash, hash_crc_len);\r
- if (source_buffer == NULL) {\r
- kv_free(key_hash);\r
- g_current_state_machine->error_code = ERRCODE_NV_KEY_HASH_BUFFER_NOT_ALLOCATED;\r
- return EVENT_ERROR;\r
- }\r
- source_buffer->release_data = true;\r
- source_buffer->receive_hash = true;\r
- source_buffer->encrypt_data = false;\r
- return EVENT_KEY_DATA_READY;\r
-}\r
-\r
-/*\r
- * Prepare kv_managed_source_buffers for the three separate parts of a key i.e. header, data and hash\r
- * Ensures state machine write buffer is ready to receive data for writing\r
- * CNcomment: 准备好NV写入需要的source buffer,NV的三个部分(header、数据、HASH/CRC)被放置在三个source buffer中\r
- */\r
-STATIC kv_update_event_t kv_update_helper_prepare_source_buffers(bool release_key_data)\r
-{\r
- kv_update_event_t event;\r
-\r
- event = kv_update_helper_prepare_write_buffer();\r
- if (event == EVENT_KEY_DATA_READY) {\r
- event = kv_update_helper_setup_key_header_source_buffer();\r
- }\r
- if (event == EVENT_KEY_DATA_READY) {\r
- event = kv_update_helper_setup_key_data_source_buffer(release_key_data);\r
- }\r
- if ((event != EVENT_KEY_DATA_READY) && release_key_data) {\r
- /* Ensure we release any new key data as it will not have been placed into a source buffer */\r
- /* which get cleaned up automatically when a state machine ends */\r
- kv_free((void *)g_new_key_details.kvalue);\r
- g_new_key_details.kvalue = NULL;\r
- }\r
- if (event == EVENT_KEY_DATA_READY) {\r
- event = kv_update_helper_setup_key_hash_source_buffer();\r
- }\r
- return event;\r
-}\r
-\r
-/*\r
- * Checks if g_current_key would end up being modified by any changes detailed in g_new_key_details\r
- * Will return an error if g_current_key is permanent\r
- */\r
-STATIC kv_update_event_t kv_update_helper_check_for_key_updates(void)\r
-{\r
- errcode_t res;\r
- kv_attributes_t attributes = kv_key_attributes(&g_current_key);\r
- if ((((uint32_t)attributes & (uint32_t)KV_ATTRIBUTE_PERMANENT) != 0) &&\r
- (g_new_key_details.focre_write != true)) {\r
- /* We can't modify permanent keys in any way */\r
- g_current_state_machine->error_code = ERRCODE_NV_TRYING_TO_MODIFY_A_PERMANENT_KEY;\r
- return EVENT_ERROR;\r
- }\r
- if (((uint32_t)attributes & (uint32_t)KV_ATTRIBUTE_NON_UPGRADE) !=\r
- ((uint32_t)g_new_key_details.attributes & (uint32_t)KV_ATTRIBUTE_NON_UPGRADE)) {\r
- /* Attributes are being updated */\r
- return EVENT_KEY_UPDATE_REQUIRED;\r
- }\r
- if (((uint32_t)attributes & (uint32_t)g_new_key_details.attributes) != (uint32_t)g_new_key_details.attributes) {\r
- /* Attributes are being updated */\r
- return EVENT_KEY_UPDATE_REQUIRED;\r
- }\r
-\r
- if (g_current_key.header.length != g_new_key_details.kvalue_length) {\r
- /* New data is of a different length */\r
- return EVENT_KEY_UPDATE_REQUIRED;\r
- }\r
- res = kv_helper_compare_key_data(&g_current_key, g_new_key_details.kvalue,\r
- (uint16_t)g_new_key_details.kvalue_length);\r
- if (res != ERRCODE_SUCC) {\r
- /* New data content is different */\r
- return EVENT_KEY_UPDATE_REQUIRED;\r
- }\r
-\r
- /* Key not actually being updated */\r
- return EVENT_KEY_UPDATE_NOT_NEEDED;\r
-}\r
-\r
-/*\r
- * Constructs a new key header from g_new_key_details, copying the attributes of any existing key with the same key_id\r
- * Updates g_current_key with details of the new key header\r
- */\r
-STATIC kv_update_event_t kv_update_action_prepare_write_key(void)\r
-{\r
- errcode_t res = kv_store_find_valid_key(g_current_store, g_new_key_details.key_id, &g_current_key);\r
- if (res == ERRCODE_SUCC) {\r
- kv_update_event_t event = kv_update_helper_check_for_key_updates();\r
- if (event != EVENT_KEY_UPDATE_REQUIRED) {\r
- nv_log_debug("[NV] The Key not need update. Key id = 0x%x\r\n", g_current_key.header.key_id);\r
- return event;\r
- }\r
-\r
- /* Combine existing key attributes with those requested for the new key */\r
- if (g_current_key.header.type != KV_KEY_TYPE_NORMAL) {\r
- g_new_key_details.attributes = (kv_attributes_t)((uint32_t)g_new_key_details.attributes |\r
- (uint32_t)KV_ATTRIBUTE_PERMANENT);\r
- }\r
- if (g_current_key.header.enc_key != 0) {\r
- g_new_key_details.attributes = (kv_attributes_t)((uint32_t)g_new_key_details.attributes |\r
- (uint32_t)KV_ATTRIBUTE_ENCRYPTED);\r
- }\r
- }\r
-\r
- kv_key_build_from_new(&g_current_key, &g_new_key_details, g_current_store_write_location);\r
- return kv_update_helper_prepare_source_buffers(false);\r
-}\r
-\r
-STATIC errcode_t kv_update_helper_get_current_key(uint8_t **old_kvalue, uint32_t *kvalue_length)\r
-{\r
- kv_key_handle_t current_key;\r
- errcode_t res;\r
-\r
- res = kv_store_find_valid_key(g_current_store, g_new_key_details.key_id, ¤t_key);\r
- if (res != ERRCODE_SUCC) {\r
- return res;\r
- }\r
-\r
- kv_attributes_t attributes = kv_key_attributes(¤t_key);\r
- if (((uint32_t)attributes & KV_ATTRIBUTE_PERMANENT) != 0) {\r
- return ERRCODE_SUCC;\r
- }\r
-\r
- if (((uint32_t)attributes & KV_ATTRIBUTE_ENCRYPTED) != 0) {\r
- /* if old key is encrypted, the new key must be encrypted too */\r
- g_new_key_details.attributes = (kv_attributes_t)((uint32_t)g_new_key_details.attributes |\r
- (uint32_t)KV_ATTRIBUTE_ENCRYPTED);\r
- }\r
-\r
- *kvalue_length = current_key.header.length;\r
- *old_kvalue = (uint8_t *)kv_malloc(current_key.header.length);\r
- if (*old_kvalue == NULL) {\r
- return ERRCODE_MALLOC;\r
- }\r
-\r
- res = kv_key_read_data(¤t_key, *old_kvalue);\r
- if (res != ERRCODE_SUCC) {\r
- kv_free((void *)*old_kvalue);\r
- }\r
- return res;\r
-}\r
-\r
-/*\r
- * Attempt to claim access to hardware AES and hash functions\r
- */\r
-STATIC kv_update_event_t kv_update_action_claim_crypto(void)\r
-{\r
- kv_managed_write_buffer_t *write_buffer = g_current_state_machine->write_buffer;\r
-\r
- /* Reset write location */\r
- write_buffer->write_location = (uint32_t)(uintptr_t)g_current_key.key_location;\r
-\r
- /* Point current source buffer at first buffer and reset data_consumed values */\r
- g_current_state_machine->current_source = g_current_state_machine->source_buffer;\r
- kv_managed_source_buffer_t *source_buffer = g_current_state_machine->source_buffer;\r
- while (source_buffer != NULL) {\r
- source_buffer->data_consumed = 0;\r
- source_buffer = source_buffer->next;\r
- }\r
-\r
- write_buffer->crypto_handle = INVAILD_CRYPTO_HANDLE;\r
-\r
- if (((uint32_t)g_new_key_details.attributes & (uint32_t)KV_ATTRIBUTE_ENCRYPTED) != 0) {\r
-#if (CONFIG_NV_SUPPORT_ENCRYPT == NV_YES)\r
- /* Claim cryptographic engines */\r
- if (!write_buffer->encrypt_claimed) {\r
- errcode_t res = nv_crypto_claim_aes(&write_buffer->crypto_handle, &(g_current_key.header));\r
- if (res != ERRCODE_SUCC) {\r
- g_current_state_machine->error_code = res;\r
- return EVENT_ERROR;\r
- }\r
- write_buffer->encrypt_claimed = true;\r
- }\r
-\r
-#if (CONFIG_NV_SUPPORT_HASH_FOR_CRYPT == NV_YES)\r
- if (!write_buffer->hash_claimed) {\r
- errcode_t res = nv_crypto_start_hash();\r
- if (res != ERRCODE_SUCC) {\r
- nv_crypto_release_aes(write_buffer->crypto_handle);\r
- write_buffer->crypto_handle = INVAILD_CRYPTO_HANDLE;\r
- write_buffer->encrypt_claimed = false;\r
- g_current_state_machine->error_code = res;\r
- return EVENT_ERROR;\r
- }\r
- write_buffer->crc_ret = 0;\r
- write_buffer->hash_claimed = true;\r
- }\r
-#else\r
- if (!write_buffer->gcm_tag_claimed) {\r
- write_buffer->crc_ret = 0;\r
- write_buffer->gcm_tag_claimed = true;\r
- }\r
-#endif /* #if (CONFIG_NV_SUPPORT_HASH_FOR_CRYPT == NV_YES) */\r
-#endif /* #if (CONFIG_NV_SUPPORT_ENCRYPT == NV_YES) */\r
- } else {\r
- if (!write_buffer->crc_claimed) {\r
- write_buffer->crc_ret = 0;\r
- write_buffer->crc_claimed = true;\r
- }\r
- }\r
-\r
- return EVENT_CRYPTO_CLAIMED;\r
-}\r
-\r
-/*\r
- * Attempts to erases all instances of g_current_key (in g_current_store), by marking them as invalid\r
- * Configures g_search_filter to find all valid keys with an id matching g_current_key.header.key_id\r
- * Starts the g_erase_keys_machine state machine to perform the erasing of keys\r
- */\r
-STATIC kv_update_event_t kv_update_action_erase_old_keys(void)\r
-{\r
- g_search_filter.pattern = g_current_key.header.key_id;\r
- g_search_filter.mask = 0xFFFF;\r
- g_search_filter.state = KV_KEY_FILTER_STATE_VALID;\r
- g_search_filter.type = KV_KEY_FILTER_TYPE_ANY;\r
- g_search_filter.location = g_current_key.key_location;\r
-\r
- errcode_t res = begin_state_machine(&g_erase_keys_machine);\r
- if (res == ERRCODE_SUCC) {\r
- return EVENT_NONE;\r
- } else {\r
- g_current_state_machine->error_code = res;\r
- return EVENT_ERROR;\r
- }\r
-}\r
-\r
-STATIC kv_update_event_t kv_update_action_update_nvmap_for_erase_key(void)\r
-{\r
- kv_nvregion_map_t *nv_map = kv_nvregion_get_map();\r
- uint32_t page_number = kv_nvregion_get_page_number(g_current_page.page_location);\r
- if (page_number >= nv_map->num_entries) {\r
- nv_log_debug("[NV] get erase page_number failed! page_location = 0x%x\r\n", g_current_page.page_location);\r
- g_current_state_machine->error_code = ERRCODE_NV_INVALID_PAGE;\r
- return EVENT_ERROR;\r
- }\r
-\r
- uint16_t key_size = (uint32_t)sizeof(kv_key_header_t);\r
- uint16_t hash_crc_size;\r
- if (g_current_key.header.enc_key == AES_KDFKEY_SDRK_TYPE) {\r
- hash_crc_size = KV_CRYPTO_HASH_SIZE;\r
- } else {\r
- hash_crc_size = KV_CRYPTO_CRC_SIZE;\r
- }\r
-\r
- key_size += hash_crc_size;\r
-\r
- kv_attributes_t attributes = kv_key_attributes(&g_current_key);\r
- key_size += kv_key_padded_data_length(attributes, g_current_key.header.length);\r
-\r
- nv_page_status_map_t *page_status = &(nv_map->page_status_map[page_number]);\r
- page_status->reclaimable_space += key_size;\r
- nv_log_debug("[NV] update nv map for erase key = 0x%x (page = 0x%x) size = %d\r\n",\r
- g_current_key.header.key_id, g_current_page.page_location, key_size);\r
- return EVENT_PAGE_MAP_UPDATED;\r
-}\r
-\r
-STATIC kv_update_event_t kv_update_action_update_nvmap_for_new_key(void)\r
-{\r
- kv_nvregion_map_t *nv_map = kv_nvregion_get_map();\r
-\r
- uint32_t page_number = kv_nvregion_get_page_number(g_current_page.page_location);\r
- if (page_number >= nv_map->num_entries) {\r
- nv_log_debug("[NV] get new key page_number failed! page_location = 0x%x\r\n", g_current_page.page_location);\r
- g_current_state_machine->error_code = ERRCODE_NV_INVALID_PAGE;\r
- return EVENT_ERROR;\r
- }\r
-\r
- uint16_t key_size = (uint32_t)sizeof(kv_key_header_t);\r
- uint16_t hash_crc_size;\r
- if (((uint32_t)g_new_key_details.attributes & (uint32_t)KV_ATTRIBUTE_ENCRYPTED) != 0) {\r
- hash_crc_size = KV_CRYPTO_HASH_SIZE;\r
- } else {\r
- hash_crc_size = KV_CRYPTO_CRC_SIZE;\r
- }\r
-\r
- key_size += hash_crc_size;\r
- key_size += kv_key_padded_data_length(g_new_key_details.attributes, g_new_key_details.kvalue_length);\r
-\r
- nv_page_status_map_t *page_status = &(nv_map->page_status_map[page_number]);\r
- page_status->used_space += key_size;\r
- page_status->first_writable_offset += key_size;\r
- nv_log_debug("[NV] update nv map for new key = 0x%x (page = 0x%x) size = %d\r\n",\r
- g_new_key_details.key_id, g_current_page.page_location, key_size);\r
- return EVENT_PAGE_MAP_UPDATED;\r
-}\r
-\r
-/*\r
- * Begins a new state machine by creating a new entry at the end of the active state machines list\r
- * and updating g_current_state_machine\r
- *\r
- * Either called to begin processing a *new* top-level state machine, before calling process_state_machine()\r
- * or called by a state machine action function to kick off the processing of a new (nested) state machine\r
- */\r
-STATIC errcode_t begin_state_machine(const kv_state_machine_t *machine)\r
-{\r
- kv_active_state_machine_t *new_state_machine =\r
- (kv_active_state_machine_t *)kv_malloc(sizeof(kv_active_state_machine_t));\r
- if (new_state_machine == NULL) {\r
- return ERRCODE_MALLOC;\r
- }\r
- (void)memset_s(new_state_machine, sizeof(kv_active_state_machine_t), 0, sizeof(kv_active_state_machine_t));\r
- new_state_machine->prev = g_current_state_machine;\r
-\r
- /* Configure default initial state and event for new state machine */\r
- new_state_machine->machine = machine;\r
- new_state_machine->state = machine->initial_state;\r
- new_state_machine->event = EVENT_NONE;\r
- new_state_machine->error_code = ERRCODE_SUCC;\r
- g_current_state_machine = new_state_machine;\r
-\r
- if (g_current_state_machine->machine->write_buffer_size > 0) {\r
- return kv_create_write_buffer(0, g_current_state_machine->machine->write_buffer_size);\r
- } else {\r
- return ERRCODE_SUCC;\r
- }\r
-}\r
-\r
-/*\r
- * Should only be called by process_state_machine()\r
- *\r
- * Ends current state machine and updates g_current_state_machine to point to the previous state machine.\r
- * Passes either the pre-defined exit_event or the last event returned by an action function as the\r
- * event received by the previous state machine.\r
- */\r
-STATIC void end_state_machine(void)\r
-{\r
- if (g_current_state_machine != NULL) {\r
- kv_remove_write_buffer();\r
- kv_remove_source_buffer();\r
-\r
- kv_active_state_machine_t *prev_state_machine = g_current_state_machine->prev;\r
- if (prev_state_machine != NULL) {\r
- if (g_current_state_machine->machine->exit_event != EVENT_NONE) {\r
- /* Single explicit exit event defined for exiting state machine */\r
- prev_state_machine->event = g_current_state_machine->machine->exit_event;\r
- } else {\r
- /* Pass last event raised from exiting state machine back to invoking state machine */\r
- prev_state_machine->event = g_current_state_machine->event;\r
- }\r
- }\r
-\r
- kv_free(g_current_state_machine);\r
- g_current_state_machine = prev_state_machine;\r
- }\r
-}\r
-\r
-static void clean_state_machine(void)\r
-{\r
- while (g_current_state_machine != NULL) {\r
- end_state_machine();\r
- }\r
-}\r
-\r
-/*\r
- * Should only be called by process_state_machine()\r
- *\r
- * Attempts to locate and call an action function based on the state of the current state machine\r
- * An action function may spawn another state machine and hence cause g_current_state_machine to change.\r
- *\r
- * g_current_state_machine->event is used to record the return value from the action function.\r
- *\r
- * If a new state machine is spawned, this return value will effectively be lost as the procedure for processing\r
- * new state machines is to assume there is no initial event, just an initial state and thus action to perform.\r
- *\r
- * If an action function is not found then g_current_state_machine->event will not be updated. This really should\r
- * be reserved for when state transitions cause g_current_state_machine->state to be one of the following\r
- * special cases, which are handled by process_state_machine():\r
- * - STATE_INVALID\r
- * - STATE_SUSPENDED\r
- * - STATE_EXIT\r
- */\r
-static void invoke_current_state_action(void)\r
-{\r
- if (g_current_state_machine != NULL) {\r
- /* Search action table to locate an action function for the current state */\r
- /* STATE_EXIT always marks end of an action table */\r
- const kv_update_action_t *update_action = g_current_state_machine->machine->action_table;\r
- while (update_action->state != STATE_EXIT) {\r
- if (update_action->state == g_current_state_machine->state) {\r
- /* Found a matching entry */\r
- break;\r
- }\r
- update_action++;\r
- }\r
-\r
- /* Call action function if we have found one */\r
- if (update_action->action != NULL) {\r
- /* Action function could cause a change of g_current_state_machine */\r
- g_current_state_machine->event = (*update_action->action)();\r
- }\r
- }\r
-}\r
-\r
-/*\r
- * Should only be called by process_state_machine()\r
- *\r
- * Uses the last event recorded for the state machine, in g_current_state_machine->event, to select the\r
- * next appropriate state for the current state machine.\r
- *\r
- * In the case of no suitable entry on the transition_table an error state of STATE_INVALID is entered.\r
- * This will cause process_state_machine() to terminate processing of all queued state machines and\r
- * return an error.\r
- */\r
-STATIC void update_current_state(void)\r
-{\r
- /* Don't update anything for EVENT_NONE. We'll eventually call the action function again */\r
- if ((g_current_state_machine != NULL) && (g_current_state_machine->event != EVENT_NONE)) {\r
- /* Search transition table looking for a transition entry for current state and event */\r
- /* A transition table is always terminated with transition->state == STATE_EXIT */\r
- const kv_update_transition_t *transition = g_current_state_machine->machine->transition_table;\r
- while (transition->state != STATE_EXIT) {\r
- if ((g_current_state_machine->state == transition->state) &&\r
- (g_current_state_machine->event == transition->event)) {\r
- /* Found a matching entry */\r
- break;\r
- }\r
- transition++;\r
- }\r
-\r
- /* Update current state if we have found a suitable transition table entry */\r
- if (transition->state != STATE_EXIT) {\r
- g_current_state_machine->state = transition->next_state;\r
- } else {\r
- /* No state transition found for the current state and event */\r
- g_current_state_machine->state = STATE_INVALID;\r
- }\r
- }\r
-}\r
-\r
-#if defined (DEBUG_PRINT_ENABLED)\r
-static const char *g_update_state_strings[STATE_EXIT + 1] = {\r
- [STATE_INVALID] = "STATE_INVALID",\r
- [STATE_SELECT_FIRST_PAGE] = "STATE_SELECT_FIRST_PAGE",\r
- [STATE_SELECT_NEXT_PAGE] = "STATE_SELECT_NEXT_PAGE",\r
- [STATE_FIND_FIRST_KEY] = "STATE_FIND_FIRST_KEY",\r
- [STATE_FIND_NEXT_KEY] = "STATE_FIND_NEXT_KEY",\r
- [STATE_FIND_EXISTING_KEY] = "STATE_FIND_EXISTING_KEY",\r
- [STATE_PREP_COPY_KEY] = "STATE_PREP_COPY_KEY",\r
- [STATE_PREP_DELETE_KEY] = "STATE_PREP_DELETE_KEY",\r
- [STATE_PREP_MODIFY_KEY] = "STATE_PREP_MODIFY_KEY",\r
- [STATE_FIND_DEFRAG] = "STATE_FIND_DEFRAG",\r
- [STATE_ERASE_DEFRAG] = "STATE_ERASE_DEFRAG",\r
- [STATE_PREP_DEFRAG] = "STATE_PREP_DEFRAG",\r
- [STATE_COPY_ALL_KEYS] = "STATE_COPY_ALL_KEYS",\r
- [STATE_PREP_PAGE_HEADER] = "STATE_PREP_PAGE_HEADER",\r
- [STATE_WRITE_PAGE_HEADER] = "STATE_WRITE_PAGE_HEADER",\r
- [STATE_UPDATE_NVREGION_MAP] = "STATE_UPDATE_NVREGION_MAP",\r
- [STATE_FIND_WRITE_POS] = "STATE_FIND_WRITE_POS",\r
- [STATE_DEFRAG_PAGE] = "STATE_DEFRAG_PAGE",\r
- [STATE_PREP_STORE] = "STATE_PREP_STORE",\r
- [STATE_PREP_KEY_DATA] = "STATE_PREP_KEY_DATA",\r
- [STATE_CLAIM_CRYPTO] = "STATE_CLAIM_CRYPTO",\r
- [STATE_ERASE_OLD_KEYS] = "STATE_ERASE_OLD_KEYS",\r
- [STATE_PRIME_WRITE] = "STATE_PRIME_WRITE",\r
- [STATE_PERFORM_WRITE] = "STATE_PERFORM_WRITE",\r
- [STATE_SUSPENDED] = "STATE_SUSPENDED",\r
- [STATE_EXIT] = "STATE_EXIT"\r
-};\r
-\r
-#if defined (DEBUG_KV_UPDATE_STATE_MACHINE)\r
-static const char *g_update_event_strings[EVENT_ERROR + 1] = {\r
- [EVENT_NONE] = "EVENT_NONE",\r
- [EVENT_SUSPEND] = "EVENT_SUSPEND",\r
- [EVENT_WRITE_DATA_EXHAUSTED] = "EVENT_WRITE_DATA_EXHAUSTED",\r
- [EVENT_WRITE_BUFFER_PRIMED] = "EVENT_WRITE_BUFFER_PRIMED",\r
- [EVENT_WRITE_BUFFER_STORED] = "EVENT_WRITE_BUFFER_STORED",\r
- [EVENT_PAGE_SELECTED] = "EVENT_PAGE_SELECTED",\r
- [EVENT_PAGE_NOT_SELECTED] = "EVENT_PAGE_NOT_SELECTED",\r
- [EVENT_KEY_FOUND] = "EVENT_KEY_FOUND",\r
- [EVENT_KEY_NOT_FOUND] = "EVENT_KEY_NOT_FOUND",\r
- [EVENT_COPY_KEY_READY] = "EVENT_COPY_KEY_READY",\r
- [EVENT_KEYS_ERASED] = "EVENT_KEYS_ERASED",\r
- [EVENT_KEY_UPDATE_REQUIRED] = "EVENT_KEY_UPDATE_REQUIRED",\r
- [EVENT_KEY_UPDATE_NOT_NEEDED] = "EVENT_KEY_UPDATE_NOT_NEEDED",\r
- [EVENT_DEFRAG_FOUND] = "EVENT_DEFRAG_FOUND",\r
- [EVENT_DEFRAG_ERASED] = "EVENT_DEFRAG_ERASED",\r
- [EVENT_DEFRAG_PREPARED] = "EVENT_DEFRAG_PREPARED",\r
- [EVENT_ALL_KEYS_COPIED] = "EVENT_ALL_KEYS_COPIED",\r
- [EVENT_PAGE_HEADER_READY] = "EVENT_PAGE_HEADER_READY",\r
- [EVENT_NVREGION_MAP_UPDATED] = "EVENT_NVREGION_MAP_UPDATED",\r
- [EVENT_DEFRAG_REQUIRED] = "EVENT_DEFRAG_REQUIRED",\r
- [EVENT_DEFRAG_COMPLETE] = "EVENT_DEFRAG_COMPLETE",\r
- [EVENT_WRITE_POS_FOUND] = "EVENT_WRITE_POS_FOUND",\r
- [EVENT_STORE_READY] = "EVENT_STORE_READY",\r
- [EVENT_KEY_DATA_READY] = "EVENT_KEY_DATA_READY",\r
- [EVENT_CRYPTO_CLAIMED] = "EVENT_CRYPTO_CLAIMED",\r
- [EVENT_WRITE_COMPLETE] = "EVENT_WRITE_COMPLETE",\r
- [EVENT_ERROR] = "EVENT_ERROR"\r
-};\r
-#endif\r
-#endif\r
-\r
-static void dprint_state_machine(const char *str)\r
-{\r
- unused(str);\r
-#if defined (DEBUG_KV_UPDATE_STATE_MACHINE)\r
- if (g_current_state_machine != NULL) {\r
- nv_log_debug("%08X: ", (uint32_t)g_current_state_machine);\r
- nv_log_debug("%s ", g_update_state_strings[g_current_state_machine->state]);\r
- nv_log_debug("%s ", str);\r
- nv_log_debug("%s\r\n", g_update_event_strings[g_current_state_machine->event]);\r
- } else {\r
- nv_log_debug("*** State Machine %s ***\r\n", str);\r
- }\r
-#endif\r
-}\r
-\r
-/*\r
- * Function used to process queued state machines\r
- *\r
- * There is only ever one state machine being processed at any one time.\r
- *\r
- * State machines are started (queued) by explicitly calling begin_state_machine() before this function is called.\r
- * State machines themselves can spawn a nested state machine by also calling begin_state_machine() in an action\r
- * function.\r
- * When started, a state machine will be in the initial_state defined for the state machine.\r
- *\r
- * State machines are ended normally by transitioning to STATE_EXIT or abnormally by transitioning to STATE_INVALID.\r
- * In such instances, process_state_machine(), will call end_state_machine() to tidy up.\r
- *\r
- * State machines can be suspended at any point by transitioning to STATE_SUSPENDED.\r
- * When resumed, by calling process_state_machine() again, they will start from the resume_state defined for the\r
- * state machine.\r
- *\r
- * g_current_state_machine points to the current active state machine\r
- *\r
- * When a state machine is processed for the first time, or resumed following suspension, it is assumed there is no\r
- * current event that exists for the state machine in this context. An action function for the current state, which\r
- * will be defined by initial_state or resume_state, will provide an event to provide for further transitions through\r
- * the state machine.\r
- */\r
-STATIC errcode_t process_state_machine(void)\r
-{\r
- errcode_t error_code = ERRCODE_SUCC;\r
- uint64_t flash_access_time = 0;\r
- unused(flash_access_time);\r
-\r
- while (g_current_state_machine != NULL) {\r
- /* Call action function for the current state */\r
- dprint_state_machine("---");\r
- invoke_current_state_action();\r
- dprint_state_machine("-->");\r
-\r
- /* Check event returned by action function */\r
- if (g_current_state_machine->event == EVENT_ERROR) {\r
- /* Ripple back down chain of state machines and report error */\r
- error_code = g_current_state_machine->error_code;\r
- clean_state_machine();\r
- return error_code;\r
- }\r
-\r
- /* Transition state machine */\r
- update_current_state();\r
- dprint_state_machine("<--");\r
-\r
- /* Has the state machine finished? */\r
- while ((g_current_state_machine != NULL) && (g_current_state_machine->state == STATE_EXIT)) {\r
- invoke_current_state_action(); /* Exit state could have an action function if it needs to do more */\r
- end_state_machine(); /* than just end the current state machine */\r
- update_current_state();\r
- dprint_state_machine("<==");\r
- }\r
-\r
- /* Has state machine processing been suspended? */\r
- if ((g_current_state_machine != NULL) && (g_current_state_machine->state == STATE_SUSPENDED)) {\r
-#ifdef CONFIG_NV_SUPPORT_SINGLE_CORE_SYSTEM\r
- /* if in one-core system, the flash must be writen in one time */\r
- /* so once the suspend happened, clear the state_machine */\r
- clean_state_machine();\r
-#else\r
- /* We need to suspend processing due to other cores waking up */\r
- /* Configure state machine to resume from the correct place */\r
- g_current_state_machine->state = g_current_state_machine->machine->resume_state;\r
- g_current_state_machine->event = EVENT_NONE;\r
-#endif\r
- kv_release_crypto();\r
- return ERRCODE_NV_WRITE_VETOED;\r
- }\r
-\r
- /* Has the state machine entered an invalid state? */\r
- if ((g_current_state_machine != NULL) && (g_current_state_machine->state == STATE_INVALID)) {\r
-#ifdef CONFIG_NV_SUPPORT_SINGLE_CORE_SYSTEM\r
- clean_state_machine();\r
-#endif\r
- kv_release_crypto();\r
- return ERRCODE_NV_STATE_INVALID;\r
- }\r
- }\r
-\r
- return error_code;\r
-}\r
-\r
-/* Creates a write buffer used to prepare data, gathered from one or more source buffers, for writing to flash */\r
-STATIC errcode_t kv_create_write_buffer(uint32_t write_location, uint32_t size)\r
-{\r
- if (size == 0) {\r
- return ERRCODE_INVALID_PARAM;\r
- }\r
-\r
- if (g_current_state_machine->write_buffer != NULL) {\r
- return ERRCODE_FAIL;\r
- }\r
-\r
- uint8_t *data = (uint8_t *)kv_malloc(size);\r
- if (data == NULL) {\r
- return ERRCODE_MALLOC;\r
- }\r
- kv_managed_write_buffer_t *write_buffer =\r
- (kv_managed_write_buffer_t *)kv_malloc(sizeof(kv_managed_write_buffer_t));\r
- if (write_buffer == NULL) {\r
- kv_free(data);\r
- return ERRCODE_MALLOC;\r
- }\r
-\r
- (void)memset_s(write_buffer, sizeof(kv_managed_write_buffer_t), 0, sizeof(kv_managed_write_buffer_t));\r
- write_buffer->data = data;\r
- write_buffer->size = (uint16_t)size;\r
- write_buffer->data_consumed = (uint16_t)size;\r
- write_buffer->write_location = write_location;\r
- write_buffer->resume_location = write_location;\r
- g_current_state_machine->write_buffer = write_buffer;\r
-\r
- return ERRCODE_SUCC;\r
-}\r
-\r
-/* Removes the write buffer owned by the current state machine */\r
-STATIC void kv_remove_write_buffer(void)\r
-{\r
- if (g_current_state_machine->write_buffer != NULL) {\r
- kv_free(g_current_state_machine->write_buffer->data);\r
- kv_free(g_current_state_machine->write_buffer);\r
- g_current_state_machine->write_buffer = NULL;\r
- }\r
-}\r
-\r
-/* Every key consists of a header, data and a hash. Multiple kv_managed_source_buffer_t are constructed to\r
- * manage these separate blocks of data.\r
- * They keep track of where the data is located and how much of it has\r
- * been written to flash.\r
- * These buffers are used in a scatter-gather type approach to feed a single write buffer that holds data\r
- * that has been fully prepared for writing to flash */\r
-STATIC kv_managed_source_buffer_t* kv_update_source_buffer(uint8_t *data, uint32_t data_length)\r
-{\r
- kv_managed_source_buffer_t *source_buffer = kv_malloc(sizeof(kv_managed_source_buffer_t));\r
- if (source_buffer == NULL) {\r
- return NULL;\r
- }\r
-\r
- errno_t err = memset_s(source_buffer, sizeof(kv_managed_source_buffer_t), 0, sizeof(kv_managed_source_buffer_t));\r
- if (err != EOK) {\r
- kv_free(source_buffer);\r
- return NULL;\r
- }\r
- source_buffer->data = data;\r
- source_buffer->data_length = (uint16_t)data_length;\r
- source_buffer->next = NULL;\r
-\r
- /* Append new buffer to end of list */\r
- kv_managed_source_buffer_t **next_buffer = &g_current_state_machine->source_buffer;\r
- while (*next_buffer != NULL) {\r
- next_buffer = &(*next_buffer)->next;\r
- }\r
- *next_buffer = source_buffer;\r
- return source_buffer;\r
-}\r
-\r
-/* Removes kv_managed_source_buffer_t data structures, freeing memory holding actual data too, if necessary */\r
-STATIC void kv_remove_source_buffer(void)\r
-{\r
- kv_managed_source_buffer_t *current = g_current_state_machine->source_buffer;\r
- while (current != NULL) {\r
- kv_managed_source_buffer_t *next = current->next;\r
- if (current->release_data) {\r
- kv_free(current->data);\r
- }\r
- kv_free(current);\r
- current = next;\r
- }\r
- g_current_state_machine->source_buffer = NULL;\r
-}\r
-\r
-/* Releases claim over hardware AES and hash functions */\r
-STATIC void kv_release_crypto(void)\r
-{\r
- if (g_current_state_machine == NULL) {\r
- return;\r
- }\r
- kv_managed_write_buffer_t *write_buffer = g_current_state_machine->write_buffer;\r
- uint32_t status;\r
-\r
- if (write_buffer != NULL) {\r
- status = osal_irq_lock();\r
- if (write_buffer->crc_claimed) {\r
- write_buffer->crc_ret = 0;\r
- write_buffer->crc_claimed = false;\r
- }\r
-#if (CONFIG_NV_SUPPORT_ENCRYPT == NV_YES)\r
- if (write_buffer->hash_claimed) {\r
- write_buffer->hash_claimed = false;\r
- }\r
- if (write_buffer->gcm_tag_claimed) {\r
- write_buffer->gcm_tag_claimed = false;\r
- }\r
- if (write_buffer->encrypt_claimed) {\r
- nv_crypto_release_aes(write_buffer->crypto_handle);\r
- write_buffer->crypto_handle = INVAILD_CRYPTO_HANDLE;\r
- write_buffer->encrypt_claimed = false;\r
- }\r
-#endif\r
- osal_irq_restore(status);\r
- }\r
-}\r
-\r
-STATIC bool active_state_machine(void)\r
-{\r
- if (g_current_state_machine != NULL) {\r
- return true;\r
- }\r
- return false;\r
-}\r
-\r
-STATIC uint32_t determine_flash_task_state_code(errcode_t res)\r
-{\r
- switch (res) {\r
- case ERRCODE_NV_WRITE_VETOED:\r
- case ERRCODE_FLASH_TASK_PE_VETO:\r
- return FLASH_TASK_BEING_PROCESSED;\r
- case ERRCODE_SUCC:\r
- return FLASH_TASK_COMPLETED;\r
- default:\r
- return (uint32_t)res;\r
- }\r
-}\r
-\r
-#if defined(CONFIG_PARTITION_FEATURE_SUPPORT)\r
-STATIC errcode_t nv_read_partition_addr(uint32_t *start_address, uint32_t *size)\r
-{\r
- /* 获取分区表标记 */\r
- errcode_t ret;\r
- partition_information_t info;\r
-\r
- ret = uapi_partition_get_info(PARTITION_NV_DATA, &info);\r
- if (ret != ERRCODE_SUCC) {\r
- return ret;\r
- }\r
- *start_address = info.part_info.addr_info.addr + FLASH_PHYSICAL_ADDR_START;\r
- *size = info.part_info.addr_info.size;\r
-\r
- return ERRCODE_SUCC;\r
-}\r
-#endif\r
-\r
-errcode_t kv_update_init(cores_t core)\r
-{\r
- unused(core);\r
-\r
- errcode_t res;\r
- uint32_t kv_start_addr = 0;\r
- uint32_t kv_size = 0;\r
-#if defined(CONFIG_PARTITION_FEATURE_SUPPORT)\r
- res = nv_read_partition_addr(&kv_start_addr, &kv_size);\r
- if (res != ERRCODE_SUCC) {\r
- return res;\r
- }\r
-#endif\r
- if (kv_start_addr != 0 && kv_size != 0) {\r
- kv_nvregion_init(kv_start_addr, KV_STORE_DATA_SIZE, kv_start_addr + KV_STORE_DATA_SIZE, KV_BACKUP_DATA_SIZE);\r
- } else {\r
- kv_nvregion_init(KV_STORE_START_ADDR, KV_STORE_DATA_SIZE, KV_BACKUP_START_ADDR, KV_BACKUP_DATA_SIZE);\r
- }\r
-\r
- /* Scan NV region looking for KV pages */\r
- res = kv_nvregion_scan();\r
- if (res != ERRCODE_SUCC) {\r
- return res;\r
- }\r
-\r
- /* Check expected KV pages have been found */\r
- for (uint8_t store = (uint8_t)KV_STORE_APPLICATION; store < (uint8_t)KV_STORE_MAX_NUM; store++) {\r
- uint16_t store_id = kv_store_get_id(store);\r
- uint8_t pages_num = kv_store_get_page_count(store);\r
- for (uint8_t page_index = 0; page_index < pages_num; page_index++) {\r
- res = kv_nvregion_find_page(store_id, page_index, NULL, NULL);\r
- if (res != ERRCODE_SUCC) {\r
- /* NV Region does not contain an expected KV page, attempt to create it */\r
- res = kv_nvregion_create_page(store_id, page_index);\r
- }\r
- if (res != ERRCODE_SUCC) {\r
- return ERRCODE_FAIL;\r
- }\r
- }\r
- }\r
- return ERRCODE_SUCC;\r
-}\r
-\r
-#if (CONFIG_NV_SUPPORT_BACKUP_RESTORE == NV_YES)\r
-STATIC errcode_t kv_updata_backup_page_head_is_valid(const kv_page_header_t *backup_head)\r
-{\r
- if ((backup_head->details.store_id == KV_STORE_ID_BACKUP) &&\r
- ((~backup_head->inverted_details_word) == *(uint32_t *)(uintptr_t)(&backup_head->details)) &&\r
- ((~backup_head->inverted_sequence_number) == backup_head->sequence_number)) {\r
- return ERRCODE_SUCC;\r
- }\r
- return ERRCODE_FAIL;\r
-}\r
-\r
-STATIC errcode_t kv_updata_backup_fail_process(uint32_t back_maybe_need_process_location)\r
-{\r
- errcode_t res;\r
- kv_page_location unused_page_location = NULL;\r
- uint32_t page_head_size = (uint32_t)sizeof(kv_page_header_t);\r
- kv_nvregion_area_t* nvregion_area = nv_get_region_area();\r
- if (nvregion_area == NULL) {\r
- return ERRCODE_FAIL;\r
- }\r
- /* 去找工作区可以换页的页,然后将可以换页的页地址给传出 */\r
- res = kv_nvregion_find_unused_page(&unused_page_location);\r
- if (res != ERRCODE_SUCC) {\r
- return res;\r
- }\r
- /* 获取工作区换页页的页头 */\r
- kv_page_header_t store_head_buffer;\r
- res = kv_key_helper_copy_flash((uint32_t)(uintptr_t)&store_head_buffer,\r
- (uint32_t)unused_page_location, page_head_size);\r
- if (res != ERRCODE_SUCC) {\r
- return res;\r
- }\r
- /* 如果页头中包含 KV_STORE_ID_BACKUP 并且该page_location与maybe_need_process_location时都指向同一页 那么说明存在掉电丢数据的情况 */\r
- if (kv_updata_backup_page_head_is_valid(&store_head_buffer) == ERRCODE_SUCC) {\r
- uint32_t back_page_location = nvregion_area->nv_backup_addr\r
- + store_head_buffer.details.page_index * KV_PAGE_SIZE;\r
- if (back_page_location == back_maybe_need_process_location) {\r
- res = kv_key_erase_flash(back_page_location, KV_PAGE_SIZE);\r
- if (res != ERRCODE_SUCC) {\r
- return res;\r
- }\r
- res = kv_backup_copy_unused_page_to_dragpage(back_page_location, (uint32_t)unused_page_location);\r
- if (res != ERRCODE_SUCC) {\r
- return res;\r
- }\r
- }\r
- }\r
- return ERRCODE_SUCC;\r
-}\r
-\r
-errcode_t kv_update_backup_init(void)\r
-{\r
- /* Initializes the page header of the backup area */\r
- errcode_t res;\r
- uint32_t backup_not_inited_count = 0; /* 重启时备份区没有初始化的数量为0 */\r
- uint32_t back_maybe_need_process_location = 0;\r
- kv_nvregion_area_t* nvregion_area = nv_get_region_area();\r
- if (nvregion_area == NULL) {\r
- return ERRCODE_FAIL;\r
- }\r
- uint32_t back_page_location = nvregion_area->nv_backup_addr;\r
- kv_page_header_t backup_head_buffer;\r
- for (uint32_t page_index = 0; page_index < KV_BACKUP_PAGE_NUM; page_index++) {\r
- res = kv_key_helper_copy_flash((uint32_t)(uintptr_t)&backup_head_buffer,\r
- (uint32_t)back_page_location, sizeof(kv_page_header_t));\r
- if (res != ERRCODE_SUCC) {\r
- return res;\r
- }\r
- if (kv_updata_backup_page_head_is_valid(&backup_head_buffer) == ERRCODE_FAIL) {\r
- backup_not_inited_count++;\r
- res = kv_nvregion_write_page((kv_page_location)back_page_location, KV_STORE_ID_BACKUP, page_index);\r
- if (res != ERRCODE_SUCC) {\r
- return res;\r
- }\r
- back_maybe_need_process_location = back_page_location;\r
- }\r
- back_page_location += KV_PAGE_SIZE;\r
- }\r
-\r
- /* 如果备份区没有初始化的页的数量为0, 说明备份区页不存在异常,正常重启 */\r
- /* 如果备份区未初始化的页的数量大于1, 说明是刚刚烧录状态,对备份区页头的初始化 */\r
- /* 如果备份区没有初始化的页的数量为1, 那么说明可能存在掉电异常场景进行处理 */\r
- if (backup_not_inited_count == 1) {\r
- res = kv_updata_backup_fail_process(back_maybe_need_process_location);\r
- if (res != ERRCODE_SUCC) {\r
- return res;\r
- }\r
- }\r
- return res;\r
-}\r
-#endif /* #if (CONFIG_NV_SUPPORT_BACKUP_RESTORE == NV_YES) */\r
-\r
-\r
-/* Top level function used to mark a single key in a store as invalid (and hence erased) */\r
-/* Will actually scan a store for all keys matching the specified key_id, to ensure they are all marked as erased */\r
-errcode_t kv_update_erase_key(kv_store_t core, flash_task_node *sanitised_tasks)\r
-{\r
- if (!active_state_machine()) {\r
- g_current_store = core;\r
-\r
- /* Configure search filter to obtain all valid instances of a specific key_id */\r
- g_search_filter.pattern = sanitised_tasks->data.kv_erase.key;\r
- g_search_filter.mask = 0xFFFF;\r
- g_search_filter.state = KV_KEY_FILTER_STATE_VALID;\r
- g_search_filter.type = KV_KEY_FILTER_TYPE_ANY;\r
- g_search_filter.location = 0;\r
-\r
- /* Queue Erase Keys state machine for processing */\r
- begin_state_machine(&g_erase_keys_machine);\r
- }\r
-\r
- /* Run state machine until it is either suspended (yielding due to flash contention) or */\r
- /* completed (either successfully or otherwise) */\r
- sanitised_tasks->state_code = determine_flash_task_state_code(process_state_machine());\r
-#ifdef CONFIG_NV_SUPPORT_SINGLE_CORE_SYSTEM\r
- if (sanitised_tasks->state_code == FLASH_TASK_COMPLETED) {\r
- return ERRCODE_SUCC;\r
- } else {\r
- return (errcode_t)sanitised_tasks->state_code;\r
- }\r
-#else\r
- return ERRCODE_SUCC;\r
-#endif\r
-}\r
-\r
-/* Top level function to write a new key or modify an existing key-value */\r
-/* Will cause a new instance of the key to be generated */\r
-errcode_t kv_update_write_key(kv_store_t core, flash_task_node *sanitised_task)\r
-{\r
- if (!active_state_machine()) {\r
- g_current_store = core;\r
-\r
- /* Log details of new key to be written */\r
- g_new_key_details.key_id = sanitised_task->data.kv.key;\r
- g_new_key_details.kvalue = sanitised_task->data.kv.kvalue;\r
- g_new_key_details.kvalue_length = sanitised_task->data.kv.kvalue_length;\r
- g_new_key_details.attributes = sanitised_task->data.kv.attribute;\r
- g_new_key_details.focre_write = sanitised_task->data.kv.force_write;\r
-\r
- /* Queue Write Key state machine for processing */\r
- begin_state_machine(&g_write_key_machine);\r
- }\r
-\r
- /* Run state machine until it is either suspended (yielding due to flash contention) or */\r
- /* completed (either successfully or otherwise) */\r
- sanitised_task->state_code = determine_flash_task_state_code(process_state_machine());\r
-#ifdef CONFIG_NV_SUPPORT_SINGLE_CORE_SYSTEM\r
- if (sanitised_task->state_code == FLASH_TASK_COMPLETED) {\r
- return ERRCODE_SUCC;\r
- } else {\r
- return (errcode_t)sanitised_task->state_code;\r
- }\r
-#else\r
- return ERRCODE_SUCC;\r
-#endif\r
-}\r
-\r
-/* Modify the attribute on an existing key-value */\r
-/* Top level function to modify the attributes of an existing key */\r
-/* Will cause a new instance of the key to be generated */\r
-/* Existing key attributes will be maintained */\r
-errcode_t kv_update_modify_attribute(kv_store_t core, flash_task_node *sanitised_task)\r
-{\r
- uint8_t *old_kvalue = NULL;\r
- errcode_t res;\r
- if (!active_state_machine()) {\r
- g_current_store = core;\r
- /* Log details of *additional* attributes to be applied to an existing key */\r
- g_new_key_details.key_id = sanitised_task->data.kv_attribute.key;\r
- g_new_key_details.kvalue = NULL;\r
- g_new_key_details.kvalue_length = 0;\r
- g_new_key_details.attributes = sanitised_task->data.kv_attribute.attribute;\r
- g_new_key_details.focre_write = false;\r
-\r
- res = kv_update_helper_get_current_key(&old_kvalue, &g_new_key_details.kvalue_length);\r
- if (res != ERRCODE_SUCC) {\r
- return res;\r
- }\r
- g_new_key_details.kvalue = old_kvalue;\r
- /* Queue Modify Key state machine for processing */\r
- begin_state_machine(&g_write_key_machine);\r
- }\r
-\r
- /* Run state machine until it is either suspended (yielding due to flash contention) or */\r
- /* completed (either successfully or otherwise) */\r
- sanitised_task->state_code = determine_flash_task_state_code(process_state_machine());\r
-#ifdef CONFIG_NV_SUPPORT_SINGLE_CORE_SYSTEM\r
- kv_free((void *)old_kvalue);\r
- if (sanitised_task->state_code == FLASH_TASK_COMPLETED) {\r
- return ERRCODE_SUCC;\r
- } else {\r
- return (errcode_t)sanitised_task->state_code;\r
- }\r
-#else\r
- if (sanitised_task->state_code != FLASH_TASK_BEING_PROCESSED) {\r
- kv_free((void *)old_kvalue);\r
- }\r
- return ERRCODE_SUCC;\r
-#endif\r
-}\r
-\r
+/*
+ * Copyright (c) @CompanyNameMagicTag 2019-2022. All rights reserved.
+ * Description: KV Storage Library flash updating module implementation
+ */
+
+#include "nv_update.h"
+#include "nv_store.h"
+#include "nv_page.h"
+#include "nv_key.h"
+#include "nv_porting.h"
+#include "nv_config.h"
+#include "nv_reset.h"
+#include "nv_nvregion.h"
+#if defined(CONFIG_PARTITION_FEATURE_SUPPORT)
+#include "partition.h"
+#endif
+#include "assert.h"
+#include "common_def.h"
+#include "uapi_crc.h"
+#ifndef CONFIG_NV_SUPPORT_SINGLE_CORE_SYSTEM
+#include "flash_task_mutex.h"
+#include "flash_task_adapt.h"
+#endif
+
+#define KV_WRITE_KEY_BUFFER_SIZE NV_KEY_DATA_CHUNK_LEN
+#define KV_WRITE_CHUNK_SIZE 32
+
+#define assert__(x) ((void)0)
+
+/* Pointer to current state machine being processed.
+ * Will point to the last instance in a list of kv_active_state_machine_t, or NULL if
+ * no state machine is being processed */
+STATIC kv_active_state_machine_t *g_current_state_machine = NULL;
+
+/* Holds a search filter being used by searching operations performed by state machines */
+STATIC kv_key_filter_t g_search_filter;
+
+/* Holds details of key data to be written to a store */
+STATIC kv_key_details_t g_new_key_details;
+
+/* The key within a KV store begin accessed by a state machine */
+STATIC kv_key_handle_t g_current_key;
+
+/* The flash page within a KV store begin accessed by a state machine */
+STATIC kv_page_handle_t g_current_page;
+
+/* The KV store begin accessed by a state machine */
+STATIC kv_store_t g_current_store;
+
+/* The position in a flash page where a new key will be written */
+STATIC kv_key_location g_current_store_write_location;
+
+/* Memory address of flash page being used to defrag an existing page into */
+STATIC kv_page_location g_defrag_page_location = NULL;
+
+
+STATIC errcode_t kv_create_write_buffer(uint32_t write_location, uint32_t size);
+STATIC void kv_remove_write_buffer(void);
+STATIC kv_managed_source_buffer_t* kv_update_source_buffer(uint8_t *data, uint32_t data_length);
+STATIC void kv_remove_source_buffer(void);
+STATIC void kv_release_crypto(void);
+STATIC errcode_t begin_state_machine(const kv_state_machine_t *machine);
+STATIC void end_state_machine(void);
+
+/* State machine action functions */
+STATIC kv_update_event_t kv_update_action_store_write_buffer(void);
+STATIC kv_update_event_t kv_update_action_prime_write_buffer(void);
+
+STATIC kv_update_event_t kv_update_action_select_first_page(void);
+STATIC kv_update_event_t kv_update_action_select_next_page(void);
+STATIC kv_update_event_t kv_update_action_find_first_key(void);
+STATIC kv_update_event_t kv_update_action_find_next_key(void);
+STATIC kv_update_event_t kv_update_action_prepare_delete_key(void);
+
+STATIC kv_update_event_t kv_update_action_find_defrag_page(void);
+STATIC kv_update_event_t kv_update_action_erase_defrag_page(void);
+STATIC kv_update_event_t kv_update_action_prepare_copy_key(void);
+
+STATIC kv_update_event_t kv_update_action_prepare_defrag_page(void);
+STATIC kv_update_event_t kv_update_action_copy_all_keys(void);
+STATIC kv_update_event_t kv_update_action_prepare_page_header(void);
+STATIC kv_update_event_t kv_update_action_find_write_position(void);
+STATIC kv_update_event_t kv_update_action_defrag_current_page(void);
+STATIC kv_update_event_t kv_update_action_update_nvregion_map(void);
+
+STATIC kv_update_event_t kv_update_action_prepare_store(void);
+STATIC kv_update_event_t kv_update_action_prepare_write_key(void);
+STATIC kv_update_event_t kv_update_action_claim_crypto(void);
+STATIC kv_update_event_t kv_update_action_erase_old_keys(void);
+
+STATIC kv_update_event_t kv_update_action_update_nvmap_for_erase_key(void);
+STATIC kv_update_event_t kv_update_action_update_nvmap_for_new_key(void);
+
+
+/* State machine to iterate through keys in a store marking all keys as erased
+ * - Uses a pre-configured filter to determine which keys to select for erasing
+ */
+static const kv_update_transition_t g_erase_keys_transitions[] = {
+ {.state = STATE_SELECT_FIRST_PAGE, .event = EVENT_PAGE_SELECTED, .next_state = STATE_FIND_FIRST_KEY },
+ {.state = STATE_SELECT_FIRST_PAGE, .event = EVENT_PAGE_NOT_SELECTED, .next_state = STATE_EXIT },
+ {.state = STATE_SELECT_NEXT_PAGE, .event = EVENT_PAGE_SELECTED, .next_state = STATE_FIND_FIRST_KEY },
+ {.state = STATE_SELECT_NEXT_PAGE, .event = EVENT_PAGE_NOT_SELECTED, .next_state = STATE_EXIT },
+ {.state = STATE_FIND_FIRST_KEY, .event = EVENT_KEY_FOUND, .next_state = STATE_PREP_DELETE_KEY },
+ {.state = STATE_FIND_FIRST_KEY, .event = EVENT_KEY_NOT_FOUND, .next_state = STATE_SELECT_NEXT_PAGE},
+ {.state = STATE_FIND_NEXT_KEY, .event = EVENT_KEY_FOUND, .next_state = STATE_PREP_DELETE_KEY },
+ {.state = STATE_FIND_NEXT_KEY, .event = EVENT_KEY_NOT_FOUND, .next_state = STATE_SELECT_NEXT_PAGE},
+ {.state = STATE_PREP_DELETE_KEY, .event = EVENT_WRITE_BUFFER_PRIMED, .next_state = STATE_PERFORM_WRITE },
+ {.state = STATE_PERFORM_WRITE, .event = EVENT_WRITE_BUFFER_STORED, .next_state = STATE_UPDATE_MAP_FOR_OLD },
+ {.state = STATE_UPDATE_MAP_FOR_OLD, .event = EVENT_PAGE_MAP_UPDATED, .next_state = STATE_FIND_NEXT_KEY },
+ {.state = STATE_PERFORM_WRITE, .event = EVENT_SUSPEND, .next_state = STATE_SUSPENDED },
+ {.state = STATE_EXIT, .event = EVENT_NONE, .next_state = STATE_EXIT }
+};
+static const kv_update_action_t g_erase_keys_actions[] = {
+ {.state = STATE_SELECT_FIRST_PAGE, .action = kv_update_action_select_first_page },
+ {.state = STATE_SELECT_NEXT_PAGE, .action = kv_update_action_select_next_page },
+ {.state = STATE_FIND_FIRST_KEY, .action = kv_update_action_find_first_key },
+ {.state = STATE_FIND_NEXT_KEY, .action = kv_update_action_find_next_key },
+ {.state = STATE_PREP_DELETE_KEY, .action = kv_update_action_prepare_delete_key},
+ {.state = STATE_PERFORM_WRITE, .action = kv_update_action_store_write_buffer},
+ {.state = STATE_UPDATE_MAP_FOR_OLD, .action = kv_update_action_update_nvmap_for_erase_key},
+ {.state = STATE_EXIT, .action = NULL }
+};
+static const kv_state_machine_t g_erase_keys_machine = {
+ .initial_state = STATE_SELECT_FIRST_PAGE,
+ .resume_state = STATE_PERFORM_WRITE,
+ .exit_event = EVENT_KEYS_ERASED,
+ .transition_table = g_erase_keys_transitions,
+ .action_table = g_erase_keys_actions,
+ .write_buffer_size = sizeof(uint32_t)
+};
+
+/*
+ * State machine to locate a defrag page
+ * - Scans NV Region for an unused page of flash
+ * - Sets g_defrag_page_location to point the unused flash page, thus declaring it to be the current defrag page
+ * - Ensures defrag page is erased
+ */
+static const kv_update_transition_t g_prep_defrag_page_transitions[] = {
+ {.state = STATE_FIND_DEFRAG, .event = EVENT_DEFRAG_FOUND, .next_state = STATE_ERASE_DEFRAG},
+ {.state = STATE_ERASE_DEFRAG, .event = EVENT_DEFRAG_ERASED, .next_state = STATE_EXIT },
+ {.state = STATE_ERASE_DEFRAG, .event = EVENT_SUSPEND, .next_state = STATE_SUSPENDED },
+ {.state = STATE_EXIT, .event = EVENT_NONE, .next_state = STATE_EXIT }
+};
+static const kv_update_action_t g_prep_defrag_page_actions[] = {
+ {.state = STATE_FIND_DEFRAG, .action = kv_update_action_find_defrag_page },
+ {.state = STATE_ERASE_DEFRAG, .action = kv_update_action_erase_defrag_page},
+ {.state = STATE_EXIT, .action = NULL }
+};
+static const kv_state_machine_t g_prep_defrag_page_machine = {
+ .initial_state = STATE_FIND_DEFRAG,
+ .resume_state = STATE_ERASE_DEFRAG,
+ .exit_event = EVENT_DEFRAG_PREPARED,
+ .transition_table = g_prep_defrag_page_transitions,
+ .action_table = g_prep_defrag_page_actions,
+ .write_buffer_size = 0
+};
+
+/*
+ * State machine to copy keys from the current page to the defrag page.
+ * Keys to copy are selected by a pre-configured filter, contained in g_search_filter
+ * Current page identified by g_current_page
+ * Defrag page identified by g_defrag_page_location
+ */
+static const kv_update_transition_t g_copy_all_keys_transitions[] = {
+ {.state = STATE_FIND_FIRST_KEY, .event = EVENT_KEY_FOUND, .next_state = STATE_PREP_COPY_KEY },
+ {.state = STATE_FIND_FIRST_KEY, .event = EVENT_KEY_NOT_FOUND, .next_state = STATE_EXIT },
+ {.state = STATE_FIND_NEXT_KEY, .event = EVENT_KEY_FOUND, .next_state = STATE_PREP_COPY_KEY },
+ {.state = STATE_FIND_NEXT_KEY, .event = EVENT_KEY_NOT_FOUND, .next_state = STATE_EXIT },
+ {.state = STATE_PREP_COPY_KEY, .event = EVENT_COPY_KEY_READY, .next_state = STATE_PRIME_WRITE },
+ {.state = STATE_PRIME_WRITE, .event = EVENT_WRITE_BUFFER_PRIMED, .next_state = STATE_PERFORM_WRITE },
+ {.state = STATE_PRIME_WRITE, .event = EVENT_WRITE_DATA_EXHAUSTED, .next_state = STATE_FIND_NEXT_KEY },
+ {.state = STATE_PERFORM_WRITE, .event = EVENT_WRITE_BUFFER_STORED, .next_state = STATE_PRIME_WRITE },
+ {.state = STATE_PERFORM_WRITE, .event = EVENT_SUSPEND, .next_state = STATE_SUSPENDED },
+ {.state = STATE_EXIT, .event = EVENT_NONE, .next_state = STATE_EXIT }
+};
+static const kv_update_action_t g_copy_all_keys_actions[] = {
+ {.state = STATE_FIND_FIRST_KEY, .action = kv_update_action_find_first_key },
+ {.state = STATE_FIND_NEXT_KEY, .action = kv_update_action_find_next_key },
+ {.state = STATE_PREP_COPY_KEY, .action = kv_update_action_prepare_copy_key },
+ {.state = STATE_PRIME_WRITE, .action = kv_update_action_prime_write_buffer},
+ {.state = STATE_PERFORM_WRITE, .action = kv_update_action_store_write_buffer},
+ {.state = STATE_EXIT, .action = NULL }
+};
+static const kv_state_machine_t g_copy_all_keys_machine = {
+ .initial_state = STATE_FIND_FIRST_KEY,
+ .resume_state = STATE_PERFORM_WRITE,
+ .exit_event = EVENT_ALL_KEYS_COPIED,
+ .transition_table = g_copy_all_keys_transitions,
+ .action_table = g_copy_all_keys_actions,
+ .write_buffer_size = KV_WRITE_KEY_BUFFER_SIZE
+};
+
+/*
+ * State machine to defrag a store page
+ * Makes use of the g_prep_defrag_page_machine state machine
+ * Makes use of the g_copy_all_keys_machine state machine
+ * - Locates an unused flash page in the NV Region and erased it
+ * - Copies valid keys from page being defragged to the new page
+ * - Writes an updated page header to top of the new page, so that it supercedes the recently defragged page
+ */
+static const kv_update_transition_t g_defrag_page_transitions[] = {
+ {.state = STATE_PREP_DEFRAG, .event = EVENT_DEFRAG_PREPARED, .next_state = STATE_COPY_ALL_KEYS },
+ {.state = STATE_COPY_ALL_KEYS, .event = EVENT_ALL_KEYS_COPIED, .next_state = STATE_PREP_PAGE_HEADER },
+ {.state = STATE_PREP_PAGE_HEADER, .event = EVENT_WRITE_BUFFER_PRIMED, .next_state = STATE_WRITE_PAGE_HEADER },
+ {.state = STATE_WRITE_PAGE_HEADER, .event = EVENT_WRITE_BUFFER_STORED, .next_state = STATE_UPDATE_NVREGION_MAP},
+ {.state = STATE_WRITE_PAGE_HEADER, .event = EVENT_SUSPEND, .next_state = STATE_SUSPENDED },
+ {.state = STATE_UPDATE_NVREGION_MAP, .event = EVENT_NVREGION_MAP_UPDATED, .next_state = STATE_EXIT },
+ {.state = STATE_EXIT, .event = EVENT_NONE, .next_state = STATE_EXIT }
+};
+static const kv_update_action_t g_defrag_page_actions[] = {
+ {.state = STATE_PREP_DEFRAG, .action = kv_update_action_prepare_defrag_page},
+ {.state = STATE_COPY_ALL_KEYS, .action = kv_update_action_copy_all_keys },
+ {.state = STATE_PREP_PAGE_HEADER, .action = kv_update_action_prepare_page_header},
+ {.state = STATE_WRITE_PAGE_HEADER, .action = kv_update_action_store_write_buffer },
+ {.state = STATE_UPDATE_NVREGION_MAP, .action = kv_update_action_update_nvregion_map},
+ {.state = STATE_EXIT, .action = NULL }
+};
+static const kv_state_machine_t g_defrag_page_machine = {
+ .initial_state = STATE_PREP_DEFRAG,
+ .resume_state = STATE_WRITE_PAGE_HEADER,
+ .exit_event = EVENT_DEFRAG_COMPLETE,
+ .transition_table = g_defrag_page_transitions,
+ .action_table = g_defrag_page_actions,
+ .write_buffer_size = sizeof(kv_page_header_t)
+};
+
+/*
+ * State machine to prepare a store for writing a key
+ * May make use of the g_defrag_page_machine state machine
+ * - Selects a suitable page in a store for receiving a key
+ * - Kicks off a defrag of the selected page if necessary
+ */
+static const kv_update_transition_t g_prepare_store_transitions[] = {
+ {.state = STATE_FIND_WRITE_POS, .event = EVENT_DEFRAG_REQUIRED, .next_state = STATE_DEFRAG_PAGE },
+ {.state = STATE_FIND_WRITE_POS, .event = EVENT_WRITE_POS_FOUND, .next_state = STATE_EXIT },
+ {.state = STATE_DEFRAG_PAGE, .event = EVENT_DEFRAG_COMPLETE, .next_state = STATE_FIND_WRITE_POS},
+ {.state = STATE_EXIT, .event = EVENT_NONE, .next_state = STATE_EXIT }
+};
+static const kv_update_action_t g_prepare_store_actions[] = {
+ {.state = STATE_FIND_WRITE_POS, .action = kv_update_action_find_write_position},
+ {.state = STATE_DEFRAG_PAGE, .action = kv_update_action_defrag_current_page},
+ {.state = STATE_EXIT, .action = NULL }
+};
+STATIC const kv_state_machine_t g_prepare_store_machine = {
+ .initial_state = STATE_FIND_WRITE_POS,
+ .resume_state = STATE_INVALID,
+ .exit_event = EVENT_STORE_READY,
+ .transition_table = g_prepare_store_transitions,
+ .action_table = g_prepare_store_actions,
+ .write_buffer_size = 0
+};
+
+/*
+ * State machine to write a key to a store
+ * Makes use of the g_prepare_store_machine state machine
+ */
+static const kv_update_transition_t g_write_key_transitions[] = {
+ {.state = STATE_PREP_STORE, .event = EVENT_STORE_READY, .next_state = STATE_PREP_KEY_DATA },
+ {.state = STATE_PREP_KEY_DATA, .event = EVENT_KEY_UPDATE_NOT_NEEDED, .next_state = STATE_EXIT },
+ {.state = STATE_PREP_KEY_DATA, .event = EVENT_KEY_DATA_READY, .next_state = STATE_CLAIM_CRYPTO },
+ {.state = STATE_CLAIM_CRYPTO, .event = EVENT_CRYPTO_CLAIMED, .next_state = STATE_PRIME_WRITE },
+ {.state = STATE_PRIME_WRITE, .event = EVENT_WRITE_BUFFER_PRIMED, .next_state = STATE_PERFORM_WRITE },
+ {.state = STATE_PRIME_WRITE, .event = EVENT_WRITE_DATA_EXHAUSTED, .next_state = STATE_UPDATE_MAP_FOR_NEW},
+ {.state = STATE_UPDATE_MAP_FOR_NEW, .event = EVENT_PAGE_MAP_UPDATED, .next_state = STATE_ERASE_OLD_KEYS },
+ {.state = STATE_PERFORM_WRITE, .event = EVENT_WRITE_BUFFER_STORED, .next_state = STATE_PRIME_WRITE },
+ {.state = STATE_PERFORM_WRITE, .event = EVENT_SUSPEND, .next_state = STATE_SUSPENDED },
+ {.state = STATE_ERASE_OLD_KEYS, .event = EVENT_KEYS_ERASED, .next_state = STATE_EXIT },
+ {.state = STATE_EXIT, .event = EVENT_NONE, .next_state = STATE_EXIT }
+};
+static const kv_update_action_t g_write_key_actions[] = {
+ {.state = STATE_PREP_STORE, .action = kv_update_action_prepare_store },
+ {.state = STATE_PREP_KEY_DATA, .action = kv_update_action_prepare_write_key },
+ {.state = STATE_CLAIM_CRYPTO, .action = kv_update_action_claim_crypto },
+ {.state = STATE_PRIME_WRITE, .action = kv_update_action_prime_write_buffer},
+ {.state = STATE_PERFORM_WRITE, .action = kv_update_action_store_write_buffer},
+ {.state = STATE_UPDATE_MAP_FOR_NEW, .action = kv_update_action_update_nvmap_for_new_key},
+ {.state = STATE_ERASE_OLD_KEYS, .action = kv_update_action_erase_old_keys },
+ {.state = STATE_EXIT, .action = NULL }
+};
+static const kv_state_machine_t g_write_key_machine = {
+ .initial_state = STATE_PREP_STORE,
+ .resume_state = STATE_CLAIM_CRYPTO,
+ .exit_event = EVENT_WRITE_COMPLETE,
+ .transition_table = g_write_key_transitions,
+ .action_table = g_write_key_actions,
+ .write_buffer_size = KV_WRITE_KEY_BUFFER_SIZE
+};
+
+/* Attempts to write all data in a write buffer to flash */
+STATIC kv_update_event_t kv_update_action_store_write_buffer(void)
+{
+ errcode_t ret;
+ uint32_t read_location;
+ uint32_t write_length;
+ uint32_t written;
+
+ kv_managed_write_buffer_t *write_buffer = g_current_state_machine->write_buffer;
+
+ /* Skip over section of write buffer that has already been written, due to being suspended */
+ if (write_buffer->write_location < write_buffer->resume_location) {
+ uint32_t data_already_written = write_buffer->resume_location - write_buffer->write_location;
+ if (data_already_written > write_buffer->data_length) {
+ data_already_written = write_buffer->data_length;
+ }
+ write_buffer->data_consumed += (uint16_t)data_already_written;
+ write_buffer->write_location += data_already_written;
+ }
+
+ /* Attempt to write chunks of data from the write_buffer to flash */
+ while (write_buffer->data_consumed < write_buffer->data_length) {
+ /* Determine size and position of chunk to write */
+ read_location = (uint32_t)(uintptr_t)write_buffer->data + write_buffer->data_consumed;
+ write_length = uapi_min(KV_WRITE_CHUNK_SIZE, write_buffer->data_length - write_buffer->data_consumed);
+
+ /* Attempt to write chunk */
+ ret = kv_key_write_flash(write_buffer->write_location, write_length, (uint8_t *)(uintptr_t)read_location);
+ if (ret == ERRCODE_SUCC) {
+ written = write_length;
+ } else {
+ written = 0;
+ }
+ write_buffer->data_consumed += (uint16_t)written;
+ /* Update write position, automatically allowing for subsequent writes to occur sequentially in flash */
+ write_buffer->write_location += written;
+ if (written < write_length) {
+ /* Write was aborted early */
+ write_buffer->resume_location = write_buffer->write_location;
+ return EVENT_SUSPEND;
+ }
+ }
+ /* Write completed */
+ return EVENT_WRITE_BUFFER_STORED;
+}
+STATIC errcode_t kv_update_hash_encrypt_chunk(kv_managed_write_buffer_t *write_buffer,
+ const kv_managed_source_buffer_t *source_buffer, uint32_t chunk_dest, uint32_t chunk_len)
+{
+ /* Need to cal CRC? */
+ errcode_t ret = ERRCODE_SUCC;
+ if (source_buffer->crc_data) {
+ if (!write_buffer->crc_claimed) {
+ return ERRCODE_NV_HASH_UNAVAILABLE;
+ }
+ write_buffer->crc_ret = uapi_crc32(write_buffer->crc_ret, (const uint8_t *)(uintptr_t)chunk_dest,
+ chunk_len);
+ }
+
+#if (CONFIG_NV_SUPPORT_ENCRYPT == NV_YES)
+ /* Need to hash data? */
+ if (source_buffer->hash_data) {
+ if (!write_buffer->hash_claimed) {
+ return ERRCODE_NV_HASH_UNAVAILABLE;
+ }
+ nv_crypto_update_hash((const uint8_t *)(uintptr_t)chunk_dest, chunk_len);
+ }
+
+ /* Need to encrypt data? */
+ if (source_buffer->encrypt_data) {
+ if (!write_buffer->encrypt_claimed) {
+ return ERRCODE_NV_AES_UNAVAILABLE;
+ }
+
+ ret = nv_crypto_encode(write_buffer->crypto_handle, (uintptr_t)chunk_dest, (uintptr_t)chunk_dest, chunk_len);
+ }
+
+ if (source_buffer->gcm_tag_data || source_buffer->hash_data) {
+ if ((source_buffer->gcm_tag_data && !write_buffer->gcm_tag_claimed) ||
+ (source_buffer->hash_data && !write_buffer->hash_claimed)) {
+ return ERRCODE_NV_HASH_UNAVAILABLE;
+ }
+ write_buffer->crc_ret = uapi_crc32(write_buffer->crc_ret, (const uint8_t *)(uintptr_t)chunk_dest,
+ chunk_len);
+ }
+#endif
+ return ret;
+}
+
+/* Copy data from the selected source buffer to the write_buffer, hashing and encrypting the data as necessary */
+STATIC errcode_t prime_next_chunk(void)
+{
+ kv_managed_source_buffer_t *source_buffer = g_current_state_machine->current_source;
+ kv_managed_write_buffer_t *write_buffer = g_current_state_machine->write_buffer;
+
+ uint32_t remaining_dest_space = write_buffer->size - write_buffer->data_length;
+ uint32_t remaining_src_data = source_buffer->data_length - source_buffer->data_consumed;
+ uint16_t chunk_len = (uint16_t)uapi_min(remaining_dest_space, remaining_src_data);
+
+ uint32_t chunk_dest = (uint32_t)(uintptr_t)write_buffer->data + write_buffer->data_length;
+ uint32_t chunk_src = (uint32_t)(uintptr_t)source_buffer->data + source_buffer->data_consumed;
+
+ if (source_buffer->encrypt_data) {
+ /* For encrypted data, the length of each operation is the same as that of NV reading.
+ * CNCommont: 对于加密数据,每次操作的数据长度与读NV时保持一致(KEY_DATA_CHUNK_LEN),因此当write_buffer剩余空间
+ * 小于KEY_DATA_CHUNK_LEN时,暂不处理.
+ */
+ chunk_len = (uint16_t)uapi_min(chunk_len, NV_KEY_DATA_CHUNK_LEN);
+ if (remaining_dest_space < NV_KEY_DATA_CHUNK_LEN) {
+ return ERRCODE_NV_BUFFER_PRIMED_PREMATURELY;
+ }
+ }
+
+ kv_attributes_t attributes = (source_buffer->encrypt_data) ? KV_ATTRIBUTE_ENCRYPTED : 0;
+ if (remaining_dest_space < kv_key_padded_data_length(attributes, chunk_len)) {
+ /* Not enough space for padded data, exit early and write what we have */
+ return ERRCODE_NV_BUFFER_PRIMED_PREMATURELY;
+ }
+
+ /* Copy a chunk of source_buffer data to write buffer */
+ if ((chunk_src >= FLASH_PHYSICAL_ADDR_START) &&
+ (chunk_src + chunk_len) <= FLASH_PHYSICAL_ADDR_END) {
+ /* Source buffer is referencing data in Flash */
+ errcode_t res = kv_key_helper_copy_flash(chunk_dest, chunk_src, chunk_len);
+ if (res != ERRCODE_SUCC) {
+ return res;
+ }
+ } else {
+ int32_t result;
+ /* Source buffer is referencing data in RAM */
+ result = memcpy_s((void *)(uintptr_t)chunk_dest, remaining_dest_space,
+ (const void *)(uintptr_t)chunk_src, chunk_len);
+ if (result != (int32_t)EOK) {
+ assert__(false);
+ }
+ }
+
+ /* Round chunk_len up to a multiple of 4 or 16 bytes, depending upon the encryption requirement */
+ /* We have already checked there is enough space in the write buffer */
+ chunk_len = kv_key_padded_data_length(attributes, chunk_len);
+
+ errcode_t hash_crypt_ret;
+ hash_crypt_ret = kv_update_hash_encrypt_chunk(write_buffer, source_buffer, chunk_dest, chunk_len);
+ if (hash_crypt_ret != ERRCODE_SUCC) {
+ return hash_crypt_ret;
+ }
+
+ write_buffer->data_length += chunk_len;
+ source_buffer->data_consumed += chunk_len;
+ return ERRCODE_SUCC;
+}
+
+/* Select next source buffer, filling it with a hash if required */
+STATIC errcode_t select_next_source_buffer(void)
+{
+ kv_managed_source_buffer_t *source_buffer = g_current_state_machine->current_source->next;
+ kv_managed_write_buffer_t *write_buffer = g_current_state_machine->write_buffer;
+
+ g_current_state_machine->current_source = source_buffer;
+
+ /* Has the next source_buffer buffer been designated to receive the hash calculation */
+ if ((source_buffer != NULL) && (source_buffer->receive_hash)) {
+ if (write_buffer->crc_claimed) {
+ if (source_buffer->data_length < KV_CRYPTO_CRC_SIZE) {
+ return ERRCODE_NV_KEY_HASH_BUFFER_TOO_SMALL;
+ }
+
+ write_buffer->crc_ret = kv_crc32_swap(write_buffer->crc_ret);
+ (void)memcpy_s((void *)source_buffer->data, KV_CRYPTO_CRC_SIZE, (const void *)&write_buffer->crc_ret,
+ KV_CRYPTO_CRC_SIZE);
+ write_buffer->crc_claimed = false;
+ source_buffer->data_length = KV_CRYPTO_CRC_SIZE;
+ }
+#if (CONFIG_NV_SUPPORT_ENCRYPT == NV_YES)
+ else if (write_buffer->hash_claimed) {
+ if (source_buffer->data_length < KV_CRYPTO_HASH_SIZE) {
+ return ERRCODE_NV_KEY_HASH_BUFFER_TOO_SMALL;
+ }
+ (void)memset_s(source_buffer->data, source_buffer->data_length, 0, source_buffer->data_length);
+ nv_crypto_complete_hash(source_buffer->data);
+
+ write_buffer->crc_ret = kv_crc32_swap(write_buffer->crc_ret);
+ (void)memcpy_s((void *)(source_buffer->data + KV_CRYPTO_HASH_SIZE - KV_CRYPTO_CRC_SIZE),
+ KV_CRYPTO_CRC_SIZE, (const void *)&write_buffer->crc_ret, KV_CRYPTO_CRC_SIZE);
+
+ write_buffer->hash_claimed = false;
+ source_buffer->data_length = KV_CRYPTO_HASH_SIZE;
+ } else if (write_buffer->gcm_tag_claimed) {
+ uint32_t tag_len = NV_AES_GCM_TAG_LENGTH;
+ if (source_buffer->data_length < KV_CRYPTO_HASH_SIZE) {
+ return ERRCODE_NV_KEY_HASH_BUFFER_TOO_SMALL;
+ }
+
+ write_buffer->crc_ret = kv_crc32_swap(write_buffer->crc_ret);
+ memset_s(source_buffer->data, source_buffer->data_length, 0, source_buffer->data_length);
+
+ nv_crypto_get_tag(write_buffer->crypto_handle, source_buffer->data, &tag_len);
+
+ (void)memcpy_s((void *)(source_buffer->data + KV_CRYPTO_HASH_SIZE - KV_CRYPTO_CRC_SIZE),
+ KV_CRYPTO_CRC_SIZE, (const void *)&write_buffer->crc_ret, KV_CRYPTO_CRC_SIZE);
+
+ write_buffer->gcm_tag_claimed = false;
+ source_buffer->data_length = KV_CRYPTO_HASH_SIZE;
+ }
+#endif
+ else {
+ return ERRCODE_NV_HASH_UNAVAILABLE;
+ }
+ }
+
+ return ERRCODE_SUCC;
+}
+
+/* Gather data from one or more source buffers to fill write_buffer with more data ready for writing to flash */
+STATIC kv_update_event_t kv_update_action_prime_write_buffer(void)
+{
+ errcode_t res;
+
+ kv_managed_source_buffer_t *source_buffer = g_current_state_machine->current_source;
+ kv_managed_write_buffer_t *write_buffer = g_current_state_machine->write_buffer;
+
+ (void)memset_s(write_buffer->data, write_buffer->size, 0, write_buffer->size);
+
+ write_buffer->data_length = 0;
+ write_buffer->data_consumed = 0;
+
+ while ((source_buffer != NULL) && (write_buffer->data_length < write_buffer->size)) {
+ if (source_buffer->data_consumed < source_buffer->data_length) {
+ res = prime_next_chunk();
+ if (res == ERRCODE_NV_BUFFER_PRIMED_PREMATURELY) {
+ /* Not enough space for padded data so just write what we have for now */
+ break;
+ }
+ if (res != ERRCODE_SUCC) {
+ g_current_state_machine->error_code = res;
+ return EVENT_ERROR;
+ }
+ } else {
+ /* Select next source buffer */
+ res = select_next_source_buffer();
+ if (res != ERRCODE_SUCC) {
+ g_current_state_machine->error_code = res;
+ return EVENT_ERROR;
+ }
+ source_buffer = g_current_state_machine->current_source;
+ }
+ }
+
+ /* Did we manage to place any data in the write buffer? */
+ if (write_buffer->data_length == 0) {
+ kv_release_crypto();
+ return EVENT_WRITE_DATA_EXHAUSTED;
+ } else {
+ return EVENT_WRITE_BUFFER_PRIMED;
+ }
+}
+
+/*
+ * Attempts to obtain a handle to the first page of the g_current_store
+ * - g_current_page will be populated if a page is obtained
+ */
+STATIC kv_update_event_t kv_update_action_select_first_page(void)
+{
+ errcode_t res;
+
+ res = kv_store_get_page_handle(g_current_store, 0, &g_current_page);
+ if (res == ERRCODE_SUCC) {
+ return EVENT_PAGE_SELECTED;
+ }
+ return EVENT_PAGE_NOT_SELECTED;
+}
+
+/*
+ * Attempts to obtain a handle to the next page of the g_current_store, based on g_current_page
+ * - g_current_page will be updated if a page is obtained
+ */
+STATIC kv_update_event_t kv_update_action_select_next_page(void)
+{
+ errcode_t res;
+ uint32_t page_index;
+ if (kv_page_get_index(&g_current_page, &page_index) != ERRCODE_SUCC) {
+ return EVENT_PAGE_NOT_SELECTED;
+ }
+ page_index++;
+ if (page_index < kv_store_get_page_count(g_current_store)) {
+ res = kv_store_get_page_handle(g_current_store, page_index, &g_current_page);
+ if (res == ERRCODE_SUCC) {
+ return EVENT_PAGE_SELECTED;
+ }
+ }
+
+ return EVENT_PAGE_NOT_SELECTED;
+}
+
+/*
+ * Attempts to obtain the first key in the g_current_page, that conforms to g_search_filter
+ * - g_current_key will be populated if a key is obtained
+ */
+STATIC kv_update_event_t kv_update_action_find_first_key(void)
+{
+ errcode_t res;
+
+ res = kv_page_find_first_key(&g_current_page, &g_search_filter, &g_current_key);
+ while (res == ERRCODE_SUCC) {
+ if (g_current_key.key_location != g_search_filter.location) {
+ return EVENT_KEY_FOUND;
+ }
+ res = kv_page_find_next_key(&g_current_page, &g_search_filter, &g_current_key);
+ }
+ return EVENT_KEY_NOT_FOUND;
+}
+
+/*
+ * Attempts to obtain the next key in the g_current_page, based on g_current_key and that conforms to g_search_filter
+ * - g_current_key will be updated if a key is obtained
+ */
+STATIC kv_update_event_t kv_update_action_find_next_key(void)
+{
+ errcode_t res;
+
+ res = kv_page_find_next_key(&g_current_page, &g_search_filter, &g_current_key);
+ while (res == ERRCODE_SUCC) {
+ if (g_current_key.key_location != g_search_filter.location) {
+ return EVENT_KEY_FOUND;
+ }
+ res = kv_page_find_next_key(&g_current_page, &g_search_filter, &g_current_key);
+ }
+ return EVENT_KEY_NOT_FOUND;
+}
+
+/*
+ * Prepares the g_current_state_machine->write_buffer to mark g_current_key as invalid (and hence erased)
+ * Will not mark keys with the attribute KV_ATTRIBUTE_PERMANENT as invalid
+ */
+STATIC kv_update_event_t kv_update_action_prepare_delete_key(void)
+{
+ kv_managed_write_buffer_t *write_buffer = NULL;
+ kv_attributes_t attributes = kv_key_attributes(&g_current_key);
+ if (((uint32_t)attributes & (uint32_t)KV_ATTRIBUTE_PERMANENT) != 0 &&
+ (g_new_key_details.focre_write != true)) {
+ g_current_state_machine->error_code = ERRCODE_NV_TRYING_TO_MODIFY_A_PERMANENT_KEY;
+ return EVENT_ERROR;
+ }
+
+ write_buffer = g_current_state_machine->write_buffer;
+ if (write_buffer == NULL) {
+ g_current_state_machine->error_code = ERRCODE_NV_WRITE_BUFFER_NOT_ALLOCATED;
+ return EVENT_ERROR;
+ }
+ if (write_buffer->size < sizeof(uint32_t)) {
+ g_current_state_machine->error_code = ERRCODE_NV_WRITE_BUFFER_TOO_SMALL;
+ return EVENT_ERROR;
+ }
+
+ /* As only 4 bytes are being written, directly prime write buffer rather that setting up a source buffer */
+ *(uint32_t *)write_buffer->data = KV_KEY_INVALID;
+ write_buffer->data_consumed = 0;
+
+ if (g_nv_header_magic == KV_KEY_MAGIC) {
+ write_buffer->data_length = (uint16_t)sizeof(uint8_t);
+ /* Need to manually configure the write location for each key being deleted */
+ /* as they will not follow in an ordered sequence */
+ write_buffer->write_location = (uint32_t)(uintptr_t)((uint8_t *)g_current_key.key_location + sizeof(uint8_t));
+ } else {
+ write_buffer->data_length = (uint16_t)sizeof(uint16_t);
+ write_buffer->write_location = (uint32_t)(uintptr_t)g_current_key.key_location;
+ }
+
+ return EVENT_WRITE_BUFFER_PRIMED;
+}
+
+STATIC kv_update_event_t kv_update_action_prepare_copy_key(void)
+{
+ kv_managed_write_buffer_t *write_buffer = g_current_state_machine->write_buffer;
+ if (write_buffer == NULL) {
+ g_current_state_machine->error_code = ERRCODE_NV_WRITE_BUFFER_NOT_ALLOCATED;
+ return EVENT_ERROR;
+ }
+ if (write_buffer->write_location == 0) {
+ write_buffer->write_location = (uint32_t)(uintptr_t)g_defrag_page_location + (uint32_t)sizeof(kv_page_header_t);
+ }
+
+ /* Use a source buffer to manage copy progress of key data */
+ uint8_t *source = (uint8_t *)g_current_key.key_location;
+ uint16_t length = kv_key_flash_size(&g_current_key);
+ kv_managed_source_buffer_t *source_buffer = kv_update_source_buffer(source, length);
+ g_current_state_machine->current_source = source_buffer; /* Point current source buffer at first buffer */
+ if (source_buffer == NULL) {
+ g_current_state_machine->error_code = ERRCODE_MALLOC;
+ return EVENT_ERROR;
+ }
+
+ return EVENT_COPY_KEY_READY;
+}
+
+/*
+ * Attempts to locate an unused page of flash, in the KV NV flash region, to use as the current defrag page
+ * g_defrag_page_location will be set to point to the unused flash page
+ */
+STATIC kv_update_event_t kv_update_action_find_defrag_page(void)
+{
+ errcode_t res;
+ g_defrag_page_location = NULL;
+ res = kv_nvregion_find_unused_page(&g_defrag_page_location);
+ if (res == ERRCODE_SUCC) {
+ nv_log_debug("[NV] found defrag_page. loc = 0x%x\r\n", g_defrag_page_location);
+ return EVENT_DEFRAG_FOUND;
+ } else {
+ g_current_state_machine->error_code = res;
+ return EVENT_ERROR;
+ }
+}
+
+/*
+ * Attempts to erase the current defrag page, as defined by g_defrag_page_location
+ */
+STATIC kv_update_event_t kv_update_action_erase_defrag_page(void)
+{
+ errcode_t res;
+ res = kv_nvregion_erase_page(g_defrag_page_location);
+ if (res == ERRCODE_SUCC) {
+ return EVENT_DEFRAG_ERASED;
+ } else if (res == ERRCODE_NV_WRITE_VETOED) {
+ return EVENT_SUSPEND;
+ } else {
+ g_current_state_machine->error_code = res;
+ return EVENT_ERROR;
+ }
+}
+
+/*
+ * Attempts to start the g_prep_defrag_page_machine state machine
+ */
+STATIC kv_update_event_t kv_update_action_prepare_defrag_page(void)
+{
+ errcode_t res = begin_state_machine(&g_prep_defrag_page_machine);
+ if (res == ERRCODE_SUCC) {
+ return EVENT_NONE;
+ } else {
+ g_current_state_machine->error_code = res;
+ return EVENT_ERROR;
+ }
+}
+
+/*
+ * Attempts to start the g_copy_all_keys_machine state machine
+ * Configures g_search_filter to find all valid (un-erased) keys
+ */
+STATIC kv_update_event_t kv_update_action_copy_all_keys(void)
+{
+ /* Configure search filter to copy all valid keys */
+ g_search_filter.pattern = 0;
+ g_search_filter.mask = 0;
+ g_search_filter.state = KV_KEY_FILTER_STATE_VALID;
+ g_search_filter.type = KV_KEY_FILTER_TYPE_ANY;
+ g_search_filter.location = 0;
+
+ errcode_t res = begin_state_machine(&g_copy_all_keys_machine);
+ if (res == ERRCODE_SUCC) {
+ return EVENT_NONE;
+ } else {
+ g_current_state_machine->error_code = res;
+ return EVENT_ERROR;
+ }
+}
+
+/*
+ * Prepares g_current_state_machine->write_buffer with information to construct a page header for the
+ * current defrag page, as defined by g_defrag_page_location
+ * All information is obtained from g_current_page
+ * The page sequence_number is updated to indicate that this page will supersede g_current_page
+ */
+STATIC kv_update_event_t kv_update_action_prepare_page_header(void)
+{
+ kv_managed_write_buffer_t *write_buffer = g_current_state_machine->write_buffer;
+ if (write_buffer == NULL) {
+ g_current_state_machine->error_code = ERRCODE_NV_WRITE_BUFFER_NOT_ALLOCATED;
+ return EVENT_ERROR;
+ }
+ write_buffer->write_location = (uint32_t)(uintptr_t)g_defrag_page_location;
+
+ if (memcpy_s(write_buffer->data, sizeof(kv_page_header_t), &g_current_page, sizeof(kv_page_header_t)) != EOK) {
+ return EVENT_ERROR;
+ }
+ write_buffer->data_length = (uint16_t)sizeof(kv_page_header_t);
+ write_buffer->data_consumed = 0;
+
+ /* Too specific, move to kv_page? */
+ kv_page_header_t *defrag_page_header = (kv_page_header_t *)write_buffer->data;
+ defrag_page_header->sequence_number++;
+ defrag_page_header->inverted_sequence_number = ~defrag_page_header->sequence_number;
+
+ return EVENT_WRITE_BUFFER_PRIMED;
+}
+
+/*
+ * Forces a re-scan of the KV NV flash region to update a map of KV pages
+ */
+STATIC kv_update_event_t kv_update_action_update_nvregion_map(void)
+{
+ errcode_t res;
+
+ res = kv_nvregion_scan();
+ if (res != ERRCODE_SUCC) {
+ g_current_state_machine->error_code = res;
+ return EVENT_ERROR;
+ }
+ return EVENT_NVREGION_MAP_UPDATED;
+}
+
+/*
+ * Attempts to find a suitable write location in g_current_store for g_new_key_details
+ * Will set g_current_store_write_location if enough space is found in g_current_store
+ * Will indicate that a defrag is required if there is not currently enough free space in g_current_store
+ * Will return an error if a defrag would not free up enough space for the new key
+ */
+STATIC kv_update_event_t kv_update_action_find_write_position(void)
+{
+ errcode_t res;
+ uint16_t required_space;
+ kv_key_handle_t new_key;
+ kv_page_status_t page_status;
+
+ /* Work out space needed for new key */
+ kv_key_build_from_new(&new_key, &g_new_key_details, 0);
+ required_space = kv_key_flash_size(&new_key);
+
+ res = kv_store_find_write_page(g_current_store, required_space, &g_current_page, &page_status);
+ if (res == ERRCODE_SUCC) {
+ g_current_store_write_location = (kv_key_location)(uintptr_t)page_status.first_writable_location;
+ return EVENT_WRITE_POS_FOUND;
+ } else if (res == ERRCODE_NV_DEFRAGMENTATION_NEEDED) {
+ return EVENT_DEFRAG_REQUIRED;
+ } else {
+ g_current_state_machine->error_code = res;
+ return EVENT_ERROR;
+ }
+}
+
+/*
+ * Attempts to start the g_defrag_page_machine state machine
+ */
+STATIC kv_update_event_t kv_update_action_defrag_current_page(void)
+{
+ errcode_t res = begin_state_machine(&g_defrag_page_machine);
+ if (res == ERRCODE_SUCC) {
+ return EVENT_NONE;
+ } else {
+ g_current_state_machine->error_code = res;
+ return EVENT_ERROR;
+ }
+}
+
+/*
+ * Attempts to start the g_prepare_store_machine state machine
+ */
+STATIC kv_update_event_t kv_update_action_prepare_store(void)
+{
+ errcode_t res = begin_state_machine(&g_prepare_store_machine);
+ if (res == ERRCODE_SUCC) {
+ return EVENT_NONE;
+ } else {
+ g_current_state_machine->error_code = res;
+ return EVENT_ERROR;
+ }
+}
+
+/*
+ * Configure write location for g_current_state_machine->write_buffer
+ * Attempt to allocate memory for AES Control data structure, if key data is to be encrypted
+ */
+STATIC kv_update_event_t kv_update_helper_prepare_write_buffer(void)
+{
+ kv_managed_write_buffer_t *write_buffer = g_current_state_machine->write_buffer;
+
+ if (write_buffer == NULL) {
+ g_current_state_machine->error_code = ERRCODE_NV_WRITE_BUFFER_NOT_ALLOCATED;
+ return EVENT_ERROR;
+ }
+ write_buffer->write_location = (uint32_t)(uintptr_t)g_current_store_write_location;
+ return EVENT_KEY_DATA_READY;
+}
+
+/*
+ * Attempt to allocate a kv_managed_source_buffer_t for a key header
+ */
+STATIC kv_update_event_t kv_update_helper_setup_key_header_source_buffer(void)
+{
+ kv_managed_source_buffer_t *source_buffer = NULL;
+
+ /* Setup source buffers for key header */
+ source_buffer = kv_update_source_buffer((uint8_t *)&g_current_key.header, sizeof(kv_key_header_t));
+ if (source_buffer == NULL) {
+ g_current_state_machine->error_code = ERRCODE_NV_KEY_HEADER_BUFFER_NOT_ALLOCATED;
+ return EVENT_ERROR;
+ }
+
+ if (((uint32_t)g_new_key_details.attributes & (uint32_t)KV_ATTRIBUTE_ENCRYPTED) != 0) {
+#if (CONFIG_NV_SUPPORT_HASH_FOR_CRYPT == NV_YES)
+ source_buffer->hash_data = true;
+#else
+ source_buffer->gcm_tag_data = true;
+#endif
+ } else {
+ source_buffer->crc_data = true;
+ }
+ source_buffer->encrypt_data = false;
+ return EVENT_KEY_DATA_READY;
+}
+
+/*
+ * Attempt to allocate a kv_managed_source_buffer_t for key data
+ */
+STATIC kv_update_event_t kv_update_helper_setup_key_data_source_buffer(bool release_key_data)
+{
+ kv_managed_source_buffer_t *source_buffer = NULL;
+
+ /* Setup source buffer for key data */
+ source_buffer = kv_update_source_buffer((uint8_t *)g_new_key_details.kvalue, g_new_key_details.kvalue_length);
+ if (source_buffer == NULL) {
+ g_current_state_machine->error_code = ERRCODE_NV_KEY_DATA_BUFFER_NOT_ALLOCATED;
+ return EVENT_ERROR;
+ }
+ source_buffer->release_data = release_key_data;
+
+ if (((uint32_t)g_new_key_details.attributes & (uint32_t)KV_ATTRIBUTE_ENCRYPTED) != 0) {
+#if (CONFIG_NV_SUPPORT_HASH_FOR_CRYPT == NV_YES)
+ source_buffer->hash_data = true;
+#else
+ source_buffer->gcm_tag_data = true;
+#endif
+ } else {
+ source_buffer->crc_data = true;
+ }
+ source_buffer->encrypt_data = (((uint32_t)g_new_key_details.attributes & (uint32_t)KV_ATTRIBUTE_ENCRYPTED) != 0);
+ return EVENT_KEY_DATA_READY;
+}
+
+/*
+ * Attempt to allocate a kv_managed_source_buffer_t for a key hash
+ * Will also attempt to allocate a buffer to contain the calculated hash
+ */
+STATIC kv_update_event_t kv_update_helper_setup_key_hash_source_buffer(void)
+{
+ uint8_t *key_hash = NULL;
+ kv_managed_source_buffer_t *source_buffer = NULL;
+ uint32_t hash_crc_len = 0;
+ if (((uint32_t)g_new_key_details.attributes & (uint32_t)KV_ATTRIBUTE_ENCRYPTED) != 0) {
+ hash_crc_len = KV_CRYPTO_HASH_SIZE;
+ } else {
+ hash_crc_len = KV_CRYPTO_CRC_SIZE;
+ }
+
+ /* Setup source buffer for key hash */
+ /* The hash is calculated while data is written to flash then placed into this buffer for writing */
+ key_hash = (uint8_t *)kv_malloc(hash_crc_len);
+ if (key_hash == NULL) {
+ g_current_state_machine->error_code = ERRCODE_MALLOC;
+ return EVENT_ERROR;
+ }
+ source_buffer = kv_update_source_buffer(key_hash, hash_crc_len);
+ if (source_buffer == NULL) {
+ kv_free(key_hash);
+ g_current_state_machine->error_code = ERRCODE_NV_KEY_HASH_BUFFER_NOT_ALLOCATED;
+ return EVENT_ERROR;
+ }
+ source_buffer->release_data = true;
+ source_buffer->receive_hash = true;
+ source_buffer->encrypt_data = false;
+ return EVENT_KEY_DATA_READY;
+}
+
+/*
+ * Prepare kv_managed_source_buffers for the three separate parts of a key i.e. header, data and hash
+ * Ensures state machine write buffer is ready to receive data for writing
+ * CNcomment: 准备好NV写入需要的source buffer,NV的三个部分(header、数据、HASH/CRC)被放置在三个source buffer中
+ */
+STATIC kv_update_event_t kv_update_helper_prepare_source_buffers(bool release_key_data)
+{
+ kv_update_event_t event;
+
+ event = kv_update_helper_prepare_write_buffer();
+ if (event == EVENT_KEY_DATA_READY) {
+ event = kv_update_helper_setup_key_header_source_buffer();
+ }
+ if (event == EVENT_KEY_DATA_READY) {
+ event = kv_update_helper_setup_key_data_source_buffer(release_key_data);
+ }
+ if ((event != EVENT_KEY_DATA_READY) && release_key_data) {
+ /* Ensure we release any new key data as it will not have been placed into a source buffer */
+ /* which get cleaned up automatically when a state machine ends */
+ kv_free((void *)g_new_key_details.kvalue);
+ g_new_key_details.kvalue = NULL;
+ }
+ if (event == EVENT_KEY_DATA_READY) {
+ event = kv_update_helper_setup_key_hash_source_buffer();
+ }
+ return event;
+}
+
+/*
+ * Checks if g_current_key would end up being modified by any changes detailed in g_new_key_details
+ * Will return an error if g_current_key is permanent
+ */
+STATIC kv_update_event_t kv_update_helper_check_for_key_updates(void)
+{
+ errcode_t res;
+ kv_attributes_t attributes = kv_key_attributes(&g_current_key);
+ if ((((uint32_t)attributes & (uint32_t)KV_ATTRIBUTE_PERMANENT) != 0) &&
+ (g_new_key_details.focre_write != true)) {
+ /* We can't modify permanent keys in any way */
+ g_current_state_machine->error_code = ERRCODE_NV_TRYING_TO_MODIFY_A_PERMANENT_KEY;
+ return EVENT_ERROR;
+ }
+ if (((uint32_t)attributes & (uint32_t)KV_ATTRIBUTE_NON_UPGRADE) !=
+ ((uint32_t)g_new_key_details.attributes & (uint32_t)KV_ATTRIBUTE_NON_UPGRADE)) {
+ /* Attributes are being updated */
+ return EVENT_KEY_UPDATE_REQUIRED;
+ }
+ if (((uint32_t)attributes & (uint32_t)g_new_key_details.attributes) != (uint32_t)g_new_key_details.attributes) {
+ /* Attributes are being updated */
+ return EVENT_KEY_UPDATE_REQUIRED;
+ }
+
+ if (g_current_key.header.length != g_new_key_details.kvalue_length) {
+ /* New data is of a different length */
+ return EVENT_KEY_UPDATE_REQUIRED;
+ }
+ res = kv_helper_compare_key_data(&g_current_key, g_new_key_details.kvalue,
+ (uint16_t)g_new_key_details.kvalue_length);
+ if (res != ERRCODE_SUCC) {
+ /* New data content is different */
+ return EVENT_KEY_UPDATE_REQUIRED;
+ }
+
+ /* Key not actually being updated */
+ return EVENT_KEY_UPDATE_NOT_NEEDED;
+}
+
+/*
+ * Constructs a new key header from g_new_key_details, copying the attributes of any existing key with the same key_id
+ * Updates g_current_key with details of the new key header
+ */
+STATIC kv_update_event_t kv_update_action_prepare_write_key(void)
+{
+ errcode_t res = kv_store_find_valid_key(g_current_store, g_new_key_details.key_id, &g_current_key);
+ if (res == ERRCODE_SUCC) {
+ kv_update_event_t event = kv_update_helper_check_for_key_updates();
+ if (event != EVENT_KEY_UPDATE_REQUIRED) {
+ nv_log_debug("[NV] The Key not need update. Key id = 0x%x\r\n", g_current_key.header.key_id);
+ return event;
+ }
+
+ /* Combine existing key attributes with those requested for the new key */
+ if (g_current_key.header.type != KV_KEY_TYPE_NORMAL) {
+ g_new_key_details.attributes = (kv_attributes_t)((uint32_t)g_new_key_details.attributes |
+ (uint32_t)KV_ATTRIBUTE_PERMANENT);
+ }
+ if (g_current_key.header.enc_key != 0) {
+ g_new_key_details.attributes = (kv_attributes_t)((uint32_t)g_new_key_details.attributes |
+ (uint32_t)KV_ATTRIBUTE_ENCRYPTED);
+ }
+ }
+
+ kv_key_build_from_new(&g_current_key, &g_new_key_details, g_current_store_write_location);
+ return kv_update_helper_prepare_source_buffers(false);
+}
+
+STATIC errcode_t kv_update_helper_get_current_key(uint8_t **old_kvalue, uint32_t *kvalue_length)
+{
+ kv_key_handle_t current_key;
+ errcode_t res;
+
+ res = kv_store_find_valid_key(g_current_store, g_new_key_details.key_id, ¤t_key);
+ if (res != ERRCODE_SUCC) {
+ return res;
+ }
+
+ kv_attributes_t attributes = kv_key_attributes(¤t_key);
+ if (((uint32_t)attributes & KV_ATTRIBUTE_PERMANENT) != 0) {
+ return ERRCODE_SUCC;
+ }
+
+ if (((uint32_t)attributes & KV_ATTRIBUTE_ENCRYPTED) != 0) {
+ /* if old key is encrypted, the new key must be encrypted too */
+ g_new_key_details.attributes = (kv_attributes_t)((uint32_t)g_new_key_details.attributes |
+ (uint32_t)KV_ATTRIBUTE_ENCRYPTED);
+ }
+
+ *kvalue_length = current_key.header.length;
+ *old_kvalue = (uint8_t *)kv_malloc(current_key.header.length);
+ if (*old_kvalue == NULL) {
+ return ERRCODE_MALLOC;
+ }
+
+ res = kv_key_read_data(¤t_key, *old_kvalue);
+ if (res != ERRCODE_SUCC) {
+ kv_free((void *)*old_kvalue);
+ }
+ return res;
+}
+
+/*
+ * Attempt to claim access to hardware AES and hash functions
+ */
+STATIC kv_update_event_t kv_update_action_claim_crypto(void)
+{
+ kv_managed_write_buffer_t *write_buffer = g_current_state_machine->write_buffer;
+
+ /* Reset write location */
+ write_buffer->write_location = (uint32_t)(uintptr_t)g_current_key.key_location;
+
+ /* Point current source buffer at first buffer and reset data_consumed values */
+ g_current_state_machine->current_source = g_current_state_machine->source_buffer;
+ kv_managed_source_buffer_t *source_buffer = g_current_state_machine->source_buffer;
+ while (source_buffer != NULL) {
+ source_buffer->data_consumed = 0;
+ source_buffer = source_buffer->next;
+ }
+
+ write_buffer->crypto_handle = INVAILD_CRYPTO_HANDLE;
+
+ if (((uint32_t)g_new_key_details.attributes & (uint32_t)KV_ATTRIBUTE_ENCRYPTED) != 0) {
+#if (CONFIG_NV_SUPPORT_ENCRYPT == NV_YES)
+ /* Claim cryptographic engines */
+ if (!write_buffer->encrypt_claimed) {
+ errcode_t res = nv_crypto_claim_aes(&write_buffer->crypto_handle, &(g_current_key.header));
+ if (res != ERRCODE_SUCC) {
+ g_current_state_machine->error_code = res;
+ return EVENT_ERROR;
+ }
+ write_buffer->encrypt_claimed = true;
+ }
+
+#if (CONFIG_NV_SUPPORT_HASH_FOR_CRYPT == NV_YES)
+ if (!write_buffer->hash_claimed) {
+ errcode_t res = nv_crypto_start_hash();
+ if (res != ERRCODE_SUCC) {
+ nv_crypto_release_aes(write_buffer->crypto_handle);
+ write_buffer->crypto_handle = INVAILD_CRYPTO_HANDLE;
+ write_buffer->encrypt_claimed = false;
+ g_current_state_machine->error_code = res;
+ return EVENT_ERROR;
+ }
+ write_buffer->crc_ret = 0;
+ write_buffer->hash_claimed = true;
+ }
+#else
+ if (!write_buffer->gcm_tag_claimed) {
+ write_buffer->crc_ret = 0;
+ write_buffer->gcm_tag_claimed = true;
+ }
+#endif /* #if (CONFIG_NV_SUPPORT_HASH_FOR_CRYPT == NV_YES) */
+#endif /* #if (CONFIG_NV_SUPPORT_ENCRYPT == NV_YES) */
+ } else {
+ if (!write_buffer->crc_claimed) {
+ write_buffer->crc_ret = 0;
+ write_buffer->crc_claimed = true;
+ }
+ }
+
+ return EVENT_CRYPTO_CLAIMED;
+}
+
+/*
+ * Attempts to erases all instances of g_current_key (in g_current_store), by marking them as invalid
+ * Configures g_search_filter to find all valid keys with an id matching g_current_key.header.key_id
+ * Starts the g_erase_keys_machine state machine to perform the erasing of keys
+ */
+STATIC kv_update_event_t kv_update_action_erase_old_keys(void)
+{
+ g_search_filter.pattern = g_current_key.header.key_id;
+ g_search_filter.mask = 0xFFFF;
+ g_search_filter.state = KV_KEY_FILTER_STATE_VALID;
+ g_search_filter.type = KV_KEY_FILTER_TYPE_ANY;
+ g_search_filter.location = g_current_key.key_location;
+
+ errcode_t res = begin_state_machine(&g_erase_keys_machine);
+ if (res == ERRCODE_SUCC) {
+ return EVENT_NONE;
+ } else {
+ g_current_state_machine->error_code = res;
+ return EVENT_ERROR;
+ }
+}
+
+STATIC kv_update_event_t kv_update_action_update_nvmap_for_erase_key(void)
+{
+ kv_nvregion_map_t *nv_map = kv_nvregion_get_map();
+ uint32_t page_number = kv_nvregion_get_page_number(g_current_page.page_location);
+ if (page_number >= nv_map->num_entries) {
+ nv_log_debug("[NV] get erase page_number failed! page_location = 0x%x\r\n", g_current_page.page_location);
+ g_current_state_machine->error_code = ERRCODE_NV_INVALID_PAGE;
+ return EVENT_ERROR;
+ }
+
+ uint16_t key_size = (uint32_t)sizeof(kv_key_header_t);
+ uint16_t hash_crc_size;
+ if (g_current_key.header.enc_key == AES_KDFKEY_SDRK_TYPE) {
+ hash_crc_size = KV_CRYPTO_HASH_SIZE;
+ } else {
+ hash_crc_size = KV_CRYPTO_CRC_SIZE;
+ }
+
+ key_size += hash_crc_size;
+
+ kv_attributes_t attributes = kv_key_attributes(&g_current_key);
+ key_size += kv_key_padded_data_length(attributes, g_current_key.header.length);
+
+ nv_page_status_map_t *page_status = &(nv_map->page_status_map[page_number]);
+ page_status->reclaimable_space += key_size;
+ nv_log_debug("[NV] update nv map for erase key = 0x%x (page = 0x%x) size = %d\r\n",
+ g_current_key.header.key_id, g_current_page.page_location, key_size);
+ return EVENT_PAGE_MAP_UPDATED;
+}
+
+STATIC kv_update_event_t kv_update_action_update_nvmap_for_new_key(void)
+{
+ kv_nvregion_map_t *nv_map = kv_nvregion_get_map();
+
+ uint32_t page_number = kv_nvregion_get_page_number(g_current_page.page_location);
+ if (page_number >= nv_map->num_entries) {
+ nv_log_debug("[NV] get new key page_number failed! page_location = 0x%x\r\n", g_current_page.page_location);
+ g_current_state_machine->error_code = ERRCODE_NV_INVALID_PAGE;
+ return EVENT_ERROR;
+ }
+
+ uint16_t key_size = (uint32_t)sizeof(kv_key_header_t);
+ uint16_t hash_crc_size;
+ if (((uint32_t)g_new_key_details.attributes & (uint32_t)KV_ATTRIBUTE_ENCRYPTED) != 0) {
+ hash_crc_size = KV_CRYPTO_HASH_SIZE;
+ } else {
+ hash_crc_size = KV_CRYPTO_CRC_SIZE;
+ }
+
+ key_size += hash_crc_size;
+ key_size += kv_key_padded_data_length(g_new_key_details.attributes, g_new_key_details.kvalue_length);
+
+ nv_page_status_map_t *page_status = &(nv_map->page_status_map[page_number]);
+ page_status->used_space += key_size;
+ page_status->first_writable_offset += key_size;
+ nv_log_debug("[NV] update nv map for new key = 0x%x (page = 0x%x) size = %d\r\n",
+ g_new_key_details.key_id, g_current_page.page_location, key_size);
+ return EVENT_PAGE_MAP_UPDATED;
+}
+
+/*
+ * Begins a new state machine by creating a new entry at the end of the active state machines list
+ * and updating g_current_state_machine
+ *
+ * Either called to begin processing a *new* top-level state machine, before calling process_state_machine()
+ * or called by a state machine action function to kick off the processing of a new (nested) state machine
+ */
+STATIC errcode_t begin_state_machine(const kv_state_machine_t *machine)
+{
+ kv_active_state_machine_t *new_state_machine =
+ (kv_active_state_machine_t *)kv_malloc(sizeof(kv_active_state_machine_t));
+ if (new_state_machine == NULL) {
+ return ERRCODE_MALLOC;
+ }
+ (void)memset_s(new_state_machine, sizeof(kv_active_state_machine_t), 0, sizeof(kv_active_state_machine_t));
+ new_state_machine->prev = g_current_state_machine;
+
+ /* Configure default initial state and event for new state machine */
+ new_state_machine->machine = machine;
+ new_state_machine->state = machine->initial_state;
+ new_state_machine->event = EVENT_NONE;
+ new_state_machine->error_code = ERRCODE_SUCC;
+ g_current_state_machine = new_state_machine;
+
+ if (g_current_state_machine->machine->write_buffer_size > 0) {
+ return kv_create_write_buffer(0, g_current_state_machine->machine->write_buffer_size);
+ } else {
+ return ERRCODE_SUCC;
+ }
+}
+
+/*
+ * Should only be called by process_state_machine()
+ *
+ * Ends current state machine and updates g_current_state_machine to point to the previous state machine.
+ * Passes either the pre-defined exit_event or the last event returned by an action function as the
+ * event received by the previous state machine.
+ */
+STATIC void end_state_machine(void)
+{
+ if (g_current_state_machine != NULL) {
+ kv_remove_write_buffer();
+ kv_remove_source_buffer();
+
+ kv_active_state_machine_t *prev_state_machine = g_current_state_machine->prev;
+ if (prev_state_machine != NULL) {
+ if (g_current_state_machine->machine->exit_event != EVENT_NONE) {
+ /* Single explicit exit event defined for exiting state machine */
+ prev_state_machine->event = g_current_state_machine->machine->exit_event;
+ } else {
+ /* Pass last event raised from exiting state machine back to invoking state machine */
+ prev_state_machine->event = g_current_state_machine->event;
+ }
+ }
+
+ kv_free(g_current_state_machine);
+ g_current_state_machine = prev_state_machine;
+ }
+}
+
+static void clean_state_machine(void)
+{
+ while (g_current_state_machine != NULL) {
+ end_state_machine();
+ }
+}
+
+/*
+ * Should only be called by process_state_machine()
+ *
+ * Attempts to locate and call an action function based on the state of the current state machine
+ * An action function may spawn another state machine and hence cause g_current_state_machine to change.
+ *
+ * g_current_state_machine->event is used to record the return value from the action function.
+ *
+ * If a new state machine is spawned, this return value will effectively be lost as the procedure for processing
+ * new state machines is to assume there is no initial event, just an initial state and thus action to perform.
+ *
+ * If an action function is not found then g_current_state_machine->event will not be updated. This really should
+ * be reserved for when state transitions cause g_current_state_machine->state to be one of the following
+ * special cases, which are handled by process_state_machine():
+ * - STATE_INVALID
+ * - STATE_SUSPENDED
+ * - STATE_EXIT
+ */
+static void invoke_current_state_action(void)
+{
+ if (g_current_state_machine != NULL) {
+ /* Search action table to locate an action function for the current state */
+ /* STATE_EXIT always marks end of an action table */
+ const kv_update_action_t *update_action = g_current_state_machine->machine->action_table;
+ while (update_action->state != STATE_EXIT) {
+ if (update_action->state == g_current_state_machine->state) {
+ /* Found a matching entry */
+ break;
+ }
+ update_action++;
+ }
+
+ /* Call action function if we have found one */
+ if (update_action->action != NULL) {
+ /* Action function could cause a change of g_current_state_machine */
+ g_current_state_machine->event = (*update_action->action)();
+ }
+ }
+}
+
+/*
+ * Should only be called by process_state_machine()
+ *
+ * Uses the last event recorded for the state machine, in g_current_state_machine->event, to select the
+ * next appropriate state for the current state machine.
+ *
+ * In the case of no suitable entry on the transition_table an error state of STATE_INVALID is entered.
+ * This will cause process_state_machine() to terminate processing of all queued state machines and
+ * return an error.
+ */
+STATIC void update_current_state(void)
+{
+ /* Don't update anything for EVENT_NONE. We'll eventually call the action function again */
+ if ((g_current_state_machine != NULL) && (g_current_state_machine->event != EVENT_NONE)) {
+ /* Search transition table looking for a transition entry for current state and event */
+ /* A transition table is always terminated with transition->state == STATE_EXIT */
+ const kv_update_transition_t *transition = g_current_state_machine->machine->transition_table;
+ while (transition->state != STATE_EXIT) {
+ if ((g_current_state_machine->state == transition->state) &&
+ (g_current_state_machine->event == transition->event)) {
+ /* Found a matching entry */
+ break;
+ }
+ transition++;
+ }
+
+ /* Update current state if we have found a suitable transition table entry */
+ if (transition->state != STATE_EXIT) {
+ g_current_state_machine->state = transition->next_state;
+ } else {
+ /* No state transition found for the current state and event */
+ g_current_state_machine->state = STATE_INVALID;
+ }
+ }
+}
+
+#if defined (DEBUG_PRINT_ENABLED)
+static const char *g_update_state_strings[STATE_EXIT + 1] = {
+ [STATE_INVALID] = "STATE_INVALID",
+ [STATE_SELECT_FIRST_PAGE] = "STATE_SELECT_FIRST_PAGE",
+ [STATE_SELECT_NEXT_PAGE] = "STATE_SELECT_NEXT_PAGE",
+ [STATE_FIND_FIRST_KEY] = "STATE_FIND_FIRST_KEY",
+ [STATE_FIND_NEXT_KEY] = "STATE_FIND_NEXT_KEY",
+ [STATE_FIND_EXISTING_KEY] = "STATE_FIND_EXISTING_KEY",
+ [STATE_PREP_COPY_KEY] = "STATE_PREP_COPY_KEY",
+ [STATE_PREP_DELETE_KEY] = "STATE_PREP_DELETE_KEY",
+ [STATE_PREP_MODIFY_KEY] = "STATE_PREP_MODIFY_KEY",
+ [STATE_FIND_DEFRAG] = "STATE_FIND_DEFRAG",
+ [STATE_ERASE_DEFRAG] = "STATE_ERASE_DEFRAG",
+ [STATE_PREP_DEFRAG] = "STATE_PREP_DEFRAG",
+ [STATE_COPY_ALL_KEYS] = "STATE_COPY_ALL_KEYS",
+ [STATE_PREP_PAGE_HEADER] = "STATE_PREP_PAGE_HEADER",
+ [STATE_WRITE_PAGE_HEADER] = "STATE_WRITE_PAGE_HEADER",
+ [STATE_UPDATE_NVREGION_MAP] = "STATE_UPDATE_NVREGION_MAP",
+ [STATE_FIND_WRITE_POS] = "STATE_FIND_WRITE_POS",
+ [STATE_DEFRAG_PAGE] = "STATE_DEFRAG_PAGE",
+ [STATE_PREP_STORE] = "STATE_PREP_STORE",
+ [STATE_PREP_KEY_DATA] = "STATE_PREP_KEY_DATA",
+ [STATE_CLAIM_CRYPTO] = "STATE_CLAIM_CRYPTO",
+ [STATE_ERASE_OLD_KEYS] = "STATE_ERASE_OLD_KEYS",
+ [STATE_PRIME_WRITE] = "STATE_PRIME_WRITE",
+ [STATE_PERFORM_WRITE] = "STATE_PERFORM_WRITE",
+ [STATE_SUSPENDED] = "STATE_SUSPENDED",
+ [STATE_EXIT] = "STATE_EXIT"
+};
+
+#if defined (DEBUG_KV_UPDATE_STATE_MACHINE)
+static const char *g_update_event_strings[EVENT_ERROR + 1] = {
+ [EVENT_NONE] = "EVENT_NONE",
+ [EVENT_SUSPEND] = "EVENT_SUSPEND",
+ [EVENT_WRITE_DATA_EXHAUSTED] = "EVENT_WRITE_DATA_EXHAUSTED",
+ [EVENT_WRITE_BUFFER_PRIMED] = "EVENT_WRITE_BUFFER_PRIMED",
+ [EVENT_WRITE_BUFFER_STORED] = "EVENT_WRITE_BUFFER_STORED",
+ [EVENT_PAGE_SELECTED] = "EVENT_PAGE_SELECTED",
+ [EVENT_PAGE_NOT_SELECTED] = "EVENT_PAGE_NOT_SELECTED",
+ [EVENT_KEY_FOUND] = "EVENT_KEY_FOUND",
+ [EVENT_KEY_NOT_FOUND] = "EVENT_KEY_NOT_FOUND",
+ [EVENT_COPY_KEY_READY] = "EVENT_COPY_KEY_READY",
+ [EVENT_KEYS_ERASED] = "EVENT_KEYS_ERASED",
+ [EVENT_KEY_UPDATE_REQUIRED] = "EVENT_KEY_UPDATE_REQUIRED",
+ [EVENT_KEY_UPDATE_NOT_NEEDED] = "EVENT_KEY_UPDATE_NOT_NEEDED",
+ [EVENT_DEFRAG_FOUND] = "EVENT_DEFRAG_FOUND",
+ [EVENT_DEFRAG_ERASED] = "EVENT_DEFRAG_ERASED",
+ [EVENT_DEFRAG_PREPARED] = "EVENT_DEFRAG_PREPARED",
+ [EVENT_ALL_KEYS_COPIED] = "EVENT_ALL_KEYS_COPIED",
+ [EVENT_PAGE_HEADER_READY] = "EVENT_PAGE_HEADER_READY",
+ [EVENT_NVREGION_MAP_UPDATED] = "EVENT_NVREGION_MAP_UPDATED",
+ [EVENT_DEFRAG_REQUIRED] = "EVENT_DEFRAG_REQUIRED",
+ [EVENT_DEFRAG_COMPLETE] = "EVENT_DEFRAG_COMPLETE",
+ [EVENT_WRITE_POS_FOUND] = "EVENT_WRITE_POS_FOUND",
+ [EVENT_STORE_READY] = "EVENT_STORE_READY",
+ [EVENT_KEY_DATA_READY] = "EVENT_KEY_DATA_READY",
+ [EVENT_CRYPTO_CLAIMED] = "EVENT_CRYPTO_CLAIMED",
+ [EVENT_WRITE_COMPLETE] = "EVENT_WRITE_COMPLETE",
+ [EVENT_ERROR] = "EVENT_ERROR"
+};
+#endif
+#endif
+
+static void dprint_state_machine(const char *str)
+{
+ unused(str);
+#if defined (DEBUG_KV_UPDATE_STATE_MACHINE)
+ if (g_current_state_machine != NULL) {
+ nv_log_debug("%08X: ", (uint32_t)g_current_state_machine);
+ nv_log_debug("%s ", g_update_state_strings[g_current_state_machine->state]);
+ nv_log_debug("%s ", str);
+ nv_log_debug("%s\r\n", g_update_event_strings[g_current_state_machine->event]);
+ } else {
+ nv_log_debug("*** State Machine %s ***\r\n", str);
+ }
+#endif
+}
+
+/*
+ * Function used to process queued state machines
+ *
+ * There is only ever one state machine being processed at any one time.
+ *
+ * State machines are started (queued) by explicitly calling begin_state_machine() before this function is called.
+ * State machines themselves can spawn a nested state machine by also calling begin_state_machine() in an action
+ * function.
+ * When started, a state machine will be in the initial_state defined for the state machine.
+ *
+ * State machines are ended normally by transitioning to STATE_EXIT or abnormally by transitioning to STATE_INVALID.
+ * In such instances, process_state_machine(), will call end_state_machine() to tidy up.
+ *
+ * State machines can be suspended at any point by transitioning to STATE_SUSPENDED.
+ * When resumed, by calling process_state_machine() again, they will start from the resume_state defined for the
+ * state machine.
+ *
+ * g_current_state_machine points to the current active state machine
+ *
+ * When a state machine is processed for the first time, or resumed following suspension, it is assumed there is no
+ * current event that exists for the state machine in this context. An action function for the current state, which
+ * will be defined by initial_state or resume_state, will provide an event to provide for further transitions through
+ * the state machine.
+ */
+STATIC errcode_t process_state_machine(void)
+{
+ errcode_t error_code = ERRCODE_SUCC;
+ uint64_t flash_access_time = 0;
+ unused(flash_access_time);
+
+ while (g_current_state_machine != NULL) {
+ /* Call action function for the current state */
+ dprint_state_machine("---");
+ invoke_current_state_action();
+ dprint_state_machine("-->");
+
+ /* Check event returned by action function */
+ if (g_current_state_machine->event == EVENT_ERROR) {
+ /* Ripple back down chain of state machines and report error */
+ error_code = g_current_state_machine->error_code;
+ clean_state_machine();
+ return error_code;
+ }
+
+ /* Transition state machine */
+ update_current_state();
+ dprint_state_machine("<--");
+
+ /* Has the state machine finished? */
+ while ((g_current_state_machine != NULL) && (g_current_state_machine->state == STATE_EXIT)) {
+ invoke_current_state_action(); /* Exit state could have an action function if it needs to do more */
+ end_state_machine(); /* than just end the current state machine */
+ update_current_state();
+ dprint_state_machine("<==");
+ }
+
+ /* Has state machine processing been suspended? */
+ if ((g_current_state_machine != NULL) && (g_current_state_machine->state == STATE_SUSPENDED)) {
+#ifdef CONFIG_NV_SUPPORT_SINGLE_CORE_SYSTEM
+ /* if in one-core system, the flash must be writen in one time */
+ /* so once the suspend happened, clear the state_machine */
+ clean_state_machine();
+#else
+ /* We need to suspend processing due to other cores waking up */
+ /* Configure state machine to resume from the correct place */
+ g_current_state_machine->state = g_current_state_machine->machine->resume_state;
+ g_current_state_machine->event = EVENT_NONE;
+#endif
+ kv_release_crypto();
+ return ERRCODE_NV_WRITE_VETOED;
+ }
+
+ /* Has the state machine entered an invalid state? */
+ if ((g_current_state_machine != NULL) && (g_current_state_machine->state == STATE_INVALID)) {
+#ifdef CONFIG_NV_SUPPORT_SINGLE_CORE_SYSTEM
+ clean_state_machine();
+#endif
+ kv_release_crypto();
+ return ERRCODE_NV_STATE_INVALID;
+ }
+ }
+
+ return error_code;
+}
+
+/* Creates a write buffer used to prepare data, gathered from one or more source buffers, for writing to flash */
+STATIC errcode_t kv_create_write_buffer(uint32_t write_location, uint32_t size)
+{
+ if (size == 0) {
+ return ERRCODE_INVALID_PARAM;
+ }
+
+ if (g_current_state_machine->write_buffer != NULL) {
+ return ERRCODE_FAIL;
+ }
+
+ uint8_t *data = (uint8_t *)kv_malloc(size);
+ if (data == NULL) {
+ return ERRCODE_MALLOC;
+ }
+ kv_managed_write_buffer_t *write_buffer =
+ (kv_managed_write_buffer_t *)kv_malloc(sizeof(kv_managed_write_buffer_t));
+ if (write_buffer == NULL) {
+ kv_free(data);
+ return ERRCODE_MALLOC;
+ }
+
+ (void)memset_s(write_buffer, sizeof(kv_managed_write_buffer_t), 0, sizeof(kv_managed_write_buffer_t));
+ write_buffer->data = data;
+ write_buffer->size = (uint16_t)size;
+ write_buffer->data_consumed = (uint16_t)size;
+ write_buffer->write_location = write_location;
+ write_buffer->resume_location = write_location;
+ g_current_state_machine->write_buffer = write_buffer;
+
+ return ERRCODE_SUCC;
+}
+
+/* Removes the write buffer owned by the current state machine */
+STATIC void kv_remove_write_buffer(void)
+{
+ if (g_current_state_machine->write_buffer != NULL) {
+ kv_free(g_current_state_machine->write_buffer->data);
+ kv_free(g_current_state_machine->write_buffer);
+ g_current_state_machine->write_buffer = NULL;
+ }
+}
+
+/* Every key consists of a header, data and a hash. Multiple kv_managed_source_buffer_t are constructed to
+ * manage these separate blocks of data.
+ * They keep track of where the data is located and how much of it has
+ * been written to flash.
+ * These buffers are used in a scatter-gather type approach to feed a single write buffer that holds data
+ * that has been fully prepared for writing to flash */
+STATIC kv_managed_source_buffer_t* kv_update_source_buffer(uint8_t *data, uint32_t data_length)
+{
+ kv_managed_source_buffer_t *source_buffer = kv_malloc(sizeof(kv_managed_source_buffer_t));
+ if (source_buffer == NULL) {
+ return NULL;
+ }
+
+ errno_t err = memset_s(source_buffer, sizeof(kv_managed_source_buffer_t), 0, sizeof(kv_managed_source_buffer_t));
+ if (err != EOK) {
+ kv_free(source_buffer);
+ return NULL;
+ }
+ source_buffer->data = data;
+ source_buffer->data_length = (uint16_t)data_length;
+ source_buffer->next = NULL;
+
+ /* Append new buffer to end of list */
+ kv_managed_source_buffer_t **next_buffer = &g_current_state_machine->source_buffer;
+ while (*next_buffer != NULL) {
+ next_buffer = &(*next_buffer)->next;
+ }
+ *next_buffer = source_buffer;
+ return source_buffer;
+}
+
+/* Removes kv_managed_source_buffer_t data structures, freeing memory holding actual data too, if necessary */
+STATIC void kv_remove_source_buffer(void)
+{
+ kv_managed_source_buffer_t *current = g_current_state_machine->source_buffer;
+ while (current != NULL) {
+ kv_managed_source_buffer_t *next = current->next;
+ if (current->release_data) {
+ kv_free(current->data);
+ }
+ kv_free(current);
+ current = next;
+ }
+ g_current_state_machine->source_buffer = NULL;
+}
+
+/* Releases claim over hardware AES and hash functions */
+STATIC void kv_release_crypto(void)
+{
+ if (g_current_state_machine == NULL) {
+ return;
+ }
+ kv_managed_write_buffer_t *write_buffer = g_current_state_machine->write_buffer;
+ uint32_t status;
+
+ if (write_buffer != NULL) {
+ status = osal_irq_lock();
+ if (write_buffer->crc_claimed) {
+ write_buffer->crc_ret = 0;
+ write_buffer->crc_claimed = false;
+ }
+#if (CONFIG_NV_SUPPORT_ENCRYPT == NV_YES)
+ if (write_buffer->hash_claimed) {
+ write_buffer->hash_claimed = false;
+ }
+ if (write_buffer->gcm_tag_claimed) {
+ write_buffer->gcm_tag_claimed = false;
+ }
+ if (write_buffer->encrypt_claimed) {
+ nv_crypto_release_aes(write_buffer->crypto_handle);
+ write_buffer->crypto_handle = INVAILD_CRYPTO_HANDLE;
+ write_buffer->encrypt_claimed = false;
+ }
+#endif
+ osal_irq_restore(status);
+ }
+}
+
+STATIC bool active_state_machine(void)
+{
+ if (g_current_state_machine != NULL) {
+ return true;
+ }
+ return false;
+}
+
+STATIC uint32_t determine_flash_task_state_code(errcode_t res)
+{
+ switch (res) {
+ case ERRCODE_NV_WRITE_VETOED:
+ case ERRCODE_FLASH_TASK_PE_VETO:
+ return FLASH_TASK_BEING_PROCESSED;
+ case ERRCODE_SUCC:
+ return FLASH_TASK_COMPLETED;
+ default:
+ return (uint32_t)res;
+ }
+}
+
+#if defined(CONFIG_PARTITION_FEATURE_SUPPORT)
+STATIC errcode_t nv_read_partition_addr(uint32_t *start_address, uint32_t *size)
+{
+ /* 获取分区表标记 */
+ errcode_t ret;
+ partition_information_t info;
+
+ ret = uapi_partition_get_info(PARTITION_NV_DATA, &info);
+ if (ret != ERRCODE_SUCC) {
+ return ret;
+ }
+ *start_address = info.part_info.addr_info.addr + FLASH_PHYSICAL_ADDR_START;
+ *size = info.part_info.addr_info.size;
+
+ return ERRCODE_SUCC;
+}
+#endif
+
+errcode_t kv_update_init(cores_t core)
+{
+ unused(core);
+
+ errcode_t res;
+ uint32_t kv_start_addr = 0;
+ uint32_t kv_size = 0;
+#if defined(CONFIG_PARTITION_FEATURE_SUPPORT)
+ res = nv_read_partition_addr(&kv_start_addr, &kv_size);
+ if (res != ERRCODE_SUCC) {
+ return res;
+ }
+#endif
+ if (kv_start_addr != 0 && kv_size != 0) {
+ kv_nvregion_init(kv_start_addr, KV_STORE_DATA_SIZE, kv_start_addr + KV_STORE_DATA_SIZE, KV_BACKUP_DATA_SIZE);
+ } else {
+ kv_nvregion_init(KV_STORE_START_ADDR, KV_STORE_DATA_SIZE, KV_BACKUP_START_ADDR, KV_BACKUP_DATA_SIZE);
+ }
+
+ /* Scan NV region looking for KV pages */
+ res = kv_nvregion_scan();
+ if (res != ERRCODE_SUCC) {
+ return res;
+ }
+
+ /* Check expected KV pages have been found */
+ for (uint8_t store = (uint8_t)KV_STORE_APPLICATION; store < (uint8_t)KV_STORE_MAX_NUM; store++) {
+ uint16_t store_id = kv_store_get_id(store);
+ uint8_t pages_num = kv_store_get_page_count(store);
+ for (uint8_t page_index = 0; page_index < pages_num; page_index++) {
+ res = kv_nvregion_find_page(store_id, page_index, NULL, NULL);
+ if (res != ERRCODE_SUCC) {
+ /* NV Region does not contain an expected KV page, attempt to create it */
+ res = kv_nvregion_create_page(store_id, page_index);
+ }
+ if (res != ERRCODE_SUCC) {
+ return ERRCODE_FAIL;
+ }
+ }
+ }
+ return ERRCODE_SUCC;
+}
+
+#if (CONFIG_NV_SUPPORT_BACKUP_RESTORE == NV_YES)
+STATIC errcode_t kv_updata_backup_page_head_is_valid(const kv_page_header_t *backup_head)
+{
+ if ((backup_head->details.store_id == KV_STORE_ID_BACKUP) &&
+ ((~backup_head->inverted_details_word) == *(uint32_t *)(uintptr_t)(&backup_head->details)) &&
+ ((~backup_head->inverted_sequence_number) == backup_head->sequence_number)) {
+ return ERRCODE_SUCC;
+ }
+ return ERRCODE_FAIL;
+}
+
+STATIC errcode_t kv_updata_backup_fail_process(uint32_t back_maybe_need_process_location)
+{
+ errcode_t res;
+ kv_page_location unused_page_location = NULL;
+ uint32_t page_head_size = (uint32_t)sizeof(kv_page_header_t);
+ kv_nvregion_area_t* nvregion_area = nv_get_region_area();
+ if (nvregion_area == NULL) {
+ return ERRCODE_FAIL;
+ }
+ /* 去找工作区可以换页的页,然后将可以换页的页地址给传出 */
+ res = kv_nvregion_find_unused_page(&unused_page_location);
+ if (res != ERRCODE_SUCC) {
+ return res;
+ }
+ /* 获取工作区换页页的页头 */
+ kv_page_header_t store_head_buffer;
+ res = kv_key_helper_copy_flash((uint32_t)(uintptr_t)&store_head_buffer,
+ (uint32_t)unused_page_location, page_head_size);
+ if (res != ERRCODE_SUCC) {
+ return res;
+ }
+ /* 如果页头中包含 KV_STORE_ID_BACKUP 并且该page_location与maybe_need_process_location时都指向同一页 那么说明存在掉电丢数据的情况 */
+ if (kv_updata_backup_page_head_is_valid(&store_head_buffer) == ERRCODE_SUCC) {
+ uint32_t back_page_location = nvregion_area->nv_backup_addr
+ + store_head_buffer.details.page_index * KV_PAGE_SIZE;
+ if (back_page_location == back_maybe_need_process_location) {
+ res = kv_key_erase_flash(back_page_location, KV_PAGE_SIZE);
+ if (res != ERRCODE_SUCC) {
+ return res;
+ }
+ res = kv_backup_copy_unused_page_to_dragpage(back_page_location, (uint32_t)unused_page_location);
+ if (res != ERRCODE_SUCC) {
+ return res;
+ }
+ }
+ }
+ return ERRCODE_SUCC;
+}
+
+errcode_t kv_update_backup_init(void)
+{
+ /* Initializes the page header of the backup area */
+ errcode_t res;
+ uint32_t backup_not_inited_count = 0; /* 重启时备份区没有初始化的数量为0 */
+ uint32_t back_maybe_need_process_location = 0;
+ kv_nvregion_area_t* nvregion_area = nv_get_region_area();
+ if (nvregion_area == NULL) {
+ return ERRCODE_FAIL;
+ }
+ uint32_t back_page_location = nvregion_area->nv_backup_addr;
+ kv_page_header_t backup_head_buffer;
+ for (uint32_t page_index = 0; page_index < KV_BACKUP_PAGE_NUM; page_index++) {
+ res = kv_key_helper_copy_flash((uint32_t)(uintptr_t)&backup_head_buffer,
+ (uint32_t)back_page_location, sizeof(kv_page_header_t));
+ if (res != ERRCODE_SUCC) {
+ return res;
+ }
+ if (kv_updata_backup_page_head_is_valid(&backup_head_buffer) == ERRCODE_FAIL) {
+ backup_not_inited_count++;
+ res = kv_nvregion_write_page((kv_page_location)back_page_location, KV_STORE_ID_BACKUP, page_index);
+ if (res != ERRCODE_SUCC) {
+ return res;
+ }
+ back_maybe_need_process_location = back_page_location;
+ }
+ back_page_location += KV_PAGE_SIZE;
+ }
+
+ /* 如果备份区没有初始化的页的数量为0, 说明备份区页不存在异常,正常重启 */
+ /* 如果备份区未初始化的页的数量大于1, 说明是刚刚烧录状态,对备份区页头的初始化 */
+ /* 如果备份区没有初始化的页的数量为1, 那么说明可能存在掉电异常场景进行处理 */
+ if (backup_not_inited_count == 1) {
+ res = kv_updata_backup_fail_process(back_maybe_need_process_location);
+ if (res != ERRCODE_SUCC) {
+ return res;
+ }
+ }
+ return res;
+}
+#endif /* #if (CONFIG_NV_SUPPORT_BACKUP_RESTORE == NV_YES) */
+
+
+/* Top level function used to mark a single key in a store as invalid (and hence erased) */
+/* Will actually scan a store for all keys matching the specified key_id, to ensure they are all marked as erased */
+errcode_t kv_update_erase_key(kv_store_t core, flash_task_node *sanitised_tasks)
+{
+ if (!active_state_machine()) {
+ g_current_store = core;
+
+ /* Configure search filter to obtain all valid instances of a specific key_id */
+ g_search_filter.pattern = sanitised_tasks->data.kv_erase.key;
+ g_search_filter.mask = 0xFFFF;
+ g_search_filter.state = KV_KEY_FILTER_STATE_VALID;
+ g_search_filter.type = KV_KEY_FILTER_TYPE_ANY;
+ g_search_filter.location = 0;
+
+ /* Queue Erase Keys state machine for processing */
+ begin_state_machine(&g_erase_keys_machine);
+ }
+
+ /* Run state machine until it is either suspended (yielding due to flash contention) or */
+ /* completed (either successfully or otherwise) */
+ sanitised_tasks->state_code = determine_flash_task_state_code(process_state_machine());
+#ifdef CONFIG_NV_SUPPORT_SINGLE_CORE_SYSTEM
+ if (sanitised_tasks->state_code == FLASH_TASK_COMPLETED) {
+ return ERRCODE_SUCC;
+ } else {
+ return (errcode_t)sanitised_tasks->state_code;
+ }
+#else
+ return ERRCODE_SUCC;
+#endif
+}
+
+/* Top level function to write a new key or modify an existing key-value */
+/* Will cause a new instance of the key to be generated */
+errcode_t kv_update_write_key(kv_store_t core, flash_task_node *sanitised_task)
+{
+ if (!active_state_machine()) {
+ g_current_store = core;
+
+ /* Log details of new key to be written */
+ g_new_key_details.key_id = sanitised_task->data.kv.key;
+ g_new_key_details.kvalue = sanitised_task->data.kv.kvalue;
+ g_new_key_details.kvalue_length = sanitised_task->data.kv.kvalue_length;
+ g_new_key_details.attributes = sanitised_task->data.kv.attribute;
+ g_new_key_details.focre_write = sanitised_task->data.kv.force_write;
+
+ /* Queue Write Key state machine for processing */
+ begin_state_machine(&g_write_key_machine);
+ }
+
+ /* Run state machine until it is either suspended (yielding due to flash contention) or */
+ /* completed (either successfully or otherwise) */
+ sanitised_task->state_code = determine_flash_task_state_code(process_state_machine());
+#ifdef CONFIG_NV_SUPPORT_SINGLE_CORE_SYSTEM
+ if (sanitised_task->state_code == FLASH_TASK_COMPLETED) {
+ return ERRCODE_SUCC;
+ } else {
+ return (errcode_t)sanitised_task->state_code;
+ }
+#else
+ return ERRCODE_SUCC;
+#endif
+}
+
+/* Modify the attribute on an existing key-value */
+/* Top level function to modify the attributes of an existing key */
+/* Will cause a new instance of the key to be generated */
+/* Existing key attributes will be maintained */
+errcode_t kv_update_modify_attribute(kv_store_t core, flash_task_node *sanitised_task)
+{
+ uint8_t *old_kvalue = NULL;
+ errcode_t res;
+ if (!active_state_machine()) {
+ g_current_store = core;
+ /* Log details of *additional* attributes to be applied to an existing key */
+ g_new_key_details.key_id = sanitised_task->data.kv_attribute.key;
+ g_new_key_details.kvalue = NULL;
+ g_new_key_details.kvalue_length = 0;
+ g_new_key_details.attributes = sanitised_task->data.kv_attribute.attribute;
+ g_new_key_details.focre_write = false;
+
+ res = kv_update_helper_get_current_key(&old_kvalue, &g_new_key_details.kvalue_length);
+ if (res != ERRCODE_SUCC) {
+ return res;
+ }
+ g_new_key_details.kvalue = old_kvalue;
+ /* Queue Modify Key state machine for processing */
+ begin_state_machine(&g_write_key_machine);
+ }
+
+ /* Run state machine until it is either suspended (yielding due to flash contention) or */
+ /* completed (either successfully or otherwise) */
+ sanitised_task->state_code = determine_flash_task_state_code(process_state_machine());
+#ifdef CONFIG_NV_SUPPORT_SINGLE_CORE_SYSTEM
+ kv_free((void *)old_kvalue);
+ if (sanitised_task->state_code == FLASH_TASK_COMPLETED) {
+ return ERRCODE_SUCC;
+ } else {
+ return (errcode_t)sanitised_task->state_code;
+ }
+#else
+ if (sanitised_task->state_code != FLASH_TASK_BEING_PROCESSED) {
+ kv_free((void *)old_kvalue);
+ }
+ return ERRCODE_SUCC;
+#endif
+}
+
-/*\r
- * Copyright (c) @CompanyNameMagicTag 2019-2022. All rights reserved.\r
- * Description: KV Storage Library flash updating module interface\r
- */\r
-\r
-#ifndef NV_UPDATE_H_\r
-#define NV_UPDATE_H_\r
-\r
-#ifdef CONFIG_NV_SUPPORT_SINGLE_CORE_SYSTEM\r
-#include "nv_task_adapt.h"\r
-#else\r
-#include "flash_task_types.h"\r
-#endif\r
-#include "nv_store.h"\r
-#include "errcode.h"\r
-#include "nv_porting.h"\r
-\r
-#ifdef __cplusplus\r
-#if __cplusplus\r
-extern "C" {\r
-#endif /* __cplusplus */\r
-#endif /* __cplusplus */\r
-\r
-/* Unused states and events should be removed */\r
-typedef enum {\r
- STATE_INVALID,\r
-\r
- /* erase_all_keys, erase_old_keys, copy_all_keys */\r
- STATE_SELECT_FIRST_PAGE,\r
- STATE_SELECT_NEXT_PAGE,\r
- STATE_FIND_FIRST_KEY,\r
- STATE_FIND_NEXT_KEY,\r
- STATE_FIND_EXISTING_KEY,\r
- STATE_PREP_COPY_KEY,\r
- STATE_PREP_DELETE_KEY,\r
- STATE_PREP_MODIFY_KEY,\r
- STATE_UPDATE_MAP_FOR_OLD,\r
-\r
- /* prep_defrag_page, defrag_page */\r
- STATE_FIND_DEFRAG,\r
- STATE_ERASE_DEFRAG,\r
- STATE_PREP_DEFRAG,\r
- STATE_COPY_ALL_KEYS,\r
- STATE_PREP_PAGE_HEADER,\r
- STATE_WRITE_PAGE_HEADER,\r
- STATE_UPDATE_NVREGION_MAP,\r
-\r
- /* prepare_write */\r
- STATE_FIND_WRITE_POS,\r
- STATE_DEFRAG_PAGE,\r
-\r
- /* write_key */\r
- STATE_PREP_STORE,\r
- STATE_PREP_KEY_DATA,\r
- STATE_CLAIM_CRYPTO,\r
- STATE_ERASE_OLD_KEYS,\r
-\r
- STATE_PRIME_WRITE,\r
- STATE_PERFORM_WRITE,\r
- STATE_UPDATE_MAP_FOR_NEW,\r
-\r
- STATE_SUSPENDED = 0x1E,\r
- STATE_EXIT = 0x1F\r
-} kv_update_state_t;\r
-\r
-typedef enum {\r
- EVENT_NONE,\r
- EVENT_SUSPEND,\r
-\r
- EVENT_WRITE_DATA_EXHAUSTED,\r
- EVENT_WRITE_BUFFER_PRIMED,\r
- EVENT_WRITE_BUFFER_STORED,\r
-\r
- /* erase_all_keys, erase_old_keys, copy_all_keys, modify_key */\r
- EVENT_PAGE_SELECTED,\r
- EVENT_PAGE_NOT_SELECTED,\r
- EVENT_KEY_FOUND,\r
- EVENT_KEY_NOT_FOUND,\r
- EVENT_COPY_KEY_READY,\r
-\r
- /* erase_all_keys */\r
- EVENT_KEYS_ERASED,\r
-\r
- /* modify_key */\r
- EVENT_KEY_UPDATE_REQUIRED,\r
- EVENT_KEY_UPDATE_NOT_NEEDED,\r
-\r
- /* prep_defrag_page */\r
- EVENT_DEFRAG_FOUND,\r
- EVENT_DEFRAG_ERASED,\r
-\r
- /* defrag_page */\r
- EVENT_DEFRAG_PREPARED,\r
- EVENT_ALL_KEYS_COPIED,\r
- EVENT_PAGE_HEADER_READY,\r
- EVENT_NVREGION_MAP_UPDATED,\r
-\r
- /* prepare_write */\r
- EVENT_DEFRAG_REQUIRED,\r
- EVENT_DEFRAG_COMPLETE,\r
- EVENT_WRITE_POS_FOUND,\r
-\r
- /* write_key */\r
- EVENT_STORE_READY,\r
- EVENT_KEY_DATA_READY,\r
- EVENT_CRYPTO_CLAIMED,\r
- EVENT_WRITE_COMPLETE,\r
- EVENT_PAGE_MAP_UPDATED,\r
-\r
- EVENT_ERROR = 0x1F\r
-} kv_update_event_t;\r
-\r
-/* State machine action function */\r
-typedef kv_update_event_t (*kv_state_action)(void);\r
-\r
-/* Data to be written to flash can be scattered across a chain of one or more source buffers */\r
-typedef struct managed_source_buffer {\r
- struct managed_source_buffer *next; /* Pointer to next source buffer in chain */\r
- uint8_t *data; /* Pointer to data in RAM/Flash */\r
- uint16_t data_length; /* Length of the data */\r
- uint16_t data_consumed; /* How much of the data has been read so far */\r
- bool release_data : 1; /* Data pointer is a temporary buffer that needs freeing */\r
- bool receive_hash : 1; /* Data buffer should receive a hash/CRC calculation */\r
- bool hash_data : 1; /* Indicates that the data needs hashing */\r
- bool crc_data : 1; /* Indicates that the data needs CRC calculation */\r
- bool encrypt_data : 1; /* Indicates that the data need encrypting */\r
- bool gcm_tag_data : 1; /* Indicates that the data needs AES GCM tag */\r
-} kv_managed_source_buffer_t;\r
-\r
-/* Write buffer, used to gather chunks of data from one or more source buffers */\r
-typedef struct {\r
- uint8_t *data; /* RAM buffer used to hold chunks of data to be written to flash */\r
- uint32_t write_location; /* Current location in flash to write to */\r
- uint32_t resume_location; /* Write location reached when state machine was suspended */\r
- uint32_t crypto_handle; /* AES crypo handle */\r
- uint16_t size; /* Size of the RAM buffer */\r
- uint16_t data_length; /* Length of data written to RAM buffer */\r
- uint32_t crc_ret; /* crc_ret counter */\r
- uint16_t data_consumed; /* How much of the data has been written to flash */\r
- bool hash_claimed : 1; /* Hash function (SHA256) has been claimed */\r
- bool encrypt_claimed : 1; /* Encryption function (AES) has been claimed */\r
- bool crc_claimed : 1; /* CRC function has been claimed */\r
- bool gcm_tag_claimed : 1; /* AES GCM tag has been claimed */\r
-} kv_managed_write_buffer_t;\r
-\r
-/* State machine state transition table entry */\r
-typedef struct {\r
- kv_update_state_t state : 5; /* Current state */\r
- kv_update_state_t next_state : 5; /* State after the transition has occurred */\r
- kv_update_event_t event : 5; /* Event required to cause the transition (raised by an action function) */\r
-} kv_update_transition_t;\r
-\r
-/* State machine state action table entry */\r
-typedef struct {\r
- kv_update_state_t state; /* Applicable state */\r
- kv_state_action action; /* Action function to be called */\r
-} kv_update_action_t;\r
-\r
-/* Structure used to describe a state machine */\r
-typedef struct {\r
- const kv_update_transition_t *transition_table; /* State Transition table for this state machine */\r
- const kv_update_action_t *action_table; /* State Action table for this state machine */\r
- uint16_t write_buffer_size; /* Size of the write buffer needed for this state machine */\r
- kv_update_state_t initial_state; /* State to set on initial entry to this state machine */\r
- kv_update_state_t resume_state; /* State to set on resuming this state machine */\r
- kv_update_event_t exit_event; /* Event to send to previous state machine, if no exit action function defined */\r
-} kv_state_machine_t;\r
-\r
-/* Used to hold run-time details of a state machine being processed */\r
-/* Individual state machines will often invoke a sub state machine.\r
- * These data structures will be used to form a linked list of currently active state machines, although only one\r
- * state machine is ever being processed at any one time.\r
- * The tail of the list will be the initial state machine, started by one of the four kv_update_xxx() functions at the\r
- * end of this file.\r
- * The head of the list will be the current state machine being actively processed.\r
- * The 'prev' field provides a path back to the initial state machine\r
- */\r
-typedef struct kv_active_state_machine_t {\r
- struct kv_active_state_machine_t *prev; /* Previous state machine. Will be NULL if this is a root machine */\r
- const kv_state_machine_t *machine; /* State machine being processed */\r
- kv_managed_write_buffer_t *write_buffer; /* Holds chunk of data to be written to flash */\r
- kv_managed_source_buffer_t *source_buffer; /* Linked list of source data buffers used to populate write buffer */\r
- kv_managed_source_buffer_t *current_source; /* Current source buffer being used */\r
- kv_update_state_t state; /* Current state machine state */\r
- kv_update_event_t event; /* Last event raised (will have caused transition to current state) */\r
- errcode_t error_code; /* Records the error code produced when raising an error event */\r
-} kv_active_state_machine_t;\r
-\r
-errcode_t kv_update_init(cores_t core);\r
-errcode_t kv_update_erase_key(kv_store_t core, flash_task_node *sanitised_tasks);\r
-errcode_t kv_update_write_key(kv_store_t core, flash_task_node *sanitised_task);\r
-errcode_t kv_update_modify_attribute(kv_store_t core, flash_task_node *sanitised_task);\r
-errcode_t kv_update_backup_init(void);\r
-\r
-#ifdef __cplusplus\r
-#if __cplusplus\r
-}\r
-#endif /* __cplusplus */\r
-#endif /* __cplusplus */\r
-\r
-#endif\r
+/*
+ * Copyright (c) @CompanyNameMagicTag 2019-2022. All rights reserved.
+ * Description: KV Storage Library flash updating module interface
+ */
+
+#ifndef NV_UPDATE_H_
+#define NV_UPDATE_H_
+
+#ifdef CONFIG_NV_SUPPORT_SINGLE_CORE_SYSTEM
+#include "nv_task_adapt.h"
+#else
+#include "flash_task_types.h"
+#endif
+#include "nv_store.h"
+#include "errcode.h"
+#include "nv_porting.h"
+
+#ifdef __cplusplus
+#if __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+#endif /* __cplusplus */
+
+/* Unused states and events should be removed */
+typedef enum {
+ STATE_INVALID,
+
+ /* erase_all_keys, erase_old_keys, copy_all_keys */
+ STATE_SELECT_FIRST_PAGE,
+ STATE_SELECT_NEXT_PAGE,
+ STATE_FIND_FIRST_KEY,
+ STATE_FIND_NEXT_KEY,
+ STATE_FIND_EXISTING_KEY,
+ STATE_PREP_COPY_KEY,
+ STATE_PREP_DELETE_KEY,
+ STATE_PREP_MODIFY_KEY,
+ STATE_UPDATE_MAP_FOR_OLD,
+
+ /* prep_defrag_page, defrag_page */
+ STATE_FIND_DEFRAG,
+ STATE_ERASE_DEFRAG,
+ STATE_PREP_DEFRAG,
+ STATE_COPY_ALL_KEYS,
+ STATE_PREP_PAGE_HEADER,
+ STATE_WRITE_PAGE_HEADER,
+ STATE_UPDATE_NVREGION_MAP,
+
+ /* prepare_write */
+ STATE_FIND_WRITE_POS,
+ STATE_DEFRAG_PAGE,
+
+ /* write_key */
+ STATE_PREP_STORE,
+ STATE_PREP_KEY_DATA,
+ STATE_CLAIM_CRYPTO,
+ STATE_ERASE_OLD_KEYS,
+
+ STATE_PRIME_WRITE,
+ STATE_PERFORM_WRITE,
+ STATE_UPDATE_MAP_FOR_NEW,
+
+ STATE_SUSPENDED = 0x1E,
+ STATE_EXIT = 0x1F
+} kv_update_state_t;
+
+typedef enum {
+ EVENT_NONE,
+ EVENT_SUSPEND,
+
+ EVENT_WRITE_DATA_EXHAUSTED,
+ EVENT_WRITE_BUFFER_PRIMED,
+ EVENT_WRITE_BUFFER_STORED,
+
+ /* erase_all_keys, erase_old_keys, copy_all_keys, modify_key */
+ EVENT_PAGE_SELECTED,
+ EVENT_PAGE_NOT_SELECTED,
+ EVENT_KEY_FOUND,
+ EVENT_KEY_NOT_FOUND,
+ EVENT_COPY_KEY_READY,
+
+ /* erase_all_keys */
+ EVENT_KEYS_ERASED,
+
+ /* modify_key */
+ EVENT_KEY_UPDATE_REQUIRED,
+ EVENT_KEY_UPDATE_NOT_NEEDED,
+
+ /* prep_defrag_page */
+ EVENT_DEFRAG_FOUND,
+ EVENT_DEFRAG_ERASED,
+
+ /* defrag_page */
+ EVENT_DEFRAG_PREPARED,
+ EVENT_ALL_KEYS_COPIED,
+ EVENT_PAGE_HEADER_READY,
+ EVENT_NVREGION_MAP_UPDATED,
+
+ /* prepare_write */
+ EVENT_DEFRAG_REQUIRED,
+ EVENT_DEFRAG_COMPLETE,
+ EVENT_WRITE_POS_FOUND,
+
+ /* write_key */
+ EVENT_STORE_READY,
+ EVENT_KEY_DATA_READY,
+ EVENT_CRYPTO_CLAIMED,
+ EVENT_WRITE_COMPLETE,
+ EVENT_PAGE_MAP_UPDATED,
+
+ EVENT_ERROR = 0x1F
+} kv_update_event_t;
+
+/* State machine action function */
+typedef kv_update_event_t (*kv_state_action)(void);
+
+/* Data to be written to flash can be scattered across a chain of one or more source buffers */
+typedef struct managed_source_buffer {
+ struct managed_source_buffer *next; /* Pointer to next source buffer in chain */
+ uint8_t *data; /* Pointer to data in RAM/Flash */
+ uint16_t data_length; /* Length of the data */
+ uint16_t data_consumed; /* How much of the data has been read so far */
+ bool release_data : 1; /* Data pointer is a temporary buffer that needs freeing */
+ bool receive_hash : 1; /* Data buffer should receive a hash/CRC calculation */
+ bool hash_data : 1; /* Indicates that the data needs hashing */
+ bool crc_data : 1; /* Indicates that the data needs CRC calculation */
+ bool encrypt_data : 1; /* Indicates that the data need encrypting */
+ bool gcm_tag_data : 1; /* Indicates that the data needs AES GCM tag */
+} kv_managed_source_buffer_t;
+
+/* Write buffer, used to gather chunks of data from one or more source buffers */
+typedef struct {
+ uint8_t *data; /* RAM buffer used to hold chunks of data to be written to flash */
+ uint32_t write_location; /* Current location in flash to write to */
+ uint32_t resume_location; /* Write location reached when state machine was suspended */
+ uint32_t crypto_handle; /* AES crypo handle */
+ uint16_t size; /* Size of the RAM buffer */
+ uint16_t data_length; /* Length of data written to RAM buffer */
+ uint32_t crc_ret; /* crc_ret counter */
+ uint16_t data_consumed; /* How much of the data has been written to flash */
+ bool hash_claimed : 1; /* Hash function (SHA256) has been claimed */
+ bool encrypt_claimed : 1; /* Encryption function (AES) has been claimed */
+ bool crc_claimed : 1; /* CRC function has been claimed */
+ bool gcm_tag_claimed : 1; /* AES GCM tag has been claimed */
+} kv_managed_write_buffer_t;
+
+/* State machine state transition table entry */
+typedef struct {
+ kv_update_state_t state : 5; /* Current state */
+ kv_update_state_t next_state : 5; /* State after the transition has occurred */
+ kv_update_event_t event : 5; /* Event required to cause the transition (raised by an action function) */
+} kv_update_transition_t;
+
+/* State machine state action table entry */
+typedef struct {
+ kv_update_state_t state; /* Applicable state */
+ kv_state_action action; /* Action function to be called */
+} kv_update_action_t;
+
+/* Structure used to describe a state machine */
+typedef struct {
+ const kv_update_transition_t *transition_table; /* State Transition table for this state machine */
+ const kv_update_action_t *action_table; /* State Action table for this state machine */
+ uint16_t write_buffer_size; /* Size of the write buffer needed for this state machine */
+ kv_update_state_t initial_state; /* State to set on initial entry to this state machine */
+ kv_update_state_t resume_state; /* State to set on resuming this state machine */
+ kv_update_event_t exit_event; /* Event to send to previous state machine, if no exit action function defined */
+} kv_state_machine_t;
+
+/* Used to hold run-time details of a state machine being processed */
+/* Individual state machines will often invoke a sub state machine.
+ * These data structures will be used to form a linked list of currently active state machines, although only one
+ * state machine is ever being processed at any one time.
+ * The tail of the list will be the initial state machine, started by one of the four kv_update_xxx() functions at the
+ * end of this file.
+ * The head of the list will be the current state machine being actively processed.
+ * The 'prev' field provides a path back to the initial state machine
+ */
+typedef struct kv_active_state_machine_t {
+ struct kv_active_state_machine_t *prev; /* Previous state machine. Will be NULL if this is a root machine */
+ const kv_state_machine_t *machine; /* State machine being processed */
+ kv_managed_write_buffer_t *write_buffer; /* Holds chunk of data to be written to flash */
+ kv_managed_source_buffer_t *source_buffer; /* Linked list of source data buffers used to populate write buffer */
+ kv_managed_source_buffer_t *current_source; /* Current source buffer being used */
+ kv_update_state_t state; /* Current state machine state */
+ kv_update_event_t event; /* Last event raised (will have caused transition to current state) */
+ errcode_t error_code; /* Records the error code produced when raising an error event */
+} kv_active_state_machine_t;
+
+errcode_t kv_update_init(cores_t core);
+errcode_t kv_update_erase_key(kv_store_t core, flash_task_node *sanitised_tasks);
+errcode_t kv_update_write_key(kv_store_t core, flash_task_node *sanitised_task);
+errcode_t kv_update_modify_attribute(kv_store_t core, flash_task_node *sanitised_task);
+errcode_t kv_update_backup_init(void);
+
+#ifdef __cplusplus
+#if __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* __cplusplus */
+
+#endif
-/*\r
- * Copyright (c) @CompanyNameMagicTag 2022-2022. All rights reserved.\r
- * Description: UPG KV functions source file\r
- */\r
-\r
-#include "nv_porting.h"\r
-#if (defined(CONFIG_OTA_UPDATE_SUPPORT) && (CONFIG_NV_SUPPORT_OTA_UPDATE == NV_YES))\r
-#include "upg_common.h"\r
-#include "upg_definitions.h"\r
-#include "nv_key.h"\r
-#include "nv_update.h"\r
-#include "nv_store.h"\r
-#include "nv.h"\r
-#include "nv_storage.h"\r
-#include "upg_porting.h"\r
-#include "upg_alloc.h"\r
-#include "nv_async_store.h"\r
-#include "nv_upg.h"\r
-\r
-#define KV_INDEX 0\r
-#define KV_CRC_DATA_LENGTH 4\r
-\r
-#ifndef UPG_VERIFY_CIPHER_BUF_ATTR\r
-#define UPG_VERIFY_CIPHER_BUF_ATTR DRV_CIPHER_BUF_NONSECURE\r
-#endif\r
-#ifndef STATIC_UT\r
-#define STATIC_UT static\r
-#endif\r
-\r
-static inline uint32_t kv_aligned(uint32_t len, uint32_t align)\r
-{\r
- return ((uint32_t)(len) + ((align) - 1)) & ~((align) - 1);\r
-}\r
-\r
-STATIC_UT bool kv_upg_upgrade_is_update_requested(fota_upgrade_flag_area_t *flag_info)\r
-{\r
- upg_image_status_t status;\r
- status = upg_get_image_update_status(flag_info, KV_INDEX, UPG_IMAGE_ID_NV);\r
- if (status == UPG_IMAGE_STATUS_FINISHED || status == UPG_IMAGE_STATUS_NOT_STARTED) {\r
- return false;\r
- }\r
- return true;\r
-}\r
-\r
-STATIC_UT errcode_t kv_upg_copy_pkg_image_data(uint32_t data_addr, uint32_t data_length,\r
- uint32_t data_offset, uint32_t *data_len, uint8_t *img_data)\r
-{\r
- errcode_t ret_val;\r
- uint32_t aligned_image_len = kv_aligned(data_length, 16); /* 16-byte alignment */\r
- uint32_t actual_len;\r
-\r
- if (data_offset >= aligned_image_len || img_data == NULL ||\r
- data_len == NULL || *data_len == 0) {\r
- return ERRCODE_UPG_INVALID_PARAMETER;\r
- }\r
-\r
- actual_len = (data_offset + *data_len > aligned_image_len) ? (aligned_image_len - data_offset) : *data_len;\r
- ret_val = upg_read_fota_pkg_data(data_addr + data_offset, img_data, &actual_len);\r
- if (ret_val != ERRCODE_SUCC) {\r
- return ret_val;\r
- }\r
- *data_len = actual_len;\r
- return ERRCODE_SUCC;\r
-}\r
-\r
-STATIC_UT errcode_t kv_upg_upgrade_data_verify(uint32_t kv_data_offset, uint32_t kv_data_len, const uint8_t* hash_data)\r
-{\r
- uint32_t handle;\r
- uint32_t out_length = SHA_256_LENGTH;\r
- uint32_t image_offset = 0;\r
- uint8_t data_sha[SHA_256_LENGTH] = {0};\r
- uint32_t aligned_image_len = kv_aligned(kv_data_len, 16); /* 16-byte alignment */\r
-\r
- /* 考虑内存资源按0x1000处理 */\r
- uint8_t *image_data = (uint8_t *)kv_malloc(VERIFY_BUFF_LEN);\r
- if (image_data == NULL) {\r
- return ERRCODE_MALLOC;\r
- }\r
-\r
- errcode_t ret = uapi_drv_cipher_sha256_start(&handle);\r
- if (ret != ERRCODE_SUCC) {\r
- kv_free(image_data);\r
- return ret;\r
- }\r
-\r
- do {\r
- uint32_t read_len = (image_offset + VERIFY_BUFF_LEN < aligned_image_len) ?\r
- VERIFY_BUFF_LEN : (aligned_image_len - image_offset);\r
- ret = kv_upg_copy_pkg_image_data(kv_data_offset, kv_data_len,\r
- image_offset, &read_len, image_data);\r
- if (ret != ERRCODE_SUCC) {\r
- nv_log_err("[NV] kv upgrade kv_upg_copy_pkg_image_data error\r\n");\r
- break;\r
- }\r
-\r
- ret = uapi_drv_cipher_sha256_update(handle, image_data, read_len);\r
- if (ret != ERRCODE_SUCC) {\r
- nv_log_err("[NV] kv upgrade uapi_drv_cipher_sha256_update error\r\n");\r
- break;\r
- }\r
- image_offset += read_len;\r
- } while (image_offset < aligned_image_len);\r
-\r
- kv_free(image_data);\r
- ret = uapi_drv_cipher_sha256_finish(handle, data_sha, &out_length);\r
- if (ret != ERRCODE_SUCC) {\r
- return ret;\r
- }\r
-\r
- if (memcmp(hash_data, data_sha, SHA_256_LENGTH) != 0) {\r
- return ERRCODE_FAIL;\r
- }\r
- return ERRCODE_SUCC;\r
-}\r
-\r
-STATIC_UT errcode_t kv_upg_upgrade_key_helper(kv_store_t core, kv_key_header_t* key_header)\r
-{\r
- uint8_t attributes;\r
- flash_task_node task_node = {0};\r
- if (key_header->valid == 0 && key_header->length == 0) {\r
- task_node.data.kv_erase.key = key_header->key_id;\r
- (void)kv_update_erase_key(core, &task_node);\r
- } else {\r
- attributes = 0;\r
- if (key_header->upgrade == 0) {\r
- attributes = NV_ATTRIBUTE_NON_UPGRADE;\r
- }\r
- /* 如果既是不可升级又是永久的,属性设置为永久即可 */\r
- if (key_header->type == 0) {\r
- attributes = NV_ATTRIBUTE_PERMANENT;\r
- }\r
-\r
- task_node.data.kv.key = key_header->key_id;\r
- task_node.data.kv.kvalue = (uint8_t *)(key_header + 1);\r
- task_node.data.kv.kvalue_length = key_header->length;\r
- task_node.data.kv.attribute = attributes;\r
- (void)kv_update_write_key(core, &task_node);\r
- }\r
- return task_node.state_code;\r
-}\r
-\r
-STATIC_UT bool kv_upg_check_upgradeable(kv_key_header_t* key_header)\r
-{\r
- kv_attributes_t attr = 0;\r
- uint16_t key_length = 0;\r
-\r
- (void)kv_store_get_key_attr(KV_STORE_APPLICATION, key_header->key_id, &key_length, &attr);\r
- if ((((uint32_t)attr & NV_ATTRIBUTE_PERMANENT) != 0) || (((uint32_t)attr & NV_ATTRIBUTE_NON_UPGRADE) != 0)) {\r
- return false;\r
- }\r
- return true;\r
-}\r
-\r
-STATIC_UT kv_store_t kv_upg_convert_core_id(uint16_t core_id)\r
-{\r
- switch (core_id) {\r
- case KV_STORE_ID_ACPU:\r
- return KV_STORE_APPLICATION;\r
- default:\r
- return KV_STORE_MAX_NUM;\r
- }\r
-}\r
-\r
-STATIC_UT errcode_t kv_upg_process_one_page(kv_store_t core, uint8_t* image_data, uint32_t data_len)\r
-{\r
- errcode_t ret;\r
- uint32_t offset = 0;\r
- kv_key_header_t* key_header = NULL;\r
- uint32_t key_flash_size;\r
- uint32_t key_length;\r
-\r
- key_header = (kv_key_header_t *)image_data;\r
- while (offset + sizeof(kv_key_header_t) <= data_len &&\r
- offset + sizeof(kv_key_header_t) + key_header->length <= data_len) {\r
- if (kv_upg_check_upgradeable(key_header)) {\r
- ret = kv_upg_upgrade_key_helper(core, key_header);\r
- if (ret != ERRCODE_FLASH_TASK_COMPLETED && ret != ERRCODE_NV_KEY_NOT_FOUND) {\r
- nv_log_err("[NV] kv upgrade error keyid:0x%x\r\n", key_header->key_id);\r
- return ret;\r
- }\r
- }\r
- key_length = kv_aligned(key_header->length, 4); /* 4-byte alignment */\r
- key_flash_size = (uint32_t)sizeof(kv_key_header_t) + key_length + KV_CRC_DATA_LENGTH;\r
- offset += key_flash_size;\r
- key_header = (kv_key_header_t *)((uintptr_t)key_header + key_flash_size);\r
- }\r
-\r
- return ERRCODE_SUCC;\r
-}\r
-\r
-STATIC_UT errcode_t kv_upg_process_kv_data(uint32_t kv_data_offset, uint32_t length)\r
-{\r
- errcode_t ret;\r
- uint32_t offset = 0;\r
- uint32_t read_len;\r
- kv_page_header_t *page_head = NULL;\r
- kv_store_t core;\r
-\r
- if (length % VERIFY_BUFF_LEN != 0) {\r
- nv_log_err("[NV] kv upgrade data length error!\r\n");\r
- return ERRCODE_FAIL;\r
- }\r
-\r
- uint8_t *image_data = (uint8_t *)kv_malloc(VERIFY_BUFF_LEN);\r
- if (image_data == NULL) {\r
- return ERRCODE_MALLOC;\r
- }\r
-\r
- while (offset < length) {\r
- read_len = VERIFY_BUFF_LEN;\r
- ret = kv_upg_copy_pkg_image_data(kv_data_offset, length, offset, &read_len, image_data);\r
- if (ret != ERRCODE_SUCC) {\r
- kv_free(image_data);\r
- return ret;\r
- }\r
-\r
- page_head = (kv_page_header_t*)image_data;\r
- core = kv_upg_convert_core_id(page_head->details.store_id);\r
- if (core == KV_STORE_MAX_NUM) {\r
- offset += VERIFY_BUFF_LEN;\r
- continue;\r
- }\r
-\r
- ret = kv_upg_process_one_page(core, image_data + sizeof(kv_page_header_t), read_len - sizeof(kv_page_header_t));\r
- if (ret != ERRCODE_SUCC) {\r
- nv_log_err("[NV] kv upgrade data error read_len_:%d offset:%d\r\n", read_len, offset);\r
- kv_free(image_data);\r
- return ret;\r
- }\r
-\r
- offset += VERIFY_BUFF_LEN;\r
- }\r
- kv_free(image_data);\r
- return ERRCODE_SUCC;\r
-}\r
-\r
-STATIC_UT errcode_t kv_upg_upgrade_process(fota_upgrade_flag_area_t* upg_flag)\r
-{\r
- errcode_t ret;\r
- uint32_t hash_read_len = upg_flag->nv_hash_len;\r
-\r
- uint8_t *hash_data = (uint8_t *)kv_malloc(upg_flag->nv_hash_len);\r
- if (hash_data == NULL) {\r
- return ERRCODE_MALLOC;\r
- }\r
-\r
- ret = kv_upg_copy_pkg_image_data(upg_flag->nv_hash_offset, upg_flag->nv_hash_len,\r
- 0, &hash_read_len, hash_data);\r
- if (ret != ERRCODE_SUCC) {\r
- kv_free(hash_data);\r
- return ret;\r
- }\r
-\r
- ret = kv_upg_upgrade_data_verify(upg_flag->nv_data_offset, upg_flag->nv_data_len, hash_data);\r
- if (ret != ERRCODE_SUCC) {\r
- nv_log_err("[NV] upg verify fail!\r\n");\r
- kv_free(hash_data);\r
- return ret;\r
- }\r
-\r
- kv_free(hash_data);\r
-\r
- ret = kv_upg_process_kv_data(upg_flag->nv_data_offset, upg_flag->nv_data_len);\r
-\r
- return ret;\r
-}\r
-\r
-#endif /* (defined(CONFIG_OTA_UPDATE_SUPPORT) && (CONFIG_NV_SUPPORT_OTA_UPDATE == NV_YES)) */\r
-\r
-errcode_t nv_upg_upgrade_task_process(void)\r
-{\r
-#if (defined(CONFIG_OTA_UPDATE_SUPPORT) && (CONFIG_NV_SUPPORT_OTA_UPDATE == NV_YES))\r
-\r
- errcode_t ret;\r
- upg_image_status_switch_t status;\r
- fota_upgrade_flag_area_t *upg_flag_info = NULL;\r
-\r
- ret = upg_alloc_and_get_upgrade_flag(&upg_flag_info);\r
- if (ret != ERRCODE_SUCC || upg_flag_info == NULL) {\r
- nv_log_err("[NV] get upgrade flag error!\r\n");\r
- return ret;\r
- }\r
-\r
- if (kv_upg_upgrade_is_update_requested(upg_flag_info) == false) {\r
- nv_log_info("[NV] Not need to upgrade NV ...\r\n");\r
- upg_free(upg_flag_info);\r
- return ERRCODE_SUCC;\r
- }\r
-\r
- ret = upg_set_firmware_update_status(upg_flag_info, UPG_IMAGE_ID_NV, UPG_IMAGE_STATUS_SWITCH_TO_STARTED);\r
- if (ret != ERRCODE_SUCC) {\r
- nv_log_err("[NV] upg set update status error\r\n");\r
- }\r
-\r
- ret = kv_upg_upgrade_process(upg_flag_info);\r
- if (ret == ERRCODE_SUCC) {\r
- nv_log_err("[NV] kv upgrade success!\r\n");\r
- status = UPG_IMAGE_STATUS_SWITCH_TO_FINISHED;\r
- } else {\r
- nv_log_err("[NV] kv upgrade fail! error code:0x%x", ret);\r
- status = UPG_IMAGE_STATUS_SWITCH_TO_RETRY;\r
- }\r
-\r
- ret = upg_set_firmware_update_status(upg_flag_info, UPG_IMAGE_ID_NV, status);\r
- upg_free(upg_flag_info);\r
-\r
- /* 升级成功之后再进行NV-Hashtable的初始化 */\r
-#if (CONFIG_NV_SUPPORT_ASYNCHRONOUS_STORE == NV_YES)\r
- ret = nv_key_map_init(KV_STORE_APPLICATION, kv_store_get_page_count(KV_STORE_APPLICATION));\r
-#endif\r
- return ret;\r
-\r
-#endif\r
- return ERRCODE_SUCC;\r
-}\r
-\r
+/*
+ * Copyright (c) @CompanyNameMagicTag 2022-2022. All rights reserved.
+ * Description: UPG KV functions source file
+ */
+
+#include "nv_porting.h"
+#if (defined(CONFIG_OTA_UPDATE_SUPPORT) && (CONFIG_NV_SUPPORT_OTA_UPDATE == NV_YES))
+#include "upg_common.h"
+#include "upg_definitions.h"
+#include "nv_key.h"
+#include "nv_update.h"
+#include "nv_store.h"
+#include "nv.h"
+#include "nv_storage.h"
+#include "upg_porting.h"
+#include "upg_alloc.h"
+#include "nv_async_store.h"
+#include "nv_upg.h"
+
+#define KV_INDEX 0
+#define KV_CRC_DATA_LENGTH 4
+
+#ifndef UPG_VERIFY_CIPHER_BUF_ATTR
+#define UPG_VERIFY_CIPHER_BUF_ATTR DRV_CIPHER_BUF_NONSECURE
+#endif
+#ifndef STATIC_UT
+#define STATIC_UT static
+#endif
+
+static inline uint32_t kv_aligned(uint32_t len, uint32_t align)
+{
+ return ((uint32_t)(len) + ((align) - 1)) & ~((align) - 1);
+}
+
+STATIC_UT bool kv_upg_upgrade_is_update_requested(fota_upgrade_flag_area_t *flag_info)
+{
+ upg_image_status_t status;
+ status = upg_get_image_update_status(flag_info, KV_INDEX, UPG_IMAGE_ID_NV);
+ if (status == UPG_IMAGE_STATUS_FINISHED || status == UPG_IMAGE_STATUS_NOT_STARTED) {
+ return false;
+ }
+ return true;
+}
+
+STATIC_UT errcode_t kv_upg_copy_pkg_image_data(uint32_t data_addr, uint32_t data_length,
+ uint32_t data_offset, uint32_t *data_len, uint8_t *img_data)
+{
+ errcode_t ret_val;
+ uint32_t aligned_image_len = kv_aligned(data_length, 16); /* 16-byte alignment */
+ uint32_t actual_len;
+
+ if (data_offset >= aligned_image_len || img_data == NULL ||
+ data_len == NULL || *data_len == 0) {
+ return ERRCODE_UPG_INVALID_PARAMETER;
+ }
+
+ actual_len = (data_offset + *data_len > aligned_image_len) ? (aligned_image_len - data_offset) : *data_len;
+ ret_val = upg_read_fota_pkg_data(data_addr + data_offset, img_data, &actual_len);
+ if (ret_val != ERRCODE_SUCC) {
+ return ret_val;
+ }
+ *data_len = actual_len;
+ return ERRCODE_SUCC;
+}
+
+STATIC_UT errcode_t kv_upg_upgrade_data_verify(uint32_t kv_data_offset, uint32_t kv_data_len, const uint8_t* hash_data)
+{
+ uint32_t handle;
+ uint32_t out_length = SHA_256_LENGTH;
+ uint32_t image_offset = 0;
+ uint8_t data_sha[SHA_256_LENGTH] = {0};
+ uint32_t aligned_image_len = kv_aligned(kv_data_len, 16); /* 16-byte alignment */
+
+ /* 考虑内存资源按0x1000处理 */
+ uint8_t *image_data = (uint8_t *)kv_malloc(VERIFY_BUFF_LEN);
+ if (image_data == NULL) {
+ return ERRCODE_MALLOC;
+ }
+
+ errcode_t ret = uapi_drv_cipher_sha256_start(&handle);
+ if (ret != ERRCODE_SUCC) {
+ kv_free(image_data);
+ return ret;
+ }
+
+ do {
+ uint32_t read_len = (image_offset + VERIFY_BUFF_LEN < aligned_image_len) ?
+ VERIFY_BUFF_LEN : (aligned_image_len - image_offset);
+ ret = kv_upg_copy_pkg_image_data(kv_data_offset, kv_data_len,
+ image_offset, &read_len, image_data);
+ if (ret != ERRCODE_SUCC) {
+ nv_log_err("[NV] kv upgrade kv_upg_copy_pkg_image_data error\r\n");
+ break;
+ }
+
+ ret = uapi_drv_cipher_sha256_update(handle, image_data, read_len);
+ if (ret != ERRCODE_SUCC) {
+ nv_log_err("[NV] kv upgrade uapi_drv_cipher_sha256_update error\r\n");
+ break;
+ }
+ image_offset += read_len;
+ } while (image_offset < aligned_image_len);
+
+ kv_free(image_data);
+ ret = uapi_drv_cipher_sha256_finish(handle, data_sha, &out_length);
+ if (ret != ERRCODE_SUCC) {
+ return ret;
+ }
+
+ if (memcmp(hash_data, data_sha, SHA_256_LENGTH) != 0) {
+ return ERRCODE_FAIL;
+ }
+ return ERRCODE_SUCC;
+}
+
+STATIC_UT errcode_t kv_upg_upgrade_key_helper(kv_store_t core, kv_key_header_t* key_header)
+{
+ uint8_t attributes;
+ flash_task_node task_node = {0};
+ if (key_header->valid == 0 && key_header->length == 0) {
+ task_node.data.kv_erase.key = key_header->key_id;
+ (void)kv_update_erase_key(core, &task_node);
+ } else {
+ attributes = 0;
+ if (key_header->upgrade == 0) {
+ attributes = NV_ATTRIBUTE_NON_UPGRADE;
+ }
+ /* 如果既是不可升级又是永久的,属性设置为永久即可 */
+ if (key_header->type == 0) {
+ attributes = NV_ATTRIBUTE_PERMANENT;
+ }
+
+ task_node.data.kv.key = key_header->key_id;
+ task_node.data.kv.kvalue = (uint8_t *)(key_header + 1);
+ task_node.data.kv.kvalue_length = key_header->length;
+ task_node.data.kv.attribute = attributes;
+ (void)kv_update_write_key(core, &task_node);
+ }
+ return task_node.state_code;
+}
+
+STATIC_UT bool kv_upg_check_upgradeable(kv_key_header_t* key_header)
+{
+ kv_attributes_t attr = 0;
+ uint16_t key_length = 0;
+
+ (void)kv_store_get_key_attr(KV_STORE_APPLICATION, key_header->key_id, &key_length, &attr);
+ if ((((uint32_t)attr & NV_ATTRIBUTE_PERMANENT) != 0) || (((uint32_t)attr & NV_ATTRIBUTE_NON_UPGRADE) != 0)) {
+ return false;
+ }
+ return true;
+}
+
+STATIC_UT kv_store_t kv_upg_convert_core_id(uint16_t core_id)
+{
+ switch (core_id) {
+ case KV_STORE_ID_ACPU:
+ return KV_STORE_APPLICATION;
+ default:
+ return KV_STORE_MAX_NUM;
+ }
+}
+
+STATIC_UT errcode_t kv_upg_process_one_page(kv_store_t core, uint8_t* image_data, uint32_t data_len)
+{
+ errcode_t ret;
+ uint32_t offset = 0;
+ kv_key_header_t* key_header = NULL;
+ uint32_t key_flash_size;
+ uint32_t key_length;
+
+ key_header = (kv_key_header_t *)image_data;
+ while (offset + sizeof(kv_key_header_t) <= data_len &&
+ offset + sizeof(kv_key_header_t) + key_header->length <= data_len) {
+ if (kv_upg_check_upgradeable(key_header)) {
+ ret = kv_upg_upgrade_key_helper(core, key_header);
+ if (ret != ERRCODE_FLASH_TASK_COMPLETED && ret != ERRCODE_NV_KEY_NOT_FOUND) {
+ nv_log_err("[NV] kv upgrade error keyid:0x%x\r\n", key_header->key_id);
+ return ret;
+ }
+ }
+ key_length = kv_aligned(key_header->length, 4); /* 4-byte alignment */
+ key_flash_size = (uint32_t)sizeof(kv_key_header_t) + key_length + KV_CRC_DATA_LENGTH;
+ offset += key_flash_size;
+ key_header = (kv_key_header_t *)((uintptr_t)key_header + key_flash_size);
+ }
+
+ return ERRCODE_SUCC;
+}
+
+STATIC_UT errcode_t kv_upg_process_kv_data(uint32_t kv_data_offset, uint32_t length)
+{
+ errcode_t ret;
+ uint32_t offset = 0;
+ uint32_t read_len;
+ kv_page_header_t *page_head = NULL;
+ kv_store_t core;
+
+ if (length % VERIFY_BUFF_LEN != 0) {
+ nv_log_err("[NV] kv upgrade data length error!\r\n");
+ return ERRCODE_FAIL;
+ }
+
+ uint8_t *image_data = (uint8_t *)kv_malloc(VERIFY_BUFF_LEN);
+ if (image_data == NULL) {
+ return ERRCODE_MALLOC;
+ }
+
+ while (offset < length) {
+ read_len = VERIFY_BUFF_LEN;
+ ret = kv_upg_copy_pkg_image_data(kv_data_offset, length, offset, &read_len, image_data);
+ if (ret != ERRCODE_SUCC) {
+ kv_free(image_data);
+ return ret;
+ }
+
+ page_head = (kv_page_header_t*)image_data;
+ core = kv_upg_convert_core_id(page_head->details.store_id);
+ if (core == KV_STORE_MAX_NUM) {
+ offset += VERIFY_BUFF_LEN;
+ continue;
+ }
+
+ ret = kv_upg_process_one_page(core, image_data + sizeof(kv_page_header_t), read_len - sizeof(kv_page_header_t));
+ if (ret != ERRCODE_SUCC) {
+ nv_log_err("[NV] kv upgrade data error read_len_:%d offset:%d\r\n", read_len, offset);
+ kv_free(image_data);
+ return ret;
+ }
+
+ offset += VERIFY_BUFF_LEN;
+ }
+ kv_free(image_data);
+ return ERRCODE_SUCC;
+}
+
+STATIC_UT errcode_t kv_upg_upgrade_process(fota_upgrade_flag_area_t* upg_flag)
+{
+ errcode_t ret;
+ uint32_t hash_read_len = upg_flag->nv_hash_len;
+
+ uint8_t *hash_data = (uint8_t *)kv_malloc(upg_flag->nv_hash_len);
+ if (hash_data == NULL) {
+ return ERRCODE_MALLOC;
+ }
+
+ ret = kv_upg_copy_pkg_image_data(upg_flag->nv_hash_offset, upg_flag->nv_hash_len,
+ 0, &hash_read_len, hash_data);
+ if (ret != ERRCODE_SUCC) {
+ kv_free(hash_data);
+ return ret;
+ }
+
+ ret = kv_upg_upgrade_data_verify(upg_flag->nv_data_offset, upg_flag->nv_data_len, hash_data);
+ if (ret != ERRCODE_SUCC) {
+ nv_log_err("[NV] upg verify fail!\r\n");
+ kv_free(hash_data);
+ return ret;
+ }
+
+ kv_free(hash_data);
+
+ ret = kv_upg_process_kv_data(upg_flag->nv_data_offset, upg_flag->nv_data_len);
+
+ return ret;
+}
+
+#endif /* (defined(CONFIG_OTA_UPDATE_SUPPORT) && (CONFIG_NV_SUPPORT_OTA_UPDATE == NV_YES)) */
+
+errcode_t nv_upg_upgrade_task_process(void)
+{
+#if (defined(CONFIG_OTA_UPDATE_SUPPORT) && (CONFIG_NV_SUPPORT_OTA_UPDATE == NV_YES))
+
+ errcode_t ret;
+ upg_image_status_switch_t status;
+ fota_upgrade_flag_area_t *upg_flag_info = NULL;
+
+ ret = upg_alloc_and_get_upgrade_flag(&upg_flag_info);
+ if (ret != ERRCODE_SUCC || upg_flag_info == NULL) {
+ nv_log_err("[NV] get upgrade flag error!\r\n");
+ return ret;
+ }
+
+ if (kv_upg_upgrade_is_update_requested(upg_flag_info) == false) {
+ nv_log_info("[NV] Not need to upgrade NV ...\r\n");
+ upg_free(upg_flag_info);
+ return ERRCODE_SUCC;
+ }
+
+ ret = upg_set_firmware_update_status(upg_flag_info, UPG_IMAGE_ID_NV, UPG_IMAGE_STATUS_SWITCH_TO_STARTED);
+ if (ret != ERRCODE_SUCC) {
+ nv_log_err("[NV] upg set update status error\r\n");
+ }
+
+ ret = kv_upg_upgrade_process(upg_flag_info);
+ if (ret == ERRCODE_SUCC) {
+ nv_log_err("[NV] kv upgrade success!\r\n");
+ status = UPG_IMAGE_STATUS_SWITCH_TO_FINISHED;
+ } else {
+ nv_log_err("[NV] kv upgrade fail! error code:0x%x", ret);
+ status = UPG_IMAGE_STATUS_SWITCH_TO_RETRY;
+ }
+
+ ret = upg_set_firmware_update_status(upg_flag_info, UPG_IMAGE_ID_NV, status);
+ upg_free(upg_flag_info);
+
+ /* 升级成功之后再进行NV-Hashtable的初始化 */
+#if (CONFIG_NV_SUPPORT_ASYNCHRONOUS_STORE == NV_YES)
+ ret = nv_key_map_init(KV_STORE_APPLICATION, kv_store_get_page_count(KV_STORE_APPLICATION));
+#endif
+ return ret;
+
+#endif
+ return ERRCODE_SUCC;
+}
+
-/*\r
- * Copyright (c) @CompanyNameMagicTag 2021-2022. All rights reserved.\r
- * Description: UPG nv functions header file\r
- */\r
-\r
-#ifndef NV_UPG_H\r
-#define NV_UPG_H\r
-\r
-#include "errcode.h"\r
-\r
-#ifdef __cplusplus\r
-#if __cplusplus\r
-extern "C" {\r
-#endif /* __cplusplus */\r
-#endif /* __cplusplus */\r
-\r
-errcode_t nv_upg_upgrade_task_process(void);\r
-\r
-#ifdef __cplusplus\r
-#if __cplusplus\r
-}\r
-#endif /* __cplusplus */\r
-#endif /* __cplusplus */\r
-\r
+/*
+ * Copyright (c) @CompanyNameMagicTag 2021-2022. All rights reserved.
+ * Description: UPG nv functions header file
+ */
+
+#ifndef NV_UPG_H
+#define NV_UPG_H
+
+#include "errcode.h"
+
+#ifdef __cplusplus
+#if __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+#endif /* __cplusplus */
+
+errcode_t nv_upg_upgrade_task_process(void);
+
+#ifdef __cplusplus
+#if __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* __cplusplus */
+
#endif /* NV_UPG_H */
\ No newline at end of file