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.Linq;
9 using Antmicro.Renode.Core;
10 using Antmicro.Renode.Logging;
11 using Antmicro.Renode.Debugging;
12 using Antmicro.Renode.Exceptions;
13 using Antmicro.Renode.Peripherals.Bus;
14 using Antmicro.Renode.Core.Structure.Registers;
15 using System.Collections.Generic;
16 using System.Collections.ObjectModel;
17 using Antmicro.Renode.Utilities;
18 using Antmicro.Renode.Peripherals.CPU;
19 using Antmicro.Renode.Peripherals.IRQControllers.ARM_GenericInterruptControllerModel;
20 
21 namespace Antmicro.Renode.Peripherals.IRQControllers
22 {
23     // NOTE: Memory mapped Virtual CPU Interface is currently not supported.
24     //       It can be accesses only through system registers.
25     public class ARM_GenericInterruptController : IARMCPUsConnectionsProvider, IBusPeripheral, ILocalGPIOReceiver, INumberedGPIOOutput, IIRQController
26     {
ARM_GenericInterruptController(IMachine machine, bool supportsTwoSecurityStates = true, ARM_GenericInterruptControllerVersion architectureVersion = ARM_GenericInterruptControllerVersion.Default, uint sharedPeripheralCount = 960)27         public ARM_GenericInterruptController(IMachine machine, bool supportsTwoSecurityStates = true, ARM_GenericInterruptControllerVersion architectureVersion = ARM_GenericInterruptControllerVersion.Default, uint sharedPeripheralCount = 960)
28         {
29             if(architectureVersion == ARM_GenericInterruptControllerVersion.GICv1)
30             {
31                 // On GICv1, `GICD_ITARGETSRn` supports up to 8 CPU targets, so we ignore higher affinity levels completely
32                 affinityToIdMask = 0xFF;
33             }
34             else
35             {
36                 affinityToIdMask = uint.MaxValue;
37             }
38             busController = machine.GetSystemBus(this);
39 
40             if(sharedPeripheralCount > InterruptsDecoder.MaximumSharedPeripheralCount)
41             {
42                 throw new ConstructionException($"The number of shared peripherals {sharedPeripheralCount} is larger than supported {(InterruptsDecoder.MaximumSharedPeripheralCount)}");
43             }
44 
45             // The behavior of the GIC doesn't directly depend on the supportsTwoSecurityState field
46             // The disabledSecurity field corresponds to the GICD_CTRL.DS flag described in the GICv3 Architecture Specification
47             // The GIC without support for two security states has disabled security
48             // Changing the disabledSecurity field affects the behavior and register map of a GIC
49             // Once security is disabled it's impossible to enable it
50             // So it is impossible to enable security for the GIC that doesn't support two security states
51             this.supportsTwoSecurityStates = supportsTwoSecurityStates;
52             if(architectureVersion == ARM_GenericInterruptControllerVersion.Default)
53             {
54                 this.Log(LogLevel.Warning, "GIC architecture version not explicitly set. Defaulting to v3");
55                 this.ArchitectureVersion = DefaultArchitectureVersion;
56             }
57             else
58             {
59                 this.ArchitectureVersion = architectureVersion;
60             }
61 
62             this.irqsDecoder = new InterruptsDecoder(sharedPeripheralCount, identifierBits: 10);
63 
64             var irqIds = InterruptId.GetRange(irqsDecoder.SharedPeripheralFirst, irqsDecoder.SharedPeripheralLast)
65                 .Concat(InterruptId.GetRange(irqsDecoder.ExtendedSharedPeripheralFirst, irqsDecoder.ExtendedSharedPeripheralLast));
66             sharedInterrupts = new ReadOnlyDictionary<InterruptId, SharedInterrupt>(irqIds.ToDictionary(id => id, id => new SharedInterrupt(id)));
67 
68             var groupTypes = new[]
69             {
70                 GroupType.Group0,
71                 GroupType.Group1NonSecure,
72                 GroupType.Group1Secure
73             };
74             groups = new ReadOnlyDictionary<GroupType, InterruptGroup>(groupTypes.ToDictionary(type => type, _ => new InterruptGroup()));
75 
76             supportedInterruptSignals = (InterruptSignalType[])Enum.GetValues(typeof(InterruptSignalType));
77             Connections = new ReadOnlyDictionary<int, IGPIO>(new Dictionary<int, IGPIO>());
78 
79             // Field layouts of some of the registers depend on the current security state
80             distributorRegistersSecureView = new DoubleWordRegisterCollection(this, BuildDistributorRegistersMapSecurityView(false, SecurityState.Secure));
81             distributorRegistersNonSecureView = new DoubleWordRegisterCollection(this, BuildDistributorRegistersMapSecurityView(false, SecurityState.NonSecure));
82             distributorRegistersDisabledSecurityView = new DoubleWordRegisterCollection(this, BuildDistributorRegistersMapSecurityView(true));
83             cpuInterfaceRegistersSecureView = new DoubleWordRegisterCollection(this, BuildCPUInterfaceRegistersMapSecurityView(false, SecurityState.Secure));
84             cpuInterfaceRegistersNonSecureView = new DoubleWordRegisterCollection(this, BuildCPUInterfaceRegistersMapSecurityView(false, SecurityState.NonSecure));
85             cpuInterfaceRegistersDisabledSecurityView = new DoubleWordRegisterCollection(this, BuildCPUInterfaceRegistersMapSecurityView(true));
86 
87             // The rest may behave differently for various security settings, but the layout of fields doesn't change
88             distributorDoubleWordRegisters = new DoubleWordRegisterCollection(this, BuildDistributorDoubleWordRegistersMap());
89             distributorQuadWordRegisters = new QuadWordRegisterCollection(this, BuildDistributorQuadWordRegistersMap());
90             cpuInterfaceRegisters = new DoubleWordRegisterCollection(this, BuildCPUInterfaceRegistersMap());
91             cpuInterfaceSystemRegisters = new QuadWordRegisterCollection(this, BuildCPUInterfaceSystemRegistersMap());
92 
93             Reset();
94         }
95 
Reset()96         public void Reset()
97         {
98             LockExecuteAndUpdate(() =>
99                 {
100                     ackControl = false;
101                     enableFIQ = ArchitectureVersionAtLeast3;
102                     disabledSecurity = false;
103                     affinityRoutingEnabledSecure = false;
104                     affinityRoutingEnabledNonSecure = false;
105                     foreach(var irq in sharedInterrupts.Values)
106                     {
107                         irq.Reset();
108                     }
109                     foreach(var group in groups.Values)
110                     {
111                         group.Reset();
112                     }
113                     foreach(var cpu in cpuEntries.Values)
114                     {
115                         cpu.Reset();
116                     }
117                 });
118 
119             distributorRegistersSecureView.Reset();
120             distributorRegistersNonSecureView.Reset();
121             distributorRegistersDisabledSecurityView.Reset();
122             distributorDoubleWordRegisters.Reset();
123             distributorQuadWordRegisters.Reset();
124             cpuInterfaceRegistersSecureView.Reset();
125             cpuInterfaceRegistersNonSecureView.Reset();
126             cpuInterfaceRegistersDisabledSecurityView.Reset();
127             cpuInterfaceRegisters.Reset();
128             cpuInterfaceSystemRegisters.Reset();
129         }
130 
AttachCPU(IARMSingleSecurityStateCPU cpu)131         public void AttachCPU(IARMSingleSecurityStateCPU cpu)
132         {
133             if(ArchitectureVersion == ARM_GenericInterruptControllerVersion.GICv1)
134             {
135                 var affinities = cpuEntries.Keys.Select(c => c.Affinity).Select(a => new Affinity(a.AllLevels & ~affinityToIdMask));
136                 var highAffinity = cpu.Affinity.AllLevels & ~affinityToIdMask;
137                 if(affinities.Any(a => highAffinity != a.AllLevels))
138                 {
139                     throw new RecoverableException($"Previously registered CPUs have Affinities above Aff0 different from {new Affinity(highAffinity)}."
140                         + $" This is illegal for {nameof(ARM_GenericInterruptControllerVersion.GICv1)}.");
141                 }
142             }
143             var processorNumber = GetProcessorNumber(cpu);
144             this.Log(LogLevel.Noisy, "Trying to attach CPU {0}", cpu.Affinity);
145             if(TryGetCPUEntry(processorNumber, out var existingCPUEntry))
146             {
147                 throw new RecoverableException($"The CPU with the Processor Number {processorNumber} already exists.");
148             }
149             if(cpuEntries.Values.Any(entry => entry.Affinity.AllLevels == cpu.Affinity.AllLevels))
150             {
151                 throw new RecoverableException($"The CPU with the affinity {cpu.Affinity} already exists.");
152             }
153 
154             var cpuMappedConnections = supportedInterruptSignals.ToDictionary(type => type, _ => (IGPIO)new GPIO());
155             CPUEntry cpuEntry = null;
156             var cpuTwoSecurityStates = cpu as IARMTwoSecurityStatesCPU;
157             if(cpuTwoSecurityStates != null)
158             {
159                 cpuEntry = new CPUEntryWithTwoSecurityStates(this, cpuTwoSecurityStates, groups.Keys, cpuMappedConnections);
160                 cpuTwoSecurityStates.ExecutionModeChanged += (_, __) => OnExecutionModeChanged(cpuEntry);
161             }
162             else
163             {
164                 if(this.supportsTwoSecurityStates)
165                 {
166                     cpu.Log(LogLevel.Info, "CPU is attached to GIC supporting Security Extensions."
167                             + " This CPU doesn't implement Security Extensions so all its GIC accesses will be {0}. GIC's '{1}'"
168                             + " constructor argument can be set to 'false' to create GIC without support for Security Extensions.",
169                             cpu.SecurityState, nameof(supportsTwoSecurityStates));
170                 }
171                 cpuEntry = new CPUEntry(this, cpu, groups.Keys, cpuMappedConnections);
172             }
173             cpuEntry.PrivateInterruptChanged += OnPrivateInterrupt;
174 
175             // The convention of connecting interrupt signals can be found near the InterruptSignalType definition
176             var firstGPIO = (int)processorNumber * supportedInterruptSignals.Length;
177             var cpuConnections = cpuMappedConnections.Select(x => new KeyValuePair<int, IGPIO>(firstGPIO + (int)x.Key, x.Value));
178             Connections = new ReadOnlyDictionary<int, IGPIO>(Connections.Concat(cpuConnections).ToDictionary(x => x.Key, x => x.Value));
179 
180             // SGIs require an information about a requesting CPU when Affinity Routing is disabled.
181             foreach(var requester in cpuEntries.Values.Where(req => req.ProcessorNumber < CPUsCountLegacySupport))
182             {
183                 cpuEntry.RegisterLegacySGIRequester(requester);
184             }
185 
186             cpusByProcessorNumberCache.Add(processorNumber, cpu);
187             cpuEntries.Add(cpu, cpuEntry);
188             CPUAttached?.Invoke(cpu);
189 
190             if(processorNumber < CPUsCountLegacySupport)
191             {
192                 legacyCpusAttachedMask |= cpuEntry.TargetFieldFlag;
193                 // The new attached CPU need to be registered for all CPUs including itself.
194                 foreach(var target in cpuEntries.Values)
195                 {
196                     target.RegisterLegacySGIRequester(cpuEntry);
197                 }
198             }
199         }
200 
201         [ConnectionRegion("distributor")]
WriteByteToDistributor(long offset, byte value)202         public void WriteByteToDistributor(long offset, byte value)
203         {
204             LockExecuteAndUpdate(() =>
205                 {
206                     var registerExists = IsDistributorByteAccessible(offset) && Utils.TryWriteByteToDoubleWordCollection(distributorDoubleWordRegisters, offset, value, this);
207                     LogWriteAccess(registerExists, value, "Distributor (byte access)", offset, (DistributorRegisters)offset);
208                 }
209             );
210         }
211 
212         [ConnectionRegion("distributor")]
ReadByteFromDistributor(long offset)213         public byte ReadByteFromDistributor(long offset)
214         {
215             byte value = 0;
216             LockExecuteAndUpdate(() =>
217                 {
218                     var registerExists = IsDistributorByteAccessible(offset) && Utils.TryReadByteFromDoubleWordCollection(distributorDoubleWordRegisters, offset, out value, this);
219                     LogReadAccess(registerExists, value, "Distributor (byte access)", offset, (DistributorRegisters)offset);
220                 }
221             );
222             return value;
223         }
224 
225         [ConnectionRegion("distributor")]
WriteDoubleWordToDistributor(long offset, uint value)226         public void WriteDoubleWordToDistributor(long offset, uint value)
227         {
228             LockExecuteAndUpdate(() =>
229                 {
230                     var registerExists = TryWriteRegisterSecurityView(offset, value, distributorDoubleWordRegisters,
231                         distributorRegistersSecureView, distributorRegistersNonSecureView, distributorRegistersDisabledSecurityView);
232                     registerExists = registerExists || Utils.TryWriteDoubleWordToQuadWordCollection(distributorQuadWordRegisters, offset, value, this);
233                     LogWriteAccess(registerExists, value, "Distributor", offset, (DistributorRegisters)offset);
234                 }
235             );
236         }
237 
238         [ConnectionRegion("distributor")]
ReadDoubleWordFromDistributor(long offset)239         public uint ReadDoubleWordFromDistributor(long offset)
240         {
241             uint value = 0;
242             LockExecuteAndUpdate(() =>
243                 {
244                     var registerExists = TryReadRegisterSecurityView(offset, out value, distributorDoubleWordRegisters,
245                         distributorRegistersSecureView, distributorRegistersNonSecureView, distributorRegistersDisabledSecurityView);
246                     registerExists = registerExists || Utils.TryReadDoubleWordFromQuadWordCollection(distributorQuadWordRegisters, offset, out value, this);
247                     LogReadAccess(registerExists, value, "Distributor", offset, (DistributorRegisters)offset);
248                 }
249             );
250             return value;
251         }
252 
253         [ConnectionRegion("distributor")]
WriteQuadWordToDistributor(long offset, ulong value)254         public void WriteQuadWordToDistributor(long offset, ulong value)
255         {
256             LockExecuteAndUpdate(() =>
257                 LogWriteAccess(distributorQuadWordRegisters.TryWrite(offset, value), value, "Distributor", offset, (DistributorRegisters)offset)
258             );
259         }
260 
261         [ConnectionRegion("distributor")]
ReadQuadWordFromDistributor(long offset)262         public ulong ReadQuadWordFromDistributor(long offset)
263         {
264             ulong value = 0;
265             LockExecuteAndUpdate(() =>
266                 LogWriteAccess(distributorQuadWordRegisters.TryRead(offset, out value), value, "Distributor", offset, (DistributorRegisters)offset)
267             );
268             return value;
269         }
270 
271         [ConnectionRegion("cpuInterface")]
WriteDoubleWordToCPUInterface(long offset, uint value)272         public void WriteDoubleWordToCPUInterface(long offset, uint value)
273         {
274             LockExecuteAndUpdate(() =>
275                 {
276                     var registerExists = TryWriteRegisterSecurityView(offset, value, cpuInterfaceRegisters,
277                         cpuInterfaceRegistersSecureView, cpuInterfaceRegistersNonSecureView, cpuInterfaceRegistersDisabledSecurityView);
278                     LogWriteAccess(registerExists, value, "memory-mapped CPU Interface", offset, (CPUInterfaceRegisters)offset);
279                 }
280             );
281         }
282 
283         [ConnectionRegion("cpuInterface")]
ReadDoubleWordFromCPUInterface(long offset)284         public uint ReadDoubleWordFromCPUInterface(long offset)
285         {
286             uint value = 0;
287             LockExecuteAndUpdate(() =>
288                 {
289                     var registerExists = TryReadRegisterSecurityView(offset, out value, cpuInterfaceRegisters,
290                         cpuInterfaceRegistersSecureView, cpuInterfaceRegistersNonSecureView, cpuInterfaceRegistersDisabledSecurityView);
291                     LogReadAccess(registerExists, value, "memory-mapped CPU Interface", offset, (CPUInterfaceRegisters)offset);
292                 }
293             );
294             return value;
295         }
296 
WriteSystemRegisterCPUInterface(uint offset, ulong value)297         public void WriteSystemRegisterCPUInterface(uint offset, ulong value)
298         {
299             LockExecuteAndUpdate(() =>
300                 LogWriteAccess(cpuInterfaceSystemRegisters.TryWrite(offset, value), value, "CPU Interface", offset, (CPUInterfaceSystemRegisters)offset)
301             );
302         }
303 
ReadSystemRegisterCPUInterface(uint offset)304         public ulong ReadSystemRegisterCPUInterface(uint offset)
305         {
306             ulong value = 0;
307             LockExecuteAndUpdate(() =>
308                 LogReadAccess(cpuInterfaceSystemRegisters.TryRead(offset, out value), value, "CPU Interface", offset, (CPUInterfaceSystemRegisters)offset)
309             );
310             return value;
311         }
312 
313         // Handles SPIs.
OnGPIO(int number, bool value)314         public void OnGPIO(int number, bool value)
315         {
316             var irqId = new InterruptId((uint)number + (uint)irqsDecoder.SharedPeripheralFirst);
317             if(!irqsDecoder.IsSharedPeripheral(irqId))
318             {
319                 this.Log(LogLevel.Warning, "Generated interrupt isn't a Shared Peripheral Interrupt, interrupt identifier: {0}", irqId);
320                 return;
321             }
322             this.Log(LogLevel.Debug, "Setting Shared Peripheral Interrupt #{0} signal to {1}", irqId, value);
323             LockExecuteAndUpdate(() =>
324                 sharedInterrupts[irqId].State.AssertAsPending(value)
325             );
326         }
327 
328         // Private Peripheral Interrupts are connected using the ILocalGPIOReceiver interface
329         // Every CPUEntry class implements the IGPIOReceiver interface used to connect PPIs to each CPU
330         // The CPUEntry provides event for handling received interrupts by an external action
331         // It's expected to handle all of these interrupts by OnPrivateInterrupt method
GetLocalReceiver(int processorNumber)332         public IGPIOReceiver GetLocalReceiver(int processorNumber)
333         {
334             return GetCPUEntry((uint)processorNumber);
335         }
336 
GetEnabledInterruptIdentifiers(uint processorNumber)337         public IEnumerable<uint> GetEnabledInterruptIdentifiers(uint processorNumber)
338         {
339             var cpu = GetCPUEntry(processorNumber);
340             lock(locker)
341             {
342                 return GetAllEnabledInterrupts(cpu).Select(irq => (uint)irq.Identifier);
343             }
344         }
345 
GetRedistributorRegistrations()346         public IEnumerable<ArmGicRedistributorRegistration> GetRedistributorRegistrations()
347         {
348             return this.GetMachine().GetSystemBus(this).GetRegistrationPoints(this).OfType<ArmGicRedistributorRegistration>();
349         }
350 
LockExecuteAndUpdate(Action action)351         public void LockExecuteAndUpdate(Action action)
352         {
353             lock(locker)
354             {
355                 action();
356                 UpdateBestPendingInterrupts();
357                 foreach(var cpu in cpuEntries.Values)
358                 {
359                     cpu.UpdateSignals();
360                 }
361             }
362         }
363 
LogWriteAccess(bool registerExists, object value, string collectionName, long offset, object prettyOffset)364         public void LogWriteAccess(bool registerExists, object value, string collectionName, long offset, object prettyOffset)
365         {
366             this.Log(LogLevel.Noisy, "{0} writes to 0x{1:X} ({2}) register of {3}, value 0x{4:X}.", GetAskingCPUEntry().Name, offset, prettyOffset, collectionName, value);
367             if(!registerExists)
368             {
369                 this.Log(LogLevel.Warning, "Unhandled write to 0x{0:X} register of {1}, value 0x{2:X}.", offset, collectionName, value);
370             }
371         }
372 
LogReadAccess(bool registerExists, object value, string collectionName, long offset, object prettyOffset)373         public void LogReadAccess(bool registerExists, object value, string collectionName, long offset, object prettyOffset)
374         {
375             if(!registerExists)
376             {
377                 this.Log(LogLevel.Warning, "Unhandled read from 0x{0:X} register of {1}.", offset, collectionName);
378             }
379             this.Log(LogLevel.Noisy, "{0} reads from 0x{1:X} ({2}) register of {3}, returned 0x{4:X}.", GetAskingCPUEntry().Name, offset, prettyOffset, collectionName, value);
380         }
381 
TryGetCPUEntry(uint processorNumber, out CPUEntry cpuEntry)382         public bool TryGetCPUEntry(uint processorNumber, out CPUEntry cpuEntry)
383         {
384             var exists = cpusByProcessorNumberCache.TryGetValue(processorNumber, out var cpu);
385             cpuEntry = exists ? cpuEntries[cpu] : null;
386             return exists;
387         }
388 
TryGetCPUEntryForCPU(IARMSingleSecurityStateCPU cpu, out CPUEntry cpuEntry)389         public bool TryGetCPUEntryForCPU(IARMSingleSecurityStateCPU cpu, out CPUEntry cpuEntry)
390         {
391             return cpuEntries.TryGetValue(cpu, out cpuEntry);
392         }
393 
GetCPUEntry(uint processorNumber)394         public CPUEntry GetCPUEntry(uint processorNumber)
395         {
396             if(!TryGetCPUEntry(processorNumber, out var cpuEntry))
397             {
398                 throw new RecoverableException($"There is no CPU with the Processor Number {processorNumber}.");
399             }
400             return cpuEntry;
401         }
402 
BuildInterruptSetEnableRegisters(InterruptId startId, InterruptId endId, string name, Func<CPUEntry> cpuEntryProvider = null)403         public IEnumerable<DoubleWordRegister> BuildInterruptSetEnableRegisters(InterruptId startId, InterruptId endId, string name, Func<CPUEntry> cpuEntryProvider = null)
404         {
405             return BuildInterruptFlagRegisters(startId, endId, name,
406                 writeCallback: (irq, val) => irq.Config.Enabled |= val,
407                 valueProviderCallback: irq => irq.Config.Enabled,
408                 cpuEntryProvider: cpuEntryProvider
409             );
410         }
411 
BuildInterruptClearEnableRegisters(InterruptId startId, InterruptId endId, string name, Func<CPUEntry> cpuEntryProvider = null)412         public IEnumerable<DoubleWordRegister> BuildInterruptClearEnableRegisters(InterruptId startId, InterruptId endId, string name, Func<CPUEntry> cpuEntryProvider = null)
413         {
414             return BuildInterruptFlagRegisters(startId, endId, name,
415                 writeCallback: (irq, val) => irq.Config.Enabled &= !val,
416                 valueProviderCallback: irq => irq.Config.Enabled,
417                 cpuEntryProvider: cpuEntryProvider
418             );
419         }
420 
BuildInterruptPriorityRegisters(InterruptId startId, InterruptId endId, string name, Func<CPUEntry> cpuEntryProvider = null)421         public IEnumerable<DoubleWordRegister> BuildInterruptPriorityRegisters(InterruptId startId, InterruptId endId, string name, Func<CPUEntry> cpuEntryProvider = null)
422         {
423             return BuildInterruptEnumRegisters<InterruptPriority>(startId, endId, name, 4,
424                 writeCallback: (irq, val) => irq.Config.Priority = val,
425                 valueProviderCallback: irq => irq.Config.Priority,
426                 cpuEntryProvider: cpuEntryProvider
427             );
428         }
429 
BuildPrivateInterruptTargetsRegisters(InterruptId startId, InterruptId endId, string name)430         public IEnumerable<DoubleWordRegister> BuildPrivateInterruptTargetsRegisters(InterruptId startId, InterruptId endId, string name)
431         {
432             return BuildInterruptValueRegisters(startId, endId, name, 4,
433                 valueProviderCallback: _ => GetAskingCPUEntry().TargetFieldFlag
434             );
435         }
436 
BuildSharedInterruptTargetsRegisters(InterruptId startId, InterruptId endId, string name)437         public IEnumerable<DoubleWordRegister> BuildSharedInterruptTargetsRegisters(InterruptId startId, InterruptId endId, string name)
438         {
439             return BuildInterruptValueRegisters(startId, endId, name, 4,
440                 writeCallback: (irq, val) =>
441                     {
442                         if(IsAffinityRoutingEnabled(GetAskingCPUEntry()))
443                         {
444                             this.Log(LogLevel.Warning, "Trying to write ITARGETSR register when Affinity Routing is enabled, write ignored.");
445                             return;
446                         }
447                         var validTargets = legacyCpusAttachedMask & val;
448                         ((SharedInterrupt)irq).TargetCPUs = (byte)validTargets;
449                         if(validTargets != val)
450                         {
451                             this.Log(LogLevel.Warning, "Interrupt {0} configured to target an invalid CPU, id: {1}, writes ignored.", irq.Identifier, String.Join(", ", BitHelper.GetSetBits(validTargets ^ val)));
452                         }
453                     },
454                 valueProviderCallback: irq =>
455                     {
456                         if(IsAffinityRoutingEnabled(GetAskingCPUEntry()))
457                         {
458                             this.Log(LogLevel.Warning, "Trying to read ITARGETSR register when Affinity Routing is enabled, returning 0.");
459                             return 0;
460                         }
461                         return ((SharedInterrupt)irq).TargetCPUs;
462                     }
463             );
464         }
465 
BuildInterruptConfigurationRegisters(InterruptId startId, InterruptId endId, string name, bool isReadonly = false, Func<CPUEntry> cpuEntryProvider = null)466         public IEnumerable<DoubleWordRegister> BuildInterruptConfigurationRegisters(InterruptId startId, InterruptId endId, string name, bool isReadonly = false, Func<CPUEntry> cpuEntryProvider = null)
467         {
468             Action<Interrupt, InterruptTriggerType> writeCallback = null;
469             if(!isReadonly)
470             {
471                 writeCallback = (irq, val) =>
472                 {
473                     irq.State.TriggerType = val;
474                     if(val != InterruptTriggerType.LevelSensitive && val != InterruptTriggerType.EdgeTriggered)
475                     {
476                         this.Log(LogLevel.Error, "Setting an unknown interrupt trigger type, value {0}", val);
477                     }
478                 };
479             }
480             return BuildInterruptEnumRegisters<InterruptTriggerType>(startId, endId, name, 16,
481                 writeCallback: writeCallback,
482                 valueProviderCallback: irq => irq.State.TriggerType,
483                 cpuEntryProvider: cpuEntryProvider
484             );
485         }
486 
BuildInterruptSetActiveRegisters(InterruptId startId, InterruptId endId, string name)487         public IEnumerable<DoubleWordRegister> BuildInterruptSetActiveRegisters(InterruptId startId, InterruptId endId, string name)
488         {
489             return BuildInterruptFlagRegisters(startId, endId, name,
490                 writeCallback: (irq, val) => irq.State.Active |= val,
491                 valueProviderCallback: irq => irq.State.Active
492             );
493         }
494 
BuildInterruptClearActiveRegisters(InterruptId startId, InterruptId endId, string name)495         public IEnumerable<DoubleWordRegister> BuildInterruptClearActiveRegisters(InterruptId startId, InterruptId endId, string name)
496         {
497             return BuildInterruptFlagRegisters(startId, endId, name,
498                 writeCallback: (irq, val) => irq.State.Active &= !val,
499                 valueProviderCallback: irq => irq.State.Active
500             );
501         }
502 
BuildInterruptSetPendingRegisters(InterruptId startId, InterruptId endId, string name)503         public IEnumerable<DoubleWordRegister> BuildInterruptSetPendingRegisters(InterruptId startId, InterruptId endId, string name)
504         {
505             return BuildInterruptFlagRegisters(startId, endId, name,
506                 writeCallback: (irq, val) => irq.State.Pending |= val,
507                 valueProviderCallback: irq => irq.State.Pending
508             );
509         }
510 
BuildInterruptClearPendingRegisters(InterruptId startId, InterruptId endId, string name, Func<CPUEntry> cpuEntryProvider = null)511         public IEnumerable<DoubleWordRegister> BuildInterruptClearPendingRegisters(InterruptId startId, InterruptId endId, string name, Func<CPUEntry> cpuEntryProvider = null)
512         {
513             return BuildInterruptFlagRegisters(startId, endId, name,
514                 writeCallback: (irq, val) => irq.State.Pending &= !val,
515                 valueProviderCallback: irq => irq.State.Pending,
516                 cpuEntryProvider: cpuEntryProvider
517             );
518         }
519 
BuildInterruptGroupRegisters(InterruptId startId, InterruptId endId, string name, Func<CPUEntry> cpuEntryProvider = null)520         public IEnumerable<DoubleWordRegister> BuildInterruptGroupRegisters(InterruptId startId, InterruptId endId, string name, Func<CPUEntry> cpuEntryProvider = null)
521         {
522             return BuildInterruptFlagRegisters(startId, endId, name,
523                 writeCallback: (irq, val) => irq.Config.GroupBit = val,
524                 valueProviderCallback: irq => irq.Config.GroupBit,
525                 allowAccessWhenNonSecureGroup: false,
526                 cpuEntryProvider: cpuEntryProvider
527             );
528         }
529 
BuildInterruptGroupModifierRegisters(InterruptId startId, InterruptId endId, string name, Func<CPUEntry> cpuEntryProvider = null)530         public IEnumerable<DoubleWordRegister> BuildInterruptGroupModifierRegisters(InterruptId startId, InterruptId endId, string name, Func<CPUEntry> cpuEntryProvider = null)
531         {
532             return BuildInterruptFlagRegisters(startId, endId, name,
533                 writeCallback: (irq, val) =>
534                     {
535                         if(!DisabledSecurity)
536                         {
537                             irq.Config.GroupModifierBit = val;
538                         }
539                         else
540                         {
541                             // The Zephyr uses this field as usual, so the log message isn't a warning
542                             this.Log(LogLevel.Debug, "The group modifier register is reserved for the disabled security, write ignored.");
543                         }
544                     },
545                 valueProviderCallback: irq => irq.Config.GroupModifierBit,
546                 allowAccessWhenNonSecureGroup: false,
547                 cpuEntryProvider: cpuEntryProvider
548             );
549         }
550 
BuildPrivateOrSharedPeripheralInterruptStatusRegisters(InterruptId startId, InterruptId endId, string name)551         public IEnumerable<DoubleWordRegister> BuildPrivateOrSharedPeripheralInterruptStatusRegisters(InterruptId startId, InterruptId endId, string name)
552         {
553             return BuildInterruptFlagRegisters(startId, endId, name,
554                 valueProviderCallback: irq => irq.State.Pending
555             );
556         }
557 
558         public long PeripheralIdentificationOffset => ArchitectureVersionAtLeast3
559             ? (long)RedistributorRegisters.PeripheralIdentification2_v3v4
560             : (long)RedistributorRegisters.PeripheralIdentification2_v1v2;
561 
562         public IReadOnlyDictionary<int, IGPIO> Connections { get; private set; }
563         public IEnumerable<IARMSingleSecurityStateCPU> AttachedCPUs => cpuEntries.Keys;
564         public InterruptsDecoder IrqsDecoder => irqsDecoder;
565 
566         public bool DisabledSecurity
567         {
568             get => !supportsTwoSecurityStates || disabledSecurity;
569             set
570             {
571                 if(!supportsTwoSecurityStates)
572                 {
573                     this.Log(LogLevel.Warning, "Disabling security isn't allowed for a single security GIC, write ignored.");
574                     return;
575                 }
576                 if(!SetOnTransitionToTrue(ref disabledSecurity, value, "Trying to enable security when it's disabled, write ignored."))
577                 {
578                     return;
579                 }
580 
581                 if(groups.Values.Any(group => group.Enabled))
582                 {
583                     this.Log(LogLevel.Warning, "Disabling security when a group of interrupts is enabled.");
584                 }
585 
586                 var cpuConfigs = cpuEntries.Values.SelectMany(cpu => cpu.AllPrivateAndSoftwareGeneratedInterruptsConfigs);
587                 var allInterruptConfigs = cpuConfigs.Concat(sharedInterrupts.Values.Select(irq => irq.Config));
588                 foreach(var config in allInterruptConfigs)
589                 {
590                     config.GroupModifierBit = false;
591                 }
592             }
593         }
594 
595         public bool AffinityRoutingEnabledSecure
596         {
597             get => affinityRoutingEnabledSecure;
598             set
599             {
600                 if(!SetOnTransitionToTrue(ref affinityRoutingEnabledSecure, value, "Trying to disable affinity routing for secure state when it's enabled, write ignored."))
601                 {
602                     return;
603                 }
604                 // According to the specification the value for secure state overrides the value for non-secure state.
605                 affinityRoutingEnabledNonSecure = true;
606 
607                 if(groups.Values.Any(group => group.Enabled))
608                 {
609                     this.Log(LogLevel.Warning, "Enabling affinity routing for secure state when a group of interrupts is enabled.");
610                 }
611             }
612         }
613 
614         public bool AffinityRoutingEnabledNonSecure
615         {
616             get => affinityRoutingEnabledNonSecure;
617             set
618             {
619                 if(!SetOnTransitionToTrue(ref affinityRoutingEnabledNonSecure, value, "Trying to disable affinity routing for non-secure state when it's enabled, write ignored."))
620                 {
621                     return;
622                 }
623                 if(groups[GroupType.Group1NonSecure].Enabled)
624                 {
625                     this.Log(LogLevel.Warning, "Enabling affinity routing for non-secure state when the Group 1 Non-secure is enabled.");
626                 }
627             }
628         }
629 
630         public bool AffinityRoutingEnabledBoth => DisabledSecurity || AffinityRoutingEnabledSecure && AffinityRoutingEnabledNonSecure;
631 
632         /// <summary>
633         /// Setting this property to true will causes all interrupts to be reported to a core with lowest ID, which configuration allows it to take.
634         ///
635         /// This is mostly for debugging purposes.
636         /// It allows to predict a core (in a multi-core setup) to handle the given interrupt making it easier to debug.
637         /// </summary>
638         public bool ForceLowestIdCpuAsInterruptTarget { get; set; }
639 
640         public ARM_GenericInterruptControllerVersion ArchitectureVersion { get; }
641         public bool ArchitectureVersionAtLeast3 => ArchitectureVersion >= ARM_GenericInterruptControllerVersion.GICv3;
642         public uint CPUInterfaceProductIdentifier { get; set; } = DefaultCPUInterfaceProductIdentifier;
643         public uint DistributorProductIdentifier { get; set; } = DefaultDistributorProductIdentifier;
644         public byte CPUInterfaceRevision { get; set; } = DefaultRevisionNumber;
645         public uint CPUInterfaceImplementer { get; set; } = DefaultImplementerIdentification;
646         public byte DistributorVariant { get; set; } = DefaultVariantNumber;
647         public byte DistributorRevision { get; set; } = DefaultRevisionNumber;
648         public uint DistributorImplementer { get; set; } = DefaultImplementerIdentification;
649         public uint RedistributorProductIdentifier { get; set; } = DefaultRedistributorProductIdentifier;
650         public byte RedistributorVariant { get; set; } = DefaultVariantNumber;
651         public byte RedistributorRevision { get; set; } = DefaultRevisionNumber;
652         public uint RedistributorImplementer { get; set; } = DefaultImplementerIdentification;
653 
654         public event Action<IARMSingleSecurityStateCPU> CPUAttached;
655 
OnPrivateInterrupt(CPUEntry cpu, int id, bool value)656         private void OnPrivateInterrupt(CPUEntry cpu, int id, bool value)
657         {
658             var irqId = new InterruptId((uint)id);
659             if(!irqsDecoder.IsPrivatePeripheral(irqId))
660             {
661                 this.Log(LogLevel.Warning, "Generated interrupt isn't a Private Peripheral Interrupt, interrupt identifier: {0}", irqId);
662                 return;
663             }
664             this.Log(LogLevel.Debug, "Setting Private Peripheral Interrupt #{0} signal to {1} for {2}", irqId, value, cpu.Name);
665             LockExecuteAndUpdate(() =>
666                 cpu.PrivatePeripheralInterrupts[irqId].State.AssertAsPending(value)
667             );
668         }
669 
OnSoftwareGeneratedInterrupt(CPUEntry requestingCPU, SoftwareGeneratedInterruptRequest request)670         private void OnSoftwareGeneratedInterrupt(CPUEntry requestingCPU, SoftwareGeneratedInterruptRequest request)
671         {
672             // The GIC uses a groups configuration at the moment of SGI request to choose correct target CPUs.
673             var irqId = request.InterruptId;
674             DebugHelper.Assert(irqsDecoder.IsSoftwareGenerated(irqId), $"Invalid interrupt identifier ({irqId}), it doesn't indicate an SGI.");
675             this.Log(LogLevel.Noisy, "The {0} requests an SGI with id {1}.", requestingCPU.Name, irqId);
676 
677             var targetCPUs = new List<CPUEntry>();
678             switch(request.TargetCPUsType)
679             {
680                 case SoftwareGeneratedInterruptRequest.TargetType.Loopback:
681                     targetCPUs.Add(requestingCPU);
682                     break;
683                 case SoftwareGeneratedInterruptRequest.TargetType.AllCPUs:
684                     targetCPUs.AddRange(cpuEntries.Values.Where(cpu => cpu != requestingCPU));
685                     break;
686                 case SoftwareGeneratedInterruptRequest.TargetType.TargetList:
687                     foreach(var affinity in request.TargetsList)
688                     {
689                         if(TryGetCPUEntry(affinity.AllLevels, out var targetCPU))
690                         {
691                             targetCPUs.Add(targetCPU);
692                         }
693                         else
694                         {
695                             this.Log(LogLevel.Debug, "There is no target CPU with the affinity {0} for an SGI request.", affinity);
696                         }
697                     }
698                     break;
699                 default:
700                     this.Log(LogLevel.Warning, "Unknown Software Generated Interrupt target type {0}", request.TargetCPUsType);
701                     return;
702             }
703 
704             if(IsAffinityRoutingEnabled(requestingCPU))
705             {
706                 OnSGIAffinityRouting(requestingCPU, targetCPUs, request);
707             }
708             else
709             {
710                 OnSGILegacyRouting(requestingCPU, targetCPUs, request);
711             }
712         }
713 
OnSGIAffinityRouting(CPUEntry requestingCPU, List<CPUEntry> targetCPUs, SoftwareGeneratedInterruptRequest request)714         private void OnSGIAffinityRouting(CPUEntry requestingCPU, List<CPUEntry> targetCPUs, SoftwareGeneratedInterruptRequest request)
715         {
716             var isOtherSecurityState = GroupTypeToIsStateSecure(request.TargetGroup) != requestingCPU.IsStateSecure;
717             if(isOtherSecurityState && !AffinityRoutingEnabledBoth)
718             {
719                 this.Log(LogLevel.Debug, "Generating SGIs for the other Security state is only supported when affinity rouing is enabled for both Security states.");
720                 return;
721             }
722             var irqId = request.InterruptId;
723             foreach(var target in targetCPUs)
724             {
725                 var interrupt = target.SoftwareGeneratedInterruptsUnknownRequester[irqId];
726                 this.Log(LogLevel.Noisy, "Trying to request interrupt for target {0}, interrupt group type {1}, request group {2}, access in {3} state.",
727                     target.Name, interrupt.Config.GroupType, request.TargetGroup, requestingCPU.IsStateSecure ? "secure" : "non-secure");
728 
729                 if(ShouldAssertSGIAffinityRouting(requestingCPU, request, interrupt, target.NonSecureSGIAccess[(uint)irqId]))
730                 {
731                     this.Log(LogLevel.Noisy, "Setting Software Generated Interrupt #{0} signal for {1}", irqId, target.Name);
732                     // SGIs are triggered by a register access so the method is already called inside a lock.
733                     interrupt.State.AssertAsPending(true);
734                 }
735                 else
736                 {
737                     this.Log(LogLevel.Noisy, "SGI #{0} not forwarded for {1}.", irqId, target.Name);
738                 }
739             }
740         }
741 
ShouldAssertSGIAffinityRouting(CPUEntry requestingCPU, SoftwareGeneratedInterruptRequest request, SoftwareGeneratedInterrupt interrupt, NonSecureAccess targetNonSecureAccess)742         private bool ShouldAssertSGIAffinityRouting(CPUEntry requestingCPU, SoftwareGeneratedInterruptRequest request, SoftwareGeneratedInterrupt interrupt,
743             NonSecureAccess targetNonSecureAccess)
744         {
745             if(requestingCPU.IsStateSecure)
746             {
747                 return request.TargetGroup == interrupt.Config.GroupType ||
748                     request.TargetGroup == GroupType.Group1Secure && interrupt.Config.GroupType == GroupType.Group0 && DisabledSecurity;
749             }
750             if(request.TargetGroup == interrupt.Config.GroupType || interrupt.Config.GroupType == GroupType.Group0)
751             {
752                 return request.TargetGroup == GroupType.Group1NonSecure || DisabledSecurity || NonSecureAccessPermitsGroup(targetNonSecureAccess, request.TargetGroup);
753             }
754             return request.TargetGroup == GroupType.Group1NonSecure && interrupt.Config.GroupType == GroupType.Group1Secure && NonSecureAccessPermitsGroup(targetNonSecureAccess, request.TargetGroup);
755         }
756 
OnSGILegacyRouting(CPUEntry requestingCPU, List<CPUEntry> targetCPUs, SoftwareGeneratedInterruptRequest request)757         private void OnSGILegacyRouting(CPUEntry requestingCPU, List<CPUEntry> targetCPUs, SoftwareGeneratedInterruptRequest request)
758         {
759             var irqId = request.InterruptId;
760             foreach(var target in targetCPUs)
761             {
762                 if(!target.SoftwareGeneratedInterruptsLegacyRequester.TryGetValue(requestingCPU, out var interrupts))
763                 {
764                     this.Log(LogLevel.Warning, "The GIC doesn't support requesting an SGI from the CPU with the Processor Number ({0}) greater than {1}, request ignored.", requestingCPU.ProcessorNumber, CPUsCountLegacySupport - 1);
765                     return;
766                 }
767 
768                 var interrupt = interrupts[irqId];
769                 this.Log(LogLevel.Noisy, "Trying to request interrupt for target {0}, interrupt group type (GICD_IGROUPRn) {1}, request group (NSATT) {2}, access in {3} state.",
770                     target.Name, interrupt.Config.GroupType, request.TargetGroup, requestingCPU.IsStateSecure ? "secure" : "non-secure");
771 
772                 if(ShouldAssertSGILegacyRouting(requestingCPU, request, interrupt))
773                 {
774                     this.Log(LogLevel.Noisy, "Setting Software Generated Interrupt #{0} signal for {1}", irqId, target.Name);
775                     // SGIs are triggered by a register access so the method is already called inside a lock.
776                     interrupt.State.AssertAsPending(true);
777                 }
778                 else
779                 {
780                     this.Log(LogLevel.Noisy, "SGI #{0} not forwarded for {1}.", irqId, target.Name);
781                 }
782             }
783         }
784 
ShouldAssertSGILegacyRouting(CPUEntry requestingCPU, SoftwareGeneratedInterruptRequest request, SoftwareGeneratedInterrupt interrupt)785         private bool ShouldAssertSGILegacyRouting(CPUEntry requestingCPU, SoftwareGeneratedInterruptRequest request, SoftwareGeneratedInterrupt interrupt)
786         {
787             // See: "SGI generation when the GIC implements the Security Extensions" for the truth table
788             // Without the Security Extension, this is irrelevant
789             if(DisabledSecurity)
790             {
791                 return true;
792             }
793             // If GICD_SGIR write is done in non-secure mode, only Group1 is forwarded
794             else if(!requestingCPU.IsStateSecure)
795             {
796                 return interrupt.Config.GroupType != GroupType.Group0;
797             }
798             // According to the specification, interrupt with a group different than a target group is just ignored, if GICD_SGIR write is done in secure mode.
799             else if(requestingCPU.IsStateSecure && interrupt.Config.GroupType == request.TargetGroup)
800             {
801                 return true;
802             }
803 
804             return false;
805         }
806 
NonSecureAccessPermitsGroup(NonSecureAccess access, GroupType type)807         private bool NonSecureAccessPermitsGroup(NonSecureAccess access, GroupType type)
808         {
809             // To maintain the principle that as the value increases additional accesses are permitted
810             // Arm strongly recommends that implementations treat the reserved value as 0b10.
811             // That's why we compare `access` using the >= operator.
812             switch(type)
813             {
814                 case GroupType.Group1NonSecure:
815                     return true;
816                 case GroupType.Group1Secure:
817                     return access >= NonSecureAccess.BothGroupsPermitted;
818                 case GroupType.Group0:
819                     return access >= NonSecureAccess.SecureGroup0Permitted;
820                 default:
821                     throw new ArgumentOutOfRangeException($"There is no valid GroupType for value: {type}.");
822             }
823         }
824 
GroupTypeToIsStateSecure(GroupType type)825         private bool GroupTypeToIsStateSecure(GroupType type)
826         {
827             switch(type)
828             {
829                 case GroupType.Group0:
830                 case GroupType.Group1Secure:
831                     return true;
832                 case GroupType.Group1NonSecure:
833                     return false;
834                 default:
835                     throw new ArgumentOutOfRangeException($"There is no valid GroupType for value: {type}.");
836             }
837         }
838 
GetGroup1ForSecurityState(bool isSecure)839         private GroupType GetGroup1ForSecurityState(bool isSecure)
840         {
841             return isSecure
842                 // When System register access is not enabled for Secure EL1, or when GICD_CTLR.DS == 1,
843                 // the Distributor treats Secure Group 1 interrupts as Group 0 interrupts
844                 ? DisabledSecurity ? GroupType.Group0 : GroupType.Group1Secure
845                 : GroupType.Group1NonSecure;
846         }
847 
848         // The GIC uses the latest CPU state and the latest groups configuration to choose a correct interrupt signal to assert.
OnExecutionModeChanged(CPUEntry cpu)849         private void OnExecutionModeChanged(CPUEntry cpu)
850         {
851             lock(locker)
852             {
853                 cpu.UpdateSignals();
854             }
855         }
856 
857         // Returns the best pending interrupt for given candidates.
858         // If null is returned, that means there's no pending interrupt.
859         private T FindBestPendingInterrupt<T>(IEnumerable<T> pendingCandidates) where T : Interrupt
860         {
861             var bestPending = pendingCandidates.FirstOrDefault();
862             foreach(var irq in pendingCandidates.Skip(1))
863             {
864                 if(irq.Config.Priority < bestPending.Config.Priority)
865                 {
866                     bestPending = irq;
867                     if(bestPending.Config.Priority == InterruptPriority.Highest)
868                     {
869                         break;
870                     }
871                 }
872             }
873             return bestPending;
874         }
875 
UpdateBestPendingInterrupts()876         private void UpdateBestPendingInterrupts()
877         {
878             foreach(var cpu in cpuEntries.Values)
879             {
880                 if(cpu.VirtualCPUInterfaceEnabled)
881                 {
882                     cpu.BestPendingVirtual = FindBestPendingInterrupt(GetAllPendingCandidateVirtualInterrupts(cpu));
883                 }
884                 else
885                 {
886                     cpu.BestPendingVirtual = null;
887                 }
888                 cpu.BestPending = FindBestPendingInterrupt(GetAllPendingCandidateInterrupts(cpu));
889             }
890         }
891 
GetSharedInterruptsTargetingCPU(CPUEntry cpu)892         private IEnumerable<Interrupt> GetSharedInterruptsTargetingCPU(CPUEntry cpu)
893         {
894             IEnumerable<SharedInterrupt> interrupts = sharedInterrupts.Values;
895             if(cpuEntries.Count == 1)
896             {
897                 // If there is only one CPU all interrupts target it.
898                 return interrupts;
899             }
900             if(!IsAffinityRoutingEnabled(cpu))
901             {
902                 return interrupts.Where(irq =>
903                     irq.IsLegacyRoutingTargetingCPU(cpu) && (!ForceLowestIdCpuAsInterruptTarget || irq.IsLowestLegacyRoutingTargettedCPU(cpu))
904                 );
905             }
906 
907             return interrupts.Where(irq =>
908                 irq.IsAffinityRoutingTargetingCPU(cpu) && (!ForceLowestIdCpuAsInterruptTarget || irq.IsLowestAffinityRoutingTargettedCPU(cpu, this))
909             );
910         }
911 
GetAllEnabledInterrupts(CPUEntry cpu)912         private IEnumerable<Interrupt> GetAllEnabledInterrupts(CPUEntry cpu)
913         {
914             var enabledGroups = groups.Keys.Where(type => groups[type].Enabled && cpu.Groups.Physical[type].Enabled).ToArray();
915             IEnumerable<SharedInterrupt> filteredSharedInterrupts = sharedInterrupts.Values;
916             return cpu.AllPrivateAndSoftwareGeneratedInterrupts
917                 .Concat(GetSharedInterruptsTargetingCPU(cpu))
918                 .Where(irq => irq.Config.Enabled && enabledGroups.Contains(irq.Config.GroupType));
919         }
920 
GetAllInterrupts(CPUEntry cpu)921         private IEnumerable<Interrupt> GetAllInterrupts(CPUEntry cpu)
922         {
923             return cpu.AllPrivateAndSoftwareGeneratedInterrupts.Concat(sharedInterrupts.Values);
924         }
925 
InterruptPriorityFilter(InterruptPriority priorityMask, InterruptPriority runningPriority)926         private Func<Interrupt, bool> InterruptPriorityFilter(InterruptPriority priorityMask, InterruptPriority runningPriority)
927         {
928             return irq => irq.State.Pending && !irq.State.Active && irq.Config.Priority < priorityMask && irq.Config.Priority < runningPriority;
929         }
930 
GetAllPendingCandidateInterrupts(CPUEntry cpu)931         private IEnumerable<Interrupt> GetAllPendingCandidateInterrupts(CPUEntry cpu)
932         {
933             var filter = InterruptPriorityFilter(cpu.PhysicalPriorityMask, cpu.RunningInterrupts.PhysicalPriority);
934             return GetAllEnabledInterrupts(cpu).Where(filter);
935         }
936 
GetAllPendingCandidateVirtualInterrupts(CPUEntry cpu)937         private IEnumerable<VirtualInterrupt> GetAllPendingCandidateVirtualInterrupts(CPUEntry cpu)
938         {
939             var enabledGroups = groups.Keys.Where(type => cpu.Groups.Virtual[type].Enabled).ToArray();
940             var filter = InterruptPriorityFilter(cpu.VirtualPriorityMask, cpu.RunningInterrupts.VirtualPriority);
941             return cpu
942                 .VirtualInterrupts
943                 .Where(irq => enabledGroups.Contains(irq.Config.GroupType) && filter(irq));
944         }
945 
BuildDistributorDoubleWordRegistersMap()946         private Dictionary<long, DoubleWordRegister> BuildDistributorDoubleWordRegistersMap()
947         {
948             var registersMap = new Dictionary<long, DoubleWordRegister>
949             {
950                 {(long)DistributorRegisters.ControllerType, new DoubleWordRegister(this)
951                     .WithValueField(27, 5, name: "SharedPeripheralInterruptsExtendedCount",
952                         valueProviderCallback: _ => irqsDecoder.SharedPeripheralExtendedCount / 32 - 1
953                     )
954                     .WithFlag(26, name: "AffinityLevel0RangeSupport",
955                         valueProviderCallback: _ => false
956                     )
957                     .WithFlag(25, name: "1OfNSharedPeripheralInterruptsSupport",
958                         valueProviderCallback: _ => false
959                     )
960                     .WithFlag(24, name: "AffinityLevel3Support",
961                         valueProviderCallback: _ => false
962                     )
963                     .WithValueField(19, 5, name: "SupportedInterruptIdentifierBits",
964                         valueProviderCallback: _ => irqsDecoder.IdentifierBits - 1
965                     )
966                     .WithFlag(18, name: "DirectVirtualLocalitySpecificPeripheralInterruptInjectionSupport",
967                         valueProviderCallback: _ => false
968                     )
969                     .WithFlag(17, name: "LocalitySpecificPeripheralInterruptSupport",
970                         valueProviderCallback: _ => false
971                     )
972                     .WithFlag(16, name: "MessageBasedInterruptActivationByWriteSupport",
973                         valueProviderCallback: _ => false
974                     )
975                     .WithReservedBits(11, 5) // Indicates the lack of the Locality-specific Peripheral Interrupt support
976                     .WithFlag(10, name: "SecurityStateSupport",
977                         valueProviderCallback: _ => !DisabledSecurity
978                     )
979                     .WithFlag(9, name: "NonMaskableInterruptSupport",
980                         valueProviderCallback: _ => false
981                     )
982                     .WithFlag(8, name: "SharedPeripheralInterruptsExtendedSupport",
983                         valueProviderCallback: _ => false
984                     )
985                     .WithValueField(5, 3, name: "LegacyCpusCount",
986                         valueProviderCallback: _ => (ulong)BitHelper.GetSetBits(legacyCpusAttachedMask).Count - 1
987                     )
988                     .WithValueField(0, 5, name: "SharedPeripheralInterruptsCount",
989                         valueProviderCallback: _ => ((uint)irqsDecoder.SharedPeripheralLast + 1) / 32 - 1
990                     )
991                 },
992                 {(long)DistributorRegisters.ImplementerIdentification, new DoubleWordRegister(this)
993                     .WithValueField(24, 8, FieldMode.Read, valueProviderCallback: _ => DistributorProductIdentifier, name: "ProductIdentifier")
994                     .WithReservedBits(20, 4)
995                     .WithValueField(16, 4, FieldMode.Read, valueProviderCallback: _ => DistributorVariant, name: "VariantNumber")
996                     .WithValueField(12, 4, FieldMode.Read, valueProviderCallback: _ => DistributorRevision, name: "RevisionNumber")
997                     .WithValueField(0, 12, FieldMode.Read, valueProviderCallback: _ => DistributorImplementer, name: "ImplementerIdentification")
998                 },
999                 {(long)DistributorRegisters.SoftwareGeneratedInterruptControl, new DoubleWordRegister(this)
1000                     .WithReservedBits(26, 6)
1001                     .WithEnumField<DoubleWordRegister, SoftwareGeneratedInterruptRequest.TargetType>(24, 2, out var type, FieldMode.Write, name: "TargetListFilter")
1002                     .WithValueField(16, 8, out var targetList, FieldMode.Write, name: "TargetsList")
1003                     .WithFlag(15, out var group, FieldMode.Write, name: "GroupFilterSecureAccess")
1004                     .WithReservedBits(4, 10)
1005                     .WithValueField(0, 4, out var id, FieldMode.Write, name: "SoftwareGeneratedInterruptIdentifier")
1006                     .WithWriteCallback((_, __) =>
1007                     {
1008                         var list = new Affinity[]{};
1009                         if(type.Value == SoftwareGeneratedInterruptRequest.TargetType.TargetList)
1010                         {
1011                             list = BitHelper.GetSetBits(targetList.Value).Select(n => new Affinity((byte)n)).ToArray();
1012                         }
1013                         var interrupt =  new SoftwareGeneratedInterruptRequest(type.Value, list, group.Value ? GroupType.Group1 : GroupType.Group0, new InterruptId((uint)id.Value));
1014                         OnSoftwareGeneratedInterrupt(GetAskingCPUEntry(), interrupt);
1015                     })
1016                 },
1017                 {PeripheralIdentificationOffset, new DoubleWordRegister(this)
1018                     .WithReservedBits(8, 24)
1019                     .WithEnumField<DoubleWordRegister, ARM_GenericInterruptControllerVersion>(4, 4, FieldMode.Read, name: "ArchitectureVersion",
1020                         valueProviderCallback: _ => ArchitectureVersion
1021                     )
1022                     .WithTag("ImplementationDefinedIdentificator", 0, 4)
1023                 }
1024             };
1025 
1026             // All BuildInterrupt*Registers methods create registers with respect for Security State
1027             // There is no separate view (RegistersCollection) for this kind of registers, because their layout are independent of Security State
1028             Utils.AddRegistersAtOffset(registersMap, (long)DistributorRegisters.InterruptSetEnable_0,
1029                 BuildInterruptSetEnableRegisters(irqsDecoder.SoftwareGeneratedFirst, irqsDecoder.SharedPeripheralLast, "InterruptSetEnable")
1030             );
1031 
1032             Utils.AddRegistersAtOffset(registersMap, (long)DistributorRegisters.InterruptClearEnable_0,
1033                 BuildInterruptClearEnableRegisters(irqsDecoder.SoftwareGeneratedFirst, irqsDecoder.SharedPeripheralLast, "InterruptClearEnable")
1034             );
1035 
1036             Utils.AddRegistersAtOffset(registersMap, (long)DistributorRegisters.InterruptPriority_0,
1037                 BuildInterruptPriorityRegisters(irqsDecoder.SoftwareGeneratedFirst, irqsDecoder.SharedPeripheralLast, "InterruptPriority")
1038             );
1039 
1040             Utils.AddRegistersAtOffset(registersMap, (long)DistributorRegisters.InterruptProcessorTargets_0,
1041                 BuildPrivateInterruptTargetsRegisters(irqsDecoder.SoftwareGeneratedFirst, irqsDecoder.PrivatePeripheralLast, "InterruptProcessorTargets")
1042             );
1043             Utils.AddRegistersAtOffset(registersMap, (long)DistributorRegisters.InterruptProcessorTargets_8,
1044                 BuildSharedInterruptTargetsRegisters(irqsDecoder.SharedPeripheralFirst, irqsDecoder.SharedPeripheralLast, "InterruptProcessorTargets")
1045             );
1046 
1047             Utils.AddRegistersAtOffset(registersMap, (long)DistributorRegisters.InterruptConfiguration_0,
1048                 BuildInterruptConfigurationRegisters(irqsDecoder.SoftwareGeneratedFirst, irqsDecoder.SoftwareGeneratedLast, "InterruptConfiguration", isReadonly: true)
1049             );
1050             Utils.AddRegistersAtOffset(registersMap, (long)DistributorRegisters.InterruptConfiguration_1,
1051                 BuildInterruptConfigurationRegisters(irqsDecoder.PrivatePeripheralFirst, irqsDecoder.SharedPeripheralLast, "InterruptConfiguration")
1052             );
1053 
1054             Utils.AddRegistersAtOffset(registersMap, (long)DistributorRegisters.InterruptSetActive_0,
1055                 BuildInterruptSetActiveRegisters(irqsDecoder.SoftwareGeneratedFirst, irqsDecoder.SharedPeripheralLast, "InterruptSetActive")
1056             );
1057 
1058             Utils.AddRegistersAtOffset(registersMap, (long)DistributorRegisters.InterruptClearActive_0,
1059                 BuildInterruptClearActiveRegisters(irqsDecoder.SoftwareGeneratedFirst, irqsDecoder.SharedPeripheralLast, "InterruptClearActive")
1060             );
1061 
1062             Utils.AddRegistersAtOffset(registersMap, (long)DistributorRegisters.InterruptSetPending_0,
1063                 BuildInterruptSetPendingRegisters(irqsDecoder.SoftwareGeneratedFirst, irqsDecoder.SharedPeripheralLast, "InterruptSetPending")
1064             );
1065 
1066             Utils.AddRegistersAtOffset(registersMap, (long)DistributorRegisters.InterruptClearPending_0,
1067                 BuildInterruptClearPendingRegisters(irqsDecoder.SoftwareGeneratedFirst, irqsDecoder.SharedPeripheralLast, "InterruptClearPending")
1068             );
1069 
1070             Utils.AddRegistersAtOffset(registersMap, (long)DistributorRegisters.InterruptGroup_0,
1071                 BuildInterruptGroupRegisters(irqsDecoder.SoftwareGeneratedFirst, irqsDecoder.SharedPeripheralLast, "InterruptGroup")
1072             );
1073 
1074             // The range between 0xD00-0xDFC is implementation defined for GICv1 and GICv2.
1075             if(ArchitectureVersionAtLeast3)
1076             {
1077                 Utils.AddRegistersAtOffset(registersMap, (long)DistributorRegisters.InterruptGroupModifier_0_PPIStatus,
1078                     BuildInterruptGroupModifierRegisters(irqsDecoder.SoftwareGeneratedFirst, irqsDecoder.SharedPeripheralLast, "InterruptGroupModifier")
1079                 );
1080             }
1081             else
1082             {
1083                 // See e.g. https://developer.arm.com/documentation/ddi0416/b/programmers-model/distributor-register-descriptions and https://developer.arm.com/documentation/ddi0471/b/programmers-model/distributor-register-summary
1084                 Utils.AddRegistersAtOffset(registersMap, (long)DistributorRegisters.InterruptGroupModifier_0_PPIStatus,
1085                     BuildPrivateOrSharedPeripheralInterruptStatusRegisters(irqsDecoder.PrivatePeripheralFirst, irqsDecoder.PrivatePeripheralLast, "PPI Status")
1086                 );
1087 
1088                 Utils.AddRegistersAtOffset(registersMap, (long)DistributorRegisters.InterruptGroupModifier_1_SPIStatus_0,
1089                     BuildPrivateOrSharedPeripheralInterruptStatusRegisters(irqsDecoder.SharedPeripheralFirst, irqsDecoder.SharedPeripheralLast, "SPI Status")
1090                 );
1091             }
1092 
1093             return registersMap;
1094         }
1095 
BuildDistributorQuadWordRegistersMap()1096         private Dictionary<long, QuadWordRegister> BuildDistributorQuadWordRegistersMap()
1097         {
1098             var registersMap = new Dictionary<long, QuadWordRegister>();
1099             Utils.AddRegistersAtOffset(registersMap, (long)DistributorRegisters.InterruptRouting_0,
1100                 BuildInterruptRoutingRegisters(irqsDecoder.SharedPeripheralFirst, irqsDecoder.SharedPeripheralLast)
1101             );
1102             return registersMap;
1103         }
1104 
BuildDistributorRegistersMapSecurityView(bool accessForDisabledSecurity, SecurityState? securityStateAccess = null)1105         private Dictionary<long, DoubleWordRegister> BuildDistributorRegistersMapSecurityView(bool accessForDisabledSecurity, SecurityState? securityStateAccess = null)
1106         {
1107             var controlRegister = new DoubleWordRegister(this)
1108                 .WithFlag(31, FieldMode.Read, name: "RegisterWritePending", valueProviderCallback: _ => false);
1109             var registersMap = new Dictionary<long, DoubleWordRegister>
1110             {
1111                 {(long)DistributorRegisters.Control, controlRegister}
1112             };
1113 
1114             if(accessForDisabledSecurity)
1115             {
1116                 controlRegister
1117                     .WithReservedBits(9, 22)
1118                     .WithTaggedFlag("nASSGIreq", 8) // Requires FEAT_GICv4p1 support
1119                     .WithFlag(7, FieldMode.Read, name: "Enable1ofNWakeup", valueProviderCallback: _ => false) // There is no support for waking up
1120                     .WithFlag(6, FieldMode.Read, name: "DisableSecurity", valueProviderCallback: _ => true)
1121                     .WithReservedBits(5, 1)
1122                     .WithFlag(4, name: "EnableAffinityRouting",
1123                         writeCallback: (_, value) => AffinityRoutingEnabledSecure = value,
1124                         valueProviderCallback: _ => AffinityRoutingEnabledSecure
1125                     )
1126                     .WithReservedBits(2, 2)
1127                     .WithFlag(1, name: "EnableGroup1",
1128                         writeCallback: (_, val) => groups[GroupType.Group1].Enabled = val,
1129                         valueProviderCallback: _ => groups[GroupType.Group1].Enabled
1130                     )
1131                     .WithFlag(0, name: "EnableGroup0",
1132                         writeCallback: (_, val) => groups[GroupType.Group0].Enabled = val,
1133                         valueProviderCallback: _ => groups[GroupType.Group0].Enabled
1134                     );
1135             }
1136             else if(securityStateAccess == SecurityState.Secure)
1137             {
1138                 controlRegister
1139                     .WithReservedBits(8, 23)
1140                     .WithFlag(7, FieldMode.Read, name: "Enable1ofNWakeUp", valueProviderCallback: _ => false) // There is no support for waking up
1141                     .WithFlag(6, name: "DisableSecurity",
1142                         writeCallback: (_, val) => DisabledSecurity = val,
1143                         valueProviderCallback: _ => DisabledSecurity
1144                     )
1145                     .WithFlag(5, name: "EnableAffinityRoutingNonSecure",
1146                         writeCallback: (_, value) => AffinityRoutingEnabledNonSecure = value,
1147                         valueProviderCallback: _ => AffinityRoutingEnabledNonSecure
1148                     )
1149                     .WithFlag(4, name: "EnableAffinityRoutingSecure",
1150                         writeCallback: (_, value) => AffinityRoutingEnabledSecure = value,
1151                         valueProviderCallback: _ => AffinityRoutingEnabledSecure
1152                     )
1153                     .WithReservedBits(3, 1)
1154                     .WithFlag(2, name: "EnableGroup1Secure",
1155                         writeCallback: (_, val) => groups[GroupType.Group1Secure].Enabled = val,
1156                         valueProviderCallback: _ => groups[GroupType.Group1Secure].Enabled
1157                     )
1158                     .WithFlag(1, name: "EnableGroup1NonSecure",
1159                         writeCallback: (_, val) => groups[GroupType.Group1NonSecure].Enabled = val,
1160                         valueProviderCallback: _ => groups[GroupType.Group1NonSecure].Enabled
1161                     )
1162                     .WithFlag(0, name: "EnableGroup0",
1163                         writeCallback: (_, val) => groups[GroupType.Group0].Enabled = val,
1164                         valueProviderCallback: _ => groups[GroupType.Group0].Enabled
1165                     );
1166                 registersMap.Add((long)DistributorRegisters.NonSecureAccessControl_0, new DoubleWordRegister(this)
1167                     .WithEnumFields<DoubleWordRegister, NonSecureAccess>(0, 2, 16, name: "NS_access",
1168                         writeCallback: (i, _, val) =>
1169                         {
1170                             var cpu = GetAskingCPUEntry();
1171                             if(IsAffinityRoutingEnabled(cpu))
1172                             {
1173                                 cpu.NonSecureSGIAccess[i] = val;
1174                             }
1175                         },
1176                         valueProviderCallback: (i, _) =>
1177                         {
1178                             var cpu = GetAskingCPUEntry();
1179                             return IsAffinityRoutingEnabled(cpu) ? (NonSecureAccess)0 : cpu.NonSecureSGIAccess[i];
1180                         }
1181                     )
1182                     // Those could be emitted in valueProvider/writeCallback instead,
1183                     // but we don't want to emit the same warning 16 times per access.
1184                     .WithWriteCallback((_, __) =>
1185                     {
1186                         var cpu = GetAskingCPUEntry();
1187                         if(IsAffinityRoutingEnabled(cpu))
1188                         {
1189                             this.Log(LogLevel.Warning, "Tried to write to GICD_NSACR0 when affinity routing is enabled. Access ignored, use GICR_NSACR instead.");
1190                         }
1191                     })
1192                     .WithReadCallback((_, __) =>
1193                     {
1194                         var cpu = GetAskingCPUEntry();
1195                         if(IsAffinityRoutingEnabled(cpu))
1196                         {
1197                             this.Log(LogLevel.Warning, "Tried to read from GICD_NSACR0 when affinity routing is enabled. Access ignored, use GICR_NSACR instead.");
1198                         }
1199                     })
1200                 );
1201                 // These registers do not support PPIs, therefore GICD_NSACR1 is RAZ/WI
1202                 registersMap.Add((long)DistributorRegisters.NonSecureAccessControl_0 + 4, new DoubleWordRegister(this)
1203                     .WithValueFields(0, 2, 16, FieldMode.Read, name: "NS_access", valueProviderCallback: (_, __) => 0)
1204                 );
1205                 for(var j = 2; j < 64; ++j)
1206                 {
1207                     var i = j;
1208                     registersMap.Add((long)DistributorRegisters.NonSecureAccessControl_0 + 4 * i, new DoubleWordRegister(this)
1209                         .WithValueFields(0, 2, 16, name: "NS_access", valueProviderCallback: (_, __) => 0)
1210                         .WithReadCallback((_, __) => this.Log(LogLevel.Warning, "GICD_NSACR{0} is not implemented yet", i))
1211                         .WithWriteCallback((_, __) => this.Log(LogLevel.Warning, "GICD_NSACR{0} is not implemented yet", i))
1212                     );
1213                 }
1214             }
1215             else
1216             {
1217                 controlRegister
1218                     .WithReservedBits(5, 26)
1219                     .WithFlag(4, name: "EnableAffinityRoutingNonSecure",
1220                         writeCallback: (_, value) => AffinityRoutingEnabledNonSecure = value,
1221                         valueProviderCallback: _ => AffinityRoutingEnabledNonSecure
1222                     )
1223                     .WithReservedBits(2, 2)
1224                     .WithFlag(1, name: "EnableGroup1NonSecureAlias",
1225                         writeCallback: (_, val) => groups[GroupType.Group1NonSecure].Enabled = val,
1226                         valueProviderCallback: _ => groups[GroupType.Group1NonSecure].Enabled
1227                     )
1228                     .WithFlag(0, name: "EnableGroup1NonSecureAlias",
1229                         writeCallback: (_, val) => groups[GroupType.Group1NonSecure].Enabled = val,
1230                         valueProviderCallback: _ => groups[GroupType.Group1NonSecure].Enabled
1231                     );
1232             }
1233 
1234             return registersMap;
1235         }
1236 
BuildCPUInterfaceSystemRegistersMap()1237         private Dictionary<long, QuadWordRegister> BuildCPUInterfaceSystemRegistersMap()
1238         {
1239             var registersMap = new Dictionary<long, QuadWordRegister>
1240             {
1241                 {(long)CPUInterfaceSystemRegisters.SystemRegisterEnableEL3, new QuadWordRegister(this)
1242                     .WithReservedBits(4, 60)
1243                     .WithFlag(3, FieldMode.Read, name: "EnableAccessOnLowerThanEL3", valueProviderCallback: _ => true)
1244                     .WithFlag(2, FieldMode.Read, name: "DisableIRQBypass", valueProviderCallback: _ => true)
1245                     .WithFlag(1, FieldMode.Read, name: "DisableFIQBypass", valueProviderCallback: _ => true)
1246                     .WithFlag(0, FieldMode.Read, name: "EnableSystemRegisterAccess", valueProviderCallback: _ => true)
1247                 },
1248                 {(long)CPUInterfaceSystemRegisters.SystemRegisterEnableEL2, new QuadWordRegister(this)
1249                     .WithReservedBits(4, 60)
1250                     .WithFlag(3, FieldMode.Read, name: "EnableAccessOnLowerThanEL2", valueProviderCallback: _ => true)
1251                     .WithFlag(2, FieldMode.Read, name: "DisableIRQBypass", valueProviderCallback: _ => true)
1252                     .WithFlag(1, FieldMode.Read, name: "DisableFIQBypass", valueProviderCallback: _ => true)
1253                     .WithFlag(0, FieldMode.Read, name: "EnableSystemRegisterAccess", valueProviderCallback: _ => true)
1254                 },
1255                 {(long)CPUInterfaceSystemRegisters.SystemRegisterEnableEL1, new QuadWordRegister(this)
1256                     .WithReservedBits(3, 61)
1257                     .WithFlag(2, FieldMode.Read, name: "DisableIRQBypass", valueProviderCallback: _ => true)
1258                     .WithFlag(1, FieldMode.Read, name: "DisableFIQBypass", valueProviderCallback: _ => true)
1259                     .WithFlag(0, FieldMode.Read, name: "EnableSystemRegisterAccess", valueProviderCallback: _ => true)
1260                 },
1261                 {(long)CPUInterfaceSystemRegisters.GroupEnable0, new QuadWordRegister(this)
1262                     .WithReservedBits(1, 63)
1263                     .WithFlag(0, name: "EnableGroup0",
1264                         valueProviderCallback: _ => GetAskingCPUEntry().Groups[GroupType.Group0].Enabled,
1265                         writeCallback: (_, val) => GetAskingCPUEntry().Groups[GroupType.Group0].Enabled = val
1266                     )
1267                 },
1268                 {(long)CPUInterfaceSystemRegisters.GroupEnable1, new QuadWordRegister(this)
1269                     .WithReservedBits(1, 63)
1270                     .WithFlag(0, name: "EnableGroup1",
1271                         valueProviderCallback: _ => GetAskingCPUEntry().GetGroupForRegisterSecurityAgnostic(GroupTypeSecurityAgnostic.Group1).Enabled,
1272                         writeCallback: (_, val) => GetAskingCPUEntry().GetGroupForRegisterSecurityAgnostic(GroupTypeSecurityAgnostic.Group1).Enabled = val
1273                     )
1274                 },
1275                 {(long)CPUInterfaceSystemRegisters.GroupEnable1EL3, new QuadWordRegister(this)
1276                     .WithReservedBits(2, 62)
1277                     .WithFlag(1, name: "EnableGroup1S",
1278                         valueProviderCallback: _ => GetAskingCPUEntry().Groups[GroupType.Group1Secure].Enabled,
1279                         writeCallback: (_, val) => GetAskingCPUEntry().Groups[GroupType.Group1Secure].Enabled = val
1280                     )
1281                     .WithFlag(0, name: "EnableGroup1NS",
1282                         valueProviderCallback: _ => GetAskingCPUEntry().Groups[GroupType.Group1NonSecure].Enabled,
1283                         writeCallback: (_, val) => GetAskingCPUEntry().Groups[GroupType.Group1NonSecure].Enabled = val
1284                     )
1285                 },
1286                 {(long)CPUInterfaceSystemRegisters.RunningPriority, new QuadWordRegister(this)
1287                     .WithTaggedFlag("PriorityFromNonMaskableInterrupt", 63) // Requires FEAT_GICv3_NMI extension
1288                     .WithTaggedFlag("PriorityFromNonSecureNonMaskableInterrupt", 62) // Requires FEAT_GICv3_NMI extension
1289                     .WithReservedBits(8, 54)
1290                     .WithEnumField<QuadWordRegister, InterruptPriority>(0, 8, FieldMode.Read, name: "RunningPriority",
1291                         valueProviderCallback: _ => GetAskingCPUEntry().RunningInterrupts.Priority
1292                     )
1293                 },
1294                 {(long)CPUInterfaceSystemRegisters.PriorityMask, new QuadWordRegister(this)
1295                     .WithReservedBits(8, 56)
1296                     .WithEnumField<QuadWordRegister, InterruptPriority>(0, 8, name: "PriorityMask",
1297                         writeCallback: (_, val) => GetAskingCPUEntry().PriorityMask = val,
1298                         valueProviderCallback: _ => GetAskingCPUEntry().PriorityMask
1299                     )
1300                 },
1301                 {(long)CPUInterfaceSystemRegisters.InterruptAcknowledgeGroup0,
1302                     BuildInterruptAcknowledgeRegister(new QuadWordRegister(this), 64, "InterruptAcknowledgeGroup0", () => GroupTypeSecurityAgnostic.Group0, false)
1303                 },
1304                 {(long)CPUInterfaceSystemRegisters.InterruptAcknowledgeGroup1,
1305                     BuildInterruptAcknowledgeRegister(new QuadWordRegister(this), 64, "InterruptAcknowledgeGroup1", () => GroupTypeSecurityAgnostic.Group1, false)
1306                 },
1307                 {(long)CPUInterfaceSystemRegisters.InterruptDeactivate,
1308                     BuildInterruptDeactivateOrInterruptEndRegister(new QuadWordRegister(this), 64, "InterruptDeactivate", null,
1309                         useCPUIdentifier: false, isDeactivateRegister: true)
1310                 },
1311                 {(long)CPUInterfaceSystemRegisters.InterruptEndGroup0,
1312                     BuildInterruptDeactivateOrInterruptEndRegister(new QuadWordRegister(this), 64, "InterruptEndGroup0", () => GroupTypeSecurityAgnostic.Group0,
1313                         useCPUIdentifier: false, isDeactivateRegister: false)
1314                 },
1315                 {(long)CPUInterfaceSystemRegisters.InterruptEndGroup1,
1316                     BuildInterruptDeactivateOrInterruptEndRegister(new QuadWordRegister(this), 64, "InterruptEndGroup1", () => GroupTypeSecurityAgnostic.Group1,
1317                         useCPUIdentifier: false, isDeactivateRegister: false)
1318                 },
1319                 {(long)CPUInterfaceSystemRegisters.ControlEL1, new QuadWordRegister(this)
1320                     .WithReservedBits(20, 44)
1321                     .WithTaggedFlag("ExtendedINTIDRange", 19)
1322                     .WithTaggedFlag("RangeSelectorSupport", 18)
1323                     .WithReservedBits(16, 2)
1324                     .WithTaggedFlag("Affinity3Valid", 15)
1325                     .WithTaggedFlag("SEISupport", 14)
1326                     .WithTag("Identifier bits", 11, 3)
1327                     .WithTag("PriorityBits", 8, 3)
1328                     .WithReservedBits(7, 1)
1329                     .WithTaggedFlag("PriorityMaskHintEnable", 6)
1330                     .WithReservedBits(2, 4)
1331                     .WithFlag(1, name: "EndOfInterruptMode",
1332                         writeCallback: (_, val) => GetAskingCPUEntry().EndOfInterruptModeEL1 = (EndOfInterruptModes)(val ? 1 : 0),
1333                         valueProviderCallback: _ => GetAskingCPUEntry().EndOfInterruptModeEL1 == EndOfInterruptModes.PriorityDropOnly
1334                     )
1335                     .WithTaggedFlag("CommonBinaryPointRegister", 0)
1336                 },
1337                 {(long)CPUInterfaceSystemRegisters.ControlEL3, new QuadWordRegister(this)
1338                     .WithReservedBits(20, 44)
1339                     .WithTaggedFlag("ExtendedINTIDRange", 19)
1340                     .WithTaggedFlag("RangeSelectorSupport", 18)
1341                     .WithTaggedFlag("DisableSecurityNotSupported", 17)
1342                     .WithReservedBits(16, 1)
1343                     .WithTaggedFlag("Affinity3Valid", 15)
1344                     .WithTaggedFlag("SEISupport", 14)
1345                     .WithTag("Identifier bits", 11, 3)
1346                     .WithTag("PriorityBits", 8, 3)
1347                     .WithReservedBits(7, 1)
1348                     .WithTaggedFlag("PriorityMaskHintEnable", 6)
1349                     .WithTaggedFlag("RoutingModifier", 5)
1350                     .WithFlag(4, name: "EndOfInterruptModeEL1NonSecure",
1351                         writeCallback: (_, val) => GetAskingCPUEntry().EndOfInterruptModeEL1NonSecure = (EndOfInterruptModes)(val ? 1 : 0),
1352                         valueProviderCallback: _ => GetAskingCPUEntry().EndOfInterruptModeEL1NonSecure == EndOfInterruptModes.PriorityDropOnly
1353                     )
1354                     .WithFlag(3, name: "EndOfInterruptModeEL1Secure",
1355                         writeCallback: (_, val) => GetAskingCPUEntry().EndOfInterruptModeEL1Secure = (EndOfInterruptModes)(val ? 1 : 0),
1356                         valueProviderCallback: _ => GetAskingCPUEntry().EndOfInterruptModeEL1Secure == EndOfInterruptModes.PriorityDropOnly
1357                     )
1358                     .WithFlag(2, name: "EndOfInterruptModeEL3",
1359                         writeCallback: (_, val) => GetAskingCPUEntryWithTwoSecurityStates().EndOfInterruptModeEL3 = (EndOfInterruptModes)(val ? 1 : 0),
1360                         valueProviderCallback: _ => GetAskingCPUEntryWithTwoSecurityStates().EndOfInterruptModeEL3 == EndOfInterruptModes.PriorityDropOnly
1361                     )
1362                     .WithTaggedFlag("CommonBinaryPointRegisterEL1NonSecure", 1)
1363                     .WithTaggedFlag("CommonBinaryPointRegisterEL1Secure", 0)
1364                 },
1365                 {(long)CPUInterfaceSystemRegisters.HypControl, new QuadWordRegister(this)
1366                     .WithReservedBits(32, 32)
1367                     .WithValueField(27, 5, FieldMode.Read, name: "EOIcount",
1368                         valueProviderCallback: _ => GetAskingCPUEntry().EOICount
1369                     )
1370                     .WithReservedBits(16, 11)
1371                     .WithTaggedFlag("DVIM", 15) // Reserved when ICH_VTR_EL2.DVIM = 0
1372                     .WithTaggedFlag("TDIR", 14) // Reserved when FEAT_GICv3_TDIR is not implemented
1373                     .WithTaggedFlag("TSEI", 13)
1374                     .WithTaggedFlag("TALL1", 12)
1375                     .WithTaggedFlag("TALL0", 11)
1376                     .WithTaggedFlag("TC", 10)
1377                     .WithReservedBits(9, 1)
1378                     .WithTaggedFlag("vSGIEOICount", 8) // Reserved when FEAT_GICv4p1 is not implemented
1379                     .WithTaggedFlag("VGrp0DIE", 7)
1380                     .WithTaggedFlag("VGrp1EIE", 6)
1381                     .WithTaggedFlag("VGrp0DIE", 5)
1382                     .WithTaggedFlag("VGrp0EIE", 4)
1383                     .WithTaggedFlag("NPIE", 3)
1384                     .WithTaggedFlag("LRENPIE", 2)
1385                     .WithTaggedFlag("UIE", 1)
1386                     .WithFlag(0, name: "En",
1387                         valueProviderCallback: _ => GetAskingCPUEntry().VirtualCPUInterfaceEnabled,
1388                         writeCallback: (_, val) => GetAskingCPUEntry().VirtualCPUInterfaceEnabled = val
1389                     )
1390                 },
1391                 {(long)CPUInterfaceSystemRegisters.VGICType, new QuadWordRegister(this)
1392                     .WithReservedBits(32, 32)
1393                     .WithValueField(29, 3, FieldMode.Read, name: "PRIbits",
1394                         valueProviderCallback: _ => VirtualPriorityBits - 1
1395                     )
1396                     .WithTag("PREbits", 26, 3)
1397                     .WithTag("IDbits", 23, 3)
1398                     .WithTaggedFlag("SEIS", 22)
1399                     .WithTaggedFlag("A3V", 21)
1400                     .WithTaggedFlag("nV4", 20)
1401                     .WithTaggedFlag("TDS", 19)
1402                     .WithTaggedFlag("DVIM", 18)
1403                     .WithReservedBits(5, 13)
1404                     .WithValueField(0, 5, FieldMode.Read, name: "ListRegs",
1405                         valueProviderCallback: _ => VirtualInterruptCount - 1
1406                     )
1407                 },
1408                 {(long)CPUInterfaceSystemRegisters.VMControl, new QuadWordRegister(this)
1409                     .WithReservedBits(32, 32)
1410                     .WithEnumField<QuadWordRegister, InterruptPriority>(24, 8, name: "VPMR",
1411                         valueProviderCallback: _ => GetAskingCPUEntry().VirtualPriorityMask,
1412                         writeCallback: (_, val) => GetAskingCPUEntry().VirtualPriorityMask = val
1413                     )
1414                     .WithTag("VBPR0", 21, 3)
1415                     .WithTag("VBPR1", 18, 3)
1416                     .WithEnumField<QuadWordRegister, EndOfInterruptModes>(9, 1, name: "VEOIM",
1417                         valueProviderCallback: _ => GetAskingCPUEntry().EndOfInterruptModeVirtual,
1418                         writeCallback: (_, val) => GetAskingCPUEntry().EndOfInterruptModeVirtual = val
1419                     )
1420                     .WithReservedBits(5, 4)
1421                     .WithTaggedFlag("VCBPR", 4)
1422                     .WithFlag(3, name: "VFIQEn",
1423                         valueProviderCallback: _ => GetAskingCPUEntry().VirtualFIQEnabled,
1424                         writeCallback: (_, val) => GetAskingCPUEntry().VirtualFIQEnabled = val
1425                     )
1426                     .WithTaggedFlag("VAckCtl", 2)
1427                     .WithFlag(1, name: "VENG1",
1428                         valueProviderCallback: _ => GetAskingCPUEntry().Groups.Virtual[GroupType.Group1].Enabled,
1429                         writeCallback: (_, val) => GetAskingCPUEntry().Groups.Virtual[GroupType.Group1].Enabled = val
1430                     )
1431                     .WithFlag(0, name: "VENG0",
1432                         valueProviderCallback: _ => GetAskingCPUEntry().Groups.Virtual[GroupType.Group0].Enabled,
1433                         writeCallback: (_, val) => GetAskingCPUEntry().Groups.Virtual[GroupType.Group0].Enabled = val
1434                     )
1435                 },
1436                 {(long)CPUInterfaceSystemRegisters.SoftwareGeneratedInterruptGroup1Generate, BuildSGIGenerateRegister(() => GetGroup1ForSecurityState(GetAskingCPUEntry().IsStateSecure))},
1437                 {(long)CPUInterfaceSystemRegisters.SoftwareGeneratedInterruptGroup1GenerateAlias, BuildSGIGenerateRegister(() => GetGroup1ForSecurityState(!GetAskingCPUEntry().IsStateSecure))},
1438                 {(long)CPUInterfaceSystemRegisters.SoftwareGeneratedInterruptGroup0Generate, BuildSGIGenerateRegister(() => GroupType.Group0)},
1439             };
1440 
1441             for(int j = 0; j < VirtualInterruptCount; ++j)
1442             {
1443                 var i = j;
1444 
1445                 Func<VirtualInterrupt> virtualInterrupt = () => GetAskingCPUEntry().VirtualInterrupts[i];
1446                 Action syncInterruptState = () =>
1447                 {
1448                     var currentInterrupt = virtualInterrupt();
1449                     var activeInterrupt = GetAskingCPUEntry().RunningInterrupts.GetActiveVirtual(currentInterrupt.Identifier);
1450                     if(activeInterrupt != null)
1451                     {
1452                         activeInterrupt.Sync(currentInterrupt);
1453                     }
1454                 };
1455 
1456                 registersMap.Add((long)CPUInterfaceSystemRegisters.ListRegister_0 + i, new QuadWordRegister(this)
1457                     .WithValueField(62, 2, name: "State",
1458                         valueProviderCallback: _ => virtualInterrupt().State.Bits,
1459                         writeCallback: (_, val) => virtualInterrupt().State.Bits = val
1460                     )
1461                     .WithFlag(61, name: "HW",
1462                         valueProviderCallback: _ => virtualInterrupt().Hardware,
1463                         writeCallback: (_, val) => virtualInterrupt().Hardware = val
1464                     )
1465                     .WithFlag(60, name: "Group",
1466                         valueProviderCallback: _ => virtualInterrupt().Config.GroupBit,
1467                         writeCallback: (_, val) => virtualInterrupt().Config.GroupBit = val
1468                     )
1469                     .WithReservedBits(56, 4)
1470                     .WithEnumField<QuadWordRegister, InterruptPriority>(48, 8, name: "Priority",
1471                         valueProviderCallback: _ => virtualInterrupt().Config.Priority,
1472                         writeCallback: (_, val) => virtualInterrupt().Config.Priority = val
1473                     )
1474                     .WithReservedBits(45, 3)
1475                     .WithValueField(32, 13, name: "pINTID",
1476                         valueProviderCallback: _ => (uint)virtualInterrupt().HardwareIdentifier,
1477                         writeCallback: (_, val) => virtualInterrupt().SetHardwareIdentifier((uint)val)
1478                     )
1479                     .WithValueField(0, 32, name: "vINTID",
1480                         valueProviderCallback: _ => (uint)virtualInterrupt().Identifier,
1481                         writeCallback: (_, val) => virtualInterrupt().SetIdentifier((uint)val)
1482                     )
1483                     .WithWriteCallback((_, __) => syncInterruptState())
1484                 );
1485 
1486                 registersMap.Add((long)CPUInterfaceSystemRegisters.ListRegisterUpper_0 + i, new QuadWordRegister(this)
1487                     .WithReservedBits(32, 32)
1488                     .WithValueField(30, 2, name: "State",
1489                         valueProviderCallback: _ => virtualInterrupt().State.Bits,
1490                         writeCallback: (_, val) => virtualInterrupt().State.Bits = val
1491                     )
1492                     .WithFlag(29, name: "HW",
1493                         valueProviderCallback: _ => virtualInterrupt().Hardware,
1494                         writeCallback: (_, val) => virtualInterrupt().Hardware = val
1495                     )
1496                     .WithFlag(28, name: "Group",
1497                         valueProviderCallback: _ => virtualInterrupt().Config.GroupBit,
1498                         writeCallback: (_, val) => virtualInterrupt().Config.GroupBit = val
1499                     )
1500                     .WithReservedBits(24, 4)
1501                     .WithEnumField<QuadWordRegister, InterruptPriority>(16, 8, name: "Priority",
1502                         valueProviderCallback: _ => virtualInterrupt().Config.Priority,
1503                         writeCallback: (_, val) => virtualInterrupt().Config.Priority = val
1504                     )
1505                     .WithReservedBits(13, 3)
1506                     .WithValueField(0, 13, name: "pINTID",
1507                         valueProviderCallback: _ => (uint)virtualInterrupt().HardwareIdentifier,
1508                         writeCallback: (_, val) => virtualInterrupt().SetHardwareIdentifier((uint)val)
1509                     )
1510                     .WithWriteCallback((_, __) => syncInterruptState())
1511                 );
1512             }
1513 
1514             return registersMap;
1515         }
1516 
BuildCPUInterfaceRegistersMap()1517         private Dictionary<long, DoubleWordRegister> BuildCPUInterfaceRegistersMap()
1518         {
1519             Func<GroupTypeSecurityAgnostic> getRegisterGroupTypeSecurityAgnostic = () => (DisabledSecurity || GetAskingCPUEntry().IsStateSecure) ? GroupTypeSecurityAgnostic.Group0 : GroupTypeSecurityAgnostic.Group1;
1520             var registersMap = new Dictionary<long, DoubleWordRegister>
1521             {
1522                 {(long)CPUInterfaceRegisters.InterfaceIdentification, new DoubleWordRegister(this)
1523                     .WithValueField(20, 12, FieldMode.Read, valueProviderCallback: _ => CPUInterfaceProductIdentifier, name: "ProductIdentifier")
1524                     .WithEnumField<DoubleWordRegister, ARM_GenericInterruptControllerVersion>(16, 4, FieldMode.Read, valueProviderCallback: _ => ArchitectureVersion, name: "ArchitectureVersion")
1525                     .WithValueField(12, 4, FieldMode.Read, valueProviderCallback: _ => CPUInterfaceRevision, name: "RevisionNumber")
1526                     .WithValueField(0, 12, FieldMode.Read, valueProviderCallback: _ => CPUInterfaceImplementer, name: "ImplementerIdentification")
1527                 },
1528                 {(long)CPUInterfaceRegisters.RunningPriority, new DoubleWordRegister(this)
1529                     .WithReservedBits(8, 24)
1530                     .WithEnumField<DoubleWordRegister, InterruptPriority>(0, 8, FieldMode.Read, name: "RunningPriority",
1531                         valueProviderCallback: _ => GetAskingCPUEntry().RunningInterrupts.Priority
1532                     )
1533                 },
1534                 {(long)CPUInterfaceRegisters.PriorityMask, new DoubleWordRegister(this)
1535                     .WithReservedBits(8, 24)
1536                     .WithEnumField<DoubleWordRegister, InterruptPriority>(0, 8, name: "PriorityMask",
1537                         writeCallback: (_, val) => GetAskingCPUEntry().PriorityMask = val,
1538                         valueProviderCallback: _ => GetAskingCPUEntry().PriorityMask
1539                     )
1540                 },
1541                 {(long)CPUInterfaceRegisters.InterruptAcknowledge,
1542                     BuildInterruptAcknowledgeRegister(new DoubleWordRegister(this), 32, "InterruptAcknowledge", getRegisterGroupTypeSecurityAgnostic, true)
1543                 },
1544                 {(long)CPUInterfaceRegisters.InterruptEnd,
1545                     BuildInterruptDeactivateOrInterruptEndRegister(new DoubleWordRegister(this), 32, "InterruptEnd", getRegisterGroupTypeSecurityAgnostic,
1546                         useCPUIdentifier: true, isDeactivateRegister: false)
1547                 },
1548                 {(long)CPUInterfaceRegisters.InterruptDeactivate,
1549                     BuildInterruptDeactivateOrInterruptEndRegister(new DoubleWordRegister(this), 32, "InterruptDeactivate", null,
1550                         useCPUIdentifier: true, isDeactivateRegister: true)
1551                 },
1552             };
1553 
1554             return registersMap;
1555         }
1556 
BuildCPUInterfaceRegistersMapSecurityView(bool accessForDisabledSecurity, SecurityState? securityStateAccess = null)1557         private Dictionary<long, DoubleWordRegister> BuildCPUInterfaceRegistersMapSecurityView(bool accessForDisabledSecurity, SecurityState? securityStateAccess = null)
1558         {
1559             var controlRegister = new DoubleWordRegister(this);
1560             if(accessForDisabledSecurity || securityStateAccess == SecurityState.Secure)
1561             {
1562                 controlRegister
1563                     .WithFlag(8, FieldMode.Read, name: "IRQBypassGroup1", valueProviderCallback: _ => false)
1564                     .WithFlag(7, FieldMode.Read, name: "FIQBypassGroup1", valueProviderCallback: _ => false)
1565                     .WithFlag(6, FieldMode.Read, name: "IRQBypassGroup0", valueProviderCallback: _ => false)
1566                     .WithFlag(5, FieldMode.Read, name: "FIQBypassGroup0", valueProviderCallback: _ => false)
1567                     .WithTaggedFlag("PreemptionControl", 4)
1568                     .WithFlag(3, name: "EnableFIQ",
1569                         writeCallback: (_, val) =>
1570                         {
1571                             if(!ArchitectureVersionAtLeast3)
1572                             {
1573                                 enableFIQ = val;
1574                             }
1575                             else
1576                             {
1577                                 this.Log(LogLevel.Warning, "Modifying EnableFIQ flag is not supported in this architecture version");
1578                             }
1579                         },
1580                         valueProviderCallback: _ => enableFIQ
1581                     )
1582                     .WithFlag(2, name: "AcknowledgementControl",
1583                         writeCallback: (_, val) => { if(val) this.Log(LogLevel.Warning, "Setting deprecated GICC_CTLR.AckCtl flag!"); ackControl = val; },
1584                         valueProviderCallback: _ => ackControl
1585                     )
1586                     .WithFlag(1, name: "EnableGroup1",
1587                         writeCallback: (_, val) => GetAskingCPUEntry().Groups[GroupType.Group1].Enabled = val,
1588                         valueProviderCallback: _ => GetAskingCPUEntry().Groups[GroupType.Group1].Enabled
1589                     )
1590                     .WithFlag(0, name: "EnableGroup0",
1591                         writeCallback: (_, val) => GetAskingCPUEntry().Groups[GroupType.Group0].Enabled = val,
1592                         valueProviderCallback: _ => GetAskingCPUEntry().Groups[GroupType.Group0].Enabled
1593                     );
1594 
1595                 if(accessForDisabledSecurity)
1596                 {
1597                     controlRegister
1598                         .WithReservedBits(10, 22)
1599                         .WithFlag(9, name: "EndOfInterruptMode",
1600                             writeCallback: (_, val) => GetAskingCPUEntry().EndOfInterruptModeEL1 = (EndOfInterruptModes)(val ? 1 : 0),
1601                             valueProviderCallback: _ => GetAskingCPUEntry().EndOfInterruptModeEL1 == EndOfInterruptModes.PriorityDropOnly
1602                         );
1603                 }
1604                 else
1605                 {
1606                     controlRegister
1607                         .WithReservedBits(11, 21)
1608                         .WithFlag(10, name: "EndOfInterruptModeNonSecure",
1609                             writeCallback: (_, val) => GetAskingCPUEntry().EndOfInterruptModeEL1NonSecure = (EndOfInterruptModes)(val ? 1 : 0),
1610                             valueProviderCallback: _ => GetAskingCPUEntry().EndOfInterruptModeEL1NonSecure == EndOfInterruptModes.PriorityDropOnly
1611                         )
1612                         .WithFlag(9, name: "EndOfInterruptModeSecure",
1613                             writeCallback: (_, val) => GetAskingCPUEntry().EndOfInterruptModeEL1Secure = (EndOfInterruptModes)(val ? 1 : 0),
1614                             valueProviderCallback: _ => GetAskingCPUEntry().EndOfInterruptModeEL1Secure == EndOfInterruptModes.PriorityDropOnly
1615                         );
1616                 }
1617             }
1618             else
1619             {
1620                 controlRegister
1621                     .WithReservedBits(10, 22)
1622                     .WithFlag(9, name: "EndOfInterruptModeNonSecure",
1623                         writeCallback: (_, val) => GetAskingCPUEntry().EndOfInterruptModeEL1NonSecure = (EndOfInterruptModes)(val ? 1 : 0),
1624                         valueProviderCallback: _ => GetAskingCPUEntry().EndOfInterruptModeEL1NonSecure == EndOfInterruptModes.PriorityDropOnly
1625                     )
1626                     .WithReservedBits(7, 2)
1627                     .WithFlag(6, FieldMode.Read, name: "IRQBypassGroup1", valueProviderCallback: _ => false)
1628                     .WithFlag(5, FieldMode.Read, name: "FIQBypassGroup1", valueProviderCallback: _ => false)
1629                     .WithReservedBits(1, 4)
1630                     .WithFlag(0, name: "EnableGroup1",
1631                         writeCallback: (_, val) => GetAskingCPUEntry().Groups[GroupType.Group1].Enabled = val,
1632                         valueProviderCallback: _ => GetAskingCPUEntry().Groups[GroupType.Group1].Enabled
1633                     );
1634             }
1635 
1636             var registersMap = new Dictionary<long, DoubleWordRegister>
1637             {
1638                 {(long)CPUInterfaceRegisters.Control, controlRegister},
1639 
1640                 // Aliases for acknowledging/ending non-secure interrupts in secure state.
1641                 {(long)CPUInterfaceRegisters.InterruptAcknowledgeAlias,
1642                     BuildInterruptAcknowledgeRegister(new DoubleWordRegister(this), 32, "InterruptAcknowledgeAlias", () => GroupTypeSecurityAgnostic.Group1, true)
1643                 },
1644                 {(long)CPUInterfaceRegisters.InterruptEndAlias,
1645                     BuildInterruptDeactivateOrInterruptEndRegister(new DoubleWordRegister(this), 32, "InterruptEndAlias", () => GroupTypeSecurityAgnostic.Group1,
1646                         useCPUIdentifier: true, isDeactivateRegister: false)
1647                 },
1648             };
1649             return registersMap;
1650         }
1651 
BuildSGIGenerateRegister(Func<GroupType> getGroupType)1652         private QuadWordRegister BuildSGIGenerateRegister(Func<GroupType> getGroupType)
1653         {
1654             return new QuadWordRegister(this)
1655                 .WithReservedBits(56, 8)
1656                 .WithValueField(48, 8, out var affinity3, FieldMode.Write, name: "Affinity3")
1657                 .WithValueField(44, 4, out var rangeSelector, FieldMode.Write, name: "Range Selector")
1658                 .WithReservedBits(41, 3)
1659                 .WithFlag(40, out var interruptRoutingMode, FieldMode.Write, name: "Interrupt Routing Mode")
1660                 .WithValueField(32, 8, out var affinity2, FieldMode.Write, name: "Affinity2")
1661                 .WithReservedBits(28, 4)
1662                 .WithValueField(24, 4, out var interruptID, FieldMode.Write, name: "Interrupt ID")
1663                 .WithValueField(16, 8, out var affinity1, FieldMode.Write, name: "Affinity1")
1664                 .WithReservedBits(5, 11)
1665                 .WithValueField(0, 5, out var targetList)
1666                 .WithWriteCallback((_, newValue) =>
1667                 {
1668                     var targetType = interruptRoutingMode.Value ? SoftwareGeneratedInterruptRequest.TargetType.AllCPUs : SoftwareGeneratedInterruptRequest.TargetType.TargetList;
1669 
1670                     var list = new Affinity[]{};
1671                     if(targetType == SoftwareGeneratedInterruptRequest.TargetType.TargetList)
1672                     {
1673                         var range = 16 * (byte)rangeSelector.Value;
1674                         var aff1 = (byte)affinity1.Value;
1675                         var aff2 = (byte)affinity2.Value;
1676                         var aff3 = (byte)affinity3.Value;
1677                         list = BitHelper.GetSetBits(targetList.Value).Select(n => new Affinity((byte)(range + n), aff1, aff2, aff3)).ToArray();
1678                     }
1679 
1680                     var interrupt = new SoftwareGeneratedInterruptRequest(targetType, list, getGroupType(), new InterruptId((uint)interruptID.Value));
1681                     OnSoftwareGeneratedInterrupt(GetAskingCPUEntry(), interrupt);
1682                 });
1683         }
1684 
1685         private T BuildInterruptAcknowledgeRegister<T>(T register, int registerWidth, string name,
1686             Func<GroupTypeSecurityAgnostic> groupTypeRegisterProvider, bool useCPUIdentifier) where T : PeripheralRegister
1687         {
1688             return register
1689                 .WithReservedBits(24, registerWidth - 24)
1690                 .WithValueField(0, 24, FieldMode.Read, name: name,
1691                     valueProviderCallback: _ =>
1692                     {
1693                         var irqId = (uint)GetAskingCPUEntry().AcknowledgeBestPending(groupTypeRegisterProvider(), out var requester);
1694                         var cpuId = useCPUIdentifier ? requester?.ProcessorNumber ?? 0 : 0;
1695                         return cpuId << 10 | irqId;
1696                     }
1697                 );
1698         }
1699 
1700         private T BuildInterruptDeactivateOrInterruptEndRegister<T>(T register, int registerWidth, string name,
1701             Func<GroupTypeSecurityAgnostic> groupTypeRegisterProvider, bool useCPUIdentifier, bool isDeactivateRegister) where T : PeripheralRegister
1702         {
1703             return register
1704                 .WithReservedBits(24, registerWidth - 24)
1705                 .WithValueField(0, 24, FieldMode.Write, name: name,
1706                     writeCallback: (_, val) =>
1707                     {
1708                         var irqId = BitHelper.GetValue((uint)val, 0, 10);
1709                         CPUEntry cpu = null;
1710                         var cpuId = BitHelper.GetValue((uint)val, 10, 3);
1711                         if(useCPUIdentifier && !TryGetCPUEntry(cpuId, out cpu))
1712                         {
1713                             this.Log(LogLevel.Warning, "Trying to {0} the interrupt ({1}) for the non-existing CPU ({2}), write ignored.",
1714                                 isDeactivateRegister ? "deactivate" : "end", irqId, cpuId);
1715                             return;
1716                         }
1717                         var askingCPUEntry = GetAskingCPUEntry();
1718                         if(isDeactivateRegister)
1719                         {
1720                             askingCPUEntry.InterruptDeactivate(new InterruptId(irqId), cpu);
1721                         }
1722                         else
1723                         {
1724                             askingCPUEntry.InterruptEnd(new InterruptId(irqId), groupTypeRegisterProvider(), cpu);
1725                         }
1726                     }
1727                 );
1728         }
1729 
BuildInterruptRoutingRegisters(InterruptId startId, InterruptId endId)1730         private IEnumerable<QuadWordRegister> BuildInterruptRoutingRegisters(InterruptId startId, InterruptId endId)
1731         {
1732             return InterruptId.GetRange(startId, endId).Select(
1733                 id => new QuadWordRegister(this)
1734                     .WithReservedBits(40, 24)
1735                     .WithValueField(32, 8, name: $"Affinity3_{(uint)id}",
1736                         writeCallback: (_, val) => { if(IsAffinityRoutingEnabled(GetAskingCPUEntry())) sharedInterrupts[id].TargetAffinity.SetLevel(3, (byte)val); },
1737                         valueProviderCallback: _ => IsAffinityRoutingEnabled(GetAskingCPUEntry()) ? (ulong)sharedInterrupts[id].TargetAffinity.GetLevel(3) : 0
1738                     )
1739                     .WithEnumField<QuadWordRegister, InterruptRoutingMode>(31, 1, name: $"RoutingMode_{(uint)id}",
1740                         writeCallback: (_, val) => { if(IsAffinityRoutingEnabled(GetAskingCPUEntry())) sharedInterrupts[id].RoutingMode = val; },
1741                         valueProviderCallback: _ => IsAffinityRoutingEnabled(GetAskingCPUEntry()) ? sharedInterrupts[id].RoutingMode : default(InterruptRoutingMode)
1742                     )
1743                     .WithReservedBits(24, 7)
1744                     .WithValueField(16, 8, name: $"Affinity2_{(uint)id}",
1745                         writeCallback: (_, val) => { if(IsAffinityRoutingEnabled(GetAskingCPUEntry())) sharedInterrupts[id].TargetAffinity.SetLevel(2, (byte)val); },
1746                         valueProviderCallback: _ => IsAffinityRoutingEnabled(GetAskingCPUEntry()) ? (ulong)sharedInterrupts[id].TargetAffinity.GetLevel(2) : 0
1747                     )
1748                     .WithValueField(8, 8, name: $"Affinity1_{(uint)id}",
1749                         writeCallback: (_, val) => { if(IsAffinityRoutingEnabled(GetAskingCPUEntry())) sharedInterrupts[id].TargetAffinity.SetLevel(1, (byte)val); },
1750                         valueProviderCallback: _ => IsAffinityRoutingEnabled(GetAskingCPUEntry()) ? (ulong)sharedInterrupts[id].TargetAffinity.GetLevel(1) : 0
1751                     )
1752                     .WithValueField(0, 8, name: $"Affinity0_{(uint)id}",
1753                         writeCallback: (_, val) => { if(IsAffinityRoutingEnabled(GetAskingCPUEntry())) sharedInterrupts[id].TargetAffinity.SetLevel(0, (byte)val); },
1754                         valueProviderCallback: _ => IsAffinityRoutingEnabled(GetAskingCPUEntry()) ? (ulong)sharedInterrupts[id].TargetAffinity.GetLevel(0) : 0
1755                     )
1756                     .WithWriteCallback((_, __) => { if(!IsAffinityRoutingEnabled(GetAskingCPUEntry())) this.Log(LogLevel.Warning, "Trying to write IROUTER register when Affinity Routing is disabled, write ignored."); })
1757                     .WithReadCallback((_, __) => { if(!IsAffinityRoutingEnabled(GetAskingCPUEntry())) this.Log(LogLevel.Warning, "Trying to read IROUTER register when Affinity Routing is disabled."); })
1758             );
1759         }
1760 
BuildInterruptFlagRegisters(InterruptId startId, InterruptId endId, string name, Action<Interrupt, bool> writeCallback = null, Func<Interrupt, bool> valueProviderCallback = null, bool allowAccessWhenNonSecureGroup = true, CPUEntry sgiRequestingCPU = null, Func<CPUEntry> cpuEntryProvider = null)1761         private IEnumerable<DoubleWordRegister> BuildInterruptFlagRegisters(InterruptId startId, InterruptId endId, string name,
1762             Action<Interrupt, bool> writeCallback = null, Func<Interrupt, bool> valueProviderCallback = null, bool allowAccessWhenNonSecureGroup = true,
1763             CPUEntry sgiRequestingCPU = null, Func<CPUEntry> cpuEntryProvider = null)
1764         {
1765             const int BitsPerRegister = 32;
1766             return BuildInterruptRegisters(startId, endId, BitsPerRegister,
1767                 (register, irqGetter, irqId, fieldIndex) =>
1768                     {
1769                         FieldMode fieldMode = 0;
1770                         Action<bool, bool> writeCallbackWrapped = null;
1771                         if(writeCallback != null)
1772                         {
1773                             fieldMode |= FieldMode.Write;
1774                             writeCallbackWrapped = (_, val) => { if(CheckInterruptAccess(irqGetter, allowAccessWhenNonSecureGroup)) writeCallback(irqGetter(), val); };
1775                         }
1776                         Func<bool, bool> valueProviderCallbackWrapped = null;
1777                         if(valueProviderCallback != null)
1778                         {
1779                             fieldMode |= FieldMode.Read;
1780                             valueProviderCallbackWrapped = _ => CheckInterruptAccess(irqGetter, allowAccessWhenNonSecureGroup) ? valueProviderCallback(irqGetter()) : false;
1781                         }
1782                         register.WithFlag(fieldIndex, fieldMode, name: $"{name}_{(uint)irqId}",
1783                             writeCallback: writeCallbackWrapped, valueProviderCallback: valueProviderCallbackWrapped);
1784                     },
1785 
1786                 (register, fieldIndex) => register.WithReservedBits(fieldIndex, 1),
1787                 sgiRequestingCPU, cpuEntryProvider
1788             );
1789         }
1790 
BuildInterruptValueRegisters(InterruptId startId, InterruptId endId, string name, int fieldsPerRegister, Action<Interrupt, ulong> writeCallback = null, Func<Interrupt, ulong> valueProviderCallback = null, bool allowAccessWhenNonSecureGroup = true, CPUEntry sgiRequestingCPU = null, Func<CPUEntry> cpuEntryProvider = null)1791         private IEnumerable<DoubleWordRegister> BuildInterruptValueRegisters(InterruptId startId, InterruptId endId, string name, int fieldsPerRegister,
1792             Action<Interrupt, ulong> writeCallback = null, Func<Interrupt, ulong> valueProviderCallback = null, bool allowAccessWhenNonSecureGroup = true,
1793             CPUEntry sgiRequestingCPU = null, Func<CPUEntry> cpuEntryProvider = null)
1794         {
1795             const int registerWidth = 32;
1796             var fieldWidth = registerWidth / fieldsPerRegister;
1797             return BuildInterruptRegisters(startId, endId, fieldsPerRegister,
1798                 (register, irqGetter, irqId, fieldIndex) =>
1799                     {
1800                         FieldMode fieldMode = 0;
1801                         Action<ulong, ulong> writeCallbackWrapped = null;
1802                         if(writeCallback != null)
1803                         {
1804                             fieldMode |= FieldMode.Write;
1805                             writeCallbackWrapped = (_, val) => { if(CheckInterruptAccess(irqGetter, allowAccessWhenNonSecureGroup)) writeCallback(irqGetter(), val); };
1806                         }
1807                         Func<ulong, ulong> valueProviderCallbackWrapped = null;
1808                         if(valueProviderCallback != null)
1809                         {
1810                             fieldMode |= FieldMode.Read;
1811                             valueProviderCallbackWrapped = _ => CheckInterruptAccess(irqGetter, allowAccessWhenNonSecureGroup) ? valueProviderCallback(irqGetter()) : 0;
1812                         }
1813                         register.WithValueField(fieldIndex * fieldWidth, fieldWidth, fieldMode, name: $"{name}_{(uint)irqId}",
1814                             writeCallback: writeCallbackWrapped, valueProviderCallback: valueProviderCallbackWrapped);
1815                     },
1816                 (register, fieldIndex) => register.WithReservedBits(fieldIndex * fieldWidth, fieldWidth),
1817                 sgiRequestingCPU, cpuEntryProvider
1818             );
1819         }
1820 
1821         private IEnumerable<DoubleWordRegister> BuildInterruptEnumRegisters<TEnum>(InterruptId startId, InterruptId endId, string name, int fieldsPerRegister,
1822             Action<Interrupt, TEnum> writeCallback = null, Func<Interrupt, TEnum> valueProviderCallback = null, bool allowAccessWhenNonSecureGroup = true,
1823             CPUEntry sgiRequestingCPU = null, Func<CPUEntry> cpuEntryProvider = null) where TEnum : struct, IConvertible
1824         {
1825             const int registerWidth = 32;
1826             var fieldWidth = registerWidth / fieldsPerRegister;
1827             return BuildInterruptRegisters(startId, endId, fieldsPerRegister,
1828                 (register, irqGetter, irqId, fieldIndex) =>
1829                     {
1830                         FieldMode fieldMode = 0;
1831                         Action<TEnum, TEnum> writeCallbackWrapped = null;
1832                         if(writeCallback != null)
1833                         {
1834                             fieldMode |= FieldMode.Write;
1835                             writeCallbackWrapped = (_, val) => { if(CheckInterruptAccess(irqGetter, allowAccessWhenNonSecureGroup)) writeCallback(irqGetter(), val); };
1836                         }
1837                         Func<TEnum, TEnum> valueProviderCallbackWrapped = null;
1838                         if(valueProviderCallback != null)
1839                         {
1840                             fieldMode |= FieldMode.Read;
1841                             valueProviderCallbackWrapped = _ => CheckInterruptAccess(irqGetter, allowAccessWhenNonSecureGroup) ? valueProviderCallback(irqGetter()) : default(TEnum);
1842                         }
register.WithEnumFieldAntmicro.Renode.Peripherals.IRQControllers.ARM_GenericInterruptController.IConvertible1843                         register.WithEnumField<DoubleWordRegister, TEnum>(fieldIndex * fieldWidth, fieldWidth, fieldMode, name: $"{name}_{(uint)irqId}",
1844                             writeCallback: writeCallbackWrapped, valueProviderCallback: valueProviderCallbackWrapped);
1845                     },
1846                 (register, fieldIndex) => register.WithReservedBits(fieldIndex * fieldWidth, fieldWidth),
1847                 sgiRequestingCPU, cpuEntryProvider
1848             );
1849         }
1850 
IsAffinityRoutingEnabled(CPUEntry cpu)1851         private bool IsAffinityRoutingEnabled(CPUEntry cpu)
1852         {
1853             return DisabledSecurity || cpu.IsStateSecure ? AffinityRoutingEnabledSecure : AffinityRoutingEnabledNonSecure;
1854         }
1855 
CheckInterruptAccess(Func<Interrupt> irqGetter, bool allowAccessWhenNonSecureGroup)1856         private bool CheckInterruptAccess(Func<Interrupt> irqGetter, bool allowAccessWhenNonSecureGroup)
1857         {
1858             if(DisabledSecurity || GetAskingCPUEntry().IsStateSecure
1859                 || (allowAccessWhenNonSecureGroup && irqGetter().Config.GroupType == GroupType.Group1NonSecure))
1860             {
1861                 return true;
1862             }
1863             this.Log(LogLevel.Debug, "Trying to access a field of the interrupt {0}, which isn't accessible from the current security state.", irqGetter().Identifier);
1864             return false;
1865         }
1866 
BuildInterruptRegisters(InterruptId startId, InterruptId endId, int fieldsPerRegister, Action<DoubleWordRegister, Func<Interrupt>, InterruptId, int> fieldAction, Action<DoubleWordRegister, int> fieldPlaceholderAction, CPUEntry sgiRequestingCPU, Func<CPUEntry> cpuEntryProvider )1867         private IEnumerable<DoubleWordRegister> BuildInterruptRegisters(InterruptId startId, InterruptId endId, int fieldsPerRegister,
1868             Action<DoubleWordRegister, Func<Interrupt>, InterruptId, int> fieldAction,
1869             Action<DoubleWordRegister, int> fieldPlaceholderAction,
1870             // NOTE: sgiRequestingCPU is currently always null as we don't support registers like GICD_CPENDSGIRn which need this information
1871             CPUEntry sgiRequestingCPU, Func<CPUEntry> cpuEntryProvider
1872         )
1873         {
1874             if(cpuEntryProvider == null)
1875             {
1876                 cpuEntryProvider = GetAskingCPUEntry;
1877             }
1878             var interruptsCount = (int)endId - (int)startId + 1;
1879             var registersCount = (interruptsCount + fieldsPerRegister - 1) / fieldsPerRegister;
1880             var fieldIndex = 0;
1881             foreach(var registerFirstIrqId in InterruptId.GetRange(startId, endId, (uint)fieldsPerRegister))
1882             {
1883                 var register = new DoubleWordRegister(this);
1884                 var fieldsCount = fieldsPerRegister;
1885                 if(fieldIndex + fieldsCount > interruptsCount)
1886                 {
1887                     fieldsCount = interruptsCount % fieldsPerRegister;
1888                 }
1889                 foreach(var irqId in InterruptId.GetRange(registerFirstIrqId, new InterruptId((uint)registerFirstIrqId + (uint)fieldsCount - 1)))
1890                 {
1891                     var inRegisterIndex = fieldIndex % fieldsPerRegister;
1892                     if(irqsDecoder.Type(irqId) == InterruptType.Reserved)
1893                     {
1894                         fieldPlaceholderAction(register, inRegisterIndex);
1895                     }
1896                     else if(irqsDecoder.IsSoftwareGenerated(irqId))
1897                     {
1898                         if(sgiRequestingCPU == null)
1899                         {
1900                             // NOTE: We're returning here from SoftwareGeneratedInterruptsUnknownRequester despite some of the registers operating on
1901                             //       the LegacyRequester interrupts. This is fine, as all of those registers actually operate on InterruptConfig
1902                             //       and that's shared between interrupts in UnknownRequester and LegacyRequester.
1903                             fieldAction(register, () => cpuEntryProvider().SoftwareGeneratedInterruptsUnknownRequester[irqId], irqId, inRegisterIndex);
1904                         }
1905                         else
1906                         {
1907                             fieldAction(register, () => cpuEntryProvider().SoftwareGeneratedInterruptsLegacyRequester[sgiRequestingCPU][irqId], irqId, inRegisterIndex);
1908                         }
1909                     }
1910                     else if(irqsDecoder.IsPrivatePeripheral(irqId))
1911                     {
1912                         fieldAction(register, () => cpuEntryProvider().PrivatePeripheralInterrupts[irqId], irqId, inRegisterIndex);
1913                     }
1914                     else
1915                     {
1916                         fieldAction(register, () => sharedInterrupts[irqId], irqId, inRegisterIndex);
1917                     }
1918                     fieldIndex++;
1919                 }
1920                 // Always fill the whole register
1921                 for(; (fieldIndex % fieldsPerRegister) != 0; fieldIndex++)
1922                 {
1923                     fieldPlaceholderAction(register, fieldIndex % fieldsPerRegister);
1924                 }
1925 
1926                 yield return register;
1927             }
1928         }
1929 
GetAskingCPUEntry()1930         private CPUEntry GetAskingCPUEntry()
1931         {
1932             var cpu = busController.GetCurrentCPU();
1933             var armCPU = cpu as IARMSingleSecurityStateCPU;
1934             if(armCPU == null || !cpuEntries.ContainsKey(armCPU))
1935             {
1936                 throw new InvalidOperationException($"Non-Arm CPU or one that isn't attached to the GIC tried to access the GIC: {cpu.GetName()}");
1937             }
1938             return cpuEntries[armCPU];
1939         }
1940 
GetAskingCPUEntryWithTwoSecurityStates()1941         private CPUEntryWithTwoSecurityStates GetAskingCPUEntryWithTwoSecurityStates()
1942         {
1943             var cpuEntry = GetAskingCPUEntry();
1944             if(!(cpuEntry is CPUEntryWithTwoSecurityStates cpuEntryWithTwoSecurityStates))
1945             {
1946                 throw new InvalidOperationException($"The asking CPU '{cpuEntry.Name}' doesn't have two security states!");
1947             }
1948             return cpuEntryWithTwoSecurityStates;
1949         }
1950 
TryWriteRegisterSecurityView(long offset, uint value, DoubleWordRegisterCollection notBankedRegisters, DoubleWordRegisterCollection secureRegisters, DoubleWordRegisterCollection nonSecureRegisters, DoubleWordRegisterCollection disabledSecurityRegisters)1951         private bool TryWriteRegisterSecurityView(long offset, uint value, DoubleWordRegisterCollection notBankedRegisters,
1952             DoubleWordRegisterCollection secureRegisters, DoubleWordRegisterCollection nonSecureRegisters, DoubleWordRegisterCollection disabledSecurityRegisters)
1953         {
1954             var bankedRegisters = GetBankedRegistersCollection(secureRegisters, nonSecureRegisters, disabledSecurityRegisters);
1955             return bankedRegisters.TryWrite(offset, value) || notBankedRegisters.TryWrite(offset, value);
1956         }
1957 
TryReadRegisterSecurityView(long offset, out uint value, DoubleWordRegisterCollection notBankedRegisters, DoubleWordRegisterCollection secureRegisters, DoubleWordRegisterCollection nonSecureRegisters, DoubleWordRegisterCollection disabledSecurityRegisters)1958         private bool TryReadRegisterSecurityView(long offset, out uint value, DoubleWordRegisterCollection notBankedRegisters,
1959             DoubleWordRegisterCollection secureRegisters, DoubleWordRegisterCollection nonSecureRegisters, DoubleWordRegisterCollection disabledSecurityRegisters)
1960         {
1961             var bankedRegisters = GetBankedRegistersCollection(secureRegisters, nonSecureRegisters, disabledSecurityRegisters);
1962             return bankedRegisters.TryRead(offset, out value) || notBankedRegisters.TryRead(offset, out value);
1963         }
1964 
GetBankedRegistersCollection(DoubleWordRegisterCollection secureRegisters, DoubleWordRegisterCollection nonSecureRegisters, DoubleWordRegisterCollection disabledSecurityRegisters)1965         private DoubleWordRegisterCollection GetBankedRegistersCollection(DoubleWordRegisterCollection secureRegisters, DoubleWordRegisterCollection nonSecureRegisters,
1966             DoubleWordRegisterCollection disabledSecurityRegisters)
1967         {
1968             if(DisabledSecurity)
1969             {
1970                 return disabledSecurityRegisters;
1971             }
1972             else
1973             {
1974                 return GetAskingCPUEntry().IsStateSecure ? secureRegisters : nonSecureRegisters;
1975             }
1976         }
1977 
IsDistributorByteAccessible(long offset)1978         private bool IsDistributorByteAccessible(long offset)
1979         {
1980             return IsByteOffsetInDistributorRegistersRange(offset, DistributorRegisters.InterruptPriority_0, DistributorRegisters.InterruptPriority_254)
1981                 || IsByteOffsetInDistributorRegistersRange(offset, DistributorRegisters.InterruptProcessorTargets_0, DistributorRegisters.InterruptProcessorTargets_254)
1982                 || IsByteOffsetInDistributorRegistersRange(offset, DistributorRegisters.SoftwareGeneratedIntrruptClearPending_0, DistributorRegisters.SoftwareGeneratedIntrruptClearPending_3)
1983                 || IsByteOffsetInDistributorRegistersRange(offset, DistributorRegisters.SoftwareGeneratedIntrruptSetPending_0, DistributorRegisters.SoftwareGeneratedIntrruptSetPending_3);
1984         }
1985 
IsByteOffsetInDistributorRegistersRange(long offset, DistributorRegisters startOffset, DistributorRegisters endOffset)1986         private bool IsByteOffsetInDistributorRegistersRange(long offset, DistributorRegisters startOffset, DistributorRegisters endOffset)
1987         {
1988             const long maxByteOffset = 3;
1989             return (long)startOffset <= offset && offset <= (long)endOffset + maxByteOffset;
1990         }
1991 
IsRedistributorByteAccessible(long offset)1992         private bool IsRedistributorByteAccessible(long offset)
1993         {
1994             const long maxByteOffset = 3;
1995             return (long)RedistributorRegisters.InterruptPriority_0 <= offset && offset <= (long)RedistributorRegisters.InterruptPriority_7 + maxByteOffset;
1996         }
1997 
SetOnTransitionToTrue(ref bool value, bool newValue, string warningOnTransitionToFalse)1998         private bool SetOnTransitionToTrue(ref bool value, bool newValue, string warningOnTransitionToFalse)
1999         {
2000             if(newValue == value)
2001             {
2002                 return false;
2003             }
2004             if(!newValue)
2005             {
2006                 this.Log(LogLevel.Warning, warningOnTransitionToFalse);
2007                 return false;
2008             }
2009             value = true;
2010             return true;
2011         }
2012 
ClearForcedTargettingCache()2013         private void ClearForcedTargettingCache()
2014         {
2015             forcedTargettedCpuForAffinityRouting = null;
2016         }
2017 
GetProcessorNumber(ICPU cpu)2018         private static uint GetProcessorNumber(ICPU cpu)
2019         {
2020             switch(cpu.Model)
2021             {
2022                 // For Cortex-A55 core number is stored in affinity level 1.
2023                 // In older CPUs (i.e. Cortex-A53) it's stored in affinity level 0.
2024                 case "cortex-a55":
2025                     return BitHelper.GetValue(cpu.MultiprocessingId, 8, 8);
2026                 default:
2027                     return BitHelper.GetValue(cpu.MultiprocessingId, 0, 8);
2028             }
2029         }
2030 
2031         private CPUEntry ForcedTargettingCpuForAffinityRouting
2032         {
2033             get
2034             {
2035                 if(forcedTargettedCpuForAffinityRouting == null)
2036                 {
2037                     forcedTargettedCpuForAffinityRouting = cpuEntries.Values.Where(cpu => cpu.IsParticipatingInRouting).MinBy(cpu => cpu.Affinity.AllLevels);
2038                 }
2039                 return forcedTargettedCpuForAffinityRouting;
2040             }
2041         }
2042 
2043         private bool ackControl;
2044         private bool enableFIQ;
2045         private bool disabledSecurity;
2046         private bool affinityRoutingEnabledSecure;
2047         private bool affinityRoutingEnabledNonSecure;
2048         private uint legacyCpusAttachedMask;
2049         private CPUEntry forcedTargettedCpuForAffinityRouting;
2050 
2051         private readonly IBusController busController;
2052         private readonly Object locker = new Object();
2053         private readonly Dictionary<uint, IARMSingleSecurityStateCPU> cpusByProcessorNumberCache = new Dictionary<uint, IARMSingleSecurityStateCPU>();
2054         private readonly Dictionary<IARMSingleSecurityStateCPU, CPUEntry> cpuEntries = new Dictionary<IARMSingleSecurityStateCPU, CPUEntry>();
2055         private readonly InterruptSignalType[] supportedInterruptSignals;
2056         private readonly ReadOnlyDictionary<InterruptId, SharedInterrupt> sharedInterrupts;
2057         private readonly ReadOnlyDictionary<GroupType, InterruptGroup> groups;
2058         private readonly DoubleWordRegisterCollection distributorDoubleWordRegisters;
2059         private readonly QuadWordRegisterCollection distributorQuadWordRegisters;
2060         private readonly DoubleWordRegisterCollection distributorRegistersSecureView;
2061         private readonly DoubleWordRegisterCollection distributorRegistersNonSecureView;
2062         private readonly DoubleWordRegisterCollection distributorRegistersDisabledSecurityView;
2063         private readonly DoubleWordRegisterCollection cpuInterfaceRegisters;
2064         private readonly DoubleWordRegisterCollection cpuInterfaceRegistersSecureView;
2065         private readonly DoubleWordRegisterCollection cpuInterfaceRegistersNonSecureView;
2066         private readonly DoubleWordRegisterCollection cpuInterfaceRegistersDisabledSecurityView;
2067         private readonly QuadWordRegisterCollection cpuInterfaceSystemRegisters;
2068 
2069         private readonly uint affinityToIdMask;
2070         private readonly bool supportsTwoSecurityStates;
2071         private readonly InterruptsDecoder irqsDecoder;
2072 
2073         private const ARM_GenericInterruptControllerVersion DefaultArchitectureVersion = ARM_GenericInterruptControllerVersion.GICv3;
2074         private const uint DefaultCPUInterfaceProductIdentifier = 0x0;
2075         private const uint DefaultDistributorProductIdentifier = 0x0;
2076         private const uint DefaultRedistributorProductIdentifier = 0x0;
2077         private const byte DefaultVariantNumber = 0x0;
2078         private const byte DefaultRevisionNumber = 0x0;
2079         private const uint DefaultImplementerIdentification = 0x43B; // This value indicates the JEP106 code of the Arm as an implementer
2080 
2081         private const uint CPUsCountLegacySupport = 8;
2082         private const long RedistributorPrivateInterruptsFrameOffset = 0x10000;
2083 
2084         private const uint VirtualInterruptCount = 16;
2085         private const uint VirtualPriorityBits = 5;
2086 
2087         public class CPUEntry : IGPIOReceiver
2088         {
CPUEntry(ARM_GenericInterruptController gic, IARMSingleSecurityStateCPU cpu, IEnumerable<GroupType> groupTypes, IReadOnlyDictionary<InterruptSignalType, IGPIO> interruptConnections)2089             public CPUEntry(ARM_GenericInterruptController gic, IARMSingleSecurityStateCPU cpu, IEnumerable<GroupType> groupTypes, IReadOnlyDictionary<InterruptSignalType, IGPIO> interruptConnections)
2090             {
2091                 this.gic = gic;
2092                 this.cpu = cpu;
2093                 Name = $"cpu{Affinity}";
2094                 interruptSignals = interruptConnections;
2095 
2096                 var sgiIds = InterruptId.GetRange(gic.IrqsDecoder.SoftwareGeneratedFirst, gic.IrqsDecoder.SoftwareGeneratedLast);
2097                 SoftwareGeneratedInterruptsConfig = new ReadOnlyDictionary<InterruptId, InterruptConfig>(sgiIds.ToDictionary(id => id, _ => new InterruptConfig()));
2098                 SoftwareGeneratedInterruptsUnknownRequester = GenerateSGIs(SoftwareGeneratedInterruptsConfig, null);
2099                 SoftwareGeneratedInterruptsLegacyRequester = new Dictionary<CPUEntry, ReadOnlyDictionary<InterruptId, SoftwareGeneratedInterrupt>>();
2100 
2101                 redistributorDoubleWordRegisters = new DoubleWordRegisterCollection(this, BuildRedistributorDoubleWordRegisterMap());
2102                 redistributorQuadWordRegisters = new QuadWordRegisterCollection(this, BuildRedistributorQuadWordRegisterMap());
2103 
2104                 var ppiIds = InterruptId.GetRange(gic.IrqsDecoder.PrivatePeripheralFirst, gic.IrqsDecoder.PrivatePeripheralLast)
2105                     .Concat(InterruptId.GetRange(gic.IrqsDecoder.ExtendedPrivatePeripheralFirst, gic.IrqsDecoder.ExtendedPrivatePeripheralLast));
2106                 PrivatePeripheralInterrupts = new ReadOnlyDictionary<InterruptId, Interrupt>(ppiIds.ToDictionary(id => id, id => new Interrupt(id)));
2107 
2108                 Groups = new GroupCollection(this, groupTypes);
2109                 RunningInterrupts = new RunningInterrupts(this);
2110                 VirtualInterrupts = new VirtualInterrupt[VirtualInterruptCount];
2111                 for(var i = 0; i < VirtualInterruptCount; ++i)
2112                 {
2113                     VirtualInterrupts[i] = new VirtualInterrupt();
2114                 }
2115             }
2116 
Reset()2117             public virtual void Reset()
2118             {
2119                 foreach(var irq in AllPrivateAndSoftwareGeneratedInterrupts)
2120                 {
2121                     irq.Reset();
2122                 }
2123                 foreach(var interrupt in VirtualInterrupts)
2124                 {
2125                     interrupt.Reset();
2126                 }
2127                 for(var i = 0; i < NonSecureSGIAccess.Length; ++i)
2128                 {
2129                     NonSecureSGIAccess[i] = NonSecureAccess.NotPermitted;
2130                 }
2131                 Groups.Reset();
2132                 redistributorQuadWordRegisters.Reset();
2133                 redistributorDoubleWordRegisters.Reset();
2134                 VirtualCPUInterfaceEnabled = false;
2135                 BestPending = null;
2136                 BestPendingVirtual = null;
2137                 EndOfInterruptModeEL1NonSecure = EndOfInterruptModes.PriorityDropAndDeactivation;
2138                 EndOfInterruptModeEL1Secure = EndOfInterruptModes.PriorityDropAndDeactivation;
2139                 EndOfInterruptModeVirtual = EndOfInterruptModes.PriorityDropAndDeactivation;
2140                 VirtualFIQEnabled = false;
2141                 IsSleeping = true;
2142                 RunningInterrupts.Clear();
2143                 PhysicalPriorityMask = InterruptPriority.Idle;
2144                 VirtualPriorityMask = InterruptPriority.Idle;
2145                 UpdateSignals();
2146             }
2147 
2148             // It's expected to pass handling of private interrupts to the ARM_GenericInterruptController class using an event handler
OnGPIO(int number, bool value)2149             public void OnGPIO(int number, bool value)
2150             {
2151                 PrivateInterruptChanged?.Invoke(this, number, value);
2152             }
2153 
AcknowledgeBestPending(GroupTypeSecurityAgnostic groupTypeRegister, out CPUEntry sgiRequestingCPU)2154             public virtual InterruptId AcknowledgeBestPending(GroupTypeSecurityAgnostic groupTypeRegister, out CPUEntry sgiRequestingCPU)
2155             {
2156                 var groupType = GetGroupTypeForRegisterSecurityAgnostic(groupTypeRegister);
2157                 var isVirtualized = IsVirtualized(groupType);
2158                 sgiRequestingCPU = null;
2159                 var pendingIrq = isVirtualized ? BestPendingVirtual : BestPending;
2160                 if(pendingIrq == null)
2161                 {
2162                     return gic.IrqsDecoder.NoPending;
2163                 }
2164                 if(pendingIrq.Config.GroupType != groupType)
2165                 {
2166                     // In GICv2, Secure (Group 0) access can acknowledge Group 1 interrupt if GICC_CTLR.AckCtl is set. Otherwise, the returned Interrupt ID is 1022 (NoPending=1023).
2167                     if(!isVirtualized && gic.ArchitectureVersion == ARM_GenericInterruptControllerVersion.GICv2 && groupTypeRegister == GroupTypeSecurityAgnostic.Group0)
2168                     {
2169                         if(!gic.ackControl)
2170                         {
2171                             gic.Log(LogLevel.Warning, "Trying to acknowledge pending Group 1 interrupt (#{0}) with secure GIC access while GICC_CTLR.AckCtl isn't set", (uint)pendingIrq.Identifier);
2172                             return gic.IrqsDecoder.NonMaskableInterruptOrGICv2GroupMismatch;
2173                         }
2174                     }
2175                     else
2176                     {
2177                         gic.Log(LogLevel.Warning, "Trying to acknowledge pending interrupt using register of an incorrect interrupt group ({0}), expected {1}.", groupType, pendingIrq.Config.GroupType);
2178                         return gic.IrqsDecoder.NoPending;
2179                     }
2180                 }
2181                 pendingIrq.State.Acknowledge();
2182                 RunningInterrupts.Push(groupType, pendingIrq);
2183                 if(isVirtualized)
2184                 {
2185                     BestPendingVirtual = null;
2186                 }
2187                 else
2188                 {
2189                     BestPending = null;
2190                 }
2191 
2192                 var pendingIrqAsSGI = pendingIrq as SoftwareGeneratedInterrupt;
2193                 if(pendingIrqAsSGI != null)
2194                 {
2195                     sgiRequestingCPU = pendingIrqAsSGI.Requester;
2196                 }
2197 
2198                 return pendingIrq.Identifier;
2199             }
2200 
2201             // Performs priority drop and, if independentEOIControls is false, IRQ deactivation.
InterruptEnd(InterruptId id, GroupTypeSecurityAgnostic groupTypeRegister, CPUEntry sgiRequestingCPU)2202             public void InterruptEnd(InterruptId id, GroupTypeSecurityAgnostic groupTypeRegister, CPUEntry sgiRequestingCPU)
2203             {
2204                 var groupType = GetGroupTypeForRegisterSecurityAgnostic(groupTypeRegister);
2205                 var isVirtualized = IsVirtualized(groupType);
2206                 var shouldBeDeactivated = CurrentEndOfInterruptMode == EndOfInterruptModes.PriorityDropAndDeactivation;
2207                 gic.Log(LogLevel.Debug, "Ending interrupt with id {0}{1}.", (uint)id, shouldBeDeactivated ? "" : " but it won't be deactivated");
2208 
2209                 if(RunningInterrupts.Count(groupType) == 0)
2210                 {
2211                     gic.Log(LogLevel.Warning, "Trying to end the running interrupt when no interrupt is running.");
2212                     return;
2213                 }
2214 
2215                 var runningIrq = RunningInterrupts.Peek(groupType);
2216                 if(runningIrq.Config.GroupType != groupType)
2217                 {
2218                     // In GICv2, Secure (Group 0) access can affect Group 1 interrupts if GICC_CTLR.AckCtl is set.
2219                     if(!isVirtualized && gic.ArchitectureVersion == ARM_GenericInterruptControllerVersion.GICv2 && groupTypeRegister == GroupTypeSecurityAgnostic.Group0)
2220                     {
2221                         if(!gic.ackControl)
2222                         {
2223                             gic.Log(LogLevel.Warning, "Trying to end the running Group 1 interrupt (#{0}) with secure GIC access while GICC_CTLR.AckCtl isn't set, request ignored.", (uint)runningIrq.Identifier);
2224                             return;
2225                         }
2226                     }
2227                     else
2228                     {
2229                         gic.Log(LogLevel.Warning, "Trying to end the running interrupt using the register of an incorrect interrupt group ({0}), expected {1}, request ignored.", groupType, runningIrq.Config.GroupType);
2230                         return;
2231                     }
2232                 }
2233 
2234                 if(!runningIrq.Identifier.Equals(id))
2235                 {
2236                     gic.Log(LogLevel.Error, "Incorrect interrupt identifier to end the running interrupt, expected INTID {0}, given {1}, request ignored.", runningIrq.Identifier, id);
2237                     return;
2238                 }
2239 
2240                 if(!IsSoftwareGeneratedInterruptAccessValid(runningIrq, sgiRequestingCPU, "InterruptEnd"))
2241                 {
2242                     return;
2243                 }
2244 
2245                 // The interrupt are just removed from stack of currently running interrupts
2246                 // It's still accessible using one of the read only collections of interrupts (shared or private ones)
2247                 // If the stack becomes empty, RunningPriority will return InterruptPriority.Idle; otherwise the priority of an interrupt on top of stack.
2248                 RunningInterrupts.Pop(groupType);
2249 
2250                 if(shouldBeDeactivated)
2251                 {
2252                     DeactivateInterrupt(runningIrq, isVirtualized);
2253                 }
2254             }
2255 
InterruptDeactivate(InterruptId id, CPUEntry sgiRequestingCPU)2256             public void InterruptDeactivate(InterruptId id, CPUEntry sgiRequestingCPU)
2257             {
2258                 Action<string> logFailure = failureReason => gic.Log(
2259                     LogLevel.Warning, "Trying to deactivate interrupt with id {0} {1}, write ignored.", (uint)id, failureReason
2260                 );
2261 
2262                 var isVirtualized = IsVirtualized();
2263 
2264                 if(CurrentEndOfInterruptMode == EndOfInterruptModes.PriorityDropAndDeactivation)
2265                 {
2266                     logFailure($"with EOImode=0 in the current CPU state: {CurrentCPUSecurityStateString}");
2267                     return;
2268                 }
2269 
2270                 var interrupt = isVirtualized
2271                     ? RunningInterrupts.GetActiveVirtual(id)
2272                     : gic.GetAllInterrupts(this).SingleOrDefault(x => ((uint)x.Identifier) == ((uint)id) && x.State.Active);
2273                 if(interrupt == null)
2274                 {
2275                     logFailure("which is an invalid INTID");
2276                     return;
2277                 }
2278 
2279                 if(!gic.DisabledSecurity && !IsStateSecure)
2280                 {
2281                     var interruptGroupType = interrupt.Config.GroupType;
2282                     if(interruptGroupType != GroupType.Group1NonSecure)
2283                     {
2284                         logFailure("belonging to " + (interruptGroupType == GroupType.Group0 ? "group 0" : "secure group 1") + " from a non-secure state");
2285                         return;
2286                     }
2287                 }
2288 
2289                 if(!IsSoftwareGeneratedInterruptAccessValid(interrupt, sgiRequestingCPU, "InterruptDeactivate"))
2290                 {
2291                     return;
2292                 }
2293 
2294                 if(!interrupt.State.Active)
2295                 {
2296                     logFailure("but it isn't active");
2297                     return;
2298                 }
2299                 DeactivateInterrupt(interrupt, isVirtualized);
2300             }
2301 
UpdateSignals()2302             public void UpdateSignals()
2303             {
2304                 if(BestPending == null && BestPendingVirtual == null)
2305                 {
2306                     foreach(var signal in interruptSignals.Values)
2307                     {
2308                         signal.Set(false);
2309                     }
2310                     return;
2311                 }
2312 
2313                 var signalType = GetBestPendingInterruptSignalType();
2314                 foreach(var signal in interruptSignals)
2315                 {
2316                     signal.Value.Set(signal.Key == signalType);
2317                 }
2318             }
2319 
GetGroupForRegisterSecurityAgnostic(GroupTypeSecurityAgnostic type)2320             public InterruptGroup GetGroupForRegisterSecurityAgnostic(GroupTypeSecurityAgnostic type)
2321             {
2322                 return Groups[GetGroupTypeForRegisterSecurityAgnostic(type)];
2323             }
2324 
GetGroupTypeForRegisterSecurityAgnostic(GroupTypeSecurityAgnostic type)2325             public GroupType GetGroupTypeForRegisterSecurityAgnostic(GroupTypeSecurityAgnostic type)
2326             {
2327                 if(type == GroupTypeSecurityAgnostic.Group0)
2328                 {
2329                     return GroupType.Group0;
2330                 }
2331                 else if(type == GroupTypeSecurityAgnostic.Group1)
2332                 {
2333                     var securityState = cpu.SecurityState;
2334                     if(gic.DisabledSecurity || securityState == SecurityState.NonSecure)
2335                     {
2336                         return GroupType.Group1NonSecure;
2337                     }
2338                     else if(securityState == SecurityState.Secure)
2339                     {
2340                         return GroupType.Group1Secure;
2341                     }
2342                 }
2343                 throw new ArgumentOutOfRangeException($"There is no valid InterruptGroupType for value: {type}.");
2344             }
2345 
RegisterLegacySGIRequester(CPUEntry cpu)2346             public void RegisterLegacySGIRequester(CPUEntry cpu)
2347             {
2348                 if(SoftwareGeneratedInterruptsLegacyRequester.ContainsKey(cpu))
2349                 {
2350                     throw new ArgumentException($"The CPU ({cpu}) was already registered as a legacy requester.");
2351                 }
2352                 SoftwareGeneratedInterruptsLegacyRequester[cpu] = GenerateSGIs(SoftwareGeneratedInterruptsConfig, cpu);
2353             }
2354 
IsVirtualized()2355             public bool IsVirtualized()
2356             {
2357                 if(cpu.SecurityState != SecurityState.NonSecure || cpu.ExceptionLevel > ExceptionLevel.EL1_SystemMode)
2358                 {
2359                     return false;
2360                 }
2361                 return cpu.FIQMaskOverride || cpu.IRQMaskOverride;
2362             }
2363 
IsVirtualized(GroupType type)2364             public bool IsVirtualized(GroupType type)
2365             {
2366                 if(cpu.SecurityState != SecurityState.NonSecure || cpu.ExceptionLevel > ExceptionLevel.EL1_SystemMode)
2367                 {
2368                     return false;
2369                 }
2370                 if(type == GroupType.Group0)
2371                 {
2372                     return cpu.FIQMaskOverride;
2373                 }
2374                 if(type == GroupType.Group1)
2375                 {
2376                     return cpu.IRQMaskOverride;
2377                 }
2378                 throw new ArgumentOutOfRangeException($"There is no valid InterruptGroupType for value: {type}.");
2379             }
2380 
2381             public QuadWordRegisterCollection RedistributorQuadWordRegisters => redistributorQuadWordRegisters;
2382             public DoubleWordRegisterCollection RedistributorDoubleWordRegisters => redistributorDoubleWordRegisters;
2383 
2384             public event Action<CPUEntry, int, bool> PrivateInterruptChanged;
2385 
2386             public IReadOnlyDictionary<InterruptId, Interrupt> PrivatePeripheralInterrupts { get; }
2387             public IReadOnlyDictionary<InterruptId, InterruptConfig> SoftwareGeneratedInterruptsConfig { get; }
2388             public IReadOnlyDictionary<InterruptId, SoftwareGeneratedInterrupt> SoftwareGeneratedInterruptsUnknownRequester;
2389             public Dictionary<CPUEntry, ReadOnlyDictionary<InterruptId, SoftwareGeneratedInterrupt>> SoftwareGeneratedInterruptsLegacyRequester { get; }
2390 
2391             public IEnumerable<Interrupt> AllPrivateAndSoftwareGeneratedInterrupts => PrivatePeripheralInterrupts.Values
2392                 .Concat(SoftwareGeneratedInterruptsUnknownRequester.Values)
2393                 .Concat(SoftwareGeneratedInterruptsLegacyRequester.Values.SelectMany(x => x.Values));
2394             public IEnumerable<InterruptConfig> AllPrivateAndSoftwareGeneratedInterruptsConfigs => PrivatePeripheralInterrupts.Values.Select(irq => irq.Config)
2395                 .Concat(SoftwareGeneratedInterruptsConfig.Values);
2396 
2397             public GroupCollection Groups { get; }
2398 
2399             public bool IsSleeping
2400             {
2401                 get => isSleeping;
2402                 set
2403                 {
2404                     var changed = isSleeping != value;
2405                     if(changed)
2406                     {
2407                         isSleeping = value;
2408                         gic.ClearForcedTargettingCache();
2409                     }
2410                 }
2411             }
2412 
2413             public Affinity Affinity => cpu.Affinity;
2414             public bool IsParticipatingInRouting => !IsSleeping;
2415             public virtual string CurrentCPUSecurityStateString => $"state: {cpu.SecurityState}";
2416             public virtual EndOfInterruptModes CurrentEndOfInterruptMode => EndOfInterruptModeEL1;
2417 
2418             public EndOfInterruptModes EndOfInterruptModeEL1
2419             {
2420                 get => IsVirtualized()
2421                     ? EndOfInterruptModeVirtual
2422                     : gic.DisabledSecurity || IsStateSecure ? EndOfInterruptModeEL1Secure : EndOfInterruptModeEL1NonSecure;
2423                 set
2424                 {
2425                     if(IsVirtualized())
2426                     {
2427                         EndOfInterruptModeVirtual = value;
2428                     }
2429                     else if(gic.DisabledSecurity || IsStateSecure)
2430                     {
2431                         EndOfInterruptModeEL1Secure = value;
2432                     }
2433                     else
2434                     {
2435                         EndOfInterruptModeEL1NonSecure = value;
2436                     }
2437                 }
2438             }
2439 
2440             public EndOfInterruptModes EndOfInterruptModeEL1NonSecure { get; set; }
2441             public EndOfInterruptModes EndOfInterruptModeEL1Secure { get; set; }
2442             public EndOfInterruptModes EndOfInterruptModeVirtual { get; set; }
2443             public bool IsStateSecure => cpu.SecurityState == SecurityState.Secure;
2444             public string Name { get; }
2445             public uint ProcessorNumber => GetProcessorNumber(cpu);
2446             public uint TargetFieldFlag => ProcessorNumber <= CPUsCountLegacySupport ? 1U << (int)ProcessorNumber : 0;
2447             public bool VirtualCPUInterfaceEnabled { get; set; }
2448             public bool VirtualFIQEnabled { get; set; }
2449 
2450             public Interrupt BestPending { get; set; }
2451             public Interrupt BestPendingVirtual { get; set; }
2452             public VirtualInterrupt[] VirtualInterrupts { get; }
2453             public InterruptPriority PriorityMask
2454             {
2455                 get => IsVirtualized() ? VirtualPriorityMask : PhysicalPriorityMask;
2456                 set
2457                 {
2458                     if(IsVirtualized())
2459                     {
2460                         VirtualPriorityMask = value;
2461                     }
2462                     else
2463                     {
2464                         PhysicalPriorityMask = value;
2465                     }
2466                 }
2467             }
2468             public InterruptPriority VirtualPriorityMask { get; set; }
2469             public InterruptPriority PhysicalPriorityMask { get; set; }
2470             public RunningInterrupts RunningInterrupts { get; }
2471             public uint EOICount { get; private set; }
2472             public NonSecureAccess[] NonSecureSGIAccess { get; } = new NonSecureAccess[InterruptsDecoder.SoftwareGeneratedCount];
2473 
2474             public const int EOICountWidth = 5;
2475             public const int EOICountMask = (1 << EOICountWidth) - 1;
2476 
GetBestPendingInterruptSignalType()2477             protected virtual InterruptSignalType GetBestPendingInterruptSignalType()
2478             {
2479                 if(BestPending == null)
2480                 {
2481                     if(VirtualFIQEnabled && BestPendingVirtual.Config.GroupType == GroupType.Group0)
2482                     {
2483                         return InterruptSignalType.vFIQ;
2484                     }
2485                     return InterruptSignalType.vIRQ;
2486                 }
2487                 if(gic.enableFIQ && BestPending.Config.GroupType == GroupType.Group0)
2488                 {
2489                     return InterruptSignalType.FIQ;
2490                 }
2491                 return InterruptSignalType.IRQ;
2492             }
2493 
2494             protected readonly ARM_GenericInterruptController gic;
2495 
GenerateSGIs(IReadOnlyDictionary<InterruptId, InterruptConfig> configs, CPUEntry requester)2496             private ReadOnlyDictionary<InterruptId, SoftwareGeneratedInterrupt> GenerateSGIs(IReadOnlyDictionary<InterruptId, InterruptConfig> configs, CPUEntry requester)
2497             {
2498                 return new ReadOnlyDictionary<InterruptId, SoftwareGeneratedInterrupt>(configs.ToDictionary(config => config.Key, config => new SoftwareGeneratedInterrupt(config.Key, config.Value, requester)));
2499             }
2500 
DeactivateInterrupt(Interrupt interrupt, bool isVirtualized)2501             private void DeactivateInterrupt(Interrupt interrupt, bool isVirtualized)
2502             {
2503                 gic.Log(LogLevel.Debug, "Deactivating interrupt with id {0}", (uint)interrupt.Identifier);
2504                 interrupt.State.Active = false;
2505 
2506                 if(isVirtualized)
2507                 {
2508                     var virtualInterrupt = (VirtualInterrupt)interrupt;
2509                     var lrEntry = VirtualInterrupts.SingleOrDefault(x => x.Identifier.Equals(interrupt.Identifier));
2510                     if(lrEntry == null)
2511                     {
2512                         EOICount = (EOICount + 1) & EOICountMask;
2513                     }
2514                     else
2515                     {
2516                         lrEntry.Sync(virtualInterrupt);
2517                     }
2518                     RunningInterrupts.RemoveActiveVirtual(virtualInterrupt.Identifier);
2519                     if(virtualInterrupt.Hardware)
2520                     {
2521                         var physicalIrq = gic.GetAllInterrupts(this).SingleOrDefault(x => x.Identifier.Equals(virtualInterrupt.HardwareIdentifier));
2522                         if(physicalIrq != null)
2523                         {
2524                             physicalIrq.State.Active = false;
2525                         }
2526                     }
2527                 }
2528             }
2529 
IsSoftwareGeneratedInterruptAccessValid(Interrupt interrupt, CPUEntry accessingCPU, string registerTypeName)2530             private bool IsSoftwareGeneratedInterruptAccessValid(Interrupt interrupt, CPUEntry accessingCPU, string registerTypeName)
2531             {
2532                 if(interrupt is SoftwareGeneratedInterrupt sgi)
2533                 {
2534                     if(sgi.Requester != null && sgi.Requester != accessingCPU && !gic.IsAffinityRoutingEnabled(sgi.Requester))
2535                     {
2536                         var logMessage = "{0}: Incorrect Processor Number {1} passed for SGI ({2}), expected to be {3}.";
2537                         if(gic.ArchitectureVersionAtLeast3)
2538                         {
2539                             logMessage += " Request will be ignored.";
2540                         }
2541                         gic.Log(LogLevel.Warning, logMessage, registerTypeName, accessingCPU.Affinity, interrupt.Identifier, sgi.Requester.Affinity);
2542 
2543                         if(gic.ArchitectureVersionAtLeast3)
2544                         {
2545                             /*
2546                              * The documentation is not clear what happens here, if we are in SMP system, and there is a CPUID mismatch
2547                              * -> For GICv3 the whole field of INTID (bits 23:0) should be passed, which contains CPUID in bits 12:10 - so it's safe to abort if they mismatch
2548                              * -> For GICv2 it's not clear, but "For every read of a valid Interrupt ID from the GICC_IAR, the connected processor must perform a matching write to the GICC_EOIR.
2549                              *    The value written to the GICC_EOIR must be the interrupt ID read from the GICC_IAR."
2550                              *    So we stay permissive, and still allow the request to go through
2551                              */
2552                             return false;
2553                         }
2554                     }
2555                 }
2556                 else if(accessingCPU != null && accessingCPU.ProcessorNumber != 0)
2557                 {
2558                     gic.Log(LogLevel.Debug, "{0}: Processor Number ({1}) passed for non-SGI interrupt ({2}).", registerTypeName, accessingCPU.ProcessorNumber, interrupt.Identifier);
2559                 }
2560                 return true;
2561             }
2562 
BuildRedistributorQuadWordRegisterMap()2563             private Dictionary<long, QuadWordRegister> BuildRedistributorQuadWordRegisterMap()
2564             {
2565                 var registerMap = new Dictionary<long, QuadWordRegister>
2566                 {
2567                     {(long)RedistributorRegisters.ControllerType, new QuadWordRegister(this)
2568                         .WithValueField(32, 32, FieldMode.Read, name: "CPUAffinity",
2569                             valueProviderCallback: _ => this.Affinity.AllLevels
2570                         )
2571                         .WithValueField(27, 5, FieldMode.Read, name: "MaximumPrivatePeripheralInterruptIdentifier",
2572                             valueProviderCallback: _ => 0b00 // The maximum PPI identifier is 31, because the GIC doesn't support an extended range of PPI
2573                         )
2574                         .WithFlag(26, FieldMode.Read, name: "DirectSoftwareGeneratedInterruptInjectionSupport",
2575                             valueProviderCallback: _ => false
2576                         )
2577                         .WithTag("LocalitySpecificInterruptConfigurationSharing", 24, 2)
2578                         .WithValueField(8, 16, FieldMode.Read, name: "ProcessorNumber",
2579                             valueProviderCallback: _ => this.ProcessorNumber
2580                         )
2581                         .WithTaggedFlag("vPEResidentIndicator", 7)
2582                         .WithFlag(6, FieldMode.Read, name: "MPAMSupport",
2583                             valueProviderCallback: _ => false
2584                         )
2585                         .WithFlag(5, FieldMode.Read, name: "DPGSupport",
2586                             valueProviderCallback: _ => false
2587                         )
2588                         .WithFlag(4, FieldMode.Read, name: "HighestRedistributorInSeries", valueProviderCallback: _ =>
2589                         {
2590                             var redistributorsNotLowerThanCurrent = gic.GetRedistributorRegistrations().OrderBy(redist => redist.Range.StartAddress).SkipWhile(x => x.Cpu != cpu);
2591                             // there must exist a corresponding redistributor for this CPUEntry, as otherwise we wouldn't be able to access this register
2592                             var currentRedistributor = redistributorsNotLowerThanCurrent.First();
2593                             var nextRedistributor = redistributorsNotLowerThanCurrent.Skip(1).FirstOrDefault();
2594                             // return true for the highest redistrubutor in the contiguous region
2595                             return nextRedistributor == null || (nextRedistributor.Range.StartAddress - currentRedistributor.Range.EndAddress) > 1;
2596                         })
2597                         .WithFlag(3, FieldMode.Read, name: "LocalitySpecificInterruptDirectInjectionSupport",
2598                             valueProviderCallback: _ => false
2599                         )
2600                         .WithTaggedFlag("DirtyBitControl", 2)
2601                         .WithFlag(1, FieldMode.Read, name: "VirtualLocalitySpecificInterruptSupport",
2602                             valueProviderCallback: _ => false
2603                         )
2604                         .WithFlag(0, FieldMode.Read, name: "PhysicalLocalitySpecificInterruptSupport",
2605                             valueProviderCallback: _ => false
2606                         )
2607                     }
2608                 };
2609 
2610                 return registerMap;
2611             }
2612 
BuildRedistributorDoubleWordRegisterMap()2613             private Dictionary<long, DoubleWordRegister> BuildRedistributorDoubleWordRegisterMap()
2614             {
2615                 var registerMap = new Dictionary<long, DoubleWordRegister>
2616                 {
2617                     {(long)RedistributorRegisters.Control, new DoubleWordRegister(this)
2618                         .WithFlag(31, FieldMode.Read, name: "UpstreamWritePending",
2619                             valueProviderCallback: _ => false
2620                         )
2621                         .WithReservedBits(27, 4)
2622                         .WithTaggedFlag("DisableProcessorSelectionGroup1Secure", 26)
2623                         .WithTaggedFlag("DisableProcessorSelectionGroup1NonSecure", 25)
2624                         .WithTaggedFlag("DisableProcessorSelectionGroup0", 24)
2625                         .WithReservedBits(4, 20)
2626                         .WithFlag(3, FieldMode.Read, name: "RegisterWritePending",
2627                             valueProviderCallback: _ => false
2628                         )
2629                         .WithFlag(2, FieldMode.Read, name: "LocalitySpecificInterruptInvalidateSupport",
2630                             valueProviderCallback: _ => false
2631                         )
2632                         .WithFlag(1, FieldMode.Read, name: "LocalitySpecificInterruptClearEnableSupport",
2633                             valueProviderCallback: _ => false
2634                         )
2635                         .WithTaggedFlag("LocalitySpecificInterruptEnable", 0)
2636                     },
2637                     {(long)RedistributorRegisters.ImplementerIdentification, new DoubleWordRegister(this)
2638                         .WithValueField(24, 8, FieldMode.Read, name: "ProductID", valueProviderCallback: _ => gic.RedistributorProductIdentifier)
2639                         .WithReservedBits(20, 4)
2640                         .WithValueField(16, 4, FieldMode.Read, name: "Variant", valueProviderCallback: _ => gic.RedistributorVariant)
2641                         .WithValueField(12, 4, FieldMode.Read, name: "Revision", valueProviderCallback: _ => gic.RedistributorRevision)
2642                         .WithValueField(0, 12, FieldMode.Read, name: "Implementer", valueProviderCallback: _ => gic.RedistributorImplementer)
2643                     },
2644                     {(long)RedistributorRegisters.Wake, new DoubleWordRegister(this, 0x1)
2645                         // "There is only one GICR_WAKER.Sleep and one GICR_WAKER.Quiescent bit that can be read and written through the GICR_WAKER register of any Redistributor."
2646                         .WithTaggedFlag("Quiescent", 31)
2647                         .WithReservedBits(3, 28)
2648                         .WithFlag(2, FieldMode.Read, name: "ChildrenAsleep",
2649                             valueProviderCallback: _ => this.IsSleeping
2650                         )
2651                         .WithFlag(1, name: "ProcessorSleep",
2652                             writeCallback: (_, val) => this.IsSleeping = val,
2653                             valueProviderCallback: _ => this.IsSleeping
2654                         )
2655                         .WithTaggedFlag("Sleep", 0)
2656                         // According to the reference manual this register should be RAZ/WI for an unsecure access, but we just show a warning in such a situation.
2657                         .WithWriteCallback((_, __) => { if(!gic.DisabledSecurity && !this.IsStateSecure) this.Log(LogLevel.Warning, "Writing to the GICR_WAKER register from wrong security state."); })
2658                         .WithReadCallback((_, __) => { if(!gic.DisabledSecurity && !this.IsStateSecure) this.Log(LogLevel.Warning, "Reading from the GICR_WAKER register from wrong security state."); })
2659                     },
2660                     {(long)RedistributorRegisters.NonSecureAccessControl, new DoubleWordRegister(this)
2661                         .WithEnumFields<DoubleWordRegister, NonSecureAccess>(0, 2, 16, name: "NS_access",
2662                             writeCallback: (i, _, val) =>
2663                             {
2664                                 if(!gic.IsAffinityRoutingEnabled(this))
2665                                 {
2666                                     NonSecureSGIAccess[i] = val;
2667                                 }
2668                             },
2669                             valueProviderCallback: (i, _) => !gic.IsAffinityRoutingEnabled(this) ? (NonSecureAccess)0 : NonSecureSGIAccess[i]
2670                         )
2671                         // Those could be emitted in valueProvider/writeCallback instead,
2672                         // but we don't want to emit the same warning 16 times per access.
2673                         .WithWriteCallback((_, __) =>
2674                         {
2675                             if(!gic.IsAffinityRoutingEnabled(this))
2676                             {
2677                                 this.Log(LogLevel.Warning, "Tried to write to GICR_NSACR when affinity routing is disabled. Access ignored, use GICD_NSACR0 instead.");
2678                             }
2679                         })
2680                         .WithReadCallback((_, __) =>
2681                         {
2682                             if(!gic.IsAffinityRoutingEnabled(this))
2683                             {
2684                                 this.Log(LogLevel.Warning, "Tried to read from GICR_NSACR when affinity routing is disabled. Access ignored, use GICD_NSACR0 instead.");
2685                             }
2686                         })
2687                     },
2688                     {gic.PeripheralIdentificationOffset, new DoubleWordRegister(this)
2689                         .WithReservedBits(8, 24)
2690                         .WithEnumField<DoubleWordRegister, ARM_GenericInterruptControllerVersion>(4, 4, FieldMode.Read, name: "ArchitectureVersion",
2691                             valueProviderCallback: _ => gic.ArchitectureVersion
2692                         )
2693                         .WithTag("ImplementationDefinedIdentificator", 0, 4)
2694                     }
2695                 };
2696 
2697                 Utils.AddRegistersAtOffset(registerMap, (long)RedistributorRegisters.InterruptSetEnable_0,
2698                     gic.BuildInterruptSetEnableRegisters(gic.IrqsDecoder.SoftwareGeneratedFirst, gic.IrqsDecoder.PrivatePeripheralLast, "InterruptSetEnable",
2699                         cpuEntryProvider: () => this)
2700                 );
2701 
2702                 Utils.AddRegistersAtOffset(registerMap, (long)RedistributorRegisters.InterruptClearEnable_0,
2703                     gic.BuildInterruptClearEnableRegisters(gic.IrqsDecoder.SoftwareGeneratedFirst, gic.IrqsDecoder.PrivatePeripheralLast, "InterruptClearEnable",
2704                         cpuEntryProvider: () => this)
2705                 );
2706 
2707                 Utils.AddRegistersAtOffset(registerMap, (long)RedistributorRegisters.InterruptClearPending_0,
2708                     gic.BuildInterruptClearPendingRegisters(gic.IrqsDecoder.SoftwareGeneratedFirst, gic.IrqsDecoder.PrivatePeripheralLast, "InterruptClearPending",
2709                         cpuEntryProvider: () => this)
2710                 );
2711 
2712                 Utils.AddRegistersAtOffset(registerMap, (long)RedistributorRegisters.InterruptPriority_0,
2713                     gic.BuildInterruptPriorityRegisters(gic.IrqsDecoder.SoftwareGeneratedFirst, gic.IrqsDecoder.PrivatePeripheralLast, "InterruptPriority",
2714                         cpuEntryProvider: () => this)
2715                 );
2716 
2717                 Utils.AddRegistersAtOffset(registerMap, (long)RedistributorRegisters.PrivatePeripheralInterruptConfiguration,
2718                     gic.BuildInterruptConfigurationRegisters(gic.IrqsDecoder.PrivatePeripheralFirst, gic.IrqsDecoder.PrivatePeripheralLast, "PrivatePeripheralInterruptConfiguration",
2719                         cpuEntryProvider: () => this)
2720                 );
2721 
2722                 Utils.AddRegistersAtOffset(registerMap, (long)RedistributorRegisters.InterruptGroup_0,
2723                     gic.BuildInterruptGroupRegisters(gic.IrqsDecoder.SoftwareGeneratedFirst, gic.IrqsDecoder.PrivatePeripheralLast, "InterruptGroup",
2724                         cpuEntryProvider: () => this)
2725                 );
2726 
2727                 Utils.AddRegistersAtOffset(registerMap, (long)RedistributorRegisters.InterruptGroupModifier_0,
2728                     gic.BuildInterruptGroupModifierRegisters(gic.IrqsDecoder.SoftwareGeneratedFirst, gic.IrqsDecoder.PrivatePeripheralLast, "InterruptGroupModifier",
2729                         cpuEntryProvider: () => this)
2730                 );
2731 
2732                 return registerMap;
2733             }
2734 
2735             private bool isSleeping = true;
2736             private readonly IARMSingleSecurityStateCPU cpu;
2737             private readonly IReadOnlyDictionary<InterruptSignalType, IGPIO> interruptSignals;
2738             private readonly QuadWordRegisterCollection redistributorQuadWordRegisters;
2739             private readonly DoubleWordRegisterCollection redistributorDoubleWordRegisters;
2740 
2741             public class GroupCollection
2742             {
GroupCollection(CPUEntry cpu, IEnumerable<GroupType> groupTypes)2743                 public GroupCollection(CPUEntry cpu, IEnumerable<GroupType> groupTypes)
2744                 {
2745                     this.cpu = cpu;
2746                     Physical = new ReadOnlyDictionary<GroupType, InterruptGroup>(groupTypes.ToDictionary(type => type, _ => new InterruptGroup()));
2747                     Virtual = new ReadOnlyDictionary<GroupType, InterruptGroup>(groupTypes.ToDictionary(type => type, _ => new InterruptGroup()));
2748                 }
2749 
Reset()2750                 public void Reset()
2751                 {
2752                     foreach(var group in Physical.Values)
2753                     {
2754                         group.Reset();
2755                     }
2756                     foreach(var group in Virtual.Values)
2757                     {
2758                         group.Reset();
2759                     }
2760                 }
2761 
2762                 public InterruptGroup this[GroupType type] => cpu.IsVirtualized(type) ? Virtual[type] : Physical[type];
2763                 public IReadOnlyDictionary<GroupType, InterruptGroup> Physical { get; }
2764                 public IReadOnlyDictionary<GroupType, InterruptGroup> Virtual { get; }
2765 
2766                 private readonly CPUEntry cpu;
2767             }
2768         }
2769 
2770         public class RunningInterrupts
2771         {
RunningInterrupts(CPUEntry cpu)2772             public RunningInterrupts(CPUEntry cpu)
2773             {
2774                 this.cpu = cpu;
2775                 Physical = new Stack<Interrupt>();
2776                 Virtual = new Stack<VirtualInterrupt>();
2777                 activeVirtual = new Dictionary<InterruptId, VirtualInterrupt>();
2778             }
2779 
Clear()2780             public void Clear()
2781             {
2782                 Physical.Clear();
2783                 Virtual.Clear();
2784                 activeVirtual.Clear();
2785             }
2786 
Push(GroupType type, Interrupt interrupt)2787             public void Push(GroupType type, Interrupt interrupt)
2788             {
2789                 if(!interrupt.State.Active)
2790                 {
2791                     throw new ArgumentException("Trying to push inactive interrupt to interrupt stack");
2792                 }
2793                 if(cpu.IsVirtualized(type))
2794                 {
2795                     if(interrupt is VirtualInterrupt virtualInterrupt)
2796                     {
2797                         // GICv3 only has a maximum of 16 List Registers.
2798                         // To allow the hypervisor to manage more virtual interrutps
2799                         // than the implementation has list registers,
2800                         // the LRs only act as a "cache" of the virtual interrupts.
2801                         // The only requirement is that for a pending virtual interrupt to be raised,
2802                         // it has to be in a list register.
2803                         // This means that once we're acknowledging the interrupt, we're "cutting it off"
2804                         // from the List Register, and the hypervisor is free to
2805                         // reuse that LR to schedule another interrupt.
2806                         // However, both the active and the list-register interrupts,
2807                         // provided they have the same virtual identifier set,
2808                         // have to be kept in sync, which is what the VirtualInterrupt.Sync method is used for.
2809                         var cloned = virtualInterrupt.Clone();
2810                         Virtual.Push(cloned);
2811                         activeVirtual.Add(cloned.Identifier, cloned);
2812                     }
2813                     else
2814                     {
2815                         throw new ArgumentException("Trying to push physical interrupt to virtual interrupt stack");
2816                     }
2817                 }
2818                 else
2819                 {
2820                     if(interrupt is VirtualInterrupt)
2821                     {
2822                         throw new ArgumentException("Trying to push virtual interrupt to physical interrupt stack");
2823                     }
2824                     else
2825                     {
2826                         Physical.Push(interrupt);
2827                     }
2828                 }
2829             }
2830 
Pop(GroupType type)2831             public Interrupt Pop(GroupType type)
2832             {
2833                 return cpu.IsVirtualized(type) ? Virtual.Pop() : Physical.Pop();
2834             }
2835 
Peek(GroupType type)2836             public Interrupt Peek(GroupType type)
2837             {
2838                 return cpu.IsVirtualized(type) ? Virtual.Peek() : Physical.Peek();
2839             }
2840 
Count(GroupType type)2841             public int Count(GroupType type)
2842             {
2843                 return cpu.IsVirtualized(type) ? Virtual.Count : Physical.Count;
2844             }
2845 
GetActiveVirtual(InterruptId id)2846             public VirtualInterrupt GetActiveVirtual(InterruptId id)
2847             {
2848                 VirtualInterrupt result;
2849                 var present = activeVirtual.TryGetValue(id, out result);
2850                 return present ? result : null;
2851             }
2852 
RemoveActiveVirtual(InterruptId id)2853             public void RemoveActiveVirtual(InterruptId id)
2854             {
2855                 activeVirtual.Remove(id);
2856             }
2857 
2858             public InterruptPriority Priority => cpu.IsVirtualized() ? VirtualPriority : PhysicalPriority;
2859             public Stack<Interrupt> Physical { get; }
2860             public InterruptPriority PhysicalPriority => Physical.Count > 0 ? Physical.Peek().Config.Priority : InterruptPriority.Idle;
2861             public Stack<VirtualInterrupt> Virtual { get; }
2862             public InterruptPriority VirtualPriority => Virtual.Count > 0 ? Virtual.Peek().Config.Priority : InterruptPriority.Idle;
2863 
2864             private readonly Dictionary<InterruptId, VirtualInterrupt> activeVirtual;
2865             private readonly CPUEntry cpu;
2866         }
2867 
2868         public class CPUEntryWithTwoSecurityStates : CPUEntry
2869         {
CPUEntryWithTwoSecurityStates(ARM_GenericInterruptController gic, IARMTwoSecurityStatesCPU cpu, IEnumerable<GroupType> groupTypes, IReadOnlyDictionary<InterruptSignalType, IGPIO> interruptConnections)2870             public CPUEntryWithTwoSecurityStates(ARM_GenericInterruptController gic, IARMTwoSecurityStatesCPU cpu, IEnumerable<GroupType> groupTypes, IReadOnlyDictionary<InterruptSignalType, IGPIO> interruptConnections)
2871                 : base(gic, cpu, groupTypes, interruptConnections)
2872             {
2873                 this.cpu = cpu;
2874             }
2875 
Reset()2876             public override void Reset()
2877             {
2878                 EndOfInterruptModeEL3 = EndOfInterruptModes.PriorityDropAndDeactivation;
2879                 base.Reset();
2880             }
2881 
AcknowledgeBestPending(GroupTypeSecurityAgnostic groupTypeRegister, out CPUEntry sgiRequestingCPU)2882             public override InterruptId AcknowledgeBestPending(GroupTypeSecurityAgnostic groupTypeRegister, out CPUEntry sgiRequestingCPU)
2883             {
2884                 sgiRequestingCPU = null;
2885                 if(BestPending != null && groupTypeRegister == GroupTypeSecurityAgnostic.Group0 && cpu.ExceptionLevel == ExceptionLevel.EL3_MonitorMode)
2886                 {
2887                     if(BestPending.Config.GroupType == GroupType.Group1Secure)
2888                     {
2889                         return gic.IrqsDecoder.ExpectedToHandleAtSecure;
2890                     }
2891                     else if(BestPending.Config.GroupType == GroupType.Group1NonSecure)
2892                     {
2893                         return gic.IrqsDecoder.ExpectedToHandleAtNonSecure;
2894                     }
2895                 }
2896                 return base.AcknowledgeBestPending(groupTypeRegister, out sgiRequestingCPU);
2897             }
2898 
2899             public override string CurrentCPUSecurityStateString => $"{cpu.ExceptionLevel}, {cpu.SecurityState}" + (gic.DisabledSecurity ? " (GIC security disabled)" : "");
2900             public override EndOfInterruptModes CurrentEndOfInterruptMode => cpu.ExceptionLevel == ExceptionLevel.EL3_MonitorMode ? EndOfInterruptModeEL3 : EndOfInterruptModeEL1;
2901             public EndOfInterruptModes EndOfInterruptModeEL3 { get; set; }
2902 
GetBestPendingInterruptSignalType()2903             protected override InterruptSignalType GetBestPendingInterruptSignalType()
2904             {
2905                 // Based on the "4.6.2 Interrupt assignment to IRQ and FIQ signals" subsection of GICv3 and GICv4 Architecture Specification
2906                 if(BestPending == null || cpu.HasSingleSecurityState || gic.DisabledSecurity)
2907                 {
2908                     return base.GetBestPendingInterruptSignalType();
2909                 }
2910 
2911                 var groupType = BestPending.Config.GroupType;
2912                 cpu.GetAtomicExceptionLevelAndSecurityState(out var exceptionLevel, out var securityState);
2913                 if(groupType == GroupType.Group0
2914                     || (groupType == GroupType.Group1NonSecure && securityState == SecurityState.Secure) // Includes the case when the current EL is EL3_MonitorMode
2915                     || (groupType == GroupType.Group1Secure && securityState == SecurityState.NonSecure)
2916                     || (groupType == GroupType.Group1Secure && exceptionLevel == ExceptionLevel.EL3_MonitorMode && !cpu.IsEL3UsingAArch32State))
2917                 {
2918                     return InterruptSignalType.FIQ;
2919                 }
2920                 return InterruptSignalType.IRQ;
2921             }
2922 
2923             private readonly IARMTwoSecurityStatesCPU cpu;
2924         }
2925 
2926         public class InterruptState
2927         {
InterruptState()2928             public InterruptState()
2929             {
2930                 TriggerType = DefaultTriggerType;
2931             }
2932 
Reset()2933             public virtual void Reset()
2934             {
2935                 Active = false;
2936                 Pending = false;
2937                 TriggerType = DefaultTriggerType;
2938             }
2939 
Acknowledge()2940             public void Acknowledge()
2941             {
2942                 if(!Pending || Active)
2943                 {
2944                     throw new InvalidOperationException("It's invalid to acknowledge an interrupt not in the pending state.");
2945                 }
2946                 if(TriggerType == InterruptTriggerType.EdgeTriggered)
2947                 {
2948                     Active = true;
2949                     Pending = false;
2950                 }
2951                 else if(TriggerType == InterruptTriggerType.LevelSensitive)
2952                 {
2953                     Active = true;
2954                     Pending = true;
2955                 }
2956             }
2957 
AssertAsPending(bool signal)2958             public void AssertAsPending(bool signal)
2959             {
2960                 if(TriggerType == InterruptTriggerType.EdgeTriggered)
2961                 {
2962                     Pending |= signal;
2963                 }
2964                 else if(TriggerType == InterruptTriggerType.LevelSensitive)
2965                 {
2966                     Pending = signal;
2967                 }
2968             }
2969 
2970             public bool Active { get; set; }
2971             public bool IsInactive => !Active && !Pending;
2972             public bool Pending { get; set; }
2973             public ulong Bits
2974             {
2975                 get => (Active ? 0b10ul : 0) | (Pending ? 0b01ul : 0);
2976                 set
2977                 {
2978                     bool active = (value & 0b10) != 0;
2979                     bool pending = (value & 1) != 0;
2980                     Active = active;
2981                     Pending = pending;
2982                 }
2983             }
2984             public virtual InterruptTriggerType TriggerType { get; set; }
2985 
2986             protected virtual InterruptTriggerType DefaultTriggerType => default(InterruptTriggerType);
2987         }
2988 
2989         public class InterruptConfig
2990         {
Reset()2991             public virtual void Reset()
2992             {
2993                 Enabled = false;
2994                 GroupBit = false;
2995                 GroupModifierBit = false;
2996                 Priority = default(InterruptPriority);
2997             }
2998 
2999             public bool Enabled { get; set; }
3000             public bool GroupBit { get; set; }
3001             public bool GroupModifierBit { get; set; }
3002             public InterruptPriority Priority { get; set; }
3003 
3004             public GroupType GroupType
3005             {
3006                 get
3007                 {
3008                     if(GroupBit)
3009                     {
3010                         return GroupType.Group1NonSecure;
3011                     }
3012                     else if(GroupModifierBit)
3013                     {
3014                         return GroupType.Group1Secure;
3015                     }
3016                     else
3017                     {
3018                         return GroupType.Group0;
3019                     }
3020                 }
3021             }
3022         }
3023 
3024         public class Interrupt
3025         {
Interrupt(InterruptId identifier)3026             public Interrupt(InterruptId identifier) : base()
3027             {
3028                 Identifier = identifier;
3029             }
3030 
Reset()3031             public virtual void Reset()
3032             {
3033                 Config.Reset();
3034                 State.Reset();
3035             }
3036 
3037             public InterruptId Identifier { get; protected set; }
3038             public virtual InterruptConfig Config { get; } = new InterruptConfig();
3039             public virtual InterruptState State { get; } = new InterruptState();
3040         }
3041 
3042         public class SoftwareGeneratedInterruptState : InterruptState
3043         {
3044             public override InterruptTriggerType TriggerType
3045             {
3046                 get => DefaultTriggerType;
3047                 set
3048                 {
3049                     if(value != TriggerType)
3050                     {
3051                         throw new InvalidOperationException("SoftwareGeneratedInterrupt has constant trigger type.");
3052                     }
3053                 }
3054             }
3055 
3056             protected override InterruptTriggerType DefaultTriggerType => InterruptTriggerType.EdgeTriggered;
3057         }
3058 
3059         public class VirtualInterrupt : Interrupt
3060         {
VirtualInterrupt()3061             public VirtualInterrupt() : base(new InterruptId(0))
3062             {
3063                 Reset();
3064             }
3065 
Reset()3066             public override void Reset()
3067             {
3068                 base.Reset();
3069                 State.TriggerType = InterruptTriggerType.EdgeTriggered;
3070                 Identifier = new InterruptId(0);
3071                 HardwareIdentifier = new InterruptId(0);
3072                 Hardware = false;
3073             }
3074 
3075             // Performs a deep clone of the Virtual Interrupt.
Clone()3076             public VirtualInterrupt Clone()
3077             {
3078                 var cloned = new VirtualInterrupt();
3079                 cloned.Sync(this);
3080                 return cloned;
3081             }
3082 
SetIdentifier(uint id)3083             public void SetIdentifier(uint id)
3084             {
3085                 Identifier = new InterruptId(id);
3086             }
3087 
SetHardwareIdentifier(uint id)3088             public void SetHardwareIdentifier(uint id)
3089             {
3090                 HardwareIdentifier = new InterruptId(id);
3091             }
3092 
Sync(VirtualInterrupt other)3093             public void Sync(VirtualInterrupt other)
3094             {
3095                 this.Identifier = other.Identifier;
3096                 this.HardwareIdentifier = other.HardwareIdentifier;
3097                 this.Hardware = other.Hardware;
3098                 this.State.Bits = other.State.Bits;
3099                 this.Config.GroupBit = other.Config.GroupBit;
3100                 this.Config.Priority = other.Config.Priority;
3101             }
3102 
3103             public InterruptId HardwareIdentifier { get; protected set; }
3104             public bool Hardware { get; set; }
3105 
3106             public bool EOIMaitenance => !Hardware && EOIMaintenanceBit;
3107 
3108             private bool EOIMaintenanceBit => ((uint)HardwareIdentifier & (1 << 9)) != 0;
3109         }
3110 
3111         public class SoftwareGeneratedInterrupt : Interrupt
3112         {
SoftwareGeneratedInterrupt(InterruptId irqId, InterruptConfig config, CPUEntry requester)3113             public SoftwareGeneratedInterrupt(InterruptId irqId, InterruptConfig config, CPUEntry requester) : base(irqId)
3114             {
3115                 // Software Generated Interrupts with a same requester share a config.
3116                 Config = config;
3117                 Requester = requester;
3118             }
3119 
3120             public override InterruptConfig Config { get; }
3121             public override InterruptState State { get; } = new SoftwareGeneratedInterruptState();
3122             // The null value indicates that the Requester is unknown, what is typical for a GICv3 with Affinity Routing enabled.
3123             public CPUEntry Requester { get; }
3124         }
3125 
3126         public class SharedInterrupt : Interrupt
3127         {
SharedInterrupt(InterruptId irqId)3128             public SharedInterrupt(InterruptId irqId) : base(irqId) { }
3129 
Reset()3130             public override void Reset()
3131             {
3132                 base.Reset();
3133                 TargetCPUs = 0;
3134             }
3135 
IsLegacyRoutingTargetingCPU(CPUEntry cpu)3136             public bool IsLegacyRoutingTargetingCPU(CPUEntry cpu)
3137             {
3138                 return (TargetCPUs & cpu.TargetFieldFlag) != 0;
3139             }
3140 
IsLowestLegacyRoutingTargettedCPU(CPUEntry cpu)3141             public bool IsLowestLegacyRoutingTargettedCPU(CPUEntry cpu)
3142             {
3143                 return (TargetCPUs & (cpu.TargetFieldFlag - 1)) == 0;
3144             }
3145 
IsAffinityRoutingTargetingCPU(CPUEntry cpu)3146             public bool IsAffinityRoutingTargetingCPU(CPUEntry cpu)
3147             {
3148                 if(RoutingMode == InterruptRoutingMode.AnyTarget || TargetAffinity.AllLevels == cpu.Affinity.AllLevels)
3149                 {
3150                     return cpu.IsParticipatingInRouting;
3151                 }
3152                 return false;
3153             }
3154 
IsLowestAffinityRoutingTargettedCPU(CPUEntry cpu, ARM_GenericInterruptController gic)3155             public bool IsLowestAffinityRoutingTargettedCPU(CPUEntry cpu, ARM_GenericInterruptController gic)
3156             {
3157                 return cpu.IsParticipatingInRouting && (RoutingMode == InterruptRoutingMode.SpecifiedTarget ? TargetAffinity.AllLevels == cpu.Affinity.AllLevels : cpu == gic.ForcedTargettingCpuForAffinityRouting);
3158             }
3159 
3160             public byte TargetCPUs { get; set; }
3161             public MutableAffinity TargetAffinity { get; } = new MutableAffinity();
3162             public InterruptRoutingMode RoutingMode { get; set; }
3163         }
3164 
3165         // This class will be extended at least for the Binary Point register support
3166         // It may be needed to separate it for CPUInterface and Distributor
3167         public class InterruptGroup
3168         {
Reset()3169             public void Reset()
3170             {
3171                 Enabled = false;
3172             }
3173 
3174             public bool Enabled { get; set; }
3175         }
3176 
3177         public struct InterruptId
3178         {
GetRangeAntmicro.Renode.Peripherals.IRQControllers.InterruptId3179             public static IEnumerable<InterruptId> GetRange(InterruptId start, InterruptId end, uint step = 1)
3180             {
3181                 for(var id = (uint)start; id <= (uint)end; id += step)
3182                 {
3183                     yield return new InterruptId(id);
3184                 }
3185             }
3186 
3187             public static explicit operator uint(InterruptId id) => id.id;
3188             public static explicit operator int(InterruptId id) => (int)id.id;
3189 
InterruptIdAntmicro.Renode.Peripherals.IRQControllers.InterruptId3190             public InterruptId(uint interruptId)
3191             {
3192                 id = interruptId;
3193             }
3194 
ToStringAntmicro.Renode.Peripherals.IRQControllers.InterruptId3195             public override string ToString()
3196             {
3197                 return $"{id}";
3198             }
3199 
3200             private readonly uint id;
3201         }
3202 
3203         public class InterruptsDecoder
3204         {
InterruptsDecoder(uint sharedPeripheralCount, uint identifierBits)3205             public InterruptsDecoder(uint sharedPeripheralCount, uint identifierBits)
3206             {
3207                 this.sharedPeripheralCount = sharedPeripheralCount;
3208                 this.identifierBits = identifierBits;
3209 
3210                 sharedPeripheralLast = new InterruptId((uint)SharedPeripheralFirst + sharedPeripheralCount - 1);
3211                 extendedSharedPeripheralLast = new InterruptId((uint)extendedSharedPeripheralFirst + SharedPeripheralExtendedCount - 1);
3212             }
3213 
Type(InterruptId id)3214             public InterruptType Type(InterruptId id)
3215             {
3216                 if(IsSoftwareGenerated(id))
3217                 {
3218                     return InterruptType.SoftwareGenerated;
3219                 }
3220                 else if(IsPrivatePeripheral(id))
3221                 {
3222                     return InterruptType.PrivatePeripheral;
3223                 }
3224                 else if(IsSharedPeripheral(id))
3225                 {
3226                     return InterruptType.SharedPeripheral;
3227                 }
3228                 else if(IsSpecial(id))
3229                 {
3230                     return InterruptType.SpecialIdentifier;
3231                 }
3232                 else if(IsLocalitySpecificPeripheral(id))
3233                 {
3234                     return InterruptType.LocalitySpecificPeripheral;
3235                 }
3236                 return InterruptType.Reserved;
3237             }
3238 
3239             public bool IsSoftwareGenerated(InterruptId id) => (uint)id <= (uint)SoftwareGeneratedLast;
3240 
3241             public bool IsPrivatePeripheral(InterruptId id) => ((uint)PrivatePeripheralFirst <= (uint)id && (uint)id <= (uint)PrivatePeripheralLast)
3242                 || ((uint)ExtendedPrivatePeripheralFirst <= (uint)id && (uint)id <= (uint)ExtendedPrivatePeripheralLast);
3243 
3244             public bool IsSharedPeripheral(InterruptId id) => ((uint)SharedPeripheralFirst <= (uint)id && (uint)id <= (uint)SharedPeripheralLast)
3245                 || ((uint)ExtendedSharedPeripheralFirst <= (uint)id && (uint)id <= (uint)ExtendedSharedPeripheralLast);
3246 
3247             public bool IsSpecial(InterruptId id) => (uint)ExpectedToHandleAtSecure <= (uint)id && (uint)id <= (uint)NoPending;
3248 
3249             public bool IsLocalitySpecificPeripheral(InterruptId id) => (uint)id <= (uint)LocalitySpecificPeripheralFirst;
3250 
3251             public InterruptId SoftwareGeneratedFirst => softwareGeneratedFirst;
3252             public InterruptId SoftwareGeneratedLast => softwareGeneratedLast;
3253             public InterruptId PrivatePeripheralFirst => privatePeripheralFirst;
3254             public InterruptId PrivatePeripheralLast => privatePeripheralLast;
3255             public InterruptId SharedPeripheralFirst => sharedPeripheralFirst;
3256             public InterruptId SharedPeripheralLast => sharedPeripheralLast;
3257             public InterruptId ExpectedToHandleAtSecure => expectedToHandleAtSecure;
3258             public InterruptId ExpectedToHandleAtNonSecure => expectedToHandleAtNonSecure;
3259             public InterruptId NonMaskableInterruptOrGICv2GroupMismatch => nonMaskableInterruptOrGICv2GroupMismatch;
3260             public InterruptId NoPending => noPending;
3261             public InterruptId ExtendedPrivatePeripheralFirst => extendedPrivatePeripheralFirst;
3262             public InterruptId ExtendedPrivatePeripheralLast => extendedPrivatePeripheralLast;
3263             public InterruptId ExtendedSharedPeripheralFirst => extendedSharedPeripheralFirst;
3264             public InterruptId ExtendedSharedPeripheralLast => extendedSharedPeripheralLast;
3265             public InterruptId LocalitySpecificPeripheralFirst => localitySpecificPeripheralFirst;
3266             public uint IdentifierBits => identifierBits;
3267 
3268             public readonly uint SharedPeripheralExtendedCount = 1024;
3269 
3270             private readonly InterruptId softwareGeneratedFirst = new InterruptId(0);
3271             private readonly InterruptId softwareGeneratedLast = new InterruptId(15);
3272             private readonly InterruptId privatePeripheralFirst = new InterruptId(16);
3273             private readonly InterruptId privatePeripheralLast = new InterruptId(31);
3274             private readonly InterruptId sharedPeripheralFirst = new InterruptId(32);
3275             private readonly InterruptId sharedPeripheralLast; // set in the constructor
3276             private readonly InterruptId expectedToHandleAtSecure = new InterruptId(1020);
3277             private readonly InterruptId expectedToHandleAtNonSecure = new InterruptId(1021);
3278             private readonly InterruptId nonMaskableInterruptOrGICv2GroupMismatch = new InterruptId(1022);
3279             private readonly InterruptId noPending = new InterruptId(1023);
3280             private readonly InterruptId extendedPrivatePeripheralFirst = new InterruptId(1056);
3281             private readonly InterruptId extendedPrivatePeripheralLast = new InterruptId(1119);
3282             private readonly InterruptId extendedSharedPeripheralFirst = new InterruptId(4096);
3283             private readonly InterruptId extendedSharedPeripheralLast; // set in the constructor
3284             private readonly InterruptId localitySpecificPeripheralFirst = new InterruptId(819);
3285 
3286             private readonly uint sharedPeripheralCount;
3287             private readonly uint identifierBits;
3288 
3289             public const uint MaximumSharedPeripheralCount = 988;
3290             public const uint SoftwareGeneratedCount = 16;
3291         }
3292 
3293         public struct SoftwareGeneratedInterruptRequest
3294         {
SoftwareGeneratedInterruptRequestAntmicro.Renode.Peripherals.IRQControllers.SoftwareGeneratedInterruptRequest3295             public SoftwareGeneratedInterruptRequest(TargetType type, Affinity[] list, GroupType group, InterruptId id)
3296             {
3297                 TargetCPUsType = type;
3298                 TargetsList = list;
3299                 TargetGroup = group;
3300                 InterruptId = id;
3301             }
3302 
3303             public TargetType TargetCPUsType { get; }
3304             public Affinity[] TargetsList { get; }
3305             public GroupType TargetGroup { get; }
3306             public InterruptId InterruptId { get; }
3307 
3308             public enum TargetType
3309             {
3310                 TargetList = 0b00,
3311                 AllCPUs = 0b01,
3312                 Loopback = 0b10
3313             }
3314         }
3315 
3316         public enum NonSecureAccess
3317         {
3318             NotPermitted = 0b00,
3319             SecureGroup0Permitted = 0b01,
3320             BothGroupsPermitted = 0b10,
3321             Reserved = 0b11,
3322         }
3323 
3324         public enum EndOfInterruptModes
3325         {
3326             PriorityDropAndDeactivation = 0,
3327             PriorityDropOnly = 1,
3328         }
3329 
3330         public enum InterruptPriority : byte
3331         {
3332             Highest = 0x00,
3333             Idle = 0xFF
3334         }
3335 
3336         public enum InterruptTriggerType : byte
3337         {
3338             LevelSensitive = 0b00,
3339             EdgeTriggered = 0b10
3340         }
3341 
3342         public enum InterruptRoutingMode : byte
3343         {
3344             SpecifiedTarget = 0b0,
3345             AnyTarget = 0b1
3346         }
3347 
3348         public enum InterruptType
3349         {
3350             SoftwareGenerated,
3351             PrivatePeripheral,
3352             SharedPeripheral,
3353             SpecialIdentifier,
3354             LocalitySpecificPeripheral,
3355             Reserved
3356         }
3357 
3358         public enum GroupType
3359         {
3360             Group0,
3361             Group1NonSecure,
3362             Group1Secure,
3363             Group1 = Group1NonSecure,
3364         }
3365 
3366         public enum GroupTypeSecurityAgnostic
3367         {
3368             Group0,
3369             Group1
3370         }
3371 
3372         public enum DistributorRegisters : long
3373         {
3374             Control = 0x0000, // GICD_CTLR
3375             ControllerType = 0x0004, // GICD_TYPER
3376             ImplementerIdentification = 0x0008, // GICD_IIDR
3377             ControllerType2 = 0x000C2, // GICD_TYPER2
3378             ErrorReportingStatus = 0x0010, // GICD_STATUSR
3379             SharedPeripheralInterruptSetNonSecure = 0x0040, // GICD_SETSPI_NSR
3380             SharedPeripheralInterruptClearNonSecure = 0x0048, // GICD_CLRSPI_NSR
3381             SharedPeripheralInterruptSetSecure = 0x0050, // GICD_SETSPI_SR
3382             SharedPeripheralInterruptClearSecure = 0x0058, // GICD_CLRSPI_SR
3383             InterruptGroup_0 = 0x0080, // GICD_IGROUPR<n>
3384             InterruptSetEnable_0 = 0x0100, // GICD_ISENABLER<n>
3385             InterruptClearEnable_0 = 0x0180, // GICD_ICENABLER<n>
3386             InterruptSetPending_0 = 0x0200, // GICD_ISPENDR<n>
3387             InterruptClearPending_0 = 0x0280, // GICD_ICPENDR<n>
3388             InterruptSetActive_0 = 0x0300, // GICD_ISACTIVER<n>
3389             InterruptClearActive_0 = 0x0380, // GICD_ICACTIVER<n>
3390             InterruptPriority_0 = 0x0400, // GICD_IPRIORITYR<n>
3391             InterruptPriority_254 = 0x07F8, // GICD_IPRIORITYR<n>
3392             InterruptProcessorTargets_0 = 0x0800, // GICD_ITARGETSR<n>
3393             InterruptProcessorTargets_8 = 0x0820, // GICD_ITARGETSR<n>
3394             InterruptProcessorTargets_254 = 0x0AF8, // GICD_ITARGETSR<n>
3395             InterruptConfiguration_0 = 0x0C00, // GICD_ICFGR<n>
3396             InterruptConfiguration_1 = 0x0C04, // GICD_ICFGR<n>
3397             InterruptGroupModifier_0_PPIStatus = 0x0D00, // GICD_IGRPMODR0 on GICv3, GICD_PPISR on GICv1/2
3398             InterruptGroupModifier_1_SPIStatus_0 = 0x0D04, // GICD_IGRPMODR<n> on GICv3, GICD_SPISR<n> on GICv1/2
3399             NonSecureAccessControl_0 = 0x0E00, // GICD_NSACR<n>
3400             SoftwareGeneratedInterruptControl = 0x0F00, // GICD_SGI
3401             SoftwareGeneratedIntrruptClearPending_0 = 0x0F10, // GICD_CPENDSGIR<n>
3402             SoftwareGeneratedIntrruptClearPending_3 = 0x0F1C, // GICD_CPENDSGIR<n>
3403             SoftwareGeneratedIntrruptSetPending_0 = 0x0F20, // GICD_SPENDSGIR<n>
3404             SoftwareGeneratedIntrruptSetPending_3 = 0x0F2C, // GICD_SPENDSGIR<n>
3405             NonMaskableInterrupt_0 = 0x0F80, // GICD_INMIR<n>
3406             PeripheralIdentification2_v1v2 = 0xFE8, // GICD_PIDR2 for GICv1 and GICv2
3407             SharedPeripheralInterruptExtendedGroup_0 = 0x1000, // GICD_IGROUPR<n>E
3408             SharedPeripheralInterruptExtendedSetEnable_0 = 0x1200, // GICD_ISENABLER<n>E
3409             SharedPeripheralInterruptExtendedClearEnable_0 = 0x1400, // GICD_ICENABLER<n>E
3410             SharedPeripheralInterruptExtendedSetPending_0 = 0x1600, // GICD_ISPENDR<n>E
3411             SharedPeripheralInterruptExtendedClearPending_0 = 0x1800, // GICD_ICPENDR<n>E
3412             SharedPeripheralInterruptExtendedSetActive_0 = 0x1A00, // GICD_ISACTIVER<n>E
3413             SharedPeripheralInterruptExtendedClearActive_0 = 0x1C00, // GICD_ICACTIVER<n>E
3414             SharedPeripheralInterruptExtendedPriority_0 = 0x2000, // GICD_IPRIORITYR<n>E
3415             SharedPeripheralInterruptExtendedConfiguration_0 = 0x3000, // GICD_ICFGR<n>E
3416             SharedPeripheralInterruptExtendedGroupModifier_0 = 0x3400, // GICD_IGRPMODR<n>E
3417             SharedPeripheralInterruptExtendedNonSecureAccessControl_0 = 0x3600, // GICD_NSACR<n>E
3418             SharedPeripheralInterruptExtendedNonMaskable_0 = 0x3B00, // GICD_INMIR<n>E
3419             InterruptRouting_0 = 0x6100, // GICD_IROUTER<n>
3420             SharedPeripheralInterruptExtendedRouting_0 = 0x8000, // GICD_IROUTER<n>E
3421             PeripheralIdentification2_v3v4 = 0xFFE8, // GICD_PIDR2 for GICv3 and GICv4
3422         }
3423 
3424         public enum RedistributorRegisters : long
3425         {
3426             Control = 0x0000, // GICR_CTLR
3427             ImplementerIdentification = 0x0004, // GICR_IIDR
3428             ControllerType = 0x0008, // GICR_TYPER
3429             ErrorReportingStatus = 0x0010, // GICR_STATUSR
3430             Wake = 0x0014, // GICR_WAKER
3431             MaximumPARTIDAndPMG = 0x0018, // GICR_MPAMIDR
3432             SetPARTIDAndPMG = 0x001C, // GICR_PARTIDR
3433             SetLocalitySpecificPeripheralInterruptPending = 0x0040, // GICR_SETLPIR
3434             ClearLocalitySpecificPeripheralInterruptPending = 0x0048, // GICR_CLRLPIR
3435             PropertiesBaseAddress = 0x0070, // GICR_PROPBASER
3436             LocalitySpecificPeripheralInterruptPendingTableBaseAddress = 0x0078, // GICR_PENDBASER
3437             InvalidateLocalitySpecificPeripheralInterrupt = 0x00A0, // GICR_INVLPIR
3438             InvalidateAll = 0x00B0, // GICR_INVALLR
3439             Synchronize = 0x00C0, // GICR_SYNCR
3440             PeripheralIdentification2_v1v2 = 0xFE8, // GICR_PIDR2 for GICv1 and GICv2
3441 
3442             // Registers from the SGI_base frame
3443             InterruptGroup_0 = 0x0080 + RedistributorPrivateInterruptsFrameOffset, // GICR_IGROUPR0
3444             PrivatePeripheralInterruptExtendedGroup_0 = 0x0084 + RedistributorPrivateInterruptsFrameOffset, // GICR_IGROUPR<n>E
3445             InterruptSetEnable_0 = 0x0100 + RedistributorPrivateInterruptsFrameOffset, // GICR_ISENABLER0
3446             PrivatePeripheralInterruptExtendedSetEnable_0 = 0x0104 + RedistributorPrivateInterruptsFrameOffset, // GICR_ISENABLER<n>E
3447             PrivatePeripheralInterruptExtendedClearEnable_0 = 0x0184 + RedistributorPrivateInterruptsFrameOffset, // GICR_ICENABLER<n>E
3448             InterruptClearEnable_0 = 0x0180 + RedistributorPrivateInterruptsFrameOffset, // GICR_ICENABLER0
3449             InterruptSetPending0 = 0x0200 + RedistributorPrivateInterruptsFrameOffset, // GICR_ISPENDR0
3450             PrivatePeripheralInterruptExtendedSetPending_0 = 0x0204 + RedistributorPrivateInterruptsFrameOffset, // GICR_ISPENDR<n>E
3451             InterruptClearPending_0 = 0x0280 + RedistributorPrivateInterruptsFrameOffset, // GICR_ICPENDR0
3452             PrivatePeripheralInterruptExtendedClearPending_0 = 0x0284 + RedistributorPrivateInterruptsFrameOffset, // GICR_ICPENDR<n>E
3453             InterruptSetActive_0 = 0x0300 + RedistributorPrivateInterruptsFrameOffset, // GICR_ISACTIVER0
3454             PrivatePeripheralInterruptExtendedSetActive_0 = 0x0304 + RedistributorPrivateInterruptsFrameOffset, // GICR_ISACTIVER<n>E
3455             InterruptClearActive_0 = 0x0380 + RedistributorPrivateInterruptsFrameOffset, // GICR_ICACTIVER0
3456             PrivatePeripheralInterruptExtendedClearActive_0 = 0x0384 + RedistributorPrivateInterruptsFrameOffset, // GICR_ICACTIVER<n>E
3457             InterruptPriority_0 = 0x0400 + RedistributorPrivateInterruptsFrameOffset, // GICR_IPRIORITYR<n>
3458             InterruptPriority_7 = 0x041C + RedistributorPrivateInterruptsFrameOffset, // GICR_IPRIORITYR<n>
3459             PrivatePeripheralInterruptExtendedPriority_0 = 0x0420 + RedistributorPrivateInterruptsFrameOffset, // GICR_IPRIORITYR<n>E
3460             SoftwareGeneratedInterruptConfiguration = 0x0C00 + RedistributorPrivateInterruptsFrameOffset, // GICR_ICFGR0
3461             PrivatePeripheralInterruptConfiguration = 0x0C04 + RedistributorPrivateInterruptsFrameOffset, // GICR_ICFGR1
3462             PrivatePeripheralInterruptExtendedConfiguration_0 = 0x0C08 + RedistributorPrivateInterruptsFrameOffset, // GICR_ICFGR<n>E
3463             InterruptGroupModifier_0 = 0x0D00 + RedistributorPrivateInterruptsFrameOffset, // GICR_IGRPMODR0
3464             PrivatePeripheralInterruptExtendedGroupModifier_0 = 0x0D04 + RedistributorPrivateInterruptsFrameOffset, // GICR_IGRPMODR<n>E
3465             NonSecureAccessControl = 0x0E00 + RedistributorPrivateInterruptsFrameOffset, // GICR_NSACR
3466             PrivatePeripheralInterruptNonMaskable = 0x0F80 + RedistributorPrivateInterruptsFrameOffset, // GICR_INMIR0
3467             PrivatePeripheralInterruptExtendeNonMaskable_0 = 0x0F84 + RedistributorPrivateInterruptsFrameOffset, // GICR_INMIR<n>E
3468             PeripheralIdentification2_v3v4 = 0xFFE8, // GICR_PIDR2 for GICv3 and GICv4
3469         }
3470 
3471         public enum CPUInterfaceRegisters : long
3472         {
3473             Control = 0x0000, // GICC_CTLR
3474             PriorityMask = 0x0004, // GICC_PMR
3475             PriorityBinaryPoint = 0x0008, // GICC_BPR
3476             InterruptAcknowledge = 0x000C, // GICC_IAR
3477             InterruptEnd = 0x0010, // GICC_EOIR
3478             RunningPriority = 0x0014, // GICC_RPR
3479             HighestPriorityPendingInterrupt = 0x0018, // GICC_HPPIR
3480             ActivePriority = 0x001C, // GICC_ABPR
3481             InterruptAcknowledgeAlias = 0x0020, // GICC_AIAR
3482             InterruptEndAlias = 0x0024, // GICC_AEOIR
3483             HighestPriorityPendingInterruptAlias = 0x0028, // GICC_AHPPIR
3484             ErrorReportingStatus = 0x002C, // GICC_STATUSR
3485             ActivePriorities_0 = 0x00D0, // GICC_APR<n>
3486             ActivePrioritiesNonSecure_0 = 0x00E0, // GICC_NSAPR<n>
3487             InterfaceIdentification = 0x00FC, // GICC_IIDR
3488             InterruptDeactivate = 0x1000, // GICC_DIR
3489         }
3490 
3491         // Those are used for both AArch32 and AArch64 registers. For AArch32,
3492         // the encoding is slightly modified (see encode_as_aarch64_register
3493         // in tlib for details) and the names are mostly the same except for
3494         // the '_ELx' suffix.
3495         public enum CPUInterfaceSystemRegisters : long
3496         {
3497             // Enum values are created from op0, op1, CRn, CRm and op2 fields of the MRS instruction
3498             SystemRegisterEnableEL3 = 0xF665, // ICC_SRE_EL3 / ICC_MSRE
3499             SystemRegisterEnableEL1 = 0xC665, // ICC_SRE_EL1
3500             PriorityMask = 0xC230, // ICC_PMR_EL1
3501             GroupEnable1 = 0xC667, // ICC_IGRPEN1_EL1
3502             ActivePriorityGroup0_0 = 0xC644, // ICC_AP0R0_EL1
3503             ActivePriorityGroup0_1 = 0xC645, // ICC_AP0R1_EL1
3504             ActivePriorityGroup0_2 = 0xC646, // ICC_AP0R2_EL1
3505             ActivePriorityGroup0_3 = 0xC647, // ICC_AP0R3_EL1
3506             ActivePriorityGroup1_0 = 0xC648, // ICC_AP1R0_EL1
3507             ActivePriorityGroup1_1 = 0xC649, // ICC_AP1R1_EL1
3508             ActivePriorityGroup1_2 = 0xC64A, // ICC_AP1R2_EL1
3509             ActivePriorityGroup1_3 = 0xC64B, // ICC_AP1R3_EL1
3510             SoftwareGeneratedInterruptGroup1GenerateAlias = 0xC65E, // ICC_ASGI1R_EL1
3511             PriorityBinaryPointGroup0 = 0xC643, // ICC_BPR0_EL1
3512             PriorityBinaryPointGroup1 = 0xC663, // ICC_BPR1_EL1
3513             ControlEL1 = 0xC664, // ICC_CTLR_EL1
3514             ControlEL3 = 0xF664, // ICC_CTLR_EL3 / ICC_MCTLR
3515             InterruptDeactivate = 0xC659, // ICC_DIR_EL1
3516             InterruptEndGroup0 = 0xC641, // ICC_EOIR0_EL1
3517             InterruptEndGroup1 = 0xC661, // ICC_EOIR1_EL1
3518             HighestPriorityPendingInterruptGroup0 = 0xC642, // ICC_HPPIR0_EL1
3519             HighestPriorityPendingInterruptGroup1 = 0xC662, // ICC_HPPIR1_EL1
3520             InterruptAcknowledgeGroup0 = 0xC640, // ICC_IAR0_EL1
3521             InterruptAcknowledgeGroup1 = 0xC660, // ICC_IAR1_EL1
3522             GroupEnable0 = 0xC666, // ICC_IGRPEN0_EL1
3523             GroupEnable1EL3 = 0xF667, // ICC_IGRPEN1_EL3 / ICC_MGRPEN1
3524             InterruptAcknowladgeNonMaskable = 0xC64D, // ICC_NMIAR1_EL1
3525             RunningPriority = 0xC65B, // ICC_RPR_EL1
3526             SoftwareGeneratedInterruptGroup0Generate = 0xC65F, // ICC_SGI0R_EL1
3527             SoftwareGeneratedInterruptGroup1Generate = 0xC65D, // ICC_SGI1R_EL1
3528             SystemRegisterEnableEL2 = 0xE64D, // ICC_SRE_EL2
3529             HypControl = 0xE658, // ICH_HCR_EL2
3530             VGICType = 0xE659, // ICH_VTR_EL2
3531             VMControl = 0xE65F, // ICH_VMCR_EL2
3532             ListRegister_0 = 0xE660, // ICH_LR<n>_EL2
3533             ListRegisterUpper_0 = 0xE670, // ICH_LRC<n> (Aarch32 only)
3534         }
3535     }
3536 
3537     public enum ARM_GenericInterruptControllerVersion : byte
3538     {
3539         Default = 255,
3540         GICv1 = 1,
3541         GICv2 = 2,
3542         GICv3 = 3,
3543         GICv4 = 4
3544     }
3545 }
3546