[sw/silicon_creator] Refactor mask_rom_boot() to prioritize newer ROM_EXT

Signed-off-by: Alphan Ulusoy <alphan@google.com>
diff --git a/sw/device/silicon_creator/mask_rom/mask_rom.c b/sw/device/silicon_creator/mask_rom/mask_rom.c
index 775b3f6..50ed8c7 100644
--- a/sw/device/silicon_creator/mask_rom/mask_rom.c
+++ b/sw/device/silicon_creator/mask_rom/mask_rom.c
@@ -32,6 +32,10 @@
 // sec_mmio requires this symbol. For the actual target, we need to define a
 // memory region to share the data with ROM_EXT.
 sec_mmio_ctx_t sec_mmio_ctx;
+// In-memory copy of the ePMP register configuration.
+epmp_state_t epmp;
+// Life cycle state of the chip.
+lifecycle_state_t lc_state = kLcStateProd;
 
 static inline rom_error_t mask_rom_irq_error(void) {
   uint32_t mcause;
@@ -50,6 +54,112 @@
   return kErrorInterrupt + mcause;
 }
 
+/**
+ * Performs once-per-boot initialization of mask ROM modules and peripherals.
+ */
+static void mask_rom_init(void) {
+  sec_mmio_init(shutdown_finalize);
+  // Initialize the shutdown policy according to lifecycle state.
+  lc_state = lifecycle_state_get();
+  shutdown_init(lc_state);
+  // Initiaize in-memory copy of the ePMP register configuration.
+  mask_rom_epmp_state_init(&epmp);
+  // Initialize pinmux configuration so we can use the UART.
+  pinmux_init();
+  // Configure UART0 as stdout.
+  uart_init(kUartNCOValue);
+  base_set_stdout((buffer_sink_t){
+      .data = NULL,
+      .sink = uart_sink,
+  });
+  // TODO: Clean device state based on reset reason (chip-specific startup).
+  // TODO: Bootstrap.
+}
+
+/**
+ * Verifies a ROM_EXT.
+ *
+ * This function performs bounds checks on the fields of the manifest, checks
+ * its `identifier` and `security_version` fields, and verifies its signature.
+ *
+ * @param Manifest of the ROM_EXT to be verified.
+ * @return Result of the operation.
+ */
+static rom_error_t mask_rom_verify(const manifest_t *manifest) {
+  RETURN_IF_ERROR(boot_policy_manifest_check(manifest));
+
+  const sigverify_rsa_key_t *key;
+  RETURN_IF_ERROR(sigverify_rsa_key_get(
+      sigverify_rsa_key_id_get(&manifest->modulus), lc_state, &key));
+
+  // TODO(#5956): Manifest usage constraints.
+  manifest_signed_region_t signed_region = manifest_signed_region_get(manifest);
+  RETURN_IF_ERROR(sigverify_rsa_verify(signed_region.start,
+                                       signed_region.length,
+                                       &manifest->signature, key, lc_state));
+  return kErrorOk;
+}
+
+/**
+ * Boots a ROM_EXT.
+ *
+ * Note: This function should not return under normal conditions. Any returns
+ * from this function must result in shutdown.
+ *
+ * @param manifest Manifest of the ROM_EXT to boot.
+ * @return rom_error_t Result of the operation.
+ */
+static rom_error_t mask_rom_boot(const manifest_t *manifest) {
+  RETURN_IF_ERROR(keymgr_check_state(kKeymgrStateReset));
+  keymgr_set_next_stage_inputs(&manifest->binding_value,
+                               manifest->max_key_version);
+
+  // Unlock execution of ROM_EXT executable code (text) sections.
+  RETURN_IF_ERROR(epmp_state_check(&epmp));
+  mask_rom_epmp_unlock_rom_ext_rx(&epmp, manifest_code_region_get(manifest));
+  RETURN_IF_ERROR(epmp_state_check(&epmp));
+
+  // Jump to ROM_EXT entry point.
+  uintptr_t entry_point = manifest_entry_point_get(manifest);
+  base_printf("rom_ext_entry: %p\r\n", entry_point);
+  ((rom_ext_entry_point *)entry_point)();
+
+  return kErrorMaskRomBootFailed;
+}
+
+/**
+ * Attempts to boot ROM_EXTs in the order given by the boot policy module.
+ *
+ * @return Result of the last attempt.
+ */
+static rom_error_t mask_rom_try_boot(void) {
+  boot_policy_manifests_t manifests = boot_policy_manifests_get();
+  rom_error_t error = kErrorMaskRomBootFailed;
+  for (size_t i = 0; i < ARRAYSIZE(manifests.ordered); ++i) {
+    error = mask_rom_verify(manifests.ordered[i]);
+    if (error != kErrorOk) {
+      continue;
+    }
+    // Boot fails if a verified ROM_EXT cannot be booted.
+    RETURN_IF_ERROR(mask_rom_boot(manifests.ordered[i]));
+    // `mask_rom_boot()` should never return `kErrorOk`, but if it does
+    // we must shut down the chip instead of trying the next ROM_EXT.
+    return kErrorMaskRomBootFailed;
+  }
+  return error;
+}
+
+void mask_rom_main(void) {
+  mask_rom_init();
+
+  // TODO(#7894): What (if anything) should we print at startup?
+  base_printf("OpenTitan: \"version-tag\"\r\n");
+  base_printf("lc_state: %s\r\n", lifecycle_state_name[lc_state]);
+
+  rom_error_t error = mask_rom_try_boot();
+  shutdown_finalize(error);
+}
+
 void mask_rom_interrupt_handler(void) {
   shutdown_finalize(mask_rom_irq_error());
 }
@@ -62,156 +172,3 @@
 
 void mask_rom_nmi_handler(void)
     __attribute__((alias("mask_rom_interrupt_handler")));
-
-rom_error_t mask_rom_boot(void) {
-  // Initialize the shutdown policy according to lifecycle state.
-  lifecycle_state_t lc_state = lifecycle_state_get();
-  shutdown_init(lc_state);
-
-  // Initiaize shadow copy of the ePMP register configuration.
-  epmp_state_t epmp;
-  mask_rom_epmp_state_init(&epmp);
-
-  // Initialize pinmux configuration so we can use the UART.
-  pinmux_init();
-
-  // Configure UART0 as stdout.
-  uart_init(kUartNCOValue);
-  base_set_stdout((buffer_sink_t){
-      .data = NULL,
-      .sink = uart_sink,
-  });
-
-  // FIXME: what (if anything) should we print at startup?
-  base_printf("OpenTitan: \"version-tag\"\r\n");
-  base_printf("lc_state: %s\r\n", lifecycle_state_name[lc_state]);
-
-  // boot_reason = read_boot_reason(); // Boot Policy Module
-
-  // Clean Device State Part 2.
-  // See "Cleaning Device State" Below.
-  // clean_device_state_part_2(boot_reason); // Chip-Specific Startup Module
-
-  // Chip-specific startup functionality (NOTE: we expect this portion of
-  // initialization to be done in assembly before C runtime init.  Delete
-  // this segment of comments & pseudo-code when done).
-  // **Open Q:** Proprietary Software Strategy.
-  // - Clocks
-  // - AST / Entropy.
-  // - Flash
-  // - Fuses
-  // chip_specific_startup(); // Chip-Specific Startup Module
-
-  // Manufacturing boot-strapping intervention.
-  // **Open Q:** How does the Mask ROM deal with chip recovery?
-  // Tentative answer: recovery is performed by ROM_EXT.
-  // **Open Q:** Pin Strapping Configuration.
-  // manufacturing_boot_strap(boot_policy, boot_reason); // Bootstrap Module
-
-  // Read Boot Policy for ROM_EXTs from flash (2.b)
-  // boot_policy = read_boot_policy(boot_reason); // Boot Policy Module
-
-  // Determine which ROM_EXT slot is prioritised (2.b, 2.c.i)
-  // for ( current_rom_ext_manifest in rom_ext_manifests_to_try(boot_policy) ) {
-  // // Boot Policy Module
-  while (true) {
-    // Check ROM_EXT Manifest (2.c.ii)
-    // **Open Q:** Integration with Secure Boot Hardware
-    // - Header Format (ROM_EXT Manifest Module)
-    // - **Open Q**: ROM_EXT Anti-rollback (??)
-    // if (!check_rom_ext_manifest(current_rom_ext_manifest)) {
-    //  // Manifest Failure (check Boot Policy)
-    //  if (try_next_on_manifest_failed(boot_policy)) // Boot Policy Module
-    //    continue
-    //  else
-    //    break
-    //}
-
-    const manifest_t *manifest = boot_policy_manifests_get().ordered[0];
-    const sigverify_rsa_key_t *key;
-    RETURN_IF_ERROR(boot_policy_manifest_check(manifest));
-    manifest_signed_region_t signed_region =
-        manifest_signed_region_get(manifest);
-    RETURN_IF_ERROR(sigverify_rsa_key_get(
-        sigverify_rsa_key_id_get(&manifest->modulus), lc_state, &key));
-    RETURN_IF_ERROR(sigverify_rsa_verify(signed_region.start,
-                                         signed_region.length,
-                                         &manifest->signature, key, lc_state));
-
-    //  // Manifest Failure (check Boot Policy)
-    //  // **Open Q:** Does this need different logic to the check after
-    //  //   `check_rom_ext_manifest`?
-    //  if (try_next_on_manifest_failed(boot_policy)) // Boot Policy Module
-    //    continue
-    //  else
-    //    break
-    //}
-
-    // Update Boot Policy on Successful Signature
-    // **Open Q:** Does this ensure ROM_EXT Anti-rollback is updated?
-    // update_next_boot_policy(boot_policy, current_rom_ext_manifest); // Boot
-    // Policy Module
-
-    // Beyond this point, you know `current_rom_ext_manifest` is valid and the
-    // signature has been authenticated.
-    //
-    // The Mask ROM now has to do various things to prepare to execute the
-    // current ROM_EXT. Most of this is initializing identities, keys, and
-    // potentially locking down some hardware, before jumping to ROM_EXT.
-    //
-    // If any of these steps fail, we've probably left the hardware in an
-    // invalid state and should just reboot (because it may not be possible to
-    // undo the changes, for instance to write-enable bits).
-
-    // CreatorRootKey (2.c.iv)
-    // - This is only allowed to be done if we have verified the signature on
-    //   the current ROM_EXT.
-    RETURN_IF_ERROR(keymgr_check_state(kKeymgrStateReset));
-    keymgr_set_next_stage_inputs(&manifest->binding_value,
-                                 manifest->max_key_version);
-
-    // Lock down Peripherals based on descriptors in ROM_EXT manifest.
-    // - This does not cover key-related lockdown, which is done in
-    //   `derive_and_lock_creator_root_key_inputs`.
-    // peripheral_lockdown(current_rom_ext_manifest); // Lockdown Module
-
-    // PMP Region for ROM_EXT (2.c.v)
-    // **Open Q:** Integration with Secure Boot Hardware
-    // **Open Q:** Do we need to prevent access to Mask ROM after final jump?
-    // pmp_unlock_rom_ext(); // Hardened Jump Module.
-
-    // Transfer Execution to ROM_EXT (2.c.vi)
-    // **Open Q:** Integration with Secure Boot Hardware
-    // **Open Q:** Accumulated measurements to tie manifest to hardware config.
-    // if (!final_jump_to_rom_ext(current_rom_ext_manifest)) { // Hardened Jump
-    // Module
-    if (true) {
-      // Unlock execution of ROM_EXT executable code (text) sections.
-      RETURN_IF_ERROR(epmp_state_check(&epmp));
-      mask_rom_epmp_unlock_rom_ext_rx(&epmp,
-                                      manifest_code_region_get(manifest));
-      RETURN_IF_ERROR(epmp_state_check(&epmp));
-
-      // Jump to ROM_EXT entry point.
-      uintptr_t entry_point = manifest_entry_point_get(manifest);
-      base_printf("rom_ext_entry: %p\r\n", entry_point);
-      ((rom_ext_entry_point *)entry_point)();
-      // NOTE: never expecting a return, but if something were to go wrong
-      // in the real `jump` implementation, we need to enter a failure case.
-
-      // Boot Failure (no policy check)
-      // - Because we may have locked some write-enable bits that any following
-      //   manifest cannot change if it needs to, we have to just reboot here.
-      // boot_failed(boot_policy, current_rom_ext_manifest); // Boot Policy
-      // Module
-    }
-  }
-
-  return kErrorMaskRomBootFailed;
-}
-
-void mask_rom_main(void) {
-  sec_mmio_init(shutdown_finalize);
-  rom_error_t error = mask_rom_boot();
-  shutdown_finalize(error);
-}