aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcIpc.cs
diff options
context:
space:
mode:
authorgdkchan <gab.dark.100@gmail.com>2019-01-18 20:26:39 -0200
committerGitHub <noreply@github.com>2019-01-18 20:26:39 -0200
commit22bacc618815170c0d186a82e1ea4558e36b7063 (patch)
tree79b97959481fea1ac301da6d4e9dea9b991ece6f /Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcIpc.cs
parent3731d0ce8412c3c48286c242842bcb4940b4ca6d (diff)
Improve kernel IPC implementation (#550)
* Implement some IPC related kernel SVCs properly * Fix BLZ decompression when the segment also has a uncompressed chunck * Set default cpu core on process start from ProgramLoader, remove debug message * Load process capabilities properly on KIPs * Fix a copy/paste error in UnmapPhysicalMemory64 * Implement smarter switching between old and new IPC system to support the old HLE services implementation without the manual switch * Implement RegisterService on sm and AcceptSession (partial) * Misc fixes and improvements on new IPC methods * Move IPC related SVCs into a separate file, and logging on RegisterService (sm) * Some small fixes related to receive list buffers and error cases * Load NSOs using the correct pool partition * Fix corner case on GetMaskFromMinMax where range is 64, doesn't happen in pratice however * Fix send static buffer copy * Session release, implement closing requests on client disconnect * Implement ConnectToPort SVC * KLightSession init
Diffstat (limited to 'Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcIpc.cs')
-rw-r--r--Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcIpc.cs532
1 files changed, 532 insertions, 0 deletions
diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcIpc.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcIpc.cs
new file mode 100644
index 00000000..54939418
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcIpc.cs
@@ -0,0 +1,532 @@
+using Ryujinx.Common.Logging;
+using Ryujinx.HLE.HOS.Ipc;
+using Ryujinx.HLE.HOS.Kernel.Common;
+using Ryujinx.HLE.HOS.Kernel.Ipc;
+using Ryujinx.HLE.HOS.Kernel.Process;
+using Ryujinx.HLE.HOS.Kernel.Threading;
+using System.Threading;
+
+namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
+{
+ partial class SvcHandler
+ {
+ private struct HleIpcMessage
+ {
+ public KThread Thread { get; private set; }
+ public KClientSession Session { get; private set; }
+ public IpcMessage Message { get; private set; }
+ public long MessagePtr { get; private set; }
+
+ public HleIpcMessage(
+ KThread thread,
+ KClientSession session,
+ IpcMessage message,
+ long messagePtr)
+ {
+ Thread = thread;
+ Session = session;
+ Message = message;
+ MessagePtr = messagePtr;
+ }
+ }
+
+ public KernelResult ConnectToNamedPort64(ulong namePtr, out int handle)
+ {
+ return ConnectToNamedPort(namePtr, out handle);
+ }
+
+ private KernelResult ConnectToNamedPort(ulong namePtr, out int handle)
+ {
+ handle = 0;
+
+ if (!KernelTransfer.UserToKernelString(_system, namePtr, 12, out string name))
+ {
+ return KernelResult.UserCopyFailed;
+ }
+
+ if (name.Length > 11)
+ {
+ return KernelResult.MaximumExceeded;
+ }
+
+ KAutoObject autoObj = KAutoObject.FindNamedObject(_system, name);
+
+ if (!(autoObj is KClientPort clientPort))
+ {
+ return KernelResult.NotFound;
+ }
+
+ KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
+
+ KernelResult result = currentProcess.HandleTable.ReserveHandle(out handle);
+
+ if (result != KernelResult.Success)
+ {
+ return result;
+ }
+
+ result = clientPort.Connect(out KClientSession clientSession);
+
+ if (result != KernelResult.Success)
+ {
+ currentProcess.HandleTable.CancelHandleReservation(handle);
+
+ return result;
+ }
+
+ currentProcess.HandleTable.SetReservedHandleObj(handle, clientSession);
+
+ clientSession.DecrementReferenceCount();
+
+ return result;
+ }
+
+ public KernelResult SendSyncRequest64(int handle)
+ {
+ return SendSyncRequest((ulong)_system.Scheduler.GetCurrentThread().Context.ThreadState.Tpidr, 0x100, handle);
+ }
+
+ public KernelResult SendSyncRequestWithUserBuffer64(ulong messagePtr, ulong size, int handle)
+ {
+ return SendSyncRequest(messagePtr, size, handle);
+ }
+
+ private KernelResult SendSyncRequest(ulong messagePtr, ulong size, int handle)
+ {
+ byte[] messageData = _memory.ReadBytes((long)messagePtr, (long)size);
+
+ KClientSession clientSession = _process.HandleTable.GetObject<KClientSession>(handle);
+
+ if (clientSession == null || clientSession.Service == null)
+ {
+ return SendSyncRequest_(handle);
+ }
+
+ if (clientSession != null)
+ {
+ _system.CriticalSection.Enter();
+
+ KThread currentThread = _system.Scheduler.GetCurrentThread();
+
+ currentThread.SignaledObj = null;
+ currentThread.ObjSyncResult = KernelResult.Success;
+
+ currentThread.Reschedule(ThreadSchedState.Paused);
+
+ IpcMessage message = new IpcMessage(messageData, (long)messagePtr);
+
+ ThreadPool.QueueUserWorkItem(ProcessIpcRequest, new HleIpcMessage(
+ currentThread,
+ clientSession,
+ message,
+ (long)messagePtr));
+
+ _system.ThreadCounter.AddCount();
+
+ _system.CriticalSection.Leave();
+
+ return currentThread.ObjSyncResult;
+ }
+ else
+ {
+ Logger.PrintWarning(LogClass.KernelSvc, $"Invalid session handle 0x{handle:x8}!");
+
+ return KernelResult.InvalidHandle;
+ }
+ }
+
+ private void ProcessIpcRequest(object state)
+ {
+ HleIpcMessage ipcMessage = (HleIpcMessage)state;
+
+ ipcMessage.Thread.ObjSyncResult = IpcHandler.IpcCall(
+ _device,
+ _process,
+ _memory,
+ ipcMessage.Session,
+ ipcMessage.Message,
+ ipcMessage.MessagePtr);
+
+ _system.ThreadCounter.Signal();
+
+ ipcMessage.Thread.Reschedule(ThreadSchedState.Running);
+ }
+
+ private KernelResult SendSyncRequest_(int handle)
+ {
+ KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
+
+ KClientSession session = currentProcess.HandleTable.GetObject<KClientSession>(handle);
+
+ if (session == null)
+ {
+ return KernelResult.InvalidHandle;
+ }
+
+ return session.SendSyncRequest();
+ }
+
+ public KernelResult CreateSession64(
+ bool isLight,
+ ulong namePtr,
+ out int serverSessionHandle,
+ out int clientSessionHandle)
+ {
+ return CreateSession(isLight, namePtr, out serverSessionHandle, out clientSessionHandle);
+ }
+
+ private KernelResult CreateSession(
+ bool isLight,
+ ulong namePtr,
+ out int serverSessionHandle,
+ out int clientSessionHandle)
+ {
+ serverSessionHandle = 0;
+ clientSessionHandle = 0;
+
+ KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
+
+ KResourceLimit resourceLimit = currentProcess.ResourceLimit;
+
+ KernelResult result = KernelResult.Success;
+
+ if (resourceLimit != null && !resourceLimit.Reserve(LimitableResource.Session, 1))
+ {
+ return KernelResult.ResLimitExceeded;
+ }
+
+ if (isLight)
+ {
+ KLightSession session = new KLightSession(_system);
+
+ result = currentProcess.HandleTable.GenerateHandle(session.ServerSession, out serverSessionHandle);
+
+ if (result == KernelResult.Success)
+ {
+ result = currentProcess.HandleTable.GenerateHandle(session.ClientSession, out clientSessionHandle);
+
+ if (result != KernelResult.Success)
+ {
+ currentProcess.HandleTable.CloseHandle(serverSessionHandle);
+
+ serverSessionHandle = 0;
+ }
+ }
+
+ session.ServerSession.DecrementReferenceCount();
+ session.ClientSession.DecrementReferenceCount();
+ }
+ else
+ {
+ KSession session = new KSession(_system);
+
+ result = currentProcess.HandleTable.GenerateHandle(session.ServerSession, out serverSessionHandle);
+
+ if (result == KernelResult.Success)
+ {
+ result = currentProcess.HandleTable.GenerateHandle(session.ClientSession, out clientSessionHandle);
+
+ if (result != KernelResult.Success)
+ {
+ currentProcess.HandleTable.CloseHandle(serverSessionHandle);
+
+ serverSessionHandle = 0;
+ }
+ }
+
+ session.ServerSession.DecrementReferenceCount();
+ session.ClientSession.DecrementReferenceCount();
+ }
+
+ return result;
+ }
+
+ public KernelResult AcceptSession64(int portHandle, out int sessionHandle)
+ {
+ return AcceptSession(portHandle, out sessionHandle);
+ }
+
+ private KernelResult AcceptSession(int portHandle, out int sessionHandle)
+ {
+ sessionHandle = 0;
+
+ KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
+
+ KServerPort serverPort = currentProcess.HandleTable.GetObject<KServerPort>(portHandle);
+
+ if (serverPort == null)
+ {
+ return KernelResult.InvalidHandle;
+ }
+
+ KernelResult result = currentProcess.HandleTable.ReserveHandle(out int handle);
+
+ if (result != KernelResult.Success)
+ {
+ return result;
+ }
+
+ KAutoObject session;
+
+ if (serverPort.IsLight)
+ {
+ session = serverPort.AcceptIncomingLightConnection();
+ }
+ else
+ {
+ session = serverPort.AcceptIncomingConnection();
+ }
+
+ if (session != null)
+ {
+ currentProcess.HandleTable.SetReservedHandleObj(handle, session);
+
+ session.DecrementReferenceCount();
+
+ sessionHandle = handle;
+
+ result = KernelResult.Success;
+ }
+ else
+ {
+ currentProcess.HandleTable.CancelHandleReservation(handle);
+
+ result = KernelResult.NotFound;
+ }
+
+ return result;
+ }
+
+ public KernelResult ReplyAndReceive64(
+ ulong handlesPtr,
+ int handlesCount,
+ int replyTargetHandle,
+ long timeout,
+ out int handleIndex)
+ {
+ handleIndex = 0;
+
+ if ((uint)handlesCount > 0x40)
+ {
+ return KernelResult.MaximumExceeded;
+ }
+
+ KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
+
+ ulong copySize = (ulong)((long)handlesCount * 4);
+
+ if (!currentProcess.MemoryManager.InsideAddrSpace(handlesPtr, copySize))
+ {
+ return KernelResult.UserCopyFailed;
+ }
+
+ if (handlesPtr + copySize < handlesPtr)
+ {
+ return KernelResult.UserCopyFailed;
+ }
+
+ int[] handles = new int[handlesCount];
+
+ if (!KernelTransfer.UserToKernelInt32Array(_system, handlesPtr, handles))
+ {
+ 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 KernelResult.InvalidHandle;
+ }
+
+ syncObjs[index] = obj;
+ }
+
+ KernelResult result;
+
+ if (replyTargetHandle != 0)
+ {
+ KServerSession replyTarget = currentProcess.HandleTable.GetObject<KServerSession>(replyTargetHandle);
+
+ if (replyTarget == null)
+ {
+ return KernelResult.InvalidHandle;
+ }
+
+ result = replyTarget.Reply();
+
+ if (result != KernelResult.Success)
+ {
+ return result;
+ }
+ }
+
+ while ((result = _system.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 CreatePort64(
+ int maxSessions,
+ bool isLight,
+ ulong namePtr,
+ out int serverPortHandle,
+ out int clientPortHandle)
+ {
+ return CreatePort(maxSessions, isLight, namePtr, out serverPortHandle, out clientPortHandle);
+ }
+
+ private KernelResult CreatePort(
+ int maxSessions,
+ bool isLight,
+ ulong namePtr,
+ out int serverPortHandle,
+ out int clientPortHandle)
+ {
+ serverPortHandle = clientPortHandle = 0;
+
+ if (maxSessions < 1)
+ {
+ return KernelResult.MaximumExceeded;
+ }
+
+ KPort port = new KPort(_system, maxSessions, isLight, (long)namePtr);
+
+ KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
+
+ KernelResult result = currentProcess.HandleTable.GenerateHandle(port.ClientPort, out clientPortHandle);
+
+ if (result != KernelResult.Success)
+ {
+ return result;
+ }
+
+ result = currentProcess.HandleTable.GenerateHandle(port.ServerPort, out serverPortHandle);
+
+ if (result != KernelResult.Success)
+ {
+ currentProcess.HandleTable.CloseHandle(clientPortHandle);
+ }
+
+ return result;
+ }
+
+ public KernelResult ManageNamedPort64(ulong namePtr, int maxSessions, out int handle)
+ {
+ return ManageNamedPort(namePtr, maxSessions, out handle);
+ }
+
+ private KernelResult ManageNamedPort(ulong namePtr, int maxSessions, out int handle)
+ {
+ handle = 0;
+
+ if (!KernelTransfer.UserToKernelString(_system, namePtr, 12, out string name))
+ {
+ return KernelResult.UserCopyFailed;
+ }
+
+ if (maxSessions < 0 || name.Length > 11)
+ {
+ return KernelResult.MaximumExceeded;
+ }
+
+ if (maxSessions == 0)
+ {
+ return KClientPort.RemoveName(_system, name);
+ }
+
+ KPort port = new KPort(_system, maxSessions, false, 0);
+
+ KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
+
+ KernelResult result = currentProcess.HandleTable.GenerateHandle(port.ServerPort, out handle);
+
+ if (result != KernelResult.Success)
+ {
+ return result;
+ }
+
+ result = port.ClientPort.SetName(name);
+
+ if (result != KernelResult.Success)
+ {
+ currentProcess.HandleTable.CloseHandle(handle);
+ }
+
+ return result;
+ }
+
+ public KernelResult ConnectToPort64(int clientPortHandle, out int clientSessionHandle)
+ {
+ return ConnectToPort(clientPortHandle, out clientSessionHandle);
+ }
+
+ private KernelResult ConnectToPort(int clientPortHandle, out int clientSessionHandle)
+ {
+ clientSessionHandle = 0;
+
+ KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
+
+ KClientPort clientPort = currentProcess.HandleTable.GetObject<KClientPort>(clientPortHandle);
+
+ if (clientPort == null)
+ {
+ return KernelResult.InvalidHandle;
+ }
+
+ KernelResult result = currentProcess.HandleTable.ReserveHandle(out int handle);
+
+ if (result != KernelResult.Success)
+ {
+ return result;
+ }
+
+ KAutoObject session;
+
+ if (clientPort.IsLight)
+ {
+ result = clientPort.ConnectLight(out KLightClientSession clientSession);
+
+ session = clientSession;
+ }
+ else
+ {
+ result = clientPort.Connect(out KClientSession clientSession);
+
+ session = clientSession;
+ }
+
+ if (result != KernelResult.Success)
+ {
+ currentProcess.HandleTable.CancelHandleReservation(handle);
+
+ return result;
+ }
+
+ currentProcess.HandleTable.SetReservedHandleObj(handle, session);
+
+ session.DecrementReferenceCount();
+
+ clientSessionHandle = handle;
+
+ return result;
+ }
+ }
+}