Software APIs
flash_ecc_error_test.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 
7 #include "sw/device/lib/base/status.h"
12 #include "sw/device/lib/testing/flash_ctrl_testutils.h"
13 #include "sw/device/lib/testing/otp_ctrl_testutils.h"
14 #include "sw/device/lib/testing/rstmgr_testutils.h"
15 #include "sw/device/lib/testing/test_framework/check.h"
18 #include "sw/device/silicon_creator/lib/boot_data.h"
19 #include "sw/device/silicon_creator/lib/boot_log.h"
20 #include "sw/device/silicon_creator/lib/drivers/retention_sram.h"
21 #include "sw/device/silicon_creator/lib/manifest.h"
22 #include "sw/device/silicon_creator/lib/manifest_def.h"
23 #include "sw/device/silicon_creator/rom/boot_policy_ptrs.h"
24 
25 #include "flash_ctrl_regs.h" // Generated.
27 #include "otp_ctrl_regs.h" // Generated.
28 
29 OTTF_DEFINE_TEST_CONFIG();
30 
31 static dif_flash_ctrl_state_t flash_ctrl;
32 static dif_rstmgr_t rstmgr;
33 static dif_otp_ctrl_t otp_ctrl;
34 
35 static dif_flash_ctrl_region_properties_t kFlashFullAccessScrambledEcc = {
36  .ecc_en = kMultiBitBool4True,
37  .high_endurance_en = kMultiBitBool4False,
38  .scramble_en = kMultiBitBool4True,
39  .erase_en = kMultiBitBool4True,
40  .prog_en = kMultiBitBool4True,
41  .rd_en = kMultiBitBool4True};
42 
43 /**
44  * ROM_EXT slots.
45  */
46 typedef enum rom_ext_slot {
47  kRomExtSlotA = 0,
48  kRomExtSlotB = 1,
49 } rom_ext_slot_t;
50 
51 /**
52  * Manifest offsets to corrupt.
53  *
54  * These fields are unique as they are read via pointer dereferces in the ROM.
55  * See `sw/device/silicon_creator/lib/manifest.h`.
56  */
57 enum {
58  kManifestSecurityVersionOffset = 844,
59  kManifestIdOffest = 820,
60  kManifestLengthOffset = 832,
61  kManifestManifestVersionOffset = 824,
62  kManifestSignedRegionEndOffset = 828,
63  kManifestCodeStartOffset = 892,
64  kManifestCodeEndOffset = 896,
65  kManifestEntryPointOffset = 900,
66  kManifestEcdsaPublicKeyOffset = 432,
67  kManifestUsageConstraintsSelectorBitsOffset = 384,
68  kManifestEcdsaSignatureOffset = 0,
69  kManifestExtensionsOffset = 904,
70 };
71 
72 /**
73  * ROM_EXT code offsets to corrupt.
74  *
75  * These are assigned non-word aligned values so thay can be differentiated from
76  * the hardcoded manifest offsets above.
77  */
78 enum {
79  kCodeFirstWordOffset = 1,
80  kCodeMiddleWordOffset = 2,
81  kCodeLastWordOffset = 3,
82 };
83 
84 enum {
85  // First flash pages in each ROM_EXT slot.
86  kRomExtSlotAFirstPageIndex = 0,
87  kRomExtSlotBFirstPageIndex = FLASH_CTRL_PARAM_REG_PAGES_PER_BANK,
88 
89  // Addresses of the first words in each ROM_EXT slot.
90  kRomExtSlotAFirstAddr = TOP_EARLGREY_EFLASH_BASE_ADDR,
91  kRomExtSlotBFirstAddr =
93 
94  // Addresses of the manifest identifiers words in each ROM_EXT slot.
95  kRomExtSlotAManifestIdAddr = kRomExtSlotAFirstAddr + kManifestIdOffest,
96  kRomExtSlotBManifestIdAddr = kRomExtSlotBFirstAddr + kManifestIdOffest,
97 
98  // Flash banks for each half of flash.
99  kFlashBank0DataRegion = 0,
100  kFlashBank1DataRegion = 1,
101 };
102 
104  kManifestSecurityVersionOffset);
105 OT_ASSERT_MEMBER_OFFSET_AS_ENUM(manifest_t, identifier, kManifestIdOffest);
106 OT_ASSERT_MEMBER_OFFSET_AS_ENUM(manifest_t, length, kManifestLengthOffset);
108  kManifestManifestVersionOffset);
110  kManifestSignedRegionEndOffset);
112  kManifestCodeStartOffset);
113 OT_ASSERT_MEMBER_OFFSET_AS_ENUM(manifest_t, code_end, kManifestCodeEndOffset);
115  kManifestEntryPointOffset);
117  kManifestEcdsaPublicKeyOffset);
119  kManifestUsageConstraintsSelectorBitsOffset);
121  kManifestEcdsaSignatureOffset);
123  kManifestExtensionsOffset);
124 
125 typedef struct corruption_word {
126  /**
127  * The byte offset within the flash bank to corrupt.
128  */
129  uint32_t offset;
130  /**
131  * A string description of what value is being corrupted.
132  */
133  char *description;
135 
136 /**
137  * Offsets of the manifest fields and code words we corrupt in this test.
138  *
139  * The fields of the manifest we corrupt are based on the order in which ROM
140  * manifest fields are currently checked during boot process:
141  * 1. security_version
142  * 2. identifier
143  * 3. length
144  * 4. manifest_version->major
145  * 5. signed_region_end
146  * 6. code_start
147  * 7. code_end
148  * 8. entry_point
149  * 9. ecdsa public key
150  * 10. spx+ public key (from manifest extension)
151  * 11. spx+ signature (from manifest extension)
152  * 12. usage_constraints->selector_bits,
153  * 13. ecdsa signature
154  *
155  * We corrupt the flash ECC of each field / code word to test the ROM either:
156  * a. proceeds immediately to try to boot the next slot, or
157  * b. triggers a signature verification failure that will then cause the ROM
158  * to try to boot the next slot.
159  */
160 static const corruption_word_t kWords2Corrupt[] = {
161  // Manifest corruption offsets.
162  {.offset = kManifestSecurityVersionOffset,
163  .description = "manifest_security_version"},
164  {.offset = kManifestIdOffest, .description = "manifest_identifier"},
165  {.offset = kManifestLengthOffset, .description = "manifest_length"},
166  {.offset = kManifestManifestVersionOffset,
167  .description = "manifest_manifest_version"},
168  {.offset = kManifestSignedRegionEndOffset,
169  .description = "manifest_signed_region_end"},
170  {.offset = kManifestCodeStartOffset, .description = "manifest_code_start"},
171  {.offset = kManifestCodeEndOffset, .description = "manifest_code_end"},
172  {.offset = kManifestEntryPointOffset,
173  .description = "manifest_entry_point"},
174  {.offset = kManifestEcdsaPublicKeyOffset,
175  .description = "manifest_ecdsa_public_key"},
176  {.offset = kManifestUsageConstraintsSelectorBitsOffset,
177  .description = "manifest_usage_constraints_selector_bits"},
178  {.offset = kManifestEcdsaSignatureOffset,
179  .description = "manifest_ecdsa_signature"},
180  {.offset = kManifestExtIdSpxKey, // Represented as the extension ID; offset
181  // is computed below.
182  .description = "manifest_extension_spx_public_key"},
183  {.offset = kManifestExtIdSpxSignature, // Represented as the extension ID;
184  // offset is computed below.
185  .description = "manifest_extension_spx_signature"},
186 
187  // Code corruption offsets.
188  {.offset = kCodeFirstWordOffset, .description = "code_first_word"},
189 };
190 
191 static void init_peripherals(void) {
192  CHECK_DIF_OK(dif_flash_ctrl_init_state(
193  &flash_ctrl,
195  CHECK_DIF_OK(dif_otp_ctrl_init(
197  CHECK_DIF_OK(dif_rstmgr_init(
199 }
200 
201 /**
202  * Issue a software reset.
203  */
204 static void sw_reset(void) {
205  rstmgr_testutils_reason_clear();
206  CHECK_DIF_OK(dif_rstmgr_software_device_reset(&rstmgr));
208 }
209 
210 /**
211  * Computes the page index of a byte address within a bank.
212  */
213 static uint32_t compute_flash_page_index(rom_ext_slot_t rom_ext_slot,
214  uint32_t byte_offset_in_bank) {
215  uint32_t page_idx = rom_ext_slot == kRomExtSlotA ? kRomExtSlotAFirstPageIndex
216  : kRomExtSlotBFirstPageIndex;
217  page_idx += byte_offset_in_bank / FLASH_CTRL_PARAM_BYTES_PER_PAGE;
218  return page_idx;
219 }
220 
221 /**
222  * Corrupts a word of the currently running ROM_EXT at the provided offset
223  * address of the given slot to trigger an ECC integrity errory (load access
224  * fault) on the next boot.
225  */
226 static status_t corrupt_rom_ext_word(rom_ext_slot_t slot,
227  uint32_t offset_to_corrupt) {
228  uint32_t page_idx = compute_flash_page_index(slot, offset_to_corrupt);
229  uint32_t flash_data_bank =
230  slot == kRomExtSlotA ? kFlashBank0DataRegion : kFlashBank1DataRegion;
231  uint32_t first_flash_addr_in_slot =
232  slot == kRomExtSlotA ? kRomExtSlotAFirstAddr : kRomExtSlotBFirstAddr;
233 
234  // Setup full access (read, erase, program) to first flash page in the slot.
235  TRY(flash_ctrl_testutils_data_region_setup_properties(
236  &flash_ctrl, page_idx, flash_data_bank,
237  /*region_size=*/1, kFlashFullAccessScrambledEcc, NULL));
238 
239  // Read the flash word we are about to corrupt, via the flash_ctrl interface,
240  // to confirm it is not corrupted.
241  uint32_t word_to_corrupt = 0;
242  TRY(flash_ctrl_testutils_read(
243  &flash_ctrl, first_flash_addr_in_slot + offset_to_corrupt,
244  /*partition_id=*/0, &word_to_corrupt, kDifFlashCtrlPartitionTypeData,
245  /*word_count=*/1, /*delay_micros=*/0));
246  LOG_INFO("Uncorrupted Flash Word: 0x%08x", word_to_corrupt);
247 
248  // Overwrite the target flash word inverted value (without first erasing the
249  // page) to corrupt its ECC (since flash writes can only transition 1 bits to
250  // 0 bits).
251  uint32_t corrupted_word = ~word_to_corrupt;
252  TRY(flash_ctrl_testutils_write(
253  &flash_ctrl, first_flash_addr_in_slot + offset_to_corrupt,
254  /*partition_id=*/0, &corrupted_word, kDifFlashCtrlPartitionTypeData,
255  /*word_count=*/1));
256 
257  return OK_STATUS();
258 }
259 
260 bool test_main(void) {
261  init_peripherals();
262 
263  // Get the cause ID we will test and store the cause description in retention
264  // SRAM (we will check it when the valid slot boots after a flash ECC error is
265  // triggered).
266  uint32_t cause_id = CAUSE_ID; // Set in Bazel build file.
267  CHECK(cause_id < ARRAYSIZE(kWords2Corrupt));
268  char *corruption_desc = (char *)retention_sram_get()->owner.reserved;
269  const char *str = kWords2Corrupt[cause_id].description;
270  int len = -1;
271  do {
272  ++len;
273  corruption_desc[len] = str[len];
274  } while (str[len] != '\0' && len < sizeof(retention_sram_owner_t));
275 
276  // Get the manifest of the current slot we are running in.
277  const manifest_t *manifest = manifest_def_get();
278 
279  // Compute the address we are corrupting within the slot. Some addresses are
280  // hardcoded constants, others we must compute dynamically.
281  uint32_t addr_of_corruption = kWords2Corrupt[cause_id].offset;
282  switch (addr_of_corruption) {
283  case kManifestExtIdSpxKey:
284  CHECK(manifest->extensions.entries[0].identifier == kManifestExtIdSpxKey);
285  addr_of_corruption = manifest->extensions.entries[0].offset;
286  break;
287  case kManifestExtIdSpxSignature:
288  CHECK(manifest->extensions.entries[1].identifier ==
289  kManifestExtIdSpxSignature);
290  addr_of_corruption = manifest->extensions.entries[1].offset;
291  break;
292  case kCodeFirstWordOffset:
293  addr_of_corruption = manifest->code_start;
294  break;
295  case kCodeMiddleWordOffset:
296  addr_of_corruption =
297  ((manifest->code_start + manifest->code_end) / 2) & ~0x3u;
298  break;
299  case kCodeLastWordOffset:
300  addr_of_corruption = manifest->code_end;
301  break;
302  default:
303  break;
304  }
305 
306  // Corrupt the ECC of a targeted flash word by performing a double write.
307  boot_log_t *boot_log = &retention_sram_get()->creator.boot_log;
308  if (boot_log->rom_ext_slot == kBootSlotA) {
309  LOG_INFO("Slot A self corrupting (%s) ...", corruption_desc);
310  CHECK_STATUS_OK(corrupt_rom_ext_word(kRomExtSlotA, addr_of_corruption));
311  } else {
312  LOG_INFO("Slot B self corrupting (%s) ...", corruption_desc);
313  CHECK_STATUS_OK(corrupt_rom_ext_word(kRomExtSlotB, addr_of_corruption));
314  }
315 
316  // Issue a reset.
317  LOG_INFO("Resetting chip to trigger load access fault in the ROM ...");
318  sw_reset();
319 
320  return true;
321 }