HvExecutionContext.cs 8.8 KB

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