KPageHeap.cs 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. using Ryujinx.Common;
  2. using System;
  3. namespace Ryujinx.HLE.HOS.Kernel.Memory
  4. {
  5. class KPageHeap
  6. {
  7. private class Block
  8. {
  9. private KPageBitmap _bitmap = new KPageBitmap();
  10. private ulong _heapAddress;
  11. private ulong _endOffset;
  12. public int Shift { get; private set; }
  13. public int NextShift { get; private set; }
  14. public ulong Size => 1UL << Shift;
  15. public int PagesCount => (int)(Size / KPageTableBase.PageSize);
  16. public int FreeBlocksCount => _bitmap.BitsCount;
  17. public int FreePagesCount => FreeBlocksCount * PagesCount;
  18. public ArraySegment<ulong> Initialize(ulong address, ulong size, int blockShift, int nextBlockShift, ArraySegment<ulong> bitStorage)
  19. {
  20. Shift = blockShift;
  21. NextShift = nextBlockShift;
  22. ulong endAddress = address + size;
  23. ulong align = nextBlockShift != 0
  24. ? 1UL << nextBlockShift
  25. : 1UL << blockShift;
  26. address = BitUtils.AlignDown(address, align);
  27. endAddress = BitUtils.AlignUp (endAddress, align);
  28. _heapAddress = address;
  29. _endOffset = (endAddress - address) / (1UL << blockShift);
  30. return _bitmap.Initialize(bitStorage, _endOffset);
  31. }
  32. public ulong PushBlock(ulong address)
  33. {
  34. ulong offset = (address - _heapAddress) >> Shift;
  35. _bitmap.SetBit(offset);
  36. if (NextShift != 0)
  37. {
  38. int diff = 1 << (NextShift - Shift);
  39. offset = BitUtils.AlignDown(offset, (ulong)diff);
  40. if (_bitmap.ClearRange(offset, diff))
  41. {
  42. return _heapAddress + (offset << Shift);
  43. }
  44. }
  45. return 0;
  46. }
  47. public ulong PopBlock(bool random)
  48. {
  49. long sOffset = (long)_bitmap.FindFreeBlock(random);
  50. if (sOffset < 0L)
  51. {
  52. return 0;
  53. }
  54. ulong offset = (ulong)sOffset;
  55. _bitmap.ClearBit(offset);
  56. return _heapAddress + (offset << Shift);
  57. }
  58. public static int CalculateManagementOverheadSize(ulong regionSize, int currBlockShift, int nextBlockShift)
  59. {
  60. ulong currBlockSize = 1UL << currBlockShift;
  61. ulong nextBlockSize = 1UL << nextBlockShift;
  62. ulong align = nextBlockShift != 0 ? nextBlockSize : currBlockSize;
  63. return KPageBitmap.CalculateManagementOverheadSize((align * 2 + BitUtils.AlignUp(regionSize, align)) / currBlockSize);
  64. }
  65. }
  66. private static readonly int[] _memoryBlockPageShifts = new int[] { 12, 16, 21, 22, 25, 29, 30 };
  67. private readonly ulong _heapAddress;
  68. private readonly ulong _heapSize;
  69. private ulong _usedSize;
  70. private readonly int _blocksCount;
  71. private readonly Block[] _blocks;
  72. public KPageHeap(ulong address, ulong size) : this(address, size, _memoryBlockPageShifts)
  73. {
  74. }
  75. public KPageHeap(ulong address, ulong size, int[] blockShifts)
  76. {
  77. _heapAddress = address;
  78. _heapSize = size;
  79. _blocksCount = blockShifts.Length;
  80. _blocks = new Block[_memoryBlockPageShifts.Length];
  81. var currBitmapStorage = new ArraySegment<ulong>(new ulong[CalculateManagementOverheadSize(size, blockShifts)]);
  82. for (int i = 0; i < blockShifts.Length; i++)
  83. {
  84. int currBlockShift = blockShifts[i];
  85. int nextBlockShift = i != blockShifts.Length - 1 ? blockShifts[i + 1] : 0;
  86. _blocks[i] = new Block();
  87. currBitmapStorage = _blocks[i].Initialize(address, size, currBlockShift, nextBlockShift, currBitmapStorage);
  88. }
  89. }
  90. public void UpdateUsedSize()
  91. {
  92. _usedSize = _heapSize - (GetFreePagesCount() * KPageTableBase.PageSize);
  93. }
  94. public ulong GetFreePagesCount()
  95. {
  96. ulong freeCount = 0;
  97. for (int i = 0; i < _blocksCount; i++)
  98. {
  99. freeCount += (ulong)_blocks[i].FreePagesCount;
  100. }
  101. return freeCount;
  102. }
  103. public ulong AllocateBlock(int index, bool random)
  104. {
  105. ulong neededSize = _blocks[index].Size;
  106. for (int i = index; i < _blocksCount; i++)
  107. {
  108. ulong address = _blocks[i].PopBlock(random);
  109. if (address != 0)
  110. {
  111. ulong allocatedSize = _blocks[i].Size;
  112. if (allocatedSize > neededSize)
  113. {
  114. Free(address + neededSize, (allocatedSize - neededSize) / KPageTableBase.PageSize);
  115. }
  116. return address;
  117. }
  118. }
  119. return 0;
  120. }
  121. private void FreeBlock(ulong block, int index)
  122. {
  123. do
  124. {
  125. block = _blocks[index++].PushBlock(block);
  126. }
  127. while (block != 0);
  128. }
  129. public void Free(ulong address, ulong pagesCount)
  130. {
  131. if (pagesCount == 0)
  132. {
  133. return;
  134. }
  135. int bigIndex = _blocksCount - 1;
  136. ulong start = address;
  137. ulong end = address + pagesCount * KPageTableBase.PageSize;
  138. ulong beforeStart = start;
  139. ulong beforeEnd = start;
  140. ulong afterStart = end;
  141. ulong afterEnd = end;
  142. while (bigIndex >= 0)
  143. {
  144. ulong blockSize = _blocks[bigIndex].Size;
  145. ulong bigStart = BitUtils.AlignUp (start, blockSize);
  146. ulong bigEnd = BitUtils.AlignDown(end, blockSize);
  147. if (bigStart < bigEnd)
  148. {
  149. for (ulong block = bigStart; block < bigEnd; block += blockSize)
  150. {
  151. FreeBlock(block, bigIndex);
  152. }
  153. beforeEnd = bigStart;
  154. afterStart = bigEnd;
  155. break;
  156. }
  157. bigIndex--;
  158. }
  159. for (int i = bigIndex - 1; i >= 0; i--)
  160. {
  161. ulong blockSize = _blocks[i].Size;
  162. while (beforeStart + blockSize <= beforeEnd)
  163. {
  164. beforeEnd -= blockSize;
  165. FreeBlock(beforeEnd, i);
  166. }
  167. }
  168. for (int i = bigIndex - 1; i >= 0; i--)
  169. {
  170. ulong blockSize = _blocks[i].Size;
  171. while (afterStart + blockSize <= afterEnd)
  172. {
  173. FreeBlock(afterStart, i);
  174. afterStart += blockSize;
  175. }
  176. }
  177. }
  178. public static int GetAlignedBlockIndex(ulong pagesCount, ulong alignPages)
  179. {
  180. ulong targetPages = Math.Max(pagesCount, alignPages);
  181. for (int i = 0; i < _memoryBlockPageShifts.Length; i++)
  182. {
  183. if (targetPages <= GetBlockPagesCount(i))
  184. {
  185. return i;
  186. }
  187. }
  188. return -1;
  189. }
  190. public static int GetBlockIndex(ulong pagesCount)
  191. {
  192. for (int i = _memoryBlockPageShifts.Length - 1; i >= 0; i--)
  193. {
  194. if (pagesCount >= GetBlockPagesCount(i))
  195. {
  196. return i;
  197. }
  198. }
  199. return -1;
  200. }
  201. public static ulong GetBlockSize(int index)
  202. {
  203. return 1UL << _memoryBlockPageShifts[index];
  204. }
  205. public static ulong GetBlockPagesCount(int index)
  206. {
  207. return GetBlockSize(index) / KPageTableBase.PageSize;
  208. }
  209. private static int CalculateManagementOverheadSize(ulong regionSize, int[] blockShifts)
  210. {
  211. int overheadSize = 0;
  212. for (int i = 0; i < blockShifts.Length; i++)
  213. {
  214. int currBlockShift = blockShifts[i];
  215. int nextBlockShift = i != blockShifts.Length - 1 ? blockShifts[i + 1] : 0;
  216. overheadSize += Block.CalculateManagementOverheadSize(regionSize, currBlockShift, nextBlockShift);
  217. }
  218. return BitUtils.AlignUp(overheadSize, KPageTableBase.PageSize);
  219. }
  220. }
  221. }