HvVcpuPool.cs 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. using System;
  2. using System.Threading;
  3. namespace Ryujinx.Cpu.AppleHv
  4. {
  5. class HvVcpuPool
  6. {
  7. // Since there's a limit on the number of VCPUs we can create,
  8. // and we assign one VCPU per guest thread, we need to ensure
  9. // there are enough VCPUs available for at least the maximum number of active guest threads.
  10. // To do that, we always destroy and re-create VCPUs that are above a given limit.
  11. // Those VCPUs are called "ephemeral" here because they are not kept for long.
  12. //
  13. // In the future, we might want to consider a smarter approach that only makes
  14. // VCPUs for threads that are not running frequently "ephemeral", but this is
  15. // complicated because VCPUs can only be destroyed by the same thread that created them.
  16. private const int MaxActiveVcpus = 4;
  17. public static readonly HvVcpuPool Instance = new HvVcpuPool();
  18. private int _totalVcpus;
  19. private int _maxVcpus;
  20. public HvVcpuPool()
  21. {
  22. HvApi.hv_vm_get_max_vcpu_count(out uint maxVcpuCount).ThrowOnError();
  23. _maxVcpus = (int)maxVcpuCount;
  24. }
  25. public HvVcpu Create(HvAddressSpace addressSpace, IHvExecutionContext shadowContext, Action<IHvExecutionContext> swapContext)
  26. {
  27. HvVcpu vcpu = CreateNew(addressSpace, shadowContext);
  28. vcpu.NativeContext.Load(shadowContext);
  29. swapContext(vcpu.NativeContext);
  30. return vcpu;
  31. }
  32. public void Destroy(HvVcpu vcpu, Action<IHvExecutionContext> swapContext)
  33. {
  34. vcpu.ShadowContext.Load(vcpu.NativeContext);
  35. swapContext(vcpu.ShadowContext);
  36. DestroyVcpu(vcpu);
  37. }
  38. public void Return(HvVcpu vcpu, Action<IHvExecutionContext> swapContext)
  39. {
  40. if (vcpu.IsEphemeral)
  41. {
  42. Destroy(vcpu, swapContext);
  43. }
  44. }
  45. public HvVcpu Rent(HvAddressSpace addressSpace, IHvExecutionContext shadowContext, HvVcpu vcpu, Action<IHvExecutionContext> swapContext)
  46. {
  47. if (vcpu.IsEphemeral)
  48. {
  49. return Create(addressSpace, shadowContext, swapContext);
  50. }
  51. else
  52. {
  53. return vcpu;
  54. }
  55. }
  56. private unsafe HvVcpu CreateNew(HvAddressSpace addressSpace, IHvExecutionContext shadowContext)
  57. {
  58. int newCount = IncrementVcpuCount();
  59. bool isEphemeral = newCount > _maxVcpus - MaxActiveVcpus;
  60. // Create VCPU.
  61. hv_vcpu_exit_t* exitInfo = null;
  62. HvApi.hv_vcpu_create(out ulong vcpuHandle, ref exitInfo, IntPtr.Zero).ThrowOnError();
  63. // Enable FP and SIMD instructions.
  64. HvApi.hv_vcpu_set_sys_reg(vcpuHandle, hv_sys_reg_t.HV_SYS_REG_CPACR_EL1, 0b11 << 20).ThrowOnError();
  65. addressSpace.InitializeMmu(vcpuHandle);
  66. HvExecutionContextVcpu nativeContext = new HvExecutionContextVcpu(vcpuHandle);
  67. HvVcpu vcpu = new HvVcpu(vcpuHandle, exitInfo, shadowContext, nativeContext, isEphemeral);
  68. return vcpu;
  69. }
  70. private void DestroyVcpu(HvVcpu vcpu)
  71. {
  72. HvApi.hv_vcpu_destroy(vcpu.Handle).ThrowOnError();
  73. DecrementVcpuCount();
  74. }
  75. private int IncrementVcpuCount()
  76. {
  77. return Interlocked.Increment(ref _totalVcpus);
  78. }
  79. private void DecrementVcpuCount()
  80. {
  81. Interlocked.Decrement(ref _totalVcpus);
  82. }
  83. }
  84. }