ServerBase.cs 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. using Ryujinx.Common;
  2. using Ryujinx.HLE.HOS.Ipc;
  3. using Ryujinx.HLE.HOS.Kernel.Common;
  4. using Ryujinx.HLE.HOS.Kernel.Ipc;
  5. using Ryujinx.HLE.HOS.Kernel.Process;
  6. using Ryujinx.HLE.HOS.Kernel.Threading;
  7. using System;
  8. using System.IO;
  9. namespace Ryujinx.HLE.HOS.Services
  10. {
  11. class ServerBase
  12. {
  13. private struct IpcRequest
  14. {
  15. public Switch Device { get; }
  16. public KProcess Process => Thread?.Owner;
  17. public KThread Thread { get; }
  18. public KClientSession Session { get; }
  19. public ulong MessagePtr { get; }
  20. public ulong MessageSize { get; }
  21. public IpcRequest(Switch device, KThread thread, KClientSession session, ulong messagePtr, ulong messageSize)
  22. {
  23. Device = device;
  24. Thread = thread;
  25. Session = session;
  26. MessagePtr = messagePtr;
  27. MessageSize = messageSize;
  28. }
  29. public void SignalDone(KernelResult result)
  30. {
  31. Thread.ObjSyncResult = result;
  32. Thread.Reschedule(ThreadSchedState.Running);
  33. }
  34. }
  35. private readonly AsyncWorkQueue<IpcRequest> _ipcProcessor;
  36. public ServerBase(string name)
  37. {
  38. _ipcProcessor = new AsyncWorkQueue<IpcRequest>(Process, name);
  39. }
  40. public void PushMessage(Switch device, KThread thread, KClientSession session, ulong messagePtr, ulong messageSize)
  41. {
  42. _ipcProcessor.Add(new IpcRequest(device, thread, session, messagePtr, messageSize));
  43. }
  44. private void Process(IpcRequest message)
  45. {
  46. byte[] reqData = new byte[message.MessageSize];
  47. message.Process.CpuMemory.Read(message.MessagePtr, reqData);
  48. IpcMessage request = new IpcMessage(reqData, (long)message.MessagePtr);
  49. IpcMessage response = new IpcMessage();
  50. using (MemoryStream raw = new MemoryStream(request.RawData))
  51. {
  52. BinaryReader reqReader = new BinaryReader(raw);
  53. if (request.Type == IpcMessageType.Request ||
  54. request.Type == IpcMessageType.RequestWithContext)
  55. {
  56. response.Type = IpcMessageType.Response;
  57. using (MemoryStream resMs = new MemoryStream())
  58. {
  59. BinaryWriter resWriter = new BinaryWriter(resMs);
  60. ServiceCtx context = new ServiceCtx(
  61. message.Device,
  62. message.Process,
  63. message.Process.CpuMemory,
  64. message.Thread,
  65. message.Session,
  66. request,
  67. response,
  68. reqReader,
  69. resWriter);
  70. message.Session.Service.CallMethod(context);
  71. response.RawData = resMs.ToArray();
  72. }
  73. }
  74. else if (request.Type == IpcMessageType.Control ||
  75. request.Type == IpcMessageType.ControlWithContext)
  76. {
  77. uint magic = (uint)reqReader.ReadUInt64();
  78. uint cmdId = (uint)reqReader.ReadUInt64();
  79. switch (cmdId)
  80. {
  81. case 0:
  82. request = FillResponse(response, 0, message.Session.Service.ConvertToDomain());
  83. break;
  84. case 3:
  85. request = FillResponse(response, 0, 0x1000);
  86. break;
  87. // TODO: Whats the difference between IpcDuplicateSession/Ex?
  88. case 2:
  89. case 4:
  90. int unknown = reqReader.ReadInt32();
  91. if (message.Process.HandleTable.GenerateHandle(message.Session, out int handle) != KernelResult.Success)
  92. {
  93. throw new InvalidOperationException("Out of handles!");
  94. }
  95. response.HandleDesc = IpcHandleDesc.MakeMove(handle);
  96. request = FillResponse(response, 0);
  97. break;
  98. default: throw new NotImplementedException(cmdId.ToString());
  99. }
  100. }
  101. else if (request.Type == IpcMessageType.CloseSession)
  102. {
  103. message.SignalDone(KernelResult.PortRemoteClosed);
  104. return;
  105. }
  106. else
  107. {
  108. throw new NotImplementedException(request.Type.ToString());
  109. }
  110. message.Process.CpuMemory.Write(message.MessagePtr, response.GetBytes((long)message.MessagePtr));
  111. }
  112. message.SignalDone(KernelResult.Success);
  113. }
  114. private static IpcMessage FillResponse(IpcMessage response, long result, params int[] values)
  115. {
  116. using (MemoryStream ms = new MemoryStream())
  117. {
  118. BinaryWriter writer = new BinaryWriter(ms);
  119. foreach (int value in values)
  120. {
  121. writer.Write(value);
  122. }
  123. return FillResponse(response, result, ms.ToArray());
  124. }
  125. }
  126. private static IpcMessage FillResponse(IpcMessage response, long result, byte[] data = null)
  127. {
  128. response.Type = IpcMessageType.Response;
  129. using (MemoryStream ms = new MemoryStream())
  130. {
  131. BinaryWriter writer = new BinaryWriter(ms);
  132. writer.Write(IpcMagic.Sfco);
  133. writer.Write(result);
  134. if (data != null)
  135. {
  136. writer.Write(data);
  137. }
  138. response.RawData = ms.ToArray();
  139. }
  140. return response;
  141. }
  142. }
  143. }