KMemoryBlockManager.cs 10 KB

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