Software APIs
boot_data.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/boot_data.h"
6 
7 #include <stddef.h>
8 #include <stdint.h>
9 
13 #include "sw/device/silicon_creator/lib/drivers/flash_ctrl.h"
14 #include "sw/device/silicon_creator/lib/drivers/hmac.h"
15 #include "sw/device/silicon_creator/lib/drivers/otp.h"
16 #include "sw/device/silicon_creator/lib/error.h"
17 
18 #include "flash_ctrl_regs.h"
20 #include "otp_ctrl_regs.h"
21 
22 static_assert(kBootDataValidEntry ==
23  ((uint64_t)kFlashCtrlErasedWord << 32 | kFlashCtrlErasedWord),
24  "kBootDataValidEntry words must be kFlashCtrlErasedWord");
25 static_assert(kBootDataEntriesPerPage ==
26  FLASH_CTRL_PARAM_BYTES_PER_PAGE / sizeof(boot_data_t),
27  "Number of boot data entries per page is incorrect");
28 static_assert(sizeof(boot_data_t) % FLASH_CTRL_PARAM_BYTES_PER_WORD == 0,
29  "Size of `boot_data_t` must be a multiple of flash word size.");
30 static_assert(!(FLASH_CTRL_PARAM_BYTES_PER_PAGE &
31  (FLASH_CTRL_PARAM_BYTES_PER_PAGE - 1)),
32  "Size of a flash page must be a power of two.");
33 static_assert(!(sizeof(boot_data_t) & (sizeof(boot_data_t) - 1)),
34  "Size of `boot_data_t` must be a power of two.");
35 OT_ASSERT_MEMBER_SIZE(boot_data_t, is_valid, FLASH_CTRL_PARAM_BYTES_PER_WORD);
36 static_assert(offsetof(boot_data_t, is_valid) %
37  FLASH_CTRL_PARAM_BYTES_PER_WORD ==
38  0,
39  "`is_valid` must be flash word aligned.");
40 
41 enum {
42  /**
43  * Number of boot data flash info pages.
44  */
45  kPageCount = 2,
46 };
47 /**
48  * Boot data flash info pages.
49  */
50 static const flash_ctrl_info_page_t *kPages[kPageCount] = {
51  &kFlashCtrlInfoPageBootData0,
52  &kFlashCtrlInfoPageBootData1,
53 };
54 
55 /**
56  * Computes the SHA-256 digest of a boot data entry.
57  *
58  * The region covered by this digest starts immediately after the `identifier`
59  * field and ends at the end of the entry.
60  *
61  * @param boot_data A boot data entry.
62  * @param[out] digest Digest of the boot data entry.
63  * @return The result of the operation.
64  */
65 static void boot_data_digest_compute(const void *boot_data,
66  hmac_digest_t *digest) {
67  enum {
68  kDigestRegionOffset = sizeof((boot_data_t){0}.digest),
69  kDigestRegionSize = sizeof(boot_data_t) - kDigestRegionOffset,
70  };
71  static_assert(offsetof(boot_data_t, digest) == 0,
72  "`digest` must be the first field of `boot_data_t`.");
73 
74  hmac_sha256((const char *)boot_data + kDigestRegionOffset, kDigestRegionSize,
75  digest);
76 }
77 
78 /**
79  * Checks whether a boot data entry is empty.
80  *
81  * @param boot_data A buffer that holds a boot data entry. Must be word aligned.
82  * @return Whether the entry is empty.
83  */
85 static hardened_bool_t boot_data_is_empty(const void *boot_data) {
86  static_assert(kFlashCtrlErasedWord == UINT32_MAX,
87  "kFlashCtrlErasedWord must be UINT32_MAX");
88  size_t i = 0, r = kBootDataNumWords - 1;
90  uint32_t res = kFlashCtrlErasedWord;
91  for (; launder32(i) < kBootDataNumWords && launder32(r) < kBootDataNumWords;
92  ++i, --r) {
93  res &= read_32(boot_data);
94  is_empty &= read_32(boot_data);
95  boot_data = (char *)boot_data + sizeof(uint32_t);
96  }
97  HARDENED_CHECK_EQ(i, kBootDataNumWords);
98  HARDENED_CHECK_EQ(r, SIZE_MAX);
99  if (launder32(res) == kFlashCtrlErasedWord) {
100  HARDENED_CHECK_EQ(res, kFlashCtrlErasedWord);
101  return is_empty;
102  }
103  return kHardenedBoolFalse;
104 }
105 
106 /**
107  * Returns the `identifier` of the boot data entry at the given page and
108  * index after masking it with the words of its `is_valid` field.
109  *
110  * This function can be used to quickly determine if an entry can be empty or
111  * valid. Due to the values chosen for valid and invalid entries,
112  * `masked_identifier` will be `kFlashCtrlErasedWord` for entries that can be
113  * empty, `kBootDataIdentifier` for entries that are not invalidated, and `0`
114  * for invalidated entries.
115  *
116  * @param page A boot data page.
117  * @param index Index of the entry to read in the given page.
118  * @param[out] masked_identifier Identifier masked with the words of `is_valid`.
119  * @return The result of the operation.
120  */
122 static rom_error_t boot_data_sniff(const flash_ctrl_info_page_t *page,
123  size_t index, uint32_t *masked_identifier) {
124  static_assert(kBootDataValidEntry == UINT64_MAX,
125  "is_valid must be UINT64_MAX for valid entries.");
126  static_assert(kBootDataInvalidEntry == 0,
127  "is_valid must be 0 for invalid entries.");
128 
129  enum {
130  kIsValidOffset = offsetof(boot_data_t, is_valid),
131  kIdentifierOffset = offsetof(boot_data_t, identifier),
132  };
133  static_assert(
134  kIdentifierOffset - kIsValidOffset == sizeof((boot_data_t){0}.is_valid),
135  "is_valid and identifier must be consecutive.");
136 
137  *masked_identifier = 0;
138  uint32_t buf[3];
139  const uint32_t offset = index * sizeof(boot_data_t) + kIsValidOffset;
140  HARDENED_RETURN_IF_ERROR(flash_ctrl_info_read(page, offset, 3, buf));
141  *masked_identifier = buf[0] & buf[1] & buf[2];
142  return kErrorOk;
143 }
144 
145 /**
146  * Reads the boot data entry at the given page and index.
147  *
148  * @param page A boot data page.
149  * @param index Index of the entry to read in the given page.
150  * @param[out] boot_data A buffer that will hold the entry.
151  * @return The result of the operation.
152  */
154 static rom_error_t boot_data_entry_read(const flash_ctrl_info_page_t *page,
155  size_t index, boot_data_t *boot_data) {
156  const uint32_t offset = index * sizeof(boot_data_t);
157  return flash_ctrl_info_read(page, offset, kBootDataNumWords, boot_data);
158 }
159 
160 /**
161  * Populates the boot data entry at the given page and index.
162  *
163  * This function writes the new entry in two transactions skipping over the
164  * `is_valid` field so that the entry can be invalidated later. If `erase` is
165  * `kHardenedBoolTrue`, this function erases the given page before writing the
166  * new entry. This function also also verifies the newly written entry by
167  * reading it back. Reads, writes, and erases (if applicable) must be enabled
168  * for the given page before this function is called, see
169  * `boot_data_entry_write()`.
170  *
171  * @param page A boot data page.
172  * @param index Index of the entry to write in the given page.
173  * @param boot_data Entry to write.
174  * @param erase Whether to erase the page before writing the entry.
175  * @return The result of the operation.
176  */
178 static rom_error_t boot_data_entry_write_impl(
179  const flash_ctrl_info_page_t *page, size_t index,
180  const boot_data_t *boot_data, hardened_bool_t erase) {
181  // This function assumes the following layout for the first three fields.
183  OT_ASSERT_MEMBER_OFFSET(boot_data_t, is_valid, 32);
184  OT_ASSERT_MEMBER_OFFSET(boot_data_t, identifier, 40);
185 
186  if (erase == kHardenedBoolTrue) {
187  RETURN_IF_ERROR(flash_ctrl_info_erase(page, kFlashCtrlEraseTypePage));
188  }
189 
190  // Write digest
191  const uint32_t offset = index * sizeof(boot_data_t);
192  RETURN_IF_ERROR(
193  flash_ctrl_info_write(page, offset, kHmacDigestNumWords, boot_data));
194  // Write the rest of the entry, skipping over `is_valid`.
195  enum {
196  kSecondWriteOffsetBytes = offsetof(boot_data_t, identifier),
197  kSecondWriteOffsetWords = kSecondWriteOffsetBytes / sizeof(uint32_t),
198  kSecondWriteNumWords = kBootDataNumWords - kSecondWriteOffsetWords,
199  };
200  RETURN_IF_ERROR(flash_ctrl_info_write(
201  page, offset + kSecondWriteOffsetBytes, kSecondWriteNumWords,
202  (const char *)boot_data + kSecondWriteOffsetBytes));
203 
204  // Check.
205  boot_data_t written;
206  RETURN_IF_ERROR(
207  flash_ctrl_info_read(page, offset, kBootDataNumWords, &written));
208  if (memcmp(&written, boot_data, sizeof(boot_data_t)) != 0) {
209  return kErrorBootDataWriteCheck;
210  }
211  return kErrorOk;
212 }
213 
214 /**
215  * Handles access permissions and populates the boot data entry at the given
216  * page and index.
217  *
218  * This function wraps the actual implementation to enable and disable reads,
219  * writes, and erases (if applicable) for the given page, see
220  * `boot_data_page_entry_write_impl()`.
221  *
222  * @param page A boot data page.
223  * @param index Index of the entry to write in the given page.
224  * @param boot_data Entry to write.
225  * @param erase Whether to erase the page before writing the entry.
226  * @return The result of the operation.
227  */
229 static rom_error_t boot_data_entry_write(const flash_ctrl_info_page_t *page,
230  size_t index,
231  const boot_data_t *boot_data,
232  hardened_bool_t erase) {
233  flash_ctrl_info_perms_set(
234  page, (flash_ctrl_perms_t){
235  .read = kMultiBitBool4True,
236  .write = kMultiBitBool4True,
237  .erase = erase == kHardenedBoolTrue ? kMultiBitBool4True
238  : kMultiBitBool4False,
239  });
240  rom_error_t error = boot_data_entry_write_impl(page, index, boot_data, erase);
241  flash_ctrl_info_perms_set(page, (flash_ctrl_perms_t){
242  .read = kMultiBitBool4False,
243  .write = kMultiBitBool4False,
244  .erase = kMultiBitBool4False,
245  });
246  SEC_MMIO_WRITE_INCREMENT(2 * kFlashCtrlSecMmioInfoPermsSet);
247  return error;
248 }
249 
250 /**
251  * Invalidates the boot data entry at the given page and index.
252  *
253  * This function handles write permissions for the given page and sets the
254  * `is_valid` field of the given entry to `kBootDataInvalidEntry` which will
255  * cause the digest checks to fail in subsequent reads.
256  *
257  * This function must be called only after the new entry is successfully
258  * written since writes can potentially be interrupted.
259  *
260  * @param page A boot data page.
261  * @param index Index of the entry to invalidate in the given page.
262  * @return The result of the operation.
263  */
265 static rom_error_t boot_data_entry_invalidate(
266  const flash_ctrl_info_page_t *page, size_t index) {
267  // Assertions for the assumptions below.
268  OT_ASSERT_MEMBER_SIZE(boot_data_t, is_valid, 8);
269  static_assert(kBootDataInvalidEntry == 0,
270  "Unexpected kBootDataInvalidEntry value.");
271 
272  const uint32_t offset =
273  index * sizeof(boot_data_t) + offsetof(boot_data_t, is_valid);
274  const uint32_t val[2] = {0, 0};
275  flash_ctrl_info_perms_set(page, (flash_ctrl_perms_t){
276  .read = kMultiBitBool4False,
277  .write = kMultiBitBool4True,
278  .erase = kMultiBitBool4False,
279  });
280  rom_error_t error = flash_ctrl_info_write(page, offset, 2, val);
281  flash_ctrl_info_perms_set(page, (flash_ctrl_perms_t){
282  .read = kMultiBitBool4False,
283  .write = kMultiBitBool4False,
284  .erase = kMultiBitBool4False,
285  });
286  SEC_MMIO_WRITE_INCREMENT(2 * kFlashCtrlSecMmioInfoPermsSet);
287  return error;
288 }
289 
290 /**
291  * A struct that stores some information about the first empty and last valid
292  * entries in the active flash info page.
293  */
294 typedef struct active_page_info {
295  /**
296  * Info page.
297  */
299  /**
300  * Whether this page has an empty entry.
301  */
303  /**
304  * Index of the first empty entry.
305  */
307  /**
308  * Whether this page has a valid boot data entry.
309  */
311  /**
312  * Index of the last valid entry in the page.
313  */
316 
317 /**
318  * Updates the given active page info struct and last valid boot data entry
319  * using the given page.
320  *
321  * This function performs a forward search to find the first empty boot data
322  * entry followed by a backward search to find the last valid boot data entry.
323  * If the page has an entry that is newer than the one passed in, this function
324  * updates `page_info` and `boot_data`. Reads must be enabled for the given page
325  * before this function is called, see `boot_data_page_info_get()`.
326  *
327  * @param page A boot data page.
328  * @param[in,out] page_info Active page info struct. Updated if the given page
329  * has a newer entry.
330  * @param[in,out] boot_data Last valid boot data entry found so far. Updated if
331  * the given page has a newer entry.
332  * @return The result of the operation.
333  */
335 static rom_error_t boot_data_page_info_update_impl(
336  const flash_ctrl_info_page_t *page, active_page_info_t *page_info,
338  uint32_t sniff_results[kBootDataEntriesPerPage];
339 
340  boot_data_t buf;
341 
342  // Perform a forward search to find the first empty entry.
343  hardened_bool_t has_empty_entry = kHardenedBoolFalse;
344  size_t i = 0, r = kBootDataEntriesPerPage - 1;
345  for (; launder32(i) < kBootDataEntriesPerPage &&
346  launder32(r) < kBootDataEntriesPerPage;
347  ++i, --r) {
348  // Read and cache the identifier to quickly determine if an entry can be
349  // empty or valid.
350  HARDENED_RETURN_IF_ERROR(boot_data_sniff(page, i, &sniff_results[i]));
351  // Check all words of this entry only if it can be empty.
352  if (sniff_results[i] == kFlashCtrlErasedWord) {
353  HARDENED_RETURN_IF_ERROR(boot_data_entry_read(page, i, &buf));
354  has_empty_entry = boot_data_is_empty(&buf);
355  if (launder32(has_empty_entry) == kHardenedBoolTrue) {
356  HARDENED_CHECK_EQ(has_empty_entry, kHardenedBoolTrue);
357  break;
358  }
359  HARDENED_CHECK_EQ(has_empty_entry, kHardenedBoolFalse);
360  }
361  }
362  // At the end of this loop, `i` is the index of the first empty entry if any
363  // and `kBootDataEntriesPerPage` otherwise.
364  HARDENED_CHECK_LE(i, kBootDataEntriesPerPage);
365  size_t first_empty_index = i;
366  HARDENED_CHECK_EQ(i + r, kBootDataEntriesPerPage - 1);
367 
368  // Perform a backward search to find the last valid entry.
369  hardened_bool_t has_valid_entry = kHardenedBoolFalse;
370  for (--i, ++r; launder32(i) < kBootDataEntriesPerPage &&
371  launder32(r) < kBootDataEntriesPerPage;
372  --i, ++r) {
373  // Check the digest only if this entry can be valid.
374  if (sniff_results[i] == kBootDataIdentifier) {
375  HARDENED_RETURN_IF_ERROR(boot_data_entry_read(page, i, &buf));
376  rom_error_t is_valid = boot_data_check(&buf);
377  if (launder32(is_valid) == kErrorOk) {
378  HARDENED_CHECK_EQ(is_valid, kErrorOk);
379  static_assert(kErrorOk == (rom_error_t)kHardenedBoolTrue,
380  "kErrorOk must be equal to kHardenedBoolTrue");
381  has_valid_entry = (hardened_bool_t)is_valid;
382  break;
383  }
384  HARDENED_CHECK_EQ(is_valid, kErrorBootDataInvalid);
385  }
386  }
387  // At the end of this loop, `i` is the index of the last valid entry if any
388  // and `UINT32_MAX`, otherwise. `r` must be less than or equal to
389  // `first_empty_index`.
390  HARDENED_CHECK_EQ(i + r, kBootDataEntriesPerPage - 1);
391 
392  if (launder32(has_valid_entry) == kHardenedBoolTrue) {
393  HARDENED_CHECK_EQ(has_valid_entry, kHardenedBoolTrue);
394  if (launder32(page_info->has_valid_entry) == kHardenedBoolFalse) {
395  // Update `page_info` and `boot_data` since this is the first valid entry.
397  *page_info = (active_page_info_t){
398  .page = page,
399  .has_empty_entry = has_empty_entry,
400  .first_empty_index = first_empty_index,
401  .has_valid_entry = has_valid_entry,
402  .last_valid_index = i,
403  };
404  *boot_data = buf;
405  } else if (launder32(page_info->has_valid_entry) == kHardenedBoolTrue &&
406  launder32(buf.counter) > boot_data->counter) {
407  // Update `page_info` and `boot_data` since this entry is newer.
409  HARDENED_CHECK_GT(buf.counter, boot_data->counter);
410  *page_info = (active_page_info_t){
411  .page = page,
412  .has_empty_entry = has_empty_entry,
413  .first_empty_index = first_empty_index,
414  .has_valid_entry = has_valid_entry,
415  .last_valid_index = i,
416  };
417  *boot_data = buf;
418  } else {
420  // Counters cannot be equal if we have two valid entries since they are
421  // incremented at each write.
422  HARDENED_CHECK_LT(buf.counter, boot_data->counter);
423  }
424  }
425 
426  return kErrorOk;
427 }
428 
429 /**
430  * Handles read permissions and updates the active page info struct and last
431  * valid boot data entry using the given page.
432  *
433  * This function wraps the actual implementation to enable and disable reads for
434  * the given page, see `boot_data_page_info_get_impl()`.
435  *
436  * @param page A boot data page.
437  * @param[in,out] page_info Active page info struct. Updated if the given page
438  * has a newer entry.
439  * @param[in,out] boot_data Last valid boot data entry found so far. Updated if
440  * the given page has a newer entry.
441  * @return The result of the operation.
442  */
444 static rom_error_t boot_data_page_info_update(
445  const flash_ctrl_info_page_t *page, active_page_info_t *page_info,
447  flash_ctrl_info_perms_set(page, (flash_ctrl_perms_t){
448  .read = kMultiBitBool4True,
449  .write = kMultiBitBool4False,
450  .erase = kMultiBitBool4False,
451  });
452  rom_error_t error =
453  boot_data_page_info_update_impl(page, page_info, boot_data);
454  flash_ctrl_info_perms_set(page, (flash_ctrl_perms_t){
455  .read = kMultiBitBool4False,
456  .write = kMultiBitBool4False,
457  .erase = kMultiBitBool4False,
458  });
459  SEC_MMIO_WRITE_INCREMENT(2 * kFlashCtrlSecMmioInfoPermsSet);
460  return error;
461 }
462 
463 /**
464  * Finds the active info page and returns its page info struct and last boot
465  * data entry.
466  *
467  * The active info page is the one that has the newest valid boot data entry,
468  * i.e. the entry with the greatest counter value.
469  *
470  * @param[out] page_info Page info struct of the active info page.
471  * @param[out] boot_data Last valid boot data entry.
472  * @return The result of the operation.
473  */
475 static rom_error_t boot_data_active_page_find(active_page_info_t *page_info,
477  *page_info = (active_page_info_t){
478  .page = NULL,
479  .has_empty_entry = kHardenedBoolFalse,
480  .first_empty_index = kBootDataEntriesPerPage,
481  .has_valid_entry = kHardenedBoolFalse,
482  .last_valid_index = kBootDataEntriesPerPage,
483  };
484 
485  static_assert(kPageCount == 2,
486  "Number of pages changed, unrolled loop must be updated");
487  HARDENED_RETURN_IF_ERROR(
488  boot_data_page_info_update(kPages[0], page_info, boot_data));
489  HARDENED_RETURN_IF_ERROR(
490  boot_data_page_info_update(kPages[1], page_info, boot_data));
491 
492  return kErrorOk;
493 }
494 
495 /**
496  * Returns the default boot data.
497  *
498  * Default boot data can be used only in TEST_UNLOCKED, DEV, and RMA life cycle
499  * states unless explicitly allowed by setting the
500  * `CREATOR_SW_CFG_DEFAULT_BOOT_DATA_IN_PROD_EN` OTP item to
501  * `kHardenedBoolTrue`.
502  *
503  * @param lc_state Life cycle state of the device.
504  * @param[out] boot_data Default boot data.
505  * @return The result of the operation.
506  */
508 static rom_error_t boot_data_default_get(lifecycle_state_t lc_state,
510  uint32_t allowed_in_prod = otp_read32(
511  OTP_CTRL_PARAM_CREATOR_SW_CFG_DEFAULT_BOOT_DATA_IN_PROD_EN_OFFSET);
512  rom_error_t res = lc_state ^ launder32(kErrorBootDataNotFound);
513  barrier32(res);
514  switch (launder32(lc_state)) {
515  case kLcStateTest:
516  HARDENED_CHECK_EQ(lc_state, kLcStateTest);
517  res ^= kLcStateTest ^ kErrorBootDataNotFound ^ kErrorOk;
518  break;
519  case kLcStateDev:
520  HARDENED_CHECK_EQ(lc_state, kLcStateDev);
521  res ^= kLcStateDev ^ kErrorBootDataNotFound ^ kErrorOk;
522  break;
523  case kLcStateProd:
524  HARDENED_CHECK_EQ(lc_state, kLcStateProd);
525  res ^= kLcStateProd;
526  if (launder32(allowed_in_prod) == kHardenedBoolTrue) {
527  HARDENED_CHECK_EQ(allowed_in_prod, kHardenedBoolTrue);
528  res ^= kErrorBootDataNotFound ^ kErrorOk;
529  }
530  break;
531  case kLcStateProdEnd:
532  HARDENED_CHECK_EQ(lc_state, kLcStateProdEnd);
533  res ^= kLcStateProdEnd;
534  if (launder32(allowed_in_prod) == kHardenedBoolTrue) {
535  HARDENED_CHECK_EQ(allowed_in_prod, kHardenedBoolTrue);
536  res ^= kErrorBootDataNotFound ^ kErrorOk;
537  }
538  break;
539  case kLcStateRma:
540  HARDENED_CHECK_EQ(lc_state, kLcStateRma);
541  res ^= kLcStateRma ^ kErrorBootDataNotFound ^ kErrorOk;
542  break;
543  default:
544  HARDENED_TRAP();
545  }
546 
547  HARDENED_RETURN_IF_ERROR(res);
548 
549  memset(boot_data, 0, sizeof(*boot_data));
550  boot_data->is_valid = kBootDataValidEntry;
551  boot_data->identifier = kBootDataIdentifier;
552  boot_data->version = kBootDataVersion2;
553  boot_data->counter = kBootDataDefaultCounterVal;
555  otp_read32(OTP_CTRL_PARAM_CREATOR_SW_CFG_MIN_SEC_VER_ROM_EXT_OFFSET);
557  otp_read32(OTP_CTRL_PARAM_CREATOR_SW_CFG_MIN_SEC_VER_BL0_OFFSET);
558  boot_data->primary_bl0_slot = kBootSlotA;
559  // We cannot use a constant digest since some fields are read from the OTP
560  // and we check the digest of the cached boot data entry in rom.c
561  boot_data_digest_compute(boot_data, &boot_data->digest);
562 
563  return res;
564 }
565 
566 /**
567  * Populates fields not present in older versions of `boot_data_t`.
568  *
569  * For older `boot_data_t` entries, some fields may be missing after a call to
570  * `boot_data_read()`. This function will populate fields with their default
571  * values, the same values that `boot_data_read()` uses when returning default
572  * boot data.
573  *
574  * @param boot_data A buffer the holds a boot data entry.
575  * @return The result of the operation.
576  */
578 static rom_error_t boot_data_as_v2(boot_data_t *boot_data) {
579  switch (launder32(boot_data->version)) {
580  case kBootDataVersion1:
581  HARDENED_CHECK_EQ(boot_data->version, kBootDataVersion1);
582  boot_data->primary_bl0_slot = kBootSlotA;
583  boot_data->version = kBootDataVersion2;
584  boot_data_digest_compute(boot_data, &boot_data->digest);
585  return kErrorOk;
586  case kBootDataVersion2:
587  HARDENED_CHECK_EQ(boot_data->version, kBootDataVersion2);
588  return kErrorOk;
589  default:
590  HARDENED_TRAP();
591  OT_UNREACHABLE();
592  }
593 }
594 
595 rom_error_t boot_data_read(lifecycle_state_t lc_state, boot_data_t *boot_data) {
596  active_page_info_t active_page;
597  HARDENED_RETURN_IF_ERROR(boot_data_active_page_find(&active_page, boot_data));
598  switch (launder32(active_page.has_valid_entry)) {
599  case kHardenedBoolTrue:
601  return boot_data_as_v2(boot_data);
602  case kHardenedBoolFalse:
604  return boot_data_default_get(lc_state, boot_data);
605  default:
606  HARDENED_TRAP();
607  OT_UNREACHABLE();
608  }
609 }
610 
611 rom_error_t boot_data_write(const boot_data_t *boot_data) {
612  boot_data_t new_entry = *boot_data;
613  new_entry.is_valid = kBootDataValidEntry;
614  new_entry.identifier = kBootDataIdentifier;
615  active_page_info_t active_page;
616  boot_data_t last_entry;
617  RETURN_IF_ERROR(boot_data_active_page_find(&active_page, &last_entry));
618 
619  if (active_page.has_valid_entry == kHardenedBoolTrue) {
620  // Note: Not checking for wraparound since a successful write will
621  // invalidate the old entry.
622  new_entry.counter = last_entry.counter + 1;
623  boot_data_digest_compute(&new_entry, &new_entry.digest);
624  if (active_page.has_empty_entry == kHardenedBoolTrue) {
625  RETURN_IF_ERROR(boot_data_entry_write(active_page.page,
626  active_page.first_empty_index,
627  &new_entry, kHardenedBoolFalse));
628  } else {
629  // Erase the other page and write the new entry there if the active page
630  // is full.
631  const flash_ctrl_info_page_t *new_page =
632  active_page.page == kPages[0] ? kPages[1] : kPages[0];
633  RETURN_IF_ERROR(
634  boot_data_entry_write(new_page, 0, &new_entry, kHardenedBoolTrue));
635  }
636  // Invalidate the previous entry so that there is only one valid entry
637  // across both pages.
638  RETURN_IF_ERROR(boot_data_entry_invalidate(active_page.page,
639  active_page.last_valid_index));
640  } else {
641  // Erase the first page and write the entry there if the active page cannot
642  // be found, i.e. the storage is not initialized yet.
643  new_entry.counter = kBootDataDefaultCounterVal + 1;
644  boot_data_digest_compute(&new_entry, &new_entry.digest);
645  RETURN_IF_ERROR(
646  boot_data_entry_write(kPages[0], 0, &new_entry, kHardenedBoolTrue));
647  }
648 
649  return kErrorOk;
650 }
651 
652 /**
653  * Shares for producing the `error` value in `boot_data_check()`. First 8
654  * shares are generated using the `sparse-fsm-encode` script while the last
655  * share is `kErrorOk ^ kBootDataInvalid ^ kBootDataIdentifier ^
656  * kCheckShares[0] ^ ... ^ kCheckShares[7]` so that xor'ing all shares with
657  * the initial value of `error`, i.e. `kErrorBootDataInvalid`, and
658  * `kBootDataIdentifier` produces `kErrorOk`.
659  *
660  * Encoding generated with
661  * $ ./util/design/sparse-fsm-encode.py -d 6 -m 8 -n 32 \
662  * -s 49105412 --language=c
663  *
664  * Minimum Hamming distance: 12
665  * Maximum Hamming distance: 23
666  * Minimum Hamming weight: 13
667  * Maximum Hamming weight: 20
668  */
669 static const uint32_t kCheckShares[kHmacDigestNumWords + 1] = {
670  0xe021e1a9, 0xf81e8365, 0xbf8322db, 0xc7a37080, 0x271a933f,
671  0xdd8ce33f, 0x7585d574, 0x951777af, 0x381dee3a,
672 };
673 
674 /**
675  * Checks whether the digest of a boot data entry is valid.
676  *
677  * @param boot_data A buffer that holds a boot data entry.
678  * @return Whether the digest of the entry is valid.
679  */
680 rom_error_t boot_data_check(const boot_data_t *boot_data) {
681  static_assert(offsetof(boot_data_t, digest) == 0,
682  "`digest` must be the first field of `boot_data_t`.");
683 
684  rom_error_t error = kErrorBootDataInvalid;
685  hmac_digest_t act_digest;
686  boot_data_digest_compute(boot_data, &act_digest);
687 
688  size_t i = 0;
689  for (; launder32(i) < kHmacDigestNumWords; ++i) {
690  error ^=
691  boot_data->digest.digest[i] ^ act_digest.digest[i] ^ kCheckShares[i];
692  }
693  HARDENED_CHECK_EQ(i, kHmacDigestNumWords);
694  error ^= boot_data->identifier ^ kCheckShares[kHmacDigestNumWords];
695  if (launder32(error) == kErrorOk) {
696  HARDENED_CHECK_EQ(error, kErrorOk);
697  return error;
698  }
699 
700  return kErrorBootDataInvalid;
701 }