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