| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428 |
- using Ryujinx.Common;
- namespace Ryujinx.HLE.HOS.Kernel
- {
- class KMemoryRegionManager
- {
- private static readonly int[] BlockOrders = new int[] { 12, 16, 21, 22, 25, 29, 30 };
- public ulong Address { get; private set; }
- public ulong EndAddr { get; private set; }
- public ulong Size { get; private set; }
- private int BlockOrdersCount;
- private KMemoryRegionBlock[] Blocks;
- public KMemoryRegionManager(ulong Address, ulong Size, ulong EndAddr)
- {
- Blocks = new KMemoryRegionBlock[BlockOrders.Length];
- this.Address = Address;
- this.Size = Size;
- this.EndAddr = EndAddr;
- BlockOrdersCount = BlockOrders.Length;
- for (int BlockIndex = 0; BlockIndex < BlockOrdersCount; BlockIndex++)
- {
- Blocks[BlockIndex] = new KMemoryRegionBlock();
- Blocks[BlockIndex].Order = BlockOrders[BlockIndex];
- int NextOrder = BlockIndex == BlockOrdersCount - 1 ? 0 : BlockOrders[BlockIndex + 1];
- Blocks[BlockIndex].NextOrder = NextOrder;
- int CurrBlockSize = 1 << BlockOrders[BlockIndex];
- int NextBlockSize = CurrBlockSize;
- if (NextOrder != 0)
- {
- NextBlockSize = 1 << NextOrder;
- }
- ulong StartAligned = BitUtils.AlignDown(Address, NextBlockSize);
- ulong EndAddrAligned = BitUtils.AlignDown(EndAddr, CurrBlockSize);
- ulong SizeInBlocksTruncated = (EndAddrAligned - StartAligned) >> BlockOrders[BlockIndex];
- ulong EndAddrRounded = BitUtils.AlignUp(Address + Size, NextBlockSize);
- ulong SizeInBlocksRounded = (EndAddrRounded - StartAligned) >> BlockOrders[BlockIndex];
- Blocks[BlockIndex].StartAligned = StartAligned;
- Blocks[BlockIndex].SizeInBlocksTruncated = SizeInBlocksTruncated;
- Blocks[BlockIndex].SizeInBlocksRounded = SizeInBlocksRounded;
- ulong CurrSizeInBlocks = SizeInBlocksRounded;
- int MaxLevel = 0;
- do
- {
- MaxLevel++;
- }
- while ((CurrSizeInBlocks /= 64) != 0);
- Blocks[BlockIndex].MaxLevel = MaxLevel;
- Blocks[BlockIndex].Masks = new long[MaxLevel][];
- CurrSizeInBlocks = SizeInBlocksRounded;
- for (int Level = MaxLevel - 1; Level >= 0; Level--)
- {
- CurrSizeInBlocks = (CurrSizeInBlocks + 63) / 64;
- Blocks[BlockIndex].Masks[Level] = new long[CurrSizeInBlocks];
- }
- }
- if (Size != 0)
- {
- FreePages(Address, Size / KMemoryManager.PageSize);
- }
- }
- public KernelResult AllocatePages(ulong PagesCount, bool Backwards, out KPageList PageList)
- {
- lock (Blocks)
- {
- return AllocatePagesImpl(PagesCount, Backwards, out PageList);
- }
- }
- private KernelResult AllocatePagesImpl(ulong PagesCount, bool Backwards, out KPageList PageList)
- {
- PageList = new KPageList();
- if (BlockOrdersCount > 0)
- {
- if (GetFreePagesImpl() < PagesCount)
- {
- return KernelResult.OutOfMemory;
- }
- }
- else if (PagesCount != 0)
- {
- return KernelResult.OutOfMemory;
- }
- for (int BlockIndex = BlockOrdersCount - 1; BlockIndex >= 0; BlockIndex--)
- {
- KMemoryRegionBlock Block = Blocks[BlockIndex];
- ulong BestFitBlockSize = 1UL << Block.Order;
- ulong BlockPagesCount = BestFitBlockSize / KMemoryManager.PageSize;
- //Check if this is the best fit for this page size.
- //If so, try allocating as much requested pages as possible.
- while (BlockPagesCount <= PagesCount)
- {
- ulong Address = 0;
- for (int CurrBlockIndex = BlockIndex;
- CurrBlockIndex < BlockOrdersCount && Address == 0;
- CurrBlockIndex++)
- {
- Block = Blocks[CurrBlockIndex];
- int Index = 0;
- bool ZeroMask = false;
- for (int Level = 0; Level < Block.MaxLevel; Level++)
- {
- long Mask = Block.Masks[Level][Index];
- if (Mask == 0)
- {
- ZeroMask = true;
- break;
- }
- if (Backwards)
- {
- Index = (Index * 64 + 63) - BitUtils.CountLeadingZeros64(Mask);
- }
- else
- {
- Index = Index * 64 + BitUtils.CountLeadingZeros64(BitUtils.ReverseBits64(Mask));
- }
- }
- if (Block.SizeInBlocksTruncated <= (ulong)Index || ZeroMask)
- {
- continue;
- }
- Block.FreeCount--;
- int TempIdx = Index;
- for (int Level = Block.MaxLevel - 1; Level >= 0; Level--, TempIdx /= 64)
- {
- Block.Masks[Level][TempIdx / 64] &= ~(1L << (TempIdx & 63));
- if (Block.Masks[Level][TempIdx / 64] != 0)
- {
- break;
- }
- }
- Address = Block.StartAligned + ((ulong)Index << Block.Order);
- }
- for (int CurrBlockIndex = BlockIndex;
- CurrBlockIndex < BlockOrdersCount && Address == 0;
- CurrBlockIndex++)
- {
- Block = Blocks[CurrBlockIndex];
- int Index = 0;
- bool ZeroMask = false;
- for (int Level = 0; Level < Block.MaxLevel; Level++)
- {
- long Mask = Block.Masks[Level][Index];
- if (Mask == 0)
- {
- ZeroMask = true;
- break;
- }
- if (Backwards)
- {
- Index = Index * 64 + BitUtils.CountLeadingZeros64(BitUtils.ReverseBits64(Mask));
- }
- else
- {
- Index = (Index * 64 + 63) - BitUtils.CountLeadingZeros64(Mask);
- }
- }
- if (Block.SizeInBlocksTruncated <= (ulong)Index || ZeroMask)
- {
- continue;
- }
- Block.FreeCount--;
- int TempIdx = Index;
- for (int Level = Block.MaxLevel - 1; Level >= 0; Level--, TempIdx /= 64)
- {
- Block.Masks[Level][TempIdx / 64] &= ~(1L << (TempIdx & 63));
- if (Block.Masks[Level][TempIdx / 64] != 0)
- {
- break;
- }
- }
- Address = Block.StartAligned + ((ulong)Index << Block.Order);
- }
- //The address being zero means that no free space was found on that order,
- //just give up and try with the next one.
- if (Address == 0)
- {
- break;
- }
- //If we are using a larger order than best fit, then we should
- //split it into smaller blocks.
- ulong FirstFreeBlockSize = 1UL << Block.Order;
- if (FirstFreeBlockSize > BestFitBlockSize)
- {
- FreePages(Address + BestFitBlockSize, (FirstFreeBlockSize - BestFitBlockSize) / KMemoryManager.PageSize);
- }
- //Add new allocated page(s) to the pages list.
- //If an error occurs, then free all allocated pages and fail.
- KernelResult Result = PageList.AddRange(Address, BlockPagesCount);
- if (Result != KernelResult.Success)
- {
- FreePages(Address, BlockPagesCount);
- foreach (KPageNode PageNode in PageList)
- {
- FreePages(PageNode.Address, PageNode.PagesCount);
- }
- return Result;
- }
- PagesCount -= BlockPagesCount;
- }
- }
- //Success case, all requested pages were allocated successfully.
- if (PagesCount == 0)
- {
- return KernelResult.Success;
- }
- //Error case, free allocated pages and return out of memory.
- foreach (KPageNode PageNode in PageList)
- {
- FreePages(PageNode.Address, PageNode.PagesCount);
- }
- PageList = null;
- return KernelResult.OutOfMemory;
- }
- public void FreePages(KPageList PageList)
- {
- lock (Blocks)
- {
- foreach (KPageNode PageNode in PageList)
- {
- FreePages(PageNode.Address, PageNode.PagesCount);
- }
- }
- }
- private void FreePages(ulong Address, ulong PagesCount)
- {
- ulong EndAddr = Address + PagesCount * KMemoryManager.PageSize;
- int BlockIndex = BlockOrdersCount - 1;
- ulong AddressRounded = 0;
- ulong EndAddrTruncated = 0;
- for (; BlockIndex >= 0; BlockIndex--)
- {
- KMemoryRegionBlock AllocInfo = Blocks[BlockIndex];
- int BlockSize = 1 << AllocInfo.Order;
- AddressRounded = BitUtils.AlignUp (Address, BlockSize);
- EndAddrTruncated = BitUtils.AlignDown(EndAddr, BlockSize);
- if (AddressRounded < EndAddrTruncated)
- {
- break;
- }
- }
- void FreeRegion(ulong CurrAddress)
- {
- for (int CurrBlockIndex = BlockIndex;
- CurrBlockIndex < BlockOrdersCount && CurrAddress != 0;
- CurrBlockIndex++)
- {
- KMemoryRegionBlock Block = Blocks[CurrBlockIndex];
- Block.FreeCount++;
- ulong FreedBlocks = (CurrAddress - Block.StartAligned) >> Block.Order;
- int Index = (int)FreedBlocks;
- for (int Level = Block.MaxLevel - 1; Level >= 0; Level--, Index /= 64)
- {
- long Mask = Block.Masks[Level][Index / 64];
- Block.Masks[Level][Index / 64] = Mask | (1L << (Index & 63));
- if (Mask != 0)
- {
- break;
- }
- }
- int BlockSizeDelta = 1 << (Block.NextOrder - Block.Order);
- int FreedBlocksTruncated = BitUtils.AlignDown((int)FreedBlocks, BlockSizeDelta);
- if (!Block.TryCoalesce(FreedBlocksTruncated, BlockSizeDelta))
- {
- break;
- }
- CurrAddress = Block.StartAligned + ((ulong)FreedBlocksTruncated << Block.Order);
- }
- }
- //Free inside aligned region.
- ulong BaseAddress = AddressRounded;
- while (BaseAddress < EndAddrTruncated)
- {
- ulong BlockSize = 1UL << Blocks[BlockIndex].Order;
- FreeRegion(BaseAddress);
- BaseAddress += BlockSize;
- }
- int NextBlockIndex = BlockIndex - 1;
- //Free region between Address and aligned region start.
- BaseAddress = AddressRounded;
- for (BlockIndex = NextBlockIndex; BlockIndex >= 0; BlockIndex--)
- {
- ulong BlockSize = 1UL << Blocks[BlockIndex].Order;
- while (BaseAddress - BlockSize >= Address)
- {
- BaseAddress -= BlockSize;
- FreeRegion(BaseAddress);
- }
- }
- //Free region between aligned region end and End Address.
- BaseAddress = EndAddrTruncated;
- for (BlockIndex = NextBlockIndex; BlockIndex >= 0; BlockIndex--)
- {
- ulong BlockSize = 1UL << Blocks[BlockIndex].Order;
- while (BaseAddress + BlockSize <= EndAddr)
- {
- FreeRegion(BaseAddress);
- BaseAddress += BlockSize;
- }
- }
- }
- public ulong GetFreePages()
- {
- lock (Blocks)
- {
- return GetFreePagesImpl();
- }
- }
- private ulong GetFreePagesImpl()
- {
- ulong AvailablePages = 0;
- for (int BlockIndex = 0; BlockIndex < BlockOrdersCount; BlockIndex++)
- {
- KMemoryRegionBlock Block = Blocks[BlockIndex];
- ulong BlockPagesCount = (1UL << Block.Order) / KMemoryManager.PageSize;
- AvailablePages += BlockPagesCount * Block.FreeCount;
- }
- return AvailablePages;
- }
- }
- }
|