1 // 2 // Copyright (c) 2010-2025 Antmicro 3 // 4 // This file is licensed under the MIT License. 5 // Full license text is available in 'licenses/MIT.txt'. 6 // 7 using System; 8 using System.Collections.Generic; 9 using Antmicro.Renode.Core; 10 using Antmicro.Renode.Core.Structure; 11 using Antmicro.Renode.Exceptions; 12 using Antmicro.Renode.Logging; 13 using Antmicro.Renode.Utilities.Binding; 14 using Antmicro.Renode.Peripherals.Timers; 15 using Antmicro.Renode.Peripherals.IRQControllers; 16 using Antmicro.Renode.Debugging; 17 18 using Endianess = ELFSharp.ELF.Endianess; 19 20 namespace Antmicro.Renode.Peripherals.CPU 21 { 22 public partial class ARMv8R : BaseARMv8, IARMSingleSecurityStateCPU, IPeripheralRegister<ARM_GenericTimer, NullRegistrationPoint> 23 { ARMv8R(string cpuType, IMachine machine, ARM_GenericInterruptController genericInterruptController, uint cpuId = 0, Endianess endianness = Endianess.LittleEndian, uint mpuRegionsCount = 16, ulong defaultHVBARValue = 0, ulong defaultVBARValue = 0, uint mpuHyperRegionsCount = 16)24 public ARMv8R(string cpuType, IMachine machine, ARM_GenericInterruptController genericInterruptController, uint cpuId = 0, Endianess endianness = Endianess.LittleEndian, uint mpuRegionsCount = 16, ulong defaultHVBARValue = 0, ulong defaultVBARValue = 0, uint mpuHyperRegionsCount = 16) 25 : base(cpuId, cpuType, machine, endianness) 26 { 27 Affinity = new Affinity(cpuId); 28 this.defaultHVBARValue = defaultHVBARValue; 29 this.defaultVBARValue = defaultVBARValue; 30 31 gic = genericInterruptController; 32 try 33 { 34 gic.AttachCPU(this); 35 } 36 catch(Exception e) 37 { 38 throw new ConstructionException($"Failed to attach CPU to Generic Interrupt Controller: {e.Message}", e); 39 } 40 TlibSetMpuRegionsCount(mpuRegionsCount, mpuHyperRegionsCount); 41 TlibSetGicCpuRegisterInterfaceVersion(gic.ArchitectureVersionAtLeast3 ? GICCPUInterfaceVersion.Version30Or40 : GICCPUInterfaceVersion.None); 42 Reset(); 43 } 44 Reset()45 public override void Reset() 46 { 47 base.Reset(); 48 SetSystemRegisterValue("hvbar", defaultHVBARValue); 49 SetSystemRegisterValue("vbar", defaultVBARValue); 50 foreach(var config in defaultTCMConfiguration) 51 { 52 RegisterTCMRegion(config); 53 } 54 } 55 GetSystemRegisterValue(string name)56 public ulong GetSystemRegisterValue(string name) 57 { 58 ValidateSystemRegisterAccess(name, isWrite: false); 59 60 return TlibGetSystemRegister(name, 1u /* log_unhandled_access: true */); 61 } 62 SetAvailableExceptionLevels(bool el2Enabled, bool el3Enabled)63 public void SetAvailableExceptionLevels(bool el2Enabled, bool el3Enabled) 64 { 65 if(started) 66 { 67 throw new RecoverableException("Available Exception Levels can only be set before starting the simulation."); 68 } 69 70 var returnValue = TlibSetAvailableEls(el2Enabled ? 1u : 0u, el3Enabled ? 1u : 0u); 71 switch((SetAvailableElsReturnValue)returnValue) 72 { 73 case SetAvailableElsReturnValue.Success: 74 return; 75 case SetAvailableElsReturnValue.EL2OrEL3EnablingFailed: 76 throw new RecoverableException($"The '{Model}' core doesn't support all the enabled Exception Levels."); 77 // It should never be returned if 'started' is false. 78 case SetAvailableElsReturnValue.SimulationAlreadyStarted: 79 default: 80 throw new ArgumentException("Invalid TlibSetAvailableEls return value!"); 81 } 82 } 83 SetSystemRegisterValue(string name, ulong value)84 public void SetSystemRegisterValue(string name, ulong value) 85 { 86 ValidateSystemRegisterAccess(name, isWrite: true); 87 88 TlibSetSystemRegister(name, value, 1u /* log_unhandled_access: true */); 89 } 90 Register(ARM_GenericTimer peripheral, NullRegistrationPoint registrationPoint)91 public void Register(ARM_GenericTimer peripheral, NullRegistrationPoint registrationPoint) 92 { 93 if(timer != null) 94 { 95 throw new RegistrationException("A generic timer is already registered."); 96 } 97 timer = peripheral; 98 machine.RegisterAsAChildOf(this, peripheral, registrationPoint); 99 } 100 Unregister(ARM_GenericTimer peripheral)101 public void Unregister(ARM_GenericTimer peripheral) 102 { 103 timer = null; 104 machine.UnregisterAsAChildOf(this, peripheral); 105 } 106 107 public override string Architecture { get { return "arm64"; } } 108 109 public override string GDBArchitecture { get { return "arm"; } } 110 111 public override List<GDBFeatureDescriptor> GDBFeatures 112 { 113 get 114 { 115 var features = new List<GDBFeatureDescriptor>(); 116 117 var coreFeature = new GDBFeatureDescriptor("org.gnu.gdb.arm.core"); 118 for(var index = 0u; index <= 12; index++) 119 { 120 var cpuRegisterIdx = (uint)ARMv8RRegisters.R0 + index; 121 coreFeature.Registers.Add(new GDBRegisterDescriptor(cpuRegisterIdx, 32, $"r{index}", "uint32", "general")); 122 } 123 coreFeature.Registers.Add(new GDBRegisterDescriptor((uint)ARMv8RRegisters.R13, 32, "sp", "data_ptr", "general")); 124 coreFeature.Registers.Add(new GDBRegisterDescriptor((uint)ARMv8RRegisters.R14, 32, "lr", "code_ptr", "general")); 125 coreFeature.Registers.Add(new GDBRegisterDescriptor((uint)ARMv8RRegisters.R15, 32, "pc", "code_ptr", "general")); 126 coreFeature.Registers.Add(new GDBRegisterDescriptor((uint)ARMv8RRegisters.CPSR, 32, "cpsr", "uint32", "general")); 127 features.Add(coreFeature); 128 129 return features; 130 } 131 } 132 RegisterTCMRegion(IMemory memory, uint regionIndex)133 public void RegisterTCMRegion(IMemory memory, uint regionIndex) 134 { 135 if(!machine.IsPaused) 136 { 137 throw new RecoverableException("Registering TCM regions might only take place on paused machine"); 138 } 139 if(!TryRegisterTCMRegion(memory, regionIndex)) 140 { 141 this.Log(LogLevel.Error, "Attempted to register a TCM region #{0}, but {1} is not registered for this CPU.", regionIndex, machine.GetLocalName(memory)); 142 } 143 } 144 RegisterTCMRegion(TCMConfiguration config)145 private void RegisterTCMRegion(TCMConfiguration config) 146 { 147 try 148 { 149 TlibRegisterTcmRegion(config.Address, config.Size, config.RegionIndex); 150 } 151 catch(Exception e) 152 { 153 throw new RecoverableException(e); 154 } 155 } 156 TryRegisterTCMRegion(IMemory memory, uint regionIndex)157 private bool TryRegisterTCMRegion(IMemory memory, uint regionIndex) 158 { 159 ulong address; 160 if(!TCMConfiguration.TryFindRegistrationAddress(machine.SystemBus, this, memory, out address)) 161 { 162 return false; 163 } 164 165 var config = new TCMConfiguration(checked((uint)address), checked((ulong)memory.Size), regionIndex); 166 RegisterTCMRegion(config); 167 defaultTCMConfiguration.Add(config); 168 169 return true; 170 } 171 172 public ExceptionLevel ExceptionLevel => exceptionLevel; 173 // ARMv8R AArch32 cores always execute in NonSecure mode ("Arm Architecture Reference Manual Supplement Armv8, for the Armv8-R AArch32 architecture profile" - A1.3.1) 174 // ARMv8R AArch64 cores always execute in Secure mode ("Arm Architecture Reference Manual Supplement Armv8, for R-profile AArch64 architecture" - C1.11 and A1.3) 175 // since at this moment we only have AArch32 core supporting this ISA, let's lock it in NonSecure state 176 public SecurityState SecurityState => SecurityState.NonSecure; 177 178 public bool TrapGeneralExceptions => (GetSystemRegisterValue("hcr") & (1 << 27)) != 0; 179 public bool FIQMaskOverride => (GetSystemRegisterValue("hcr") & 0b01000) != 0 || TrapGeneralExceptions; 180 public bool IRQMaskOverride => (GetSystemRegisterValue("hcr") & 0b10000) != 0 || TrapGeneralExceptions; 181 182 public Affinity Affinity { get; } 183 DecodeInterrupt(int number)184 protected override Interrupt DecodeInterrupt(int number) 185 { 186 switch((InterruptSignalType)number) 187 { 188 case InterruptSignalType.IRQ: 189 return Interrupt.Hard; 190 case InterruptSignalType.FIQ: 191 return Interrupt.TargetExternal1; 192 case InterruptSignalType.vIRQ: 193 return Interrupt.TargetExternal2; 194 case InterruptSignalType.vFIQ: 195 return Interrupt.TargetExternal3; 196 default: 197 this.Log(LogLevel.Error, "Unexpected interrupt type for IRQ#{0}", number); 198 throw InvalidInterruptNumberException; 199 } 200 } 201 202 [Export] ReadSystemRegisterInterruptCPUInterface(uint offset)203 protected ulong ReadSystemRegisterInterruptCPUInterface(uint offset) 204 { 205 return gic.ReadSystemRegisterCPUInterface(offset); 206 } 207 208 [Export] WriteSystemRegisterInterruptCPUInterface(uint offset, ulong value)209 protected void WriteSystemRegisterInterruptCPUInterface(uint offset, ulong value) 210 { 211 gic.WriteSystemRegisterCPUInterface(offset, value); 212 } 213 214 [Export] ReadSystemRegisterGenericTimer64(uint offset)215 protected ulong ReadSystemRegisterGenericTimer64(uint offset) 216 { 217 if(timer == null) 218 { 219 this.Log(LogLevel.Error, "Trying to read a 64-bit register of the ARM Generic Timer, but the timer was not found."); 220 return 0; 221 } 222 223 return timer.ReadQuadWordRegisterAArch32(offset); 224 } 225 226 [Export] ReadSystemRegisterGenericTimer32(uint offset)227 protected uint ReadSystemRegisterGenericTimer32(uint offset) 228 { 229 if(timer == null) 230 { 231 this.Log(LogLevel.Error, "Trying to read a 32-bit register of the ARM Generic Timer, but the timer was not found."); 232 return 0; 233 } 234 235 return timer.ReadDoubleWordRegisterAArch32(offset); 236 } 237 238 [Export] WriteSystemRegisterGenericTimer64(uint offset, ulong value)239 protected void WriteSystemRegisterGenericTimer64(uint offset, ulong value) 240 { 241 if(timer == null) 242 { 243 this.Log(LogLevel.Error, "Trying to write a 64-bit register of the ARM Generic Timer, but the timer was not found."); 244 return; 245 } 246 247 timer.WriteQuadWordRegisterAArch32(offset, value); 248 } 249 250 [Export] WriteSystemRegisterGenericTimer32(uint offset, uint value)251 protected void WriteSystemRegisterGenericTimer32(uint offset, uint value) 252 { 253 if(timer == null) 254 { 255 this.Log(LogLevel.Error, "Trying to write a 32-bit register of the ARM Generic Timer, but the timer was not found."); 256 return; 257 } 258 259 timer.WriteDoubleWordRegisterAArch32(offset, value); 260 } 261 262 [Export] OnExecutionModeChanged(uint el, uint isSecure)263 private void OnExecutionModeChanged(uint el, uint isSecure) 264 { 265 exceptionLevel = (ExceptionLevel)el; 266 // ARMv8R cores cannot change security state (Architecture Manual mandates it) 267 DebugHelper.Assert((isSecure != 0 ? SecurityState.Secure : SecurityState.NonSecure) == SecurityState, $"{nameof(ARMv8R)} should not change its Security State."); 268 } 269 ValidateSystemRegisterAccess(string name, bool isWrite)270 private void ValidateSystemRegisterAccess(string name, bool isWrite) 271 { 272 if(name.ToLower().Equals("nzcv")) 273 { 274 throw new RecoverableException("Use '<cpu_name> PSTATE' to access NZCV."); 275 } 276 277 switch((SystemRegisterCheckReturnValue)TlibCheckSystemRegisterAccess(name, isWrite ? 1u : 0u)) 278 { 279 case SystemRegisterCheckReturnValue.AccessValid: 280 return; 281 case SystemRegisterCheckReturnValue.AccessorNotFound: 282 var accessName = isWrite ? "Writing" : "Reading"; 283 throw new RecoverableException($"{accessName} the {name} register isn't supported."); 284 case SystemRegisterCheckReturnValue.RegisterNotFound: 285 throw new RecoverableException("No such register."); 286 default: 287 throw new ArgumentException("Invalid TlibCheckSystemRegisterAccess return value!"); 288 } 289 } 290 291 private ExceptionLevel exceptionLevel; 292 private ARM_GenericTimer timer; 293 294 private readonly ARM_GenericInterruptController gic; 295 private readonly ulong defaultHVBARValue; 296 private readonly ulong defaultVBARValue; 297 298 private readonly List<TCMConfiguration> defaultTCMConfiguration = new List<TCMConfiguration>(); 299 300 // These '*ReturnValue' enums have to be in sync with their counterparts in 'tlib/arch/arm64/arch_exports.c'. 301 private enum SetAvailableElsReturnValue 302 { 303 SimulationAlreadyStarted = 1, 304 EL2OrEL3EnablingFailed = 2, 305 Success = 3, 306 } 307 308 private enum SystemRegisterCheckReturnValue 309 { 310 RegisterNotFound = 1, 311 AccessorNotFound = 2, 312 AccessValid = 3, 313 } 314 315 #pragma warning disable 649 316 [Import] 317 private Action<GICCPUInterfaceVersion> TlibSetGicCpuRegisterInterfaceVersion; 318 319 [Import] 320 private Func<string, uint, uint> TlibCheckSystemRegisterAccess; 321 322 [Import] 323 // The arguments are: char *name, bool log_unhandled_access. 324 private Func<string, uint, ulong> TlibGetSystemRegister; 325 326 [Import] 327 private Func<uint, uint, uint> TlibSetAvailableEls; 328 329 [Import] 330 // The arguments are: char *name, uint64_t value, bool log_unhandled_access. 331 private Action<string, ulong, uint> TlibSetSystemRegister; 332 333 [Import] 334 private Action<uint, uint> TlibSetMpuRegionsCount; 335 336 [Import] 337 private Action<uint, ulong, ulong> TlibRegisterTcmRegion; 338 #pragma warning restore 649 339 } 340 } 341