server_session.cpp 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. // Copyright 2016 Citra Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <tuple>
  5. #include <utility>
  6. #include "common/assert.h"
  7. #include "common/logging/log.h"
  8. #include "core/core.h"
  9. #include "core/hle/ipc_helpers.h"
  10. #include "core/hle/kernel/client_port.h"
  11. #include "core/hle/kernel/client_session.h"
  12. #include "core/hle/kernel/handle_table.h"
  13. #include "core/hle/kernel/hle_ipc.h"
  14. #include "core/hle/kernel/process.h"
  15. #include "core/hle/kernel/server_session.h"
  16. #include "core/hle/kernel/session.h"
  17. #include "core/hle/kernel/thread.h"
  18. namespace Kernel {
  19. ServerSession::ServerSession() = default;
  20. ServerSession::~ServerSession() {
  21. // This destructor will be called automatically when the last ServerSession handle is closed by
  22. // the emulated application.
  23. // Decrease the port's connection count.
  24. if (parent->port)
  25. parent->port->ConnectionClosed();
  26. // TODO(Subv): Wake up all the ClientSession's waiting threads and set
  27. // the SendSyncRequest result to 0xC920181A.
  28. parent->server = nullptr;
  29. }
  30. ResultVal<SharedPtr<ServerSession>> ServerSession::Create(std::string name) {
  31. SharedPtr<ServerSession> server_session(new ServerSession);
  32. server_session->name = std::move(name);
  33. server_session->parent = nullptr;
  34. return MakeResult(std::move(server_session));
  35. }
  36. bool ServerSession::ShouldWait(Thread* thread) const {
  37. // Closed sessions should never wait, an error will be returned from svcReplyAndReceive.
  38. if (parent->client == nullptr)
  39. return false;
  40. // Wait if we have no pending requests, or if we're currently handling a request.
  41. return pending_requesting_threads.empty() || currently_handling != nullptr;
  42. }
  43. void ServerSession::Acquire(Thread* thread) {
  44. ASSERT_MSG(!ShouldWait(thread), "object unavailable!");
  45. // We are now handling a request, pop it from the stack.
  46. // TODO(Subv): What happens if the client endpoint is closed before any requests are made?
  47. ASSERT(!pending_requesting_threads.empty());
  48. currently_handling = pending_requesting_threads.back();
  49. pending_requesting_threads.pop_back();
  50. }
  51. ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& context) {
  52. auto& domain_message_header = context.GetDomainMessageHeader();
  53. if (domain_message_header) {
  54. // Set domain handlers in HLE context, used for domain objects (IPC interfaces) as inputs
  55. context.SetDomainRequestHandlers(domain_request_handlers);
  56. // If there is a DomainMessageHeader, then this is CommandType "Request"
  57. const u32 object_id{context.GetDomainMessageHeader()->object_id};
  58. switch (domain_message_header->command) {
  59. case IPC::DomainMessageHeader::CommandType::SendMessage:
  60. return domain_request_handlers[object_id - 1]->HandleSyncRequest(context);
  61. case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: {
  62. LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x{:08X}", object_id);
  63. domain_request_handlers[object_id - 1] = nullptr;
  64. IPC::ResponseBuilder rb{context, 2};
  65. rb.Push(RESULT_SUCCESS);
  66. return RESULT_SUCCESS;
  67. }
  68. }
  69. LOG_CRITICAL(IPC, "Unknown domain command={}",
  70. static_cast<int>(domain_message_header->command.Value()));
  71. ASSERT(false);
  72. }
  73. return RESULT_SUCCESS;
  74. }
  75. ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) {
  76. // The ServerSession received a sync request, this means that there's new data available
  77. // from its ClientSession, so wake up any threads that may be waiting on a svcReplyAndReceive or
  78. // similar.
  79. Kernel::HLERequestContext context(this);
  80. u32* cmd_buf = (u32*)Memory::GetPointer(thread->GetTLSAddress());
  81. context.PopulateFromIncomingCommandBuffer(cmd_buf, *Core::CurrentProcess(),
  82. Kernel::g_handle_table);
  83. ResultCode result = RESULT_SUCCESS;
  84. // If the session has been converted to a domain, handle the domain request
  85. if (IsDomain() && context.GetDomainMessageHeader()) {
  86. result = HandleDomainSyncRequest(context);
  87. // If there is no domain header, the regular session handler is used
  88. } else if (hle_handler != nullptr) {
  89. // If this ServerSession has an associated HLE handler, forward the request to it.
  90. result = hle_handler->HandleSyncRequest(context);
  91. }
  92. if (thread->status == ThreadStatus::Running) {
  93. // Put the thread to sleep until the server replies, it will be awoken in
  94. // svcReplyAndReceive for LLE servers.
  95. thread->status = ThreadStatus::WaitIPC;
  96. if (hle_handler != nullptr) {
  97. // For HLE services, we put the request threads to sleep for a short duration to
  98. // simulate IPC overhead, but only if the HLE handler didn't put the thread to sleep for
  99. // other reasons like an async callback. The IPC overhead is needed to prevent
  100. // starvation when a thread only does sync requests to HLE services while a
  101. // lower-priority thread is waiting to run.
  102. // This delay was approximated in a homebrew application by measuring the average time
  103. // it takes for svcSendSyncRequest to return when performing the SetLcdForceBlack IPC
  104. // request to the GSP:GPU service in a n3DS with firmware 11.6. The measured values have
  105. // a high variance and vary between models.
  106. static constexpr u64 IPCDelayNanoseconds = 39000;
  107. thread->WakeAfterDelay(IPCDelayNanoseconds);
  108. } else {
  109. // Add the thread to the list of threads that have issued a sync request with this
  110. // server.
  111. pending_requesting_threads.push_back(std::move(thread));
  112. }
  113. }
  114. // If this ServerSession does not have an HLE implementation, just wake up the threads waiting
  115. // on it.
  116. WakeupAllWaitingThreads();
  117. // Handle scenario when ConvertToDomain command was issued, as we must do the conversion at the
  118. // end of the command such that only commands following this one are handled as domains
  119. if (convert_to_domain) {
  120. ASSERT_MSG(domain_request_handlers.empty(), "already a domain");
  121. domain_request_handlers = {hle_handler};
  122. convert_to_domain = false;
  123. }
  124. return result;
  125. }
  126. ServerSession::SessionPair ServerSession::CreateSessionPair(const std::string& name,
  127. SharedPtr<ClientPort> port) {
  128. auto server_session = ServerSession::Create(name + "_Server").Unwrap();
  129. SharedPtr<ClientSession> client_session(new ClientSession);
  130. client_session->name = name + "_Client";
  131. std::shared_ptr<Session> parent(new Session);
  132. parent->client = client_session.get();
  133. parent->server = server_session.get();
  134. parent->port = std::move(port);
  135. client_session->parent = parent;
  136. server_session->parent = parent;
  137. return std::make_tuple(std::move(server_session), std::move(client_session));
  138. }
  139. } // namespace Kernel