blob: b2777dadd3951605144e8e18f6c52333d21281b9 [file] [log] [blame]
/*
* Copyright (c) 2017 - 2018, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include "cmsis.h"
#include "tfm_api.h"
#include "cmsis_os2.h"
#include "tfm_nspm_api.h"
#include "tfm_integ_test.h"
#include "test/framework/test_framework_integ_test.h"
#ifdef TEST_FRAMEWORK_S
#include \
"test/test_services/tfm_secure_client_service/tfm_secure_client_service_api.h"
#endif
#ifdef CORE_TEST_INTERACTIVE
#include "test/test_services/tfm_core_test/core_test_defs.h"
#include "test/test_services/tfm_core_test/tfm_ss_core_test_veneers.h"
#define TRY_SFN(fn, ...) \
do { \
enum tfm_status_e res = (enum tfm_status_e) fn(__VA_ARGS__); \
switch(res) { \
case TFM_SUCCESS: \
LOG_MSG("Secure call to " #fn "(" #__VA_ARGS__") successful!");\
break; \
case TFM_ERROR_SECURE_DOMAIN_LOCKED: \
LOG_MSG("Secure call to " #fn "(" #__VA_ARGS__") failed, " \
"S domain locked!");\
break; \
default: \
LOG_MSG("Secure call to " #fn "(" #__VA_ARGS__") failed, " \
"generic!");\
} \
} while(0)
/**
* \brief secure_decrement_ns_lock_1
*
*/
void secure_decrement_ns_lock_1(void)
{
TRY_SFN(tfm_core_test_sfn, CORE_TEST_ID_BLOCK, 0x1, 0x1, 0x1);
}
/**
* \brief secure_decrement_ns_lock_2
*
*/
void secure_decrement_ns_lock_2(void)
{
TRY_SFN(tfm_core_test_sfn, CORE_TEST_ID_BLOCK, 0x2, 0x2, 0x2);
}
/**
* \brief Test definition for the RTX - TFM integration tests
* scenarios
*/
enum test_type {
TEST_TYPE_1 = 1, /*!< Sequential test: single task using the NS lock to
access TFM */
TEST_TYPE_2, /*!< Priority test: high priority tries to preempt TFM,
gets delayed */
TEST_TYPE_3, /*!< Priority inversion: classical scenario with high
priority task waiting on lower priority task
undefinitely if NS lock is configured without priority
inheritance */
TEST_TYPE_4, /*!< non-NS lock: like sequential, but doesn't use any NS
lock mechanism */
TEST_TYPE_5, /*!< non-NS lock, core locked: high priority tries to
overcome the NS lock but finds TFM core locked by
lower priority task and fails */
TEST_TYPE_6 /*!< Like TEST_TYPE_2, but the high priority task has now a
timeout to acquire the NS lock. The timeout will
expire only if TFM Core is built with the
de-prioritization disabled */
};
static const osThreadAttr_t tattr_seq = {
.name = "seq_task",
.stack_size = 1024U,
.attr_bits = osThreadJoinable,
.tz_module = 1,
};
static const osThreadAttr_t tattr_mid = {
.name = "mid_task",
.stack_size = 512U,
.attr_bits = osThreadJoinable,
.tz_module = 0,
.priority = osPriorityAboveNormal
};
static const osThreadAttr_t tattr_pri = {
.name = "pri_task",
.stack_size = 1024U,
.attr_bits = osThreadJoinable,
.tz_module = 1,
.priority = osPriorityHigh
};
/**
* \brief Mutex id, NS lock
*/
static osMutexId_t mutex_id;
/**
* \brief Mutex properties, NS lock
*/
static const osMutexAttr_t mattr_ns_lock = {
.name = "ns_lock",
//.attr_bits = osMutexPrioInherit
};
/**
* \brief TFM NS lock options
*
* \details Options used while acquiring the NS lock
*/
struct tfm_ns_lock_options
{
bool use_ns_lock;
uint32_t timeout;
};
/**
* \brief tfm_service_request
*
* \details This function is used to request a TFM service in thread mode.
* Optionally uses the NS lock and specifies a timeout for obtaining
* the NS lock.
*/
static void tfm_service_request(void(*fn)(void),
struct tfm_ns_lock_options *ns_lock_options_p)
{
osStatus_t result;
char buffer[80];
#define LOG_MSG_THREAD(MSG_THREAD) \
do { \
sprintf(buffer,"%s [%s]", MSG_THREAD, osThreadGetName(osThreadGetId())); \
LOG_MSG(buffer); \
} \
while(0)
LOG_MSG_THREAD("Trying to acquire the TFM core from NS");
if (ns_lock_options_p->use_ns_lock) {
result = osMutexAcquire(mutex_id,0);
if (result == osOK) {
LOG_MSG_THREAD("NS Lock: acquired");
/* Add a delay here just to let the pri_task try to
* acquire the NS lock before seq_task enters secure world
*/
if (!strcmp(osThreadGetName(osThreadGetId()),"seq_task")) {
osDelay(100U);
}
fn();
LOG_MSG_THREAD("NS Lock: releasing...");
osMutexRelease(mutex_id);
} else {
if (ns_lock_options_p->timeout == osWaitForever) {
LOG_MSG_THREAD("Failed to acquire NS lock, keep waiting");
} else {
LOG_MSG_THREAD("Failed to acquire NS lock, wait with timeout");
}
result = osMutexAcquire(mutex_id,ns_lock_options_p->timeout);
if (result == osOK) {
LOG_MSG_THREAD("NS Lock: acquired");
fn();
LOG_MSG_THREAD("NS Lock: releasing...");
osMutexRelease(mutex_id);
} else if (result == osErrorTimeout) {
LOG_MSG_THREAD("NS Lock: failed to acquire, timeout expired");
} else {
LOG_MSG_THREAD("NS Lock: unexpected failure trying to acquire");
}
}
} else {
/* Add a delay here to let the seq_task (which always uses the NS lock)
* enter secure world before the pri_task (which can try to overcome the
* NS lock in test scenario 5)
*/
if (!strcmp(osThreadGetName(osThreadGetId()),"pri_task")) {
osDelay(100U);
}
fn();
}
}
/**
* \brief Non-blocking test thread
*
*/
__attribute__((noreturn))
static void mid_task(void *argument)
{
osThreadId_t thread_id_pri;
osThreadState_t thread_pri_state;
uint32_t idx;
#ifdef TFM_NS_CLIENT_IDENTIFICATION
tfm_nspm_register_client_id();
#endif /* TFM_NS_CLIENT_IDENTIFICATION */
thread_id_pri = *((osThreadId_t *)argument);
/* go to sleep */
osDelay(100U);
thread_pri_state = osThreadGetState(thread_id_pri);
if (thread_pri_state == osThreadBlocked) {
LOG_MSG("Running [mid_task] while [pri_task] is blocked");
} else if (thread_pri_state == osThreadTerminated) {
LOG_MSG("Running [mid_task] while [pri_task] is terminated");
} else {
LOG_MSG("Running [mid_task]");
}
/* Do non TFM related, non blocking, operations */
for (idx=0; idx<0x3ffffff; idx++) {
}
LOG_MSG("Exiting [mid_task]");
osThreadExit();
}
/**
* \brief Priority test thread
*
*/
__attribute__((noreturn))
static void pri_task(void *argument)
{
#ifdef TFM_NS_CLIENT_IDENTIFICATION
tfm_nspm_register_client_id();
#endif /* TFM_NS_CLIENT_IDENTIFICATION */
/* go to sleep */
osDelay(100U);
/* After wake up, try to get hold of the NS lock */
tfm_service_request(secure_decrement_ns_lock_2,
(struct tfm_ns_lock_options *)argument);
osThreadExit();
}
/**
* \brief Sequential test thread
*
*/
__attribute__((noreturn))
static void seq_task(void *argument)
{
osThreadId_t thread_id, thread_id_mid;
enum test_type test_type;
/* By default, use NS lock and wait forever if busy, i.e. until unblocked */
struct tfm_ns_lock_options ns_lock_opt =
{.use_ns_lock=true, .timeout=osWaitForever};
struct tfm_ns_lock_options ns_lock_opt_pri =
{.use_ns_lock=true, .timeout=osWaitForever};
#ifdef TFM_NS_CLIENT_IDENTIFICATION
tfm_nspm_register_client_id();
#endif /* TFM_NS_CLIENT_IDENTIFICATION */
test_type = *((enum test_type *)argument);
if (test_type == TEST_TYPE_1) {
LOG_MSG("Scenario 1 - Sequential");
} else if (test_type == TEST_TYPE_2) {
LOG_MSG("Scenario 2 - Priority");
thread_id = osThreadNew(pri_task, &ns_lock_opt_pri, &tattr_pri);
} else if (test_type == TEST_TYPE_3) {
LOG_MSG("Scenario 3 - Priority inversion");
thread_id = osThreadNew(pri_task, &ns_lock_opt_pri, &tattr_pri);
thread_id_mid = osThreadNew(mid_task, &thread_id, &tattr_mid);
} else if (test_type == TEST_TYPE_4) {
LOG_MSG("Scenario 4 - non-NS lock");
ns_lock_opt.use_ns_lock = false;
} else if (test_type == TEST_TYPE_5) {
LOG_MSG("Scenario 5 - non-NS lock, core locked");
ns_lock_opt_pri.use_ns_lock = false;
thread_id = osThreadNew(pri_task, &ns_lock_opt_pri, &tattr_pri);
} else if (test_type == TEST_TYPE_6) {
LOG_MSG("Scenario 6 - Core prioritization effects on NS world");
ns_lock_opt_pri.timeout = 0x10000; /* timed_wait for NS lock */
thread_id = osThreadNew(pri_task, &ns_lock_opt_pri, &tattr_pri);
} else {
LOG_MSG("Scenario not supported");
osThreadExit();
}
/* Try to acquire the NS lock */
tfm_service_request(secure_decrement_ns_lock_1, &ns_lock_opt);
if (test_type == TEST_TYPE_1) {
LOG_MSG("Scenario 1 - test finished\n");
} else if (test_type == TEST_TYPE_2) {
osThreadJoin(thread_id);
LOG_MSG("Scenario 2 - test finished\n");
} else if (test_type == TEST_TYPE_3) {
osThreadJoin(thread_id);
osThreadJoin(thread_id_mid);
LOG_MSG("Scenario 3 - test finished\n");
} else if (test_type == TEST_TYPE_4) {
LOG_MSG("Scenario 4 - test finished\n");
} else if (test_type == TEST_TYPE_5) {
osThreadJoin(thread_id);
LOG_MSG("Scenario 5 - test finished\n");
} else if (test_type == TEST_TYPE_6) {
osThreadJoin(thread_id);
LOG_MSG("Scenario 6 - test finished\n");
}
osThreadExit();
}
/**
* \brief Execute the interactive tets cases
*
*/
void execute_ns_interactive_tests(void)
{
uint8_t idx;
osThreadId_t thread_id;
/* Test type list */
enum test_type test_type[] = {TEST_TYPE_1, TEST_TYPE_2, TEST_TYPE_3,
TEST_TYPE_4, TEST_TYPE_5, TEST_TYPE_6};
/* Create the NS lock -- shared among testing scenarios */
mutex_id = osMutexNew(&mattr_ns_lock);
/* Loop in the test list */
for (idx=0; idx<sizeof(test_type); idx++) {
/* Spawn the main thread */
thread_id = osThreadNew(seq_task, &test_type[idx], &tattr_seq);
/* Wait for it to finish before moving to the next scenario */
osThreadJoin(thread_id);
}
}
#endif /* CORE_TEST_INTERACTIVE */
#if defined(TEST_FRAMEWORK_NS) || defined(TEST_FRAMEWORK_S)
/**
* \brief Services test thread
*
*/
__attribute__((noreturn))
void test_app(void *argument)
{
UNUSED_VARIABLE(argument);
#ifdef TEST_FRAMEWORK_S
/* FIXME: The non-secure audit log test currently relies on the fact that
* the audit log secure test is run first. However the Non-secure tests
* represent simpler and more common test cases which would make more sense
* to be run first. Therefore if this dependency is removed the execution
* order of these test classes should be reversed. */
tfm_secure_client_run_tests();
#endif
#ifdef TEST_FRAMEWORK_NS
tfm_non_secure_client_run_tests();
#endif
/* End of test */
for (;;) {
}
}
#endif /* TEST_FRAMEWORK_NS OR TEST_FRAMEWORK_S */