| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283 |
- using Ryujinx.Common;
- using System;
- namespace Ryujinx.HLE.HOS.Kernel.Memory
- {
- class KPageHeap
- {
- private class Block
- {
- private KPageBitmap _bitmap = new KPageBitmap();
- private ulong _heapAddress;
- private ulong _endOffset;
- public int Shift { get; private set; }
- public int NextShift { get; private set; }
- public ulong Size => 1UL << Shift;
- public int PagesCount => (int)(Size / KPageTableBase.PageSize);
- public int FreeBlocksCount => _bitmap.BitsCount;
- public int FreePagesCount => FreeBlocksCount * PagesCount;
- public ArraySegment<ulong> Initialize(ulong address, ulong size, int blockShift, int nextBlockShift, ArraySegment<ulong> bitStorage)
- {
- Shift = blockShift;
- NextShift = nextBlockShift;
- ulong endAddress = address + size;
- ulong align = nextBlockShift != 0
- ? 1UL << nextBlockShift
- : 1UL << blockShift;
- address = BitUtils.AlignDown(address, align);
- endAddress = BitUtils.AlignUp (endAddress, align);
- _heapAddress = address;
- _endOffset = (endAddress - address) / (1UL << blockShift);
- return _bitmap.Initialize(bitStorage, _endOffset);
- }
- public ulong PushBlock(ulong address)
- {
- ulong offset = (address - _heapAddress) >> Shift;
- _bitmap.SetBit(offset);
- if (NextShift != 0)
- {
- int diff = 1 << (NextShift - Shift);
- offset = BitUtils.AlignDown(offset, (ulong)diff);
- if (_bitmap.ClearRange(offset, diff))
- {
- return _heapAddress + (offset << Shift);
- }
- }
- return 0;
- }
- public ulong PopBlock(bool random)
- {
- long sOffset = (long)_bitmap.FindFreeBlock(random);
- if (sOffset < 0L)
- {
- return 0;
- }
- ulong offset = (ulong)sOffset;
- _bitmap.ClearBit(offset);
- return _heapAddress + (offset << Shift);
- }
- public static int CalculateManagementOverheadSize(ulong regionSize, int currBlockShift, int nextBlockShift)
- {
- ulong currBlockSize = 1UL << currBlockShift;
- ulong nextBlockSize = 1UL << nextBlockShift;
- ulong align = nextBlockShift != 0 ? nextBlockSize : currBlockSize;
- return KPageBitmap.CalculateManagementOverheadSize((align * 2 + BitUtils.AlignUp(regionSize, align)) / currBlockSize);
- }
- }
- private static readonly int[] _memoryBlockPageShifts = new int[] { 12, 16, 21, 22, 25, 29, 30 };
- private readonly ulong _heapAddress;
- private readonly ulong _heapSize;
- private ulong _usedSize;
- private readonly int _blocksCount;
- private readonly Block[] _blocks;
- public KPageHeap(ulong address, ulong size) : this(address, size, _memoryBlockPageShifts)
- {
- }
- public KPageHeap(ulong address, ulong size, int[] blockShifts)
- {
- _heapAddress = address;
- _heapSize = size;
- _blocksCount = blockShifts.Length;
- _blocks = new Block[_memoryBlockPageShifts.Length];
- var currBitmapStorage = new ArraySegment<ulong>(new ulong[CalculateManagementOverheadSize(size, blockShifts)]);
- for (int i = 0; i < blockShifts.Length; i++)
- {
- int currBlockShift = blockShifts[i];
- int nextBlockShift = i != blockShifts.Length - 1 ? blockShifts[i + 1] : 0;
- _blocks[i] = new Block();
- currBitmapStorage = _blocks[i].Initialize(address, size, currBlockShift, nextBlockShift, currBitmapStorage);
- }
- }
- public void UpdateUsedSize()
- {
- _usedSize = _heapSize - (GetFreePagesCount() * KPageTableBase.PageSize);
- }
- public ulong GetFreePagesCount()
- {
- ulong freeCount = 0;
- for (int i = 0; i < _blocksCount; i++)
- {
- freeCount += (ulong)_blocks[i].FreePagesCount;
- }
- return freeCount;
- }
- public ulong AllocateBlock(int index, bool random)
- {
- ulong neededSize = _blocks[index].Size;
- for (int i = index; i < _blocksCount; i++)
- {
- ulong address = _blocks[i].PopBlock(random);
- if (address != 0)
- {
- ulong allocatedSize = _blocks[i].Size;
- if (allocatedSize > neededSize)
- {
- Free(address + neededSize, (allocatedSize - neededSize) / KPageTableBase.PageSize);
- }
- return address;
- }
- }
- return 0;
- }
- private void FreeBlock(ulong block, int index)
- {
- do
- {
- block = _blocks[index++].PushBlock(block);
- }
- while (block != 0);
- }
- public void Free(ulong address, ulong pagesCount)
- {
- if (pagesCount == 0)
- {
- return;
- }
- int bigIndex = _blocksCount - 1;
- ulong start = address;
- ulong end = address + pagesCount * KPageTableBase.PageSize;
- ulong beforeStart = start;
- ulong beforeEnd = start;
- ulong afterStart = end;
- ulong afterEnd = end;
- while (bigIndex >= 0)
- {
- ulong blockSize = _blocks[bigIndex].Size;
- ulong bigStart = BitUtils.AlignUp (start, blockSize);
- ulong bigEnd = BitUtils.AlignDown(end, blockSize);
- if (bigStart < bigEnd)
- {
- for (ulong block = bigStart; block < bigEnd; block += blockSize)
- {
- FreeBlock(block, bigIndex);
- }
- beforeEnd = bigStart;
- afterStart = bigEnd;
- break;
- }
- bigIndex--;
- }
- for (int i = bigIndex - 1; i >= 0; i--)
- {
- ulong blockSize = _blocks[i].Size;
- while (beforeStart + blockSize <= beforeEnd)
- {
- beforeEnd -= blockSize;
- FreeBlock(beforeEnd, i);
- }
- }
- for (int i = bigIndex - 1; i >= 0; i--)
- {
- ulong blockSize = _blocks[i].Size;
- while (afterStart + blockSize <= afterEnd)
- {
- FreeBlock(afterStart, i);
- afterStart += blockSize;
- }
- }
- }
- public static int GetAlignedBlockIndex(ulong pagesCount, ulong alignPages)
- {
- ulong targetPages = Math.Max(pagesCount, alignPages);
- for (int i = 0; i < _memoryBlockPageShifts.Length; i++)
- {
- if (targetPages <= GetBlockPagesCount(i))
- {
- return i;
- }
- }
- return -1;
- }
- public static int GetBlockIndex(ulong pagesCount)
- {
- for (int i = _memoryBlockPageShifts.Length - 1; i >= 0; i--)
- {
- if (pagesCount >= GetBlockPagesCount(i))
- {
- return i;
- }
- }
- return -1;
- }
- public static ulong GetBlockSize(int index)
- {
- return 1UL << _memoryBlockPageShifts[index];
- }
- public static ulong GetBlockPagesCount(int index)
- {
- return GetBlockSize(index) / KPageTableBase.PageSize;
- }
- private static int CalculateManagementOverheadSize(ulong regionSize, int[] blockShifts)
- {
- int overheadSize = 0;
- for (int i = 0; i < blockShifts.Length; i++)
- {
- int currBlockShift = blockShifts[i];
- int nextBlockShift = i != blockShifts.Length - 1 ? blockShifts[i + 1] : 0;
- overheadSize += Block.CalculateManagementOverheadSize(regionSize, currBlockShift, nextBlockShift);
- }
- return BitUtils.AlignUp(overheadSize, KPageTableBase.PageSize);
- }
- }
- }
|