diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KClientSession.cs b/Ryujinx.HLE/HOS/Kernel/Ipc/KClientSession.cs index 262058d97..2c2d9644d 100644 --- a/Ryujinx.HLE/HOS/Kernel/Ipc/KClientSession.cs +++ b/Ryujinx.HLE/HOS/Kernel/Ipc/KClientSession.cs @@ -2,6 +2,7 @@ using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Process; using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Services; +using System; namespace Ryujinx.HLE.HOS.Kernel.Ipc { @@ -83,6 +84,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc { _parent.DisconnectClient(); _parent.DecrementReferenceCount(); + + if (Service is IDisposable disposableObj) + { + disposableObj.Dispose(); + } } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryManager.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryManager.cs index b13e28412..43e7ad691 100644 --- a/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryManager.cs +++ b/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryManager.cs @@ -1129,82 +1129,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory } } - public KernelResult ReserveTransferMemory(ulong address, ulong size, MemoryPermission permission) - { - lock (_blocks) - { - if (CheckRange( - address, - size, - MemoryState.TransferMemoryAllowed | MemoryState.IsPoolAllocated, - MemoryState.TransferMemoryAllowed | MemoryState.IsPoolAllocated, - MemoryPermission.Mask, - MemoryPermission.ReadAndWrite, - MemoryAttribute.Mask, - MemoryAttribute.None, - MemoryAttribute.IpcAndDeviceMapped, - out MemoryState state, - out _, - out MemoryAttribute attribute)) - { - // TODO: Missing checks. - - if (!_blockAllocator.CanAllocate(MaxBlocksNeededForInsertion)) - { - return KernelResult.OutOfResource; - } - - ulong pagesCount = size / PageSize; - - attribute |= MemoryAttribute.Borrowed; - - InsertBlock(address, pagesCount, state, permission, attribute); - - return KernelResult.Success; - } - else - { - return KernelResult.InvalidMemState; - } - } - } - - public KernelResult ResetTransferMemory(ulong address, ulong size) - { - lock (_blocks) - { - if (CheckRange( - address, - size, - MemoryState.TransferMemoryAllowed | MemoryState.IsPoolAllocated, - MemoryState.TransferMemoryAllowed | MemoryState.IsPoolAllocated, - MemoryPermission.None, - MemoryPermission.None, - MemoryAttribute.Mask, - MemoryAttribute.Borrowed, - MemoryAttribute.IpcAndDeviceMapped, - out MemoryState state, - out _, - out _)) - { - if (!_blockAllocator.CanAllocate(MaxBlocksNeededForInsertion)) - { - return KernelResult.OutOfResource; - } - - ulong pagesCount = size / PageSize; - - InsertBlock(address, pagesCount, state, MemoryPermission.ReadAndWrite); - - return KernelResult.Success; - } - else - { - return KernelResult.InvalidMemState; - } - } - } - public KernelResult SetProcessMemoryPermission(ulong address, ulong size, MemoryPermission permission) { lock (_blocks) @@ -2195,6 +2119,22 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory MemoryAttribute.Borrowed); } + public KernelResult BorrowTransferMemory(KPageList pageList, ulong address, ulong size, MemoryPermission permission) + { + return SetAttributesAndChangePermission( + address, + size, + MemoryState.TransferMemoryAllowed, + MemoryState.TransferMemoryAllowed, + MemoryPermission.Mask, + MemoryPermission.ReadAndWrite, + MemoryAttribute.Mask, + MemoryAttribute.None, + permission, + MemoryAttribute.Borrowed, + pageList); + } + private KernelResult SetAttributesAndChangePermission( ulong address, ulong size, @@ -2233,14 +2173,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory if (pageList != null) { - KPageList currPageList = new KPageList(); - - AddVaRangeToPageList(currPageList, address, pagesCount); - - if (!currPageList.IsEqual(pageList)) - { - return KernelResult.InvalidMemRange; - } + AddVaRangeToPageList(pageList, address, pagesCount); } if (!_blockAllocator.CanAllocate(MaxBlocksNeededForInsertion)) @@ -2297,6 +2230,22 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory MemoryAttribute.Borrowed); } + public KernelResult UnborrowTransferMemory(ulong address, ulong size, KPageList pageList) + { + return ClearAttributesAndChangePermission( + address, + size, + MemoryState.TransferMemoryAllowed, + MemoryState.TransferMemoryAllowed, + MemoryPermission.None, + MemoryPermission.None, + MemoryAttribute.Mask, + MemoryAttribute.Borrowed, + MemoryPermission.ReadAndWrite, + MemoryAttribute.Borrowed, + pageList); + } + private KernelResult ClearAttributesAndChangePermission( ulong address, ulong size, diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KTransferMemory.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KTransferMemory.cs index 6da0c4056..d3e6208e3 100644 --- a/Ryujinx.HLE/HOS/Kernel/Memory/KTransferMemory.cs +++ b/Ryujinx.HLE/HOS/Kernel/Memory/KTransferMemory.cs @@ -1,16 +1,63 @@ using Ryujinx.HLE.HOS.Kernel.Common; +using Ryujinx.HLE.HOS.Kernel.Process; +using System; namespace Ryujinx.HLE.HOS.Kernel.Memory { class KTransferMemory : KAutoObject { - public ulong Address { get; private set; } - public ulong Size { get; private set; } + private KProcess _creator; - public KTransferMemory(KernelContext context, ulong address, ulong size) : base(context) + private readonly KPageList _pageList; + + public ulong Address { get; private set; } + public ulong Size => _pageList.GetPagesCount() * KMemoryManager.PageSize; + + public MemoryPermission Permission { get; private set; } + + private bool _hasBeenInitialized; + private bool _isMapped; + + public KTransferMemory(KernelContext context) : base(context) { + _pageList = new KPageList(); + } + + public KernelResult Initialize(ulong address, ulong size, MemoryPermission permission) + { + KProcess creator = KernelContext.Scheduler.GetCurrentProcess(); + + _creator = creator; + + KernelResult result = creator.MemoryManager.BorrowTransferMemory(_pageList, address, size, permission); + + if (result != KernelResult.Success) + { + return result; + } + + creator.IncrementReferenceCount(); + + Permission = permission; Address = address; - Size = size; + _hasBeenInitialized = true; + _isMapped = false; + + return result; + } + + protected override void Destroy() + { + if (_hasBeenInitialized) + { + if (!_isMapped && _creator.MemoryManager.UnborrowTransferMemory(Address, Size, _pageList) != KernelResult.Success) + { + throw new InvalidOperationException("Unexpected failure restoring transfer memory attributes."); + } + + _creator.ResourceLimit?.Release(LimitableResource.TransferMemory, 1); + _creator.DecrementReferenceCount(); + } } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs index c0f894553..e29259bf4 100644 --- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs +++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs @@ -991,16 +991,41 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall KProcess process = _context.Scheduler.GetCurrentProcess(); - KernelResult result = process.MemoryManager.ReserveTransferMemory(address, size, permission); + KResourceLimit resourceLimit = process.ResourceLimit; + + if (resourceLimit != null && !resourceLimit.Reserve(LimitableResource.TransferMemory, 1)) + { + return KernelResult.ResLimitExceeded; + } + + void CleanUpForError() + { + resourceLimit?.Release(LimitableResource.TransferMemory, 1); + } + + if (!process.MemoryManager.InsideAddrSpace(address, size)) + { + CleanUpForError(); + + return KernelResult.InvalidMemState; + } + + KTransferMemory transferMemory = new KTransferMemory(_context); + + KernelResult result = transferMemory.Initialize(address, size, permission); if (result != KernelResult.Success) { + CleanUpForError(); + return result; } - KTransferMemory transferMemory = new KTransferMemory(_context, address, size); + result = process.HandleTable.GenerateHandle(transferMemory, out handle); - return process.HandleTable.GenerateHandle(transferMemory, out handle); + transferMemory.DecrementReferenceCount(); + + return result; } public KernelResult MapPhysicalMemory(ulong address, ulong size) @@ -1271,29 +1296,9 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall public KernelResult CloseHandle(int handle) { - KProcess process = _context.Scheduler.GetCurrentProcess(); + KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); - KAutoObject obj = process.HandleTable.GetObject(handle); - - process.HandleTable.CloseHandle(handle); - - if (obj == null) - { - return KernelResult.InvalidHandle; - } - - if (obj is KSession session) - { - session.Dispose(); - } - else if (obj is KTransferMemory transferMemory) - { - process.MemoryManager.ResetTransferMemory( - transferMemory.Address, - transferMemory.Size); - } - - return KernelResult.Success; + return currentProcess.HandleTable.CloseHandle(handle) ? KernelResult.Success : KernelResult.InvalidHandle; } public KernelResult ResetSignal(int handle) diff --git a/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs b/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs index 2a0624d57..54f28af5b 100644 --- a/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs +++ b/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs @@ -104,6 +104,9 @@ namespace Ryujinx.HLE.HOS.Services.Sm throw new InvalidOperationException("Out of handles!"); } + session.ServerSession.DecrementReferenceCount(); + session.ClientSession.DecrementReferenceCount(); + context.Response.HandleDesc = IpcHandleDesc.MakeMove(handle); return ResultCode.Success;