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,
+ }
+ }
+}