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 | |
Mark Horvath | 90605b5 | 2020-09-23 11:27:55 +0200 | [diff] [blame^] | 46 | #ifndef NV_COUNTERS_FLASH_DEV_NAME |
| 47 | #ifndef FLASH_DEV_NAME |
| 48 | #error "NV_COUNTERS_FLASH_DEV_NAME or FLASH_DEV_NAME must be defined in flash_layout.h" |
| 49 | #else |
| 50 | #define NV_COUNTERS_FLASH_DEV_NAME FLASH_DEV_NAME |
| 51 | #endif |
Jamie Fox | 7a4170d | 2018-08-15 14:13:42 +0100 | [diff] [blame] | 52 | #endif |
| 53 | /* End of compilation time checks to be sure the defines are well defined */ |
| 54 | |
Jamie Fox | 7a4170d | 2018-08-15 14:13:42 +0100 | [diff] [blame] | 55 | #define NV_COUNTER_SIZE sizeof(uint32_t) |
| 56 | #define INIT_VALUE_SIZE NV_COUNTER_SIZE |
Jamie Fox | a504c2c | 2019-08-29 17:39:09 +0100 | [diff] [blame] | 57 | #define NUM_NV_COUNTERS ((TFM_NV_COUNTERS_AREA_SIZE - INIT_VALUE_SIZE) \ |
| 58 | / NV_COUNTER_SIZE) |
Jamie Fox | 7a4170d | 2018-08-15 14:13:42 +0100 | [diff] [blame] | 59 | |
Jamie Fox | a504c2c | 2019-08-29 17:39:09 +0100 | [diff] [blame] | 60 | #define NV_COUNTERS_INITIALIZED 0xC0DE0042U |
| 61 | |
| 62 | /** |
| 63 | * \brief Struct representing the NV counter data in flash. |
| 64 | */ |
| 65 | struct nv_counters_t { |
| 66 | uint32_t counters[NUM_NV_COUNTERS]; /**< Array of NV counters */ |
| 67 | uint32_t init_value; /**< Watermark to indicate if the NV counters have been |
| 68 | * initialised |
| 69 | */ |
| 70 | }; |
Jamie Fox | 7a4170d | 2018-08-15 14:13:42 +0100 | [diff] [blame] | 71 | |
| 72 | /* Import the CMSIS flash device driver */ |
Mark Horvath | 90605b5 | 2020-09-23 11:27:55 +0200 | [diff] [blame^] | 73 | extern ARM_DRIVER_FLASH NV_COUNTERS_FLASH_DEV_NAME; |
Jamie Fox | 7a4170d | 2018-08-15 14:13:42 +0100 | [diff] [blame] | 74 | |
| 75 | enum tfm_plat_err_t tfm_plat_init_nv_counter(void) |
| 76 | { |
Jamie Fox | a504c2c | 2019-08-29 17:39:09 +0100 | [diff] [blame] | 77 | int32_t err; |
Jamie Fox | 7a4170d | 2018-08-15 14:13:42 +0100 | [diff] [blame] | 78 | uint32_t i; |
Jamie Fox | a504c2c | 2019-08-29 17:39:09 +0100 | [diff] [blame] | 79 | struct nv_counters_t nv_counters = {{0}}; |
Jamie Fox | 7a4170d | 2018-08-15 14:13:42 +0100 | [diff] [blame] | 80 | |
Mark Horvath | 90605b5 | 2020-09-23 11:27:55 +0200 | [diff] [blame^] | 81 | err = NV_COUNTERS_FLASH_DEV_NAME.Initialize(NULL); |
Jamie Fox | 43e80d9 | 2019-06-03 17:41:17 +0100 | [diff] [blame] | 82 | if (err != ARM_DRIVER_OK) { |
| 83 | return TFM_PLAT_ERR_SYSTEM_ERR; |
| 84 | } |
| 85 | |
Jamie Fox | a504c2c | 2019-08-29 17:39:09 +0100 | [diff] [blame] | 86 | /* Read the NV counter area to be able to erase the sector and write later |
| 87 | * in the flash. |
| 88 | */ |
Mark Horvath | 90605b5 | 2020-09-23 11:27:55 +0200 | [diff] [blame^] | 89 | err = NV_COUNTERS_FLASH_DEV_NAME.ReadData(TFM_NV_COUNTERS_AREA_ADDR, |
| 90 | &nv_counters, |
| 91 | TFM_NV_COUNTERS_AREA_SIZE); |
Jamie Fox | 7a4170d | 2018-08-15 14:13:42 +0100 | [diff] [blame] | 92 | if (err != ARM_DRIVER_OK) { |
| 93 | return TFM_PLAT_ERR_SYSTEM_ERR; |
| 94 | } |
| 95 | |
Jamie Fox | a504c2c | 2019-08-29 17:39:09 +0100 | [diff] [blame] | 96 | if (nv_counters.init_value == NV_COUNTERS_INITIALIZED) { |
Jamie Fox | 7a4170d | 2018-08-15 14:13:42 +0100 | [diff] [blame] | 97 | return TFM_PLAT_ERR_SUCCESS; |
| 98 | } |
| 99 | |
| 100 | /* Add watermark, at the end of the NV counters area, to indicate that NV |
| 101 | * counters have been initialized. |
| 102 | */ |
Jamie Fox | a504c2c | 2019-08-29 17:39:09 +0100 | [diff] [blame] | 103 | nv_counters.init_value = NV_COUNTERS_INITIALIZED; |
Jamie Fox | 7a4170d | 2018-08-15 14:13:42 +0100 | [diff] [blame] | 104 | |
| 105 | /* Initialize all counters to 0 */ |
Jamie Fox | a504c2c | 2019-08-29 17:39:09 +0100 | [diff] [blame] | 106 | for (i = 0; i < NUM_NV_COUNTERS; i++) { |
| 107 | nv_counters.counters[i] = 0; |
Jamie Fox | 7a4170d | 2018-08-15 14:13:42 +0100 | [diff] [blame] | 108 | } |
| 109 | |
| 110 | /* Erase sector before write in it */ |
Mark Horvath | 90605b5 | 2020-09-23 11:27:55 +0200 | [diff] [blame^] | 111 | err = NV_COUNTERS_FLASH_DEV_NAME.EraseSector(TFM_NV_COUNTERS_SECTOR_ADDR); |
Jamie Fox | 7a4170d | 2018-08-15 14:13:42 +0100 | [diff] [blame] | 112 | if (err != ARM_DRIVER_OK) { |
| 113 | return TFM_PLAT_ERR_SYSTEM_ERR; |
| 114 | } |
| 115 | |
Jamie Fox | a504c2c | 2019-08-29 17:39:09 +0100 | [diff] [blame] | 116 | /* Write in flash the in-memory NV counter content after modification */ |
Mark Horvath | 90605b5 | 2020-09-23 11:27:55 +0200 | [diff] [blame^] | 117 | err = NV_COUNTERS_FLASH_DEV_NAME.ProgramData(TFM_NV_COUNTERS_AREA_ADDR, |
| 118 | &nv_counters, |
| 119 | TFM_NV_COUNTERS_AREA_SIZE); |
Jamie Fox | 7a4170d | 2018-08-15 14:13:42 +0100 | [diff] [blame] | 120 | if (err != ARM_DRIVER_OK) { |
| 121 | return TFM_PLAT_ERR_SYSTEM_ERR; |
| 122 | } |
| 123 | |
| 124 | return TFM_PLAT_ERR_SUCCESS; |
| 125 | } |
| 126 | |
| 127 | enum tfm_plat_err_t tfm_plat_read_nv_counter(enum tfm_nv_counter_t counter_id, |
| 128 | uint32_t size, uint8_t *val) |
| 129 | { |
| 130 | int32_t err; |
| 131 | uint32_t flash_addr; |
| 132 | |
| 133 | if (size != NV_COUNTER_SIZE) { |
| 134 | return TFM_PLAT_ERR_SYSTEM_ERR; |
| 135 | } |
| 136 | |
| 137 | flash_addr = TFM_NV_COUNTERS_AREA_ADDR + (counter_id * NV_COUNTER_SIZE); |
| 138 | |
Mark Horvath | 90605b5 | 2020-09-23 11:27:55 +0200 | [diff] [blame^] | 139 | err = NV_COUNTERS_FLASH_DEV_NAME.ReadData(flash_addr, val, NV_COUNTER_SIZE); |
Jamie Fox | 7a4170d | 2018-08-15 14:13:42 +0100 | [diff] [blame] | 140 | if (err != ARM_DRIVER_OK) { |
| 141 | return TFM_PLAT_ERR_SYSTEM_ERR; |
| 142 | } |
| 143 | |
| 144 | return TFM_PLAT_ERR_SUCCESS; |
| 145 | } |
| 146 | |
David Vincze | 2ff8c6a | 2019-05-21 11:53:00 +0200 | [diff] [blame] | 147 | enum tfm_plat_err_t tfm_plat_set_nv_counter(enum tfm_nv_counter_t counter_id, |
| 148 | uint32_t value) |
Jamie Fox | 7a4170d | 2018-08-15 14:13:42 +0100 | [diff] [blame] | 149 | { |
Jamie Fox | a504c2c | 2019-08-29 17:39:09 +0100 | [diff] [blame] | 150 | int32_t err; |
| 151 | struct nv_counters_t nv_counters = {{0}}; |
Jamie Fox | 7a4170d | 2018-08-15 14:13:42 +0100 | [diff] [blame] | 152 | |
Jamie Fox | a504c2c | 2019-08-29 17:39:09 +0100 | [diff] [blame] | 153 | /* Read the NV counter area to be able to erase the sector and write later |
| 154 | * in the flash. |
| 155 | */ |
Mark Horvath | 90605b5 | 2020-09-23 11:27:55 +0200 | [diff] [blame^] | 156 | err = NV_COUNTERS_FLASH_DEV_NAME.ReadData(TFM_NV_COUNTERS_AREA_ADDR, |
| 157 | &nv_counters, |
| 158 | TFM_NV_COUNTERS_AREA_SIZE); |
Jamie Fox | 7a4170d | 2018-08-15 14:13:42 +0100 | [diff] [blame] | 159 | if (err != ARM_DRIVER_OK) { |
| 160 | return TFM_PLAT_ERR_SYSTEM_ERR; |
| 161 | } |
| 162 | |
Jamie Fox | a504c2c | 2019-08-29 17:39:09 +0100 | [diff] [blame] | 163 | if (value != nv_counters.counters[counter_id]) { |
Jamie Fox | 7a4170d | 2018-08-15 14:13:42 +0100 | [diff] [blame] | 164 | |
Jamie Fox | a504c2c | 2019-08-29 17:39:09 +0100 | [diff] [blame] | 165 | if (value > nv_counters.counters[counter_id]) { |
| 166 | nv_counters.counters[counter_id] = value; |
David Vincze | 2ff8c6a | 2019-05-21 11:53:00 +0200 | [diff] [blame] | 167 | } else { |
| 168 | return TFM_PLAT_ERR_INVALID_INPUT; |
| 169 | } |
Jamie Fox | 7a4170d | 2018-08-15 14:13:42 +0100 | [diff] [blame] | 170 | |
David Vincze | 2ff8c6a | 2019-05-21 11:53:00 +0200 | [diff] [blame] | 171 | /* Erase sector before write in it */ |
Mark Horvath | 90605b5 | 2020-09-23 11:27:55 +0200 | [diff] [blame^] | 172 | err = NV_COUNTERS_FLASH_DEV_NAME.EraseSector( |
| 173 | TFM_NV_COUNTERS_SECTOR_ADDR); |
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 | |
Jamie Fox | a504c2c | 2019-08-29 17:39:09 +0100 | [diff] [blame] | 178 | /* Write in flash the in-memory NV counter content after modification */ |
Mark Horvath | 90605b5 | 2020-09-23 11:27:55 +0200 | [diff] [blame^] | 179 | err = NV_COUNTERS_FLASH_DEV_NAME.ProgramData(TFM_NV_COUNTERS_AREA_ADDR, |
| 180 | &nv_counters, |
| 181 | TFM_NV_COUNTERS_AREA_SIZE); |
David Vincze | 2ff8c6a | 2019-05-21 11:53:00 +0200 | [diff] [blame] | 182 | if (err != ARM_DRIVER_OK) { |
| 183 | return TFM_PLAT_ERR_SYSTEM_ERR; |
| 184 | } |
Jamie Fox | 7a4170d | 2018-08-15 14:13:42 +0100 | [diff] [blame] | 185 | } |
| 186 | |
| 187 | return TFM_PLAT_ERR_SUCCESS; |
| 188 | } |
David Vincze | 2ff8c6a | 2019-05-21 11:53:00 +0200 | [diff] [blame] | 189 | |
| 190 | enum tfm_plat_err_t tfm_plat_increment_nv_counter( |
| 191 | enum tfm_nv_counter_t counter_id) |
| 192 | { |
| 193 | uint32_t security_cnt; |
| 194 | enum tfm_plat_err_t err; |
| 195 | |
| 196 | err = tfm_plat_read_nv_counter(counter_id, |
| 197 | sizeof(security_cnt), |
| 198 | (uint8_t *)&security_cnt); |
| 199 | if (err != TFM_PLAT_ERR_SUCCESS) { |
| 200 | return err; |
| 201 | } |
| 202 | |
| 203 | if (security_cnt == UINT32_MAX) { |
| 204 | return TFM_PLAT_ERR_MAX_VALUE; |
| 205 | } |
| 206 | |
| 207 | return tfm_plat_set_nv_counter(counter_id, security_cnt + 1u); |
| 208 | } |