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