MappingTree.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. using Ryujinx.Common.Collections;
  2. using Ryujinx.Memory;
  3. using Ryujinx.Memory.Range;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Threading;
  7. namespace Ryujinx.Cpu.Jit
  8. {
  9. class MappingTree
  10. {
  11. private const ulong PageSize = 0x1000;
  12. private enum MappingState : byte
  13. {
  14. Unmapped,
  15. Mapped,
  16. MappedWithMirror
  17. }
  18. private class Mapping : IntrusiveRedBlackTreeNode<Mapping>, IComparable<Mapping>
  19. {
  20. public ulong Address { get; private set; }
  21. public ulong Size { get; private set; }
  22. public ulong EndAddress => Address + Size;
  23. public ulong BackingOffset { get; private set; }
  24. public MappingState State { get; private set; }
  25. public Mapping(ulong address, ulong size, ulong backingOffset, MappingState state)
  26. {
  27. Address = address;
  28. Size = size;
  29. BackingOffset = backingOffset;
  30. State = state;
  31. }
  32. public Mapping Split(ulong splitAddress)
  33. {
  34. ulong leftSize = splitAddress - Address;
  35. ulong rightSize = EndAddress - splitAddress;
  36. Mapping left = new Mapping(Address, leftSize, BackingOffset, State);
  37. Address = splitAddress;
  38. Size = rightSize;
  39. if (State != MappingState.Unmapped)
  40. {
  41. BackingOffset += leftSize;
  42. }
  43. return left;
  44. }
  45. public void UpdateState(ulong newBackingOffset, MappingState newState)
  46. {
  47. BackingOffset = newBackingOffset;
  48. State = newState;
  49. }
  50. public void Extend(ulong sizeDelta)
  51. {
  52. Size += sizeDelta;
  53. }
  54. public int CompareTo(Mapping other)
  55. {
  56. if (Address < other.Address)
  57. {
  58. return -1;
  59. }
  60. else if (Address <= other.EndAddress - 1UL)
  61. {
  62. return 0;
  63. }
  64. else
  65. {
  66. return 1;
  67. }
  68. }
  69. }
  70. private readonly IntrusiveRedBlackTree<Mapping> _tree;
  71. private readonly ReaderWriterLock _treeLock;
  72. public MappingTree(ulong addressSpaceSize)
  73. {
  74. _tree = new IntrusiveRedBlackTree<Mapping>();
  75. _treeLock = new ReaderWriterLock();
  76. _tree.Add(new Mapping(0UL, addressSpaceSize, 0UL, MappingState.Unmapped));
  77. }
  78. public void Map(ulong va, ulong pa, ulong size)
  79. {
  80. _treeLock.AcquireWriterLock(Timeout.Infinite);
  81. Update(va, pa, size, MappingState.Mapped);
  82. _treeLock.ReleaseWriterLock();
  83. }
  84. public void Unmap(ulong va, ulong size)
  85. {
  86. _treeLock.AcquireWriterLock(Timeout.Infinite);
  87. Update(va, 0UL, size, MappingState.Unmapped);
  88. _treeLock.ReleaseWriterLock();
  89. }
  90. public IEnumerable<MemoryRange> GetPhysicalRegions(ulong va, ulong size)
  91. {
  92. _treeLock.AcquireReaderLock(Timeout.Infinite);
  93. var regions = GetPhysicalRegionsImpl(va, size);
  94. _treeLock.ReleaseReaderLock();
  95. return regions;
  96. }
  97. public (MemoryBlock, ulong) GetContiguousBlock(MemoryBlock backingMemory, MemoryBlock mirror, ulong va, ulong size)
  98. {
  99. _treeLock.AcquireReaderLock(Timeout.Infinite);
  100. var result = GetContiguousBlockImpl(backingMemory, mirror, va, size);
  101. _treeLock.ReleaseReaderLock();
  102. return result;
  103. }
  104. private void Update(ulong va, ulong pa, ulong size, MappingState state)
  105. {
  106. Mapping map = _tree.GetNode(new Mapping(va, 1UL, 0UL, MappingState.Unmapped));
  107. Update(map, va, pa, size, state);
  108. }
  109. private Mapping Update(Mapping map, ulong va, ulong pa, ulong size, MappingState state)
  110. {
  111. ulong endAddress = va + size;
  112. for (; map != null; map = map.Successor)
  113. {
  114. if (map.Address < va)
  115. {
  116. _tree.Add(map.Split(va));
  117. }
  118. if (map.EndAddress > endAddress)
  119. {
  120. Mapping newMap = map.Split(endAddress);
  121. _tree.Add(newMap);
  122. map = newMap;
  123. }
  124. map.UpdateState(pa, state);
  125. map = TryCoalesce(map);
  126. if (map.EndAddress >= endAddress)
  127. {
  128. break;
  129. }
  130. }
  131. return map;
  132. }
  133. private Mapping TryCoalesce(Mapping map)
  134. {
  135. Mapping previousMap = map.Predecessor;
  136. Mapping nextMap = map.Successor;
  137. if (previousMap != null && CanCoalesce(previousMap, map))
  138. {
  139. previousMap.Extend(map.Size);
  140. _tree.Remove(map);
  141. map = previousMap;
  142. }
  143. if (nextMap != null && CanCoalesce(map, nextMap))
  144. {
  145. map.Extend(nextMap.Size);
  146. _tree.Remove(nextMap);
  147. }
  148. return map;
  149. }
  150. private static bool CanCoalesce(Mapping left, Mapping right)
  151. {
  152. if (left.State != right.State)
  153. {
  154. return false;
  155. }
  156. return left.State == MappingState.Unmapped || (left.BackingOffset + left.Size == right.BackingOffset);
  157. }
  158. private IEnumerable<MemoryRange> GetPhysicalRegionsImpl(ulong va, ulong size)
  159. {
  160. Mapping map = _tree.GetNode(new Mapping(va, 1UL, 0UL, MappingState.Unmapped));
  161. if (map == null)
  162. {
  163. ThrowInvalidMemoryRegionException($"Not mapped: va=0x{va:X16}, size=0x{size:X16}");
  164. }
  165. var regions = new List<MemoryRange>();
  166. ulong endAddress = va + size;
  167. ulong regionStart = 0;
  168. ulong regionSize = 0;
  169. for (; map != null; map = map.Successor)
  170. {
  171. if (map.State == MappingState.Unmapped)
  172. {
  173. ThrowInvalidMemoryRegionException($"Not mapped: va=0x{va:X16}, size=0x{size:X16}");
  174. }
  175. ulong clampedAddress = Math.Max(map.Address, va);
  176. ulong clampedEndAddress = Math.Min(map.EndAddress, endAddress);
  177. ulong clampedSize = clampedEndAddress - clampedAddress;
  178. ulong pa = map.BackingOffset + (clampedAddress - map.Address);
  179. if (pa != regionStart + regionSize)
  180. {
  181. if (regionSize != 0)
  182. {
  183. regions.Add(new MemoryRange(regionStart, regionSize));
  184. }
  185. regionStart = pa;
  186. regionSize = clampedSize;
  187. }
  188. else
  189. {
  190. regionSize += clampedSize;
  191. }
  192. if (map.EndAddress >= endAddress)
  193. {
  194. break;
  195. }
  196. }
  197. if (regionSize != 0)
  198. {
  199. regions.Add(new MemoryRange(regionStart, regionSize));
  200. }
  201. return regions;
  202. }
  203. private (MemoryBlock, ulong) GetContiguousBlockImpl(MemoryBlock backingMemory, MemoryBlock mirror, ulong va, ulong size)
  204. {
  205. Mapping map = _tree.GetNode(new Mapping(va, 1UL, 0UL, MappingState.Unmapped));
  206. ulong endAddress = va + size;
  207. if (map != null && map.Address <= va && map.EndAddress >= endAddress)
  208. {
  209. ulong pa = map.BackingOffset + (va - map.Address);
  210. return (backingMemory, pa);
  211. }
  212. if (map != null)
  213. {
  214. Mapping firstMap = map;
  215. bool contiguous = true;
  216. ulong expectedPa = map.BackingOffset + map.Size;
  217. while ((map = map.Successor) != null && map.Address < endAddress)
  218. {
  219. if (map.State == MappingState.Unmapped || map.BackingOffset != expectedPa)
  220. {
  221. contiguous = false;
  222. break;
  223. }
  224. if (map.EndAddress >= endAddress)
  225. {
  226. break;
  227. }
  228. expectedPa = map.BackingOffset + map.Size;
  229. }
  230. if (contiguous && map != null)
  231. {
  232. ulong pa = firstMap.BackingOffset + (va - firstMap.Address);
  233. return (backingMemory, pa);
  234. }
  235. map = firstMap;
  236. }
  237. ulong endVaAligned = (endAddress + PageSize - 1) & ~(PageSize - 1);
  238. ulong vaAligned = va & ~(PageSize - 1);
  239. // Make sure the range that will be accessed on the mirror is fully mapped.
  240. for (; map != null; map = map.Successor)
  241. {
  242. if (map.State == MappingState.Mapped)
  243. {
  244. ulong clampedAddress = Math.Max(map.Address, vaAligned);
  245. ulong clampedEndAddress = Math.Min(map.EndAddress, endVaAligned);
  246. ulong clampedSize = clampedEndAddress - clampedAddress;
  247. ulong backingOffset = map.BackingOffset + (clampedAddress - map.Address);
  248. LockCookie lockCookie = _treeLock.UpgradeToWriterLock(Timeout.Infinite);
  249. mirror.MapView(backingMemory, backingOffset, clampedAddress, clampedSize);
  250. map = Update(map, clampedAddress, backingOffset, clampedSize, MappingState.MappedWithMirror);
  251. _treeLock.DowngradeFromWriterLock(ref lockCookie);
  252. }
  253. if (map.EndAddress >= endAddress)
  254. {
  255. break;
  256. }
  257. }
  258. return (mirror, va);
  259. }
  260. private static void ThrowInvalidMemoryRegionException(string message) => throw new InvalidMemoryRegionException(message);
  261. }
  262. }