| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305 |
- 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/>
- public ulong Pc => _impl.ElrEl1;
- /// <inheritdoc/>
- public long TpidrEl0
- {
- get => _impl.TpidrEl0;
- set => _impl.TpidrEl0 = value;
- }
- /// <inheritdoc/>
- public long TpidrroEl0
- {
- get => _impl.TpidrroEl0;
- set => _impl.TpidrroEl0 = value;
- }
- /// <inheritdoc/>
- public uint Pstate
- {
- get => _impl.Pstate;
- set => _impl.Pstate = value;
- }
- /// <inheritdoc/>
- public uint Fpcr
- {
- get => _impl.Fpcr;
- set => _impl.Fpcr = value;
- }
- /// <inheritdoc/>
- public uint Fpsr
- {
- get => _impl.Fpsr;
- set => _impl.Fpsr = value;
- }
- /// <inheritdoc/>
- public bool IsAarch32
- {
- get => false;
- set
- {
- if (value)
- {
- throw new NotSupportedException();
- }
- }
- }
- /// <inheritdoc/>
- public bool Running { get; private set; }
- private readonly ICounter _counter;
- private readonly IHvExecutionContext _shadowContext;
- private IHvExecutionContext _impl;
- private readonly ExceptionCallbacks _exceptionCallbacks;
- private int _interruptRequested;
- public HvExecutionContext(ICounter counter, ExceptionCallbacks exceptionCallbacks)
- {
- _counter = counter;
- _shadowContext = new HvExecutionContextShadow();
- _impl = _shadowContext;
- _exceptionCallbacks = exceptionCallbacks;
- Running = true;
- }
- /// <inheritdoc/>
- public ulong GetX(int index) => _impl.GetX(index);
- /// <inheritdoc/>
- public void SetX(int index, ulong value) => _impl.SetX(index, value);
- /// <inheritdoc/>
- public V128 GetV(int index) => _impl.GetV(index);
- /// <inheritdoc/>
- public void SetV(int index, V128 value) => _impl.SetV(index, value);
- private void InterruptHandler()
- {
- _exceptionCallbacks.InterruptCallback?.Invoke(this);
- }
- private void BreakHandler(ulong address, int imm)
- {
- _exceptionCallbacks.BreakCallback?.Invoke(this, address, imm);
- }
- private void SupervisorCallHandler(ulong address, int imm)
- {
- _exceptionCallbacks.SupervisorCallback?.Invoke(this, address, imm);
- }
- private void UndefinedHandler(ulong address, int opCode)
- {
- _exceptionCallbacks.UndefinedCallback?.Invoke(this, address, opCode);
- }
- /// <inheritdoc/>
- public void 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/>
- public void StopRunning()
- {
- Running = false;
- RequestInterrupt();
- }
- public unsafe void Execute(HvMemoryManager memoryManager, ulong address)
- {
- HvVcpu vcpu = HvVcpuPool.Instance.Create(memoryManager.AddressSpace, _shadowContext, SwapContext);
- HvApi.hv_vcpu_set_reg(vcpu.Handle, HvReg.PC, address).ThrowOnError();
- while (Running)
- {
- HvApi.hv_vcpu_run(vcpu.Handle).ThrowOnError();
- HvExitReason reason = vcpu.ExitInfo->Reason;
- if (reason == HvExitReason.Exception)
- {
- uint hvEsr = (uint)vcpu.ExitInfo->Exception.Syndrome;
- ExceptionClass hvEc = (ExceptionClass)(hvEsr >> 26);
- if (hvEc != ExceptionClass.HvcAarch64)
- {
- throw new Exception($"Unhandled exception from guest kernel with ESR 0x{hvEsr:X} ({hvEc}).");
- }
- address = SynchronousException(memoryManager, ref vcpu);
- HvApi.hv_vcpu_set_reg(vcpu.Handle, HvReg.PC, address).ThrowOnError();
- }
- else if (reason == HvExitReason.Canceled || reason == HvExitReason.VTimerActivated)
- {
- 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
- {
- throw new Exception($"Unhandled exit reason {reason}.");
- }
- }
- HvVcpuPool.Instance.Destroy(vcpu, SwapContext);
- }
- private ulong SynchronousException(HvMemoryManager memoryManager, ref HvVcpu vcpu)
- {
- ulong vcpuHandle = vcpu.Handle;
- HvApi.hv_vcpu_get_sys_reg(vcpuHandle, HvSysReg.ELR_EL1, out ulong elr).ThrowOnError();
- HvApi.hv_vcpu_get_sys_reg(vcpuHandle, HvSysReg.ESR_EL1, out ulong esr).ThrowOnError();
- ExceptionClass ec = (ExceptionClass)((uint)esr >> 26);
- switch (ec)
- {
- case ExceptionClass.DataAbortLowerEl:
- DataAbort(memoryManager.Tracking, vcpuHandle, (uint)esr);
- break;
- case ExceptionClass.TrappedMsrMrsSystem:
- InstructionTrap((uint)esr);
- HvApi.hv_vcpu_set_sys_reg(vcpuHandle, HvSysReg.ELR_EL1, elr + 4UL).ThrowOnError();
- break;
- case ExceptionClass.SvcAarch64:
- ReturnToPool(vcpu);
- ushort id = (ushort)esr;
- SupervisorCallHandler(elr - 4UL, id);
- vcpu = RentFromPool(memoryManager.AddressSpace, vcpu);
- break;
- default:
- throw new Exception($"Unhandled guest exception {ec}.");
- }
- // Make sure we will continue running at EL0.
- if (memoryManager.AddressSpace.GetAndClearUserTlbInvalidationPending())
- {
- // TODO: Invalidate only the range that was modified?
- return HvAddressSpace.KernelRegionTlbiEretAddress;
- }
- else
- {
- return HvAddressSpace.KernelRegionEretAddress;
- }
- }
- private static void DataAbort(MemoryTracking tracking, ulong vcpu, uint esr)
- {
- bool write = (esr & (1u << 6)) != 0;
- bool farValid = (esr & (1u << 10)) == 0;
- int accessSizeLog2 = (int)((esr >> 22) & 3);
- if (farValid)
- {
- HvApi.hv_vcpu_get_sys_reg(vcpu, HvSysReg.FAR_EL1, out ulong far).ThrowOnError();
- ulong size = 1UL << accessSizeLog2;
- if (!tracking.VirtualMemoryEvent(far, size, write))
- {
- string rw = write ? "write" : "read";
- throw new Exception($"Unhandled invalid memory access at VA 0x{far:X} with size 0x{size:X} ({rw}).");
- }
- }
- else
- {
- throw new Exception($"Unhandled invalid memory access at unknown VA with ESR 0x{esr:X}.");
- }
- }
- private void InstructionTrap(uint esr)
- {
- bool read = (esr & 1) != 0;
- uint rt = (esr >> 5) & 0x1f;
- if (read)
- {
- // Op0 Op2 Op1 CRn 00000 CRm
- switch ((esr >> 1) & 0x1ffe0f)
- {
- case 0b11_000_011_1110_00000_0000: // CNTFRQ_EL0
- WriteRt(rt, _counter.Frequency);
- break;
- case 0b11_001_011_1110_00000_0000: // CNTPCT_EL0
- WriteRt(rt, _counter.Counter);
- break;
- default:
- throw new Exception($"Unhandled system register read with ESR 0x{esr:X}");
- }
- }
- else
- {
- throw new Exception($"Unhandled system register write with ESR 0x{esr:X}");
- }
- }
- private void WriteRt(uint rt, ulong value)
- {
- if (rt < 31)
- {
- SetX((int)rt, value);
- }
- }
- private void ReturnToPool(HvVcpu vcpu)
- {
- HvVcpuPool.Instance.Return(vcpu, SwapContext);
- }
- private HvVcpu RentFromPool(HvAddressSpace addressSpace, HvVcpu vcpu)
- {
- return HvVcpuPool.Instance.Rent(addressSpace, _shadowContext, vcpu, SwapContext);
- }
- private void SwapContext(IHvExecutionContext newContext)
- {
- _impl = newContext;
- }
- public void Dispose()
- {
- }
- }
- }
|