HvMemoryManager.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  1. using ARMeilleure.Memory;
  2. using Ryujinx.Memory;
  3. using Ryujinx.Memory.Range;
  4. using Ryujinx.Memory.Tracking;
  5. using System;
  6. using System.Buffers;
  7. using System.Collections.Generic;
  8. using System.Linq;
  9. using System.Runtime.CompilerServices;
  10. using System.Runtime.Versioning;
  11. namespace Ryujinx.Cpu.AppleHv
  12. {
  13. /// <summary>
  14. /// Represents a CPU memory manager which maps guest virtual memory directly onto the Hypervisor page table.
  15. /// </summary>
  16. [SupportedOSPlatform("macos")]
  17. public sealed class HvMemoryManager : VirtualMemoryManagerRefCountedBase, IMemoryManager, IVirtualMemoryManagerTracked
  18. {
  19. private readonly InvalidAccessHandler _invalidAccessHandler;
  20. private readonly HvAddressSpace _addressSpace;
  21. internal HvAddressSpace AddressSpace => _addressSpace;
  22. private readonly MemoryBlock _backingMemory;
  23. private readonly PageTable<ulong> _pageTable;
  24. private readonly ManagedPageFlags _pages;
  25. public bool UsesPrivateAllocations => false;
  26. public int AddressSpaceBits { get; }
  27. public IntPtr PageTablePointer => IntPtr.Zero;
  28. public MemoryManagerType Type => MemoryManagerType.SoftwarePageTable;
  29. public MemoryTracking Tracking { get; }
  30. public event Action<ulong, ulong> UnmapEvent;
  31. protected override ulong AddressSpaceSize { get; }
  32. /// <summary>
  33. /// Creates a new instance of the Hypervisor memory manager.
  34. /// </summary>
  35. /// <param name="backingMemory">Physical backing memory where virtual memory will be mapped to</param>
  36. /// <param name="addressSpaceSize">Size of the address space</param>
  37. /// <param name="invalidAccessHandler">Optional function to handle invalid memory accesses</param>
  38. public HvMemoryManager(MemoryBlock backingMemory, ulong addressSpaceSize, InvalidAccessHandler invalidAccessHandler = null)
  39. {
  40. _backingMemory = backingMemory;
  41. _pageTable = new PageTable<ulong>();
  42. _invalidAccessHandler = invalidAccessHandler;
  43. AddressSpaceSize = addressSpaceSize;
  44. ulong asSize = PageSize;
  45. int asBits = PageBits;
  46. while (asSize < addressSpaceSize)
  47. {
  48. asSize <<= 1;
  49. asBits++;
  50. }
  51. _addressSpace = new HvAddressSpace(backingMemory, asSize);
  52. AddressSpaceBits = asBits;
  53. _pages = new ManagedPageFlags(AddressSpaceBits);
  54. Tracking = new MemoryTracking(this, PageSize, invalidAccessHandler);
  55. }
  56. /// <inheritdoc/>
  57. public void Map(ulong va, ulong pa, ulong size, MemoryMapFlags flags)
  58. {
  59. AssertValidAddressAndSize(va, size);
  60. PtMap(va, pa, size);
  61. _addressSpace.MapUser(va, pa, size, MemoryPermission.ReadWriteExecute);
  62. _pages.AddMapping(va, size);
  63. Tracking.Map(va, size);
  64. }
  65. private void PtMap(ulong va, ulong pa, ulong size)
  66. {
  67. while (size != 0)
  68. {
  69. _pageTable.Map(va, pa);
  70. va += PageSize;
  71. pa += PageSize;
  72. size -= PageSize;
  73. }
  74. }
  75. /// <inheritdoc/>
  76. public void Unmap(ulong va, ulong size)
  77. {
  78. AssertValidAddressAndSize(va, size);
  79. UnmapEvent?.Invoke(va, size);
  80. Tracking.Unmap(va, size);
  81. _pages.RemoveMapping(va, size);
  82. _addressSpace.UnmapUser(va, size);
  83. PtUnmap(va, size);
  84. }
  85. private void PtUnmap(ulong va, ulong size)
  86. {
  87. while (size != 0)
  88. {
  89. _pageTable.Unmap(va);
  90. va += PageSize;
  91. size -= PageSize;
  92. }
  93. }
  94. public override T ReadTracked<T>(ulong va)
  95. {
  96. try
  97. {
  98. return base.ReadTracked<T>(va);
  99. }
  100. catch (InvalidMemoryRegionException)
  101. {
  102. if (_invalidAccessHandler == null || !_invalidAccessHandler(va))
  103. {
  104. throw;
  105. }
  106. return default;
  107. }
  108. }
  109. public override void Read(ulong va, Span<byte> data)
  110. {
  111. try
  112. {
  113. base.Read(va, data);
  114. }
  115. catch (InvalidMemoryRegionException)
  116. {
  117. if (_invalidAccessHandler == null || !_invalidAccessHandler(va))
  118. {
  119. throw;
  120. }
  121. }
  122. }
  123. public override void Write(ulong va, ReadOnlySpan<byte> data)
  124. {
  125. try
  126. {
  127. base.Write(va, data);
  128. }
  129. catch (InvalidMemoryRegionException)
  130. {
  131. if (_invalidAccessHandler == null || !_invalidAccessHandler(va))
  132. {
  133. throw;
  134. }
  135. }
  136. }
  137. public override void WriteUntracked(ulong va, ReadOnlySpan<byte> data)
  138. {
  139. try
  140. {
  141. base.WriteUntracked(va, data);
  142. }
  143. catch (InvalidMemoryRegionException)
  144. {
  145. if (_invalidAccessHandler == null || !_invalidAccessHandler(va))
  146. {
  147. throw;
  148. }
  149. }
  150. }
  151. public override ReadOnlySequence<byte> GetReadOnlySequence(ulong va, int size, bool tracked = false)
  152. {
  153. try
  154. {
  155. return base.GetReadOnlySequence(va, size, tracked);
  156. }
  157. catch (InvalidMemoryRegionException)
  158. {
  159. if (_invalidAccessHandler == null || !_invalidAccessHandler(va))
  160. {
  161. throw;
  162. }
  163. return ReadOnlySequence<byte>.Empty;
  164. }
  165. }
  166. public ref T GetRef<T>(ulong va) where T : unmanaged
  167. {
  168. if (!IsContiguous(va, Unsafe.SizeOf<T>()))
  169. {
  170. ThrowMemoryNotContiguous();
  171. }
  172. SignalMemoryTracking(va, (ulong)Unsafe.SizeOf<T>(), true);
  173. return ref _backingMemory.GetRef<T>(GetPhysicalAddressChecked(va));
  174. }
  175. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  176. public override bool IsMapped(ulong va)
  177. {
  178. return ValidateAddress(va) && _pages.IsMapped(va);
  179. }
  180. /// <inheritdoc/>
  181. public bool IsRangeMapped(ulong va, ulong size)
  182. {
  183. AssertValidAddressAndSize(va, size);
  184. return _pages.IsRangeMapped(va, size);
  185. }
  186. /// <inheritdoc/>
  187. public IEnumerable<HostMemoryRange> GetHostRegions(ulong va, ulong size)
  188. {
  189. if (size == 0)
  190. {
  191. return Enumerable.Empty<HostMemoryRange>();
  192. }
  193. var guestRegions = GetPhysicalRegionsImpl(va, size);
  194. if (guestRegions == null)
  195. {
  196. return null;
  197. }
  198. var regions = new HostMemoryRange[guestRegions.Count];
  199. for (int i = 0; i < regions.Length; i++)
  200. {
  201. var guestRegion = guestRegions[i];
  202. IntPtr pointer = _backingMemory.GetPointer(guestRegion.Address, guestRegion.Size);
  203. regions[i] = new HostMemoryRange((nuint)(ulong)pointer, guestRegion.Size);
  204. }
  205. return regions;
  206. }
  207. /// <inheritdoc/>
  208. public IEnumerable<MemoryRange> GetPhysicalRegions(ulong va, ulong size)
  209. {
  210. if (size == 0)
  211. {
  212. return Enumerable.Empty<MemoryRange>();
  213. }
  214. return GetPhysicalRegionsImpl(va, size);
  215. }
  216. private List<MemoryRange> GetPhysicalRegionsImpl(ulong va, ulong size)
  217. {
  218. if (!ValidateAddress(va) || !ValidateAddressAndSize(va, size))
  219. {
  220. return null;
  221. }
  222. int pages = GetPagesCount(va, (uint)size, out va);
  223. var regions = new List<MemoryRange>();
  224. ulong regionStart = GetPhysicalAddressInternal(va);
  225. ulong regionSize = PageSize;
  226. for (int page = 0; page < pages - 1; page++)
  227. {
  228. if (!ValidateAddress(va + PageSize))
  229. {
  230. return null;
  231. }
  232. ulong newPa = GetPhysicalAddressInternal(va + PageSize);
  233. if (GetPhysicalAddressInternal(va) + PageSize != newPa)
  234. {
  235. regions.Add(new MemoryRange(regionStart, regionSize));
  236. regionStart = newPa;
  237. regionSize = 0;
  238. }
  239. va += PageSize;
  240. regionSize += PageSize;
  241. }
  242. regions.Add(new MemoryRange(regionStart, regionSize));
  243. return regions;
  244. }
  245. /// <remarks>
  246. /// This function also validates that the given range is both valid and mapped, and will throw if it is not.
  247. /// </remarks>
  248. public override void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null)
  249. {
  250. AssertValidAddressAndSize(va, size);
  251. if (precise)
  252. {
  253. Tracking.VirtualMemoryEvent(va, size, write, precise: true, exemptId);
  254. return;
  255. }
  256. _pages.SignalMemoryTracking(Tracking, va, size, write, exemptId);
  257. }
  258. public void Reprotect(ulong va, ulong size, MemoryPermission protection)
  259. {
  260. // TODO
  261. }
  262. /// <inheritdoc/>
  263. public void TrackingReprotect(ulong va, ulong size, MemoryPermission protection, bool guest)
  264. {
  265. if (guest)
  266. {
  267. _addressSpace.ReprotectUser(va, size, protection);
  268. }
  269. else
  270. {
  271. _pages.TrackingReprotect(va, size, protection);
  272. }
  273. }
  274. /// <inheritdoc/>
  275. public RegionHandle BeginTracking(ulong address, ulong size, int id, RegionFlags flags = RegionFlags.None)
  276. {
  277. return Tracking.BeginTracking(address, size, id, flags);
  278. }
  279. /// <inheritdoc/>
  280. public MultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id, RegionFlags flags = RegionFlags.None)
  281. {
  282. return Tracking.BeginGranularTracking(address, size, handles, granularity, id, flags);
  283. }
  284. /// <inheritdoc/>
  285. public SmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id)
  286. {
  287. return Tracking.BeginSmartGranularTracking(address, size, granularity, id);
  288. }
  289. private nuint GetPhysicalAddressChecked(ulong va)
  290. {
  291. if (!IsMapped(va))
  292. {
  293. ThrowInvalidMemoryRegionException($"Not mapped: va=0x{va:X16}");
  294. }
  295. return GetPhysicalAddressInternal(va);
  296. }
  297. private nuint GetPhysicalAddressInternal(ulong va)
  298. {
  299. return (nuint)(_pageTable.Read(va) + (va & PageMask));
  300. }
  301. /// <summary>
  302. /// Disposes of resources used by the memory manager.
  303. /// </summary>
  304. protected override void Destroy()
  305. {
  306. _addressSpace.Dispose();
  307. }
  308. protected override Memory<byte> GetPhysicalAddressMemory(nuint pa, int size)
  309. => _backingMemory.GetMemory(pa, size);
  310. protected override Span<byte> GetPhysicalAddressSpan(nuint pa, int size)
  311. => _backingMemory.GetSpan(pa, size);
  312. protected override nuint TranslateVirtualAddressChecked(ulong va)
  313. => GetPhysicalAddressChecked(va);
  314. protected override nuint TranslateVirtualAddressUnchecked(ulong va)
  315. => GetPhysicalAddressInternal(va);
  316. }
  317. }