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.cs261
1 files changed, 228 insertions, 33 deletions
diff --git a/Ryujinx.Memory/MemoryManagementWindows.cs b/Ryujinx.Memory/MemoryManagementWindows.cs
index 9513bb54..b14fb6c1 100644
--- a/Ryujinx.Memory/MemoryManagementWindows.cs
+++ b/Ryujinx.Memory/MemoryManagementWindows.cs
@@ -1,57 +1,69 @@
-using System;
+using Ryujinx.Memory.WindowsShared;
+using System;
+using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace Ryujinx.Memory
{
static class MemoryManagementWindows
{
- [Flags]
- private enum AllocationType : uint
- {
- Commit = 0x1000,
- Reserve = 0x2000,
- Decommit = 0x4000,
- Release = 0x8000,
- Reset = 0x80000,
- Physical = 0x400000,
- TopDown = 0x100000,
- WriteWatch = 0x200000,
- LargePages = 0x20000000
- }
+ private static readonly IntPtr InvalidHandleValue = new IntPtr(-1);
+ private static bool UseWin10Placeholders;
- [Flags]
- private enum MemoryProtection : uint
- {
- NoAccess = 0x01,
- ReadOnly = 0x02,
- ReadWrite = 0x04,
- WriteCopy = 0x08,
- Execute = 0x10,
- ExecuteRead = 0x20,
- ExecuteReadWrite = 0x40,
- ExecuteWriteCopy = 0x80,
- GuardModifierflag = 0x100,
- NoCacheModifierflag = 0x200,
- WriteCombineModifierflag = 0x400
- }
+ private static object _emulatedHandleLock = new object();
+ private static EmulatedSharedMemoryWindows[] _emulatedShared = new EmulatedSharedMemoryWindows[64];
+ private static List<EmulatedSharedMemoryWindows> _emulatedSharedList = new List<EmulatedSharedMemoryWindows>();
- [DllImport("kernel32.dll")]
+ [DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr VirtualAlloc(
IntPtr lpAddress,
IntPtr dwSize,
AllocationType flAllocationType,
MemoryProtection flProtect);
- [DllImport("kernel32.dll")]
+ [DllImport("kernel32.dll", SetLastError = true)]
private static extern bool VirtualProtect(
IntPtr lpAddress,
IntPtr dwSize,
MemoryProtection flNewProtect,
out MemoryProtection lpflOldProtect);
- [DllImport("kernel32.dll")]
+ [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()
+ {
+ Version version = Environment.OSVersion.Version;
+
+ UseWin10Placeholders = (version.Major == 10 && version.Build >= 17134) || version.Major > 10;
+ }
+
public static IntPtr Allocate(IntPtr size)
{
return AllocateInternal(size, AllocationType.Reserve | AllocationType.Commit);
@@ -76,12 +88,68 @@ namespace Ryujinx.Memory
public static bool Commit(IntPtr location, IntPtr size)
{
+ if (UseWin10Placeholders)
+ {
+ lock (_emulatedSharedList)
+ {
+ foreach (var shared in _emulatedSharedList)
+ {
+ if (shared.CommitMap(location, size))
+ {
+ return true;
+ }
+ }
+ }
+ }
+
return 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 VirtualFree(location, size, AllocationType.Decommit);
+ }
+
public static bool Reprotect(IntPtr address, IntPtr size, MemoryPermission permission)
{
- return VirtualProtect(address, size, GetProtection(permission), out _);
+ 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);
+
+ if (!VirtualProtect((IntPtr)uaddress, (IntPtr)mapSize, GetProtection(permission), out _))
+ {
+ return false;
+ }
+
+ uaddress = nextGranular;
+ usize -= mapSize;
+ }
+
+ return true;
+ }
+ else
+ {
+ return VirtualProtect(address, size, GetProtection(permission), out _);
+ }
}
private static MemoryProtection GetProtection(MemoryPermission permission)
@@ -102,5 +170,132 @@ namespace Ryujinx.Memory
{
return VirtualFree(address, IntPtr.Zero, AllocationType.Release);
}
+
+ private static int GetEmulatedHandle()
+ {
+ // Assumes we have the handle lock.
+
+ for (int i = 0; i < _emulatedShared.Length; i++)
+ {
+ if (_emulatedShared[i] == null)
+ {
+ return i + 1;
+ }
+ }
+
+ throw new InvalidProgramException("Too many shared memory handles were created.");
+ }
+
+ public static bool EmulatedHandleValid(ref int handle)
+ {
+ handle--;
+ return handle >= 0 && handle < _emulatedShared.Length && _emulatedShared[handle] != null;
+ }
+
+ public static IntPtr CreateSharedMemory(IntPtr size, bool reserve)
+ {
+ if (UseWin10Placeholders && reserve)
+ {
+ lock (_emulatedHandleLock)
+ {
+ int handle = GetEmulatedHandle();
+ _emulatedShared[handle - 1] = new EmulatedSharedMemoryWindows((ulong)size);
+ _emulatedSharedList.Add(_emulatedShared[handle - 1]);
+
+ return (IntPtr)handle;
+ }
+ }
+ 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);
+
+ if (handle == IntPtr.Zero)
+ {
+ throw new OutOfMemoryException();
+ }
+
+ return handle;
+ }
+ }
+
+ public static void DestroySharedMemory(IntPtr handle)
+ {
+ if (UseWin10Placeholders)
+ {
+ lock (_emulatedHandleLock)
+ {
+ int iHandle = (int)(ulong)handle;
+
+ if (EmulatedHandleValid(ref iHandle))
+ {
+ _emulatedSharedList.Remove(_emulatedShared[iHandle]);
+ _emulatedShared[iHandle].Dispose();
+ _emulatedShared[iHandle] = null;
+
+ return;
+ }
+ }
+ }
+
+ if (!CloseHandle(handle))
+ {
+ throw new ArgumentException("Invalid handle.", nameof(handle));
+ }
+ }
+
+ public static IntPtr MapSharedMemory(IntPtr handle)
+ {
+ if (UseWin10Placeholders)
+ {
+ lock (_emulatedHandleLock)
+ {
+ int iHandle = (int)(ulong)handle;
+
+ if (EmulatedHandleValid(ref iHandle))
+ {
+ return _emulatedShared[iHandle].Map();
+ }
+ }
+ }
+
+ IntPtr ptr = MapViewOfFile(handle, 4 | 2, 0, 0, IntPtr.Zero);
+
+ if (ptr == IntPtr.Zero)
+ {
+ throw new OutOfMemoryException();
+ }
+
+ return ptr;
+ }
+
+ public static void UnmapSharedMemory(IntPtr address)
+ {
+ if (UseWin10Placeholders)
+ {
+ lock (_emulatedHandleLock)
+ {
+ foreach (EmulatedSharedMemoryWindows shared in _emulatedSharedList)
+ {
+ if (shared.Unmap((ulong)address))
+ {
+ return;
+ }
+ }
+ }
+ }
+
+ if (!UnmapViewOfFile(address))
+ {
+ throw new ArgumentException("Invalid address.", nameof(address));
+ }
+ }
}
} \ No newline at end of file