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