| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380 |
- 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 Ryujinx.HLE.HOS.Services.Sm;
- using System;
- using System.Buffers.Binary;
- using System.Collections.Generic;
- using System.IO;
- using System.Threading;
- namespace Ryujinx.HLE.HOS.Services
- {
- class ServerBase : IDisposable
- {
- // 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[]
- {
- 0x030363F7,
- 0x1FFFFFCF,
- 0x207FFFEF,
- 0x47E0060F,
- 0x0048BFFF,
- 0x01007FFF
- };
- private readonly KernelContext _context;
- private 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, Func<IpcService>> _ports = new Dictionary<int, Func<IpcService>>();
- public ManualResetEvent InitDone { get; }
- public Func<IpcService> SmObjectFactory { get; }
- public string Name { get; }
- public ServerBase(KernelContext context, string name, Func<IpcService> smObjectFactory = null)
- {
- InitDone = new ManualResetEvent(false);
- Name = name;
- SmObjectFactory = smObjectFactory;
- _context = context;
- const ProcessCreationFlags flags =
- ProcessCreationFlags.EnableAslr |
- ProcessCreationFlags.AddressSpace64Bit |
- ProcessCreationFlags.Is64Bit |
- ProcessCreationFlags.PoolPartitionSystem;
- ProcessCreationInfo creationInfo = new ProcessCreationInfo("Service", 1, 0, 0x8000000, 1, flags, 0, 0);
- KernelStatic.StartInitialProcess(context, creationInfo, DefaultCapabilities, 44, ServerLoop);
- }
- private void AddPort(int serverPortHandle, Func<IpcService> objectFactory)
- {
- _portHandles.Add(serverPortHandle);
- _ports.Add(serverPortHandle, objectFactory);
- }
- public void AddSessionObj(KServerSession serverSession, IpcService obj)
- {
- // Ensure that the sever loop is running.
- InitDone.WaitOne();
- _selfProcess.HandleTable.GenerateHandle(serverSession, out int serverSessionHandle);
- AddSessionObj(serverSessionHandle, obj);
- }
- public void AddSessionObj(int serverSessionHandle, IpcService obj)
- {
- _sessionHandles.Add(serverSessionHandle);
- _sessions.Add(serverSessionHandle, obj);
- }
- private void ServerLoop()
- {
- _selfProcess = KernelStatic.GetCurrentProcess();
- if (SmObjectFactory != null)
- {
- _context.Syscall.ManageNamedPort("sm:", 50, out int serverPortHandle);
- AddPort(serverPortHandle, SmObjectFactory);
- }
- InitDone.Set();
- KThread thread = KernelStatic.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[] portHandles = _portHandles.ToArray();
- int[] sessionHandles = _sessionHandles.ToArray();
- int[] handles = new int[portHandles.Length + sessionHandles.Length];
- portHandles.CopyTo(handles, 0);
- sessionHandles.CopyTo(handles, portHandles.Length);
- // We still need a timeout here to allow the service to pick up and listen new sessions...
- var rc = _context.Syscall.ReplyAndReceive(handles, replyTargetHandle, 1000000L, out int signaledIndex);
- thread.HandlePostSyscall();
- if (!thread.Context.Running)
- {
- break;
- }
- replyTargetHandle = 0;
- if (rc == KernelResult.Success && signaledIndex >= portHandles.Length)
- {
- // We got a IPC request, process it, pass to the appropriate service if needed.
- int signaledHandle = handles[signaledIndex];
- if (Process(signaledHandle, heapAddr))
- {
- replyTargetHandle = signaledHandle;
- }
- }
- else
- {
- if (rc == KernelResult.Success)
- {
- // We got a new connection, accept the session to allow servicing future requests.
- if (_context.Syscall.AcceptSession(handles[signaledIndex], out int serverSessionHandle) == KernelResult.Success)
- {
- IpcService obj = _ports[handles[signaledIndex]].Invoke();
- AddSessionObj(serverSessionHandle, obj);
- }
- }
- _selfProcess.CpuMemory.Write(messagePtr + 0x0, 0);
- _selfProcess.CpuMemory.Write(messagePtr + 0x4, 2 << 10);
- _selfProcess.CpuMemory.Write(messagePtr + 0x8, heapAddr | ((ulong)PointerBufferSize << 48));
- }
- }
- Dispose();
- }
- private bool Process(int serverSessionHandle, ulong recvListAddr)
- {
- KProcess process = KernelStatic.GetCurrentProcess();
- KThread thread = KernelStatic.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++)
- {
- ulong size = (ulong)BinaryPrimitives.ReadInt16LittleEndian(request.RawData.AsSpan().Slice(sizesOffset + i * 2, 2));
- response.PtrBuff.Add(new IpcPtrBuffDesc(tempAddr, (uint)i, size));
- request.RecvListBuff[i] = new IpcRecvListBuffDesc(tempAddr, size);
- tempAddr += size;
- }
- }
- bool shouldReply = true;
- bool isTipcCommunication = false;
- using (MemoryStream raw = new MemoryStream(request.RawData))
- {
- BinaryReader reqReader = new BinaryReader(raw);
- if (request.Type == IpcMessageType.HipcRequest ||
- request.Type == IpcMessageType.HipcRequestWithContext)
- {
- response.Type = IpcMessageType.HipcResponse;
- using (MemoryStream resMs = new MemoryStream())
- {
- BinaryWriter resWriter = new BinaryWriter(resMs);
- ServiceCtx context = new ServiceCtx(
- _context.Device,
- process,
- process.CpuMemory,
- thread,
- request,
- response,
- reqReader,
- resWriter);
- _sessions[serverSessionHandle].CallHipcMethod(context);
- response.RawData = resMs.ToArray();
- }
- }
- else if (request.Type == IpcMessageType.HipcControl ||
- request.Type == IpcMessageType.HipcControlWithContext)
- {
- uint magic = (uint)reqReader.ReadUInt64();
- uint cmdId = (uint)reqReader.ReadUInt64();
- switch (cmdId)
- {
- case 0:
- request = FillResponse(response, 0, _sessions[serverSessionHandle].ConvertToDomain());
- break;
- case 3:
- request = FillResponse(response, 0, PointerBufferSize);
- break;
- // TODO: Whats the difference between IpcDuplicateSession/Ex?
- case 2:
- case 4:
- int unknown = reqReader.ReadInt32();
- _context.Syscall.CreateSession(false, 0, out int dupServerSessionHandle, out int dupClientSessionHandle);
- AddSessionObj(dupServerSessionHandle, _sessions[serverSessionHandle]);
- response.HandleDesc = IpcHandleDesc.MakeMove(dupClientSessionHandle);
- request = FillResponse(response, 0);
- break;
- default: throw new NotImplementedException(cmdId.ToString());
- }
- }
- else if (request.Type == IpcMessageType.HipcCloseSession || request.Type == IpcMessageType.TipcCloseSession)
- {
- _context.Syscall.CloseHandle(serverSessionHandle);
- _sessionHandles.Remove(serverSessionHandle);
- IpcService service = _sessions[serverSessionHandle];
- if (service is IDisposable disposableObj)
- {
- disposableObj.Dispose();
- }
- _sessions.Remove(serverSessionHandle);
- shouldReply = false;
- }
- // If the type is past 0xF, we are using TIPC
- else if (request.Type > IpcMessageType.TipcCloseSession)
- {
- isTipcCommunication = true;
- // Response type is always the same as request on TIPC.
- response.Type = request.Type;
- using (MemoryStream resMs = new MemoryStream())
- {
- BinaryWriter resWriter = new BinaryWriter(resMs);
- ServiceCtx context = new ServiceCtx(
- _context.Device,
- process,
- process.CpuMemory,
- thread,
- request,
- response,
- reqReader,
- resWriter);
- _sessions[serverSessionHandle].CallTipcMethod(context);
- response.RawData = resMs.ToArray();
- }
- process.CpuMemory.Write(messagePtr, response.GetBytesTipc());
- }
- else
- {
- throw new NotImplementedException(request.Type.ToString());
- }
- if (!isTipcCommunication)
- {
- process.CpuMemory.Write(messagePtr, response.GetBytes((long)messagePtr, recvListAddr | ((ulong)PointerBufferSize << 48)));
- }
- return shouldReply;
- }
- }
- private static IpcMessage FillResponse(IpcMessage response, long result, params int[] values)
- {
- using (MemoryStream ms = new MemoryStream())
- {
- BinaryWriter writer = new BinaryWriter(ms);
- foreach (int value in values)
- {
- writer.Write(value);
- }
- return FillResponse(response, result, ms.ToArray());
- }
- }
- private static IpcMessage FillResponse(IpcMessage response, long result, byte[] data = null)
- {
- response.Type = IpcMessageType.HipcResponse;
- using (MemoryStream ms = new MemoryStream())
- {
- BinaryWriter writer = new BinaryWriter(ms);
- writer.Write(IpcMagic.Sfco);
- writer.Write(result);
- if (data != null)
- {
- writer.Write(data);
- }
- response.RawData = ms.ToArray();
- }
- return response;
- }
- protected virtual void Dispose(bool disposing)
- {
- if (disposing)
- {
- foreach (IpcService service in _sessions.Values)
- {
- if (service is IDisposable disposableObj)
- {
- disposableObj.Dispose();
- }
- service.DestroyAtExit();
- }
- _sessions.Clear();
- InitDone.Dispose();
- }
- }
- public void Dispose()
- {
- Dispose(true);
- }
- }
- }
|