diff options
6 files changed, 320 insertions, 16 deletions
diff --git a/interface/include/psa_manifest/sid.h b/interface/include/psa_manifest/sid.h index 4b66a3e9b1..6ee681b833 100644 --- a/interface/include/psa_manifest/sid.h +++ b/interface/include/psa_manifest/sid.h @@ -137,8 +137,10 @@ extern "C" { #define TFM_SECURE_CLIENT_2_VERSION (1U) /******** TFM_SP_MULTI_CORE_TEST ********/ -#define MULTI_CORE_TEST_DUMMY_SID (0x0000F100U) -#define MULTI_CORE_TEST_DUMMY_VERSION (1U) +#define MULTI_CORE_MULTI_CLIENT_CALL_TEST_0_SID (0x0000F100U) +#define MULTI_CORE_MULTI_CLIENT_CALL_TEST_0_VERSION (1U) +#define MULTI_CORE_MULTI_CLIENT_CALL_TEST_1_SID (0x0000F101U) +#define MULTI_CORE_MULTI_CLIENT_CALL_TEST_1_VERSION (1U) #ifdef __cplusplus } diff --git a/secure_fw/services/tfm_service_list.inc b/secure_fw/services/tfm_service_list.inc index e3bc1a3e84..539dada747 100644 --- a/secure_fw/services/tfm_service_list.inc +++ b/secure_fw/services/tfm_service_list.inc @@ -515,14 +515,23 @@ const struct tfm_spm_service_db_t service_db[] = #ifdef TFM_MULTI_CORE_TEST /******** TFM_SP_MULTI_CORE_TEST ********/ { - .name = "MULTI_CORE_TEST_DUMMY", + .name = "MULTI_CORE_MULTI_CLIENT_CALL_TEST_0", .partition_id = TFM_SP_MULTI_CORE_TEST, - .signal = MULTI_CORE_TEST_DUMMY_SIGNAL, + .signal = MULTI_CORE_MULTI_CLIENT_CALL_TEST_0_SIGNAL, .sid = 0x0000F100, .non_secure_client = true, .version = 1, .version_policy = TFM_VERSION_POLICY_STRICT }, + { + .name = "MULTI_CORE_MULTI_CLIENT_CALL_TEST_1", + .partition_id = TFM_SP_MULTI_CORE_TEST, + .signal = MULTI_CORE_MULTI_CLIENT_CALL_TEST_1_SIGNAL, + .sid = 0x0000F101, + .non_secure_client = true, + .version = 1, + .version_policy = TFM_VERSION_POLICY_STRICT + }, #endif /* TFM_MULTI_CORE_TEST */ }; @@ -929,6 +938,13 @@ struct tfm_spm_service_t service[] = .msg_queue = {0}, .list = {0}, }, + { + .service_db = NULL, + .partition = NULL, + .handle_list = {0}, + .msg_queue = {0}, + .list = {0}, + }, #endif /* TFM_MULTI_CORE_TEST */ }; diff --git a/test/suites/multi_core/non_secure/multi_core_ns_interface_testsuite.c b/test/suites/multi_core/non_secure/multi_core_ns_interface_testsuite.c index 38929c83e8..14ab0d4c69 100644 --- a/test/suites/multi_core/non_secure/multi_core_ns_interface_testsuite.c +++ b/test/suites/multi_core/non_secure/multi_core_ns_interface_testsuite.c @@ -5,16 +5,62 @@ * */ +#include <stdbool.h> +#include <stdint.h> +#include "os_wrapper/mutex.h" +#include "os_wrapper/thread.h" #include "psa/client.h" #include "psa_manifest/sid.h" #include "test/framework/test_framework_helpers.h" +#include "tfm_ns_mailbox.h" + +/* Max number of child threads for multiple outstanding PSA client call test */ +#define NR_MULTI_CALL_CHILD (NUM_MAILBOX_QUEUE_SLOT * 2) + +/* The event flag to sync up between parent thread and child threads */ +#define TEST_CHILD_EVENT_FLAG(x) (uint32_t)(0x1UL << (x)) + +/* Max number of test rounds */ +#define MAX_NR_LIGHT_TEST_ROUND 0x200 + +/* Default stack size for child thread */ +#define MULTI_CALL_LIGHT_TEST_STACK_SIZE 0x200 + +/* Structure passed to test threads */ +struct test_params { + void *parent_handle; /* The thread handle of parent thread */ + uint32_t child_idx; /* The index of current child thread */ + uint32_t nr_rounds; /* The number of test rounds */ + void *mutex_handle; /* Mutex to protect is_complete flag */ + enum test_status_t ret; /* The test result */ + bool is_complete; /* Whether current test thread completes */ + bool is_parent; /* Whether executed in parent thread */ +}; + +/* Multiple outstanding PSA client call test secure service SID and version */ +struct multi_call_service_info { + uint32_t sid; + uint32_t version; +}; + +/* + * If not enough secure services are defined for multiple outstanding PSA + * client call test, the definitions below will trigger compiling error. + */ +const static struct multi_call_service_info multi_call_service_list[] = { + {MULTI_CORE_MULTI_CLIENT_CALL_TEST_0_SID, + MULTI_CORE_MULTI_CLIENT_CALL_TEST_0_VERSION}, + {MULTI_CORE_MULTI_CLIENT_CALL_TEST_1_SID, + MULTI_CORE_MULTI_CLIENT_CALL_TEST_1_VERSION} +}; /* List of tests */ -static void tfm_multi_core_dummy_test(struct test_result_t *ret); +static void multi_client_call_light_test(struct test_result_t *ret); static struct test_t multi_core_tests[] = { - {&tfm_multi_core_dummy_test, "TFM_MULTI_CORE_TEST_DUMMY_TEST", - "A dummy test in multi-core test service", {0}}, + {&multi_client_call_light_test, + "MULTI_CLIENT_CALL_LIGHT_TEST", + "Multiple outstanding NS PSA client calls lightweight test", {0}}, }; void register_testsuite_multi_core_ns_interface( @@ -28,12 +74,187 @@ void register_testsuite_multi_core_ns_interface( multi_core_tests, list_size, p_test_suite); } -/** - * \brief A dummy test case. It will be replaced by actual test cases. - */ -static void tfm_multi_core_dummy_test(struct test_result_t *ret) +static void wait_child_thread_completion(struct test_params *params_array, + uint8_t child_idx) +{ + bool is_complete; + uint8_t i; + void *mutex = params_array[0].mutex_handle; + + for (i = 0; i < child_idx; i++) { + while (1) { + os_wrapper_mutex_acquire(mutex, OS_WRAPPER_WAIT_FOREVER); + is_complete = params_array[i].is_complete; + os_wrapper_mutex_release(mutex); + + if (is_complete) { + break; + } + } + } +} + +static void multi_client_call_test(struct test_result_t *ret, + os_wrapper_thread_func test_runner, + int32_t stack_size, + int32_t nr_rounds) { - TEST_LOG("A fake test and of course it succeeds\r\n"); + uint8_t i, nr_child; + void *current_thread_handle; + uint32_t current_thread_priority, err; + void *mutex_handle; + void *child_ids[NR_MULTI_CALL_CHILD]; + struct test_params parent_params, params[NR_MULTI_CALL_CHILD]; + + current_thread_handle = os_wrapper_thread_get_handle(); + if (!current_thread_handle) { + TEST_FAIL("Failed to get current thread ID\r\n"); + return; + } + + err = os_wrapper_thread_get_priority(current_thread_handle, + ¤t_thread_priority); + if (err == OS_WRAPPER_ERROR) { + TEST_FAIL("Failed to get current thread priority\r\n"); + return; + } + + /* + * Create a mutex to protect the synchronization between child test thread + * about the completion status. + * The best way is to use os_wrapper_thread_wait/set_flag(). However, due to + * the implementation of the wait event functions in some RTOS, if the + * child test threads already exit before the main thread starts to wait for + * event (main thread itself has to perform test too), the main thread + * cannot receive the event flags. + * As a result, use a flag and a mutex to make sure the main thread can + * capture the completion event of child threads. + */ + mutex_handle = os_wrapper_mutex_create(); + if (!mutex_handle) { + TEST_FAIL("Failed to create a mutex\r\n"); + return; + } + + /* Create test threads one by one */ + for (i = 0; i < NR_MULTI_CALL_CHILD; i++) { + params[i].parent_handle = current_thread_handle; + params[i].child_idx = i; + params[i].nr_rounds = nr_rounds; + params[i].mutex_handle = mutex_handle; + params[i].is_complete = false; + params[i].is_parent = false; + + child_ids[i] = os_wrapper_thread_new(NULL, + stack_size, + test_runner, + ¶ms[i], + current_thread_priority); + if (!child_ids[i]) { + break; + } + } + + nr_child = i; + TEST_LOG("Totally %d threads for test start\r\n", nr_child + 1); + TEST_LOG("Each thread run 0x%x rounds tests\r\n", nr_rounds); + + /* + * Activate test threads one by one. + * Try to make test threads to run together. + */ + for (i = 0; i < nr_child; i++) { + os_wrapper_thread_set_flag(child_ids[i], TEST_CHILD_EVENT_FLAG(i)); + } + + /* Use current thread to execute a test instance */ + parent_params.child_idx = nr_child; + parent_params.nr_rounds = nr_rounds; + parent_params.is_parent = true; + test_runner(&parent_params); + + /* Wait for all the test threads completes */ + wait_child_thread_completion(params, nr_child); + + os_wrapper_mutex_delete(mutex_handle); + + if (parent_params.ret != TEST_PASSED) { + ret->val = TEST_FAILED; + return; + } + + /* Check the test result of each child thread */ + for (i = 0; i < nr_child; i++) { + if (params[i].ret != TEST_PASSED) { + ret->val = TEST_FAILED; + return; + } + } ret->val = TEST_PASSED; } + +static inline enum test_status_t multi_client_call_light_loop(uint8_t child_idx, + uint32_t nr_rounds) +{ + psa_handle_t handle; + psa_status_t status; + uint32_t i, nr_calls, nr_service, sid, version; + struct psa_outvec outvec = {&nr_calls, sizeof(nr_calls)}; + + nr_service = sizeof(multi_call_service_list) / + sizeof(multi_call_service_list[0]); + /* Determine the secure service ID and version */ + sid = multi_call_service_list[child_idx % nr_service].sid; + version = multi_call_service_list[child_idx % nr_service].version; + + for (i = 0; i < nr_rounds; i++) { + handle = psa_connect(sid, version); + if (handle <= 0) { + TEST_LOG("Fail to connect test service!\r\n"); + return TEST_FAILED; + } + + status = psa_call(handle, PSA_IPC_CALL, NULL, 0, &outvec, 1); + if (status < 0) { + TEST_LOG("Fail to call test service\r\n"); + return TEST_FAILED; + } + + psa_close(handle); + } + + return TEST_PASSED; +} + +static void multi_client_call_light_runner(void *argument) +{ + struct test_params *params = (struct test_params *)argument; + + if (!params->is_parent) { + /* Wait for the signal to kick-off the test */ + os_wrapper_thread_wait_flag(TEST_CHILD_EVENT_FLAG(params->child_idx), + OS_WRAPPER_WAIT_FOREVER); + } + + params->ret = multi_client_call_light_loop(params->child_idx, + params->nr_rounds); + + if (!params->is_parent) { + /* Mark this child thread has completed */ + os_wrapper_mutex_acquire(params->mutex_handle, OS_WRAPPER_WAIT_FOREVER); + params->is_complete = true; + os_wrapper_mutex_release(params->mutex_handle); + } +} + +/** + * \brief Lightweight test case to verify multiple outstanding PSA client calls + * feature. + */ +static void multi_client_call_light_test(struct test_result_t *ret) +{ + multi_client_call_test(ret, multi_client_call_light_runner, + MULTI_CALL_LIGHT_TEST_STACK_SIZE, + MAX_NR_LIGHT_TEST_ROUND); +} diff --git a/test/test_services/tfm_multi_core_test/psa_manifest/tfm_multi_core_test.h b/test/test_services/tfm_multi_core_test/psa_manifest/tfm_multi_core_test.h index a4b9cb24db..1692715f42 100644 --- a/test/test_services/tfm_multi_core_test/psa_manifest/tfm_multi_core_test.h +++ b/test/test_services/tfm_multi_core_test/psa_manifest/tfm_multi_core_test.h @@ -14,7 +14,8 @@ extern "C" { #endif -#define MULTI_CORE_TEST_DUMMY_SIGNAL (1U << (0 + 4)) +#define MULTI_CORE_MULTI_CLIENT_CALL_TEST_0_SIGNAL (1U << (0 + 4)) +#define MULTI_CORE_MULTI_CLIENT_CALL_TEST_1_SIGNAL (1U << (1 + 4)) #ifdef __cplusplus } diff --git a/test/test_services/tfm_multi_core_test/tfm_multi_core_test.c b/test/test_services/tfm_multi_core_test/tfm_multi_core_test.c index 9d371fcb9e..fe2238ee2f 100644 --- a/test/test_services/tfm_multi_core_test/tfm_multi_core_test.c +++ b/test/test_services/tfm_multi_core_test/tfm_multi_core_test.c @@ -9,10 +9,66 @@ #include "psa/service.h" #include "psa_manifest/tfm_multi_core_test.h" +static uint32_t nr_psa_call; + +/* + * Fixme: Temporarily implement abort as infinite loop, + * will replace it later. + */ +static void tfm_abort(void) +{ + while (1) + ; +} + +static void multi_core_multi_client_call_test(uint32_t signal) +{ + psa_msg_t msg; + psa_status_t status; + + status = psa_get(signal, &msg); + if (status != PSA_SUCCESS) { + return; + } + + switch(msg.type) { + case PSA_IPC_CONNECT: + psa_reply(msg.handle, PSA_SUCCESS); + break; + case PSA_IPC_CALL: + nr_psa_call++; + /* Write current number of calls to outvec. */ + psa_write(msg.handle, 0, &nr_psa_call, sizeof(nr_psa_call)); + psa_reply(msg.handle, PSA_SUCCESS); + break; + case PSA_IPC_DISCONNECT: + psa_reply(msg.handle, PSA_SUCCESS); + break; + default: + /* Unsupported operations */ + tfm_abort(); + } +} + /* Test thread */ void multi_core_test_main(void *param) { + uint32_t signals = 0; + (void)param; - /* Do nothing */ + while (1) { + signals = psa_wait(PSA_WAIT_ANY, PSA_BLOCK); + + if (signals & MULTI_CORE_MULTI_CLIENT_CALL_TEST_0_SIGNAL) { + multi_core_multi_client_call_test( + MULTI_CORE_MULTI_CLIENT_CALL_TEST_0_SIGNAL); + } else if (signals & MULTI_CORE_MULTI_CLIENT_CALL_TEST_1_SIGNAL) { + multi_core_multi_client_call_test( + MULTI_CORE_MULTI_CLIENT_CALL_TEST_1_SIGNAL); + } else { + /* Should not come here */ + tfm_abort(); + } + } } diff --git a/test/test_services/tfm_multi_core_test/tfm_multi_core_test.yaml b/test/test_services/tfm_multi_core_test/tfm_multi_core_test.yaml index 3d2df093bd..43142f4afa 100644 --- a/test/test_services/tfm_multi_core_test/tfm_multi_core_test.yaml +++ b/test/test_services/tfm_multi_core_test/tfm_multi_core_test.yaml @@ -17,9 +17,17 @@ ], "services" : [ { - "name": "MULTI_CORE_TEST_DUMMY", + "name": "MULTI_CORE_MULTI_CLIENT_CALL_TEST_0", "sid": "0x0000F100", - "signal": "MULTI_CORE_TEST_DUMMY_SIGNAL", + "signal": "MULTI_CORE_MULTI_CLIENT_CALL_TEST_0_SIGNAL", + "non_secure_clients": true, + "minor_version": 1, + "minor_policy": "STRICT" + }, + { + "name": "MULTI_CORE_MULTI_CLIENT_CALL_TEST_1", + "sid": "0x0000F101", + "signal": "MULTI_CORE_MULTI_CLIENT_CALL_TEST_1_SIGNAL", "non_secure_clients": true, "minor_version": 1, "minor_policy": "STRICT" |