Archived
1
0
forked from MeloNX/MeloNX

Add NCE code

(cherry picked from commit 555f9a2db23751da924be7ed5bb63874a976237e)
This commit is contained in:
gdk 2023-07-03 19:28:05 -03:00 committed by Emmanuel Hansen
parent 1dd2d68be8
commit 7a2869ab6e
42 changed files with 2347 additions and 44 deletions

View File

@ -0,0 +1,14 @@
using System;
namespace Ryujinx.Common.SystemInfo
{
public class SystemInfo
{
public static bool IsBionic { get; set; }
public static bool IsAndroid()
{
return OperatingSystem.IsAndroid() || IsBionic;
}
}
}

View File

@ -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();

View File

@ -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);
}

View File

@ -15,7 +15,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 sealed class HvMemoryManager : VirtualMemoryManagerRefCountedBase, IMemoryManager, IVirtualMemoryManagerTracked
public sealed class HvMemoryManager : VirtualMemoryManagerRefCountedBase, ICpuMemoryManager, IVirtualMemoryManagerTracked
{
private readonly InvalidAccessHandler _invalidAccessHandler;

View 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()
{
}
}
}

View File

@ -38,6 +38,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>

View File

@ -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);
}
}

View 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);
}
}

View File

@ -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)
{

View File

@ -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);
}

View File

@ -15,7 +15,7 @@ namespace Ryujinx.Cpu.Jit
/// <summary>
/// Represents a CPU memory manager.
/// </summary>
public sealed class MemoryManager : VirtualMemoryManagerRefCountedBase, IMemoryManager, IVirtualMemoryManagerTracked
public sealed class MemoryManager : VirtualMemoryManagerRefCountedBase, ICpuMemoryManager, IVirtualMemoryManagerTracked
{
private const int PteSize = 8;

View File

@ -1,4 +1,4 @@
using ARMeilleure.Memory;
using ARMeilleure.Memory;
using Ryujinx.Memory;
using Ryujinx.Memory.Range;
using Ryujinx.Memory.Tracking;
@ -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 : VirtualMemoryManagerRefCountedBase, IMemoryManager, IVirtualMemoryManagerTracked
public sealed class MemoryManagerHostMapped : VirtualMemoryManagerRefCountedBase, ICpuMemoryManager, IVirtualMemoryManagerTracked
{
private readonly InvalidAccessHandler _invalidAccessHandler;
private readonly bool _unsafeMode;

View File

@ -16,7 +16,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 MemoryManagerHostTracked : VirtualMemoryManagerRefCountedBase, IMemoryManager, IVirtualMemoryManagerTracked
public sealed class MemoryManagerHostTracked : VirtualMemoryManagerRefCountedBase, ICpuMemoryManager, IVirtualMemoryManagerTracked
{
private readonly InvalidAccessHandler _invalidAccessHandler;
private readonly bool _unsafeMode;

View File

@ -39,6 +39,11 @@ namespace Ryujinx.Cpu.LightningJit
_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)
{

View File

@ -12,7 +12,7 @@ namespace Ryujinx.Cpu.LightningJit
}
/// <inheritdoc/>
public ICpuContext CreateCpuContext(IMemoryManager memoryManager, bool for64Bit)
public ICpuContext CreateCpuContext(ICpuMemoryManager memoryManager, bool for64Bit)
{
return new LightningJitCpuContext(_tickSource, memoryManager, for64Bit);
}

View File

@ -55,6 +55,21 @@ namespace Ryujinx.Cpu
}
}
public MemoryEhMeilleure(ulong asSize, MemoryTracking tracking)
{
_tracking = tracking;
_baseAddress = 0UL;
ulong endAddress = asSize;
_trackingEvent = VirtualMemoryEvent;
bool added = NativeSignalHandler.AddTrackedRegion((nuint)_baseAddress, (nuint)endAddress, Marshal.GetFunctionPointerForDelegate(_trackingEvent));
if (!added)
{
throw new InvalidOperationException("Number of allowed tracked regions exceeded.");
}
}
private ulong VirtualMemoryEvent(ulong address, ulong size, bool write)
{
ulong pageSize = _pageSize;

View File

@ -0,0 +1,495 @@
using ARMeilleure.Memory;
using Ryujinx.Memory;
using Ryujinx.Memory.Range;
using Ryujinx.Memory.Tracking;
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
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 : VirtualMemoryManagerRefCountedBase, ICpuMemoryManager, IVirtualMemoryManagerTracked, IWritableBlock
{
private readonly InvalidAccessHandler _invalidAccessHandler;
private readonly MemoryBlock _addressSpace;
private readonly MemoryBlock _addressSpaceMirror;
private readonly MemoryBlock _backingMemory;
private readonly PageTable<ulong> _pageTable;
private readonly MemoryEhMeilleure _memoryEh;
private readonly ManagedPageFlags _pages;
/// <inheritdoc/>
public bool UsesPrivateAllocations => false;
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;
protected override ulong AddressSpaceSize { get; }
/// <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;
ulong asSize = PageSize;
int asBits = PageBits;
while (asSize < addressSpaceSize)
{
asSize <<= 1;
asBits++;
}
AddressSpaceBits = asBits;
AddressSpaceSize = addressSpace.AddressSpaceSize;
_pages = new ManagedPageFlags(AddressSpaceBits);
_addressSpace = addressSpace.Base;
_addressSpaceMirror = addressSpace.Mirror;
Tracking = new MemoryTracking(this, PageSize, invalidAccessHandler);
_memoryEh = new MemoryEhMeilleure(asSize, Tracking);
}
/// <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) || !_pages.IsRangeMapped(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);
_pages.AddMapping(va, size);
PtMap(va, pa, size);
Tracking.Map(va, size);
}
/// <inheritdoc/>
public void Unmap(ulong va, ulong size)
{
AssertValidAddressAndSize(va, size);
UnmapEvent?.Invoke(va, size);
Tracking.Unmap(va, size);
_pages.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);
}
public override T Read<T>(ulong va)
{
try
{
AssertMapped(va, (ulong)Unsafe.SizeOf<T>());
return _addressSpaceMirror.Read<T>(AddressToOffset(va));
}
catch (InvalidMemoryRegionException)
{
if (_invalidAccessHandler == null || !_invalidAccessHandler(va))
{
throw;
}
return default;
}
}
public override T ReadTracked<T>(ulong va)
{
try
{
SignalMemoryTracking(va, (ulong)Unsafe.SizeOf<T>(), false);
return Read<T>(va);
}
catch (InvalidMemoryRegionException)
{
if (_invalidAccessHandler == null || !_invalidAccessHandler(va))
{
throw;
}
return default;
}
}
public override 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;
}
}
}
public override void Write<T>(ulong va, T value)
{
try
{
SignalMemoryTracking(va, (ulong)Unsafe.SizeOf<T>(), write: true);
_addressSpaceMirror.Write(AddressToOffset(va), value);
}
catch (InvalidMemoryRegionException)
{
if (_invalidAccessHandler == null || !_invalidAccessHandler(va))
{
throw;
}
}
}
public override 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;
}
}
}
public override 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;
}
}
}
public override 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;
}
}
public override ReadOnlySequence<byte> GetReadOnlySequence(ulong va, int size, bool tracked = false)
{
if (tracked)
{
SignalMemoryTracking(va, (ulong)size, write: false);
}
else
{
AssertMapped(va, (ulong)size);
}
return new ReadOnlySequence<byte>(_addressSpaceMirror.GetMemory(va, size));
}
public override 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);
}
public override 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));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool IsMapped(ulong va)
{
return ValidateAddress(va) && _pages.IsMapped(va);
}
/// <inheritdoc/>
public bool IsRangeMapped(ulong va, ulong size)
{
AssertValidAddressAndSize(va, size);
return _pages.IsRangeMapped(va, size);
}
/// <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 override 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;
}
_pages.SignalMemoryTracking(Tracking, va, size, write, exemptId);
}
/// <inheritdoc/>
public void TrackingReprotect(ulong va, ulong size, MemoryPermission protection, bool guest)
{
if (guest)
{
_addressSpace.Reprotect(AddressToOffset(va), size, protection, false);
}
else
{
_pages.TrackingReprotect(va, size, protection);
}
}
/// <inheritdoc/>
public RegionHandle BeginTracking(ulong address, ulong size, int id, RegionFlags flags)
{
return Tracking.BeginTracking(address, size, id, flags);
}
/// <inheritdoc/>
public MultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id, RegionFlags flags)
{
return Tracking.BeginGranularTracking(address, size, handles, granularity, id, flags);
}
/// <inheritdoc/>
public SmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id)
{
return Tracking.BeginSmartGranularTracking(address, size, granularity, id);
}
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();
}
protected override Memory<byte> GetPhysicalAddressMemory(nuint pa, int size)
=> _addressSpaceMirror.GetMemory(pa, size);
protected override Span<byte> GetPhysicalAddressSpan(nuint pa, int size)
=> _addressSpaceMirror.GetSpan(pa, size);
protected override nuint TranslateVirtualAddressChecked(ulong va)
=> (nuint)GetPhysicalAddressChecked(va);
protected override nuint TranslateVirtualAddressUnchecked(ulong va)
=> (nuint)GetPhysicalAddressInternal(va);
}
}

View 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
}
}
}
}

View File

@ -0,0 +1,100 @@
using Ryujinx.Cpu.Jit;
using Ryujinx.Cpu.Signal;
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.InitializeSignalHandler((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)
{
}
public void Dispose()
{
}
}
}

View 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);
}
}
}

View 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();
}
}
}

View 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();
}
}

View File

@ -0,0 +1,25 @@
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 MapAsRw(ulong offset, ulong size) => _impl.Reprotect(offset, size, MemoryPermission.ReadAndWrite);
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();
}
}

View 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();
}
}

View 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;
}
}
}

View 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]);
}
}
}
}

View 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();
}
}
}
}

View 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}.");
}
}
}
}

View 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}.");
}
}
}
}

View 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
View 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

View File

@ -70,7 +70,7 @@ namespace Ryujinx.Cpu.Signal
config = new SignalHandlerConfig();
}
public static void InitializeSignalHandler(Func<IntPtr, IntPtr, IntPtr> customSignalHandlerFactory = null)
public static void InitializeSignalHandler(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;

View File

@ -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;
@ -148,6 +148,21 @@ namespace Ryujinx.Cpu.Signal
throw new InvalidOperationException($"Could not register SIGBUS sigaction. Error: {result}");
}
}
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;

View File

@ -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)

View File

@ -4,6 +4,7 @@ using Ryujinx.Cpu;
using Ryujinx.Cpu.AppleHv;
using Ryujinx.Cpu.Jit;
using Ryujinx.Cpu.LightningJit;
using Ryujinx.Cpu.Nce;
using Ryujinx.Graphics.Gpu;
using Ryujinx.HLE.HOS.Kernel;
using Ryujinx.HLE.HOS.Kernel.Process;
@ -46,14 +47,31 @@ namespace Ryujinx.HLE.HOS
public IProcessContext Create(KernelContext context, ulong pid, ulong addressSpaceSize, InvalidAccessHandler invalidAccessHandler, bool for64Bit)
{
IArmProcessContext processContext;
AddressSpace addressSpace = null;
bool isArm64Host = RuntimeInformation.ProcessArchitecture == Architecture.Arm64;
if (OperatingSystem.IsMacOS() && isArm64Host && for64Bit && context.Device.Configuration.UseHypervisor)
if (isArm64Host && 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, 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
{
@ -70,8 +88,6 @@ namespace Ryujinx.HLE.HOS
? new LightningJitEngine(_tickSource)
: new JitEngine(_tickSource);
AddressSpace addressSpace = null;
// We want to use host tracked mode if the host page size is > 4KB.
if ((mode == MemoryManagerMode.HostMapped || mode == MemoryManagerMode.HostMappedUnsafe) && MemoryBlock.GetPageSize() <= 0x1000)
{

View File

@ -104,6 +104,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
MemoryRegion memRegion,
ulong address,
ulong size,
ulong reservedSize,
KMemoryBlockSlabManager slabManager)
{
_contextId = Context.ContextIdManager.GetId();
@ -119,6 +120,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
memRegion,
address,
size,
reservedSize,
slabManager);
if (result != Result.Success)
@ -161,6 +163,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
MemoryRegion memRegion,
ulong address,
ulong size,
ulong reservedSize,
KMemoryBlockSlabManager slabManager)
{
ulong endAddr = address + size;
@ -186,7 +189,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
CodeRegionStart = 0x200000;
codeRegionSize = 0x3fe00000;
AslrRegionStart = 0x200000;
AslrRegionEnd = AslrRegionStart + 0xffe00000;
AslrRegionEnd = 0x100000000;
stackAndTlsIoStart = 0x200000;
stackAndTlsIoEnd = 0x40000000;
break;
@ -199,7 +202,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
CodeRegionStart = 0x8000000;
codeRegionSize = 0x78000000;
AslrRegionStart = 0x8000000;
AslrRegionEnd = AslrRegionStart + 0xff8000000;
AslrRegionEnd = 0x1000000000;
stackAndTlsIoStart = 0x8000000;
stackAndTlsIoEnd = 0x80000000;
break;
@ -212,7 +215,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
CodeRegionStart = 0x200000;
codeRegionSize = 0x3fe00000;
AslrRegionStart = 0x200000;
AslrRegionEnd = AslrRegionStart + 0xffe00000;
AslrRegionEnd = 0x100000000;
stackAndTlsIoStart = 0x200000;
stackAndTlsIoEnd = 0x40000000;
break;
@ -230,8 +233,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
codeRegionSize = BitUtils.AlignUp(endAddr, RegionAlignment) - CodeRegionStart;
stackAndTlsIoStart = 0;
stackAndTlsIoEnd = 0;
AslrRegionStart = 0x8000000;
addrSpaceEnd = 1UL << addressSpaceWidth;
AslrRegionStart = reservedSize + 0x8000000;
addrSpaceEnd = reservedSize + (1UL << addressSpaceWidth);
AslrRegionEnd = addrSpaceEnd;
}
else
@ -242,8 +245,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;
}

View File

@ -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);
}
}

View File

@ -135,7 +135,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
InitializeMemoryManager(creationInfo.Flags);
ulong codeAddress = creationInfo.CodeAddress;
ulong codeAddress = creationInfo.CodeAddress + Context.ReservedSize;
ulong codeSize = (ulong)creationInfo.CodePagesCount * KPageTableBase.PageSize;
@ -149,6 +149,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
memRegion,
codeAddress,
codeSize,
Context.ReservedSize,
slabManager);
if (result != Result.Success)
@ -184,7 +185,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;
@ -238,7 +240,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
InitializeMemoryManager(creationInfo.Flags);
ulong codeAddress = creationInfo.CodeAddress;
ulong codeAddress = creationInfo.CodeAddress + Context.ReservedSize;
ulong codeSize = codePagesCount * KPageTableBase.PageSize;
@ -248,6 +250,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
memRegion,
codeAddress,
codeSize,
Context.ReservedSize,
slabManager);
if (result != Result.Success)
@ -293,6 +296,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
CleanUpForError();
}
_entrypoint += entrypointOffset;
return result;
}
@ -337,7 +342,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)

View File

@ -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()
{
}

View File

@ -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}\".");
@ -258,6 +262,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++)
@ -282,6 +287,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;
@ -305,7 +314,7 @@ namespace Ryujinx.HLE.Loaders.Processes
programId,
codeStart,
codePagesCount,
(ProcessCreationFlags)meta.Flags | ProcessCreationFlags.IsApplication,
(ProcessCreationFlags)meta.Flags,
0,
personalMmHeapPagesCount);
@ -382,7 +391,8 @@ namespace Ryujinx.HLE.Loaders.Processes
MemoryMarshal.Cast<byte, uint>(npdm.KernelCapabilityData),
resourceLimit,
memoryRegion,
processContextFactory);
processContextFactory,
entrypointOffset: ReservedPatchSize);
if (result != Result.Success)
{
@ -393,9 +403,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}\".");
@ -453,7 +467,7 @@ namespace Ryujinx.HLE.Loaders.Processes
return processResult;
}
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;
@ -473,6 +487,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)

View File

@ -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;

View File

@ -113,7 +113,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
{