This commit is contained in:
Domenico V 2024-03-29 13:53:20 -03:00 committed by GitHub
commit cbedc2d380
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 150 additions and 54 deletions

View file

@ -10,7 +10,7 @@ namespace ARMeilleure.Memory
MemoryManagerType Type { get; }
event Action<ulong, ulong> UnmapEvent;
event Action<ulong, ulong, bool> UnmapEvent;
/// <summary>
/// Reads data from CPU mapped memory.

View file

@ -496,7 +496,7 @@ namespace ARMeilleure.Translation
context.MarkLabel(lblExit);
}
public void InvalidateJitCacheRegion(ulong address, ulong size)
public void InvalidateJitCacheRegion(ulong address, ulong size, bool clearRejitQueueOnly = false)
{
ulong[] overlapAddresses = Array.Empty<ulong>();
@ -508,6 +508,11 @@ namespace ARMeilleure.Translation
ClearRejitQueue(allowRequeue: true);
}
if (clearRejitQueueOnly)
{
return;
}
for (int index = 0; index < overlapsCount; index++)
{
ulong overlapAddress = overlapAddresses[index];

View file

@ -38,7 +38,7 @@ namespace Ryujinx.Cpu.AppleHv
public MemoryTracking Tracking { get; }
public event Action<ulong, ulong> UnmapEvent;
public event Action<ulong, ulong, bool> UnmapEvent;
protected override ulong AddressSpaceSize { get; }
@ -103,11 +103,11 @@ namespace Ryujinx.Cpu.AppleHv
}
/// <inheritdoc/>
public void Unmap(ulong va, ulong size)
public void Unmap(ulong va, ulong size, bool clearRejitQueueOnly = false)
{
AssertValidAddressAndSize(va, size);
UnmapEvent?.Invoke(va, size);
UnmapEvent?.Invoke(va, size, clearRejitQueueOnly);
Tracking.Unmap(va, size);
_pages.RemoveMapping(va, size);

View file

@ -23,9 +23,9 @@ namespace Ryujinx.Cpu.Jit
memory.UnmapEvent += UnmapHandler;
}
private void UnmapHandler(ulong address, ulong size)
private void UnmapHandler(ulong address, ulong size, bool clearRejitQueueOnly = false)
{
_translator.InvalidateJitCacheRegion(address, size);
_translator.InvalidateJitCacheRegion(address, size, clearRejitQueueOnly);
}
/// <inheritdoc/>

View file

@ -44,7 +44,7 @@ namespace Ryujinx.Cpu.Jit
public MemoryTracking Tracking { get; }
public event Action<ulong, ulong> UnmapEvent;
public event Action<ulong, ulong, bool> UnmapEvent;
protected override ulong AddressSpaceSize { get; }
@ -104,7 +104,7 @@ namespace Ryujinx.Cpu.Jit
}
/// <inheritdoc/>
public void Unmap(ulong va, ulong size)
public void Unmap(ulong va, ulong size, bool clearRejitQueueOnly = false)
{
// If size is 0, there's nothing to unmap, just exit early.
if (size == 0)
@ -114,7 +114,7 @@ namespace Ryujinx.Cpu.Jit
AssertValidAddressAndSize(va, size);
UnmapEvent?.Invoke(va, size);
UnmapEvent?.Invoke(va, size, clearRejitQueueOnly);
Tracking.Unmap(va, size);
_pages.RemoveMapping(va, size);

View file

@ -36,7 +36,7 @@ namespace Ryujinx.Cpu.Jit
public MemoryTracking Tracking { get; }
public event Action<ulong, ulong> UnmapEvent;
public event Action<ulong, ulong, bool> UnmapEvent;
protected override ulong AddressSpaceSize { get; }
@ -103,11 +103,11 @@ namespace Ryujinx.Cpu.Jit
}
/// <inheritdoc/>
public void Unmap(ulong va, ulong size)
public void Unmap(ulong va, ulong size, bool clearRejitQueueOnly = false)
{
AssertValidAddressAndSize(va, size);
UnmapEvent?.Invoke(va, size);
UnmapEvent?.Invoke(va, size, clearRejitQueueOnly);
Tracking.Unmap(va, size);
_pages.RemoveMapping(va, size);

View file

@ -203,7 +203,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
}
/// <inheritdoc/>
protected override Result Unmap(ulong address, ulong pagesCount)
protected override Result Unmap(ulong address, ulong pagesCount, bool clearRejitQueueOnly = false)
{
KPageList pagesToClose = new();
@ -218,7 +218,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
}
}
_cpuMemory.Unmap(address, pagesCount * PageSize);
_cpuMemory.Unmap(address, pagesCount * PageSize, clearRejitQueueOnly);
pagesToClose.DecrementPagesReferenceCount(Context.MemoryManager);

View file

@ -648,7 +648,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
}
}
public Result UnmapProcessCodeMemory(ulong dst, ulong src, ulong size)
public Result UnmapProcessCodeMemory(ulong dst, ulong src, ulong size, bool clearRejitQueueOnly = false)
{
lock (_blockManager)
{
@ -694,7 +694,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
{
ulong pagesCount = size / PageSize;
Result result = Unmap(dst, pagesCount);
Result result = Unmap(dst, pagesCount, clearRejitQueueOnly);
if (result != Result.Success)
{
@ -3032,7 +3032,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
/// <param name="address">Virtual address of the region to unmap</param>
/// <param name="pagesCount">Number of pages to unmap</param>
/// <returns>Result of the unmapping operation</returns>
protected abstract Result Unmap(ulong address, ulong pagesCount);
protected abstract Result Unmap(ulong address, ulong pagesCount, bool clearRejitQueueOnly = false);
/// <summary>
/// Changes the permissions of a given virtual memory region.

View file

@ -18,6 +18,8 @@ namespace Ryujinx.HLE.HOS.Services.Ro
[Service("ro:1")] // 7.0.0+
class IRoInterface : DisposableIpcService
{
private const bool EnableOpt = true;
private const int MaxNrr = 0x40;
private const int MaxNro = 0x40;
private const int MaxMapRetries = 0x200;
@ -28,6 +30,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro
private readonly List<NrrInfo> _nrrInfos;
private readonly List<NroInfo> _nroInfos;
private readonly List<NroInfo> _nroInfosUnloaded;
private KProcess _owner;
private IVirtualMemoryManager _ownerMm;
@ -36,6 +39,8 @@ namespace Ryujinx.HLE.HOS.Services.Ro
{
_nrrInfos = new List<NrrInfo>(MaxNrr);
_nroInfos = new List<NroInfo>(MaxNro);
_nroInfosUnloaded = new List<NroInfo>(MaxNro);
_owner = null;
_ownerMm = null;
}
@ -198,17 +203,18 @@ namespace Ryujinx.HLE.HOS.Services.Ro
return ResultCode.Success;
}
private ResultCode MapNro(KProcess process, NroInfo info, out ulong nroMappedAddress)
private ResultCode MapNro(KProcess process, NroInfo info, MapType mapType, out ulong nroMappedAddress)
{
KPageTableBase memMgr = process.MemoryManager;
int retryCount = 0;
int maxMapRetries = mapType != MapType.Within ? MaxMapRetries : 1;
nroMappedAddress = 0;
while (retryCount++ < MaxMapRetries)
while (retryCount++ < maxMapRetries)
{
ResultCode result = MapCodeMemoryInProcess(process, info.NroAddress, info.NroSize, out nroMappedAddress);
ResultCode result = MapCodeMemoryInProcess(process, info, mapType, out nroMappedAddress);
if (result != ResultCode.Success)
{
@ -262,31 +268,73 @@ namespace Ryujinx.HLE.HOS.Services.Ro
return false;
}
private ResultCode MapCodeMemoryInProcess(KProcess process, ulong baseAddress, ulong size, out ulong targetAddress)
private static bool NotOverlapsWith(ulong newAddress, ulong newSize, ulong oldAddress, ulong oldSize)
{
return newAddress + newSize <= oldAddress || oldAddress + oldSize <= newAddress;
}
private ResultCode MapCodeMemoryInProcess(KProcess process, NroInfo info, MapType mapType, out ulong targetAddress)
{
KPageTableBase memMgr = process.MemoryManager;
int retryCount = 0;
int maxMapRetries = mapType != MapType.Within ? MaxMapRetries : 1;
targetAddress = 0;
int retryCount;
ulong addressSpacePageLimit = (memMgr.GetAddrSpaceSize() - size) >> 12;
for (retryCount = 0; retryCount < MaxMapRetries; retryCount++)
while (retryCount++ < maxMapRetries)
{
while (true)
if (mapType == MapType.Default)
{
ulong randomOffset = (ulong)(uint)Random.Shared.Next(0, (int)addressSpacePageLimit) << 12;
targetAddress = memMgr.GetAddrSpaceBaseAddr() + randomOffset;
if (memMgr.InsideAddrSpace(targetAddress, size) && !memMgr.InsideHeapRegion(targetAddress, size) && !memMgr.InsideAliasRegion(targetAddress, size))
while (true)
{
break;
ulong addressSpacePageLimit = (memMgr.GetAddrSpaceSize() - info.NroSize) >> 12;
ulong randomOffset = (ulong)(uint)Random.Shared.Next(0, (int)addressSpacePageLimit) << 12;
targetAddress = memMgr.GetAddrSpaceBaseAddr() + randomOffset;
if (memMgr.InsideAddrSpace(targetAddress, info.NroSize) &&
!memMgr.InsideHeapRegion(targetAddress, info.NroSize) &&
!memMgr.InsideAliasRegion(targetAddress, info.NroSize))
{
break;
}
}
}
else if (mapType == MapType.Between)
{
while (true)
{
while (true)
{
ulong addressSpacePageLimit = (memMgr.GetAddrSpaceSize() - info.NroSize) >> 12;
ulong randomOffset = (ulong)(uint)Random.Shared.Next(0, (int)addressSpacePageLimit) << 12;
Result result = memMgr.MapProcessCodeMemory(targetAddress, baseAddress, size);
targetAddress = memMgr.GetAddrSpaceBaseAddr() + randomOffset;
if (memMgr.InsideAddrSpace(targetAddress, info.NroSize) &&
!memMgr.InsideHeapRegion(targetAddress, info.NroSize) &&
!memMgr.InsideAliasRegion(targetAddress, info.NroSize))
{
break;
}
}
ulong targetAddressTmp = targetAddress; // CS1628.
if (_nroInfosUnloaded.TrueForAll((infoU) => NotOverlapsWith(targetAddressTmp, info.NroSize + info.BssSize, infoU.NroMappedAddress, infoU.NroSize + infoU.BssSize)))
{
break;
}
}
}
else /* if (mapType == MapType.Within) */
{
NroInfo infoU = _nroInfosUnloaded.Find((infoU) => infoU.Hash.SequenceEqual(info.Hash));
targetAddress = infoU.NroMappedAddress;
}
Result result = memMgr.MapProcessCodeMemory(targetAddress, info.NroAddress, info.NroSize);
if (result == KernelResult.InvalidMemState)
{
@ -297,7 +345,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro
return (ResultCode)result.ErrorCode;
}
if (!CanAddGuardRegionsInProcess(process, targetAddress, size))
if (!CanAddGuardRegionsInProcess(process, targetAddress, info.NroSize))
{
continue;
}
@ -305,12 +353,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro
return ResultCode.Success;
}
if (retryCount == MaxMapRetries)
{
return ResultCode.InsufficientAddressSpace;
}
return ResultCode.Success;
return ResultCode.InsufficientAddressSpace;
}
private Result SetNroMemoryPermissions(KProcess process, IExecutable relocatableObject, ulong baseAddress)
@ -369,16 +412,20 @@ namespace Ryujinx.HLE.HOS.Services.Ro
{
if (info.NroMappedAddress == nroMappedAddress)
{
if (EnableOpt)
{
_nroInfosUnloaded.Add(info);
}
_nroInfos.Remove(info);
return UnmapNroFromInfo(info);
return UnmapNroFromInfo(info, clearRejitQueueOnly: EnableOpt);
}
}
return ResultCode.NotLoaded;
}
private ResultCode UnmapNroFromInfo(NroInfo info)
private ResultCode UnmapNroFromInfo(NroInfo info, bool clearRejitQueueOnly = false)
{
ulong textSize = (ulong)info.Executable.Text.Length;
ulong roSize = (ulong)info.Executable.Ro.Length;
@ -392,7 +439,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro
result = _owner.MemoryManager.UnmapProcessCodeMemory(
info.NroMappedAddress + textSize + roSize + dataSize,
info.Executable.BssAddress,
bssSize);
bssSize, clearRejitQueueOnly);
}
if (result == Result.Success)
@ -400,14 +447,14 @@ namespace Ryujinx.HLE.HOS.Services.Ro
result = _owner.MemoryManager.UnmapProcessCodeMemory(
info.NroMappedAddress + textSize + roSize,
info.Executable.SourceAddress + textSize + roSize,
dataSize);
dataSize, clearRejitQueueOnly);
if (result == Result.Success)
{
result = _owner.MemoryManager.UnmapProcessCodeMemory(
info.NroMappedAddress,
info.Executable.SourceAddress,
textSize + roSize);
textSize + roSize, clearRejitQueueOnly);
}
}
@ -424,6 +471,8 @@ namespace Ryujinx.HLE.HOS.Services.Ro
return ResultCode.InvalidProcess;
}
private enum MapType { Default, Between, Within }
[CommandCmif(0)]
// LoadNro(u64, u64, u64, u64, u64, pid) -> u64
public ResultCode LoadNro(ServiceCtx context)
@ -447,7 +496,48 @@ namespace Ryujinx.HLE.HOS.Services.Ro
if (result == ResultCode.Success)
{
result = MapNro(_owner, info, out nroMappedAddress);
MapType mapType = MapType.Default;
if (_nroInfosUnloaded.Count != 0)
{
mapType = _nroInfosUnloaded.Exists((infoU) => infoU.Hash.SequenceEqual(info.Hash)) // One/zero match.
? MapType.Within
: MapType.Between;
}
result = MapNro(_owner, info, mapType, out nroMappedAddress);
if (mapType == MapType.Between)
{
if (result != ResultCode.Success)
{
_nroInfosUnloaded.Clear();
mapType = MapType.Default;
result = MapNro(_owner, info, mapType, out nroMappedAddress);
}
}
else if (mapType == MapType.Within)
{
_nroInfosUnloaded.RemoveAll((infoU) => infoU.Hash.SequenceEqual(info.Hash)); // One match.
if (result != ResultCode.Success)
{
mapType = MapType.Between;
result = MapNro(_owner, info, mapType, out nroMappedAddress);
if (result != ResultCode.Success)
{
_nroInfosUnloaded.Clear();
mapType = MapType.Default;
result = MapNro(_owner, info, mapType, out nroMappedAddress);
}
}
}
if (result == ResultCode.Success)
{
@ -589,6 +679,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro
}
_nroInfos.Clear();
_nroInfosUnloaded.Clear();
if (_ownerMm is IRefCounted rc)
{

View file

@ -79,7 +79,7 @@ namespace Ryujinx.Memory
}
/// <inheritdoc/>
public void Unmap(ulong va, ulong size)
public void Unmap(ulong va, ulong size, bool clearRejitQueueOnly = false)
{
AssertValidAddressAndSize(va, size);

View file

@ -42,7 +42,7 @@ namespace Ryujinx.Memory
/// </summary>
/// <param name="va">Virtual address of the range to be unmapped</param>
/// <param name="size">Size of the range to be unmapped</param>
void Unmap(ulong va, ulong size);
void Unmap(ulong va, ulong size, bool clearRejitQueueOnly = false);
/// <summary>
/// Reads data from CPU mapped memory.

View file

@ -27,7 +27,7 @@ namespace Ryujinx.Tests.Memory
throw new NotImplementedException();
}
public void Unmap(ulong va, ulong size)
public void Unmap(ulong va, ulong size, bool clearRejitQueueOnly = false)
{
throw new NotImplementedException();
}

View file

@ -16,9 +16,9 @@ namespace Ryujinx.Tests.Cpu
memory.UnmapEvent += UnmapHandler;
}
private void UnmapHandler(ulong address, ulong size)
private void UnmapHandler(ulong address, ulong size, bool clearRejitQueueOnly = false)
{
_translator.InvalidateJitCacheRegion(address, size);
_translator.InvalidateJitCacheRegion(address, size, clearRejitQueueOnly);
}
public static ExecutionContext CreateExecutionContext()

View file

@ -12,7 +12,7 @@ namespace Ryujinx.Tests.Memory
public MemoryManagerType Type => MemoryManagerType.HostMappedUnsafe;
#pragma warning disable CS0067 // The event is never used
public event Action<ulong, ulong> UnmapEvent;
public event Action<ulong, ulong, bool> UnmapEvent;
#pragma warning restore CS0067
public ref T GetRef<T>(ulong va) where T : unmanaged