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