[sw/silicon_creator] Introduce romextimage module

Signed-off-by: Alphan Ulusoy <alphan@google.com>
diff --git a/sw/device/silicon_creator/lib/error.h b/sw/device/silicon_creator/lib/error.h
index 90750d6..4c5ec89 100644
--- a/sw/device/silicon_creator/lib/error.h
+++ b/sw/device/silicon_creator/lib/error.h
@@ -21,12 +21,13 @@
  */
 enum module_ {
   kModuleUnknown = 0,
-  kModuleUart = 0x4155,       // ASCII "UA".
-  kModuleHmac = 0x4d48,       // ASCII "HM".
-  kModuleSigverify = 0x5653,  // ASCII "SV".
-  kModuleOtp = 0x504f,        // ASCII "OP".
-  kModuleKeymgr = 0x4d4b,     // ASCII "KM".
-  kModuleManifest = 0x414d,   // ASCII "MA".
+  kModuleUart = 0x4155,         // ASCII "UA".
+  kModuleHmac = 0x4d48,         // ASCII "HM".
+  kModuleSigverify = 0x5653,    // ASCII "SV".
+  kModuleOtp = 0x504f,          // ASCII "OP".
+  kModuleKeymgr = 0x4d4b,       // ASCII "KM".
+  kModuleManifest = 0x414d,     // ASCII "MA".
+  kModuleRomextimage = 0x4552,  // ASCII "RE".
 };
 
 /**
@@ -46,14 +47,16 @@
 //       after the ROM is frozen.
 #define DEFINE_ERRORS(X) \
   X(kErrorOk, 0x739), \
-  X(kErrorUartInvalidArgument,      ERROR_(1, kModuleUart, kInvalidArgument)), \
-  X(kErrorUartBadBaudRate,          ERROR_(2, kModuleUart, kInvalidArgument)), \
-  X(kErrorHmacInvalidArgument,      ERROR_(1, kModuleHmac, kInvalidArgument)), \
-  X(kErrorSigverifyInvalidArgument, ERROR_(1, kModuleSigverify, kInvalidArgument)), \
-  X(kErrorOtpBusy,                  ERROR_(1, kModuleOtp, kUnavailable)),  \
-  X(kErrorOtpUnknown,               ERROR_(2, kModuleOtp, kUnknown)), \
-  X(kErrorKeymgrInternal,           ERROR_(1, kModuleKeymgr, kInternal)), \
-  X(kErrorManifestInternal,         ERROR_(1, kModuleManifest, kInternal)), \
+  X(kErrorUartInvalidArgument,        ERROR_(1, kModuleUart, kInvalidArgument)), \
+  X(kErrorUartBadBaudRate,            ERROR_(2, kModuleUart, kInvalidArgument)), \
+  X(kErrorHmacInvalidArgument,        ERROR_(1, kModuleHmac, kInvalidArgument)), \
+  X(kErrorSigverifyInvalidArgument,   ERROR_(1, kModuleSigverify, kInvalidArgument)), \
+  X(kErrorOtpBusy,                    ERROR_(1, kModuleOtp, kUnavailable)),  \
+  X(kErrorOtpUnknown,                 ERROR_(2, kModuleOtp, kUnknown)), \
+  X(kErrorKeymgrInternal,             ERROR_(1, kModuleKeymgr, kInternal)), \
+  X(kErrorManifestInternal,           ERROR_(1, kModuleManifest, kInternal)), \
+  X(kErrorRomextimageInvalidArgument, ERROR_(1, kModuleRomextimage, kInvalidArgument)), \
+  X(kErrorRomextimageInternal,        ERROR_(2, kModuleRomextimage, kInternal)), \
   X(kErrorUnknown, 0xFFFFFFFF)
 // clang-format on
 
diff --git a/sw/device/silicon_creator/mask_rom/mask_rom.c b/sw/device/silicon_creator/mask_rom/mask_rom.c
index 4a77c7b..0b76c46 100644
--- a/sw/device/silicon_creator/mask_rom/mask_rom.c
+++ b/sw/device/silicon_creator/mask_rom/mask_rom.c
@@ -16,13 +16,11 @@
 #include "sw/device/lib/runtime/print.h"
 #include "sw/device/silicon_creator/lib/drivers/keymgr.h"
 #include "sw/device/silicon_creator/lib/drivers/uart.h"
+#include "sw/device/silicon_creator/mask_rom/romextimage.h"
 #include "sw/device/silicon_creator/mask_rom/sig_verify.h"
-#include "sw/device/silicon_creator/rom_exts/rom_ext_manifest_parser.h"
 
 #include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
 
-typedef void(boot_fn)(void);
-
 void mask_rom_exception_handler(void) { wait_for_interrupt(); }
 void mask_rom_nmi_handler(void) { wait_for_interrupt(); }
 
@@ -91,25 +89,17 @@
     //    break
     //}
 
-    // TODO: Range checks for manifest fields used before signature
-    // verification.
-    rom_ext_manifest_t rom_ext = rom_ext_get_parameters(kRomExtManifestSlotA);
-    rom_ext_signature_key_modulus_t key_modulus;
-    if (!rom_ext_get_signature_key_modulus(rom_ext, &key_modulus)) {
+    const manifest_t *manifest;
+    manifest_signed_region_t signed_region;
+    if (romextimage_manifest_get(kFlashSlotA, &manifest) != kErrorOk) {
       break;
     }
-    rom_ext_signature_t rom_ext_signature;
-    if (!rom_ext_get_signature(rom_ext, &rom_ext_signature)) {
+    if (manifest_signed_region_get(manifest, &signed_region) != kErrorOk) {
       break;
     }
-    sigverify_rsa_buffer_t signature;
-    memcpy(signature.data, rom_ext_signature.data, sizeof(signature.data));
-    rom_ext_ranges_t ranges = rom_ext_get_ranges(rom_ext);
-
     if (sigverify_rom_ext_signature_verify(
-            (void *)ranges.signed_area_start,
-            ranges.image_end - ranges.signed_area_start, &signature,
-            key_modulus.data[0]) != kErrorOk) {
+            signed_region.start, signed_region.length, &manifest->signature,
+            manifest->modulus.data[0]) != kErrorOk) {
       break;
     }
 
@@ -171,14 +161,11 @@
     // if (!final_jump_to_rom_ext(current_rom_ext_manifest)) { // Hardened Jump
     // Module
     if (true) {
-      // Set mtvec for ROM_EXT.
-      uintptr_t interrupt_vector = rom_ext_get_interrupt_vector(rom_ext);
-      CSR_WRITE(CSR_REG_MTVEC, (uint32_t)interrupt_vector);
-
       // Jump to ROM_EXT entry point.
-      boot_fn *rom_ext_entry = (boot_fn *)rom_ext_get_entry(rom_ext);
-      base_printf("rom_ext_entry: %p\r\n", rom_ext_entry);
-      rom_ext_entry();
+      romextimage_entry_point *entry_point =
+          (romextimage_entry_point *)manifest_entry_point_address_get(manifest);
+      base_printf("rom_ext_entry: %p\r\n", 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.
 
diff --git a/sw/device/silicon_creator/mask_rom/meson.build b/sw/device/silicon_creator/mask_rom/meson.build
index f6039c2..fd267fe 100644
--- a/sw/device/silicon_creator/mask_rom/meson.build
+++ b/sw/device/silicon_creator/mask_rom/meson.build
@@ -28,6 +28,16 @@
   ),
 )
 
+# ROM_EXT image.
+sw_silicon_creator_mask_rom_romextimage = declare_dependency(
+  link_with: static_library(
+    'sw_silicon_creator_mask_rom_romextimage',
+    sources: [
+      'romextimage.c',
+    ],
+  ),
+)
+
 # MaskROM library.
 mask_rom_lib = declare_dependency(
   sources: [
@@ -46,6 +56,7 @@
       sw_silicon_creator_lib_manifest,
       sw_silicon_creator_mask_rom_sigverify,
       rom_ext_manifest_parser,
+      sw_silicon_creator_mask_rom_romextimage,
       sw_lib_crt,
       sw_lib_pinmux,
       sw_lib_runtime_print,
@@ -157,3 +168,19 @@
   ),
   suite: 'mask_rom',
 )
+
+test('sw_silicon_creator_mask_rom_romextimage_unittest', executable(
+    'sw_silicon_creator_mask_rom_romextimage_unittest',
+    sources: [
+      'romextimage_unittest.cc',
+      'romextimage.c',
+    ],
+    dependencies: [
+      sw_vendor_gtest,
+    ],
+    native: true,
+    c_args: ['-DOT_OFF_TARGET_TEST'],
+    cpp_args: ['-DOT_OFF_TARGET_TEST'],
+  ),
+  suite: 'mask_rom',
+)
diff --git a/sw/device/silicon_creator/mask_rom/mock_romextimage_ptrs.h b/sw/device/silicon_creator/mask_rom/mock_romextimage_ptrs.h
new file mode 100644
index 0000000..0616427
--- /dev/null
+++ b/sw/device/silicon_creator/mask_rom/mock_romextimage_ptrs.h
@@ -0,0 +1,43 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#ifndef OPENTITAN_SW_DEVICE_SILICON_CREATOR_MASK_ROM_MOCK_ROMEXTIMAGE_PTRS_H_
+#define OPENTITAN_SW_DEVICE_SILICON_CREATOR_MASK_ROM_MOCK_ROMEXTIMAGE_PTRS_H_
+
+#include "sw/device/lib/testing/mask_rom_test.h"
+#include "sw/device/silicon_creator/mask_rom/romextimage_ptrs.h"
+
+namespace mask_rom_test {
+namespace internal {
+
+/**
+ * Mock class for romextimage_ptrs.h
+ */
+class MockRomextimagePtrs {
+ public:
+  MOCK_METHOD(const manifest_t *, slot_a_manifest_ptr_get, ());
+  MOCK_METHOD(const manifest_t *, slot_b_manifest_ptr_get, ());
+
+  virtual ~MockRomextimagePtrs() {}
+};
+
+}  // namespace internal
+
+using MockRomextimagePtrs =
+    GlobalMock<testing::StrictMock<internal::MockRomextimagePtrs>>;
+
+extern "C" {
+
+const manifest_t *romextimage_slot_a_manifest_ptr_get() {
+  return MockRomextimagePtrs::Instance().slot_a_manifest_ptr_get();
+}
+
+const manifest_t *romextimage_slot_b_manifest_ptr_get() {
+  return MockRomextimagePtrs::Instance().slot_b_manifest_ptr_get();
+}
+
+}  // extern "C"
+}  // namespace mask_rom_test
+
+#endif  // OPENTITAN_SW_DEVICE_SILICON_CREATOR_MASK_ROM_MOCK_ROMEXTIMAGE_PTRS_H_
diff --git a/sw/device/silicon_creator/mask_rom/romextimage.c b/sw/device/silicon_creator/mask_rom/romextimage.c
new file mode 100644
index 0000000..5ab9a63
--- /dev/null
+++ b/sw/device/silicon_creator/mask_rom/romextimage.c
@@ -0,0 +1,31 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#include "sw/device/silicon_creator/mask_rom/romextimage.h"
+
+#include "sw/device/silicon_creator/lib/error.h"
+#include "sw/device/silicon_creator/mask_rom/romextimage_ptrs.h"
+
+rom_error_t romextimage_manifest_get(flash_slot_t slot,
+                                     const manifest_t **manifest) {
+  const manifest_t *ptr;
+  switch (slot) {
+    case kFlashSlotA:
+      ptr = romextimage_slot_a_manifest_ptr_get();
+      break;
+    case kFlashSlotB:
+      ptr = romextimage_slot_b_manifest_ptr_get();
+      break;
+    default:
+      return kErrorRomextimageInvalidArgument;
+  }
+  if (ptr->identifier != kRomextimageManifestIdentifier) {
+    return kErrorRomextimageInternal;
+  }
+  *manifest = ptr;
+  return kErrorOk;
+}
+
+extern const manifest_t *romextimage_slot_a_manifest_ptr_get(void);
+extern const manifest_t *romextimage_slot_b_manifest_ptr_get(void);
diff --git a/sw/device/silicon_creator/mask_rom/romextimage.h b/sw/device/silicon_creator/mask_rom/romextimage.h
new file mode 100644
index 0000000..b5f0578
--- /dev/null
+++ b/sw/device/silicon_creator/mask_rom/romextimage.h
@@ -0,0 +1,47 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#ifndef OPENTITAN_SW_DEVICE_SILICON_CREATOR_MASK_ROM_ROMEXTIMAGE_H_
+#define OPENTITAN_SW_DEVICE_SILICON_CREATOR_MASK_ROM_ROMEXTIMAGE_H_
+
+#include "sw/device/silicon_creator/lib/error.h"
+#include "sw/device/silicon_creator/lib/manifest.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif  // __cplusplus
+
+/**
+ * Type alias for the ROM_EXT entry point.
+ *
+ * The entry point address obtained from the ROM_EXT manifest must be cast to a
+ * pointer to this type before being called.
+ */
+typedef void romextimage_entry_point(void);
+
+enum {
+  /**
+   * ROM_EXT manifest identifier (ASCII "OTRE").
+   */
+  kRomextimageManifestIdentifier = 0x4552544f,
+};
+
+/**
+ * Gets the manifest of the ROM_EXT image in the given slot.
+ *
+ * This function also checks that the manifest's identifier field has the
+ * expected value.
+ *
+ * @param slot A flash slot.
+ * @param[out] The manifest of the ROM_EXT image in the given slot.
+ * @return The result of the operation.
+ */
+rom_error_t romextimage_manifest_get(flash_slot_t slot,
+                                     const manifest_t **manifest);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif  // __cplusplus
+
+#endif  // OPENTITAN_SW_DEVICE_SILICON_CREATOR_MASK_ROM_ROMEXTIMAGE_H_
diff --git a/sw/device/silicon_creator/mask_rom/romextimage_ptrs.h b/sw/device/silicon_creator/mask_rom/romextimage_ptrs.h
new file mode 100644
index 0000000..7360ed6
--- /dev/null
+++ b/sw/device/silicon_creator/mask_rom/romextimage_ptrs.h
@@ -0,0 +1,52 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#ifndef OPENTITAN_SW_DEVICE_SILICON_CREATOR_MASK_ROM_ROMEXTIMAGE_PTRS_H_
+#define OPENTITAN_SW_DEVICE_SILICON_CREATOR_MASK_ROM_ROMEXTIMAGE_PTRS_H_
+
+#include "sw/device/silicon_creator/lib/manifest.h"
+
+#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif  // __cplusplus
+
+static_assert((TOP_EARLGREY_EFLASH_SIZE_BYTES % 2) == 0,
+              "Flash size is not divisible by 2");
+
+#ifndef OT_OFF_TARGET_TEST
+/**
+ * Returns a pointer to the manifest of the ROM_EXT image stored in flash
+ * slot A.
+ *
+ * @return Pointer to the manifest of the ROM_EXT image in slot A.
+ */
+inline const manifest_t *romextimage_slot_a_manifest_ptr_get(void) {
+  return (const manifest_t *)TOP_EARLGREY_EFLASH_BASE_ADDR;
+}
+
+/**
+ * Returns a pointer to the manifest of the ROM_EXT image stored in flash
+ * slot B.
+ *
+ * @return Pointer to the manifest of the ROM_EXT image in slot B.
+ */
+inline const manifest_t *romextimage_slot_b_manifest_ptr_get(void) {
+  return (const manifest_t *)(TOP_EARLGREY_EFLASH_BASE_ADDR +
+                              (TOP_EARLGREY_EFLASH_SIZE_BYTES / 2));
+}
+#else
+/**
+ * Declarations for the functions above that should be defined in tests.
+ */
+const manifest_t *romextimage_slot_a_manifest_ptr_get(void);
+const manifest_t *romextimage_slot_b_manifest_ptr_get(void);
+#endif
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif  // __cplusplus
+
+#endif  // OPENTITAN_SW_DEVICE_SILICON_CREATOR_MASK_ROM_ROMEXTIMAGE_PTRS_H_
diff --git a/sw/device/silicon_creator/mask_rom/romextimage_unittest.cc b/sw/device/silicon_creator/mask_rom/romextimage_unittest.cc
new file mode 100644
index 0000000..82c41b7
--- /dev/null
+++ b/sw/device/silicon_creator/mask_rom/romextimage_unittest.cc
@@ -0,0 +1,61 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#include "sw/device/silicon_creator/mask_rom/romextimage.h"
+
+#include "gtest/gtest.h"
+#include "sw/device/lib/testing/mask_rom_test.h"
+#include "sw/device/silicon_creator/lib/error.h"
+#include "sw/device/silicon_creator/mask_rom/mock_romextimage_ptrs.h"
+
+namespace manifest_unittest {
+namespace {
+using ::testing::Return;
+
+class RomExtImage : public mask_rom_test::MaskRomTest {
+ protected:
+  mask_rom_test::MockRomextimagePtrs romextimage_ptrs_;
+  manifest_t manifest_;
+};
+
+TEST_F(RomExtImage, ManifestGet) {
+  const manifest_t *act_manifest;
+  manifest_.identifier = kRomextimageManifestIdentifier;
+
+  EXPECT_CALL(romextimage_ptrs_, slot_a_manifest_ptr_get)
+      .WillOnce(Return(&manifest_));
+  EXPECT_EQ(romextimage_manifest_get(kFlashSlotA, &act_manifest), kErrorOk);
+  EXPECT_EQ(act_manifest, &manifest_);
+
+  act_manifest = nullptr;
+  EXPECT_CALL(romextimage_ptrs_, slot_b_manifest_ptr_get)
+      .WillOnce(Return(&manifest_));
+  EXPECT_EQ(romextimage_manifest_get(kFlashSlotB, &act_manifest), kErrorOk);
+  EXPECT_EQ(act_manifest, &manifest_);
+}
+
+TEST_F(RomExtImage, ManifestGetBadIdentifier) {
+  const manifest_t *act_manifest;
+
+  EXPECT_CALL(romextimage_ptrs_, slot_a_manifest_ptr_get)
+      .WillOnce(Return(&manifest_));
+  EXPECT_EQ(romextimage_manifest_get(kFlashSlotA, &act_manifest),
+            kErrorRomextimageInternal);
+
+  EXPECT_CALL(romextimage_ptrs_, slot_b_manifest_ptr_get)
+      .WillOnce(Return(&manifest_));
+  EXPECT_EQ(romextimage_manifest_get(kFlashSlotB, &act_manifest),
+            kErrorRomextimageInternal);
+}
+
+TEST_F(RomExtImage, ManifestGetBadSlot) {
+  const manifest_t *act_manifest;
+
+  EXPECT_EQ(romextimage_manifest_get(static_cast<flash_slot_t>(kFlashSlotB + 1),
+                                     &act_manifest),
+            kErrorRomextimageInvalidArgument);
+}
+
+}  // namespace
+}  // namespace manifest_unittest