ServerSessionManager.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. using Ryujinx.Common.Logging;
  2. using Ryujinx.Horizon.Common;
  3. using Ryujinx.Horizon.Sdk.OsTypes;
  4. using Ryujinx.Horizon.Sdk.Sf.Cmif;
  5. using Ryujinx.Horizon.Sdk.Sm;
  6. using System;
  7. using System.Runtime.InteropServices;
  8. namespace Ryujinx.Horizon.Sdk.Sf.Hipc
  9. {
  10. class ServerSessionManager
  11. {
  12. public Result AcceptSession(int portHandle, ServiceObjectHolder obj)
  13. {
  14. return AcceptSession(out _, portHandle, obj);
  15. }
  16. private Result AcceptSession(out ServerSession session, int portHandle, ServiceObjectHolder obj)
  17. {
  18. return AcceptSessionImpl(out session, portHandle, obj);
  19. }
  20. private Result AcceptSessionImpl(out ServerSession session, int portHandle, ServiceObjectHolder obj)
  21. {
  22. session = null;
  23. Result result = HorizonStatic.Syscall.AcceptSession(out int sessionHandle, portHandle);
  24. if (result.IsFailure)
  25. {
  26. return result;
  27. }
  28. bool succeeded = false;
  29. try
  30. {
  31. result = RegisterSessionImpl(out session, sessionHandle, obj);
  32. if (result.IsFailure)
  33. {
  34. return result;
  35. }
  36. succeeded = true;
  37. }
  38. finally
  39. {
  40. if (!succeeded)
  41. {
  42. HorizonStatic.Syscall.CloseHandle(sessionHandle);
  43. }
  44. }
  45. return Result.Success;
  46. }
  47. public Result RegisterSession(int sessionHandle, ServiceObjectHolder obj)
  48. {
  49. return RegisterSession(out _, sessionHandle, obj);
  50. }
  51. public Result RegisterSession(out ServerSession session, int sessionHandle, ServiceObjectHolder obj)
  52. {
  53. return RegisterSessionImpl(out session, sessionHandle, obj);
  54. }
  55. private Result RegisterSessionImpl(out ServerSession session, int sessionHandle, ServiceObjectHolder obj)
  56. {
  57. Result result = CreateSessionImpl(out session, sessionHandle, obj);
  58. if (result.IsFailure)
  59. {
  60. return result;
  61. }
  62. session.PointerBuffer = GetSessionPointerBuffer(session);
  63. session.SavedMessage = GetSessionSavedMessageBuffer(session);
  64. RegisterSessionToWaitList(session);
  65. return Result.Success;
  66. }
  67. protected virtual void RegisterSessionToWaitList(ServerSession session)
  68. {
  69. throw new NotSupportedException();
  70. }
  71. private Result CreateSessionImpl(out ServerSession session, int sessionHandle, ServiceObjectHolder obj)
  72. {
  73. session = AllocateSession(sessionHandle, obj);
  74. if (session == null)
  75. {
  76. return HipcResult.OutOfSessionMemory;
  77. }
  78. return Result.Success;
  79. }
  80. protected virtual ServerSession AllocateSession(int sessionHandle, ServiceObjectHolder obj)
  81. {
  82. throw new NotSupportedException();
  83. }
  84. protected virtual void FreeSession(ServerSession session)
  85. {
  86. throw new NotSupportedException();
  87. }
  88. protected virtual Server AllocateServer(
  89. int portIndex,
  90. int portHandle,
  91. ServiceName name,
  92. bool managed,
  93. ServiceObjectHolder staticHoder)
  94. {
  95. throw new NotSupportedException();
  96. }
  97. protected virtual void DestroyServer(Server server)
  98. {
  99. throw new NotSupportedException();
  100. }
  101. protected virtual PointerAndSize GetSessionPointerBuffer(ServerSession session)
  102. {
  103. throw new NotSupportedException();
  104. }
  105. protected virtual PointerAndSize GetSessionSavedMessageBuffer(ServerSession session)
  106. {
  107. throw new NotSupportedException();
  108. }
  109. private void DestroySession(ServerSession session)
  110. {
  111. FreeSession(session);
  112. }
  113. protected void CloseSessionImpl(ServerSession session)
  114. {
  115. int sessionHandle = session.Handle;
  116. Os.FinalizeMultiWaitHolder(session);
  117. DestroySession(session);
  118. HorizonStatic.Syscall.CloseHandle(sessionHandle).AbortOnFailure();
  119. }
  120. private static CommandType GetCmifCommandType(ReadOnlySpan<byte> message)
  121. {
  122. return MemoryMarshal.Cast<byte, Header>(message)[0].Type;
  123. }
  124. public Result ProcessRequest(ServerSession session, Span<byte> message)
  125. {
  126. if (session.IsClosed || GetCmifCommandType(message) == CommandType.Close)
  127. {
  128. CloseSessionImpl(session);
  129. return Result.Success;
  130. }
  131. else
  132. {
  133. Result result = ProcessRequestImpl(session, message, message);
  134. if (result.IsSuccess)
  135. {
  136. RegisterSessionToWaitList(session);
  137. return Result.Success;
  138. }
  139. else if (SfResult.RequestContextChanged(result))
  140. {
  141. return result;
  142. }
  143. else
  144. {
  145. Logger.Warning?.Print(LogClass.KernelIpc, $"Request processing returned error {result}");
  146. CloseSessionImpl(session);
  147. return Result.Success;
  148. }
  149. }
  150. }
  151. private Result ProcessRequestImpl(ServerSession session, Span<byte> inMessage, Span<byte> outMessage)
  152. {
  153. CommandType commandType = GetCmifCommandType(inMessage);
  154. using var _ = new ScopedInlineContextChange(GetInlineContext(commandType, inMessage));
  155. switch (commandType)
  156. {
  157. case CommandType.Request:
  158. case CommandType.RequestWithContext:
  159. return DispatchRequest(session.ServiceObjectHolder, session, inMessage, outMessage);
  160. case CommandType.Control:
  161. case CommandType.ControlWithContext:
  162. return DispatchManagerRequest(session, inMessage, outMessage);
  163. default:
  164. return HipcResult.UnknownCommandType;
  165. }
  166. }
  167. private static int GetInlineContext(CommandType commandType, ReadOnlySpan<byte> inMessage)
  168. {
  169. switch (commandType)
  170. {
  171. case CommandType.RequestWithContext:
  172. case CommandType.ControlWithContext:
  173. if (inMessage.Length >= 0x10)
  174. {
  175. return MemoryMarshal.Cast<byte, int>(inMessage)[3];
  176. }
  177. break;
  178. }
  179. return 0;
  180. }
  181. protected Result ReceiveRequest(ServerSession session, Span<byte> message)
  182. {
  183. return ReceiveRequestImpl(session, message);
  184. }
  185. private Result ReceiveRequestImpl(ServerSession session, Span<byte> message)
  186. {
  187. PointerAndSize pointerBuffer = session.PointerBuffer;
  188. while (true)
  189. {
  190. if (pointerBuffer.Address != 0)
  191. {
  192. HipcMessageData messageData = HipcMessage.WriteMessage(message, new HipcMetadata()
  193. {
  194. Type = (int)CommandType.Invalid,
  195. ReceiveStaticsCount = HipcMessage.AutoReceiveStatic
  196. });
  197. messageData.ReceiveList[0] = new HipcReceiveListEntry(pointerBuffer.Address, pointerBuffer.Size);
  198. }
  199. else
  200. {
  201. MemoryMarshal.Cast<byte, Header>(message)[0] = new Header()
  202. {
  203. Type = CommandType.Invalid
  204. };
  205. }
  206. Result result = Api.Receive(out ReceiveResult recvResult, session.Handle, message);
  207. if (result.IsFailure)
  208. {
  209. return result;
  210. }
  211. switch (recvResult)
  212. {
  213. case ReceiveResult.Success:
  214. session.IsClosed = false;
  215. return Result.Success;
  216. case ReceiveResult.Closed:
  217. session.IsClosed = true;
  218. return Result.Success;
  219. }
  220. }
  221. }
  222. protected virtual Result DispatchManagerRequest(ServerSession session, Span<byte> inMessage, Span<byte> outMessage)
  223. {
  224. return SfResult.NotSupported;
  225. }
  226. protected virtual Result DispatchRequest(
  227. ServiceObjectHolder objectHolder,
  228. ServerSession session,
  229. Span<byte> inMessage,
  230. Span<byte> outMessage)
  231. {
  232. HipcMessage request;
  233. try
  234. {
  235. request = new HipcMessage(inMessage);
  236. }
  237. catch (ArgumentOutOfRangeException)
  238. {
  239. return HipcResult.InvalidRequestSize;
  240. }
  241. var dispatchCtx = new ServiceDispatchContext()
  242. {
  243. ServiceObject = objectHolder.ServiceObject,
  244. Manager = this,
  245. Session = session,
  246. HandlesToClose = new HandlesToClose(),
  247. PointerBuffer = session.PointerBuffer,
  248. InMessageBuffer = inMessage,
  249. OutMessageBuffer = outMessage,
  250. Request = request
  251. };
  252. ReadOnlySpan<byte> inRawData = MemoryMarshal.Cast<uint, byte>(dispatchCtx.Request.Data.DataWords);
  253. int inRawSize = dispatchCtx.Request.Meta.DataWordsCount * sizeof(uint);
  254. if (inRawSize < 0x10)
  255. {
  256. return HipcResult.InvalidRequestSize;
  257. }
  258. Result result = objectHolder.ProcessMessage(ref dispatchCtx, inRawData);
  259. if (result.IsFailure)
  260. {
  261. return result;
  262. }
  263. result = Api.Reply(session.SessionHandle, outMessage);
  264. ref var handlesToClose = ref dispatchCtx.HandlesToClose;
  265. for (int i = 0; i < handlesToClose.Count; i++)
  266. {
  267. HorizonStatic.Syscall.CloseHandle(handlesToClose[i]).AbortOnFailure();
  268. }
  269. return result;
  270. }
  271. public ServerSessionManager GetSessionManagerByTag(uint tag)
  272. {
  273. // Official FW does not do anything with the tag currently.
  274. return this;
  275. }
  276. }
  277. }