| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288 |
- using Ryujinx.Common.Collections;
- using Ryujinx.HLE.HOS.Kernel.Common;
- using System.Diagnostics;
- namespace Ryujinx.HLE.HOS.Kernel.Memory
- {
- class KMemoryBlockManager
- {
- private const int PageSize = KPageTableBase.PageSize;
- private readonly IntrusiveRedBlackTree<KMemoryBlock> _blockTree;
- public int BlocksCount => _blockTree.Count;
- private KMemoryBlockSlabManager _slabManager;
- private ulong _addrSpaceStart;
- private ulong _addrSpaceEnd;
- public KMemoryBlockManager()
- {
- _blockTree = new IntrusiveRedBlackTree<KMemoryBlock>();
- }
- public KernelResult Initialize(ulong addrSpaceStart, ulong addrSpaceEnd, KMemoryBlockSlabManager slabManager)
- {
- _slabManager = slabManager;
- _addrSpaceStart = addrSpaceStart;
- _addrSpaceEnd = addrSpaceEnd;
- // First insertion will always need only a single block, because there's nothing to split.
- if (!slabManager.CanAllocate(1))
- {
- return KernelResult.OutOfResource;
- }
- ulong addrSpacePagesCount = (addrSpaceEnd - addrSpaceStart) / PageSize;
- _blockTree.Add(new KMemoryBlock(
- addrSpaceStart,
- addrSpacePagesCount,
- MemoryState.Unmapped,
- KMemoryPermission.None,
- MemoryAttribute.None));
- return KernelResult.Success;
- }
- public void InsertBlock(
- ulong baseAddress,
- ulong pagesCount,
- MemoryState oldState,
- KMemoryPermission oldPermission,
- MemoryAttribute oldAttribute,
- MemoryState newState,
- KMemoryPermission newPermission,
- MemoryAttribute newAttribute)
- {
- // Insert new block on the list only on areas where the state
- // of the block matches the state specified on the old* state
- // arguments, otherwise leave it as is.
- int oldCount = _blockTree.Count;
- oldAttribute |= MemoryAttribute.IpcAndDeviceMapped;
- ulong endAddr = baseAddress + pagesCount * PageSize;
- KMemoryBlock currBlock = FindBlock(baseAddress);
- while (currBlock != null)
- {
- ulong currBaseAddr = currBlock.BaseAddress;
- ulong currEndAddr = currBlock.PagesCount * PageSize + currBaseAddr;
- if (baseAddress < currEndAddr && currBaseAddr < endAddr)
- {
- MemoryAttribute currBlockAttr = currBlock.Attribute | MemoryAttribute.IpcAndDeviceMapped;
- if (currBlock.State != oldState ||
- currBlock.Permission != oldPermission ||
- currBlockAttr != oldAttribute)
- {
- currBlock = currBlock.Successor;
- continue;
- }
- if (baseAddress > currBaseAddr)
- {
- KMemoryBlock newBlock = currBlock.SplitRightAtAddress(baseAddress);
- _blockTree.Add(newBlock);
- }
- if (endAddr < currEndAddr)
- {
- KMemoryBlock newBlock = currBlock.SplitRightAtAddress(endAddr);
- _blockTree.Add(newBlock);
- currBlock = newBlock;
- }
- currBlock.SetState(newPermission, newState, newAttribute);
- currBlock = MergeEqualStateNeighbors(currBlock);
- }
- if (currEndAddr - 1 >= endAddr - 1)
- {
- break;
- }
- currBlock = currBlock.Successor;
- }
- _slabManager.Count += _blockTree.Count - oldCount;
- ValidateInternalState();
- }
- public void InsertBlock(
- ulong baseAddress,
- ulong pagesCount,
- MemoryState state,
- KMemoryPermission permission = KMemoryPermission.None,
- MemoryAttribute attribute = MemoryAttribute.None)
- {
- // Inserts new block at the list, replacing and splitting
- // existing blocks as needed.
- int oldCount = _blockTree.Count;
- ulong endAddr = baseAddress + pagesCount * PageSize;
- KMemoryBlock currBlock = FindBlock(baseAddress);
- while (currBlock != null)
- {
- ulong currBaseAddr = currBlock.BaseAddress;
- ulong currEndAddr = currBlock.PagesCount * PageSize + currBaseAddr;
- if (baseAddress < currEndAddr && currBaseAddr < endAddr)
- {
- if (baseAddress > currBaseAddr)
- {
- KMemoryBlock newBlock = currBlock.SplitRightAtAddress(baseAddress);
- _blockTree.Add(newBlock);
- }
- if (endAddr < currEndAddr)
- {
- KMemoryBlock newBlock = currBlock.SplitRightAtAddress(endAddr);
- _blockTree.Add(newBlock);
- currBlock = newBlock;
- }
- currBlock.SetState(permission, state, attribute);
- currBlock = MergeEqualStateNeighbors(currBlock);
- }
- if (currEndAddr - 1 >= endAddr - 1)
- {
- break;
- }
- currBlock = currBlock.Successor;
- }
- _slabManager.Count += _blockTree.Count - oldCount;
- ValidateInternalState();
- }
- public delegate void BlockMutator(KMemoryBlock block, KMemoryPermission newPerm);
- public void InsertBlock(
- ulong baseAddress,
- ulong pagesCount,
- BlockMutator blockMutate,
- KMemoryPermission permission = KMemoryPermission.None)
- {
- // Inserts new block at the list, replacing and splitting
- // existing blocks as needed, then calling the callback
- // function on the new block.
- int oldCount = _blockTree.Count;
- ulong endAddr = baseAddress + pagesCount * PageSize;
- KMemoryBlock currBlock = FindBlock(baseAddress);
- while (currBlock != null)
- {
- ulong currBaseAddr = currBlock.BaseAddress;
- ulong currEndAddr = currBlock.PagesCount * PageSize + currBaseAddr;
- if (baseAddress < currEndAddr && currBaseAddr < endAddr)
- {
- if (baseAddress > currBaseAddr)
- {
- KMemoryBlock newBlock = currBlock.SplitRightAtAddress(baseAddress);
- _blockTree.Add(newBlock);
- }
- if (endAddr < currEndAddr)
- {
- KMemoryBlock newBlock = currBlock.SplitRightAtAddress(endAddr);
- _blockTree.Add(newBlock);
- currBlock = newBlock;
- }
- blockMutate(currBlock, permission);
- currBlock = MergeEqualStateNeighbors(currBlock);
- }
- if (currEndAddr - 1 >= endAddr - 1)
- {
- break;
- }
- currBlock = currBlock.Successor;
- }
- _slabManager.Count += _blockTree.Count - oldCount;
- ValidateInternalState();
- }
- [Conditional("DEBUG")]
- private void ValidateInternalState()
- {
- ulong expectedAddress = 0;
- KMemoryBlock currBlock = FindBlock(_addrSpaceStart);
- while (currBlock != null)
- {
- Debug.Assert(currBlock.BaseAddress == expectedAddress);
- expectedAddress = currBlock.BaseAddress + currBlock.PagesCount * PageSize;
- currBlock = currBlock.Successor;
- }
- Debug.Assert(expectedAddress == _addrSpaceEnd);
- }
- private KMemoryBlock MergeEqualStateNeighbors(KMemoryBlock block)
- {
- KMemoryBlock previousBlock = block.Predecessor;
- KMemoryBlock nextBlock = block.Successor;
- if (previousBlock != null && BlockStateEquals(block, previousBlock))
- {
- _blockTree.Remove(block);
- previousBlock.AddPages(block.PagesCount);
- block = previousBlock;
- }
- if (nextBlock != null && BlockStateEquals(block, nextBlock))
- {
- _blockTree.Remove(nextBlock);
- block.AddPages(nextBlock.PagesCount);
- }
- return block;
- }
- private static bool BlockStateEquals(KMemoryBlock lhs, KMemoryBlock rhs)
- {
- return lhs.State == rhs.State &&
- lhs.Permission == rhs.Permission &&
- lhs.Attribute == rhs.Attribute &&
- lhs.SourcePermission == rhs.SourcePermission &&
- lhs.DeviceRefCount == rhs.DeviceRefCount &&
- lhs.IpcRefCount == rhs.IpcRefCount;
- }
- public KMemoryBlock FindBlock(ulong address)
- {
- return _blockTree.GetNodeByKey(address);
- }
- }
- }
|