HvExecutionContext.cs 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. using ARMeilleure.State;
  2. using Ryujinx.Cpu.AppleHv.Arm;
  3. using Ryujinx.Memory.Tracking;
  4. using System;
  5. using System.Runtime.Versioning;
  6. using System.Threading;
  7. namespace Ryujinx.Cpu.AppleHv
  8. {
  9. [SupportedOSPlatform("macos")]
  10. class HvExecutionContext : IExecutionContext
  11. {
  12. /// <inheritdoc/>
  13. public ulong Pc => _impl.ElrEl1;
  14. /// <inheritdoc/>
  15. public long TpidrEl0
  16. {
  17. get => _impl.TpidrEl0;
  18. set => _impl.TpidrEl0 = value;
  19. }
  20. /// <inheritdoc/>
  21. public long TpidrroEl0
  22. {
  23. get => _impl.TpidrroEl0;
  24. set => _impl.TpidrroEl0 = value;
  25. }
  26. /// <inheritdoc/>
  27. public uint Pstate
  28. {
  29. get => _impl.Pstate;
  30. set => _impl.Pstate = value;
  31. }
  32. /// <inheritdoc/>
  33. public uint Fpcr
  34. {
  35. get => _impl.Fpcr;
  36. set => _impl.Fpcr = value;
  37. }
  38. /// <inheritdoc/>
  39. public uint Fpsr
  40. {
  41. get => _impl.Fpsr;
  42. set => _impl.Fpsr = value;
  43. }
  44. /// <inheritdoc/>
  45. public bool IsAarch32
  46. {
  47. get => false;
  48. set
  49. {
  50. if (value)
  51. {
  52. throw new NotSupportedException();
  53. }
  54. }
  55. }
  56. /// <inheritdoc/>
  57. public bool Running { get; private set; }
  58. private readonly ICounter _counter;
  59. private readonly IHvExecutionContext _shadowContext;
  60. private IHvExecutionContext _impl;
  61. private readonly ExceptionCallbacks _exceptionCallbacks;
  62. private int _interruptRequested;
  63. public HvExecutionContext(ICounter counter, ExceptionCallbacks exceptionCallbacks)
  64. {
  65. _counter = counter;
  66. _shadowContext = new HvExecutionContextShadow();
  67. _impl = _shadowContext;
  68. _exceptionCallbacks = exceptionCallbacks;
  69. Running = true;
  70. }
  71. /// <inheritdoc/>
  72. public ulong GetX(int index) => _impl.GetX(index);
  73. /// <inheritdoc/>
  74. public void SetX(int index, ulong value) => _impl.SetX(index, value);
  75. /// <inheritdoc/>
  76. public V128 GetV(int index) => _impl.GetV(index);
  77. /// <inheritdoc/>
  78. public void SetV(int index, V128 value) => _impl.SetV(index, value);
  79. private void InterruptHandler()
  80. {
  81. _exceptionCallbacks.InterruptCallback?.Invoke(this);
  82. }
  83. private void BreakHandler(ulong address, int imm)
  84. {
  85. _exceptionCallbacks.BreakCallback?.Invoke(this, address, imm);
  86. }
  87. private void SupervisorCallHandler(ulong address, int imm)
  88. {
  89. _exceptionCallbacks.SupervisorCallback?.Invoke(this, address, imm);
  90. }
  91. private void UndefinedHandler(ulong address, int opCode)
  92. {
  93. _exceptionCallbacks.UndefinedCallback?.Invoke(this, address, opCode);
  94. }
  95. /// <inheritdoc/>
  96. public void RequestInterrupt()
  97. {
  98. if (Interlocked.Exchange(ref _interruptRequested, 1) == 0 && _impl is HvExecutionContextVcpu impl)
  99. {
  100. impl.RequestInterrupt();
  101. }
  102. }
  103. private bool GetAndClearInterruptRequested()
  104. {
  105. return Interlocked.Exchange(ref _interruptRequested, 0) != 0;
  106. }
  107. /// <inheritdoc/>
  108. public void StopRunning()
  109. {
  110. Running = false;
  111. RequestInterrupt();
  112. }
  113. public unsafe void Execute(HvMemoryManager memoryManager, ulong address)
  114. {
  115. HvVcpu vcpu = HvVcpuPool.Instance.Create(memoryManager.AddressSpace, _shadowContext, SwapContext);
  116. HvApi.hv_vcpu_set_reg(vcpu.Handle, HvReg.PC, address).ThrowOnError();
  117. while (Running)
  118. {
  119. HvApi.hv_vcpu_run(vcpu.Handle).ThrowOnError();
  120. HvExitReason reason = vcpu.ExitInfo->Reason;
  121. if (reason == HvExitReason.Exception)
  122. {
  123. uint hvEsr = (uint)vcpu.ExitInfo->Exception.Syndrome;
  124. ExceptionClass hvEc = (ExceptionClass)(hvEsr >> 26);
  125. if (hvEc != ExceptionClass.HvcAarch64)
  126. {
  127. throw new Exception($"Unhandled exception from guest kernel with ESR 0x{hvEsr:X} ({hvEc}).");
  128. }
  129. address = SynchronousException(memoryManager, ref vcpu);
  130. HvApi.hv_vcpu_set_reg(vcpu.Handle, HvReg.PC, address).ThrowOnError();
  131. }
  132. else if (reason == HvExitReason.Canceled || reason == HvExitReason.VTimerActivated)
  133. {
  134. if (GetAndClearInterruptRequested())
  135. {
  136. ReturnToPool(vcpu);
  137. InterruptHandler();
  138. vcpu = RentFromPool(memoryManager.AddressSpace, vcpu);
  139. }
  140. if (reason == HvExitReason.VTimerActivated)
  141. {
  142. vcpu.EnableAndUpdateVTimer();
  143. // Unmask VTimer interrupts.
  144. HvApi.hv_vcpu_set_vtimer_mask(vcpu.Handle, false).ThrowOnError();
  145. }
  146. }
  147. else
  148. {
  149. throw new Exception($"Unhandled exit reason {reason}.");
  150. }
  151. }
  152. HvVcpuPool.Instance.Destroy(vcpu, SwapContext);
  153. }
  154. private ulong SynchronousException(HvMemoryManager memoryManager, ref HvVcpu vcpu)
  155. {
  156. ulong vcpuHandle = vcpu.Handle;
  157. HvApi.hv_vcpu_get_sys_reg(vcpuHandle, HvSysReg.ELR_EL1, out ulong elr).ThrowOnError();
  158. HvApi.hv_vcpu_get_sys_reg(vcpuHandle, HvSysReg.ESR_EL1, out ulong esr).ThrowOnError();
  159. ExceptionClass ec = (ExceptionClass)((uint)esr >> 26);
  160. switch (ec)
  161. {
  162. case ExceptionClass.DataAbortLowerEl:
  163. DataAbort(memoryManager.Tracking, vcpuHandle, (uint)esr);
  164. break;
  165. case ExceptionClass.TrappedMsrMrsSystem:
  166. InstructionTrap((uint)esr);
  167. HvApi.hv_vcpu_set_sys_reg(vcpuHandle, HvSysReg.ELR_EL1, elr + 4UL).ThrowOnError();
  168. break;
  169. case ExceptionClass.SvcAarch64:
  170. ReturnToPool(vcpu);
  171. ushort id = (ushort)esr;
  172. SupervisorCallHandler(elr - 4UL, id);
  173. vcpu = RentFromPool(memoryManager.AddressSpace, vcpu);
  174. break;
  175. default:
  176. throw new Exception($"Unhandled guest exception {ec}.");
  177. }
  178. // Make sure we will continue running at EL0.
  179. if (memoryManager.AddressSpace.GetAndClearUserTlbInvalidationPending())
  180. {
  181. // TODO: Invalidate only the range that was modified?
  182. return HvAddressSpace.KernelRegionTlbiEretAddress;
  183. }
  184. else
  185. {
  186. return HvAddressSpace.KernelRegionEretAddress;
  187. }
  188. }
  189. private static void DataAbort(MemoryTracking tracking, ulong vcpu, uint esr)
  190. {
  191. bool write = (esr & (1u << 6)) != 0;
  192. bool farValid = (esr & (1u << 10)) == 0;
  193. int accessSizeLog2 = (int)((esr >> 22) & 3);
  194. if (farValid)
  195. {
  196. HvApi.hv_vcpu_get_sys_reg(vcpu, HvSysReg.FAR_EL1, out ulong far).ThrowOnError();
  197. ulong size = 1UL << accessSizeLog2;
  198. if (!tracking.VirtualMemoryEvent(far, size, write))
  199. {
  200. string rw = write ? "write" : "read";
  201. throw new Exception($"Unhandled invalid memory access at VA 0x{far:X} with size 0x{size:X} ({rw}).");
  202. }
  203. }
  204. else
  205. {
  206. throw new Exception($"Unhandled invalid memory access at unknown VA with ESR 0x{esr:X}.");
  207. }
  208. }
  209. private void InstructionTrap(uint esr)
  210. {
  211. bool read = (esr & 1) != 0;
  212. uint rt = (esr >> 5) & 0x1f;
  213. if (read)
  214. {
  215. // Op0 Op2 Op1 CRn 00000 CRm
  216. switch ((esr >> 1) & 0x1ffe0f)
  217. {
  218. case 0b11_000_011_1110_00000_0000: // CNTFRQ_EL0
  219. WriteRt(rt, _counter.Frequency);
  220. break;
  221. case 0b11_001_011_1110_00000_0000: // CNTPCT_EL0
  222. WriteRt(rt, _counter.Counter);
  223. break;
  224. default:
  225. throw new Exception($"Unhandled system register read with ESR 0x{esr:X}");
  226. }
  227. }
  228. else
  229. {
  230. throw new Exception($"Unhandled system register write with ESR 0x{esr:X}");
  231. }
  232. }
  233. private void WriteRt(uint rt, ulong value)
  234. {
  235. if (rt < 31)
  236. {
  237. SetX((int)rt, value);
  238. }
  239. }
  240. private void ReturnToPool(HvVcpu vcpu)
  241. {
  242. HvVcpuPool.Instance.Return(vcpu, SwapContext);
  243. }
  244. private HvVcpu RentFromPool(HvAddressSpace addressSpace, HvVcpu vcpu)
  245. {
  246. return HvVcpuPool.Instance.Rent(addressSpace, _shadowContext, vcpu, SwapContext);
  247. }
  248. private void SwapContext(IHvExecutionContext newContext)
  249. {
  250. _impl = newContext;
  251. }
  252. public void Dispose()
  253. {
  254. }
  255. }
  256. }