ipc_helpers.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. // Copyright 2016 Citra Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #pragma once
  5. #include "core/hle/ipc.h"
  6. #include "core/hle/kernel/handle_table.h"
  7. #include "core/hle/kernel/kernel.h"
  8. namespace IPC {
  9. class RequestHelperBase {
  10. protected:
  11. u32* cmdbuf;
  12. ptrdiff_t index = 1;
  13. Header header;
  14. public:
  15. RequestHelperBase(u32* command_buffer, Header command_header)
  16. : cmdbuf(command_buffer), header(command_header) {}
  17. /// Returns the total size of the request in words
  18. size_t TotalSize() const {
  19. return 1 /* command header */ + header.normal_params_size + header.translate_params_size;
  20. }
  21. void ValidateHeader() {
  22. DEBUG_ASSERT_MSG(index == TotalSize(), "Operations do not match the header (cmd 0x%x)",
  23. header.raw);
  24. }
  25. void Skip(unsigned size_in_words, bool set_to_null) {
  26. if (set_to_null)
  27. memset(cmdbuf + index, 0, size_in_words * sizeof(u32));
  28. index += size_in_words;
  29. }
  30. /**
  31. * @brief Retrieves the address of a static buffer, used when a buffer is needed for output
  32. * @param buffer_id The index of the static buffer
  33. * @param data_size If non-null, will store the size of the buffer
  34. */
  35. VAddr PeekStaticBuffer(u8 buffer_id, size_t* data_size = nullptr) const {
  36. u32* static_buffer = cmdbuf + Kernel::kStaticBuffersOffset / sizeof(u32) + buffer_id * 2;
  37. if (data_size)
  38. *data_size = StaticBufferDescInfo{static_buffer[0]}.size;
  39. return static_buffer[1];
  40. }
  41. };
  42. class RequestBuilder : public RequestHelperBase {
  43. public:
  44. RequestBuilder(u32* command_buffer, Header command_header)
  45. : RequestHelperBase(command_buffer, command_header) {
  46. cmdbuf[0] = header.raw;
  47. }
  48. explicit RequestBuilder(u32* command_buffer, u32 command_header)
  49. : RequestBuilder(command_buffer, Header{command_header}) {}
  50. RequestBuilder(u32* command_buffer, u16 command_id, unsigned normal_params_size,
  51. unsigned translate_params_size)
  52. : RequestBuilder(command_buffer,
  53. MakeHeader(command_id, normal_params_size, translate_params_size)) {}
  54. // Validate on destruction, as there shouldn't be any case where we don't want it
  55. ~RequestBuilder() {
  56. ValidateHeader();
  57. }
  58. template <typename T>
  59. void Push(T value);
  60. template <typename First, typename... Other>
  61. void Push(const First& first_value, const Other&... other_values);
  62. /**
  63. * @brief Copies the content of the given trivially copyable class to the buffer as a normal
  64. * param
  65. * @note: The input class must be correctly packed/padded to fit hardware layout.
  66. */
  67. template <typename T>
  68. void PushRaw(const T& value);
  69. // TODO : ensure that translate params are added after all regular params
  70. template <typename... H>
  71. void PushCopyHandles(H... handles);
  72. template <typename... H>
  73. void PushMoveHandles(H... handles);
  74. void PushCurrentPIDHandle();
  75. void PushStaticBuffer(VAddr buffer_vaddr, u32 size, u8 buffer_id);
  76. void PushMappedBuffer(VAddr buffer_vaddr, u32 size, MappedBufferPermissions perms);
  77. };
  78. /// Push ///
  79. template <>
  80. inline void RequestBuilder::Push(u32 value) {
  81. cmdbuf[index++] = value;
  82. }
  83. template <typename T>
  84. void RequestBuilder::PushRaw(const T& value) {
  85. static_assert(std::is_trivially_copyable<T>(), "Raw types should be trivially copyable");
  86. std::memcpy(cmdbuf + index, &value, sizeof(T));
  87. index += (sizeof(T) + 3) / 4; // round up to word length
  88. }
  89. template <>
  90. inline void RequestBuilder::Push(u8 value) {
  91. PushRaw(value);
  92. }
  93. template <>
  94. inline void RequestBuilder::Push(u16 value) {
  95. PushRaw(value);
  96. }
  97. template <>
  98. inline void RequestBuilder::Push(u64 value) {
  99. Push(static_cast<u32>(value));
  100. Push(static_cast<u32>(value >> 32));
  101. }
  102. template <>
  103. inline void RequestBuilder::Push(bool value) {
  104. Push(static_cast<u8>(value));
  105. }
  106. template <>
  107. inline void RequestBuilder::Push(ResultCode value) {
  108. Push(value.raw);
  109. }
  110. template <typename First, typename... Other>
  111. void RequestBuilder::Push(const First& first_value, const Other&... other_values) {
  112. Push(first_value);
  113. Push(other_values...);
  114. }
  115. template <typename... H>
  116. inline void RequestBuilder::PushCopyHandles(H... handles) {
  117. Push(CopyHandleDesc(sizeof...(H)));
  118. Push(static_cast<Kernel::Handle>(handles)...);
  119. }
  120. template <typename... H>
  121. inline void RequestBuilder::PushMoveHandles(H... handles) {
  122. Push(MoveHandleDesc(sizeof...(H)));
  123. Push(static_cast<Kernel::Handle>(handles)...);
  124. }
  125. inline void RequestBuilder::PushCurrentPIDHandle() {
  126. Push(CallingPidDesc());
  127. Push(u32(0));
  128. }
  129. inline void RequestBuilder::PushStaticBuffer(VAddr buffer_vaddr, u32 size, u8 buffer_id) {
  130. Push(StaticBufferDesc(size, buffer_id));
  131. Push(buffer_vaddr);
  132. }
  133. inline void RequestBuilder::PushMappedBuffer(VAddr buffer_vaddr, u32 size,
  134. MappedBufferPermissions perms) {
  135. Push(MappedBufferDesc(size, perms));
  136. Push(buffer_vaddr);
  137. }
  138. class RequestParser : public RequestHelperBase {
  139. public:
  140. RequestParser(u32* command_buffer, Header command_header)
  141. : RequestHelperBase(command_buffer, command_header) {}
  142. explicit RequestParser(u32* command_buffer, u32 command_header)
  143. : RequestParser(command_buffer, Header{command_header}) {}
  144. RequestParser(u32* command_buffer, u16 command_id, unsigned normal_params_size,
  145. unsigned translate_params_size)
  146. : RequestParser(command_buffer,
  147. MakeHeader(command_id, normal_params_size, translate_params_size)) {}
  148. RequestBuilder MakeBuilder(u32 normal_params_size, u32 translate_params_size,
  149. bool validateHeader = true) {
  150. if (validateHeader)
  151. ValidateHeader();
  152. Header builderHeader{
  153. MakeHeader(header.command_id, normal_params_size, translate_params_size)};
  154. return {cmdbuf, builderHeader};
  155. }
  156. template <typename T>
  157. T Pop();
  158. template <typename T>
  159. void Pop(T& value);
  160. template <typename First, typename... Other>
  161. void Pop(First& first_value, Other&... other_values);
  162. Kernel::Handle PopHandle();
  163. template <typename... H>
  164. void PopHandles(H&... handles);
  165. /**
  166. * @brief Pops the static buffer vaddr
  167. * @return The virtual address of the buffer
  168. * @param[out] data_size If non-null, the pointed value will be set to the size of the data
  169. * @param[out] useStaticBuffersToGetVaddr Indicates if we should read the vaddr from the static
  170. * buffers (which is the correct thing to do, but no service presently implement it) instead of
  171. * using the same value as the process who sent the request
  172. * given by the source process
  173. *
  174. * Static buffers must be set up before any IPC request using those is sent.
  175. * It is the duty of the process (usually services) to allocate and set up the receiving static
  176. * buffer information
  177. * Please note that the setup uses virtual addresses.
  178. */
  179. VAddr PopStaticBuffer(size_t* data_size = nullptr, bool useStaticBuffersToGetVaddr = false);
  180. /**
  181. * @brief Pops the mapped buffer vaddr
  182. * @return The virtual address of the buffer
  183. * @param[out] data_size If non-null, the pointed value will be set to the size of the data
  184. * given by the source process
  185. * @param[out] buffer_perms If non-null, the pointed value will be set to the permissions of the
  186. * buffer
  187. */
  188. VAddr PopMappedBuffer(size_t* data_size = nullptr,
  189. MappedBufferPermissions* buffer_perms = nullptr);
  190. /**
  191. * @brief Reads the next normal parameters as a struct, by copying it
  192. * @note: The output class must be correctly packed/padded to fit hardware layout.
  193. */
  194. template <typename T>
  195. void PopRaw(T& value);
  196. /**
  197. * @brief Reads the next normal parameters as a struct, by copying it into a new value
  198. * @note: The output class must be correctly packed/padded to fit hardware layout.
  199. */
  200. template <typename T>
  201. T PopRaw();
  202. };
  203. /// Pop ///
  204. template <>
  205. inline u32 RequestParser::Pop() {
  206. return cmdbuf[index++];
  207. }
  208. template <typename T>
  209. void RequestParser::PopRaw(T& value) {
  210. static_assert(std::is_trivially_copyable<T>(), "Raw types should be trivially copyable");
  211. std::memcpy(&value, cmdbuf + index, sizeof(T));
  212. index += (sizeof(T) + 3) / 4; // round up to word length
  213. }
  214. template <typename T>
  215. T RequestParser::PopRaw() {
  216. T value;
  217. PopRaw(value);
  218. return value;
  219. }
  220. template <>
  221. inline u8 RequestParser::Pop() {
  222. return PopRaw<u8>();
  223. }
  224. template <>
  225. inline u16 RequestParser::Pop() {
  226. return PopRaw<u16>();
  227. }
  228. template <>
  229. inline u64 RequestParser::Pop() {
  230. const u64 lsw = Pop<u32>();
  231. const u64 msw = Pop<u32>();
  232. return msw << 32 | lsw;
  233. }
  234. template <>
  235. inline bool RequestParser::Pop() {
  236. return Pop<u8>() != 0;
  237. }
  238. template <>
  239. inline ResultCode RequestParser::Pop() {
  240. return ResultCode{Pop<u32>()};
  241. }
  242. template <typename T>
  243. void RequestParser::Pop(T& value) {
  244. value = Pop<T>();
  245. }
  246. template <typename First, typename... Other>
  247. void RequestParser::Pop(First& first_value, Other&... other_values) {
  248. first_value = Pop<First>();
  249. Pop(other_values...);
  250. }
  251. inline Kernel::Handle RequestParser::PopHandle() {
  252. const u32 handle_descriptor = Pop<u32>();
  253. DEBUG_ASSERT_MSG(IsHandleDescriptor(handle_descriptor),
  254. "Tried to pop handle(s) but the descriptor is not a handle descriptor");
  255. DEBUG_ASSERT_MSG(HandleNumberFromDesc(handle_descriptor) == 1,
  256. "Descriptor indicates that there isn't exactly one handle");
  257. return Pop<Kernel::Handle>();
  258. }
  259. template <typename... H>
  260. void RequestParser::PopHandles(H&... handles) {
  261. const u32 handle_descriptor = Pop<u32>();
  262. const int handles_number = sizeof...(H);
  263. DEBUG_ASSERT_MSG(IsHandleDescriptor(handle_descriptor),
  264. "Tried to pop handle(s) but the descriptor is not a handle descriptor");
  265. DEBUG_ASSERT_MSG(handles_number == HandleNumberFromDesc(handle_descriptor),
  266. "Number of handles doesn't match the descriptor");
  267. Pop(static_cast<Kernel::Handle&>(handles)...);
  268. }
  269. inline VAddr RequestParser::PopStaticBuffer(size_t* data_size, bool useStaticBuffersToGetVaddr) {
  270. const u32 sbuffer_descriptor = Pop<u32>();
  271. StaticBufferDescInfo bufferInfo{sbuffer_descriptor};
  272. if (data_size != nullptr)
  273. *data_size = bufferInfo.size;
  274. if (!useStaticBuffersToGetVaddr)
  275. return Pop<VAddr>();
  276. else {
  277. ASSERT_MSG(0, "remove the assert if multiprocess/IPC translation are implemented.");
  278. // The buffer has already been copied to the static buffer by the kernel during
  279. // translation
  280. Pop<VAddr>(); // Pop the calling process buffer address
  281. // and get the vaddr from the static buffers
  282. return cmdbuf[(0x100 >> 2) + bufferInfo.buffer_id * 2 + 1];
  283. }
  284. }
  285. inline VAddr RequestParser::PopMappedBuffer(size_t* data_size,
  286. MappedBufferPermissions* buffer_perms) {
  287. const u32 sbuffer_descriptor = Pop<u32>();
  288. MappedBufferDescInfo bufferInfo{sbuffer_descriptor};
  289. if (data_size != nullptr)
  290. *data_size = bufferInfo.size;
  291. if (buffer_perms != nullptr)
  292. *buffer_perms = bufferInfo.perms;
  293. return Pop<VAddr>();
  294. }
  295. } // namespace IPC