TF-M Reference Manual  1.2.0
TrustedFirmware-M
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
multi_core_ns_interface_testsuite.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  *
6  */
7 
8 #include <stdbool.h>
9 #include <stdint.h>
10 #include "os_wrapper/mutex.h"
11 #include "os_wrapper/thread.h"
12 #include "os_wrapper/tick.h"
13 #include "psa/client.h"
15 #include "psa_manifest/sid.h"
16 #include "test_framework_helpers.h"
17 #include "tfm_ns_mailbox.h"
18 
19 #ifdef TFM_MULTI_CORE_MULTI_CLIENT_CALL
20 /* Max number of child threads for multiple outstanding PSA client call test */
21 #define NR_MULTI_CALL_CHILD (NUM_MAILBOX_QUEUE_SLOT * 2)
22 #else
23 #define NR_MULTI_CALL_CHILD 0
24 #endif
25 
26 /* The event flag to sync up between parent thread and child threads */
27 #define TEST_CHILD_EVENT_FLAG(x) (uint32_t)(0x1UL << (x))
28 
29 /* Max number of test rounds */
30 #define MAX_NR_LIGHT_TEST_ROUND 0x200
31 #define MAX_NR_HEAVY_TEST_ROUND 0x20
32 
33 /* Default stack size for child thread */
34 #define MULTI_CALL_LIGHT_TEST_STACK_SIZE 0x200
35 #define MULTI_CALL_HEAVY_TEST_STACK_SIZE 0x300
36 
37 /* Test UID copied from ITS test cases */
38 #define TEST_UID_1 2U
39 /* ITS data for multiple PSA client call heavy tests */
40 #define ITS_DATA "ITSDataForMultiCore"
41 #define ITS_DATA_LEN sizeof(ITS_DATA)
42 
43 /* Structure passed to test threads */
44 struct test_params {
45  void *parent_handle; /* The thread handle of parent thread */
46  uint32_t child_idx; /* The index of current child thread */
47  uint32_t nr_rounds; /* The number of test rounds */
48  uint32_t nr_calls; /* The number of PSA client calls */
49  void *mutex_handle; /* Mutex to protect is_complete flag */
50  enum test_status_t ret; /* The test result */
51  uint32_t total_ticks; /* The total ticks cost to complete tests */
52  bool is_complete; /* Whether current test thread completes */
53  bool is_parent; /* Whether executed in parent thread */
54 };
55 
56 /* List of tests */
57 static void multi_client_call_light_test(struct test_result_t *ret);
58 static void multi_client_call_heavy_test(struct test_result_t *ret);
59 static void multi_client_call_ooo_test(struct test_result_t *ret);
60 
61 static struct test_t multi_core_tests[] = {
62  {&multi_client_call_light_test,
63  "MULTI_CLIENT_CALL_LIGHT_TEST",
64  "Multiple outstanding NS PSA client calls lightweight test",
65  {TEST_PASSED}},
66  {&multi_client_call_heavy_test,
67  "MULTI_CLIENT_CALL_HEAVY_TEST",
68  "Multiple outstanding NS PSA client calls heavyweight test",
69  {TEST_PASSED}},
70  {&multi_client_call_ooo_test,
71  "MULTI_CLIENT_CALL_OOO_TEST",
72  "Multiple outstanding NS PSA client calls test with out-of-order calls",
73  {TEST_PASSED}},
74 };
75 
77  struct test_suite_t *p_test_suite)
78 {
79  uint32_t list_size;
80 
81  if (!sizeof(multi_core_tests)) {
82  return;
83  }
84 
85  list_size = (sizeof(multi_core_tests) / sizeof(multi_core_tests[0]));
86 
87  set_testsuite("TF-M test cases for multi-core topology",
88  multi_core_tests, list_size, p_test_suite);
89 }
90 
91 static void wait_child_thread_completion(struct test_params *params_array,
92  uint8_t child_idx)
93 {
94  bool is_complete;
95  uint8_t i;
96  void *mutex = params_array[0].mutex_handle;
97 
98  for (i = 0; i < child_idx; i++) {
99  while (1) {
101  is_complete = params_array[i].is_complete;
103 
104  if (is_complete) {
105  break;
106  }
107  }
108  }
109 }
110 
111 static void multi_client_call_test(struct test_result_t *ret,
112  os_wrapper_thread_func test_runner,
113  int32_t stack_size,
114  int32_t nr_rounds,
115  bool is_mixed)
116 {
117  uint8_t i, nr_child;
118  void *current_thread_handle;
119  uint32_t current_thread_priority, err, total_ticks, total_calls, avg_ticks;
120  void *mutex_handle;
121  void *child_ids[NR_MULTI_CALL_CHILD];
122  struct ns_mailbox_stats_res_t stats_res;
123  struct test_params parent_params, params[NR_MULTI_CALL_CHILD];
124 
125  tfm_ns_mailbox_tx_stats_init();
126 
127  current_thread_handle = os_wrapper_thread_get_handle();
128  if (!current_thread_handle) {
129  TEST_FAIL("Failed to get current thread ID\r\n");
130  return;
131  }
132 
133  err = os_wrapper_thread_get_priority(current_thread_handle,
134  &current_thread_priority);
135  if (err == OS_WRAPPER_ERROR) {
136  TEST_FAIL("Failed to get current thread priority\r\n");
137  return;
138  }
139 
140  /*
141  * Create a mutex to protect the synchronization between child test thread
142  * about the completion status.
143  * The best way is to use os_wrapper_thread_wait/set_flag(). However, due to
144  * the implementation of the wait event functions in some RTOS, if the
145  * child test threads already exit before the main thread starts to wait for
146  * event (main thread itself has to perform test too), the main thread
147  * cannot receive the event flags.
148  * As a result, use a flag and a mutex to make sure the main thread can
149  * capture the completion event of child threads.
150  */
151  mutex_handle = os_wrapper_mutex_create();
152  if (!mutex_handle) {
153  TEST_FAIL("Failed to create a mutex\r\n");
154  return;
155  }
156 
157  /* Create test threads one by one */
158  for (i = 0; i < NR_MULTI_CALL_CHILD; i++) {
159  params[i].parent_handle = current_thread_handle;
160  params[i].child_idx = i;
161  params[i].nr_rounds = nr_rounds;
162  params[i].mutex_handle = mutex_handle;
163  params[i].is_complete = false;
164  params[i].is_parent = false;
165 
166  child_ids[i] = os_wrapper_thread_new(NULL,
167  stack_size,
168  test_runner,
169  &params[i],
170  current_thread_priority);
171  if (!child_ids[i]) {
172  break;
173  }
174  }
175 
176  nr_child = i;
177  TEST_LOG("Totally %d threads for test start\r\n", nr_child + 1);
178  if (!is_mixed) {
179  TEST_LOG("Each thread run 0x%x rounds tests\r\n", nr_rounds);
180  }
181 
182  /*
183  * Activate test threads one by one.
184  * Try to make test threads to run together.
185  */
186  for (i = 0; i < nr_child; i++) {
188  }
189 
190  /* Use current thread to execute a test instance */
191  parent_params.child_idx = nr_child;
192  parent_params.nr_rounds = nr_rounds;
193  parent_params.is_parent = true;
194  test_runner(&parent_params);
195 
196  /* Wait for all the test threads completes */
197  wait_child_thread_completion(params, nr_child);
198 
199  os_wrapper_mutex_delete(mutex_handle);
200 
201  if (parent_params.ret != TEST_PASSED) {
202  ret->val = TEST_FAILED;
203  return;
204  }
205 
206  total_ticks = parent_params.total_ticks;
207  total_calls = parent_params.nr_calls;
208 
209  /* Check the test result of each child thread */
210  for (i = 0; i < nr_child; i++) {
211  if (params[i].ret != TEST_PASSED) {
212  ret->val = TEST_FAILED;
213  return;
214  }
215 
216  total_ticks += params[i].total_ticks;
217  total_calls += params[i].nr_calls;
218  }
219 
220  tfm_ns_mailbox_stats_avg_slot(&stats_res);
221  TEST_LOG("Totally %d NS mailbox queue slots\r\n", NUM_MAILBOX_QUEUE_SLOT);
222  TEST_LOG("%d.%d NS mailbox queue slots are occupied each time in average.\r\n",
223  stats_res.avg_nr_slots, stats_res.avg_nr_slots_tenths);
224 
225  TEST_LOG("Cost %d ticks totally\r\n", total_ticks);
226  avg_ticks = total_ticks / total_calls;
227  total_ticks %= total_calls;
228  TEST_LOG("Each PSA client call cost %d.%d ticks in average\r\n", avg_ticks,
229  total_ticks * 10 / total_calls);
230 
231  ret->val = TEST_PASSED;
232 }
233 
234 static inline
235 enum test_status_t multi_client_call_light_loop(struct test_params *params)
236 {
237  uint32_t i, version, total_ticks;
238  uint32_t nr_rounds = params->nr_rounds;
239 
240  total_ticks = os_wrapper_get_tick();
241 
242  for (i = 0; i < nr_rounds; i++) {
243  version = psa_framework_version();
244  if (version != PSA_FRAMEWORK_VERSION) {
245  TEST_LOG("Incorrect PSA framework version!\r\n");
246  return TEST_FAILED;
247  }
248  }
249 
251  params->nr_calls = nr_rounds;
252 
253  return TEST_PASSED;
254 }
255 
256 static void multi_client_call_light_runner(void *argument)
257 {
258  struct test_params *params = (struct test_params *)argument;
259 
260  if (!params->is_parent) {
261  /* Wait for the signal to kick-off the test */
264  }
265 
266  params->ret = multi_client_call_light_loop(params);
267 
268  if (!params->is_parent) {
269  /* Mark this child thread has completed */
271  params->is_complete = true;
273  }
274 }
275 
280 static void multi_client_call_light_test(struct test_result_t *ret)
281 {
282  multi_client_call_test(ret, multi_client_call_light_runner,
285  false);
286 }
287 
288 static inline
289 enum test_status_t multi_client_call_heavy_loop(const psa_storage_uid_t uid,
290  struct test_params *params)
291 {
292  uint32_t i, total_ticks, rounds = params->nr_rounds;
293  psa_status_t status;
294  size_t rd_data_len;
295  char rd_data[ITS_DATA_LEN];
297 
298  total_ticks = os_wrapper_get_tick();
299 
300  for (i = 0; i < rounds; i++) {
301  /* Set a data in the asset */
302  status = psa_its_set(uid, ITS_DATA_LEN, ITS_DATA, flags);
303  if (status != PSA_SUCCESS) {
304  TEST_LOG("Fail to write ITS asset\r\n");
305  return TEST_FAILED;
306  }
307 
308  /* Get data from the asset */
309  status = psa_its_get(uid, 0, ITS_DATA_LEN, rd_data, &rd_data_len);
310  if (status != PSA_SUCCESS) {
311  TEST_LOG("Fail to read ITS asset\r\n");
312  return TEST_FAILED;
313  }
314 
315  /* Remove the asset to clean up storage */
316  status = psa_its_remove(uid);
317  if (status != PSA_SUCCESS) {
318  TEST_LOG("Fail to remove ITS asset\r\n");
319  return TEST_FAILED;
320  }
321  }
322 
324  params->nr_calls = rounds * 3;
325 
326  return TEST_PASSED;
327 }
328 
329 static void multi_client_call_heavy_runner(void *argument)
330 {
331  struct test_params *params = (struct test_params *)argument;
332  const psa_storage_uid_t uid = TEST_UID_1 + params->child_idx;
333 
334  if (!params->is_parent) {
335  /* Wait for the signal to kick-off the test */
338  }
339 
340  params->ret = multi_client_call_heavy_loop(uid, params);
341 
342  if (!params->is_parent) {
343  /* Mark this child thread has completed */
345  params->is_complete = true;
347  }
348 }
349 
354 static void multi_client_call_heavy_test(struct test_result_t *ret)
355 {
356  multi_client_call_test(ret, multi_client_call_heavy_runner,
359  false);
360 }
361 
362 static void multi_client_call_ooo_runner(void *argument)
363 {
364  struct test_params *params = (struct test_params *)argument;
365  const psa_storage_uid_t uid = TEST_UID_1 + params->child_idx;
366 
367  if (!params->is_parent) {
368  /* Wait for the signal to kick-off the test */
371  }
372 
373  if (!params->child_idx % 2) {
374  params->ret = multi_client_call_heavy_loop(uid, params);
375  } else {
376  params->nr_rounds *= 20;
377  params->ret = multi_client_call_light_loop(params);
378  }
379 
380  if (!params->is_parent) {
381  /* Mark this child thread has completed */
383  params->is_complete = true;
385  }
386 }
387 
392 static void multi_client_call_ooo_test(struct test_result_t *ret)
393 {
394  multi_client_call_test(ret, multi_client_call_ooo_runner,
397  true);
398 }
#define MULTI_CALL_LIGHT_TEST_STACK_SIZE
void * os_wrapper_thread_new(const char *name, int32_t stack_size, os_wrapper_thread_func func, void *arg, uint32_t priority)
Creates a new thread.
#define PSA_FRAMEWORK_VERSION
Definition: client.h:26
uint32_t os_wrapper_mutex_release(void *handle)
Releases the mutex acquired previously.
void(* os_wrapper_thread_func)(void *argument)
Definition: thread.h:18
#define PSA_SUCCESS
Definition: crypto_values.h:35
#define TEST_FAIL(info_msg)
uint32_t os_wrapper_get_tick(void)
Return RTOS current tick count.
enum test_suite_err_t set_testsuite(const char *name, struct test_t *test_list, uint32_t size, struct test_suite_t *p_ts)
Sets test suite parameters.
void * os_wrapper_thread_get_handle(void)
Gets current thread handle.
psa_status_t psa_its_get(psa_storage_uid_t uid, size_t data_offset, size_t data_size, void *p_data, size_t *p_data_length)
Retrieve data associated with a provided UID.
psa_status_t psa_its_remove(psa_storage_uid_t uid)
Remove the provided uid and its associated data from the storage.
#define NR_MULTI_CALL_CHILD
uint32_t psa_framework_version(void)
Retrieve the version of the PSA Framework API that is implemented.
Definition: psa_client.c:14
psa_status_t psa_its_set(psa_storage_uid_t uid, size_t data_length, const void *p_data, psa_storage_create_flags_t create_flags)
Create a new, or modify an existing, uid/value pair.
#define OS_WRAPPER_WAIT_FOREVER
Definition: common.h:19
#define MAX_NR_LIGHT_TEST_ROUND
#define NUM_MAILBOX_QUEUE_SLOT
Definition: tfm_mailbox.h:57
#define MULTI_CALL_HEAVY_TEST_STACK_SIZE
#define TEST_CHILD_EVENT_FLAG(x)
struct test_result_t ret
void * os_wrapper_mutex_create(void)
Creates a mutex for mutual exclusion of resources.
void register_testsuite_multi_core_ns_interface(struct test_suite_t *p_test_suite)
Register testsuite for multi-core topology.
#define OS_WRAPPER_ERROR
Definition: common.h:18
uint32_t os_wrapper_thread_get_priority(void *handle, uint32_t *priority)
Gets thread priority.
uint32_t os_wrapper_mutex_delete(void *handle)
Deletes a mutex that is created by os_wrapper_mutex_create()
#define MAX_NR_HEAVY_TEST_ROUND
#define TEST_LOG(...)
uint64_t psa_storage_uid_t
uint32_t os_wrapper_mutex_acquire(void *handle, uint32_t timeout)
Acquires a mutex that is created by os_wrapper_mutex_create()
enum test_status_t val
test_status_t
#define PSA_STORAGE_FLAG_NONE
uint32_t psa_storage_create_flags_t
int32_t psa_status_t
Function return status.
Definition: crypto_types.h:43
uint32_t os_wrapper_thread_wait_flag(uint32_t flags, uint32_t timeout)
Wait for the event flags for synchronizing threads.
uint32_t os_wrapper_thread_set_flag(void *handle, uint32_t flags)
Set the event flags for synchronizing a thread specified by handle.