From a2d48bb140dbe68ce6efcb11717455750d43bedc Mon Sep 17 00:00:00 2001 From: Tomasz Chyrowicz Date: Fri, 3 Jan 2025 14:30:36 +0100 Subject: [PATCH 1/4] suit: Handle IPUC regions in memory_layout Return IPUC pseudo flash driver if a supported range is requested. Ref: NCSDK-30809 Signed-off-by: Tomasz Chyrowicz --- .../memory_layout/src/suit_memory_layout.c | 42 +++++++++++++++++-- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/subsys/suit/memory_layout/src/suit_memory_layout.c b/subsys/suit/memory_layout/src/suit_memory_layout.c index 2ce9a4358c34..cfef763be9af 100644 --- a/subsys/suit/memory_layout/src/suit_memory_layout.c +++ b/subsys/suit/memory_layout/src/suit_memory_layout.c @@ -7,6 +7,9 @@ #include #include #include +#ifdef CONFIG_FLASH_IPUC +#include +#endif /* CONFIG_FLASH_IPUC */ /* Definitions for SOC internal nonvolatile memory */ #if (DT_NODE_EXISTS(DT_NODELABEL(mram1x))) /* nrf54H20 */ @@ -170,18 +173,33 @@ static const struct nvm_area *find_area_by_device(const struct device *fdev) bool suit_memory_global_address_to_nvm_address(uintptr_t address, struct nvm_address *result) { - const struct nvm_area *area = find_area(address); + struct nvm_area *area = (struct nvm_area *)find_area(address); if (result == NULL) { return false; } - if (area == NULL) { + result->fdev = NULL; + if (area != NULL) { + result->fdev = area->na_fdev; + result->offset = address - area_address_get(area); + } +#ifdef CONFIG_FLASH_IPUC + struct nvm_area ipuc_area; + + ipuc_area.na_fdev = flash_ipuc_find(address, 0, &ipuc_area.na_start, &ipuc_area.na_size); + if (ipuc_area.na_fdev != NULL) { + /* Use IPUC instead of internal NVM driver to gain write access. */ + result->fdev = ipuc_area.na_fdev; + /* IPUCs use relative addressing */ + result->offset = address - ipuc_area.na_start; + } +#endif /* CONFIG_FLASH_IPUC */ + + if (result->fdev == NULL) { return false; } - result->fdev = area->na_fdev; - result->offset = address - area_address_get(area); return true; } @@ -204,7 +222,15 @@ bool suit_memory_nvm_address_to_global_address(struct nvm_address *address, uint bool suit_memory_global_address_is_in_nvm(uintptr_t address) { +#ifdef CONFIG_FLASH_IPUC + struct nvm_area ipuc_area; + + ipuc_area.na_fdev = flash_ipuc_find(address, 0, &ipuc_area.na_start, &ipuc_area.na_size); + + return (ipuc_area.na_fdev != NULL) && (find_area(address) != NULL); +#else /* CONFIG_FLASH_IPUC */ return find_area(address) != NULL; +#endif /* CONFIG_FLASH_IPUC */ } bool suit_memory_global_address_range_is_in_nvm(uintptr_t address, size_t size) @@ -215,7 +241,15 @@ bool suit_memory_global_address_range_is_in_nvm(uintptr_t address, size_t size) const struct nvm_area *start = find_area(address); const struct nvm_area *end = find_area(address + size - 1); +#ifdef CONFIG_FLASH_IPUC + struct nvm_area ipuc_area; + + ipuc_area.na_fdev = flash_ipuc_find(address, size, &ipuc_area.na_start, &ipuc_area.na_size); + + return (start != NULL && start == end) || (ipuc_area.na_fdev != NULL); +#else /* CONFIG_FLASH_IPUC */ return start != NULL && start == end; +#endif /* CONFIG_FLASH_IPUC */ } bool suit_memory_global_address_is_in_ram(uintptr_t address) From 8bea5ea2db287a894655804515f9e1e76af23896 Mon Sep 17 00:00:00 2001 From: Tomasz Chyrowicz Date: Fri, 3 Jan 2025 16:52:46 +0100 Subject: [PATCH 2/4] suit: Add support for IPUC in fetching Add support for IPUC regions in platform fetchers. Ref: NCSDK-30809 Signed-off-by: Tomasz Chyrowicz --- .../app/src/suit_plat_fetch_app_specific.c | 146 ++++++++++++++++++ .../stream/stream_sinks/src/suit_flash_sink.c | 8 + 2 files changed, 154 insertions(+) diff --git a/subsys/suit/platform/app/src/suit_plat_fetch_app_specific.c b/subsys/suit/platform/app/src/suit_plat_fetch_app_specific.c index f26f8e9a7ff9..f231c7c57797 100644 --- a/subsys/suit/platform/app/src/suit_plat_fetch_app_specific.c +++ b/subsys/suit/platform/app/src/suit_plat_fetch_app_specific.c @@ -13,6 +13,14 @@ #include #include +#ifdef CONFIG_SUIT_STREAM_SINK_FLASH +#include +#endif /* CONFIG_SUIT_STREAM_SINK_FLASH */ + +#ifdef CONFIG_FLASH_IPUC +#include +#endif /* CONFIG_FLASH_IPUC */ + #ifdef CONFIG_SUIT_STREAM_SINK_CACHE #include #endif /* CONFIG_SUIT_STREAM_SINK_CACHE */ @@ -125,6 +133,131 @@ static int suit_plat_fetch_cache(suit_component_t dst_handle, struct zcbor_strin } #endif /* CONFIG_SUIT_STREAM_SINK_CACHE */ +#ifdef CONFIG_FLASH_IPUC +static int suit_plat_fetch_mem(suit_component_t dst_handle, struct zcbor_string *uri, + struct zcbor_string *manifest_component_id, + struct suit_encryption_info *enc_info, bool dry_run) +{ +#ifndef CONFIG_SUIT_STREAM_FETCH_SOURCE_MGR + return SUIT_ERR_UNSUPPORTED_COMMAND; +#else + suit_plat_err_t plat_ret = SUIT_PLAT_SUCCESS; + struct stream_sink dst_sink = {0}; + struct zcbor_string *component_id = NULL; + int ret = SUIT_SUCCESS; + struct device *ipuc_dev = NULL; + struct zcbor_string enc_info_buf; + intptr_t run_address = 0; + size_t size = 0; + + /* + * Validate streaming operation. + */ + ret = suit_plat_component_id_get(dst_handle, &component_id); + if (ret != SUIT_SUCCESS) { + LOG_ERR("Failed to parse component ID from handle: %d", ret); + return SUIT_ERR_UNSUPPORTED_COMPONENT_ID; + } + + if (suit_plat_decode_address_size(component_id, &run_address, &size) != SUIT_PLAT_SUCCESS) { + LOG_ERR("Failed to decode address and size"); + return SUIT_ERR_UNSUPPORTED_COMPONENT_ID; + } + + if (!flash_component_ipuc_check(component_id)) { + LOG_ERR("Unable to find IPUC to modify MEM component"); + return SUIT_ERR_UNSUPPORTED_COMPONENT_ID; + } + + /* + * Construct the stream. + */ + + if (!dry_run) { + /* Create IPUC instance and erase the destination memory */ + if (enc_info != NULL) { + enc_info_buf.value = (const uint8_t *)enc_info; + enc_info_buf.len = sizeof(*enc_info); + ipuc_dev = flash_component_ipuc_create(component_id, &enc_info_buf, NULL); + } else { + ipuc_dev = flash_component_ipuc_create(component_id, NULL, NULL); + } + if (ipuc_dev == NULL) { + LOG_ERR("Unable to create IPUC to modify MEM component: %d", ret); + ret = SUIT_ERR_UNSUPPORTED_COMPONENT_ID; + } + +#ifdef CONFIG_SUIT_STREAM_SINK_FLASH + /* Internal MRAM/Flash on single controller */ + if (ret == SUIT_SUCCESS) { + if (!suit_flash_sink_is_address_supported((uint8_t *)run_address)) { + LOG_ERR("IPUC address not supported by flash sink (0x%lx, 0x%x)", + run_address, size); + ret = SUIT_ERR_UNSUPPORTED_COMPONENT_ID; + } else { + plat_ret = suit_flash_sink_get(&dst_sink, (uint8_t *)run_address, + size); + if (plat_ret != SUIT_PLAT_SUCCESS) { + LOG_ERR("Unable to create flash sink: %d", plat_ret); + ret = SUIT_ERR_UNSUPPORTED_COMPONENT_ID; + } + } + } +#else /* CONFIG_SUIT_STREAM_SINK_FLASH */ + ret = SUIT_ERR_UNSUPPORTED_COMPONENT_ID; +#endif /* CONFIG_SUIT_STREAM_SINK_FLASH */ + + /* + * Stream the data. + */ + + /* Skip memory erase - it was already cleared by the IPUC constructor. */ + + /* Start streaming the data. */ + if (ret == SUIT_SUCCESS) { + plat_ret = suit_fetch_source_stream(uri->value, uri->len, &dst_sink); + + /* Failures of fetch_source streamer are treated as "unavailable payload" + * failures. These are cases where suit-condition-image-match will detect + * the failure, however suit-plat-fetch should return success to allow + * soft failures. + */ + ret = SUIT_SUCCESS; + } + + if ((ret == SUIT_SUCCESS) && (dst_sink.flush != NULL)) { + plat_ret = dst_sink.flush(dst_sink.ctx); + if (plat_ret != SUIT_PLAT_SUCCESS) { + LOG_ERR("Sink flush failed: %d", plat_ret); + ret = suit_plat_err_to_processor_err_convert(plat_ret); + } + } + } + + /* + * Destroy the stream. + */ + + if (dst_sink.write != NULL) { + plat_ret = release_sink(&dst_sink); + if (plat_ret != SUIT_PLAT_SUCCESS) { + LOG_ERR("Sink release failed: %d", plat_ret); + } + } + + if (ipuc_dev != NULL) { + flash_ipuc_release(ipuc_dev); + } + + if (ret == SUIT_SUCCESS) { + ret = suit_plat_err_to_processor_err_convert(plat_ret); + } + + return ret; +#endif /* CONFIG_SUIT_STREAM_FETCH_SOURCE_MGR */ +} +#endif /* CONFIG_FLASH_IPUC */ + bool suit_plat_fetch_domain_specific_is_type_supported(suit_component_type_t dst_component_type) { #ifdef CONFIG_SUIT_STREAM_SINK_CACHE @@ -132,6 +265,11 @@ bool suit_plat_fetch_domain_specific_is_type_supported(suit_component_type_t dst return true; } #endif /* CONFIG_SUIT_STREAM_SINK_CACHE */ +#ifdef CONFIG_FLASH_IPUC + if (dst_component_type == SUIT_COMPONENT_TYPE_MEM) { + return true; + } +#endif /* CONFIG_FLASH_IPUC */ return false; } @@ -148,6 +286,10 @@ int suit_plat_check_fetch_domain_specific(suit_component_t dst_handle, return suit_plat_fetch_cache(dst_handle, uri, manifest_component_id, enc_info, true); #endif /* CONFIG_SUIT_STREAM_SINK_CACHE */ +#ifdef CONFIG_FLASH_IPUC + case SUIT_COMPONENT_TYPE_MEM: + return suit_plat_fetch_mem(dst_handle, uri, manifest_component_id, enc_info, true); +#endif /* CONFIG_FLASH_IPUC */ default: break; } @@ -167,6 +309,10 @@ int suit_plat_fetch_domain_specific(suit_component_t dst_handle, return suit_plat_fetch_cache(dst_handle, uri, manifest_component_id, enc_info, false); #endif /* CONFIG_SUIT_STREAM_SINK_CACHE */ +#ifdef CONFIG_FLASH_IPUC + case SUIT_COMPONENT_TYPE_MEM: + return suit_plat_fetch_mem(dst_handle, uri, manifest_component_id, enc_info, false); +#endif /* CONFIG_FLASH_IPUC */ default: break; } diff --git a/subsys/suit/stream/stream_sinks/src/suit_flash_sink.c b/subsys/suit/stream/stream_sinks/src/suit_flash_sink.c index 685dd9a3a0a7..eb46e5230d9c 100644 --- a/subsys/suit/stream/stream_sinks/src/suit_flash_sink.c +++ b/subsys/suit/stream/stream_sinks/src/suit_flash_sink.c @@ -398,6 +398,14 @@ static suit_plat_err_t seek(void *ctx, size_t offset) static suit_plat_err_t flush(void *ctx) { + struct flash_ctx *flash_ctx = (struct flash_ctx *)ctx; + + int ret = flash_write(flash_ctx->fdev, WRITE_OFFSET(flash_ctx), NULL, 0); + + if (ret != 0) { + return SUIT_PLAT_ERR_IO; + } + return SUIT_PLAT_SUCCESS; } From ccd269fe40b77f64af39c4f20d1884f637971f28 Mon Sep 17 00:00:00 2001 From: Tomasz Chyrowicz Date: Thu, 6 Feb 2025 14:02:27 +0100 Subject: [PATCH 3/4] suit: Add suupport for IPUC in cache Add support for IPUCs in DFU cache RW. Ref: NCSDK-30809 Signed-off-by: Tomasz Chyrowicz --- subsys/suit/cache/Kconfig | 12 ++ subsys/suit/cache/src/suit_dfu_cache_rw.c | 178 +++++++++++++++++- .../src/suit_orchestrator_app.c | 17 ++ 3 files changed, 201 insertions(+), 6 deletions(-) diff --git a/subsys/suit/cache/Kconfig b/subsys/suit/cache/Kconfig index 46a495cdc842..330512ec3cf1 100644 --- a/subsys/suit/cache/Kconfig +++ b/subsys/suit/cache/Kconfig @@ -39,4 +39,16 @@ config SUIT_CACHE_RW depends on SUIT_UTILS depends on !SUIT_PLATFORM_VARIANT_SDFW +config SUIT_CACHE_SDFW_IPUC_ID + int "IPUC-based parition number, capable of performing SDFW updates" + depends on SUIT_CACHE_RW + depends on FLASH_IPUC + default 254 + +config SUIT_CACHE_APP_IPUC_ID + int "IPUC-based parition number, without address restrictions" + depends on SUIT_CACHE_RW + depends on FLASH_IPUC + default 255 + endif # SUIT_CACHE diff --git a/subsys/suit/cache/src/suit_dfu_cache_rw.c b/subsys/suit/cache/src/suit_dfu_cache_rw.c index e4dff5f93513..6b039ae66977 100644 --- a/subsys/suit/cache/src/suit_dfu_cache_rw.c +++ b/subsys/suit/cache/src/suit_dfu_cache_rw.c @@ -14,10 +14,13 @@ #include #include #include - #include "suit_dfu_cache_internal.h" #include +#ifdef CONFIG_FLASH_IPUC +#include +#endif /* CONFIG_FLASH_IPUC */ + LOG_MODULE_REGISTER(suit_cache_rw, CONFIG_SUIT_LOG_LEVEL); #ifndef BSWAP_32 @@ -79,7 +82,18 @@ static struct dfu_cache_partition_ext dfu_partitions_ext[] = { { .id = 0, }, - LISTIFY(CONFIG_SUIT_CACHE_MAX_CACHES, PARTITION_DEFINE, (), dfu_cache_partition_)}; + LISTIFY(CONFIG_SUIT_CACHE_MAX_CACHES, PARTITION_DEFINE, (), dfu_cache_partition_) +#ifdef CONFIG_SUIT_CACHE_SDFW_IPUC_ID + { + .id = CONFIG_SUIT_CACHE_SDFW_IPUC_ID, + }, +#endif /* CONFIG_SUIT_CACHE_SDFW_IPUC_ID */ +#ifdef CONFIG_SUIT_CACHE_APP_IPUC_ID + { + .id = CONFIG_SUIT_CACHE_APP_IPUC_ID, + }, +#endif /* CONFIG_SUIT_CACHE_APP_IPUC_ID */ +}; /** * @brief Get cache partition of specified id @@ -197,6 +211,32 @@ static suit_plat_err_t erase_on_sink(uint8_t *address, size_t size) return SUIT_PLAT_SUCCESS; } +static void erase_cache_partition(struct dfu_cache_partition_ext *partition) +{ + if (partition == NULL) { + return; + } + +#ifdef CONFIG_SUIT_CACHE_SDFW_IPUC_ID + if (partition->id == CONFIG_SUIT_CACHE_SDFW_IPUC_ID) { + if (partition->fdev != NULL) { + flash_erase(partition->fdev, 0, partition->size); + } + return; + } +#endif /* CONFIG_SUIT_CACHE_SDFW_IPUC_ID */ +#ifdef CONFIG_SUIT_CACHE_APP_IPUC_ID + if (partition->id == CONFIG_SUIT_CACHE_APP_IPUC_ID) { + if (partition->fdev != NULL) { + flash_erase(partition->fdev, 0, partition->size); + } + return; + } +#endif /* CONFIG_SUIT_CACHE_APP_IPUC_ID */ + + erase_on_sink(partition->address, partition->size); +} + /** * @brief Check size of available free space in given cache and get allocable slot info * @@ -464,7 +504,7 @@ suit_plat_err_t suit_dfu_cache_validate_content(void) if (err == SUIT_PLAT_ERR_NOMEM) { LOG_INF("DFU Cache pool, id: %d is not empty... Erasing", partition->id); - erase_on_sink(partition->address, partition->size); + erase_cache_partition(partition); } } else if (err != SUIT_PLAT_SUCCESS) { LOG_ERR("DFU Cache pool, id: %d unavailable, err: %d", @@ -495,7 +535,7 @@ suit_plat_err_t suit_dfu_cache_drop_content(void) if (err == SUIT_PLAT_ERR_NOMEM) { LOG_INF("DFU Cache pool, id: %d is not empty... Erasing", partition->id); - erase_on_sink(partition->address, partition->size); + erase_cache_partition(partition); } } } @@ -547,6 +587,62 @@ suit_plat_err_t suit_dfu_cache_rw_slot_create(uint8_t cache_partition_id, return SUIT_PLAT_ERR_NOT_FOUND; } + /* Initialize DFU cache partition through IPUC API (erase memory). + * This operation is delayed, so only if a manifest uses IPUC-based cache partition, + * it's contents are deleted. + */ +#ifdef CONFIG_SUIT_CACHE_SDFW_IPUC_ID + if ((cache_partition_id == CONFIG_SUIT_CACHE_SDFW_IPUC_ID) && + (part->fdev == NULL)) { + uintptr_t sdfw_update_area_addr = 0; + size_t sdfw_update_area_size = 0; + + suit_memory_sdfw_update_area_info_get(&sdfw_update_area_addr, + &sdfw_update_area_size); + if (sdfw_update_area_size == 0) { + /* SoC does not enforce constrains on update candidate location + */ + sdfw_update_area_addr = 0; + } + + part->fdev = flash_cache_ipuc_create( + sdfw_update_area_addr, (uintptr_t *)&part->address, &part->size); + + if (part->fdev == NULL) { + part->address = NULL; + part->size = 0; + } else { + /* Disable the area before sdfw_update_area_addr */ + if ((uintptr_t)part->address < sdfw_update_area_addr) { + part->size -= + (sdfw_update_area_addr - (uintptr_t)part->address); + part->address = (uint8_t *)sdfw_update_area_addr; + } + + /* Disable the area after sdfw_update_area_addr + + * sdfw_update_area_size + */ + if ((sdfw_update_area_size > 0) && + ((uintptr_t)part->address + part->size > + sdfw_update_area_addr + sdfw_update_area_size)) { + part->size = sdfw_update_area_addr + sdfw_update_area_size - + (uintptr_t)part->address; + } + } + } +#endif /* CONFIG_SUIT_CACHE_SDFW_IPUC_ID */ +#ifdef CONFIG_SUIT_CACHE_APP_IPUC_ID + if ((cache_partition_id == CONFIG_SUIT_CACHE_APP_IPUC_ID) && (part->fdev == NULL)) { + part->fdev = flash_cache_ipuc_create( + (uintptr_t)part->address, (uintptr_t *)&part->address, &part->size); + } +#endif /* CONFIG_SUIT_CACHE_APP_IPUC_ID */ + + if (part->fdev == NULL) { + LOG_ERR("Partition %d unavailable", cache_partition_id); + return SUIT_PLAT_ERR_NOT_FOUND; + } + slot->eb_size = part->eb_size; suit_plat_err_t ret = slot_in_cache_partition_allocate(&tmp_uri, slot, part); @@ -719,7 +815,63 @@ suit_plat_err_t suit_dfu_cache_rw_slot_drop(struct suit_cache_slot *slot) return SUIT_PLAT_ERR_INVAL; } -int suit_dfu_cache_rw_init(void) +#ifdef CONFIG_FLASH_IPUC +/** + * @brief Check the availability of IPUCs that can be used as SUIT cache parition. + */ +static void dfu_cache_ipuc_init(void) +{ + for (size_t i = 1; i < ARRAY_SIZE(dfu_partitions_ext); i++) { + /* Calculating memory-mapped address for cache pool */ + struct dfu_cache_partition_ext *partition = &dfu_partitions_ext[i]; + +#ifdef CONFIG_SUIT_CACHE_SDFW_IPUC_ID + if (partition->id == CONFIG_SUIT_CACHE_SDFW_IPUC_ID) { + uintptr_t sdfw_update_area_addr = 0; + size_t sdfw_update_area_size = 0; + + suit_memory_sdfw_update_area_info_get(&sdfw_update_area_addr, + &sdfw_update_area_size); + if (sdfw_update_area_size == 0) { + /* SoC does not enforce constrains on update candidate location + */ + sdfw_update_area_addr = 0; + } + + bool available = flash_cache_ipuc_check(sdfw_update_area_addr, + (uintptr_t *)&partition->address, + &partition->size); + + if (!available) { + partition->size = 0; + } + + partition->wb_size = 1; + partition->eb_size = 1; + + continue; + } +#endif /* CONFIG_SUIT_CACHE_SDFW_IPUC_ID */ +#ifdef CONFIG_SUIT_CACHE_APP_IPUC_ID + if (partition->id == CONFIG_SUIT_CACHE_APP_IPUC_ID) { + bool available = flash_cache_ipuc_check(0, (uintptr_t *)&partition->address, + &partition->size); + + if (!available) { + partition->size = 0; + } + + partition->wb_size = 1; + partition->eb_size = 1; + + continue; + } +#endif /* CONFIG_SUIT_CACHE_APP_IPUC_ID */ + } +} +#endif /* CONFIG_FLASH_IPUC */ + +suit_plat_err_t suit_dfu_cache_rw_init(void) { for (size_t i = 1; i < ARRAY_SIZE(dfu_partitions_ext); i++) { @@ -730,6 +882,17 @@ int suit_dfu_cache_rw_init(void) .offset = partition->offset, }; +#ifdef CONFIG_SUIT_CACHE_SDFW_IPUC_ID + if (partition->id == CONFIG_SUIT_CACHE_SDFW_IPUC_ID) { + continue; + } +#endif /* CONFIG_SUIT_CACHE_SDFW_IPUC_ID */ +#ifdef CONFIG_SUIT_CACHE_APP_IPUC_ID + if (partition->id == CONFIG_SUIT_CACHE_APP_IPUC_ID) { + continue; + } +#endif /* CONFIG_SUIT_CACHE_APP_IPUC_ID */ + uintptr_t mapped_addr = 0; if (suit_memory_nvm_address_to_global_address(&device_offset, &mapped_addr)) { @@ -756,7 +919,6 @@ int suit_dfu_cache_rw_init(void) size_t eb_size = info.size; for (uint32_t p_idx = info.index + 1; p_idx < page_count; p_idx++) { - if (0 == flash_get_page_info_by_idx(partition->fdev, p_idx, &info)) { if (info.size > eb_size) { @@ -786,6 +948,10 @@ int suit_dfu_cache_rw_init(void) } } +#ifdef CONFIG_FLASH_IPUC + dfu_cache_ipuc_init(); +#endif /* CONFIG_FLASH_IPUC */ + int err = cache_0_resize(false); if (err != SUIT_PLAT_SUCCESS) { diff --git a/subsys/suit/orchestrator_app/src/suit_orchestrator_app.c b/subsys/suit/orchestrator_app/src/suit_orchestrator_app.c index 3386ef29980e..abcc7880308b 100644 --- a/subsys/suit/orchestrator_app/src/suit_orchestrator_app.c +++ b/subsys/suit/orchestrator_app/src/suit_orchestrator_app.c @@ -247,6 +247,23 @@ int suit_dfu_update_start(void) update_regions_count++; } } + +#ifdef CONFIG_SUIT_CACHE_SDFW_IPUC_ID + if (suit_dfu_cache_rw_device_info_get(CONFIG_SUIT_CACHE_SDFW_IPUC_ID, &device_info) == + SUIT_PLAT_SUCCESS) { + update_candidate[update_regions_count].mem = device_info.mapped_address; + update_candidate[update_regions_count].size = device_info.partition_size; + update_regions_count++; + } +#endif /* CONFIG_SUIT_CACHE_SDFW_IPUC_ID */ +#ifdef CONFIG_SUIT_CACHE_APP_IPUC_ID + if (suit_dfu_cache_rw_device_info_get(CONFIG_SUIT_CACHE_APP_IPUC_ID, &device_info) == + SUIT_PLAT_SUCCESS) { + update_candidate[update_regions_count].mem = device_info.mapped_address; + update_candidate[update_regions_count].size = device_info.partition_size; + update_regions_count++; + } +#endif /* CONFIG_SUIT_CACHE_APP_IPUC_ID */ #endif return suit_trigger_update(update_candidate, update_regions_count); From f77831d2ba97395c460bba1c432a988b9ddb08da Mon Sep 17 00:00:00 2001 From: Tomasz Chyrowicz Date: Tue, 14 Jan 2025 16:55:03 +0100 Subject: [PATCH 4/4] suit: Split fetch tests into app and SDFW Add tests that verify fetching into MEM component from both Application and SDFW contexts. Add smoke tests for IPUCs as MEM driver as well as cache backend. Ref: NCSDK-30809 Signed-off-by: Tomasz Chyrowicz --- tests/subsys/suit/fetch/CMakeLists.txt | 10 +- tests/subsys/suit/fetch/Kconfig | 35 ++ .../suit/fetch/boards/native_sim.overlay | 28 +- .../suit/fetch/boards/native_sim_64.overlay | 28 +- .../subsys/suit/fetch/mocks/mock_suit_ipuc.h | 29 ++ tests/subsys/suit/fetch/prj.conf | 9 +- tests/subsys/suit/fetch/src/main.c | 262 +--------- tests/subsys/suit/fetch/src/test_app_ipuc.c | 484 ++++++++++++++++++ tests/subsys/suit/fetch/src/test_sdfw_mem.c | 276 ++++++++++ tests/subsys/suit/fetch/testcase.yaml | 15 + 10 files changed, 898 insertions(+), 278 deletions(-) create mode 100644 tests/subsys/suit/fetch/mocks/mock_suit_ipuc.h create mode 100644 tests/subsys/suit/fetch/src/test_app_ipuc.c create mode 100644 tests/subsys/suit/fetch/src/test_sdfw_mem.c diff --git a/tests/subsys/suit/fetch/CMakeLists.txt b/tests/subsys/suit/fetch/CMakeLists.txt index a89f4f225a89..774c2027a99c 100644 --- a/tests/subsys/suit/fetch/CMakeLists.txt +++ b/tests/subsys/suit/fetch/CMakeLists.txt @@ -11,8 +11,14 @@ find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) project(integration_test_fetch) include(../cmake/test_template.cmake) +target_include_directories(app PRIVATE mocks) + zephyr_library_link_libraries(suit_memptr_storage_interface) zephyr_library_link_libraries(suit_platform_interface) zephyr_library_link_libraries(suit_ipuc) -zephyr_library_link_libraries(suit_stream_filters_interface) -zephyr_library_link_libraries(suit_decrypt_test_utils) + +zephyr_library_link_libraries_ifdef(CONFIG_TEST_SUIT_PLATFORM_FETCH_VARIANT_APP suit_memory_layout_interface) +zephyr_library_link_libraries_ifdef(CONFIG_TEST_SUIT_PLATFORM_FETCH_VARIANT_SDFW suit_decrypt_test_utils) +zephyr_library_link_libraries_ifdef(CONFIG_TEST_SUIT_PLATFORM_FETCH_VARIANT_SDFW suit_stream_filters_interface) + +zephyr_compile_definitions(CONFIG_SOC_NRF54H20_CPUAPP) diff --git a/tests/subsys/suit/fetch/Kconfig b/tests/subsys/suit/fetch/Kconfig index 8edc11bd7cde..2a0a29ea192e 100644 --- a/tests/subsys/suit/fetch/Kconfig +++ b/tests/subsys/suit/fetch/Kconfig @@ -8,4 +8,39 @@ rsource "../mocks/Kconfig" rsource "../tests/Kconfig" +choice TEST_SUIT_PLATFORM_FETCH_VARIANT + prompt "SUIT platform implementation variant to test" + default TEST_SUIT_PLATFORM_FETCH_VARIANT_SDFW + +config TEST_SUIT_PLATFORM_FETCH_VARIANT_APP + bool "Local or application" + depends on SUIT_PLATFORM_VARIANT_APP + select FLASH_IPUC + select SUIT_ENVELOPE_INFO + select SUIT_CACHE_RW + select SUIT_STREAM_SINK_CACHE + select SUIT_STREAM_FETCH_SOURCE_MGR + +config TEST_SUIT_PLATFORM_FETCH_VARIANT_SDFW + bool "Bootloader or SDFW" + depends on SUIT_PLATFORM_VARIANT_SDFW + select MOCK_SDFW_ARBITER + select SUIT_EXECUTION_MODE + select SUIT_MCI + select SUIT_STREAM_FILTER_DECRYPT + select SUIT_CRYPTO + select SUIT_STORAGE + +endchoice # TEST_SUIT_PLATFORM_FETCH_VARIANT + +if TEST_SUIT_PLATFORM_FETCH_VARIANT_APP + +config SUIT_CACHE_SDFW_IPUC_ID + default 254 + +config SUIT_CACHE_APP_IPUC_ID + default 255 + +endif # TEST_SUIT_PLATFORM_FETCH_VARIANT_APP + source "Kconfig.zephyr" diff --git a/tests/subsys/suit/fetch/boards/native_sim.overlay b/tests/subsys/suit/fetch/boards/native_sim.overlay index ac659f573154..369c537bd235 100644 --- a/tests/subsys/suit/fetch/boards/native_sim.overlay +++ b/tests/subsys/suit/fetch/boards/native_sim.overlay @@ -7,12 +7,30 @@ &flash0 { partitions { compatible = "fixed-partitions"; - #address-cells = <1>; - #size-cells = <1>; + #address-cells = < 0x1 >; + #size-cells = < 0x1 >; - /* Use the last 48KB of NVM as suit storage. */ - suit_storage: partition@f4000 { - reg = <0xf4000 DT_SIZE_K(48)>; + suit_storage: partition@e9000 { + reg = <0xe9000 DT_SIZE_K(48)>; }; + + dfu_partition: partition@f5000 { + reg = < 0xf5000 DT_SIZE_K(4) >; + }; + + dfu_cache_partition_1: partition@f6000 { + reg = <0xf6000 DT_SIZE_K(4)>; + }; + + dfu_cache_partition_3: partition@f7000 { + reg = <0xf7000 DT_SIZE_K(4)>; + }; + }; +}; + +/ { + sram0: memory@20000000 { + compatible = "mmio-sram"; + reg = <0x20000000 DT_SIZE_K(256)>; }; }; diff --git a/tests/subsys/suit/fetch/boards/native_sim_64.overlay b/tests/subsys/suit/fetch/boards/native_sim_64.overlay index ac659f573154..369c537bd235 100644 --- a/tests/subsys/suit/fetch/boards/native_sim_64.overlay +++ b/tests/subsys/suit/fetch/boards/native_sim_64.overlay @@ -7,12 +7,30 @@ &flash0 { partitions { compatible = "fixed-partitions"; - #address-cells = <1>; - #size-cells = <1>; + #address-cells = < 0x1 >; + #size-cells = < 0x1 >; - /* Use the last 48KB of NVM as suit storage. */ - suit_storage: partition@f4000 { - reg = <0xf4000 DT_SIZE_K(48)>; + suit_storage: partition@e9000 { + reg = <0xe9000 DT_SIZE_K(48)>; }; + + dfu_partition: partition@f5000 { + reg = < 0xf5000 DT_SIZE_K(4) >; + }; + + dfu_cache_partition_1: partition@f6000 { + reg = <0xf6000 DT_SIZE_K(4)>; + }; + + dfu_cache_partition_3: partition@f7000 { + reg = <0xf7000 DT_SIZE_K(4)>; + }; + }; +}; + +/ { + sram0: memory@20000000 { + compatible = "mmio-sram"; + reg = <0x20000000 DT_SIZE_K(256)>; }; }; diff --git a/tests/subsys/suit/fetch/mocks/mock_suit_ipuc.h b/tests/subsys/suit/fetch/mocks/mock_suit_ipuc.h new file mode 100644 index 000000000000..2405f8139b53 --- /dev/null +++ b/tests/subsys/suit/fetch/mocks/mock_suit_ipuc.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#ifndef MOCK_SUIT_IPUC_H__ +#define MOCK_SUIT_IPUC_H__ + +#include +#include + +DEFINE_FFF_GLOBALS; + +FAKE_VALUE_FUNC(int, suit_ipuc_get_count, size_t *); +FAKE_VALUE_FUNC(int, suit_ipuc_get_info, size_t, struct zcbor_string *, suit_manifest_role_t *); +FAKE_VALUE_FUNC(int, suit_ipuc_write_setup, struct zcbor_string *, struct zcbor_string *, + struct zcbor_string *); +FAKE_VALUE_FUNC(int, suit_ipuc_write, struct zcbor_string *, size_t, uintptr_t, size_t, bool); + +static inline void mock_suit_ipuc_reset(void) +{ + RESET_FAKE(suit_ipuc_get_count); + RESET_FAKE(suit_ipuc_get_info); + RESET_FAKE(suit_ipuc_write_setup); + RESET_FAKE(suit_ipuc_write); +} + +#endif /* MOCK_SUIT_IPUC_H__ */ diff --git a/tests/subsys/suit/fetch/prj.conf b/tests/subsys/suit/fetch/prj.conf index 39dfd781706c..7b8b600e6ccd 100644 --- a/tests/subsys/suit/fetch/prj.conf +++ b/tests/subsys/suit/fetch/prj.conf @@ -22,6 +22,8 @@ CONFIG_SUIT_UTILS=y CONFIG_SUIT_CACHE=y CONFIG_SUIT_MEMPTR_STORAGE=y +CONFIG_SUIT_LOG_LEVEL_DBG=y + CONFIG_ZCBOR=y CONFIG_ZCBOR_CANONICAL=y @@ -29,10 +31,3 @@ CONFIG_FLASH=y CONFIG_FLASH_MAP=y CONFIG_SUIT_IPUC=y -CONFIG_MOCK_SDFW_ARBITER=y -CONFIG_SUIT_STORAGE=y - -CONFIG_SUIT_EXECUTION_MODE=y -CONFIG_SUIT_MCI=y -CONFIG_SUIT_STREAM_FILTER_DECRYPT=y -CONFIG_SUIT_CRYPTO=y diff --git a/tests/subsys/suit/fetch/src/main.c b/tests/subsys/suit/fetch/src/main.c index 547c080ae02a..1f491589db22 100644 --- a/tests/subsys/suit/fetch/src/main.c +++ b/tests/subsys/suit/fetch/src/main.c @@ -8,15 +8,7 @@ #include #include #include -#include #include -#include -#include -#include -#include - -#define TEST_DATA_SIZE 64 -#define WRITE_ADDR 0x1A00080000 static uint8_t test_data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, @@ -91,58 +83,14 @@ static void setup_cache_with_sample_entries(void) dfu_caches.pools[1].size = (size_t)cache2_len; dfu_caches.pools_count = 2; + suit_dfu_cache_deinitialize(); + suit_plat_err_t rc = suit_dfu_cache_initialize(&dfu_caches); zassert_equal(rc, SUIT_PLAT_SUCCESS, "Failed to initialize cache: %i", rc); } -static void test_before(void *data) -{ - /* Reset mocks */ - mocks_sdfw_reset(); - - /* Reset common FFF internal structures */ - FFF_RESET_HISTORY(); -} - -ZTEST_SUITE(fetch_tests, NULL, NULL, test_before, NULL, NULL); - -ZTEST(fetch_tests, test_integrated_fetch_to_msink_OK) -{ - struct zcbor_string source = {.value = test_data, .len = sizeof(test_data)}; - /* Create handle that will be used as destination */ - suit_component_t dst_handle; - /* [h'MEM', h'02', h'1A00080000', h'191000'] */ - uint8_t valid_dst_value[] = {0x84, 0x44, 0x63, 'M', 'E', 'M', 0x41, 0x02, 0x45, - 0x1A, 0x00, 0x08, 0x00, 0x00, 0x43, 0x19, 0x10, 0x00}; - - struct zcbor_string valid_dst_component_id = { - .value = valid_dst_value, - .len = sizeof(valid_dst_value), - }; - - int ipc_client_id = 1234; - int ret = suit_plat_create_component_handle(&valid_dst_component_id, false, &dst_handle); - - zassert_equal(ret, SUIT_SUCCESS, "create_component_handle failed - error %i", ret); - ret = suit_ipuc_sdfw_write_setup(ipc_client_id, &valid_dst_component_id, NULL, NULL); - zassert_equal(ret, SUIT_PLAT_ERR_NOT_FOUND, "in-place updateable component found"); - - ret = suit_ipuc_sdfw_declare(dst_handle, SUIT_MANIFEST_UNKNOWN); - zassert_equal(ret, SUIT_PLAT_SUCCESS, "suit_ipuc_sdfw_declare failed - error %i", ret); - - ret = suit_ipuc_sdfw_write_setup(ipc_client_id, &valid_dst_component_id, NULL, NULL); - zassert_equal(ret, SUIT_PLAT_SUCCESS, "suit_ipuc_sdfw_write_setup failed - error %i", ret); - - ret = suit_plat_fetch_integrated(dst_handle, &source, &valid_dst_component_id, NULL); - zassert_equal(ret, SUIT_SUCCESS, "suit_plat_fetch failed - error %i", ret); - - ret = suit_ipuc_sdfw_write_setup(ipc_client_id, &valid_dst_component_id, NULL, NULL); - zassert_equal(ret, SUIT_PLAT_ERR_NOT_FOUND, "in-place updateable component found"); - - ret = suit_plat_release_component_handle(dst_handle); - zassert_equal(ret, SUIT_SUCCESS, "dst_handle release failed - error %i", ret); -} +ZTEST_SUITE(fetch_tests, NULL, NULL, NULL, NULL, NULL); ZTEST(fetch_tests, test_integrated_fetch_to_memptr_OK) { @@ -356,207 +304,3 @@ ZTEST(fetch_tests, test_integrated_fetch_to_memptr_NOK_handle_NULL) ret = suit_plat_release_component_handle(component_handle); zassert_equal(ret, SUIT_SUCCESS, "Handle release failed - error %i", ret); } - -ZTEST(fetch_tests, test_integrated_fetch_to_msink_encrypted_OK) -{ - suit_component_t component_handle; - memptr_storage_handle_t handle = NULL; - struct zcbor_string source = { - .value = decrypt_test_ciphertext_direct, - .len = sizeof(decrypt_test_ciphertext_direct) - }; - - /* [h'MEM', h'02', h'1A00080000', h'191000'] */ - uint8_t valid_value[] = {0x84, 0x44, 0x63, 'M', 'E', 'M', 0x41, 0x02, 0x45, - 0x1A, 0x00, 0x08, 0x00, 0x00, 0x43, 0x19, 0x10, 0x00}; - - struct zcbor_string valid_component_id = { - .value = valid_value, - .len = sizeof(valid_value), - }; - - psa_key_id_t cek_key_id; - uint8_t cek_key_id_cbor[] = { - 0x1A, 0x00, 0x00, 0x00, 0x00, - }; - - psa_status_t status = psa_crypto_init(); - - zassert_equal(status, PSA_SUCCESS, "Failed to init psa crypto"); - - status = decrypt_test_init_encryption_key(decrypt_test_key_data, - sizeof(decrypt_test_key_data), &cek_key_id, - PSA_ALG_GCM, cek_key_id_cbor); - zassert_equal(status, PSA_SUCCESS, "Failed to import key"); - - struct suit_encryption_info enc_info = - DECRYPT_TEST_ENC_INFO_DEFAULT_INIT(cek_key_id_cbor); - - int ret = suit_plat_create_component_handle(&valid_component_id, false, &component_handle); - - zassert_equal(ret, SUIT_SUCCESS, "create_component_handle failed - error %i", ret); - - ret = suit_plat_fetch_integrated(component_handle, &source, - &valid_manifest_component_id, &enc_info); - zassert_equal(ret, SUIT_SUCCESS, "suit_plat_fetch failed - error %i", ret); - - ret = suit_plat_component_impl_data_get(component_handle, &handle); - zassert_equal(ret, SUIT_SUCCESS, "suit_plat_component_impl_data_get failed - error %i", - ret); - - const uint8_t *payload; - size_t payload_size = 0; - - - ret = suit_memptr_storage_ptr_get(handle, &payload, &payload_size); - - zassert_equal(ret, SUIT_PLAT_SUCCESS, "storage.get failed - error %i", ret); - zassert_equal(memcmp(decrypt_test_plaintext, payload, - strlen(decrypt_test_plaintext)), 0, - "Retrieved decrypted payload doesn't mach decrypt_test_plaintext"); - zassert_equal(sizeof(decrypt_test_plaintext), payload_size, - "Retrieved payload_size doesn't mach size of decrypt_test_plaintext"); - - ret = suit_plat_release_component_handle(component_handle); - zassert_equal(ret, SUIT_SUCCESS, "Handle release failed - error %i", ret); - - psa_destroy_key(cek_key_id); -} - -ZTEST(fetch_tests, test_integrated_fetch_to_memptr_encrypted_NOK) -{ - suit_component_t component_handle; - memptr_storage_handle_t handle = NULL; - struct zcbor_string source = { - .value = decrypt_test_ciphertext_direct, - .len = sizeof(decrypt_test_ciphertext_direct) - }; - - /* [h'CAND_IMG', h'02'] */ - uint8_t valid_value[] = {0x82, 0x49, 0x68, 'C', 'A', 'N', 'D', - '_', 'I', 'M', 'G', 0x41, 0x02}; - - struct zcbor_string valid_component_id = { - .value = valid_value, - .len = sizeof(valid_value), - }; - - psa_key_id_t cek_key_id; - uint8_t cek_key_id_cbor[] = { - 0x1A, 0x00, 0x00, 0x00, 0x00, - }; - - psa_status_t status = psa_crypto_init(); - - zassert_equal(status, PSA_SUCCESS, "Failed to init psa crypto"); - - status = decrypt_test_init_encryption_key(decrypt_test_key_data, - sizeof(decrypt_test_key_data), &cek_key_id, - PSA_ALG_GCM, cek_key_id_cbor); - zassert_equal(status, PSA_SUCCESS, "Failed to import key"); - - struct suit_encryption_info enc_info = - DECRYPT_TEST_ENC_INFO_DEFAULT_INIT(cek_key_id_cbor); - - int ret = suit_plat_create_component_handle(&valid_component_id, false, &component_handle); - - zassert_equal(ret, SUIT_SUCCESS, "create_component_handle failed - error %i", ret); - - ret = suit_plat_fetch_integrated(component_handle, &source, - &valid_manifest_component_id, &enc_info); - zassert_equal(ret, SUIT_SUCCESS, "suit_plat_fetch failed - error %i", ret); - - ret = suit_plat_component_impl_data_get(component_handle, &handle); - zassert_equal(ret, SUIT_SUCCESS, "suit_plat_component_impl_data_get failed - error %i", - ret); - - const uint8_t *payload; - size_t payload_size = 0; - - - ret = suit_memptr_storage_ptr_get(handle, &payload, &payload_size); - - zassert_equal(ret, SUIT_PLAT_SUCCESS, "storage.get failed - error %i", ret); - zassert_not_equal(memcmp(decrypt_test_plaintext, payload, - strlen(decrypt_test_plaintext)), 0, - "Retrieved decrypted payload should not mach decrypt_test_plaintext"); - zassert_equal(sizeof(decrypt_test_plaintext), payload_size, - "Retrieved payload_size doesn't mach size of decrypt_test_plaintext"); - - ret = suit_plat_release_component_handle(component_handle); - zassert_equal(ret, SUIT_SUCCESS, "Handle release failed - error %i", ret); - - psa_destroy_key(cek_key_id); -} - -ZTEST(fetch_tests, test_integrated_fetch_to_msink_encrypted_wrong_key_NOK) -{ - suit_component_t component_handle; - memptr_storage_handle_t handle = NULL; - struct zcbor_string source = { - .value = decrypt_test_ciphertext_direct, - .len = sizeof(decrypt_test_ciphertext_direct) - }; - - /* [h'MEM', h'02', h'1A00080000', h'191000'] */ - uint8_t valid_value[] = {0x84, 0x44, 0x63, 'M', 'E', 'M', 0x41, 0x02, 0x45, - 0x1A, 0x00, 0x08, 0x00, 0x00, 0x43, 0x19, 0x10, 0x00}; - - struct zcbor_string valid_component_id = { - .value = valid_value, - .len = sizeof(valid_value), - }; - - psa_key_id_t cek_key_id; - uint8_t cek_key_id_cbor[] = { - 0x1A, 0x00, 0x00, 0x00, 0x00, - }; - - psa_status_t status = psa_crypto_init(); - - zassert_equal(status, PSA_SUCCESS, "Failed to init psa crypto"); - - uint8_t wrong_test_key_data[sizeof(decrypt_test_key_data)]; - - memcpy(wrong_test_key_data, decrypt_test_key_data, sizeof(wrong_test_key_data)); - /* Corrupt CEK that we store.*/ - wrong_test_key_data[10] = 0x00; - - status = decrypt_test_init_encryption_key(wrong_test_key_data, - sizeof(wrong_test_key_data), &cek_key_id, - PSA_ALG_GCM, cek_key_id_cbor); - zassert_equal(status, PSA_SUCCESS, "Failed to import key"); - - struct suit_encryption_info enc_info = - DECRYPT_TEST_ENC_INFO_DEFAULT_INIT(cek_key_id_cbor); - - int ret = suit_plat_create_component_handle(&valid_component_id, false, &component_handle); - - zassert_equal(ret, SUIT_SUCCESS, "create_component_handle failed - error %i", ret); - - ret = suit_plat_fetch_integrated(component_handle, &source, - &valid_manifest_component_id, &enc_info); - zassert_equal(ret, SUIT_ERR_AUTHENTICATION, - "suit_plat_fetch should have failed with SUIT_ERR_AUTHENTICATION"); - - ret = suit_plat_component_impl_data_get(component_handle, &handle); - zassert_equal(ret, SUIT_SUCCESS, "suit_plat_component_impl_data_get failed - error %i", - ret); - - const uint8_t *payload; - size_t payload_size = 0; - - ret = suit_memptr_storage_ptr_get(handle, &payload, &payload_size); - - zassert_equal(ret, SUIT_PLAT_SUCCESS, "storage.get failed - error %i", ret); - zassert_not_equal(memcmp(decrypt_test_plaintext, payload, - strlen(decrypt_test_plaintext)), 0, - "Retrieved decrypted payload should not mach decrypt_test_plaintext"); - zassert_equal(payload_size, 0, - "Retrieved payload_size is not 0"); - - ret = suit_plat_release_component_handle(component_handle); - zassert_equal(ret, SUIT_SUCCESS, "Handle release failed - error %i", ret); - - psa_destroy_key(cek_key_id); -} diff --git a/tests/subsys/suit/fetch/src/test_app_ipuc.c b/tests/subsys/suit/fetch/src/test_app_ipuc.c new file mode 100644 index 000000000000..a09b656d249b --- /dev/null +++ b/tests/subsys/suit/fetch/src/test_app_ipuc.c @@ -0,0 +1,484 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#ifdef CONFIG_TEST_SUIT_PLATFORM_FETCH_VARIANT_APP +#include +#include +#include +#include +#include +#include +#include +#include + +#if IS_ENABLED(CONFIG_FLASH) +#if (DT_NODE_EXISTS(DT_CHOSEN(zephyr_flash_controller))) +#define SUIT_PLAT_INTERNAL_NVM_DEV DEVICE_DT_GET(DT_CHOSEN(zephyr_flash_controller)) +#else +#define SUIT_PLAT_INTERNAL_NVM_DEV DEVICE_DT_GET(DT_CHOSEN(zephyr_flash)) +#endif +#else +#define SUIT_PLAT_INTERNAL_NVM_DEV NULL +#endif + +static const uint8_t test_data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}; +static const uint8_t test_data_sdfw[] = {'S', 'D', 'F', 'W', '.', 'b', 'i', 'n'}; +static const uint8_t test_data_scfw[] = {'S', 'y', 's', 'C', 't', 'r', 'l', '.', 'b', 'i', 'n'}; + +/* clang-format off */ +static const uint8_t component_rad_0x80000_0x1000[] = { + 0x84, /* array(4) */ + 0x44, /* bstr(4) */ + 0x63, /* text(3) */ + 'M', 'E', 'M', + 0x41, /* bstr(1) */ + 0x20, /* Data */ + 0x45, /* bstr(5) */ + 0x1a, /* uint32 */ + 0x00, 0x08, 0x00, 0x00, + 0x43, /* bstr(3) */ + 0x19, /* uint16 */ + 0x10, 0x00, +}; +static const uint8_t component_app_0x90000_0x1000[] = { + 0x84, /* array(4) */ + 0x44, /* bstr(4) */ + 0x63, /* text(3) */ + 'M', 'E', 'M', + 0x41, /* bstr(1) */ + 0x20, /* Data */ + 0x45, /* bstr(5) */ + 0x1a, /* uint32 */ + 0x00, 0x09, 0x00, 0x00, + 0x43, /* bstr(3) */ + 0x19, /* uint16 */ + 0x10, 0x00, +}; +/* clang-format on */ + +static suit_plat_err_t suit_ipuc_get_count_single_fake_func(size_t *count) +{ + zassert_not_null(count, "Caller must provide non-Null pointer"); + + *count = 1; + + return SUIT_PLAT_SUCCESS; +} + +static suit_plat_err_t +suit_ipuc_get_info_0x80000_0x1000_fake_func(size_t idx, struct zcbor_string *component_id, + suit_manifest_role_t *role) +{ + zassert_not_null(component_id, "Caller must provide non-Null pointer to read component_id"); + zassert_not_null(role, "Caller must provide non-Null pointer to read role"); + zassert_equal(idx, 0, "Unexpected idx value"); + + component_id->value = component_rad_0x80000_0x1000; + component_id->len = sizeof(component_rad_0x80000_0x1000); + *role = SUIT_MANIFEST_RAD_LOCAL_1; + + return SUIT_PLAT_SUCCESS; +} + +static suit_plat_err_t suit_ipuc_write_rad_fake_func(struct zcbor_string *component_id, + size_t offset, uintptr_t buffer, + size_t chunk_size, bool last_chunk) +{ + zassert_not_null(component_id, "Caller must provide non-Null pointer to read component_id"); + zassert_equal(component_id->len, sizeof(component_rad_0x80000_0x1000), + "Invalid component ID length passed"); + zassert_equal(memcmp(component_id->value, component_rad_0x80000_0x1000, component_id->len), + 0, "Invalid component ID value passed"); + + return SUIT_PLAT_SUCCESS; +} + +static suit_plat_err_t suit_ipuc_get_count_fake_func(size_t *count) +{ + zassert_not_null(count, "Caller must provide non-Null pointer"); + + *count = 2; + + return SUIT_PLAT_SUCCESS; +} + +static suit_plat_err_t suit_ipuc_get_info_fake_func(size_t idx, struct zcbor_string *component_id, + suit_manifest_role_t *role) +{ + zassert_not_null(component_id, "Caller must provide non-Null pointer to read component_id"); + zassert_not_null(role, "Caller must provide non-Null pointer to read role"); + zassert_true(idx < 2, "Unexpected idx value"); + + if (idx == 0) { + component_id->value = component_rad_0x80000_0x1000; + component_id->len = sizeof(component_rad_0x80000_0x1000); + *role = SUIT_MANIFEST_RAD_LOCAL_1; + } else if (idx == 1) { + component_id->value = component_app_0x90000_0x1000; + component_id->len = sizeof(component_app_0x90000_0x1000); + *role = SUIT_MANIFEST_APP_LOCAL_1; + } + + return SUIT_PLAT_SUCCESS; +} + +static suit_plat_err_t suit_ipuc_write_fake_func(struct zcbor_string *component_id, size_t offset, + uintptr_t buffer, size_t chunk_size, + bool last_chunk) +{ + zassert_not_null(component_id, "Caller must provide non-Null pointer to read component_id"); + zassert_equal(component_id->len, sizeof(component_app_0x90000_0x1000), + "Invalid component ID length passed"); + zassert_equal(memcmp(component_id->value, component_app_0x90000_0x1000, component_id->len), + 0, "Invalid component ID value passed"); + + zassert_equal(flash_write(SUIT_PLAT_INTERNAL_NVM_DEV, 0x90000 + offset, (uint8_t *)buffer, + chunk_size), + 0, "Unable to modify NVM to match requested changes"); + + return SUIT_PLAT_SUCCESS; +} + +static int fetch_request_fn(const uint8_t *uri, size_t uri_length, uint32_t session_id) +{ + int rc = 0; + + if (((uri_length == sizeof("http://databucket.com")) && + memcmp(uri, "http://databucket.com", uri_length) == 0)) { + zassert_equal(uri_length, sizeof("http://databucket.com"), + "Unexpected URI length: %d", uri_length); + zassert_equal(memcmp(uri, "http://databucket.com", uri_length), 0, + "Unexpected URI value"); + rc = suit_dfu_fetch_source_write_fetched_data(session_id, test_data, + sizeof(test_data)); + } else if (((uri_length == sizeof("http://databucket.com/sdfw.bin")) && + memcmp(uri, "http://databucket.com/sdfw.bin", uri_length) == 0)) { + zassert_equal(uri_length, sizeof("http://databucket.com/sdfw.bin"), + "Unexpected URI length: %d", uri_length); + zassert_equal(memcmp(uri, "http://databucket.com/sdfw.bin", uri_length), 0, + "Unexpected URI value"); + rc = suit_dfu_fetch_source_write_fetched_data(session_id, test_data_sdfw, + sizeof(test_data_sdfw)); + } else if (((uri_length == sizeof("http://databucket.com/scfw.bin")) && + memcmp(uri, "http://databucket.com/scfw.bin", uri_length) == 0)) { + zassert_equal(uri_length, sizeof("http://databucket.com/scfw.bin"), + "Unexpected URI length: %d", uri_length); + zassert_equal(memcmp(uri, "http://databucket.com/scfw.bin", uri_length), 0, + "Unexpected URI value"); + rc = suit_dfu_fetch_source_write_fetched_data(session_id, test_data_scfw, + sizeof(test_data_scfw)); + } else { + zassert_true(false, "Unsupporte URI value"); + } + + if (rc != 0) { + return SUIT_PLAT_ERR_CRASH; + } + + return SUIT_PLAT_SUCCESS; +} + +static void test_before(void *data) +{ + /* Reset mocks */ + mock_suit_ipuc_reset(); + + /* Reset common FFF internal structures */ + FFF_RESET_HISTORY(); + + /* Erase NVM area */ + zassert_equal(flash_erase(SUIT_PLAT_INTERNAL_NVM_DEV, 0x90000, 0x1000), 0, + "Unable to erase NVM before test execution"); + + /* Load IPUCs as SUIT caches */ + suit_ipuc_get_count_fake.custom_fake = suit_ipuc_get_count_fake_func; + suit_ipuc_get_info_fake.custom_fake = suit_ipuc_get_info_fake_func; + + suit_dfu_cache_rw_init(); + zassert_equal(suit_dfu_cache_0_resize(), SUIT_PLAT_SUCCESS, + "Failed to initialize SUIT cache before test execution."); + zassert_equal(suit_ipuc_get_count_fake.call_count, 2, + "Incorrect number of suit_ipuc_get_count() calls (%d)", + suit_ipuc_get_count_fake.call_count); + /* 2x ((count: search for max) + (info of the max))*/ + zassert_equal(suit_ipuc_get_info_fake.call_count, 6, + "Incorrect number of suit_ipuc_get_info() calls (%d)", + suit_ipuc_get_info_fake.call_count); + + /* Reset mocks */ + mock_suit_ipuc_reset(); + + /* Reset common FFF internal structures */ + FFF_RESET_HISTORY(); +} + +static void test_after(void *data) +{ + uintptr_t cache_ipuc_address = (uintptr_t)suit_plat_mem_ptr_get(0x90000); + size_t cache_ipuc_size = 0x1000; + struct device *flash_ipuc = NULL; + uintptr_t ipuc_address = 0; + size_t ipuc_size = 0; + + /* SUIT cache IPUC is not released automatically as it is possible to populate cache + * with miltiple payloads. + * Relase them manually to preserve constant number of IPUCs for all test cases. + */ + flash_ipuc = + flash_ipuc_find(cache_ipuc_address, cache_ipuc_size, &ipuc_address, &ipuc_size); + if (flash_ipuc != NULL) { + flash_ipuc_release(flash_ipuc); + } +} + +ZTEST_SUITE(fetch_app_mem_tests, NULL, NULL, test_before, NULL, NULL); + +ZTEST(fetch_app_mem_tests, test_fetch_to_ipuc_mem_OK) +{ + struct zcbor_string uri = {.value = "http://databucket.com", + .len = sizeof("http://databucket.com")}; + /* Create handle that will be used as destination */ + suit_component_t dst_handle; + /* [h'MEM', h'20', h'1A00080000', h'191000'] */ + uint8_t valid_dst_value[] = {0x84, 0x44, 0x63, 'M', 'E', 'M', 0x41, 0x20, 0x45, + 0x1A, 0x00, 0x08, 0x00, 0x00, 0x43, 0x19, 0x10, 0x00}; + + struct zcbor_string valid_dst_component_id = { + .value = valid_dst_value, + .len = sizeof(valid_dst_value), + }; + + suit_ipuc_get_count_fake.custom_fake = suit_ipuc_get_count_single_fake_func; + suit_ipuc_get_info_fake.custom_fake = suit_ipuc_get_info_0x80000_0x1000_fake_func; + suit_ipuc_write_setup_fake.return_val = SUIT_PLAT_SUCCESS; + suit_ipuc_write_fake.custom_fake = suit_ipuc_write_rad_fake_func; + + zassert_equal(suit_dfu_fetch_source_register(fetch_request_fn), SUIT_PLAT_SUCCESS, + "Failed to register fetch source mock"); + + int ret = suit_plat_create_component_handle(&valid_dst_component_id, false, &dst_handle); + + zassert_equal(ret, SUIT_SUCCESS, "create_component_handle failed - error %i", ret); + + ret = suit_plat_fetch(dst_handle, &uri, &valid_dst_component_id, NULL); + zassert_equal(ret, SUIT_SUCCESS, "suit_plat_fetch failed - error %i", ret); + + zassert_equal(suit_ipuc_get_count_fake.call_count, 2, + "Incorrect number of suit_ipuc_get_count() calls (%d)", + suit_ipuc_get_count_fake.call_count); + zassert_equal(suit_ipuc_get_info_fake.call_count, 2, + "Incorrect number of suit_ipuc_get_info() calls (%d)", + suit_ipuc_get_info_fake.call_count); + zassert_equal(suit_ipuc_write_setup_fake.call_count, 1, + "Incorrect number of suit_ipuc_write_setup() calls (%d)", + suit_ipuc_write_setup_fake.call_count); + zassert_equal(suit_ipuc_write_fake.call_count, 2, + "Incorrect number of suit_ipuc_write() calls (%d)", + suit_ipuc_write_fake.call_count); + + zassert_equal(suit_ipuc_write_fake.arg1_history[0], 0, + "Unexpected offset in the first suit_ipuc_write() call (0x%x)", + suit_ipuc_write_fake.arg1_history[0]); + zassert_equal_ptr(suit_ipuc_write_fake.arg2_history[0], test_data, + "Unexpected data pointer in the first suit_ipuc_write() call (0x%x)", + suit_ipuc_write_fake.arg2_history[0]); + zassert_equal(suit_ipuc_write_fake.arg3_history[0], sizeof(test_data), + "Unexpected data size in the first suit_ipuc_write() call (0x%x)", + suit_ipuc_write_fake.arg3_history[0]); + zassert_equal(suit_ipuc_write_fake.arg4_history[0], false, + "Write closed on the first suit_ipuc_write() call"); + + zassert_equal(suit_ipuc_write_fake.arg1_history[1], sizeof(test_data), + "Unexpected offset in the second suit_ipuc_write() call (0x%x)", + suit_ipuc_write_fake.arg1_history[1]); + zassert_equal_ptr(suit_ipuc_write_fake.arg2_history[1], NULL, + "Unexpected data pointer in the second suit_ipuc_write() call (0x%x)", + suit_ipuc_write_fake.arg2_history[1]); + zassert_equal(suit_ipuc_write_fake.arg3_history[1], 0, + "Unexpected data size in the second suit_ipuc_write() call (0x%x)", + suit_ipuc_write_fake.arg3_history[1]); + zassert_equal(suit_ipuc_write_fake.arg4_history[1], true, + "Write was not closed on the second suit_ipuc_write() call"); + + ret = suit_plat_release_component_handle(dst_handle); + zassert_equal(ret, SUIT_SUCCESS, "dst_handle release failed - error %i", ret); +} + +ZTEST_SUITE(fetch_app_cache_tests, NULL, NULL, test_before, test_after, NULL); + +ZTEST(fetch_app_cache_tests, test_fetch_to_ipuc_cache_OK) +{ + struct zcbor_string uri = {.value = "http://databucket.com", + .len = sizeof("http://databucket.com")}; + /* Create handle that will be used as destination */ + suit_component_t dst_handle; + /* clang-format off */ + uint8_t valid_dst_value[] = { + 0x82, /* array(2) */ + 0x4B, /* bytes(11) */ + 0x6A, /* text(10) */ + 'C', 'A', 'C', 'H', 'E', '_', 'P', 'O', 'O', 'L', + 0x42, /* bytes(2) */ + 0x18, /* uint8_t */ + CONFIG_SUIT_CACHE_APP_IPUC_ID, + }; + /* clang-format on */ + struct zcbor_string valid_dst_component_id = { + .value = valid_dst_value, + .len = sizeof(valid_dst_value), + }; + size_t cached_size = 0; + const uint8_t *cached_payload = NULL; + + suit_ipuc_get_count_fake.custom_fake = suit_ipuc_get_count_fake_func; + suit_ipuc_get_info_fake.custom_fake = suit_ipuc_get_info_fake_func; + suit_ipuc_write_setup_fake.return_val = SUIT_PLAT_SUCCESS; + suit_ipuc_write_fake.custom_fake = suit_ipuc_write_fake_func; + + zassert_equal(suit_dfu_fetch_source_register(fetch_request_fn), SUIT_PLAT_SUCCESS, + "Failed to register fetch source mock"); + + int ret = suit_plat_create_component_handle(&valid_dst_component_id, false, &dst_handle); + + zassert_equal(ret, SUIT_SUCCESS, "create_component_handle failed - error %i", ret); + + ret = suit_plat_fetch(dst_handle, &uri, &valid_dst_component_id, NULL); + zassert_equal(ret, SUIT_SUCCESS, "suit_plat_fetch failed - error %i", ret); + + zassert_equal(suit_ipuc_write_setup_fake.call_count, 1, + "Incorrect number of suit_ipuc_write_setup() calls (%d)", + suit_ipuc_write_setup_fake.call_count); + zassert_equal(suit_ipuc_get_count_fake.call_count, 1, + "Incorrect number of suit_ipuc_get_count() calls (%d)", + suit_ipuc_get_count_fake.call_count); + zassert_equal(suit_ipuc_get_info_fake.call_count, 3, + "Incorrect number of suit_ipuc_get_info() calls (%d)", + suit_ipuc_get_info_fake.call_count); + + zassert_true(suit_ipuc_write_fake.call_count >= 4, + "Too small number of suit_ipuc_write() calls (%d)", + suit_ipuc_write_fake.call_count); + + size_t last_write = suit_ipuc_write_fake.call_count - 1; + + for (size_t i = 0; i < suit_ipuc_write_fake.call_count; i++) { + printk("\t%d\n", suit_ipuc_write_fake.arg3_history[i]); + zassert_equal(suit_ipuc_write_fake.arg4_history[i], false, + "Write closed too early in suit_ipuc_write() call"); + } + + /* partition header + slot header + URI + (data length) */ + /* data */ + + /* slot length */ + zassert_equal(suit_ipuc_write_fake.arg3_history[last_write - 1], 4, + "Unexpected data size in the third suit_ipuc_write() call (0x%x)", + suit_ipuc_write_fake.arg3_history[last_write - 1]); + + /* slot end marker */ + zassert_equal(suit_ipuc_write_fake.arg3_history[last_write], 1, + "Unexpected data size in the fourth suit_ipuc_write() call (0x%x)", + suit_ipuc_write_fake.arg3_history[last_write]); + + ret = suit_plat_release_component_handle(dst_handle); + zassert_equal(ret, SUIT_SUCCESS, "dst_handle release failed - error %i", ret); + + /* Verify the cache contents. */ + zassert_equal(suit_dfu_cache_search(uri.value, uri.len, &cached_payload, &cached_size), + SUIT_PLAT_SUCCESS, "Failed to find cached entry inside the SUIT cache"); + zassert_equal(cached_size, sizeof(test_data), "Invalid size of the cached data (%d)", + cached_size); + zassert_equal(memcmp(cached_payload, test_data, sizeof(test_data)), 0, + "Invalid cached payload"); +} + +ZTEST(fetch_app_cache_tests, test_fetch_to_ipuc_cache_sdfw_scfw_OK) +{ + struct zcbor_string uri_sdfw = {.value = "http://databucket.com/sdfw.bin", + .len = sizeof("http://databucket.com/sdfw.bin")}; + struct zcbor_string uri_scfw = {.value = "http://databucket.com/scfw.bin", + .len = sizeof("http://databucket.com/scfw.bin")}; + /* Create handle that will be used as destination */ + suit_component_t dst_handle; + /* clang-format off */ + uint8_t valid_dst_value[] = { + 0x82, /* array(2) */ + 0x4B, /* bytes(11) */ + 0x6A, /* text(10) */ + 'C', 'A', 'C', 'H', 'E', '_', 'P', 'O', 'O', 'L', + 0x42, /* bytes(2) */ + 0x18, /* uint8_t */ + CONFIG_SUIT_CACHE_SDFW_IPUC_ID, + }; + /* clang-format on */ + struct zcbor_string valid_dst_component_id = { + .value = valid_dst_value, + .len = sizeof(valid_dst_value), + }; + size_t cached_size = 0; + const uint8_t *cached_payload = NULL; + + suit_ipuc_get_count_fake.custom_fake = suit_ipuc_get_count_fake_func; + suit_ipuc_get_info_fake.custom_fake = suit_ipuc_get_info_fake_func; + suit_ipuc_write_setup_fake.return_val = SUIT_PLAT_SUCCESS; + suit_ipuc_write_fake.custom_fake = suit_ipuc_write_fake_func; + + zassert_equal(suit_dfu_fetch_source_register(fetch_request_fn), SUIT_PLAT_SUCCESS, + "Failed to register fetch source mock"); + + int ret = suit_plat_create_component_handle(&valid_dst_component_id, false, &dst_handle); + + zassert_equal(ret, SUIT_SUCCESS, "create_component_handle failed - error %i", ret); + + /* Fetch SDFW binary */ + ret = suit_plat_fetch(dst_handle, &uri_sdfw, &valid_dst_component_id, NULL); + zassert_equal(ret, SUIT_SUCCESS, "suit_plat_fetch failed - error %i", ret); + + zassert_equal(suit_ipuc_write_setup_fake.call_count, 1, + "Incorrect number of suit_ipuc_write_setup() calls (%d)", + suit_ipuc_write_setup_fake.call_count); + zassert_equal(suit_ipuc_get_count_fake.call_count, 1, + "Incorrect number of suit_ipuc_get_count() calls (%d)", + suit_ipuc_get_count_fake.call_count); + zassert_equal(suit_ipuc_get_info_fake.call_count, 3, + "Incorrect number of suit_ipuc_get_info() calls (%d)", + suit_ipuc_get_info_fake.call_count); + + /* Fetch SCFW binary using the same IPUC and component context */ + ret = suit_plat_fetch(dst_handle, &uri_scfw, &valid_dst_component_id, NULL); + zassert_equal(ret, SUIT_SUCCESS, "suit_plat_fetch failed - error %i", ret); + + /* The write_setup call count should remain the same - IPUC must not be erased at this + * point. + */ + zassert_equal(suit_ipuc_write_setup_fake.call_count, 1, + "Incorrect number of suit_ipuc_write_setup() calls (%d)", + suit_ipuc_write_setup_fake.call_count); + + ret = suit_plat_release_component_handle(dst_handle); + zassert_equal(ret, SUIT_SUCCESS, "dst_handle release failed - error %i", ret); + + /* Verify the cache contents. */ + zassert_equal( + suit_dfu_cache_search(uri_sdfw.value, uri_sdfw.len, &cached_payload, &cached_size), + SUIT_PLAT_SUCCESS, "Failed to find SDFW entry inside the SUIT cache"); + zassert_equal(cached_size, sizeof(test_data_sdfw), "Invalid SDFW size in cache (%d)", + cached_size); + zassert_equal(memcmp(cached_payload, test_data_sdfw, sizeof(test_data_sdfw)), 0, + "Invalid SDFW payload"); + + zassert_equal( + suit_dfu_cache_search(uri_scfw.value, uri_scfw.len, &cached_payload, &cached_size), + SUIT_PLAT_SUCCESS, "Failed to find SCFW entry inside the SUIT cache"); + zassert_equal(cached_size, sizeof(test_data_scfw), "Invalid SCFW size in cache (%d)", + cached_size); + zassert_equal(memcmp(cached_payload, test_data_scfw, sizeof(test_data_scfw)), 0, + "Invalid SCFW payload"); +} +#endif /* CONFIG_TEST_SUIT_PLATFORM_FETCH_VARIANT_APP */ diff --git a/tests/subsys/suit/fetch/src/test_sdfw_mem.c b/tests/subsys/suit/fetch/src/test_sdfw_mem.c new file mode 100644 index 000000000000..bb89723a0dd4 --- /dev/null +++ b/tests/subsys/suit/fetch/src/test_sdfw_mem.c @@ -0,0 +1,276 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#ifdef CONFIG_TEST_SUIT_PLATFORM_FETCH_VARIANT_SDFW +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static uint8_t test_data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}; + +/* clang-format off */ +static const uint8_t valid_manifest_component[] = { + 0x82, /* array: 2 elements */ + 0x4c, /* byte string: 12 bytes */ + 0x6b, /* string: 11 characters */ + 'I', 'N', 'S', 'T', 'L', 'D', '_', 'M', 'F', 'S', 'T', + 0x50, /* byte string: 16 bytes */ + 0x5b, 0x46, 0x9f, 0xd1, 0x90, 0xee, 0x53, 0x9c, + 0xa3, 0x18, 0x68, 0x1b, 0x03, 0x69, 0x5e, 0x36 +}; +/* clang-format on */ + +static struct zcbor_string valid_manifest_component_id = { + .value = valid_manifest_component, + .len = sizeof(valid_manifest_component), +}; + +static void test_before(void *data) +{ + /* Reset mocks */ + mocks_sdfw_reset(); + + /* Reset common FFF internal structures */ + FFF_RESET_HISTORY(); +} + +ZTEST_SUITE(fetch_sdfw_mem_tests, NULL, NULL, test_before, NULL, NULL); + +ZTEST(fetch_sdfw_mem_tests, test_integrated_fetch_to_msink_OK) +{ + struct zcbor_string source = {.value = test_data, .len = sizeof(test_data)}; + /* Create handle that will be used as destination */ + suit_component_t dst_handle; + /* [h'MEM', h'02', h'1A00080000', h'191000'] */ + uint8_t valid_dst_value[] = {0x84, 0x44, 0x63, 'M', 'E', 'M', 0x41, 0x02, 0x45, + 0x1A, 0x00, 0x08, 0x00, 0x00, 0x43, 0x19, 0x10, 0x00}; + + struct zcbor_string valid_dst_component_id = { + .value = valid_dst_value, + .len = sizeof(valid_dst_value), + }; + + int ipc_client_id = 1234; + int ret = suit_plat_create_component_handle(&valid_dst_component_id, false, &dst_handle); + + zassert_equal(ret, SUIT_SUCCESS, "create_component_handle failed - error %i", ret); + ret = suit_ipuc_sdfw_write_setup(ipc_client_id, &valid_dst_component_id, NULL, NULL); + zassert_equal(ret, SUIT_PLAT_ERR_NOT_FOUND, "in-place updateable component found"); + + ret = suit_ipuc_sdfw_declare(dst_handle, SUIT_MANIFEST_UNKNOWN); + zassert_equal(ret, SUIT_PLAT_SUCCESS, "suit_ipuc_sdfw_declare failed - error %i", ret); + + ret = suit_ipuc_sdfw_write_setup(ipc_client_id, &valid_dst_component_id, NULL, NULL); + zassert_equal(ret, SUIT_PLAT_SUCCESS, "suit_ipuc_sdfw_write_setup failed - error %i", ret); + + ret = suit_plat_fetch_integrated(dst_handle, &source, &valid_dst_component_id, NULL); + zassert_equal(ret, SUIT_SUCCESS, "suit_plat_fetch failed - error %i", ret); + + ret = suit_ipuc_sdfw_write_setup(ipc_client_id, &valid_dst_component_id, NULL, NULL); + zassert_equal(ret, SUIT_PLAT_ERR_NOT_FOUND, "in-place updateable component found"); + + ret = suit_plat_release_component_handle(dst_handle); + zassert_equal(ret, SUIT_SUCCESS, "dst_handle release failed - error %i", ret); +} + +ZTEST(fetch_tests, test_integrated_fetch_to_msink_encrypted_OK) +{ + suit_component_t component_handle; + memptr_storage_handle_t handle = NULL; + struct zcbor_string source = {.value = decrypt_test_ciphertext_direct, + .len = sizeof(decrypt_test_ciphertext_direct)}; + + /* [h'MEM', h'02', h'1A00080000', h'191000'] */ + uint8_t valid_value[] = {0x84, 0x44, 0x63, 'M', 'E', 'M', 0x41, 0x02, 0x45, + 0x1A, 0x00, 0x08, 0x00, 0x00, 0x43, 0x19, 0x10, 0x00}; + + struct zcbor_string valid_component_id = { + .value = valid_value, + .len = sizeof(valid_value), + }; + + psa_key_id_t cek_key_id; + uint8_t cek_key_id_cbor[] = { + 0x1A, 0x00, 0x00, 0x00, 0x00, + }; + + psa_status_t status = psa_crypto_init(); + + zassert_equal(status, PSA_SUCCESS, "Failed to init psa crypto"); + + status = decrypt_test_init_encryption_key(decrypt_test_key_data, + sizeof(decrypt_test_key_data), &cek_key_id, + PSA_ALG_GCM, cek_key_id_cbor); + zassert_equal(status, PSA_SUCCESS, "Failed to import key"); + + struct suit_encryption_info enc_info = DECRYPT_TEST_ENC_INFO_DEFAULT_INIT(cek_key_id_cbor); + + int ret = suit_plat_create_component_handle(&valid_component_id, false, &component_handle); + + zassert_equal(ret, SUIT_SUCCESS, "create_component_handle failed - error %i", ret); + + ret = suit_plat_fetch_integrated(component_handle, &source, &valid_manifest_component_id, + &enc_info); + zassert_equal(ret, SUIT_SUCCESS, "suit_plat_fetch failed - error %i", ret); + + ret = suit_plat_component_impl_data_get(component_handle, &handle); + zassert_equal(ret, SUIT_SUCCESS, "suit_plat_component_impl_data_get failed - error %i", + ret); + + const uint8_t *payload; + size_t payload_size = 0; + + ret = suit_memptr_storage_ptr_get(handle, &payload, &payload_size); + + zassert_equal(ret, SUIT_PLAT_SUCCESS, "storage.get failed - error %i", ret); + zassert_equal(memcmp(decrypt_test_plaintext, payload, strlen(decrypt_test_plaintext)), 0, + "Retrieved decrypted payload doesn't mach decrypt_test_plaintext"); + zassert_equal(sizeof(decrypt_test_plaintext), payload_size, + "Retrieved payload_size doesn't mach size of decrypt_test_plaintext"); + + ret = suit_plat_release_component_handle(component_handle); + zassert_equal(ret, SUIT_SUCCESS, "Handle release failed - error %i", ret); + + psa_destroy_key(cek_key_id); +} + +ZTEST(fetch_tests, test_integrated_fetch_to_memptr_encrypted_NOK) +{ + suit_component_t component_handle; + memptr_storage_handle_t handle = NULL; + struct zcbor_string source = {.value = decrypt_test_ciphertext_direct, + .len = sizeof(decrypt_test_ciphertext_direct)}; + + /* [h'CAND_IMG', h'02'] */ + uint8_t valid_value[] = {0x82, 0x49, 0x68, 'C', 'A', 'N', 'D', + '_', 'I', 'M', 'G', 0x41, 0x02}; + + struct zcbor_string valid_component_id = { + .value = valid_value, + .len = sizeof(valid_value), + }; + + psa_key_id_t cek_key_id; + uint8_t cek_key_id_cbor[] = { + 0x1A, 0x00, 0x00, 0x00, 0x00, + }; + + psa_status_t status = psa_crypto_init(); + + zassert_equal(status, PSA_SUCCESS, "Failed to init psa crypto"); + + status = decrypt_test_init_encryption_key(decrypt_test_key_data, + sizeof(decrypt_test_key_data), &cek_key_id, + PSA_ALG_GCM, cek_key_id_cbor); + zassert_equal(status, PSA_SUCCESS, "Failed to import key"); + + struct suit_encryption_info enc_info = DECRYPT_TEST_ENC_INFO_DEFAULT_INIT(cek_key_id_cbor); + + int ret = suit_plat_create_component_handle(&valid_component_id, false, &component_handle); + + zassert_equal(ret, SUIT_SUCCESS, "create_component_handle failed - error %i", ret); + + ret = suit_plat_fetch_integrated(component_handle, &source, &valid_manifest_component_id, + &enc_info); + zassert_equal(ret, SUIT_SUCCESS, "suit_plat_fetch failed - error %i", ret); + + ret = suit_plat_component_impl_data_get(component_handle, &handle); + zassert_equal(ret, SUIT_SUCCESS, "suit_plat_component_impl_data_get failed - error %i", + ret); + + const uint8_t *payload; + size_t payload_size = 0; + + ret = suit_memptr_storage_ptr_get(handle, &payload, &payload_size); + + zassert_equal(ret, SUIT_PLAT_SUCCESS, "storage.get failed - error %i", ret); + zassert_not_equal(memcmp(decrypt_test_plaintext, payload, strlen(decrypt_test_plaintext)), + 0, "Retrieved decrypted payload should not mach decrypt_test_plaintext"); + zassert_equal(sizeof(decrypt_test_plaintext), payload_size, + "Retrieved payload_size doesn't mach size of decrypt_test_plaintext"); + + ret = suit_plat_release_component_handle(component_handle); + zassert_equal(ret, SUIT_SUCCESS, "Handle release failed - error %i", ret); + + psa_destroy_key(cek_key_id); +} + +ZTEST(fetch_tests, test_integrated_fetch_to_msink_encrypted_wrong_key_NOK) +{ + suit_component_t component_handle; + memptr_storage_handle_t handle = NULL; + struct zcbor_string source = {.value = decrypt_test_ciphertext_direct, + .len = sizeof(decrypt_test_ciphertext_direct)}; + + /* [h'MEM', h'02', h'1A00080000', h'191000'] */ + uint8_t valid_value[] = {0x84, 0x44, 0x63, 'M', 'E', 'M', 0x41, 0x02, 0x45, + 0x1A, 0x00, 0x08, 0x00, 0x00, 0x43, 0x19, 0x10, 0x00}; + + struct zcbor_string valid_component_id = { + .value = valid_value, + .len = sizeof(valid_value), + }; + + psa_key_id_t cek_key_id; + uint8_t cek_key_id_cbor[] = { + 0x1A, 0x00, 0x00, 0x00, 0x00, + }; + + psa_status_t status = psa_crypto_init(); + + zassert_equal(status, PSA_SUCCESS, "Failed to init psa crypto"); + + uint8_t wrong_test_key_data[sizeof(decrypt_test_key_data)]; + + memcpy(wrong_test_key_data, decrypt_test_key_data, sizeof(wrong_test_key_data)); + /* Corrupt CEK that we store.*/ + wrong_test_key_data[10] = 0x00; + + status = decrypt_test_init_encryption_key(wrong_test_key_data, sizeof(wrong_test_key_data), + &cek_key_id, PSA_ALG_GCM, cek_key_id_cbor); + zassert_equal(status, PSA_SUCCESS, "Failed to import key"); + + struct suit_encryption_info enc_info = DECRYPT_TEST_ENC_INFO_DEFAULT_INIT(cek_key_id_cbor); + + int ret = suit_plat_create_component_handle(&valid_component_id, false, &component_handle); + + zassert_equal(ret, SUIT_SUCCESS, "create_component_handle failed - error %i", ret); + + ret = suit_plat_fetch_integrated(component_handle, &source, &valid_manifest_component_id, + &enc_info); + zassert_equal(ret, SUIT_ERR_AUTHENTICATION, + "suit_plat_fetch should have failed with SUIT_ERR_AUTHENTICATION"); + + ret = suit_plat_component_impl_data_get(component_handle, &handle); + zassert_equal(ret, SUIT_SUCCESS, "suit_plat_component_impl_data_get failed - error %i", + ret); + + const uint8_t *payload; + size_t payload_size = 0; + + ret = suit_memptr_storage_ptr_get(handle, &payload, &payload_size); + + zassert_equal(ret, SUIT_PLAT_SUCCESS, "storage.get failed - error %i", ret); + zassert_not_equal(memcmp(decrypt_test_plaintext, payload, strlen(decrypt_test_plaintext)), + 0, "Retrieved decrypted payload should not mach decrypt_test_plaintext"); + zassert_equal(payload_size, 0, "Retrieved payload_size is not 0"); + + ret = suit_plat_release_component_handle(component_handle); + zassert_equal(ret, SUIT_SUCCESS, "Handle release failed - error %i", ret); + + psa_destroy_key(cek_key_id); +} +#endif /* CONFIG_TEST_SUIT_PLATFORM_FETCH_VARIANT_SDFW */ diff --git a/tests/subsys/suit/fetch/testcase.yaml b/tests/subsys/suit/fetch/testcase.yaml index 3cea0c3522ae..293e472e1b92 100644 --- a/tests/subsys/suit/fetch/testcase.yaml +++ b/tests/subsys/suit/fetch/testcase.yaml @@ -13,3 +13,18 @@ tests: - nrf52840dk/nrf52840 - native_sim - native_sim/native/64 + + suit-platform.integration.fetch.app: + platform_allow: + - native_sim + - native_sim/native/64 + tags: + - suit-processor + - suit_platform + - suit + - ci_tests_subsys_suit + extra_configs: + - CONFIG_SUIT_PLATFORM_VARIANT_APP=y + integration_platforms: + - native_sim + - native_sim/native/64