Initial sencha support

Builds & runs a release firmware image. Enabling built-in audio
data allows demo app to run further but it hangs (likely in the
i2s emulation where it also hangs for nexus).

TODO: gdb support needs work, stock gdb complains:

  warning: while parsing target description: not well-formed (invalid token)
  warning: Could not load XML target description; ignoring
  warning: multi-threaded target stopped without sending a thread-id, using first non-exited thread
  Remote 'g' packet reply is too long (expected 132 bytes, got 264 bytes):...

Bug: 330742177

Change-Id: I3d2565572313725765b91bac55f93cb0a30babb6
diff --git a/platforms/sencha.repl b/platforms/sencha.repl
new file mode 100644
index 0000000..780c709
--- /dev/null
+++ b/platforms/sencha.repl
@@ -0,0 +1,66 @@
+//
+// Copyright (c) 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.
+
+// XXX this can go away after nexus-release.repl is refactored
+
+using "sim/config/platforms/sencha_smc.repl"
+using "sim/config/platforms/secure.repl"
+using "sim/config/platforms/kelvin.repl"
+
+// To model the TLUL mailbox spec, we need a Renode peripheral that listens
+// to address ranges for both endpoints of the mailbox. This is the current
+// best way to do that, per Renode dev's recommendations.
+
+mailbox : Mailbox @ {
+        sysbus new Bus.BusMultiRegistration {
+            address: 0x40800000;  // TOP_MATCHA_MAILBOX_SEC_BASE_ADDR
+            size: 0x28;
+            region: "endpoint_a"
+        };
+        sysbus new Bus.BusMultiRegistration {
+            address: 0x540F1000;  // TOP_MATCHA_MAILBOX_SMC_BASE_ADDR
+            size: 0x28;
+            region: "endpoint_b"
+        }
+    }
+    endpoint_a_name: "SEC"
+    endpoint_b_name: "SMC"
+    wtirq_A -> plic@187    // kTopMatchaPlicIrqIdMailboxSecWtirq
+    rtirq_A -> plic@188    // kTopMatchaPlicIrqIdMailboxSecRtirq
+    eirq_A  -> plic@189    // kTopMatchaPlicIrqIdMailboxSecEirq
+    wtirq_B -> smc_plic@30 // kTopMatchaPlicIrqIdMailboxSmcWtirq
+    rtirq_B -> smc_plic@31 // kTopMatchaPlicIrqIdMailboxSmcRtirq
+    eirq_B  -> smc_plic@32 // kTopMatchaPlicIrqIdMailboxSmcEirq
+
+// ISP               [‘h4200_0000 - ‘h4200_FFFF)  64KB ISP registers
+// DMA Ctrl          [‘h4201_0000 - ‘h4201_FFFF)  64KB DMA control interface
+// DSP Ctrl          [‘h4202_0000 - ‘h4202_FFFF)  64KB Audio DSP control interface
+
+// TOP_MATCHA_I2S0_BASE_ADDR @ top_matcha.h
+// I2S0              ['h5410_0000 - 'h5410_0040)  64B registers
+i2s0 : Sound.MatchaI2S @ sysbus 0x54100000
+    TxWatermarkIRQ -> smc_plic@39       // kTopMatchaPlicIrqIdI2s0TxWatermark @ top_matcha.h
+    RxWatermarkIRQ -> smc_plic@40       // kTopMatchaPlicIrqIdI2s0RxWatermark @ top_matcha.h
+    TxEmptyIRQ -> smc_plic@41           // kTopMatchaPlicIrqIdI2s0TxEmpty @ top_matcha.h
+    RxOverflowIRQ -> smc_plic@42        // kTopMatchaPlicIrqIdI2s0RxOverflow @ top_matcha.h
+
+ml_top_controlblock :
+    HostReqIRQ -> smc_plic@33
+    FinishIRQ -> smc_plic@34
+    InstructionFaultIRQ -> smc_plic@35
+
+// Flash/MRAM        [‘h4400_0000 - ‘h47FF_FFFF)  64MB External Non-Volatile Memory
+extflash_mem:
+    size: 0x04000000
diff --git a/platforms/sencha_smc.repl b/platforms/sencha_smc.repl
new file mode 100644
index 0000000..e6d2519
--- /dev/null
+++ b/platforms/sencha_smc.repl
@@ -0,0 +1,50 @@
+//
+// Copyright (c) 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.
+
+// ***************************************************
+// Sencha SMC is a CHERIoT RISC-V CPU
+// ***************************************************
+
+// XXX set sysbus address elsewhere
+cpu1: MpactCPU.MpactCheriotCPU @ sysbus
+    id: 1
+    cpuType: "Mpact.Cheriot"
+    endianness: Endianess.LittleEndian
+    memoryBase: 0x80000000
+    memorySize: 0x04000000
+    revocationMemoryBase: 0x83000000 // Shadow bitmap
+    clintMMRBase: 0 // NB: disables simulator built-in impl
+
+ram_smc: MpactPeripheral.MpactCheriotPeripheral @ sysbus 0x80000000
+    size: 0x04000000
+    id: 1  // NB: must match MpactCheriotCPU.id
+
+// Control block for the SMC, lets us pause/restart the core at an arbitrary PC.
+smc_control: MpactCPU.SmcCheriot_ControlBlock @ sysbus 0x54020000
+    cpu: cpu1
+    pc: 0x80000000
+
+smc_clint: IRQControllers.CoreLevelInterruptor @ sysbus 0x02000000
+    frequency: 66000000
+    [0, 1] ->cpu1@[3, 7]
+
+smc_plic: IRQControllers.PlatformLevelInterruptController @ sysbus 0x60000000 // TOP_MATCHA_RV_PLIC_SMC_BASE_ADDR @ top_matcha.h
+    0 -> cpu1@11
+    numberOfSources: 42
+    numberOfContexts: 1
+    prioritiesEnabled: false
+
+// XXX use TrivialUart until cheriot-rtos has an OpenTitan_UART driver
+uart5: Antmicro.Renode.Peripherals.UART.TrivialUart @ sysbus 0x54000000
diff --git a/sencha.resc b/sencha.resc
new file mode 100644
index 0000000..a9ced9d
--- /dev/null
+++ b/sencha.resc
@@ -0,0 +1,104 @@
+# 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
+#
+#      http:#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.
+
+# Renode script for testing the 3-core version of the Sancha platform
+
+$name?="sencha"
+
+using sysbus
+mach create $name
+
+include @sim/config/shodan_infrastructure/MpactCheriotCPU.cs
+EnsureTypeIsLoaded "Antmicro.Renode.Peripherals.MpactCPU.MpactCheriotCPU"
+include @sim/config/shodan_infrastructure/MpactCheriotPeripheral.cs
+EnsureTypeIsLoaded "Antmicro.Renode.Peripherals.MpactPeripheral.MpactCheriotPeripheral"
+EnsureTypeIsLoaded "Antmicro.Renode.Peripherals.CPU.RiscV32"
+include @sim/config/shodan_infrastructure/KelvinCPU.cs
+EnsureTypeIsLoaded "Antmicro.Renode.Peripherals.CPU.KelvinCPU"
+EnsureTypeIsLoaded "Antmicro.Renode.Peripherals.CPU.MlTopControlBlock"
+include @sim/config/shodan_infrastructure/SmcCheriotCPU.cs
+include @sim/config/shodan_infrastructure/Mailbox.cs
+include @sim/config/shodan_infrastructure/AddressRangeStub.cs
+include @sim/config/shodan_infrastructure/MatchaI2S.cs
+EnsureTypeIsLoaded "Antmicro.Renode.Peripherals.Sound.MatchaI2S"
+
+$repl_file ?= @sim/config/platforms/sencha.repl
+
+machine LoadPlatformDescription $repl_file
+
+$term_port?=3456
+emulation CreateServerSocketTerminal $term_port "term" false
+connector Connect uart5 term
+
+showAnalyzer "uart0-analyzer" sysbus.uart0 Antmicro.Renode.Analyzers.LoggingUartAnalyzer
+showAnalyzer "uart5-analyzer" sysbus.uart5 Antmicro.Renode.Analyzers.LoggingUartAnalyzer
+
+# Set the uarts host/virt timestamp format. Options: None, Virtual, Host, Full.
+uart0-analyzer TimestampFormat None
+uart5-analyzer TimestampFormat None
+
+# Load the boot rom into the 32k rom at 0x8000 (useVirtualAddress = false, allowLoadsOnlyToMemory = true)
+sysbus LoadELF @out/matcha/hw/boot_rom.elf false true cpu0
+
+# Start cpu0 at the bootrom reset vector, which is stored immediately after the
+# bootrom interrupt vector table at 0x8080.
+# (see https://ibex-core.readthedocs.io/en/latest/03_reference/exception_interrupts.html for details)
+sysbus.cpu0 PC 0x8080
+
+cpu1 IsHalted true
+$cheriotLibrary ?= @out/cheriot/sim/librenode_mpact_cheriot.so
+sysbus.cpu1 CpuLibraryPath $cheriotLibrary
+
+# Setup ram_smc to coordinate with cpu1
+sysbus.ram_smc CpuLibraryPath $cheriotLibrary
+sysbus.ram_smc Start
+
+$tar ?= @out/cantrip/shodan/release/ext_flash.tar
+$spi_flash_load_address ?= 0x44000000
+$sc_bin ?= @out/cantrip/shodan/release/tmp/matcha-tock-bundle.bin
+$eflash_address ?= 0x20000000
+sysbus LoadBinary $tar $spi_flash_load_address
+sysbus LoadBinary $sc_bin $eflash_address
+#sysbus LoadSymbolsFrom $kernel
+
+# If we have a I2S peripheral, setup an audio file that we can use
+# for sampling data. Note that the format for these files is raw
+# sample data, left channel followed by right.
+$i2s_mic_audio_file     ?= @sim/config/shodan_infrastructure/test.raw
+$i2s_speaker_audio_file ?= @/tmp/speaker.raw
+
+# Uncomment these lines to enable audio
+i2s0 InputFile $i2s_mic_audio_file
+#i2s0 OutputFile $i2s_speaker_audio_file
+
+$kelvinLibrary ?= @out/kelvin/sim/librenode_kelvin.so
+sysbus.cpu2 CpuLibraryPath $kelvinLibrary
+# Start the vector core at address 0 of its instruction TCM.
+sysbus.cpu2 PC 0x80000000
+# Set the vector core to be less interactive to IO for faster execution
+$vector_core_mips ?= 1000
+sysbus.cpu2 PerformanceInMips $vector_core_mips
+
+# Start GDB and halt both cores so we can connect GDB before the bootrom has
+# started.
+# cpu2 does not support GDB. Please follow docs/KelvinIssDebugging.md to debug
+# Kelvin programs.
+$gdb_port?=3333
+machine StartGdbServer $gdb_port false cpu0
+machine StartGdbServer $gdb_port false cpu1
+
+cpu0 IsHalted true
+cpu1 IsHalted true
+cpu2 IsHalted true
+
diff --git a/shodan_infrastructure/MpactCheriotCPU.cs b/shodan_infrastructure/MpactCheriotCPU.cs
new file mode 100644
index 0000000..20161b4
--- /dev/null
+++ b/shodan_infrastructure/MpactCheriotCPU.cs
@@ -0,0 +1,553 @@
+//
+// Copyright (c) 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.
+
+using System;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Threading;
+using System.Collections.Generic;
+using System.Collections.Concurrent;
+using Antmicro.Renode.Core;
+using Antmicro.Renode.Debugging;
+using Antmicro.Renode.Exceptions;
+using Antmicro.Renode.Hooks;
+using Antmicro.Renode.Logging;
+using Antmicro.Renode.Peripherals;
+using Antmicro.Renode.Peripherals.Bus;
+using Antmicro.Renode.Peripherals.CPU;
+using Antmicro.Renode.Peripherals.Timers;
+using Antmicro.Renode.Peripherals.CPU.Disassembler;
+using Antmicro.Renode.Peripherals.CPU.Registers;
+using Antmicro.Renode.Utilities;
+using Antmicro.Renode.Time;
+using Antmicro.Renode.Utilities.Binding;
+using ELFSharp.ELF;
+using ELFSharp.UImage;
+
+namespace Antmicro.Renode.Peripherals.MpactCPU
+{
+	// Declare some additional function signatures.
+	[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+	public delegate Int32 FuncInt32Int32ActionInt32(Int32 param0, ActionInt32 param1);
+	[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+	public delegate Int32 FuncInt32Int32Int32FuncInt32UInt64IntPtrInt32FuncInt32UInt64IntPtrInt32(Int32 param0, Int32 param1, FuncInt32UInt64IntPtrInt32 param2,
+			FuncInt32UInt64IntPtrInt32 param3);
+	[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+	public delegate Int32 FuncInt32Int32FuncInt32UInt64IntPtrInt32FuncInt32UInt64IntPtrInt32(Int32 param0, FuncInt32UInt64IntPtrInt32 param1,
+			FuncInt32UInt64IntPtrInt32 param2);
+	[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+	public delegate Int32 FuncInt32Int32StringArrStringArrInt32(Int32 param0, string[] param1, string[] param2, Int32 param3);
+	[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+	public delegate Int32 FuncInt32Int32Int32Bool(Int32 param0, Int32 param1, bool param2);
+
+	public class MpactCheriotCPU : BaseCPU, ICpuSupportingGdb, ICPUWithRegisters, ICPUWithHooks, IGPIOReceiver, ITimeSink, IDisposable {
+		[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi, Pack = 1)]
+		private struct RegInfo {
+			[FieldOffset(0)]
+			public Int32 index;
+			[FieldOffset(4)]
+			public Int32 width;
+			[FieldOffset(8)]
+			public bool isGeneral;
+			[FieldOffset(9)]
+			public bool isReadOnly;
+		}
+
+		public MpactCheriotCPU(uint id, UInt64 memoryBase, UInt64 memorySize,
+				       UInt64 revocationMemoryBase, UInt64 clintMMRBase,
+				       string cpuType, IMachine machine, Endianess endianness,
+               CpuBitness bitness = CpuBitness.Bits32)
+		: base(id, cpuType, machine, endianness, bitness)
+		{
+			this.memoryBase = memoryBase;
+			this.memorySize = memorySize;
+			revocationMemBase = revocationMemoryBase;
+			clintBase = clintMMRBase;
+			error_ptr = Marshal.AllocHGlobal(4);
+			value_ptr = Marshal.AllocHGlobal(8);
+			reg_info_ptr = Marshal.AllocHGlobal(Marshal.SizeOf<RegInfo>());
+			string_ptr = Marshal.AllocHGlobal(maxStringLen);
+			run_cb_delegate = new ActionInt32(RunComplete);
+			read_sysmem_delegate = new FuncInt32UInt64IntPtrInt32(ReadSysMemory);
+			write_sysmem_delegate = new FuncInt32UInt64IntPtrInt32(WriteSysMemory);
+			cpuLibraryRegistered = false;
+		}
+
+		~MpactCheriotCPU() {
+			Marshal.FreeHGlobal(error_ptr);
+			Marshal.FreeHGlobal(value_ptr);
+			Marshal.FreeHGlobal(reg_info_ptr);
+			Marshal.FreeHGlobal(string_ptr);
+		}
+
+		public override string Architecture { get { return "MpactCheriot";} }
+
+		public override void Start()
+		{
+			if (!cpuLibraryRegistered)
+			{
+					LogAndThrowRE("Failed to register cpu library");
+					return;
+			}
+			base.Start();
+
+			// Simulator initialization code goes here.
+			mpact_id = connect_with_sysbus((Int32)Id, maxStringLen, read_sysmem_delegate, write_sysmem_delegate);
+			if (mpact_id < 0) {
+				LogAndThrowRE("Failed to create simulator instance");
+				return;
+			}
+			// Set up configuration array.
+			var config_names = new string[4] {"memoryBase", "memorySize", "revocationMemoryBase", "clintMMRBase"};
+			var config_values = new string[4];
+			config_values[0] = "0x" + memoryBase.ToString("X");
+			config_values[1] = "0x" + memorySize.ToString("X");
+			config_values[2] = "0x" + revocationMemBase.ToString("X");
+			config_values[3] = "0x" + clintBase.ToString("X");
+			Int32 cfg_res = set_config(mpact_id, config_names, config_values, 4);
+			if (cfg_res < 0) {
+				LogAndThrowRE("Failed to set configuration information");
+			}
+		}
+
+		public override void Reset()
+		{
+			base.Reset();
+			instructionsExecutedThisRound = 0;
+			totalExecutedInstructions = 0;
+			// Call simulator to reset.
+			reset(mpact_id);
+		}
+
+		public override void Dispose()
+		{
+			base.Dispose();
+			// Cleanup: simulator and any unmanaged resources.
+			halt(mpact_id, error_ptr);
+			destruct(mpact_id);
+		}
+
+		public string CpuLibraryPath
+		{
+				get
+				{
+						return cpuLibraryPath;
+				}
+				set
+				{
+						if (!String.IsNullOrWhiteSpace(value))
+						{
+								if (cpuLibraryRegistered)
+								{
+										this.WarningLog("cpu library already registerd and " +
+																		"should not be updated again.");
+										return;
+								}
+								cpuLibraryPath = value;
+								try
+								{
+										binder = new NativeBinder(this, cpuLibraryPath);
+								}
+								catch (System.Exception e)
+								{
+										LogAndThrowRE("Failed to load CPU library: " + e.Message);
+								}
+								cpuLibraryRegistered = true;
+						}
+						else
+						{
+								return;
+						}
+				}
+		}
+
+		public override ulong ExecutedInstructions => totalExecutedInstructions;
+
+		public override ExecutionMode ExecutionMode
+		{
+			get
+			{
+				return executionMode;
+			}
+			set
+			{
+				lock(singleStepSynchronizer.Guard)
+				{
+					if (executionMode == value)
+					{
+						return;
+					}
+
+					executionMode = value;
+
+					singleStepSynchronizer.Enabled = IsSingleStepMode;
+					UpdateHaltedState();
+				}
+			}
+		}
+
+		[Register]
+		public override RegisterValue PC
+		{
+			get
+			{
+				Int32 error = read_register(mpact_id, PC_ID, value_ptr);
+				// TODO(torerik): Check for error.
+				if (error < 0) {
+					this.Log(LogLevel.Error, "Failed to read PC");
+				}
+				Int64 value = Marshal.ReadInt64(value_ptr);
+				return Convert.ToUInt64(value);
+			}
+
+			set
+			{
+				Int32 error = write_register(mpact_id, PC_ID, value);
+				// TODO(torerik): Check for error.
+				if (error < 0) {
+					this.Log(LogLevel.Error, "Failed to write PC");
+				}
+			}
+		}
+
+		protected override ExecutionResult ExecuteInstructions(ulong numberOfInstructionsToExecute, out ulong numberOfExecutedInstructions)
+		{
+			UInt64 instructionsExecutedThisRound = 0UL;
+			ExecutionResult result = ExecutionResult.Ok;
+			try
+			{
+				// Invoke simulator for the number of instructions.
+				instructionsExecutedThisRound = step(mpact_id, numberOfInstructionsToExecute, error_ptr);
+				// Parse the result.
+				Int32 step_result = Marshal.ReadInt32(error_ptr);
+				switch (step_result) {
+					case -1:
+						result = ExecutionResult.Aborted;
+						break;
+					case 0:
+						result = ExecutionResult.Ok;
+						break;
+					case 1:
+						result = ExecutionResult.Interrupted;
+						break;
+					case 2:
+						result = ExecutionResult.WaitingForInterrupt;
+						break;
+					case 3:
+						result = ExecutionResult.StoppedAtBreakpoint;
+						break;
+					case 4:
+						result = ExecutionResult.StoppedAtWatchpoint;
+						break;
+					case 5:
+						result = ExecutionResult.ExternalMmuFault;
+						break;
+					default:
+						LogAndThrowRE("Unknown return value from step - " + step_result);
+						break;
+				}
+			}
+			catch (Exception)
+			{
+				this.NoisyLog("CPU exception detected, halting.");
+				InvokeHalted(new HaltArguments(HaltReason.Abort, Id));
+				return ExecutionResult.Aborted;
+			}
+			finally
+			{
+				numberOfExecutedInstructions = instructionsExecutedThisRound;
+				totalExecutedInstructions += instructionsExecutedThisRound;
+			}
+
+			return result;
+		}
+
+		// ICPUWithRegisters methods implementations.
+		public  void SetRegisterUnsafe(int register, RegisterValue value) {
+			var status = write_register((Int32)mpact_id, (Int32)register, (UInt64)value);
+			if (status < 0) {
+				LogAndThrowRE("Failed to write register " + register);
+			}
+		}
+
+		public  RegisterValue GetRegisterUnsafe(int register) {
+			var status = read_register(mpact_id, register, value_ptr);
+			if (status < 0) {
+				LogAndThrowRE("Failed to read register " + register);
+			}
+			Int64 value = Marshal.ReadInt64(value_ptr);
+			return (UInt64)value;
+		}
+
+		private void GetMpactRegisters() {
+			if (registerMap.Count != 0) return;
+			Int32 num_regs = get_reg_info_size(mpact_id);
+			for (Int32 i = 0; i < num_regs; i++) {
+				Int32 result = get_reg_info(mpact_id, i, string_ptr, reg_info_ptr);
+				if (result < 0) {
+					this.Log(LogLevel.Error, "Failed to get register info for index " + i);
+					continue;
+				}
+				var reg_info = Marshal.PtrToStructure<RegInfo>(reg_info_ptr);
+				var cpu_reg = new CPURegister(reg_info.index, reg_info.width, reg_info.isGeneral, reg_info.isReadOnly);
+				var reg_name = Marshal.PtrToStringAuto(string_ptr);
+				registerMap.Add(reg_info.index, cpu_reg);
+				registerNamesMap.Add(reg_name, reg_info.index);
+			}
+		}
+
+		public  IEnumerable<CPURegister> GetRegisters() {
+			if (registerMap.Count == 0) {
+				GetMpactRegisters();
+			}
+			return registerMap.Values.OrderBy(x => x.Index);
+		}
+
+		public new string[,] GetRegistersValues() {
+			if (registerMap.Count == 0) {
+				GetMpactRegisters();
+			}
+			var result = new Dictionary<string, ulong>();
+			foreach (var reg in registerNamesMap) {
+				var status = read_register(mpact_id, reg.Value, value_ptr);
+				if (status < 0) continue;
+				Int64 value = Marshal.ReadInt64(value_ptr);
+				result.Add(reg.Key, Convert.ToUInt64(value));
+			}
+			var table = new Table().AddRow("Name", "Value");
+			table.AddRows(result, x=> x.Key, x=> "0x{0:X}".FormatWith(x.Value));
+			return table.ToArray();
+		}
+
+		private void LogAndThrowRE(string info)
+		{
+				this.Log(LogLevel.Error, info);
+				throw new RecoverableException(info);
+		}
+
+	public Int32 LoadImageFile(String file_name, UInt64 address) {
+		return load_image(mpact_id, file_name, address);
+	}
+
+	public void RunComplete(Int32 result) {
+		Console.Error.WriteLine($"RunComplete({result})");
+	}
+
+	public void Run() {
+		Int32 status = run(mpact_id, run_cb_delegate);
+		Console.Error.WriteLine($"Run -> {status}");
+	}
+
+	public void OnGPIO(int number, bool value) {
+		if (mpact_id <= 0) {
+			this.Log(LogLevel.Noisy, "OnGPIO: no simulator, discard gpio {0}:{1}", number, value);
+			return;
+		}
+		Int32 status = set_irq_value(mpact_id, number, value);
+		if (status < 0) {
+			Console.Error.WriteLine("Failure in setting irq value");
+		}
+	}
+
+	public Int32 ReadSysMemory(UInt64 address, IntPtr buffer, Int32 length) {
+		// Read from System Bus.
+		switch (length) {
+			case 1:
+				var data8 = new byte[length];
+				data8[0] = machine.SystemBus.ReadByte(address, this);
+				Marshal.Copy(data8, 0, buffer, length);
+				break;
+			case 2:
+				var data16 = new Int16[1];
+				data16[0] = (Int16) machine.SystemBus.ReadWord(address, this);
+				Marshal.Copy(data16, 0, buffer, 1);
+				break;
+			case 4:
+				var data32 = new Int32[1];
+				data32[0] = (Int32) machine.SystemBus.ReadDoubleWord(address, this);
+				Marshal.Copy(data32, 0, buffer, 1);
+				break;
+			case 8:
+				var data64 = new Int64[1];
+				data64[0] = (Int64) machine.SystemBus.ReadQuadWord(address, this);
+				Marshal.Copy(data64, 0, buffer, 1);
+				break;
+			default:
+				var dataN = new byte[length];
+				machine.SystemBus.ReadBytes(address, length, dataN, 0, false, this);
+				Marshal.Copy(dataN, 0, buffer, length);
+				break;
+		}
+		return length;
+	}
+
+	public Int32 WriteSysMemory(UInt64 address, IntPtr buffer, Int32 length) {
+		// Create a managed buffer for the store data.
+		// Copy the store data to the managed buffer.
+		// Write the data to the system bus.
+		switch (length) {
+			case 1:
+				var data8 = new byte[1];
+				Marshal.Copy(buffer, data8, 0, 1);
+				machine.SystemBus.WriteByte(address, data8[0], this);
+				break;
+			case 2:
+				var data16 = new Int16[1];
+				Marshal.Copy(buffer, data16, 0, 1);
+				machine.SystemBus.WriteWord(address, (ushort)data16[0], this);
+				break;
+			case 4:
+				var data32 = new Int32[1];
+				Marshal.Copy(buffer, data32, 0, 1);
+				machine.SystemBus.WriteDoubleWord(address, (uint)data32[0], this);
+				break;
+			case 8:
+				var data64 = new Int64[1];
+				Marshal.Copy(buffer, data64, 0, 1);
+				machine.SystemBus.WriteQuadWord(address, (ulong)data64[0], this);
+				break;
+			default:
+				var dataN = new byte[length];
+				Marshal.Copy(buffer, dataN, 0, length);
+				machine.SystemBus.WriteBytes(dataN, address);
+				break;
+		}
+		return length;
+	}
+
+		// ICPUWithHooks methods.
+
+		public void AddHookAtInterruptBegin(Action<ulong> hook) {
+			throw new RecoverableException("Interrupt hooks not currently supported");
+		}
+		public void AddHookAtInterruptEnd(Action<ulong> hook) {
+			throw new RecoverableException("Interrupt hooks not currently supported");
+		}
+		public void AddHookAtWfiStateChange(Action<bool> hook) {
+			throw new RecoverableException("Wfi state change hook not currently supported");
+		}
+
+		public void AddHook(ulong addr, Action<ICpuSupportingGdb, ulong> hook) {
+			throw new RecoverableException("Hooks not currently supported");
+		}
+		public void RemoveHook(ulong addr, Action<ICpuSupportingGdb, ulong> hook) {
+		}
+		public void RemoveHooksAt(ulong addr) {
+		}
+		public void RemoveAllHooks() {
+		}
+
+		// ICpuSuportingGdb methods.
+
+		public void EnterSingleStepModeSafely(HaltArguments args, bool? blocking = null) {
+			CheckCpuThreadId();
+			ChangeExecutionModeToSingleStep(blocking);
+			UpdateHaltedState();
+			InvokeHalted(args);
+		}
+
+		public string GDBArchitecture { get { return "riscv:cheriot"; } }
+
+		public List<GDBFeatureDescriptor> GDBFeatures {
+			get {
+				if (gdbFeatures.Any()) return gdbFeatures;
+
+				// Populate the register map if it hasn't been done yet.
+				if (registerNamesMap.Count == 0) {
+					GetMpactRegisters();
+				}
+				var cpuGroup = new GDBFeatureDescriptor("org.gnu.gdb.riscv.cpu");
+				var intType = $"uint32";
+				var c0_index = registerNamesMap["c0"];
+				for (var i = 0u; i < 16; ++i) {
+					cpuGroup.Registers.Add(new GDBRegisterDescriptor((uint)c0_index + i, 32u, $"c{i}", intType, "general"));
+				}
+				var pcc_index = registerNamesMap["pcc"];
+				cpuGroup.Registers.Add(new GDBRegisterDescriptor((uint)pcc_index, 32u, "pcc", "code_ptr", "general"));
+				gdbFeatures.Add(cpuGroup);
+
+				return gdbFeatures;
+			}
+		}
+
+		// End of ICpuSupportingGdb methods.
+
+		private List<GDBFeatureDescriptor> gdbFeatures = new List<GDBFeatureDescriptor>();
+		private UInt64 memoryBase;
+		private UInt64 memorySize;
+		private UInt64 revocationMemBase;
+		private UInt64 clintBase;
+		private Dictionary<Int32, CPURegister> registerMap = new Dictionary<Int32, CPURegister>();
+		private Dictionary<string, Int32> registerNamesMap = new Dictionary<string, Int32>();
+		private string cpuLibraryPath;
+    private bool cpuLibraryRegistered;
+		private string executableFile;
+		private string simulationFilePath;
+		private NativeBinder binder;
+		private readonly object nativeLock;
+		private readonly Int32 maxStringLen = 32;
+		private IntPtr value_ptr {get; set;}
+		private IntPtr error_ptr {get; set;}
+		private IntPtr reg_info_ptr {get; set;}
+		private IntPtr string_ptr {get; set;}
+		private ActionInt32 run_cb_delegate;
+		private FuncInt32UInt64IntPtrInt32 read_sysmem_delegate;
+		private FuncInt32UInt64IntPtrInt32 write_sysmem_delegate;
+#pragma warning disable 649
+		[Import(UseExceptionWrapper = false)]
+		private FuncInt32Int32FuncInt32UInt64IntPtrInt32FuncInt32UInt64IntPtrInt32 construct_with_sysbus; // (Int32 id, Int32 callback(Uint64, IntPtr, Int32), Int32 callback(Uint64, IntPtr, Int32));
+		[Import(UseExceptionWrapper = false)]
+		private FuncInt32Int32Int32FuncInt32UInt64IntPtrInt32FuncInt32UInt64IntPtrInt32 connect_with_sysbus; // (Int32 id, Int32 callback(Uint64, IntPtr, Int32), Int32 callback(Uint64, IntPtr, Int32));
+		[Import(UseExceptionWrapper = false)]
+		private ActionInt32 destruct;  // (Int32 id);
+		[Import(UseExceptionWrapper = false)]
+		private ActionInt32 reset;  // (Int32 id);
+		[Import(UseExceptionWrapper = false)]
+		private FuncInt32Int32 get_reg_info_size; // (Int32 id)
+		[Import(UseExceptionWrapper = false)]
+		private FuncInt32Int32Int32IntPtrIntPtr get_reg_info; // (Int32 id, IntPtr *name, IntPtr *struct);
+		[Import(UseExceptionWrapper = false)]
+		private FuncUInt64Int32StringIntPtr load_executable;  // (Int32 id, String file_name);
+		[Import(UseExceptionWrapper = false)]
+		private FuncInt32Int32StringUInt64 load_image; // (Int32 id, String file_name, UInt64 address);
+		[Import(UseExceptionWrapper = false)]
+        	private FuncUInt64Int32UInt64IntPtr step;  // (Int32 id, UInt64 step, IntPtr *error);
+		[Import(UseExceptionWrapper = false)]
+		private FuncInt32Int32Int32IntPtr read_register;  // (Int32 id, Int32 reg_id, IntPtr *value);
+		[Import(UseExceptionWrapper = false)]
+		private FuncInt32Int32Int32UInt64 write_register;  // (Int32 id, Int32 reg_id, UInt64 value);
+		[Import(UseExceptionWrapper = false)]
+		private FuncInt32Int32UInt64IntPtrInt32 read_memory; // (Int32 id, UInt64 address, IntPtr buffer, Int32 size);
+		[Import(UseExceptionWrapper = false)]
+		private FuncInt32Int32UInt64IntPtrInt32 write_memory; // (Int32 id, UInt64 address, IntPtr buffer, Int32 size);
+		[Import(UseExceptionWrapper = false)]
+		private FuncInt32Int32ActionInt32 run; // (Int32 id, void callback(Int32) )
+		[Import(UseExceptionWrapper = false)]
+		private FuncInt32Int32IntPtr halt; // (Int32 id, IntPtr *value)
+		// Set config.
+		[Import(UseExceptionWrapper = false)]
+		private FuncInt32Int32StringArrStringArrInt32 set_config; // Int32 set_config(Int32 id, string[] names string[] values, Int32 size);
+		[Import(UseExceptionWrapper = false)]
+		private FuncInt32Int32Int32Bool set_irq_value; // Int32 set_irq_value(Int32 id, Int32 irq_no, bool value);
+
+#pragma warning restore 649
+
+		private Int32 mpact_id {get; set;}
+		private ulong instructionsExecutedThisRound {get; set;}
+		private ulong totalExecutedInstructions {get; set;}
+		private const int PC_ID = 0x07b1;
+	}
+}
+
diff --git a/shodan_infrastructure/MpactCheriotPeripheral.cs b/shodan_infrastructure/MpactCheriotPeripheral.cs
new file mode 100644
index 0000000..e885bfa
--- /dev/null
+++ b/shodan_infrastructure/MpactCheriotPeripheral.cs
@@ -0,0 +1,180 @@
+using System;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Threading;
+using System.Collections.Generic;
+using System.Collections.Concurrent;
+using Antmicro.Renode.Core;
+using Antmicro.Renode.Debugging;
+using Antmicro.Renode.Exceptions;
+using Antmicro.Renode.Hooks;
+using Antmicro.Renode.Logging;
+using Antmicro.Renode.Peripherals;
+using Antmicro.Renode.Peripherals.Bus;
+using Antmicro.Renode.Peripherals.CPU;
+using Antmicro.Renode.Peripherals.Timers;
+using Antmicro.Renode.Peripherals.CPU.Disassembler;
+using Antmicro.Renode.Peripherals.CPU.Registers;
+using Antmicro.Renode.Utilities;
+using Antmicro.Renode.Time;
+using Antmicro.Renode.Utilities.Binding;
+
+namespace Antmicro.Renode.Peripherals.MpactPeripheral {
+
+				[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+				public delegate Int32 FuncInt32Int32Int32FuncInt32UInt64IntPtrInt32FuncInt32UInt64IntPtrInt32(Int32 param0, Int32 param1,
+			FuncInt32UInt64IntPtrInt32 param2, FuncInt32UInt64IntPtrInt32 param3);
+
+	public class MpactCheriotPeripheral : IKnownSize, IBytePeripheral, IWordPeripheral, IDoubleWordPeripheral, IQuadWordPeripheral, IMultibyteWritePeripheral, IDisposable {
+
+		public MpactCheriotPeripheral(IMachine machine, long size, int id) {
+			if (size == 0) throw new ConstructionException("Memory size cannot be 0");
+			this.machine = machine;
+			this.size = size;
+			this.id = id;
+			cpuLibraryRegistered = false;
+			error_ptr = Marshal.AllocHGlobal(4);
+			value_ptr = Marshal.AllocHGlobal(8);
+			string_ptr = Marshal.AllocHGlobal(maxStringLen);
+		}
+
+		public string CpuLibraryPath {
+			get {
+				return cpuLibraryPath;
+			}
+			set {
+				if (!String.IsNullOrWhiteSpace(value)) {
+					if (cpuLibraryRegistered) {
+						this.WarningLog("cpu library already registerd and " +
+														"should not be updated again.");
+						return;
+					}
+					cpuLibraryPath = value;
+					try {
+						binder = new NativeBinder(this, cpuLibraryPath);
+					} catch (System.Exception e) {
+						LogAndThrowRE("Failed to load CPU library: " + e.Message);
+					}
+					cpuLibraryRegistered = true;
+				} else {
+					return;
+				}
+			}
+		}
+
+		public void Start() {
+			if (!cpuLibraryRegistered) {
+				LogAndThrowRE("Failed to register cpu library");
+				return;
+			}
+			this.Log(LogLevel.Noisy, "Start simulator with id {0}", id);
+			mpact_id = connect_with_sysbus(id, maxStringLen, null, null);
+			if (mpact_id < 0) {
+				LogAndThrowRE("Failed to create simulator instance");
+				return;
+			}
+		}
+
+		public void Reset() {}
+		public void Dispose() {}
+
+		public void WriteByte(long address, byte value) {
+			var data8 = new byte[1];
+			data8[0] = (byte) value;
+			Marshal.Copy(data8, 0, value_ptr, 1);
+			write_memory(mpact_id, (UInt64) address, value_ptr, 1);
+		}
+
+		public byte ReadByte(long address) {
+			var data8 = new byte[1];
+			read_memory(mpact_id, (UInt64) address, value_ptr, 1);
+			Marshal.Copy(value_ptr, data8, 0, 1);
+			return (byte) data8[0];
+		}
+
+		public void WriteWord(long address, ushort value) {
+			var data16 = new Int16[1];
+			data16[0] = (Int16) value;
+			Marshal.Copy(data16, 0, value_ptr, 1);
+			write_memory(mpact_id, (UInt64) address, value_ptr, 2);
+		}
+
+		public ushort ReadWord(long address) {
+			var data16 = new Int16[1];
+			read_memory(mpact_id, (UInt64) address, value_ptr, 2);
+			Marshal.Copy(value_ptr, data16, 0, 1);
+			return (ushort) data16[0];
+		}
+
+		public void WriteDoubleWord(long address, uint value) {
+			var data32 = new Int32[1];
+			data32[0] = (Int32) value;
+			Marshal.Copy(data32, 0, value_ptr, 1);
+			write_memory(mpact_id, (UInt64) address, value_ptr, 4);
+		}
+
+		public uint ReadDoubleWord(long address) {
+			var data32 = new Int32[1];
+			read_memory(mpact_id, (UInt64) address, value_ptr, 4);
+			Marshal.Copy(value_ptr, data32, 0, 1);
+			return (uint) data32[0];
+		}
+
+		public void WriteQuadWord(long address, ulong value) {
+			var data64 = new Int64[1];
+			data64[0] = (Int64) value;
+			Marshal.Copy(data64, 0, value_ptr, 1);
+			write_memory(mpact_id, (UInt64) address, value_ptr, 8);
+		}
+
+		public ulong ReadQuadWord(long address) {
+			var data64 = new Int64[1];
+			read_memory(mpact_id, (UInt64) address, value_ptr, 8);
+			Marshal.Copy(value_ptr, data64, 0, 1);
+			return (ulong) data64[0];
+		}
+
+		public byte[] ReadBytes(long offset, int count, ICPU context = null) {
+			var bytes = new byte[count];
+			var byte_array_ptr = Marshal.AllocHGlobal(count);
+			read_memory(mpact_id, (UInt64) offset, byte_array_ptr, count);
+			Marshal.Copy(value_ptr, bytes, 0, count);
+			Marshal.FreeHGlobal(byte_array_ptr);
+			return bytes;
+		}
+
+		public void WriteBytes(long offset, byte[] array, int startingIndex, int count, ICPU context = null) {
+			var byte_array_ptr = Marshal.AllocHGlobal(count);
+			Marshal.Copy(array, startingIndex, byte_array_ptr, count);
+			write_memory(mpact_id, (UInt64) offset, byte_array_ptr, count);
+			Marshal.FreeHGlobal(byte_array_ptr);
+		}
+
+		public long Size { get { return size;} }
+		private Int32 mpact_id;
+		private Int32 id {get; set;}
+		private long size;
+		private NativeBinder binder;
+		private string cpuLibraryPath;
+		private bool cpuLibraryRegistered;
+		private IMachine machine;
+		private readonly Int32 maxStringLen = 32;
+		private IntPtr value_ptr {get; set;}
+		private IntPtr error_ptr {get; set;}
+		private IntPtr string_ptr {get; set;}
+
+		private void LogAndThrowRE(string info) {
+			this.Log(LogLevel.Error, info);
+			throw new RecoverableException(info);
+		}
+
+	#pragma warning disable 649
+		[Import(UseExceptionWrapper = false)]
+		private FuncInt32Int32Int32FuncInt32UInt64IntPtrInt32FuncInt32UInt64IntPtrInt32 connect_with_sysbus; // (Int32 id, Int32 callback(Uint64, IntPtr, Int32), Int32 callback(Uint64, IntPtr, Int32));
+		[Import(UseExceptionWrapper = false)]
+		private FuncInt32Int32UInt64IntPtrInt32 read_memory; // (Int32 id, UInt64 address, IntPtr buffer, Int32 size);
+		[Import(UseExceptionWrapper = false)]
+		private FuncInt32Int32UInt64IntPtrInt32 write_memory; // (Int32 id, UInt64 address, IntPtr buffer, Int32 size);
+	}
+
+}  // namespace Antmicro.Renode.Peripherals.MpactPeripheral
diff --git a/shodan_infrastructure/SmcCheriotCPU.cs b/shodan_infrastructure/SmcCheriotCPU.cs
new file mode 100644
index 0000000..defca40
--- /dev/null
+++ b/shodan_infrastructure/SmcCheriotCPU.cs
@@ -0,0 +1,81 @@
+//
+// Copyright (c) 2023 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.
+
+// XXX need to refactor Mpact to share/reuse SmcRiscv32.cs
+
+using System;
+using System.Collections.Generic;
+using Antmicro.Renode.Core.Structure.Registers;
+using Antmicro.Renode.Core;
+using Antmicro.Renode.Logging;
+using Antmicro.Renode.Peripherals.Bus;
+
+namespace Antmicro.Renode.Peripherals.MpactCPU
+{
+  public class SmcCheriot_ControlBlock : IDoubleWordPeripheral, IKnownSize
+  {
+    public SmcCheriot_ControlBlock(MpactCheriotCPU cpu, long pc)
+    {
+      this.cpu = cpu;
+      this.startPC = pc;
+      DefineRegisters();
+      Reset();
+    }
+
+    public void Reset() // IPeripheral
+    {
+    }
+
+    public uint ReadDoubleWord(long addr) // IDoubleWordPeripheral
+    {
+      return RegistersCollection.Read(addr);
+    }
+
+    public void WriteDoubleWord(long addr, uint value) // IDoubleWordPeripheral
+    {
+      RegistersCollection.Write(addr, value);
+    }
+
+    public long Size => 0x8;
+    private MpactCheriotCPU cpu;
+    private long startPC;
+
+    public DoubleWordRegisterCollection RegistersCollection { get; private set; }
+    private void DefineRegisters() {
+      var registerDictionary = new Dictionary<long, DoubleWordRegister>
+      {{(long)Registers.BootEnRegwen, new DoubleWordRegister(this)
+        .WithReservedBits(0, 31)
+      },
+      {(long)Registers.BootEnCtrl, new DoubleWordRegister(this)
+        .WithFlag(0, FieldMode.Write, writeCallback: (_, val) => {
+          if (val) {
+            this.Log(LogLevel.Noisy, "Starting SMC at 0x{0:X}", this.startPC);
+            cpu.PC = this.startPC;
+            cpu.IsHalted = false;
+          } else {
+            this.Log(LogLevel.Noisy, "Stopping SMC");
+            cpu.PC = 0;
+            cpu.IsHalted = true;
+          }
+        })
+      }};
+      RegistersCollection = new DoubleWordRegisterCollection(this, registerDictionary);
+    }
+    public enum Registers {
+       BootEnRegwen = 0x0,
+       BootEnCtrl = 0x4,
+    }
+  }
+}