k_server_session.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  1. // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include <tuple>
  4. #include <utility>
  5. #include "common/assert.h"
  6. #include "common/common_types.h"
  7. #include "common/logging/log.h"
  8. #include "common/scope_exit.h"
  9. #include "core/core.h"
  10. #include "core/core_timing.h"
  11. #include "core/hle/ipc_helpers.h"
  12. #include "core/hle/kernel/hle_ipc.h"
  13. #include "core/hle/kernel/k_client_port.h"
  14. #include "core/hle/kernel/k_handle_table.h"
  15. #include "core/hle/kernel/k_process.h"
  16. #include "core/hle/kernel/k_scheduler.h"
  17. #include "core/hle/kernel/k_server_port.h"
  18. #include "core/hle/kernel/k_server_session.h"
  19. #include "core/hle/kernel/k_session.h"
  20. #include "core/hle/kernel/k_thread.h"
  21. #include "core/hle/kernel/k_thread_queue.h"
  22. #include "core/hle/kernel/kernel.h"
  23. #include "core/memory.h"
  24. namespace Kernel {
  25. using ThreadQueueImplForKServerSessionRequest = KThreadQueue;
  26. KServerSession::KServerSession(KernelCore& kernel_)
  27. : KSynchronizationObject{kernel_}, m_lock{kernel_} {}
  28. KServerSession::~KServerSession() = default;
  29. void KServerSession::Initialize(KSession* parent_session_, std::string&& name_) {
  30. // Set member variables.
  31. parent = parent_session_;
  32. name = std::move(name_);
  33. }
  34. void KServerSession::Destroy() {
  35. parent->OnServerClosed();
  36. this->CleanupRequests();
  37. parent->Close();
  38. }
  39. void KServerSession::OnClientClosed() {
  40. KScopedLightLock lk{m_lock};
  41. // Handle any pending requests.
  42. KSessionRequest* prev_request = nullptr;
  43. while (true) {
  44. // Declare variables for processing the request.
  45. KSessionRequest* request = nullptr;
  46. KEvent* event = nullptr;
  47. KThread* thread = nullptr;
  48. bool cur_request = false;
  49. bool terminate = false;
  50. // Get the next request.
  51. {
  52. KScopedSchedulerLock sl{kernel};
  53. if (m_current_request != nullptr && m_current_request != prev_request) {
  54. // Set the request, open a reference as we process it.
  55. request = m_current_request;
  56. request->Open();
  57. cur_request = true;
  58. // Get thread and event for the request.
  59. thread = request->GetThread();
  60. event = request->GetEvent();
  61. // If the thread is terminating, handle that.
  62. if (thread->IsTerminationRequested()) {
  63. request->ClearThread();
  64. request->ClearEvent();
  65. terminate = true;
  66. }
  67. prev_request = request;
  68. } else if (!m_request_list.empty()) {
  69. // Pop the request from the front of the list.
  70. request = std::addressof(m_request_list.front());
  71. m_request_list.pop_front();
  72. // Get thread and event for the request.
  73. thread = request->GetThread();
  74. event = request->GetEvent();
  75. }
  76. }
  77. // If there are no requests, we're done.
  78. if (request == nullptr) {
  79. break;
  80. }
  81. // All requests must have threads.
  82. ASSERT(thread != nullptr);
  83. // Ensure that we close the request when done.
  84. SCOPE_EXIT({ request->Close(); });
  85. // If we're terminating, close a reference to the thread and event.
  86. if (terminate) {
  87. thread->Close();
  88. if (event != nullptr) {
  89. event->Close();
  90. }
  91. }
  92. // If we need to, reply.
  93. if (event != nullptr && !cur_request) {
  94. // There must be no mappings.
  95. ASSERT(request->GetSendCount() == 0);
  96. ASSERT(request->GetReceiveCount() == 0);
  97. ASSERT(request->GetExchangeCount() == 0);
  98. // // Get the process and page table.
  99. // KProcess *client_process = thread->GetOwnerProcess();
  100. // auto &client_pt = client_process->GetPageTable();
  101. // // Reply to the request.
  102. // ReplyAsyncError(client_process, request->GetAddress(), request->GetSize(),
  103. // ResultSessionClosed);
  104. // // Unlock the buffer.
  105. // // NOTE: Nintendo does not check the result of this.
  106. // client_pt.UnlockForIpcUserBuffer(request->GetAddress(), request->GetSize());
  107. // Signal the event.
  108. event->Signal();
  109. }
  110. }
  111. // Notify.
  112. this->NotifyAvailable(ResultSessionClosed);
  113. }
  114. bool KServerSession::IsSignaled() const {
  115. ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel));
  116. // If the client is closed, we're always signaled.
  117. if (parent->IsClientClosed()) {
  118. return true;
  119. }
  120. // Otherwise, we're signaled if we have a request and aren't handling one.
  121. return !m_request_list.empty() && m_current_request == nullptr;
  122. }
  123. Result KServerSession::OnRequest(KSessionRequest* request) {
  124. // Create the wait queue.
  125. ThreadQueueImplForKServerSessionRequest wait_queue{kernel};
  126. {
  127. // Lock the scheduler.
  128. KScopedSchedulerLock sl{kernel};
  129. // Ensure that we can handle new requests.
  130. R_UNLESS(!parent->IsServerClosed(), ResultSessionClosed);
  131. // Check that we're not terminating.
  132. R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(), ResultTerminationRequested);
  133. // Get whether we're empty.
  134. const bool was_empty = m_request_list.empty();
  135. // Add the request to the list.
  136. request->Open();
  137. m_request_list.push_back(*request);
  138. // If we were empty, signal.
  139. if (was_empty) {
  140. this->NotifyAvailable();
  141. }
  142. // If we have a request event, this is asynchronous, and we don't need to wait.
  143. R_SUCCEED_IF(request->GetEvent() != nullptr);
  144. // This is a synchronous request, so we should wait for our request to complete.
  145. GetCurrentThread(kernel).SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC);
  146. GetCurrentThread(kernel).BeginWait(&wait_queue);
  147. }
  148. return GetCurrentThread(kernel).GetWaitResult();
  149. }
  150. Result KServerSession::SendReply(bool is_hle) {
  151. // Lock the session.
  152. KScopedLightLock lk{m_lock};
  153. // Get the request.
  154. KSessionRequest* request;
  155. {
  156. KScopedSchedulerLock sl{kernel};
  157. // Get the current request.
  158. request = m_current_request;
  159. R_UNLESS(request != nullptr, ResultInvalidState);
  160. // Clear the current request, since we're processing it.
  161. m_current_request = nullptr;
  162. if (!m_request_list.empty()) {
  163. this->NotifyAvailable();
  164. }
  165. }
  166. // Close reference to the request once we're done processing it.
  167. SCOPE_EXIT({ request->Close(); });
  168. // Extract relevant information from the request.
  169. const uintptr_t client_message = request->GetAddress();
  170. const size_t client_buffer_size = request->GetSize();
  171. KThread* client_thread = request->GetThread();
  172. KEvent* event = request->GetEvent();
  173. // Check whether we're closed.
  174. const bool closed = (client_thread == nullptr || parent->IsClientClosed());
  175. Result result = ResultSuccess;
  176. if (!closed) {
  177. // If we're not closed, send the reply.
  178. if (is_hle) {
  179. // HLE servers write directly to a pointer to the thread command buffer. Therefore
  180. // the reply has already been written in this case.
  181. } else {
  182. Core::Memory::Memory& memory{kernel.System().Memory()};
  183. KThread* server_thread{GetCurrentThreadPointer(kernel)};
  184. UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess());
  185. auto* src_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress());
  186. auto* dst_msg_buffer = memory.GetPointer(client_message);
  187. std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size);
  188. }
  189. } else {
  190. result = ResultSessionClosed;
  191. }
  192. // Select a result for the client.
  193. Result client_result = result;
  194. if (closed && R_SUCCEEDED(result)) {
  195. result = ResultSessionClosed;
  196. client_result = ResultSessionClosed;
  197. } else {
  198. result = ResultSuccess;
  199. }
  200. // If there's a client thread, update it.
  201. if (client_thread != nullptr) {
  202. if (event != nullptr) {
  203. // // Get the client process/page table.
  204. // KProcess *client_process = client_thread->GetOwnerProcess();
  205. // KPageTable *client_page_table = &client_process->PageTable();
  206. // // If we need to, reply with an async error.
  207. // if (R_FAILED(client_result)) {
  208. // ReplyAsyncError(client_process, client_message, client_buffer_size,
  209. // client_result);
  210. // }
  211. // // Unlock the client buffer.
  212. // // NOTE: Nintendo does not check the result of this.
  213. // client_page_table->UnlockForIpcUserBuffer(client_message, client_buffer_size);
  214. // Signal the event.
  215. event->Signal();
  216. } else {
  217. // End the client thread's wait.
  218. KScopedSchedulerLock sl{kernel};
  219. if (!client_thread->IsTerminationRequested()) {
  220. client_thread->EndWait(client_result);
  221. }
  222. }
  223. }
  224. return result;
  225. }
  226. Result KServerSession::ReceiveRequest(std::shared_ptr<HLERequestContext>* out_context,
  227. std::weak_ptr<SessionRequestManager> manager) {
  228. // Lock the session.
  229. KScopedLightLock lk{m_lock};
  230. // Get the request and client thread.
  231. KSessionRequest* request;
  232. KThread* client_thread;
  233. {
  234. KScopedSchedulerLock sl{kernel};
  235. // Ensure that we can service the request.
  236. R_UNLESS(!parent->IsClientClosed(), ResultSessionClosed);
  237. // Ensure we aren't already servicing a request.
  238. R_UNLESS(m_current_request == nullptr, ResultNotFound);
  239. // Ensure we have a request to service.
  240. R_UNLESS(!m_request_list.empty(), ResultNotFound);
  241. // Pop the first request from the list.
  242. request = &m_request_list.front();
  243. m_request_list.pop_front();
  244. // Get the thread for the request.
  245. client_thread = request->GetThread();
  246. R_UNLESS(client_thread != nullptr, ResultSessionClosed);
  247. // Open the client thread.
  248. client_thread->Open();
  249. }
  250. SCOPE_EXIT({ client_thread->Close(); });
  251. // Set the request as our current.
  252. m_current_request = request;
  253. // Get the client address.
  254. uintptr_t client_message = request->GetAddress();
  255. size_t client_buffer_size = request->GetSize();
  256. // bool recv_list_broken = false;
  257. // Receive the message.
  258. Core::Memory::Memory& memory{kernel.System().Memory()};
  259. if (out_context != nullptr) {
  260. // HLE request.
  261. u32* cmd_buf{reinterpret_cast<u32*>(memory.GetPointer(client_message))};
  262. *out_context = std::make_shared<HLERequestContext>(kernel, memory, this, client_thread);
  263. (*out_context)->SetSessionRequestManager(manager);
  264. (*out_context)
  265. ->PopulateFromIncomingCommandBuffer(client_thread->GetOwnerProcess()->GetHandleTable(),
  266. cmd_buf);
  267. } else {
  268. KThread* server_thread{GetCurrentThreadPointer(kernel)};
  269. UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess());
  270. auto* src_msg_buffer = memory.GetPointer(client_message);
  271. auto* dst_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress());
  272. std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size);
  273. }
  274. // We succeeded.
  275. return ResultSuccess;
  276. }
  277. void KServerSession::CleanupRequests() {
  278. KScopedLightLock lk(m_lock);
  279. // Clean up any pending requests.
  280. while (true) {
  281. // Get the next request.
  282. KSessionRequest* request = nullptr;
  283. {
  284. KScopedSchedulerLock sl{kernel};
  285. if (m_current_request) {
  286. // Choose the current request if we have one.
  287. request = m_current_request;
  288. m_current_request = nullptr;
  289. } else if (!m_request_list.empty()) {
  290. // Pop the request from the front of the list.
  291. request = &m_request_list.front();
  292. m_request_list.pop_front();
  293. }
  294. }
  295. // If there's no request, we're done.
  296. if (request == nullptr) {
  297. break;
  298. }
  299. // Close a reference to the request once it's cleaned up.
  300. SCOPE_EXIT({ request->Close(); });
  301. // Extract relevant information from the request.
  302. // const uintptr_t client_message = request->GetAddress();
  303. // const size_t client_buffer_size = request->GetSize();
  304. KThread* client_thread = request->GetThread();
  305. KEvent* event = request->GetEvent();
  306. // KProcess *server_process = request->GetServerProcess();
  307. // KProcess *client_process = (client_thread != nullptr) ?
  308. // client_thread->GetOwnerProcess() : nullptr;
  309. // KProcessPageTable *client_page_table = (client_process != nullptr) ?
  310. // &client_process->GetPageTable() : nullptr;
  311. // Cleanup the mappings.
  312. // Result result = CleanupMap(request, server_process, client_page_table);
  313. // If there's a client thread, update it.
  314. if (client_thread != nullptr) {
  315. if (event != nullptr) {
  316. // // We need to reply async.
  317. // ReplyAsyncError(client_process, client_message, client_buffer_size,
  318. // (R_SUCCEEDED(result) ? ResultSessionClosed : result));
  319. // // Unlock the client buffer.
  320. // NOTE: Nintendo does not check the result of this.
  321. // client_page_table->UnlockForIpcUserBuffer(client_message, client_buffer_size);
  322. // Signal the event.
  323. event->Signal();
  324. } else {
  325. // End the client thread's wait.
  326. KScopedSchedulerLock sl{kernel};
  327. if (!client_thread->IsTerminationRequested()) {
  328. client_thread->EndWait(ResultSessionClosed);
  329. }
  330. }
  331. }
  332. }
  333. }
  334. } // namespace Kernel