sencha: consolidate logic and add support for running OTTF targets

Consolidate simulation logic into __sencha* and __cheriot* targets to
reduce lotsa duplicate code. Splits code into three files: sim.mk (main
targets and common code), sim_ottf.mk (OTTF support), sim_testbench.mk
(cheriot-rtos test suite).

Adds sencha-ottf & sencha-ottf+cli targets for running an OpenTitan Test
Framework build (OTTF) on sencha.  To override the default OTTF targets
set the OTTF_SEC and/or OTTF_SMC macros on the cmd line; e.g.

m sencha-ottf+cli OTTF_SMC=smc_tlul_mailbox_test_cheri

Note objdump output for the firmware image is left in
${CHERIOT_OUT_DIR}/release/tmp/${OTFF_SMC}.dump.

Change-Id: Ic6e89124d4b34a21600f2c30c5043ba61a1d9627
diff --git a/platforms/sencha/cheriot-buffer_overflow.mk b/platforms/sencha/cheriot-buffer_overflow.mk
index 8fd9594..9ff1167 100644
--- a/platforms/sencha/cheriot-buffer_overflow.mk
+++ b/platforms/sencha/cheriot-buffer_overflow.mk
@@ -1,35 +1,82 @@
-BUFFER_OVERFLOW_DIR  := $(CHERIOT_OUT_DIR)
-BUFFER_OVERFLOW  := $(BUFFER_OVERFLOW_DIR)/buffer_overflow-firmware
+# Copyright 2024 Googl4 LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+BUFFER_OVERFLOW_DIR  := $(OUT)/cheriot/sencha/buffer_overflow
+BUFFER_OVERFLOW  := $(BUFFER_OVERFLOW_DIR)/release/cheriot/cheriot/release/buffer_overflow-firmware
 BUFFER_OVERFLOW_SRC_DIR := ${ROOTDIR}/hw/matcha/sw/device/cheriot/buffer_overflow
 
-cheriot-buffer_overflow: renode cheriot_sim
+cheriot-buffer_overflow: __sail-buffer_overflow
 	${MAKE} \
-		CHERIOT_BOARD=sail \
-		CHERIOT_OUT_DIR="${BUFFER_OVERFLOW_DIR}" \
-		CHERIOT_FIRMWARE_SRC_DIR=$(BUFFER_OVERFLOW_SRC_DIR) \
-		CHERIOT_FIRMWARE_RELEASE=$(BUFFER_OVERFLOW) \
-		$(BUFFER_OVERFLOW)
-	$(RENODE_CMD) -e "\
-	\$$cheriot_elf =@${BUFFER_OVERFLOW_DIR}/release/cheriot/cheriot/release/buffer_overflow-firmware; \
-	i @${CHERIOT_RESC_RELEASE}; \
-		pause; sysbus.cpu1 IsHalted false; start"
+		__CHERIOT_ELF="@${BUFFER_OVERFLOW}" \
+		__cheriot-simulate
 
-cheriot-buffer_overflow+cli: renode cheriot_sim
+cheriot-buffer_overflow+cli: __sail-buffer_overflow
 	${MAKE} \
-		CHERIOT_BOARD=sail \
-		CHERIOT_OUT_DIR="${BUFFER_OVERFLOW_DIR}" \
-		CHERIOT_FIRMWARE_SRC_DIR=$(BUFFER_OVERFLOW_SRC_DIR) \
-		CHERIOT_FIRMWARE_RELEASE=$(BUFFER_OVERFLOW) \
-		$(BUFFER_OVERFLOW)
-	$(RENODE_CMD) -e "\
-	\$$cheriot_elf =@${BUFFER_OVERFLOW_DIR}/release/cheriot/cheriot/release/buffer_overflow-firmware; \
-	\$$cli_port = 4567; \
-	\$$wait_for_cli = true; \
-	i @${CHERIOT_RESC_RELEASE}; \
-		pause; sysbus.cpu1 IsHalted false; start"
+		__CHERIOT_ELF="@${BUFFER_OVERFLOW}" \
+		__cheriot-simulate+cli
 
-## Cleans all build artifacts for cheriot-rtos examples
-cheriot-buffer_overflow-clean:
-	rm -rf ${BUFFER_OVERFLOW_DIR}
+# Generates the buffer overflow firmware for sail
+__sail-buffer_overflow:
+	${MAKE} CHERIOT_BOARD=sail __buffer_overflow
 
 .PHONY:: cheriot-buffer_overflow cheriot-buffer_overflow+cli
+.PHONY:: __sail-buffer_overflow
+
+BUFFER_OVERFLOW_FLASH_RELEASE=$(CHERIOT_OUT_RELEASE)/buffer_overflow_flash.tar
+
+# XXX symlink cheriot fw to "kernel" to satisfy elfloader
+$(BUFFER_OVERFLOW_FLASH_RELEASE): $(MATCHA_BUNDLE_RELEASE) | $(TMP_RELEASE)
+	cp -f $(MATCHA_BUNDLE_RELEASE) $(TMP_RELEASE)/matcha-tock-bundle
+	${C_PREFIX}strip $(TMP_RELEASE)/matcha-tock-bundle
+	${C_PREFIX}objcopy -O binary -g $(TMP_RELEASE)/matcha-tock-bundle $(TMP_RELEASE)/matcha-tock-bundle.bin
+	ln -sf $(BUFFER_OVERFLOW) $(TMP_RELEASE)/kernel
+	tar -C $(TMP_RELEASE) -cvhf $@ matcha-tock-bundle.bin kernel
+buffer_overflow_flash_release: __sencha-buffer_overflow $(BUFFER_OVERFLOW_FLASH_RELEASE)
+
+## Runs the buffer overflow demo in renode on sencha
+sencha-buffer_overflow: buffer_overflow_flash_release
+	${MAKE} \
+		__SENCHA_TAR="@${BUFFER_OVERFLOW_FLASH_RELEASE}" \
+		__SENCHA_CHERIOT_ELF="@${TMP_RELEASE}/kernel" \
+		__sencha-simulate
+
+## Version of the `sencha-buffer_overflow` target that also enables the simulator command
+## line interface on port 4567. To access the simulator use something like
+## telnet localhost 4567. Note renode will block until the cli is connected.
+sencha-buffer_overflow+cli: buffer_overflow_flash_release
+	${MAKE} \
+		__SENCHA_TAR="@${BUFFER_OVERFLOW_FLASH_RELEASE}" \
+		__SENCHA_CHERIOT_ELF="@${TMP_RELEASE}/kernel" \
+		__sencha-simulate+cli
+
+# Generates the buffer overflow firmware for sencha
+__sencha-buffer_overflow:
+	${MAKE} CHERIOT_BOARD=sencha __buffer_overflow
+
+# Helper that requires CHERIOT_BOARD be supplied on the cmd line
+__buffer_overflow:
+	${MAKE} \
+		CHERIOT_OUT_DIR=${BUFFER_OVERFLOW_DIR} \
+		CHERIOT_FIRMWARE_SRC_DIR=${BUFFER_OVERFLOW_SRC_DIR} \
+		CHERIOT_FIRMWARE_RELEASE=$(BUFFER_OVERFLOW) \
+		$(BUFFER_OVERFLOW)
+
+## Cleans all build artifacts for the buffer_overflow demo
+buffer_overflow-clean:
+	rm -rf ${BUFFER_OVERFLOW_DIR}
+
+.PHONY:: sencha-buffer_overflow sencha-buffer_overflow+cli
+.PHONY:: buffer_overflow_flash_release
+.PHONY:: buffer_overflow-clean
+.PHONY:: __sencha-buffer_overflow __buffer_overflow
diff --git a/platforms/sencha/cheriot-tests.mk b/platforms/sencha/cheriot-tests.mk
index f1139f3..53a4817 100644
--- a/platforms/sencha/cheriot-tests.mk
+++ b/platforms/sencha/cheriot-tests.mk
@@ -19,21 +19,17 @@
 
 ## Runs the cheriot-rtos testbench in renode
 cheriot-testbench: renode cheriot_sim cheriot-test
-	$(RENODE_CMD) -e "\
-	\$$cheriot_elf =@${CHERIOT_TEST}; \
-	i @${CHERIOT_RESC_RELEASE}; \
-		pause; sysbus.cpu1 IsHalted false; start"
+	${MAKE} \
+		__CHERIOT_ELF="@${CHERIOT_TEST}" \
+		__cheriot-simulate
 
 ## Version of the `cheriot-testbench` target that also enables the simulator command
 ## line interface on port 4567. To access the simulator use something like
 ## telnet localhost 4567. Note renode will block until the cli is connected.
 cheriot-testbench+cli: renode cheriot_sim cheriot-test
-	$(RENODE_CMD) -e "\
-	\$$cheriot_elf =@${CHERIOT_TEST}; \
-	\$$cli_port = 4567; \
-	\$$wait_for_cli = true; \
-	i @${CHERIOT_RESC_RELEASE}; \
-		pause; sysbus.cpu1 IsHalted false; start"
+	${MAKE} \
+		__CHERIOT_ELF="@${CHERIOT_TEST}" \
+		__cheriot-simulate+cli
 
 ## Generates the cheriot-rtos test-suite firmware
 cheriot-test:
diff --git a/platforms/sencha/cheriot.mk b/platforms/sencha/cheriot.mk
index 355e6ea..81b8964 100644
--- a/platforms/sencha/cheriot.mk
+++ b/platforms/sencha/cheriot.mk
@@ -36,6 +36,7 @@
 # XXX debug builds don't work atm
 #CHERIOT_FIRMWARE_DEBUG   := $(CHERIOT_OUT_DEBUG)/cheriot/cheriot/debug/${CHERIOT_FIRMWARE}-firmware
 CHERIOT_FIRMWARE_RELEASE := $(CHERIOT_OUT_RELEASE)/cheriot/cheriot/release/${CHERIOT_FIRMWARE}-firmware
+CHERIOT_RESC_RELEASE := sim/config/cheriot.resc
 
 # TODO: add CHERIOT_FIRMWARE_SRC_DIR (not currently defined in time to use here)
 CHERIOT_SOURCES := $(shell find $(CHERIOT_SRC_DIR) \(\
@@ -141,6 +142,21 @@
 ## Generates both debug & release CHERIoT build artifacts
 cheriot: cheriot-bundle-debug cheriot-bundle-release
 
+# Internal helpers for running standalone sims for the sail platform
+
+CHERIOT_SCRIPT+= i @${CHERIOT_RESC_RELEASE};
+CHERIOT_SCRIPT+= pause; sysbus.cpu1 IsHalted false;
+
+CHERIOT_CLI_SCRIPT+= $$cli_port = 4567;
+CHERIOT_CLI_SCRIPT+= $$wait_for_cli = true;
+CHERIOT_CLI_SCRIPT+= i @${CHERIOT_RESC_RELEASE};
+CHERIOT_CLI_SCRIPT+= pause; sysbus.cpu1 IsHalted false;
+
+__cheriot-simulate: renode cheriot_sim
+	$(RENODE_CMD) -e '$$cheriot_elf=${__CHERIOT_ELF}; ${CHERIOT_SCRIPT} start'
+__cheriot-simulate+cli: renode cheriot_sim
+	$(RENODE_CMD) -e '$$cheriot_elf=${__CHERIOT_ELF}; ${CHERIOT_CLI_SCRIPT} start'
+
 include $(ROOTDIR)/build/platforms/sencha/cheriot-examples.mk
 include $(ROOTDIR)/build/platforms/sencha/cheriot-buffer_overflow.mk
 include $(ROOTDIR)/build/platforms/sencha/cheriot-tests.mk
@@ -150,4 +166,3 @@
 .PHONY:: cheriot-builtins-debug cheriot-builtins-release
 .PHONY:: cheriot-gen-headers cheriot-clean-headers
 .PHONY:: cheriot-build-debug-prepare cheriot-build-release-prepare
-.PHONY:: $(CHERIOT_OUT_DEBUG) $(CHERIOT_OUT_RELEASE)
diff --git a/platforms/sencha/sim.mk b/platforms/sencha/sim.mk
index 8c4d398..5892bdd 100644
--- a/platforms/sencha/sim.mk
+++ b/platforms/sencha/sim.mk
@@ -12,6 +12,9 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+# NB: The SMC/CHERIoT has no (working) gdb support so debug builds
+#   are not useful.
+
 EXT_FLASH_DEBUG=$(CHERIOT_OUT_DEBUG)/ext_flash.tar
 EXT_FLASH_RELEASE=$(CHERIOT_OUT_RELEASE)/ext_flash.tar
 
@@ -27,6 +30,56 @@
 clean_sim_configs:
 	@rm -rf $(OUT)/renode_configs
 
+.PHONY:: sim_configs clean_sim_configs
+
+# Renode commands to issue before the initial start of a simulation.
+# This pauses all cores and then sets cpu0 (SC).
+RENODE_PRESTART_CMDS := pause; cpu0 IsHalted false;
+PORT_PRESTART_CMDS:=$(shell $(ROOTDIR)/scripts/generate-renode-port-cmd.sh $(RENODE_PORT))
+
+SENCHA_REPL=sim/config/platforms/sencha.repl
+SENCHA_RESC_RELEASE=sim/config/sencha.resc
+
+# Common logic for running a CHERIoT firmware ELF image on sencha.
+# SEC boots first using multihart_boot_rom. It loads matcha-tock
+# which loads the SMC firmware image (cheriot_elf) from the tarball
+# located in flash and then resets the SMC with the pc set to the
+# entry_point in cheriot_elf.
+#
+# The following targets do slightly different versions of the above;
+# some of which are useless due to the missing gdb support.
+
+# Sencha-specific renode scripts. We construct them in make as much
+# as possible to avoid shell quoting/escape issues. The only parts
+# done in the shell use __SENCHA_* variables that are dynamically
+# calculated.
+SENCHA_SCRIPT:= $$repl_file = @${SENCHA_REPL};
+SENCHA_SCRIPT+= $$sc_bin =@$(TMP_RELEASE)/matcha-tock-bundle.bin;
+SENCHA_SCRIPT+= i @${SENCHA_RESC_RELEASE};
+# Hack: copy before the prestart commands are appended
+SENCHA_DEBUG_SCRIPT := ${SENCHA_SCRIPT}
+SENCHA_SCRIPT+= $(RENODE_PRESTART_CMDS)
+
+SENCHA_CLI_SCRIPT:= $$repl_file = @${SENCHA_REPL};
+SENCHA_CLI_SCRIPT+= $$sc_bin =@$(TMP_RELEASE)/matcha-tock-bundle.bin;
+SENCHA_CLI_SCRIPT+= $$cli_port = 4567;
+SENCHA_CLI_SCRIPT+= $$wait_for_cli = true;
+SENCHA_CLI_SCRIPT+= i @${SENCHA_RESC_RELEASE};
+SENCHA_CLI_SCRIPT+= $(RENODE_PRESTART_CMDS)
+
+__sencha-simulate: renode cheriot_sim kelvin_sim multihart_boot_rom
+	$(RENODE_CMD) -e '$$tar=${__SENCHA_TAR}; $$cheriot_elf=${__SENCHA_CHERIOT_ELF}; ${SENCHA_SCRIPT} start'
+__sencha-simulate+cli: renode cheriot_sim kelvin_sim multihart_boot_rom
+	$(RENODE_CMD) -e '$$tar=${__SENCHA_TAR}; $$cheriot_elf=${__SENCHA_CHERIOT_ELF}; ${SENCHA_CLI_SCRIPT} start'
+# NB: just run the release version for now
+__sencha-simulate-debug: __sencha-simulate
+# NB: can attach with gdb but runs the release build artifacts
+__sencha-debug-simulation: renode cheriot_sim kelvin_sim multihart_boot_rom
+	$(RENODE_CMD) -e '$$tar=${__SENCHA_TAR}; $$cheriot_elf=${__SENCHA_CHERIOT_ELF}; ${SENCHA_DEBUG_SCRIPT} start'
+
+.PHONY:: __sencha-simulate __sencha-simulate+cli
+.PHONY:: __sencha-simulate-debug __sencha-debug-simulation
+
 $(TMP_DEBUG):
 	mkdir -p $(TMP_DEBUG)
 $(TMP_RELEASE):
@@ -50,138 +103,61 @@
 	tar -C $(TMP_RELEASE) -cvhf $@ matcha-tock-bundle.bin kernel
 ext_flash_release: $(EXT_FLASH_RELEASE)
 
-# Renode commands to issue before the initial start of a simulation.
-# This pauses all cores and then sets cpu0 (SC).
-RENODE_PRESTART_CMDS=pause; cpu0 IsHalted false;
-PORT_PRESTART_CMDS:=$(shell $(ROOTDIR)/scripts/generate-renode-port-cmd.sh $(RENODE_PORT))
-
-# XXX the same for now
-SENCHA_RESC_DEBUG=sim/config/sencha.resc
-SENCHA_RESC_RELEASE=sim/config/sencha.resc
-CHERIOT_RESC_RELEASE=sim/config/cheriot.resc
-
-SENCHA_REPL=sim/config/platforms/sencha.repl
-
 ## Launches an end-to-end build of the system and starts Renode
 #
-# This top-level target triggers the `matcha_tock_release`, `cheriot`, `renode`,
-# `multihart_boot_rom`, and `iree` targets to build the entire system and then
+# This top-level target triggers the `ext_flash_release`, `cheriot_sim`, `renode`,
+# `multihart_boot_rom`, and `kelvin_sim` targets to build the entire system and then
 # finally starts the Renode simulator.
 #
 # This is the default target for the build system, and is generally what you
 # need for day-to-day work on the software side of Shodan.
-simulate: renode cheriot_sim kelvin_sim multihart_boot_rom ext_flash_release
-	$(RENODE_CMD) -e "\
-    \$$repl_file = @${SENCHA_REPL}; \
-    \$$tar = @$(EXT_FLASH_RELEASE); \
-    \$$sc_bin =@$(TMP_RELEASE)/matcha-tock-bundle.bin; \
-    \$$cheriot_elf = @$(TMP_RELEASE)/kernel; \
-    $(PORT_PRESTART_CMDS) i @${SENCHA_RESC_RELEASE}; \
-        $(RENODE_PRESTART_CMDS) start"
+simulate: ext_flash_release
+	${MAKE} \
+		__SENCHA_TAR="@${EXT_FLASH_RELEASE}" \
+		__SENCHA_CHERIOT_ELF="@${TMP_RELEASE}/kernel" \
+		__sencha-simulate
 
-## Version of the `simulate` target that also enables the simulator command
-## line interface on port 4567. To access the simulator use something like
-## telnet localhost 4567. Note renode will block until the cli is connected.
-simulate+cli: renode cheriot_sim kelvin_sim multihart_boot_rom ext_flash_release
-	$(RENODE_CMD) -e "\
-    \$$repl_file = @${SENCHA_REPL}; \
-    \$$tar = @$(EXT_FLASH_RELEASE); \
-    \$$sc_bin =@$(TMP_RELEASE)/matcha-tock-bundle.bin; \
-    \$$cheriot_elf = @$(TMP_RELEASE)/kernel; \
-    \$$cli_port = 4567; \
-    \$$wait_for_cli = true; \
-    $(PORT_PRESTART_CMDS) i @${SENCHA_RESC_RELEASE}; \
-        $(RENODE_PRESTART_CMDS) start"
+## Version of the `simulate` target that also enables the mpact-cheriot
+## simulator command line interface on port 4567. To access the simulator
+## use something like `telnet localhost 4567`. Note the simulation will
+## block until the cli is connected.
+simulate+cli: ext_flash_release
+	${MAKE} \
+		__SENCHA_TAR="@${EXT_FLASH_RELEASE}" \
+		__SENCHA_CHERIOT_ELF="@${TMP_RELEASE}/kernel" \
+		__sencha-simulate+cli
 
 ## Debug version of the `simulate` target
 #
 # This top-level target does the same job as `simulate`, but instead of
 # unhalting the CPUs and starting the system, this alternate target only unhalts
-# cpu0, and uses the debug build of TockOS from the `matcha_tock_debug` target.
-simulate-debug: renode cheriot_sim kelvin_sim multihart_boot_rom ext_flash_debug
-	$(RENODE_CMD) -e "\
-    \$$repl_file = @${SENCHA_REPL}; \
-    \$$tar = @$(EXT_FLASH_DEBUG); \
-    \$$sc_bin =@$(TMP_DEBUG)/matcha-tock-bundle.bin; \
-    \$$cheriot_elf = @$(TMP_DEBUG)/kernel; \
-    $(PORT_PRESTART_CMDS) i @${SENCHA_RESC_DEBUG}; \
-        $(RENODE_PRESTART_CMDS); start"
+# cpu0, and uses the debug build of TockOS from the `ext_flash_debug` target.
+simulate-debug: ext_flash_debug
+	${MAKE} \
+		__SENCHA_TAR="@${EXT_FLASH_DEBUG}" \
+		__SENCHA_CHERIOT_ELF="@${TMP_DEBUG}/kernel" \
+		__sencha-simulate-debug
 
 ## Debug version of the `simulate` target
 #
 # This top-level target does the same job as `simulate-debug`, but instead of
 # unhalting the CPUs and starting the system, this alternate target starts
 # renode with no CPUs unhalted, allowing for GDB to be used for early system
-# start.
-debug-simulation: renode cheriot_sim kelvin_sim multihart_boot_rom ext_flash_debug
-	$(RENODE_CMD) -e "\
-    \$$repl_file = @${SENCHA_REPL}; \
-    \$$tar = @$(EXT_FLASH_DEBUG); \
-    \$$sc_bin =@$(TMP_DEBUG)/matcha-tock-bundle.bin; \
-    \$$cheriot_elf = @$(TMP_DEBUG)/kernel; \
-    $(PORT_PRESTART_CMDS) i @${SENCHA_RESC_DEBUG}; start"
-
-
-# Support for building cheriot-rtos functional tests for sencha
-
-SENCHA_TEST_DIR := $(OUT)/cheriot/sencha/test-suite
-SENCHA_TEST := $(SENCHA_TEST_DIR)/release/cheriot/cheriot/release/test-suite
-
-TEST_FLASH_RELEASE=$(CHERIOT_OUT_RELEASE)/test_flash.tar
-
-# XXX symlink cheriot fw to "kernel" to satisfy elfloader
-$(TEST_FLASH_RELEASE): $(MATCHA_BUNDLE_RELEASE) $(SENCHA_TEST) | $(TMP_RELEASE)
-	cp -f $(MATCHA_BUNDLE_RELEASE) $(TMP_RELEASE)/matcha-tock-bundle
-	${C_PREFIX}strip $(TMP_RELEASE)/matcha-tock-bundle
-	${C_PREFIX}objcopy -O binary -g $(TMP_RELEASE)/matcha-tock-bundle $(TMP_RELEASE)/matcha-tock-bundle.bin
-	ln -sf $(SENCHA_TEST) $(TMP_RELEASE)/kernel
-	tar -C $(TMP_RELEASE) -cvhf $@ matcha-tock-bundle.bin kernel
-test_flash_release: sencha-test $(TEST_FLASH_RELEASE)
-
-## Runs the cheriot-rtos testbench in renode on sencha
-sencha-testbench: renode cheriot_sim kelvin_sim multihart_boot_rom test_flash_release
-	$(RENODE_CMD) -e "\
-    \$$repl_file = @${SENCHA_REPL}; \
-    \$$tar = @$(TEST_FLASH_RELEASE); \
-    \$$sc_bin =@$(TMP_RELEASE)/matcha-tock-bundle.bin; \
-    \$$cheriot_elf = @$(TMP_RELEASE)/kernel; \
-    $(PORT_PRESTART_CMDS) i @${SENCHA_RESC_RELEASE}; \
-        $(RENODE_PRESTART_CMDS) start"
-
-## Version of the `sencha-testbench` target that also enables the simulator command
-## line interface on port 4567. To access the simulator use something like
-## telnet localhost 4567. Note renode will block until the cli is connected.
-sencha-testbench+cli: renode cheriot_sim kelvin_sim multihart_boot_rom test_flash_release
-	$(RENODE_CMD) -e "\
-    \$$repl_file = @${SENCHA_REPL}; \
-    \$$tar = @$(TEST_FLASH_RELEASE); \
-    \$$sc_bin =@$(TMP_RELEASE)/matcha-tock-bundle.bin; \
-    \$$cheriot_elf = @$(TMP_RELEASE)/kernel; \
-    \$$cli_port = 4567; \
-    \$$wait_for_cli = true; \
-    $(PORT_PRESTART_CMDS) i @${SENCHA_RESC_RELEASE}; \
-        $(RENODE_PRESTART_CMDS) start"
-
-## Generates the cheriot-rtos test-suite firmware
-sencha-test:
+# debugging.
+debug-simulation: ext_flash_debug
 	${MAKE} \
-		CHERIOT_BOARD=sencha \
-		CHERIOT_OUT_DIR=${SENCHA_TEST_DIR} \
-		CHERIOT_FIRMWARE_SRC_DIR=${CHERIOT_SRC_DIR}/tests \
-		CHERIOT_FIRMWARE_RELEASE=$(SENCHA_TEST) \
-		$(SENCHA_TEST)
+		__SENCHA_TAR="@${EXT_FLASH_DEBUG}" \
+		__SENCHA_CHERIOT_ELF="@${TMP_DEBUG}/kernel" \
+		__sencha-debug-simulation
 
-## Cleans all build artifacts for the cheriot-rtos test suite built for sencha
-sencha-test-clean:
-	rm -rf ${SENCHA_TEST_DIR}
-
-.PHONY:: sencha-testbench sencha-testbench+cli
-.PHONY:: test_flash_release
-.PHONY:: sencha-test sencha-test-clean
+.PHONY:: simulate simulate+cli simulate-debug debug-simulation
 
 $(CHERIOT_SIM_OUT_DIR):
 	mkdir -p "$(CHERIOT_SIM_OUT_DIR)"
 
+# Support for building the standalone mpact-cheriot simulator and the
+# library packaged for use within renode.
+
 ## Build CHERIoT ISS
 #
 # Build mpact-sim-based CHERIoT ISS with bazel, and copy it to out/
@@ -203,5 +179,7 @@
 		bazel clean --expunge
 	rm -rf $(CHERIOT_SIM_OUT_DIR)
 
-.PHONY:: sim_configs clean_sim_configs simulate simulate-debug debug-simulation
 .PHONY:: cheriot_sim cheriot_sim_clean
+
+include $(ROOTDIR)/build/platforms/sencha/sim_ottf.mk
+include $(ROOTDIR)/build/platforms/sencha/sim_testbench.mk
diff --git a/platforms/sencha/sim_ottf.mk b/platforms/sencha/sim_ottf.mk
new file mode 100644
index 0000000..b0363e1
--- /dev/null
+++ b/platforms/sencha/sim_ottf.mk
@@ -0,0 +1,83 @@
+# Copyright 2024 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Support for running OTTF tests on sencha
+
+OTTF_FLASH_RELEASE := $(CHERIOT_OUT_RELEASE)/ottf_flash.tar
+
+# Bazel target for the test case to run; override this on the cmd line; e.g.
+#     m sencha-ottf OTTF_SMC=smc_tlul_mailbox_test_cheri
+
+OTTF_SEC := tlul_mailbox_test
+OTTF_SMC := smc_tlul_mailbox_test_cheri
+
+# NB: the SEC fw image is named "matcha-tock-bundle.bin" to satisfy elfloader
+__sencha_ottf_sec: | ${TMP_RELEASE}
+	cd $(SENCHA_SRC_DIR) && \
+		bazel build //sw/device/tests:${OTTF_SEC}
+	cd $(SENCHA_SRC_DIR) && \
+		find "bazel-matcha/" -wholename "*tests/${OTTF_SEC}_fpga_nexus.bin" \
+			-exec cp -f '{}' "${TMP_RELEASE}"/ottf_sec.bin \;
+
+
+# Helper to build the ELF image for the SMC and copy it to the tmp dir
+# for constructing the tarball used during boot.
+# NB: the SMC fw image is named "kernel" to satisfy elfloader
+__sencha_ottf_smc: | ${TMP_RELEASE}
+	cd $(SENCHA_SRC_DIR) && \
+		bazel build -s //sw/device/tests/smc:${OTTF_SMC}
+	cd $(SENCHA_SRC_DIR) && \
+		find "bazel-bin/" -wholename "*smc/${OTTF_SMC}.elf" \
+			-exec cp -f '{}' "${TMP_RELEASE}"/ottf_smc.elf \;
+	cd $(SENCHA_SRC_DIR) && \
+		find "bazel-matcha/" -wholename "*smc/${OTTF_SMC}.elf" \
+		  -exec $(CACHE)/cheriot-tools/bin/llvm-objdump -glxsdrS --demangle '{}' \
+				> $(TMP_RELEASE)/$(OTTF_SMC).dump \;
+__sencha_ottf_smc-clean:
+	cd $(SENCHA_SRC_DIR) && bazel clean --expunge
+
+$(OTTF_FLASH_RELEASE): __sencha_ottf_sec __sencha_ottf_smc
+	tar -C $(TMP_RELEASE) -cvhf $@ ottf_sec.bin ottf_smc.elf
+ottf_flash_release: $(OTTF_FLASH_RELEASE)
+
+SENCHA_OTTF_RESC := sim/config/sencha-ottf.resc
+
+# OTTF-specific scripts that side-load the SMC firmware. We
+# construct them in make to avoid shell quoting/escape issues.
+# The only part done in the shell uses OTTF_FLASH_RELEASE
+# which is a dynamically calculated make variable.
+OTTF_SCRIPT:= $$repl_file = @${SENCHA_REPL};
+OTTF_SCRIPT+= $$sc_bin =@$(TMP_RELEASE)/ottf_sec.bin;
+OTTF_SCRIPT+= $$cheriot_elf = @${TMP_RELEASE}/ottf_smc.elf;
+OTTF_SCRIPT+= i @${SENCHA_OTTF_RESC};
+OTTF_SCRIPT+= $(RENODE_PRESTART_CMDS)
+
+OTTF_CLI_SCRIPT:= $$repl_file = @${SENCHA_REPL};
+OTTF_CLI_SCRIPT+= $$sc_bin =@$(TMP_RELEASE)/ottf_sec.bin;
+OTTF_CLI_SCRIPT+= $$cheriot_elf = @${TMP_RELEASE}/ottf_smc.elf;
+OTTF_CLI_SCRIPT+= $$cli_port=4567;
+OTTF_CLI_SCRIPT+= $$wait_for_cli=true;
+OTTF_CLI_SCRIPT+= i @${SENCHA_OTTF_RESC};
+OTTF_CLI_SCRIPT+= $(RENODE_PRESTART_CMDS)
+
+# XXX $tar is not needed but the .resc loads it
+sencha-ottf: renode cheriot_sim kelvin_sim multihart_boot_rom ottf_flash_release
+	$(RENODE_CMD) -e '$$tar=@${OTTF_FLASH_RELEASE}; ${OTTF_SCRIPT} start'
+
+sencha-ottf+cli: renode cheriot_sim kelvin_sim multihart_boot_rom ottf_flash_release
+	$(RENODE_CMD) -e '$$tar=@${OTTF_FLASH_RELEASE}; ${OTTF_CLI_SCRIPT} start'
+
+sencha-ottf-clean:
+	cd $(SENCHA_SRC_DIR) && bazel clean --expunge
+	rm -rf ${TMP_RELEASE}
diff --git a/platforms/sencha/sim_testbench.mk b/platforms/sencha/sim_testbench.mk
new file mode 100644
index 0000000..fd8504f
--- /dev/null
+++ b/platforms/sencha/sim_testbench.mk
@@ -0,0 +1,62 @@
+# Copyright 2024 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Support for building cheriot-rtos functional tests to run on sencha
+
+SENCHA_TEST_DIR := $(OUT)/cheriot/sencha/test-suite
+SENCHA_TEST := $(SENCHA_TEST_DIR)/release/cheriot/cheriot/release/test-suite
+
+TEST_FLASH_RELEASE=$(CHERIOT_OUT_RELEASE)/test_flash.tar
+
+# XXX symlink cheriot fw to "kernel" to satisfy elfloader
+$(TEST_FLASH_RELEASE): $(MATCHA_BUNDLE_RELEASE) $(SENCHA_TEST) | $(TMP_RELEASE)
+	cp -f $(MATCHA_BUNDLE_RELEASE) $(TMP_RELEASE)/matcha-tock-bundle
+	${C_PREFIX}strip $(TMP_RELEASE)/matcha-tock-bundle
+	${C_PREFIX}objcopy -O binary -g $(TMP_RELEASE)/matcha-tock-bundle $(TMP_RELEASE)/matcha-tock-bundle.bin
+	ln -sf $(SENCHA_TEST) $(TMP_RELEASE)/kernel
+	tar -C $(TMP_RELEASE) -cvhf $@ matcha-tock-bundle.bin kernel
+test_flash_release: sencha-test $(TEST_FLASH_RELEASE)
+
+## Runs the cheriot-rtos testbench in renode on sencha
+sencha-testbench: test_flash_release
+	${MAKE} \
+		__SENCHA_TAR="@${TEST_FLASH_RELEASE}" \
+		__SENCHA_CHERIOT_ELF="@${TMP_RELEASE}/kernel" \
+		__sencha-simulate
+
+## Version of the `sencha-testbench` target that also enables the simulator command
+## line interface on port 4567. To access the simulator use something like
+## telnet localhost 4567. Note renode will block until the cli is connected.
+sencha-testbench+cli: test_flash_release
+	${MAKE} \
+		__SENCHA_TAR="@${TEST_FLASH_RELEASE}" \
+		__SENCHA_CHERIOT_ELF="@${TMP_RELEASE}/kernel" \
+		__sencha-simulate+cli
+
+## Generates the cheriot-rtos test-suite firmware
+sencha-test:
+	${MAKE} \
+		CHERIOT_BOARD=sencha \
+		CHERIOT_OUT_DIR=${SENCHA_TEST_DIR} \
+		CHERIOT_FIRMWARE_SRC_DIR=${CHERIOT_SRC_DIR}/tests \
+		CHERIOT_FIRMWARE_RELEASE=$(SENCHA_TEST) \
+		$(SENCHA_TEST)
+
+## Cleans all build artifacts for the cheriot-rtos test suite built for sencha
+sencha-test-clean:
+	rm -rf ${SENCHA_TEST_DIR} ${TMP_RELEASE}
+
+.PHONY:: sencha-testbench sencha-testbench+cli
+.PHONY:: test_flash_release
+.PHONY:: sencha-test sencha-test-clean