Platform: Extend NV counter interface
This patch adds the tfm_plat_set_nv_counter() function to the NV counter
interface for supported platforms: AN521, AN519, Musca-A and Musca-B1.
To set a NV counter to a specified value can take a lot of time and
leads to early flash wear out when incrementing the counter by one.
Change-Id: I8702de54d373aff3201669540c541a24815cf09b
Signed-off-by: David Vincze <david.vincze@arm.com>
diff --git a/platform/ext/target/mps2/an519/dummy_nv_counters.c b/platform/ext/target/mps2/an519/dummy_nv_counters.c
index 6f8ea62..8eda9bd 100644
--- a/platform/ext/target/mps2/an519/dummy_nv_counters.c
+++ b/platform/ext/target/mps2/an519/dummy_nv_counters.c
@@ -1,18 +1,20 @@
/*
- * Copyright (c) 2018, Arm Limited. All rights reserved.
+ * Copyright (c) 2018-2019, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
-/* NOTE: This API should be implemented by platform vendor. For the
- * security of the secure storage system rollback protection and others, it is
- * CRITICAL to use a internal (in-die) persistent memory for multiple time
- * programabe (MTP) non-volatile counters or use a One-time Programmable (OTP)
+/* NOTE: This API should be implemented by platform vendor. For the security of
+ * the secure storage system's and the bootloader's rollback protection etc. it
+ * is CRITICAL to use a internal (in-die) persistent memory for multiple time
+ * programmable (MTP) non-volatile counters or use a One-time Programmable (OTP)
* non-volatile counters solution.
*
* AN519 does not have any available MTP or OTP non-volatile counters, so a
- * software dummy implementation has been implemented in this case.
+ * software dummy implementation has been implemented in this case. The current
+ * implementation is not resistant to asynchronous power failures and should
+ * not be used in production code. It is exclusively for testing purposes.
*/
#include "platform/include/tfm_plat_nv_counters.h"
@@ -123,8 +125,8 @@
return TFM_PLAT_ERR_SUCCESS;
}
-enum tfm_plat_err_t tfm_plat_increment_nv_counter(
- enum tfm_nv_counter_t counter_id)
+enum tfm_plat_err_t tfm_plat_set_nv_counter(enum tfm_nv_counter_t counter_id,
+ uint32_t value)
{
int32_t err;
uint32_t *p_nv_counter;
@@ -141,25 +143,48 @@
p_nv_counter = (uint32_t *)(sector_data + NV_COUNTERS_AREA_OFFSET +
(counter_id * NV_COUNTER_SIZE));
- if (*p_nv_counter == UINT32_MAX) {
- return TFM_PLAT_ERR_MAX_VALUE;
- }
+ if (value != *p_nv_counter) {
- /* Next value is the current value + 1 */
- *p_nv_counter = *p_nv_counter + 1;
+ if (value > *p_nv_counter) {
+ *p_nv_counter = value;
+ } else {
+ return TFM_PLAT_ERR_INVALID_INPUT;
+ }
- /* Erase sector before write in it */
- err = FLASH_DEV_NAME.EraseSector(TFM_NV_COUNTERS_SECTOR_ADDR);
- if (err != ARM_DRIVER_OK) {
- return TFM_PLAT_ERR_SYSTEM_ERR;
- }
+ /* Erase sector before write in it */
+ err = FLASH_DEV_NAME.EraseSector(TFM_NV_COUNTERS_SECTOR_ADDR);
+ if (err != ARM_DRIVER_OK) {
+ return TFM_PLAT_ERR_SYSTEM_ERR;
+ }
- /* Write in flash the in-memory block content after modification */
- err = FLASH_DEV_NAME.ProgramData(TFM_NV_COUNTERS_SECTOR_ADDR, sector_data,
- TFM_NV_COUNTERS_SECTOR_SIZE);
- if (err != ARM_DRIVER_OK) {
- return TFM_PLAT_ERR_SYSTEM_ERR;
+ /* Write in flash the in-memory block content after modification */
+ err = FLASH_DEV_NAME.ProgramData(TFM_NV_COUNTERS_SECTOR_ADDR,
+ sector_data,
+ TFM_NV_COUNTERS_SECTOR_SIZE);
+ if (err != ARM_DRIVER_OK) {
+ return TFM_PLAT_ERR_SYSTEM_ERR;
+ }
}
return TFM_PLAT_ERR_SUCCESS;
}
+
+enum tfm_plat_err_t tfm_plat_increment_nv_counter(
+ enum tfm_nv_counter_t counter_id)
+{
+ uint32_t security_cnt;
+ enum tfm_plat_err_t err;
+
+ err = tfm_plat_read_nv_counter(counter_id,
+ sizeof(security_cnt),
+ (uint8_t *)&security_cnt);
+ if (err != TFM_PLAT_ERR_SUCCESS) {
+ return err;
+ }
+
+ if (security_cnt == UINT32_MAX) {
+ return TFM_PLAT_ERR_MAX_VALUE;
+ }
+
+ return tfm_plat_set_nv_counter(counter_id, security_cnt + 1u);
+}
diff --git a/platform/ext/target/mps2/an521/dummy_nv_counters.c b/platform/ext/target/mps2/an521/dummy_nv_counters.c
index e82cd7c..b144994 100644
--- a/platform/ext/target/mps2/an521/dummy_nv_counters.c
+++ b/platform/ext/target/mps2/an521/dummy_nv_counters.c
@@ -1,18 +1,20 @@
/*
- * Copyright (c) 2018, Arm Limited. All rights reserved.
+ * Copyright (c) 2018-2019, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
-/* NOTE: This API should be implemented by platform vendor. For the
- * security of the secure storage system rollback protection and others, it is
- * CRITICAL to use a internal (in-die) persistent memory for multiple time
- * programabe (MTP) non-volatile counters or use a One-time Programmable (OTP)
+/* NOTE: This API should be implemented by platform vendor. For the security of
+ * the secure storage system's and the bootloader's rollback protection etc. it
+ * is CRITICAL to use a internal (in-die) persistent memory for multiple time
+ * programmable (MTP) non-volatile counters or use a One-time Programmable (OTP)
* non-volatile counters solution.
*
* AN521 does not have any available MTP or OTP non-volatile counters, so a
- * software dummy implementation has been implemented in this case.
+ * software dummy implementation has been implemented in this case. The current
+ * implementation is not resistant to asynchronous power failures and should
+ * not be used in production code. It is exclusively for testing purposes.
*/
#include "platform/include/tfm_plat_nv_counters.h"
@@ -123,8 +125,8 @@
return TFM_PLAT_ERR_SUCCESS;
}
-enum tfm_plat_err_t tfm_plat_increment_nv_counter(
- enum tfm_nv_counter_t counter_id)
+enum tfm_plat_err_t tfm_plat_set_nv_counter(enum tfm_nv_counter_t counter_id,
+ uint32_t value)
{
int32_t err;
uint32_t *p_nv_counter;
@@ -141,25 +143,48 @@
p_nv_counter = (uint32_t *)(sector_data + NV_COUNTERS_AREA_OFFSET +
(counter_id * NV_COUNTER_SIZE));
- if (*p_nv_counter == UINT32_MAX) {
- return TFM_PLAT_ERR_MAX_VALUE;
- }
+ if (value != *p_nv_counter) {
- /* Next value is the current value + 1 */
- *p_nv_counter = *p_nv_counter + 1;
+ if (value > *p_nv_counter) {
+ *p_nv_counter = value;
+ } else {
+ return TFM_PLAT_ERR_INVALID_INPUT;
+ }
- /* Erase sector before write in it */
- err = FLASH_DEV_NAME.EraseSector(TFM_NV_COUNTERS_SECTOR_ADDR);
- if (err != ARM_DRIVER_OK) {
- return TFM_PLAT_ERR_SYSTEM_ERR;
- }
+ /* Erase sector before write in it */
+ err = FLASH_DEV_NAME.EraseSector(TFM_NV_COUNTERS_SECTOR_ADDR);
+ if (err != ARM_DRIVER_OK) {
+ return TFM_PLAT_ERR_SYSTEM_ERR;
+ }
- /* Write in flash the in-memory block content after modification */
- err = FLASH_DEV_NAME.ProgramData(TFM_NV_COUNTERS_SECTOR_ADDR, sector_data,
- TFM_NV_COUNTERS_SECTOR_SIZE);
- if (err != ARM_DRIVER_OK) {
- return TFM_PLAT_ERR_SYSTEM_ERR;
+ /* Write in flash the in-memory block content after modification */
+ err = FLASH_DEV_NAME.ProgramData(TFM_NV_COUNTERS_SECTOR_ADDR,
+ sector_data,
+ TFM_NV_COUNTERS_SECTOR_SIZE);
+ if (err != ARM_DRIVER_OK) {
+ return TFM_PLAT_ERR_SYSTEM_ERR;
+ }
}
return TFM_PLAT_ERR_SUCCESS;
}
+
+enum tfm_plat_err_t tfm_plat_increment_nv_counter(
+ enum tfm_nv_counter_t counter_id)
+{
+ uint32_t security_cnt;
+ enum tfm_plat_err_t err;
+
+ err = tfm_plat_read_nv_counter(counter_id,
+ sizeof(security_cnt),
+ (uint8_t *)&security_cnt);
+ if (err != TFM_PLAT_ERR_SUCCESS) {
+ return err;
+ }
+
+ if (security_cnt == UINT32_MAX) {
+ return TFM_PLAT_ERR_MAX_VALUE;
+ }
+
+ return tfm_plat_set_nv_counter(counter_id, security_cnt + 1u);
+}
diff --git a/platform/ext/target/musca_a/dummy_nv_counters.c b/platform/ext/target/musca_a/dummy_nv_counters.c
index 96c6a23..2703582 100644
--- a/platform/ext/target/musca_a/dummy_nv_counters.c
+++ b/platform/ext/target/musca_a/dummy_nv_counters.c
@@ -5,14 +5,16 @@
*
*/
-/* NOTE: This API should be implemented by platform vendor. For the
- * security of the secure storage system rollback protection and others, it is
- * CRITICAL to use a internal (in-die) persistent memory for multiple time
- * programabe (MTP) non-volatile counters or use a One-time Programmable (OTP)
+/* NOTE: This API should be implemented by platform vendor. For the security of
+ * the secure storage system's and the bootloader's rollback protection etc. it
+ * is CRITICAL to use a internal (in-die) persistent memory for multiple time
+ * programmable (MTP) non-volatile counters or use a One-time Programmable (OTP)
* non-volatile counters solution.
*
* Musca-A does not have any available MTP or OTP non-volatile counters, so a
- * software dummy implementation has been implemented in this case.
+ * software dummy implementation has been implemented in this case. The current
+ * implementation is not resistant to asynchronous power failures and should
+ * not be used in production code. It is exclusively for testing purposes.
*/
#include "platform/include/tfm_plat_nv_counters.h"
@@ -123,8 +125,8 @@
return TFM_PLAT_ERR_SUCCESS;
}
-enum tfm_plat_err_t tfm_plat_increment_nv_counter(
- enum tfm_nv_counter_t counter_id)
+enum tfm_plat_err_t tfm_plat_set_nv_counter(enum tfm_nv_counter_t counter_id,
+ uint32_t value)
{
int32_t err;
uint32_t *p_nv_counter;
@@ -141,25 +143,48 @@
p_nv_counter = (uint32_t *)(sector_data + NV_COUNTERS_AREA_OFFSET +
(counter_id * NV_COUNTER_SIZE));
- if (*p_nv_counter == UINT32_MAX) {
- return TFM_PLAT_ERR_MAX_VALUE;
- }
+ if (value != *p_nv_counter) {
- /* Next value is the current value + 1 */
- *p_nv_counter = *p_nv_counter + 1;
+ if (value > *p_nv_counter) {
+ *p_nv_counter = value;
+ } else {
+ return TFM_PLAT_ERR_INVALID_INPUT;
+ }
- /* Erase sector before write in it */
- err = FLASH_DEV_NAME.EraseSector(TFM_NV_COUNTERS_SECTOR_ADDR);
- if (err != ARM_DRIVER_OK) {
- return TFM_PLAT_ERR_SYSTEM_ERR;
- }
+ /* Erase sector before write in it */
+ err = FLASH_DEV_NAME.EraseSector(TFM_NV_COUNTERS_SECTOR_ADDR);
+ if (err != ARM_DRIVER_OK) {
+ return TFM_PLAT_ERR_SYSTEM_ERR;
+ }
- /* Write in flash the in-memory block content after modification */
- err = FLASH_DEV_NAME.ProgramData(TFM_NV_COUNTERS_SECTOR_ADDR, sector_data,
- TFM_NV_COUNTERS_SECTOR_SIZE);
- if (err != ARM_DRIVER_OK) {
- return TFM_PLAT_ERR_SYSTEM_ERR;
+ /* Write in flash the in-memory block content after modification */
+ err = FLASH_DEV_NAME.ProgramData(TFM_NV_COUNTERS_SECTOR_ADDR,
+ sector_data,
+ TFM_NV_COUNTERS_SECTOR_SIZE);
+ if (err != ARM_DRIVER_OK) {
+ return TFM_PLAT_ERR_SYSTEM_ERR;
+ }
}
return TFM_PLAT_ERR_SUCCESS;
}
+
+enum tfm_plat_err_t tfm_plat_increment_nv_counter(
+ enum tfm_nv_counter_t counter_id)
+{
+ uint32_t security_cnt;
+ enum tfm_plat_err_t err;
+
+ err = tfm_plat_read_nv_counter(counter_id,
+ sizeof(security_cnt),
+ (uint8_t *)&security_cnt);
+ if (err != TFM_PLAT_ERR_SUCCESS) {
+ return err;
+ }
+
+ if (security_cnt == UINT32_MAX) {
+ return TFM_PLAT_ERR_MAX_VALUE;
+ }
+
+ return tfm_plat_set_nv_counter(counter_id, security_cnt + 1u);
+}
diff --git a/platform/ext/target/musca_b1/dummy_nv_counters.c b/platform/ext/target/musca_b1/dummy_nv_counters.c
index 17c1c4f..a935959 100644
--- a/platform/ext/target/musca_b1/dummy_nv_counters.c
+++ b/platform/ext/target/musca_b1/dummy_nv_counters.c
@@ -1,15 +1,19 @@
/*
- * Copyright (c) 2018, Arm Limited. All rights reserved.
+ * Copyright (c) 2018-2019, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
-/* NOTE: This API should be implemented by platform vendor. For the
- * security of the secure storage system rollback protection and others, it is
- * CRITICAL to use a internal (in-die) persistent memory for multiple time
- * programabe (MTP) non-volatile counters or use a One-time Programmable (OTP)
+/* NOTE: This API should be implemented by platform vendor. For the security of
+ * the secure storage system's and the bootloader's rollback protection etc. it
+ * is CRITICAL to use a internal (in-die) persistent memory for multiple time
+ * programmable (MTP) non-volatile counters or use a One-time Programmable (OTP)
* non-volatile counters solution.
+ *
+ * The current software dummy implementation is not resistant to asynchronous
+ * power failures and should not be used in production code. It is exclusively
+ * for testing purposes.
*/
#include "platform/include/tfm_plat_nv_counters.h"
@@ -120,8 +124,8 @@
return TFM_PLAT_ERR_SUCCESS;
}
-enum tfm_plat_err_t tfm_plat_increment_nv_counter(
- enum tfm_nv_counter_t counter_id)
+enum tfm_plat_err_t tfm_plat_set_nv_counter(enum tfm_nv_counter_t counter_id,
+ uint32_t value)
{
int32_t err;
uint32_t *p_nv_counter;
@@ -138,25 +142,48 @@
p_nv_counter = (uint32_t *)(sector_data + NV_COUNTERS_AREA_OFFSET +
(counter_id * NV_COUNTER_SIZE));
- if (*p_nv_counter == UINT32_MAX) {
- return TFM_PLAT_ERR_MAX_VALUE;
- }
+ if (value != *p_nv_counter) {
- /* Next value is the current value + 1 */
- *p_nv_counter = *p_nv_counter + 1;
+ if (value > *p_nv_counter) {
+ *p_nv_counter = value;
+ } else {
+ return TFM_PLAT_ERR_INVALID_INPUT;
+ }
- /* Erase sector before write in it */
- err = FLASH_DEV_NAME.EraseSector(TFM_NV_COUNTERS_SECTOR_ADDR);
- if (err != ARM_DRIVER_OK) {
- return TFM_PLAT_ERR_SYSTEM_ERR;
- }
+ /* Erase sector before write in it */
+ err = FLASH_DEV_NAME.EraseSector(TFM_NV_COUNTERS_SECTOR_ADDR);
+ if (err != ARM_DRIVER_OK) {
+ return TFM_PLAT_ERR_SYSTEM_ERR;
+ }
- /* Write in flash the in-memory block content after modification */
- err = FLASH_DEV_NAME.ProgramData(TFM_NV_COUNTERS_SECTOR_ADDR, sector_data,
- TFM_NV_COUNTERS_SECTOR_SIZE);
- if (err != ARM_DRIVER_OK) {
- return TFM_PLAT_ERR_SYSTEM_ERR;
+ /* Write in flash the in-memory block content after modification */
+ err = FLASH_DEV_NAME.ProgramData(TFM_NV_COUNTERS_SECTOR_ADDR,
+ sector_data,
+ TFM_NV_COUNTERS_SECTOR_SIZE);
+ if (err != ARM_DRIVER_OK) {
+ return TFM_PLAT_ERR_SYSTEM_ERR;
+ }
}
return TFM_PLAT_ERR_SUCCESS;
}
+
+enum tfm_plat_err_t tfm_plat_increment_nv_counter(
+ enum tfm_nv_counter_t counter_id)
+{
+ uint32_t security_cnt;
+ enum tfm_plat_err_t err;
+
+ err = tfm_plat_read_nv_counter(counter_id,
+ sizeof(security_cnt),
+ (uint8_t *)&security_cnt);
+ if (err != TFM_PLAT_ERR_SUCCESS) {
+ return err;
+ }
+
+ if (security_cnt == UINT32_MAX) {
+ return TFM_PLAT_ERR_MAX_VALUE;
+ }
+
+ return tfm_plat_set_nv_counter(counter_id, security_cnt + 1u);
+}
diff --git a/platform/include/tfm_plat_defs.h b/platform/include/tfm_plat_defs.h
index f0b4297..ffe61f6 100644
--- a/platform/include/tfm_plat_defs.h
+++ b/platform/include/tfm_plat_defs.h
@@ -19,6 +19,8 @@
TFM_PLAT_ERR_SUCCESS = 0,
TFM_PLAT_ERR_SYSTEM_ERR,
TFM_PLAT_ERR_MAX_VALUE,
+ TFM_PLAT_ERR_INVALID_INPUT,
+ TFM_PLAT_ERR_UNSUPPORTED,
/* Following entry is only to ensure the error code of int size */
TFM_PLAT_ERR_FORCE_INT_SIZE = INT_MAX
};
diff --git a/platform/include/tfm_plat_nv_counters.h b/platform/include/tfm_plat_nv_counters.h
index d79c235..9b86112 100644
--- a/platform/include/tfm_plat_nv_counters.h
+++ b/platform/include/tfm_plat_nv_counters.h
@@ -13,6 +13,14 @@
*
* \note The interfaces defined in this file must be implemented for each
* SoC.
+ * \note The interface must be implemented in a fail-safe way that is
+ * resistant to asynchronous power failures or it can use hardware
+ * counters that have this capability, if supported by the platform.
+ * When a counter incrementation was interrupted it must be able to
+ * continue the incrementation process or recover the previous consistent
+ * status of the counters. If the counters have reached a stable status
+ * (every counter incrementation operation has finished), from that point
+ * their value cannot decrease due to any kind of power failure.
*/
#include <stdint.h>
@@ -64,6 +72,31 @@
enum tfm_plat_err_t tfm_plat_increment_nv_counter(
enum tfm_nv_counter_t counter_id);
+/**
+ * \brief Sets the given non-volatile (NV) counter to the specified value.
+ *
+ * \param[in] counter_id NV counter ID.
+ * \param[in] value New value of the NV counter. The maximum value that
+ * can be set depends on the constraints of the
+ * underlying implementation, but it always must be
+ * greater than or equal to the current NV counter value.
+ *
+ * \retval TFM_PLAT_ERR_SUCCESS The NV counter is set successfully
+ * \retval TFM_PLAT_ERR_INVALID_INPUT The new value is less than the current
+ * counter value
+ * \retval TFM_PLAT_ERR_MAX_VALUE The new value is greater than the
+ * maximum value of the NV counter
+ * \retval TFM_PLAT_ERR_UNSUPPORTED The function is not implemented for
+ * the given platform or the new value is
+ * not representable on the underlying
+ * counter implementation
+ * \retval TFM_PLAT_ERR_SYSTEM_ERR An unspecified error occurred
+ * (none of the other standard error codes
+ * are applicable)
+ */
+enum tfm_plat_err_t tfm_plat_set_nv_counter(enum tfm_nv_counter_t counter_id,
+ uint32_t value);
+
#ifdef __cplusplus
}
#endif