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;
     }