diff options
Diffstat (limited to 'src/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryRegionManager.cs')
| -rw-r--r-- | src/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryRegionManager.cs | 242 |
1 files changed, 242 insertions, 0 deletions
diff --git a/src/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryRegionManager.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryRegionManager.cs new file mode 100644 index 00000000..4596b15d --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryRegionManager.cs @@ -0,0 +1,242 @@ +using Ryujinx.Horizon.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 Result AllocatePages(out KPageList pageList, ulong pagesCount) + { + if (pagesCount == 0) + { + pageList = new KPageList(); + + return Result.Success; + } + + lock (_pageHeap) + { + Result result = AllocatePagesImpl(out pageList, pagesCount, false); + + if (result == Result.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.CommitMemory(address - DramMemoryMap.DramBase, pagesCount * KPageTableBase.PageSize); + } + + return address; + } + } + + private Result 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; + } + + Result result = pageList.AddRange(allocatedBlock, pagesPerAlloc); + + if (result != Result.Success) + { + FreePages(pageList); + _pageHeap.Free(allocatedBlock, pagesPerAlloc); + + return result; + } + + pagesCount -= pagesPerAlloc; + } + } + + if (pagesCount != 0) + { + FreePages(pageList); + + return KernelResult.OutOfMemory; + } + + return Result.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; + } + } +}
\ No newline at end of file |
