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