svc_ipc.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include "common/scope_exit.h"
  4. #include "common/scratch_buffer.h"
  5. #include "core/core.h"
  6. #include "core/hle/kernel/k_client_session.h"
  7. #include "core/hle/kernel/k_hardware_timer.h"
  8. #include "core/hle/kernel/k_process.h"
  9. #include "core/hle/kernel/k_scoped_resource_reservation.h"
  10. #include "core/hle/kernel/k_server_session.h"
  11. #include "core/hle/kernel/k_session.h"
  12. #include "core/hle/kernel/svc.h"
  13. #include "core/hle/kernel/svc_results.h"
  14. namespace Kernel::Svc {
  15. namespace {
  16. Result SendSyncRequestImpl(KernelCore& kernel, uintptr_t message, size_t buffer_size,
  17. Handle session_handle) {
  18. // Get the client session.
  19. KScopedAutoObject session =
  20. GetCurrentProcess(kernel).GetHandleTable().GetObject<KClientSession>(session_handle);
  21. R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
  22. // Get the parent, and persist a reference to it until we're done.
  23. KScopedAutoObject parent = session->GetParent();
  24. ASSERT(parent.IsNotNull());
  25. // Send the request.
  26. R_RETURN(session->SendSyncRequest(message, buffer_size));
  27. }
  28. Result ReplyAndReceiveImpl(KernelCore& kernel, int32_t* out_index, uintptr_t message,
  29. size_t buffer_size, KPhysicalAddress message_paddr,
  30. KSynchronizationObject** objs, int32_t num_objects, Handle reply_target,
  31. int64_t timeout_ns) {
  32. // Reply to the target, if one is specified.
  33. if (reply_target != InvalidHandle) {
  34. KScopedAutoObject session =
  35. GetCurrentProcess(kernel).GetHandleTable().GetObject<KServerSession>(reply_target);
  36. R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
  37. // If we fail to reply, we want to set the output index to -1.
  38. ON_RESULT_FAILURE {
  39. *out_index = -1;
  40. };
  41. // Send the reply.
  42. R_TRY(session->SendReply());
  43. // R_TRY(session->SendReply(message, buffer_size, message_paddr));
  44. }
  45. // Receive a message.
  46. {
  47. // Convert the timeout from nanoseconds to ticks.
  48. // NOTE: Nintendo does not use this conversion logic in WaitSynchronization...
  49. s64 timeout;
  50. if (timeout_ns > 0) {
  51. const s64 offset_tick(timeout_ns);
  52. if (offset_tick > 0) {
  53. timeout = kernel.HardwareTimer().GetTick() + offset_tick + 2;
  54. if (timeout <= 0) {
  55. timeout = std::numeric_limits<s64>::max();
  56. }
  57. } else {
  58. timeout = std::numeric_limits<s64>::max();
  59. }
  60. } else {
  61. timeout = timeout_ns;
  62. }
  63. // Wait for a message.
  64. while (true) {
  65. // Wait for an object.
  66. s32 index;
  67. Result result = KSynchronizationObject::Wait(kernel, std::addressof(index), objs,
  68. num_objects, timeout);
  69. if (ResultTimedOut == result) {
  70. R_THROW(result);
  71. }
  72. // Receive the request.
  73. if (R_SUCCEEDED(result)) {
  74. KServerSession* session = objs[index]->DynamicCast<KServerSession*>();
  75. if (session != nullptr) {
  76. // result = session->ReceiveRequest(message, buffer_size, message_paddr);
  77. result = session->ReceiveRequest();
  78. if (ResultNotFound == result) {
  79. continue;
  80. }
  81. }
  82. }
  83. *out_index = index;
  84. R_RETURN(result);
  85. }
  86. }
  87. }
  88. Result ReplyAndReceiveImpl(KernelCore& kernel, int32_t* out_index, uintptr_t message,
  89. size_t buffer_size, KPhysicalAddress message_paddr,
  90. KProcessAddress user_handles, int32_t num_handles, Handle reply_target,
  91. int64_t timeout_ns) {
  92. // Ensure number of handles is valid.
  93. R_UNLESS(0 <= num_handles && num_handles <= Svc::ArgumentHandleCountMax, ResultOutOfRange);
  94. // Get the synchronization context.
  95. auto& process = GetCurrentProcess(kernel);
  96. auto& thread = GetCurrentThread(kernel);
  97. auto& handle_table = process.GetHandleTable();
  98. KSynchronizationObject** objs = thread.GetSynchronizationObjectBuffer().data();
  99. Handle* handles = thread.GetHandleBuffer().data();
  100. // Copy user handles.
  101. if (num_handles > 0) {
  102. // Ensure that we can try to get the handles.
  103. R_UNLESS(process.GetPageTable().Contains(user_handles, num_handles * sizeof(Handle)),
  104. ResultInvalidPointer);
  105. // Get the handles
  106. R_UNLESS(
  107. GetCurrentMemory(kernel).ReadBlock(user_handles, handles, sizeof(Handle) * num_handles),
  108. ResultInvalidPointer);
  109. // Convert the handles to objects.
  110. R_UNLESS(
  111. handle_table.GetMultipleObjects<KSynchronizationObject>(objs, handles, num_handles),
  112. ResultInvalidHandle);
  113. }
  114. // Ensure handles are closed when we're done.
  115. SCOPE_EXIT({
  116. for (auto i = 0; i < num_handles; ++i) {
  117. objs[i]->Close();
  118. }
  119. });
  120. R_RETURN(ReplyAndReceiveImpl(kernel, out_index, message, buffer_size, message_paddr, objs,
  121. num_handles, reply_target, timeout_ns));
  122. }
  123. } // namespace
  124. /// Makes a blocking IPC call to a service.
  125. Result SendSyncRequest(Core::System& system, Handle session_handle) {
  126. R_RETURN(SendSyncRequestImpl(system.Kernel(), 0, 0, session_handle));
  127. }
  128. Result SendSyncRequestWithUserBuffer(Core::System& system, uint64_t message, uint64_t buffer_size,
  129. Handle session_handle) {
  130. auto& kernel = system.Kernel();
  131. // Validate that the message buffer is page aligned and does not overflow.
  132. R_UNLESS(Common::IsAligned(message, PageSize), ResultInvalidAddress);
  133. R_UNLESS(buffer_size > 0, ResultInvalidSize);
  134. R_UNLESS(Common::IsAligned(buffer_size, PageSize), ResultInvalidSize);
  135. R_UNLESS(message < message + buffer_size, ResultInvalidCurrentMemory);
  136. // Get the process page table.
  137. auto& page_table = GetCurrentProcess(kernel).GetPageTable();
  138. // Lock the message buffer.
  139. R_TRY(page_table.LockForIpcUserBuffer(nullptr, message, buffer_size));
  140. {
  141. // If we fail to send the message, unlock the message buffer.
  142. ON_RESULT_FAILURE {
  143. page_table.UnlockForIpcUserBuffer(message, buffer_size);
  144. };
  145. // Send the request.
  146. ASSERT(message != 0);
  147. R_TRY(SendSyncRequestImpl(kernel, message, buffer_size, session_handle));
  148. }
  149. // We successfully processed, so try to unlock the message buffer.
  150. R_RETURN(page_table.UnlockForIpcUserBuffer(message, buffer_size));
  151. }
  152. Result SendAsyncRequestWithUserBuffer(Core::System& system, Handle* out_event_handle,
  153. uint64_t message, uint64_t buffer_size,
  154. Handle session_handle) {
  155. // Get the process and handle table.
  156. auto& process = GetCurrentProcess(system.Kernel());
  157. auto& handle_table = process.GetHandleTable();
  158. // Reserve a new event from the process resource limit.
  159. KScopedResourceReservation event_reservation(std::addressof(process),
  160. Svc::LimitableResource::EventCountMax);
  161. R_UNLESS(event_reservation.Succeeded(), ResultLimitReached);
  162. // Get the client session.
  163. KScopedAutoObject session = process.GetHandleTable().GetObject<KClientSession>(session_handle);
  164. R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
  165. // Get the parent, and persist a reference to it until we're done.
  166. KScopedAutoObject parent = session->GetParent();
  167. ASSERT(parent.IsNotNull());
  168. // Create a new event.
  169. KEvent* event = KEvent::Create(system.Kernel());
  170. R_UNLESS(event != nullptr, ResultOutOfResource);
  171. // Initialize the event.
  172. event->Initialize(std::addressof(process));
  173. // Commit our reservation.
  174. event_reservation.Commit();
  175. // At end of scope, kill the standing references to the sub events.
  176. SCOPE_EXIT({
  177. event->GetReadableEvent().Close();
  178. event->Close();
  179. });
  180. // Register the event.
  181. KEvent::Register(system.Kernel(), event);
  182. // Add the readable event to the handle table.
  183. R_TRY(handle_table.Add(out_event_handle, std::addressof(event->GetReadableEvent())));
  184. // Ensure that if we fail to send the request, we close the readable handle.
  185. ON_RESULT_FAILURE {
  186. handle_table.Remove(*out_event_handle);
  187. };
  188. // Send the async request.
  189. R_RETURN(session->SendAsyncRequest(event, message, buffer_size));
  190. }
  191. Result ReplyAndReceive(Core::System& system, s32* out_index, uint64_t handles, s32 num_handles,
  192. Handle reply_target, s64 timeout_ns) {
  193. R_RETURN(ReplyAndReceiveImpl(system.Kernel(), out_index, 0, 0, 0, handles, num_handles,
  194. reply_target, timeout_ns));
  195. }
  196. Result ReplyAndReceiveWithUserBuffer(Core::System& system, int32_t* out_index, uint64_t message,
  197. uint64_t buffer_size, uint64_t handles, int32_t num_handles,
  198. Handle reply_target, int64_t timeout_ns) {
  199. // Validate that the message buffer is page aligned and does not overflow.
  200. R_UNLESS(Common::IsAligned(message, PageSize), ResultInvalidAddress);
  201. R_UNLESS(buffer_size > 0, ResultInvalidSize);
  202. R_UNLESS(Common::IsAligned(buffer_size, PageSize), ResultInvalidSize);
  203. R_UNLESS(message < message + buffer_size, ResultInvalidCurrentMemory);
  204. // Get the process page table.
  205. auto& page_table = GetCurrentProcess(system.Kernel()).GetPageTable();
  206. // Lock the message buffer, getting its physical address.
  207. KPhysicalAddress message_paddr;
  208. R_TRY(page_table.LockForIpcUserBuffer(std::addressof(message_paddr), message, buffer_size));
  209. {
  210. // If we fail to send the message, unlock the message buffer.
  211. ON_RESULT_FAILURE {
  212. page_table.UnlockForIpcUserBuffer(message, buffer_size);
  213. };
  214. // Reply/Receive the request.
  215. ASSERT(message != 0);
  216. R_TRY(ReplyAndReceiveImpl(system.Kernel(), out_index, message, buffer_size, message_paddr,
  217. handles, num_handles, reply_target, timeout_ns));
  218. }
  219. // We successfully processed, so try to unlock the message buffer.
  220. R_RETURN(page_table.UnlockForIpcUserBuffer(message, buffer_size));
  221. }
  222. Result SendSyncRequest64(Core::System& system, Handle session_handle) {
  223. R_RETURN(SendSyncRequest(system, session_handle));
  224. }
  225. Result SendSyncRequestWithUserBuffer64(Core::System& system, uint64_t message_buffer,
  226. uint64_t message_buffer_size, Handle session_handle) {
  227. R_RETURN(
  228. SendSyncRequestWithUserBuffer(system, message_buffer, message_buffer_size, session_handle));
  229. }
  230. Result SendAsyncRequestWithUserBuffer64(Core::System& system, Handle* out_event_handle,
  231. uint64_t message_buffer, uint64_t message_buffer_size,
  232. Handle session_handle) {
  233. R_RETURN(SendAsyncRequestWithUserBuffer(system, out_event_handle, message_buffer,
  234. message_buffer_size, session_handle));
  235. }
  236. Result ReplyAndReceive64(Core::System& system, int32_t* out_index, uint64_t handles,
  237. int32_t num_handles, Handle reply_target, int64_t timeout_ns) {
  238. R_RETURN(ReplyAndReceive(system, out_index, handles, num_handles, reply_target, timeout_ns));
  239. }
  240. Result ReplyAndReceiveWithUserBuffer64(Core::System& system, int32_t* out_index,
  241. uint64_t message_buffer, uint64_t message_buffer_size,
  242. uint64_t handles, int32_t num_handles, Handle reply_target,
  243. int64_t timeout_ns) {
  244. R_RETURN(ReplyAndReceiveWithUserBuffer(system, out_index, message_buffer, message_buffer_size,
  245. handles, num_handles, reply_target, timeout_ns));
  246. }
  247. Result SendSyncRequest64From32(Core::System& system, Handle session_handle) {
  248. R_RETURN(SendSyncRequest(system, session_handle));
  249. }
  250. Result SendSyncRequestWithUserBuffer64From32(Core::System& system, uint32_t message_buffer,
  251. uint32_t message_buffer_size, Handle session_handle) {
  252. R_RETURN(
  253. SendSyncRequestWithUserBuffer(system, message_buffer, message_buffer_size, session_handle));
  254. }
  255. Result SendAsyncRequestWithUserBuffer64From32(Core::System& system, Handle* out_event_handle,
  256. uint32_t message_buffer, uint32_t message_buffer_size,
  257. Handle session_handle) {
  258. R_RETURN(SendAsyncRequestWithUserBuffer(system, out_event_handle, message_buffer,
  259. message_buffer_size, session_handle));
  260. }
  261. Result ReplyAndReceive64From32(Core::System& system, int32_t* out_index, uint32_t handles,
  262. int32_t num_handles, Handle reply_target, int64_t timeout_ns) {
  263. R_RETURN(ReplyAndReceive(system, out_index, handles, num_handles, reply_target, timeout_ns));
  264. }
  265. Result ReplyAndReceiveWithUserBuffer64From32(Core::System& system, int32_t* out_index,
  266. uint32_t message_buffer, uint32_t message_buffer_size,
  267. uint32_t handles, int32_t num_handles,
  268. Handle reply_target, int64_t timeout_ns) {
  269. R_RETURN(ReplyAndReceiveWithUserBuffer(system, out_index, message_buffer, message_buffer_size,
  270. handles, num_handles, reply_target, timeout_ns));
  271. }
  272. } // namespace Kernel::Svc