[sw/ottf] Rename test_rom_ext to ottf_start

Previously the boot sequence for chip level tests looked like:

chip reset --> test_rom --> test_rom_ext --> ottf --> test_main()

In the above sequence the `test_rom_ext` was really just an ASM file
that performed initializations prior to jumping to the OTTF `main()`.
However, as we refactor the OTTF to enable running chip-level tests at
various boot stages (e.g., ROM_EXT and BL0, see #10498), it makes more
sense to model the combined OTTF and test bundle as its own boot stage
that can be run in inplace of the ROM_EXT or BL0.

Therefore, this commit renames/consolidates the test_rom_ext into a
component of the OTTF, called the `ottf_start`. The `ottf_start` is kept
as a separate static library from the `ottf` to enable running tests
that do not use the OTTF, i.e. the crt_test (which tests the
functionality of the `ottf_start` library), and the "Hello World"
example programs. However, when test images are built, both the
`ottf_start` and `ottf` are linked with the test library itself, to
create one cohesive "test boot stage".

The new boot sequence for a test will look like:

chip reset --> test_rom --> ottf --> test_main(),

where OTTF = (ottf_start --> OTTF).

This partially addresses a task in #10498.

Signed-off-by: Timothy Trippel <ttrippel@google.com>
diff --git a/hw/top_englishbreakfast/util/sw_sources.patch b/hw/top_englishbreakfast/util/sw_sources.patch
index 54cbca3..2a93e3f 100644
--- a/hw/top_englishbreakfast/util/sw_sources.patch
+++ b/hw/top_englishbreakfast/util/sw_sources.patch
@@ -105,7 +105,7 @@
      simple_serial_process_packet();
    }
 diff --git a/sw/device/sca/lib/meson.build b/sw/device/sca/lib/meson.build
-index c4a65f7e0..3f6ee67f1 100644
+index 3a613b2b8..fa939775a 100644
 --- a/sw/device/sca/lib/meson.build
 +++ b/sw/device/sca/lib/meson.build
 @@ -17,8 +17,6 @@ sw_sca_lib_sca  = declare_dependency(
@@ -115,8 +115,8 @@
 -      sw_lib_dif_csrng,
 -      sw_lib_dif_edn,
        sw_lib_irq,
-       sw_lib_testing_ottf_isrs,
        sw_lib_mmio,
+       sw_lib_pinmux,
 diff --git a/sw/device/sca/lib/sca.c b/sw/device/sca/lib/sca.c
 index 3c0c86f26..7354aa9cb 100644
 --- a/sw/device/sca/lib/sca.c
@@ -257,10 +257,10 @@
    CHECK_DIF_OK(
        dif_aes_init(mmio_region_from_addr(TOP_EARLGREY_AES_BASE_ADDR), &aes));
 diff --git a/sw/device/tests/meson.build b/sw/device/tests/meson.build
-index 8e60b6e41..6036f1353 100644
+index 529f26e38..e638f25e1 100644
 --- a/sw/device/tests/meson.build
 +++ b/sw/device/tests/meson.build
-@@ -253,7 +253,6 @@ aes_smoketest_lib = declare_dependency(
+@@ -254,7 +254,6 @@ aes_smoketest_lib = declare_dependency(
        sw_lib_dif_aes,
        sw_lib_mmio,
        sw_lib_runtime_log,
diff --git a/rules/opentitan.bzl b/rules/opentitan.bzl
index d8633f4..48f1647 100644
--- a/rules/opentitan.bzl
+++ b/rules/opentitan.bzl
@@ -301,15 +301,14 @@
     return [x.format(**kwargs) for x in list1 + datadict.pop(name, [])]
 
 _OTTF_DEPS = [
+    "//sw/device/lib/arch:device",
     "//sw/device/lib/base",
     "//sw/device/lib/runtime:hart",
     "//sw/device/lib/runtime:log",
     "//sw/device/lib/runtime:print",
-    "//sw/device/lib/arch:device",
-    "//sw/device/lib/testing/test_rom_ext",
     "//sw/device/lib/crt",
+    "//sw/device/lib/testing/test_framework:ottf_start",
     "//sw/device/lib/testing/test_framework:ottf",
-    "//sw/device/lib/testing/test_framework:ottf_isrs",
     "//sw/device/lib/base:mmio",
 ]
 
diff --git a/sw/device/benchmarks/coremark/meson.build b/sw/device/benchmarks/coremark/meson.build
index c255259..bee242f 100644
--- a/sw/device/benchmarks/coremark/meson.build
+++ b/sw/device/benchmarks/coremark/meson.build
@@ -18,11 +18,10 @@
     ],
     name_suffix: 'elf',
     dependencies: [
+      device_lib,
+      ottf_start_lib,
       sw_lib_dif_uart,
       sw_lib_mem,
-      test_rom_ext,
-      device_lib,
-      sw_lib_testing_ottf_isrs,
       sw_lib_testing_test_status,
     ],
     # Set up coremark-specific defines.
diff --git a/sw/device/examples/hello_usbdev/BUILD b/sw/device/examples/hello_usbdev/BUILD
index 3f32a0c..60e4cd6 100644
--- a/sw/device/examples/hello_usbdev/BUILD
+++ b/sw/device/examples/hello_usbdev/BUILD
@@ -40,7 +40,6 @@
         "//sw/device/lib/runtime:log",
         "//sw/device/lib/runtime:print",
         "//sw/device/lib/testing/test_framework",
-        "//sw/device/lib/testing/test_framework:ottf_isrs",
-        "//sw/device/lib/testing/test_rom_ext",
+        "//sw/device/lib/testing/test_framework:ottf_start",
     ],
 )
diff --git a/sw/device/examples/hello_usbdev/meson.build b/sw/device/examples/hello_usbdev/meson.build
index 3abac44..ffe5862 100644
--- a/sw/device/examples/hello_usbdev/meson.build
+++ b/sw/device/examples/hello_usbdev/meson.build
@@ -11,6 +11,8 @@
     ],
     name_suffix: 'elf',
     dependencies: [
+      device_lib,
+      ottf_start_lib,
       sw_examples_demos,
       sw_lib_runtime_hart,
       sw_lib_pinmux,
@@ -21,9 +23,6 @@
       sw_lib_runtime_log,
       sw_lib_dif_uart,
       sw_lib_usb,
-      test_rom_ext,
-      device_lib,
-      sw_lib_testing_ottf_isrs,
       sw_lib_testing_test_status,
     ],
   )
diff --git a/sw/device/examples/hello_world/BUILD b/sw/device/examples/hello_world/BUILD
index 8c2947b..80de671 100644
--- a/sw/device/examples/hello_world/BUILD
+++ b/sw/device/examples/hello_world/BUILD
@@ -37,7 +37,6 @@
         "//sw/device/lib/runtime:log",
         "//sw/device/lib/runtime:print",
         "//sw/device/lib/testing/test_framework",
-        "//sw/device/lib/testing/test_framework:ottf_isrs",
-        "//sw/device/lib/testing/test_rom_ext",
+        "//sw/device/lib/testing/test_framework:ottf_start",
     ],
 )
diff --git a/sw/device/examples/hello_world/meson.build b/sw/device/examples/hello_world/meson.build
index f26f62e..f5f40d1 100644
--- a/sw/device/examples/hello_world/meson.build
+++ b/sw/device/examples/hello_world/meson.build
@@ -8,6 +8,8 @@
     sources: ['hello_world.c'],
     name_suffix: 'elf',
     dependencies: [
+      device_lib,
+      ottf_start_lib,
       sw_examples_demos,
       sw_lib_runtime_hart,
       sw_lib_runtime_print,
@@ -17,9 +19,6 @@
       sw_lib_irq,
       sw_lib_dif_spi_device,
       sw_lib_dif_uart,
-      test_rom_ext,
-      device_lib,
-      sw_lib_testing_ottf_isrs,
       sw_lib_testing_test_status,
     ],
   )
diff --git a/sw/device/lib/testing/meson.build b/sw/device/lib/testing/meson.build
index 7062e72..bf870cf 100644
--- a/sw/device/lib/testing/meson.build
+++ b/sw/device/lib/testing/meson.build
@@ -175,4 +175,3 @@
 
 subdir('test_framework')
 subdir('test_rom')
-subdir('test_rom_ext')
diff --git a/sw/device/lib/testing/test_framework/BUILD b/sw/device/lib/testing/test_framework/BUILD
index bbc309e..bdeeaf0 100644
--- a/sw/device/lib/testing/test_framework/BUILD
+++ b/sw/device/lib/testing/test_framework/BUILD
@@ -2,10 +2,10 @@
 # Licensed under the Apache License, Version 2.0, see LICENSE for details.
 # SPDX-License-Identifier: Apache-2.0
 
-package(default_visibility = ["//visibility:public"])
-
 load("//rules:opentitan.bzl", "OPENTITAN_CPU")
 
+package(default_visibility = ["//visibility:public"])
+
 # TODO use more specific targets to avoid unnecesary dependencies
 # https://github.com/lowRISC/opentitan/issues/9098
 cc_library(
@@ -79,6 +79,36 @@
 )
 
 cc_library(
+    name = "ottf_start",
+    srcs = [
+        "ottf_isrs.S",
+        "ottf_isrs.c",
+        "ottf_start.S",
+    ],
+    hdrs = [
+        "ottf_isrs.h",
+        "ottf_macros.h",
+    ],
+    copts = [
+        "-nostdlib",
+        "-ffreestanding",
+    ],
+    linkopts = [
+        "-T $(location ottf.ld)",
+    ],
+    target_compatible_with = [OPENTITAN_CPU],
+    deps = [
+        "ottf.ld",
+        "//hw/top_earlgrey/sw/autogen:linker_script",
+        "//sw/device:info_sections",
+        "//sw/device/lib/base",
+        "//sw/device/lib/crt",
+        "//sw/device/lib/runtime:hart",
+        "//sw/device/lib/runtime:log",
+    ],
+)
+
+cc_library(
     name = "ottf",
     srcs = [
         "ottf.c",
@@ -109,18 +139,3 @@
         "//sw/vendor/freertos_freertos_kernel:kernel",
     ],
 )
-
-cc_library(
-    name = "ottf_isrs",
-    srcs = [
-        "ottf_isrs.S",
-        "ottf_isrs.c",
-    ],
-    hdrs = [
-        "ottf_isrs.h",
-    ],
-    target_compatible_with = [OPENTITAN_CPU],
-    deps = [
-        ":freertos_port",
-    ],
-)
diff --git a/sw/device/lib/testing/test_framework/meson.build b/sw/device/lib/testing/test_framework/meson.build
index 33adaee..c49285c 100644
--- a/sw/device/lib/testing/test_framework/meson.build
+++ b/sw/device/lib/testing/test_framework/meson.build
@@ -49,27 +49,54 @@
   )
 endif
 
-# OTTF (Default) Interrupt Service Routines
+# OTTF linker parameters.
+ottf_linker_script = '@0@/@1@'.format(meson.source_root(), files(['ottf.ld'])[0])
+ottf_linker_args = [
+  # The linker script includes an autogenerated definition of the available
+  # memory regions. The include path is given relative to the source root, so we
+  # need to add the source root to the linker lookup path (which is the same for
+  # libraries and linker scripts).
+  '-Wl,-L,@0@'.format(meson.source_root()),
+  '-Wl,-T,@0@'.format(ottf_linker_script),
+  # Depending on the compiler and its configuration (*), |--build-id| is passed
+  # to the linker, causing the insertion of a ".note.gnu.build-id" section into
+  # the ELF file. We do not use the build ID. Achieve consistent behavior and
+  # potentially a small improvement in linking time by not computing the build
+  # ID in the first place.
+  #
+  # * GCC built with |--enable-linker-build-id|, clang before 3.9 or built with
+  #   |-DENABLE_LINKER_BUILD_ID|.
+  '-Wl,--build-id=none',
+]
+
+# OTTF startup library. Every OpenTitan (RISC-V) test executable
+# should depend on this target.
 #
-# ottf_isrs.c contains various definitions with weak linkage, for interrupt
-# service routine (ISR) symbols referenced from test_rom_ext.
-#
-# To override any default ISRs, implement the declarations in ottf_isrs.h,
-# you intend to override, and link this library or the OTTF below (depending if
-# your tests intends to make use of the OTTF or not).
-sw_lib_testing_ottf_isrs = declare_dependency(
+# The crt defines the interrupt vector, the symbols of which are defined in 
+# in `sw/device/lib/testing/test_framework/ottf_isrs.S`.
+ottf_start_lib = declare_dependency(
+  # The following assembly files need to be included as a source, not a
+  # static library, so that their custom sections can be picked up when
+  # linking.
+  sources: [
+    'ottf_start.S',
+    'ottf_isrs.S',
+  ],
+  link_args: ottf_linker_args,
+  dependencies: [
+    freestanding_headers,
+    sw_lib_crt,
+    sw_lib_mem,
+    sw_lib_runtime_hart,
+    sw_lib_runtime_ibex,
+    sw_lib_runtime_log,
+  ],
   link_with: static_library(
-    'sw_lib_testing_ottf_isrs',
+    'ottf_start_lib',
     sources: [
       'ottf_isrs.c',
-      'ottf_isrs.S',
     ],
-    dependencies: [
-      sw_lib_mem,
-      sw_lib_runtime_hart,
-      sw_lib_runtime_ibex,
-      sw_lib_runtime_log,
-    ],
+    link_depends: [ottf_linker_script],
   )
 )
 
@@ -80,9 +107,9 @@
 ottf_incdirs = include_directories(
   '../../../../vendor/freertos_freertos_kernel/include',
   '../../../../vendor/freertos_freertos_kernel/portable/GCC/RISC-V')
-sw_lib_testing_ottf = declare_dependency(
+ottf_lib = declare_dependency(
   link_with: static_library(
-    'sw_lib_testing_ottf',
+    'ottf_lib',
     sources: [
       'ottf.c',
       'freertos_hooks.c',
@@ -98,13 +125,14 @@
       '-D__riscv_float_abi_soft',
     ],
     dependencies: [
+      ottf_start_lib,
       sw_lib_irq,
       sw_lib_mem,
       sw_lib_dif_uart,
       sw_lib_dif_rv_timer,
+      sw_lib_runtime_hart,
       sw_lib_runtime_log,
       sw_lib_runtime_print,
-      sw_lib_testing_ottf_isrs,
       sw_lib_testing_test_status,
       sw_lib_testing_test_coverage,
     ],
diff --git a/sw/device/lib/testing/test_framework/ottf.c b/sw/device/lib/testing/test_framework/ottf.c
index c5bdc55..02ae166 100644
--- a/sw/device/lib/testing/test_framework/ottf.c
+++ b/sw/device/lib/testing/test_framework/ottf.c
@@ -9,6 +9,7 @@
 
 #include "sw/device/lib/arch/device.h"
 #include "sw/device/lib/dif/dif_uart.h"
+#include "sw/device/lib/runtime/hart.h"
 #include "sw/device/lib/runtime/log.h"
 #include "sw/device/lib/runtime/print.h"
 #include "sw/device/lib/testing/check.h"
@@ -71,8 +72,8 @@
 
   // Run the test.
   if (kTestConfig.enable_concurrency) {
-    // Run `test_main()` in a FreeRTOS task, allowing other FreeRTOS tasks to be
-    // spawned, if requested in the main test task.
+    // Run `test_main()` in a FreeRTOS task, allowing other FreeRTOS tasks to
+    // be spawned, if requested in the main test task.
     xTaskCreate(test_wrapper, "TestTask", configMINIMAL_STACK_SIZE, NULL,
                 tskIDLE_PRIORITY + 1, NULL);
     vTaskStartScheduler();
@@ -82,5 +83,6 @@
   }
 
   // Unreachable code.
+  abort();
   return 1;
 }
diff --git a/sw/device/lib/testing/test_rom_ext/test_rom_ext.ld b/sw/device/lib/testing/test_framework/ottf.ld
similarity index 96%
rename from sw/device/lib/testing/test_rom_ext/test_rom_ext.ld
rename to sw/device/lib/testing/test_framework/ottf.ld
index 7be1be8..d4d4ed9 100644
--- a/sw/device/lib/testing/test_rom_ext/test_rom_ext.ld
+++ b/sw/device/lib/testing/test_framework/ottf.ld
@@ -33,7 +33,7 @@
 _dv_log_offset = 0x10000;
 
 /**
- * The entry point in `test_rom_ext_start.S` is called `_start`. This
+ * The entry point in `ottf_start.S` is called `_start`. This
  * information in the ELF file is not used - instead we use the information in
  * the `.flash_header` section to understand where to start execution of a flash
  * image. We need this declaration so LLD does not error.
@@ -60,7 +60,7 @@
   } > eflash_virtual
 
   /**
-   * Ibex interrupt vectors. See 'test_rom_ext_start.S' for more information.
+   * OTTF interrupt vectors. See 'ottf_start.S' for more information.
    */
   .vectors : {
     *(.vectors)
diff --git a/sw/device/lib/testing/test_rom_ext/test_rom_ext_start.S b/sw/device/lib/testing/test_framework/ottf_start.S
similarity index 89%
rename from sw/device/lib/testing/test_rom_ext/test_rom_ext_start.S
rename to sw/device/lib/testing/test_framework/ottf_start.S
index 462c44c..ae89cfc 100644
--- a/sw/device/lib/testing/test_rom_ext/test_rom_ext_start.S
+++ b/sw/device/lib/testing/test_framework/ottf_start.S
@@ -40,9 +40,9 @@
   .option norelax
 
   .balign 256
-  .global _test_rom_ext_interrupt_vector
-  .type _test_rom_ext_interrupt_vector, @function
-_test_rom_ext_interrupt_vector:
+  .global _interrupt_vector
+  .type _interrupt_vector, @function
+_interrupt_vector:
 
   // RISC-V Standard (Vectored) Interrupt Handlers:
 
@@ -90,7 +90,7 @@
   unimp
 
   // Set size so vector can be disassembled.
-  .size _test_rom_ext_interrupt_vector, .-_test_rom_ext_interrupt_vector
+  .size _interrupt_vector, .-_interrupt_vector
 
   .option pop
 
@@ -102,20 +102,15 @@
   // is allocated space in ROM by the linker.
   .section .crt, "ax", @progbits
 
-  .extern abort
-  .extern main
-  .extern crt_interrupt_vector
-  .extern crt_section_clear
-  .extern crt_section_copy
-
 /**
  * Callable entry point for flash.
  *
  * This sets up the stack, zeroes `.bss`, and sets up `.data`.
  * It then jumps into main.
  */
-_start:
   .globl _start
+  .type _start, @function
+_start:
 
   // Set up the stack. We have no expectation that the rom that
   // jumps here will have the correct stack start linked in.
@@ -129,18 +124,20 @@
   .option pop
 
   // Set up the new interrupt vector.
-  la   t0, (_test_rom_ext_interrupt_vector + 1)
+  la   t0, (_interrupt_vector + 1)
   csrw mtvec, t0
 
   // Zero out the `.bss` segment.
   la   a0, _bss_start
   la   a1, _bss_end
+  .extern crt_section_clear
   call crt_section_clear
 
   // Initialize the `.data` segment from the `.idata` segment.
   la   a0, _data_start
   la   a1, _data_end
   la   a2, _data_init_start
+  .extern crt_section_copy
   call crt_section_copy
 
   // Call the functions in the `.init_array` section.
@@ -163,11 +160,12 @@
   bltu s0, s1, init_array_loop
 init_array_loop_end:
 
-  // Jump into the C program entry point. This is your standard
-  // C `main()`, so we need to pass dummy values for `argc` and `argv`.
+  // Jump into the OTTF C entry point. This is your standard C `main()`, so we
+  // need to pass dummy values for `argc` and `argv`.
   li   a0, 0x0  // argc = 0
   li   a1, 0x0  // argv = NULL
-  call main
+  .extern main
+  tail main
 
-  // Halt the core (wfi loop)
-  tail abort
+  // Set size so this function can be disassembled.
+  .size _start, .-_start
diff --git a/sw/device/lib/testing/test_rom/meson.build b/sw/device/lib/testing/test_rom/meson.build
index b5e8c9c..ad625eb 100644
--- a/sw/device/lib/testing/test_rom/meson.build
+++ b/sw/device/lib/testing/test_rom/meson.build
@@ -21,7 +21,7 @@
 
 # Test ROM linker parameters.
 #
-# See `sw/device/lib/testing/test_rom_ext/test_rom_ext.ld` for additional info
+# See `sw/device/lib/testing/test_framework/ottf.ld` for additional info
 # about these parameters.
 test_rom_linker_script = '@0@/@1@'.format(meson.source_root(), files(['test_rom.ld'])[0])
 test_rom_link_args = [
diff --git a/sw/device/lib/testing/test_rom/test_rom.c b/sw/device/lib/testing/test_rom/test_rom.c
index 9fdc2da..e66ec6f 100644
--- a/sw/device/lib/testing/test_rom/test_rom.c
+++ b/sw/device/lib/testing/test_rom/test_rom.c
@@ -24,7 +24,7 @@
  * and describes the location of the flash header.
  *
  * The actual contents are not defined by the ROM, but rather
- * by the flash binary: see `sw/device/lib/testing/test_rom_ext/test_rom_ext.ld`
+ * by the flash binary: see `sw/device/lib/testing/test_framework/ottf.ld`
  * for that.
  */
 extern struct { void (*entry)(void); } _flash_header;
diff --git a/sw/device/lib/testing/test_rom/test_rom.ld b/sw/device/lib/testing/test_rom/test_rom.ld
index a7785b5..b7a67cc 100644
--- a/sw/device/lib/testing/test_rom/test_rom.ld
+++ b/sw/device/lib/testing/test_rom/test_rom.ld
@@ -36,7 +36,7 @@
  * This symbol points at the header of the flash binary, which contains loading
  * and signing information.
  *
- * See `sw/device/lib/testing/test_rom_ext/test_rom_ext.ld`, under the
+ * See `sw/device/lib/testing/test_framework/ottf.ld`, under the
  * .flash_header section, which populates it.
  */
 _flash_header = _vflash_start;
diff --git a/sw/device/lib/testing/test_rom_ext/BUILD b/sw/device/lib/testing/test_rom_ext/BUILD
deleted file mode 100644
index 8fdae3d..0000000
--- a/sw/device/lib/testing/test_rom_ext/BUILD
+++ /dev/null
@@ -1,29 +0,0 @@
-# Copyright lowRISC contributors.
-# Licensed under the Apache License, Version 2.0, see LICENSE for details.
-# SPDX-License-Identifier: Apache-2.0
-
-load("//rules:opentitan.bzl", "OPENTITAN_CPU")
-
-package(default_visibility = ["//visibility:public"])
-
-cc_library(
-    name = "test_rom_ext",
-    srcs = [
-        "test_rom_ext_start.S",
-    ],
-    copts = [
-        "-nostdlib",
-        "-ffreestanding",
-    ],
-    linkopts = [
-        "-T $(location test_rom_ext.ld)",
-    ],
-    target_compatible_with = [OPENTITAN_CPU],
-    deps = [
-        "test_rom_ext.ld",
-        "//hw/top_earlgrey/sw/autogen:linker_script",
-        "//sw/device:info_sections",
-        "//sw/device/lib/crt",
-        "//sw/device/lib/testing/test_framework:ottf_isrs",
-    ],
-)
diff --git a/sw/device/lib/testing/test_rom_ext/empty.c b/sw/device/lib/testing/test_rom_ext/empty.c
deleted file mode 100644
index c6af3f1..0000000
--- a/sw/device/lib/testing/test_rom_ext/empty.c
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright lowRISC contributors.
-// Licensed under the Apache License, Version 2.0, see LICENSE for details.
-// SPDX-License-Identifier: Apache-2.0
-
-/**
- * This file serves no purpose other than to ensure that Meson treats otherwise
- * empty static libraries as cross-compiled C.
- */
diff --git a/sw/device/lib/testing/test_rom_ext/meson.build b/sw/device/lib/testing/test_rom_ext/meson.build
deleted file mode 100644
index 897a420..0000000
--- a/sw/device/lib/testing/test_rom_ext/meson.build
+++ /dev/null
@@ -1,65 +0,0 @@
-# Copyright lowRISC contributors.
-# Licensed under the Apache License, Version 2.0, see LICENSE for details.
-# SPDX-License-Identifier: Apache-2.0
-
-# Test ROM_EXT linker parameters.
-test_rom_ext_linker_script = '@0@/@1@'.format(meson.source_root(), files(['test_rom_ext.ld'])[0])
-test_rom_ext_linker_args = [
-  # The linker script includes an autogenerated definition of the available
-  # memory regions. The include path is given relative to the source root, so we
-  # need to add the source root to the linker lookup path (which is the same for
-  # libraries and linker scripts.
-  '-Wl,-L,@0@'.format(meson.source_root()),
-  '-Wl,-T,@0@'.format(test_rom_ext_linker_script),
-  # Depending on the compiler and its configuration (*), |--build-id| is passed
-  # to the linker, causing the insertion of a ".note.gnu.build-id" section into
-  # the ELF file. We do not use the build ID. Achieve consistent behavior and
-  # potentially a small improvement in linking time by not computing the build
-  # ID in the first place.
-  #
-  # * GCC built with |--enable-linker-build-id|, clang before 3.9 or built with
-  #   |-DENABLE_LINKER_BUILD_ID|.
-  '-Wl,--build-id=none',
-]
-
-# Test ROM_EXT-like startup library. Every OpenTitan (RISC-V) test executable
-# should depend on this target.
-#
-# The crt defines the interrupt vector, the symbols of which are defined in 
-# in `sw/device/lib/testing/test_framework/ottf_isrs.S`.
-test_rom_ext = declare_dependency(
-  link_args: test_rom_ext_linker_args,
-  # The following assembly files need to be included as a source, not a
-  # static library, so that their custom sections can be picked up when
-  # linking.
-  sources: [
-    # This contains the contents of the `.crt` section which does the initial
-    # setup before jumping to `main`.
-    'test_rom_ext_start.S',
-  ],
-  dependencies: [
-    freestanding_headers,
-    sw_lib_crt,
-    sw_lib_mem,
-    sw_lib_irq,
-    top_earlgrey,
-  ],
-  # This argument exists solely so that Meson realizes that
-  # test_rom_ext_linker_script is part of the dependency graph. This seems to be
-  # the only way to convince Meson to behave in this way, for the following
-  # reasons:
-  # - The dependencies arg can only include artifacts from declare_dependency().
-  # - We can't put linker scripts into the sources list, since Meson has no
-  #   clue how to deal with them.
-  # - custom_target() doesn't help, because we can't convince Meson to depend on
-  #   a custom_target() unless it produces source files.
-  # - If we go with static_library, sources needs to be non-empty in order for
-  #   Meson to correctly treat it as a cross-compile target (otherwise, we get
-  #   linker errors). This is because Meson guesses the type of a target based
-  #   off of the file extensions of the source files.
-  link_with: static_library(
-    'test_rom_ext_linker_script_dep_shim',
-    sources: ['empty.c'],
-    link_depends: [test_rom_ext_linker_script],
-  )
-)
diff --git a/sw/device/riscv_compliance_support/meson.build b/sw/device/riscv_compliance_support/meson.build
index 514adf1..9b7fcb7 100644
--- a/sw/device/riscv_compliance_support/meson.build
+++ b/sw/device/riscv_compliance_support/meson.build
@@ -12,12 +12,12 @@
     'ot_riscv_compliance_support_inner_' + device_name,
     sources: ['support.c'],
     dependencies: [
+      device_lib,
+      ottf_start_lib,
       sw_lib_runtime_print,
       sw_lib_runtime_log,
       sw_lib_mem,
-      sw_lib_testing_ottf_isrs,
       sw_lib_testing_test_status,
-      device_lib,
     ],
   )
 
diff --git a/sw/device/sca/lib/meson.build b/sw/device/sca/lib/meson.build
index c4a65f7..3a613b2 100644
--- a/sw/device/sca/lib/meson.build
+++ b/sw/device/sca/lib/meson.build
@@ -20,7 +20,6 @@
       sw_lib_dif_csrng,
       sw_lib_dif_edn,
       sw_lib_irq,
-      sw_lib_testing_ottf_isrs,
       sw_lib_mmio,
       sw_lib_pinmux,
       sw_lib_runtime_log,
diff --git a/sw/device/sca/meson.build b/sw/device/sca/meson.build
index 8fe38d4..7fc17f3 100644
--- a/sw/device/sca/meson.build
+++ b/sw/device/sca/meson.build
@@ -45,7 +45,7 @@
       name_suffix: 'elf',
       dependencies: [
         device_lib,
-        test_rom_ext,
+        ottf_start_lib,
         sw_lib_mmio,
         sw_lib_runtime_hart,
         sw_lib_runtime_log,
diff --git a/sw/device/silicon_creator/mask_rom/meson.build b/sw/device/silicon_creator/mask_rom/meson.build
index 02025b3..d0ef04e 100644
--- a/sw/device/silicon_creator/mask_rom/meson.build
+++ b/sw/device/silicon_creator/mask_rom/meson.build
@@ -4,7 +4,7 @@
 
 # Mask ROM Linker Parameters
 #
-# See `sw/device/lib/testing/test_rom_ext/test_rom_ext.ld` for additional info
+# See `sw/device/lib/testing/test_framework/ottf.ld` for additional info
 # about these parameters.
 
 rom_linkfile = files(['mask_rom.ld'])
diff --git a/sw/device/silicon_creator/rom_ext/meson.build b/sw/device/silicon_creator/rom_ext/meson.build
index 45bf2de..f5d1e3d 100644
--- a/sw/device/silicon_creator/rom_ext/meson.build
+++ b/sw/device/silicon_creator/rom_ext/meson.build
@@ -46,7 +46,7 @@
 
 # Mask ROM Linker Parameters
 #
-# See `sw/device/lib/testing/test_rom_ext/test_rom_ext.ld` for additional info
+# See `sw/device/lib/testing/test_framework/ottf.ld` for additional info
 # about these parameters.
 
 rom_ext_linkfile_slot_a = files(['rom_ext_slot_a.ld'])
diff --git a/sw/device/silicon_creator/testing/meson.build b/sw/device/silicon_creator/testing/meson.build
index e1d6140..13a501a 100644
--- a/sw/device/silicon_creator/testing/meson.build
+++ b/sw/device/silicon_creator/testing/meson.build
@@ -19,10 +19,9 @@
       mask_rom_test_name + '_' + device_name,
       name_suffix: 'elf',
       dependencies: [
-        test_rom_ext,
         device_lib,
         mask_rom_test_info['library'],
-        sw_lib_testing_ottf,
+        ottf_lib,
       ],
     )
 
diff --git a/sw/device/silicon_owner/bare_metal/meson.build b/sw/device/silicon_owner/bare_metal/meson.build
index deca733..f2d3a6a 100644
--- a/sw/device/silicon_owner/bare_metal/meson.build
+++ b/sw/device/silicon_owner/bare_metal/meson.build
@@ -4,7 +4,7 @@
 
 # Mask ROM Linker Parameters
 #
-# See `sw/device/lib/testing/test_rom_ext/test_rom_ext.ld` for additional info
+# See `sw/device/lib/testing/test_framework/ottf.ld` for additional info
 # about these parameters.
 
 bare_metal_linkfile_slot_a = files(['bare_metal_slot_a.ld'])
diff --git a/sw/device/tests/crt_test.c b/sw/device/tests/crt_test.c
index 9653994..011e259 100644
--- a/sw/device/tests/crt_test.c
+++ b/sw/device/tests/crt_test.c
@@ -16,7 +16,7 @@
 
 #include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
 
-// Symbols defined in `sw/device/lib/testing/test_rom_ext/test_rom_ext.ld`,
+// Symbols defined in `sw/device/lib/testing/test_framework/ottf.ld`,
 // which we use to check that the CRT did what it was supposed to.
 extern char _bss_start;
 extern char _bss_end;
diff --git a/sw/device/tests/meson.build b/sw/device/tests/meson.build
index 8e60b6e..529f26e 100644
--- a/sw/device/tests/meson.build
+++ b/sw/device/tests/meson.build
@@ -67,6 +67,7 @@
       sw_lib_runtime_log,
       sw_lib_runtime_hart,
       sw_lib_testing_test_status,
+      top_earlgrey,
     ],
   ),
 )
@@ -823,10 +824,9 @@
       sw_test_name + '_' + device_name,
       name_suffix: 'elf',
       dependencies: [
-        test_rom_ext,
         device_lib,
+        ottf_lib,
         sw_test_info['library'],
-        sw_lib_testing_ottf,
       ],
     )
 
@@ -936,99 +936,99 @@
 endforeach
 
 # (signed) test binaries loaded with mask ROM (from sw/device/silicon_creator/)
-foreach sw_test_name, sw_test_info : sw_rom_ext_tests
-  foreach device_name, device_lib : sw_lib_arch_core_devices
-    sw_test_elf = executable(
-      '_'.join(['rom_ext', sw_test_name, device_name]),
-      name_suffix: 'elf',
-      dependencies: [
-        # Only use ROM_EXT slot A for now.
-        rom_ext_slot_libs['rom_ext_slot_a'],
-        device_lib,
-        sw_test_info['library'],
-        sw_lib_testing_ottf,
-      ],
-    )
+#foreach sw_test_name, sw_test_info : sw_rom_ext_tests
+  #foreach device_name, device_lib : sw_lib_arch_core_devices
+    #sw_test_elf = executable(
+      #'_'.join(['rom_ext', sw_test_name, device_name]),
+      #name_suffix: 'elf',
+      #dependencies: [
+        ## Only use ROM_EXT slot A for now.
+        #rom_ext_slot_libs['rom_ext_slot_a'],
+        #device_lib,
+        #ottf_lib,
+        #sw_test_info['library'],
+      #],
+    #)
 
-    target_name = '_'.join(['rom_ext', sw_test_name, '@0@', device_name])
+    #target_name = '_'.join(['rom_ext', sw_test_name, '@0@', device_name])
 
-    sw_test_dis = custom_target(
-      target_name.format('dis'),
-      input: sw_test_elf,
-      kwargs: elf_to_dis_custom_target_args,
-    )
+    #sw_test_dis = custom_target(
+      #target_name.format('dis'),
+      #input: sw_test_elf,
+      #kwargs: elf_to_dis_custom_target_args,
+    #)
 
-    sw_test_bin = custom_target(
-      target_name.format('bin'),
-      input: sw_test_elf,
-      kwargs: elf_to_bin_custom_target_args,
-    )
+    #sw_test_bin = custom_target(
+      #target_name.format('bin'),
+      #input: sw_test_elf,
+      #kwargs: elf_to_bin_custom_target_args,
+    #)
 
-    targets_to_export = [
-      sw_test_elf,
-      sw_test_dis,
-      sw_test_bin,
-    ]
+    #targets_to_export = [
+      #sw_test_elf,
+      #sw_test_dis,
+      #sw_test_bin,
+    #]
 
-    foreach key_name, key_info : signing_keys
-      signed_target_name = '_'.join(['rom_ext', sw_test_name, key_name, 'signed', '@0@', device_name])
+    #foreach key_name, key_info : signing_keys
+      #signed_target_name = '_'.join(['rom_ext', sw_test_name, key_name, 'signed', '@0@', device_name])
 
-      sw_test_signed_bin = custom_target(
-        signed_target_name.format('bin'),
-        input: sw_test_bin,
-        output: '@BASENAME@.@0@.signed.bin'.format(key_name),
-        command: [
-          rom_ext_signer_export.full_path(),
-          'rom_ext',
-          '@INPUT@',
-          key_info['path'],
-          sw_test_elf.full_path(),
-          '@OUTPUT@',
-        ],
-        depends: rom_ext_signer_export,
-        build_by_default: true,
-      )
+      #sw_test_signed_bin = custom_target(
+        #signed_target_name.format('bin'),
+        #input: sw_test_bin,
+        #output: '@BASENAME@.@0@.signed.bin'.format(key_name),
+        #command: [
+          #rom_ext_signer_export.full_path(),
+          #'rom_ext',
+          #'@INPUT@',
+          #key_info['path'],
+          #sw_test_elf.full_path(),
+          #'@OUTPUT@',
+        #],
+        #depends: rom_ext_signer_export,
+        #build_by_default: true,
+      #)
 
-      sw_test_signed_vmem32 = custom_target(
-        signed_target_name.format('vmem32'),
-        input: sw_test_signed_bin,
-        kwargs: bin_to_vmem32_custom_target_args,
-      )
+      #sw_test_signed_vmem32 = custom_target(
+        #signed_target_name.format('vmem32'),
+        #input: sw_test_signed_bin,
+        #kwargs: bin_to_vmem32_custom_target_args,
+      #)
 
-      sw_test_signed_vmem64 = custom_target(
-        signed_target_name.format('vmem64'),
-        input: sw_test_signed_bin,
-        kwargs: bin_to_vmem64_custom_target_args,
-      )
+      #sw_test_signed_vmem64 = custom_target(
+        #signed_target_name.format('vmem64'),
+        #input: sw_test_signed_bin,
+        #kwargs: bin_to_vmem64_custom_target_args,
+      #)
 
-      sw_test_signed_scr_vmem64 = custom_target(
-        signed_target_name.format('scrambled'),
-        input: sw_test_signed_vmem64,
-        output: flash_image_outputs,
-        command: flash_image_command,
-        depend_files: flash_image_depend_files,
-        build_by_default: true,
-      )
+      #sw_test_signed_scr_vmem64 = custom_target(
+        #signed_target_name.format('scrambled'),
+        #input: sw_test_signed_vmem64,
+        #output: flash_image_outputs,
+        #command: flash_image_command,
+        #depend_files: flash_image_depend_files,
+        #build_by_default: true,
+      #)
 
-      targets_to_export += [
-        sw_test_signed_bin,
-        sw_test_signed_vmem32,
-        sw_test_signed_vmem64,
-        sw_test_signed_scr_vmem64
-      ]
-    endforeach
+      #targets_to_export += [
+        #sw_test_signed_bin,
+        #sw_test_signed_vmem32,
+        #sw_test_signed_vmem64,
+        #sw_test_signed_scr_vmem64
+      #]
+    #endforeach
 
-    custom_target(
-      target_name.format('export'),
-      command: export_target_command,
-      depend_files: [export_target_depend_files,],
-      input: targets_to_export,
-      output: target_name.format('export'),
-      build_always_stale: true,
-      build_by_default: true,
-    )
-  endforeach
-endforeach
+    #custom_target(
+      #target_name.format('export'),
+      #command: export_target_command,
+      #depend_files: [export_target_depend_files,],
+      #input: targets_to_export,
+      #output: target_name.format('export'),
+      #build_always_stale: true,
+      #build_by_default: true,
+    #)
+  #endforeach
+#endforeach
 
 # Specific custom configuration for `crt_test`
 foreach device_name, device_lib : sw_lib_arch_core_devices
@@ -1037,20 +1037,19 @@
     name_suffix: 'elf',
     sources: ['crt_test.c'],
     dependencies: [
-      test_rom_ext,
       device_lib,
+      # Explicitly ONLY pull in the OTTF startup library since we need to run
+      # right after the ottf_start.S is done executing. Additionally, the
+      # startup library contains default OTTF ISRs. While this test does not
+      # override any of the default ISR symbols, they should be linked in since
+      # the `mtvec` is set to point to these in the
+      # `sw/device/lib/testing/test_framework/ottf_start.S` initialization
+      # assembly (contained in the ottf_start_lib target below).
+      ottf_start_lib,
       sw_lib_testing_test_status,
       sw_lib_runtime_print,
       sw_lib_runtime_log,
       sw_lib_dif_uart,
-      # Explicitly DO NOT pull in the OTTF; we need to run right after
-      # the test ROM_EXT is done executing. However, DO pull in the OTTF ISRs.
-      # While this test does not override any of the default ISR symbols, they
-      # should be linked in since the `mtvec` is set to point to these in the
-      # `sw/device/lib/testing/test_rom_ext/test_rom_ext_start.S` initialization
-      # assembly (contained in the test_rom_ext target above).
-      # sw_lib_testing_ottf,
-      sw_lib_testing_ottf_isrs,
     ],
   )
 
diff --git a/sw/vendor/patches/riscv_compliance/0002-Add-OpenTitan-target.patch b/sw/vendor/patches/riscv_compliance/0002-Add-OpenTitan-target.patch
index 7581c99..02beca9 100644
--- a/sw/vendor/patches/riscv_compliance/0002-Add-OpenTitan-target.patch
+++ b/sw/vendor/patches/riscv_compliance/0002-Add-OpenTitan-target.patch
@@ -257,7 +257,7 @@
 index 0000000..b71105c
 --- /dev/null
 +++ b/riscv-target/opentitan/device/rv32imc/Makefile.include
-@@ -0,0 +1,75 @@
+@@ -0,0 +1,74 @@
 +# Copyright lowRISC contributors.
 +# Licensed under the Apache License, Version 2.0, see LICENSE for details.
 +# SPDX-License-Identifier: Apache-2.0
@@ -269,7 +269,7 @@
 +OT_TOOLS     ?= /tools/riscv/bin
 +OT_FPGA_UART ?=
 +OT_TARGET    ?= fpga_nexysvideo
-+LDSCRIPT      = $(OT_ROOT)/sw/device/lib/testing/test_rom_ext/test_rom_ext.ld
++LDSCRIPT      = $(OT_ROOT)/sw/device/lib/testing/test_framework/ottf.ld
 +DEFINES       = $(CARG) -DPRIV_MISA_S=0 -DPRIV_MISA_U=0 -DRVTEST_ENTRY=_rvc_start -DTRAPALIGN=8
 +TARGET_SIM   ?= $(OT_ROOT)/build/lowrisc_systems_top_earlgrey_verilator_0.1/sim-verilator/Vtop_earlgrey_verilator
 +
@@ -324,8 +324,7 @@
 +		-L$(OT_ROOT) -T$(LDSCRIPT) $$< \
 +		$(OPENTITAN)/main.c \
 +		$(OPENTITAN)/run_rvc_test.S \
-+		$(OT_ROOT)/sw/device/lib/testing/test_rom_ext/test_rom_ext_irq_vector.S \
-+		$(OT_ROOT)/sw/device/lib/testing/test_rom_ext/test_rom_ext_start.S \
++		$(OT_ROOT)/sw/device/lib/testing/test_framework/ottf_start.S \
 +		-L$(OT_BIN)/sw/device/riscv_compliance_support \
 +		-l$(COMPLIANCE_LIB) \
 +		-o $$(@); \
diff --git a/sw/vendor/patches/riscv_compliance/0005-verilator-Rename-top_-chip_.patch b/sw/vendor/patches/riscv_compliance/0005-verilator-Rename-top_-chip_.patch
index 3f06cc7..2c897fc 100644
--- a/sw/vendor/patches/riscv_compliance/0005-verilator-Rename-top_-chip_.patch
+++ b/sw/vendor/patches/riscv_compliance/0005-verilator-Rename-top_-chip_.patch
@@ -24,7 +24,7 @@
 +++ b/riscv-target/opentitan/device/rv32imc/Makefile.include
 @@ -11,7 +11,7 @@ OT_FPGA_UART ?=
  OT_TARGET    ?= fpga_nexysvideo
- LDSCRIPT      = $(OT_ROOT)/sw/device/lib/testing/test_rom_ext/test_rom_ext.ld
+ LDSCRIPT      = $(OT_ROOT)/sw/device/lib/testing/test_framework/ottf.ld
  DEFINES       = $(CARG) -DPRIV_MISA_S=0 -DPRIV_MISA_U=0 -DRVTEST_ENTRY=_rvc_start -DTRAPALIGN=8
 -TARGET_SIM   ?= $(OT_ROOT)/build/lowrisc_systems_top_earlgrey_verilator_0.1/sim-verilator/Vtop_earlgrey_verilator
 +TARGET_SIM   ?= $(OT_ROOT)/build/lowrisc_systems_chip_earlgrey_verilator_0.1/sim-verilator/Vchip_earlgrey_verilator
diff --git a/sw/vendor/riscv_compliance/riscv-target/opentitan/device/rv32imc/Makefile.include b/sw/vendor/riscv_compliance/riscv-target/opentitan/device/rv32imc/Makefile.include
index b3ddf1e..829cfd1 100644
--- a/sw/vendor/riscv_compliance/riscv-target/opentitan/device/rv32imc/Makefile.include
+++ b/sw/vendor/riscv_compliance/riscv-target/opentitan/device/rv32imc/Makefile.include
@@ -9,7 +9,7 @@
 OT_TOOLS     ?= /tools/riscv/bin
 OT_FPGA_UART ?=
 OT_TARGET    ?= fpga_nexysvideo
-LDSCRIPT      = $(OT_ROOT)/sw/device/lib/testing/test_rom_ext/test_rom_ext.ld
+LDSCRIPT      = $(OT_ROOT)/sw/device/lib/testing/test_framework/ottf.ld
 DEFINES       = $(CARG) -DPRIV_MISA_S=0 -DPRIV_MISA_U=0 -DRVTEST_ENTRY=_rvc_start -DTRAPALIGN=8
 TARGET_SIM   ?= $(OT_ROOT)/build/lowrisc_systems_chip_earlgrey_verilator_0.1/sim-verilator/Vchip_earlgrey_verilator
 
@@ -65,8 +65,7 @@
 		-L$(OT_ROOT) -T$(LDSCRIPT) $$< \
 		$(OPENTITAN)/main.c \
 		$(OPENTITAN)/run_rvc_test.S \
-		$(OT_ROOT)/sw/device/lib/testing/test_rom_ext/test_rom_ext_irq_vector.S \
-		$(OT_ROOT)/sw/device/lib/testing/test_rom_ext/test_rom_ext_start.S \
+		$(OT_ROOT)/sw/device/lib/testing/test_framework/ottf_start.S \
 		-L$(OT_BIN)/sw/device/riscv_compliance_support \
 		-l$(COMPLIANCE_LIB) \
 		-o $$(@); \