aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Memory/MemoryManagementWindows.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Ryujinx.Memory/MemoryManagementWindows.cs')
-rw-r--r--Ryujinx.Memory/MemoryManagementWindows.cs311
1 files changed, 111 insertions, 200 deletions
diff --git a/Ryujinx.Memory/MemoryManagementWindows.cs b/Ryujinx.Memory/MemoryManagementWindows.cs
index 48616ec3..1885376c 100644
--- a/Ryujinx.Memory/MemoryManagementWindows.cs
+++ b/Ryujinx.Memory/MemoryManagementWindows.cs
@@ -1,7 +1,5 @@
using Ryujinx.Memory.WindowsShared;
using System;
-using System.Collections.Generic;
-using System.Runtime.InteropServices;
using System.Runtime.Versioning;
namespace Ryujinx.Memory
@@ -9,74 +7,30 @@ namespace Ryujinx.Memory
[SupportedOSPlatform("windows")]
static class MemoryManagementWindows
{
- private static readonly IntPtr InvalidHandleValue = new IntPtr(-1);
- private static bool UseWin10Placeholders;
-
- private static object _emulatedHandleLock = new object();
- private static EmulatedSharedMemoryWindows[] _emulatedShared = new EmulatedSharedMemoryWindows[64];
- private static List<EmulatedSharedMemoryWindows> _emulatedSharedList = new List<EmulatedSharedMemoryWindows>();
-
- [DllImport("kernel32.dll", SetLastError = true)]
- private static extern IntPtr VirtualAlloc(
- IntPtr lpAddress,
- IntPtr dwSize,
- AllocationType flAllocationType,
- MemoryProtection flProtect);
-
- [DllImport("kernel32.dll", SetLastError = true)]
- private static extern bool VirtualProtect(
- IntPtr lpAddress,
- IntPtr dwSize,
- MemoryProtection flNewProtect,
- out MemoryProtection lpflOldProtect);
-
- [DllImport("kernel32.dll", SetLastError = true)]
- private static extern bool VirtualFree(IntPtr lpAddress, IntPtr dwSize, AllocationType dwFreeType);
-
- [DllImport("kernel32.dll", SetLastError = true)]
- private static extern IntPtr CreateFileMapping(
- IntPtr hFile,
- IntPtr lpFileMappingAttributes,
- FileMapProtection flProtect,
- uint dwMaximumSizeHigh,
- uint dwMaximumSizeLow,
- [MarshalAs(UnmanagedType.LPWStr)] string lpName);
-
- [DllImport("kernel32.dll", SetLastError = true)]
- private static extern bool CloseHandle(IntPtr hObject);
-
- [DllImport("kernel32.dll", SetLastError = true)]
- private static extern IntPtr MapViewOfFile(
- IntPtr hFileMappingObject,
- uint dwDesiredAccess,
- uint dwFileOffsetHigh,
- uint dwFileOffsetLow,
- IntPtr dwNumberOfBytesToMap);
-
- [DllImport("kernel32.dll", SetLastError = true)]
- private static extern bool UnmapViewOfFile(IntPtr lpBaseAddress);
-
- [DllImport("kernel32.dll", SetLastError = true)]
- private static extern uint GetLastError();
-
- static MemoryManagementWindows()
- {
- UseWin10Placeholders = OperatingSystem.IsWindowsVersionAtLeast(10, 0, 17134);
- }
+ private const int PageSize = 0x1000;
+
+ private static readonly PlaceholderManager _placeholders = new PlaceholderManager();
public static IntPtr Allocate(IntPtr size)
{
return AllocateInternal(size, AllocationType.Reserve | AllocationType.Commit);
}
- public static IntPtr Reserve(IntPtr size)
+ public static IntPtr Reserve(IntPtr size, bool viewCompatible)
{
+ if (viewCompatible)
+ {
+ IntPtr baseAddress = AllocateInternal2(size, AllocationType.Reserve | AllocationType.ReservePlaceholder);
+ _placeholders.ReserveRange((ulong)baseAddress, (ulong)size);
+ return baseAddress;
+ }
+
return AllocateInternal(size, AllocationType.Reserve);
}
private static IntPtr AllocateInternal(IntPtr size, AllocationType flags = 0)
{
- IntPtr ptr = VirtualAlloc(IntPtr.Zero, size, flags, MemoryProtection.ReadWrite);
+ IntPtr ptr = WindowsApi.VirtualAlloc(IntPtr.Zero, size, flags, MemoryProtection.ReadWrite);
if (ptr == IntPtr.Zero)
{
@@ -86,187 +40,153 @@ namespace Ryujinx.Memory
return ptr;
}
- public static bool Commit(IntPtr location, IntPtr size)
+ private static IntPtr AllocateInternal2(IntPtr size, AllocationType flags = 0)
{
- if (UseWin10Placeholders)
+ IntPtr ptr = WindowsApi.VirtualAlloc2(WindowsApi.CurrentProcessHandle, IntPtr.Zero, size, flags, MemoryProtection.NoAccess, IntPtr.Zero, 0);
+
+ if (ptr == IntPtr.Zero)
{
- lock (_emulatedSharedList)
- {
- foreach (var shared in _emulatedSharedList)
- {
- if (shared.CommitMap(location, size))
- {
- return true;
- }
- }
- }
+ throw new OutOfMemoryException();
}
- return VirtualAlloc(location, size, AllocationType.Commit, MemoryProtection.ReadWrite) != IntPtr.Zero;
+ return ptr;
+ }
+
+ public static bool Commit(IntPtr location, IntPtr size)
+ {
+ return WindowsApi.VirtualAlloc(location, size, AllocationType.Commit, MemoryProtection.ReadWrite) != IntPtr.Zero;
}
public static bool Decommit(IntPtr location, IntPtr size)
{
- if (UseWin10Placeholders)
- {
- lock (_emulatedSharedList)
- {
- foreach (var shared in _emulatedSharedList)
- {
- if (shared.DecommitMap(location, size))
- {
- return true;
- }
- }
- }
- }
+ return WindowsApi.VirtualFree(location, size, AllocationType.Decommit);
+ }
- return VirtualFree(location, size, AllocationType.Decommit);
+ public static void MapView(IntPtr sharedMemory, ulong srcOffset, IntPtr location, IntPtr size)
+ {
+ _placeholders.MapView(sharedMemory, srcOffset, location, size);
}
- public static bool Reprotect(IntPtr address, IntPtr size, MemoryPermission permission)
+ public static void MapView4KB(IntPtr sharedMemory, ulong srcOffset, IntPtr location, IntPtr size)
{
- if (UseWin10Placeholders)
- {
- ulong uaddress = (ulong)address;
- ulong usize = (ulong)size;
- while (usize > 0)
- {
- ulong nextGranular = (uaddress & ~EmulatedSharedMemoryWindows.MappingMask) + EmulatedSharedMemoryWindows.MappingGranularity;
- ulong mapSize = Math.Min(usize, nextGranular - uaddress);
+ ulong uaddress = (ulong)location;
+ ulong usize = (ulong)size;
+ IntPtr endLocation = (IntPtr)(uaddress + usize);
- if (!VirtualProtect((IntPtr)uaddress, (IntPtr)mapSize, GetProtection(permission), out _))
- {
- return false;
- }
+ while (location != endLocation)
+ {
+ WindowsApi.VirtualFree(location, (IntPtr)PageSize, AllocationType.Release | AllocationType.PreservePlaceholder);
+
+ var ptr = WindowsApi.MapViewOfFile3(
+ sharedMemory,
+ WindowsApi.CurrentProcessHandle,
+ location,
+ srcOffset,
+ (IntPtr)PageSize,
+ 0x4000,
+ MemoryProtection.ReadWrite,
+ IntPtr.Zero,
+ 0);
- uaddress = nextGranular;
- usize -= mapSize;
+ if (ptr == IntPtr.Zero)
+ {
+ throw new WindowsApiException("MapViewOfFile3");
}
- return true;
- }
- else
- {
- return VirtualProtect(address, size, GetProtection(permission), out _);
+ location += PageSize;
+ srcOffset += PageSize;
}
}
- private static MemoryProtection GetProtection(MemoryPermission permission)
+ public static void UnmapView(IntPtr sharedMemory, IntPtr location, IntPtr size)
{
- return permission switch
- {
- MemoryPermission.None => MemoryProtection.NoAccess,
- MemoryPermission.Read => MemoryProtection.ReadOnly,
- MemoryPermission.ReadAndWrite => MemoryProtection.ReadWrite,
- MemoryPermission.ReadAndExecute => MemoryProtection.ExecuteRead,
- MemoryPermission.ReadWriteExecute => MemoryProtection.ExecuteReadWrite,
- MemoryPermission.Execute => MemoryProtection.Execute,
- _ => throw new MemoryProtectionException(permission)
- };
- }
-
- public static bool Free(IntPtr address)
- {
- return VirtualFree(address, IntPtr.Zero, AllocationType.Release);
+ _placeholders.UnmapView(sharedMemory, location, size);
}
- private static int GetEmulatedHandle()
+ public static void UnmapView4KB(IntPtr location, IntPtr size)
{
- // Assumes we have the handle lock.
+ ulong uaddress = (ulong)location;
+ ulong usize = (ulong)size;
+ IntPtr endLocation = (IntPtr)(uaddress + usize);
- for (int i = 0; i < _emulatedShared.Length; i++)
+ while (location != endLocation)
{
- if (_emulatedShared[i] == null)
+ bool result = WindowsApi.UnmapViewOfFile2(WindowsApi.CurrentProcessHandle, location, 2);
+ if (!result)
{
- return i + 1;
+ throw new WindowsApiException("UnmapViewOfFile2");
}
- }
- throw new InvalidProgramException("Too many shared memory handles were created.");
+ location += PageSize;
+ }
}
- public static bool EmulatedHandleValid(ref int handle)
+ public static bool Reprotect(IntPtr address, IntPtr size, MemoryPermission permission, bool forView)
{
- handle--;
- return handle >= 0 && handle < _emulatedShared.Length && _emulatedShared[handle] != null;
- }
-
- public static IntPtr CreateSharedMemory(IntPtr size, bool reserve)
- {
- if (UseWin10Placeholders && reserve)
+ if (forView)
{
- lock (_emulatedHandleLock)
- {
- int handle = GetEmulatedHandle();
- _emulatedShared[handle - 1] = new EmulatedSharedMemoryWindows((ulong)size);
- _emulatedSharedList.Add(_emulatedShared[handle - 1]);
-
- return (IntPtr)handle;
- }
+ return _placeholders.ReprotectView(address, size, permission);
}
else
{
- var prot = reserve ? FileMapProtection.SectionReserve : FileMapProtection.SectionCommit;
-
- IntPtr handle = CreateFileMapping(
- InvalidHandleValue,
- IntPtr.Zero,
- FileMapProtection.PageReadWrite | prot,
- (uint)(size.ToInt64() >> 32),
- (uint)size.ToInt64(),
- null);
+ return WindowsApi.VirtualProtect(address, size, WindowsApi.GetProtection(permission), out _);
+ }
+ }
- if (handle == IntPtr.Zero)
+ public static bool Reprotect4KB(IntPtr address, IntPtr size, MemoryPermission permission, bool forView)
+ {
+ ulong uaddress = (ulong)address;
+ ulong usize = (ulong)size;
+ while (usize > 0)
+ {
+ if (!WindowsApi.VirtualProtect((IntPtr)uaddress, (IntPtr)PageSize, WindowsApi.GetProtection(permission), out _))
{
- throw new OutOfMemoryException();
+ return false;
}
- return handle;
+ uaddress += PageSize;
+ usize -= PageSize;
}
+
+ return true;
}
- public static void DestroySharedMemory(IntPtr handle)
+ public static bool Free(IntPtr address)
{
- if (UseWin10Placeholders)
- {
- lock (_emulatedHandleLock)
- {
- int iHandle = (int)(ulong)handle;
+ return WindowsApi.VirtualFree(address, IntPtr.Zero, AllocationType.Release);
+ }
- if (EmulatedHandleValid(ref iHandle))
- {
- _emulatedSharedList.Remove(_emulatedShared[iHandle]);
- _emulatedShared[iHandle].Dispose();
- _emulatedShared[iHandle] = null;
+ public static IntPtr CreateSharedMemory(IntPtr size, bool reserve)
+ {
+ var prot = reserve ? FileMapProtection.SectionReserve : FileMapProtection.SectionCommit;
- return;
- }
- }
- }
+ IntPtr handle = WindowsApi.CreateFileMapping(
+ WindowsApi.InvalidHandleValue,
+ IntPtr.Zero,
+ FileMapProtection.PageReadWrite | prot,
+ (uint)(size.ToInt64() >> 32),
+ (uint)size.ToInt64(),
+ null);
- if (!CloseHandle(handle))
+ if (handle == IntPtr.Zero)
{
- throw new ArgumentException("Invalid handle.", nameof(handle));
+ throw new OutOfMemoryException();
}
+
+ return handle;
}
- public static IntPtr MapSharedMemory(IntPtr handle)
+ public static void DestroySharedMemory(IntPtr handle)
{
- if (UseWin10Placeholders)
+ if (!WindowsApi.CloseHandle(handle))
{
- lock (_emulatedHandleLock)
- {
- int iHandle = (int)(ulong)handle;
-
- if (EmulatedHandleValid(ref iHandle))
- {
- return _emulatedShared[iHandle].Map();
- }
- }
+ throw new ArgumentException("Invalid handle.", nameof(handle));
}
+ }
- IntPtr ptr = MapViewOfFile(handle, 4 | 2, 0, 0, IntPtr.Zero);
+ public static IntPtr MapSharedMemory(IntPtr handle)
+ {
+ IntPtr ptr = WindowsApi.MapViewOfFile(handle, 4 | 2, 0, 0, IntPtr.Zero);
if (ptr == IntPtr.Zero)
{
@@ -278,24 +198,15 @@ namespace Ryujinx.Memory
public static void UnmapSharedMemory(IntPtr address)
{
- if (UseWin10Placeholders)
- {
- lock (_emulatedHandleLock)
- {
- foreach (EmulatedSharedMemoryWindows shared in _emulatedSharedList)
- {
- if (shared.Unmap((ulong)address))
- {
- return;
- }
- }
- }
- }
-
- if (!UnmapViewOfFile(address))
+ if (!WindowsApi.UnmapViewOfFile(address))
{
throw new ArgumentException("Invalid address.", nameof(address));
}
}
+
+ public static bool RetryFromAccessViolation()
+ {
+ return _placeholders.RetryFromAccessViolation();
+ }
}
} \ No newline at end of file