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