HipcCommandProcessor.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. using Ryujinx.Common;
  2. using Ryujinx.Horizon.Common;
  3. using Ryujinx.Horizon.Sdk.Sf.Cmif;
  4. using Ryujinx.Horizon.Sdk.Sf.Hipc;
  5. using System;
  6. using System.Linq;
  7. using System.Runtime.CompilerServices;
  8. using System.Runtime.InteropServices;
  9. namespace Ryujinx.Horizon.Sdk.Sf
  10. {
  11. class HipcCommandProcessor : ServerMessageProcessor
  12. {
  13. private readonly CommandArg[] _args;
  14. private readonly int[] _inOffsets;
  15. private readonly int[] _outOffsets;
  16. private readonly PointerAndSize[] _bufferRanges;
  17. private readonly bool _hasInProcessIdHolder;
  18. private readonly int _inObjectsCount;
  19. private readonly int _outObjectsCount;
  20. private readonly int _inMapAliasBuffersCount;
  21. private readonly int _outMapAliasBuffersCount;
  22. private readonly int _inPointerBuffersCount;
  23. private readonly int _outPointerBuffersCount;
  24. private readonly int _outFixedSizePointerBuffersCount;
  25. private readonly int _inMoveHandlesCount;
  26. private readonly int _inCopyHandlesCount;
  27. private readonly int _outMoveHandlesCount;
  28. private readonly int _outCopyHandlesCount;
  29. public int FunctionArgumentsCount => _args.Length;
  30. public int InRawDataSize => BitUtils.AlignUp(_inOffsets[^1], sizeof(ushort));
  31. public int OutRawDataSize => BitUtils.AlignUp(_outOffsets[^1], sizeof(uint));
  32. private int OutUnfixedSizePointerBuffersCount => _outPointerBuffersCount - _outFixedSizePointerBuffersCount;
  33. public HipcCommandProcessor(CommandArg[] args)
  34. {
  35. _args = args;
  36. foreach (CommandArg argInfo in args)
  37. {
  38. switch (argInfo.Type)
  39. {
  40. case CommandArgType.Buffer:
  41. HipcBufferFlags flags = argInfo.BufferFlags;
  42. if (flags.HasFlag(HipcBufferFlags.In))
  43. {
  44. if (flags.HasFlag(HipcBufferFlags.AutoSelect))
  45. {
  46. _inMapAliasBuffersCount++;
  47. _inPointerBuffersCount++;
  48. }
  49. else if (flags.HasFlag(HipcBufferFlags.MapAlias))
  50. {
  51. _inMapAliasBuffersCount++;
  52. }
  53. else if (flags.HasFlag(HipcBufferFlags.Pointer))
  54. {
  55. _inPointerBuffersCount++;
  56. }
  57. }
  58. else
  59. {
  60. bool autoSelect = flags.HasFlag(HipcBufferFlags.AutoSelect);
  61. if (autoSelect || flags.HasFlag(HipcBufferFlags.Pointer))
  62. {
  63. _outPointerBuffersCount++;
  64. if (flags.HasFlag(HipcBufferFlags.FixedSize))
  65. {
  66. _outFixedSizePointerBuffersCount++;
  67. }
  68. }
  69. if (autoSelect || flags.HasFlag(HipcBufferFlags.MapAlias))
  70. {
  71. _outMapAliasBuffersCount++;
  72. }
  73. }
  74. break;
  75. case CommandArgType.InCopyHandle:
  76. _inCopyHandlesCount++;
  77. break;
  78. case CommandArgType.InMoveHandle:
  79. _inMoveHandlesCount++;
  80. break;
  81. case CommandArgType.InObject:
  82. _inObjectsCount++;
  83. break;
  84. case CommandArgType.ProcessId:
  85. _hasInProcessIdHolder = true;
  86. break;
  87. case CommandArgType.OutCopyHandle:
  88. _outCopyHandlesCount++;
  89. break;
  90. case CommandArgType.OutMoveHandle:
  91. _outMoveHandlesCount++;
  92. break;
  93. case CommandArgType.OutObject:
  94. _outObjectsCount++;
  95. break;
  96. }
  97. }
  98. _inOffsets = RawDataOffsetCalculator.Calculate(args.Where(x => x.Type == CommandArgType.InArgument).ToArray());
  99. _outOffsets = RawDataOffsetCalculator.Calculate(args.Where(x => x.Type == CommandArgType.OutArgument).ToArray());
  100. _bufferRanges = new PointerAndSize[args.Length];
  101. }
  102. public int GetInArgOffset(int argIndex)
  103. {
  104. return _inOffsets[argIndex];
  105. }
  106. public int GetOutArgOffset(int argIndex)
  107. {
  108. return _outOffsets[argIndex];
  109. }
  110. public PointerAndSize GetBufferRange(int argIndex)
  111. {
  112. return _bufferRanges[argIndex];
  113. }
  114. public Result ProcessBuffers(ref ServiceDispatchContext context, scoped Span<bool> isBufferMapAlias, ServerMessageRuntimeMetadata runtimeMetadata)
  115. {
  116. bool mapAliasBuffersValid = true;
  117. ulong pointerBufferTail = context.PointerBuffer.Address;
  118. ulong pointerBufferHead = pointerBufferTail + context.PointerBuffer.Size;
  119. int sendMapAliasIndex = 0;
  120. int recvMapAliasIndex = 0;
  121. int sendPointerIndex = 0;
  122. int unfixedRecvPointerIndex = 0;
  123. for (int i = 0; i < _args.Length; i++)
  124. {
  125. if (_args[i].Type != CommandArgType.Buffer)
  126. {
  127. continue;
  128. }
  129. HipcBufferFlags flags = _args[i].BufferFlags;
  130. bool isMapAlias;
  131. if (flags.HasFlag(HipcBufferFlags.MapAlias))
  132. {
  133. isMapAlias = true;
  134. }
  135. else if (flags.HasFlag(HipcBufferFlags.Pointer))
  136. {
  137. isMapAlias = false;
  138. }
  139. else /* if (flags.HasFlag(HipcBufferFlags.HipcAutoSelect)) */
  140. {
  141. HipcBufferDescriptor descriptor = flags.HasFlag(HipcBufferFlags.In)
  142. ? context.Request.Data.SendBuffers[sendMapAliasIndex]
  143. : context.Request.Data.ReceiveBuffers[recvMapAliasIndex];
  144. isMapAlias = descriptor.Address != 0UL;
  145. }
  146. isBufferMapAlias[i] = isMapAlias;
  147. if (isMapAlias)
  148. {
  149. HipcBufferDescriptor descriptor = flags.HasFlag(HipcBufferFlags.In)
  150. ? context.Request.Data.SendBuffers[sendMapAliasIndex++]
  151. : context.Request.Data.ReceiveBuffers[recvMapAliasIndex++];
  152. _bufferRanges[i] = new PointerAndSize(descriptor.Address, descriptor.Size);
  153. if (!IsMapTransferModeValid(flags, descriptor.Mode))
  154. {
  155. mapAliasBuffersValid = false;
  156. }
  157. }
  158. else
  159. {
  160. if (flags.HasFlag(HipcBufferFlags.In))
  161. {
  162. HipcStaticDescriptor descriptor = context.Request.Data.SendStatics[sendPointerIndex++];
  163. ulong address = descriptor.Address;
  164. ulong size = descriptor.Size;
  165. _bufferRanges[i] = new PointerAndSize(address, size);
  166. if (size != 0)
  167. {
  168. pointerBufferTail = Math.Max(pointerBufferTail, address + size);
  169. }
  170. }
  171. else /* if (flags.HasFlag(HipcBufferFlags.Out)) */
  172. {
  173. ulong size;
  174. if (flags.HasFlag(HipcBufferFlags.FixedSize))
  175. {
  176. size = _args[i].BufferFixedSize;
  177. }
  178. else
  179. {
  180. Span<byte> data = MemoryMarshal.Cast<uint, byte>(context.Request.Data.DataWordsPadded);
  181. Span<ushort> recvPointerSizes = MemoryMarshal.Cast<byte, ushort>(data[runtimeMetadata.UnfixedOutPointerSizeOffset..]);
  182. size = recvPointerSizes[unfixedRecvPointerIndex++];
  183. }
  184. pointerBufferHead = BitUtils.AlignDown(pointerBufferHead - size, 0x10UL);
  185. _bufferRanges[i] = new PointerAndSize(pointerBufferHead, size);
  186. }
  187. }
  188. }
  189. if (!mapAliasBuffersValid)
  190. {
  191. return HipcResult.InvalidCmifRequest;
  192. }
  193. if (_outPointerBuffersCount != 0 && pointerBufferTail > pointerBufferHead)
  194. {
  195. return HipcResult.PointerBufferTooSmall;
  196. }
  197. return Result.Success;
  198. }
  199. private static bool IsMapTransferModeValid(HipcBufferFlags flags, HipcBufferMode mode)
  200. {
  201. if (flags.HasFlag(HipcBufferFlags.MapTransferAllowsNonSecure))
  202. {
  203. return mode == HipcBufferMode.NonSecure;
  204. }
  205. if (flags.HasFlag(HipcBufferFlags.MapTransferAllowsNonDevice))
  206. {
  207. return mode == HipcBufferMode.NonDevice;
  208. }
  209. return mode == HipcBufferMode.Normal;
  210. }
  211. public void SetOutBuffers(HipcMessageData response, ReadOnlySpan<bool> isBufferMapAlias)
  212. {
  213. int recvPointerIndex = 0;
  214. for (int i = 0; i < _args.Length; i++)
  215. {
  216. if (_args[i].Type != CommandArgType.Buffer)
  217. {
  218. continue;
  219. }
  220. HipcBufferFlags flags = _args[i].BufferFlags;
  221. if (!flags.HasFlag(HipcBufferFlags.Out))
  222. {
  223. continue;
  224. }
  225. PointerAndSize buffer = _bufferRanges[i];
  226. if (flags.HasFlag(HipcBufferFlags.Pointer))
  227. {
  228. response.SendStatics[recvPointerIndex] = new HipcStaticDescriptor(buffer.Address, (ushort)buffer.Size, recvPointerIndex);
  229. }
  230. else if (flags.HasFlag(HipcBufferFlags.AutoSelect))
  231. {
  232. if (!isBufferMapAlias[i])
  233. {
  234. response.SendStatics[recvPointerIndex] = new HipcStaticDescriptor(buffer.Address, (ushort)buffer.Size, recvPointerIndex);
  235. }
  236. else
  237. {
  238. response.SendStatics[recvPointerIndex] = new HipcStaticDescriptor(0UL, 0, recvPointerIndex);
  239. }
  240. }
  241. recvPointerIndex++;
  242. }
  243. }
  244. public override void SetImplementationProcessor(ServerMessageProcessor impl)
  245. {
  246. // We don't need to do anything here as this should be always the last processor to be called.
  247. }
  248. public override ServerMessageRuntimeMetadata GetRuntimeMetadata()
  249. {
  250. return new ServerMessageRuntimeMetadata(
  251. (ushort)InRawDataSize,
  252. (ushort)OutRawDataSize,
  253. (byte)Unsafe.SizeOf<CmifInHeader>(),
  254. (byte)Unsafe.SizeOf<CmifOutHeader>(),
  255. (byte)_inObjectsCount,
  256. (byte)_outObjectsCount);
  257. }
  258. public override Result PrepareForProcess(ref ServiceDispatchContext context, ServerMessageRuntimeMetadata runtimeMetadata)
  259. {
  260. ref HipcMetadata meta = ref context.Request.Meta;
  261. bool requestValid = true;
  262. requestValid &= meta.SendPid == _hasInProcessIdHolder;
  263. requestValid &= meta.SendStaticsCount == _inPointerBuffersCount;
  264. requestValid &= meta.SendBuffersCount == _inMapAliasBuffersCount;
  265. requestValid &= meta.ReceiveBuffersCount == _outMapAliasBuffersCount;
  266. requestValid &= meta.ExchangeBuffersCount == 0;
  267. requestValid &= meta.CopyHandlesCount == _inCopyHandlesCount;
  268. requestValid &= meta.MoveHandlesCount == _inMoveHandlesCount;
  269. int rawSizeInBytes = meta.DataWordsCount * sizeof(uint);
  270. int commandRawSize = BitUtils.AlignUp(runtimeMetadata.UnfixedOutPointerSizeOffset + (OutUnfixedSizePointerBuffersCount * sizeof(ushort)), sizeof(uint));
  271. requestValid &= rawSizeInBytes >= commandRawSize;
  272. return requestValid ? Result.Success : HipcResult.InvalidCmifRequest;
  273. }
  274. public Result GetInObjects(ServerMessageProcessor processor, Span<IServiceObject> objects)
  275. {
  276. if (objects.Length == 0)
  277. {
  278. return Result.Success;
  279. }
  280. ServiceObjectHolder[] inObjects = new ServiceObjectHolder[objects.Length];
  281. Result result = processor.GetInObjects(inObjects);
  282. if (result.IsFailure)
  283. {
  284. return result;
  285. }
  286. int inObjectIndex = 0;
  287. foreach (CommandArg t in _args)
  288. {
  289. if (t.Type != CommandArgType.InObject)
  290. {
  291. continue;
  292. }
  293. int index = inObjectIndex++;
  294. ServiceObjectHolder inObject = inObjects[index];
  295. objects[index] = inObject?.ServiceObject;
  296. }
  297. return Result.Success;
  298. }
  299. public override Result GetInObjects(Span<ServiceObjectHolder> inObjects)
  300. {
  301. return SfResult.NotSupported;
  302. }
  303. public override HipcMessageData PrepareForReply(scoped ref ServiceDispatchContext context, out Span<byte> outRawData, ServerMessageRuntimeMetadata runtimeMetadata)
  304. {
  305. int rawDataSize = OutRawDataSize + runtimeMetadata.OutHeadersSize;
  306. HipcMessageData response = HipcMessage.WriteResponse(
  307. context.OutMessageBuffer,
  308. _outPointerBuffersCount,
  309. (BitUtils.AlignUp(rawDataSize, 4) + 0x10) / sizeof(uint),
  310. _outCopyHandlesCount,
  311. _outMoveHandlesCount + runtimeMetadata.OutObjectsCount);
  312. outRawData = MemoryMarshal.Cast<uint, byte>(response.DataWords);
  313. return response;
  314. }
  315. public override void PrepareForErrorReply(scoped ref ServiceDispatchContext context, out Span<byte> outRawData, ServerMessageRuntimeMetadata runtimeMetadata)
  316. {
  317. int rawDataSize = runtimeMetadata.OutHeadersSize;
  318. HipcMessageData response = HipcMessage.WriteResponse(
  319. context.OutMessageBuffer,
  320. 0,
  321. (BitUtils.AlignUp(rawDataSize, 4) + 0x10) / sizeof(uint),
  322. 0,
  323. 0);
  324. outRawData = MemoryMarshal.Cast<uint, byte>(response.DataWords);
  325. }
  326. #pragma warning disable CA1822 // Mark member as static
  327. public void SetOutObjects(ref ServiceDispatchContext context, HipcMessageData response, Span<IServiceObject> objects)
  328. #pragma warning restore CA1822
  329. {
  330. if (objects.Length == 0)
  331. {
  332. return;
  333. }
  334. ServiceObjectHolder[] outObjects = new ServiceObjectHolder[objects.Length];
  335. for (int i = 0; i < objects.Length; i++)
  336. {
  337. outObjects[i] = objects[i] != null ? new ServiceObjectHolder(objects[i]) : null;
  338. }
  339. context.Processor.SetOutObjects(ref context, response, outObjects);
  340. }
  341. public override void SetOutObjects(scoped ref ServiceDispatchContext context, HipcMessageData response, Span<ServiceObjectHolder> outObjects)
  342. {
  343. for (int index = 0; index < _outObjectsCount; index++)
  344. {
  345. SetOutObjectImpl(index, response, context.Manager, outObjects[index]);
  346. }
  347. }
  348. private static void SetOutObjectImpl(int index, HipcMessageData response, ServerSessionManager manager, ServiceObjectHolder obj)
  349. {
  350. if (obj == null)
  351. {
  352. response.MoveHandles[index] = 0;
  353. return;
  354. }
  355. Api.CreateSession(out int serverHandle, out int clientHandle).AbortOnFailure();
  356. manager.RegisterSession(serverHandle, obj).AbortOnFailure();
  357. response.MoveHandles[index] = clientHandle;
  358. }
  359. }
  360. }