From 357c0c7977eb685cbe5df83d50b7b784660728db Mon Sep 17 00:00:00 2001 From: gdkchan Date: Fri, 28 Dec 2018 02:45:18 -0300 Subject: [PATCH] Implement RegisterService on sm and AcceptSession (partial) --- Ryujinx.HLE/HOS/Kernel/Ipc/KClientSession.cs | 2 + Ryujinx.HLE/HOS/Kernel/Ipc/KLightSession.cs | 7 ++ .../HOS/Kernel/SupervisorCall/SvcSystem.cs | 60 ++++++++++ .../HOS/Kernel/SupervisorCall/SvcTable.cs | 1 + Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs | 112 ++++++++++++++---- Ryujinx.HLE/HOS/Services/Sm/SmErr.cs | 9 ++ 6 files changed, 168 insertions(+), 23 deletions(-) create mode 100644 Ryujinx.HLE/HOS/Kernel/Ipc/KLightSession.cs create mode 100644 Ryujinx.HLE/HOS/Services/Sm/SmErr.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KClientSession.cs b/Ryujinx.HLE/HOS/Kernel/Ipc/KClientSession.cs index abb99440a1..eaaf65805b 100644 --- a/Ryujinx.HLE/HOS/Kernel/Ipc/KClientSession.cs +++ b/Ryujinx.HLE/HOS/Kernel/Ipc/KClientSession.cs @@ -13,6 +13,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc public int ResourceStatus { get; private set; } + //TODO: Remove that, we need it for now to allow HLE + //services implementation to work with the new IPC system. public IpcService Service { get; set; } public KClientSession(Horizon system, KSession parent) : base(system) diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KLightSession.cs b/Ryujinx.HLE/HOS/Kernel/Ipc/KLightSession.cs new file mode 100644 index 0000000000..90eb897d23 --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/Ipc/KLightSession.cs @@ -0,0 +1,7 @@ +namespace Ryujinx.HLE.HOS.Kernel.Ipc +{ + class KLightSession : KSession + { + public KLightSession(Horizon system) : base(system) { } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcSystem.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcSystem.cs index d2e4754f21..821a2afb4b 100644 --- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcSystem.cs +++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcSystem.cs @@ -573,6 +573,66 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return KernelResult.Success; } + 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; + } + + KSession session; + + if (isLight) + { + session = new KLightSession(_system); + } + else + { + 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); diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcTable.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcTable.cs index b04f476022..96fe51f783 100644 --- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcTable.cs +++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcTable.cs @@ -63,6 +63,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall { 0x33, nameof(SvcHandler.GetThreadContext364) }, { 0x34, nameof(SvcHandler.WaitForAddress64) }, { 0x35, nameof(SvcHandler.SignalToAddress64) }, + { 0x40, nameof(SvcHandler.CreateSession64) }, { 0x41, nameof(SvcHandler.AcceptSession64) }, { 0x43, nameof(SvcHandler.ReplyAndReceive64) }, { 0x45, nameof(SvcHandler.CreateEvent64) }, diff --git a/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs b/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs index 86204b54ce..d09c92a4a0 100644 --- a/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs +++ b/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs @@ -2,7 +2,9 @@ using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Ipc; using System; +using System.Collections.Concurrent; using System.Collections.Generic; +using System.IO; namespace Ryujinx.HLE.HOS.Services.Sm { @@ -12,15 +14,20 @@ namespace Ryujinx.HLE.HOS.Services.Sm public override IReadOnlyDictionary Commands => _commands; + private ConcurrentDictionary _registeredServices; + private bool _isInitialized; public IUserInterface() { _commands = new Dictionary { - { 0, Initialize }, - { 1, GetService } + { 0, Initialize }, + { 1, GetService }, + { 2, RegisterService } }; + + _registeredServices = new ConcurrentDictionary(); } public static void InitializePort(Horizon system) @@ -34,8 +41,6 @@ namespace Ryujinx.HLE.HOS.Services.Sm port.ClientPort.Service = new IUserInterface(); } - private const int SmNotInitialized = 0x415; - public long Initialize(ServiceCtx context) { _isInitialized = true; @@ -45,12 +50,89 @@ namespace Ryujinx.HLE.HOS.Services.Sm public long GetService(ServiceCtx context) { - //Only for kernel version > 3.0.0. if (!_isInitialized) { - //return SmNotInitialized; + return ErrorCode.MakeError(ErrorModule.Sm, SmErr.NotInitialized); } + string name = ReadName(context); + + if (name == string.Empty) + { + return ErrorCode.MakeError(ErrorModule.Sm, SmErr.InvalidName); + } + + KSession session = new KSession(context.Device.System); + + if (_registeredServices.TryGetValue(name, out KPort port)) + { + KernelResult result = port.EnqueueIncomingSession(session.ServerSession); + + if (result != KernelResult.Success) + { + throw new InvalidOperationException($"Session enqueue on port returned error \"{result}\"."); + } + } + else + { + session.ClientSession.Service = ServiceFactory.MakeService(context.Device.System, name); + } + + if (context.Process.HandleTable.GenerateHandle(session.ClientSession, out int handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + + context.Response.HandleDesc = IpcHandleDesc.MakeMove(handle); + + return 0; + } + + public long RegisterService(ServiceCtx context) + { + if (!_isInitialized) + { + return ErrorCode.MakeError(ErrorModule.Sm, SmErr.NotInitialized); + } + + long namePosition = context.RequestData.BaseStream.Position; + + string name = ReadName(context); + + context.RequestData.BaseStream.Seek(namePosition + 8, SeekOrigin.Begin); + + bool isLight = (context.RequestData.ReadInt32() & 1) != 0; + + int maxSessions = context.RequestData.ReadInt32(); + + if (name == string.Empty) + { + return ErrorCode.MakeError(ErrorModule.Sm, SmErr.InvalidName); + } + + System.Console.WriteLine("register service " + name + " " + maxSessions); + + KPort port = new KPort(context.Device.System); + + port.Initialize(maxSessions, isLight, 0); + + if (!_registeredServices.TryAdd(name, port)) + { + return ErrorCode.MakeError(ErrorModule.Sm, SmErr.AlreadyRegistered); + } + + if (context.Process.HandleTable.GenerateHandle(port.ServerPort, out int handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + + context.Response.HandleDesc = IpcHandleDesc.MakeMove(handle); + + return 0; + } + + private static string ReadName(ServiceCtx context) + { string name = string.Empty; for (int index = 0; index < 8 && @@ -65,23 +147,7 @@ namespace Ryujinx.HLE.HOS.Services.Sm } } - if (name == string.Empty) - { - return 0; - } - - KSession session = new KSession(context.Device.System); - - session.ClientSession.Service = ServiceFactory.MakeService(context.Device.System, name); - - if (context.Process.HandleTable.GenerateHandle(session.ClientSession, out int handle) != KernelResult.Success) - { - throw new InvalidOperationException("Out of handles!"); - } - - context.Response.HandleDesc = IpcHandleDesc.MakeMove(handle); - - return 0; + return name; } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Sm/SmErr.cs b/Ryujinx.HLE/HOS/Services/Sm/SmErr.cs new file mode 100644 index 0000000000..5b5a66dc2f --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Sm/SmErr.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.HLE.HOS.Services.Sm +{ + static class SmErr + { + public const int NotInitialized = 2; + public const int AlreadyRegistered = 4; + public const int InvalidName = 6; + } +} \ No newline at end of file