1 /*
2 * SPDX-License-Identifier: BSD-3-Clause
3 * SPDX-FileCopyrightText: Copyright The TrustedFirmware-M Contributors
4 *
5 */
6
7 #include "tfm_plat_nv_counters.h"
8 #include "tfm_plat_otp.h"
9
10 #if (PS_NS_NV_COUNTER_IN_ITS == 1)
11 #include "psa/internal_trusted_storage.h"
12 #endif
13
14 #include "psa_manifest/pid.h"
15 #include <limits.h>
16 #include <string.h>
17
18 #define OTP_COUNTER_MAX_SIZE 64
19 #define NV_COUNTER_SIZE 4
20 #define OTP_COUNTER_MAGIC 0xAEC7
21 #define ITS_NV_COUNTER_UID_BASE 0xFFFFFFFFFFFFA000
22
tfm_plat_init_nv_counter(void)23 enum tfm_plat_err_t tfm_plat_init_nv_counter(void)
24 {
25 return TFM_PLAT_ERR_SUCCESS;
26 }
27
read_otp_counter(enum tfm_otp_element_id_t id,uint8_t * val)28 static enum tfm_plat_err_t read_otp_counter(enum tfm_otp_element_id_t id,
29 uint8_t *val)
30 {
31 size_t counter_size;
32 enum tfm_plat_err_t err;
33 size_t idx;
34 uint16_t counter_value[OTP_COUNTER_MAX_SIZE / sizeof(uint16_t)] = {0};
35 uint32_t count;
36
37 err = tfm_plat_otp_get_size(id, &counter_size);
38 if (err != TFM_PLAT_ERR_SUCCESS) {
39 return err;
40 }
41
42 counter_size = counter_size > OTP_COUNTER_MAX_SIZE ? OTP_COUNTER_MAX_SIZE
43 : counter_size;
44
45 err = tfm_plat_otp_read(id, counter_size, (uint8_t *)counter_value);
46 if (err != TFM_PLAT_ERR_SUCCESS) {
47 return err;
48 }
49
50 count = 0;
51 for (idx = 0; idx < counter_size / sizeof(uint16_t); idx++) {
52 if (counter_value[idx] == OTP_COUNTER_MAGIC) {
53 count += 1;
54 } else if (counter_value[idx] == 0) {
55 break;
56 } else {
57 return TFM_PLAT_ERR_SYSTEM_ERR;
58 }
59 }
60
61 memcpy(val, &count, NV_COUNTER_SIZE);
62
63 return TFM_PLAT_ERR_SUCCESS;
64 }
65
66 #if (PS_NS_NV_COUNTER_IN_ITS == 1)
read_its_nv_counter(enum tfm_otp_element_id_t id,uint8_t * val)67 static enum tfm_plat_err_t read_its_nv_counter(enum tfm_otp_element_id_t id,
68 uint8_t *val)
69 {
70 psa_storage_uid_t uid = (ITS_NV_COUNTER_UID_BASE + id);
71 psa_status_t status;
72 size_t data_length = 0;
73
74 status = psa_its_get(uid, 0, NV_COUNTER_SIZE, val, &data_length);
75
76 if (status == PSA_ERROR_DOES_NOT_EXIST) {
77 memset(val, 0, NV_COUNTER_SIZE);
78 }
79
80 if (((status == PSA_SUCCESS) && (data_length == NV_COUNTER_SIZE)) ||
81 (status == PSA_ERROR_DOES_NOT_EXIST)) {
82 return TFM_PLAT_ERR_SUCCESS;
83 } else {
84 return TFM_PLAT_ERR_SYSTEM_ERR;
85 }
86 }
87 #endif /* (PS_NS_NV_COUNTER_IN_ITS == 1) */
88
tfm_plat_read_nv_counter(enum tfm_nv_counter_t counter_id,uint32_t size,uint8_t * val)89 enum tfm_plat_err_t tfm_plat_read_nv_counter(enum tfm_nv_counter_t counter_id,
90 uint32_t size, uint8_t *val)
91 {
92 if (size != NV_COUNTER_SIZE) {
93 return TFM_PLAT_ERR_INVALID_INPUT;
94 }
95
96 /* Assumes Platform nv counters are contiguous*/
97 if (counter_id >= PLAT_NV_COUNTER_BL2_0 &&
98 counter_id < (PLAT_NV_COUNTER_BL2_0 + MCUBOOT_IMAGE_NUMBER)) {
99 return read_otp_counter(PLAT_OTP_ID_NV_COUNTER_BL2_0 +
100 (counter_id - PLAT_NV_COUNTER_BL2_0),
101 val);
102 }
103
104 switch (counter_id) {
105 #if (PS_NS_NV_COUNTER_IN_ITS == 0)
106 case (PLAT_NV_COUNTER_NS_0):
107 return read_otp_counter(PLAT_OTP_ID_NV_COUNTER_NS_0, val);
108 case (PLAT_NV_COUNTER_NS_1):
109 return read_otp_counter(PLAT_OTP_ID_NV_COUNTER_NS_1, val);
110 case (PLAT_NV_COUNTER_NS_2):
111 return read_otp_counter(PLAT_OTP_ID_NV_COUNTER_NS_2, val);
112 case (PLAT_NV_COUNTER_PS_0):
113 return read_otp_counter(PLAT_OTP_ID_NV_COUNTER_PS_0, val);
114 case (PLAT_NV_COUNTER_PS_1):
115 return read_otp_counter(PLAT_OTP_ID_NV_COUNTER_PS_1, val);
116 case (PLAT_NV_COUNTER_PS_2):
117 return read_otp_counter(PLAT_OTP_ID_NV_COUNTER_PS_2, val);
118 #else
119 case (PLAT_NV_COUNTER_NS_0):
120 return read_its_nv_counter(PLAT_OTP_ID_NV_COUNTER_NS_0, val);
121 case (PLAT_NV_COUNTER_NS_1):
122 return read_its_nv_counter(PLAT_OTP_ID_NV_COUNTER_NS_1, val);
123 case (PLAT_NV_COUNTER_NS_2):
124 return read_its_nv_counter(PLAT_OTP_ID_NV_COUNTER_NS_2, val);
125 case (PLAT_NV_COUNTER_PS_0):
126 return read_its_nv_counter(PLAT_OTP_ID_NV_COUNTER_PS_0, val);
127 case (PLAT_NV_COUNTER_PS_1):
128 return read_its_nv_counter(PLAT_OTP_ID_NV_COUNTER_PS_1, val);
129 case (PLAT_NV_COUNTER_PS_2):
130 return read_its_nv_counter(PLAT_OTP_ID_NV_COUNTER_PS_2, val);
131 #endif /* (PS_NS_NV_COUNTER_IN_ITS == 0) */
132
133 default:
134 return TFM_PLAT_ERR_UNSUPPORTED;
135 }
136 }
137
set_otp_counter(enum tfm_otp_element_id_t id,uint32_t val)138 static enum tfm_plat_err_t set_otp_counter(enum tfm_otp_element_id_t id,
139 uint32_t val)
140 {
141 size_t counter_size;
142 enum tfm_plat_err_t err;
143 size_t idx;
144 uint16_t counter_value[OTP_COUNTER_MAX_SIZE / sizeof(uint16_t)] = {0};
145
146 err = tfm_plat_otp_get_size(id, &counter_size);
147 if (err != TFM_PLAT_ERR_SUCCESS) {
148 return err;
149 }
150
151 counter_size = counter_size > OTP_COUNTER_MAX_SIZE ? OTP_COUNTER_MAX_SIZE
152 : counter_size;
153
154 if (val > (counter_size / sizeof(uint16_t))) {
155 return TFM_PLAT_ERR_INVALID_INPUT;
156 }
157
158 for (idx = 0; idx < val; idx++) {
159 counter_value[idx] = OTP_COUNTER_MAGIC;
160 }
161
162 err = tfm_plat_otp_write(id, counter_size,
163 (uint8_t *)counter_value);
164
165 return err;
166 }
167
168 #if (PS_NS_NV_COUNTER_IN_ITS == 1)
set_its_nv_counter(enum tfm_otp_element_id_t id,uint32_t val)169 static enum tfm_plat_err_t set_its_nv_counter(enum tfm_otp_element_id_t id,
170 uint32_t val)
171 {
172 psa_storage_uid_t uid = (ITS_NV_COUNTER_UID_BASE + id);
173 psa_status_t status;
174
175 status = psa_its_set(uid, NV_COUNTER_SIZE, &val, PSA_STORAGE_FLAG_NONE);
176
177 if (status == PSA_SUCCESS) {
178 return TFM_PLAT_ERR_SUCCESS;
179 } else {
180 return TFM_PLAT_ERR_SYSTEM_ERR;
181 }
182 }
183 #endif /* (PS_NS_NV_COUNTER_IN_ITS == 1) */
184
tfm_plat_set_nv_counter(enum tfm_nv_counter_t counter_id,uint32_t value)185 enum tfm_plat_err_t tfm_plat_set_nv_counter(enum tfm_nv_counter_t counter_id,
186 uint32_t value)
187 {
188 /* Assumes Platform nv counters are contiguous*/
189 if (counter_id >= PLAT_NV_COUNTER_BL2_0 &&
190 counter_id < (PLAT_NV_COUNTER_BL2_0 + MCUBOOT_IMAGE_NUMBER)) {
191 return set_otp_counter(PLAT_OTP_ID_NV_COUNTER_BL2_0 +
192 (counter_id - PLAT_NV_COUNTER_BL2_0),
193 value);
194 }
195
196 switch (counter_id) {
197 #if (PS_NS_NV_COUNTER_IN_ITS == 0)
198 case (PLAT_NV_COUNTER_NS_0):
199 return set_otp_counter(PLAT_OTP_ID_NV_COUNTER_NS_0, value);
200 case (PLAT_NV_COUNTER_NS_1):
201 return set_otp_counter(PLAT_OTP_ID_NV_COUNTER_NS_1, value);
202 case (PLAT_NV_COUNTER_NS_2):
203 return set_otp_counter(PLAT_OTP_ID_NV_COUNTER_NS_2, value);
204 case (PLAT_NV_COUNTER_PS_0):
205 return set_otp_counter(PLAT_OTP_ID_NV_COUNTER_PS_0, value);
206 case (PLAT_NV_COUNTER_PS_1):
207 return set_otp_counter(PLAT_OTP_ID_NV_COUNTER_PS_1, value);
208 case (PLAT_NV_COUNTER_PS_2):
209 return set_otp_counter(PLAT_OTP_ID_NV_COUNTER_PS_2, value);
210 #else
211 case (PLAT_NV_COUNTER_NS_0):
212 return set_its_nv_counter(PLAT_OTP_ID_NV_COUNTER_NS_0, value);
213 case (PLAT_NV_COUNTER_NS_1):
214 return set_its_nv_counter(PLAT_OTP_ID_NV_COUNTER_NS_1, value);
215 case (PLAT_NV_COUNTER_NS_2):
216 return set_its_nv_counter(PLAT_OTP_ID_NV_COUNTER_NS_2, value);
217 case (PLAT_NV_COUNTER_PS_0):
218 return set_its_nv_counter(PLAT_OTP_ID_NV_COUNTER_PS_0, value);
219 case (PLAT_NV_COUNTER_PS_1):
220 return set_its_nv_counter(PLAT_OTP_ID_NV_COUNTER_PS_1, value);
221 case (PLAT_NV_COUNTER_PS_2):
222 return set_its_nv_counter(PLAT_OTP_ID_NV_COUNTER_PS_2, value);
223 #endif /* (PS_NS_NV_COUNTER_IN_ITS == 0) */
224
225 default:
226 return TFM_PLAT_ERR_UNSUPPORTED;
227 }
228 }
229
tfm_plat_increment_nv_counter(enum tfm_nv_counter_t counter_id)230 enum tfm_plat_err_t tfm_plat_increment_nv_counter(
231 enum tfm_nv_counter_t counter_id)
232 {
233 uint32_t security_cnt;
234 enum tfm_plat_err_t err;
235
236 err = tfm_plat_read_nv_counter(counter_id,
237 sizeof(security_cnt),
238 (uint8_t *)&security_cnt);
239 if (err != TFM_PLAT_ERR_SUCCESS) {
240 return err;
241 }
242
243 return tfm_plat_set_nv_counter(counter_id, security_cnt + 1u);
244 }
245
tfm_plat_nv_counter_permissions_check(int32_t client_id,enum tfm_nv_counter_t nv_counter_no,bool is_read)246 enum tfm_plat_err_t tfm_plat_nv_counter_permissions_check(int32_t client_id,
247 enum tfm_nv_counter_t nv_counter_no,
248 bool is_read)
249 {
250 (void)is_read;
251
252 switch (nv_counter_no) {
253 #ifdef TFM_PARTITION_PROTECTED_STORAGE
254 case PLAT_NV_COUNTER_PS_0:
255 case PLAT_NV_COUNTER_PS_1:
256 case PLAT_NV_COUNTER_PS_2:
257 if (client_id == TFM_SP_PS) {
258 return TFM_PLAT_ERR_SUCCESS;
259 } else {
260 return TFM_PLAT_ERR_UNSUPPORTED;
261 }
262 #endif /* TFM_PARTITION_PROTECTED_STORAGE */
263 case PLAT_NV_COUNTER_NS_0:
264 case PLAT_NV_COUNTER_NS_1:
265 case PLAT_NV_COUNTER_NS_2:
266 /* TODO how does this interact with the ns_ctx extension? */
267 if (client_id < 0) {
268 return TFM_PLAT_ERR_SUCCESS;
269 } else {
270 return TFM_PLAT_ERR_UNSUPPORTED;
271 }
272 default:
273 return TFM_PLAT_ERR_UNSUPPORTED;
274 }
275 }
276
tfm_plat_ns_counter_idx_to_nv_counter(uint32_t ns_counter_idx,enum tfm_nv_counter_t * counter_id)277 enum tfm_plat_err_t tfm_plat_ns_counter_idx_to_nv_counter(uint32_t ns_counter_idx,
278 enum tfm_nv_counter_t *counter_id)
279 {
280 /* Default NV counters only have PLAT_NV_COUNTERS_NS_0, _1 and _2 */
281 if ((ns_counter_idx > 2) || (counter_id == NULL)) {
282 return TFM_PLAT_ERR_INVALID_INPUT;
283 }
284
285 *counter_id = PLAT_NV_COUNTER_NS_0 + ns_counter_idx;
286
287 return TFM_PLAT_ERR_SUCCESS;
288 }
289