Software APIs
boot_data_unittest.cc
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 <array>
8 #include <cstring>
9 
10 #include "gtest/gtest.h"
11 #include "sw/device/silicon_creator/lib/drivers/mock_flash_ctrl.h"
12 #include "sw/device/silicon_creator/lib/drivers/mock_hmac.h"
13 #include "sw/device/silicon_creator/lib/drivers/mock_otp.h"
14 #include "sw/device/silicon_creator/testing/rom_test.h"
15 
16 #include "flash_ctrl_regs.h"
18 #include "otp_ctrl_regs.h"
19 
20 bool operator==(flash_ctrl_perms_t lhs, flash_ctrl_perms_t rhs) {
21  return std::memcmp(&lhs, &rhs, sizeof(flash_ctrl_perms_t)) == 0;
22 }
23 
24 bool operator==(boot_data_t lhs, boot_data_t rhs) {
25  return std::memcmp(&lhs, &rhs, sizeof(boot_data_t)) == 0;
26 }
27 
28 /**
29  * Example boot data entry with the first `.counter` value.
30  */
31 constexpr boot_data_t kValidEntry0 = {
32  .digest = {kBootDataIdentifier, kBootDataIdentifier, kBootDataIdentifier,
33  0x00000000, 0x11111111, 0x22222222, 0x33333333, 0x44444444},
34  .is_valid = kBootDataValidEntry,
35  .identifier = kBootDataIdentifier,
36  .version = kBootDataVersion2,
37  .counter = kBootDataDefaultCounterVal,
38  .min_security_version_rom_ext = 0,
39  .min_security_version_bl0 = 0,
40  .primary_bl0_slot = kBootSlotA,
41 };
42 
43 /**
44  * Example boot data entry with a _higher_ `.counter` value.
45  */
46 constexpr boot_data_t kValidEntry1 = {
47  .digest = {kBootDataIdentifier, kBootDataIdentifier, kBootDataIdentifier,
48  0x44444444, 0x33333333, 0x22222222, 0x11111111, 0x00000000},
49  .is_valid = kBootDataValidEntry,
50  .identifier = kBootDataIdentifier,
51  .version = kBootDataVersion2,
52  .counter = kBootDataDefaultCounterVal + 1,
53  .min_security_version_rom_ext = 0,
54  .min_security_version_bl0 = 0,
55  .primary_bl0_slot = kBootSlotA,
56 };
57 
58 /**
59  * Example version 1 boot data entry.
60  */
61 constexpr boot_data_t kValidEntryV1 = {
62  .digest = {kBootDataIdentifier, kBootDataIdentifier, kBootDataIdentifier,
63  0x00000000, 0x11111111, 0x22222222, 0x33333333, 0x44444444},
64  .is_valid = kBootDataValidEntry,
65  .identifier = kBootDataIdentifier,
66  .version = kBootDataVersion1,
67  .counter = kBootDataDefaultCounterVal,
68  .min_security_version_rom_ext = 0,
69  .min_security_version_bl0 = 0,
70 };
71 
72 /**
73  * Default boot data entry loaded by `boot_data_default_get`.
74  */
75 constexpr boot_data_t kDefaultEntry = {
76  .digest = {kBootDataIdentifier, kBootDataIdentifier, kBootDataIdentifier,
77  0xcc761df1, 0xff42f0f2, 0x3f1955ee, 0x9465b3e7, 0x81ce0fdb},
78  .is_valid = kBootDataValidEntry,
79  .identifier = kBootDataIdentifier,
80  .version = kBootDataVersion2,
81  .counter = kBootDataDefaultCounterVal,
82  .min_security_version_rom_ext = 0x01234567,
83  .min_security_version_bl0 = 0x89abcdef,
84  .primary_bl0_slot = kBootSlotA,
85 };
86 
87 namespace boot_data_unittest {
88 namespace {
89 using ::testing::_;
90 using ::testing::DoAll;
91 using ::testing::Return;
92 using ::testing::SetArgPointee;
93 
95  protected:
96  rom_test::MockFlashCtrl flash_ctrl_;
97  rom_test::MockHmac hmac_;
98  rom_test::MockOtp otp_;
99 
100  // Data for an entry which is fully erased.
101  std::array<uint32_t, kBootDataNumWords> erased_entry_ = {};
102  // Data for a non-erased but non-bootable entry.
103  std::array<uint32_t, kBootDataNumWords> non_erased_entry_ = {};
104  // Data for a `boot_data_t` entry with only the first three words erased.
105  std::array<uint32_t, kBootDataNumWords> part_erased_entry_ = {};
106 
107  BootDataTest() {
108  std::fill_n(erased_entry_.begin(), kBootDataNumWords, kFlashCtrlErasedWord);
109  std::fill_n(non_erased_entry_.begin(), kBootDataNumWords, 0x01234567);
110  std::fill_n(part_erased_entry_.begin(), kBootDataNumWords, 0x01234567);
111  std::fill_n(part_erased_entry_.begin(), 3, kFlashCtrlErasedWord);
112  }
113 
114  /**
115  * Sets an expectation that a given boot data entry in the given info page
116  * is read. The data and return value given by the read can be specified.
117  *
118  * @param page The info page containing the boot data entry.
119  * @param index The index of the boot data entry in the page.
120  * @param offset Offset into the boot data entry expected to be read from.
121  * @param data Mock data to be read at this entry. The number of words is
122  * unchecked and can be less than (or greater) than the boot
123  * data entry size.
124  * @param error Value to be returned by the read.
125  * @param count Optionally the number of values expected to be read from the
126  * start of the entry. Useful for expecting sniffs.
127  */
128  void ExpectRead(const flash_ctrl_info_page_t *page, size_t index,
129  std::array<uint32_t, kBootDataNumWords> data,
130  rom_error_t error) {
131  size_t offset = index * sizeof(boot_data_t);
132 
133  // Mock out flash_ctrl_page_info_read to pass the given `data` and return
134  // the given `error`.
135  //
136  // Using a lambda rather than `.SetArrayArgument(...).Return(error)`
137  // because we have to cast the `void*` argument to a real pointer type
138  // before we can write to it.
139  EXPECT_CALL(flash_ctrl_, InfoRead(page, offset, kBootDataNumWords, _))
140  .WillOnce([data, error](auto, auto, auto, void *out) {
141  uint32_t *out_words = static_cast<uint32_t *>(out);
142  std::copy_n(data.begin(), kBootDataNumWords, out_words);
143  return error;
144  });
145  }
146 
147  /**
148  * Sets an expectation that a given boot data entry in an info page was
149  * sniffed (i.e. with `boot_data_sniff`).
150  *
151  * @param page The info page containing the boot entry.
152  * @param index The index of the boot info entry in the page.
153  * @param data Data of the boot data entry (only first three words read).
154  * @param error Value to be returned by the read.
155  */
156  template <size_t N>
157  void ExpectSniff(const flash_ctrl_info_page_t *page, size_t index,
158  std::array<uint32_t, N> data, rom_error_t error) {
159  static_assert(N > 3, "Data must be at least three words for a sniff");
160 
161  constexpr uint32_t kIsValidOffset = offsetof(boot_data_t, is_valid);
162  size_t offset = index * sizeof(boot_data_t) + kIsValidOffset;
163 
164  // As with `ExpectRead`, provide the given `data` and `error` using a lambda
165  // to support casting the `void*` parameter before writing.
166  EXPECT_CALL(flash_ctrl_, InfoRead(page, offset, 3, _))
167  .WillOnce([data, error](auto, auto, auto, void *out) {
168  uint32_t *out_words = static_cast<uint32_t *>(out);
169  std::copy_n(data.begin(), 3, out_words);
170  return error;
171  });
172  }
173 
174  /**
175  * Sets an expectation that a digest for the given `boot` data is computed.
176  *
177  * @param boot_data The boot data expected to be used in computation.
178  * @param valid Whether the mocked digest computed should match.
179  */
181  constexpr size_t kDigestRegionOffset = sizeof(boot_data.digest);
182  constexpr size_t kDigestRegionSize =
183  sizeof(boot_data_t) - kDigestRegionOffset;
184 
185  // If mocking as invalid, break the digest.
186  hmac_digest_t digest = boot_data.digest;
187  if (!valid) {
188  digest.digest[0] += 1;
189  }
190 
191  // Check the post-digest data we're computing with matches what's given.
192  EXPECT_CALL(hmac_, sha256(_, kDigestRegionSize, _))
193  .WillOnce(DoAll([boot_data, kDigestRegionSize, digest](
194  const void *digest_region, size_t size,
195  hmac_digest_t *digest_) {
196  int digest_region_cmp = std::memcmp(
197  digest_region,
198  reinterpret_cast<const char *>(&boot_data) + kDigestRegionOffset,
199  kDigestRegionSize);
200  EXPECT_EQ(digest_region_cmp, 0);
201  EXPECT_EQ(size, kDigestRegionSize);
202  *digest_ = digest;
203  }));
204  }
205 
206  /**
207  * Sets an expectation that the given page's permissions are set to the given
208  * values for `read`, `write`, and `erase`.
209  *
210  * @param page The page whose permissions are expected to be set.
211  * @param read Expected setting for the `.read` permission.
212  * @param write Expected setting for the `.write` permission.
213  * @param erase Expected setting for the `.erase` permission.
214  */
215  void ExpectPermsSet(const flash_ctrl_info_page_t *page, bool read, bool write,
216  bool erase) {
217  flash_ctrl_perms_t perms = {
218  .read = read ? kMultiBitBool4True : kMultiBitBool4False,
219  .write = write ? kMultiBitBool4True : kMultiBitBool4False,
220  .erase = erase ? kMultiBitBool4True : kMultiBitBool4False,
221  };
222  EXPECT_CALL(flash_ctrl_, InfoPermsSet(page, perms));
223  }
224 
225  /**
226  * Sets an expectation that the given page is searched for its last valid boot
227  * data entry.
228  *
229  * This acts as a wrapper around the provided function which should contain
230  * the expectations of sniffs and reads that happen within the given page.
231  *
232  * @param page The page expected to be searched for boot data entries.
233  * @param reads Function given the `page` containing expectations of the reads
234  * happening there.
235  */
237  const flash_ctrl_info_page_t *page,
238  std::function<void(const flash_ctrl_info_page_t *)> reads) {
239  ExpectPermsSet(page, true, false, false);
240  reads(page);
241  ExpectPermsSet(page, false, false, false);
242  }
243 
244  /**
245  * Provides a lambda function mocking a page containing various boot
246  * data entries (all non-bootable or invalid) plus the given `boot_data` which
247  * is expected to be bootable.
248  *
249  * @param boot_data Bootable boot data entry to be inserted into the page.
250  * @param valid_digest Whether to mock that `boot_data`'s digest is valid.
251  * @return Lambda function for use with `ExpectPageScan`.
252  */
253  auto EntryPage(boot_data_t boot_data, bool valid_digest = true) {
254  // Ensures the following memcpy is safe
255  static_assert(sizeof(uint32_t) * kBootDataNumWords == sizeof(boot_data_t),
256  "`kBootDataNumWords` must match size of `boot_data_t`");
257 
258  // Convert the given boot data into an array of words.
259  std::array<uint32_t, kBootDataNumWords> boot_data_raw = {};
260  std::memcpy(boot_data_raw.data(), &boot_data, sizeof(boot_data_t));
261 
262  // Mock the page to have the following layout:
263  // #0. Non-erased but non-bootable.
264  // #1. Non-erased and bootable provided boot_data.
265  // #2. Non-erased and bootable but invalid digest.
266  // #3. Entry with sniffed area erased but the rest not.
267  // #4. Fully erased entry.
268  return [=](const flash_ctrl_info_page_t *page) {
269  // Expect to sniff each entry, fully reading if it could be erased.
270  ExpectSniff(page, 0, non_erased_entry_, kErrorOk);
271  ExpectSniff(page, 1, boot_data_raw, kErrorOk);
272  ExpectSniff(page, 2, boot_data_raw, kErrorOk);
273  ExpectSniff(page, 3, part_erased_entry_, kErrorOk);
274  ExpectRead(page, 3, part_erased_entry_, kErrorOk);
275  ExpectSniff(page, 4, erased_entry_, kErrorOk);
276  ExpectRead(page, 4, erased_entry_, kErrorOk);
277 
278  // Check the last seen bootable entry's digest (mocked as invalid).
279  ExpectRead(page, 2, boot_data_raw, kErrorOk);
280  ExpectDigestCompute(boot_data, false);
281 
282  // Step back to the previously seen bootable entry (provided `boot_data`).
283  ExpectRead(page, 1, boot_data_raw, kErrorOk);
284  ExpectDigestCompute(boot_data, valid_digest);
285  };
286  }
287 
288  /**
289  * Provides a lambda function mocking a page with only an erased entry.
290  *
291  * @return Lambda function for use with `ExpectPageScan`.
292  */
293  auto ErasedPage() {
294  return [this](auto page) {
295  ExpectSniff(page, 0, erased_entry_, kErrorOk);
296  ExpectRead(page, 0, erased_entry_, kErrorOk);
297  };
298  }
299 
300  /**
301  * Sets an expectation that the device queries for whether the default boot
302  * data entry should be loaded when in the `prod` lifecycle state.
303  *
304  * @param allowed_in_prod Whether loading the default entry should be allowed.
305  */
306  void ExpectAllowedInProdCheck(bool allowed_in_prod) {
307  EXPECT_CALL(
308  otp_,
309  read32(
310  OTP_CTRL_PARAM_CREATOR_SW_CFG_DEFAULT_BOOT_DATA_IN_PROD_EN_OFFSET))
311  .WillOnce(
312  Return(allowed_in_prod ? kHardenedBoolTrue : kHardenedBoolFalse));
313  }
314 
315  /**
316  * Sets an expectation that the default boot data entry is loaded and its
317  * digest is checked.
318  */
320  EXPECT_CALL(
321  otp_, read32(OTP_CTRL_PARAM_CREATOR_SW_CFG_MIN_SEC_VER_ROM_EXT_OFFSET))
322  .WillOnce(Return(kDefaultEntry.min_security_version_rom_ext));
323  EXPECT_CALL(otp_,
324  read32(OTP_CTRL_PARAM_CREATOR_SW_CFG_MIN_SEC_VER_BL0_OFFSET))
325  .WillOnce(Return(kDefaultEntry.min_security_version_bl0));
326 
327  ExpectDigestCompute(kDefaultEntry, true);
328  }
329 };
330 
331 class BootDataReadTest : public BootDataTest {};
332 
333 TEST_F(BootDataReadTest, ReadBothValidTest1) {
334  // Expect both pages to be checked, with both giving valid entries.
335  ExpectPageScan(&kFlashCtrlInfoPageBootData0, EntryPage(kValidEntry0));
336  ExpectPageScan(&kFlashCtrlInfoPageBootData1, EntryPage(kValidEntry1));
337 
338  boot_data_t boot_data = {{0}};
339  EXPECT_EQ(boot_data_read(kLcStateTest, &boot_data), kErrorOk);
340  // Expect the entry with the higher `.counter` to have been selected.
341  EXPECT_EQ(boot_data, kValidEntry1);
342 }
343 
344 TEST_F(BootDataReadTest, ReadBothValidTest2) {
345  // Same as above, but swap which page contains `test_entry_1`.
346  ExpectPageScan(&kFlashCtrlInfoPageBootData0, EntryPage(kValidEntry1));
347  ExpectPageScan(&kFlashCtrlInfoPageBootData1, EntryPage(kValidEntry0));
348 
349  boot_data_t boot_data = {{0}};
350  EXPECT_EQ(boot_data_read(kLcStateTest, &boot_data), kErrorOk);
351  // Expect the entry with the higher `.counter` to have been selected.
352  EXPECT_EQ(boot_data, kValidEntry1);
353 }
354 
355 TEST_F(BootDataReadTest, ReadOneEntryTest) {
356  // Expect both pages to be searched, but give only a valid entry for one.
357  ExpectPageScan(&kFlashCtrlInfoPageBootData0, EntryPage(kValidEntry0));
358  ExpectPageScan(&kFlashCtrlInfoPageBootData1, ErasedPage());
359 
360  boot_data_t boot_data = {{0}};
361  EXPECT_EQ(boot_data_read(kLcStateTest, &boot_data), kErrorOk);
362  EXPECT_EQ(boot_data, kValidEntry0);
363 }
364 
365 TEST_F(BootDataReadTest, ReadOneValidTest) {
366  // Expect both pages to be searched, but give only a valid entry for one.
367  ExpectPageScan(&kFlashCtrlInfoPageBootData0, EntryPage(kValidEntry0));
368  ExpectPageScan(&kFlashCtrlInfoPageBootData1, EntryPage(kValidEntry1, false));
369 
370  boot_data_t boot_data = {{0}};
371  EXPECT_EQ(boot_data_read(kLcStateTest, &boot_data), kErrorOk);
372  // `...BootData1` had an entry with a higher `.counter`, but it has an
373  // invalid digest and should not be chosen.
374  EXPECT_EQ(boot_data, kValidEntry0);
375 }
376 
377 TEST_F(BootDataReadTest, ReadErasedDefaultTest) {
378  // Expect both pages to be searched, but give no entry for either.
379  ExpectPageScan(&kFlashCtrlInfoPageBootData0, ErasedPage());
380  ExpectPageScan(&kFlashCtrlInfoPageBootData1, ErasedPage());
381 
382  // Expect to fall back to loading the default entry.
383  ExpectAllowedInProdCheck(false);
384  ExpectDefaultEntryRead();
385 
386  boot_data_t boot_data = {{0}};
387  EXPECT_EQ(boot_data_read(kLcStateTest, &boot_data), kErrorOk);
388  EXPECT_EQ(boot_data, kDefaultEntry);
389 }
390 
391 TEST_F(BootDataReadTest, ReadInvalidDefaultTest) {
392  // Expect both pages to be searched, but give invalid entries for both.
393  ExpectPageScan(&kFlashCtrlInfoPageBootData0, EntryPage(kValidEntry0, false));
394  ExpectPageScan(&kFlashCtrlInfoPageBootData1, EntryPage(kValidEntry1, false));
395 
396  // Expect to fall back to loading the default entry.
397  ExpectAllowedInProdCheck(false);
398  ExpectDefaultEntryRead();
399 
400  boot_data_t boot_data = {{0}};
401  EXPECT_EQ(boot_data_read(kLcStateTest, &boot_data), kErrorOk);
402  EXPECT_EQ(boot_data, kDefaultEntry);
403 }
404 
405 TEST_F(BootDataReadTest, ReadDefaultAllowedInProdTest) {
406  // Expect both pages to be searched, but give no entry for either.
407  ExpectPageScan(&kFlashCtrlInfoPageBootData0, ErasedPage());
408  ExpectPageScan(&kFlashCtrlInfoPageBootData1, ErasedPage());
409 
410  // Expect to fall back to loading the default entry (allowed in prod).
411  ExpectAllowedInProdCheck(true);
412  ExpectDefaultEntryRead();
413 
414  boot_data_t boot_data = {{0}};
415  EXPECT_EQ(boot_data_read(kLcStateProd, &boot_data), kErrorOk);
416  EXPECT_EQ(boot_data, kDefaultEntry);
417 }
418 
419 TEST_F(BootDataReadTest, ReadDefaultNotAllowedInProdTest) {
420  // Expect both pages to be searched, but give no entry for either.
421  ExpectPageScan(&kFlashCtrlInfoPageBootData0, ErasedPage());
422  ExpectPageScan(&kFlashCtrlInfoPageBootData1, ErasedPage());
423 
424  // Expect to fall back to loading the default entry (now allowed in prod).
425  ExpectAllowedInProdCheck(false);
426  // Do not expect the default entry to be read.
427 
428  boot_data_t boot_data = {{0}};
429  EXPECT_EQ(boot_data_read(kLcStateProd, &boot_data), kErrorBootDataNotFound);
430 }
431 
432 TEST_F(BootDataReadTest, ReadV1AsV2Test) {
433  // Expect both to be searched, but only provide an entry in one.
434  ExpectPageScan(&kFlashCtrlInfoPageBootData0, EntryPage(kValidEntryV1));
435  ExpectPageScan(&kFlashCtrlInfoPageBootData1, ErasedPage());
436 
437  // Expect a new digest computation on version 2 of the boot data.
438  ExpectDigestCompute(kValidEntry0, true);
439 
440  // Expect to read the version 1 boot data.
441  boot_data_t boot_data = {{0}};
442  EXPECT_EQ(boot_data_read(kLcStateTest, &boot_data), kErrorOk);
443  EXPECT_EQ(boot_data, kValidEntry0);
444 
445  EXPECT_EQ(boot_data, kValidEntry0);
446 }
447 
448 } // namespace
449 } // namespace boot_data_unittest