aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.HLE/HOS/Services/ServerBase.cs
diff options
context:
space:
mode:
authorgdkchan <gab.dark.100@gmail.com>2020-12-01 20:23:43 -0300
committerGitHub <noreply@github.com>2020-12-02 00:23:43 +0100
commitcf6cd714884c41e9550757e364c2f4f5b04fc7f3 (patch)
treebea748b4d1a350e5b8075d63ec9d39d49693829d /Ryujinx.HLE/HOS/Services/ServerBase.cs
parent461c24092ae6e148d896c18aa3e86220c89981f8 (diff)
IPC refactor part 2: Use ReplyAndReceive on HLE services and remove special handling from kernel (#1458)
* IPC refactor part 2: Use ReplyAndReceive on HLE services and remove special handling from kernel * Fix for applet transfer memory + some nits * Keep handles if possible to avoid server handle table exhaustion * Fix IPC ZeroFill bug * am: Correctly implement CreateManagedDisplayLayer and implement CreateManagedDisplaySeparableLayer CreateManagedDisplaySeparableLayer is requires since 10.x+ when appletResourceUserId != 0 * Make it exit properly * Make ServiceNotImplementedException show the full message again * Allow yielding execution to avoid starving other threads * Only wait if active * Merge IVirtualMemoryManager and IAddressSpaceManager * Fix Ro loading data from the wrong process Co-authored-by: Thog <me@thog.eu>
Diffstat (limited to 'Ryujinx.HLE/HOS/Services/ServerBase.cs')
-rw-r--r--Ryujinx.HLE/HOS/Services/ServerBase.cs233
1 files changed, 184 insertions, 49 deletions
diff --git a/Ryujinx.HLE/HOS/Services/ServerBase.cs b/Ryujinx.HLE/HOS/Services/ServerBase.cs
index 211e0e6b..f70d930f 100644
--- a/Ryujinx.HLE/HOS/Services/ServerBase.cs
+++ b/Ryujinx.HLE/HOS/Services/ServerBase.cs
@@ -1,62 +1,193 @@
-using Ryujinx.Common;
using Ryujinx.HLE.HOS.Ipc;
+using Ryujinx.HLE.HOS.Kernel;
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;
+using System.Buffers.Binary;
+using System.Collections.Generic;
using System.IO;
+using System.Threading;
namespace Ryujinx.HLE.HOS.Services
{
class ServerBase
{
- private struct IpcRequest
+ // Must be the maximum value used by services (highest one know is the one used by nvservices = 0x8000).
+ // Having a size that is too low will cause failures as data copy will fail if the receiving buffer is
+ // not large enough.
+ private const int PointerBufferSize = 0x8000;
+
+ private readonly static int[] DefaultCapabilities = new int[]
{
- public Switch Device { get; }
- public KProcess Process => Thread?.Owner;
- public KThread Thread { get; }
- public KClientSession Session { get; }
- public ulong MessagePtr { get; }
- public ulong MessageSize { get; }
-
- public IpcRequest(Switch device, KThread thread, KClientSession session, ulong messagePtr, ulong messageSize)
- {
- Device = device;
- Thread = thread;
- Session = session;
- MessagePtr = messagePtr;
- MessageSize = messageSize;
- }
+ 0x030363F7,
+ 0x1FFFFFCF,
+ 0x207FFFEF,
+ 0x47E0060F,
+ 0x0048BFFF,
+ 0x01007FFF
+ };
+
+ private readonly KernelContext _context;
+ private readonly KProcess _selfProcess;
+
+ private readonly List<int> _sessionHandles = new List<int>();
+ private readonly List<int> _portHandles = new List<int>();
+ private readonly Dictionary<int, IpcService> _sessions = new Dictionary<int, IpcService>();
+ private readonly Dictionary<int, IpcService> _ports = new Dictionary<int, IpcService>();
+
+ public ManualResetEvent InitDone { get; }
+ public IpcService SmObject { get; set; }
+ public string Name { get; }
+
+ public ServerBase(KernelContext context, string name)
+ {
+ InitDone = new ManualResetEvent(false);
+ Name = name;
+ _context = context;
- public void SignalDone(KernelResult result)
- {
- Thread.ObjSyncResult = result;
- Thread.Reschedule(ThreadSchedState.Running);
- }
+ const ProcessCreationFlags flags =
+ ProcessCreationFlags.EnableAslr |
+ ProcessCreationFlags.AddressSpace64Bit |
+ ProcessCreationFlags.Is64Bit |
+ ProcessCreationFlags.PoolPartitionSystem;
+
+ ProcessCreationInfo creationInfo = new ProcessCreationInfo("Service", 1, 0, 0x8000000, 1, flags, 0, 0);
+
+ context.Syscall.CreateProcess(creationInfo, DefaultCapabilities, out int handle, null, ServerLoop);
+
+ _selfProcess = context.Scheduler.GetCurrentProcess().HandleTable.GetKProcess(handle);
+
+ context.Syscall.StartProcess(handle, 44, 3, 0x1000);
}
- private readonly AsyncWorkQueue<IpcRequest> _ipcProcessor;
+ private void AddPort(int serverPortHandle, IpcService obj)
+ {
+ _portHandles.Add(serverPortHandle);
+ _ports.Add(serverPortHandle, obj);
+ }
- public ServerBase(string name)
+ public void AddSessionObj(KServerSession serverSession, IpcService obj)
{
- _ipcProcessor = new AsyncWorkQueue<IpcRequest>(Process, name);
+ _selfProcess.HandleTable.GenerateHandle(serverSession, out int serverSessionHandle);
+ AddSessionObj(serverSessionHandle, obj);
}
- public void PushMessage(Switch device, KThread thread, KClientSession session, ulong messagePtr, ulong messageSize)
+ public void AddSessionObj(int serverSessionHandle, IpcService obj)
{
- _ipcProcessor.Add(new IpcRequest(device, thread, session, messagePtr, messageSize));
+ _sessionHandles.Add(serverSessionHandle);
+ _sessions.Add(serverSessionHandle, obj);
}
- private void Process(IpcRequest message)
+ private void ServerLoop()
{
- byte[] reqData = new byte[message.MessageSize];
+ if (SmObject != null)
+ {
+ _context.Syscall.ManageNamedPort("sm:", 50, out int serverPortHandle);
+
+ AddPort(serverPortHandle, SmObject);
+
+ InitDone.Set();
+ }
+ else
+ {
+ InitDone.Dispose();
+ }
+
+ KThread thread = _context.Scheduler.GetCurrentThread();
+ ulong messagePtr = thread.TlsAddress;
+ _context.Syscall.SetHeapSize(0x200000, out ulong heapAddr);
+
+ _selfProcess.CpuMemory.Write(messagePtr + 0x0, 0);
+ _selfProcess.CpuMemory.Write(messagePtr + 0x4, 2 << 10);
+ _selfProcess.CpuMemory.Write(messagePtr + 0x8, heapAddr | ((ulong)PointerBufferSize << 48));
+
+ int replyTargetHandle = 0;
+
+ while (true)
+ {
+ int[] handles = _portHandles.ToArray();
+
+ for (int i = 0; i < handles.Length; i++)
+ {
+ if (_context.Syscall.AcceptSession(handles[i], out int serverSessionHandle) == KernelResult.Success)
+ {
+ AddSessionObj(serverSessionHandle, _ports[handles[i]]);
+ }
+ }
+
+ handles = _sessionHandles.ToArray();
+
+ var rc = _context.Syscall.ReplyAndReceive(handles, replyTargetHandle, 1000000L, out int signaledIndex);
+
+ thread.HandlePostSyscall();
+
+ if (!thread.Context.Running)
+ {
+ break;
+ }
- message.Process.CpuMemory.Read(message.MessagePtr, reqData);
+ replyTargetHandle = 0;
- IpcMessage request = new IpcMessage(reqData, (long)message.MessagePtr);
+ if (rc == KernelResult.Success && signaledIndex != -1)
+ {
+ int signaledHandle = handles[signaledIndex];
+
+ if (Process(signaledHandle, heapAddr))
+ {
+ replyTargetHandle = signaledHandle;
+ }
+ }
+ else
+ {
+ _selfProcess.CpuMemory.Write(messagePtr + 0x0, 0);
+ _selfProcess.CpuMemory.Write(messagePtr + 0x4, 2 << 10);
+ _selfProcess.CpuMemory.Write(messagePtr + 0x8, heapAddr | ((ulong)PointerBufferSize << 48));
+ }
+ }
+ }
+
+ private bool Process(int serverSessionHandle, ulong recvListAddr)
+ {
+ KProcess process = _context.Scheduler.GetCurrentProcess();
+ KThread thread = _context.Scheduler.GetCurrentThread();
+ ulong messagePtr = thread.TlsAddress;
+ ulong messageSize = 0x100;
+
+ byte[] reqData = new byte[messageSize];
+
+ process.CpuMemory.Read(messagePtr, reqData);
+
+ IpcMessage request = new IpcMessage(reqData, (long)messagePtr);
IpcMessage response = new IpcMessage();
+ ulong tempAddr = recvListAddr;
+ int sizesOffset = request.RawData.Length - ((request.RecvListBuff.Count * 2 + 3) & ~3);
+
+ bool noReceive = true;
+
+ for (int i = 0; i < request.ReceiveBuff.Count; i++)
+ {
+ noReceive &= (request.ReceiveBuff[i].Position == 0);
+ }
+
+ if (noReceive)
+ {
+ for (int i = 0; i < request.RecvListBuff.Count; i++)
+ {
+ int size = BinaryPrimitives.ReadInt16LittleEndian(request.RawData.AsSpan().Slice(sizesOffset + i * 2, 2));
+
+ response.PtrBuff.Add(new IpcPtrBuffDesc((long)tempAddr, i, size));
+
+ request.RecvListBuff[i] = new IpcRecvListBuffDesc((long)tempAddr, size);
+
+ tempAddr += (ulong)size;
+ }
+ }
+
+ bool shouldReply = true;
+
using (MemoryStream raw = new MemoryStream(request.RawData))
{
BinaryReader reqReader = new BinaryReader(raw);
@@ -71,17 +202,16 @@ namespace Ryujinx.HLE.HOS.Services
BinaryWriter resWriter = new BinaryWriter(resMs);
ServiceCtx context = new ServiceCtx(
- message.Device,
- message.Process,
- message.Process.CpuMemory,
- message.Thread,
- message.Session,
+ _context.Device,
+ process,
+ process.CpuMemory,
+ thread,
request,
response,
reqReader,
resWriter);
- message.Session.Service.CallMethod(context);
+ _sessions[serverSessionHandle].CallMethod(context);
response.RawData = resMs.ToArray();
}
@@ -95,11 +225,11 @@ namespace Ryujinx.HLE.HOS.Services
switch (cmdId)
{
case 0:
- request = FillResponse(response, 0, message.Session.Service.ConvertToDomain());
+ request = FillResponse(response, 0, _sessions[serverSessionHandle].ConvertToDomain());
break;
case 3:
- request = FillResponse(response, 0, 0x1000);
+ request = FillResponse(response, 0, PointerBufferSize);
break;
// TODO: Whats the difference between IpcDuplicateSession/Ex?
@@ -107,12 +237,11 @@ namespace Ryujinx.HLE.HOS.Services
case 4:
int unknown = reqReader.ReadInt32();
- if (message.Process.HandleTable.GenerateHandle(message.Session, out int handle) != KernelResult.Success)
- {
- throw new InvalidOperationException("Out of handles!");
- }
+ _context.Syscall.CreateSession(false, 0, out int dupServerSessionHandle, out int dupClientSessionHandle);
- response.HandleDesc = IpcHandleDesc.MakeMove(handle);
+ AddSessionObj(dupServerSessionHandle, _sessions[serverSessionHandle]);
+
+ response.HandleDesc = IpcHandleDesc.MakeMove(dupClientSessionHandle);
request = FillResponse(response, 0);
@@ -123,18 +252,24 @@ namespace Ryujinx.HLE.HOS.Services
}
else if (request.Type == IpcMessageType.CloseSession)
{
- message.SignalDone(KernelResult.PortRemoteClosed);
- return;
+ _context.Syscall.CloseHandle(serverSessionHandle);
+ _sessionHandles.Remove(serverSessionHandle);
+ IpcService service = _sessions[serverSessionHandle];
+ if (service is IDisposable disposableObj)
+ {
+ disposableObj.Dispose();
+ }
+ _sessions.Remove(serverSessionHandle);
+ shouldReply = false;
}
else
{
throw new NotImplementedException(request.Type.ToString());
}
- message.Process.CpuMemory.Write(message.MessagePtr, response.GetBytes((long)message.MessagePtr));
+ process.CpuMemory.Write(messagePtr, response.GetBytes((long)messagePtr, recvListAddr | ((ulong)PointerBufferSize << 48)));
+ return shouldReply;
}
-
- message.SignalDone(KernelResult.Success);
}
private static IpcMessage FillResponse(IpcMessage response, long result, params int[] values)