HvMemoryManager.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  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 nint PageTablePointer => nint.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. yield break;
  192. }
  193. IEnumerable<MemoryRange> guestRegions = GetPhysicalRegionsImpl(va, size);
  194. if (guestRegions == null)
  195. {
  196. yield break;
  197. }
  198. foreach (MemoryRange guestRegion in guestRegions)
  199. {
  200. nint pointer = _backingMemory.GetPointer(guestRegion.Address, guestRegion.Size);
  201. yield return new HostMemoryRange((nuint)(ulong)pointer, guestRegion.Size);
  202. }
  203. }
  204. /// <inheritdoc/>
  205. public IEnumerable<MemoryRange> GetPhysicalRegions(ulong va, ulong size)
  206. {
  207. if (size == 0)
  208. {
  209. yield break;
  210. }
  211. foreach (MemoryRange physicalRegion in GetPhysicalRegionsImpl(va, size))
  212. {
  213. yield return physicalRegion;
  214. }
  215. }
  216. private IEnumerable<MemoryRange> GetPhysicalRegionsImpl(ulong va, ulong size)
  217. {
  218. if (!ValidateAddress(va) || !ValidateAddressAndSize(va, size))
  219. {
  220. yield break;
  221. }
  222. int pages = GetPagesCount(va, (uint)size, out va);
  223. ulong regionStart = GetPhysicalAddressInternal(va);
  224. ulong regionSize = PageSize;
  225. for (int page = 0; page < pages - 1; page++)
  226. {
  227. if (!ValidateAddress(va + PageSize))
  228. {
  229. yield break;
  230. }
  231. ulong newPa = GetPhysicalAddressInternal(va + PageSize);
  232. if (GetPhysicalAddressInternal(va) + PageSize != newPa)
  233. {
  234. yield return new MemoryRange(regionStart, regionSize);
  235. regionStart = newPa;
  236. regionSize = 0;
  237. }
  238. va += PageSize;
  239. regionSize += PageSize;
  240. }
  241. yield return new MemoryRange(regionStart, regionSize);
  242. }
  243. /// <remarks>
  244. /// This function also validates that the given range is both valid and mapped, and will throw if it is not.
  245. /// </remarks>
  246. public override void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null)
  247. {
  248. AssertValidAddressAndSize(va, size);
  249. if (precise)
  250. {
  251. Tracking.VirtualMemoryEvent(va, size, write, precise: true, exemptId);
  252. return;
  253. }
  254. _pages.SignalMemoryTracking(Tracking, va, size, write, exemptId);
  255. }
  256. public void Reprotect(ulong va, ulong size, MemoryPermission protection)
  257. {
  258. // TODO
  259. }
  260. /// <inheritdoc/>
  261. public void TrackingReprotect(ulong va, ulong size, MemoryPermission protection, bool guest)
  262. {
  263. if (guest)
  264. {
  265. _addressSpace.ReprotectUser(va, size, protection);
  266. }
  267. else
  268. {
  269. _pages.TrackingReprotect(va, size, protection);
  270. }
  271. }
  272. /// <inheritdoc/>
  273. public RegionHandle BeginTracking(ulong address, ulong size, int id, RegionFlags flags = RegionFlags.None)
  274. {
  275. return Tracking.BeginTracking(address, size, id, flags);
  276. }
  277. /// <inheritdoc/>
  278. public MultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id, RegionFlags flags = RegionFlags.None)
  279. {
  280. return Tracking.BeginGranularTracking(address, size, handles, granularity, id, flags);
  281. }
  282. /// <inheritdoc/>
  283. public SmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id)
  284. {
  285. return Tracking.BeginSmartGranularTracking(address, size, granularity, id);
  286. }
  287. private nuint GetPhysicalAddressChecked(ulong va)
  288. {
  289. if (!IsMapped(va))
  290. {
  291. ThrowInvalidMemoryRegionException($"Not mapped: va=0x{va:X16}");
  292. }
  293. return GetPhysicalAddressInternal(va);
  294. }
  295. private nuint GetPhysicalAddressInternal(ulong va)
  296. {
  297. return (nuint)(_pageTable.Read(va) + (va & PageMask));
  298. }
  299. /// <summary>
  300. /// Disposes of resources used by the memory manager.
  301. /// </summary>
  302. protected override void Destroy()
  303. {
  304. _addressSpace.Dispose();
  305. }
  306. protected override Memory<byte> GetPhysicalAddressMemory(nuint pa, int size)
  307. => _backingMemory.GetMemory(pa, size);
  308. protected override Span<byte> GetPhysicalAddressSpan(nuint pa, int size)
  309. => _backingMemory.GetSpan(pa, size);
  310. protected override nuint TranslateVirtualAddressChecked(ulong va)
  311. => GetPhysicalAddressChecked(va);
  312. protected override nuint TranslateVirtualAddressUnchecked(ulong va)
  313. => GetPhysicalAddressInternal(va);
  314. }
  315. }