Support memory aliasing (#2954)
* Back to the origins: Make memory manager take guest PA rather than host address once again * Direct mapping with alias support on Windows * Fixes and remove more of the emulated shared memory * Linux support * Make shared and transfer memory not depend on SharedMemoryStorage * More efficient view mapping on Windows (no more restricted to 4KB pages at a time) * Handle potential access violations caused by partial unmap * Implement host mapping using shared memory on Linux * Add new GetPhysicalAddressChecked method, used to ensure the virtual address is mapped before address translation Also align GetRef behaviour with software memory manager * We don't need a mirrorable memory block for software memory manager mode * Disable memory aliasing tests while we don't have shared memory support on Mac * Shared memory & SIGBUS handler for macOS * Fix typo + nits + re-enable memory tests * Set MAP_JIT_DARWIN on x86 Mac too * Add back the address space mirror * Only set MAP_JIT_DARWIN if we are mapping as executable * Disable aliasing tests again (still fails on Mac) * Fix UnmapView4KB (by not casting size to int) * Use ref counting on memory blocks to delay closing the shared memory handle until all blocks using it are disposed * Address PR feedback * Make RO hold a reference to the guest process memory manager to avoid early disposal Co-authored-by: nastys <nastys@users.noreply.github.com>
This commit is contained in:
parent
4a892fbdc9
commit
95017b8c66
41 changed files with 2373 additions and 2155 deletions
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
|
||||
|
@ -11,8 +12,12 @@ namespace Ryujinx.Memory
|
|||
{
|
||||
private readonly bool _usesSharedMemory;
|
||||
private readonly bool _isMirror;
|
||||
private readonly bool _viewCompatible;
|
||||
private readonly bool _forceWindows4KBView;
|
||||
private IntPtr _sharedMemory;
|
||||
private IntPtr _pointer;
|
||||
private ConcurrentDictionary<MemoryBlock, byte> _viewStorages;
|
||||
private int _viewCount;
|
||||
|
||||
/// <summary>
|
||||
/// Pointer to the memory block data.
|
||||
|
@ -36,12 +41,14 @@ namespace Ryujinx.Memory
|
|||
if (flags.HasFlag(MemoryAllocationFlags.Mirrorable))
|
||||
{
|
||||
_sharedMemory = MemoryManagement.CreateSharedMemory(size, flags.HasFlag(MemoryAllocationFlags.Reserve));
|
||||
_pointer = MemoryManagement.MapSharedMemory(_sharedMemory);
|
||||
_pointer = MemoryManagement.MapSharedMemory(_sharedMemory, size);
|
||||
_usesSharedMemory = true;
|
||||
}
|
||||
else if (flags.HasFlag(MemoryAllocationFlags.Reserve))
|
||||
{
|
||||
_pointer = MemoryManagement.Reserve(size);
|
||||
_viewCompatible = flags.HasFlag(MemoryAllocationFlags.ViewCompatible);
|
||||
_forceWindows4KBView = flags.HasFlag(MemoryAllocationFlags.ForceWindows4KBViewMapping);
|
||||
_pointer = MemoryManagement.Reserve(size, _viewCompatible);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -49,6 +56,10 @@ namespace Ryujinx.Memory
|
|||
}
|
||||
|
||||
Size = size;
|
||||
|
||||
_viewStorages = new ConcurrentDictionary<MemoryBlock, byte>();
|
||||
_viewStorages.TryAdd(this, 0);
|
||||
_viewCount = 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -60,7 +71,7 @@ namespace Ryujinx.Memory
|
|||
/// <exception cref="PlatformNotSupportedException">Throw when the current platform is not supported</exception>
|
||||
private MemoryBlock(ulong size, IntPtr sharedMemory)
|
||||
{
|
||||
_pointer = MemoryManagement.MapSharedMemory(sharedMemory);
|
||||
_pointer = MemoryManagement.MapSharedMemory(sharedMemory, size);
|
||||
Size = size;
|
||||
_usesSharedMemory = true;
|
||||
_isMirror = true;
|
||||
|
@ -112,6 +123,42 @@ namespace Ryujinx.Memory
|
|||
return MemoryManagement.Decommit(GetPointerInternal(offset, size), size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps a view of memory from another memory block.
|
||||
/// </summary>
|
||||
/// <param name="srcBlock">Memory block from where the backing memory will be taken</param>
|
||||
/// <param name="srcOffset">Offset on <paramref name="srcBlock"/> of the region that should be mapped</param>
|
||||
/// <param name="dstOffset">Offset to map the view into on this block</param>
|
||||
/// <param name="size">Size of the range to be mapped</param>
|
||||
/// <exception cref="NotSupportedException">Throw when the source memory block does not support mirroring</exception>
|
||||
/// <exception cref="ObjectDisposedException">Throw when the memory block has already been disposed</exception>
|
||||
/// <exception cref="InvalidMemoryRegionException">Throw when either <paramref name="offset"/> or <paramref name="size"/> are out of range</exception>
|
||||
public void MapView(MemoryBlock srcBlock, ulong srcOffset, ulong dstOffset, ulong size)
|
||||
{
|
||||
if (srcBlock._sharedMemory == IntPtr.Zero)
|
||||
{
|
||||
throw new ArgumentException("The source memory block is not mirrorable, and thus cannot be mapped on the current block.");
|
||||
}
|
||||
|
||||
if (_viewStorages.TryAdd(srcBlock, 0))
|
||||
{
|
||||
srcBlock.IncrementViewCount();
|
||||
}
|
||||
|
||||
MemoryManagement.MapView(srcBlock._sharedMemory, srcOffset, GetPointerInternal(dstOffset, size), size, _forceWindows4KBView);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unmaps a view of memory from another memory block.
|
||||
/// </summary>
|
||||
/// <param name="srcBlock">Memory block from where the backing memory was taken during map</param>
|
||||
/// <param name="offset">Offset of the view previously mapped with <see cref="MapView"/></param>
|
||||
/// <param name="size">Size of the range to be unmapped</param>
|
||||
public void UnmapView(MemoryBlock srcBlock, ulong offset, ulong size)
|
||||
{
|
||||
MemoryManagement.UnmapView(srcBlock._sharedMemory, GetPointerInternal(offset, size), size, _forceWindows4KBView);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reprotects a region of memory.
|
||||
/// </summary>
|
||||
|
@ -124,21 +171,7 @@ namespace Ryujinx.Memory
|
|||
/// <exception cref="MemoryProtectionException">Throw when <paramref name="permission"/> is invalid</exception>
|
||||
public void Reprotect(ulong offset, ulong size, MemoryPermission permission, bool throwOnFail = true)
|
||||
{
|
||||
MemoryManagement.Reprotect(GetPointerInternal(offset, size), size, permission, throwOnFail);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remaps a region of memory into this memory block.
|
||||
/// </summary>
|
||||
/// <param name="offset">Starting offset of the range to be remapped into</param>
|
||||
/// <param name="sourceAddress">Starting offset of the range to be remapped from</param>
|
||||
/// <param name="size">Size of the range to be remapped</param>
|
||||
/// <exception cref="ObjectDisposedException">Throw when the memory block has already been disposed</exception>
|
||||
/// <exception cref="InvalidMemoryRegionException">Throw when either <paramref name="offset"/> or <paramref name="size"/> are out of range</exception>
|
||||
/// <exception cref="MemoryProtectionException">Throw when <paramref name="permission"/> is invalid</exception>
|
||||
public void Remap(ulong offset, IntPtr sourceAddress, ulong size)
|
||||
{
|
||||
MemoryManagement.Remap(GetPointerInternal(offset, size), sourceAddress, size);
|
||||
MemoryManagement.Reprotect(GetPointerInternal(offset, size), size, permission, _viewCompatible, _forceWindows4KBView, throwOnFail);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -274,7 +307,7 @@ namespace Ryujinx.Memory
|
|||
/// <exception cref="ObjectDisposedException">Throw when the memory block has already been disposed</exception>
|
||||
/// <exception cref="InvalidMemoryRegionException">Throw when either <paramref name="offset"/> or <paramref name="size"/> are out of range</exception>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public nuint GetPointer(ulong offset, ulong size) => (nuint)(ulong)GetPointerInternal(offset, size);
|
||||
public IntPtr GetPointer(ulong offset, ulong size) => GetPointerInternal(offset, size);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private IntPtr GetPointerInternal(ulong offset, ulong size)
|
||||
|
@ -367,22 +400,63 @@ namespace Ryujinx.Memory
|
|||
{
|
||||
if (_usesSharedMemory)
|
||||
{
|
||||
MemoryManagement.UnmapSharedMemory(ptr);
|
||||
|
||||
if (_sharedMemory != IntPtr.Zero && !_isMirror)
|
||||
{
|
||||
MemoryManagement.DestroySharedMemory(_sharedMemory);
|
||||
_sharedMemory = IntPtr.Zero;
|
||||
}
|
||||
MemoryManagement.UnmapSharedMemory(ptr, Size);
|
||||
}
|
||||
else
|
||||
{
|
||||
MemoryManagement.Free(ptr);
|
||||
}
|
||||
|
||||
foreach (MemoryBlock viewStorage in _viewStorages.Keys)
|
||||
{
|
||||
viewStorage.DecrementViewCount();
|
||||
}
|
||||
|
||||
_viewStorages.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private void ThrowObjectDisposed() => throw new ObjectDisposedException(nameof(MemoryBlock));
|
||||
private void ThrowInvalidMemoryRegionException() => throw new InvalidMemoryRegionException();
|
||||
/// <summary>
|
||||
/// Increments the number of views that uses this memory block as storage.
|
||||
/// </summary>
|
||||
private void IncrementViewCount()
|
||||
{
|
||||
Interlocked.Increment(ref _viewCount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrements the number of views that uses this memory block as storage.
|
||||
/// </summary>
|
||||
private void DecrementViewCount()
|
||||
{
|
||||
if (Interlocked.Decrement(ref _viewCount) == 0 && _sharedMemory != IntPtr.Zero && !_isMirror)
|
||||
{
|
||||
MemoryManagement.DestroySharedMemory(_sharedMemory);
|
||||
_sharedMemory = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the specified memory allocation flags are supported on the current platform.
|
||||
/// </summary>
|
||||
/// <param name="flags">Flags to be checked</param>
|
||||
/// <returns>True if the platform supports all the flags, false otherwise</returns>
|
||||
public static bool SupportsFlags(MemoryAllocationFlags flags)
|
||||
{
|
||||
if (flags.HasFlag(MemoryAllocationFlags.ViewCompatible))
|
||||
{
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
return OperatingSystem.IsWindowsVersionAtLeast(10, 0, 17134);
|
||||
}
|
||||
|
||||
return OperatingSystem.IsLinux() || OperatingSystem.IsMacOS();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void ThrowObjectDisposed() => throw new ObjectDisposedException(nameof(MemoryBlock));
|
||||
private static void ThrowInvalidMemoryRegionException() => throw new InvalidMemoryRegionException();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue