MemoryManagementWindows.cs 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. using Ryujinx.Memory.WindowsShared;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Runtime.InteropServices;
  5. using System.Runtime.Versioning;
  6. namespace Ryujinx.Memory
  7. {
  8. [SupportedOSPlatform("windows")]
  9. static class MemoryManagementWindows
  10. {
  11. private static readonly IntPtr InvalidHandleValue = new IntPtr(-1);
  12. private static bool UseWin10Placeholders;
  13. private static object _emulatedHandleLock = new object();
  14. private static EmulatedSharedMemoryWindows[] _emulatedShared = new EmulatedSharedMemoryWindows[64];
  15. private static List<EmulatedSharedMemoryWindows> _emulatedSharedList = new List<EmulatedSharedMemoryWindows>();
  16. [DllImport("kernel32.dll", SetLastError = true)]
  17. private static extern IntPtr VirtualAlloc(
  18. IntPtr lpAddress,
  19. IntPtr dwSize,
  20. AllocationType flAllocationType,
  21. MemoryProtection flProtect);
  22. [DllImport("kernel32.dll", SetLastError = true)]
  23. private static extern bool VirtualProtect(
  24. IntPtr lpAddress,
  25. IntPtr dwSize,
  26. MemoryProtection flNewProtect,
  27. out MemoryProtection lpflOldProtect);
  28. [DllImport("kernel32.dll", SetLastError = true)]
  29. private static extern bool VirtualFree(IntPtr lpAddress, IntPtr dwSize, AllocationType dwFreeType);
  30. [DllImport("kernel32.dll", SetLastError = true)]
  31. private static extern IntPtr CreateFileMapping(
  32. IntPtr hFile,
  33. IntPtr lpFileMappingAttributes,
  34. FileMapProtection flProtect,
  35. uint dwMaximumSizeHigh,
  36. uint dwMaximumSizeLow,
  37. [MarshalAs(UnmanagedType.LPWStr)] string lpName);
  38. [DllImport("kernel32.dll", SetLastError = true)]
  39. private static extern bool CloseHandle(IntPtr hObject);
  40. [DllImport("kernel32.dll", SetLastError = true)]
  41. private static extern IntPtr MapViewOfFile(
  42. IntPtr hFileMappingObject,
  43. uint dwDesiredAccess,
  44. uint dwFileOffsetHigh,
  45. uint dwFileOffsetLow,
  46. IntPtr dwNumberOfBytesToMap);
  47. [DllImport("kernel32.dll", SetLastError = true)]
  48. private static extern bool UnmapViewOfFile(IntPtr lpBaseAddress);
  49. [DllImport("kernel32.dll", SetLastError = true)]
  50. private static extern uint GetLastError();
  51. static MemoryManagementWindows()
  52. {
  53. UseWin10Placeholders = OperatingSystem.IsWindowsVersionAtLeast(10, 0, 17134);
  54. }
  55. public static IntPtr Allocate(IntPtr size)
  56. {
  57. return AllocateInternal(size, AllocationType.Reserve | AllocationType.Commit);
  58. }
  59. public static IntPtr Reserve(IntPtr size)
  60. {
  61. return AllocateInternal(size, AllocationType.Reserve);
  62. }
  63. private static IntPtr AllocateInternal(IntPtr size, AllocationType flags = 0)
  64. {
  65. IntPtr ptr = VirtualAlloc(IntPtr.Zero, size, flags, MemoryProtection.ReadWrite);
  66. if (ptr == IntPtr.Zero)
  67. {
  68. throw new OutOfMemoryException();
  69. }
  70. return ptr;
  71. }
  72. public static bool Commit(IntPtr location, IntPtr size)
  73. {
  74. if (UseWin10Placeholders)
  75. {
  76. lock (_emulatedSharedList)
  77. {
  78. foreach (var shared in _emulatedSharedList)
  79. {
  80. if (shared.CommitMap(location, size))
  81. {
  82. return true;
  83. }
  84. }
  85. }
  86. }
  87. return VirtualAlloc(location, size, AllocationType.Commit, MemoryProtection.ReadWrite) != IntPtr.Zero;
  88. }
  89. public static bool Decommit(IntPtr location, IntPtr size)
  90. {
  91. if (UseWin10Placeholders)
  92. {
  93. lock (_emulatedSharedList)
  94. {
  95. foreach (var shared in _emulatedSharedList)
  96. {
  97. if (shared.DecommitMap(location, size))
  98. {
  99. return true;
  100. }
  101. }
  102. }
  103. }
  104. return VirtualFree(location, size, AllocationType.Decommit);
  105. }
  106. public static bool Reprotect(IntPtr address, IntPtr size, MemoryPermission permission)
  107. {
  108. if (UseWin10Placeholders)
  109. {
  110. ulong uaddress = (ulong)address;
  111. ulong usize = (ulong)size;
  112. while (usize > 0)
  113. {
  114. ulong nextGranular = (uaddress & ~EmulatedSharedMemoryWindows.MappingMask) + EmulatedSharedMemoryWindows.MappingGranularity;
  115. ulong mapSize = Math.Min(usize, nextGranular - uaddress);
  116. if (!VirtualProtect((IntPtr)uaddress, (IntPtr)mapSize, GetProtection(permission), out _))
  117. {
  118. return false;
  119. }
  120. uaddress = nextGranular;
  121. usize -= mapSize;
  122. }
  123. return true;
  124. }
  125. else
  126. {
  127. return VirtualProtect(address, size, GetProtection(permission), out _);
  128. }
  129. }
  130. private static MemoryProtection GetProtection(MemoryPermission permission)
  131. {
  132. return permission switch
  133. {
  134. MemoryPermission.None => MemoryProtection.NoAccess,
  135. MemoryPermission.Read => MemoryProtection.ReadOnly,
  136. MemoryPermission.ReadAndWrite => MemoryProtection.ReadWrite,
  137. MemoryPermission.ReadAndExecute => MemoryProtection.ExecuteRead,
  138. MemoryPermission.ReadWriteExecute => MemoryProtection.ExecuteReadWrite,
  139. MemoryPermission.Execute => MemoryProtection.Execute,
  140. _ => throw new MemoryProtectionException(permission)
  141. };
  142. }
  143. public static bool Free(IntPtr address)
  144. {
  145. return VirtualFree(address, IntPtr.Zero, AllocationType.Release);
  146. }
  147. private static int GetEmulatedHandle()
  148. {
  149. // Assumes we have the handle lock.
  150. for (int i = 0; i < _emulatedShared.Length; i++)
  151. {
  152. if (_emulatedShared[i] == null)
  153. {
  154. return i + 1;
  155. }
  156. }
  157. throw new InvalidProgramException("Too many shared memory handles were created.");
  158. }
  159. public static bool EmulatedHandleValid(ref int handle)
  160. {
  161. handle--;
  162. return handle >= 0 && handle < _emulatedShared.Length && _emulatedShared[handle] != null;
  163. }
  164. public static IntPtr CreateSharedMemory(IntPtr size, bool reserve)
  165. {
  166. if (UseWin10Placeholders && reserve)
  167. {
  168. lock (_emulatedHandleLock)
  169. {
  170. int handle = GetEmulatedHandle();
  171. _emulatedShared[handle - 1] = new EmulatedSharedMemoryWindows((ulong)size);
  172. _emulatedSharedList.Add(_emulatedShared[handle - 1]);
  173. return (IntPtr)handle;
  174. }
  175. }
  176. else
  177. {
  178. var prot = reserve ? FileMapProtection.SectionReserve : FileMapProtection.SectionCommit;
  179. IntPtr handle = CreateFileMapping(
  180. InvalidHandleValue,
  181. IntPtr.Zero,
  182. FileMapProtection.PageReadWrite | prot,
  183. (uint)(size.ToInt64() >> 32),
  184. (uint)size.ToInt64(),
  185. null);
  186. if (handle == IntPtr.Zero)
  187. {
  188. throw new OutOfMemoryException();
  189. }
  190. return handle;
  191. }
  192. }
  193. public static void DestroySharedMemory(IntPtr handle)
  194. {
  195. if (UseWin10Placeholders)
  196. {
  197. lock (_emulatedHandleLock)
  198. {
  199. int iHandle = (int)(ulong)handle;
  200. if (EmulatedHandleValid(ref iHandle))
  201. {
  202. _emulatedSharedList.Remove(_emulatedShared[iHandle]);
  203. _emulatedShared[iHandle].Dispose();
  204. _emulatedShared[iHandle] = null;
  205. return;
  206. }
  207. }
  208. }
  209. if (!CloseHandle(handle))
  210. {
  211. throw new ArgumentException("Invalid handle.", nameof(handle));
  212. }
  213. }
  214. public static IntPtr MapSharedMemory(IntPtr handle)
  215. {
  216. if (UseWin10Placeholders)
  217. {
  218. lock (_emulatedHandleLock)
  219. {
  220. int iHandle = (int)(ulong)handle;
  221. if (EmulatedHandleValid(ref iHandle))
  222. {
  223. return _emulatedShared[iHandle].Map();
  224. }
  225. }
  226. }
  227. IntPtr ptr = MapViewOfFile(handle, 4 | 2, 0, 0, IntPtr.Zero);
  228. if (ptr == IntPtr.Zero)
  229. {
  230. throw new OutOfMemoryException();
  231. }
  232. return ptr;
  233. }
  234. public static void UnmapSharedMemory(IntPtr address)
  235. {
  236. if (UseWin10Placeholders)
  237. {
  238. lock (_emulatedHandleLock)
  239. {
  240. foreach (EmulatedSharedMemoryWindows shared in _emulatedSharedList)
  241. {
  242. if (shared.Unmap((ulong)address))
  243. {
  244. return;
  245. }
  246. }
  247. }
  248. }
  249. if (!UnmapViewOfFile(address))
  250. {
  251. throw new ArgumentException("Invalid address.", nameof(address));
  252. }
  253. }
  254. }
  255. }