From 085e5a678ca84a3b5c8cd2e0c3b41de872f4b9cf Mon Sep 17 00:00:00 2001 From: Tomasz Chyrowicz Date: Mon, 13 Jan 2025 17:13:52 +0100 Subject: [PATCH] suit: Add flash_ipuc pseudo-driver tests Add tests for the pseudo-flash driver that writes data through SUIT IPUC. Ref: NCSDK-30809 Signed-off-by: Tomasz Chyrowicz --- .github/test-spec.yml | 1 + CODEOWNERS | 1 + tests/drivers/flash/flash_ipuc/CMakeLists.txt | 25 + .../flash/flash_ipuc/boards/native_sim.conf | 8 + .../flash_ipuc/boards/native_sim_64.conf | 8 + .../flash/flash_ipuc/mocks/mock_suit_ipuc.h | 29 + tests/drivers/flash/flash_ipuc/prj.conf | 15 + tests/drivers/flash/flash_ipuc/src/main.c | 1050 +++++++++++++++++ tests/drivers/flash/flash_ipuc/testcase.yaml | 16 + 9 files changed, 1153 insertions(+) create mode 100644 tests/drivers/flash/flash_ipuc/CMakeLists.txt create mode 100644 tests/drivers/flash/flash_ipuc/boards/native_sim.conf create mode 100644 tests/drivers/flash/flash_ipuc/boards/native_sim_64.conf create mode 100644 tests/drivers/flash/flash_ipuc/mocks/mock_suit_ipuc.h create mode 100644 tests/drivers/flash/flash_ipuc/prj.conf create mode 100644 tests/drivers/flash/flash_ipuc/src/main.c create mode 100644 tests/drivers/flash/flash_ipuc/testcase.yaml diff --git a/.github/test-spec.yml b/.github/test-spec.yml index 7eaa63dd3cb1..223e71d7065b 100644 --- a/.github/test-spec.yml +++ b/.github/test-spec.yml @@ -150,6 +150,7 @@ - "subsys/pcd/*" - "subsys/net/lib/*fota*/**/*" - "subsys/net/lib/downloader/**/*" + - "tests/drivers/flash/flash_ipuc/**/*" - "tests/subsys/bootloader/**/*" - "tests/subsys/dfu/**/*" diff --git a/CODEOWNERS b/CODEOWNERS index 1e04989c04cb..b3387eaef17a 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -801,6 +801,7 @@ /tests/crypto/ @stephen-nordic @magnev /tests/drivers/audio/pdm_loopback/ @nrfconnect/ncs-low-level-test /tests/drivers/gpio/ @nrfconnect/ncs-low-level-test @nrfconnect/ncs-ll-ursus +/tests/drivers/flash/flash_ipuc/ @nrfconnect/ncs-charon /tests/drivers/flash/flash_rpc/ @nrfconnect/ncs-pluto /tests/drivers/flash_patch/ @nrfconnect/ncs-pluto /tests/drivers/fprotect/ @nrfconnect/ncs-pluto diff --git a/tests/drivers/flash/flash_ipuc/CMakeLists.txt b/tests/drivers/flash/flash_ipuc/CMakeLists.txt new file mode 100644 index 000000000000..bb9c83c6e113 --- /dev/null +++ b/tests/drivers/flash/flash_ipuc/CMakeLists.txt @@ -0,0 +1,25 @@ +# +# Copyright (c) 2025 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) + +project(flash_ipuc) + +target_include_directories(app PRIVATE ${ZEPHYR_NRF_MODULE_DIR}/subsys/suit/ipuc/include) +target_include_directories(app PRIVATE mocks) + +zephyr_compile_definitions(CONFIG_FLASH_IPUC_COUNT=2) +zephyr_compile_definitions(CONFIG_FLASH_IPUC_LOG_LEVEL) +zephyr_compile_definitions(CONFIG_FLASH_IPUC_LOG_LEVEL_DBG) +# Pretend to call API as nRF54H20 application FW +zephyr_compile_definitions(CONFIG_SOC_NRF54H20_CPUAPP) + +target_sources(app PRIVATE + src/main.c + ${ZEPHYR_NRF_MODULE_DIR}/drivers/flash/flash_ipuc/flash_ipuc.c +) diff --git a/tests/drivers/flash/flash_ipuc/boards/native_sim.conf b/tests/drivers/flash/flash_ipuc/boards/native_sim.conf new file mode 100644 index 000000000000..caa8f6b64a2a --- /dev/null +++ b/tests/drivers/flash/flash_ipuc/boards/native_sim.conf @@ -0,0 +1,8 @@ +# +# Copyright (c) 2025 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +CONFIG_FLASH_SIMULATOR=y +CONFIG_FLASH_SIMULATOR_DOUBLE_WRITES=y diff --git a/tests/drivers/flash/flash_ipuc/boards/native_sim_64.conf b/tests/drivers/flash/flash_ipuc/boards/native_sim_64.conf new file mode 100644 index 000000000000..caa8f6b64a2a --- /dev/null +++ b/tests/drivers/flash/flash_ipuc/boards/native_sim_64.conf @@ -0,0 +1,8 @@ +# +# Copyright (c) 2025 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +CONFIG_FLASH_SIMULATOR=y +CONFIG_FLASH_SIMULATOR_DOUBLE_WRITES=y diff --git a/tests/drivers/flash/flash_ipuc/mocks/mock_suit_ipuc.h b/tests/drivers/flash/flash_ipuc/mocks/mock_suit_ipuc.h new file mode 100644 index 000000000000..2405f8139b53 --- /dev/null +++ b/tests/drivers/flash/flash_ipuc/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/drivers/flash/flash_ipuc/prj.conf b/tests/drivers/flash/flash_ipuc/prj.conf new file mode 100644 index 000000000000..eaeacfd4d189 --- /dev/null +++ b/tests/drivers/flash/flash_ipuc/prj.conf @@ -0,0 +1,15 @@ +# +# Copyright (c) 2025 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +CONFIG_ZTEST=y +CONFIG_SUIT=y +CONFIG_SUIT_UTILS=y +CONFIG_SUIT_METADATA=y + +CONFIG_ZCBOR=y +CONFIG_ZCBOR_CANONICAL=y + +CONFIG_FLASH=y diff --git a/tests/drivers/flash/flash_ipuc/src/main.c b/tests/drivers/flash/flash_ipuc/src/main.c new file mode 100644 index 000000000000..2b0655638549 --- /dev/null +++ b/tests/drivers/flash/flash_ipuc/src/main.c @@ -0,0 +1,1050 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include +#include +#include +#include +#include + +#define CONFIG_SUIT_IPUC_SIZE 5 + +struct flash_ipuc_tests_fixture { + size_t exp_component_idx; + struct zcbor_string *exp_encryption_info; + struct zcbor_string *exp_compression_info; + size_t count; + struct zcbor_string component_ids[CONFIG_SUIT_IPUC_SIZE]; + suit_manifest_role_t roles[CONFIG_SUIT_IPUC_SIZE]; +}; + +static struct flash_ipuc_tests_fixture test_fixture = {0}; + +/* clang-format off */ +static const uint8_t component_invalid[] = { + 'I', 'n', 'v', 'a', 'l', 'i', 'd', ' ', + 'c', 'o', 'm', 'p', 'o', 'n', 'e', 'n', 't', ' ', + 'I', 'D', '\n', +}; +static const uint8_t component_radio_0x54000_0x1000[] = { + 0x84, /* array(4) */ + 0x44, /* bstr(4) */ + 0x63, /* text(3) */ + 'M', 'E', 'M', + 0x41, /* bstr(1) */ + 0x03, /* Radio core - executable */ + 0x45, /* bstr(5) */ + 0x1a, /* uint32 */ + 0x00, 0x05, 0x40, 0x00, + 0x43, /* bstr(3) */ + 0x19, /* uint16 */ + 0x10, 0x00, +}; +static const uint8_t component_radio_0x55000_0x3f000[] = { + 0x84, /* array(4) */ + 0x44, /* bstr(4) */ + 0x63, /* text(3) */ + 'M', 'E', 'M', + 0x41, /* bstr(1) */ + 0x20, /* Radio core - data */ + 0x45, /* bstr(5) */ + 0x1a, /* uint32 */ + 0x00, 0x05, 0x50, 0x00, + 0x45, /* bstr(5) */ + 0x1a, /* uint32 */ + 0x00, 0x03, 0xf0, 0x00, +}; +static const uint8_t component_app_0x94000_0x6d000[] = { + 0x84, /* array(4) */ + 0x44, /* bstr(4) */ + 0x63, /* text(3) */ + 'M', 'E', 'M', + 0x41, /* bstr(1) */ + 0x02, /* Application core - executable */ + 0x45, /* bstr(5) */ + 0x1a, /* uint32 */ + 0x00, 0x09, 0x40, 0x00, + 0x45, /* bstr(5) */ + 0x1a, /* uint32 */ + 0x00, 0x06, 0xd0, 0x00, +}; +static const uint8_t component_app_0x101000_0x60000[] = { + 0x84, /* array(4) */ + 0x44, /* bstr(4) */ + 0x63, /* text(3) */ + 'M', 'E', 'M', + 0x41, /* bstr(1) */ + 0x20, /* Application core - data */ + 0x45, /* bstr(5) */ + 0x1a, /* uint32 */ + 0x00, 0x10, 0x10, 0x00, + 0x45, /* bstr(5) */ + 0x1a, /* uint32 */ + 0x00, 0x06, 0x00, 0x00, +}; +/* clang-format on */ + +static void setup_sample_ipucs(struct flash_ipuc_tests_fixture *f) +{ + /* Existence of an invalid component ID should not affect the usage of the IPUC APIs. */ + f->component_ids[0].value = component_invalid; + f->component_ids[0].len = sizeof(component_invalid); + f->roles[0] = SUIT_MANIFEST_APP_ROOT; + + f->component_ids[1].value = component_radio_0x54000_0x1000; + f->component_ids[1].len = sizeof(component_radio_0x54000_0x1000); + f->roles[1] = SUIT_MANIFEST_RAD_LOCAL_1; + + f->component_ids[2].value = component_radio_0x55000_0x3f000; + f->component_ids[2].len = sizeof(component_radio_0x55000_0x3f000); + f->roles[2] = SUIT_MANIFEST_RAD_LOCAL_2; + + f->component_ids[3].value = component_app_0x94000_0x6d000; + f->component_ids[3].len = sizeof(component_app_0x94000_0x6d000); + f->roles[3] = SUIT_MANIFEST_APP_LOCAL_1; + + f->component_ids[4].value = component_app_0x101000_0x60000; + f->component_ids[4].len = sizeof(component_app_0x101000_0x60000); + f->roles[4] = SUIT_MANIFEST_APP_LOCAL_2; + + f->count = 5; +} + +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 = test_fixture.count; + + 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 < test_fixture.count, "The idx value is larger than declared IPUC list"); + + *component_id = test_fixture.component_ids[idx]; + *role = test_fixture.roles[idx]; + + return SUIT_PLAT_SUCCESS; +} + +static suit_plat_err_t suit_ipuc_write_setup_fake_func(struct zcbor_string *component_id, + struct zcbor_string *encryption_info, + struct zcbor_string *compression_info) +{ + zassert_not_null(component_id, "Caller must provide non-Null pointer to read component_id"); + zassert_equal(encryption_info, test_fixture.exp_encryption_info, + "Unexpected encryption info structer passed"); + zassert_equal(compression_info, test_fixture.exp_compression_info, + "Unexpected compression info structer passed"); + zassert_true(test_fixture.exp_component_idx < test_fixture.count, + "Invalid test: expected idx value is larger than declared IPUC list"); + + struct zcbor_string *exp_component_id = + &test_fixture.component_ids[test_fixture.exp_component_idx]; + zassert_equal(component_id->len, exp_component_id->len, + "Invalid component ID length passed"); + zassert_equal(memcmp(component_id->value, exp_component_id->value, exp_component_id->len), + 0, "Invalid component ID value passed"); + + 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_true(test_fixture.exp_component_idx < test_fixture.count, + "Invalid test: expected idx value is larger than declared IPUC list"); + + struct zcbor_string *exp_component_id = + &test_fixture.component_ids[test_fixture.exp_component_idx]; + zassert_equal(component_id->len, exp_component_id->len, + "Invalid component ID length passed"); + zassert_equal(memcmp(component_id->value, exp_component_id->value, exp_component_id->len), + 0, "Invalid component ID value passed"); + + return SUIT_PLAT_SUCCESS; +} + +static void *test_suite_setup(void) +{ + return &test_fixture; +} + +static void test_before(void *f) +{ + struct flash_ipuc_tests_fixture *fixture = (struct flash_ipuc_tests_fixture *)f; + + /* Reset mocks */ + mock_suit_ipuc_reset(); + + /* Reset common FFF internal structures */ + FFF_RESET_HISTORY(); + + /* Reset test fixture */ + memset(fixture, 0, sizeof(*fixture)); +} + +ZTEST_SUITE(flash_ipuc_tests, NULL, test_suite_setup, test_before, NULL, NULL); + +ZTEST_F(flash_ipuc_tests, test_null_arg) +{ + uintptr_t mem_address = 0x1000; + size_t mem_size = 0x1000; + uintptr_t ipuc_address = 0x1000; + size_t ipuc_size = 0x1000; + uintptr_t min_address = 0; + + /* WHEN one of the required arguments is equal to NULL + */ + + /* THEN all IPUC availability check functions return false... */ + zassert_equal(flash_component_ipuc_check(NULL), false, + "IPUC component check for NULL component ID shall fail"); + zassert_equal(flash_cache_ipuc_check(min_address, NULL, &ipuc_size), false, + "IPUC cache check with NULL output address pointer shall fail"); + zassert_equal(flash_cache_ipuc_check(min_address, &ipuc_address, NULL), false, + "IPUC cache check with NULL output size pointer shall fail"); + + /* ... and all IPUC creation fails... */ + zassert_equal(flash_component_ipuc_create(NULL, NULL, NULL), NULL, + "IPUC component created for NULL component ID"); + zassert_equal(flash_cache_ipuc_create(min_address, NULL, &ipuc_size), NULL, + "IPUC cache created with NULL output address pointer"); + zassert_equal(flash_cache_ipuc_create(min_address, &ipuc_address, NULL), NULL, + "IPUC cache created with NULL output size pointer"); + + /* ... and no IPUC is found... */ + zassert_equal(flash_ipuc_find(mem_address, mem_size, NULL, &ipuc_size), NULL, + "IPUC cache found with NULL output address pointer"); + zassert_equal(flash_ipuc_find(mem_address, mem_size, &ipuc_address, NULL), NULL, + "IPUC cache found with NULL output size pointer"); + + /* ... and no IPUC SSF APIs are called... */ + zassert_equal(suit_ipuc_get_count_fake.call_count, 0, + "Incorrect number of suit_ipuc_get_count() calls"); + zassert_equal(suit_ipuc_get_info_fake.call_count, 0, + "Incorrect number of suit_ipuc_get_info() calls"); + zassert_equal(suit_ipuc_write_setup_fake.call_count, 0, + "Incorrect number of suit_ipuc_write_setup() calls"); + zassert_equal(suit_ipuc_write_fake.call_count, 0, + "Incorrect number of suit_ipuc_write() calls"); +} + +ZTEST_F(flash_ipuc_tests, test_component_check_OK) +{ + /* clang-format off */ + const uint8_t declared_component_id_cbor[] = { + 0x84, /* array(4) */ + 0x44, /* bstr(4) */ + 0x63, /* text(3) */ + 'M', 'E', 'M', + 0x41, /* bstr(1) */ + 0x03, /* Radio core - executable */ + 0x45, /* bstr(5) */ + 0x1a, /* uint32 */ + 0x00, 0x05, 0x40, 0x00, + 0x43, /* bstr(3) */ + 0x19, /* uint16 */ + 0x10, 0x00, + }; + /* clang-format on */ + struct zcbor_string test_component_id = { + .value = component_radio_0x54000_0x1000, + .len = sizeof(component_radio_0x54000_0x1000), + }; + + /* Set fakes, that return values from the test fixture. */ + 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; + + /* GIVEN a single radio manifest component IPUC is available */ + fixture->count = 1; + fixture->component_ids[0].value = declared_component_id_cbor; + fixture->component_ids[0].len = sizeof(declared_component_id_cbor); + fixture->roles[0] = SUIT_MANIFEST_RAD_LOCAL_1; + + /* WHEN the check for matching address is called */ + zassert_equal(flash_component_ipuc_check(&test_component_id), true, + "IPUC component check for component ID failed"); + + /* THEN the API returns true */ + /* ... and the IPUC was checked for component count */ + zassert_equal(suit_ipuc_get_count_fake.call_count, 1, + "Incorrect number of suit_ipuc_get_count() calls"); + /* ... and the IPUC with the first index was checked */ + zassert_equal(suit_ipuc_get_info_fake.call_count, 1, + "Incorrect number of suit_ipuc_get_info() calls"); + zassert_equal(suit_ipuc_get_info_fake.arg0_val, 0, + "Incorrect IPUC index value in suit_ipuc_get_info() call"); + + /* ... and the IPUC was not set up */ + zassert_equal(suit_ipuc_write_setup_fake.call_count, 0, + "Incorrect number of suit_ipuc_write_setup() calls"); + /* ... and the IPUC content was not modifed */ + zassert_equal(suit_ipuc_write_fake.call_count, 0, + "Incorrect number of suit_ipuc_write() calls"); +} + +ZTEST_F(flash_ipuc_tests, test_component_check_NOK_different_id) +{ + /* clang-format off */ + const uint8_t declared_component_id_cbor[] = { + 0x84, /* array(4) */ + 0x44, /* bstr(4) */ + 0x63, /* text(3) */ + 'M', 'E', 'M', + 0x41, /* bstr(1) */ + 0x03, /* Radio core - executable */ + 0x45, /* bstr(5) */ + 0x1a, /* uint32 */ + 0x00, 0x05, 0x40, 0x00, + 0x43, /* bstr(3) */ + 0x19, /* uint16 */ + 0x10, 0x10, + }; + /* clang-format on */ + struct zcbor_string test_component_id = { + .value = component_radio_0x54000_0x1000, + .len = sizeof(component_radio_0x54000_0x1000), + }; + + /* Set fakes, that return values from the test fixture. */ + 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; + + /* GIVEN a single radio manifest component IPUC is available */ + fixture->count = 1; + fixture->component_ids[0].value = declared_component_id_cbor; + fixture->component_ids[0].len = sizeof(declared_component_id_cbor); + fixture->roles[0] = SUIT_MANIFEST_RAD_LOCAL_1; + + /* WHEN the check for different address is called */ + zassert_equal(flash_component_ipuc_check(&test_component_id), false, + "IPUC component check for component ID did not fail"); + + /* THEN the API returns false */ + /* ... and the IPUC was checked for component count */ + zassert_equal(suit_ipuc_get_count_fake.call_count, 1, + "Incorrect number of suit_ipuc_get_count() calls"); + /* ... and the IPUC with the first index was checked */ + zassert_equal(suit_ipuc_get_info_fake.call_count, 1, + "Incorrect number of suit_ipuc_get_info() calls"); + zassert_equal(suit_ipuc_get_info_fake.arg0_val, 0, + "Incorrect IPUC index value in suit_ipuc_get_info() call"); + + /* ... and the IPUC was not set up */ + zassert_equal(suit_ipuc_write_setup_fake.call_count, 0, + "Incorrect number of suit_ipuc_write_setup() calls"); + /* ... and the IPUC content was not modifed */ + zassert_equal(suit_ipuc_write_fake.call_count, 0, + "Incorrect number of suit_ipuc_write() calls"); +} + +ZTEST_F(flash_ipuc_tests, test_component_create_OK) +{ + /* clang-format off */ + const uint8_t declared_component_id_cbor[] = { + 0x84, /* array(4) */ + 0x44, /* bstr(4) */ + 0x63, /* text(3) */ + 'M', 'E', 'M', + 0x41, /* bstr(1) */ + 0x03, /* Radio core - executable */ + 0x45, /* bstr(5) */ + 0x1a, /* uint32 */ + 0x00, 0x05, 0x40, 0x00, + 0x43, /* bstr(3) */ + 0x19, /* uint16 */ + 0x10, 0x00, + }; + /* clang-format on */ + struct zcbor_string test_component_id = { + .value = component_radio_0x54000_0x1000, + .len = sizeof(component_radio_0x54000_0x1000), + }; + struct device *flash_ipuc = NULL; + + /* Set fakes, that return values from the test fixture. */ + 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.custom_fake = suit_ipuc_write_setup_fake_func; + + /* GIVEN a single radio manifest component IPUC is available */ + fixture->count = 1; + fixture->component_ids[0].value = declared_component_id_cbor; + fixture->component_ids[0].len = sizeof(declared_component_id_cbor); + fixture->roles[0] = SUIT_MANIFEST_RAD_LOCAL_1; + /* ... and the test expects the first plaintext, uncompressed IPUC to be configured */ + fixture->exp_component_idx = 0; + fixture->exp_encryption_info = NULL; + fixture->exp_compression_info = NULL; + + /* WHEN the check for matching address is called */ + flash_ipuc = flash_component_ipuc_create(&test_component_id, NULL, NULL); + + /* THEN the API returns a valid IPUC driver */ + zassert_not_null(flash_ipuc, "IPUC component create for component ID failed"); + /* ... and the IPUC was checked for component count */ + zassert_equal(suit_ipuc_get_count_fake.call_count, 1, + "Incorrect number of suit_ipuc_get_count() calls"); + /* ... and the IPUC with the first index was checked */ + zassert_equal(suit_ipuc_get_info_fake.call_count, 1, + "Incorrect number of suit_ipuc_get_info() calls"); + zassert_equal(suit_ipuc_get_info_fake.arg0_val, 0, + "Incorrect IPUC index value in suit_ipuc_get_info() call"); + /* ... and the IPUC was set up for writing */ + zassert_equal(suit_ipuc_write_setup_fake.call_count, 1, + "Incorrect number of suit_ipuc_write_setup() calls"); + + /* ... and the IPUC content was not modifed */ + zassert_equal(suit_ipuc_write_fake.call_count, 0, + "Incorrect number of suit_ipuc_write() calls"); + + /* Release the IPUC */ + flash_ipuc_release(flash_ipuc); +} + +ZTEST_F(flash_ipuc_tests, test_component_create_NOK_different_id) +{ + /* clang-format off */ + const uint8_t declared_component_id_cbor[] = { + 0x84, /* array(4) */ + 0x44, /* bstr(4) */ + 0x63, /* text(3) */ + 'M', 'E', 'M', + 0x41, /* bstr(1) */ + 0x03, /* Radio core - executable */ + 0x45, /* bstr(5) */ + 0x1a, /* uint32 */ + 0x00, 0x05, 0x40, 0x00, + 0x45, /* bstr(5) */ + 0x1a, /* uint32 */ + 0x00, 0x05, 0x40, 0x00, + }; + /* clang-format on */ + struct zcbor_string test_component_id = { + .value = component_radio_0x54000_0x1000, + .len = sizeof(component_radio_0x54000_0x1000), + }; + struct device *flash_ipuc = NULL; + + /* Set fakes, that return values from the test fixture. */ + 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; + + /* GIVEN a single radio manifest component IPUC is available */ + fixture->count = 1; + fixture->component_ids[0].value = declared_component_id_cbor; + fixture->component_ids[0].len = sizeof(declared_component_id_cbor); + fixture->roles[0] = SUIT_MANIFEST_RAD_LOCAL_1; + + /* WHEN the check for different address is called */ + flash_ipuc = flash_component_ipuc_create(&test_component_id, NULL, NULL); + + /* THEN the API returns NULL */ + zassert_is_null(flash_ipuc, "IPUC component create for component ID failed"); + /* ... and the IPUC was checked for component count */ + zassert_equal(suit_ipuc_get_count_fake.call_count, 1, + "Incorrect number of suit_ipuc_get_count() calls"); + /* ... and the IPUC with the first index was checked */ + zassert_equal(suit_ipuc_get_info_fake.call_count, 1, + "Incorrect number of suit_ipuc_get_info() calls"); + zassert_equal(suit_ipuc_get_info_fake.arg0_val, 0, + "Incorrect IPUC index value in suit_ipuc_get_info() call"); + + /* ... and the IPUC was not set up */ + zassert_equal(suit_ipuc_write_setup_fake.call_count, 0, + "Incorrect number of suit_ipuc_write_setup() calls"); + /* ... and the IPUC content was not modifed */ + zassert_equal(suit_ipuc_write_fake.call_count, 0, + "Incorrect number of suit_ipuc_write() calls"); +} + +ZTEST_F(flash_ipuc_tests, test_driver_write_read_ro) +{ + struct zcbor_string test_component_id = { + .value = component_radio_0x54000_0x1000, + .len = sizeof(component_radio_0x54000_0x1000), + }; + struct device *flash_ipuc = NULL; + uint8_t sample_data[] = {0x1, 0x2, 0x3, 0x4}; + int ret = 0; + + /* Set fakes, that return values from the test fixture. */ + 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.custom_fake = suit_ipuc_write_setup_fake_func; + suit_ipuc_write_fake.custom_fake = suit_ipuc_write_fake_func; + + /* GIVEN all sample IPUCs are available */ + setup_sample_ipucs(fixture); + /* ... and the test expects the second plaintext, uncompressed IPUC to be configured */ + fixture->exp_component_idx = 1; + fixture->exp_encryption_info = NULL; + fixture->exp_compression_info = NULL; + /* ... and the radio IPUC is correctly setup */ + flash_ipuc = flash_component_ipuc_create(&test_component_id, NULL, NULL); + zassert_not_null(flash_ipuc, "IPUC component create for component ID failed"); + /* ... and the IPUC was checked for component count */ + zassert_equal(suit_ipuc_get_count_fake.call_count, 1, + "Incorrect number of suit_ipuc_get_count() calls"); + /* ... and the IPUC with the first two indexes was checked */ + zassert_equal(suit_ipuc_get_info_fake.call_count, 2, + "Incorrect number of suit_ipuc_get_info() calls"); + zassert_equal(suit_ipuc_get_info_fake.arg0_history[0], 0, + "Incorrect IPUC index value in suit_ipuc_get_info() call"); + zassert_equal(suit_ipuc_get_info_fake.arg0_history[1], 1, + "Incorrect IPUC index value in suit_ipuc_get_info() call"); + /* ... and the IPUC was set up for writing */ + zassert_equal(suit_ipuc_write_setup_fake.call_count, 1, + "Incorrect number of suit_ipuc_write_setup() calls"); + + for (size_t offset = 0; offset < 0x2000; offset += 0x300) { + /* WHEN the flash write API is called */ + ret = flash_write(flash_ipuc, offset, sample_data, sizeof(sample_data)); + + if (offset < 0x1000) { + /* ... and the offset is within the IPUC access range */ + /* THEN the memory is modified through IPUC API */ + zassert_equal(ret, 0, "Write inside IPUC area failed"); + zassert_equal(suit_ipuc_write_fake.call_count, offset / 0x300 + 1, + "Incorrect number of suit_ipuc_write() calls"); + zassert_equal(suit_ipuc_write_fake.arg1_val, offset, + "Incorrect offset passed through IPUC API"); + zassert_equal_ptr(suit_ipuc_write_fake.arg2_val, sample_data, + "Incorrect data passed through IPUC API"); + zassert_equal(suit_ipuc_write_fake.arg3_val, sizeof(sample_data), + "Incorrect data length passed through IPUC API"); + zassert_equal(suit_ipuc_write_fake.arg4_val, false, + "Incorrect last_chunk flag value passed through IPUC API"); + } else { + /* ... and the offset is not within the IPUC access range */ + /* THEN the memory is not modified through IPUC API */ + zassert_equal(ret, -ENOMEM, "Write outside of IPUC area did not fail"); + zassert_equal(suit_ipuc_write_fake.call_count, 0x1000 / 0x300 + 1, + "Incorrect number of suit_ipuc_write() calls"); + } + + /* .. and it is not possible to read back the data */ + ret = flash_read(flash_ipuc, offset, sample_data, sizeof(sample_data)); + zassert_equal(ret, -EACCES, "Read inside write-only IPUC area did not fail"); + } + + /* ... and it is possible to close the IPUC slot */ + ret = flash_write(flash_ipuc, 0, NULL, 0); + zassert_equal(ret, 0, "IPUC flush failed failed"); + zassert_equal(suit_ipuc_write_fake.call_count, 0x1000 / 0x300 + 2, + "Incorrect number of suit_ipuc_write() calls"); + zassert_equal(suit_ipuc_write_fake.arg1_val, 0, "Incorrect offset passed through IPUC API"); + zassert_equal_ptr(suit_ipuc_write_fake.arg2_val, NULL, + "Incorrect data passed through IPUC API"); + zassert_equal(suit_ipuc_write_fake.arg3_val, 0, + "Incorrect data length passed through IPUC API"); + zassert_equal(suit_ipuc_write_fake.arg4_val, true, + "Incorrect last_chunk flag value passed through IPUC API"); + + /* Release the IPUC */ + flash_ipuc_release(flash_ipuc); +} + +ZTEST_F(flash_ipuc_tests, test_driver_write_read_rw) +{ + struct zcbor_string test_component_id = { + .value = component_app_0x94000_0x6d000, + .len = sizeof(component_app_0x94000_0x6d000), + }; + struct device *flash_ipuc = NULL; + uint8_t sample_data[] = {0x1, 0x2, 0x3, 0x4}; + uint8_t read_buf[sizeof(sample_data)] = {0x00}; + int ret = 0; + + /* Set fakes, that return values from the test fixture. */ + 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.custom_fake = suit_ipuc_write_setup_fake_func; + suit_ipuc_write_fake.custom_fake = suit_ipuc_write_fake_func; + + /* GIVEN all sample IPUCs are available */ + setup_sample_ipucs(fixture); + /* ... and the test expects the third plaintext, uncompressed IPUC to be configured */ + fixture->exp_component_idx = 3; + fixture->exp_encryption_info = NULL; + fixture->exp_compression_info = NULL; + /* ... and the application IPUC is correctly setup */ + flash_ipuc = flash_component_ipuc_create(&test_component_id, NULL, NULL); + zassert_not_null(flash_ipuc, "IPUC component create for component ID failed"); + /* ... and the IPUC was checked for component count */ + zassert_equal(suit_ipuc_get_count_fake.call_count, 1, + "Incorrect number of suit_ipuc_get_count() calls"); + /* ... and the IPUC with the first 4 indexes were checked */ + zassert_equal(suit_ipuc_get_info_fake.call_count, 4, + "Incorrect number of suit_ipuc_get_info() calls"); + zassert_equal(suit_ipuc_get_info_fake.arg0_history[0], 0, + "Incorrect IPUC index value in suit_ipuc_get_info() call"); + zassert_equal(suit_ipuc_get_info_fake.arg0_history[1], 1, + "Incorrect IPUC index value in suit_ipuc_get_info() call"); + zassert_equal(suit_ipuc_get_info_fake.arg0_history[2], 2, + "Incorrect IPUC index value in suit_ipuc_get_info() call"); + zassert_equal(suit_ipuc_get_info_fake.arg0_history[3], 3, + "Incorrect IPUC index value in suit_ipuc_get_info() call"); + /* ... and the IPUC was set up for writing */ + zassert_equal(suit_ipuc_write_setup_fake.call_count, 1, + "Incorrect number of suit_ipuc_write_setup() calls"); + + for (size_t offset = 0; offset < 0x6d000; offset += 0x300) { + /* WHEN the flash write API is called */ + ret = flash_write(flash_ipuc, offset, sample_data, sizeof(sample_data)); + + if (offset < 0x6d000) { + /* ... and the offset is within the IPUC access range */ + /* THEN the memory is modified through IPUC API */ + zassert_equal(ret, 0, "Write inside IPUC area failed"); + zassert_equal(suit_ipuc_write_fake.call_count, offset / 0x300 + 1, + "Incorrect number of suit_ipuc_write() calls"); + zassert_equal(suit_ipuc_write_fake.arg1_val, offset, + "Incorrect offset passed through IPUC API"); + zassert_equal_ptr(suit_ipuc_write_fake.arg2_val, sample_data, + "Incorrect data passed through IPUC API"); + zassert_equal(suit_ipuc_write_fake.arg3_val, sizeof(sample_data), + "Incorrect data length passed through IPUC API"); + zassert_equal(suit_ipuc_write_fake.arg4_val, false, + "Incorrect last_chunk flag value passed through IPUC API"); + /* .. and it is possible to read back the data */ + ret = flash_read(flash_ipuc, offset, read_buf, sizeof(read_buf)); + zassert_equal(ret, 0, "Read inside readable IPUC area failed"); + } else { + /* ... and the offset is not within the IPUC access range */ + /* THEN the memory is not modified through IPUC API */ + zassert_equal(ret, -ENOMEM, "Write outside of IPUC area did not fail"); + zassert_equal(suit_ipuc_write_fake.call_count, 0x6d000 / 0x300 + 1, + "Incorrect number of suit_ipuc_write() calls"); + /* .. and it is not possible to read back the data */ + ret = flash_read(flash_ipuc, offset, sample_data, sizeof(sample_data)); + zassert_equal(ret, -ENOMEM, + "Read inside write-only IPUC area did not fail"); + } + } + + /* ... and it is possible to close the IPUC slot */ + ret = flash_write(flash_ipuc, 0, NULL, 0); + zassert_equal(ret, 0, "IPUC flush failed failed"); + zassert_equal(suit_ipuc_write_fake.call_count, 0x6d000 / 0x300 + 2, + "Incorrect number of suit_ipuc_write() calls"); + zassert_equal(suit_ipuc_write_fake.arg1_val, 0, "Incorrect offset passed through IPUC API"); + zassert_equal_ptr(suit_ipuc_write_fake.arg2_val, NULL, + "Incorrect data passed through IPUC API"); + zassert_equal(suit_ipuc_write_fake.arg3_val, 0, + "Incorrect data length passed through IPUC API"); + zassert_equal(suit_ipuc_write_fake.arg4_val, true, + "Incorrect last_chunk flag value passed through IPUC API"); + + /* Release the IPUC */ + flash_ipuc_release(flash_ipuc); +} + +ZTEST_F(flash_ipuc_tests, test_driver_erase_ro) +{ + struct zcbor_string test_component_id = { + .value = component_radio_0x54000_0x1000, + .len = sizeof(component_radio_0x54000_0x1000), + }; + struct device *flash_ipuc = NULL; + int ret = 0; + + /* Set fakes, that return values from the test fixture. */ + 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.custom_fake = suit_ipuc_write_setup_fake_func; + suit_ipuc_write_fake.custom_fake = suit_ipuc_write_fake_func; + + /* GIVEN all sample IPUCs are available */ + setup_sample_ipucs(fixture); + /* ... and the test expects the second plaintext, uncompressed IPUC to be configured */ + fixture->exp_component_idx = 1; + fixture->exp_encryption_info = NULL; + fixture->exp_compression_info = NULL; + /* ... and the radio IPUC is correctly setup */ + flash_ipuc = flash_component_ipuc_create(&test_component_id, NULL, NULL); + zassert_not_null(flash_ipuc, "IPUC component create for component ID failed"); + /* ... and the IPUC was checked for component count */ + zassert_equal(suit_ipuc_get_count_fake.call_count, 1, + "Incorrect number of suit_ipuc_get_count() calls"); + /* ... and the IPUC with the first two indexes was checked */ + zassert_equal(suit_ipuc_get_info_fake.call_count, 2, + "Incorrect number of suit_ipuc_get_info() calls"); + zassert_equal(suit_ipuc_get_info_fake.arg0_history[0], 0, + "Incorrect IPUC index value in suit_ipuc_get_info() call"); + zassert_equal(suit_ipuc_get_info_fake.arg0_history[1], 1, + "Incorrect IPUC index value in suit_ipuc_get_info() call"); + /* ... and the IPUC was set up for writing */ + zassert_equal(suit_ipuc_write_setup_fake.call_count, 1, + "Incorrect number of suit_ipuc_write_setup() calls"); + + /* WHEN the erase API is called */ + ret = flash_erase(flash_ipuc, 0, 0x1000); + + size_t write_blocks_passed = suit_ipuc_write_fake.call_count; + + /* THEN the memory is modified through IPUC API */ + zassert_equal(ret, 0, "Erase inside IPUC area failed"); + zassert_true(suit_ipuc_write_fake.call_count > 0, + "Incorrect number of suit_ipuc_write() calls"); + for (size_t i = 0; i < suit_ipuc_write_fake.call_count && i < FFF_ARG_HISTORY_LEN; i++) { + zassert_equal( + suit_ipuc_write_fake.arg1_history[i], 0x1000 / write_blocks_passed * i, + "Incorrect offset (0x%x != 0x%x) passed through IPUC API in iteration %d", + suit_ipuc_write_fake.arg1_history[i], 0x1000 / write_blocks_passed * i, i); + zassert_equal(suit_ipuc_write_fake.arg3_history[i], 0x1000 / write_blocks_passed, + "Incorrect data length passed through IPUC API"); + zassert_equal( + suit_ipuc_write_fake.arg4_history[i], false, + "Incorrect last_chunk flag value passed through IPUC API in iteration %d", + i); + } + + /* ... and it is possible to close the IPUC slot */ + ret = flash_write(flash_ipuc, 0, NULL, 0); + zassert_equal(ret, 0, "IPUC flush failed failed"); + zassert_equal(suit_ipuc_write_fake.call_count, write_blocks_passed + 1, + "Incorrect number of suit_ipuc_write() calls"); + zassert_equal(suit_ipuc_write_fake.arg1_val, 0, "Incorrect offset passed through IPUC API"); + zassert_equal_ptr(suit_ipuc_write_fake.arg2_val, NULL, + "Incorrect data passed through IPUC API"); + zassert_equal(suit_ipuc_write_fake.arg3_val, 0, + "Incorrect data length passed through IPUC API"); + zassert_equal(suit_ipuc_write_fake.arg4_val, true, + "Incorrect last_chunk flag value passed through IPUC API"); + + /* Release the IPUC */ + flash_ipuc_release(flash_ipuc); +} + +ZTEST_F(flash_ipuc_tests, test_cache_bank0_check_OK) +{ + uintptr_t ipuc_address = 0; + size_t ipuc_size = 0; + uintptr_t exp_address = 0; + size_t exp_size = 0; + uint8_t exp_cpu_id = 0; + struct zcbor_string exp_component_id = { + .value = component_app_0x94000_0x6d000, + .len = sizeof(component_app_0x94000_0x6d000), + }; + + /* Read the expected address and size fromt he component ID. */ + zassert_equal(suit_plat_decode_component_id(&exp_component_id, &exp_cpu_id, &exp_address, + &exp_size), + SUIT_PLAT_SUCCESS, "Unable to decode expected cache IPUC component ID"); + + /* Set fakes, that return values from the test fixture. */ + 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; + + /* GIVEN all sample IPUCs are available */ + setup_sample_ipucs(fixture); + + /* WHEN the check for any cache IPUC is called */ + zassert_equal(flash_cache_ipuc_check(0, &ipuc_address, &ipuc_size), true, + "Failed to check application cache IPUC"); + + /* THEN the API returns true */ + /* ... and the IPUC was checked for component count */ + zassert_equal(suit_ipuc_get_count_fake.call_count, 1, + "Incorrect number of suit_ipuc_get_count() calls"); + /* ... and all defined IPUCs were checked */ + zassert_equal(suit_ipuc_get_info_fake.call_count, fixture->count + 1, + "Incorrect number of suit_ipuc_get_info() calls (%d != %d)", + suit_ipuc_get_info_fake.call_count, fixture->count + 1); + for (size_t i = 0; i < fixture->count; i++) { + zassert_equal( + suit_ipuc_get_info_fake.arg0_history[i], i, + "Incorrect IPUC index value in suit_ipuc_get_info() call in iteration %d", + i); + } + + /* ... and the information about the largest application IPUC was returned */ + zassert_equal(ipuc_address, exp_address, "unexpected IPUC address returned (0x%x != 0x%x)", + ipuc_address, exp_address); + zassert_equal(ipuc_size, exp_size, "unexpected IPUC size returned (0x%x != 0x%x)", + ipuc_size, exp_size); + + /* ... and the IPUC was not set up */ + zassert_equal(suit_ipuc_write_setup_fake.call_count, 0, + "Incorrect number of suit_ipuc_write_setup() calls"); + /* ... and the IPUC content was not modifed */ + zassert_equal(suit_ipuc_write_fake.call_count, 0, + "Incorrect number of suit_ipuc_write() calls"); +} + +ZTEST_F(flash_ipuc_tests, test_cache_bank1_check_OK) +{ + uintptr_t ipuc_address = 0; + size_t ipuc_size = 0; + uintptr_t exp_address = 0; + size_t exp_size = 0; + uint8_t exp_cpu_id = 0; + struct zcbor_string exp_component_id = { + .value = component_app_0x101000_0x60000, + .len = sizeof(component_app_0x101000_0x60000), + }; + uintptr_t bank1_address = (uintptr_t)suit_plat_mem_ptr_get(0x100000); + + /* Read the expected address and size from the component ID. */ + zassert_equal(suit_plat_decode_component_id(&exp_component_id, &exp_cpu_id, &exp_address, + &exp_size), + SUIT_PLAT_SUCCESS, "Unable to decode expected cache IPUC component ID"); + + /* Set fakes, that return values from the test fixture. */ + 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; + + /* GIVEN all sample IPUCs are available */ + setup_sample_ipucs(fixture); + + /* WHEN the check for bank 1 cache is called */ + zassert_equal(flash_cache_ipuc_check(bank1_address, &ipuc_address, &ipuc_size), true, + "Failed to check application cache IPUC"); + + /* THEN the API returns true */ + /* ... and the IPUC was checked for component count */ + zassert_equal(suit_ipuc_get_count_fake.call_count, 1, + "Incorrect number of suit_ipuc_get_count() calls"); + /* ... and all defined IPUCs were checked */ + zassert_equal(suit_ipuc_get_info_fake.call_count, fixture->count + 1, + "Incorrect number of suit_ipuc_get_info() calls (%d != %d)", + suit_ipuc_get_info_fake.call_count, fixture->count + 1); + for (size_t i = 0; i < fixture->count; i++) { + zassert_equal( + suit_ipuc_get_info_fake.arg0_history[i], i, + "Incorrect IPUC index value in suit_ipuc_get_info() call in iteration %d", + i); + } + + /* ... and the information about the largest application IPUC on bank1 was returned */ + zassert_equal(ipuc_address, exp_address, "unexpected IPUC address returned (0x%x != 0x%x)", + ipuc_address, exp_address); + zassert_equal(ipuc_size, exp_size, "unexpected IPUC size returned (0x%x != 0x%x)", + ipuc_size, exp_size); + + /* ... and the IPUC was not set up */ + zassert_equal(suit_ipuc_write_setup_fake.call_count, 0, + "Incorrect number of suit_ipuc_write_setup() calls"); + /* ... and the IPUC content was not modifed */ + zassert_equal(suit_ipuc_write_fake.call_count, 0, + "Incorrect number of suit_ipuc_write() calls"); +} + +ZTEST_F(flash_ipuc_tests, test_cache_bank1_partial_check_OK) +{ + uintptr_t ipuc_address = 0; + size_t ipuc_size = 0; + uintptr_t exp_address = 0; + size_t exp_size = 0; + uint8_t exp_cpu_id = 0; + struct zcbor_string exp_component_id = { + .value = component_app_0x94000_0x6d000, + .len = sizeof(component_app_0x94000_0x6d000), + }; + uintptr_t bank1_address = (uintptr_t)suit_plat_mem_ptr_get(0x100000); + + /* Read the expected address and size from the component ID. */ + zassert_equal(suit_plat_decode_component_id(&exp_component_id, &exp_cpu_id, &exp_address, + &exp_size), + SUIT_PLAT_SUCCESS, "Unable to decode expected cache IPUC component ID"); + + /* Set fakes, that return values from the test fixture. */ + 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; + + /* GIVEN all sample IPUCs except one are available */ + setup_sample_ipucs(fixture); + fixture->count--; + + /* WHEN the check for bank 1 cache is called */ + zassert_equal(flash_cache_ipuc_check(bank1_address, &ipuc_address, &ipuc_size), true, + "Failed to check application cache IPUC"); + + /* THEN the API returns true */ + /* ... and the IPUC was checked for component count */ + zassert_equal(suit_ipuc_get_count_fake.call_count, 1, + "Incorrect number of suit_ipuc_get_count() calls"); + /* ... and all defined IPUCs were checked */ + zassert_equal(suit_ipuc_get_info_fake.call_count, fixture->count + 1, + "Incorrect number of suit_ipuc_get_info() calls (%d != %d)", + suit_ipuc_get_info_fake.call_count, fixture->count + 1); + for (size_t i = 0; i < fixture->count; i++) { + zassert_equal( + suit_ipuc_get_info_fake.arg0_history[i], i, + "Incorrect IPUC index value in suit_ipuc_get_info() call in iteration %d", + i); + } + + /* ... and the information about the largest application IPUC was returned */ + zassert_equal(ipuc_address, exp_address, "unexpected IPUC address returned (0x%x != 0x%x)", + ipuc_address, exp_address); + zassert_equal(ipuc_size, exp_size, "unexpected IPUC size returned (0x%x != 0x%x)", + ipuc_size, exp_size); + + /* ... and the IPUC was not set up */ + zassert_equal(suit_ipuc_write_setup_fake.call_count, 0, + "Incorrect number of suit_ipuc_write_setup() calls"); + /* ... and the IPUC content was not modifed */ + zassert_equal(suit_ipuc_write_fake.call_count, 0, + "Incorrect number of suit_ipuc_write() calls"); +} + +ZTEST_F(flash_ipuc_tests, test_cache_bank1_partial_create_OK) +{ + uintptr_t ipuc_address = 0; + size_t ipuc_size = 0; + uintptr_t exp_address = 0; + size_t exp_size = 0; + uint8_t exp_cpu_id = 0; + struct zcbor_string exp_component_id = { + .value = component_app_0x94000_0x6d000, + .len = sizeof(component_app_0x94000_0x6d000), + }; + uintptr_t bank1_address = (uintptr_t)suit_plat_mem_ptr_get(0x100000); + struct device *flash_ipuc = NULL; + + /* Read the expected address and size from the component ID. */ + zassert_equal(suit_plat_decode_component_id(&exp_component_id, &exp_cpu_id, &exp_address, + &exp_size), + SUIT_PLAT_SUCCESS, "Unable to decode expected cache IPUC component ID"); + + /* Set fakes, that return values from the test fixture. */ + 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; + + /* GIVEN all sample IPUCs except one are available */ + setup_sample_ipucs(fixture); + fixture->count--; + + /* WHEN the bank 1 cache IPUC is requested */ + flash_ipuc = flash_cache_ipuc_create(bank1_address, &ipuc_address, &ipuc_size); + + /* THEN the API returns a valid IPUC driver */ + zassert_not_null(flash_ipuc, "Cache IPUC create failed"); + + /* ... and the IPUC was checked for component count */ + zassert_equal(suit_ipuc_get_count_fake.call_count, 1, + "Incorrect number of suit_ipuc_get_count() calls"); + /* ... and all defined IPUCs were checked */ + zassert_equal(suit_ipuc_get_info_fake.call_count, fixture->count + 1, + "Incorrect number of suit_ipuc_get_info() calls (%d != %d)", + suit_ipuc_get_info_fake.call_count, fixture->count + 1); + for (size_t i = 0; i < fixture->count; i++) { + zassert_equal( + suit_ipuc_get_info_fake.arg0_history[i], i, + "Incorrect IPUC index value in suit_ipuc_get_info() call in iteration %d", + i); + } + + /* ... and the information about the largest application IPUC was returned */ + zassert_equal(ipuc_address, exp_address, "unexpected IPUC address returned (0x%x != 0x%x)", + ipuc_address, exp_address); + zassert_equal(ipuc_size, exp_size, "unexpected IPUC size returned (0x%x != 0x%x)", + ipuc_size, exp_size); + /* ... and the IPUC was set up for writing */ + zassert_equal(suit_ipuc_write_setup_fake.call_count, 1, + "Incorrect number of suit_ipuc_write_setup() calls"); + + /* ... and the IPUC content was not modifed */ + zassert_equal(suit_ipuc_write_fake.call_count, 0, + "Incorrect number of suit_ipuc_write() calls"); + + /* Release the IPUC */ + flash_ipuc_release(flash_ipuc); +} + +ZTEST_F(flash_ipuc_tests, test_find_ipuc) +{ + struct device *flash_ipuc = NULL; + struct zcbor_string radio_component_id = { + .value = component_radio_0x55000_0x3f000, + .len = sizeof(component_radio_0x55000_0x3f000), + }; + uintptr_t ipuc_address = 0; + size_t ipuc_size = 0; + uintptr_t cache_ipuc_address = 0; + size_t cache_ipuc_size = 0; + + /* Set fakes, that return values from the test fixture. */ + 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; + + /* GIVEN all sample IPUCs are available */ + setup_sample_ipucs(fixture); + + /* ... and two of them are configured */ + zassert_not_null(flash_component_ipuc_create(&radio_component_id, NULL, NULL), + "Failed to create radio IPUC before test execution"); + zassert_not_null(flash_cache_ipuc_create(0, &cache_ipuc_address, &cache_ipuc_size), + "Failed to create application cache IPUC before test execution"); + + /* WHEN component IPUC for unconfigured ID is searched */ + flash_ipuc = flash_ipuc_find((uintptr_t)suit_plat_mem_ptr_get(0x54000), 0x1000, + &ipuc_address, &ipuc_size); + /* THEN the API returns NULL */ + zassert_is_null(flash_ipuc, "Find for unconfigured IPUC shall fail"); + + /* WHEN component IPUC for address overlapping with configured ID is searched */ + flash_ipuc = flash_ipuc_find((uintptr_t)suit_plat_mem_ptr_get(0x55000 - 1), 0x3f000, + &ipuc_address, &ipuc_size); + /* THEN the API returns NULL */ + zassert_is_null(flash_ipuc, "Find for unconfigured IPUC shall fail"); + + /* WHEN component IPUC for matching address and too bigger size is searched */ + flash_ipuc = flash_ipuc_find((uintptr_t)suit_plat_mem_ptr_get(0x55000), 0x3f000 + 1, + &ipuc_address, &ipuc_size); + /* THEN the API returns NULL */ + zassert_is_null(flash_ipuc, "Find for unconfigured IPUC shall fail"); + + /* WHEN component IPUC for matching address and size is searched */ + flash_ipuc = flash_ipuc_find((uintptr_t)suit_plat_mem_ptr_get(0x55000), 0x3f000, + &ipuc_address, &ipuc_size); + /* THEN the API returns a valid IPUC driver */ + zassert_not_null(flash_ipuc, "Find for configured IPUC failed"); + /* ... and the returned address and size matches with the component ID */ + zassert_equal(ipuc_address, (uintptr_t)suit_plat_mem_ptr_get(0x55000), + "Component IPUC address does not match"); + zassert_equal(ipuc_size, 0x3f000, "Component IPUC size does not match"); + + /* WHEN component IPUC for address within configured ID is searched */ + flash_ipuc = flash_ipuc_find((uintptr_t)suit_plat_mem_ptr_get(0x55000 + 1), 0x1000, + &ipuc_address, &ipuc_size); + /* THEN the API returns a valid IPUC driver */ + zassert_not_null(flash_ipuc, "Find for configured IPUC failed"); + /* ... and the returned address and size matches with the component ID */ + zassert_equal(ipuc_address, (uintptr_t)suit_plat_mem_ptr_get(0x55000), + "Component IPUC address does not match"); + zassert_equal(ipuc_size, 0x3f000, "Component IPUC size does not match"); + + /* WHEN component IPUC for address returned by cache constructor is searched */ + flash_ipuc = + flash_ipuc_find(cache_ipuc_address, cache_ipuc_size, &ipuc_address, &ipuc_size); + /* THEN the API returns a valid IPUC driver */ + zassert_not_null(flash_ipuc, "Find for configured IPUC failed"); + /* ... and the returned address and size matches with cache region */ + zassert_equal(ipuc_address, cache_ipuc_address, "Cache IPUC address does not match"); + zassert_equal(ipuc_size, cache_ipuc_size, "Cache IPUC size does not match"); + + /* Release IPUCs */ + flash_ipuc = flash_ipuc_find((uintptr_t)suit_plat_mem_ptr_get(0x55000), 0x1000, + &ipuc_address, &ipuc_size); + flash_ipuc_release(flash_ipuc); + + flash_ipuc = + flash_ipuc_find(cache_ipuc_address, cache_ipuc_size, &ipuc_address, &ipuc_size); + flash_ipuc_release(flash_ipuc); +} diff --git a/tests/drivers/flash/flash_ipuc/testcase.yaml b/tests/drivers/flash/flash_ipuc/testcase.yaml new file mode 100644 index 000000000000..29aa9bfd49a1 --- /dev/null +++ b/tests/drivers/flash/flash_ipuc/testcase.yaml @@ -0,0 +1,16 @@ +common: + tags: + - drivers + - flash + - ci_tests_drivers_flash + - ci_tests_subsys_suit +tests: + drivers.flash.flash_ipuc: + platform_allow: + - nrf54h20dk/nrf54h20/cpuapp + - native_sim + - native_sim/native/64 + integration_platforms: + - nrf54h20dk/nrf54h20/cpuapp + - native_sim + - native_sim/native/64