Software APIs
nvm_ctrl.c
1// Copyright lowRISC contributors (OpenTitan project).
2// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3// SPDX-License-Identifier: Apache-2.0
4
5#include "sw/device/silicon_creator/lib/nvm_ctrl.h"
6
10#include "sw/device/silicon_creator/lib/drivers/flash_ctrl.h"
11
12// ---------------------------------------------------------------------------
13// Internal: info page count per bank in the type-0 info partition.
14// ---------------------------------------------------------------------------
15enum { kNvmInfoPagesPerBank = FLASH_CTRL_PARAM_NUM_INFOS0 };
16
17// ---------------------------------------------------------------------------
18// Internal: mapping from nvm_info_page_t enum to flash_ctrl page pointer.
19// Order must match nvm_info_page_t definition in nvm_ctrl.h.
20// ---------------------------------------------------------------------------
21static const flash_ctrl_info_page_t *const kPageTable[] = {
22 [kNvmInfoPageFactoryId] = &kFlashCtrlInfoPageFactoryId,
23 [kNvmInfoPageCreatorSecret] = &kFlashCtrlInfoPageCreatorSecret,
24 [kNvmInfoPageOwnerSecret] = &kFlashCtrlInfoPageOwnerSecret,
25 [kNvmInfoPageWaferAuthSecret] = &kFlashCtrlInfoPageWaferAuthSecret,
26 [kNvmInfoPageAttestationKeySeeds] = &kFlashCtrlInfoPageAttestationKeySeeds,
27 [kNvmInfoPageOwnerReserved0] = &kFlashCtrlInfoPageOwnerReserved0,
28 [kNvmInfoPageOwnerReserved1] = &kFlashCtrlInfoPageOwnerReserved1,
29 [kNvmInfoPageOwnerReserved2] = &kFlashCtrlInfoPageOwnerReserved2,
30 [kNvmInfoPageOwnerReserved3] = &kFlashCtrlInfoPageOwnerReserved3,
31 [kNvmInfoPageFactoryCerts] = &kFlashCtrlInfoPageFactoryCerts,
32 [kNvmInfoPageBootData0] = &kFlashCtrlInfoPageBootData0,
33 [kNvmInfoPageBootData1] = &kFlashCtrlInfoPageBootData1,
34 [kNvmInfoPageOwnerSlot0] = &kFlashCtrlInfoPageOwnerSlot0,
35 [kNvmInfoPageOwnerSlot1] = &kFlashCtrlInfoPageOwnerSlot1,
36 [kNvmInfoPageCreatorReserved0] = &kFlashCtrlInfoPageCreatorReserved0,
37 [kNvmInfoPageOwnerReserved4] = &kFlashCtrlInfoPageOwnerReserved4,
38 [kNvmInfoPageOwnerReserved5] = &kFlashCtrlInfoPageOwnerReserved5,
39 [kNvmInfoPageOwnerReserved6] = &kFlashCtrlInfoPageOwnerReserved6,
40 [kNvmInfoPageOwnerReserved7] = &kFlashCtrlInfoPageOwnerReserved7,
41 [kNvmInfoPageDiceCerts] = &kFlashCtrlInfoPageDiceCerts,
42};
43
44// Software page count must not exceed hardware capacity
45static_assert(ARRAYSIZE(kPageTable) <= NVM_NUM_BANKS * kNvmInfoPagesPerBank,
46 "More named pages than hardware supports");
47
48// ---------------------------------------------------------------------------
49// Internal helpers
50// ---------------------------------------------------------------------------
51
52static const flash_ctrl_info_page_t *page_ptr(nvm_info_page_t page) {
53 HARDENED_CHECK_LT((uint32_t)page, ARRAYSIZE(kPageTable));
54 return kPageTable[(uint32_t)page];
55}
56
57static flash_ctrl_perms_t perms_to_flash(nvm_page_perms_t p) {
58 return (flash_ctrl_perms_t){
59 .read = (uint32_t)p.read,
60 .write = (uint32_t)p.write,
61 .erase = (uint32_t)p.erase,
62 };
63}
64
65static flash_ctrl_cfg_t cfg_to_flash(nvm_page_cfg_t c) {
66 return (flash_ctrl_cfg_t){
67 .scrambling = (uint32_t)c.scrambling,
68 .ecc = (uint32_t)c.ecc,
69 .he = (uint32_t)c.he,
70 };
71}
72
73static nvm_page_cfg_t cfg_from_flash(flash_ctrl_cfg_t c) {
74 return (nvm_page_cfg_t){
75 .scrambling = (multi_bit_bool_t)c.scrambling,
76 .ecc = (multi_bit_bool_t)c.ecc,
77 .he = (multi_bit_bool_t)c.he,
78 };
79}
80
81// ---------------------------------------------------------------------------
82// Named constants
83// ---------------------------------------------------------------------------
84
85const nvm_page_perms_t kNvmPagePermsReadWrite = {.read = kMultiBitBool4True,
86 .write = kMultiBitBool4True,
87 .erase = kMultiBitBool4True};
88const nvm_page_perms_t kNvmPagePermsReadOnly = {.read = kMultiBitBool4True,
89 .write = kMultiBitBool4False,
90 .erase = kMultiBitBool4False};
91const nvm_page_perms_t kNvmPagePermsNone = {.read = kMultiBitBool4False,
92 .write = kMultiBitBool4False,
93 .erase = kMultiBitBool4False};
94const nvm_page_perms_t kNvmPagePermsErase = {.read = kMultiBitBool4False,
95 .write = kMultiBitBool4False,
96 .erase = kMultiBitBool4True};
97
98const nvm_page_cfg_t kNvmPageCfgScrambled = {.scrambling = kMultiBitBool4True,
99 .ecc = kMultiBitBool4True,
100 .he = kMultiBitBool4False};
101const nvm_page_cfg_t kNvmPageCfgPlain = {.scrambling = kMultiBitBool4False,
102 .ecc = kMultiBitBool4True,
103 .he = kMultiBitBool4False};
104const nvm_page_cfg_t kNvmPageCfgRaw = {.scrambling = kMultiBitBool4False,
105 .ecc = kMultiBitBool4False,
106 .he = kMultiBitBool4False};
107
108const nvm_page_cfg_t kNvmCertInfoPageCfg = {.scrambling = kMultiBitBool4True,
109 .ecc = kMultiBitBool4True,
110 .he = kMultiBitBool4False};
111const nvm_page_perms_t kNvmCertInfoPageCreatorAccess = {
112 .read = kMultiBitBool4True,
113 .write = kMultiBitBool4True,
114 .erase = kMultiBitBool4True};
115const nvm_page_perms_t kNvmCertInfoPageOwnerAccess = {
116 .read = kMultiBitBool4True,
117 .write = kMultiBitBool4False,
118 .erase = kMultiBitBool4False};
119
120// ---------------------------------------------------------------------------
121// Lifecycle
122// ---------------------------------------------------------------------------
123
124void nvm_ctrl_init(void) { flash_ctrl_init(); }
125void nvm_ctrl_disable(void) { flash_ctrl_disable(); }
126
127// ---------------------------------------------------------------------------
128// Wire-format bridge
129// ---------------------------------------------------------------------------
130
131rom_error_t nvm_ctrl_info_page_lookup(uint8_t bank, uint8_t page,
132 nvm_info_page_t *out) {
133 if ((uint32_t)bank >= (uint32_t)NVM_NUM_BANKS ||
134 (uint32_t)page >= (uint32_t)kNvmInfoPagesPerBank) {
135 return kErrorNvmCtrlInvalidInfoPage;
136 }
137 *out = (nvm_info_page_t)(bank * kNvmInfoPagesPerBank + page);
138 return kErrorOk;
139}
140
141// ---------------------------------------------------------------------------
142// Data partition I/O
143// ---------------------------------------------------------------------------
144
145rom_error_t nvm_ctrl_data_read(uint32_t addr, uint32_t word_count, void *data) {
146 return flash_ctrl_data_read(addr, word_count, data);
147}
148
149rom_error_t nvm_ctrl_data_write(uint32_t addr, uint32_t word_count,
150 const void *data) {
151 return flash_ctrl_data_write(addr, word_count, data);
152}
153
154rom_error_t nvm_ctrl_data_erase(uint32_t addr) {
155 return flash_ctrl_data_erase(addr, kFlashCtrlEraseTypePage);
156}
157
158rom_error_t nvm_ctrl_data_erase_verify(uint32_t addr) {
159 return flash_ctrl_data_erase_verify(addr, kFlashCtrlEraseTypePage);
160}
161
162rom_error_t nvm_ctrl_chip_erase(void) {
163 flash_ctrl_bank_erase_perms_set(kHardenedBoolTrue);
164 rom_error_t err_0 = flash_ctrl_data_erase(0, kFlashCtrlEraseTypeBank);
165 rom_error_t err_1 = flash_ctrl_data_erase(FLASH_CTRL_PARAM_BYTES_PER_BANK,
166 kFlashCtrlEraseTypeBank);
167 flash_ctrl_bank_erase_perms_set(kHardenedBoolFalse);
168 HARDENED_RETURN_IF_ERROR(err_0);
169 return err_1;
170}
171
172rom_error_t nvm_ctrl_chip_erase_verify(void) {
173 rom_error_t err_0 = flash_ctrl_data_erase_verify(0, kFlashCtrlEraseTypeBank);
174 rom_error_t err_1 = flash_ctrl_data_erase_verify(
175 FLASH_CTRL_PARAM_BYTES_PER_BANK, kFlashCtrlEraseTypeBank);
176 HARDENED_RETURN_IF_ERROR(err_0);
177 return err_1;
178}
179
180rom_error_t nvm_ctrl_page_program(uint32_t addr, size_t byte_count,
181 uint8_t *data) {
182 static_assert((FLASH_CTRL_PARAM_BYTES_PER_WORD &
183 (FLASH_CTRL_PARAM_BYTES_PER_WORD - 1)) == 0,
184 "Bytes per NVM word must be a power of two.");
185 enum {
186 kWordMask = FLASH_CTRL_PARAM_BYTES_PER_WORD - 1,
187 kProgPageSize = 256,
188 kProgPageMask = kProgPageSize - 1,
189 };
190
191 // Round up to next NVM word and fill missing bytes with 0xff.
192 size_t word_misalignment = byte_count & kWordMask;
193 if (word_misalignment > 0) {
194 size_t pad = FLASH_CTRL_PARAM_BYTES_PER_WORD - word_misalignment;
195 for (size_t i = 0; i < pad; ++i) {
196 data[byte_count++] = 0xff;
197 }
198 }
199 size_t rem_word_count = byte_count / sizeof(uint32_t);
200
201 flash_ctrl_data_default_perms_set((flash_ctrl_perms_t){
202 .read = kMultiBitBool4False,
203 .write = kMultiBitBool4True,
204 .erase = kMultiBitBool4False,
205 });
206 // Split the write if addr is not 256-byte aligned: first chunk fills up to
207 // the 256-byte boundary, second chunk starts at the aligned address (SPI
208 // PAGE_PROGRAM wrapping semantics).
209 rom_error_t err_0 = kErrorOk;
210 size_t prog_page_misalignment = addr & kProgPageMask;
211 if (prog_page_misalignment > 0) {
212 size_t word_count =
213 (kProgPageSize - prog_page_misalignment) / sizeof(uint32_t);
214 if (word_count > rem_word_count) {
215 word_count = rem_word_count;
216 }
217 err_0 = flash_ctrl_data_write(addr, word_count, data);
218 rem_word_count -= word_count;
219 data += word_count * sizeof(uint32_t);
220 addr &= ~(uint32_t)kProgPageMask;
221 }
222 rom_error_t err_1 = kErrorOk;
223 if (rem_word_count > 0) {
224 err_1 = flash_ctrl_data_write(addr, rem_word_count, data);
225 }
226 flash_ctrl_data_default_perms_set((flash_ctrl_perms_t){
227 .read = kMultiBitBool4False,
228 .write = kMultiBitBool4False,
229 .erase = kMultiBitBool4False,
230 });
231 HARDENED_RETURN_IF_ERROR(err_0);
232 return err_1;
233}
234
235rom_error_t nvm_ctrl_sector_erase(uint32_t addr) {
236 static_assert(FLASH_CTRL_PARAM_BYTES_PER_PAGE == 2048,
237 "Page size must be 2 KiB");
238 enum { kSectorAddrMask = ~UINT32_C(4096) + 1 };
239 addr &= kSectorAddrMask;
240 flash_ctrl_data_default_perms_set((flash_ctrl_perms_t){
241 .read = kMultiBitBool4False,
242 .write = kMultiBitBool4False,
243 .erase = kMultiBitBool4True,
244 });
245 rom_error_t err_0 = flash_ctrl_data_erase(addr, kFlashCtrlEraseTypePage);
246 rom_error_t err_1 = flash_ctrl_data_erase(
247 addr + FLASH_CTRL_PARAM_BYTES_PER_PAGE, kFlashCtrlEraseTypePage);
248 flash_ctrl_data_default_perms_set((flash_ctrl_perms_t){
249 .read = kMultiBitBool4False,
250 .write = kMultiBitBool4False,
251 .erase = kMultiBitBool4False,
252 });
253 HARDENED_RETURN_IF_ERROR(err_0);
254 return err_1;
255}
256
257// ---------------------------------------------------------------------------
258// Info page I/O
259// ---------------------------------------------------------------------------
260
261rom_error_t nvm_ctrl_info_read(nvm_info_page_t page, uint32_t offset,
262 uint32_t word_count, void *data) {
263 return flash_ctrl_info_read(page_ptr(page), offset, word_count, data);
264}
265
266rom_error_t nvm_ctrl_info_read_zeros_on_read_error(nvm_info_page_t page,
267 uint32_t offset,
268 uint32_t word_count,
269 void *data) {
270 return flash_ctrl_info_read_zeros_on_read_error(page_ptr(page), offset,
271 word_count, data);
272}
273
274rom_error_t nvm_ctrl_info_write(nvm_info_page_t page, uint32_t offset,
275 uint32_t word_count, const void *data) {
276 return flash_ctrl_info_write(page_ptr(page), offset, word_count, data);
277}
278
279rom_error_t nvm_ctrl_info_erase(nvm_info_page_t page) {
280 return flash_ctrl_info_erase(page_ptr(page), kFlashCtrlEraseTypePage);
281}
282
283// ---------------------------------------------------------------------------
284// Permissions and configuration
285// ---------------------------------------------------------------------------
286
287void nvm_ctrl_data_default_perms_set(nvm_page_perms_t perms) {
288 flash_ctrl_data_default_perms_set(perms_to_flash(perms));
289}
290
291void nvm_ctrl_info_perms_set(nvm_info_page_t page, nvm_page_perms_t perms) {
292 flash_ctrl_info_perms_set(page_ptr(page), perms_to_flash(perms));
293}
294
295void nvm_ctrl_data_default_cfg_set(nvm_page_cfg_t cfg) {
296 flash_ctrl_data_default_cfg_set(cfg_to_flash(cfg));
297}
298
299nvm_page_cfg_t nvm_ctrl_data_default_cfg_get(void) {
300 return cfg_from_flash(flash_ctrl_data_default_cfg_get());
301}
302
303nvm_page_cfg_t nvm_ctrl_boot_data_cfg_get(void) {
304 return cfg_from_flash(flash_ctrl_boot_data_cfg_get());
305}
306
307void nvm_ctrl_info_cfg_set(nvm_info_page_t page, nvm_page_cfg_t cfg) {
308 flash_ctrl_info_cfg_set(page_ptr(page), cfg_to_flash(cfg));
309}
310
311void nvm_ctrl_info_cfg_lock(nvm_info_page_t page) {
312 flash_ctrl_info_cfg_lock(page_ptr(page));
313}
314
315// ---------------------------------------------------------------------------
316// Data region protection
317// ---------------------------------------------------------------------------
318
319void nvm_ctrl_data_region_protect(uint32_t region, uint32_t page_offset,
320 uint32_t num_pages, nvm_page_perms_t perms,
321 nvm_page_cfg_t cfg, hardened_bool_t lock) {
322 flash_ctrl_data_region_protect(region, page_offset, num_pages,
323 perms_to_flash(perms), cfg_to_flash(cfg),
324 lock);
325}
326
327void nvm_ctrl_bank_erase_perms_set(hardened_bool_t enable) {
328 flash_ctrl_bank_erase_perms_set(enable);
329}
330
331void nvm_ctrl_exec_set(uint32_t exec_val) { flash_ctrl_exec_set(exec_val); }
332
333// ---------------------------------------------------------------------------
334// Lockdown and certificate page management
335// ---------------------------------------------------------------------------
336
337static const nvm_info_page_t kNvmPagesNoOwnerAccess[] = {
338 kNvmInfoPageFactoryId, kNvmInfoPageCreatorSecret,
339 kNvmInfoPageOwnerSecret, kNvmInfoPageWaferAuthSecret,
340 kNvmInfoPageBootData0, kNvmInfoPageBootData1,
341 kNvmInfoPageCreatorReserved0,
342};
343
344enum {
345 kNvmPagesNoOwnerAccessCount = ARRAYSIZE(kNvmPagesNoOwnerAccess),
346};
347
348void nvm_ctrl_creator_info_pages_lockdown(void) {
349 SEC_MMIO_ASSERT_WRITE_INCREMENT(kNvmCtrlSecMmioCreatorInfoPagesLockdown,
350 2 * kNvmPagesNoOwnerAccessCount);
351 size_t i = 0, r = kNvmPagesNoOwnerAccessCount - 1;
352 for (; launder32(i) < kNvmPagesNoOwnerAccessCount &&
353 launder32(r) < kNvmPagesNoOwnerAccessCount;
354 ++i, --r) {
355 flash_ctrl_info_page_lockdown(page_ptr(kNvmPagesNoOwnerAccess[i]));
356 }
357 HARDENED_CHECK_EQ(i, kNvmPagesNoOwnerAccessCount);
358 HARDENED_CHECK_EQ(r, SIZE_MAX);
359}
360
361void nvm_ctrl_cert_info_page_creator_cfg(nvm_info_page_t page) {
362 SEC_MMIO_ASSERT_WRITE_INCREMENT(kNvmCtrlSecMmioCertInfoPageCreatorCfg, 2);
363 nvm_ctrl_info_cfg_set(page, kNvmCertInfoPageCfg);
364 nvm_ctrl_info_perms_set(page, kNvmCertInfoPageCreatorAccess);
365}
366
367void nvm_ctrl_cert_info_page_owner_restrict(nvm_info_page_t page) {
368 SEC_MMIO_ASSERT_WRITE_INCREMENT(kNvmCtrlSecMmioCertInfoPageOwnerRestrict, 2);
369 nvm_ctrl_info_perms_set(page, kNvmCertInfoPageOwnerAccess);
370 nvm_ctrl_info_cfg_lock(page);
371}