diff --git a/src/wh_nvm.c b/src/wh_nvm.c index ae6c662d1..2ea7d7c82 100644 --- a/src/wh_nvm.c +++ b/src/wh_nvm.c @@ -192,7 +192,7 @@ int wh_Nvm_DestroyObjects(whNvmContext* context, whNvmId list_count, int wh_Nvm_Read(whNvmContext* context, whNvmId id, whNvmSize offset, - whNvmSize data_len, uint8_t* data) + whNvmSize data_len, uint8_t* data) { if ( (context == NULL) || (context->cb == NULL) ) { diff --git a/src/wh_nvm_flash.c b/src/wh_nvm_flash.c index aa7a95b86..3d83ab476 100644 --- a/src/wh_nvm_flash.c +++ b/src/wh_nvm_flash.c @@ -133,8 +133,8 @@ static int nfObject_Program(whNvmFlashContext* context, int partition, int object_index, uint32_t epoch, whNvmMetadata* meta, uint32_t start, const uint8_t* data); static int nfObject_ReadDataBytes(whNvmFlashContext* context, int partition, - int object_index, uint32_t byte_offset, uint32_t byte_count, - uint8_t* out_data); + int object_index, uint32_t byte_offset, + uint32_t byte_count, uint8_t* out_data); static int nfObject_Copy(whNvmFlashContext* context, int object_index, int partition, uint32_t *inout_next_object, uint32_t *inout_next_data); @@ -652,8 +652,8 @@ static int nfObject_Program(whNvmFlashContext* context, int partition, } static int nfObject_ReadDataBytes(whNvmFlashContext* context, int partition, - int object_index, - uint32_t byte_offset, uint32_t byte_count, uint8_t* out_data) + int object_index, uint32_t byte_offset, + uint32_t byte_count, uint8_t* out_data) { int start = 0; uint32_t startOffset = 0; @@ -676,18 +676,16 @@ static int nfObject_ReadDataBytes(whNvmFlashContext* context, int partition, } /* Ensure we don't read off the end of the active partition */ - if (WH_ERROR_OK != nfPartition_CheckDataRange(context, partition, - startOffset * WHFU_BYTES_PER_UNIT + byte_offset, - byte_count)) { + if (WH_ERROR_OK != nfPartition_CheckDataRange( + context, partition, + startOffset * WHFU_BYTES_PER_UNIT + byte_offset, + byte_count)) { return WH_ERROR_BADARGS; } return wh_FlashUnit_ReadBytes( - context->cb, - context->flash, - startOffset * WHFU_BYTES_PER_UNIT + byte_offset, - byte_count, - out_data); + context->cb, context->flash, + startOffset * WHFU_BYTES_PER_UNIT + byte_offset, byte_count, out_data); } static int nfObject_Copy(whNvmFlashContext* context, int object_index, @@ -728,13 +726,8 @@ static int nfObject_Copy(whNvmFlashContext* context, int object_index, } /* Read the data from the old object. */ - ret = nfObject_ReadDataBytes( - context, - context->active, - object_index, - data_offset, - this_len, - buffer); + ret = nfObject_ReadDataBytes(context, context->active, object_index, + data_offset, this_len, buffer); if (ret != 0) return ret; /* Write the data to the new object. */ @@ -1243,30 +1236,23 @@ int wh_NvmFlash_DestroyObjects(void* c, whNvmId list_count, } /* Read the data of the object starting at the byte offset */ -int wh_NvmFlash_Read(void* c, whNvmId id, whNvmSize offset, - whNvmSize data_len, uint8_t* data) +int wh_NvmFlash_Read(void* c, whNvmId id, whNvmSize offset, whNvmSize data_len, + uint8_t* data) { whNvmFlashContext* context = c; int ret = 0; - int object_index = -1; + int object_index = -1; if ( (context == NULL) || ((data_len > 0) && (data == NULL)) ){ return WH_ERROR_BADARGS; } - ret = nfMemDirectory_FindObjectIndexById( - &context->directory, - id, - &object_index); + ret = nfMemDirectory_FindObjectIndexById(&context->directory, id, + &object_index); if (ret == 0) { - ret = nfObject_ReadDataBytes( - context, - context->active, - object_index, - offset, - data_len, - data); + ret = nfObject_ReadDataBytes(context, context->active, object_index, + offset, data_len, data); } return ret; } diff --git a/src/wh_nvm_flash_log.c b/src/wh_nvm_flash_log.c new file mode 100644 index 000000000..e0ca8d489 --- /dev/null +++ b/src/wh_nvm_flash_log.c @@ -0,0 +1,698 @@ +/* + * Copyright (C) 2025 wolfSSL Inc. + * + * This file is part of wolfHSM. + * + * wolfHSM is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfHSM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with wolfHSM. If not, see . + */ + +/* + * wh NVM Flash Layer + * + * This NVM layer provides secure, atomic storage of data on flash devices + * with large write granularity (e.g., 64 bytes). The layer manages two + * equal-sized partitions in flash, with only one partition active at any time. + * All objects metadata are cached in memory for fast access. + * + * Atomicity Guarantee: + * On every modification, the inactive partition is erased, all objects are + * written to it, and only then is the partition header (containing an + * incremented epoch counter) programmed. At initialization, the layer selects + * the partition with the highest epoch as active, ensuring that after any + * interruption, either the state before or after the write is valid. + * + * Object Storage Format: + * Objects are stored back-to-back in the partition, each consisting of a + * whNvmMetadata structure immediately followed by the object data. + * + * Write Padding: + * All writes are padded to the flash's write granularity. + * + * Flash backend: + * This layer relies on the same flash backend as wh_Flash, using the whFlashCb + * interface. + * + * Limitations and performance considerations: + * + * The implementation favors simplicity over both speed and space. + * + * Regarding space: + * - An area of RAM as big as one partition is allocated to cache the + * partition. + * + * Regarding speed: + * - Each updates to the NVM (create, write, delete) requires copying all the + * objects from one partition of the flash to the other + erase operations. + * + * Possible future improvements: + * + * - cache only the metadata in RAM, access data on the FLASH as needed + * - use a true append log format to avoid copying all objects on each update + * + * Right now the implementation works well for read-heavy workloads with few + * updates. + * + * Alignment consideration: + * + * The implementation assure that writes are aligned to WRITE_GRANULARITY in + * FLASH memory space. The source data passed on the flash layer might not be + * aligned. + */ + +#include "wolfhsm/wh_settings.h" + +#if defined(WOLFHSM_CFG_SERVER_NVM_FLASH_LOG) + +#include +#include +#include + +#include "wolfhsm/wh_common.h" +#include "wolfhsm/wh_error.h" +#include "wolfhsm/wh_flash.h" + +#include "wolfhsm/wh_nvm_flash_log.h" + +#define PAD_SIZE(size) \ + (((size) + WH_NVM_FLASH_LOG_WRITE_GRANULARITY - 1) & \ + ~(WH_NVM_FLASH_LOG_WRITE_GRANULARITY - 1)) + +typedef struct { + union { + whNvmMetadata meta; + uint8_t WH_PAD[PAD_SIZE(sizeof(whNvmMetadata))]; + }; +} whNvmFlashLogMetadata; + +/* do a blank check + program + verify */ +static int nfl_FlashProgramHelper(whNvmFlashLogContext* ctx, uint32_t off, + const uint8_t* data, uint32_t len) +{ + int ret; + + if (ctx == NULL || (data == NULL && len > 0)) + return WH_ERROR_BADARGS; + + ret = ctx->flash_cb->BlankCheck(ctx->flash_ctx, off, len); + if (ret != 0) + return ret; + ret = ctx->flash_cb->Program(ctx->flash_ctx, off, len, data); + if (ret != 0) + return ret; + ret = ctx->flash_cb->Verify(ctx->flash_ctx, off, len, data); + if (ret != 0) + return ret; + return WH_ERROR_OK; +} + +/* do a erase + blank check */ +static int nfl_FlashEraseHelper(whNvmFlashLogContext* ctx, uint32_t off, + uint32_t len) +{ + int ret; + + if (ctx == NULL) + return WH_ERROR_BADARGS; + ret = ctx->flash_cb->Erase(ctx->flash_ctx, off, len); + if (ret != 0) + return ret; + ret = ctx->flash_cb->BlankCheck(ctx->flash_ctx, off, len); + if (ret != 0) + return ret; + + return WH_ERROR_OK; +} + +static whNvmFlashLogMetadata* nfl_ObjNext(whNvmFlashLogContext* ctx, + whNvmFlashLogMetadata* obj) +{ + if (obj == NULL || ctx == NULL) + return NULL; + uint8_t* next = + (uint8_t*)obj + sizeof(whNvmFlashLogMetadata) + PAD_SIZE(obj->meta.len); + if (next >= ctx->directory.data + ctx->directory.header.size) + return NULL; + return (whNvmFlashLogMetadata*)next; +} + +static int nfl_PartitionErase(whNvmFlashLogContext* ctx, uint32_t partition) +{ + uint32_t off; + + if (ctx == NULL || partition > 1) + return WH_ERROR_BADARGS; + + off = partition * ctx->partition_size; + return nfl_FlashEraseHelper(ctx, off, ctx->partition_size); +} + +static int nfl_PartitionWrite(whNvmFlashLogContext* ctx, uint32_t partition) +{ + uint32_t off; + int ret; + + if (ctx == NULL || partition > 1) + return WH_ERROR_BADARGS; + + off = partition * ctx->partition_size; + + if (ctx->directory.header.size % WH_NVM_FLASH_LOG_WRITE_GRANULARITY != 0) + return WH_ERROR_ABORTED; + + if (ctx->directory.header.size > 0) { + ret = nfl_FlashProgramHelper( + ctx, off + sizeof(whNvmFlashLogPartitionHeader), + ctx->directory.data, ctx->directory.header.size); + if (ret != 0) + return ret; + } + + return WH_ERROR_OK; +} + +static int nfl_PartitionCommit(whNvmFlashLogContext* ctx, uint32_t partition) +{ + const whFlashCb* f_cb; + uint32_t off; + int ret; + + if (ctx == NULL || partition > 1) + return WH_ERROR_BADARGS; + + off = partition * ctx->partition_size; + f_cb = ctx->flash_cb; + ret = f_cb->BlankCheck(ctx->flash_ctx, off, + sizeof(whNvmFlashLogPartitionHeader)); + if (ret != 0) + return ret; + + ret = nfl_FlashProgramHelper(ctx, off, (uint8_t*)&ctx->directory.header, + sizeof(whNvmFlashLogPartitionHeader)); + if (ret != 0) + return ret; + + return WH_ERROR_OK; +} + +static int nfl_PartitionChoose(whNvmFlashLogContext* ctx) +{ + whNvmFlashLogPartitionHeader header0, header1; + const whFlashCb* f_cb; + uint32_t part1_offset; + int part0_blank, part1_blank; + int ret; + + if (ctx == NULL) + return WH_ERROR_BADARGS; + + part1_offset = ctx->partition_size; + f_cb = ctx->flash_cb; + ret = f_cb->BlankCheck(ctx->flash_ctx, 0, sizeof(header0)); + if (ret != 0 && ret != WH_ERROR_NOTBLANK) { + return ret; + } + part0_blank = (ret == 0); + + ret = f_cb->BlankCheck(ctx->flash_ctx, part1_offset, sizeof(header0)); + if (ret != 0 && ret != WH_ERROR_NOTBLANK) { + return ret; + } + part1_blank = (ret == 0); + + if (part0_blank && part1_blank) { + /* Both partitions headers are blank, start with partition 0 */ + ret = nfl_PartitionErase(ctx, 0); + if (ret != 0) + return ret; + ret = nfl_PartitionCommit(ctx, 0); + if (ret != 0) + return ret; + return WH_ERROR_OK; + } + + if (part0_blank) { + ctx->active_partition = 1; + return WH_ERROR_OK; + } + + if (part1_blank) { + ctx->active_partition = 0; + return WH_ERROR_OK; + } + + /* both partition are programmed */ + ret = f_cb->Read(ctx->flash_ctx, 0, sizeof(header0), (uint8_t*)&header0); + if (ret != 0) { + return ret; + } + ret = f_cb->Read(ctx->flash_ctx, part1_offset, sizeof(header1), + (uint8_t*)&header1); + if (ret != 0) { + return ret; + } + + if (header0.partition_epoch > header1.partition_epoch) { + ctx->active_partition = 0; + } + else { + ctx->active_partition = 1; + } + + return WH_ERROR_OK; +} + +static whNvmFlashLogMetadata* nfl_ObjectFindById(whNvmFlashLogContext* ctx, + whNvmId id) +{ + whNvmFlashLogMetadata* obj; + + if (ctx == NULL || id == WH_NVM_ID_INVALID) + return NULL; + + obj = (whNvmFlashLogMetadata*)ctx->directory.data; + while (obj != NULL && obj->meta.id != WH_NVM_ID_INVALID) { + if (obj->meta.id == id) { + return obj; + } + obj = nfl_ObjNext(ctx, obj); + } + return NULL; +} + +static int nfl_ObjectDestroy(whNvmFlashLogContext* ctx, whNvmId id) +{ + whNvmFlashLogMetadata* obj; + uint32_t len; + uint32_t off; + uint32_t tail; + + if (ctx == NULL || id == WH_NVM_ID_INVALID) + return WH_ERROR_BADARGS; + + obj = nfl_ObjectFindById(ctx, id); + if (obj == NULL) + return WH_ERROR_OK; + + len = sizeof(whNvmFlashLogMetadata) + PAD_SIZE(obj->meta.len); + off = (uint8_t*)obj - ctx->directory.data; + tail = ctx->directory.header.size - (off + len); + memmove(obj, (uint8_t*)obj + len, tail); + /* be sure to clean-up moved objects from memory */ + memset((uint8_t*)obj + tail, 0, len); + ctx->directory.header.size -= len; + return WH_ERROR_OK; +} + +static int nfl_ObjectCount(whNvmFlashLogContext* ctx, + whNvmFlashLogMetadata* startObj) +{ + int count = 0; + + if (ctx == NULL) + return 0; + + if (startObj == NULL) { + startObj = (whNvmFlashLogMetadata*)ctx->directory.data; + } + + if ((uint8_t*)startObj < ctx->directory.data || + (uint8_t*)startObj >= + ctx->directory.data + ctx->directory.header.size) { + return 0; + } + + while (startObj != NULL) { + if (startObj->meta.id == WH_NVM_ID_INVALID) { + break; + } + count++; + startObj = nfl_ObjNext(ctx, startObj); + } + + return count; +} + +static int nfl_PartitionRead(whNvmFlashLogContext* ctx) +{ + const whFlashCb* f_cb; + uint32_t off; + int ret; + + if (ctx == NULL) + return WH_ERROR_BADARGS; + + f_cb = ctx->flash_cb; + off = ctx->active_partition * ctx->partition_size; + + ret = f_cb->Read(ctx->flash_ctx, off, sizeof(whNvmFlashLogPartitionHeader), + (uint8_t*)&ctx->directory.header); + if (ret != 0) + return ret; + + if (ctx->directory.header.size > + ctx->partition_size - sizeof(whNvmFlashLogPartitionHeader)) { + return WH_ERROR_ABORTED; + } + + if (ctx->directory.header.size > 0) { + ret = f_cb->Read(ctx->flash_ctx, + off + sizeof(whNvmFlashLogPartitionHeader), + ctx->directory.header.size, ctx->directory.data); + if (ret != 0) + return ret; + } + + return WH_ERROR_OK; +} + +static int nfl_PartitionNewEpoch(whNvmFlashLogContext* ctx) +{ + int next_active; + int ret; + + if (ctx == NULL) + return WH_ERROR_BADARGS; + + next_active = (ctx->active_partition == 0) ? 1 : 0; + ctx->directory.header.partition_epoch++; + ret = nfl_PartitionErase(ctx, next_active); + if (ret != 0) + return ret; + ret = nfl_PartitionWrite(ctx, next_active); + if (ret != 0) + return ret; + ret = nfl_PartitionCommit(ctx, next_active); + if (ret != 0) + return ret; + ctx->active_partition = next_active; + return WH_ERROR_OK; +} + +static int nfl_PartitionNewEpochOrFallback(whNvmFlashLogContext* ctx) +{ + int ret; + + if (ctx == NULL) + return WH_ERROR_BADARGS; + ret = nfl_PartitionNewEpoch(ctx); + + if (ret != WH_ERROR_OK) { + /* swtiching to new partition failed for a reason, try to restore + * back active partition. */ + nfl_PartitionRead(ctx); + } + + /* erase in-active partition, this ensure objects are truly destroyed */ + nfl_PartitionErase(ctx, ctx->active_partition == 0 ? 1 : 0); + + return ret; +} + +/* Initialization function */ +int wh_NvmFlashLog_Init(void* c, const void* cf) +{ + whNvmFlashLogContext* context = (whNvmFlashLogContext*)c; + const whNvmFlashLogConfig* config = (const whNvmFlashLogConfig*)cf; + int ret; + + if (context == NULL || config == NULL || config->flash_cb == NULL || + config->flash_ctx == NULL) { + return WH_ERROR_BADARGS; + } + if (config->flash_cb->PartitionSize == NULL) { + return WH_ERROR_BADARGS; + } + memset(context, 0, sizeof(*context)); + + ret = 0; + if (config->flash_cb->Init != NULL) + ret = config->flash_cb->Init(config->flash_ctx, config->flash_cfg); + if (ret != 0) + return ret; + + context->flash_cb = config->flash_cb; + context->flash_ctx = config->flash_ctx; + context->partition_size = + context->flash_cb->PartitionSize(context->flash_ctx); + + if (context->partition_size != WH_NVM_FLASH_LOG_PARTITION_SIZE || + context->partition_size % WH_NVM_FLASH_LOG_WRITE_GRANULARITY != 0) { + return WH_ERROR_BADARGS; + } + + /* unlock partitions */ + if (context->flash_cb->WriteUnlock != NULL) { + ret = context->flash_cb->WriteUnlock(context->flash_ctx, 0, + context->partition_size); + if (ret != 0) + return ret; + ret = context->flash_cb->WriteUnlock(context->flash_ctx, + context->partition_size, + context->partition_size); + if (ret != 0) + return ret; + } + + ret = nfl_PartitionChoose(context); + if (ret != 0) + return ret; + ret = nfl_PartitionRead(context); + if (ret != 0) + return ret; + ret = nfl_PartitionErase(context, (context->active_partition == 0) ? 1 : 0); + if (ret != 0) + return ret; + + context->is_initialized = 1; + return WH_ERROR_OK; +} + +int wh_NvmFlashLog_Cleanup(void* c) +{ + whNvmFlashLogContext* context = (whNvmFlashLogContext*)c; + int ret0, ret1; + + if (context == NULL || !context->is_initialized) + return WH_ERROR_BADARGS; + + context->is_initialized = 0; + + /* lock partitions */ + if (context->flash_cb->WriteLock == NULL) + return WH_ERROR_OK; + + ret0 = context->flash_cb->WriteLock(context->flash_ctx, 0, + context->partition_size); + ret1 = context->flash_cb->WriteLock( + context->flash_ctx, context->partition_size, context->partition_size); + + if (ret0 != WH_ERROR_OK) + return ret0; + if (ret1 != WH_ERROR_OK) + return ret1; + + return WH_ERROR_OK; +} + +/* List objects */ +int wh_NvmFlashLog_List(void* c, whNvmAccess access, whNvmFlags flags, + whNvmId start_id, whNvmId* out_count, whNvmId* out_id) +{ + whNvmFlashLogContext* ctx = (whNvmFlashLogContext*)c; + whNvmFlashLogMetadata *next_obj = NULL, *start_obj = NULL; + uint32_t count = 0; + + /* TODO: Implement access and flag matching */ + (void)access; + (void)flags; + + if (ctx == NULL || !ctx->is_initialized) + return WH_ERROR_BADARGS; + + /* list all obects if start_id is WH_NVM_ID_INVALID */ + if (start_id == WH_NVM_ID_INVALID) { + next_obj = (whNvmFlashLogMetadata*)ctx->directory.data; + } + else { + start_obj = nfl_ObjectFindById(ctx, start_id); + if (start_obj != NULL && start_obj->meta.id != WH_NVM_ID_INVALID) + next_obj = nfl_ObjNext(ctx, start_obj); + } + + if (next_obj == NULL || next_obj->meta.id == WH_NVM_ID_INVALID) { + if (out_count != NULL) + *out_count = 0; + if (out_id != NULL) + *out_id = WH_NVM_ID_INVALID; + return WH_ERROR_OK; + } + + count = nfl_ObjectCount(ctx, next_obj); + if (out_count != NULL) + *out_count = count; + if (out_id != NULL) + *out_id = next_obj->meta.id; + + return WH_ERROR_OK; +} + +/* Get available space/objects */ +int wh_NvmFlashLog_GetAvailable(void* c, uint32_t* out_avail_size, + whNvmId* out_avail_objects, + uint32_t* out_reclaim_size, + whNvmId* out_reclaim_objects) +{ + whNvmFlashLogContext* ctx = (whNvmFlashLogContext*)c; + uint8_t count; + + if (ctx == NULL || !ctx->is_initialized) + return WH_ERROR_BADARGS; + if (out_avail_size != NULL) { + *out_avail_size = ctx->partition_size - + sizeof(whNvmFlashLogPartitionHeader) - + ctx->directory.header.size; + } + + if (out_avail_objects != NULL) { + count = nfl_ObjectCount(ctx, NULL); + *out_avail_objects = WOLFHSM_CFG_NVM_OBJECT_COUNT - count; + } + + /* No reclaim in this simple implementation */ + if (out_reclaim_size != NULL) { + *out_reclaim_size = 0; + } + if (out_reclaim_objects != NULL) { + *out_reclaim_objects = 0; + } + + return WH_ERROR_OK; +} + +/* Get metadata for an object */ +int wh_NvmFlashLog_GetMetadata(void* c, whNvmId id, whNvmMetadata* meta) +{ + whNvmFlashLogContext* ctx = (whNvmFlashLogContext*)c; + whNvmFlashLogMetadata* obj; + + if (ctx == NULL || !ctx->is_initialized) + return WH_ERROR_BADARGS; + + obj = nfl_ObjectFindById(ctx, id); + if (obj == NULL) { + return WH_ERROR_NOTFOUND; + } + + if (meta != NULL) + memcpy(meta, &obj->meta, sizeof(*meta)); + return WH_ERROR_OK; +} + +int wh_NvmFlashLog_AddObject(void* c, whNvmMetadata* meta, whNvmSize data_len, + const uint8_t* data) +{ + whNvmFlashLogContext* ctx = (whNvmFlashLogContext*)c; + whNvmFlashLogMetadata *obj, *old_obj; + uint32_t available_space; + int ret; + uint32_t count; + + if (ctx == NULL || !ctx->is_initialized || meta == NULL || + (data_len > 0 && data == NULL)) + return WH_ERROR_BADARGS; + + count = nfl_ObjectCount(ctx, NULL); + available_space = ctx->partition_size - + sizeof(whNvmFlashLogPartitionHeader) - + ctx->directory.header.size; + + old_obj = nfl_ObjectFindById(ctx, meta->id); + if (old_obj != NULL) { + available_space += + sizeof(whNvmFlashLogMetadata) + PAD_SIZE(old_obj->meta.len); + count -= 1; + } + + if (PAD_SIZE(data_len) + sizeof(whNvmFlashLogMetadata) > available_space) + return WH_ERROR_NOSPACE; + + if (count >= WOLFHSM_CFG_NVM_OBJECT_COUNT) + return WH_ERROR_NOSPACE; + + if (old_obj) { + ret = nfl_ObjectDestroy(ctx, meta->id); + if (ret != WH_ERROR_OK) + return ret; + } + + obj = (whNvmFlashLogMetadata*)(ctx->directory.data + + ctx->directory.header.size); + meta->len = data_len; + memcpy(&obj->meta, meta, sizeof(*meta)); + memcpy((uint8_t*)obj + sizeof(whNvmFlashLogMetadata), data, data_len); + ctx->directory.header.size += + sizeof(whNvmFlashLogMetadata) + PAD_SIZE(data_len); + + return nfl_PartitionNewEpochOrFallback(ctx); +} + +/* Destroy objects by id list */ +int wh_NvmFlashLog_DestroyObjects(void* c, whNvmId list_count, + const whNvmId* id_list) +{ + whNvmFlashLogContext* ctx = (whNvmFlashLogContext*)c; + int i; + int ret; + + if (ctx == NULL || !ctx->is_initialized || + (list_count > 0 && id_list == NULL)) + return WH_ERROR_BADARGS; + + if (list_count == 0) + return WH_ERROR_OK; + + for (i = 0; i < list_count; i++) { + ret = nfl_ObjectDestroy(ctx, id_list[i]); + if (ret != WH_ERROR_OK) + return ret; + } + + return nfl_PartitionNewEpochOrFallback(ctx); +} + +/* Read object data */ +int wh_NvmFlashLog_Read(void* c, whNvmId id, whNvmSize offset, + whNvmSize data_len, uint8_t* data) +{ + whNvmFlashLogContext* ctx = (whNvmFlashLogContext*)c; + whNvmFlashLogMetadata* obj; + uint8_t* obj_data; + + if (ctx == NULL || !ctx->is_initialized || (data_len > 0 && data == NULL)) + return WH_ERROR_BADARGS; + + obj = nfl_ObjectFindById(ctx, id); + if (obj == NULL) + return WH_ERROR_NOTFOUND; + + if (offset + data_len > obj->meta.len) + return WH_ERROR_BADARGS; + + obj_data = (uint8_t*)obj + sizeof(whNvmFlashLogMetadata) + offset; + memcpy(data, obj_data, data_len); + + return WH_ERROR_OK; +} + +#endif /* WOLFHSM_CFG_SERVER_NVM_FLASH_LOG */ diff --git a/test/Makefile b/test/Makefile index 051212dcb..ad6393e34 100644 --- a/test/Makefile +++ b/test/Makefile @@ -123,6 +123,7 @@ else DEF += -DWOLFHSM_CFG_IS_TEST_SERVER endif + ## Source files # Assembly source files SRC_ASM += diff --git a/test/config/wolfhsm_cfg.h b/test/config/wolfhsm_cfg.h index cf9068221..4a0762140 100644 --- a/test/config/wolfhsm_cfg.h +++ b/test/config/wolfhsm_cfg.h @@ -56,4 +56,6 @@ #define WOLFHSM_CFG_CANCEL_API #endif +#define WOLFHSM_CFG_SERVER_NVM_FLASH_LOG + #endif /* WOLFHSM_CFG_H_ */ diff --git a/test/wh_test.c b/test/wh_test.c index 6c33f5bda..31124a6e4 100644 --- a/test/wh_test.c +++ b/test/wh_test.c @@ -66,7 +66,12 @@ int whTest_Unit(void) WH_TEST_ASSERT(0 == whTest_Flash_RamSim()); WH_TEST_ASSERT(0 == whTest_NvmFlash()); #if defined(WOLFHSM_CFG_CERTIFICATE_MANAGER) && !defined(WOLFHSM_CFG_NO_CRYPTO) - WH_TEST_ASSERT(0 == whTest_CertRamSim()); + WH_TEST_ASSERT(0 == whTest_CertRamSim(WH_NVM_TEST_BACKEND_FLASH)); + +#if defined(WOLFHSM_CFG_SERVER_NVM_FLASH_LOG) + WH_TEST_ASSERT(0 == whTest_CertRamSim(WH_NVM_TEST_BACKEND_FLASH_LOG)); +#endif /* WOLFHSM_CFG_SERVER_NVM_FLASH_LOG */ + #endif /* WOLFHSM_CFG_CERTIFICATE_MANAGER && !WOLFHSM_CFG_NO_CRYPTO */ @@ -80,7 +85,12 @@ int whTest_Unit(void) #if defined(WOLFHSM_CFG_SERVER_IMG_MGR) && !defined(WOLFHSM_CFG_NO_CRYPTO) /* Image Manager Tests */ - WH_TEST_ASSERT(0 == whTest_ServerImgMgr()); + WH_TEST_ASSERT(0 == whTest_ServerImgMgr(WH_NVM_TEST_BACKEND_FLASH)); + +#if defined(WOLFHSM_CFG_SERVER_NVM_FLASH_LOG) + WH_TEST_ASSERT(0 == whTest_ServerImgMgr(WH_NVM_TEST_BACKEND_FLASH_LOG)); +#endif + #endif /* WOLFHSM_CFG_SERVER_IMG_MGR && !WOLFHSM_CFG_NO_CRYPTO */ #if defined(WOLFHSM_CFG_SHE_EXTENSION) diff --git a/test/wh_test_cert.c b/test/wh_test_cert.c index 8c2e0d4fc..b48cda9f9 100644 --- a/test/wh_test_cert.c +++ b/test/wh_test_cert.c @@ -578,7 +578,7 @@ static int whTest_CertNonExportable(whClientContext* client) #endif /* WOLFHSM_CFG_ENABLE_CLIENT */ #ifdef WOLFHSM_CFG_ENABLE_SERVER -int whTest_CertRamSim(void) +int whTest_CertRamSim(whTestNvmBackendType nvmType) { int rc = WH_ERROR_OK; const uint32_t BUFFER_SIZE = 1024; @@ -614,21 +614,13 @@ int whTest_CertRamSim(void) }}; const whFlashCb fcb[1] = {WH_FLASH_RAMSIM_CB}; - /* NVM Flash Configuration using RamSim HAL Flash */ - whNvmFlashConfig nf_conf[1] = {{ - .cb = fcb, - .context = fc, - .config = fc_conf, - }}; - whNvmFlashContext nfc[1] = {0}; - whNvmCb nfcb[1] = {WH_NVM_FLASH_CB}; - - whNvmConfig n_conf[1] = {{ - .cb = nfcb, - .context = nfc, - .config = nf_conf, - }}; + whTestNvmBackendUnion nvm_setup; + whNvmConfig n_conf[1]; whNvmContext nvm[1] = {{0}}; + + WH_TEST_RETURN_ON_FAIL( + whTest_NvmCfgBackend(nvmType, &nvm_setup, n_conf, fc_conf, fc, fcb)); + #ifndef WOLFHSM_CFG_NO_CRYPTO whServerCryptoContext crypto[1] = {{ .devId = INVALID_DEVID, diff --git a/test/wh_test_cert.h b/test/wh_test_cert.h index 5deecaa69..d5d002eb6 100644 --- a/test/wh_test_cert.h +++ b/test/wh_test_cert.h @@ -26,8 +26,10 @@ #include "wolfhsm/wh_server.h" #include "wolfhsm/wh_client.h" +#include "wh_test_common.h" + /* Run certificate configuration tests */ -int whTest_CertRamSim(void); +int whTest_CertRamSim(whTestNvmBackendType nvmType); int whTest_CertServerCfg(whServerConfig* serverCfg); int whTest_CertClient(whClientContext* client); #if defined(WOLFHSM_CFG_DMA) @@ -41,4 +43,4 @@ int whTest_CertClientAcertDma_ClientServerTestInternal(whClientContext* client); #endif /* WOLFHSM_CFG_DMA */ #endif /* WOLFHSM_CFG_CERTIFICATE_MANAGER_ACERT */ -#endif /* !WOLFHSM_WH_TEST_CERT_H_ */ \ No newline at end of file +#endif /* !WOLFHSM_WH_TEST_CERT_H_ */ diff --git a/test/wh_test_clientserver.c b/test/wh_test_clientserver.c index 808598431..c91d67581 100644 --- a/test/wh_test_clientserver.c +++ b/test/wh_test_clientserver.c @@ -87,7 +87,6 @@ typedef struct { static int _testNonExportableNvmAccess(whClientContext* client); #endif - #ifdef WOLFHSM_CFG_ENABLE_SERVER /* Pointer to a local server context so a connect callback can access it. Should * be set before calling wh_ClientInit() */ @@ -727,7 +726,7 @@ static int _testOutOfBoundsNvmReads(whClientContext* client, return WH_ERROR_OK; } -int whTest_ClientServerSequential(void) +int whTest_ClientServerSequential(whTestNvmBackendType nvmType) { int ret = 0; @@ -780,21 +779,13 @@ int whTest_ClientServerSequential(void) }}; const whFlashCb fcb[1] = {WH_FLASH_RAMSIM_CB}; - /* NVM Flash Configuration using RamSim HAL Flash */ - whNvmFlashConfig nf_conf[1] = {{ - .cb = fcb, - .context = fc, - .config = fc_conf, - }}; - whNvmFlashContext nfc[1] = {0}; - whNvmCb nfcb[1] = {WH_NVM_FLASH_CB}; - - whNvmConfig n_conf[1] = {{ - .cb = nfcb, - .context = nfc, - .config = nf_conf, - }}; + whTestNvmBackendUnion nvm_setup; + whNvmConfig n_conf[1]; whNvmContext nvm[1] = {{0}}; + + WH_TEST_RETURN_ON_FAIL( + whTest_NvmCfgBackend(nvmType, &nvm_setup, n_conf, fc_conf, fc, fcb)); + #ifndef WOLFHSM_CFG_NO_CRYPTO whServerCryptoContext crypto[1] = {{ .devId = INVALID_DEVID, @@ -1766,7 +1757,7 @@ static void _whClientServerThreadTest(whClientConfig* c_conf, } } -static int wh_ClientServer_MemThreadTest(void) +static int wh_ClientServer_MemThreadTest(whTestNvmBackendType nvmType) { uint8_t req[BUFFER_SIZE] = {0}; uint8_t resp[BUFFER_SIZE] = {0}; @@ -1811,21 +1802,12 @@ static int wh_ClientServer_MemThreadTest(void) }}; const whFlashCb fcb[1] = {WH_FLASH_RAMSIM_CB}; - /* NVM Flash Configuration using RamSim HAL Flash */ - whNvmFlashConfig nf_conf[1] = {{ - .cb = fcb, - .context = fc, - .config = fc_conf, - }}; - whNvmFlashContext nfc[1] = {0}; - whNvmCb nfcb[1] = {WH_NVM_FLASH_CB}; + whTestNvmBackendUnion nvm_setup; + whNvmConfig n_conf[1] = {0}; + whNvmContext nvm[1] = {{0}}; - whNvmConfig n_conf[1] = {{ - .cb = nfcb, - .context = nfc, - .config = nf_conf, - }}; - whNvmContext nvm[1] = {{0}}; + WH_TEST_RETURN_ON_FAIL( + whTest_NvmCfgBackend(nvmType, &nvm_setup, n_conf, fc_conf, fc, fcb)); #ifndef WOLFHSM_CFG_NO_CRYPTO /* Crypto context */ @@ -1863,7 +1845,7 @@ static int wh_ClientServer_MemThreadTest(void) } -static int wh_ClientServer_PosixMemMapThreadTest(void) +static int wh_ClientServer_PosixMemMapThreadTest(whTestNvmBackendType nvmType) { posixTransportShmConfig tmcf[1] = {{ .name = "/wh_test_clientserver_shm", @@ -1905,22 +1887,13 @@ static int wh_ClientServer_PosixMemMapThreadTest(void) }}; const whFlashCb fcb[1] = {WH_FLASH_RAMSIM_CB}; - /* NVM Flash Configuration using RamSim HAL Flash */ - whNvmFlashConfig nf_conf[1] = {{ - .cb = fcb, - .context = fc, - .config = fc_conf, - }}; - whNvmFlashContext nfc[1] = {0}; - whNvmCb nfcb[1] = {WH_NVM_FLASH_CB}; - - whNvmConfig n_conf[1] = {{ - .cb = nfcb, - .context = nfc, - .config = nf_conf, - }}; + whTestNvmBackendUnion nvm_setup; + whNvmConfig n_conf[1]; whNvmContext nvm[1] = {{0}}; + WH_TEST_RETURN_ON_FAIL( + whTest_NvmCfgBackend(nvmType, &nvm_setup, n_conf, fc_conf, fc, fcb)); + #ifndef WOLFHSM_CFG_NO_CRYPTO /* Crypto context */ whServerCryptoContext crypto[1] = {{ @@ -1960,14 +1933,33 @@ static int wh_ClientServer_PosixMemMapThreadTest(void) int whTest_ClientServer(void) { printf("Testing client/server sequential: mem...\n"); - WH_TEST_ASSERT(0 == whTest_ClientServerSequential()); + WH_TEST_ASSERT(0 == whTest_ClientServerSequential(WH_NVM_TEST_BACKEND_FLASH)); + +#if defined(WOLFHSM_CFG_SERVER_NVM_FLASH_LOG) + printf("Testing client/server sequential: mem + flash log...\n"); + WH_TEST_ASSERT(0 == + whTest_ClientServerSequential(WH_NVM_TEST_BACKEND_FLASH_LOG)); +#endif /* defined(WOLFHSM_CFG_SERVER_NVM_FLASH_LOG) */ #if defined(WOLFHSM_CFG_TEST_POSIX) printf("Testing client/server: (pthread) mem...\n"); - WH_TEST_ASSERT(0 == wh_ClientServer_MemThreadTest()); + WH_TEST_ASSERT(0 == wh_ClientServer_MemThreadTest(WH_NVM_TEST_BACKEND_FLASH)); printf("Testing client/server: (pthread) POSIX shared memory ...\n"); - WH_TEST_ASSERT(0 == wh_ClientServer_PosixMemMapThreadTest()); + WH_TEST_ASSERT( + 0 == wh_ClientServer_PosixMemMapThreadTest(WH_NVM_TEST_BACKEND_FLASH)); + +#if defined(WOLFHSM_CFG_SERVER_NVM_FLASH_LOG) + printf("Testing client/server: (pthread) mem + flash log...\n"); + WH_TEST_ASSERT(0 == + wh_ClientServer_MemThreadTest(WH_NVM_TEST_BACKEND_FLASH_LOG)); + + printf("Testing client/server: (pthread) POSIX shared memory + flash " + "log...\n"); + WH_TEST_ASSERT( + 0 == wh_ClientServer_PosixMemMapThreadTest(WH_NVM_TEST_BACKEND_FLASH_LOG)); +#endif /* defined(WOLFHSM_CFG_SERVER_NVM_FLASH_LOG) */ + #endif /* defined(WOLFHSM_CFG_TEST_POSIX) */ return 0; diff --git a/test/wh_test_common.c b/test/wh_test_common.c new file mode 100644 index 000000000..2cac0aebb --- /dev/null +++ b/test/wh_test_common.c @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2025 wolfSSL Inc. + * + * This file is part of wolfHSM. + * + * wolfHSM is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfHSM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with wolfHSM. If not, see . + */ + +#include + +#include +#include +#include +#include +#include + +#include "wh_test_common.h" + + +/** + * Helper function to configure and select an NVM backend for testing. + * + * @param type The type of NVM backend to configure (see + * NvmTestBackendType). + * @param nvmSetup Pointer to a union of NVM backend setup structures (output). + * @param nvmCfg Pointer to the NVM configuration structure to populate + * (output). + * @param fCfg Pointer to the RamSim flash configuration structure. + * @param fCtx Pointer to the RamSim flash context structure. + * @param fCb Pointer to the RamSim flash callback structure. + * + * @return WH_ERROR_OK on success, or WH_ERROR_BADARGS on failure. + */ +int whTest_NvmCfgBackend(whTestNvmBackendType type, + whTestNvmBackendUnion* nvmSetup, whNvmConfig* nvmCfg, + whFlashRamsimCfg* fCfg, whFlashRamsimCtx* fCtx, + const whFlashCb* fCb) +{ + + WH_TEST_ASSERT(nvmSetup != NULL); + WH_TEST_ASSERT(nvmCfg != NULL); + WH_TEST_ASSERT(fCfg != NULL); + + switch (type) { +#if defined(WOLFHSM_CFG_SERVER_NVM_FLASH_LOG) + case WH_NVM_TEST_BACKEND_FLASH_LOG: + nvmSetup->nvmFlashLogCfg.flash_cb = fCb; + /* restrict simulated flash partition to nvm_flash_log_partition */ + WH_TEST_ASSERT(fCfg->size >= WH_NVM_FLASH_LOG_PARTITION_SIZE * 2); + fCfg->sectorSize = WH_NVM_FLASH_LOG_PARTITION_SIZE; + nvmSetup->nvmFlashLogCfg.flash_cfg = fCfg; + nvmSetup->nvmFlashLogCfg.flash_ctx = fCtx; + memset(&nvmSetup->nvmFlashLogCtx, 0, + sizeof(nvmSetup->nvmFlashLogCtx)); + static whNvmCb nflcb[1] = {WH_NVM_FLASH_LOG_CB}; + + nvmCfg->cb = nflcb; + nvmCfg->context = &nvmSetup->nvmFlashLogCtx; + nvmCfg->config = &nvmSetup->nvmFlashLogCfg; + break; +#endif + case WH_NVM_TEST_BACKEND_FLASH: + /* NVM Flash Configuration using RamSim HAL Flash */ + nvmSetup->nvmFlashCfg.cb = fCb; + nvmSetup->nvmFlashCfg.context = fCtx; + nvmSetup->nvmFlashCfg.config = fCfg; + + memset(&nvmSetup->nvmFlashCtx, 0, sizeof(nvmSetup->nvmFlashCtx)); + static whNvmCb nfcb[1] = {WH_NVM_FLASH_CB}; + + nvmCfg->cb = nfcb; + nvmCfg->context = &nvmSetup->nvmFlashCtx; + nvmCfg->config = &nvmSetup->nvmFlashCfg; + break; + + default: + return WH_ERROR_BADARGS; + } + + return 0; +} diff --git a/test/wh_test_common.h b/test/wh_test_common.h index a6ba3ff2e..c1bc5a08f 100644 --- a/test/wh_test_common.h +++ b/test/wh_test_common.h @@ -26,6 +26,10 @@ #include #endif +#include +#include +#include +#include #define WH_TEST_FAIL (-1) #define WH_TEST_SUCCESS (0) @@ -106,4 +110,32 @@ } while (0) +typedef enum { + WH_NVM_TEST_BACKEND_FLASH = 0, +#if defined(WOLFHSM_CFG_SERVER_NVM_FLASH_LOG) + WH_NVM_TEST_BACKEND_FLASH_LOG, +#endif + WH_NVM_TEST_BACKEND_COUNT +} whTestNvmBackendType; + +/* union helper struct to be able to test more than one NVM implementation */ +typedef struct { + union { + struct { + whNvmFlashContext nvmFlashCtx; + whNvmFlashConfig nvmFlashCfg; + }; +#if defined(WOLFHSM_CFG_SERVER_NVM_FLASH_LOG) + struct { + whNvmFlashLogContext nvmFlashLogCtx; + whNvmFlashLogConfig nvmFlashLogCfg; + }; +#endif /* WOLFHSM_CFG_SERVER_NVM_FLASH_LOG */ + }; +} whTestNvmBackendUnion; + +int whTest_NvmCfgBackend(whTestNvmBackendType type, + whTestNvmBackendUnion* nvmSetup, whNvmConfig* nvmCfg, + whFlashRamsimCfg* fCfg, whFlashRamsimCtx* fCtx, + const whFlashCb* fCb); #endif /* WH_TEST_COMMON_H_ */ diff --git a/test/wh_test_crypto.c b/test/wh_test_crypto.c index 7da2aaec9..f6556046e 100644 --- a/test/wh_test_crypto.c +++ b/test/wh_test_crypto.c @@ -3614,7 +3614,8 @@ static void _whClientServerThreadTest(whClientConfig* c_conf, } } -static int wh_ClientServer_MemThreadTest(void) + +static int wh_ClientServer_MemThreadTest(whTestNvmBackendType nvmType) { uint8_t req[BUFFER_SIZE] = {0}; uint8_t resp[BUFFER_SIZE] = {0}; @@ -3671,22 +3672,13 @@ static int wh_ClientServer_MemThreadTest(void) }}; const whFlashCb fcb[1] = {WH_FLASH_RAMSIM_CB}; - /* NVM Flash Configuration using RamSim HAL Flash */ - whNvmFlashConfig nf_conf[1] = {{ - .cb = fcb, - .context = fc, - .config = fc_conf, - }}; - whNvmFlashContext nfc[1] = {0}; - whNvmCb nfcb[1] = {WH_NVM_FLASH_CB}; - - whNvmConfig n_conf[1] = {{ - .cb = nfcb, - .context = nfc, - .config = nf_conf, - }}; + whTestNvmBackendUnion nvm_setup; + whNvmConfig n_conf[1]; whNvmContext nvm[1] = {{0}}; + WH_TEST_RETURN_ON_FAIL( + whTest_NvmCfgBackend(nvmType, &nvm_setup, n_conf, fc_conf, fc, fcb)); + /* Crypto context */ whServerCryptoContext crypto[1] = {{ .devId = INVALID_DEVID, @@ -3720,7 +3712,15 @@ static int wh_ClientServer_MemThreadTest(void) int whTest_Crypto(void) { printf("Testing crypto: (pthread) mem...\n"); - WH_TEST_RETURN_ON_FAIL(wh_ClientServer_MemThreadTest()); + WH_TEST_RETURN_ON_FAIL( + wh_ClientServer_MemThreadTest(WH_NVM_TEST_BACKEND_FLASH)); + +#if defined(WOLFHSM_CFG_SERVER_NVM_FLASH_LOG) + printf("Testing crypto: (pthread) mem (flash log)...\n"); + WH_TEST_RETURN_ON_FAIL( + wh_ClientServer_MemThreadTest(WH_NVM_TEST_BACKEND_FLASH_LOG)); +#endif + return 0; } #endif /* WOLFHSM_CFG_TEST_POSIX && WOLFHSM_CFG_ENABLE_CLIENT && \ diff --git a/test/wh_test_nvm_flash.c b/test/wh_test_nvm_flash.c index f6bfd69e6..7ddc41845 100644 --- a/test/wh_test_nvm_flash.c +++ b/test/wh_test_nvm_flash.c @@ -31,6 +31,7 @@ #include "wolfhsm/wh_error.h" #include "wolfhsm/wh_nvm.h" #include "wolfhsm/wh_nvm_flash.h" +#include "wolfhsm/wh_nvm_flash_log.h" #include "wolfhsm/wh_flash_unit.h" /* NVM simulator backends to use for testing NVM module */ @@ -155,8 +156,7 @@ static void _ShowList(const whNvmCb* cb, void* context) #endif -static int addObjectWithReadBackCheck(const whNvmCb* cb, - whNvmFlashContext* context, +static int addObjectWithReadBackCheck(const whNvmCb* cb, void* context, whNvmMetadata* meta, whNvmSize data_len, const uint8_t* data) @@ -316,10 +316,8 @@ int whTest_Flash(const whFlashCb* fcb, void* fctx, const void* cfg) return 0; } -int whTest_NvmFlashCfg(whNvmFlashConfig* cfg) +int whTest_NvmFlashCfg(void* cfg, void* context, const whNvmCb* cb) { - const whNvmCb cb[1] = {WH_NVM_FLASH_CB}; - whNvmFlashContext context[1] = {0}; int ret = 0; WH_TEST_RETURN_ON_FAIL(cb->Init(context, cfg)); @@ -426,7 +424,8 @@ int whTest_NvmFlashCfg(whNvmFlashConfig* cfg) goto cleanup; } - if ((ret = cb->Read(context, ids[i], 0, metaBuf.len, dataBuf)) != 0) { + if ((ret = cb->Read(context, ids[i], 0, metaBuf.len, dataBuf)) != + 0) { WH_ERROR_PRINT("Read after reclaim returned %d\n", ret); goto cleanup; } @@ -495,9 +494,24 @@ int whTest_NvmFlash_RamSim(void) .context = myHalFlashCtx, .config = myHalFlashCfg, }; + whNvmFlashContext nvmFlashCtx[1] = {0}; + const whNvmCb nvmFlashCb[1] = {WH_NVM_FLASH_CB}; + WH_TEST_RETURN_ON_FAIL( + whTest_NvmFlashCfg(&myNvmCfg, nvmFlashCtx, nvmFlashCb)); - return whTest_NvmFlashCfg(&myNvmCfg); +#if defined(WOLFHSM_CFG_SERVER_NVM_FLASH_LOG) + whNvmFlashLogConfig myLogCfg = { + .flash_cb = myCb, + .flash_ctx = myHalFlashCtx, + .flash_cfg = myHalFlashCfg, + }; + whNvmFlashLogContext nvmLogCtx[1] = {0}; + const whNvmCb nvmLogCb[1] = {WH_NVM_FLASH_LOG_CB}; + WH_TEST_RETURN_ON_FAIL(whTest_NvmFlashCfg(&myLogCfg, nvmLogCtx, nvmLogCb)); +#endif /* WOLFHSM_CFG_SERVER_NVM_FLASH_LOG */ + + return 0; } static int @@ -629,8 +643,31 @@ int whTest_NvmFlash_PosixFileSim(void) .context = myHalFlashContext, .config = myHalFlashConfig, }; + whNvmFlashContext nvmFlashCtx[1] = {0}; + const whNvmCb nvmFlashCb[1] = {WH_NVM_FLASH_CB}; + + WH_TEST_ASSERT(0 == whTest_NvmFlashCfg(&myNvmCfg, nvmFlashCtx, nvmFlashCb)); + + + unlink(myHalFlashConfig[0].filename); + +#if defined(WOLFHSM_CFG_SERVER_NVM_FLASH_LOG) + WH_TEST_ASSERT(myHalFlashConfig[0].partition_size >= + WH_NVM_FLASH_LOG_PARTITION_SIZE); + myHalFlashConfig[0].partition_size = WH_NVM_FLASH_LOG_PARTITION_SIZE; + + memset(myHalFlashContext, 0, sizeof(myHalFlashContext)); + + whNvmFlashLogConfig myLogCfg = { + .flash_cb = myCb, + .flash_ctx = myHalFlashContext, + .flash_cfg = myHalFlashConfig, + }; + whNvmFlashLogContext nvmLogCtx[1] = {0}; + const whNvmCb nvmLogCb[1] = {WH_NVM_FLASH_LOG_CB}; - WH_TEST_ASSERT(0 == whTest_NvmFlashCfg(&myNvmCfg)); + WH_TEST_RETURN_ON_FAIL(whTest_NvmFlashCfg(&myLogCfg, nvmLogCtx, nvmLogCb)); +#endif /* WOLFHSM_CFG_SERVER_NVM_FLASH_LOG */ /* Remove the configured file on success*/ unlink(myHalFlashConfig[0].filename); diff --git a/test/wh_test_server_img_mgr.c b/test/wh_test_server_img_mgr.c index 45b50d4b8..ec2cfd7ca 100644 --- a/test/wh_test_server_img_mgr.c +++ b/test/wh_test_server_img_mgr.c @@ -1192,7 +1192,7 @@ static int whTest_ServerImgMgrServerCfgRsa2048(whServerConfig* serverCfg) } #endif /* !NO_RSA */ -int whTest_ServerImgMgr(void) +int whTest_ServerImgMgr(whTestNvmBackendType nvmType) { int rc = 0; const uint32_t BUFFER_SIZE = 1024; @@ -1229,22 +1229,13 @@ int whTest_ServerImgMgr(void) }}; const whFlashCb fcb[1] = {WH_FLASH_RAMSIM_CB}; - /* NVM Flash Configuration using RamSim HAL Flash */ - whNvmFlashConfig nf_conf[1] = {{ - .cb = fcb, - .context = fc, - .config = fc_conf, - }}; - whNvmFlashContext nfc[1] = {0}; - whNvmCb nfcb[1] = {WH_NVM_FLASH_CB}; - - whNvmConfig n_conf[1] = {{ - .cb = nfcb, - .context = nfc, - .config = nf_conf, - }}; + whTestNvmBackendUnion nvm_setup; + whNvmConfig n_conf[1]; whNvmContext nvm[1] = {{0}}; + WH_TEST_RETURN_ON_FAIL( + whTest_NvmCfgBackend(nvmType, &nvm_setup, n_conf, fc_conf, fc, fcb)); + whServerCryptoContext crypto[1] = {{ .devId = INVALID_DEVID, }}; diff --git a/test/wh_test_server_img_mgr.h b/test/wh_test_server_img_mgr.h index 576422ea3..1ba8d41eb 100644 --- a/test/wh_test_server_img_mgr.h +++ b/test/wh_test_server_img_mgr.h @@ -20,6 +20,8 @@ #ifndef WH_TEST_SERVER_IMG_MGR_H #define WH_TEST_SERVER_IMG_MGR_H -int whTest_ServerImgMgr(void); +#include "wh_test_common.h" -#endif /* WH_TEST_SERVER_IMG_MGR_H */ \ No newline at end of file +int whTest_ServerImgMgr(whTestNvmBackendType nvmType); + +#endif /* WH_TEST_SERVER_IMG_MGR_H */ diff --git a/wolfhsm/wh_nvm.h b/wolfhsm/wh_nvm.h index f52e3e99b..a0c8cefd1 100644 --- a/wolfhsm/wh_nvm.h +++ b/wolfhsm/wh_nvm.h @@ -82,8 +82,8 @@ typedef struct { const whNvmId* id_list); /* Read the data of the object starting at the byte offset */ - int (*Read)(void* context, whNvmId id, whNvmSize offset, - whNvmSize data_len, uint8_t* data); + int (*Read)(void* context, whNvmId id, whNvmSize offset, whNvmSize data_len, + uint8_t* data); } whNvmCb; @@ -126,6 +126,6 @@ int wh_Nvm_DestroyObjects(whNvmContext* context, whNvmId list_count, const whNvmId* id_list); int wh_Nvm_Read(whNvmContext* context, whNvmId id, whNvmSize offset, - whNvmSize data_len, uint8_t* data); + whNvmSize data_len, uint8_t* data); #endif /* !WOLFHSM_WH_NVM_H_ */ diff --git a/wolfhsm/wh_nvm_flash.h b/wolfhsm/wh_nvm_flash.h index 622f5c484..b928a59c7 100644 --- a/wolfhsm/wh_nvm_flash.h +++ b/wolfhsm/wh_nvm_flash.h @@ -100,8 +100,8 @@ int wh_NvmFlash_AddObject(void* c, whNvmMetadata* meta, whNvmSize data_len, const uint8_t* data); int wh_NvmFlash_DestroyObjects(void* c, whNvmId list_count, const whNvmId* id_list); -int wh_NvmFlash_Read(void* c, whNvmId id, whNvmSize offset, - whNvmSize data_len, uint8_t* data); +int wh_NvmFlash_Read(void* c, whNvmId id, whNvmSize offset, whNvmSize data_len, + uint8_t* data); #define WH_NVM_FLASH_CB \ { \ diff --git a/wolfhsm/wh_nvm_flash_log.h b/wolfhsm/wh_nvm_flash_log.h new file mode 100644 index 000000000..42d879623 --- /dev/null +++ b/wolfhsm/wh_nvm_flash_log.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2025 wolfSSL Inc. + * + * This file is part of wolfHSM. + * + * wolfHSM is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfHSM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with wolfHSM. If not, see . + */ +#ifndef WOLFHSM_WH_NVM_FLASH_LOG_H_ +#define WOLFHSM_WH_NVM_FLASH_LOG_H_ + +#if defined(WOLFHSM_CFG_SERVER_NVM_FLASH_LOG) + +#include "wolfhsm/wh_settings.h" + +#include "wolfhsm/wh_common.h" +#include "wolfhsm/wh_flash.h" + +#ifndef WH_NVM_FLASH_LOG_WRITE_GRANULARITY +#define WH_NVM_FLASH_LOG_WRITE_GRANULARITY 64 +#endif + +#ifndef WOLFHSM_CFG_NVM_OBJECT_COUNT +#define WOLFHSM_CFG_NVM_OBJECT_COUNT 32 +#endif + +#ifndef WH_NVM_FLASH_LOG_PARTITION_SIZE +#define WH_NVM_FLASH_LOG_PARTITION_SIZE (4 * 1024) +#endif + +typedef struct { + uint32_t partition_epoch; + uint32_t size; + uint8_t WH_PAD[WH_NVM_FLASH_LOG_WRITE_GRANULARITY - sizeof(uint32_t) * 2]; +} whNvmFlashLogPartitionHeader; + +/* In-memory representation of a partition */ +typedef struct { + whNvmFlashLogPartitionHeader header; + uint8_t data[WH_NVM_FLASH_LOG_PARTITION_SIZE]; +} whNvmFlashLogMemPartition; + +/* Flash log backend context structure */ +typedef struct { + const whFlashCb* flash_cb; /* Flash callback interface */ + void* flash_ctx; /* Flash context */ + uint32_t partition_size; + uint32_t active_partition; /* 0 or 1 */ + int is_initialized; + whNvmFlashLogMemPartition directory; +} whNvmFlashLogContext; + +/* Flash log backend config structure */ +typedef struct { + const whFlashCb* flash_cb; /* Flash callback interface */ + void* flash_ctx; /* Flash context */ + const void* flash_cfg; /* Config to be passed to cb->Init */ +} whNvmFlashLogConfig; + +int wh_NvmFlashLog_Init(void* c, const void* cf); +int wh_NvmFlashLog_Cleanup(void* c); +int wh_NvmFlashLog_List(void* c, whNvmAccess access, whNvmFlags flags, + whNvmId start_id, whNvmId* out_avail_objects, + whNvmId* out_id); +int wh_NvmFlashLog_GetAvailable(void* c, uint32_t* out_avail_size, + whNvmId* out_avail_objects, + uint32_t* out_reclaim_size, + whNvmId* out_reclaim_objects); +int wh_NvmFlashLog_GetMetadata(void* c, whNvmId id, whNvmMetadata* meta); +int wh_NvmFlashLog_AddObject(void* c, whNvmMetadata* meta, whNvmSize data_len, + const uint8_t* data); +int wh_NvmFlashLog_DestroyObjects(void* c, whNvmId list_count, + const whNvmId* id_list); +int wh_NvmFlashLog_Read(void* c, whNvmId id, whNvmSize offset, + whNvmSize data_len, uint8_t* data); + +#define WH_NVM_FLASH_LOG_CB \ + { \ + .Init = wh_NvmFlashLog_Init, .Cleanup = wh_NvmFlashLog_Cleanup, \ + .List = wh_NvmFlashLog_List, \ + .GetAvailable = wh_NvmFlashLog_GetAvailable, \ + .GetMetadata = wh_NvmFlashLog_GetMetadata, \ + .AddObject = wh_NvmFlashLog_AddObject, \ + .DestroyObjects = wh_NvmFlashLog_DestroyObjects, \ + .Read = wh_NvmFlashLog_Read, \ + } + +#endif /* WOLFHSM_CFG_SERVER_NVM_FLASH_LOG */ + +#endif /* WOLFHSM_WH_NVM_FLASH_LOG_H_ */