blob: 5149d0e5dd0678f4775476c0382cbeffd24c41c8 [file] [log] [blame]
/*
* Copyright (c) 2018-2020, 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 protected 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.
*
* This dummy implementation assumes that the NV counters are the only data in
* the flash sector. To use it, one flash sector should be allocated exclusively
* for the NV counters.
*
* 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 "tfm_plat_nv_counters.h"
#include <limits.h>
#include "Driver_Flash.h"
#include "flash_layout.h"
/* Compilation time checks to be sure the defines are well defined */
#ifndef TFM_NV_COUNTERS_AREA_ADDR
#error "TFM_NV_COUNTERS_AREA_ADDR must be defined in flash_layout.h"
#endif
#ifndef TFM_NV_COUNTERS_AREA_SIZE
#error "TFM_NV_COUNTERS_AREA_SIZE must be defined in flash_layout.h"
#endif
#ifndef TFM_NV_COUNTERS_SECTOR_ADDR
#error "TFM_NV_COUNTERS_SECTOR_ADDR must be defined in flash_layout.h"
#endif
#ifndef TFM_NV_COUNTERS_SECTOR_SIZE
#error "TFM_NV_COUNTERS_SECTOR_SIZE must be defined in flash_layout.h"
#endif
#ifndef FLASH_DEV_NAME
#error "FLASH_DEV_NAME must be defined in flash_layout.h"
#endif
/* End of compilation time checks to be sure the defines are well defined */
#define NV_COUNTER_SIZE sizeof(uint32_t)
#define INIT_VALUE_SIZE NV_COUNTER_SIZE
#define NUM_NV_COUNTERS ((TFM_NV_COUNTERS_AREA_SIZE - INIT_VALUE_SIZE) \
/ NV_COUNTER_SIZE)
#define NV_COUNTERS_INITIALIZED 0xC0DE0042U
/**
* \brief Struct representing the NV counter data in flash.
*/
struct nv_counters_t {
uint32_t counters[NUM_NV_COUNTERS]; /**< Array of NV counters */
uint32_t init_value; /**< Watermark to indicate if the NV counters have been
* initialised
*/
};
/* Import the CMSIS flash device driver */
extern ARM_DRIVER_FLASH FLASH_DEV_NAME;
enum tfm_plat_err_t tfm_plat_init_nv_counter(void)
{
int32_t err;
uint32_t i;
struct nv_counters_t nv_counters = {{0}};
err = FLASH_DEV_NAME.Initialize(NULL);
if (err != ARM_DRIVER_OK) {
return TFM_PLAT_ERR_SYSTEM_ERR;
}
/* Read the NV counter area to be able to erase the sector and write later
* in the flash.
*/
err = FLASH_DEV_NAME.ReadData(TFM_NV_COUNTERS_AREA_ADDR, &nv_counters,
TFM_NV_COUNTERS_AREA_SIZE);
if (err != ARM_DRIVER_OK) {
return TFM_PLAT_ERR_SYSTEM_ERR;
}
if (nv_counters.init_value == NV_COUNTERS_INITIALIZED) {
return TFM_PLAT_ERR_SUCCESS;
}
/* Add watermark, at the end of the NV counters area, to indicate that NV
* counters have been initialized.
*/
nv_counters.init_value = NV_COUNTERS_INITIALIZED;
/* Initialize all counters to 0 */
for (i = 0; i < NUM_NV_COUNTERS; i++) {
nv_counters.counters[i] = 0;
}
/* 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 NV counter content after modification */
err = FLASH_DEV_NAME.ProgramData(TFM_NV_COUNTERS_AREA_ADDR, &nv_counters,
TFM_NV_COUNTERS_AREA_SIZE);
if (err != ARM_DRIVER_OK) {
return TFM_PLAT_ERR_SYSTEM_ERR;
}
return TFM_PLAT_ERR_SUCCESS;
}
enum tfm_plat_err_t tfm_plat_read_nv_counter(enum tfm_nv_counter_t counter_id,
uint32_t size, uint8_t *val)
{
int32_t err;
uint32_t flash_addr;
if (size != NV_COUNTER_SIZE) {
return TFM_PLAT_ERR_SYSTEM_ERR;
}
flash_addr = TFM_NV_COUNTERS_AREA_ADDR + (counter_id * NV_COUNTER_SIZE);
err = FLASH_DEV_NAME.ReadData(flash_addr, val, NV_COUNTER_SIZE);
if (err != ARM_DRIVER_OK) {
return TFM_PLAT_ERR_SYSTEM_ERR;
}
return TFM_PLAT_ERR_SUCCESS;
}
enum tfm_plat_err_t tfm_plat_set_nv_counter(enum tfm_nv_counter_t counter_id,
uint32_t value)
{
int32_t err;
struct nv_counters_t nv_counters = {{0}};
/* Read the NV counter area to be able to erase the sector and write later
* in the flash.
*/
err = FLASH_DEV_NAME.ReadData(TFM_NV_COUNTERS_AREA_ADDR, &nv_counters,
TFM_NV_COUNTERS_AREA_SIZE);
if (err != ARM_DRIVER_OK) {
return TFM_PLAT_ERR_SYSTEM_ERR;
}
if (value != nv_counters.counters[counter_id]) {
if (value > nv_counters.counters[counter_id]) {
nv_counters.counters[counter_id] = 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;
}
/* Write in flash the in-memory NV counter content after modification */
err = FLASH_DEV_NAME.ProgramData(TFM_NV_COUNTERS_AREA_ADDR,
&nv_counters,
TFM_NV_COUNTERS_AREA_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);
}