diff options
Diffstat (limited to 'Ryujinx.Memory/MemoryManagementWindows.cs')
-rw-r--r-- | Ryujinx.Memory/MemoryManagementWindows.cs | 311 |
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 |