1 /*
2 * SPDX-License-Identifier: BSD-3-Clause
3 * SPDX-FileCopyrightText: Copyright The TrustedFirmware-M Contributors
4 *
5 */
6
7 #include "tfm_plat_otp.h"
8 #include "pico/bootrom.h"
9 #include "hardware/regs/otp_data.h"
10 #include <string.h>
11
12 #define OTP_BUFFER_MASK 0x0000FFFF
13 #define OTP_IS_WRITE_MASK 0x00010000
14 #define OTP_IS_ECC_MASK 0x00020000
15 #define OTP_ROW_PER_PAGE 0x40
16
17 struct rp2350_otp_element_t {
18 uint16_t row_offset; /* OTP row offset, used in otp_access() bootrom api */
19 uint8_t byte_len; /* Length of the element in bytes, should be aligned(2) or
20 aligned(4) depending usage mode */
21 };
22
23 /* RP2350 OTP is accessable through the bootrom api: otp_access. This OTP is
24 organized into pages and rows, it has 64 pages, each 64 rows in size, 4096
25 rows altogether. Each row has 24 bits (16 bit data, 8bit ECC). Because of its
26 structure accesses have to be aligned to 2 bytes. The default OTP elements
27 need to be placed here. This sturct serves as a map between rows and
28 tfm_otp_element_id_t ids.
29 The first 3 pages are occupied.
30 */
31 static const struct rp2350_otp_element_t otp_map[] = {
32 [PLAT_OTP_ID_HUK] = {.row_offset = 0xC0 + 0x00 , .byte_len = 32 },
33 [PLAT_OTP_ID_GUK] = {.row_offset = 0xC0 + 0x10 , .byte_len = 32 },
34 [PLAT_OTP_ID_IAK] = {.row_offset = 0xC0 + 0x20 , .byte_len = 32 },
35 [PLAT_OTP_ID_IAK_LEN] = {.row_offset = 0xC0 + 0x30 , .byte_len = 4 },
36 [PLAT_OTP_ID_IAK_TYPE] = {.row_offset = 0xC0 + 0x32 , .byte_len = 4 },
37 [PLAT_OTP_ID_IAK_ID] = {.row_offset = 0xC0 + 0x34 , .byte_len = 32 },
38 [PLAT_OTP_ID_BOOT_SEED] = {.row_offset = 0xC0 + 0x44 , .byte_len = 32 },
39 [PLAT_OTP_ID_LCS] = {.row_offset = 0xC0 + 0x54 , .byte_len = 4 },
40 [PLAT_OTP_ID_IMPLEMENTATION_ID] = {.row_offset = 0xC0 + 0x56 , .byte_len = 32 },
41 [PLAT_OTP_ID_CERT_REF] = {.row_offset = 0xC0 + 0x66 , .byte_len = 32 },
42 [PLAT_OTP_ID_VERIFICATION_SERVICE_URL] = {.row_offset = 0xC0 + 0x76 , .byte_len = 32 },
43 [PLAT_OTP_ID_PROFILE_DEFINITION] = {.row_offset = 0xC0 + 0x86 , .byte_len = 32 },
44 [PLAT_OTP_ID_BL2_ROTPK_0] = {.row_offset = 0xC0 + 0x96 , .byte_len = 100},
45 [PLAT_OTP_ID_BL2_ROTPK_1] = {.row_offset = 0xC0 + 0xC8 , .byte_len = 100},
46 [PLAT_OTP_ID_BL2_ROTPK_2] = {.row_offset = 0xC0 + 0xFA , .byte_len = 100},
47 [PLAT_OTP_ID_BL2_ROTPK_3] = {.row_offset = 0xC0 + 0x12C, .byte_len = 100},
48 [PLAT_OTP_ID_NV_COUNTER_BL2_0] = {.row_offset = 0xC0 + 0x15E, .byte_len = 64 },
49 [PLAT_OTP_ID_NV_COUNTER_BL2_1] = {.row_offset = 0xC0 + 0x17E, .byte_len = 64 },
50 [PLAT_OTP_ID_NV_COUNTER_BL2_2] = {.row_offset = 0xC0 + 0x19E, .byte_len = 64 },
51 [PLAT_OTP_ID_NV_COUNTER_BL2_3] = {.row_offset = 0xC0 + 0x1BE, .byte_len = 64 },
52 [PLAT_OTP_ID_NV_COUNTER_NS_0] = {.row_offset = 0xC0 + 0x1DE, .byte_len = 64 },
53 [PLAT_OTP_ID_NV_COUNTER_NS_1] = {.row_offset = 0xC0 + 0x1FE, .byte_len = 64 },
54 [PLAT_OTP_ID_NV_COUNTER_NS_2] = {.row_offset = 0xC0 + 0x21E, .byte_len = 64 },
55 [PLAT_OTP_ID_KEY_BL2_ENCRYPTION] = {.row_offset = 0xC0 + 0x23E, .byte_len = 0 },
56 [PLAT_OTP_ID_BL1_2_IMAGE] = {.row_offset = 0xC0 + 0x23E, .byte_len = 0 },
57 [PLAT_OTP_ID_BL1_2_IMAGE_HASH] = {.row_offset = 0xC0 + 0x23E, .byte_len = 0 },
58 [PLAT_OTP_ID_BL2_IMAGE_HASH] = {.row_offset = 0xC0 + 0x23E, .byte_len = 0 },
59 [PLAT_OTP_ID_BL1_ROTPK_0] = {.row_offset = 0xC0 + 0x23E, .byte_len = 0 },
60 [PLAT_OTP_ID_NV_COUNTER_BL1_0] = {.row_offset = 0xC0 + 0x23E, .byte_len = 0 },
61 [PLAT_OTP_ID_ENTROPY_SEED] = {.row_offset = 0xC0 + 0x23E, .byte_len = 64 },
62 [PLAT_OTP_ID_SECURE_DEBUG_PK] = {.row_offset = 0xC0 + 0x25E, .byte_len = 32 },
63 [PLAT_OTP_ID_NV_COUNTER_PS_0] = {.row_offset = 0xC0 + 0x26E, .byte_len = 64 },
64 [PLAT_OTP_ID_NV_COUNTER_PS_1] = {.row_offset = 0xC0 + 0x28E, .byte_len = 64 },
65 [PLAT_OTP_ID_NV_COUNTER_PS_2] = {.row_offset = 0xC0 + 0x2AE, .byte_len = 64 },
66 };
67
tfm_plat_otp_init(void)68 enum tfm_plat_err_t tfm_plat_otp_init(void)
69 {
70 return TFM_PLAT_ERR_SUCCESS;
71 }
72
tfm_plat_otp_read(enum tfm_otp_element_id_t id,size_t out_len,uint8_t * out)73 enum tfm_plat_err_t tfm_plat_otp_read(enum tfm_otp_element_id_t id,
74 size_t out_len, uint8_t *out)
75 {
76 otp_cmd_t row_and_flags;
77 otp_cmd_t odd_byte_row_and_flags;
78 int rc = 0;
79 size_t out_len_checked;
80 uint8_t odd_byte_buff[2] = {0};
81 uint8_t *odd_byte_p;
82
83
84 if ((out_len == 0) || (otp_map[id].byte_len == 0)) {
85 return TFM_PLAT_ERR_SYSTEM_ERR;
86 }
87
88 if (id >= PLAT_OTP_ID_MAX) {
89 return TFM_PLAT_ERR_SYSTEM_ERR;
90 }
91
92 /* Output buffer can be bigger than the OTP element */
93 out_len_checked = (out_len < otp_map[id].byte_len) ? out_len : otp_map[id].byte_len;
94
95 /* Assemble command */
96 row_and_flags.flags = (OTP_BUFFER_MASK & otp_map[id].row_offset);
97 /* For LCS ECC is not used so it can be updated */
98 if (id != PLAT_OTP_ID_LCS) {
99 row_and_flags.flags |= OTP_IS_ECC_MASK;
100 }
101
102 /* Read OTP through API */
103 /* Bootrom API requires 2 byte alignment with ECC mode ON, handle odd byte separately */
104 if (out_len_checked % 2) {
105 /* Update len to be even */
106 out_len_checked -= 1;
107 /* Assemble the command for the odd byte, row number is incremented by (len in byte)/2 */
108 odd_byte_row_and_flags.flags = row_and_flags.flags + (out_len_checked / 2);
109 /* Set pointer to the last byte of the output (not the buffer) */
110 odd_byte_p = out + out_len_checked;
111
112 rc = rom_func_otp_access(&odd_byte_buff[0], 2, odd_byte_row_and_flags);
113 if (rc) {
114 return TFM_PLAT_ERR_SYSTEM_ERR;
115 }
116 memcpy(odd_byte_p, &odd_byte_buff[0], 1);
117 }
118 if (out_len_checked) {
119 rc = rom_func_otp_access(out, out_len_checked, row_and_flags);
120 if (rc) {
121 return TFM_PLAT_ERR_SYSTEM_ERR;
122 }
123 }
124
125 return TFM_PLAT_ERR_SUCCESS;
126
127 }
128
tfm_plat_otp_write(enum tfm_otp_element_id_t id,size_t in_len,const uint8_t * in)129 enum tfm_plat_err_t tfm_plat_otp_write(enum tfm_otp_element_id_t id,
130 size_t in_len, const uint8_t *in)
131 {
132 otp_cmd_t row_and_flags;
133 otp_cmd_t odd_byte_row_and_flags;
134 int rc = 0;
135 size_t in_len_checked;
136 uint8_t odd_byte_buff[2] = {0};
137 uint8_t *odd_byte_p;
138
139 if ((in_len == 0) || (otp_map[id].byte_len == 0)) {
140 return TFM_PLAT_ERR_SYSTEM_ERR;
141 }
142
143 if (id >= PLAT_OTP_ID_MAX) {
144 return TFM_PLAT_ERR_SYSTEM_ERR;
145 }
146
147 in_len_checked = (in_len < otp_map[id].byte_len) ? in_len : otp_map[id].byte_len;
148
149 /* Assemble command */
150 row_and_flags.flags = OTP_IS_WRITE_MASK |
151 (OTP_BUFFER_MASK & otp_map[id].row_offset);
152 /* For LCS ECC is not used so it can be updated */
153 if (id != PLAT_OTP_ID_LCS) {
154 row_and_flags.flags |= OTP_IS_ECC_MASK;
155 }
156
157 /* Write OTP through API */
158 /* Bootrom API requires 2 byte alignment with ECC mode ON, handle odd byte separately */
159 if (in_len_checked % 2) {
160 /* Update len to be even */
161 in_len_checked -= 1;
162 /* Assemble the command for the odd byte, row number is incremented by (len in byte)/2 */
163 odd_byte_row_and_flags.flags = row_and_flags.flags + (in_len_checked / 2);
164 /* Set pointer to the last byte of the input (not the buffer) */
165 odd_byte_p = in + in_len_checked;
166 memcpy(&odd_byte_buff[0], odd_byte_p, 1);
167
168 rc = rom_func_otp_access(&odd_byte_buff[0], 2, odd_byte_row_and_flags);
169 if (rc) {
170 return TFM_PLAT_ERR_SYSTEM_ERR;
171 }
172 }
173
174 if (in_len_checked) {
175 rc = rom_func_otp_access(in, in_len_checked, row_and_flags);
176 if (rc) {
177 return TFM_PLAT_ERR_SYSTEM_ERR;
178 }
179 }
180
181 return TFM_PLAT_ERR_SUCCESS;
182 }
183
tfm_plat_otp_get_size(enum tfm_otp_element_id_t id,size_t * size)184 enum tfm_plat_err_t tfm_plat_otp_get_size(enum tfm_otp_element_id_t id,
185 size_t *size)
186 {
187 if (id >= PLAT_OTP_ID_MAX) {
188 return TFM_PLAT_ERR_SYSTEM_ERR;
189 }
190 *size = otp_map[id].byte_len;
191 return TFM_PLAT_ERR_SUCCESS;
192 }
193
tfm_plat_otp_secure_provisioning_start(void)194 enum tfm_plat_err_t tfm_plat_otp_secure_provisioning_start(void)
195 {
196 return TFM_PLAT_ERR_SUCCESS;
197 }
198
tfm_plat_otp_secure_provisioning_finish(void)199 enum tfm_plat_err_t tfm_plat_otp_secure_provisioning_finish(void)
200 {
201
202 uint32_t row_count = 0;
203 uint8_t first_page_to_lock;
204 uint8_t last_page_to_lock;
205 uint8_t num_pages_to_lock;
206 otp_cmd_t row_and_flags;
207 uint8_t msg_buff[4] = {0};
208 uint32_t lock_config;
209 int rc = 0;
210
211 /* Count the number of allocated OTP rows, assuming continious usage */
212 for (int i = 0; i <= PLAT_OTP_ID_SECURE_DEBUG_PK; i++) {
213 row_count += otp_map[i].byte_len;
214 }
215
216 /* Get the pages to be locked */
217 first_page_to_lock = otp_map[0].row_offset / OTP_ROW_PER_PAGE;
218
219 last_page_to_lock = (otp_map[PLAT_OTP_ID_SECURE_DEBUG_PK].row_offset +
220 (otp_map[PLAT_OTP_ID_SECURE_DEBUG_PK].byte_len / 2)) /
221 0x40;
222 num_pages_to_lock = last_page_to_lock - first_page_to_lock + 1;
223
224 /* First and last 3 pages are already in use */
225 if ((first_page_to_lock < 3) || (last_page_to_lock > 60)) {
226 return TFM_PLAT_ERR_SYSTEM_ERR;
227 }
228
229 /* Assmble message */
230 /* Lock information encoded to the first 8 bits of lock register 1 */
231 lock_config = (OTP_DATA_PAGE0_LOCK1_LOCK_NS_BITS &
232 (OTP_DATA_PAGE0_LOCK1_LOCK_NS_VALUE_INACCESSIBLE <<
233 OTP_DATA_PAGE0_LOCK1_LOCK_NS_LSB)) |
234 (OTP_DATA_PAGE0_LOCK1_LOCK_BL_BITS &
235 (OTP_DATA_PAGE0_LOCK1_LOCK_BL_VALUE_INACCESSIBLE <<
236 OTP_DATA_PAGE0_LOCK1_LOCK_BL_LSB));
237 /* Triple majority vote */
238 msg_buff[0] = lock_config;
239 msg_buff[1] = lock_config;
240 msg_buff[2] = lock_config;
241
242 /* Lock pages, NS and BL code should not be able to access them */
243 for (int i = 0; i < num_pages_to_lock; i++) {
244 /* Assemble command */
245 row_and_flags.flags = OTP_IS_WRITE_MASK |
246 (OTP_BUFFER_MASK & (OTP_DATA_PAGE0_LOCK1_ROW +
247 ((i + first_page_to_lock) * 2)));
248
249 /* Write lock register PAGExx_LOCK1 */
250 rc = rom_func_otp_access(&msg_buff[0], 4, row_and_flags);
251 if (rc) {
252 return TFM_PLAT_ERR_SYSTEM_ERR;
253 }
254 }
255
256 return TFM_PLAT_ERR_SUCCESS;
257 }
258