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
This commit is contained in:
parent
46f8cef6a9
commit
9f6b24edfd
14 changed files with 705 additions and 247 deletions
|
@ -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)
|
||||
{
|
||||
return SendSyncRequestWithUserBuffer((ulong)_context.Scheduler.GetCurrentThread().Context.Tpidr, 0x100, handle);
|
||||
}
|
||||
|
||||
public KernelResult SendSyncRequestWithUserBuffer(ulong messagePtr, ulong size, int handle)
|
||||
public KernelResult SendSyncRequestHLE(int handle)
|
||||
{
|
||||
KProcess process = _context.Scheduler.GetCurrentProcess();
|
||||
|
||||
byte[] messageData = new byte[size];
|
||||
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 SendSyncRequestWithUserBufferHLE(ulong messagePtr, ulong messageSize, int handle)
|
||||
{
|
||||
KProcess process = _context.Scheduler.GetCurrentProcess();
|
||||
|
||||
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)
|
||||
{
|
||||
result = KernelResult.InvalidHandle;
|
||||
}
|
||||
else
|
||||
{
|
||||
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 (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)
|
||||
{
|
||||
currentProcess.MemoryManager.UnborrowIpcBuffer(messagePtr, messageSize);
|
||||
|
||||
return KernelResult.InvalidHandle;
|
||||
}
|
||||
|
||||
result = replyTarget.Reply();
|
||||
|
||||
if (result != KernelResult.Success)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
if ((result = session.Receive()) != KernelResult.NotFound)
|
||||
else
|
||||
{
|
||||
break;
|
||||
result = replyTarget.Reply(messagePtr, messageSize);
|
||||
}
|
||||
}
|
||||
|
||||
if (result == KernelResult.Success)
|
||||
{
|
||||
while ((result = _context.Synchronization.WaitFor(syncObjs, timeout, out handleIndex)) == KernelResult.Success)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue