forked from Mirror/Ryujinx
Add VTimer as alternative interrupt method on Apple Hypervisor (#5663)
* Add VTimer as alternative interrupt method on Apple Hypervisor * Fix naming violations on TimeApi * Fix timer interval (was 16us rather than 16ms) * Fix delta ticks calculation * Missing ThrowOnError call * Add SupportedOSPlatform attribute on AppleHv classes
This commit is contained in:
parent
d6d3cdd573
commit
ddc9ae2a83
16 changed files with 114 additions and 31 deletions
|
@ -1,9 +1,11 @@
|
||||||
using Ryujinx.Cpu.AppleHv.Arm;
|
using Ryujinx.Cpu.AppleHv.Arm;
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.AppleHv
|
namespace Ryujinx.Cpu.AppleHv
|
||||||
{
|
{
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
class HvAddressSpace : IDisposable
|
class HvAddressSpace : IDisposable
|
||||||
{
|
{
|
||||||
private const ulong KernelRegionBase = unchecked((ulong)-(1L << 39));
|
private const ulong KernelRegionBase = unchecked((ulong)-(1L << 39));
|
||||||
|
|
|
@ -2,10 +2,12 @@ using Ryujinx.Cpu.AppleHv.Arm;
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.AppleHv
|
namespace Ryujinx.Cpu.AppleHv
|
||||||
{
|
{
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
class HvAddressSpaceRange : IDisposable
|
class HvAddressSpaceRange : IDisposable
|
||||||
{
|
{
|
||||||
private const ulong AllocationGranule = 1UL << 14;
|
private const ulong AllocationGranule = 1UL << 14;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.AppleHv
|
namespace Ryujinx.Cpu.AppleHv
|
||||||
{
|
{
|
||||||
|
@ -12,10 +13,18 @@ namespace Ryujinx.Cpu.AppleHv
|
||||||
#pragma warning restore CS0649
|
#pragma warning restore CS0649
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum HvExitReason : uint
|
||||||
|
{
|
||||||
|
Canceled,
|
||||||
|
Exception,
|
||||||
|
VTimerActivated,
|
||||||
|
Unknown,
|
||||||
|
}
|
||||||
|
|
||||||
struct HvVcpuExit
|
struct HvVcpuExit
|
||||||
{
|
{
|
||||||
#pragma warning disable CS0649 // Field is never assigned to
|
#pragma warning disable CS0649 // Field is never assigned to
|
||||||
public uint Reason;
|
public HvExitReason Reason;
|
||||||
public HvVcpuExitException Exception;
|
public HvVcpuExitException Exception;
|
||||||
#pragma warning restore CS0649
|
#pragma warning restore CS0649
|
||||||
}
|
}
|
||||||
|
@ -255,6 +264,7 @@ namespace Ryujinx.Cpu.AppleHv
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
static partial class HvApi
|
static partial class HvApi
|
||||||
{
|
{
|
||||||
public const string LibraryName = "/System/Library/Frameworks/Hypervisor.framework/Hypervisor";
|
public const string LibraryName = "/System/Library/Frameworks/Hypervisor.framework/Hypervisor";
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
using ARMeilleure.Memory;
|
using ARMeilleure.Memory;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.AppleHv
|
namespace Ryujinx.Cpu.AppleHv
|
||||||
{
|
{
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
class HvCpuContext : ICpuContext
|
class HvCpuContext : ICpuContext
|
||||||
{
|
{
|
||||||
private readonly ITickSource _tickSource;
|
private readonly ITickSource _tickSource;
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
using ARMeilleure.Memory;
|
using ARMeilleure.Memory;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.AppleHv
|
namespace Ryujinx.Cpu.AppleHv
|
||||||
{
|
{
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
public class HvEngine : ICpuEngine
|
public class HvEngine : ICpuEngine
|
||||||
{
|
{
|
||||||
private readonly ITickSource _tickSource;
|
private readonly ITickSource _tickSource;
|
||||||
|
|
|
@ -2,9 +2,12 @@ using ARMeilleure.State;
|
||||||
using Ryujinx.Cpu.AppleHv.Arm;
|
using Ryujinx.Cpu.AppleHv.Arm;
|
||||||
using Ryujinx.Memory.Tracking;
|
using Ryujinx.Memory.Tracking;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.AppleHv
|
namespace Ryujinx.Cpu.AppleHv
|
||||||
{
|
{
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
class HvExecutionContext : IExecutionContext
|
class HvExecutionContext : IExecutionContext
|
||||||
{
|
{
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
|
@ -67,6 +70,8 @@ namespace Ryujinx.Cpu.AppleHv
|
||||||
|
|
||||||
private readonly ExceptionCallbacks _exceptionCallbacks;
|
private readonly ExceptionCallbacks _exceptionCallbacks;
|
||||||
|
|
||||||
|
private int _interruptRequested;
|
||||||
|
|
||||||
public HvExecutionContext(ICounter counter, ExceptionCallbacks exceptionCallbacks)
|
public HvExecutionContext(ICounter counter, ExceptionCallbacks exceptionCallbacks)
|
||||||
{
|
{
|
||||||
_counter = counter;
|
_counter = counter;
|
||||||
|
@ -111,7 +116,15 @@ namespace Ryujinx.Cpu.AppleHv
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void RequestInterrupt()
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
|
@ -131,9 +144,9 @@ namespace Ryujinx.Cpu.AppleHv
|
||||||
{
|
{
|
||||||
HvApi.hv_vcpu_run(vcpu.Handle).ThrowOnError();
|
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;
|
uint hvEsr = (uint)vcpu.ExitInfo->Exception.Syndrome;
|
||||||
ExceptionClass hvEc = (ExceptionClass)(hvEsr >> 26);
|
ExceptionClass hvEc = (ExceptionClass)(hvEsr >> 26);
|
||||||
|
@ -146,14 +159,22 @@ namespace Ryujinx.Cpu.AppleHv
|
||||||
address = SynchronousException(memoryManager, ref vcpu);
|
address = SynchronousException(memoryManager, ref vcpu);
|
||||||
HvApi.hv_vcpu_set_reg(vcpu.Handle, HvReg.PC, address).ThrowOnError();
|
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);
|
ReturnToPool(vcpu);
|
||||||
InterruptHandler();
|
InterruptHandler();
|
||||||
vcpu = RentFromPool(memoryManager.AddressSpace, vcpu);
|
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
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -46,14 +46,5 @@ namespace Ryujinx.Cpu.AppleHv
|
||||||
{
|
{
|
||||||
_v[index] = value;
|
_v[index] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RequestInterrupt()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool GetAndClearInterruptRequested()
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,11 @@ using ARMeilleure.State;
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Threading;
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.AppleHv
|
namespace Ryujinx.Cpu.AppleHv
|
||||||
{
|
{
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
class HvExecutionContextVcpu : IHvExecutionContext
|
class HvExecutionContextVcpu : IHvExecutionContext
|
||||||
{
|
{
|
||||||
private static readonly MemoryBlock _setSimdFpRegFuncMem;
|
private static readonly MemoryBlock _setSimdFpRegFuncMem;
|
||||||
|
@ -135,7 +136,6 @@ namespace Ryujinx.Cpu.AppleHv
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly ulong _vcpu;
|
private readonly ulong _vcpu;
|
||||||
private int _interruptRequested;
|
|
||||||
|
|
||||||
public HvExecutionContextVcpu(ulong vcpu)
|
public HvExecutionContextVcpu(ulong vcpu)
|
||||||
{
|
{
|
||||||
|
@ -180,17 +180,9 @@ namespace Ryujinx.Cpu.AppleHv
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RequestInterrupt()
|
public void RequestInterrupt()
|
||||||
{
|
|
||||||
if (Interlocked.Exchange(ref _interruptRequested, 1) == 0)
|
|
||||||
{
|
{
|
||||||
ulong vcpu = _vcpu;
|
ulong vcpu = _vcpu;
|
||||||
HvApi.hv_vcpus_exit(ref vcpu, 1);
|
HvApi.hv_vcpus_exit(ref vcpu, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool GetAndClearInterruptRequested()
|
|
||||||
{
|
|
||||||
return Interlocked.Exchange(ref _interruptRequested, 0) != 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.AppleHv
|
namespace Ryujinx.Cpu.AppleHv
|
||||||
{
|
{
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
readonly struct HvMemoryBlockAllocation : IDisposable
|
readonly struct HvMemoryBlockAllocation : IDisposable
|
||||||
{
|
{
|
||||||
private readonly HvMemoryBlockAllocator _owner;
|
private readonly HvMemoryBlockAllocator _owner;
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.AppleHv
|
namespace Ryujinx.Cpu.AppleHv
|
||||||
{
|
{
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
class HvMemoryBlockAllocator : PrivateMemoryAllocatorImpl<HvMemoryBlockAllocator.Block>
|
class HvMemoryBlockAllocator : PrivateMemoryAllocatorImpl<HvMemoryBlockAllocator.Block>
|
||||||
{
|
{
|
||||||
public class Block : PrivateMemoryAllocator.Block
|
public class Block : PrivateMemoryAllocator.Block
|
||||||
|
|
|
@ -7,6 +7,7 @@ using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.AppleHv
|
namespace Ryujinx.Cpu.AppleHv
|
||||||
|
@ -14,6 +15,7 @@ namespace Ryujinx.Cpu.AppleHv
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a CPU memory manager which maps guest virtual memory directly onto the Hypervisor page table.
|
/// Represents a CPU memory manager which maps guest virtual memory directly onto the Hypervisor page table.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
public class HvMemoryManager : MemoryManagerBase, IMemoryManager, IVirtualMemoryManagerTracked, IWritableBlock
|
public class HvMemoryManager : MemoryManagerBase, IMemoryManager, IVirtualMemoryManagerTracked, IWritableBlock
|
||||||
{
|
{
|
||||||
public const int PageBits = 12;
|
public const int PageBits = 12;
|
||||||
|
|
|
@ -1,7 +1,15 @@
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.AppleHv
|
namespace Ryujinx.Cpu.AppleHv
|
||||||
{
|
{
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
unsafe class HvVcpu
|
unsafe class HvVcpu
|
||||||
{
|
{
|
||||||
|
private const ulong InterruptIntervalNs = 16 * 1000000; // 16 ms
|
||||||
|
|
||||||
|
private static ulong _interruptTimeDeltaTicks = 0;
|
||||||
|
|
||||||
public readonly ulong Handle;
|
public readonly ulong Handle;
|
||||||
public readonly HvVcpuExit* ExitInfo;
|
public readonly HvVcpuExit* ExitInfo;
|
||||||
public readonly IHvExecutionContext ShadowContext;
|
public readonly IHvExecutionContext ShadowContext;
|
||||||
|
@ -21,5 +29,28 @@ namespace Ryujinx.Cpu.AppleHv
|
||||||
NativeContext = nativeContext;
|
NativeContext = nativeContext;
|
||||||
IsEphemeral = isEphemeral;
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.AppleHv
|
namespace Ryujinx.Cpu.AppleHv
|
||||||
{
|
{
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
class HvVcpuPool
|
class HvVcpuPool
|
||||||
{
|
{
|
||||||
// Since there's a limit on the number of VCPUs we can create,
|
// 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);
|
HvVcpu vcpu = new(vcpuHandle, exitInfo, shadowContext, nativeContext, isEphemeral);
|
||||||
|
|
||||||
|
vcpu.EnableAndUpdateVTimer();
|
||||||
|
|
||||||
return vcpu;
|
return vcpu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.AppleHv
|
namespace Ryujinx.Cpu.AppleHv
|
||||||
{
|
{
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
static class HvVm
|
static class HvVm
|
||||||
{
|
{
|
||||||
// This alignment allows us to use larger blocks on the page table.
|
// This alignment allows us to use larger blocks on the page table.
|
||||||
|
|
|
@ -2,7 +2,7 @@ using ARMeilleure.State;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.AppleHv
|
namespace Ryujinx.Cpu.AppleHv
|
||||||
{
|
{
|
||||||
public interface IHvExecutionContext
|
interface IHvExecutionContext
|
||||||
{
|
{
|
||||||
ulong Pc { get; set; }
|
ulong Pc { get; set; }
|
||||||
ulong ElrEl1 { get; set; }
|
ulong ElrEl1 { get; set; }
|
||||||
|
@ -39,8 +39,5 @@ namespace Ryujinx.Cpu.AppleHv
|
||||||
SetV(i, context.GetV(i));
|
SetV(i, context.GetV(i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RequestInterrupt();
|
|
||||||
bool GetAndClearInterruptRequested();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
21
src/Ryujinx.Cpu/AppleHv/TimeApi.cs
Normal file
21
src/Ryujinx.Cpu/AppleHv/TimeApi.cs
Normal file
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue