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