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/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__ */