forked from MeloNX/MeloNX
Remove address space mirror and tweak address space layout when host has small adress space
This commit is contained in:
parent
bbe460cecd
commit
c57f6a7fe3
@ -5,6 +5,8 @@ namespace Ryujinx.Cpu
|
||||
{
|
||||
public class AddressSpace : IDisposable
|
||||
{
|
||||
private const MemoryAllocationFlags AsFlags = MemoryAllocationFlags.Reserve | MemoryAllocationFlags.ViewCompatible;
|
||||
|
||||
private readonly MemoryBlock _backingMemory;
|
||||
|
||||
public MemoryBlock Base { get; }
|
||||
@ -25,28 +27,42 @@ namespace Ryujinx.Cpu
|
||||
{
|
||||
addressSpace = null;
|
||||
|
||||
const MemoryAllocationFlags AsFlags = MemoryAllocationFlags.Reserve | MemoryAllocationFlags.ViewCompatible;
|
||||
MemoryBlock baseMemory = null;
|
||||
MemoryBlock mirrorMemory = null;
|
||||
|
||||
try
|
||||
{
|
||||
baseMemory = new MemoryBlock(asSize, AsFlags);
|
||||
mirrorMemory = new MemoryBlock(asSize, AsFlags);
|
||||
addressSpace = new AddressSpace(backingMemory, baseMemory, mirrorMemory, asSize);
|
||||
}
|
||||
catch (SystemException)
|
||||
{
|
||||
baseMemory?.Dispose();
|
||||
mirrorMemory?.Dispose();
|
||||
}
|
||||
|
||||
return addressSpace != null;
|
||||
}
|
||||
|
||||
public static bool TryCreateWithoutMirror(ulong asSize, out MemoryBlock addressSpace)
|
||||
{
|
||||
addressSpace = null;
|
||||
|
||||
ulong minAddressSpaceSize = Math.Min(asSize, 1UL << 36);
|
||||
|
||||
// Attempt to create the address space with expected size or try to reduce it until it succeed.
|
||||
for (ulong addressSpaceSize = asSize; addressSpaceSize >= minAddressSpaceSize; addressSpaceSize >>= 1)
|
||||
for (ulong addressSpaceSize = asSize; addressSpaceSize >= minAddressSpaceSize; addressSpaceSize -= 0x100000000UL)
|
||||
{
|
||||
MemoryBlock baseMemory = null;
|
||||
MemoryBlock mirrorMemory = null;
|
||||
|
||||
try
|
||||
{
|
||||
baseMemory = new MemoryBlock(addressSpaceSize, AsFlags);
|
||||
mirrorMemory = new MemoryBlock(addressSpaceSize, AsFlags);
|
||||
addressSpace = new AddressSpace(backingMemory, baseMemory, mirrorMemory, addressSpaceSize);
|
||||
MemoryBlock baseMemory = new MemoryBlock(addressSpaceSize, AsFlags);
|
||||
addressSpace = baseMemory;
|
||||
|
||||
break;
|
||||
}
|
||||
catch (SystemException)
|
||||
{
|
||||
baseMemory?.Dispose();
|
||||
mirrorMemory?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
330
src/Ryujinx.Cpu/Jit/MemoryManagerHostNoMirror.cs
Normal file
330
src/Ryujinx.Cpu/Jit/MemoryManagerHostNoMirror.cs
Normal file
@ -0,0 +1,330 @@
|
||||
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;
|
||||
|
||||
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 MemoryManagerHostNoMirror : VirtualMemoryManagerRefCountedBase, ICpuMemoryManager, IVirtualMemoryManagerTracked, IWritableBlock
|
||||
{
|
||||
private readonly InvalidAccessHandler _invalidAccessHandler;
|
||||
private readonly bool _unsafeMode;
|
||||
|
||||
private readonly MemoryBlock _addressSpace;
|
||||
private readonly MemoryBlock _backingMemory;
|
||||
private readonly PageTable<ulong> _pageTable;
|
||||
|
||||
public int AddressSpaceBits { get; }
|
||||
protected override ulong AddressSpaceSize { get; }
|
||||
|
||||
private readonly MemoryEhMeilleure _memoryEh;
|
||||
|
||||
private readonly ManagedPageFlags _pages;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool UsesPrivateAllocations => false;
|
||||
|
||||
public IntPtr PageTablePointer => _addressSpace.Pointer;
|
||||
|
||||
public MemoryManagerType Type => _unsafeMode ? MemoryManagerType.HostMappedUnsafe : MemoryManagerType.HostMapped;
|
||||
|
||||
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="unsafeMode">True if unmanaged access should not be masked (unsafe), false otherwise.</param>
|
||||
/// <param name="invalidAccessHandler">Optional function to handle invalid memory accesses</param>
|
||||
public MemoryManagerHostNoMirror(
|
||||
MemoryBlock addressSpace,
|
||||
MemoryBlock backingMemory,
|
||||
bool unsafeMode,
|
||||
InvalidAccessHandler invalidAccessHandler)
|
||||
{
|
||||
_addressSpace = addressSpace;
|
||||
_backingMemory = backingMemory;
|
||||
_pageTable = new PageTable<ulong>();
|
||||
_invalidAccessHandler = invalidAccessHandler;
|
||||
_unsafeMode = unsafeMode;
|
||||
AddressSpaceSize = addressSpace.Size;
|
||||
|
||||
ulong asSize = PageSize;
|
||||
int asBits = PageBits;
|
||||
|
||||
while (asSize < addressSpace.Size)
|
||||
{
|
||||
asSize <<= 1;
|
||||
asBits++;
|
||||
}
|
||||
|
||||
AddressSpaceBits = asBits;
|
||||
|
||||
_pages = new ManagedPageFlags(asBits);
|
||||
|
||||
Tracking = new MemoryTracking(this, (int)MemoryBlock.GetPageSize(), invalidAccessHandler);
|
||||
_memoryEh = new MemoryEhMeilleure(addressSpace, null, 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, va, size);
|
||||
_pages.AddMapping(va, size);
|
||||
PtMap(va, pa, size);
|
||||
|
||||
Tracking.Map(va, size);
|
||||
}
|
||||
|
||||
private void PtMap(ulong va, ulong pa, ulong size)
|
||||
{
|
||||
while (size != 0)
|
||||
{
|
||||
_pageTable.Map(va, pa);
|
||||
|
||||
va += PageSize;
|
||||
pa += PageSize;
|
||||
size -= PageSize;
|
||||
}
|
||||
}
|
||||
|
||||
/// <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, va, size);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
}
|
||||
|
||||
public ref T GetRef<T>(ulong va) where T : unmanaged
|
||||
{
|
||||
if (!IsContiguous(va, Unsafe.SizeOf<T>()))
|
||||
{
|
||||
ThrowMemoryNotContiguous();
|
||||
}
|
||||
|
||||
SignalMemoryTracking(va, (ulong)Unsafe.SizeOf<T>(), true);
|
||||
|
||||
return ref _backingMemory.GetRef<T>(GetPhysicalAddressChecked(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)
|
||||
{
|
||||
if (size == 0)
|
||||
{
|
||||
return Enumerable.Empty<HostMemoryRange>();
|
||||
}
|
||||
|
||||
var guestRegions = GetPhysicalRegionsImpl(va, size);
|
||||
if (guestRegions == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var regions = new HostMemoryRange[guestRegions.Count];
|
||||
|
||||
for (int i = 0; i < regions.Length; i++)
|
||||
{
|
||||
var guestRegion = guestRegions[i];
|
||||
IntPtr pointer = _backingMemory.GetPointer(guestRegion.Address, guestRegion.Size);
|
||||
regions[i] = new HostMemoryRange((nuint)(ulong)pointer, guestRegion.Size);
|
||||
}
|
||||
|
||||
return regions;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IEnumerable<MemoryRange> GetPhysicalRegions(ulong va, ulong size)
|
||||
{
|
||||
if (size == 0)
|
||||
{
|
||||
return Enumerable.Empty<MemoryRange>();
|
||||
}
|
||||
|
||||
return GetPhysicalRegionsImpl(va, size);
|
||||
}
|
||||
|
||||
private List<MemoryRange> GetPhysicalRegionsImpl(ulong va, ulong size)
|
||||
{
|
||||
if (!ValidateAddress(va) || !ValidateAddressAndSize(va, size))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
int pages = GetPagesCount(va, (uint)size, out va);
|
||||
|
||||
var regions = new List<MemoryRange>();
|
||||
|
||||
ulong regionStart = GetPhysicalAddressInternal(va);
|
||||
ulong regionSize = PageSize;
|
||||
|
||||
for (int page = 0; page < pages - 1; page++)
|
||||
{
|
||||
if (!ValidateAddress(va + PageSize))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
ulong newPa = GetPhysicalAddressInternal(va + PageSize);
|
||||
|
||||
if (GetPhysicalAddressInternal(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(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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes of resources used by the memory manager.
|
||||
/// </summary>
|
||||
protected override void Destroy()
|
||||
{
|
||||
_addressSpace.Dispose();
|
||||
_memoryEh.Dispose();
|
||||
}
|
||||
|
||||
protected override Memory<byte> GetPhysicalAddressMemory(nuint pa, int size)
|
||||
=> _backingMemory.GetMemory(pa, size);
|
||||
|
||||
protected override Span<byte> GetPhysicalAddressSpan(nuint pa, int size)
|
||||
=> _backingMemory.GetSpan(pa, size);
|
||||
|
||||
protected override nuint TranslateVirtualAddressChecked(ulong va)
|
||||
=> (nuint)GetPhysicalAddressChecked(va);
|
||||
|
||||
protected override nuint TranslateVirtualAddressUnchecked(ulong va)
|
||||
=> (nuint)GetPhysicalAddressInternal(va);
|
||||
}
|
||||
}
|
@ -3,7 +3,6 @@ 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;
|
||||
@ -15,10 +14,7 @@ namespace Ryujinx.Cpu.Nce
|
||||
/// </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;
|
||||
@ -30,8 +26,6 @@ namespace Ryujinx.Cpu.Nce
|
||||
/// <inheritdoc/>
|
||||
public bool UsesPrivateAllocations => false;
|
||||
|
||||
public int AddressSpaceBits { get; }
|
||||
|
||||
public IntPtr PageTablePointer => IntPtr.Zero;
|
||||
|
||||
public ulong ReservedSize => (ulong)_addressSpace.Pointer.ToInt64();
|
||||
@ -42,24 +36,25 @@ namespace Ryujinx.Cpu.Nce
|
||||
|
||||
public event Action<ulong, ulong> UnmapEvent;
|
||||
|
||||
public int AddressSpaceBits { get; }
|
||||
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="addressSpace">Address space memory block</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 addressSpace,
|
||||
MemoryBlock backingMemory,
|
||||
ulong addressSpaceSize,
|
||||
InvalidAccessHandler invalidAccessHandler = null)
|
||||
{
|
||||
_backingMemory = backingMemory;
|
||||
_pageTable = new PageTable<ulong>();
|
||||
_invalidAccessHandler = invalidAccessHandler;
|
||||
AddressSpaceSize = addressSpaceSize;
|
||||
|
||||
ulong asSize = PageSize;
|
||||
int asBits = PageBits;
|
||||
@ -71,43 +66,39 @@ namespace Ryujinx.Cpu.Nce
|
||||
}
|
||||
|
||||
AddressSpaceBits = asBits;
|
||||
AddressSpaceSize = addressSpace.AddressSpaceSize;
|
||||
|
||||
_pages = new ManagedPageFlags(AddressSpaceBits);
|
||||
_pages = new ManagedPageFlags(asBits);
|
||||
|
||||
_addressSpace = addressSpace.Base;
|
||||
_addressSpaceMirror = addressSpace.Mirror;
|
||||
_addressSpace = addressSpace;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
private void PtMap(ulong va, ulong pa, ulong size)
|
||||
{
|
||||
while (size != 0)
|
||||
{
|
||||
_pageTable.Map(va, pa);
|
||||
|
||||
va += PageSize;
|
||||
pa += PageSize;
|
||||
size -= PageSize;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Unmap(ulong va, ulong size)
|
||||
{
|
||||
@ -119,19 +110,6 @@ namespace Ryujinx.Cpu.Nce
|
||||
_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)
|
||||
@ -151,193 +129,16 @@ namespace Ryujinx.Cpu.Nce
|
||||
_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
|
||||
{
|
||||
if (!IsContiguous(va, Unsafe.SizeOf<T>()))
|
||||
{
|
||||
ThrowMemoryNotContiguous();
|
||||
}
|
||||
|
||||
SignalMemoryTracking(va, (ulong)Unsafe.SizeOf<T>(), true);
|
||||
|
||||
return ref _addressSpaceMirror.GetRef<T>(AddressToOffset(va));
|
||||
return ref _backingMemory.GetRef<T>(GetPhysicalAddressChecked(va));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@ -357,19 +158,52 @@ namespace Ryujinx.Cpu.Nce
|
||||
/// <inheritdoc/>
|
||||
public IEnumerable<HostMemoryRange> GetHostRegions(ulong va, ulong size)
|
||||
{
|
||||
AssertValidAddressAndSize(va, size);
|
||||
if (size == 0)
|
||||
{
|
||||
return Enumerable.Empty<HostMemoryRange>();
|
||||
}
|
||||
|
||||
return Enumerable.Repeat(new HostMemoryRange((nuint)(ulong)_addressSpaceMirror.GetPointer(AddressToOffset(va), size), size), 1);
|
||||
var guestRegions = GetPhysicalRegionsImpl(va, size);
|
||||
if (guestRegions == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var regions = new HostMemoryRange[guestRegions.Count];
|
||||
|
||||
for (int i = 0; i < regions.Length; i++)
|
||||
{
|
||||
var guestRegion = guestRegions[i];
|
||||
IntPtr pointer = _backingMemory.GetPointer(guestRegion.Address, guestRegion.Size);
|
||||
regions[i] = new HostMemoryRange((nuint)(ulong)pointer, guestRegion.Size);
|
||||
}
|
||||
|
||||
return regions;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IEnumerable<MemoryRange> GetPhysicalRegions(ulong va, ulong size)
|
||||
{
|
||||
if (size == 0)
|
||||
{
|
||||
return Enumerable.Empty<MemoryRange>();
|
||||
}
|
||||
|
||||
return GetPhysicalRegionsImpl(va, size);
|
||||
}
|
||||
|
||||
private List<MemoryRange> GetPhysicalRegionsImpl(ulong va, ulong size)
|
||||
{
|
||||
if (!ValidateAddress(va) || !ValidateAddressAndSize(va, size))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
int pages = GetPagesCount(va, (uint)size, out va);
|
||||
|
||||
var regions = new List<MemoryRange>();
|
||||
|
||||
ulong regionStart = GetPhysicalAddressChecked(va);
|
||||
ulong regionStart = GetPhysicalAddressInternal(va);
|
||||
ulong regionSize = PageSize;
|
||||
|
||||
for (int page = 0; page < pages - 1; page++)
|
||||
@ -379,9 +213,9 @@ namespace Ryujinx.Cpu.Nce
|
||||
return null;
|
||||
}
|
||||
|
||||
ulong newPa = GetPhysicalAddressChecked(va + PageSize);
|
||||
ulong newPa = GetPhysicalAddressInternal(va + PageSize);
|
||||
|
||||
if (GetPhysicalAddressChecked(va) + PageSize != newPa)
|
||||
if (GetPhysicalAddressInternal(va) + PageSize != newPa)
|
||||
{
|
||||
regions.Add(new MemoryRange(regionStart, regionSize));
|
||||
regionStart = newPa;
|
||||
@ -476,15 +310,14 @@ namespace Ryujinx.Cpu.Nce
|
||||
protected override void Destroy()
|
||||
{
|
||||
_addressSpace.Dispose();
|
||||
_addressSpaceMirror.Dispose();
|
||||
_memoryEh.Dispose();
|
||||
}
|
||||
|
||||
protected override Memory<byte> GetPhysicalAddressMemory(nuint pa, int size)
|
||||
=> _addressSpaceMirror.GetMemory(pa, size);
|
||||
=> _backingMemory.GetMemory(pa, size);
|
||||
|
||||
protected override Span<byte> GetPhysicalAddressSpan(nuint pa, int size)
|
||||
=> _addressSpaceMirror.GetSpan(pa, size);
|
||||
=> _backingMemory.GetSpan(pa, size);
|
||||
|
||||
protected override nuint TranslateVirtualAddressChecked(ulong va)
|
||||
=> (nuint)GetPhysicalAddressChecked(va);
|
||||
|
@ -1603,42 +1603,36 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
DynamicState.ReplayIfDirty(Gd, CommandBuffer);
|
||||
|
||||
// Setting graphics state with a compute pipeline bound crashes the Adreno driver.
|
||||
if (pbp == PipelineBindPoint.Graphics)
|
||||
if (_needsIndexBufferRebind && _indexBufferPattern == null)
|
||||
{
|
||||
DynamicState.ReplayIfDirty(Gd.Api, CommandBuffer);
|
||||
_indexBuffer.BindIndexBuffer(Gd, Cbs);
|
||||
_needsIndexBufferRebind = false;
|
||||
}
|
||||
|
||||
if (_needsIndexBufferRebind && _indexBufferPattern == null)
|
||||
if (_needsTransformFeedbackBuffersRebind)
|
||||
{
|
||||
PauseTransformFeedbackInternal();
|
||||
|
||||
for (int i = 0; i < Constants.MaxTransformFeedbackBuffers; i++)
|
||||
{
|
||||
_indexBuffer.BindIndexBuffer(Gd, Cbs);
|
||||
_needsIndexBufferRebind = false;
|
||||
_transformFeedbackBuffers[i].BindTransformFeedbackBuffer(Gd, Cbs, (uint)i);
|
||||
}
|
||||
|
||||
if (_needsTransformFeedbackBuffersRebind)
|
||||
_needsTransformFeedbackBuffersRebind = false;
|
||||
}
|
||||
|
||||
if (_vertexBuffersDirty != 0)
|
||||
{
|
||||
while (_vertexBuffersDirty != 0)
|
||||
{
|
||||
PauseTransformFeedbackInternal();
|
||||
int i = BitOperations.TrailingZeroCount(_vertexBuffersDirty);
|
||||
|
||||
for (int i = 0; i < Constants.MaxTransformFeedbackBuffers; i++)
|
||||
{
|
||||
_transformFeedbackBuffers[i].BindTransformFeedbackBuffer(Gd, Cbs, (uint)i);
|
||||
}
|
||||
_vertexBuffers[i].BindVertexBuffer(Gd, Cbs, (uint)i, ref _newState, _vertexBufferUpdater);
|
||||
|
||||
_needsTransformFeedbackBuffersRebind = false;
|
||||
_vertexBuffersDirty &= ~(1UL << i);
|
||||
}
|
||||
|
||||
if (_vertexBuffersDirty != 0)
|
||||
{
|
||||
while (_vertexBuffersDirty != 0)
|
||||
{
|
||||
int i = BitOperations.TrailingZeroCount(_vertexBuffersDirty);
|
||||
|
||||
_vertexBuffers[i].BindVertexBuffer(Gd, Cbs, (uint)i, ref _newState, _vertexBufferUpdater);
|
||||
|
||||
_vertexBuffersDirty &= ~(1UL << i);
|
||||
}
|
||||
|
||||
_vertexBufferUpdater.Commit(Cbs);
|
||||
}
|
||||
_vertexBufferUpdater.Commit(Cbs);
|
||||
}
|
||||
|
||||
if (_bindingBarriersDirty)
|
||||
|
@ -57,7 +57,6 @@ 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;
|
||||
|
||||
@ -71,16 +70,16 @@ namespace Ryujinx.HLE.HOS
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!AddressSpace.TryCreate(context.Memory, addressSpaceSize, out addressSpace))
|
||||
if (!AddressSpace.TryCreateWithoutMirror(addressSpaceSize, out var 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}");
|
||||
Logger.Info?.Print(LogClass.Cpu, $"NCE Base AS Address: 0x{addressSpace.Pointer.ToInt64():X} Size: 0x{addressSpace.Size: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);
|
||||
processContext = new ArmProcessContext<MemoryManagerNative>(pid, cpuEngine, _gpu, memoryManager, addressSpace.Size, for64Bit, memoryManager.ReservedSize);
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -98,10 +97,14 @@ namespace Ryujinx.HLE.HOS
|
||||
? new LightningJitEngine(_tickSource)
|
||||
: new JitEngine(_tickSource);
|
||||
|
||||
AddressSpace addressSpace = null;
|
||||
MemoryBlock asNoMirror = 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)
|
||||
{
|
||||
if (!AddressSpace.TryCreate(context.Memory, addressSpaceSize, out addressSpace))
|
||||
if (!AddressSpace.TryCreate(context.Memory, addressSpaceSize, out addressSpace) &&
|
||||
!AddressSpace.TryCreateWithoutMirror(addressSpaceSize, out asNoMirror))
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Cpu, "Address space creation failed, falling back to software page table");
|
||||
|
||||
@ -112,13 +115,15 @@ namespace Ryujinx.HLE.HOS
|
||||
switch (mode)
|
||||
{
|
||||
case MemoryManagerMode.SoftwarePageTable:
|
||||
var memoryManager = new MemoryManager(context.Memory, addressSpaceSize, invalidAccessHandler);
|
||||
processContext = new ArmProcessContext<MemoryManager>(pid, cpuEngine, _gpu, memoryManager, addressSpaceSize, for64Bit);
|
||||
{
|
||||
var mm = new MemoryManager(context.Memory, addressSpaceSize, invalidAccessHandler);
|
||||
processContext = new ArmProcessContext<MemoryManager>(pid, cpuEngine, _gpu, mm, addressSpaceSize, for64Bit);
|
||||
}
|
||||
break;
|
||||
|
||||
case MemoryManagerMode.HostMapped:
|
||||
case MemoryManagerMode.HostMappedUnsafe:
|
||||
if (addressSpace == null)
|
||||
if (addressSpace == null && asNoMirror == null)
|
||||
{
|
||||
var memoryManagerHostTracked = new MemoryManagerHostTracked(context.Memory, addressSpaceSize, mode == MemoryManagerMode.HostMappedUnsafe, invalidAccessHandler);
|
||||
processContext = new ArmProcessContext<MemoryManagerHostTracked>(pid, cpuEngine, _gpu, memoryManagerHostTracked, addressSpaceSize, for64Bit);
|
||||
@ -130,14 +135,29 @@ namespace Ryujinx.HLE.HOS
|
||||
Logger.Warning?.Print(LogClass.Emulation, $"Allocated address space (0x{addressSpace.AddressSpaceSize:X}) is smaller than guest application requirements (0x{addressSpaceSize:X})");
|
||||
}
|
||||
|
||||
var memoryManagerHostMapped = new MemoryManagerHostMapped(addressSpace, mode == MemoryManagerMode.HostMappedUnsafe, invalidAccessHandler);
|
||||
processContext = new ArmProcessContext<MemoryManagerHostMapped>(pid, cpuEngine, _gpu, memoryManagerHostMapped, addressSpace.AddressSpaceSize, for64Bit);
|
||||
bool unsafeMode = mode == MemoryManagerMode.HostMappedUnsafe;
|
||||
|
||||
if (addressSpace != null)
|
||||
{
|
||||
var mm = new MemoryManagerHostMapped(addressSpace, unsafeMode, invalidAccessHandler);
|
||||
processContext = new ArmProcessContext<MemoryManagerHostMapped>(pid, cpuEngine, _gpu, mm, addressSpace.AddressSpaceSize, for64Bit);
|
||||
}
|
||||
else
|
||||
{
|
||||
var mm = new MemoryManagerHostNoMirror(asNoMirror, context.Memory, unsafeMode, invalidAccessHandler);
|
||||
processContext = new ArmProcessContext<MemoryManagerHostNoMirror>(pid, cpuEngine, _gpu, mm, asNoMirror.Size, for64Bit);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new InvalidOperationException($"{nameof(mode)} contains an invalid value: {mode}");
|
||||
}
|
||||
|
||||
if (addressSpaceSize != processContext.AddressSpaceSize)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Emulation, $"Allocated address space (0x{processContext.AddressSpaceSize:X}) is smaller than guest application requirements (0x{addressSpaceSize:X})");
|
||||
}
|
||||
}
|
||||
|
||||
DiskCacheLoadState = processContext.Initialize(_titleIdText, _displayVersion, _diskCacheEnabled, _codeAddress, _codeSize);
|
||||
|
@ -221,11 +221,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
break;
|
||||
|
||||
case ProcessCreationFlags.AddressSpace64Bit:
|
||||
ulong reservedAddressSpaceSize = _reservedAddressSpaceSize;
|
||||
if (_reservedAddressSpaceSize < addrSpaceEnd)
|
||||
{
|
||||
int addressSpaceWidth = (int)ulong.Log2(_reservedAddressSpaceSize);
|
||||
int addressSpaceWidth = (int)ulong.Log2(reservedAddressSpaceSize);
|
||||
|
||||
aliasRegion.Size = 1UL << (addressSpaceWidth - 3);
|
||||
aliasRegion.Size = reservedAddressSpaceSize >= 0x1800000000 ? 0x1000000000 : 1UL << (addressSpaceWidth - 3);
|
||||
heapRegion.Size = 0x180000000;
|
||||
stackRegion.Size = 1UL << (addressSpaceWidth - 8);
|
||||
tlsIoRegion.Size = 1UL << (addressSpaceWidth - 3);
|
||||
@ -234,7 +235,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
stackAndTlsIoStart = 0;
|
||||
stackAndTlsIoEnd = 0;
|
||||
AslrRegionStart = Math.Max(reservedSize, 0x8000000);
|
||||
addrSpaceEnd = reservedSize + (1UL << addressSpaceWidth);
|
||||
addrSpaceEnd = reservedSize + reservedAddressSpaceSize;
|
||||
AslrRegionEnd = addrSpaceEnd;
|
||||
}
|
||||
else
|
||||
|
@ -1,4 +1,4 @@
|
||||
using LibHac.Account;
|
||||
using LibHac.Account;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.Fsa;
|
||||
@ -252,7 +252,7 @@ namespace Ryujinx.HLE.Loaders.Processes
|
||||
ulong argsStart = 0;
|
||||
uint argsSize = 0;
|
||||
ulong codeStart = ((meta.Flags & 1) != 0 ? 0x8000000UL : 0x200000UL) + CodeStartOffset;
|
||||
uint codeSize = 0;
|
||||
ulong codeSize = 0;
|
||||
|
||||
var buildIds = executables.Select(e => (e switch
|
||||
{
|
||||
|
Reference in New Issue
Block a user