diff --git a/src/Ryujinx.Cpu/AppleHv/HvAddressSpace.cs b/src/Ryujinx.Cpu/AppleHv/HvAddressSpace.cs
index 4785a3f05e..eb7c0ef086 100644
--- a/src/Ryujinx.Cpu/AppleHv/HvAddressSpace.cs
+++ b/src/Ryujinx.Cpu/AppleHv/HvAddressSpace.cs
@@ -1,9 +1,11 @@
using Ryujinx.Cpu.AppleHv.Arm;
using Ryujinx.Memory;
using System;
+using System.Runtime.Versioning;
namespace Ryujinx.Cpu.AppleHv
{
+ [SupportedOSPlatform("macos")]
class HvAddressSpace : IDisposable
{
private const ulong KernelRegionBase = unchecked((ulong)-(1L << 39));
diff --git a/src/Ryujinx.Cpu/AppleHv/HvAddressSpaceRange.cs b/src/Ryujinx.Cpu/AppleHv/HvAddressSpaceRange.cs
index 876334307c..7754431faa 100644
--- a/src/Ryujinx.Cpu/AppleHv/HvAddressSpaceRange.cs
+++ b/src/Ryujinx.Cpu/AppleHv/HvAddressSpaceRange.cs
@@ -2,10 +2,12 @@ using Ryujinx.Cpu.AppleHv.Arm;
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
+using System.Runtime.Versioning;
using System.Threading;
namespace Ryujinx.Cpu.AppleHv
{
+ [SupportedOSPlatform("macos")]
class HvAddressSpaceRange : IDisposable
{
private const ulong AllocationGranule = 1UL << 14;
diff --git a/src/Ryujinx.Cpu/AppleHv/HvApi.cs b/src/Ryujinx.Cpu/AppleHv/HvApi.cs
index 4f74849816..e6e08111f3 100644
--- a/src/Ryujinx.Cpu/AppleHv/HvApi.cs
+++ b/src/Ryujinx.Cpu/AppleHv/HvApi.cs
@@ -1,5 +1,6 @@
using System;
using System.Runtime.InteropServices;
+using System.Runtime.Versioning;
namespace Ryujinx.Cpu.AppleHv
{
@@ -12,10 +13,18 @@ namespace Ryujinx.Cpu.AppleHv
#pragma warning restore CS0649
}
+ enum HvExitReason : uint
+ {
+ Canceled,
+ Exception,
+ VTimerActivated,
+ Unknown,
+ }
+
struct HvVcpuExit
{
#pragma warning disable CS0649 // Field is never assigned to
- public uint Reason;
+ public HvExitReason Reason;
public HvVcpuExitException Exception;
#pragma warning restore CS0649
}
@@ -255,6 +264,7 @@ namespace Ryujinx.Cpu.AppleHv
}
}
+ [SupportedOSPlatform("macos")]
static partial class HvApi
{
public const string LibraryName = "/System/Library/Frameworks/Hypervisor.framework/Hypervisor";
diff --git a/src/Ryujinx.Cpu/AppleHv/HvCpuContext.cs b/src/Ryujinx.Cpu/AppleHv/HvCpuContext.cs
index a58f835913..2c4ff2b64e 100644
--- a/src/Ryujinx.Cpu/AppleHv/HvCpuContext.cs
+++ b/src/Ryujinx.Cpu/AppleHv/HvCpuContext.cs
@@ -1,7 +1,9 @@
using ARMeilleure.Memory;
+using System.Runtime.Versioning;
namespace Ryujinx.Cpu.AppleHv
{
+ [SupportedOSPlatform("macos")]
class HvCpuContext : ICpuContext
{
private readonly ITickSource _tickSource;
diff --git a/src/Ryujinx.Cpu/AppleHv/HvEngine.cs b/src/Ryujinx.Cpu/AppleHv/HvEngine.cs
index 2967857f95..c3c1a4484f 100644
--- a/src/Ryujinx.Cpu/AppleHv/HvEngine.cs
+++ b/src/Ryujinx.Cpu/AppleHv/HvEngine.cs
@@ -1,7 +1,9 @@
using ARMeilleure.Memory;
+using System.Runtime.Versioning;
namespace Ryujinx.Cpu.AppleHv
{
+ [SupportedOSPlatform("macos")]
public class HvEngine : ICpuEngine
{
private readonly ITickSource _tickSource;
diff --git a/src/Ryujinx.Cpu/AppleHv/HvExecutionContext.cs b/src/Ryujinx.Cpu/AppleHv/HvExecutionContext.cs
index 2c9afdc4ed..fc2b76d151 100644
--- a/src/Ryujinx.Cpu/AppleHv/HvExecutionContext.cs
+++ b/src/Ryujinx.Cpu/AppleHv/HvExecutionContext.cs
@@ -2,9 +2,12 @@ using ARMeilleure.State;
using Ryujinx.Cpu.AppleHv.Arm;
using Ryujinx.Memory.Tracking;
using System;
+using System.Runtime.Versioning;
+using System.Threading;
namespace Ryujinx.Cpu.AppleHv
{
+ [SupportedOSPlatform("macos")]
class HvExecutionContext : IExecutionContext
{
///
@@ -67,6 +70,8 @@ namespace Ryujinx.Cpu.AppleHv
private readonly ExceptionCallbacks _exceptionCallbacks;
+ private int _interruptRequested;
+
public HvExecutionContext(ICounter counter, ExceptionCallbacks exceptionCallbacks)
{
_counter = counter;
@@ -111,7 +116,15 @@ namespace Ryujinx.Cpu.AppleHv
///
public void RequestInterrupt()
{
- _impl.RequestInterrupt();
+ if (Interlocked.Exchange(ref _interruptRequested, 1) == 0 && _impl is HvExecutionContextVcpu impl)
+ {
+ impl.RequestInterrupt();
+ }
+ }
+
+ private bool GetAndClearInterruptRequested()
+ {
+ return Interlocked.Exchange(ref _interruptRequested, 0) != 0;
}
///
@@ -131,9 +144,9 @@ namespace Ryujinx.Cpu.AppleHv
{
HvApi.hv_vcpu_run(vcpu.Handle).ThrowOnError();
- uint reason = vcpu.ExitInfo->Reason;
+ HvExitReason reason = vcpu.ExitInfo->Reason;
- if (reason == 1)
+ if (reason == HvExitReason.Exception)
{
uint hvEsr = (uint)vcpu.ExitInfo->Exception.Syndrome;
ExceptionClass hvEc = (ExceptionClass)(hvEsr >> 26);
@@ -146,14 +159,22 @@ namespace Ryujinx.Cpu.AppleHv
address = SynchronousException(memoryManager, ref vcpu);
HvApi.hv_vcpu_set_reg(vcpu.Handle, HvReg.PC, address).ThrowOnError();
}
- else if (reason == 0)
+ else if (reason == HvExitReason.Canceled || reason == HvExitReason.VTimerActivated)
{
- if (_impl.GetAndClearInterruptRequested())
+ if (GetAndClearInterruptRequested())
{
ReturnToPool(vcpu);
InterruptHandler();
vcpu = RentFromPool(memoryManager.AddressSpace, vcpu);
}
+
+ if (reason == HvExitReason.VTimerActivated)
+ {
+ vcpu.EnableAndUpdateVTimer();
+
+ // Unmask VTimer interrupts.
+ HvApi.hv_vcpu_set_vtimer_mask(vcpu.Handle, false).ThrowOnError();
+ }
}
else
{
diff --git a/src/Ryujinx.Cpu/AppleHv/HvExecutionContextShadow.cs b/src/Ryujinx.Cpu/AppleHv/HvExecutionContextShadow.cs
index 78ffcbe4b3..6ce8e18000 100644
--- a/src/Ryujinx.Cpu/AppleHv/HvExecutionContextShadow.cs
+++ b/src/Ryujinx.Cpu/AppleHv/HvExecutionContextShadow.cs
@@ -46,14 +46,5 @@ namespace Ryujinx.Cpu.AppleHv
{
_v[index] = value;
}
-
- public void RequestInterrupt()
- {
- }
-
- public bool GetAndClearInterruptRequested()
- {
- return false;
- }
}
}
diff --git a/src/Ryujinx.Cpu/AppleHv/HvExecutionContextVcpu.cs b/src/Ryujinx.Cpu/AppleHv/HvExecutionContextVcpu.cs
index d9ad637f8c..bb232940de 100644
--- a/src/Ryujinx.Cpu/AppleHv/HvExecutionContextVcpu.cs
+++ b/src/Ryujinx.Cpu/AppleHv/HvExecutionContextVcpu.cs
@@ -2,10 +2,11 @@ using ARMeilleure.State;
using Ryujinx.Memory;
using System;
using System.Runtime.InteropServices;
-using System.Threading;
+using System.Runtime.Versioning;
namespace Ryujinx.Cpu.AppleHv
{
+ [SupportedOSPlatform("macos")]
class HvExecutionContextVcpu : IHvExecutionContext
{
private static readonly MemoryBlock _setSimdFpRegFuncMem;
@@ -135,7 +136,6 @@ namespace Ryujinx.Cpu.AppleHv
}
private readonly ulong _vcpu;
- private int _interruptRequested;
public HvExecutionContextVcpu(ulong vcpu)
{
@@ -181,16 +181,8 @@ namespace Ryujinx.Cpu.AppleHv
public void RequestInterrupt()
{
- if (Interlocked.Exchange(ref _interruptRequested, 1) == 0)
- {
- ulong vcpu = _vcpu;
- HvApi.hv_vcpus_exit(ref vcpu, 1);
- }
- }
-
- public bool GetAndClearInterruptRequested()
- {
- return Interlocked.Exchange(ref _interruptRequested, 0) != 0;
+ ulong vcpu = _vcpu;
+ HvApi.hv_vcpus_exit(ref vcpu, 1);
}
}
}
diff --git a/src/Ryujinx.Cpu/AppleHv/HvMemoryBlockAllocation.cs b/src/Ryujinx.Cpu/AppleHv/HvMemoryBlockAllocation.cs
index 3c3f087ab7..855d313c57 100644
--- a/src/Ryujinx.Cpu/AppleHv/HvMemoryBlockAllocation.cs
+++ b/src/Ryujinx.Cpu/AppleHv/HvMemoryBlockAllocation.cs
@@ -1,8 +1,10 @@
using Ryujinx.Memory;
using System;
+using System.Runtime.Versioning;
namespace Ryujinx.Cpu.AppleHv
{
+ [SupportedOSPlatform("macos")]
readonly struct HvMemoryBlockAllocation : IDisposable
{
private readonly HvMemoryBlockAllocator _owner;
diff --git a/src/Ryujinx.Cpu/AppleHv/HvMemoryBlockAllocator.cs b/src/Ryujinx.Cpu/AppleHv/HvMemoryBlockAllocator.cs
index ac184cb9a0..4e3723d554 100644
--- a/src/Ryujinx.Cpu/AppleHv/HvMemoryBlockAllocator.cs
+++ b/src/Ryujinx.Cpu/AppleHv/HvMemoryBlockAllocator.cs
@@ -1,7 +1,9 @@
using Ryujinx.Memory;
+using System.Runtime.Versioning;
namespace Ryujinx.Cpu.AppleHv
{
+ [SupportedOSPlatform("macos")]
class HvMemoryBlockAllocator : PrivateMemoryAllocatorImpl
{
public class Block : PrivateMemoryAllocator.Block
diff --git a/src/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs b/src/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs
index 01c685d4bd..d5ce817a41 100644
--- a/src/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs
+++ b/src/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs
@@ -7,6 +7,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+using System.Runtime.Versioning;
using System.Threading;
namespace Ryujinx.Cpu.AppleHv
@@ -14,6 +15,7 @@ namespace Ryujinx.Cpu.AppleHv
///
/// Represents a CPU memory manager which maps guest virtual memory directly onto the Hypervisor page table.
///
+ [SupportedOSPlatform("macos")]
public class HvMemoryManager : MemoryManagerBase, IMemoryManager, IVirtualMemoryManagerTracked, IWritableBlock
{
public const int PageBits = 12;
diff --git a/src/Ryujinx.Cpu/AppleHv/HvVcpu.cs b/src/Ryujinx.Cpu/AppleHv/HvVcpu.cs
index 9c2cc0ff3a..ee91c478b0 100644
--- a/src/Ryujinx.Cpu/AppleHv/HvVcpu.cs
+++ b/src/Ryujinx.Cpu/AppleHv/HvVcpu.cs
@@ -1,7 +1,15 @@
+using System.Diagnostics;
+using System.Runtime.Versioning;
+
namespace Ryujinx.Cpu.AppleHv
{
+ [SupportedOSPlatform("macos")]
unsafe class HvVcpu
{
+ private const ulong InterruptIntervalNs = 16 * 1000000; // 16 ms
+
+ private static ulong _interruptTimeDeltaTicks = 0;
+
public readonly ulong Handle;
public readonly HvVcpuExit* ExitInfo;
public readonly IHvExecutionContext ShadowContext;
@@ -21,5 +29,28 @@ namespace Ryujinx.Cpu.AppleHv
NativeContext = nativeContext;
IsEphemeral = isEphemeral;
}
+
+ public void EnableAndUpdateVTimer()
+ {
+ // We need to ensure interrupts will be serviced,
+ // and for that we set up the VTime to trigger an interrupt at fixed intervals.
+
+ ulong deltaTicks = _interruptTimeDeltaTicks;
+
+ if (deltaTicks == 0)
+ {
+ // Calculate our time delta in ticks based on the current clock frequency.
+
+ int result = TimeApi.mach_timebase_info(out var timeBaseInfo);
+
+ Debug.Assert(result == 0);
+
+ deltaTicks = ((InterruptIntervalNs * timeBaseInfo.Denom) + (timeBaseInfo.Numer - 1)) / timeBaseInfo.Numer;
+ _interruptTimeDeltaTicks = deltaTicks;
+ }
+
+ HvApi.hv_vcpu_set_sys_reg(Handle, HvSysReg.CNTV_CTL_EL0, 1).ThrowOnError();
+ HvApi.hv_vcpu_set_sys_reg(Handle, HvSysReg.CNTV_CVAL_EL0, TimeApi.mach_absolute_time() + deltaTicks).ThrowOnError();
+ }
}
}
diff --git a/src/Ryujinx.Cpu/AppleHv/HvVcpuPool.cs b/src/Ryujinx.Cpu/AppleHv/HvVcpuPool.cs
index fe01dce3c3..2edcd7e4ef 100644
--- a/src/Ryujinx.Cpu/AppleHv/HvVcpuPool.cs
+++ b/src/Ryujinx.Cpu/AppleHv/HvVcpuPool.cs
@@ -1,8 +1,10 @@
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,
@@ -81,6 +83,8 @@ namespace Ryujinx.Cpu.AppleHv
HvVcpu vcpu = new(vcpuHandle, exitInfo, shadowContext, nativeContext, isEphemeral);
+ vcpu.EnableAndUpdateVTimer();
+
return vcpu;
}
diff --git a/src/Ryujinx.Cpu/AppleHv/HvVm.cs b/src/Ryujinx.Cpu/AppleHv/HvVm.cs
index 1f15022fd8..c4f107532e 100644
--- a/src/Ryujinx.Cpu/AppleHv/HvVm.cs
+++ b/src/Ryujinx.Cpu/AppleHv/HvVm.cs
@@ -1,8 +1,10 @@
using Ryujinx.Memory;
using System;
+using System.Runtime.Versioning;
namespace Ryujinx.Cpu.AppleHv
{
+ [SupportedOSPlatform("macos")]
static class HvVm
{
// This alignment allows us to use larger blocks on the page table.
diff --git a/src/Ryujinx.Cpu/AppleHv/IHvExecutionContext.cs b/src/Ryujinx.Cpu/AppleHv/IHvExecutionContext.cs
index 7990ab72aa..54b73acc63 100644
--- a/src/Ryujinx.Cpu/AppleHv/IHvExecutionContext.cs
+++ b/src/Ryujinx.Cpu/AppleHv/IHvExecutionContext.cs
@@ -2,7 +2,7 @@ using ARMeilleure.State;
namespace Ryujinx.Cpu.AppleHv
{
- public interface IHvExecutionContext
+ interface IHvExecutionContext
{
ulong Pc { get; set; }
ulong ElrEl1 { get; set; }
@@ -39,8 +39,5 @@ namespace Ryujinx.Cpu.AppleHv
SetV(i, context.GetV(i));
}
}
-
- void RequestInterrupt();
- bool GetAndClearInterruptRequested();
}
}
diff --git a/src/Ryujinx.Cpu/AppleHv/TimeApi.cs b/src/Ryujinx.Cpu/AppleHv/TimeApi.cs
new file mode 100644
index 0000000000..85bc771788
--- /dev/null
+++ b/src/Ryujinx.Cpu/AppleHv/TimeApi.cs
@@ -0,0 +1,21 @@
+using System.Runtime.InteropServices;
+using System.Runtime.Versioning;
+
+namespace Ryujinx.Cpu.AppleHv
+{
+ struct MachTimebaseInfo
+ {
+ public uint Numer;
+ public uint Denom;
+ }
+
+ [SupportedOSPlatform("macos")]
+ static partial class TimeApi
+ {
+ [LibraryImport("libc", SetLastError = true)]
+ public static partial ulong mach_absolute_time();
+
+ [LibraryImport("libc", SetLastError = true)]
+ public static partial int mach_timebase_info(out MachTimebaseInfo info);
+ }
+}