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.Linq;
10 using System.Globalization;
11 using System.Collections.Generic;
12 using Antmicro.Renode.Core;
13 using Antmicro.Renode.Core.Structure;
14 using Antmicro.Renode.Exceptions;
15 using Antmicro.Renode.Logging;
16 using Antmicro.Renode.Peripherals.Bus.Wrappers;
17 using Antmicro.Renode.Peripherals.CPU;
18 using Antmicro.Renode.Utilities;
19 using System.Threading;
20 using System.Collections.ObjectModel;
21 using System.Text;
22 using Machine = Antmicro.Renode.Core.Machine;
23 using Antmicro.Migrant;
24 using Antmicro.Migrant.Hooks;
25 using ELFSharp.ELF;
26 using ELFSharp.ELF.Segments;
27 using ELFSharp.UImage;
28 using System.IO;
29 using Antmicro.Renode.Core.Extensions;
30 using System.Reflection;
31 using Antmicro.Renode.UserInterface;
32 using Antmicro.Renode.Peripherals.Memory;
33 
34 using Range = Antmicro.Renode.Core.Range;
35 
36 namespace Antmicro.Renode.Peripherals.Bus
37 {
38     /// <summary>
39     ///     The <c>SystemBus</c> is the main system class, where all data passes through.
40     /// </summary>
41     [Icon("sysbus")]
42     [ControllerMask(typeof(IPeripheral))]
43     public sealed partial class SystemBus : IBusController, IDisposable
44     {
SystemBus(IMachine machine)45         internal SystemBus(IMachine machine)
46         {
47             this.Machine = machine;
48             cpuSync = new object();
49             binaryFingerprints = new List<BinaryFingerprint>();
50             cpuById = new Dictionary<int, ICPU>();
51             idByCpu = new Dictionary<ICPU, int>();
52             hooksOnRead = new Dictionary<ulong, List<BusHookHandler>>();
53             hooksOnWrite = new Dictionary<ulong, List<BusHookHandler>>();
54             pcCache.OnChanged += HandleChangedSymbols;
55             InitStructures();
56             this.Log(LogLevel.Info, "System bus created.");
57         }
58 
LoadFileChunks(string path, IEnumerable<FileChunk> chunks, ICPU cpu)59         public void LoadFileChunks(string path, IEnumerable<FileChunk> chunks, ICPU cpu)
60         {
61             var minAddr = this.LoadFileChunks(chunks, cpu);
62             AddFingerprint(path);
63             UpdateLowestLoadedAddress(minAddr);
64             this.DebugLog(path + " File loaded.");
65         }
66 
Unregister(IBusPeripheral peripheral)67         public void Unregister(IBusPeripheral peripheral)
68         {
69             using(Machine.ObtainPausedState(true))
70             {
71                 Machine.UnregisterAsAChildOf(this, peripheral);
72                 UnregisterInner(peripheral);
73             }
74         }
75 
Unregister(IBusRegistered<IBusPeripheral> busRegisteredPeripheral)76         public void Unregister(IBusRegistered<IBusPeripheral> busRegisteredPeripheral)
77         {
78             using(Machine.ObtainPausedState(true))
79             {
80                 Machine.UnregisterAsAChildOf(this, busRegisteredPeripheral.RegistrationPoint);
81                 UnregisterInner(busRegisteredPeripheral);
82             }
83         }
84 
Unregister(IPeripheral peripheral)85         public void Unregister(IPeripheral peripheral)
86         {
87             using(Machine.ObtainPausedState(true))
88             {
89                 Machine.UnregisterAsAChildOf(this, peripheral);
90                 RemoveContextKeys(peripheral);
91             }
92         }
93 
Register(IPeripheral peripheral, NullRegistrationPoint registrationPoint)94         public void Register(IPeripheral peripheral, NullRegistrationPoint registrationPoint)
95         {
96             using(Machine.ObtainPausedState(true))
97             {
98                 // NullRegistrationPoint peripherals are not mapped on the bus and
99                 // are not directly accessible from the emulated software.
100                 Machine.RegisterAsAChildOf(this, peripheral, registrationPoint);
101             }
102         }
103 
Register(IBusPeripheral peripheral, BusRangeRegistration registrationPoint)104         public void Register(IBusPeripheral peripheral, BusRangeRegistration registrationPoint)
105         {
106             var methods = PeripheralAccessMethods.CreateWithLock();
107             if(registrationPoint is BusParametrizedRegistration parametrizedRegistrationPoint)
108             {
109                 parametrizedRegistrationPoint.RegisterForEachContext((contextRegistration) =>
110                 {
111                     // Prepare accessor methods in the context of registration,
112                     // as it may want to fill them according to the CPU context.
113                     methods = PeripheralAccessMethods.CreateWithLock();
114                     contextRegistration.FillAccessMethods(peripheral, ref methods);
115                     FillAccessMethodsWithDefaultMethods(peripheral, ref methods);
116                     RegisterInner(peripheral, methods, contextRegistration, context: contextRegistration.Initiator);
117                 });
118             }
119             else if(registrationPoint is BusMultiRegistration multiRegistrationPoint)
120             {
121                 if(peripheral is IMapped)
122                 {
123                     throw new ConstructionException(string.Format("It is not allowed to register `{0}` peripheral using `{1}`", typeof(IMapped).Name, typeof(BusMultiRegistration).Name));
124                 }
125                 FillAccessMethodsWithTaggedMethods(peripheral, multiRegistrationPoint.ConnectionRegionName, ref methods);
126                 multiRegistrationPoint.RegisterForEachContext((contextRegistration) => RegisterInner(peripheral, methods, contextRegistration, context: contextRegistration.Initiator));
127             }
128             else
129             {
130                 FillAccessMethodsWithDefaultMethods(peripheral, ref methods);
131                 registrationPoint.RegisterForEachContext((contextRegistration) => RegisterInner(peripheral, methods, contextRegistration, context: contextRegistration.Initiator));
132             }
133         }
134 
Register(IBusPeripheral peripheral, BusMultiRegistration registrationPoint)135         public void Register(IBusPeripheral peripheral, BusMultiRegistration registrationPoint)
136         {
137             Register(peripheral, (BusRangeRegistration)registrationPoint);
138         }
139 
Register(IBusPeripheral peripheral, BusParametrizedRegistration registrationPoint)140         public void Register(IBusPeripheral peripheral, BusParametrizedRegistration registrationPoint)
141         {
142             Register(peripheral, (BusRangeRegistration)registrationPoint);
143         }
144 
Unregister(IBusPeripheral peripheral)145         void IPeripheralRegister<IBusPeripheral, BusMultiRegistration>.Unregister(IBusPeripheral peripheral)
146         {
147             Unregister(peripheral);
148         }
149 
Register(IKnownSize peripheral, BusPointRegistration registrationPoint)150         public void Register(IKnownSize peripheral, BusPointRegistration registrationPoint)
151         {
152             Register(peripheral, registrationPoint.ToRangeRegistration(checked((ulong)peripheral.Size)));
153         }
154 
Unregister(IKnownSize peripheral)155         public void Unregister(IKnownSize peripheral)
156         {
157             Unregister((IBusPeripheral)peripheral);
158         }
159 
MoveRegistrationWithinContext(IBusPeripheral peripheral, BusRangeRegistration newRegistration, ICPU context, Func<IEnumerable<IBusRegistered<IBusPeripheral>>, IBusRegistered<IBusPeripheral>> selector = null)160         public void MoveRegistrationWithinContext(IBusPeripheral peripheral, BusRangeRegistration newRegistration, ICPU context, Func<IEnumerable<IBusRegistered<IBusPeripheral>>, IBusRegistered<IBusPeripheral>> selector = null)
161         {
162             if(context == null && !TryFindCurrentThreadCPUAndId(out context, out var _))
163             {
164                 throw new RecoverableException("Moving a peripheral is supported only from CPU thread if context isn't explicitly set");
165             }
166             var wasMapped = RemoveMappingsForPeripheral(peripheral);
167             var busRegisteredEntries = peripheralsCollectionByContext[context].Peripherals.Where(x => x.Peripheral == peripheral).ToList();
168             if(busRegisteredEntries.Count == 0)
169             {
170                 throw new RecoverableException("Attempted to move a peripheral that isn't registered within current context");
171             }
172             IBusRegistered<IBusPeripheral> busRegistered;
173             if(selector != null)
174             {
175                 busRegistered = selector(busRegisteredEntries);
176                 if(busRegistered == null)
177                 {
178                     throw new RecoverableException("Provided selector failed to find a suitable registration point");
179                 }
180             }
181             else
182             {
183                 if(busRegisteredEntries.Count > 1)
184                 {
185                     throw new RecoverableException($"Unable to unambiguously determine a registration point, found multiple candidates: {String.Join(", ", busRegisteredEntries.Select(x => x.RegistrationPoint))}");
186                 }
187                 busRegistered = busRegisteredEntries[0];
188             }
189             if(IsAddressRangeLocked(busRegistered.RegistrationPoint.Range, context))
190             {
191                 throw new RecoverableException("Moving a peripheral to a locked address range is not supported");
192             }
193             UnregisterAccessFlags(busRegistered.RegistrationPoint, context);
194             peripheralsCollectionByContext.WithStateCollection(context, null, collection =>
195             {
196                 collection.Move(busRegistered, newRegistration);
197             });
198             Machine.ExchangeRegistrationPointForPeripheral(this, peripheral, busRegistered.RegistrationPoint, newRegistration);
199             if(wasMapped)
200             {
201                 AddMappingsForPeripheral(peripheral, newRegistration, context);
202             }
203 
204             if(peripheral is ArrayMemory)
205             {
206                 foreach(var cpu in GetCPUsForContext<ICPUWithMappedMemory>(context))
207                 {
208                     var range = newRegistration.Range;
209                     cpu.RegisterAccessFlags(range.StartAddress, range.Size, isIoMemory: true);
210                 }
211             }
212         }
213 
Register(ICPU cpu, CPURegistrationPoint registrationPoint)214         public void Register(ICPU cpu, CPURegistrationPoint registrationPoint)
215         {
216             lock(cpuSync)
217             {
218                 if(mappingsRemoved)
219                 {
220                     throw new RegistrationException("Currently cannot register CPU after some memory mappings have been dynamically removed.");
221                 }
222                 if(!registrationPoint.Slot.HasValue)
223                 {
224                     var i = 0;
225                     while(cpuById.ContainsKey(i))
226                     {
227                         i++;
228                     }
229                     registrationPoint = new CPURegistrationPoint(i);
230                 }
231                 Machine.RegisterAsAChildOf(this, cpu, registrationPoint);
232                 cpuById.Add(registrationPoint.Slot.Value, cpu);
233                 idByCpu.Add(cpu, registrationPoint.Slot.Value);
234                 AddContextKeys(cpu);
235                 if(cpu is ICPUWithMappedMemory memoryMappedCpu)
236                 {
237                     foreach(var mapping in mappingsForPeripheral.SelectMany(x => x.Value)
238                             .Where(x => x.Context == null || x.Context == cpu))
239                     {
240                         memoryMappedCpu.MapMemory(mapping);
241                     }
242                 }
243 
244                 if(cpu.Endianness != Endianess)
245                 {
246                     hasCpuWithMismatchedEndianness = true;
247                     UpdateAccessMethods();
248                 }
249 
250                 if(GetCPUs().Select(x => x.Endianness).Distinct().Count() > 1)
251                 {
252                     throw new RegistrationException("Currently there can't be CPUs with different endiannesses on the same bus.");
253                 }
254             }
255         }
256 
Unregister(ICPU cpu)257         public void Unregister(ICPU cpu)
258         {
259             using(Machine.ObtainPausedState(true))
260             {
261                 Machine.UnregisterFromParent(cpu);
262                 lock(cpuSync)
263                 {
264                     var id = idByCpu[cpu];
265                     idByCpu.Remove(cpu);
266                     cpuById.Remove(id);
267                     RemoveContextKeys(cpu);
268                 }
269             }
270         }
271 
SetPCOnAllCores(ulong pc)272         public void SetPCOnAllCores(ulong pc)
273         {
274             using(Machine.ObtainPausedState(true))
275             {
276                 lock(cpuSync)
277                 {
278                     foreach(var p in idByCpu.Keys.Cast<ICPU>())
279                     {
280                         p.PC = pc;
281                     }
282                 }
283             }
284         }
285 
LogAllPeripheralsAccess(bool enable = true)286         public void LogAllPeripheralsAccess(bool enable = true)
287         {
288             lock(cpuSync)
289             {
290                 foreach(var p in allPeripherals.SelectMany(x => x.Peripherals))
291                 {
292                     LogPeripheralAccess(p.Peripheral, enable);
293                 }
294             }
295         }
296 
LogPeripheralAccess(IBusPeripheral busPeripheral, bool enable = true)297         public void LogPeripheralAccess(IBusPeripheral busPeripheral, bool enable = true)
298         {
299             foreach(var peripherals in allPeripherals)
300             {
301                 peripherals.VisitAccessMethods(busPeripheral, pam =>
302                 {
303                     // first check whether logging is already enabled, method should be idempotent
304                     var loggingAlreadEnabled = pam.WriteByte.Target is HookWrapper;
305                     this.Log(LogLevel.Info, "Logging already enabled: {0}.", loggingAlreadEnabled);
306                     if(enable == loggingAlreadEnabled)
307                     {
308                         return pam;
309                     }
310                     if(enable)
311                     {
312                         pam.WrapMethods(typeof(ReadLoggingWrapper<>), typeof(WriteLoggingWrapper<>));
313                         return pam;
314                     }
315                     else
316                     {
317                         pam.RemoveWrappersOfType(typeof(ReadLoggingWrapper<>), typeof(WriteLoggingWrapper<>));
318                         return pam;
319                     }
320                 });
321             }
322         }
323 
EnableAllTranslations(bool enable = true)324         public void EnableAllTranslations(bool enable = true)
325         {
326             foreach(var p in allPeripherals.SelectMany(x => x.Peripherals))
327             {
328                 EnableAllTranslations(p.Peripheral, enable);
329             }
330         }
331 
EnableAllTranslations(IBusPeripheral busPeripheral, bool enable = true)332         public void EnableAllTranslations(IBusPeripheral busPeripheral, bool enable = true)
333         {
334             foreach(var peripherals in allPeripherals)
335             {
336                 peripherals.VisitAccessMethods(busPeripheral, pam =>
337                 {
338                     pam.EnableAllTranslations(enable, Endianess);
339                     return pam;
340                 });
341             }
342         }
343 
GetCPUs()344         public IEnumerable<ICPU> GetCPUs()
345         {
346             lock(cpuSync)
347             {
348                 return new ReadOnlyCollection<ICPU>(idByCpu.Keys.ToList());
349             }
350         }
351 
GetCPUSlot(ICPU cpu)352         public int GetCPUSlot(ICPU cpu)
353         {
354             lock(cpuSync)
355             {
356                 if(idByCpu.ContainsKey(cpu))
357                 {
358                     return idByCpu[cpu];
359                 }
360                 throw new KeyNotFoundException("Given CPU is not registered.");
361             }
362         }
363 
GetCurrentCPU()364         public ICPU GetCurrentCPU()
365         {
366             ICPU cpu;
367             if(!TryGetCurrentCPU(out cpu))
368             {
369                 // TODO: inline
370                 throw new RecoverableException(CantFindCpuIdMessage);
371             }
372             return cpu;
373         }
374 
GetCurrentCPUId()375         public int GetCurrentCPUId()
376         {
377             int id;
378             if(!TryGetCurrentCPUId(out id))
379             {
380                 throw new RecoverableException(CantFindCpuIdMessage);
381             }
382             return id;
383         }
384 
GetAllContextKeys()385         public IEnumerable<IPeripheral> GetAllContextKeys()
386         {
387             return peripheralsCollectionByContext.GetAllContextKeys();
388         }
389 
TryGetCurrentContextState(out IPeripheralWithTransactionState cpu, out T cpuState)390         public bool TryGetCurrentContextState<T>(out IPeripheralWithTransactionState cpu, out T cpuState)
391         {
392             cpu = null;
393             cpuState = default;
394             if(!threadLocalContext.InUse || !threadLocalContext.InitiatorState.HasValue)
395             {
396                 return false;
397             }
398             cpu = threadLocalContext.Initiator as IPeripheralWithTransactionState;
399             if((cpu == null) || !cpu.TryConvertUlongToStateObj(threadLocalContext.InitiatorState.Value, out var state))
400             {
401                 return false;
402             }
403             if(state is T requestedTypeSTate)
404             {
405                 cpuState = requestedTypeSTate;
406                 return true;
407             }
408             return false;
409         }
410 
TryConvertStateToUlongForContext(IPeripheral context, IContextState cpuStateObj, out ulong? state)411         public bool TryConvertStateToUlongForContext(IPeripheral context, IContextState cpuStateObj, out ulong? state)
412         {
413             state = null;
414             if(!(context is IPeripheralWithTransactionState peripheralWithTransactionState)
415                 || !peripheralWithTransactionState.TryConvertStateObjToUlong(cpuStateObj, out state))
416             {
417                 return false;
418             }
419             return true;
420         }
421 
HandleChangedSymbols()422         private void HandleChangedSymbols()
423         {
424             OnSymbolsChanged?.Invoke(Machine);
425         }
426 
GetCPUsForContext(IPeripheral context)427         private IEnumerable<ICPU> GetCPUsForContext(IPeripheral context)
428         {
429             return GetCPUs().Where(x => context == null || x == context);
430         }
431 
432         private IEnumerable<T> GetCPUsForContext<T>(IPeripheral context)
433             where T : IPeripheral
434         {
435             return GetCPUsForContext(context).OfType<T>();
436         }
437 
TryGetCurrentCPUId(out int cpuId)438         private bool TryGetCurrentCPUId(out int cpuId)
439         {
440             if(threadLocalContext.InUse && threadLocalContext.Initiator is ICPU cpu)
441             {
442                 cpuId = idByCpu[cpu];
443                 return true;
444             }
445             /*
446              * Because getting cpu id can possibly be a heavy operation, we cache the
447              * obtained ID in the thread local storage. Note that we assume here that the
448              * thread with such storage won't be used for another purposes than it was
449              * used originally (i.e. cpu loop).
450              */
451             if(cachedCpuId.IsValueCreated)
452             {
453                 cpuId = cachedCpuId.Value;
454                 return true;
455             }
456 
457             return TryFindCurrentThreadCPUAndId(out var _, out cpuId);
458         }
459 
TryFindCurrentThreadCPUAndId(out ICPU cpu, out int cpuId)460         private bool TryFindCurrentThreadCPUAndId(out ICPU cpu, out int cpuId)
461         {
462             lock(cpuSync)
463             {
464                 foreach(var entry in cpuById)
465                 {
466                     cpu = entry.Value;
467                     if(!cpu.OnPossessedThread)
468                     {
469                         continue;
470                     }
471                     cpuId = entry.Key;
472                     cachedCpuId.Value = cpuId;
473                     return true;
474                 }
475                 cpu = default(ICPU);
476                 cpuId = -1;
477                 return false;
478             }
479         }
480 
TryGetCurrentCPU(out ICPU cpu)481         public bool TryGetCurrentCPU(out ICPU cpu)
482         {
483             lock(cpuSync)
484             {
485                 int id;
486                 if(TryGetCurrentCPUId(out id))
487                 {
488                     cpu = cpuById[id];
489                     return true;
490                 }
491                 cpu = null;
492                 return false;
493             }
494         }
495 
496         /// <summary>
497         /// Unregister peripheral from the specified address.
498         ///
499         /// NOTE: After calling this method, peripheral may still be
500         /// registered in the SystemBus at another address. In order
501         /// to remove peripheral completely use 'Unregister' method.
502         /// </summary>
503         /// <param name="address">Address on system bus where the peripheral is registered.</param>
504         /// <param name="context">
505         ///     CPU context in which peripherals should be scanned.
506         ///     This is useful when some peripherals are only accessible from selected CPUs.
507         ///
508         ///     If not provided, the global peripherals collection (i.e., peripherals available for all CPUs) is searched.
509         /// </param>
UnregisterFromAddress(ulong address, ICPU context = null)510         public void UnregisterFromAddress(ulong address, ICPU context = null)
511         {
512             var busRegisteredPeripheral = WhatIsAt(address, context);
513             if(busRegisteredPeripheral == null)
514             {
515                 throw new RecoverableException(string.Format(
516                     "There is no peripheral registered at 0x{0:X}.", address));
517             }
518             Unregister(busRegisteredPeripheral);
519         }
520 
Dispose()521         public void Dispose()
522         {
523             cachedCpuId.Dispose();
524             threadLocalContext.Dispose();
525             globalLookup.Dispose();
526             foreach(var lookup in localLookups.Values)
527             {
528                 lookup.Dispose();
529             }
530             #if DEBUG
531             foreach(var peripherals in allPeripherals)
532             {
533                 peripherals.ShowStatistics();
534             }
535             #endif
536         }
537 
538         /// <summary>Checks what is at a given address.</summary>
539         /// <param name="address">
540         ///     A <see cref="ulong"/> with the address to check.
541         /// </param>
542         /// <param name="context">
543         ///     CPU context in which peripherals should be scanned.
544         ///     This is useful when some peripherals are only accessible from selected CPUs.
545         ///
546         ///     If not provided, the global peripherals collection (i.e., peripherals available for all CPUs) is searched.
547         /// </param>
548         /// <returns>
549         ///     A peripheral which is at the given address.
550         /// </returns>
WhatIsAt(ulong address, IPeripheral context = null)551         public IBusRegistered<IBusPeripheral> WhatIsAt(ulong address, IPeripheral context = null)
552         {
553             return GetAccessiblePeripheralsForContext(context).FirstOrDefault(x => x.RegistrationPoint.Range.Contains(address));
554         }
555 
WhatPeripheralIsAt(ulong address, IPeripheral context = null)556         public IPeripheral WhatPeripheralIsAt(ulong address, IPeripheral context = null)
557         {
558             var registered = WhatIsAt(address, context);
559             if(registered != null)
560             {
561                 return registered.Peripheral;
562             }
563             return null;
564         }
565 
FindMemory(ulong address, ICPU context = null)566         public IBusRegistered<MappedMemory> FindMemory(ulong address, ICPU context = null)
567         {
568             return GetAccessiblePeripheralsForContext(context)
569                 .Where(x => x.Peripheral is MappedMemory)
570                 .Convert<IBusPeripheral, MappedMemory>()
571                 .FirstOrDefault(x => x.RegistrationPoint.Range.Contains(address));
572         }
573 
IsMemory(ulong address, ICPU context = null)574         public bool IsMemory(ulong address, ICPU context = null)
575         {
576             return GetAccessiblePeripheralsForContext(context)
577                 .Any(x => x.Peripheral is IMemory && x.RegistrationPoint.Range.Contains(address));
578         }
579 
GetMappedPeripherals(IPeripheral context = null)580         public IEnumerable<IBusRegistered<IMapped>> GetMappedPeripherals(IPeripheral context = null)
581         {
582             return GetAccessiblePeripheralsForContext(context)
583                 .Where(x => x.Peripheral is IMapped)
584                 .Convert<IBusPeripheral, IMapped>();
585         }
586 
GetRegistrationsForPeripheralType(IPeripheral context = null)587         public IEnumerable<IBusRegistered<IBusPeripheral>> GetRegistrationsForPeripheralType<T>(IPeripheral context = null)
588         {
589             return GetAccessiblePeripheralsForContext(context)
590                 .Where(x => x.Peripheral is T);
591         }
592 
GetRegisteredPeripherals(IPeripheral context = null)593         public IEnumerable<IBusRegistered<IBusPeripheral>> GetRegisteredPeripherals(IPeripheral context = null)
594         {
595             return GetAccessiblePeripheralsForContext(context);
596         }
597 
SilenceRange(Range range)598         public void SilenceRange(Range range)
599         {
600             var silencer = new Silencer();
601             Register(silencer, new BusRangeRegistration(range));
602         }
603 
ReadBytes(ulong address, int count, byte[] destination, int startIndex, bool onlyMemory = false, IPeripheral context = null)604         public void ReadBytes(ulong address, int count, byte[] destination, int startIndex, bool onlyMemory = false, IPeripheral context = null)
605         {
606             using(SetLocalContext(context))
607             {
608                 var targets = FindTargets(address, checked((ulong)count), context);
609                 if(onlyMemory)
610                 {
611                     ThrowIfNotAllMemory(targets);
612                 }
613                 foreach(var target in targets)
614                 {
615                     var memory = target.What.Peripheral as MappedMemory;
616                     if(memory != null)
617                     {
618                         checked
619                         {
620                             memory.ReadBytes(checked((long)(target.Offset - target.What.RegistrationPoint.Range.StartAddress + target.What.RegistrationPoint.Offset)), (int)target.SourceLength, destination, startIndex + (int)target.SourceIndex);
621                         }
622                     }
623                     else
624                     {
625                         for(var i = 0UL; i < target.SourceLength; ++i)
626                         {
627                             destination[checked((ulong)startIndex) + target.SourceIndex + i] = ReadByte(target.Offset + i, context);
628                         }
629                     }
630                 }
631             }
632         }
633 
ReadBytes(ulong address, int count, bool onlyMemory = false, IPeripheral context = null)634         public byte[] ReadBytes(ulong address, int count, bool onlyMemory = false, IPeripheral context = null)
635         {
636             var result = new byte[count];
637             ReadBytes(address, count, result, 0, onlyMemory, context);
638             return result;
639         }
640 
ReadBytes(long offset, int count, IPeripheral context = null)641         public byte[] ReadBytes(long offset, int count, IPeripheral context = null)
642         {
643             return ReadBytes((ulong)offset, count, context: context);
644         }
645 
WriteBytes(byte[] bytes, ulong address, bool onlyMemory = false, IPeripheral context = null)646         public void WriteBytes(byte[] bytes, ulong address, bool onlyMemory = false, IPeripheral context = null)
647         {
648             WriteBytes(bytes, address, bytes.Length, onlyMemory, context);
649         }
650 
WriteBytes(byte[] bytes, ulong address, int startingIndex, long count, bool onlyMemory = false, IPeripheral context = null)651         public void WriteBytes(byte[] bytes, ulong address, int startingIndex, long count, bool onlyMemory = false, IPeripheral context = null)
652         {
653             using(SetLocalContext(context))
654             {
655                 var targets = FindTargets(address, checked((ulong)count), context);
656                 if(onlyMemory)
657                 {
658                     ThrowIfNotAllMemory(targets);
659                 }
660                 foreach(var target in targets)
661                 {
662                     var multibytePeripheral = target.What.Peripheral as IMultibyteWritePeripheral;
663                     if(multibytePeripheral != null)
664                     {
665                         checked
666                         {
667                             multibytePeripheral.WriteBytes(checked((long)(target.Offset - target.What.RegistrationPoint.Range.StartAddress + target.What.RegistrationPoint.Offset)), bytes, startingIndex + (int)target.SourceIndex, (int)target.SourceLength);
668                         }
669                     }
670                     else
671                     {
672                         for(var i = 0UL; i < target.SourceLength; ++i)
673                         {
674                             WriteByte(target.Offset + i, bytes[target.SourceIndex + (ulong)startingIndex + i]);
675                         }
676                     }
677                 }
678             }
679         }
680 
WriteBytes(byte[] bytes, ulong address, long count, bool onlyMemory = false, IPeripheral context = null)681         public void WriteBytes(byte[] bytes, ulong address, long count, bool onlyMemory = false, IPeripheral context = null)
682         {
683             WriteBytes(bytes, address, 0, count, onlyMemory, context);
684         }
685 
WriteBytes(long offset, byte[] array, int startingIndex, int count, IPeripheral context = null)686         public void WriteBytes(long offset, byte[] array, int startingIndex, int count, IPeripheral context = null)
687         {
688             WriteBytes(array, (ulong)offset, startingIndex, count, context: context);
689         }
690 
ZeroRange(Range range, IPeripheral context = null)691         public void ZeroRange(Range range, IPeripheral context = null)
692         {
693             var zeroBlock = new byte[1024 * 1024];
694             var blocksNo = range.Size / (ulong)zeroBlock.Length;
695             for(var i = 0UL; i < blocksNo; i++)
696             {
697                 WriteBytes(zeroBlock, range.StartAddress + i * (ulong)zeroBlock.Length, context: context);
698             }
699             WriteBytes(zeroBlock, range.StartAddress + blocksNo * (ulong)zeroBlock.Length, (int)range.Size % zeroBlock.Length, context: context);
700         }
701 
702         // Specifying `textAddress` will override the address of the program text - the symbols will be applied
703         // as if the first loaded segment started at the specified address. This is equivalent to the ADDR parameter
704         // to GDB's add-symbol-file.
LoadSymbolsFrom(IELF elf, bool useVirtualAddress = false, ulong? textAddress = null, ICPU context = null)705         public void LoadSymbolsFrom(IELF elf, bool useVirtualAddress = false, ulong? textAddress = null, ICPU context = null)
706         {
707             GetOrCreateLookup(context).LoadELF(elf, useVirtualAddress, textAddress);
708             pcCache.Invalidate();
709         }
710 
ClearSymbols(ICPU context = null)711         public void ClearSymbols(ICPU context = null)
712         {
713             RemoveLookup(context);
714             pcCache.Invalidate();
715         }
716 
AddSymbol(Range address, string name, bool isThumb = false, ICPU context = null)717         public void AddSymbol(Range address, string name, bool isThumb = false, ICPU context = null)
718         {
719             checked
720             {
721                 GetOrCreateLookup(context).InsertSymbol(name, address.StartAddress, address.Size);
722             }
723             pcCache.Invalidate();
724         }
725 
LoadUImage(ReadFilePath fileName, IInitableCPU cpu = null)726         public void LoadUImage(ReadFilePath fileName, IInitableCPU cpu = null)
727         {
728             if(!Machine.IsPaused)
729             {
730                 throw new RecoverableException("Cannot load ELF on an unpaused machine.");
731             }
732             UImage uImage;
733             this.DebugLog("Loading uImage {0}.", fileName);
734 
735             switch(UImageReader.TryLoad(fileName, out uImage))
736             {
737             case UImageResult.NotUImage:
738                 throw new RecoverableException(string.Format("Given file '{0}' is not a U-Boot image.", fileName));
739             case UImageResult.BadChecksum:
740                 throw new RecoverableException(string.Format("Header checksum does not match for the U-Boot image '{0}'.", fileName));
741             case UImageResult.NotSupportedImageType:
742                 throw new RecoverableException(string.Format("Given file '{0}' is not of a supported image type.", fileName));
743             }
744             byte[] toLoad;
745             switch(uImage.TryGetImageData(out toLoad))
746             {
747             case ImageDataResult.BadChecksum:
748                 throw new RecoverableException("Bad image checksum, probably corrupted image.");
749             case ImageDataResult.UnsupportedCompressionFormat:
750                 throw new RecoverableException(string.Format("Unsupported compression format '{0}'.", uImage.Compression));
751             }
752             WriteBytes(toLoad, uImage.LoadAddress, context: cpu);
753             if(cpu != null)
754             {
755                 cpu.InitFromUImage(uImage);
756             }
757             else
758             {
759                 foreach(var c in GetCPUs().OfType<IInitableCPU>())
760                 {
761                     c.InitFromUImage(uImage);
762                 }
763             }
764             this.Log(LogLevel.Info, string.Format(
765                 "Loaded U-Boot image '{0}'\n" +
766                 "load address: 0x{1:X}\n" +
767                 "size:         {2}B = {3}B\n" +
768                 "timestamp:    {4}\n" +
769                 "entry point:  0x{5:X}\n" +
770                 "architecture: {6}\n" +
771                 "OS:           {7}",
772                 uImage.Name,
773                 uImage.LoadAddress,
774                 uImage.Size, Misc.NormalizeBinary(uImage.Size),
775                 uImage.Timestamp,
776                 uImage.EntryPoint,
777                 uImage.Architecture,
778                 uImage.OperatingSystem
779             ));
780             AddFingerprint(fileName);
781             UpdateLowestLoadedAddress(uImage.LoadAddress);
782         }
783 
GetLoadedFingerprints()784         public IEnumerable<BinaryFingerprint> GetLoadedFingerprints()
785         {
786             return binaryFingerprints.ToArray();
787         }
788 
GetFingerprint(ReadFilePath fileName)789         public BinaryFingerprint GetFingerprint(ReadFilePath fileName)
790         {
791             return new BinaryFingerprint(fileName);
792         }
793 
TryGetAllSymbolAddresses(string symbolName, out IEnumerable<ulong> symbolAddresses, ICPU context = null)794         public bool TryGetAllSymbolAddresses(string symbolName, out IEnumerable<ulong> symbolAddresses, ICPU context = null)
795         {
796             var result = GetLookup(context).TryGetSymbolsByName(symbolName, out var symbols);
797             symbolAddresses = symbols.Select(symbol => symbol.Start.RawValue);
798             return result;
799         }
800 
GetAllSymbolAddresses(string symbolName, ICPU context = null)801         public IEnumerable<ulong> GetAllSymbolAddresses(string symbolName, ICPU context = null)
802         {
803             if(!TryGetAllSymbolAddresses(symbolName, out var symbolAddresses, context))
804             {
805                 throw new RecoverableException(string.Format("No symbol with name `{0}` found.", symbolName));
806             }
807             return symbolAddresses;
808         }
809 
FindSymbolAt(ulong offset, ICPU context = null)810         public string FindSymbolAt(ulong offset, ICPU context = null)
811         {
812             if(!TryFindSymbolAt(offset, out var name, out var _, context))
813             {
814                 return null;
815             }
816             return name;
817         }
818 
TryFindSymbolAt(ulong offset, out string name, out Symbol symbol, ICPU context = null)819         public bool TryFindSymbolAt(ulong offset, out string name, out Symbol symbol, ICPU context = null)
820         {
821             if(!pcCache.TryGetValue(offset, out var entry))
822             {
823                 if(!GetLookup(context).TryGetSymbolByAddress(offset, out symbol))
824                 {
825                     symbol = null;
826                     name = null;
827                     return false;
828                 }
829                 else
830                 {
831                     name = symbol.ToStringRelative(offset);
832                 }
833                 pcCache.Add(offset, Tuple.Create(name, symbol));
834             }
835             else
836             {
837                 name = entry.Item1;
838                 symbol = entry.Item2;
839             }
840 
841             return true;
842         }
843 
MapMemory(IMappedSegment segment, IBusPeripheral owner, bool relative = true, ICPUWithMappedMemory context = null)844         public void MapMemory(IMappedSegment segment, IBusPeripheral owner, bool relative = true, ICPUWithMappedMemory context = null)
845         {
846             if(relative)
847             {
848                 var wrappers = new List<MappedSegmentWrapper>();
849                 foreach(var registrationPoint in GetRegistrationPoints(owner, context))
850                 {
851                     var wrapper = FromRegistrationPointToSegmentWrapper(segment, registrationPoint, context);
852                     if(wrapper != null)
853                     {
854                         wrappers.Add(wrapper);
855                     }
856                 }
857                 AddMappings(wrappers, owner);
858             }
859             else
860             {
861                 AddMappings(new [] { new MappedSegmentWrapper(segment, 0, long.MaxValue, context) }, owner);
862             }
863         }
864 
UnmapMemory(Range range, ICPU context = null)865         public void UnmapMemory(Range range, ICPU context = null)
866         {
867             lock(cpuSync)
868             {
869                 foreach(var cpu in GetCPUsForContext<ICPUWithMappedMemory>(context))
870                 {
871                     mappingsRemoved = true;
872                     cpu.UnmapMemory(range);
873                 }
874             }
875         }
876 
SetPageAccessViaIo(ulong address)877         public void SetPageAccessViaIo(ulong address)
878         {
879             foreach(var cpu in cpuById.Values.OfType<ICPUWithMappedMemory>())
880             {
881                 cpu.SetPageAccessViaIo(address);
882             }
883         }
884 
ClearPageAccessViaIo(ulong address)885         public void ClearPageAccessViaIo(ulong address)
886         {
887             foreach(var cpu in cpuById.Values.OfType<ICPUWithMappedMemory>())
888             {
889                 cpu.ClearPageAccessViaIo(address);
890             }
891         }
892 
AddWatchpointHook(ulong address, SysbusAccessWidth width, Access access, BusHookDelegate hook)893         public void AddWatchpointHook(ulong address, SysbusAccessWidth width, Access access, BusHookDelegate hook)
894         {
895             if(!Enum.IsDefined(typeof(Access), access))
896             {
897                 throw new RecoverableException("Undefined access value.");
898             }
899             if(((((int)width) & 15) != (int)width) || width == 0)
900             {
901                 throw new RecoverableException("Undefined width value.");
902             }
903 
904             var handler = new BusHookHandler(hook, width);
905 
906             var dictionariesToUpdate = new List<Dictionary<ulong, List<BusHookHandler>>>();
907 
908             if((access & Access.Read) != 0)
909             {
910                 dictionariesToUpdate.Add(hooksOnRead);
911             }
912             if((access & Access.Write) != 0)
913             {
914                 dictionariesToUpdate.Add(hooksOnWrite);
915             }
916             foreach(var dictionary in dictionariesToUpdate)
917             {
918                 if(dictionary.ContainsKey(address))
919                 {
920                     dictionary[address].Add(handler);
921                 }
922                 else
923                 {
924                     dictionary[address] = new List<BusHookHandler> { handler };
925                 }
926             }
927             UpdatePageAccesses();
928         }
929 
RemoveWatchpointHook(ulong address, BusHookDelegate hook)930         public void RemoveWatchpointHook(ulong address, BusHookDelegate hook)
931         {
932             foreach(var hookDictionary in new [] { hooksOnRead, hooksOnWrite })
933             {
934                 List<BusHookHandler> handlers;
935                 if(hookDictionary.TryGetValue(address, out handlers))
936                 {
937                     handlers.RemoveAll(x => x.ContainsAction(hook));
938                     if(handlers.Count == 0)
939                     {
940                         hookDictionary.Remove(address);
941                     }
942                 }
943             }
944 
945             ClearPageAccessViaIo(address);
946             UpdatePageAccesses();
947         }
948 
RemoveAllWatchpointHooks(ulong address)949         public void RemoveAllWatchpointHooks(ulong address)
950         {
951             hooksOnRead.Remove(address);
952             hooksOnWrite.Remove(address);
953             ClearPageAccessViaIo(address);
954             UpdatePageAccesses();
955         }
956 
TryGetWatchpointsAt(ulong address, Access access, out List<BusHookHandler> result)957         public bool TryGetWatchpointsAt(ulong address, Access access, out List<BusHookHandler> result)
958         {
959             if(access == Access.ReadAndWrite || access == Access.Read)
960             {
961                 if(hooksOnRead.TryGetValue(address, out result))
962                 {
963                     return true;
964                 }
965                 else if(access == Access.Read)
966                 {
967                     result = null;
968                     return false;
969                 }
970             }
971             return hooksOnWrite.TryGetValue(address, out result);
972         }
973 
974         /// <remarks>Doesn't include peripherals registered using NullRegistrationPoints.</remarks>
GetRegistrationPoints(IBusPeripheral peripheral, ICPU context = null)975         public IEnumerable<BusRangeRegistration> GetRegistrationPoints(IBusPeripheral peripheral, ICPU context = null)
976         {
977             return GetAccessiblePeripheralsForContext(context)
978                 .Where(x => x.Peripheral == peripheral)
979                 .Select(x => x.RegistrationPoint);
980         }
981 
982         /// <remarks>Doesn't include peripherals registered using NullRegistrationPoints.</remarks>
GetRegistrationPoints(IBusPeripheral peripheral)983         public IEnumerable<BusRangeRegistration> GetRegistrationPoints(IBusPeripheral peripheral)
984         {
985             // try to detect the CPU context based on the current thread
986             TryGetCurrentCPU(out var context);
987             return GetRegistrationPoints(peripheral, context);
988         }
989 
ApplySVD(string path)990         public void ApplySVD(string path)
991         {
992             var svdDevice = new SVDParser(path, this);
993             svdDevices.Add(svdDevice);
994         }
995 
Tag(Range range, string tag, ulong defaultValue = 0, bool pausing = false)996         public void Tag(Range range, string tag, ulong defaultValue = 0, bool pausing = false)
997         {
998             var intersectings = tags.Where(x => x.Key.Intersects(range)).ToArray();
999             if(intersectings.Length == 0)
1000             {
1001                 tags.Add(range, new TagEntry { Name = tag, DefaultValue = defaultValue });
1002                 if(pausing)
1003                 {
1004                     pausingTags.Add(tag);
1005                 }
1006                 return;
1007             }
1008             // tag splitting
1009             if(intersectings.Length != 1)
1010             {
1011                 throw new RecoverableException(string.Format(
1012                     "Currently subtag has to be completely contained in other tag. Given one intersects with tags: {0}",
1013                     intersectings.Select(x => x.Value.Name).Aggregate((x, y) => x + ", " + y)));
1014             }
1015             var parentRange = intersectings[0].Key;
1016             var parentName = intersectings[0].Value.Name;
1017             var parentDefaultValue = intersectings[0].Value.DefaultValue;
1018             var parentPausing = pausingTags.Contains(parentName);
1019             if(!parentRange.Contains(range))
1020             {
1021                 throw new RecoverableException(string.Format(
1022                     "Currently subtag has to be completely contained in other tag, in this case {0}.", parentName));
1023             }
1024             RemoveTag(parentRange.StartAddress);
1025             var parentRangeAfterSplitSizeLeft = range.StartAddress - parentRange.StartAddress;
1026             if(parentRangeAfterSplitSizeLeft > 0)
1027             {
1028                 Tag(new Range(parentRange.StartAddress, parentRangeAfterSplitSizeLeft), parentName, parentDefaultValue, parentPausing);
1029             }
1030             var parentRangeAfterSplitSizeRight = parentRange.EndAddress - range.EndAddress;
1031             if(parentRangeAfterSplitSizeRight > 0)
1032             {
1033                 Tag(new Range(range.EndAddress + 1, parentRangeAfterSplitSizeRight), parentName, parentDefaultValue, parentPausing);
1034             }
1035             Tag(range, string.Format("{0}/{1}", parentName, tag), defaultValue, pausing);
1036         }
1037 
RemoveTag(ulong address)1038         public void RemoveTag(ulong address)
1039         {
1040             var tagsToRemove = tags.Where(x => x.Key.Contains(address)).ToArray();
1041             if(tagsToRemove.Length == 0)
1042             {
1043                 throw new RecoverableException(string.Format("There is no tag at address 0x{0:X}.", address));
1044             }
1045             foreach(var tag in tagsToRemove)
1046             {
1047                 tags.Remove(tag.Key);
1048                 pausingTags.Remove(tag.Value.Name);
1049             }
1050         }
1051 
SetAddressRangeLocked(Range range, bool locked, IPeripheral context = null)1052         public void SetAddressRangeLocked(Range range, bool locked, IPeripheral context = null)
1053         {
1054             lock(lockedRangesCollectionByContext)
1055             {
1056                 // Check if `range` needs to be locked or unlocked at all.
1057                 if(locked ? lockedRangesCollectionByContext[context].ContainsWholeRange(range) : !IsAddressRangeLocked(range, context))
1058                 {
1059                     return;
1060                 }
1061 
1062                 using(Machine.ObtainPausedState(internalPause: true))
1063                 {
1064                     RelockRange(range, locked, context);
1065 
1066                     lockedRangesCollectionByContext.WithStateCollection(context, stateMask: null, collection =>
1067                     {
1068                         if(locked)
1069                         {
1070                             collection.Add(range);
1071                         }
1072                         else
1073                         {
1074                             collection.Remove(range);
1075                         }
1076                     });
1077                 }
1078             }
1079         }
1080 
SetPeripheralEnabled(IPeripheral peripheral, bool enabled)1081         public void SetPeripheralEnabled(IPeripheral peripheral, bool enabled)
1082         {
1083             if(enabled)
1084             {
1085                 lockedPeripherals.Remove(peripheral);
1086             }
1087             else
1088             {
1089                 if(peripheral != null)
1090                 {
1091                     lockedPeripherals.Add(peripheral);
1092                 }
1093             }
1094         }
1095 
1096         /// <returns>True if any part of the <c>range</c> is locked for the given CPU (globally if <c>null</c> passed as <c>context</c>).</returns>
IsAddressRangeLocked(Range range, IPeripheral context = null)1097         public bool IsAddressRangeLocked(Range range, IPeripheral context = null)
1098         {
1099             // The locked range is either mapped for the CPU context, or globally (that is the reason for the OR)
1100             return (lockedRangesCollectionByContext.TryGetValue(context, initiatorState: null, out var collection) && collection.ContainsOverlappingRange(range))
1101                 || lockedRangesCollectionByContext[null].ContainsOverlappingRange(range);
1102         }
1103 
IsPeripheralEnabled(IPeripheral peripheral)1104         public bool IsPeripheralEnabled(IPeripheral peripheral)
1105         {
1106             if(lockedPeripherals.Contains(peripheral))
1107             {
1108                 return false;
1109             }
1110             return true;
1111         }
1112 
Clear()1113         public void Clear()
1114         {
1115             ClearAll();
1116         }
1117 
Reset()1118         public void Reset()
1119         {
1120             LowestLoadedAddress = null;
1121             globalLookup = new SymbolLookup();
1122             localLookups = new Dictionary<ICPU, SymbolLookup>();
1123             pcCache.Invalidate();
1124         }
1125 
DecorateWithCPUNameAndPC(string str)1126         public string DecorateWithCPUNameAndPC(string str)
1127         {
1128             if(!TryGetCurrentCPU(out var cpu) || !Machine.TryGetLocalName(cpu, out var cpuName))
1129             {
1130                 return str;
1131             }
1132 
1133             // you probably wonder why 26?
1134             // * we assume at least 3 characters for cpu name
1135             // * 64-bit PC value nees 18 characters
1136             // * there are 5 more separating characters
1137             var builder = new StringBuilder(str.Length + 26);
1138             builder
1139                 .Append("[")
1140                 .Append(cpuName);
1141 
1142             builder.AppendFormat(": 0x{0:X}", cpu.PC.RawValue);
1143 
1144             builder
1145                 .Append("] ")
1146                 .Append(str);
1147 
1148             return builder.ToString();
1149         }
1150 
GetLookup(ICPU context = null)1151         public SymbolLookup GetLookup(ICPU context = null)
1152         {
1153             if(context == null
1154                || !localLookups.TryGetValue(context, out var cpuLookup))
1155             {
1156                 return globalLookup;
1157             }
1158 
1159             return cpuLookup;
1160         }
1161 
1162         public IMachine Machine { get; }
1163 
1164         public int UnexpectedReads
1165         {
1166             get
1167             {
1168                 return Interlocked.CompareExchange(ref unexpectedReads, 0, 0);
1169             }
1170         }
1171 
1172         public int UnexpectedWrites
1173         {
1174             get
1175             {
1176                 return Interlocked.CompareExchange(ref unexpectedWrites, 0, 0);
1177             }
1178         }
1179 
1180         public ulong? LowestLoadedAddress { get; private set; }
1181 
1182         /// <returns>
1183         /// The returned <c>IEnumerable</c> always enumerates all peripherals registered globally
1184         /// but also peripherals registered per CPU are enumerated if called in CPU context.
1185         /// </returns>
1186         public IEnumerable<IRegistered<IBusPeripheral, BusRangeRegistration>> Children
1187         {
1188             get
1189             {
1190                 foreach(var peripheral in GetPeripheralsForCurrentCPU())
1191                 {
1192                     yield return peripheral;
1193                 }
1194             }
1195         }
1196 
1197         public bool IsMultiCore
1198         {
1199             get
1200             {
1201                 return cpuById.Count() > 1;
1202             }
1203         }
1204 
1205         public Endianess Endianess
1206         {
1207             get
1208             {
1209                 return endianess;
1210             }
1211             set
1212             {
1213                 if(peripheralRegistered)
1214                 {
1215                     throw new RecoverableException("Currently one has to set endianess before any peripheral is registered.");
1216                 }
1217                 endianess = value;
1218             }
1219         }
1220 
1221         public UnhandledAccessBehaviour UnhandledAccessBehaviour { get; set; }
1222 
1223         public event Action<IMachine> OnSymbolsChanged;
1224 
GetOrCreateLookup(ICPU context)1225         private SymbolLookup GetOrCreateLookup(ICPU context)
1226         {
1227             if(context == null)
1228             {
1229                 return globalLookup;
1230             }
1231 
1232             if(!localLookups.TryGetValue(context, out var lookup))
1233             {
1234                 lookup = new SymbolLookup();
1235                 localLookups[context] = lookup;
1236             }
1237 
1238             return lookup;
1239         }
1240 
RemoveLookup(ICPU context = null)1241         private void RemoveLookup(ICPU context = null)
1242         {
1243             if(context == null)
1244             {
1245                 globalLookup = new SymbolLookup();
1246             }
1247             else
1248             {
1249                 localLookups.Remove(context);
1250             }
1251         }
1252 
UnregisterInner(IBusPeripheral peripheral)1253         private void UnregisterInner(IBusPeripheral peripheral)
1254         {
1255             RemoveMappingsForPeripheral(peripheral);
1256 
1257             // remove the peripheral from all cpu-local and the global mappings
1258             foreach(var pair in peripheralsCollectionByContext.GetAllContextKeys()
1259                 .SelectMany(context => peripheralsCollectionByContext.GetAllStateKeys(context).Select(stateMask => new { context, stateMask })))
1260             {
1261                 peripheralsCollectionByContext.WithStateCollection(pair.context, stateMask: pair.stateMask, collection =>
1262                 {
1263                     collection.Remove(peripheral);
1264                 });
1265             }
1266             foreach(var stateMask in peripheralsCollectionByContext.GetAllStateKeys(context: null))
1267             {
1268                 peripheralsCollectionByContext.WithStateCollection(context: null, stateMask, collection =>
1269                 {
1270                     collection.Remove(peripheral);
1271                 });
1272             }
1273             RemoveContextKeys(peripheral);
1274         }
1275 
UnregisterInner(IBusRegistered<IBusPeripheral> busRegistered)1276         private void UnregisterInner(IBusRegistered<IBusPeripheral> busRegistered)
1277         {
1278             if(mappingsForPeripheral.ContainsKey(busRegistered.Peripheral))
1279             {
1280                 var toRemove = new HashSet<MappedSegmentWrapper>();
1281                 // it is assumed that mapped segment cannot be partially outside the registration point range
1282                 foreach(var mapping in mappingsForPeripheral[busRegistered.Peripheral].Where(x => busRegistered.RegistrationPoint.Range.Contains(x.StartingOffset)))
1283                 {
1284                     UnmapMemory(new Range(mapping.StartingOffset, checked((ulong)mapping.Size)));
1285                     toRemove.Add(mapping);
1286                 }
1287                 mappingsForPeripheral[busRegistered.Peripheral].RemoveAll(x => toRemove.Contains(x));
1288                 if(mappingsForPeripheral[busRegistered.Peripheral].Count == 0)
1289                 {
1290                     mappingsForPeripheral.Remove(busRegistered.Peripheral);
1291                 }
1292             }
1293             var perCoreRegistration = busRegistered.RegistrationPoint as IBusRegistration;
1294             peripheralsCollectionByContext.WithStateCollection(perCoreRegistration.Initiator, perCoreRegistration.StateMask, collection =>
1295             {
1296                 collection.Remove(busRegistered.RegistrationPoint.Range.StartAddress, busRegistered.RegistrationPoint.Range.EndAddress);
1297             });
1298             RemoveContextKeys(busRegistered.Peripheral);
1299         }
1300 
1301         // this wrapper is to avoid compiler crashing on Ubuntu 20.04;
1302         // for unknown reasons calling `TryGetCurrentCPU` in the `Children` getter
1303         // caused the compiler to throw na InternalErrorException/NullReferenceException
1304         // when building sources
GetPeripheralsForCurrentCPU()1305         private IEnumerable<IBusRegistered<IBusPeripheral>> GetPeripheralsForCurrentCPU()
1306         {
1307             TryGetCurrentCPU(out var context);
1308             return GetAccessiblePeripheralsForContext(context);
1309         }
1310 
1311         /// <summary>
1312         /// This method will return all accessible peripherals for the given context.
1313         /// This means all peripherals accessible only in the current context, and peripherals accessible globally (from any - `null` - context).
1314         /// </summary>
GetAccessiblePeripheralsForContext(IPeripheral context, ulong? initiatorState = null)1315         private IEnumerable<IBusRegistered<IBusPeripheral>> GetAccessiblePeripheralsForContext(IPeripheral context, ulong? initiatorState = null)
1316         {
1317             var locals = peripheralsCollectionByContext.GetValue(context, initiatorState).Peripherals;
1318             return context != null ? locals.Concat(peripheralsCollectionByContext[null].Peripherals) : locals;
1319         }
1320 
FillAccessMethodsWithTaggedMethods(IBusPeripheral peripheral, string tag, ref PeripheralAccessMethods methods)1321         private void FillAccessMethodsWithTaggedMethods(IBusPeripheral peripheral, string tag, ref PeripheralAccessMethods methods)
1322         {
1323             methods.Peripheral = peripheral;
1324             methods.Tag = tag;
1325 
1326             var customAccessMethods = new Dictionary<Tuple<BusAccess.Method, BusAccess.Operation>, MethodInfo>();
1327             bool noRegion = true;
1328             foreach(var method in peripheral.GetType().GetMethods())
1329             {
1330                 Type signature = null;
1331                 if(!Misc.TryGetMatchingSignature(BusAccess.Delegates, method, out signature))
1332                 {
1333                     continue;
1334                 }
1335 
1336                 var accessGroupAttribute = (ConnectionRegionAttribute)method.GetCustomAttributes(typeof(ConnectionRegionAttribute), true).FirstOrDefault();
1337                 if(accessGroupAttribute == null || accessGroupAttribute.Name != tag)
1338                 {
1339                     continue;
1340                 }
1341 
1342                 var accessMethod = BusAccess.GetMethodFromSignature(signature);
1343                 var accessOperation = BusAccess.GetOperationFromSignature(signature);
1344 
1345                 var tuple = Tuple.Create(accessMethod, accessOperation);
1346                 if(customAccessMethods.ContainsKey(tuple))
1347                 {
1348                     throw new RegistrationException(string.Format("Only one method for operation {0} accessing {1} registers is allowed.", accessOperation, accessMethod));
1349                 }
1350 
1351                 customAccessMethods[tuple] = method;
1352                 methods.SetMethod(method, peripheral, accessOperation, accessMethod);
1353                 noRegion = false;
1354             }
1355 
1356             if(noRegion)
1357             {
1358                 throw new RegistrationException($"No region \"{tag}\" is available for {peripheral}.");
1359             }
1360 
1361             foreach(var tuple in customAccessMethods)
1362             {
1363                 var complementingOperation = BusAccess.GetComplementingOperation(tuple.Key.Item2);
1364                 if(!customAccessMethods.ContainsKey(Tuple.Create(tuple.Key.Item1, complementingOperation)))
1365                 {
1366                     throw new RegistrationException($"{complementingOperation}{tuple.Key.Item1} is not specified for {tag}");
1367                 }
1368             }
1369 
1370             FillAccessMethodsWithDefaultMethods(peripheral, ref methods);
1371         }
1372 
FillAccessMethodsWithDefaultMethods(IBusPeripheral peripheral, ref PeripheralAccessMethods methods)1373         private void FillAccessMethodsWithDefaultMethods(IBusPeripheral peripheral, ref PeripheralAccessMethods methods)
1374         {
1375             methods.Peripheral = peripheral;
1376 
1377             var bytePeripheral = peripheral as IBytePeripheral;
1378             var wordPeripheral = peripheral as IWordPeripheral;
1379             var dwordPeripheral = peripheral as IDoubleWordPeripheral;
1380             var qwordPeripheral = peripheral as IQuadWordPeripheral;
1381             BytePeripheralWrapper byteWrapper = null;
1382             WordPeripheralWrapper wordWrapper = null;
1383             DoubleWordPeripheralWrapper dwordWrapper = null;
1384             QuadWordPeripheralWrapper qwordWrapper = null;
1385 
1386             if(methods.ReadByte != null)
1387             {
1388                 byteWrapper = new BytePeripheralWrapper(methods.ReadByte, methods.WriteByte);
1389             }
1390             if(methods.ReadWord != null)
1391             {
1392                 // why there are such wrappers? since device can be registered through
1393                 // method specific registration points
1394                 wordWrapper = new WordPeripheralWrapper(methods.ReadWord, methods.WriteWord);
1395             }
1396             if(methods.ReadDoubleWord != null)
1397             {
1398                 dwordWrapper = new DoubleWordPeripheralWrapper(methods.ReadDoubleWord, methods.WriteDoubleWord);
1399             }
1400             if(methods.ReadQuadWord != null)
1401             {
1402                 qwordWrapper = new QuadWordPeripheralWrapper(methods.ReadQuadWord, methods.WriteQuadWord);
1403             }
1404 
1405             if(bytePeripheral == null && wordPeripheral == null && dwordPeripheral == null && qwordPeripheral == null
1406                     && byteWrapper == null && wordWrapper == null && dwordWrapper == null && qwordWrapper == null)
1407             {
1408                 throw new RegistrationException(string.Format("Cannot register peripheral {0}, it does not implement any of IBusPeripheral derived interfaces," +
1409                 "nor any other methods were pointed.", peripheral));
1410             }
1411 
1412             // We need to pass in Endianess as a default because at this point the peripheral
1413             // is not yet associated with a machine.
1414             Endianess periEndianess = peripheral.GetEndianness(Endianess);
1415             // Note that the condition for applying ReadByteUsingDoubleWordBigEndian et al is different than the condition
1416             // for byte-swapping correctly-sized accesses! The former is needed for all big-endian peripherals, the
1417             // latter - only in the cross-endianness case. This is to ensure that on a big-endian bus, reading a byte
1418             // at offset 0 from a peripheral that only supports double word access and has a single register with value
1419             // 0x11223344 correctly returns 0x11, and not 0x44 as it would if ReadByteUsingDoubleWord were used.
1420             var translatedAccessNeedsSwap = Endianess == Endianess.BigEndian;
1421             var matchingAccessNeedsSwap = periEndianess != Endianess;
1422 
1423             var allowedTranslations = default(AllowedTranslation);
1424             var allowedTranslationsAttributes = peripheral.GetType().GetCustomAttributes(typeof(AllowedTranslationsAttribute), true);
1425             if(allowedTranslationsAttributes.Length != 0)
1426             {
1427                 allowedTranslations = ((AllowedTranslationsAttribute)allowedTranslationsAttributes[0]).AllowedTranslations;
1428             }
1429 
1430             // If the CPU endianness does not match the bus endianness, this endianness mismatch
1431             // is basically an additional swap. Instead of performing it we reverse the condition.
1432             // This is the case on PowerPC, for example: there the translation library is always built
1433             // in big-endian mode, and if the CPU is running in little-endian mode, it performs byte
1434             // swapping separately. This is to support switching endianness at runtime, but note that
1435             // if this is actually done, peripheral accesses will be reversed.
1436             if(hasCpuWithMismatchedEndianness)
1437             {
1438                 matchingAccessNeedsSwap = !matchingAccessNeedsSwap;
1439                 translatedAccessNeedsSwap = !translatedAccessNeedsSwap;
1440                 // Other translation types will behave incorrectly in this case.
1441                 allowedTranslations &=
1442                     AllowedTranslation.ByteToWord | AllowedTranslation.ByteToDoubleWord | AllowedTranslation.ByteToQuadWord |
1443                     AllowedTranslation.WordToByte | AllowedTranslation.DoubleWordToByte | AllowedTranslation.QuadWordToByte;
1444             }
1445 
1446             // When methods don't have tag, it means they are regular peripheral methods (not regions).
1447             // Use peripheral methods for accesses.
1448             if(methods.Tag == null)
1449             {
1450                 if(methods.ReadByte == null && bytePeripheral != null)
1451                 {
1452                     methods.ReadByte = bytePeripheral.ReadByte;
1453                     methods.WriteByte = bytePeripheral.WriteByte;
1454                 }
1455                 if(methods.ReadWord == null && wordPeripheral != null)
1456                 {
1457                     methods.ReadWord = matchingAccessNeedsSwap ? (BusAccess.WordReadMethod)wordPeripheral.ReadWordBigEndian : wordPeripheral.ReadWord;
1458                     methods.WriteWord = matchingAccessNeedsSwap ? (BusAccess.WordWriteMethod)wordPeripheral.WriteWordBigEndian : wordPeripheral.WriteWord;
1459                 }
1460                 if(methods.ReadDoubleWord == null && dwordPeripheral != null)
1461                 {
1462                     methods.ReadDoubleWord = matchingAccessNeedsSwap ? (BusAccess.DoubleWordReadMethod)dwordPeripheral.ReadDoubleWordBigEndian : dwordPeripheral.ReadDoubleWord;
1463                     methods.WriteDoubleWord = matchingAccessNeedsSwap ? (BusAccess.DoubleWordWriteMethod)dwordPeripheral.WriteDoubleWordBigEndian : dwordPeripheral.WriteDoubleWord;
1464                 }
1465                 if(methods.ReadQuadWord == null && qwordPeripheral != null)
1466                 {
1467                     methods.ReadQuadWord = matchingAccessNeedsSwap ? (BusAccess.QuadWordReadMethod)qwordPeripheral.ReadQuadWordBigEndian : qwordPeripheral.ReadQuadWord;
1468                     methods.WriteQuadWord = matchingAccessNeedsSwap ? (BusAccess.QuadWordWriteMethod)qwordPeripheral.WriteQuadWordBigEndian : qwordPeripheral.WriteQuadWord;
1469                 }
1470             }
1471 
1472             if(methods.ReadByte == null) // they are null or not always in pairs
1473             {
1474                 if(qwordWrapper != null && (allowedTranslations & AllowedTranslation.ByteToQuadWord) != 0)
1475                 {
1476                     methods.ReadByte = translatedAccessNeedsSwap ? (BusAccess.ByteReadMethod)qwordWrapper.ReadByteUsingQuadWordBigEndian : qwordWrapper.ReadByteUsingQuadWord;
1477                     methods.WriteByte = translatedAccessNeedsSwap ? (BusAccess.ByteWriteMethod)qwordWrapper.WriteByteUsingQuadWordBigEndian : qwordWrapper.WriteByteUsingQuadWord;
1478                 }
1479                 else if(dwordWrapper != null && (allowedTranslations & AllowedTranslation.ByteToDoubleWord) != 0)
1480                 {
1481                     methods.ReadByte = translatedAccessNeedsSwap ? (BusAccess.ByteReadMethod)dwordWrapper.ReadByteUsingDoubleWordBigEndian : dwordWrapper.ReadByteUsingDoubleWord;
1482                     methods.WriteByte = translatedAccessNeedsSwap ? (BusAccess.ByteWriteMethod)dwordWrapper.WriteByteUsingDoubleWordBigEndian : dwordWrapper.WriteByteUsingDoubleWord;
1483                 }
1484                 else if(wordWrapper != null && (allowedTranslations & AllowedTranslation.ByteToWord) != 0)
1485                 {
1486                     methods.ReadByte = translatedAccessNeedsSwap ? (BusAccess.ByteReadMethod)wordWrapper.ReadByteUsingWordBigEndian : wordWrapper.ReadByteUsingWord;
1487                     methods.WriteByte = translatedAccessNeedsSwap ? (BusAccess.ByteWriteMethod)wordWrapper.WriteByteUsingWordBigEndian : wordWrapper.WriteByteUsingWord;
1488                 }
1489                 else if(qwordPeripheral != null && (allowedTranslations & AllowedTranslation.ByteToQuadWord) != 0)
1490                 {
1491                     methods.ReadByte = translatedAccessNeedsSwap ? (BusAccess.ByteReadMethod)qwordPeripheral.ReadByteUsingQuadWordBigEndian : qwordPeripheral.ReadByteUsingQuadWord;
1492                     methods.WriteByte = translatedAccessNeedsSwap ? (BusAccess.ByteWriteMethod)qwordPeripheral.WriteByteUsingQuadWordBigEndian : qwordPeripheral.WriteByteUsingQuadWord;
1493                 }
1494                 else if(dwordPeripheral != null && (allowedTranslations & AllowedTranslation.ByteToDoubleWord) != 0)
1495                 {
1496                     methods.ReadByte = translatedAccessNeedsSwap ? (BusAccess.ByteReadMethod)dwordPeripheral.ReadByteUsingDoubleWordBigEndian : dwordPeripheral.ReadByteUsingDoubleWord;
1497                     methods.WriteByte = translatedAccessNeedsSwap ? (BusAccess.ByteWriteMethod)dwordPeripheral.WriteByteUsingDoubleWordBigEndian : dwordPeripheral.WriteByteUsingDoubleWord;
1498                 }
1499                 else if(wordPeripheral != null && (allowedTranslations & AllowedTranslation.ByteToWord) != 0)
1500                 {
1501                     methods.ReadByte = translatedAccessNeedsSwap ? (BusAccess.ByteReadMethod)wordPeripheral.ReadByteUsingWordBigEndian : wordPeripheral.ReadByteUsingWord;
1502                     methods.WriteByte = translatedAccessNeedsSwap ? (BusAccess.ByteWriteMethod)wordPeripheral.WriteByteUsingWordBigEndian : wordPeripheral.WriteByteUsingWord;
1503                 }
1504 
1505                 else
1506                 {
1507                     methods.ReadByte = peripheral.ReadByteNotTranslated;
1508                     methods.WriteByte = peripheral.WriteByteNotTranslated;
1509                 }
1510             }
1511 
1512             if(methods.ReadWord == null)
1513             {
1514                 if(qwordWrapper != null && (allowedTranslations & AllowedTranslation.WordToQuadWord) != 0)
1515                 {
1516                     methods.ReadWord = translatedAccessNeedsSwap ? (BusAccess.WordReadMethod)qwordWrapper.ReadWordUsingQuadWordBigEndian : qwordWrapper.ReadWordUsingQuadWord;
1517                     methods.WriteWord = translatedAccessNeedsSwap ? (BusAccess.WordWriteMethod)qwordWrapper.WriteWordUsingQuadWordBigEndian : qwordWrapper.WriteWordUsingQuadWord;
1518                 }
1519                 else if(dwordWrapper != null && (allowedTranslations & AllowedTranslation.WordToDoubleWord) != 0)
1520                 {
1521                     methods.ReadWord = translatedAccessNeedsSwap ? (BusAccess.WordReadMethod)dwordWrapper.ReadWordUsingDoubleWordBigEndian : dwordWrapper.ReadWordUsingDoubleWord;
1522                     methods.WriteWord = translatedAccessNeedsSwap ? (BusAccess.WordWriteMethod)dwordWrapper.WriteWordUsingDoubleWordBigEndian : dwordWrapper.WriteWordUsingDoubleWord;
1523                 }
1524                 else if(byteWrapper != null && (allowedTranslations & AllowedTranslation.WordToByte) != 0)
1525                 {
1526                     methods.ReadWord = translatedAccessNeedsSwap ? (BusAccess.WordReadMethod)byteWrapper.ReadWordUsingByteBigEndian : byteWrapper.ReadWordUsingByte;
1527                     methods.WriteWord = translatedAccessNeedsSwap ? (BusAccess.WordWriteMethod)byteWrapper.WriteWordUsingByteBigEndian : byteWrapper.WriteWordUsingByte;
1528                 }
1529                 else if(qwordPeripheral != null && (allowedTranslations & AllowedTranslation.WordToQuadWord) != 0)
1530                 {
1531                     methods.ReadWord = translatedAccessNeedsSwap ? (BusAccess.WordReadMethod)qwordPeripheral.ReadWordUsingQuadWordBigEndian : qwordPeripheral.ReadWordUsingQuadWord;
1532                     methods.WriteWord = translatedAccessNeedsSwap ? (BusAccess.WordWriteMethod)qwordPeripheral.WriteWordUsingQuadWordBigEndian : qwordPeripheral.WriteWordUsingQuadWord;
1533                 }
1534                 else if(dwordPeripheral != null && (allowedTranslations & AllowedTranslation.WordToDoubleWord) != 0)
1535                 {
1536                     methods.ReadWord = translatedAccessNeedsSwap ? (BusAccess.WordReadMethod)dwordPeripheral.ReadWordUsingDoubleWordBigEndian : dwordPeripheral.ReadWordUsingDoubleWord;
1537                     methods.WriteWord = translatedAccessNeedsSwap ? (BusAccess.WordWriteMethod)dwordPeripheral.WriteWordUsingDoubleWordBigEndian : dwordPeripheral.WriteWordUsingDoubleWord;
1538                 }
1539                 else if(bytePeripheral != null && (allowedTranslations & AllowedTranslation.WordToByte) != 0)
1540                 {
1541                     methods.ReadWord = translatedAccessNeedsSwap ? (BusAccess.WordReadMethod)bytePeripheral.ReadWordUsingByteBigEndian : bytePeripheral.ReadWordUsingByte;
1542                     methods.WriteWord = translatedAccessNeedsSwap ? (BusAccess.WordWriteMethod)bytePeripheral.WriteWordUsingByteBigEndian : bytePeripheral.WriteWordUsingByte;
1543                 }
1544                 else
1545                 {
1546                     methods.ReadWord = peripheral.ReadWordNotTranslated;
1547                     methods.WriteWord = peripheral.WriteWordNotTranslated;
1548                 }
1549             }
1550             else if(matchingAccessNeedsSwap && wordWrapper != null)
1551             {
1552                 methods.ReadWord = (BusAccess.WordReadMethod)wordWrapper.ReadWordBigEndian;
1553                 methods.WriteWord = (BusAccess.WordWriteMethod)wordWrapper.WriteWordBigEndian;
1554             }
1555 
1556             if(methods.ReadDoubleWord == null)
1557             {
1558                 if(qwordWrapper != null && (allowedTranslations & AllowedTranslation.DoubleWordToQuadWord) != 0)
1559                 {
1560                     methods.ReadDoubleWord = translatedAccessNeedsSwap ? (BusAccess.DoubleWordReadMethod)qwordWrapper.ReadDoubleWordUsingQuadWordBigEndian : qwordWrapper.ReadDoubleWordUsingQuadWord;
1561                     methods.WriteDoubleWord = translatedAccessNeedsSwap ? (BusAccess.DoubleWordWriteMethod)qwordWrapper.WriteDoubleWordUsingQuadWordBigEndian : qwordWrapper.WriteDoubleWordUsingQuadWord;
1562                 }
1563                 else if(wordWrapper != null && (allowedTranslations & AllowedTranslation.DoubleWordToWord) != 0)
1564                 {
1565                     methods.ReadDoubleWord = translatedAccessNeedsSwap ? (BusAccess.DoubleWordReadMethod)wordWrapper.ReadDoubleWordUsingWordBigEndian : wordWrapper.ReadDoubleWordUsingWord;
1566                     methods.WriteDoubleWord = translatedAccessNeedsSwap ? (BusAccess.DoubleWordWriteMethod)wordWrapper.WriteDoubleWordUsingWordBigEndian : wordWrapper.WriteDoubleWordUsingWord;
1567                 }
1568                 else if(byteWrapper != null && (allowedTranslations & AllowedTranslation.DoubleWordToByte) != 0)
1569                 {
1570                     methods.ReadDoubleWord = translatedAccessNeedsSwap ? (BusAccess.DoubleWordReadMethod)byteWrapper.ReadDoubleWordUsingByteBigEndian : byteWrapper.ReadDoubleWordUsingByte;
1571                     methods.WriteDoubleWord = translatedAccessNeedsSwap ? (BusAccess.DoubleWordWriteMethod)byteWrapper.WriteDoubleWordUsingByteBigEndian : byteWrapper.WriteDoubleWordUsingByte;
1572                 }
1573                 else if(qwordPeripheral != null && (allowedTranslations & AllowedTranslation.DoubleWordToQuadWord) != 0)
1574                 {
1575                     methods.ReadDoubleWord = translatedAccessNeedsSwap ? (BusAccess.DoubleWordReadMethod)qwordPeripheral.ReadDoubleWordUsingQuadWordBigEndian : qwordPeripheral.ReadDoubleWordUsingQuadWord;
1576                     methods.WriteDoubleWord = translatedAccessNeedsSwap ? (BusAccess.DoubleWordWriteMethod)qwordPeripheral.WriteDoubleWordUsingQuadWordBigEndian : qwordPeripheral.WriteDoubleWordUsingQuadWord;
1577                 }
1578                 else if(wordPeripheral != null && (allowedTranslations & AllowedTranslation.DoubleWordToWord) != 0)
1579                 {
1580                     methods.ReadDoubleWord = translatedAccessNeedsSwap ? (BusAccess.DoubleWordReadMethod)wordPeripheral.ReadDoubleWordUsingWordBigEndian : wordPeripheral.ReadDoubleWordUsingWord;
1581                     methods.WriteDoubleWord = translatedAccessNeedsSwap ? (BusAccess.DoubleWordWriteMethod)wordPeripheral.WriteDoubleWordUsingWordBigEndian : wordPeripheral.WriteDoubleWordUsingWord;
1582                 }
1583                 else if(bytePeripheral != null && (allowedTranslations & AllowedTranslation.DoubleWordToByte) != 0)
1584                 {
1585                     methods.ReadDoubleWord = translatedAccessNeedsSwap ? (BusAccess.DoubleWordReadMethod)bytePeripheral.ReadDoubleWordUsingByteBigEndian : bytePeripheral.ReadDoubleWordUsingByte;
1586                     methods.WriteDoubleWord = translatedAccessNeedsSwap ? (BusAccess.DoubleWordWriteMethod)bytePeripheral.WriteDoubleWordUsingByteBigEndian : bytePeripheral.WriteDoubleWordUsingByte;
1587                 }
1588                 else
1589                 {
1590                     methods.ReadDoubleWord = peripheral.ReadDoubleWordNotTranslated;
1591                     methods.WriteDoubleWord = peripheral.WriteDoubleWordNotTranslated;
1592                 }
1593             }
1594             else if(matchingAccessNeedsSwap && dwordWrapper != null)
1595             {
1596                 methods.ReadDoubleWord = (BusAccess.DoubleWordReadMethod)dwordWrapper.ReadDoubleWordBigEndian;
1597                 methods.WriteDoubleWord = (BusAccess.DoubleWordWriteMethod)dwordWrapper.WriteDoubleWordBigEndian;
1598             }
1599             if(methods.ReadQuadWord == null)
1600             {
1601                 if(dwordWrapper != null && (allowedTranslations & AllowedTranslation.QuadWordToDoubleWord) != 0)
1602                 {
1603                     methods.ReadQuadWord = translatedAccessNeedsSwap ? (BusAccess.QuadWordReadMethod)dwordWrapper.ReadQuadWordUsingDoubleWordBigEndian : dwordWrapper.ReadQuadWordUsingDoubleWord;
1604                     methods.WriteQuadWord = translatedAccessNeedsSwap ? (BusAccess.QuadWordWriteMethod)dwordWrapper.WriteQuadWordUsingDoubleWordBigEndian : dwordWrapper.WriteQuadWordUsingDoubleWord;
1605                 }
1606                 else if(wordWrapper != null && (allowedTranslations & AllowedTranslation.QuadWordToWord) != 0)
1607                 {
1608                     methods.ReadQuadWord = translatedAccessNeedsSwap ? (BusAccess.QuadWordReadMethod)wordWrapper.ReadQuadWordUsingWordBigEndian : wordWrapper.ReadQuadWordUsingWord;
1609                     methods.WriteQuadWord = translatedAccessNeedsSwap ? (BusAccess.QuadWordWriteMethod)wordWrapper.WriteQuadWordUsingWordBigEndian : wordWrapper.WriteQuadWordUsingWord;
1610                 }
1611                 else if(byteWrapper != null && (allowedTranslations & AllowedTranslation.QuadWordToByte) != 0)
1612                 {
1613                     methods.ReadQuadWord = translatedAccessNeedsSwap ? (BusAccess.QuadWordReadMethod)byteWrapper.ReadQuadWordUsingByteBigEndian : byteWrapper.ReadQuadWordUsingByte;
1614                     methods.WriteQuadWord = translatedAccessNeedsSwap ? (BusAccess.QuadWordWriteMethod)byteWrapper.WriteQuadWordUsingByteBigEndian : byteWrapper.WriteQuadWordUsingByte;
1615                 }
1616                 else if(dwordPeripheral != null && (allowedTranslations & AllowedTranslation.QuadWordToDoubleWord) != 0)
1617                 {
1618                     methods.ReadQuadWord = translatedAccessNeedsSwap ? (BusAccess.QuadWordReadMethod)dwordPeripheral.ReadQuadWordUsingDoubleWordBigEndian : dwordPeripheral.ReadQuadWordUsingDoubleWord;
1619                     methods.WriteQuadWord = translatedAccessNeedsSwap ? (BusAccess.QuadWordWriteMethod)dwordPeripheral.WriteQuadWordUsingDoubleWordBigEndian : dwordPeripheral.WriteQuadWordUsingDoubleWord;
1620                 }
1621                 else if(wordPeripheral != null && (allowedTranslations & AllowedTranslation.QuadWordToWord) != 0)
1622                 {
1623                     methods.ReadQuadWord = translatedAccessNeedsSwap ? (BusAccess.QuadWordReadMethod)wordPeripheral.ReadQuadWordUsingWordBigEndian : wordPeripheral.ReadQuadWordUsingWord;
1624                     methods.WriteQuadWord = translatedAccessNeedsSwap ? (BusAccess.QuadWordWriteMethod)wordPeripheral.WriteQuadWordUsingWordBigEndian : wordPeripheral.WriteQuadWordUsingWord;
1625                 }
1626                 else if(bytePeripheral != null && (allowedTranslations & AllowedTranslation.QuadWordToByte) != 0)
1627                 {
1628                     methods.ReadQuadWord = translatedAccessNeedsSwap ? (BusAccess.QuadWordReadMethod)bytePeripheral.ReadQuadWordUsingByteBigEndian : bytePeripheral.ReadQuadWordUsingByte;
1629                     methods.WriteQuadWord = translatedAccessNeedsSwap ? (BusAccess.QuadWordWriteMethod)bytePeripheral.WriteQuadWordUsingByteBigEndian : bytePeripheral.WriteQuadWordUsingByte;
1630                 }
1631                 else
1632                 {
1633                     methods.ReadQuadWord = peripheral.ReadQuadWordNotTranslated;
1634                     methods.WriteQuadWord = peripheral.WriteQuadWordNotTranslated;
1635                 }
1636             }
1637             else if(matchingAccessNeedsSwap && qwordWrapper != null)
1638             {
1639                 methods.ReadQuadWord = (BusAccess.QuadWordReadMethod)qwordWrapper.ReadQuadWordBigEndian;
1640                 methods.WriteQuadWord = (BusAccess.QuadWordWriteMethod)qwordWrapper.WriteQuadWordBigEndian;
1641             }
1642         }
1643 
UpdateAccessMethods()1644         private void UpdateAccessMethods()
1645         {
1646             foreach(var peripherals in allPeripherals)
1647             {
1648                 peripherals.VisitAccessMethods(null, pam =>
1649                 {
1650                     if(pam.Tag != null)
1651                     {
1652                         FillAccessMethodsWithTaggedMethods(pam.Peripheral, pam.Tag, ref pam);
1653                     }
1654                     else
1655                     {
1656                         FillAccessMethodsWithDefaultMethods(pam.Peripheral, ref pam);
1657                     }
1658                     return pam;
1659                 });
1660             }
1661         }
1662 
RegisterInner(IBusPeripheral peripheral, PeripheralAccessMethods methods, BusRangeRegistration registrationPoint, IPeripheral context)1663         private void RegisterInner(IBusPeripheral peripheral, PeripheralAccessMethods methods, BusRangeRegistration registrationPoint, IPeripheral context)
1664         {
1665             using(Machine.ObtainPausedState(true))
1666             {
1667                 // Ensure the context exists if we need one, since this peripheral might be getting registered before its context is.
1668                 // This only applies for contexts that are not CPUs, since those are always created immediately, but it's harmless to do
1669                 // for CPUs.
1670                 if(context != null)
1671                 {
1672                     AddContextKeys(context);
1673                 }
1674 
1675                 var stateMask = registrationPoint.StateMask;
1676                 var busRegisteredInContext = peripheralsCollectionByContext.GetValue(context, stateMask?.State).Peripherals;
1677                 var intersecting = busRegisteredInContext
1678                     .FirstOrDefault(x => x.RegistrationPoint.Range.Intersects(registrationPoint.Range)
1679                                       && x.RegistrationPoint.StateMask?.Mask == registrationPoint.StateMask?.Mask);
1680                 if(intersecting != null)
1681                 {
1682                     throw new RegistrationException($"Given address {registrationPoint.Range} for peripheral {peripheral} conflicts with address {intersecting.RegistrationPoint.Range} of peripheral {intersecting.Peripheral}", "address");
1683                 }
1684 
1685                 var registeredPeripheral = new BusRegistered<IBusPeripheral>(peripheral, registrationPoint);
1686 
1687                 if(IsAddressRangeLocked(registrationPoint.Range, context))
1688                 {
1689                     // If it's impossible to re-lock the range, the peripheral can't be registered
1690                     if(!CanRangeBeLocked(registrationPoint.Range, context, out var _, out var __))
1691                     {
1692                         throw new RegistrationException($"Cannot register peripheral {peripheral} on locked range {registrationPoint.Range}");
1693                     }
1694                 }
1695 
1696                 // we also have to put missing methods
1697                 var absoluteAddressAware = peripheral as IAbsoluteAddressAware;
1698                 if(absoluteAddressAware != null)
1699                 {
1700                     methods.SetAbsoluteAddress = absoluteAddressAware.SetAbsoluteAddress;
1701                 }
1702                 peripheralsCollectionByContext.WithStateCollection(context, stateMask, collection =>
1703                 {
1704                     // Currently the end address is actually "one past the end address".
1705                     collection.Add(registrationPoint.Range.StartAddress, registrationPoint.Range.EndAddress + 1, registeredPeripheral, methods);
1706                 });
1707                 // let's add new mappings
1708                 AddMappingsForPeripheral(peripheral, registrationPoint, context);
1709                 // After adding new mappings, if the address range is locked, the mappings possibly have to be modified/unmapped on the CPU's side
1710                 // RelockRange to make this happen
1711                 if(IsAddressRangeLocked(registrationPoint.Range, context))
1712                 {
1713                     // We've checked before if the range can be locked, so this should succeed
1714                     RelockRange(registrationPoint.Range, true, context);
1715                 }
1716                 Machine.RegisterAsAChildOf(this, peripheral, registrationPoint);
1717                 Machine.RegisterBusController(peripheral, this);
1718             }
1719 
1720             peripheralRegistered = true;
1721         }
1722 
1723         /// <summary>
1724         /// This method can be used to re-trigger locking/unlocking actions on a range which is already registered as locked.
1725         /// This is useful when adding or relocating peripherals to the locked range.
1726         /// <summary>
1727         /// <remarks>
1728         /// Locking should almost always be done by calling <see cref="SetAddressRangeLocked"/>
1729         /// since this method doesn't update locked ranges and so should be used with caution.
1730         /// </remarks>
RelockRange(Range range, bool locked, IPeripheral context)1731         private void RelockRange(Range range, bool locked, IPeripheral context)
1732         {
1733             using(Machine.ObtainPausedState(internalPause: true))
1734             {
1735                 var cpusWithMappedMemory = idByCpu.Keys.OfType<ICPUWithMappedMemory>();
1736                 if(cpusWithMappedMemory.Any())
1737                 {
1738                     if(!CanRangeBeLocked(range, context, out var mappedInRange, out var onlyPartiallyInRange))
1739                     {
1740                         throw new RecoverableException(
1741                             $"Mapped peripherals registered at the given range {range} have to be fully included:\n"
1742                             + $"* {string.Join("\n* ", onlyPartiallyInRange)}"
1743                         );
1744                     }
1745 
1746                     foreach(var busRegistered in mappedInRange)
1747                     {
1748                         var registrationContext = busRegistered.RegistrationPoint.Initiator;
1749                         var registrationRange = busRegistered.RegistrationPoint.Range;
1750                         foreach(var cpu in GetCPUsForContext<ICPUWithMappedMemory>(registrationContext))
1751                         {
1752                             // There's no need to remove mappings from `mappingsForPeripheral`.
1753                             // They're only used when a new ICPUWithMappedMemory gets registered
1754                             // which isn't possible after `mappingsRemoved` is set in `UnmapMemory`.
1755                             cpu.SetMappedMemoryEnabled(registrationRange, enabled: !locked);
1756                         }
1757                     }
1758                 }
1759             }
1760         }
1761 
CanRangeBeLocked(Range range, IPeripheral context, out IEnumerable<IBusRegistered<IMapped>> mappedInRange, out IEnumerable<IBusRegistered<IMapped>> onlyPartiallyInRange)1762         private bool CanRangeBeLocked(Range range, IPeripheral context, out IEnumerable<IBusRegistered<IMapped>> mappedInRange, out IEnumerable<IBusRegistered<IMapped>> onlyPartiallyInRange)
1763         {
1764             mappedInRange = GetMappedPeripherals(context).Where(x => x.RegistrationPoint.Range.Intersects(range));
1765             // Only allow including whole registration range of IMapped peripherals.
1766             onlyPartiallyInRange = mappedInRange.Where(x => !range.Contains(x.RegistrationPoint.Range));
1767             return !onlyPartiallyInRange.Any();
1768         }
1769 
FindTargets(ulong address, ulong count, IPeripheral context = null)1770         private IEnumerable<PeripheralLookupResult> FindTargets(ulong address, ulong count, IPeripheral context = null)
1771         {
1772             var result = new List<PeripheralLookupResult>();
1773             var written = 0UL;
1774             while(written < count)
1775             {
1776                 var currentPosition = address + written;
1777                 // what peripheral is at the current write position?
1778                 var what = WhatIsAt(currentPosition, context);
1779                 if(what == null)
1780                 {
1781                     var holeStart = currentPosition;
1782                     // we can omit part of the array
1783                     // but how much?
1784                     var nextPeripheral = GetAccessiblePeripheralsForContext(context).OrderBy(x => x.RegistrationPoint.Range.StartAddress).FirstOrDefault(x => x.RegistrationPoint.Range.StartAddress > currentPosition);
1785                     if(nextPeripheral == null)
1786                     {
1787                         // hole reaches the end of the required range
1788                         written = count;
1789                     }
1790                     else
1791                     {
1792                         written += Math.Min(nextPeripheral.RegistrationPoint.Range.StartAddress - currentPosition, count - written);
1793                     }
1794                     var holeSize = address + written - currentPosition;
1795                     this.Log(LogLevel.Warning, "Tried to access bytes at non-existing peripheral in range {0}.", new Range(holeStart, holeSize));
1796                     continue;
1797                 }
1798                 var toWrite = Math.Min(count - written, what.RegistrationPoint.Range.EndAddress - currentPosition + 1);
1799                 var singleResult = new PeripheralLookupResult();
1800                 singleResult.What = what;
1801                 singleResult.SourceIndex = written;
1802                 singleResult.SourceLength = toWrite;
1803                 singleResult.Offset = currentPosition;
1804                 written += toWrite;
1805                 result.Add(singleResult);
1806             }
1807 
1808             return result;
1809         }
1810 
ThrowIfNotAllMemory(IEnumerable<PeripheralLookupResult> targets)1811         private static void ThrowIfNotAllMemory(IEnumerable<PeripheralLookupResult> targets)
1812         {
1813             foreach(var target in targets)
1814             {
1815                 var iMemory = target.What.Peripheral as IMemory;
1816                 var redirector = target.What.Peripheral as Redirector;
1817                 if(iMemory == null && redirector == null)
1818                 {
1819                     throw new RecoverableException(String.Format("Tried to access {0} but only memory accesses were allowed.", target.What.Peripheral));
1820                 }
1821             }
1822         }
1823 
UpdatePageAccesses()1824         private void UpdatePageAccesses()
1825         {
1826             foreach(var address in hooksOnRead.Select(x => x.Key).Union(hooksOnWrite.Select(x => x.Key)))
1827             {
1828                 SetPageAccessViaIo(address);
1829             }
1830         }
1831 
ClearAll()1832         private void ClearAll()
1833         {
1834             lock(cpuSync)
1835             {
1836                 foreach(var group in Machine.PeripheralsGroups.ActiveGroups)
1837                 {
1838                     group.Unregister();
1839                 }
1840 
1841                 foreach(var p in allPeripherals.SelectMany(x => x.Peripherals).Select(x => x.Peripheral).Distinct().Union(GetCPUs().Cast<IPeripheral>()).ToList())
1842                 {
1843                     Machine.UnregisterFromParent(p);
1844                 }
1845 
1846                 mappingsRemoved = false;
1847                 InitStructures();
1848             }
1849         }
1850 
UpdateLowestLoadedAddress(ulong lowestLoadedAddress)1851         private void UpdateLowestLoadedAddress(ulong lowestLoadedAddress)
1852         {
1853             if(!LowestLoadedAddress.HasValue)
1854             {
1855                 LowestLoadedAddress = lowestLoadedAddress;
1856                 return;
1857             }
1858             LowestLoadedAddress = Math.Min(LowestLoadedAddress.Value, lowestLoadedAddress);
1859         }
1860 
AddFingerprint(string fileName)1861         private void AddFingerprint(string fileName)
1862         {
1863             binaryFingerprints.Add(new BinaryFingerprint(fileName));
1864         }
1865 
InitStructures()1866         private void InitStructures()
1867         {
1868             cpuById.Clear();
1869             idByCpu.Clear();
1870             hooksOnRead.Clear();
1871             hooksOnWrite.Clear();
1872             pcCache.Invalidate();
1873             globalLookup = new SymbolLookup();
1874             localLookups = new Dictionary<ICPU, SymbolLookup>();
1875             cachedCpuId = new ThreadLocal<int>();
1876             peripheralsCollectionByContext = new ContextKeyDictionary<PeripheralCollection, IReadOnlyPeripheralCollection>(() => new PeripheralCollection(this));
1877             lockedPeripherals = new HashSet<IPeripheral>();
1878             lockedRangesCollectionByContext = new ContextKeyDictionary<MinimalRangesCollection, IReadOnlyMinimalRangesCollection>(() => new MinimalRangesCollection());
1879             mappingsForPeripheral = new Dictionary<IBusPeripheral, List<MappedSegmentWrapper>>();
1880             tags = new Dictionary<Range, TagEntry>();
1881             svdDevices = new List<SVDParser>();
1882             pausingTags = new HashSet<string>();
1883             PostDeserializationInitStructures();
1884         }
1885 
ObtainMemoryList()1886         private List<MappedMemory> ObtainMemoryList()
1887         {
1888             return allPeripherals.SelectMany(x => x.Peripherals).Where(x => x.Peripheral is MappedMemory).OrderBy(x => x.RegistrationPoint.Range.StartAddress).
1889                 Select(x => x.Peripheral).Cast<MappedMemory>().Distinct().ToList();
1890         }
1891 
AddMappings(IEnumerable<MappedSegmentWrapper> newMappings, IBusPeripheral owner)1892         private void AddMappings(IEnumerable<MappedSegmentWrapper> newMappings, IBusPeripheral owner)
1893         {
1894             using(Machine.ObtainPausedState(true))
1895             {
1896                 lock(cpuSync)
1897                 {
1898                     var mappingsList = newMappings.ToList();
1899                     if(mappingsForPeripheral.ContainsKey(owner))
1900                     {
1901                         mappingsForPeripheral[owner].AddRange(newMappings);
1902                     }
1903                     else
1904                     {
1905                         mappingsForPeripheral[owner] = mappingsList;
1906                     }
1907                     // Old mappings are given to the CPU in the moment of its registration.
1908                     foreach(var mapping in mappingsList)
1909                     {
1910                         foreach(var cpu in GetCPUsForContext<ICPUWithMappedMemory>(mapping.Context))
1911                         {
1912                             cpu.MapMemory(mapping);
1913                         }
1914                     }
1915                 }
1916             }
1917         }
1918 
AddMappingsForPeripheral(IBusPeripheral peripheral, BusRangeRegistration registrationPoint, IPeripheral context)1919         private void AddMappingsForPeripheral(IBusPeripheral peripheral, BusRangeRegistration registrationPoint, IPeripheral context)
1920         {
1921             var mappedPeripheral = peripheral as IMapped;
1922             if(mappedPeripheral == null) // The context can be null to map it globally
1923             {
1924                 return;
1925             }
1926             var cpuWithMappedMemory = context as ICPUWithMappedMemory;
1927             var segments = mappedPeripheral.MappedSegments;
1928             var mappings = segments.Select(x => FromRegistrationPointToSegmentWrapper(x, registrationPoint, cpuWithMappedMemory)).Where(x => x != null);
1929             AddMappings(mappings, mappedPeripheral);
1930         }
1931 
UnregisterAccessFlags(BusRangeRegistration registrationPoint, ICPU context)1932         private void UnregisterAccessFlags(BusRangeRegistration registrationPoint, ICPU context)
1933         {
1934             foreach(var cpu in GetCPUsForContext<ICPUWithMappedMemory>(context))
1935             {
1936                 var range = registrationPoint.Range;
1937                 cpu.RegisterAccessFlags(range.StartAddress, range.Size);
1938             }
1939         }
1940 
RemoveMappingsForPeripheral(IBusPeripheral peripheral)1941         private bool RemoveMappingsForPeripheral(IBusPeripheral peripheral)
1942         {
1943             if(!mappingsForPeripheral.ContainsKey(peripheral))
1944             {
1945                 return false;
1946             }
1947             foreach(var mapping in mappingsForPeripheral[peripheral])
1948             {
1949                 UnmapMemory(new Range(mapping.StartingOffset, mapping.Size));
1950             }
1951             mappingsForPeripheral.Remove(peripheral);
1952             return true;
1953         }
1954 
TryGetTag(ulong address, out ulong defaultValue)1955         private string TryGetTag(ulong address, out ulong defaultValue)
1956         {
1957             // The `return` inside is intentional; we just want to find the first tag.
1958             // `FirstOrDefault` isn't used cause the default `Range` is a valid `<0, 0>` range.
1959             // `Any` + `First` aren't used to avoid analyzing `tags` keys up to the matching one twice.
1960             // `ToArray` isn't used to avoid analyzing all `tags` keys since we only use the first one.
1961             foreach(var tag in tags.Where(x => x.Key.Contains(address)).Select(x => x.Value))
1962             {
1963                 defaultValue = tag.DefaultValue;
1964                 return tag.Name;
1965             }
1966             defaultValue = default(ulong);
1967             return null;
1968         }
1969 
EnterTag(string str, ulong address, out bool tagEntered, out ulong defaultValue)1970         private string EnterTag(string str, ulong address, out bool tagEntered, out ulong defaultValue)
1971         {
1972             // TODO: also pausing here in a bit hacky way
1973             var tag = TryGetTag(address, out defaultValue);
1974             if(tag == null)
1975             {
1976                 tagEntered = false;
1977                 return str;
1978             }
1979             tagEntered = true;
1980             if(pausingTags.Contains(tag))
1981             {
1982                 Machine.Pause();
1983             }
1984             return string.Format("(tag: '{0}') {1}", tag, str);
1985         }
1986 
ReportNonExistingRead(ulong address, SysbusAccessWidth type)1987         private ulong ReportNonExistingRead(ulong address, SysbusAccessWidth type)
1988         {
1989             Interlocked.Increment(ref unexpectedReads);
1990             bool tagged;
1991             ulong defaultValue;
1992             var warning = EnterTag(NonExistingRead, address, out tagged, out defaultValue);
1993             warning = DecorateWithCPUNameAndPC(warning);
1994             if(UnhandledAccessBehaviour == UnhandledAccessBehaviour.DoNotReport)
1995             {
1996                 return defaultValue;
1997             }
1998             if((UnhandledAccessBehaviour == UnhandledAccessBehaviour.ReportIfTagged && !tagged)
1999                 || (UnhandledAccessBehaviour == UnhandledAccessBehaviour.ReportIfNotTagged && tagged))
2000             {
2001                 return defaultValue;
2002             }
2003             if(tagged)
2004             {
2005                 //strange parsing of default value ensures we get the right width, depending on the access type
2006                 this.Log(LogLevel.Warning, warning.TrimEnd('.') + ", returning 0x{2}.", address, type, defaultValue.ToString("X16").Substring(16 - (int)type * 2));
2007             }
2008             else
2009             {
2010                 ulong value;
2011                 foreach(var svdDevice in svdDevices)
2012                 {
2013                     if(svdDevice.TryReadAccess(address, out value, type))
2014                     {
2015                         return value;
2016                     }
2017                 }
2018                 this.Log(LogLevel.Warning, warning, address, type);
2019             }
2020             return defaultValue;
2021         }
2022 
ReportNonExistingWrite(ulong address, ulong value, SysbusAccessWidth type)2023         private void ReportNonExistingWrite(ulong address, ulong value, SysbusAccessWidth type)
2024         {
2025             Interlocked.Increment(ref unexpectedWrites);
2026             if(UnhandledAccessBehaviour == UnhandledAccessBehaviour.DoNotReport)
2027             {
2028                 return;
2029             }
2030             bool tagged;
2031             var warning = EnterTag(NonExistingWrite, address, out tagged, out var _);
2032             warning = DecorateWithCPUNameAndPC(warning);
2033             if((UnhandledAccessBehaviour == UnhandledAccessBehaviour.ReportIfTagged && !tagged)
2034                 || (UnhandledAccessBehaviour == UnhandledAccessBehaviour.ReportIfNotTagged && tagged))
2035             {
2036                 return;
2037             }
2038             foreach(var svdDevice in svdDevices)
2039             {
2040                 if(svdDevice.TryWriteAccess(address, value, type))
2041                 {
2042                     return;
2043                 }
2044             }
2045             this.Log(LogLevel.Warning, warning, address, value, type);
2046         }
2047 
SetLocalContext(IPeripheral context, ulong? initiatorState = null)2048         private IDisposable SetLocalContext(IPeripheral context, ulong? initiatorState = null)
2049         {
2050             return threadLocalContext.Initialize(context, initiatorState);
2051         }
2052 
AddContextKeys(IPeripheral peripheral)2053         private void AddContextKeys(IPeripheral peripheral)
2054         {
2055             peripheralsCollectionByContext.AddContextKey(peripheral);
2056             lockedRangesCollectionByContext.AddContextKey(peripheral);
2057         }
2058 
RemoveContextKeys(IPeripheral peripheral)2059         private void RemoveContextKeys(IPeripheral peripheral)
2060         {
2061             peripheralsCollectionByContext.RemoveContextKey(peripheral);
2062             lockedRangesCollectionByContext.RemoveContextKey(peripheral);
2063         }
2064 
2065         [PostDeserialization]
PostDeserializationInitStructures()2066         private void PostDeserializationInitStructures()
2067         {
2068             threadLocalContext = new ThreadLocalContext(this);
2069         }
2070 
FromRegistrationPointToSegmentWrapper(IMappedSegment segment, BusRangeRegistration registrationPoint, ICPUWithMappedMemory context)2071         private static MappedSegmentWrapper FromRegistrationPointToSegmentWrapper(IMappedSegment segment, BusRangeRegistration registrationPoint, ICPUWithMappedMemory context)
2072         {
2073             if(segment.StartingOffset >= registrationPoint.Range.Size + registrationPoint.Offset)
2074             {
2075                 return null;
2076             }
2077 
2078             var desiredSize = Math.Min(segment.Size, registrationPoint.Range.Size + registrationPoint.Offset - segment.StartingOffset);
2079             return new MappedSegmentWrapper(segment, registrationPoint.Range.StartAddress - registrationPoint.Offset, desiredSize, context);
2080         }
2081 
2082         private IEnumerable<PeripheralCollection> allPeripherals => peripheralsCollectionByContext.GetAllDistinctValues();
2083 
2084         private ISet<IPeripheral> lockedPeripherals;
2085 
2086         private ContextKeyDictionary<PeripheralCollection, IReadOnlyPeripheralCollection> peripheralsCollectionByContext;
2087         private ContextKeyDictionary<MinimalRangesCollection, IReadOnlyMinimalRangesCollection> lockedRangesCollectionByContext;
2088 
2089         // It doesn't implement IDictionary because serialization doesn't work correctly for dictionaries without two generic parameters.
2090         private class ContextKeyDictionary<TValue, TReadOnlyValue> where TValue : TReadOnlyValue, ICoalescable<TValue> where TReadOnlyValue : class
2091         {
ContextKeyDictionary(Func<TValue> defaultFactory)2092             public ContextKeyDictionary(Func<TValue> defaultFactory)
2093             {
2094                 this.defaultFactory = defaultFactory;
2095                 globalAllAccess = globalValue[StateMask.AllAccess] = defaultFactory();
2096             }
2097 
2098             // Adding the context key might happen on peripheral registration, or on first use.
AddContextKey(IPeripheral key)2099             public void AddContextKey(IPeripheral key)
2100             {
2101                 if(key == null)
2102                 {
2103                     throw new ArgumentNullException(nameof(key));
2104                 }
2105                 if(cpuLocalValues.ContainsKey(key))
2106                 {
2107                     return;
2108                 }
2109                 cpuLocalValues[key] = new Dictionary<StateMask, TValue>();
2110                 cpuAllAccess[key] = cpuLocalValues[key][StateMask.AllAccess] = defaultFactory();
2111             }
2112 
RemoveContextKey(IPeripheral key)2113             public void RemoveContextKey(IPeripheral key)
2114             {
2115                 if(key == null)
2116                 {
2117                     throw new ArgumentNullException(nameof(key));
2118                 }
2119                 cpuLocalValues.Remove(key);
2120                 DropCaches(); // has the effect of dropping caches when a peripheral is unregistered
2121             }
2122 
2123             public TReadOnlyValue this[IPeripheral key] => GetValue(key);
2124 
GetValue(IPeripheral key, ulong? initiatorState = null)2125             public TReadOnlyValue GetValue(IPeripheral key, ulong? initiatorState = null)
2126             {
2127                 if(!TryGetValue(key, initiatorState, out var value))
2128                 {
2129                     throw new RecoverableException($"{typeof(TValue).Name} not found for the given context: {key.GetName()} in state: {initiatorState}");
2130                 }
2131                 return value;
2132             }
2133 
2134             // Perform a mutation on the value collection for a specific initiator state and mask pair (for example to add a new peripheral)
WithStateCollection(IPeripheral context, StateMask? stateMask, Action<TValue> action)2135             public void WithStateCollection(IPeripheral context, StateMask? stateMask, Action<TValue> action)
2136             {
2137                 var collection = context == null ? globalValue : cpuLocalValues[context];
2138                 var effectiveMask = stateMask ?? StateMask.AllAccess;
2139                 if(!collection.ContainsKey(effectiveMask))
2140                 {
2141                     collection[effectiveMask] = defaultFactory();
2142                 }
2143                 action(collection[effectiveMask]);
2144                 DropCaches();
2145             }
2146 
GetAllDistinctValues()2147             public IEnumerable<TValue> GetAllDistinctValues()
2148             {
2149                 return cpuLocalValues.Values.SelectMany(v => v.Values).Concat(globalValue.Values).Distinct();
2150             }
2151 
GetAllContextKeys()2152             public IEnumerable<IPeripheral> GetAllContextKeys()
2153             {
2154                 return cpuLocalValues.Keys;
2155             }
2156 
GetAllStateKeys(IPeripheral context)2157             public IEnumerable<StateMask> GetAllStateKeys(IPeripheral context)
2158             {
2159                 return (context == null ? globalValue : cpuLocalValues[context]).Keys;
2160             }
2161 
TryGetValue(IPeripheral key, ulong? initiatorState, out TValue value)2162             public bool TryGetValue(IPeripheral key, ulong? initiatorState, out TValue value)
2163             {
2164                 if(!initiatorState.HasValue)
2165                 {
2166                     if(key == null)
2167                     {
2168                         value = globalAllAccess;
2169                         return true;
2170                     }
2171                     // Handle reads initiated by peripherals that are never used as a context and thus have no collections.
2172                     // Then this will fail and it's up to the caller to fall back to the global collection.
2173                     return cpuAllAccess.TryGetValue(key, out value);
2174                 }
2175                 if(key != null && !cpuInStateCache.ContainsKey(key))
2176                 {
2177                     cpuInStateCache[key] = new Dictionary<ulong, TValue>();
2178                 }
2179                 var cache = key == null ? globalCache : cpuInStateCache[key];
2180                 if(cache.TryGetValue(initiatorState.Value, out var cachedValue))
2181                 {
2182                     value = cachedValue;
2183                     return true;
2184                 }
2185                 else
2186                 {
2187                     cache[initiatorState.Value] = cachedValue = defaultFactory();
2188                     return TryGetValueForState(cachedValue, key, initiatorState, out value);
2189                 }
2190             }
2191 
TryGetValueForState(TValue cachedValue, IPeripheral key, ulong? initiatorState, out TValue value)2192             private bool TryGetValueForState(TValue cachedValue, IPeripheral key, ulong? initiatorState, out TValue value)
2193             {
2194                 // The core of the state filtering logic. Look up which elements fit the current initiator state.
2195                 // Then join all found state-specific collections of elements into the cached one for this state.
2196                 var collectionToSearch = globalValue.AsEnumerable();
2197                 if(key != null && cpuLocalValues.TryGetValue(key, out var thisCpuValues))
2198                 {
2199                     // Note that in order for the 'local peripheral overrides the global one' behavior to work as intended,
2200                     // we must check the local peripherals first. That way they 'reserve' their spot in the cache. This function
2201                     // also checks the global collection when called with a context argument, but it does it only after going
2202                     // through the context-specific one to ensure this.
2203                     collectionToSearch = thisCpuValues.Concat(collectionToSearch);
2204                 }
2205                 bool anyHit = false;
2206                 foreach(var pair in collectionToSearch)
2207                 {
2208                     var stateMask = pair.Key;
2209                     if((initiatorState.Value & stateMask.Mask) == stateMask.State)
2210                     {
2211                         anyHit = true;
2212                         /// <see cref="PeripheralCollection.Coalesce"> doesn't add overlapping peripherals. This means that for the 'local peripheral
2213                         /// overrides the global one' behavior to work as intended we must check the local peripherals first, as we did above.
2214                         cachedValue.Coalesce(pair.Value);
2215                     }
2216                 }
2217                 value = anyHit ? cachedValue : default(TValue);
2218                 return anyHit;
2219             }
2220 
DropCaches()2221             private void DropCaches()
2222             {
2223                 cpuInStateCache.Clear();
2224                 globalCache.Clear();
2225             }
2226 
2227             private readonly Dictionary<IPeripheral, Dictionary<StateMask, TValue>> cpuLocalValues = new Dictionary<IPeripheral, Dictionary<StateMask, TValue>>();
2228             private readonly Dictionary<IPeripheral, Dictionary<ulong, TValue>> cpuInStateCache = new Dictionary<IPeripheral, Dictionary<ulong, TValue>>();
2229             private readonly Func<TValue> defaultFactory;
2230             private readonly Dictionary<StateMask, TValue> globalValue = new Dictionary<StateMask, TValue>();
2231             private readonly Dictionary<ulong, TValue> globalCache = new Dictionary<ulong, TValue>();
2232             // Please take performance into account before removing these caches (they were added because looking up a value
2233             // in a Dictionary<StateMask, TValue> was very slow)
2234             private readonly Dictionary<IPeripheral, TValue> cpuAllAccess = new Dictionary<IPeripheral, TValue>();
2235             private readonly TValue globalAllAccess;
2236         }
2237 
2238         private Dictionary<IBusPeripheral, List<MappedSegmentWrapper>> mappingsForPeripheral;
2239         private bool mappingsRemoved;
2240         private bool peripheralRegistered;
2241         private Endianess endianess;
2242         private bool hasCpuWithMismatchedEndianness;
2243         private readonly Dictionary<ICPU, int> idByCpu;
2244         private readonly Dictionary<int, ICPU> cpuById;
2245         private readonly Dictionary<ulong, List<BusHookHandler>> hooksOnRead;
2246         private readonly Dictionary<ulong, List<BusHookHandler>> hooksOnWrite;
2247 
2248         [Constructor]
2249         private ThreadLocal<int> cachedCpuId;
2250         [Transient]
2251         private ThreadLocalContext threadLocalContext;
2252         private object cpuSync;
2253         private SymbolLookup globalLookup;
2254         private Dictionary<ICPU, SymbolLookup> localLookups;
2255         private Dictionary<Range, TagEntry> tags;
2256         private List<SVDParser> svdDevices;
2257         private HashSet<string> pausingTags;
2258         private readonly List<BinaryFingerprint> binaryFingerprints;
2259         private const string NonExistingRead = "Read{1} from non existing peripheral at 0x{0:X}.";
2260         private const string NonExistingWrite = "Write{2} to non existing peripheral at 0x{0:X}, value 0x{1:X}.";
2261         private const string IOExceptionMessage = "I/O error while loading ELF: {0}.";
2262         private const string CantFindCpuIdMessage = "Can't verify current CPU in the given context.";
2263 
2264         private int unexpectedReads;
2265         private int unexpectedWrites;
2266 
2267         private LRUCache<ulong, Tuple<string, Symbol>> pcCache = new LRUCache<ulong, Tuple<string, Symbol>>(10000);
2268 
2269         public class MappedSegmentWrapper : IMappedSegment
2270         {
MappedSegmentWrapper(IMappedSegment wrappedSegment, ulong peripheralOffset, ulong maximumSize, ICPUWithMappedMemory context)2271             public MappedSegmentWrapper(IMappedSegment wrappedSegment, ulong peripheralOffset, ulong maximumSize, ICPUWithMappedMemory context)
2272             {
2273                 this.wrappedSegment = wrappedSegment;
2274                 this.peripheralOffset = peripheralOffset;
2275                 usedSize = Math.Min(maximumSize, wrappedSegment.Size);
2276                 this.context = context;
2277             }
2278 
Touch()2279             public void Touch()
2280             {
2281                 wrappedSegment.Touch();
2282             }
2283 
ToString()2284             public override string ToString()
2285             {
2286                 return string.Format("[MappedSegmentWrapper: StartingOffset=0x{0:X}, Size=0x{1:X}, OriginalStartingOffset=0x{2:X}, PeripheralOffset=0x{3:X}, Context={4}]",
2287                     StartingOffset, Size, OriginalStartingOffset, PeripheralOffset, context);
2288             }
2289 
2290             public ICPUWithMappedMemory Context
2291             {
2292                 get
2293                 {
2294                     return context;
2295                 }
2296             }
2297 
2298             public ulong StartingOffset
2299             {
2300                 get
2301                 {
2302                     return peripheralOffset + wrappedSegment.StartingOffset;
2303                 }
2304             }
2305 
2306             public ulong Size
2307             {
2308                 get
2309                 {
2310                     return usedSize;
2311                 }
2312             }
2313 
2314             public IntPtr Pointer
2315             {
2316                 get
2317                 {
2318                     return wrappedSegment.Pointer;
2319                 }
2320             }
2321 
2322             public ulong OriginalStartingOffset
2323             {
2324                 get
2325                 {
2326                     return wrappedSegment.StartingOffset;
2327                 }
2328             }
2329 
2330             public ulong PeripheralOffset
2331             {
2332                 get
2333                 {
2334                     return peripheralOffset;
2335                 }
2336             }
2337 
Equals(object obj)2338             public override bool Equals(object obj)
2339             {
2340                 var objAsMappedSegmentWrapper = obj as MappedSegmentWrapper;
2341                 if(objAsMappedSegmentWrapper == null)
2342                 {
2343                     return false;
2344                 }
2345 
2346                 return wrappedSegment.Equals(objAsMappedSegmentWrapper.wrappedSegment)
2347                     && peripheralOffset == objAsMappedSegmentWrapper.peripheralOffset
2348                     && usedSize == objAsMappedSegmentWrapper.usedSize;
2349             }
2350 
GetHashCode()2351             public override int GetHashCode()
2352             {
2353                 unchecked
2354                 {
2355                     var hash = 17;
2356                     hash = hash * 23 + wrappedSegment.GetHashCode();
2357                     hash = hash * 23 + (int)peripheralOffset;
2358                     hash = hash * 23 + (int)usedSize;
2359                     return hash;
2360                 }
2361             }
2362 
2363             private readonly IMappedSegment wrappedSegment;
2364             private readonly ulong peripheralOffset;
2365             private readonly ulong usedSize;
2366             private readonly ICPUWithMappedMemory context;
2367         }
2368 
2369         private struct PeripheralLookupResult
2370         {
2371             public IBusRegistered<IBusPeripheral> What;
2372             public ulong Offset;
2373             public ulong SourceIndex;
2374             public ulong SourceLength;
2375         }
2376 
2377         private struct TagEntry
2378         {
2379             public string Name;
2380             public ulong DefaultValue;
2381         }
2382 
2383         private class ThreadLocalContext : IDisposable
2384         {
ThreadLocalContext(IBusController parent)2385             public ThreadLocalContext(IBusController parent)
2386             {
2387                 context = new ThreadLocal<Tuple<IPeripheral, ulong?>>(() => null, true);
2388                 emptyDisposable.Disable();
2389             }
2390 
Initialize(IPeripheral cpu, ulong? initiatorState)2391             public IDisposable Initialize(IPeripheral cpu, ulong? initiatorState)
2392             {
2393                 if(cpu == null)
2394                 {
2395                     return emptyDisposable;
2396                 }
2397                 var previousContext = context.Value;
2398                 context.Value = Tuple.Create(cpu, initiatorState);
2399                 return DisposableWrapper.New(() =>
2400                 {
2401                     context.Value = previousContext;
2402                 });
2403             }
2404 
Dispose()2405             public void Dispose()
2406             {
2407                 context.Dispose();
2408             }
2409 
2410             public bool InUse => context.Value != null;
2411             public IPeripheral Initiator => context.Value.Item1;
2412             public ulong? InitiatorState => context.Value.Item2;
2413 
2414             private readonly ThreadLocal<Tuple<IPeripheral, ulong?>> context;
2415 
2416             private static readonly DisposableWrapper emptyDisposable = new DisposableWrapper();
2417         }
2418     }
2419 }
2420 
2421