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.Memory;
|
||||
using System;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
namespace Ryujinx.Cpu.AppleHv
|
||||
{
|
||||
[SupportedOSPlatform("macos")]
|
||||
class HvAddressSpace : IDisposable
|
||||
{
|
||||
private const ulong KernelRegionBase = unchecked((ulong)-(1L << 39));
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
using ARMeilleure.Memory;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
namespace Ryujinx.Cpu.AppleHv
|
||||
{
|
||||
[SupportedOSPlatform("macos")]
|
||||
class HvCpuContext : ICpuContext
|
||||
{
|
||||
private readonly ITickSource _tickSource;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
|
@ -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
|
|||
/// <inheritdoc/>
|
||||
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/>
|
||||
|
@ -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
|
||||
{
|
||||
|
|
|
@ -46,14 +46,5 @@ namespace Ryujinx.Cpu.AppleHv
|
|||
{
|
||||
_v[index] = value;
|
||||
}
|
||||
|
||||
public void RequestInterrupt()
|
||||
{
|
||||
}
|
||||
|
||||
public bool GetAndClearInterruptRequested()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
@ -180,17 +180,9 @@ 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
using Ryujinx.Memory;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
namespace Ryujinx.Cpu.AppleHv
|
||||
{
|
||||
[SupportedOSPlatform("macos")]
|
||||
class HvMemoryBlockAllocator : PrivateMemoryAllocatorImpl<HvMemoryBlockAllocator.Block>
|
||||
{
|
||||
public class Block : PrivateMemoryAllocator.Block
|
||||
|
|
|
@ -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
|
|||
/// <summary>
|
||||
/// Represents a CPU memory manager which maps guest virtual memory directly onto the Hypervisor page table.
|
||||
/// </summary>
|
||||
[SupportedOSPlatform("macos")]
|
||||
public class HvMemoryManager : MemoryManagerBase, IMemoryManager, IVirtualMemoryManagerTracked, IWritableBlock
|
||||
{
|
||||
public const int PageBits = 12;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
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);
|
||||
}
|
||||
}
|
Reference in a new issue