forked from MeloNX/MeloNX
Add NCE code
This commit is contained in:
parent
9658525434
commit
1aff88648e
@ -115,7 +115,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
public bool IsOpenGLAvailable => !OperatingSystem.IsMacOS();
|
||||
|
||||
public bool IsHypervisorAvailable => OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64;
|
||||
public bool IsHypervisorAvailable => RuntimeInformation.ProcessArchitecture == Architecture.Arm64;
|
||||
|
||||
public bool DirectoryChanged
|
||||
{
|
||||
|
@ -32,6 +32,11 @@ namespace Ryujinx.Cpu.AppleHv
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void PatchCodeForNce(ulong textAddress, ulong textSize, ulong patchRegionAddress, ulong patchRegionSize)
|
||||
{
|
||||
}
|
||||
|
||||
public IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled)
|
||||
{
|
||||
return new DummyDiskCacheLoadState();
|
||||
|
@ -14,7 +14,7 @@ namespace Ryujinx.Cpu.AppleHv
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ICpuContext CreateCpuContext(IMemoryManager memoryManager, bool for64Bit)
|
||||
public ICpuContext CreateCpuContext(ICpuMemoryManager memoryManager, bool for64Bit)
|
||||
{
|
||||
return new HvCpuContext(_tickSource, memoryManager, for64Bit);
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ namespace Ryujinx.Cpu.AppleHv
|
||||
/// 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 class HvMemoryManager : MemoryManagerBase, ICpuMemoryManager, IVirtualMemoryManagerTracked, IWritableBlock
|
||||
{
|
||||
public const int PageBits = 12;
|
||||
public const int PageSize = 1 << PageBits;
|
||||
@ -182,6 +182,11 @@ namespace Ryujinx.Cpu.AppleHv
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Reprotect(ulong va, ulong size, MemoryPermission permission)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public T Read<T>(ulong va) where T : unmanaged
|
||||
{
|
||||
|
15
src/Ryujinx.Cpu/DiskCacheLoadState.cs
Normal file
15
src/Ryujinx.Cpu/DiskCacheLoadState.cs
Normal file
@ -0,0 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Cpu.Jit
|
||||
{
|
||||
class DiskCacheLoadState : IDiskCacheLoadState
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public event Action<LoadState, int, int> StateChanged;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Cancel()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -36,6 +36,8 @@ namespace Ryujinx.Cpu
|
||||
/// <param name="size">Size of the region to be invalidated</param>
|
||||
void InvalidateCacheRegion(ulong address, ulong size);
|
||||
|
||||
void PatchCodeForNce(ulong textAddress, ulong textSize, ulong patchRegionAddress, ulong patchRegionSize);
|
||||
|
||||
/// <summary>
|
||||
/// Loads cached code from disk for a given application.
|
||||
/// </summary>
|
||||
|
@ -1,5 +1,3 @@
|
||||
using ARMeilleure.Memory;
|
||||
|
||||
namespace Ryujinx.Cpu
|
||||
{
|
||||
/// <summary>
|
||||
@ -13,6 +11,6 @@ namespace Ryujinx.Cpu
|
||||
/// <param name="memoryManager">Memory manager for the address space of the context</param>
|
||||
/// <param name="for64Bit">Indicates if the context will be used to run 64-bit or 32-bit Arm code</param>
|
||||
/// <returns>CPU context</returns>
|
||||
ICpuContext CreateCpuContext(IMemoryManager memoryManager, bool for64Bit);
|
||||
ICpuContext CreateCpuContext(ICpuMemoryManager memoryManager, bool for64Bit);
|
||||
}
|
||||
}
|
||||
|
19
src/Ryujinx.Cpu/ICpuMemoryManager.cs
Normal file
19
src/Ryujinx.Cpu/ICpuMemoryManager.cs
Normal file
@ -0,0 +1,19 @@
|
||||
using ARMeilleure.Memory;
|
||||
using Ryujinx.Memory;
|
||||
|
||||
namespace Ryujinx.Cpu
|
||||
{
|
||||
/// <summary>
|
||||
/// CPU memory manager interface.
|
||||
/// </summary>
|
||||
public interface ICpuMemoryManager : IMemoryManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Reprotects a previously mapped range of virtual memory.
|
||||
/// </summary>
|
||||
/// <param name="va">Virtual address of the range to be reprotected</param>
|
||||
/// <param name="size">Size of the range to be reprotected</param>
|
||||
/// <param name="permission">New protection of the memory range</param>
|
||||
void Reprotect(ulong va, ulong size, MemoryPermission permission);
|
||||
}
|
||||
}
|
@ -46,6 +46,11 @@ namespace Ryujinx.Cpu.Jit
|
||||
_translator.InvalidateJitCacheRegion(address, size);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void PatchCodeForNce(ulong textAddress, ulong textSize, ulong patchRegionAddress, ulong patchRegionSize)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled)
|
||||
{
|
||||
|
@ -12,7 +12,7 @@ namespace Ryujinx.Cpu.Jit
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ICpuContext CreateCpuContext(IMemoryManager memoryManager, bool for64Bit)
|
||||
public ICpuContext CreateCpuContext(ICpuMemoryManager memoryManager, bool for64Bit)
|
||||
{
|
||||
return new JitCpuContext(_tickSource, memoryManager, for64Bit);
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ namespace Ryujinx.Cpu.Jit
|
||||
/// <summary>
|
||||
/// Represents a CPU memory manager.
|
||||
/// </summary>
|
||||
public sealed class MemoryManager : MemoryManagerBase, IMemoryManager, IVirtualMemoryManagerTracked, IWritableBlock
|
||||
public sealed class MemoryManager : MemoryManagerBase, ICpuMemoryManager, IVirtualMemoryManagerTracked, IWritableBlock
|
||||
{
|
||||
public const int PageBits = 12;
|
||||
public const int PageSize = 1 << PageBits;
|
||||
@ -126,6 +126,11 @@ namespace Ryujinx.Cpu.Jit
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Reprotect(ulong va, ulong size, MemoryPermission permission)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public T Read<T>(ulong va) where T : unmanaged
|
||||
{
|
||||
|
@ -13,7 +13,7 @@ namespace Ryujinx.Cpu.Jit
|
||||
/// <summary>
|
||||
/// Represents a CPU memory manager which maps guest virtual memory directly onto a host virtual region.
|
||||
/// </summary>
|
||||
public sealed class MemoryManagerHostMapped : MemoryManagerBase, IMemoryManager, IVirtualMemoryManagerTracked, IWritableBlock
|
||||
public sealed class MemoryManagerHostMapped : MemoryManagerBase, ICpuMemoryManager, IVirtualMemoryManagerTracked, IWritableBlock
|
||||
{
|
||||
public const int PageBits = 12;
|
||||
public const int PageSize = 1 << PageBits;
|
||||
@ -194,6 +194,11 @@ namespace Ryujinx.Cpu.Jit
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Reprotect(ulong va, ulong size, MemoryPermission permission)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public T Read<T>(ulong va) where T : unmanaged
|
||||
{
|
||||
|
@ -46,6 +46,20 @@ namespace Ryujinx.Cpu
|
||||
}
|
||||
}
|
||||
|
||||
public MemoryEhMeilleure(ulong asSize, MemoryTracking tracking)
|
||||
{
|
||||
_baseAddress = 0UL;
|
||||
ulong endAddress = asSize;
|
||||
|
||||
_trackingEvent = new TrackingEventDelegate(tracking.VirtualMemoryEvent);
|
||||
bool added = NativeSignalHandler.AddTrackedRegion((nuint)_baseAddress, (nuint)endAddress, Marshal.GetFunctionPointerForDelegate(_trackingEvent));
|
||||
|
||||
if (!added)
|
||||
{
|
||||
throw new InvalidOperationException("Number of allowed tracked regions exceeded.");
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
GC.SuppressFinalize(this);
|
||||
|
848
src/Ryujinx.Cpu/Nce/MemoryManagerNative.cs
Normal file
848
src/Ryujinx.Cpu/Nce/MemoryManagerNative.cs
Normal file
@ -0,0 +1,848 @@
|
||||
using ARMeilleure.Memory;
|
||||
using Ryujinx.Memory;
|
||||
using Ryujinx.Memory.Range;
|
||||
using Ryujinx.Memory.Tracking;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.Cpu.Nce
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a CPU memory manager which maps guest virtual memory directly onto a host virtual region.
|
||||
/// </summary>
|
||||
public sealed class MemoryManagerNative : MemoryManagerBase, ICpuMemoryManager, IVirtualMemoryManagerTracked, IWritableBlock
|
||||
{
|
||||
public const int PageBits = 12;
|
||||
public const int PageSize = 1 << PageBits;
|
||||
public const int PageMask = PageSize - 1;
|
||||
|
||||
public const int PageToPteShift = 5; // 32 pages (2 bits each) in one ulong page table entry.
|
||||
public const ulong BlockMappedMask = 0x5555555555555555; // First bit of each table entry set.
|
||||
|
||||
private enum HostMappedPtBits : ulong
|
||||
{
|
||||
Unmapped = 0,
|
||||
Mapped,
|
||||
WriteTracked,
|
||||
ReadWriteTracked,
|
||||
|
||||
MappedReplicated = 0x5555555555555555,
|
||||
WriteTrackedReplicated = 0xaaaaaaaaaaaaaaaa,
|
||||
ReadWriteTrackedReplicated = ulong.MaxValue
|
||||
}
|
||||
|
||||
private readonly InvalidAccessHandler _invalidAccessHandler;
|
||||
|
||||
private readonly MemoryBlock _addressSpace;
|
||||
private readonly MemoryBlock _addressSpaceMirror;
|
||||
private readonly ulong _addressSpaceSize;
|
||||
|
||||
private readonly MemoryBlock _backingMemory;
|
||||
private readonly PageTable<ulong> _pageTable;
|
||||
|
||||
private readonly MemoryEhMeilleure _memoryEh;
|
||||
|
||||
private readonly ulong[] _pageBitmap;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Supports4KBPages => MemoryBlock.GetPageSize() == PageSize;
|
||||
|
||||
public int AddressSpaceBits { get; }
|
||||
|
||||
public IntPtr PageTablePointer => IntPtr.Zero;
|
||||
|
||||
public ulong ReservedSize => (ulong)_addressSpace.Pointer.ToInt64();
|
||||
|
||||
public MemoryManagerType Type => MemoryManagerType.HostMappedUnsafe;
|
||||
|
||||
public MemoryTracking Tracking { get; }
|
||||
|
||||
public event Action<ulong, ulong> UnmapEvent;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the host mapped memory manager.
|
||||
/// </summary>
|
||||
/// <param name="addressSpace">Address space instance to use</param>
|
||||
/// <param name="backingMemory">Physical backing memory where virtual memory will be mapped to</param>
|
||||
/// <param name="addressSpaceSize">Size of the address space</param>
|
||||
/// <param name="invalidAccessHandler">Optional function to handle invalid memory accesses</param>
|
||||
public MemoryManagerNative(
|
||||
AddressSpace addressSpace,
|
||||
MemoryBlock backingMemory,
|
||||
ulong addressSpaceSize,
|
||||
InvalidAccessHandler invalidAccessHandler = null)
|
||||
{
|
||||
_backingMemory = backingMemory;
|
||||
_pageTable = new PageTable<ulong>();
|
||||
_invalidAccessHandler = invalidAccessHandler;
|
||||
_addressSpaceSize = addressSpaceSize;
|
||||
|
||||
ulong asSize = PageSize;
|
||||
int asBits = PageBits;
|
||||
|
||||
while (asSize < addressSpaceSize)
|
||||
{
|
||||
asSize <<= 1;
|
||||
asBits++;
|
||||
}
|
||||
|
||||
AddressSpaceBits = asBits;
|
||||
|
||||
_pageBitmap = new ulong[1 << (AddressSpaceBits - (PageBits + PageToPteShift))];
|
||||
|
||||
_addressSpace = addressSpace.Base;
|
||||
_addressSpaceMirror = addressSpace.Mirror;
|
||||
|
||||
Tracking = new MemoryTracking(this, PageSize, invalidAccessHandler);
|
||||
_memoryEh = new MemoryEhMeilleure(asSize, Tracking);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the virtual address is part of the addressable space.
|
||||
/// </summary>
|
||||
/// <param name="va">Virtual address</param>
|
||||
/// <returns>True if the virtual address is part of the addressable space</returns>
|
||||
private bool ValidateAddress(ulong va)
|
||||
{
|
||||
return va < _addressSpaceSize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the combination of virtual address and size is part of the addressable space.
|
||||
/// </summary>
|
||||
/// <param name="va">Virtual address of the range</param>
|
||||
/// <param name="size">Size of the range in bytes</param>
|
||||
/// <returns>True if the combination of virtual address and size is part of the addressable space</returns>
|
||||
private bool ValidateAddressAndSize(ulong va, ulong size)
|
||||
{
|
||||
ulong endVa = va + size;
|
||||
return endVa >= va && endVa >= size && endVa <= _addressSpaceSize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures the combination of virtual address and size is part of the addressable space.
|
||||
/// </summary>
|
||||
/// <param name="va">Virtual address of the range</param>
|
||||
/// <param name="size">Size of the range in bytes</param>
|
||||
/// <exception cref="InvalidMemoryRegionException">Throw when the memory region specified outside the addressable space</exception>
|
||||
private void AssertValidAddressAndSize(ulong va, ulong size)
|
||||
{
|
||||
if (!ValidateAddressAndSize(va, size))
|
||||
{
|
||||
throw new InvalidMemoryRegionException($"va=0x{va:X16}, size=0x{size:X16}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures the combination of virtual address and size is part of the addressable space and fully mapped.
|
||||
/// </summary>
|
||||
/// <param name="va">Virtual address of the range</param>
|
||||
/// <param name="size">Size of the range in bytes</param>
|
||||
private void AssertMapped(ulong va, ulong size)
|
||||
{
|
||||
if (!ValidateAddressAndSize(va, size) || !IsRangeMappedImpl(va, size))
|
||||
{
|
||||
throw new InvalidMemoryRegionException($"Not mapped: va=0x{va:X16}, size=0x{size:X16}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Map(ulong va, ulong pa, ulong size, MemoryMapFlags flags)
|
||||
{
|
||||
AssertValidAddressAndSize(va, size);
|
||||
|
||||
_addressSpace.MapView(_backingMemory, pa, AddressToOffset(va), size);
|
||||
_addressSpaceMirror.MapView(_backingMemory, pa, AddressToOffset(va), size);
|
||||
AddMapping(va, size);
|
||||
PtMap(va, pa, size);
|
||||
|
||||
Tracking.Map(va, size);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void MapForeign(ulong va, nuint hostPointer, ulong size)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Unmap(ulong va, ulong size)
|
||||
{
|
||||
AssertValidAddressAndSize(va, size);
|
||||
|
||||
UnmapEvent?.Invoke(va, size);
|
||||
Tracking.Unmap(va, size);
|
||||
|
||||
RemoveMapping(va, size);
|
||||
PtUnmap(va, size);
|
||||
_addressSpace.UnmapView(_backingMemory, AddressToOffset(va), size);
|
||||
_addressSpaceMirror.UnmapView(_backingMemory, AddressToOffset(va), size);
|
||||
}
|
||||
|
||||
private void PtMap(ulong va, ulong pa, ulong size)
|
||||
{
|
||||
while (size != 0)
|
||||
{
|
||||
_pageTable.Map(va, pa);
|
||||
|
||||
va += PageSize;
|
||||
pa += PageSize;
|
||||
size -= PageSize;
|
||||
}
|
||||
}
|
||||
|
||||
private void PtUnmap(ulong va, ulong size)
|
||||
{
|
||||
while (size != 0)
|
||||
{
|
||||
_pageTable.Unmap(va);
|
||||
|
||||
va += PageSize;
|
||||
size -= PageSize;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Reprotect(ulong va, ulong size, MemoryPermission permission)
|
||||
{
|
||||
_addressSpace.Reprotect(AddressToOffset(va), size, permission);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public T Read<T>(ulong va) where T : unmanaged
|
||||
{
|
||||
try
|
||||
{
|
||||
AssertMapped(va, (ulong)Unsafe.SizeOf<T>());
|
||||
|
||||
return _addressSpaceMirror.Read<T>(AddressToOffset(va));
|
||||
}
|
||||
catch (InvalidMemoryRegionException)
|
||||
{
|
||||
if (_invalidAccessHandler == null || !_invalidAccessHandler(va))
|
||||
{
|
||||
throw;
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public T ReadTracked<T>(ulong va) where T : unmanaged
|
||||
{
|
||||
try
|
||||
{
|
||||
SignalMemoryTracking(va, (ulong)Unsafe.SizeOf<T>(), false);
|
||||
|
||||
return Read<T>(va);
|
||||
}
|
||||
catch (InvalidMemoryRegionException)
|
||||
{
|
||||
if (_invalidAccessHandler == null || !_invalidAccessHandler(va))
|
||||
{
|
||||
throw;
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Read(ulong va, Span<byte> data)
|
||||
{
|
||||
try
|
||||
{
|
||||
AssertMapped(va, (ulong)data.Length);
|
||||
|
||||
_addressSpaceMirror.Read(AddressToOffset(va), data);
|
||||
}
|
||||
catch (InvalidMemoryRegionException)
|
||||
{
|
||||
if (_invalidAccessHandler == null || !_invalidAccessHandler(va))
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Write<T>(ulong va, T value) where T : unmanaged
|
||||
{
|
||||
try
|
||||
{
|
||||
SignalMemoryTracking(va, (ulong)Unsafe.SizeOf<T>(), write: true);
|
||||
|
||||
_addressSpaceMirror.Write(AddressToOffset(va), value);
|
||||
}
|
||||
catch (InvalidMemoryRegionException)
|
||||
{
|
||||
if (_invalidAccessHandler == null || !_invalidAccessHandler(va))
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Write(ulong va, ReadOnlySpan<byte> data)
|
||||
{
|
||||
try
|
||||
{
|
||||
SignalMemoryTracking(va, (ulong)data.Length, write: true);
|
||||
|
||||
_addressSpaceMirror.Write(AddressToOffset(va), data);
|
||||
}
|
||||
catch (InvalidMemoryRegionException)
|
||||
{
|
||||
if (_invalidAccessHandler == null || !_invalidAccessHandler(va))
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void WriteUntracked(ulong va, ReadOnlySpan<byte> data)
|
||||
{
|
||||
try
|
||||
{
|
||||
AssertMapped(va, (ulong)data.Length);
|
||||
|
||||
_addressSpaceMirror.Write(AddressToOffset(va), data);
|
||||
}
|
||||
catch (InvalidMemoryRegionException)
|
||||
{
|
||||
if (_invalidAccessHandler == null || !_invalidAccessHandler(va))
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool WriteWithRedundancyCheck(ulong va, ReadOnlySpan<byte> data)
|
||||
{
|
||||
try
|
||||
{
|
||||
SignalMemoryTracking(va, (ulong)data.Length, false);
|
||||
|
||||
Span<byte> target = _addressSpaceMirror.GetSpan(AddressToOffset(va), data.Length);
|
||||
bool changed = !data.SequenceEqual(target);
|
||||
|
||||
if (changed)
|
||||
{
|
||||
data.CopyTo(target);
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
catch (InvalidMemoryRegionException)
|
||||
{
|
||||
if (_invalidAccessHandler == null || !_invalidAccessHandler(va))
|
||||
{
|
||||
throw;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ReadOnlySpan<byte> GetSpan(ulong va, int size, bool tracked = false)
|
||||
{
|
||||
if (tracked)
|
||||
{
|
||||
SignalMemoryTracking(va, (ulong)size, write: false);
|
||||
}
|
||||
else
|
||||
{
|
||||
AssertMapped(va, (ulong)size);
|
||||
}
|
||||
|
||||
if (size == 0)
|
||||
{
|
||||
return ReadOnlySpan<byte>.Empty;
|
||||
}
|
||||
|
||||
return _addressSpaceMirror.GetSpan(AddressToOffset(va), size);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public WritableRegion GetWritableRegion(ulong va, int size, bool tracked = false)
|
||||
{
|
||||
if (tracked)
|
||||
{
|
||||
SignalMemoryTracking(va, (ulong)size, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
AssertMapped(va, (ulong)size);
|
||||
}
|
||||
|
||||
return _addressSpaceMirror.GetWritableRegion(AddressToOffset(va), size);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ref T GetRef<T>(ulong va) where T : unmanaged
|
||||
{
|
||||
SignalMemoryTracking(va, (ulong)Unsafe.SizeOf<T>(), true);
|
||||
|
||||
return ref _addressSpaceMirror.GetRef<T>(AddressToOffset(va));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool IsMapped(ulong va)
|
||||
{
|
||||
return ValidateAddress(va) && IsMappedImpl(va);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private bool IsMappedImpl(ulong va)
|
||||
{
|
||||
ulong page = va >> PageBits;
|
||||
|
||||
int bit = (int)((page & 31) << 1);
|
||||
|
||||
int pageIndex = (int)(page >> PageToPteShift);
|
||||
ref ulong pageRef = ref _pageBitmap[pageIndex];
|
||||
|
||||
ulong pte = Volatile.Read(ref pageRef);
|
||||
|
||||
return ((pte >> bit) & 3) != 0;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsRangeMapped(ulong va, ulong size)
|
||||
{
|
||||
AssertValidAddressAndSize(va, size);
|
||||
|
||||
return IsRangeMappedImpl(va, size);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void GetPageBlockRange(ulong pageStart, ulong pageEnd, out ulong startMask, out ulong endMask, out int pageIndex, out int pageEndIndex)
|
||||
{
|
||||
startMask = ulong.MaxValue << ((int)(pageStart & 31) << 1);
|
||||
endMask = ulong.MaxValue >> (64 - ((int)(pageEnd & 31) << 1));
|
||||
|
||||
pageIndex = (int)(pageStart >> PageToPteShift);
|
||||
pageEndIndex = (int)((pageEnd - 1) >> PageToPteShift);
|
||||
}
|
||||
|
||||
private bool IsRangeMappedImpl(ulong va, ulong size)
|
||||
{
|
||||
int pages = GetPagesCount(va, size, out _);
|
||||
|
||||
if (pages == 1)
|
||||
{
|
||||
return IsMappedImpl(va);
|
||||
}
|
||||
|
||||
ulong pageStart = va >> PageBits;
|
||||
ulong pageEnd = pageStart + (ulong)pages;
|
||||
|
||||
GetPageBlockRange(pageStart, pageEnd, out ulong startMask, out ulong endMask, out int pageIndex, out int pageEndIndex);
|
||||
|
||||
// Check if either bit in each 2 bit page entry is set.
|
||||
// OR the block with itself shifted down by 1, and check the first bit of each entry.
|
||||
|
||||
ulong mask = BlockMappedMask & startMask;
|
||||
|
||||
while (pageIndex <= pageEndIndex)
|
||||
{
|
||||
if (pageIndex == pageEndIndex)
|
||||
{
|
||||
mask &= endMask;
|
||||
}
|
||||
|
||||
ref ulong pageRef = ref _pageBitmap[pageIndex++];
|
||||
ulong pte = Volatile.Read(ref pageRef);
|
||||
|
||||
pte |= pte >> 1;
|
||||
if ((pte & mask) != mask)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
mask = BlockMappedMask;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IEnumerable<HostMemoryRange> GetHostRegions(ulong va, ulong size)
|
||||
{
|
||||
AssertValidAddressAndSize(va, size);
|
||||
|
||||
return Enumerable.Repeat(new HostMemoryRange((nuint)(ulong)_addressSpaceMirror.GetPointer(AddressToOffset(va), size), size), 1);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IEnumerable<MemoryRange> GetPhysicalRegions(ulong va, ulong size)
|
||||
{
|
||||
int pages = GetPagesCount(va, (uint)size, out va);
|
||||
|
||||
var regions = new List<MemoryRange>();
|
||||
|
||||
ulong regionStart = GetPhysicalAddressChecked(va);
|
||||
ulong regionSize = PageSize;
|
||||
|
||||
for (int page = 0; page < pages - 1; page++)
|
||||
{
|
||||
if (!ValidateAddress(va + PageSize))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
ulong newPa = GetPhysicalAddressChecked(va + PageSize);
|
||||
|
||||
if (GetPhysicalAddressChecked(va) + PageSize != newPa)
|
||||
{
|
||||
regions.Add(new MemoryRange(regionStart, regionSize));
|
||||
regionStart = newPa;
|
||||
regionSize = 0;
|
||||
}
|
||||
|
||||
va += PageSize;
|
||||
regionSize += PageSize;
|
||||
}
|
||||
|
||||
regions.Add(new MemoryRange(regionStart, regionSize));
|
||||
|
||||
return regions;
|
||||
}
|
||||
|
||||
private ulong GetPhysicalAddressChecked(ulong va)
|
||||
{
|
||||
if (!IsMapped(va))
|
||||
{
|
||||
ThrowInvalidMemoryRegionException($"Not mapped: va=0x{va:X16}");
|
||||
}
|
||||
|
||||
return GetPhysicalAddressInternal(va);
|
||||
}
|
||||
|
||||
private ulong GetPhysicalAddressInternal(ulong va)
|
||||
{
|
||||
return _pageTable.Read(va) + (va & PageMask);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
/// <remarks>
|
||||
/// This function also validates that the given range is both valid and mapped, and will throw if it is not.
|
||||
/// </remarks>
|
||||
public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null)
|
||||
{
|
||||
AssertValidAddressAndSize(va, size);
|
||||
|
||||
if (precise)
|
||||
{
|
||||
Tracking.VirtualMemoryEvent(va, size, write, precise: true, exemptId);
|
||||
return;
|
||||
}
|
||||
|
||||
// Software table, used for managed memory tracking.
|
||||
|
||||
int pages = GetPagesCount(va, size, out _);
|
||||
ulong pageStart = va >> PageBits;
|
||||
|
||||
if (pages == 1)
|
||||
{
|
||||
ulong tag = (ulong)(write ? HostMappedPtBits.WriteTracked : HostMappedPtBits.ReadWriteTracked);
|
||||
|
||||
int bit = (int)((pageStart & 31) << 1);
|
||||
|
||||
int pageIndex = (int)(pageStart >> PageToPteShift);
|
||||
ref ulong pageRef = ref _pageBitmap[pageIndex];
|
||||
|
||||
ulong pte = Volatile.Read(ref pageRef);
|
||||
ulong state = ((pte >> bit) & 3);
|
||||
|
||||
if (state >= tag)
|
||||
{
|
||||
Tracking.VirtualMemoryEvent(va, size, write, precise: false, exemptId);
|
||||
return;
|
||||
}
|
||||
else if (state == 0)
|
||||
{
|
||||
ThrowInvalidMemoryRegionException($"Not mapped: va=0x{va:X16}, size=0x{size:X16}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ulong pageEnd = pageStart + (ulong)pages;
|
||||
|
||||
GetPageBlockRange(pageStart, pageEnd, out ulong startMask, out ulong endMask, out int pageIndex, out int pageEndIndex);
|
||||
|
||||
ulong mask = startMask;
|
||||
|
||||
ulong anyTrackingTag = (ulong)HostMappedPtBits.WriteTrackedReplicated;
|
||||
|
||||
while (pageIndex <= pageEndIndex)
|
||||
{
|
||||
if (pageIndex == pageEndIndex)
|
||||
{
|
||||
mask &= endMask;
|
||||
}
|
||||
|
||||
ref ulong pageRef = ref _pageBitmap[pageIndex++];
|
||||
|
||||
ulong pte = Volatile.Read(ref pageRef);
|
||||
ulong mappedMask = mask & BlockMappedMask;
|
||||
|
||||
ulong mappedPte = pte | (pte >> 1);
|
||||
if ((mappedPte & mappedMask) != mappedMask)
|
||||
{
|
||||
ThrowInvalidMemoryRegionException($"Not mapped: va=0x{va:X16}, size=0x{size:X16}");
|
||||
}
|
||||
|
||||
pte &= mask;
|
||||
if ((pte & anyTrackingTag) != 0) // Search for any tracking.
|
||||
{
|
||||
// Writes trigger any tracking.
|
||||
// Only trigger tracking from reads if both bits are set on any page.
|
||||
if (write || (pte & (pte >> 1) & BlockMappedMask) != 0)
|
||||
{
|
||||
Tracking.VirtualMemoryEvent(va, size, write, precise: false, exemptId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mask = ulong.MaxValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the number of pages in a virtual address range.
|
||||
/// </summary>
|
||||
/// <param name="va">Virtual address of the range</param>
|
||||
/// <param name="size">Size of the range</param>
|
||||
/// <param name="startVa">The virtual address of the beginning of the first page</param>
|
||||
/// <remarks>This function does not differentiate between allocated and unallocated pages.</remarks>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private int GetPagesCount(ulong va, ulong size, out ulong startVa)
|
||||
{
|
||||
// WARNING: Always check if ulong does not overflow during the operations.
|
||||
startVa = va & ~(ulong)PageMask;
|
||||
ulong vaSpan = (va - startVa + size + PageMask) & ~(ulong)PageMask;
|
||||
|
||||
return (int)(vaSpan / PageSize);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void TrackingReprotect(ulong va, ulong size, MemoryPermission protection)
|
||||
{
|
||||
// Protection is inverted on software pages, since the default value is 0.
|
||||
protection = (~protection) & MemoryPermission.ReadAndWrite;
|
||||
|
||||
int pages = GetPagesCount(va, size, out va);
|
||||
ulong pageStart = va >> PageBits;
|
||||
|
||||
if (pages == 1)
|
||||
{
|
||||
ulong protTag = protection switch
|
||||
{
|
||||
MemoryPermission.None => (ulong)HostMappedPtBits.Mapped,
|
||||
MemoryPermission.Write => (ulong)HostMappedPtBits.WriteTracked,
|
||||
_ => (ulong)HostMappedPtBits.ReadWriteTracked,
|
||||
};
|
||||
|
||||
int bit = (int)((pageStart & 31) << 1);
|
||||
|
||||
ulong tagMask = 3UL << bit;
|
||||
ulong invTagMask = ~tagMask;
|
||||
|
||||
ulong tag = protTag << bit;
|
||||
|
||||
int pageIndex = (int)(pageStart >> PageToPteShift);
|
||||
ref ulong pageRef = ref _pageBitmap[pageIndex];
|
||||
|
||||
ulong pte;
|
||||
|
||||
do
|
||||
{
|
||||
pte = Volatile.Read(ref pageRef);
|
||||
}
|
||||
while ((pte & tagMask) != 0 && Interlocked.CompareExchange(ref pageRef, (pte & invTagMask) | tag, pte) != pte);
|
||||
}
|
||||
else
|
||||
{
|
||||
ulong pageEnd = pageStart + (ulong)pages;
|
||||
|
||||
GetPageBlockRange(pageStart, pageEnd, out ulong startMask, out ulong endMask, out int pageIndex, out int pageEndIndex);
|
||||
|
||||
ulong mask = startMask;
|
||||
|
||||
ulong protTag = protection switch
|
||||
{
|
||||
MemoryPermission.None => (ulong)HostMappedPtBits.MappedReplicated,
|
||||
MemoryPermission.Write => (ulong)HostMappedPtBits.WriteTrackedReplicated,
|
||||
_ => (ulong)HostMappedPtBits.ReadWriteTrackedReplicated,
|
||||
};
|
||||
|
||||
while (pageIndex <= pageEndIndex)
|
||||
{
|
||||
if (pageIndex == pageEndIndex)
|
||||
{
|
||||
mask &= endMask;
|
||||
}
|
||||
|
||||
ref ulong pageRef = ref _pageBitmap[pageIndex++];
|
||||
|
||||
ulong pte;
|
||||
ulong mappedMask;
|
||||
|
||||
// Change the protection of all 2 bit entries that are mapped.
|
||||
do
|
||||
{
|
||||
pte = Volatile.Read(ref pageRef);
|
||||
|
||||
mappedMask = pte | (pte >> 1);
|
||||
mappedMask |= (mappedMask & BlockMappedMask) << 1;
|
||||
mappedMask &= mask; // Only update mapped pages within the given range.
|
||||
}
|
||||
while (Interlocked.CompareExchange(ref pageRef, (pte & (~mappedMask)) | (protTag & mappedMask), pte) != pte);
|
||||
|
||||
mask = ulong.MaxValue;
|
||||
}
|
||||
}
|
||||
|
||||
protection = protection switch
|
||||
{
|
||||
MemoryPermission.None => MemoryPermission.ReadAndWrite,
|
||||
MemoryPermission.Write => MemoryPermission.Read,
|
||||
_ => MemoryPermission.None
|
||||
};
|
||||
|
||||
_addressSpace.Reprotect(AddressToOffset(va), size, protection, false);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public RegionHandle BeginTracking(ulong address, ulong size, int id)
|
||||
{
|
||||
return Tracking.BeginTracking(address, size, id);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public MultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id)
|
||||
{
|
||||
return Tracking.BeginGranularTracking(address, size, handles, granularity, id);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public SmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id)
|
||||
{
|
||||
return Tracking.BeginSmartGranularTracking(address, size, granularity, id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the given address mapping to the page table.
|
||||
/// </summary>
|
||||
/// <param name="va">Virtual memory address</param>
|
||||
/// <param name="size">Size to be mapped</param>
|
||||
private void AddMapping(ulong va, ulong size)
|
||||
{
|
||||
int pages = GetPagesCount(va, size, out _);
|
||||
ulong pageStart = va >> PageBits;
|
||||
ulong pageEnd = pageStart + (ulong)pages;
|
||||
|
||||
GetPageBlockRange(pageStart, pageEnd, out ulong startMask, out ulong endMask, out int pageIndex, out int pageEndIndex);
|
||||
|
||||
ulong mask = startMask;
|
||||
|
||||
while (pageIndex <= pageEndIndex)
|
||||
{
|
||||
if (pageIndex == pageEndIndex)
|
||||
{
|
||||
mask &= endMask;
|
||||
}
|
||||
|
||||
ref ulong pageRef = ref _pageBitmap[pageIndex++];
|
||||
|
||||
ulong pte;
|
||||
ulong mappedMask;
|
||||
|
||||
// Map all 2-bit entries that are unmapped.
|
||||
do
|
||||
{
|
||||
pte = Volatile.Read(ref pageRef);
|
||||
|
||||
mappedMask = pte | (pte >> 1);
|
||||
mappedMask |= (mappedMask & BlockMappedMask) << 1;
|
||||
mappedMask |= ~mask; // Treat everything outside the range as mapped, thus unchanged.
|
||||
}
|
||||
while (Interlocked.CompareExchange(ref pageRef, (pte & mappedMask) | (BlockMappedMask & (~mappedMask)), pte) != pte);
|
||||
|
||||
mask = ulong.MaxValue;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the given address mapping from the page table.
|
||||
/// </summary>
|
||||
/// <param name="va">Virtual memory address</param>
|
||||
/// <param name="size">Size to be unmapped</param>
|
||||
private void RemoveMapping(ulong va, ulong size)
|
||||
{
|
||||
int pages = GetPagesCount(va, size, out _);
|
||||
ulong pageStart = va >> PageBits;
|
||||
ulong pageEnd = pageStart + (ulong)pages;
|
||||
|
||||
GetPageBlockRange(pageStart, pageEnd, out ulong startMask, out ulong endMask, out int pageIndex, out int pageEndIndex);
|
||||
|
||||
startMask = ~startMask;
|
||||
endMask = ~endMask;
|
||||
|
||||
ulong mask = startMask;
|
||||
|
||||
while (pageIndex <= pageEndIndex)
|
||||
{
|
||||
if (pageIndex == pageEndIndex)
|
||||
{
|
||||
mask |= endMask;
|
||||
}
|
||||
|
||||
ref ulong pageRef = ref _pageBitmap[pageIndex++];
|
||||
ulong pte;
|
||||
|
||||
do
|
||||
{
|
||||
pte = Volatile.Read(ref pageRef);
|
||||
}
|
||||
while (Interlocked.CompareExchange(ref pageRef, pte & mask, pte) != pte);
|
||||
|
||||
mask = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private ulong AddressToOffset(ulong address)
|
||||
{
|
||||
if (address < ReservedSize)
|
||||
{
|
||||
throw new ArgumentException($"Invalid address 0x{address:x16}");
|
||||
}
|
||||
|
||||
return address - ReservedSize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes of resources used by the memory manager.
|
||||
/// </summary>
|
||||
protected override void Destroy()
|
||||
{
|
||||
_addressSpace.Dispose();
|
||||
_addressSpaceMirror.Dispose();
|
||||
_memoryEh.Dispose();
|
||||
}
|
||||
|
||||
private static void ThrowInvalidMemoryRegionException(string message) => throw new InvalidMemoryRegionException(message);
|
||||
}
|
||||
}
|
409
src/Ryujinx.Cpu/Nce/NceAsmTable.cs
Normal file
409
src/Ryujinx.Cpu/Nce/NceAsmTable.cs
Normal file
@ -0,0 +1,409 @@
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Cpu.Nce
|
||||
{
|
||||
static class NceAsmTable
|
||||
{
|
||||
public static uint[] GetTpidrEl0Code = new uint[]
|
||||
{
|
||||
GetMrsTpidrEl0(0), // mrs x0, tpidr_el0
|
||||
0xd65f03c0u, // ret
|
||||
};
|
||||
|
||||
public static uint[] ThreadStartCode = new uint[]
|
||||
{
|
||||
0xa9ae53f3u, // stp x19, x20, [sp, #-288]!
|
||||
0xa9015bf5u, // stp x21, x22, [sp, #16]
|
||||
0xa90263f7u, // stp x23, x24, [sp, #32]
|
||||
0xa9036bf9u, // stp x25, x26, [sp, #48]
|
||||
0xa90473fbu, // stp x27, x28, [sp, #64]
|
||||
0xa9057bfdu, // stp x29, x30, [sp, #80]
|
||||
0x6d0627e8u, // stp d8, d9, [sp, #96]
|
||||
0x6d072feau, // stp d10, d11, [sp, #112]
|
||||
0x6d0837ecu, // stp d12, d13, [sp, #128]
|
||||
0x6d093feeu, // stp d14, d15, [sp, #144]
|
||||
0x6d0a47f0u, // stp d16, d17, [sp, #160]
|
||||
0x6d0b4ff2u, // stp d18, d19, [sp, #176]
|
||||
0x6d0c57f4u, // stp d20, d21, [sp, #192]
|
||||
0x6d0d5ff6u, // stp d22, d23, [sp, #208]
|
||||
0x6d0e67f8u, // stp d24, d25, [sp, #224]
|
||||
0x6d0f6ffau, // stp d26, d27, [sp, #240]
|
||||
0x6d1077fcu, // stp d28, d29, [sp, #256]
|
||||
0x6d117ffeu, // stp d30, d31, [sp, #272]
|
||||
0xb9031c1fu, // str wzr, [x0, #796]
|
||||
0x910003e1u, // mov x1, sp
|
||||
0xf9019001u, // str x1, [x0, #800]
|
||||
0xa9410c02u, // ldp x2, x3, [x0, #16]
|
||||
0xa9421404u, // ldp x4, x5, [x0, #32]
|
||||
0xa9431c06u, // ldp x6, x7, [x0, #48]
|
||||
0xa9442408u, // ldp x8, x9, [x0, #64]
|
||||
0xa9452c0au, // ldp x10, x11, [x0, #80]
|
||||
0xa946340cu, // ldp x12, x13, [x0, #96]
|
||||
0xa9473c0eu, // ldp x14, x15, [x0, #112]
|
||||
0xa9484410u, // ldp x16, x17, [x0, #128]
|
||||
0xa9494c12u, // ldp x18, x19, [x0, #144]
|
||||
0xa94a5414u, // ldp x20, x21, [x0, #160]
|
||||
0xa94b5c16u, // ldp x22, x23, [x0, #176]
|
||||
0xa94c6418u, // ldp x24, x25, [x0, #192]
|
||||
0xa94d6c1au, // ldp x26, x27, [x0, #208]
|
||||
0xa94e741cu, // ldp x28, x29, [x0, #224]
|
||||
0xad480400u, // ldp q0, q1, [x0, #256]
|
||||
0xad490c02u, // ldp q2, q3, [x0, #288]
|
||||
0xad4a1404u, // ldp q4, q5, [x0, #320]
|
||||
0xad4b1c06u, // ldp q6, q7, [x0, #352]
|
||||
0xad4c2408u, // ldp q8, q9, [x0, #384]
|
||||
0xad4d2c0au, // ldp q10, q11, [x0, #416]
|
||||
0xad4e340cu, // ldp q12, q13, [x0, #448]
|
||||
0xad4f3c0eu, // ldp q14, q15, [x0, #480]
|
||||
0xad504410u, // ldp q16, q17, [x0, #512]
|
||||
0xad514c12u, // ldp q18, q19, [x0, #544]
|
||||
0xad525414u, // ldp q20, q21, [x0, #576]
|
||||
0xad535c16u, // ldp q22, q23, [x0, #608]
|
||||
0xad546418u, // ldp q24, q25, [x0, #640]
|
||||
0xad556c1au, // ldp q26, q27, [x0, #672]
|
||||
0xad56741cu, // ldp q28, q29, [x0, #704]
|
||||
0xad577c1eu, // ldp q30, q31, [x0, #736]
|
||||
0xa94f041eu, // ldp x30, x1, [x0, #240]
|
||||
0x9100003fu, // mov sp, x1
|
||||
0xa9400400u, // ldp x0, x1, [x0]
|
||||
0xd61f03c0u, // br x30
|
||||
};
|
||||
|
||||
public static uint[] ExceptionHandlerEntryCode = new uint[]
|
||||
{
|
||||
0xa9bc53f3u, // stp x19, x20, [sp, #-64]!
|
||||
0xa9015bf5u, // stp x21, x22, [sp, #16]
|
||||
0xa90263f7u, // stp x23, x24, [sp, #32]
|
||||
0xf9001bf9u, // str x25, [sp, #48]
|
||||
0xaa0003f3u, // mov x19, x0
|
||||
0xaa0103f4u, // mov x20, x1
|
||||
0xaa0203f5u, // mov x21, x2
|
||||
0x910003f6u, // mov x22, sp
|
||||
0xaa1e03f7u, // mov x23, x30
|
||||
0xd2800018u, // mov x24, #0x0
|
||||
0xf2a00018u, // movk x24, #0x0, lsl #16
|
||||
0xf2c00018u, // movk x24, #0x0, lsl #32
|
||||
0xf2e00018u, // movk x24, #0x0, lsl #48
|
||||
0xf85f8319u, // ldur x25, [x24, #-8]
|
||||
0x8b191319u, // add x25, x24, x25, lsl #4
|
||||
GetMrsTpidrEl0(1), // mrs x1, tpidr_el0
|
||||
0xeb19031fu, // cmp x24, x25
|
||||
0x540000a0u, // b.eq 13c <ExceptionHandlerEntryCode+0x58>
|
||||
0xf8410702u, // ldr x2, [x24], #16
|
||||
0xeb02003fu, // cmp x1, x2
|
||||
0x54000080u, // b.eq 144 <ExceptionHandlerEntryCode+0x60>
|
||||
0x17fffffbu, // b 124 <ExceptionHandlerEntryCode+0x40>
|
||||
0xd2800018u, // mov x24, #0x0
|
||||
0x14000002u, // b 148 <ExceptionHandlerEntryCode+0x64>
|
||||
0xf85f8318u, // ldur x24, [x24, #-8]
|
||||
0xb4000438u, // cbz x24, 1cc <ExceptionHandlerEntryCode+0xe8>
|
||||
0xf9419300u, // ldr x0, [x24, #800]
|
||||
0x9100001fu, // mov sp, x0
|
||||
0x7100027fu, // cmp w19, #0x0
|
||||
0x54000180u, // b.eq 188 <ExceptionHandlerEntryCode+0xa4>
|
||||
0x52800020u, // mov w0, #0x1
|
||||
0xb9031f00u, // str w0, [x24, #796]
|
||||
0xaa1303e0u, // mov x0, x19
|
||||
0xaa1403e1u, // mov x1, x20
|
||||
0xaa1503e2u, // mov x2, x21
|
||||
0xd2800008u, // mov x8, #0x0
|
||||
0xf2a00008u, // movk x8, #0x0, lsl #16
|
||||
0xf2c00008u, // movk x8, #0x0, lsl #32
|
||||
0xf2e00008u, // movk x8, #0x0, lsl #48
|
||||
0xd63f0100u, // blr x8
|
||||
0x1400000au, // b 1ac <ExceptionHandlerEntryCode+0xc8>
|
||||
0xb9431f00u, // ldr w0, [x24, #796]
|
||||
0x35000120u, // cbnz w0, 1b0 <ExceptionHandlerEntryCode+0xcc>
|
||||
0x52800020u, // mov w0, #0x1
|
||||
0xb9031f00u, // str w0, [x24, #796]
|
||||
0xd2800000u, // mov x0, #0x0
|
||||
0xf2a00000u, // movk x0, #0x0, lsl #16
|
||||
0xf2c00000u, // movk x0, #0x0, lsl #32
|
||||
0xf2e00000u, // movk x0, #0x0, lsl #48
|
||||
0xd63f0000u, // blr x0
|
||||
0xb9031f1fu, // str wzr, [x24, #796]
|
||||
0x910002dfu, // mov sp, x22
|
||||
0xaa1703feu, // mov x30, x23
|
||||
0xa9415bf5u, // ldp x21, x22, [sp, #16]
|
||||
0xa94263f7u, // ldp x23, x24, [sp, #32]
|
||||
0xa9436bf9u, // ldp x25, x26, [sp, #48]
|
||||
0xa8c453f3u, // ldp x19, x20, [sp], #64
|
||||
0xd65f03c0u, // ret
|
||||
0xaa1303e0u, // mov x0, x19
|
||||
0xaa1403e1u, // mov x1, x20
|
||||
0xaa1503e2u, // mov x2, x21
|
||||
0x910002dfu, // mov sp, x22
|
||||
0xa9415bf5u, // ldp x21, x22, [sp, #16]
|
||||
0xa94263f7u, // ldp x23, x24, [sp, #32]
|
||||
0xf9401bf9u, // ldr x25, [sp, #48]
|
||||
0xa8c453f3u, // ldp x19, x20, [sp], #64
|
||||
0xd2800003u, // mov x3, #0x0
|
||||
0xf2a00003u, // movk x3, #0x0, lsl #16
|
||||
0xf2c00003u, // movk x3, #0x0, lsl #32
|
||||
0xf2e00003u, // movk x3, #0x0, lsl #48
|
||||
0xd61f0060u, // br x3
|
||||
};
|
||||
|
||||
public static uint[] SvcPatchCode = new uint[]
|
||||
{
|
||||
0xa9be53f3u, // stp x19, x20, [sp, #-32]!
|
||||
0xf9000bf5u, // str x21, [sp, #16]
|
||||
0xd2800013u, // mov x19, #0x0
|
||||
0xf2a00013u, // movk x19, #0x0, lsl #16
|
||||
0xf2c00013u, // movk x19, #0x0, lsl #32
|
||||
0xf2e00013u, // movk x19, #0x0, lsl #48
|
||||
GetMrsTpidrEl0(20), // mrs x20, tpidr_el0
|
||||
0xf8410675u, // ldr x21, [x19], #16
|
||||
0xeb15029fu, // cmp x20, x21
|
||||
0x54000040u, // b.eq 22c <SvcPatchCode+0x2c>
|
||||
0x17fffffdu, // b 21c <SvcPatchCode+0x1c>
|
||||
0xf85f8273u, // ldur x19, [x19, #-8]
|
||||
0xa9000660u, // stp x0, x1, [x19]
|
||||
0xa9010e62u, // stp x2, x3, [x19, #16]
|
||||
0xa9021664u, // stp x4, x5, [x19, #32]
|
||||
0xa9031e66u, // stp x6, x7, [x19, #48]
|
||||
0xa9042668u, // stp x8, x9, [x19, #64]
|
||||
0xa9052e6au, // stp x10, x11, [x19, #80]
|
||||
0xa906366cu, // stp x12, x13, [x19, #96]
|
||||
0xa9073e6eu, // stp x14, x15, [x19, #112]
|
||||
0xa9084670u, // stp x16, x17, [x19, #128]
|
||||
0xf9400bf5u, // ldr x21, [sp, #16]
|
||||
0xa8c253e0u, // ldp x0, x20, [sp], #32
|
||||
0xa9090272u, // stp x18, x0, [x19, #144]
|
||||
0xa90a5674u, // stp x20, x21, [x19, #160]
|
||||
0xa90b5e76u, // stp x22, x23, [x19, #176]
|
||||
0xa90c6678u, // stp x24, x25, [x19, #192]
|
||||
0xa90d6e7au, // stp x26, x27, [x19, #208]
|
||||
0xa90e767cu, // stp x28, x29, [x19, #224]
|
||||
0x910003e0u, // mov x0, sp
|
||||
0xa90f027eu, // stp x30, x0, [x19, #240]
|
||||
0xad080660u, // stp q0, q1, [x19, #256]
|
||||
0xad090e62u, // stp q2, q3, [x19, #288]
|
||||
0xad0a1664u, // stp q4, q5, [x19, #320]
|
||||
0xad0b1e66u, // stp q6, q7, [x19, #352]
|
||||
0xad0c2668u, // stp q8, q9, [x19, #384]
|
||||
0xad0d2e6au, // stp q10, q11, [x19, #416]
|
||||
0xad0e366cu, // stp q12, q13, [x19, #448]
|
||||
0xad0f3e6eu, // stp q14, q15, [x19, #480]
|
||||
0xad104670u, // stp q16, q17, [x19, #512]
|
||||
0xad114e72u, // stp q18, q19, [x19, #544]
|
||||
0xad125674u, // stp q20, q21, [x19, #576]
|
||||
0xad135e76u, // stp q22, q23, [x19, #608]
|
||||
0xad146678u, // stp q24, q25, [x19, #640]
|
||||
0xad156e7au, // stp q26, q27, [x19, #672]
|
||||
0xad16767cu, // stp q28, q29, [x19, #704]
|
||||
0xad177e7eu, // stp q30, q31, [x19, #736]
|
||||
0xf9419260u, // ldr x0, [x19, #800]
|
||||
0x9100001fu, // mov sp, x0
|
||||
0x52800020u, // mov w0, #0x1
|
||||
0xb9031e60u, // str w0, [x19, #796]
|
||||
0x52800000u, // mov w0, #0x0
|
||||
0xf941aa68u, // ldr x8, [x19, #848]
|
||||
0xd63f0100u, // blr x8
|
||||
0x35000280u, // cbnz w0, 328 <SvcPatchCode+0x128>
|
||||
0x6d517ffeu, // ldp d30, d31, [sp, #272]
|
||||
0x6d5077fcu, // ldp d28, d29, [sp, #256]
|
||||
0x6d4f6ffau, // ldp d26, d27, [sp, #240]
|
||||
0x6d4e67f8u, // ldp d24, d25, [sp, #224]
|
||||
0x6d4d5ff6u, // ldp d22, d23, [sp, #208]
|
||||
0x6d4c57f4u, // ldp d20, d21, [sp, #192]
|
||||
0x6d4b4ff2u, // ldp d18, d19, [sp, #176]
|
||||
0x6d4a47f0u, // ldp d16, d17, [sp, #160]
|
||||
0x6d493feeu, // ldp d14, d15, [sp, #144]
|
||||
0x6d4837ecu, // ldp d12, d13, [sp, #128]
|
||||
0x6d472feau, // ldp d10, d11, [sp, #112]
|
||||
0x6d4627e8u, // ldp d8, d9, [sp, #96]
|
||||
0xa9457bfdu, // ldp x29, x30, [sp, #80]
|
||||
0xa94473fbu, // ldp x27, x28, [sp, #64]
|
||||
0xa9436bf9u, // ldp x25, x26, [sp, #48]
|
||||
0xa94263f7u, // ldp x23, x24, [sp, #32]
|
||||
0xa9415bf5u, // ldp x21, x22, [sp, #16]
|
||||
0xa8d253f3u, // ldp x19, x20, [sp], #288
|
||||
0xd65f03c0u, // ret
|
||||
0xb9031e7fu, // str wzr, [x19, #796]
|
||||
0xa94f027eu, // ldp x30, x0, [x19, #240]
|
||||
0x9100001fu, // mov sp, x0
|
||||
0xa9400660u, // ldp x0, x1, [x19]
|
||||
0xa9410e62u, // ldp x2, x3, [x19, #16]
|
||||
0xa9421664u, // ldp x4, x5, [x19, #32]
|
||||
0xa9431e66u, // ldp x6, x7, [x19, #48]
|
||||
0xa9442668u, // ldp x8, x9, [x19, #64]
|
||||
0xa9452e6au, // ldp x10, x11, [x19, #80]
|
||||
0xa946366cu, // ldp x12, x13, [x19, #96]
|
||||
0xa9473e6eu, // ldp x14, x15, [x19, #112]
|
||||
0xa9484670u, // ldp x16, x17, [x19, #128]
|
||||
0xf9404a72u, // ldr x18, [x19, #144]
|
||||
0xa94a5674u, // ldp x20, x21, [x19, #160]
|
||||
0xa94b5e76u, // ldp x22, x23, [x19, #176]
|
||||
0xa94c6678u, // ldp x24, x25, [x19, #192]
|
||||
0xa94d6e7au, // ldp x26, x27, [x19, #208]
|
||||
0xa94e767cu, // ldp x28, x29, [x19, #224]
|
||||
0xad480660u, // ldp q0, q1, [x19, #256]
|
||||
0xad490e62u, // ldp q2, q3, [x19, #288]
|
||||
0xad4a1664u, // ldp q4, q5, [x19, #320]
|
||||
0xad4b1e66u, // ldp q6, q7, [x19, #352]
|
||||
0xad4c2668u, // ldp q8, q9, [x19, #384]
|
||||
0xad4d2e6au, // ldp q10, q11, [x19, #416]
|
||||
0xad4e366cu, // ldp q12, q13, [x19, #448]
|
||||
0xad4f3e6eu, // ldp q14, q15, [x19, #480]
|
||||
0xad504670u, // ldp q16, q17, [x19, #512]
|
||||
0xad514e72u, // ldp q18, q19, [x19, #544]
|
||||
0xad525674u, // ldp q20, q21, [x19, #576]
|
||||
0xad535e76u, // ldp q22, q23, [x19, #608]
|
||||
0xad546678u, // ldp q24, q25, [x19, #640]
|
||||
0xad556e7au, // ldp q26, q27, [x19, #672]
|
||||
0xad56767cu, // ldp q28, q29, [x19, #704]
|
||||
0xad577e7eu, // ldp q30, q31, [x19, #736]
|
||||
0xf9404e73u, // ldr x19, [x19, #152]
|
||||
0x14000000u, // b 3b4 <SvcPatchCode+0x1b4>
|
||||
};
|
||||
|
||||
public static uint[] MrsTpidrroEl0PatchCode = new uint[]
|
||||
{
|
||||
0xa9be4fffu, // stp xzr, x19, [sp, #-32]!
|
||||
0xa90157f4u, // stp x20, x21, [sp, #16]
|
||||
0xd2800013u, // mov x19, #0x0
|
||||
0xf2a00013u, // movk x19, #0x0, lsl #16
|
||||
0xf2c00013u, // movk x19, #0x0, lsl #32
|
||||
0xf2e00013u, // movk x19, #0x0, lsl #48
|
||||
GetMrsTpidrEl0(20), // mrs x20, tpidr_el0
|
||||
0xf8410675u, // ldr x21, [x19], #16
|
||||
0xeb15029fu, // cmp x20, x21
|
||||
0x54000040u, // b.eq 3e4 <MrsTpidrroEl0PatchCode+0x2c>
|
||||
0x17fffffdu, // b 3d4 <MrsTpidrroEl0PatchCode+0x1c>
|
||||
0xf85f8273u, // ldur x19, [x19, #-8]
|
||||
0xf9418673u, // ldr x19, [x19, #776]
|
||||
0xf90003f3u, // str x19, [sp]
|
||||
0xa94157f4u, // ldp x20, x21, [sp, #16]
|
||||
0xf94007f3u, // ldr x19, [sp, #8]
|
||||
0xf84207e0u, // ldr x0, [sp], #32
|
||||
0x14000000u, // b 3fc <MrsTpidrroEl0PatchCode+0x44>
|
||||
};
|
||||
|
||||
public static uint[] MrsTpidrEl0PatchCode = new uint[]
|
||||
{
|
||||
0xa9be4fffu, // stp xzr, x19, [sp, #-32]!
|
||||
0xa90157f4u, // stp x20, x21, [sp, #16]
|
||||
0xd2800013u, // mov x19, #0x0
|
||||
0xf2a00013u, // movk x19, #0x0, lsl #16
|
||||
0xf2c00013u, // movk x19, #0x0, lsl #32
|
||||
0xf2e00013u, // movk x19, #0x0, lsl #48
|
||||
GetMrsTpidrEl0(20), // mrs x20, tpidr_el0
|
||||
0xf8410675u, // ldr x21, [x19], #16
|
||||
0xeb15029fu, // cmp x20, x21
|
||||
0x54000040u, // b.eq 42c <MrsTpidrEl0PatchCode+0x2c>
|
||||
0x17fffffdu, // b 41c <MrsTpidrEl0PatchCode+0x1c>
|
||||
0xf85f8273u, // ldur x19, [x19, #-8]
|
||||
0xf9418273u, // ldr x19, [x19, #768]
|
||||
0xf90003f3u, // str x19, [sp]
|
||||
0xa94157f4u, // ldp x20, x21, [sp, #16]
|
||||
0xf94007f3u, // ldr x19, [sp, #8]
|
||||
0xf84207e0u, // ldr x0, [sp], #32
|
||||
0x14000000u, // b 444 <MrsTpidrEl0PatchCode+0x44>
|
||||
};
|
||||
|
||||
public static uint[] MrsCtrEl0PatchCode = new uint[]
|
||||
{
|
||||
0xa9be4fffu, // stp xzr, x19, [sp, #-32]!
|
||||
0xa90157f4u, // stp x20, x21, [sp, #16]
|
||||
0xd2800013u, // mov x19, #0x0
|
||||
0xf2a00013u, // movk x19, #0x0, lsl #16
|
||||
0xf2c00013u, // movk x19, #0x0, lsl #32
|
||||
0xf2e00013u, // movk x19, #0x0, lsl #48
|
||||
GetMrsTpidrEl0(20), // mrs x20, tpidr_el0
|
||||
0xf8410675u, // ldr x21, [x19], #16
|
||||
0xeb15029fu, // cmp x20, x21
|
||||
0x54000040u, // b.eq 474 <MrsCtrEl0PatchCode+0x2c>
|
||||
0x17fffffdu, // b 464 <MrsCtrEl0PatchCode+0x1c>
|
||||
0xf85f8273u, // ldur x19, [x19, #-8]
|
||||
0xf9419e73u, // ldr x19, [x19, #824]
|
||||
0xf90003f3u, // str x19, [sp]
|
||||
0xa94157f4u, // ldp x20, x21, [sp, #16]
|
||||
0xf94007f3u, // ldr x19, [sp, #8]
|
||||
0xf84207e0u, // ldr x0, [sp], #32
|
||||
0x14000000u, // b 48c <MrsCtrEl0PatchCode+0x44>
|
||||
};
|
||||
|
||||
public static uint[] MsrTpidrEl0PatchCode = new uint[]
|
||||
{
|
||||
0xa9be03f3u, // stp x19, x0, [sp, #-32]!
|
||||
0xa90157f4u, // stp x20, x21, [sp, #16]
|
||||
0xd2800013u, // mov x19, #0x0
|
||||
0xf2a00013u, // movk x19, #0x0, lsl #16
|
||||
0xf2c00013u, // movk x19, #0x0, lsl #32
|
||||
0xf2e00013u, // movk x19, #0x0, lsl #48
|
||||
GetMrsTpidrEl0(20), // mrs x20, tpidr_el0
|
||||
0xf8410675u, // ldr x21, [x19], #16
|
||||
0xeb15029fu, // cmp x20, x21
|
||||
0x54000040u, // b.eq 4bc <MsrTpidrEl0PatchCode+0x2c>
|
||||
0x17fffffdu, // b 4ac <MsrTpidrEl0PatchCode+0x1c>
|
||||
0xf85f8273u, // ldur x19, [x19, #-8]
|
||||
0xf94007f4u, // ldr x20, [sp, #8]
|
||||
0xf9018274u, // str x20, [x19, #768]
|
||||
0xa94157f4u, // ldp x20, x21, [sp, #16]
|
||||
0xf84207f3u, // ldr x19, [sp], #32
|
||||
0x14000000u, // b 4d0 <MsrTpidrEl0PatchCode+0x40>
|
||||
};
|
||||
|
||||
public static uint[] MrsCntpctEl0PatchCode = new uint[]
|
||||
{
|
||||
0xa9b407e0u, // stp x0, x1, [sp, #-192]!
|
||||
0xa9010fe2u, // stp x2, x3, [sp, #16]
|
||||
0xa90217e4u, // stp x4, x5, [sp, #32]
|
||||
0xa9031fe6u, // stp x6, x7, [sp, #48]
|
||||
0xa90427e8u, // stp x8, x9, [sp, #64]
|
||||
0xa9052feau, // stp x10, x11, [sp, #80]
|
||||
0xa90637ecu, // stp x12, x13, [sp, #96]
|
||||
0xa9073feeu, // stp x14, x15, [sp, #112]
|
||||
0xa90847f0u, // stp x16, x17, [sp, #128]
|
||||
0xa9094ff2u, // stp x18, x19, [sp, #144]
|
||||
0xa90a57f4u, // stp x20, x21, [sp, #160]
|
||||
0xf9005ffeu, // str x30, [sp, #184]
|
||||
0xd2800013u, // mov x19, #0x0
|
||||
0xf2a00013u, // movk x19, #0x0, lsl #16
|
||||
0xf2c00013u, // movk x19, #0x0, lsl #32
|
||||
0xf2e00013u, // movk x19, #0x0, lsl #48
|
||||
GetMrsTpidrEl0(20), // mrs x20, tpidr_el0
|
||||
0xf8410675u, // ldr x21, [x19], #16
|
||||
0xeb15029fu, // cmp x20, x21
|
||||
0x54000040u, // b.eq 528 <MrsCntpctEl0PatchCode+0x54>
|
||||
0x17fffffdu, // b 518 <MrsCntpctEl0PatchCode+0x44>
|
||||
0xf85f8273u, // ldur x19, [x19, #-8]
|
||||
0x52800020u, // mov w0, #0x1
|
||||
0xb9031e60u, // str w0, [x19, #796]
|
||||
0xd2800000u, // mov x0, #0x0
|
||||
0xf2a00000u, // movk x0, #0x0, lsl #16
|
||||
0xf2c00000u, // movk x0, #0x0, lsl #32
|
||||
0xf2e00000u, // movk x0, #0x0, lsl #48
|
||||
0xd63f0000u, // blr x0
|
||||
0xb9031e7fu, // str wzr, [x19, #796]
|
||||
0xf9005be0u, // str x0, [sp, #176]
|
||||
0xf9405ffeu, // ldr x30, [sp, #184]
|
||||
0xa94a57f4u, // ldp x20, x21, [sp, #160]
|
||||
0xa9494ff2u, // ldp x18, x19, [sp, #144]
|
||||
0xa94847f0u, // ldp x16, x17, [sp, #128]
|
||||
0xa9473feeu, // ldp x14, x15, [sp, #112]
|
||||
0xa94637ecu, // ldp x12, x13, [sp, #96]
|
||||
0xa9452feau, // ldp x10, x11, [sp, #80]
|
||||
0xa94427e8u, // ldp x8, x9, [sp, #64]
|
||||
0xa9431fe6u, // ldp x6, x7, [sp, #48]
|
||||
0xa94217e4u, // ldp x4, x5, [sp, #32]
|
||||
0xa9410fe2u, // ldp x2, x3, [sp, #16]
|
||||
0xa8cb07e0u, // ldp x0, x1, [sp], #176
|
||||
0xf84107e0u, // ldr x0, [sp], #16
|
||||
0x14000000u, // b 584 <MrsCntpctEl0PatchCode+0xb0>
|
||||
};
|
||||
|
||||
private static uint GetMrsTpidrEl0(uint rd)
|
||||
{
|
||||
if (OperatingSystem.IsMacOS())
|
||||
{
|
||||
return 0xd53bd060u | rd; // TPIDRRO
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0xd53bd040u | rd; // TPIDR
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
99
src/Ryujinx.Cpu/Nce/NceCpuContext.cs
Normal file
99
src/Ryujinx.Cpu/Nce/NceCpuContext.cs
Normal file
@ -0,0 +1,99 @@
|
||||
using ARMeilleure.Memory;
|
||||
using ARMeilleure.Signal;
|
||||
using Ryujinx.Cpu.Jit;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Memory;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Cpu.Nce
|
||||
{
|
||||
class NceCpuContext : ICpuContext
|
||||
{
|
||||
private delegate void ThreadStart(IntPtr nativeContextPtr);
|
||||
private delegate IntPtr GetTpidrEl0();
|
||||
private static MemoryBlock _codeBlock;
|
||||
private static ThreadStart _threadStart;
|
||||
private static GetTpidrEl0 _getTpidrEl0;
|
||||
|
||||
private readonly ITickSource _tickSource;
|
||||
private readonly ICpuMemoryManager _memoryManager;
|
||||
|
||||
static NceCpuContext()
|
||||
{
|
||||
ulong threadStartCodeSize = (ulong)NceAsmTable.ThreadStartCode.Length * 4;
|
||||
ulong enEntryCodeOffset = threadStartCodeSize;
|
||||
ulong ehEntryCodeSize = (ulong)NceAsmTable.ExceptionHandlerEntryCode.Length * 4;
|
||||
ulong getTpidrEl0CodeOffset = threadStartCodeSize + ehEntryCodeSize;
|
||||
ulong getTpidrEl0CodeSize = (ulong)NceAsmTable.GetTpidrEl0Code.Length * 4;
|
||||
|
||||
ulong size = BitUtils.AlignUp(threadStartCodeSize + ehEntryCodeSize + getTpidrEl0CodeSize, 0x1000UL);
|
||||
|
||||
MemoryBlock codeBlock = new MemoryBlock(size);
|
||||
|
||||
codeBlock.Write(0, MemoryMarshal.Cast<uint, byte>(NceAsmTable.ThreadStartCode.AsSpan()));
|
||||
codeBlock.Write(getTpidrEl0CodeOffset, MemoryMarshal.Cast<uint, byte>(NceAsmTable.GetTpidrEl0Code.AsSpan()));
|
||||
|
||||
NativeSignalHandler.Initialize(new JitMemoryAllocator());
|
||||
|
||||
NativeSignalHandler.InitializeSignalHandler(MemoryBlock.GetPageSize(), (IntPtr oldSignalHandlerSegfaultPtr, IntPtr signalHandlerPtr) =>
|
||||
{
|
||||
uint[] ehEntryCode = NcePatcher.GenerateExceptionHandlerEntry(oldSignalHandlerSegfaultPtr, signalHandlerPtr);
|
||||
codeBlock.Write(enEntryCodeOffset, MemoryMarshal.Cast<uint, byte>(ehEntryCode.AsSpan()));
|
||||
codeBlock.Reprotect(0, size, MemoryPermission.ReadAndExecute, true);
|
||||
return codeBlock.GetPointer(enEntryCodeOffset, ehEntryCodeSize);
|
||||
}, NceThreadPal.UnixSuspendSignal);
|
||||
|
||||
_threadStart = Marshal.GetDelegateForFunctionPointer<ThreadStart>(codeBlock.GetPointer(0, threadStartCodeSize));
|
||||
_getTpidrEl0 = Marshal.GetDelegateForFunctionPointer<GetTpidrEl0>(codeBlock.GetPointer(getTpidrEl0CodeOffset, getTpidrEl0CodeSize));
|
||||
_codeBlock = codeBlock;
|
||||
}
|
||||
|
||||
public NceCpuContext(ITickSource tickSource, ICpuMemoryManager memory, bool for64Bit)
|
||||
{
|
||||
_tickSource = tickSource;
|
||||
_memoryManager = memory;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IExecutionContext CreateExecutionContext(ExceptionCallbacks exceptionCallbacks)
|
||||
{
|
||||
return new NceExecutionContext(exceptionCallbacks);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Execute(IExecutionContext context, ulong address)
|
||||
{
|
||||
NceExecutionContext nec = (NceExecutionContext)context;
|
||||
NceNativeInterface.RegisterThread(nec, _tickSource);
|
||||
int tableIndex = NceThreadTable.Register(_getTpidrEl0(), nec.NativeContextPtr);
|
||||
|
||||
nec.SetStartAddress(address);
|
||||
_threadStart(nec.NativeContextPtr);
|
||||
|
||||
NceThreadTable.Unregister(tableIndex);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void InvalidateCacheRegion(ulong address, ulong size)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void PatchCodeForNce(ulong textAddress, ulong textSize, ulong patchRegionAddress, ulong patchRegionSize)
|
||||
{
|
||||
NcePatcher.Patch(_memoryManager, textAddress, textSize, patchRegionAddress, patchRegionSize);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled)
|
||||
{
|
||||
return new DiskCacheLoadState();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void PrepareCodeRange(ulong address, ulong size)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
19
src/Ryujinx.Cpu/Nce/NceEngine.cs
Normal file
19
src/Ryujinx.Cpu/Nce/NceEngine.cs
Normal file
@ -0,0 +1,19 @@
|
||||
using ARMeilleure.Memory;
|
||||
|
||||
namespace Ryujinx.Cpu.Nce
|
||||
{
|
||||
public class NceEngine : ICpuEngine
|
||||
{
|
||||
public ITickSource TickSource{ get; }
|
||||
|
||||
public NceEngine(ITickSource tickSource)
|
||||
{
|
||||
TickSource = tickSource;
|
||||
}
|
||||
|
||||
public ICpuContext CreateCpuContext(ICpuMemoryManager memoryManager, bool for64Bit)
|
||||
{
|
||||
return new NceCpuContext(TickSource, memoryManager, for64Bit);
|
||||
}
|
||||
}
|
||||
}
|
131
src/Ryujinx.Cpu/Nce/NceExecutionContext.cs
Normal file
131
src/Ryujinx.Cpu/Nce/NceExecutionContext.cs
Normal file
@ -0,0 +1,131 @@
|
||||
using ARMeilleure.State;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Cpu.Nce
|
||||
{
|
||||
class NceExecutionContext : IExecutionContext
|
||||
{
|
||||
private readonly NceNativeContext _context;
|
||||
private readonly ExceptionCallbacks _exceptionCallbacks;
|
||||
|
||||
internal IntPtr NativeContextPtr => _context.BasePtr;
|
||||
|
||||
public ulong Pc => 0UL;
|
||||
|
||||
public long TpidrEl0
|
||||
{
|
||||
get => (long)_context.GetStorage().TpidrEl0;
|
||||
set => _context.GetStorage().TpidrEl0 = (ulong)value;
|
||||
}
|
||||
|
||||
public long TpidrroEl0
|
||||
{
|
||||
get => (long)_context.GetStorage().TpidrroEl0;
|
||||
set => _context.GetStorage().TpidrroEl0 = (ulong)value;
|
||||
}
|
||||
|
||||
public uint Pstate
|
||||
{
|
||||
get => _context.GetStorage().Pstate;
|
||||
set => _context.GetStorage().Pstate = value;
|
||||
}
|
||||
|
||||
public uint Fpcr
|
||||
{
|
||||
get => _context.GetStorage().Fpcr;
|
||||
set => _context.GetStorage().Fpcr = value;
|
||||
}
|
||||
|
||||
public uint Fpsr
|
||||
{
|
||||
get => _context.GetStorage().Fpsr;
|
||||
set => _context.GetStorage().Fpsr = value;
|
||||
}
|
||||
|
||||
public bool IsAarch32
|
||||
{
|
||||
get => false;
|
||||
set
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool Running { get; private set; }
|
||||
|
||||
private delegate bool SupervisorCallHandler(int imm);
|
||||
private SupervisorCallHandler _svcHandler;
|
||||
|
||||
public NceExecutionContext(ExceptionCallbacks exceptionCallbacks)
|
||||
{
|
||||
_svcHandler = OnSupervisorCall;
|
||||
IntPtr svcHandlerPtr = Marshal.GetFunctionPointerForDelegate(_svcHandler);
|
||||
|
||||
_context = new NceNativeContext();
|
||||
|
||||
ref var storage = ref _context.GetStorage();
|
||||
storage.SvcCallHandler = svcHandlerPtr;
|
||||
storage.InManaged = 1u;
|
||||
storage.CtrEl0 = 0x8444c004; // TODO: Get value from host CPU instead of using guest one?
|
||||
|
||||
Running = true;
|
||||
_exceptionCallbacks = exceptionCallbacks;
|
||||
}
|
||||
|
||||
public ulong GetX(int index) => _context.GetStorage().X[index];
|
||||
public void SetX(int index, ulong value) => _context.GetStorage().X[index] = value;
|
||||
|
||||
public V128 GetV(int index) => _context.GetStorage().V[index];
|
||||
public void SetV(int index, V128 value) => _context.GetStorage().V[index] = value;
|
||||
|
||||
// TODO
|
||||
public bool GetPstateFlag(PState flag) => false;
|
||||
public void SetPstateFlag(PState flag, bool value) { }
|
||||
|
||||
// TODO
|
||||
public bool GetFPstateFlag(FPState flag) => false;
|
||||
public void SetFPstateFlag(FPState flag, bool value) { }
|
||||
|
||||
public void SetStartAddress(ulong address)
|
||||
{
|
||||
ref var storage = ref _context.GetStorage();
|
||||
storage.X[30] = address;
|
||||
storage.HostThreadHandle = NceThreadPal.GetCurrentThreadHandle();
|
||||
}
|
||||
|
||||
public bool OnSupervisorCall(int imm)
|
||||
{
|
||||
_exceptionCallbacks.SupervisorCallback?.Invoke(this, 0UL, imm);
|
||||
return Running;
|
||||
}
|
||||
|
||||
public bool OnInterrupt()
|
||||
{
|
||||
_exceptionCallbacks.InterruptCallback?.Invoke(this);
|
||||
return Running;
|
||||
}
|
||||
|
||||
public void RequestInterrupt()
|
||||
{
|
||||
IntPtr threadHandle = _context.GetStorage().HostThreadHandle;
|
||||
if (threadHandle != IntPtr.Zero)
|
||||
{
|
||||
NceThreadPal.SuspendThread(threadHandle);
|
||||
}
|
||||
}
|
||||
|
||||
public void StopRunning()
|
||||
{
|
||||
Running = false;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_context.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
13
src/Ryujinx.Cpu/Nce/NceMemoryAllocator.cs
Normal file
13
src/Ryujinx.Cpu/Nce/NceMemoryAllocator.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using ARMeilleure.Memory;
|
||||
using Ryujinx.Memory;
|
||||
|
||||
namespace Ryujinx.Cpu.Nce
|
||||
{
|
||||
class NceMemoryAllocator : IJitMemoryAllocator
|
||||
{
|
||||
public IJitMemoryBlock Allocate(ulong size) => new NceMemoryBlock(size, MemoryAllocationFlags.None);
|
||||
public IJitMemoryBlock Reserve(ulong size) => new NceMemoryBlock(size, MemoryAllocationFlags.Reserve);
|
||||
|
||||
public ulong GetPageSize() => MemoryBlock.GetPageSize();
|
||||
}
|
||||
}
|
24
src/Ryujinx.Cpu/Nce/NceMemoryBlock.cs
Normal file
24
src/Ryujinx.Cpu/Nce/NceMemoryBlock.cs
Normal file
@ -0,0 +1,24 @@
|
||||
using ARMeilleure.Memory;
|
||||
using Ryujinx.Memory;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Cpu.Nce
|
||||
{
|
||||
class NceMemoryBlock : IJitMemoryBlock
|
||||
{
|
||||
private readonly MemoryBlock _impl;
|
||||
|
||||
public IntPtr Pointer => _impl.Pointer;
|
||||
|
||||
public NceMemoryBlock(ulong size, MemoryAllocationFlags flags)
|
||||
{
|
||||
_impl = new MemoryBlock(size, flags);
|
||||
}
|
||||
|
||||
public void Commit(ulong offset, ulong size) => _impl.Commit(offset, size);
|
||||
public void MapAsRx(ulong offset, ulong size) => _impl.Reprotect(offset, size, MemoryPermission.ReadAndExecute);
|
||||
public void MapAsRwx(ulong offset, ulong size) => _impl.Reprotect(offset, size, MemoryPermission.ReadWriteExecute);
|
||||
|
||||
public void Dispose() => _impl.Dispose();
|
||||
}
|
||||
}
|
43
src/Ryujinx.Cpu/Nce/NceNativeContext.cs
Normal file
43
src/Ryujinx.Cpu/Nce/NceNativeContext.cs
Normal file
@ -0,0 +1,43 @@
|
||||
using ARMeilleure.State;
|
||||
using Ryujinx.Common.Memory;
|
||||
using Ryujinx.Memory;
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ryujinx.Cpu.Nce
|
||||
{
|
||||
class NceNativeContext : IDisposable
|
||||
{
|
||||
public struct NativeCtxStorage
|
||||
{
|
||||
public Array32<ulong> X;
|
||||
public Array32<V128> V;
|
||||
public ulong TpidrEl0; // 0x300
|
||||
public ulong TpidrroEl0; // 0x308
|
||||
public uint Pstate; // 0x310
|
||||
public uint Fpcr; // 0x314
|
||||
public uint Fpsr; // 0x318
|
||||
public uint InManaged; // 0x31C
|
||||
public ulong HostSp; // 0x320
|
||||
public IntPtr HostThreadHandle; // 0x328
|
||||
public ulong HostX30; // 0x330
|
||||
public ulong CtrEl0; // 0x338
|
||||
public ulong Reserved340; // 0x340
|
||||
public ulong Reserved348; // 0x348
|
||||
public IntPtr SvcCallHandler; // 0x350
|
||||
}
|
||||
|
||||
private readonly MemoryBlock _block;
|
||||
|
||||
public IntPtr BasePtr => _block.Pointer;
|
||||
|
||||
public NceNativeContext()
|
||||
{
|
||||
_block = new MemoryBlock((ulong)Unsafe.SizeOf<NativeCtxStorage>());
|
||||
}
|
||||
|
||||
public unsafe ref NativeCtxStorage GetStorage() => ref Unsafe.AsRef<NativeCtxStorage>((void*)_block.Pointer);
|
||||
|
||||
public void Dispose() => _block.Dispose();
|
||||
}
|
||||
}
|
55
src/Ryujinx.Cpu/Nce/NceNativeInterface.cs
Normal file
55
src/Ryujinx.Cpu/Nce/NceNativeInterface.cs
Normal file
@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Cpu.Nce
|
||||
{
|
||||
static class NceNativeInterface
|
||||
{
|
||||
private delegate ulong GetTickCounterDelegate();
|
||||
private delegate bool SuspendThreadHandlerDelegate();
|
||||
private static GetTickCounterDelegate _getTickCounter;
|
||||
private static SuspendThreadHandlerDelegate _suspendThreadHandler;
|
||||
private static IntPtr _getTickCounterPtr;
|
||||
private static IntPtr _suspendThreadHandlerPtr;
|
||||
|
||||
[ThreadStatic]
|
||||
private static NceExecutionContext _context;
|
||||
|
||||
[ThreadStatic]
|
||||
private static ITickSource _tickSource;
|
||||
|
||||
static NceNativeInterface()
|
||||
{
|
||||
_getTickCounter = GetTickCounter;
|
||||
_suspendThreadHandler = SuspendThreadHandler;
|
||||
_getTickCounterPtr = Marshal.GetFunctionPointerForDelegate(_getTickCounter);
|
||||
_suspendThreadHandlerPtr = Marshal.GetFunctionPointerForDelegate(_suspendThreadHandler);
|
||||
}
|
||||
|
||||
public static void RegisterThread(NceExecutionContext context, ITickSource tickSource)
|
||||
{
|
||||
_context = context;
|
||||
_tickSource = tickSource;
|
||||
}
|
||||
|
||||
public static ulong GetTickCounter()
|
||||
{
|
||||
return _tickSource.Counter;
|
||||
}
|
||||
|
||||
public static bool SuspendThreadHandler()
|
||||
{
|
||||
return _context.OnInterrupt();
|
||||
}
|
||||
|
||||
public static IntPtr GetTickCounterAccessFunctionPointer()
|
||||
{
|
||||
return _getTickCounterPtr;
|
||||
}
|
||||
|
||||
public static IntPtr GetSuspendThreadHandlerFunctionPointer()
|
||||
{
|
||||
return _suspendThreadHandlerPtr;
|
||||
}
|
||||
}
|
||||
}
|
293
src/Ryujinx.Cpu/Nce/NcePatcher.cs
Normal file
293
src/Ryujinx.Cpu/Nce/NcePatcher.cs
Normal file
@ -0,0 +1,293 @@
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Memory;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Cpu.Nce
|
||||
{
|
||||
static class NcePatcher
|
||||
{
|
||||
private struct Context
|
||||
{
|
||||
public readonly ICpuMemoryManager MemoryManager;
|
||||
public ulong PatchRegionAddress;
|
||||
public ulong PatchRegionSize;
|
||||
|
||||
public Context(ICpuMemoryManager memoryManager, ulong patchRegionAddress, ulong patchRegionSize)
|
||||
{
|
||||
MemoryManager = memoryManager;
|
||||
PatchRegionAddress = patchRegionAddress;
|
||||
PatchRegionSize = patchRegionSize;
|
||||
}
|
||||
|
||||
public ulong GetPatchWriteAddress(int length)
|
||||
{
|
||||
ulong byteLength = (ulong)length * 4;
|
||||
|
||||
if (PatchRegionSize < byteLength)
|
||||
{
|
||||
throw new Exception("No enough space for patch.");
|
||||
}
|
||||
|
||||
ulong address = PatchRegionAddress;
|
||||
PatchRegionAddress += byteLength;
|
||||
PatchRegionSize -= byteLength;
|
||||
|
||||
return address;
|
||||
}
|
||||
}
|
||||
|
||||
public static void Patch(
|
||||
ICpuMemoryManager memoryManager,
|
||||
ulong textAddress,
|
||||
ulong textSize,
|
||||
ulong patchRegionAddress,
|
||||
ulong patchRegionSize)
|
||||
{
|
||||
Context context = new Context(memoryManager, patchRegionAddress, patchRegionSize);
|
||||
|
||||
ulong address = textAddress;
|
||||
while (address < textAddress + textSize)
|
||||
{
|
||||
uint inst = memoryManager.Read<uint>(address);
|
||||
|
||||
if ((inst & ~(0xffffu << 5)) == 0xd4000001u) // svc #0
|
||||
{
|
||||
uint svcId = (ushort)(inst >> 5);
|
||||
PatchInstruction(memoryManager, address, WriteSvcPatch(ref context, address, svcId));
|
||||
Logger.Debug?.Print(LogClass.Cpu, $"Patched SVC #{svcId} at 0x{address:X}.");
|
||||
}
|
||||
else if ((inst & ~0x1f) == 0xd53bd060) // mrs x0, tpidrro_el0
|
||||
{
|
||||
uint rd = inst & 0x1f;
|
||||
PatchInstruction(memoryManager, address, WriteMrsTpidrroEl0Patch(ref context, address, rd));
|
||||
Logger.Debug?.Print(LogClass.Cpu, $"Patched MRS x{rd}, tpidrro_el0 at 0x{address:X}.");
|
||||
}
|
||||
else if ((inst & ~0x1f) == 0xd53bd040) // mrs x0, tpidr_el0
|
||||
{
|
||||
uint rd = inst & 0x1f;
|
||||
PatchInstruction(memoryManager, address, WriteMrsTpidrEl0Patch(ref context, address, rd));
|
||||
Logger.Debug?.Print(LogClass.Cpu, $"Patched MRS x{rd}, tpidr_el0 at 0x{address:X}.");
|
||||
}
|
||||
else if ((inst & ~0x1f) == 0xd53b0020 && OperatingSystem.IsMacOS()) // mrs x0, ctr_el0
|
||||
{
|
||||
uint rd = inst & 0x1f;
|
||||
PatchInstruction(memoryManager, address, WriteMrsCtrEl0Patch(ref context, address, rd));
|
||||
Logger.Debug?.Print(LogClass.Cpu, $"Patched MRS x{rd}, ctr_el0 at 0x{address:X}.");
|
||||
}
|
||||
else if ((inst & ~0x1f) == 0xd51bd040) // msr tpidr_el0, x0
|
||||
{
|
||||
uint rd = inst & 0x1f;
|
||||
PatchInstruction(memoryManager, address, WriteMsrTpidrEl0Patch(ref context, address, rd));
|
||||
Logger.Debug?.Print(LogClass.Cpu, $"Patched MSR tpidr_el0, x{rd} at 0x{address:X}.");
|
||||
}
|
||||
else if ((inst & ~0x1f) == 0xd53be020) // mrs x0, cntpct_el0
|
||||
{
|
||||
uint rd = inst & 0x1f;
|
||||
PatchInstruction(memoryManager, address, WriteMrsCntpctEl0Patch(ref context, address, rd));
|
||||
Logger.Debug?.Print(LogClass.Cpu, $"Patched MRS x{rd}, cntpct_el0 at 0x{address:X}.");
|
||||
}
|
||||
|
||||
address += 4;
|
||||
}
|
||||
|
||||
ulong patchRegionConsumed = BitUtils.AlignUp(context.PatchRegionAddress - patchRegionAddress, 0x1000UL);
|
||||
if (patchRegionConsumed != 0)
|
||||
{
|
||||
memoryManager.Reprotect(patchRegionAddress, patchRegionConsumed, MemoryPermission.ReadAndExecute);
|
||||
}
|
||||
}
|
||||
|
||||
private static void PatchInstruction(ICpuMemoryManager memoryManager, ulong instructionAddress, ulong targetAddress)
|
||||
{
|
||||
memoryManager.Write(instructionAddress, 0x14000000u | GetImm26(instructionAddress, targetAddress));
|
||||
}
|
||||
|
||||
private static ulong WriteSvcPatch(ref Context context, ulong svcAddress, uint svcId)
|
||||
{
|
||||
uint[] code = GetCopy(NceAsmTable.SvcPatchCode);
|
||||
|
||||
int movIndex = Array.IndexOf(code, 0xd2800013u);
|
||||
|
||||
WritePointer(code, movIndex, (ulong)NceThreadTable.EntriesPointer);
|
||||
|
||||
int mov2Index = Array.IndexOf(code, 0x52800000u, movIndex + 1);
|
||||
|
||||
ulong targetAddress = context.GetPatchWriteAddress(code.Length);
|
||||
|
||||
code[mov2Index] |= svcId << 5;
|
||||
code[code.Length - 1] |= GetImm26(targetAddress + (ulong)(code.Length - 1) * 4, svcAddress + 4);
|
||||
|
||||
WriteCode(context.MemoryManager, targetAddress, code);
|
||||
|
||||
return targetAddress;
|
||||
}
|
||||
|
||||
private static ulong WriteMrsTpidrroEl0Patch(ref Context context, ulong mrsAddress, uint rd)
|
||||
{
|
||||
uint[] code = GetCopy(NceAsmTable.MrsTpidrroEl0PatchCode);
|
||||
|
||||
int movIndex = Array.IndexOf(code, 0xd2800013u);
|
||||
|
||||
WritePointer(code, movIndex, (ulong)NceThreadTable.EntriesPointer);
|
||||
|
||||
ulong targetAddress = context.GetPatchWriteAddress(code.Length);
|
||||
|
||||
code[code.Length - 2] |= rd;
|
||||
code[code.Length - 1] |= GetImm26(targetAddress + (ulong)(code.Length - 1) * 4, mrsAddress + 4);
|
||||
|
||||
WriteCode(context.MemoryManager, targetAddress, code);
|
||||
|
||||
return targetAddress;
|
||||
}
|
||||
|
||||
private static ulong WriteMrsTpidrEl0Patch(ref Context context, ulong mrsAddress, uint rd)
|
||||
{
|
||||
uint[] code = GetCopy(NceAsmTable.MrsTpidrEl0PatchCode);
|
||||
|
||||
int movIndex = Array.IndexOf(code, 0xd2800013u);
|
||||
|
||||
WritePointer(code, movIndex, (ulong)NceThreadTable.EntriesPointer);
|
||||
|
||||
ulong targetAddress = context.GetPatchWriteAddress(code.Length);
|
||||
|
||||
code[code.Length - 2] |= rd;
|
||||
code[code.Length - 1] |= GetImm26(targetAddress + (ulong)(code.Length - 1) * 4, mrsAddress + 4);
|
||||
|
||||
WriteCode(context.MemoryManager, targetAddress, code);
|
||||
|
||||
return targetAddress;
|
||||
}
|
||||
|
||||
private static ulong WriteMrsCtrEl0Patch(ref Context context, ulong mrsAddress, uint rd)
|
||||
{
|
||||
uint[] code = GetCopy(NceAsmTable.MrsCtrEl0PatchCode);
|
||||
|
||||
int movIndex = Array.IndexOf(code, 0xd2800013u);
|
||||
|
||||
WritePointer(code, movIndex, (ulong)NceThreadTable.EntriesPointer);
|
||||
|
||||
ulong targetAddress = context.GetPatchWriteAddress(code.Length);
|
||||
|
||||
code[code.Length - 2] |= rd;
|
||||
code[code.Length - 1] |= GetImm26(targetAddress + (ulong)(code.Length - 1) * 4, mrsAddress + 4);
|
||||
|
||||
WriteCode(context.MemoryManager, targetAddress, code);
|
||||
|
||||
return targetAddress;
|
||||
}
|
||||
|
||||
private static ulong WriteMsrTpidrEl0Patch(ref Context context, ulong msrAddress, uint rd)
|
||||
{
|
||||
uint r2 = rd == 0 ? 1u : 0u;
|
||||
|
||||
uint[] code = GetCopy(NceAsmTable.MsrTpidrEl0PatchCode);
|
||||
|
||||
code[0] |= rd << 10;
|
||||
|
||||
int movIndex = Array.IndexOf(code, 0xd2800013u);
|
||||
|
||||
WritePointer(code, movIndex, (ulong)NceThreadTable.EntriesPointer);
|
||||
|
||||
ulong targetAddress = context.GetPatchWriteAddress(code.Length);
|
||||
|
||||
code[code.Length - 1] |= GetImm26(targetAddress + (ulong)(code.Length - 1) * 4, msrAddress + 4);
|
||||
|
||||
WriteCode(context.MemoryManager, targetAddress, code);
|
||||
|
||||
return targetAddress;
|
||||
}
|
||||
|
||||
private static ulong WriteMrsCntpctEl0Patch(ref Context context, ulong mrsAddress, uint rd)
|
||||
{
|
||||
uint[] code = GetCopy(NceAsmTable.MrsCntpctEl0PatchCode);
|
||||
|
||||
int movIndex = Array.IndexOf(code, 0xd2800013u);
|
||||
|
||||
WritePointer(code, movIndex, (ulong)NceThreadTable.EntriesPointer);
|
||||
|
||||
int mov2Index = Array.IndexOf(code, 0xD2800000u, movIndex + 1);
|
||||
|
||||
WriteTickCounterAccessFunctionPointer(code, mov2Index);
|
||||
|
||||
ulong targetAddress = context.GetPatchWriteAddress(code.Length);
|
||||
|
||||
code[code.Length - 2] |= rd;
|
||||
code[code.Length - 1] |= GetImm26(targetAddress + (ulong)(code.Length - 1) * 4, mrsAddress + 4);
|
||||
|
||||
WriteCode(context.MemoryManager, targetAddress, code);
|
||||
|
||||
return targetAddress;
|
||||
}
|
||||
|
||||
public static uint[] GenerateExceptionHandlerEntry(IntPtr oldSignalHandlerSegfaultPtr, IntPtr signalHandlerPtr)
|
||||
{
|
||||
uint[] code = GetCopy(NceAsmTable.ExceptionHandlerEntryCode);
|
||||
|
||||
int movIndex = Array.IndexOf(code, 0xd2800018u);
|
||||
|
||||
WritePointer(code, movIndex, (ulong)NceThreadTable.EntriesPointer);
|
||||
|
||||
int mov2Index = Array.IndexOf(code, 0xd2800008u, movIndex + 1);
|
||||
|
||||
WritePointer(code, mov2Index, (ulong)signalHandlerPtr);
|
||||
|
||||
int mov3Index = Array.IndexOf(code, 0xd2800000u, mov2Index + 1);
|
||||
|
||||
WritePointer(code, mov3Index, (ulong)NceNativeInterface.GetSuspendThreadHandlerFunctionPointer());
|
||||
|
||||
int mov4Index = Array.IndexOf(code, 0xd2800003u, mov3Index + 1);
|
||||
|
||||
WritePointer(code, mov4Index, (ulong)oldSignalHandlerSegfaultPtr);
|
||||
|
||||
int cmpIndex = Array.IndexOf(code, 0x7100027fu);
|
||||
|
||||
code[cmpIndex] |= (uint)NceThreadPal.UnixSuspendSignal << 10;
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
private static void WriteTickCounterAccessFunctionPointer(uint[] code, int movIndex)
|
||||
{
|
||||
WritePointer(code, movIndex, (ulong)NceNativeInterface.GetTickCounterAccessFunctionPointer());
|
||||
}
|
||||
|
||||
private static void WritePointer(uint[] code, int movIndex, ulong ptr)
|
||||
{
|
||||
code[movIndex] |= (uint)(ushort)ptr << 5;
|
||||
code[movIndex + 1] |= (uint)(ushort)(ptr >> 16) << 5;
|
||||
code[movIndex + 2] |= (uint)(ushort)(ptr >> 32) << 5;
|
||||
code[movIndex + 3] |= (uint)(ushort)(ptr >> 48) << 5;
|
||||
}
|
||||
|
||||
private static uint GetImm26(ulong sourceAddress, ulong targetAddress)
|
||||
{
|
||||
long offset = (long)(targetAddress - sourceAddress);
|
||||
long offsetTrunc = (offset >> 2) & 0x3FFFFFF;
|
||||
|
||||
if ((offsetTrunc << 38) >> 36 != offset)
|
||||
{
|
||||
throw new Exception($"Offset out of range: 0x{sourceAddress:X} -> 0x{targetAddress:X} (0x{offset:X})");
|
||||
}
|
||||
|
||||
return (uint)offsetTrunc;
|
||||
}
|
||||
|
||||
private static uint[] GetCopy(uint[] code)
|
||||
{
|
||||
uint[] codeCopy = new uint[code.Length];
|
||||
code.CopyTo(codeCopy, 0);
|
||||
|
||||
return codeCopy;
|
||||
}
|
||||
|
||||
private static void WriteCode(ICpuMemoryManager memoryManager, ulong address, uint[] code)
|
||||
{
|
||||
for (int i = 0; i < code.Length; i++)
|
||||
{
|
||||
memoryManager.Write(address + (ulong)i * sizeof(uint), code[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
40
src/Ryujinx.Cpu/Nce/NceThreadPal.cs
Normal file
40
src/Ryujinx.Cpu/Nce/NceThreadPal.cs
Normal file
@ -0,0 +1,40 @@
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Cpu.Nce
|
||||
{
|
||||
static class NceThreadPal
|
||||
{
|
||||
private const int SigUsr2Linux = 12;
|
||||
private const int SigUsr2MacOS = 31;
|
||||
|
||||
public static int UnixSuspendSignal => OperatingSystem.IsMacOS() ? SigUsr2MacOS : SigUsr2Linux;
|
||||
|
||||
public static IntPtr GetCurrentThreadHandle()
|
||||
{
|
||||
if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || OperatingSystem.IsAndroid())
|
||||
{
|
||||
return NceThreadPalUnix.GetCurrentThreadHandle();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
public static void SuspendThread(IntPtr handle)
|
||||
{
|
||||
if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
|
||||
{
|
||||
NceThreadPalUnix.SuspendThread(handle);
|
||||
}
|
||||
else if (OperatingSystem.IsAndroid())
|
||||
{
|
||||
NceThreadPalAndroid.SuspendThread(handle);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
20
src/Ryujinx.Cpu/Nce/NceThreadPalAndroid.cs
Normal file
20
src/Ryujinx.Cpu/Nce/NceThreadPalAndroid.cs
Normal file
@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Cpu.Nce
|
||||
{
|
||||
static class NceThreadPalAndroid
|
||||
{
|
||||
[DllImport("libc", SetLastError = true)]
|
||||
private static extern int pthread_kill(IntPtr thread, int sig);
|
||||
|
||||
public static void SuspendThread(IntPtr handle)
|
||||
{
|
||||
int result = pthread_kill(handle, NceThreadPal.UnixSuspendSignal);
|
||||
if (result != 0)
|
||||
{
|
||||
throw new Exception($"Thread kill returned error 0x{result:X}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
37
src/Ryujinx.Cpu/Nce/NceThreadPalUnix.cs
Normal file
37
src/Ryujinx.Cpu/Nce/NceThreadPalUnix.cs
Normal file
@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Cpu.Nce
|
||||
{
|
||||
static class NceThreadPalUnix
|
||||
{
|
||||
[DllImport("libc", SetLastError = true)]
|
||||
private static extern IntPtr pthread_self();
|
||||
|
||||
[DllImport("libc", SetLastError = true)]
|
||||
private static extern int pthread_threadid_np(IntPtr arg0, out ulong tid);
|
||||
|
||||
[DllImport("libpthread", SetLastError = true)]
|
||||
private static extern int pthread_kill(IntPtr thread, int sig);
|
||||
|
||||
public static IntPtr GetCurrentThreadHandle()
|
||||
{
|
||||
return pthread_self();
|
||||
}
|
||||
|
||||
public static ulong GetCurrentThreadId()
|
||||
{
|
||||
pthread_threadid_np(IntPtr.Zero, out ulong tid);
|
||||
return tid;
|
||||
}
|
||||
|
||||
public static void SuspendThread(IntPtr handle)
|
||||
{
|
||||
int result = pthread_kill(handle, NceThreadPal.UnixSuspendSignal);
|
||||
if (result != 0)
|
||||
{
|
||||
throw new Exception($"Thread kill returned error 0x{result:X}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
97
src/Ryujinx.Cpu/Nce/NceThreadTable.cs
Normal file
97
src/Ryujinx.Cpu/Nce/NceThreadTable.cs
Normal file
@ -0,0 +1,97 @@
|
||||
using Ryujinx.Memory;
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ryujinx.Cpu.Nce
|
||||
{
|
||||
static class NceThreadTable
|
||||
{
|
||||
private const int MaxThreads = 4096;
|
||||
|
||||
private struct Entry
|
||||
{
|
||||
public IntPtr ThreadId;
|
||||
public IntPtr NativeContextPtr;
|
||||
|
||||
public Entry(IntPtr threadId, IntPtr nativeContextPtr)
|
||||
{
|
||||
ThreadId = threadId;
|
||||
NativeContextPtr = nativeContextPtr;
|
||||
}
|
||||
}
|
||||
|
||||
private static MemoryBlock _block;
|
||||
|
||||
public static IntPtr EntriesPointer => _block.Pointer + 8;
|
||||
|
||||
static NceThreadTable()
|
||||
{
|
||||
_block = new MemoryBlock((ulong)Unsafe.SizeOf<Entry>() * MaxThreads + 8UL);
|
||||
_block.Write(0UL, 0UL);
|
||||
}
|
||||
|
||||
public static int Register(IntPtr threadId, IntPtr nativeContextPtr)
|
||||
{
|
||||
Span<Entry> entries = GetStorage();
|
||||
|
||||
lock (_block)
|
||||
{
|
||||
ref ulong currentThreadCount = ref GetThreadsCount();
|
||||
|
||||
for (int i = 0; i < MaxThreads; i++)
|
||||
{
|
||||
if (entries[i].ThreadId == IntPtr.Zero)
|
||||
{
|
||||
entries[i] = new Entry(threadId, nativeContextPtr);
|
||||
|
||||
if (currentThreadCount < (ulong)i + 1)
|
||||
{
|
||||
currentThreadCount = (ulong)i + 1;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new Exception($"Number of active threads exceeds limit of {MaxThreads}.");
|
||||
}
|
||||
|
||||
public static void Unregister(int tableIndex)
|
||||
{
|
||||
Span<Entry> entries = GetStorage();
|
||||
|
||||
lock (_block)
|
||||
{
|
||||
if (entries[tableIndex].ThreadId != IntPtr.Zero)
|
||||
{
|
||||
entries[tableIndex] = default;
|
||||
|
||||
ulong currentThreadCount = GetThreadsCount();
|
||||
|
||||
for (int i = (int)currentThreadCount - 1; i >= 0; i--)
|
||||
{
|
||||
if (entries[i].ThreadId != IntPtr.Zero)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
currentThreadCount = (ulong)i;
|
||||
}
|
||||
|
||||
GetThreadsCount() = currentThreadCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static ref ulong GetThreadsCount()
|
||||
{
|
||||
return ref _block.GetRef<ulong>(0UL);
|
||||
}
|
||||
|
||||
private static unsafe Span<Entry> GetStorage()
|
||||
{
|
||||
return new Span<Entry>((void*)_block.GetPointer(8UL, (ulong)Unsafe.SizeOf<Entry>() * MaxThreads), MaxThreads);
|
||||
}
|
||||
}
|
||||
}
|
378
src/Ryujinx.Cpu/Nce/nce.S
Normal file
378
src/Ryujinx.Cpu/Nce/nce.S
Normal file
@ -0,0 +1,378 @@
|
||||
.text
|
||||
|
||||
.macro longmov0 reg
|
||||
mov \reg, #0
|
||||
movk \reg, #0, lsl #16
|
||||
movk \reg, #0, lsl #32
|
||||
movk \reg, #0, lsl #48
|
||||
.endm
|
||||
|
||||
// r1 = EntriesPointer
|
||||
// r2 = current_thread_id_local
|
||||
// r3 = expected_thread_id
|
||||
// r4 = ThreadsCount_local
|
||||
.macro loadctxptr_reg r1, r2, r3
|
||||
longmov0 \r1
|
||||
mrs \r2, tpidr_el0
|
||||
1:
|
||||
ldr \r3, [\r1], #16
|
||||
cmp \r2, \r3
|
||||
beq 2f
|
||||
b 1b
|
||||
2:
|
||||
ldr \r1, [\r1, #-8]
|
||||
.endm
|
||||
|
||||
.macro loadctxptr
|
||||
loadctxptr_reg x19, x20, x21
|
||||
.endm
|
||||
|
||||
.global GetTpidrEl0Code
|
||||
GetTpidrEl0Code:
|
||||
mrs x0, tpidr_el0
|
||||
ret
|
||||
|
||||
.global ThreadStartCode
|
||||
ThreadStartCode:
|
||||
stp x19, x20, [sp, #-0x120]!
|
||||
stp x21, x22, [sp, #0x10]
|
||||
stp x23, x24, [sp, #0x20]
|
||||
stp x25, x26, [sp, #0x30]
|
||||
stp x27, x28, [sp, #0x40]
|
||||
stp x29, x30, [sp, #0x50]
|
||||
stp d8, d9, [sp, #0x60]
|
||||
stp d10, d11, [sp, #0x70]
|
||||
stp d12, d13, [sp, #0x80]
|
||||
stp d14, d15, [sp, #0x90]
|
||||
stp d16, d17, [sp, #0xA0]
|
||||
stp d18, d19, [sp, #0xB0]
|
||||
stp d20, d21, [sp, #0xC0]
|
||||
stp d22, d23, [sp, #0xD0]
|
||||
stp d24, d25, [sp, #0xE0]
|
||||
stp d26, d27, [sp, #0xF0]
|
||||
stp d28, d29, [sp, #0x100]
|
||||
stp d30, d31, [sp, #0x110]
|
||||
str wzr, [x0, #0x31C]
|
||||
mov x1, sp
|
||||
str x1, [x0, #0x320]
|
||||
ldp x2, x3, [x0, #0x10]
|
||||
ldp x4, x5, [x0, #0x20]
|
||||
ldp x6, x7, [x0, #0x30]
|
||||
ldp x8, x9, [x0, #0x40]
|
||||
ldp x10, x11, [x0, #0x50]
|
||||
ldp x12, x13, [x0, #0x60]
|
||||
ldp x14, x15, [x0, #0x70]
|
||||
ldp x16, x17, [x0, #0x80]
|
||||
ldp x18, x19, [x0, #0x90]
|
||||
ldp x20, x21, [x0, #0xA0]
|
||||
ldp x22, x23, [x0, #0xB0]
|
||||
ldp x24, x25, [x0, #0xC0]
|
||||
ldp x26, x27, [x0, #0xD0]
|
||||
ldp x28, x29, [x0, #0xE0]
|
||||
ldp q0, q1, [x0, #0x100]
|
||||
ldp q2, q3, [x0, #0x120]
|
||||
ldp q4, q5, [x0, #0x140]
|
||||
ldp q6, q7, [x0, #0x160]
|
||||
ldp q8, q9, [x0, #0x180]
|
||||
ldp q10, q11, [x0, #0x1A0]
|
||||
ldp q12, q13, [x0, #0x1C0]
|
||||
ldp q14, q15, [x0, #0x1E0]
|
||||
ldp q16, q17, [x0, #0x200]
|
||||
ldp q18, q19, [x0, #0x220]
|
||||
ldp q20, q21, [x0, #0x240]
|
||||
ldp q22, q23, [x0, #0x260]
|
||||
ldp q24, q25, [x0, #0x280]
|
||||
ldp q26, q27, [x0, #0x2A0]
|
||||
ldp q28, q29, [x0, #0x2C0]
|
||||
ldp q30, q31, [x0, #0x2E0]
|
||||
ldp x30, x1, [x0, #0xF0]
|
||||
mov sp, x1
|
||||
ldp x0, x1, [x0, #0x0]
|
||||
br x30
|
||||
|
||||
// Inputs
|
||||
// r1 = EntriesPointer
|
||||
// r2 = current_thread_id_local
|
||||
// r3 = expected_thread_id
|
||||
// r4 = EntriesPointerEnd
|
||||
|
||||
// Outputs
|
||||
// r1 = EntryPointer or 0x0 on not found
|
||||
.macro loadctxptr_safe_reg r1, r2, r3, r4
|
||||
longmov0 \r1
|
||||
ldr \r4, [\r1, #-8]
|
||||
add \r4, \r1, \r4, lsl #4
|
||||
mrs \r2, tpidr_el0
|
||||
1:
|
||||
cmp \r1, \r4
|
||||
beq 2f
|
||||
ldr \r3, [\r1], #16
|
||||
cmp \r2, \r3
|
||||
beq 3f
|
||||
b 1b
|
||||
2:
|
||||
mov \r1, 0x0
|
||||
b 4f
|
||||
3:
|
||||
ldr \r1, [\r1, #-8]
|
||||
4:
|
||||
.endm
|
||||
|
||||
.global ExceptionHandlerEntryCode
|
||||
ExceptionHandlerEntryCode:
|
||||
stp x19, x20, [sp, #-0x40]!
|
||||
stp x21, x22, [sp, #0x10]
|
||||
stp x23, x24, [sp, #0x20]
|
||||
str x25, [sp, #0x30]
|
||||
// signo
|
||||
mov x19, x0
|
||||
// siginfo_t *si
|
||||
mov x20, x1
|
||||
// void *thread_id
|
||||
mov x21, x2
|
||||
mov x22, sp
|
||||
mov x23, x30
|
||||
// x24 = EntriesPointer
|
||||
// x1 = si
|
||||
// x2 = thread_id
|
||||
loadctxptr_safe_reg x24, x1, x2, x25
|
||||
cbz x24, 4f
|
||||
ldr x0, [x24, 0x320]
|
||||
mov sp, x0
|
||||
cmp w19, #0
|
||||
beq 1f
|
||||
mov w0, #1
|
||||
str w0, [x24, 0x31C]
|
||||
mov x0, x19
|
||||
mov x1, x20
|
||||
mov x2, x21
|
||||
mov x8, #0
|
||||
movk x8, #0, lsl #16
|
||||
movk x8, #0, lsl #32
|
||||
movk x8, #0, lsl #48
|
||||
blr x8
|
||||
b 2f
|
||||
1:
|
||||
ldr w0, [x24, 0x31C]
|
||||
cbnz w0, 3f
|
||||
mov w0, #1
|
||||
str w0, [x24, 0x31C]
|
||||
mov x0, #0
|
||||
movk x0, #0, lsl #16
|
||||
movk x0, #0, lsl #32
|
||||
movk x0, #0, lsl #48
|
||||
blr x0
|
||||
2:
|
||||
str wzr, [x24, 0x31C]
|
||||
3:
|
||||
mov sp, x22
|
||||
mov x30, x23
|
||||
ldp x21, x22, [sp, #0x10]
|
||||
ldp x23, x24, [sp, #0x20]
|
||||
ldp x25, x26, [sp, #0x30]
|
||||
ldp x19, x20, [sp], #0x40
|
||||
ret
|
||||
4:
|
||||
// ThreadId is invalid, forward to other handler.
|
||||
mov x0, x19
|
||||
mov x1, x20
|
||||
mov x2, x21
|
||||
mov sp, x22
|
||||
ldp x21, x22, [sp, #0x10]
|
||||
ldp x23, x24, [sp, #0x20]
|
||||
ldr x25, [sp, #0x30]
|
||||
ldp x19, x20, [sp], #0x40
|
||||
longmov0 x3
|
||||
br x3
|
||||
|
||||
.global SvcPatchCode
|
||||
SvcPatchCode:
|
||||
|
||||
stp x19, x20, [sp, #-0x20]!
|
||||
str x21, [sp, #0x10]
|
||||
loadctxptr
|
||||
stp x0, x1, [x19, #0x0]
|
||||
stp x2, x3, [x19, #0x10]
|
||||
stp x4, x5, [x19, #0x20]
|
||||
stp x6, x7, [x19, #0x30]
|
||||
stp x8, x9, [x19, #0x40]
|
||||
stp x10, x11, [x19, #0x50]
|
||||
stp x12, x13, [x19, #0x60]
|
||||
stp x14, x15, [x19, #0x70]
|
||||
stp x16, x17, [x19, #0x80]
|
||||
ldr x21, [sp, #0x10]
|
||||
ldp x0, x20, [sp], #0x20
|
||||
stp x18, x0, [x19, #0x90]
|
||||
stp x20, x21, [x19, #0xA0]
|
||||
stp x22, x23, [x19, #0xB0]
|
||||
stp x24, x25, [x19, #0xC0]
|
||||
stp x26, x27, [x19, #0xD0]
|
||||
stp x28, x29, [x19, #0xE0]
|
||||
mov x0, sp
|
||||
stp x30, x0, [x19, #0xF0]
|
||||
stp q0, q1, [x19, #0x100]
|
||||
stp q2, q3, [x19, #0x120]
|
||||
stp q4, q5, [x19, #0x140]
|
||||
stp q6, q7, [x19, #0x160]
|
||||
stp q8, q9, [x19, #0x180]
|
||||
stp q10, q11, [x19, #0x1A0]
|
||||
stp q12, q13, [x19, #0x1C0]
|
||||
stp q14, q15, [x19, #0x1E0]
|
||||
stp q16, q17, [x19, #0x200]
|
||||
stp q18, q19, [x19, #0x220]
|
||||
stp q20, q21, [x19, #0x240]
|
||||
stp q22, q23, [x19, #0x260]
|
||||
stp q24, q25, [x19, #0x280]
|
||||
stp q26, q27, [x19, #0x2A0]
|
||||
stp q28, q29, [x19, #0x2C0]
|
||||
stp q30, q31, [x19, #0x2E0]
|
||||
ldr x0, [x19, #0x320]
|
||||
mov sp, x0
|
||||
mov w0, #1
|
||||
str w0, [x19, #0x31C]
|
||||
mov w0, #0
|
||||
ldr x8, [x19, #0x350]
|
||||
blr x8
|
||||
cbnz w0, 1f
|
||||
ldp d30, d31, [sp, #0x110]
|
||||
ldp d28, d29, [sp, #0x100]
|
||||
ldp d26, d27, [sp, #0xF0]
|
||||
ldp d24, d25, [sp, #0xE0]
|
||||
ldp d22, d23, [sp, #0xD0]
|
||||
ldp d20, d21, [sp, #0xC0]
|
||||
ldp d18, d19, [sp, #0xB0]
|
||||
ldp d16, d17, [sp, #0xA0]
|
||||
ldp d14, d15, [sp, #0x90]
|
||||
ldp d12, d13, [sp, #0x80]
|
||||
ldp d10, d11, [sp, #0x70]
|
||||
ldp d8, d9, [sp, #0x60]
|
||||
ldp x29, x30, [sp, #0x50]
|
||||
ldp x27, x28, [sp, #0x40]
|
||||
ldp x25, x26, [sp, #0x30]
|
||||
ldp x23, x24, [sp, #0x20]
|
||||
ldp x21, x22, [sp, #0x10]
|
||||
ldp x19, x20, [sp], #0x120
|
||||
ret
|
||||
1:
|
||||
str wzr, [x19, #0x31C]
|
||||
ldp x30, x0, [x19, #0xF0]
|
||||
mov sp, x0
|
||||
ldp x0, x1, [x19, #0x0]
|
||||
ldp x2, x3, [x19, #0x10]
|
||||
ldp x4, x5, [x19, #0x20]
|
||||
ldp x6, x7, [x19, #0x30]
|
||||
ldp x8, x9, [x19, #0x40]
|
||||
ldp x10, x11, [x19, #0x50]
|
||||
ldp x12, x13, [x19, #0x60]
|
||||
ldp x14, x15, [x19, #0x70]
|
||||
ldp x16, x17, [x19, #0x80]
|
||||
ldr x18, [x19, #0x90]
|
||||
ldp x20, x21, [x19, #0xA0]
|
||||
ldp x22, x23, [x19, #0xB0]
|
||||
ldp x24, x25, [x19, #0xC0]
|
||||
ldp x26, x27, [x19, #0xD0]
|
||||
ldp x28, x29, [x19, #0xE0]
|
||||
ldp q0, q1, [x19, #0x100]
|
||||
ldp q2, q3, [x19, #0x120]
|
||||
ldp q4, q5, [x19, #0x140]
|
||||
ldp q6, q7, [x19, #0x160]
|
||||
ldp q8, q9, [x19, #0x180]
|
||||
ldp q10, q11, [x19, #0x1A0]
|
||||
ldp q12, q13, [x19, #0x1C0]
|
||||
ldp q14, q15, [x19, #0x1E0]
|
||||
ldp q16, q17, [x19, #0x200]
|
||||
ldp q18, q19, [x19, #0x220]
|
||||
ldp q20, q21, [x19, #0x240]
|
||||
ldp q22, q23, [x19, #0x260]
|
||||
ldp q24, q25, [x19, #0x280]
|
||||
ldp q26, q27, [x19, #0x2A0]
|
||||
ldp q28, q29, [x19, #0x2C0]
|
||||
ldp q30, q31, [x19, #0x2E0]
|
||||
ldr x19, [x19, #0x98]
|
||||
b #0
|
||||
|
||||
.global MrsTpidrroEl0PatchCode
|
||||
MrsTpidrroEl0PatchCode:
|
||||
stp xzr, x19, [sp, #-0x20]!
|
||||
stp x20, x21, [sp, #0x10]
|
||||
loadctxptr
|
||||
ldr x19, [x19, #0x308]
|
||||
str x19, [sp]
|
||||
ldp x20, x21, [sp, #0x10]
|
||||
ldr x19, [sp, #8]
|
||||
ldr x0, [sp], #0x20
|
||||
b #0
|
||||
|
||||
.global MrsTpidrEl0PatchCode
|
||||
MrsTpidrEl0PatchCode:
|
||||
stp xzr, x19, [sp, #-0x20]!
|
||||
stp x20, x21, [sp, #0x10]
|
||||
loadctxptr
|
||||
ldr x19, [x19, #0x300]
|
||||
str x19, [sp]
|
||||
ldp x20, x21, [sp, #0x10]
|
||||
ldr x19, [sp, #8]
|
||||
ldr x0, [sp], #0x20
|
||||
b #0
|
||||
|
||||
.global MrsCtrEl0PatchCode
|
||||
MrsCtrEl0PatchCode:
|
||||
stp xzr, x19, [sp, #-0x20]!
|
||||
stp x20, x21, [sp, #0x10]
|
||||
loadctxptr
|
||||
ldr x19, [x19, #0x338]
|
||||
str x19, [sp]
|
||||
ldp x20, x21, [sp, #0x10]
|
||||
ldr x19, [sp, #8]
|
||||
ldr x0, [sp], #0x20
|
||||
b #0
|
||||
|
||||
.global MsrTpidrEl0PatchCode
|
||||
MsrTpidrEl0PatchCode:
|
||||
stp x19, x0, [sp, #-0x20]!
|
||||
stp x20, x21, [sp, #0x10]
|
||||
loadctxptr
|
||||
ldr x20, [sp, #8]
|
||||
str x20, [x19, #0x300]
|
||||
ldp x20, x21, [sp, #0x10]
|
||||
ldr x19, [sp], #0x20
|
||||
b #0
|
||||
|
||||
.global MrsCntpctEl0PatchCode
|
||||
MrsCntpctEl0PatchCode:
|
||||
stp x0, x1, [sp, #-0xC0]!
|
||||
stp x2, x3, [sp, #0x10]
|
||||
stp x4, x5, [sp, #0x20]
|
||||
stp x6, x7, [sp, #0x30]
|
||||
stp x8, x9, [sp, #0x40]
|
||||
stp x10, x11, [sp, #0x50]
|
||||
stp x12, x13, [sp, #0x60]
|
||||
stp x14, x15, [sp, #0x70]
|
||||
stp x16, x17, [sp, #0x80]
|
||||
stp x18, x19, [sp, #0x90]
|
||||
stp x20, x21, [sp, #0xA0]
|
||||
str x30, [sp, #0xB8]
|
||||
loadctxptr
|
||||
mov w0, #1
|
||||
str w0, [x19, #0x31C]
|
||||
mov x0, #0
|
||||
movk x0, #0, lsl #16
|
||||
movk x0, #0, lsl #32
|
||||
movk x0, #0, lsl #48
|
||||
blr x0
|
||||
str wzr, [x19, #0x31C]
|
||||
str x0, [sp, #0xB0]
|
||||
ldr x30, [sp, #0xB8]
|
||||
ldp x20, x21, [sp, #0xA0]
|
||||
ldp x18, x19, [sp, #0x90]
|
||||
ldp x16, x17, [sp, #0x80]
|
||||
ldp x14, x15, [sp, #0x70]
|
||||
ldp x12, x13, [sp, #0x60]
|
||||
ldp x10, x11, [sp, #0x50]
|
||||
ldp x8, x9, [sp, #0x40]
|
||||
ldp x6, x7, [sp, #0x30]
|
||||
ldp x4, x5, [sp, #0x20]
|
||||
ldp x2, x3, [sp, #0x10]
|
||||
ldp x0, x1, [sp], #0xB0
|
||||
ldr x0, [sp], #0x10
|
||||
b #0
|
@ -70,7 +70,7 @@ namespace Ryujinx.Cpu.Signal
|
||||
config = new SignalHandlerConfig();
|
||||
}
|
||||
|
||||
public static void InitializeSignalHandler(ulong pageSize, Func<IntPtr, IntPtr, IntPtr> customSignalHandlerFactory = null)
|
||||
public static void InitializeSignalHandler(ulong pageSize, Func<IntPtr, IntPtr, IntPtr> customSignalHandlerFactory = null, int userSignal = -1)
|
||||
{
|
||||
if (_initialized)
|
||||
{
|
||||
@ -103,7 +103,7 @@ namespace Ryujinx.Cpu.Signal
|
||||
_signalHandlerPtr = customSignalHandlerFactory(UnixSignalHandlerRegistration.GetSegfaultExceptionHandler().sa_handler, _signalHandlerPtr);
|
||||
}
|
||||
|
||||
var old = UnixSignalHandlerRegistration.RegisterExceptionHandler(_signalHandlerPtr);
|
||||
var old = UnixSignalHandlerRegistration.RegisterExceptionHandler(_signalHandlerPtr, userSignal);
|
||||
|
||||
config.UnixOldSigaction = (nuint)(ulong)old.sa_handler;
|
||||
config.UnixOldSigaction3Arg = old.sa_flags & 4;
|
||||
|
@ -82,7 +82,7 @@ namespace Ryujinx.Cpu.Signal
|
||||
return old;
|
||||
}
|
||||
|
||||
public static SigAction RegisterExceptionHandler(IntPtr action)
|
||||
public static SigAction RegisterExceptionHandler(IntPtr action, int userSignal = -1)
|
||||
{
|
||||
int result;
|
||||
SigAction old;
|
||||
@ -150,6 +150,21 @@ namespace Ryujinx.Cpu.Signal
|
||||
}
|
||||
}
|
||||
|
||||
if (userSignal != -1)
|
||||
{
|
||||
result = sigaction(userSignal, ref sig, out SigAction oldu);
|
||||
|
||||
if (oldu.sa_handler != IntPtr.Zero)
|
||||
{
|
||||
throw new InvalidOperationException($"SIG{userSignal} is already in use.");
|
||||
}
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
throw new InvalidOperationException($"Could not register SIG{userSignal} sigaction. Error: {result}");
|
||||
}
|
||||
}
|
||||
|
||||
return old;
|
||||
}
|
||||
|
||||
|
@ -16,13 +16,15 @@ namespace Ryujinx.HLE.HOS
|
||||
ulong codeSize);
|
||||
}
|
||||
|
||||
class ArmProcessContext<T> : IArmProcessContext where T : class, IVirtualMemoryManagerTracked, IMemoryManager
|
||||
class ArmProcessContext<T> : IArmProcessContext where T : class, IVirtualMemoryManagerTracked, ICpuMemoryManager
|
||||
{
|
||||
private readonly ulong _pid;
|
||||
private readonly GpuContext _gpuContext;
|
||||
private readonly ICpuContext _cpuContext;
|
||||
private T _memoryManager;
|
||||
|
||||
public ulong ReservedSize { get; }
|
||||
|
||||
public IVirtualMemoryManager AddressSpace => _memoryManager;
|
||||
|
||||
public ulong AddressSpaceSize { get; }
|
||||
@ -33,7 +35,8 @@ namespace Ryujinx.HLE.HOS
|
||||
GpuContext gpuContext,
|
||||
T memoryManager,
|
||||
ulong addressSpaceSize,
|
||||
bool for64Bit)
|
||||
bool for64Bit,
|
||||
ulong reservedSize = 0UL)
|
||||
{
|
||||
if (memoryManager is IRefCounted rc)
|
||||
{
|
||||
@ -46,8 +49,8 @@ namespace Ryujinx.HLE.HOS
|
||||
_gpuContext = gpuContext;
|
||||
_cpuContext = cpuEngine.CreateCpuContext(memoryManager, for64Bit);
|
||||
_memoryManager = memoryManager;
|
||||
|
||||
AddressSpaceSize = addressSpaceSize;
|
||||
ReservedSize = reservedSize;
|
||||
}
|
||||
|
||||
public IExecutionContext CreateExecutionContext(ExceptionCallbacks exceptionCallbacks)
|
||||
@ -78,6 +81,11 @@ namespace Ryujinx.HLE.HOS
|
||||
_cpuContext.InvalidateCacheRegion(address, size);
|
||||
}
|
||||
|
||||
public void PatchCodeForNce(ulong textAddress, ulong textSize, ulong patchRegionAddress, ulong patchRegionSize)
|
||||
{
|
||||
_cpuContext.PatchCodeForNce(textAddress, textSize, patchRegionAddress, patchRegionSize);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_memoryManager is IRefCounted rc)
|
||||
|
@ -3,6 +3,7 @@ using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Cpu;
|
||||
using Ryujinx.Cpu.AppleHv;
|
||||
using Ryujinx.Cpu.Jit;
|
||||
using Ryujinx.Cpu.Nce;
|
||||
using Ryujinx.Graphics.Gpu;
|
||||
using Ryujinx.HLE.HOS.Kernel;
|
||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||
@ -45,12 +46,29 @@ namespace Ryujinx.HLE.HOS
|
||||
public IProcessContext Create(KernelContext context, ulong pid, ulong addressSpaceSize, InvalidAccessHandler invalidAccessHandler, bool for64Bit)
|
||||
{
|
||||
IArmProcessContext processContext;
|
||||
AddressSpace addressSpace = null;
|
||||
|
||||
if (OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64 && for64Bit && context.Device.Configuration.UseHypervisor)
|
||||
if (RuntimeInformation.ProcessArchitecture == Architecture.Arm64 && for64Bit && context.Device.Configuration.UseHypervisor)
|
||||
{
|
||||
var cpuEngine = new HvEngine(_tickSource);
|
||||
var memoryManager = new HvMemoryManager(context.Memory, addressSpaceSize, invalidAccessHandler);
|
||||
processContext = new ArmProcessContext<HvMemoryManager>(pid, cpuEngine, _gpu, memoryManager, addressSpaceSize, for64Bit);
|
||||
if (OperatingSystem.IsMacOS())
|
||||
{
|
||||
var cpuEngine = new HvEngine(_tickSource);
|
||||
var memoryManager = new HvMemoryManager(context.Memory, addressSpaceSize, invalidAccessHandler);
|
||||
processContext = new ArmProcessContext<HvMemoryManager>(pid, cpuEngine, _gpu, memoryManager, addressSpaceSize, for64Bit);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!AddressSpace.TryCreate(context.Memory, addressSpaceSize, MemoryBlock.GetPageSize() == MemoryManagerHostMapped.PageSize, out addressSpace))
|
||||
{
|
||||
throw new Exception("Address space creation failed");
|
||||
}
|
||||
|
||||
Logger.Info?.Print(LogClass.Cpu, $"NCE Base AS Address: 0x{addressSpace.Base.Pointer.ToInt64():X} Size: 0x{addressSpace.AddressSpaceSize:X}");
|
||||
|
||||
var cpuEngine = new NceEngine(_tickSource);
|
||||
var memoryManager = new MemoryManagerNative(addressSpace, context.Memory, addressSpaceSize, invalidAccessHandler);
|
||||
processContext = new ArmProcessContext<MemoryManagerNative>(pid, cpuEngine, _gpu, memoryManager, addressSpace.AddressSpaceSize, for64Bit, memoryManager.ReservedSize);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -65,8 +83,6 @@ namespace Ryujinx.HLE.HOS
|
||||
|
||||
var cpuEngine = new JitEngine(_tickSource);
|
||||
|
||||
AddressSpace addressSpace = null;
|
||||
|
||||
if (mode == MemoryManagerMode.HostMapped || mode == MemoryManagerMode.HostMappedUnsafe)
|
||||
{
|
||||
if (!AddressSpace.TryCreate(context.Memory, addressSpaceSize, MemoryBlock.GetPageSize() == MemoryManagerHostMapped.PageSize, out addressSpace))
|
||||
|
@ -107,6 +107,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
MemoryRegion memRegion,
|
||||
ulong address,
|
||||
ulong size,
|
||||
ulong reservedSize,
|
||||
KMemoryBlockSlabManager slabManager)
|
||||
{
|
||||
if ((uint)addrSpaceType > (uint)AddressSpaceType.Addr39Bits)
|
||||
@ -128,6 +129,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
memRegion,
|
||||
address,
|
||||
size,
|
||||
reservedSize,
|
||||
slabManager);
|
||||
|
||||
if (result != Result.Success)
|
||||
@ -155,6 +157,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
MemoryRegion memRegion,
|
||||
ulong address,
|
||||
ulong size,
|
||||
ulong reservedSize,
|
||||
KMemoryBlockSlabManager slabManager)
|
||||
{
|
||||
ulong endAddr = address + size;
|
||||
@ -178,7 +181,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
CodeRegionStart = 0x200000;
|
||||
codeRegionSize = 0x3fe00000;
|
||||
AslrRegionStart = 0x200000;
|
||||
AslrRegionEnd = AslrRegionStart + 0xffe00000;
|
||||
AslrRegionEnd = 0x100000000;
|
||||
stackAndTlsIoStart = 0x200000;
|
||||
stackAndTlsIoEnd = 0x40000000;
|
||||
break;
|
||||
@ -191,7 +194,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
CodeRegionStart = 0x8000000;
|
||||
codeRegionSize = 0x78000000;
|
||||
AslrRegionStart = 0x8000000;
|
||||
AslrRegionEnd = AslrRegionStart + 0xff8000000;
|
||||
AslrRegionEnd = 0x1000000000;
|
||||
stackAndTlsIoStart = 0x8000000;
|
||||
stackAndTlsIoEnd = 0x80000000;
|
||||
break;
|
||||
@ -204,7 +207,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
CodeRegionStart = 0x200000;
|
||||
codeRegionSize = 0x3fe00000;
|
||||
AslrRegionStart = 0x200000;
|
||||
AslrRegionEnd = AslrRegionStart + 0xffe00000;
|
||||
AslrRegionEnd = 0x100000000;
|
||||
stackAndTlsIoStart = 0x200000;
|
||||
stackAndTlsIoEnd = 0x40000000;
|
||||
break;
|
||||
@ -222,8 +225,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
codeRegionSize = BitUtils.AlignUp<ulong>(endAddr, RegionAlignment) - CodeRegionStart;
|
||||
stackAndTlsIoStart = 0;
|
||||
stackAndTlsIoEnd = 0;
|
||||
AslrRegionStart = 0x8000000;
|
||||
addrSpaceEnd = 1UL << addressSpaceWidth;
|
||||
AslrRegionStart = reservedSize + 0x8000000;
|
||||
addrSpaceEnd = reservedSize + (1UL << addressSpaceWidth);
|
||||
AslrRegionEnd = addrSpaceEnd;
|
||||
}
|
||||
else
|
||||
@ -234,8 +237,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
tlsIoRegion.Size = 0x1000000000;
|
||||
CodeRegionStart = BitUtils.AlignDown(address, RegionAlignment);
|
||||
codeRegionSize = BitUtils.AlignUp(endAddr, RegionAlignment) - CodeRegionStart;
|
||||
AslrRegionStart = 0x8000000;
|
||||
AslrRegionEnd = AslrRegionStart + 0x7ff8000000;
|
||||
AslrRegionStart = reservedSize + 0x8000000;
|
||||
AslrRegionEnd = 0x8000000000;
|
||||
stackAndTlsIoStart = 0;
|
||||
stackAndTlsIoEnd = 0;
|
||||
}
|
||||
|
@ -7,11 +7,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||
interface IProcessContext : IDisposable
|
||||
{
|
||||
IVirtualMemoryManager AddressSpace { get; }
|
||||
ulong ReservedSize { get; }
|
||||
|
||||
ulong AddressSpaceSize { get; }
|
||||
|
||||
IExecutionContext CreateExecutionContext(ExceptionCallbacks exceptionCallbacks);
|
||||
void Execute(IExecutionContext context, ulong codeAddress);
|
||||
void InvalidateCacheRegion(ulong address, ulong size);
|
||||
void PatchCodeForNce(ulong textAddress, ulong textSize, ulong patchRegionAddress, ulong patchRegionSize);
|
||||
}
|
||||
}
|
||||
|
@ -139,7 +139,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||
|
||||
bool aslrEnabled = creationInfo.Flags.HasFlag(ProcessCreationFlags.EnableAslr);
|
||||
|
||||
ulong codeAddress = creationInfo.CodeAddress;
|
||||
ulong codeAddress = creationInfo.CodeAddress + Context.ReservedSize;
|
||||
|
||||
ulong codeSize = (ulong)creationInfo.CodePagesCount * KPageTableBase.PageSize;
|
||||
|
||||
@ -154,6 +154,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||
memRegion,
|
||||
codeAddress,
|
||||
codeSize,
|
||||
Context.ReservedSize,
|
||||
slabManager);
|
||||
|
||||
if (result != Result.Success)
|
||||
@ -189,7 +190,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||
KResourceLimit resourceLimit,
|
||||
MemoryRegion memRegion,
|
||||
IProcessContextFactory contextFactory,
|
||||
ThreadStart customThreadStart = null)
|
||||
ThreadStart customThreadStart = null,
|
||||
ulong entrypointOffset = 0UL)
|
||||
{
|
||||
ResourceLimit = resourceLimit;
|
||||
_memRegion = memRegion;
|
||||
@ -247,7 +249,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||
|
||||
bool aslrEnabled = creationInfo.Flags.HasFlag(ProcessCreationFlags.EnableAslr);
|
||||
|
||||
ulong codeAddress = creationInfo.CodeAddress;
|
||||
ulong codeAddress = creationInfo.CodeAddress + Context.ReservedSize;
|
||||
|
||||
ulong codeSize = codePagesCount * KPageTableBase.PageSize;
|
||||
|
||||
@ -258,6 +260,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||
memRegion,
|
||||
codeAddress,
|
||||
codeSize,
|
||||
Context.ReservedSize,
|
||||
slabManager);
|
||||
|
||||
if (result != Result.Success)
|
||||
@ -303,6 +306,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||
CleanUpForError();
|
||||
}
|
||||
|
||||
_entrypoint += entrypointOffset;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -347,7 +352,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||
|
||||
Flags = creationInfo.Flags;
|
||||
TitleId = creationInfo.TitleId;
|
||||
_entrypoint = creationInfo.CodeAddress;
|
||||
_entrypoint = creationInfo.CodeAddress + Context.ReservedSize;
|
||||
_imageSize = (ulong)creationInfo.CodePagesCount * KPageTableBase.PageSize;
|
||||
|
||||
switch (Flags & ProcessCreationFlags.AddressSpaceMask)
|
||||
|
@ -7,6 +7,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||
class ProcessContext : IProcessContext
|
||||
{
|
||||
public IVirtualMemoryManager AddressSpace { get; }
|
||||
public ulong ReservedSize => 0UL;
|
||||
|
||||
public ulong AddressSpaceSize { get; }
|
||||
|
||||
@ -30,6 +31,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||
{
|
||||
}
|
||||
|
||||
public void PatchCodeForNce(ulong textAddress, ulong textSize, ulong patchRegionAddress, ulong patchRegionSize)
|
||||
{
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
@ -133,6 +133,8 @@ namespace Ryujinx.HLE.Loaders.Processes
|
||||
return resultCode;
|
||||
}
|
||||
|
||||
private const int ReservedPatchSize = 0x100000;
|
||||
|
||||
public static bool LoadKip(KernelContext context, KipExecutable kip)
|
||||
{
|
||||
uint endOffset = kip.DataOffset + (uint)kip.Data.Length;
|
||||
@ -197,7 +199,9 @@ namespace Ryujinx.HLE.Loaders.Processes
|
||||
return false;
|
||||
}
|
||||
|
||||
result = LoadIntoMemory(process, kip, codeBaseAddress);
|
||||
// TODO: Support NCE of KIPs too.
|
||||
result = LoadIntoMemory(process, kip, codeBaseAddress, 0UL);
|
||||
|
||||
if (result != Result.Success)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Loader, $"Process initialization returned error \"{result}\".");
|
||||
@ -257,6 +261,7 @@ namespace Ryujinx.HLE.Loaders.Processes
|
||||
_ => "",
|
||||
}).ToUpper());
|
||||
|
||||
ulong[] nsoPatch = new ulong[executables.Length];
|
||||
ulong[] nsoBase = new ulong[executables.Length];
|
||||
|
||||
for (int index = 0; index < executables.Length; index++)
|
||||
@ -281,6 +286,10 @@ namespace Ryujinx.HLE.Loaders.Processes
|
||||
|
||||
nsoSize = BitUtils.AlignUp<uint>(nsoSize, KPageTableBase.PageSize);
|
||||
|
||||
nsoPatch[index] = codeStart + codeSize;
|
||||
|
||||
codeSize += ReservedPatchSize;
|
||||
|
||||
nsoBase[index] = codeStart + codeSize;
|
||||
|
||||
codeSize += nsoSize;
|
||||
@ -304,7 +313,7 @@ namespace Ryujinx.HLE.Loaders.Processes
|
||||
programId,
|
||||
codeStart,
|
||||
codePagesCount,
|
||||
(ProcessCreationFlags)meta.Flags | ProcessCreationFlags.IsApplication,
|
||||
(ProcessCreationFlags)meta.Flags,
|
||||
0,
|
||||
personalMmHeapPagesCount);
|
||||
|
||||
@ -381,7 +390,8 @@ namespace Ryujinx.HLE.Loaders.Processes
|
||||
MemoryMarshal.Cast<byte, uint>(npdm.KernelCapabilityData),
|
||||
resourceLimit,
|
||||
memoryRegion,
|
||||
processContextFactory);
|
||||
processContextFactory,
|
||||
entrypointOffset: ReservedPatchSize);
|
||||
|
||||
if (result != Result.Success)
|
||||
{
|
||||
@ -392,9 +402,13 @@ namespace Ryujinx.HLE.Loaders.Processes
|
||||
|
||||
for (int index = 0; index < executables.Length; index++)
|
||||
{
|
||||
Logger.Info?.Print(LogClass.Loader, $"Loading image {index} at 0x{nsoBase[index]:x16}...");
|
||||
ulong nsoPatchAddress = process.Context.ReservedSize + nsoPatch[index];
|
||||
ulong nsoBaseAddress = process.Context.ReservedSize + nsoBase[index];
|
||||
|
||||
Logger.Info?.Print(LogClass.Loader, $"Loading image {index} at 0x{nsoBaseAddress:x16}...");
|
||||
|
||||
result = LoadIntoMemory(process, executables[index], nsoBaseAddress, nsoPatchAddress);
|
||||
|
||||
result = LoadIntoMemory(process, executables[index], nsoBase[index]);
|
||||
if (result != Result.Success)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Loader, $"Process initialization returned error \"{result}\".");
|
||||
@ -433,7 +447,7 @@ namespace Ryujinx.HLE.Loaders.Processes
|
||||
device.System.State.DesiredTitleLanguage);
|
||||
}
|
||||
|
||||
public static Result LoadIntoMemory(KProcess process, IExecutable image, ulong baseAddress)
|
||||
private static Result LoadIntoMemory(KProcess process, IExecutable image, ulong baseAddress, ulong patchAddress)
|
||||
{
|
||||
ulong textStart = baseAddress + image.TextOffset;
|
||||
ulong roStart = baseAddress + image.RoOffset;
|
||||
@ -453,6 +467,8 @@ namespace Ryujinx.HLE.Loaders.Processes
|
||||
|
||||
process.CpuMemory.Fill(bssStart, image.BssSize, 0);
|
||||
|
||||
process.Context.PatchCodeForNce(textStart, (ulong)image.Text.Length, patchAddress, ReservedPatchSize);
|
||||
|
||||
Result SetProcessMemoryPermission(ulong address, ulong size, KMemoryPermission permission)
|
||||
{
|
||||
if (size == 0)
|
||||
|
@ -96,6 +96,11 @@ namespace Ryujinx.Memory
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Reprotect(ulong va, ulong size, MemoryPermission permission)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public T Read<T>(ulong va) where T : unmanaged
|
||||
{
|
||||
|
@ -44,6 +44,14 @@ namespace Ryujinx.Memory
|
||||
/// <param name="size">Size of the range to be unmapped</param>
|
||||
void Unmap(ulong va, ulong size);
|
||||
|
||||
/// <summary>
|
||||
/// Reprotects a previously mapped range of virtual memory.
|
||||
/// </summary>
|
||||
/// <param name="va">Virtual address of the range to be reprotected</param>
|
||||
/// <param name="size">Size of the range to be reprotected</param>
|
||||
/// <param name="permission">New protection of the memory range</param>
|
||||
void Reprotect(ulong va, ulong size, MemoryPermission permission);
|
||||
|
||||
/// <summary>
|
||||
/// Reads data from CPU mapped memory.
|
||||
/// </summary>
|
||||
|
@ -16,15 +16,15 @@ namespace Ryujinx.Memory
|
||||
|
||||
public static IntPtr Allocate(ulong size, bool forJit)
|
||||
{
|
||||
return AllocateInternal(size, MmapProts.PROT_READ | MmapProts.PROT_WRITE, forJit);
|
||||
return AllocateInternal(size, MmapProts.PROT_READ | MmapProts.PROT_WRITE, forJit, false);
|
||||
}
|
||||
|
||||
public static IntPtr Reserve(ulong size, bool forJit)
|
||||
{
|
||||
return AllocateInternal(size, MmapProts.PROT_NONE, forJit);
|
||||
return AllocateInternal(size, MmapProts.PROT_NONE, forJit, false);
|
||||
}
|
||||
|
||||
private static IntPtr AllocateInternal(ulong size, MmapProts prot, bool forJit, bool shared = false)
|
||||
private static IntPtr AllocateInternal(ulong size, MmapProts prot, bool forJit, bool shared)
|
||||
{
|
||||
MmapFlags flags = MmapFlags.MAP_ANONYMOUS;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user