Test: Add firmware update tests

Signed-off-by: Sherry Zhang <sherry.zhang2@arm.com>
Change-Id: I25db2144af216a028255ca58c01bc98538e0bb8b
diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt
index aabdaed..a210219 100644
--- a/app/CMakeLists.txt
+++ b/app/CMakeLists.txt
@@ -80,7 +80,8 @@
         $<$<OR:$<BOOL:{$FORWARD_PROT_MSG}>,$<BOOL:${TFM_PARTITION_INTERNAL_TRUSTED_STORAGE}>>:${INTERFACE_SRC_DIR}/tfm_its_ipc_api.c>
         $<$<OR:$<BOOL:{$FORWARD_PROT_MSG}>,$<BOOL:${TFM_PARTITION_CRYPTO}>>:${INTERFACE_SRC_DIR}/tfm_crypto_ipc_api.c>
         $<$<OR:$<BOOL:{$FORWARD_PROT_MSG}>,$<BOOL:${TFM_PARTITION_INITIAL_ATTESTATION}>>:${INTERFACE_SRC_DIR}/tfm_initial_attestation_ipc_api.c>
-    )
+        $<$<BOOL:${TFM_PARTITION_FIRMWARE_UPDATE}>:${INTERFACE_SRC_DIR}/tfm_firmware_update_ipc_api.c>
+        )
 
     if (TFM_MULTI_CORE_TOPOLOGY)
         target_sources(tfm_api_ns PRIVATE
@@ -105,6 +106,7 @@
         $<$<BOOL:${TFM_PARTITION_INTERNAL_TRUSTED_STORAGE}>:${INTERFACE_SRC_DIR}/tfm_its_func_api.c>
         $<$<BOOL:${TFM_PARTITION_CRYPTO}>:${INTERFACE_SRC_DIR}/tfm_crypto_func_api.c>
         $<$<BOOL:${TFM_PARTITION_INITIAL_ATTESTATION}>:${INTERFACE_SRC_DIR}/tfm_initial_attestation_func_api.c>
+        $<$<BOOL:${TFM_PARTITION_FIRMWARE_UPDATE}>:${INTERFACE_SRC_DIR}/tfm_firmware_update_func_api.c>
         ${INTERFACE_SRC_DIR}/tfm_psa_ns_api.c
         ${INTERFACE_SRC_DIR}/tfm_ns_interface.c
     )
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 69c7832..30d2356 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -31,6 +31,8 @@
 add_subdirectory(suites/ps)
 add_subdirectory(suites/t_cose)
 add_subdirectory(suites/platform)
+add_subdirectory(suites/fwu)
+
 if(TFM_PSA_API)
     add_subdirectory(suites/ipc)
 endif()
diff --git a/test/framework/non_secure_suites.c b/test/framework/non_secure_suites.c
index 0b0037c..0d29063 100644
--- a/test/framework/non_secure_suites.c
+++ b/test/framework/non_secure_suites.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017-2020, Arm Limited. All rights reserved.
+ * Copyright (c) 2017-2021, Arm Limited. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  *
@@ -19,6 +19,9 @@
 #if defined(TFM_PARTITION_CRYPTO) || defined(FORWARD_PROT_MSG)
 #include "crypto_ns_tests.h"
 #endif
+#if defined(TFM_PARTITION_FIRMWARE_UPDATE)
+#include "fwu_ns_tests.h"
+#endif
 #if defined(TFM_PARTITION_INITIAL_ATTESTATION) || defined(FORWARD_PROT_MSG)
 #include "attest_ns_tests.h"
 #include "qcbor_ns_tests.h"
@@ -81,6 +84,11 @@
     {&register_testsuite_ns_audit_interface, 0, 0, 0},
 #endif
 
+#ifdef TFM_PARTITION_FIRMWARE_UPDATE
+    /* Non-secure Firmware Update test cases */
+    {&register_testsuite_ns_psa_fwu_interface, 0, 0, 0},
+#endif
+
 /* Non-secure core test cases */
 {&register_testsuite_ns_core_positive, 0, 0, 0},
 
diff --git a/test/framework/secure_suites.c b/test/framework/secure_suites.c
index 5464ac8..8a1d821 100644
--- a/test/framework/secure_suites.c
+++ b/test/framework/secure_suites.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017-2020, Arm Limited. All rights reserved.
+ * Copyright (c) 2017-2021, Arm Limited. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  *
@@ -22,6 +22,9 @@
 #if defined(TFM_PARTITION_CRYPTO) || defined(FORWARD_PROT_MSG)
 #include "crypto_s_tests.h"
 #endif
+#if defined(TFM_PARTITION_FIRMWARE_UPDATE)
+#include "fwu_s_tests.h"
+#endif
 #if defined(TFM_PARTITION_PLATFORM) || defined(FORWARD_PROT_MSG)
 #include "platform_s_tests.h"
 #endif
@@ -69,6 +72,11 @@
     {&register_testsuite_s_audit_interface, 0, 0, 0},
 #endif
 
+#ifdef TFM_PARTITION_FIRMWARE_UPDATE
+    /* Non-secure Firmware Update test cases */
+    {&register_testsuite_s_psa_fwu_interface, 0, 0, 0},
+#endif
+
 #ifdef TFM_PSA_API
     /* Secure IPC test cases */
     {&register_testsuite_s_ipc_interface, 0, 0, 0},
diff --git a/test/suites/fwu/CMakeLists.txt b/test/suites/fwu/CMakeLists.txt
new file mode 100644
index 0000000..d1e38ad
--- /dev/null
+++ b/test/suites/fwu/CMakeLists.txt
@@ -0,0 +1,84 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2021, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+cmake_policy(SET CMP0079 NEW)
+
+if (NOT TFM_PARTITION_FIRMWARE_UPDATE)
+    return()
+endif()
+
+####################### Non Secure #############################################
+
+add_library(tfm_test_suite_fwu_ns STATIC EXCLUDE_FROM_ALL)
+
+target_sources(tfm_test_suite_fwu_ns
+    PRIVATE
+        ./fwu_tests_common.c
+        ./non_secure/psa_fwu_ns_interface_testsuite.c
+)
+
+target_include_directories(tfm_test_suite_fwu_ns
+    PUBLIC
+        ./non_secure
+)
+
+target_link_libraries(tfm_test_suite_fwu_ns
+    PRIVATE
+        tfm_test_framework_ns
+)
+
+target_compile_definitions(tfm_test_suite_fwu_ns
+    PRIVATE
+        DOMAIN_NS=1
+        $<$<BOOL:${TFM_PSA_API}>:TFM_PSA_API>
+        $<$<BOOL:${TFM_FWU_TEST_WRITE_WITH_NULL}>:TFM_FWU_TEST_WRITE_WITH_NULL>
+        $<$<BOOL:${TFM_FWU_TEST_REQUEST_REBOOT}>:TFM_FWU_TEST_REQUEST_REBOOT>
+        $<$<BOOL:${TFM_FWU_TEST_QUERY_WITH_NULL}>:TFM_FWU_TEST_QUERY_WITH_NULL>
+        $<$<BOOL:${MCUBOOT_IMAGE_NUMBER}>:MCUBOOT_IMAGE_NUMBER=${MCUBOOT_IMAGE_NUMBER}>
+)
+
+target_link_libraries(tfm_ns_tests
+    INTERFACE
+        tfm_test_suite_fwu_ns
+)
+
+####################### Secure #################################################
+
+if (TEST_S)
+    target_sources(tfm_test_suite_fwu_s
+        PRIVATE
+            ${CMAKE_CURRENT_SOURCE_DIR}/fwu_tests_common.c
+            ${CMAKE_CURRENT_SOURCE_DIR}/secure/psa_fwu_s_interface_testsuite.c
+    )
+
+    target_include_directories(tfm_test_suite_fwu_s
+        PUBLIC
+            ${CMAKE_CURRENT_SOURCE_DIR}/secure
+    )
+
+    target_link_libraries(tfm_test_suite_fwu_s
+        PRIVATE
+            tfm_secure_api
+            tfm_test_framework_s
+            platform_s
+    )
+
+    target_compile_definitions(tfm_test_suite_fwu_s
+        PRIVATE
+            $<$<BOOL:${TFM_PSA_API}>:TFM_PSA_API>
+            $<$<BOOL:${TFM_FWU_TEST_WRITE_WITH_NULL}>:TFM_FWU_TEST_WRITE_WITH_NULL>
+            $<$<BOOL:${TFM_FWU_TEST_REQUEST_REBOOT}>:TFM_FWU_TEST_REQUEST_REBOOT>
+            $<$<BOOL:${TFM_FWU_TEST_QUERY_WITH_NULL}>:TFM_FWU_TEST_QUERY_WITH_NULL>
+            $<$<BOOL:${MCUBOOT_IMAGE_NUMBER}>:MCUBOOT_IMAGE_NUMBER=${MCUBOOT_IMAGE_NUMBER}>
+    )
+
+    target_link_libraries(tfm_s_tests
+        INTERFACE
+            tfm_test_suite_fwu_s
+    )
+
+endif()
diff --git a/test/suites/fwu/fwu_tests_common.c b/test/suites/fwu/fwu_tests_common.c
new file mode 100644
index 0000000..e4b3cbe
--- /dev/null
+++ b/test/suites/fwu/fwu_tests_common.c
@@ -0,0 +1,384 @@
+/*
+ * Copyright (c) 2021, 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 "fwu_tests_common.h"
+
+
+#if (MCUBOOT_IMAGE_NUMBER == 2)
+#define image_type_test FWU_IMAGE_TYPE_SECURE
+#else
+#define image_type_test FWU_IMAGE_TYPE_FULL
+#endif
+
+static psa_image_id_t image_id = \
+                (psa_image_id_t)FWU_CALCULATE_IMAGE_ID(FWU_IMAGE_ID_SLOT_STAGE,
+                                                       image_type_test,
+                                                       0);
+static const uint8_t data_block[32] = \
+                        { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, \
+                          0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x11, 0x12, \
+                          0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, \
+                          0x1b, 0x1c, 0x1d,  0x1e, 0x1f, 0x20, 0x21, 0x22 };
+
+void tfm_fwu_test_common_001(struct test_result_t *ret)
+{
+    psa_status_t status;
+    psa_image_id_t dependency_uuid;
+    psa_image_version_t dependency_version;
+    psa_image_info_t info;
+
+    /* Write the data block with block_offset 0. */
+    status = psa_fwu_write(image_id, 0, data_block, sizeof(data_block));
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Write should not fail with block_offset 0");
+        return;
+    }
+
+    /* Query the staging image. */
+    status = psa_fwu_query(image_id, &info);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Query should success");
+        return;
+    }
+
+    /* Check the image state. */
+    if (info.state != PSA_IMAGE_CANDIDATE) {
+        TEST_FAIL("Image should be in CANDIDATE state after succesfull write");
+        return;
+    }
+
+    /* Write the second data block with offset 32. */
+    status = psa_fwu_write(image_id,
+                           sizeof(data_block),
+                           data_block,
+                           sizeof(data_block));
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Write should not fail with block_offset 32");
+        return;
+    }
+
+    status = psa_fwu_install(image_id, &dependency_uuid, &dependency_version);
+
+    /* In the currently inplementation, image verification is deferred to
+     * reboot, so PSA_SUCCESS_REBOOT is returned when success.
+     */
+    if ((status != PSA_SUCCESS_REBOOT) &&
+        (status != PSA_ERROR_DEPENDENCY_NEEDED)) {
+        TEST_FAIL("Install should not fail after write");
+        return;
+    }
+
+    /* Query the staging image. */
+    if (psa_fwu_query(image_id, &info) != PSA_SUCCESS) {
+        TEST_FAIL("Query should success");
+        return;
+    }
+
+    /* Check the image state. */
+    if ((status == PSA_SUCCESS_REBOOT) &&
+       (info.state != PSA_IMAGE_REBOOT_NEEDED)) {
+        TEST_FAIL("Image should be in REBOOT_NEEDED state if PSA_SUCCESS_REBOOT is retuned in install");
+        return;
+    }
+
+    /* Abort the fwu process. */
+    status = psa_fwu_abort(image_id);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Abort should not fail after install");
+        return;
+    }
+
+    /* Query the staging image. */
+    status = psa_fwu_query(image_id, &info);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Query should success");
+        return;
+    }
+
+    /* Check the image state. */
+    if (info.state != PSA_IMAGE_UNDEFINED) {
+        TEST_FAIL("Image should be in UNDEFINED state after abort");
+        return;
+    }
+
+    /* Write the data block with block_offset 0 again after abort. */
+    status = psa_fwu_write(image_id, 0, data_block, sizeof(data_block));
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Write should not fail with block_offset 0 after abort");
+        return;
+    }
+
+    /* Query the staging image. */
+    status = psa_fwu_query(image_id, &info);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Query should success");
+        return;
+    }
+
+    /* Check the image state. */
+    if (info.state != PSA_IMAGE_CANDIDATE) {
+        TEST_FAIL("Image should be in CANDIDATE state after succesfull write");
+        return;
+    }
+
+    /* Abort the fwu process. */
+    status = psa_fwu_abort(image_id);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Abort should not fail after write");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+#if defined TFM_FWU_TEST_WRITE_WITH_NULL
+/* The TF-M core will reboot the device if the parameter check fails
+ * in partition request in libaray mode.
+ */
+void tfm_fwu_test_common_002(struct test_result_t *ret)
+{
+    psa_status_t status;
+
+    /* Write the data block NULL. */
+    status = psa_fwu_write(image_id, 0, NULL, sizeof(data_block));
+    if (status != PSA_ERROR_INVALID_ARGUMENT) {
+        TEST_FAIL("Write should fail with data block NULL");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+#endif
+
+void tfm_fwu_test_common_003(struct test_result_t *ret)
+{
+    psa_status_t status;
+
+    /* Write the data block with block_offset + len overflow. */
+    status = psa_fwu_write(image_id, (size_t)0xFFFFFFFF, data_block, 0x10);
+    if (status == PSA_SUCCESS) {
+        TEST_FAIL("Write should fail with len + image_offset overflow");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+void tfm_fwu_test_common_004(struct test_result_t *ret)
+{
+    psa_status_t status;
+    psa_image_version_t dependency_version;
+
+    /* Write the data block with block_offset 0. */
+    status = psa_fwu_write(image_id, 0, data_block, sizeof(data_block));
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Write should not fail with block_offset 0");
+        return;
+    }
+
+    status = psa_fwu_install(image_id, NULL, &dependency_version);
+
+    /* If PSA_ERROR_DEPENDENCY_NEEDED is returned, the dependency should be
+     * filled in the dependency argument.
+     */
+    if (status == PSA_ERROR_DEPENDENCY_NEEDED) {
+        TEST_FAIL("Install should not return PSA_ERROR_DEPENDENCY_NEEDED with NULL dependency");
+        return;
+    }
+
+    /* Abort the fwu process. */
+    status = psa_fwu_abort(image_id);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Abort should not fail after install");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+void tfm_fwu_test_common_005(struct test_result_t *ret)
+{
+    psa_status_t status;
+    psa_image_id_t dependency_uuid;
+
+    /* Write the data block with block_offset 0. */
+    status = psa_fwu_write(image_id, 0, data_block, sizeof(data_block));
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Write should not fail with block_offset 0");
+        return;
+    }
+
+    status = psa_fwu_install(image_id, &dependency_uuid, NULL);
+
+    /* If PSA_ERROR_DEPENDENCY_NEEDED is returned, the dependency should be
+     * filled in the dependency argument.
+     */
+    if (status == PSA_ERROR_DEPENDENCY_NEEDED) {
+        TEST_FAIL("Install should not return PSA_ERROR_DEPENDENCY_NEEDED with NULL dependency version");
+        return;
+    }
+
+    /* Abort the fwu process. */
+    status = psa_fwu_abort(image_id);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Abort should not fail after install");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+void tfm_fwu_test_common_006(struct test_result_t *ret)
+{
+    psa_status_t status;
+    psa_image_id_t dependency_uuid;
+    psa_image_version_t dependency_version;
+
+    status = psa_fwu_install(image_id, &dependency_uuid, &dependency_version);
+
+    /* Install will always fail if write is not called before. */
+    if (status != PSA_ERROR_INVALID_ARGUMENT) {
+        TEST_FAIL("Install should not fail after write");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+void tfm_fwu_test_common_007(struct test_result_t *ret)
+{
+    psa_status_t status;
+    psa_image_id_t dependency_uuid;
+    psa_image_version_t dependency_version;
+
+    /* Write the data block with block_offset 0. */
+    status = psa_fwu_write(image_id, 0, data_block, sizeof(data_block));
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Write should not fail with block_offset 0");
+        return;
+    }
+
+    /* Abort the fwu process. */
+    status = psa_fwu_abort(image_id);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Abort should not fail after install");
+        return;
+    }
+
+    status = psa_fwu_install(image_id, &dependency_uuid, &dependency_version);
+
+    /* Install should fail as the fwu process has been aborted. */
+    if (status != PSA_ERROR_INVALID_ARGUMENT) {
+        TEST_FAIL("Install should fail after abort");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+void tfm_fwu_test_common_008(struct test_result_t *ret)
+{
+    psa_status_t status;
+
+    status = psa_fwu_abort(image_id);
+
+    /* Install will always fail if write is not called before. */
+    if (status != PSA_ERROR_INVALID_ARGUMENT) {
+        TEST_FAIL("Install should fail with no write before");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+void tfm_fwu_test_common_009(struct test_result_t *ret)
+{
+    psa_status_t status;
+    psa_image_info_t info = { 0 };
+    psa_image_id_t image_id = FWU_CALCULATE_IMAGE_ID(FWU_IMAGE_ID_SLOT_ACTIVE,
+                                                     image_type_test,
+                                                     0);
+
+    /* Query the running image. */
+    status = psa_fwu_query(image_id, &info);
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Query should success");
+        return;
+    }
+
+    if ((info.state != PSA_IMAGE_INSTALLED) &&
+       (info.state != PSA_IMAGE_PENDING_INSTALL)) {
+        TEST_FAIL("The active image should be in INSTALLED or PENDING_INSTALL state");
+        return;
+    }
+    ret->val = TEST_PASSED;
+}
+
+#if defined TFM_FWU_TEST_QUERY_WITH_NULL
+void tfm_fwu_test_common_010(struct test_result_t *ret)
+{
+    psa_status_t status;
+
+    /* Query the running image. */
+    status = psa_fwu_query(image_id, NULL);
+    if (status == PSA_SUCCESS) {
+        TEST_FAIL("Query should fail with NULL info");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+#endif
+
+void tfm_fwu_test_common_011(struct test_result_t *ret)
+{
+    psa_status_t status;
+    psa_image_info_t info = { 0 };
+
+    psa_image_id_t image_id = FWU_CALCULATE_IMAGE_ID(3,
+                                                     image_type_test,
+                                                     0);
+
+    /* Query the running image. */
+    status = psa_fwu_query(image_id, &info);
+    if (status != PSA_ERROR_INVALID_ARGUMENT) {
+        TEST_FAIL("Query should fail with NULL info");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+void tfm_fwu_test_common_012(struct test_result_t *ret)
+{
+    psa_status_t status;
+
+    /* Accept the running image. */
+    status = psa_fwu_accept();
+    if (status != PSA_SUCCESS) {
+        TEST_FAIL("Accept should not fail");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
+
+#ifdef TFM_FWU_TEST_REQUEST_REBOOT
+void tfm_fwu_test_common_013(struct test_result_t *ret)
+{
+    /* Request reboot. */
+    psa_fwu_request_reboot();
+    TEST_FAIL("Request reboot should not fail");
+
+    ret->val = TEST_FAILED;
+}
+#endif
diff --git a/test/suites/fwu/fwu_tests_common.h b/test/suites/fwu/fwu_tests_common.h
new file mode 100644
index 0000000..fed3fde
--- /dev/null
+++ b/test/suites/fwu/fwu_tests_common.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2021, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __FWU_TESTS_COMMON_H__
+#define __FWU_TESTS_COMMON_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "psa/update.h"
+#include "test_framework_helpers.h"
+
+void tfm_fwu_test_common_001(struct test_result_t *ret);
+void tfm_fwu_test_common_002(struct test_result_t *ret);
+void tfm_fwu_test_common_003(struct test_result_t *ret);
+void tfm_fwu_test_common_004(struct test_result_t *ret);
+void tfm_fwu_test_common_005(struct test_result_t *ret);
+void tfm_fwu_test_common_006(struct test_result_t *ret);
+void tfm_fwu_test_common_007(struct test_result_t *ret);
+void tfm_fwu_test_common_008(struct test_result_t *ret);
+void tfm_fwu_test_common_009(struct test_result_t *ret);
+void tfm_fwu_test_common_010(struct test_result_t *ret);
+void tfm_fwu_test_common_011(struct test_result_t *ret);
+void tfm_fwu_test_common_012(struct test_result_t *ret);
+void tfm_fwu_test_common_013(struct test_result_t *ret);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __FWU_TESTS_COMMON_H__ */
diff --git a/test/suites/fwu/non_secure/fwu_ns_tests.h b/test/suites/fwu/non_secure/fwu_ns_tests.h
new file mode 100644
index 0000000..2e0e6c6
--- /dev/null
+++ b/test/suites/fwu/non_secure/fwu_ns_tests.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2021, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __FWU_NS_TESTS_H__
+#define __FWU_NS_TESTS_H__
+
+#include "test_framework.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Register testsuite for the PSA Firmware Update service NS interface
+ *        tests.
+ *
+ * \param[in] p_test_suite  The test suite to be executed.
+ */
+void register_testsuite_ns_psa_fwu_interface(struct test_suite_t *p_test_suite);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __FWU_NS_TESTS_H__ */
diff --git a/test/suites/fwu/non_secure/psa_fwu_ns_interface_testsuite.c b/test/suites/fwu/non_secure/psa_fwu_ns_interface_testsuite.c
new file mode 100644
index 0000000..590e791
--- /dev/null
+++ b/test/suites/fwu/non_secure/psa_fwu_ns_interface_testsuite.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2021, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "fwu_ns_tests.h"
+#include "test_framework_helpers.h"
+#include "../fwu_tests_common.h"
+
+static struct test_t psa_fwu_ns_tests[] = {
+    {&tfm_fwu_test_common_001, "TFM_FWU_TEST_1001",
+     "Write, install and abort interface", {TEST_PASSED} },
+#if defined TFM_FWU_TEST_WRITE_WITH_NULL
+    {&tfm_fwu_test_common_002, "TFM_FWU_TEST_1002",
+     "Write interface with NULL block pointer", {TEST_PASSED} },
+#endif
+    {&tfm_fwu_test_common_003, "TFM_FWU_TEST_1003",
+     "Write interface with image_offset + block_size overflow", {TEST_PASSED} },
+    {&tfm_fwu_test_common_004, "TFM_FWU_TEST_1004",
+     "Install interface with NULL dependency uuid", {TEST_PASSED} },
+    {&tfm_fwu_test_common_005, "TFM_FWU_TEST_1005",
+     "Install interface with NULL dependency version", {TEST_PASSED} },
+    {&tfm_fwu_test_common_006, "TFM_FWU_TEST_1006",
+     "Install before write", {TEST_PASSED} },
+    {&tfm_fwu_test_common_007, "TFM_FWU_TEST_1007",
+     "Install after abort", {TEST_PASSED} },
+    {&tfm_fwu_test_common_008, "TFM_FWU_TEST_1008",
+     "Abort interface with no image is being installing", {TEST_PASSED} },
+    {&tfm_fwu_test_common_009, "TFM_FWU_TEST_1009",
+     "Query interface with the active image", {TEST_PASSED} },
+#if defined TFM_FWU_TEST_QUERY_WITH_NULL
+    {&tfm_fwu_test_common_010, "TFM_FWU_TEST_1010",
+     "Query interface with NULL info", {TEST_PASSED} },
+#endif
+    {&tfm_fwu_test_common_011, "TFM_FWU_TEST_1011",
+     "Query interface invald image id", {TEST_PASSED} },
+    {&tfm_fwu_test_common_012, "TFM_FWU_TEST_1012",
+     "Accept interface", {TEST_PASSED} },
+#ifdef TFM_FWU_TEST_REQUEST_REBOOT
+    {&tfm_fwu_test_common_013, "TFM_FWU_TEST_1013",
+     "Reboot interface", {TEST_PASSED} },
+#endif
+};
+
+void register_testsuite_ns_psa_fwu_interface(struct test_suite_t *p_test_suite)
+{
+    uint32_t list_size;
+
+    list_size = (sizeof(psa_fwu_ns_tests) / sizeof(psa_fwu_ns_tests[0]));
+
+    set_testsuite("PSA firmware update NS interface tests "
+                 "(TFM_FWU_TEST_1xxx)",
+                  psa_fwu_ns_tests, list_size, p_test_suite);
+}
diff --git a/test/suites/fwu/secure/fwu_s_tests.h b/test/suites/fwu/secure/fwu_s_tests.h
new file mode 100644
index 0000000..05ce915
--- /dev/null
+++ b/test/suites/fwu/secure/fwu_s_tests.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2021, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __FWU_S_TESTS_H__
+#define __FWU_S_TESTS_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "test_framework.h"
+
+/**
+ * \brief Register testsuite for the PSA Firmware Update service secure
+ *        interface tests.
+ *
+ * \param[in] p_test_suite  The test suite to be executed.
+ */
+void register_testsuite_s_psa_fwu_interface(struct test_suite_t *p_test_suite);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __FWU_S_TESTS_H__ */
diff --git a/test/suites/fwu/secure/psa_fwu_s_interface_testsuite.c b/test/suites/fwu/secure/psa_fwu_s_interface_testsuite.c
new file mode 100644
index 0000000..c502dcb
--- /dev/null
+++ b/test/suites/fwu/secure/psa_fwu_s_interface_testsuite.c
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2021, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+
+#include "fwu_s_tests.h"
+#include "psa/update.h"
+#include "test_framework_helpers.h"
+#include "../fwu_tests_common.h"
+#include "tfm_memory_utils.h"
+
+static struct test_t psa_fwu_s_tests[] = {
+    {&tfm_fwu_test_common_001, "TFM_FWU_TEST_1001",
+     "Write, install and abort interface", {TEST_PASSED} },
+#if defined TFM_FWU_TEST_WRITE_WITH_NULL
+    {&tfm_fwu_test_common_002, "TFM_FWU_TEST_1002",
+     "Write interface with NULL block pointer", {TEST_PASSED} },
+#endif
+    {&tfm_fwu_test_common_003, "TFM_FWU_TEST_1003",
+     "Write interface with image_offset + block_size overflow", {TEST_PASSED} },
+    {&tfm_fwu_test_common_004, "TFM_FWU_TEST_1004",
+     "Install interface with NULL dependency uuid", {TEST_PASSED} },
+    {&tfm_fwu_test_common_005, "TFM_FWU_TEST_1005",
+     "Install interface with NULL dependency version", {TEST_PASSED} },
+    {&tfm_fwu_test_common_006, "TFM_FWU_TEST_1006",
+     "Install before write", {TEST_PASSED} },
+    {&tfm_fwu_test_common_007, "TFM_FWU_TEST_1007",
+     "Install after abort", {TEST_PASSED} },
+    {&tfm_fwu_test_common_008, "TFM_FWU_TEST_1008",
+     "Abort interface with no image is being installing", {TEST_PASSED} },
+    {&tfm_fwu_test_common_009, "TFM_FWU_TEST_1009",
+     "Query interface with the active image", {TEST_PASSED} },
+#if defined TFM_FWU_TEST_QUERY_WITH_NULL
+    {&tfm_fwu_test_common_010, "TFM_FWU_TEST_1010",
+     "Query interface with NULL info", {TEST_PASSED} },
+#endif
+    {&tfm_fwu_test_common_011, "TFM_FWU_TEST_1011",
+     "Query interface invald image id", {TEST_PASSED} },
+    {&tfm_fwu_test_common_012, "TFM_FWU_TEST_1012",
+     "Accept interface", {TEST_PASSED} },
+#ifdef TFM_FWU_TEST_REQUEST_REBOOT
+    {&tfm_fwu_test_common_013, "TFM_FWU_TEST_1013",
+     "Reboot interface", {TEST_PASSED} },
+#endif
+};
+
+void register_testsuite_s_psa_fwu_interface(struct test_suite_t *p_test_suite)
+{
+    uint32_t list_size;
+
+    list_size = (sizeof(psa_fwu_s_tests) / sizeof(psa_fwu_s_tests[0]));
+
+    set_testsuite("PSA firmware update interface tests"
+                  "(TFM_FWU_TEST_1XXX)",
+                  psa_fwu_s_tests, list_size, p_test_suite);
+}
diff --git a/test/test_services/CMakeLists.txt b/test/test_services/CMakeLists.txt
index 5516364..139d825 100644
--- a/test/test_services/CMakeLists.txt
+++ b/test/test_services/CMakeLists.txt
@@ -1,5 +1,5 @@
 #-------------------------------------------------------------------------------
-# Copyright (c) 2020, Arm Limited. All rights reserved.
+# Copyright (c) 2020-2021, Arm Limited. All rights reserved.
 #
 # SPDX-License-Identifier: BSD-3-Clause
 #
@@ -37,6 +37,9 @@
     if(TFM_PSA_API)
         add_library(tfm_test_suite_ipc_s STATIC EXCLUDE_FROM_ALL)
     endif()
+    if(TFM_PARTITION_FIRMWARE_UPDATE)
+        add_library(tfm_test_suite_fwu_s STATIC EXCLUDE_FROM_ALL)
+    endif()
 endif()
 
 add_subdirectory(tfm_core_test)
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 7931f16..a63d08a 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
@@ -1,5 +1,5 @@
 #-------------------------------------------------------------------------------
-# Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+# Copyright (c) 2018-2021, Arm Limited. All rights reserved.
 #
 # SPDX-License-Identifier: BSD-3-Clause
 #
@@ -53,6 +53,12 @@
     "TFM_ATTEST_GET_PUBLIC_KEY",
     "TFM_PS_TEST_PREPARE",
     "TFM_SP_PLATFORM_SYSTEM_RESET",
-    "TFM_SP_PLATFORM_IOCTL"
+    "TFM_SP_PLATFORM_IOCTL",
+    "TFM_FWU_WRITE",
+    "TFM_FWU_INSTALL",
+    "TFM_FWU_ABORT",
+    "TFM_FWU_QUERY",
+    "TFM_FWU_REQUEST_REBOOT",
+    "TFM_FWU_ACCEPT"
   ]
 }