diff options
| author | gdkchan <gab.dark.100@gmail.com> | 2020-07-17 01:19:07 -0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-07-17 14:19:07 +1000 |
| commit | 9f6b24edfddf871320290463437b3f3cb7e29006 (patch) | |
| tree | 4b8b45db5fe931ac37f843778c58a2d676fe3fba /Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs | |
| parent | 46f8cef6a9e4a305803f1356446e25ce54909130 (diff) | |
Improve kernel IPC related syscalls (#1379)
* Implement session count decrement when the handle is closed
* Remove unused field
* Implement SendSyncRequestWithUserBuffer, SendAsyncRequestWithUserBuffer and ReplyAndReceiveWithUserBuffer syscalls
* Nits
* Fix swapped copy dst/src
* Add missing pointer buffer descriptor write on reply
* Fix IPC unaligned buffer copy and restoring client attributes on reply
* Oops
* Fix SetIpcMappingPermission
* Fix unaligned copy bugs
* Free memory used for temporary IPC buffers
Diffstat (limited to 'Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs')
| -rw-r--r-- | Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs | 262 |
1 files changed, 243 insertions, 19 deletions
diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs index a0a15fcf..fba22fc1 100644 --- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs +++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs @@ -1,5 +1,4 @@ -using ARMeilleure.Memory; -using Ryujinx.Common; +using Ryujinx.Common; using Ryujinx.Common.Logging; using Ryujinx.Cpu; using Ryujinx.HLE.Exceptions; @@ -96,16 +95,25 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return result; } - public KernelResult SendSyncRequest(int handle) + public KernelResult SendSyncRequestHLE(int handle) { - return SendSyncRequestWithUserBuffer((ulong)_context.Scheduler.GetCurrentThread().Context.Tpidr, 0x100, handle); + KProcess process = _context.Scheduler.GetCurrentProcess(); + + KClientSession clientSession = process.HandleTable.GetObject<KClientSession>(handle); + + if (clientSession == null || clientSession.Service == null) + { + return SendSyncRequest(handle); + } + + return SendSyncRequestWithUserBufferHLE((ulong)_context.Scheduler.GetCurrentThread().Context.Tpidr, 0x100, handle); } - public KernelResult SendSyncRequestWithUserBuffer(ulong messagePtr, ulong size, int handle) + public KernelResult SendSyncRequestWithUserBufferHLE(ulong messagePtr, ulong messageSize, int handle) { KProcess process = _context.Scheduler.GetCurrentProcess(); - byte[] messageData = new byte[size]; + byte[] messageData = new byte[messageSize]; process.CpuMemory.Read(messagePtr, messageData); @@ -113,7 +121,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall if (clientSession == null || clientSession.Service == null) { - return SendSyncRequest_(handle); + return SendSyncRequestWithUserBuffer(messagePtr, messageSize, handle); } if (clientSession != null) @@ -168,7 +176,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall ipcMessage.Thread.Reschedule(ThreadSchedState.Running); } - private KernelResult SendSyncRequest_(int handle) + private KernelResult SendSyncRequest(int handle) { KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); @@ -182,6 +190,123 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return session.SendSyncRequest(); } + public KernelResult SendSyncRequestWithUserBuffer(ulong messagePtr, ulong messageSize, int handle) + { + if (!PageAligned(messagePtr)) + { + return KernelResult.InvalidAddress; + } + + if (!PageAligned(messageSize) || messageSize == 0) + { + return KernelResult.InvalidSize; + } + + if (messagePtr + messageSize <= messagePtr) + { + return KernelResult.InvalidMemState; + } + + KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); + + KernelResult result = currentProcess.MemoryManager.BorrowIpcBuffer(messagePtr, messageSize); + + if (result != KernelResult.Success) + { + return result; + } + + KClientSession session = currentProcess.HandleTable.GetObject<KClientSession>(handle); + + if (session == null) + { + result = KernelResult.InvalidHandle; + } + else + { + result = session.SendSyncRequest(messagePtr, messageSize); + } + + KernelResult result2 = currentProcess.MemoryManager.UnborrowIpcBuffer(messagePtr, messageSize); + + if (result == KernelResult.Success) + { + result = result2; + } + + return result; + } + + public KernelResult SendAsyncRequestWithUserBuffer(ulong messagePtr, ulong messageSize, int handle, out int doneEventHandle) + { + doneEventHandle = 0; + + if (!PageAligned(messagePtr)) + { + return KernelResult.InvalidAddress; + } + + if (!PageAligned(messageSize) || messageSize == 0) + { + return KernelResult.InvalidSize; + } + + if (messagePtr + messageSize <= messagePtr) + { + return KernelResult.InvalidMemState; + } + + KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); + + KernelResult result = currentProcess.MemoryManager.BorrowIpcBuffer(messagePtr, messageSize); + + if (result != KernelResult.Success) + { + return result; + } + + KResourceLimit resourceLimit = currentProcess.ResourceLimit; + + if (resourceLimit != null && !resourceLimit.Reserve(LimitableResource.Event, 1)) + { + currentProcess.MemoryManager.UnborrowIpcBuffer(messagePtr, messageSize); + + return KernelResult.ResLimitExceeded; + } + + KClientSession session = currentProcess.HandleTable.GetObject<KClientSession>(handle); + + if (session == null) + { + result = KernelResult.InvalidHandle; + } + else + { + KEvent doneEvent = new KEvent(_context); + + result = currentProcess.HandleTable.GenerateHandle(doneEvent.ReadableEvent, out doneEventHandle); + + if (result == KernelResult.Success) + { + result = session.SendAsyncRequest(doneEvent.WritableEvent, messagePtr, messageSize); + + if (result != KernelResult.Success) + { + currentProcess.HandleTable.CloseHandle(doneEventHandle); + } + } + } + + if (result != KernelResult.Success) + { + resourceLimit?.Release(LimitableResource.Event, 1); + + currentProcess.MemoryManager.UnborrowIpcBuffer(messagePtr, messageSize); + } + + return result; + } + public KernelResult CreateSession( bool isLight, ulong namePtr, @@ -348,7 +473,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall syncObjs[index] = obj; } - KernelResult result; + KernelResult result = KernelResult.Success; if (replyTargetHandle != 0) { @@ -356,32 +481,131 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall if (replyTarget == null) { - return KernelResult.InvalidHandle; + result = KernelResult.InvalidHandle; } + else + { + result = replyTarget.Reply(); + } + } - result = replyTarget.Reply(); + if (result == KernelResult.Success) + { + while ((result = _context.Synchronization.WaitFor(syncObjs, timeout, out handleIndex)) == KernelResult.Success) + { + KServerSession session = currentProcess.HandleTable.GetObject<KServerSession>(handles[handleIndex]); - if (result != KernelResult.Success) + if (session == null) + { + break; + } + + if ((result = session.Receive()) != KernelResult.NotFound) + { + break; + } + } + } + + return result; + } + + public KernelResult ReplyAndReceiveWithUserBuffer( + ulong handlesPtr, + ulong messagePtr, + ulong messageSize, + int handlesCount, + int replyTargetHandle, + long timeout, + out int handleIndex) + { + handleIndex = 0; + + if ((uint)handlesCount > 0x40) + { + return KernelResult.MaximumExceeded; + } + + KProcess currentProcess = _context.Scheduler.GetCurrentProcess(); + + ulong copySize = (ulong)((long)handlesCount * 4); + + if (!currentProcess.MemoryManager.InsideAddrSpace(handlesPtr, copySize)) + { + return KernelResult.UserCopyFailed; + } + + if (handlesPtr + copySize < handlesPtr) + { + return KernelResult.UserCopyFailed; + } + + KernelResult result = currentProcess.MemoryManager.BorrowIpcBuffer(messagePtr, messageSize); + + if (result != KernelResult.Success) + { + return result; + } + + int[] handles = new int[handlesCount]; + + if (!KernelTransfer.UserToKernelInt32Array(_context, handlesPtr, handles)) + { + currentProcess.MemoryManager.UnborrowIpcBuffer(messagePtr, messageSize); + + return KernelResult.UserCopyFailed; + } + + KSynchronizationObject[] syncObjs = new KSynchronizationObject[handlesCount]; + + for (int index = 0; index < handlesCount; index++) + { + KSynchronizationObject obj = currentProcess.HandleTable.GetObject<KSynchronizationObject>(handles[index]); + + if (obj == null) { - return result; + currentProcess.MemoryManager.UnborrowIpcBuffer(messagePtr, messageSize); + + return KernelResult.InvalidHandle; } + + syncObjs[index] = obj; } - while ((result = _context.Synchronization.WaitFor(syncObjs, timeout, out handleIndex)) == KernelResult.Success) + if (replyTargetHandle != 0) { - KServerSession session = currentProcess.HandleTable.GetObject<KServerSession>(handles[handleIndex]); + KServerSession replyTarget = currentProcess.HandleTable.GetObject<KServerSession>(replyTargetHandle); - if (session == null) + if (replyTarget == null) { - break; + result = KernelResult.InvalidHandle; + } + else + { + result = replyTarget.Reply(messagePtr, messageSize); } + } - if ((result = session.Receive()) != KernelResult.NotFound) + if (result == KernelResult.Success) + { + while ((result = _context.Synchronization.WaitFor(syncObjs, timeout, out handleIndex)) == KernelResult.Success) { - break; + KServerSession session = currentProcess.HandleTable.GetObject<KServerSession>(handles[handleIndex]); + + if (session == null) + { + break; + } + + if ((result = session.Receive(messagePtr, messageSize)) != KernelResult.NotFound) + { + break; + } } } + currentProcess.MemoryManager.UnborrowIpcBuffer(messagePtr, messageSize); + return result; } |
