Add 1-window WindowMMU to Kelvin to support TCM offset This CL adds an untouchable WindowMMU to Kelvin with a single window translating addresses 0x00000000 : MemorySize to MemoryRangeStart : MemoryRangeStart + MemorySize. This is needed in simulation because Kelvin's software expects memory to begin at address 0x00000000 but Renode doesn't support multiple address spaces without the use of MMUs. Change-Id: I08b7baf071f916f29f62470224e43ab7e7c221d8
diff --git a/kelvin.resc b/kelvin.resc index b3ee710..4a8aead 100644 --- a/kelvin.resc +++ b/kelvin.resc
@@ -30,9 +30,18 @@ macro reset """ - sysbus LoadBIN $bin 0x5A000000 - # Start the vector core at address 0 of its TCM. - sysbus.cpu2 IsHalted true - sysbus.cpu2 PC 0x5A000000 + sysbus LoadBinary $bin 0x5A000000 + # Start the vector core at address 0 of its TCM and halt / reset it. + sysbus.vec_controlblock WriteDoubleWord 12 3 """ runMacro $reset + +# Note: GDB doesn't seem to like the way we're setting the program counter in +# the vec_controlblock. Breakpoints set to the exact reset address don't seem to +# get reliably triggered. We should probably root cause this later when we have +# time, but as a workaround setting the breakpoint to reset_addr+4 instead seems +# to work fine. +$gdb_port?=3333 +machine StartGdbServer $gdb_port false sysbus.cpu2 + +start
diff --git a/platforms/kelvin.repl b/platforms/kelvin.repl index 0aa30ae..00469f3 100644 --- a/platforms/kelvin.repl +++ b/platforms/kelvin.repl
@@ -16,8 +16,18 @@ // Kelvin single test core // *************************************************** -using "sim/config/platforms/ml_core.repl" +// TODO(jonathantate): Move the cpu and dmem blocks back into ml_core.repl and +// uncomment this 'using ".../ml_core.repl"' line. +// using "sim/config/platforms/ml_core.repl" + +cpu2: CPU.KelvinRiscV32 @ sysbus + hartId: 2 + +//RAM_VEC_MEM [‘h5A00_0000 - ‘h5A3F_FFFF) 4MB RAM for Vector core +ram_vec_dmem: Memory.MappedMemory @ sysbus 0x5A000000 + size: 0x00400000 vec_controlblock : CPU.KelvinRiscV32_ControlBlock @ sysbus 0x5C000000 core: cpu2 - memoryRangeStart: 0x5A000000 + tcmSize: 0x00400000 + tcmRangeStart: 0x5A000000
diff --git a/shodan_infrastructure/KelvinRiscV32.cs b/shodan_infrastructure/KelvinRiscV32.cs index 1b7772d..8b912c1 100644 --- a/shodan_infrastructure/KelvinRiscV32.cs +++ b/shodan_infrastructure/KelvinRiscV32.cs
@@ -30,8 +30,17 @@ string cpuType = "rv32im") : base(null, cpuType, machine, hartId, privilegeArchitecture, endianness) { - InstallCustomInstruction(pattern: "00001000000000000000000001110011", handler: HandleKelvinMPause); InstallCustomInstruction(pattern: "00000000000100000000000001110011", handler: HandleKelvinEBreak); // Kelvin doesn't implement rv32i ebreak correctly + InstallCustomInstruction(pattern: "00000010000000000000000001110011", handler: HandleKelvinEExit); + InstallCustomInstruction(pattern: "00000100000000000000000001110011", handler: HandleKelvinEYield); + InstallCustomInstruction(pattern: "00000110000000000000000001110011", handler: HandleKelvinECtxSw); + InstallCustomInstruction(pattern: "00001000000000000000000001110011", handler: HandleKelvinMPause); + InstallCustomInstruction(pattern: "011110000000-----000000001110111", handler: HandleKelvinFLog); + InstallCustomInstruction(pattern: "011110000000-----001000001110111", handler: HandleKelvinSLog); + InstallCustomInstruction(pattern: "011110000000-----010000001110111", handler: HandleKelvinCLog); + InstallCustomInstruction(pattern: "011110000000-----011000001110111", handler: HandleKelvinKLog); + InstallCustomInstruction(pattern: "00100110000000000000000001110111", handler: HandleKelvinFlushAll); + InstallCustomInstruction(pattern: "001001100000-----000000001110111", handler: HandleKelvinFlushAt); Reset(); } @@ -58,14 +67,65 @@ private KelvinRiscV32_ControlBlock ControlBlock; private bool ControlBlockRegistered = false; + private void HandleKelvinEBreak(UInt64 opcode) + { + ControlBlock.ExecEBreak(); + } + + private void HandleKelvinEExit(UInt64 opcode) + { + ControlBlock.ExecEBreak(); + } + + private void HandleKelvinEYield(UInt64 opcode) + { + // To be implemented + ControlBlock.ExecEBreak(); + } + + private void HandleKelvinECtxSw(UInt64 opcode) + { + // To be implemented + ControlBlock.ExecEBreak(); + } + private void HandleKelvinMPause(UInt64 opcode) { ControlBlock.ExecMPause(); } - private void HandleKelvinEBreak(UInt64 opcode) + private void HandleKelvinFLog(UInt64 opcode) { - ControlBlock.ExecEBreak(); + this.Log(LogLevel.Noisy, "FLog dropped (unimplemented)"); + // To be implemented + } + + private void HandleKelvinSLog(UInt64 opcode) + { + this.Log(LogLevel.Noisy, "SLog dropped (unimplemented)"); + // To be implemented + } + + private void HandleKelvinCLog(UInt64 opcode) + { + this.Log(LogLevel.Noisy, "CLog dropped (unimplemented)"); + // To be implemented + } + + private void HandleKelvinKLog(UInt64 opcode) + { + this.Log(LogLevel.Noisy, "KLog dropped (unimplemented)"); + // To be implemented + } + + private void HandleKelvinFlushAll(UInt64 opcode) + { + // No-op in simulator + } + + private void HandleKelvinFlushAt(UInt64 opcode) + { + // No-op in simulator } } @@ -77,12 +137,16 @@ public KelvinRiscV32_ControlBlock(Machine machine, KelvinRiscV32 core, - ulong memoryRangeStart) + ulong tcmSize, + ulong tcmRangeStart) { Machine = machine; Core = core; - this.memoryRangestart = memoryRangeStart; + this.tcmRangeStart = tcmRangeStart; + this.tcmSize = tcmSize; + + Mmu = InitMmu(tcmRangeStart, tcmSize); HostReqIRQ = new GPIO(); FinishIRQ = new GPIO(); @@ -94,6 +158,8 @@ RegistersCollection = new DoubleWordRegisterCollection(this); DefineRegisters(); + Core.AddHookOnMmuFault(HandleFault); + Reset(); } @@ -103,6 +169,18 @@ RegistersCollection.Reset(); } + private ExternalMmuBase InitMmu(ulong rangeStart, ulong tcmSize) + { + var mmu = new ExternalMmuBase(this.Core, 1); + + mmu.SetWindowStart(0, 0); + mmu.SetWindowEnd(0, tcmSize); + mmu.SetWindowAddend(0, tcmRangeStart); + mmu.SetWindowPrivileges(0, 7); // Read / write / execute + + return mmu; + } + private void DefineRegisters() { Registers.IntrState.Define32(this) @@ -161,9 +239,17 @@ { this.Log(LogLevel.Noisy, "Resetting core."); Core.Reset(); - ulong startAddress = (val >> (ulong)Mode.NumBits) + memoryRangeStart; - this.Log(LogLevel.Noisy, "Setting PC to 0x{0:X}.", startAddress); - Core.PC = startAddress; + } + + if ((mode & Mode.SwReset) != 0) + { + // Always set the PC on assignment to help with GDB. + ulong startAddress = (val >> (int)Mode.NumBits); + if ((Core.PC != startAddress)) + { + this.Log(LogLevel.Noisy, "Setting PC to 0x{0:X}.", startAddress); + Core.PC = startAddress; + } } // Unpause the core when both freeze and SwReset are deasserted. @@ -182,20 +268,20 @@ // To-do: Not sure how to implement disablable memory banks. Registers.MemoryBankControl.Define32(this) - .WithValueField(0, 16, out MemoryEnable, name: "MEM_ENABLE") + .WithValueField(0, 4, out MemoryEnable, name: "MEM_ENABLE") .WithIgnoredBits(16, 32 - 16) ; // To-do: Not sure how to implement memory access range checks. Registers.ErrorStatus.Define32(this) .WithFlag(0, name: "MEM_OUT_OF_RANGE") - .WithValueField(1, 9, out MemoryDisableAccess, name: "MEM_DISABLE_ACCESS") - .WithIgnoredBits(9, 32 - 9) + .WithValueField(2, 4, out MemoryDisableAccess, name: "MEM_DISABLE_ACCESS") + .WithIgnoredBits(16, 32 - 16) ; Registers.InitStart.Define32(this) .WithValueField(0, 22, out InitStartAddress, name: "ADDRESS") - .WithIgnoredBits(22, 32 - 22) + .WithIgnoredBits(24, 32 - 24) ; Registers.InitEnd.Define32(this) @@ -216,7 +302,6 @@ writeAddress += DataPageSize) { Machine.SystemBus.WriteBytes(DataErasePattern, - memoryRangeStart + (ulong)writeAddress, (uint)DataPageSize, true); } @@ -282,6 +367,45 @@ public DoubleWordRegisterCollection RegistersCollection { get; private set; } + + private void HandleFault(ulong faultAddress, AccessType accessType, int windowIndex) + { + LogFaultType(Mmu, faultAddress, accessType); + ExecEBreak(); + } + + private void LogFaultType(ExternalMmuBase mmu, ulong faultAddress, AccessType accessType) + { + var mmuName = mmu.GetType().Name; + var virtualWindowIndex = (uint)(faultAddress / tcmSize); + + if(virtualWindowIndex >= 1) + { + this.Log(LogLevel.Error, "{0}: Translation request for {1} at 0x{2:X}: failure, no window at address", mmuName, accessType, faultAddress); + } + else + { + var windowEnd = mmu.GetWindowEnd(virtualWindowIndex); + var windowPrivilege = mmu.GetWindowPrivileges(virtualWindowIndex); + + if(faultAddress > windowEnd) + { + this.Log(LogLevel.Error, "{0}: Translation request for {1} at 0x{2:X}: failure on window {3}, out of range, window ends at 0x{4:X}", + mmuName, accessType, faultAddress, virtualWindowIndex, windowEnd); + } + else if((windowPrivilege & (uint)accessType) == 0) + { + this.Log(LogLevel.Error, "{0}: Translation request for {1} at 0x{2:X}: failure on window {3}, no permission, window permissions are 0x{4:X}", + mmuName, accessType, faultAddress, virtualWindowIndex, windowPrivilege); + } + else + { + this.Log(LogLevel.Error, "{0}: Translation request for {1} at 0x{2:X}: failed for unknown reason. This should not occur", + mmuName, accessType, faultAddress); + } + } + } + public GPIO HostReqIRQ { get; } public GPIO FinishIRQ { get; } public GPIO InstructionFaultIRQ { get; } @@ -318,7 +442,9 @@ private Mode mode; private readonly Machine Machine; private readonly KelvinRiscV32 Core; - private readonly ulong memoryRangeStart; + private readonly ExternalMmuBase Mmu; + private readonly ulong tcmSize; + private readonly ulong tcmRangeStart; // Length of register space. public long Size => 0x1000;