1 /*
2  * Copyright (c) 2018-2023, Arm Limited. All rights reserved.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  *
6  */
7 
8 /* NOTE: For the security of the protected storage system, the bootloader
9  * rollback protection, and the protection of cryptographic material  it is
10  * CRITICAL to use a internal (in-die) persistent memory for the implementation
11  * of the OTP_NV_COUNTERS flash area (see flash_otp_nv_layout.c).
12  */
13 
14 #include "tfm_plat_nv_counters.h"
15 
16 #include <limits.h>
17 #include "Driver_Flash.h"
18 #include "flash_layout.h"
19 #include "tfm_plat_otp.h"
20 #include "cmsis_compiler.h"
21 
22 #include <string.h>
23 
24 #define OTP_COUNTER_MAX_SIZE    (16u * sizeof(uint32_t))
25 #define NV_COUNTER_SIZE         4
26 #define OTP_COUNTER_MAGIC       0x3333CAFE
27 
tfm_plat_init_nv_counter(void)28 enum tfm_plat_err_t tfm_plat_init_nv_counter(void)
29 {
30     return TFM_PLAT_ERR_SUCCESS;
31 }
32 
read_nv_counter_otp(enum tfm_otp_element_id_t id,uint8_t * val)33 static enum tfm_plat_err_t read_nv_counter_otp(enum tfm_otp_element_id_t id,
34                                                uint8_t *val)
35 {
36     size_t counter_size;
37     enum tfm_plat_err_t err;
38     size_t idx;
39     uint32_t counter_value[OTP_COUNTER_MAX_SIZE / sizeof(uint32_t)] = {0};
40     uint32_t count;
41 
42     err = tfm_plat_otp_get_size(id, &counter_size);
43     if (err != TFM_PLAT_ERR_SUCCESS) {
44         return err;
45     }
46 
47     counter_size = counter_size > OTP_COUNTER_MAX_SIZE ? OTP_COUNTER_MAX_SIZE
48                                                        : counter_size;
49 
50     err = tfm_plat_otp_read(id, counter_size, (uint8_t *)counter_value);
51     if (err != TFM_PLAT_ERR_SUCCESS) {
52         return err;
53     }
54 
55     count = 0;
56     for (idx = 0; idx < counter_size / sizeof(uint32_t); idx++) {
57         if (counter_value[idx] == OTP_COUNTER_MAGIC) {
58             count += 1;
59         } else if (counter_value[idx] == 0) {
60             break;
61         } else {
62             return TFM_PLAT_ERR_SYSTEM_ERR;
63         }
64     }
65 
66     memcpy(val, &count, NV_COUNTER_SIZE);
67 
68     return TFM_PLAT_ERR_SUCCESS;
69 }
70 
tfm_plat_read_nv_counter(enum tfm_nv_counter_t counter_id,uint32_t size,uint8_t * val)71 enum tfm_plat_err_t tfm_plat_read_nv_counter(enum tfm_nv_counter_t counter_id,
72                                              uint32_t size, uint8_t *val)
73 {
74     if (size != NV_COUNTER_SIZE) {
75         return TFM_PLAT_ERR_SYSTEM_ERR;
76     }
77 
78     /* Assumes Platform nv counters are contiguous*/
79     if (counter_id >= PLAT_NV_COUNTER_BL2_0 &&
80         counter_id < (PLAT_NV_COUNTER_BL2_0 + MCUBOOT_IMAGE_NUMBER)) {
81         return read_nv_counter_otp(PLAT_OTP_ID_NV_COUNTER_BL2_0 +
82                                        (counter_id - PLAT_NV_COUNTER_BL2_0),
83                                    val);
84     }
85 
86     switch (counter_id) {
87 #ifdef PLATFORM_HAS_PS_NV_OTP_COUNTERS
88     case (PLAT_NV_COUNTER_PS_0):
89         return read_nv_counter_otp(PLAT_OTP_ID_NV_COUNTER_PS_0, val);
90     case (PLAT_NV_COUNTER_PS_1):
91         return read_nv_counter_otp(PLAT_OTP_ID_NV_COUNTER_PS_1, val);
92     case (PLAT_NV_COUNTER_PS_2):
93         return read_nv_counter_otp(PLAT_OTP_ID_NV_COUNTER_PS_2, val);
94 #endif /* PLATFORM_HAS_PS_NV_OTP_COUNTERS */
95     case (PLAT_NV_COUNTER_NS_0):
96         return read_nv_counter_otp(PLAT_OTP_ID_NV_COUNTER_NS_0, val);
97     case (PLAT_NV_COUNTER_NS_1):
98         return read_nv_counter_otp(PLAT_OTP_ID_NV_COUNTER_NS_1, val);
99     case (PLAT_NV_COUNTER_NS_2):
100         return read_nv_counter_otp(PLAT_OTP_ID_NV_COUNTER_NS_2, val);
101 
102     case (PLAT_NV_COUNTER_BL1_0):
103         return read_nv_counter_otp(PLAT_OTP_ID_NV_COUNTER_BL1_0, val);
104 
105     default:
106         return TFM_PLAT_ERR_UNSUPPORTED;
107     }
108 }
109 
set_nv_counter_otp(enum tfm_otp_element_id_t id,uint32_t value)110 static enum tfm_plat_err_t set_nv_counter_otp(enum tfm_otp_element_id_t id,
111                                               uint32_t value)
112 {
113     size_t counter_size;
114     enum tfm_plat_err_t err;
115     size_t idx;
116     uint32_t counter_value[OTP_COUNTER_MAX_SIZE / sizeof(uint32_t)] = {0};
117 
118     err = tfm_plat_otp_get_size(id, &counter_size);
119     if (err != TFM_PLAT_ERR_SUCCESS) {
120         return err;
121     }
122 
123     counter_size = counter_size > OTP_COUNTER_MAX_SIZE ? OTP_COUNTER_MAX_SIZE
124                                                        : counter_size;
125 
126     if (value > counter_size) {
127         return TFM_PLAT_ERR_MAX_VALUE;
128     }
129 
130     for (idx = 0; idx < value; idx++) {
131         counter_value[idx] = OTP_COUNTER_MAGIC;
132     }
133 
134     err = tfm_plat_otp_write(id, sizeof(counter_value),
135                              (uint8_t *)counter_value);
136 
137     return err;
138 }
139 
tfm_plat_set_nv_counter(enum tfm_nv_counter_t counter_id,uint32_t value)140 enum tfm_plat_err_t tfm_plat_set_nv_counter(enum tfm_nv_counter_t counter_id,
141                                             uint32_t value)
142 {
143     /* Assumes Platform nv counters are contiguous*/
144     if (counter_id >= PLAT_NV_COUNTER_BL2_0 &&
145         counter_id < (PLAT_NV_COUNTER_BL2_0 + MCUBOOT_IMAGE_NUMBER)) {
146         return set_nv_counter_otp(PLAT_OTP_ID_NV_COUNTER_BL2_0 +
147                                       (counter_id - PLAT_NV_COUNTER_BL2_0),
148                                   value);
149     }
150 
151     switch (counter_id) {
152 #ifdef PLATFORM_HAS_PS_NV_OTP_COUNTERS
153     case (PLAT_NV_COUNTER_PS_0):
154         return set_nv_counter_otp(PLAT_OTP_ID_NV_COUNTER_PS_0, value);
155     case (PLAT_NV_COUNTER_PS_1):
156         return set_nv_counter_otp(PLAT_OTP_ID_NV_COUNTER_PS_1, value);
157     case (PLAT_NV_COUNTER_PS_2):
158         return set_nv_counter_otp(PLAT_OTP_ID_NV_COUNTER_PS_2, value);
159 #endif /* PLATFORM_HAS_PS_NV_OTP_COUNTERS */
160 
161     case (PLAT_NV_COUNTER_NS_0):
162         return set_nv_counter_otp(PLAT_OTP_ID_NV_COUNTER_NS_0, value);
163     case (PLAT_NV_COUNTER_NS_1):
164         return set_nv_counter_otp(PLAT_OTP_ID_NV_COUNTER_NS_1, value);
165     case (PLAT_NV_COUNTER_NS_2):
166         return set_nv_counter_otp(PLAT_OTP_ID_NV_COUNTER_NS_2, value);
167 
168     case (PLAT_NV_COUNTER_BL1_0):
169         return set_nv_counter_otp(PLAT_OTP_ID_NV_COUNTER_BL1_0, value);
170 
171     default:
172         return TFM_PLAT_ERR_UNSUPPORTED;
173     }
174 }
175 
tfm_plat_increment_nv_counter(enum tfm_nv_counter_t counter_id)176 enum tfm_plat_err_t tfm_plat_increment_nv_counter(
177                                            enum tfm_nv_counter_t counter_id)
178 {
179     uint32_t security_cnt;
180     enum tfm_plat_err_t err;
181 
182     err = tfm_plat_read_nv_counter(counter_id,
183                                    sizeof(security_cnt),
184                                    (uint8_t *)&security_cnt);
185     if (err != TFM_PLAT_ERR_SUCCESS) {
186         return err;
187     }
188 
189     if (security_cnt == UINT32_MAX) {
190         return TFM_PLAT_ERR_MAX_VALUE;
191     }
192 
193     return tfm_plat_set_nv_counter(counter_id, security_cnt + 1u);
194 }
195