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 System.Linq; 10 using System.Runtime.InteropServices; 11 using Antmicro.Renode.Core; 12 using Antmicro.Renode.Core.Structure; 13 using Antmicro.Renode.Debugging; 14 using Antmicro.Renode.Exceptions; 15 using Antmicro.Renode.Logging; 16 using Antmicro.Renode.Peripherals.Timers; 17 using Antmicro.Renode.Peripherals.IRQControllers; 18 using Antmicro.Renode.Utilities.Binding; 19 20 using Endianess = ELFSharp.ELF.Endianess; 21 22 namespace Antmicro.Renode.Peripherals.CPU 23 { 24 public partial class ARMv8A : BaseARMv8, IARMTwoSecurityStatesCPU, IPeripheralRegister<ARM_GenericTimer, NullRegistrationPoint>, ICPUWithAArch64Support 25 { ARMv8A(IMachine machine, string cpuType, ARM_GenericInterruptController genericInterruptController, uint cpuId = 0, Endianess endianness = Endianess.LittleEndian)26 public ARMv8A(IMachine machine, string cpuType, ARM_GenericInterruptController genericInterruptController, uint cpuId = 0, Endianess endianness = Endianess.LittleEndian) 27 : base(cpuId, cpuType, machine, endianness) 28 { 29 Affinity = new Affinity(cpuId); 30 gic = genericInterruptController; 31 try 32 { 33 gic.AttachCPU(this); 34 } 35 catch(Exception e) 36 { 37 throw new ConstructionException($"Failed to attach CPU to Generic Interrupt Controller: {e.Message}", e); 38 } 39 TlibSetGicCpuRegisterInterfaceVersion(gic.ArchitectureVersionAtLeast3 ? GICCPUInterfaceVersion.Version30Or40 : GICCPUInterfaceVersion.None); 40 Reset(); 41 HasSingleSecurityState = TlibHasEl3() == 0; 42 } 43 GetAllSystemRegisterValues()44 public string[,] GetAllSystemRegisterValues() 45 { 46 var table = new Renode.Utilities.Table().AddRow("Name", "Value"); 47 foreach(var indexSystemRegisterPair in SystemRegistersDictionary) 48 { 49 // Value is 0 if the attempt is unsuccessful so we don't need to care about the result. 50 _ = TryGetSystemRegisterValue(indexSystemRegisterPair.Key, out var value, logUnhandledAccess: false); 51 table.AddRow(indexSystemRegisterPair.Value.Name, $"0x{value:X}"); 52 } 53 return table.ToArray(); 54 } 55 GetAtomicExceptionLevelAndSecurityState(out ExceptionLevel exceptionLevel, out SecurityState securityState)56 public void GetAtomicExceptionLevelAndSecurityState(out ExceptionLevel exceptionLevel, out SecurityState securityState) 57 { 58 lock(elAndSecurityLock) 59 { 60 exceptionLevel = this.exceptionLevel; 61 securityState = this.securityState; 62 } 63 } 64 GetSystemRegisterValue(string name)65 public ulong GetSystemRegisterValue(string name) 66 { 67 ValidateSystemRegisterAccess(name, isWrite: false); 68 69 return TlibGetSystemRegister(name, 1u /* log_unhandled_access: true */); 70 } 71 SetAvailableExceptionLevels(bool el2Enabled, bool el3Enabled)72 public void SetAvailableExceptionLevels(bool el2Enabled, bool el3Enabled) 73 { 74 if(started) 75 { 76 throw new RecoverableException("Available Exception Levels can only be set before starting the simulation."); 77 } 78 79 var returnValue = TlibSetAvailableEls(el2Enabled ? 1u : 0u, el3Enabled ? 1u : 0u); 80 switch((SetAvailableElsReturnValue)returnValue) 81 { 82 case SetAvailableElsReturnValue.Success: 83 HasSingleSecurityState = el3Enabled; 84 return; 85 case SetAvailableElsReturnValue.EL2OrEL3EnablingFailed: 86 throw new RecoverableException($"The '{Model}' core doesn't support all the enabled Exception Levels."); 87 // It should never be returned if 'started' is false. 88 case SetAvailableElsReturnValue.SimulationAlreadyStarted: 89 default: 90 throw new ArgumentException("Invalid TlibSetAvailableEls return value!"); 91 } 92 } 93 SetSystemRegisterValue(string name, ulong value)94 public void SetSystemRegisterValue(string name, ulong value) 95 { 96 ValidateSystemRegisterAccess(name, isWrite: true); 97 98 TlibSetSystemRegister(name, value, 1u /* log_unhandled_access: true */); 99 } 100 Register(ARM_GenericTimer peripheral, NullRegistrationPoint registrationPoint)101 public void Register(ARM_GenericTimer peripheral, NullRegistrationPoint registrationPoint) 102 { 103 if(timer != null) 104 { 105 throw new RegistrationException("A generic timer is already registered."); 106 } 107 timer = peripheral; 108 machine.RegisterAsAChildOf(this, peripheral, registrationPoint); 109 } 110 TryGetSystemRegisterValue(AArch64SystemRegisterEncoding encoding, out ulong value)111 public bool TryGetSystemRegisterValue(AArch64SystemRegisterEncoding encoding, out ulong value) 112 { 113 value = 0; 114 return TryGetSystemRegisterIndex(encoding, out var systemRegisterIndex) 115 && TryGetSystemRegisterValue(systemRegisterIndex, out value, logUnhandledAccess: false); 116 } 117 TrySetSystemRegisterValue(AArch64SystemRegisterEncoding encoding, ulong value)118 public bool TrySetSystemRegisterValue(AArch64SystemRegisterEncoding encoding, ulong value) 119 { 120 return TryGetSystemRegisterIndex(encoding, out var systemRegisterIndex) 121 && TrySetSystemRegisterValue(systemRegisterIndex, value); 122 } 123 Unregister(ARM_GenericTimer peripheral)124 public void Unregister(ARM_GenericTimer peripheral) 125 { 126 timer = null; 127 machine.UnregisterAsAChildOf(this, peripheral); 128 } 129 130 public override string Architecture { get { return "arm64"; } } 131 132 public override string GDBArchitecture { get { return "aarch64"; } } 133 134 public override List<GDBFeatureDescriptor> GDBFeatures 135 { 136 get 137 { 138 var features = new List<GDBFeatureDescriptor>(); 139 140 var coreFeature = new GDBFeatureDescriptor("org.gnu.gdb.aarch64.core"); 141 for(var index = 0u; index <= 30; index++) 142 { 143 coreFeature.Registers.Add(new GDBRegisterDescriptor(index, 64, $"x{index}", "uint64", "general")); 144 } 145 coreFeature.Registers.Add(new GDBRegisterDescriptor((uint)ARMv8ARegisters.SP, 64, "sp", "data_ptr", "general")); 146 coreFeature.Registers.Add(new GDBRegisterDescriptor((uint)ARMv8ARegisters.PC, 64, "pc", "code_ptr", "general")); 147 // CPSR name is in line with GDB's 'G.5.1 AArch64 Features' manual page though it should be named PSTATE. 148 coreFeature.Registers.Add(new GDBRegisterDescriptor((uint)ARMv8ARegisters.PSTATE, 32, "cpsr", "uint32", "general")); 149 features.Add(coreFeature); 150 151 var systemRegistersFeature = new GDBFeatureDescriptor("org.renode.gdb.aarch64.sysregs"); 152 foreach(var indexSystemRegisterPair in SystemRegistersDictionary) 153 { 154 systemRegistersFeature.Registers.Add(new GDBRegisterDescriptor(indexSystemRegisterPair.Key, SystemRegistersWidth, indexSystemRegisterPair.Value.Name, "uint64")); 155 } 156 features.Add(systemRegistersFeature); 157 158 /* 159 * TODO 160 * The ‘org.gnu.gdb.aarch64.fpu’ feature is optional. If present, it should contain registers ‘v0’ through ‘v31’, ‘fpsr’, and ‘fpcr’. 161 * The ‘org.gnu.gdb.aarch64.sve’ feature is optional. If present, it should contain registers ‘z0’ through ‘z31’, ‘p0’ through ‘p15’, ‘ffr’ and ‘vg’. 162 * The ‘org.gnu.gdb.aarch64.pauth’ feature is optional. If present, it should contain registers ‘pauth_dmask’ and ‘pauth_cmask’. 163 */ 164 165 return features; 166 } 167 } 168 169 public ExceptionLevel ExceptionLevel 170 { 171 get 172 { 173 lock(elAndSecurityLock) 174 { 175 return exceptionLevel; 176 } 177 } 178 set => TlibSetCurrentEl((uint)value); 179 } 180 181 public SecurityState SecurityState 182 { 183 get 184 { 185 lock(elAndSecurityLock) 186 { 187 return securityState; 188 } 189 } 190 } 191 192 public bool FIQMaskOverride => (GetSystemRegisterValue("hcr_el2") & 0b01000) != 0; 193 public bool IRQMaskOverride => (GetSystemRegisterValue("hcr_el2") & 0b10000) != 0; 194 195 public Affinity Affinity { get; } 196 public bool IsEL3UsingAArch32State => false; // ARM8vA currently supports only AArch64 execution 197 public bool HasSingleSecurityState { get; private set; } 198 199 public event Action<ExceptionLevel, SecurityState> ExecutionModeChanged; 200 DecodeInterrupt(int number)201 protected override Interrupt DecodeInterrupt(int number) 202 { 203 switch((InterruptSignalType)number) 204 { 205 case InterruptSignalType.IRQ: 206 return Interrupt.Hard; 207 case InterruptSignalType.FIQ: 208 return Interrupt.TargetExternal1; 209 case InterruptSignalType.vIRQ: 210 return Interrupt.TargetExternal2; 211 case InterruptSignalType.vFIQ: 212 return Interrupt.TargetExternal3; 213 default: 214 throw InvalidInterruptNumberException; 215 } 216 } 217 GetNonMappedRegisters()218 protected IEnumerable<CPURegister> GetNonMappedRegisters() 219 { 220 return SystemRegistersDictionary.Keys.Select(index => new CPURegister((int)index, SystemRegistersWidth, false, false)); 221 } 222 223 [Export] ReadSystemRegisterInterruptCPUInterface(uint offset)224 protected ulong ReadSystemRegisterInterruptCPUInterface(uint offset) 225 { 226 return gic.ReadSystemRegisterCPUInterface(offset); 227 } 228 229 [Export] WriteSystemRegisterInterruptCPUInterface(uint offset, ulong value)230 protected void WriteSystemRegisterInterruptCPUInterface(uint offset, ulong value) 231 { 232 gic.WriteSystemRegisterCPUInterface(offset, value); 233 } 234 235 236 [Export] ReadSystemRegisterGenericTimer32(uint offset)237 protected uint ReadSystemRegisterGenericTimer32(uint offset) 238 { 239 this.Log(LogLevel.Error, "Reading 32-bit registers of the ARM Generic Timer is not allowed in 64bit version of the CPU"); 240 return 0; 241 } 242 243 [Export] WriteSystemRegisterGenericTimer32(uint offset, uint value)244 protected void WriteSystemRegisterGenericTimer32(uint offset, uint value) 245 { 246 this.Log(LogLevel.Error, "Writing 32-bit registers of the ARM Generic Timer is not allowed in 64bit version of the CPU"); 247 return; 248 249 } 250 251 [Export] ReadSystemRegisterGenericTimer64(uint offset)252 protected ulong ReadSystemRegisterGenericTimer64(uint offset) 253 { 254 if(timer == null) 255 { 256 this.Log(LogLevel.Error, "Trying to read a register of the ARM Generic Timer, but the timer was not found."); 257 return 0; 258 } 259 return timer.ReadRegisterAArch64(offset); 260 } 261 262 [Export] WriteSystemRegisterGenericTimer64(uint offset, ulong value)263 protected void WriteSystemRegisterGenericTimer64(uint offset, ulong value) 264 { 265 if(timer == null) 266 { 267 this.Log(LogLevel.Error, "Trying to write a register of the ARM Generic Timer, but the timer was not found."); 268 return; 269 } 270 timer.WriteRegisterAArch64(offset, value); 271 } 272 TryGetNonMappedRegister(int index, out RegisterValue value)273 protected bool TryGetNonMappedRegister(int index, out RegisterValue value) 274 { 275 // This method will be mostly used by GDB so let's prevent unhandled access logs. 276 // Otherwise, 'info all-registers' generates a lot of warnings. 277 var result = TryGetSystemRegisterValue((uint)index, out var ulongValue, logUnhandledAccess: false); 278 279 value = RegisterValue.Create(ulongValue, SystemRegistersWidth); 280 return result; 281 } 282 TrySetNonMappedRegister(int index, RegisterValue value)283 protected bool TrySetNonMappedRegister(int index, RegisterValue value) 284 { 285 return TrySetSystemRegisterValue((uint)index, value); 286 } 287 IsGICOrGenericTimerSystemRegister(SystemRegister systemRegister)288 private bool IsGICOrGenericTimerSystemRegister(SystemRegister systemRegister) 289 { 290 return TlibIsGicOrGenericTimerSystemRegister(systemRegister.Name) == 1u; 291 } 292 293 [Export] OnExecutionModeChanged(uint el, uint isSecure)294 private void OnExecutionModeChanged(uint el, uint isSecure) 295 { 296 lock(elAndSecurityLock) 297 { 298 exceptionLevel = (ExceptionLevel)el; 299 securityState = isSecure != 0 ? SecurityState.Secure : SecurityState.NonSecure; 300 } 301 ExecutionModeChanged?.Invoke(ExceptionLevel, SecurityState); 302 } 303 TryGetSystemRegisterIndex(AArch64SystemRegisterEncoding encoding, out uint index)304 private bool TryGetSystemRegisterIndex(AArch64SystemRegisterEncoding encoding, out uint index) 305 { 306 index = uint.MaxValue; 307 var matchingEntries = SystemRegistersDictionary.Where(entry => encoding.Equals(entry.Value.Encoding)); 308 DebugHelper.Assert(matchingEntries.Count() <= 1); 309 310 if(!matchingEntries.Any()) 311 { 312 this.Log(LogLevel.Warning, "Unknown AArch64 system register encoding: {0}", encoding); 313 return false; 314 } 315 index = matchingEntries.Single().Key; 316 return true; 317 } 318 TryGetSystemRegisterValue(uint index, out ulong value, bool logUnhandledAccess)319 private bool TryGetSystemRegisterValue(uint index, out ulong value, bool logUnhandledAccess) 320 { 321 if(SystemRegistersDictionary.TryGetValue(index, out var systemRegister)) 322 { 323 // ValidateSystemRegisterAccess isn't used because most of its checks aren't needed. 324 // The register must exist at this point cause it's in the dictionary built based on tlib 325 // and we don't really care about the invalid access type error for unreadable registers. 326 value = TlibGetSystemRegister(systemRegister.Name, logUnhandledAccess ? 1u : 0u); 327 return true; 328 } 329 value = 0; 330 return false; 331 } 332 TrySetSystemRegisterValue(uint index, ulong value)333 private bool TrySetSystemRegisterValue(uint index, ulong value) 334 { 335 if(SystemRegistersDictionary.TryGetValue(index, out var systemRegister)) 336 { 337 ValidateSystemRegisterAccess(systemRegister.Name, isWrite: true); 338 TlibSetSystemRegister(systemRegister.Name, value, 1u /* log_unhandled_access: true */); 339 return true; 340 } 341 return false; 342 } 343 ValidateSystemRegisterAccess(string name, bool isWrite)344 private void ValidateSystemRegisterAccess(string name, bool isWrite) 345 { 346 switch((SystemRegisterCheckReturnValue)TlibCheckSystemRegisterAccess(name, isWrite ? 1u : 0u)) 347 { 348 case SystemRegisterCheckReturnValue.AccessValid: 349 return; 350 case SystemRegisterCheckReturnValue.AccessorNotFound: 351 var accessName = isWrite ? "Writing" : "Reading"; 352 throw new RecoverableException($"{accessName} the {name} register isn't supported."); 353 case SystemRegisterCheckReturnValue.RegisterNotFound: 354 throw new RecoverableException($"No such register: {name}."); 355 default: 356 throw new ArgumentException("Invalid TlibCheckSystemRegisterAccess return value!"); 357 } 358 } 359 360 private Dictionary<uint, SystemRegister> SystemRegistersDictionary 361 { 362 get 363 { 364 if(systemRegisters == null) 365 { 366 systemRegisters = new Dictionary<uint, SystemRegister>(); 367 368 var array = IntPtr.Zero; 369 var arrayPointer = Marshal.AllocHGlobal(IntPtr.Size); 370 try 371 { 372 var count = TlibCreateSystemRegistersArray(arrayPointer); 373 if(count == 0) 374 { 375 return systemRegisters; 376 } 377 array = Marshal.ReadIntPtr(arrayPointer); 378 379 var ArmCpRegInfoPointersArray = new IntPtr[count]; 380 Marshal.Copy(array, ArmCpRegInfoPointersArray, 0, (int)count); 381 382 var lastRegisterIndex = Enum.GetValues(typeof(ARMv8ARegisters)).Cast<uint>().Max(); 383 systemRegisters = ArmCpRegInfoPointersArray 384 .Select(armCpRegInfoPointer => ARMCPRegInfo.FromIntPtr(armCpRegInfoPointer).ToSystemRegister()) 385 // Currently, GIC and Generic Timer system registers can only be accessed by software. 386 // Let's not add them to the dictionary so that GDB won't fail on read until it's fixed. 387 .Where(systemRegister => !IsGICOrGenericTimerSystemRegister(systemRegister)) 388 .OrderBy(systemRegister => systemRegister.Name) 389 .ToDictionary(_ => ++lastRegisterIndex); 390 } 391 finally 392 { 393 if(array != IntPtr.Zero) 394 { 395 Free(array); 396 } 397 Marshal.FreeHGlobal(arrayPointer); 398 } 399 } 400 return systemRegisters; 401 } 402 } 403 404 private ExceptionLevel exceptionLevel; 405 private SecurityState securityState; 406 private Dictionary<uint, SystemRegister> systemRegisters; 407 private ARM_GenericTimer timer; 408 409 private readonly object elAndSecurityLock = new object(); 410 private readonly ARM_GenericInterruptController gic; 411 412 private const int SystemRegistersWidth = 64; 413 414 [StructLayout(LayoutKind.Sequential)] 415 private struct ARMCPRegInfo 416 { FromIntPtrAntmicro.Renode.Peripherals.CPU.ARMv8A.ARMCPRegInfo417 public static ARMCPRegInfo FromIntPtr(IntPtr pointer) 418 { 419 return (ARMCPRegInfo)Marshal.PtrToStructure(pointer, typeof(ARMCPRegInfo)); 420 } 421 ToSystemRegisterAntmicro.Renode.Peripherals.CPU.ARMv8A.ARMCPRegInfo422 public SystemRegister ToSystemRegister() 423 { 424 return new SystemRegister 425 { 426 Name = Marshal.PtrToStringAnsi(Name), 427 Coprocessor = Coprocessor, 428 Type = Type, 429 Encoding = new AArch64SystemRegisterEncoding { Op0 = Op0, Op1 = Op1, Crn = Crn, Crm = Crm, Op2 = Op2 }, 430 }; 431 } 432 433 // These have to be in line with tlib/arch/arm_common/system_registers_common.h 434 public IntPtr Name; 435 public uint Coprocessor; 436 public uint Type; 437 438 public byte Op0; 439 public byte Op1; 440 public byte Crn; 441 public byte Crm; 442 public byte Op2; 443 444 public uint FieldOffset; 445 public ulong ResetValue; 446 public IntPtr AccessFunction; 447 public IntPtr ReadFunction; 448 public IntPtr WriteFunction; 449 public bool IsDynamic; 450 }; 451 452 private struct SystemRegister 453 { 454 public string Name; 455 public uint Coprocessor; 456 public AArch64SystemRegisterEncoding Encoding; 457 public uint Type; 458 } 459 460 // These '*ReturnValue' enums have to be in sync with their counterparts in 'tlib/arch/arm64/arch_exports.c'. 461 private enum SetAvailableElsReturnValue 462 { 463 SimulationAlreadyStarted = 1, 464 EL2OrEL3EnablingFailed = 2, 465 Success = 3, 466 } 467 468 private enum SystemRegisterCheckReturnValue 469 { 470 RegisterNotFound = 1, 471 AccessorNotFound = 2, 472 AccessValid = 3, 473 } 474 475 #pragma warning disable 649 476 [Import] 477 private Action<GICCPUInterfaceVersion> TlibSetGicCpuRegisterInterfaceVersion; 478 479 [Import] 480 private Func<string, uint, uint> TlibCheckSystemRegisterAccess; 481 482 [Import] 483 private Func<IntPtr, uint> TlibCreateSystemRegistersArray; 484 485 [Import] 486 private Func<string, uint> TlibIsGicOrGenericTimerSystemRegister; 487 488 [Import] 489 // The arguments are: char *name, bool log_unhandled_access. 490 private Func<string, uint, ulong> TlibGetSystemRegister; 491 492 [Import] 493 private Func<uint> TlibHasEl3; 494 495 [Import] 496 private Func<uint, uint, uint> TlibSetAvailableEls; 497 498 [Import] 499 private Action<uint> TlibSetCurrentEl; 500 501 [Import] 502 // The arguments are: char *name, uint64_t value, bool log_unhandled_access. 503 private Action<string, ulong, uint> TlibSetSystemRegister; 504 #pragma warning restore 649 505 } 506 } 507