1 //
2 // Copyright (c) 2010-2025 Antmicro
3 // Copyright (c) 2011-2015 Realtime Embedded
4 // Copyright (c) 2020-2021 Microsoft
5 //
6 // This file is licensed under the MIT License.
7 // Full license text is available in 'licenses/MIT.txt'.
8 //
9 using System;
10 using System.Collections;
11 using System.Collections.Generic;
12 using System.Linq;
13 using Antmicro.Renode.Core;
14 using Antmicro.Renode.Core.Structure.Registers;
15 using Antmicro.Renode.Debugging;
16 using Antmicro.Renode.Exceptions;
17 using Antmicro.Renode.Logging;
18 using Antmicro.Renode.Peripherals.Bus;
19 using Antmicro.Renode.Peripherals.CPU;
20 using Antmicro.Renode.Peripherals.Timers;
21 using Antmicro.Renode.Time;
22 using Antmicro.Renode.UserInterface;
23 using Antmicro.Renode.Utilities;
24 
25 namespace Antmicro.Renode.Peripherals.IRQControllers
26 {
27     [AllowedTranslations(AllowedTranslation.ByteToDoubleWord | AllowedTranslation.WordToDoubleWord)]
28     public class NVIC : IDoubleWordPeripheral, IHasDivisibleFrequency, IKnownSize, IIRQController
29     {
NVIC(IMachine machine, long systickFrequency = 50 * 0x800000, byte priorityMask = 0xFF, bool haltSystickOnDeepSleep = true)30         public NVIC(IMachine machine, long systickFrequency = 50 * 0x800000, byte priorityMask = 0xFF, bool haltSystickOnDeepSleep = true)
31         {
32             priorities = new ExceptionSimpleArray<byte>();
33             activeIRQs = new Stack<int>();
34             pendingIRQs = new SortedSet<int>();
35             this.machine = machine;
36             this.priorityMask = priorityMask;
37             defaultHaltSystickOnDeepSleep = haltSystickOnDeepSleep;
38             binaryPointPosition = new SecurityBanked<int>();
39             currentSevOnPending = new SecurityBanked<bool>();
40             basepri = new SecurityBanked<byte>();
41             ccr = new SecurityBanked<uint>();
42             irqs = new ExceptionSimpleArray<IRQState>();
43             targetInterruptSecurityState = new InterruptTargetSecurityState[IRQCount];
44             IRQ = new GPIO();
45             resetMachine = machine.RequestReset;
46             systick = new SecurityBanked<SysTick>
47             {
48                 NonSecureVal = new SysTick(machine, this, systickFrequency)
49             };
50             RegisterCollection = new DoubleWordRegisterCollection(this);
51             DefineRegisters();
52             Reset();
53         }
54 
AttachCPU(CortexM cpu)55         public void AttachCPU(CortexM cpu)
56         {
57             if(this.cpu != null)
58             {
59                 throw new RecoverableException("The NVIC has already attached CPU.");
60             }
61             this.cpu = cpu;
62             this.cpuId = cpu.ModelID;
63             mpuVersion = cpu.IsV8 ? MPUVersion.PMSAv8 : MPUVersion.PMSAv7;
64 
65             if(cpu.TrustZoneEnabled)
66             {
67                 systick.SecureVal = new SysTick(machine, this, systick.NonSecureVal.Frequency, true);
68             }
69 
70             if(cpu.Model == "cortex-m7")
71             {
72                 DefineTightlyCoupledMemoryControlRegisters();
73             }
74 
75             cpu.AddHookAtWfiStateChange(HandleWfiStateChange);
76         }
77 
78         public bool MaskedInterruptPresent { get { return maskedInterruptPresent; } }
79 
80         public bool PauseInsteadOfReset { get; set; }
81 
GetEnabledExternalInterrupts()82         public IEnumerable<int> GetEnabledExternalInterrupts()
83         {
84             return irqs.Skip(16).Select((x,i) => new {x,i}).Where(y => (y.x & IRQState.Enabled) != 0).Select(y => y.i).OrderBy(x => x);
85         }
86 
GetEnabledInternalInterrupts()87         public IEnumerable<int> GetEnabledInternalInterrupts()
88         {
89             return irqs.Take(16).Select((x,i) => new {x,i}).Where(y => (y.x & IRQState.Enabled) != 0).Select(y => y.i).OrderBy(x => x);
90         }
91 
92         public long Frequency
93         {
94             get => systick.Get(IsCurrentCPUInSecureState(out var _)).Frequency;
95             set
96             {
97                 systick.Get(IsCurrentCPUInSecureState(out var _)).Frequency = value;
98             }
99         }
100 
101         public int Divider
102         {
103             get => systick.Get(IsCurrentCPUInSecureState(out var _)).Divider;
104             set
105             {
106                 systick.Get(IsCurrentCPUInSecureState(out var _)).Divider = value;
107             }
108         }
109 
110         public bool HaltSystickOnDeepSleep { get; set; }
111 
112         [ConnectionRegion("NonSecure")]
ReadDoubleWordNonSecureAlias(long offset)113         public uint ReadDoubleWordNonSecureAlias(long offset)
114         {
115             if(!cpu.TrustZoneEnabled)
116             {
117                 throw new RecoverableException(TrustZoneNSRegionWarning);
118             }
119             return ReadDoubleWord(offset, false);
120         }
121 
ReadDoubleWord(long offset)122         public uint ReadDoubleWord(long offset)
123         {
124             return ReadDoubleWord(offset, IsCurrentCPUInSecureState(out var _));
125         }
126 
ReadDoubleWord(long offset, bool isSecure)127         public uint ReadDoubleWord(long offset, bool isSecure)
128         {
129             if(offset >= PriorityStart && offset < PriorityEnd)
130             {
131                 return HandlePriorityRead(offset - PriorityStart, true, isSecure);
132             }
133             if(offset >= SetEnableStart && offset < SetEnableEnd)
134             {
135                 return HandleEnableRead((int)(offset - SetEnableStart), isSecure);
136             }
137             if(offset >= ClearEnableStart && offset < ClearEnableEnd)
138             {
139                 return HandleEnableRead((int)(offset - ClearEnableStart), isSecure);
140             }
141             if(offset >= SetPendingStart && offset < SetPendingEnd)
142             {
143                 return GetPending((int)(offset - SetPendingStart), isSecure);
144             }
145             if(offset >= ClearPendingStart && offset < ClearPendingEnd)
146             {
147                 return GetPending((int)(offset - ClearPendingStart), isSecure);
148             }
149             if(offset >= ActiveBitStart && offset < ActiveBitEnd)
150             {
151                 return GetActive((int)(offset - ActiveBitStart), isSecure);
152             }
153             if(offset >= TargetNonSecureStart && offset < TargetNonSecureEnd)
154             {
155                 bool isCpu = machine.GetSystemBus(this).TryGetCurrentCPU(out var cpu);
156                 // For convenience, if we access this from outside CPU context (e.g. Monitor) let's allow this access through
157                 if(!isSecure && isCpu)
158                 {
159                     this.WarningLog("CPU {0} tries to read from ITNS register, but it's in Non-secure state", cpu);
160                     return 0;
161                 }
162                 return GetSecurityTarget((int)(offset - TargetNonSecureStart));
163             }
164             if(offset >= MPUStart && offset < MPUEnd)
165             {
166                 return HandleMPURead(offset - MPUStart);
167             }
168             if(offset >= SAUStart && offset < SAUEnd)
169             {
170                 // SAU access is filtered at tlib level
171                 return HandleSAURead(offset - SAUStart);
172             }
173             switch((Registers)offset)
174             {
175             case Registers.VectorTableOffset:
176                 return (isSecure || !cpu.TrustZoneEnabled) ? cpu.VectorTableOffset : cpu.VectorTableOffsetNonSecure;
177             case Registers.CPUID:
178                 return cpuId;
179             case Registers.CoprocessorAccessControl:
180                 return (isSecure || !cpu.TrustZoneEnabled) ? cpu.CPACR : cpu.CPACR_NS;
181             case Registers.FPContextControl:
182                 if(!IsPrivilegedMode())
183                 {
184                     this.Log(LogLevel.Warning, "Tried to read FPContextControl from an unprivileged context. Returning 0.");
185                     return 0;
186                 }
187                 return (isSecure || !cpu.TrustZoneEnabled) ? cpu.FPCCR: cpu.FPCCR_NS;
188             case Registers.FPContextAddress:
189                 if(!IsPrivilegedMode())
190                 {
191                     this.Log(LogLevel.Warning, "Tried to read FPContextAddress from an unprivileged context. Returning 0.");
192                     return 0;
193                 }
194                 return isSecure || !cpu.TrustZoneEnabled ? cpu.FPCAR : cpu.FPCAR_NS;
195             case Registers.FPDefaultStatusControl:
196                 if(!IsPrivilegedMode())
197                 {
198                     this.Log(LogLevel.Warning, "Tried to read FPDefaultStatusControl from an unprivileged context. Returning 0.");
199                     return 0;
200                 }
201                 return isSecure || !cpu.TrustZoneEnabled ? cpu.FPDSCR : cpu.FPDSCR_NS;
202             case Registers.ConfigurationAndControl:
203                 return ccr.Get(isSecure);
204             case Registers.SystemHandlerPriority1:
205             case Registers.SystemHandlerPriority2:
206             case Registers.SystemHandlerPriority3:
207                 return HandlePriorityRead(offset - 0xD14, false, isSecure);
208             case Registers.ConfigurableFaultStatus:
209                 return isSecure || !cpu.TrustZoneEnabled ? cpu.FaultStatus : cpu.FaultStatusNonSecure;
210             case Registers.InterruptControllerType:
211                 return 0b0111;
212             case Registers.MemoryFaultAddress:
213                 return isSecure || !cpu.TrustZoneEnabled ? cpu.MemoryFaultAddress : cpu.MemoryFaultAddressNonSecure;
214             default:
215                 lock(RegisterCollection)
216                 {
217                     isNextAccessSecure = isSecure;
218                     return RegisterCollection.Read(offset);
219                 }
220             }
221         }
222 
223         public GPIO IRQ { get; private set; }
224 
225         [ConnectionRegion("NonSecure")]
WriteDoubleWordNonSecureAlias(long offset, uint value)226         public void WriteDoubleWordNonSecureAlias(long offset, uint value)
227         {
228             if(!cpu.TrustZoneEnabled)
229             {
230                 throw new RecoverableException(TrustZoneNSRegionWarning);
231             }
232             WriteDoubleWord(offset, value, false);
233         }
234 
WriteDoubleWord(long offset, uint value)235         public void WriteDoubleWord(long offset, uint value)
236         {
237             WriteDoubleWord(offset, value, IsCurrentCPUInSecureState(out var _));
238         }
239 
WriteDoubleWord(long offset, uint value, bool isSecure)240         public void WriteDoubleWord(long offset, uint value, bool isSecure)
241         {
242             if(offset >= SetEnableStart && offset < SetEnableEnd)
243             {
244                 EnableOrDisableInterrupt((int)offset - SetEnableStart, value, true, isSecure);
245                 return;
246             }
247             if(offset >= PriorityStart && offset < PriorityEnd)
248             {
249                 HandlePriorityWrite(offset - PriorityStart, true, value, isSecure);
250                 return;
251             }
252             if(offset >= ClearEnableStart && offset < ClearEnableEnd)
253             {
254                 EnableOrDisableInterrupt((int)offset - ClearEnableStart, value, false, isSecure);
255                 return;
256             }
257             if(offset >= ClearPendingStart && offset < ClearPendingEnd)
258             {
259                 SetOrClearPendingInterrupt((int)offset - ClearPendingStart, value, false, isSecure);
260                 return;
261             }
262             if(offset >= SetPendingStart && offset < SetPendingEnd)
263             {
264                 SetOrClearPendingInterrupt((int)offset - SetPendingStart, value, true, isSecure);
265                 return;
266             }
267             if(offset >= TargetNonSecureStart && offset < TargetNonSecureEnd)
268             {
269                 bool isCpu = machine.GetSystemBus(this).TryGetCurrentCPU(out var cpu);
270                 // For convenience, if we access this from outside CPU context (e.g. Monitor) let's allow this access through
271                 if(!isSecure && isCpu)
272                 {
273                     this.WarningLog("CPU {0} tries to write to ITNS register, but it's in Non-secure state", cpu);
274                     return;
275                 }
276                 ModifySecurityTarget((int)(offset - TargetNonSecureStart), value);
277                 return;
278             }
279             if(offset >= MPUStart && offset < MPUEnd)
280             {
281                 HandleMPUWrite(offset - MPUStart, value);
282                 return;
283             }
284             if(offset >= SAUStart && offset < SAUEnd)
285             {
286                 // SAU access is filtered at tlib level
287                 HandleSAUWrite(offset - SAUStart, value);
288                 return;
289             }
290             switch((Registers)offset)
291             {
292             case Registers.VectorTableOffset:
293                 if(isSecure || !cpu.TrustZoneEnabled)
294                 {
295                     cpu.VectorTableOffset = value & 0xFFFFFF80;
296                 }
297                 else
298                 {
299                     cpu.VectorTableOffsetNonSecure = value & 0xFFFFFF80;
300                 }
301                 break;
302             case Registers.ApplicationInterruptAndReset:
303                 var key = value >> 16;
304                 if(key != VectKey)
305                 {
306                     this.DebugLog("Wrong key while accessing Application Interrupt and Reset Control register 0x{0:X}.", key);
307                     break;
308                 }
309                 // Key is OK, allow access to go through
310                 goto default;
311             case Registers.ConfigurableFaultStatus:
312                 if(isSecure || !cpu.TrustZoneEnabled)
313                 {
314                     cpu.FaultStatus &= ~value;
315                 }
316                 else
317                 {
318                     cpu.FaultStatusNonSecure &= ~value;
319                 }
320                 break;
321             case Registers.SystemHandlerPriority1:
322                 // 7th interrupt is ignored
323                 priorities[(int)(isSecure ? SystemException.MemManageFault_S : SystemException.MemManageFault)] = (byte)value;
324                 priorities[(int)SystemException.BusFault] = (byte)(value >> 8);
325                 priorities[(int)(isSecure ? SystemException.UsageFault_S : SystemException.UsageFault)] = (byte)(value >> 16);
326                 this.DebugLog("Priority of IRQs 4, 5, 6 set to 0x{0:X}, 0x{1:X}, 0x{2:X} respectively.", (byte)value, (byte)(value >> 8), (byte)(value >> 16));
327                 break;
328             case Registers.SystemHandlerPriority2:
329                 // only 11th is not ignored
330                 priorities[(int)(isSecure ? SystemException.SuperVisorCall_S : SystemException.SuperVisorCall)] = (byte)(value >> 24);
331                 this.DebugLog("Priority of IRQ 11 set to 0x{0:X}.", (byte)(value >> 24));
332                 break;
333             case Registers.SystemHandlerPriority3:
334                 priorities[(int)(isSecure ? SystemException.PendSV_S : SystemException.PendSV)] = (byte)(value >> 16);
335                 priorities[(int)(isSecure ? SystemException.SysTick_S : SystemException.SysTick)] = (byte)(value >> 24);
336                 this.DebugLog("Priority of IRQs 14, 15 set to 0x{0:X}, 0x{1:X} respectively.", (byte)(value >> 16), (byte)(value >> 24));
337                 break;
338             case Registers.CoprocessorAccessControl:
339                 // for ARM v8 and CP10 values:
340                 //      0b11 Full access to the FP Extension and MVE
341                 //      0b01 Privileged access only to the FP Extension and MVE
342                 //      0b00 No access to the FP Extension and MVE
343                 //      0b10 Reserved
344                 // Any attempted use without access generates a NOCP UsageFault.
345                 // same for ARM v7, but if values of CP11 and CP10 differ then effects are unpredictable
346                 if(isSecure || !cpu.TrustZoneEnabled)
347                 {
348                     cpu.CPACR = value;
349                 }
350                 else
351                 {
352                     cpu.CPACR_NS = value;
353                 }
354                 // Enable FPU if any access is permitted, privilege checks in tlib use CPACR register.
355                 // Similarly, if TrustZone is enabled, CPACR should be used to check if FPU is enabled in respective Security state
356                 if((value & 0x100000) == 0x100000)
357                 {
358                     this.DebugLog("Enabling FPU.");
359                     cpu.FpuEnabled = true;
360                 }
361                 else
362                 {
363                     this.DebugLog("Disabling FPU.");
364                     cpu.FpuEnabled = false;
365                 }
366                 break;
367             case Registers.SoftwareTriggerInterrupt:
368                 // This register is implemented only in ARMv7m and ARMv8m
369                 if(cpu.Model == "cortex-m3" || cpu.Model == "cortex-m4" || cpu.Model == "cortex-m4f" || cpu.Model == "cortex-m7")
370                 {
371                     SetPendingIRQ((int)(16 + value));
372                 }
373                 else
374                 {
375                     this.Log(LogLevel.Error, "Software Trigger Interrupt Register not implemented for {0}", cpu.Model);
376                 }
377                 break;
378             case Registers.FPContextControl:
379                 if(!IsPrivilegedMode())
380                 {
381                     this.Log(LogLevel.Warning, "Writing to FPContextControl requires privileged access.");
382                     break;
383                 }
384                 if(isSecure || !cpu.TrustZoneEnabled)
385                 {
386                     cpu.FPCCR = value;
387                 }
388                 else
389                 {
390                     cpu.FPCCR_NS = value;
391                 }
392                 break;
393             case Registers.FPContextAddress:
394                 if(!IsPrivilegedMode())
395                 {
396                     this.Log(LogLevel.Warning, "Writing to FPContextAddress requires privileged access.");
397                     break;
398                 }
399                 // address must be 8-byte aligned
400                 if(isSecure || !cpu.TrustZoneEnabled)
401                 {
402                     cpu.FPCAR = value & ~0x7u;
403                 }
404                 else
405                 {
406                     cpu.FPCAR_NS = value & ~0x7u;
407                 }
408                 break;
409             case Registers.FPDefaultStatusControl:
410                 if(!IsPrivilegedMode())
411                 {
412                     this.Log(LogLevel.Warning, "Writing to FPDefaultStatusControl requires privileged access.");
413                     break;
414                 }
415                 // set only not reserved values
416                 if(isSecure || !cpu.TrustZoneEnabled)
417                 {
418                     cpu.FPDSCR = value & 0x07c00000;
419                 }
420                 else
421                 {
422                     cpu.FPDSCR_NS = value & 0x07c00000;
423                 }
424                 break;
425             case Registers.ConfigurationAndControl:
426                 ccr.Get(isSecure) = value;
427                 break;
428             default:
429                 lock(RegisterCollection)
430                 {
431                     isNextAccessSecure = isSecure;
432                     RegisterCollection.Write(offset, value);
433                 }
434                 break;
435             }
436         }
437 
Reset()438         public void Reset()
439         {
440             RegisterCollection.Reset();
441             InitInterrupts();
442             for(var i = 0; i < priorities.Length; i++)
443             {
444                 priorities[i] = 0x00;
445             }
446             activeIRQs.Clear();
447             systick.NonSecureVal.Reset();
448             systick.SecureVal?.Reset();
449 
450             IRQ.Unset();
451             currentSevOnPending.Reset();
452             mpuControlRegister = 0;
453             HaltSystickOnDeepSleep = defaultHaltSystickOnDeepSleep;
454             canResetOnlyFromSecure = false;
455             deepSleepOnlyFromSecure = false;
456             binaryPointPosition.Reset();
457 
458             // bit [16] DC / Cache enable. This is a global enable bit for data and unified caches.
459             ccr.Reset(0x10000);
460         }
461 
462         public long Size
463         {
464             get
465             {
466                 return 0x1000;
467             }
468         }
469 
AcknowledgeIRQ()470         public int AcknowledgeIRQ()
471         {
472             lock(irqs)
473             {
474                 var result = FindPendingInterrupt();
475                 if(result != SpuriousInterrupt)
476                 {
477                     irqs[result] |= IRQState.Active;
478                     irqs[result] &= ~IRQState.Pending;
479                     pendingIRQs.Remove(result);
480                     this.NoisyLog("Acknowledged IRQ {0}.", ExceptionToString(result));
481                     activeIRQs.Push(result);
482                 }
483                 // at this point we can surely deactivate interrupt, because the best was chosen
484                 IRQ.Set(false);
485                 return result;
486             }
487         }
488 
CompleteIRQ(int number)489         public void CompleteIRQ(int number)
490         {
491             lock(irqs)
492             {
493                 var currentIRQ = irqs[number];
494                 if((currentIRQ & IRQState.Active) == 0)
495                 {
496                     this.Log(LogLevel.Error, "Trying to complete not active IRQ {0}.", ExceptionToString(number));
497                     return;
498                 }
499                 irqs[number] &= ~IRQState.Active;
500                 var activeIRQ = activeIRQs.Pop();
501                 if(activeIRQ != number)
502                 {
503                     this.Log(LogLevel.Error, "Trying to complete IRQ {0} that was not the last active. Last active was {1}.", ExceptionToString(number), ExceptionToString(activeIRQ));
504                     return;
505                 }
506                 if((currentIRQ & IRQState.Running) > 0)
507                 {
508                     this.NoisyLog("Completed IRQ {0} active -> pending.", ExceptionToString(number));
509                     irqs[number] |= IRQState.Pending;
510                     pendingIRQs.Add(number);
511                 }
512                 else if((currentIRQ & IRQState.Pending) != 0)
513                 {
514                     this.NoisyLog("Completed IRQ {0} active -> pending.", number);
515                 }
516                 else
517                 {
518                     this.NoisyLog("Completed IRQ {0} active -> inactive.", ExceptionToString(number));
519                 }
520                 FindPendingInterrupt();
521             }
522         }
523 
SetPendingIRQ(int number)524         public void SetPendingIRQ(int number)
525         {
526             lock(irqs)
527             {
528                 this.NoisyLog("Internal IRQ {0}.", ExceptionToString(number));
529                 SetPending(number);
530                 FindPendingInterrupt();
531             }
532         }
533 
OnGPIO(int number, bool value)534         public void OnGPIO(int number, bool value)
535         {
536             number += 16; // because this is HW interrupt
537             this.NoisyLog("External IRQ {0}: {1}", number, value);
538             var pendingInterrupt = SpuriousInterrupt;
539             lock(irqs)
540             {
541                 if(value)
542                 {
543                     irqs[number] |= IRQState.Running;
544                     SetPending(number);
545                 }
546                 else
547                 {
548                     irqs[number] &= ~IRQState.Running;
549                 }
550                 pendingInterrupt = FindPendingInterrupt();
551             }
552             if(pendingInterrupt != SpuriousInterrupt && value)
553             {
554                 // We assume both SysTicks are woken up on exiting deep sleep
555                 // docs aren't clear on this, but this seems like a logical behavior
556                 if(!systick.NonSecureVal.Enabled)
557                 {
558                     this.NoisyLog("Waking up from deep sleep");
559                 }
560                 systick.NonSecureVal.Enabled |= value;
561                 if(cpu.TrustZoneEnabled)
562                 {
563                     systick.SecureVal.Enabled |= value;
564                 }
565             }
566         }
567 
SetSevOnPendingOnAllCPUs(bool value)568         public void SetSevOnPendingOnAllCPUs(bool value)
569         {
570             foreach(var cpu in machine.SystemBus.GetCPUs().OfType<Arm>())
571             {
572                 cpu.SetSevOnPending(value);
573             }
574         }
575 
SetSleepOnExceptionExitOnAllCPUs(bool value)576         public void SetSleepOnExceptionExitOnAllCPUs(bool value)
577         {
578             foreach(var cpu in machine.SystemBus.GetCPUs().OfType<CortexM>())
579             {
580                 cpu.SetSleepOnExceptionExit(value);
581             }
582         }
583 
584         public DoubleWordRegisterCollection RegisterCollection { get; }
585 
586         public bool DeepSleepEnabled { get; set; }
587 
DefineRegisters()588         private void DefineRegisters()
589         {
590             Registers.SysTickControl.Define(RegisterCollection)
591                 .WithFlag(0,
592                     changeCallback: (_, value) => systick.Get(isNextAccessSecure).Enabled = value,
593                     valueProviderCallback: _ => systick.Get(isNextAccessSecure).Enabled,
594                     name: "ENABLE")
595                 .WithFlag(1,
596                     valueProviderCallback: _ => systick.Get(isNextAccessSecure).TickInterruptEnabled,
597                     changeCallback: (_, newValue) =>
598                     {
599                         this.NoisyLog("Systick_{0} interrupt {1}", isNextAccessSecure ? "S" : "NS", newValue  ? "enabled" : "disabled");
600                         systick.Get(isNextAccessSecure).TickInterruptEnabled = newValue;
601                     }, name: "TICKINT")
602                 // If no external clock is provided, this bit reads as 1 and ignores writes.
603                 .WithFlag(2, FieldMode.Read, valueProviderCallback: _ => true, name: "CLKSOURCE") // SysTick uses the processor clock
604                 .WithReservedBits(3, 13)
605                 .WithFlag(16, FieldMode.ReadToClear,
606                     valueProviderCallback: _ =>
607                     {
608                         var ret = systick.Get(isNextAccessSecure).CountFlag;
609                         systick.Get(isNextAccessSecure).CountFlag = false;
610                         return ret;
611                     },
612                     name: "COUNTFLAG")
613                 .WithReservedBits(17, 15);
614 
615             Registers.SysTickReloadValue.Define(RegisterCollection)
616                 .WithValueField(0, 24,
617                     valueProviderCallback: _ => systick.Get(isNextAccessSecure).Reload,
618                     changeCallback: (_, newValue) => systick.Get(isNextAccessSecure).Reload = newValue,
619                     name: "RELOAD")
620                 .WithReservedBits(24, 8);
621 
622             Registers.SysTickValue.Define(RegisterCollection)
623                 .WithValueField(0, 24,
624                     writeCallback: (_, __) => systick.Get(isNextAccessSecure).UpdateSystickValue(),
625                     valueProviderCallback: _ =>
626                     {
627                         cpu?.SyncTime();
628                         return (uint)systick.Get(isNextAccessSecure).Value;
629                     }, name: "CURRENT")
630                 .WithReservedBits(24, 8);
631 
632             Registers.SysTickCalibrationValue.Define(RegisterCollection)
633                 // Note that some reference manuals state that this value is for 1ms interval and not for 10ms
634                 .WithValueField(0, 24, FieldMode.Read, valueProviderCallback: _ => SysTickMaxValue & (uint)(systick.Get(isNextAccessSecure).Frequency / SysTickCalibration100Hz), name: "TENMS")
635                 .WithReservedBits(24, 6)
636                 .WithFlag(30, FieldMode.Read, valueProviderCallback: _ => systick.Get(isNextAccessSecure).Frequency % SysTickCalibration100Hz != 0, name: "SKEW")
637                 .WithFlag(31, FieldMode.Read, valueProviderCallback: _ => true, name: "NOREF");
638 
639             Registers.InterruptControlState.Define(RegisterCollection)
640                 .WithValueField(0, 9, FieldMode.Read, valueProviderCallback: _ => (uint)(activeIRQs.Count == 0 ? 0 : activeIRQs.Peek()), name: "VECTACTIVE")
641                 .WithReservedBits(9, 2)
642                 .WithTaggedFlag("RETTOBASE", 11)
643                 .WithValueField(12, 9, FieldMode.Read, valueProviderCallback: _ => (uint)FindPendingInterrupt(), name: "VECTPENDING")
644                 .WithReservedBits(21, 1)
645                 .WithTaggedFlag("ISRPENDING", 22)
646                 .WithTaggedFlag("ISRPREEMPT", 23)
647                 .WithTaggedFlag("STTNS", 24)
648                 .WithFlag(25, FieldMode.WriteOneToClear, writeCallback: (_, value) =>
649                 {
650                     if(value)
651                     {
652                         ClearPending((int)(isNextAccessSecure ? SystemException.SysTick_S : SystemException.SysTick));
653                     }
654                 }, name: "PENDSTCLR")
655                 .WithFlag(26, writeCallback: (_, value) =>
656                 {
657                     if(value)
658                     {
659                         SetPendingIRQ((int)(isNextAccessSecure ? SystemException.SysTick_S : SystemException.SysTick));
660                     }
661                 }, valueProviderCallback: _ => irqs[(int)SystemException.SysTick].HasFlag(IRQState.Pending), name: "PENDSTSET")
662                 .WithFlag(27, FieldMode.WriteOneToClear, writeCallback: (_, value) =>
663                 {
664                     if(value)
665                     {
666                         ClearPending((int)(isNextAccessSecure ? SystemException.PendSV_S : SystemException.PendSV));
667                     }
668                 }, name: "PENDSVCLR")
669                 .WithFlag(28, writeCallback: (_, value) =>
670                 {
671                     if(value)
672                     {
673                         SetPendingIRQ((int)(isNextAccessSecure ? SystemException.PendSV_S : SystemException.PendSV));
674                     }
675                 }, valueProviderCallback: _ => irqs[(int)SystemException.PendSV].HasFlag(IRQState.Pending), name: "PENDSVSET")
676                 .WithReservedBits(29, 1)
677                 .WithFlag(30, FieldMode.WriteOneToClear, writeCallback: (_, value) =>
678                 {
679                     if(value)
680                     {
681                         ClearPending((int)SystemException.NMI);
682                     }
683                 }, name: "PENDNMICLR")
684                 .WithFlag(31, writeCallback: (_, value) =>
685                 {
686                     if(value)
687                     {
688                         SetPendingIRQ((int)SystemException.NMI);
689                     }
690                 }, valueProviderCallback: _ => irqs[(int)SystemException.NMI].HasFlag(IRQState.Pending), name: "PENDNMISET");
691 
692             Registers.SystemControlRegister.Define(RegisterCollection)
693                 .WithReservedBits(0, 1)
694                 .WithFlag(1, out sleepOnExitEnabled, name: "SLEEPONEXIT",
695                     changeCallback: (_, value) => SetSleepOnExceptionExitOnAllCPUs(value))
696                 .WithFlag(2,
697                     writeCallback: (_, value) =>
698                     {
699                         if(!isNextAccessSecure && deepSleepOnlyFromSecure)
700                         {
701                             this.WarningLog("Trying to set SLEEPDEEP but SLEEPDEEPS is set and the access is Non-secure. Ignoring");
702                             return;
703                         }
704                         DeepSleepEnabled = value;
705                     },
706                     valueProviderCallback: _ =>
707                     {
708                         if(!isNextAccessSecure && deepSleepOnlyFromSecure)
709                         {
710                             return false;
711                         }
712                         return DeepSleepEnabled;
713                     },
714                     name: "SLEEPDEEP")
715                 .WithFlag(3,
716                     writeCallback: (_, value) =>
717                     {
718                         if(isNextAccessSecure)
719                         {
720                             deepSleepOnlyFromSecure = value;
721                         }
722                     },
723                     valueProviderCallback: _ =>
724                     {
725                         if(!isNextAccessSecure)
726                         {
727                             return false;
728                         }
729                         return deepSleepOnlyFromSecure;
730                     },
731                     name: "SLEEPDEEPS")
732                 .WithFlag(4,
733                     changeCallback: (_, value) =>
734                     {
735                         SetSevOnPendingOnAllCPUs(value);
736                         currentSevOnPending.Get(isNextAccessSecure) = value;
737                     },
738                     valueProviderCallback: _ => currentSevOnPending.Get(isNextAccessSecure),
739                     name: "SEVONPEND")
740                 .WithReservedBits(5, 27);
741 
742             Registers.ApplicationInterruptAndReset.Define(RegisterCollection)
743                 .WithReservedBits(0, 1)
744                 .WithTaggedFlag("VECTCLRACTIVE", 1)
745                 .WithFlag(2, writeCallback: (_, value) =>
746                 {
747                     if(value)
748                     {
749                         if(canResetOnlyFromSecure && !isNextAccessSecure)
750                         {
751                             this.WarningLog("Requested reset with SYSRESETREQ but SYSRESETREQS is set and the access is Non-secure. Ignoring");
752                             return;
753                         }
754                         this.InfoLog("Resetting platform with SYSRESETREQ");
755                         if(PauseInsteadOfReset)
756                         {
757                             machine.Pause();
758                         }
759                         else
760                         {
761                             resetMachine();
762                         }
763                     }
764                 }, name: "SYSRESETREQ")
765                 .WithFlag(3, writeCallback: (_, value) =>
766                 {
767                     if(!isNextAccessSecure)
768                     {
769                         // This bit is RAZ/WI from Non-secure state.
770                         return;
771                     }
772                     canResetOnlyFromSecure = value;
773                 }, valueProviderCallback: _ =>
774                 {
775                     if(!isNextAccessSecure)
776                     {
777                         // This bit is RAZ/WI from Non-secure state.
778                         return false;
779                     }
780                     return canResetOnlyFromSecure;
781                 }, name: "SYSRESETREQS")
782                 .WithTaggedFlag("DIT", 4)
783                 .WithTaggedFlag("IESB", 5)
784                 .WithReservedBits(6, 2)
785                 .WithValueField(8, 3, writeCallback: (_, value) =>
786                 {
787                     binaryPointPosition.Get(isNextAccessSecure) = (int)value;
788                 }, name: "PRIGROUP")
789                 .WithReservedBits(11, 2)
790                 .WithFlag(13, writeCallback: (_, value) =>
791                 {
792                     if(!isNextAccessSecure)
793                     {
794                         // This bit is WI from Non-secure state.
795                         return;
796                     }
797                     var sec = value ? InterruptTargetSecurityState.NonSecure : InterruptTargetSecurityState.Secure;
798                     foreach(var excp in new SystemException[] {SystemException.NMI, SystemException.HardFault, SystemException.BusFault})
799                     {
800                         targetInterruptSecurityState[(int)excp] = sec;
801                     }
802                 }, name: "BFHFNMINS")
803                 .WithFlag(14, writeCallback: (_, value) =>
804                 {
805                     if(!isNextAccessSecure)
806                     {
807                         // This bit is RAZ/WI from Non-secure state.
808                         return;
809                     }
810                     prioritizeSecureInterrupts = value;
811                 }, valueProviderCallback: _ =>
812                 {
813                     if(!isNextAccessSecure)
814                     {
815                         // This bit is RAZ/WI from Non-secure state.
816                         return false;
817                     }
818                     return prioritizeSecureInterrupts;
819                 }, name: "PRIS")
820                 .WithTaggedFlag(name: "ENDIANNESS", 15)
821                 // We guard access with VectKey in `WriteDoubleWord` - here we just return the expected value
822                 .WithValueField(16, 16, valueProviderCallback: _ => VectKeyStat, name: "VECTKEYSTAT");
823 
824 
825             Registers.SystemHandlerControlAndState.Define(RegisterCollection)
826                 .WithTaggedFlag("MEMFAULTACT (Memory Manage Active)", 0)
827                 .WithTaggedFlag("BUSFAULTACT (Bus Fault Active)", 1)
828                 .WithReservedBits(2, 1)
829                 .WithTaggedFlag("USGFAULTACT (Usage Fault Active)", 3)
830                 .WithReservedBits(4, 3)
831                 .WithTaggedFlag("SVCALLACT (SV Call Active)", 7)
832                 .WithTaggedFlag("MONITORACT (Monitor Active)", 8)
833                 .WithReservedBits(9, 1)
834                 .WithTaggedFlag("PENDSVACT (Pend SV Active)", 10)
835                 .WithTaggedFlag("SYSTICKACT (Sys Tick Active)", 11)
836                 .WithTaggedFlag("USGFAULTPENDED (Usage Fault Pending)", 12)
837                 .WithTaggedFlag("MEMFAULTPENDED (Mem Manage Pending)", 13)
838                 .WithTaggedFlag("BUSFAULTPENDED (Bus Fault Pending)", 14)
839                 .WithTaggedFlag("SVCALLPENDED (SV Call Pending)", 15)
840                 // The enable flags only store written data.
841                 // Changing them doesn't change a behavior of the model.
842                 .WithFlag(16, name: "MEMFAULTENA (Memory Manage Fault Enable)")
843                 .WithFlag(17, name: "BUSFAULTENA (Bus Fault Enable)")
844                 .WithFlag(18, name: "USGFAULTENA (Usage Fault Enable)")
845                 .WithReservedBits(19, 13)
846                 .WithChangeCallback((_, val) =>
847                     this.Log(LogLevel.Warning, "Changing value of the SHCSR register to 0x{0:X}, the register isn't supported by Renode", val)
848                 );
849 
850             Registers.CacheSizeSelection.Define(RegisterCollection)
851                 .WithTaggedFlag("InD (Instruction or Data Selection)", 0)
852                 .WithTag("Level", 1, 3)
853                 .WithReservedBits(4, 28);
854 
855             Registers.CacheSizeID.Define(RegisterCollection)
856                 .WithTag("LineSize", 0, 3)
857                 .WithTag("Associativity", 3, 10)
858                 .WithTag("NumSets", 13, 15)
859                 .WithTaggedFlag("WA (Write Allocation Support)", 28)
860                 .WithTaggedFlag("RA (Read Allocation Support)", 29)
861                 .WithTaggedFlag("WB (Write Back Support)", 30)
862                 .WithTaggedFlag("WT (Write Through Support)", 31);
863 
864             Registers.DebugExceptionAndMonitorControlRegister.Define(RegisterCollection)
865                 .WithTaggedFlag("VC_CORERESET (Reset Vector Catch)", 0)
866                 .WithReservedBits(1, 3)
867                 .WithTaggedFlag("VC_MMERR (Debug trap on Memory Management faults)", 4)
868                 .WithTaggedFlag("VC_NOCPERR (Debug trap on Usage Fault access to Coprocessor which is not present)", 5)
869                 .WithTaggedFlag("VC_CHKERR (Debug trap on Usage Fault enabled checking errors)", 6)
870                 .WithTaggedFlag("VC_STATERR (Debug trap on Usage Fault state error)", 7)
871                 .WithTaggedFlag("VC_BUSERR (Debug trap on normal Bus error)", 8)
872                 .WithTaggedFlag("VC_INTERR (Debug trap on interrupt/exception service errors)", 9)
873                 .WithTaggedFlag("VC_HARDERR (Debug trap on Hard Fault)", 10)
874                 .WithReservedBits(11, 5)
875                 .WithTaggedFlag("MON_EN (Monitor Enable)", 16)
876                 .WithTaggedFlag("MON_PEND (Monitor Pend)", 17)
877                 .WithTaggedFlag("MON_STEP (Monitor Step)", 18)
878                 .WithTaggedFlag("MON_REQ (Monitor Request)", 19)
879                 .WithReservedBits(20, 4)
880                 // The trace flag only store written data.
881                 // Changing it doesn't change the behavior of the model.
882                 .WithFlag(24, name: "TRCENA (Trace Enable)")
883                 .WithReservedBits(25, 7);
884 
885             Registers.SecureFaultStatus.Define(RegisterCollection)
886                 .WithValueField(0, 8, writeCallback: (_, value) =>
887                 {
888                     if(!isNextAccessSecure)
889                     {
890                         // This bit is RAZ/WI from Non-secure state.
891                         return;
892                     }
893                     cpu.SecureFaultStatus = (uint)value;
894                 }, valueProviderCallback: _ =>
895                 {
896                     if(!isNextAccessSecure)
897                     {
898                         // This bit is RAZ/WI from Non-secure state.
899                         return 0;
900                     }
901                     return cpu.SecureFaultStatus;
902                 }, name: "Status bits")
903                 .WithReservedBits(8, 24);
904 
905             /* While the ISA manual permits this to be shared with MMFAR, we keep it separate,
906              * so we don't have to worry about invalidating it between exceptions.
907              * If there is an address here, it's always valid */
908             Registers.SecureFaultAddress.Define(RegisterCollection)
909                 .WithValueField(0, 32, FieldMode.Read, valueProviderCallback: _ => isNextAccessSecure ? cpu.SecureFaultAddress : 0, name: "Address");
910         }
911 
DefineTightlyCoupledMemoryControlRegisters()912         private void DefineTightlyCoupledMemoryControlRegisters()
913         {
914             // The ITCMC and DTCMC registers have same fields.
915             Registers.InstructionTightlyCoupledMemoryControl.DefineMany(RegisterCollection, 2, setup: (reg, index) => reg
916                 .WithTaggedFlag("EN (TCM Enable)", 0)
917                 .WithTaggedFlag("RMW (Read Modify Write Enable)", 1)
918                 .WithTaggedFlag("RETEN (Retry Phase Enable)", 2)
919                 .WithTag("SZ (TCM Size)", 3, 4)
920                 .WithReservedBits(7, 25)
921             );
922         }
923 
InitInterrupts()924         private void InitInterrupts()
925         {
926             irqs.Clear();
927             Array.Clear(targetInterruptSecurityState, 0, targetInterruptSecurityState.Length);
928             for(var i = 0; i < 16; i++)
929             {
930                 irqs[i] = IRQState.Enabled;
931             }
932             foreach(var i in bankedInterrupts)
933             {
934                 irqs[i] = IRQState.Enabled;
935             }
936             maskedInterruptPresent = false;
937             prioritizeSecureInterrupts = false;
938             pendingIRQs.Clear();
939         }
940 
GetStartingInterrupt(long offset, bool externalInterrupt)941         private static int GetStartingInterrupt(long offset, bool externalInterrupt)
942         {
943             return (int)(offset + (externalInterrupt ? 16 : 0));
944         }
945 
HandlePriorityWrite(long offset, bool externalInterrupt, uint value, bool isSecure)946         private void HandlePriorityWrite(long offset, bool externalInterrupt, uint value, bool isSecure)
947         {
948             lock(irqs)
949             {
950                 var startingInterrupt = GetStartingInterrupt(offset, externalInterrupt);
951                 for(var i = startingInterrupt; i < startingInterrupt + 4; i++)
952                 {
953                     if(!isSecure && !IsInterruptTargetNonSecure(i))
954                     {
955                         this.WarningLog("Cannot set priority for IRQ {0}, since it targets Secure state, and the access is in Non-secure",
956                             ExceptionToString(i));
957                         continue;
958                     }
959 
960                     if((((byte)value) & ~priorityMask) != 0)
961                     {
962                         this.Log(LogLevel.Warning, "Trying to set the priority for interrupt {0} to 0x{1:X}, but it should be maskable with 0x{2:X}", i, value, priorityMask);
963                     }
964 
965                     priorities[i] = (byte)(value & priorityMask);
966 
967                     this.DebugLog("Priority 0x{0:X} set for interrupt {1}.", priorities[i], i);
968                     value >>= 8;
969                 }
970             }
971         }
972 
HandlePriorityRead(long offset, bool externalInterrupt, bool isSecure)973         private uint HandlePriorityRead(long offset, bool externalInterrupt, bool isSecure)
974         {
975             lock(irqs)
976             {
977                 var returnValue = 0u;
978                 var startingInterrupt = GetStartingInterrupt(offset, externalInterrupt);
979                 for(var i = startingInterrupt + 3; i >= startingInterrupt; i--)
980                 {
981                     returnValue <<= 8;
982                     // If we are in Secure state, get Secure variant for banked exception
983                     if(isSecure && bankedInterrupts.Contains(i))
984                     {
985                         returnValue |= priorities[i | BankedExcpSecureBit];
986                     }
987                     else
988                     {
989                         // Cannot read the priority of a Secure interrupt in a Non-secure state, read zero instead
990                         if(isSecure || IsInterruptTargetNonSecure(i))
991                         {
992                             returnValue |= priorities[i];
993                         }
994                     }
995                 }
996                 return returnValue;
997             }
998         }
999 
HandleMPURead(long offset)1000         private uint HandleMPURead(long offset)
1001         {
1002             switch(mpuVersion)
1003             {
1004                 case MPUVersion.PMSAv7:
1005                     return HandleMPUReadV7(offset);
1006                 case MPUVersion.PMSAv8:
1007                     return HandleMPUReadV8(offset);
1008                 default:
1009                     throw new Exception("Attempted MPU read, but the MPU version is unknown");
1010             }
1011         }
1012 
HandleMPUWrite(long offset, uint value)1013         private void HandleMPUWrite(long offset, uint value)
1014         {
1015             switch(mpuVersion)
1016             {
1017                 case MPUVersion.PMSAv7:
1018                     HandleMPUWriteV7(offset, value);
1019                     break;
1020                 case MPUVersion.PMSAv8:
1021                     HandleMPUWriteV8(offset, value);
1022                     break;
1023                 default:
1024                     throw new Exception("Attempted MPU write, but the mpuVersion is unknown");
1025             }
1026         }
1027 
HandleMPUWriteV7(long offset, uint value)1028         private void HandleMPUWriteV7(long offset, uint value)
1029         {
1030             this.Log(LogLevel.Debug, "MPU: Trying to write to {0} (value: 0x{1:X08})", Enum.GetName(typeof(RegistersV7), offset), value);
1031             switch((RegistersV7)offset)
1032             {
1033                 case RegistersV7.Type:
1034                     this.Log(LogLevel.Warning, "MPU: Trying to write to a read-only register (MPU_TYPE)");
1035                     break;
1036                 case RegistersV7.Control:
1037                     if((mpuControlRegister & 0x1) != (value & 0x1))
1038                     {
1039                         this.cpu.MPUEnabled = (value & 0x1) != 0x0;
1040                     }
1041                     mpuControlRegister = value;
1042                     break;
1043                 case RegistersV7.RegionNumber: // MPU_RNR
1044                     cpu.MPURegionNumber = value;
1045                     break;
1046                 case RegistersV7.RegionBaseAddress:
1047                 case RegistersV7.RegionBaseAddressAlias1:
1048                 case RegistersV7.RegionBaseAddressAlias2:
1049                 case RegistersV7.RegionBaseAddressAlias3:
1050                     cpu.MPURegionBaseAddress = value;
1051                     break;
1052                 case RegistersV7.RegionAttributeAndSize:
1053                 case RegistersV7.RegionAttributeAndSizeAlias1:
1054                 case RegistersV7.RegionAttributeAndSizeAlias2:
1055                 case RegistersV7.RegionAttributeAndSizeAlias3:
1056                     cpu.MPURegionAttributeAndSize = value;
1057                     break;
1058             }
1059         }
1060 
HandleMPUWriteV8(long offset, uint value)1061         private void HandleMPUWriteV8(long offset, uint value)
1062         {
1063             this.Log(LogLevel.Debug, "MPU: Trying to write to {0} (value: 0x{1:X08})", Enum.GetName(typeof(RegistersV8), offset), value);
1064 
1065             if (cpu.NumberOfMPURegions == 0)
1066             {
1067                 this.Log(LogLevel.Error, $"CPU abort [PC={cpu.PC:x}]. Attempted a write to an MPU register, but the CPU doesn't support MPU. Set 'numberOfMPURegions' in CPU configuration to enable it.");
1068                 throw new CpuAbortException();
1069             }
1070             switch((RegistersV8)offset)
1071             {
1072                 case RegistersV8.Type:
1073                     this.Log(LogLevel.Warning, "MPU: Trying to write to a read-only register (MPU_TYPE)");
1074                     break;
1075                 case RegistersV8.Control:
1076                     cpu.PmsaV8Ctrl = value;
1077                     break;
1078                 case RegistersV8.RegionNumberRegister:
1079                     cpu.PmsaV8Rnr = value;
1080                     break;
1081                 case RegistersV8.RegionBaseAddressRegister:
1082                 case RegistersV8.RegionBaseAddressRegisterAlias1:
1083                 case RegistersV8.RegionBaseAddressRegisterAlias2:
1084                 case RegistersV8.RegionBaseAddressRegisterAlias3:
1085                     cpu.PmsaV8Rbar = value;
1086                     break;
1087                 case RegistersV8.RegionLimitAddressRegister:
1088                 case RegistersV8.RegionLimitAddressRegisterAlias1:
1089                 case RegistersV8.RegionLimitAddressRegisterAlias2:
1090                 case RegistersV8.RegionLimitAddressRegisterAlias3:
1091                     cpu.PmsaV8Rlar = value;
1092                     break;
1093                 case RegistersV8.MemoryAttributeIndirectionRegister0:
1094                     cpu.PmsaV8Mair0 = value;
1095                     break;
1096                 case RegistersV8.MemoryAttributeIndirectionRegister1:
1097                     cpu.PmsaV8Mair1 = value;
1098                     break;
1099             }
1100         }
1101 
HandleMPUReadV7(long offset)1102         private uint HandleMPUReadV7(long offset)
1103         {
1104             uint value;
1105             switch((RegistersV7)offset)
1106             {
1107                 case RegistersV7.Type:
1108                     value = (cpu.NumberOfMPURegions & 0xFF) << 8;
1109                     break;
1110                 case RegistersV7.Control:
1111                     value = mpuControlRegister;
1112                     break;
1113                 case RegistersV7.RegionNumber:
1114                     value = cpu.MPURegionNumber;
1115                     break;
1116                 case RegistersV7.RegionBaseAddress:
1117                 case RegistersV7.RegionBaseAddressAlias1:
1118                 case RegistersV7.RegionBaseAddressAlias2:
1119                 case RegistersV7.RegionBaseAddressAlias3:
1120                     value = cpu.MPURegionBaseAddress;
1121                     break;
1122                 case RegistersV7.RegionAttributeAndSize:
1123                 case RegistersV7.RegionAttributeAndSizeAlias1:
1124                 case RegistersV7.RegionAttributeAndSizeAlias2:
1125                 case RegistersV7.RegionAttributeAndSizeAlias3:
1126                     value = cpu.MPURegionAttributeAndSize;
1127                     break;
1128                 default:
1129                     value = 0x0;
1130                     break;
1131             }
1132             this.Log(LogLevel.Debug, "MPU: Trying to read {0} (value: 0x{1:X08})", Enum.GetName(typeof(RegistersV7), offset), value);
1133             return value;
1134         }
1135 
HandleMPUReadV8(long offset)1136         private uint HandleMPUReadV8(long offset)
1137         {
1138             if (cpu.NumberOfMPURegions == 0)
1139             {
1140                 this.Log(LogLevel.Debug, $"Attempted a read from an MPU register, but the CPU doesn't support MPU. Set 'numberOfMPURegions' in CPU configuration to enable it.");
1141                 return 0;
1142             }
1143             uint value;
1144             switch((RegistersV8)offset)
1145             {
1146                 case RegistersV8.Type:
1147                     value = (cpu.NumberOfMPURegions & 0xFF) << 8;
1148                     break;
1149                 case RegistersV8.Control:
1150                     value = cpu.PmsaV8Ctrl;
1151                     break;
1152                 case RegistersV8.RegionNumberRegister:
1153                     value = cpu.PmsaV8Rnr;
1154                     break;
1155                 case RegistersV8.RegionBaseAddressRegister:
1156                 case RegistersV8.RegionBaseAddressRegisterAlias1:
1157                 case RegistersV8.RegionBaseAddressRegisterAlias2:
1158                 case RegistersV8.RegionBaseAddressRegisterAlias3:
1159                     value = cpu.PmsaV8Rbar;
1160                     break;
1161                 case RegistersV8.RegionLimitAddressRegister:
1162                 case RegistersV8.RegionLimitAddressRegisterAlias1:
1163                 case RegistersV8.RegionLimitAddressRegisterAlias2:
1164                 case RegistersV8.RegionLimitAddressRegisterAlias3:
1165                     value = cpu.PmsaV8Rlar;
1166                     break;
1167                 case RegistersV8.MemoryAttributeIndirectionRegister0:
1168                     value = cpu.PmsaV8Mair0;
1169                     break;
1170                 case RegistersV8.MemoryAttributeIndirectionRegister1:
1171                     value = cpu.PmsaV8Mair1;
1172                     break;
1173                 default:
1174                     value = 0x0;
1175                     break;
1176             }
1177             this.Log(LogLevel.Debug, "MPU: Trying to read {0} (value: 0x{1:X08})", Enum.GetName(typeof(RegistersV7), offset), value);
1178             return value;
1179         }
1180 
HandleSAURead(long offset)1181         private uint HandleSAURead(long offset)
1182         {
1183             switch((RegistersSAU)offset)
1184             {
1185                 case RegistersSAU.Control:
1186                     return cpu.SAUControl;
1187                 case RegistersSAU.Type:
1188                     return cpu.NumberOfSAURegions;
1189                 case RegistersSAU.RegionNumber:
1190                     return cpu.SAURegionNumber;
1191                 case RegistersSAU.RegionBaseAddress:
1192                     return cpu.SAURegionBaseAddress;
1193                 case RegistersSAU.RegionLimitAddress:
1194                     return cpu.SAURegionLimitAddress;
1195                 default:
1196                     this.WarningLog("SAU: Read from unhandled register 0x{0:x}", offset);
1197                     return 0;
1198             }
1199         }
1200 
HandleSAUWrite(long offset, uint value)1201         private void HandleSAUWrite(long offset, uint value)
1202         {
1203             switch((RegistersSAU)offset)
1204             {
1205                 case RegistersSAU.Control:
1206                     cpu.SAUControl = value;
1207                     break;
1208                 case RegistersSAU.Type:
1209                     this.WarningLog("SAU: Write to read-only register 0x{0:x} ({1}), value 0x{2:x}", offset, nameof(RegistersSAU.Type), value);
1210                     break;
1211                 case RegistersSAU.RegionNumber:
1212                     cpu.SAURegionNumber = value;
1213                     break;
1214                 case RegistersSAU.RegionBaseAddress:
1215                     cpu.SAURegionBaseAddress = value;
1216                     break;
1217                 case RegistersSAU.RegionLimitAddress:
1218                     cpu.SAURegionLimitAddress = value;
1219                     break;
1220                 default:
1221                     this.WarningLog("SAU: Write to unhandled register 0x{0:x}, value 0x{1:x}", offset, value);
1222                     break;
1223             }
1224         }
1225 
HandleWfiStateChange(bool enteredWfi)1226         private void HandleWfiStateChange(bool enteredWfi)
1227         {
1228             if(enteredWfi && DeepSleepEnabled && HaltSystickOnDeepSleep)
1229             {
1230                 systick.NonSecureVal.Enabled = false;
1231                 if(cpu.TrustZoneEnabled)
1232                 {
1233                     systick.SecureVal.Enabled = false;
1234                 }
1235                 this.NoisyLog("Entering deep sleep");
1236             }
1237         }
1238 
EnableOrDisableInterrupt(int offset, uint value, bool enable, bool isSecure)1239         private void EnableOrDisableInterrupt(int offset, uint value, bool enable, bool isSecure)
1240         {
1241             lock(irqs)
1242             {
1243                 var firstIRQNo = 8 * offset + 16;  // 16 is added because this is HW interrupt
1244                 {
1245                     var lastIRQNo = firstIRQNo + 31;
1246                     var mask = 1u;
1247                     for(var i = firstIRQNo; i <= lastIRQNo; i++)
1248                     {
1249                         if((value & mask) > 0)
1250                         {
1251                             if(!isSecure && !IsInterruptTargetNonSecure(i))
1252                             {
1253                                 this.WarningLog("Cannot {0} IRQ {1}, since it targets Secure state, and the access is in Non-secure",
1254                                     enable ? "enable" : "disable", ExceptionToString(i));
1255                                 continue;
1256                             }
1257 
1258                             if(enable)
1259                             {
1260                                 this.NoisyLog("Enabled IRQ {0}.", ExceptionToString(i));
1261                                 irqs[i] |= IRQState.Enabled;
1262                             }
1263                             else
1264                             {
1265                                 this.NoisyLog("Disabled IRQ {0}.", ExceptionToString(i));
1266                                 irqs[i] &= ~IRQState.Enabled;
1267                             }
1268                         }
1269                         mask <<= 1;
1270                     }
1271                     FindPendingInterrupt();
1272                 }
1273             }
1274         }
1275 
HandleEnableRead(int offset, bool isSecure)1276         private uint HandleEnableRead(int offset, bool isSecure)
1277         {
1278             lock(irqs)
1279             {
1280                 var firstIRQNo = 8 * offset + 16;
1281                 var lastIRQNo = firstIRQNo + 31;
1282                 var result = 0u;
1283                 if(firstIRQNo < 0 || lastIRQNo > irqs.Length)
1284                 {
1285                     this.Log(LogLevel.Error, "Trying to access IRQs from range {0}-{1}, but only {2} are defined (offset 0x{3:X})", firstIRQNo, lastIRQNo, irqs.Length, offset);
1286                     return result;
1287                 }
1288                 for(var i = lastIRQNo; i > firstIRQNo; i--)
1289                 {
1290                     // Cannot read the enable state of a Secure interrupt in a Non-secure state, read zero instead
1291                     if(isSecure || IsInterruptTargetNonSecure(i))
1292                     {
1293                         result |= ((irqs[(int)i] & IRQState.Enabled) != 0) ? 1u : 0u;
1294                     }
1295                     result <<= 1;
1296                 }
1297                 if(isSecure || IsInterruptTargetNonSecure(firstIRQNo))
1298                 {
1299                     result |= ((irqs[(int)firstIRQNo] & IRQState.Enabled) != 0) ? 1u : 0u;
1300                 }
1301                 return result;
1302             }
1303         }
1304 
SetPending(int i)1305         private void SetPending(int i)
1306         {
1307             this.DebugLog("Set pending IRQ {0}.", ExceptionToString(i));
1308             var before = irqs[i];
1309             irqs[i] |= IRQState.Pending;
1310             pendingIRQs.Add(i);
1311 
1312             // when SEVONPEND is set all interrupts (even those masked)
1313             // generate an event when entering the pending state
1314             if(before != irqs[i] && currentSevOnPending.Get(IsInterruptTargetNonSecure(i)))
1315             {
1316                 foreach(var cpu in machine.SystemBus.GetCPUs().OfType<CortexM>())
1317                 {
1318                     cpu.SetEventFlag(true);
1319                 }
1320             }
1321         }
1322 
ClearPending(int i)1323         private void ClearPending(int i)
1324         {
1325             lock(irqs)
1326             {
1327                 if((irqs[i] & IRQState.Running) == 0)
1328                 {
1329                     this.DebugLog("Cleared pending IRQ {0}.", ExceptionToString(i));
1330                     irqs[i] &= ~IRQState.Pending;
1331                     pendingIRQs.Remove(i);
1332                 }
1333                 else
1334                 {
1335                     this.DebugLog("Not clearing pending IRQ {0} as it is currently running.", ExceptionToString(i));
1336                 }
1337             }
1338         }
1339 
SetOrClearPendingInterrupt(int offset, uint value, bool set, bool isSecure)1340         private void SetOrClearPendingInterrupt(int offset, uint value, bool set, bool isSecure)
1341         {
1342             lock(irqs)
1343             {
1344                 var firstIRQNo = 8 * offset + 16;  // 16 is added because this is HW interrupt
1345                 {
1346                     var lastIRQNo = firstIRQNo + 31;
1347                     var mask = 1u;
1348                     for(var i = firstIRQNo; i <= lastIRQNo; i++)
1349                     {
1350                         if((value & mask) > 0)
1351                         {
1352                             if(!isSecure && !IsInterruptTargetNonSecure(i))
1353                             {
1354                                 this.WarningLog("Cannot {0} pending IRQ {1}, since it targets Secure state, and the access is in Non-secure",
1355                                     set ? "set" : "clear", ExceptionToString(i));
1356                                 continue;
1357                             }
1358                             if (set)
1359                             {
1360                                 SetPending(i);
1361                             }
1362                             else
1363                             {
1364                                 ClearPending(i);
1365                             }
1366                         }
1367                         mask <<= 1;
1368                     }
1369                     FindPendingInterrupt();
1370                 }
1371             }
1372         }
1373 
ModifySecurityTarget(int offset, uint value)1374         private void ModifySecurityTarget(int offset, uint value)
1375         {
1376             lock(irqs)
1377             {
1378                 var mask = 1u;
1379                 // Only HW interrupts are modified by this
1380                 for(var i = 0; i < 32; i++)
1381                 {
1382                     var pos = 16 + offset * 8 + i;
1383                     this.NoisyLog("IRQ {0} configured as {1}", ExceptionToString(pos), (value & mask) > 0 ? "Non-secure" : "Secure");
1384                     targetInterruptSecurityState[pos] = (value & mask) > 0 ? InterruptTargetSecurityState.NonSecure : InterruptTargetSecurityState.Secure;
1385                     mask <<= 1;
1386                 }
1387             }
1388         }
1389 
FindPendingInterrupt()1390         public int FindPendingInterrupt()
1391         {
1392             lock(irqs)
1393             {
1394                 var bestPriority = 0xFF + 1;
1395                 var preemptNeeded = activeIRQs.Count != 0;
1396                 var result = SpuriousInterrupt; // TODO (and some log?)
1397 
1398                 foreach(int i in pendingIRQs)
1399                 {
1400                     var currentIRQ = irqs[i];
1401                     if(IsCandidate(currentIRQ, i) && AdjustPriority(i) < bestPriority)
1402                     {
1403                         result = i;
1404                         bestPriority = AdjustPriority(i);
1405                     }
1406                 }
1407                 if(preemptNeeded)
1408                 {
1409                     var activeTop = activeIRQs.Peek();
1410                     var activePriority = AdjustPriority(activeTop);
1411                     if(!DoesAPreemptB(bestPriority, activePriority, !IsInterruptTargetNonSecure(result), !IsInterruptTargetNonSecure(activeTop)))
1412                     {
1413                         result = SpuriousInterrupt;
1414                     }
1415                     else
1416                     {
1417                         this.NoisyLog("IRQ {0} preempts {1}.", ExceptionToString(result), ExceptionToString(activeTop));
1418                     }
1419                 }
1420 
1421                 if(result != SpuriousInterrupt)
1422                 {
1423                     if(ShouldRaiseException(result))
1424                     {
1425                         IRQ.Set(true);
1426                     }
1427                     // This field has side-effects, and can cause Cortex-M CPU running in another thread to exit WFI immediately.
1428                     // Make absolutely sure to execute last, after signaling IRQ handler to run with `IRQ.Set`.
1429                     // Only this way the CPU will enter an exception handler immediately upon waking from WFI.
1430                     // This doesn't matter for async (HW) interrupts, arriving when the core is executing normally.
1431                     maskedInterruptPresent = true;
1432                 }
1433                 else
1434                 {
1435                     maskedInterruptPresent = false;
1436                 }
1437 
1438                 return result;
1439             }
1440         }
1441 
1442         /// <remarks>
1443         /// This exposes raw value of <see cref="targetInterruptSecurityState"/>
1444         /// note, that for some exceptions, this doesn't mean that they are Secure, since some exceptions are banked
1445         /// </remarks>
1446         [HideInMonitor]
GetTargetInterruptSecurityState(int interruptNumber)1447         public InterruptTargetSecurityState GetTargetInterruptSecurityState(int interruptNumber)
1448         {
1449             // Don't check this for banked IRQs - this makes no sense at all!
1450             // since they are taken to the state in which they occurred - they can be triggered independently
1451             DebugHelper.Assert(!bankedInterrupts.Contains(interruptNumber));
1452 
1453             return targetInterruptSecurityState[interruptNumber];
1454         }
1455 
1456 
ShouldRaiseException(int excp)1457         private bool ShouldRaiseException(int excp)
1458         {
1459             /* The intuition to understanding this:
1460              * PRIMASK is used to mask all exceptions, minus Reset, NMI and HardFault
1461              * FAULTMASK is a more restrictive PRIMASK, also masking HardFault
1462              * _NS variants will attempt to only disable NonSecure exceptions, but this can only happen if "PRIS" is set
1463              * here `BFHFNMINS` will also matter, since it retargets several exceptions (e.g. NMI), and enables HardFault banking
1464              */
1465             if(!cpu.TrustZoneEnabled)
1466             {
1467                 // These have prio below -1, so they are never maskable
1468                 if(excp == (int)SystemException.NMI || excp == (int)SystemException.Reset)
1469                 {
1470                     return true;
1471                 }
1472 
1473                 if(cpu.PRIMASK == 0 && cpu.FAULTMASK == 0)
1474                 {
1475                     return true;
1476                 }
1477                 else if(cpu.PRIMASK != 0 && cpu.FAULTMASK == 0)
1478                 {
1479                     // If only PRIMASK is set, HardFault always goes through
1480                     return excp == (int)SystemException.HardFault;
1481                 }
1482                 // Otherwise, if FAULTMASK is set, deny everything
1483             }
1484             else
1485             {
1486                 // Reset is not maskable
1487                 if(excp == (int)SystemException.Reset)
1488                 {
1489                     return true;
1490                 }
1491 
1492                 if(cpu.GetFaultmask(true) > 0 || cpu.GetFaultmask(false) > 0)
1493                 {
1494                     // "BFHFNMINS" is 1
1495                     bool isNSEnabled = GetTargetInterruptSecurityState((int)SystemException.HardFault) == InterruptTargetSecurityState.NonSecure;
1496                     if(cpu.GetFaultmask(true) > 0)
1497                     {
1498                         if(isNSEnabled)
1499                         {
1500                             return false;
1501                         }
1502                         else
1503                         {
1504                             return excp == (int)SystemException.HardFault_S || excp == (int)SystemException.NMI;
1505                         }
1506                     }
1507                     else if(cpu.GetFaultmask(false) > 0)
1508                     {
1509                         if(isNSEnabled)
1510                         {
1511                             // If Configurable exceptions target Non-secure mode, and PRIS is set, we boost current execution priority to 0x80
1512                             // which is the boundary between Secure and Non-secure priorities, so only Secure exceptions will pass.
1513                             // If PRIS is unset, we block everything (raise priotity to 0), but not HardFault (Secure and Non-Secure) and NMI.
1514                             return (prioritizeSecureInterrupts && !IsInterruptTargetNonSecure(excp))
1515                                 || excp == (int)SystemException.NMI || excp == (int)SystemException.HardFault || excp == (int)SystemException.HardFault_S;
1516                         }
1517                         else
1518                         {
1519                             // Configurable exceptions target Secure mode only, so we raise execution priority to -1
1520                             return excp == (int)SystemException.NMI;
1521                         }
1522                     }
1523                 }
1524                 // Secure PRIMASK is unset, so all pass, unless Non-secure is set
1525                 else if(cpu.GetPrimask(true) == 0)
1526                 {
1527                     if(cpu.GetPrimask(false) == 0)
1528                     {
1529                         return true;
1530                     }
1531                     else
1532                     {
1533                         // If PRIMASK_NS is set, and PRIS is set, we boost current execution priority to 0x80
1534                         // which is the boundary between Secure and Non-secure priorities, so only Secure exceptions will pass.
1535                         // If PRIS is unset, we block everything (raise priotity to 0), but not HardFault and NMI.
1536                         return (prioritizeSecureInterrupts && !IsInterruptTargetNonSecure(excp))
1537                             || excp == (int)SystemException.NMI || excp == (int)SystemException.HardFault;
1538                     }
1539                 }
1540                 // Secure PRIMASK is set - deny all, except HardFault and NMI (exec priority boosted to 0)
1541                 else if(cpu.GetPrimask(true) > 0)
1542                 {
1543                     return excp == (int)SystemException.HardFault || excp == (int)SystemException.HardFault_S || excp == (int)SystemException.NMI;
1544                 }
1545             }
1546 
1547             // Ignore exception otherwise
1548             return false;
1549         }
1550 
AdjustPriority(int interruptNo)1551         private int AdjustPriority(int interruptNo)
1552         {
1553             byte priority = priorities[interruptNo];
1554             if(!prioritizeSecureInterrupts)
1555             {
1556                 return priority;
1557             }
1558             /* Rule: RWQWK (ARMv8-M Architecture Reference Manual)
1559              * When AIRCR.PRIS is 1, each Non-secure SHPRn_NS.PRI_n priority field value [7:0] has the following sequence
1560              * applied to it, it:
1561              *  1. Is divided by two.
1562              *  2. The constant 0x80 is then added to it.
1563              * though it appears that it applies to all Non-secure interrupts, not only exceptions configurable with SHPRn_NS.PRI_n
1564              */
1565 
1566             // It is a "byte" after all
1567             DebugHelper.Assert(priority <= 255);
1568 
1569             if(IsInterruptTargetNonSecure(interruptNo))
1570             {
1571                 // Divide by two, and set 7th bit. Since 255 is the lowest priority (highest number), this is fine
1572                 priority >>= 1;
1573                 priority |= 0x80;
1574             }
1575             return priority;
1576         }
1577 
IsInterruptTargetNonSecure(int interruptNo)1578         private bool IsInterruptTargetNonSecure(int interruptNo)
1579         {
1580             if(!cpu.TrustZoneEnabled)
1581             {
1582                 return true;
1583             }
1584             if(bankedInterrupts.Contains(interruptNo))
1585             {
1586                 return (interruptNo & BankedExcpSecureBit) == 0;
1587             }
1588             // HardFault is banked if "BFHFNMINS" is set
1589             if((interruptNo == (int)SystemException.HardFault_S || interruptNo == (int)SystemException.HardFault)
1590                 && targetInterruptSecurityState[(int)SystemException.HardFault] == InterruptTargetSecurityState.NonSecure)
1591             {
1592                 return (interruptNo & BankedExcpSecureBit) == 0;
1593             }
1594             return targetInterruptSecurityState[interruptNo] == InterruptTargetSecurityState.NonSecure;
1595         }
1596 
IsCandidate(IRQState state, int index)1597         private bool IsCandidate(IRQState state, int index)
1598         {
1599             const IRQState mask = IRQState.Pending | IRQState.Enabled | IRQState.Active;
1600             const IRQState candidate = IRQState.Pending | IRQState.Enabled;
1601 
1602             return ((state & mask) == candidate) &&
1603                    (basepri.Get(!IsInterruptTargetNonSecure(index)) == 0 || priorities[index] < basepri.Get(!IsInterruptTargetNonSecure(index)));
1604         }
1605 
DoesAPreemptB(int priorityA, int priorityB, bool secureA, bool secureB)1606         private bool DoesAPreemptB(int priorityA, int priorityB, bool secureA, bool secureB)
1607         {
1608             var binaryPointMaskA = ~((1 << binaryPointPosition.Get(secureA) + 1) - 1);
1609             var binaryPointMaskB = ~((1 << binaryPointPosition.Get(secureB) + 1) - 1);
1610             return (priorityA & binaryPointMaskA) < (priorityB & binaryPointMaskB);
1611         }
1612 
GetPending(int offset, bool isSecure)1613         private uint GetPending(int offset, bool isSecure)
1614         {
1615             int startIndex = 16 + offset * 8;
1616             return BitHelper.GetValueFromBitsArray(irqs.Skip(startIndex).Take(32).Select((irq, idx) => (isSecure || IsInterruptTargetNonSecure(startIndex + idx)) && ((irq & IRQState.Pending) != 0)));
1617         }
1618 
GetActive(int offset, bool isSecure)1619         private uint GetActive(int offset, bool isSecure)
1620         {
1621             int startIndex = 16 + offset * 8;
1622             return BitHelper.GetValueFromBitsArray(irqs.Skip(startIndex).Take(32).Select((irq, idx) => (isSecure || IsInterruptTargetNonSecure(startIndex + idx)) && ((irq & IRQState.Active) != 0)));
1623         }
1624 
GetSecurityTarget(int offset)1625         private uint GetSecurityTarget(int offset)
1626         {
1627             // We skip first 16 entries, as these are not Hard IRQs. Some of them can be configured by `AIRCR.BFHFNMINS`, but we use common list to store their configuration
1628             return BitHelper.GetValueFromBitsArray(targetInterruptSecurityState.Skip(16 + offset * 8).Take(32).Select(irq => irq == InterruptTargetSecurityState.NonSecure));
1629         }
1630 
IsPrivilegedMode()1631         private bool IsPrivilegedMode()
1632         {
1633             // Is in handler mode or is privileged
1634             return (cpu.XProgramStatusRegister & InterruptProgramStatusRegisterMask) != 0 || (cpu.Control & 1) == 0;
1635         }
1636 
1637         /* Expect this to return `true` only if the CPU is in Secure state
1638          * otherwise this will return `false` - also for CPUs with disabled TrustZone
1639          * or for Monitor access. If the CPU was not the originator (e.g. Monitor), then `currentCpu` will be `null`
1640          */
IsCurrentCPUInSecureState(out ICPU currentCpu)1641         private bool IsCurrentCPUInSecureState(out ICPU currentCpu)
1642         {
1643             currentCpu = null;
1644             try
1645             {
1646                 currentCpu = machine.GetSystemBus(this).GetCurrentCPU();
1647                 // Checking if TZ is enabled is a short-cut to avoid lengthy lookups into CPU state
1648                 // and stack unwinding if an exception is thrown.
1649                 // It results in a significant speed-up
1650                 if(currentCpu is CortexM mcpu && mcpu.TrustZoneEnabled)
1651                 {
1652                     return mcpu.SecureState;
1653                 }
1654                 return false;
1655             }
1656             catch(RecoverableException)
1657             {
1658                 // TrustZone might not be enabled
1659                 return false;
1660             }
1661         }
1662 
ExceptionToString(int exception)1663         private static string ExceptionToString(int exception)
1664         {
1665             if(Enum.IsDefined(typeof(SystemException), exception))
1666             {
1667                 return ((SystemException)exception).ToString();
1668             }
1669             // Otherwise, it's an external Hard IRQ.
1670             return $"HardwareIRQ#{exception - ((int)SystemException.SysTick) - 1} ({exception})";
1671         }
1672 
1673         // This is just a cache of BASEPRI register, present in ARMv7M and newer CPUs
1674         // modifying them here will cause a bad desync with the CPU state
1675         private readonly SecurityBanked<byte> basepri;
1676 
1677         [HideInMonitor]
1678         public byte BASEPRI_NS
1679         {
1680             get { return basepri.NonSecureVal; }
1681             set
1682             {
1683                 if(value == basepri.NonSecureVal)
1684                 {
1685                     return;
1686                 }
1687                 basepri.NonSecureVal = value;
1688                 FindPendingInterrupt();
1689             }
1690         }
1691 
1692         [HideInMonitor]
1693         public byte BASEPRI_S
1694         {
1695             get { return basepri.SecureVal; }
1696             set
1697             {
1698                 if(value == basepri.SecureVal)
1699                 {
1700                     return;
1701                 }
1702                 basepri.SecureVal = value;
1703                 FindPendingInterrupt();
1704             }
1705         }
1706 
1707         [Flags]
1708         private enum IRQState : byte
1709         {
1710             Running = 1,
1711             Pending = 2,
1712             Active = 4,
1713             Enabled = 32
1714         }
1715 
1716         private enum Registers
1717         {
1718             InterruptControllerType = 0x4,
1719             SysTickControl = 0x10,
1720             SysTickReloadValue = 0x14,
1721             SysTickValue = 0x18,
1722             SysTickCalibrationValue = 0x1C,
1723             SetEnable = 0x100,
1724             ClearEnable = 0x180,
1725             SetPending = 0x200,
1726             ClearPending = 0x280,
1727             ActiveBit = 0x300,
1728             InterruptTargetNonSecure = 0x380, // ITNS
1729             InterruptPriority = 0x400,
1730             CPUID = 0xD00,
1731             InterruptControlState = 0xD04,
1732             VectorTableOffset = 0xD08,
1733             ApplicationInterruptAndReset = 0xD0C, // AIRCR
1734             SystemControlRegister = 0xD10, // SCR
1735             ConfigurationAndControl = 0xD14, // CCR
1736             SystemHandlerPriority1 = 0xD18, // SHPR1
1737             SystemHandlerPriority2 = 0xD1C, // SHPR2
1738             SystemHandlerPriority3 = 0xD20, // SHPR3
1739             SystemHandlerControlAndState = 0xD24, // SHCSR
1740             ConfigurableFaultStatus = 0xD28, // CFSR
1741             HardFaultStatus = 0xD2C, // HFSR
1742             DebugFaultStatus = 0xD30, // DFSR
1743             // FPU registers 0xD88 .. F3C
1744             MemoryFaultAddress = 0xD34, // MMFAR
1745             BusFaultAddress = 0xD38, // BFAR
1746             AuxiliaryFaultStatus = 0xD3C, // AFSR
1747             ProcessorFeature0 = 0xD40, // ID_PFR0
1748             ProcessorFeature1 = 0xD44, // ID_PFR1
1749             DebugFeature0 = 0xD48, // ID_DFR0
1750             AuxiliaryFeature0 = 0xD4C, // ID_AFR0
1751             MemoryModelFeature0 = 0xD50, // ID_MMFR0
1752             MemoryModelFeature1 = 0xD54, // ID_MMFR1
1753             MemoryModelFeature2 = 0xD58, // ID_MMFR2
1754             MemoryModelFeature3 = 0xD5C, // ID_MMFR3
1755             InstructionSetAttribute0 = 0xD60, // ID_ISAR0
1756             InstructionSetAttribute1 = 0xD64, // ID_ISAR1
1757             InstructionSetAttribute2 = 0xD68, // ID_ISAR2
1758             InstructionSetAttribute3 = 0xD6C, // ID_ISAR3
1759             InstructionSetAttribute4 = 0xD70, // ID_ISAR4
1760             ID_ISAR5 = 0xD74, // ID_ISAR5
1761             CacheLevelID = 0xD78, // CLIDR
1762             CacheType = 0xD7C, // CTR
1763             CacheSizeID = 0xD80, // CCSIDR
1764             CacheSizeSelection = 0xD84, // CSSELR
1765             CoprocessorAccessControl = 0xD88, // CPACR
1766             MPUType = 0xD90, // MPU_TYPE
1767             MPUControl = 0xD94, // MPU_CTRL
1768             MPURegionNumber = 0xD98, // MPU_RNR
1769             MPURegionBaseAddress = 0xD9C, // MPU_RBAR
1770             MPURegionAttributeAndSize = 0xDA0, // MPU_RASR
1771             Alias1OfMPURegionBaseAddress = 0xDA4, // MPU_RBAR_A1
1772             Alias1OfMPURegionAttributeAndSize = 0xDA8, // MPU_RASR_A1
1773             Alias2OfMPURegionBaseAddress = 0xDAC, // MPU_RBAR_A2
1774             Alias2OfMPURegionAttributeAndSize = 0xDB0, // MPU_RASR_A2
1775             Alias3OfMPURegionBaseAddress = 0xDB4, // MPU_RBAR_A3
1776             Alias3OfMPURegionAttributeAndSize = 0xDB8, // MPU_RASR_A3
1777             SAUControl = 0xDD0, // SAU_CTRL
1778             SAUType = 0xDD4, // SAU_TYPE
1779             SAURegionNumber = 0xDD8, // SAU_RNR
1780             SAURegionBaseAddress = 0xDDC, // SAU_RBAR
1781             SAURegionLimitAddress = 0xDE0, // SAU_RLAR
1782             SecureFaultStatus = 0xDE4, // SAU_SFSR
1783             SecureFaultAddress = 0xDE8, // SAU_SFAR
1784             DebugExceptionAndMonitorControlRegister = 0xDFC, // DEMCR
1785             SoftwareTriggerInterrupt = 0xF00, // STIR
1786             FPContextControl = 0xF34, // FPCCR
1787             FPContextAddress = 0xF38, // FPCAR
1788             FPDefaultStatusControl = 0xF3C, // FPDSCR
1789             FloatingPointDefaultStatusControl = 0xF3C, // FPDSCR
1790             MediaAndFPFeature0 = 0xF40, // MVFR0
1791             MediaAndFPFeature1 = 0xF44, // MVFR1
1792             MediaAndFPFeature2 = 0xF48, // MVFR2
1793             ICacheInvalidateAllToPoUaIgnored = 0xF50, // ICIALLU
1794             ICacheInvalidateByMVAToPoUaAddress = 0xF58, // ICIMVAU
1795             DCacheInvalidateByMVAToPoCAddress = 0xF5C, // DCIMVAC
1796             DCacheInvalidateBySetWay= 0xF60, // DCISW
1797             DCacheCleanByMVAToPoUAddress = 0xF64, // DCCMVAU
1798             DCacheCleanByMVAToPoCAddress = 0xF68, // DCCMVAC
1799             DCacheCleanBySetWay= 0xF6C, // DCCSW
1800             DCacheCleanAndInvalidateByMVAToPoCAddress = 0xF70, // DCCIMVAC
1801             DCacheCleanAndInvalidateBySetWay= 0xF74, // DCCISW
1802             BranchPredictorInvalidateAllIgnored = 0xF78, // BPIALL
1803 
1804             // Registers with addresses from 0xF90 to 0xFCF are implementation defined.
1805             // The following ones are valid for Cortex-M7.
1806             InstructionTightlyCoupledMemoryControl = 0xF90, // ITCMCR
1807             DataTightlyCoupledMemoryControl = 0xF94, // DTCMCR
1808             AHBPControl = 0xF98, // AHBPCR
1809             L1CacheControl = 0xF9C, // CACR
1810             AHBSlaveControl = 0xFA0, // AHBSCR
1811             AuxiliaryBusFaultStatus = 0xFA8, // ABFSR
1812         }
1813 
1814         private enum RegistersV7
1815         {
1816             Type = 0x00,
1817             Control = 0x04,
1818             RegionNumber = 0x08,
1819             RegionBaseAddress = 0x0C,
1820             RegionAttributeAndSize = 0x10,
1821             RegionBaseAddressAlias1 = 0x14,
1822             RegionAttributeAndSizeAlias1 = 0x18,
1823             RegionBaseAddressAlias2 = 0x1C,
1824             RegionAttributeAndSizeAlias2 = 0x20,
1825             RegionBaseAddressAlias3 = 0x24,
1826             RegionAttributeAndSizeAlias3 = 0x28,
1827         }
1828 
1829         private enum RegistersV8
1830         {
1831             Type = 0x00,
1832             Control = 0x04,
1833             RegionNumberRegister = 0x08,
1834             RegionBaseAddressRegister = 0x0C,
1835             RegionLimitAddressRegister = 0x10,
1836             RegionBaseAddressRegisterAlias1 = 0x14,
1837             RegionLimitAddressRegisterAlias1 = 0x18,
1838             RegionBaseAddressRegisterAlias2 = 0x1C,
1839             RegionLimitAddressRegisterAlias2 = 0x20,
1840             RegionBaseAddressRegisterAlias3 = 0x24,
1841             RegionLimitAddressRegisterAlias3 = 0x28,
1842             MemoryAttributeIndirectionRegister0 = 0x30,
1843             MemoryAttributeIndirectionRegister1 = 0x34
1844         }
1845 
1846         private enum RegistersSAU
1847         {
1848             Control = 0x0, // SAU_CTRL
1849             Type = 0x4, // SAU_TYPE
1850             RegionNumber = 0x8, // SAU_RNR
1851             RegionBaseAddress = 0xc, // SAU_RBAR
1852             RegionLimitAddress = 0x10, // SAU_RLAR
1853         }
1854 
1855         private enum MPUVersion
1856         {
1857             PMSAv7,
1858             PMSAv8
1859         }
1860 
1861         private enum SystemException
1862         {
1863             Reset = 1,
1864             NMI = 2,
1865             HardFault = 3,
1866             MemManageFault = 4,
1867             BusFault = 5,
1868             UsageFault = 6,
1869             SecureFault = 7,
1870             SuperVisorCall = 11,
1871             DebugMonitor = 12,
1872             PendSV = 14,
1873             SysTick = 15,
1874 
1875             // These are not real exceptions!
1876             // We cheat here a bit, to create banked exceptions for TrustZone
1877             // since they can live indepenently from their not-banked variants (so effectively they behave like separate exceptions)
1878             HardFault_S = HardFault | BankedExcpSecureBit,
1879             MemManageFault_S = MemManageFault | BankedExcpSecureBit,
1880             UsageFault_S = UsageFault | BankedExcpSecureBit,
1881             SuperVisorCall_S = SuperVisorCall | BankedExcpSecureBit,
1882             DebugMonitor_S = DebugMonitor | BankedExcpSecureBit,
1883             PendSV_S = PendSV | BankedExcpSecureBit,
1884             SysTick_S = SysTick | BankedExcpSecureBit,
1885         }
1886 
1887         public enum InterruptTargetSecurityState
1888         {
1889             Secure = 0,
1890             NonSecure = 1,
1891         }
1892 
1893         private class SecurityBanked<T>
1894         {
1895             public T SecureVal;
1896             public T NonSecureVal;
1897 
Reset()1898             public void Reset()
1899             {
1900                 SecureVal = default(T);
1901                 NonSecureVal = default(T);
1902             }
1903 
Reset(T val)1904             public void Reset(T val)
1905             {
1906                 SecureVal = val;
1907                 NonSecureVal = val;
1908             }
1909 
Get(bool IsSecure)1910             public ref T Get(bool IsSecure)
1911             {
1912                 return ref IsSecure ? ref SecureVal : ref NonSecureVal;
1913             }
1914         }
1915 
1916         private class SysTick
1917         {
SysTick(IMachine machine, NVIC parent, long systickFrequency, bool isSecure = false)1918             public SysTick(IMachine machine, NVIC parent, long systickFrequency, bool isSecure = false)
1919             {
1920                 IsSecure = isSecure;
1921                 this.parent = parent;
1922                 // We set initial limit to the maximum 24-bit value, and don't modify it afterwards.
1923                 // Instead we reload counter with RELOAD value when counter reaches 0.
1924                 systick = new LimitTimer(machine.ClockSource, systickFrequency, parent, nameof(systick) + (isSecure ? "_S" : "_NS"), SysTickMaxValue, Direction.Descending, false, eventEnabled: true);
1925                 systick.LimitReached += () =>
1926                 {
1927                     CountFlag = true;
1928                     if(TickInterruptEnabled)
1929                     {
1930                         parent.SetPendingIRQ((int)(IsSecure ? SystemException.SysTick_S : SystemException.SysTick));
1931                     }
1932 
1933                     // If the systick timer is running and the reload value is 0, this has the effect of disabling the counter on the expiration.
1934                     if(Reload == 0)
1935                     {
1936                         systick.Enabled = false;
1937                     }
1938                     else
1939                     {
1940                         systick.Value = Reload;
1941                     }
1942                 };
1943             }
1944 
Reset()1945             public void Reset()
1946             {
1947                 systickEnabled = false;
1948                 reloadValue = 0;
1949 
1950                 systick.Reset();
1951                 systick.AutoUpdate = true;
1952             }
1953 
UpdateSystickValue()1954             public void UpdateSystickValue()
1955             {
1956                 if(reloadValue != 0)
1957                 {
1958                     // Write to this register does not trigger the SysTick exception logic - we can't write zero to timer value as it would trigger an event.
1959                     systick.Value = reloadValue;
1960                 }
1961                 CountFlag = false;
1962             }
1963 
1964             public bool IsSecure { get; }
1965 
1966             public bool TickInterruptEnabled { get; set; } // TICKINT
1967             public bool CountFlag { get; set; } // COUNTFLAG
1968 
1969             // Systick can be enabled but doesn't have to count, e.g. if RELOAD value is set to 0,
1970             // or we are in DEEPSLEEP
1971             public bool Enabled
1972             {
1973                 get => systickEnabled;
1974                 set
1975                 {
1976                     systickEnabled = value;
1977                     if(value && Reload == 0)
1978                     {
1979                         parent.DebugLog("Systick_{0} enabled but it won't be started as long as the reload value is zero", IsSecure ? "S" : "NS");
1980                         return;
1981                     }
1982                     parent.NoisyLog("Systick_{0} {1}", IsSecure ? "S" : "NS", value ? "enabled" : "disabled");
1983                     systick.Enabled = value;
1984                 }
1985             }
1986 
1987             public ulong Value // CURRENT
1988             {
1989                 get => systick.Value;
1990             }
1991 
1992             public ulong Reload // RELOAD
1993             {
1994                 get => reloadValue;
1995                 set
1996                 {
1997                     if(Enabled && reloadValue == 0 && !systick.Enabled)
1998                     {
1999                         // We explicitly enable underlying SysTick counter only in the case it was blocked by RELOAD=0.
2000                         // We ignore other cases, as we don't want to accidentally enable counter in DEEPSLEEP just by writing to this register.
2001                         parent.DebugLog("Resuming Systick_{0} counter due to reload value change from 0x0 to 0x{1:X}", IsSecure ? "_S" : "_NS", value);
2002                         systick.Value = value;
2003                         systick.Enabled = true;
2004                     }
2005                     reloadValue = value;
2006                 }
2007             }
2008 
2009             public long Frequency
2010             {
2011                 get => systick.Frequency;
2012                 set
2013                 {
2014                     systick.Frequency = value;
2015                 }
2016             }
2017 
2018             public int Divider
2019             {
2020                 get => systick.Divider;
2021                 set
2022                 {
2023                     systick.Divider = value;
2024                 }
2025             }
2026 
2027             private ulong reloadValue;
2028             private bool systickEnabled; // ENABLE
2029             private readonly LimitTimer systick;
2030             private readonly NVIC parent;
2031         }
2032 
2033         /// This is the simplest hash-map-like class, to store exceptions.
2034         /// We use <see cref="BankedExcpSecureBit"/> to mark banked exception as Secure.
2035         /// Such exception behaves like separate exception (e.g. Secure and Non-secure banked exception can exist at once).
2036         /// This way, we can use the end of the array to place the extra Secure banked exceptions
2037         /// it's still easier than using a dictionary.
2038         private class ExceptionSimpleArray<T> : IEnumerable<T>
2039         {
ExceptionSimpleArray()2040             public ExceptionSimpleArray()
2041             {
2042                 // Regular IRQs, plus banked, plus HardFault (which can be banked, but doesn't have to be)
2043                 container = new T[IRQCount + bankedInterrupts.Length / 2 + 1];
2044             }
2045 
Clear()2046             public void Clear()
2047             {
2048                 Array.Clear(container, 0, container.Length);
2049             }
2050 
2051             public T this[int index]
2052             {
2053                 get
2054                 {
2055                     return container[MapSystemExceptionToInteger(index)];
2056                 }
2057                 set
2058                 {
2059                     container[MapSystemExceptionToInteger(index)] = value;
2060                 }
2061             }
2062 
2063             public int Length => container.Length;
2064 
MapSystemExceptionToInteger(int exception)2065             private static int MapSystemExceptionToInteger(int exception)
2066             {
2067                 if(exception < BankedExcpSecureBit)
2068                 {
2069                     return exception;
2070                 }
2071                 switch(exception)
2072                 {
2073                     case (int)SystemException.MemManageFault_S:
2074                         return IRQCount;
2075                     case (int)SystemException.UsageFault_S:
2076                         return IRQCount + 1;
2077                     case (int)SystemException.SuperVisorCall_S:
2078                         return IRQCount + 2;
2079                     case (int)SystemException.PendSV_S:
2080                         return IRQCount + 3;
2081                     case (int)SystemException.SysTick_S:
2082                         return IRQCount + 4;
2083                     case (int)SystemException.HardFault_S:
2084                         return IRQCount + 5;
2085                     case (int)SystemException.DebugMonitor_S:
2086                         return IRQCount + 6;
2087                     default:
2088                         throw new InvalidOperationException($"Exception number {exception} is invalid");
2089                 }
2090             }
2091 
GetEnumerator()2092             public IEnumerator<T> GetEnumerator()
2093             {
2094                 return container.AsEnumerable().GetEnumerator();
2095             }
2096 
IEnumerable.GetEnumerator()2097             IEnumerator IEnumerable.GetEnumerator()
2098             {
2099                 return GetEnumerator();
2100             }
2101 
2102             private readonly T[] container;
2103         }
2104 
2105         // bit [16] DC / Cache enable. This is a global enable bit for data and unified caches.
2106         private readonly SecurityBanked<uint> ccr;
2107 
2108         private readonly bool defaultHaltSystickOnDeepSleep;
2109         private readonly SecurityBanked<SysTick> systick;
2110         private readonly byte priorityMask;
2111         private readonly SecurityBanked<bool> currentSevOnPending;
2112         private readonly Stack<int> activeIRQs;
2113         private readonly ISet<int> pendingIRQs;
2114         private readonly SecurityBanked<int> binaryPointPosition; // from the right
2115         private bool isNextAccessSecure;
2116         private bool canResetOnlyFromSecure;
2117         private bool deepSleepOnlyFromSecure;
2118         private bool prioritizeSecureInterrupts;
2119         private uint mpuControlRegister;
2120         private MPUVersion mpuVersion;
2121 
2122         private bool maskedInterruptPresent;
2123 
2124         // This is configurable through NVIC_ITNSx only for Hardware Interrupts (exception numbered 16 and above)
2125         // for configuring selected exceptions, look at AIRCR.BFHFNMINS
2126         // for other exceptions, this will be completely ignored
2127         private readonly InterruptTargetSecurityState[] targetInterruptSecurityState;
2128 
2129         private readonly ExceptionSimpleArray<IRQState> irqs;
2130         private readonly ExceptionSimpleArray<byte> priorities;
2131         private readonly Action resetMachine;
2132         private CortexM cpu;
2133         private readonly IMachine machine;
2134         private uint cpuId;
2135 
2136         private IFlagRegisterField sleepOnExitEnabled;
2137 
2138         private static readonly int[] bankedInterrupts = new int []
2139         {
2140             (int)SystemException.MemManageFault,
2141             (int)SystemException.UsageFault,
2142             (int)SystemException.SuperVisorCall,
2143             (int)SystemException.PendSV,
2144             (int)SystemException.SysTick,
2145             (int)SystemException.DebugMonitor,
2146 
2147             (int)SystemException.MemManageFault_S,
2148             (int)SystemException.UsageFault_S,
2149             (int)SystemException.SuperVisorCall_S,
2150             (int)SystemException.PendSV_S,
2151             (int)SystemException.SysTick_S,
2152             (int)SystemException.DebugMonitor_S,
2153             // Lack of HardFault here is not a mistake
2154             // HardFault is by default handled as Secure exception
2155         };
2156 
2157         private const string TrustZoneNSRegionWarning = "Without TrustZone enabled in the CPU, a NonSecure region should not be registered";
2158         private const int MPUStart             = 0xD90;
2159         private const int MPUEnd               = 0xDC4;    // resized for compat. with V8 MPU
2160         private const int SAUStart             = 0xDD0;
2161         private const int SAUEnd               = 0xDE4;
2162         private const int SetEnableStart       = 0x100;
2163         private const int SetEnableEnd         = 0x140;
2164         private const int ClearEnableStart     = 0x180;
2165         private const int ClearEnableEnd       = 0x1C0;
2166         private const int SetPendingStart      = 0x200;
2167         private const int SetPendingEnd        = 0x240;
2168         private const int ClearPendingStart    = 0x280;
2169         private const int ClearPendingEnd      = 0x2C0;
2170         private const int ActiveBitStart       = 0x300;
2171         private const int ActiveBitEnd         = 0x320;
2172         private const int TargetNonSecureStart = 0x380;
2173         private const int TargetNonSecureEnd   = 0x3C0;
2174         private const int PriorityStart        = 0x400;
2175         private const int PriorityEnd          = 0x7F0;
2176         private const int IRQCount             = 512 + 16 + 1;
2177         private const int SpuriousInterrupt    = IRQCount - 1;
2178         private const int VectKey              = 0x5FA;
2179         private const int VectKeyStat          = 0xFA05;
2180         private const uint SysTickMaximumValue = 0x00FFFFFF;
2181 
2182         private const int BankedExcpSecureBit  = 1 << 30;
2183         private const uint InterruptProgramStatusRegisterMask = 0x1FF;
2184         private const int SysTickCalibration100Hz = 100;
2185         private const int SysTickMaxValue = (1 << 24) - 1;
2186     }
2187 }
2188