Test: Add FP test cases

1. Enable FP support for test framework.
2. Adding FP service partition for FPU test.
3. Adding test cases in TFM_SP_SECURE_TEST_PARTITION partition to
   verify FP context protection.
4. Separate qcbor, t_cose for SPE and NSPE.

Note:
   FPU is used by SPE only, NSPE is not allowed to use FPU at current
   stage.

Change-Id: I703cac5b3fc8e8c0840babb7af16fa346b5d2c95
Signed-off-by: Feder Liang <Feder.Liang@arm.com>
diff --git a/test/config/check_config.cmake b/test/config/check_config.cmake
index 41f2c0b..171f6d2 100644
--- a/test/config/check_config.cmake
+++ b/test/config/check_config.cmake
@@ -34,6 +34,7 @@
 message(STATUS "TEST_S_PLATFORM is set as ${TEST_S_PLATFORM}")
 message(STATUS "TEST_S_FWU is set as ${TEST_S_FWU}")
 message(STATUS "TEST_S_IPC is set as ${TEST_S_IPC}")
+message(STATUS "TEST_S_FPU is set as ${TEST_S_FPU}")
 
 message(STATUS "---------- Display TEST Configuration - stop ---------------")
 
@@ -47,6 +48,8 @@
 tfm_invalid_config(NOT TFM_PARTITION_FIRMWARE_UPDATE AND (TEST_NS_FWU OR TEST_S_FWU))
 tfm_invalid_config(NOT TFM_PARTITION_AUDIT_LOG AND (TEST_NS_AUDIT OR TEST_S_AUDIT))
 tfm_invalid_config((TFM_LIB_MODEL) AND (TEST_NS_IPC OR TEST_S_IPC OR TEST_NS_SLIH_IRQ OR TEST_NS_FLIH_IRQ))
+tfm_invalid_config(CONFIG_TFM_SPE_FP STREQUAL "0" AND TEST_S_FPU)
+tfm_invalid_config(TFM_LIB_MODEL AND TEST_S_FPU)
 tfm_invalid_config((NOT TFM_MULTI_CORE_TOPOLOGY) AND TEST_NS_MULTI_CORE)
 tfm_invalid_config(TEST_NS_T_COSE AND SYMMETRIC_INITIAL_ATTESTATION)
 tfm_invalid_config((NOT TFM_NS_MANAGE_NSID) AND TEST_NS_MANAGE_NSID)
diff --git a/test/config/default_s_test_config.cmake b/test/config/default_s_test_config.cmake
index 4a38da7..b2a8575 100644
--- a/test/config/default_s_test_config.cmake
+++ b/test/config/default_s_test_config.cmake
@@ -19,3 +19,4 @@
 set(TEST_S_PLATFORM         ON        CACHE BOOL      "Whether to build S regression Platform tests")
 set(TEST_S_FWU              OFF       CACHE BOOL      "Whether to build S regression FWU tests")
 set(TEST_S_IPC              ON        CACHE BOOL      "Whether to build S regression IPC tests")
+set(TEST_S_FPU              OFF       CACHE BOOL      "Whether to build S regression FPU tests")
diff --git a/test/config/default_test_config.cmake b/test/config/default_test_config.cmake
index 7f3deee..1bfacae 100644
--- a/test/config/default_test_config.cmake
+++ b/test/config/default_test_config.cmake
@@ -70,6 +70,10 @@
 
 set(ATTEST_TEST_GET_PUBLIC_KEY          OFF         CACHE BOOL      "Require to retrieve Initial Attestation public in runtime for test purpose")
 
+############################################ FPU Tests #########################
+
+set(TEST_S_FPU                          OFF         CACHE BOOL      "Whether to build S regression FPU tests")
+
 ################################## Extra test suites ###########################
 
 set(EXTRA_NS_TEST_SUITES_PATHS          ""          CACHE PATH      "List of extra non-secure test suites directories. An extra test suite folder contains source code, CMakeLists.txt and cmake configuration file")
diff --git a/test/config/set_config.cmake b/test/config/set_config.cmake
index 75f6477..57f2117 100644
--- a/test/config/set_config.cmake
+++ b/test/config/set_config.cmake
@@ -67,6 +67,10 @@
     set(TEST_NS_MANAGE_NSID     OFF        CACHE BOOL      "Whether to build NS regression NSID management tests")
 endif()
 
+if (CONFIG_TFM_SPE_FP STREQUAL "0")
+    set(TEST_S_FPU              OFF        CACHE BOOL      "Whether to build S regression FPU tests")
+endif()
+
 ########################## Test framework sync #################################
 
 get_cmake_property(CACHE_VARS CACHE_VARIABLES)
diff --git a/test/framework/secure_suites.c b/test/framework/secure_suites.c
index bbe3676..674144e 100644
--- a/test/framework/secure_suites.c
+++ b/test/framework/secure_suites.c
@@ -35,6 +35,9 @@
 #include "audit_s_tests.h"
 #endif
 #endif /* TFM_PSA_API */
+#if defined (TEST_S_FPU)
+#include "fpu_s_tests.h"
+#endif
 #ifdef EXTRA_S_TEST_SUITE
 #include "extra_s_tests.h"
 #endif
@@ -85,6 +88,11 @@
     {&register_testsuite_s_ipc_interface, 0, 0, 0},
 #endif
 
+#ifdef TEST_S_FPU
+    /* Secure FPU test cases */
+    {&register_testsuite_s_fpu_interface, 0, 0, 0},
+#endif
+
 #ifdef EXTRA_S_TEST_SUITE
     /* Secure extra test cases */
     {&register_testsuite_extra_s_interface, 0, 0, 0},
diff --git a/test/secure_tests.cmake b/test/secure_tests.cmake
index e1111ab..9212b7f 100644
--- a/test/secure_tests.cmake
+++ b/test/secure_tests.cmake
@@ -74,6 +74,9 @@
 if (TEST_S_FWU)
     add_library(tfm_test_suite_fwu_s STATIC EXCLUDE_FROM_ALL)
 endif()
+if (TEST_S_FPU)
+    add_library(tfm_test_suite_fpu_s STATIC EXCLUDE_FROM_ALL)
+endif()
 if (EXTRA_S_TEST_SUITES_PATHS)
     add_library(tfm_test_suite_extra_s STATIC EXCLUDE_FROM_ALL)
 endif()
diff --git a/test/suites/CMakeLists.txt b/test/suites/CMakeLists.txt
index deafdd8..691f1fd 100644
--- a/test/suites/CMakeLists.txt
+++ b/test/suites/CMakeLists.txt
@@ -84,6 +84,7 @@
 add_subdirectory(irq)
 add_subdirectory(multi_core/non_secure)
 add_subdirectory(nsid)
+add_subdirectory(fpu)
 
 if (TFM_FUZZER_TOOL_TESTS AND TEST_FRAMEWORK_NS)
     if (NOT DEFINED TFM_FUZZER_TOOL_TESTS_CMAKE_INC_PATH)
diff --git a/test/suites/attestation/CMakeLists.txt b/test/suites/attestation/CMakeLists.txt
index 08ae89b..d24bd9c 100644
--- a/test/suites/attestation/CMakeLists.txt
+++ b/test/suites/attestation/CMakeLists.txt
@@ -48,7 +48,7 @@
 
     target_link_libraries(tfm_test_suite_attestation_ns
         PRIVATE
-            tfm_qcbor
+            tfm_qcbor_ns
             tfm_t_cose_ns
             tfm_test_framework_ns
             #TODO I'm not sure this should _really_ be linked here
@@ -96,9 +96,14 @@
             TEST_S_ATTESTATION
     )
 
+    target_compile_options(tfm_test_suite_attestation_s
+        PUBLIC
+            ${COMPILER_CP_FLAG}
+    )
+
     target_link_libraries(tfm_test_suite_attestation_s
         PRIVATE
-            tfm_qcbor
+            tfm_qcbor_s
             tfm_t_cose_s
             tfm_test_framework_s
             tfm_attestation_defs
diff --git a/test/suites/fpu/CMakeLists.txt b/test/suites/fpu/CMakeLists.txt
new file mode 100644
index 0000000..f5ce54b
--- /dev/null
+++ b/test/suites/fpu/CMakeLists.txt
@@ -0,0 +1,42 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2021, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+cmake_policy(SET CMP0079 NEW)
+
+if (NOT TEST_S_FPU)
+    return()
+endif()
+
+####################### Secure #################################################
+
+target_sources(tfm_test_suite_fpu_s
+    PRIVATE
+        ${CMAKE_CURRENT_SOURCE_DIR}/fpu_tests_common.c
+        ${CMAKE_CURRENT_SOURCE_DIR}/secure/fpu_s_interface_testsuite.c
+)
+
+target_include_directories(tfm_test_suite_fpu_s
+    PUBLIC
+        ${CMAKE_CURRENT_SOURCE_DIR}/secure
+)
+
+target_compile_definitions(tfm_test_suite_fpu_s
+    INTERFACE
+        TEST_S_FPU
+)
+
+target_link_libraries(tfm_test_suite_fpu_s
+    PRIVATE
+        tfm_test_framework_s
+    PUBLIC
+        platform_s
+)
+
+target_link_libraries(tfm_s_tests
+    INTERFACE
+        tfm_test_suite_fpu_s
+)
diff --git a/test/suites/fpu/fpu_tests_common.c b/test/suites/fpu/fpu_tests_common.c
new file mode 100644
index 0000000..49d85eb
--- /dev/null
+++ b/test/suites/fpu/fpu_tests_common.c
@@ -0,0 +1,355 @@
+/*
+ * Copyright (c) 2021, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "fpu_tests_common.h"
+#include "psa_manifest/sid.h"
+#include "tfm_arch.h"
+
+/**
+ * Change FP registers.
+ */
+__attribute__((naked)) static void change_fp_in_client(void)
+{
+    __asm volatile(
+        "mov       r0, #0x000000C0         \n"
+        "vmov      s0, r0                  \n"
+        "mov       r0, #0x000000C1         \n"
+        "vmov      s1, r0                  \n"
+        "mov       r0, #0x000000C2         \n"
+        "vmov      s2, r0                  \n"
+        "mov       r0, #0x000000C3         \n"
+        "vmov      s3, r0                  \n"
+        "mov       r0, #0x000000C4         \n"
+        "vmov      s4, r0                  \n"
+        "mov       r0, #0x000000C5         \n"
+        "vmov      s5, r0                  \n"
+        "mov       r0, #0x000000C6         \n"
+        "vmov      s6, r0                  \n"
+        "mov       r0, #0x000000C7         \n"
+        "vmov      s7, r0                  \n"
+        "mov       r0, #0x000000C8         \n"
+        "vmov      s8, r0                  \n"
+        "mov       r0, #0x000000C9         \n"
+        "vmov      s9, r0                  \n"
+        "mov       r0, #0x000000CA         \n"
+        "vmov      s10, r0                 \n"
+        "mov       r0, #0x000000CB         \n"
+        "vmov      s11, r0                 \n"
+        "mov       r0, #0x000000CC         \n"
+        "vmov      s12, r0                 \n"
+        "mov       r0, #0x000000CD         \n"
+        "vmov      s13, r0                 \n"
+        "mov       r0, #0x000000CE         \n"
+        "vmov      s14, r0                 \n"
+        "mov       r0, #0x000000CF         \n"
+        "vmov      s15, r0                 \n"
+        "mov       r0, #0x000000D0         \n"
+        "vmov      s16, r0                 \n"
+        "mov       r0, #0x000000D1         \n"
+        "vmov      s17, r0                 \n"
+        "mov       r0, #0x000000D2         \n"
+        "vmov      s18, r0                 \n"
+        "mov       r0, #0x000000D3         \n"
+        "vmov      s19, r0                 \n"
+        "mov       r0, #0x000000D4         \n"
+        "vmov      s20, r0                 \n"
+        "mov       r0, #0x000000D5         \n"
+        "vmov      s21, r0                 \n"
+        "mov       r0, #0x000000D6         \n"
+        "vmov      s22, r0                 \n"
+        "mov       r0, #0x000000D7         \n"
+        "vmov      s23, r0                 \n"
+        "mov       r0, #0x000000D8         \n"
+        "vmov      s24, r0                 \n"
+        "mov       r0, #0x000000D9         \n"
+        "vmov      s25, r0                 \n"
+        "mov       r0, #0x000000DA         \n"
+        "vmov      s26, r0                 \n"
+        "mov       r0, #0x000000DB         \n"
+        "vmov      s27, r0                 \n"
+        "mov       r0, #0x000000DC         \n"
+        "vmov      s28, r0                 \n"
+        "mov       r0, #0x000000DD         \n"
+        "vmov      s29, r0                 \n"
+        "mov       r0, #0x000000DE         \n"
+        "vmov      s30, r0                 \n"
+        "mov       r0, #0x000000DF         \n"
+        "vmov      s31, r0                 \n"
+
+        "bx        lr                      \n"
+    );
+}
+
+/**
+ * Check whether FP registers are restored correctly.
+ * Return:
+ *   1 - FP registers are restored correctly
+ *   0 - FP registers are not restored correctly
+ */
+__attribute__((naked)) static bool check_fp_restored_client(void)
+{
+    __asm volatile(
+        "mov       r3, #0                  \n"
+
+        "vmov      r2, s0                  \n"
+        "cmp       r2, 0x000000C0          \n"
+        "bne       exit                    \n"
+
+        "vmov      r2, s1                  \n"
+        "cmp       r2, 0x000000C1          \n"
+        "bne       exit                    \n"
+
+        "vmov      r2, s2                  \n"
+        "cmp       r2, 0x000000C2          \n"
+        "bne       exit                    \n"
+
+        "vmov      r2, s3                  \n"
+        "cmp       r2, 0x000000C3          \n"
+        "bne       exit                    \n"
+
+        "vmov      r2, s4                  \n"
+        "cmp       r2, 0x000000C4          \n"
+        "bne       exit                    \n"
+
+        "vmov      r2, s5                  \n"
+        "cmp       r2, 0x000000C5          \n"
+        "bne       exit                    \n"
+
+        "vmov      r2, s6                  \n"
+        "cmp       r2, 0x000000C6          \n"
+        "bne       exit                    \n"
+
+        "vmov      r2, s7                  \n"
+        "cmp       r2, 0x000000C7          \n"
+        "bne       exit                    \n"
+
+        "vmov      r2, s8                  \n"
+        "cmp       r2, 0x000000C8          \n"
+        "bne       exit                    \n"
+
+        "vmov      r2, s9                  \n"
+        "cmp       r2, 0x000000C9          \n"
+        "bne       exit                    \n"
+
+        "vmov      r2, s10                 \n"
+        "cmp       r2, 0x000000CA          \n"
+        "bne       exit                    \n"
+
+        "vmov      r2, s11                 \n"
+        "cmp       r2, 0x000000CB          \n"
+        "bne       exit                    \n"
+
+        "vmov      r2, s12                 \n"
+        "cmp       r2, 0x000000CC          \n"
+        "bne       exit                    \n"
+
+        "vmov      r2, s13                 \n"
+        "cmp       r2, 0x000000CD          \n"
+        "bne       exit                    \n"
+
+        "vmov      r2, s14                 \n"
+        "cmp       r2, 0x000000CE          \n"
+        "bne       exit                    \n"
+
+        "vmov      r2, s15                 \n"
+        "cmp       r2, 0x000000CF          \n"
+        "bne       exit                    \n"
+
+        "vmov      r2, s16                 \n"
+        "cmp       r2, 0x000000D0          \n"
+        "bne       exit                    \n"
+
+        "vmov      r2, s17                 \n"
+        "cmp       r2, 0x000000D1          \n"
+        "bne       exit                    \n"
+
+        "vmov      r2, s18                 \n"
+        "cmp       r2, 0x000000D2          \n"
+        "bne       exit                    \n"
+
+        "vmov      r2, s19                 \n"
+        "cmp       r2, 0x000000D3          \n"
+        "bne       exit                    \n"
+
+        "vmov      r2, s20                 \n"
+        "cmp       r2, 0x000000D4          \n"
+        "bne       exit                    \n"
+
+        "vmov      r2, s21                 \n"
+        "cmp       r2, 0x000000D5          \n"
+        "bne       exit                    \n"
+
+        "vmov      r2, s22                 \n"
+        "cmp       r2, 0x000000D6          \n"
+        "bne       exit                    \n"
+
+        "vmov      r2, s23                 \n"
+        "cmp       r2, 0x000000D7          \n"
+        "bne       exit                    \n"
+
+        "vmov      r2, s24                 \n"
+        "cmp       r2, 0x000000D8          \n"
+        "bne       exit                    \n"
+
+        "vmov      r2, s25                 \n"
+        "cmp       r2, 0x000000D9          \n"
+        "bne       exit                    \n"
+
+        "vmov      r2, s26                 \n"
+        "cmp       r2, 0x000000DA          \n"
+        "bne       exit                    \n"
+
+        "vmov      r2, s27                 \n"
+        "cmp       r2, 0x000000DB          \n"
+        "bne       exit                    \n"
+
+        "vmov      r2, s28                 \n"
+        "cmp       r2, 0x000000DC          \n"
+        "bne       exit                    \n"
+
+        "vmov      r2, s29                 \n"
+        "cmp       r2, 0x000000DD          \n"
+        "bne       exit                    \n"
+
+        "vmov      r2, s30                 \n"
+        "cmp       r2, 0x000000DE          \n"
+        "bne       exit                    \n"
+
+        "vmov      r2, s31                 \n"
+        "cmp       r2, 0x000000DF          \n"
+        "bne       exit                    \n"
+
+        "mov       r3, #1                  \n"
+        "exit:                             \n"
+        "mov       r0, r3                  \n"
+
+        "bx        lr                      \n"
+    );
+}
+
+/**
+ * Description: Test FP context protection after psa calls. Change FP registers
+ * in FPU client/service partition separately, then check FP registers after
+ * psa calls.
+ * Expectation: FP registers in FPU client/service partition should be saved
+ * and restored correctly.
+ */
+void tfm_fpu_test_fp_protection_psa_call(struct test_result_t *ret)
+{
+    psa_handle_t handle;
+    psa_status_t status;
+
+    handle = psa_connect(TFM_FPU_SERVICE_CHECK_FP_REGISTER_SID,
+                            TFM_FPU_SERVICE_CHECK_FP_REGISTER_VERSION);
+    if (!PSA_HANDLE_IS_VALID(handle)) {
+        ret->val = TEST_FAILED;
+        return;
+    }
+
+    /* Change FP registers in secure thread */
+    change_fp_in_client();
+
+    status = psa_call(handle, PSA_IPC_CALL, NULL, 0, NULL, 0);
+    if (status == PSA_SUCCESS) {
+        /* FP registers should be restored */
+        if (check_fp_restored_client()) {
+            ret->val = TEST_PASSED;
+        } else {
+            ret->val = TEST_FAILED;
+        }
+    }
+
+    psa_close(handle);
+}
+
+/**
+ * Description: Clear FP registers in FPU service partition for next test.
+ * Expectation: FP registers in FPU service partition should be cleared.
+ */
+void tfm_fpu_test_clear_service_fp_data(struct test_result_t *ret)
+{
+    psa_handle_t handle;
+    psa_status_t status;
+
+    handle = psa_connect(TFM_FPU_SERVICE_CLEAR_FP_REGISTER_SID,
+                            TFM_FPU_SERVICE_CLEAR_FP_REGISTER_VERSION);
+    if (!PSA_HANDLE_IS_VALID(handle)) {
+        ret->val = TEST_FAILED;
+        return;
+    }
+
+    status = psa_call(handle, PSA_IPC_CALL, NULL, 0, NULL, 0);
+    if (status == PSA_SUCCESS) {
+        ret->val = TEST_PASSED;
+    } else {
+        ret->val = TEST_FAILED;
+    }
+
+    psa_close(handle);
+}
+
+/**
+ * Description: Test reliability of FP context protection after psa calls by
+ * loops. Change FP registers in FPU client/service partition separately, then
+ * check FP registers after psa calls.
+ * Expectation: FP registers in FPU client/service partition should be saved
+ * and restored correctly.
+ */
+void tfm_fpu_test_fp_protection_psa_call_loop(struct test_result_t *ret)
+{
+    psa_handle_t handle;
+    psa_status_t status;
+    uint32_t itr;
+
+    for (itr = 0; itr < LOOP_ITERATIONS; itr++) {
+        TEST_LOG("  > Iteration %d of %d\r", itr + 1, LOOP_ITERATIONS);
+
+        handle = psa_connect(TFM_FPU_SERVICE_CHECK_FP_REGISTER_SID,
+                                TFM_FPU_SERVICE_CHECK_FP_REGISTER_VERSION);
+        if (handle <= 0) {
+            TEST_FAIL("The RoT Service has refused the connection!\r\n");
+            ret->val = TEST_FAILED;
+            return;
+        }
+
+        /* Change FP registers in secure thread */
+        change_fp_in_client();
+
+        status = psa_call(handle, PSA_IPC_CALL, NULL, 0, NULL, 0);
+        if (status == PSA_SUCCESS) {
+            /* FP registers should be restored */
+            if (check_fp_restored_client()) {
+                ret->val = TEST_PASSED;
+            } else {
+                ret->val = TEST_FAILED;
+            }
+        } else {
+            ret->val = TEST_FAILED;
+        }
+
+        psa_close(handle);
+
+        handle = psa_connect(TFM_FPU_SERVICE_CLEAR_FP_REGISTER_SID,
+                                TFM_FPU_SERVICE_CLEAR_FP_REGISTER_VERSION);
+        if (handle <= 0) {
+            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_SUCCESS) {
+            ret->val = TEST_PASSED;
+        } else {
+            ret->val = TEST_FAILED;
+        }
+
+        psa_close(handle);
+    }
+
+    ret->val = TEST_PASSED;
+}
diff --git a/test/suites/fpu/fpu_tests_common.h b/test/suites/fpu/fpu_tests_common.h
new file mode 100644
index 0000000..bcbe19d
--- /dev/null
+++ b/test/suites/fpu/fpu_tests_common.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2021, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+
+#ifndef __FPU_TESTS_COMMON_H__
+#define __FPU_TESTS_COMMON_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "psa/client.h"
+#include "test_framework_helpers.h"
+#ifdef TFM_PSA_API
+#include "psa_manifest/sid.h"
+#endif
+
+
+#define LOOP_ITERATIONS                         (300U)
+
+/**
+ * Test FP context protection after psa calls.
+ */
+void tfm_fpu_test_fp_protection_psa_call(struct test_result_t *ret);
+
+/**
+ * Clear FP registers in FPU service partition.
+ */
+void tfm_fpu_test_clear_service_fp_data(struct test_result_t *ret);
+
+/**
+ * Test reliability of FP context protection after psa calls by loops.
+ */
+void tfm_fpu_test_fp_protection_psa_call_loop(struct test_result_t *ret);
+
+/**
+ * Clear FP registers in FPU client partition.
+ */
+void tfm_fpu_test_clear_client_fp_data(struct test_result_t *ret);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __FPU_TESTS_COMMON__ */
diff --git a/test/suites/fpu/secure/fpu_s_interface_testsuite.c b/test/suites/fpu/secure/fpu_s_interface_testsuite.c
new file mode 100755
index 0000000..f648283
--- /dev/null
+++ b/test/suites/fpu/secure/fpu_s_interface_testsuite.c
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2021, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "fpu_s_tests.h"
+#include "../fpu_tests_common.h"
+
+static struct test_t fpu_s_tests[] = {
+    {
+        &tfm_fpu_test_clear_client_fp_data, "TFM_S_FPU_TEST_1001",
+        "Clear FP registers in FPU client partition",
+        {TEST_PASSED}
+    },
+    {
+        &tfm_fpu_test_fp_protection_psa_call, "TFM_S_FPU_TEST_1002",
+        "Test FP context protection after psa calls",
+        {TEST_PASSED}
+    },
+    {
+        &tfm_fpu_test_clear_service_fp_data, "TFM_S_FPU_TEST_1003",
+        "Clear FP registers in FPU service partition for next test",
+        {TEST_PASSED}
+    },
+    {
+        &tfm_fpu_test_fp_protection_psa_call_loop, "TFM_S_FPU_TEST_1004",
+        "Test reliability of FP context protection after psa calls",
+        {TEST_PASSED}
+    }
+};
+
+/**
+ * Check invalidation of FP registers.
+ */
+__attribute__((naked)) static uint32_t check_fp_invalidated(void);
+
+void register_testsuite_s_fpu_interface(struct test_suite_t *p_test_suite)
+{
+    uint32_t list_size;
+
+    list_size = (sizeof(fpu_s_tests) / sizeof(fpu_s_tests[0]));
+
+    set_testsuite("FPU secure interface test (TFM_S_FPU_TEST_1XXX)",
+                  fpu_s_tests, list_size, p_test_suite);
+}
+
+/**
+ * Clear FP registers.
+ */
+__attribute__((naked)) static int fpu_client_fp_clear_test(void)
+{
+    __asm volatile(
+        "eor       r0, r0, r0              \n"
+        "vmov      s1, r0                  \n"
+        "vmov      s2, r0                  \n"
+        "vmov      s3, r0                  \n"
+        "vmov      s4, r0                  \n"
+        "vmov      s5, r0                  \n"
+        "vmov      s6, r0                  \n"
+        "vmov      s7, r0                  \n"
+        "vmov      s8, r0                  \n"
+        "vmov      s9, r0                  \n"
+        "vmov      s10, r0                 \n"
+        "vmov      s11, r0                 \n"
+        "vmov      s12, r0                 \n"
+        "vmov      s13, r0                 \n"
+        "vmov      s14, r0                 \n"
+        "vmov      s15, r0                 \n"
+        "vmov      s16, r0                 \n"
+        "vmov      s17, r0                 \n"
+        "vmov      s18, r0                 \n"
+        "vmov      s19, r0                 \n"
+        "vmov      s20, r0                 \n"
+        "vmov      s21, r0                 \n"
+        "vmov      s22, r0                 \n"
+        "vmov      s23, r0                 \n"
+        "vmov      s24, r0                 \n"
+        "vmov      s25, r0                 \n"
+        "vmov      s26, r0                 \n"
+        "vmov      s27, r0                 \n"
+        "vmov      s28, r0                 \n"
+        "vmov      s29, r0                 \n"
+        "vmov      s30, r0                 \n"
+        "vmov      s31, r0                 \n"
+
+        "bx        lr                      \n"
+    );
+}
+
+/**
+ * Description: Clear FP registers in FPU client partition for next test.
+ * Expectation: FP registers in FPU client partition should be cleared.
+ */
+void tfm_fpu_test_clear_client_fp_data(struct test_result_t *ret)
+{
+    fpu_client_fp_clear_test();
+
+    if (check_fp_invalidated()) {
+        ret->val = TEST_PASSED;
+    } else {
+        ret->val = TEST_FAILED;
+    }
+}
+
+/**
+ * Check invalidation of FP registers.
+ * Return:
+ *   1 - FP registers are invalidated
+ *   0 - FP registers are not invalidated
+ */
+__attribute__((naked)) static uint32_t check_fp_invalidated(void)
+{
+    __asm volatile(
+        "mov       r3, #1                  \n"
+        "mov       r2, #0                  \n"
+
+        "vadd.f32  s2, s1, s0              \n"
+        "vadd.f32  s4, s3, s2              \n"
+        "vadd.f32  s6, s5, s4              \n"
+        "vadd.f32  s8, s7, s6              \n"
+        "vadd.f32  s10, s9, s8            \n"
+        "vadd.f32  s12, s11, s10           \n"
+        "vadd.f32  s14, s13, s12           \n"
+        "vadd.f32  s16, s15, s14           \n"
+        "vadd.f32  s18, s17, s16           \n"
+        "vadd.f32  s20, s19, s18           \n"
+        "vadd.f32  s22, s21, s20           \n"
+        "vadd.f32  s24, s23, s22           \n"
+        "vadd.f32  s26, s25, s24           \n"
+        "vadd.f32  s28, s27, s26           \n"
+        "vadd.f32  s30, s29, s28           \n"
+        "vadd.f32  s31, s29, s30           \n"
+        "vcmp.f32  s31, #0.0               \n"
+        "vmrs      APSR_nzcv, fpscr        \n"
+        "beq       cleared                 \n"
+        "mov       r3, r2                  \n"
+        "cleared:                          \n"
+
+        "mov       r0, r3                  \n"
+        "bx        lr                      \n"
+    );
+}
diff --git a/test/suites/fpu/secure/fpu_s_tests.h b/test/suites/fpu/secure/fpu_s_tests.h
new file mode 100644
index 0000000..bde86f4
--- /dev/null
+++ b/test/suites/fpu/secure/fpu_s_tests.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2021, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __FPU_S_TESTS_H__
+#define __FPU_S_TESTS_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "test_framework.h"
+
+/**
+ * \brief Register testsuite for fpu secure interface.
+ *
+ * \param[in] p_test_suite The test suite to be executed.
+ */
+void register_testsuite_s_fpu_interface(struct test_suite_t *p_test_suite);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __FPU_S_TESTS_H__ */
diff --git a/test/suites/qcbor/CMakeLists.txt b/test/suites/qcbor/CMakeLists.txt
index b7b95fd..b1ac7eb 100644
--- a/test/suites/qcbor/CMakeLists.txt
+++ b/test/suites/qcbor/CMakeLists.txt
@@ -6,11 +6,35 @@
 #-------------------------------------------------------------------------------
 cmake_policy(SET CMP0079 NEW)
 
-if (NOT TEST_NS_QCBOR)
+if (NOT TEST_NS_ATTESTATION AND NOT TEST_NS_QCBOR)
     return()
 endif()
 
-####################### Non Secure #############################################
+######################### qcbor ns ############################
+
+set(QCBOR_SRC_DIR ${CMAKE_SOURCE_DIR}/lib/ext/qcbor)
+
+add_library(tfm_qcbor_ns STATIC EXCLUDE_FROM_ALL)
+
+target_sources(tfm_qcbor_ns
+    PRIVATE
+        ${QCBOR_SRC_DIR}/src/ieee754.c
+        ${QCBOR_SRC_DIR}/src/qcbor_encode.c
+        ${QCBOR_SRC_DIR}/src/qcbor_decode.c
+        ${QCBOR_SRC_DIR}/src/UsefulBuf.c
+)
+
+target_include_directories(tfm_qcbor_ns
+    PUBLIC
+        $<BUILD_INTERFACE:${QCBOR_SRC_DIR}/inc>
+)
+
+target_link_libraries(tfm_qcbor_ns
+    PRIVATE
+        tfm_t_cose_defs
+)
+
+####################### Non Secure Test #############################################
 
 add_library(tfm_test_suite_qcbor_ns STATIC EXCLUDE_FROM_ALL)
 
@@ -22,6 +46,7 @@
 target_include_directories(tfm_test_suite_qcbor_ns
     PUBLIC
         non_secure
+        ${QCBOR_SRC_DIR}/test/
 )
 
 target_compile_definitions(tfm_test_suite_qcbor_ns
@@ -41,3 +66,33 @@
     INTERFACE
         tfm_test_suite_qcbor_ns
 )
+
+if (NOT TEST_NS_QCBOR)
+    return()
+endif()
+
+############################ qcbor test ########################################
+
+add_library(tfm_qcbor_test STATIC EXCLUDE_FROM_ALL)
+
+target_sources(tfm_qcbor_test
+    PRIVATE
+        ${QCBOR_SRC_DIR}/test/float_tests.c
+        ${QCBOR_SRC_DIR}/test/half_to_double_from_rfc7049.c
+        ${QCBOR_SRC_DIR}/test/qcbor_decode_tests.c
+        ${QCBOR_SRC_DIR}/test/qcbor_encode_tests.c
+        ${QCBOR_SRC_DIR}/test/run_tests.c
+        ${QCBOR_SRC_DIR}/test/UsefulBuf_Tests.c
+)
+
+target_include_directories(tfm_qcbor_test
+    PUBLIC
+        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/test>
+    PRIVATE
+        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/inc>
+)
+
+target_link_libraries(tfm_qcbor_test
+    PRIVATE
+        tfm_qcbor_ns
+)
diff --git a/test/suites/t_cose/CMakeLists.txt b/test/suites/t_cose/CMakeLists.txt
index 54dc2d9..1553517 100644
--- a/test/suites/t_cose/CMakeLists.txt
+++ b/test/suites/t_cose/CMakeLists.txt
@@ -7,10 +7,47 @@
 
 cmake_policy(SET CMP0079 NEW)
 
+if (NOT TEST_NS_ATTESTATION AND NOT TEST_NS_T_COSE)
+    return()
+endif()
+
+set(T_COSE_SRC_DIR ${CMAKE_SOURCE_DIR}/lib/ext/t_cose)
+
+############################ t_cose non secure #################################
+
+add_library(tfm_t_cose_ns STATIC EXCLUDE_FROM_ALL)
+
+target_link_libraries(tfm_t_cose_ns
+    PUBLIC
+        tfm_t_cose_common
+        tfm_t_cose_defs
+        tfm_qcbor_ns
+        tfm_ns_interface
+)
+
 if (NOT TEST_NS_T_COSE)
     return()
 endif()
 
+############################ t_cose test #######################################
+
+add_library(tfm_t_cose_test STATIC EXCLUDE_FROM_ALL)
+
+target_sources(tfm_t_cose_test
+    PRIVATE
+        ${T_COSE_SRC_DIR}/test/run_tests.c
+        ${T_COSE_SRC_DIR}/test/t_cose_make_psa_test_key.c
+        ${T_COSE_SRC_DIR}/test/t_cose_make_test_messages.c
+        ${T_COSE_SRC_DIR}/test/t_cose_sign_verify_test.c
+        ${T_COSE_SRC_DIR}/test/t_cose_test.c
+)
+
+target_include_directories(tfm_t_cose_test
+    PUBLIC
+        $<BUILD_INTERFACE:${T_COSE_SRC_DIR}/inc>
+        $<BUILD_INTERFACE:${T_COSE_SRC_DIR}/test>
+)
+
 ####################### Non Secure #############################################
 
 add_library(tfm_test_suite_t_cose_ns STATIC EXCLUDE_FROM_ALL)
@@ -42,3 +79,8 @@
     INTERFACE
         tfm_test_suite_t_cose_ns
 )
+
+target_link_libraries(tfm_t_cose_test
+    PRIVATE
+        tfm_t_cose_ns
+)
diff --git a/test/test_services/CMakeLists.txt b/test/test_services/CMakeLists.txt
index b894765..6c179c7 100644
--- a/test/test_services/CMakeLists.txt
+++ b/test/test_services/CMakeLists.txt
@@ -40,3 +40,7 @@
 if (TEST_NS_FLIH_IRQ)
     add_subdirectory(tfm_flih_test_service)
 endif()
+
+if (TEST_S_FPU)
+    add_subdirectory(tfm_fpu_service)
+endif()
diff --git a/test/test_services/tfm_fpu_service/CMakeLists.txt b/test/test_services/tfm_fpu_service/CMakeLists.txt
new file mode 100644
index 0000000..2de27e7
--- /dev/null
+++ b/test/test_services/tfm_fpu_service/CMakeLists.txt
@@ -0,0 +1,65 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2021, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+cmake_policy(SET CMP0079 NEW)
+
+add_library(tfm_app_rot_partition_fpu_service STATIC)
+
+target_sources(tfm_app_rot_partition_fpu_service
+    PRIVATE
+        ./tfm_fpu_service_test.c
+)
+
+# The generated sources
+target_sources(tfm_app_rot_partition_fpu_service
+    PRIVATE
+        $<$<BOOL:${TFM_PSA_API}>:
+            ${CMAKE_BINARY_DIR}/generated/test_services/tfm_fpu_service/auto_generated/intermedia_tfm_fpu_service_test.c>
+)
+
+target_sources(tfm_partitions
+    INTERFACE
+        $<$<BOOL:${TFM_PSA_API}>:
+            ${CMAKE_BINARY_DIR}/generated/test_services/tfm_fpu_service/auto_generated/load_info_tfm_fpu_service_test.c>
+)
+
+target_include_directories(tfm_app_rot_partition_fpu_service
+    PUBLIC
+        .
+    PRIVATE
+        ${CMAKE_BINARY_DIR}/generated/test_services/tfm_fpu_service
+)
+
+target_include_directories(tfm_partitions
+    INTERFACE
+        ${CMAKE_BINARY_DIR}/generated/test_services/tfm_fpu_service
+)
+
+target_link_libraries(tfm_app_rot_partition_fpu_service
+    PRIVATE
+        tfm_secure_api
+        platform_s
+        tfm_test_framework_s
+        tfm_arch
+)
+
+target_link_libraries(tfm_spm
+    PRIVATE
+        tfm_app_rot_partition_fpu_service
+)
+
+############################ Partition Defs ####################################
+
+target_link_libraries(tfm_partitions
+    INTERFACE
+        tfm_app_rot_partition_fpu_service
+)
+
+target_compile_definitions(tfm_partition_defs
+    INTERFACE
+        TEST_S_FPU
+)
diff --git a/test/test_services/tfm_fpu_service/tfm_fpu_service_test.c b/test/test_services/tfm_fpu_service/tfm_fpu_service_test.c
new file mode 100644
index 0000000..91bee8c
--- /dev/null
+++ b/test/test_services/tfm_fpu_service/tfm_fpu_service_test.c
@@ -0,0 +1,411 @@
+/*
+ * Copyright (c) 2021, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <stdbool.h>
+#include "psa/client.h"
+#include "psa/service.h"
+#include "psa_manifest/tfm_fpu_service_test.h"
+#include "tfm_api.h"
+#include "tfm_hal_isolation.h"
+#include "tfm_secure_api.h"
+
+/* Define the return status */
+#define FPU_SP_TEST_SUCCESS     (0)
+#define FPU_SP_TEST_FAILED      (-1)
+
+/*
+ * Fixme: Temporarily implement abort as infinite loop,
+ * will replace it later.
+ */
+static void tfm_abort(void)
+{
+    while (1)
+        ;
+}
+
+/**
+ * Clear FP registers.
+ */
+__attribute__((naked)) static void clear_fp_regs(void)
+{
+    __asm volatile(
+        "eor       r0, r0, r0              \n"
+        "vmov      s0, r0                  \n"
+        "vmov      s1, r0                  \n"
+        "vmov      s2, r0                  \n"
+        "vmov      s3, r0                  \n"
+        "vmov      s4, r0                  \n"
+        "vmov      s5, r0                  \n"
+        "vmov      s6, r0                  \n"
+        "vmov      s7, r0                  \n"
+        "vmov      s8, r0                  \n"
+        "vmov      s9, r0                  \n"
+        "vmov      s10, r0                 \n"
+        "vmov      s11, r0                 \n"
+        "vmov      s12, r0                 \n"
+        "vmov      s13, r0                 \n"
+        "vmov      s14, r0                 \n"
+        "vmov      s15, r0                 \n"
+        "vmov      s16, r0                 \n"
+        "vmov      s17, r0                 \n"
+        "vmov      s18, r0                 \n"
+        "vmov      s19, r0                 \n"
+        "vmov      s20, r0                 \n"
+        "vmov      s21, r0                 \n"
+        "vmov      s22, r0                 \n"
+        "vmov      s23, r0                 \n"
+        "vmov      s24, r0                 \n"
+        "vmov      s25, r0                 \n"
+        "vmov      s26, r0                 \n"
+        "vmov      s27, r0                 \n"
+        "vmov      s28, r0                 \n"
+        "vmov      s29, r0                 \n"
+        "vmov      s30, r0                 \n"
+        "vmov      s31, r0                 \n"
+        "bx        lr                      \n"
+    );
+}
+
+/**
+ * Check whether FP registers are restored correctly.
+ * Return:
+ *   1 - FP registers are restored correctly
+ *   0 - FP registers are not restored correctly
+ */
+__attribute__((naked)) static bool check_fp_restored_service(void)
+{
+    __asm volatile(
+        "mov       r3, #0                  \n"
+
+        "vmov      r2, s0                  \n"
+        "cmp       r2, 0x000000E0          \n"
+        "bne       quit                    \n"
+
+        "vmov      r2, s1                  \n"
+        "cmp       r2, 0x000000E1          \n"
+        "bne       quit                    \n"
+
+        "vmov      r2, s2                  \n"
+        "cmp       r2, 0x000000E2          \n"
+        "bne       quit                    \n"
+
+        "vmov      r2, s3                  \n"
+        "cmp       r2, 0x000000E3          \n"
+        "bne       quit                    \n"
+
+        "vmov      r2, s4                  \n"
+        "cmp       r2, 0x000000E4          \n"
+        "bne       quit                    \n"
+
+        "vmov      r2, s5                  \n"
+        "cmp       r2, 0x000000E5          \n"
+        "bne       quit                    \n"
+
+        "vmov      r2, s6                  \n"
+        "cmp       r2, 0x000000E6          \n"
+        "bne       quit                    \n"
+
+        "vmov      r2, s7                  \n"
+        "cmp       r2, 0x000000E7          \n"
+        "bne       quit                    \n"
+
+        "vmov      r2, s8                  \n"
+        "cmp       r2, 0x000000E8          \n"
+        "bne       quit                    \n"
+
+        "vmov      r2, s9                  \n"
+        "cmp       r2, 0x000000E9          \n"
+        "bne       quit                    \n"
+
+        "vmov      r2, s10                 \n"
+        "cmp       r2, 0x000000EA          \n"
+        "bne       quit                    \n"
+
+        "vmov      r2, s11                 \n"
+        "cmp       r2, 0x000000EB          \n"
+        "bne       quit                    \n"
+
+        "vmov      r2, s12                 \n"
+        "cmp       r2, 0x000000EC          \n"
+        "bne       quit                    \n"
+
+        "vmov      r2, s13                 \n"
+        "cmp       r2, 0x000000ED          \n"
+        "bne       quit                    \n"
+
+        "vmov      r2, s14                 \n"
+        "cmp       r2, 0x000000EE          \n"
+        "bne       quit                    \n"
+
+        "vmov      r2, s15                 \n"
+        "cmp       r2, 0x000000EF          \n"
+        "bne       quit                    \n"
+
+        "vmov      r2, s16                 \n"
+        "cmp       r2, 0x000000F0          \n"
+        "bne       quit                    \n"
+
+        "vmov      r2, s17                 \n"
+        "cmp       r2, 0x000000F1          \n"
+        "bne       quit                    \n"
+
+        "vmov      r2, s18                 \n"
+        "cmp       r2, 0x000000F2          \n"
+        "bne       quit                    \n"
+
+        "vmov      r2, s19                 \n"
+        "cmp       r2, 0x000000F3          \n"
+        "bne       quit                    \n"
+
+        "vmov      r2, s20                 \n"
+        "cmp       r2, 0x000000F4          \n"
+        "bne       quit                    \n"
+
+        "vmov      r2, s21                 \n"
+        "cmp       r2, 0x000000F5          \n"
+        "bne       quit                    \n"
+
+        "vmov      r2, s22                 \n"
+        "cmp       r2, 0x000000F6          \n"
+        "bne       quit                    \n"
+
+        "vmov      r2, s23                 \n"
+        "cmp       r2, 0x000000F7          \n"
+        "bne       quit                    \n"
+
+        "vmov      r2, s24                 \n"
+        "cmp       r2, 0x000000F8          \n"
+        "bne       quit                    \n"
+
+        "vmov      r2, s25                 \n"
+        "cmp       r2, 0x000000F9          \n"
+        "bne       quit                    \n"
+
+        "vmov      r2, s26                 \n"
+        "cmp       r2, 0x000000FA          \n"
+        "bne       quit                    \n"
+
+        "vmov      r2, s27                 \n"
+        "cmp       r2, 0x000000FB          \n"
+        "bne       quit                    \n"
+
+        "vmov      r2, s28                 \n"
+        "cmp       r2, 0x000000FC          \n"
+        "bne       quit                    \n"
+
+        "vmov      r2, s29                 \n"
+        "cmp       r2, 0x000000FD          \n"
+        "bne       quit                    \n"
+
+        "vmov      r2, s30                 \n"
+        "cmp       r2, 0x000000FE          \n"
+        "bne       quit                    \n"
+
+        "vmov      r2, s31                 \n"
+        "cmp       r2, 0x000000FF          \n"
+        "bne       quit                    \n"
+
+        "mov       r3, #1                  \n"
+        "quit:                             \n"
+        "mov       r0, r3                  \n"
+
+        "bx        lr                      \n"
+    );
+}
+
+/**
+ * Change FP registers in FP service partition.
+ */
+__attribute__((naked)) static void change_fp_in_service(void)
+{
+    __asm volatile(
+        "mov       r0, #0x000000E0         \n"
+        "vmov      s0, r0                  \n"
+        "mov       r0, #0x000000E1         \n"
+        "vmov      s1, r0                  \n"
+        "mov       r0, #0x000000E2         \n"
+        "vmov      s2, r0                  \n"
+        "mov       r0, #0x000000E3         \n"
+        "vmov      s3, r0                  \n"
+        "mov       r0, #0x000000E4         \n"
+        "vmov      s4, r0                  \n"
+        "mov       r0, #0x000000E5         \n"
+        "vmov      s5, r0                  \n"
+        "mov       r0, #0x000000E6         \n"
+        "vmov      s6, r0                  \n"
+        "mov       r0, #0x000000E7         \n"
+        "vmov      s7, r0                  \n"
+        "mov       r0, #0x000000E8         \n"
+        "vmov      s8, r0                  \n"
+        "mov       r0, #0x000000E9         \n"
+        "vmov      s9, r0                  \n"
+        "mov       r0, #0x000000EA         \n"
+        "vmov      s10, r0                 \n"
+        "mov       r0, #0x000000EB         \n"
+        "vmov      s11, r0                 \n"
+        "mov       r0, #0x000000EC         \n"
+        "vmov      s12, r0                 \n"
+        "mov       r0, #0x000000ED         \n"
+        "vmov      s13, r0                 \n"
+        "mov       r0, #0x000000EE         \n"
+        "vmov      s14, r0                 \n"
+        "mov       r0, #0x000000EF         \n"
+        "vmov      s15, r0                 \n"
+        "mov       r0, #0x000000F0         \n"
+        "vmov      s16, r0                 \n"
+        "mov       r0, #0x000000F1         \n"
+        "vmov      s17, r0                 \n"
+        "mov       r0, #0x000000F2         \n"
+        "vmov      s18, r0                 \n"
+        "mov       r0, #0x000000F3         \n"
+        "vmov      s19, r0                 \n"
+        "mov       r0, #0x000000F4         \n"
+        "vmov      s20, r0                 \n"
+        "mov       r0, #0x000000F5         \n"
+        "vmov      s21, r0                 \n"
+        "mov       r0, #0x000000F6         \n"
+        "vmov      s22, r0                 \n"
+        "mov       r0, #0x000000F7         \n"
+        "vmov      s23, r0                 \n"
+        "mov       r0, #0x000000F8         \n"
+        "vmov      s24, r0                 \n"
+        "mov       r0, #0x000000F9         \n"
+        "vmov      s25, r0                 \n"
+        "mov       r0, #0x000000FA         \n"
+        "vmov      s26, r0                 \n"
+        "mov       r0, #0x000000FB         \n"
+        "vmov      s27, r0                 \n"
+        "mov       r0, #0x000000FC         \n"
+        "vmov      s28, r0                 \n"
+        "mov       r0, #0x000000FD         \n"
+        "vmov      s29, r0                 \n"
+        "mov       r0, #0x000000FE         \n"
+        "vmov      s30, r0                 \n"
+        "mov       r0, #0x000000FF         \n"
+        "vmov      s31, r0                 \n"
+
+        "bx        lr                      \n"
+    );
+}
+
+/**
+ * Check whether FP registers is invalidated.
+ */
+__attribute__((naked)) static bool is_fp_cleaned(void)
+{
+    __asm volatile(
+        "mov       r3, #1                  \n"
+        "mov       r2, #0                  \n"
+        "vadd.f32  s2, s1, s0              \n"
+        "vadd.f32  s4, s3, s2              \n"
+        "vadd.f32  s6, s5, s4              \n"
+        "vadd.f32  s8, s7, s6              \n"
+        "vadd.f32  s10, s9, s8             \n"
+        "vadd.f32  s12, s11, s10           \n"
+        "vadd.f32  s14, s13, s12           \n"
+        "vadd.f32  s16, s15, s14           \n"
+        "vadd.f32  s18, s17, s16           \n"
+        "vadd.f32  s20, s19, s18           \n"
+        "vadd.f32  s22, s21, s20           \n"
+        "vadd.f32  s24, s23, s22           \n"
+        "vadd.f32  s26, s25, s24           \n"
+        "vadd.f32  s28, s27, s26           \n"
+        "vadd.f32  s30, s29, s28           \n"
+        "vadd.f32  s31, s29, s30           \n"
+        "vcmp.f32  s31, #0.0               \n"
+        "vmrs      APSR_nzcv, fpscr        \n"
+        "beq       cleaned                 \n"
+        "mov       r3, r2                  \n"
+        "cleaned:                          \n"
+        "mov       r0, r3                  \n"
+
+        "bx       lr                      \n"
+    );
+}
+
+/**
+ * Service handler for clear FP register.
+ */
+static void fpu_service_clear_fp_register(void)
+{
+    psa_msg_t msg;
+    psa_status_t r;
+
+    psa_get(TFM_FPU_SERVICE_CLEAR_FP_REGISTER_SIGNAL, &msg);
+    switch (msg.type) {
+    case PSA_IPC_CONNECT:
+        psa_reply(msg.handle, PSA_SUCCESS);
+        break;
+    case PSA_IPC_CALL:
+        if (check_fp_restored_service()) {
+            clear_fp_regs();
+            r = PSA_SUCCESS;
+        } else {
+            r = PSA_ERROR_INVALID_ARGUMENT;
+        }
+        psa_reply(msg.handle, r);
+        break;
+    case PSA_IPC_DISCONNECT:
+        psa_reply(msg.handle, PSA_SUCCESS);
+        break;
+    default:
+        /* Should not come here */
+        tfm_abort();
+        break;
+    }
+}
+
+/**
+ * Service handler for checking FP register.
+ */
+static void fpu_service_check_fp_register(void)
+{
+    psa_msg_t msg;
+
+    psa_get(TFM_FPU_SERVICE_CHECK_FP_REGISTER_SIGNAL, &msg);
+    switch (msg.type) {
+    case PSA_IPC_CONNECT:
+        psa_reply(msg.handle, PSA_SUCCESS);
+        break;
+    case PSA_IPC_CALL:
+        if (is_fp_cleaned()) {
+            change_fp_in_service();
+            psa_reply(msg.handle, PSA_SUCCESS);
+        } else {
+            psa_reply(msg.handle, PSA_ERROR_GENERIC_ERROR);
+        }
+        break;
+    case PSA_IPC_DISCONNECT:
+        psa_reply(msg.handle, PSA_SUCCESS);
+        break;
+    default:
+        /* Should not come here */
+        tfm_abort();
+        break;
+    }
+}
+
+/**
+ * FP service partition main thread.
+ */
+void fpu_service_test_main(void *param)
+{
+    uint32_t signals = 0;
+
+    clear_fp_regs();
+    while (1) {
+        signals = psa_wait(PSA_WAIT_ANY, PSA_BLOCK);
+        if (signals & TFM_FPU_SERVICE_CLEAR_FP_REGISTER_SIGNAL) {
+            fpu_service_clear_fp_register();
+        } else if (signals & TFM_FPU_SERVICE_CHECK_FP_REGISTER_SIGNAL) {
+            fpu_service_check_fp_register();
+        } else {
+            /* Should not come here */
+            tfm_abort();
+        }
+    }
+}
diff --git a/test/test_services/tfm_fpu_service/tfm_fpu_service_test.yaml b/test/test_services/tfm_fpu_service/tfm_fpu_service_test.yaml
new file mode 100644
index 0000000..96a05d9
--- /dev/null
+++ b/test/test_services/tfm_fpu_service/tfm_fpu_service_test.yaml
@@ -0,0 +1,33 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2021, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+{
+  "psa_framework_version": 1.0,
+  "name": "TFM_SP_FPU_SERVICE_TEST",
+  "type": "APPLICATION-ROT",
+  "priority": "NORMAL",
+  "entry_point": "fpu_service_test_main",
+  "stack_size": "0x0220",
+  "secure_functions": [
+  ],
+  "services" : [
+    {
+      "name": "TFM_FPU_SERVICE_CLEAR_FP_REGISTER",
+      "sid": "0x0000F090",
+      "non_secure_clients": false,
+      "version": 1,
+      "version_policy": "STRICT"
+    },
+    {
+      "name": "TFM_FPU_SERVICE_CHECK_FP_REGISTER",
+      "sid": "0x0000F091",
+      "non_secure_clients": false,
+      "version": 1,
+      "version_policy": "STRICT"
+    }
+  ]
+}
diff --git a/test/test_services/tfm_secure_client_service/tfm_secure_client_service.yaml b/test/test_services/tfm_secure_client_service/tfm_secure_client_service.yaml
index 8943133..b448017 100644
--- a/test/test_services/tfm_secure_client_service/tfm_secure_client_service.yaml
+++ b/test/test_services/tfm_secure_client_service/tfm_secure_client_service.yaml
@@ -56,6 +56,8 @@
     "TFM_FWU_REQUEST_REBOOT",
     "TFM_FWU_ACCEPT",
     "IPC_SERVICE_TEST_STATELESS_ROT",
-    "TFM_ATTEST_TEST_GET_PUBLIC_KEY"
+    "TFM_ATTEST_TEST_GET_PUBLIC_KEY",
+    "TFM_FPU_SERVICE_CLEAR_FP_REGISTER",
+    "TFM_FPU_SERVICE_CHECK_FP_REGISTER"
   ]
 }
diff --git a/test/test_services/tfm_test_manifest_list.yaml b/test/test_services/tfm_test_manifest_list.yaml
index 791b64f..bd11cfd 100644
--- a/test/test_services/tfm_test_manifest_list.yaml
+++ b/test/test_services/tfm_test_manifest_list.yaml
@@ -158,6 +158,20 @@
            "*tfm_*partition_flih_test.*"
          ]
       }
+    },
+    {
+      "name": "TFM FPU Test Service",
+      "short_name": "TFM_SP_FPU_SERVICE_TEST",
+      "manifest": "tfm_fpu_service/tfm_fpu_service_test.yaml",
+      "output_path": "test_services/tfm_fpu_service",
+      "conditional": "@TEST_S_FPU@",
+      "version_major": 0,
+      "version_minor": 1,
+      "linker_pattern": {
+        "library_list": [
+           "*tfm_*partition_fpu_service.*"
+         ]
+      }
     }
   ]
 }