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
11#if defined(USE_RRAM)
13#include "sw/device/silicon_creator/lib/drivers/rram_ctrl.h"
14#else
15#include "sw/device/silicon_creator/lib/drivers/flash_ctrl.h"
16#endif // USE_RRAM
17
18// ---------------------------------------------------------------------------
19// Internal: info page count per bank in the wire-format (bank, page) address
20// bridge (see `nvm_ctrl_info_page_lookup`).
21//
22// These are fixed constants independent of NVM technology: on-flash owner
23// config structs encode info pages as (bank, page) pairs assuming 2 banks of
24// 10 pages each, matching flash's original type-0 info partition layout.
25// `NVM_NUM_BANKS` must not be used here instead of `kNvmWireFormatBankCount`:
26// it is a hardware-derived value (1 for RRAM, which has no bank concept) and
27// would incorrectly reject bank-1 pages (BootData0/1, OwnerSlot0/1,
28// DiceCerts, ...) that owner configs still legitimately encode.
29// ---------------------------------------------------------------------------
30enum {
31 kNvmInfoPagesPerBank = 10,
32 kNvmWireFormatBankCount = 2,
33};
34
35// ---------------------------------------------------------------------------
36// Internal: mapping from nvm_info_page_t enum to a driver-level page pointer.
37// Order must match nvm_info_page_t definition in nvm_ctrl.h.
38// ---------------------------------------------------------------------------
39#if defined(USE_RRAM)
40static const rram_ctrl_info_page_t *const kPageTable[] = {
41 [kNvmInfoPageFactoryId] = &kRramCtrlInfoPageFactoryId,
42 [kNvmInfoPageCreatorSecret] = &kRramCtrlInfoPageCreatorSecret,
43 [kNvmInfoPageOwnerSecret] = &kRramCtrlInfoPageOwnerSecret,
44 [kNvmInfoPageWaferAuthSecret] = &kRramCtrlInfoPageWaferAuthSecret,
45 [kNvmInfoPageAttestationKeySeeds] = &kRramCtrlInfoPageAttestationKeySeeds,
46 [kNvmInfoPageOwnerReserved0] = &kRramCtrlInfoPageOwnerReserved0,
47 [kNvmInfoPageOwnerReserved1] = &kRramCtrlInfoPageOwnerReserved1,
48 [kNvmInfoPageOwnerReserved2] = &kRramCtrlInfoPageOwnerReserved2,
49 [kNvmInfoPageOwnerReserved3] = &kRramCtrlInfoPageOwnerReserved3,
50 [kNvmInfoPageFactoryCerts] = &kRramCtrlInfoPageFactoryCerts,
51 [kNvmInfoPageBootData0] = &kRramCtrlInfoPageBootData0,
52 [kNvmInfoPageBootData1] = &kRramCtrlInfoPageBootData1,
53 [kNvmInfoPageOwnerSlot0] = &kRramCtrlInfoPageOwnerSlot0,
54 [kNvmInfoPageOwnerSlot1] = &kRramCtrlInfoPageOwnerSlot1,
55 [kNvmInfoPageCreatorReserved0] = &kRramCtrlInfoPageCreatorReserved0,
56 [kNvmInfoPageOwnerReserved4] = &kRramCtrlInfoPageOwnerReserved4,
57 [kNvmInfoPageOwnerReserved5] = &kRramCtrlInfoPageOwnerReserved5,
58 [kNvmInfoPageOwnerReserved6] = &kRramCtrlInfoPageOwnerReserved6,
59 [kNvmInfoPageOwnerReserved7] = &kRramCtrlInfoPageOwnerReserved7,
60 [kNvmInfoPageDiceCerts] = &kRramCtrlInfoPageDiceCerts,
61};
62#else
63static const flash_ctrl_info_page_t *const kPageTable[] = {
64 [kNvmInfoPageFactoryId] = &kFlashCtrlInfoPageFactoryId,
65 [kNvmInfoPageCreatorSecret] = &kFlashCtrlInfoPageCreatorSecret,
66 [kNvmInfoPageOwnerSecret] = &kFlashCtrlInfoPageOwnerSecret,
67 [kNvmInfoPageWaferAuthSecret] = &kFlashCtrlInfoPageWaferAuthSecret,
68 [kNvmInfoPageAttestationKeySeeds] = &kFlashCtrlInfoPageAttestationKeySeeds,
69 [kNvmInfoPageOwnerReserved0] = &kFlashCtrlInfoPageOwnerReserved0,
70 [kNvmInfoPageOwnerReserved1] = &kFlashCtrlInfoPageOwnerReserved1,
71 [kNvmInfoPageOwnerReserved2] = &kFlashCtrlInfoPageOwnerReserved2,
72 [kNvmInfoPageOwnerReserved3] = &kFlashCtrlInfoPageOwnerReserved3,
73 [kNvmInfoPageFactoryCerts] = &kFlashCtrlInfoPageFactoryCerts,
74 [kNvmInfoPageBootData0] = &kFlashCtrlInfoPageBootData0,
75 [kNvmInfoPageBootData1] = &kFlashCtrlInfoPageBootData1,
76 [kNvmInfoPageOwnerSlot0] = &kFlashCtrlInfoPageOwnerSlot0,
77 [kNvmInfoPageOwnerSlot1] = &kFlashCtrlInfoPageOwnerSlot1,
78 [kNvmInfoPageCreatorReserved0] = &kFlashCtrlInfoPageCreatorReserved0,
79 [kNvmInfoPageOwnerReserved4] = &kFlashCtrlInfoPageOwnerReserved4,
80 [kNvmInfoPageOwnerReserved5] = &kFlashCtrlInfoPageOwnerReserved5,
81 [kNvmInfoPageOwnerReserved6] = &kFlashCtrlInfoPageOwnerReserved6,
82 [kNvmInfoPageOwnerReserved7] = &kFlashCtrlInfoPageOwnerReserved7,
83 [kNvmInfoPageDiceCerts] = &kFlashCtrlInfoPageDiceCerts,
84};
85#endif // USE_RRAM
86
87// The wire format reserves 2 banks x kNvmInfoPagesPerBank addressable slots,
88// independent of the number of banks the underlying technology actually has.
89static_assert(ARRAYSIZE(kPageTable) <= 2 * kNvmInfoPagesPerBank,
90 "More named pages than the wire format supports");
91
92// ---------------------------------------------------------------------------
93// Internal helpers
94// ---------------------------------------------------------------------------
95
96#if defined(USE_RRAM)
97static const rram_ctrl_info_page_t *page_ptr(nvm_info_page_t page) {
98 HARDENED_CHECK_LT((uint32_t)page, ARRAYSIZE(kPageTable));
99 return kPageTable[(uint32_t)page];
100}
101
102const rram_ctrl_info_page_t *nvm_ctrl_rram_page_info(nvm_info_page_t page) {
103 return page_ptr(page);
104}
105
106static rram_ctrl_perms_t perms_to_rram(nvm_page_perms_t p) {
107 return (rram_ctrl_perms_t){
108 .read = (uint32_t)p.read,
109 .write = (uint32_t)p.write,
110 };
111}
112
113static rram_ctrl_cfg_t cfg_to_rram(nvm_page_cfg_t c) {
114 return (rram_ctrl_cfg_t){
115 .scrambling = (uint32_t)c.scrambling,
116 .ecc = (uint32_t)c.ecc,
117 };
118}
119
120static nvm_page_cfg_t cfg_from_rram(rram_ctrl_cfg_t c) {
121 return (nvm_page_cfg_t){
122 .scrambling = c.scrambling,
123 .ecc = c.ecc,
124 .he = kMultiBitBool4False,
125 };
126}
127
128#else
129static const flash_ctrl_info_page_t *page_ptr(nvm_info_page_t page) {
130 HARDENED_CHECK_LT((uint32_t)page, ARRAYSIZE(kPageTable));
131 return kPageTable[(uint32_t)page];
132}
133
134static flash_ctrl_perms_t perms_to_flash(nvm_page_perms_t p) {
135 return (flash_ctrl_perms_t){
136 .read = (uint32_t)p.read,
137 .write = (uint32_t)p.write,
138 .erase = (uint32_t)p.erase,
139 };
140}
141
142static flash_ctrl_cfg_t cfg_to_flash(nvm_page_cfg_t c) {
143 return (flash_ctrl_cfg_t){
144 .scrambling = (uint32_t)c.scrambling,
145 .ecc = (uint32_t)c.ecc,
146 .he = (uint32_t)c.he,
147 };
148}
149
150static nvm_page_cfg_t cfg_from_flash(flash_ctrl_cfg_t c) {
151 return (nvm_page_cfg_t){
152 .scrambling = c.scrambling,
153 .ecc = c.ecc,
154 .he = c.he,
155 };
156}
157#endif // USE_RRAM
158
159// ---------------------------------------------------------------------------
160// Named constants
161// ---------------------------------------------------------------------------
162
163const nvm_page_perms_t kNvmPagePermsReadWrite = {.read = kMultiBitBool4True,
164 .write = kMultiBitBool4True,
165 .erase = kMultiBitBool4True};
166const nvm_page_perms_t kNvmPagePermsReadOnly = {.read = kMultiBitBool4True,
167 .write = kMultiBitBool4False,
168 .erase = kMultiBitBool4False};
169const nvm_page_perms_t kNvmPagePermsNone = {.read = kMultiBitBool4False,
170 .write = kMultiBitBool4False,
171 .erase = kMultiBitBool4False};
172const nvm_page_perms_t kNvmPagePermsErase = {.read = kMultiBitBool4False,
173 .write = kMultiBitBool4False,
174 .erase = kMultiBitBool4True};
175
176const nvm_page_cfg_t kNvmPageCfgScrambled = {.scrambling = kMultiBitBool4True,
177 .ecc = kMultiBitBool4True,
178 .he = kMultiBitBool4False};
179const nvm_page_cfg_t kNvmPageCfgPlain = {.scrambling = kMultiBitBool4False,
180 .ecc = kMultiBitBool4True,
181 .he = kMultiBitBool4False};
182const nvm_page_cfg_t kNvmPageCfgRaw = {.scrambling = kMultiBitBool4False,
183 .ecc = kMultiBitBool4False,
184 .he = kMultiBitBool4False};
185
186const nvm_page_cfg_t kNvmCertInfoPageCfg = {.scrambling = kMultiBitBool4True,
187 .ecc = kMultiBitBool4True,
188 .he = kMultiBitBool4False};
189const nvm_page_perms_t kNvmCertInfoPageCreatorAccess = {
190 .read = kMultiBitBool4True,
191 .write = kMultiBitBool4True,
192 .erase = kMultiBitBool4True};
193const nvm_page_perms_t kNvmCertInfoPageOwnerAccess = {
194 .read = kMultiBitBool4True,
195 .write = kMultiBitBool4False,
196 .erase = kMultiBitBool4False};
197
198// ---------------------------------------------------------------------------
199// Wire-format bridge
200// ---------------------------------------------------------------------------
201
202rom_error_t nvm_ctrl_info_page_lookup(uint8_t bank, uint8_t page,
203 nvm_info_page_t *out) {
204 if ((uint32_t)bank >= (uint32_t)kNvmWireFormatBankCount ||
205 (uint32_t)page >= (uint32_t)kNvmInfoPagesPerBank) {
206 return kErrorNvmCtrlInvalidInfoPage;
207 }
208 *out = (nvm_info_page_t)(bank * kNvmInfoPagesPerBank + page);
209 return kErrorOk;
210}
211
212static const nvm_info_page_t kNvmPagesNoOwnerAccess[] = {
213 kNvmInfoPageFactoryId, kNvmInfoPageCreatorSecret,
214 kNvmInfoPageOwnerSecret, kNvmInfoPageWaferAuthSecret,
215 kNvmInfoPageBootData0, kNvmInfoPageBootData1,
216 kNvmInfoPageCreatorReserved0,
217};
218
219enum {
220 kNvmPagesNoOwnerAccessCount = ARRAYSIZE(kNvmPagesNoOwnerAccess),
221};
222
223// ---------------------------------------------------------------------------
224// Technology-specific implementation.
225//
226// Every `nvm_ctrl_*` function declared in nvm_ctrl.h (besides the
227// tech-independent ones above and the cert-page helpers below, which are
228// built on top of these) is implemented once here for RRAM and once for
229// flash, rather than function-by-function, to keep each technology's full
230// behavior readable as a single, self-contained unit.
231// ---------------------------------------------------------------------------
232
233#if defined(USE_RRAM)
234
235void nvm_ctrl_init(void) {
236 rram_ctrl_init();
237 // `DEFAULT_REGION.RD_EN` resets to false in hardware, and boot data lives
238 // on an emulated info page (mapped onto the data partition), so it must be
239 // persistently enabled here rather than only bracketing individual
240 // operations like `nvm_ctrl_bootstrap_page_program` does.
241 rram_ctrl_data_default_perms_set((rram_ctrl_perms_t){
242 .read = kMultiBitBool4True,
243 .write = kMultiBitBool4False,
244 });
245 // Emulated info pages (e.g. OwnerSlot0/1, DiceCerts, BootData0/1) don't
246 // get individual permissions like real info pages; they all share Region
247 // B instead. Grant it read/write access so they can be written during
248 // boot. The grant covers Region B's whole reserved window, not just the
249 // pages the table in rram_ctrl.h currently assigns (`kRramCtrlEmulPageCountB`
250 // pages) -- everything up to the OTP tail, which must never be granted
251 // access -- so a page added there later needs no change here.
252 rram_ctrl_data_region_protect(
253 kRramCtrlEmulRegionB, kRramCtrlEmulPageBaseB,
254 kRramCtrlEmulPageEndB - kRramCtrlOtpPageCount - kRramCtrlEmulPageBaseB,
255 (rram_ctrl_perms_t){
256 .read = kMultiBitBool4True,
257 .write = kMultiBitBool4True,
258 },
259 rram_ctrl_data_default_cfg_get(), kHardenedBoolFalse);
260 // `CreatorReserved0` lives in Region A instead (see the comment on
261 // `kRramCtrlEmulRegionA`), so it needs its own, identical grant -- also
262 // covering the whole window, with no OTP tail to exclude here.
263 rram_ctrl_data_region_protect(kRramCtrlEmulRegionA, kRramCtrlEmulPageBaseA,
264 kRramCtrlEmulPageEndA - kRramCtrlEmulPageBaseA,
265 (rram_ctrl_perms_t){
266 .read = kMultiBitBool4True,
267 .write = kMultiBitBool4True,
268 },
269 rram_ctrl_data_default_cfg_get(),
271 // Two calls above, each unlocked, so 2 * `kRramCtrlSecMmioDataRegionProtect`
272 // writes total.
273 SEC_MMIO_WRITE_INCREMENT(2 * kRramCtrlSecMmioDataRegionProtect);
274}
275
276void nvm_ctrl_disable(void) { rram_ctrl_disable(); }
277
278rom_error_t nvm_ctrl_data_read(uint32_t addr, uint32_t word_count, void *data) {
279 return rram_ctrl_data_read(addr, word_count, data);
280}
281
282rom_error_t nvm_ctrl_data_write(uint32_t addr, uint32_t word_count,
283 const void *data) {
284 return rram_ctrl_data_write(addr, word_count, data);
285}
286
287rom_error_t nvm_ctrl_data_erase(uint32_t addr) {
288 // RRAM supports direct overwrite, so no separate hardware erase step is
289 // required before a write. Explicitly write the page to kNvmErasedWord
290 // (all-ones, matching flash's actual erased value) rather than leaving it
291 // untouched: callers rely on "erase resets a page to a known, blank
292 // state" (e.g. boot_data.c's erased-slot detection compares words against
293 // kNvmErasedWord), which a no-op can't provide, and old page contents
294 // would otherwise linger indefinitely after an explicit erase request.
295 // write_aligned() (called via rram_ctrl_data_write()) already chunks
296 // against the hardware's per-transaction word limit internally, so the
297 // whole page can be written in one call.
298 static_assert(kNvmErasedWord == UINT32_MAX,
299 "memset(..., 0xff, ...) below assumes an all-ones erased "
300 "value");
301 enum { kPageWords = NVM_BYTES_PER_PAGE / sizeof(uint32_t) };
302 uint32_t erased[kPageWords];
303 memset(erased, 0xff, sizeof(erased));
304 return rram_ctrl_data_write(addr, kPageWords, erased);
305}
306
307rom_error_t nvm_ctrl_data_erase_verify(uint32_t addr) {
308 (void)addr;
309 return kErrorOk;
310}
311
312rom_error_t nvm_ctrl_chip_erase(void) {
313 // Mirror flash's "erase both firmware banks" semantics: erase every usable
314 // page (see `NVM_SLOT_B_END_BYTES`), stopping before the emulated
315 // info-page region. That region -- like flash's separate INFO partition --
316 // must survive a chip erase since it backs OwnerSlot0/1, DiceCerts,
317 // BootData0/1, etc. Without this, bootstrap's initial CHIP_ERASE (which
318 // precedes every bootstrap session) was a no-op, leaving a previous
319 // image's data in any page the new image doesn't explicitly program.
320 //
321 // Slot A has its own separate reserved tail (see `NVM_SLOT_A_END_BYTES`),
322 // used for `CreatorReserved0`'s dedicated region; skip it for the same
323 // reason.
324 rram_ctrl_data_default_perms_set((rram_ctrl_perms_t){
325 .read = kMultiBitBool4True,
326 .write = kMultiBitBool4True,
327 });
328 // Erase every page regardless of earlier failures, rather than stopping at
329 // the first one: report the first error (if any), but still attempt every
330 // remaining page.
331 rom_error_t err = kErrorOk;
332 for (uint32_t addr = 0; addr < NVM_SLOT_B_END_BYTES;
333 addr += NVM_BYTES_PER_PAGE) {
334 if (addr >= NVM_SLOT_A_END_BYTES && addr < NVM_SLOT_B_START_BYTES) {
335 continue;
336 }
337 rom_error_t page_err = nvm_ctrl_data_erase(addr);
338 if (err == kErrorOk) {
339 err = page_err;
340 }
341 }
342 rram_ctrl_data_default_perms_set((rram_ctrl_perms_t){
343 .read = kMultiBitBool4False,
344 .write = kMultiBitBool4False,
345 });
346 return err;
347}
348
349rom_error_t nvm_ctrl_chip_erase_verify(void) { return kErrorOk; }
350
351rom_error_t nvm_ctrl_bootstrap_page_program(uint32_t addr, size_t byte_count,
352 uint8_t *data) {
353 enum {
354 kProgPageSize = NVM_PROG_PAGE_SIZE,
355 kProgPageMask = kProgPageSize - 1,
356 };
357
358 // Round up to next NVM word and fill missing bytes with 0xff.
359 size_t word_misalignment = byte_count & (NVM_BYTES_PER_WORD - 1);
360 if (word_misalignment > 0) {
361 size_t pad = NVM_BYTES_PER_WORD - word_misalignment;
362 for (size_t i = 0; i < pad; ++i) {
363 data[byte_count++] = 0xff;
364 }
365 }
366 size_t rem_word_count = byte_count / sizeof(uint32_t);
367
368 // Unlike flash, RRAM's memory protection also gates the FIFO-transaction
369 // read path used by `rram_ctrl_data_write()`'s internal read-modify-write
370 // of unaligned partial granules, so read must be enabled here in addition
371 // to write.
372 rram_ctrl_data_default_perms_set((rram_ctrl_perms_t){
373 .read = kMultiBitBool4True,
374 .write = kMultiBitBool4True,
375 });
376 // Split the write if addr is not `kProgPageSize`-aligned: first chunk fills
377 // up to the page boundary, second chunk starts at the aligned address (SPI
378 // PAGE_PROGRAM wrapping semantics). `rram_ctrl_data_write` itself handles
379 // any addr/word_count alignment, so no further splitting is needed within
380 // each chunk.
381 rom_error_t err_0 = kErrorOk;
382 size_t prog_page_misalignment = addr & kProgPageMask;
383 if (prog_page_misalignment > 0) {
384 size_t word_count =
385 (kProgPageSize - prog_page_misalignment) / sizeof(uint32_t);
386 if (word_count > rem_word_count) {
387 word_count = rem_word_count;
388 }
389 err_0 = rram_ctrl_data_write(addr, (uint32_t)word_count, data);
390 rem_word_count -= word_count;
391 data += word_count * sizeof(uint32_t);
392 addr &= ~(uint32_t)kProgPageMask;
393 }
394 rom_error_t err_1 = kErrorOk;
395 if (rem_word_count > 0) {
396 err_1 = rram_ctrl_data_write(addr, (uint32_t)rem_word_count, data);
397 }
398 rram_ctrl_data_default_perms_set((rram_ctrl_perms_t){
399 .read = kMultiBitBool4False,
400 .write = kMultiBitBool4False,
401 });
402 HARDENED_RETURN_IF_ERROR(err_0);
403 return err_1;
404}
405
406rom_error_t nvm_ctrl_bootstrap_sector_erase(uint32_t addr) {
407 // Bootstrap's SPI SECTOR_ERASE always covers a fixed 4 KiB region,
408 // independent of RRAM's own erase/write granularity (`NVM_BYTES_PER_PAGE`,
409 // 512 bytes); erase every page within that region so unwritten pages left
410 // over from a previous image don't linger (see `nvm_ctrl_data_erase()`).
411 enum { kSectorSizeBytes = 4096 };
412 addr &= ~(uint32_t)(kSectorSizeBytes - 1);
413 rram_ctrl_data_default_perms_set((rram_ctrl_perms_t){
414 .read = kMultiBitBool4True,
415 .write = kMultiBitBool4True,
416 });
417 // Erase every page regardless of earlier failures, rather than stopping at
418 // the first one: report the first error (if any), but still attempt every
419 // remaining page.
420 rom_error_t err = kErrorOk;
421 for (uint32_t offset = 0; offset < kSectorSizeBytes;
422 offset += NVM_BYTES_PER_PAGE) {
423 rom_error_t page_err = nvm_ctrl_data_erase(addr + offset);
424 if (err == kErrorOk) {
425 err = page_err;
426 }
427 }
428 rram_ctrl_data_default_perms_set((rram_ctrl_perms_t){
429 .read = kMultiBitBool4False,
430 .write = kMultiBitBool4False,
431 });
432 return err;
433}
434
435rom_error_t nvm_ctrl_info_read(nvm_info_page_t page, uint32_t offset,
436 uint32_t word_count, void *data) {
437 const rram_ctrl_info_page_t *p = page_ptr(page);
438 if (p->emulated) {
439 return rram_ctrl_data_read(p->page_id * NVM_BYTES_PER_PAGE + offset,
440 word_count, data);
441 }
442 return rram_ctrl_info_read(p, offset, word_count, data);
443}
444
445rom_error_t nvm_ctrl_info_read_zeros_on_read_error(nvm_info_page_t page,
446 uint32_t offset,
447 uint32_t word_count,
448 void *data) {
449 const rram_ctrl_info_page_t *p = page_ptr(page);
450 if (p->emulated) {
451 // Emulated pages are plain data-partition reads; there is no separate
452 // "zeros on read error" data-partition primitive to fall back to.
453 return rram_ctrl_data_read(p->page_id * NVM_BYTES_PER_PAGE + offset,
454 word_count, data);
455 }
456 return rram_ctrl_info_read_zeros_on_read_error(p, offset, word_count, data);
457}
458
459rom_error_t nvm_ctrl_info_write(nvm_info_page_t page, uint32_t offset,
460 uint32_t word_count, const void *data) {
461 const rram_ctrl_info_page_t *p = page_ptr(page);
462 if (p->emulated) {
463 return rram_ctrl_data_write(p->page_id * NVM_BYTES_PER_PAGE + offset,
464 word_count, data);
465 }
466 return rram_ctrl_info_write(p, offset, word_count, data);
467}
468
469rom_error_t nvm_ctrl_info_erase(nvm_info_page_t page) {
470 // RRAM has no erase primitive; this approximates flash's per-page erase by
471 // directly overwriting each page's contents with kNvmErasedWord (all-ones).
472 // Loops over `p->num_pages` since some entries (OwnerSlot0/1, DiceCerts,
473 // FactoryCerts) span more than one physical page.
474 static_assert(kNvmErasedWord == UINT32_MAX,
475 "memset(..., 0xff, ...) below assumes an all-ones erased "
476 "value");
477 const rram_ctrl_info_page_t *p = page_ptr(page);
478 enum { kPageWords = NVM_BYTES_PER_PAGE / sizeof(uint32_t) };
479 uint32_t erased[kPageWords];
480 memset(erased, 0xff, sizeof(erased));
481 if (p->emulated) {
482 // Emulated pages are addressed individually within the shared
483 // data-partition region: each physical page this entry spans is just a
484 // data-partition page.
485 for (uint32_t i = 0; i < p->num_pages; ++i) {
486 HARDENED_RETURN_IF_ERROR(rram_ctrl_data_write(
487 (p->page_id + i) * NVM_BYTES_PER_PAGE, kPageWords, erased));
488 }
489 return kErrorOk;
490 }
491 // Real info pages have no data-partition address; overwrite directly via
492 // rram_ctrl_info_write().
493 for (uint32_t i = 0; i < p->num_pages; ++i) {
494 HARDENED_RETURN_IF_ERROR(
495 rram_ctrl_info_write(p, i * NVM_BYTES_PER_PAGE, kPageWords, erased));
496 }
497 return kErrorOk;
498}
499
500void nvm_ctrl_data_default_perms_set(nvm_page_perms_t perms) {
501 rram_ctrl_data_default_perms_set(perms_to_rram(perms));
502}
503
504// clang-format off
505// NOTE (RRAM): the functions below are no-ops for emulated info pages, since
506// emulated pages share one memory-protection region with a fixed,
507// always-open policy and cannot be configured individually; see the TODO on
508// `rram_ctrl_info_page_t`. Callers at existing SEC_MMIO_WRITE_INCREMENT()
509// call sites assume a fixed number of actual register writes regardless of
510// which page they target; on an emulated page these no-ops perform fewer
511// (typically zero) writes than that, so `sec_mmio_check_counters()` will see
512// fewer actual writes than expected on any boot path that calls one of these
513// on an emulated page. Auditing/adjusting those call sites (in ownership.c,
514// boot_data.c, owner_block.c, cert/dice_chain.c, manuf/ft_personalize.c,
515// rom_ext.c, etc.) to account for real vs. emulated pages is a known,
516// accepted follow-up, not solved here.
517// clang-format on
518void nvm_ctrl_info_perms_set(nvm_info_page_t page, nvm_page_perms_t perms) {
519 const rram_ctrl_info_page_t *p = page_ptr(page);
520 if (p->emulated) {
521 return;
522 }
523 rram_ctrl_info_perms_set(p, perms_to_rram(perms));
524}
525
526void nvm_ctrl_data_default_cfg_set(nvm_page_cfg_t cfg) {
527 rram_ctrl_data_default_cfg_set(cfg_to_rram(cfg));
528}
529
530nvm_page_cfg_t nvm_ctrl_data_default_cfg_get(void) {
531 return cfg_from_rram(rram_ctrl_data_default_cfg_get());
532}
533
534nvm_page_cfg_t nvm_ctrl_boot_data_cfg_get(void) {
535 return cfg_from_rram(rram_ctrl_boot_data_cfg_get());
536}
537
538// See the RRAM emulated-page NOTE above `nvm_ctrl_info_perms_set`.
539void nvm_ctrl_info_cfg_set(nvm_info_page_t page, nvm_page_cfg_t cfg) {
540 const rram_ctrl_info_page_t *p = page_ptr(page);
541 if (p->emulated) {
542 return;
543 }
544 rram_ctrl_info_cfg_set(p, cfg_to_rram(cfg));
545}
546
547// See the RRAM emulated-page NOTE above `nvm_ctrl_info_perms_set`.
548void nvm_ctrl_info_cfg_lock(nvm_info_page_t page) {
549 const rram_ctrl_info_page_t *p = page_ptr(page);
550 if (p->emulated) {
551 return;
552 }
553 rram_ctrl_info_cfg_lock(p);
554}
555
556void nvm_ctrl_data_region_protect(uint32_t region, uint32_t page_offset,
557 uint32_t num_pages, nvm_page_perms_t perms,
558 nvm_page_cfg_t cfg, hardened_bool_t lock) {
559 rram_ctrl_data_region_protect(region, page_offset, num_pages,
560 perms_to_rram(perms), cfg_to_rram(cfg), lock);
561}
562
563void nvm_ctrl_bank_erase_perms_set(hardened_bool_t enable) {
564 // RRAM has no bank-erase concept; nothing to permission-gate.
565 (void)enable;
566}
567
568void nvm_ctrl_exec_set(uint32_t exec_val) { rram_ctrl_exec_set(exec_val); }
569
570void nvm_ctrl_creator_info_pages_lockdown(void) {
571 // Only 4 of the 7 pages below are real (non-emulated) RRAM info pages; see
572 // the loop below and the TODO on `rram_ctrl_info_page_t`.
573 SEC_MMIO_ASSERT_WRITE_INCREMENT(kNvmCtrlSecMmioCreatorInfoPagesLockdown, 8);
574 size_t i = 0, r = kNvmPagesNoOwnerAccessCount - 1;
575 for (; launder32(i) < kNvmPagesNoOwnerAccessCount &&
576 launder32(r) < kNvmPagesNoOwnerAccessCount;
577 ++i, --r) {
578 const rram_ctrl_info_page_t *p = page_ptr(kNvmPagesNoOwnerAccess[i]);
579 // Of the 7 pages above, 3 (BootData0, BootData1, CreatorReserved0) are
580 // emulated, so none get the real-page lockdown below. 2 of those
581 // (BootData0, BootData1) additionally share a memory-protection region
582 // with owner-needed pages (OwnerSlot0/1, DiceCerts, ...), so lockdown
583 // couldn't revoke creator access to them alone even if it tried;
584 // CreatorReserved0 has its own dedicated region (see
585 // `kRramCtrlEmulRegionA`) but isn't revoked either -- nothing here
586 // does region-level lockdown yet, only per-page. Known, accepted gap;
587 // see the TODO on `rram_ctrl_info_page_t`.
588 // `kNvmCtrlSecMmioCreatorInfoPagesLockdown` is tech-conditional in
589 // nvm_ctrl.h and already accounts for only the real pages below being
590 // written.
591 if (!p->emulated) {
592 rram_ctrl_info_page_lockdown(p);
593 }
594 }
595 HARDENED_CHECK_EQ(i, kNvmPagesNoOwnerAccessCount);
596 HARDENED_CHECK_EQ(r, SIZE_MAX);
597}
598
599#else // !USE_RRAM
600
601void nvm_ctrl_init(void) { flash_ctrl_init(); }
602
603void nvm_ctrl_disable(void) { flash_ctrl_disable(); }
604
605rom_error_t nvm_ctrl_data_read(uint32_t addr, uint32_t word_count, void *data) {
606 return flash_ctrl_data_read(addr, word_count, data);
607}
608
609rom_error_t nvm_ctrl_data_write(uint32_t addr, uint32_t word_count,
610 const void *data) {
611 return flash_ctrl_data_write(addr, word_count, data);
612}
613
614rom_error_t nvm_ctrl_data_erase(uint32_t addr) {
615 return flash_ctrl_data_erase(addr, kFlashCtrlEraseTypePage);
616}
617
618rom_error_t nvm_ctrl_data_erase_verify(uint32_t addr) {
619 return flash_ctrl_data_erase_verify(addr, kFlashCtrlEraseTypePage);
620}
621
622rom_error_t nvm_ctrl_chip_erase(void) {
623 flash_ctrl_bank_erase_perms_set(kHardenedBoolTrue);
624 rom_error_t err_0 = flash_ctrl_data_erase(0, kFlashCtrlEraseTypeBank);
625 rom_error_t err_1 = flash_ctrl_data_erase(FLASH_CTRL_PARAM_BYTES_PER_BANK,
626 kFlashCtrlEraseTypeBank);
627 flash_ctrl_bank_erase_perms_set(kHardenedBoolFalse);
628 HARDENED_RETURN_IF_ERROR(err_0);
629 return err_1;
630}
631
632rom_error_t nvm_ctrl_chip_erase_verify(void) {
633 rom_error_t err_0 = flash_ctrl_data_erase_verify(0, kFlashCtrlEraseTypeBank);
634 rom_error_t err_1 = flash_ctrl_data_erase_verify(
635 FLASH_CTRL_PARAM_BYTES_PER_BANK, kFlashCtrlEraseTypeBank);
636 HARDENED_RETURN_IF_ERROR(err_0);
637 return err_1;
638}
639
640rom_error_t nvm_ctrl_bootstrap_page_program(uint32_t addr, size_t byte_count,
641 uint8_t *data) {
642 static_assert((FLASH_CTRL_PARAM_BYTES_PER_WORD &
643 (FLASH_CTRL_PARAM_BYTES_PER_WORD - 1)) == 0,
644 "Bytes per NVM word must be a power of two.");
645 enum {
646 kWordMask = FLASH_CTRL_PARAM_BYTES_PER_WORD - 1,
647 kProgPageSize = NVM_PROG_PAGE_SIZE,
648 kProgPageMask = kProgPageSize - 1,
649 };
650
651 // Round up to next NVM word and fill missing bytes with 0xff.
652 size_t word_misalignment = byte_count & kWordMask;
653 if (word_misalignment > 0) {
654 size_t pad = FLASH_CTRL_PARAM_BYTES_PER_WORD - word_misalignment;
655 for (size_t i = 0; i < pad; ++i) {
656 data[byte_count++] = 0xff;
657 }
658 }
659 size_t rem_word_count = byte_count / sizeof(uint32_t);
660
661 flash_ctrl_data_default_perms_set((flash_ctrl_perms_t){
662 .read = kMultiBitBool4False,
663 .write = kMultiBitBool4True,
664 .erase = kMultiBitBool4False,
665 });
666 // Split the write if addr is not `kProgPageSize`-aligned: first chunk fills
667 // up to the page boundary, second chunk starts at the aligned address (SPI
668 // PAGE_PROGRAM wrapping semantics).
669 rom_error_t err_0 = kErrorOk;
670 size_t prog_page_misalignment = addr & kProgPageMask;
671 if (prog_page_misalignment > 0) {
672 size_t word_count =
673 (kProgPageSize - prog_page_misalignment) / sizeof(uint32_t);
674 if (word_count > rem_word_count) {
675 word_count = rem_word_count;
676 }
677 err_0 = flash_ctrl_data_write(addr, word_count, data);
678 rem_word_count -= word_count;
679 data += word_count * sizeof(uint32_t);
680 addr &= ~(uint32_t)kProgPageMask;
681 }
682 rom_error_t err_1 = kErrorOk;
683 if (rem_word_count > 0) {
684 err_1 = flash_ctrl_data_write(addr, rem_word_count, data);
685 }
686 flash_ctrl_data_default_perms_set((flash_ctrl_perms_t){
687 .read = kMultiBitBool4False,
688 .write = kMultiBitBool4False,
689 .erase = kMultiBitBool4False,
690 });
691 HARDENED_RETURN_IF_ERROR(err_0);
692 return err_1;
693}
694
695rom_error_t nvm_ctrl_bootstrap_sector_erase(uint32_t addr) {
696 static_assert(FLASH_CTRL_PARAM_BYTES_PER_PAGE == 2048,
697 "Page size must be 2 KiB");
698 enum { kSectorAddrMask = ~UINT32_C(4096) + 1 };
699 addr &= kSectorAddrMask;
700 flash_ctrl_data_default_perms_set((flash_ctrl_perms_t){
701 .read = kMultiBitBool4False,
702 .write = kMultiBitBool4False,
703 .erase = kMultiBitBool4True,
704 });
705 rom_error_t err_0 = flash_ctrl_data_erase(addr, kFlashCtrlEraseTypePage);
706 rom_error_t err_1 = flash_ctrl_data_erase(
707 addr + FLASH_CTRL_PARAM_BYTES_PER_PAGE, kFlashCtrlEraseTypePage);
708 flash_ctrl_data_default_perms_set((flash_ctrl_perms_t){
709 .read = kMultiBitBool4False,
710 .write = kMultiBitBool4False,
711 .erase = kMultiBitBool4False,
712 });
713 HARDENED_RETURN_IF_ERROR(err_0);
714 return err_1;
715}
716
717rom_error_t nvm_ctrl_info_read(nvm_info_page_t page, uint32_t offset,
718 uint32_t word_count, void *data) {
719 return flash_ctrl_info_read(page_ptr(page), offset, word_count, data);
720}
721
722rom_error_t nvm_ctrl_info_read_zeros_on_read_error(nvm_info_page_t page,
723 uint32_t offset,
724 uint32_t word_count,
725 void *data) {
726 return flash_ctrl_info_read_zeros_on_read_error(page_ptr(page), offset,
727 word_count, data);
728}
729
730rom_error_t nvm_ctrl_info_write(nvm_info_page_t page, uint32_t offset,
731 uint32_t word_count, const void *data) {
732 return flash_ctrl_info_write(page_ptr(page), offset, word_count, data);
733}
734
735rom_error_t nvm_ctrl_info_erase(nvm_info_page_t page) {
736 return flash_ctrl_info_erase(page_ptr(page), kFlashCtrlEraseTypePage);
737}
738
739void nvm_ctrl_data_default_perms_set(nvm_page_perms_t perms) {
740 flash_ctrl_data_default_perms_set(perms_to_flash(perms));
741}
742
743void nvm_ctrl_info_perms_set(nvm_info_page_t page, nvm_page_perms_t perms) {
744 flash_ctrl_info_perms_set(page_ptr(page), perms_to_flash(perms));
745}
746
747void nvm_ctrl_data_default_cfg_set(nvm_page_cfg_t cfg) {
748 flash_ctrl_data_default_cfg_set(cfg_to_flash(cfg));
749}
750
751nvm_page_cfg_t nvm_ctrl_data_default_cfg_get(void) {
752 return cfg_from_flash(flash_ctrl_data_default_cfg_get());
753}
754
755nvm_page_cfg_t nvm_ctrl_boot_data_cfg_get(void) {
756 return cfg_from_flash(flash_ctrl_boot_data_cfg_get());
757}
758
759void nvm_ctrl_info_cfg_set(nvm_info_page_t page, nvm_page_cfg_t cfg) {
760 flash_ctrl_info_cfg_set(page_ptr(page), cfg_to_flash(cfg));
761}
762
763void nvm_ctrl_info_cfg_lock(nvm_info_page_t page) {
764 flash_ctrl_info_cfg_lock(page_ptr(page));
765}
766
767void nvm_ctrl_data_region_protect(uint32_t region, uint32_t page_offset,
768 uint32_t num_pages, nvm_page_perms_t perms,
769 nvm_page_cfg_t cfg, hardened_bool_t lock) {
770 flash_ctrl_data_region_protect(region, page_offset, num_pages,
771 perms_to_flash(perms), cfg_to_flash(cfg),
772 lock);
773}
774
775void nvm_ctrl_bank_erase_perms_set(hardened_bool_t enable) {
776 flash_ctrl_bank_erase_perms_set(enable);
777}
778
779void nvm_ctrl_exec_set(uint32_t exec_val) { flash_ctrl_exec_set(exec_val); }
780
781void nvm_ctrl_creator_info_pages_lockdown(void) {
782 SEC_MMIO_ASSERT_WRITE_INCREMENT(kNvmCtrlSecMmioCreatorInfoPagesLockdown,
783 2 * kNvmPagesNoOwnerAccessCount);
784 size_t i = 0, r = kNvmPagesNoOwnerAccessCount - 1;
785 for (; launder32(i) < kNvmPagesNoOwnerAccessCount &&
786 launder32(r) < kNvmPagesNoOwnerAccessCount;
787 ++i, --r) {
788 flash_ctrl_info_page_lockdown(page_ptr(kNvmPagesNoOwnerAccess[i]));
789 }
790 HARDENED_CHECK_EQ(i, kNvmPagesNoOwnerAccessCount);
791 HARDENED_CHECK_EQ(r, SIZE_MAX);
792}
793
794#endif // USE_RRAM
795
796void nvm_ctrl_cert_info_page_creator_cfg(nvm_info_page_t page) {
797 SEC_MMIO_ASSERT_WRITE_INCREMENT(kNvmCtrlSecMmioCertInfoPageCreatorCfg, 2);
798 nvm_ctrl_info_cfg_set(page, kNvmCertInfoPageCfg);
799 nvm_ctrl_info_perms_set(page, kNvmCertInfoPageCreatorAccess);
800}
801
802void nvm_ctrl_cert_info_page_owner_restrict(nvm_info_page_t page) {
803 SEC_MMIO_ASSERT_WRITE_INCREMENT(kNvmCtrlSecMmioCertInfoPageOwnerRestrict, 2);
804 nvm_ctrl_info_perms_set(page, kNvmCertInfoPageOwnerAccess);
805 nvm_ctrl_info_cfg_lock(page);
806}