Share memory between renode and kelvin_sim The MappedMemory is passed to kelvin_sim for load/store in the Kelvin SW program, so Renode and CantripOS can access the memory content same as the previous KelvinRiscV32 module. Change-Id: I73853336789f90bd755eaf9f742957010deff7a5
diff --git a/kelvin.resc b/kelvin.resc index 68ecce9..8bae286 100644 --- a/kelvin.resc +++ b/kelvin.resc
@@ -31,12 +31,10 @@ sysbus.cpu2 IsHalted true sysbus.cpu2 CpuLibraryPath $cpuLibrary -sysbus.cpu2 MemoryOffset 0 macro reset """ - # TODO(hcindyl): Replace it with LoadBinary `sysbus LoadBinary $bin 0x5A000000` - sysbus.cpu2 ExecutableFile $bin + sysbus LoadBinary $bin 0x5A000000 # Start the vector core at address 0 of its TCM and halt / reset it. sysbus.ml_top_controlblock WriteDoubleWord 0xc 3 """
diff --git a/platforms/kelvin_ml_core_external_cpu.repl b/platforms/kelvin_ml_core_external_cpu.repl index a2f3b11..a4dd79f 100644 --- a/platforms/kelvin_ml_core_external_cpu.repl +++ b/platforms/kelvin_ml_core_external_cpu.repl
@@ -13,12 +13,13 @@ // limitations under the License. // *************************************************** -// ML Core that uses kelvin_sim as the external CPU library. WIP +// ML Core that uses kelvin_sim as the external CPU library. // *************************************************** cpu2: CPU.KelvinCPU @ sysbus id: 1 cpuType: "Kelvin" + memory: ram_ml_top_dmem // RAM_ML_TOP_MEM [‘h5A00_0000 - ‘h5A3F_FFFF) 4MB RAM for ML core ram_ml_top_dmem: Memory.MappedMemory @ sysbus 0x5A000000
diff --git a/shodan_infrastructure/KelvinCPU.cs b/shodan_infrastructure/KelvinCPU.cs index d9df2ef..6653ee7 100644 --- a/shodan_infrastructure/KelvinCPU.cs +++ b/shodan_infrastructure/KelvinCPU.cs
@@ -25,6 +25,7 @@ using Antmicro.Renode.Logging; using Antmicro.Renode.Peripherals.Bus; using Antmicro.Renode.Peripherals.CPU; +using Antmicro.Renode.Peripherals.Memory; using Antmicro.Renode.Peripherals.Timers; using Antmicro.Renode.Peripherals.CPU.Disassembler; using Antmicro.Renode.Peripherals.CPU.Registers; @@ -55,6 +56,7 @@ } public KelvinCPU(uint id, string cpuType, Machine machine, + MappedMemory memory, Endianess endianess = Endianess.LittleEndian, CpuBitness bitness = CpuBitness.Bits32) : base(id, cpuType, machine, endianess, bitness) @@ -63,7 +65,12 @@ value_ptr = Marshal.AllocHGlobal(8); reg_info_ptr = Marshal.AllocHGlobal(Marshal.SizeOf<RegInfo>()); string_ptr = Marshal.AllocHGlobal(maxStringLen); - memoryOffset = 0; + dataMemory = memory; + + // Touch all the memory segments to ensure all of segments are + // allocated with valid pointers. + dataMemory.TouchAllSegments(); + cpuLibraryRegistered = false; } ~KelvinCPU() @@ -82,30 +89,20 @@ LogAndThrowRE("Failed to create kelvin sim instance"); return; } - Int32 result = load_image(kelvin_id, executableFile, memoryOffset); - if (result < 0) - { - LogAndThrowRE("Failed to load executable image"); - } - this.NoisyLog("Load executable image " + executableFile); - PC = 0; } public void RegisterControlBlock(MlTopControlBlock controlBlock) { - ControlBlock = controlBlock; - ControlBlockRegistered = true; + this.controlBlock = controlBlock; + controlBlockRegistered = true; } - private MlTopControlBlock ControlBlock; - private bool ControlBlockRegistered = false; - private void HandleKelvinEBreak() { - if (ControlBlockRegistered) + if (controlBlockRegistered) { - ControlBlock.ExecEBreak(); + controlBlock.ExecEBreak(); } else { @@ -115,9 +112,9 @@ private void HandleKelvinMPause() { - if (ControlBlockRegistered) + if (controlBlockRegistered) { - ControlBlock.ExecMPause(); + controlBlock.ExecMPause(); } else { @@ -143,7 +140,10 @@ { base.Dispose(); // Cleanup: simulator and any unmanaged resources. - destruct(kelvin_id); + lock(nativeLock) + { + destruct(kelvin_id); + } } public override ulong ExecutedInstructions => totalExecutedInstructions; @@ -176,23 +176,32 @@ { get { - Int32 error = read_register(kelvin_id, PC_ID, value_ptr); - // TODO(hcindyl): Check different error types. - if (error < 0) + Int64 value = 0; + lock(nativeLock) { - this.ErrorLog("Failed to read PC"); + Int32 error = read_register(kelvin_id, PC_ID, value_ptr); + // TODO(hcindyl): Check different error types. + if (error < 0) + { + this.ErrorLog("Failed to read PC"); + } + value = Marshal.ReadInt64(value_ptr); + } - Int64 value = Marshal.ReadInt64(value_ptr); return Convert.ToUInt64(value); } set { - Int32 error = write_register(kelvin_id, PC_ID, value); - // TODO(hcindyl): Check different error types. - if (error < 0) + lock(nativeLock) { - this.NoisyLog("Failed to write PC"); + Int32 error = write_register(kelvin_id, PC_ID, value); + + // TODO(hcindyl): Check different error types. + if (error < 0) + { + this.NoisyLog("Failed to write PC"); + } } } } @@ -205,37 +214,41 @@ ExecutionResult result = ExecutionResult.Ok; try { - // Invoke simulator for the number of instructions. - instructionsExecutedThisRound = step(kelvin_id, numberOfInstructionsToExecute, - error_ptr); - // Parse the result. - Int32 step_result = Marshal.ReadInt32(error_ptr); - switch (step_result) + lock(nativeLock) { - 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; + // Invoke simulator for the number of instructions. + instructionsExecutedThisRound = step(kelvin_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) @@ -272,21 +285,28 @@ // ICPUWithRegisters methods implementations. public void SetRegisterUnsafe(int register, RegisterValue value) { - var status = write_register((Int32)kelvin_id, (Int32)register, (UInt64)value); - if (status < 0) + lock(nativeLock) { - LogAndThrowRE("Failed to write register " + register); + var status = write_register((Int32)kelvin_id, (Int32)register, (UInt64)value); + if (status < 0) + { + LogAndThrowRE("Failed to write register " + register); + } } } public RegisterValue GetRegisterUnsafe(int register) { - var status = read_register(kelvin_id, (UInt32)register, value_ptr); - if (status < 0) + Int64 value = 0; + lock(nativeLock) { - LogAndThrowRE("Failed to read register " + register); + var status = read_register(kelvin_id, (UInt32)register, value_ptr); + if (status < 0) + { + LogAndThrowRE("Failed to read register " + register); + } + value = Marshal.ReadInt64(value_ptr); } - Int64 value = Marshal.ReadInt64(value_ptr); return (UInt64)value; } @@ -294,22 +314,25 @@ { if (registerMap.Count == 0) { - Int32 num_regs = get_reg_info_size(kelvin_id); - for (Int32 i = 0; i < num_regs; i++) + lock(nativeLock) { - Int32 result = get_reg_info(kelvin_id, i, string_ptr, reg_info_ptr); - if (result < 0) + Int32 num_regs = get_reg_info_size(kelvin_id); + for (Int32 i = 0; i < num_regs; i++) { - this.ErrorLog("Failed to get register info for index " + i); - continue; + Int32 result = get_reg_info(kelvin_id, i, string_ptr, reg_info_ptr); + if (result < 0) + { + this.ErrorLog("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); } - 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); } } return registerMap.Values.OrderBy(x => x.Index); @@ -324,9 +347,13 @@ var result = new Dictionary<string, ulong>(); foreach (var reg in registerNamesMap) { - var status = read_register(kelvin_id, (UInt32)reg.Value, value_ptr); - if (status < 0) continue; - Int64 value = Marshal.ReadInt64(value_ptr); + Int64 value = 0; + lock(nativeLock) + { + var status = read_register(kelvin_id, (UInt32)reg.Value, value_ptr); + if (status < 0) continue; + value = Marshal.ReadInt64(value_ptr); + } result.Add(reg.Key, Convert.ToUInt64(value)); } var table = new Table().AddRow("Name", "Value"); @@ -344,6 +371,12 @@ { if (!String.IsNullOrWhiteSpace(value)) { + if (cpuLibraryRegistered) + { + this.WarningLog("cpu library already registerd and " + + "should not be updated again."); + return; + } cpuLibraryPath = value; try { @@ -353,9 +386,40 @@ { LogAndThrowRE("Failed to load CPU library: " + e.Message); } + cpuLibraryRegistered = true; + + this.NoisyLog("Memory block size: {0}, total size: {1}", + dataMemory.SegmentSize, dataMemory.Size); + IntPtr[] memoryBlocks = new IntPtr[dataMemory.SegmentCount]; + for (int i = 0; i < dataMemory.SegmentCount; i++) + { + this.NoisyLog("Segment {0}: pointer {1:8X}, size {2}", + i, dataMemory.GetSegment(i), + dataMemory.SegmentSize); + memoryBlocks[i] = dataMemory.GetSegment(i); + } // Simulator initialization code goes here. - kelvin_id = construct(maxStringLen); + IntPtr memoryBlocksPtr = Marshal.AllocHGlobal( + dataMemory.SegmentCount * Marshal.SizeOf<IntPtr>()); + Marshal.Copy(memoryBlocks, 0, memoryBlocksPtr, memoryBlocks.Length); + lock(nativeLock) + { + // Initiate a kelvin_sim simulator. For memory access + // inside the kelvin_sim, it refers to the memory block + // pointers provided by the dataMemory here. + // + // Note we don't preempt the memory access from other + // cores (e.g., MlCoordinator) with the nativeLock. The + // control flow we have in CantripOS should not cause + // the conflict memory access. + kelvin_id = construct_with_memory(maxStringLen, + (ulong)dataMemory.SegmentSize, + (ulong)dataMemory.Size, + memoryBlocksPtr); + } + Marshal.FreeHGlobal(memoryBlocksPtr); + if (kelvin_id < 0) { LogAndThrowRE("Failed to create simulator instance"); @@ -370,63 +434,30 @@ } } - public string ExecutableFile - { - get - { - return executableFile; - } - set - { - if (!String.IsNullOrWhiteSpace(value)) - { - executableFile = value; - } - else - { - return; - } - } - } - - public UInt64 MemoryOffset - { - get - { - return memoryOffset; - } - set - { - - memoryOffset = value; - - } - } - private void LogAndThrowRE(string info) { this.Log(LogLevel.Error, info); throw new RecoverableException(info); } - private Dictionary<Int32, CPURegister> registerMap = new Dictionary<Int32, CPURegister>(); - private Dictionary<string, Int32> registerNamesMap = new Dictionary<string, Int32>(); - protected string cpuLibraryPath; - protected string executableFile; + private bool cpuLibraryRegistered; - protected UInt64 memoryOffset; + private readonly MappedMemory dataMemory; private NativeBinder binder; - private readonly object nativeLock; + // lock to ensure only one binding function call is running. + private readonly object nativeLock = new object(); 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; } -#pragma warning disable 649 + + // Functions from cpuLibrary [Import(UseExceptionWrapper = false)] - // Int32(Int32 max_string_lenth) - private FuncInt32Int32 construct; + // Int32(Int32 max_string_lenth, UInt64 mem_block_size, + // UInt64 mem_size, uint8_t ** mem_block_ptr_list); + private FuncInt32Int32UInt64UInt64IntPtr construct_with_memory; [Import(UseExceptionWrapper = false)] // Int32(Int32 id); private FuncInt32Int32 destruct; @@ -440,9 +471,6 @@ // Int32(Int32 id, Int32 index, char *name, IntPtr *info); private FuncInt32Int32Int32IntPtrIntPtr get_reg_info; [Import(UseExceptionWrapper = false)] - // Int32(Int32 id, String file_name, Int64 address); - private FuncInt32Int32StringUInt64 load_image; - [Import(UseExceptionWrapper = false)] // UInt64(Int32 id, UInt64 step, IntPtr *status); private FuncUInt64Int32UInt64IntPtr step; [Import(UseExceptionWrapper = false)] @@ -451,12 +479,25 @@ [Import(UseExceptionWrapper = false)] // Int32(Int32 id, Int32 reg_id, UInt64 value); private FuncInt32Int32Int32UInt64 write_register; -#pragma warning restore 649 - private Int32 kelvin_id { get; set; } + // Register access. + private Dictionary<Int32, CPURegister> registerMap = new Dictionary<Int32, CPURegister>(); + private Dictionary<string, Int32> registerNamesMap = new Dictionary<string, Int32>(); + + // PC magic ID. Defined in + // https://github.com/riscv/riscv-opcodes/blob/d752f193cec46cfd33300abba2a3a0d020c36755/constants.py#L317 + // It is used to set/get PC by passing this register ID via `read_register` + // and `write_register`. + private const int PC_ID = 0x07b1; + private ulong instructionsExecutedThisRound { get; set; } private ulong totalExecutedInstructions { get; set; } - private const int PC_ID = 0x07b1; // PC magic ID + + private Int32 kelvin_id { get; set; } + + // MlTopControlBlock + private MlTopControlBlock controlBlock; + private bool controlBlockRegistered = false; }