cmif_serialization.h 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  1. // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #pragma once
  4. #include "common/div_ceil.h"
  5. #include "core/hle/service/cmif_types.h"
  6. #include "core/hle/service/ipc_helpers.h"
  7. #include "core/hle/service/service.h"
  8. namespace Service {
  9. // clang-format off
  10. template <typename T>
  11. struct UnwrapArg {
  12. using Type = std::remove_cvref_t<T>;
  13. };
  14. template <typename T, int A>
  15. struct UnwrapArg<InLargeData<T, A>> {
  16. using Type = std::remove_cv_t<typename InLargeData<T, A>::Type>;
  17. };
  18. template <typename T>
  19. struct UnwrapArg<Out<T>> {
  20. using Type = AutoOut<typename Out<T>::Type>;
  21. };
  22. template <typename T>
  23. struct UnwrapArg<OutCopyHandle<T>> {
  24. using Type = AutoOut<typename OutCopyHandle<T>::Type>;
  25. };
  26. template <typename T>
  27. struct UnwrapArg<OutMoveHandle<T>> {
  28. using Type = AutoOut<typename OutMoveHandle<T>::Type>;
  29. };
  30. template <typename T, int A>
  31. struct UnwrapArg<OutLargeData<T, A>> {
  32. using Type = AutoOut<typename OutLargeData<T, A>::Type>;
  33. };
  34. enum class ArgumentType {
  35. InProcessId,
  36. InData,
  37. InInterface,
  38. InCopyHandle,
  39. OutData,
  40. OutInterface,
  41. OutCopyHandle,
  42. OutMoveHandle,
  43. InBuffer,
  44. InLargeData,
  45. OutBuffer,
  46. OutLargeData,
  47. };
  48. template <typename T>
  49. struct ArgumentTraits;
  50. template <>
  51. struct ArgumentTraits<ClientProcessId> {
  52. static constexpr ArgumentType Type = ArgumentType::InProcessId;
  53. };
  54. template <typename T>
  55. struct ArgumentTraits<SharedPointer<T>> {
  56. static constexpr ArgumentType Type = ArgumentType::InInterface;
  57. };
  58. template <typename T>
  59. struct ArgumentTraits<InCopyHandle<T>> {
  60. static constexpr ArgumentType Type = ArgumentType::InCopyHandle;
  61. };
  62. template <typename T>
  63. struct ArgumentTraits<Out<SharedPointer<T>>> {
  64. static constexpr ArgumentType Type = ArgumentType::OutInterface;
  65. };
  66. template <typename T>
  67. struct ArgumentTraits<Out<T>> {
  68. static constexpr ArgumentType Type = ArgumentType::OutData;
  69. };
  70. template <typename T>
  71. struct ArgumentTraits<OutCopyHandle<T>> {
  72. static constexpr ArgumentType Type = ArgumentType::OutCopyHandle;
  73. };
  74. template <typename T>
  75. struct ArgumentTraits<OutMoveHandle<T>> {
  76. static constexpr ArgumentType Type = ArgumentType::OutMoveHandle;
  77. };
  78. template <typename T, int A>
  79. struct ArgumentTraits<Buffer<T, A>> {
  80. static constexpr ArgumentType Type = (A & BufferAttr_In) == 0 ? ArgumentType::OutBuffer : ArgumentType::InBuffer;
  81. };
  82. template <typename T, int A>
  83. struct ArgumentTraits<InLargeData<T, A>> {
  84. static constexpr ArgumentType Type = ArgumentType::InLargeData;
  85. };
  86. template <typename T, int A>
  87. struct ArgumentTraits<OutLargeData<T, A>> {
  88. static constexpr ArgumentType Type = ArgumentType::OutLargeData;
  89. };
  90. template <typename T>
  91. struct ArgumentTraits {
  92. static constexpr ArgumentType Type = ArgumentType::InData;
  93. };
  94. template <typename... Ts>
  95. consteval bool ConstIfReference() {
  96. return ((!std::is_reference_v<Ts> || std::is_const_v<std::remove_reference_t<Ts>>) && ... && true);
  97. }
  98. struct RequestLayout {
  99. u32 copy_handle_count;
  100. u32 move_handle_count;
  101. u32 cmif_raw_data_size;
  102. u32 domain_interface_count;
  103. };
  104. template <typename MethodArguments, size_t PrevAlign = 1, size_t DataOffset = 0, size_t ArgIndex = 0>
  105. constexpr u32 GetInRawDataSize() {
  106. if constexpr (ArgIndex >= std::tuple_size_v<MethodArguments>) {
  107. return static_cast<u32>(DataOffset);
  108. } else {
  109. using ArgType = std::tuple_element_t<ArgIndex, MethodArguments>;
  110. if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InData || ArgumentTraits<ArgType>::Type == ArgumentType::InProcessId) {
  111. constexpr size_t ArgAlign = alignof(ArgType);
  112. constexpr size_t ArgSize = sizeof(ArgType);
  113. static_assert(PrevAlign <= ArgAlign, "Input argument is not ordered by alignment");
  114. constexpr size_t ArgOffset = Common::AlignUp(DataOffset, ArgAlign);
  115. constexpr size_t ArgEnd = ArgOffset + ArgSize;
  116. return GetInRawDataSize<MethodArguments, ArgAlign, ArgEnd, ArgIndex + 1>();
  117. } else {
  118. return GetInRawDataSize<MethodArguments, PrevAlign, DataOffset, ArgIndex + 1>();
  119. }
  120. }
  121. }
  122. template <typename MethodArguments, size_t PrevAlign = 1, size_t DataOffset = 0, size_t ArgIndex = 0>
  123. constexpr u32 GetOutRawDataSize() {
  124. if constexpr (ArgIndex >= std::tuple_size_v<MethodArguments>) {
  125. return static_cast<u32>(DataOffset);
  126. } else {
  127. using ArgType = std::tuple_element_t<ArgIndex, MethodArguments>;
  128. if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutData) {
  129. using RawArgType = typename ArgType::Type;
  130. constexpr size_t ArgAlign = alignof(RawArgType);
  131. constexpr size_t ArgSize = sizeof(RawArgType);
  132. static_assert(PrevAlign <= ArgAlign, "Output argument is not ordered by alignment");
  133. constexpr size_t ArgOffset = Common::AlignUp(DataOffset, ArgAlign);
  134. constexpr size_t ArgEnd = ArgOffset + ArgSize;
  135. return GetOutRawDataSize<MethodArguments, ArgAlign, ArgEnd, ArgIndex + 1>();
  136. } else {
  137. return GetOutRawDataSize<MethodArguments, PrevAlign, DataOffset, ArgIndex + 1>();
  138. }
  139. }
  140. }
  141. template <ArgumentType DataType, typename MethodArguments, size_t ArgCount = 0, size_t ArgIndex = 0>
  142. constexpr u32 GetArgumentTypeCount() {
  143. if constexpr (ArgIndex >= std::tuple_size_v<MethodArguments>) {
  144. return static_cast<u32>(ArgCount);
  145. } else {
  146. using ArgType = std::tuple_element_t<ArgIndex, MethodArguments>;
  147. if constexpr (ArgumentTraits<ArgType>::Type == DataType) {
  148. return GetArgumentTypeCount<DataType, MethodArguments, ArgCount + 1, ArgIndex + 1>();
  149. } else {
  150. return GetArgumentTypeCount<DataType, MethodArguments, ArgCount, ArgIndex + 1>();
  151. }
  152. }
  153. }
  154. template <typename MethodArguments>
  155. constexpr RequestLayout GetNonDomainReplyInLayout() {
  156. return RequestLayout{
  157. .copy_handle_count = GetArgumentTypeCount<ArgumentType::InCopyHandle, MethodArguments>(),
  158. .move_handle_count = 0,
  159. .cmif_raw_data_size = GetInRawDataSize<MethodArguments>(),
  160. .domain_interface_count = 0,
  161. };
  162. }
  163. template <typename MethodArguments>
  164. constexpr RequestLayout GetDomainReplyInLayout() {
  165. return RequestLayout{
  166. .copy_handle_count = GetArgumentTypeCount<ArgumentType::InCopyHandle, MethodArguments>(),
  167. .move_handle_count = 0,
  168. .cmif_raw_data_size = GetInRawDataSize<MethodArguments>(),
  169. .domain_interface_count = GetArgumentTypeCount<ArgumentType::InInterface, MethodArguments>(),
  170. };
  171. }
  172. template <typename MethodArguments>
  173. constexpr RequestLayout GetNonDomainReplyOutLayout() {
  174. return RequestLayout{
  175. .copy_handle_count = GetArgumentTypeCount<ArgumentType::OutCopyHandle, MethodArguments>(),
  176. .move_handle_count = GetArgumentTypeCount<ArgumentType::OutMoveHandle, MethodArguments>() + GetArgumentTypeCount<ArgumentType::OutInterface, MethodArguments>(),
  177. .cmif_raw_data_size = GetOutRawDataSize<MethodArguments>(),
  178. .domain_interface_count = 0,
  179. };
  180. }
  181. template <typename MethodArguments>
  182. constexpr RequestLayout GetDomainReplyOutLayout() {
  183. return RequestLayout{
  184. .copy_handle_count = GetArgumentTypeCount<ArgumentType::OutCopyHandle, MethodArguments>(),
  185. .move_handle_count = GetArgumentTypeCount<ArgumentType::OutMoveHandle, MethodArguments>(),
  186. .cmif_raw_data_size = GetOutRawDataSize<MethodArguments>(),
  187. .domain_interface_count = GetArgumentTypeCount<ArgumentType::OutInterface, MethodArguments>(),
  188. };
  189. }
  190. template <typename MethodArguments>
  191. constexpr RequestLayout GetReplyInLayout(bool is_domain) {
  192. return is_domain ? GetDomainReplyInLayout<MethodArguments>() : GetNonDomainReplyInLayout<MethodArguments>();
  193. }
  194. template <typename MethodArguments>
  195. constexpr RequestLayout GetReplyOutLayout(bool is_domain) {
  196. return is_domain ? GetDomainReplyOutLayout<MethodArguments>() : GetNonDomainReplyOutLayout<MethodArguments>();
  197. }
  198. using OutTemporaryBuffers = std::array<Common::ScratchBuffer<u8>, 3>;
  199. template <typename MethodArguments, typename CallArguments, size_t PrevAlign = 1, size_t DataOffset = 0, size_t HandleIndex = 0, size_t InBufferIndex = 0, size_t OutBufferIndex = 0, bool RawDataFinished = false, size_t ArgIndex = 0>
  200. void ReadInArgument(bool is_domain, CallArguments& args, const u8* raw_data, HLERequestContext& ctx, OutTemporaryBuffers& temp) {
  201. if constexpr (ArgIndex >= std::tuple_size_v<CallArguments>) {
  202. return;
  203. } else {
  204. using ArgType = std::tuple_element_t<ArgIndex, MethodArguments>;
  205. if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InData || ArgumentTraits<ArgType>::Type == ArgumentType::InProcessId) {
  206. constexpr size_t ArgAlign = alignof(ArgType);
  207. constexpr size_t ArgSize = sizeof(ArgType);
  208. static_assert(PrevAlign <= ArgAlign, "Input argument is not ordered by alignment");
  209. static_assert(!RawDataFinished, "All input interface arguments must appear after raw data");
  210. static_assert(!std::is_pointer_v<ArgType>, "Input raw data must not be a pointer");
  211. static_assert(std::is_trivially_copyable_v<ArgType>, "Input raw data must be trivially copyable");
  212. constexpr size_t ArgOffset = Common::AlignUp(DataOffset, ArgAlign);
  213. constexpr size_t ArgEnd = ArgOffset + ArgSize;
  214. if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InProcessId) {
  215. // TODO: abort parsing if PID is not provided?
  216. // TODO: validate against raw data value?
  217. std::get<ArgIndex>(args).pid = ctx.GetPID();
  218. } else {
  219. std::memcpy(&std::get<ArgIndex>(args), raw_data + ArgOffset, ArgSize);
  220. }
  221. return ReadInArgument<MethodArguments, CallArguments, ArgAlign, ArgEnd, HandleIndex, InBufferIndex, OutBufferIndex, false, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp);
  222. } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InInterface) {
  223. constexpr size_t ArgAlign = alignof(u32);
  224. constexpr size_t ArgSize = sizeof(u32);
  225. constexpr size_t ArgOffset = Common::AlignUp(DataOffset, ArgAlign);
  226. constexpr size_t ArgEnd = ArgOffset + ArgSize;
  227. ASSERT(is_domain);
  228. ASSERT(ctx.GetDomainMessageHeader().input_object_count > 0);
  229. u32 value{};
  230. std::memcpy(&value, raw_data + ArgOffset, ArgSize);
  231. std::get<ArgIndex>(args) = ctx.GetDomainHandler<typename ArgType::element_type>(value - 1);
  232. return ReadInArgument<MethodArguments, CallArguments, ArgAlign, ArgEnd, HandleIndex, InBufferIndex, OutBufferIndex, true, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp);
  233. } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InCopyHandle) {
  234. std::get<ArgIndex>(args) = ctx.GetObjectFromHandle<typename ArgType::Type>(ctx.GetCopyHandle(HandleIndex)).GetPointerUnsafe();
  235. return ReadInArgument<MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex + 1, InBufferIndex, OutBufferIndex, RawDataFinished, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp);
  236. } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InLargeData) {
  237. constexpr size_t BufferSize = sizeof(typename ArgType::Type);
  238. // Clear the existing data.
  239. std::memset(&std::get<ArgIndex>(args), 0, BufferSize);
  240. std::span<const u8> buffer{};
  241. ASSERT(ctx.CanReadBuffer(InBufferIndex));
  242. if constexpr (ArgType::Attr & BufferAttr_HipcAutoSelect) {
  243. buffer = ctx.ReadBuffer(InBufferIndex);
  244. } else if constexpr (ArgType::Attr & BufferAttr_HipcMapAlias) {
  245. buffer = ctx.ReadBufferA(InBufferIndex);
  246. } else /* if (ArgType::Attr & BufferAttr_HipcPointer) */ {
  247. buffer = ctx.ReadBufferX(InBufferIndex);
  248. }
  249. std::memcpy(&std::get<ArgIndex>(args), buffer.data(), std::min(BufferSize, buffer.size()));
  250. return ReadInArgument<MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex, InBufferIndex + 1, OutBufferIndex, RawDataFinished, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp);
  251. } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InBuffer) {
  252. using ElementType = typename ArgType::Type;
  253. std::span<const u8> buffer{};
  254. if (ctx.CanReadBuffer(InBufferIndex)) {
  255. if constexpr (ArgType::Attr & BufferAttr_HipcAutoSelect) {
  256. buffer = ctx.ReadBuffer(InBufferIndex);
  257. } else if constexpr (ArgType::Attr & BufferAttr_HipcMapAlias) {
  258. buffer = ctx.ReadBufferA(InBufferIndex);
  259. } else /* if (ArgType::Attr & BufferAttr_HipcPointer) */ {
  260. buffer = ctx.ReadBufferX(InBufferIndex);
  261. }
  262. }
  263. ElementType* ptr = (ElementType*) buffer.data();
  264. size_t size = buffer.size() / sizeof(ElementType);
  265. std::get<ArgIndex>(args) = std::span(ptr, size);
  266. return ReadInArgument<MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex, InBufferIndex + 1, OutBufferIndex, RawDataFinished, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp);
  267. } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutLargeData) {
  268. constexpr size_t BufferSize = sizeof(typename ArgType::Type);
  269. // Clear the existing data.
  270. std::memset(&std::get<ArgIndex>(args).raw, 0, BufferSize);
  271. return ReadInArgument<MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex, InBufferIndex, OutBufferIndex + 1, RawDataFinished, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp);
  272. } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutBuffer) {
  273. using ElementType = typename ArgType::Type;
  274. // Set up scratch buffer.
  275. auto& buffer = temp[OutBufferIndex];
  276. if (ctx.CanWriteBuffer(OutBufferIndex)) {
  277. buffer.resize_destructive(ctx.GetWriteBufferSize(OutBufferIndex));
  278. } else {
  279. buffer.resize_destructive(0);
  280. }
  281. ElementType* ptr = (ElementType*) buffer.data();
  282. size_t size = buffer.size() / sizeof(ElementType);
  283. std::get<ArgIndex>(args) = std::span(ptr, size);
  284. return ReadInArgument<MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex, InBufferIndex, OutBufferIndex + 1, RawDataFinished, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp);
  285. } else {
  286. return ReadInArgument<MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex, InBufferIndex, OutBufferIndex, RawDataFinished, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp);
  287. }
  288. }
  289. }
  290. template <typename MethodArguments, typename CallArguments, size_t PrevAlign = 1, size_t DataOffset = 0, size_t OutBufferIndex = 0, bool RawDataFinished = false, size_t ArgIndex = 0>
  291. void WriteOutArgument(bool is_domain, CallArguments& args, u8* raw_data, HLERequestContext& ctx, OutTemporaryBuffers& temp) {
  292. if constexpr (ArgIndex >= std::tuple_size_v<CallArguments>) {
  293. return;
  294. } else {
  295. using ArgType = std::tuple_element_t<ArgIndex, MethodArguments>;
  296. if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutData) {
  297. using RawArgType = decltype(std::get<ArgIndex>(args).raw);
  298. constexpr size_t ArgAlign = alignof(RawArgType);
  299. constexpr size_t ArgSize = sizeof(RawArgType);
  300. static_assert(PrevAlign <= ArgAlign, "Output argument is not ordered by alignment");
  301. static_assert(!RawDataFinished, "All output interface arguments must appear after raw data");
  302. static_assert(!std::is_pointer_v<ArgType>, "Output raw data must not be a pointer");
  303. static_assert(!std::is_pointer_v<RawArgType>, "Output raw data must not be a pointer");
  304. static_assert(std::is_trivially_copyable_v<RawArgType>, "Output raw data must be trivially copyable");
  305. constexpr size_t ArgOffset = Common::AlignUp(DataOffset, ArgAlign);
  306. constexpr size_t ArgEnd = ArgOffset + ArgSize;
  307. std::memcpy(raw_data + ArgOffset, &std::get<ArgIndex>(args).raw, ArgSize);
  308. return WriteOutArgument<MethodArguments, CallArguments, ArgAlign, ArgEnd, OutBufferIndex, false, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp);
  309. } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutInterface) {
  310. if (is_domain) {
  311. ctx.AddDomainObject(std::get<ArgIndex>(args).raw);
  312. } else {
  313. ctx.AddMoveInterface(std::get<ArgIndex>(args).raw);
  314. }
  315. return WriteOutArgument<MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex, true, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp);
  316. } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutCopyHandle) {
  317. ctx.AddCopyObject(std::get<ArgIndex>(args).raw);
  318. return WriteOutArgument<MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex, RawDataFinished, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp);
  319. } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutMoveHandle) {
  320. ctx.AddMoveObject(std::get<ArgIndex>(args).raw);
  321. return WriteOutArgument<MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex, RawDataFinished, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp);
  322. } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutLargeData) {
  323. constexpr size_t BufferSize = sizeof(typename ArgType::Type);
  324. ASSERT(ctx.CanWriteBuffer(OutBufferIndex));
  325. if constexpr (ArgType::Attr & BufferAttr_HipcAutoSelect) {
  326. ctx.WriteBuffer(std::get<ArgIndex>(args), OutBufferIndex);
  327. } else if constexpr (ArgType::Attr & BufferAttr_HipcMapAlias) {
  328. ctx.WriteBufferB(&std::get<ArgIndex>(args), BufferSize, OutBufferIndex);
  329. } else /* if (ArgType::Attr & BufferAttr_HipcPointer) */ {
  330. ctx.WriteBufferC(&std::get<ArgIndex>(args), BufferSize, OutBufferIndex);
  331. }
  332. return WriteOutArgument<MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex + 1, RawDataFinished, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp);
  333. } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutBuffer) {
  334. auto& buffer = temp[OutBufferIndex];
  335. const size_t size = buffer.size();
  336. if (ctx.CanWriteBuffer(OutBufferIndex)) {
  337. if constexpr (ArgType::Attr & BufferAttr_HipcAutoSelect) {
  338. ctx.WriteBuffer(buffer.data(), size, OutBufferIndex);
  339. } else if constexpr (ArgType::Attr & BufferAttr_HipcMapAlias) {
  340. ctx.WriteBufferB(buffer.data(), size, OutBufferIndex);
  341. } else /* if (ArgType::Attr & BufferAttr_HipcPointer) */ {
  342. ctx.WriteBufferC(buffer.data(), size, OutBufferIndex);
  343. }
  344. }
  345. return WriteOutArgument<MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex + 1, RawDataFinished, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp);
  346. } else {
  347. return WriteOutArgument<MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex, RawDataFinished, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp);
  348. }
  349. }
  350. }
  351. template <bool Domain, typename T, typename... A>
  352. void CmifReplyWrapImpl(HLERequestContext& ctx, T& t, Result (T::*f)(A...)) {
  353. // Verify domain state.
  354. if constexpr (!Domain) {
  355. ASSERT_MSG(!ctx.GetManager()->IsDomain(), "Non-domain reply used on domain session");
  356. }
  357. const bool is_domain = Domain ? ctx.GetManager()->IsDomain() : false;
  358. static_assert(ConstIfReference<A...>(), "Arguments taken by reference must be const");
  359. using MethodArguments = std::tuple<std::remove_cvref_t<A>...>;
  360. OutTemporaryBuffers buffers{};
  361. auto call_arguments = std::tuple<typename UnwrapArg<A>::Type...>();
  362. // Read inputs.
  363. const size_t offset_plus_command_id = ctx.GetDataPayloadOffset() + 2;
  364. ReadInArgument<MethodArguments>(is_domain, call_arguments, reinterpret_cast<u8*>(ctx.CommandBuffer() + offset_plus_command_id), ctx, buffers);
  365. // Call.
  366. const auto Callable = [&]<typename... CallArgs>(CallArgs&... args) {
  367. return (t.*f)(args...);
  368. };
  369. const Result res = std::apply(Callable, call_arguments);
  370. // Write result.
  371. const RequestLayout layout = GetReplyOutLayout<MethodArguments>(is_domain);
  372. IPC::ResponseBuilder rb{ctx, 2 + Common::DivCeil(layout.cmif_raw_data_size, sizeof(u32)), layout.copy_handle_count, layout.move_handle_count + layout.domain_interface_count};
  373. rb.Push(res);
  374. // Write out arguments.
  375. WriteOutArgument<MethodArguments>(is_domain, call_arguments, reinterpret_cast<u8*>(ctx.CommandBuffer() + rb.GetCurrentOffset()), ctx, buffers);
  376. }
  377. // clang-format on
  378. template <typename Self>
  379. template <bool Domain, auto F>
  380. inline void ServiceFramework<Self>::CmifReplyWrap(HLERequestContext& ctx) {
  381. return CmifReplyWrapImpl<Domain>(ctx, *static_cast<Self*>(this), F);
  382. }
  383. } // namespace Service