Jamie Fox | 7a4170d | 2018-08-15 14:13:42 +0100 | [diff] [blame] | 1 | /* |
Mingyang Sun | c9bdcd7 | 2020-06-04 11:44:49 +0800 | [diff] [blame] | 2 | * Copyright (c) 2018-2020, Arm Limited. All rights reserved. |
Jamie Fox | 7a4170d | 2018-08-15 14:13:42 +0100 | [diff] [blame] | 3 | * |
| 4 | * SPDX-License-Identifier: BSD-3-Clause |
| 5 | * |
| 6 | */ |
| 7 | |
David Vincze | 2ff8c6a | 2019-05-21 11:53:00 +0200 | [diff] [blame] | 8 | /* NOTE: This API should be implemented by platform vendor. For the security of |
Kevin Peng | c6d7450 | 2020-03-04 16:55:37 +0800 | [diff] [blame^] | 9 | * the protected storage system's and the bootloader's rollback protection etc. |
| 10 | * it is CRITICAL to use a internal (in-die) persistent memory for multiple time |
David Vincze | 2ff8c6a | 2019-05-21 11:53:00 +0200 | [diff] [blame] | 11 | * programmable (MTP) non-volatile counters or use a One-time Programmable (OTP) |
Jamie Fox | 7a4170d | 2018-08-15 14:13:42 +0100 | [diff] [blame] | 12 | * non-volatile counters solution. |
David Vincze | 2ff8c6a | 2019-05-21 11:53:00 +0200 | [diff] [blame] | 13 | * |
Jamie Fox | a504c2c | 2019-08-29 17:39:09 +0100 | [diff] [blame] | 14 | * This dummy implementation assumes that the NV counters are the only data in |
| 15 | * the flash sector. To use it, one flash sector should be allocated exclusively |
| 16 | * for the NV counters. |
| 17 | * |
David Vincze | 2ff8c6a | 2019-05-21 11:53:00 +0200 | [diff] [blame] | 18 | * The current software dummy implementation is not resistant to asynchronous |
| 19 | * power failures and should not be used in production code. It is exclusively |
| 20 | * for testing purposes. |
Jamie Fox | 7a4170d | 2018-08-15 14:13:42 +0100 | [diff] [blame] | 21 | */ |
| 22 | |
Mingyang Sun | c9bdcd7 | 2020-06-04 11:44:49 +0800 | [diff] [blame] | 23 | #include "tfm_plat_nv_counters.h" |
Jamie Fox | 7a4170d | 2018-08-15 14:13:42 +0100 | [diff] [blame] | 24 | |
| 25 | #include <limits.h> |
| 26 | #include "Driver_Flash.h" |
| 27 | #include "flash_layout.h" |
| 28 | |
| 29 | /* Compilation time checks to be sure the defines are well defined */ |
| 30 | #ifndef TFM_NV_COUNTERS_AREA_ADDR |
| 31 | #error "TFM_NV_COUNTERS_AREA_ADDR must be defined in flash_layout.h" |
| 32 | #endif |
| 33 | |
| 34 | #ifndef TFM_NV_COUNTERS_AREA_SIZE |
| 35 | #error "TFM_NV_COUNTERS_AREA_SIZE must be defined in flash_layout.h" |
| 36 | #endif |
| 37 | |
| 38 | #ifndef TFM_NV_COUNTERS_SECTOR_ADDR |
| 39 | #error "TFM_NV_COUNTERS_SECTOR_ADDR must be defined in flash_layout.h" |
| 40 | #endif |
| 41 | |
| 42 | #ifndef TFM_NV_COUNTERS_SECTOR_SIZE |
| 43 | #error "TFM_NV_COUNTERS_SECTOR_SIZE must be defined in flash_layout.h" |
| 44 | #endif |
| 45 | |
| 46 | #ifndef FLASH_DEV_NAME |
| 47 | #error "FLASH_DEV_NAME must be defined in flash_layout.h" |
| 48 | #endif |
| 49 | /* End of compilation time checks to be sure the defines are well defined */ |
| 50 | |
Jamie Fox | 7a4170d | 2018-08-15 14:13:42 +0100 | [diff] [blame] | 51 | #define NV_COUNTER_SIZE sizeof(uint32_t) |
| 52 | #define INIT_VALUE_SIZE NV_COUNTER_SIZE |
Jamie Fox | a504c2c | 2019-08-29 17:39:09 +0100 | [diff] [blame] | 53 | #define NUM_NV_COUNTERS ((TFM_NV_COUNTERS_AREA_SIZE - INIT_VALUE_SIZE) \ |
| 54 | / NV_COUNTER_SIZE) |
Jamie Fox | 7a4170d | 2018-08-15 14:13:42 +0100 | [diff] [blame] | 55 | |
Jamie Fox | a504c2c | 2019-08-29 17:39:09 +0100 | [diff] [blame] | 56 | #define NV_COUNTERS_INITIALIZED 0xC0DE0042U |
| 57 | |
| 58 | /** |
| 59 | * \brief Struct representing the NV counter data in flash. |
| 60 | */ |
| 61 | struct nv_counters_t { |
| 62 | uint32_t counters[NUM_NV_COUNTERS]; /**< Array of NV counters */ |
| 63 | uint32_t init_value; /**< Watermark to indicate if the NV counters have been |
| 64 | * initialised |
| 65 | */ |
| 66 | }; |
Jamie Fox | 7a4170d | 2018-08-15 14:13:42 +0100 | [diff] [blame] | 67 | |
| 68 | /* Import the CMSIS flash device driver */ |
| 69 | extern ARM_DRIVER_FLASH FLASH_DEV_NAME; |
| 70 | |
| 71 | enum tfm_plat_err_t tfm_plat_init_nv_counter(void) |
| 72 | { |
Jamie Fox | a504c2c | 2019-08-29 17:39:09 +0100 | [diff] [blame] | 73 | int32_t err; |
Jamie Fox | 7a4170d | 2018-08-15 14:13:42 +0100 | [diff] [blame] | 74 | uint32_t i; |
Jamie Fox | a504c2c | 2019-08-29 17:39:09 +0100 | [diff] [blame] | 75 | struct nv_counters_t nv_counters = {{0}}; |
Jamie Fox | 7a4170d | 2018-08-15 14:13:42 +0100 | [diff] [blame] | 76 | |
Jamie Fox | 43e80d9 | 2019-06-03 17:41:17 +0100 | [diff] [blame] | 77 | err = FLASH_DEV_NAME.Initialize(NULL); |
| 78 | if (err != ARM_DRIVER_OK) { |
| 79 | return TFM_PLAT_ERR_SYSTEM_ERR; |
| 80 | } |
| 81 | |
Jamie Fox | a504c2c | 2019-08-29 17:39:09 +0100 | [diff] [blame] | 82 | /* Read the NV counter area to be able to erase the sector and write later |
| 83 | * in the flash. |
| 84 | */ |
| 85 | err = FLASH_DEV_NAME.ReadData(TFM_NV_COUNTERS_AREA_ADDR, &nv_counters, |
| 86 | TFM_NV_COUNTERS_AREA_SIZE); |
Jamie Fox | 7a4170d | 2018-08-15 14:13:42 +0100 | [diff] [blame] | 87 | if (err != ARM_DRIVER_OK) { |
| 88 | return TFM_PLAT_ERR_SYSTEM_ERR; |
| 89 | } |
| 90 | |
Jamie Fox | a504c2c | 2019-08-29 17:39:09 +0100 | [diff] [blame] | 91 | if (nv_counters.init_value == NV_COUNTERS_INITIALIZED) { |
Jamie Fox | 7a4170d | 2018-08-15 14:13:42 +0100 | [diff] [blame] | 92 | return TFM_PLAT_ERR_SUCCESS; |
| 93 | } |
| 94 | |
| 95 | /* Add watermark, at the end of the NV counters area, to indicate that NV |
| 96 | * counters have been initialized. |
| 97 | */ |
Jamie Fox | a504c2c | 2019-08-29 17:39:09 +0100 | [diff] [blame] | 98 | nv_counters.init_value = NV_COUNTERS_INITIALIZED; |
Jamie Fox | 7a4170d | 2018-08-15 14:13:42 +0100 | [diff] [blame] | 99 | |
| 100 | /* Initialize all counters to 0 */ |
Jamie Fox | a504c2c | 2019-08-29 17:39:09 +0100 | [diff] [blame] | 101 | for (i = 0; i < NUM_NV_COUNTERS; i++) { |
| 102 | nv_counters.counters[i] = 0; |
Jamie Fox | 7a4170d | 2018-08-15 14:13:42 +0100 | [diff] [blame] | 103 | } |
| 104 | |
| 105 | /* Erase sector before write in it */ |
| 106 | err = FLASH_DEV_NAME.EraseSector(TFM_NV_COUNTERS_SECTOR_ADDR); |
| 107 | if (err != ARM_DRIVER_OK) { |
| 108 | return TFM_PLAT_ERR_SYSTEM_ERR; |
| 109 | } |
| 110 | |
Jamie Fox | a504c2c | 2019-08-29 17:39:09 +0100 | [diff] [blame] | 111 | /* Write in flash the in-memory NV counter content after modification */ |
| 112 | err = FLASH_DEV_NAME.ProgramData(TFM_NV_COUNTERS_AREA_ADDR, &nv_counters, |
| 113 | TFM_NV_COUNTERS_AREA_SIZE); |
Jamie Fox | 7a4170d | 2018-08-15 14:13:42 +0100 | [diff] [blame] | 114 | if (err != ARM_DRIVER_OK) { |
| 115 | return TFM_PLAT_ERR_SYSTEM_ERR; |
| 116 | } |
| 117 | |
| 118 | return TFM_PLAT_ERR_SUCCESS; |
| 119 | } |
| 120 | |
| 121 | enum tfm_plat_err_t tfm_plat_read_nv_counter(enum tfm_nv_counter_t counter_id, |
| 122 | uint32_t size, uint8_t *val) |
| 123 | { |
| 124 | int32_t err; |
| 125 | uint32_t flash_addr; |
| 126 | |
| 127 | if (size != NV_COUNTER_SIZE) { |
| 128 | return TFM_PLAT_ERR_SYSTEM_ERR; |
| 129 | } |
| 130 | |
| 131 | flash_addr = TFM_NV_COUNTERS_AREA_ADDR + (counter_id * NV_COUNTER_SIZE); |
| 132 | |
| 133 | err = FLASH_DEV_NAME.ReadData(flash_addr, val, NV_COUNTER_SIZE); |
| 134 | if (err != ARM_DRIVER_OK) { |
| 135 | return TFM_PLAT_ERR_SYSTEM_ERR; |
| 136 | } |
| 137 | |
| 138 | return TFM_PLAT_ERR_SUCCESS; |
| 139 | } |
| 140 | |
David Vincze | 2ff8c6a | 2019-05-21 11:53:00 +0200 | [diff] [blame] | 141 | enum tfm_plat_err_t tfm_plat_set_nv_counter(enum tfm_nv_counter_t counter_id, |
| 142 | uint32_t value) |
Jamie Fox | 7a4170d | 2018-08-15 14:13:42 +0100 | [diff] [blame] | 143 | { |
Jamie Fox | a504c2c | 2019-08-29 17:39:09 +0100 | [diff] [blame] | 144 | int32_t err; |
| 145 | struct nv_counters_t nv_counters = {{0}}; |
Jamie Fox | 7a4170d | 2018-08-15 14:13:42 +0100 | [diff] [blame] | 146 | |
Jamie Fox | a504c2c | 2019-08-29 17:39:09 +0100 | [diff] [blame] | 147 | /* Read the NV counter area to be able to erase the sector and write later |
| 148 | * in the flash. |
| 149 | */ |
| 150 | err = FLASH_DEV_NAME.ReadData(TFM_NV_COUNTERS_AREA_ADDR, &nv_counters, |
| 151 | TFM_NV_COUNTERS_AREA_SIZE); |
Jamie Fox | 7a4170d | 2018-08-15 14:13:42 +0100 | [diff] [blame] | 152 | if (err != ARM_DRIVER_OK) { |
| 153 | return TFM_PLAT_ERR_SYSTEM_ERR; |
| 154 | } |
| 155 | |
Jamie Fox | a504c2c | 2019-08-29 17:39:09 +0100 | [diff] [blame] | 156 | if (value != nv_counters.counters[counter_id]) { |
Jamie Fox | 7a4170d | 2018-08-15 14:13:42 +0100 | [diff] [blame] | 157 | |
Jamie Fox | a504c2c | 2019-08-29 17:39:09 +0100 | [diff] [blame] | 158 | if (value > nv_counters.counters[counter_id]) { |
| 159 | nv_counters.counters[counter_id] = value; |
David Vincze | 2ff8c6a | 2019-05-21 11:53:00 +0200 | [diff] [blame] | 160 | } else { |
| 161 | return TFM_PLAT_ERR_INVALID_INPUT; |
| 162 | } |
Jamie Fox | 7a4170d | 2018-08-15 14:13:42 +0100 | [diff] [blame] | 163 | |
David Vincze | 2ff8c6a | 2019-05-21 11:53:00 +0200 | [diff] [blame] | 164 | /* Erase sector before write in it */ |
| 165 | err = FLASH_DEV_NAME.EraseSector(TFM_NV_COUNTERS_SECTOR_ADDR); |
| 166 | if (err != ARM_DRIVER_OK) { |
| 167 | return TFM_PLAT_ERR_SYSTEM_ERR; |
| 168 | } |
Jamie Fox | 7a4170d | 2018-08-15 14:13:42 +0100 | [diff] [blame] | 169 | |
Jamie Fox | a504c2c | 2019-08-29 17:39:09 +0100 | [diff] [blame] | 170 | /* Write in flash the in-memory NV counter content after modification */ |
| 171 | err = FLASH_DEV_NAME.ProgramData(TFM_NV_COUNTERS_AREA_ADDR, |
| 172 | &nv_counters, |
| 173 | TFM_NV_COUNTERS_AREA_SIZE); |
David Vincze | 2ff8c6a | 2019-05-21 11:53:00 +0200 | [diff] [blame] | 174 | if (err != ARM_DRIVER_OK) { |
| 175 | return TFM_PLAT_ERR_SYSTEM_ERR; |
| 176 | } |
Jamie Fox | 7a4170d | 2018-08-15 14:13:42 +0100 | [diff] [blame] | 177 | } |
| 178 | |
| 179 | return TFM_PLAT_ERR_SUCCESS; |
| 180 | } |
David Vincze | 2ff8c6a | 2019-05-21 11:53:00 +0200 | [diff] [blame] | 181 | |
| 182 | enum tfm_plat_err_t tfm_plat_increment_nv_counter( |
| 183 | enum tfm_nv_counter_t counter_id) |
| 184 | { |
| 185 | uint32_t security_cnt; |
| 186 | enum tfm_plat_err_t err; |
| 187 | |
| 188 | err = tfm_plat_read_nv_counter(counter_id, |
| 189 | sizeof(security_cnt), |
| 190 | (uint8_t *)&security_cnt); |
| 191 | if (err != TFM_PLAT_ERR_SUCCESS) { |
| 192 | return err; |
| 193 | } |
| 194 | |
| 195 | if (security_cnt == UINT32_MAX) { |
| 196 | return TFM_PLAT_ERR_MAX_VALUE; |
| 197 | } |
| 198 | |
| 199 | return tfm_plat_set_nv_counter(counter_id, security_cnt + 1u); |
| 200 | } |