KMemoryBlockManager.cs 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. using Ryujinx.Common.Collections;
  2. using Ryujinx.HLE.HOS.Kernel.Common;
  3. using System.Diagnostics;
  4. namespace Ryujinx.HLE.HOS.Kernel.Memory
  5. {
  6. class KMemoryBlockManager
  7. {
  8. private const int PageSize = KPageTableBase.PageSize;
  9. private readonly IntrusiveRedBlackTree<KMemoryBlock> _blockTree;
  10. public int BlocksCount => _blockTree.Count;
  11. private KMemoryBlockSlabManager _slabManager;
  12. private ulong _addrSpaceStart;
  13. private ulong _addrSpaceEnd;
  14. public KMemoryBlockManager()
  15. {
  16. _blockTree = new IntrusiveRedBlackTree<KMemoryBlock>();
  17. }
  18. public KernelResult Initialize(ulong addrSpaceStart, ulong addrSpaceEnd, KMemoryBlockSlabManager slabManager)
  19. {
  20. _slabManager = slabManager;
  21. _addrSpaceStart = addrSpaceStart;
  22. _addrSpaceEnd = addrSpaceEnd;
  23. // First insertion will always need only a single block, because there's nothing to split.
  24. if (!slabManager.CanAllocate(1))
  25. {
  26. return KernelResult.OutOfResource;
  27. }
  28. ulong addrSpacePagesCount = (addrSpaceEnd - addrSpaceStart) / PageSize;
  29. _blockTree.Add(new KMemoryBlock(
  30. addrSpaceStart,
  31. addrSpacePagesCount,
  32. MemoryState.Unmapped,
  33. KMemoryPermission.None,
  34. MemoryAttribute.None));
  35. return KernelResult.Success;
  36. }
  37. public void InsertBlock(
  38. ulong baseAddress,
  39. ulong pagesCount,
  40. MemoryState oldState,
  41. KMemoryPermission oldPermission,
  42. MemoryAttribute oldAttribute,
  43. MemoryState newState,
  44. KMemoryPermission newPermission,
  45. MemoryAttribute newAttribute)
  46. {
  47. // Insert new block on the list only on areas where the state
  48. // of the block matches the state specified on the old* state
  49. // arguments, otherwise leave it as is.
  50. int oldCount = _blockTree.Count;
  51. oldAttribute |= MemoryAttribute.IpcAndDeviceMapped;
  52. ulong endAddr = baseAddress + pagesCount * PageSize;
  53. KMemoryBlock currBlock = FindBlock(baseAddress);
  54. while (currBlock != null)
  55. {
  56. ulong currBaseAddr = currBlock.BaseAddress;
  57. ulong currEndAddr = currBlock.PagesCount * PageSize + currBaseAddr;
  58. if (baseAddress < currEndAddr && currBaseAddr < endAddr)
  59. {
  60. MemoryAttribute currBlockAttr = currBlock.Attribute | MemoryAttribute.IpcAndDeviceMapped;
  61. if (currBlock.State != oldState ||
  62. currBlock.Permission != oldPermission ||
  63. currBlockAttr != oldAttribute)
  64. {
  65. currBlock = currBlock.Successor;
  66. continue;
  67. }
  68. if (baseAddress > currBaseAddr)
  69. {
  70. KMemoryBlock newBlock = currBlock.SplitRightAtAddress(baseAddress);
  71. _blockTree.Add(newBlock);
  72. }
  73. if (endAddr < currEndAddr)
  74. {
  75. KMemoryBlock newBlock = currBlock.SplitRightAtAddress(endAddr);
  76. _blockTree.Add(newBlock);
  77. currBlock = newBlock;
  78. }
  79. currBlock.SetState(newPermission, newState, newAttribute);
  80. currBlock = MergeEqualStateNeighbors(currBlock);
  81. }
  82. if (currEndAddr - 1 >= endAddr - 1)
  83. {
  84. break;
  85. }
  86. currBlock = currBlock.Successor;
  87. }
  88. _slabManager.Count += _blockTree.Count - oldCount;
  89. ValidateInternalState();
  90. }
  91. public void InsertBlock(
  92. ulong baseAddress,
  93. ulong pagesCount,
  94. MemoryState state,
  95. KMemoryPermission permission = KMemoryPermission.None,
  96. MemoryAttribute attribute = MemoryAttribute.None)
  97. {
  98. // Inserts new block at the list, replacing and splitting
  99. // existing blocks as needed.
  100. int oldCount = _blockTree.Count;
  101. ulong endAddr = baseAddress + pagesCount * PageSize;
  102. KMemoryBlock currBlock = FindBlock(baseAddress);
  103. while (currBlock != null)
  104. {
  105. ulong currBaseAddr = currBlock.BaseAddress;
  106. ulong currEndAddr = currBlock.PagesCount * PageSize + currBaseAddr;
  107. if (baseAddress < currEndAddr && currBaseAddr < endAddr)
  108. {
  109. if (baseAddress > currBaseAddr)
  110. {
  111. KMemoryBlock newBlock = currBlock.SplitRightAtAddress(baseAddress);
  112. _blockTree.Add(newBlock);
  113. }
  114. if (endAddr < currEndAddr)
  115. {
  116. KMemoryBlock newBlock = currBlock.SplitRightAtAddress(endAddr);
  117. _blockTree.Add(newBlock);
  118. currBlock = newBlock;
  119. }
  120. currBlock.SetState(permission, state, attribute);
  121. currBlock = MergeEqualStateNeighbors(currBlock);
  122. }
  123. if (currEndAddr - 1 >= endAddr - 1)
  124. {
  125. break;
  126. }
  127. currBlock = currBlock.Successor;
  128. }
  129. _slabManager.Count += _blockTree.Count - oldCount;
  130. ValidateInternalState();
  131. }
  132. public delegate void BlockMutator(KMemoryBlock block, KMemoryPermission newPerm);
  133. public void InsertBlock(
  134. ulong baseAddress,
  135. ulong pagesCount,
  136. BlockMutator blockMutate,
  137. KMemoryPermission permission = KMemoryPermission.None)
  138. {
  139. // Inserts new block at the list, replacing and splitting
  140. // existing blocks as needed, then calling the callback
  141. // function on the new block.
  142. int oldCount = _blockTree.Count;
  143. ulong endAddr = baseAddress + pagesCount * PageSize;
  144. KMemoryBlock currBlock = FindBlock(baseAddress);
  145. while (currBlock != null)
  146. {
  147. ulong currBaseAddr = currBlock.BaseAddress;
  148. ulong currEndAddr = currBlock.PagesCount * PageSize + currBaseAddr;
  149. if (baseAddress < currEndAddr && currBaseAddr < endAddr)
  150. {
  151. if (baseAddress > currBaseAddr)
  152. {
  153. KMemoryBlock newBlock = currBlock.SplitRightAtAddress(baseAddress);
  154. _blockTree.Add(newBlock);
  155. }
  156. if (endAddr < currEndAddr)
  157. {
  158. KMemoryBlock newBlock = currBlock.SplitRightAtAddress(endAddr);
  159. _blockTree.Add(newBlock);
  160. currBlock = newBlock;
  161. }
  162. blockMutate(currBlock, permission);
  163. currBlock = MergeEqualStateNeighbors(currBlock);
  164. }
  165. if (currEndAddr - 1 >= endAddr - 1)
  166. {
  167. break;
  168. }
  169. currBlock = currBlock.Successor;
  170. }
  171. _slabManager.Count += _blockTree.Count - oldCount;
  172. ValidateInternalState();
  173. }
  174. [Conditional("DEBUG")]
  175. private void ValidateInternalState()
  176. {
  177. ulong expectedAddress = 0;
  178. KMemoryBlock currBlock = FindBlock(_addrSpaceStart);
  179. while (currBlock != null)
  180. {
  181. Debug.Assert(currBlock.BaseAddress == expectedAddress);
  182. expectedAddress = currBlock.BaseAddress + currBlock.PagesCount * PageSize;
  183. currBlock = currBlock.Successor;
  184. }
  185. Debug.Assert(expectedAddress == _addrSpaceEnd);
  186. }
  187. private KMemoryBlock MergeEqualStateNeighbors(KMemoryBlock block)
  188. {
  189. KMemoryBlock previousBlock = block.Predecessor;
  190. KMemoryBlock nextBlock = block.Successor;
  191. if (previousBlock != null && BlockStateEquals(block, previousBlock))
  192. {
  193. _blockTree.Remove(block);
  194. previousBlock.AddPages(block.PagesCount);
  195. block = previousBlock;
  196. }
  197. if (nextBlock != null && BlockStateEquals(block, nextBlock))
  198. {
  199. _blockTree.Remove(nextBlock);
  200. block.AddPages(nextBlock.PagesCount);
  201. }
  202. return block;
  203. }
  204. private static bool BlockStateEquals(KMemoryBlock lhs, KMemoryBlock rhs)
  205. {
  206. return lhs.State == rhs.State &&
  207. lhs.Permission == rhs.Permission &&
  208. lhs.Attribute == rhs.Attribute &&
  209. lhs.SourcePermission == rhs.SourcePermission &&
  210. lhs.DeviceRefCount == rhs.DeviceRefCount &&
  211. lhs.IpcRefCount == rhs.IpcRefCount;
  212. }
  213. public KMemoryBlock FindBlock(ulong address)
  214. {
  215. return _blockTree.GetNodeByKey(address);
  216. }
  217. }
  218. }