Test/App: Add the original test and app codes from tf-m

The version of the tf-m is:
ac9ccf207e153726b8dc1f5569f702f56d94297f

Change-Id: I445ada360540da55bbe74b3e7aa8d622e8fda501
Signed-off-by: Kevin Peng <kevin.peng@arm.com>
diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt
new file mode 100644
index 0000000..2039a6a
--- /dev/null
+++ b/app/CMakeLists.txt
@@ -0,0 +1,435 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2017-2020, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+cmake_minimum_required(VERSION 3.7)
+
+set(TFM_BUILD_IN_SPE OFF)
+
+#Tell cmake where our modules can be found
+list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/../cmake)
+
+set(APP_DIR ${CMAKE_CURRENT_LIST_DIR})
+get_filename_component(TFM_ROOT_DIR ${APP_DIR}/.. ABSOLUTE)
+set(INTERFACE_DIR ${TFM_ROOT_DIR}/interface)
+
+#Include common stuff to control cmake.
+include("Common/BuildSys")
+
+#Start an embedded project.
+embedded_project_start(CONFIG "${TFM_ROOT_DIR}/configs/ConfigDefault.cmake")
+project(tfm_ns LANGUAGES ASM C)
+embedded_project_fixup()
+
+#Include BL2 bootloader related functions
+set(MCUBOOT_DIR "${TFM_ROOT_DIR}/bl2/ext/mcuboot")
+include("${MCUBOOT_DIR}/MCUBoot.cmake")
+
+#CMSIS
+get_filename_component(CMSIS_DIR ${TFM_ROOT_DIR}/../tf-m-tests/CMSIS ABSOLUTE)
+
+if(NOT EXISTS ${CMSIS_DIR})
+	message(FATAL_ERROR "Missing CMSIS. Please clone the tf-m-tests repo.")
+endif()
+
+if (NOT DEFINED BL2)
+	message(FATAL_ERROR "Incomplete build configuration: BL2 is undefined. ")
+endif ()
+
+if (NOT DEFINED TFM_PARTITION_AUDIT_LOG)
+	message(FATAL_ERROR "Incomplete build configuration: TFM_PARTITION_AUDIT_LOG is undefined.")
+endif()
+
+if (NOT DEFINED TFM_PARTITION_PLATFORM)
+	message(FATAL_ERROR "Incomplete build configuration: TFM_PARTITION_PLATFORM is undefined.")
+endif()
+
+if (NOT DEFINED TFM_PARTITION_PROTECTED_STORAGE)
+	message(FATAL_ERROR "Incomplete build configuration: TFM_PARTITION_PROTECTED_STORAGE is undefined.")
+endif()
+
+if (NOT DEFINED TFM_PARTITION_INTERNAL_TRUSTED_STORAGE)
+	message(FATAL_ERROR "Incomplete build configuration: TFM_PARTITION_INTERNAL_TRUSTED_STORAGE is undefined.")
+endif()
+
+if (NOT DEFINED TFM_PARTITION_CRYPTO)
+	message(FATAL_ERROR "Incomplete build configuration: TFM_PARTITION_CRYPTO is undefined.")
+endif()
+
+if (NOT DEFINED TFM_PARTITION_INITIAL_ATTESTATION)
+	message(FATAL_ERROR "Incomplete build configuration: TFM_PARTITION_INITIAL_ATTESTATION is undefined.")
+endif()
+
+if (NOT DEFINED TFM_PSA_API)
+	message(FATAL_ERROR "Incomplete build configuration: TFM_PSA_API is undefined.")
+endif()
+
+embedded_include_directories(PATH ${TFM_ROOT_DIR}/app ABSOLUTE)
+
+set(NS_APP_SRC "${CMSIS_DIR}/RTOS2/RTX/Config/RTX_Config.c"
+	"${CMSIS_DIR}/RTOS2/RTX/Source/rtx_lib.c"
+	"${APP_DIR}/main_ns.c"
+	"${APP_DIR}/tfm_integ_test.c"
+	"${APP_DIR}/os_wrapper_cmsis_rtos_v2.c"
+	"${TFM_ROOT_DIR}/interface/src/log/tfm_log_raw.c"
+	)
+
+if (NOT DEFINED TFM_MULTI_CORE_TOPOLOGY OR NOT TFM_MULTI_CORE_TOPOLOGY)
+	list(APPEND NS_APP_SRC "${INTERFACE_DIR}/src/tfm_ns_interface.c")
+endif()
+
+if (TFM_PARTITION_AUDIT_LOG)
+	if (TFM_PSA_API)
+		message(FATAL_ERROR "Audit log has not been supported in IPC model yet.")
+	else()
+		list(APPEND NS_APP_SRC "${INTERFACE_DIR}/src/tfm_audit_func_api.c")
+	endif()
+endif()
+
+if (TFM_PARTITION_PLATFORM)
+	if (TFM_PSA_API)
+		list(APPEND NS_APP_SRC "${INTERFACE_DIR}/src/tfm_platform_ipc_api.c")
+	else()
+		list(APPEND NS_APP_SRC "${INTERFACE_DIR}/src/tfm_platform_func_api.c")
+	endif()
+endif()
+
+if (TFM_PARTITION_PROTECTED_STORAGE)
+	if (TFM_PSA_API)
+		list(APPEND NS_APP_SRC "${INTERFACE_DIR}/src/tfm_ps_ipc_api.c")
+	else()
+		list(APPEND NS_APP_SRC "${INTERFACE_DIR}/src/tfm_ps_func_api.c")
+	endif()
+endif()
+
+if (TFM_PARTITION_INTERNAL_TRUSTED_STORAGE)
+	if (TFM_PSA_API)
+		list(APPEND NS_APP_SRC "${INTERFACE_DIR}/src/tfm_its_ipc_api.c")
+	else()
+		list(APPEND NS_APP_SRC "${INTERFACE_DIR}/src/tfm_its_func_api.c")
+	endif()
+endif()
+
+if (TFM_PARTITION_CRYPTO)
+	if (TFM_PSA_API)
+		list(APPEND NS_APP_SRC "${INTERFACE_DIR}/src/tfm_crypto_ipc_api.c")
+	else()
+		list(APPEND NS_APP_SRC "${INTERFACE_DIR}/src/tfm_crypto_func_api.c")
+	endif()
+endif()
+
+if (TFM_PARTITION_INITIAL_ATTESTATION)
+	if (TFM_PSA_API)
+		list(APPEND NS_APP_SRC "${INTERFACE_DIR}/src/tfm_initial_attestation_ipc_api.c")
+	else()
+		list(APPEND NS_APP_SRC "${INTERFACE_DIR}/src/tfm_initial_attestation_func_api.c")
+	endif()
+endif()
+
+if (NOT DEFINED TFM_NS_CLIENT_IDENTIFICATION)
+	message(FATAL_ERROR "Incomplete build configuration: TFM_NS_CLIENT_IDENTIFICATION is undefined.")
+elseif (TFM_NS_CLIENT_IDENTIFICATION)
+	list(APPEND NS_APP_SRC
+		"${INTERFACE_DIR}/src/tfm_nspm_svc_handler.c"
+		"${INTERFACE_DIR}/src/tfm_nspm_api.c"
+		)
+endif()
+
+if (PSA_API_TEST_NS)
+	list(APPEND NS_APP_SRC "${APP_DIR}/psa_api_test.c")
+endif()
+
+if (TFM_PSA_API)
+	if (DEFINED TFM_MULTI_CORE_TOPOLOGY AND TFM_MULTI_CORE_TOPOLOGY)
+		list(APPEND NS_APP_SRC "${INTERFACE_DIR}/src/tfm_ns_mailbox.c"
+			"${INTERFACE_DIR}/src/tfm_multi_core_api.c"
+			"${INTERFACE_DIR}/src/tfm_multi_core_psa_ns_api.c"
+			)
+
+		if (TFM_MULTI_CORE_TEST)
+			add_definitions(-DTFM_MULTI_CORE_TEST)
+		endif()
+	else()
+		list(APPEND NS_APP_SRC "${INTERFACE_DIR}/src/tfm_psa_ns_api.c")
+	endif()
+endif()
+
+set(BUILD_CMSIS_CORE On)
+set(BUILD_RETARGET On)
+set(BUILD_NATIVE_DRIVERS On)
+set(BUILD_TIME On)
+set(BUILD_STARTUP On)
+set(BUILD_TARGET_CFG Off)
+set(BUILD_TARGET_HARDWARE_KEYS Off)
+set(BUILD_TARGET_NV_COUNTERS Off)
+set(BUILD_CMSIS_DRIVERS On)
+set(BUILD_UART_STDOUT On)
+set(BUILD_FLASH Off)
+if(CORE_TEST_POSITIVE)
+	set(BUILD_PLAT_TEST On)
+	set(BUILD_TIME On)
+else()
+	set(BUILD_PLAT_TEST Off)
+	set(BUILD_TIME Off)
+endif()
+if(NOT DEFINED PLATFORM_CMAKE_FILE)
+	message (FATAL_ERROR "Platform specific CMake is not defined. Please set PLATFORM_CMAKE_FILE.")
+elseif(NOT EXISTS ${PLATFORM_CMAKE_FILE})
+	message (FATAL_ERROR "Platform specific CMake \"${PLATFORM_CMAKE_FILE}\" file does not exist. Please fix value of PLATFORM_CMAKE_FILE.")
+else()
+	include(${PLATFORM_CMAKE_FILE})
+endif()
+
+if(NOT DEFINED NS_SCATTER_FILE_NAME)
+	message(FATAL_ERROR "ERROR: Incomplete Configuration: NS_SCATTER_FILE_NAME not defined, Include this file from a Config*.cmake")
+endif()
+embedded_set_target_linker_file(TARGET ${PROJECT_NAME} PATH  "${NS_SCATTER_FILE_NAME}")
+
+#Create an object library to avoid compiling all source files twice, when two executables
+#with different memory map need to be linked(BL2 non-swapping)
+set(PROJECT_OBJ_LIB ${PROJECT_NAME}_obj_lib)
+add_library(${PROJECT_OBJ_LIB} OBJECT ${ALL_SRC_C} ${ALL_SRC_C_NS} ${ALL_SRC_ASM} ${ALL_SRC_ASM_NS} ${NS_APP_SRC})
+
+#Set common compiler flags
+config_setting_shared_compiler_flags(${PROJECT_OBJ_LIB})
+
+#Set macro definitions
+set(TARGET_COMPILE_DEFINITIONS __thumb2__ __DOMAIN_NS=1 DOMAIN_NS=__DOMAIN_NS)
+target_compile_definitions(${PROJECT_OBJ_LIB} PRIVATE ${TARGET_COMPILE_DEFINITIONS})
+
+#Set include directories.
+embedded_target_include_directories(TARGET ${PROJECT_OBJ_LIB} PATH ${TEST_INTERFACE_DIR}/include ABSOLUTE APPEND)
+embedded_target_include_directories(TARGET ${PROJECT_OBJ_LIB} PATH ${INTERFACE_DIR}/include ABSOLUTE APPEND)
+embedded_target_include_directories(TARGET ${PROJECT_OBJ_LIB} PATH ${TFM_ROOT_DIR} ABSOLUTE APPEND)
+embedded_target_include_directories(TARGET ${PROJECT_OBJ_LIB} PATH ${TFM_ROOT_DIR}/secure_fw/spm ABSOLUTE APPEND)
+embedded_target_include_directories(TARGET ${PROJECT_OBJ_LIB} PATH ${CMSIS_DIR}/RTOS2/RTX/Include ABSOLUTE APPEND)
+embedded_target_include_directories(TARGET ${PROJECT_OBJ_LIB} PATH ${CMSIS_DIR}/RTOS2/Include ABSOLUTE APPEND)
+embedded_target_include_directories(TARGET ${PROJECT_OBJ_LIB} PATH ${CMSIS_DIR}/RTOS2/RTX/Config  ABSOLUTE APPEND)
+
+if (NOT DEFINED TFM_NS_CLIENT_IDENTIFICATION)
+	message(FATAL_ERROR "Incomplete build configuration: TFM_NS_CLIENT_IDENTIFICATION is undefined.")
+elseif (TFM_NS_CLIENT_IDENTIFICATION)
+	target_compile_definitions(${PROJECT_OBJ_LIB} PRIVATE TFM_NS_CLIENT_IDENTIFICATION)
+endif()
+
+add_subdirectory(${TEST_DIR} ${CMAKE_BINARY_DIR}/test/non_secure_test)
+
+# For the non-swapping BL2 configuration two executables need to be built.
+# One can be executed from the primary slot flash partition and other from the
+# secondary slot. Only the linking phase is different. This function captures
+# common settings and eliminates copy-paste.
+function(set_up_app_build)
+	set( _OPTIONS_ARGS)                                                            #Option (on/off) arguments (e.g. IGNORE_CASE)
+	set( _ONE_VALUE_ARGS NS_TARGET S_TARGET FULL_BIN SIGN_BIN VENEER_NAME POSTFIX) #Single option arguments (e.g. PATH "./foo/bar")
+	set( _MULTI_VALUE_ARGS LINK_DEFINES)                                           #List arguments (e.g. LANGUAGES C ASM CXX)
+	cmake_parse_arguments(_MY_PARAMS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN})
+
+	if (NOT DEFINED _MY_PARAMS_NS_TARGET)
+		message(FATAL_ERROR "set_up_app_build(): mandatory parameter 'NS_TARGET' missing.")
+	endif()
+
+	if (NOT DEFINED _MY_PARAMS_S_TARGET)
+		message(FATAL_ERROR "set_up_app_build(): mandatory parameter 'S_TARGET' missing.")
+	endif()
+
+	if (NOT DEFINED _MY_PARAMS_FULL_BIN)
+		message(FATAL_ERROR "set_up_app_build(): mandatory parameter 'FULL_BIN' missing.")
+	endif()
+
+	if (NOT DEFINED _MY_PARAMS_SIGN_BIN)
+		message(FATAL_ERROR "set_up_app_build(): mandatory parameter 'SIGN_BIN' missing.")
+	endif()
+
+	if (NOT DEFINED _MY_PARAMS_VENEER_NAME)
+		message(FATAL_ERROR "set_up_app_build(): mandatory parameter 'VENEER_NAME' missing.")
+	endif()
+
+	set(EXE_NAME ${_MY_PARAMS_NS_TARGET}${_MY_PARAMS_POSTFIX})
+	set(S_BIN ${_MY_PARAMS_S_TARGET}${_MY_PARAMS_POSTFIX})
+	set(FULL_NAME ${_MY_PARAMS_FULL_BIN}${_MY_PARAMS_POSTFIX})
+	set(SIGN_NAME ${_MY_PARAMS_SIGN_BIN}${_MY_PARAMS_POSTFIX})
+	set(VENEER_NAME ${_MY_PARAMS_VENEER_NAME}${_MY_PARAMS_POSTFIX}.o)
+
+	#Create linker target: add object library to executable
+	add_executable(${EXE_NAME} $<TARGET_OBJECTS:${PROJECT_OBJ_LIB}>)
+
+	#Set common linker flags
+	config_setting_shared_linker_flags(${EXE_NAME})
+
+	#Set individual linker flags per linker target/executable
+	foreach(flag ${_MY_PARAMS_LINK_DEFINES})
+		embedded_set_target_link_defines(TARGET ${EXE_NAME} DEFINES "${flag}")
+	endforeach(flag)
+
+	embedded_set_target_linker_file(TARGET ${EXE_NAME} PATH "${NS_SCATTER_FILE_NAME}")
+
+	#Add the RTX library
+	if(NOT DEFINED RTX_LIB_PATH)
+		message(FATAL_ERROR "ERROR: Incomplete Configuration: RTX_LIB_PATH is not defined.")
+	endif()
+
+	#Add the PSA API compliance test libraries
+	if(PSA_API_TEST_NS)
+		target_link_libraries(${EXE_NAME} "${PSA_API_TEST_BUILD_PATH}/val/val_nspe.a")
+		target_link_libraries(${EXE_NAME} "${PSA_API_TEST_BUILD_PATH}/platform/pal_nspe.a")
+	endif()
+	if(PSA_API_TEST_NS AND (PSA_API_TEST_INTERNAL_TRUSTED_STORAGE OR PSA_API_TEST_PROTECTED_STORAGE OR PSA_API_TEST_STORAGE))
+		target_link_libraries(${EXE_NAME} "${PSA_API_TEST_BUILD_PATH}/dev_apis/storage/test_combine.a")
+	endif()
+	if(PSA_API_TEST_NS AND PSA_API_TEST_CRYPTO)
+		target_link_libraries(${EXE_NAME} "${PSA_API_TEST_BUILD_PATH}/dev_apis/crypto/test_combine.a")
+	endif()
+	if(PSA_API_TEST_NS AND PSA_API_TEST_INITIAL_ATTESTATION)
+		target_link_libraries(${EXE_NAME} "${PSA_API_TEST_BUILD_PATH}/dev_apis/initial_attestation/test_combine.a")
+	endif()
+	if(PSA_API_TEST_NS AND PSA_API_TEST_IPC)
+		target_link_libraries(${EXE_NAME} "${PSA_API_TEST_BUILD_PATH}/ff/ipc/test_combine.a")
+	endif()
+
+	if(NOT DEFINED PLATFORM_LINK_INCLUDES)
+		message(FATAL_ERROR "ERROR: Incomplete Configuration: PLATFORM_LINK_INCLUDES is not defined.")
+	endif()
+	embedded_set_target_link_includes(TARGET ${EXE_NAME} INCLUDES "${PLATFORM_LINK_INCLUDES}")
+
+	#Generate binary file from axf
+	compiler_generate_binary_output(${EXE_NAME})
+
+	#Generate intel hex file from axf
+	compiler_generate_hex_output(${EXE_NAME})
+
+	#Generate elf file from axf
+	compiler_generate_elf_output(${EXE_NAME})
+
+	#Generate MCUBoot compatible payload
+	if (BL2)
+		mcuboot_create_boot_payload(S_BIN    ${S_BIN}
+									NS_BIN   ${EXE_NAME}
+									FULL_BIN ${FULL_NAME}
+									SIGN_BIN ${SIGN_NAME}
+									POSTFIX  ${_MY_PARAMS_POSTFIX})
+	endif()
+
+	if (NOT DEFINED TFM_PARTITION_TEST_CORE)
+		message(FATAL_ERROR "Incomplete build configuration: TFM_PARTITION_TEST_CORE is undefined. ")
+	elseif (TFM_PARTITION_TEST_CORE)
+		embedded_set_target_link_defines(TARGET ${EXE_NAME} DEFINES "TFM_PARTITION_TEST_CORE")
+	endif()
+
+	if (NOT DEFINED TFM_PARTITION_TEST_CORE_IPC)
+		message(FATAL_ERROR "Incomplete build configuration: TFM_PARTITION_TEST_CORE_IPC is undefined.")
+	elseif (TFM_PARTITION_TEST_CORE_IPC)
+		embedded_set_target_link_defines(TARGET ${EXE_NAME} DEFINES "TFM_PARTITION_TEST_CORE_IPC")
+	endif()
+
+	if (NOT DEFINED TFM_PARTITION_TEST_SECURE_SERVICES)
+		message(FATAL_ERROR "Incomplete build configuration: TFM_PARTITION_TEST_SECURE_SERVICES is undefined. ")
+	elseif (TFM_PARTITION_TEST_SECURE_SERVICES)
+		embedded_set_target_link_defines(TARGET ${EXE_NAME} DEFINES "TFM_PARTITION_TEST_SECURE_SERVICES")
+	endif()
+
+	if (NOT DEFINED TEST_FRAMEWORK_S)
+		message(FATAL_ERROR "Incomplete build configuration: TEST_FRAMEWORK_S is undefined.")
+	elseif (TEST_FRAMEWORK_S)
+		embedded_set_target_link_defines(TARGET ${EXE_NAME} DEFINES "TEST_FRAMEWORK_S")
+	endif()
+
+	if (NOT DEFINED TEST_FRAMEWORK_NS)
+		message(FATAL_ERROR "Incomplete build configuration: TEST_FRAMEWORK_NS is undefined.")
+	elseif (TEST_FRAMEWORK_NS)
+		embedded_set_target_link_defines(TARGET ${EXE_NAME} DEFINES "TEST_FRAMEWORK_NS")
+	endif()
+
+	#Set BL2 specific settings.
+	if (BL2)
+		#Add BL2 and MCUBOOT_IMAGE_NUMBER defines to linker to resolve symbols in region_defs.h and flash_layout.h
+		embedded_set_target_link_defines(TARGET ${EXE_NAME} DEFINES "BL2" "MCUBOOT_IMAGE_NUMBER=${MCUBOOT_IMAGE_NUMBER}")
+	endif()
+
+	#We depend on the non secure tests. See if the library target is available.
+	if(TARGET tfm_non_secure_tests)
+		#If yes, then use the library.
+		target_link_libraries(${EXE_NAME} tfm_non_secure_tests)
+		#Ensure library is built first.
+		add_dependencies(${EXE_NAME} tfm_non_secure_tests)
+	endif()
+
+	target_link_libraries(${EXE_NAME} "${RTX_LIB_PATH}")
+
+	#Ensure secure_fw is built before our executable.
+	add_dependencies(${EXE_NAME} ${S_BIN})
+
+	if (NOT DEFINED TFM_MULTI_CORE_TOPOLOGY OR NOT TFM_MULTI_CORE_TOPOLOGY)
+		if (NOT DEFINED S_VENEER_PATH)
+			if (EXISTS ${CMAKE_CURRENT_BINARY_DIR}/../secure_fw)
+				set (S_VENEER_PATH "${CMAKE_CURRENT_BINARY_DIR}/../secure_fw")
+			else()
+				message(FATAL_ERROR "No valid path for S_VENEER_PATH, secure_fw is built?")
+			endif()
+		endif()
+
+		#Add the veneers to the executable.
+		set(S_VENEER_FILE "${S_VENEER_PATH}/${VENEER_NAME}")
+		set_property(TARGET ${EXE_NAME} APPEND PROPERTY LINK_LIBRARIES ${S_VENEER_FILE})
+	endif()
+
+	#Collect executables to common location: build/install/outputs/
+	install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${EXE_NAME}.axf
+				  ${CMAKE_CURRENT_BINARY_DIR}/${EXE_NAME}.bin
+				  ${CMAKE_CURRENT_BINARY_DIR}/${EXE_NAME}.hex
+				  ${CMAKE_CURRENT_BINARY_DIR}/${EXE_NAME}.elf
+			DESTINATION outputs/${TARGET_PLATFORM}/)
+
+	install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${EXE_NAME}.axf
+				  ${CMAKE_CURRENT_BINARY_DIR}/${EXE_NAME}.bin
+				  ${CMAKE_CURRENT_BINARY_DIR}/${EXE_NAME}.hex
+				  ${CMAKE_CURRENT_BINARY_DIR}/${EXE_NAME}.elf
+			DESTINATION outputs/fvp/)
+endfunction()
+
+if (LINK_TO_BOTH_MEMORY_REGION)
+	#Link to primary memory region
+	set_up_app_build(NS_TARGET     ${PROJECT_NAME}
+					 S_TARGET      tfm_s
+					 FULL_BIN      tfm_full
+					 SIGN_BIN      tfm_sign
+					 VENEER_NAME   s_veneers)
+
+	#Link to secondary memory region(add extra linker flag)
+	set_up_app_build(NS_TARGET     ${PROJECT_NAME}
+					 LINK_DEFINES  "LINK_TO_SECONDARY_PARTITION"
+					 S_TARGET      tfm_s
+					 FULL_BIN      tfm_full
+					 SIGN_BIN      tfm_sign
+					 VENEER_NAME   s_veneers
+					 POSTFIX       "_1")
+else()
+	#Link to primary memory region only
+	set_up_app_build(NS_TARGET     ${PROJECT_NAME}
+					 S_TARGET      tfm_s
+					 FULL_BIN      tfm_full
+					 SIGN_BIN      tfm_sign
+					 VENEER_NAME   s_veneers)
+endif()
+
+#If the tfm_non_secure_tests target is not available
+if(NOT TARGET tfm_non_secure_tests)
+	#Add the test source to the build.
+	#As of today since secure_fw is built as a sub-project this code will never execute.
+	option(ENABLE_PROTECTED_STORAGE_SERVICE_TESTS "" TRUE)
+	include(${TEST_DIR}/CMakeLists.inc)
+	target_sources(${PROJECT_OBJ_LIB} PUBLIC ${ALL_SRC_C} ${ALL_SRC_C_NS})
+endif()
+
+#Finally let CMake system apply changes after the whole project is defined.
+if (TARGET ${PROJECT_NAME})
+	embedded_project_end(${PROJECT_NAME})
+endif()
+
+if (TARGET ${PROJECT_NAME}_1)
+	embedded_project_end(${PROJECT_NAME}_1)
+endif()
+
+embedded_project_end(${PROJECT_OBJ_LIB})
diff --git a/app/main_ns.c b/app/main_ns.c
new file mode 100644
index 0000000..fc874bd
--- /dev/null
+++ b/app/main_ns.c
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2017-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "tfm_api.h"
+#include "cmsis_os2.h"
+#include "tfm_integ_test.h"
+#include "tfm_ns_svc.h"
+#include "tfm_ns_interface.h"
+#ifdef TEST_FRAMEWORK_NS
+#include "test/framework/test_framework_integ_test.h"
+#endif
+#ifdef PSA_API_TEST_NS
+#include "psa_api_test.h"
+#endif
+#include "target_cfg.h"
+#include "tfm_plat_ns.h"
+#include "Driver_USART.h"
+#include "device_cfg.h"
+#ifdef TFM_MULTI_CORE_TOPOLOGY
+#include "tfm_multi_core_api.h"
+#include "tfm_ns_mailbox.h"
+#endif
+#include "log/tfm_assert.h"
+#include "log/tfm_log.h"
+#include "uart_stdout.h"
+#include "region.h"
+
+/**
+ * \brief Modified table template for user defined SVC functions
+ *
+ * \details RTX has a weak definition of osRtxUserSVC, which
+ *          is overridden here
+ */
+#if (defined(__ARMCC_VERSION) && (__ARMCC_VERSION == 6110004))
+/* Workaround needed for a bug in Armclang 6.11, more details at:
+ * http://www.keil.com/support/docs/4089.htm
+ */
+__attribute__((section(".gnu.linkonce")))
+#endif
+extern void * const osRtxUserSVC[1+USER_SVC_COUNT];
+       void * const osRtxUserSVC[1+USER_SVC_COUNT] = {
+  (void *)USER_SVC_COUNT,
+
+#define X(SVC_ENUM, SVC_HANDLER) (void*)SVC_HANDLER,
+
+    /* SVC API for Services */
+#ifdef TFM_NS_CLIENT_IDENTIFICATION
+    LIST_SVC_NSPM
+#endif
+
+#undef X
+
+/*
+ * (void *)user_function1,
+ *  ...
+ */
+};
+
+/**
+ * \brief List of RTOS thread attributes
+ */
+#if defined(TEST_FRAMEWORK_NS) || defined(PSA_API_TEST_NS)
+static uint64_t test_app_stack[(4u * 1024u) / (sizeof(uint64_t))]; /* 4KB */
+static const osThreadAttr_t thread_attr = {
+    .name = "test_thread",
+    .stack_mem = test_app_stack,
+    .stack_size = sizeof(test_app_stack),
+};
+#endif
+
+/**
+ * \brief Static globals to hold RTOS related quantities,
+ *        main thread
+ */
+#if defined(TEST_FRAMEWORK_NS) || defined(PSA_API_TEST_NS)
+static osThreadFunc_t thread_func;
+#endif
+
+#ifdef TFM_MULTI_CORE_TOPOLOGY
+static struct ns_mailbox_queue_t ns_mailbox_queue;
+
+static void tfm_ns_multi_core_boot(void)
+{
+    int32_t ret;
+
+    LOG_MSG("Non-secure code running on non-secure core.");
+
+    if (tfm_ns_wait_for_s_cpu_ready()) {
+        LOG_MSG("Error sync'ing with secure core.");
+
+        /* Avoid undefined behavior after multi-core sync-up failed */
+        for (;;) {
+        }
+    }
+
+    ret = tfm_ns_mailbox_init(&ns_mailbox_queue);
+    if (ret != MAILBOX_SUCCESS) {
+        LOG_MSG("Non-secure mailbox initialization failed.");
+
+        /* Avoid undefined behavior after NS mailbox initialization failed */
+        for (;;) {
+        }
+    }
+}
+#endif
+
+/**
+ * \brief Platform peripherals and devices initialization.
+ *        Can be overridden for platform specific initialization.
+ *
+ * \return  ARM_DRIVER_OK if the initialization succeeds
+ */
+__WEAK int32_t tfm_ns_platform_init(void)
+{
+    stdio_init();
+
+    return ARM_DRIVER_OK;
+}
+
+/**
+ * \brief Platform peripherals and devices de-initialization.
+ *        Can be overridden for platform specific initialization.
+ *
+ * \return  ARM_DRIVER_OK if the de-initialization succeeds
+ */
+__WEAK int32_t tfm_ns_platform_uninit(void)
+{
+    stdio_uninit();
+
+    return ARM_DRIVER_OK;
+}
+
+/**
+ * \brief main() function
+ */
+#ifndef __GNUC__
+__attribute__((noreturn))
+#endif
+int main(void)
+{
+#if defined(__ARM_ARCH_8_1M_MAIN__) || defined(__ARM_ARCH_8M_MAIN__)
+    /* Set Main Stack Pointer limit */
+    REGION_DECLARE(Image$$, ARM_LIB_STACK_MSP, $$ZI$$Base);
+    __set_MSPLIM((uint32_t)&REGION_NAME(Image$$, ARM_LIB_STACK_MSP,
+                                        $$ZI$$Base));
+#endif
+
+    if (tfm_ns_platform_init() != ARM_DRIVER_OK) {
+        /* Avoid undefined behavior if platform init failed */
+        while(1);
+    }
+
+#ifdef TFM_MULTI_CORE_TOPOLOGY
+    tfm_ns_multi_core_boot();
+#endif
+
+    (void) osKernelInitialize();
+
+    /* Initialize the TFM NS interface */
+    tfm_ns_interface_init();
+
+#if defined(TEST_FRAMEWORK_NS)
+    thread_func = test_app;
+#elif defined(PSA_API_TEST_NS)
+    thread_func = psa_api_test;
+#endif
+
+#if defined(TEST_FRAMEWORK_NS) || defined(PSA_API_TEST_NS)
+    (void) osThreadNew(thread_func, NULL, &thread_attr);
+#endif
+
+    LOG_MSG("Non-Secure system starting...\r\n");
+    (void) osKernelStart();
+
+    /* Reached only in case of error */
+    for (;;) {
+    }
+}
diff --git a/app/os_wrapper_cmsis_rtos_v2.c b/app/os_wrapper_cmsis_rtos_v2.c
new file mode 100644
index 0000000..9892290
--- /dev/null
+++ b/app/os_wrapper_cmsis_rtos_v2.c
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 2017-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "os_wrapper/thread.h"
+#include "os_wrapper/mutex.h"
+#include "os_wrapper/semaphore.h"
+
+#include <string.h>
+#include "cmsis_os2.h"
+
+/* This is an example OS abstraction layer for CMSIS-RTOSv2 */
+
+void *os_wrapper_thread_new(const char *name, int32_t stack_size,
+                            os_wrapper_thread_func func, void *arg,
+                            uint32_t priority)
+{
+    osThreadAttr_t task_attribs = {.tz_module = 1};
+
+    /* By default, the thread starts as osThreadDetached */
+    if (stack_size != OS_WRAPPER_DEFAULT_STACK_SIZE) {
+        task_attribs.stack_size = stack_size;
+    }
+    task_attribs.name = name;
+    task_attribs.priority = (osPriority_t) priority;
+
+    return (void *)osThreadNew(func, arg, &task_attribs);
+}
+
+void *os_wrapper_semaphore_create(uint32_t max_count, uint32_t initial_count,
+                                  const char *name)
+{
+    osSemaphoreAttr_t sema_attrib = {0};
+
+    sema_attrib.name = name;
+
+    return (void *)osSemaphoreNew(max_count, initial_count, &sema_attrib);
+}
+
+uint32_t os_wrapper_semaphore_acquire(void *handle, uint32_t timeout)
+{
+    osStatus_t status;
+
+    status = osSemaphoreAcquire((osSemaphoreId_t)handle,
+                                (timeout == OS_WRAPPER_WAIT_FOREVER) ?
+                                osWaitForever : timeout);
+    if (status != osOK) {
+        return OS_WRAPPER_ERROR;
+    }
+
+    return OS_WRAPPER_SUCCESS;
+}
+
+uint32_t os_wrapper_semaphore_release(void *handle)
+{
+    osStatus_t status;
+
+    status = osSemaphoreRelease((osSemaphoreId_t)handle);
+    if (status != osOK) {
+        return OS_WRAPPER_ERROR;
+    }
+
+    return OS_WRAPPER_SUCCESS;
+}
+
+uint32_t os_wrapper_semaphore_delete(void *handle)
+{
+    osStatus_t status;
+
+    status = osSemaphoreDelete((osSemaphoreId_t)handle);
+    if (status != osOK) {
+        return OS_WRAPPER_ERROR;
+    }
+
+    return OS_WRAPPER_SUCCESS;
+}
+
+void *os_wrapper_mutex_create(void)
+{
+    const osMutexAttr_t attr = {
+        .name = NULL,
+        .attr_bits = osMutexPrioInherit, /* Priority inheritance is recommended
+                                          * to enable if it is supported.
+                                          * For recursive mutex and the ability
+                                          * of auto release when owner being
+                                          * terminated is not required.
+                                          */
+        .cb_mem = NULL,
+        .cb_size = 0U
+    };
+
+    return (void *)osMutexNew(&attr);
+}
+
+uint32_t os_wrapper_mutex_acquire(void *handle, uint32_t timeout)
+{
+    osStatus_t status = osOK;
+
+    if (!handle) {
+        return OS_WRAPPER_ERROR;
+    }
+
+    status = osMutexAcquire((osMutexId_t)handle,
+                            (timeout == OS_WRAPPER_WAIT_FOREVER) ?
+                             osWaitForever : timeout);
+    if (status != osOK) {
+        return OS_WRAPPER_ERROR;
+    }
+
+    return OS_WRAPPER_SUCCESS;
+}
+
+uint32_t os_wrapper_mutex_release(void *handle)
+{
+    osStatus_t status = osOK;
+
+    if (!handle) {
+        return OS_WRAPPER_ERROR;
+    }
+
+    status = osMutexRelease((osMutexId_t)handle);
+    if (status != osOK) {
+        return OS_WRAPPER_ERROR;
+    }
+
+    return OS_WRAPPER_SUCCESS;
+}
+
+uint32_t os_wrapper_mutex_delete(void *handle)
+{
+    osStatus_t status = osOK;
+
+    if (!handle) {
+        return OS_WRAPPER_ERROR;
+    }
+
+    status = osMutexDelete((osMutexId_t)handle);
+    if (status != osOK) {
+        return OS_WRAPPER_ERROR;
+    }
+
+    return OS_WRAPPER_SUCCESS;
+}
+
+void *os_wrapper_thread_get_handle(void)
+{
+    return (void *)osThreadGetId();
+}
+
+uint32_t os_wrapper_thread_get_priority(void *handle, uint32_t *priority)
+{
+    osPriority_t prio;
+
+    prio = osThreadGetPriority((osThreadId_t)handle);
+    if (prio == osPriorityError) {
+        return OS_WRAPPER_ERROR;
+    }
+
+    *priority = (uint32_t)prio;
+
+    return OS_WRAPPER_SUCCESS;
+}
+
+void os_wrapper_thread_exit(void)
+{
+    osThreadExit();
+}
+
+uint32_t os_wrapper_thread_set_flag(void *handle, uint32_t flags)
+{
+    uint32_t ret;
+
+    ret = osThreadFlagsSet((osThreadId_t)handle, flags);
+    if (ret & osFlagsError) {
+        return OS_WRAPPER_ERROR;
+    }
+
+    return OS_WRAPPER_SUCCESS;
+}
+
+/*
+ * According to the description of CMSIS-RTOS v2 Thread Flags,
+ * osThreadFlagsSet() can be called inside Interrupt Service Routine.
+ */
+uint32_t os_wrapper_thread_set_flag_isr(void *handle, uint32_t flags)
+{
+    uint32_t ret;
+
+    ret = osThreadFlagsSet((osThreadId_t)handle, flags);
+    if (ret & osFlagsError) {
+        return OS_WRAPPER_ERROR;
+    }
+
+    return OS_WRAPPER_SUCCESS;
+}
+
+uint32_t os_wrapper_thread_wait_flag(uint32_t flags, uint32_t timeout)
+{
+    uint32_t ret;
+
+    ret = osThreadFlagsWait(flags, osFlagsWaitAll,
+                            (timeout == OS_WRAPPER_WAIT_FOREVER) ?
+                            osWaitForever : timeout);
+    if (ret & osFlagsError) {
+        return OS_WRAPPER_ERROR;
+    }
+
+    return OS_WRAPPER_SUCCESS;
+}
+
+uint32_t os_wrapper_get_tick(void)
+{
+    return osKernelGetTickCount();
+}
diff --git a/app/psa_api_test.c b/app/psa_api_test.c
new file mode 100644
index 0000000..3a6cc53
--- /dev/null
+++ b/app/psa_api_test.c
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "psa_api_test.h"
+#include "tfm_nspm_api.h"
+#include "tfm_integ_test.h"
+
+/**
+ * \brief This symbol is the entry point provided by the PSA API compliance
+ *        test libraries
+ */
+extern void val_entry(void);
+
+__attribute__((noreturn))
+void psa_api_test(void *arg)
+{
+    UNUSED_VARIABLE(arg);
+
+#ifdef TFM_NS_CLIENT_IDENTIFICATION
+    tfm_nspm_register_client_id();
+#endif /* TFM_NS_CLIENT_IDENTIFICATION */
+
+    val_entry();
+
+    for (;;) {
+    }
+}
diff --git a/app/psa_api_test.h b/app/psa_api_test.h
new file mode 100644
index 0000000..5423b15
--- /dev/null
+++ b/app/psa_api_test.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __PSA_API_TEST_H__
+#define __PSA_API_TEST_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Main test application for the PSA API compliance tests
+ *
+ */
+void psa_api_test(void *arg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __PSA_API_TEST_H__ */
diff --git a/app/tfm_integ_test.c b/app/tfm_integ_test.c
new file mode 100644
index 0000000..32cb674
--- /dev/null
+++ b/app/tfm_integ_test.c
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2017-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "tfm_integ_test.h"
+
+#ifdef TEST_FRAMEWORK_NS
+#include "test/framework/test_framework_integ_test.h"
+#endif
+
+#ifdef TEST_FRAMEWORK_S
+#include \
+  "test/test_services/tfm_secure_client_service/tfm_secure_client_service_api.h"
+#endif
+
+#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 */
diff --git a/app/tfm_integ_test.h b/app/tfm_integ_test.h
new file mode 100644
index 0000000..778f2ae
--- /dev/null
+++ b/app/tfm_integ_test.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2017-2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <stdio.h>
+#include "cmsis_compiler.h"
+
+#ifndef __TFM_INTEG_TEST_H__
+#define __TFM_INTEG_TEST_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Avoids the semihosting issue */
+#if defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050)
+__asm("  .global __ARM_use_no_argv\n");
+#endif
+
+/**
+ * \brief Simple macro to mark UNUSED variables
+ *
+ */
+#define UNUSED_VARIABLE(X) ((void)(X))
+
+#ifdef TEST_FRAMEWORK_NS
+/**
+ * \brief Main test application for the RTX-TFM core
+ *        integration tests
+ *
+ */
+void test_app(void *argument);
+#endif /* TEST_FRAMEWORK_NS */
+
+/**
+ * \brief Execute the interactive test cases (button push)
+ *
+ */
+void execute_ns_interactive_tests(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __TFM_INTEG_TEST_H__ */
diff --git a/test/CMakeLists.inc b/test/CMakeLists.inc
new file mode 100644
index 0000000..ac308f2
--- /dev/null
+++ b/test/CMakeLists.inc
@@ -0,0 +1,60 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2017-2020, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+#Definitions to compile the "test" module.
+#This file assumes it will be included from a project specific cmakefile, and
+#will not create a library or executable.
+#Inputs:
+#	TFM_ROOT_DIR - root directory of the TF-M repo.
+#
+#Outputs:
+#	Will modify include directories to make the source compile.
+#	ALL_SRC_C: C source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+#	ALL_SRC_CXX: C++ source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+#	ALL_SRC_ASM: assembly source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+#	Include directories will be modified by using the include_directories() commands as needed.
+
+#Get the current directory where this file is located.
+if(NOT DEFINED TFM_ROOT_DIR)
+	message(FATAL_ERROR "Please set TFM_ROOT_DIR before including this file.")
+endif()
+
+if (NOT DEFINED ENABLE_AUDIT_LOGGING_SERVICE_TESTS)
+	message(FATAL_ERROR "Incomplete build configuration: ENABLE_AUDIT_LOGGING_SERVICE_TESTS is undefined.")
+endif()
+
+if (NOT DEFINED ENABLE_PLATFORM_SERVICE_TESTS)
+	message(FATAL_ERROR "Incomplete build configuration: ENABLE_PLATFORM_SERVICE_TESTS is undefined.")
+endif()
+
+embedded_include_directories(PATH ${TFM_ROOT_DIR}/secure_fw/spm ABSOLUTE)
+embedded_include_directories(PATH ${TFM_ROOT_DIR}/bl2/include ABSOLUTE)
+
+# Include the test framework
+include(${CMAKE_CURRENT_LIST_DIR}/framework/CMakeLists.inc)
+
+# Include the test suites
+include(${CMAKE_CURRENT_LIST_DIR}/suites/core/CMakeLists.inc)
+include(${CMAKE_CURRENT_LIST_DIR}/suites/ps/CMakeLists.inc)
+include(${CMAKE_CURRENT_LIST_DIR}/suites/its/CMakeLists.inc)
+include(${CMAKE_CURRENT_LIST_DIR}/suites/crypto/CMakeLists.inc)
+include(${CMAKE_CURRENT_LIST_DIR}/suites/attestation/CMakeLists.inc)
+include(${CMAKE_CURRENT_LIST_DIR}/suites/qcbor/CMakeLists.inc)
+include(${CMAKE_CURRENT_LIST_DIR}/suites/t_cose/CMakeLists.inc)
+include(${CMAKE_CURRENT_LIST_DIR}/suites/ipc/CMakeLists.inc)
+if (ENABLE_AUDIT_LOGGING_SERVICE_TESTS)
+	include(${CMAKE_CURRENT_LIST_DIR}/suites/audit/CMakeLists.inc)
+endif()
+if (ENABLE_PLATFORM_SERVICE_TESTS)
+	include(${CMAKE_CURRENT_LIST_DIR}/suites/platform/CMakeLists.inc)
+endif()
+if (TFM_MULTI_CORE_TEST)
+	include(${CMAKE_CURRENT_LIST_DIR}/suites/multi_core/CMakeLists.inc)
+endif()
+
+# Include the test partitions
+include(${CMAKE_CURRENT_LIST_DIR}/test_services/CMakeLists.inc)
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
new file mode 100644
index 0000000..50a7b17
--- /dev/null
+++ b/test/CMakeLists.txt
@@ -0,0 +1,221 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2017-2020, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+cmake_minimum_required(VERSION 3.7)
+
+#Tell cmake where our modules can be found
+list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/../cmake)
+
+#Tell TFM Root before calling sub cmake
+get_filename_component(TFM_ROOT_DIR "${CMAKE_CURRENT_LIST_DIR}/.." ABSOLUTE)
+
+#Include common stuff to control cmake.
+include("Common/BuildSys")
+
+#Start an embedded project.
+embedded_project_start(CONFIG "${TFM_ROOT_DIR}/configs/ConfigDefault.cmake")
+project(tfm_tests LANGUAGES ASM C)
+embedded_project_fixup()
+
+#Check incoming configuration options
+if (NOT DEFINED SERVICES_TEST_ENABLED)
+	message(FATAL_ERROR "Incomplete build configuration: SERVICES_TEST_ENABLED is undefined. ")
+endif()
+
+if (NOT DEFINED CORE_TEST_INTERACTIVE)
+	message(FATAL_ERROR "Incomplete build configuration: CORE_TEST_INTERACTIVE is undefined. ")
+endif()
+
+if (NOT DEFINED CORE_TEST_POSITIVE)
+	message(FATAL_ERROR "Incomplete build configuration: CORE_TEST_POSITIVE is undefined. ")
+endif()
+
+if (NOT DEFINED TFM_LVL)
+	message(FATAL_ERROR "Incomplete build configuration: TFM_LVL is undefined. ")
+endif()
+
+if (NOT DEFINED TFM_PARTITION_AUDIT_LOG)
+	message(FATAL_ERROR "Incomplete build configuration: TFM_PARTITION_AUDIT_LOG is undefined.")
+endif()
+
+if (NOT DEFINED TFM_PARTITION_PROTECTED_STORAGE)
+	message(FATAL_ERROR "Incomplete build configuration: TFM_PARTITION_PROTECTED_STORAGE is undefined.")
+endif()
+
+if (NOT DEFINED TFM_PARTITION_INTERNAL_TRUSTED_STORAGE)
+	message(FATAL_ERROR "Incomplete build configuration: TFM_PARTITION_INTERNAL_TRUSTED_STORAGE is undefined.")
+endif()
+
+if (NOT DEFINED TFM_PARTITION_CRYPTO)
+	message(FATAL_ERROR "Incomplete build configuration: TFM_PARTITION_CRYPTO is undefined.")
+endif()
+
+if (NOT DEFINED TFM_PARTITION_INITIAL_ATTESTATION)
+	message(FATAL_ERROR "Incomplete build configuration: TFM_PARTITION_INITIAL_ATTESTATION is undefined.")
+endif()
+
+if (NOT DEFINED TFM_ENABLE_IRQ_TEST)
+	message(FATAL_ERROR "Incomplete build configuration: TFM_ENABLE_IRQ_TEST is undefined.")
+endif()
+
+#Configure our options as needed.
+if (CORE_TEST_INTERACTIVE OR CORE_TEST_POSITIVE)
+	set(ENABLE_CORE_TESTS True)
+	set(ENABLE_CORE_TESTS_2 True)
+else()
+	set(ENABLE_CORE_TESTS False)
+	set(ENABLE_CORE_TESTS_2 False)
+endif()
+
+if (TFM_ENABLE_IRQ_TEST)
+	set(ENABLE_IRQ_TEST_SERVICES ON)
+else()
+	set(ENABLE_IRQ_TEST_SERVICES OFF)
+endif()
+
+if (ENABLE_CORE_TESTS)
+	# If the platform doesn't specify whether the peripheral test is enabled
+	# or not, select it by default.
+	if (NOT DEFINED TFM_ENABLE_PERIPH_ACCESS_TEST)
+		set(TFM_ENABLE_PERIPH_ACCESS_TEST TRUE)
+	endif()
+
+	if (TFM_ENABLE_PERIPH_ACCESS_TEST)
+		add_definitions(-DTFM_ENABLE_PERIPH_ACCESS_TEST)
+	endif()
+else()
+	set(TFM_ENABLE_PERIPH_ACCESS_TEST FALSE)
+endif()
+
+include(${CMAKE_CURRENT_LIST_DIR}/TestConfig.cmake)
+include(${CMAKE_CURRENT_LIST_DIR}/CMakeLists.inc)
+
+if (ENABLE_PROTECTED_STORAGE_SERVICE_TESTS)
+	embedded_set_target_compile_defines(TARGET tfm_secure_tests LANGUAGE C DEFINES ENABLE_PROTECTED_STORAGE_SERVICE_TESTS APPEND)
+	embedded_set_target_compile_defines(TARGET tfm_non_secure_tests LANGUAGE C DEFINES ENABLE_PROTECTED_STORAGE_SERVICE_TESTS APPEND)
+endif()
+
+if (ENABLE_INTERNAL_TRUSTED_STORAGE_SERVICE_TESTS)
+	embedded_set_target_compile_defines(TARGET tfm_secure_tests LANGUAGE C DEFINES ENABLE_INTERNAL_TRUSTED_STORAGE_SERVICE_TESTS APPEND)
+	embedded_set_target_compile_defines(TARGET tfm_non_secure_tests LANGUAGE C DEFINES ENABLE_INTERNAL_TRUSTED_STORAGE_SERVICE_TESTS APPEND)
+endif()
+
+if (ENABLE_CRYPTO_SERVICE_TESTS)
+	embedded_set_target_compile_defines(TARGET tfm_secure_tests LANGUAGE C DEFINES ENABLE_CRYPTO_SERVICE_TESTS APPEND)
+	embedded_set_target_compile_defines(TARGET tfm_non_secure_tests LANGUAGE C DEFINES ENABLE_CRYPTO_SERVICE_TESTS APPEND)
+endif()
+
+if (ENABLE_ATTESTATION_SERVICE_TESTS)
+	embedded_set_target_compile_defines(TARGET tfm_secure_tests LANGUAGE C DEFINES ENABLE_ATTESTATION_SERVICE_TESTS APPEND)
+	embedded_set_target_compile_defines(TARGET tfm_non_secure_tests LANGUAGE C DEFINES ENABLE_ATTESTATION_SERVICE_TESTS APPEND)
+endif()
+
+if (ENABLE_PLATFORM_SERVICE_TESTS)
+	embedded_set_target_compile_defines(TARGET tfm_secure_tests LANGUAGE C DEFINES ENABLE_PLATFORM_SERVICE_TESTS APPEND)
+	embedded_set_target_compile_defines(TARGET tfm_non_secure_tests LANGUAGE C DEFINES ENABLE_PLATFORM_SERVICE_TESTS APPEND)
+endif()
+
+if (ENABLE_QCBOR_TESTS)
+	embedded_set_target_compile_defines(TARGET tfm_secure_tests LANGUAGE C DEFINES ENABLE_QCBOR_TESTS APPEND)
+	embedded_set_target_compile_defines(TARGET tfm_non_secure_tests LANGUAGE C DEFINES ENABLE_QCBOR_TESTS APPEND)
+endif()
+
+if (ENABLE_T_COSE_TESTS)
+	embedded_set_target_compile_defines(TARGET tfm_non_secure_tests LANGUAGE C DEFINES ENABLE_T_COSE_TESTS APPEND)
+endif()
+
+if (ENABLE_AUDIT_LOGGING_SERVICE_TESTS)
+	embedded_set_target_compile_defines(TARGET tfm_secure_tests LANGUAGE C DEFINES ENABLE_AUDIT_LOGGING_SERVICE_TESTS APPEND)
+	embedded_set_target_compile_defines(TARGET tfm_non_secure_tests LANGUAGE C DEFINES ENABLE_AUDIT_LOGGING_SERVICE_TESTS APPEND)
+endif()
+
+if (TFM_MULTI_CORE_TEST)
+	embedded_set_target_compile_defines(TARGET tfm_non_secure_tests LANGUAGE C DEFINES TFM_MULTI_CORE_TEST APPEND)
+endif()
+
+if (NOT DEFINED TFM_BUILD_IN_SPE)
+	message(FATAL_ERROR "TFM_BUILD_IN_SPE is not set. Cannot specify current building status")
+endif()
+
+if (NOT TARGET tfm_t_cose_verify AND (ENABLE_ATTESTATION_SERVICE_TESTS OR ENABLE_T_COSE_TESTS))
+	add_subdirectory(${TFM_ROOT_DIR}/lib/ext/t_cose ${CMAKE_CURRENT_BINARY_DIR}/t_cose)
+endif()
+
+if ((NOT TARGET tfm_qcbor_encode OR NOT TARGET tfm_qcbor_decode) AND (ENABLE_ATTESTATION_SERVICE_TESTS OR ENABLE_QCBOR_TESTS OR ENABLE_T_COSE_TESTS))
+	add_subdirectory(${TFM_ROOT_DIR}/lib/ext/qcbor ${CMAKE_CURRENT_BINARY_DIR}/qcbor)
+endif()
+
+if (TFM_BUILD_IN_SPE)
+	#Build the secure library. Even though secure tests files depend on
+	#tfm_qcbor, this is not expressed here as the tfm_attest library is expected
+	#to hold the compiled tfm_qcbor files.
+	add_library(tfm_secure_tests STATIC ${ALL_SRC_C} ${ALL_SRC_C_S})
+	if (ENABLE_ATTESTATION_SERVICE_TESTS)
+		target_sources(tfm_secure_tests PRIVATE $<TARGET_OBJECTS:tfm_t_cose_verify> $<TARGET_OBJECTS:tfm_qcbor_decode>)
+	endif()
+
+	#Set common compiler and linker flags
+	if (DEFINED CMSE_FLAGS)
+		embedded_set_target_compile_flags(TARGET tfm_secure_tests LANGUAGE C APPEND FLAGS ${CMSE_FLAGS})
+	endif()
+	config_setting_shared_compiler_flags(tfm_secure_tests)
+	config_setting_shared_linker_flags(tfm_secure_tests)
+
+	embedded_set_target_compile_defines(TARGET tfm_secure_tests LANGUAGE C DEFINES __thumb2__ TFM_LVL=${TFM_LVL} APPEND)
+
+	if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
+		set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/install" CACHE
+			PATH "Default install location for tfm_storage."
+			FORCE)
+	endif()
+
+	install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/framework/test_framework_integ_test.h
+			${CMAKE_CURRENT_SOURCE_DIR}/test_services/tfm_secure_client_service/tfm_secure_client_service_api.h
+			DESTINATION export/tfm/test/inc)
+
+	embedded_project_end(tfm_secure_tests)
+else ()
+	#Build the non-secure library
+	set(CMAKE_STATIC_LIBRARY_PREFIX_C "lib")
+	add_library(tfm_non_secure_tests STATIC ${ALL_SRC_C} ${ALL_SRC_C_NS})
+
+	embedded_target_include_directories(TARGET tfm_non_secure_tests PATH ${CMSIS_DIR}/RTOS2/Include ABSOLUTE APPEND)
+
+	if (ENABLE_ATTESTATION_SERVICE_TESTS OR ENABLE_T_COSE_TESTS)
+		target_sources(tfm_non_secure_tests PRIVATE $<TARGET_OBJECTS:tfm_t_cose_verify> PRIVATE $<TARGET_OBJECTS:tfm_t_cose_sign>)
+	endif()
+	if (ENABLE_ATTESTATION_SERVICE_TESTS OR ENABLE_QCBOR_TESTS OR ENABLE_T_COSE_TESTS)
+		target_sources(tfm_non_secure_tests PRIVATE $<TARGET_OBJECTS:tfm_qcbor_decode> PRIVATE $<TARGET_OBJECTS:tfm_qcbor_encode>)
+	endif()
+
+	#Set common compiler and linker flags
+	config_setting_shared_compiler_flags(tfm_non_secure_tests)
+	config_setting_shared_linker_flags(tfm_non_secure_tests)
+
+	#Set macro definitions
+	set(TARGET_COMPILE_DEFINITIONS __thumb2__ __DOMAIN_NS=1 DOMAIN_NS=__DOMAIN_NS TFM_LVL=${TFM_LVL})
+	embedded_set_target_compile_defines(TARGET tfm_non_secure_tests LANGUAGE C DEFINES ${TARGET_COMPILE_DEFINITIONS} APPEND)
+
+	if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
+		set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/install" CACHE
+			PATH "Default install location for tfm_storage."
+			FORCE)
+	endif()
+
+	install(TARGETS tfm_non_secure_tests
+			DESTINATION export/tfm/test/lib
+			PUBLIC_HEADER DESTINATION export/tfm/test/inc)
+
+	if(ENABLE_PROTECTED_STORAGE_SERVICE_TESTS)
+		#only PS tests are using semaphore and thread APIs
+		install(FILES ${TFM_ROOT_DIR}/interface/include/os_wrapper/semaphore.h
+				${TFM_ROOT_DIR}/interface/include/os_wrapper/thread.h
+			DESTINATION export/tfm/include/os_wrapper)
+	endif()
+
+	embedded_project_end(tfm_non_secure_tests)
+endif()
diff --git a/test/TestConfig.cmake b/test/TestConfig.cmake
new file mode 100644
index 0000000..0fd3178
--- /dev/null
+++ b/test/TestConfig.cmake
@@ -0,0 +1,48 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+option(ENABLE_PROTECTED_STORAGE_SERVICE_TESTS "Option for protected storage service tests" TRUE)
+option(ENABLE_INTERNAL_TRUSTED_STORAGE_SERVICE_TESTS "Option for internal trusted storage services tests" TRUE)
+option(ENABLE_AUDIT_LOGGING_SERVICE_TESTS "Option for audit logging service tests" TRUE)
+option(ENABLE_CRYPTO_SERVICE_TESTS "Option for crypto service tests" TRUE)
+option(ENABLE_ATTESTATION_SERVICE_TESTS "Option for attestation service tests" TRUE)
+option(ENABLE_PLATFORM_SERVICE_TESTS "Option for platform service tests" TRUE)
+option(ENABLE_QCBOR_TESTS "Option for QCBOR tests" TRUE)
+option(ENABLE_T_COSE_TESTS "Option for T_COSE tests" TRUE)
+
+# If a partition is not enabled, then neither should its tests.
+if (NOT TFM_PARTITION_PROTECTED_STORAGE)
+	set(ENABLE_PROTECTED_STORAGE_SERVICE_TESTS FALSE)
+endif()
+
+if (NOT TFM_PARTITION_INTERNAL_TRUSTED_STORAGE)
+	set(ENABLE_INTERNAL_TRUSTED_STORAGE_SERVICE_TESTS FALSE)
+endif()
+
+if (NOT TFM_PARTITION_CRYPTO)
+	set(ENABLE_CRYPTO_SERVICE_TESTS FALSE)
+endif()
+
+if (NOT TFM_PARTITION_INITIAL_ATTESTATION)
+	set(ENABLE_ATTESTATION_SERVICE_TESTS FALSE)
+	set(ENABLE_QCBOR_TESTS FALSE)
+	set(ENABLE_T_COSE_TESTS FALSE)
+endif()
+
+# Disable external library test if SERVICES_TEST_ENABLED is OFF
+if (NOT SERVICES_TEST_ENABLED)
+	set(ENABLE_QCBOR_TESTS FALSE)
+	set(ENABLE_T_COSE_TESTS FALSE)
+endif()
+
+if (NOT TFM_PARTITION_PLATFORM)
+	set(ENABLE_PLATFORM_SERVICE_TESTS FALSE)
+endif()
+
+if (NOT TFM_PARTITION_AUDIT_LOG)
+	set(ENABLE_AUDIT_LOGGING_SERVICE_TESTS FALSE)
+endif()
diff --git a/test/dir_test.dox b/test/dir_test.dox
new file mode 100644
index 0000000..2ffc86a
--- /dev/null
+++ b/test/dir_test.dox
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2018-2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+//This file holds description for the current directory. This documentation
+//will be included in the Doxygen output.
+
+/*!
+\dir
+\brief Source code for TF-M tests.
+\details TF-M tests exercise the TF-M core and various services to check correct
+operation.
+
+*/
\ No newline at end of file
diff --git a/test/framework/CMakeLists.inc b/test/framework/CMakeLists.inc
new file mode 100644
index 0000000..50fafb3
--- /dev/null
+++ b/test/framework/CMakeLists.inc
@@ -0,0 +1,45 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2017-2019, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+#Definitions to compile the "test framework" module.
+#This file assumes it will be included from a project specific cmakefile, and
+#will not create a library or executable.
+#Inputs:
+#	TFM_ROOT_DIR - root directory of the TF-M repo.
+#
+#Outputs:
+#	Will modify include directories to make the source compile.
+#	ALL_SRC_C: C source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+#	ALL_SRC_CXX: C++ source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+#	ALL_SRC_ASM: assembly source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+#	Include directories will be modified by using the include_directories() commands as needed.
+
+#Get the current directory where this file is located.
+set(TEST_FRAMEWORK_DIR ${CMAKE_CURRENT_LIST_DIR})
+if(NOT DEFINED TFM_ROOT_DIR)
+	message(FATAL_ERROR "Please set TFM_ROOT_DIR before including this file.")
+endif()
+
+set (TEST_FRAMEWORK_C_SRC "${TEST_FRAMEWORK_DIR}/test_framework_helpers.c"
+		"${TEST_FRAMEWORK_DIR}/test_framework_integ_test_helper.c"
+		"${TEST_FRAMEWORK_DIR}/test_framework.c"
+	)
+
+set (TEST_FRAMEWORK_C_SRC_NS "${TEST_FRAMEWORK_DIR}/non_secure_suites.c"
+	)
+
+set (TEST_FRAMEWORK_C_SRC_S "${TEST_FRAMEWORK_DIR}/secure_suites.c"
+	)
+
+#Append all our source files to global lists.
+list(APPEND ALL_SRC_C ${TEST_FRAMEWORK_C_SRC})
+list(APPEND ALL_SRC_C_S ${TEST_FRAMEWORK_C_SRC_S})
+list(APPEND ALL_SRC_C_NS ${TEST_FRAMEWORK_C_SRC_NS})
+
+#Setting include directories
+embedded_include_directories(PATH ${TFM_ROOT_DIR} ABSOLUTE)
+embedded_include_directories(PATH ${TFM_ROOT_DIR}/interface/include ABSOLUTE)
diff --git a/test/framework/non_secure_suites.c b/test/framework/non_secure_suites.c
new file mode 100644
index 0000000..ef3a94c
--- /dev/null
+++ b/test/framework/non_secure_suites.c
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2017-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "test_framework_integ_test.h"
+#include "test_framework_integ_test_helper.h"
+#include "test_framework.h"
+
+/* Service specific includes */
+#include "test/suites/ps/non_secure/ps_ns_tests.h"
+#include "test/suites/its/non_secure/its_ns_tests.h"
+#include "test/suites/audit/non_secure/audit_ns_tests.h"
+#include "test/suites/crypto/non_secure/crypto_ns_tests.h"
+#include "test/suites/attestation/non_secure/attestation_ns_tests.h"
+#include "test/suites/qcbor/non_secure/qcbor_ns_tests.h"
+#include "test/suites/t_cose/non_secure/t_cose_ns_tests.h"
+#include "test/suites/core/non_secure/core_ns_tests.h"
+#include "test/suites/ipc/non_secure/ipc_ns_tests.h"
+#include "test/suites/platform/non_secure/platform_ns_tests.h"
+#include "test/suites/multi_core/non_secure/multi_core_ns_test.h"
+
+static struct test_suite_t test_suites[] = {
+#ifdef SERVICES_TEST_NS
+    /* List test cases which are compliant with level 1 isolation */
+
+#ifdef ENABLE_PROTECTED_STORAGE_SERVICE_TESTS
+    {&register_testsuite_ns_psa_ps_interface, 0, 0, 0},
+#endif
+
+#ifdef ENABLE_INTERNAL_TRUSTED_STORAGE_SERVICE_TESTS
+    /* Non-secure ITS test cases */
+    {&register_testsuite_ns_psa_its_interface, 0, 0, 0},
+#endif
+
+#ifdef ENABLE_CRYPTO_SERVICE_TESTS
+    /* Non-secure Crypto test cases */
+    {&register_testsuite_ns_crypto_interface, 0, 0, 0},
+#endif
+
+#ifdef ENABLE_ATTESTATION_SERVICE_TESTS
+    /* Non-secure initial attestation service test cases */
+    {&register_testsuite_ns_attestation_interface, 0, 0, 0},
+#endif
+
+#ifdef ENABLE_PLATFORM_SERVICE_TESTS
+    /* Non-secure platform service test cases */
+    {&register_testsuite_ns_platform_interface, 0, 0, 0},
+#endif
+
+#ifdef ENABLE_QCBOR_TESTS
+    /* Non-secure QCBOR library test cases */
+    {&register_testsuite_ns_qcbor, 0, 0, 0},
+#endif
+
+#ifdef ENABLE_T_COSE_TESTS
+    /* Non-secure T_COSE library test cases */
+    {&register_testsuite_ns_t_cose, 0, 0, 0},
+#endif
+
+#ifdef ENABLE_AUDIT_LOGGING_SERVICE_TESTS
+    /* Non-secure Audit Logging test cases */
+    {&register_testsuite_ns_audit_interface, 0, 0, 0},
+#endif
+
+#endif /* SERVICES_TEST_NS */
+
+#ifdef CORE_TEST_POSITIVE
+    /* Non-secure core test cases */
+    {&register_testsuite_ns_core_positive, 0, 0, 0},
+#endif
+
+#ifdef CORE_TEST_INTERACTIVE
+    /* Non-secure interactive test cases */
+    {&register_testsuite_ns_core_interactive, 0, 0, 0},
+#endif
+
+#ifdef ENABLE_IPC_TEST
+    /* Non-secure IPC test cases */
+    {&register_testsuite_ns_ipc_interface, 0, 0, 0},
+#endif
+
+#ifdef TFM_MULTI_CORE_TEST
+    /* Multi-core topology test cases */
+    {&register_testsuite_multi_core_ns_interface, 0, 0, 0},
+#endif
+
+    /* End of test suites */
+    {0, 0, 0, 0}
+};
+
+enum test_suite_err_t start_integ_test(void)
+{
+    return integ_test("Non-secure", test_suites);
+}
+
+/* Service stand-in for NS tests. To be called from a non-secure context */
+enum test_suite_err_t tfm_non_secure_client_run_tests(void)
+{
+    return start_integ_test();
+}
diff --git a/test/framework/secure_suites.c b/test/framework/secure_suites.c
new file mode 100644
index 0000000..d89d27b
--- /dev/null
+++ b/test/framework/secure_suites.c
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2017-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "test_framework_integ_test.h"
+#include "test_framework_integ_test_helper.h"
+#include "test_framework.h"
+
+/* Service specific includes */
+#include "test/suites/ps/secure/ps_tests.h"
+#include "test/suites/its/secure/its_s_tests.h"
+#include "test/suites/audit/secure/audit_s_tests.h"
+#include "test/suites/attestation/secure/attestation_s_tests.h"
+#include "test/suites/crypto/secure/crypto_s_tests.h"
+#include "test/suites/ipc/secure/ipc_s_tests.h"
+#include "test/suites/platform/secure/platform_s_tests.h"
+
+static struct test_suite_t test_suites[] = {
+#ifdef SERVICES_TEST_S
+    /* List test cases which are compliant with level 1 isolation */
+
+#ifdef ENABLE_PROTECTED_STORAGE_SERVICE_TESTS
+    {&register_testsuite_s_psa_ps_interface, 0, 0, 0},
+    {&register_testsuite_s_psa_ps_reliability, 0, 0, 0},
+
+#ifdef PS_TEST_NV_COUNTERS
+    {&register_testsuite_s_rollback_protection, 0, 0, 0},
+#endif
+#endif
+
+#ifdef ENABLE_INTERNAL_TRUSTED_STORAGE_SERVICE_TESTS
+    /* Secure ITS test cases */
+    {&register_testsuite_s_psa_its_interface, 0, 0, 0},
+    {&register_testsuite_s_psa_its_reliability, 0, 0, 0},
+#endif
+
+#ifdef ENABLE_CRYPTO_SERVICE_TESTS
+    /* Crypto test cases */
+    {&register_testsuite_s_crypto_interface, 0, 0, 0},
+#endif
+
+#ifdef ENABLE_ATTESTATION_SERVICE_TESTS
+    /* Secure initial attestation service test cases */
+    {&register_testsuite_s_attestation_interface, 0, 0, 0},
+#endif
+
+#ifdef ENABLE_PLATFORM_SERVICE_TESTS
+    /* Secure platform service test cases */
+    {&register_testsuite_s_platform_interface, 0, 0, 0},
+#endif
+
+#ifdef ENABLE_AUDIT_LOGGING_SERVICE_TESTS
+    /* Secure Audit Logging test cases */
+    {&register_testsuite_s_audit_interface, 0, 0, 0},
+#endif
+
+#ifdef ENABLE_IPC_TEST
+    /* Secure IPC test cases */
+    {&register_testsuite_s_ipc_interface, 0, 0, 0},
+#endif
+#endif /* SERVICES_TEST_S */
+    /* End of test suites */
+    {0, 0, 0, 0}
+};
+
+static void setup_integ_test(void)
+{
+    /* Left empty intentionally, currently implemented
+     * test suites require no setup
+     */
+}
+
+static void tear_down_integ_test(void)
+{
+    /* Left empty intentionally, currently implemented
+     * test suites require no tear down
+     */
+}
+
+enum test_suite_err_t start_integ_test(void)
+{
+    enum test_suite_err_t retval;
+
+    setup_integ_test();
+    retval = integ_test("Secure", test_suites);
+    tear_down_integ_test();
+    return retval;
+}
diff --git a/test/framework/test_framework.c b/test/framework/test_framework.c
new file mode 100644
index 0000000..85e757f
--- /dev/null
+++ b/test/framework/test_framework.c
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2017-2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "test_framework.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static void test_failed(const struct test_result_t *ret)
+{
+    printf_set_color(RED);
+    if (ret->info_msg != 0) {
+        TEST_LOG("  %s", ret->info_msg);
+        if (ret->filename != 0) {
+            TEST_LOG(" (Failed at %s:%d)\r\n", ret->filename, ret->line);
+        }
+    } else {
+        if (ret->filename != 0) {
+            TEST_LOG("  Failed at %s:%d\r\n", ret->filename, ret->line);
+        }
+    }
+
+    TEST_LOG("  TEST FAILED!\r\n");
+}
+
+static void print_error(const char *err_msg)
+{
+    printf_set_color(RED);
+    TEST_LOG("Error ( %s )\r\n", err_msg);
+}
+
+const char *test_err_to_str(enum test_suite_err_t err)
+{
+    switch (err) {
+    case TEST_SUITE_ERR_NO_ERROR:
+        return "TEST_SUITE_ERR_NO_ERROR";
+    case TEST_SUITE_ERR_INVALID_DATA:
+        return "TEST_SUITE_ERR_INVALID_DATA";
+    case TEST_SUITE_ERR_INVALID_TEST_DATA:
+        return "TEST_SUITE_ERR_INVALID_TEST_DATA";
+    case TEST_SUITE_ERR_TEST_FAILED:
+        return "TEST_SUITE_ERR_TEST_FAILED";
+    /* default:  The default is not defined intentionally to force the
+     *           compiler to check that all the enumeration values are
+     *           covered in the switch.
+     */
+    }
+}
+
+enum test_suite_err_t set_testsuite(const char *name,
+                                    struct test_t *test_list, uint32_t size,
+                                    struct test_suite_t *p_ts)
+{
+    if (p_ts == 0) {
+        print_error("TEST_SUITE_ERR_INVALID_DATA!");
+        return TEST_SUITE_ERR_INVALID_DATA;
+    }
+
+    p_ts->name = name;
+    p_ts->test_list = test_list;
+    p_ts->list_size = size;
+
+    return TEST_SUITE_ERR_NO_ERROR;
+}
+
+void set_test_failed(const char *info_msg, const char *filename, uint32_t line,
+                     struct test_result_t *ret)
+{
+    if (ret == 0) {
+        print_error("TEST_SUITE_ERR_INVALID_TEST_DATA!");
+        return;
+    }
+
+    ret->val = TEST_FAILED;
+    ret->info_msg = info_msg;
+    ret->filename = filename;
+    ret->line = line;
+}
+
+enum test_suite_err_t run_testsuite(struct test_suite_t *test_suite)
+{
+    uint32_t failed_tests = 0;
+    uint32_t i;
+    struct test_t *p_test;
+
+    if (test_suite == 0 || test_suite->freg == 0) {
+        print_error("TEST_SUITE_ERR_INVALID_DATA!");
+        return TEST_SUITE_ERR_INVALID_DATA;
+    }
+
+    /* Sets test suite parameters */
+    test_suite->freg(test_suite);
+    if (test_suite->name == 0 || test_suite->list_size == 0) {
+        print_error("TEST_SUITE_ERR_INVALID_DATA!");
+        return TEST_SUITE_ERR_INVALID_DATA;
+    }
+
+    printf_set_color(YELLOW);
+    TEST_LOG("Running Test Suite %s...\r\n", test_suite->name);
+
+    /* Sets pointer to the first test */
+    p_test = test_suite->test_list;
+
+    for (i = 0; i < test_suite->list_size; i++) {
+
+        if (p_test->test == 0 || p_test->name == 0) {
+            print_error("TEST_SUITE_ERR_INVALID_TEST_DATA!");
+            return TEST_SUITE_ERR_INVALID_TEST_DATA;
+        }
+
+        printf_set_color(WHITE);
+        TEST_LOG("> Executing '%s' \r\n  Description: '%s'\r\n",
+                 p_test->name, p_test->desc);
+
+        /* Sets the default value before the test */
+        p_test->ret.val = TEST_PASSED;
+
+        /* Executes the test */
+        p_test->test(&p_test->ret);
+        if (p_test->ret.val == TEST_FAILED) {
+            test_failed(&p_test->ret);
+            failed_tests++;
+        } else {
+            printf_set_color(GREEN);
+            TEST_LOG("  TEST PASSED!\r\n");
+        }
+
+        /* Sets pointer to the next test */
+        p_test++;
+    }
+
+
+    if (failed_tests == 0) {
+        printf_set_color(GREEN);
+        TEST_LOG("TESTSUITE PASSED!\r\n");
+        test_suite->val = TEST_PASSED;
+    } else {
+        printf_set_color(RED);
+        TEST_LOG("TESTSUITE FAILED!\r\n");
+        printf_set_color(YELLOW);
+        TEST_LOG("Number of failed tests: %d of %d\r\n",
+                 failed_tests, test_suite->list_size);
+        test_suite->val = TEST_FAILED;
+    }
+
+    return TEST_SUITE_ERR_NO_ERROR;
+}
diff --git a/test/framework/test_framework.h b/test/framework/test_framework.h
new file mode 100644
index 0000000..9772875
--- /dev/null
+++ b/test/framework/test_framework.h
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2017-2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __TEST_FRAMEWORK_H__
+#define __TEST_FRAMEWORK_H__
+
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include "log/tfm_log_raw.h"
+#include "test_framework_helpers.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum test_status_t {
+    TEST_PASSED = 0,  /*!< Test has passed */
+    TEST_FAILED = 1,  /*!< Test has failed */
+};
+
+struct test_result_t {
+    enum test_status_t val;  /*!< Test result \ref test_status_t */
+    const char *info_msg;    /*!< Information message to show in case of
+                              *   failure
+                              */
+    const char *filename;    /*!< Filename where the failure has occured */
+    uint32_t    line;        /*!< Line where the failure has occured */
+};
+
+/**
+ * \brief Runs the test.
+ *
+ * \param[out] ret  Test result value
+ */
+typedef void TEST_FUN(struct test_result_t *ret);
+
+struct test_t {
+    TEST_FUN * const test;         /*!< Test function to call */
+    const char *name;              /*!< Test name */
+    const char *desc;              /*!< Test description */
+    struct test_result_t ret;      /*!< Test result */
+};
+
+struct test_suite_t;
+
+/**
+ * \brief Registers test in the testsuite structure and sets the name.
+ *
+ * \param[in] p_test_suite  Pointer to the p_test_suite_location.
+ */
+typedef void TESTSUITE_REG(struct test_suite_t *p_test_suite);
+
+struct test_suite_t {
+    TESTSUITE_REG * const freg;     /*!< Function to set all follow fields
+                                     *   of the current test suite
+                                     */
+    struct test_t *test_list;      /*!< List of tests */
+    uint32_t list_size;            /*!< List size */
+    const char *name;              /*!< Test suite name */
+    enum test_status_t val;        /*!< Test suite result \ref test_result_t */
+};
+
+enum test_suite_err_t {
+    TEST_SUITE_ERR_NO_ERROR = 0,           /*!< No error */
+    TEST_SUITE_ERR_INVALID_DATA = 1,       /*!< Invalid test suite if any of the
+                                            *   pointers is NULL
+                                            */
+    TEST_SUITE_ERR_INVALID_TEST_DATA = 2,  /*!< Invalid test if any of the
+                                            *  pointers is NULL
+                                            */
+    TEST_SUITE_ERR_TEST_FAILED  = 3,       /*!< Last executed test has failed */
+};
+
+/**
+ * \brief Translates the test suite error into a string.
+ *
+ * \param[in] err  Error value \ref test_suite_err_t
+ *
+ * \returns error as string.
+ */
+const char *test_err_to_str(enum test_suite_err_t err);
+
+/**
+ * \brief Sets test suite parameters.
+ *
+ * \param[in] name       Test suite name
+ * \param[in] test_list  Pointer to the test list
+ * \param[in] size       Test list size
+ * \param[in,out] p_ts   Pointer to test suite object to fill in the
+ *                       parameters
+ *
+ * \returns Returns error code as specified in \ref test_suite_err_t
+ */
+enum test_suite_err_t set_testsuite(const char *name,
+                                    struct test_t *test_list, uint32_t size,
+                                    struct test_suite_t *p_ts);
+
+/**
+ * \brief Runs the given test suite.
+ *
+ * \param[in,out] test_suite  Test suite to run the list of tests and
+ *                            store test results.
+ *
+ * \returns Returns error code as specified in \ref test_suite_err_t
+ */
+enum test_suite_err_t run_testsuite(struct test_suite_t *test_suite);
+
+/**
+ * \brief Prints all test in the the given test suite.
+ *
+ * \param[in] ts  Test suite to print the list of tests
+ */
+void show_tests(const struct test_suite_t *ts);
+
+/**
+ * \brief Sets test failure state and information in the \ref test_result_t
+ *        structure.
+ *
+ * \param[in]  info_msg  Information message to show
+ * \param[in]  filename  Filename where the error has ocurred
+ * \param[in]  line      Line in the file where the error has ocurred
+ * \param[out] ret       Pointer to \ref test_result_t structure to
+ *                       set the values
+ *
+ * \note: If info_msg is "" or , info message is not shown. If filename is "",
+ *        filename and line are not shown.
+ */
+void set_test_failed(const char *info_msg, const char *filename, uint32_t line,
+                     struct test_result_t *ret);
+
+#define TEST_FAIL(info_msg)  set_test_failed(info_msg, __FILE__, __LINE__, ret)
+
+#define TEST_LOG(...) tfm_log_printf(__VA_ARGS__)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __TEST_FRAMEWORK_H__ */
diff --git a/test/framework/test_framework_helpers.c b/test/framework/test_framework_helpers.c
new file mode 100644
index 0000000..5341058
--- /dev/null
+++ b/test/framework/test_framework_helpers.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2017-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "test_framework_helpers.h"
+
+#include <stdio.h>
+
+const char *asset_perms_to_str(uint8_t permissions)
+{
+    switch (permissions) {
+    case 0:
+        return "No permissions";
+    case 1:
+        return "SECURE_ASSET_REFERENCE";
+    case 2:
+        return "SECURE_ASSET_WRITE";
+    case 3:
+        return "SECURE_ASSET_REFERENCE | SECURE_ASSET_WRITE";
+    case 4:
+        return "SECURE_ASSET_READ";
+    case 5:
+        return "SECURE_ASSET_REFERENCE | SECURE_ASSET_READ";
+    case 6:
+        return "SECURE_ASSET_WRITE | SECURE_ASSET_READ";
+    case 7:
+        return "SECURE_ASSET_REFERENCE | SECURE_ASSET_WRITE | "
+               "SECURE_ASSET_READ";
+    default:
+        return "Unknown permissions";
+    }
+}
+
+void printf_set_color(enum serial_color_t color_id)
+{
+    TEST_LOG("\33[3%dm", color_id);
+}
diff --git a/test/framework/test_framework_helpers.h b/test/framework/test_framework_helpers.h
new file mode 100644
index 0000000..6a99067
--- /dev/null
+++ b/test/framework/test_framework_helpers.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2017-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __TEST_FRAMEWORK_HELPERS_H__
+#define __TEST_FRAMEWORK_HELPERS_H__
+
+#include <stdint.h>
+#include "psa/protected_storage.h"
+#include "test_framework.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum serial_color_t {
+    BLACK = 0,
+    RED = 1,
+    GREEN = 2,
+    YELLOW = 3,
+    BLUE = 4,
+    MAGENDA = 5,
+    CYAN = 6,
+    WHITE = 7,
+};
+
+/**
+ * \brief Translates asset permissions into a string.
+ *
+ * \param[in] permissions  Asset permissions value.
+ *
+ * \return asset permissions as string.
+ */
+const char *asset_perms_to_str(uint8_t permissions);
+
+/**
+ * \brief Sets the the text color in the serial port.
+ *
+ * \param[in] color_id  Serial foreground color.
+ *
+ * \return 0 if the message is send successfully, 1 otherwise.
+ */
+void printf_set_color(enum serial_color_t color_id);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __TEST_FRAMEWORK_HELPERS_H__ */
diff --git a/test/framework/test_framework_integ_test.h b/test/framework/test_framework_integ_test.h
new file mode 100644
index 0000000..acbcfc0
--- /dev/null
+++ b/test/framework/test_framework_integ_test.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2017-2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __INTEG_TEST_H__
+#define __INTEG_TEST_H__
+
+#include "test_framework.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Executes integration tests. To be called from appropriate
+ *        secure/non-secure service client.
+ *
+ * \returns Returns error code as specified in \ref test_suite_err_t
+ */
+enum test_suite_err_t start_integ_test(void);
+
+/**
+ * \brief Service stand-in shim for non secure tests. To be called from a
+ *        non-secure client.
+ *
+ * \returns Returns error code as specified in \ref test_suite_err_t
+ */
+enum test_suite_err_t tfm_non_secure_client_run_tests(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __INTEG_TEST_H__ */
diff --git a/test/framework/test_framework_integ_test_helper.c b/test/framework/test_framework_integ_test_helper.c
new file mode 100644
index 0000000..26105cd
--- /dev/null
+++ b/test/framework/test_framework_integ_test_helper.c
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2017-2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <stdio.h>
+
+#include "test_framework.h"
+#include "test_framework_integ_test_helper.h"
+
+enum test_suite_err_t integ_test(const char *suite_type,
+                                 struct test_suite_t test_suites[])
+{
+    uint32_t i;
+    enum test_suite_err_t retval = TEST_SUITE_ERR_NO_ERROR;
+
+    printf_set_color(YELLOW);
+    TEST_LOG("\r\n#### Execute test suites for the %s area ####\r\n",
+             suite_type);
+
+    /* Executes test suites */
+    for (i = 0; test_suites[i].freg != NULL; i++) {
+        retval = run_testsuite(&test_suites[i]);
+        if (retval != TEST_SUITE_ERR_NO_ERROR) {
+            /* End function execution */
+            return retval;
+        }
+    }
+
+    /* Prints test suites summary */
+    printf_set_color(YELLOW);
+    TEST_LOG("\r\n*** %s test suites summary ***\r\n", suite_type);
+    for (i = 0; test_suites[i].freg != NULL; i++) {
+        printf_set_color(WHITE);
+        TEST_LOG("Test suite '%s' has ", test_suites[i].name);
+        if (test_suites[i].val == TEST_PASSED) {
+            printf_set_color(GREEN);
+            TEST_LOG(" PASSED\r\n");
+        } else {
+            printf_set_color(RED);
+            TEST_LOG(" FAILED\r\n");
+            retval = TEST_SUITE_ERR_TEST_FAILED;
+        }
+    }
+
+    printf_set_color(YELLOW);
+    TEST_LOG("\r\n*** End of %s test suites ***\r\n", suite_type);
+    return retval;
+}
diff --git a/test/framework/test_framework_integ_test_helper.h b/test/framework/test_framework_integ_test_helper.h
new file mode 100644
index 0000000..c37aa14
--- /dev/null
+++ b/test/framework/test_framework_integ_test_helper.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2017, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __INTEG_TEST_HELPER_H__
+#define __INTEG_TEST_HELPER_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "test_framework.h"
+
+/**
+ * \brief Executes integration test suites provided in the parameters.
+ *
+ * \param[in] suite_type      A string containing the type of the suite
+ *                            (used for printing).
+ * \param[in] test_suites     The suites to be executed.
+ * \param[in] test_suite_cnt  The number of test suites to be executed.
+ *
+ * \returns Returns error code as specified in \ref test_suite_err_t
+ */
+enum test_suite_err_t integ_test(const char *suite_type,
+                                 struct test_suite_t test_suites[]);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __INTEG_TEST_HELPER_H__ */
diff --git a/test/suites/attestation/CMakeLists.inc b/test/suites/attestation/CMakeLists.inc
new file mode 100644
index 0000000..1a739f2
--- /dev/null
+++ b/test/suites/attestation/CMakeLists.inc
@@ -0,0 +1,95 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+#Definitions to compile the "secure attestation test" module.
+#This file assumes it will be included from a project specific cmakefile, and
+#will not create a library or executable.
+#Inputs:
+#	TFM_ROOT_DIR - root directory of the TF-M repo.
+#
+#Outputs:
+#	Will modify include directories to make the source compile.
+#	ALL_SRC_C: C source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+#	ALL_SRC_CXX: C++ source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+#	ALL_SRC_ASM: assembly source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+#	Include directories will be modified by using the include_directories() commands as needed.
+
+#Get the current directory where this file is located.
+set(ATTESTATION_TEST_DIR ${CMAKE_CURRENT_LIST_DIR})
+if(NOT DEFINED TFM_ROOT_DIR)
+	message(FATAL_ERROR "Please set TFM_ROOT_DIR before including this file.")
+endif()
+
+if (NOT DEFINED ATTEST_INCLUDE_TEST_CODE)
+	message(FATAL_ERROR "Incomplete build configuration: ATTEST_INCLUDE_TEST_CODE is undefined. ")
+endif()
+
+if (NOT DEFINED ATTEST_CLAIM_VALUE_CHECK)
+	message(FATAL_ERROR "Incomplete build configuration: ATTEST_CLAIM_VALUE_CHECK is undefined. ")
+endif()
+
+if (NOT DEFINED ENABLE_ATTESTATION_SERVICE_TESTS)
+	message(FATAL_ERROR "Incomplete build configuration: ENABLE_ATTESTATION_SERVICE_TESTS is undefined. ")
+elseif(ENABLE_ATTESTATION_SERVICE_TESTS)
+	list(APPEND ATTEST_TEST_SRC_S
+		"${ATTESTATION_TEST_DIR}/attest_token_test.c"
+		"${ATTESTATION_TEST_DIR}/attest_token_decode_common.c"
+		"${TFM_ROOT_DIR}/lib/ext/qcbor/util/qcbor_util.c"
+	)
+
+	list(APPEND ATTEST_TEST_SRC_NS
+		"${ATTESTATION_TEST_DIR}/attest_token_test.c"
+		"${ATTESTATION_TEST_DIR}/attest_token_decode_common.c"
+		"${TFM_ROOT_DIR}/lib/ext/qcbor/util/qcbor_util.c"
+	)
+
+	if (SYMMETRIC_INITIAL_ATTESTATION)
+		list(APPEND ATTEST_TEST_SRC_S
+			"${ATTESTATION_TEST_DIR}/secure/symmetric_attest_s_interface_testsuite.c"
+			"${ATTESTATION_TEST_DIR}/attest_symmetric_iat_decode.c"
+		)
+		list(APPEND ATTEST_TEST_SRC_NS
+			"${ATTESTATION_TEST_DIR}/non_secure/symmetric_attest_ns_interface_testsuite.c"
+			"${ATTESTATION_TEST_DIR}/attest_symmetric_iat_decode.c"
+		)
+	else()
+		list(APPEND ATTEST_TEST_SRC_S
+			"${ATTESTATION_TEST_DIR}/secure/attestation_s_interface_testsuite.c"
+			"${ATTESTATION_TEST_DIR}/attest_public_key.c"
+			"${ATTESTATION_TEST_DIR}/attest_token_decode.c"
+		)
+		list(APPEND ATTEST_TEST_SRC_NS
+			"${ATTESTATION_TEST_DIR}/non_secure/attestation_ns_interface_testsuite.c"
+			"${ATTESTATION_TEST_DIR}/attest_public_key.c"
+			"${ATTESTATION_TEST_DIR}/attest_token_decode.c"
+		)
+	endif()
+
+	if (ATTEST_INCLUDE_TEST_CODE)
+		set_property(SOURCE ${ATTEST_TEST_SRC_S}  APPEND PROPERTY COMPILE_DEFINITIONS INCLUDE_TEST_CODE)
+		set_property(SOURCE ${ATTEST_TEST_SRC_NS} APPEND PROPERTY COMPILE_DEFINITIONS INCLUDE_TEST_CODE)
+	endif()
+
+	if (ATTEST_CLAIM_VALUE_CHECK)
+		set_property(SOURCE ${ATTEST_TEST_SRC_S}  APPEND PROPERTY COMPILE_DEFINITIONS CLAIM_VALUE_CHECK)
+		set_property(SOURCE ${ATTEST_TEST_SRC_NS} APPEND PROPERTY COMPILE_DEFINITIONS CLAIM_VALUE_CHECK)
+	endif()
+
+	#Setting include directories
+	embedded_include_directories(PATH ${TFM_ROOT_DIR} ABSOLUTE)
+	embedded_include_directories(PATH ${TFM_ROOT_DIR}/interface/include ABSOLUTE)
+	embedded_include_directories(PATH ${TFM_ROOT_DIR}/secure_fw/partitions/initial_attestation ABSOLUTE)
+	embedded_include_directories(PATH ${TFM_ROOT_DIR}/lib/ext/qcbor/inc ABSOLUTE)
+	embedded_include_directories(PATH ${TFM_ROOT_DIR}/lib/ext/qcbor/util ABSOLUTE)
+	embedded_include_directories(PATH ${TFM_ROOT_DIR}/lib/ext/t_cose/inc ABSOLUTE)
+
+	#Append all our source files to global lists.
+	list(APPEND ALL_SRC_C_S  ${ATTEST_TEST_SRC_S})
+	list(APPEND ALL_SRC_C_NS ${ATTEST_TEST_SRC_NS})
+	unset(ATTEST_TEST_SRC_S)
+	unset(ATTEST_TEST_SRC_NS)
+endif()
diff --git a/test/suites/attestation/attest_public_key.c b/test/suites/attestation/attest_public_key.c
new file mode 100644
index 0000000..d6e5aca
--- /dev/null
+++ b/test/suites/attestation/attest_public_key.c
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "attest_public_key.h"
+#include "psa/crypto.h"
+#include <stdint.h>
+#include "attestation.h"
+
+/*!
+ * \def ECC_CURVE_SECP256R1_PUBLIC_KEY_LENGTH
+ *
+ * \brief Calculates the size of ECC public key in bytes based on the bit size
+ *        of the curve
+ */
+#define ECC_CURVE_SECP256R1_PUBLIC_KEY_LENGTH (1 + 2 * PSA_BITS_TO_BYTES(256))
+
+/*!
+ *   Byte string representation of ECC public key according to
+ *   psa_export_public_key() in interface/include/psa/crypto.h
+ */
+struct ecc_public_key_t {
+    const uint8_t a;
+    uint8_t public_key[]; /* X-coordinate || Y-coordinate */
+};
+
+/*!
+ * \var public_key_registered
+ *
+ * \brief Indicates whether the public part of the attestation key pair was
+ *        registered to Crypto service or not.
+ */
+static uint32_t public_key_registered = 0;
+
+enum psa_attest_err_t
+attest_register_initial_attestation_public_key(psa_key_handle_t *public_key)
+{
+    psa_status_t res;
+    psa_key_attributes_t key_attributes = psa_key_attributes_init();
+    uint8_t public_key_buff[ECC_CURVE_SECP256R1_PUBLIC_KEY_LENGTH] = {0};
+    size_t public_key_len;
+    psa_ecc_curve_t ecc_curve;
+
+    /* Public key should be unregistered at this point */
+    if (public_key_registered != 0) {
+        return PSA_ATTEST_ERR_GENERAL;
+    }
+
+    res = tfm_initial_attest_get_public_key(public_key_buff,
+                                            sizeof(public_key_buff),
+                                            &public_key_len,
+                                            &ecc_curve);
+    if (res != PSA_SUCCESS) {
+        return PSA_ATTEST_ERR_GENERAL;
+    }
+
+    /* Setup the key usage flags, algorithm and key type for public key */
+    psa_set_key_usage_flags(&key_attributes, PSA_KEY_USAGE_VERIFY);
+    psa_set_key_algorithm(&key_attributes, PSA_ALG_ECDSA(PSA_ALG_SHA_256));
+    psa_set_key_type(&key_attributes, PSA_KEY_TYPE_ECC_PUBLIC_KEY(ecc_curve));
+
+    /* Register public key to Crypto service */
+    res = psa_import_key(&key_attributes,
+                         (const uint8_t *)&public_key_buff,
+                         public_key_len,
+                         public_key);
+
+    if (res != PSA_SUCCESS) {
+        return PSA_ATTEST_ERR_GENERAL;
+    }
+
+    public_key_registered = 1;
+
+    return PSA_ATTEST_ERR_SUCCESS;
+}
+
+enum psa_attest_err_t
+attest_unregister_initial_attestation_public_key(psa_key_handle_t public_key)
+{
+    psa_status_t crypto_res;
+
+    if (public_key_registered != 1) {
+        return PSA_ATTEST_ERR_GENERAL;
+    }
+
+    crypto_res = psa_destroy_key(public_key);
+    if (crypto_res != PSA_SUCCESS) {
+        return PSA_ATTEST_ERR_GENERAL;
+    }
+    public_key_registered = 0;
+
+    return PSA_ATTEST_ERR_SUCCESS;
+}
diff --git a/test/suites/attestation/attest_public_key.h b/test/suites/attestation/attest_public_key.h
new file mode 100644
index 0000000..fa9d03f
--- /dev/null
+++ b/test/suites/attestation/attest_public_key.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __ATTEST_PUBLIC_KEY_H__
+#define __ATTEST_PUBLIC_KEY_H__
+
+#include "psa/initial_attestation.h"
+#include "psa/crypto.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Register the initial attestation public key to Crypto service to
+ *        verify the signature of the token.
+ *
+ * \param[out] key_handle_public  Pointer to the key handle allocated for the
+ *                                public key
+ *
+ * \retval  PSA_ATTEST_ERR_SUCCESS   Key was registered.
+ * \retval  PSA_ATTEST_ERR_GENERAL   Key could not be registered.
+ */
+enum psa_attest_err_t attest_register_initial_attestation_public_key(
+                                          psa_key_handle_t *key_handle_public);
+
+/**
+ * \brief Unregister the initial attestation public key from Crypto service
+ *        to do not occupy key slot.
+ *
+ * \param[in] key_handle_public  Key handle associated to the public key
+ *
+ * \retval  PSA_ATTEST_ERR_SUCCESS   Key was unregistered.
+ * \retval  PSA_ATTEST_ERR_GENERAL   Key could not be unregistered.
+ */
+enum psa_attest_err_t attest_unregister_initial_attestation_public_key(
+                                           psa_key_handle_t key_handle_public);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ATTEST_PUBLIC_KEY_H__ */
diff --git a/test/suites/attestation/attest_symmetric_iat_decode.c b/test/suites/attestation/attest_symmetric_iat_decode.c
new file mode 100644
index 0000000..77312a3
--- /dev/null
+++ b/test/suites/attestation/attest_symmetric_iat_decode.c
@@ -0,0 +1,163 @@
+/*
+ * attest_symmetric_iat_decode.c
+ *
+ * Copyright (c) 2019, Laurence Lundblade.
+ * Copyright (c) 2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * See BSD-3-Clause license in README.md
+ */
+
+#include "attest_token_decode.h"
+#include "attestation.h"
+#include "psa/crypto.h"
+#include "q_useful_buf.h"
+#include "qcbor_util.h"
+#include "t_cose_common.h"
+#include "t_cose_mac0_verify.h"
+#include "tfm_plat_crypto_keys.h"
+
+/* Only support HMAC as MAC algorithm in COSE_Mac0 so far */
+#define SYMMETRIC_IAK_MAX_SIZE        PSA_MAC_MAX_SIZE
+
+#if DOMAIN_NS == 1U
+/*
+ * Public function. See attest_token_decode.h
+ * It is not allowed to let NS side fetch the symmetric IAK and perform the MAC
+ * verification.
+ */
+enum attest_token_err_t
+attest_token_decode_validate_token(struct attest_token_decode_context *me,
+                                   struct q_useful_buf_c               token)
+{
+    enum t_cose_err_t              t_cose_error;
+    enum attest_token_err_t        return_value;
+    /* Decode only without signature verification */
+    int32_t                        t_cose_options = T_COSE_OPT_DECODE_ONLY;
+    struct t_cose_mac0_verify_ctx  verify_ctx;
+    struct t_cose_key              attest_key = T_COSE_NULL_KEY;
+
+    t_cose_mac0_verify_init(&verify_ctx, t_cose_options);
+
+    t_cose_mac0_set_verify_key(&verify_ctx, attest_key);
+
+    t_cose_error = t_cose_mac0_verify(&verify_ctx,
+                                      token, /* COSE to verify */
+                                      &me->payload, /* Payload from token */
+                                      NULL
+                                     );
+
+    return_value = map_t_cose_errors(t_cose_error);
+    me->last_error = return_value;
+
+    return return_value;
+}
+#else /* DOMAIN_NS == 1U */
+/*
+ * \note The symmetric Initial Attestation key (IAK) will be fetched for
+ *       authentication tag verification in secure test cases.
+ *       Authentication tag verification in tests is for debug purpose only.
+ *       Do not fetch the IAK outside attestation service in real products.
+ */
+static inline enum attest_token_err_t
+decode_register_verify_key(psa_key_handle_t *verify_key_handle)
+{
+    uint8_t key_buf[SYMMETRIC_IAK_MAX_SIZE];
+    psa_algorithm_t key_alg;
+    psa_key_handle_t key_handle;
+    size_t key_len;
+    enum tfm_plat_err_t plat_res;
+    psa_status_t psa_res;
+    psa_key_attributes_t key_attributes = PSA_KEY_ATTRIBUTES_INIT;
+
+    /* Get the symmetric initial attestation key for HMAC operation */
+    plat_res = tfm_plat_get_symmetric_iak(key_buf, sizeof(key_buf),
+                                          &key_len, &key_alg);
+    if (plat_res != TFM_PLAT_ERR_SUCCESS) {
+        return ATTEST_TOKEN_ERR_VERIFICATION_KEY;
+    }
+
+    /*
+     * Verify if HMAC algorithm is valid.
+     * According to COSE (RFC 8152), only SHA-256, SHA-384 and SHA-512 are
+     * supported in HMAC.
+     */
+    if ((key_alg != PSA_ALG_HMAC(PSA_ALG_SHA_256)) && \
+        (key_alg != PSA_ALG_HMAC(PSA_ALG_SHA_384)) && \
+        (key_alg != PSA_ALG_HMAC(PSA_ALG_SHA_512))) {
+        return ATTEST_TOKEN_ERR_VERIFICATION_KEY;
+    }
+
+    /* Setup the key attributes */
+    psa_set_key_usage_flags(&key_attributes, PSA_KEY_USAGE_VERIFY);
+    psa_set_key_algorithm(&key_attributes, key_alg);
+    psa_set_key_type(&key_attributes, PSA_KEY_TYPE_HMAC);
+
+    /* Register the symmetric key to Crypto service */
+    psa_res = psa_import_key(&key_attributes, key_buf, key_len, &key_handle);
+    if (psa_res != PSA_SUCCESS) {
+        return ATTEST_TOKEN_ERR_VERIFICATION_KEY;
+    }
+
+    *verify_key_handle = key_handle;
+
+    return ATTEST_TOKEN_ERR_SUCCESS;
+}
+
+static inline enum attest_token_err_t
+decode_unregister_verify_key(psa_key_handle_t verify_key_handle)
+{
+    psa_status_t status;
+
+    status = psa_destroy_key(verify_key_handle);
+    if (status == PSA_SUCCESS) {
+        return ATTEST_TOKEN_ERR_SUCCESS;
+    }
+
+    return ATTEST_TOKEN_ERR_GENERAL;
+}
+
+/*
+ * Public function. See attest_token_decode.h
+ * Decode the received COSE_Mac0 structure and verify the tag.
+ */
+enum attest_token_err_t
+attest_token_decode_validate_token(struct attest_token_decode_context *me,
+                                   struct q_useful_buf_c               token)
+{
+    enum t_cose_err_t              t_cose_error;
+    enum attest_token_err_t        return_value;
+    int32_t                        t_cose_options = 0;
+    struct t_cose_mac0_verify_ctx  verify_ctx;
+    struct t_cose_key              attest_key;
+    psa_key_handle_t               key_handle;
+
+    return_value = decode_register_verify_key(&key_handle);
+    if (return_value != ATTEST_TOKEN_ERR_SUCCESS) {
+        return return_value;
+    }
+
+    if (me->options & TOKEN_OPT_SHORT_CIRCUIT_SIGN) {
+        t_cose_options |= T_COSE_OPT_ALLOW_SHORT_CIRCUIT;
+    }
+
+    t_cose_mac0_verify_init(&verify_ctx, t_cose_options);
+
+    attest_key.crypto_lib = T_COSE_CRYPTO_LIB_PSA;
+    attest_key.k.key_handle = (uint64_t)key_handle;
+    t_cose_mac0_set_verify_key(&verify_ctx, attest_key);
+
+    t_cose_error = t_cose_mac0_verify(&verify_ctx,
+                                      token, /* COSE to verify */
+                                      &me->payload, /* Payload from token */
+                                      NULL);
+
+    return_value = map_t_cose_errors(t_cose_error);
+    me->last_error = return_value;
+
+    decode_unregister_verify_key(key_handle);
+
+    return return_value;
+}
+#endif /* DOMAIN_NS == 1U */
diff --git a/test/suites/attestation/attest_token_decode.c b/test/suites/attestation/attest_token_decode.c
new file mode 100644
index 0000000..90fbcf5
--- /dev/null
+++ b/test/suites/attestation/attest_token_decode.c
@@ -0,0 +1,71 @@
+/*
+ * attest_token_decode.c
+ *
+ * Copyright (c) 2019, Laurence Lundblade.
+ * Copyright (c) 2020, Arm Limited.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * See BSD-3-Clause license in README.md
+ */
+
+#include "attest_token_decode.h"
+#include "t_cose_sign1_verify.h"
+#include "q_useful_buf.h"
+#include "qcbor_util.h"
+#include "psa/crypto.h"
+#include "attest_public_key.h"
+#include "attestation.h"
+
+/**
+ * \file attest_token_decode.c
+ *
+ * \brief Attestation token decoder for COSE_Sign1.
+ */
+
+/*
+ * Public function. See attest_token_decode.h
+ */
+enum attest_token_err_t
+attest_token_decode_validate_token(struct attest_token_decode_context *me,
+                                   struct q_useful_buf_c token)
+{
+    enum t_cose_err_t              t_cose_error;
+    enum attest_token_err_t        return_value;
+    enum psa_attest_err_t          attest_ret;
+    int32_t                        t_cose_options = 0;
+    struct t_cose_sign1_verify_ctx verify_ctx;
+    struct t_cose_key              attest_key;
+    psa_key_handle_t               public_key;
+
+    /* Run the signature verification */
+    if(me->options & TOKEN_OPT_SHORT_CIRCUIT_SIGN) {
+        t_cose_options |= T_COSE_OPT_ALLOW_SHORT_CIRCUIT;
+    }
+    t_cose_sign1_verify_init(&verify_ctx, t_cose_options);
+
+    attest_ret = attest_register_initial_attestation_public_key(&public_key);
+    if (attest_ret != PSA_ATTEST_ERR_SUCCESS) {
+        return ATTEST_TOKEN_ERR_VERIFICATION_KEY;
+    }
+
+    attest_key.crypto_lib = T_COSE_CRYPTO_LIB_PSA;
+    attest_key.k.key_handle = public_key;
+
+    t_cose_sign1_set_verification_key(&verify_ctx, attest_key);
+
+    t_cose_error =  t_cose_sign1_verify(&verify_ctx,
+                                        token, /* COSE to verify */
+                                        &me->payload, /* Payload from token */
+                                        NULL); /* Don't return parameters */
+
+    return_value = map_t_cose_errors(t_cose_error);
+    me->last_error = return_value;
+
+    attest_ret = attest_unregister_initial_attestation_public_key(public_key);
+    if (attest_ret != PSA_ATTEST_ERR_SUCCESS) {
+        return ATTEST_TOKEN_ERR_GENERAL;
+    }
+
+    return return_value;
+}
diff --git a/test/suites/attestation/attest_token_decode.h b/test/suites/attestation/attest_token_decode.h
new file mode 100644
index 0000000..880336b
--- /dev/null
+++ b/test/suites/attestation/attest_token_decode.h
@@ -0,0 +1,934 @@
+/*
+ * attest_token_decode.h
+ *
+ * Copyright (c) 2019, Laurence Lundblade.
+ * Copyright (c) 2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * See BSD-3-Clause license in README.md
+ */
+#ifndef __ATTEST_TOKEN_DECODE_H__
+#define __ATTEST_TOKEN_DECODE_H__
+
+#include "q_useful_buf.h"
+#include <stdbool.h>
+#include "attest_token.h"
+#include "attest_eat_defines.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \file attest_token_decode.h
+ *
+ * \brief Attestation Token Decoding Interface
+ *
+ * The context and functions here are used to decode an attestation
+ * token as follows:
+ *
+ * -# Create a \ref attest_token_decode_context, most likely as a
+ *    stack variable.
+ *
+ * -# Initialize it by calling attest_token_decode_init()
+ *
+ * -# Tell it which public key to use for verification using
+ *    attest_token_decode_set_cose_pub_key() or
+ *    attest_token_decode_set_pub_key_select().
+ *
+ * -# Pass the token in and validate it by calling
+ *    attest_token_decode_validate_token().
+ *
+ * -# Call the various \c attest_token_get_xxx() methods in any
+ * order. The strings returned by the these functions will point into
+ * the token passed to attest_token_decode_validate_token(). A copy is
+ * NOT made.
+ *
+ * The entire token is validated and decoded in place.  No copies are
+ * made internally. The data returned by the \c attest_token_get_xxx()
+ * methods is not a copy so the lifetime of the \c struct \c
+ * q_useful_buf_c containing the token must be maintained.
+ *
+ * Aside from the cryptographic functions, this allocates no
+ * memory. It works entirely off the stack. It makes use of t_cose to
+ * validate the signature and QCBOR for CBOR decoding.
+ *
+ * This decoder only works with labels (keys) that are integers even
+ * though labels can be any data type in CBOR. The presumption is that
+ * this is for small embedded use cases where space is a premium and
+ * only integer labels will be used.
+ *
+ * All claims are optional in tokens. This decoder will ignore all
+ * CBOR encoded data that it doesn't understand without error.
+ *
+ * This interface is primarily for the claims defined by Arm for
+ * TF-M. It includes only some of the claims from the EAT IETF draft,
+ * https://tools.ietf.org/html/draft-mandyam-eat-01.
+ *
+ * The claims are not described in detail here. That is left to the
+ * definition documents and eventually an IETF standard.
+ *
+ * If a method to get the claim you are interested in doesn't exist,
+ * there are several methods where you can give the label (the key)
+ * for the claim and have it returned. This only works for simple
+ * claims (strings and integers).
+ *
+ * The entire payload can be retrieved unparsed. Then you can use a
+ * separate CBOR parser to decode the claims out of it.  Future work may
+ * include more general facilities for handling claims with complex
+ * structures made up of maps and arrays.
+ *
+ * This should not yet be considered a real commercial
+ * implementation of token decoding. It is
+ * close, but not there yet. It's purpose is to test
+ * token encoding. The main thing this needs to become
+ * a real commercial implementation is code that
+ * tests this. It is a parser / decoder, so a
+ * proper test involves a lot of hostile input.
+ */
+
+
+/**
+ * The context for decoding an attestation token. The caller of must
+ * create one of these and pass it to the functions here. It is small
+ * enough that it can go on the stack. It is most of the memory needed
+ * to create a token except the output buffer and any memory
+ * requirements for the cryptographic operations.
+ *
+ * The structure is opaque for the caller.
+ *
+ */
+struct attest_token_decode_context {
+    /* PRIVATE DATA STRUCTURE. USE ACCESSOR FUNCTIONS. */
+    struct q_useful_buf_c   payload;
+    uint32_t                options;
+    enum attest_token_err_t last_error;
+    /* FIXME: This will have to expand when the pub key
+       handling functions are implemented */
+};
+
+
+/**
+ * \brief Initialize token decoder.
+ *
+ * \param[in] me      The token decoder context to be initialized.
+ * \param[in] options Decoding options.
+ *
+ * Must be called on a \ref attest_token_decode_context before
+ * use. An instance of \ref attest_token_decode_context can
+ * be used again by calling this on it again.
+ **/
+void attest_token_decode_init(struct attest_token_decode_context *me,
+                              uint32_t options);
+
+
+
+/**
+ * \brief Set specific public key to use for verification.
+ *
+ * \param[in] me           The token decoder context to configure.
+ * \param[in] cose_pub_key A CBOR-encoded \c COSE_Key containing
+ *                         the public key to use for signature
+ *                         verification.
+ *
+ * \return An error from \ref attest_token_err_t.
+ *
+ * (This has not been implemented yet)
+ *
+ * The key type must work with the signing algorithm in the token
+ * being verified.
+ *
+ * The \c kid in the \c COSE_Key must match the one in the token.
+ *
+ * If there is no kid in the \c COSE_Key it will be used no matter
+ * what kid is indicated in the token.
+ *
+ * Once set, a key can be used for multiple verifications.
+ *
+ * Calling this again will replace the previous key that was
+ * configured. It will also replace the key set by
+ * attest_token_decode_set_pub_key_select().
+ */
+enum attest_token_err_t
+attest_token_decode_set_cose_pub_key(struct attest_token_decode_context *me,
+                                     struct q_useful_buf cose_pub_key);
+
+
+/**
+ * \brief Set specific public key to use for verification.
+ *
+ * \param[in] me         The token decoder context to configure.
+ * \param[in] key_select Selects the key to verify.
+ *
+ * \return An error from \ref attest_token_err_t.
+ *
+ * (This has not been implemented yet)
+ *
+ * The key type must work with the signing algorithm in the token
+ * being verified.
+ *
+ * The meaning of key_select depends on the platform this is running
+ * on.
+ *
+ * Once set, a key can be used for multiple verifications.
+ *
+ * Calling this again will replace the previous key that was
+ * configured. It will also replace the key set by
+ * attest_token_decode_set_cose_pub_key().
+ *
+ */
+enum attest_token_err_t
+attest_token_decode_set_pub_key_select(struct attest_token_decode_context *me,
+                                       int32_t key_select);
+
+
+/**
+ * \brief Set the token to work on and validate its signature.
+ *
+ * \param[in] me     The token decoder context to validate with.
+ * \param[in] token  The CBOR-encoded token to validate and decode.
+ *
+ * \return An error from \ref attest_token_err_t.
+ *
+ * The signature on the token is validated. If it is successful the
+ * token and its payload is remembered in the \ref
+ * attest_token_decode_context \c me so the \c
+ * attest_token_decode_get_xxx() functions can be called to get the
+ * various claims out of it.
+ *
+ * Generally, a public key has to be configured for this to work. It
+ * can however validate short-circuit signatures even if one is not
+ * set.
+ *
+ * The code for any error that occurs during validation is remembered
+ * in decode context. The \c attest_token_decode_get_xxx() functions
+ * can be called and they will just return this error. The \c
+ * attest_token_decode_get_xxx() functions will generally return 0 or
+ * \c NULL if the token is in error.
+ *
+ * It is thus possible to call attest_token_decode_validate_token()
+ * and all the \c attest_token_decode_get_xxx() functions to parse the
+ * token and ignore the error codes as long as
+ * attest_token_decode_get_error() is called before any of the claim
+ * data returned is used.
+ */
+enum attest_token_err_t
+attest_token_decode_validate_token(struct attest_token_decode_context *me,
+                                   struct q_useful_buf_c token);
+
+
+/**
+ * \brief Get the last decode error.
+ *
+ * \param[in] me The token decoder context.
+ *
+ * \return An error from \ref attest_token_err_t.
+ */
+static enum attest_token_err_t
+attest_token_decode_get_error(struct attest_token_decode_context *me);
+
+
+/**
+ * \brief Get undecoded CBOR payload from the token.
+ *
+ * \param[in]  me      The token decoder context.
+ * \param[out] payload The returned, verified token payload.
+ *
+ * \return An error from \ref attest_token_err_t.
+ *
+ * This will return an error if the signature over the payload did not
+ * validate.
+ *
+ * This allows the caller to parse the payload with any CBOR decoder
+ * they wish to use. It also an "escape hatch" to get to claims in the
+ * token not supported by decoding in this implementation, for example
+ * claims that have non-integer labels.
+ */
+enum attest_token_err_t
+attest_token_decode_get_payload(struct attest_token_decode_context *me,
+                                struct q_useful_buf_c *payload);
+
+
+/** Label for bits in \c item_flags in \ref
+    attest_token_iat_simple_t */
+enum attest_token_item_index_t {
+    NONCE_FLAG =              0,
+    UEID_FLAG  =              1,
+    BOOT_SEED_FLAG =          2,
+    HW_VERSION_FLAG =         3,
+    IMPLEMENTATION_ID_FLAG =  4,
+    CLIENT_ID_FLAG =          5,
+    SECURITY_LIFECYCLE_FLAG = 6,
+    PROFILE_DEFINITION_FLAG = 7,
+    ORIGINATION_FLAG =        8,
+    NUMBER_OF_ITEMS =         9
+};
+
+
+/**
+ * This structure holds the simple-to-get fields from the
+ * token that can be bundled into one structure.
+ *
+ * This is 7 * 8 + 12 = 72 bytes on a 32-bit machine.
+ */
+struct attest_token_iat_simple_t {
+    struct q_useful_buf_c nonce; /* byte string */
+    struct q_useful_buf_c ueid; /* byte string */
+    struct q_useful_buf_c boot_seed; /* byte string */
+    struct q_useful_buf_c hw_version; /* text string */
+    struct q_useful_buf_c implementation_id; /* byte string */
+    uint32_t              security_lifecycle;
+    int32_t               client_id;
+    struct q_useful_buf_c profile_definition; /* text string */
+    struct q_useful_buf_c origination; /* text string */
+    uint32_t              item_flags;
+};
+
+
+/**
+ * Macro to determine if data item is present in \ref
+ * attest_token_iat_simple_t
+ */
+ #define IS_ITEM_FLAG_SET(item_index, item_flags) \
+     (((0x01U << (item_index))) & (item_flags))
+
+
+/**
+ * \brief Batch fetch of all simple data items in a token.
+ *
+ * \param[in]  me     The token decoder context.
+ * \param[out] items  Structure into which all found items are placed.
+ *
+ * \return An error from \ref attest_token_err_t.
+ *
+ * \retval ATTEST_TOKEN_ERR_SUCCESS
+ *         Indicates that the token was successfully searched. It
+ *         could mean that all the data item were found, only
+ *         some were found, or even none were found.
+ *
+ * This searches the token for the simple unstructured data items all
+ * at once. It can be a little more efficient than getting them one by
+ * one.
+ *
+ * Use \ref IS_ITEM_FLAG_SET on \c item_flags in \c
+ * attest_token_iat_simple_t to determine if the data item was found or
+ * not and whether the corresponding member in the structure is valid.
+ */
+enum attest_token_err_t
+attest_token_decode_get_iat_simple(struct attest_token_decode_context *me,
+                                   struct attest_token_iat_simple_t *items);
+
+
+/**
+ \brief Get the nonce out of the token.
+ *
+ * \param[in]  me     The token decoder context.
+ * \param[out] nonce  Returned pointer and length of nonce.
+ *
+ * \return An error from \ref attest_token_err_t.
+ *
+ * The nonce is a byte string. The nonce is also known as the
+ * challenge.
+ */
+static enum attest_token_err_t
+attest_token_decode_get_nonce(struct attest_token_decode_context *me,
+                              struct q_useful_buf_c *nonce);
+
+
+/**
+ * \brief Get the boot seed out of the token.
+ *
+ * \param[in]  me         The token decoder context.
+ * \param[out] boot_seed  Returned pointer and length of boot_seed.
+ *
+ * \return An error from \ref attest_token_err_t.
+ *
+ * The boot seed is a byte string.
+ */
+static enum attest_token_err_t
+attest_token_decode_get_boot_seed(struct attest_token_decode_context *me,
+                                  struct q_useful_buf_c *boot_seed);
+
+
+/**
+ * \brief Get the UEID out of the token.
+ *
+ * \param[in]  me    The token decoder context.
+ * \param[out] ueid  Returned pointer and length of ueid.
+ *
+ * \return An error from \ref attest_token_err_t.
+ *
+ * The UEID is a byte string.
+ */
+static enum attest_token_err_t
+attest_token_decode_get_ueid(struct attest_token_decode_context *me,
+                             struct q_useful_buf_c *ueid);
+
+
+
+/**
+ * \brief Get the HW Version out of the token
+ *
+ * \param[in]  me          The token decoder context.
+ * \param[out] hw_version  Returned pointer and length of
+ *                         \c hw_version.
+ *
+ * \return An error from \ref attest_token_err_t.
+ *
+ * This is also known as the HW ID.
+ *
+ * The HW Version is a UTF-8 text string. It is returned as a pointer
+ * and length. It is NOT \c NULL terminated.
+ */
+static enum attest_token_err_t
+attest_token_decode_get_hw_version(struct attest_token_decode_context *me,
+                                   struct q_useful_buf_c *hw_version);
+
+
+/**
+ * \brief Get the implementation ID out of the token.
+ *
+ * \param[in]  me                 The token decoder context.
+ * \param[out] implementation_id  Returned pointer and length of
+ *                                implementation_id.
+ *
+ * \return An error from \ref attest_token_err_t.
+ *
+ * The implementation ID is a byte string.
+ */
+static enum attest_token_err_t
+attest_token_decode_get_implementation_id(struct attest_token_decode_context*me,
+                                      struct q_useful_buf_c *implementation_id);
+
+
+/**
+ * \brief Get the origination out of the token.
+ *
+ * \param[in]  me           The token decoder context.
+ * \param[out] origination  Returned pointer and length of origination.
+ *
+ * \return An error from \ref attest_token_err_t.
+ *
+ * This is also known as the Verification Service Indicator.
+ *
+ * The \c origination is a UTF-8 text string. It is returned as a
+ * pointer* and length. It is NOT \c NULL terminated.
+ */
+static enum attest_token_err_t
+attest_token_decode_get_origination(struct attest_token_decode_context *me,
+                                    struct q_useful_buf_c *origination);
+
+
+/**
+ * \brief Get the profile definition out of the token.
+ *
+ * \param[in]  me                  The token decoder context.
+ * \param[out] profile_definition  Returned pointer and length of
+ *                                 profile_definition.
+ *
+ * \return An error from \ref attest_token_err_t.
+ *
+ * The profile definition is a UTF-8 text string. It is returned as a
+ * pointer and length. It is NOT \c NULL terminated.
+ */
+static enum attest_token_err_t
+attest_token_decode_get_profile_definition(
+                                    struct attest_token_decode_context *me,
+                                    struct q_useful_buf_c *profile_definition);
+
+
+/**
+ * \brief Get the client ID out of the token.
+ *
+ * \param[in]  me         The token decoder context.
+ * \param[out] client_id  Returned pointer and length of client_id.
+ *
+ * \return An error from \ref attest_token_err_t.
+ *
+ * \retval ATTEST_TOKEN_ERR_INTEGER_VALUE
+ *         If integer is larger or smaller than will fit
+ *         in an \c int32_t.
+ *
+ * Also called the caller ID.
+ */
+static enum attest_token_err_t
+attest_token_decode_get_client_id(struct attest_token_decode_context *me,
+                                  int32_t *client_id);
+
+
+/**
+ * \brief Get the security lifecycle out of the token.
+ *
+ * \param[in]  me         The token decoder context.
+ * \param[out] lifecycle  Returned pointer and length of lifecycle.
+ *
+ * \return An error from \ref attest_token_err_t.
+ *
+ * \retval ATTEST_TOKEN_ERR_INTEGER_VALUE
+ *         If integer is larger
+ *         or smaller than will fit in a \c uint32_t.
+ */
+static enum attest_token_err_t
+attest_token_decode_get_security_lifecycle(
+    struct attest_token_decode_context *me,
+    uint32_t *lifecycle);
+
+
+/**
+ * Use \ref IS_ITEM_FLAG_SET macro with these values and \c
+ * attest_token_sw_component_t.item_flags to find out if the
+ * data item is filled in in the attest_token_sw_component_t structure.
+ *
+ * Items that are of type \c struct \c q_useful_buf_c will also be \c
+ * NULL_Q_USEFUL_BUF_C when they are absent.
+ */
+enum attest_token_sw_index_t {
+    SW_MEASUREMENT_TYPE_FLAG = 0,
+    SW_MEASURMENT_VAL_FLAG = 1,
+    /* Reserved: 2 */
+    SW_VERSION_FLAG = 3,
+    SW_SIGNER_ID_FLAG = 5,
+    SW_MEASUREMENT_DESC_FLAG = 6,
+};
+
+/**
+ * Structure to hold one SW component
+ *
+ * This is about 50 bytes on a 32-bit machine and 100 on a 64-bit
+ * machine.
+ *
+ * There will probably be an expanded version of this when more is
+ * added to describe a SW component.
+ */
+struct attest_token_sw_component_t {
+    struct q_useful_buf_c measurement_type; /* text string */
+    struct q_useful_buf_c measurement_val; /* binary string */
+    struct q_useful_buf_c version; /* text string */
+    struct q_useful_buf_c signer_id; /* binary string */
+    struct q_useful_buf_c measurement_desc; /* text string */
+    uint32_t              item_flags;
+};
+
+
+/**
+ * \brief Get the number of SW components in the token
+ *
+ * \param[in]  me                The token decoder context.
+ * \param[out] num_sw_components The number of SW components in the
+ *                               token.
+ *
+ * \return An error from \ref attest_token_err_t.
+ *
+ * If there are explicitly no SW components, this will return successfully
+ * and the \c num_sw_components will be zero.
+ *
+ * Per Arm's IAT specification the only two ways this will succeed
+ * are.
+ * - The SW components array is present and has one or more (not zero)
+ * SW components and the "no SW Components" claim is absent.
+ * - The "no SW Components" integer claim is present, its value
+ * is 1, and the SW Components array is absent.
+ */
+enum attest_token_err_t
+attest_token_get_num_sw_components(struct attest_token_decode_context *me,
+                                   uint32_t *num_sw_components);
+
+
+/**
+ * \brief Get the nth SW component.
+ *
+ * \param[in] me              The token decoder context.
+ * \param[in] requested_index Index, from 0 to num_sw_components,
+ *                            of request component.
+ * \param[out] sw_components  Place to return the details of the
+ *                            SW component
+ *
+ * \retval ATTEST_TOKEN_ERR_NOT_FOUND
+ *         There were not \c requested_index in the token.
+ *
+ * \retval ATTEST_TOKEN_ERR_CBOR_TYPE
+ *         The claim labeled to contain SW components is not an array.
+ */
+enum attest_token_err_t
+attest_token_get_sw_component(struct attest_token_decode_context *me,
+                       uint32_t requested_index,
+                       struct attest_token_sw_component_t *sw_components);
+
+
+/**
+ *
+ * \brief Get a top-level claim, by integer label that is a byte
+ * string.
+ *
+ * \param[in]  me    The token decoder context.
+ * \param[in]  label The integer label identifying the claim.
+ * \param[out] claim The byte string or \c NULL_Q_USEFUL_BUF_C.
+ *
+ * \return An error from \ref attest_token_err_t.
+ *
+ * \retval ATTEST_TOKEN_ERR_CBOR_STRUCTURE
+ *         General structure of the token is incorrect, for example
+ *         the top level is not a map or some map wasn't closed.
+ *
+ * \retval ATTEST_TOKEN_ERR_CBOR_NOT_WELL_FORMED
+ *         CBOR syntax is wrong and it is not decodable.
+ *
+ * \retval ATTEST_TOKEN_ERR_CBOR_TYPE
+ *         Returned if the claim is not a byte string.
+ *
+ * \retval ATTEST_TOKEN_ERR_NOT_FOUND
+ *         Data item for \c label was not found in token.
+ *
+ * If an error occurs, the claim will be set to \c NULL_Q_USEFUL_BUF_C
+ * and the error state inside \c attest_token_decode_context will
+ * be set.
+ */
+enum attest_token_err_t
+attest_token_decode_get_bstr(struct attest_token_decode_context *me,
+                             int32_t label,
+                             struct q_useful_buf_c *claim);
+
+
+/**
+ * \brief Get a top-level claim, by integer label that is a text
+ * string.
+ *
+ * \param[in] me     The token decoder context.
+ * \param[in] label  The integer label identifying the claim.
+ * \param[out] claim The byte string or \c NULL_Q_USEFUL_BUF_C.
+ *
+ * \return An error from \ref attest_token_err_t.
+ *
+ * \retval ATTEST_TOKEN_ERR_CBOR_STRUCTURE
+ *         General structure of the token is incorrect, for example
+ *         the top level is not a map or some map wasn't closed.
+ *
+ * \retval ATTEST_TOKEN_ERR_CBOR_NOT_WELL_FORMED
+ *         CBOR syntax is wrong and it is not decodable.
+ *
+ * \retval ATTEST_TOKEN_ERR_CBOR_TYPE
+ *         Returned if the claim is not a byte string.
+ *
+ * \retval ATTEST_TOKEN_ERR_NOT_FOUND
+ *         Data item for \c label was not found in token.
+ *
+ * Even though this is a text string, it is not NULL-terminated.
+ *
+ * If an error occurs, the claim will be set to \c NULL_Q_USEFUL_BUF_C
+ * and the error state inside \c attest_token_decode_context will
+ * be set.
+ */
+enum attest_token_err_t
+attest_token_decode_get_tstr(struct attest_token_decode_context *me,
+                             int32_t label,
+                             struct q_useful_buf_c *claim);
+
+
+
+/**
+ * \brief Get a top-level claim by integer label who's value is a
+ * signed integer
+ *
+ * \param[in]  me    The token decoder context.
+ * \param[in]  label The integer label identifying the claim.
+ * \param[out] claim The signed integer or 0.
+ *
+ * \return An error from \ref attest_token_err_t.
+ *
+ * \retval ATTEST_TOKEN_ERR_CBOR_STRUCTURE
+ *         General structure of the token is incorrect, for example
+ *         the top level is not a map or some map wasn't closed.
+ *
+ * \retval ATTEST_TOKEN_ERR_CBOR_NOT_WELL_FORMED
+ *         CBOR syntax is wrong and it is not decodable.
+ *
+ * \retval ATTEST_TOKEN_ERR_CBOR_TYPE
+ *         Returned if the claim is not a byte string.
+ *
+ * \retval ATTEST_TOKEN_ERR_NOT_FOUND
+ *         Data item for \c label was not found in token.
+ *
+ * \retval ATTEST_TOKEN_ERR_INTEGER_VALUE
+ *         Returned if the integer value is larger
+ *         than \c INT64_MAX.
+ *
+ * This will succeed if the CBOR type of the claim is either a
+ * positive or negative integer as long as the value is between \c
+ * INT64_MIN and \c INT64_MAX.
+ *
+ * See also attest_token_decode_get_uint().
+ *
+ * If an error occurs the value 0 will be returned and the error
+ * inside the \c attest_token_decode_context will be set.
+ */
+enum attest_token_err_t
+attest_token_decode_get_int(struct attest_token_decode_context *me,
+                            int32_t label,
+                            int64_t *claim);
+
+
+/**
+ * \brief Get a top-level claim by integer label who's value is an
+ * unsigned integer
+ *
+ * \param[in]  me    The token decoder context.
+ * \param[in]  label The integer label identifying the claim.
+ * \param[out] claim The unsigned integer or 0.
+ *
+ * \return An error from \ref attest_token_err_t.
+ *
+ * \retval ATTEST_TOKEN_ERR_CBOR_STRUCTURE
+ *         General structure of the token is incorrect, for example
+ *         the top level is not a map or some map wasn't closed.
+ *
+ * \retval ATTEST_TOKEN_ERR_CBOR_NOT_WELL_FORMED
+ *         CBOR syntax is wrong and it is not decodable.
+ *
+ * \retval ATTEST_TOKEN_ERR_CBOR_TYPE
+ *         Returned if the claim is not a byte string.
+ *
+ * \retval ATTEST_TOKEN_ERR_NOT_FOUND
+ *         Data item for \c label was not found in token.
+ *
+ * \retval ATTEST_TOKEN_ERR_INTEGER_VALUE
+ *         Returned if the integer value is negative.
+ *
+ * This will succeed if the CBOR type of the claim is either a
+ * positive or negative integer as long as the value is between 0 and
+ * \c MAX_UINT64.
+ *
+ * See also attest_token_decode_get_int().
+ *
+ *  If an error occurs the value 0 will be returned and the error
+ *  inside the \c attest_token_decode_context will be set.
+ */
+enum attest_token_err_t
+attest_token_decode_get_uint(struct attest_token_decode_context *me,
+                             int32_t label,
+                             uint64_t *claim);
+
+
+
+
+/* ====================================================================
+ *   Inline Implementations
+ *   Typically, these are small and called only once.
+ * ==================================================================== */
+
+static inline enum attest_token_err_t
+attest_token_decode_get_error(struct attest_token_decode_context *me)
+{
+    return me->last_error;
+}
+
+
+static inline enum attest_token_err_t
+attest_token_decode_get_nonce(struct attest_token_decode_context *me,
+                              struct q_useful_buf_c *nonce)
+{
+    return attest_token_decode_get_bstr(me,
+                                        EAT_CBOR_ARM_LABEL_CHALLENGE,
+                                        nonce);
+}
+
+
+static inline enum attest_token_err_t
+attest_token_decode_get_ueid(struct attest_token_decode_context *me,
+                             struct q_useful_buf_c *ueid)
+{
+    return attest_token_decode_get_bstr(me, EAT_CBOR_ARM_LABEL_UEID, ueid);
+}
+
+
+static inline enum attest_token_err_t
+attest_token_decode_get_boot_seed(struct attest_token_decode_context *me,
+                                  struct q_useful_buf_c *boot_seed)
+{
+    return attest_token_decode_get_bstr(me,
+                                        EAT_CBOR_ARM_LABEL_BOOT_SEED,
+                                        boot_seed);
+}
+
+
+static inline enum attest_token_err_t
+attest_token_decode_get_hw_version(struct attest_token_decode_context *me,
+                                   struct q_useful_buf_c *hw_version)
+{
+    return attest_token_decode_get_tstr(me,
+                                        EAT_CBOR_ARM_LABEL_HW_VERSION,
+                                        hw_version);
+}
+
+
+static inline enum attest_token_err_t
+attest_token_decode_get_implementation_id(
+                                       struct attest_token_decode_context *me,
+                                       struct q_useful_buf_c*implementation_id)
+{
+    return attest_token_decode_get_bstr(me,
+                                        EAT_CBOR_ARM_LABEL_IMPLEMENTATION_ID,
+                                        implementation_id);
+}
+
+
+static inline enum attest_token_err_t
+attest_token_decode_get_client_id(struct attest_token_decode_context *me,
+                                  int32_t *caller_id)
+{
+    enum attest_token_err_t return_value;
+    int64_t caller_id_64;
+
+    return_value = attest_token_decode_get_int(me,
+                                               EAT_CBOR_ARM_LABEL_CLIENT_ID,
+                                               &caller_id_64);
+    if(return_value != ATTEST_TOKEN_ERR_SUCCESS) {
+        goto Done;
+    }
+    if(caller_id_64 > INT32_MAX || caller_id_64 < INT32_MIN) {
+        return_value = ATTEST_TOKEN_ERR_INTEGER_VALUE;
+        goto Done;
+    }
+    *caller_id = (int32_t)caller_id_64;
+
+Done:
+    return return_value;
+}
+
+
+static inline enum attest_token_err_t
+attest_token_decode_get_security_lifecycle(
+                                 struct attest_token_decode_context *me,
+                                 uint32_t *security_lifecycle)
+{
+    enum attest_token_err_t return_value;
+    uint64_t security_lifecycle_64;
+
+    return_value = attest_token_decode_get_uint(me,
+                                       EAT_CBOR_ARM_LABEL_SECURITY_LIFECYCLE,
+                                       &security_lifecycle_64);
+    if(security_lifecycle_64 > UINT32_MAX) {
+        return_value = ATTEST_TOKEN_ERR_INTEGER_VALUE;
+        goto Done;
+    }
+
+    *security_lifecycle = (uint32_t)security_lifecycle_64;
+
+Done:
+    return return_value;
+}
+
+static inline enum attest_token_err_t
+attest_token_decode_get_profile_definition(
+                                    struct attest_token_decode_context *me,
+                                    struct q_useful_buf_c *profile_definition)
+{
+    return attest_token_decode_get_tstr(me,
+                                        EAT_CBOR_ARM_LABEL_PROFILE_DEFINITION,
+                                        profile_definition);
+}
+
+static inline enum attest_token_err_t
+attest_token_decode_get_origination(struct attest_token_decode_context*me,
+                                    struct q_useful_buf_c *origination)
+{
+    return attest_token_decode_get_tstr(me,
+                                        EAT_CBOR_ARM_LABEL_ORIGINATION,
+                                        origination);
+}
+
+/**
+
+ \brief Map t_cose errors into attestation token errors
+
+ \param[in] t_cose_error  The t_cose error to map
+
+ \return The attestation token error.
+ */
+static inline enum attest_token_err_t
+map_t_cose_errors(enum t_cose_err_t t_cose_error)
+{
+    switch (t_cose_error) {
+    case T_COSE_SUCCESS:
+        return ATTEST_TOKEN_ERR_SUCCESS;
+        break;
+    case T_COSE_ERR_UNSUPPORTED_SIGNING_ALG:
+        return ATTEST_TOKEN_ERR_UNSUPPORTED_SIG_ALG;
+        break;
+    case T_COSE_ERR_UNSUPPORTED_HASH:
+        return ATTEST_TOKEN_ERR_HASH_UNAVAILABLE;
+        break;
+    case T_COSE_ERR_CBOR_NOT_WELL_FORMED:
+        return ATTEST_TOKEN_ERR_CBOR_NOT_WELL_FORMED;
+        break;
+    case T_COSE_ERR_INSUFFICIENT_MEMORY:
+        return ATTEST_TOKEN_ERR_INSUFFICIENT_MEMORY;
+        break;
+    case T_COSE_ERR_TAMPERING_DETECTED:
+        return ATTEST_TOKEN_ERR_TAMPERING_DETECTED;
+        break;
+    case T_COSE_ERR_CBOR_FORMATTING:
+        return ATTEST_TOKEN_ERR_CBOR_FORMATTING;
+        break;
+    case T_COSE_ERR_TOO_SMALL:
+        return ATTEST_TOKEN_ERR_TOO_SMALL;
+        break;
+
+    case T_COSE_ERR_PARAMETER_CBOR:
+    case T_COSE_ERR_NON_INTEGER_ALG_ID:
+        return ATTEST_TOKEN_ERR_CBOR_STRUCTURE;
+        break;
+
+    case T_COSE_ERR_SIG_VERIFY:
+    case T_COSE_ERR_SHORT_CIRCUIT_SIG:
+        return ATTEST_TOKEN_ERR_COSE_VALIDATION;
+        break;
+
+    case T_COSE_ERR_SIGN1_FORMAT:
+        return ATTEST_TOKEN_ERR_COSE_FORMAT;
+        break;
+
+    case T_COSE_ERR_MAC0_FORMAT:
+        return ATTEST_TOKEN_ERR_COSE_FORMAT;
+        break;
+
+    case T_COSE_ERR_NO_ALG_ID:
+    case T_COSE_ERR_NO_KID:
+    case T_COSE_ERR_BAD_SHORT_CIRCUIT_KID:
+    case T_COSE_ERR_SIG_STRUCT:
+        return ATTEST_TOKEN_ERR_COSE_FORMAT;
+        break;
+
+    case T_COSE_ERR_UNKNOWN_KEY:
+    case T_COSE_ERR_WRONG_TYPE_OF_KEY:
+        return ATTEST_TOKEN_ERR_VERIFICATION_KEY;
+        break;
+
+    case T_COSE_ERR_MAKING_PROTECTED:
+    case T_COSE_ERR_HASH_GENERAL_FAIL:
+    case T_COSE_ERR_HASH_BUFFER_SIZE:
+    case T_COSE_ERR_SIG_BUFFER_SIZE:
+    case T_COSE_ERR_INVALID_ARGUMENT:
+    case T_COSE_ERR_FAIL:
+    case T_COSE_ERR_SIG_FAIL:
+    case T_COSE_ERR_TOO_MANY_PARAMETERS:
+    case T_COSE_ERR_UNKNOWN_CRITICAL_PARAMETER:
+    case T_COSE_ERR_SHORT_CIRCUIT_SIG_DISABLED:
+    case T_COSE_ERR_INCORRECT_KEY_FOR_LIB:
+    case T_COSE_ERR_BAD_CONTENT_TYPE:
+    case T_COSE_ERR_INCORRECTLY_TAGGED:
+    case T_COSE_ERR_EMPTY_KEY:
+    case T_COSE_ERR_DUPLICATE_PARAMETER:
+    case T_COSE_ERR_PARAMETER_NOT_PROTECTED:
+    case T_COSE_ERR_CRIT_PARAMETER:
+    default:
+        return ATTEST_TOKEN_ERR_GENERAL;
+    }
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* __ATTEST_TOKEN_DECODE_H__ */
diff --git a/test/suites/attestation/attest_token_decode_common.c b/test/suites/attestation/attest_token_decode_common.c
new file mode 100644
index 0000000..52987c9
--- /dev/null
+++ b/test/suites/attestation/attest_token_decode_common.c
@@ -0,0 +1,601 @@
+/*
+ * attest_token_decode_common.c
+ *
+ * Copyright (c) 2019, Laurence Lundblade.
+ * Copyright (c) 2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * See BSD-3-Clause license in README.md
+ */
+
+#include "attest_token_decode.h"
+#include "attestation.h"
+#include "q_useful_buf.h"
+#include "qcbor_util.h"
+
+/**
+ * \file attest_token_decode_common.c
+ *
+ * \brief Common functions in attestation token decoder.
+ *
+ * This decodes and verifies an attestation token giving access to the
+ * data items in the token. The data items are also known as claims.
+ *
+ * This is written primarily as tests for the token encoder, though it
+ * close to a full commercial token decoder. The main thing missing is
+ * a thorough test suite for it. Test before commercial use is
+ * important as this is a parser / decoder and thus subject to attack
+ * by malicious input. It does however, use QCBOR for most base
+ * parsing, and QCBOR is thoroughly tested and commercial.
+ *
+ * This is oriented around the Arm-defined initial attestation token.
+ *
+ * \c uint_fast8_t is used for type and nest levels. They are
+ * 8-bit quantities, but making using uint8_t variables
+ * and parameters can result in bigger, slower code.
+ * \c uint_fast8_t is part of \c <stdint.h>. It is not
+ * used in structures where it is more important to keep
+ * the size smaller.
+ */
+
+
+/**
+ * Compute the bit indicating a claim is present
+ */
+#define CLAIM_PRESENT_BIT(item_index) (0x01U << (item_index))
+
+
+/*
+ * Public function. See attest_token_decode.h
+ */
+void attest_token_decode_init(struct attest_token_decode_context *me,
+                              uint32_t options)
+{
+    memset(me, 0, sizeof(struct attest_token_decode_context));
+    me->options = options;
+    me->last_error = ATTEST_TOKEN_ERR_NO_VALID_TOKEN;
+}
+
+/*
+ * Public function. See attest_token_decode.h
+ */
+enum attest_token_err_t
+attest_token_decode_get_bstr(struct attest_token_decode_context *me,
+                             int32_t label,
+                             struct q_useful_buf_c *claim)
+{
+    enum attest_token_err_t return_value;
+    QCBORItem               item;
+
+    if(me->last_error != ATTEST_TOKEN_ERR_SUCCESS) {
+        return_value = me->last_error;
+        *claim = NULL_Q_USEFUL_BUF_C;
+        goto Done;
+    }
+
+    return_value = qcbor_util_get_top_level_item_in_map(me->payload,
+                                                       label,
+                                                       QCBOR_TYPE_BYTE_STRING,
+                                                       &item);
+    if(return_value != ATTEST_TOKEN_ERR_SUCCESS) {
+        goto Done;
+    }
+
+    *claim = item.val.string;
+
+Done:
+    return return_value;
+}
+
+
+/*
+ * Public function. See attest_token_decode.h
+ */
+enum attest_token_err_t
+attest_token_decode_get_tstr(struct attest_token_decode_context *me,
+                             int32_t label,
+                             struct q_useful_buf_c *claim)
+{
+    enum attest_token_err_t return_value;
+    QCBORItem               item;
+
+    if(me->last_error != ATTEST_TOKEN_ERR_SUCCESS) {
+        return_value = me->last_error;
+        *claim = NULL_Q_USEFUL_BUF_C;
+        goto Done;
+    }
+
+    return_value = qcbor_util_get_top_level_item_in_map(me->payload,
+                                                       label,
+                                                       QCBOR_TYPE_TEXT_STRING,
+                                                       &item);
+    if(return_value != ATTEST_TOKEN_ERR_SUCCESS) {
+        goto Done;
+    }
+
+    *claim = item.val.string;
+
+Done:
+    return return_value;
+}
+
+
+/*
+ * Public function. See attest_token_decode.h
+ */
+enum attest_token_err_t
+attest_token_decode_get_int(struct attest_token_decode_context *me,
+                            int32_t label,
+                            int64_t *integer)
+{
+    enum attest_token_err_t return_value;
+    QCBORItem               item;
+    QCBORDecodeContext      decode_context;
+
+    if(me->last_error != ATTEST_TOKEN_ERR_SUCCESS) {
+        return_value = me->last_error;
+        *integer = 0;
+        goto Done;
+    }
+
+    QCBORDecode_Init(&decode_context, me->payload, QCBOR_DECODE_MODE_NORMAL);
+
+    return_value = qcbor_util_get_item_in_map(&decode_context,
+                                             label,
+                                             &item);
+    if(return_value != ATTEST_TOKEN_ERR_SUCCESS) {
+        goto Done;
+    }
+
+    if(QCBORDecode_Finish(&decode_context)) {
+        return_value = ATTEST_TOKEN_ERR_CBOR_STRUCTURE;
+    }
+
+    if(item.uDataType == QCBOR_TYPE_INT64) {
+        *integer = item.val.int64;
+    } else if(item.uDataType == QCBOR_TYPE_UINT64) {
+        if(item.val.uint64 < INT64_MAX) {
+            *integer = (int64_t)item.val.uint64;
+        } else {
+            return_value = ATTEST_TOKEN_ERR_INTEGER_VALUE;
+        }
+    } else {
+        return_value = ATTEST_TOKEN_ERR_CBOR_TYPE;
+    }
+
+Done:
+    return return_value;
+}
+
+
+/*
+ * Public function. See attest_token_decode.h
+ */
+enum attest_token_err_t
+attest_token_decode_get_uint(struct attest_token_decode_context *me,
+                             int32_t label,
+                             uint64_t *integer)
+{
+    enum attest_token_err_t return_value;
+    QCBORItem               item;
+    QCBORDecodeContext      decode_context;
+
+    if(me->last_error != ATTEST_TOKEN_ERR_SUCCESS) {
+        return_value = me->last_error;
+        *integer = 0;
+        goto Done;
+    }
+
+    QCBORDecode_Init(&decode_context, me->payload, QCBOR_DECODE_MODE_NORMAL);
+
+    return_value = qcbor_util_get_item_in_map(&decode_context,
+                                             label,
+                                             &item);
+    if(return_value != 0) {
+        goto Done;
+    }
+
+    if(QCBORDecode_Finish(&decode_context)) {
+        return_value = ATTEST_TOKEN_ERR_CBOR_STRUCTURE;
+    }
+
+    if(item.uDataType == QCBOR_TYPE_UINT64) {
+        *integer = item.val.uint64;
+    } else if(item.uDataType == QCBOR_TYPE_INT64) {
+        if(item.val.int64 >= 0) {
+            *integer = (uint64_t)item.val.int64;
+        } else {
+            return_value = ATTEST_TOKEN_ERR_INTEGER_VALUE;
+        }
+    } else {
+        return_value = ATTEST_TOKEN_ERR_CBOR_TYPE;
+    }
+
+Done:
+    return return_value;
+}
+
+
+/*
+ * Public function. See attest_token_decode.h
+ */
+enum attest_token_err_t
+attest_token_decode_get_payload(struct attest_token_decode_context *me,
+                                struct q_useful_buf_c *payload)
+{
+    enum attest_token_err_t return_value;
+
+    if(me->last_error != ATTEST_TOKEN_ERR_SUCCESS) {
+        return_value = me->last_error;
+        *payload = NULL_Q_USEFUL_BUF_C;
+        goto Done;
+    }
+
+    if(q_useful_buf_c_is_null_or_empty(me->payload)) {
+        return_value = ATTEST_TOKEN_ERR_NO_VALID_TOKEN;
+        goto Done;
+    }
+
+    *payload = me->payload;
+    return_value = ATTEST_TOKEN_ERR_SUCCESS;
+
+Done:
+    return return_value;
+}
+
+
+/*
+ * Public function. See attest_token_decode.h
+ */
+enum attest_token_err_t
+attest_token_decode_get_iat_simple(struct attest_token_decode_context *me,
+                                   struct attest_token_iat_simple_t *items)
+{
+    struct qcbor_util_items_to_get_t  list[NUMBER_OF_ITEMS+1];
+    QCBORDecodeContext                decode_context;
+    int64_t                           client_id_64;
+    enum attest_token_err_t           return_value;
+
+    /* Set all q_useful_bufs to NULL and flags to 0 */
+    memset(items, 0, sizeof(struct attest_token_iat_simple_t));
+
+    /* Re use flags as array indexes because it works nicely */
+    list[NONCE_FLAG].label              = EAT_CBOR_ARM_LABEL_CHALLENGE;
+    list[UEID_FLAG].label               = EAT_CBOR_ARM_LABEL_UEID;
+    list[BOOT_SEED_FLAG].label          = EAT_CBOR_ARM_LABEL_BOOT_SEED;
+    list[HW_VERSION_FLAG].label         = EAT_CBOR_ARM_LABEL_HW_VERSION;
+    list[IMPLEMENTATION_ID_FLAG].label  = EAT_CBOR_ARM_LABEL_IMPLEMENTATION_ID;
+    list[CLIENT_ID_FLAG].label          = EAT_CBOR_ARM_LABEL_CLIENT_ID;
+    list[SECURITY_LIFECYCLE_FLAG].label = EAT_CBOR_ARM_LABEL_SECURITY_LIFECYCLE;
+    list[PROFILE_DEFINITION_FLAG].label = EAT_CBOR_ARM_LABEL_PROFILE_DEFINITION;
+    list[ORIGINATION_FLAG].label        = EAT_CBOR_ARM_LABEL_ORIGINATION;
+    list[NUMBER_OF_ITEMS].label         = 0; /* terminate the list. */
+
+    if(me->last_error != ATTEST_TOKEN_ERR_SUCCESS) {
+        return_value = me->last_error;
+        goto Done;
+    }
+
+    QCBORDecode_Init(&decode_context, me->payload, QCBOR_DECODE_MODE_NORMAL);
+
+    return_value = qcbor_util_get_items_in_map(&decode_context,
+                                              list);
+    if(return_value != ATTEST_TOKEN_ERR_SUCCESS) {
+        goto Done;
+    }
+
+    /* ---- NONCE ---- */
+    if(list[NONCE_FLAG].item.uDataType == QCBOR_TYPE_BYTE_STRING) {
+        items->nonce = list[NONCE_FLAG].item.val.string;
+        items->item_flags |= CLAIM_PRESENT_BIT(NONCE_FLAG);
+    }
+
+    /* ---- UEID -------*/
+    if(list[UEID_FLAG].item.uDataType == QCBOR_TYPE_BYTE_STRING) {
+        items->ueid = list[UEID_FLAG].item.val.string;
+        items->item_flags |= CLAIM_PRESENT_BIT(UEID_FLAG);
+    }
+
+    /* ---- BOOT SEED -------*/
+    if(list[BOOT_SEED_FLAG].item.uDataType ==  QCBOR_TYPE_BYTE_STRING) {
+        items->boot_seed = list[BOOT_SEED_FLAG].item.val.string;
+        items->item_flags |= CLAIM_PRESENT_BIT(BOOT_SEED_FLAG);\
+    }
+
+    /* ---- HW VERSION -------*/
+    if(list[HW_VERSION_FLAG].item.uDataType == QCBOR_TYPE_TEXT_STRING) {
+        items->hw_version = list[HW_VERSION_FLAG].item.val.string;
+        items->item_flags |= CLAIM_PRESENT_BIT(HW_VERSION_FLAG);
+
+    }
+
+    /* ----IMPLEMENTATION ID -------*/
+    if(list[IMPLEMENTATION_ID_FLAG].item.uDataType == QCBOR_TYPE_BYTE_STRING) {
+        items->implementation_id = list[IMPLEMENTATION_ID_FLAG].item.val.string;
+        items->item_flags |= CLAIM_PRESENT_BIT(IMPLEMENTATION_ID_FLAG);
+    }
+
+    /* ----CLIENT ID -------*/
+    if(list[CLIENT_ID_FLAG].item.uDataType == QCBOR_TYPE_INT64) {
+        client_id_64 = list[CLIENT_ID_FLAG].item.val.int64;
+        if(client_id_64 < INT32_MAX || client_id_64 > INT32_MIN) {
+            items->client_id = (int32_t)client_id_64;
+            items->item_flags |= CLAIM_PRESENT_BIT(CLIENT_ID_FLAG);
+        }
+    }
+
+    /* ----SECURITY LIFECYCLE -------*/
+    if(list[SECURITY_LIFECYCLE_FLAG].item.uDataType == QCBOR_TYPE_INT64) {
+        if(list[SECURITY_LIFECYCLE_FLAG].item.val.int64 < UINT32_MAX) {
+            items->security_lifecycle =
+                (uint32_t)list[SECURITY_LIFECYCLE_FLAG].item.val.int64;
+            items->item_flags |=CLAIM_PRESENT_BIT(SECURITY_LIFECYCLE_FLAG);
+        }
+    }
+
+    /* ---- PROFILE_DEFINITION -------*/
+    if(list[PROFILE_DEFINITION_FLAG].item.uDataType == QCBOR_TYPE_TEXT_STRING) {
+        items->profile_definition=list[PROFILE_DEFINITION_FLAG].item.val.string;
+        items->item_flags |= CLAIM_PRESENT_BIT(PROFILE_DEFINITION_FLAG);
+    }
+
+    /* ---- ORIGINATION -------*/
+    if(list[ORIGINATION_FLAG].item.uDataType == QCBOR_TYPE_TEXT_STRING) {
+        items->origination = list[ORIGINATION_FLAG].item.val.string;
+        items->item_flags |= CLAIM_PRESENT_BIT(ORIGINATION_FLAG);
+    }
+
+Done:
+    return return_value;
+}
+
+
+/*
+ * Public function. See attest_token_decode.h
+ */
+enum attest_token_err_t
+attest_token_get_num_sw_components(struct attest_token_decode_context *me,
+                                   uint32_t *num_sw_components)
+{
+    enum attest_token_err_t return_value;
+    QCBORItem               item;
+
+    if(me->last_error != ATTEST_TOKEN_ERR_SUCCESS) {
+        return_value = me->last_error;
+        goto Done;
+    }
+
+    return_value = qcbor_util_get_top_level_item_in_map(me->payload,
+                                        EAT_CBOR_ARM_LABEL_SW_COMPONENTS,
+                                        QCBOR_TYPE_ARRAY,
+                                        &item);
+    if(return_value != ATTEST_TOKEN_ERR_SUCCESS) {
+        if(return_value != ATTEST_TOKEN_ERR_NOT_FOUND) {
+            /* Something very wrong. Bail out passing on the return_value */
+            goto Done;
+        } else {
+            /* Now decide if it was intentionally left out. */
+            return_value = qcbor_util_get_top_level_item_in_map(me->payload,
+                                          EAT_CBOR_ARM_LABEL_NO_SW_COMPONENTS,
+                                          QCBOR_TYPE_INT64,
+                                          &item);
+            if(return_value == ATTEST_TOKEN_ERR_SUCCESS) {
+                if(item.val.int64 == NO_SW_COMPONENT_FIXED_VALUE) {
+                    /* Successful omission of SW components. Pass on the
+                     * success return_value */
+                    *num_sw_components = 0;
+                } else {
+                    /* Indicator for no SW components malformed */
+                    return_value = ATTEST_TOKEN_ERR_SW_COMPONENTS_MISSING;
+                }
+            } else if(return_value == ATTEST_TOKEN_ERR_NOT_FOUND) {
+                /* Should have been an indicator for no SW components */
+                return_value = ATTEST_TOKEN_ERR_SW_COMPONENTS_MISSING;
+            }
+        }
+    } else {
+        /* The SW components claim exists */
+        if(item.val.uCount == 0) {
+            /* Empty SW component not allowed */
+            return_value = ATTEST_TOKEN_ERR_SW_COMPONENTS_MISSING;
+        } else {
+            /* SUCESSS! Pass on the success return_value */
+            /* Note that this assumes the array is definite length */
+            *num_sw_components = item.val.uCount;
+        }
+    }
+
+Done:
+    return return_value;
+}
+
+
+/**
+ * \brief Decode a single SW component
+ *
+ * \param[in] decode_context    The CBOR decoder context to decode from
+ * \param[in] sw_component_item The top-level map item for this SW
+ *                              component.
+ * \param[out] sw_component     The structure to fill in with decoded data.
+ *
+ * \return An error from \ref attest_token_err_t.
+ *
+ */
+static inline enum attest_token_err_t
+decode_sw_component(QCBORDecodeContext               *decode_context,
+                    const QCBORItem                  *sw_component_item,
+                    struct attest_token_sw_component_t *sw_component)
+{
+    enum attest_token_err_t return_value;
+    QCBORItem claim_item;
+    QCBORError cbor_error;
+    uint_fast8_t next_nest_level; /* nest levels are 8-bit, but a uint8_t
+                                     var is often slower and more code */
+
+    if(sw_component_item->uDataType != QCBOR_TYPE_MAP) {
+        return_value = ATTEST_TOKEN_ERR_CBOR_STRUCTURE;
+        goto Done;
+    }
+
+    /* Zero it, setting booleans to false, pointers to NULL and
+       lengths to 0 */
+    memset(sw_component, 0, sizeof(struct attest_token_sw_component_t));
+
+    return_value = ATTEST_TOKEN_ERR_SUCCESS;
+
+    while(1) {
+        cbor_error = QCBORDecode_GetNext(decode_context, &claim_item);
+        if(cbor_error != QCBOR_SUCCESS) {
+            /* no tolerance for any errors here */
+            return_value = ATTEST_TOKEN_ERR_CBOR_NOT_WELL_FORMED;
+            goto Done;
+        }
+
+        if(claim_item.uLabelType == QCBOR_TYPE_INT64) {
+            switch(claim_item.label.int64) {
+            case EAT_CBOR_SW_COMPONENT_MEASUREMENT_TYPE:
+                if(claim_item.uDataType != QCBOR_TYPE_TEXT_STRING) {
+                    return_value = ATTEST_TOKEN_ERR_CBOR_TYPE;
+                    goto Done;
+                }
+                sw_component->measurement_type = claim_item.val.string;
+                sw_component->item_flags |=
+                    CLAIM_PRESENT_BIT(SW_MEASUREMENT_TYPE_FLAG);
+
+                break;
+
+            case EAT_CBOR_SW_COMPONENT_MEASUREMENT_VALUE:
+                if(claim_item.uDataType != QCBOR_TYPE_BYTE_STRING) {
+                    return_value = ATTEST_TOKEN_ERR_CBOR_TYPE;
+                    goto Done;
+                }
+                sw_component->measurement_val = claim_item.val.string;
+                sw_component->item_flags |=
+                    CLAIM_PRESENT_BIT(SW_MEASURMENT_VAL_FLAG);
+                break;
+
+            case EAT_CBOR_SW_COMPONENT_VERSION:
+                if(claim_item.uDataType != QCBOR_TYPE_TEXT_STRING) {
+                    return_value = ATTEST_TOKEN_ERR_CBOR_TYPE;
+                    goto Done;
+                }
+                sw_component->version = claim_item.val.string;
+                sw_component->item_flags |=
+                    CLAIM_PRESENT_BIT(SW_VERSION_FLAG);
+                break;
+
+            case EAT_CBOR_SW_COMPONENT_SIGNER_ID:
+                if(claim_item.uDataType != QCBOR_TYPE_BYTE_STRING) {
+                    return_value = ATTEST_TOKEN_ERR_CBOR_TYPE;
+                    goto Done;
+                }
+                sw_component->signer_id = claim_item.val.string;
+                sw_component->item_flags |=
+                    CLAIM_PRESENT_BIT(SW_SIGNER_ID_FLAG);
+                break;
+
+            case EAT_CBOR_SW_COMPONENT_MEASUREMENT_DESC:
+                if(claim_item.uDataType != QCBOR_TYPE_TEXT_STRING) {
+                    return_value = ATTEST_TOKEN_ERR_CBOR_TYPE;
+                    goto Done;
+                }
+                sw_component->measurement_desc = claim_item.val.string;
+                sw_component->item_flags |=
+                    CLAIM_PRESENT_BIT(SW_MEASUREMENT_DESC_FLAG);
+                break;
+            }
+        }
+
+        if(qcbor_util_consume_item(decode_context,
+                                  &claim_item,
+                                  &next_nest_level)) {
+            return_value = ATTEST_TOKEN_ERR_CBOR_NOT_WELL_FORMED;
+            goto Done;
+        }
+        if(next_nest_level < sw_component_item->uNextNestLevel) {
+            /* Got all the items in the map */
+            break;
+        }
+    }
+
+Done:
+    return return_value;
+}
+
+
+/*
+ * Public function. See attest_token_decode.h
+ */
+enum attest_token_err_t
+attest_token_get_sw_component(struct attest_token_decode_context *me,
+                              uint32_t requested_index,
+                              struct attest_token_sw_component_t *sw_components)
+{
+    enum attest_token_err_t return_value;
+    QCBORItem               sw_components_array_item;
+    QCBORDecodeContext      decode_context;
+    QCBORItem               sw_component_item;
+    QCBORError              qcbor_error;
+    uint_fast8_t            exit_array_level;
+
+    if(me->last_error != ATTEST_TOKEN_ERR_SUCCESS) {
+        return_value = me->last_error;
+        goto Done;
+    }
+
+    QCBORDecode_Init(&decode_context, me->payload, QCBOR_DECODE_MODE_NORMAL);
+
+    /* Find the map containing all the SW Components */
+    return_value = qcbor_util_decode_to_labeled_item(&decode_context,
+                                              EAT_CBOR_ARM_LABEL_SW_COMPONENTS,
+                                              &sw_components_array_item);
+    if(return_value != ATTEST_TOKEN_ERR_SUCCESS) {
+        goto Done;
+    }
+
+    if(sw_components_array_item.uDataType != QCBOR_TYPE_ARRAY) {
+        return_value = ATTEST_TOKEN_ERR_CBOR_TYPE;
+        goto Done;
+    }
+
+    exit_array_level = sw_components_array_item.uNextNestLevel;
+
+    /* Loop over contents of SW Components array */
+    while(1) {
+        qcbor_error = QCBORDecode_GetNext(&decode_context, &sw_component_item);
+        if(qcbor_error) {
+            /* no tolerance for any errors here */
+            return_value = ATTEST_TOKEN_ERR_CBOR_NOT_WELL_FORMED;
+            goto Done;
+        }
+
+        if(sw_component_item.uNextNestLevel <= exit_array_level) {
+            /* Next item will be outside the array */
+            return_value = ATTEST_TOKEN_ERR_NOT_FOUND;
+            /* The end of the array containing SW components
+               and didn't get to the requested_index. */
+            goto Done;
+        }
+
+        if(requested_index == 0) {
+            /* Found the one of interest. Decode it and break out */
+            return_value = decode_sw_component(&decode_context,
+                                               &sw_component_item,
+                                               sw_components);
+            break; /* The normal, non-error exit from this loop */
+        }
+
+        /* Every member in the array counts even if they are not
+         * what is expected */
+        requested_index--;
+
+        if(qcbor_util_consume_item(&decode_context, &sw_component_item, NULL)) {
+            return_value = ATTEST_TOKEN_ERR_CBOR_NOT_WELL_FORMED;
+            goto Done;
+        }
+    }
+
+Done:
+    return return_value;
+}
diff --git a/test/suites/attestation/attest_token_test.c b/test/suites/attestation/attest_token_test.c
new file mode 100644
index 0000000..52fc9aa
--- /dev/null
+++ b/test/suites/attestation/attest_token_test.c
@@ -0,0 +1,1011 @@
+/*
+ * attest_token_test.c
+ *
+ * Copyright (c) 2018-2019, Laurence Lundblade.
+ * Copyright (c) 2020, Arm Limited.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * See BSD-3-Clause license in README.md
+ */
+
+#include "attest_token_test.h"
+#include "q_useful_buf.h"
+#include "psa/initial_attestation.h"
+#include "attest_token_decode.h"
+#include "attest_token_test_values.h"
+#include "psa/crypto.h"
+
+
+/**
+ * \file attest_token_test.c
+ *
+ * \brief Implementation for attestation token tests.
+ *
+ * This uses \c attest_token_decode for the main full
+ * test case. That in turn uses t_cose_sign1_verify.
+ * These implementations of token decoding and COSE
+ * sig verification are still considered test
+ * code even though they are getting close to
+ * real implementations that could be used for
+ * other than test.
+ *
+ * Most of the code here is for comparing the
+ * values in the token to expected good
+ * values.
+ */
+
+
+/**
+ * \brief An alternate token_main() that packs the option flags into the nonce.
+ *
+ * \param[in] option_flags      Flag bits to pack into nonce.
+ * \param[in] nonce             Pointer and length of the nonce.
+ * \param[in] buffer            Pointer and length of buffer to
+ *                              output the token into.
+ * \param[out] completed_token  Place to put pointer and length
+ *                              of completed token.
+ *
+ * \return various errors. See \ref psa_status_t.
+ *
+ */
+int token_main_alt(uint32_t option_flags,
+                   struct q_useful_buf_c nonce,
+                   struct q_useful_buf buffer,
+                   struct q_useful_buf_c *completed_token)
+{
+    psa_status_t                 return_value;
+    size_t                       token_buf_size;
+    size_t                       completed_token_size;
+    struct q_useful_buf_c        actual_nonce;
+    Q_USEFUL_BUF_MAKE_STACK_UB(  actual_nonce_storage, 64);
+
+    if(nonce.len == 64 && q_useful_buf_is_value(nonce, 0)) {
+        /* Go into special option-packed nonce mode */
+        actual_nonce = q_useful_buf_copy(actual_nonce_storage, nonce);
+        /* Use memcpy as it always works and avoids type punning */
+        memcpy((uint8_t *)actual_nonce_storage.ptr,
+               &option_flags,
+               sizeof(uint32_t));
+    } else {
+        actual_nonce = nonce;
+    }
+
+    token_buf_size = buffer.len;
+    return_value = psa_initial_attest_get_token(actual_nonce.ptr,
+                                                actual_nonce.len,
+                                                buffer.ptr,
+                                                token_buf_size,
+                                                &completed_token_size);
+
+    *completed_token =
+        (struct q_useful_buf_c){buffer.ptr, completed_token_size};
+
+    if (return_value != PSA_SUCCESS) {
+        return (int)return_value;
+    }
+
+    return 0;
+}
+
+#ifdef INCLUDE_TEST_CODE /* Remove them from release build */
+#ifdef SYMMETRIC_INITIAL_ATTESTATION
+/**
+ * This is the expected output for the minimal test. It is the result
+ * of creating a token with \ref TOKEN_OPT_SHORT_CIRCUIT_SIGN and \ref
+ * TOKEN_OPT_OMIT_CLAIMS set. The nonce is the above constant string
+ * \ref nonce_bytes.  The token output is completely deterministic.
+ *
+ *     17(
+ *       [
+ *           / protected / h'A10105' / {
+ *               \ alg \ 1:5 \ HMAC-SHA256 \
+ *             } /,
+ *           / unprotected / {},
+ *           / payload / h'A13A000124FF5840000000C0000000000000000000000
+ *           00000000000000000000000000000000000000000000000000000000000
+ *           0000000000000000000000000000000000000000' / {
+ *               / arm_psa_nonce / -75008: h'000000C00000000000000000000
+ *               0000000000000000000000000000000000000000000000000000000
+ *               0000000000000000000000000000000000000000000000,
+ *           } /,
+ *           / tag / h'966840FC0A60AE968F906D7092E57B205D3BBE83ED47EBBC2
+ *           AD9D1CFB41C87F3'
+ *       ]
+ *     )
+ *
+ * The above is in CBOR Diagnostic notation. See RFC 8152.
+ */
+static const uint8_t expected_minimal_token_bytes[] = {
+    0xD1, 0x84, 0x43, 0xA1, 0x01, 0x05, 0xA0, 0x58,
+    0x48, 0xA1, 0x3A, 0x00, 0x01, 0x24, 0xFF, 0x58,
+    0x40, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x58, 0x20, 0x96, 0x68, 0x40, 0xFC, 0x0A,
+    0x60, 0xAE, 0x96, 0x8F, 0x90, 0x6D, 0x70, 0x92,
+    0xE5, 0x7B, 0x20, 0x5D, 0x3B, 0xBE, 0x83, 0xED,
+    0x47, 0xEB, 0xBC, 0x2A, 0xD9, 0xD1, 0xCF, 0xB4,
+    0x1C, 0x87, 0xF3
+};
+#else /* SYMMETRIC_INITIAL_ATTESTATION */
+/**
+ * This is the expected output for the minimal test. It is the result
+ * of creating a token with \ref TOKEN_OPT_SHORT_CIRCUIT_SIGN and \ref
+ * TOKEN_OPT_OMIT_CLAIMS set. The nonce is the above constant string
+ * \ref nonce_bytes.  The token output is completely deterministic.
+ *
+ * The implementation of TOKEN_OPT_SHORT_CIRCUIT_SIGN always uses the
+ * kid
+ * EF954B4BD9BDF670D0336082F5EF152AF8F35B6A6C00EFA6A9A71F49517E18C6.
+ *
+ *     18(
+ *       [
+ *           h'A10126', // protected headers
+ *           { // unprotected headers
+ *               4: h'EF954B4BD9BDF670D0336082F5EF152AF8F35B6A6C00EFA6A9
+ *                    A71F49517E18C6'
+ *           },
+ *           h'A13A000124FF5840000000C0000000000000000000000000000000000
+ *             000000000000000000000000000000000000000000000000000000000
+ *             000000000000000000000000000000',
+ *           h'CE52E46D564F1A6DBCEE106341CC80CDC0A3480999AFA8067747CA255
+ *             EEDFD8BCE52E46D564F1A6DBCEE106341CC80CDC0A3480999AFA80677
+ *             47CA255EEDFD8B'
+ *       ]
+ *     )
+ *
+ * The above is in CBOR Diagnostic notation. See RFC 8152.
+ */
+static const uint8_t expected_minimal_token_bytes[] = {
+    0xD2, 0x84, 0x43, 0xA1, 0x01, 0x26, 0xA1, 0x04,
+    0x58, 0x20, 0xEF, 0x95, 0x4B, 0x4B, 0xD9, 0xBD,
+    0xF6, 0x70, 0xD0, 0x33, 0x60, 0x82, 0xF5, 0xEF,
+    0x15, 0x2A, 0xF8, 0xF3, 0x5B, 0x6A, 0x6C, 0x00,
+    0xEF, 0xA6, 0xA9, 0xA7, 0x1F, 0x49, 0x51, 0x7E,
+    0x18, 0xC6, 0x58, 0x48, 0xA1, 0x3A, 0x00, 0x01,
+    0x24, 0xFF, 0x58, 0x40, 0x00, 0x00, 0x00, 0xC0,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x58, 0x40, 0x45, 0x0B,
+    0x2C, 0x09, 0x68, 0xA1, 0x92, 0xA8, 0x85, 0xBE,
+    0x59, 0xE5, 0xA0, 0x9B, 0xDA, 0x4A, 0x8B, 0xA3,
+    0xA6, 0xFC, 0x7F, 0x51, 0x90, 0x35, 0x2D, 0x3A,
+    0x16, 0xBC, 0x30, 0x7B, 0x50, 0x3D, 0x45, 0x0B,
+    0x2C, 0x09, 0x68, 0xA1, 0x92, 0xA8, 0x85, 0xBE,
+    0x59, 0xE5, 0xA0, 0x9B, 0xDA, 0x4A, 0x8B, 0xA3,
+    0xA6, 0xFC, 0x7F, 0x51, 0x90, 0x35, 0x2D, 0x3A,
+    0x16, 0xBC, 0x30, 0x7B, 0x50, 0x3D
+};
+#endif /* SYMMETRIC_INITIAL_ATTESTATION */
+
+
+/*
+ * Public function. See token_test.h
+ */
+int_fast16_t minimal_test()
+{
+    int_fast16_t return_value = 0;
+    Q_USEFUL_BUF_MAKE_STACK_UB(token_storage,
+                               sizeof(expected_minimal_token_bytes));
+    struct q_useful_buf_c completed_token;
+    struct q_useful_buf_c expected_token;
+
+    return_value =
+        token_main_alt(TOKEN_OPT_SHORT_CIRCUIT_SIGN |
+                           TOKEN_OPT_OMIT_CLAIMS,
+                       TOKEN_TEST_VALUE_NONCE,
+                       token_storage,
+                       &completed_token);
+    if(return_value) {
+        goto Done;
+    }
+
+    expected_token =
+        Q_USEFUL_BUF_FROM_BYTE_ARRAY_LITERAL(expected_minimal_token_bytes);
+
+    if(q_useful_buf_compare(completed_token, expected_token)) {
+       return_value = -3;
+    }
+
+Done:
+    return return_value;
+}
+
+
+/*
+ * Public function. See token_test.h
+ */
+int_fast16_t minimal_get_size_test()
+{
+    int_fast16_t          return_value = 0;
+    size_t                length;
+    struct q_useful_buf_c expected_token;
+    struct q_useful_buf_c nonce;
+
+    nonce = TOKEN_TEST_VALUE_NONCE;
+    expected_token =
+        Q_USEFUL_BUF_FROM_BYTE_ARRAY_LITERAL(expected_minimal_token_bytes);
+
+
+    return_value = psa_initial_attest_get_token_size(nonce.len,
+                                                     &length);
+
+    /*
+     * It is not possible to predict the size of the token returned
+     * here because options like TOKEN_OPT_OMIT_CLAIMS and
+     * TOKEN_OPT_SHORT_CIRCUIT_SIGN cannot be passed to limit what it
+     * does. Instead check to see if the size is in a reasonable
+     * range. The minimal_test() will actually check the size for
+     * exactitude because it can pass those options,
+     */
+    if(length < expected_token.len || length > 10000) {
+        return_value = -1;
+    }
+
+    return return_value;
+}
+
+
+/*
+ * Public function. See token_test.h
+ */
+int_fast16_t buffer_too_small_test()
+{
+    int_fast16_t return_value = 0;
+    /*
+     * Construct a buffer with enough capacity in case buffer size check fails
+     * and the token is generated. If a smaller buffer is allocated, the
+     * incorrectly generated token may overwrite and corrupt the data following
+     * this buffer.
+     */
+    Q_USEFUL_BUF_MAKE_STACK_UB(token_storage,
+                               sizeof(expected_minimal_token_bytes));
+    struct q_useful_buf_c completed_token;
+    struct q_useful_buf_c nonce;
+
+
+    nonce = TOKEN_TEST_VALUE_NONCE;
+    /* Fake the size and cheat the token generation process. */
+    token_storage.len = sizeof(expected_minimal_token_bytes) - 1;
+
+    return_value = token_main_alt(TOKEN_OPT_SHORT_CIRCUIT_SIGN |
+                                      TOKEN_OPT_OMIT_CLAIMS,
+                                  nonce,
+                                  token_storage,
+                                  &completed_token);
+
+    if(return_value != PSA_ERROR_BUFFER_TOO_SMALL) {
+        return_value = -1;
+    } else {
+        return_value = 0;
+    }
+
+    return return_value;
+}
+#endif /* INCLUDE_TEST_CODE */
+
+
+/**
+ * \brief Check the simple IAT claims against compiled-in known values
+ *
+ * \param[in] simple_claims Pointer to claims structure to check
+ *
+ * \return 0 means success. Any other value indicates failure. Since
+ *           this is just test code there is no formal definition or
+ *           documentation of the detailed values on the assumption
+ *           that source code for the test is available.
+ *
+ * The compiled values come from token_test_values.h. They can be
+ * #defined as \c NULL, \c NULL_Q_USEFUL_BUF_C or \c MAX_INT32 to disabled
+ * checking for the presence and value of the claim.
+ *
+ * See \ref attest_token_iat_simple_t for the claims that are checked. All
+ * claims in that structure are checked.
+ */
+static int_fast16_t check_simple_claims(
+                    const struct attest_token_iat_simple_t *simple_claims)
+{
+    int_fast16_t return_value;
+    /* Use temp variables to make lines less than 80 columns below. */
+    struct q_useful_buf_c tmp;
+    struct q_useful_buf_c tail;
+    /* Use a temporary string variable to make the static analyzer
+     * happy. It doesn't like comparing a string literal to NULL
+     */
+    const char *tmp_string;
+
+    return_value = 0;
+
+    /* -- check value of the nonce claim -- */
+    if(!IS_ITEM_FLAG_SET(NONCE_FLAG, simple_claims->item_flags)) {
+        /* Claim is not present in token */
+        if(TOKEN_TEST_REQUIRE_NONCE) {
+            /* It should have been present */
+            return_value = -50;
+            goto Done;
+        }
+    } else {
+        /* Claim is present */
+        /* Don't have to check if its presence is required */
+        tmp = TOKEN_TEST_VALUE_NONCE;
+        if(!q_useful_buf_c_is_null(tmp)) {
+            /* request to check for the nonce */
+            if(q_useful_buf_compare(simple_claims->nonce, tmp)) {
+                /* Didn't match in the standard way. See if it is a
+                 * special option-packed nonce by checking for length
+                 * 64 and all bytes except the first four are 0.
+                 * nonce_tail is everything after the first 4 bytes.
+                 */
+                tail = q_useful_buf_tail(simple_claims->nonce, 4);
+                if(simple_claims->nonce.len == 64 &&
+                   q_useful_buf_is_value(tail, 0) == SIZE_MAX){
+                    /* It is an option-packed nonce.
+                     * Don't compare the first four bytes.
+                     */
+                    if(q_useful_buf_compare(q_useful_buf_tail(tmp, 4), tail)) {
+                        /* The option-packed nonce didn't match */
+                        return_value = -51;
+                        goto Done;
+                    }
+                } else {
+                    /* Just a normal nonce that didn't match */
+                    return_value = -51;
+                    goto Done;
+                }
+            }
+        }
+    }
+
+    /* -- check value of the UEID claim -- */
+    if(!IS_ITEM_FLAG_SET(UEID_FLAG, simple_claims->item_flags)) {
+        /* Claim is not present in token */
+        if(TOKEN_TEST_REQUIRE_UEID) {
+            /* It should have been present */
+            return_value = -52;
+            goto Done;
+        }
+    } else {
+        /* Claim is present */
+        /* Don't have to check if its presence is required */
+        tmp = TOKEN_TEST_VALUE_UEID;
+        if(!q_useful_buf_c_is_null(tmp) &&
+           q_useful_buf_compare(simple_claims->ueid, tmp)) {
+            /* Check of its value was requested and failed */
+            return_value = -53;
+            goto Done;
+        }
+    }
+
+    /* -- check value of the boot seed claim -- */
+    if(!IS_ITEM_FLAG_SET(BOOT_SEED_FLAG, simple_claims->item_flags)) {
+        /* Claim is not present in token */
+        if(TOKEN_TEST_REQUIRE_BOOT_SEED) {
+            /* It should have been present */
+            return_value = -54;
+            goto Done;
+        }
+    } else {
+        /* Claim is present */
+        /* Don't have to check if its presence is required */
+        tmp = TOKEN_TEST_VALUE_BOOT_SEED;
+        if(!q_useful_buf_c_is_null(tmp) &&
+           q_useful_buf_compare(simple_claims->boot_seed, tmp)) {
+            /* Check of its value was requested and failed */
+            return_value = -55;
+            goto Done;
+        }
+    }
+
+    /* -- check value of the hw_version claim -- */
+    if(!IS_ITEM_FLAG_SET(HW_VERSION_FLAG, simple_claims->item_flags)) {
+        /* Claim is not present in token */
+        if(TOKEN_TEST_REQUIRE_HW_VERSION) {
+            /* It should have been present */
+            return_value = -56;
+            goto Done;
+        }
+    } else {
+        /* Claim is present */
+        /* Don't have to check if its presence is required */
+        tmp_string = TOKEN_TEST_VALUE_HW_VERSION;
+        if(tmp_string != NULL) {
+            tmp = Q_USEFUL_BUF_FROM_SZ_LITERAL(TOKEN_TEST_VALUE_HW_VERSION);
+            if(q_useful_buf_compare(simple_claims->hw_version, tmp)) {
+                /* Check of its value was requested and failed */
+                return_value = -57;
+                goto Done;
+            }
+        }
+    }
+
+    /* -- check value of the implementation ID -- */
+    if(!IS_ITEM_FLAG_SET(IMPLEMENTATION_ID_FLAG,simple_claims->item_flags)) {
+        /* Claim is not present in token */
+        if(TOKEN_TEST_REQUIRE_IMPLEMENTATION_ID) {
+            return_value = -58;
+            goto Done;
+        }
+    } else {
+        /* Claim is present */
+        /* Don't have to check if its presence is required */
+        tmp = TOKEN_TEST_VALUE_IMPLEMENTATION_ID;
+        if(!q_useful_buf_c_is_null(tmp) &&
+           q_useful_buf_compare(simple_claims->implementation_id, tmp)) {
+            /* Check of its value was requested and failed */
+            return_value = -59;
+            goto Done;
+        }
+    }
+
+    /* -- check value of the security lifecycle claim -- */
+    if(!IS_ITEM_FLAG_SET(SECURITY_LIFECYCLE_FLAG,simple_claims->item_flags)) {
+        /* Claim is not present in token */
+        if(TOKEN_TEST_REQUIRE_SECURITY_LIFECYCLE) {
+            /* It should have been present */
+            return_value = -60;
+            goto Done;
+        }
+    } else {
+        /* Claim is present */
+        /* Don't have to check if its presence is required */
+        if(TOKEN_TEST_VALUE_SECURITY_LIFECYCLE != INT32_MAX &&
+           simple_claims->security_lifecycle !=
+           TOKEN_TEST_VALUE_SECURITY_LIFECYCLE) {
+            /* Check of its value was requested and failed */
+            return_value = -61;
+            goto Done;
+        }
+    }
+
+    /* -- check value of the client_id claim -- */
+    if(!IS_ITEM_FLAG_SET(CLIENT_ID_FLAG, simple_claims->item_flags)) {
+        /* Claim is not present in token */
+        if(TOKEN_TEST_REQUIRE_CLIENT_ID) {
+            return_value = -62;
+            goto Done;
+        }
+    } else {
+        /* Claim is present */
+        /* Don't have to check if its presence is required */
+        if(TOKEN_TEST_VALUE_CLIENT_ID != INT32_MAX &&
+           simple_claims->client_id != TOKEN_TEST_VALUE_CLIENT_ID) {
+#if DOMAIN_NS == 1U
+            /* Non-secure caller client ID has to be negative */
+            if(simple_claims->client_id > -1) {
+#else
+            /* Secure caller client ID has to be positive */
+            if(simple_claims->client_id < 1) {
+#endif
+                return_value = -63;
+                goto Done;
+            }
+        }
+    }
+
+    /* -- check value of the profile_definition claim -- */
+    if(!IS_ITEM_FLAG_SET(PROFILE_DEFINITION_FLAG, simple_claims->item_flags)) {
+        /* Claim is not present in token */
+        if(TOKEN_TEST_REQUIRE_PROFILE_DEFINITION) {
+            /* It should have been present */
+            return_value = -64;
+            goto Done;
+        }
+    } else {
+        /* Claim is present */
+        /* Don't have to check if its presence is required */
+        tmp_string = TOKEN_TEST_VALUE_PROFILE_DEFINITION;
+        if(tmp_string != NULL) {
+            tmp = Q_USEFUL_BUF_FROM_SZ_LITERAL(
+                                        TOKEN_TEST_VALUE_PROFILE_DEFINITION);
+            if(q_useful_buf_compare(simple_claims->profile_definition, tmp)) {
+                /* Check of its value was requested and failed */
+                return_value = -65;
+                goto Done;
+            }
+        }
+    }
+
+    /* -- check value of the origination claim -- */
+    if(!IS_ITEM_FLAG_SET(ORIGINATION_FLAG, simple_claims->item_flags)) {
+        /* Claim is not present in token */
+        if(TOKEN_TEST_REQUIRE_ORIGINATION) {
+            /* It should have been present */
+            return_value = -66;
+            goto Done;
+        }
+    } else {
+        /* Claim is present */
+        /* Don't have to check if its presence is required */
+        tmp_string = TOKEN_TEST_VALUE_ORIGINATION;
+        if(tmp_string != NULL) {
+            tmp = Q_USEFUL_BUF_FROM_SZ_LITERAL(TOKEN_TEST_VALUE_ORIGINATION);
+            if(q_useful_buf_compare(simple_claims->origination, tmp)) {
+                /* Check of its value was requested and failed */
+                return_value = -67;
+                goto Done;
+            }
+        }
+    }
+
+Done:
+    return return_value;
+}
+
+
+/**
+ * \brief Check a SW component claims against compiled-in known values.
+ *
+ * \param[in] sw_component Pointer to SW component claims structure to check.
+ *
+ * \return 0 means success. Any other value indicates failure. Since
+ *         this is just test code there is no formal definition or
+ *         documentation of the detailed values on the assumption
+ *         that source code for the test is available.
+ *
+ * The compiled values come from token_test_values.h. They can be
+ * #defined as \c NULL, \c NULL_Q_USEFUL_BUF_C or \c MAX_INT32 to
+ * disabled checking for the presence and value of the claim.
+ *
+ * See \ref attest_token_sw_component_t for the claims that are checked. All
+ * claims in that structure are checked.
+ *
+ * This checks for the "first" SW component. See check_sw_component_2().
+ */
+static int_fast16_t check_sw_component_1(
+                    const struct attest_token_sw_component_t *sw_component)
+{
+    int_fast16_t return_value;
+    /* Use a temp variable to make lines less than 80 columns below. */
+    struct q_useful_buf_c tmp;
+    /* Use a temporary string variable to make the static analyzer
+     * happy. It doesn't like comparing a string literal to NULL
+     */
+    const char *tmp_string;
+
+    return_value = 0;
+
+    /* -- Check first type -- */
+    if(!IS_ITEM_FLAG_SET(SW_MEASUREMENT_TYPE_FLAG, sw_component->item_flags)) {
+        /* Claim is not present in token */
+        if(TOKEN_TEST_REQUIRE_SWC1_MEASUREMENT_TYPE) {
+            /* It should have been present */
+            return_value = -100;
+            goto Done;
+        }
+    } else {
+        /* Claim is present */
+        /* Don't have to check if its presence is required */
+        tmp_string = TOKEN_TEST_VALUE_SWC1_MEASUREMENT_TYPE;
+        if(tmp_string != NULL) {
+            tmp = Q_USEFUL_BUF_FROM_SZ_LITERAL(
+                                    TOKEN_TEST_VALUE_SWC1_MEASUREMENT_TYPE);
+            if(q_useful_buf_compare(sw_component->measurement_type, tmp)) {
+                /* Check of its value was requested and failed */
+                return_value = -101;
+                goto Done;
+            }
+        }
+    }
+
+    /* -- Check first measurement -- */
+    if(!IS_ITEM_FLAG_SET(SW_MEASURMENT_VAL_FLAG, sw_component->item_flags)) {
+        /* Claim is not present in token */
+        if(TOKEN_TEST_REQUIRE_SWC1_MEASUREMENT_VAL) {
+            /* It should have been present */
+            return_value = -102;
+            goto Done;
+        }
+    } else {
+        /* Claim is present */
+        /* Don't have to check if its presence is required */
+        tmp = TOKEN_TEST_VALUE_SWC1_MEASUREMENT_VAL;
+        if(!q_useful_buf_c_is_null(tmp) &&
+           q_useful_buf_compare(sw_component->measurement_val, tmp)) {
+            /* Check of its value was requested and failed */
+            return_value = -103;
+            goto Done;
+        }
+    }
+
+    /* -- Check first version -- */
+    if(!IS_ITEM_FLAG_SET(SW_VERSION_FLAG, sw_component->item_flags)) {
+        /* Claim is not present in token */
+        if(TOKEN_TEST_REQUIRE_SWC1_VERSION) {
+            /* It should have been present */
+            return_value = -106;
+            goto Done;
+        }
+    } else {
+        /* Claim is present */
+        /* Don't have to check if its presence is required */
+        tmp_string = TOKEN_TEST_VALUE_SWC1_VERSION;
+        if(tmp_string != NULL) {
+            tmp = Q_USEFUL_BUF_FROM_SZ_LITERAL(TOKEN_TEST_VALUE_SWC1_VERSION);
+            if(q_useful_buf_compare(sw_component->version, tmp)) {
+                /* Check of its value was requested and failed */
+                return_value = -107;
+                goto Done;
+            }
+        }
+    }
+
+    /* -- Check first signer ID -- */
+    if(!IS_ITEM_FLAG_SET(SW_SIGNER_ID_FLAG, sw_component->item_flags)) {
+        /* Claim is not present in token */
+        if(TOKEN_TEST_REQUIRE_SWC1_SIGNER_ID) {
+            /* It should have been present */
+            return_value = -108;
+            goto Done;
+        }
+    } else {
+        /* Claim is present */
+        /* Don't have to check if its presence is required */
+        tmp = TOKEN_TEST_VALUE_SWC1_SIGNER_ID;
+        if(!q_useful_buf_c_is_null(tmp) &&
+           q_useful_buf_compare(sw_component->signer_id, tmp)) {
+            /* Check of its value was requested and failed */
+            return_value = -109;
+            goto Done;
+        }
+    }
+
+    /* -- Check first measurement description -- */
+    if(!IS_ITEM_FLAG_SET(SW_MEASUREMENT_DESC_FLAG, sw_component->item_flags)) {
+        /* Claim is not present in token */
+        if(TOKEN_TEST_REQUIRE_SWC1_MEASUREMENT_DESC) {
+            /* It should have been present */
+            return_value = -110;
+            goto Done;
+        }
+    } else {
+        /* Claim is present */
+        /* Don't have to check if its presence is required */
+        tmp_string = TOKEN_TEST_VALUE_SWC1_MEASUREMENT_DESC;
+        if(tmp_string != NULL) {
+            tmp = Q_USEFUL_BUF_FROM_SZ_LITERAL(
+                                    TOKEN_TEST_VALUE_SWC1_MEASUREMENT_DESC);
+            if(q_useful_buf_compare(sw_component->measurement_desc, tmp)) {
+                /* Check of its value was requested and failed */
+                return_value = -111;
+                goto Done;
+            }
+        }
+    }
+
+ Done:
+    return return_value;
+}
+
+
+/**
+ * \brief Check a SW component claims against compiled-in known values.
+ *
+ * \param[in] sw_component Pointer to SW component claims structure to check.
+ *
+ * \return 0 means success. Any other value indicates failure. Since
+ *         this is just test code there is no formal definition or
+ *         documentation of the detailed values on the assumption
+ *         that source code for the test is available.
+ *
+ * The compiled values come from token_test_values.h. They can be
+ * #defined as \c NULL, \c NULL_Q_USEFUL_BUF_C or \c MAX_INT32 to disabled
+ * checking for the presence and value of the claim.
+ *
+ * See \ref attest_token_sw_component_t for the claims that are checked. All
+ * claims in that structure are checked.
+ *
+ * This checks for the "second" SW component. See check_sw_component_1().
+ */
+static int_fast16_t check_sw_component_2(
+                    const struct attest_token_sw_component_t *sw_component)
+{
+    int_fast16_t return_value;
+
+    /* Use a temp variable to make lines less than 80 columns below. */
+    struct q_useful_buf_c tmp;
+    /* Use a temporary string variable to make the static analyzer
+     * happy. It doesn't like comparing a string literal to NULL
+     */
+    const char *tmp_string;
+
+    return_value = 0;
+
+    /* -- Check second type -- */
+    if(!IS_ITEM_FLAG_SET(SW_MEASUREMENT_TYPE_FLAG, sw_component->item_flags)) {
+        /* Claim is not present in token */
+        if(TOKEN_TEST_REQUIRE_SWC2_MEASUREMENT_TYPE) {
+            /* It should have been present */
+            return_value = -100;
+            goto Done;
+        }
+    } else {
+        /* Claim is present */
+        /* Don't have to check if its presence is required */
+        tmp_string = TOKEN_TEST_VALUE_SWC2_MEASUREMENT_TYPE;
+        if(tmp_string != NULL) {
+            tmp = Q_USEFUL_BUF_FROM_SZ_LITERAL(
+                                    TOKEN_TEST_VALUE_SWC2_MEASUREMENT_TYPE);
+            if(q_useful_buf_compare(sw_component->measurement_type, tmp)) {
+                /* Check of its value was requested and failed */
+                return_value = -101;
+                goto Done;
+            }
+        }
+    }
+
+    /* -- Check second measurement -- */
+    if(!IS_ITEM_FLAG_SET(SW_MEASURMENT_VAL_FLAG, sw_component->item_flags)) {
+        /* Claim is not present in token */
+        if(TOKEN_TEST_REQUIRE_SWC2_MEASUREMENT_VAL) {
+            /* It should have been present */
+            return_value = -102;
+            goto Done;
+        }
+    } else {
+        /* Claim is present */
+        /* Don't have to check if its presence is required */
+        tmp = TOKEN_TEST_VALUE_SWC2_MEASUREMENT_VAL;
+        if(!q_useful_buf_c_is_null(tmp) &&
+           q_useful_buf_compare(sw_component->measurement_val, tmp)) {
+            /* Check of its value was requested and failed */
+            return_value = -103;
+            goto Done;
+        }
+    }
+
+    /* -- Check second version -- */
+    if(!IS_ITEM_FLAG_SET(SW_VERSION_FLAG, sw_component->item_flags)) {
+        /* Claim is not present in token */
+        if(TOKEN_TEST_REQUIRE_SWC2_VERSION) {
+            /* It should have been present */
+            return_value = -106;
+            goto Done;
+        }
+    } else {
+        /* Claim is present */
+        /* Don't have to check if its presence is required */
+        tmp_string = TOKEN_TEST_VALUE_SWC2_VERSION;
+        if(tmp_string != NULL) {
+            tmp = Q_USEFUL_BUF_FROM_SZ_LITERAL(TOKEN_TEST_VALUE_SWC2_VERSION);
+            if(q_useful_buf_compare(sw_component->version, tmp)) {
+                /* Check of its value was requested and failed */
+                return_value = -107;
+                goto Done;
+            }
+        }
+    }
+
+    /* -- Check second signer ID -- */
+    if(!IS_ITEM_FLAG_SET(SW_SIGNER_ID_FLAG, sw_component->item_flags)) {
+        /* Claim is not present in token */
+        if(TOKEN_TEST_REQUIRE_SWC2_SIGNER_ID) {
+            /* It should have been present */
+            return_value = -108;
+            goto Done;
+        }
+    } else {
+        /* Claim is present */
+        /* Don't have to check if its presence is required */
+        tmp = TOKEN_TEST_VALUE_SWC2_SIGNER_ID;
+        if(!q_useful_buf_c_is_null(tmp) &&
+           q_useful_buf_compare(sw_component->signer_id, tmp)) {
+            /* Check of its value was requested and failed */
+            return_value = -109;
+            goto Done;
+        }
+    }
+
+    /* -- Check second measurement description -- */
+    if(!IS_ITEM_FLAG_SET(SW_MEASUREMENT_DESC_FLAG, sw_component->item_flags)) {
+        /* Claim is not present in token */
+        if(TOKEN_TEST_REQUIRE_SWC2_MEASUREMENT_DESC) {
+            /* It should have been present */
+            return_value = -110;
+            goto Done;
+        }
+    } else {
+        /* Claim is present */
+        /* Don't have to check if its presence is required */
+        tmp_string = TOKEN_TEST_VALUE_SWC2_MEASUREMENT_DESC;
+        if(tmp_string != NULL) {
+            tmp = Q_USEFUL_BUF_FROM_SZ_LITERAL(
+                                    TOKEN_TEST_VALUE_SWC2_MEASUREMENT_DESC);
+            if(q_useful_buf_compare(sw_component->measurement_desc, tmp)) {
+                /* Check of its value was requested and failed */
+                return_value = -111;
+                goto Done;
+            }
+        }
+    }
+
+Done:
+    return return_value;
+}
+
+/**
+ * Modes for decode_test_internal()
+ */
+enum decode_test_mode_t {
+    /** See documentation for decode_test_short_circuit_sig() */
+    SHORT_CIRCUIT_SIGN,
+    /** See documentation for decode_test_normal_sig() */
+    NORMAL_SIGN,
+    /** See documentation for decode_test_symmetric_initial_attest() */
+    COSE_MAC0,
+    /** See documentation for decode_test_symmetric_iat_short_circuit_tag() */
+    COSE_MAC0_SHORT_CIRCUIT_TAG
+};
+
+/**
+ * \brief Run the main decode test
+ *
+ * \param[in] mode Selects test modes
+ *
+ * \return 0 for success, test failure code otherwise.
+ *
+ * See decode_test_normal_sig() and decode_test_short_circuit_sig().
+ */
+static int_fast16_t decode_test_internal(enum decode_test_mode_t mode)
+{
+    int_fast16_t                        return_value;
+    Q_USEFUL_BUF_MAKE_STACK_UB(         token_storage, ATTEST_TOKEN_MAX_SIZE);
+    struct q_useful_buf_c               completed_token;
+    struct attest_token_decode_context  token_decode;
+    struct attest_token_iat_simple_t    simple_claims;
+    struct attest_token_sw_component_t  sw_component;
+    uint32_t                            num_sw_components;
+    int32_t                             num_sw_components_signed;
+    struct q_useful_buf_c               tmp;
+    uint32_t                            token_encode_options;
+    uint32_t                            token_decode_options;
+
+    switch(mode) {
+        case SHORT_CIRCUIT_SIGN:
+            token_encode_options = TOKEN_OPT_SHORT_CIRCUIT_SIGN;
+            token_decode_options = TOKEN_OPT_SHORT_CIRCUIT_SIGN;
+            break;
+
+        case NORMAL_SIGN:
+            token_encode_options = 0;
+            token_decode_options = 0;
+            break;
+
+        case COSE_MAC0:
+            token_encode_options = 0;
+            token_decode_options = 0;
+            break;
+
+        case COSE_MAC0_SHORT_CIRCUIT_TAG:
+            token_encode_options = TOKEN_OPT_SHORT_CIRCUIT_SIGN;
+            token_decode_options = TOKEN_OPT_SHORT_CIRCUIT_SIGN;
+            break;
+
+        default:
+            return_value = -1000;
+            goto Done;
+    }
+
+    /* -- Make a token with all the claims -- */
+    tmp = TOKEN_TEST_VALUE_NONCE;
+    return_value = token_main_alt(token_encode_options,
+                                  tmp,
+                                  token_storage,
+                                  &completed_token);
+    if(return_value) {
+        goto Done;
+    }
+
+    /* -- Initialize and validate the signature on the token -- */
+    attest_token_decode_init(&token_decode, token_decode_options);
+    return_value = attest_token_decode_validate_token(&token_decode,
+                                                      completed_token);
+    if(return_value != ATTEST_TOKEN_ERR_SUCCESS) {
+        goto Done;
+    }
+
+    /* -- Get the all simple claims at once and check them -- */
+    return_value = attest_token_decode_get_iat_simple(&token_decode,
+                                                      &simple_claims);
+    if(return_value != ATTEST_TOKEN_ERR_SUCCESS) {
+        goto Done;
+    }
+
+
+    return_value = check_simple_claims(&simple_claims);
+    if(return_value != ATTEST_TOKEN_ERR_SUCCESS) {
+        goto Done;
+    }
+
+    /* -- SW components -- */
+    if(TOKEN_TEST_REQUIRED_NUM_SWC != INT32_MAX) {
+        /* -- Configured to check for SW components, so do that -- */
+
+        /* -- Get num SW components -- */
+        return_value = attest_token_get_num_sw_components(&token_decode,
+                                                          &num_sw_components);
+        if(return_value) {
+            goto Done;
+        }
+        /* This conversion to signed avoids a compiler warning
+         * when TOKEN_TEST_REQUIRED_NUM_SWC is defined as 0 */
+        num_sw_components_signed = (int32_t)num_sw_components;
+        if(num_sw_components_signed < TOKEN_TEST_REQUIRED_NUM_SWC) {
+            return_value = -5;
+            goto Done;
+        }
+
+        if(num_sw_components >= 1) {
+            /* -- Get the first SW component and check it -- */
+            return_value = attest_token_get_sw_component(&token_decode,
+                                                         0,
+                                                         &sw_component);
+            if(return_value) {
+                goto Done;
+            }
+
+            return_value = check_sw_component_1(&sw_component);
+            if(return_value) {
+                goto Done;
+            }
+
+            if(num_sw_components >= 2) {
+                /* -- Get the second SW component and check it -- */
+                return_value = attest_token_get_sw_component(&token_decode,
+                                                             1,
+                                                             &sw_component);
+                if(return_value) {
+                    goto Done;
+                }
+
+                return_value = check_sw_component_2(&sw_component);
+                if(return_value) {
+                    goto Done;
+                }
+            }
+        }
+    }
+    return_value = 0;
+
+Done:
+    return return_value;
+}
+
+#ifdef SYMMETRIC_INITIAL_ATTESTATION
+int_fast16_t decode_test_symmetric_initial_attest(void)
+{
+    return decode_test_internal(COSE_MAC0);
+}
+
+int_fast16_t decode_test_symmetric_iat_short_circuit_tag(void)
+{
+    return decode_test_internal(COSE_MAC0_SHORT_CIRCUIT_TAG);
+}
+#else /* SYMMETRIC_INITIAL_ATTESTATION */
+/*
+ * Public function. See token_test.h
+ */
+int_fast16_t decode_test_short_circuit_sig(void)
+{
+    return decode_test_internal(SHORT_CIRCUIT_SIGN);
+}
+
+
+/*
+ * Public function. See token_test.h
+ */
+int_fast16_t decode_test_normal_sig(void)
+{
+    return decode_test_internal(NORMAL_SIGN);
+}
+#endif /* SYMMETRIC_INITIAL_ATTESTATION */
diff --git a/test/suites/attestation/attest_token_test.h b/test/suites/attestation/attest_token_test.h
new file mode 100644
index 0000000..37b12e0
--- /dev/null
+++ b/test/suites/attestation/attest_token_test.h
@@ -0,0 +1,143 @@
+/*
+ * attest_token_test.h
+ *
+ * Copyright (c) 2018-2019, Laurence Lundblade.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * See BSD-3-Clause license in README.md
+ */
+
+#ifndef __ATTEST_TOKEN_TEST_H__
+#define __ATTEST_TOKEN_TEST_H__
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \file attest_token_test.h
+ *
+ * \brief Entry points for attestation token tests.
+ *
+ * Errors codes are in the range of [-32767,32767] so
+ * int_fast16_t is used so they will work nice
+ * even on 16-bit machines. Plain old int could
+ * also be used, but many compilers make it
+ * 32-bits for backwards compatibility with
+ * SW that assume it is always 32 bits and
+ * it isn't efficient.  (This code has probably
+ * not yet been tested on a 16-bit machines).
+ *
+ * https://stackoverflow.com/questions/30942107/
+ * whats-the-difference-between-int-and-int-fast16-t
+ */
+
+
+/**
+ * \brief Minimal token creation test using a short-circuit signature.
+ *
+ * \return non-zero on failure.
+ */
+int_fast16_t minimal_test(void);
+
+
+/**
+ * \brief Test token size calculation.
+ *
+ * \return non-zero on failure.
+ */
+int_fast16_t minimal_get_size_test(void);
+
+
+/**
+ * \brief Pass too small a buffer and confirm correct error result.
+ *
+ * \return non-zero on failure.
+ */
+int_fast16_t buffer_too_small_test(void);
+
+#ifdef SYMMETRIC_INITIAL_ATTESTATION
+/**
+ * \brief Test by checking token generated by symmetric key algorithms based
+ *        Initial Attestation.
+ *
+ * \return non-zero on failure.
+ *
+ * This is an extensive test that can compare the values in the token
+ * to expected valued compiled into the test app from
+ * token_test_values.h. All the values represented in \ref
+ * attest_token_iat_simple_t and in \ref attest_token_sw_component_t
+ * are checked.
+ *
+ * The generated token will be decoded.
+ * The tag in COSE_Mac0 structure will be verified in secure side when
+ * INCLUDE_TEST_CODE is enabled.
+ */
+int_fast16_t decode_test_symmetric_initial_attest(void);
+
+/**
+ * \brief Test by checking short-circuit tagged values of claims.
+ *
+ * \return non-zero on failure.
+ *
+ * This is an extensive test that can compare the values in the token
+ * to expected valued compiled into the test app from
+ * token_test_values.h. All the values represented in \ref
+ * attest_token_iat_simple_t and in \ref attest_token_sw_component_t
+ * are checked.
+ *
+ * This uses a short-circuit tag rather than real HMAC operation with
+ * symmetric IAK. This tests everything in the implementation except the final
+ * MAC. It can work even without HMAC integration and without
+ * any keys configured.
+ */
+int_fast16_t decode_test_symmetric_iat_short_circuit_tag(void);
+#else /* SYMMETRIC_INITIAL_ATTESTATION */
+/**
+ * \brief Test by checking signed values of claims.
+ *
+ * \return non-zero on failure.
+ *
+ * This is an extensive test that can compare the values in the token
+ * to expected valued compiled into the test app from
+ * token_test_values.h. All the values represented in \ref
+ * attest_token_iat_simple_t and in \ref attest_token_sw_component_t
+ * are checked.
+ *
+ * This uses real ECDSA keys for both signing and verificaiton.  It
+ * requires that the t_cose crypto porting layer operates correctly
+ * and that all keys are present. See also
+ * decode_test_short_circuit_sig().
+ */
+int_fast16_t decode_test_normal_sig(void);
+
+
+/**
+ * \brief Test by checking short-circuit signed values of claims.
+ *
+ * \return non-zero on failure.
+ *
+ * This is an extensive test that can compare the values in the token
+ * to expected valued compiled into the test app from
+ * token_test_values.h. All the values represented in \ref
+ * attest_token_iat_simple_t and in \ref attest_token_sw_component_t
+ * are checked.
+ *
+ * This uses a short-circuit signature rather than real ECDSA
+ * keys. This tests everything in the implementation except the final
+ * signing of the final hash with ECDSA and the converse
+ * verification. It is thorough test of everything by ECDSA
+ * integration. It can work even without ECDSA integration and without
+ * any keys configured.
+ */
+int_fast16_t decode_test_short_circuit_sig(void);
+#endif /* SYMMETRIC_INITIAL_ATTESTATION */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __TOKEN_TEST_H__ */
diff --git a/test/suites/attestation/attest_token_test_values.h b/test/suites/attestation/attest_token_test_values.h
new file mode 100644
index 0000000..c8ecb21
--- /dev/null
+++ b/test/suites/attestation/attest_token_test_values.h
@@ -0,0 +1,317 @@
+/*
+ * attest_token_test_values.h
+ *
+ * Copyright (c) 2019, Laurence Lundblade.
+ * Copyright (c) 2019-2020, Arm Limited.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * See BSD-3-Clause license in README.md
+ */
+
+#ifndef __ATTEST_TOKEN_TEST_VALUES_H__
+#define __ATTEST_TOKEN_TEST_VALUES_H__
+
+/**
+ * \file attest_token_test_values.h
+ *
+ * \brief Expected values for test suite.
+ *
+ * This is a bunch of made up values for hard-coded test cases for
+ * attestation tokens.
+ *
+ * There are four possible test configurations for testing each claim:
+ *
+ *  1. No checking at all. \c TOKEN_TEST_REQUIRE_XXX is false and
+ *  TOKEN_TEST_VALUE_XXX is not given.
+ *
+ *  2. Check for presence only. \c TOKEN_TEST_REQUIRE_XXX is true and
+ *  TOKEN_TEST_VALUE_XXX is not given.
+ *
+ *  3. Check value if it is present, but it is not required to be
+ *  present. \c TOKEN_TEST_REQUIRE_XXX is false and \c
+ *  TOKEN_TEST_VALUE_XXX is given.
+ *
+ *  4. Must be present and of specific value. \c
+ *  TOKEN_TEST_REQUIRE_XXX is true and \c TOKEN_TEST_VALUE_XXX is
+ *  given.
+ *
+ * TOKEN_TEST_VALUE_XXX is not given as follows:
+ *  - #define text strings as \c NULL
+ *  - #define binary strings as \c NULL_Q_USEFUL_BUF_C
+ *  - #define the integer value as \c INT32_MAX
+ *
+ * It is assumed that the expected value for any test will never be
+ * any of these.
+ *
+ * Individual test can also be made to return values that are not
+ * fixed at compile time by defining them to be a function and
+ * implementing the funciton.  Here are examples for the three types:
+ *
+ *      struct q_useful_buf_c get_expected_nonce(void);
+ *      #define TOKEN_TEST_VALUE_NONCE get_expected_nonce()
+ *
+ *      const char *get_expected_hw_version(void);
+ *      #define TOKEN_TEST_VALUE_HW_VERSION get_expected_hw_version()
+ *
+ *      uint32_t get_expected_client_id(void);
+ *      #define TOKEN_TEST_VALUE_CLIENT_ID get_expected_client_id()
+ *
+ * The initialization value for byte strings uses a compound literal
+ * to create the \c ptr and \c len for a \c struct \c q_useful_buf_c.
+ * They are a bit ugly, but they work and setting up this way allows
+ * the literal value to be replaced by a function call for dynamic
+ * expected values.
+ *
+ * The first part of the compound literal is the value of the
+ * bytes. The second is an integer that is the length, the number of
+ * bytes. They length must be the number of bytes in the first.
+ */
+
+/* The 64 byte special option-packed nonce where option flags
+ * are packed in at the start. Binary. */
+#define TOKEN_TEST_NONCE_BYTES \
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+#define TOKEN_TEST_VALUE_NONCE \
+    (struct q_useful_buf_c) {\
+      (uint8_t[]){TOKEN_TEST_NONCE_BYTES},\
+        64\
+    }
+#define TOKEN_TEST_REQUIRE_NONCE true /* Mandatory claim */
+
+/* A 32 byte mostly random value. Binary. Value not checked */
+#define TOKEN_TEST_VALUE_UEID NULL_Q_USEFUL_BUF_C
+
+/* A 32 byte mostly random value. Binary.
+ *    platform/ext/common/template/tfm_initial_attestation_key_material.c
+ */
+/*
+#define TOKEN_TEST_VALUE_UEID \
+    (struct q_useful_buf_c) {\
+        (uint8_t[]){ \
+            0x01, \
+            0xfa, 0x58, 0x75, 0x5f, 0x65, 0x86, 0x27, 0xce, \
+            0x54, 0x60, 0xf2, 0x9b, 0x75, 0x29, 0x67, 0x13, \
+            0x24, 0x8c, 0xae, 0x7a, 0xd9, 0xe2, 0x98, 0x4b, \
+            0x90, 0x28, 0x0e, 0xfc, 0xbc, 0xb5, 0x02, 0x48  \
+        },\
+        33\
+    }
+*/
+#define TOKEN_TEST_REQUIRE_UEID true /* Mandatory claim */
+
+/* If defined, check for the constant values defined in
+ *    platform/ext/common/template/attest_hal.c
+ */
+#ifdef CLAIM_VALUE_CHECK
+
+/* A 32 byte mostly random value. Binary.
+ *    platform/ext/common/template/attest_hal.c
+ */
+#define TOKEN_TEST_VALUE_BOOT_SEED \
+    (struct q_useful_buf_c) {\
+        (uint8_t[]){ \
+            0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, \
+            0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, \
+            0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, \
+            0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF  \
+        },\
+        32\
+    }
+
+/* A text string in EAN 13 format
+ *    platform/ext/common/template/attest_hal.c
+ */
+#define TOKEN_TEST_VALUE_HW_VERSION "060456527282910010" /* Hard-coded value */
+
+/* A 32 byte mostly random value. Binary.
+ *    platform/ext/common/template/attest_hal.c
+ */
+#define TOKEN_TEST_VALUE_IMPLEMENTATION_ID \
+    (struct q_useful_buf_c) {\
+        (uint8_t[]){ \
+            0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, \
+            0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, \
+            0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, \
+            0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD  \
+        },\
+        32\
+    }
+
+/* Text string naming the profile definition:
+ *    platform/ext/common/template/attest_hal.c
+ */
+#define TOKEN_TEST_VALUE_PROFILE_DEFINITION  "PSA_IOT_PROFILE_1"
+
+/* Text string with verification URL or similar
+ *    platform/ext/common/template/attest_hal.c
+ */
+#define TOKEN_TEST_VALUE_ORIGINATION "www.trustedfirmware.org"
+
+/* A small unsigned integer
+ *    platform/ext/common/template/attest_hal.c
+ */
+#define TOKEN_TEST_VALUE_SECURITY_LIFECYCLE   0x3000u
+#else
+/* Do not check for the constant values defined in
+ *    platform/ext/common/template/attest_hal.c
+ */
+
+#define TOKEN_TEST_VALUE_BOOT_SEED          NULL_Q_USEFUL_BUF_C
+#define TOKEN_TEST_VALUE_HW_VERSION         NULL
+#define TOKEN_TEST_VALUE_IMPLEMENTATION_ID  NULL_Q_USEFUL_BUF_C
+#define TOKEN_TEST_VALUE_PROFILE_DEFINITION NULL
+#define TOKEN_TEST_VALUE_ORIGINATION        NULL
+#define TOKEN_TEST_VALUE_SECURITY_LIFECYCLE INT32_MAX
+
+#endif /* CLAIM_VALUE_CHECK */
+
+#define TOKEN_TEST_REQUIRE_BOOT_SEED            true /* Mandatory claim */
+#define TOKEN_TEST_REQUIRE_HW_VERSION           false /* Optional claim */
+#define TOKEN_TEST_REQUIRE_IMPLEMENTATION_ID    true /* Mandatory claim */
+#define TOKEN_TEST_REQUIRE_PROFILE_DEFINITION   false /* Optional field */
+#define TOKEN_TEST_REQUIRE_ORIGINATION          false /* Optional field */
+#define TOKEN_TEST_REQUIRE_SECURITY_LIFECYCLE   true /* Mandatory claim */
+
+/* An integer (can be positive or negative) */
+#define TOKEN_TEST_VALUE_CLIENT_ID 0 /* Invalid value to trigger check */
+#define TOKEN_TEST_REQUIRE_CLIENT_ID true /* Mandatory claim */
+
+/**
+ * \c TOKEN_TEST_REQUIRED_NUM_SWC can be either 0, 1, 2 or \c
+ * INT32_MAX
+ *
+ * 0 -- No SW components are required, but if there is 1, its values
+ * must compare to SWC1 correctly and if there are 2, the first must
+ * compare to SWC1 and the second to SWC2.
+ *
+ * 1 -- At least one SW component is required and it must compare
+ * correctly to SWC1. If a second one is present its values will also
+ * be checked.
+ *
+ * 2 -- Two SW components are required and their values must compare
+ * correctly.
+ *
+ * INT32_MAX -- No checking of the SW components of any sort is
+ * performed.
+ *
+ * Note that attest_token_decode() checks for the presence of the the
+ * EAT_CBOR_ARM_LABEL_NO_SW_COMPONENTS CBOR data item for the case of
+ * no SW components and gives an error if it is absent.
+ */
+#define TOKEN_TEST_REQUIRED_NUM_SWC 0
+
+/* Text string */
+#define TOKEN_TEST_VALUE_SWC1_MEASUREMENT_TYPE NULL /* Value not checked */
+#define TOKEN_TEST_REQUIRE_SWC1_MEASUREMENT_TYPE false /* Optional field */
+
+/* A 32 byte mostly random value. Binary. Value not checked */
+#define TOKEN_TEST_VALUE_SWC1_MEASUREMENT_VAL NULL_Q_USEFUL_BUF_C
+/*
+#define TOKEN_TEST_VALUE_SWC1_MEASUREMENT_VAL \
+    (struct q_useful_buf_c) {\
+        (uint8_t[]){ \
+            0x51, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, \
+            0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, \
+            0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, \
+            0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08  \
+        },\
+        32\
+    }
+*/
+#define TOKEN_TEST_REQUIRE_SWC1_MEASUREMENT_VAL true /* Mandatory field */
+
+
+/* Text string */
+#define TOKEN_TEST_VALUE_SWC1_VERSION NULL /* Value not checked */
+/* This field must be mandatory to ensure PSA compliance -
+ * based on PSA Security Model document.
+ */
+#define TOKEN_TEST_REQUIRE_SWC1_VERSION true /* Mandatory field */
+
+/* A 32 byte mostly random value. Binary. Value not checked */
+#define TOKEN_TEST_VALUE_SWC1_SIGNER_ID NULL_Q_USEFUL_BUF_C
+/*
+#define TOKEN_TEST_VALUE_SWC1_SIGNER_ID \
+    (struct q_useful_buf_c) {\
+        (uint8_t[]){ \
+            0x61, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, \
+            0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, \
+            0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, \
+            0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08  \
+        },\
+        32\
+    }
+*/
+/* This field must be mandatory to ensure PSA compliance -
+ * based on PSA Security Model document.
+ */
+#define TOKEN_TEST_REQUIRE_SWC1_SIGNER_ID true /* Mandatory field */
+
+/* Text string */
+#define TOKEN_TEST_VALUE_SWC1_MEASUREMENT_DESC "SHA256" /* Hard-coded value */
+#define TOKEN_TEST_REQUIRE_SWC1_MEASUREMENT_DESC false /* Optional field */
+
+/* Text string */
+#define TOKEN_TEST_VALUE_SWC2_MEASUREMENT_TYPE NULL /* Value not checked */
+#define TOKEN_TEST_REQUIRE_SWC2_MEASUREMENT_TYPE false /* Optional field */
+
+/* A 32 byte mostly random value. Binary. Value not checked */
+#define TOKEN_TEST_VALUE_SWC2_MEASUREMENT_VAL NULL_Q_USEFUL_BUF_C
+/*
+#define TOKEN_TEST_VALUE_SWC2_MEASUREMENT_VAL \
+    (struct q_useful_buf_c) {\
+        (uint8_t[]){ \
+            0x71, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, \
+            0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, \
+            0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, \
+            0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08  \
+        },\
+        32\
+    }
+*/
+#define TOKEN_TEST_REQUIRE_SWC2_MEASUREMENT_VAL true /* Mandatory field */
+
+/* Text string */
+#define TOKEN_TEST_VALUE_SWC2_VERSION NULL /* Value not checked */
+/* This field must be mandatory to ensure PSA compliance -
+ * based on PSA Security Model document.
+ */
+#define TOKEN_TEST_REQUIRE_SWC2_VERSION true /* Mandatory field */
+
+/* A 32 byte mostly random value. Binary. Value not checked */
+#define TOKEN_TEST_VALUE_SWC2_SIGNER_ID NULL_Q_USEFUL_BUF_C
+/*
+#define TOKEN_TEST_VALUE_SWC2_SIGNER_ID \
+    (struct q_useful_buf_c) {\
+        (uint8_t[]){ \
+            0x81, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, \
+            0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, \
+            0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, \
+            0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08  \
+        },\
+        32\
+    }
+*/
+/* This field must be mandatory to ensure PSA compliance -
+ * based on PSA Security Model document.
+ */
+#define TOKEN_TEST_REQUIRE_SWC2_SIGNER_ID true /* Mandatory field */
+
+/* Text string */
+#define TOKEN_TEST_VALUE_SWC2_MEASUREMENT_DESC "SHA256" /* Hard-coded value */
+#define TOKEN_TEST_REQUIRE_SWC2_MEASUREMENT_DESC false /* Optional field */
+
+/* Attest token maximum size, there are also platform dependent values
+ * defined in region_defs.h
+ */
+#define ATTEST_TOKEN_MAX_SIZE  0x250
+
+#endif /* __ATTEST_TOKEN_TEST_VALUES_H__ */
diff --git a/test/suites/attestation/attestation_tests_common.h b/test/suites/attestation/attestation_tests_common.h
new file mode 100644
index 0000000..03c6199
--- /dev/null
+++ b/test/suites/attestation/attestation_tests_common.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __ATTESTATION_TESTS_COMMON_H__
+#define __ATTESTATION_TESTS_COMMON_H__
+
+#include "psa/initial_attestation.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*!
+ * \def TEST_TOKEN_SIZE
+ *
+ * \brief Size of token buffer in bytes.
+ */
+#define TEST_TOKEN_SIZE (0x200)
+
+/*!
+ * \def TOO_SMALL_TOKEN_BUFFER
+ *
+ * \brief Smaller buffer size which is not big enough to store the created token
+ *        by attestation service.
+ */
+#define TOO_SMALL_TOKEN_BUFFER (16u)
+
+/*!
+ * \def TEST_CHALLENGE_OBJ_SIZE
+ *
+ * \brief Size of challenge object in bytes used for test.
+ */
+#define TEST_CHALLENGE_OBJ_SIZE (PSA_INITIAL_ATTEST_CHALLENGE_SIZE_64)
+
+/*!
+ * \def INVALID_CHALLENGE_OBJECT_SIZE
+ *
+ * \brief Size of challenge object that is invalid.
+ */
+#define INVALID_CHALLENGE_OBJECT_SIZE (PSA_INITIAL_ATTEST_CHALLENGE_SIZE_32 + 1)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ATTESTATION_TESTS_COMMON_H__ */
diff --git a/test/suites/attestation/non_secure/attestation_ns_interface_testsuite.c b/test/suites/attestation/non_secure/attestation_ns_interface_testsuite.c
new file mode 100644
index 0000000..e827ae2
--- /dev/null
+++ b/test/suites/attestation/non_secure/attestation_ns_interface_testsuite.c
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "attestation_ns_tests.h"
+#include "psa/initial_attestation.h"
+#include "attestation.h"
+#include "../attestation_tests_common.h"
+#include "../attest_token_test_values.h"
+#include "../attest_token_test.h"
+
+static uint8_t token_buffer[TEST_TOKEN_SIZE];
+static const uint8_t challenge_buffer[TEST_CHALLENGE_OBJ_SIZE] = {
+                                      TOKEN_TEST_NONCE_BYTES};
+
+/* Define test suite for attestation service tests */
+/* List of tests */
+#ifdef INCLUDE_TEST_CODE /* Remove them from release build */
+static void tfm_attest_test_2001(struct test_result_t *ret);
+static void tfm_attest_test_2002(struct test_result_t *ret);
+static void tfm_attest_test_2003(struct test_result_t *ret);
+#endif
+static void tfm_attest_test_2004(struct test_result_t *ret);
+static void tfm_attest_test_2005(struct test_result_t *ret);
+
+static struct test_t attestation_interface_tests[] = {
+#ifdef INCLUDE_TEST_CODE /* Remove them from release build */
+    {&tfm_attest_test_2001, "TFM_ATTEST_TEST_2001",
+     "Minimal token test of attest token", {TEST_PASSED} },
+    {&tfm_attest_test_2002, "TFM_ATTEST_TEST_2002",
+     "Minimal token size test of attest token", {TEST_PASSED} },
+    {&tfm_attest_test_2003, "TFM_ATTEST_TEST_2003",
+     "Short circuit signature test of attest token", {TEST_PASSED} },
+#endif
+    {&tfm_attest_test_2004, "TFM_ATTEST_TEST_2004",
+     "ECDSA signature test of attest token", {TEST_PASSED} },
+    {&tfm_attest_test_2005, "TFM_ATTEST_TEST_2005",
+     "Negative test cases for initial attestation service", {TEST_PASSED} },
+};
+
+void
+register_testsuite_ns_attestation_interface(struct test_suite_t *p_test_suite)
+{
+    uint32_t list_size;
+
+    list_size = (sizeof(attestation_interface_tests) /
+                 sizeof(attestation_interface_tests[0]));
+
+    set_testsuite("Initial Attestation Service non-secure interface tests"
+                  "(TFM_ATTEST_TEST_2XXX)",
+                  attestation_interface_tests, list_size, p_test_suite);
+}
+
+#ifdef INCLUDE_TEST_CODE /* Remove them from release build */
+/*!
+ * \brief Get minimal token, only include a hard coded challenge, but omit the
+ *        rest of the claims
+ *
+ * Calling the minimal_test, which just retrieves a specific token:
+ *  - only hard coded challenge is included
+ *  - token signature is the hash of the token concatenated twice
+ */
+static void tfm_attest_test_2001(struct test_result_t *ret)
+{
+    int32_t err;
+
+    err = minimal_test();
+    if (err != 0) {
+        TEST_LOG("minimal_test() returned: %d\r\n", err);
+        TEST_FAIL("Attest token minimal_test() has failed");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/*!
+ * \brief Get the size of the minimal token, only include a hard coded
+ *        challenge, but omit the rest of the claims
+ */
+static void tfm_attest_test_2002(struct test_result_t *ret)
+{
+    int32_t err;
+
+    err = minimal_get_size_test();
+    if (err != 0) {
+        TEST_LOG("minimal_get_size_test() returned: %d\r\n", err);
+        TEST_FAIL("Attest token minimal_get_size_test() has failed");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/*!
+ * \brief Get an IAT with short circuit signature (signature is composed of
+ *        hash of token). Parse the token, validate presence of claims and
+ *        compare them against expected values in token_test_values.h
+ *
+ * More info in token_test.h
+ */
+static void tfm_attest_test_2003(struct test_result_t *ret)
+{
+    int32_t err;
+
+    err = decode_test_short_circuit_sig();
+    if (err != 0) {
+        TEST_LOG("decode_test_short_circuit_sig() returned: %d\r\n", err);
+        TEST_FAIL("Attest token decode_test_short_circuit_sig() has failed");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+#endif /* INCLUDE_TEST_CODE */
+
+/*!
+ * \brief Get an IAT with proper ECDSA signature. Parse the token, validate
+ *        presence of claims and compare them against expected values in
+ *        token_test_values.h
+ *
+ * ECDSA signing is currently not supported in TF_M.
+ *
+ * More info in token_test.h
+ */
+static void tfm_attest_test_2004(struct test_result_t *ret)
+{
+    int32_t err;
+
+    err = decode_test_normal_sig();
+    if (err != 0) {
+        TEST_LOG("decode_test_normal_sig() returned: %d\r\n", err);
+        TEST_FAIL("Attest token decode_test_normal_sig() has failed");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/*!
+ * \brief Negative tests for initial attestation service
+ *
+ *    - Calling initial attestation service with bigger challenge object than
+ *      allowed.
+ *    - Calling initial attestation service with smaller buffer size than the
+ *      expected size of the token.
+ */
+static void tfm_attest_test_2005(struct test_result_t *ret)
+{
+    psa_status_t err;
+    size_t token_buf_size = TEST_TOKEN_SIZE;
+    size_t token_size;
+
+    /* Call with with bigger challenge object than allowed */
+    err = psa_initial_attest_get_token(challenge_buffer,
+                                       INVALID_CHALLENGE_OBJECT_SIZE,
+                                       token_buffer,
+                                       token_buf_size,
+                                       &token_size);
+
+    if (err != PSA_ERROR_INVALID_ARGUMENT) {
+        TEST_FAIL("Attestation should fail with too big challenge object");
+        return;
+    }
+
+    /* Call with smaller buffer size than size of test token */
+    token_buf_size = TOO_SMALL_TOKEN_BUFFER;
+    err = psa_initial_attest_get_token(challenge_buffer,
+                                       TEST_CHALLENGE_OBJ_SIZE,
+                                       token_buffer,
+                                       token_buf_size,
+                                       &token_size);
+
+    if (err != PSA_ERROR_BUFFER_TOO_SMALL) {
+        TEST_FAIL("Attestation should fail with too small token buffer");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
diff --git a/test/suites/attestation/non_secure/attestation_ns_tests.h b/test/suites/attestation/non_secure/attestation_ns_tests.h
new file mode 100644
index 0000000..c2d5a3e
--- /dev/null
+++ b/test/suites/attestation/non_secure/attestation_ns_tests.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __ATTESTATION_NS_TESTS_H__
+#define __ATTESTATION_NS_TESTS_H__
+
+#include "test/framework/test_framework.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Register testsuite for the initial attestation service.
+ *
+ * \param[in] p_test_suite The test suite to be executed.
+ */
+void
+register_testsuite_ns_attestation_interface(struct test_suite_t *p_test_suite);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ATTESTATION_NS_TESTS_H__ */
diff --git a/test/suites/attestation/non_secure/symmetric_attest_ns_interface_testsuite.c b/test/suites/attestation/non_secure/symmetric_attest_ns_interface_testsuite.c
new file mode 100644
index 0000000..4f5d99a
--- /dev/null
+++ b/test/suites/attestation/non_secure/symmetric_attest_ns_interface_testsuite.c
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "attestation_ns_tests.h"
+#include "psa/initial_attestation.h"
+#include "secure_fw/partitions/initial_attestation/attestation.h"
+#include "../attestation_tests_common.h"
+#include "../attest_token_test_values.h"
+#include "../attest_token_test.h"
+
+/* Define test suite for attestation service tests */
+/* List of tests */
+static void tfm_attest_test_2001(struct test_result_t *ret);
+#ifdef INCLUDE_TEST_CODE /* Remove them from release build */
+static void tfm_attest_test_2002(struct test_result_t *ret);
+static void tfm_attest_test_2003(struct test_result_t *ret);
+static void tfm_attest_test_2004(struct test_result_t *ret);
+static void tfm_attest_test_2005(struct test_result_t *ret);
+#endif
+
+static struct test_t attestation_interface_tests[] = {
+    {&tfm_attest_test_2001, "TFM_ATTEST_TEST_2001",
+     "Symmetric key algorithm based Initial Attestation test", {0} },
+#ifdef INCLUDE_TEST_CODE /* Remove them from release build */
+    {&tfm_attest_test_2002, "TFM_ATTEST_TEST_2002",
+     "Minimal token test of attest token", {0} },
+    {&tfm_attest_test_2003, "TFM_ATTEST_TEST_2003",
+     "Minimal token size test of attest token", {0} },
+    {&tfm_attest_test_2004, "TFM_ATTEST_TEST_2004",
+     "Short circuit tag test of attest token", {0} },
+    {&tfm_attest_test_2005, "TFM_ATTEST_TEST_2005",
+     "Negative test cases for initial attestation service", {0} },
+#endif
+};
+
+void
+register_testsuite_ns_attestation_interface(struct test_suite_t *p_test_suite)
+{
+    uint32_t list_size;
+
+    list_size = (sizeof(attestation_interface_tests) /
+                 sizeof(attestation_interface_tests[0]));
+
+    set_testsuite("Symmetric key algorithm based Initial Attestation Service "
+                  "non-secure interface tests (TFM_ATTEST_TEST_2XXX)",
+                  attestation_interface_tests, list_size, p_test_suite);
+}
+
+/*!
+ * \brief Get an IAT with symmetric key algorithm based Initial Attestation.
+ */
+static void tfm_attest_test_2001(struct test_result_t *ret)
+{
+    int32_t err;
+
+    err = decode_test_symmetric_initial_attest();
+    if (err != 0) {
+        TEST_LOG("tfm_attest_test_2001() returned: %d\r\n", err);
+        TEST_FAIL("Attest token tfm_attest_test_2001() has failed");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+#ifdef INCLUDE_TEST_CODE /* Remove them from release build */
+/*!
+ * \brief Get minimal token, only include a hard coded challenge, but omit the
+ *        rest of the claims
+ *
+ * Calling the minimal_test, which just retrieves a specific token:
+ *  - only hard coded challenge is included
+ *  - only mandatory claims are included
+ */
+static void tfm_attest_test_2002(struct test_result_t *ret)
+{
+    int32_t err;
+
+    err = minimal_test();
+    if (err != 0) {
+        TEST_LOG("minimal_test() returned: %d\r\n", err);
+        TEST_FAIL("Attest token minimal_test() has failed");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/*!
+ * \brief Get the size of the minimal token, only include a hard coded
+ *        challenge, but omit the rest of the claims
+ */
+static void tfm_attest_test_2003(struct test_result_t *ret)
+{
+    int32_t err;
+
+    err = minimal_get_size_test();
+    if (err != 0) {
+        TEST_LOG("minimal_get_size_test() returned: %d\r\n", err);
+        TEST_FAIL("Attest token minimal_get_size_test() has failed");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/*!
+ * \brief Get an IAT with short circuit tag (tag is hash of token).
+ *        Parse the token, validate presence of claims and verify the hash value
+ *
+ * More info in token_test.h
+ */
+static void tfm_attest_test_2004(struct test_result_t *ret)
+{
+    int32_t err;
+
+    err = decode_test_symmetric_iat_short_circuit_tag();
+    if (err != 0) {
+        TEST_LOG("decode_test_symmetric_iat_short_circuit_tag() returned: %d\r\n", err);
+        TEST_FAIL("Attest token decode_test_symmetric_iat_short_circuit_tag() has failed");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/*!
+ * \brief Negative tests for initial attestation service
+ *
+ *    - Calling initial attestation service with bigger challenge object than
+ *      allowed.
+ *    - Calling initial attestation service with smaller buffer size than the
+ *      expected size of the token.
+ */
+static void tfm_attest_test_2005(struct test_result_t *ret)
+{
+    psa_status_t err;
+    size_t token_size;
+
+    /* Call with with bigger challenge object than allowed */
+    err = psa_initial_attest_get_token_size(INVALID_CHALLENGE_OBJECT_SIZE,
+                                            &token_size);
+
+    if (err != PSA_ERROR_INVALID_ARGUMENT) {
+        TEST_FAIL("Attestation should fail with too big challenge object");
+        return;
+    }
+
+    /* Call with smaller buffer size than size of test token */
+    err = buffer_too_small_test();
+    if (err != 0) {
+        TEST_LOG("buffer_too_small_test() returned: %d\r\n", err);
+        TEST_FAIL("Attest token buffer_too_small_test() has failed");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+#endif /* INCLUDE_TEST_CODE */
diff --git a/test/suites/attestation/secure/attestation_s_interface_testsuite.c b/test/suites/attestation/secure/attestation_s_interface_testsuite.c
new file mode 100644
index 0000000..0ff7df1
--- /dev/null
+++ b/test/suites/attestation/secure/attestation_s_interface_testsuite.c
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "attestation_s_tests.h"
+#include "psa/initial_attestation.h"
+#include "secure_fw/partitions/initial_attestation/attestation.h"
+#include "../attestation_tests_common.h"
+#include "../attest_token_test_values.h"
+#include "../attest_token_test.h"
+
+static uint8_t token_buffer[TEST_TOKEN_SIZE];
+static const uint8_t challenge_buffer[TEST_CHALLENGE_OBJ_SIZE] = {
+                                      TOKEN_TEST_NONCE_BYTES};
+
+/* Define test suite for attestation service tests */
+/* List of tests */
+#ifdef INCLUDE_TEST_CODE /* Remove them from release build */
+static void tfm_attest_test_1001(struct test_result_t *ret);
+static void tfm_attest_test_1002(struct test_result_t *ret);
+static void tfm_attest_test_1003(struct test_result_t *ret);
+#endif
+static void tfm_attest_test_1004(struct test_result_t *ret);
+static void tfm_attest_test_1005(struct test_result_t *ret);
+
+static struct test_t attestation_interface_tests[] = {
+#ifdef INCLUDE_TEST_CODE /* Remove them from release build */
+    {&tfm_attest_test_1001, "TFM_ATTEST_TEST_1001",
+     "Minimal token test of attest token", {TEST_PASSED} },
+    {&tfm_attest_test_1002, "TFM_ATTEST_TEST_1002",
+     "Minimal token size test of attest token", {TEST_PASSED} },
+    {&tfm_attest_test_1003, "TFM_ATTEST_TEST_1003",
+     "Short circuit signature test of attest token", {TEST_PASSED} },
+#endif
+    {&tfm_attest_test_1004, "TFM_ATTEST_TEST_1004",
+     "ECDSA signature test of attest token", {TEST_PASSED} },
+    {&tfm_attest_test_1005, "TFM_ATTEST_TEST_1005",
+     "Negative test cases for initial attestation service", {TEST_PASSED} },
+};
+
+void
+register_testsuite_s_attestation_interface(struct test_suite_t *p_test_suite)
+{
+    uint32_t list_size;
+
+    list_size = (sizeof(attestation_interface_tests) /
+                 sizeof(attestation_interface_tests[0]));
+
+    set_testsuite("Initial Attestation Service secure interface tests"
+                  "(TFM_ATTEST_TEST_1XXX)",
+                  attestation_interface_tests, list_size, p_test_suite);
+}
+
+#ifdef INCLUDE_TEST_CODE /* Remove them from release build */
+/*!
+ * \brief Get minimal token, only include a hard coded challenge, but omit the
+ *        rest of the claims
+ *
+ * Calling the minimal_test, which just retrieves a specific token:
+ *  - only hard coded challenge is included
+ *  - token signature is the hash of the token concatenated twice
+ */
+static void tfm_attest_test_1001(struct test_result_t *ret)
+{
+    int32_t err;
+
+    err = minimal_test();
+    if (err != 0) {
+        TEST_LOG("minimal_test() returned: %d\r\n", err);
+        TEST_FAIL("Attest token minimal_test() has failed");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/*!
+ * \brief Get the size of the minimal token, only include a hard coded
+ *        challenge, but omit the rest of the claims
+ */
+static void tfm_attest_test_1002(struct test_result_t *ret)
+{
+    int32_t err;
+
+    err = minimal_get_size_test();
+    if (err != 0) {
+        TEST_LOG("minimal_get_size_test() returned: %d\r\n", err);
+        TEST_FAIL("Attest token minimal_get_size_test() has failed");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/*!
+ * \brief Get an IAT with short circuit signature (signature is composed of
+ *        hash of token). Parse the token, validate presence of claims and
+ *        compare them against expected values in token_test_values.h
+ *
+ * More info in token_test.h
+ */
+static void tfm_attest_test_1003(struct test_result_t *ret)
+{
+    int32_t err;
+
+    err = decode_test_short_circuit_sig();
+    if (err != 0) {
+        TEST_LOG("decode_test_short_circuit_sig() returned: %d\r\n", err);
+        TEST_FAIL("Attest token decode_test_short_circuit_sig() has failed");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+#endif /* INCLUDE_TEST_CODE */
+
+/*!
+ * \brief Get an IAT with proper ECDSA signature. Parse the token, validate
+ *        presence of claims and compare them against expected values in
+ *        token_test_values.h
+ *
+ * ECDSA signing is currently not supported in TF_M.
+ *
+ * More info in token_test.h
+ */
+static void tfm_attest_test_1004(struct test_result_t *ret)
+{
+    int32_t err;
+
+    err = decode_test_normal_sig();
+    if (err != 0) {
+        TEST_LOG("decode_test_normal_sig() returned: %d\r\n", err);
+        TEST_FAIL("Attest token decode_test_normal_sig() has failed");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/*!
+ * \brief Negative tests for initial attestation service
+ *
+ *    - Calling initial attestation service with bigger challenge object than
+ *      allowed.
+ *    - Calling initial attestation service with smaller buffer size than the
+ *      expected size of the token.
+ */
+static void tfm_attest_test_1005(struct test_result_t *ret)
+{
+    psa_status_t err;
+    size_t token_buf_size = TEST_TOKEN_SIZE;
+    size_t token_size;
+
+    /* Call with with bigger challenge object than allowed */
+    err = psa_initial_attest_get_token(challenge_buffer,
+                                       INVALID_CHALLENGE_OBJECT_SIZE,
+                                       token_buffer,
+                                       token_buf_size,
+                                       &token_size);
+
+    if (err != PSA_ERROR_INVALID_ARGUMENT) {
+        TEST_FAIL("Attestation should fail with too big challenge object");
+        return;
+    }
+
+    /* Call with smaller buffer size than size of test token */
+    token_buf_size = TOO_SMALL_TOKEN_BUFFER;
+    err = psa_initial_attest_get_token(challenge_buffer,
+                                       TEST_CHALLENGE_OBJ_SIZE,
+                                       token_buffer,
+                                       token_buf_size,
+                                       &token_size);
+
+    if (err != PSA_ERROR_BUFFER_TOO_SMALL) {
+        TEST_FAIL("Attestation should fail with too small token buffer");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
diff --git a/test/suites/attestation/secure/attestation_s_tests.h b/test/suites/attestation/secure/attestation_s_tests.h
new file mode 100644
index 0000000..97e8c40
--- /dev/null
+++ b/test/suites/attestation/secure/attestation_s_tests.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __ATTESTATION_S_TESTS_H__
+#define __ATTESTATION_S_TESTS_H__
+
+#include "test/framework/test_framework.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Register testsuite for the initial attestation service.
+ *
+ * \param[in] "p_test_suite" The test suite to be executed.
+ */
+void
+register_testsuite_s_attestation_interface(struct test_suite_t *p_test_suite);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ATTESTATION_S_TESTS_H__ */
diff --git a/test/suites/attestation/secure/symmetric_attest_s_interface_testsuite.c b/test/suites/attestation/secure/symmetric_attest_s_interface_testsuite.c
new file mode 100644
index 0000000..a2eb1fd
--- /dev/null
+++ b/test/suites/attestation/secure/symmetric_attest_s_interface_testsuite.c
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "attestation_s_tests.h"
+#include "psa/initial_attestation.h"
+#include "secure_fw/partitions/initial_attestation/attestation.h"
+#include "../attestation_tests_common.h"
+#include "../attest_token_test_values.h"
+#include "../attest_token_test.h"
+
+/* Define test suite for attestation service tests */
+/* List of tests */
+static void tfm_attest_test_1001(struct test_result_t *ret);
+#ifdef INCLUDE_TEST_CODE /* Remove them from release build */
+static void tfm_attest_test_1002(struct test_result_t *ret);
+static void tfm_attest_test_1003(struct test_result_t *ret);
+static void tfm_attest_test_1004(struct test_result_t *ret);
+static void tfm_attest_test_1005(struct test_result_t *ret);
+#endif
+
+static struct test_t attestation_interface_tests[] = {
+    {&tfm_attest_test_1001, "TFM_ATTEST_TEST_1001",
+     "Symmetric key algorithm based Initial Attestation test", {0} },
+#ifdef INCLUDE_TEST_CODE /* Remove them from release build */
+    {&tfm_attest_test_1002, "TFM_ATTEST_TEST_1002",
+     "Minimal token test of attest token", {0} },
+    {&tfm_attest_test_1003, "TFM_ATTEST_TEST_1003",
+     "Minimal token size test of attest token", {0} },
+    {&tfm_attest_test_1004, "TFM_ATTEST_TEST_1004",
+     "Short circuit tag test of attest token", {0} },
+    {&tfm_attest_test_1005, "TFM_ATTEST_TEST_1005",
+     "Negative test cases for initial attestation service", {0} },
+#endif
+};
+
+void
+register_testsuite_s_attestation_interface(struct test_suite_t *p_test_suite)
+{
+    uint32_t list_size;
+
+    list_size = (sizeof(attestation_interface_tests) /
+                 sizeof(attestation_interface_tests[0]));
+
+    set_testsuite("Symmetric key algorithm based Initial Attestation Service "
+                  "secure interface tests (TFM_ATTEST_TEST_1XXX)",
+                  attestation_interface_tests, list_size, p_test_suite);
+}
+
+/*!
+ * \brief Get an IAT with symmetric key algorithm based Initial Attestation.
+ */
+static void tfm_attest_test_1001(struct test_result_t *ret)
+{
+    int32_t err;
+
+    err = decode_test_symmetric_initial_attest();
+    if (err != 0) {
+        TEST_LOG("tfm_attest_test_1001() returned: %d\r\n", err);
+        TEST_FAIL("Attest token tfm_attest_test_1001() has failed");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+#ifdef INCLUDE_TEST_CODE /* Remove them from release build */
+/*!
+ * \brief Get minimal token, only include a hard coded challenge, but omit the
+ *        rest of the claims
+ *
+ * Calling the minimal_test, which just retrieves a specific token:
+ *  - only hard coded challenge is included
+ *  - only mandatory claims are included
+ */
+static void tfm_attest_test_1002(struct test_result_t *ret)
+{
+    int32_t err;
+
+    err = minimal_test();
+    if (err != 0) {
+        TEST_LOG("minimal_test() returned: %d\r\n", err);
+        TEST_FAIL("Attest token minimal_test() has failed");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/*!
+ * \brief Get the size of the minimal token, only include a hard coded
+ *        challenge, but omit the rest of the claims
+ */
+static void tfm_attest_test_1003(struct test_result_t *ret)
+{
+    int32_t err;
+
+    err = minimal_get_size_test();
+    if (err != 0) {
+        TEST_LOG("minimal_get_size_test() returned: %d\r\n", err);
+        TEST_FAIL("Attest token minimal_get_size_test() has failed");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/*!
+ * \brief Get an IAT with short circuit tag (tag is hash of token).
+ *        Parse the token, validate presence of claims and verify the hash value
+ *
+ * More info in token_test.h
+ */
+static void tfm_attest_test_1004(struct test_result_t *ret)
+{
+    int32_t err;
+
+    err = decode_test_symmetric_iat_short_circuit_tag();
+    if (err != 0) {
+        TEST_LOG("decode_test_symmetric_iat_short_circuit_tag() returned: %d\r\n", err);
+        TEST_FAIL("Attest token decode_test_symmetric_iat_short_circuit_tag() has failed");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/*!
+ * \brief Negative tests for initial attestation service
+ *
+ *    - Calling initial attestation service with bigger challenge object than
+ *      allowed.
+ *    - Calling initial attestation service with smaller buffer size than the
+ *      expected size of the token.
+ */
+static void tfm_attest_test_1005(struct test_result_t *ret)
+{
+    psa_status_t err;
+    size_t token_size;
+
+    /* Call with with bigger challenge object than allowed */
+    err = psa_initial_attest_get_token_size(INVALID_CHALLENGE_OBJECT_SIZE,
+                                            &token_size);
+
+    if (err != PSA_ERROR_INVALID_ARGUMENT) {
+        TEST_FAIL("Attestation should fail with too big challenge object");
+        return;
+    }
+
+    /* Call with smaller buffer size than size of test token */
+    err = buffer_too_small_test();
+    if (err != 0) {
+        TEST_LOG("buffer_too_small_test() returned: %d\r\n", err);
+        TEST_FAIL("Attest token buffer_too_small_test() has failed");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+#endif /* INCLUDE_TEST_CODE */
diff --git a/test/suites/audit/CMakeLists.inc b/test/suites/audit/CMakeLists.inc
new file mode 100644
index 0000000..b1a8f46
--- /dev/null
+++ b/test/suites/audit/CMakeLists.inc
@@ -0,0 +1,32 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2018-2019, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+#Definitions to compile the "audit logging test" module.
+#This file assumes it will be included from a project specific cmakefile, and
+#will not create a library or executable.
+#Inputs:
+#	TFM_ROOT_DIR - root directory of the TF-M repo.
+#
+#Outputs:
+#	Will modify include directories to make the source compile.
+#	ALL_SRC_C: C source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+#	ALL_SRC_CXX: C++ source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+#	ALL_SRC_ASM: assembly source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+#	Include directories will be modified by using the include_directories() commands as needed.
+
+#Get the current directory where this file is located.
+set(AUDIT_LOGGING_TEST_DIR ${CMAKE_CURRENT_LIST_DIR})
+if(NOT DEFINED TFM_ROOT_DIR)
+	message(FATAL_ERROR "Please set TFM_ROOT_DIR before including this file.")
+endif()
+
+list(APPEND ALL_SRC_C_S "${AUDIT_LOGGING_TEST_DIR}/secure/audit_s_interface_testsuite.c")
+list(APPEND ALL_SRC_C_NS "${AUDIT_LOGGING_TEST_DIR}/non_secure/audit_ns_interface_testsuite.c")
+
+#Setting include directories
+embedded_include_directories(PATH ${TFM_ROOT_DIR} ABSOLUTE)
+embedded_include_directories(PATH ${TFM_ROOT_DIR}/interface/include ABSOLUTE)
diff --git a/test/suites/audit/audit_tests_common.h b/test/suites/audit/audit_tests_common.h
new file mode 100644
index 0000000..93bd6a8
--- /dev/null
+++ b/test/suites/audit/audit_tests_common.h
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2018-2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __AUDIT_TESTS_COMMON_H__
+#define __AUDIT_TESTS_COMMON_H__
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*!
+ * \def STR(a)
+ *
+ * \brief A standard stringify macro
+ */
+#define STR(a) _STR(a)
+#define _STR(a) #a
+
+/*!
+ * \def LOCAL_BUFFER_SIZE
+ *
+ * \brief Size in bytes of the local buffer. Size accomodates two standard size
+ *        (no payload) log items, at maximum
+ */
+#define LOCAL_BUFFER_SIZE (80)
+
+/*!
+ * \def LOCAL_BUFFER_ITEMS
+ *
+ * \brief Number of items which can be held within a buffer of size
+ *        LOCAL_BUFFER_SIZE
+ */
+#define LOCAL_BUFFER_ITEMS (2)
+
+/*!
+ * \def STANDARD_LOG_ENTRY_SIZE
+ *
+ * \brief A log item with no payload (standard size) has the following size.
+ *        More details can be found observing \ref psa_audit_record
+ *        \ref log_tlr and \ref log_hdr
+ */
+#define STANDARD_LOG_ENTRY_SIZE (28)
+
+/*!
+ * \def INITIAL_LOGGING_REQUESTS
+ *
+ * \brief Number of initial consecutive logging requests to perform
+ */
+#define INITIAL_LOGGING_REQUESTS (36)
+
+/*!
+ * \def INITIAL_LOGGING_SIZE
+ *
+ * \brief Size of the initial consecutive logging requests
+ */
+#define INITIAL_LOGGING_SIZE (1008)
+
+/*!
+ * \def FINAL_LOGGING_REQUESTS
+ *
+ * \brief Number of final consecutive logging requests to perform
+ *
+ * \note This defines the state of the log when secure interface tests are
+ *       terminated
+ */
+#define FINAL_LOGGING_REQUESTS (2)
+
+/*!
+ * \def FINAL_LOGGING_SIZE
+ *
+ * \brief Size of the final consecutive logging requests
+ *
+ * \note This defines the state of the log when secure interface tests are
+ *       terminated
+ */
+#define FINAL_LOGGING_SIZE (56)
+
+/*!
+ * \def DUMMY_TEST_RECORD_ID_BASE
+ *
+ * \brief The log record is initialized with a dummy ID which uses this value as
+ *        base value
+ */
+#define DUMMY_TEST_RECORD_ID_BASE (0xABCD0000)
+
+/*!
+ * \def SECOND_ELEMENT_EXPECTED_CONTENT
+ *
+ * \brief Content of the log record in the second log item in the final request
+ *
+ */
+#define SECOND_ELEMENT_EXPECTED_CONTENT ( (DUMMY_TEST_RECORD_ID_BASE) + \
+                      (INITIAL_LOGGING_REQUESTS+1+FINAL_LOGGING_REQUESTS) )
+/*!
+ * \def MAX_LOG_SIZE
+ *
+ * \brief The maximum possible log size in the current implementation
+ *
+ * \brief This parameter for tests has to be changed for the tests in case the
+ *        implementation is modified
+ */
+#define MAX_LOG_SIZE (1024)
+
+/*!
+ * \def MAX_LOG_RECORD_SIZE
+ *
+ * \brief The maximum possible log line size to fill a MAX_LOG_SIZE bytes log
+ *
+ * \note This takes into account additional fields that are concatenated to the
+ *       record in the header and trailer
+ */
+#define MAX_LOG_RECORD_SIZE (1000)
+
+/*!
+ * \def INITIAL_LOG_SIZE
+ *
+ * \brief Initial state of the log size in bytes
+ *
+ * \note This defines the state of the log when non-secure interface tests start
+ */
+#define INITIAL_LOG_SIZE (FINAL_LOGGING_SIZE)
+
+/*!
+ * \def INITIAL_LOG_RECORDS
+ *
+ * \brief Initial state of the log number of records
+ *
+ * \note This defines the state of the log when non-secure interface tests start
+ */
+#define INITIAL_LOG_RECORDS (FINAL_LOGGING_REQUESTS)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __AUDIT_TESTS_COMMON_H__ */
diff --git a/test/suites/audit/non_secure/audit_ns_interface_testsuite.c b/test/suites/audit/non_secure/audit_ns_interface_testsuite.c
new file mode 100644
index 0000000..a6179ac
--- /dev/null
+++ b/test/suites/audit/non_secure/audit_ns_interface_testsuite.c
@@ -0,0 +1,256 @@
+/*
+ * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "test/framework/test_framework_helpers.h"
+#include "psa_audit_api.h"
+#include "audit_ns_tests.h"
+#include "tfm_api.h"
+#include "secure_fw/partitions/audit_logging/audit_core.h"
+
+#include "../audit_tests_common.h"
+
+/*!
+ * \def EMPTY_RETRIEVED_LOG_SIZE
+ *
+ * \brief Log size when the retrieved buffer is empty
+ */
+#define EMPTY_RETRIEVED_LOG_SIZE (0)
+
+/*!
+ * \def EMPTY_RETRIEVED_LOG_ITEMS
+ *
+ * \brief Number of log items when retrieved buffer is empty
+ */
+#define EMPTY_RETRIEVED_LOG_ITEMS (0)
+
+/*!
+ * \def SINGLE_RETRIEVED_LOG_SIZE
+ *
+ * \brief Log size when the retrieved buffer has 1 item
+ *        of standard size (no payload)
+ */
+#define SINGLE_RETRIEVED_LOG_SIZE (STANDARD_LOG_ENTRY_SIZE)
+
+/*!
+ * \def SINGLE_RETRIEVED_LOG_ITEMS
+ *
+ * \brief Number of log items when retrieved buffer has 1 item
+ */
+#define SINGLE_RETRIEVED_LOG_ITEMS (1)
+
+/*!
+ * \def SECOND_ELEMENT_START_INDEX
+ *
+ * \brief Index of the second item in the log
+ */
+#define SECOND_ELEMENT_START_INDEX (1)
+
+/* List of tests */
+static void tfm_audit_test_1001(struct test_result_t *ret);
+
+static struct test_t audit_veneers_tests[] = {
+    {&tfm_audit_test_1001, "TFM_AUDIT_TEST_1001",
+     "Non Secure functional", {TEST_PASSED} },
+};
+
+void register_testsuite_ns_audit_interface(struct test_suite_t *p_test_suite)
+{
+    uint32_t list_size;
+
+    list_size = (sizeof(audit_veneers_tests) /
+                 sizeof(audit_veneers_tests[0]));
+
+    set_testsuite("AuditLog non-secure interface test (TFM_AUDIT_TEST_1XXX)",
+                  audit_veneers_tests, list_size, p_test_suite);
+}
+
+/**
+ * \brief Functional test of NS API
+ *
+ * \note This is a functional test only and doesn't
+ *       mean to test all possible combinations of
+ *       input parameters and return values.
+ *       This tests the current status of the log as
+ *       it's been left from the Secure tests. In case
+ *       other tests are added in the Secure test suite,
+ *       the status of the log will change and these
+ *       tests may start failing.
+ */
+static void tfm_audit_test_1001(struct test_result_t *ret)
+{
+    psa_status_t status;
+
+    uint8_t local_buffer[LOCAL_BUFFER_SIZE];
+    uint32_t idx, stored_size, num_records, retrieved_size;
+
+    struct psa_audit_record *retrieved_buffer;
+
+    /* Get the log size (current state) */
+    status = psa_audit_get_info(&num_records, &stored_size);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Getting log info has returned error");
+        return;
+    }
+
+    if (stored_size != INITIAL_LOG_SIZE) {
+        TEST_FAIL("Stored size different from " STR(INITIAL_LOG_SIZE));
+        return;
+    }
+
+    if (num_records != INITIAL_LOG_RECORDS) {
+        TEST_FAIL("Stored records different from " STR(INITIAL_LOG_RECORDS));
+        return;
+    }
+
+    /* Check the length of each record individually */
+    for (idx=0; idx<num_records; idx++) {
+        status = psa_audit_get_record_info(idx, &stored_size);
+        if (status != PSA_SUCCESS) {
+            TEST_FAIL("Getting record size individually has returned error");
+            return;
+        }
+
+        if (stored_size != STANDARD_LOG_ENTRY_SIZE) {
+            TEST_FAIL("Unexpected record size for a single standard record");
+            return;
+        }
+    }
+
+    /* Check that if requesting length of a record which is not there fails */
+    status = psa_audit_get_record_info(num_records, &stored_size);
+    if (status == PSA_SUCCESS) {
+        TEST_FAIL("Getting record size for non-existent record has not failed");
+        return;
+    }
+
+    /* Log contains 2 items. Retrieve into buffer which is able to contain the
+     * the full contents of the log, one record at a time
+     */
+    for (idx=0; idx<INITIAL_LOG_RECORDS; idx++) {
+        status = psa_audit_retrieve_record(
+                                     idx,
+                                     LOCAL_BUFFER_SIZE,
+                                     NULL,
+                                     0,
+                                     &local_buffer[idx*STANDARD_LOG_ENTRY_SIZE],
+                                     &retrieved_size);
+
+        if (status != PSA_SUCCESS) {
+            TEST_FAIL("Log retrieval from NS returned error");
+            return;
+        }
+
+        if (retrieved_size != STANDARD_LOG_ENTRY_SIZE) {
+            TEST_FAIL("Expected retrieve size: " STR(STANDARD_LOG_ENTRY_SIZE));
+            return;
+        }
+    }
+
+    /* Retrieve into a small buffer. It's not enough to store a single
+     * item so the provided buffer must be empty after retrieval. We
+     * check the info structure to count how many items and bytes have
+     * been returned, and if they're zeros items / zero bytes, there is
+     * no point in checking the contents of the local_buffer.
+     */
+    status = psa_audit_retrieve_record(0,
+                                       LOCAL_BUFFER_SIZE/4,
+                                       NULL,
+                                       0,
+                                       &local_buffer[0],
+                                       &retrieved_size);
+
+    if (status == PSA_SUCCESS) {
+        TEST_FAIL("Log retrieval from NS should fail, buffer too small");
+        return;
+    }
+
+    if (retrieved_size != EMPTY_RETRIEVED_LOG_SIZE) {
+        TEST_FAIL("Expected log size is " STR(EMPTY_RETRIEVED_LOG_SIZE));
+        return;
+    }
+
+    /* Retrieve into a buffer which can hold a single element, but start from
+     * the second element that is stored in the log
+     */
+    status = psa_audit_retrieve_record(1,
+                                       STANDARD_LOG_ENTRY_SIZE,
+                                       NULL,
+                                       0,
+                                       &local_buffer[0],
+                                       &retrieved_size);
+
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Log retrieval from NS returned error");
+        return;
+    }
+
+    if (retrieved_size != SINGLE_RETRIEVED_LOG_SIZE) {
+        TEST_FAIL("Expected log size is " STR(SINGLE_RETRIEVED_LOG_SIZE));
+        return;
+    }
+
+    /* Inspect the contents of the retrieved buffer, i.e. check the
+     * retrieved log record contents
+     */
+    retrieved_buffer = (struct psa_audit_record *)
+                           &local_buffer[offsetof(struct log_hdr, size)];
+
+    if (retrieved_buffer->id != SECOND_ELEMENT_EXPECTED_CONTENT) {
+        TEST_FAIL("Unexpected argument in the first entry");
+        return;
+    }
+
+    /* Delete oldest element in the log */
+    status = psa_audit_delete_record(0, NULL, 0);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Log record deletion from NS returned error");
+        return;
+    }
+
+    /* Get the log size (current state) */
+    status = psa_audit_get_info(&num_records, &stored_size);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Getting log info has returned error");
+        return;
+    }
+
+    if (num_records != 1) {
+        TEST_FAIL("Unexpected number of records in the log after delete");
+        return;
+    }
+
+    if (stored_size != STANDARD_LOG_ENTRY_SIZE) {
+        TEST_FAIL("Unexpected size in the log after deletion");
+        return;
+    }
+
+    /* Delete oldest element in the log. After this, the log will be empty */
+    status = psa_audit_delete_record(0, NULL, 0);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Log record deletion from NS returned error");
+        return;
+    }
+
+    /* Get the log size (current state) */
+    status = psa_audit_get_info(&num_records, &stored_size);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Getting log info has returned error");
+        return;
+    }
+
+    if (num_records != 0) {
+        TEST_FAIL("Unexpected number of records in the log after deletion");
+        return;
+    }
+
+    if (stored_size != 0) {
+        TEST_FAIL("Unexpected size in the log after deletion");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
diff --git a/test/suites/audit/non_secure/audit_ns_tests.h b/test/suites/audit/non_secure/audit_ns_tests.h
new file mode 100644
index 0000000..3578bbe
--- /dev/null
+++ b/test/suites/audit/non_secure/audit_ns_tests.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __AUDIT_NS_TESTS_H__
+#define __AUDIT_NS_TESTS_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "test/framework/test_framework.h"
+
+/**
+ * \brief Register testsuite for audit logging non-secure interface.
+ *
+ * \param[in] p_test_suite The test suite to be executed.
+ */
+void register_testsuite_ns_audit_interface(struct test_suite_t *p_test_suite);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __AUDIT_NS_TESTS_H__ */
diff --git a/test/suites/audit/secure/audit_s_interface_testsuite.c b/test/suites/audit/secure/audit_s_interface_testsuite.c
new file mode 100644
index 0000000..550ec10
--- /dev/null
+++ b/test/suites/audit/secure/audit_s_interface_testsuite.c
@@ -0,0 +1,397 @@
+/*
+ * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "test/framework/test_framework_helpers.h"
+#include "psa_audit_api.h"
+#include "audit_s_tests.h"
+#include "tfm_api.h"
+#include "psa_audit_api.h"
+#include "secure_fw/partitions/audit_logging/audit_core.h"
+
+#include "../audit_tests_common.h"
+
+/*!
+ * \def BASE_RETRIEVAL_LOG_INDEX
+ *
+ * \brief Base index from where to start elements retrieval
+ */
+#define BASE_RETRIEVAL_LOG_INDEX (6)
+
+/*!
+ * \def FIRST_RETRIEVAL_LOG_INDEX
+ *
+ * \brief Index of the first element in the log
+ */
+#define FIRST_RETRIEVAL_LOG_INDEX (0)
+
+/* List of tests */
+static void tfm_audit_test_1001(struct test_result_t *ret);
+
+static struct test_t audit_veneers_tests[] = {
+    {&tfm_audit_test_1001, "TFM_AUDIT_TEST_1001",
+     "Secure functional", {TEST_PASSED} },
+};
+
+void register_testsuite_s_audit_interface(struct test_suite_t *p_test_suite)
+{
+    uint32_t list_size;
+
+    list_size = (sizeof(audit_veneers_tests) /
+                 sizeof(audit_veneers_tests[0]));
+
+    set_testsuite("Audit Logging secure interface test (TFM_AUDIT_TEST_1XXX)",
+                  audit_veneers_tests, list_size, p_test_suite);
+}
+
+/**
+ * \brief Functional test of the Secure interface
+ *
+ * \note This is a functional test only and doesn't
+ *       mean to test all possible combinations of
+ *       input parameters and return values.
+ *       This tests will leave the log in a certain
+ *       status which, in turn, will be evaluated by
+ *       the Non Secure functional tests. If any tests
+ *       are added here that will leave the log in a
+ *       different state, Non Secure functional tests
+ *       need to be amended accordingly.
+ */
+static void tfm_audit_test_1001(struct test_result_t *ret)
+{
+    psa_status_t status;
+    uint8_t local_buffer[LOCAL_BUFFER_SIZE], idx;
+    struct psa_audit_record *record = (struct psa_audit_record *)
+                                                  &local_buffer[0];
+    uint32_t num_records, stored_size, record_size;
+    struct psa_audit_record *retrieved_buffer;
+
+    /* Fill the log with 36 records, each record is 28 bytes
+     * we end up filling the log without wrapping
+     */
+    for (idx=0; idx<INITIAL_LOGGING_REQUESTS; idx++) {
+        record->size = sizeof(struct psa_audit_record) - 4;
+        record->id = DUMMY_TEST_RECORD_ID_BASE + idx;
+
+        /* The record doesn't contain any payload */
+        status = psa_audit_add_record(record);
+        if (status != PSA_SUCCESS) {
+            TEST_FAIL("Record addition has returned an error");
+            return;
+        }
+    }
+
+    /* Get the log size */
+    status = psa_audit_get_info(&num_records, &stored_size);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Getting log info has returned error");
+        return;
+    }
+
+    if (stored_size != INITIAL_LOGGING_SIZE) {
+        TEST_FAIL("Expected log size is " STR(INITIAL_LOGGING_SIZE));
+        return;
+    }
+
+    if (num_records != INITIAL_LOGGING_REQUESTS) {
+        TEST_FAIL("Expected log records are " STR(INITIAL_LOGGING_REQUESTS));
+        return;
+    }
+
+    /* Retrieve two log records starting from a given index */
+    for (idx=BASE_RETRIEVAL_LOG_INDEX; idx<BASE_RETRIEVAL_LOG_INDEX+2; idx++) {
+        uint8_t *p_buf =
+          &local_buffer[(idx-BASE_RETRIEVAL_LOG_INDEX)*STANDARD_LOG_ENTRY_SIZE];
+
+        status = psa_audit_retrieve_record(idx,
+                                           LOCAL_BUFFER_SIZE,
+                                           NULL,
+                                           0,
+                                           p_buf,
+                                           &record_size);
+
+        if (status != PSA_SUCCESS) {
+            TEST_FAIL("Retrieve indexes 6 or 7 has returned an error");
+            return;
+        }
+
+        if (record_size != STANDARD_LOG_ENTRY_SIZE) {
+            TEST_FAIL("Expected log size is " STR(STANDARD_LOG_ENTRY_SIZE));
+            return;
+        }
+    }
+
+    /* Inspect the content of the second log record retrieved */
+    retrieved_buffer = (struct psa_audit_record *)
+        &local_buffer[offsetof(struct log_hdr,size)+STANDARD_LOG_ENTRY_SIZE];
+
+    if (retrieved_buffer->id != ( DUMMY_TEST_RECORD_ID_BASE +
+                                  (BASE_RETRIEVAL_LOG_INDEX+1) )) {
+        TEST_FAIL("Unexpected argument in the index 7 entry");
+        return;
+    }
+
+    /* Retrieve the last two log records */
+    for (idx=num_records-2; idx<num_records; idx++) {
+        uint8_t *p_buf =
+            &local_buffer[(idx-(num_records-2))*STANDARD_LOG_ENTRY_SIZE];
+
+        status = psa_audit_retrieve_record(idx,
+                                           LOCAL_BUFFER_SIZE,
+                                           NULL,
+                                           0,
+                                           p_buf,
+                                           &record_size);
+
+        if (status != PSA_SUCCESS) {
+            TEST_FAIL("Retrieve of last two log records has returned error");
+            return;
+        }
+
+        if (record_size != STANDARD_LOG_ENTRY_SIZE) {
+            TEST_FAIL("Expected log size is " STR(STANDARD_LOG_ENTRY_SIZE));
+            return;
+        }
+    }
+
+    /* Inspect the first record retrieved in the local buffer */
+    retrieved_buffer = (struct psa_audit_record *)
+                           &local_buffer[offsetof(struct log_hdr,size)];
+
+    if (retrieved_buffer->id != ( DUMMY_TEST_RECORD_ID_BASE +
+                                  (INITIAL_LOGGING_REQUESTS-2) )) {
+        TEST_FAIL("Unexpected argument in the second last entry");
+        return;
+    }
+
+    /* Retrieve the first log item */
+    status = psa_audit_retrieve_record(0,
+                                       LOCAL_BUFFER_SIZE,
+                                       NULL,
+                                       0,
+                                       &local_buffer[0],
+                                       &record_size);
+
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Retrieve of the first log entry has returned error");
+        return;
+    }
+
+    if (record_size != STANDARD_LOG_ENTRY_SIZE) {
+        TEST_FAIL("Expected log size is " STR(STANDARD_LOG_ENTRY_SIZE));
+        return;
+    }
+
+    if (retrieved_buffer->id != DUMMY_TEST_RECORD_ID_BASE) {
+        TEST_FAIL("Unexpected argument in the first entry");
+        return;
+    }
+
+    status = psa_audit_retrieve_record(num_records - 1,
+                                       LOCAL_BUFFER_SIZE,
+                                       NULL,
+                                       0,
+                                       &local_buffer[0],
+                                       &record_size);
+
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Retrieve of last two log entries has returned error");
+        return;
+    }
+
+    if (record_size != STANDARD_LOG_ENTRY_SIZE) {
+        TEST_FAIL("Expected log size is " STR(STANDARD_LOG_ENTRY_SIZE));
+        return;
+    }
+
+    /* Inspect the item just retrieved */
+    if (retrieved_buffer->id != ( DUMMY_TEST_RECORD_ID_BASE +
+                                  (INITIAL_LOGGING_REQUESTS-1) )) {
+        TEST_FAIL("Unexpected argument in the second last entry");
+        return;
+    }
+
+    /* Fill one more log record, this will wrap */
+    record->size = sizeof(struct psa_audit_record) - 4;
+    record->id = DUMMY_TEST_RECORD_ID_BASE + INITIAL_LOGGING_REQUESTS;
+
+    /* The addition of this new log item will wrap the log ending */
+    status = psa_audit_add_record(record);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Record addition has returned an error");
+        return;
+    }
+
+    /* Get the log size */
+    status = psa_audit_get_info(&num_records, &stored_size);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Getting log info has returned error");
+        return;
+    }
+
+    /* Check that the log state is the same, the item addition just performed
+     * is resulted into the removal of the oldest entry, so log size and number
+     * of log records is still the same as before
+     */
+    if (stored_size != INITIAL_LOGGING_SIZE) {
+        TEST_FAIL("Expected log size is " STR(INITIAL_LOGGING_SIZE));
+        return;
+    }
+
+    if (num_records != INITIAL_LOGGING_REQUESTS) {
+        TEST_FAIL("Expected log records are " STR(INITIAL_LOGGING_REQUESTS));
+        return;
+    }
+
+    /* Retrieve the last two log records */
+    for (idx=num_records-2; idx<num_records; idx++) {
+        uint8_t *p_buf =
+            &local_buffer[(idx-(num_records-2))*STANDARD_LOG_ENTRY_SIZE];
+
+        /* Retrieve the last two items */
+        status = psa_audit_retrieve_record(idx,
+                                           LOCAL_BUFFER_SIZE,
+                                           NULL,
+                                           0,
+                                           p_buf,
+                                           &record_size);
+
+        if (status != PSA_SUCCESS) {
+            TEST_FAIL("Retrieve of last two log records has returned error");
+            return;
+        }
+
+        if (record_size != STANDARD_LOG_ENTRY_SIZE) {
+            TEST_FAIL("Expected record size is " STR(STANDARD_LOG_ENTRY_SIZE));
+            return;
+        }
+    }
+
+    /* Inspect the first record retrieved */
+    if (retrieved_buffer->id != ( DUMMY_TEST_RECORD_ID_BASE +
+                                  (INITIAL_LOGGING_REQUESTS-1) )) {
+        TEST_FAIL("Unexpected argument in the second last entry");
+        return;
+    }
+
+    /* Inspect the second record retrieved in the local buffer */
+    retrieved_buffer = (struct psa_audit_record *)
+        &local_buffer[offsetof(struct log_hdr,size)+STANDARD_LOG_ENTRY_SIZE];
+
+    if (retrieved_buffer->id != ( DUMMY_TEST_RECORD_ID_BASE +
+                                  (INITIAL_LOGGING_REQUESTS) )) {
+        TEST_FAIL("Unexpected argument in the last entry");
+        return;
+    }
+
+    /* Fill now one big record that will invalidate all existing records */
+    record->size = MAX_LOG_RECORD_SIZE;
+    record->id = DUMMY_TEST_RECORD_ID_BASE + INITIAL_LOGGING_REQUESTS + 1;
+
+    /* The record has maximum possible payload for log size of 1024 */
+    status = psa_audit_add_record(record);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Record addition has returned an error");
+        return;
+    }
+
+    /* Get the log size */
+    status = psa_audit_get_info(&num_records, &stored_size);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Getting log info has returned error");
+        return;
+    }
+
+    /* Check that the log state has one element with maximum size */
+    if (stored_size != MAX_LOG_SIZE) {
+        TEST_FAIL("Expected log size is " STR(MAX_LOG_SIZE));
+        return;
+    }
+
+    if (num_records != 1) {
+        TEST_FAIL("Expected log records are 1");
+        return;
+    }
+
+    /* Try to retrieve the maximum possible size that fits our buffer.
+     * As there is just one big record filling the whole space, nothing
+     * will be returned and the API will fail
+     */
+    status = psa_audit_retrieve_record(0,
+                                       LOCAL_BUFFER_SIZE,
+                                       NULL,
+                                       0,
+                                       &local_buffer[0],
+                                       &record_size);
+
+    if (status == PSA_SUCCESS) {
+        TEST_FAIL("Retrieve of index 0 should fail as it's too big");
+        return;
+    }
+
+    if (record_size != 0) {
+        TEST_FAIL("Retrieved log size has unexpected size instead of 0");
+        return;
+    }
+
+    /* Add two standard length records again */
+    for (idx=0; idx<2; idx++) {
+        record->size = sizeof(struct psa_audit_record) - 4;
+        record->id = DUMMY_TEST_RECORD_ID_BASE +
+                     INITIAL_LOGGING_REQUESTS + 2 + idx;
+
+        /* The record doesn't contain any payload */
+        status = psa_audit_add_record(record);
+        if (status != PSA_SUCCESS) {
+            TEST_FAIL("Record addition has returned an error");
+            return;
+        }
+    }
+
+    /* Get the log size */
+    status = psa_audit_get_info(&num_records, &stored_size);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Getting log info has returned error");
+        return;
+    }
+
+    /* As the log was full, the addition of the last two log records results
+     * in the resetting of the log completely. The log will contain only
+     * the last two items we have just added.
+     */
+    if (stored_size != FINAL_LOGGING_SIZE) {
+        TEST_FAIL("Expected log size is " STR(FINAL_LOGGING_SIZE));
+        return;
+    }
+
+    if (num_records != FINAL_LOGGING_REQUESTS) {
+        TEST_FAIL("Expected log records are " STR(FINAL_LOGGING_REQUESTS));
+        return;
+    }
+
+    /* Check the length of each record individually */
+    for (idx=0; idx<num_records; idx++) {
+        status = psa_audit_get_record_info(idx, &stored_size);
+        if (status != PSA_SUCCESS) {
+            TEST_FAIL("Getting record size individually has returned error");
+            return;
+        }
+
+        if (stored_size != STANDARD_LOG_ENTRY_SIZE) {
+            TEST_FAIL("Unexpected log record size for a single standard item");
+            return;
+        }
+    }
+
+    /* Check that if requesting length of a record which is not there fails */
+    status = psa_audit_get_record_info(num_records, &stored_size);
+    if (status == PSA_SUCCESS) {
+        TEST_FAIL("Getting record size for non-existent record has not failed");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
diff --git a/test/suites/audit/secure/audit_s_tests.h b/test/suites/audit/secure/audit_s_tests.h
new file mode 100644
index 0000000..87bc904
--- /dev/null
+++ b/test/suites/audit/secure/audit_s_tests.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __AUDIT_S_TESTS_H__
+#define __AUDIT_S_TESTS_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "test/framework/test_framework.h"
+
+/**
+ * \brief Register testsuite for audit logging secure interface.
+ *
+ * \param[in] p_test_suite The test suite to be executed.
+ */
+void register_testsuite_s_audit_interface(struct test_suite_t *p_test_suite);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __AUDIT_S_TESTS_H__ */
diff --git a/test/suites/core/CMakeLists.inc b/test/suites/core/CMakeLists.inc
new file mode 100644
index 0000000..3c7d267
--- /dev/null
+++ b/test/suites/core/CMakeLists.inc
@@ -0,0 +1,53 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2017-2019, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+#Definitions to compile the "core test" module.
+#This file assumes it will be included from a project specific cmakefile, and
+#will not create a library or executable.
+#Inputs:
+#	TFM_ROOT_DIR - root directory of the TF-M repo.
+#
+#Outputs:
+#	Will modify include directories to make the source compile.
+#	ALL_SRC_C: C source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+#	ALL_SRC_CXX: C++ source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+#	ALL_SRC_ASM: assembly source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+#	Include directories will be modified by using the include_directories() commands as needed.
+
+#Get the current directory where this file is located.
+set(CORE_TEST_DIR ${CMAKE_CURRENT_LIST_DIR})
+if(NOT DEFINED TFM_ROOT_DIR)
+	message(FATAL_ERROR "Please set TFM_ROOT_DIR before including this file.")
+endif()
+
+if (NOT DEFINED TFM_PARTITION_TEST_CORE)
+	message(FATAL_ERROR "Incomplete build configuration: TFM_PARTITION_TEST_CORE is undefined. ")
+elseif (TFM_PARTITION_TEST_CORE)
+	list(APPEND ALL_SRC_C_NS "${CORE_TEST_DIR}/non_secure/core_test_api.c")
+endif()
+
+if (NOT DEFINED CORE_TEST_POSITIVE)
+	message(FATAL_ERROR "Incomplete build configuration: CORE_TEST_POSITIVE is undefined. ")
+elseif (CORE_TEST_POSITIVE)
+	list(APPEND ALL_SRC_C_NS "${CORE_TEST_DIR}/non_secure/core_ns_positive_testsuite.c")
+endif()
+
+if (NOT DEFINED CORE_TEST_INTERACTIVE)
+	message(FATAL_ERROR "Incomplete build configuration: CORE_TEST_INTERACTIVE is undefined. ")
+elseif (CORE_TEST_INTERACTIVE)
+	list(APPEND ALL_SRC_C_NS "${CORE_TEST_DIR}/non_secure/core_ns_interactive_testsuite.c")
+endif()
+
+# Disable recursion test from core test by default
+if (ENABLE_TFM_CORE_RECURSION_TESTS)
+	add_definitions(-DENABLE_TFM_CORE_RECURSION_TESTS)
+endif()
+
+#Setting include directories
+embedded_include_directories(PATH ${TFM_ROOT_DIR} ABSOLUTE)
+embedded_include_directories(PATH ${TFM_ROOT_DIR}/test/interface/include ABSOLUTE)
+embedded_include_directories(PATH ${TFM_ROOT_DIR}/interface/include ABSOLUTE)
diff --git a/test/suites/core/non_secure/core_ns_interactive_testsuite.c b/test/suites/core/non_secure/core_ns_interactive_testsuite.c
new file mode 100644
index 0000000..36cd2d1
--- /dev/null
+++ b/test/suites/core/non_secure/core_ns_interactive_testsuite.c
@@ -0,0 +1,440 @@
+/*
+ * Copyright (c) 2017-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "core_ns_tests.h"
+#include "tfm_api.h"
+#include "app/tfm_integ_test.h"
+#include "cmsis_os2.h"
+#include "tfm_nspm_api.h"
+#include "tfm_veneers.h"
+#include "test/suites/core/non_secure/core_test_api.h"
+#include "test/test_services/tfm_core_test/core_test_defs.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
+
+#ifdef TFM_PSA_API
+#include "psa_manifest/sid.h"
+#endif
+
+#ifdef TEST_FRAMEWORK_S
+#include \
+  "test/test_services/tfm_secure_client_service/tfm_secure_client_service_api.h"
+#endif
+
+#define TRY_SFN(fn, ...) \
+    do { \
+        enum tfm_status_e res = (enum tfm_status_e) fn(__VA_ARGS__); \
+        switch(res) { \
+            case TFM_SUCCESS: \
+                TEST_LOG("Secure call to " #fn "(" #__VA_ARGS__") successful!");\
+                break; \
+            case TFM_ERROR_SECURE_DOMAIN_LOCKED: \
+                TEST_LOG("Secure call to " #fn "(" #__VA_ARGS__") failed, " \
+                                                           "S domain locked!");\
+                break; \
+            default: \
+                TEST_LOG("Secure call to " #fn "(" #__VA_ARGS__") failed, " \
+                                                                   "generic!");\
+        } \
+    } while(0)
+
+/* Define test suite for core interactive tests */
+/* List of tests */
+static void tfm_core_test_2001(struct test_result_t *ret);
+
+static struct test_t core_tests[] = {
+    {&tfm_core_test_2001, "TFM_CORE_TEST_2001",
+     "Interactive tests", {TEST_PASSED} },
+};
+
+void register_testsuite_ns_core_interactive(struct test_suite_t *p_test_suite)
+{
+    uint32_t list_size;
+
+    list_size = (sizeof(core_tests) / sizeof(core_tests[0]));
+
+    set_testsuite("Core non-secure interactive tests (TFM_CORE_TEST_2XXX)",
+                  core_tests, list_size, p_test_suite);
+}
+
+void execute_ns_interactive_tests(void);
+
+/**
+ * \brief Tests core function with interactive test cases
+ */
+static void tfm_core_test_2001(struct test_result_t *ret)
+{
+    execute_ns_interactive_tests();
+
+    ret->val = TEST_PASSED;
+}
+
+#ifdef TFM_PSA_API
+static psa_status_t psa_test_common(uint32_t sid, uint32_t version,
+                                    const psa_invec *in_vecs, size_t in_len,
+                                    psa_outvec *out_vecs, size_t out_len)
+{
+    psa_handle_t handle;
+    psa_status_t status;
+
+    handle = psa_connect(sid, version);
+    if (handle <= 0) {
+        return CORE_TEST_ERRNO_INVALID_PARAMETER;
+    }
+
+    status = psa_call(handle, PSA_IPC_CALL, in_vecs, in_len, out_vecs, out_len);
+    if (status < 0) {
+        status = CORE_TEST_ERRNO_UNEXPECTED_CORE_BEHAVIOUR;
+    }
+
+    psa_close(handle);
+    return status;
+}
+#endif /* TFM_PSA_API */
+
+/**
+ * \brief secure_decrement_ns_lock_1
+ *
+ */
+void secure_decrement_ns_lock_1(void)
+{
+#ifndef TFM_PSA_API
+    uint32_t testcase_id = CORE_TEST_ID_BLOCK;
+    psa_invec in_vec = {&testcase_id, sizeof(testcase_id)};
+
+    TRY_SFN(tfm_spm_core_test_sfn_veneer, &in_vec, 1, NULL, 0);
+#else
+    psa_status_t err;
+
+    err = psa_test_common(SPM_CORE_TEST_BLOCK_SID,
+                          SPM_CORE_TEST_BLOCK_VERSION,
+                          NULL, 0, NULL, 0);
+    if (err != PSA_SUCCESS) {
+        TEST_LOG("Secure call to sfn block failed, generic!");
+    }
+#endif
+}
+
+/**
+ * \brief secure_decrement_ns_lock_2
+ *
+ */
+void secure_decrement_ns_lock_2(void)
+{
+#ifndef TFM_PSA_API
+    uint32_t testcase_id = CORE_TEST_ID_BLOCK;
+    psa_invec in_vec = {&testcase_id, sizeof(testcase_id)};
+
+    TRY_SFN(tfm_spm_core_test_sfn_veneer, &in_vec, 1, NULL, 0);
+#else
+    psa_status_t err;
+
+    err = psa_test_common(SPM_CORE_TEST_BLOCK_SID,
+                          SPM_CORE_TEST_BLOCK_VERSION,
+                          NULL, 0, NULL, 0);
+    if (err != PSA_SUCCESS) {
+        TEST_LOG("Secure call to sfn block failed, generic!");
+    }
+#endif
+}
+/**
+ * \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())); \
+      TEST_LOG(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) {
+        TEST_LOG("Running [mid_task] while [pri_task] is blocked");
+    } else if (thread_pri_state == osThreadTerminated) {
+        TEST_LOG("Running [mid_task] while [pri_task] is terminated");
+    } else {
+        TEST_LOG("Running [mid_task]");
+    }
+
+    /* Do non TFM related, non blocking, operations */
+    for (idx=0; idx<0x3ffffff; idx++) {
+    }
+
+    TEST_LOG("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) {
+        TEST_LOG("Scenario 1 - Sequential");
+    } else if (test_type == TEST_TYPE_2) {
+        TEST_LOG("Scenario 2 - Priority");
+        thread_id = osThreadNew(pri_task, &ns_lock_opt_pri, &tattr_pri);
+    } else if (test_type == TEST_TYPE_3) {
+        TEST_LOG("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) {
+        TEST_LOG("Scenario 4 - non-NS lock");
+        ns_lock_opt.use_ns_lock = false;
+    } else if (test_type == TEST_TYPE_5) {
+        TEST_LOG("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) {
+        TEST_LOG("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 {
+        TEST_LOG("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) {
+        TEST_LOG("Scenario 1 - test finished\n");
+    } else if (test_type == TEST_TYPE_2) {
+        osThreadJoin(thread_id);
+        TEST_LOG("Scenario 2 - test finished\n");
+    } else if (test_type == TEST_TYPE_3) {
+        osThreadJoin(thread_id);
+        osThreadJoin(thread_id_mid);
+        TEST_LOG("Scenario 3 - test finished\n");
+    } else if (test_type == TEST_TYPE_4) {
+        TEST_LOG("Scenario 4 - test finished\n");
+    } else if (test_type == TEST_TYPE_5) {
+        osThreadJoin(thread_id);
+        TEST_LOG("Scenario 5 - test finished\n");
+    } else if (test_type == TEST_TYPE_6) {
+        osThreadJoin(thread_id);
+        TEST_LOG("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);
+    }
+}
diff --git a/test/suites/core/non_secure/core_ns_positive_testsuite.c b/test/suites/core/non_secure/core_ns_positive_testsuite.c
new file mode 100644
index 0000000..82b5fac
--- /dev/null
+++ b/test/suites/core/non_secure/core_ns_positive_testsuite.c
@@ -0,0 +1,853 @@
+/*
+ * Copyright (c) 2017-2020, Arm Limited. All rights reserved.
+ * Copyright (c) 2020, Cypress Semiconductor Corporation. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "core_ns_tests.h"
+#include "cmsis.h"
+#include "tfm_api.h"
+#include "tfm_plat_test.h"
+#include "test/suites/core/non_secure/core_test_api.h"
+#include "test/test_services/tfm_core_test/core_test_defs.h"
+#ifdef TFM_PSA_API
+#include "psa_manifest/sid.h"
+#else  /* TFM_PSA_API */
+#include "tfm_veneers.h"
+#endif /* TFM_PSA_API */
+#ifdef TFM_ENABLE_IRQ_TEST
+#include "tfm_peripherals_def.h"
+#endif
+
+/* Define test suite for core tests */
+/* List of tests */
+
+#define TOSTRING(x) #x
+#define CORE_TEST_DESCRIPTION(number, fn, description) \
+    {fn, "TFM_CORE_TEST_"TOSTRING(number),\
+     description, {TEST_PASSED} }
+
+#ifndef TFM_PSA_API
+static void tfm_core_test_get_caller_client_id(struct test_result_t *ret);
+static void tfm_core_test_spm_request(struct test_result_t *ret);
+#endif /* TFM_PSA_API */
+static void tfm_core_test_ns_thread(struct test_result_t *ret);
+static void tfm_core_test_check_init(struct test_result_t *ret);
+#ifdef ENABLE_TFM_CORE_RECURSION_TESTS
+static void tfm_core_test_recursion(struct test_result_t *ret);
+#endif
+static void tfm_core_test_buffer_check(struct test_result_t *ret);
+static void tfm_core_test_ss_to_ss(struct test_result_t *ret);
+static void tfm_core_test_ss_to_ss_buffer(struct test_result_t *ret);
+#ifdef TFM_ENABLE_PERIPH_ACCESS_TEST
+static void tfm_core_test_peripheral_access(struct test_result_t *ret);
+#endif
+static void tfm_core_test_iovec_sanitization(struct test_result_t *ret);
+static void tfm_core_test_outvec_write(struct test_result_t *ret);
+#ifdef TFM_ENABLE_IRQ_TEST
+static void tfm_core_test_irq(struct test_result_t *ret);
+
+static enum irq_test_scenario_t executing_irq_test_scenario = IRQ_TEST_SCENARIO_NONE;
+static struct irq_test_execution_data_t irq_test_execution_data = {0};
+#endif
+
+static struct test_t core_tests[] = {
+CORE_TEST_DESCRIPTION(CORE_TEST_ID_NS_THREAD, tfm_core_test_ns_thread,
+    "Test service request from NS thread mode"),
+CORE_TEST_DESCRIPTION(CORE_TEST_ID_CHECK_INIT, tfm_core_test_check_init,
+    "Test the success of service init"),
+#ifdef ENABLE_TFM_CORE_RECURSION_TESTS
+CORE_TEST_DESCRIPTION(CORE_TEST_ID_RECURSION, tfm_core_test_recursion,
+    "Test direct recursion of secure services"),
+#endif
+#ifdef TFM_ENABLE_IRQ_TEST
+CORE_TEST_DESCRIPTION(CORE_TEST_ID_SECURE_IRQ,
+    tfm_core_test_irq,
+    "Test secure irq"),
+#endif
+CORE_TEST_DESCRIPTION(CORE_TEST_ID_BUFFER_CHECK, tfm_core_test_buffer_check,
+    "Test secure service buffer accesses"),
+CORE_TEST_DESCRIPTION(CORE_TEST_ID_SS_TO_SS, tfm_core_test_ss_to_ss,
+    "Test secure service to service call"),
+CORE_TEST_DESCRIPTION(CORE_TEST_ID_SS_TO_SS_BUFFER,
+    tfm_core_test_ss_to_ss_buffer,
+    "Test secure service to service call with buffer handling"),
+#ifdef TFM_ENABLE_PERIPH_ACCESS_TEST
+CORE_TEST_DESCRIPTION(CORE_TEST_ID_PERIPHERAL_ACCESS,
+    tfm_core_test_peripheral_access,
+    "Test service peripheral access"),
+#endif
+#ifndef TFM_PSA_API
+CORE_TEST_DESCRIPTION(CORE_TEST_ID_GET_CALLER_CLIENT_ID,
+    tfm_core_test_get_caller_client_id,
+    "Test get caller client ID function"),
+CORE_TEST_DESCRIPTION(CORE_TEST_ID_SPM_REQUEST,
+    tfm_core_test_spm_request,
+    "Test SPM request function"),
+#endif /* TFM_PSA_API */
+CORE_TEST_DESCRIPTION(CORE_TEST_ID_IOVEC_SANITIZATION,
+    tfm_core_test_iovec_sanitization,
+    "Test service parameter sanitization"),
+CORE_TEST_DESCRIPTION(CORE_TEST_ID_OUTVEC_WRITE,
+    tfm_core_test_outvec_write,
+    "Test outvec write"),
+};
+
+void register_testsuite_ns_core_positive(struct test_suite_t *p_test_suite)
+{
+    uint32_t list_size;
+
+    list_size = (sizeof(core_tests) / sizeof(core_tests[0]));
+
+    set_testsuite("Core non-secure positive tests (TFM_CORE_TEST_1XXX)",
+                  core_tests, list_size, p_test_suite);
+}
+
+#ifdef TFM_PSA_API
+static psa_status_t psa_test_common(uint32_t sid, uint32_t version,
+                                    const psa_invec *in_vecs, size_t in_len,
+                                    psa_outvec *out_vecs, size_t out_len)
+{
+    psa_handle_t handle;
+    psa_status_t status;
+
+    handle = psa_connect(sid, version);
+    if (handle <= 0) {
+        return CORE_TEST_ERRNO_INVALID_PARAMETER;
+    }
+
+    status = psa_call(handle, PSA_IPC_CALL, in_vecs, in_len, out_vecs, out_len);
+    if (status < 0) {
+        status = CORE_TEST_ERRNO_UNEXPECTED_CORE_BEHAVIOUR;
+    }
+
+    psa_close(handle);
+    return status;
+}
+#endif /* TFM_PSA_API */
+
+static void tfm_core_test_ns_thread(struct test_result_t *ret)
+{
+    int32_t err;
+#ifndef TFM_PSA_API
+    int32_t test_case_id = CORE_TEST_ID_NS_THREAD;
+    psa_invec in_vec[] = { {&test_case_id, sizeof(int32_t)} };
+
+    err = tfm_spm_core_test_sfn_veneer(in_vec, 1, NULL, 0);
+#else /* TFM_PSA_API */
+    err = psa_test_common(SPM_CORE_TEST_NS_THREAD_SID,
+                          SPM_CORE_TEST_NS_THREAD_VERSION,
+                          NULL, 0, NULL, 0);
+#endif /* TFM_PSA_API */
+
+    if (err != CORE_TEST_ERRNO_SUCCESS) {
+        TEST_FAIL("Secure function call from thread mode should be successful");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+#ifdef TFM_ENABLE_PERIPH_ACCESS_TEST
+static void tfm_core_test_peripheral_access(struct test_result_t *ret)
+{
+    int32_t err;
+
+#ifndef TFM_PSA_API
+    int32_t test_case_id = CORE_TEST_ID_PERIPHERAL_ACCESS;
+    psa_invec in_vec[] = { {&test_case_id, sizeof(int32_t)} };
+    struct tfm_core_test_call_args_t args = {in_vec, 1, NULL, 0};
+
+    err = tfm_core_test_call(tfm_spm_core_test_sfn_veneer, &args);
+#else /* TFM_PSA_API */
+    err = psa_test_common(SPM_CORE_TEST_PERIPHERAL_ACCESS_SID,
+                          SPM_CORE_TEST_PERIPHERAL_ACCESS_VERSION,
+                          NULL, 0, NULL, 0);
+#endif /* TFM_PSA_API */
+
+    switch (err) {
+    case CORE_TEST_ERRNO_SUCCESS:
+        ret->val = TEST_PASSED;
+        return;
+    case CORE_TEST_ERRNO_PERIPHERAL_ACCESS_FAILED:
+        TEST_FAIL("Service peripheral access failed.");
+        return;
+    default:
+        TEST_FAIL("Unexpected return value received.");
+        return;
+    }
+}
+#endif
+
+static void empty_iovecs(psa_invec invec[], psa_outvec outvec[])
+{
+    int i = 0;
+
+    for (i = 0; i < PSA_MAX_IOVEC; ++i) {
+        invec[i].len = 0;
+        invec[i].base = NULL;
+        outvec[i].len = 0;
+        outvec[i].base = NULL;
+    }
+}
+
+static void full_iovecs(psa_invec invec[], psa_outvec outvec[])
+{
+    int i = 0;
+
+    for (i = 0; i < PSA_MAX_IOVEC; ++i) {
+        invec[i].len = sizeof(psa_invec);
+        invec[i].base = invec + i;
+        outvec[i].len = PSA_MAX_IOVEC * sizeof(psa_outvec);
+        outvec[i].base = outvec;
+    }
+}
+
+static void tfm_core_test_iovec_sanitization(struct test_result_t *ret)
+{
+    int32_t err;
+    psa_invec in_vec[PSA_MAX_IOVEC] = {
+                                   {NULL, 0}, {NULL, 0}, {NULL, 0}, {NULL, 0} };
+    psa_outvec out_vec[PSA_MAX_IOVEC] = {
+                                   {NULL, 0}, {NULL, 0}, {NULL, 0}, {NULL, 0} };
+
+#ifndef TFM_PSA_API
+    struct tfm_core_test_call_args_t args = {NULL, 0, NULL, 0};
+#endif /* TFM_PSA_API */
+
+    /* Check a few valid cases */
+
+    /* Execute a call with valid iovecs (empty) */
+    empty_iovecs(in_vec, out_vec);
+
+#ifndef TFM_PSA_API
+    args.in_vec = NULL;
+    args.in_len = 0;
+    args.out_vec = NULL;
+    args.out_len = 0;
+    err = tfm_core_test_call(tfm_spm_core_test_2_slave_service_veneer, &args);
+#else /* TFM_PSA_API */
+    err = psa_test_common(SPM_CORE_TEST_2_SLAVE_SERVICE_SID,
+                          SPM_CORE_TEST_2_SLAVE_SERVICE_VERSION,
+                          NULL, 0, NULL, 0);
+#endif /* TFM_PSA_API */
+    if (err != CORE_TEST_ERRNO_SUCCESS_2) {
+        TEST_FAIL("iovec sanitization failed on empty vectors.");
+        return;
+    }
+
+    /* Execute a call with valid iovecs (full) */
+    full_iovecs(in_vec, out_vec);
+#ifndef TFM_PSA_API
+    args.in_vec = in_vec;
+    args.in_len = 2;
+    args.out_vec = out_vec;
+    args.out_len = PSA_MAX_IOVEC - args.in_len;
+    err = tfm_core_test_call(tfm_spm_core_test_2_slave_service_veneer, &args);
+#else /* TFM_PSA_API */
+    err = psa_test_common(SPM_CORE_TEST_2_SLAVE_SERVICE_SID,
+                          SPM_CORE_TEST_2_SLAVE_SERVICE_VERSION,
+                          in_vec, 2, out_vec, 2);
+#endif /* TFM_PSA_API */
+    if (err != CORE_TEST_ERRNO_SUCCESS_2) {
+        TEST_FAIL("iovec sanitization failed on full vectors.");
+        return;
+    }
+
+    /* Execute a call with valid iovecs (different number of vectors) */
+    full_iovecs(in_vec, out_vec);
+#ifndef TFM_PSA_API
+    args.in_vec = in_vec;
+    args.in_len = 2;
+    args.out_vec = out_vec;
+    args.out_len = 1;
+    err = tfm_core_test_call(tfm_spm_core_test_2_slave_service_veneer, &args);
+#else /* TFM_PSA_API */
+    err = psa_test_common(SPM_CORE_TEST_2_SLAVE_SERVICE_SID,
+                          SPM_CORE_TEST_2_SLAVE_SERVICE_VERSION,
+                          in_vec, 2, out_vec, 1);
+#endif /* TFM_PSA_API */
+    if (err != CORE_TEST_ERRNO_SUCCESS_2) {
+        TEST_FAIL(
+                 "iovec sanitization failed on valid, partially full vectors.");
+        return;
+    }
+
+    /* Check some further valid cases to be sure that checks happen only iovecs
+     * that specified valid by the parameters
+     */
+
+    /* Execute a call with base = 0 in single vector in outvec that is out of
+     * range
+     */
+    full_iovecs(in_vec, out_vec);
+    out_vec[1].base = NULL;
+#ifndef TFM_PSA_API
+    args.in_vec = in_vec;
+    args.in_len = 2;
+    args.out_vec = out_vec;
+    args.out_len = 1;
+    err = tfm_core_test_call(tfm_spm_core_test_2_slave_service_veneer, &args);
+#else /* TFM_PSA_API */
+    err = psa_test_common(SPM_CORE_TEST_2_SLAVE_SERVICE_SID,
+                          SPM_CORE_TEST_2_SLAVE_SERVICE_VERSION,
+                          in_vec, 2, out_vec, 1);
+#endif /* TFM_PSA_API */
+    if (err != CORE_TEST_ERRNO_SUCCESS_2) {
+        TEST_FAIL("content of an outvec out of range should not be checked");
+        return;
+    }
+
+    /* Execute a call with len = 0 in single vector in invec that is out of
+     * range
+     */
+    full_iovecs(in_vec, out_vec);
+    in_vec[2].len = 0;
+#ifndef TFM_PSA_API
+    args.in_vec = in_vec;
+    args.in_len = 2;
+    args.out_vec = out_vec;
+    args.out_len = 1;
+    err = tfm_core_test_call(tfm_spm_core_test_2_slave_service_veneer, &args);
+#else /* TFM_PSA_API */
+    err = psa_test_common(SPM_CORE_TEST_2_SLAVE_SERVICE_SID,
+                          SPM_CORE_TEST_2_SLAVE_SERVICE_VERSION,
+                          in_vec, 2, out_vec, 1);
+#endif /* TFM_PSA_API */
+    if (err != CORE_TEST_ERRNO_SUCCESS_2) {
+        TEST_FAIL("content of an outvec out of range should not be checked");
+        return;
+    }
+
+    /* Execute a call with len = 0 in single vector in invec */
+    full_iovecs(in_vec, out_vec);
+    in_vec[1].len = 0;
+    in_vec[1].base = NULL;
+#ifndef TFM_PSA_API
+    args.in_vec = in_vec;
+    args.in_len = 2;
+    args.out_vec = out_vec;
+    args.out_len = 2;
+    err = tfm_core_test_call(tfm_spm_core_test_2_slave_service_veneer, &args);
+#else /* TFM_PSA_API */
+    err = psa_test_common(SPM_CORE_TEST_2_SLAVE_SERVICE_SID,
+                          SPM_CORE_TEST_2_SLAVE_SERVICE_VERSION,
+                          in_vec, 2, out_vec, 2);
+#endif /* TFM_PSA_API */
+    if (err != CORE_TEST_ERRNO_SUCCESS_2) {
+        TEST_FAIL("If the len of an invec is 0, the base should be ignored");
+        return;
+    }
+
+    /* Execute a call with len = 0 in single vector in outvec */
+    full_iovecs(in_vec, out_vec);
+    out_vec[1].len = 0;
+    out_vec[1].base = NULL;
+#ifndef TFM_PSA_API
+    args.in_vec = in_vec;
+    args.in_len = 2;
+    args.out_vec = out_vec;
+    args.out_len = 2;
+    err = tfm_core_test_call(tfm_spm_core_test_2_slave_service_veneer, &args);
+#else /* TFM_PSA_API */
+    err = psa_test_common(SPM_CORE_TEST_2_SLAVE_SERVICE_SID,
+                          SPM_CORE_TEST_2_SLAVE_SERVICE_VERSION,
+                          in_vec, 2, out_vec, 2);
+#endif /* TFM_PSA_API */
+    if (err != CORE_TEST_ERRNO_SUCCESS_2) {
+        TEST_FAIL("If the len of an outvec is 0, the base should be ignored");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+static void tfm_core_test_outvec_write(struct test_result_t *ret)
+{
+    int32_t err;
+    int i;
+    uint8_t in_buf_0[] = {0, 1, 2, 3, 4};
+    uint8_t in_buf_1[] = {1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89};
+    uint8_t out_buf_0[sizeof(in_buf_0)];
+    uint8_t out_buf_1[sizeof(in_buf_1)];
+
+    psa_invec in_vec[PSA_MAX_IOVEC] = { {in_buf_0, sizeof(in_buf_0)},
+                                        {in_buf_1, sizeof(in_buf_1)} };
+    psa_outvec out_vec[PSA_MAX_IOVEC] = { {out_buf_0, sizeof(out_buf_0) },
+                                        {out_buf_1, sizeof(out_buf_1)} };
+#ifndef TFM_PSA_API
+    int32_t test_case_id = CORE_TEST_ID_OUTVEC_WRITE;
+    struct tfm_core_test_call_args_t args1 = {in_vec, 2, out_vec, 2};
+    struct tfm_core_test_call_args_t args2 = {in_vec, 1, NULL, 0};
+
+    err = tfm_core_test_call(tfm_spm_core_test_2_get_every_second_byte_veneer,
+                                                                        &args1);
+#else /* TFM_PSA_API */
+    err = psa_test_common(SPM_CORE_TEST_2_GET_EVERY_SECOND_BYTE_SID,
+                          SPM_CORE_TEST_2_GET_EVERY_SECOND_BYTE_VERSION,
+                          in_vec, 2, out_vec, 2);
+#endif /* TFM_PSA_API */
+
+    if (err != CORE_TEST_ERRNO_SUCCESS) {
+        TEST_FAIL("call to secure function should be successful");
+        return;
+    }
+
+    if (out_vec[0].len != sizeof(in_buf_0)/2 ||
+        out_vec[1].len != sizeof(in_buf_1)/2) {
+        TEST_FAIL("Number of elements in outvec is not set properly");
+        return;
+    }
+    for (i = 1; i < sizeof(in_buf_0); i += 2) {
+        if (((uint8_t *)out_vec[0].base)[i/2] != in_buf_0[i]) {
+            TEST_FAIL("result is not correct");
+            return;
+        }
+    }
+    for (i = 1; i < sizeof(in_buf_1); i += 2) {
+        if (((uint8_t *)out_vec[1].base)[i/2] != in_buf_1[i]) {
+            TEST_FAIL("result is not correct");
+            return;
+        }
+    }
+
+    /* do the same test on the secure side */
+#ifndef TFM_PSA_API
+    in_vec[0].base = &test_case_id;
+    in_vec[0].len = sizeof(int32_t);
+    err = tfm_core_test_call(tfm_spm_core_test_sfn_veneer, &args2);
+#else /* TFM_PSA_API */
+    err = psa_test_common(SPM_CORE_TEST_OUTVEC_WRITE_SID,
+                          SPM_CORE_TEST_OUTVEC_WRITE_VERSION,
+                          in_vec, 0, out_vec, 0);
+#endif /* TFM_PSA_API */
+
+    if (err != CORE_TEST_ERRNO_SUCCESS) {
+        TEST_FAIL("Failed to execute secure side test");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+#ifdef TFM_ENABLE_IRQ_TEST
+static int32_t prepare_test_scenario_ns(
+                               enum irq_test_scenario_t test_scenario,
+                               struct irq_test_execution_data_t *execution_data)
+{
+    executing_irq_test_scenario = test_scenario;
+    switch (test_scenario) {
+    case IRQ_TEST_SCENARIO_NONE:
+        return CORE_TEST_ERRNO_INVALID_PARAMETER;
+    case IRQ_TEST_SCENARIO_1:
+    case IRQ_TEST_SCENARIO_2:
+    case IRQ_TEST_SCENARIO_3:
+    case IRQ_TEST_SCENARIO_4:
+        /* nothing to be done here */
+        break;
+    case IRQ_TEST_SCENARIO_5:
+        execution_data->timer1_triggered = 0;
+        tfm_plat_test_non_secure_timer_start();
+        break;
+    default:
+        return CORE_TEST_ERRNO_INVALID_PARAMETER;
+    }
+
+    return CORE_TEST_ERRNO_SUCCESS;
+}
+
+static int32_t execute_test_scenario_ns(
+                               enum irq_test_scenario_t test_scenario,
+                               struct irq_test_execution_data_t *execution_data)
+{
+
+    switch (test_scenario) {
+    case IRQ_TEST_SCENARIO_NONE:
+        return CORE_TEST_ERRNO_INVALID_PARAMETER;
+    case IRQ_TEST_SCENARIO_1:
+        if (execution_data->timer0_triggered) {
+            return CORE_TEST_ERRNO_TEST_FAULT;
+        }
+        while (!execution_data->timer0_triggered) {
+            ;
+        }
+        break;
+    case IRQ_TEST_SCENARIO_2:
+    case IRQ_TEST_SCENARIO_3:
+    case IRQ_TEST_SCENARIO_4:
+    case IRQ_TEST_SCENARIO_5:
+        /* nothing to be done here */
+        break;
+    default:
+        return CORE_TEST_ERRNO_INVALID_PARAMETER;
+    }
+
+    return CORE_TEST_ERRNO_SUCCESS;
+}
+
+void TIMER1_Handler (void)
+{
+    tfm_plat_test_non_secure_timer_stop();
+
+    switch (executing_irq_test_scenario) {
+    case IRQ_TEST_SCENARIO_NONE:
+    case IRQ_TEST_SCENARIO_1:
+    case IRQ_TEST_SCENARIO_2:
+    case IRQ_TEST_SCENARIO_3:
+    case IRQ_TEST_SCENARIO_4:
+        while (1) {}
+        /* shouldn't happen */
+        break;
+    case IRQ_TEST_SCENARIO_5:
+        irq_test_execution_data.timer1_triggered = 1;
+        break;
+    default:
+        while (1) {}
+        /* shouldn't happen */
+        break;
+    }
+}
+
+static int32_t tfm_core_test_irq_scenario(
+                                         enum irq_test_scenario_t test_scenario)
+{
+    struct irq_test_execution_data_t *execution_data_address =
+                                                       &irq_test_execution_data;
+    uint32_t scenario = test_scenario;
+
+    psa_invec in_vec[] = {
+                 {&scenario, sizeof(uint32_t)},
+                 {&execution_data_address,
+                                  sizeof(struct irq_test_execution_data_t *)} };
+    int32_t err;
+
+#ifdef TFM_PSA_API
+    err = psa_test_common(SPM_CORE_IRQ_TEST_1_PREPARE_TEST_SCENARIO_SID,
+                          SPM_CORE_IRQ_TEST_1_PREPARE_TEST_SCENARIO_VERSION,
+                          in_vec, 2, NULL, 0);
+#else
+    err = tfm_spm_irq_test_1_prepare_test_scenario_veneer(in_vec, 2, NULL, 0);
+#endif
+    if (err != CORE_TEST_ERRNO_SUCCESS) {
+        return err;
+    }
+
+#ifdef TFM_PSA_API
+    err = psa_test_common(SPM_CORE_TEST_2_PREPARE_TEST_SCENARIO_SID,
+                          SPM_CORE_TEST_2_PREPARE_TEST_SCENARIO_VERSION,
+                          in_vec, 2, NULL, 0);
+#else
+    err = tfm_spm_core_test_2_prepare_test_scenario_veneer(in_vec, 2, NULL, 0);
+#endif
+    if (err != CORE_TEST_ERRNO_SUCCESS) {
+        return err;
+    }
+
+    err = prepare_test_scenario_ns(test_scenario, &irq_test_execution_data);
+    if (err != CORE_TEST_ERRNO_SUCCESS) {
+        return err;
+    }
+
+#ifdef TFM_PSA_API
+    err = psa_test_common(SPM_CORE_IRQ_TEST_1_EXECUTE_TEST_SCENARIO_SID,
+                          SPM_CORE_IRQ_TEST_1_EXECUTE_TEST_SCENARIO_VERSION,
+                          in_vec, 2, NULL, 0);
+#else
+    err = tfm_spm_irq_test_1_execute_test_scenario_veneer(in_vec, 1, NULL, 0);
+#endif
+    if (err != CORE_TEST_ERRNO_SUCCESS) {
+        return err;
+    }
+
+#ifdef TFM_PSA_API
+    err = psa_test_common(SPM_CORE_TEST_2_EXECUTE_TEST_SCENARIO_SID,
+                          SPM_CORE_TEST_2_EXECUTE_TEST_SCENARIO_VERSION,
+                          in_vec, 2, NULL, 0);
+#else
+    err = tfm_spm_core_test_2_execute_test_scenario_veneer(in_vec, 1, NULL, 0);
+#endif
+    if (err != CORE_TEST_ERRNO_SUCCESS) {
+        return err;
+    }
+
+    err = execute_test_scenario_ns(test_scenario, &irq_test_execution_data);
+    if (err != CORE_TEST_ERRNO_SUCCESS) {
+        return err;
+    }
+
+    return CORE_TEST_ERRNO_SUCCESS;
+}
+
+static void tfm_core_test_irq(struct test_result_t *ret)
+{
+    int32_t err;
+
+    struct irq_test_execution_data_t *execution_data_address =
+                                                       &irq_test_execution_data;
+    uint32_t scenario = IRQ_TEST_SCENARIO_NONE;
+
+    psa_invec in_vec[] = {
+                 {&scenario, sizeof(uint32_t)},
+                 {&execution_data_address,
+                                  sizeof(struct irq_test_execution_data_t *)} };
+
+    NVIC_EnableIRQ(TFM_TIMER1_IRQ);
+
+    err = tfm_core_test_irq_scenario(IRQ_TEST_SCENARIO_1);
+    if (err != CORE_TEST_ERRNO_SUCCESS) {
+        TEST_FAIL("Failed to execute IRQ test scenario 1.");
+        return;
+    }
+
+    err = tfm_core_test_irq_scenario(IRQ_TEST_SCENARIO_2);
+    if (err != CORE_TEST_ERRNO_SUCCESS) {
+        TEST_FAIL("Failed to execute IRQ test scenario 2.");
+        return;
+    }
+
+    err = tfm_core_test_irq_scenario(IRQ_TEST_SCENARIO_3);
+    if (err != CORE_TEST_ERRNO_SUCCESS) {
+        TEST_FAIL("Failed to execute IRQ test scenario 3.");
+        return;
+    }
+
+    err = tfm_core_test_irq_scenario(IRQ_TEST_SCENARIO_4);
+    if (err != CORE_TEST_ERRNO_SUCCESS) {
+        TEST_FAIL("Failed to execute IRQ test scenario 4.");
+        return;
+    }
+
+    err = tfm_core_test_irq_scenario(IRQ_TEST_SCENARIO_5);
+    if (err != CORE_TEST_ERRNO_SUCCESS) {
+        TEST_FAIL("Failed to execute IRQ test scenario 5.");
+        return;
+    }
+
+    /* finally call prepare with scenario none as a teardown */
+#ifdef TFM_PSA_API
+    err = psa_test_common(SPM_CORE_IRQ_TEST_1_PREPARE_TEST_SCENARIO_SID,
+                          SPM_CORE_IRQ_TEST_1_PREPARE_TEST_SCENARIO_VERSION,
+                          in_vec, 2, NULL, 0);
+#else
+    err = tfm_spm_irq_test_1_prepare_test_scenario_veneer(in_vec, 2, NULL, 0);
+#endif
+    if (err != CORE_TEST_ERRNO_SUCCESS) {
+        TEST_FAIL("Failed to tear down IRQ tests");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+#endif
+
+/*
+ * \brief Tests whether the initialisation of the service was successful.
+ *
+ */
+static void tfm_core_test_check_init(struct test_result_t *ret)
+{
+    int32_t err;
+#ifndef TFM_PSA_API
+    struct tfm_core_test_call_args_t args = {NULL, 0, NULL, 0};
+
+    err = tfm_core_test_call(tfm_spm_core_test_sfn_init_success_veneer, &args);
+#else /* TFM_PSA_API */
+    err = psa_test_common(SPM_CORE_TEST_INIT_SUCCESS_SID,
+                          SPM_CORE_TEST_INIT_SUCCESS_VERSION,
+                          NULL, 0, NULL, 0);
+#endif /* TFM_PSA_API */
+
+    if (err != CORE_TEST_ERRNO_SUCCESS) {
+        TEST_FAIL("Failed to initialise test service.");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+#ifdef ENABLE_TFM_CORE_RECURSION_TESTS
+/**
+ * \brief Tests what happens when a service calls itself directly
+ */
+static void tfm_core_test_recursion(struct test_result_t *ret)
+{
+    /*
+     * TODO
+     * Recursive calls will trigger a fatal error. Current test framework cannot
+     * recover from that error.
+     * Leave a test case stub here for further implementation.
+     */
+    TEST_FAIL("The test case is not implemented yet.");
+}
+#endif
+
+static void tfm_core_test_buffer_check(struct test_result_t *ret)
+{
+    int32_t res, i;
+
+    uint32_t inbuf[] = {1, 2, 3, 4, 0xAAAFFF, 0xFFFFFFFF};
+    uint32_t outbuf[16] = {0};
+    int32_t result = 1;
+    psa_invec in_vec[] = { {inbuf, sizeof(inbuf)} };
+    psa_outvec outvec[] = { {outbuf, sizeof(outbuf)},
+                           {&result, sizeof(int32_t)} };
+#ifndef TFM_PSA_API
+    struct tfm_core_test_call_args_t args = {in_vec, 1, outvec, 2};
+
+    res = tfm_core_test_call(tfm_spm_core_test_2_sfn_invert_veneer, &args);
+#else /* TFM_PSA_API */
+    res = psa_test_common(SPM_CORE_TEST_2_INVERT_SID,
+                          SPM_CORE_TEST_2_INVERT_VERSION,
+                          in_vec, 1, outvec, 2);
+#endif /* TFM_PSA_API */
+    if (res != CORE_TEST_ERRNO_SUCCESS) {
+        TEST_FAIL("Call to secure service should be successful.");
+        return;
+    }
+    if (result == 0) {
+        for (i = 0; i < sizeof(inbuf) >> 2; i++) {
+            if (outbuf[i] != ~inbuf[i]) {
+                TEST_FAIL("Secure function failed to modify buffer.");
+                return;
+            }
+        }
+        for (; i < sizeof(outbuf) >> 2; i++) {
+            if (outbuf[i] != 0) {
+                TEST_FAIL("Secure function buffer access overflow.");
+                return;
+            }
+        }
+    } else {
+        TEST_FAIL("Secure service returned error.");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+static void tfm_core_test_ss_to_ss(struct test_result_t *ret)
+{
+    int32_t err;
+
+#ifndef TFM_PSA_API
+    int32_t test_case_id = CORE_TEST_ID_SS_TO_SS;
+    psa_invec in_vec[] = { {&test_case_id, sizeof(int32_t)} };
+    struct tfm_core_test_call_args_t args = {in_vec, 1, NULL, 0};
+
+    err = tfm_core_test_call(tfm_spm_core_test_sfn_veneer, &args);
+#else /* TFM_PSA_API */
+    err = psa_test_common(SPM_CORE_TEST_SS_TO_SS_SID,
+                          SPM_CORE_TEST_SS_TO_SS_VERSION,
+                          NULL, 0, NULL, 0);
+#endif /* TFM_PSA_API */
+
+    if (err != CORE_TEST_ERRNO_SUCCESS) {
+        TEST_FAIL("The internal service call failed.");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+static void tfm_core_test_ss_to_ss_buffer(struct test_result_t *ret)
+{
+    int32_t res, i;
+
+    uint32_t inbuf[] = {1, 2, 3, 4, 0xAAAFFF, 0xFFFFFFFF};
+    uint32_t outbuf[16] = {0};
+    int32_t len = (int32_t)sizeof(inbuf) >> 2;
+    psa_outvec out_vec[] = { {outbuf, sizeof(outbuf)} };
+#ifndef TFM_PSA_API
+    int32_t test_case_id = CORE_TEST_ID_SS_TO_SS_BUFFER;
+    psa_invec in_vec[] = { {&test_case_id, sizeof(int32_t)},
+                          {inbuf, sizeof(inbuf)},
+                          {&len, sizeof(int32_t)} };
+    struct tfm_core_test_call_args_t args = {in_vec, 3, out_vec, 1};
+
+    res = tfm_core_test_call(tfm_spm_core_test_sfn_veneer, &args);
+#else /* TFM_PSA_API */
+    psa_invec in_vec[] = {{inbuf, sizeof(inbuf)},
+                          {&len, sizeof(int32_t)} };
+
+    res = psa_test_common(SPM_CORE_TEST_SS_TO_SS_BUFFER_SID,
+                          SPM_CORE_TEST_SS_TO_SS_BUFFER_VERSION,
+                          in_vec, 2, out_vec, 1);
+#endif /* TFM_PSA_API */
+    switch (res) {
+    case CORE_TEST_ERRNO_SUCCESS:
+        for (i = 0; i < sizeof(inbuf) >> 2; i++) {
+            if (outbuf[i] != ~inbuf[i]) {
+                TEST_FAIL("Secure function failed to modify buffer.");
+                return;
+            }
+        }
+        for (; i < sizeof(outbuf) >> 2; i++) {
+            if (outbuf[i] != 0) {
+                TEST_FAIL("Secure function buffer access overflow.");
+                return;
+            }
+        }
+        ret->val = TEST_PASSED;
+        return;
+    case CORE_TEST_ERRNO_INVALID_BUFFER:
+        TEST_FAIL("NS buffer rejected by TF-M core.");
+        return;
+    case CORE_TEST_ERRNO_SLAVE_SP_CALL_FAILURE:
+        TEST_FAIL("Slave service call failed.");
+        return;
+    case CORE_TEST_ERRNO_SLAVE_SP_BUFFER_FAILURE:
+        TEST_FAIL("Slave secure function failed to modify buffer.");
+        return;
+    default:
+        TEST_FAIL("Secure service returned error.");
+        return;
+    }
+}
+
+#ifndef TFM_PSA_API
+static void tfm_core_test_get_caller_client_id(struct test_result_t *ret)
+{
+    int32_t err;
+    int32_t test_case_id = CORE_TEST_ID_GET_CALLER_CLIENT_ID;
+    psa_invec in_vec[] = { {&test_case_id, sizeof(int32_t)} };
+    struct tfm_core_test_call_args_t args = {in_vec, 1, NULL, 0};
+
+    err = tfm_core_test_call(tfm_spm_core_test_sfn_veneer, &args);
+
+    if (err != CORE_TEST_ERRNO_SUCCESS) {
+        TEST_FAIL("The internal service call failed.");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+static void tfm_core_test_spm_request(struct test_result_t *ret)
+{
+    int32_t err;
+#ifndef TFM_PSA_API
+    int32_t test_case_id = CORE_TEST_ID_SPM_REQUEST;
+    psa_invec in_vec[] = { {&test_case_id, sizeof(int32_t)} };
+    struct tfm_core_test_call_args_t args = {in_vec, 1, NULL, 0};
+
+    err = tfm_core_test_call(tfm_spm_core_test_sfn_veneer, &args);
+#else /* TFM_PSA_API */
+    err = psa_test_common(SPM_CORE_TEST_SPM_REQUEST_SID,
+                          SPM_CORE_TEST_SPM_REQUEST_VERSION,
+                          NULL, 0, NULL, 0);
+#endif /* TFM_PSA_API */
+
+    if (err != CORE_TEST_ERRNO_SUCCESS) {
+        TEST_FAIL("The SPM request failed.");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+#endif /* TFM_PSA_API */
diff --git a/test/suites/core/non_secure/core_ns_tests.h b/test/suites/core/non_secure/core_ns_tests.h
new file mode 100644
index 0000000..52d10b5
--- /dev/null
+++ b/test/suites/core/non_secure/core_ns_tests.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2017, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __CORE_NS_TESTS_H__
+#define __CORE_NS_TESTS_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "test/framework/test_framework.h"
+
+/**
+ * \brief Register testsuite for the core positive tests.
+ *
+ * \param[in] p_test_suite  The test suite to be executed.
+ */
+void register_testsuite_ns_core_positive(struct test_suite_t *p_test_suite);
+
+/**
+ * \brief Register testsuite for the core interactive tests.
+ *
+ * \param[in] p_test_suite  The test suite to be executed.
+ */
+void register_testsuite_ns_core_interactive(struct test_suite_t *p_test_suite);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CORE_NS_TESTS_H__ */
diff --git a/test/suites/core/non_secure/core_test_api.c b/test/suites/core/non_secure/core_test_api.c
new file mode 100644
index 0000000..b49af5d
--- /dev/null
+++ b/test/suites/core/non_secure/core_test_api.c
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2017-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "core_test_api.h"
+#include "test/test_services/tfm_core_test/core_test_defs.h"
+
+int32_t tfm_core_test_call(int32_t (*fn_ptr)(struct psa_invec*, size_t,
+                                             struct psa_outvec*, size_t),
+                           struct tfm_core_test_call_args_t *args)
+{
+    return fn_ptr(args->in_vec, args->in_len,
+                  args->out_vec, args->out_len);
+}
diff --git a/test/suites/core/non_secure/core_test_api.h b/test/suites/core/non_secure/core_test_api.h
new file mode 100644
index 0000000..eaa8b44
--- /dev/null
+++ b/test/suites/core/non_secure/core_test_api.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2017-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __CORE_TEST_API_H__
+#define __CORE_TEST_API_H__
+
+#include <stdio.h>
+#include "cmsis_compiler.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief This structure is to pass iovec arguments to the tfm_core_test_call
+ *        function.
+ */
+struct tfm_core_test_call_args_t {
+    struct psa_invec *in_vec;   /*!< Array of psa_invec objects */
+    size_t in_len;              /*!< Number psa_invec objects in in_vec */
+    struct psa_outvec *out_vec; /*!< Array of psa_outvec objects */
+    size_t out_len;             /*!< Number psa_outvec objects in out_vec */
+};
+
+/**
+ * \brief Calls the secure function provided in \c fn_ptr
+ *
+ * \param[in] fn_ptr  Secure function to be called.
+ * \param[in] args    Arguments for fn_ptr.
+ *
+ * \return Returns value depending on fn_ptr.
+ */
+int32_t tfm_core_test_call(int32_t (*fn_ptr)(),
+                                        struct tfm_core_test_call_args_t *args);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CORE_TEST_API_H__ */
diff --git a/test/suites/crypto/CMakeLists.inc b/test/suites/crypto/CMakeLists.inc
new file mode 100644
index 0000000..ae6baf3
--- /dev/null
+++ b/test/suites/crypto/CMakeLists.inc
@@ -0,0 +1,84 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+#Definitions to compile the "Crypto test" module.
+#This file assumes it will be included from a project specific cmakefile, and
+#will not create a library or executable.
+#Inputs:
+# TFM_ROOT_DIR - root directory of the TF-M repo.
+#
+#Outputs:
+# Will modify include directories to make the source compile.
+# ALL_SRC_C: C source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+# ALL_SRC_CXX: C++ source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+# ALL_SRC_ASM: assembly source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+# Include directories will be modified by using the include_directories() commands as needed.
+
+#Get the current directory where this file is located.
+set(CRYPTO_TEST_DIR ${CMAKE_CURRENT_LIST_DIR})
+if(NOT DEFINED TFM_ROOT_DIR)
+  message(FATAL_ERROR "Please set TFM_ROOT_DIR before including this file.")
+endif()
+
+if (NOT DEFINED ENABLE_CRYPTO_SERVICE_TESTS)
+  message(FATAL_ERROR "Incomplete build configuration: ENABLE_CRYPTO_SERVICE_TESTS is undefined. ")
+elseif (ENABLE_CRYPTO_SERVICE_TESTS)
+  list(APPEND ALL_SRC_C_S "${CRYPTO_TEST_DIR}/secure/crypto_sec_interface_testsuite.c"
+                          "${CRYPTO_TEST_DIR}/crypto_tests_common.c")
+  list(APPEND ALL_SRC_C_NS "${CRYPTO_TEST_DIR}/non_secure/crypto_ns_interface_testsuite.c"
+                           "${CRYPTO_TEST_DIR}/crypto_tests_common.c")
+
+  #Enable the test cases by default
+  if (NOT DEFINED TFM_CRYPTO_TEST_ALG_CBC)
+    set(TFM_CRYPTO_TEST_ALG_CBC ON)
+  endif()
+  if (NOT DEFINED TFM_CRYPTO_TEST_ALG_CCM)
+    set(TFM_CRYPTO_TEST_ALG_CCM ON)
+  endif()
+  if (NOT DEFINED TFM_CRYPTO_TEST_ALG_CFB)
+    set(TFM_CRYPTO_TEST_ALG_CFB ON)
+  endif()
+  if (NOT DEFINED TFM_CRYPTO_TEST_ALG_CTR)
+    set(TFM_CRYPTO_TEST_ALG_CTR ON)
+  endif()
+  if (NOT DEFINED TFM_CRYPTO_TEST_ALG_GCM)
+    set(TFM_CRYPTO_TEST_ALG_GCM ON)
+  endif()
+  if (NOT DEFINED TFM_CRYPTO_TEST_ALG_SHA_512)
+    set(TFM_CRYPTO_TEST_ALG_SHA_512 ON)
+  endif()
+  if (NOT DEFINED TFM_CRYPTO_TEST_HKDF)
+    set(TFM_CRYPTO_TEST_HKDF ON)
+  endif()
+
+  if (TFM_CRYPTO_TEST_ALG_CBC)
+    add_definitions(-DTFM_CRYPTO_TEST_ALG_CBC)
+  endif()
+  if (TFM_CRYPTO_TEST_ALG_CCM)
+    add_definitions(-DTFM_CRYPTO_TEST_ALG_CCM)
+  endif()
+  if (TFM_CRYPTO_TEST_ALG_CFB)
+    add_definitions(-DTFM_CRYPTO_TEST_ALG_CFB)
+  endif()
+  if (TFM_CRYPTO_TEST_ALG_CTR)
+    add_definitions(-DTFM_CRYPTO_TEST_ALG_CTR)
+  endif()
+  if (TFM_CRYPTO_TEST_ALG_GCM)
+    add_definitions(-DTFM_CRYPTO_TEST_ALG_GCM)
+  endif()
+  if (TFM_CRYPTO_TEST_ALG_SHA_512)
+    add_definitions(-DTFM_CRYPTO_TEST_ALG_SHA_512)
+  endif()
+  if (TFM_CRYPTO_TEST_HKDF)
+    add_definitions(-DTFM_CRYPTO_TEST_HKDF)
+  endif()
+
+  #Setting include directories
+  embedded_include_directories(PATH ${TFM_ROOT_DIR} ABSOLUTE)
+  embedded_include_directories(PATH ${TFM_ROOT_DIR}/interface/include ABSOLUTE)
+
+endif()
diff --git a/test/suites/crypto/crypto_tests_common.c b/test/suites/crypto/crypto_tests_common.c
new file mode 100644
index 0000000..edbca4d
--- /dev/null
+++ b/test/suites/crypto/crypto_tests_common.c
@@ -0,0 +1,1187 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#if DOMAIN_NS == 1
+#include <string.h>
+#else
+#include "tfm_memory_utils.h"
+#endif
+#include "crypto_tests_common.h"
+
+void psa_key_interface_test(const psa_key_type_t key_type,
+                            struct test_result_t *ret)
+{
+    psa_status_t status = PSA_SUCCESS;
+    uint32_t i = 0;
+    psa_key_handle_t key_handle = 0x0u;
+    const uint8_t data[] = "THIS IS MY KEY1";
+    uint8_t exported_data[sizeof(data)] = {0};
+    size_t exported_data_size = 0;
+    psa_key_attributes_t key_attributes = psa_key_attributes_init();
+    psa_key_attributes_t retrieved_attributes = psa_key_attributes_init();
+
+    /* Setup the key policy */
+    psa_set_key_usage_flags(&key_attributes, PSA_KEY_USAGE_EXPORT);
+    psa_set_key_type(&key_attributes, key_type);
+
+    status = psa_import_key(&key_attributes, data, sizeof(data),
+                            &key_handle);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Error importing a key");
+        return;
+    }
+
+    status = psa_get_key_attributes(key_handle, &retrieved_attributes);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Error getting key metadata");
+        return;
+    }
+
+    if (psa_get_key_bits(&retrieved_attributes) != BIT_SIZE_TEST_KEY) {
+        TEST_FAIL("The number of key bits is different from expected");
+        return;
+    }
+
+    if (psa_get_key_type(&retrieved_attributes) != key_type) {
+        TEST_FAIL("The type of the key is different from expected");
+        return;
+    }
+
+    psa_reset_key_attributes(&retrieved_attributes);
+
+    status = psa_export_key(key_handle,
+                            exported_data,
+                            sizeof(data),
+                            &exported_data_size);
+
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Error exporting a key");
+        return;
+    }
+
+    if (exported_data_size != BYTE_SIZE_TEST_KEY) {
+        TEST_FAIL("Number of bytes of exported key different from expected");
+        return;
+    }
+
+    /* Check that the exported key is the same as the imported one */
+    for (i=0; i<exported_data_size; i++) {
+        if (exported_data[i] != data[i]) {
+            TEST_FAIL("Exported key doesn't match the imported key");
+            return;
+        }
+    }
+
+    status = psa_destroy_key(key_handle);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Error destroying the key");
+        return;
+    }
+
+    status = psa_get_key_attributes(key_handle, &retrieved_attributes);
+    if (status != PSA_ERROR_INVALID_HANDLE) {
+        TEST_FAIL("Key handle should be invalid now");
+        return;
+    }
+
+    psa_reset_key_attributes(&retrieved_attributes);
+
+    ret->val = TEST_PASSED;
+}
+
+void psa_cipher_test(const psa_key_type_t key_type,
+                     const psa_algorithm_t alg,
+                     struct test_result_t *ret)
+{
+    psa_cipher_operation_t handle = psa_cipher_operation_init();
+    psa_cipher_operation_t handle_dec = psa_cipher_operation_init();
+    psa_status_t status = PSA_SUCCESS;
+    psa_key_handle_t key_handle;
+    const uint8_t data[] = "THIS IS MY KEY1";
+    const size_t iv_length = PSA_BLOCK_CIPHER_BLOCK_SIZE(key_type);
+    const uint8_t iv[] = "012345678901234";
+    const uint8_t plain_text[BYTE_SIZE_CHUNK] = "Sixteen bytes!!";
+    uint8_t decrypted_data[ENC_DEC_BUFFER_SIZE] = {0};
+    size_t output_length = 0, total_output_length = 0;
+    uint8_t encrypted_data[ENC_DEC_BUFFER_SIZE] = {0};
+    uint32_t comp_result;
+    psa_key_attributes_t key_attributes = psa_key_attributes_init();
+    psa_key_usage_t usage = (PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT);
+    uint32_t i;
+
+    ret->val = TEST_PASSED;
+
+    /* FIXME: Special override for the CC312 accelerator. Implemented because
+     * there is not yet a generic way to override tests.
+     */
+#ifdef CRYPTO_HW_ACCELERATOR_CC312
+    if (alg == PSA_ALG_CFB) {
+        TEST_LOG("%s %s", "The CC312 does not support CFB mode.",
+                 "The test execution was SKIPPED.\r\n");
+        return;
+    }
+#endif /* CRYPTO_HW_ACCELERATOR_CC312 */
+
+    /* Setup the key policy */
+    psa_set_key_usage_flags(&key_attributes, usage);
+    psa_set_key_algorithm(&key_attributes, alg);
+    psa_set_key_type(&key_attributes, key_type);
+
+    /* Import a key */
+    status = psa_import_key(&key_attributes, data, sizeof(data), &key_handle);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Error importing a key");
+        goto destroy_key;
+    }
+
+    status = psa_get_key_attributes(key_handle, &key_attributes);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Error getting key metadata");
+        goto destroy_key;
+    }
+
+    if (psa_get_key_bits(&key_attributes) != BIT_SIZE_TEST_KEY) {
+        TEST_FAIL("The number of key bits is different from expected");
+        goto destroy_key;
+    }
+
+    if (psa_get_key_type(&key_attributes) != key_type) {
+        TEST_FAIL("The type of the key is different from expected");
+        goto destroy_key;
+    }
+
+    psa_reset_key_attributes(&key_attributes);
+
+    /* Setup the encryption object */
+    status = psa_cipher_encrypt_setup(&handle, key_handle, alg);
+    if (status != PSA_SUCCESS) {
+        if (status == PSA_ERROR_NOT_SUPPORTED) {
+            TEST_FAIL("Algorithm NOT SUPPORTED by the implementation");
+        } else {
+            TEST_FAIL("Error setting up cipher operation object");
+        }
+        goto destroy_key;
+    }
+
+    /* Set the IV */
+    status = psa_cipher_set_iv(&handle, iv, iv_length);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Error setting the IV on the cypher operation object");
+        status = psa_cipher_abort(&handle);
+        if (status != PSA_SUCCESS) {
+            TEST_FAIL("Error aborting the operation");
+        }
+        goto destroy_key;
+    }
+
+    /* Encrypt one chunk of information */
+    status = psa_cipher_update(&handle, plain_text, BYTE_SIZE_CHUNK,
+                               encrypted_data, ENC_DEC_BUFFER_SIZE,
+                               &output_length);
+
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Error encrypting one chunk of information");
+        status = psa_cipher_abort(&handle);
+        if (status != PSA_SUCCESS) {
+            TEST_FAIL("Error aborting the operation");
+        }
+        goto destroy_key;
+    }
+
+    if (output_length != BYTE_SIZE_CHUNK) {
+        TEST_FAIL("Expected encrypted data length is different from expected");
+        status = psa_cipher_abort(&handle);
+        if (status != PSA_SUCCESS) {
+            TEST_FAIL("Error aborting the operation");
+        }
+        goto destroy_key;
+    }
+
+    /* Finalise the cipher operation */
+    status = psa_cipher_finish(&handle, &encrypted_data[output_length],
+                               ENC_DEC_BUFFER_SIZE - output_length,
+                               &output_length);
+
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Error finalising the cipher operation");
+        status = psa_cipher_abort(&handle);
+        if (status != PSA_SUCCESS) {
+            TEST_FAIL("Error aborting the operation");
+        }
+        goto destroy_key;
+    }
+
+    if (output_length != 0) {
+        TEST_FAIL("Unexpected output length after finalisation");
+        goto destroy_key;
+    }
+
+    /* Setup the decryption object */
+    if (alg == PSA_ALG_CFB) {
+        /* In CFB mode the object is always in encryption mode */
+        status = psa_cipher_encrypt_setup(&handle_dec, key_handle, alg);
+    } else {
+        status = psa_cipher_decrypt_setup(&handle_dec, key_handle, alg);
+    }
+
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Error setting up cipher operation object");
+        goto destroy_key;
+    }
+
+    /* Set the IV for decryption */
+    status = psa_cipher_set_iv(&handle_dec, iv, iv_length);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Error setting the IV for decryption");
+        status = psa_cipher_abort(&handle_dec);
+        if (status != PSA_SUCCESS) {
+            TEST_FAIL("Error aborting the operation");
+        }
+        goto destroy_key;
+    }
+
+    /* Decrypt */
+    for (i = 0; i < ENC_DEC_BUFFER_SIZE; i += BYTE_SIZE_CHUNK) {
+        status = psa_cipher_update(&handle_dec,
+                                   (encrypted_data + i), BYTE_SIZE_CHUNK,
+                                   (decrypted_data + total_output_length),
+                                   (ENC_DEC_BUFFER_SIZE - total_output_length),
+                                   &output_length);
+
+        if (status != PSA_SUCCESS) {
+            TEST_FAIL("Error during decryption");
+            status = psa_cipher_abort(&handle_dec);
+            if (status != PSA_SUCCESS) {
+                TEST_FAIL("Error aborting the operation");
+            }
+            goto destroy_key;
+        }
+
+        total_output_length += output_length;
+    }
+
+#if DOMAIN_NS == 1U
+    /* Check that the plain text matches the decrypted data */
+    comp_result = memcmp(plain_text, decrypted_data, sizeof(plain_text));
+#else
+    comp_result = tfm_memcmp(plain_text, decrypted_data, sizeof(plain_text));
+#endif
+    if (comp_result != 0) {
+        TEST_FAIL("Decrypted data doesn't match with plain text");
+        status = psa_cipher_abort(&handle_dec);
+        if (status != PSA_SUCCESS) {
+            TEST_FAIL("Error aborting the operation");
+        }
+        goto destroy_key;
+    }
+
+    /* Finalise the cipher operation for decryption (destroys decrypted data) */
+    status = psa_cipher_finish(&handle_dec, decrypted_data, BYTE_SIZE_CHUNK,
+                               &output_length);
+
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Error finalising the cipher operation");
+        status = psa_cipher_abort(&handle_dec);
+        if (status != PSA_SUCCESS) {
+            TEST_FAIL("Error aborting the operation");
+        }
+        goto destroy_key;
+    }
+
+    total_output_length += output_length;
+
+    /* Check that the decrypted length is equal to the original length */
+    if (total_output_length != ENC_DEC_BUFFER_SIZE) {
+        TEST_FAIL("After finalising, unexpected decrypted length");
+        goto destroy_key;
+    }
+
+destroy_key:
+    /* Destroy the key */
+    status = psa_destroy_key(key_handle);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Error destroying a key");
+    }
+
+}
+
+void psa_invalid_cipher_test(const psa_key_type_t key_type,
+                             const psa_algorithm_t alg,
+                             const size_t key_size,
+                             struct test_result_t *ret)
+{
+    psa_status_t status;
+    psa_cipher_operation_t handle = psa_cipher_operation_init();
+    psa_key_handle_t key_handle;
+    uint8_t data[TEST_MAX_KEY_LENGTH];
+    psa_key_attributes_t key_attributes = psa_key_attributes_init();
+    psa_key_usage_t usage = (PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT);
+
+    /* Setup the key policy */
+    psa_set_key_usage_flags(&key_attributes, usage);
+    psa_set_key_algorithm(&key_attributes, alg);
+    psa_set_key_type(&key_attributes, key_type);
+
+#if DOMAIN_NS == 1U
+    /* Fill the key data */
+    (void)memset(data, 'A', key_size);
+#else
+    (void)tfm_memset(data, 'A', key_size);
+#endif
+
+    /* Import a key */
+    status = psa_import_key(&key_attributes, data, key_size, &key_handle);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Error importing a key");
+        return;
+    }
+
+    /* Setup the encryption object */
+    status = psa_cipher_encrypt_setup(&handle, key_handle, alg);
+    if (status == PSA_SUCCESS) {
+        TEST_FAIL("Should not successfully setup an invalid cipher");
+        (void)psa_destroy_key(key_handle);
+        return;
+    }
+
+    /* Destroy the key */
+    status = psa_destroy_key(key_handle);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Error destroying a key");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+void psa_unsupported_hash_test(const psa_algorithm_t alg,
+                               struct test_result_t *ret)
+{
+    psa_status_t status;
+    psa_hash_operation_t handle = PSA_HASH_OPERATION_INIT;
+
+    /* Setup the hash object for the unsupported hash algorithm */
+    status = psa_hash_setup(&handle, alg);
+    if (status != PSA_ERROR_NOT_SUPPORTED) {
+        TEST_FAIL("Should not successfully setup an unsupported hash alg");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/*
+ * \brief This is the list of algorithms supported by the current
+ *        configuration of the crypto engine used by the crypto
+ *        service. In case the crypto engine default capabilities
+ *        is changed, this list needs to be updated accordingly
+ */
+static const psa_algorithm_t hash_alg[] = {
+    PSA_ALG_SHA_224,
+    PSA_ALG_SHA_256,
+    PSA_ALG_SHA_384,
+    PSA_ALG_SHA_512,
+};
+
+static const uint8_t hash_val[][PSA_HASH_SIZE(PSA_ALG_SHA_512)] = {
+    {0x00, 0xD2, 0x90, 0xE2, 0x0E, 0x4E, 0xC1, 0x7E, /*!< SHA-224 */
+     0x7A, 0x95, 0xF5, 0x10, 0x5C, 0x76, 0x74, 0x04,
+     0x6E, 0xB5, 0x56, 0x5E, 0xE5, 0xE7, 0xBA, 0x15,
+     0x6C, 0x23, 0x47, 0xF3},
+    {0x6B, 0x22, 0x09, 0x2A, 0x37, 0x1E, 0xF5, 0x14, /*!< SHA-256 */
+     0xF7, 0x39, 0x4D, 0xCF, 0xAD, 0x4D, 0x17, 0x46,
+     0x66, 0xCB, 0x33, 0xA0, 0x39, 0xD8, 0x41, 0x4E,
+     0xF1, 0x2A, 0xD3, 0x4D, 0x69, 0xC3, 0xB5, 0x3E},
+    {0x64, 0x79, 0x11, 0xBB, 0x47, 0x4E, 0x47, 0x59, /*!< SHA-384 */
+     0x3E, 0x4D, 0xBC, 0x60, 0xA5, 0xF9, 0xBF, 0x9C,
+     0xC0, 0xBA, 0x55, 0x0F, 0x93, 0xCA, 0x72, 0xDF,
+     0x57, 0x1E, 0x50, 0x56, 0xF9, 0x4A, 0x01, 0xD6,
+     0xA5, 0x6F, 0xF7, 0x62, 0x34, 0x4F, 0x48, 0xFD,
+     0x9D, 0x15, 0x07, 0x42, 0xB7, 0x72, 0x94, 0xB8},
+    {0xB4, 0x1C, 0xA3, 0x6C, 0xA9, 0x67, 0x1D, 0xAD, /*!< SHA-512 */
+     0x34, 0x1F, 0xBE, 0x1B, 0x83, 0xC4, 0x40, 0x2A,
+     0x47, 0x42, 0x79, 0xBB, 0x21, 0xCA, 0xF0, 0x60,
+     0xE4, 0xD2, 0x6E, 0x9B, 0x70, 0x12, 0x34, 0x3F,
+     0x55, 0x2C, 0x09, 0x31, 0x0A, 0x5B, 0x40, 0x21,
+     0x01, 0xA8, 0x3B, 0x58, 0xE7, 0x48, 0x13, 0x1A,
+     0x7E, 0xCD, 0xE1, 0xD2, 0x46, 0x10, 0x58, 0x34,
+     0x49, 0x14, 0x4B, 0xAA, 0x89, 0xA9, 0xF5, 0xB1},
+};
+
+void psa_hash_test(const psa_algorithm_t alg,
+                   struct test_result_t *ret)
+{
+    const char *msg[] = {"This is my test message, ",
+                         "please generate a hash for this."};
+
+    const size_t msg_size[] = {25, 32}; /* Length in bytes of msg[0], msg[1] */
+    const uint32_t msg_num = sizeof(msg)/sizeof(msg[0]);
+    uint32_t idx;
+
+    psa_status_t status;
+    psa_hash_operation_t handle = psa_hash_operation_init();
+
+    /* Setup the hash object for the desired hash*/
+    status = psa_hash_setup(&handle, alg);
+
+    if (status != PSA_SUCCESS) {
+        if (status == PSA_ERROR_NOT_SUPPORTED) {
+            TEST_FAIL("Algorithm NOT SUPPORTED by the implementation");
+            return;
+        }
+
+        TEST_FAIL("Error setting up hash operation object");
+        return;
+    }
+
+    /* Update object with all the chunks of message */
+    for (idx=0; idx<msg_num; idx++) {
+        status = psa_hash_update(&handle,
+                                 (const uint8_t *)msg[idx],msg_size[idx]);
+        if (status != PSA_SUCCESS) {
+            TEST_FAIL("Error updating the hash operation object");
+            return;
+        }
+    }
+
+    /* Cycle until idx points to the correct index in the algorithm table */
+    for (idx=0; hash_alg[idx] != alg; idx++);
+
+    /* Finalise and verify that the hash is as expected */
+    status = psa_hash_verify(&handle, &(hash_val[idx][0]), PSA_HASH_SIZE(alg));
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Error verifying the hash operation object");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+void psa_unsupported_mac_test(const psa_key_type_t key_type,
+                              const psa_algorithm_t alg,
+                              struct test_result_t *ret)
+{
+    psa_status_t status;
+    psa_key_handle_t key_handle;
+    psa_mac_operation_t handle = PSA_MAC_OPERATION_INIT;
+    psa_key_attributes_t key_attributes = PSA_KEY_ATTRIBUTES_INIT;
+    const uint8_t data[] = "THIS IS MY KEY1";
+
+    ret->val = TEST_PASSED;
+
+    /* Setup the key policy */
+    psa_set_key_usage_flags(&key_attributes, PSA_KEY_USAGE_VERIFY);
+    psa_set_key_algorithm(&key_attributes, alg);
+    psa_set_key_type(&key_attributes, key_type);
+
+    /* Import key */
+    status = psa_import_key(&key_attributes, data, sizeof(data), &key_handle);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Error importing a key");
+        return;
+    }
+
+    /* Setup the mac object for the unsupported mac algorithm */
+    status = psa_mac_verify_setup(&handle, key_handle, alg);
+    if (status != PSA_ERROR_NOT_SUPPORTED) {
+        TEST_FAIL("Should not successfully setup an unsupported MAC alg");
+        /* Do not return, to ensure key is destroyed */
+    }
+
+    /* Destroy the key */
+    status = psa_destroy_key(key_handle);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Error destroying the key");
+    }
+}
+
+static const uint8_t hmac_val[][PSA_HASH_SIZE(PSA_ALG_SHA_512)] = {
+    {0xc1, 0x9f, 0x19, 0xac, 0x05, 0x65, 0x5f, 0x02, /*!< SHA-224 */
+     0x1b, 0x64, 0x32, 0xd9, 0xb1, 0x49, 0xba, 0x75,
+     0x05, 0x60, 0x52, 0x4e, 0x78, 0xfa, 0x61, 0xc9,
+     0x37, 0x5d, 0x7f, 0x58},
+    {0x94, 0x37, 0xbe, 0xb5, 0x7f, 0x7c, 0x5c, 0xb0, /*!< SHA-256 */
+     0x0a, 0x92, 0x4d, 0xd3, 0xba, 0x7e, 0xb1, 0x1a,
+     0xdb, 0xa2, 0x25, 0xb2, 0x82, 0x8e, 0xdf, 0xbb,
+     0x61, 0xbf, 0x91, 0x1d, 0x28, 0x23, 0x4a, 0x04},
+    {0x94, 0x21, 0x9b, 0xc3, 0xd5, 0xed, 0xe6, 0xee, /*!< SHA-384 */
+     0x42, 0x10, 0x5a, 0x58, 0xa4, 0x4d, 0x67, 0x87,
+     0x16, 0xa2, 0xa7, 0x6c, 0x2e, 0xc5, 0x85, 0xb7,
+     0x6a, 0x4c, 0x90, 0xb2, 0x73, 0xee, 0x58, 0x3c,
+     0x59, 0x16, 0x67, 0xf3, 0x6f, 0x30, 0x99, 0x1c,
+     0x2a, 0xf7, 0xb1, 0x5f, 0x45, 0x83, 0xf5, 0x9f},
+    {0x8f, 0x76, 0xef, 0x12, 0x0b, 0x92, 0xc2, 0x06, /*!< SHA-512 */
+     0xce, 0x01, 0x18, 0x75, 0x84, 0x96, 0xd9, 0x6f,
+     0x23, 0x88, 0xd4, 0xf8, 0xcf, 0x79, 0xf8, 0xcf,
+     0x27, 0x12, 0x9f, 0xa6, 0x7e, 0x87, 0x9a, 0x68,
+     0xee, 0xe2, 0xe7, 0x1d, 0x4b, 0xf2, 0x87, 0xc0,
+     0x05, 0x6a, 0xbd, 0x7f, 0x9d, 0xff, 0xaa, 0xf3,
+     0x9a, 0x1c, 0xb7, 0xb7, 0xbd, 0x03, 0x61, 0xa3,
+     0xa9, 0x6a, 0x5d, 0xb2, 0x81, 0xe1, 0x6f, 0x1f},
+};
+
+static const uint8_t long_key_hmac_val[PSA_HASH_SIZE(PSA_ALG_SHA_224)] = {
+    0x47, 0xa3, 0x42, 0xb1, 0x2f, 0x52, 0xd3, 0x8f, /*!< SHA-224 */
+    0x1e, 0x02, 0x4a, 0x46, 0x73, 0x0b, 0x77, 0xc1,
+    0x5e, 0x93, 0x31, 0xa9, 0x3e, 0xc2, 0x81, 0xb5,
+    0x3d, 0x07, 0x6f, 0x31
+};
+
+void psa_mac_test(const psa_algorithm_t alg,
+                  uint8_t use_long_key,
+                  struct test_result_t *ret)
+{
+    const char *msg[] = {"This is my test message, ",
+                         "please generate a hmac for this."};
+    const size_t msg_size[] = {25, 32}; /* Length in bytes of msg[0], msg[1] */
+    const uint32_t msg_num = sizeof(msg)/sizeof(msg[0]);
+    uint32_t idx;
+
+    psa_key_handle_t key_handle;
+    const uint8_t data[] = "THIS IS MY KEY1";
+    const uint8_t long_data[] = "THIS IS MY UNCOMMONLY LONG KEY1";
+    psa_key_type_t key_type = PSA_KEY_TYPE_HMAC;
+    size_t bit_size_test_key = 0;
+    psa_status_t status;
+    psa_mac_operation_t handle = psa_mac_operation_init();
+    psa_key_attributes_t key_attributes = psa_key_attributes_init();
+    psa_key_attributes_t retrieved_attributes = psa_key_attributes_init();
+    psa_key_usage_t usage = PSA_KEY_USAGE_VERIFY;
+
+    ret->val = TEST_PASSED;
+
+    /* Setup the key policy */
+    psa_set_key_usage_flags(&key_attributes, usage);
+    psa_set_key_algorithm(&key_attributes, alg);
+    psa_set_key_type(&key_attributes, key_type);
+
+    /* Import key */
+    if (use_long_key == 1) {
+        status = psa_import_key(&key_attributes,
+                                long_data,
+                                sizeof(long_data),
+                                &key_handle);
+    } else {
+        status = psa_import_key(&key_attributes,
+                                data,
+                                sizeof(data),
+                                &key_handle);
+    }
+
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Error importing a key");
+        return;
+    }
+
+    if (use_long_key == 1) {
+        bit_size_test_key = BIT_SIZE_TEST_LONG_KEY;
+    } else {
+        bit_size_test_key = BIT_SIZE_TEST_KEY;
+    }
+
+    status = psa_get_key_attributes(key_handle, &retrieved_attributes);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Error getting key metadata");
+        goto destroy_key_mac;
+    }
+
+    if (psa_get_key_bits(&retrieved_attributes) != bit_size_test_key) {
+        TEST_FAIL("The number of key bits is different from expected");
+        goto destroy_key_mac;
+    }
+
+    if (psa_get_key_type(&retrieved_attributes) != key_type) {
+        TEST_FAIL("The type of the key is different from expected");
+        goto destroy_key_mac;
+    }
+
+    psa_reset_key_attributes(&retrieved_attributes);
+
+    /* Setup the mac object for hmac */
+    status = psa_mac_verify_setup(&handle, key_handle, alg);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Error setting up mac operation object");
+        goto destroy_key_mac;
+    }
+
+    /* Update object with all the chunks of message */
+    for (idx=0; idx<msg_num; idx++) {
+        status = psa_mac_update(&handle,
+                                (const uint8_t *)msg[idx],
+                                msg_size[idx]);
+        if (status != PSA_SUCCESS) {
+            TEST_FAIL("Error during mac operation");
+            goto destroy_key_mac;
+        }
+    }
+
+    /* Cycle until idx points to the correct index in the algorithm table */
+    for (idx=0; hash_alg[idx] != PSA_ALG_HMAC_GET_HASH(alg); idx++);
+
+    /* Finalise and verify the mac value */
+    if (use_long_key == 1) {
+        status = psa_mac_verify_finish(
+                                     &handle,
+                                     &(long_key_hmac_val[0]),
+                                     PSA_HASH_SIZE(PSA_ALG_HMAC_GET_HASH(alg)));
+    } else {
+        status = psa_mac_verify_finish(
+                                     &handle,
+                                     &(hmac_val[idx][0]),
+                                     PSA_HASH_SIZE(PSA_ALG_HMAC_GET_HASH(alg)));
+    }
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Error during finalising the mac operation");
+        goto destroy_key_mac;
+    }
+
+destroy_key_mac:
+    /* Destroy the key */
+    status = psa_destroy_key(key_handle);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Error destroying the key");
+    }
+}
+
+void psa_aead_test(const psa_key_type_t key_type,
+                   const psa_algorithm_t alg,
+                   struct test_result_t *ret)
+{
+    psa_key_handle_t key_handle;
+    const size_t nonce_length = 12;
+    const uint8_t nonce[] = "01234567890";
+    const uint8_t plain_text[BYTE_SIZE_CHUNK] = "Sixteen bytes!!";
+    const uint8_t associated_data[ASSOCIATED_DATA_SIZE] =
+                                                      "This is associated data";
+    uint8_t encrypted_data[ENC_DEC_BUFFER_SIZE] = {0};
+    size_t encrypted_data_length = 0, decrypted_data_length = 0;
+    uint8_t decrypted_data[ENC_DEC_BUFFER_SIZE] = {0};
+    psa_status_t status;
+    const uint8_t data[] = "THIS IS MY KEY1";
+    uint32_t comp_result;
+    psa_key_attributes_t key_attributes = psa_key_attributes_init();
+    psa_key_attributes_t retrieved_attributes = psa_key_attributes_init();
+    psa_key_usage_t usage = (PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT);
+
+    ret->val = TEST_PASSED;
+
+    /* Setup the key policy */
+    psa_set_key_usage_flags(&key_attributes, usage);
+    psa_set_key_algorithm(&key_attributes, alg);
+    psa_set_key_type(&key_attributes, key_type);
+
+    /* Import a key */
+    status = psa_import_key(&key_attributes, data, sizeof(data), &key_handle);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Error importing a key");
+        return;
+    }
+
+    status = psa_get_key_attributes(key_handle, &retrieved_attributes);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Error getting key metadata");
+        goto destroy_key_aead;
+    }
+
+    if (psa_get_key_bits(&retrieved_attributes) != BIT_SIZE_TEST_KEY) {
+        TEST_FAIL("The number of key bits is different from expected");
+        goto destroy_key_aead;
+    }
+
+    if (psa_get_key_type(&retrieved_attributes) != key_type) {
+        TEST_FAIL("The type of the key is different from expected");
+        goto destroy_key_aead;
+    }
+
+    psa_reset_key_attributes(&retrieved_attributes);
+
+    /* Perform AEAD encryption */
+    status = psa_aead_encrypt(key_handle, alg, nonce, nonce_length,
+                              associated_data,
+                              sizeof(associated_data),
+                              plain_text,
+                              sizeof(plain_text),
+                              encrypted_data,
+                              sizeof(encrypted_data),
+                              &encrypted_data_length);
+
+    if (status != PSA_SUCCESS) {
+        if (status == PSA_ERROR_NOT_SUPPORTED) {
+            TEST_FAIL("Algorithm NOT SUPPORTED by the implementation");
+            goto destroy_key_aead;
+        }
+
+        TEST_FAIL("Error performing AEAD encryption");
+        goto destroy_key_aead;
+    }
+
+    if (encrypted_data_length
+        != PSA_AEAD_ENCRYPT_OUTPUT_SIZE(alg, sizeof(plain_text))) {
+        TEST_FAIL("Encrypted data length is different than expected");
+        goto destroy_key_aead;
+    }
+
+    /* Perform AEAD decryption */
+    status = psa_aead_decrypt(key_handle, alg, nonce, nonce_length,
+                              associated_data,
+                              sizeof(associated_data),
+                              encrypted_data,
+                              encrypted_data_length,
+                              decrypted_data,
+                              sizeof(decrypted_data),
+                              &decrypted_data_length);
+
+    if (status != PSA_SUCCESS) {
+        if (status == PSA_ERROR_NOT_SUPPORTED) {
+            TEST_FAIL("Algorithm NOT SUPPORTED by the implementation");
+        } else {
+            TEST_FAIL("Error performing AEAD decryption");
+        }
+
+        goto destroy_key_aead;
+    }
+
+    if (sizeof(plain_text) != decrypted_data_length) {
+        TEST_FAIL("Decrypted data length is different from plain text");
+        goto destroy_key_aead;
+    }
+
+#if DOMAIN_NS == 1U
+    /* Check that the decrypted data is the same as the original data */
+    comp_result = memcmp(plain_text, decrypted_data, sizeof(plain_text));
+#else
+    comp_result = tfm_memcmp(plain_text, decrypted_data, sizeof(plain_text));
+#endif
+    if (comp_result != 0) {
+        TEST_FAIL("Decrypted data doesn't match with plain text");
+        goto destroy_key_aead;
+    }
+
+destroy_key_aead:
+    /* Destroy the key */
+    status = psa_destroy_key(key_handle);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Error destroying a key");
+    }
+}
+
+/*
+ * The list of available AES cipher/AEAD mode for test.
+ * Not all the modes can be available in some use cases and configurations.
+ */
+static const psa_algorithm_t test_aes_mode_array[] = {
+#ifdef TFM_CRYPTO_TEST_ALG_CBC
+    PSA_ALG_CBC_NO_PADDING,
+#endif
+#ifdef TFM_CRYPTO_TEST_ALG_CCM
+    PSA_ALG_CCM,
+#endif
+#ifdef TFM_CRYPTO_TEST_ALG_CFB
+    PSA_ALG_CFB,
+#endif
+#ifdef TFM_CRYPTO_TEST_ALG_CTR
+    PSA_ALG_CTR,
+#endif
+#ifdef TFM_CRYPTO_TEST_ALG_GCM
+    PSA_ALG_GCM,
+#endif
+    /* In case no AES algorithm is available */
+    PSA_ALG_VENDOR_FLAG,
+};
+
+/* Number of available AES cipher modes */
+#define NR_TEST_AES_MODE        (sizeof(test_aes_mode_array) / \
+                                 sizeof(test_aes_mode_array[0]) - 1)
+
+void psa_invalid_key_length_test(struct test_result_t *ret)
+{
+    psa_status_t status;
+    psa_key_attributes_t key_attributes = psa_key_attributes_init();
+    psa_key_handle_t key_handle;
+    const uint8_t data[19] = {0};
+
+    if (NR_TEST_AES_MODE < 1) {
+        TEST_FAIL("A cipher mode in AES is required in current test case");
+        return;
+    }
+
+    /* Setup the key policy */
+    psa_set_key_usage_flags(&key_attributes, PSA_KEY_USAGE_ENCRYPT);
+    psa_set_key_algorithm(&key_attributes, test_aes_mode_array[0]);
+    psa_set_key_type(&key_attributes, PSA_KEY_TYPE_AES);
+
+    /* AES does not support 152-bit keys */
+    status = psa_import_key(&key_attributes, data, sizeof(data), &key_handle);
+    if (status != PSA_ERROR_INVALID_ARGUMENT) {
+        TEST_FAIL("Should not successfully import with an invalid key length");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+void psa_policy_key_interface_test(struct test_result_t *ret)
+{
+    psa_algorithm_t alg = test_aes_mode_array[0];
+    psa_algorithm_t alg_out;
+    psa_key_lifetime_t lifetime = PSA_KEY_LIFETIME_VOLATILE;
+    psa_key_lifetime_t lifetime_out;
+    psa_key_attributes_t key_attributes = psa_key_attributes_init();
+    psa_key_usage_t usage = PSA_KEY_USAGE_EXPORT;
+    psa_key_usage_t usage_out;
+
+    if (NR_TEST_AES_MODE < 1) {
+        TEST_FAIL("A cipher mode in AES is required in current test case");
+        return;
+    }
+
+    /* Verify that initialised policy forbids all usage */
+    usage_out = psa_get_key_usage_flags(&key_attributes);
+    if (usage_out != 0) {
+        TEST_FAIL("Unexpected usage value");
+        return;
+    }
+
+    alg_out = psa_get_key_algorithm(&key_attributes);
+    if (alg_out != 0) {
+        TEST_FAIL("Unexpected algorithm value");
+        return;
+    }
+
+    /* Set the key policy values */
+    psa_set_key_usage_flags(&key_attributes, usage);
+    psa_set_key_algorithm(&key_attributes, alg);
+
+    /* Check that the key policy has the correct usage */
+    usage_out = psa_get_key_usage_flags(&key_attributes);
+    if (usage_out != usage) {
+        TEST_FAIL("Unexpected usage value");
+        return;
+    }
+
+    /* Check that the key policy has the correct algorithm */
+    alg_out = psa_get_key_algorithm(&key_attributes);
+    if (alg_out != alg) {
+        TEST_FAIL("Unexpected algorithm value");
+        return;
+    }
+
+    /* Check the key handle has the correct key lifetime */
+    lifetime_out = psa_get_key_lifetime(&key_attributes);
+
+    if (lifetime_out != lifetime) {
+        TEST_FAIL("Unexpected key lifetime value");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+void psa_policy_invalid_policy_usage_test(struct test_result_t *ret)
+{
+    psa_status_t status;
+    psa_algorithm_t alg, not_permit_alg;
+    psa_cipher_operation_t handle = psa_cipher_operation_init();
+    psa_key_attributes_t key_attributes = psa_key_attributes_init();
+    psa_key_handle_t key_handle;
+    psa_key_usage_t usage = (PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT);
+    size_t data_len;
+    const uint8_t data[] = "THIS IS MY KEY1";
+    uint8_t data_out[sizeof(data)];
+    uint8_t i, j;
+
+    ret->val = TEST_PASSED;
+
+    if (NR_TEST_AES_MODE < 2) {
+        TEST_LOG("Two cipher modes are required. Skip this test case\r\n");
+        return;
+    }
+
+    /*
+     * Search for two modes for test. Both modes should be Cipher algorithms.
+     * Otherwise, cipher setup may fail before policy permission check.
+     */
+    for (i = 0; i < NR_TEST_AES_MODE - 1; i++) {
+        if (PSA_ALG_IS_CIPHER(test_aes_mode_array[i])) {
+            alg = test_aes_mode_array[i];
+            break;
+        }
+    }
+
+    for (j = i + 1; j < NR_TEST_AES_MODE; j++) {
+        if (PSA_ALG_IS_CIPHER(test_aes_mode_array[j])) {
+            not_permit_alg = test_aes_mode_array[j];
+            break;
+        }
+    }
+
+    if (j == NR_TEST_AES_MODE) {
+        TEST_LOG("Unable to find two Cipher algs. Skip this test case.\r\n");
+        return;
+    }
+
+    /* Setup the key policy */
+    psa_set_key_usage_flags(&key_attributes, usage);
+    psa_set_key_algorithm(&key_attributes, alg);
+    psa_set_key_type(&key_attributes, PSA_KEY_TYPE_AES);
+
+    /* Import a key to the key handle for which policy has been set */
+    status = psa_import_key(&key_attributes, data, sizeof(data), &key_handle);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Failed to import a key");
+        return;
+    }
+
+    /* Setup a cipher permitted by the key policy */
+    status = psa_cipher_encrypt_setup(&handle, key_handle, alg);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Failed to setup cipher operation");
+        goto destroy_key;
+    }
+
+    status = psa_cipher_abort(&handle);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Failed to abort cipher operation");
+        goto destroy_key;
+    }
+
+    /* Attempt to setup a cipher with an alg not permitted by the policy */
+    status = psa_cipher_encrypt_setup(&handle, key_handle, not_permit_alg);
+    if (status != PSA_ERROR_NOT_PERMITTED) {
+        TEST_FAIL("Was able to setup cipher operation with wrong alg");
+        goto destroy_key;
+    }
+
+    /* Attempt to export the key, which is forbidden by the key policy */
+    status = psa_export_key(key_handle, data_out, sizeof(data_out), &data_len);
+    if (status != PSA_ERROR_NOT_PERMITTED) {
+        TEST_FAIL("Should not be able to export key without correct usage");
+        goto destroy_key;
+    }
+
+destroy_key:
+    status = psa_destroy_key(key_handle);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Failed to destroy key");
+    }
+}
+
+void psa_persistent_key_test(psa_key_id_t key_id, struct test_result_t *ret)
+{
+    psa_status_t status;
+    int comp_result;
+    psa_key_handle_t key_handle;
+    psa_algorithm_t alg = test_aes_mode_array[0];
+    psa_key_usage_t usage = PSA_KEY_USAGE_EXPORT;
+    psa_key_attributes_t key_attributes = PSA_KEY_ATTRIBUTES_INIT;
+    size_t data_len;
+    const uint8_t data[] = "THIS IS MY KEY1";
+    uint8_t data_out[sizeof(data)] = {0};
+
+    if (NR_TEST_AES_MODE < 1) {
+        TEST_FAIL("A cipher mode in AES is required in current test case");
+        return;
+    }
+
+    /* Setup the key attributes with a key ID to create a persistent key */
+    psa_set_key_id(&key_attributes, key_id);
+    psa_set_key_usage_flags(&key_attributes, usage);
+    psa_set_key_algorithm(&key_attributes, alg);
+    psa_set_key_type(&key_attributes, PSA_KEY_TYPE_AES);
+
+    /* Import key data to create the persistent key */
+    status = psa_import_key(&key_attributes, data, sizeof(data), &key_handle);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Failed to import a key");
+        return;
+    }
+
+    /* Close the persistent key handle */
+    status = psa_close_key(key_handle);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Failed to close a persistent key handle");
+        return;
+    }
+
+    /* Open the previsously-created persistent key */
+    status = psa_open_key(key_id, &key_handle);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Failed to open a persistent key");
+        return;
+    }
+
+    /* Export the persistent key */
+    status = psa_export_key(key_handle, data_out, sizeof(data_out), &data_len);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Failed to export a persistent key");
+        return;
+    }
+
+    if (data_len != sizeof(data)) {
+        TEST_FAIL("Number of bytes of exported key different from expected");
+        return;
+    }
+
+    /* Check that the exported key is the same as the imported one */
+#if DOMAIN_NS == 1U
+    comp_result = memcmp(data_out, data, sizeof(data));
+#else
+    comp_result = tfm_memcmp(data_out, data, sizeof(data));
+#endif
+    if (comp_result != 0) {
+        TEST_FAIL("Exported key does not match the imported key");
+        return;
+    }
+
+    /* Destroy the persistent key */
+    status = psa_destroy_key(key_handle);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Failed to destroy a persistent key");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+#define KEY_DERIVE_OUTPUT_LEN          32
+#define KEY_DERIV_SECRET_LEN           16
+#define KEY_DERIV_LABEL_INFO_LEN       8
+#define KEY_DERIV_SEED_SALT_LEN        8
+
+static uint8_t key_deriv_secret[KEY_DERIV_SECRET_LEN];
+static uint8_t key_deriv_label_info[KEY_DERIV_LABEL_INFO_LEN];
+static uint8_t key_deriv_seed_salt[KEY_DERIV_SEED_SALT_LEN];
+
+void psa_key_derivation_test(psa_algorithm_t deriv_alg,
+                             struct test_result_t *ret)
+{
+    psa_key_handle_t input_handle = 0, output_handle = 0;
+    psa_key_attributes_t input_key_attr = PSA_KEY_ATTRIBUTES_INIT;
+    psa_key_attributes_t output_key_attr = PSA_KEY_ATTRIBUTES_INIT;
+    psa_key_derivation_operation_t deriv_ops;
+    psa_status_t status;
+    uint8_t counter = 0xA5;
+
+    /* Prepare the parameters */
+#if DOMAIN_NS == 1U
+    memset(key_deriv_secret, counter, KEY_DERIV_SECRET_LEN);
+    memset(key_deriv_label_info, counter++, KEY_DERIV_LABEL_INFO_LEN);
+    memset(key_deriv_seed_salt, counter++, KEY_DERIV_SEED_SALT_LEN);
+#else
+    tfm_memset(key_deriv_secret, counter, KEY_DERIV_SECRET_LEN);
+    tfm_memset(key_deriv_label_info, counter++, KEY_DERIV_LABEL_INFO_LEN);
+    tfm_memset(key_deriv_seed_salt, counter++, KEY_DERIV_SEED_SALT_LEN);
+#endif
+
+    deriv_ops = psa_key_derivation_operation_init();
+
+    psa_set_key_usage_flags(&input_key_attr, PSA_KEY_USAGE_DERIVE);
+    psa_set_key_algorithm(&input_key_attr, deriv_alg);
+    psa_set_key_type(&input_key_attr, PSA_KEY_TYPE_DERIVE);
+
+    /* Force to use HMAC-SHA256 as HMAC operation so far */
+    status = psa_import_key(&input_key_attr, key_deriv_secret,
+                            KEY_DERIV_SECRET_LEN, &input_handle);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Failed to import secret");
+        return;
+    }
+
+    status = psa_key_derivation_setup(&deriv_ops, deriv_alg);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Failed to setup derivation operation");
+        goto destroy_key;
+    }
+
+    if (PSA_ALG_IS_TLS12_PRF(deriv_alg) ||
+        PSA_ALG_IS_TLS12_PSK_TO_MS(deriv_alg)) {
+        status = psa_key_derivation_input_bytes(&deriv_ops,
+                                                PSA_KEY_DERIVATION_INPUT_SEED,
+                                                key_deriv_seed_salt,
+                                                KEY_DERIV_SEED_SALT_LEN);
+        if (status != PSA_SUCCESS) {
+            TEST_FAIL("Failed to input seed");
+            goto deriv_abort;
+        }
+
+        status = psa_key_derivation_input_key(&deriv_ops,
+                                              PSA_KEY_DERIVATION_INPUT_SECRET,
+                                              input_handle);
+        if (status != PSA_SUCCESS) {
+            TEST_FAIL("Failed to input key");
+            goto deriv_abort;
+        }
+
+        status = psa_key_derivation_input_bytes(&deriv_ops,
+                                                PSA_KEY_DERIVATION_INPUT_LABEL,
+                                                key_deriv_label_info,
+                                                KEY_DERIV_LABEL_INFO_LEN);
+        if (status != PSA_SUCCESS) {
+            TEST_FAIL("Failed to input label");
+            goto deriv_abort;
+        }
+    } else if (PSA_ALG_IS_HKDF(deriv_alg)) {
+        status = psa_key_derivation_input_bytes(&deriv_ops,
+                                                PSA_KEY_DERIVATION_INPUT_SALT,
+                                                key_deriv_seed_salt,
+                                                KEY_DERIV_SEED_SALT_LEN);
+        if (status != PSA_SUCCESS) {
+            TEST_FAIL("Failed to input salt");
+            goto deriv_abort;
+        }
+
+        status = psa_key_derivation_input_key(&deriv_ops,
+                                              PSA_KEY_DERIVATION_INPUT_SECRET,
+                                              input_handle);
+        if (status != PSA_SUCCESS) {
+            TEST_FAIL("Failed to input key");
+            goto deriv_abort;
+        }
+
+        status = psa_key_derivation_input_bytes(&deriv_ops,
+                                                PSA_KEY_DERIVATION_INPUT_INFO,
+                                                key_deriv_label_info,
+                                                KEY_DERIV_LABEL_INFO_LEN);
+        if (status != PSA_SUCCESS) {
+            TEST_FAIL("Failed to input info");
+            goto deriv_abort;
+        }
+    } else {
+        TEST_FAIL("Unsupported derivation algorithm");
+        goto deriv_abort;
+    }
+
+    if (NR_TEST_AES_MODE < 1) {
+        TEST_LOG("No AES algorithm to verify. Output raw data instead");
+        psa_set_key_type(&output_key_attr, PSA_KEY_TYPE_RAW_DATA);
+    } else {
+        psa_set_key_usage_flags(&output_key_attr, PSA_KEY_USAGE_ENCRYPT);
+        psa_set_key_algorithm(&output_key_attr, test_aes_mode_array[0]);
+        psa_set_key_type(&output_key_attr, PSA_KEY_TYPE_AES);
+    }
+    psa_set_key_bits(&output_key_attr,
+                     PSA_BYTES_TO_BITS(KEY_DERIVE_OUTPUT_LEN));
+
+    status = psa_key_derivation_output_key(&output_key_attr, &deriv_ops,
+                                           &output_handle);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Failed to output key");
+        goto deriv_abort;
+    }
+
+    ret->val = TEST_PASSED;
+
+deriv_abort:
+    psa_key_derivation_abort(&deriv_ops);
+destroy_key:
+    psa_destroy_key(input_handle);
+    if (output_handle) {
+        psa_destroy_key(output_handle);
+    }
+
+    return;
+}
diff --git a/test/suites/crypto/crypto_tests_common.h b/test/suites/crypto/crypto_tests_common.h
new file mode 100644
index 0000000..67e6a33
--- /dev/null
+++ b/test/suites/crypto/crypto_tests_common.h
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __CRYPTO_TESTS_COMMON_H__
+#define __CRYPTO_TESTS_COMMON_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "psa/crypto.h"
+#include "test/framework/test_framework_helpers.h"
+
+/**
+ * \brief Size of the key to use in tests in bits
+ *
+ */
+#define BIT_SIZE_TEST_KEY (128)
+
+/**
+ * \brief Size of the long key to use in tests in bits
+ *
+ */
+#define BIT_SIZE_TEST_LONG_KEY (256)
+
+/**
+ * \brief Macro to extract the size of the key in bytes
+ *
+ */
+#define BYTE_SIZE_TEST_KEY (BIT_SIZE_TEST_KEY/8)
+
+/**
+ * \brief Size in bytes of a chunk of data to process
+ *
+ */
+#define BYTE_SIZE_CHUNK (16)
+
+/**
+ * \brief Size in bytes of the encryption/decryption buffers
+ *
+ */
+#define ENC_DEC_BUFFER_SIZE (32)
+
+/**
+ * \brief Size in bytes of the associated data to authenticate
+ *        in AEAD tests
+ *
+ */
+#define ASSOCIATED_DATA_SIZE (24)
+
+/**
+ * \brief The maximum allowed key length in bytes used in the
+ *        tests
+ */
+#define TEST_MAX_KEY_LENGTH (64)
+
+/**
+ * \brief The length of truncated authentication tag for AEAD algorithm
+ */
+#define TRUNCATED_AUTH_TAG_LEN (8)
+
+/**
+ * \brief Tests the key interfaces with different key types
+ *
+ * \param[in]  key_type PSA key type
+ * \param[out] ret      Test result
+ *
+ */
+void psa_key_interface_test(const psa_key_type_t key_type,
+                            struct test_result_t *ret);
+/**
+ * \brief Run block ciphering tests with different algorithms and key types
+ *
+ * \param[in]  key_type PSA key type
+ * \param[in]  alg      PSA algorithm
+ * \param[out] ret      Test result
+ *
+ */
+void psa_cipher_test(const psa_key_type_t key_type,
+                     const psa_algorithm_t alg,
+                     struct test_result_t *ret);
+/**
+ * \brief Tests invalid key type and algorithm combinations for block ciphers
+ *
+ * \param[in]  key_type  PSA key type
+ * \param[in]  alg       PSA algorithm
+ * \param[in]  key_size  Key size
+ * \param[out] ret       Test result
+ *
+ */
+void psa_invalid_cipher_test(const psa_key_type_t key_type,
+                             const psa_algorithm_t alg,
+                             const size_t key_size,
+                             struct test_result_t *ret);
+/**
+ * \brief Tests setup of an unsupported hash algorithm
+ *
+ * \param[in]  alg PSA algorithm
+ * \param[out] ret Test result
+ *
+ */
+void psa_unsupported_hash_test(const psa_algorithm_t alg,
+                               struct test_result_t *ret);
+/**
+ * \brief Tests different hashing algorithms
+ *
+ * \param[in]  alg PSA algorithm
+ * \param[out] ret Test result
+ *
+ */
+void psa_hash_test(const psa_algorithm_t alg,
+                   struct test_result_t *ret);
+/**
+ * \brief Tests setup of an unsupported MAC algorithm
+ *
+ * \param[in]  key_type PSA key type
+ * \param[in]  alg      PSA algorithm
+ * \param[out] ret      Test result
+ *
+ */
+void psa_unsupported_mac_test(const psa_key_type_t key_type,
+                              const psa_algorithm_t alg,
+                              struct test_result_t *ret);
+/**
+ * \brief Tests different MAC algorithms
+ *
+ * \param[in]  alg          PSA algorithm
+ * \param[in]  use_long_key Flag used to indicate to use the long test key
+ * \param[out] ret          Test result
+ *
+ */
+void psa_mac_test(const psa_algorithm_t alg,
+                  uint8_t use_long_key,
+                  struct test_result_t *ret);
+/**
+ * \brief Run AEAD tests with different algorithms and key types
+ *
+ * \param[in]  key_type PSA key type
+ * \param[in]  alg      PSA algorithm
+ * \param[out] ret      Test result
+ *
+ */
+void psa_aead_test(const psa_key_type_t key_type,
+                   const psa_algorithm_t alg,
+                   struct test_result_t *ret);
+/**
+ * \brief Tests invalid key length
+ *
+ * \param[out] ret Test result
+ *
+ */
+void psa_invalid_key_length_test(struct test_result_t *ret);
+
+/**
+ * \brief Tests the policy key interface
+ *
+ * \param[out] ret Test result
+ *
+ */
+void psa_policy_key_interface_test(struct test_result_t *ret);
+
+/**
+ * \brief Tests invalid policy usage
+ *
+ * \param[out] ret Test result
+ *
+ */
+void psa_policy_invalid_policy_usage_test(struct test_result_t *ret);
+
+/**
+ * \brief Tests persistent keys
+ *
+ * \param[out] ret Test result
+ *
+ */
+void psa_persistent_key_test(psa_key_id_t key_id, struct test_result_t *ret);
+
+/**
+ * \brief Key derivation test
+ *
+ * \param[in] deriv_alg Key derivation algorithm
+ * \param[out] ret      Test result
+ */
+void psa_key_derivation_test(psa_algorithm_t deriv_alg,
+                             struct test_result_t *ret);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CRYPTO_TESTS_COMMON__ */
diff --git a/test/suites/crypto/non_secure/crypto_ns_interface_testsuite.c b/test/suites/crypto/non_secure/crypto_ns_interface_testsuite.c
new file mode 100644
index 0000000..22a0c9d
--- /dev/null
+++ b/test/suites/crypto/non_secure/crypto_ns_interface_testsuite.c
@@ -0,0 +1,307 @@
+/*
+ * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "test/framework/test_framework_helpers.h"
+#include "tfm_api.h"
+#include "../crypto_tests_common.h"
+
+/* List of tests */
+static void tfm_crypto_test_6001(struct test_result_t *ret);
+#ifdef TFM_CRYPTO_TEST_ALG_CBC
+static void tfm_crypto_test_6002(struct test_result_t *ret);
+#endif /* TFM_CRYPTO_TEST_ALG_CBC */
+#ifdef TFM_CRYPTO_TEST_ALG_CFB
+static void tfm_crypto_test_6003(struct test_result_t *ret);
+#endif /* TFM_CRYPTO_TEST_ALG_CFB */
+#ifdef TFM_CRYPTO_TEST_ALG_CTR
+static void tfm_crypto_test_6005(struct test_result_t *ret);
+#endif /* TFM_CRYPTO_TEST_ALG_CTR */
+static void tfm_crypto_test_6007(struct test_result_t *ret);
+static void tfm_crypto_test_6008(struct test_result_t *ret);
+#ifdef TFM_CRYPTO_TEST_ALG_CFB
+static void tfm_crypto_test_6009(struct test_result_t *ret);
+#endif /* TFM_CRYPTO_TEST_ALG_CFB */
+static void tfm_crypto_test_6010(struct test_result_t *ret);
+static void tfm_crypto_test_6011(struct test_result_t *ret);
+static void tfm_crypto_test_6012(struct test_result_t *ret);
+#ifdef TFM_CRYPTO_TEST_ALG_SHA_512
+static void tfm_crypto_test_6013(struct test_result_t *ret);
+static void tfm_crypto_test_6014(struct test_result_t *ret);
+#endif /* TFM_CRYPTO_TEST_ALG_SHA_512 */
+static void tfm_crypto_test_6019(struct test_result_t *ret);
+static void tfm_crypto_test_6020(struct test_result_t *ret);
+#ifdef TFM_CRYPTO_TEST_ALG_SHA_512
+static void tfm_crypto_test_6021(struct test_result_t *ret);
+static void tfm_crypto_test_6022(struct test_result_t *ret);
+#endif /* TFM_CRYPTO_TEST_ALG_SHA_512 */
+static void tfm_crypto_test_6024(struct test_result_t *ret);
+#ifdef TFM_CRYPTO_TEST_ALG_CCM
+static void tfm_crypto_test_6030(struct test_result_t *ret);
+#endif /* TFM_CRYPTO_TEST_ALG_CCM */
+#ifdef TFM_CRYPTO_TEST_ALG_GCM
+static void tfm_crypto_test_6031(struct test_result_t *ret);
+#endif /* TFM_CRYPTO_TEST_ALG_GCM */
+static void tfm_crypto_test_6032(struct test_result_t *ret);
+static void tfm_crypto_test_6033(struct test_result_t *ret);
+static void tfm_crypto_test_6034(struct test_result_t *ret);
+#ifdef TFM_CRYPTO_TEST_ALG_CCM
+static void tfm_crypto_test_6035(struct test_result_t *ret);
+#endif /* TFM_CRYPTO_TEST_ALG_CCM */
+static void tfm_crypto_test_6036(struct test_result_t *ret);
+static void tfm_crypto_test_6037(struct test_result_t *ret);
+#ifdef TFM_CRYPTO_TEST_HKDF
+static void tfm_crypto_test_6038(struct test_result_t *ret);
+#endif /* TFM_CRYPTO_TEST_HKDF */
+
+static struct test_t crypto_tests[] = {
+    {&tfm_crypto_test_6001, "TFM_CRYPTO_TEST_6001",
+     "Non Secure Key management interface", {TEST_PASSED} },
+#ifdef TFM_CRYPTO_TEST_ALG_CBC
+    {&tfm_crypto_test_6002, "TFM_CRYPTO_TEST_6002",
+     "Non Secure Symmetric encryption (AES-128-CBC) interface", {TEST_PASSED} },
+#endif /* TFM_CRYPTO_TEST_ALG_CBC */
+#ifdef TFM_CRYPTO_TEST_ALG_CFB
+    {&tfm_crypto_test_6003, "TFM_CRYPTO_TEST_6003",
+     "Non Secure Symmetric encryption (AES-128-CFB) interface", {TEST_PASSED} },
+#endif /* TFM_CRYPTO_TEST_ALG_CFB */
+#ifdef TFM_CRYPTO_TEST_ALG_CTR
+    {&tfm_crypto_test_6005, "TFM_CRYPTO_TEST_6005",
+     "Non Secure Symmetric encryption (AES-128-CTR) interface", {TEST_PASSED} },
+#endif /* TFM_CRYPTO_TEST_ALG_CTR */
+    {&tfm_crypto_test_6007, "TFM_CRYPTO_TEST_6007",
+     "Non Secure Symmetric encryption invalid cipher", {TEST_PASSED} },
+    {&tfm_crypto_test_6008, "TFM_CRYPTO_TEST_6008",
+     "Non Secure Symmetric encryption invalid cipher (AES-152)", {TEST_PASSED} },
+#ifdef TFM_CRYPTO_TEST_ALG_CFB
+    {&tfm_crypto_test_6009, "TFM_CRYPTO_TEST_6009",
+     "Non Secure Symmetric encryption invalid cipher (HMAC-128-CFB)", {TEST_PASSED} },
+#endif /* TFM_CRYPTO_TEST_ALG_CFB */
+    {&tfm_crypto_test_6010, "TFM_CRYPTO_TEST_6010",
+     "Non Secure Unsupported Hash (SHA-1) interface", {TEST_PASSED} },
+    {&tfm_crypto_test_6011, "TFM_CRYPTO_TEST_6011",
+     "Non Secure Hash (SHA-224) interface", {TEST_PASSED} },
+    {&tfm_crypto_test_6012, "TFM_CRYPTO_TEST_6012",
+     "Non Secure Hash (SHA-256) interface", {TEST_PASSED} },
+#ifdef TFM_CRYPTO_TEST_ALG_SHA_512
+    {&tfm_crypto_test_6013, "TFM_CRYPTO_TEST_6013",
+     "Non Secure Hash (SHA-384) interface", {TEST_PASSED} },
+    {&tfm_crypto_test_6014, "TFM_CRYPTO_TEST_6014",
+     "Non Secure Hash (SHA-512) interface", {TEST_PASSED} },
+#endif /* TFM_CRYPTO_TEST_ALG_SHA_512 */
+    {&tfm_crypto_test_6019, "TFM_CRYPTO_TEST_6019",
+     "Non Secure Unsupported HMAC (SHA-1) interface", {TEST_PASSED} },
+    {&tfm_crypto_test_6020, "TFM_CRYPTO_TEST_6020",
+     "Non Secure HMAC (SHA-256) interface", {TEST_PASSED} },
+#ifdef TFM_CRYPTO_TEST_ALG_SHA_512
+    {&tfm_crypto_test_6021, "TFM_CRYPTO_TEST_6021",
+     "Non Secure HMAC (SHA-384) interface", {TEST_PASSED} },
+    {&tfm_crypto_test_6022, "TFM_CRYPTO_TEST_6022",
+     "Non Secure HMAC (SHA-512) interface", {TEST_PASSED} },
+#endif /* TFM_CRYPTO_TEST_ALG_SHA_512 */
+    {&tfm_crypto_test_6024, "TFM_CRYPTO_TEST_6024",
+     "Non Secure HMAC with long key (SHA-224) interface", {TEST_PASSED} },
+#ifdef TFM_CRYPTO_TEST_ALG_CCM
+    {&tfm_crypto_test_6030, "TFM_CRYPTO_TEST_6030",
+     "Non Secure AEAD (AES-128-CCM) interface", {TEST_PASSED} },
+#endif /* TFM_CRYPTO_TEST_ALG_CCM */
+#ifdef TFM_CRYPTO_TEST_ALG_GCM
+    {&tfm_crypto_test_6031, "TFM_CRYPTO_TEST_6031",
+     "Non Secure AEAD (AES-128-GCM) interface", {TEST_PASSED} },
+#endif /* TFM_CRYPTO_TEST_ALG_GCM */
+    {&tfm_crypto_test_6032, "TFM_CRYPTO_TEST_6032",
+     "Non Secure key policy interface", {TEST_PASSED} },
+    {&tfm_crypto_test_6033, "TFM_CRYPTO_TEST_6033",
+     "Non Secure key policy check permissions", {TEST_PASSED} },
+    {&tfm_crypto_test_6034, "TFM_CRYPTO_TEST_6034",
+     "Non Secure persistent key interface", {TEST_PASSED} },
+#ifdef TFM_CRYPTO_TEST_ALG_CCM
+    {&tfm_crypto_test_6035, "TFM_CRYPTO_TEST_6035",
+     "Non Secure AEAD interface with truncated auth tag (AES-128-CCM-8)",
+     {TEST_PASSED} },
+#endif /* TFM_CRYPTO_TEST_ALG_CCM */
+    {&tfm_crypto_test_6036, "TFM_CRYPTO_TEST_6036",
+     "Non Secure TLS 1.2 PRF key derivation", {TEST_PASSED} },
+    {&tfm_crypto_test_6037, "TFM_CRYPTO_TEST_6037",
+     "Non Secure TLS-1.2 PSK-to-MasterSecret key derivation", {TEST_PASSED} },
+#ifdef TFM_CRYPTO_TEST_HKDF
+    {&tfm_crypto_test_6038, "TFM_CRYPTO_TEST_6038",
+     "Non Secure HKDF key derivation", {TEST_PASSED} },
+#endif /* TFM_CRYPTO_TEST_HKDF */
+};
+
+void register_testsuite_ns_crypto_interface(struct test_suite_t *p_test_suite)
+{
+    uint32_t list_size = (sizeof(crypto_tests) / sizeof(crypto_tests[0]));
+
+    set_testsuite("Crypto non-secure interface test (TFM_CRYPTO_TEST_6XXX)",
+                  crypto_tests, list_size, p_test_suite);
+}
+
+/**
+ * \brief Non-Secure interface test for Crypto
+ *
+ * \details The scope of this set of tests is to functionally verify
+ *          the interfaces specified by psa/crypto.h are working
+ *          as expected. This is not meant to cover all possible
+ *          scenarios and corner cases.
+ *
+ */
+static void tfm_crypto_test_6001(struct test_result_t *ret)
+{
+    psa_key_interface_test(PSA_KEY_TYPE_AES, ret);
+}
+
+#ifdef TFM_CRYPTO_TEST_ALG_CBC
+static void tfm_crypto_test_6002(struct test_result_t *ret)
+{
+    psa_cipher_test(PSA_KEY_TYPE_AES, PSA_ALG_CBC_NO_PADDING, ret);
+}
+#endif /* TFM_CRYPTO_TEST_ALG_CBC */
+
+#ifdef TFM_CRYPTO_TEST_ALG_CFB
+static void tfm_crypto_test_6003(struct test_result_t *ret)
+{
+    psa_cipher_test(PSA_KEY_TYPE_AES, PSA_ALG_CFB, ret);
+}
+#endif /* TFM_CRYPTO_TEST_ALG_CFB */
+
+#ifdef TFM_CRYPTO_TEST_ALG_CTR
+static void tfm_crypto_test_6005(struct test_result_t *ret)
+{
+    psa_cipher_test(PSA_KEY_TYPE_AES, PSA_ALG_CTR, ret);
+}
+#endif /* TFM_CRYPTO_TEST_ALG_CTR */
+
+static void tfm_crypto_test_6007(struct test_result_t *ret)
+{
+    psa_invalid_cipher_test(PSA_KEY_TYPE_AES, PSA_ALG_HMAC(PSA_ALG_SHA_256),
+                            16, ret);
+}
+
+static void tfm_crypto_test_6008(struct test_result_t *ret)
+{
+    psa_invalid_key_length_test(ret);
+}
+
+#ifdef TFM_CRYPTO_TEST_ALG_CFB
+static void tfm_crypto_test_6009(struct test_result_t *ret)
+{
+    /* HMAC is not a block cipher */
+    psa_invalid_cipher_test(PSA_KEY_TYPE_HMAC, PSA_ALG_CFB, 16, ret);
+}
+#endif /* TFM_CRYPTO_TEST_ALG_CFB */
+
+static void tfm_crypto_test_6010(struct test_result_t *ret)
+{
+    psa_unsupported_hash_test(PSA_ALG_SHA_1, ret);
+}
+
+static void tfm_crypto_test_6011(struct test_result_t *ret)
+{
+    psa_hash_test(PSA_ALG_SHA_224, ret);
+}
+
+static void tfm_crypto_test_6012(struct test_result_t *ret)
+{
+    psa_hash_test(PSA_ALG_SHA_256, ret);
+}
+
+#ifdef TFM_CRYPTO_TEST_ALG_SHA_512
+static void tfm_crypto_test_6013(struct test_result_t *ret)
+{
+    psa_hash_test(PSA_ALG_SHA_384, ret);
+}
+
+static void tfm_crypto_test_6014(struct test_result_t *ret)
+{
+    psa_hash_test(PSA_ALG_SHA_512, ret);
+}
+#endif /* TFM_CRYPTO_TEST_ALG_SHA_512 */
+
+static void tfm_crypto_test_6019(struct test_result_t *ret)
+{
+    psa_unsupported_mac_test(PSA_KEY_TYPE_HMAC, PSA_ALG_HMAC(PSA_ALG_SHA_1),
+                             ret);
+}
+
+static void tfm_crypto_test_6020(struct test_result_t *ret)
+{
+    psa_mac_test(PSA_ALG_HMAC(PSA_ALG_SHA_256), 0, ret);
+}
+
+#ifdef TFM_CRYPTO_TEST_ALG_SHA_512
+static void tfm_crypto_test_6021(struct test_result_t *ret)
+{
+    psa_mac_test(PSA_ALG_HMAC(PSA_ALG_SHA_384), 0, ret);
+}
+
+static void tfm_crypto_test_6022(struct test_result_t *ret)
+{
+    psa_mac_test(PSA_ALG_HMAC(PSA_ALG_SHA_512), 0, ret);
+}
+#endif /* TFM_CRYPTO_TEST_ALG_SHA_512 */
+
+static void tfm_crypto_test_6024(struct test_result_t *ret)
+{
+    psa_mac_test(PSA_ALG_HMAC(PSA_ALG_SHA_224), 1, ret);
+}
+
+#ifdef TFM_CRYPTO_TEST_ALG_CCM
+static void tfm_crypto_test_6030(struct test_result_t *ret)
+{
+    psa_aead_test(PSA_KEY_TYPE_AES, PSA_ALG_CCM, ret);
+}
+#endif /* TFM_CRYPTO_TEST_ALG_CCM */
+
+#ifdef TFM_CRYPTO_TEST_ALG_GCM
+static void tfm_crypto_test_6031(struct test_result_t *ret)
+{
+    psa_aead_test(PSA_KEY_TYPE_AES, PSA_ALG_GCM, ret);
+}
+#endif /* TFM_CRYPTO_TEST_ALG_GCM */
+
+static void tfm_crypto_test_6032(struct test_result_t *ret)
+{
+    psa_policy_key_interface_test(ret);
+}
+
+static void tfm_crypto_test_6033(struct test_result_t *ret)
+{
+    psa_policy_invalid_policy_usage_test(ret);
+}
+
+static void tfm_crypto_test_6034(struct test_result_t *ret)
+{
+    psa_persistent_key_test(1, ret);
+}
+
+#ifdef TFM_CRYPTO_TEST_ALG_CCM
+static void tfm_crypto_test_6035(struct test_result_t *ret)
+{
+    psa_algorithm_t alg = PSA_ALG_AEAD_WITH_TAG_LENGTH(PSA_ALG_CCM,
+                                                       TRUNCATED_AUTH_TAG_LEN);
+
+    psa_aead_test(PSA_KEY_TYPE_AES, alg, ret);
+}
+#endif /* TFM_CRYPTO_TEST_ALG_GCM */
+
+static void tfm_crypto_test_6036(struct test_result_t *ret)
+{
+    psa_key_derivation_test(PSA_ALG_TLS12_PRF(PSA_ALG_SHA_256), ret);
+}
+
+static void tfm_crypto_test_6037(struct test_result_t *ret)
+{
+    psa_key_derivation_test(PSA_ALG_TLS12_PSK_TO_MS(PSA_ALG_SHA_256), ret);
+}
+
+#ifdef TFM_CRYPTO_TEST_HKDF
+static void tfm_crypto_test_6038(struct test_result_t *ret)
+{
+    psa_key_derivation_test(PSA_ALG_HKDF(PSA_ALG_SHA_256), ret);
+}
+#endif /* TFM_CRYPTO_TEST_HKDF */
diff --git a/test/suites/crypto/non_secure/crypto_ns_tests.h b/test/suites/crypto/non_secure/crypto_ns_tests.h
new file mode 100644
index 0000000..aba8c87
--- /dev/null
+++ b/test/suites/crypto/non_secure/crypto_ns_tests.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __CRYPTO_NS_TESTS_H__
+#define __CRYPTO_NS_TESTS_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "test/framework/test_framework.h"
+
+/**
+ * \brief Register testsuite for Crypto non-secure interface.
+ *
+ * \param[in] p_test_suite The test suite to be executed.
+ */
+void register_testsuite_ns_crypto_interface(struct test_suite_t *p_test_suite);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CRYPTO_NS_TESTS_H__ */
diff --git a/test/suites/crypto/secure/crypto_s_tests.h b/test/suites/crypto/secure/crypto_s_tests.h
new file mode 100644
index 0000000..6f5f359
--- /dev/null
+++ b/test/suites/crypto/secure/crypto_s_tests.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2018-2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __CRYPTO_S_TESTS_H__
+#define __CRYPTO_S_TESTS_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "test/framework/test_framework.h"
+
+void register_testsuite_s_crypto_interface(struct test_suite_t *p_test_suite);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CRYPTO_S_TESTS_H__ */
diff --git a/test/suites/crypto/secure/crypto_sec_interface_testsuite.c b/test/suites/crypto/secure/crypto_sec_interface_testsuite.c
new file mode 100644
index 0000000..bd314e0
--- /dev/null
+++ b/test/suites/crypto/secure/crypto_sec_interface_testsuite.c
@@ -0,0 +1,351 @@
+/*
+ * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "test/framework/test_framework_helpers.h"
+#include "test/test_services/tfm_secure_client_2/tfm_secure_client_2_api.h"
+#include "tfm_api.h"
+#include "../crypto_tests_common.h"
+
+/* List of tests */
+static void tfm_crypto_test_5001(struct test_result_t *ret);
+#ifdef TFM_CRYPTO_TEST_ALG_CBC
+static void tfm_crypto_test_5002(struct test_result_t *ret);
+#endif /* TFM_CRYPTO_TEST_ALG_CBC */
+#ifdef TFM_CRYPTO_TEST_ALG_CFB
+static void tfm_crypto_test_5003(struct test_result_t *ret);
+#endif /* TFM_CRYPTO_TEST_ALG_CFB */
+#ifdef TFM_CRYPTO_TEST_ALG_CTR
+static void tfm_crypto_test_5005(struct test_result_t *ret);
+#endif /* TFM_CRYPTO_TEST_ALG_CTR */
+static void tfm_crypto_test_5007(struct test_result_t *ret);
+static void tfm_crypto_test_5008(struct test_result_t *ret);
+#ifdef TFM_CRYPTO_TEST_ALG_CFB
+static void tfm_crypto_test_5009(struct test_result_t *ret);
+#endif /* TFM_CRYPTO_TEST_ALG_CFB */
+static void tfm_crypto_test_5010(struct test_result_t *ret);
+static void tfm_crypto_test_5011(struct test_result_t *ret);
+static void tfm_crypto_test_5012(struct test_result_t *ret);
+#ifdef TFM_CRYPTO_TEST_ALG_SHA_512
+static void tfm_crypto_test_5013(struct test_result_t *ret);
+static void tfm_crypto_test_5014(struct test_result_t *ret);
+#endif /* TFM_CRYPTO_TEST_ALG_SHA_512 */
+static void tfm_crypto_test_5019(struct test_result_t *ret);
+static void tfm_crypto_test_5020(struct test_result_t *ret);
+#ifdef TFM_CRYPTO_TEST_ALG_SHA_512
+static void tfm_crypto_test_5021(struct test_result_t *ret);
+static void tfm_crypto_test_5022(struct test_result_t *ret);
+#endif /* TFM_CRYPTO_TEST_ALG_SHA_512 */
+static void tfm_crypto_test_5024(struct test_result_t *ret);
+#ifdef TFM_CRYPTO_TEST_ALG_CCM
+static void tfm_crypto_test_5030(struct test_result_t *ret);
+#endif /* TFM_CRYPTO_TEST_ALG_CCM */
+#ifdef TFM_CRYPTO_TEST_ALG_GCM
+static void tfm_crypto_test_5031(struct test_result_t *ret);
+#endif /* TFM_CRYPTO_TEST_ALG_GCM */
+static void tfm_crypto_test_5032(struct test_result_t *ret);
+static void tfm_crypto_test_5033(struct test_result_t *ret);
+static void tfm_crypto_test_5034(struct test_result_t *ret);
+static void tfm_crypto_test_5035(struct test_result_t *ret);
+#ifdef TFM_CRYPTO_TEST_ALG_CCM
+static void tfm_crypto_test_5036(struct test_result_t *ret);
+#endif /* TFM_CRYPTO_TEST_ALG_CCM */
+static void tfm_crypto_test_5037(struct test_result_t *ret);
+static void tfm_crypto_test_5038(struct test_result_t *ret);
+#ifdef TFM_CRYPTO_TEST_HKDF
+static void tfm_crypto_test_5039(struct test_result_t *ret);
+#endif /* TFM_CRYPTO_TEST_HKDF */
+
+static struct test_t crypto_tests[] = {
+    {&tfm_crypto_test_5001, "TFM_CRYPTO_TEST_5001",
+     "Secure Key management interface", {TEST_PASSED} },
+#ifdef TFM_CRYPTO_TEST_ALG_CBC
+    {&tfm_crypto_test_5002, "TFM_CRYPTO_TEST_5002",
+     "Secure Symmetric encryption (AES-128-CBC) interface", {TEST_PASSED} },
+#endif /* TFM_CRYPTO_TEST_ALG_CBC */
+#ifdef TFM_CRYPTO_TEST_ALG_CFB
+    {&tfm_crypto_test_5003, "TFM_CRYPTO_TEST_5003",
+     "Secure Symmetric encryption (AES-128-CFB) interface", {TEST_PASSED} },
+#endif /* TFM_CRYPTO_TEST_ALG_CFB */
+#ifdef TFM_CRYPTO_TEST_ALG_CTR
+    {&tfm_crypto_test_5005, "TFM_CRYPTO_TEST_5005",
+     "Secure Symmetric encryption (AES-128-CTR) interface", {TEST_PASSED} },
+#endif /* TFM_CRYPTO_TEST_ALG_CTR */
+    {&tfm_crypto_test_5007, "TFM_CRYPTO_TEST_5007",
+     "Secure Symmetric encryption invalid cipher", {TEST_PASSED} },
+    {&tfm_crypto_test_5008, "TFM_CRYPTO_TEST_5008",
+     "Secure Symmetric encryption invalid cipher (AES-152)", {TEST_PASSED} },
+#ifdef TFM_CRYPTO_TEST_ALG_CFB
+    {&tfm_crypto_test_5009, "TFM_CRYPTO_TEST_5009",
+     "Secure Symmetric encryption invalid cipher (HMAC-128-CFB)", {TEST_PASSED} },
+#endif /* TFM_CRYPTO_TEST_ALG_CFB */
+    {&tfm_crypto_test_5010, "TFM_CRYPTO_TEST_5010",
+     "Secure Unsupported Hash (SHA-1) interface", {TEST_PASSED} },
+    {&tfm_crypto_test_5011, "TFM_CRYPTO_TEST_5011",
+     "Secure Hash (SHA-224) interface", {TEST_PASSED} },
+    {&tfm_crypto_test_5012, "TFM_CRYPTO_TEST_5012",
+     "Secure Hash (SHA-256) interface", {TEST_PASSED} },
+#ifdef TFM_CRYPTO_TEST_ALG_SHA_512
+    {&tfm_crypto_test_5013, "TFM_CRYPTO_TEST_5013",
+     "Secure Hash (SHA-384) interface", {TEST_PASSED} },
+    {&tfm_crypto_test_5014, "TFM_CRYPTO_TEST_5014",
+     "Secure Hash (SHA-512) interface", {TEST_PASSED} },
+#endif /* TFM_CRYPTO_TEST_ALG_SHA_512 */
+    {&tfm_crypto_test_5019, "TFM_CRYPTO_TEST_5019",
+     "Secure Unsupported HMAC (SHA-1) interface", {TEST_PASSED} },
+    {&tfm_crypto_test_5020, "TFM_CRYPTO_TEST_5020",
+     "Secure HMAC (SHA-256) interface", {TEST_PASSED} },
+#ifdef TFM_CRYPTO_TEST_ALG_SHA_512
+    {&tfm_crypto_test_5021, "TFM_CRYPTO_TEST_5021",
+     "Secure HMAC (SHA-384) interface", {TEST_PASSED} },
+    {&tfm_crypto_test_5022, "TFM_CRYPTO_TEST_5022",
+     "Secure HMAC (SHA-512) interface", {TEST_PASSED} },
+#endif /* TFM_CRYPTO_TEST_ALG_SHA_512 */
+    {&tfm_crypto_test_5024, "TFM_CRYPTO_TEST_5024",
+     "Secure HMAC with long key (SHA-224) interface", {TEST_PASSED} },
+#ifdef TFM_CRYPTO_TEST_ALG_CCM
+    {&tfm_crypto_test_5030, "TFM_CRYPTO_TEST_5030",
+     "Secure AEAD (AES-128-CCM) interface", {TEST_PASSED} },
+#endif /* TFM_CRYPTO_TEST_ALG_CCM */
+#ifdef TFM_CRYPTO_TEST_ALG_GCM
+    {&tfm_crypto_test_5031, "TFM_CRYPTO_TEST_5031",
+     "Secure AEAD (AES-128-GCM) interface", {TEST_PASSED} },
+#endif /* TFM_CRYPTO_TEST_ALG_GCM */
+    {&tfm_crypto_test_5032, "TFM_CRYPTO_TEST_5032",
+     "Secure key policy interface", {TEST_PASSED} },
+    {&tfm_crypto_test_5033, "TFM_CRYPTO_TEST_5033",
+     "Secure key policy check permissions", {TEST_PASSED} },
+    {&tfm_crypto_test_5034, "TFM_CRYPTO_TEST_5034",
+     "Secure persistent key interface", {TEST_PASSED} },
+    {&tfm_crypto_test_5035, "TFM_CRYPTO_TEST_5035",
+     "Key access control", {TEST_PASSED} },
+#ifdef TFM_CRYPTO_TEST_ALG_CCM
+    {&tfm_crypto_test_5036, "TFM_CRYPTO_TEST_6036",
+     "Secure AEAD interface with truncated auth tag (AES-128-CCM-8)",
+     {TEST_PASSED} },
+#endif /* TFM_CRYPTO_TEST_ALG_CCM */
+    {&tfm_crypto_test_5037, "TFM_CRYPTO_TEST_5037",
+     "Secure TLS 1.2 PRF key derivation", {TEST_PASSED} },
+    {&tfm_crypto_test_5038, "TFM_CRYPTO_TEST_5038",
+     "Secure TLS-1.2 PSK-to-MasterSecret key derivation", {TEST_PASSED} },
+#ifdef TFM_CRYPTO_TEST_HKDF
+    {&tfm_crypto_test_5039, "TFM_CRYPTO_TEST_5039",
+     "Secure HKDF key derivation", {TEST_PASSED} },
+#endif /* TFM_CRYPTO_TEST_HKDF */
+};
+
+void register_testsuite_s_crypto_interface(struct test_suite_t *p_test_suite)
+{
+    uint32_t list_size = (sizeof(crypto_tests) / sizeof(crypto_tests[0]));
+
+    set_testsuite("Crypto secure interface tests (TFM_CRYPTO_TEST_5XXX)",
+                  crypto_tests, list_size, p_test_suite);
+}
+
+/**
+ * \brief Secure interface test for Crypto
+ *
+ * \details The scope of this set of tests is to functionally verify
+ *          the interfaces specified by psa/crypto.h are working
+ *          as expected. This is not meant to cover all possible
+ *          scenarios and corner cases.
+ *
+ */
+static void tfm_crypto_test_5001(struct test_result_t *ret)
+{
+    psa_key_interface_test(PSA_KEY_TYPE_AES, ret);
+}
+
+#ifdef TFM_CRYPTO_TEST_ALG_CBC
+static void tfm_crypto_test_5002(struct test_result_t *ret)
+{
+    psa_cipher_test(PSA_KEY_TYPE_AES, PSA_ALG_CBC_NO_PADDING, ret);
+}
+#endif /* TFM_CRYPTO_TEST_ALG_CBC */
+
+#ifdef TFM_CRYPTO_TEST_ALG_CFB
+static void tfm_crypto_test_5003(struct test_result_t *ret)
+{
+    psa_cipher_test(PSA_KEY_TYPE_AES, PSA_ALG_CFB, ret);
+}
+#endif /* TFM_CRYPTO_TEST_ALG_CFB */
+
+#ifdef TFM_CRYPTO_TEST_ALG_CTR
+static void tfm_crypto_test_5005(struct test_result_t *ret)
+{
+    psa_cipher_test(PSA_KEY_TYPE_AES, PSA_ALG_CTR, ret);
+}
+#endif /* TFM_CRYPTO_TEST_ALG_CTR */
+
+static void tfm_crypto_test_5007(struct test_result_t *ret)
+{
+    psa_invalid_cipher_test(PSA_KEY_TYPE_AES, PSA_ALG_HMAC(PSA_ALG_SHA_256),
+                            16, ret);
+}
+
+static void tfm_crypto_test_5008(struct test_result_t *ret)
+{
+    psa_invalid_key_length_test(ret);
+}
+
+#ifdef TFM_CRYPTO_TEST_ALG_CFB
+static void tfm_crypto_test_5009(struct test_result_t *ret)
+{
+    /* HMAC is not a block cipher */
+    psa_invalid_cipher_test(PSA_KEY_TYPE_HMAC, PSA_ALG_CFB, 16, ret);
+}
+#endif /* TFM_CRYPTO_TEST_ALG_CFB */
+
+static void tfm_crypto_test_5010(struct test_result_t *ret)
+{
+    psa_unsupported_hash_test(PSA_ALG_SHA_1, ret);
+}
+
+static void tfm_crypto_test_5011(struct test_result_t *ret)
+{
+    psa_hash_test(PSA_ALG_SHA_224, ret);
+}
+
+static void tfm_crypto_test_5012(struct test_result_t *ret)
+{
+    psa_hash_test(PSA_ALG_SHA_256, ret);
+}
+
+#ifdef TFM_CRYPTO_TEST_ALG_SHA_512
+static void tfm_crypto_test_5013(struct test_result_t *ret)
+{
+    psa_hash_test(PSA_ALG_SHA_384, ret);
+}
+
+static void tfm_crypto_test_5014(struct test_result_t *ret)
+{
+    psa_hash_test(PSA_ALG_SHA_512, ret);
+}
+#endif /* TFM_CRYPTO_TEST_ALG_SHA_512 */
+
+static void tfm_crypto_test_5019(struct test_result_t *ret)
+{
+    psa_unsupported_mac_test(PSA_KEY_TYPE_HMAC, PSA_ALG_HMAC(PSA_ALG_SHA_1),
+                             ret);
+}
+
+static void tfm_crypto_test_5020(struct test_result_t *ret)
+{
+    psa_mac_test(PSA_ALG_HMAC(PSA_ALG_SHA_256), 0, ret);
+}
+
+#ifdef TFM_CRYPTO_TEST_ALG_SHA_512
+static void tfm_crypto_test_5021(struct test_result_t *ret)
+{
+    psa_mac_test(PSA_ALG_HMAC(PSA_ALG_SHA_384), 0, ret);
+}
+
+static void tfm_crypto_test_5022(struct test_result_t *ret)
+{
+    psa_mac_test(PSA_ALG_HMAC(PSA_ALG_SHA_512), 0, ret);
+}
+#endif /* TFM_CRYPTO_TEST_ALG_SHA_512 */
+
+static void tfm_crypto_test_5024(struct test_result_t *ret)
+{
+    psa_mac_test(PSA_ALG_HMAC(PSA_ALG_SHA_224), 1, ret);
+}
+
+#ifdef TFM_CRYPTO_TEST_ALG_CCM
+static void tfm_crypto_test_5030(struct test_result_t *ret)
+{
+    psa_aead_test(PSA_KEY_TYPE_AES, PSA_ALG_CCM, ret);
+}
+#endif /* TFM_CRYPTO_TEST_ALG_CCM */
+
+#ifdef TFM_CRYPTO_TEST_ALG_GCM
+static void tfm_crypto_test_5031(struct test_result_t *ret)
+{
+    psa_aead_test(PSA_KEY_TYPE_AES, PSA_ALG_GCM, ret);
+}
+#endif /* TFM_CRYPTO_TEST_ALG_GCM */
+
+static void tfm_crypto_test_5032(struct test_result_t *ret)
+{
+    psa_policy_key_interface_test(ret);
+}
+
+static void tfm_crypto_test_5033(struct test_result_t *ret)
+{
+    psa_policy_invalid_policy_usage_test(ret);
+}
+
+static void tfm_crypto_test_5034(struct test_result_t *ret)
+{
+    psa_persistent_key_test(1, ret);
+}
+
+/**
+ * \brief Tests key access control based on partition ID
+ *
+ * \param[out] ret  Test result
+ */
+static void tfm_crypto_test_5035(struct test_result_t *ret)
+{
+    psa_status_t status;
+    psa_key_handle_t key_handle;
+    const uint8_t data[] = "THIS IS MY KEY1";
+    psa_key_attributes_t key_attributes = psa_key_attributes_init();
+
+    /* Set key sage and type */
+    psa_set_key_usage_flags(&key_attributes, PSA_KEY_USAGE_EXPORT);
+    psa_set_key_type(&key_attributes, PSA_KEY_TYPE_AES);
+
+    status = psa_import_key(&key_attributes, data, sizeof(data),
+                            &key_handle);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Failed to import key");
+        return;
+    }
+
+    /* Attempt to destroy the key handle from the Secure Client 2 partition */
+    status = tfm_secure_client_2_call_test(
+                                      TFM_SECURE_CLIENT_2_ID_CRYPTO_ACCESS_CTRL,
+                                      &key_handle, sizeof(key_handle));
+    if (status != PSA_ERROR_NOT_PERMITTED) {
+        TEST_FAIL("Should not be able to destroy key from another partition");
+        return;
+    }
+
+    /* Destroy the key */
+    status = psa_destroy_key(key_handle);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Error destroying a key");
+    }
+    return;
+}
+
+#ifdef TFM_CRYPTO_TEST_ALG_CCM
+static void tfm_crypto_test_5036(struct test_result_t *ret)
+{
+    psa_algorithm_t alg = PSA_ALG_AEAD_WITH_TAG_LENGTH(PSA_ALG_CCM,
+                                                       TRUNCATED_AUTH_TAG_LEN);
+
+    psa_aead_test(PSA_KEY_TYPE_AES, alg, ret);
+}
+#endif /* TFM_CRYPTO_TEST_ALG_GCM */
+
+static void tfm_crypto_test_5037(struct test_result_t *ret)
+{
+    psa_key_derivation_test(PSA_ALG_TLS12_PRF(PSA_ALG_SHA_256), ret);
+}
+
+static void tfm_crypto_test_5038(struct test_result_t *ret)
+{
+    psa_key_derivation_test(PSA_ALG_TLS12_PSK_TO_MS(PSA_ALG_SHA_256), ret);
+}
+
+#ifdef TFM_CRYPTO_TEST_HKDF
+static void tfm_crypto_test_5039(struct test_result_t *ret)
+{
+    psa_key_derivation_test(PSA_ALG_HKDF(PSA_ALG_SHA_256), ret);
+}
+#endif /* TFM_CRYPTO_TEST_HKDF */
diff --git a/test/suites/ipc/CMakeLists.inc b/test/suites/ipc/CMakeLists.inc
new file mode 100644
index 0000000..c7a48be
--- /dev/null
+++ b/test/suites/ipc/CMakeLists.inc
@@ -0,0 +1,36 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2018-2019, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+#Definitions to compile the "ipc test" module.
+#This file assumes it will be included from a project specific cmakefile, and
+#will not create a library or executable.
+#Inputs:
+#	TFM_ROOT_DIR - root directory of the TF-M repo.
+#
+#Outputs:
+#	Will modify include directories to make the source compile.
+#	ALL_SRC_C: C source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+#	ALL_SRC_CXX: C++ source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+#	ALL_SRC_ASM: assembly source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+#	Include directories will be modified by using the include_directories() commands as needed.
+
+#Get the current directory where this file is located.
+set(IPC_TEST_DIR ${CMAKE_CURRENT_LIST_DIR})
+if(NOT DEFINED TFM_ROOT_DIR)
+	message(FATAL_ERROR "Please set TFM_ROOT_DIR before including this file.")
+endif()
+
+if (NOT DEFINED IPC_TEST)
+	message(FATAL_ERROR "Incomplete build configuration: IPC_TEST is undefined. ")
+elseif(IPC_TEST)
+	list(APPEND ALL_SRC_C_S "${IPC_TEST_DIR}/secure/ipc_s_interface_testsuite.c")
+	list(APPEND ALL_SRC_C_NS "${IPC_TEST_DIR}/non_secure/ipc_ns_interface_testsuite.c")
+
+	#Setting include directories
+	embedded_include_directories(PATH ${TFM_ROOT_DIR} ABSOLUTE)
+	embedded_include_directories(PATH ${TFM_ROOT_DIR}/interface/include ABSOLUTE)
+endif()
diff --git a/test/suites/ipc/non_secure/ipc_ns_interface_testsuite.c b/test/suites/ipc/non_secure/ipc_ns_interface_testsuite.c
new file mode 100644
index 0000000..d051b78
--- /dev/null
+++ b/test/suites/ipc/non_secure/ipc_ns_interface_testsuite.c
@@ -0,0 +1,362 @@
+/*
+ * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <stdio.h>
+#include "ipc_ns_tests.h"
+#include "psa/client.h"
+#include "test/framework/test_framework_helpers.h"
+#ifdef TFM_PSA_API
+#include "psa_manifest/sid.h"
+#endif
+
+/* List of tests */
+static void tfm_ipc_test_1001(struct test_result_t *ret);
+static void tfm_ipc_test_1002(struct test_result_t *ret);
+static void tfm_ipc_test_1003(struct test_result_t *ret);
+static void tfm_ipc_test_1004(struct test_result_t *ret);
+static void tfm_ipc_test_1005(struct test_result_t *ret);
+static void tfm_ipc_test_1006(struct test_result_t *ret);
+
+#ifdef TFM_IPC_ISOLATION_2_TEST_READ_ONLY_MEM
+static void tfm_ipc_test_1007(struct test_result_t *ret);
+#endif
+
+#ifdef TFM_IPC_ISOLATION_2_APP_ACCESS_PSA_MEM
+static void tfm_ipc_test_1008(struct test_result_t *ret);
+#endif
+
+#ifdef TFM_IPC_ISOLATION_2_MEM_CHECK
+static void tfm_ipc_test_1009(struct test_result_t *ret);
+#endif
+
+static void tfm_ipc_test_1010(struct test_result_t *ret);
+
+static struct test_t ipc_veneers_tests[] = {
+    {&tfm_ipc_test_1001, "TFM_IPC_TEST_1001",
+     "Get PSA framework version", {TEST_PASSED}},
+    {&tfm_ipc_test_1002, "TFM_IPC_TEST_1002",
+     "Get version of an RoT Service", {TEST_PASSED}},
+    {&tfm_ipc_test_1003, "TFM_IPC_TEST_1003",
+     "Connect to an RoT Service", {TEST_PASSED}},
+    {&tfm_ipc_test_1004, "TFM_IPC_TEST_1004",
+     "Call an RoT Service", {TEST_PASSED}},
+    {&tfm_ipc_test_1005, "TFM_IPC_TEST_1005",
+     "Call IPC_INIT_BASIC_TEST service", {TEST_PASSED}},
+    {&tfm_ipc_test_1006, "TFM_IPC_TEST_1006",
+     "Call PSA RoT access APP RoT memory test service", {TEST_PASSED}},
+#ifdef TFM_IPC_ISOLATION_2_TEST_READ_ONLY_MEM
+    {&tfm_ipc_test_1007, "TFM_IPC_TEST_1007",
+     "Call PSA RoT access APP RoT readonly memory test service", {TEST_PASSED}},
+#endif
+#ifdef TFM_IPC_ISOLATION_2_APP_ACCESS_PSA_MEM
+    {&tfm_ipc_test_1008, "TFM_IPC_TEST_1008",
+     "Call APP RoT access PSA RoT memory test service", {TEST_PASSED}},
+#endif
+#ifdef TFM_IPC_ISOLATION_2_MEM_CHECK
+    {&tfm_ipc_test_1009, "TFM_IPC_TEST_1009",
+     "Call APP RoT memory check test service", {TEST_PASSED}},
+#endif
+    {&tfm_ipc_test_1010, "TFM_IPC_TEST_1010",
+     "Test psa_call with the status of PSA_ERROR_PROGRAMMER_ERROR", {TEST_PASSED}},
+};
+
+void register_testsuite_ns_ipc_interface(struct test_suite_t *p_test_suite)
+{
+    uint32_t list_size;
+
+    list_size = (sizeof(ipc_veneers_tests) / sizeof(ipc_veneers_tests[0]));
+
+    set_testsuite("IPC non-secure interface test (TFM_IPC_TEST_1XXX)",
+                  ipc_veneers_tests, list_size, p_test_suite);
+}
+
+/**
+ * \brief Retrieve the version of the PSA Framework API.
+ *
+ * \note This is a functional test only and doesn't
+ *       mean to test all possible combinations of
+ *       input parameters and return values.
+ */
+static void tfm_ipc_test_1001(struct test_result_t *ret)
+{
+    uint32_t version;
+
+    version = psa_framework_version();
+    if (version == PSA_FRAMEWORK_VERSION) {
+        TEST_LOG("The version of the PSA Framework API is %d.\r\n", version);
+    } else {
+        TEST_FAIL("The version of the PSA Framework API is not valid!\r\n");
+        return;
+    }
+}
+
+/**
+ * \brief Retrieve the version of an RoT Service.
+ */
+static void tfm_ipc_test_1002(struct test_result_t *ret)
+{
+    uint32_t version;
+
+    version = psa_version(IPC_SERVICE_TEST_BASIC_SID);
+    if (version == PSA_VERSION_NONE) {
+        TEST_FAIL("RoT Service is not implemented or caller is not authorized" \
+                  "to access it!\r\n");
+        return;
+    } else {
+        /* Valid version number */
+        TEST_LOG("The service version is %d.\r\n", version);
+    }
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Connect to an RoT Service by its SID.
+ */
+static void tfm_ipc_test_1003(struct test_result_t *ret)
+{
+    psa_handle_t handle;
+
+    handle = psa_connect(IPC_SERVICE_TEST_BASIC_SID,
+                         IPC_SERVICE_TEST_BASIC_VERSION);
+    if (handle > 0) {
+        TEST_LOG("Connect success!\r\n");
+    } else {
+        TEST_FAIL("The RoT Service has refused the connection!\r\n");
+        return;
+    }
+    psa_close(handle);
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Call an RoT Service.
+ */
+static void tfm_ipc_test_1004(struct test_result_t *ret)
+{
+    char str1[] = "str1";
+    char str2[] = "str2";
+    char str3[128], str4[128];
+    struct psa_invec invecs[2] = {{str1, sizeof(str1)/sizeof(char)},
+                                  {str2, sizeof(str2)/sizeof(char)}};
+    struct psa_outvec outvecs[2] = {{str3, sizeof(str3)/sizeof(char)},
+                                    {str4, sizeof(str4)/sizeof(char)}};
+    psa_handle_t handle;
+    psa_status_t status;
+    uint32_t version;
+
+    version = psa_version(IPC_SERVICE_TEST_BASIC_SID);
+    TEST_LOG("TFM service support version is %d.\r\n", version);
+    handle = psa_connect(IPC_SERVICE_TEST_BASIC_SID,
+                         IPC_SERVICE_TEST_BASIC_VERSION);
+    status = psa_call(handle, PSA_IPC_CALL, invecs, 2, outvecs, 2);
+    if (status >= 0) {
+        TEST_LOG("psa_call is successful!\r\n");
+    } else {
+        TEST_FAIL("psa_call is failed!\r\n");
+        return;
+    }
+
+    TEST_LOG("outvec1 is: %s\r\n", outvecs[0].base);
+    TEST_LOG("outvec2 is: %s\r\n", outvecs[1].base);
+    psa_close(handle);
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Call IPC_CLIENT_TEST_BASIC_SID RoT Service to run the IPC basic test.
+ */
+static void tfm_ipc_test_1005(struct test_result_t *ret)
+{
+    psa_handle_t handle;
+    psa_status_t status;
+    int test_result;
+    struct psa_outvec outvecs[1] = {{&test_result, sizeof(test_result)}};
+
+    handle = psa_connect(IPC_CLIENT_TEST_BASIC_SID,
+                         IPC_CLIENT_TEST_BASIC_VERSION);
+    if (handle > 0) {
+        TEST_LOG("Connect success!\r\n");
+    } else {
+        TEST_LOG("The RoT Service has refused the connection!\r\n");
+        ret->val = TEST_FAILED;
+        return;
+    }
+
+    status = psa_call(handle, PSA_IPC_CALL, NULL, 0, outvecs, 1);
+    if (status >= 0) {
+        TEST_LOG("Call success!\r\n");
+        if (test_result > 0) {
+            ret->val = TEST_PASSED;
+        } else {
+            ret->val = TEST_FAILED;
+        }
+    } else {
+        TEST_LOG("Call failed!\r\n");
+        ret->val = TEST_FAILED;
+    }
+
+    psa_close(handle);
+}
+
+/**
+ * \brief Call IPC_CLIENT_TEST_PSA_ACCESS_APP_MEM_SID RoT Service
+ *  to run the IPC PSA access APP mem test.
+ */
+static void tfm_ipc_test_1006(struct test_result_t *ret)
+{
+    psa_handle_t handle;
+    psa_status_t status;
+    int test_result;
+    struct psa_outvec outvecs[1] = {{&test_result, sizeof(test_result)}};
+
+    handle = psa_connect(IPC_CLIENT_TEST_PSA_ACCESS_APP_MEM_SID,
+                         IPC_CLIENT_TEST_PSA_ACCESS_APP_MEM_VERSION);
+    if (handle > 0) {
+        TEST_LOG("Connect success!\r\n");
+    } else {
+        TEST_LOG("The RoT Service has refused the connection!\r\n");
+        ret->val = TEST_FAILED;
+        return;
+    }
+
+    status = psa_call(handle, PSA_IPC_CALL, NULL, 0, outvecs, 1);
+    if (status >= 0) {
+        TEST_LOG("Call success!\r\n");
+        if (test_result > 0) {
+            ret->val = TEST_PASSED;
+        } else {
+            ret->val = TEST_FAILED;
+        }
+    } else {
+        TEST_LOG("Call failed!\r\n");
+        ret->val = TEST_FAILED;
+    }
+
+    psa_close(handle);
+}
+
+#ifdef TFM_IPC_ISOLATION_2_TEST_READ_ONLY_MEM
+/**
+ * \brief Call IPC_CLIENT_TEST_PSA_ACCESS_APP_READ_ONLY_MEM_SID RoT Service
+ *  to run the IPC PSA access APP readonly mem test.
+ */
+static void tfm_ipc_test_1007(struct test_result_t *ret)
+{
+    psa_handle_t handle;
+    int test_result;
+    struct psa_outvec outvecs[1] = {{&test_result, sizeof(test_result)}};
+
+    handle = psa_connect(IPC_CLIENT_TEST_PSA_ACCESS_APP_READ_ONLY_MEM_SID,
+                         IPC_CLIENT_TEST_PSA_ACCESS_APP_READ_ONLY_MEM_VERSION);
+    if (handle > 0) {
+        TEST_LOG("Connect success!\r\n");
+    } else {
+        TEST_LOG("The RoT Service has refused the connection!\r\n");
+        ret->val = TEST_FAILED;
+        return;
+    }
+
+    psa_call(handle, PSA_IPC_CALL, NULL, 0, outvecs, 1);
+
+    /* The system should panic in psa_call. If runs here, the test fails. */
+    ret->val = TEST_FAILED;
+    psa_close(handle);
+}
+#endif
+
+#ifdef TFM_IPC_ISOLATION_2_APP_ACCESS_PSA_MEM
+/**
+ * \brief Call IPC_CLIENT_TEST_APP_ACCESS_PSA_MEM_SID RoT Service
+ *  to run the IPC APP access PSA mem test.
+ */
+static void tfm_ipc_test_1008(struct test_result_t *ret)
+{
+    psa_handle_t handle;
+    int test_result;
+    struct psa_outvec outvecs[1] = {{&test_result, sizeof(test_result)}};
+
+    handle = psa_connect(IPC_CLIENT_TEST_APP_ACCESS_PSA_MEM_SID,
+                         IPC_CLIENT_TEST_APP_ACCESS_PSA_MEM_VERSION);
+    if (handle > 0) {
+        TEST_LOG("Connect success!\r\n");
+    } else {
+        TEST_LOG("The RoT Service has refused the connection!\r\n");
+        ret->val = TEST_FAILED;
+        return;
+    }
+
+    psa_call(handle, PSA_IPC_CALL, NULL, 0, outvecs, 1);
+
+    /* The system should panic in psa_call. If runs here, the test fails. */
+    ret->val = TEST_FAILED;
+    psa_close(handle);
+}
+#endif
+
+#ifdef TFM_IPC_ISOLATION_2_MEM_CHECK
+/**
+ * \brief Call IPC_CLIENT_TEST_MEM_CHECK_SID RoT Service
+ *  to run the IPC mem check test.
+ */
+static void tfm_ipc_test_1009(struct test_result_t *ret)
+{
+    psa_handle_t handle;
+    int test_result;
+    struct psa_outvec outvecs[1] = {{&test_result, sizeof(test_result)}};
+
+    handle = psa_connect(IPC_CLIENT_TEST_MEM_CHECK_SID,
+                         IPC_CLIENT_TEST_MEM_CHECK_VERSION);
+    if (handle > 0) {
+        TEST_LOG("Connect success!\r\n");
+    } else {
+        TEST_LOG("The RoT Service has refused the connection!\r\n");
+        ret->val = TEST_FAILED;
+        return;
+    }
+
+    psa_call(handle, PSA_IPC_CALL, NULL, 0, outvecs, 1);
+
+    /* The system should panic in psa_call. If runs here, the test fails. */
+    ret->val = TEST_FAILED;
+    psa_close(handle);
+}
+#endif
+
+/**
+ * \brief Call IPC_SERVICE_TEST_CLIENT_PREGRAMMER_ERROR RoT Service to
+ *  test psa_call with the status of PSA_ERROR_PROGRAMMER_ERROR.
+ */
+static void tfm_ipc_test_1010(struct test_result_t *ret)
+{
+    psa_handle_t handle;
+    psa_status_t status;
+    handle = psa_connect(IPC_SERVICE_TEST_CLIENT_PROGRAMMER_ERROR_SID,
+                         IPC_SERVICE_TEST_CLIENT_PROGRAMMER_ERROR_VERSION);
+    if (handle > 0) {
+        TEST_LOG("Connect success!\r\n");
+    } else {
+        TEST_LOG("The RoT Service has refused the connection!\r\n");
+        ret->val = TEST_FAILED;
+        return;
+    }
+    status = psa_call(handle, PSA_IPC_CALL, NULL, 0, NULL, 0);
+    if (status == PSA_ERROR_PROGRAMMER_ERROR) {
+        TEST_LOG("The first time call success!\r\n");
+    } else {
+        TEST_LOG("The first time call failed!\r\n");
+        ret->val = TEST_FAILED;
+    }
+    status = psa_call(handle, PSA_IPC_CALL, NULL, 0, NULL, 0);
+    if (status == PSA_ERROR_PROGRAMMER_ERROR) {
+        TEST_LOG("The second time call success!\r\n");
+    } else {
+        TEST_LOG("The second time call failed!\r\n");
+        ret->val = TEST_FAILED;
+    }
+
+    psa_close(handle);
+}
diff --git a/test/suites/ipc/non_secure/ipc_ns_tests.h b/test/suites/ipc/non_secure/ipc_ns_tests.h
new file mode 100644
index 0000000..12f3bb6
--- /dev/null
+++ b/test/suites/ipc/non_secure/ipc_ns_tests.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __IPC_NS_TESTS_H__
+#define __IPC_NS_TESTS_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "test/framework/test_framework.h"
+
+/**
+ * \brief Register testsuite for ipc non-secure interface.
+ *
+ * \param[in] p_test_suite The test suite to be executed.
+ */
+void register_testsuite_ns_ipc_interface(struct test_suite_t *p_test_suite);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __IPC_NS_TESTS_H__ */
diff --git a/test/suites/ipc/secure/ipc_s_interface_testsuite.c b/test/suites/ipc/secure/ipc_s_interface_testsuite.c
new file mode 100644
index 0000000..213dfff
--- /dev/null
+++ b/test/suites/ipc/secure/ipc_s_interface_testsuite.c
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "ipc_s_tests.h"
+#include "psa/client.h"
+#include "test/framework/test_framework_helpers.h"
+
+/* List of tests */
+static void tfm_ipc_test_1001(struct test_result_t *ret);
+
+static struct test_t ipc_veneers_tests[] = {
+    {&tfm_ipc_test_1001, "TFM_IPC_TEST_1001", "Secure functional", {TEST_PASSED} },
+};
+
+void register_testsuite_s_ipc_interface(struct test_suite_t *p_test_suite)
+{
+    uint32_t list_size;
+
+    list_size = (sizeof(ipc_veneers_tests) / sizeof(ipc_veneers_tests[0]));
+
+    set_testsuite("IPC secure interface test (TFM_IPC_TEST_1XXX)",
+                  ipc_veneers_tests, list_size, p_test_suite);
+}
+
+/**
+ * \brief Functional test of the Secure interface
+ *
+ * \note This is a functional test only and doesn't
+ *       mean to test all possible combinations of
+ *       input parameters and return values.
+ */
+static void tfm_ipc_test_1001(struct test_result_t *ret)
+{
+    ret->val = TEST_PASSED;
+}
diff --git a/test/suites/ipc/secure/ipc_s_tests.h b/test/suites/ipc/secure/ipc_s_tests.h
new file mode 100644
index 0000000..3b6a755
--- /dev/null
+++ b/test/suites/ipc/secure/ipc_s_tests.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __IPC_S_TESTS_H__
+#define __IPC_S_TESTS_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "test/framework/test_framework.h"
+
+/**
+ * \brief Register testsuite for ipc secure interface.
+ *
+ * \param[in] p_test_suite The test suite to be executed.
+ */
+void register_testsuite_s_ipc_interface(struct test_suite_t *p_test_suite);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __IPC_S_TESTS_H__ */
diff --git a/test/suites/its/CMakeLists.inc b/test/suites/its/CMakeLists.inc
new file mode 100644
index 0000000..b79140c
--- /dev/null
+++ b/test/suites/its/CMakeLists.inc
@@ -0,0 +1,40 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+#Definitions to compile the "internal trusted storage test" module.
+#This file assumes it will be included from a project specific cmakefile, and
+#will not create a library or executable.
+#Inputs:
+#   TFM_ROOT_DIR - root directory of the TF-M repo.
+#
+#Outputs:
+#   Will modify include directories to make the source compile.
+#   ALL_SRC_C: C source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+#   ALL_SRC_CXX: C++ source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+#   ALL_SRC_ASM: assembly source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+#   Include directories will be modified by using the include_directories() commands as needed.
+
+#Get the current directory where this file is located.
+set(ITS_TEST_DIR ${CMAKE_CURRENT_LIST_DIR})
+if(NOT DEFINED TFM_ROOT_DIR)
+    message(FATAL_ERROR "Please set TFM_ROOT_DIR before including this file.")
+endif()
+
+if (NOT DEFINED ENABLE_INTERNAL_TRUSTED_STORAGE_SERVICE_TESTS)
+    message(FATAL_ERROR "Incomplete build configuration: ENABLE_INTERNAL_TRUSTED_STORAGE_SERVICE_TESTS is undefined. ")
+elseif (ENABLE_INTERNAL_TRUSTED_STORAGE_SERVICE_TESTS)
+    list(APPEND ALL_SRC_C_NS "${ITS_TEST_DIR}/non_secure/psa_its_ns_interface_testsuite.c"
+                "${ITS_TEST_DIR}/its_tests_common.c")
+
+    list(APPEND ALL_SRC_C_S "${ITS_TEST_DIR}/secure/psa_its_s_interface_testsuite.c"
+                "${ITS_TEST_DIR}/secure/psa_its_s_reliability_testsuite.c"
+                "${ITS_TEST_DIR}/its_tests_common.c")
+
+    #Setting include directories
+    embedded_include_directories(PATH ${TFM_ROOT_DIR} ABSOLUTE)
+    embedded_include_directories(PATH ${TFM_ROOT_DIR}/interface/include ABSOLUTE)
+endif()
diff --git a/test/suites/its/its_tests_common.c b/test/suites/its/its_tests_common.c
new file mode 100644
index 0000000..eb4e21c
--- /dev/null
+++ b/test/suites/its/its_tests_common.c
@@ -0,0 +1,1051 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "its_tests_common.h"
+#include "psa/internal_trusted_storage.h"
+#if DOMAIN_NS == 1
+#include <string.h>
+#else
+#include "tfm_memory_utils.h"
+#endif
+#include "flash_layout.h"
+
+#define TEST_019_CYCLES    3U
+
+static const uint8_t write_asset_data[ITS_MAX_ASSET_SIZE] = {0xBF};
+static uint8_t read_asset_data[ITS_MAX_ASSET_SIZE] = {0};
+
+void tfm_its_test_common_001(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_1;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const size_t data_len = 0;
+    const uint8_t write_data[] = {0};
+
+    /* Set with no data and no flags and a valid UID */
+    status = psa_its_set(uid, data_len, write_data, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should not fail with valid UID");
+        return;
+    }
+
+    /* Attempt to set a second time */
+    status = psa_its_set(uid, data_len, write_data, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should not fail the second time with valid UID");
+        return;
+    }
+
+    /* Set with an invalid UID */
+    status = psa_its_set(INVALID_UID, data_len, write_data, flags);
+    if (status != PSA_ERROR_INVALID_ARGUMENT) {
+        TEST_FAIL("Set should not succeed with an invalid UID");
+        return;
+    }
+
+    /* Call remove to clean up storage for the next test */
+    status = psa_its_remove(uid);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Remove should not fail with valid UID");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+void tfm_its_test_common_002(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_2;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const size_t data_len = WRITE_DATA_SIZE;
+    const uint8_t write_data[] = WRITE_DATA;
+
+    /* Set with no flags */
+    status = psa_its_set(WRITE_ONCE_UID, data_len, write_data, flags);
+    if (status == PSA_SUCCESS) {
+        /* Set with valid flag: PSA_STORAGE_FLAG_WRITE_ONCE (with previously
+         * created UID)
+         * Note: Once created, WRITE_ONCE_UID cannot be deleted. It is reused
+         * across multiple tests.
+         */
+        status = psa_its_set(WRITE_ONCE_UID, WRITE_ONCE_DATA_SIZE,
+                             WRITE_ONCE_DATA, PSA_STORAGE_FLAG_WRITE_ONCE);
+        if (status != PSA_SUCCESS) {
+            TEST_FAIL("Set should not fail with valid flags (existing UID)");
+            return;
+        }
+    } else if (status == PSA_ERROR_NOT_PERMITTED) {
+        /* The UID has already been created with the PSA_STORAGE_FLAG_WRITE_ONCE
+         * flag in a previous test run, so skip creating it again and emit a
+         * warning.
+         */
+        TEST_LOG("Note: The UID in this test has already been created with\r\n"
+                 "the PSA_STORAGE_FLAG_WRITE_ONCE flag in a previous test\r\n"
+                 "run. Wipe the storage area to run the full test.\r\n");
+    } else {
+        TEST_FAIL("Set should not fail with no flags");
+        return;
+    }
+
+    /* Set with invalid flags */
+    status = psa_its_set(uid, data_len, write_data, INVALID_FLAG);
+    if (status != PSA_ERROR_NOT_SUPPORTED) {
+        TEST_FAIL("Set should not succeed with invalid flags");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+void tfm_its_test_common_003(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_3;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const size_t data_len = 0;
+
+    /* Set with NULL data pointer */
+    status = psa_its_set(uid, data_len, NULL, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should succeed with NULL data pointer and zero length");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+void tfm_its_test_common_004(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = WRITE_ONCE_UID;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const size_t write_len = WRITE_DATA_SIZE;
+    const size_t read_len = WRITE_ONCE_DATA_SIZE;
+    const size_t offset = 0;
+    const uint8_t write_data[] = WRITE_DATA;
+    uint8_t read_data[] = WRITE_ONCE_READ_DATA;
+    size_t read_data_length = 0;
+    int comp_result;
+
+    /* Set a write once UID a second time */
+    status = psa_its_set(uid, write_len, write_data, flags);
+    if (status != PSA_ERROR_NOT_PERMITTED) {
+        TEST_FAIL("Set should not rewrite a write once UID");
+        return;
+    }
+
+    /* Get write once data */
+    status = psa_its_get(uid, offset, read_len, read_data + HALF_PADDING_SIZE,
+                         &read_data_length);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Get should not fail");
+        return;
+    }
+
+#if DOMAIN_NS == 1U
+    /* Check that write once data has not changed */
+    comp_result = memcmp(read_data, WRITE_ONCE_RESULT_DATA, sizeof(read_data));
+#else
+    comp_result = tfm_memcmp(read_data, WRITE_ONCE_RESULT_DATA,
+                             sizeof(read_data));
+#endif
+    if (comp_result != 0) {
+        TEST_FAIL("Write once data should not have changed");
+        return;
+    }
+
+    /* Check that write once data length has not changed */
+    if (read_data_length != read_len) {
+        TEST_FAIL("Write once data length should not have changed");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+void tfm_its_test_common_005(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_2;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    size_t data_len = WRITE_DATA_SIZE;
+    size_t offset = 0;
+    const uint8_t write_data[] = WRITE_DATA;
+    uint8_t read_data[] = READ_DATA;
+    const uint8_t *p_read_data = read_data;
+    size_t read_data_length = 0;
+    int comp_result;
+
+    status = psa_its_set(uid, data_len, write_data, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should not fail");
+        return;
+    }
+
+    /* Get the entire data */
+    status = psa_its_get(uid, offset, data_len, read_data + HALF_PADDING_SIZE,
+                         &read_data_length);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Get should not fail");
+        return;
+    }
+
+#if DOMAIN_NS == 1U
+    /* Check that the data is correct, including no illegal pre- or post-data */
+    comp_result = memcmp(read_data, RESULT_DATA, sizeof(read_data));
+#else
+    comp_result = tfm_memcmp(read_data, RESULT_DATA, sizeof(read_data));
+#endif
+    if (comp_result != 0) {
+        TEST_FAIL("Read data should be equal to result data");
+        return;
+    }
+
+    /* Check that the length of data is correct */
+    if (read_data_length != data_len) {
+        TEST_FAIL("Read data length should be equal to requested data length");
+        return;
+    }
+
+#if DOMAIN_NS == 1U
+    /* Reset read data */
+    memcpy(read_data, READ_DATA, sizeof(read_data));
+#else
+    tfm_memcpy(read_data, READ_DATA, sizeof(read_data));
+#endif
+
+    /* Read from offset 2 to 2 bytes before end of the data */
+    offset = 2;
+    data_len -= offset + 2;
+
+    status = psa_its_get(uid, offset, data_len, read_data + HALF_PADDING_SIZE,
+                         &read_data_length);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Get should not fail");
+        return;
+    }
+
+#if DOMAIN_NS == 1U
+    /* Check that the correct data was read */
+    comp_result = memcmp(p_read_data, "____", HALF_PADDING_SIZE);
+#else
+    comp_result = tfm_memcmp(p_read_data, "____", HALF_PADDING_SIZE);
+#endif
+    if (comp_result != 0) {
+        TEST_FAIL("Read data contains illegal pre-data");
+        return;
+    }
+
+    p_read_data += HALF_PADDING_SIZE;
+
+#if DOMAIN_NS == 1U
+    comp_result = memcmp(p_read_data, write_data + offset, data_len);
+#else
+    comp_result = tfm_memcmp(p_read_data, write_data + offset, data_len);
+#endif
+    if (comp_result != 0) {
+        TEST_FAIL("Read data incorrect");
+        return;
+    }
+
+    p_read_data += data_len;
+
+#if DOMAIN_NS == 1U
+    comp_result = memcmp(p_read_data, "____", HALF_PADDING_SIZE);
+#else
+    comp_result = tfm_memcmp(p_read_data, "____", HALF_PADDING_SIZE);
+#endif
+    if (comp_result != 0) {
+        TEST_FAIL("Read data contains illegal post-data");
+        return;
+    }
+
+    /* Call remove to clean up storage for the next test */
+    status = psa_its_remove(uid);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Remove should not fail with valid UID");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+void tfm_its_test_common_006(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_3;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const size_t write_len = WRITE_DATA_SIZE;
+    const size_t read_len = 0;
+    size_t offset = 0;
+    const uint8_t write_data[] = WRITE_DATA;
+    uint8_t read_data[] = READ_DATA;
+    size_t read_data_length = 1;
+    int comp_result;
+
+    status = psa_its_set(uid, write_len, write_data, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should not fail");
+        return;
+    }
+
+    /* Get zero data from zero offset */
+    status = psa_its_get(uid, offset, read_len, read_data + HALF_PADDING_SIZE,
+                         &read_data_length);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Get should not fail with zero data len");
+        return;
+    }
+
+#if DOMAIN_NS == 1U
+    /* Check that the read data is unchanged */
+    comp_result = memcmp(read_data, READ_DATA, sizeof(read_data));
+#else
+    comp_result = tfm_memcmp(read_data, READ_DATA, sizeof(read_data));
+#endif
+    if (comp_result != 0) {
+        TEST_FAIL("Read data should be equal to original read data");
+        return;
+    }
+
+    /* Check that the read data length is zero */
+    if (read_data_length != 0) {
+        TEST_FAIL("Read data length should be equal to zero");
+        return;
+    }
+
+    offset = 5;
+    read_data_length = 1;
+
+    /* Get zero data from non-zero offset */
+    status = psa_its_get(uid, offset, read_len, read_data + HALF_PADDING_SIZE,
+                         &read_data_length);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Get should not fail");
+        return;
+    }
+
+#if DOMAIN_NS == 1U
+    /* Check that the read data is unchanged */
+    comp_result = memcmp(read_data, READ_DATA, sizeof(read_data));
+#else
+    comp_result = tfm_memcmp(read_data, READ_DATA, sizeof(read_data));
+#endif
+    if (comp_result != 0) {
+        TEST_FAIL("Read data should be equal to original read data");
+        return;
+    }
+
+    /* Check that the read data length is zero */
+    if (read_data_length != 0) {
+        TEST_FAIL("Read data length should be equal to zero");
+        return;
+    }
+
+    /* Call remove to clean up storage for the next test */
+    status = psa_its_remove(uid);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Remove should not fail with valid UID");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+void tfm_its_test_common_007(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_1;
+    const size_t data_len = 1;
+    const size_t offset = 0;
+    uint8_t read_data[] = READ_DATA;
+    size_t read_data_length = 0;
+    int comp_result;
+
+    /* Get with UID that has not yet been set */
+    status = psa_its_get(uid, offset, data_len, read_data + HALF_PADDING_SIZE,
+                         &read_data_length);
+    if (status != PSA_ERROR_DOES_NOT_EXIST) {
+        TEST_FAIL("Get succeeded with non-existant UID");
+        return;
+    }
+
+#if DOMAIN_NS == 1U
+    /* Check that the read data is unchanged */
+    comp_result = memcmp(read_data, READ_DATA, sizeof(read_data));
+#else
+    comp_result = tfm_memcmp(read_data, READ_DATA, sizeof(read_data));
+#endif
+    if (comp_result != 0) {
+        TEST_FAIL("Read data not equal to original read data");
+        return;
+    }
+
+    /* Get with invalid UID */
+    status = psa_its_get(INVALID_UID, offset, data_len,
+                         read_data + HALF_PADDING_SIZE, &read_data_length);
+    if (status != PSA_ERROR_INVALID_ARGUMENT) {
+        TEST_FAIL("Get succeeded with invalid UID");
+        return;
+    }
+
+#if DOMAIN_NS == 1U
+    /* Check that the read data is unchanged */
+    comp_result = memcmp(read_data, READ_DATA, sizeof(read_data));
+#else
+    comp_result = tfm_memcmp(read_data, READ_DATA, sizeof(read_data));
+#endif
+    if (comp_result != 0) {
+        TEST_FAIL("Read data not equal to original read data");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+void tfm_its_test_common_008(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_2;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const size_t write_len = WRITE_DATA_SIZE;
+    size_t read_len;
+    size_t offset;
+    const uint8_t write_data[] = WRITE_DATA;
+    uint8_t read_data[] = READ_DATA;
+    size_t read_data_length = 0;
+    int comp_result;
+
+    status = psa_its_set(uid, write_len, write_data, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should not fail");
+        return;
+    }
+
+    /* Get with offset greater than UID's length */
+    read_len = 1;
+    offset = write_len + 1;
+
+    status = psa_its_get(uid, offset, read_len, read_data + HALF_PADDING_SIZE,
+                         &read_data_length);
+    if (status != PSA_ERROR_INVALID_ARGUMENT) {
+        TEST_FAIL("Get should not succeed with offset too large");
+        return;
+    }
+
+#if DOMAIN_NS == 1U
+    /* Check that the read data is unchanged */
+    comp_result = memcmp(read_data, READ_DATA, sizeof(read_data));
+#else
+    comp_result = tfm_memcmp(read_data, READ_DATA, sizeof(read_data));
+#endif
+    if (comp_result != 0) {
+        TEST_FAIL("Read data should be equal to original read data");
+        return;
+    }
+
+    /* Get with data length greater than UID's length */
+    read_len = write_len + 1;
+    offset = 0;
+
+    status = psa_its_get(uid, offset, read_len, read_data + HALF_PADDING_SIZE,
+                         &read_data_length);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Get should succeed with data length larger than UID's "
+                  "length");
+        return;
+    }
+
+    if (read_data_length != write_len) {
+        TEST_FAIL("Read data length should be equal to UID's length");
+        return;
+    }
+
+#if DOMAIN_NS == 1U
+    /* Check that the read data is changed */
+    comp_result = memcmp(read_data, RESULT_DATA, sizeof(read_data));
+#else
+    comp_result = tfm_memcmp(read_data, RESULT_DATA, sizeof(read_data));
+#endif
+    if (comp_result != 0) {
+        TEST_FAIL("Read data should be equal to newly read data");
+        return;
+    }
+
+    /* Get with offset + data length greater than UID's length, but individually
+     * valid
+     */
+#if DOMAIN_NS == 1U
+    /* Reset read_data to original READ_DATA */
+    memcpy(read_data, READ_DATA, sizeof(read_data));
+#else
+    tfm_memcpy(read_data, READ_DATA, sizeof(read_data));
+#endif
+    read_len = write_len;
+    offset = 1;
+
+    status = psa_its_get(uid, offset, read_len,
+                         read_data + HALF_PADDING_SIZE,
+                         &read_data_length);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Get should succeed with offset + data length too large, "
+                  "but individually valid");
+        return;
+    }
+
+    if (read_data_length != write_len - offset) {
+        TEST_FAIL("Read data length should be equal to the UID's remaining "
+                  "size starting from offset");
+        return;
+    }
+
+#if DOMAIN_NS == 1U
+    /* Check that the read data is changed */
+    comp_result = memcmp(read_data, OFFSET_RESULT_DATA, sizeof(read_data));
+#else
+    comp_result = tfm_memcmp(read_data, OFFSET_RESULT_DATA, sizeof(read_data));
+#endif
+    if (comp_result != 0) {
+        TEST_FAIL("Read data should be equal to newly read data starting at "
+                  "offset");
+        return;
+    }
+
+    /* Call remove to clean up storage for the next test */
+    status = psa_its_remove(uid);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Remove should not fail with valid UID");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+void tfm_its_test_common_009(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_3;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const size_t data_len = WRITE_DATA_SIZE;
+    const size_t offset = 0;
+    const uint8_t write_data[] = WRITE_DATA;
+    size_t read_data_length = 0;
+
+    status = psa_its_set(uid, data_len, write_data, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should not fail");
+        return;
+    }
+
+    /* Get with NULL data pointer */
+    status = psa_its_get(uid, offset, 0, NULL, &read_data_length);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Get should succeed with NULL data pointer and zero length");
+        return;
+    }
+
+    /* Check that the read data length is unchanged */
+    if (read_data_length != 0) {
+        TEST_FAIL("Read data length should be 0 with NULL data pointer and "
+                  "zero length");
+        return;
+    }
+
+    /* Call remove to clean up storage for the next test */
+    status = psa_its_remove(uid);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Remove should not fail with valid UID");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+void tfm_its_test_common_010(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = WRITE_ONCE_UID;
+    struct psa_storage_info_t info = {0};
+
+    /* Get info for write once UID */
+    status = psa_its_get_info(uid, &info);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Get info should not fail for write once UID");
+        return;
+    }
+
+    /* Check that the info struct contains the correct values */
+    if (info.size != WRITE_ONCE_DATA_SIZE) {
+        TEST_FAIL("Size incorrect for write once UID");
+        return;
+    }
+
+    if (info.capacity != WRITE_ONCE_DATA_SIZE) {
+        TEST_FAIL("Capacity incorrect for write once UID");
+        return;
+    }
+
+    if (info.flags != PSA_STORAGE_FLAG_WRITE_ONCE) {
+        TEST_FAIL("Flags incorrect for write once UID");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+void tfm_its_test_common_011(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_1;
+    struct psa_storage_info_t info = {0};
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const size_t data_len = WRITE_DATA_SIZE;
+    const uint8_t write_data[] = WRITE_DATA;
+
+    status = psa_its_set(uid, data_len, write_data, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should not fail");
+        return;
+    }
+
+    /* Get info for valid UID */
+    status = psa_its_get_info(uid, &info);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Get info should not fail with valid UID");
+        return;
+    }
+
+    /* Check that the info struct contains the correct values */
+    if (info.size != data_len) {
+        TEST_FAIL("Size incorrect for valid UID");
+        return;
+    }
+
+    if (info.capacity != data_len) {
+        TEST_FAIL("Capacity incorrect for valid UID");
+        return;
+    }
+
+    if (info.flags != flags) {
+        TEST_FAIL("Flags incorrect for valid UID");
+        return;
+    }
+
+    /* Call remove to clean up storage for the next test */
+    status = psa_its_remove(uid);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Remove should not fail with valid UID");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+void tfm_its_test_common_012(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_2;
+    struct psa_storage_info_t info = {0};
+
+    /* Get info with UID that has not yet been set */
+    status = psa_its_get_info(uid, &info);
+    if (status != PSA_ERROR_DOES_NOT_EXIST) {
+        TEST_FAIL("Get info should not succeed with unset UID");
+        return;
+    }
+
+    /* Check that the info struct has not been modified */
+    if (info.size != 0) {
+        TEST_FAIL("Size should not have changed");
+        return;
+    }
+
+    /* Get info with invalid UID */
+    status = psa_its_get_info(INVALID_UID, &info);
+    if (status != PSA_ERROR_INVALID_ARGUMENT) {
+        TEST_FAIL("Get info should not succeed with invalid UID");
+        return;
+    }
+
+    /* Check that the info struct has not been modified */
+    if (info.size != 0) {
+        TEST_FAIL("Size should not have changed");
+        return;
+    }
+
+    if (info.capacity != 0) {
+        TEST_FAIL("Capacity should not have changed");
+        return;
+    }
+
+    if (info.flags != PSA_STORAGE_FLAG_NONE) {
+        TEST_FAIL("Flags should not have changed");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+void tfm_its_test_common_013(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_1;
+    struct psa_storage_info_t info = {0};
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const size_t data_len = WRITE_DATA_SIZE;
+    const size_t offset = 0;
+    const uint8_t write_data[] = WRITE_DATA;
+    uint8_t read_data[] = READ_DATA;
+    size_t read_data_length = 0;
+
+    status = psa_its_set(uid, data_len, write_data, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should not fail");
+        return;
+    }
+
+    /* Call remove with valid ID */
+    status = psa_its_remove(uid);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Remove should not fail with valid UID");
+        return;
+    }
+
+    /* Check that get info fails for removed UID */
+    status = psa_its_get_info(uid, &info);
+    if (status != PSA_ERROR_DOES_NOT_EXIST) {
+        TEST_FAIL("Get info should not succeed with removed UID");
+        return;
+    }
+
+    /* Check that get fails for removed UID */
+    status = psa_its_get(uid, offset, data_len, read_data, &read_data_length);
+    if (status != PSA_ERROR_DOES_NOT_EXIST) {
+        TEST_FAIL("Get should not succeed with removed UID");
+        return;
+    }
+
+    /* Check that remove fails for removed UID */
+    status = psa_its_remove(uid);
+    if (status != PSA_ERROR_DOES_NOT_EXIST) {
+        TEST_FAIL("Remove should not succeed with removed UID");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+void tfm_its_test_common_014(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = WRITE_ONCE_UID;
+
+    /* Call remove with write once UID */
+    status = psa_its_remove(uid);
+    if (status != PSA_ERROR_NOT_PERMITTED) {
+        TEST_FAIL("Remove should not succeed with write once UID");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+void tfm_its_test_common_015(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = INVALID_UID;
+
+    /* Call remove with an invalid UID */
+    status = psa_its_remove(uid);
+    if (status != PSA_ERROR_INVALID_ARGUMENT) {
+        TEST_FAIL("Remove should not succeed with invalid UID");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+void tfm_its_test_common_016(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid_1 = TEST_UID_2;
+    const psa_storage_uid_t uid_2 = TEST_UID_3;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const size_t data_len_2 = WRITE_DATA_SIZE;
+    const size_t offset = 0;
+    const uint8_t write_data_1[] = "UID 1 DATA";
+    const uint8_t write_data_2[] = WRITE_DATA;
+    uint8_t read_data[] = READ_DATA;
+    size_t read_data_length = 0;
+    int comp_result;
+
+    /* Set UID 1 */
+    status = psa_its_set(uid_1, sizeof(write_data_1), write_data_1, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should not fail for UID 1");
+        return;
+    }
+
+    /* Set UID 2 */
+    status = psa_its_set(uid_2, data_len_2, write_data_2, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should not fail for UID 2");
+        return;
+    }
+
+    /* Remove UID 1. This should cause UID 2 to be compacted to the beginning of
+     * the block.
+     */
+    status = psa_its_remove(uid_1);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Remove should not fail for UID 1");
+        return;
+    }
+
+    /* If the compact worked as expected, the test should be able to read back
+     * the data from UID 2 correctly.
+     */
+    status = psa_its_get(uid_2, offset, data_len_2,
+                        read_data + HALF_PADDING_SIZE, &read_data_length);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Get should not fail for UID 2");
+        return;
+    }
+
+#if DOMAIN_NS == 1U
+    comp_result = memcmp(read_data, RESULT_DATA, sizeof(read_data));
+#else
+    comp_result = tfm_memcmp(read_data, RESULT_DATA, sizeof(read_data));
+#endif
+    if (comp_result != 0) {
+        TEST_FAIL("Read buffer has incorrect data");
+        return;
+    }
+
+    if (read_data_length != WRITE_DATA_SIZE) {
+        TEST_FAIL("Read data length should be equal to result data length");
+        return;
+    }
+
+    /* Remove UID 2 to clean up storage for the next test */
+    status = psa_its_remove(uid_2);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Remove should not fail for UID 2");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+void tfm_its_test_common_017(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_1;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const size_t data_len = WRITE_DATA_SIZE;
+    size_t offset = 0;
+    const uint8_t write_data[] = WRITE_DATA;
+    uint8_t read_data[] = READ_DATA;
+    size_t read_data_length = 0;
+    int comp_result;
+
+    /* Set the entire data into UID */
+    status = psa_its_set(uid, data_len, write_data, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should not fail");
+        return;
+    }
+
+    /* Get the data from UID one byte at a time */
+    for (offset = 0; offset < data_len; ++offset) {
+        status = psa_its_get(uid, offset, 1,
+                             (read_data + HALF_PADDING_SIZE + offset),
+                             &read_data_length);
+        if (status != PSA_SUCCESS) {
+            TEST_FAIL("Get should not fail for partial read");
+            return;
+        }
+    }
+
+#if DOMAIN_NS == 1U
+    comp_result = memcmp(read_data, RESULT_DATA, sizeof(read_data));
+#else
+    comp_result = tfm_memcmp(read_data, RESULT_DATA, sizeof(read_data));
+#endif
+    if (comp_result != 0) {
+        TEST_FAIL("Read buffer has incorrect data");
+        return;
+    }
+
+    /* Remove UID to clean up storage for the next test */
+    status = psa_its_remove(uid);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Remove should not fail");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+void tfm_its_test_common_018(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_2;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const size_t offset = 0;
+    const uint8_t write_data_1[] = "ONE";
+    const uint8_t write_data_2[] = "TWO";
+    const uint8_t write_data_3[] = "THREE";
+    uint8_t read_data[] = READ_DATA;
+    size_t read_data_length = 0;
+    int comp_result;
+
+    /* Set write data 1 into UID */
+    status = psa_its_set(uid, sizeof(write_data_1), write_data_1, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("First set should not fail");
+        return;
+    }
+
+    /* Set write data 2 into UID */
+    status = psa_its_set(uid, sizeof(write_data_2), write_data_2, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Second set should not fail");
+        return;
+    }
+
+    /* Set write data 3 into UID */
+    status = psa_its_set(uid, sizeof(write_data_3), write_data_3, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Third set should not fail");
+        return;
+    }
+
+    status = psa_its_get(uid, offset, sizeof(write_data_3), read_data,
+                         &read_data_length);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Get should not fail");
+        return;
+    }
+
+#if DOMAIN_NS == 1U
+    /* Check that get returns the last data to be set */
+    comp_result = memcmp(read_data, write_data_3, sizeof(write_data_3));
+#else
+    comp_result = tfm_memcmp(read_data, write_data_3, sizeof(write_data_3));
+#endif
+    if (comp_result != 0) {
+        TEST_FAIL("Read buffer has incorrect data");
+        return;
+    }
+
+    if (read_data_length != sizeof(write_data_3)) {
+        TEST_FAIL("Read data length should be equal to result data length");
+        return;
+    }
+
+    /* Remove UID to clean up storage for the next test */
+    status = psa_its_remove(uid);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Remove should not fail");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+void tfm_its_test_common_019(struct test_result_t *ret)
+{
+    uint8_t cycle;
+    psa_status_t status;
+    int comp_result;
+    size_t read_data_length = 0;
+    const psa_storage_uid_t test_uid[TEST_019_CYCLES] = {
+        TEST_UID_1,
+        TEST_UID_2,
+        TEST_UID_3};
+    const size_t test_asset_sizes[TEST_019_CYCLES] = {
+        ITS_MAX_ASSET_SIZE >> 2,
+        ITS_MAX_ASSET_SIZE >> 1,
+        ITS_MAX_ASSET_SIZE};
+
+    /* Loop to test different asset sizes and UID's*/
+    for (cycle = 0; cycle < TEST_019_CYCLES; cycle++) {
+        size_t data_size = test_asset_sizes[cycle];
+        psa_storage_uid_t uid = test_uid[cycle];
+        struct psa_storage_info_t info = {0};
+
+#if DOMAIN_NS == 1U
+        memset(read_asset_data, 0x00, sizeof(read_asset_data));
+#else
+        tfm_memset(read_asset_data, 0x00, sizeof(read_asset_data));
+#endif
+
+        /* Set with data and no flags and a valid UID */
+        status = psa_its_set(uid,
+                             data_size,
+                             write_asset_data,
+                             PSA_STORAGE_FLAG_NONE);
+        if (status != PSA_SUCCESS) {
+            TEST_FAIL("Set should not fail with valid UID");
+            return;
+        }
+
+        /* Get info for valid UID */
+        status = psa_its_get_info(uid, &info);
+        if (status != PSA_SUCCESS) {
+            TEST_FAIL("Get info should not fail with valid UID");
+            return;
+        }
+
+        /* Check that the info struct contains the correct values */
+        if (info.size != data_size) {
+            TEST_FAIL("Size incorrect for valid UID");
+            return;
+        }
+
+        if (info.flags != PSA_STORAGE_FLAG_NONE) {
+            TEST_FAIL("Flags incorrect for valid UID");
+            return;
+        }
+
+        /* Check that thread can still get UID */
+        status = psa_its_get(uid, 0, data_size, read_asset_data,
+                             &read_data_length);
+        if (status != PSA_SUCCESS) {
+            TEST_FAIL("Get should not fail with valid UID");
+            return;
+        }
+
+#if DOMAIN_NS == 1U
+        /* Check that get returns the last data which was set */
+        comp_result = memcmp(read_asset_data, write_asset_data, data_size);
+#else
+        comp_result = tfm_memcmp(read_asset_data, write_asset_data, data_size);
+#endif
+        if (comp_result != 0) {
+            TEST_FAIL("Read data should be equal to original write data");
+            return;
+        }
+
+        if (read_data_length != data_size) {
+            TEST_FAIL("Read data length should be equal to result data length");
+            return;
+        }
+
+        /* Call remove to clean up storage for the next test */
+        status = psa_its_remove(uid);
+        if (status != PSA_SUCCESS) {
+            TEST_FAIL("Remove should not fail with valid UID");
+            return;
+        }
+    }
+
+    ret->val = TEST_PASSED;
+}
diff --git a/test/suites/its/its_tests_common.h b/test/suites/its/its_tests_common.h
new file mode 100644
index 0000000..f1b0845
--- /dev/null
+++ b/test/suites/its/its_tests_common.h
@@ -0,0 +1,237 @@
+/*
+ * Copyright (c) 2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __ITS_TESTS_COMMON_H__
+#define __ITS_TESTS_COMMON_H__
+
+#include "test/framework/test_framework_helpers.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Test UIDs */
+#define WRITE_ONCE_UID  1U /* Cannot be modified or deleted once created */
+#define TEST_UID_1      2U
+#define TEST_UID_2      3U
+#define TEST_UID_3      4U
+
+/* Invalid values */
+#define INVALID_UID              0U
+#define INVALID_DATA_LEN         UINT32_MAX
+#define INVALID_OFFSET           UINT32_MAX
+#define INVALID_FLAG             (1U << 31)
+
+/* Write once data */
+#define WRITE_ONCE_DATA          "THE_FIVE_BOXING_WIZARDS_JUMP_QUICKLY"
+#define WRITE_ONCE_DATA_SIZE     (sizeof(WRITE_ONCE_DATA) - 1)
+#define WRITE_ONCE_READ_DATA     "############################################"
+#define WRITE_ONCE_RESULT_DATA   ("####" WRITE_ONCE_DATA "####")
+
+#define WRITE_DATA               "THEQUICKBROWNFOXJUMPSOVERALAZYDOG"
+#define WRITE_DATA_SIZE          (sizeof(WRITE_DATA) - 1)
+#define READ_DATA                "_________________________________________"
+#define RESULT_DATA              ("____" WRITE_DATA "____")
+#define OFFSET_READ_DATA         "HEQUICKBROWNFOXJUMPSOVERALAZYDOG"
+#define OFFSET_RESULT_DATA       ("____" OFFSET_READ_DATA "_____")
+
+/**
+ * Several tests use a buffer to read back data from an asset. This buffer is
+ * larger than the size of the asset data by PADDING_SIZE bytes. This allows
+ * us to ensure that only the expected data is read back and that it is read
+ * back correctly.
+ *
+ * For example if the buffer and asset are as follows:
+ * Buffer - "XXXXXXXXXXXX", Asset data - "AAAA"
+ *
+ * Then a correct and successful read would give this result: "XXXXAAAAXXXX"
+ * (Assuming a PADDING_SIZE of 8)
+ */
+#define BUFFER_SIZE                   24
+#define PADDING_SIZE                  8
+#define HALF_PADDING_SIZE             4
+#define BUFFER_PLUS_PADDING_SIZE      (BUFFER_SIZE + PADDING_SIZE)
+#define BUFFER_PLUS_HALF_PADDING_SIZE (BUFFER_SIZE + HALF_PADDING_SIZE)
+
+/**
+ * \brief Tests set function with:
+ *        - Valid UID, no data, no flags
+ *        - Invalid UID, no data, no flags
+ *
+ * \param[out] ret  Test result
+ */
+void tfm_its_test_common_001(struct test_result_t *ret);
+
+/**
+ * \brief Tests set function with:
+ *        - Zero create flags
+ *        - Valid create flags (with previously created UID)
+ *        - Invalid create flags
+ *
+ * \param[out] ret  Test result
+ */
+void tfm_its_test_common_002(struct test_result_t *ret);
+
+/**
+ * \brief Tests set function with:
+ *        - NULL data pointer and zero data length
+ *
+ * \param[out] ret  Test result
+ *
+ * \note A request with a null data pointer and data length not equal to zero is
+ *       treated as a secure violation. TF-M framework will reject such requests
+ *       and not return to the caller so this case is not tested here.
+ *
+ */
+void tfm_its_test_common_003(struct test_result_t *ret);
+
+/**
+ * \brief Tests set function with:
+ *        - Write once UID that has already been created
+ *
+ * \param[out] ret  Test result
+ */
+void tfm_its_test_common_004(struct test_result_t *ret);
+
+/**
+ * \brief Tests get function with:
+ *        - Valid data, zero offset
+ *        - Valid data, non-zero offset
+ *
+ * \param[out] ret  Test result
+ */
+void tfm_its_test_common_005(struct test_result_t *ret);
+
+/**
+ * \brief Tests get function with:
+ *        - Zero data length, zero offset
+ *        - Zero data length, non-zero offset
+ *
+ * \param[out] ret  Test result
+ */
+void tfm_its_test_common_006(struct test_result_t *ret);
+
+/**
+ * \brief Tests get function with:
+ *        - Unset UID
+ *        - Invalid UID
+ *
+ * \param[out] ret  Test result
+ */
+void tfm_its_test_common_007(struct test_result_t *ret);
+
+/**
+ * \brief Tests get function with:
+ *        - Offset greater than UID length
+ *        - Data length greater than UID length
+ *        - Data length + offset greater than UID length
+ *
+ * \param[out] ret  Test result
+ */
+void tfm_its_test_common_008(struct test_result_t *ret);
+
+/**
+ * \brief Tests get function with:
+ *        - NULL data pointer and zero data length
+ *
+ * \param[out] ret  Test result
+ *
+ * \note A request with a null data pointer and data length not equal to zero is
+ *       treated as a secure violation. TF-M framework will reject such requests
+ *       and not return to the caller so this case is not tested here.
+ *
+ */
+void tfm_its_test_common_009(struct test_result_t *ret);
+
+/**
+ * \brief Tests get info function with:
+ *        - Write once UID
+ *
+ * \param[out] ret  Test result
+ */
+void tfm_its_test_common_010(struct test_result_t *ret);
+
+/**
+ * \brief Tests get info function with:
+ *        - Valid UID
+ *
+ * \param[out] ret  Test result
+ */
+void tfm_its_test_common_011(struct test_result_t *ret);
+
+/**
+ * \brief Tests get info function with:
+ *        - Unset UID
+ *        - Invalid UID
+ *
+ * \param[out] ret  Test result
+ */
+void tfm_its_test_common_012(struct test_result_t *ret);
+
+/**
+ * \brief Tests remove function with:
+ *        - Valid UID
+ *
+ * \param[out] ret  Test result
+ */
+void tfm_its_test_common_013(struct test_result_t *ret);
+
+/**
+ * \brief Tests remove function with:
+ *        - Write once UID
+ *
+ * \param[out] ret  Test result
+ */
+void tfm_its_test_common_014(struct test_result_t *ret);
+
+/**
+ * \brief Tests remove function with:
+ *        - Invalid UID
+ *
+ * \param[out] ret  Test result
+ */
+void tfm_its_test_common_015(struct test_result_t *ret);
+
+/**
+ * \brief Tests data block compact feature.
+ *        Set UID 1 to locate it at the beginning of the block. Then set UID 2
+ *        to be located after UID 1 and remove UID 1. UID 2 will be compacted to
+ *        the beginning of the block. This test verifies that the compaction
+ *        works correctly by reading back UID 2.
+ *
+ * \param[out] ret  Test result
+ */
+void tfm_its_test_common_016(struct test_result_t *ret);
+
+/**
+ * \brief Tests set and multiple partial gets.
+ *
+ * \param[out] ret  Test result
+ */
+void tfm_its_test_common_017(struct test_result_t *ret);
+
+/**
+ * \brief Tests multiple sets to the same UID.
+ *
+ * \param[out] ret  Test result
+ */
+void tfm_its_test_common_018(struct test_result_t *ret);
+
+/**
+ * \brief Tests set, get_info, get and remove function with:
+ *        - Valid UID's, No Flags
+ *        - Data length of different asset sizes
+ *
+ * \param[out] ret  Test result
+ */
+void tfm_its_test_common_019(struct test_result_t *ret);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ITS_TESTS_COMMON_H__ */
diff --git a/test/suites/its/non_secure/its_ns_tests.h b/test/suites/its/non_secure/its_ns_tests.h
new file mode 100644
index 0000000..534dd17
--- /dev/null
+++ b/test/suites/its/non_secure/its_ns_tests.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __ITS_NS_TESTS_H__
+#define __ITS_NS_TESTS_H__
+
+#include "test/framework/test_framework.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Register testsuite for the PSA internal trusted storage NS interface
+ *        tests.
+ *
+ * \param[in] p_test_suite  The test suite to be executed.
+ */
+void register_testsuite_ns_psa_its_interface(struct test_suite_t *p_test_suite);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ITS_NS_TESTS_H__ */
diff --git a/test/suites/its/non_secure/psa_its_ns_interface_testsuite.c b/test/suites/its/non_secure/psa_its_ns_interface_testsuite.c
new file mode 100644
index 0000000..3891600
--- /dev/null
+++ b/test/suites/its/non_secure/psa_its_ns_interface_testsuite.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "its_ns_tests.h"
+#include "test/framework/test_framework_helpers.h"
+#include "../its_tests_common.h"
+
+static struct test_t psa_its_ns_tests[] = {
+    {&tfm_its_test_common_001, "TFM_ITS_TEST_1001",
+     "Set interface"},
+    {&tfm_its_test_common_002, "TFM_ITS_TEST_1002",
+     "Set interface with create flags"},
+    {&tfm_its_test_common_003, "TFM_ITS_TEST_1003",
+     "Set interface with NULL data pointer"},
+    {&tfm_its_test_common_004, "TFM_ITS_TEST_1004",
+     "Set interface with write once UID"},
+    {&tfm_its_test_common_005, "TFM_ITS_TEST_1005",
+     "Get interface with valid data"},
+    {&tfm_its_test_common_006, "TFM_ITS_TEST_1006",
+     "Get interface with zero data length"},
+    {&tfm_its_test_common_007, "TFM_ITS_TEST_1007",
+     "Get interface with invalid UIDs"},
+    {&tfm_its_test_common_008, "TFM_ITS_TEST_1008",
+     "Get interface with invalid data lengths and offsets"},
+    {&tfm_its_test_common_009, "TFM_ITS_TEST_1009",
+     "Get interface with NULL data pointer"},
+    {&tfm_its_test_common_010, "TFM_ITS_TEST_1010",
+     "Get info interface with write once UID"},
+    {&tfm_its_test_common_011, "TFM_ITS_TEST_1011",
+     "Get info interface with valid UID"},
+    {&tfm_its_test_common_012, "TFM_ITS_TEST_1012",
+     "Get info interface with invalid UIDs"},
+    {&tfm_its_test_common_013, "TFM_ITS_TEST_1013",
+     "Remove interface with valid UID"},
+    {&tfm_its_test_common_014, "TFM_ITS_TEST_1014",
+     "Remove interface with write once UID"},
+    {&tfm_its_test_common_015, "TFM_ITS_TEST_1015",
+     "Remove interface with invalid UID"},
+    {&tfm_its_test_common_016, "TFM_ITS_TEST_1016",
+     "Block compaction after remove"},
+    {&tfm_its_test_common_017, "TFM_ITS_TEST_1017",
+     "Multiple partial gets"},
+    {&tfm_its_test_common_018, "TFM_ITS_TEST_1018",
+     "Multiple sets to same UID from same thread"},
+    {&tfm_its_test_common_019, "TFM_ITS_TEST_1019",
+     "Set, get and remove interface with different asset sizes"},
+};
+
+void register_testsuite_ns_psa_its_interface(struct test_suite_t *p_test_suite)
+{
+    uint32_t list_size;
+
+    list_size = (sizeof(psa_its_ns_tests) / sizeof(psa_its_ns_tests[0]));
+
+    set_testsuite("PSA internal trusted storage NS interface tests "
+                 "(TFM_ITS_TEST_1XXX)",
+                  psa_its_ns_tests, list_size, p_test_suite);
+}
diff --git a/test/suites/its/secure/its_s_tests.h b/test/suites/its/secure/its_s_tests.h
new file mode 100644
index 0000000..42d739f
--- /dev/null
+++ b/test/suites/its/secure/its_s_tests.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __ITS_S_TESTS_H__
+#define __ITS_S_TESTS_H__
+
+#include "test/framework/test_framework.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Register testsuite for the PSA internal trusted storage S interface
+ *        tests.
+ *
+ * \param[in] p_test_suite  The test suite to be executed.
+ */
+void register_testsuite_s_psa_its_interface(struct test_suite_t *p_test_suite);
+
+/**
+ * \brief Register testsuite for the ITS reliability tests.
+ *
+ * \param[in] p_test_suite  The test suite to be executed.
+ */
+void register_testsuite_s_psa_its_reliability(struct test_suite_t
+                                                                 *p_test_suite);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ITS_S_TESTS_H__ */
diff --git a/test/suites/its/secure/psa_its_s_interface_testsuite.c b/test/suites/its/secure/psa_its_s_interface_testsuite.c
new file mode 100644
index 0000000..187ee48
--- /dev/null
+++ b/test/suites/its/secure/psa_its_s_interface_testsuite.c
@@ -0,0 +1,274 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "its_s_tests.h"
+#include "psa/internal_trusted_storage.h"
+#include "test/framework/test_framework_helpers.h"
+#include "test/test_services/tfm_secure_client_2/tfm_secure_client_2_api.h"
+#include "../its_tests_common.h"
+#include "tfm_memory_utils.h"
+
+/* UID to test partition access control */
+#define TEST_UID_ACCESS_CONTROL 42U
+
+/* List of tests */
+static void tfm_its_test_2020(struct test_result_t *ret);
+static void tfm_its_test_2021(struct test_result_t *ret);
+static void tfm_its_test_2022(struct test_result_t *ret);
+static void tfm_its_test_2023(struct test_result_t *ret);
+
+static struct test_t psa_its_s_tests[] = {
+    {&tfm_its_test_common_001, "TFM_ITS_TEST_2001",
+     "Set interface"},
+    {&tfm_its_test_common_002, "TFM_ITS_TEST_2002",
+     "Set interface with create flags"},
+    {&tfm_its_test_common_003, "TFM_ITS_TEST_2003",
+     "Set interface with NULL data pointer"},
+    {&tfm_its_test_common_004, "TFM_ITS_TEST_2004",
+     "Set interface with write once UID"},
+    {&tfm_its_test_common_005, "TFM_ITS_TEST_2005",
+     "Get interface with valid data"},
+    {&tfm_its_test_common_006, "TFM_ITS_TEST_2006",
+     "Get interface with zero data length"},
+    {&tfm_its_test_common_007, "TFM_ITS_TEST_2007",
+     "Get interface with invalid UIDs"},
+    {&tfm_its_test_common_008, "TFM_ITS_TEST_2008",
+     "Get interface with data lengths and offsets greater than UID length"},
+    {&tfm_its_test_common_009, "TFM_ITS_TEST_2009",
+     "Get interface with NULL data pointer"},
+    {&tfm_its_test_common_010, "TFM_ITS_TEST_2010",
+     "Get info interface with write once UID"},
+    {&tfm_its_test_common_011, "TFM_ITS_TEST_2011",
+     "Get info interface with valid UID"},
+    {&tfm_its_test_common_012, "TFM_ITS_TEST_2012",
+     "Get info interface with invalid UIDs"},
+    {&tfm_its_test_common_013, "TFM_ITS_TEST_2013",
+     "Remove interface with valid UID"},
+    {&tfm_its_test_common_014, "TFM_ITS_TEST_2014",
+     "Remove interface with write once UID"},
+    {&tfm_its_test_common_015, "TFM_ITS_TEST_2015",
+     "Remove interface with invalid UID"},
+    {&tfm_its_test_common_016, "TFM_ITS_TEST_2016",
+     "Block compaction after remove"},
+    {&tfm_its_test_common_017, "TFM_ITS_TEST_2017",
+     "Multiple partial gets"},
+    {&tfm_its_test_common_018, "TFM_ITS_TEST_2018",
+     "Multiple sets to same UID from same thread"},
+    {&tfm_its_test_common_019, "TFM_ITS_TEST_2019",
+     "Set, get and remove interface with different asset sizes"},
+    {&tfm_its_test_2020, "TFM_ITS_TEST_2020",
+     "Set interface with invalid data length"},
+    {&tfm_its_test_2021, "TFM_ITS_TEST_2021",
+     "Get interface with invalid data lengths and offsets"},
+    {&tfm_its_test_2022, "TFM_ITS_TEST_2022",
+     "Get info interface with NULL info pointer"},
+    {&tfm_its_test_2023, "TFM_ITS_TEST_2023",
+     "Attempt to get a UID set by a different partition"},
+};
+
+void register_testsuite_s_psa_its_interface(struct test_suite_t *p_test_suite)
+{
+    uint32_t list_size;
+
+    list_size = (sizeof(psa_its_s_tests) / sizeof(psa_its_s_tests[0]));
+
+    set_testsuite("PSA internal trusted storage S interface tests "
+                  "(TFM_ITS_TEST_2XXX)",
+                  psa_its_s_tests, list_size, p_test_suite);
+}
+
+/**
+ * \brief Tests set function with:
+ *        - Data length longer than maximum permitted
+ *
+ * \param[out] ret  Test result
+ */
+static void tfm_its_test_2020(struct test_result_t *ret)
+{
+#ifndef TFM_PSA_API
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_1;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const size_t data_len = INVALID_DATA_LEN;
+    const uint8_t write_data[] = WRITE_DATA;
+
+    /* A parameter with a buffer pointer where its data length is longer than
+     * maximum permitted, it is treated as a secure violation.
+     * TF-M framework rejects the request with a proper error code.
+     * The ITS secure PSA implementation returns
+     * PSA_ERROR_INVALID_ARGUMENT in that case.
+     */
+
+    /* Set with data length longer than the maximum supported */
+    status = psa_its_set(uid, data_len, write_data, flags);
+    if (status != PSA_ERROR_INVALID_ARGUMENT) {
+        TEST_FAIL("Set should not succeed with invalid data length");
+        return;
+    }
+
+#endif
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests get function with:
+ *        - Invalid data len and offset
+ *        - NULL read data length pointer
+ *
+ * \param[out] ret  Test result
+ */
+static void tfm_its_test_2021(struct test_result_t *ret)
+{
+#ifndef TFM_PSA_API
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_2;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const size_t write_len = WRITE_DATA_SIZE;
+    size_t read_len;
+    size_t offset;
+    const uint8_t write_data[] = WRITE_DATA;
+    uint8_t read_data[] = READ_DATA;
+    size_t read_data_length = 0;
+
+    status = psa_its_set(uid, write_len, write_data, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should not fail");
+        return;
+    }
+
+    /* Get with data length and offset set to invalid values */
+    read_len = INVALID_DATA_LEN;
+    offset = INVALID_OFFSET;
+
+    /* A parameter with a buffer pointer where its data length is longer than
+     * maximum permitted, it is treated as a secure violation.
+     * TF-M framework rejects the request with a proper error code.
+     * The ITS secure PSA implementation returns
+     * PSA_ERROR_INVALID_ARGUMENT in that case.
+     */
+
+    status = psa_its_get(uid, offset, read_len, read_data + HALF_PADDING_SIZE,
+                         &read_data_length);
+    if (status != PSA_ERROR_INVALID_ARGUMENT) {
+        TEST_FAIL("Get should not succeed with invalid arguments");
+        return;
+    }
+
+    /* Check that the read data is unchanged */
+    if (tfm_memcmp(read_data, READ_DATA, sizeof(read_data)) != 0) {
+        TEST_FAIL("Read data should be equal to original read data");
+        return;
+    }
+
+    read_len = 1;
+    offset = 0;
+
+    status = psa_its_get(uid, offset, read_len, read_data + HALF_PADDING_SIZE,
+                         NULL);
+    if (status != PSA_ERROR_INVALID_ARGUMENT) {
+        TEST_FAIL("Get should not succeed with invalid arguments");
+        return;
+    }
+
+    /* Check that the read data is unchanged */
+    if (tfm_memcmp(read_data, READ_DATA, sizeof(read_data)) != 0) {
+        TEST_FAIL("Read data should be equal to original read data");
+        return;
+    }
+
+    /* Call remove to clean up storage for the next test */
+    status = psa_its_remove(uid);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Remove should not fail with valid UID");
+        return;
+    }
+
+#endif
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests get info function with:
+ *        - NULL info pointer
+ *
+ * \param[out] ret  Test result
+ */
+static void tfm_its_test_2022(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_3;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const size_t data_len = WRITE_DATA_SIZE;
+    const uint8_t write_data[] = WRITE_DATA;
+
+    status = psa_its_set(uid, data_len, write_data, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should not fail");
+        return;
+    }
+
+    /* A parameter with a null pointer is treated as a secure violation.
+     * TF-M framework rejects the request with a proper error code.
+     * The secure PSA ITS implementation returns
+     * PSA_ERROR_INVALID_ARGUMENT in that case.
+     */
+
+    /* Get info with NULL info pointer */
+#ifndef TFM_PSA_API
+    status = psa_its_get_info(uid, NULL);
+    if (status != PSA_ERROR_INVALID_ARGUMENT) {
+        TEST_FAIL("Get info should not succeed with NULL info pointer");
+        return;
+    }
+#endif
+
+    /* Call remove to clean up storage for the next test */
+    status = psa_its_remove(uid);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Remove should not fail with valid UID");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Attempt to get a UID set by a different partition.
+ *
+ * \param[out] ret  Test result
+ */
+static void tfm_its_test_2023(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_ACCESS_CONTROL;
+
+    /* Set the UID from this partition's context */
+    status = psa_its_set(uid, WRITE_DATA_SIZE, WRITE_DATA,
+                         PSA_STORAGE_FLAG_NONE);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should not fail");
+        return;
+    }
+
+    /* Attempt to get the UID from the Secure Client 2 partition */
+    status = tfm_secure_client_2_call_test(
+                                         TFM_SECURE_CLIENT_2_ID_ITS_ACCESS_CTRL,
+                                         &uid, sizeof(uid));
+    if (status != PSA_ERROR_DOES_NOT_EXIST) {
+        TEST_FAIL("Get should not succeed from a different partition");
+        return;
+    }
+
+    /* Call remove to clean up storage for the next test */
+    status = psa_its_remove(uid);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Remove should not fail with valid UID");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
diff --git a/test/suites/its/secure/psa_its_s_reliability_testsuite.c b/test/suites/its/secure/psa_its_s_reliability_testsuite.c
new file mode 100644
index 0000000..4528424
--- /dev/null
+++ b/test/suites/its/secure/psa_its_s_reliability_testsuite.c
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "its_s_tests.h"
+#include "psa/internal_trusted_storage.h"
+#include "test/framework/test_framework_helpers.h"
+#include "tfm_memory_utils.h"
+
+/* Test UIDs */
+#define TEST_UID 2UL  /* UID 1 cannot be used as it references a write once
+                       * asset, created in psa_its_s_interface_testsuite.c
+                       */
+
+/* Test suite defines */
+#define LOOP_ITERATIONS_001 15U
+#define LOOP_ITERATIONS_002 15U
+
+/* Write data */
+#define WRITE_DATA       "THEQUICKBROWNFOXJUMPSOVERALAZYDOG"
+#define WRITE_DATA_SIZE  (sizeof(WRITE_DATA) - 1)
+#define READ_DATA        "_________________________________________"
+#define RESULT_DATA      ("____" WRITE_DATA "____")
+
+/* Size of ____ from RESULT_DATA */
+#define HALF_PADDING_SIZE 4
+
+/* Define test suite for ITS reliability tests */
+/* List of tests */
+static void tfm_its_test_3001(struct test_result_t *ret);
+static void tfm_its_test_3002(struct test_result_t *ret);
+
+static struct test_t reliability_tests[] = {
+    {&tfm_its_test_3001, "TFM_ITS_TEST_3001",
+     "repetitive sets and gets in/from an asset", {TEST_PASSED} },
+    {&tfm_its_test_3002, "TFM_ITS_TEST_3002",
+     "repetitive sets, gets and removes", {TEST_PASSED} },
+};
+
+void register_testsuite_s_psa_its_reliability(struct test_suite_t *p_test_suite)
+{
+    uint32_t list_size = (sizeof(reliability_tests) /
+                          sizeof(reliability_tests[0]));
+
+    set_testsuite("ITS reliability tests (TFM_ITS_TEST_3XXX)",
+                  reliability_tests, list_size, p_test_suite);
+}
+
+/**
+ * \brief Tests repetitive sets and gets in/from an asset.
+ *
+ * \param[out] ret  Test result
+ */
+static void tfm_its_test_3001(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const size_t data_len = WRITE_DATA_SIZE;
+    const size_t offset = 0;
+    uint32_t itr;
+    const uint8_t write_data[] = WRITE_DATA;
+    uint8_t read_data[] = READ_DATA;
+    size_t read_data_length = 0;
+
+    for (itr = 0; itr < LOOP_ITERATIONS_001; itr++) {
+        TEST_LOG("  > Iteration %d of %d\r", itr + 1, LOOP_ITERATIONS_001);
+
+        /* Set a data in the asset */
+        status = psa_its_set(uid, data_len, write_data, flags);
+        if (status != PSA_SUCCESS) {
+            TEST_FAIL("Set should not fail with valid UID");
+            return;
+        }
+
+        /* Get data from the asset */
+        status = psa_its_get(uid, offset, data_len, (read_data +
+                                                    HALF_PADDING_SIZE),
+                                                    &read_data_length);
+        if (status != PSA_SUCCESS) {
+            TEST_FAIL("Get should not fail");
+            return;
+        }
+
+        /* Check that the data has not changed */
+        if (tfm_memcmp(read_data, RESULT_DATA, sizeof(read_data)) != 0) {
+            TEST_FAIL("The data should not have changed");
+            return;
+        }
+
+        /* Set the original data into read buffer */
+        tfm_memcpy(read_data, READ_DATA, sizeof(read_data));
+    }
+
+    TEST_LOG("\n");
+
+    /* Remove the asset to clean up storage for the next test */
+    status = psa_its_remove(uid);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Remove should not fail with valid UID");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests repetitive sets, gets and removes.
+ *
+ * \param[out] ret  Test result
+ */
+static void tfm_its_test_3002(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const size_t data_len = WRITE_DATA_SIZE;
+    const size_t offset = 0;
+    uint32_t itr;
+    const uint8_t write_data[] = WRITE_DATA;
+    uint8_t read_data[] = READ_DATA;
+    size_t read_data_length = 0;
+
+    for (itr = 0; itr < LOOP_ITERATIONS_002; itr++) {
+        TEST_LOG("  > Iteration %d of %d\r", itr + 1, LOOP_ITERATIONS_002);
+
+        /* Set a data in the asset */
+        status = psa_its_set(uid, data_len, write_data, flags);
+        if (status != PSA_SUCCESS) {
+            TEST_FAIL("Set should not fail with valid UID");
+            return;
+        }
+
+        /* Get data from the asset */
+        status = psa_its_get(uid, offset, data_len, (read_data +
+                                                    HALF_PADDING_SIZE),
+                                                    &read_data_length);
+        if (status != PSA_SUCCESS) {
+            TEST_FAIL("Get should not fail");
+            return;
+        }
+
+        /* Check that the data has not changed */
+        if (tfm_memcmp(read_data, RESULT_DATA, sizeof(read_data)) != 0) {
+            TEST_FAIL("The data should not have changed");
+            return;
+        }
+
+        /* Remove the asset from the secure storage */
+        status = psa_its_remove(uid);
+        if (status != PSA_SUCCESS) {
+            TEST_FAIL("Remove should not fail with valid UID");
+            return;
+        }
+
+        /* Set the original data into read buffer */
+        tfm_memcpy(read_data, READ_DATA, sizeof(read_data));
+    }
+
+    TEST_LOG("\n");
+
+    ret->val = TEST_PASSED;
+}
diff --git a/test/suites/multi_core/CMakeLists.inc b/test/suites/multi_core/CMakeLists.inc
new file mode 100644
index 0000000..521354a
--- /dev/null
+++ b/test/suites/multi_core/CMakeLists.inc
@@ -0,0 +1,31 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+#Definitions to compile the "multi-core test" module.
+#This file assumes it will be included from a project specific cmakefile, and
+#will not create a library or executable.
+#Inputs:
+#	TFM_ROOT_DIR - root directory of the TF-M repo.
+#
+#Outputs:
+#	Will modify include directories to make the source compile.
+#	ALL_SRC_C: C source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+#	ALL_SRC_CXX: C++ source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+#	ALL_SRC_ASM: assembly source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+#	Include directories will be modified by using the include_directories() commands as needed.
+
+#Get the current directory where this file is located.
+set(MULTI_CORE_TEST_DIR ${CMAKE_CURRENT_LIST_DIR})
+if(NOT DEFINED TFM_ROOT_DIR)
+	message(FATAL_ERROR "Please set TFM_ROOT_DIR before including this file.")
+endif()
+
+list(APPEND ALL_SRC_C_NS "${MULTI_CORE_TEST_DIR}/non_secure/multi_core_ns_interface_testsuite.c")
+
+#Setting include directories
+embedded_include_directories(PATH ${TFM_ROOT_DIR} ABSOLUTE)
+embedded_include_directories(PATH ${TFM_ROOT_DIR}/interface/include ABSOLUTE)
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
new file mode 100644
index 0000000..3b83487
--- /dev/null
+++ b/test/suites/multi_core/non_secure/multi_core_ns_interface_testsuite.c
@@ -0,0 +1,398 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include "os_wrapper/mutex.h"
+#include "os_wrapper/thread.h"
+#include "os_wrapper/tick.h"
+#include "psa/client.h"
+#include "psa/internal_trusted_storage.h"
+#include "psa_manifest/sid.h"
+#include "test/framework/test_framework_helpers.h"
+#include "tfm_ns_mailbox.h"
+
+#ifdef TFM_MULTI_CORE_MULTI_CLIENT_CALL
+/* Max number of child threads for multiple outstanding PSA client call test */
+#define NR_MULTI_CALL_CHILD                   (NUM_MAILBOX_QUEUE_SLOT * 2)
+#else
+#define NR_MULTI_CALL_CHILD                   0
+#endif
+
+/* 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
+#define MAX_NR_HEAVY_TEST_ROUND               0x20
+
+/* Default stack size for child thread */
+#define MULTI_CALL_LIGHT_TEST_STACK_SIZE      0x200
+#define MULTI_CALL_HEAVY_TEST_STACK_SIZE      0x300
+
+/* Test UID copied from ITS test cases */
+#define TEST_UID_1                            2U
+/* ITS data for multiple PSA client call heavy tests */
+#define ITS_DATA                               "ITSDataForMultiCore"
+#define ITS_DATA_LEN                           sizeof(ITS_DATA)
+
+/* 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 */
+    uint32_t nr_calls;              /* The number of PSA client calls */
+    void *mutex_handle;             /* Mutex to protect is_complete flag */
+    enum test_status_t ret;         /* The test result */
+    uint32_t total_ticks;           /* The total ticks cost to complete tests */
+    bool is_complete;               /* Whether current test thread completes */
+    bool is_parent;                 /* Whether executed in parent thread */
+};
+
+/* List of tests */
+static void multi_client_call_light_test(struct test_result_t *ret);
+static void multi_client_call_heavy_test(struct test_result_t *ret);
+static void multi_client_call_ooo_test(struct test_result_t *ret);
+
+static struct test_t multi_core_tests[] = {
+    {&multi_client_call_light_test,
+     "MULTI_CLIENT_CALL_LIGHT_TEST",
+     "Multiple outstanding NS PSA client calls lightweight test",
+     {TEST_PASSED}},
+    {&multi_client_call_heavy_test,
+     "MULTI_CLIENT_CALL_HEAVY_TEST",
+     "Multiple outstanding NS PSA client calls heavyweight test",
+     {TEST_PASSED}},
+    {&multi_client_call_ooo_test,
+     "MULTI_CLIENT_CALL_OOO_TEST",
+     "Multiple outstanding NS PSA client calls test with out-of-order calls",
+     {TEST_PASSED}},
+};
+
+void register_testsuite_multi_core_ns_interface(
+                                              struct test_suite_t *p_test_suite)
+{
+    uint32_t list_size;
+
+    if (!sizeof(multi_core_tests)) {
+        return;
+    }
+
+    list_size = (sizeof(multi_core_tests) / sizeof(multi_core_tests[0]));
+
+    set_testsuite("TF-M test cases for multi-core topology",
+                  multi_core_tests, list_size, p_test_suite);
+}
+
+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,
+                                   bool is_mixed)
+{
+    uint8_t i, nr_child;
+    void *current_thread_handle;
+    uint32_t current_thread_priority, err, total_ticks, total_calls, avg_ticks;
+    void *mutex_handle;
+    void *child_ids[NR_MULTI_CALL_CHILD];
+    struct ns_mailbox_stats_res_t stats_res;
+    struct test_params parent_params, params[NR_MULTI_CALL_CHILD];
+
+    tfm_ns_mailbox_tx_stats_init();
+
+    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,
+                                         &current_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,
+                                             &params[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);
+    if (!is_mixed) {
+        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;
+    }
+
+    total_ticks = parent_params.total_ticks;
+    total_calls = parent_params.nr_calls;
+
+    /* 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;
+        }
+
+        total_ticks += params[i].total_ticks;
+        total_calls += params[i].nr_calls;
+    }
+
+    tfm_ns_mailbox_stats_avg_slot(&stats_res);
+    TEST_LOG("Totally %d NS mailbox queue slots\r\n", NUM_MAILBOX_QUEUE_SLOT);
+    TEST_LOG("%d.%d NS mailbox queue slots are occupied each time in average.\r\n",
+             stats_res.avg_nr_slots, stats_res.avg_nr_slots_tenths);
+
+    TEST_LOG("Cost %d ticks totally\r\n", total_ticks);
+    avg_ticks = total_ticks / total_calls;
+    total_ticks %= total_calls;
+    TEST_LOG("Each PSA client call cost %d.%d ticks in average\r\n", avg_ticks,
+             total_ticks * 10 / total_calls);
+
+    ret->val = TEST_PASSED;
+}
+
+static inline
+enum test_status_t multi_client_call_light_loop(struct test_params *params)
+{
+    uint32_t i, version, total_ticks;
+    uint32_t nr_rounds = params->nr_rounds;
+
+    total_ticks = os_wrapper_get_tick();
+
+    for (i = 0; i < nr_rounds; i++) {
+        version = psa_framework_version();
+        if (version != PSA_FRAMEWORK_VERSION) {
+            TEST_LOG("Incorrect PSA framework version!\r\n");
+            return TEST_FAILED;
+        }
+    }
+
+    params->total_ticks = os_wrapper_get_tick() - total_ticks;
+    params->nr_calls = nr_rounds;
+
+    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);
+
+    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,
+                           false);
+}
+
+static inline
+enum test_status_t multi_client_call_heavy_loop(const psa_storage_uid_t uid,
+                                                struct test_params *params)
+{
+    uint32_t i, total_ticks, rounds = params->nr_rounds;
+    psa_status_t status;
+    size_t rd_data_len;
+    char rd_data[ITS_DATA_LEN];
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+
+    total_ticks = os_wrapper_get_tick();
+
+    for (i = 0; i < rounds; i++) {
+        /* Set a data in the asset */
+        status = psa_its_set(uid, ITS_DATA_LEN, ITS_DATA, flags);
+        if (status != PSA_SUCCESS) {
+            TEST_LOG("Fail to write ITS asset\r\n");
+            return TEST_FAILED;
+        }
+
+        /* Get data from the asset */
+        status = psa_its_get(uid, 0, ITS_DATA_LEN, rd_data, &rd_data_len);
+        if (status != PSA_SUCCESS) {
+            TEST_LOG("Fail to read ITS asset\r\n");
+            return TEST_FAILED;
+        }
+
+        /* Remove the asset to clean up storage */
+        status = psa_its_remove(uid);
+        if (status != PSA_SUCCESS) {
+            TEST_LOG("Fail to remove ITS asset\r\n");
+            return TEST_FAILED;
+        }
+    }
+
+    params->total_ticks = os_wrapper_get_tick() - total_ticks;
+    params->nr_calls = rounds * 3;
+
+    return TEST_PASSED;
+}
+
+static void multi_client_call_heavy_runner(void *argument)
+{
+    struct test_params *params = (struct test_params *)argument;
+    const psa_storage_uid_t uid = TEST_UID_1 + params->child_idx;
+
+    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_heavy_loop(uid, params);
+
+    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 NS interface to verify multiple outstanding PSA client calls feature
+ *        by calling heavyweight secure services.
+ */
+static void multi_client_call_heavy_test(struct test_result_t *ret)
+{
+    multi_client_call_test(ret, multi_client_call_heavy_runner,
+                           MULTI_CALL_HEAVY_TEST_STACK_SIZE,
+                           MAX_NR_HEAVY_TEST_ROUND,
+                           false);
+}
+
+static void multi_client_call_ooo_runner(void *argument)
+{
+    struct test_params *params = (struct test_params *)argument;
+    const psa_storage_uid_t uid = TEST_UID_1 + params->child_idx;
+
+    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);
+    }
+
+    if (!params->child_idx % 2) {
+        params->ret = multi_client_call_heavy_loop(uid, params);
+    } else {
+        params->nr_rounds *= 20;
+        params->ret = multi_client_call_light_loop(params);
+    }
+
+    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 Mix lightweight test and heavyweight test to verify multiple
+ *        outstanding PSA client calls feature with out-of-order calls.
+ */
+static void multi_client_call_ooo_test(struct test_result_t *ret)
+{
+    multi_client_call_test(ret, multi_client_call_ooo_runner,
+                           MULTI_CALL_HEAVY_TEST_STACK_SIZE,
+                           MAX_NR_HEAVY_TEST_ROUND,
+                           true);
+}
diff --git a/test/suites/multi_core/non_secure/multi_core_ns_test.h b/test/suites/multi_core/non_secure/multi_core_ns_test.h
new file mode 100644
index 0000000..44dda5d
--- /dev/null
+++ b/test/suites/multi_core/non_secure/multi_core_ns_test.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __MULTI_CORE_NS_TESTS_H__
+#define __MULTI_CORE_NS_TESTS_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "test/framework/test_framework.h"
+
+/**
+ * \brief Register testsuite for multi-core topology.
+ *
+ * \param[in] p_test_suite The test suite to be executed.
+ */
+void register_testsuite_multi_core_ns_interface(
+                                             struct test_suite_t *p_test_suite);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __MULTI_CORE_NS_TESTS_H__ */
diff --git a/test/suites/platform/CMakeLists.inc b/test/suites/platform/CMakeLists.inc
new file mode 100644
index 0000000..82d19ba
--- /dev/null
+++ b/test/suites/platform/CMakeLists.inc
@@ -0,0 +1,52 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2019, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+#Definitions to compile the "secure platform service test" module.
+#This file assumes it will be included from a project specific cmakefile, and
+#will not create a library or executable.
+#Inputs:
+#	TFM_ROOT_DIR - root directory of the TF-M repo.
+#
+#Outputs:
+#	Will modify include directories to make the source compile.
+#	ALL_SRC_C: C source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+#	ALL_SRC_CXX: C++ source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+#	ALL_SRC_ASM: assembly source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+#	Include directories will be modified by using the include_directories() commands as needed.
+
+#Get the current directory where this file is located.
+set(PLATFORM_TEST_DIR ${CMAKE_CURRENT_LIST_DIR})
+if(NOT DEFINED TFM_ROOT_DIR)
+	message(FATAL_ERROR "Please set TFM_ROOT_DIR before including this file.")
+endif()
+
+if (NOT DEFINED ENABLE_PLATFORM_SERVICE_TESTS)
+	message(FATAL_ERROR "Incomplete build configuration: ENABLE_PLATFORM_SERVICE_TESTS is undefined. ")
+elseif(ENABLE_PLATFORM_SERVICE_TESTS)
+	list(APPEND PLATFORM_TEST_SRC_S
+		"${PLATFORM_TEST_DIR}/secure/platform_s_interface_testsuite.c"
+	)
+
+	list(APPEND PLATFORM_TEST_SRC_NS
+		"${PLATFORM_TEST_DIR}/non_secure/platform_ns_interface_testsuite.c"
+	)
+
+	list(APPEND PLATFORM_TEST_SRC
+		"${PLATFORM_TEST_DIR}/platform_tests_common.c"
+	)
+
+	#Setting include directories
+	embedded_include_directories(PATH ${TFM_ROOT_DIR} ABSOLUTE)
+	embedded_include_directories(PATH ${TFM_ROOT_DIR}/interface/include ABSOLUTE)
+	embedded_include_directories(PATH ${PLATFORM_TEST_DIR} ABSOLUTE)
+
+	#Append all our source files to global lists.
+	list(APPEND ALL_SRC_C_S  ${PLATFORM_TEST_SRC_S}  ${PLATFORM_TEST_SRC})
+	list(APPEND ALL_SRC_C_NS ${PLATFORM_TEST_SRC_NS} ${PLATFORM_TEST_SRC})
+	unset(PLATFORM_TEST_SRC_S)
+	unset(PLATFORM_TEST_SRC_NS)
+endif()
diff --git a/test/suites/platform/non_secure/platform_ns_interface_testsuite.c b/test/suites/platform/non_secure/platform_ns_interface_testsuite.c
new file mode 100644
index 0000000..3d664bb
--- /dev/null
+++ b/test/suites/platform/non_secure/platform_ns_interface_testsuite.c
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "platform_ns_tests.h"
+#include "tfm_platform_api.h"
+#include "platform_tests_common.h"
+
+static struct test_t platform_interface_tests[] = {
+    {&tfm_platform_test_common_001, "TFM_PLATFORM_TEST_2001",
+     "Minimal platform service test", {TEST_PASSED} },
+};
+
+void
+register_testsuite_ns_platform_interface(struct test_suite_t *p_test_suite)
+{
+    uint32_t list_size;
+
+    list_size = (sizeof(platform_interface_tests) /
+                 sizeof(platform_interface_tests[0]));
+
+    set_testsuite("Platform Service Non-Secure interface tests"
+                  "(TFM_PLATFORM_TEST_2XXX)",
+                  platform_interface_tests, list_size, p_test_suite);
+}
diff --git a/test/suites/platform/non_secure/platform_ns_tests.h b/test/suites/platform/non_secure/platform_ns_tests.h
new file mode 100644
index 0000000..ac54aca
--- /dev/null
+++ b/test/suites/platform/non_secure/platform_ns_tests.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __PLATFORM_NS_TESTS_H__
+#define __PLATFORM_NS_TESTS_H__
+
+#include "test/framework/test_framework.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Register testsuite for the platform service.
+ *
+ * \param[in] p_test_suite The test suite to be executed.
+ */
+void
+register_testsuite_ns_platform_interface(struct test_suite_t *p_test_suite);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __PLATFORM_NS_TESTS_H__ */
diff --git a/test/suites/platform/platform_tests_common.c b/test/suites/platform/platform_tests_common.c
new file mode 100644
index 0000000..52d388a
--- /dev/null
+++ b/test/suites/platform/platform_tests_common.c
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "tfm_platform_api.h"
+#include "platform_tests_common.h"
+
+/*!
+ * \brief Call the platform service with an invalid request
+ */
+void tfm_platform_test_common_001(struct test_result_t *ret)
+{
+    int32_t err;
+
+    err = tfm_platform_ioctl((tfm_platform_ioctl_req_t) INVALID_REQUEST,
+                             NULL,
+                             NULL);
+    if (err != TFM_PLATFORM_ERR_NOT_SUPPORTED) {
+        TEST_FAIL("Call with invalid request should fail.");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
diff --git a/test/suites/platform/platform_tests_common.h b/test/suites/platform/platform_tests_common.h
new file mode 100644
index 0000000..348788c
--- /dev/null
+++ b/test/suites/platform/platform_tests_common.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __PLATFORM_TESTS_COMMON_H__
+#define __PLATFORM_TESTS_COMMON_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "test/framework/test_framework.h"
+
+#define INVALID_REQUEST 0xffffffff
+
+/*!
+ * \brief Call the platform service with an invalid request
+ *
+ * \param[out] ret  Test results
+ */
+void tfm_platform_test_common_001(struct test_result_t *ret);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __PLATFORM_TESTS_COMMON_H__ */
diff --git a/test/suites/platform/secure/platform_s_interface_testsuite.c b/test/suites/platform/secure/platform_s_interface_testsuite.c
new file mode 100644
index 0000000..3bd1872
--- /dev/null
+++ b/test/suites/platform/secure/platform_s_interface_testsuite.c
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "platform_s_tests.h"
+#include "tfm_platform_api.h"
+#include "platform_tests_common.h"
+
+static struct test_t platform_interface_tests[] = {
+    {&tfm_platform_test_common_001, "TFM_PLATFORM_TEST_1001",
+     "Minimal platform service test", {TEST_PASSED} },
+};
+
+void
+register_testsuite_s_platform_interface(struct test_suite_t *p_test_suite)
+{
+    uint32_t list_size;
+
+    list_size = (sizeof(platform_interface_tests) /
+                 sizeof(platform_interface_tests[0]));
+
+    set_testsuite("Platform Service Secure interface tests"
+                  "(TFM_PLATFORM_TEST_1XXX)",
+                  platform_interface_tests, list_size, p_test_suite);
+}
diff --git a/test/suites/platform/secure/platform_s_tests.h b/test/suites/platform/secure/platform_s_tests.h
new file mode 100644
index 0000000..2ca23dc
--- /dev/null
+++ b/test/suites/platform/secure/platform_s_tests.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __PLATFORM_S_TESTS_H__
+#define __PLATFORM_S_TESTS_H__
+
+#include "test/framework/test_framework.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Register testsuite for the platform service.
+ *
+ * \param[in] "p_test_suite" The test suite to be executed.
+ */
+void
+register_testsuite_s_platform_interface(struct test_suite_t *p_test_suite);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __PLATFORM_S_TESTS_H__ */
diff --git a/test/suites/ps/CMakeLists.inc b/test/suites/ps/CMakeLists.inc
new file mode 100644
index 0000000..41a29c9
--- /dev/null
+++ b/test/suites/ps/CMakeLists.inc
@@ -0,0 +1,52 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2017-2020, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+#Definitions to compile the "protected storage test" module.
+#This file assumes it will be included from a project specific cmakefile, and
+#will not create a library or executable.
+#Inputs:
+#	TFM_ROOT_DIR - root directory of the TF-M repo.
+#
+#Outputs:
+#	Will modify include directories to make the source compile.
+#	ALL_SRC_C: C source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+#	ALL_SRC_CXX: C++ source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+#	ALL_SRC_ASM: assembly source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+#	Include directories will be modified by using the include_directories() commands as needed.
+
+#Get the current directory where this file is located.
+set(PROTECTED_STORAGE_TEST_DIR ${CMAKE_CURRENT_LIST_DIR})
+if(NOT DEFINED TFM_ROOT_DIR)
+	message(FATAL_ERROR "Please set TFM_ROOT_DIR before including this file.")
+endif()
+
+if (NOT DEFINED ENABLE_PROTECTED_STORAGE_SERVICE_TESTS)
+	message(FATAL_ERROR "Incomplete build configuration: ENABLE_PROTECTED_STORAGE_SERVICE_TESTS is undefined. ")
+elseif (ENABLE_PROTECTED_STORAGE_SERVICE_TESTS)
+	list(APPEND ALL_SRC_C_NS "${PROTECTED_STORAGE_TEST_DIR}/non_secure/ns_test_helpers.c"
+				 "${PROTECTED_STORAGE_TEST_DIR}/non_secure/psa_ps_ns_interface_testsuite.c")
+
+	list(APPEND ALL_SRC_C_S "${PROTECTED_STORAGE_TEST_DIR}/secure/psa_ps_s_interface_testsuite.c"
+				"${PROTECTED_STORAGE_TEST_DIR}/secure/psa_ps_s_reliability_testsuite.c")
+
+	if (PS_ENCRYPTION AND PS_ROLLBACK_PROTECTION AND PS_TEST_NV_COUNTERS)
+		list(APPEND ALL_SRC_C_S "${PROTECTED_STORAGE_TEST_DIR}/secure/ps_rollback_protection_testsuite.c"
+					"${PROTECTED_STORAGE_TEST_DIR}/secure/nv_counters/test_ps_nv_counters.c")
+		set_property(SOURCE ${ALL_SRC_C_S} APPEND PROPERTY COMPILE_DEFINITIONS PS_TEST_NV_COUNTERS)
+	endif()
+
+	if (NOT DEFINED TFM_NS_CLIENT_IDENTIFICATION)
+		message(FATAL_ERROR "Incomplete build configuration: TFM_NS_CLIENT_IDENTIFICATION is undefined.")
+	elseif (TFM_NS_CLIENT_IDENTIFICATION)
+		set_property(SOURCE ${ALL_SRC_C_NS} APPEND PROPERTY COMPILE_DEFINITIONS TFM_NS_CLIENT_IDENTIFICATION)
+	endif()
+
+	#Setting include directories
+	embedded_include_directories(PATH ${TFM_ROOT_DIR} ABSOLUTE)
+	embedded_include_directories(PATH ${TFM_ROOT_DIR}/test/interface/include ABSOLUTE)
+	embedded_include_directories(PATH ${TFM_ROOT_DIR}/interface/include ABSOLUTE)
+endif()
diff --git a/test/suites/ps/non_secure/ns_test_helpers.c b/test/suites/ps/non_secure/ns_test_helpers.c
new file mode 100644
index 0000000..149b767
--- /dev/null
+++ b/test/suites/ps/non_secure/ns_test_helpers.c
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "ns_test_helpers.h"
+
+#include "os_wrapper/thread.h"
+#include "os_wrapper/semaphore.h"
+
+#include "tfm_nspm_api.h"
+
+#define PS_TEST_TASK_STACK_SIZE (768)
+
+struct test_task_t {
+    test_func_t *func;
+    struct test_result_t *ret;
+};
+
+static void *test_semaphore;
+
+/**
+ * \brief Executes the supplied test task and then releases the test semaphore.
+ *
+ * \param[in,out] arg  Pointer to the test task. Must be a pointer to a
+ *                     struct test_task_t
+ */
+static void test_task_runner(void *arg)
+{
+    struct test_task_t *test = arg;
+
+#ifdef TFM_NS_CLIENT_IDENTIFICATION
+    tfm_nspm_register_client_id();
+#endif /* TFM_NS_CLIENT_IDENTIFICATION */
+
+    /* Call the test function */
+    test->func(test->ret);
+
+    /* Release the semaphore to unblock the parent thread */
+    os_wrapper_semaphore_release(test_semaphore);
+
+    /* Signal to the RTOS that the thread is finished */
+    os_wrapper_thread_exit();
+}
+
+void tfm_ps_run_test(const char *thread_name, struct test_result_t *ret,
+                     test_func_t *test_func)
+{
+    void *current_thread_handle;
+    uint32_t current_thread_priority;
+    uint32_t err;
+    void *thread;
+    struct test_task_t test_task = { .func = test_func, .ret = ret };
+
+    /* Create a binary semaphore with initial count of 0 tokens available */
+    test_semaphore = os_wrapper_semaphore_create(1, 0, "ps_tests_sema");
+    if (!test_semaphore) {
+        TEST_FAIL("Semaphore creation failed");
+        return;
+    }
+
+    current_thread_handle = os_wrapper_thread_get_handle();
+    if (!current_thread_handle) {
+        os_wrapper_semaphore_delete(test_semaphore);
+        TEST_FAIL("Failed to get current thread ID");
+        return;
+    }
+
+    err = os_wrapper_thread_get_priority(current_thread_handle,
+                                         &current_thread_priority);
+    if (err == OS_WRAPPER_ERROR) {
+        os_wrapper_semaphore_delete(test_semaphore);
+        TEST_FAIL("Failed to get current thread priority");
+        return;
+    }
+
+    /* Start test thread */
+    thread = os_wrapper_thread_new(thread_name, PS_TEST_TASK_STACK_SIZE,
+                                   test_task_runner, &test_task,
+                                   current_thread_priority);
+    if (!thread) {
+        os_wrapper_semaphore_delete(test_semaphore);
+        TEST_FAIL("Failed to create test thread");
+        return;
+    }
+
+    /* Signal semaphore, wait indefinitely until unblocked by child thread */
+    err = os_wrapper_semaphore_acquire(test_semaphore, OS_WRAPPER_WAIT_FOREVER);
+
+    /* At this point, it means the binary semaphore has been released by the
+     * test and re-acquired by this thread, so just finally release it and
+     * delete it
+     */
+    os_wrapper_semaphore_release(test_semaphore);
+
+    os_wrapper_semaphore_delete(test_semaphore);
+}
diff --git a/test/suites/ps/non_secure/ns_test_helpers.h b/test/suites/ps/non_secure/ns_test_helpers.h
new file mode 100644
index 0000000..22226f4
--- /dev/null
+++ b/test/suites/ps/non_secure/ns_test_helpers.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __NS_TEST_HELPERS_H__
+#define __NS_TEST_HELPERS_H__
+
+#include "test/framework/test_framework.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define CONCAT_(x, y) x ## y
+#define CONCAT(x, y) CONCAT_(x, y)
+
+/**
+ * Several tests use a buffer to read back data from an asset. This buffer is
+ * larger than the size of the asset data by PADDING_SIZE bytes. This allows
+ * us to ensure that the only the expected data is read back and that it is read
+ * back correctly.
+ *
+ * For example if the buffer and asset are as follows:
+ * Buffer - "XXXXXXXXXXXX", Asset data - "AAAA"
+ *
+ * Then a correct and successful read would give this result: "XXXXAAAAXXXX"
+ * (Assuming a PADDING_SIZE of 8)
+ */
+#define BUFFER_SIZE 24
+#define PADDING_SIZE 8
+#define HALF_PADDING_SIZE 4
+
+#define BUFFER_PLUS_PADDING_SIZE (BUFFER_SIZE + PADDING_SIZE)
+#define BUFFER_PLUS_HALF_PADDING_SIZE (BUFFER_SIZE + HALF_PADDING_SIZE)
+
+/**
+ * \brief Expands to the prototype of a test function.
+ *
+ * \param[in] test_name  Name of the test function
+ */
+#define TFM_PS_TEST_PROTO(test_name) \
+    static void test_name(struct test_result_t *ret)
+
+/**
+ * \brief Expands to the standard name of a test function.
+ *
+ * \param[in] test_num  Identification number of the test
+ */
+#define TFM_PS_TEST_NAME(test_num) CONCAT(tfm_ps_test_, test_num)
+
+/**
+ * \brief Expands to the standard name of a task function.
+ *
+ * \param[in] test_num  Identification number of the task
+ */
+#define TFM_PS_TASK_NAME(test_num) CONCAT(TFM_PS_TEST_NAME(test_num), _task)
+
+/**
+ * \brief Expands to a test function declaration.
+ *
+ * \param[in] test_num  Identification number of the test
+ */
+#define TFM_PS_TEST(test_num) TFM_PS_TEST_PROTO(TFM_PS_TEST_NAME(test_num))
+
+/**
+ * \brief Expands to a task function declaration.
+ *
+ * \param[in] test_num  Identification number of the task
+ */
+#define TFM_PS_TASK(test_num) TFM_PS_TEST_PROTO(TFM_PS_TASK_NAME(test_num))
+
+/**
+ * \brief Defines a single-threaded PS NS test function and declares the
+ *        corresponding task function.
+ *
+ * \param[in] test_num     Identification number of the test
+ * \param[in] thread_name  Name of the thread in which to run the test
+ */
+#define TFM_PS_NS_TEST(test_num, thread_name)                           \
+    TFM_PS_TASK(test_num);                                              \
+    TFM_PS_TEST(test_num)                                               \
+    {                                                                    \
+        tfm_ps_run_test(thread_name, ret, TFM_PS_TASK_NAME(test_num)); \
+    }                                                                    \
+    TFM_PS_TASK(test_num)
+
+/* The type of a test function */
+typedef void test_func_t(struct test_result_t *ret);
+
+/**
+ * \brief Executes the given test function from the specified thread context.
+ *
+ * \param[in]  thread_name  Name of the thread to be created for test
+ * \param[out] ret          Result of the test
+ * \param[in]  test_func    Test function to be run in the new thread
+ */
+void tfm_ps_run_test(const char *thread_name, struct test_result_t *ret,
+                     test_func_t *test_func);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __NS_TEST_HELPERS_H__ */
diff --git a/test/suites/ps/non_secure/ps_ns_tests.h b/test/suites/ps/non_secure/ps_ns_tests.h
new file mode 100644
index 0000000..7153827
--- /dev/null
+++ b/test/suites/ps/non_secure/ps_ns_tests.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2017-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __PS_NS_TESTS_H__
+#define __PS_NS_TESTS_H__
+
+#include "test/framework/test_framework.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Register testsuite for the PSA protected storage NS interface tests.
+ *
+ * \param[in] p_test_suite  The test suite to be executed.
+ */
+void register_testsuite_ns_psa_ps_interface(struct test_suite_t *p_test_suite);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __PS_NS_TESTS_H__ */
diff --git a/test/suites/ps/non_secure/psa_ps_ns_interface_testsuite.c b/test/suites/ps/non_secure/psa_ps_ns_interface_testsuite.c
new file mode 100644
index 0000000..638ab8e
--- /dev/null
+++ b/test/suites/ps/non_secure/psa_ps_ns_interface_testsuite.c
@@ -0,0 +1,1726 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "ps_ns_tests.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include "ns_test_helpers.h"
+#include "psa/protected_storage.h"
+#include "test/framework/test_framework_helpers.h"
+#include "flash_layout.h"
+
+/* Test UIDs */
+#define WRITE_ONCE_UID  1U /* Cannot be modified or deleted once created */
+#define TEST_UID_1      2U
+#define TEST_UID_2      3U
+#define TEST_UID_3      4U
+
+/* Invalid values */
+#define INVALID_UID              0U
+#define INVALID_DATA_LEN         UINT32_MAX
+#define INVALID_OFFSET           UINT32_MAX
+#define INVALID_FLAG             (1U << 31)
+#define INVALID_THREAD_NAME      "Thread_INVALID"
+
+/* Write once data */
+#define WRITE_ONCE_DATA          "THE_FIVE_BOXING_WIZARDS_JUMP_QUICKLY"
+#define WRITE_ONCE_DATA_SIZE     (sizeof(WRITE_ONCE_DATA) - 1)
+#define WRITE_ONCE_READ_DATA     "############################################"
+#define WRITE_ONCE_RESULT_DATA   ("####" WRITE_ONCE_DATA "####")
+#define OFFSET_READ_DATA         "HEQUICKBROWNFOXJUMPSOVERALAZYDOG"
+#define OFFSET_RESULT_DATA       ("____" OFFSET_READ_DATA "_____")
+
+#define WRITE_DATA               "THEQUICKBROWNFOXJUMPSOVERALAZYDOG"
+#define WRITE_DATA_SIZE          (sizeof(WRITE_DATA) - 1)
+#define READ_DATA                "_________________________________________"
+#define RESULT_DATA              ("____" WRITE_DATA "____")
+
+#define TEST_1025_CYCLES         3U
+
+static const uint8_t write_asset_data[PS_MAX_ASSET_SIZE] = {0xAF};
+static uint8_t read_asset_data[PS_MAX_ASSET_SIZE] = {0};
+static size_t read_asset_data_len = 0;
+
+/* List of tests */
+static void tfm_ps_test_1001(struct test_result_t *ret);
+static void tfm_ps_test_1002(struct test_result_t *ret);
+static void tfm_ps_test_1003(struct test_result_t *ret);
+static void tfm_ps_test_1004(struct test_result_t *ret);
+static void tfm_ps_test_1005(struct test_result_t *ret);
+static void tfm_ps_test_1006(struct test_result_t *ret);
+static void tfm_ps_test_1007(struct test_result_t *ret);
+static void tfm_ps_test_1008(struct test_result_t *ret);
+static void tfm_ps_test_1009(struct test_result_t *ret);
+static void tfm_ps_test_1010(struct test_result_t *ret);
+static void tfm_ps_test_1011(struct test_result_t *ret);
+static void tfm_ps_test_1012(struct test_result_t *ret);
+static void tfm_ps_test_1013(struct test_result_t *ret);
+static void tfm_ps_test_1014(struct test_result_t *ret);
+static void tfm_ps_test_1015(struct test_result_t *ret);
+#ifdef TFM_NS_CLIENT_IDENTIFICATION
+static void tfm_ps_test_1016(struct test_result_t *ret);
+static void tfm_ps_test_1017(struct test_result_t *ret);
+static void tfm_ps_test_1018(struct test_result_t *ret);
+static void tfm_ps_test_1019(struct test_result_t *ret);
+static void tfm_ps_test_1020(struct test_result_t *ret);
+#endif /* TFM_NS_CLIENT_IDENTIFICATION */
+static void tfm_ps_test_1021(struct test_result_t *ret);
+static void tfm_ps_test_1022(struct test_result_t *ret);
+static void tfm_ps_test_1023(struct test_result_t *ret);
+static void tfm_ps_test_1024(struct test_result_t *ret);
+static void tfm_ps_test_1025(struct test_result_t *ret);
+
+static struct test_t psa_ps_ns_tests[] = {
+    {&tfm_ps_test_1001, "TFM_PS_TEST_1001",
+     "Set interface"},
+    {&tfm_ps_test_1002, "TFM_PS_TEST_1002",
+     "Set interface with create flags"},
+    {&tfm_ps_test_1003, "TFM_PS_TEST_1003",
+     "Set interface with NULL data pointer"},
+    {&tfm_ps_test_1004, "TFM_PS_TEST_1004",
+     "Set interface with write once UID"},
+    {&tfm_ps_test_1005, "TFM_PS_TEST_1005",
+     "Get interface with valid data"},
+    {&tfm_ps_test_1006, "TFM_PS_TEST_1006",
+     "Get interface with zero data length"},
+    {&tfm_ps_test_1007, "TFM_PS_TEST_1007",
+     "Get interface with invalid UIDs"},
+    {&tfm_ps_test_1008, "TFM_PS_TEST_1008",
+     "Get interface with invalid data lengths and offsets"},
+    {&tfm_ps_test_1009, "TFM_PS_TEST_1009",
+     "Get interface with NULL data pointer"},
+    {&tfm_ps_test_1010, "TFM_PS_TEST_1010",
+     "Get info interface with write once UID"},
+    {&tfm_ps_test_1011, "TFM_PS_TEST_1011",
+     "Get info interface with valid UID"},
+    {&tfm_ps_test_1012, "TFM_PS_TEST_1012",
+     "Get info interface with invalid UIDs"},
+    {&tfm_ps_test_1013, "TFM_PS_TEST_1013",
+     "Remove interface with valid UID"},
+    {&tfm_ps_test_1014, "TFM_PS_TEST_1014",
+     "Remove interface with write once UID"},
+    {&tfm_ps_test_1015, "TFM_PS_TEST_1015",
+     "Remove interface with invalid UID"},
+#ifdef TFM_NS_CLIENT_IDENTIFICATION
+    {&tfm_ps_test_1016, "TFM_PS_TEST_1016",
+     "Get interface with invalid thread name"},
+    {&tfm_ps_test_1017, "TFM_PS_TEST_1017",
+     "Get info interface with invalid thread name"},
+    {&tfm_ps_test_1018, "TFM_PS_TEST_1018",
+     "Remove interface with invalid thread name"},
+    {&tfm_ps_test_1019, "TFM_PS_TEST_1019",
+     "Attempt to access UID belonging to another thread"},
+    {&tfm_ps_test_1020, "TFM_PS_TEST_1020",
+     "Set UID alternately from two threads"},
+#endif /* TFM_NS_CLIENT_IDENTIFICATION */
+    {&tfm_ps_test_1021, "TFM_PS_TEST_1021",
+     "Block compaction after remove"},
+    {&tfm_ps_test_1022, "TFM_PS_TEST_1022",
+     "Multiple partial gets"},
+    {&tfm_ps_test_1023, "TFM_PS_TEST_1023",
+     "Multiple sets to same UID from same thread"},
+    {&tfm_ps_test_1024, "TFM_PS_TEST_1024",
+     "Get support interface"},
+    {&tfm_ps_test_1025, "TFM_PS_TEST_1025",
+     "Set, get and remove interface with different asset sizes"},
+};
+
+void register_testsuite_ns_psa_ps_interface(struct test_suite_t *p_test_suite)
+{
+    uint32_t list_size;
+
+    list_size = (sizeof(psa_ps_ns_tests) / sizeof(psa_ps_ns_tests[0]));
+
+    set_testsuite("PSA protected storage NS interface tests (TFM_PS_TEST_1XXX)",
+                  psa_ps_ns_tests, list_size, p_test_suite);
+}
+
+/**
+ * \brief Tests set function with:
+ * - Valid UID, no data, no flags
+ * - Invalid UID, no data, no flags
+ */
+TFM_PS_NS_TEST(1001, "Thread_A")
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_1;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const uint32_t data_len = 0;
+    const uint8_t write_data[] = {0};
+
+    /* Set with no data and no flags and a valid UID */
+    status = psa_ps_set(uid, data_len, write_data, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should not fail with valid UID");
+        return;
+    }
+
+    /* Attempt to set a second time */
+    status = psa_ps_set(uid, data_len, write_data, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should not fail the second time with valid UID");
+        return;
+    }
+
+    /* Set with an invalid UID */
+    status = psa_ps_set(INVALID_UID, data_len, write_data, flags);
+    if (status != PSA_ERROR_INVALID_ARGUMENT) {
+        TEST_FAIL("Set should not succeed with an invalid UID");
+        return;
+    }
+
+    /* Call remove to clean up storage for the next test */
+    status = psa_ps_remove(uid);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Remove should not fail with valid UID");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests set function with:
+ * - Zero create flags
+ * - Valid create flags (with previously created UID)
+ * - Invalid create flags
+ */
+TFM_PS_NS_TEST(1002, "Thread_A")
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_2;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const uint32_t data_len = WRITE_DATA_SIZE;
+    const uint8_t write_data[] = WRITE_DATA;
+
+    /* Set with no flags */
+    status = psa_ps_set(WRITE_ONCE_UID, data_len, write_data, flags);
+    if (status == PSA_SUCCESS) {
+        /* Set with valid flag: PSA_STORAGE_FLAG_WRITE_ONCE (with previously
+         * created UID)
+         * Note: Once created, WRITE_ONCE_UID cannot be deleted. It is reused
+         * across multiple tests.
+         */
+        status = psa_ps_set(WRITE_ONCE_UID, WRITE_ONCE_DATA_SIZE,
+                            WRITE_ONCE_DATA, PSA_STORAGE_FLAG_WRITE_ONCE);
+        if (status != PSA_SUCCESS) {
+            TEST_FAIL("Set should not fail with valid flags (existing UID)");
+            return;
+        }
+    } else if (status == PSA_ERROR_NOT_PERMITTED) {
+        /* The UID has already been created with the PSA_STORAGE_FLAG_WRITE_ONCE
+         * flag in a previous test run, so skip creating it again and emit a
+         * warning.
+         */
+        TEST_LOG("Note: The UID in this test has already been created with\r\n"
+                 "the PSA_STORAGE_FLAG_WRITE_ONCE flag in a previous test\r\n"
+                 "run. Wipe the storage area to run the full test.\r\n");
+    } else {
+        TEST_FAIL("Set should not fail with no flags");
+        return;
+    }
+
+    /* Set with invalid flags */
+    status = psa_ps_set(uid, data_len, write_data, INVALID_FLAG);
+    if (status != PSA_ERROR_NOT_SUPPORTED) {
+        TEST_FAIL("Set should not succeed with invalid flags");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests set function with:
+ * - NULL data pointer and zero data length
+ *
+ * \note A request with a null data pointer and data length not equal to zero is
+ *       treated as a secure violation. TF-M framework will reject such requests
+ *       and not return to the NSPE so this case is not tested here.
+ *
+ */
+TFM_PS_NS_TEST(1003, "Thread_A")
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_3;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const uint32_t data_len = 0;
+
+    /* Set with NULL data pointer */
+    status = psa_ps_set(uid, data_len, NULL, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should succeed with NULL data pointer and zero length");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests set function with:
+ * - Write once UID that has already been created
+ */
+TFM_PS_NS_TEST(1004, "Thread_A")
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = WRITE_ONCE_UID;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const uint32_t write_len = WRITE_DATA_SIZE;
+    const uint32_t read_len = WRITE_ONCE_DATA_SIZE;
+    const uint32_t offset = 0;
+    const uint8_t write_data[] = WRITE_DATA;
+    uint8_t read_data[] = WRITE_ONCE_READ_DATA;
+    size_t read_data_len = 0;
+
+    /* Set a write once UID a second time */
+    status = psa_ps_set(uid, write_len, write_data, flags);
+    if (status != PSA_ERROR_NOT_PERMITTED) {
+        TEST_FAIL("Set should not rewrite a write once UID");
+        return;
+    }
+
+    /* Get write once data */
+    status = psa_ps_get(uid, offset, read_len, read_data + HALF_PADDING_SIZE,
+                        &read_data_len);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Get should not fail");
+        return;
+    }
+
+    /* Check that write once data has not changed */
+    if (memcmp(read_data, WRITE_ONCE_RESULT_DATA, sizeof(read_data)) != 0) {
+        TEST_FAIL("Write once data should not have changed");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests get function with:
+ * - Valid data, zero offset
+ * - Valid data, non-zero offset
+ */
+TFM_PS_NS_TEST(1005, "Thread_A")
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_2;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    uint32_t data_len = WRITE_DATA_SIZE;
+    uint32_t offset = 0;
+    const uint8_t write_data[] = WRITE_DATA;
+    uint8_t read_data[] = READ_DATA;
+    size_t read_data_len = 0;
+
+    const uint8_t *p_read_data = read_data;
+
+    status = psa_ps_set(uid, data_len, write_data, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should not fail");
+        return;
+    }
+
+    /* Get the entire data */
+    status = psa_ps_get(uid, offset, data_len, read_data + HALF_PADDING_SIZE,
+                        &read_data_len);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Get should not fail");
+        return;
+    }
+
+    /* Check that the data is correct, including no illegal pre- or post-data */
+    if (memcmp(read_data, RESULT_DATA, sizeof(read_data)) != 0) {
+        TEST_FAIL("Read data should be equal to result data");
+        return;
+    }
+
+    /* Reset read data */
+    memcpy(read_data, READ_DATA, sizeof(read_data));
+
+    /* Read from offset 2 to 2 bytes before end of the data */
+    offset = 2;
+    data_len -= offset + 2;
+
+    status = psa_ps_get(uid, offset, data_len, read_data + HALF_PADDING_SIZE,
+                        &read_data_len);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Get should not fail");
+        return;
+    }
+
+    /* Check that the correct data was read */
+    if (memcmp(p_read_data, "____", HALF_PADDING_SIZE) != 0) {
+        TEST_FAIL("Read data contains illegal pre-data");
+        return;
+    }
+
+    p_read_data += HALF_PADDING_SIZE;
+
+    if (memcmp(p_read_data, write_data + offset, data_len) != 0) {
+        TEST_FAIL("Read data incorrect");
+        return;
+    }
+
+    p_read_data += data_len;
+
+    if (memcmp(p_read_data, "____", HALF_PADDING_SIZE) != 0) {
+        TEST_FAIL("Read data contains illegal post-data");
+        return;
+    }
+
+    /* Call remove to clean up storage for the next test */
+    status = psa_ps_remove(uid);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Remove should not fail with valid UID");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests get function with:
+ * - Zero data length, zero offset
+ * - Zero data length, non-zero offset
+ */
+TFM_PS_NS_TEST(1006, "Thread_A")
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_3;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const uint32_t write_len = WRITE_DATA_SIZE;
+    const uint32_t read_len = 0;
+    uint32_t offset = 0;
+    const uint8_t write_data[] = WRITE_DATA;
+    uint8_t read_data[] = READ_DATA;
+    size_t read_data_len = 0;
+
+    status = psa_ps_set(uid, write_len, write_data, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should not fail");
+        return;
+    }
+
+    /* Get zero data from zero offset */
+    status = psa_ps_get(uid, offset, read_len, read_data + HALF_PADDING_SIZE,
+                        &read_data_len);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Get should not fail with zero data len");
+        return;
+    }
+
+    /* Check that the read data is unchanged */
+    if (memcmp(read_data, READ_DATA, sizeof(read_data)) != 0) {
+        TEST_FAIL("Read data should be equal to original read data");
+        return;
+    }
+
+    offset = 5;
+
+    /* Get zero data from non-zero offset */
+    status = psa_ps_get(uid, offset, read_len, read_data + HALF_PADDING_SIZE,
+                        &read_data_len);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Get should not fail");
+        return;
+    }
+
+    /* Check that the read data is unchanged */
+    if (memcmp(read_data, READ_DATA, sizeof(read_data)) != 0) {
+        TEST_FAIL("Read data should be equal to original read data");
+        return;
+    }
+
+    /* Call remove to clean up storage for the next test */
+    status = psa_ps_remove(uid);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Remove should not fail with valid UID");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests get function with:
+ * - Unset UID
+ * - Invalid UID
+ */
+TFM_PS_NS_TEST(1007, "Thread_A")
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_1;
+    const uint32_t data_len = 1;
+    const uint32_t offset = 0;
+    uint8_t read_data[] = READ_DATA;
+    size_t read_data_len = 0;
+
+    /* Get with UID that has not yet been set */
+    status = psa_ps_get(uid, offset, data_len, read_data + HALF_PADDING_SIZE,
+                        &read_data_len);
+    if (status != PSA_ERROR_DOES_NOT_EXIST) {
+        TEST_FAIL("Get succeeded with non-existant UID");
+        return;
+    }
+
+    /* Check that the read data is unchanged */
+    if (memcmp(read_data, READ_DATA, sizeof(read_data)) != 0) {
+        TEST_FAIL("Read data not equal to original read data");
+        return;
+    }
+
+    /* Get with invalid UID */
+    status = psa_ps_get(INVALID_UID, offset, data_len,
+                        read_data + HALF_PADDING_SIZE, &read_data_len);
+    if (status != PSA_ERROR_INVALID_ARGUMENT) {
+        TEST_FAIL("Get succeeded with invalid UID");
+        return;
+    }
+
+    /* Check that the read data is unchanged */
+    if (memcmp(read_data, READ_DATA, sizeof(read_data)) != 0) {
+        TEST_FAIL("Read data not equal to original read data");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests get function with:
+ * - Offset greater than UID length
+ * - Data length greater than UID length
+ * - Data length + offset greater than UID length
+ */
+TFM_PS_NS_TEST(1008, "Thread_A")
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_2;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const uint32_t write_len = WRITE_DATA_SIZE;
+    uint32_t read_len;
+    uint32_t offset;
+    const uint8_t write_data[] = WRITE_DATA;
+    uint8_t read_data[] = READ_DATA;
+    size_t read_data_len = 0;
+
+    status = psa_ps_set(uid, write_len, write_data, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should not fail");
+        return;
+    }
+
+    /* Get with offset greater than UID's length */
+    read_len = 1;
+    offset = write_len + 1;
+
+    status = psa_ps_get(uid, offset, read_len, read_data + HALF_PADDING_SIZE,
+                        &read_data_len);
+    if (status != PSA_ERROR_INVALID_ARGUMENT) {
+        TEST_FAIL("Get should not succeed with offset too large");
+        return;
+    }
+
+    /* Check that the read data is unchanged */
+    if (memcmp(read_data, READ_DATA, sizeof(read_data)) != 0) {
+        TEST_FAIL("Read data should be equal to original read data");
+        return;
+    }
+
+    /* Get with data length greater than UID's length */
+    read_len = write_len + 1;
+    offset = 0;
+
+    status = psa_ps_get(uid, offset, read_len, read_data + HALF_PADDING_SIZE,
+                        &read_data_len);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Get should succeed with data length larger than UID's "
+                  "length");
+        return;
+    }
+
+    if (read_data_len != write_len) {
+        TEST_FAIL("Read data length should be equal to UID's length");
+        return;
+    }
+
+    /* Check that the read data is unchanged */
+    if (memcmp(read_data, RESULT_DATA, sizeof(read_data)) != 0) {
+        TEST_FAIL("Read data should be equal to original read data");
+        return;
+    }
+
+    /* Get with offset + data length greater than UID's length, but individually
+     * valid
+     */
+    read_len = write_len;
+    offset = 1;
+
+    /* Reset read_data to original READ_DATA */
+    memcpy(read_data, READ_DATA, sizeof(read_data));
+
+    status = psa_ps_get(uid, offset, read_len, read_data + HALF_PADDING_SIZE,
+                        &read_data_len);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Get should succeed with offset + data length too large, "
+                  "but individually valid");
+        return;
+    }
+
+    if (read_data_len != write_len - offset) {
+        TEST_FAIL("Read data length should be equal to the UID's remaining "
+                  "size starting from offset");
+        return;
+    }
+
+    /* Check that the read data is unchanged */
+    if (memcmp(read_data, OFFSET_RESULT_DATA, sizeof(read_data)) != 0) {
+        TEST_FAIL("Read data should be equal to original read data");
+        return;
+    }
+
+    /* Call remove to clean up storage for the next test */
+    status = psa_ps_remove(uid);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Remove should not fail with valid UID");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests get function with:
+ * - NULL data pointer and zero data length
+ *
+ * \note A request with a null data pointer and data length not equal to zero is
+ *       treated as a secure violation. TF-M framework will reject such requests
+ *       and not return to the NSPE so this case is not tested here.
+ *
+ */
+TFM_PS_NS_TEST(1009, "Thread_A")
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_3;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const uint32_t data_len = WRITE_DATA_SIZE;
+    const uint32_t offset = 0;
+    const uint8_t write_data[] = WRITE_DATA;
+    size_t read_data_length = 0;
+
+    status = psa_ps_set(uid, data_len, write_data, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should not fail");
+        return;
+    }
+
+    /* Get with NULL data pointer */
+    status = psa_ps_get(uid, offset, 0, NULL, &read_data_length);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Get should succeed with NULL data pointer and zero length");
+        return;
+    }
+
+    /* Call remove to clean up storage for the next test */
+    status = psa_ps_remove(uid);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Remove should not fail with valid UID");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests get info function with:
+ * - Write once UID
+ */
+TFM_PS_NS_TEST(1010, "Thread_A")
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = WRITE_ONCE_UID;
+    struct psa_storage_info_t info = {0};
+
+    /* Get info for write once UID */
+    status = psa_ps_get_info(uid, &info);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Get info should not fail for write once UID");
+        return;
+    }
+
+    /* Check that the info struct contains the correct values */
+    if (info.size != WRITE_ONCE_DATA_SIZE) {
+        TEST_FAIL("Size incorrect for write once UID");
+        return;
+    }
+
+    if (info.flags != PSA_STORAGE_FLAG_WRITE_ONCE) {
+        TEST_FAIL("Flags incorrect for write once UID");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests get info function with:
+ * - Valid UID
+ */
+TFM_PS_NS_TEST(1011, "Thread_A")
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_1;
+    struct psa_storage_info_t info = {0};
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const uint32_t data_len = WRITE_DATA_SIZE;
+    const uint8_t write_data[] = WRITE_DATA;
+
+    status = psa_ps_set(uid, data_len, write_data, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should not fail");
+        return;
+    }
+
+    /* Get info for valid UID */
+    status = psa_ps_get_info(uid, &info);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Get info should not fail with valid UID");
+        return;
+    }
+
+    /* Check that the info struct contains the correct values */
+    if (info.size != data_len) {
+        TEST_FAIL("Size incorrect for valid UID");
+        return;
+    }
+
+    if (info.flags != flags) {
+        TEST_FAIL("Flags incorrect for valid UID");
+        return;
+    }
+
+    /* Call remove to clean up storage for the next test */
+    status = psa_ps_remove(uid);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Remove should not fail with valid UID");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests get info function with:
+ * - Unset UID
+ * - Invalid UID
+ */
+TFM_PS_NS_TEST(1012, "Thread_A")
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_2;
+    struct psa_storage_info_t info = {0};
+
+    /* Get info with UID that has not yet been set */
+    status = psa_ps_get_info(uid, &info);
+    if (status != PSA_ERROR_DOES_NOT_EXIST) {
+        TEST_FAIL("Get info should not succeed with unset UID");
+        return;
+    }
+
+    /* Check that the info struct has not been modified */
+    if (info.size != 0) {
+        TEST_FAIL("Size should not have changed");
+        return;
+    }
+
+    if (info.flags != 0) {
+        TEST_FAIL("Flags should not have changed");
+        return;
+    }
+
+    /* Get info with invalid UID */
+    status = psa_ps_get_info(INVALID_UID, &info);
+    if (status != PSA_ERROR_INVALID_ARGUMENT) {
+        TEST_FAIL("Get info should not succeed with invalid UID");
+        return;
+    }
+
+    /* Check that the info struct has not been modified */
+    if (info.size != 0) {
+        TEST_FAIL("Size should not have changed");
+        return;
+    }
+
+    if (info.flags != 0) {
+        TEST_FAIL("Flags should not have changed");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests remove function with:
+ * - Valid UID
+ */
+TFM_PS_NS_TEST(1013, "Thread_A")
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_1;
+    struct psa_storage_info_t info = {0};
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const uint32_t data_len = WRITE_DATA_SIZE;
+    const uint32_t offset = 0;
+    const uint8_t write_data[] = WRITE_DATA;
+    uint8_t read_data[] = READ_DATA;
+    size_t read_data_len = 0;
+
+    status = psa_ps_set(uid, data_len, write_data, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should not fail");
+        return;
+    }
+
+    /* Call remove with valid ID */
+    status = psa_ps_remove(uid);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Remove should not fail with valid UID");
+        return;
+    }
+
+    /* Check that get info fails for removed UID */
+    status = psa_ps_get_info(uid, &info);
+    if (status != PSA_ERROR_DOES_NOT_EXIST) {
+        TEST_FAIL("Get info should not succeed with removed UID");
+        return;
+    }
+
+    /* Check that get fails for removed UID */
+    status = psa_ps_get(uid, offset, data_len, read_data, &read_data_len);
+    if (status != PSA_ERROR_DOES_NOT_EXIST) {
+        TEST_FAIL("Get should not succeed with removed UID");
+        return;
+    }
+
+    /* Check that remove fails for removed UID */
+    status = psa_ps_remove(uid);
+    if (status != PSA_ERROR_DOES_NOT_EXIST) {
+        TEST_FAIL("Remove should not succeed with removed UID");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests remove function with:
+ * - Write once UID
+ */
+TFM_PS_NS_TEST(1014, "Thread_A")
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = WRITE_ONCE_UID;
+
+    /* Call remove with write once UID */
+    status = psa_ps_remove(uid);
+    if (status != PSA_ERROR_NOT_PERMITTED) {
+        TEST_FAIL("Remove should not succeed with write once UID");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests remove function with:
+ * - Invalid UID
+ */
+TFM_PS_NS_TEST(1015, "Thread_A")
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = INVALID_UID;
+
+    /* Call remove with an invalid UID */
+    status = psa_ps_remove(uid);
+    if (status != PSA_ERROR_INVALID_ARGUMENT) {
+        TEST_FAIL("Remove should not succeed with invalid UID");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+#ifdef TFM_NS_CLIENT_IDENTIFICATION
+/**
+ * \brief Sets UID with a valid thread name.
+ */
+static void tfm_ps_test_1016_task_1(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_3;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const uint32_t data_len = WRITE_DATA_SIZE;
+    const uint8_t write_data[] = WRITE_DATA;
+
+    status = psa_ps_set(uid, data_len, write_data, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should not fail with valid thread name");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Calls get with an invalid thread name.
+ */
+static void tfm_ps_test_1016_task_2(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_3;
+    const uint32_t data_len = WRITE_DATA_SIZE;
+    const uint32_t offset = 0;
+    uint8_t read_data[] = READ_DATA;
+    size_t read_data_len = 0;
+
+    status = psa_ps_get(uid, offset, data_len, read_data, &read_data_len);
+    if (status != PSA_ERROR_DOES_NOT_EXIST) {
+        TEST_FAIL("Get should not succeed with invalid thread name");
+        return;
+    }
+
+    /* Check that read data has not been modified */
+    if (memcmp(read_data, READ_DATA, sizeof(read_data)) != 0) {
+        TEST_FAIL("Read data should not have changed");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Removes UID with a valid thread name to clean up storage.
+ */
+static void tfm_ps_test_1016_task_3(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_3;
+
+    status = psa_ps_remove(uid);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Remove should not fail with valid thread name");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests get function with an invalid thread name.
+ */
+static void tfm_ps_test_1016(struct test_result_t *ret)
+{
+    tfm_ps_run_test("Thread_A", ret, tfm_ps_test_1016_task_1);
+    if (ret->val != TEST_PASSED) {
+        return;
+    }
+
+    tfm_ps_run_test(INVALID_THREAD_NAME, ret, tfm_ps_test_1016_task_2);
+    if (ret->val != TEST_PASSED) {
+        return;
+    }
+
+    tfm_ps_run_test("Thread_A", ret, tfm_ps_test_1016_task_3);
+}
+
+/**
+ * \brief Sets UID with a valid thread name.
+ */
+static void tfm_ps_test_1017_task_1(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_1;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const uint32_t data_len = WRITE_DATA_SIZE;
+    const uint8_t write_data[] = WRITE_DATA;
+
+    status = psa_ps_set(uid, data_len, write_data, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should not fail with valid thread name");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Calls get info with an invalid thread name.
+ */
+static void tfm_ps_test_1017_task_2(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_1;
+    struct psa_storage_info_t info = {0};
+
+    status = psa_ps_get_info(uid, &info);
+    if (status != PSA_ERROR_DOES_NOT_EXIST) {
+        TEST_FAIL("Get info should not succeed with invalid thread name");
+        return;
+    }
+
+    /* Check that info has not been modified */
+    if (info.size != 0 || info.flags != 0) {
+        TEST_FAIL("Info should not have changed");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Removes UID with a valid thread name to clean up storage.
+ */
+static void tfm_ps_test_1017_task_3(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_1;
+
+    status = psa_ps_remove(uid);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Remove should not fail with valid thread name");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests get info function with an invalid thread name.
+ */
+static void tfm_ps_test_1017(struct test_result_t *ret)
+{
+    tfm_ps_run_test("Thread_A", ret, tfm_ps_test_1017_task_1);
+    if (ret->val != TEST_PASSED) {
+        return;
+    }
+
+    tfm_ps_run_test(INVALID_THREAD_NAME, ret, tfm_ps_test_1017_task_2);
+    if (ret->val != TEST_PASSED) {
+        return;
+    }
+
+    tfm_ps_run_test("Thread_A", ret, tfm_ps_test_1017_task_3);
+}
+
+/**
+ * \brief Sets UID with a valid thread name.
+ */
+static void tfm_ps_test_1018_task_1(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_2;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const uint32_t data_len = WRITE_DATA_SIZE;
+    const uint8_t write_data[] = WRITE_DATA;
+
+    status = psa_ps_set(uid, data_len, write_data, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should not fail with valid thread name");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Calls remove with an invalid thread name.
+ */
+static void tfm_ps_test_1018_task_2(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_2;
+
+    status = psa_ps_remove(uid);
+    if (status != PSA_ERROR_DOES_NOT_EXIST) {
+        TEST_FAIL("Remove should not succeed with invalid thread name");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Removes UID with a valid thread name to clean up storage.
+ */
+static void tfm_ps_test_1018_task_3(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_2;
+
+    status = psa_ps_remove(uid);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Remove should not fail with valid thread name");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests remove function with an invalid thread name.
+ */
+static void tfm_ps_test_1018(struct test_result_t *ret)
+{
+    tfm_ps_run_test("Thread_A", ret, tfm_ps_test_1018_task_1);
+    if (ret->val != TEST_PASSED) {
+        return;
+    }
+
+    tfm_ps_run_test(INVALID_THREAD_NAME, ret, tfm_ps_test_1018_task_2);
+    if (ret->val != TEST_PASSED) {
+        return;
+    }
+
+    tfm_ps_run_test("Thread_A", ret, tfm_ps_test_1018_task_3);
+}
+
+/**
+ * \brief Sets UID with first thread.
+ */
+static void tfm_ps_test_1019_task_1(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_3;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const uint8_t write_data[] = "Thread A data";
+
+    status = psa_ps_set(uid, sizeof(write_data), write_data, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should not fail with valid UID");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Attempts to access same UID from second thread.
+ */
+static void tfm_ps_test_1019_task_2(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_3;
+    struct psa_storage_info_t info = {0};
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const uint32_t data_len = WRITE_DATA_SIZE;
+    const uint32_t offset = 0;
+    const uint8_t write_data[] = WRITE_DATA;
+    uint8_t read_data[] = READ_DATA;
+    size_t read_data_len = 0;
+
+    /* Attempt to access the other thread's UID */
+    status = psa_ps_get(uid, offset, data_len, read_data, &read_data_len);
+    if (status != PSA_ERROR_DOES_NOT_EXIST) {
+        TEST_FAIL("Get should not find another thread's UID");
+        return;
+    }
+
+    /* Check that read data has not been modified */
+    if (memcmp(read_data, READ_DATA, sizeof(read_data)) != 0) {
+        TEST_FAIL("Read data should not have changed");
+        return;
+    }
+
+    status = psa_ps_get_info(uid, &info);
+    if (status != PSA_ERROR_DOES_NOT_EXIST) {
+        TEST_FAIL("Get info should not find another thread's UID");
+        return;
+    }
+
+    /* Check that info has not been modified */
+    if (info.size != 0 || info.flags != 0) {
+        TEST_FAIL("Info should not have changed");
+        return;
+    }
+
+    status = psa_ps_remove(uid);
+    if (status != PSA_ERROR_DOES_NOT_EXIST) {
+        TEST_FAIL("Remove should not find another thread's UID");
+        return;
+    }
+
+    /* Create the same UID, but belonging to this thread */
+    status = psa_ps_set(uid, data_len, write_data, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should not fail with valid UID");
+        return;
+    }
+
+    status = psa_ps_get(uid, offset, data_len, read_data + HALF_PADDING_SIZE,
+                        &read_data_len);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Get should not fail with valid UID");
+        return;
+    }
+
+    /* Check that the data read belongs to this thread, not the other one */
+    if (memcmp(read_data, RESULT_DATA, sizeof(read_data)) != 0) {
+        TEST_FAIL("Read data should be equal to result data");
+        return;
+    }
+
+    /* Call remove to clean up storage for the next test */
+    status = psa_ps_remove(uid);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Remove should not fail with valid UID");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Checks that first thread's UID has not been modified.
+ */
+static void tfm_ps_test_1019_task_3(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_3;
+    struct psa_storage_info_t info = {0};
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const uint32_t offset = 0;
+    const uint8_t write_data[] = "Thread A data";
+    uint8_t read_data[] = READ_DATA;
+    size_t read_data_len = 0;
+
+    const uint32_t data_len = sizeof(write_data);
+
+    /* Check that first thread can still get info for UID */
+    status = psa_ps_get_info(uid, &info);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Get info should not fail with valid UID");
+        return;
+    }
+
+    /* Check that first thread's UID info has not been modified */
+    if (info.size != data_len || info.flags != flags) {
+        TEST_FAIL("Info should be equal to original info");
+        return;
+    }
+
+    /* Check that first thread can still get UID */
+    status = psa_ps_get(uid, offset, data_len, read_data, &read_data_len);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Get should not fail with valid UID");
+        return;
+    }
+
+    /* Check that first thread's UID data has not been modified */
+    if (memcmp(read_data, write_data, data_len) != 0) {
+        TEST_FAIL("Read data should be equal to original write data");
+        return;
+    }
+
+    /* Call remove to clean up storage for the next test */
+    status = psa_ps_remove(uid);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Remove should not fail with valid UID");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests attempting to access UID belonging to another thread.
+ */
+static void tfm_ps_test_1019(struct test_result_t *ret)
+{
+    tfm_ps_run_test("Thread_A", ret, tfm_ps_test_1019_task_1);
+    if (ret->val != TEST_PASSED) {
+        return;
+    }
+
+    tfm_ps_run_test("Thread_B", ret, tfm_ps_test_1019_task_2);
+    if (ret->val != TEST_PASSED) {
+        return;
+    }
+
+    tfm_ps_run_test("Thread_A", ret, tfm_ps_test_1019_task_3);
+}
+
+/**
+ * \brief Sets TEST_UID_1 from Thread_A.
+ */
+static void tfm_ps_test_1020_task_1(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_1;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const uint8_t write_data[] = "A";
+
+    status = psa_ps_set(uid, sizeof(write_data), write_data, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should succeed for Thread_A");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Sets TEST_UID_1 from Thread_B.
+ */
+static void tfm_ps_test_1020_task_2(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_1;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const uint8_t write_data[] = "B";
+
+    status = psa_ps_set(uid, sizeof(write_data), write_data, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should succeed for Thread_B");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Sets TEST_UID_1 again from Thread_A.
+ */
+static void tfm_ps_test_1020_task_3(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_1;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const uint8_t write_data[] = "HELLO";
+
+    status = psa_ps_set(uid, sizeof(write_data), write_data, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Second set should succeed for Thread_A");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Sets TEST_UID_1 again from Thread_B.
+ */
+static void tfm_ps_test_1020_task_4(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_1;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const uint8_t write_data[] = "WORLD_1234";
+
+    status = psa_ps_set(uid, sizeof(write_data), write_data, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Second set should succeed for Thread_B");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Gets TEST_UID_1 from Thread_A.
+ */
+static void tfm_ps_test_1020_task_5(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_1;
+    const uint32_t offset = 0;
+    const uint8_t write_data[] = "HELLO";
+    uint8_t read_data[] = READ_DATA;
+    size_t read_data_len = 0;
+
+    status = psa_ps_get(uid, offset, sizeof(write_data), read_data,
+                        &read_data_len);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Get should succeed for Thread_A");
+        return;
+    }
+
+    /* Check that UID contains Thread_A's data */
+    if (memcmp(read_data, write_data, sizeof(write_data)) != 0) {
+        TEST_FAIL("Read data incorrect for Thread_A");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Gets TEST_UID_1 from Thread_B.
+ */
+static void tfm_ps_test_1020_task_6(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_1;
+    const uint32_t offset = 0;
+    const uint8_t write_data[] = "WORLD_1234";
+    uint8_t read_data[] = READ_DATA;
+    size_t read_data_len = 0;
+
+    status = psa_ps_get(uid, offset, sizeof(write_data), read_data,
+                        &read_data_len);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Get should succeed for Thread_B");
+        return;
+    }
+
+    /* Check that UID contains Thread_B's data */
+    if (memcmp(read_data, write_data, sizeof(write_data)) != 0) {
+        TEST_FAIL("Read data incorrect for Thread_B");
+        return;
+    }
+
+    /* Call remove to clean up storage for the next test */
+    status = psa_ps_remove(uid);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Remove should work form Thread_B");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Calls removes from Thread_B to clean up storage for the next test.
+ */
+static void tfm_ps_test_1020_task_7(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_1;
+
+    /* Call remove to clean up storage for the next test */
+    status = psa_ps_remove(uid);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Remove should work form Thread_B");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests writing data to a UID alternately from two threads before
+ *        read-back.
+ */
+static void tfm_ps_test_1020(struct test_result_t *ret)
+{
+    tfm_ps_run_test("Thread_A", ret, tfm_ps_test_1020_task_1);
+    if (ret->val != TEST_PASSED) {
+        return;
+    }
+
+    tfm_ps_run_test("Thread_B", ret, tfm_ps_test_1020_task_2);
+    if (ret->val != TEST_PASSED) {
+        return;
+    }
+
+    tfm_ps_run_test("Thread_A", ret, tfm_ps_test_1020_task_3);
+    if (ret->val != TEST_PASSED) {
+        return;
+    }
+
+    tfm_ps_run_test("Thread_B", ret, tfm_ps_test_1020_task_4);
+    if (ret->val != TEST_PASSED) {
+        return;
+    }
+
+    tfm_ps_run_test("Thread_A", ret, tfm_ps_test_1020_task_5);
+    if (ret->val != TEST_PASSED) {
+        return;
+    }
+
+    tfm_ps_run_test("Thread_B", ret, tfm_ps_test_1020_task_6);
+    if (ret->val != TEST_PASSED) {
+        return;
+    }
+
+    tfm_ps_run_test("Thread_A", ret, tfm_ps_test_1020_task_7);
+}
+#endif /* TFM_NS_CLIENT_IDENTIFICATION */
+
+/**
+ * \brief Tests data block compact feature.
+ *        Set UID 1 to locate it at the beginning of the block. Then set UID 2
+ *        to be located after UID 1 and remove UID 1. UID 2 will be compacted to
+ *        the beginning of the block. This test verifies that the compaction
+ *        works correctly by reading back UID 2.
+ */
+TFM_PS_NS_TEST(1021, "Thread_A")
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid_1 = TEST_UID_2;
+    const psa_storage_uid_t uid_2 = TEST_UID_3;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const uint32_t data_len_2 = WRITE_DATA_SIZE;
+    const uint32_t offset = 0;
+    const uint8_t write_data_1[] = "UID 1 DATA";
+    const uint8_t write_data_2[] = WRITE_DATA;
+    uint8_t read_data[] = READ_DATA;
+    size_t read_data_len = 0;
+
+    /* Set UID 1 */
+    status = psa_ps_set(uid_1, sizeof(write_data_1), write_data_1, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should not fail for UID 1");
+        return;
+    }
+
+    /* Set UID 2 */
+    status = psa_ps_set(uid_2, data_len_2, write_data_2, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should not fail for UID 2");
+        return;
+    }
+
+    /* Remove UID 1. This should cause UID 2 to be compacted to the beginning of
+     * the block.
+     */
+    status = psa_ps_remove(uid_1);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Remove should not fail for UID 1");
+        return;
+    }
+
+    /* If the compact worked as expected, the test should be able to read back
+     * the data from UID 2 correctly.
+     */
+    status = psa_ps_get(uid_2, offset, data_len_2,
+                        read_data + HALF_PADDING_SIZE, &read_data_len);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Get should not fail for UID 2");
+        return;
+    }
+
+    if (memcmp(read_data, RESULT_DATA, sizeof(read_data)) != 0) {
+        TEST_FAIL("Read buffer has incorrect data");
+        return;
+    }
+
+    /* Remove UID 2 to clean up storage for the next test */
+    status = psa_ps_remove(uid_2);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Remove should not fail for UID 2");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests set and multiple partial gets.
+ */
+TFM_PS_NS_TEST(1022, "Thread_A")
+{
+    psa_status_t status;
+    uint32_t i;
+    const psa_storage_uid_t uid = TEST_UID_1;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const uint32_t data_len = WRITE_DATA_SIZE;
+    uint32_t offset = 0;
+    const uint8_t write_data[] = WRITE_DATA;
+    uint8_t read_data[] = READ_DATA;
+    size_t read_data_len = 0;
+
+    /* Set the entire data into UID */
+    status = psa_ps_set(uid, data_len, write_data, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should not fail");
+        return;
+    }
+
+    /* Get the data from UID one byte at a time */
+    for (i = 0; i < data_len; ++i) {
+        status = psa_ps_get(uid, offset, 1,
+                            (read_data + HALF_PADDING_SIZE + i),
+                             &read_data_len);
+        if (status != PSA_SUCCESS) {
+            TEST_FAIL("Get should not fail for partial read");
+            return;
+        }
+
+        ++offset;
+    }
+
+    if (memcmp(read_data, RESULT_DATA, sizeof(read_data)) != 0) {
+        TEST_FAIL("Read buffer has incorrect data");
+        return;
+    }
+
+    /* Remove UID to clean up storage for the next test */
+    status = psa_ps_remove(uid);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Remove should not fail");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests multiple sets to the same UID.
+ */
+TFM_PS_NS_TEST(1023, "Thread_A")
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_2;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const uint32_t offset = 0;
+    const uint8_t write_data_1[] = "ONE";
+    const uint8_t write_data_2[] = "TWO";
+    const uint8_t write_data_3[] = "THREE";
+    uint8_t read_data[] = READ_DATA;
+    size_t read_data_len = 0;
+
+    /* Set write data 1 into UID */
+    status = psa_ps_set(uid, sizeof(write_data_1), write_data_1, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("First set should not fail");
+        return;
+    }
+
+    /* Set write data 2 into UID */
+    status = psa_ps_set(uid, sizeof(write_data_2), write_data_2, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Second set should not fail");
+        return;
+    }
+
+    /* Set write data 3 into UID */
+    status = psa_ps_set(uid, sizeof(write_data_3), write_data_3, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Third set should not fail");
+        return;
+    }
+
+    status = psa_ps_get(uid, offset, sizeof(write_data_3), read_data,
+                        &read_data_len);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Get should not fail");
+        return;
+    }
+
+    /* Check that get returns the last data to be set */
+    if (memcmp(read_data, write_data_3, sizeof(write_data_3)) != 0) {
+        TEST_FAIL("Read buffer has incorrect data");
+        return;
+    }
+
+    /* Remove UID to clean up storage for the next test */
+    status = psa_ps_remove(uid);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Remove should not fail");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests get support function.
+ */
+TFM_PS_NS_TEST(1024, "Thread_A")
+{
+    uint32_t support_flags;
+
+    support_flags = psa_ps_get_support();
+    if (support_flags != 0) {
+        TEST_FAIL("Support flags should be 0");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests set, get_info, get and remove function with:
+ * - Valid UID's
+ * - Data length of different asset sizes
+ * - No flags
+ */
+TFM_PS_NS_TEST(1025, "Thread_A")
+{
+    uint8_t cycle;
+    psa_status_t status;
+    const psa_storage_uid_t test_uid[TEST_1025_CYCLES] = {
+        TEST_UID_1,
+        TEST_UID_2,
+        TEST_UID_3};
+    const uint32_t test_asset_sizes[TEST_1025_CYCLES] = {
+        PS_MAX_ASSET_SIZE >> 2,
+        PS_MAX_ASSET_SIZE >> 1,
+        PS_MAX_ASSET_SIZE};
+
+    /* Loop to test different asset sizes and UID's*/
+    for (cycle = 0; cycle < TEST_1025_CYCLES; cycle++) {
+        uint32_t data_size = test_asset_sizes[cycle];
+        psa_storage_uid_t uid = test_uid[cycle];
+        struct psa_storage_info_t info = {0};
+
+        memset(read_asset_data, 0x00, sizeof(read_asset_data));
+
+        /* Set with data and no flags and a valid UID */
+        status = psa_ps_set(uid,
+                            data_size,
+                            write_asset_data,
+                            PSA_STORAGE_FLAG_NONE);
+        if (status != PSA_SUCCESS) {
+            TEST_FAIL("Set should not fail with valid UID");
+            return;
+        }
+
+        /* Get info for valid UID */
+        status = psa_ps_get_info(uid, &info);
+        if (status != PSA_SUCCESS) {
+            TEST_FAIL("Get info should not fail with valid UID");
+            return;
+        }
+
+        /* Check that the info struct contains the correct values */
+        if (info.size != data_size) {
+            TEST_FAIL("Size incorrect for valid UID");
+            return;
+        }
+
+        if (info.flags != PSA_STORAGE_FLAG_NONE) {
+            TEST_FAIL("Flags incorrect for valid UID");
+            return;
+        }
+
+        /* Check that thread can still get UID */
+        status = psa_ps_get(uid, 0, data_size, read_asset_data,
+                            &read_asset_data_len);
+        if (status != PSA_SUCCESS) {
+            TEST_FAIL("Get should not fail with valid UID");
+            return;
+        }
+
+        /* Check that thread's UID data has not been modified */
+        if (memcmp(read_asset_data, write_asset_data, data_size) != 0) {
+            TEST_FAIL("Read data should be equal to original write data");
+            return;
+        }
+
+        /* Call remove to clean up storage for the next test */
+        status = psa_ps_remove(uid);
+        if (status != PSA_SUCCESS) {
+            TEST_FAIL("Remove should not fail with valid UID");
+            return;
+        }
+    }
+
+    ret->val = TEST_PASSED;
+}
diff --git a/test/suites/ps/secure/nv_counters/test_ps_nv_counters.c b/test/suites/ps/secure/nv_counters/test_ps_nv_counters.c
new file mode 100644
index 0000000..a2a86b3
--- /dev/null
+++ b/test/suites/ps/secure/nv_counters/test_ps_nv_counters.c
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "test_ps_nv_counters.h"
+
+#include <limits.h>
+#include "secure_fw/partitions/protected_storage/nv_counters/ps_nv_counters.h"
+#include "secure_fw/partitions/protected_storage/ps_utils.h"
+
+#define DISABLE_INCREMENT 0
+#define ENABLE_INCREMENT  1
+
+#define TOTAL_PS_NV_COUNTERS  3
+#define INIT_NV_COUNTERS_VALUE 42
+
+static uint8_t nv_increment_status = ENABLE_INCREMENT;
+static uint32_t test_nv_counters[TOTAL_PS_NV_COUNTERS] = {
+                                                [0] = INIT_NV_COUNTERS_VALUE,
+                                                [1] = INIT_NV_COUNTERS_VALUE,
+                                                [2] = INIT_NV_COUNTERS_VALUE
+                };
+
+static uint32_t get_nv_counter_position(enum tfm_nv_counter_t counter_id)
+{
+    switch (counter_id) {
+    case TFM_PS_NV_COUNTER_1:
+        return 0;
+    case TFM_PS_NV_COUNTER_2:
+        return 1;
+    case TFM_PS_NV_COUNTER_3:
+        return 2;
+    default:
+        return TOTAL_PS_NV_COUNTERS;
+    }
+}
+
+psa_status_t ps_read_nv_counter(enum tfm_nv_counter_t counter_id,
+                                 uint32_t *val)
+{
+    uint32_t nv_pos;
+
+    nv_pos = get_nv_counter_position(counter_id);
+    if (nv_pos >= TOTAL_PS_NV_COUNTERS) {
+        return PSA_ERROR_GENERIC_ERROR;
+    }
+
+    /* Reads counter value */
+    *val = test_nv_counters[nv_pos];
+
+    return PSA_SUCCESS;
+}
+
+psa_status_t ps_increment_nv_counter(enum tfm_nv_counter_t counter_id)
+{
+    uint32_t nv_pos;
+
+    if (nv_increment_status == DISABLE_INCREMENT) {
+        return PSA_ERROR_GENERIC_ERROR;
+    }
+
+    nv_pos = get_nv_counter_position(counter_id);
+    if (nv_pos >= TOTAL_PS_NV_COUNTERS) {
+        return PSA_ERROR_GENERIC_ERROR;
+    }
+
+    if (test_nv_counters[nv_pos] == UINT32_MAX) {
+        return PSA_ERROR_GENERIC_ERROR;
+    }
+
+    /* Increments counter value */
+    test_nv_counters[nv_pos]++;
+
+    return PSA_SUCCESS;
+}
+
+/* Implementation of PS NV counter interfaces defined by
+ * test_ps_nv_counters.h
+ */
+void test_ps_disable_increment_nv_counter(void)
+{
+    nv_increment_status = DISABLE_INCREMENT;
+}
+
+void test_ps_enable_increment_nv_counter(void)
+{
+    nv_increment_status = ENABLE_INCREMENT;
+}
+
+psa_status_t test_ps_read_nv_counter(enum tfm_nv_counter_t counter_id,
+                                      uint32_t *val)
+{
+    return ps_read_nv_counter(counter_id, val);
+}
+
+psa_status_t test_ps_increment_nv_counter(enum tfm_nv_counter_t counter_id)
+{
+    return ps_increment_nv_counter(counter_id);
+}
+
+psa_status_t test_ps_decrement_nv_counter(enum tfm_nv_counter_t counter_id)
+{
+    uint32_t nv_pos;
+
+    nv_pos = get_nv_counter_position(counter_id);
+    if (nv_pos >= TOTAL_PS_NV_COUNTERS) {
+        return PSA_ERROR_GENERIC_ERROR;
+    }
+
+    if (test_nv_counters[nv_pos] == 0) {
+        return PSA_ERROR_GENERIC_ERROR;
+    }
+
+    /* Decrements counter value */
+    test_nv_counters[nv_pos]--;
+
+    return PSA_SUCCESS;
+}
+
+psa_status_t test_ps_set_nv_counter(enum tfm_nv_counter_t counter_id,
+                                    uint32_t value)
+{
+    uint32_t nv_pos;
+
+    nv_pos = get_nv_counter_position(counter_id);
+    if (nv_pos >= TOTAL_PS_NV_COUNTERS) {
+        return PSA_ERROR_GENERIC_ERROR;
+    }
+
+    /* Sets counter value */
+    test_nv_counters[nv_pos] = value;
+
+    return PSA_SUCCESS;
+}
diff --git a/test/suites/ps/secure/nv_counters/test_ps_nv_counters.h b/test/suites/ps/secure/nv_counters/test_ps_nv_counters.h
new file mode 100644
index 0000000..2f3b332
--- /dev/null
+++ b/test/suites/ps/secure/nv_counters/test_ps_nv_counters.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __TEST_PS_NV_COUNTERS_H__
+#define __TEST_PS_NV_COUNTERS_H__
+
+#include <stdint.h>
+#include "psa/protected_storage.h"
+#include "platform/include/tfm_plat_nv_counters.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Reads the given non-volatile (NV) counter.
+ *
+ * \param[in]  counter_id  NV counter ID.
+ * \param[out] val         Pointer to store the current NV counter value.
+ *
+ * \return  PSA_SUCCESS if the value is read correctly, otherwise
+ *          PSA_ERROR_GENERIC_ERROR
+ */
+psa_status_t test_ps_read_nv_counter(enum tfm_nv_counter_t counter_id,
+                                      uint32_t *val);
+
+/**
+ * \brief Increments the given non-volatile (NV) counter.
+ *
+ * \param[in] counter_id  NV counter ID.
+ *
+ * \return  When the NV counter reaches its maximum value, the
+ *          PSA_ERROR_GENERIC_ERROR error is returned to indicate the
+ *          value cannot be incremented. Otherwise, PSA_SUCCESS.
+ */
+psa_status_t test_ps_increment_nv_counter(enum tfm_nv_counter_t counter_id);
+
+/**
+ * \brief Decrements the given non-volatile (NV) counter.
+ *
+ * \param[in] counter_id  NV counter ID.
+ *
+ * \return  When the NV counter reaches its minimum value, the
+ *          PSA_ERROR_GENERIC_ERROR error is returned to indicate the
+ *          value cannot be decremented. Otherwise, PSA_SUCCESS.
+ */
+psa_status_t test_ps_decrement_nv_counter(enum tfm_nv_counter_t counter_id);
+
+/**
+ * \brief Disables PS increment nv counter function to force
+ *        PSA_ERROR_GENERIC_ERROR return value as an indication that NV
+ *        counter reaches its maximum value.
+ */
+void test_ps_disable_increment_nv_counter(void);
+
+/**
+ * \brief Enables PS increment nv counter function to work normally.
+ */
+void test_ps_enable_increment_nv_counter(void);
+
+/**
+ * \brief Sets a new value into the given non-volatile (NV) counter.
+ *
+ * \param[in] counter_id  NV counter Id.
+ * \param[in] value       New NV counter value.
+ *
+ * \return  When the NV counter reaches its maximum value, the
+ *          PSA_ERROR_GENERIC_ERROR error is returned to indicate the
+ *          value cannot be set. Otherwise, PSA_SUCCESS.
+ */
+psa_status_t test_ps_set_nv_counter(enum tfm_nv_counter_t counter_id,
+                                     uint32_t value);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __TEST_PS_NV_COUNTERS_H__ */
diff --git a/test/suites/ps/secure/ps_rollback_protection_testsuite.c b/test/suites/ps/secure/ps_rollback_protection_testsuite.c
new file mode 100644
index 0000000..d8c1bd9
--- /dev/null
+++ b/test/suites/ps/secure/ps_rollback_protection_testsuite.c
@@ -0,0 +1,819 @@
+/*
+ * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "ps_tests.h"
+
+#include <stdio.h>
+
+#include "secure_fw/partitions/protected_storage/nv_counters/ps_nv_counters.h"
+#include "nv_counters/test_ps_nv_counters.h"
+#include "psa/protected_storage.h"
+#include "tfm_memory_utils.h"
+#include "s_test_helpers.h"
+
+/* This include is required to expose the ps_system_prepare function, via the
+ * tfm_ps_test_system_prepare API, to simulate a reboot in the system.
+ * ps_system_prepare is called when the PS service is initialized.
+ */
+#include "test/test_services/tfm_ps_test_service/tfm_ps_test_service_api.h"
+
+#include "test/framework/test_framework_helpers.h"
+
+/* Test UIDs */
+#define TEST_UID 2UL  /* UID 1 cannot be used as it references a write once
+                       * asset, created in psa_ps_s_interface_testsuite.c
+                       */
+
+/* Write data */
+#define WRITE_DATA       "THE_FIVE_BOXING_WIZARDS_JUMP_QUICKLY"
+#define WRITE_DATA_SIZE  (sizeof(WRITE_DATA) - 1)
+#define READ_DATA        "############################################"
+#define RESULT_DATA      ("####" WRITE_DATA "####")
+
+/*
+ * Summary of tests covered by the test suite.
+ *
+ * PS version  | NVC1 | NVC2 | NVC3 |  Result  |  Test Num
+ * ------------|------|------|------|----------|------------
+ *      X      |   X  |   X  |   X  |  Valid   |     1
+ *      N      |   X  |   X  |   X  |  Invalid |     2
+ *      X      |   X  |   X  |   N  |  Valid   |     3
+ *      N      |   X  |   N  |   N  |  Valid   |     4
+ *      X      |   X  |   N  |   N  |  Valid   |     5
+ *      X      |   X  |   M  |   N  |  Valid   |     6
+ *      M      |   X  |   M  |   N  |  Invalid |     7
+ *      N      |   X  |   M  |   N  |  Invalid |     8
+ *
+ * Test 9 checks the PS result when the non-volatile (NV) counter 1 cannot be
+ * incremented (e.g it has reached its maximum value).
+ */
+
+/* List of tests */
+static void tfm_ps_test_4001(struct test_result_t *ret);
+static void tfm_ps_test_4002(struct test_result_t *ret);
+static void tfm_ps_test_4003(struct test_result_t *ret);
+static void tfm_ps_test_4004(struct test_result_t *ret);
+static void tfm_ps_test_4005(struct test_result_t *ret);
+static void tfm_ps_test_4006(struct test_result_t *ret);
+static void tfm_ps_test_4007(struct test_result_t *ret);
+static void tfm_ps_test_4008(struct test_result_t *ret);
+static void tfm_ps_test_4009(struct test_result_t *ret);
+
+static struct test_t interface_tests[] = {
+    {&tfm_ps_test_4001, "TFM_PS_TEST_4001",
+     "Check PS area version when NV counters 1/2/3 have the same value", {TEST_PASSED}},
+    {&tfm_ps_test_4002, "TFM_PS_TEST_4002",
+     "Check PS area version when it is different from NV counters 1/2/3", {TEST_PASSED}},
+    {&tfm_ps_test_4003, "TFM_PS_TEST_4003",
+     "Check PS area version when NV counters 1 and 2 are equals, 3 is "
+     "different, and PS area version match NV counters 1 and 2", {TEST_PASSED}},
+    {&tfm_ps_test_4004, "TFM_PS_TEST_4004",
+     "Check PS area version when NV counters 2 and 3 are equals, 1 is "
+     "different and PS area version match NV counter 2 and 3", {TEST_PASSED}},
+    {&tfm_ps_test_4005, "TFM_PS_TEST_4005",
+     "Check PS area version when NV counters 2 and 3 are equals, 1 is "
+     "different and PS area version match NV counter 1", {TEST_PASSED}},
+    {&tfm_ps_test_4006, "TFM_PS_TEST_4006",
+     "Check PS area version when NV counters 1, 2 and 3 have different values "
+     "and PS area version match NV counter 1 value", {TEST_PASSED}},
+    {&tfm_ps_test_4007, "TFM_PS_TEST_4007",
+     "Check PS area version when NV counters 1, 2 and 3 have different values "
+     "and PS area version match NV counter 2 value", {TEST_PASSED}},
+    {&tfm_ps_test_4008, "TFM_PS_TEST_4008",
+     "Check PS area version when NV counters 1, 2 and 3 have different values "
+     "and PS area version match NV counter 3 value", {TEST_PASSED}},
+    {&tfm_ps_test_4009, "TFM_PS_TEST_4009",
+     "Check PS area version when NV counter 1 cannot be incremented", {TEST_PASSED}},
+};
+
+void register_testsuite_s_rollback_protection(struct test_suite_t *p_test_suite)
+{
+    uint32_t list_size = (sizeof(interface_tests) / sizeof(interface_tests[0]));
+
+    set_testsuite("PS rollback protection tests (TFM_PS_TEST_4XXX)",
+                  interface_tests, list_size, p_test_suite);
+}
+
+/**
+ * \brief Check PS area version when NV counters 1/2/3 have the same value.
+ *        It also checks that the 3 NV counters are aligned and they have been
+ *        increased by 1 unit.
+ */
+static void tfm_ps_test_4001(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const uint32_t data_len = WRITE_DATA_SIZE;
+    const uint32_t offset = 0;
+    uint32_t old_nvc_1, nvc_1, nvc_2, nvc_3;
+    const uint8_t write_data[] = WRITE_DATA;
+    uint8_t read_data[] = READ_DATA;
+    size_t read_data_len = 0;
+
+    /* Creates an asset in the PS area to generate a new PS area version */
+    status = psa_ps_set(uid, data_len, write_data, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should not fail with valid UID");
+        return;
+    }
+
+    /* Reads NV counter 1 to get the saved value to compare it later */
+    status = test_ps_read_nv_counter(TFM_PS_NV_COUNTER_1, &old_nvc_1);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Read should not fail");
+        return;
+    }
+
+    /* Sets new data in the asset to generate a new PS area version */
+    status = psa_ps_set(uid, data_len, write_data, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should not fail with valid UID");
+        return;
+    }
+
+    /* Validates the 3 NV counters have the same value and it has been increased
+     * by 1 unit.
+     */
+
+    /* Reads NV counter 1 to get the current value */
+    status = test_ps_read_nv_counter(TFM_PS_NV_COUNTER_1, &nvc_1);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Read should not fail");
+        return;
+    }
+
+    /* Checks if NV counter 1 value has been increased by 1 unit as result of
+     * process the write request.
+     */
+    if (nvc_1 != (old_nvc_1 + 1)) {
+        TEST_FAIL("NV counter 1 has been increased more than 1 unit");
+        return;
+    }
+
+    /* Reads NV counter 2 to get the current value */
+    status = test_ps_read_nv_counter(TFM_PS_NV_COUNTER_2, &nvc_2);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Read should not fail");
+        return;
+    }
+
+    if (nvc_1 != nvc_2) {
+        TEST_FAIL("NV counter 1 and 2 should have the same value");
+        return;
+    }
+
+    /* Reads NV counter 3 to get the current value */
+    status = test_ps_read_nv_counter(TFM_PS_NV_COUNTER_3, &nvc_3);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Read should not fail");
+        return;
+    }
+
+    if (nvc_2 != nvc_3) {
+        TEST_FAIL("NV counter 2 and 3 should have the same value");
+        return;
+    }
+
+    /* Simulates a reboot in the system by calling ps_system_prepare(). This
+     * function is called when the PS service is initialized.
+     *
+     * Prepare should not fail as the NV counters has the same values and
+     * the PS area authentication is aligned with those values.
+     */
+    status = tfm_ps_test_system_prepare();
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("AM prepare should not fail");
+        return;
+    }
+
+    /* Gets data from the asset */
+    status = psa_ps_get(uid, offset, data_len, (read_data + HALF_PADDING_SIZE),
+                        &read_data_len);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Get should not fail");
+        return;
+    }
+
+    /* Checks that the data has not changed */
+    if (tfm_memcmp(read_data, RESULT_DATA, sizeof(read_data)) != 0) {
+        TEST_FAIL("The data should not have changed");
+        return;
+    }
+
+    /* Removes the asset to clean up storage for the next test */
+    status = psa_ps_remove(uid);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Remove should not fail with valid UID");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Check PS area version when it is different from NV counters
+ *        1/2/3.
+ */
+static void tfm_ps_test_4002(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const uint32_t data_len = WRITE_DATA_SIZE;
+    const uint8_t write_data[] = WRITE_DATA;
+
+    /* Creates an asset in the PS area to generate a new PS area version */
+    status = psa_ps_set(uid, data_len, write_data, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should not fail with valid UID");
+        return;
+    }
+
+    /* Increments all counters to make that PS area version old/invalid */
+    status = test_ps_increment_nv_counter(TFM_PS_NV_COUNTER_1);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Increment should not fail");
+        return;
+    }
+
+    status = test_ps_increment_nv_counter(TFM_PS_NV_COUNTER_2);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Increment should not fail");
+        return;
+    }
+
+    status = test_ps_increment_nv_counter(TFM_PS_NV_COUNTER_3);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Increment should not fail");
+        return;
+    }
+
+    /* Simulates a reboot in the system by calling ps_system_prepare(). This
+     * function is called when the PS service is initialized.
+     *
+     * Prepare should fail as the PS area version does not match the
+     * NV counters values.
+     */
+    status = tfm_ps_test_system_prepare();
+    if (status != PSA_ERROR_GENERIC_ERROR) {
+        TEST_FAIL("PS system prepare should fail as version is old");
+        return;
+    }
+
+    /* Removes the asset to clean up storage for the next test.
+     *
+     * To be able to remove the asset, the PS area version should match
+     * with the counter values. So, it is required to:
+     *
+     * 1. align the counters with the PS area version
+     * 2. re-call ps_system_prepare to mark the PS area as a valid image
+     * 3. remove the asset.
+     */
+
+    /* Aligns NV counters with the PS area version */
+    status = test_ps_decrement_nv_counter(TFM_PS_NV_COUNTER_1);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Decrement should not fail");
+        return;
+    }
+
+    status = test_ps_decrement_nv_counter(TFM_PS_NV_COUNTER_2);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Decrement should not fail");
+        return;
+    }
+
+    status = test_ps_decrement_nv_counter(TFM_PS_NV_COUNTER_3);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Decrement should not fail");
+        return;
+    }
+
+    /* Calls ps_system_prepare to mark the PS area as a valid image */
+    status = tfm_ps_test_system_prepare();
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("PS system prepare should not fail");
+        return;
+    }
+
+    /* Removes the asset to clean up storage for the next test */
+    status = psa_ps_remove(uid);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Remove should not fail with valid UID");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Check PS area version when NV counters 1 and 2 are equals, 3 is
+ *        different and PS area version match NV counter 1 and 2 values.
+ *        It simulates a power cut during write action while the counter 3 is
+ *        being increased.
+ */
+static void tfm_ps_test_4003(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const uint32_t data_len = WRITE_DATA_SIZE;
+    const uint32_t offset = 0;
+    const uint8_t write_data[] = WRITE_DATA;
+    uint8_t read_data[] = READ_DATA;
+    size_t read_data_len = 0;
+
+    /* Creates an asset in the PS area to generate a new PS area version */
+    status = psa_ps_set(uid, data_len, write_data, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should not fail with valid UID");
+        return;
+    }
+
+    /* Decrements NV counters 3 to make it different from the other two counters
+     * and make the current PS area version match NV counter 1 and 2 values.
+     */
+    status = test_ps_decrement_nv_counter(TFM_PS_NV_COUNTER_3);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Decrement should not fail");
+        return;
+    }
+
+    /* Simulates a reboot in the system by calling ps_system_prepare(). This
+     * function is called when the PS service is initialized.
+     *
+     * Prepare should not fail as the PS area version match NV counters 1 and
+     * 2 values.
+     */
+    status = tfm_ps_test_system_prepare();
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("PS system prepare should not fail");
+        return;
+    }
+
+    /* Gets the data from the asset */
+    status = psa_ps_get(uid, offset, data_len, (read_data + HALF_PADDING_SIZE),
+                                                &read_data_len);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Get should not fail");
+        return;
+    }
+
+    /* Checks that the data has not changed */
+    if (tfm_memcmp(read_data, RESULT_DATA, sizeof(read_data)) != 0) {
+        TEST_FAIL("The data should not have changed");
+        return;
+    }
+
+    /* Removes the asset to clean up storage for the next test */
+    status = psa_ps_remove(uid);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Remove should not fail with valid UID");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Check PS area version when NV counters 2 and 3 are equals, 1 is
+ *        different and PS area version match NV counter 2 and 3 values.
+ *        It simulates a power cut during write action before increment counter
+ *        2 and 3, and the new PS area version is corrupted and only the old
+ *        version match the NV counters.
+ */
+static void tfm_ps_test_4004(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const uint32_t data_len = WRITE_DATA_SIZE;
+    const uint32_t offset = 0;
+    const uint8_t write_data[] = WRITE_DATA;
+    uint8_t read_data[] = READ_DATA;
+    size_t read_data_len = 0;
+
+    /* Creates an asset in the PS area to generate a new PS area version */
+    status = psa_ps_set(uid, data_len, write_data, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should not fail with valid UID");
+        return;
+    }
+
+    /* Increments NV counters 1 to make it different from the other two counters
+     * and make the current PS area version match NV counter 2 and 3 values.
+     */
+    status = test_ps_increment_nv_counter(TFM_PS_NV_COUNTER_1);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Increment should not fail");
+        return;
+    }
+
+    /* Simulates a reboot in the system by calling ps_system_prepare(). This
+     * function is called when the PS service is initialized.
+     *
+     * Prepare should not fail as the PS area version match the NV counter 2
+     * and 3 values.
+     */
+    status = tfm_ps_test_system_prepare();
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("PS system prepare should not fail");
+        return;
+    }
+
+    /* Gets the data from the asset */
+    status = psa_ps_get(uid, offset, data_len, (read_data + HALF_PADDING_SIZE),
+                                                &read_data_len);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Get should not fail");
+        return;
+    }
+
+    /* Checks that the data has not changed */
+    if (tfm_memcmp(read_data, RESULT_DATA, sizeof(read_data)) != 0) {
+        TEST_FAIL("The data should not have changed");
+        return;
+    }
+
+    /* Removes the asset to clean up storage for the next test */
+    status = psa_ps_remove(uid);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Remove should not fail with valid UID");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Check PS area version when NV counters 2 and 3 are equals, 1 is
+ *        different and PS area version match NV counter 1 value.
+ *        It simulates a power cut during write action before increment counter
+ *        2 and 3, and the new PS area version is corrupted and only the old
+ *        version match the NV counters.
+ */
+static void tfm_ps_test_4005(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const uint32_t data_len = WRITE_DATA_SIZE;
+    const uint32_t offset = 0;
+    const uint8_t write_data[] = WRITE_DATA;
+    uint8_t read_data[] = READ_DATA;
+    size_t read_data_len = 0;
+
+    /* Creates an asset in the PS area to generate a new PS area version */
+    status = psa_ps_set(uid, data_len, write_data, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should not fail with valid UID");
+        return;
+    }
+
+    /* Decrements NV counter 2 and 3 to make the PS area version match NV
+     * counter 1 only.
+     */
+    status = test_ps_decrement_nv_counter(TFM_PS_NV_COUNTER_2);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Decrement should not fail");
+        return;
+    }
+
+    status = test_ps_decrement_nv_counter(TFM_PS_NV_COUNTER_3);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Decrement should not fail");
+        return;
+    }
+
+    /* Simulates a reboot in the system by calling ps_system_prepare(). This
+     * function is called when the PS service is initialized.
+     *
+     * Prepare should not fail as the PS area version match the NV counter 1.
+     */
+    status = tfm_ps_test_system_prepare();
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("PS system prepare should not fail");
+        return;
+    }
+
+    /* Gets the data from the asset */
+    status = psa_ps_get(uid, offset, data_len, (read_data + HALF_PADDING_SIZE),
+                                                &read_data_len);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Get should not fail");
+        return;
+    }
+
+    /* Checks that the data has not changed */
+    if (tfm_memcmp(read_data, RESULT_DATA, sizeof(read_data)) != 0) {
+        TEST_FAIL("The data should not have changed");
+        return;
+    }
+
+    /* Removes the asset to clean up storage for the next test */
+    status = psa_ps_remove(uid);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Remove should not fail with valid UID");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Check PS area version when NV counters 1, 2 and 3 have different
+ *        values and PS area version match NV counter 1 value.
+ */
+static void tfm_ps_test_4006(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const uint32_t data_len = WRITE_DATA_SIZE;
+    const uint32_t offset = 0;
+    const uint8_t write_data[] = WRITE_DATA;
+    uint8_t read_data[] = READ_DATA;
+    size_t read_data_len = 0;
+
+    /* Creates an asset in the PS area to generate a new PS area version */
+    status = psa_ps_set(uid, data_len, write_data, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should not fail with valid UID");
+        return;
+    }
+
+    /* Decrements NV counter 2 (1 time) and 3 (2 times) to make the PS area
+     * version match NV counter 1 only.
+     */
+    status = test_ps_decrement_nv_counter(TFM_PS_NV_COUNTER_2);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Decrement should not fail");
+        return;
+    }
+
+    status = test_ps_decrement_nv_counter(TFM_PS_NV_COUNTER_3);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Decrement should not fail");
+        return;
+    }
+
+    status = test_ps_decrement_nv_counter(TFM_PS_NV_COUNTER_3);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Decrement should not fail");
+        return;
+    }
+
+    /* Simulates a reboot in the system by calling ps_system_prepare(). This
+     * function is called when the PS service is initialized.
+     *
+     * Prepare should not fail as the PS area version match the NV counter 1.
+     */
+    status = tfm_ps_test_system_prepare();
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("PS system prepare should not fail");
+        return;
+    }
+
+    /* Gets data from the asset */
+    status = psa_ps_get(uid, offset, data_len, (read_data + HALF_PADDING_SIZE),
+                                                &read_data_len);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Get should not fail");
+        return;
+    }
+
+    /* Checks that the data has not changed */
+    if (tfm_memcmp(read_data, RESULT_DATA, sizeof(read_data)) != 0) {
+        TEST_FAIL("The data should not have changed");
+        return;
+    }
+
+    /* Removes the asset to clean up storage for the next test */
+    status = psa_ps_remove(uid);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Remove should not fail with valid UID");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Check PS area version when NV counters 1, 2 and 3 have different
+ *        values and PS area version match NV counter 2 value.
+ */
+static void tfm_ps_test_4007(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const uint32_t data_len = WRITE_DATA_SIZE;
+    const uint8_t write_data[] = WRITE_DATA;
+
+    /* Creates an asset in the PS area to generate a new PS area version */
+    status = psa_ps_set(uid, data_len, write_data, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should not fail with valid UID");
+        return;
+    }
+
+    /* Increments NV counter 1 and decrements 3 to make the PS area
+     * version match NV counter 2 only.
+     */
+    status = test_ps_increment_nv_counter(TFM_PS_NV_COUNTER_1);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Increment should not fail");
+        return;
+    }
+
+    status = test_ps_decrement_nv_counter(TFM_PS_NV_COUNTER_3);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Decrement should not fail");
+        return;
+    }
+
+    /* Simulates a reboot in the system by calling ps_system_prepare(). This
+     * function is called when the PS service is initialized.
+     *
+     * Prepare should fail as the PS area version match the NV counter 2 and
+     * the other counters are different.
+     */
+    status = tfm_ps_test_system_prepare();
+    if (status != PSA_ERROR_GENERIC_ERROR) {
+        TEST_FAIL("PS system prepare should fail");
+        return;
+    }
+
+    /* Removes the asset to clean up storage for the next test.
+     *
+     * To be able to remove the asset, the PS area version should match
+     * with the counter values. So, it is required to:
+     *
+     * 1. align the counters with the PS area version
+     * 2. re-call ps_system_prepare to mark the PS area as a valid image
+     * 3. remove the asset.
+     */
+
+    /* Aligns NV counters with the PS area version */
+    status = test_ps_decrement_nv_counter(TFM_PS_NV_COUNTER_1);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Decrement should not fail");
+        return;
+    }
+
+    status = test_ps_increment_nv_counter(TFM_PS_NV_COUNTER_3);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Increment should not fail");
+        return;
+    }
+
+    /* Calls ps_system_prepare to mark the PS area as a valid image */
+    status = tfm_ps_test_system_prepare();
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("PS system prepare should not fail");
+        return;
+    }
+
+    /* Removes the asset to clean up storage for the next test */
+    status = psa_ps_remove(uid);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Remove should not fail with valid UID");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Check PS area version when NV counters 1, 2 and 3 have different
+ *        values and PS area version match NV counter 3 value.
+ */
+static void tfm_ps_test_4008(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const uint32_t data_len = WRITE_DATA_SIZE;
+    const uint8_t write_data[] = WRITE_DATA;
+
+    /* Creates an asset in the PS area to generate a new PS area version */
+    status = psa_ps_set(uid, data_len, write_data, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should not fail with valid UID");
+        return;
+    }
+
+    /* Increments NV counter 1 (2 times) and 2 (1 time) to make the PS area
+     * version match NV counter 3 only.
+     */
+    status = test_ps_increment_nv_counter(TFM_PS_NV_COUNTER_1);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Increment should not fail");
+        return;
+    }
+
+    status = test_ps_increment_nv_counter(TFM_PS_NV_COUNTER_1);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Increment should not fail");
+        return;
+    }
+
+    status = test_ps_increment_nv_counter(TFM_PS_NV_COUNTER_2);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Increment should not fail");
+        return;
+    }
+
+    /* Simulates a reboot in the system by calling ps_system_prepare(). This
+     * function is called when the PS service is initialized.
+     *
+     * Prepare should fail as the PS area version match the NV counter 2 and
+     * the other counters are different.
+     */
+    status = tfm_ps_test_system_prepare();
+    if (status != PSA_ERROR_GENERIC_ERROR) {
+        TEST_FAIL("AM prepare should fail");
+        return;
+    }
+
+    /* Removes the asset to clean up storage for the next test.
+     *
+     * To be able to remove the asset, the PS area version should match
+     * with the counter values. So, it is required to:
+     *
+     * 1. align the counters with the PS area version
+     * 2. re-call ps_system_prepare to mark the PS area as a valid image
+     * 3. remove the asset.
+     */
+
+    /* Align NV counters with the PS area version */
+    status = test_ps_decrement_nv_counter(TFM_PS_NV_COUNTER_1);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Decrement should not fail");
+        return;
+    }
+
+    status = test_ps_decrement_nv_counter(TFM_PS_NV_COUNTER_1);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Decrement should not fail");
+        return;
+    }
+
+    status = test_ps_decrement_nv_counter(TFM_PS_NV_COUNTER_2);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Decrement should not fail");
+        return;
+    }
+
+    /* Calls ps_system_prepare to mark the PS area as a valid image */
+    status = tfm_ps_test_system_prepare();
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("PS system prepare should not fail");
+        return;
+    }
+
+    /* Removes the asset to clean up storage for the next test */
+    status = psa_ps_remove(uid);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Remove should not fail with valid UID");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Check PS area version when NV counter 1 cannot be incremented
+ *        (e.g it has reached its maximum value)
+ */
+static void tfm_ps_test_4009(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const uint32_t data_len = WRITE_DATA_SIZE;
+    const uint8_t write_data[] = WRITE_DATA;
+
+    /* Disables increment function to simulate that NV counter 1 has
+     * reached its maximum value.
+     */
+    test_ps_disable_increment_nv_counter();
+
+    /* Creates an asset in the PS area to generate a new PS area version */
+    status = psa_ps_set(uid, data_len, write_data, flags);
+    if (status != PSA_ERROR_GENERIC_ERROR) {
+        TEST_FAIL("Set should fail as the non-volatile counters can not be"
+                  " increased");
+        return;
+    }
+
+    /* Enables counter again to not affect the next tests, if any */
+    test_ps_enable_increment_nv_counter();
+
+    ret->val = TEST_PASSED;
+}
diff --git a/test/suites/ps/secure/ps_tests.h b/test/suites/ps/secure/ps_tests.h
new file mode 100644
index 0000000..f2b9959
--- /dev/null
+++ b/test/suites/ps/secure/ps_tests.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2017-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __PS_TESTS_H__
+#define __PS_TESTS_H__
+
+#include "test/framework/test_framework.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Register testsuite for the PSA protected storage S interface tests.
+ *
+ * \param[in] p_test_suite  The test suite to be executed.
+ */
+void register_testsuite_s_psa_ps_interface(struct test_suite_t *p_test_suite);
+
+/**
+ * \brief Register testsuite for the ps reliability tests.
+ *
+ * \param[in] p_test_suite  The test suite to be executed.
+ */
+void register_testsuite_s_psa_ps_reliability(struct test_suite_t *p_test_suite);
+
+#ifdef PS_TEST_NV_COUNTERS
+/**
+ * \brief Register testsuite for the ps rollback protection tests.
+ *
+ * \param[in] p_test_suite  The test suite to be executed.
+ */
+void register_testsuite_s_rollback_protection(
+                                             struct test_suite_t *p_test_suite);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __PS_TESTS_H__ */
diff --git a/test/suites/ps/secure/psa_ps_s_interface_testsuite.c b/test/suites/ps/secure/psa_ps_s_interface_testsuite.c
new file mode 100644
index 0000000..7b44d52
--- /dev/null
+++ b/test/suites/ps/secure/psa_ps_s_interface_testsuite.c
@@ -0,0 +1,1220 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "ps_tests.h"
+
+#include <stdio.h>
+
+#include "s_test_helpers.h"
+#include "tfm_memory_utils.h"
+#include "psa/protected_storage.h"
+#include "test/framework/test_framework_helpers.h"
+#include "flash_layout.h"
+
+/* Test UIDs */
+#define WRITE_ONCE_UID  1U /* Cannot be modified or deleted once created */
+#define TEST_UID_1      2U
+#define TEST_UID_2      3U
+#define TEST_UID_3      4U
+
+/* Invalid values */
+#define INVALID_UID              0U
+#define INVALID_DATA_LEN         UINT32_MAX
+#define INVALID_OFFSET           UINT32_MAX
+#define INVALID_FLAG             (1U << 31)
+#define INVALID_THREAD_NAME      "Thread_INVALID"
+
+/* Write once data */
+#define WRITE_ONCE_DATA          "THE_FIVE_BOXING_WIZARDS_JUMP_QUICKLY"
+#define WRITE_ONCE_DATA_SIZE     (sizeof(WRITE_ONCE_DATA) - 1)
+#define WRITE_ONCE_READ_DATA     "############################################"
+#define WRITE_ONCE_RESULT_DATA   ("####" WRITE_ONCE_DATA "####")
+
+#define WRITE_DATA               "THEQUICKBROWNFOXJUMPSOVERALAZYDOG"
+#define WRITE_DATA_SIZE          (sizeof(WRITE_DATA) - 1)
+#define READ_DATA                "_________________________________________"
+#define RESULT_DATA              ("____" WRITE_DATA "____")
+#define OFFSET_READ_DATA         "HEQUICKBROWNFOXJUMPSOVERALAZYDOG"
+#define OFFSET_RESULT_DATA       ("____" OFFSET_READ_DATA "_____")
+
+#define TEST_1022_CYCLES         3U
+
+static const uint8_t write_asset_data[PS_MAX_ASSET_SIZE] = {0xBF};
+static uint8_t read_asset_data[PS_MAX_ASSET_SIZE] = {0};
+static size_t read_asset_datal_len = 0;
+
+/* List of tests */
+static void tfm_ps_test_2001(struct test_result_t *ret);
+static void tfm_ps_test_2002(struct test_result_t *ret);
+static void tfm_ps_test_2003(struct test_result_t *ret);
+static void tfm_ps_test_2004(struct test_result_t *ret);
+static void tfm_ps_test_2005(struct test_result_t *ret);
+static void tfm_ps_test_2006(struct test_result_t *ret);
+static void tfm_ps_test_2007(struct test_result_t *ret);
+static void tfm_ps_test_2008(struct test_result_t *ret);
+static void tfm_ps_test_2009(struct test_result_t *ret);
+static void tfm_ps_test_2010(struct test_result_t *ret);
+static void tfm_ps_test_2011(struct test_result_t *ret);
+static void tfm_ps_test_2012(struct test_result_t *ret);
+static void tfm_ps_test_2013(struct test_result_t *ret);
+static void tfm_ps_test_2014(struct test_result_t *ret);
+static void tfm_ps_test_2015(struct test_result_t *ret);
+static void tfm_ps_test_2016(struct test_result_t *ret);
+static void tfm_ps_test_2017(struct test_result_t *ret);
+static void tfm_ps_test_2018(struct test_result_t *ret);
+static void tfm_ps_test_2019(struct test_result_t *ret);
+static void tfm_ps_test_2020(struct test_result_t *ret);
+static void tfm_ps_test_2021(struct test_result_t *ret);
+static void tfm_ps_test_2022(struct test_result_t *ret);
+
+static struct test_t psa_ps_s_tests[] = {
+    {&tfm_ps_test_2001, "TFM_PS_TEST_2001",
+     "Set interface"},
+    {&tfm_ps_test_2002, "TFM_PS_TEST_2002",
+     "Set interface with create flags"},
+    {&tfm_ps_test_2003, "TFM_PS_TEST_2003",
+     "Set interface with NULL data pointer"},
+    {&tfm_ps_test_2004, "TFM_PS_TEST_2004",
+     "Set interface with invalid data length"},
+    {&tfm_ps_test_2005, "TFM_PS_TEST_2005",
+     "Set interface with write once UID"},
+    {&tfm_ps_test_2006, "TFM_PS_TEST_2006",
+     "Get interface with valid data"},
+    {&tfm_ps_test_2007, "TFM_PS_TEST_2007",
+     "Get interface with zero data length"},
+    {&tfm_ps_test_2008, "TFM_PS_TEST_2008",
+     "Get interface with invalid UIDs"},
+    {&tfm_ps_test_2009, "TFM_PS_TEST_2009",
+     "Get interface with invalid data lengths and offsets"},
+    {&tfm_ps_test_2010, "TFM_PS_TEST_2010",
+     "Get interface with NULL data pointer"},
+    {&tfm_ps_test_2011, "TFM_PS_TEST_2011",
+     "Get info interface with write once UID"},
+    {&tfm_ps_test_2012, "TFM_PS_TEST_2012",
+     "Get info interface with valid UID"},
+    {&tfm_ps_test_2013, "TFM_PS_TEST_2013",
+     "Get info interface with invalid UIDs"},
+    {&tfm_ps_test_2014, "TFM_PS_TEST_2014",
+     "Get info interface with NULL info pointer"},
+    {&tfm_ps_test_2015, "TFM_PS_TEST_2015",
+     "Remove interface with valid UID"},
+    {&tfm_ps_test_2016, "TFM_PS_TEST_2016",
+     "Remove interface with write once UID"},
+    {&tfm_ps_test_2017, "TFM_PS_TEST_2017",
+     "Remove interface with invalid UID"},
+    {&tfm_ps_test_2018, "TFM_PS_TEST_2018",
+     "Block compaction after remove"},
+    {&tfm_ps_test_2019, "TFM_PS_TEST_2019",
+     "Multiple partial gets"},
+    {&tfm_ps_test_2020, "TFM_PS_TEST_2020",
+     "Multiple sets to same UID from same thread"},
+    {&tfm_ps_test_2021, "TFM_PS_TEST_2021",
+     "Get support interface"},
+    {&tfm_ps_test_2022, "TFM_PS_TEST_2022",
+     "Set, get and remove interface with different asset sizes"},
+};
+
+void register_testsuite_s_psa_ps_interface(struct test_suite_t *p_test_suite)
+{
+    uint32_t list_size;
+
+    list_size = (sizeof(psa_ps_s_tests) / sizeof(psa_ps_s_tests[0]));
+
+    set_testsuite("PSA protected storage S interface tests (TFM_PS_TEST_2XXX)",
+                  psa_ps_s_tests, list_size, p_test_suite);
+}
+
+/**
+ * \brief Tests set function with:
+ * - Valid UID, no data, no flags
+ * - Invalid UID, no data, no flags
+ */
+static void tfm_ps_test_2001(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_1;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const uint32_t data_len = 0;
+    const uint8_t write_data[] = {0};
+
+    /* Set with no data and no flags and a valid UID */
+    status = psa_ps_set(uid, data_len, write_data, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should not fail with valid UID");
+        return;
+    }
+
+    /* Attempt to set a second time */
+    status = psa_ps_set(uid, data_len, write_data, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should not fail the second time with valid UID");
+        return;
+    }
+
+    /* Set with an invalid UID */
+    status = psa_ps_set(INVALID_UID, data_len, write_data, flags);
+    if (status != PSA_ERROR_INVALID_ARGUMENT) {
+        TEST_FAIL("Set should not succeed with an invalid UID");
+        return;
+    }
+
+    /* Call remove to clean up storage for the next test */
+    status = psa_ps_remove(uid);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Remove should not fail with valid UID");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests set function with:
+ * - Zero create flags
+ * - Valid create flags (with previously created UID)
+ * - Invalid create flags
+ */
+static void tfm_ps_test_2002(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_2;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const uint32_t data_len = WRITE_DATA_SIZE;
+    const uint8_t write_data[] = WRITE_DATA;
+
+    /* Set with no flags */
+    status = psa_ps_set(WRITE_ONCE_UID, data_len, write_data, flags);
+    if (status == PSA_SUCCESS) {
+        /* Set with valid flag: PSA_STORAGE_FLAG_WRITE_ONCE (with previously
+         * created UID)
+         * Note: Once created, WRITE_ONCE_UID cannot be deleted. It is reused
+         * across multiple tests.
+         */
+        status = psa_ps_set(WRITE_ONCE_UID, WRITE_ONCE_DATA_SIZE,
+                            WRITE_ONCE_DATA, PSA_STORAGE_FLAG_WRITE_ONCE);
+        if (status != PSA_SUCCESS) {
+            TEST_FAIL("Set should not fail with valid flags (existing UID)");
+            return;
+        }
+    } else if (status == PSA_ERROR_NOT_PERMITTED) {
+        /* The UID has already been created with the PSA_STORAGE_FLAG_WRITE_ONCE
+         * flag in a previous test run, so skip creating it again and emit a
+         * warning.
+         */
+        TEST_LOG("Note: The UID in this test has already been created with\r\n"
+                 "the PSA_STORAGE_FLAG_WRITE_ONCE flag in a previous test\r\n"
+                 "run. Wipe the storage area to run the full test.\r\n");
+    } else {
+        TEST_FAIL("Set should not fail with no flags");
+        return;
+    }
+
+    /* Set with invalid flags */
+    status = psa_ps_set(uid, data_len, write_data, INVALID_FLAG);
+    if (status != PSA_ERROR_NOT_SUPPORTED) {
+        TEST_FAIL("Set should not succeed with invalid flags");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests set function with:
+ * - NULL data pointer and zero data length
+ *
+ * \note A request with a null data pointer and data length not equal to zero is
+ *       treated as a secure violation. TF-M framework will reject such requests
+ *       so this case is not tested here.
+ *
+ */
+static void tfm_ps_test_2003(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_3;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const uint32_t data_len = 0;
+
+    /* Set with NULL data pointer */
+    status = psa_ps_set(uid, data_len, NULL, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should succeed with NULL data pointer and zero length");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests set function with:
+ * - Data length longer than maximum permitted
+ */
+static void tfm_ps_test_2004(struct test_result_t *ret)
+{
+#ifndef TFM_PSA_API
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_1;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const uint32_t data_len = INVALID_DATA_LEN;
+    const uint8_t write_data[] = WRITE_DATA;
+
+    /* A parameter with a buffer pointer where its data length is longer than
+     * maximum permitted, it is treated as a secure violation.
+     * TF-M framework rejects the request with a proper error code.
+     * The PS secure PSA PS implementation returns
+     * PSA_ERROR_INVALID_ARGUMENT in that case.
+     */
+
+    /* Set with data length longer than the maximum supported */
+    status = psa_ps_set(uid, data_len, write_data, flags);
+    if (status != PSA_ERROR_INVALID_ARGUMENT) {
+        TEST_FAIL("Set should not succeed with invalid data length");
+        return;
+    }
+
+#endif
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests set function with:
+ * - Write once UID that has already been created
+ */
+static void tfm_ps_test_2005(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = WRITE_ONCE_UID;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const uint32_t write_len = WRITE_DATA_SIZE;
+    const uint32_t read_len = WRITE_ONCE_DATA_SIZE;
+    const uint32_t offset = 0;
+    const uint8_t write_data[] = WRITE_DATA;
+    uint8_t read_data[] = WRITE_ONCE_READ_DATA;
+    size_t read_data_len = 0;
+
+    /* Set a write once UID a second time */
+    status = psa_ps_set(uid, write_len, write_data, flags);
+    if (status != PSA_ERROR_NOT_PERMITTED) {
+        TEST_FAIL("Set should not rewrite a write once UID");
+        return;
+    }
+
+    /* Get write once data */
+    status = psa_ps_get(uid, offset, read_len, read_data + HALF_PADDING_SIZE,
+                        &read_data_len);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Get should not fail");
+        return;
+    }
+
+    /* Check that write once data has not changed */
+    if (tfm_memcmp(read_data, WRITE_ONCE_RESULT_DATA, sizeof(read_data)) != 0) {
+        TEST_FAIL("Write once data should not have changed");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests get function with:
+ * - Valid data, zero offset
+ * - Valid data, non-zero offset
+ */
+static void tfm_ps_test_2006(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_2;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    uint32_t data_len = WRITE_DATA_SIZE;
+    uint32_t offset = 0;
+    const uint8_t write_data[] = WRITE_DATA;
+    uint8_t read_data[] = READ_DATA;
+    const uint8_t *p_read_data = read_data;
+    size_t read_data_len = 0;
+
+    status = psa_ps_set(uid, data_len, write_data, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should not fail");
+        return;
+    }
+
+    /* Get the entire data */
+    status = psa_ps_get(uid, offset, data_len, read_data + HALF_PADDING_SIZE,
+                        &read_data_len);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Get should not fail");
+        return;
+    }
+
+    /* Check that the data is correct, including no illegal pre- or post-data */
+    if (tfm_memcmp(read_data, RESULT_DATA, sizeof(read_data)) != 0) {
+        TEST_FAIL("Read data should be equal to result data");
+        return;
+    }
+
+    /* Reset read data */
+    tfm_memcpy(read_data, READ_DATA, sizeof(read_data));
+
+    /* Read from offset 2 to 2 bytes before end of the data */
+    offset = 2;
+    data_len -= offset + 2;
+
+    status = psa_ps_get(uid, offset, data_len, read_data + HALF_PADDING_SIZE,
+                        &read_data_len);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Get should not fail");
+        return;
+    }
+
+    /* Check that the correct data was read */
+    if (tfm_memcmp(p_read_data, "____", HALF_PADDING_SIZE) != 0) {
+        TEST_FAIL("Read data contains illegal pre-data");
+        return;
+    }
+
+    p_read_data += HALF_PADDING_SIZE;
+
+    if (tfm_memcmp(p_read_data, write_data + offset, data_len) != 0) {
+        TEST_FAIL("Read data incorrect");
+        return;
+    }
+
+    p_read_data += data_len;
+
+    if (tfm_memcmp(p_read_data, "____", HALF_PADDING_SIZE) != 0) {
+        TEST_FAIL("Read data contains illegal post-data");
+        return;
+    }
+
+    /* Call remove to clean up storage for the next test */
+    status = psa_ps_remove(uid);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Remove should not fail with valid UID");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests get function with:
+ * - Zero data length, zero offset
+ * - Zero data length, non-zero offset
+ */
+static void tfm_ps_test_2007(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_3;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const uint32_t write_len = WRITE_DATA_SIZE;
+    const uint32_t read_len = 0;
+    uint32_t offset = 0;
+    const uint8_t write_data[] = WRITE_DATA;
+    uint8_t read_data[] = READ_DATA;
+    size_t read_data_len = 0;
+
+    status = psa_ps_set(uid, write_len, write_data, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should not fail");
+        return;
+    }
+
+    /* Get zero data from zero offset */
+    status = psa_ps_get(uid, offset, read_len, read_data + HALF_PADDING_SIZE,
+                        &read_data_len);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Get should not fail with zero data len");
+        return;
+    }
+
+    /* Check that the read data is unchanged */
+    if (tfm_memcmp(read_data, READ_DATA, sizeof(read_data)) != 0) {
+        TEST_FAIL("Read data should be equal to original read data");
+        return;
+    }
+
+    offset = 5;
+
+    /* Get zero data from non-zero offset */
+    status = psa_ps_get(uid, offset, read_len, read_data + HALF_PADDING_SIZE,
+                        &read_data_len);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Get should not fail");
+        return;
+    }
+
+    /* Check that the read data is unchanged */
+    if (tfm_memcmp(read_data, READ_DATA, sizeof(read_data)) != 0) {
+        TEST_FAIL("Read data should be equal to original read data");
+        return;
+    }
+
+    /* Call remove to clean up storage for the next test */
+    status = psa_ps_remove(uid);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Remove should not fail with valid UID");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests get function with:
+ * - Unset UID
+ * - Invalid UID
+ */
+static void tfm_ps_test_2008(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_1;
+    const uint32_t data_len = 1;
+    const uint32_t offset = 0;
+    uint8_t read_data[] = READ_DATA;
+    size_t read_data_len = 0;
+
+    /* Get with UID that has not yet been set */
+    status = psa_ps_get(uid, offset, data_len, read_data + HALF_PADDING_SIZE,
+                        &read_data_len);
+    if (status != PSA_ERROR_DOES_NOT_EXIST) {
+        TEST_FAIL("Get succeeded with non-existant UID");
+        return;
+    }
+
+    /* Check that the read data is unchanged */
+    if (tfm_memcmp(read_data, READ_DATA, sizeof(read_data)) != 0) {
+        TEST_FAIL("Read data not equal to original read data");
+        return;
+    }
+
+    /* Get with invalid UID */
+    status = psa_ps_get(INVALID_UID, offset, data_len,
+                        read_data + HALF_PADDING_SIZE, &read_data_len);
+    if (status != PSA_ERROR_INVALID_ARGUMENT) {
+        TEST_FAIL("Get succeeded with invalid UID");
+        return;
+    }
+
+    /* Check that the read data is unchanged */
+    if (tfm_memcmp(read_data, READ_DATA, sizeof(read_data)) != 0) {
+        TEST_FAIL("Read data not equal to original read data");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests get function with:
+ * - Offset greater than UID length
+ * - Data length greater than UID length
+ * - Data length + offset greater than UID length
+ * - Invalid data len and offset
+ */
+static void tfm_ps_test_2009(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_2;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const uint32_t write_len = WRITE_DATA_SIZE;
+    uint32_t read_len;
+    uint32_t offset;
+    const uint8_t write_data[] = WRITE_DATA;
+    uint8_t read_data[] = READ_DATA;
+    size_t read_data_len = 0;
+
+    status = psa_ps_set(uid, write_len, write_data, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should not fail");
+        return;
+    }
+
+    /* Get with offset greater than UID's length */
+    read_len = 1;
+    offset = write_len + 1;
+
+    status = psa_ps_get(uid, offset, read_len, read_data + HALF_PADDING_SIZE,
+                        &read_data_len);
+    if (status != PSA_ERROR_INVALID_ARGUMENT) {
+        TEST_FAIL("Get should not succeed with offset too large");
+        return;
+    }
+
+    /* Check that the read data is unchanged */
+    if (tfm_memcmp(read_data, READ_DATA, sizeof(read_data)) != 0) {
+        TEST_FAIL("Read data should be equal to original read data");
+        return;
+    }
+
+    /* Get with data length greater than UID's length */
+    read_len = write_len + 1;
+    offset = 0;
+
+    status = psa_ps_get(uid, offset, read_len, read_data + HALF_PADDING_SIZE,
+                        &read_data_len);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Get should succeed with data length larger than UID's "
+                  "length");
+        return;
+    }
+
+    if (read_data_len != write_len) {
+        TEST_FAIL("Read data length should be equal to UID's length");
+        return;
+    }
+
+    /* Check that the read data is unchanged */
+    if (tfm_memcmp(read_data, RESULT_DATA, sizeof(read_data)) != 0) {
+        TEST_FAIL("Read data should be equal to original read data");
+        return;
+    }
+
+    /* Get with offset + data length greater than UID's length, but individually
+     * valid
+     */
+    read_len = write_len;
+    offset = 1;
+
+    /* Reset read_data to original READ_DATA */
+    tfm_memcpy(read_data, READ_DATA, sizeof(read_data));
+
+    status = psa_ps_get(uid, offset, read_len, read_data + HALF_PADDING_SIZE,
+                        &read_data_len);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Get should succeed with offset + data length too large, "
+                  "but individually valid");
+        return;
+    }
+
+    if (read_data_len != write_len - offset) {
+        TEST_FAIL("Read data length should be equal to the UID's remaining "
+                  "size starting from offset");
+        return;
+    }
+
+    /* Check that the read data is unchanged */
+    if (tfm_memcmp(read_data, OFFSET_RESULT_DATA, sizeof(read_data)) != 0) {
+        TEST_FAIL("Read data should be equal to original read data");
+        return;
+    }
+
+#ifndef TFM_PSA_API
+    /* Get with data length and offset set to invalid values */
+    read_len = INVALID_DATA_LEN;
+    offset = INVALID_OFFSET;
+
+    /* Reset read_data to original READ_DATA */
+    tfm_memcpy(read_data, READ_DATA, sizeof(read_data));
+
+    /* A parameter with a buffer pointer where its data length is longer than
+     * maximum permitted, it is treated as a secure violation.
+     * TF-M framework rejects the request with a proper error code.
+     * The PS secure PSA PS implementation returns
+     * PSA_ERROR_INVALID_ARGUMENT in that case.
+     */
+
+    status = psa_ps_get(uid, offset, read_len, read_data + HALF_PADDING_SIZE,
+                        &read_data_len);
+    if (status != PSA_ERROR_INVALID_ARGUMENT) {
+        TEST_FAIL("Get should not succeed with invalid arguments");
+        return;
+    }
+
+    /* Check that the read data is unchanged */
+    if (tfm_memcmp(read_data, READ_DATA, sizeof(read_data)) != 0) {
+        TEST_FAIL("Read data should be equal to original read data");
+        return;
+    }
+#endif
+
+    /* Call remove to clean up storage for the next test */
+    status = psa_ps_remove(uid);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Remove should not fail with valid UID");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests get function with:
+ * - NULL data pointer and zero data length
+ *
+ * \note A request with a null data pointer and data length not equal to zero is
+ *       treated as a secure violation. TF-M framework will reject such requests
+ *       so this case is not tested here.
+ *
+ */
+static void tfm_ps_test_2010(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_3;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const uint32_t data_len = WRITE_DATA_SIZE;
+    const uint32_t offset = 0;
+    const uint8_t write_data[] = WRITE_DATA;
+    size_t read_data_len = 0;
+
+    status = psa_ps_set(uid, data_len, write_data, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should not fail");
+        return;
+    }
+
+    /* Get with NULL data pointer */
+    status = psa_ps_get(uid, offset, 0, NULL, &read_data_len);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Get should succeed with NULL data pointer and zero length");
+        return;
+    }
+
+    /* Call remove to clean up storage for the next test */
+    status = psa_ps_remove(uid);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Remove should not fail with valid UID");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests get info function with:
+ * - Write once UID
+ */
+static void tfm_ps_test_2011(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = WRITE_ONCE_UID;
+    struct psa_storage_info_t info = {0};
+
+    /* Get info for write once UID */
+    status = psa_ps_get_info(uid, &info);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Get info should not fail for write once UID");
+        return;
+    }
+
+    /* Check that the info struct contains the correct values */
+    if (info.size != WRITE_ONCE_DATA_SIZE) {
+        TEST_FAIL("Size incorrect for write once UID");
+        return;
+    }
+
+    if (info.flags != PSA_STORAGE_FLAG_WRITE_ONCE) {
+        TEST_FAIL("Flags incorrect for write once UID");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests get info function with:
+ * - Valid UID
+ */
+static void tfm_ps_test_2012(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_1;
+    struct psa_storage_info_t info = {0};
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const uint32_t data_len = WRITE_DATA_SIZE;
+    const uint8_t write_data[] = WRITE_DATA;
+
+    status = psa_ps_set(uid, data_len, write_data, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should not fail");
+        return;
+    }
+
+    /* Get info for valid UID */
+    status = psa_ps_get_info(uid, &info);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Get info should not fail with valid UID");
+        return;
+    }
+
+    /* Check that the info struct contains the correct values */
+    if (info.size != data_len) {
+        TEST_FAIL("Size incorrect for valid UID");
+        return;
+    }
+
+    if (info.flags != flags) {
+        TEST_FAIL("Flags incorrect for valid UID");
+        return;
+    }
+
+    /* Call remove to clean up storage for the next test */
+    status = psa_ps_remove(uid);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Remove should not fail with valid UID");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests get info function with:
+ * - Unset UID
+ * - Invalid UID
+ */
+static void tfm_ps_test_2013(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_2;
+    struct psa_storage_info_t info = {0};
+
+    /* Get info with UID that has not yet been set */
+    status = psa_ps_get_info(uid, &info);
+    if (status != PSA_ERROR_DOES_NOT_EXIST) {
+        TEST_FAIL("Get info should not succeed with unset UID");
+        return;
+    }
+
+    /* Check that the info struct has not been modified */
+    if (info.size != 0) {
+        TEST_FAIL("Size should not have changed");
+        return;
+    }
+
+    if (info.flags != 0) {
+        TEST_FAIL("Flags should not have changed");
+        return;
+    }
+
+    /* Get info with invalid UID */
+    status = psa_ps_get_info(INVALID_UID, &info);
+    if (status != PSA_ERROR_INVALID_ARGUMENT) {
+        TEST_FAIL("Get info should not succeed with invalid UID");
+        return;
+    }
+
+    /* Check that the info struct has not been modified */
+    if (info.size != 0) {
+        TEST_FAIL("Size should not have changed");
+        return;
+    }
+
+    if (info.flags != 0) {
+        TEST_FAIL("Flags should not have changed");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests get info function with:
+ * - NULL info pointer
+ */
+static void tfm_ps_test_2014(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_3;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const uint32_t data_len = WRITE_DATA_SIZE;
+    const uint8_t write_data[] = WRITE_DATA;
+
+    status = psa_ps_set(uid, data_len, write_data, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should not fail");
+        return;
+    }
+
+    /* A parameter with a null pointer is treated as a secure violation.
+     * TF-M framework rejects the request with a proper error code.
+     * The PS secure PSA PS implementation returns
+     * PSA_ERROR_GENERIC_ERROR in that case.
+     */
+
+    /* Get info with NULL info pointer */
+#ifndef TFM_PSA_API
+    status = psa_ps_get_info(uid, NULL);
+    if (status != PSA_ERROR_INVALID_ARGUMENT) {
+        TEST_FAIL("Get info should not succeed with NULL info pointer");
+        return;
+    }
+#endif
+
+    /* Call remove to clean up storage for the next test */
+    status = psa_ps_remove(uid);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Remove should not fail with valid UID");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests remove function with:
+ * - Valid UID
+ */
+static void tfm_ps_test_2015(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_1;
+    struct psa_storage_info_t info = {0};
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const uint32_t data_len = WRITE_DATA_SIZE;
+    const uint32_t offset = 0;
+    const uint8_t write_data[] = WRITE_DATA;
+    uint8_t read_data[] = READ_DATA;
+    size_t read_data_len = 0;
+
+    status = psa_ps_set(uid, data_len, write_data, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should not fail");
+        return;
+    }
+
+    /* Call remove with valid ID */
+    status = psa_ps_remove(uid);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Remove should not fail with valid UID");
+        return;
+    }
+
+    /* Check that get info fails for removed UID */
+    status = psa_ps_get_info(uid, &info);
+    if (status != PSA_ERROR_DOES_NOT_EXIST) {
+        TEST_FAIL("Get info should not succeed with removed UID");
+        return;
+    }
+
+    /* Check that get fails for removed UID */
+    status = psa_ps_get(uid, offset, data_len, read_data, &read_data_len);
+    if (status != PSA_ERROR_DOES_NOT_EXIST) {
+        TEST_FAIL("Get should not succeed with removed UID");
+        return;
+    }
+
+    /* Check that remove fails for removed UID */
+    status = psa_ps_remove(uid);
+    if (status != PSA_ERROR_DOES_NOT_EXIST) {
+        TEST_FAIL("Remove should not succeed with removed UID");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests remove function with:
+ * - Write once UID
+ */
+static void tfm_ps_test_2016(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = WRITE_ONCE_UID;
+
+    /* Call remove with write once UID */
+    status = psa_ps_remove(uid);
+    if (status != PSA_ERROR_NOT_PERMITTED) {
+        TEST_FAIL("Remove should not succeed with write once UID");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests remove function with:
+ * - Invalid UID
+ */
+static void tfm_ps_test_2017(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = INVALID_UID;
+
+    /* Call remove with an invalid UID */
+    status = psa_ps_remove(uid);
+    if (status != PSA_ERROR_INVALID_ARGUMENT) {
+        TEST_FAIL("Remove should not succeed with invalid UID");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests data block compact feature.
+ *        Set UID 1 to locate it at the beginning of the block. Then set UID 2
+ *        to be located after UID 1 and remove UID 1. UID 2 will be compacted to
+ *        the beginning of the block. This test verifies that the compaction
+ *        works correctly by reading back UID 2.
+ */
+static void tfm_ps_test_2018(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid_1 = TEST_UID_2;
+    const psa_storage_uid_t uid_2 = TEST_UID_3;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const uint32_t data_len_2 = WRITE_DATA_SIZE;
+    const uint32_t offset = 0;
+    const uint8_t write_data_1[] = "UID 1 DATA";
+    const uint8_t write_data_2[] = WRITE_DATA;
+    uint8_t read_data[] = READ_DATA;
+    size_t read_data_len = 0;
+
+    /* Set UID 1 */
+    status = psa_ps_set(uid_1, sizeof(write_data_1), write_data_1, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should not fail for UID 1");
+        return;
+    }
+
+    /* Set UID 2 */
+    status = psa_ps_set(uid_2, data_len_2, write_data_2, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should not fail for UID 2");
+        return;
+    }
+
+    /* Remove UID 1. This should cause UID 2 to be compacted to the beginning of
+     * the block.
+     */
+    status = psa_ps_remove(uid_1);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Remove should not fail for UID 1");
+        return;
+    }
+
+    /* If the compact worked as expected, the test should be able to read back
+     * the data from UID 2 correctly.
+     */
+    status = psa_ps_get(uid_2, offset, data_len_2,
+                        read_data + HALF_PADDING_SIZE, &read_data_len);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Get should not fail for UID 2");
+        return;
+    }
+
+    if (tfm_memcmp(read_data, RESULT_DATA, sizeof(read_data)) != 0) {
+        TEST_FAIL("Read buffer has incorrect data");
+        return;
+    }
+
+    /* Remove UID 2 to clean up storage for the next test */
+    status = psa_ps_remove(uid_2);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Remove should not fail for UID 2");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests set and multiple partial gets.
+ */
+static void tfm_ps_test_2019(struct test_result_t *ret)
+{
+    psa_status_t status;
+    uint32_t i;
+    const psa_storage_uid_t uid = TEST_UID_1;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const uint32_t data_len = WRITE_DATA_SIZE;
+    uint32_t offset = 0;
+    const uint8_t write_data[] = WRITE_DATA;
+    uint8_t read_data[] = READ_DATA;
+    size_t read_data_len = 0;
+
+    /* Set the entire data into UID */
+    status = psa_ps_set(uid, data_len, write_data, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Set should not fail");
+        return;
+    }
+
+    /* Get the data from UID one byte at a time */
+    for (i = 0; i < data_len; ++i) {
+        status = psa_ps_get(uid, offset, 1,
+                            (read_data + HALF_PADDING_SIZE + i),
+                             &read_data_len);
+        if (status != PSA_SUCCESS) {
+            TEST_FAIL("Get should not fail for partial read");
+            return;
+        }
+
+        ++offset;
+    }
+
+    if (tfm_memcmp(read_data, RESULT_DATA, sizeof(read_data)) != 0) {
+        TEST_FAIL("Read buffer has incorrect data");
+        return;
+    }
+
+    /* Remove UID to clean up storage for the next test */
+    status = psa_ps_remove(uid);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Remove should not fail");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests multiple sets to the same UID.
+ */
+static void tfm_ps_test_2020(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID_2;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const uint32_t offset = 0;
+    const uint8_t write_data_1[] = "ONE";
+    const uint8_t write_data_2[] = "TWO";
+    const uint8_t write_data_3[] = "THREE";
+    uint8_t read_data[] = READ_DATA;
+    size_t read_data_len = 0;
+
+    /* Set write data 1 into UID */
+    status = psa_ps_set(uid, sizeof(write_data_1), write_data_1, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("First set should not fail");
+        return;
+    }
+
+    /* Set write data 2 into UID */
+    status = psa_ps_set(uid, sizeof(write_data_2), write_data_2, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Second set should not fail");
+        return;
+    }
+
+    /* Set write data 3 into UID */
+    status = psa_ps_set(uid, sizeof(write_data_3), write_data_3, flags);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Third set should not fail");
+        return;
+    }
+
+    status = psa_ps_get(uid, offset, sizeof(write_data_3), read_data,
+                        &read_data_len);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Get should not fail");
+        return;
+    }
+
+    /* Check that get returns the last data to be set */
+    if (tfm_memcmp(read_data, write_data_3, sizeof(write_data_3)) != 0) {
+        TEST_FAIL("Read buffer has incorrect data");
+        return;
+    }
+
+    /* Remove UID to clean up storage for the next test */
+    status = psa_ps_remove(uid);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Remove should not fail");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests get support function.
+ */
+static void tfm_ps_test_2021(struct test_result_t *ret)
+{
+    uint32_t support_flags;
+
+    support_flags = psa_ps_get_support();
+    if (support_flags != 0) {
+        TEST_FAIL("Support flags should be 0");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests set, get_info, get and remove function with:
+ * - Valid UID's
+ * - Data length of different asset sizes
+ * - No flags
+ */
+static void tfm_ps_test_2022(struct test_result_t *ret)
+{
+    uint8_t cycle;
+    psa_status_t status;
+    const psa_storage_uid_t test_uid[TEST_1022_CYCLES] = {
+        TEST_UID_1,
+        TEST_UID_2,
+        TEST_UID_3};
+    const uint32_t test_asset_sizes[TEST_1022_CYCLES] = {
+        PS_MAX_ASSET_SIZE >> 2,
+        PS_MAX_ASSET_SIZE >> 1,
+        PS_MAX_ASSET_SIZE};
+
+    /* Loop to test different asset sizes and UID's*/
+    for (cycle = 0; cycle < TEST_1022_CYCLES; cycle++) {
+        uint32_t data_size = test_asset_sizes[cycle];
+        psa_storage_uid_t uid = test_uid[cycle];
+        struct psa_storage_info_t info = {0};
+
+        tfm_memset(read_asset_data, 0x00, sizeof(read_asset_data));
+
+        /* Set with data and no flags and a valid UID */
+        status = psa_ps_set(uid,
+                            data_size,
+                            write_asset_data,
+                            PSA_STORAGE_FLAG_NONE);
+        if (status != PSA_SUCCESS) {
+            TEST_FAIL("Set should not fail with valid UID");
+            return;
+        }
+
+        /* Get info for valid UID */
+        status = psa_ps_get_info(uid, &info);
+        if (status != PSA_SUCCESS) {
+            TEST_FAIL("Get info should not fail with valid UID");
+            return;
+        }
+
+        /* Check that the info struct contains the correct values */
+        if (info.size != data_size) {
+            TEST_FAIL("Size incorrect for valid UID");
+            return;
+        }
+
+        if (info.flags != PSA_STORAGE_FLAG_NONE) {
+            TEST_FAIL("Flags incorrect for valid UID");
+            return;
+        }
+
+        /* Check that thread can still get UID */
+        status = psa_ps_get(uid, 0, data_size, read_asset_data,
+                            &read_asset_datal_len);
+        if (status != PSA_SUCCESS) {
+            TEST_FAIL("Get should not fail with valid UID");
+            return;
+        }
+
+        /* Check that thread's UID data has not been modified */
+        if (tfm_memcmp(read_asset_data, write_asset_data, data_size) != 0) {
+            TEST_FAIL("Read data should be equal to original write data");
+            return;
+        }
+
+        /* Call remove to clean up storage for the next test */
+        status = psa_ps_remove(uid);
+        if (status != PSA_SUCCESS) {
+            TEST_FAIL("Remove should not fail with valid UID");
+            return;
+        }
+    }
+
+    ret->val = TEST_PASSED;
+}
diff --git a/test/suites/ps/secure/psa_ps_s_reliability_testsuite.c b/test/suites/ps/secure/psa_ps_s_reliability_testsuite.c
new file mode 100644
index 0000000..658f4f5
--- /dev/null
+++ b/test/suites/ps/secure/psa_ps_s_reliability_testsuite.c
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2017-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "ps_tests.h"
+
+#include <stdio.h>
+
+#include "psa/protected_storage.h"
+#include "tfm_memory_utils.h"
+#include "s_test_helpers.h"
+#include "test/framework/test_framework_helpers.h"
+
+/* Test UIDs */
+#define TEST_UID 2UL  /* UID 1 cannot be used as it references a write once
+                       * asset, created in psa_ps_s_interface_testsuite.c
+                       */
+
+/* Test suite defines */
+#define LOOP_ITERATIONS_001 15U
+#define LOOP_ITERATIONS_002 15U
+
+/* Write data */
+#define WRITE_DATA       "THE_FIVE_BOXING_WIZARDS_JUMP_QUICKLY"
+#define WRITE_DATA_SIZE  (sizeof(WRITE_DATA) - 1)
+#define READ_DATA        "############################################"
+#define RESULT_DATA      ("####" WRITE_DATA "####")
+
+/* Define test suite for PS reliability tests */
+/* List of tests */
+static void tfm_ps_test_3001(struct test_result_t *ret);
+static void tfm_ps_test_3002(struct test_result_t *ret);
+
+static struct test_t reliability_tests[] = {
+    {&tfm_ps_test_3001, "TFM_PS_TEST_3001",
+     "repetitive sets and gets in/from an asset", {TEST_PASSED} },
+    {&tfm_ps_test_3002, "TFM_PS_TEST_3002",
+     "repetitive sets, gets and removes", {TEST_PASSED} },
+};
+
+void register_testsuite_s_psa_ps_reliability(struct test_suite_t *p_test_suite)
+{
+    uint32_t list_size = (sizeof(reliability_tests) /
+                          sizeof(reliability_tests[0]));
+
+    set_testsuite("PS reliability tests (TFM_PS_TEST_3XXX)",
+                  reliability_tests, list_size, p_test_suite);
+}
+
+/**
+ * \brief Tests repetitive sets and gets in/from an asset.
+ */
+static void tfm_ps_test_3001(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const uint32_t data_len = WRITE_DATA_SIZE;
+    const uint32_t offset = 0;
+    uint32_t itr;
+    const uint8_t write_data[] = WRITE_DATA;
+    uint8_t read_data[] = READ_DATA;
+    size_t read_data_len = 0;
+
+    for (itr = 0; itr < LOOP_ITERATIONS_001; itr++) {
+        TEST_LOG("  > Iteration %d of %d\r", itr + 1, LOOP_ITERATIONS_001);
+
+        /* Set a data in the asset */
+        status = psa_ps_set(uid, data_len, write_data, flags);
+        if (status != PSA_SUCCESS) {
+            TEST_FAIL("Set should not fail with valid UID");
+            return;
+        }
+
+        /* Get data from the asset */
+        status = psa_ps_get(uid, offset, data_len, (read_data +
+                                                    HALF_PADDING_SIZE),
+                                                    &read_data_len);
+        if (status != PSA_SUCCESS) {
+            TEST_FAIL("Get should not fail");
+            return;
+        }
+
+        /* Check that the data has not changed */
+        if (tfm_memcmp(read_data, RESULT_DATA, sizeof(read_data)) != 0) {
+            TEST_FAIL("The data should not have changed");
+            return;
+        }
+
+        /* Set the original data into read buffer */
+        tfm_memcpy(read_data, READ_DATA, sizeof(read_data));
+    }
+
+    TEST_LOG("\n");
+
+    /* Remove the asset to clean up storage for the next test */
+    status = psa_ps_remove(uid);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Remove should not fail with valid UID");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+/**
+ * \brief Tests repetitive sets, gets and removes.
+ */
+static void tfm_ps_test_3002(struct test_result_t *ret)
+{
+    psa_status_t status;
+    const psa_storage_uid_t uid = TEST_UID;
+    const psa_storage_create_flags_t flags = PSA_STORAGE_FLAG_NONE;
+    const uint32_t data_len = WRITE_DATA_SIZE;
+    const uint32_t offset = 0;
+    uint32_t itr;
+    const uint8_t write_data[] = WRITE_DATA;
+    uint8_t read_data[] = READ_DATA;
+    size_t read_data_len = 0;
+
+    for (itr = 0; itr < LOOP_ITERATIONS_002; itr++) {
+        TEST_LOG("  > Iteration %d of %d\r", itr + 1, LOOP_ITERATIONS_002);
+
+        /* Set a data in the asset */
+        status = psa_ps_set(uid, data_len, write_data, flags);
+        if (status != PSA_SUCCESS) {
+            TEST_FAIL("Set should not fail with valid UID");
+            return;
+        }
+
+        /* Get data from the asset */
+        status = psa_ps_get(uid, offset, data_len, (read_data +
+                                                    HALF_PADDING_SIZE),
+                                                    &read_data_len);
+        if (status != PSA_SUCCESS) {
+            TEST_FAIL("Get should not fail");
+            return;
+        }
+
+        /* Check that the data has not changed */
+        if (tfm_memcmp(read_data, RESULT_DATA, sizeof(read_data)) != 0) {
+            TEST_FAIL("The data should not have changed");
+            return;
+        }
+
+        /* Remove the asset from the protected storage */
+        status = psa_ps_remove(uid);
+        if (status != PSA_SUCCESS) {
+            TEST_FAIL("Remove should not fail with valid UID");
+            return;
+        }
+
+        /* Set the original data into read buffer */
+        tfm_memcpy(read_data, READ_DATA, sizeof(read_data));
+    }
+
+    TEST_LOG("\n");
+
+    ret->val = TEST_PASSED;
+}
diff --git a/test/suites/ps/secure/s_test_helpers.h b/test/suites/ps/secure/s_test_helpers.h
new file mode 100644
index 0000000..1da7390
--- /dev/null
+++ b/test/suites/ps/secure/s_test_helpers.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2017-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __S_TEST_HELPERS_H__
+#define __S_TEST_HELPERS_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Several tests use a buffer to read back data from an asset. This buffer is
+ * larger than the size of the asset data by PADDING_SIZE bytes. This allows
+ * us to ensure that the only the expected data is read back and that it is read
+ * back correctly.
+ *
+ * For example if the buffer and asset are as follows:
+ * Buffer - "XXXXXXXXXXXX", Asset data - "AAAA"
+ *
+ * Then a correct and successful read would give this result: "XXXXAAAAXXXX"
+ * (Assuming a PADDING_SIZE of 8)
+ */
+#define BUFFER_SIZE 24
+#define PADDING_SIZE 8
+#define HALF_PADDING_SIZE 4
+
+#define BUFFER_PLUS_PADDING_SIZE (BUFFER_SIZE + PADDING_SIZE)
+#define BUFFER_PLUS_HALF_PADDING_SIZE (BUFFER_SIZE + HALF_PADDING_SIZE)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __S_TEST_HELPERS_H__ */
diff --git a/test/suites/qcbor/CMakeLists.inc b/test/suites/qcbor/CMakeLists.inc
new file mode 100644
index 0000000..d8fa1f7
--- /dev/null
+++ b/test/suites/qcbor/CMakeLists.inc
@@ -0,0 +1,36 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+#Definitions to compile the QCBOR module.
+#This file assumes it will be included from a project specific cmakefile, and
+#will not create a library or executable.
+#Inputs:
+#	TFM_ROOT_DIR - root directory of the TF-M repo.
+#
+#Outputs:
+#	Will modify include directories to make the source compile.
+#	ALL_SRC_C: C source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+#	ALL_SRC_CXX: C++ source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+#	ALL_SRC_ASM: assembly source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+#	Include directories will be modified by using the include_directories() commands as needed.
+
+#Get the current directory where this file is located.
+set(QCBOR_TEST_SUIT_DIR ${CMAKE_CURRENT_LIST_DIR})
+
+#Verify input parameters
+if(NOT DEFINED TFM_ROOT_DIR)
+	message(FATAL_ERROR "Please set TFM_ROOT_DIR before including this file.")
+endif()
+
+if (NOT DEFINED ENABLE_QCBOR_TESTS)
+	message(FATAL_ERROR "Incomplete build configuration: ENABLE_QCBOR_TESTS is undefined.")
+elseif(ENABLE_QCBOR_TESTS)
+	list(APPEND ALL_SRC_C_NS
+		"${QCBOR_TEST_SUIT_DIR}/non_secure/qcbor_ns_testsuite.c"
+		$<TARGET_OBJECTS:tfm_qcbor_test>   #See in lib/ext/qcbor/CMakeList.txt
+	)
+endif()
diff --git a/test/suites/qcbor/non_secure/qcbor_ns_tests.h b/test/suites/qcbor/non_secure/qcbor_ns_tests.h
new file mode 100644
index 0000000..03d2e46
--- /dev/null
+++ b/test/suites/qcbor/non_secure/qcbor_ns_tests.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __QCBOR_NS_TESTS_H__
+#define __QCBOR_NS_TESTS_H__
+
+#include "test/framework/test_framework.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Register test suite for the QCBOR library
+ *
+ * \param[in] p_test_suite The test suite to be executed.
+ */
+void
+register_testsuite_ns_qcbor(struct test_suite_t *p_test_suite);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __QCBOR_NS_TESTS_H__ */
diff --git a/test/suites/qcbor/non_secure/qcbor_ns_testsuite.c b/test/suites/qcbor/non_secure/qcbor_ns_testsuite.c
new file mode 100644
index 0000000..f44994b
--- /dev/null
+++ b/test/suites/qcbor/non_secure/qcbor_ns_testsuite.c
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "qcbor_ns_tests.h"
+#include <stdio.h>
+#include "lib/ext/qcbor/test/run_tests.h"
+
+/*
+ * The QCBOR test cases are defined in: lib/ext/qcbor/test/run_test.c
+ *
+ * There are two types of configuration to execute test cases:
+ *  - All tests: Just pass an array containing a NULL pointer to RunTest():
+ *               qcbor_test_cases_all[]. This is the default configuration.
+ *
+ *  - Selected tests: qcbor_test_cases[] array contains the name of all tests.
+ *                    To execute only selected test cases, just remove/comment
+ *                    out the unwanted tests from the array and pass it to
+ *                    RunTests(). This configuration disables all tests with a
+ *                    long execution time to speed up development.
+ */
+
+/* Define test suite for QCBOR library */
+static void tfm_qcbor_test_7001(struct test_result_t *ret);
+
+static struct test_t qcbor_regression_test[] = {
+    {&tfm_qcbor_test_7001, "TFM_QCBOR_TEST_7001",
+     "Regression test of QCBOR library", {TEST_PASSED} },
+};
+
+/* To execute only selected test cases, then remove unwanted ones from the array
+ * and pass it to RunTests().
+ * Not static to avoid compiler warning due to non-usage
+ */
+const char *qcbor_test_cases[] = {
+    "ParseMapAsArrayTest",
+    "AllocAllStringsTest",
+    "IndefiniteLengthNestTest",
+    "NestedMapTestIndefLen",
+    "ParseSimpleTest",
+    "EncodeRawTest",
+    "RTICResultsTest",
+    "MapEncodeTest",
+    "ArrayNestingTest1",
+    "ArrayNestingTest2",
+    "ArrayNestingTest3",
+    "EncodeDateTest",
+    "SimpleValuesTest1",
+    "IntegerValuesTest1",
+    "AllAddMethodsTest",
+    "ParseTooDeepArrayTest",
+    "ComprehensiveInputTest",
+    "ParseMapTest",
+    "IndefiniteLengthArrayMapTest",
+    "BasicEncodeTest",
+    "NestedMapTest",
+    "BignumParseTest",
+    "OptTagParseTest",
+    "DateParseTest",
+    "ShortBufferParseTest2",
+    "ShortBufferParseTest",
+    "ParseDeepArrayTest",
+    "SimpleArrayTest",
+    "IntegerValuesParseTest",
+    "MemPoolTest",
+    "IndefiniteLengthStringTest", /**/
+    "HalfPrecisionDecodeBasicTests",
+    "DoubleAsSmallestTest",
+    "HalfPrecisionAgainstRFCCodeTest",
+    "BstrWrapTest",
+    "BstrWrapErrorTest",
+    "BstrWrapNestTest", /**/
+    "CoseSign1TBSTest",
+    "StringDecoderModeFailTest",
+    /* "BigComprehensiveInputTest", */ /* Takes too long to execute */
+    "EncodeErrorTests",
+    "UBUTest_CopyUtil",
+    "UOBTest_NonAdversarial",
+    "TestBasicSanity",
+    "UOBTest_BoundaryConditionsTest",
+    "UBMacroConversionsTest",
+    "UBUtilTests",
+    "UIBTest_IntegerFormat",
+};
+
+/* To execute all test cases, then pass this array to RunTests()
+ * Test cases are defined in: lib/ext/qcbor/test/run_tests.c
+ */
+const static char *qcbor_test_cases_all[] = {
+    0, /* Indicates to run all enabled test case */
+};
+
+/**
+ * \brief Print QCBOR test results per test case
+ *
+ * \param[in]  szString  String to print out
+ * \param[in]  ctx       Where to print
+ */
+
+static void fputs_wrapper(const char *string, void *out_ctx, int new_line)
+{
+    (void)out_ctx;
+    (void)new_line;
+    /*
+     * To get test result per test case, change
+     * the preprocessor '#if 0' to '#if 1'.
+     */
+# if 0
+    TEST_LOG("%s\r\n", string);
+#else
+    (void)string;
+#endif
+}
+
+static void tfm_qcbor_test_7001(struct test_result_t *ret)
+{
+    int32_t test_failed_cnt = 0;
+
+    test_failed_cnt = RunTestsQCBOR(qcbor_test_cases_all, fputs_wrapper,
+                                    NULL, NULL);
+    if (test_failed_cnt != 0) {
+        TEST_FAIL("QCBOR test failed");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+void
+register_testsuite_ns_qcbor(struct test_suite_t *p_test_suite)
+{
+    uint32_t list_size;
+
+    list_size = (sizeof(qcbor_regression_test) /
+                 sizeof(qcbor_regression_test[0]));
+
+    set_testsuite("QCBOR regression test"
+                  "(TFM_QCBOR_TEST_7XXX)",
+                  qcbor_regression_test, list_size, p_test_suite);
+}
diff --git a/test/suites/t_cose/CMakeLists.inc b/test/suites/t_cose/CMakeLists.inc
new file mode 100644
index 0000000..c030683
--- /dev/null
+++ b/test/suites/t_cose/CMakeLists.inc
@@ -0,0 +1,36 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2020, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+#Definitions to compile the T_COSE module.
+#This file assumes it will be included from a project specific cmakefile, and
+#will not create a library or executable.
+#Inputs:
+#	TFM_ROOT_DIR - root directory of the TF-M repo.
+#
+#Outputs:
+#	Will modify include directories to make the source compile.
+#	ALL_SRC_C: C source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+#	ALL_SRC_CXX: C++ source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+#	ALL_SRC_ASM: assembly source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+#	Include directories will be modified by using the include_directories() commands as needed.
+
+#Get the current directory where this file is located.
+set(T_COSE_TEST_SUIT_DIR ${CMAKE_CURRENT_LIST_DIR})
+
+#Verify input parameters
+if(NOT DEFINED TFM_ROOT_DIR)
+	message(FATAL_ERROR "Please set TFM_ROOT_DIR before including this file.")
+endif()
+
+if (NOT DEFINED ENABLE_T_COSE_TESTS)
+	message(FATAL_ERROR "Incomplete build configuration: ENABLE_T_COSE_TESTS is undefined.")
+elseif(ENABLE_T_COSE_TESTS)
+	list(APPEND ALL_SRC_C_NS
+		"${T_COSE_TEST_SUIT_DIR}/non_secure/t_cose_ns_testsuite.c"
+		$<TARGET_OBJECTS:tfm_t_cose_test>   #See in lib/ext/t_cose/CMakeList.txt
+	)
+endif()
diff --git a/test/suites/t_cose/non_secure/t_cose_ns_tests.h b/test/suites/t_cose/non_secure/t_cose_ns_tests.h
new file mode 100644
index 0000000..8313797
--- /dev/null
+++ b/test/suites/t_cose/non_secure/t_cose_ns_tests.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __T_COSE_NS_TESTS_H__
+#define __T_COSE_NS_TESTS_H__
+
+#include "test/framework/test_framework.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Register test suite for the t_cose library
+ *
+ * \param[in] p_test_suite The test suite to be executed.
+ */
+void
+register_testsuite_ns_t_cose(struct test_suite_t *p_test_suite);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __T_COSE_NS_TESTS_H__ */
diff --git a/test/suites/t_cose/non_secure/t_cose_ns_testsuite.c b/test/suites/t_cose/non_secure/t_cose_ns_testsuite.c
new file mode 100644
index 0000000..3428a08
--- /dev/null
+++ b/test/suites/t_cose/non_secure/t_cose_ns_testsuite.c
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "t_cose_ns_tests.h"
+#include <stdio.h>
+#include "lib/ext/t_cose/test/run_tests.h"
+
+/*
+ * The t_cose test cases are defined in: lib/ext/t_cose/test/run_test.c
+ *
+ * There are two types of configuration to execute test cases:
+ *  - All tests: Just pass an array containing a NULL pointer to RunTest():
+ *               t_cose_test_cases_all[]. This is the default configuration.
+ *
+ *  - Selected tests: Edit the s_tests[] array in
+ *                    lib/ext/t_cose/test/run_tests.c.
+ */
+
+/* Define test suite for t_cose library */
+static void tfm_t_cose_test_8001(struct test_result_t *ret);
+
+static struct test_t t_cose_regression_test[] = {
+    {&tfm_t_cose_test_8001, "TFM_T_COSE_TEST_8001",
+     "Regression test of t_cose library", {TEST_PASSED} },
+};
+
+/* To execute all test cases, then pass this array to RunTestsTCose()
+ * Test cases are defined in: lib/ext/t_cose/test/run_tests.c
+ */
+const static char *t_cose_test_cases_all[] = {
+    0, /* Indicates to run all enabled test case */
+};
+
+/**
+ * \brief Print t_cose test results per test case
+ *
+ * \param[in]  szString  String to print out
+ * \param[in]  ctx       Where to print
+ */
+
+static void fputs_wrapper(const char *string, void *out_ctx, int new_line)
+{
+    (void)out_ctx;
+    (void)new_line;
+    /*
+     * To get test result per test case, change
+     * the preprocessor '#if 0' to '#if 1'.
+     */
+# if 0
+    TEST_LOG("%s\r\n", string);
+#else
+    (void)string;
+#endif
+}
+
+static void tfm_t_cose_test_8001(struct test_result_t *ret)
+{
+    int32_t test_failed_cnt = 0;
+
+    test_failed_cnt = RunTestsTCose(t_cose_test_cases_all, fputs_wrapper,
+                                    NULL, NULL);
+    if (test_failed_cnt != 0) {
+        TEST_FAIL("t_cose test failed");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+void
+register_testsuite_ns_t_cose(struct test_suite_t *p_test_suite)
+{
+    uint32_t list_size;
+
+    list_size = (sizeof(t_cose_regression_test) /
+                 sizeof(t_cose_regression_test[0]));
+
+    set_testsuite("T_COSE regression test"
+                  "(TFM_T_COSE_TEST_8XXX)",
+                  t_cose_regression_test, list_size, p_test_suite);
+}
diff --git a/test/test_services/CMakeLists.inc b/test/test_services/CMakeLists.inc
new file mode 100644
index 0000000..f4f0482
--- /dev/null
+++ b/test/test_services/CMakeLists.inc
@@ -0,0 +1,101 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2017-2020, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+#Definitions to compile the "secure services" module.
+#This file assumes it will be included from a project specific cmakefile, and
+#will not create a library or executable.
+#Inputs:
+#	TFM_ROOT_DIR - root directory of the TF-M repository.
+#
+#Outputs:
+#	Will modify include directories to make the source compile.
+#	ALL_SRC_C: C source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+#	ALL_SRC_CXX: C++ source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+#	ALL_SRC_ASM: assembly source files to be compiled will be added to this list. This shall be added to your add_executable or add_library command.
+#	Include directories will be modified by using the include_directories() commands as needed.
+
+#Get the current directory where this file is located.
+set(CORE_TEST_DIR ${CMAKE_CURRENT_LIST_DIR})
+
+#Check input variables
+if (NOT DEFINED TFM_ROOT_DIR)
+	message(FATAL_ERROR "Please set TFM_ROOT_DIR before including this file.")
+endif()
+
+if (NOT DEFINED ENABLE_CORE_TESTS)
+	message(FATAL_ERROR "Incomplete build configuration: ENABLE_CORE_TESTS is undefined. ")
+elseif(ENABLE_CORE_TESTS)
+	list(APPEND ALL_SRC_C_S "${CORE_TEST_DIR}/tfm_core_test/tfm_ss_core_test.c")
+endif()
+
+if (NOT DEFINED ENABLE_CORE_TESTS_2)
+	message(FATAL_ERROR "Incomplete build configuration: ENABLE_CORE_TESTS_2 is undefined. ")
+elseif(ENABLE_CORE_TESTS_2)
+	list(APPEND ALL_SRC_C_S "${CORE_TEST_DIR}/tfm_core_test_2/tfm_ss_core_test_2.c")
+endif()
+
+if (NOT DEFINED ENABLE_IRQ_TEST_SERVICES)
+	message(FATAL_ERROR "Incomplete build configuration: ENABLE_IRQ_TEST_SERVICES is undefined. ")
+elseif(ENABLE_IRQ_TEST_SERVICES)
+	list(APPEND ALL_SRC_C_S
+		"${CORE_TEST_DIR}/tfm_irq_test_service_1/tfm_irq_test_service_1.c")
+endif()
+
+if (NOT DEFINED TFM_PARTITION_TEST_SECURE_SERVICES)
+	message(FATAL_ERROR "Incomplete build configuration: TFM_PARTITION_TEST_SECURE_SERVICES is undefined. ")
+elseif (TFM_PARTITION_TEST_SECURE_SERVICES)
+	list(APPEND ALL_SRC_C_S "${CORE_TEST_DIR}/tfm_secure_client_service/tfm_secure_client_service.c"
+		"${CORE_TEST_DIR}/tfm_secure_client_2/tfm_secure_client_2.c"
+		"${CORE_TEST_DIR}/tfm_secure_client_2/tfm_secure_client_2_api.c")
+
+	list(APPEND ALL_SRC_C_NS "${CORE_TEST_DIR}/tfm_secure_client_service/tfm_secure_client_service_api.c")
+endif()
+
+if (NOT DEFINED TFM_PARTITION_TEST_CORE_IPC)
+	message(FATAL_ERROR "Incomplete build configuration: TFM_PARTITION_TEST_CORE_IPC is undefined. ")
+elseif (TFM_PARTITION_TEST_CORE_IPC)
+	list(APPEND ALL_SRC_C_S "${CORE_TEST_DIR}/tfm_ipc_service/tfm_ipc_service_test.c"
+		"${CORE_TEST_DIR}/tfm_ipc_client/tfm_ipc_client_test.c"
+		)
+endif()
+
+if (NOT DEFINED TFM_PARTITION_TEST_PS)
+	message(FATAL_ERROR "Incomplete build configuration: TFM_PARTITION_TEST_PS is undefined.")
+elseif (TFM_PARTITION_TEST_PS)
+	list(APPEND ALL_SRC_C_S "${CORE_TEST_DIR}/tfm_ps_test_service/tfm_ps_test_service.c"
+		"${CORE_TEST_DIR}/tfm_ps_test_service/tfm_ps_test_service_api.c")
+endif()
+
+embedded_include_directories(PATH ${TFM_ROOT_DIR} ABSOLUTE)
+embedded_include_directories(PATH ${TFM_ROOT_DIR}/interface/include ABSOLUTE)
+embedded_include_directories(PATH ${TFM_ROOT_DIR}/platform/include ABSOLUTE)
+embedded_include_directories(PATH ${TFM_ROOT_DIR}/secure_fw/include ABSOLUTE)
+
+set(BUILD_CMSIS_CORE Off)
+set(BUILD_RETARGET Off)
+set(BUILD_NATIVE_DRIVERS Off)
+set(BUILD_STARTUP Off)
+set(BUILD_TARGET_CFG Off)
+set(BUILD_TARGET_HARDWARE_KEYS Off)
+set(BUILD_TARGET_NV_COUNTERS Off)
+set(BUILD_CMSIS_DRIVERS Off)
+set(BUILD_UART_STDOUT Off)
+set(BUILD_FLASH Off)
+if (CORE_TEST_POSITIVE OR CORE_TEST_INTERACTIVE)
+	set(BUILD_PLAT_TEST On)
+	set(BUILD_TIME On)
+else()
+	set(BUILD_PLAT_TEST Off)
+	set(BUILD_TIME Off)
+endif()
+if(NOT DEFINED PLATFORM_CMAKE_FILE)
+	message (FATAL_ERROR "Platform specific CMake is not defined. Please set PLATFORM_CMAKE_FILE.")
+elseif(NOT EXISTS ${PLATFORM_CMAKE_FILE})
+	message (FATAL_ERROR "Platform specific CMake \"${PLATFORM_CMAKE_FILE}\" file does not exist. Please fix value of PLATFORM_CMAKE_FILE.")
+else()
+	include(${PLATFORM_CMAKE_FILE})
+endif()
diff --git a/test/test_services/tfm_core_test/core_test_defs.h b/test/test_services/tfm_core_test/core_test_defs.h
new file mode 100644
index 0000000..819d2a8
--- /dev/null
+++ b/test/test_services/tfm_core_test/core_test_defs.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2017-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __CORE_TEST_DEFS_H__
+#define __CORE_TEST_DEFS_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <inttypes.h>
+#include <limits.h>
+#include "tfm_api.h"
+
+/* These definitions are used in symbols, only digits are permitted */
+#define CORE_TEST_ID_NS_THREAD            1001
+#define CORE_TEST_ID_CHECK_INIT           1003
+#define CORE_TEST_ID_RECURSION            1004
+#define CORE_TEST_ID_BUFFER_CHECK         1007
+#define CORE_TEST_ID_SS_TO_SS             1008
+#define CORE_TEST_ID_SS_TO_SS_BUFFER      1010
+#define CORE_TEST_ID_PERIPHERAL_ACCESS    1012
+#define CORE_TEST_ID_GET_CALLER_CLIENT_ID 1013
+#define CORE_TEST_ID_SPM_REQUEST          1014
+#define CORE_TEST_ID_IOVEC_SANITIZATION   1015
+#define CORE_TEST_ID_OUTVEC_WRITE         1016
+#define CORE_TEST_ID_SECURE_IRQ           1017
+#define CORE_TEST_ID_BLOCK                2001
+
+enum irq_test_scenario_t {
+    IRQ_TEST_SCENARIO_NONE,
+    IRQ_TEST_SCENARIO_1,
+    IRQ_TEST_SCENARIO_2,
+    IRQ_TEST_SCENARIO_3,
+    IRQ_TEST_SCENARIO_4,
+    IRQ_TEST_SCENARIO_5,
+};
+
+struct irq_test_execution_data_t {
+    volatile int32_t timer0_triggered;
+    volatile int32_t timer1_triggered;
+};
+
+/* Use lower 16 bits in return value for error code, upper 16 for line number
+ * in test service
+ */
+#define CORE_TEST_RETURN_ERROR(x) return (((__LINE__) << 16) | x)
+#define CORE_TEST_ERROR_GET_EXTRA(x) (x >> 16)
+#define CORE_TEST_ERROR_GET_CODE(x) (x & 0xFFFF)
+
+enum core_test_errno_t {
+    CORE_TEST_ERRNO_TEST_NOT_SUPPORTED         = -13,
+    CORE_TEST_ERRNO_SP_NOT_INITED              = -12,
+    CORE_TEST_ERRNO_UNEXPECTED_CORE_BEHAVIOUR  = -11,
+    CORE_TEST_ERRNO_SP_RECURSION_NOT_REJECTED  = -10,
+    CORE_TEST_ERRNO_INVALID_BUFFER             = -9,
+    CORE_TEST_ERRNO_SLAVE_SP_CALL_FAILURE      = -8,
+    CORE_TEST_ERRNO_SLAVE_SP_BUFFER_FAILURE    = -7,
+    CORE_TEST_ERRNO_FIRST_CALL_FAILED          = -6,
+    CORE_TEST_ERRNO_SECOND_CALL_FAILED         = -5,
+    CORE_TEST_ERRNO_PERIPHERAL_ACCESS_FAILED   = -4,
+    CORE_TEST_ERRNO_TEST_FAULT                 = -3,
+    CORE_TEST_ERRNO_INVALID_TEST_ID            = -2,
+    CORE_TEST_ERRNO_INVALID_PARAMETER          = -1,
+
+    CORE_TEST_ERRNO_SUCCESS                    =  0,
+
+    CORE_TEST_ERRNO_SUCCESS_2                  =  1,
+
+    /* Following entry is only to ensure the error code of int size */
+    CORE_TEST_ERRNO_FORCE_INT_SIZE = INT_MAX
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CORE_TEST_DEFS_H__ */
diff --git a/test/test_services/tfm_core_test/psa_manifest/tfm_test_core.h b/test/test_services/tfm_core_test/psa_manifest/tfm_test_core.h
new file mode 100644
index 0000000..e133c46
--- /dev/null
+++ b/test/test_services/tfm_core_test/psa_manifest/tfm_test_core.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+/*********** WARNING: This is an auto-generated file. Do not edit! ***********/
+
+#ifndef __PSA_MANIFEST_TFM_TEST_CORE_H__
+#define __PSA_MANIFEST_TFM_TEST_CORE_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SPM_CORE_TEST_INIT_SUCCESS_SIGNAL                       (1U << (0 + 4))
+#define SPM_CORE_TEST_DIRECT_RECURSION_SIGNAL                   (1U << (1 + 4))
+#define SPM_CORE_TEST_SS_TO_SS_SIGNAL                           (1U << (2 + 4))
+#define SPM_CORE_TEST_SS_TO_SS_BUFFER_SIGNAL                    (1U << (3 + 4))
+#define SPM_CORE_TEST_OUTVEC_WRITE_SIGNAL                       (1U << (4 + 4))
+#define SPM_CORE_TEST_PERIPHERAL_ACCESS_SIGNAL                  (1U << (5 + 4))
+#define SPM_CORE_TEST_GET_CALLER_CLIENT_ID_SIGNAL               (1U << (6 + 4))
+#define SPM_CORE_TEST_SPM_REQUEST_SIGNAL                        (1U << (7 + 4))
+#define SPM_CORE_TEST_BLOCK_SIGNAL                              (1U << (8 + 4))
+#define SPM_CORE_TEST_NS_THREAD_SIGNAL                          (1U << (9 + 4))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __PSA_MANIFEST_TFM_TEST_CORE_H__ */
diff --git a/test/test_services/tfm_core_test/tfm_ss_core_test.c b/test/test_services/tfm_core_test/tfm_ss_core_test.c
new file mode 100644
index 0000000..7390fc3
--- /dev/null
+++ b/test/test_services/tfm_core_test/tfm_ss_core_test.c
@@ -0,0 +1,593 @@
+/*
+ * Copyright (c) 2017-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <stddef.h>
+#include "tfm_ss_core_test.h"
+#include "tfm_api.h"
+#include "test/test_services/tfm_core_test/core_test_defs.h"
+#include "test/framework/test_framework.h"
+#include "tfm_veneers.h"
+#include "tfm_secure_api.h"
+#include "secure_fw/include/tfm/tfm_spm_services.h"
+#include "psa/service.h"
+#include "tfm_plat_test.h"
+#include "psa_manifest/pid.h"
+#include "psa_manifest/tfm_test_core.h"
+#ifdef TFM_PSA_API
+#include "psa_manifest/sid.h"
+#endif
+
+static int32_t partition_init_done;
+
+#define INVALID_NS_CLIENT_ID  0x49abcdef
+#define EXPECTED_NS_CLIENT_ID (-1)
+
+#define IRQ_TEST_TOOL_CODE_LOCATION(name)
+
+#ifndef TFM_PSA_API
+/* Don't initialise caller_partition_id_zi and expect it to be linked in the
+ * zero-initialised data area
+ */
+static int32_t caller_client_id_zi;
+
+/* Initialise caller_partition_id_rw and expect it to be linked in the
+ * read-write data area
+ */
+static int32_t caller_client_id_rw = INVALID_NS_CLIENT_ID;
+
+static int32_t* invalid_addresses [] = {(int32_t*)0x0, (int32_t*)0xFFF12000};
+
+#else /* !defined(TFM_PSA_API) */
+
+static psa_status_t psa_test_common(uint32_t sid, uint32_t version,
+                                    const psa_invec *in_vecs, size_t in_len,
+                                    psa_outvec *out_vecs, size_t out_len)
+{
+    psa_handle_t handle;
+    psa_status_t status;
+
+    handle = psa_connect(sid, version);
+    if (handle <= 0) {
+        return CORE_TEST_ERRNO_INVALID_PARAMETER;
+    }
+
+    status = psa_call(handle, PSA_IPC_CALL, in_vecs, in_len, out_vecs, out_len);
+    if (status < 0) {
+        status = CORE_TEST_ERRNO_UNEXPECTED_CORE_BEHAVIOUR;
+    }
+
+    psa_close(handle);
+    return status;
+}
+#endif /* !defined(TFM_PSA_API) */
+
+psa_status_t spm_core_test_sfn_init_success(
+                                     struct psa_invec *in_vec, size_t in_len,
+                                     struct psa_outvec *out_vec, size_t out_len)
+{
+    if ((in_len != 0) || (out_len != 0)) {
+        return CORE_TEST_ERRNO_INVALID_PARAMETER;
+    }
+
+    if (partition_init_done) {
+        return CORE_TEST_ERRNO_SUCCESS;
+    } else {
+        return CORE_TEST_ERRNO_SP_NOT_INITED;
+    }
+}
+
+#ifndef TFM_PSA_API
+psa_status_t spm_core_test_sfn_direct_recursion(
+                                     struct psa_invec *in_vec, size_t in_len,
+                                     struct psa_outvec *out_vec, size_t out_len)
+{
+    uint32_t depth;
+    struct psa_invec new_vec = {NULL, sizeof(uint32_t)};
+
+    if ((in_len != 1) || (out_len != 0) ||
+        (in_vec[0].len != sizeof(uint32_t))) {
+            return CORE_TEST_ERRNO_INVALID_PARAMETER;
+    }
+
+    depth = *((uint32_t *)in_vec[0].base);
+
+    if (depth != 0) {
+        /* Protect against scenario where TF-M core fails to block recursion */
+        return CORE_TEST_ERRNO_SP_RECURSION_NOT_REJECTED;
+    }
+    /* Call to the same service again, should be rejected */
+    depth += 1;
+    new_vec.base = &depth;
+    int32_t ret = tfm_spm_core_test_sfn_direct_recursion_veneer(&new_vec,
+                                                                1, NULL, 0);
+
+    if (ret == CORE_TEST_ERRNO_SUCCESS) {
+        /* This is an unexpected return value */
+        return CORE_TEST_ERRNO_UNEXPECTED_CORE_BEHAVIOUR;
+    } else if (ret == CORE_TEST_ERRNO_SP_RECURSION_NOT_REJECTED) {
+        /* This means that service was started in recursion */
+        return CORE_TEST_ERRNO_SP_RECURSION_NOT_REJECTED;
+    } else {
+        return CORE_TEST_ERRNO_SUCCESS;
+    }
+}
+#endif /* !defined(TFM_PSA_API) */
+
+static psa_status_t test_peripheral_access(void)
+{
+#ifdef TFM_ENABLE_PERIPH_ACCESS_TEST
+    uint32_t leds;
+    uint32_t invleds;
+    uint32_t userled_mask;
+
+    leds = tfm_plat_test_get_led_status();
+    tfm_plat_test_set_led_status(~leds);
+    invleds = tfm_plat_test_get_led_status();
+    userled_mask = tfm_plat_test_get_userled_mask();
+
+    if ((invleds & userled_mask) != (~leds & userled_mask)) {
+        /* Code failed to invert value in peripheral reg */
+        return CORE_TEST_ERRNO_PERIPHERAL_ACCESS_FAILED;
+    }
+
+    return CORE_TEST_ERRNO_SUCCESS;
+#else
+    return CORE_TEST_ERRNO_TEST_NOT_SUPPORTED;
+#endif
+}
+
+#define SS_BUFFER_LEN 16
+
+static psa_status_t test_ss_to_ss_buffer(uint32_t *in_ptr, uint32_t *out_ptr,
+                                         int32_t len)
+{
+    int32_t i;
+    /* Service internal buffer */
+    uint32_t ss_buffer[SS_BUFFER_LEN] = {0};
+    uint32_t slave_buffer [len];
+    int32_t result;
+    int32_t *result_ptr = &result;
+    int32_t res;
+    psa_invec in_vec[] = { {slave_buffer, len*sizeof(uint32_t)} };
+    psa_outvec outvec[] = { {slave_buffer, len*sizeof(uint32_t)},
+                            {result_ptr, sizeof(int32_t)} };
+
+    if (len > SS_BUFFER_LEN) {
+        return CORE_TEST_ERRNO_TEST_FAULT;
+    }
+
+    for (i = 0; i < len; i++) {
+        ss_buffer[i] = in_ptr[i];
+    }
+
+    for (i = 0; i < len; i++) {
+        slave_buffer[i] = ss_buffer[i];
+    }
+
+    /* Call internal service with buffer handling */
+
+#ifdef TFM_PSA_API
+    res = psa_test_common(SPM_CORE_TEST_2_INVERT_SID,
+                          SPM_CORE_TEST_2_INVERT_VERSION,
+                          in_vec, 1, outvec, 2);
+#else /* defined(TFM_PSA_API) */
+    res = tfm_spm_core_test_2_sfn_invert_veneer(in_vec, 1, outvec, 2);
+#endif /* defined(TFM_PSA_API) */
+
+    if (res != CORE_TEST_ERRNO_SUCCESS) {
+        return CORE_TEST_ERRNO_SLAVE_SP_CALL_FAILURE;
+    }
+
+    for (i = 0; i < len; i++) {
+        if (slave_buffer[i] != ~ss_buffer[i]) {
+            return CORE_TEST_ERRNO_SLAVE_SP_BUFFER_FAILURE;
+        }
+        ss_buffer[i] = slave_buffer[i];
+    }
+
+    for (i = 0; i < len; i++) {
+        out_ptr[i] = ss_buffer[i];
+    }
+
+    return CORE_TEST_ERRNO_SUCCESS;
+}
+
+static psa_status_t test_outvec_write(void)
+{
+    int32_t err;
+    int i;
+    uint8_t data_buf [36]; /* (6 + 12) * 2 = 36 plus some alignment */
+    uint8_t *data_buf_ptr = data_buf;
+    psa_invec in_vec [2];
+    psa_outvec out_vec [2];
+    uint8_t *in_buf_0;
+    uint8_t *in_buf_1;
+    uint8_t *out_buf_0;
+    uint8_t *out_buf_1;
+
+    in_buf_0 = data_buf_ptr;
+    for (i = 0; i < 5; ++i, ++data_buf_ptr)
+    {
+        *data_buf_ptr = i;
+    }
+    in_vec[0].base = in_buf_0;
+    in_vec[0].len = data_buf_ptr - in_buf_0;
+
+    in_buf_1 = data_buf_ptr;
+    *(data_buf_ptr++) = 1;
+    *(data_buf_ptr++) = 1;
+    for (i = 2; i < 11; ++i, ++data_buf_ptr)
+    {
+        *data_buf_ptr = *(data_buf_ptr-1) + *(data_buf_ptr-2);
+    }
+    in_vec[1].base = in_buf_1;
+    in_vec[1].len = data_buf_ptr - in_buf_1;
+
+    out_buf_0 = data_buf_ptr;
+    data_buf_ptr += in_vec[0].len;
+    out_vec[0].base = out_buf_0;
+    out_vec[0].len = data_buf_ptr - out_buf_0;
+
+    out_buf_1 = data_buf_ptr;
+    data_buf_ptr += in_vec[1].len;
+    out_vec[1].base = out_buf_1;
+    out_vec[1].len = data_buf_ptr - out_buf_1;
+
+#ifdef TFM_PSA_API
+    err = psa_test_common(SPM_CORE_TEST_2_GET_EVERY_SECOND_BYTE_SID,
+                          SPM_CORE_TEST_2_GET_EVERY_SECOND_BYTE_VERSION,
+                          in_vec, 2, out_vec, 2);
+#else /* defined(TFM_PSA_API) */
+    err = tfm_spm_core_test_2_get_every_second_byte_veneer(in_vec, 2,
+                                                           out_vec, 2);
+#endif /* defined(TFM_PSA_API) */
+
+    if (err != CORE_TEST_ERRNO_SUCCESS) {
+        return CORE_TEST_ERRNO_TEST_FAULT;
+    }
+
+    if (out_vec[0].len != in_vec[0].len/2 ||
+        out_vec[1].len != in_vec[1].len/2) {
+        return CORE_TEST_ERRNO_TEST_FAULT;
+    }
+    for (i = 1; i < sizeof(in_buf_0); i += 2) {
+        if (((uint8_t *)out_vec[0].base)[i/2] != in_buf_0[i]) {
+            return CORE_TEST_ERRNO_TEST_FAULT;
+        }
+    }
+    for (i = 1; i < sizeof(in_buf_1); i += 2) {
+        if (((uint8_t *)out_vec[1].base)[i/2] != in_buf_1[i]) {
+            return CORE_TEST_ERRNO_TEST_FAULT;
+        }
+    }
+
+    return CORE_TEST_ERRNO_SUCCESS;
+}
+
+static psa_status_t test_ss_to_ss(void)
+{
+    int32_t ret;
+    /* Call to a different service, should be successful */
+    IRQ_TEST_TOOL_CODE_LOCATION(example_secure_service_start);
+#ifdef TFM_PSA_API
+    ret = psa_test_common(SPM_CORE_TEST_2_SLAVE_SERVICE_SID,
+                          SPM_CORE_TEST_2_SLAVE_SERVICE_VERSION,
+                          NULL, 0, NULL, 0);
+#else /* defined(TFM_PSA_API) */
+    ret = tfm_spm_core_test_2_slave_service_veneer(NULL, 0, NULL, 0);
+#endif /* defined(TFM_PSA_API) */
+    IRQ_TEST_TOOL_CODE_LOCATION(example_secure_service_end);
+    if (ret == CORE_TEST_ERRNO_SUCCESS_2) {
+        return CORE_TEST_ERRNO_SUCCESS;
+    } else {
+        return CORE_TEST_ERRNO_SLAVE_SP_CALL_FAILURE;
+    }
+}
+
+#ifndef TFM_PSA_API
+static psa_status_t test_get_caller_client_id(void)
+{
+    /* Call to a special service that checks the caller service ID */
+    size_t i;
+    int32_t ret;
+    int32_t caller_client_id_stack = INVALID_NS_CLIENT_ID;
+
+    caller_client_id_zi = INVALID_NS_CLIENT_ID;
+
+    ret = tfm_spm_core_test_2_check_caller_client_id_veneer(NULL, 0, NULL, 0);
+    if (ret != CORE_TEST_ERRNO_SUCCESS) {
+        return CORE_TEST_ERRNO_SLAVE_SP_CALL_FAILURE;
+    }
+
+    /* test with invalid output pointers */
+    for (i = 0; i < sizeof(invalid_addresses)/sizeof(invalid_addresses[0]); ++i)
+    {
+        ret = tfm_core_get_caller_client_id(invalid_addresses[i]);
+        if (ret != TFM_ERROR_INVALID_PARAMETER) {
+            return CORE_TEST_ERRNO_TEST_FAULT;
+        }
+    }
+
+    /* test with valid output pointers */
+    ret = tfm_core_get_caller_client_id(&caller_client_id_zi);
+    if (ret != TFM_SUCCESS || caller_client_id_zi != EXPECTED_NS_CLIENT_ID) {
+        return CORE_TEST_ERRNO_TEST_FAULT;
+    }
+
+    ret = tfm_core_get_caller_client_id(&caller_client_id_rw);
+    if (ret != TFM_SUCCESS || caller_client_id_rw != EXPECTED_NS_CLIENT_ID) {
+        return CORE_TEST_ERRNO_TEST_FAULT;
+    }
+
+    ret = tfm_core_get_caller_client_id(&caller_client_id_stack);
+    if (ret != TFM_SUCCESS ||
+            caller_client_id_stack != EXPECTED_NS_CLIENT_ID) {
+        return CORE_TEST_ERRNO_TEST_FAULT;
+    }
+
+    return CORE_TEST_ERRNO_SUCCESS;
+}
+
+static psa_status_t test_spm_request(void)
+{
+    /* Request a reset vote, should be successful */
+    int32_t ret = tfm_spm_request_reset_vote();
+
+    if (ret != TFM_SUCCESS) {
+        return CORE_TEST_ERRNO_SLAVE_SP_CALL_FAILURE;
+    }
+
+    return CORE_TEST_ERRNO_SUCCESS;
+}
+#endif /* !defined(TFM_PSA_API) */
+
+#ifdef CORE_TEST_INTERACTIVE
+static void wait_button_event(void)
+{
+    tfm_plat_test_wait_user_button_pressed();
+    /*
+     * The follow wait is required to skip multiple continues in one go due to
+     * the fast execution of the code and time used by the user to
+     * release button.
+     */
+
+    tfm_plat_test_wait_user_button_released();
+}
+
+psa_status_t test_wait_button(void)
+{
+    TEST_LOG("Inside the service, press button to continue...");
+    wait_button_event();
+    TEST_LOG("Leaving the service");
+    return CORE_TEST_ERRNO_SUCCESS;
+}
+#endif /* defined(CORE_TEST_INTERACTIVE) */
+
+static psa_status_t test_block(void)
+{
+#ifdef CORE_TEST_INTERACTIVE
+    /* Only block if interactive test is turned on */
+    return test_wait_button();
+#else /* defined(CORE_TEST_INTERACTIVE) */
+    /* This test should not be run if interactive tests are disabled */
+    return CORE_TEST_ERRNO_TEST_FAULT;
+#endif /* defined(CORE_TEST_INTERACTIVE) */
+}
+
+#ifndef TFM_PSA_API
+psa_status_t spm_core_test_sfn(struct psa_invec *in_vec, size_t in_len,
+                          struct psa_outvec *out_vec, size_t out_len)
+{
+    uint32_t tc;
+    int32_t arg1;
+    int32_t arg2;
+    int32_t arg3;
+
+    if ((in_len < 1) || (in_vec[0].len != sizeof(uint32_t))) {
+        return CORE_TEST_ERRNO_INVALID_PARAMETER;
+    }
+    tc = *((uint32_t *)in_vec[0].base);
+
+    switch (tc) {
+    case CORE_TEST_ID_SS_TO_SS:
+        return test_ss_to_ss();
+    case CORE_TEST_ID_SS_TO_SS_BUFFER:
+        if ((in_len != 3) || (out_len != 1) ||
+        (in_vec[2].len != sizeof(int32_t))) {
+            return CORE_TEST_ERRNO_INVALID_PARAMETER;
+        }
+        arg3 = *((int32_t *)in_vec[2].base);
+        if ((in_vec[1].len < arg3*sizeof(int32_t)) ||
+            (out_vec[0].len < arg3*sizeof(int32_t))) {
+            return CORE_TEST_ERRNO_INVALID_PARAMETER;
+        }
+        arg1 = (int32_t)in_vec[1].base;
+        arg2 = (int32_t)out_vec[0].base;
+        return test_ss_to_ss_buffer((uint32_t *)arg1, (uint32_t *)arg2, arg3);
+    case CORE_TEST_ID_OUTVEC_WRITE:
+        return test_outvec_write();
+    case CORE_TEST_ID_PERIPHERAL_ACCESS:
+        return test_peripheral_access();
+    case CORE_TEST_ID_GET_CALLER_CLIENT_ID:
+        return test_get_caller_client_id();
+    case CORE_TEST_ID_SPM_REQUEST:
+        return test_spm_request();
+    case CORE_TEST_ID_BLOCK:
+        return test_block();
+    case CORE_TEST_ID_NS_THREAD:
+        /* dummy service call is enough */
+        return CORE_TEST_ERRNO_SUCCESS;
+    default:
+        return CORE_TEST_ERRNO_INVALID_TEST_ID;
+    }
+}
+
+#else /* !defined(TFM_PSA_API) */
+
+#define SS_TO_SS_BUFFER_SIZE (16*4)
+
+typedef psa_status_t (*core_test_func_t)(psa_msg_t *msg);
+
+static psa_status_t tfm_core_test_sfn_wrap_init_success(psa_msg_t *msg)
+{
+    return spm_core_test_sfn_init_success(NULL, 0, NULL, 0);
+}
+
+static psa_status_t tfm_core_test_sfn_wrap_direct_recursion(psa_msg_t *msg)
+{
+    return CORE_TEST_ERRNO_TEST_FAULT;
+}
+
+static psa_status_t tfm_core_test_sfn_wrap_ss_to_ss(psa_msg_t *msg)
+{
+    return test_ss_to_ss();
+}
+
+static psa_status_t tfm_core_test_sfn_wrap_ss_to_ss_buffer(psa_msg_t *msg)
+{
+    size_t num;
+    uint32_t inbuf[SS_TO_SS_BUFFER_SIZE/sizeof(uint32_t)] = {0};
+    uint32_t outbuf[SS_TO_SS_BUFFER_SIZE/sizeof(uint32_t)] = {0};
+    uint32_t len;
+    psa_status_t res;
+
+    if ((msg->in_size[0] > SS_TO_SS_BUFFER_SIZE) ||
+        (msg->in_size[1] != sizeof(int32_t)) ||
+        (msg->out_size[0] > SS_TO_SS_BUFFER_SIZE)) {
+        return CORE_TEST_ERRNO_INVALID_PARAMETER;
+    }
+
+    num = psa_read(msg->handle, 0, inbuf, msg->in_size[0]);
+    if (num != msg->in_size[0]) {
+        return CORE_TEST_ERRNO_INVALID_PARAMETER;
+    }
+
+    num = psa_read(msg->handle, 1, &len, sizeof(int32_t));
+    if (num != sizeof(int32_t)) {
+        return CORE_TEST_ERRNO_INVALID_PARAMETER;
+    }
+
+    if (len > SS_TO_SS_BUFFER_SIZE) {
+        return CORE_TEST_ERRNO_INVALID_PARAMETER;
+    }
+
+    res = test_ss_to_ss_buffer(inbuf, outbuf, len);
+    if (res < 0) {
+        return res;
+    }
+
+    psa_write(msg->handle, 0, outbuf, len * sizeof(uint32_t));
+
+    return res;
+}
+
+static psa_status_t tfm_core_test_sfn_wrap_outvec_write(psa_msg_t *msg)
+{
+    return test_outvec_write();
+}
+
+static psa_status_t tfm_core_test_sfn_wrap_peripheral_access(psa_msg_t *msg)
+{
+    return test_peripheral_access();
+}
+
+static psa_status_t tfm_core_test_sfn_wrap_get_caller_client_id(psa_msg_t *msg)
+{
+    return CORE_TEST_ERRNO_TEST_NOT_SUPPORTED;
+}
+
+static psa_status_t tfm_core_test_sfn_wrap_spm_request(psa_msg_t *msg)
+{
+    return CORE_TEST_ERRNO_TEST_NOT_SUPPORTED;
+}
+
+static psa_status_t tfm_core_test_sfn_wrap_block(psa_msg_t *msg)
+{
+    return test_block();
+}
+
+static psa_status_t tfm_core_test_sfn_wrap_ns_thread(psa_msg_t *msg)
+{
+    return CORE_TEST_ERRNO_SUCCESS;
+}
+
+static void core_test_signal_handle(psa_signal_t signal, core_test_func_t pfn)
+{
+    psa_msg_t msg;
+    psa_status_t status;
+
+    status = psa_get(signal, &msg);
+    if (status) {
+        return;
+    }
+
+    switch (msg.type) {
+    case PSA_IPC_CONNECT:
+        psa_reply(msg.handle, PSA_SUCCESS);
+        break;
+    case PSA_IPC_CALL:
+        status = pfn(&msg);
+        psa_reply(msg.handle, status);
+        break;
+    case PSA_IPC_DISCONNECT:
+        psa_reply(msg.handle, PSA_SUCCESS);
+        break;
+    default:
+        break;
+    }
+}
+#endif /* !defined(TFM_PSA_API) */
+
+psa_status_t core_test_init(void)
+{
+#ifdef TFM_PSA_API
+    psa_signal_t signals = 0;
+#endif /* defined(TFM_PSA_API) */
+
+    partition_init_done = 1;
+
+#ifdef TFM_PSA_API
+    while (1) {
+        signals = psa_wait(PSA_WAIT_ANY, PSA_BLOCK);
+        if (signals & SPM_CORE_TEST_INIT_SUCCESS_SIGNAL) {
+            core_test_signal_handle(SPM_CORE_TEST_INIT_SUCCESS_SIGNAL,
+                                    tfm_core_test_sfn_wrap_init_success);
+        } else if (signals & SPM_CORE_TEST_DIRECT_RECURSION_SIGNAL) {
+            core_test_signal_handle(SPM_CORE_TEST_DIRECT_RECURSION_SIGNAL,
+                                    tfm_core_test_sfn_wrap_direct_recursion);
+        } else if (signals & SPM_CORE_TEST_SS_TO_SS_SIGNAL) {
+            core_test_signal_handle(SPM_CORE_TEST_SS_TO_SS_SIGNAL,
+                                    tfm_core_test_sfn_wrap_ss_to_ss);
+        } else if (signals & SPM_CORE_TEST_SS_TO_SS_BUFFER_SIGNAL) {
+            core_test_signal_handle(SPM_CORE_TEST_SS_TO_SS_BUFFER_SIGNAL,
+                                    tfm_core_test_sfn_wrap_ss_to_ss_buffer);
+        } else if (signals & SPM_CORE_TEST_OUTVEC_WRITE_SIGNAL) {
+            core_test_signal_handle(SPM_CORE_TEST_OUTVEC_WRITE_SIGNAL,
+                                    tfm_core_test_sfn_wrap_outvec_write);
+        } else if (signals & SPM_CORE_TEST_PERIPHERAL_ACCESS_SIGNAL) {
+            core_test_signal_handle(SPM_CORE_TEST_PERIPHERAL_ACCESS_SIGNAL,
+                                    tfm_core_test_sfn_wrap_peripheral_access);
+        } else if (signals & SPM_CORE_TEST_GET_CALLER_CLIENT_ID_SIGNAL) {
+            core_test_signal_handle(SPM_CORE_TEST_GET_CALLER_CLIENT_ID_SIGNAL,
+                                   tfm_core_test_sfn_wrap_get_caller_client_id);
+        } else if (signals & SPM_CORE_TEST_SPM_REQUEST_SIGNAL) {
+            core_test_signal_handle(SPM_CORE_TEST_SPM_REQUEST_SIGNAL,
+                                    tfm_core_test_sfn_wrap_spm_request);
+        } else if (signals & SPM_CORE_TEST_BLOCK_SIGNAL) {
+            core_test_signal_handle(SPM_CORE_TEST_BLOCK_SIGNAL,
+                                    tfm_core_test_sfn_wrap_block);
+        } else if (signals & SPM_CORE_TEST_NS_THREAD_SIGNAL) {
+            core_test_signal_handle(SPM_CORE_TEST_NS_THREAD_SIGNAL,
+                                    tfm_core_test_sfn_wrap_ns_thread);
+        } else {
+            ; /* do nothing */
+        }
+    }
+#else
+    return CORE_TEST_ERRNO_SUCCESS;
+#endif /* defined(TFM_PSA_API) */
+}
diff --git a/test/test_services/tfm_core_test/tfm_ss_core_test.h b/test/test_services/tfm_core_test/tfm_ss_core_test.h
new file mode 100644
index 0000000..eb9598f
--- /dev/null
+++ b/test/test_services/tfm_core_test/tfm_ss_core_test.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2017-2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __TFM_SS_CORE_TEST_H__
+#define __TFM_SS_CORE_TEST_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <inttypes.h>
+#include <limits.h>
+#include "tfm_api.h"
+
+/**
+ * \brief Tests whether the initialisation of the service was successful.
+ *
+ * \param[in] in_vec    Array of psa_invec objects
+ * \param[in] in_len    Number psa_invec objects in in_vec
+ * \param[in] out_vec   Array of psa_outvec objects
+ * \param[in] out_len   Number psa_outvec objects in out_vec
+ *
+ * \param[in] in_vec    Array of psa_invec objects
+ * \param[in] in_len    Number psa_invec objects in in_vec
+ * \param[in] out_vec   Array of psa_outvec objects
+ * \param[in] out_len   Number psa_outvec objects in out_vec
+ *
+ * The function expects 0 in_vec objects.
+ * The function expects 0 out_vec objects.
+ *
+ * \return Returns \ref CORE_TEST_ERRNO_SUCCESS on success, and
+ *                 \ref CORE_TEST_ERRNO_SP_NOT_INITED on failure.
+ */
+psa_status_t spm_core_test_sfn_init_success(
+                                    struct psa_invec *in_vec, size_t in_len,
+                                    struct psa_outvec *out_vec, size_t out_len);
+
+/**
+ * \brief Tests what happens when a service calls itself directly.
+ *
+ * \param[in] in_vec    Array of psa_invec objects
+ * \param[in] in_len    Number psa_invec objects in in_vec
+ * \param[in] out_vec   Array of psa_outvec objects
+ * \param[in] out_len   Number psa_outvec objects in out_vec
+ *
+ * The function expects 1 in_vec object:
+ * in_vec[0].base: A buffer containing a pointer to an uint32_t value
+ *                 containing the current depth of the call (the value of the
+ *                 depth is 0 when first called).
+ * in_vec[0].len:  The size of a pointer in bytes.
+
+ * The function expects 0 out_vec objects.
+ *
+ * \return Returns \ref CORE_TEST_ERRNO_SUCCESS.
+ */
+psa_status_t spm_core_test_sfn_direct_recursion(
+                                    struct psa_invec *in_vec, size_t in_len,
+                                    struct psa_outvec *out_vec, size_t out_len);
+
+/**
+ * \brief Entry point for multiple test cases to be executed on the secure side.
+ *
+ * \param[in] in_vec    Array of psa_invec objects
+ * \param[in] in_len    Number psa_invec objects in in_vec
+ * \param[in] out_vec   Array of psa_outvec objects
+ * \param[in] out_len   Number psa_outvec objects in out_vec
+ *
+ * The function expects at least 1 in_vec object:
+ *
+ * in_vec[0].base: A buffer containing a pointer to an uint32_t value
+ *                 containing the testcase id to be executed.
+ * in_vec[0].len:  The size of a pointer in bytes.
+ *
+ * The number of expected additional in_vecs and out_vecs is dependent on the id
+ * of the test case. For details see the function implementation.
+ *
+ * \return Can return various error codes.
+ */
+psa_status_t spm_core_test_sfn(struct psa_invec *in_vec, size_t in_len,
+                          struct psa_outvec *out_vec, size_t out_len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __TFM_SS_CORE_TEST_H__ */
diff --git a/test/test_services/tfm_core_test/tfm_test_core.yaml b/test/test_services/tfm_core_test/tfm_test_core.yaml
new file mode 100644
index 0000000..a7e6a81
--- /dev/null
+++ b/test/test_services/tfm_core_test/tfm_test_core.yaml
@@ -0,0 +1,121 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+{
+  "psa_framework_version": 1.0,
+  "name": "TFM_SP_CORE_TEST",
+  "type": "PSA-ROT",
+  "priority": "NORMAL",
+  "entry_point": "core_test_init",
+  "stack_size": "0x0380",
+  "mmio_regions": [
+    {
+      "name": "TFM_PERIPHERAL_FPGA_IO",
+      "permission": "READ-WRITE"
+    }
+  ],
+  "secure_functions": [
+    {
+      "name": "TFM_CORE_TEST_SFN",
+      "signal": "SPM_CORE_TEST_SFN",
+      "non_secure_clients": true,
+      "version": 1,
+      "version_policy": "STRICT"
+    },
+    {
+      "name": "TFM_CORE_TEST_SFN_INIT_SUCCESS",
+      "signal": "SPM_CORE_TEST_SFN_INIT_SUCCESS",
+      "non_secure_clients": true,
+      "version": 1,
+      "version_policy": "STRICT"
+    },
+    {
+      "name": "TFM_CORE_TEST_SFN_DIRECT_RECURSION",
+      "signal": "SPM_CORE_TEST_SFN_DIRECT_RECURSION",
+      "non_secure_clients": true,
+      "version": 1,
+      "version_policy": "STRICT"
+    }
+  ],
+  "services": [
+    {
+      "name": "SPM_CORE_TEST_INIT_SUCCESS",
+      "sid": "0x0000F020",
+      "non_secure_clients": true,
+      "version": 1,
+      "version_policy": "STRICT"
+    },
+    {
+      "name": "SPM_CORE_TEST_DIRECT_RECURSION",
+      "sid": "0x0000F021",
+      "non_secure_clients": true,
+      "version": 1,
+      "version_policy": "STRICT"
+    },
+    {
+      "name": "SPM_CORE_TEST_SS_TO_SS",
+      "sid": "0x0000F024",
+      "non_secure_clients": true,
+      "version": 1,
+      "version_policy": "STRICT"
+    },
+    {
+      "name": "SPM_CORE_TEST_SS_TO_SS_BUFFER",
+      "sid": "0x0000F025",
+      "non_secure_clients": true,
+      "version": 1,
+      "version_policy": "STRICT"
+    },
+    {
+      "name": "SPM_CORE_TEST_OUTVEC_WRITE",
+      "sid": "0x0000F026",
+      "non_secure_clients": true,
+      "version": 1,
+      "version_policy": "STRICT"
+    },
+    {
+      "name": "SPM_CORE_TEST_PERIPHERAL_ACCESS",
+      "sid": "0x0000F027",
+      "non_secure_clients": true,
+      "version": 1,
+      "version_policy": "STRICT"
+    },
+    {
+      "name": "SPM_CORE_TEST_GET_CALLER_CLIENT_ID",
+      "sid": "0x0000F028",
+      "non_secure_clients": true,
+      "version": 1,
+      "version_policy": "STRICT"
+    },
+    {
+      "name": "SPM_CORE_TEST_SPM_REQUEST",
+      "sid": "0x0000F029",
+      "non_secure_clients": true,
+      "version": 1,
+      "version_policy": "STRICT"
+    },
+    {
+      "name": "SPM_CORE_TEST_BLOCK",
+      "sid": "0x0000F02A",
+      "non_secure_clients": true,
+      "version": 1,
+      "version_policy": "STRICT"
+    },
+    {
+      "name": "SPM_CORE_TEST_NS_THREAD",
+      "sid": "0x0000F02B",
+      "non_secure_clients": true,
+      "version": 1,
+      "version_policy": "STRICT"
+    }
+  ],
+  "dependencies": [
+    "SPM_CORE_TEST_2_INVERT",
+    "SPM_CORE_TEST_2_GET_EVERY_SECOND_BYTE",
+    "SPM_CORE_TEST_2_SLAVE_SERVICE"
+  ]
+}
diff --git a/test/test_services/tfm_core_test_2/psa_manifest/tfm_test_core_2.h b/test/test_services/tfm_core_test_2/psa_manifest/tfm_test_core_2.h
new file mode 100644
index 0000000..c636835
--- /dev/null
+++ b/test/test_services/tfm_core_test_2/psa_manifest/tfm_test_core_2.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+/*********** WARNING: This is an auto-generated file. Do not edit! ***********/
+
+#ifndef __PSA_MANIFEST_TFM_TEST_CORE_2_H__
+#define __PSA_MANIFEST_TFM_TEST_CORE_2_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SPM_CORE_TEST_2_SLAVE_SERVICE_SIGNAL                    (1U << (0 + 4))
+#define SPM_CORE_TEST_2_CHECK_CALLER_CLIENT_ID_SIGNAL           (1U << (1 + 4))
+#define SPM_CORE_TEST_2_GET_EVERY_SECOND_BYTE_SIGNAL            (1U << (2 + 4))
+#define SPM_CORE_TEST_2_INVERT_SIGNAL                           (1U << (3 + 4))
+#define SPM_CORE_TEST_2_PREPARE_TEST_SCENARIO_SIGNAL            (1U << (4 + 4))
+#define SPM_CORE_TEST_2_EXECUTE_TEST_SCENARIO_SIGNAL            (1U << (5 + 4))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __PSA_MANIFEST_TFM_TEST_CORE_2_H__ */
diff --git a/test/test_services/tfm_core_test_2/tfm_ss_core_test_2.c b/test/test_services/tfm_core_test_2/tfm_ss_core_test_2.c
new file mode 100644
index 0000000..e9d8c95
--- /dev/null
+++ b/test/test_services/tfm_core_test_2/tfm_ss_core_test_2.c
@@ -0,0 +1,478 @@
+/*
+ * Copyright (c) 2017-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <stddef.h>
+#include "test/test_services/tfm_core_test/core_test_defs.h"
+#include "tfm_ss_core_test_2.h"
+#include "tfm_api.h"
+#include "tfm_secure_api.h"
+#include "psa/service.h"
+#include "psa_manifest/pid.h"
+#include "psa_manifest/tfm_test_core_2.h"
+
+#define INVALID_NS_CLIENT_ID  0x49abcdef
+#define INVERT_BUFFER_SIZE    (16*4)
+
+#ifndef TFM_PSA_API
+/* Don't initialise caller_partition_id_zi and expect it to be linked in the
+ * zero-initialised data area
+ */
+static int32_t caller_client_id_zi;
+
+/* Initialise caller_partition_id_rw and expect it to be linked in the
+ * read-write data area
+ */
+static int32_t caller_client_id_rw = INVALID_NS_CLIENT_ID;
+
+static int32_t* invalid_addresses [] = {(int32_t*)0x0, (int32_t*)0xFFF12000};
+#endif /* !defined(TFM_PSA_API) */
+
+/* structures for secure IRQ testing */
+static struct irq_test_execution_data_t *current_execution_data;
+
+psa_status_t spm_core_test_2_slave_service(struct psa_invec *in_vec,
+                                           size_t in_len,
+                                           struct psa_outvec *out_vec,
+                                           size_t out_len)
+{
+    /* This function doesn't do any sanity check on the input parameters, nor
+     * makes any expectation of them, always returns successfully, with a
+     * non-zero return value.
+     * This is to test the parameter sanitization mechanisms implemented in SPM,
+     * and the handling of non-zero success codes.
+     */
+
+    return CORE_TEST_ERRNO_SUCCESS_2;
+}
+
+#ifndef TFM_PSA_API
+psa_status_t spm_core_test_2_check_caller_client_id(struct psa_invec *in_vec,
+                                                    size_t in_len,
+                                                    struct psa_outvec *out_vec,
+                                                    size_t out_len)
+{
+    size_t i;
+    int32_t caller_client_id_stack = INVALID_NS_CLIENT_ID;
+    int32_t ret;
+
+    caller_client_id_zi = INVALID_NS_CLIENT_ID;
+
+    /* test with invalid output pointers */
+    for (i = 0; i < sizeof(invalid_addresses)/sizeof(invalid_addresses[0]); ++i)
+    {
+        ret = tfm_core_get_caller_client_id(invalid_addresses[i]);
+        if (ret != TFM_ERROR_INVALID_PARAMETER) {
+            return CORE_TEST_ERRNO_TEST_FAULT;
+        }
+    }
+
+    /* test with valid output pointers */
+    ret = tfm_core_get_caller_client_id(&caller_client_id_zi);
+    if (ret != TFM_SUCCESS || caller_client_id_zi != TFM_SP_CORE_TEST) {
+        return CORE_TEST_ERRNO_TEST_FAULT;
+    }
+
+    ret = tfm_core_get_caller_client_id(&caller_client_id_rw);
+    if (ret != TFM_SUCCESS || caller_client_id_rw != TFM_SP_CORE_TEST) {
+        return CORE_TEST_ERRNO_TEST_FAULT;
+    }
+
+    ret = tfm_core_get_caller_client_id(&caller_client_id_stack);
+    if (ret != TFM_SUCCESS ||
+            caller_client_id_stack != TFM_SP_CORE_TEST) {
+        return CORE_TEST_ERRNO_TEST_FAULT;
+    }
+
+    return CORE_TEST_ERRNO_SUCCESS;
+}
+#endif /* !defined(TFM_PSA_API) */
+
+psa_status_t spm_core_test_2_get_every_second_byte_internal(
+                                                           const uint8_t *inbuf,
+                                                           uint8_t *outbuf,
+                                                           size_t in_size,
+                                                           size_t *out_size)
+{
+    int j;
+
+    if (in_size/2 > *out_size) {
+        return CORE_TEST_ERRNO_INVALID_PARAMETER;
+    }
+    for (j = 1; j < in_size; j += 2) {
+        outbuf[j/2] = inbuf[j];
+    }
+    *out_size = in_size/2;
+
+    return CORE_TEST_ERRNO_SUCCESS;
+}
+
+psa_status_t spm_core_test_2_get_every_second_byte(
+                                     struct psa_invec *in_vec, size_t in_len,
+                                     struct psa_outvec *out_vec, size_t out_len)
+{
+    int i;
+    psa_status_t res;
+
+    if (in_len != out_len) {
+        return CORE_TEST_ERRNO_INVALID_PARAMETER;
+    }
+    for (i = 0; i < in_len; ++i) {
+        res = spm_core_test_2_get_every_second_byte_internal(
+                              in_vec[i].base, out_vec[i].base,
+                              in_vec[i].len, &out_vec[i].len);
+        if (res < 0) {
+            return res;
+        }
+    }
+    return CORE_TEST_ERRNO_SUCCESS;
+}
+
+/* Invert function */
+#define SFN_INVERT_MAX_LEN 128
+static psa_status_t spm_core_test_2_sfn_invert_internal(uint32_t *in_ptr,
+                                                        uint32_t *out_ptr,
+                                                        int32_t *res_ptr,
+                                                        int32_t len)
+{
+    int32_t i;
+    static uint32_t invert_buffer[SFN_INVERT_MAX_LEN];
+
+    *res_ptr = -1;
+
+    if (len > SFN_INVERT_MAX_LEN) {
+        return CORE_TEST_ERRNO_INVALID_BUFFER;
+    }
+
+    for (i = 0; i < len; i++) {
+        invert_buffer[i] = in_ptr[i];
+    }
+    for (i = 0; i < len; i++) {
+        invert_buffer[i] = ~invert_buffer[i];
+    }
+    for (i = 0; i < len; i++) {
+        out_ptr[i] = invert_buffer[i];
+    }
+
+    *res_ptr = 0;
+    return CORE_TEST_ERRNO_SUCCESS;
+}
+
+psa_status_t spm_core_test_2_sfn_invert(
+                                     struct psa_invec *in_vec, size_t in_len,
+                                     struct psa_outvec *out_vec, size_t out_len)
+{
+    int32_t len;
+    uint32_t *in_ptr;
+    uint32_t *out_ptr;
+    int32_t *res_ptr;
+
+    if ((in_len != 1) || (out_len != 2)) {
+        return CORE_TEST_ERRNO_INVALID_PARAMETER;
+    }
+
+    if ((out_vec[0].len < in_vec[0].len) || (in_vec[0].len%4 != 0) ||
+        (out_vec[1].len < sizeof(int32_t))) {
+        return CORE_TEST_ERRNO_INVALID_PARAMETER;
+    }
+
+    len = in_vec[0].len / 4;
+
+    in_ptr = (uint32_t *)in_vec[0].base;
+    out_ptr = (uint32_t *)out_vec[0].base;
+    res_ptr = (int32_t *)out_vec[1].base;
+
+    return spm_core_test_2_sfn_invert_internal(in_ptr, out_ptr, res_ptr, len);
+}
+
+static psa_status_t spm_core_test_2_prepare_test_scenario_internal(
+                               enum irq_test_scenario_t irq_test_scenario,
+                               struct irq_test_execution_data_t *execution_data)
+{
+    current_execution_data = execution_data;
+
+    switch (irq_test_scenario) {
+    case IRQ_TEST_SCENARIO_NONE:
+        return CORE_TEST_ERRNO_INVALID_PARAMETER;
+    case IRQ_TEST_SCENARIO_1:
+    case IRQ_TEST_SCENARIO_2:
+    case IRQ_TEST_SCENARIO_3:
+    case IRQ_TEST_SCENARIO_4:
+    case IRQ_TEST_SCENARIO_5:
+        /* No action is necessary*/
+        break;
+    default:
+        return CORE_TEST_ERRNO_INVALID_PARAMETER;
+    }
+
+    return CORE_TEST_ERRNO_SUCCESS;
+}
+
+psa_status_t spm_core_test_2_prepare_test_scenario(
+                             struct psa_invec *in_vec, size_t in_len,
+                             struct psa_outvec *out_vec, size_t out_size)
+{
+    if ((in_len != 2) ||
+        (in_vec[0].len != sizeof(uint32_t)) ||
+        (in_vec[1].len != sizeof(struct irq_test_execution_data_t *))) {
+        return CORE_TEST_ERRNO_INVALID_PARAMETER;
+    }
+
+    enum irq_test_scenario_t irq_test_scenario =
+            (enum irq_test_scenario_t) *(uint32_t *)in_vec[0].base;
+
+    struct irq_test_execution_data_t *execution_data =
+            *(struct irq_test_execution_data_t **)in_vec[1].base;
+
+    return spm_core_test_2_prepare_test_scenario_internal(irq_test_scenario,
+                                                          execution_data);
+}
+
+static psa_status_t spm_core_test_2_execute_test_scenario_internal(
+                                     enum irq_test_scenario_t irq_test_scenario)
+{
+    switch (irq_test_scenario) {
+    case IRQ_TEST_SCENARIO_NONE:
+        return CORE_TEST_ERRNO_INVALID_PARAMETER;
+    case IRQ_TEST_SCENARIO_1:
+        /* No action is necessary*/
+        break;
+    case IRQ_TEST_SCENARIO_2:
+        if (current_execution_data->timer0_triggered) {
+            return CORE_TEST_ERRNO_TEST_FAULT;
+        }
+        while (!current_execution_data->timer0_triggered) {
+            ;
+        }
+        break;
+    case IRQ_TEST_SCENARIO_3:
+    case IRQ_TEST_SCENARIO_4:
+        /* No action is necessary*/
+        break;
+    case IRQ_TEST_SCENARIO_5:
+        if (current_execution_data->timer1_triggered) {
+            return CORE_TEST_ERRNO_TEST_FAULT;
+        }
+        while (!current_execution_data->timer1_triggered) {
+            ;
+        }
+        break;
+    default:
+        return CORE_TEST_ERRNO_INVALID_PARAMETER;
+    }
+
+    return CORE_TEST_ERRNO_SUCCESS;
+}
+
+psa_status_t spm_core_test_2_execute_test_scenario(
+                                    struct psa_invec *in_vec, size_t in_len,
+                                    struct psa_outvec *out_vec, size_t out_size)
+{
+    enum irq_test_scenario_t irq_test_scenario =
+            (enum irq_test_scenario_t) *(uint32_t *)in_vec[0].base;
+
+    return spm_core_test_2_execute_test_scenario_internal(irq_test_scenario);
+}
+
+
+#ifdef TFM_PSA_API
+
+typedef psa_status_t (*core_test_2_func_t)(psa_msg_t *msg);
+
+static void core_test_2_signal_handle(psa_signal_t signal,
+                                      core_test_2_func_t pfn)
+{
+    psa_msg_t msg;
+    psa_status_t status;
+
+    status = psa_get(signal, &msg);
+    if (status) {
+        return;
+    }
+
+    switch (msg.type) {
+    case PSA_IPC_CONNECT:
+        psa_reply(msg.handle, PSA_SUCCESS);
+        break;
+    case PSA_IPC_CALL:
+        status = pfn(&msg);
+        psa_reply(msg.handle, status);
+        break;
+    case PSA_IPC_DISCONNECT:
+        psa_reply(msg.handle, PSA_SUCCESS);
+        break;
+    default:
+        break;
+    }
+}
+
+psa_status_t spm_core_test_2_wrap_slave_service(psa_msg_t *msg)
+{
+    return spm_core_test_2_slave_service(NULL, 0, NULL, 0);
+}
+
+psa_status_t spm_core_test_2_wrap_check_caller_client_id(psa_msg_t *msg)
+{
+    return CORE_TEST_ERRNO_TEST_NOT_SUPPORTED;
+}
+
+psa_status_t spm_core_test_2_wrap_get_every_second_byte(psa_msg_t *msg)
+{
+    uint32_t inbuf[INVERT_BUFFER_SIZE/sizeof(uint32_t)] = {0};
+    uint32_t outbuf[INVERT_BUFFER_SIZE/sizeof(uint32_t)] = {0};
+
+    int i;
+    size_t num;
+    size_t out_len;
+    psa_status_t res;
+
+    for (i = 0; i < PSA_MAX_IOVEC; ++i) {
+        if (msg->in_size[i] > INVERT_BUFFER_SIZE) {
+            return CORE_TEST_ERRNO_INVALID_PARAMETER;
+        }
+
+        if (msg->in_size[i] == 0) {
+            continue;
+        }
+
+        num = psa_read(msg->handle, i, inbuf, msg->in_size[i]);
+        if (num != msg->in_size[i]) {
+            return CORE_TEST_ERRNO_INVALID_PARAMETER;
+        }
+
+        out_len = msg->out_size[i];
+
+        res = spm_core_test_2_get_every_second_byte_internal((uint8_t *)inbuf,
+                (uint8_t *)outbuf, msg->in_size[i], &out_len);
+        if (res < 0) {
+            return res;
+        }
+
+        psa_write(msg->handle, i, outbuf, out_len);
+    }
+    return CORE_TEST_ERRNO_SUCCESS;
+}
+
+psa_status_t spm_core_test_2_wrap_sfn_invert(psa_msg_t *msg)
+{
+    uint32_t inbuf[INVERT_BUFFER_SIZE/sizeof(uint32_t)] = {0};
+    uint32_t outbuf[INVERT_BUFFER_SIZE/sizeof(uint32_t)] = {0};
+    size_t num;
+    int32_t res_ptr;
+    psa_status_t ret;
+
+    if ((msg->out_size[0] < msg->in_size[0]) ||
+        (msg->in_size[0] > INVERT_BUFFER_SIZE) ||
+        (msg->in_size[0]%4 != 0) ||
+        (msg->out_size[1] < sizeof(int32_t))) {
+        return CORE_TEST_ERRNO_INVALID_PARAMETER;
+    }
+
+    num = psa_read(msg->handle, 0, inbuf, msg->in_size[0]);
+    if (num != msg->in_size[0]) {
+        return CORE_TEST_ERRNO_INVALID_PARAMETER;
+    }
+
+    ret = spm_core_test_2_sfn_invert_internal(inbuf, outbuf,
+                                              &res_ptr, msg->in_size[0] / 4);
+    if (ret < 0) {
+        return ret;
+    }
+
+    psa_write(msg->handle, 0, outbuf, msg->in_size[0]);
+    psa_write(msg->handle, 1, &res_ptr, sizeof(int32_t));
+
+    return ret;
+}
+
+psa_status_t spm_core_test_2_wrap_prepare_test_scenario(psa_msg_t *msg)
+{
+    uint32_t irq_test_scenario;
+    struct irq_test_execution_data_t *execution_data;
+    size_t num;
+
+    if ((msg->in_size[0] != sizeof(uint32_t)) ||
+        (msg->in_size[1] != sizeof(struct irq_test_execution_data_t*)))  {
+        return CORE_TEST_ERRNO_INVALID_PARAMETER;
+    }
+
+    num = psa_read(msg->handle, 0, &irq_test_scenario, sizeof(irq_test_scenario));
+    if (num != msg->in_size[0]) {
+        return CORE_TEST_ERRNO_INVALID_PARAMETER;
+    }
+
+    num = psa_read(msg->handle, 1, &execution_data, sizeof(
+                                            struct irq_test_execution_data_t*));
+    if (num != msg->in_size[1]) {
+        return CORE_TEST_ERRNO_INVALID_PARAMETER;
+    }
+
+    return spm_core_test_2_prepare_test_scenario_internal((enum irq_test_scenario_t)
+                                                          irq_test_scenario,
+                                                          execution_data);
+}
+
+psa_status_t spm_core_test_2_wrap_execute_test_scenario(psa_msg_t *msg)
+{
+    uint32_t irq_test_scenario;
+    size_t num;
+
+    if (msg->in_size[0] != sizeof(uint32_t))  {
+        return CORE_TEST_ERRNO_INVALID_PARAMETER;
+    }
+
+    num = psa_read(msg->handle, 0, &irq_test_scenario, sizeof(irq_test_scenario));
+    if (num != msg->in_size[0]) {
+        return CORE_TEST_ERRNO_INVALID_PARAMETER;
+    }
+
+    return spm_core_test_2_execute_test_scenario_internal((enum irq_test_scenario_t)
+                                                          irq_test_scenario);
+}
+
+#endif /* defined(TFM_PSA_API) */
+
+/* FIXME: Add a testcase to test that a failed init makes the secure partition
+ * closed, and none of its functions can be called.
+ * A new test service for this purpose is to be added.
+ */
+psa_status_t core_test_2_init(void)
+{
+#ifdef TFM_PSA_API
+    psa_signal_t signals = 0;
+
+    while (1) {
+        signals = psa_wait(PSA_WAIT_ANY, PSA_BLOCK);
+        if (signals & SPM_CORE_TEST_2_SLAVE_SERVICE_SIGNAL) {
+            core_test_2_signal_handle(SPM_CORE_TEST_2_SLAVE_SERVICE_SIGNAL,
+                                      spm_core_test_2_wrap_slave_service);
+        } else if (signals & SPM_CORE_TEST_2_CHECK_CALLER_CLIENT_ID_SIGNAL) {
+            core_test_2_signal_handle(
+                                  SPM_CORE_TEST_2_CHECK_CALLER_CLIENT_ID_SIGNAL,
+                                  spm_core_test_2_wrap_check_caller_client_id);
+        } else if (signals & SPM_CORE_TEST_2_GET_EVERY_SECOND_BYTE_SIGNAL) {
+            core_test_2_signal_handle(
+                                   SPM_CORE_TEST_2_GET_EVERY_SECOND_BYTE_SIGNAL,
+                                   spm_core_test_2_wrap_get_every_second_byte);
+        } else if (signals & SPM_CORE_TEST_2_INVERT_SIGNAL) {
+            core_test_2_signal_handle(SPM_CORE_TEST_2_INVERT_SIGNAL,
+                                      spm_core_test_2_wrap_sfn_invert);
+        } else if (signals & SPM_CORE_TEST_2_PREPARE_TEST_SCENARIO_SIGNAL) {
+            core_test_2_signal_handle(
+                                   SPM_CORE_TEST_2_PREPARE_TEST_SCENARIO_SIGNAL,
+                                   spm_core_test_2_wrap_prepare_test_scenario);
+        } else if (signals & SPM_CORE_TEST_2_EXECUTE_TEST_SCENARIO_SIGNAL) {
+            core_test_2_signal_handle(
+                                   SPM_CORE_TEST_2_EXECUTE_TEST_SCENARIO_SIGNAL,
+                                   spm_core_test_2_wrap_execute_test_scenario);
+        } else {
+            ; /* do nothing */
+        }
+    }
+#else
+    return CORE_TEST_ERRNO_SUCCESS;
+#endif /* defined(TFM_PSA_API) */
+}
diff --git a/test/test_services/tfm_core_test_2/tfm_ss_core_test_2.h b/test/test_services/tfm_core_test_2/tfm_ss_core_test_2.h
new file mode 100644
index 0000000..44b12df
--- /dev/null
+++ b/test/test_services/tfm_core_test_2/tfm_ss_core_test_2.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2017-2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __TFM_SS_CORE_TEST_2_H__
+#define __TFM_SS_CORE_TEST_2_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <inttypes.h>
+#include <limits.h>
+
+#include "tfm_api.h"
+
+/**
+ * \brief A minimal test service to be called from another service.
+ *
+ * \param[in] in_vec    Array of psa_invec objects
+ * \param[in] in_len    Number psa_invec objects in in_vec
+ * \param[in] out_vec   Array of psa_outvec objects
+ * \param[in] out_len   Number psa_outvec objects in out_vec
+ *
+ * The function expects 0 in_vec objects.
+ * The function expects 0 out_vec objects.
+ *
+ * \return Returns \ref TFM_SUCCESS.
+ */
+psa_status_t spm_core_test_2_slave_service(
+                                    struct psa_invec *in_vec, size_t in_len,
+                                    struct psa_outvec *out_vec, size_t out_len);
+
+
+/**
+ * \brief Bitwise inverts the buffer received as input.
+ *
+ * \param[in] in_vec    Array of psa_invec objects
+ * \param[in] in_len    Number psa_invec objects in in_vec
+ * \param[in] out_vec   Array of psa_outvec objects
+ * \param[in] out_len   Number psa_outvec objects in out_vec
+ *
+ * The function expects 2 in_vec objects:
+ * in_vec[0].base: A pointer to the buffer containing the data to be inverted
+ * in_vec[0].len:  The length of the buffer
+ * in_vec[1].base: A pointer to an int32_t object
+ * in_vec[1].len:  The size of int32_t object
+ *
+ * The function expects 1 out_vec object:
+ * out_vec[0].base: A pointer to the buffer to put the result to
+ * out_vec[0].len:  The length of the buffer
+ *
+ * \return Returns \ref TFM_SUCCESS on success, TFM_PARTITION_BUSY otherwise.
+ */
+psa_status_t spm_core_test_2_sfn_invert(struct psa_invec *in_vec, size_t in_len,
+                                   struct psa_outvec *out_vec, size_t out_len);
+
+/**
+ * \brief A minimal test secure function to be called from another partition.
+ *
+ * Checks the functionality of querying the client ID of the caller service.
+ *
+ * \param[in] in_vec    Array of psa_invec objects
+ * \param[in] in_len    Number psa_invec objects in in_vec
+ * \param[in] out_vec   Array of psa_outvec objects
+ * \param[in] out_len   Number psa_outvec objects in out_vec
+ *
+ * The function expects 0 in_vec objects.
+ * The function expects 0 out_vec objects.
+ *
+ * \return Returns \ref TFM_SUCCESS on success, \ref CORE_TEST_ERRNO_TEST_FAULT
+ *         othervise.
+ */
+    psa_status_t spm_core_test_2_check_caller_client_id(
+            struct psa_invec *in_vec, size_t in_len, struct psa_outvec *out_vec,
+            size_t out_len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __TFM_SS_CORE_TEST_2_H__ */
diff --git a/test/test_services/tfm_core_test_2/tfm_test_core_2.yaml b/test/test_services/tfm_core_test_2/tfm_test_core_2.yaml
new file mode 100644
index 0000000..8b5d43c
--- /dev/null
+++ b/test/test_services/tfm_core_test_2/tfm_test_core_2.yaml
@@ -0,0 +1,103 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+{
+  "psa_framework_version": 1.0,
+  "name": "TFM_SP_CORE_TEST_2",
+  "type": "APPLICATION-ROT",
+  "priority": "NORMAL",
+  "entry_point": "core_test_2_init",
+  "stack_size": "0x0280",
+  "secure_functions": [
+    {
+      "name": "TFM_CORE_TEST_2_SFN_SLAVE_SERVICE",
+      "signal": "SPM_CORE_TEST_2_SLAVE_SERVICE",
+      "non_secure_clients": false,
+      "version": 1,
+      "version_policy": "STRICT"
+    },
+    {
+      "name": "TFM_CORE_TEST_2_SFN_INVERT",
+      "signal": "SPM_CORE_TEST_2_SFN_INVERT",
+      "non_secure_clients": true,
+      "version": 1,
+      "version_policy": "STRICT"
+    },
+    {
+      "name": "TFM_CORE_TEST_2_SFN_CHECK_CALLER_CLIENT_ID",
+      "signal": "SPM_CORE_TEST_2_CHECK_CALLER_CLIENT_ID",
+      "non_secure_clients": true,
+      "version": 1,
+      "version_policy": "STRICT"
+    },
+    {
+      "name": "TFM_CORE_TEST_2_SFN_GET_EVERY_SECOND_BYTE",
+      "signal": "SPM_CORE_TEST_2_GET_EVERY_SECOND_BYTE",
+      "non_secure_clients": true,
+      "version": 1,
+      "version_policy": "STRICT"
+    },
+    {
+      "name": "TFM_CORE_TEST_2_SFN_PREPARE_TEST_SCENARIO",
+      "signal": "SPM_CORE_TEST_2_PREPARE_TEST_SCENARIO",
+      "non_secure_clients": true,
+      "version": 1,
+      "version_policy": "STRICT"
+    },
+    {
+      "name": "TFM_CORE_TEST_2_SFN_EXECUTE_TEST_SCENARIO",
+      "signal": "SPM_CORE_TEST_2_EXECUTE_TEST_SCENARIO",
+      "non_secure_clients": true,
+      "version": 1,
+      "version_policy": "STRICT"
+    }
+  ],
+  "services": [
+    {
+      "name": "SPM_CORE_TEST_2_SLAVE_SERVICE",
+      "sid": "0x0000F040",
+      "non_secure_clients": true,
+      "version": 1,
+      "version_policy": "STRICT"
+    },
+    {
+      "name": "SPM_CORE_TEST_2_CHECK_CALLER_CLIENT_ID",
+      "sid": "0x0000F041",
+      "non_secure_clients": true,
+      "version": 1,
+      "version_policy": "STRICT"
+    },
+    {
+      "name": "SPM_CORE_TEST_2_GET_EVERY_SECOND_BYTE",
+      "sid": "0x0000F042",
+      "non_secure_clients": true,
+      "version": 1,
+      "version_policy": "STRICT"
+    },
+    {
+      "name": "SPM_CORE_TEST_2_INVERT",
+      "sid": "0x0000F043",
+      "non_secure_clients": true,
+      "version": 1,
+      "version_policy": "STRICT"
+    },
+    {
+    "name": "SPM_CORE_TEST_2_PREPARE_TEST_SCENARIO",
+    "sid": "0x0000F044",
+    "non_secure_clients": true,
+    "version": 1,
+    "version_policy": "STRICT"
+    },
+    {
+    "name": "SPM_CORE_TEST_2_EXECUTE_TEST_SCENARIO",
+    "sid": "0x0000F045",
+    "non_secure_clients": true,
+    "version": 1,
+    "version_policy": "STRICT"
+    },
+  ]
+}
diff --git a/test/test_services/tfm_ipc_client/psa_manifest/tfm_ipc_client_partition.h b/test/test_services/tfm_ipc_client/psa_manifest/tfm_ipc_client_partition.h
new file mode 100644
index 0000000..d6d7513
--- /dev/null
+++ b/test/test_services/tfm_ipc_client/psa_manifest/tfm_ipc_client_partition.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+/*********** WARNING: This is an auto-generated file. Do not edit! ***********/
+
+#ifndef __PSA_MANIFEST_TFM_IPC_CLIENT_PARTITION_H__
+#define __PSA_MANIFEST_TFM_IPC_CLIENT_PARTITION_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define IPC_CLIENT_TEST_BASIC_SIGNAL                            (1U << (0 + 4))
+#define IPC_CLIENT_TEST_PSA_ACCESS_APP_MEM_SIGNAL               (1U << (1 + 4))
+#define IPC_CLIENT_TEST_PSA_ACCESS_APP_READ_ONLY_MEM_SIGNAL     (1U << (2 + 4))
+#define IPC_CLIENT_TEST_APP_ACCESS_PSA_MEM_SIGNAL               (1U << (3 + 4))
+#define IPC_CLIENT_TEST_MEM_CHECK_SIGNAL                        (1U << (4 + 4))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __PSA_MANIFEST_TFM_IPC_CLIENT_PARTITION_H__ */
diff --git a/test/test_services/tfm_ipc_client/tfm_ipc_client_partition.yaml b/test/test_services/tfm_ipc_client/tfm_ipc_client_partition.yaml
new file mode 100644
index 0000000..e66139e
--- /dev/null
+++ b/test/test_services/tfm_ipc_client/tfm_ipc_client_partition.yaml
@@ -0,0 +1,60 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+{
+  "psa_framework_version": 1.0,
+  "name": "TFM_SP_IPC_CLIENT_TEST",
+  "type": "APPLICATION-ROT",
+  "priority": "NORMAL",
+  "entry_point": "ipc_client_test_main",
+  "stack_size": "0x0300",
+  "secure_functions": [
+  ],
+  "services" : [
+    {
+      "name": "IPC_CLIENT_TEST_BASIC",
+      "sid": "0x0000F060",
+      "non_secure_clients": true,
+      "version": 1,
+      "version_policy": "STRICT"
+    },
+    {
+      "name": "IPC_CLIENT_TEST_PSA_ACCESS_APP_MEM",
+      "sid": "0x0000F061",
+      "non_secure_clients": true,
+      "version": 1,
+      "version_policy": "STRICT"
+    },
+    {
+      "name": "IPC_CLIENT_TEST_PSA_ACCESS_APP_READ_ONLY_MEM",
+      "sid": "0x0000F062",
+      "non_secure_clients": true,
+      "version": 1,
+      "version_policy": "STRICT"
+    },
+    {
+      "name": "IPC_CLIENT_TEST_APP_ACCESS_PSA_MEM",
+      "sid": "0x0000F063",
+      "non_secure_clients": true,
+      "version": 1,
+      "version_policy": "STRICT"
+    },
+    {
+      "name": "IPC_CLIENT_TEST_MEM_CHECK",
+      "sid": "0x0000F064",
+      "non_secure_clients": true,
+      "version": 1,
+      "version_policy": "STRICT"
+    }
+  ],
+  "dependencies": [
+    "IPC_SERVICE_TEST_PSA_ACCESS_APP_READ_ONLY_MEM",
+    "IPC_SERVICE_TEST_PSA_ACCESS_APP_MEM",
+    "IPC_SERVICE_TEST_BASIC",
+    "IPC_SERVICE_TEST_APP_ACCESS_PSA_MEM"
+  ]
+}
diff --git a/test/test_services/tfm_ipc_client/tfm_ipc_client_test.c b/test/test_services/tfm_ipc_client/tfm_ipc_client_test.c
new file mode 100644
index 0000000..40c5560
--- /dev/null
+++ b/test/test_services/tfm_ipc_client/tfm_ipc_client_test.c
@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <stdio.h>
+#include <assert.h>
+#include "psa/client.h"
+#include "psa/service.h"
+#include "psa_manifest/tfm_ipc_client_partition.h"
+#include "utilities.h"
+#include "psa_manifest/sid.h"
+
+/* Define the return status */
+#define IPC_SP_TEST_SUCCESS     (1)
+#define IPC_SP_TEST_FAILED      (-1)
+
+/*
+ * The bit corresponding to service signal indicates whether
+ * the service is in use.
+ */
+uint32_t service_in_use = 0;
+
+/*
+ * Create a global const data, so that it is stored in code
+ * section which is read only.
+ */
+char const client_data_read_only = 'A';
+
+/*
+ * Fixme: Temporarily implement abort as infinite loop,
+ * will replace it later.
+ */
+static void tfm_abort(void)
+{
+    while (1)
+        ;
+}
+
+#ifdef TFM_IPC_ISOLATION_2_TEST_READ_ONLY_MEM
+static int ipc_isolation_2_psa_access_app_readonly_memory(void)
+{
+    psa_handle_t handle;
+    psa_status_t status;
+    char const *client_data_p = &client_data_read_only;
+    struct psa_invec invecs[1] = {{&client_data_p, sizeof(client_data_p)}};
+
+    handle = psa_connect(IPC_SERVICE_TEST_PSA_ACCESS_APP_READ_ONLY_MEM_SID,
+                         IPC_SERVICE_TEST_PSA_ACCESS_APP_READ_ONLY_MEM_VERSION);
+
+    if (handle <= 0) {
+        return IPC_SP_TEST_FAILED;
+    }
+
+    status = psa_call(handle, PSA_IPC_CALL, invecs, 1, NULL, 0);
+
+    /* The system should panic before here. */
+    psa_close(handle);
+    return IPC_SP_TEST_FAILED;
+}
+#endif
+
+static int ipc_isolation_2_psa_access_app_memory(void)
+{
+    psa_handle_t handle;
+    psa_status_t status;
+    int32_t result = IPC_SP_TEST_FAILED;
+    char client_data = 'A';
+    char *client_data_p = &client_data;
+    struct psa_invec invecs[1] = {{&client_data_p, sizeof(client_data_p)}};
+
+    handle = psa_connect(IPC_SERVICE_TEST_PSA_ACCESS_APP_MEM_SID,
+                         IPC_SERVICE_TEST_PSA_ACCESS_APP_MEM_VERSION);
+
+    if (handle <= 0) {
+        return result;
+    }
+
+    status = psa_call(handle, PSA_IPC_CALL, invecs, 1, NULL, 0);
+
+    if ((client_data == 'B') && (status >= 0)) {
+        result = IPC_SP_TEST_SUCCESS;
+    }
+
+    psa_close(handle);
+    return result;
+}
+
+static int ipc_client_base_test(void)
+{
+    psa_handle_t handle;
+    psa_status_t status;
+    int32_t result = IPC_SP_TEST_FAILED;
+    char str1[] = "123";
+    char str2[] = "456";
+    char str3[32], str4[32];
+    struct psa_invec invecs[2] = {{str1, sizeof(str1)/sizeof(char)},
+                                  {str2, sizeof(str2)/sizeof(char)}};
+    struct psa_outvec outvecs[2] = {{str3, sizeof(str3)/sizeof(char)},
+                                    {str4, sizeof(str4)/sizeof(char)}};
+
+    handle = psa_connect(IPC_SERVICE_TEST_BASIC_SID,
+                         IPC_SERVICE_TEST_BASIC_VERSION);
+    if (handle <= 0) {
+        return result;
+    }
+
+    status = psa_call(handle, PSA_IPC_CALL, invecs, 2, outvecs, 2);
+    if (status >= 0) {
+        result = IPC_SP_TEST_SUCCESS;
+    }
+
+    psa_close(handle);
+    return result;
+}
+
+#ifdef TFM_IPC_ISOLATION_2_APP_ACCESS_PSA_MEM
+static int ipc_client_app_access_psa_mem_test(void)
+{
+    psa_handle_t handle;
+    psa_status_t status;
+    uint8_t *outvec_data[1] = {0};
+    struct psa_outvec outvecs[1] = {{outvec_data, sizeof(outvec_data[0])}};
+
+    handle = psa_connect(IPC_SERVICE_TEST_APP_ACCESS_PSA_MEM_SID,
+                         IPC_SERVICE_TEST_APP_ACCESS_PSA_MEM_VERSION);
+
+    if (handle <= 0) {
+        return IPC_SP_TEST_FAILED;
+    }
+
+    status = psa_call(handle, PSA_IPC_CALL, NULL, 0, outvecs, 1);
+    if (status >= 0) {
+        /*
+         * outvecs should contain the pointer pointed to ipc service parition
+         * memory. Read the pointed memory should cause panic.
+         */
+        uint8_t *psa_data_p = outvec_data[0];
+        if (psa_data_p) {
+            (*psa_data_p)++;
+        }
+    }
+
+    /* The system should panic before here. */
+    psa_close(handle);
+    return IPC_SP_TEST_FAILED;
+}
+#endif
+
+#ifdef TFM_IPC_ISOLATION_2_MEM_CHECK
+static int ipc_client_mem_check_test(void)
+{
+    psa_handle_t handle;
+    psa_status_t status;
+    uint8_t *outvec_data[1] = {0};
+    struct psa_outvec outvecs[1] = {{outvec_data, sizeof(outvec_data[0])}};
+    struct psa_invec invecs[1] = {{NULL, 0}};
+
+    handle = psa_connect(IPC_SERVICE_TEST_APP_ACCESS_PSA_MEM_SID,
+                         IPC_SERVICE_TEST_APP_ACCESS_PSA_MEM_VERSION);
+
+    if (handle <= 0) {
+        return IPC_SP_TEST_FAILED;
+    }
+
+    status = psa_call(handle, PSA_IPC_CALL, NULL, 0, outvecs, 1);
+    if (status >= 0) {
+        /*
+         * outvecs should contain the pointer pointed to ipc service parition
+         * memory. In psa_call, it checks whether the target partition has the
+         * access right to the invecs indicated memory. If no, the system will
+         * panic.
+         */
+        uint8_t *psa_data_p = outvec_data[0];
+        if (psa_data_p) {
+            invecs[0].base = psa_data_p;
+            invecs[0].len = sizeof(psa_data_p);
+            psa_call(handle, PSA_IPC_CALL, invecs, 1, NULL, 0);
+        }
+    }
+
+    /* The system should panic before here. */
+    psa_close(handle);
+    return IPC_SP_TEST_FAILED;
+}
+#endif
+
+static void ipc_client_handle_ser_req(psa_msg_t msg, uint32_t signals,
+                                      int (*fn)(void))
+{
+    psa_status_t r;
+    int32_t ret;
+
+    switch (msg.type) {
+    case PSA_IPC_CONNECT:
+        if (service_in_use & signals) {
+            r = PSA_ERROR_CONNECTION_REFUSED;
+        } else {
+            service_in_use |= signals;
+            r = PSA_SUCCESS;
+        }
+        psa_reply(msg.handle, r);
+        break;
+    case PSA_IPC_CALL:
+        ret = (*fn)();
+        if (msg.out_size[0] != 0) {
+            psa_write(msg.handle, 0, &ret, sizeof(ret));
+        }
+        psa_reply(msg.handle, PSA_SUCCESS);
+        break;
+    case PSA_IPC_DISCONNECT:
+        assert((service_in_use & signals) != 0);
+        service_in_use &= ~(signals);
+        psa_reply(msg.handle, PSA_SUCCESS);
+        break;
+    default:
+        /* cannot get here? [broken SPM]. TODO*/
+        tfm_abort();
+        break;
+    }
+}
+
+void ipc_client_test_main(void)
+{
+    psa_msg_t msg;
+    uint32_t signals = 0;
+
+    while (1) {
+        signals = psa_wait(PSA_WAIT_ANY, PSA_BLOCK);
+        psa_get(signals, &msg);
+        if ((signals & IPC_CLIENT_TEST_BASIC_SIGNAL)) {
+            ipc_client_handle_ser_req(msg, IPC_CLIENT_TEST_BASIC_SIGNAL,
+                                      &ipc_client_base_test);
+        } else if (signals & IPC_CLIENT_TEST_PSA_ACCESS_APP_MEM_SIGNAL) {
+            ipc_client_handle_ser_req(msg,
+                                     IPC_CLIENT_TEST_PSA_ACCESS_APP_MEM_SIGNAL,
+                                     &ipc_isolation_2_psa_access_app_memory);
+#ifdef TFM_IPC_ISOLATION_2_TEST_READ_ONLY_MEM
+        } else if (signals &
+                    IPC_CLIENT_TEST_PSA_ACCESS_APP_READ_ONLY_MEM_SIGNAL) {
+            ipc_client_handle_ser_req(msg,
+                           IPC_CLIENT_TEST_PSA_ACCESS_APP_READ_ONLY_MEM_SIGNAL,
+                           &ipc_isolation_2_psa_access_app_readonly_memory);
+#endif
+#ifdef TFM_IPC_ISOLATION_2_APP_ACCESS_PSA_MEM
+        } else if (signals & IPC_CLIENT_TEST_APP_ACCESS_PSA_MEM_SIGNAL) {
+            ipc_client_handle_ser_req(msg,
+                                     IPC_CLIENT_TEST_APP_ACCESS_PSA_MEM_SIGNAL,
+                                     &ipc_client_app_access_psa_mem_test);
+#endif
+#ifdef TFM_IPC_ISOLATION_2_MEM_CHECK
+        } else if (signals & IPC_CLIENT_TEST_MEM_CHECK_SIGNAL) {
+            ipc_client_handle_ser_req(msg, IPC_CLIENT_TEST_MEM_CHECK_SIGNAL,
+                                      &ipc_client_mem_check_test);
+#endif
+        } else {
+            /* Should not go here. */
+            tfm_abort();
+        }
+    }
+}
diff --git a/test/test_services/tfm_ipc_service/psa_manifest/tfm_ipc_service_partition.h b/test/test_services/tfm_ipc_service/psa_manifest/tfm_ipc_service_partition.h
new file mode 100644
index 0000000..47427a0
--- /dev/null
+++ b/test/test_services/tfm_ipc_service/psa_manifest/tfm_ipc_service_partition.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+/*********** WARNING: This is an auto-generated file. Do not edit! ***********/
+
+#ifndef __PSA_MANIFEST_TFM_IPC_SERVICE_PARTITION_H__
+#define __PSA_MANIFEST_TFM_IPC_SERVICE_PARTITION_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define IPC_SERVICE_TEST_BASIC_SIGNAL                           (1U << (0 + 4))
+#define IPC_SERVICE_TEST_PSA_ACCESS_APP_MEM_SIGNAL              (1U << (1 + 4))
+#define IPC_SERVICE_TEST_PSA_ACCESS_APP_READ_ONLY_MEM_SIGNAL    (1U << (2 + 4))
+#define IPC_SERVICE_TEST_APP_ACCESS_PSA_MEM_SIGNAL              (1U << (3 + 4))
+#define IPC_SERVICE_TEST_CLIENT_PROGRAMMER_ERROR_SIGNAL         (1U << (4 + 4))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __PSA_MANIFEST_TFM_IPC_SERVICE_PARTITION_H__ */
diff --git a/test/test_services/tfm_ipc_service/tfm_ipc_service_partition.yaml b/test/test_services/tfm_ipc_service/tfm_ipc_service_partition.yaml
new file mode 100644
index 0000000..d06bddb
--- /dev/null
+++ b/test/test_services/tfm_ipc_service/tfm_ipc_service_partition.yaml
@@ -0,0 +1,54 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+{
+  "psa_framework_version": 1.0,
+  "name": "TFM_SP_IPC_SERVICE_TEST",
+  "type": "PSA-ROT",
+  "priority": "HIGH",
+  "entry_point": "ipc_service_test_main",
+  "stack_size": "0x0220",
+  "secure_functions": [
+  ],
+  "services" : [
+    {
+      "name": "IPC_SERVICE_TEST_BASIC",
+      "sid": "0x0000F080",
+      "non_secure_clients": true,
+      "version": 1,
+      "version_policy": "STRICT"
+    },
+    {
+      "name": "IPC_SERVICE_TEST_PSA_ACCESS_APP_MEM",
+      "sid": "0x0000F081",
+      "non_secure_clients": true,
+      "version": 1,
+      "version_policy": "STRICT"
+    },
+    {
+      "name": "IPC_SERVICE_TEST_PSA_ACCESS_APP_READ_ONLY_MEM",
+      "sid": "0x0000F082",
+      "non_secure_clients": true,
+      "version": 1,
+      "version_policy": "STRICT"
+    },
+    {
+      "name": "IPC_SERVICE_TEST_APP_ACCESS_PSA_MEM",
+      "sid": "0x0000F083",
+      "non_secure_clients": true,
+      "version": 1,
+      "version_policy": "STRICT"
+    },
+    {
+      "name": "IPC_SERVICE_TEST_CLIENT_PROGRAMMER_ERROR",
+      "sid": "0x0000F084",
+      "non_secure_clients": true,
+      "version": 1,
+      "version_policy": "STRICT"
+    }
+  ]
+}
diff --git a/test/test_services/tfm_ipc_service/tfm_ipc_service_test.c b/test/test_services/tfm_ipc_service/tfm_ipc_service_test.c
new file mode 100644
index 0000000..ad7d1f2
--- /dev/null
+++ b/test/test_services/tfm_ipc_service/tfm_ipc_service_test.c
@@ -0,0 +1,291 @@
+/*
+ * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include "psa/client.h"
+#include "psa/service.h"
+#include "tfm_secure_api.h"
+#include "tfm_api.h"
+#include "psa_manifest/tfm_ipc_service_partition.h"
+
+#define IPC_SERVICE_BUFFER_LEN                          32
+
+/* Define the whether the service is inuse flag. */
+static uint32_t service_in_use = 0;
+
+/* Define the global variable for the IPC_APP_ACCESS_PSA_MEM_SID service. */
+uint8_t ipc_servic_data;
+uint8_t *ipc_service_data_p = &ipc_servic_data;
+
+/*
+ * Fixme: Temporarily implement abort as infinite loop,
+ * will replace it later.
+ */
+static void tfm_abort(void)
+{
+    while (1)
+        ;
+}
+
+static void ipc_service_basic(void)
+{
+    psa_msg_t msg;
+    psa_status_t r;
+    int i;
+    uint8_t rec_buf[IPC_SERVICE_BUFFER_LEN];
+    uint8_t send_buf[IPC_SERVICE_BUFFER_LEN] = "It is just for IPC call test.";
+
+    psa_get(IPC_SERVICE_TEST_BASIC_SIGNAL, &msg);
+    switch (msg.type) {
+    case PSA_IPC_CONNECT:
+        if (service_in_use & IPC_SERVICE_TEST_BASIC_SIGNAL) {
+            r = PSA_ERROR_CONNECTION_REFUSED;
+        } else {
+            service_in_use |= IPC_SERVICE_TEST_BASIC_SIGNAL;
+            r = PSA_SUCCESS;
+        }
+        psa_reply(msg.handle, r);
+        break;
+    case PSA_IPC_CALL:
+        for (i = 0; i < PSA_MAX_IOVEC; i++) {
+            if (msg.in_size[i] != 0) {
+                psa_read(msg.handle, i, rec_buf, IPC_SERVICE_BUFFER_LEN);
+            }
+            if (msg.out_size[i] != 0) {
+                psa_write(msg.handle, i, send_buf, IPC_SERVICE_BUFFER_LEN);
+            }
+        }
+        psa_reply(msg.handle, PSA_SUCCESS);
+        break;
+    case PSA_IPC_DISCONNECT:
+        assert((service_in_use & IPC_SERVICE_TEST_BASIC_SIGNAL) != 0);
+        service_in_use &= ~IPC_SERVICE_TEST_BASIC_SIGNAL;
+        psa_reply(msg.handle, PSA_SUCCESS);
+        break;
+    default:
+        /* cannot get here? [broken SPM]. TODO*/
+        tfm_abort();
+        break;
+    }
+}
+
+static void ipc_service_psa_access_app_mem(void)
+{
+    psa_msg_t msg;
+    psa_status_t r;
+    char rec_data;
+    uint32_t rec_buf;
+
+    psa_get(IPC_SERVICE_TEST_PSA_ACCESS_APP_MEM_SIGNAL, &msg);
+    switch (msg.type) {
+    case PSA_IPC_CONNECT:
+        if (service_in_use & IPC_SERVICE_TEST_PSA_ACCESS_APP_MEM_SIGNAL) {
+            r = PSA_ERROR_CONNECTION_REFUSED;
+        } else {
+            service_in_use |= IPC_SERVICE_TEST_PSA_ACCESS_APP_MEM_SIGNAL;
+            r = PSA_SUCCESS;
+        }
+        psa_reply(msg.handle, r);
+        break;
+    case PSA_IPC_CALL:
+        /*
+         * rec_buf is a pointer pointed to a char type memory in client stack.
+         */
+        if (msg.in_size[0] != 0) {
+            psa_read(msg.handle, 0, &rec_buf, 4);
+            rec_data = *(char *)rec_buf;
+
+            /* rec_data is assigned to 'A' by the client side. */
+            if (rec_data != 'A') {
+                psa_reply(msg.handle, -1);
+                break;
+            }
+
+            /* Change the char type client stack memory to 'B'. */
+            *((char *)rec_buf) = 'B';
+        }
+        psa_reply(msg.handle, PSA_SUCCESS);
+        break;
+    case PSA_IPC_DISCONNECT:
+        assert((service_in_use & IPC_SERVICE_TEST_PSA_ACCESS_APP_MEM_SIGNAL)
+               != 0);
+        service_in_use &= ~IPC_SERVICE_TEST_PSA_ACCESS_APP_MEM_SIGNAL;
+        psa_reply(msg.handle, PSA_SUCCESS);
+        break;
+    default:
+        /* cannot get here? [broken SPM]. TODO*/
+        tfm_abort();
+        break;
+    }
+}
+
+#ifdef TFM_IPC_ISOLATION_2_TEST_READ_ONLY_MEM
+static void ipc_service_psa_access_app_readonly_mem(void)
+{
+    psa_msg_t msg;
+    psa_status_t r;
+    char rec_data;
+    uint32_t rec_buf;
+
+    psa_get(IPC_SERVICE_TEST_PSA_ACCESS_APP_READ_ONLY_MEM_SIGNAL, &msg);
+    switch (msg.type) {
+    case PSA_IPC_CONNECT:
+        if (service_in_use &
+            IPC_SERVICE_TEST_PSA_ACCESS_APP_READ_ONLY_MEM_SIGNAL) {
+            r = PSA_ERROR_CONNECTION_REFUSED;
+        } else {
+            service_in_use |=
+                          IPC_SERVICE_TEST_PSA_ACCESS_APP_READ_ONLY_MEM_SIGNAL;
+            r = PSA_SUCCESS;
+        }
+        psa_reply(msg.handle, r);
+        break;
+    case PSA_IPC_CALL:
+         /*
+          * rec_buf is a pointer pointed to a char type memory in client
+          * code section. Write the memory will cause MemManage fault.
+          */
+        if (msg.in_size[0] != 0) {
+            psa_read(msg.handle, 0, &rec_buf, 4);
+            rec_data = *(char *)rec_buf;
+
+            /* rec_data is assigned to 'A' by the client side. */
+            if (rec_data != 'A') {
+                psa_reply(msg.handle, -1);
+                break;
+            }
+
+            /* Write the char type read only memory. */
+            *((char *)rec_buf) = 'B';
+        }
+
+        psa_reply(msg.handle, PSA_SUCCESS);
+        break;
+    case PSA_IPC_DISCONNECT:
+        assert((service_in_use &
+                IPC_SERVICE_TEST_PSA_ACCESS_APP_READ_ONLY_MEM_SIGNAL) != 0);
+        service_in_use &=
+                         ~IPC_SERVICE_TEST_PSA_ACCESS_APP_READ_ONLY_MEM_SIGNAL;
+        psa_reply(msg.handle, PSA_SUCCESS);
+        break;
+    default:
+        /* cannot get here? [broken SPM]. TODO*/
+        tfm_abort();
+        break;
+    }
+}
+#endif
+
+#if defined TFM_IPC_ISOLATION_2_MEM_CHECK \
+    || defined TFM_IPC_ISOLATION_2_APP_ACCESS_PSA_MEM
+static void ipc_service_app_access_psa_mem(void)
+{
+    psa_msg_t msg;
+    psa_status_t r;
+
+    psa_get(IPC_SERVICE_TEST_APP_ACCESS_PSA_MEM_SIGNAL, &msg);
+    switch (msg.type) {
+    case PSA_IPC_CONNECT:
+        if (service_in_use & IPC_SERVICE_TEST_APP_ACCESS_PSA_MEM_SIGNAL) {
+            r = PSA_ERROR_CONNECTION_REFUSED;
+        } else {
+            service_in_use |= IPC_SERVICE_TEST_APP_ACCESS_PSA_MEM_SIGNAL;
+            r = PSA_SUCCESS;
+        }
+
+        psa_reply(msg.handle, r);
+        break;
+    case PSA_IPC_CALL:
+        if (msg.out_size[0] != 0) {
+
+            /*
+             * Write a pointer to outvec. The pointer points to uint8_t
+             * memory in ipc servive partition.
+             */
+            psa_write(msg.handle, 0, &ipc_service_data_p,
+                      sizeof(ipc_service_data_p));
+        }
+
+        psa_reply(msg.handle, PSA_SUCCESS);
+        break;
+    case PSA_IPC_DISCONNECT:
+        assert((service_in_use & IPC_SERVICE_TEST_APP_ACCESS_PSA_MEM_SIGNAL)
+               != 0);
+        service_in_use &= ~IPC_SERVICE_TEST_APP_ACCESS_PSA_MEM_SIGNAL;
+        psa_reply(msg.handle, PSA_SUCCESS);
+        break;
+    default:
+        /* cannot get here? [broken SPM]. TODO*/
+        tfm_abort();
+        break;
+    }
+}
+#endif
+
+static void ipc_service_programmer_error(void)
+{
+    psa_msg_t msg;
+    psa_status_t r;
+
+    psa_get(IPC_SERVICE_TEST_CLIENT_PROGRAMMER_ERROR_SIGNAL, &msg);
+    switch (msg.type) {
+    case PSA_IPC_CONNECT:
+        if (service_in_use & IPC_SERVICE_TEST_CLIENT_PROGRAMMER_ERROR_SIGNAL) {
+            r = PSA_ERROR_CONNECTION_REFUSED;
+        } else {
+            service_in_use |= IPC_SERVICE_TEST_CLIENT_PROGRAMMER_ERROR_SIGNAL;
+            r = PSA_SUCCESS;
+        }
+        psa_reply(msg.handle, r);
+        break;
+    case PSA_IPC_CALL:
+        psa_reply(msg.handle, PSA_ERROR_PROGRAMMER_ERROR);
+        break;
+    case PSA_IPC_DISCONNECT:
+        assert((service_in_use
+                & IPC_SERVICE_TEST_CLIENT_PROGRAMMER_ERROR_SIGNAL) != 0);
+        service_in_use &= ~IPC_SERVICE_TEST_CLIENT_PROGRAMMER_ERROR_SIGNAL;
+        psa_reply(msg.handle, PSA_SUCCESS);
+        break;
+    default:
+        /* cannot get here? [broken SPM]. TODO*/
+        tfm_abort();
+        break;
+    }
+}
+
+/* Test thread */
+void ipc_service_test_main(void *param)
+{
+    uint32_t signals = 0;
+
+    while (1) {
+        signals = psa_wait(PSA_WAIT_ANY, PSA_BLOCK);
+        if (signals & IPC_SERVICE_TEST_BASIC_SIGNAL) {
+            ipc_service_basic();
+        } else if (signals & IPC_SERVICE_TEST_PSA_ACCESS_APP_MEM_SIGNAL) {
+            ipc_service_psa_access_app_mem();
+#ifdef TFM_IPC_ISOLATION_2_TEST_READ_ONLY_MEM
+        } else if (signals
+                   & IPC_SERVICE_TEST_PSA_ACCESS_APP_READ_ONLY_MEM_SIGNAL) {
+            ipc_service_psa_access_app_readonly_mem();
+#endif
+#if defined TFM_IPC_ISOLATION_2_MEM_CHECK \
+    || defined TFM_IPC_ISOLATION_2_APP_ACCESS_PSA_MEM
+        } else if (signals & IPC_SERVICE_TEST_APP_ACCESS_PSA_MEM_SIGNAL) {
+            ipc_service_app_access_psa_mem();
+#endif
+        } else if (signals & IPC_SERVICE_TEST_CLIENT_PROGRAMMER_ERROR_SIGNAL) {
+            ipc_service_programmer_error();
+        } else {
+            /* Should not come here */
+            tfm_abort();
+        }
+    }
+}
diff --git a/test/test_services/tfm_irq_test_service_1/psa_manifest/tfm_irq_test_service_1.h b/test/test_services/tfm_irq_test_service_1/psa_manifest/tfm_irq_test_service_1.h
new file mode 100644
index 0000000..18ef22a
--- /dev/null
+++ b/test/test_services/tfm_irq_test_service_1/psa_manifest/tfm_irq_test_service_1.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+/*********** WARNING: This is an auto-generated file. Do not edit! ***********/
+
+#ifndef __PSA_MANIFEST_TFM_IRQ_TEST_SERVICE_1_H__
+#define __PSA_MANIFEST_TFM_IRQ_TEST_SERVICE_1_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SPM_CORE_IRQ_TEST_1_PREPARE_TEST_SCENARIO_SIGNAL        (1U << (0 + 4))
+#define SPM_CORE_IRQ_TEST_1_EXECUTE_TEST_SCENARIO_SIGNAL        (1U << (1 + 4))
+
+#define SPM_CORE_IRQ_TEST_1_SIGNAL_TIMER_0_IRQ                  (1U << (27 + 4))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __PSA_MANIFEST_TFM_IRQ_TEST_SERVICE_1_H__ */
diff --git a/test/test_services/tfm_irq_test_service_1/tfm_irq_test_service_1.c b/test/test_services/tfm_irq_test_service_1/tfm_irq_test_service_1.c
new file mode 100644
index 0000000..559a3ec
--- /dev/null
+++ b/test/test_services/tfm_irq_test_service_1/tfm_irq_test_service_1.c
@@ -0,0 +1,409 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <stddef.h>
+#include "tfm_api.h"
+#include "tfm_veneers.h"
+#include "tfm_secure_api.h"
+#include "tfm/tfm_spm_services.h"
+#include "test/test_services/tfm_core_test/core_test_defs.h"
+#include "psa/service.h"
+#include "psa_manifest/pid.h"
+#include "psa_manifest/tfm_irq_test_service_1.h"
+#include "tfm_plat_test.h"
+
+#define IRQ_TEST_TOOL_CODE_LOCATION(name)
+
+static enum irq_test_scenario_t current_scenario = IRQ_TEST_SCENARIO_NONE;
+static struct irq_test_execution_data_t *current_execution_data;
+
+#ifdef TFM_PSA_API
+static psa_handle_t execute_msg_handle = -1;
+#endif
+
+/**
+ * \brief unrecoverable error during test execution.
+ *
+ * Called from places, where error code would be very difficult, or impossible
+ * to return.
+ */
+static void halt_test_execution(void)
+{
+    while (1) {
+        ; /* Test fail */
+    }
+}
+
+/**
+ * \brief Stop the timer, and disable and clear interrupts
+ */
+static void stop_timer(void)
+{
+    IRQ_TEST_TOOL_CODE_LOCATION(stop_secure_timer);
+    tfm_plat_test_secure_timer_stop();
+}
+
+int32_t spm_irq_test_1_prepare_test_scenario_internal(
+                               enum irq_test_scenario_t irq_test_scenario,
+                               struct irq_test_execution_data_t *execution_data)
+{
+    current_scenario = irq_test_scenario;
+    current_execution_data = execution_data;
+
+    current_execution_data->timer0_triggered = 0;
+
+    switch (irq_test_scenario) {
+    case IRQ_TEST_SCENARIO_NONE:
+        break; /* uninitialised scenario */
+    case IRQ_TEST_SCENARIO_1:
+    case IRQ_TEST_SCENARIO_2:
+    case IRQ_TEST_SCENARIO_3:
+    case IRQ_TEST_SCENARIO_4:
+        tfm_plat_test_secure_timer_start();
+        break;
+    case IRQ_TEST_SCENARIO_5:
+        /* Do nothing */
+        break;
+    default:
+        return CORE_TEST_ERRNO_INVALID_PARAMETER;
+    }
+
+    return CORE_TEST_ERRNO_SUCCESS;
+}
+
+int32_t spm_irq_test_1_execute_test_scenario(
+                                     struct psa_invec *in_vec, size_t in_len,
+                                     struct psa_outvec *out_vec, size_t out_len)
+{
+    enum irq_test_scenario_t irq_test_scenario =
+            (enum irq_test_scenario_t) *(uint32_t *)in_vec[0].base;
+    psa_signal_t signals = 0;
+
+    if (irq_test_scenario != current_scenario) {
+        return CORE_TEST_ERRNO_INVALID_PARAMETER;
+    }
+
+    switch (irq_test_scenario) {
+    case IRQ_TEST_SCENARIO_NONE:
+        return CORE_TEST_ERRNO_INVALID_PARAMETER;
+    case IRQ_TEST_SCENARIO_1:
+    case IRQ_TEST_SCENARIO_2:
+        /* nothing to do*/
+        break;
+    case IRQ_TEST_SCENARIO_3:
+        if (current_execution_data->timer0_triggered) {
+            return CORE_TEST_ERRNO_TEST_FAULT;
+        }
+        while (!current_execution_data->timer0_triggered) {
+            /* Wait for the timer to be triggered */
+            ;
+        }
+        break;
+    case IRQ_TEST_SCENARIO_4:
+        if (current_execution_data->timer0_triggered) {
+            return CORE_TEST_ERRNO_TEST_FAULT;
+        }
+        while ((signals & SPM_CORE_IRQ_TEST_1_SIGNAL_TIMER_0_IRQ) == 0) {
+            signals = psa_wait(SPM_CORE_IRQ_TEST_1_SIGNAL_TIMER_0_IRQ,
+                               PSA_BLOCK);
+        }
+        if (!current_execution_data->timer0_triggered) {
+            return CORE_TEST_ERRNO_TEST_FAULT;
+        }
+        psa_eoi(SPM_CORE_IRQ_TEST_1_SIGNAL_TIMER_0_IRQ);
+        break;
+    case IRQ_TEST_SCENARIO_5:
+        /* nothing to do*/
+        break;
+    default:
+        return CORE_TEST_ERRNO_INVALID_PARAMETER;
+    }
+
+    return CORE_TEST_ERRNO_SUCCESS;
+}
+
+uint32_t spm_irq_test_1_prepare_test_scenario(
+                                     struct psa_invec *in_vec, size_t in_len,
+                                     struct psa_outvec *out_vec, size_t out_len)
+{
+    enum irq_test_scenario_t irq_test_scenario;
+    struct irq_test_execution_data_t *execution_data;
+
+    if ((in_len != 2) || (out_len != 0)) {
+        return CORE_TEST_ERRNO_INVALID_PARAMETER;
+    }
+
+    if ((in_vec[0].len != sizeof(uint32_t)) ||
+        (in_vec[1].len != sizeof(struct irq_test_execution_data_t *))) {
+        return CORE_TEST_ERRNO_INVALID_PARAMETER;
+    }
+
+    irq_test_scenario =
+            (enum irq_test_scenario_t) *(uint32_t *)in_vec[0].base;
+
+    execution_data =
+            *(struct irq_test_execution_data_t **)in_vec[1].base;
+
+    return spm_irq_test_1_prepare_test_scenario_internal(irq_test_scenario,
+                                                         execution_data);
+}
+
+#ifndef TFM_PSA_API
+
+void SPM_CORE_IRQ_TEST_1_SIGNAL_TIMER_0_IRQ_isr(void)
+{
+    stop_timer();
+
+    if ((current_execution_data == NULL) ||
+        (current_execution_data->timer0_triggered != 0)) {
+            halt_test_execution();
+    }
+
+    current_execution_data->timer0_triggered = 1;
+
+    switch (current_scenario) {
+    case IRQ_TEST_SCENARIO_NONE:
+        psa_eoi(SPM_CORE_IRQ_TEST_1_SIGNAL_TIMER_0_IRQ);
+        break;
+    case IRQ_TEST_SCENARIO_1:
+    case IRQ_TEST_SCENARIO_2:
+    case IRQ_TEST_SCENARIO_3:
+        psa_eoi(SPM_CORE_IRQ_TEST_1_SIGNAL_TIMER_0_IRQ);
+        break;
+    case IRQ_TEST_SCENARIO_4:
+        /* nothing to do*/
+        break;
+    case IRQ_TEST_SCENARIO_5:
+        halt_test_execution();
+        /* No secure interrups are used in this scenario */
+        break;
+    default:
+        halt_test_execution();
+        break;
+    }
+
+    __asm("DSB");
+}
+
+#else /* TFM_PSA_API */
+
+typedef psa_status_t (*irq_test_1_func_t)(psa_msg_t *msg);
+
+static void spm_irq_test_1_signal_handle(psa_signal_t signal,
+                                         irq_test_1_func_t pfn)
+{
+    psa_msg_t msg;
+    psa_status_t status;
+
+    status = psa_get(signal, &msg);
+    if (status) {
+        return;
+    }
+
+    switch (msg.type) {
+    case PSA_IPC_CONNECT:
+        psa_reply(msg.handle, PSA_SUCCESS);
+        break;
+    case PSA_IPC_CALL:
+        status = pfn(&msg);
+        psa_reply(msg.handle, status);
+        break;
+    case PSA_IPC_DISCONNECT:
+        psa_reply(msg.handle, PSA_SUCCESS);
+        break;
+    default:
+        break;
+    }
+}
+
+void TIMER_0_isr_ipc(void)
+{
+    current_execution_data->timer0_triggered = 1;
+
+    stop_timer();
+
+    switch (current_scenario) {
+    case IRQ_TEST_SCENARIO_NONE:
+        psa_eoi(SPM_CORE_IRQ_TEST_1_SIGNAL_TIMER_0_IRQ);
+        break;
+    case IRQ_TEST_SCENARIO_1:
+    case IRQ_TEST_SCENARIO_2:
+        psa_eoi(SPM_CORE_IRQ_TEST_1_SIGNAL_TIMER_0_IRQ);
+        break;
+    case IRQ_TEST_SCENARIO_3:
+        /* execute_msg_handle have to be valid at this point */
+        if (execute_msg_handle <= 0) {
+            halt_test_execution();
+        }
+        /* reply to the execute message, to unblock NS side */
+        psa_reply(execute_msg_handle, CORE_TEST_ERRNO_SUCCESS);
+        execute_msg_handle = -1;
+        psa_eoi(SPM_CORE_IRQ_TEST_1_SIGNAL_TIMER_0_IRQ);
+        break;
+    case IRQ_TEST_SCENARIO_4:
+        /* This case should never be executed as in this scenario the 'execute
+         * function does the psa wait, and calls psa_eoi immediately'. So when
+         * execution gets to the psa_wait in the main loop, the IRQ signal is
+         * unset.
+         */
+        halt_test_execution();
+        break;
+    case IRQ_TEST_SCENARIO_5:
+        /* No secure interrups are used in this scenario */
+        halt_test_execution();
+        break;
+    default:
+        halt_test_execution();
+        break;
+    }
+}
+
+static psa_status_t spm_irq_test_1_wrap_prepare_test_scenario(psa_msg_t *msg)
+{
+    uint32_t irq_test_scenario;
+    struct irq_test_execution_data_t *execution_data;
+    size_t num;
+
+    if ((msg->in_size[0] != sizeof(uint32_t)) ||
+        (msg->in_size[1] != sizeof(struct irq_test_execution_data_t *))) {
+        return CORE_TEST_ERRNO_INVALID_PARAMETER;
+    }
+
+
+    num = psa_read(msg->handle, 0, &irq_test_scenario, sizeof(uint32_t));
+    if (num != msg->in_size[0]) {
+        return CORE_TEST_ERRNO_INVALID_PARAMETER;
+    }
+
+    num = psa_read(msg->handle, 1, &execution_data,
+                   sizeof(struct irq_test_execution_data_t *));
+    if (num != msg->in_size[1]) {
+        return CORE_TEST_ERRNO_INVALID_PARAMETER;
+    }
+
+    return spm_irq_test_1_prepare_test_scenario_internal((enum irq_test_scenario_t)
+                                                         irq_test_scenario,
+                                                         execution_data);
+}
+
+static void spm_irq_test_1_execute_test_scenario_ipc_call(psa_msg_t *msg)
+{
+    size_t num;
+    uint32_t irq_test_scenario;
+    psa_signal_t signals = 0;
+
+    num = psa_read(msg->handle, 0, &irq_test_scenario, sizeof(uint32_t));
+    if ((num != msg->in_size[0]) ||
+        (irq_test_scenario != current_scenario)) {
+        psa_reply(msg->handle, CORE_TEST_ERRNO_INVALID_PARAMETER);
+        return;
+    }
+
+    switch (irq_test_scenario) {
+    case IRQ_TEST_SCENARIO_NONE:
+        psa_reply(msg->handle, CORE_TEST_ERRNO_INVALID_PARAMETER);
+        return;
+    case IRQ_TEST_SCENARIO_1:
+    case IRQ_TEST_SCENARIO_2:
+    case IRQ_TEST_SCENARIO_5:
+        /* nothing to do, return success */
+        psa_reply(msg->handle, CORE_TEST_ERRNO_SUCCESS);
+        return;
+    case IRQ_TEST_SCENARIO_3:
+        if (current_execution_data->timer0_triggered) {
+            psa_reply(msg->handle, CORE_TEST_ERRNO_TEST_FAULT);
+            return;
+        }
+        /* We need the ISR to be able to run. So we do a wait to let
+         * it run and set timer0_triggered. This message will be replied
+         * from the ISR, so the NS side remains blocked for now. To be able
+         * to reply, we also save the handle of the message.
+         */
+        if (execute_msg_handle > 0) {
+            /* execute_msg_handle should be uninitialised at this point */
+            psa_reply(msg->handle, CORE_TEST_ERRNO_TEST_FAULT);
+            return;
+        }
+        execute_msg_handle = msg->handle;
+        while (!(signals & SPM_CORE_IRQ_TEST_1_SIGNAL_TIMER_0_IRQ)) {
+           signals = psa_wait(SPM_CORE_IRQ_TEST_1_SIGNAL_TIMER_0_IRQ,
+                              PSA_BLOCK);
+        }
+        return;
+    case IRQ_TEST_SCENARIO_4:
+        while (!(signals & SPM_CORE_IRQ_TEST_1_SIGNAL_TIMER_0_IRQ)) {
+            signals = psa_wait(SPM_CORE_IRQ_TEST_1_SIGNAL_TIMER_0_IRQ,
+                              PSA_BLOCK);
+        }
+        /* There is no need to call the ISR in this scenario, so we can
+         * clear the IRQ signal
+         */
+        stop_timer();
+        psa_eoi(SPM_CORE_IRQ_TEST_1_SIGNAL_TIMER_0_IRQ);
+        psa_reply(msg->handle, CORE_TEST_ERRNO_SUCCESS);
+        break;
+    default:
+        psa_reply(msg->handle, CORE_TEST_ERRNO_INVALID_PARAMETER);
+        return;
+    }
+}
+
+static void spm_irq_test_1_execute_test_scenario_ipc(psa_signal_t signal)
+{
+    psa_msg_t msg;
+    psa_status_t status;
+
+    status = psa_get(signal, &msg);
+    if (status) {
+        return;
+    }
+
+    if (signal != SPM_CORE_IRQ_TEST_1_EXECUTE_TEST_SCENARIO_SIGNAL) {
+        psa_reply(msg.handle, CORE_TEST_ERRNO_INVALID_PARAMETER);
+    }
+
+    switch (msg.type) {
+    case PSA_IPC_CONNECT:
+        psa_reply(msg.handle, PSA_SUCCESS);
+        break;
+    case PSA_IPC_CALL:
+        spm_irq_test_1_execute_test_scenario_ipc_call(&msg);
+    break;
+    case PSA_IPC_DISCONNECT:
+        psa_reply(msg.handle, PSA_SUCCESS);
+        break;
+    default:
+        break;
+    }
+}
+#endif /* TFM_PSA_API */
+
+int32_t tfm_irq_test_1_init(void)
+{
+    tfm_enable_irq(SPM_CORE_IRQ_TEST_1_SIGNAL_TIMER_0_IRQ);
+#ifdef TFM_PSA_API
+    psa_signal_t signals = 0;
+
+    while (1) {
+        signals = psa_wait(PSA_WAIT_ANY, PSA_BLOCK);
+        if (signals & SPM_CORE_IRQ_TEST_1_SIGNAL_TIMER_0_IRQ) {
+            TIMER_0_isr_ipc();
+        } else if (signals & SPM_CORE_IRQ_TEST_1_PREPARE_TEST_SCENARIO_SIGNAL) {
+            spm_irq_test_1_signal_handle(
+                               SPM_CORE_IRQ_TEST_1_PREPARE_TEST_SCENARIO_SIGNAL,
+                               spm_irq_test_1_wrap_prepare_test_scenario);
+        } else if (signals & SPM_CORE_IRQ_TEST_1_EXECUTE_TEST_SCENARIO_SIGNAL) {
+            spm_irq_test_1_execute_test_scenario_ipc(
+                              SPM_CORE_IRQ_TEST_1_EXECUTE_TEST_SCENARIO_SIGNAL);
+        } else {
+            ; /* do nothing */
+        }
+    }
+#else
+    return TFM_SUCCESS;
+#endif /* TFM_PSA_API */
+}
diff --git a/test/test_services/tfm_irq_test_service_1/tfm_irq_test_service_1.yaml b/test/test_services/tfm_irq_test_service_1/tfm_irq_test_service_1.yaml
new file mode 100644
index 0000000..1825666
--- /dev/null
+++ b/test/test_services/tfm_irq_test_service_1/tfm_irq_test_service_1.yaml
@@ -0,0 +1,60 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+{
+  "psa_framework_version": 1.0,
+  "name": "TFM_IRQ_TEST_1",
+  "type": "APPLICATION-ROT",
+  "priority": "NORMAL",
+  "entry_point": "tfm_irq_test_1_init",
+  "stack_size": "0x0400",
+  "mmio_regions": [
+    {
+      "name": "TFM_PERIPHERAL_TIMER0",
+      "permission": "READ-WRITE"
+    }
+  ],
+  "secure_functions": [
+    {
+      "name": "SPM_IRQ_TEST_1_PREPARE_TEST_SCENARIO",
+      "signal": "SPM_IRQ_TEST_1_PREPARE_TEST_SCENARIO",
+      "non_secure_clients": true,
+      "version": 1,
+      "version_policy": "STRICT"
+    },
+    {
+      "name": "SPM_IRQ_TEST_1_EXECUTE_TEST_SCENARIO",
+      "signal": "SPM_IRQ_TEST_1_EXECUTE_TEST_SCENARIO",
+      "non_secure_clients": true,
+      "version": 1,
+      "version_policy": "STRICT"
+    }
+  ],
+  "irqs": [
+    {
+      "source": "TFM_TIMER0_IRQ",
+      "signal": "SPM_CORE_IRQ_TEST_1_SIGNAL_TIMER_0_IRQ",
+      "tfm_irq_priority": 64,
+    }
+  ],
+  "services": [
+    {
+      "name": "SPM_CORE_IRQ_TEST_1_PREPARE_TEST_SCENARIO",
+      "sid": "0x0000F0A0",
+      "non_secure_clients": true,
+      "version": 1,
+      "version_policy": "STRICT"
+    },
+    {
+      "name": "SPM_CORE_IRQ_TEST_1_EXECUTE_TEST_SCENARIO",
+      "sid": "0x0000F0A1",
+      "non_secure_clients": true,
+      "version": 1,
+      "version_policy": "STRICT"
+    },
+  ]
+}
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
new file mode 100644
index 0000000..ad7ccb5
--- /dev/null
+++ b/test/test_services/tfm_multi_core_test/psa_manifest/tfm_multi_core_test.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+/*********** WARNING: This is an auto-generated file. Do not edit! ***********/
+
+#ifndef __PSA_MANIFEST_TFM_MULTI_CORE_TEST_H__
+#define __PSA_MANIFEST_TFM_MULTI_CORE_TEST_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#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
+}
+#endif
+
+#endif /* __PSA_MANIFEST_TFM_MULTI_CORE_TEST_H__ */
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
new file mode 100644
index 0000000..fe2238e
--- /dev/null
+++ b/test/test_services/tfm_multi_core_test/tfm_multi_core_test.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "psa/client.h"
+#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;
+
+    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
new file mode 100644
index 0000000..0b910bc
--- /dev/null
+++ b/test/test_services/tfm_multi_core_test/tfm_multi_core_test.yaml
@@ -0,0 +1,36 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2019, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+{
+  "psa_framework_version": 1.0,
+  "name": "TFM_SP_MULTI_CORE_TEST",
+  "type": "APPLICATION-ROT",
+  "priority": "NORMAL",
+  "id": "0x00000007",
+  "entry_point": "multi_core_test_main",
+  "stack_size": "0x0100",
+  "secure_functions": [
+  ],
+  "services" : [
+    {
+      "name": "MULTI_CORE_MULTI_CLIENT_CALL_TEST_0",
+      "sid": "0x0000F100",
+      "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"
+    }
+  ]
+}
diff --git a/test/test_services/tfm_ps_test_service/psa_manifest/tfm_ps_test_service.h b/test/test_services/tfm_ps_test_service/psa_manifest/tfm_ps_test_service.h
new file mode 100644
index 0000000..e748ddf
--- /dev/null
+++ b/test/test_services/tfm_ps_test_service/psa_manifest/tfm_ps_test_service.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+/*********** WARNING: This is an auto-generated file. Do not edit! ***********/
+
+#ifndef __PSA_MANIFEST_TFM_PS_TEST_SERVICE_H__
+#define __PSA_MANIFEST_TFM_PS_TEST_SERVICE_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define TFM_PS_TEST_PREPARE_SIGNAL                              (1U << (0 + 4))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __PSA_MANIFEST_TFM_PS_TEST_SERVICE_H__ */
diff --git a/test/test_services/tfm_ps_test_service/tfm_ps_test_service.c b/test/test_services/tfm_ps_test_service/tfm_ps_test_service.c
new file mode 100644
index 0000000..2e30796
--- /dev/null
+++ b/test/test_services/tfm_ps_test_service/tfm_ps_test_service.c
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifdef TFM_PSA_API
+#include "psa/service.h"
+#include "psa_manifest/tfm_ps_test_service.h"
+#else
+#include "psa/client.h"
+#endif
+
+#include "secure_fw/partitions/protected_storage/ps_object_system.h"
+
+psa_status_t tfm_ps_test_prepare(psa_invec *in_vec, size_t in_len,
+                                 psa_outvec *out_vec, size_t out_len)
+{
+    (void)in_vec;
+    (void)in_len;
+    (void)out_vec;
+    (void)out_len;
+
+    return ps_system_prepare();
+}
+
+psa_status_t tfm_ps_test_init(void)
+{
+#ifdef TFM_PSA_API
+    psa_msg_t msg;
+
+    while (1) {
+        (void)psa_wait(TFM_PS_TEST_PREPARE_SIGNAL, PSA_BLOCK);
+        (void)psa_get(TFM_PS_TEST_PREPARE_SIGNAL, &msg);
+        switch (msg.type) {
+        case PSA_IPC_CONNECT:
+        case PSA_IPC_DISCONNECT:
+            psa_reply(msg.handle, PSA_SUCCESS);
+            break;
+        case PSA_IPC_CALL:
+            psa_reply(msg.handle, ps_system_prepare());
+            break;
+        }
+    }
+#else
+    return PSA_SUCCESS;
+#endif
+}
diff --git a/test/test_services/tfm_ps_test_service/tfm_ps_test_service.yaml b/test/test_services/tfm_ps_test_service/tfm_ps_test_service.yaml
new file mode 100644
index 0000000..eddc3ed
--- /dev/null
+++ b/test/test_services/tfm_ps_test_service/tfm_ps_test_service.yaml
@@ -0,0 +1,38 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+{
+  "psa_framework_version": 1.0,
+  "name": "TFM_SP_PS_TEST",
+  "type": "PSA-ROT",
+  "priority": "NORMAL",
+  "entry_point": "tfm_ps_test_init",
+  "stack_size": "0x500",
+  "secure_functions": [
+    {
+      "name": "TFM_PS_TEST_PREPARE",
+      "signal": "TFM_PS_TEST_PREPARE",
+      "non_secure_clients": false,
+      "version": 1,
+      "version_policy": "STRICT"
+    },
+  ],
+  "services": [
+    {
+      "name": "TFM_PS_TEST_PREPARE",
+      "sid": "0x0000F0C0",
+      "non_secure_clients": false,
+      "version": 1,
+      "version_policy": "STRICT"
+    }
+  ],
+  "dependencies": [
+    "TFM_CRYPTO",
+    "TFM_ITS_GET",
+    "TFM_ITS_REMOVE"
+  ]
+}
diff --git a/test/test_services/tfm_ps_test_service/tfm_ps_test_service_api.c b/test/test_services/tfm_ps_test_service/tfm_ps_test_service_api.c
new file mode 100644
index 0000000..1377e93
--- /dev/null
+++ b/test/test_services/tfm_ps_test_service/tfm_ps_test_service_api.c
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "tfm_ps_test_service_api.h"
+
+#ifdef TFM_PSA_API
+#include "psa/client.h"
+#include "psa_manifest/sid.h"
+#else
+#include "tfm_veneers.h"
+#endif
+
+__attribute__((section("SFN")))
+psa_status_t tfm_ps_test_system_prepare(void)
+{
+#ifdef TFM_PSA_API
+    psa_handle_t handle;
+    psa_status_t status;
+
+    handle = psa_connect(TFM_PS_TEST_PREPARE_SID,
+                         TFM_PS_TEST_PREPARE_VERSION);
+    if (!PSA_HANDLE_IS_VALID(handle)) {
+        return PSA_ERROR_GENERIC_ERROR;
+    }
+
+    status = psa_call(handle, PSA_IPC_CALL, NULL, 0, NULL, 0);
+    psa_close(handle);
+
+    return status;
+#else
+    return tfm_tfm_ps_test_prepare_veneer(NULL, 0, NULL, 0);
+#endif
+}
diff --git a/test/test_services/tfm_ps_test_service/tfm_ps_test_service_api.h b/test/test_services/tfm_ps_test_service/tfm_ps_test_service_api.h
new file mode 100644
index 0000000..50aaa69
--- /dev/null
+++ b/test/test_services/tfm_ps_test_service/tfm_ps_test_service_api.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __TFM_PS_TEST_SERVICE_API_H__
+#define __TFM_PS_TEST_SERVICE_API_H__
+
+#include "psa/error.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Requests the PS Test Service to call ps_system_prepare().
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+psa_status_t tfm_ps_test_system_prepare(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __TFM_PS_TEST_SERVICE_API_H__ */
diff --git a/test/test_services/tfm_secure_client_2/psa_manifest/tfm_secure_client_2.h b/test/test_services/tfm_secure_client_2/psa_manifest/tfm_secure_client_2.h
new file mode 100644
index 0000000..a8fdf47
--- /dev/null
+++ b/test/test_services/tfm_secure_client_2/psa_manifest/tfm_secure_client_2.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+/*********** WARNING: This is an auto-generated file. Do not edit! ***********/
+
+#ifndef __PSA_MANIFEST_TFM_SECURE_CLIENT_2_H__
+#define __PSA_MANIFEST_TFM_SECURE_CLIENT_2_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define TFM_SECURE_CLIENT_2_SIGNAL                              (1U << (0 + 4))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __PSA_MANIFEST_TFM_SECURE_CLIENT_2_H__ */
diff --git a/test/test_services/tfm_secure_client_2/tfm_secure_client_2.c b/test/test_services/tfm_secure_client_2/tfm_secure_client_2.c
new file mode 100644
index 0000000..153caac
--- /dev/null
+++ b/test/test_services/tfm_secure_client_2/tfm_secure_client_2.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "tfm_secure_client_2_api.h"
+#include "psa/internal_trusted_storage.h"
+#include "psa/crypto.h"
+
+#ifdef TFM_PSA_API
+#include "psa/service.h"
+#include "psa_manifest/tfm_secure_client_2.h"
+#else
+#include "psa/client.h"
+#endif
+
+#ifdef ENABLE_CRYPTO_SERVICE_TESTS
+/**
+ * \brief Tests calling psa_destroy_key() with the supplied key handle.
+ *
+ * \param[in] arg      Pointer to key handle
+ * \param[in] arg_len  Length of arg in bytes
+ *
+ * \return Returns test result as specified in \ref psa_status_t
+ */
+static psa_status_t secure_client_2_test_crypto_access_ctrl(const void *arg,
+                                                            size_t arg_len)
+{
+    psa_key_handle_t key_handle;
+
+    if (arg_len != sizeof(key_handle)) {
+        return PSA_ERROR_PROGRAMMER_ERROR;
+    }
+
+    key_handle = *((psa_key_handle_t *)arg);
+
+    /* Attempt to destroy the key handle */
+    return psa_destroy_key(key_handle);
+}
+#endif /* ENABLE_CRYPTO_SERVICE_TESTS */
+
+#ifdef ENABLE_INTERNAL_TRUSTED_STORAGE_SERVICE_TESTS
+/**
+ * \brief Tests calling psa_its_get() with the supplied uid.
+ *
+ * \param[in] arg      Pointer to uid
+ * \param[in] arg_len  Length of arg in bytes
+ *
+ * \return Returns test result as specified in \ref psa_status_t
+ */
+static psa_status_t secure_client_2_test_its_access_ctrl(const void *arg,
+                                                         size_t arg_len)
+{
+    psa_storage_uid_t uid;
+    size_t p_data_length;
+    uint8_t data[1];
+
+    if (arg_len != sizeof(uid)) {
+        return PSA_ERROR_PROGRAMMER_ERROR;
+    }
+
+    uid = *((psa_storage_uid_t *)arg);
+
+    /* Attempt to get one byte from the UID and return the resulting status */
+    return psa_its_get(uid, 0, sizeof(data), data, &p_data_length);
+}
+#endif /* ENABLE_INTERNAL_TRUSTED_STORAGE_SERVICE_TESTS */
+
+/**
+ * \brief Calls the test function with the supplied ID and returns the result
+ *        from the test function.
+ *
+ * \param[in] id       The ID of the test function
+ * \param[in] arg      Pointer to argument to pass to test function
+ * \param[in] arg_len  Length of argument in bytes
+ *
+ * \return Returns test result as specified in \ref psa_status_t
+ */
+static psa_status_t secure_client_2_dispatch(int32_t id, const void *arg,
+                                             size_t arg_len)
+{
+    switch (id) {
+#ifdef ENABLE_INTERNAL_TRUSTED_STORAGE_SERVICE_TESTS
+    case TFM_SECURE_CLIENT_2_ID_ITS_ACCESS_CTRL:
+        return secure_client_2_test_its_access_ctrl(arg, arg_len);
+#endif
+#ifdef ENABLE_CRYPTO_SERVICE_TESTS
+    case TFM_SECURE_CLIENT_2_ID_CRYPTO_ACCESS_CTRL:
+        return secure_client_2_test_crypto_access_ctrl(arg, arg_len);
+#endif
+    default:
+        return PSA_ERROR_PROGRAMMER_ERROR;
+    }
+}
+
+#ifdef TFM_PSA_API
+#define SECURE_CLIENT_2_MAX_ARG_LEN 8U
+
+void tfm_secure_client_2_init(void)
+{
+    psa_msg_t msg;
+    size_t len;
+    char arg[SECURE_CLIENT_2_MAX_ARG_LEN] __attribute__((__aligned__(8)));
+
+    while (1) {
+        (void)psa_wait(TFM_SECURE_CLIENT_2_SIGNAL, PSA_BLOCK);
+        if (psa_get(TFM_SECURE_CLIENT_2_SIGNAL, &msg) != PSA_SUCCESS) {
+            continue;
+        }
+        switch (msg.type) {
+        case PSA_IPC_CONNECT:
+        case PSA_IPC_DISCONNECT:
+            psa_reply(msg.handle, PSA_SUCCESS);
+            break;
+        default:
+            len = psa_read(msg.handle, 0, arg, SECURE_CLIENT_2_MAX_ARG_LEN);
+            psa_reply(msg.handle, secure_client_2_dispatch(msg.type, arg, len));
+            break;
+        }
+    }
+}
+#else /* TFM_PSA_API */
+psa_status_t tfm_secure_client_2_init(void)
+{
+    return PSA_SUCCESS;
+}
+
+psa_status_t tfm_secure_client_2_call(psa_invec *in_vec, size_t in_len,
+                                      psa_outvec *out_vec, size_t out_len)
+{
+    int32_t id;
+
+    (void)out_vec;
+
+    if (in_len != 2 || out_len != 0 || in_vec[0].len != sizeof(id)) {
+        return PSA_ERROR_PROGRAMMER_ERROR;
+    }
+
+    id = *((int32_t *)in_vec[0].base);
+
+    return secure_client_2_dispatch(id, in_vec[1].base, in_vec[1].len);
+}
+#endif /* TFM_PSA_API */
diff --git a/test/test_services/tfm_secure_client_2/tfm_secure_client_2.yaml b/test/test_services/tfm_secure_client_2/tfm_secure_client_2.yaml
new file mode 100644
index 0000000..3399933
--- /dev/null
+++ b/test/test_services/tfm_secure_client_2/tfm_secure_client_2.yaml
@@ -0,0 +1,37 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+{
+  "psa_framework_version": 1.0,
+  "name": "TFM_SP_SECURE_CLIENT_2",
+  "type": "APPLICATION-ROT",
+  "priority": "NORMAL",
+  "entry_point": "tfm_secure_client_2_init",
+  "stack_size": "0x300",
+  "secure_functions": [
+    {
+      "name": "TFM_SECURE_CLIENT_2_CALL",
+      "signal": "TFM_SECURE_CLIENT_2_CALL",
+      "non_secure_clients": false,
+      "version": 1,
+      "version_policy": "STRICT"
+    },
+  ],
+  "services": [
+    {
+      "name": "TFM_SECURE_CLIENT_2",
+      "sid": "0x0000F0E0",
+      "non_secure_clients": false,
+      "version": 1,
+      "version_policy": "STRICT"
+    }
+  ],
+  "dependencies": [
+    "TFM_ITS_GET",
+    "TFM_CRYPTO"
+  ]
+}
diff --git a/test/test_services/tfm_secure_client_2/tfm_secure_client_2_api.c b/test/test_services/tfm_secure_client_2/tfm_secure_client_2_api.c
new file mode 100644
index 0000000..b5beb9a
--- /dev/null
+++ b/test/test_services/tfm_secure_client_2/tfm_secure_client_2_api.c
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "tfm_secure_client_2_api.h"
+#include "psa/client.h"
+
+#ifdef TFM_PSA_API
+#include "psa_manifest/sid.h"
+#else
+#include "tfm_veneers.h"
+#endif
+
+__attribute__((section("SFN")))
+psa_status_t tfm_secure_client_2_call_test(int32_t id, const void *arg,
+                                           size_t arg_len)
+{
+#ifdef TFM_PSA_API
+    psa_handle_t handle;
+    psa_status_t status;
+    psa_invec in_vec[] = {
+        { .base = arg, .len = arg_len },
+    };
+
+    handle = psa_connect(TFM_SECURE_CLIENT_2_SID, TFM_SECURE_CLIENT_2_VERSION);
+    if (!PSA_HANDLE_IS_VALID(handle)) {
+        return PSA_ERROR_GENERIC_ERROR;
+    }
+
+    /* Pass the ID through the type parameter of psa_call() */
+    status = psa_call(handle, id, in_vec, 1, NULL, 0);
+    psa_close(handle);
+
+    return status;
+#else
+    /* Pack the ID in the invec */
+    psa_invec in_vec[] = {
+        { .base = &id, .len = sizeof(id) },
+        { .base = arg, .len = arg_len },
+    };
+
+    return tfm_tfm_secure_client_2_call_veneer(in_vec, 2, NULL, 0);
+#endif
+}
diff --git a/test/test_services/tfm_secure_client_2/tfm_secure_client_2_api.h b/test/test_services/tfm_secure_client_2/tfm_secure_client_2_api.h
new file mode 100644
index 0000000..a6018c5
--- /dev/null
+++ b/test/test_services/tfm_secure_client_2/tfm_secure_client_2_api.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __TFM_SECURE_CLIENT_2_API_H__
+#define __TFM_SECURE_CLIENT_2_API_H__
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "psa/error.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define TFM_SECURE_CLIENT_2_ID_ITS_ACCESS_CTRL    1001
+#define TFM_SECURE_CLIENT_2_ID_CRYPTO_ACCESS_CTRL 2001
+
+/**
+ * \brief Calls the test function with the supplied ID within the execution
+ *        context of the Secure Client 2 partition and returns the resulting
+ *        status.
+ *
+ * \param[in] id       The ID of the test function
+ * \param[in] arg      Pointer to argument to pass to test function
+ * \param[in] arg_len  Length of argument in bytes
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+psa_status_t tfm_secure_client_2_call_test(int32_t id, const void *arg,
+                                           size_t arg_len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __TFM_SECURE_CLIENT_2_API_H__ */
diff --git a/test/test_services/tfm_secure_client_service/psa_manifest/tfm_test_client_service.h b/test/test_services/tfm_secure_client_service/psa_manifest/tfm_test_client_service.h
new file mode 100644
index 0000000..2e03069
--- /dev/null
+++ b/test/test_services/tfm_secure_client_service/psa_manifest/tfm_test_client_service.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+/*********** WARNING: This is an auto-generated file. Do not edit! ***********/
+
+#ifndef __PSA_MANIFEST_TFM_TEST_CLIENT_SERVICE_H__
+#define __PSA_MANIFEST_TFM_TEST_CLIENT_SERVICE_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define TFM_SECURE_CLIENT_SFN_RUN_TESTS_SIGNAL                  (1U << (0 + 4))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __PSA_MANIFEST_TFM_TEST_CLIENT_SERVICE_H__ */
diff --git a/test/test_services/tfm_secure_client_service/tfm_secure_client_service.c b/test/test_services/tfm_secure_client_service/tfm_secure_client_service.c
new file mode 100644
index 0000000..62a8b5d
--- /dev/null
+++ b/test/test_services/tfm_secure_client_service/tfm_secure_client_service.c
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "tfm_secure_client_service.h"
+#include "test/framework/test_framework_integ_test.h"
+#ifdef TFM_PSA_API
+#include "psa/client.h"
+#include "psa/service.h"
+#include "psa_manifest/tfm_test_client_service.h"
+#endif
+
+/**
+ * \brief Service initialisation function. No special initialisation is
+ *        required.
+ *
+ * \return Returns 0 on success
+ */
+int32_t tfm_secure_client_service_init(void)
+{
+#ifdef TFM_PSA_API
+    psa_msg_t msg;
+
+    while (1) {
+        psa_wait(TFM_SECURE_CLIENT_SFN_RUN_TESTS_SIGNAL, PSA_BLOCK);
+        psa_get(TFM_SECURE_CLIENT_SFN_RUN_TESTS_SIGNAL, &msg);
+        switch (msg.type) {
+        case PSA_IPC_CONNECT:
+            psa_reply(msg.handle, PSA_SUCCESS);
+            break;
+        case PSA_IPC_CALL:
+            psa_reply(msg.handle, tfm_secure_client_service_sfn_run_tests());
+            break;
+        case PSA_IPC_DISCONNECT:
+            psa_reply(msg.handle, PSA_SUCCESS);
+            break;
+        default:
+            /* cannot get here? [broken SPM]. TODO*/
+            break;
+        }
+    }
+#else
+    return 0;
+#endif
+}
+
+int32_t tfm_secure_client_service_sfn_run_tests(void)
+{
+    start_integ_test();
+    return 0;
+}
diff --git a/test/test_services/tfm_secure_client_service/tfm_secure_client_service.h b/test/test_services/tfm_secure_client_service/tfm_secure_client_service.h
new file mode 100644
index 0000000..baa650a
--- /dev/null
+++ b/test/test_services/tfm_secure_client_service/tfm_secure_client_service.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2018-2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __TFM_SECURE_CLIENT_SERVICE_H__
+#define __TFM_SECURE_CLIENT_SERVICE_H__
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Runs the secure integration tests.
+ *
+ * \return Returns 0 on success.
+ */
+int32_t tfm_secure_client_service_sfn_run_tests(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __TFM_SECURE_CLIENT_SERVICE_H__ */
diff --git a/test/test_services/tfm_secure_client_service/tfm_secure_client_service_api.c b/test/test_services/tfm_secure_client_service/tfm_secure_client_service_api.c
new file mode 100644
index 0000000..3a80446
--- /dev/null
+++ b/test/test_services/tfm_secure_client_service/tfm_secure_client_service_api.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2018-2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "tfm_secure_client_service_api.h"
+#ifdef TFM_PSA_API
+#include "psa/client.h"
+#include "tfm_api.h"
+#include "psa_manifest/sid.h"
+#else /* TFM_PSA_API */
+#include "tfm_veneers.h"
+#endif /* TFM_PSA_API */
+
+int32_t tfm_secure_client_run_tests(void)
+{
+#ifdef TFM_PSA_API
+    psa_handle_t handle;
+    psa_status_t status;
+
+    handle = psa_connect(TFM_SECURE_CLIENT_SFN_RUN_TESTS_SID,
+                         TFM_SECURE_CLIENT_SFN_RUN_TESTS_VERSION);
+    if (handle <= 0) {
+        return TFM_ERROR_GENERIC;
+    }
+
+    status = psa_call(handle, PSA_IPC_CALL, NULL, 0, NULL, 0);
+    psa_close(handle);
+
+    if (status != PSA_SUCCESS) {
+        return TFM_ERROR_GENERIC;
+    }
+#else
+    tfm_tfm_secure_client_service_sfn_run_tests_veneer(NULL, 0, NULL, 0);
+#endif
+
+    return 0;
+}
diff --git a/test/test_services/tfm_secure_client_service/tfm_secure_client_service_api.h b/test/test_services/tfm_secure_client_service/tfm_secure_client_service_api.h
new file mode 100644
index 0000000..35e9004
--- /dev/null
+++ b/test/test_services/tfm_secure_client_service/tfm_secure_client_service_api.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __TFM_SECURE_CLIENT_SERVICE_API_H__
+#define __TFM_SECURE_CLIENT_SERVICE_API_H__
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Runs the secure integration tests.
+ *
+ * \return Returns 0 on success
+ */
+int32_t tfm_secure_client_run_tests(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __TFM_SECURE_CLIENT_SERVICE_API_H__ */
diff --git a/test/test_services/tfm_secure_client_service/tfm_test_client_service.yaml b/test/test_services/tfm_secure_client_service/tfm_test_client_service.yaml
new file mode 100644
index 0000000..7931f16
--- /dev/null
+++ b/test/test_services/tfm_secure_client_service/tfm_test_client_service.yaml
@@ -0,0 +1,58 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+{
+  "psa_framework_version": 1.0,
+  "name": "TFM_SP_SECURE_TEST_PARTITION",
+  "type": "PSA-ROT",
+  "priority": "NORMAL",
+  "entry_point": "tfm_secure_client_service_init",
+  "stack_size": "0x0D00",
+  "mmio_regions": [
+    {
+      "name": "TFM_PERIPHERAL_STD_UART",
+      "permission": "READ-WRITE"
+    }
+  ],
+  "secure_functions": [
+    {
+      "name": "TFM_SECURE_CLIENT_SFN_RUN_TESTS",
+      "signal": "TFM_SECURE_CLIENT_SERVICE_SFN_RUN_TESTS",
+      "non_secure_clients": true,
+      "version": 1,
+      "version_policy": "STRICT"
+    },
+  ],
+  "services": [
+    {
+      "name": "TFM_SECURE_CLIENT_SFN_RUN_TESTS",
+      "sid": "0x0000F000",
+      "non_secure_clients": true,
+      "version": 1,
+      "version_policy": "STRICT"
+    }
+  ],
+  "dependencies": [
+    "TFM_SECURE_CLIENT_2",
+    "TFM_CRYPTO",
+    "TFM_PS_SET",
+    "TFM_PS_GET",
+    "TFM_PS_GET_INFO",
+    "TFM_PS_REMOVE",
+    "TFM_PS_GET_SUPPORT",
+    "TFM_ITS_SET",
+    "TFM_ITS_GET",
+    "TFM_ITS_GET_INFO",
+    "TFM_ITS_REMOVE",
+    "TFM_ATTEST_GET_TOKEN",
+    "TFM_ATTEST_GET_TOKEN_SIZE",
+    "TFM_ATTEST_GET_PUBLIC_KEY",
+    "TFM_PS_TEST_PREPARE",
+    "TFM_SP_PLATFORM_SYSTEM_RESET",
+    "TFM_SP_PLATFORM_IOCTL"
+  ]
+}