Move solution and projects to src

This commit is contained in:
TSR Berry 2023-04-08 01:22:00 +02:00 committed by Mary
parent cd124bda58
commit cee7121058
3466 changed files with 55 additions and 55 deletions

View file

@ -0,0 +1,10 @@
namespace Ryujinx.HLE.HOS.Kernel.Memory
{
enum AddressSpaceType
{
Addr32Bits = 0,
Addr36Bits = 1,
Addr32BitsNoMap = 2,
Addr39Bits = 3
}
}

View 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;
}
}
}

View 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();
}
}
}

View 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;
}
}
}
}

View 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);
}
}
}

View 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;
}
}
}

View 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;
}
}
}

View 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;
}
}
}
}

View 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;
}
}
}

View 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);
}
}
}

View 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);
}
}
}

View 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();
}
}
}

View 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;
}
}
}

View 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);
}
}
}

File diff suppressed because it is too large Load diff

View 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);
}
}
}

View 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);
}
}
}

View 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);
}
}
}
}

View 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();
}
}
}
}

View 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
}
}

View file

@ -0,0 +1,10 @@
namespace Ryujinx.HLE.HOS.Kernel.Memory
{
enum MemoryFillValue : byte
{
Zero = 0,
Stack = 0x58,
Ipc = 0x59,
Heap = 0x5A,
}
}

View 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
}
}

View file

@ -0,0 +1,10 @@
namespace Ryujinx.HLE.HOS.Kernel.Memory
{
enum MemoryRegion
{
Application = 0,
Applet = 1,
Service = 2,
NvServices = 3
}
}

View 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
}
}

View 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;
}
}
}