KPageHeap.cs 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  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 readonly KPageBitmap _bitmap = new();
  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 = { 12, 16, 21, 22, 25, 29, 30 };
  67. #pragma warning disable IDE0052 // Remove unread private member
  68. private readonly ulong _heapAddress;
  69. private readonly ulong _heapSize;
  70. private ulong _usedSize;
  71. #pragma warning restore IDE0052
  72. private readonly int _blocksCount;
  73. private readonly Block[] _blocks;
  74. public KPageHeap(ulong address, ulong size) : this(address, size, _memoryBlockPageShifts)
  75. {
  76. }
  77. public KPageHeap(ulong address, ulong size, int[] blockShifts)
  78. {
  79. _heapAddress = address;
  80. _heapSize = size;
  81. _blocksCount = blockShifts.Length;
  82. _blocks = new Block[_memoryBlockPageShifts.Length];
  83. ArraySegment<ulong> currBitmapStorage = new(new ulong[CalculateManagementOverheadSize(size, blockShifts)]);
  84. for (int i = 0; i < blockShifts.Length; i++)
  85. {
  86. int currBlockShift = blockShifts[i];
  87. int nextBlockShift = i != blockShifts.Length - 1 ? blockShifts[i + 1] : 0;
  88. _blocks[i] = new Block();
  89. currBitmapStorage = _blocks[i].Initialize(address, size, currBlockShift, nextBlockShift, currBitmapStorage);
  90. }
  91. }
  92. public void UpdateUsedSize()
  93. {
  94. _usedSize = _heapSize - (GetFreePagesCount() * KPageTableBase.PageSize);
  95. }
  96. public ulong GetFreePagesCount()
  97. {
  98. ulong freeCount = 0;
  99. for (int i = 0; i < _blocksCount; i++)
  100. {
  101. freeCount += (ulong)_blocks[i].FreePagesCount;
  102. }
  103. return freeCount;
  104. }
  105. public ulong AllocateBlock(int index, bool random)
  106. {
  107. ulong neededSize = _blocks[index].Size;
  108. for (int i = index; i < _blocksCount; i++)
  109. {
  110. ulong address = _blocks[i].PopBlock(random);
  111. if (address != 0)
  112. {
  113. ulong allocatedSize = _blocks[i].Size;
  114. if (allocatedSize > neededSize)
  115. {
  116. Free(address + neededSize, (allocatedSize - neededSize) / KPageTableBase.PageSize);
  117. }
  118. return address;
  119. }
  120. }
  121. return 0;
  122. }
  123. private void FreeBlock(ulong block, int index)
  124. {
  125. do
  126. {
  127. block = _blocks[index++].PushBlock(block);
  128. }
  129. while (block != 0);
  130. }
  131. public void Free(ulong address, ulong pagesCount)
  132. {
  133. if (pagesCount == 0)
  134. {
  135. return;
  136. }
  137. int bigIndex = _blocksCount - 1;
  138. ulong start = address;
  139. ulong end = address + pagesCount * KPageTableBase.PageSize;
  140. ulong beforeStart = start;
  141. ulong beforeEnd = start;
  142. ulong afterStart = end;
  143. ulong afterEnd = end;
  144. while (bigIndex >= 0)
  145. {
  146. ulong blockSize = _blocks[bigIndex].Size;
  147. ulong bigStart = BitUtils.AlignUp(start, blockSize);
  148. ulong bigEnd = BitUtils.AlignDown(end, blockSize);
  149. if (bigStart < bigEnd)
  150. {
  151. for (ulong block = bigStart; block < bigEnd; block += blockSize)
  152. {
  153. FreeBlock(block, bigIndex);
  154. }
  155. beforeEnd = bigStart;
  156. afterStart = bigEnd;
  157. break;
  158. }
  159. bigIndex--;
  160. }
  161. for (int i = bigIndex - 1; i >= 0; i--)
  162. {
  163. ulong blockSize = _blocks[i].Size;
  164. while (beforeStart + blockSize <= beforeEnd)
  165. {
  166. beforeEnd -= blockSize;
  167. FreeBlock(beforeEnd, i);
  168. }
  169. }
  170. for (int i = bigIndex - 1; i >= 0; i--)
  171. {
  172. ulong blockSize = _blocks[i].Size;
  173. while (afterStart + blockSize <= afterEnd)
  174. {
  175. FreeBlock(afterStart, i);
  176. afterStart += blockSize;
  177. }
  178. }
  179. }
  180. public static int GetAlignedBlockIndex(ulong pagesCount, ulong alignPages)
  181. {
  182. ulong targetPages = Math.Max(pagesCount, alignPages);
  183. for (int i = 0; i < _memoryBlockPageShifts.Length; i++)
  184. {
  185. if (targetPages <= GetBlockPagesCount(i))
  186. {
  187. return i;
  188. }
  189. }
  190. return -1;
  191. }
  192. public static int GetBlockIndex(ulong pagesCount)
  193. {
  194. for (int i = _memoryBlockPageShifts.Length - 1; i >= 0; i--)
  195. {
  196. if (pagesCount >= GetBlockPagesCount(i))
  197. {
  198. return i;
  199. }
  200. }
  201. return -1;
  202. }
  203. public static ulong GetBlockSize(int index)
  204. {
  205. return 1UL << _memoryBlockPageShifts[index];
  206. }
  207. public static ulong GetBlockPagesCount(int index)
  208. {
  209. return GetBlockSize(index) / KPageTableBase.PageSize;
  210. }
  211. private static int CalculateManagementOverheadSize(ulong regionSize, int[] blockShifts)
  212. {
  213. int overheadSize = 0;
  214. for (int i = 0; i < blockShifts.Length; i++)
  215. {
  216. int currBlockShift = blockShifts[i];
  217. int nextBlockShift = i != blockShifts.Length - 1 ? blockShifts[i + 1] : 0;
  218. overheadSize += Block.CalculateManagementOverheadSize(regionSize, currBlockShift, nextBlockShift);
  219. }
  220. return BitUtils.AlignUp(overheadSize, KPageTableBase.PageSize);
  221. }
  222. }
  223. }