Platform: Add NS NV counters

Add a configurable amount of NS NV counters (up to 3), and expose them
to the NS caller.

Change-Id: I8adda25c1fae49c4dae36ccb562360f45ecbfc0a
Signed-off-by: Raef Coles <raef.coles@arm.com>
diff --git a/config/check_config.cmake b/config/check_config.cmake
index 2730889..58e0f2f 100644
--- a/config/check_config.cmake
+++ b/config/check_config.cmake
@@ -82,6 +82,7 @@
 tfm_invalid_config(OTP_NV_COUNTERS_RAM_EMULATION AND NOT (PLATFORM_DEFAULT_OTP OR PLATFORM_DEFAULT_NV_COUNTERS))
 tfm_invalid_config(PLATFORM_DEFAULT_NV_COUNTERS AND  NOT PLATFORM_DEFAULT_OTP_WRITEABLE)
 tfm_invalid_config(TFM_DUMMY_PROVISIONING AND  NOT PLATFORM_DEFAULT_OTP_WRITEABLE)
+tfm_invalid_config(TFM_NS_NV_COUNTER_AMOUNT GREATER 3)
 
 ####################### Firmware Update Parttion ###############################
 
diff --git a/config/config_default.cmake b/config/config_default.cmake
index 6d7e097..398339c 100755
--- a/config/config_default.cmake
+++ b/config/config_default.cmake
@@ -80,6 +80,7 @@
 set(CRYPTO_HW_ACCELERATOR               OFF         CACHE BOOL      "Whether to enable the crypto hardware accelerator on supported platforms")
 
 set(OTP_NV_COUNTERS_RAM_EMULATION       OFF         CACHE BOOL      "Enable OTP/NV_COUNTERS emulation in RAM. Has no effect on non-default implementations of the OTP and NV_COUNTERS")
+set(TFM_NS_NV_COUNTER_AMOUNT            0           CACHE STRING    "How many NS NV counters are enabled")
 
 set(PLATFORM_DEFAULT_BL1                ON          CACHE STRING    "Whether to use default BL1 or platform-specific one")
 
diff --git a/interface/src/tfm_platform_func_api.c b/interface/src/tfm_platform_func_api.c
index 3ef0c86..df566ec 100644
--- a/interface/src/tfm_platform_func_api.c
+++ b/interface/src/tfm_platform_func_api.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018-2019, Arm Limited. All rights reserved.
+ * Copyright (c) 2018-2022, Arm Limited. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  *
@@ -50,3 +50,35 @@
                                 (uint32_t)output, (uint32_t)outlen);
 }
 
+enum tfm_platform_err_t
+tfm_platform_nv_counter_increment(uint32_t counter_id)
+{
+    struct psa_invec in_vec[1];
+
+    in_vec[0].base = &counter_id;
+    in_vec[0].len = sizeof(counter_id);
+
+    return (enum tfm_platform_err_t) tfm_ns_interface_dispatch(
+            (veneer_fn)tfm_platform_sp_nv_counter_increment_veneer,
+            (uint32_t)in_vec, 1,
+            (uint32_t)NULL, 0);
+}
+
+enum tfm_platform_err_t
+tfm_platform_nv_counter_read(uint32_t counter_id,
+                             uint32_t size, uint8_t *val)
+{
+    struct psa_invec in_vec[1];
+    struct psa_outvec out_vec[1];
+
+    in_vec[0].base = &counter_id;
+    in_vec[0].len = sizeof(counter_id);
+
+    out_vec[0].base = val;
+    out_vec[0].len = size;
+
+    return (enum tfm_platform_err_t) tfm_ns_interface_dispatch(
+            (veneer_fn)tfm_platform_sp_nv_counter_read_veneer,
+            (uint32_t)in_vec, 1,
+            (uint32_t)out_vec, 1);
+}
diff --git a/interface/src/tfm_platform_ipc_api.c b/interface/src/tfm_platform_ipc_api.c
index 0c1edf4..072e243 100644
--- a/interface/src/tfm_platform_ipc_api.c
+++ b/interface/src/tfm_platform_ipc_api.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019, Arm Limited. All rights reserved.
+ * Copyright (c) 2019-2022, Arm Limited. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  *
@@ -27,7 +27,7 @@
     if (status < PSA_SUCCESS) {
         return TFM_PLATFORM_ERR_SYSTEM_ERROR;
     } else {
-        return (enum tfm_platform_err_t) status;
+        return (enum tfm_platform_err_t)status;
     }
 
 }
@@ -72,7 +72,67 @@
     if (status < PSA_SUCCESS) {
         return TFM_PLATFORM_ERR_SYSTEM_ERROR;
     } else {
-        return (enum tfm_platform_err_t) status;
+        return (enum tfm_platform_err_t)status;
     }
 }
 
+enum tfm_platform_err_t
+tfm_platform_nv_counter_increment(uint32_t counter_id)
+{
+    psa_status_t status = PSA_ERROR_CONNECTION_REFUSED;
+    psa_handle_t handle = PSA_NULL_HANDLE;
+    struct psa_invec in_vec[1];
+
+    in_vec[0].base = &counter_id;
+    in_vec[0].len = sizeof(counter_id);
+
+    handle = psa_connect(TFM_SP_PLATFORM_NV_COUNTER_SID,
+                         TFM_SP_PLATFORM_NV_COUNTER_VERSION);
+    if (handle <= 0) {
+        return TFM_PLATFORM_ERR_SYSTEM_ERROR;
+    }
+
+    status = psa_call(handle, TFM_PLATFORM_API_ID_NV_INCREMENT,
+                      in_vec, 1, (psa_outvec *)NULL, 0);
+
+    psa_close(handle);
+
+    if (status < PSA_SUCCESS) {
+        return TFM_PLATFORM_ERR_SYSTEM_ERROR;
+    } else {
+        return (enum tfm_platform_err_t)status;
+    }
+}
+
+enum tfm_platform_err_t
+tfm_platform_nv_counter_read(uint32_t counter_id,
+                             uint32_t size, uint8_t *val)
+{
+    psa_status_t status = PSA_ERROR_CONNECTION_REFUSED;
+    psa_handle_t handle = PSA_NULL_HANDLE;
+    struct psa_invec in_vec[1];
+    struct psa_outvec out_vec[1];
+
+    in_vec[0].base = &counter_id;
+    in_vec[0].len = sizeof(counter_id);
+
+    out_vec[0].base = val;
+    out_vec[0].len = size;
+
+    handle = psa_connect(TFM_SP_PLATFORM_NV_COUNTER_SID,
+                         TFM_SP_PLATFORM_NV_COUNTER_VERSION);
+    if (handle <= 0) {
+        return TFM_PLATFORM_ERR_SYSTEM_ERROR;
+    }
+
+    status = psa_call(handle, TFM_PLATFORM_API_ID_NV_READ,
+                      in_vec, 1, out_vec, 1);
+
+    psa_close(handle);
+
+    if (status < PSA_SUCCESS) {
+        return TFM_PLATFORM_ERR_SYSTEM_ERROR;
+    } else {
+        return (enum tfm_platform_err_t)status;
+    }
+}
diff --git a/platform/CMakeLists.txt b/platform/CMakeLists.txt
index 7fad26a..89bd2a6 100755
--- a/platform/CMakeLists.txt
+++ b/platform/CMakeLists.txt
@@ -248,4 +248,5 @@
         $<$<BOOL:${FORWARD_PROT_MSG}>:FORWARD_PROT_MSG=${FORWARD_PROT_MSG}>
         $<$<BOOL:${TFM_CODE_SHARING}>:CODE_SHARING>
         $<$<OR:$<CONFIG:Debug>,$<CONFIG:relwithdebinfo>>:ENABLE_HEAP>
+        PLATFORM_NS_NV_COUNTERS=${TFM_NS_NV_COUNTER_AMOUNT}
 )
diff --git a/platform/ext/accelerator/cc312/otp_cc312.c b/platform/ext/accelerator/cc312/otp_cc312.c
index dae765f..282965c 100644
--- a/platform/ext/accelerator/cc312/otp_cc312.c
+++ b/platform/ext/accelerator/cc312/otp_cc312.c
@@ -240,6 +240,16 @@
 #endif /* PLATFORM_DEFAULT_BL1 */
 #endif /* BL1 */
 
+#if (PLATFORM_NS_NV_COUNTERS > 0)
+        uint8_t ns_nv_counter_0[64];
+#endif
+#if (PLATFORM_NS_NV_COUNTERS > 1)
+        uint8_t ns_nv_counter_1[64];
+#endif
+#if (PLATFORM_NS_NV_COUNTERS > 2)
+        uint8_t ns_nv_counter_2[64];
+#endif
+
         uint8_t secure_debug_pk[32];
     };
 };
@@ -726,6 +736,22 @@
 #endif /* PLATFORM_DEFAULT_BL1 */
 #endif /* BL1 */
 
+#if (PLATFORM_NS_NV_COUNTERS > 0)
+    case PLAT_OTP_ID_NV_COUNTER_NS_0:
+        return otp_read(otp->ns_nv_counter_0,
+                        sizeof(otp->ns_nv_counter_0), out_len, out);
+#endif
+#if (PLATFORM_NS_NV_COUNTERS > 1)
+    case PLAT_OTP_ID_NV_COUNTER_NS_1:
+        return otp_read(otp->ns_nv_counter_1,
+                        sizeof(otp->ns_nv_counter_1), out_len, out);
+#endif
+#if (PLATFORM_NS_NV_COUNTERS > 2)
+    case PLAT_OTP_ID_NV_COUNTER_NS_2:
+        return otp_read(otp->ns_nv_counter_2,
+                        sizeof(otp->ns_nv_counter_2), out_len, out);
+#endif
+
     case PLAT_OTP_ID_ENTROPY_SEED:
         return TFM_PLAT_ERR_UNSUPPORTED;
 
@@ -947,6 +973,22 @@
 #endif /* PLATFORM_DEFAULT_BL1 */
 #endif /* BL1 */
 
+#if (PLATFORM_NS_NV_COUNTERS > 0)
+    case PLAT_OTP_ID_NV_COUNTER_NS_0:
+        return otp_write(otp->ns_nv_counter_0,
+                         sizeof(otp->ns_nv_counter_0), in_len, in, NULL);
+#endif
+#if (PLATFORM_NS_NV_COUNTERS > 1)
+    case PLAT_OTP_ID_NV_COUNTER_NS_1:
+        return otp_write(otp->ns_nv_counter_1,
+                         sizeof(otp->ns_nv_counter_1), in_len, in, NULL);
+#endif
+#if (PLATFORM_NS_NV_COUNTERS > 2)
+    case PLAT_OTP_ID_NV_COUNTER_NS_2:
+        return otp_write(otp->ns_nv_counter_2,
+                         sizeof(otp->ns_nv_counter_2), in_len, in, NULL);
+#endif
+
     case PLAT_OTP_ID_ENTROPY_SEED:
         return TFM_PLAT_ERR_UNSUPPORTED;
 
@@ -1055,6 +1097,22 @@
 #endif /* PLATFORM_DEFAULT_BL1 */
 #endif
 
+#if (PLATFORM_NS_NV_COUNTERS > 0)
+    case PLAT_OTP_ID_NV_COUNTER_NS_0:
+        *size = sizeof(otp->ns_nv_counter_0);
+        break;
+#endif
+#if (PLATFORM_NS_NV_COUNTERS > 1)
+    case PLAT_OTP_ID_NV_COUNTER_NS_1:
+        *size = sizeof(otp->ns_nv_counter_1);
+        break;
+#endif
+#if (PLATFORM_NS_NV_COUNTERS > 2)
+    case PLAT_OTP_ID_NV_COUNTER_NS_2:
+        *size = sizeof(otp->ns_nv_counter_2);
+        break;
+#endif
+
     case PLAT_OTP_ID_ENTROPY_SEED:
         return TFM_PLAT_ERR_UNSUPPORTED;
 
diff --git a/platform/ext/common/template/flash_otp_nv_counters_backend.h b/platform/ext/common/template/flash_otp_nv_counters_backend.h
index 61f9d8d..14d156f 100644
--- a/platform/ext/common/template/flash_otp_nv_counters_backend.h
+++ b/platform/ext/common/template/flash_otp_nv_counters_backend.h
@@ -55,6 +55,16 @@
         uint8_t bl1_nv_counter_0[16];
 #endif /* BL1 */
 
+#if (PLATFORM_NS_NV_COUNTERS > 0)
+        uint8_t ns_nv_counter_0[64];
+#endif
+#if (PLATFORM_NS_NV_COUNTERS > 1)
+        uint8_t ns_nv_counter_1[64];
+#endif
+#if (PLATFORM_NS_NV_COUNTERS > 2)
+        uint8_t ns_nv_counter_2[64];
+#endif
+
         uint8_t entropy_seed[64];
 
         uint8_t secure_debug_pk[32];
diff --git a/platform/ext/common/template/nv_counters.c b/platform/ext/common/template/nv_counters.c
index 7294f62..41648ef 100644
--- a/platform/ext/common/template/nv_counters.c
+++ b/platform/ext/common/template/nv_counters.c
@@ -140,6 +140,19 @@
         return read_nv_counter_otp(PLAT_OTP_ID_NV_COUNTER_BL1_0, size, val);
 #endif /* BL1 */
 
+#if (PLATFORM_NS_NV_COUNTERS > 0)
+    case (PLAT_NV_COUNTER_NS_0):
+        return read_nv_counter_otp(PLAT_OTP_ID_NV_COUNTER_NS_0, size, val);
+#endif
+#if (PLATFORM_NS_NV_COUNTERS > 1)
+    case (PLAT_NV_COUNTER_NS_1):
+        return read_nv_counter_otp(PLAT_OTP_ID_NV_COUNTER_NS_1, size, val);
+#endif
+#if (PLATFORM_NS_NV_COUNTERS > 2)
+    case (PLAT_NV_COUNTER_NS_2):
+        return read_nv_counter_otp(PLAT_OTP_ID_NV_COUNTER_NS_2, size, val);
+#endif
+
     default:
         return TFM_PLAT_ERR_UNSUPPORTED;
     }
@@ -245,6 +258,20 @@
     case (PLAT_NV_COUNTER_BL1_0):
         return set_nv_counter_otp(PLAT_OTP_ID_NV_COUNTER_BL1_0, value);
 #endif /* BL1 */
+
+#if (PLATFORM_NS_NV_COUNTERS > 0)
+    case (PLAT_NV_COUNTER_NS_0):
+        return set_nv_counter_otp(PLAT_OTP_ID_NV_COUNTER_NS_0, value);
+#endif
+#if (PLATFORM_NS_NV_COUNTERS > 1)
+    case (PLAT_NV_COUNTER_NS_1):
+        return set_nv_counter_otp(PLAT_OTP_ID_NV_COUNTER_NS_1, value);
+#endif
+#if (PLATFORM_NS_NV_COUNTERS > 2)
+    case (PLAT_NV_COUNTER_NS_2):
+        return set_nv_counter_otp(PLAT_OTP_ID_NV_COUNTER_NS_2, value);
+#endif
+
     default:
         return TFM_PLAT_ERR_UNSUPPORTED;
     }
diff --git a/platform/ext/common/template/otp_flash.c b/platform/ext/common/template/otp_flash.c
index 54df5a1..4b834d6 100644
--- a/platform/ext/common/template/otp_flash.c
+++ b/platform/ext/common/template/otp_flash.c
@@ -50,67 +50,80 @@
                                       size_t out_len, uint8_t *out)
 {
     switch (id) {
-        case PLAT_OTP_ID_HUK:
-            return write_to_output(id, offsetof(struct flash_otp_nv_counters_region_t, huk), out_len, out);
-        case PLAT_OTP_ID_IAK:
-            return write_to_output(id, offsetof(struct flash_otp_nv_counters_region_t, iak), out_len, out);
-        case PLAT_OTP_ID_IAK_LEN:
-            return write_to_output(id, offsetof(struct flash_otp_nv_counters_region_t, iak_len), out_len, out);
-        case PLAT_OTP_ID_IAK_TYPE:
-            return write_to_output(id, offsetof(struct flash_otp_nv_counters_region_t, iak_type), out_len, out);
-        case PLAT_OTP_ID_IAK_ID:
-            return write_to_output(id, offsetof(struct flash_otp_nv_counters_region_t, iak_id), out_len, out);
+    case PLAT_OTP_ID_HUK:
+        return write_to_output(id, offsetof(struct flash_otp_nv_counters_region_t, huk), out_len, out);
+    case PLAT_OTP_ID_IAK:
+        return write_to_output(id, offsetof(struct flash_otp_nv_counters_region_t, iak), out_len, out);
+    case PLAT_OTP_ID_IAK_LEN:
+        return write_to_output(id, offsetof(struct flash_otp_nv_counters_region_t, iak_len), out_len, out);
+    case PLAT_OTP_ID_IAK_TYPE:
+        return write_to_output(id, offsetof(struct flash_otp_nv_counters_region_t, iak_type), out_len, out);
+    case PLAT_OTP_ID_IAK_ID:
+        return write_to_output(id, offsetof(struct flash_otp_nv_counters_region_t, iak_id), out_len, out);
 
-        case PLAT_OTP_ID_BOOT_SEED:
-            return write_to_output(id, offsetof(struct flash_otp_nv_counters_region_t, boot_seed), out_len, out);
-        case PLAT_OTP_ID_LCS:
-            return write_to_output(id, offsetof(struct flash_otp_nv_counters_region_t, lcs), out_len, out);
-        case PLAT_OTP_ID_IMPLEMENTATION_ID:
-            return write_to_output(id, offsetof(struct flash_otp_nv_counters_region_t, implementation_id), out_len, out);
-        case PLAT_OTP_ID_HW_VERSION:
-            return write_to_output(id, offsetof(struct flash_otp_nv_counters_region_t, hw_version), out_len, out);
-        case PLAT_OTP_ID_VERIFICATION_SERVICE_URL:
-            return write_to_output(id, offsetof(struct flash_otp_nv_counters_region_t, verification_service_url), out_len, out);
-        case PLAT_OTP_ID_PROFILE_DEFINITION:
-            return write_to_output(id, offsetof(struct flash_otp_nv_counters_region_t, profile_definition), out_len, out);
+    case PLAT_OTP_ID_BOOT_SEED:
+        return write_to_output(id, offsetof(struct flash_otp_nv_counters_region_t, boot_seed), out_len, out);
+    case PLAT_OTP_ID_LCS:
+        return write_to_output(id, offsetof(struct flash_otp_nv_counters_region_t, lcs), out_len, out);
+    case PLAT_OTP_ID_IMPLEMENTATION_ID:
+        return write_to_output(id, offsetof(struct flash_otp_nv_counters_region_t, implementation_id), out_len, out);
+    case PLAT_OTP_ID_HW_VERSION:
+        return write_to_output(id, offsetof(struct flash_otp_nv_counters_region_t, hw_version), out_len, out);
+    case PLAT_OTP_ID_VERIFICATION_SERVICE_URL:
+        return write_to_output(id, offsetof(struct flash_otp_nv_counters_region_t, verification_service_url), out_len, out);
+    case PLAT_OTP_ID_PROFILE_DEFINITION:
+        return write_to_output(id, offsetof(struct flash_otp_nv_counters_region_t, profile_definition), out_len, out);
 
 #ifdef BL2
-        case PLAT_OTP_ID_BL2_ROTPK_0:
-            return write_to_output(id, offsetof(struct flash_otp_nv_counters_region_t, bl2_rotpk_0), out_len, out);
-        case PLAT_OTP_ID_BL2_ROTPK_1:
-            return write_to_output(id, offsetof(struct flash_otp_nv_counters_region_t, bl2_rotpk_1), out_len, out);
-        case PLAT_OTP_ID_BL2_ROTPK_2:
-            return write_to_output(id, offsetof(struct flash_otp_nv_counters_region_t, bl2_rotpk_2), out_len, out);
-        case PLAT_OTP_ID_BL2_ROTPK_3:
-            return write_to_output(id, offsetof(struct flash_otp_nv_counters_region_t, bl2_rotpk_3), out_len, out);
+    case PLAT_OTP_ID_BL2_ROTPK_0:
+        return write_to_output(id, offsetof(struct flash_otp_nv_counters_region_t, bl2_rotpk_0), out_len, out);
+    case PLAT_OTP_ID_BL2_ROTPK_1:
+        return write_to_output(id, offsetof(struct flash_otp_nv_counters_region_t, bl2_rotpk_1), out_len, out);
+    case PLAT_OTP_ID_BL2_ROTPK_2:
+        return write_to_output(id, offsetof(struct flash_otp_nv_counters_region_t, bl2_rotpk_2), out_len, out);
+    case PLAT_OTP_ID_BL2_ROTPK_3:
+       return write_to_output(id, offsetof(struct flash_otp_nv_counters_region_t, bl2_rotpk_3), out_len, out);
 
-        case PLAT_OTP_ID_NV_COUNTER_BL2_0:
-            return write_to_output(id, offsetof(struct flash_otp_nv_counters_region_t, bl2_nv_counter_0), out_len, out);
-        case PLAT_OTP_ID_NV_COUNTER_BL2_1:
-            return write_to_output(id, offsetof(struct flash_otp_nv_counters_region_t, bl2_nv_counter_1), out_len, out);
-        case PLAT_OTP_ID_NV_COUNTER_BL2_2:
-            return write_to_output(id, offsetof(struct flash_otp_nv_counters_region_t, bl2_nv_counter_2), out_len, out);
-        case PLAT_OTP_ID_NV_COUNTER_BL2_3:
-            return write_to_output(id, offsetof(struct flash_otp_nv_counters_region_t, bl2_nv_counter_3), out_len, out);
+    case PLAT_OTP_ID_NV_COUNTER_BL2_0:
+        return write_to_output(id, offsetof(struct flash_otp_nv_counters_region_t, bl2_nv_counter_0), out_len, out);
+    case PLAT_OTP_ID_NV_COUNTER_BL2_1:
+        return write_to_output(id, offsetof(struct flash_otp_nv_counters_region_t, bl2_nv_counter_1), out_len, out);
+    case PLAT_OTP_ID_NV_COUNTER_BL2_2:
+        return write_to_output(id, offsetof(struct flash_otp_nv_counters_region_t, bl2_nv_counter_2), out_len, out);
+    case PLAT_OTP_ID_NV_COUNTER_BL2_3:
+        return write_to_output(id, offsetof(struct flash_otp_nv_counters_region_t, bl2_nv_counter_3), out_len, out);
 
 #endif /* BL2 */
 
 #ifdef BL1
-        case PLAT_OTP_ID_BL1_ROTPK_0:
-            return write_to_output(id, offsetof(struct flash_otp_nv_counters_region_t, bl1_rotpk_0), out_len, out);
+    case PLAT_OTP_ID_BL1_ROTPK_0:
+        return write_to_output(id, offsetof(struct flash_otp_nv_counters_region_t, bl1_rotpk_0), out_len, out);
 
-        case PLAT_OTP_ID_NV_COUNTER_BL1_0:
-            return write_to_output(id, offsetof(struct flash_otp_nv_counters_region_t, bl1_nv_counter_0), out_len, out);
+    case PLAT_OTP_ID_NV_COUNTER_BL1_0:
+        return write_to_output(id, offsetof(struct flash_otp_nv_counters_region_t, bl1_nv_counter_0), out_len, out);
 #endif /* BL1 */
 
-        case PLAT_OTP_ID_ENTROPY_SEED:
-            return write_to_output(id, offsetof(struct flash_otp_nv_counters_region_t, entropy_seed), out_len, out);
+#if (PLATFORM_NS_NV_COUNTERS > 0)
+    case PLAT_OTP_ID_NV_COUNTER_NS_0:
+        return write_to_output(id, offsetof(struct flash_otp_nv_counters_region_t, ns_nv_counter_0), out_len, out);
+#endif
+#if (PLATFORM_NS_NV_COUNTERS > 1)
+    case PLAT_OTP_ID_NV_COUNTER_NS_1:
+        return write_to_output(id, offsetof(struct flash_otp_nv_counters_region_t, ns_nv_counter_1), out_len, out);
+#endif
+#if (PLATFORM_NS_NV_COUNTERS > 2)
+    case PLAT_OTP_ID_NV_COUNTER_NS_2:
+        return write_to_output(id, offsetof(struct flash_otp_nv_counters_region_t, ns_nv_counter_2), out_len, out);
+#endif
 
-        case PLAT_OTP_ID_SECURE_DEBUG_PK:
-            return write_to_output(id, offsetof(struct flash_otp_nv_counters_region_t, secure_debug_pk), out_len, out);
+    case PLAT_OTP_ID_ENTROPY_SEED:
+        return write_to_output(id, offsetof(struct flash_otp_nv_counters_region_t, entropy_seed), out_len, out);
 
-        default:
-            return TFM_PLAT_ERR_UNSUPPORTED;
+    case PLAT_OTP_ID_SECURE_DEBUG_PK:
+        return write_to_output(id, offsetof(struct flash_otp_nv_counters_region_t, secure_debug_pk), out_len, out);
+
+    default:
+        return TFM_PLAT_ERR_UNSUPPORTED;
     }
 }
 
@@ -210,6 +223,19 @@
         return read_from_input(id, offsetof(struct flash_otp_nv_counters_region_t, bl1_nv_counter_0), in_len, in);
 #endif /* BL1 */
 
+#if (PLATFORM_NS_NV_COUNTERS > 0)
+    case PLAT_OTP_ID_NV_COUNTER_NS_0:
+        return read_from_input(id, offsetof(struct flash_otp_nv_counters_region_t, ns_nv_counter_0), in_len, in);
+#endif
+#if (PLATFORM_NS_NV_COUNTERS > 1)
+    case PLAT_OTP_ID_NV_COUNTER_NS_1:
+        return read_from_input(id, offsetof(struct flash_otp_nv_counters_region_t, ns_nv_counter_1), in_len, in);
+#endif
+#if (PLATFORM_NS_NV_COUNTERS > 2)
+    case PLAT_OTP_ID_NV_COUNTER_NS_2:
+        return read_from_input(id, offsetof(struct flash_otp_nv_counters_region_t, ns_nv_counter_2), in_len, in);
+#endif
+
     case PLAT_OTP_ID_ENTROPY_SEED:
         return read_from_input(id, offsetof(struct flash_otp_nv_counters_region_t, entropy_seed), in_len, in);
 
@@ -235,89 +261,105 @@
                                           size_t *size)
 {
     switch (id) {
-        case PLAT_OTP_ID_HUK:
-            *size = sizeof(((struct flash_otp_nv_counters_region_t*)0)->huk);
-            break;
-        case PLAT_OTP_ID_IAK:
-            *size = sizeof(((struct flash_otp_nv_counters_region_t*)0)->iak);
-            break;
-        case PLAT_OTP_ID_IAK_LEN:
-            *size = sizeof(((struct flash_otp_nv_counters_region_t*)0)->iak_len);
-            break;
-        case PLAT_OTP_ID_IAK_TYPE:
-            *size = sizeof(((struct flash_otp_nv_counters_region_t*)0)->iak_type);
-            break;
-        case PLAT_OTP_ID_IAK_ID:
-            *size = sizeof(((struct flash_otp_nv_counters_region_t*)0)->iak_id);
-            break;
+    case PLAT_OTP_ID_HUK:
+        *size = sizeof(((struct flash_otp_nv_counters_region_t*)0)->huk);
+        break;
+    case PLAT_OTP_ID_IAK:
+        *size = sizeof(((struct flash_otp_nv_counters_region_t*)0)->iak);
+        break;
+    case PLAT_OTP_ID_IAK_LEN:
+        *size = sizeof(((struct flash_otp_nv_counters_region_t*)0)->iak_len);
+        break;
+    case PLAT_OTP_ID_IAK_TYPE:
+        *size = sizeof(((struct flash_otp_nv_counters_region_t*)0)->iak_type);
+        break;
+    case PLAT_OTP_ID_IAK_ID:
+        *size = sizeof(((struct flash_otp_nv_counters_region_t*)0)->iak_id);
+        break;
 
-        case PLAT_OTP_ID_BOOT_SEED:
-            *size = sizeof(((struct flash_otp_nv_counters_region_t*)0)->boot_seed);
-            break;
-        case PLAT_OTP_ID_LCS:
-            *size = sizeof(((struct flash_otp_nv_counters_region_t*)0)->lcs);
-            break;
-        case PLAT_OTP_ID_IMPLEMENTATION_ID:
-            *size = sizeof(((struct flash_otp_nv_counters_region_t*)0)->implementation_id);
-            break;
-        case PLAT_OTP_ID_HW_VERSION:
-            *size = sizeof(((struct flash_otp_nv_counters_region_t*)0)->hw_version);
-            break;
-        case PLAT_OTP_ID_VERIFICATION_SERVICE_URL:
-            *size = sizeof(((struct flash_otp_nv_counters_region_t*)0)->verification_service_url);
-            break;
-        case PLAT_OTP_ID_PROFILE_DEFINITION:
-            *size = sizeof(((struct flash_otp_nv_counters_region_t*)0)->profile_definition);
-            break;
+    case PLAT_OTP_ID_BOOT_SEED:
+        *size = sizeof(((struct flash_otp_nv_counters_region_t*)0)->boot_seed);
+        break;
+    case PLAT_OTP_ID_LCS:
+        *size = sizeof(((struct flash_otp_nv_counters_region_t*)0)->lcs);
+        break;
+    case PLAT_OTP_ID_IMPLEMENTATION_ID:
+        *size = sizeof(((struct flash_otp_nv_counters_region_t*)0)->implementation_id);
+        break;
+    case PLAT_OTP_ID_HW_VERSION:
+        *size = sizeof(((struct flash_otp_nv_counters_region_t*)0)->hw_version);
+        break;
+    case PLAT_OTP_ID_VERIFICATION_SERVICE_URL:
+        *size = sizeof(((struct flash_otp_nv_counters_region_t*)0)->verification_service_url);
+        break;
+    case PLAT_OTP_ID_PROFILE_DEFINITION:
+        *size = sizeof(((struct flash_otp_nv_counters_region_t*)0)->profile_definition);
+        break;
 
 #ifdef BL2
-        case PLAT_OTP_ID_BL2_ROTPK_0:
-            *size = sizeof(((struct flash_otp_nv_counters_region_t*)0)->bl2_rotpk_0);
-            break;
-        case PLAT_OTP_ID_BL2_ROTPK_1:
-            *size = sizeof(((struct flash_otp_nv_counters_region_t*)0)->bl2_rotpk_1);
-            break;
-        case PLAT_OTP_ID_BL2_ROTPK_2:
-            *size = sizeof(((struct flash_otp_nv_counters_region_t*)0)->bl2_rotpk_2);
-            break;
-        case PLAT_OTP_ID_BL2_ROTPK_3:
-            *size = sizeof(((struct flash_otp_nv_counters_region_t*)0)->bl2_rotpk_3);
-            break;
+    case PLAT_OTP_ID_BL2_ROTPK_0:
+        *size = sizeof(((struct flash_otp_nv_counters_region_t*)0)->bl2_rotpk_0);
+        break;
+    case PLAT_OTP_ID_BL2_ROTPK_1:
+        *size = sizeof(((struct flash_otp_nv_counters_region_t*)0)->bl2_rotpk_1);
+        break;
+    case PLAT_OTP_ID_BL2_ROTPK_2:
+        *size = sizeof(((struct flash_otp_nv_counters_region_t*)0)->bl2_rotpk_2);
+        break;
+    case PLAT_OTP_ID_BL2_ROTPK_3:
+        *size = sizeof(((struct flash_otp_nv_counters_region_t*)0)->bl2_rotpk_3);
+        break;
 
-        case PLAT_OTP_ID_NV_COUNTER_BL2_0:
-            *size = sizeof(((struct flash_otp_nv_counters_region_t*)0)->bl2_nv_counter_0);
-            break;
-        case PLAT_OTP_ID_NV_COUNTER_BL2_1:
-            *size = sizeof(((struct flash_otp_nv_counters_region_t*)0)->bl2_nv_counter_1);
-            break;
-        case PLAT_OTP_ID_NV_COUNTER_BL2_2:
-            *size = sizeof(((struct flash_otp_nv_counters_region_t*)0)->bl2_nv_counter_2);
-            break;
-        case PLAT_OTP_ID_NV_COUNTER_BL2_3:
-            *size = sizeof(((struct flash_otp_nv_counters_region_t*)0)->bl2_nv_counter_3);
-            break;
+    case PLAT_OTP_ID_NV_COUNTER_BL2_0:
+        *size = sizeof(((struct flash_otp_nv_counters_region_t*)0)->bl2_nv_counter_0);
+        break;
+    case PLAT_OTP_ID_NV_COUNTER_BL2_1:
+        *size = sizeof(((struct flash_otp_nv_counters_region_t*)0)->bl2_nv_counter_1);
+        break;
+    case PLAT_OTP_ID_NV_COUNTER_BL2_2:
+        *size = sizeof(((struct flash_otp_nv_counters_region_t*)0)->bl2_nv_counter_2);
+        break;
+    case PLAT_OTP_ID_NV_COUNTER_BL2_3:
+        *size = sizeof(((struct flash_otp_nv_counters_region_t*)0)->bl2_nv_counter_3);
+        break;
 #endif /* BL2 */
 
 #ifdef BL1
-        case PLAT_OTP_ID_BL1_ROTPK_0:
-            *size = sizeof(((struct flash_otp_nv_counters_region_t*)0)->bl1_rotpk_0);
-            break;
+    case PLAT_OTP_ID_BL1_ROTPK_0:
+        *size = sizeof(((struct flash_otp_nv_counters_region_t*)0)->bl1_rotpk_0);
+        break;
 
-        case PLAT_OTP_ID_NV_COUNTER_BL1_0:
-            *size = sizeof(((struct flash_otp_nv_counters_region_t*)0)->bl1_nv_counter_0);
-            break;
+    case PLAT_OTP_ID_NV_COUNTER_BL1_0:
+        *size = sizeof(((struct flash_otp_nv_counters_region_t*)0)->bl1_nv_counter_0);
+        break;
 #endif /* BL1 */
 
-        case PLAT_OTP_ID_ENTROPY_SEED:
-            *size = sizeof(((struct flash_otp_nv_counters_region_t*)0)->entropy_seed);
-            break;
+#if (PLATFORM_NS_NV_COUNTERS > 0)
+    case PLAT_OTP_ID_NV_COUNTER_NS_0:
+        *size = sizeof(((struct flash_otp_nv_counters_region_t*)0)->ns_nv_counter_0);
+        break;
+#endif
+#if (PLATFORM_NS_NV_COUNTERS > 1)
+    case PLAT_OTP_ID_NV_COUNTER_NS_1:
+        *size = sizeof(((struct flash_otp_nv_counters_region_t*)0)->ns_nv_counter_1);
+        break;
+#endif
+#if (PLATFORM_NS_NV_COUNTERS > 2)
+    case PLAT_OTP_ID_NV_COUNTER_NS_2:
+        *size = sizeof(((struct flash_otp_nv_counters_region_t*)0)->ns_nv_counter_2);
+        break;
+#endif
 
-        case PLAT_OTP_ID_SECURE_DEBUG_PK:
-            *size = sizeof(((struct flash_otp_nv_counters_region_t*)0)->secure_debug_pk);
-            break;
+    case PLAT_OTP_ID_ENTROPY_SEED:
+        *size = sizeof(((struct flash_otp_nv_counters_region_t*)0)->entropy_seed);
+        break;
 
-        default:
-            return TFM_PLAT_ERR_UNSUPPORTED;
+    case PLAT_OTP_ID_SECURE_DEBUG_PK:
+        *size = sizeof(((struct flash_otp_nv_counters_region_t*)0)->secure_debug_pk);
+        break;
+
+    default:
+        return TFM_PLAT_ERR_UNSUPPORTED;
     }
 
     return TFM_PLAT_ERR_SUCCESS;
diff --git a/platform/include/tfm_plat_nv_counters.h b/platform/include/tfm_plat_nv_counters.h
index 1be2651..5a5da55 100644
--- a/platform/include/tfm_plat_nv_counters.h
+++ b/platform/include/tfm_plat_nv_counters.h
@@ -26,6 +26,10 @@
 #include <stdint.h>
 #include "tfm_plat_defs.h"
 
+#ifndef PLATFORM_NS_NV_COUNTERS
+#define PLATFORM_NS_NV_COUNTERS 0
+#endif
+
 enum tfm_nv_counter_t {
     PLAT_NV_COUNTER_PS_0 = 0,  /* Used by PS service */
     PLAT_NV_COUNTER_PS_1,      /* Used by PS service */
@@ -38,6 +42,11 @@
 
     PLAT_NV_COUNTER_BL1_0,     /* Used by bootloader */
 
+    /* NS counters must be contiguous */
+    PLAT_NV_COUNTER_NS_0,      /* Used by NS */
+    PLAT_NV_COUNTER_NS_1,      /* Used by NS */
+    PLAT_NV_COUNTER_NS_2,      /* Used by NS */
+
     PLAT_NV_COUNTER_MAX,
     PLAT_NV_COUNTER_BOUNDARY = UINT32_MAX  /* Fix  tfm_nv_counter_t size
                                               to 4 bytes */
diff --git a/platform/include/tfm_plat_otp.h b/platform/include/tfm_plat_otp.h
index 40d1afb..825614d 100644
--- a/platform/include/tfm_plat_otp.h
+++ b/platform/include/tfm_plat_otp.h
@@ -40,6 +40,10 @@
     PLAT_OTP_ID_NV_COUNTER_BL2_2,
     PLAT_OTP_ID_NV_COUNTER_BL2_3,
 
+    PLAT_OTP_ID_NV_COUNTER_NS_0,
+    PLAT_OTP_ID_NV_COUNTER_NS_1,
+    PLAT_OTP_ID_NV_COUNTER_NS_2,
+
     PLAT_OTP_ID_KEY_BL2_ENCRYPTION,
     PLAT_OTP_ID_BL1_2_IMAGE,
     PLAT_OTP_ID_BL1_2_IMAGE_HASH,
diff --git a/secure_fw/partitions/platform/platform_sp.c b/secure_fw/partitions/platform/platform_sp.c
index 4dfce2e..b0d60eb 100644
--- a/secure_fw/partitions/platform/platform_sp.c
+++ b/secure_fw/partitions/platform/platform_sp.c
@@ -57,6 +57,15 @@
             return TFM_PLATFORM_ERR_NOT_SUPPORTED;
         }
 #endif
+    case PLAT_NV_COUNTER_NS_0:
+    case PLAT_NV_COUNTER_NS_1:
+    case PLAT_NV_COUNTER_NS_2:
+        /* TODO how does this interact with the ns_ctx extension? */
+        if (client_id < 0) {
+            return TFM_PLATFORM_ERR_SUCCESS;
+        } else {
+            return TFM_PLATFORM_ERR_NOT_SUPPORTED;
+        }
     default:
         return TFM_PLATFORM_ERR_NOT_SUPPORTED;
     }
@@ -105,6 +114,9 @@
     if (status != (int32_t)TFM_SUCCESS) {
         return TFM_PLATFORM_ERR_SYSTEM_ERROR;
     }
+    if (client_id < 0) {
+        counter_id += PLAT_NV_COUNTER_NS_0;
+    }
 
     if (nv_counter_permissions_check(client_id, counter_id, true)
         != TFM_PLAT_ERR_SUCCESS) {
@@ -138,14 +150,17 @@
     }
 
     counter_id = *((enum tfm_nv_counter_t *)in_vec[0].base);
+    if (client_id < 0) {
+        counter_id += PLAT_NV_COUNTER_NS_0;
+    }
 
     if (nv_counter_permissions_check(client_id, counter_id, false)
         != TFM_PLAT_ERR_SUCCESS) {
-       return TFM_PLATFORM_ERR_SYSTEM_ERROR;
+        return TFM_PLATFORM_ERR_SYSTEM_ERROR;
     }
     err = tfm_plat_increment_nv_counter(counter_id);
     if (err != TFM_PLAT_ERR_SUCCESS) {
-       return TFM_PLATFORM_ERR_SYSTEM_ERROR;
+        return TFM_PLATFORM_ERR_SYSTEM_ERROR;
     }
 
     return TFM_PLATFORM_ERR_SUCCESS;
@@ -187,11 +202,14 @@
         }
 
         num = psa_read(msg->handle, 0, &counter_id, msg->in_size[0]);
-
         if (num != msg->in_size[0]) {
             return TFM_PLATFORM_ERR_SYSTEM_ERROR;
         }
 
+        if (msg->client_id < 0) {
+            counter_id += PLAT_NV_COUNTER_NS_0;
+        }
+
         if (nv_counter_permissions_check(msg->client_id, counter_id, false)
             != TFM_PLATFORM_ERR_SUCCESS) {
            return TFM_PLATFORM_ERR_SYSTEM_ERROR;
@@ -210,6 +228,10 @@
             return TFM_PLATFORM_ERR_SYSTEM_ERROR;
         }
 
+        if (msg->client_id < 0) {
+            counter_id += PLAT_NV_COUNTER_NS_0;
+        }
+
         if (nv_counter_permissions_check(msg->client_id, counter_id, true)
             != TFM_PLATFORM_ERR_SUCCESS) {
            return TFM_PLATFORM_ERR_SYSTEM_ERROR;
diff --git a/secure_fw/partitions/platform/tfm_platform.yaml b/secure_fw/partitions/platform/tfm_platform.yaml
index 2efa02b..a0d3972 100644
--- a/secure_fw/partitions/platform/tfm_platform.yaml
+++ b/secure_fw/partitions/platform/tfm_platform.yaml
@@ -1,5 +1,5 @@
 #-------------------------------------------------------------------------------
-# Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+# Copyright (c) 2018-2022, Arm Limited. All rights reserved.
 #
 # SPDX-License-Identifier: BSD-3-Clause
 #
@@ -30,7 +30,7 @@
      {
        "name": "TFM_SP_PLATFORM_NV_COUNTER",
        "sid": "0x00000042",
-       "non_secure_clients": false,
+       "non_secure_clients": true,
        "version": 1,
        "version_policy": "STRICT"
      }
@@ -55,14 +55,14 @@
      {
        "name": "TFM_SP_PLATFORM_NV_COUNTER_READ",
        "signal": "PLATFORM_SP_NV_COUNTER_READ",
-       "non_secure_clients": false,
+       "non_secure_clients": true,
        "minor_version": 1,
        "minor_policy": "STRICT"
       },
       {
         "name": "TFM_SP_PLATFORM_NV_COUNTER_INCREMENT",
         "signal": "PLATFORM_SP_NV_COUNTER_INCREMENT",
-        "non_secure_clients": false,
+        "non_secure_clients": true,
         "minor_version": 1,
         "minor_policy": "STRICT"
        }