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