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