| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242 |
- using Ryujinx.HLE.HOS.Kernel.Common;
- using System.Diagnostics;
- namespace Ryujinx.HLE.HOS.Kernel.Memory
- {
- class KMemoryRegionManager
- {
- private readonly KPageHeap _pageHeap;
- public ulong Address { get; }
- public ulong Size { get; }
- public ulong EndAddr => Address + Size;
- private readonly ushort[] _pageReferenceCounts;
- public KMemoryRegionManager(ulong address, ulong size, ulong endAddr)
- {
- Address = address;
- Size = size;
- _pageReferenceCounts = new ushort[size / KPageTableBase.PageSize];
- _pageHeap = new KPageHeap(address, size);
- _pageHeap.Free(address, size / KPageTableBase.PageSize);
- _pageHeap.UpdateUsedSize();
- }
- public KernelResult AllocatePages(out KPageList pageList, ulong pagesCount)
- {
- if (pagesCount == 0)
- {
- pageList = new KPageList();
- return KernelResult.Success;
- }
- lock (_pageHeap)
- {
- KernelResult result = AllocatePagesImpl(out pageList, pagesCount, false);
- if (result == KernelResult.Success)
- {
- foreach (var node in pageList)
- {
- IncrementPagesReferenceCount(node.Address, node.PagesCount);
- }
- }
- return result;
- }
- }
- public ulong AllocatePagesContiguous(KernelContext context, ulong pagesCount, bool backwards)
- {
- if (pagesCount == 0)
- {
- return 0;
- }
- lock (_pageHeap)
- {
- ulong address = AllocatePagesContiguousImpl(pagesCount, 1, backwards);
- if (address != 0)
- {
- IncrementPagesReferenceCount(address, pagesCount);
- context.Memory.Commit(address - DramMemoryMap.DramBase, pagesCount * KPageTableBase.PageSize);
- }
- return address;
- }
- }
- private KernelResult AllocatePagesImpl(out KPageList pageList, ulong pagesCount, bool random)
- {
- pageList = new KPageList();
- int heapIndex = KPageHeap.GetBlockIndex(pagesCount);
- if (heapIndex < 0)
- {
- return KernelResult.OutOfMemory;
- }
- for (int index = heapIndex; index >= 0; index--)
- {
- ulong pagesPerAlloc = KPageHeap.GetBlockPagesCount(index);
- while (pagesCount >= pagesPerAlloc)
- {
- ulong allocatedBlock = _pageHeap.AllocateBlock(index, random);
- if (allocatedBlock == 0)
- {
- break;
- }
- KernelResult result = pageList.AddRange(allocatedBlock, pagesPerAlloc);
- if (result != KernelResult.Success)
- {
- FreePages(pageList);
- _pageHeap.Free(allocatedBlock, pagesPerAlloc);
- return result;
- }
- pagesCount -= pagesPerAlloc;
- }
- }
- if (pagesCount != 0)
- {
- FreePages(pageList);
- return KernelResult.OutOfMemory;
- }
- return KernelResult.Success;
- }
- private ulong AllocatePagesContiguousImpl(ulong pagesCount, ulong alignPages, bool random)
- {
- int heapIndex = KPageHeap.GetAlignedBlockIndex(pagesCount, alignPages);
- ulong allocatedBlock = _pageHeap.AllocateBlock(heapIndex, random);
- if (allocatedBlock == 0)
- {
- return 0;
- }
- ulong allocatedPages = KPageHeap.GetBlockPagesCount(heapIndex);
- if (allocatedPages > pagesCount)
- {
- _pageHeap.Free(allocatedBlock + pagesCount * KPageTableBase.PageSize, allocatedPages - pagesCount);
- }
- return allocatedBlock;
- }
- public void FreePage(ulong address)
- {
- lock (_pageHeap)
- {
- _pageHeap.Free(address, 1);
- }
- }
- public void FreePages(KPageList pageList)
- {
- lock (_pageHeap)
- {
- foreach (KPageNode pageNode in pageList)
- {
- _pageHeap.Free(pageNode.Address, pageNode.PagesCount);
- }
- }
- }
- public void FreePages(ulong address, ulong pagesCount)
- {
- lock (_pageHeap)
- {
- _pageHeap.Free(address, pagesCount);
- }
- }
- public ulong GetFreePages()
- {
- lock (_pageHeap)
- {
- return _pageHeap.GetFreePagesCount();
- }
- }
- public void IncrementPagesReferenceCount(ulong address, ulong pagesCount)
- {
- ulong index = GetPageOffset(address);
- ulong endIndex = index + pagesCount;
- while (index < endIndex)
- {
- ushort referenceCount = ++_pageReferenceCounts[index];
- Debug.Assert(referenceCount >= 1);
- index++;
- }
- }
- public void DecrementPagesReferenceCount(ulong address, ulong pagesCount)
- {
- ulong index = GetPageOffset(address);
- ulong endIndex = index + pagesCount;
- ulong freeBaseIndex = 0;
- ulong freePagesCount = 0;
- while (index < endIndex)
- {
- Debug.Assert(_pageReferenceCounts[index] > 0);
- ushort referenceCount = --_pageReferenceCounts[index];
- if (referenceCount == 0)
- {
- if (freePagesCount != 0)
- {
- freePagesCount++;
- }
- else
- {
- freeBaseIndex = index;
- freePagesCount = 1;
- }
- }
- else if (freePagesCount != 0)
- {
- FreePages(Address + freeBaseIndex * KPageTableBase.PageSize, freePagesCount);
- freePagesCount = 0;
- }
- index++;
- }
- if (freePagesCount != 0)
- {
- FreePages(Address + freeBaseIndex * KPageTableBase.PageSize, freePagesCount);
- }
- }
- public ulong GetPageOffset(ulong address)
- {
- return (address - Address) / KPageTableBase.PageSize;
- }
- public ulong GetPageOffsetFromEnd(ulong address)
- {
- return (EndAddr - address) / KPageTableBase.PageSize;
- }
- }
- }
|