diff options
Diffstat (limited to 'Ryujinx.HLE/HOS/Kernel/Memory')
-rw-r--r-- | Ryujinx.HLE/HOS/Kernel/Memory/KCodeMemory.cs | 168 | ||||
-rw-r--r-- | Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs | 146 |
2 files changed, 314 insertions, 0 deletions
diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KCodeMemory.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KCodeMemory.cs new file mode 100644 index 00000000..2df93d78 --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/Memory/KCodeMemory.cs @@ -0,0 +1,168 @@ +using Ryujinx.Common; +using Ryujinx.HLE.HOS.Kernel.Common; +using Ryujinx.HLE.HOS.Kernel.Process; +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 KernelResult Initialize(ulong address, ulong size) + { + Owner = KernelStatic.GetCurrentProcess(); + + KernelResult result = Owner.MemoryManager.BorrowCodeMemory(_pageList, address, size); + + if (result != KernelResult.Success) + { + return result; + } + + Owner.CpuMemory.Fill(address, size, 0xff); + Owner.IncrementReferenceCount(); + + _address = address; + _isMapped = false; + _isOwnerMapped = false; + + return KernelResult.Success; + } + + public KernelResult Map(ulong address, ulong size, KMemoryPermission perm) + { + if (_pageList.GetPagesCount() != BitUtils.DivRoundUp(size, KPageTableBase.PageSize)) + { + return KernelResult.InvalidSize; + } + + lock (_lock) + { + if (_isMapped) + { + return KernelResult.InvalidState; + } + + KProcess process = KernelStatic.GetCurrentProcess(); + + KernelResult result = process.MemoryManager.MapPages(address, _pageList, MemoryState.CodeWritable, KMemoryPermission.ReadAndWrite); + + if (result != KernelResult.Success) + { + return result; + } + + _isMapped = true; + } + + return KernelResult.Success; + } + + public KernelResult MapToOwner(ulong address, ulong size, KMemoryPermission permission) + { + if (_pageList.GetPagesCount() != BitUtils.DivRoundUp(size, KPageTableBase.PageSize)) + { + return KernelResult.InvalidSize; + } + + lock (_lock) + { + if (_isOwnerMapped) + { + return KernelResult.InvalidState; + } + + Debug.Assert(permission == KMemoryPermission.Read || permission == KMemoryPermission.ReadAndExecute); + + KernelResult result = Owner.MemoryManager.MapPages(address, _pageList, MemoryState.CodeReadOnly, permission); + + if (result != KernelResult.Success) + { + return result; + } + + _isOwnerMapped = true; + } + + return KernelResult.Success; + } + + public KernelResult Unmap(ulong address, ulong size) + { + if (_pageList.GetPagesCount() != BitUtils.DivRoundUp(size, KPageTableBase.PageSize)) + { + return KernelResult.InvalidSize; + } + + lock (_lock) + { + KProcess process = KernelStatic.GetCurrentProcess(); + + KernelResult result = process.MemoryManager.UnmapPages(address, _pageList, MemoryState.CodeWritable); + + if (result != KernelResult.Success) + { + return result; + } + + Debug.Assert(_isMapped); + + _isMapped = false; + } + + return KernelResult.Success; + } + + public KernelResult UnmapFromOwner(ulong address, ulong size) + { + if (_pageList.GetPagesCount() != BitUtils.DivRoundUp(size, KPageTableBase.PageSize)) + { + return KernelResult.InvalidSize; + } + + lock (_lock) + { + KernelResult result = Owner.MemoryManager.UnmapPages(address, _pageList, MemoryState.CodeReadOnly); + + if (result != KernelResult.Success) + { + return result; + } + + Debug.Assert(_isOwnerMapped); + + _isOwnerMapped = false; + } + + return KernelResult.Success; + } + + protected override void Destroy() + { + if (!_isMapped && !_isOwnerMapped) + { + ulong size = _pageList.GetPagesCount() * KPageTableBase.PageSize; + + if (Owner.MemoryManager.UnborrowCodeMemory(_address, size, _pageList) != KernelResult.Success) + { + throw new InvalidOperationException("Unexpected failure restoring transfer memory attributes."); + } + } + + Owner.DecrementReferenceCount(); + } + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs index 518a0f09..94e8fb6a 100644 --- a/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs +++ b/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs @@ -1095,6 +1095,77 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory } } + public KernelResult UnmapProcessMemory(ulong dst, ulong size, KPageTableBase srcPageTable, ulong src) + { + lock (_blockManager) + { + lock (srcPageTable._blockManager) + { + bool success = CheckRange( + dst, + size, + MemoryState.Mask, + MemoryState.ProcessMemory, + KMemoryPermission.ReadAndWrite, + KMemoryPermission.ReadAndWrite, + MemoryAttribute.Mask, + MemoryAttribute.None, + MemoryAttribute.IpcAndDeviceMapped, + out _, + out _, + out _); + + success &= srcPageTable.CheckRange( + src, + size, + MemoryState.MapProcessAllowed, + MemoryState.MapProcessAllowed, + KMemoryPermission.None, + KMemoryPermission.None, + MemoryAttribute.Mask, + MemoryAttribute.None, + MemoryAttribute.IpcAndDeviceMapped, + out _, + out _, + out _); + + if (!success) + { + return KernelResult.InvalidMemState; + } + + KPageList srcPageList = new KPageList(); + KPageList dstPageList = new KPageList(); + + srcPageTable.GetPhysicalRegions(src, size, srcPageList); + GetPhysicalRegions(dst, size, dstPageList); + + if (!dstPageList.IsEqual(srcPageList)) + { + return KernelResult.InvalidMemRange; + } + } + + if (!_slabManager.CanAllocate(MaxBlocksNeededForInsertion)) + { + return KernelResult.OutOfResource; + } + + ulong pagesCount = size / PageSize; + + KernelResult result = Unmap(dst, pagesCount); + + if (result != KernelResult.Success) + { + return result; + } + + _blockManager.InsertBlock(dst, pagesCount, MemoryState.Unmapped); + + return KernelResult.Success; + } + } + public KernelResult SetProcessMemoryPermission(ulong address, ulong size, KMemoryPermission permission) { lock (_blockManager) @@ -2023,6 +2094,49 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory block.RestoreIpcMappingPermission(); } + public KernelResult GetPagesIfStateEquals( + ulong address, + ulong size, + MemoryState stateMask, + MemoryState stateExpected, + KMemoryPermission permissionMask, + KMemoryPermission permissionExpected, + MemoryAttribute attributeMask, + MemoryAttribute attributeExpected, + KPageList pageList) + { + if (!InsideAddrSpace(address, size)) + { + return KernelResult.InvalidMemState; + } + + lock (_blockManager) + { + if (CheckRange( + address, + size, + stateMask | MemoryState.IsPoolAllocated, + stateExpected | MemoryState.IsPoolAllocated, + permissionMask, + permissionExpected, + attributeMask, + attributeExpected, + MemoryAttribute.IpcAndDeviceMapped, + out _, + out _, + out _)) + { + GetPhysicalRegions(address, size, pageList); + + return KernelResult.Success; + } + else + { + return KernelResult.InvalidMemState; + } + } + } + public KernelResult BorrowIpcBuffer(ulong address, ulong size) { return SetAttributesAndChangePermission( @@ -2054,6 +2168,22 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory pageList); } + public KernelResult BorrowCodeMemory(KPageList pageList, ulong address, ulong size) + { + return SetAttributesAndChangePermission( + address, + size, + MemoryState.CodeMemoryAllowed, + MemoryState.CodeMemoryAllowed, + KMemoryPermission.Mask, + KMemoryPermission.ReadAndWrite, + MemoryAttribute.Mask, + MemoryAttribute.None, + KMemoryPermission.None, + MemoryAttribute.Borrowed, + pageList); + } + private KernelResult SetAttributesAndChangePermission( ulong address, ulong size, @@ -2159,6 +2289,22 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory pageList); } + public KernelResult UnborrowCodeMemory(ulong address, ulong size, KPageList pageList) + { + return ClearAttributesAndChangePermission( + address, + size, + MemoryState.CodeMemoryAllowed, + MemoryState.CodeMemoryAllowed, + KMemoryPermission.None, + KMemoryPermission.None, + MemoryAttribute.Mask, + MemoryAttribute.Borrowed, + KMemoryPermission.ReadAndWrite, + MemoryAttribute.Borrowed, + pageList); + } + private KernelResult ClearAttributesAndChangePermission( ulong address, ulong size, |