diff options
author | gdkchan <gab.dark.100@gmail.com> | 2020-12-01 20:23:43 -0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-12-02 00:23:43 +0100 |
commit | cf6cd714884c41e9550757e364c2f4f5b04fc7f3 (patch) | |
tree | bea748b4d1a350e5b8075d63ec9d39d49693829d /Ryujinx.HLE/HOS/Services/ServerBase.cs | |
parent | 461c24092ae6e148d896c18aa3e86220c89981f8 (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.cs | 233 |
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) |