1 //
2 // Copyright (c) 2010-2025 Antmicro
3 // Copyright (c) 2011-2015 Realtime Embedded
4 //
5 // This file is licensed under the MIT License.
6 // Full license text is available in 'licenses/MIT.txt'.
7 //
8 using System;
9 using System.Collections.Concurrent;
10 using System.Collections.Generic;
11 using System.IO;
12 using System.Linq;
13 using System.Runtime.InteropServices;
14 using System.Text;
15 using System.Threading;
16 using Antmicro.Migrant;
17 using Antmicro.Migrant.Hooks;
18 using Antmicro.Renode.Core;
19 using Antmicro.Renode.Debugging;
20 using Antmicro.Renode.Exceptions;
21 using Antmicro.Renode.Hooks;
22 using Antmicro.Renode.Logging;
23 using Antmicro.Renode.Logging.Profiling;
24 using Antmicro.Renode.Peripherals.Bus;
25 using Antmicro.Renode.Peripherals.CPU.Assembler;
26 using Antmicro.Renode.Peripherals.CPU.Disassembler;
27 using Antmicro.Renode.Time;
28 using Antmicro.Renode.Utilities;
29 using Antmicro.Renode.Utilities.Binding;
30 using ELFSharp.ELF;
31 using Machine = Antmicro.Renode.Core.Machine;
32 
33 using Range = Antmicro.Renode.Core.Range;
34 
35 namespace Antmicro.Renode.Peripherals.CPU
36 {
37     public static class TranslationCPUHooksExtensions
38     {
SetHookAtBlockBegin(this TranslationCPU cpu, [AutoParameter]IMachine m, string pythonScript)39         public static void SetHookAtBlockBegin(this TranslationCPU cpu, [AutoParameter]IMachine m, string pythonScript)
40         {
41             var engine = new BlockPythonEngine(m, cpu, pythonScript);
42             cpu.SetHookAtBlockBegin(engine.HookWithSize);
43         }
44 
SetHookAtBlockEnd(this TranslationCPU cpu, [AutoParameter]IMachine m, string pythonScript)45         public static void SetHookAtBlockEnd(this TranslationCPU cpu, [AutoParameter]IMachine m, string pythonScript)
46         {
47             var engine = new BlockPythonEngine(m, cpu, pythonScript);
48             cpu.SetHookAtBlockEnd(engine.HookWithSize);
49         }
50     }
51 
52     /// <summary>
53     /// <see cref="TranslationCPU"/> implements <see cref="ICluster{T}"/> interface
54     /// to seamlessly handle either cluster or CPU as a parameter to different methods.
55     /// </summary>
56     public abstract partial class TranslationCPU : BaseCPU, ICluster<TranslationCPU>, IGPIOReceiver, ICpuSupportingGdb, ICPUWithExternalMmu, ICPUWithMMU, INativeUnwindable, ICPUWithMetrics, ICPUWithMappedMemory, ICPUWithRegisters, ICPUWithMemoryAccessHooks, IControllableCPU
57     {
TranslationCPU(string cpuType, IMachine machine, Endianess endianness, CpuBitness bitness = CpuBitness.Bits32)58         protected TranslationCPU(string cpuType, IMachine machine, Endianess endianness, CpuBitness bitness = CpuBitness.Bits32)
59         : this(0, cpuType, machine, endianness, bitness)
60         {
61         }
62 
TranslationCPU(uint id, string cpuType, IMachine machine, Endianess endianness, CpuBitness bitness = CpuBitness.Bits32)63         protected TranslationCPU(uint id, string cpuType, IMachine machine, Endianess endianness, CpuBitness bitness = CpuBitness.Bits32)
64             : base(id, cpuType, machine, endianness, bitness)
65         {
66             atomicId = -1;
67             pauseGuard = new CpuThreadPauseGuard(this);
68             decodedIrqs = new Dictionary<Interrupt, HashSet<int>>();
69             hooks = new Dictionary<ulong, HookDescriptor>();
70             currentMappings = new List<SegmentMapping>();
71             InitializeRegisters();
72             Init();
73             InitDisas();
74             externalMmuWindowsCount = TlibGetMmuWindowsCount();
75             Clustered = new TranslationCPU[] { this };
76         }
77 
78         public new IEnumerable<ICluster<TranslationCPU>> Clusters { get; } = new List<ICluster<TranslationCPU>>(0);
79 
80         public new IEnumerable<TranslationCPU> Clustered { get; }
81 
82         public abstract string GDBArchitecture { get; }
83 
84         public abstract List<GDBFeatureDescriptor> GDBFeatures { get; }
85 
86         public bool TbCacheEnabled
87         {
88             get
89             {
90                 return TlibGetTbCacheEnabled() != 0;
91             }
92 
93             set
94             {
95                 TlibSetTbCacheEnabled(value ? 1u : 0u);
96             }
97         }
98 
99         public bool SyncPCEveryInstructionDisabled
100         {
101             get
102             {
103                 return TlibGetSyncPcEveryInstructionDisabled() != 0;
104             }
105 
106             set
107             {
108                 TlibSetSyncPcEveryInstructionDisabled(value ? 1u : 0u);
109             }
110         }
111 
112 
113         public bool ChainingEnabled
114         {
115             get
116             {
117                 return TlibGetChainingEnabled() != 0;
118             }
119 
120             set
121             {
122                 TlibSetChainingEnabled(value ? 1u : 0u);
123             }
124         }
125 
126         public int MaximumBlockSize
127         {
128             get
129             {
130                 return checked((int)TlibGetMaximumBlockSize());
131             }
132             set
133             {
134                 TlibSetMaximumBlockSize(checked((uint)value));
135                 ClearTranslationCache();
136             }
137         }
138 
139         /// <summary>
140         /// The value is used to convert instructions count to cycles, e.g.:
141         /// * for RISC-V CYCLE and MCYCLE CSRs
142         /// * in ARM Performance Monitoring Unit
143         /// </summary>
144         public decimal CyclesPerInstruction
145         {
146             get
147             {
148                 return checked(TlibGetMillicyclesPerInstruction() / 1000m);
149             }
150             set
151             {
152                 if(value <= 0)
153                 {
154                     throw new RecoverableException("Value must be a positive number.");
155                 }
156                 var millicycles = value * 1000m;
157                 if(millicycles % 1m != 0)
158                 {
159                     throw new RecoverableException("Value's precision can't be greater than 0.001");
160                 }
161                 TlibSetMillicyclesPerInstruction(checked((uint)millicycles));
162             }
163         }
164 
165         public bool LogTranslationBlockFetch
166         {
167             set
168             {
169                 if(value)
170                 {
171                     RenodeAttachLogTranslationBlockFetch(Marshal.GetFunctionPointerForDelegate(onTranslationBlockFetch));
172                 }
173                 else
174                 {
175                     RenodeAttachLogTranslationBlockFetch(IntPtr.Zero);
176                 }
177                 logTranslationBlockFetchEnabled = value;
178             }
179             get
180             {
181                 return logTranslationBlockFetchEnabled;
182             }
183         }
184 
185         // This value should only be read in CPU hooks (during execution of translated code).
186         public uint CurrentBlockDisassemblyFlags => TlibGetCurrentTbDisasFlags();
187 
188         public uint ExternalMmuWindowsCount => externalMmuWindowsCount;
189 
190         public bool ThreadSentinelEnabled { get; set; }
191 
192         private bool logTranslationBlockFetchEnabled;
193 
194         public override ulong ExecutedInstructions { get {return TlibGetTotalExecutedInstructions(); } }
195 
196         public int Slot { get{if(!slot.HasValue) slot = machine.SystemBus.GetCPUSlot(this); return slot.Value;} private set {slot = value;} }
197         private int? slot;
198 
ToString()199         public override string ToString()
200         {
201             return $"[CPU: {this.GetCPUThreadName(machine)}]";
202         }
203 
RequestReturn()204         public void RequestReturn()
205         {
206             TlibSetReturnRequest();
207         }
208 
FlushTlbPage(UInt64 address)209         public void FlushTlbPage(UInt64 address)
210         {
211             TlibFlushPage(address);
212         }
213 
ClearTranslationCache()214         public void ClearTranslationCache()
215         {
216             using(machine?.ObtainPausedState(true))
217             {
218                 TlibInvalidateTranslationCache();
219             }
220         }
221 
222         [PreSerialization]
PrepareState()223         private void PrepareState()
224         {
225             var statePtr = TlibExportState();
226             BeforeSave(statePtr);
227             cpuState = new byte[TlibGetStateSize()];
228             Marshal.Copy(statePtr, cpuState, 0, cpuState.Length);
229         }
230 
231         [PostSerialization]
FreeState()232         private void FreeState()
233         {
234             cpuState = null;
235         }
236 
237         [LatePostDeserialization]
RestoreState()238         private void RestoreState()
239         {
240             Init();
241             // TODO: state of the reset events
242             FreeState();
243             if(memoryAccessHook != null)
244             {
245                 // Repeat memory hook enable to make sure that the tcg context is set not to use the tlb
246                 TlibOnMemoryAccessEventEnabled(1);
247             }
248         }
249 
250         public override ExecutionMode ExecutionMode
251         {
252             get
253             {
254                 return base.ExecutionMode;
255             }
256 
257             set
258             {
259                 base.ExecutionMode = value;
260                 UpdateBlockBeginHookPresent();
261             }
262         }
263 
SyncTime()264         public override void SyncTime()
265         {
266             if(!OnPossessedThread)
267             {
268                 this.Log(LogLevel.Error, "Syncing time should be done from CPU thread only. Ignoring the operation");
269                 return;
270             }
271 
272             var numberOfExecutedInstructions = TlibGetExecutedInstructions();
273             this.Trace($"CPU executed {numberOfExecutedInstructions} instructions and time synced");
274             ReportProgress(numberOfExecutedInstructions);
275         }
276 
277         public string LogFile
278         {
279             get { return logFile; }
280             set
281             {
282                 logFile = value;
283                 LogTranslatedBlocks = (value != null);
284 
285                 try
286                 {
287                     // truncate the file
288                     File.WriteAllText(logFile, string.Empty);
289                 }
290                 catch(Exception e)
291                 {
292                     throw new RecoverableException($"There was a problem when preparing the log file {logFile}: {e.Message}");
293                 }
294             }
295         }
296 
OnLeavingResetState()297         protected override void OnLeavingResetState()
298         {
299             base.OnLeavingResetState();
300             TlibOnLeavingResetState();
301         }
302 
RequestPause()303         protected override void RequestPause()
304         {
305             base.RequestPause();
306             TlibSetReturnRequest();
307         }
308 
InnerPause(bool onCpuThread, bool checkPauseGuard)309         protected override void InnerPause(bool onCpuThread, bool checkPauseGuard)
310         {
311             base.InnerPause(onCpuThread, checkPauseGuard);
312 
313             if(onCpuThread && checkPauseGuard)
314             {
315                 pauseGuard.OrderPause();
316             }
317         }
318 
Reset()319         public override void Reset()
320         {
321             base.Reset();
322             isInterruptLoggingEnabled = false;
323             TlibReset();
324             ResetOpcodesCounters();
325             profiler?.Dispose();
326         }
327 
RequestTranslationBlockRestart(bool quiet = false)328         public bool RequestTranslationBlockRestart(bool quiet = false)
329         {
330             if(!OnPossessedThread)
331             {
332                 if(!quiet)
333                 {
334                     this.Log(LogLevel.Error, "Translation block restart should be requested from CPU thread only. Ignoring the operation.");
335                 }
336                 return false;
337             }
338             return pauseGuard.RequestTranslationBlockRestart(quiet);
339         }
340 
RaiseException(uint exceptionId)341         public void RaiseException(uint exceptionId)
342         {
343             TlibRaiseException(exceptionId);
344         }
345 
OnGPIO(int number, bool value)346         public virtual void OnGPIO(int number, bool value)
347         {
348             lock(lck)
349             {
350                 if(ThreadSentinelEnabled)
351                 {
352                     CheckIfOnSynchronizedThread();
353                 }
354                 this.NoisyLog("IRQ {0}, value {1}", number, value);
355                 // as we are waiting for an interrupt we should, obviously, not mask it
356                 if(started && (lastTlibResult == TlibExecutionResult.WaitingForInterrupt || !(DisableInterruptsWhileStepping && IsSingleStepMode)))
357                 {
358                     TlibSetIrqWrapped(number, value);
359                     if(EmulationManager.Instance.CurrentEmulation.Mode != Emulation.EmulationMode.SynchronizedIO)
360                     {
361                         sleeper.Interrupt();
362                     }
363                 }
364             }
365         }
366 
367         public override RegisterValue PC
368         {
369             get
370             {
371                 throw new NotImplementedException();
372             }
373             set
374             {
375                 throw new NotImplementedException();
376             }
377         }
378 
MapMemory(IMappedSegment segment)379         public void MapMemory(IMappedSegment segment)
380         {
381             if(segment.StartingOffset > bitness.GetMaxAddress() || segment.StartingOffset + segment.Size - 1 > bitness.GetMaxAddress())
382             {
383                 throw new RecoverableException("Could not map memory segment: it does not fit into address space");
384             }
385 
386             using(machine?.ObtainPausedState(true))
387             {
388                 currentMappings.Add(new SegmentMapping(segment));
389                 mappedMemory.Add(segment.GetRange());
390                 SetAccessMethod(segment.GetRange(), true);
391             }
392             this.NoisyLog("Registered memory at 0x{0:X}, size 0x{1:X}.", segment.StartingOffset, segment.Size);
393         }
394 
RegisterAccessFlags(ulong startAddress, ulong size, bool isIoMemory = false)395         public void RegisterAccessFlags(ulong startAddress, ulong size, bool isIoMemory = false)
396         {
397             TlibRegisterAccessFlagsForRange(startAddress, size, isIoMemory ? 1u : 0u);
398         }
399 
SetMappedMemoryEnabled(Range range, bool enabled)400         public void SetMappedMemoryEnabled(Range range, bool enabled)
401         {
402             using(machine?.ObtainPausedState(true))
403             {
404                 // Check if anything needs to be changed.
405                 if(enabled ? !disabledMemory.ContainsOverlappingRange(range) : disabledMemory.ContainsWholeRange(range))
406                 {
407                     return;
408                 }
409 
410                 if(enabled)
411                 {
412                     disabledMemory.Remove(range);
413                 }
414                 else
415                 {
416                     disabledMemory.Add(range);
417                 }
418                 SetAccessMethod(range, asMemory: enabled);
419             }
420         }
421 
UnmapMemory(Range range)422         public void UnmapMemory(Range range)
423         {
424             using(machine?.ObtainPausedState(true))
425             {
426                 // when unmapping memory, two things have to be done
427                 // first is to flag address range as no-memory (that is, I/O)
428                 SetAccessMethod(range, asMemory: false);
429 
430                 // remove mappings that are not used anymore
431                 currentMappings = currentMappings.
432                     Where(x => TlibIsRangeMapped(x.Segment.StartingOffset, x.Segment.StartingOffset + x.Segment.Size) == 1).ToList();
433                 mappedMemory.Remove(range);
434                 RebuildMemoryMappings();
435             }
436         }
437 
SetPageAccessViaIo(ulong address)438         public void SetPageAccessViaIo(ulong address)
439         {
440             TlibSetPageIoAccessed(address);
441         }
442 
ClearPageAccessViaIo(ulong address)443         public void ClearPageAccessViaIo(ulong address)
444         {
445             TlibClearPageIoAccessed(address);
446         }
447 
448         public bool DisableInterruptsWhileStepping { get; set; }
449 
450         // this is just for easier usage in Monitor
LogFunctionNames(bool value, bool removeDuplicates = false)451         public void LogFunctionNames(bool value, bool removeDuplicates = false)
452         {
453             LogFunctionNames(value, string.Empty, removeDuplicates);
454         }
455 
GetCurrentInstructionsCount()456         public ulong GetCurrentInstructionsCount()
457         {
458             return TlibGetTotalExecutedInstructions();
459         }
460 
LogFunctionNames(bool value, string spaceSeparatedPrefixes = R, bool removeDuplicates = false)461         public void LogFunctionNames(bool value, string spaceSeparatedPrefixes = "", bool removeDuplicates = false)
462         {
463             if(!value)
464             {
465                 SetInternalHookAtBlockBegin(null);
466                 return;
467             }
468 
469             var prefixesAsArray = spaceSeparatedPrefixes.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
470             // using string builder here is due to performance reasons: test shows that string.Format is much slower
471             var messageBuilder = new StringBuilder(256);
472 
473             Symbol previousSymbol = null;
474 
475             SetInternalHookAtBlockBegin((pc, size) =>
476             {
477                 if(Bus.TryFindSymbolAt(pc, out var name, out var symbol, this))
478                 {
479                     if(removeDuplicates && symbol == previousSymbol)
480                     {
481                         return;
482                     }
483                     previousSymbol = symbol;
484                 }
485 
486                 if(spaceSeparatedPrefixes != "" && (name == null || !prefixesAsArray.Any(name.StartsWith)))
487                 {
488                     return;
489                 }
490                 messageBuilder.Clear();
491                 this.Log(LogLevel.Info, messageBuilder.Append("Entering function ").Append(name ?? "without name").Append(" at 0x").Append(pc.ToString("X")).ToString());
492             });
493         }
494 
495         // TODO: improve this when backend/analyser stuff is done
496 
497         public bool UpdateContextOnLoadAndStore { get; set; }
498 
DecodeInterrupt(int number)499         protected abstract Interrupt DecodeInterrupt(int number);
500 
ClearHookAtBlockBegin()501         public void ClearHookAtBlockBegin()
502         {
503             SetHookAtBlockBegin(null);
504         }
505 
SetHookAtBlockBegin(Action<ulong, uint> hook)506         public void SetHookAtBlockBegin(Action<ulong, uint> hook)
507         {
508             using(machine?.ObtainPausedState(true))
509             {
510                 if((hook == null) ^ (blockBeginUserHook == null))
511                 {
512                     ClearTranslationCache();
513                 }
514                 blockBeginUserHook = hook;
515                 UpdateBlockBeginHookPresent();
516             }
517         }
518 
SetHookAtBlockEnd(Action<ulong, uint> hook)519         public void SetHookAtBlockEnd(Action<ulong, uint> hook)
520         {
521             using(machine?.ObtainPausedState(true))
522             {
523                 if((hook == null) ^ (blockFinishedHook == null))
524                 {
525                     ClearTranslationCache();
526                     TlibSetBlockFinishedHookPresent(hook != null ? 1u : 0u);
527                 }
528                 blockFinishedHook = hook;
529             }
530         }
531 
SetHookAtMemoryAccess(Action<ulong, MemoryOperation, ulong, ulong, ulong> hook)532         public void SetHookAtMemoryAccess(Action<ulong, MemoryOperation, ulong, ulong, ulong> hook)
533         {
534             TlibOnMemoryAccessEventEnabled(hook != null ? 1 : 0);
535             memoryAccessHook = hook;
536         }
537 
AddHookAtInterruptBegin(Action<ulong> hook)538         public void AddHookAtInterruptBegin(Action<ulong> hook)
539         {
540             if(interruptBeginHook == null)
541             {
542                 TlibSetInterruptBeginHookPresent(1u);
543             }
544             interruptBeginHook += hook;
545         }
546 
AddHookOnMmuFault(Action<ulong, AccessType, int> hook)547         public void AddHookOnMmuFault(Action<ulong, AccessType, int> hook)
548         {
549             mmuFaultHook += hook;
550         }
551 
AddHookAtInterruptEnd(Action<ulong> hook)552         public void AddHookAtInterruptEnd(Action<ulong> hook)
553         {
554             if(!Architecture.Contains("riscv") && !Architecture.Contains("arm"))
555             {
556                 throw new RecoverableException("Hooks at the end of interrupt are supported only in the RISC-V and ARM architectures");
557             }
558 
559             if(interruptEndHook == null)
560             {
561                 TlibSetInterruptEndHookPresent(1u);
562             }
563             interruptEndHook += hook;
564         }
565 
LogCpuInterrupts(bool isEnabled)566         public void LogCpuInterrupts(bool isEnabled)
567         {
568             if(isEnabled)
569             {
570                 if(!isInterruptLoggingEnabled)
571                 {
572                     AddHookAtInterruptBegin(LogCpuInterruptBegin);
573                     AddHookAtInterruptEnd(LogCpuInterruptEnd);
574                     isInterruptLoggingEnabled = true;
575                 }
576             }
577             else
578             {
579                 RemoveHookAtInterruptBegin(LogCpuInterruptBegin);
580                 RemoveHookAtInterruptEnd(LogCpuInterruptEnd);
581                 isInterruptLoggingEnabled = false;
582             }
583         }
584 
AddHookAtWfiStateChange(Action<bool> hook)585         public void AddHookAtWfiStateChange(Action<bool> hook)
586         {
587             if(wfiStateChangeHook == null)
588             {
589                 TlibSetCpuWfiStateChangeHookPresent(1);
590             }
591             wfiStateChangeHook += hook;
592         }
593 
RemoveHookAtWfiStateChange(Action<bool> hook)594         public void RemoveHookAtWfiStateChange(Action<bool> hook)
595         {
596             wfiStateChangeHook -= hook;
597             if(wfiStateChangeHook == null)
598             {
599                 TlibSetCpuWfiStateChangeHookPresent(0);
600             }
601         }
602 
603         [Export]
ReadByteFromBus(ulong offset, ulong cpuState)604         protected ulong ReadByteFromBus(ulong offset, ulong cpuState)
605         {
606             if(UpdateContextOnLoadAndStore)
607             {
608                 TlibRestoreContext();
609             }
610             using(var guard = ObtainPauseGuardForReading(offset, SysbusAccessWidth.Byte))
611             {
612                 // If the transaction was interrupted while handling a watchpoint, return 0 immediately to avoid
613                 // duplicating the access' side effect.
614                 return guard.InterruptTransaction
615                     ? 0
616                     : (ulong)machine.SystemBus.ReadByte(offset, this, cpuState);
617             }
618         }
619 
620         [Export]
ReadWordFromBus(ulong offset, ulong cpuState)621         protected ulong ReadWordFromBus(ulong offset, ulong cpuState)
622         {
623             if(UpdateContextOnLoadAndStore)
624             {
625                 TlibRestoreContext();
626             }
627             using(var guard = ObtainPauseGuardForReading(offset, SysbusAccessWidth.Word))
628             {
629                 // If the transaction was interrupted while handling a watchpoint, return 0 immediately to avoid
630                 // duplicating the access' side effect.
631                 return guard.InterruptTransaction
632                     ? 0
633                     : (ulong)machine.SystemBus.ReadWord(offset, this, cpuState);
634             }
635         }
636 
637         [Export]
ReadDoubleWordFromBus(ulong offset, ulong cpuState)638         protected ulong ReadDoubleWordFromBus(ulong offset, ulong cpuState)
639         {
640             if(UpdateContextOnLoadAndStore)
641             {
642                 TlibRestoreContext();
643             }
644             using(var guard = ObtainPauseGuardForReading(offset, SysbusAccessWidth.DoubleWord))
645             {
646                 // If the transaction was interrupted while handling a watchpoint, return 0 immediately to avoid
647                 // duplicating the access' side effect.
648                 return guard.InterruptTransaction
649                     ? 0
650                     : machine.SystemBus.ReadDoubleWord(offset, this, cpuState);
651             }
652         }
653 
654         [Export]
ReadQuadWordFromBus(ulong offset, ulong cpuState)655         protected ulong ReadQuadWordFromBus(ulong offset, ulong cpuState)
656         {
657             if(UpdateContextOnLoadAndStore)
658             {
659                 TlibRestoreContext();
660             }
661             using(var guard = ObtainPauseGuardForReading(offset, SysbusAccessWidth.QuadWord))
662             {
663                 // If the transaction was interrupted while handling a watchpoint, return 0 immediately to avoid
664                 // duplicating the access' side effect.
665                 return guard.InterruptTransaction
666                     ? 0
667                     : machine.SystemBus.ReadQuadWord(offset, this, cpuState);
668             }
669         }
670 
671         [Export]
WriteByteToBus(ulong offset, ulong value, ulong cpuState)672         protected void WriteByteToBus(ulong offset, ulong value, ulong cpuState)
673         {
674             if(UpdateContextOnLoadAndStore)
675             {
676                 TlibRestoreContext();
677             }
678             using(var guard = ObtainPauseGuardForWriting(offset, SysbusAccessWidth.Byte, value))
679             {
680                 // If the transaction was interrupted while handling a watchpoint, don't perform the write to avoid
681                 // duplicating the access' side effect.
682                 if(!guard.InterruptTransaction)
683                 {
684                     machine.SystemBus.WriteByte(offset, unchecked((byte)value), this, cpuState);
685                 }
686             }
687         }
688 
689         [Export]
WriteWordToBus(ulong offset, ulong value, ulong cpuState)690         protected void WriteWordToBus(ulong offset, ulong value, ulong cpuState)
691         {
692             if(UpdateContextOnLoadAndStore)
693             {
694                 TlibRestoreContext();
695             }
696             using(var guard = ObtainPauseGuardForWriting(offset, SysbusAccessWidth.Word, value))
697             {
698                 // If the transaction was interrupted while handling a watchpoint, don't perform the write to avoid
699                 // duplicating the access' side effect.
700                 if(!guard.InterruptTransaction)
701                 {
702                     machine.SystemBus.WriteWord(offset, unchecked((ushort)value), this, cpuState);
703                 }
704             }
705         }
706 
707         [Export]
WriteDoubleWordToBus(ulong offset, ulong value, ulong cpuState)708         protected void WriteDoubleWordToBus(ulong offset, ulong value, ulong cpuState)
709         {
710             if(UpdateContextOnLoadAndStore)
711             {
712                 TlibRestoreContext();
713             }
714             using(var guard = ObtainPauseGuardForWriting(offset, SysbusAccessWidth.DoubleWord, value))
715             {
716                 // If the transaction was interrupted while handling a watchpoint, don't perform the write to avoid
717                 // duplicating the access' side effect.
718                 if(!guard.InterruptTransaction)
719                 {
720                     machine.SystemBus.WriteDoubleWord(offset, (uint)value, this, cpuState);
721                 }
722             }
723         }
724 
725         [Export]
WriteQuadWordToBus(ulong offset, ulong value, ulong cpuState)726         protected void WriteQuadWordToBus(ulong offset, ulong value, ulong cpuState)
727         {
728             if(UpdateContextOnLoadAndStore)
729             {
730                 TlibRestoreContext();
731             }
732             using(var guard = ObtainPauseGuardForWriting(offset, SysbusAccessWidth.QuadWord, value))
733             {
734                 // If the transaction was interrupted while handling a watchpoint, don't perform the write to avoid
735                 // duplicating the access' side effect.
736                 if(!guard.InterruptTransaction)
737                 {
738                     machine.SystemBus.WriteQuadWord(offset, value, this, cpuState);
739                 }
740             }
741         }
742 
ReadByteFromBus(ulong offset)743         protected ulong ReadByteFromBus(ulong offset)
744         {
745             return ReadByteFromBus(offset, GetCPUStateForMemoryTransaction());
746         }
747 
ReadWordFromBus(ulong offset)748         protected ulong ReadWordFromBus(ulong offset)
749         {
750             return ReadWordFromBus(offset, GetCPUStateForMemoryTransaction());
751         }
752 
ReadDoubleWordFromBus(ulong offset)753         protected ulong ReadDoubleWordFromBus(ulong offset)
754         {
755             return ReadDoubleWordFromBus(offset, GetCPUStateForMemoryTransaction());
756         }
757 
ReadQuadWordFromBus(ulong offset)758         protected ulong ReadQuadWordFromBus(ulong offset)
759         {
760             return ReadQuadWordFromBus(offset, GetCPUStateForMemoryTransaction());
761         }
762 
WriteByteToBus(ulong offset, ulong value)763         protected void WriteByteToBus(ulong offset, ulong value)
764         {
765             WriteByteToBus(offset, value, GetCPUStateForMemoryTransaction());
766         }
767 
WriteWordToBus(ulong offset, ulong value)768         protected void WriteWordToBus(ulong offset, ulong value)
769         {
770             WriteWordToBus(offset, value, GetCPUStateForMemoryTransaction());
771         }
772 
WriteDoubleWordToBus(ulong offset, ulong value)773         protected void WriteDoubleWordToBus(ulong offset, ulong value)
774         {
775             WriteDoubleWordToBus(offset, value, GetCPUStateForMemoryTransaction());
776         }
777 
WriteQuadWordToBus(ulong offset, ulong value)778         protected void WriteQuadWordToBus(ulong offset, ulong value)
779         {
780             WriteQuadWordToBus(offset, value, GetCPUStateForMemoryTransaction());
781         }
782 
GetExceptionDescription(ulong exceptionIndex)783         protected virtual string GetExceptionDescription(ulong exceptionIndex)
784         {
785             return $"Undecoded {exceptionIndex}";
786         }
787 
SetRegister(int register, RegisterValue value)788         public abstract void SetRegister(int register, RegisterValue value);
789 
SetRegisterUnsafe(int register, RegisterValue value)790         public void SetRegisterUnsafe(int register, RegisterValue value)
791         {
792             // This is obsolete API, left here only for compatibility
793             this.Log(LogLevel.Warning, "Using `SetRegisterUnsafe` API is obsolete. Please change to `SetRegister`.");
794             SetRegister(register, value);
795         }
796 
GetRegister(int register)797         public abstract RegisterValue GetRegister(int register);
798 
GetRegisterUnsafe(int register)799         public RegisterValue GetRegisterUnsafe(int register)
800         {
801             // This is obsolete API, left here only for compatibility
802             this.Log(LogLevel.Warning, "Using `GetRegisterUnsafe` API is obsolete. Please change to `GetRegister`.");
803             return GetRegister(register);
804         }
805 
GetRegisters()806         public abstract IEnumerable<CPURegister> GetRegisters();
807 
LogCpuInterruptBegin(ulong exceptionIndex)808         private void LogCpuInterruptBegin(ulong exceptionIndex)
809         {
810             this.Log(LogLevel.Info, "Begin of the interrupt: {0}", GetExceptionDescription(exceptionIndex));
811         }
812 
LogCpuInterruptEnd(ulong exceptionIndex)813         private void LogCpuInterruptEnd(ulong exceptionIndex)
814         {
815             this.Log(LogLevel.Info, "End of the interrupt: {0}", GetExceptionDescription(exceptionIndex));
816         }
817 
SetInternalHookAtBlockBegin(Action<ulong, uint> hook)818         private void SetInternalHookAtBlockBegin(Action<ulong, uint> hook)
819         {
820             using(machine?.ObtainPausedState(true))
821             {
822                 if((hook == null) ^ (blockBeginInternalHook == null))
823                 {
824                     ClearTranslationCache();
825                 }
826                 blockBeginInternalHook = hook;
827                 UpdateBlockBeginHookPresent();
828             }
829         }
830 
AssertMmuEnabled()831         private bool AssertMmuEnabled()
832         {
833             if(!externalMmuEnabled)
834             {
835                 throw new RecoverableException("External MMU not enabled");
836             }
837             return externalMmuEnabled;
838         }
839 
AssertMmuEnabledAndWindowInRange(uint index)840         private bool AssertMmuEnabledAndWindowInRange(uint index)
841         {
842             var windowInRange = index < externalMmuWindowsCount;
843             if(!windowInRange)
844             {
845                 throw new RecoverableException($"Window index to high, maximum number: {externalMmuWindowsCount - 1}, got {index}");
846             }
847             return AssertMmuEnabled() && windowInRange;
848         }
849 
850         /* Currently, due to the used types, 64 bit targets will always pass this check.
851         Also on such platforms unary overflow is not possible */
AssertMmuWindowAddressInRange(ulong address, bool inclusiveRange = false)852         private bool AssertMmuWindowAddressInRange(ulong address, bool inclusiveRange = false)
853         {
854             ulong maxValue;
855             switch(this.bitness)
856             {
857                 case CpuBitness.Bits32:
858                     maxValue = UInt32.MaxValue;
859                     break;
860                 case CpuBitness.Bits64:
861                     maxValue = UInt64.MaxValue;
862                     break;
863                 default:
864                     throw new ArgumentException("Unexpected value of the CpuBitness");
865             }
866 
867             if(inclusiveRange && address != 0)
868             {
869                 address -= 1;
870             }
871 
872             if(address > maxValue)
873             {
874                 throw new RecoverableException($"Address is outside of the possible range. Maximum value: {maxValue}");
875             }
876 
877             return true;
878         }
879 
EnableExternalWindowMmu(bool value)880         public void EnableExternalWindowMmu(bool value)
881         {
882             TlibEnableExternalWindowMmu(value ? 1u : 0u);
883             externalMmuEnabled = value;
884         }
885 
AcquireExternalMmuWindow(uint type)886         public int AcquireExternalMmuWindow(uint type)
887         {
888             return AssertMmuEnabled() ? TlibAcquireMmuWindow(type) : -1;
889         }
890 
ResetMmuWindow(uint index)891         public void ResetMmuWindow(uint index)
892         {
893             if(AssertMmuEnabledAndWindowInRange(index))
894             {
895                 TlibResetMmuWindow(index);
896             }
897         }
898 
SetMmuWindowAddend(uint index, ulong addend)899         public void SetMmuWindowAddend(uint index, ulong addend)
900         {
901             if(AssertMmuEnabledAndWindowInRange(index))
902             {
903                 TlibSetMmuWindowAddend(index, addend);
904             }
905         }
906 
SetMmuWindowStart(uint index, ulong startAddress)907         public void SetMmuWindowStart(uint index, ulong startAddress)
908         {
909             if(AssertMmuEnabledAndWindowInRange(index) && AssertMmuWindowAddressInRange(startAddress))
910             {
911                 TlibSetMmuWindowStart(index, startAddress);
912             }
913         }
914 
SetMmuWindowEnd(uint index, ulong endAddress)915         public void SetMmuWindowEnd(uint index, ulong endAddress)
916         {
917             if(AssertMmuEnabledAndWindowInRange(index) && AssertMmuWindowAddressInRange(endAddress, inclusiveRange: true))
918             {
919                 bool useInclusiveEndRange= false;
920                 // Overflow on 64bits currently not possible due to type constraints
921                 if(this.bitness == CpuBitness.Bits32)
922                 {
923                     useInclusiveEndRange = ((endAddress - 1) == UInt32.MaxValue);
924                 }
925 
926                 if(useInclusiveEndRange)
927                 {
928                     endAddress -= 1;
929                 }
930 
931                 this.DebugLog("Setting range end to {0} addr 0x{1:x}", useInclusiveEndRange ? "inclusive" : "exclusive", endAddress);
932                 TlibSetMmuWindowEnd(index, endAddress, useInclusiveEndRange? 1u : 0u);
933             }
934         }
935 
SetMmuWindowPrivileges(uint index, uint permissions)936         public void SetMmuWindowPrivileges(uint index, uint permissions)
937         {
938             if(AssertMmuEnabledAndWindowInRange(index))
939             {
940                 TlibSetWindowPrivileges(index, permissions);
941             }
942         }
943 
GetMmuWindowAddend(uint index)944         public ulong GetMmuWindowAddend(uint index)
945         {
946             return AssertMmuEnabledAndWindowInRange(index) ? TlibGetMmuWindowAddend(index) : 0;
947         }
948 
GetMmuWindowStart(uint index)949         public ulong GetMmuWindowStart(uint index)
950         {
951             return AssertMmuEnabledAndWindowInRange(index) ? TlibGetMmuWindowStart(index) : 0;
952         }
953 
GetMmuWindowEnd(uint index)954         public ulong GetMmuWindowEnd(uint index)
955         {
956             return AssertMmuEnabledAndWindowInRange(index) ? TlibGetMmuWindowEnd(index) : 0;
957         }
958 
GetMmuWindowPrivileges(uint index)959         public uint GetMmuWindowPrivileges(uint index)
960         {
961             return AssertMmuEnabledAndWindowInRange(index) ? TlibGetWindowPrivileges(index) : 0;
962         }
963 
SetAccessMethod(Range range, bool asMemory)964         private void SetAccessMethod(Range range, bool asMemory)
965         {
966             using(machine?.ObtainPausedState(true))
967             {
968                 ValidateMemoryRangeAndThrow(range);
969                 if(!mappedMemory.ContainsWholeRange(range))
970                 {
971                     throw new RecoverableException(
972                         $"Tried to set mapped memory access method at {range} which isn't mapped memory in CPU: {this.GetName()}"
973                     );
974                 }
975 
976                 if(asMemory)
977                 {
978                     TlibMapRange(range.StartAddress, range.Size);
979                 }
980                 else
981                 {
982                     TlibUnmapRange(range.StartAddress, range.EndAddress);
983                 }
984             }
985         }
986 
ValidateMemoryRangeAndThrow(Range range)987         private void ValidateMemoryRangeAndThrow(Range range)
988         {
989             var pageSize = TlibGetPageSize();
990             var startUnaligned = (range.StartAddress % pageSize) != 0;
991             var sizeUnaligned = (range.Size % pageSize) != 0;
992             if(startUnaligned || sizeUnaligned)
993             {
994                 throw new RecoverableException($"Could not register memory at offset 0x{range.StartAddress:X} and size 0x{range.Size:X} - the {(startUnaligned ? "registration address" : "size")} has to be aligned to guest page size 0x{pageSize:X}.");
995             }
996         }
997 
InvokeInCpuThreadSafely(Action a)998         private void InvokeInCpuThreadSafely(Action a)
999         {
1000             actionsToExecuteOnCpuThread.Enqueue(a);
1001         }
1002 
RemoveHookAtInterruptBegin(Action<ulong> hook)1003         private void RemoveHookAtInterruptBegin(Action<ulong> hook)
1004         {
1005             interruptBeginHook -= hook;
1006             if(interruptBeginHook == null)
1007             {
1008                 TlibSetInterruptBeginHookPresent(0u);
1009             }
1010         }
1011 
RemoveHookAtInterruptEnd(Action<ulong> hook)1012         private void RemoveHookAtInterruptEnd(Action<ulong> hook)
1013         {
1014             interruptEndHook -= hook;
1015             if(interruptEndHook == null)
1016             {
1017                 TlibSetInterruptEndHookPresent(0u);
1018             }
1019         }
1020 
1021         private ConcurrentQueue<Action> actionsToExecuteOnCpuThread = new ConcurrentQueue<Action>();
1022         private TlibExecutionResult lastTlibResult;
1023 
1024         // TODO
1025         private object lck = new object();
1026 
1027         protected virtual bool IsSecondary
1028         {
1029             get
1030             {
1031                 return Slot > 0;
1032             }
1033         }
1034 
1035         [Export]
IsMemoryDisabled(ulong start, ulong size)1036         private uint IsMemoryDisabled(ulong start, ulong size)
1037         {
1038             return disabledMemory.ContainsOverlappingRange(start.By(size)) ? 1u : 0u;
1039         }
1040 
1041         /// <remarks>
1042         /// This method should be called from tlib only, and never from C#, since it uses `ObtainGenericPauseGuard`
1043         /// see: <see cref="ObtainGenericPauseGuard" /> for more information.
1044         /// </remarks>
1045         [Export]
OnWfiStateChange(int isInWfi)1046         private void OnWfiStateChange(int isInWfi)
1047         {
1048             using(ObtainGenericPauseGuard())
1049             {
1050                 wfiStateChangeHook?.Invoke(isInWfi > 0);
1051             }
1052         }
1053 
1054         /// <remarks>
1055         /// This method should be called from tlib only, and never from C#, since it uses `ObtainGenericPauseGuard`
1056         /// see: <see cref="ObtainGenericPauseGuard" /> for more information.
1057         /// </remarks>
1058         [Export]
OnBlockBegin(ulong address, uint size)1059         private uint OnBlockBegin(ulong address, uint size)
1060         {
1061             ReactivateHooks();
1062 
1063             using(ObtainGenericPauseGuard())
1064             {
1065                 blockBeginInternalHook?.Invoke(address, size);
1066                 blockBeginUserHook?.Invoke(address, size);
1067             }
1068 
1069             return (currentHaltedState || isPaused) ? 0 : 1u;
1070         }
1071 
1072         /// <remarks>
1073         /// This method should be called from tlib only, and never from C#, since it uses `ObtainGenericPauseGuard`
1074         /// see: <see cref="ObtainGenericPauseGuard" /> for more information.
1075         /// </remarks>
1076         [Export]
OnBlockFinished(ulong pc, uint executedInstructions)1077         private void OnBlockFinished(ulong pc, uint executedInstructions)
1078         {
1079             using(ObtainGenericPauseGuard())
1080             {
1081                 blockFinishedHook?.Invoke(pc, executedInstructions);
1082             }
1083         }
1084 
1085         [Export]
OnInterruptBegin(ulong interruptIndex)1086         private void OnInterruptBegin(ulong interruptIndex)
1087         {
1088             interruptBeginHook?.Invoke(interruptIndex);
1089         }
1090 
1091         [Export]
MmuFaultExternalHandler(ulong address, int accessType, int windowIndex)1092         private void MmuFaultExternalHandler(ulong address, int accessType, int windowIndex)
1093         {
1094             this.Log(LogLevel.Noisy, "External MMU fault at 0x{0:X} when trying to access as {1}", address, (AccessType)accessType);
1095 
1096             if(windowIndex == -1)
1097             {
1098                 this.Log(LogLevel.Error, "MMU fault - the address 0x{0:X} is not specified in any of the existing ranges", address);
1099             }
1100             mmuFaultHook?.Invoke(address, (AccessType)accessType, windowIndex);
1101         }
1102 
1103         [Export]
OnInterruptEnd(ulong interruptIndex)1104         private void OnInterruptEnd(ulong interruptIndex)
1105         {
1106             interruptEndHook?.Invoke(interruptIndex);
1107         }
1108 
1109         [Export]
OnMemoryAccess(ulong pc, uint operation, ulong virtualAddress, ulong value)1110         private void OnMemoryAccess(ulong pc, uint operation, ulong virtualAddress, ulong value)
1111         {
1112             // We don't care if translation fails here (the address is unchanged in this case)
1113             TryTranslateAddress(virtualAddress, Misc.MemoryOperationToMpuAccess((MemoryOperation)operation), out var physicalAddress);
1114             memoryAccessHook?.Invoke(pc, (MemoryOperation)operation, virtualAddress, physicalAddress, value);
1115         }
1116 
1117         [Export]
OnMassBroadcastDirty(IntPtr arrayStart, int size)1118         private void OnMassBroadcastDirty(IntPtr arrayStart, int size)
1119         {
1120             var tempArray = new long[size];
1121             Marshal.Copy(arrayStart, tempArray, 0, size);
1122             machine.AppendDirtyAddresses(this, tempArray);
1123         }
1124 
1125         [Transient]
1126         private IntPtr dirtyAddressesPtr = IntPtr.Zero;
1127 
1128         [Export]
GetDirty(IntPtr size)1129         private IntPtr GetDirty(IntPtr size)
1130         {
1131             var dirtyAddressesList = machine.GetNewDirtyAddressesForCore(this);
1132             var newAddressesCount = dirtyAddressesList.Length;
1133 
1134             if(newAddressesCount > 0)
1135             {
1136                 dirtyAddressesPtr = memoryManager.Reallocate(dirtyAddressesPtr, new IntPtr(newAddressesCount * 8));
1137                 Marshal.Copy(dirtyAddressesList, 0, dirtyAddressesPtr, newAddressesCount);
1138             }
1139             Marshal.WriteInt64(size, newAddressesCount);
1140 
1141             return dirtyAddressesPtr;
1142         }
1143 
InitializeRegisters()1144         protected virtual void InitializeRegisters()
1145         {
1146         }
1147 
OnTranslationBlockFetch(ulong offset)1148         private void OnTranslationBlockFetch(ulong offset)
1149         {
1150             var info = Bus.FindSymbolAt(offset, this);
1151             if(info != string.Empty)
1152             {
1153                 info = " - " + info;
1154             }
1155             this.Log(LogLevel.Info, "Fetching block @ 0x{0:X8}{1}", offset, info);
1156         }
1157 
1158         [Export]
OnTranslationCacheSizeChange(ulong realSize)1159         private void OnTranslationCacheSizeChange(ulong realSize)
1160         {
1161             this.Log(LogLevel.Debug, "Translation cache size was corrected to {0}B ({1}B).", Misc.NormalizeBinary(realSize), realSize);
1162         }
1163 
HandleRamSetup()1164         private void HandleRamSetup()
1165         {
1166             foreach(var mapping in currentMappings)
1167             {
1168                 var range = mapping.Segment.GetRange();
1169                 if(!disabledMemory.ContainsOverlappingRange(range))
1170                 {
1171                     SetAccessMethod(range, asMemory: true);
1172                 }
1173             }
1174         }
1175 
AddHook(ulong addr, Action<ICpuSupportingGdb, ulong> hook)1176         public void AddHook(ulong addr, Action<ICpuSupportingGdb, ulong> hook)
1177         {
1178             lock(hooks)
1179             {
1180                 if(!hooks.ContainsKey(addr))
1181                 {
1182                     hooks[addr] = new HookDescriptor(this, addr);
1183                 }
1184 
1185                 hooks[addr].AddCallback(hook);
1186                 this.DebugLog("Added hook @ 0x{0:X}", addr);
1187             }
1188         }
1189 
RemoveHook(ulong addr, Action<ICpuSupportingGdb, ulong> hook)1190         public void RemoveHook(ulong addr, Action<ICpuSupportingGdb, ulong> hook)
1191         {
1192             lock(hooks)
1193             {
1194                 HookDescriptor descriptor;
1195                 if(!hooks.TryGetValue(addr, out descriptor) || !descriptor.RemoveCallback(hook))
1196                 {
1197                     this.Log(LogLevel.Warning, "Tried to remove not existing hook from address 0x{0:x}", addr);
1198                     return;
1199                 }
1200                 if(descriptor.IsEmpty)
1201                 {
1202                     hooks.Remove(addr);
1203                 }
1204                 if(!hooks.Any(x => !x.Value.IsActive))
1205                 {
1206                     isAnyInactiveHook = false;
1207                 }
1208                 UpdateBlockBeginHookPresent();
1209             }
1210         }
1211 
InvalidateTranslationBlocks(IntPtr start, IntPtr end)1212         public void InvalidateTranslationBlocks(IntPtr start, IntPtr end)
1213         {
1214             if(disposing)
1215             {
1216                 return;
1217             }
1218             TlibInvalidateTranslationBlocks(start, end);
1219         }
1220 
RemoveHooksAt(ulong addr)1221         public void RemoveHooksAt(ulong addr)
1222         {
1223             lock(hooks)
1224             {
1225                 if(hooks.Remove(addr))
1226                 {
1227                     TlibRemoveBreakpoint(addr);
1228                 }
1229                 if(!hooks.Any(x => !x.Value.IsActive))
1230                 {
1231                     isAnyInactiveHook = false;
1232                 }
1233                 UpdateBlockBeginHookPresent();
1234             }
1235         }
1236 
EnterSingleStepModeSafely(HaltArguments args)1237         public void EnterSingleStepModeSafely(HaltArguments args)
1238         {
1239             // this method should only be called from CPU thread,
1240             // but we should check it anyway
1241             CheckCpuThreadId();
1242 
1243             ExecutionMode = ExecutionMode.SingleStep;
1244 
1245             UpdateHaltedState();
1246             InvokeHalted(args);
1247         }
1248 
Dispose()1249         public override void Dispose()
1250         {
1251             base.Dispose();
1252             profiler?.Dispose();
1253         }
1254 
DisposeInner(bool silent = false)1255         protected override void DisposeInner(bool silent = false)
1256         {
1257             base.DisposeInner(silent);
1258             TimeHandle.Dispose();
1259             RemoveAllHooks();
1260             TlibDispose();
1261             RenodeFreeHostBlocks();
1262             binder.Dispose();
1263             if(!EmulationManager.DisableEmulationFilesCleanup)
1264             {
1265                 File.Delete(libraryFile);
1266             }
1267             if(dirtyAddressesPtr != IntPtr.Zero)
1268             {
1269                 memoryManager.Free(dirtyAddressesPtr);
1270             }
1271             memoryManager.CheckIfAllIsFreed();
1272         }
1273 
1274         [Export]
ReportAbort(string message)1275         private void ReportAbort(string message)
1276         {
1277             this.Log(LogLevel.Error, "CPU abort [PC=0x{0:X}]: {1}.", PC.RawValue, message);
1278             throw new CpuAbortException(message);
1279         }
1280 
1281         /*
1282             Increments each time a new translation library resource is created.
1283             This counter marks each new instance of a translation library with a new number, which is used in file names to avoid collisions.
1284             It has to survive emulation reset, so the file names remain unique.
1285         */
1286         private static int CpuCounter = 0;
1287 
UpdateHaltedState(bool ignoreExecutionMode = false)1288         protected override bool UpdateHaltedState(bool ignoreExecutionMode = false)
1289         {
1290             if(!base.UpdateHaltedState(ignoreExecutionMode))
1291             {
1292                 return false;
1293             }
1294 
1295             if(currentHaltedState)
1296             {
1297                 TlibSetReturnRequest();
1298             }
1299 
1300             return true;
1301         }
1302 
Init()1303         private void Init()
1304         {
1305             memoryManager = new SimpleMemoryManager(this);
1306             isPaused = true;
1307 
1308             onTranslationBlockFetch = OnTranslationBlockFetch;
1309 
1310             // PowerPC always uses the big-endian translation library
1311             var endianSuffix = (Endianness == Endianess.BigEndian || Architecture.StartsWith("ppc")) ? "be" : "le";
1312             var libraryResource = string.Format("Antmicro.Renode.translate-{0}-{1}.so", Architecture, endianSuffix);
1313             foreach(var assembly in AppDomain.CurrentDomain.GetAssemblies())
1314             {
1315                 if(assembly.TryFromResourceToTemporaryFile(libraryResource, out libraryFile, $"{CpuCounter}-{libraryResource}"))
1316                 {
1317                     break;
1318                 }
1319             }
1320 
1321             Interlocked.Increment(ref CpuCounter);
1322 
1323             if(libraryFile == null)
1324             {
1325                 throw new ConstructionException($"Cannot find library {libraryResource}");
1326             }
1327 
1328             binder = new NativeBinder(this, libraryFile);
1329             MaximumBlockSize = DefaultMaximumBlockSize;
1330 
1331             // Need to call these before initializing TCG, so to save us an immediate TB flush
1332             // Note, that there is an additional hard limit within the translation library itself,
1333             // that can prevent setting the new size of translation cache, with a warning log
1334             var translationCacheSizeMin = (ulong)ConfigurationManager.Instance.Get("translation", "min-tb-size", DefaultMinimumTranslationCacheSize);
1335             var translationCacheSizeMax = (ulong)ConfigurationManager.Instance.Get("translation", "max-tb-size", DefaultMaximumTranslationCacheSize);
1336             TlibSetTranslationCacheConfiguration(translationCacheSizeMin, translationCacheSizeMax);
1337 
1338             var result = TlibInit(Model);
1339             if(result == -1)
1340             {
1341                 throw new ConstructionException("Unknown CPU type");
1342             }
1343             if(cpuState != null)
1344             {
1345                 var statePtr = TlibExportState();
1346                 Marshal.Copy(cpuState, 0, statePtr, cpuState.Length);
1347                 AfterLoad(statePtr);
1348             }
1349             if(machine != null)
1350             {
1351                 atomicId = TlibAtomicMemoryStateInit(machine.AtomicMemoryStatePointer, atomicId);
1352                 if(atomicId == -1)
1353                 {
1354                     throw new ConstructionException("Failed to initialize atomic state, see the log for details");
1355                 }
1356             }
1357             HandleRamSetup();
1358             foreach(var hook in hooks)
1359             {
1360                 TlibAddBreakpoint(hook.Key);
1361             }
1362             CyclesPerInstruction = 1;
1363         }
1364 
1365         public override ulong SkipInstructions
1366         {
1367             get => base.SkipInstructions;
1368             protected set
1369             {
1370                 if(!OnPossessedThread)
1371                 {
1372                     this.Log(LogLevel.Error, "Changing SkipInstructions should be only done on CPU thread, ignoring");
1373                     return;
1374                 }
1375 
1376                 base.SkipInstructions = value;
1377                 // This will be imprecise when we change SkipInstructions before end of the translation block
1378                 // as TlibSetReturnRequest doesn't finish current translation block
1379                 TlibSetReturnRequest();
1380             }
1381         }
1382 
1383         [Transient]
1384         private TranslationBlockFetchCallback onTranslationBlockFetch;
1385         private byte[] cpuState;
1386 
1387         /// <summary>
1388         /// <see cref="atomicId" /> acts as a binder between the CPU and atomic state.
1389         /// It's used to restore the atomic state after deserialization
1390         /// </summary>
1391         private int atomicId;
1392 
1393         [Transient]
1394         private string libraryFile;
1395 
1396         [Transient]
1397         private SimpleMemoryManager memoryManager;
1398 
TranslationBlockFetchCallback(ulong pc)1399         private delegate void TranslationBlockFetchCallback(ulong pc);
1400 
1401         public uint IRQ{ get { return TlibIsIrqSet(); } }
1402 
1403         [Export]
TouchHostBlock(ulong offset)1404         private void TouchHostBlock(ulong offset)
1405         {
1406             this.NoisyLog("Trying to find the mapping for offset 0x{0:X}.", offset);
1407             var mapping = currentMappings.FirstOrDefault(x => x.Segment.StartingOffset <= offset && offset <= x.Segment.StartingOffset + (x.Segment.Size - 1));
1408             if(mapping == null)
1409             {
1410                 throw new InvalidOperationException(string.Format("Could not find mapped segment for offset 0x{0:X}.", offset));
1411             }
1412             mapping.Segment.Touch();
1413             mapping.Touched = true;
1414             RebuildMemoryMappings();
1415         }
1416 
RebuildMemoryMappings()1417         private void RebuildMemoryMappings()
1418         {
1419             checked
1420             {
1421                 var hostBlocks = currentMappings.Where(x => x.Touched).Select(x => x.Segment)
1422                     .Select(x => new HostMemoryBlock { Start = x.StartingOffset, Size = x.Size, HostPointer = x.Pointer })
1423                     .OrderBy(x => x.HostPointer.ToInt64()).ToArray();
1424                 if(hostBlocks.Length > 0)
1425                 {
1426                     var blockBuffer = memoryManager.Allocate(new IntPtr(Marshal.SizeOf(typeof(HostMemoryBlock)) * hostBlocks.Length));
1427                     BlitArray(blockBuffer, hostBlocks.OrderBy(x => x.HostPointer.ToInt64()).Cast<dynamic>().ToArray());
1428                     RenodeSetHostBlocks(blockBuffer, hostBlocks.Length);
1429                     memoryManager.Free(blockBuffer);
1430                     this.NoisyLog("Memory mappings rebuilt, there are {0} host blocks now.", hostBlocks.Length);
1431                 }
1432             }
1433         }
1434 
BlitArray(IntPtr targetPointer, dynamic[] structures)1435         private void BlitArray(IntPtr targetPointer, dynamic[] structures)
1436         {
1437             var count = structures.Count();
1438             if(count == 0)
1439             {
1440                 return;
1441             }
1442             var structureSize = Marshal.SizeOf(structures.First());
1443             var currentPtr = targetPointer;
1444             for(var i = 0; i < count; i++)
1445             {
1446                 Marshal.StructureToPtr(structures[i], currentPtr + i*structureSize, false);
1447             }
1448         }
1449 
1450         [Export]
InvalidateTbInOtherCpus(IntPtr start, IntPtr end)1451         private void InvalidateTbInOtherCpus(IntPtr start, IntPtr end)
1452         {
1453             var otherCpus = machine.SystemBus.GetCPUs().OfType<TranslationCPU>().Where(x => x != this);
1454             foreach(var cpu in otherCpus)
1455             {
1456                 cpu.InvalidateTranslationBlocks(start, end);
1457             }
1458         }
1459 
ObtainPauseGuardForReading(ulong address, SysbusAccessWidth width)1460         private CpuThreadPauseGuard ObtainPauseGuardForReading(ulong address, SysbusAccessWidth width)
1461         {
1462             pauseGuard.InitializeForReading(address, width);
1463             return pauseGuard;
1464         }
1465 
ObtainPauseGuardForWriting(ulong address, SysbusAccessWidth width, ulong value)1466         private CpuThreadPauseGuard ObtainPauseGuardForWriting(ulong address, SysbusAccessWidth width, ulong value)
1467         {
1468             pauseGuard.InitializeForWriting(address, width, value);
1469             return pauseGuard;
1470         }
1471 
1472         /// <remarks>
1473         /// Be careful when using this method - the PauseGuard is used to verify if the precise pause is possible in a given context
1474         /// and as such, we should only obtain the guard when we for certain know it is.
1475         /// For example, precise pause is always possible if called from CPU loop in tlib
1476         /// </remarks>
ObtainGenericPauseGuard()1477         protected CpuThreadPauseGuard ObtainGenericPauseGuard()
1478         {
1479             pauseGuard.Initialize();
1480             return pauseGuard;
1481         }
1482 
1483         #region Memory trampolines
1484 
1485         [Export]
Allocate(IntPtr size)1486         private IntPtr Allocate(IntPtr size)
1487         {
1488             return memoryManager.Allocate(size);
1489         }
1490 
1491         [Export]
Reallocate(IntPtr oldPointer, IntPtr newSize)1492         private IntPtr Reallocate(IntPtr oldPointer, IntPtr newSize)
1493         {
1494             return memoryManager.Reallocate(oldPointer, newSize);
1495         }
1496 
1497         [Export]
Free(IntPtr pointer)1498         protected void Free(IntPtr pointer)
1499         {
1500             memoryManager.Free(pointer);
1501         }
1502 
1503         #endregion
1504 
1505         private Action<ulong, uint> blockBeginInternalHook;
1506         private Action<ulong, uint> blockBeginUserHook;
1507         private Action<ulong, uint> blockFinishedHook;
1508         private Action<ulong> interruptBeginHook;
1509         private Action<ulong> interruptEndHook;
1510         private Action<ulong, AccessType, int> mmuFaultHook;
1511         private Action<ulong, MemoryOperation, ulong, ulong, ulong> memoryAccessHook;
1512         private Action<bool> wfiStateChangeHook;
1513 
1514         private List<SegmentMapping> currentMappings;
1515 
1516         private readonly MinimalRangesCollection disabledMemory = new MinimalRangesCollection();
1517         private readonly MinimalRangesCollection mappedMemory = new MinimalRangesCollection();
1518         private readonly CpuThreadPauseGuard pauseGuard;
1519 
1520         [Transient]
1521         private NativeBinder binder;
1522 
1523         private class SimpleMemoryManager
1524         {
SimpleMemoryManager(TranslationCPU parent)1525             public SimpleMemoryManager(TranslationCPU parent)
1526             {
1527                 this.parent = parent;
1528                 ourPointers = new ConcurrentDictionary<IntPtr, long>();
1529             }
1530 
Allocate(IntPtr size)1531             public IntPtr Allocate(IntPtr size)
1532             {
1533                 var ptr = Marshal.AllocHGlobal(size);
1534                 var sizeNormalized = Misc.NormalizeBinary((double)size);
1535                 if(!ourPointers.TryAdd(ptr, (long)size))
1536                 {
1537                     throw new InvalidOperationException($"Trying to allocate a {sizeNormalized}B pointer that already exists is the memory database.");
1538                 }
1539                 Interlocked.Add(ref allocated, (long)size);
1540                 parent.NoisyLog("Allocated {0}B pointer at 0x{1:X}.", sizeNormalized, ptr);
1541                 PrintAllocated();
1542                 return ptr;
1543             }
1544 
Reallocate(IntPtr oldPointer, IntPtr newSize)1545             public IntPtr Reallocate(IntPtr oldPointer, IntPtr newSize)
1546             {
1547                 if(oldPointer == IntPtr.Zero)
1548                 {
1549                     return Allocate(newSize);
1550                 }
1551                 if(newSize == IntPtr.Zero)
1552                 {
1553                     Free(oldPointer);
1554                     return IntPtr.Zero;
1555                 }
1556                 if(!ourPointers.TryRemove(oldPointer, out var oldSize))
1557                 {
1558                     throw new InvalidOperationException($"Trying to reallocate a pointer at 0x{oldPointer:X} which wasn't allocated by this memory manager.");
1559                 }
1560                 var ptr = Marshal.ReAllocHGlobal(oldPointer, newSize);
1561                 parent.NoisyLog("Reallocated a pointer: old size {0}B at 0x{1:X}, new size {2}B at 0x{3:X}.", Misc.NormalizeBinary(oldSize), oldPointer, Misc.NormalizeBinary((double)newSize), ptr);
1562                 Interlocked.Add(ref allocated, (long)newSize - oldSize);
1563                 ourPointers.TryAdd(ptr, (long)newSize);
1564                 return ptr;
1565             }
1566 
Free(IntPtr ptr)1567             public void Free(IntPtr ptr)
1568             {
1569                 if(!ourPointers.TryRemove(ptr, out var oldSize))
1570                 {
1571                     throw new InvalidOperationException($"Trying to free a pointer at 0x{ptr:X} which wasn't allocated by this memory manager.");
1572                 }
1573                 parent.NoisyLog("Deallocated a {0}B pointer at 0x{1:X}.", Misc.NormalizeBinary(oldSize), ptr);
1574                 Marshal.FreeHGlobal(ptr);
1575                 Interlocked.Add(ref allocated, -oldSize);
1576             }
1577 
1578             public long Allocated
1579             {
1580                 get
1581                 {
1582                     return allocated;
1583                 }
1584             }
1585 
CheckIfAllIsFreed()1586             public void CheckIfAllIsFreed()
1587             {
1588                 if(!ourPointers.IsEmpty)
1589                 {
1590                     parent.Log(LogLevel.Warning, "Some memory allocated by the translation library was not freed - {0}B left allocated. This might indicate a memory leak. Cleaning up...", Misc.NormalizeBinary(allocated));
1591                     foreach(var ptr in ourPointers.Keys)
1592                     {
1593                         Marshal.FreeHGlobal(ptr);
1594                     }
1595                 }
1596             }
1597 
PrintAllocated()1598             private void PrintAllocated()
1599             {
1600                 parent.NoisyLog("Allocated is now {0}B.", Misc.NormalizeBinary(Interlocked.Read(ref allocated)));
1601             }
1602 
1603             private ConcurrentDictionary<IntPtr, long> ourPointers;
1604             private long allocated;
1605             private readonly TranslationCPU parent;
1606         }
1607 
1608         protected sealed class CpuThreadPauseGuard : IDisposable
1609         {
CpuThreadPauseGuard(TranslationCPU parent)1610             public CpuThreadPauseGuard(TranslationCPU parent)
1611             {
1612                 guard = new ThreadLocal<object>();
1613                 this.parent = parent;
1614             }
1615 
Enter()1616             public void Enter()
1617             {
1618                 active = true;
1619             }
1620 
Leave()1621             public void Leave()
1622             {
1623                 active = false;
1624             }
1625 
Initialize()1626             public void Initialize()
1627             {
1628                 guard.Value = new object();
1629             }
1630 
InitializeForWriting(ulong address, SysbusAccessWidth width, ulong value)1631             public void InitializeForWriting(ulong address, SysbusAccessWidth width, ulong value)
1632             {
1633                 InterruptTransaction = !ExecuteWatchpoints(address, width, value);
1634             }
1635 
InitializeForReading(ulong address, SysbusAccessWidth width)1636             public void InitializeForReading(ulong address, SysbusAccessWidth width)
1637             {
1638                 InterruptTransaction = !ExecuteWatchpoints(address, width, null);
1639             }
1640 
ExecuteWatchpoints(ulong address, SysbusAccessWidth width, ulong? value)1641             private bool ExecuteWatchpoints(ulong address, SysbusAccessWidth width, ulong? value)
1642             {
1643                 Initialize();
1644                 if(!parent.machine.SystemBus.TryGetWatchpointsAt(address, value.HasValue ? Access.Write : Access.Read, out var watchpoints))
1645                 {
1646                     return true;
1647                 }
1648 
1649                 /*
1650                     * In general precise pause works as follows:
1651                     * - translation libraries execute an instruction that reads/writes to/from memory
1652                     * - the execution is then transferred to the system bus (to process memory access)
1653                     * - we check whether there are any hooks registered for the accessed address (TryGetWatchpointsAt)
1654                     * - if there are (and we hit them for the first time) we call them and then invalidate the block and issue retranslation of the code at current PC
1655                     * - we exit the cpu loop so that newly translated block will be executed now
1656                     * - the next time we hit them we do nothing
1657                 */
1658 
1659                 var anyEnabled = false;
1660                 var alreadyUpdated = false;
1661                 foreach(var enabledWatchpoint in watchpoints.Where(x => x.Enabled))
1662                 {
1663                     enabledWatchpoint.Enabled = false;
1664                     if(!alreadyUpdated && parent.UpdateContextOnLoadAndStore)
1665                     {
1666                         parent.TlibRestoreContext();
1667                         alreadyUpdated = true;
1668                     }
1669 
1670                     // for reading value is always set to 0
1671                     enabledWatchpoint.Invoke(parent, address, width, value ?? 0);
1672                     anyEnabled = true;
1673                 }
1674 
1675                 if(anyEnabled)
1676                 {
1677                     parent.TlibRequestTranslationBlockInterrupt(1);
1678 
1679                     // tell sysbus to cancel the current transaction and return immediately
1680                     return false;
1681                 }
1682                 else
1683                 {
1684                     foreach(var disabledWatchpoint in watchpoints)
1685                     {
1686                         disabledWatchpoint.Enabled = true;
1687                     }
1688                 }
1689 
1690                 return true;
1691             }
1692 
OrderPause()1693             public void OrderPause()
1694             {
1695                 if(active && guard.Value == null)
1696                 {
1697                     throw new InvalidOperationException("Trying to order pause without prior guard initialization on this thread.");
1698                 }
1699             }
1700 
RequestTranslationBlockRestart(bool quiet = false)1701             public bool RequestTranslationBlockRestart(bool quiet = false)
1702             {
1703                 if(guard.Value == null)
1704                 {
1705                     if(!quiet)
1706                     {
1707                         parent.Log(LogLevel.Error, "Trying to request translation block restart without prior guard initialization on this thread.");
1708                     }
1709                     return false;
1710                 }
1711                 restartTranslationBlock = true;
1712                 return true;
1713             }
1714 
IDisposable.Dispose()1715             void IDisposable.Dispose()
1716             {
1717                 if(restartTranslationBlock)
1718                 {
1719                     restartTranslationBlock = false;
1720                     if(parent.UpdateContextOnLoadAndStore)
1721                     {
1722                         parent.TlibRestoreContext();
1723                     }
1724                     parent.TlibRequestTranslationBlockInterrupt(0);
1725                     return;
1726                 }
1727                 guard.Value = null;
1728             }
1729 
1730             public bool InterruptTransaction { get; private set; }
1731 
1732             [Constructor]
1733             private readonly ThreadLocal<object> guard;
1734 
1735             private readonly TranslationCPU parent;
1736             private bool active;
1737             private bool restartTranslationBlock;
1738         }
1739 
1740         protected enum Interrupt
1741         {
1742             Hard            = 1 << 1,
1743             TargetExternal0 = 1 << 3,
1744             TargetExternal1 = 1 << 4,
1745             TargetExternal2 = 1 << 6,
1746             TargetExternal3 = 1 << 9,
1747         }
1748 
1749         [StructLayout(LayoutKind.Sequential, Pack = 1)]
1750         private struct HostMemoryBlock
1751         {
1752             public ulong Start;
1753             public ulong Size;
1754             public IntPtr HostPointer;
1755         }
1756 
1757         private bool logTranslatedBlocks;
1758         public bool LogTranslatedBlocks
1759         {
1760             get
1761             {
1762                 return logTranslatedBlocks;
1763             }
1764 
1765             set
1766             {
1767                 if(LogFile == null && value)
1768                 {
1769                     throw new RecoverableException("Log file not set. Nothing will be logged.");
1770                 }
1771                 logTranslatedBlocks = value;
1772                 TlibSetOnBlockTranslationEnabled(value ? 1 : 0);
1773             }
1774         }
1775 
1776         /// <summary>
1777         /// Translates a logical (virtual) address to a physical address for the specified access type.
1778         /// </summary>
1779         /// <param name="logicalAddress">The logical (virtual) address to be translated.</param>
1780         /// <param name="accessType">The type of access (read, write, fetch), represented as an <see cref="MpuAccess"/> value.</param>
1781         /// <returns>
1782         /// The translated physical address if the translation was successful; otherwise, <c>ulong.MaxValue</c>.
1783         /// </returns>
TranslateAddress(ulong logicalAddress, MpuAccess accessType)1784         public ulong TranslateAddress(ulong logicalAddress, MpuAccess accessType)
1785         {
1786             return TlibTranslateToPhysicalAddress(logicalAddress, (uint)accessType);
1787         }
1788 
1789         /// <summary>
1790         /// Attempts to translate a logical (virtual) address to a physical address for the specified access type.
1791         /// </summary>
1792         /// <param name="logicalAddress">The logical (virtual) address to be translated.</param>
1793         /// <param name="accessType">The type of access (read, write, fetch), represented as an <see cref="MpuAccess"/> value.</param>
1794         /// <param name="physicalAddress">At return, contains the translated physical address if the translation is successful.
1795         /// If there is no page table entry for the requested logical address, this output will contain the original logical address.
1796         /// </param>
1797         /// <returns>
1798         /// <c>true</c> if the translation was successful; otherwise, <c>false</c>. In this case the result is the original address.
1799         /// </returns>
TryTranslateAddress(ulong logicalAddress, MpuAccess accessType, out ulong physicalAddress)1800         public bool TryTranslateAddress(ulong logicalAddress, MpuAccess accessType, out ulong physicalAddress)
1801         {
1802             var result = TranslateAddress(logicalAddress, accessType);
1803             if(result == ulong.MaxValue) // No translation
1804             {
1805                 physicalAddress = logicalAddress;
1806                 return false;
1807             }
1808             physicalAddress = result;
1809             return true;
1810         }
1811 
NativeUnwind()1812         public void NativeUnwind()
1813         {
1814             TlibUnwind();
1815         }
1816 
1817         [PostDeserialization]
InitDisas()1818         protected void InitDisas()
1819         {
1820             try
1821             {
1822                 disassembler = new LLVMDisassembler(this);
1823             }
1824             catch(ArgumentOutOfRangeException)
1825             {
1826                 this.Log(LogLevel.Warning, "Could not initialize disassembly engine");
1827             }
1828             try
1829             {
1830                 assembler = new LLVMAssembler(this);
1831             }
1832             catch(ArgumentOutOfRangeException)
1833             {
1834                 this.Log(LogLevel.Warning, "Could not initialize assembly engine");
1835             }
1836             dirtyAddressesPtr = IntPtr.Zero;
1837         }
1838 
1839         public uint PageSize
1840         {
1841             get
1842             {
1843                 return TlibGetPageSize();
1844             }
1845         }
1846 
BeforeSave(IntPtr statePtr)1847         protected virtual void BeforeSave(IntPtr statePtr)
1848         {
1849             TlibBeforeSave(statePtr);
1850         }
1851 
AfterLoad(IntPtr statePtr)1852         protected virtual void AfterLoad(IntPtr statePtr)
1853         {
1854             TlibAfterLoad(statePtr);
1855         }
1856 
GetCPUStateForMemoryTransaction()1857         protected ulong GetCPUStateForMemoryTransaction()
1858         {
1859             return TlibGetCpuStateForMemoryTransaction();
1860         }
1861 
1862         [Export]
IsInDebugMode()1863         private uint IsInDebugMode()
1864         {
1865             return InDebugMode ? 1u : 0u;
1866         }
1867 
UpdateBlockBeginHookPresent()1868         private void UpdateBlockBeginHookPresent()
1869         {
1870             TlibSetBlockBeginHookPresent((blockBeginInternalHook != null || blockBeginUserHook != null || IsSingleStepMode || isAnyInactiveHook) ? 1u : 0u);
1871         }
1872 
EnableReadCache(ulong accessAddress, ulong lowerAccessCount, ulong upperAccessCount = 0)1873         public void EnableReadCache(ulong accessAddress, ulong lowerAccessCount, ulong upperAccessCount = 0)
1874         {
1875             if(lowerAccessCount == 0)
1876             {
1877                 throw new RecoverableException("Lower access count to address cannot be zero!");
1878             }
1879             if((upperAccessCount != 0) && ((upperAccessCount <= lowerAccessCount)))
1880             {
1881                 throw new RecoverableException("Upper access count to address has to be bigger than lower access count!");
1882             }
1883             TlibEnableReadCache(accessAddress, lowerAccessCount, upperAccessCount);
1884         }
1885 
1886         // 649:  Field '...' is never assigned to, and will always have its default value null
1887 #pragma warning disable 649
1888 
1889         [Import]
1890         private Action<ulong, ulong, ulong> TlibEnableReadCache;
1891 
1892         [Import]
1893         private Action<uint> TlibSetChainingEnabled;
1894 
1895         [Import]
1896         private Func<uint> TlibGetChainingEnabled;
1897 
1898         [Import]
1899         private Action<uint> TlibSetTbCacheEnabled;
1900 
1901         [Import]
1902         private Func<uint> TlibGetTbCacheEnabled;
1903 
1904         [Import]
1905         private Action<uint> TlibSetSyncPcEveryInstructionDisabled;
1906 
1907         [Import]
1908         private Func<uint> TlibGetSyncPcEveryInstructionDisabled;
1909 
1910         [Import]
1911         private Func<string, int> TlibInit;
1912 
1913         [Import]
1914         private Action TlibDispose;
1915 
1916         [Import]
1917         private Action TlibReset;
1918 
1919         [Import]
1920         private Func<int, int> TlibExecute;
1921 
1922         [Import]
1923         protected Action<int> TlibRequestTranslationBlockInterrupt;
1924 
1925         [Import]
1926         protected Action TlibSetReturnRequest;
1927 
1928         [Import]
1929         private Func<IntPtr, int, int> TlibAtomicMemoryStateInit;
1930 
1931         [Import]
1932         private Func<uint> TlibGetPageSize;
1933 
1934         [Import]
1935         private Action<ulong, ulong> TlibMapRange;
1936 
1937         [Import]
1938         private Action<ulong, ulong> TlibUnmapRange;
1939 
1940         [Import]
1941         private Action<ulong, ulong, uint> TlibRegisterAccessFlagsForRange;
1942 
1943         [Import]
1944         private Func<ulong, ulong, uint> TlibIsRangeMapped;
1945 
1946         [Import]
1947         private Action<IntPtr, IntPtr> TlibInvalidateTranslationBlocks;
1948 
1949         [Import]
1950         protected Func<ulong, uint, ulong> TlibTranslateToPhysicalAddress;
1951 
1952         [Import]
1953         private Action<IntPtr, int> RenodeSetHostBlocks;
1954 
1955         [Import]
1956         private Action RenodeFreeHostBlocks;
1957 
1958         [Import]
1959         private Action<int, int> TlibSetIrq;
1960 
1961         [Import]
1962         private Func<uint> TlibIsIrqSet;
1963 
1964         [Import]
1965         private Action<ulong> TlibAddBreakpoint;
1966 
1967         [Import]
1968         private Action<ulong> TlibRemoveBreakpoint;
1969 
1970         [Import]
1971         private Action<IntPtr> RenodeAttachLogTranslationBlockFetch;
1972 
1973         [Import]
1974         private Action<int> TlibSetOnBlockTranslationEnabled;
1975 
1976         [Import]
1977         private Action<ulong, ulong> TlibSetTranslationCacheConfiguration;
1978 
1979         [Import]
1980         private Action TlibInvalidateTranslationCache;
1981 
1982         [Import]
1983         private Func<uint, uint> TlibSetMaximumBlockSize;
1984 
1985         [Import]
1986         private Action<ulong> TlibFlushPage;
1987 
1988         [Import]
1989         private Func<uint> TlibGetMaximumBlockSize;
1990 
1991         [Import]
1992         private Action<uint> TlibSetMillicyclesPerInstruction;
1993 
1994         [Import]
1995         private Func<uint> TlibGetMillicyclesPerInstruction;
1996 
1997         [Import]
1998         private Func<int> TlibRestoreContext;
1999 
2000         [Import]
2001         private Func<IntPtr> TlibExportState;
2002 
2003         [Import]
2004         private Func<int> TlibGetStateSize;
2005 
2006         [Import]
2007         protected Func<ulong> TlibGetExecutedInstructions;
2008 
2009         [Import]
2010         private Action<uint> TlibSetBlockFinishedHookPresent;
2011 
2012         [Import]
2013         private Action<uint> TlibSetBlockBeginHookPresent;
2014 
2015         [Import]
2016         private Action<uint> TlibSetInterruptBeginHookPresent;
2017 
2018         [Import]
2019         private Action<uint> TlibSetCpuWfiStateChangeHookPresent;
2020 
2021         [Import]
2022         private Action<uint> TlibSetInterruptEndHookPresent;
2023 
2024         [Import]
2025         private Func<ulong> TlibGetTotalExecutedInstructions;
2026 
2027         [Import]
2028         private Action<int> TlibOnMemoryAccessEventEnabled;
2029 
2030         [Import]
2031         private Action TlibCleanWfiProcState;
2032 
2033         [Import]
2034         private Action<ulong> TlibSetPageIoAccessed;
2035 
2036         [Import]
2037         private Action<ulong> TlibClearPageIoAccessed;
2038 
2039         [Import]
2040         private Func<uint> TlibGetCurrentTbDisasFlags;
2041 
2042         [Import(UseExceptionWrapper = false)]
2043         private Action TlibUnwind;
2044 
2045         [Import]
2046         private Func<uint> TlibGetMmuWindowsCount;
2047 
2048         [Import]
2049         private Action<uint> TlibRaiseException;
2050 
2051         [Import]
2052         private Action<uint> TlibEnableExternalWindowMmu;
2053 
2054         [Import]
2055         private Func<uint, int> TlibAcquireMmuWindow;
2056 
2057         [Import]
2058         private Action<uint> TlibResetMmuWindow;
2059 
2060         [Import]
2061         private Action<uint, ulong> TlibSetMmuWindowStart;
2062 
2063         [Import]
2064         private Action<uint, ulong, uint> TlibSetMmuWindowEnd;
2065 
2066         [Import]
2067         private Action<uint, uint> TlibSetWindowPrivileges;
2068 
2069         [Import]
2070         private Action<uint, ulong> TlibSetMmuWindowAddend;
2071 
2072         [Import]
2073         private Func<uint, ulong> TlibGetMmuWindowStart;
2074 
2075         [Import]
2076         private Func<uint, ulong> TlibGetMmuWindowEnd;
2077 
2078         [Import]
2079         private Func<uint, uint> TlibGetWindowPrivileges;
2080 
2081         [Import]
2082         private Func<uint, ulong> TlibGetMmuWindowAddend;
2083 
2084         [Import]
2085         private Action<int> TlibSetBroadcastDirty;
2086 
2087         [Import]
2088         private Action TlibOnLeavingResetState;
2089 
2090         [Import]
2091         private Action<IntPtr> TlibBeforeSave;
2092 
2093         [Import]
2094         private Action<IntPtr> TlibAfterLoad;
2095 
2096         [Import(UseExceptionWrapper = false)] // Not wrapped for performance
2097         private Func<ulong> TlibGetCpuStateForMemoryTransaction;
2098 
2099 #pragma warning restore 649
2100 
2101         [Export]
LogAsCpu(int level, string s)2102         protected virtual void LogAsCpu(int level, string s)
2103         {
2104             this.Log((LogLevel)level, s);
2105         }
2106 
2107         [Export]
LogDisassembly(ulong pc, uint size, uint flags)2108         private void LogDisassembly(ulong pc, uint size, uint flags)
2109         {
2110             if(LogFile == null)
2111             {
2112                 return;
2113             }
2114             if(Disassembler == null)
2115             {
2116                 return;
2117             }
2118 
2119             var phy = TranslateAddress(pc, MpuAccess.InstructionFetch);
2120             var symbol = Bus.FindSymbolAt(pc, this);
2121             var tab = Bus.ReadBytes(phy, (int)size, true, context: this);
2122             Disassembler.DisassembleBlock(pc, tab, flags, out var disas);
2123 
2124             if(disas == null)
2125             {
2126                 return;
2127             }
2128 
2129             using(var file = File.AppendText(LogFile))
2130             {
2131                 file.WriteLine("-------------------------");
2132                 if(size > 0)
2133                 {
2134                     file.Write("IN: {0} ", symbol ?? string.Empty);
2135                     if(phy != pc)
2136                     {
2137                         file.WriteLine("(physical: 0x{0:x8}, virtual: 0x{1:x8})", phy, pc);
2138                     }
2139                     else
2140                     {
2141                         file.WriteLine("(address: 0x{0:x8})", phy);
2142                     }
2143                 }
2144                 else
2145                 {
2146                     // special case when disassembling magic addresses in Cortex-M
2147                     file.WriteLine("Magic PC value detected: 0x{0:x8}", flags > 0 ? pc | 1 : pc);
2148                 }
2149 
2150                 file.WriteLine(string.IsNullOrWhiteSpace(disas) ? string.Format("Cannot disassemble from 0x{0:x8} to 0x{1:x8}", pc, pc + size)  : disas);
2151                 file.WriteLine(string.Empty);
2152             }
2153         }
2154 
2155         /// <summary>
2156         /// See <see cref="CPUCore.MultiprocessingId" /> for explanation on how this property should be interpreted and used.
2157         /// Here, we can propagate this value to translation library, e.g. so it can be reflected in CPU's registers
2158         /// </summary>
2159         [Export]
GetMpIndex()2160         private uint GetMpIndex()
2161         {
2162             return MultiprocessingId;
2163         }
2164 
DisassembleBlock(ulong addr = ulong.MaxValue, uint blockSize = 40, uint flags = 0)2165         public string DisassembleBlock(ulong addr = ulong.MaxValue, uint blockSize = 40, uint flags = 0)
2166         {
2167             if(Disassembler == null)
2168             {
2169                 throw new RecoverableException("Disassembly engine not available");
2170             }
2171             if(addr == ulong.MaxValue)
2172             {
2173                 addr = PC;
2174             }
2175 
2176             // Instruction fetch access used as we want to be able to read even pages mapped for execution only
2177             // We don't care if translation fails here (the address is unchanged in this case)
2178             TryTranslateAddress(addr, MpuAccess.InstructionFetch, out addr);
2179 
2180             var opcodes = Bus.ReadBytes(addr, (int)blockSize, true, context: this);
2181             Disassembler.DisassembleBlock(addr, opcodes, flags, out var result);
2182             return result;
2183         }
2184 
AssembleBlock(ulong addr, string instructions, uint flags = 0)2185         public uint AssembleBlock(ulong addr, string instructions, uint flags = 0)
2186         {
2187             if(Assembler == null)
2188             {
2189                 throw new RecoverableException("Assembler not available");
2190             }
2191 
2192             // Instruction fetch access used as we want to be able to write even pages mapped for execution only
2193             // We don't care if translation fails here (the address is unchanged in this case)
2194             TryTranslateAddress(addr, MpuAccess.InstructionFetch, out addr);
2195 
2196             var result = Assembler.AssembleBlock(addr, instructions, flags);
2197             Bus.WriteBytes(result, addr, true, context: this);
2198             return (uint)result.Length;
2199         }
2200 
2201         [Transient]
2202         private LLVMDisassembler disassembler;
2203         [Transient]
2204         private LLVMAssembler assembler;
2205 
2206         public LLVMDisassembler Disassembler => disassembler;
2207         public LLVMAssembler Assembler => assembler;
2208 
2209         protected static readonly Exception InvalidInterruptNumberException = new InvalidOperationException("Invalid interrupt number.");
2210 
2211         private const int DefaultMaximumBlockSize = 0x7FF;
2212         private const int DefaultMinimumTranslationCacheSize = 32 * 1024 * 1024; // 32 MiB
2213         private const int DefaultMaximumTranslationCacheSize = 512 * 1024 * 1024; // 512 MiB
2214         private bool externalMmuEnabled;
2215         private readonly uint externalMmuWindowsCount;
2216 
ExecuteHooks(ulong address)2217         private void ExecuteHooks(ulong address)
2218         {
2219             lock(hooks)
2220             {
2221                 HookDescriptor hookDescriptor;
2222                 if(!hooks.TryGetValue(address, out hookDescriptor))
2223                 {
2224                     return;
2225                 }
2226 
2227                 this.DebugLog("Executing hooks registered at address 0x{0:X8}", address);
2228                 hookDescriptor.ExecuteCallbacks();
2229             }
2230         }
2231 
DeactivateHooks(ulong address)2232         private void DeactivateHooks(ulong address)
2233         {
2234             lock(hooks)
2235             {
2236                 HookDescriptor hookDescriptor;
2237                 if(!hooks.TryGetValue(address, out hookDescriptor))
2238                 {
2239                     return;
2240                 }
2241                 hookDescriptor.Deactivate();
2242                 isAnyInactiveHook = true;
2243                 UpdateBlockBeginHookPresent();
2244             }
2245         }
2246 
ReactivateHooks()2247         private void ReactivateHooks()
2248         {
2249             lock(hooks)
2250             {
2251                 foreach(var inactive in hooks.Where(x => !x.Value.IsActive))
2252                 {
2253                     inactive.Value.Activate();
2254                 }
2255                 isAnyInactiveHook = false;
2256                 UpdateBlockBeginHookPresent();
2257             }
2258         }
2259 
ActivateNewHooks()2260         public void ActivateNewHooks()
2261         {
2262             lock(hooks)
2263             {
2264                 foreach(var newHook in hooks.Where(x => x.Value.IsNew))
2265                 {
2266                     newHook.Value.Activate();
2267                 }
2268             }
2269         }
2270 
RemoveAllHooks()2271         public void RemoveAllHooks()
2272         {
2273             lock(hooks)
2274             {
2275                 foreach(var hook in hooks)
2276                 {
2277                     TlibRemoveBreakpoint(hook.Key);
2278                 }
2279                 hooks.Clear();
2280                 isAnyInactiveHook = false;
2281                 UpdateBlockBeginHookPresent();
2282             }
2283         }
2284 
EnableProfiling()2285         public void EnableProfiling()
2286         {
2287             AddHookAtInterruptBegin(exceptionIndex =>
2288             {
2289                 machine.Profiler.Log(new ExceptionEntry(exceptionIndex));
2290             });
2291 
2292             SetHookAtMemoryAccess((_, operation, __, physicalAddress, value) =>
2293             {
2294                 switch(operation)
2295                 {
2296                     case MemoryOperation.MemoryIORead:
2297                     case MemoryOperation.MemoryIOWrite:
2298                         machine.Profiler?.Log(new PeripheralEntry((byte)operation, physicalAddress));
2299                         break;
2300                     case MemoryOperation.MemoryRead:
2301                     case MemoryOperation.MemoryWrite:
2302                         machine.Profiler?.Log(new MemoryEntry((byte)operation));
2303                         break;
2304                 }
2305             });
2306         }
2307 
ExecutionFinished(ExecutionResult result)2308         protected override bool ExecutionFinished(ExecutionResult result)
2309         {
2310             if(result == ExecutionResult.StoppedAtBreakpoint)
2311             {
2312                 this.Trace();
2313                 ExecuteHooks(PC);
2314                 // it is necessary to deactivate hooks installed on this PC before
2315                 // calling `tlib_execute` again to avoid a loop;
2316                 // we need to do this because creating a breakpoint has caused special
2317                 // exception-rising, block-breaking `trap` instruction to be
2318                 // generated by the tcg;
2319                 // in order to execute code after the breakpoint we must first remove
2320                 // this `trap` and retranslate the code right after it;
2321                 // this is achieved by deactivating the breakpoint (i.e., unregistering
2322                 // from tlib, but keeping it in C#), executing the beginning of the next
2323                 // block and registering the breakpoint again in the OnBlockBegin hook
2324                 DeactivateHooks(PC);
2325                 return true;
2326             }
2327             else if(result == ExecutionResult.StoppedAtWatchpoint)
2328             {
2329                 this.Trace();
2330                 // If we stopped at a watchpoint we must've been in the process
2331                 // of executing an instruction which accesses memory.
2332                 // That means that if there have been any hooks added for the current PC,
2333                 // they were already executed, and the PC has been moved back by one instruction.
2334                 // We don't want to execute them again, so we disable them temporarily.
2335                 DeactivateHooks(PC);
2336                 return true;
2337             }
2338             else if(result == ExecutionResult.WaitingForInterrupt)
2339             {
2340                 if(InDebugMode || neverWaitForInterrupt)
2341                 {
2342                     // NIP always points to the next instruction, on all emulated cores. If this behavior changes, this needs to change as well.
2343                     this.Trace("Clearing WaitForInterrupt processor state.");
2344                     TlibCleanWfiProcState(); // Clean WFI state in the emulated core
2345                     return true;
2346                 }
2347             }
2348 
2349             return false;
2350         }
2351 
TlibSetIrqWrapped(int number, bool state)2352         private void TlibSetIrqWrapped(int number, bool state)
2353         {
2354             var decodedInterrupt = DecodeInterrupt(number);
2355             if(!decodedIrqs.TryGetValue(decodedInterrupt, out var irqs))
2356             {
2357                 irqs = new HashSet<int>();
2358                 decodedIrqs.Add(decodedInterrupt, irqs);
2359             }
2360             this.Log(LogLevel.Noisy, "Setting CPU IRQ #{0} to {1}", number, state);
2361             if(state)
2362             {
2363                 irqs.Add(number);
2364                 TlibSetIrq((int)decodedInterrupt, 1);
2365             }
2366             else
2367             {
2368                 irqs.Remove(number);
2369                 if(irqs.Count == 0)
2370                 {
2371                     TlibSetIrq((int)decodedInterrupt, 0);
2372                 }
2373             }
2374         }
2375 
2376         protected enum TlibExecutionResult : ulong
2377         {
2378             Ok = 0x10000,
2379             WaitingForInterrupt = 0x10001,
2380             StoppedAtBreakpoint = 0x10002,
2381             StoppedAtWatchpoint = 0x10004,
2382             ReturnRequested = 0x10005,
2383             ExternalMmuFault = 0x10006,
2384         }
2385 
ExecuteInstructions(ulong numberOfInstructionsToExecute, out ulong numberOfExecutedInstructions)2386         public override ExecutionResult ExecuteInstructions(ulong numberOfInstructionsToExecute, out ulong numberOfExecutedInstructions)
2387         {
2388             ActivateNewHooks();
2389 
2390             try
2391             {
2392                 while(actionsToExecuteOnCpuThread.TryDequeue(out var queuedAction))
2393                 {
2394                     queuedAction();
2395                 }
2396 
2397                 pauseGuard.Enter();
2398                 lastTlibResult = (TlibExecutionResult)TlibExecute(checked((int)numberOfInstructionsToExecute));
2399                 pauseGuard.Leave();
2400             }
2401             catch(CpuAbortException)
2402             {
2403                 this.NoisyLog("CPU abort detected, halting.");
2404                 InvokeHalted(new HaltArguments(HaltReason.Abort, this));
2405                 return ExecutionResult.Aborted;
2406             }
2407             finally
2408             {
2409                 numberOfExecutedInstructions = TlibGetExecutedInstructions();
2410                 if(numberOfExecutedInstructions == 0)
2411                 {
2412                     this.Trace($"Asked tlib to execute {numberOfInstructionsToExecute}, but did nothing");
2413                 }
2414                 DebugHelper.Assert(numberOfExecutedInstructions <= numberOfInstructionsToExecute, "tlib executed more instructions than it was asked to");
2415             }
2416 
2417             switch(lastTlibResult)
2418             {
2419                 case TlibExecutionResult.Ok:
2420                     return ExecutionResult.Ok;
2421 
2422                 case TlibExecutionResult.WaitingForInterrupt:
2423                     return ExecutionResult.WaitingForInterrupt;
2424 
2425                 case TlibExecutionResult.ExternalMmuFault:
2426                     return ExecutionResult.ExternalMmuFault;
2427 
2428                 case TlibExecutionResult.StoppedAtBreakpoint:
2429                     return ExecutionResult.StoppedAtBreakpoint;
2430 
2431                 case TlibExecutionResult.StoppedAtWatchpoint:
2432                     return ExecutionResult.StoppedAtWatchpoint;
2433 
2434                 case TlibExecutionResult.ReturnRequested:
2435                     return ExecutionResult.Interrupted;
2436 
2437                 default:
2438                     throw new Exception();
2439             }
2440         }
2441 
SetBroadcastDirty(bool enable)2442         public void SetBroadcastDirty(bool enable)
2443         {
2444             TlibSetBroadcastDirty(enable ? 1 : 0);
2445         }
2446 
2447         private string logFile;
2448         private bool isAnyInactiveHook;
2449         private Dictionary<ulong, HookDescriptor> hooks;
2450         private Dictionary<Interrupt, HashSet<int>> decodedIrqs;
2451         private bool isInterruptLoggingEnabled;
2452 
2453         private class HookDescriptor
2454         {
HookDescriptor(TranslationCPU cpu, ulong address)2455             public HookDescriptor(TranslationCPU cpu, ulong address)
2456             {
2457                 this.cpu = cpu;
2458                 this.address = address;
2459                 callbacks = new HashSet<Action<ICpuSupportingGdb, ulong>>();
2460                 IsNew = true;
2461             }
2462 
ExecuteCallbacks()2463             public void ExecuteCallbacks()
2464             {
2465                 // As hooks can be removed inside the callback, .ToList()
2466                 // is required to avoid _Collection was modified_ exception.
2467                 foreach(var callback in callbacks.ToList())
2468                 {
2469                     callback(cpu, address);
2470                 }
2471             }
2472 
AddCallback(Action<ICpuSupportingGdb, ulong> action)2473             public void AddCallback(Action<ICpuSupportingGdb, ulong> action)
2474             {
2475                 callbacks.Add(action);
2476             }
2477 
RemoveCallback(Action<ICpuSupportingGdb, ulong> action)2478             public bool RemoveCallback(Action<ICpuSupportingGdb, ulong> action)
2479             {
2480                 var result = callbacks.Remove(action);
2481                 if(result && IsEmpty)
2482                 {
2483                     Deactivate();
2484                 }
2485                 return result;
2486             }
2487 
2488             /// <summary>
2489             /// Activates the hook by installing it in tlib.
2490             /// </summary>
Activate()2491             public void Activate()
2492             {
2493                 if(IsActive)
2494                 {
2495                     return;
2496                 }
2497 
2498                 cpu.TlibAddBreakpoint(address);
2499                 IsActive = true;
2500                 IsNew = false;
2501             }
2502 
2503             /// <summary>
2504             /// Deactivates the hook by removing it from tlib.
2505             /// </summary>
Deactivate()2506             public void Deactivate()
2507             {
2508                 if(!IsActive)
2509                 {
2510                     return;
2511                 }
2512 
2513                 cpu.TlibRemoveBreakpoint(address);
2514                 IsActive = false;
2515             }
2516 
2517             public bool IsEmpty { get { return !callbacks.Any(); } }
2518             public bool IsActive { get; private set; }
2519             public bool IsNew { get; private set; }
2520 
2521             private readonly ulong address;
2522             private readonly TranslationCPU cpu;
2523             private readonly HashSet<Action<ICpuSupportingGdb, ulong>> callbacks;
2524         }
2525     }
2526 }
2527 
2528