Move solution and projects to src
This commit is contained in:
parent
cd124bda58
commit
cee7121058
3466 changed files with 55 additions and 55 deletions
10
src/Ryujinx.HLE/HOS/Kernel/Memory/AddressSpaceType.cs
Normal file
10
src/Ryujinx.HLE/HOS/Kernel/Memory/AddressSpaceType.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
{
|
||||
enum AddressSpaceType
|
||||
{
|
||||
Addr32Bits = 0,
|
||||
Addr36Bits = 1,
|
||||
Addr32BitsNoMap = 2,
|
||||
Addr39Bits = 3
|
||||
}
|
||||
}
|
18
src/Ryujinx.HLE/HOS/Kernel/Memory/DramMemoryMap.cs
Normal file
18
src/Ryujinx.HLE/HOS/Kernel/Memory/DramMemoryMap.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
{
|
||||
static class DramMemoryMap
|
||||
{
|
||||
public const ulong DramBase = 0x80000000;
|
||||
|
||||
public const ulong KernelReserveBase = DramBase + 0x60000;
|
||||
|
||||
public const ulong SlabHeapBase = KernelReserveBase + 0x85000;
|
||||
public const ulong SlapHeapSize = 0xa21000;
|
||||
public const ulong SlabHeapEnd = SlabHeapBase + SlapHeapSize;
|
||||
|
||||
public static bool IsHeapPhysicalAddress(ulong address)
|
||||
{
|
||||
return address >= SlabHeapEnd;
|
||||
}
|
||||
}
|
||||
}
|
169
src/Ryujinx.HLE/HOS/Kernel/Memory/KCodeMemory.cs
Normal file
169
src/Ryujinx.HLE/HOS/Kernel/Memory/KCodeMemory.cs
Normal file
|
@ -0,0 +1,169 @@
|
|||
using Ryujinx.Common;
|
||||
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
{
|
||||
class KCodeMemory : KAutoObject
|
||||
{
|
||||
public KProcess Owner { get; private set; }
|
||||
private readonly KPageList _pageList;
|
||||
private readonly object _lock;
|
||||
private ulong _address;
|
||||
private bool _isOwnerMapped;
|
||||
private bool _isMapped;
|
||||
|
||||
public KCodeMemory(KernelContext context) : base(context)
|
||||
{
|
||||
_pageList = new KPageList();
|
||||
_lock = new object();
|
||||
}
|
||||
|
||||
public Result Initialize(ulong address, ulong size)
|
||||
{
|
||||
Owner = KernelStatic.GetCurrentProcess();
|
||||
|
||||
Result result = Owner.MemoryManager.BorrowCodeMemory(_pageList, address, size);
|
||||
|
||||
if (result != Result.Success)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
Owner.CpuMemory.Fill(address, size, 0xff);
|
||||
Owner.IncrementReferenceCount();
|
||||
|
||||
_address = address;
|
||||
_isMapped = false;
|
||||
_isOwnerMapped = false;
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result Map(ulong address, ulong size, KMemoryPermission perm)
|
||||
{
|
||||
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp<ulong>(size, (ulong)KPageTableBase.PageSize))
|
||||
{
|
||||
return KernelResult.InvalidSize;
|
||||
}
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
if (_isMapped)
|
||||
{
|
||||
return KernelResult.InvalidState;
|
||||
}
|
||||
|
||||
KProcess process = KernelStatic.GetCurrentProcess();
|
||||
|
||||
Result result = process.MemoryManager.MapPages(address, _pageList, MemoryState.CodeWritable, KMemoryPermission.ReadAndWrite);
|
||||
|
||||
if (result != Result.Success)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
_isMapped = true;
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result MapToOwner(ulong address, ulong size, KMemoryPermission permission)
|
||||
{
|
||||
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp<ulong>(size, (ulong)KPageTableBase.PageSize))
|
||||
{
|
||||
return KernelResult.InvalidSize;
|
||||
}
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
if (_isOwnerMapped)
|
||||
{
|
||||
return KernelResult.InvalidState;
|
||||
}
|
||||
|
||||
Debug.Assert(permission == KMemoryPermission.Read || permission == KMemoryPermission.ReadAndExecute);
|
||||
|
||||
Result result = Owner.MemoryManager.MapPages(address, _pageList, MemoryState.CodeReadOnly, permission);
|
||||
|
||||
if (result != Result.Success)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
_isOwnerMapped = true;
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result Unmap(ulong address, ulong size)
|
||||
{
|
||||
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp<ulong>(size, (ulong)KPageTableBase.PageSize))
|
||||
{
|
||||
return KernelResult.InvalidSize;
|
||||
}
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
KProcess process = KernelStatic.GetCurrentProcess();
|
||||
|
||||
Result result = process.MemoryManager.UnmapPages(address, _pageList, MemoryState.CodeWritable);
|
||||
|
||||
if (result != Result.Success)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
Debug.Assert(_isMapped);
|
||||
|
||||
_isMapped = false;
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result UnmapFromOwner(ulong address, ulong size)
|
||||
{
|
||||
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp<ulong>(size, KPageTableBase.PageSize))
|
||||
{
|
||||
return KernelResult.InvalidSize;
|
||||
}
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
Result result = Owner.MemoryManager.UnmapPages(address, _pageList, MemoryState.CodeReadOnly);
|
||||
|
||||
if (result != Result.Success)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
Debug.Assert(_isOwnerMapped);
|
||||
|
||||
_isOwnerMapped = false;
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
protected override void Destroy()
|
||||
{
|
||||
if (!_isMapped && !_isOwnerMapped)
|
||||
{
|
||||
ulong size = _pageList.GetPagesCount() * KPageTableBase.PageSize;
|
||||
|
||||
if (Owner.MemoryManager.UnborrowCodeMemory(_address, size, _pageList) != Result.Success)
|
||||
{
|
||||
throw new InvalidOperationException("Unexpected failure restoring transfer memory attributes.");
|
||||
}
|
||||
}
|
||||
|
||||
Owner.DecrementReferenceCount();
|
||||
}
|
||||
}
|
||||
}
|
156
src/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryBlock.cs
Normal file
156
src/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryBlock.cs
Normal file
|
@ -0,0 +1,156 @@
|
|||
using Ryujinx.Common.Collections;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
{
|
||||
class KMemoryBlock : IntrusiveRedBlackTreeNode<KMemoryBlock>, IComparable<KMemoryBlock>, IComparable<ulong>
|
||||
{
|
||||
public ulong BaseAddress { get; private set; }
|
||||
public ulong PagesCount { get; private set; }
|
||||
|
||||
public MemoryState State { get; private set; }
|
||||
public KMemoryPermission Permission { get; private set; }
|
||||
public MemoryAttribute Attribute { get; private set; }
|
||||
public KMemoryPermission SourcePermission { get; private set; }
|
||||
|
||||
public int IpcRefCount { get; private set; }
|
||||
public int DeviceRefCount { get; private set; }
|
||||
|
||||
public KMemoryBlock(
|
||||
ulong baseAddress,
|
||||
ulong pagesCount,
|
||||
MemoryState state,
|
||||
KMemoryPermission permission,
|
||||
MemoryAttribute attribute,
|
||||
int ipcRefCount = 0,
|
||||
int deviceRefCount = 0)
|
||||
{
|
||||
BaseAddress = baseAddress;
|
||||
PagesCount = pagesCount;
|
||||
State = state;
|
||||
Attribute = attribute;
|
||||
Permission = permission;
|
||||
IpcRefCount = ipcRefCount;
|
||||
DeviceRefCount = deviceRefCount;
|
||||
}
|
||||
|
||||
public void SetState(KMemoryPermission permission, MemoryState state, MemoryAttribute attribute)
|
||||
{
|
||||
Permission = permission;
|
||||
State = state;
|
||||
Attribute &= MemoryAttribute.IpcAndDeviceMapped;
|
||||
Attribute |= attribute;
|
||||
}
|
||||
|
||||
public void SetIpcMappingPermission(KMemoryPermission newPermission)
|
||||
{
|
||||
int oldIpcRefCount = IpcRefCount++;
|
||||
|
||||
if ((ushort)IpcRefCount == 0)
|
||||
{
|
||||
throw new InvalidOperationException("IPC reference count increment overflowed.");
|
||||
}
|
||||
|
||||
if (oldIpcRefCount == 0)
|
||||
{
|
||||
SourcePermission = Permission;
|
||||
|
||||
Permission &= ~KMemoryPermission.ReadAndWrite;
|
||||
Permission |= KMemoryPermission.ReadAndWrite & newPermission;
|
||||
}
|
||||
|
||||
Attribute |= MemoryAttribute.IpcMapped;
|
||||
}
|
||||
|
||||
public void RestoreIpcMappingPermission()
|
||||
{
|
||||
int oldIpcRefCount = IpcRefCount--;
|
||||
|
||||
if (oldIpcRefCount == 0)
|
||||
{
|
||||
throw new InvalidOperationException("IPC reference count decrement underflowed.");
|
||||
}
|
||||
|
||||
if (oldIpcRefCount == 1)
|
||||
{
|
||||
Permission = SourcePermission;
|
||||
|
||||
SourcePermission = KMemoryPermission.None;
|
||||
|
||||
Attribute &= ~MemoryAttribute.IpcMapped;
|
||||
}
|
||||
}
|
||||
|
||||
public KMemoryBlock SplitRightAtAddress(ulong address)
|
||||
{
|
||||
ulong leftAddress = BaseAddress;
|
||||
|
||||
ulong leftPagesCount = (address - leftAddress) / KPageTableBase.PageSize;
|
||||
|
||||
BaseAddress = address;
|
||||
|
||||
PagesCount -= leftPagesCount;
|
||||
|
||||
return new KMemoryBlock(
|
||||
leftAddress,
|
||||
leftPagesCount,
|
||||
State,
|
||||
Permission,
|
||||
Attribute,
|
||||
IpcRefCount,
|
||||
DeviceRefCount);
|
||||
}
|
||||
|
||||
public void AddPages(ulong pagesCount)
|
||||
{
|
||||
PagesCount += pagesCount;
|
||||
}
|
||||
|
||||
public KMemoryInfo GetInfo()
|
||||
{
|
||||
ulong size = PagesCount * KPageTableBase.PageSize;
|
||||
|
||||
return new KMemoryInfo(
|
||||
BaseAddress,
|
||||
size,
|
||||
State,
|
||||
Permission,
|
||||
Attribute,
|
||||
SourcePermission,
|
||||
IpcRefCount,
|
||||
DeviceRefCount);
|
||||
}
|
||||
|
||||
public int CompareTo(KMemoryBlock other)
|
||||
{
|
||||
if (BaseAddress < other.BaseAddress)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else if (BaseAddress <= other.BaseAddress + other.PagesCount * KPageTableBase.PageSize - 1UL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
public int CompareTo(ulong address)
|
||||
{
|
||||
if (address < BaseAddress)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else if (address <= BaseAddress + PagesCount * KPageTableBase.PageSize - 1UL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
288
src/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryBlockManager.cs
Normal file
288
src/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryBlockManager.cs
Normal file
|
@ -0,0 +1,288 @@
|
|||
using Ryujinx.Common.Collections;
|
||||
using Ryujinx.Horizon.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 Result 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 Result.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);
|
||||
}
|
||||
}
|
||||
}
|
19
src/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryBlockSlabManager.cs
Normal file
19
src/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryBlockSlabManager.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
{
|
||||
class KMemoryBlockSlabManager
|
||||
{
|
||||
private ulong _capacityElements;
|
||||
|
||||
public int Count { get; set; }
|
||||
|
||||
public KMemoryBlockSlabManager(ulong capacityElements)
|
||||
{
|
||||
_capacityElements = capacityElements;
|
||||
}
|
||||
|
||||
public bool CanAllocate(int count)
|
||||
{
|
||||
return (ulong)(Count + count) <= _capacityElements;
|
||||
}
|
||||
}
|
||||
}
|
36
src/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryInfo.cs
Normal file
36
src/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryInfo.cs
Normal file
|
@ -0,0 +1,36 @@
|
|||
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
{
|
||||
class KMemoryInfo
|
||||
{
|
||||
public ulong Address { get; }
|
||||
public ulong Size { get; }
|
||||
|
||||
public MemoryState State { get; }
|
||||
public KMemoryPermission Permission { get; }
|
||||
public MemoryAttribute Attribute { get; }
|
||||
public KMemoryPermission SourcePermission { get; }
|
||||
|
||||
public int IpcRefCount { get; }
|
||||
public int DeviceRefCount { get; }
|
||||
|
||||
public KMemoryInfo(
|
||||
ulong address,
|
||||
ulong size,
|
||||
MemoryState state,
|
||||
KMemoryPermission permission,
|
||||
MemoryAttribute attribute,
|
||||
KMemoryPermission sourcePermission,
|
||||
int ipcRefCount,
|
||||
int deviceRefCount)
|
||||
{
|
||||
Address = address;
|
||||
Size = size;
|
||||
State = state;
|
||||
Permission = permission;
|
||||
Attribute = attribute;
|
||||
SourcePermission = sourcePermission;
|
||||
IpcRefCount = ipcRefCount;
|
||||
DeviceRefCount = deviceRefCount;
|
||||
}
|
||||
}
|
||||
}
|
65
src/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryManager.cs
Normal file
65
src/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryManager.cs
Normal file
|
@ -0,0 +1,65 @@
|
|||
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
{
|
||||
class KMemoryManager
|
||||
{
|
||||
public KMemoryRegionManager[] MemoryRegions { get; }
|
||||
|
||||
public KMemoryManager(MemorySize size, MemoryArrange arrange)
|
||||
{
|
||||
MemoryRegions = KernelInit.GetMemoryRegions(size, arrange);
|
||||
}
|
||||
|
||||
private KMemoryRegionManager GetMemoryRegion(ulong address)
|
||||
{
|
||||
for (int i = 0; i < MemoryRegions.Length; i++)
|
||||
{
|
||||
var region = MemoryRegions[i];
|
||||
|
||||
if (address >= region.Address && address < region.EndAddr)
|
||||
{
|
||||
return region;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void IncrementPagesReferenceCount(ulong address, ulong pagesCount)
|
||||
{
|
||||
IncrementOrDecrementPagesReferenceCount(address, pagesCount, true);
|
||||
}
|
||||
|
||||
public void DecrementPagesReferenceCount(ulong address, ulong pagesCount)
|
||||
{
|
||||
IncrementOrDecrementPagesReferenceCount(address, pagesCount, false);
|
||||
}
|
||||
|
||||
private void IncrementOrDecrementPagesReferenceCount(ulong address, ulong pagesCount, bool increment)
|
||||
{
|
||||
while (pagesCount != 0)
|
||||
{
|
||||
var region = GetMemoryRegion(address);
|
||||
|
||||
ulong countToProcess = Math.Min(pagesCount, region.GetPageOffsetFromEnd(address));
|
||||
|
||||
lock (region)
|
||||
{
|
||||
if (increment)
|
||||
{
|
||||
region.IncrementPagesReferenceCount(address, countToProcess);
|
||||
}
|
||||
else
|
||||
{
|
||||
region.DecrementPagesReferenceCount(address, countToProcess);
|
||||
}
|
||||
}
|
||||
|
||||
pagesCount -= countToProcess;
|
||||
address += countToProcess * KPageTableBase.PageSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
242
src/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryRegionManager.cs
Normal file
242
src/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryRegionManager.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
298
src/Ryujinx.HLE/HOS/Kernel/Memory/KPageBitmap.cs
Normal file
298
src/Ryujinx.HLE/HOS/Kernel/Memory/KPageBitmap.cs
Normal file
|
@ -0,0 +1,298 @@
|
|||
using Ryujinx.Common;
|
||||
using System;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
{
|
||||
class KPageBitmap
|
||||
{
|
||||
private struct RandomNumberGenerator
|
||||
{
|
||||
private uint _entropy;
|
||||
private uint _bitsAvailable;
|
||||
|
||||
private void RefreshEntropy()
|
||||
{
|
||||
_entropy = 0;
|
||||
_bitsAvailable = sizeof(uint) * 8;
|
||||
}
|
||||
|
||||
private bool GenerateRandomBit()
|
||||
{
|
||||
if (_bitsAvailable == 0)
|
||||
{
|
||||
RefreshEntropy();
|
||||
}
|
||||
|
||||
bool bit = (_entropy & 1) != 0;
|
||||
|
||||
_entropy >>= 1;
|
||||
_bitsAvailable--;
|
||||
|
||||
return bit;
|
||||
}
|
||||
|
||||
public int SelectRandomBit(ulong bitmap)
|
||||
{
|
||||
int selected = 0;
|
||||
|
||||
int bitsCount = UInt64BitSize / 2;
|
||||
ulong mask = (1UL << bitsCount) - 1;
|
||||
|
||||
while (bitsCount != 0)
|
||||
{
|
||||
ulong low = bitmap & mask;
|
||||
ulong high = (bitmap >> bitsCount) & mask;
|
||||
|
||||
bool chooseLow;
|
||||
|
||||
if (high == 0)
|
||||
{
|
||||
chooseLow = true;
|
||||
}
|
||||
else if (low == 0)
|
||||
{
|
||||
chooseLow = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
chooseLow = GenerateRandomBit();
|
||||
}
|
||||
|
||||
if (chooseLow)
|
||||
{
|
||||
bitmap = low;
|
||||
}
|
||||
else
|
||||
{
|
||||
bitmap = high;
|
||||
selected += bitsCount;
|
||||
}
|
||||
|
||||
bitsCount /= 2;
|
||||
mask >>= bitsCount;
|
||||
}
|
||||
|
||||
return selected;
|
||||
}
|
||||
}
|
||||
|
||||
private const int UInt64BitSize = sizeof(ulong) * 8;
|
||||
private const int MaxDepth = 4;
|
||||
|
||||
private readonly RandomNumberGenerator _rng;
|
||||
private readonly ArraySegment<ulong>[] _bitStorages;
|
||||
private int _usedDepths;
|
||||
|
||||
public int BitsCount { get; private set; }
|
||||
|
||||
public int HighestDepthIndex => _usedDepths - 1;
|
||||
|
||||
public KPageBitmap()
|
||||
{
|
||||
_rng = new RandomNumberGenerator();
|
||||
_bitStorages = new ArraySegment<ulong>[MaxDepth];
|
||||
}
|
||||
|
||||
public ArraySegment<ulong> Initialize(ArraySegment<ulong> storage, ulong size)
|
||||
{
|
||||
_usedDepths = GetRequiredDepth(size);
|
||||
|
||||
for (int depth = HighestDepthIndex; depth >= 0; depth--)
|
||||
{
|
||||
_bitStorages[depth] = storage;
|
||||
size = BitUtils.DivRoundUp<ulong>(size, (ulong)UInt64BitSize);
|
||||
storage = storage.Slice((int)size);
|
||||
}
|
||||
|
||||
return storage;
|
||||
}
|
||||
|
||||
public ulong FindFreeBlock(bool random)
|
||||
{
|
||||
ulong offset = 0;
|
||||
int depth = 0;
|
||||
|
||||
if (random)
|
||||
{
|
||||
do
|
||||
{
|
||||
ulong v = _bitStorages[depth][(int)offset];
|
||||
|
||||
if (v == 0)
|
||||
{
|
||||
return ulong.MaxValue;
|
||||
}
|
||||
|
||||
offset = offset * UInt64BitSize + (ulong)_rng.SelectRandomBit(v);
|
||||
}
|
||||
while (++depth < _usedDepths);
|
||||
}
|
||||
else
|
||||
{
|
||||
do
|
||||
{
|
||||
ulong v = _bitStorages[depth][(int)offset];
|
||||
|
||||
if (v == 0)
|
||||
{
|
||||
return ulong.MaxValue;
|
||||
}
|
||||
|
||||
offset = offset * UInt64BitSize + (ulong)BitOperations.TrailingZeroCount(v);
|
||||
}
|
||||
while (++depth < _usedDepths);
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
public void SetBit(ulong offset)
|
||||
{
|
||||
SetBit(HighestDepthIndex, offset);
|
||||
BitsCount++;
|
||||
}
|
||||
|
||||
public void ClearBit(ulong offset)
|
||||
{
|
||||
ClearBit(HighestDepthIndex, offset);
|
||||
BitsCount--;
|
||||
}
|
||||
|
||||
public bool ClearRange(ulong offset, int count)
|
||||
{
|
||||
int depth = HighestDepthIndex;
|
||||
var bits = _bitStorages[depth];
|
||||
|
||||
int bitInd = (int)(offset / UInt64BitSize);
|
||||
|
||||
if (count < UInt64BitSize)
|
||||
{
|
||||
int shift = (int)(offset % UInt64BitSize);
|
||||
|
||||
ulong mask = ((1UL << count) - 1) << shift;
|
||||
|
||||
ulong v = bits[bitInd];
|
||||
|
||||
if ((v & mask) != mask)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
v &= ~mask;
|
||||
bits[bitInd] = v;
|
||||
|
||||
if (v == 0)
|
||||
{
|
||||
ClearBit(depth - 1, (ulong)bitInd);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int remaining = count;
|
||||
int i = 0;
|
||||
|
||||
do
|
||||
{
|
||||
if (bits[bitInd + i++] != ulong.MaxValue)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
remaining -= UInt64BitSize;
|
||||
}
|
||||
while (remaining > 0);
|
||||
|
||||
remaining = count;
|
||||
i = 0;
|
||||
|
||||
do
|
||||
{
|
||||
bits[bitInd + i] = 0;
|
||||
ClearBit(depth - 1, (ulong)(bitInd + i));
|
||||
i++;
|
||||
remaining -= UInt64BitSize;
|
||||
}
|
||||
while (remaining > 0);
|
||||
}
|
||||
|
||||
BitsCount -= count;
|
||||
return true;
|
||||
}
|
||||
|
||||
private void SetBit(int depth, ulong offset)
|
||||
{
|
||||
while (depth >= 0)
|
||||
{
|
||||
int ind = (int)(offset / UInt64BitSize);
|
||||
int which = (int)(offset % UInt64BitSize);
|
||||
|
||||
ulong mask = 1UL << which;
|
||||
|
||||
ulong v = _bitStorages[depth][ind];
|
||||
|
||||
_bitStorages[depth][ind] = v | mask;
|
||||
|
||||
if (v != 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
offset = (ulong)ind;
|
||||
depth--;
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearBit(int depth, ulong offset)
|
||||
{
|
||||
while (depth >= 0)
|
||||
{
|
||||
int ind = (int)(offset / UInt64BitSize);
|
||||
int which = (int)(offset % UInt64BitSize);
|
||||
|
||||
ulong mask = 1UL << which;
|
||||
|
||||
ulong v = _bitStorages[depth][ind];
|
||||
|
||||
v &= ~mask;
|
||||
|
||||
_bitStorages[depth][ind] = v;
|
||||
|
||||
if (v != 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
offset = (ulong)ind;
|
||||
depth--;
|
||||
}
|
||||
}
|
||||
|
||||
private static int GetRequiredDepth(ulong regionSize)
|
||||
{
|
||||
int depth = 0;
|
||||
|
||||
do
|
||||
{
|
||||
regionSize /= UInt64BitSize;
|
||||
depth++;
|
||||
}
|
||||
while (regionSize != 0);
|
||||
|
||||
return depth;
|
||||
}
|
||||
|
||||
public static int CalculateManagementOverheadSize(ulong regionSize)
|
||||
{
|
||||
int overheadBits = 0;
|
||||
|
||||
for (int depth = GetRequiredDepth(regionSize) - 1; depth >= 0; depth--)
|
||||
{
|
||||
regionSize = BitUtils.DivRoundUp<ulong>(regionSize, UInt64BitSize);
|
||||
overheadBits += (int)regionSize;
|
||||
}
|
||||
|
||||
return overheadBits * sizeof(ulong);
|
||||
}
|
||||
}
|
||||
}
|
283
src/Ryujinx.HLE/HOS/Kernel/Memory/KPageHeap.cs
Normal file
283
src/Ryujinx.HLE/HOS/Kernel/Memory/KPageHeap.cs
Normal file
|
@ -0,0 +1,283 @@
|
|||
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);
|
||||
}
|
||||
}
|
||||
}
|
97
src/Ryujinx.HLE/HOS/Kernel/Memory/KPageList.cs
Normal file
97
src/Ryujinx.HLE/HOS/Kernel/Memory/KPageList.cs
Normal file
|
@ -0,0 +1,97 @@
|
|||
using Ryujinx.Horizon.Common;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
{
|
||||
class KPageList : IEnumerable<KPageNode>
|
||||
{
|
||||
public LinkedList<KPageNode> Nodes { get; }
|
||||
|
||||
public KPageList()
|
||||
{
|
||||
Nodes = new LinkedList<KPageNode>();
|
||||
}
|
||||
|
||||
public Result AddRange(ulong address, ulong pagesCount)
|
||||
{
|
||||
if (pagesCount != 0)
|
||||
{
|
||||
if (Nodes.Last != null)
|
||||
{
|
||||
KPageNode lastNode = Nodes.Last.Value;
|
||||
|
||||
if (lastNode.Address + lastNode.PagesCount * KPageTableBase.PageSize == address)
|
||||
{
|
||||
address = lastNode.Address;
|
||||
pagesCount += lastNode.PagesCount;
|
||||
|
||||
Nodes.RemoveLast();
|
||||
}
|
||||
}
|
||||
|
||||
Nodes.AddLast(new KPageNode(address, pagesCount));
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public ulong GetPagesCount()
|
||||
{
|
||||
ulong sum = 0;
|
||||
|
||||
foreach (KPageNode node in Nodes)
|
||||
{
|
||||
sum += node.PagesCount;
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
public bool IsEqual(KPageList other)
|
||||
{
|
||||
LinkedListNode<KPageNode> thisNode = Nodes.First;
|
||||
LinkedListNode<KPageNode> otherNode = other.Nodes.First;
|
||||
|
||||
while (thisNode != null && otherNode != null)
|
||||
{
|
||||
if (thisNode.Value.Address != otherNode.Value.Address ||
|
||||
thisNode.Value.PagesCount != otherNode.Value.PagesCount)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
thisNode = thisNode.Next;
|
||||
otherNode = otherNode.Next;
|
||||
}
|
||||
|
||||
return thisNode == null && otherNode == null;
|
||||
}
|
||||
|
||||
public void IncrementPagesReferenceCount(KMemoryManager manager)
|
||||
{
|
||||
foreach (var node in this)
|
||||
{
|
||||
manager.IncrementPagesReferenceCount(node.Address, node.PagesCount);
|
||||
}
|
||||
}
|
||||
|
||||
public void DecrementPagesReferenceCount(KMemoryManager manager)
|
||||
{
|
||||
foreach (var node in this)
|
||||
{
|
||||
manager.DecrementPagesReferenceCount(node.Address, node.PagesCount);
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator<KPageNode> GetEnumerator()
|
||||
{
|
||||
return Nodes.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
14
src/Ryujinx.HLE/HOS/Kernel/Memory/KPageNode.cs
Normal file
14
src/Ryujinx.HLE/HOS/Kernel/Memory/KPageNode.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
{
|
||||
struct KPageNode
|
||||
{
|
||||
public ulong Address;
|
||||
public ulong PagesCount;
|
||||
|
||||
public KPageNode(ulong address, ulong pagesCount)
|
||||
{
|
||||
Address = address;
|
||||
PagesCount = pagesCount;
|
||||
}
|
||||
}
|
||||
}
|
229
src/Ryujinx.HLE/HOS/Kernel/Memory/KPageTable.cs
Normal file
229
src/Ryujinx.HLE/HOS/Kernel/Memory/KPageTable.cs
Normal file
|
@ -0,0 +1,229 @@
|
|||
using Ryujinx.Horizon.Common;
|
||||
using Ryujinx.Memory;
|
||||
using Ryujinx.Memory.Range;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
{
|
||||
class KPageTable : KPageTableBase
|
||||
{
|
||||
private readonly IVirtualMemoryManager _cpuMemory;
|
||||
|
||||
protected override bool Supports4KBPages => _cpuMemory.Supports4KBPages;
|
||||
|
||||
public KPageTable(KernelContext context, IVirtualMemoryManager cpuMemory) : base(context)
|
||||
{
|
||||
_cpuMemory = cpuMemory;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override IEnumerable<HostMemoryRange> GetHostRegions(ulong va, ulong size)
|
||||
{
|
||||
return _cpuMemory.GetHostRegions(va, size);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void GetPhysicalRegions(ulong va, ulong size, KPageList pageList)
|
||||
{
|
||||
var ranges = _cpuMemory.GetPhysicalRegions(va, size);
|
||||
foreach (var range in ranges)
|
||||
{
|
||||
pageList.AddRange(range.Address + DramMemoryMap.DramBase, range.Size / PageSize);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override ReadOnlySpan<byte> GetSpan(ulong va, int size)
|
||||
{
|
||||
return _cpuMemory.GetSpan(va, size);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override Result MapMemory(ulong src, ulong dst, ulong pagesCount, KMemoryPermission oldSrcPermission, KMemoryPermission newDstPermission)
|
||||
{
|
||||
KPageList pageList = new KPageList();
|
||||
GetPhysicalRegions(src, pagesCount * PageSize, pageList);
|
||||
|
||||
Result result = Reprotect(src, pagesCount, KMemoryPermission.None);
|
||||
|
||||
if (result != Result.Success)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
result = MapPages(dst, pageList, newDstPermission, MemoryMapFlags.Private, false, 0);
|
||||
|
||||
if (result != Result.Success)
|
||||
{
|
||||
Result reprotectResult = Reprotect(src, pagesCount, oldSrcPermission);
|
||||
Debug.Assert(reprotectResult == Result.Success);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override Result UnmapMemory(ulong dst, ulong src, ulong pagesCount, KMemoryPermission oldDstPermission, KMemoryPermission newSrcPermission)
|
||||
{
|
||||
ulong size = pagesCount * PageSize;
|
||||
|
||||
KPageList srcPageList = new KPageList();
|
||||
KPageList dstPageList = new KPageList();
|
||||
|
||||
GetPhysicalRegions(src, size, srcPageList);
|
||||
GetPhysicalRegions(dst, size, dstPageList);
|
||||
|
||||
if (!dstPageList.IsEqual(srcPageList))
|
||||
{
|
||||
return KernelResult.InvalidMemRange;
|
||||
}
|
||||
|
||||
Result result = Unmap(dst, pagesCount);
|
||||
|
||||
if (result != Result.Success)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
result = Reprotect(src, pagesCount, newSrcPermission);
|
||||
|
||||
if (result != Result.Success)
|
||||
{
|
||||
Result mapResult = MapPages(dst, dstPageList, oldDstPermission, MemoryMapFlags.Private, false, 0);
|
||||
Debug.Assert(mapResult == Result.Success);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override Result MapPages(
|
||||
ulong dstVa,
|
||||
ulong pagesCount,
|
||||
ulong srcPa,
|
||||
KMemoryPermission permission,
|
||||
MemoryMapFlags flags,
|
||||
bool shouldFillPages,
|
||||
byte fillValue)
|
||||
{
|
||||
ulong size = pagesCount * PageSize;
|
||||
|
||||
Context.CommitMemory(srcPa - DramMemoryMap.DramBase, size);
|
||||
|
||||
_cpuMemory.Map(dstVa, srcPa - DramMemoryMap.DramBase, size, flags);
|
||||
|
||||
if (DramMemoryMap.IsHeapPhysicalAddress(srcPa))
|
||||
{
|
||||
Context.MemoryManager.IncrementPagesReferenceCount(srcPa, pagesCount);
|
||||
}
|
||||
|
||||
if (shouldFillPages)
|
||||
{
|
||||
_cpuMemory.Fill(dstVa, size, fillValue);
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override Result MapPages(
|
||||
ulong address,
|
||||
KPageList pageList,
|
||||
KMemoryPermission permission,
|
||||
MemoryMapFlags flags,
|
||||
bool shouldFillPages,
|
||||
byte fillValue)
|
||||
{
|
||||
using var scopedPageList = new KScopedPageList(Context.MemoryManager, pageList);
|
||||
|
||||
ulong currentVa = address;
|
||||
|
||||
foreach (var pageNode in pageList)
|
||||
{
|
||||
ulong addr = pageNode.Address - DramMemoryMap.DramBase;
|
||||
ulong size = pageNode.PagesCount * PageSize;
|
||||
|
||||
Context.CommitMemory(addr, size);
|
||||
|
||||
_cpuMemory.Map(currentVa, addr, size, flags);
|
||||
|
||||
if (shouldFillPages)
|
||||
{
|
||||
_cpuMemory.Fill(currentVa, size, fillValue);
|
||||
}
|
||||
|
||||
currentVa += size;
|
||||
}
|
||||
|
||||
scopedPageList.SignalSuccess();
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override Result MapForeign(IEnumerable<HostMemoryRange> regions, ulong va, ulong size)
|
||||
{
|
||||
ulong offset = 0;
|
||||
|
||||
foreach (var region in regions)
|
||||
{
|
||||
_cpuMemory.MapForeign(va + offset, region.Address, region.Size);
|
||||
|
||||
offset += region.Size;
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override Result Unmap(ulong address, ulong pagesCount)
|
||||
{
|
||||
KPageList pagesToClose = new KPageList();
|
||||
|
||||
var regions = _cpuMemory.GetPhysicalRegions(address, pagesCount * PageSize);
|
||||
|
||||
foreach (var region in regions)
|
||||
{
|
||||
ulong pa = region.Address + DramMemoryMap.DramBase;
|
||||
if (DramMemoryMap.IsHeapPhysicalAddress(pa))
|
||||
{
|
||||
pagesToClose.AddRange(pa, region.Size / PageSize);
|
||||
}
|
||||
}
|
||||
|
||||
_cpuMemory.Unmap(address, pagesCount * PageSize);
|
||||
|
||||
pagesToClose.DecrementPagesReferenceCount(Context.MemoryManager);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override Result Reprotect(ulong address, ulong pagesCount, KMemoryPermission permission)
|
||||
{
|
||||
// TODO.
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override Result ReprotectWithAttributes(ulong address, ulong pagesCount, KMemoryPermission permission)
|
||||
{
|
||||
// TODO.
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void SignalMemoryTracking(ulong va, ulong size, bool write)
|
||||
{
|
||||
_cpuMemory.SignalMemoryTracking(va, size, write);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void Write(ulong va, ReadOnlySpan<byte> data)
|
||||
{
|
||||
_cpuMemory.Write(va, data);
|
||||
}
|
||||
}
|
||||
}
|
3043
src/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs
Normal file
3043
src/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs
Normal file
File diff suppressed because it is too large
Load diff
27
src/Ryujinx.HLE/HOS/Kernel/Memory/KScopedPageList.cs
Normal file
27
src/Ryujinx.HLE/HOS/Kernel/Memory/KScopedPageList.cs
Normal file
|
@ -0,0 +1,27 @@
|
|||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
{
|
||||
struct KScopedPageList : IDisposable
|
||||
{
|
||||
private readonly KMemoryManager _manager;
|
||||
private KPageList _pageList;
|
||||
|
||||
public KScopedPageList(KMemoryManager manager, KPageList pageList)
|
||||
{
|
||||
_manager = manager;
|
||||
_pageList = pageList;
|
||||
pageList.IncrementPagesReferenceCount(manager);
|
||||
}
|
||||
|
||||
public void SignalSuccess()
|
||||
{
|
||||
_pageList = null;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_pageList?.DecrementPagesReferenceCount(_manager);
|
||||
}
|
||||
}
|
||||
}
|
75
src/Ryujinx.HLE/HOS/Kernel/Memory/KSharedMemory.cs
Normal file
75
src/Ryujinx.HLE/HOS/Kernel/Memory/KSharedMemory.cs
Normal file
|
@ -0,0 +1,75 @@
|
|||
using Ryujinx.Common;
|
||||
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using Ryujinx.Memory;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
{
|
||||
class KSharedMemory : KAutoObject
|
||||
{
|
||||
private readonly KPageList _pageList;
|
||||
|
||||
private readonly ulong _ownerPid;
|
||||
|
||||
private readonly KMemoryPermission _ownerPermission;
|
||||
private readonly KMemoryPermission _userPermission;
|
||||
|
||||
public KSharedMemory(
|
||||
KernelContext context,
|
||||
SharedMemoryStorage storage,
|
||||
ulong ownerPid,
|
||||
KMemoryPermission ownerPermission,
|
||||
KMemoryPermission userPermission) : base(context)
|
||||
{
|
||||
_pageList = storage.GetPageList();
|
||||
_ownerPid = ownerPid;
|
||||
_ownerPermission = ownerPermission;
|
||||
_userPermission = userPermission;
|
||||
}
|
||||
|
||||
public Result MapIntoProcess(
|
||||
KPageTableBase memoryManager,
|
||||
ulong address,
|
||||
ulong size,
|
||||
KProcess process,
|
||||
KMemoryPermission permission)
|
||||
{
|
||||
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp<ulong>(size, KPageTableBase.PageSize))
|
||||
{
|
||||
return KernelResult.InvalidSize;
|
||||
}
|
||||
|
||||
KMemoryPermission expectedPermission = process.Pid == _ownerPid
|
||||
? _ownerPermission
|
||||
: _userPermission;
|
||||
|
||||
if (permission != expectedPermission)
|
||||
{
|
||||
return KernelResult.InvalidPermission;
|
||||
}
|
||||
|
||||
// On platforms with page size > 4 KB, this can fail due to the address not being page aligned,
|
||||
// we can return an error to force the application to retry with a different address.
|
||||
|
||||
try
|
||||
{
|
||||
return memoryManager.MapPages(address, _pageList, MemoryState.SharedMemory, permission);
|
||||
}
|
||||
catch (InvalidMemoryRegionException)
|
||||
{
|
||||
return KernelResult.InvalidMemState;
|
||||
}
|
||||
}
|
||||
|
||||
public Result UnmapFromProcess(KPageTableBase memoryManager, ulong address, ulong size, KProcess process)
|
||||
{
|
||||
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp<ulong>(size, KPageTableBase.PageSize))
|
||||
{
|
||||
return KernelResult.InvalidSize;
|
||||
}
|
||||
|
||||
return memoryManager.UnmapPages(address, _pageList, MemoryState.SharedMemory);
|
||||
}
|
||||
}
|
||||
}
|
50
src/Ryujinx.HLE/HOS/Kernel/Memory/KSlabHeap.cs
Normal file
50
src/Ryujinx.HLE/HOS/Kernel/Memory/KSlabHeap.cs
Normal file
|
@ -0,0 +1,50 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
{
|
||||
class KSlabHeap
|
||||
{
|
||||
private LinkedList<ulong> _items;
|
||||
|
||||
public KSlabHeap(ulong pa, ulong itemSize, ulong size)
|
||||
{
|
||||
_items = new LinkedList<ulong>();
|
||||
|
||||
int itemsCount = (int)(size / itemSize);
|
||||
|
||||
for (int index = 0; index < itemsCount; index++)
|
||||
{
|
||||
_items.AddLast(pa);
|
||||
|
||||
pa += itemSize;
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryGetItem(out ulong pa)
|
||||
{
|
||||
lock (_items)
|
||||
{
|
||||
if (_items.First != null)
|
||||
{
|
||||
pa = _items.First.Value;
|
||||
|
||||
_items.RemoveFirst();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
pa = 0;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Free(ulong pa)
|
||||
{
|
||||
lock (_items)
|
||||
{
|
||||
_items.AddFirst(pa);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
130
src/Ryujinx.HLE/HOS/Kernel/Memory/KTransferMemory.cs
Normal file
130
src/Ryujinx.HLE/HOS/Kernel/Memory/KTransferMemory.cs
Normal file
|
@ -0,0 +1,130 @@
|
|||
using Ryujinx.Common;
|
||||
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
{
|
||||
class KTransferMemory : KAutoObject
|
||||
{
|
||||
private KProcess _creator;
|
||||
|
||||
// TODO: Remove when we no longer need to read it from the owner directly.
|
||||
public KProcess Creator => _creator;
|
||||
|
||||
private readonly KPageList _pageList;
|
||||
|
||||
public ulong Address { get; private set; }
|
||||
public ulong Size { get; private set; }
|
||||
|
||||
public KMemoryPermission Permission { get; private set; }
|
||||
|
||||
private bool _hasBeenInitialized;
|
||||
private bool _isMapped;
|
||||
|
||||
public KTransferMemory(KernelContext context) : base(context)
|
||||
{
|
||||
_pageList = new KPageList();
|
||||
}
|
||||
|
||||
public KTransferMemory(KernelContext context, SharedMemoryStorage storage) : base(context)
|
||||
{
|
||||
_pageList = storage.GetPageList();
|
||||
Permission = KMemoryPermission.ReadAndWrite;
|
||||
|
||||
_hasBeenInitialized = true;
|
||||
_isMapped = false;
|
||||
}
|
||||
|
||||
public Result Initialize(ulong address, ulong size, KMemoryPermission permission)
|
||||
{
|
||||
KProcess creator = KernelStatic.GetCurrentProcess();
|
||||
|
||||
_creator = creator;
|
||||
|
||||
Result result = creator.MemoryManager.BorrowTransferMemory(_pageList, address, size, permission);
|
||||
|
||||
if (result != Result.Success)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
creator.IncrementReferenceCount();
|
||||
|
||||
Permission = permission;
|
||||
Address = address;
|
||||
Size = size;
|
||||
_hasBeenInitialized = true;
|
||||
_isMapped = false;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public Result MapIntoProcess(
|
||||
KPageTableBase memoryManager,
|
||||
ulong address,
|
||||
ulong size,
|
||||
KProcess process,
|
||||
KMemoryPermission permission)
|
||||
{
|
||||
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp<ulong>(size, KPageTableBase.PageSize))
|
||||
{
|
||||
return KernelResult.InvalidSize;
|
||||
}
|
||||
|
||||
if (permission != Permission || _isMapped)
|
||||
{
|
||||
return KernelResult.InvalidState;
|
||||
}
|
||||
|
||||
MemoryState state = Permission == KMemoryPermission.None ? MemoryState.TransferMemoryIsolated : MemoryState.TransferMemory;
|
||||
|
||||
Result result = memoryManager.MapPages(address, _pageList, state, KMemoryPermission.ReadAndWrite);
|
||||
|
||||
if (result == Result.Success)
|
||||
{
|
||||
_isMapped = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public Result UnmapFromProcess(
|
||||
KPageTableBase memoryManager,
|
||||
ulong address,
|
||||
ulong size,
|
||||
KProcess process)
|
||||
{
|
||||
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp<ulong>(size, (ulong)KPageTableBase.PageSize))
|
||||
{
|
||||
return KernelResult.InvalidSize;
|
||||
}
|
||||
|
||||
MemoryState state = Permission == KMemoryPermission.None ? MemoryState.TransferMemoryIsolated : MemoryState.TransferMemory;
|
||||
|
||||
Result result = memoryManager.UnmapPages(address, _pageList, state);
|
||||
|
||||
if (result == Result.Success)
|
||||
{
|
||||
_isMapped = false;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
protected override void Destroy()
|
||||
{
|
||||
if (_hasBeenInitialized)
|
||||
{
|
||||
if (!_isMapped && _creator.MemoryManager.UnborrowTransferMemory(Address, Size, _pageList) != Result.Success)
|
||||
{
|
||||
throw new InvalidOperationException("Unexpected failure restoring transfer memory attributes.");
|
||||
}
|
||||
|
||||
_creator.ResourceLimit?.Release(LimitableResource.TransferMemory, 1);
|
||||
_creator.DecrementReferenceCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
22
src/Ryujinx.HLE/HOS/Kernel/Memory/MemoryAttribute.cs
Normal file
22
src/Ryujinx.HLE/HOS/Kernel/Memory/MemoryAttribute.cs
Normal file
|
@ -0,0 +1,22 @@
|
|||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
{
|
||||
[Flags]
|
||||
enum MemoryAttribute : byte
|
||||
{
|
||||
None = 0,
|
||||
Mask = 0xff,
|
||||
|
||||
Borrowed = 1 << 0,
|
||||
IpcMapped = 1 << 1,
|
||||
DeviceMapped = 1 << 2,
|
||||
Uncached = 1 << 3,
|
||||
|
||||
IpcAndDeviceMapped = IpcMapped | DeviceMapped,
|
||||
|
||||
BorrowedAndIpcMapped = Borrowed | IpcMapped,
|
||||
|
||||
DeviceMappedAndUncached = DeviceMapped | Uncached
|
||||
}
|
||||
}
|
10
src/Ryujinx.HLE/HOS/Kernel/Memory/MemoryFillValue.cs
Normal file
10
src/Ryujinx.HLE/HOS/Kernel/Memory/MemoryFillValue.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
{
|
||||
enum MemoryFillValue : byte
|
||||
{
|
||||
Zero = 0,
|
||||
Stack = 0x58,
|
||||
Ipc = 0x59,
|
||||
Heap = 0x5A,
|
||||
}
|
||||
}
|
20
src/Ryujinx.HLE/HOS/Kernel/Memory/MemoryPermission.cs
Normal file
20
src/Ryujinx.HLE/HOS/Kernel/Memory/MemoryPermission.cs
Normal file
|
@ -0,0 +1,20 @@
|
|||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
{
|
||||
[Flags]
|
||||
enum KMemoryPermission : uint
|
||||
{
|
||||
None = 0,
|
||||
UserMask = Read | Write | Execute,
|
||||
Mask = uint.MaxValue,
|
||||
|
||||
Read = 1 << 0,
|
||||
Write = 1 << 1,
|
||||
Execute = 1 << 2,
|
||||
DontCare = 1 << 28,
|
||||
|
||||
ReadAndWrite = Read | Write,
|
||||
ReadAndExecute = Read | Execute
|
||||
}
|
||||
}
|
10
src/Ryujinx.HLE/HOS/Kernel/Memory/MemoryRegion.cs
Normal file
10
src/Ryujinx.HLE/HOS/Kernel/Memory/MemoryRegion.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
{
|
||||
enum MemoryRegion
|
||||
{
|
||||
Application = 0,
|
||||
Applet = 1,
|
||||
Service = 2,
|
||||
NvServices = 3
|
||||
}
|
||||
}
|
50
src/Ryujinx.HLE/HOS/Kernel/Memory/MemoryState.cs
Normal file
50
src/Ryujinx.HLE/HOS/Kernel/Memory/MemoryState.cs
Normal file
|
@ -0,0 +1,50 @@
|
|||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
{
|
||||
[Flags]
|
||||
enum MemoryState : uint
|
||||
{
|
||||
Unmapped = 0x00000000,
|
||||
Io = 0x00002001,
|
||||
Normal = 0x00042002,
|
||||
CodeStatic = 0x00DC7E03,
|
||||
CodeMutable = 0x03FEBD04,
|
||||
Heap = 0x037EBD05,
|
||||
SharedMemory = 0x00402006,
|
||||
ModCodeStatic = 0x00DD7E08,
|
||||
ModCodeMutable = 0x03FFBD09,
|
||||
IpcBuffer0 = 0x005C3C0A,
|
||||
Stack = 0x005C3C0B,
|
||||
ThreadLocal = 0x0040200C,
|
||||
TransferMemoryIsolated = 0x015C3C0D,
|
||||
TransferMemory = 0x005C380E,
|
||||
ProcessMemory = 0x0040380F,
|
||||
Reserved = 0x00000010,
|
||||
IpcBuffer1 = 0x005C3811,
|
||||
IpcBuffer3 = 0x004C2812,
|
||||
KernelStack = 0x00002013,
|
||||
CodeReadOnly = 0x00402214,
|
||||
CodeWritable = 0x00402015,
|
||||
UserMask = 0xff,
|
||||
Mask = 0xffffffff,
|
||||
|
||||
PermissionChangeAllowed = 1 << 8,
|
||||
ForceReadWritableByDebugSyscalls = 1 << 9,
|
||||
IpcSendAllowedType0 = 1 << 10,
|
||||
IpcSendAllowedType3 = 1 << 11,
|
||||
IpcSendAllowedType1 = 1 << 12,
|
||||
ProcessPermissionChangeAllowed = 1 << 14,
|
||||
MapAllowed = 1 << 15,
|
||||
UnmapProcessCodeMemoryAllowed = 1 << 16,
|
||||
TransferMemoryAllowed = 1 << 17,
|
||||
QueryPhysicalAddressAllowed = 1 << 18,
|
||||
MapDeviceAllowed = 1 << 19,
|
||||
MapDeviceAlignedAllowed = 1 << 20,
|
||||
IpcBufferAllowed = 1 << 21,
|
||||
IsPoolAllocated = 1 << 22,
|
||||
MapProcessAllowed = 1 << 23,
|
||||
AttributeChangeAllowed = 1 << 24,
|
||||
CodeMemoryAllowed = 1 << 25
|
||||
}
|
||||
}
|
49
src/Ryujinx.HLE/HOS/Kernel/Memory/SharedMemoryStorage.cs
Normal file
49
src/Ryujinx.HLE/HOS/Kernel/Memory/SharedMemoryStorage.cs
Normal file
|
@ -0,0 +1,49 @@
|
|||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
{
|
||||
class SharedMemoryStorage
|
||||
{
|
||||
private readonly KernelContext _context;
|
||||
private readonly KPageList _pageList;
|
||||
private readonly ulong _size;
|
||||
|
||||
public SharedMemoryStorage(KernelContext context, KPageList pageList)
|
||||
{
|
||||
_context = context;
|
||||
_pageList = pageList;
|
||||
_size = pageList.GetPagesCount() * KPageTableBase.PageSize;
|
||||
|
||||
foreach (KPageNode pageNode in pageList)
|
||||
{
|
||||
ulong address = pageNode.Address - DramMemoryMap.DramBase;
|
||||
ulong size = pageNode.PagesCount * KPageTableBase.PageSize;
|
||||
context.CommitMemory(address, size);
|
||||
}
|
||||
}
|
||||
|
||||
public void ZeroFill()
|
||||
{
|
||||
for (ulong offset = 0; offset < _size; offset += sizeof(ulong))
|
||||
{
|
||||
GetRef<ulong>(offset) = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public ref T GetRef<T>(ulong offset) where T : unmanaged
|
||||
{
|
||||
if (_pageList.Nodes.Count == 1)
|
||||
{
|
||||
ulong address = _pageList.Nodes.First.Value.Address - DramMemoryMap.DramBase;
|
||||
return ref _context.Memory.GetRef<T>(address + offset);
|
||||
}
|
||||
|
||||
throw new NotImplementedException("Non-contiguous shared memory is not yet supported.");
|
||||
}
|
||||
|
||||
public KPageList GetPageList()
|
||||
{
|
||||
return _pageList;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue