hle_ipc.cpp 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. // Copyright 2018 yuzu emulator team
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <boost/range/algorithm_ext/erase.hpp>
  5. #include "common/assert.h"
  6. #include "common/common_funcs.h"
  7. #include "common/common_types.h"
  8. #include "core/hle/ipc_helpers.h"
  9. #include "core/hle/kernel/domain.h"
  10. #include "core/hle/kernel/handle_table.h"
  11. #include "core/hle/kernel/hle_ipc.h"
  12. #include "core/hle/kernel/kernel.h"
  13. #include "core/hle/kernel/process.h"
  14. #include "core/hle/kernel/server_session.h"
  15. namespace Kernel {
  16. void SessionRequestHandler::ClientConnected(SharedPtr<ServerSession> server_session) {
  17. server_session->SetHleHandler(shared_from_this());
  18. connected_sessions.push_back(server_session);
  19. }
  20. void SessionRequestHandler::ClientDisconnected(SharedPtr<ServerSession> server_session) {
  21. server_session->SetHleHandler(nullptr);
  22. boost::range::remove_erase(connected_sessions, server_session);
  23. }
  24. HLERequestContext::HLERequestContext(SharedPtr<Kernel::Domain> domain) : domain(std::move(domain)) {
  25. cmd_buf[0] = 0;
  26. }
  27. HLERequestContext::HLERequestContext(SharedPtr<Kernel::ServerSession> server_session)
  28. : server_session(std::move(server_session)) {
  29. cmd_buf[0] = 0;
  30. }
  31. HLERequestContext::~HLERequestContext() = default;
  32. void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
  33. IPC::RequestParser rp(src_cmdbuf);
  34. command_header = std::make_unique<IPC::CommandHeader>(rp.PopRaw<IPC::CommandHeader>());
  35. if (command_header->type == IPC::CommandType::Close) {
  36. // Close does not populate the rest of the IPC header
  37. return;
  38. }
  39. // If handle descriptor is present, add size of it
  40. if (command_header->enable_handle_descriptor) {
  41. handle_descriptor_header =
  42. std::make_unique<IPC::HandleDescriptorHeader>(rp.PopRaw<IPC::HandleDescriptorHeader>());
  43. if (handle_descriptor_header->send_current_pid) {
  44. rp.Skip(2, false);
  45. }
  46. if (incoming) {
  47. // Populate the object lists with the data in the IPC request.
  48. for (u32 handle = 0; handle < handle_descriptor_header->num_handles_to_copy; ++handle) {
  49. copy_objects.push_back(Kernel::g_handle_table.GetGeneric(rp.Pop<Handle>()));
  50. }
  51. for (u32 handle = 0; handle < handle_descriptor_header->num_handles_to_move; ++handle) {
  52. move_objects.push_back(Kernel::g_handle_table.GetGeneric(rp.Pop<Handle>()));
  53. }
  54. } else {
  55. // For responses we just ignore the handles, they're empty and will be populated when
  56. // translating the response.
  57. rp.Skip(handle_descriptor_header->num_handles_to_copy, false);
  58. rp.Skip(handle_descriptor_header->num_handles_to_move, false);
  59. }
  60. }
  61. for (unsigned i = 0; i < command_header->num_buf_x_descriptors; ++i) {
  62. buffer_x_desciptors.push_back(rp.PopRaw<IPC::BufferDescriptorX>());
  63. }
  64. for (unsigned i = 0; i < command_header->num_buf_a_descriptors; ++i) {
  65. buffer_a_desciptors.push_back(rp.PopRaw<IPC::BufferDescriptorABW>());
  66. }
  67. for (unsigned i = 0; i < command_header->num_buf_b_descriptors; ++i) {
  68. buffer_b_desciptors.push_back(rp.PopRaw<IPC::BufferDescriptorABW>());
  69. }
  70. for (unsigned i = 0; i < command_header->num_buf_w_descriptors; ++i) {
  71. buffer_w_desciptors.push_back(rp.PopRaw<IPC::BufferDescriptorABW>());
  72. }
  73. buffer_c_offset = rp.GetCurrentOffset() + command_header->data_size;
  74. // Padding to align to 16 bytes
  75. rp.AlignWithPadding();
  76. if (IsDomain() && (command_header->type == IPC::CommandType::Request || !incoming)) {
  77. // If this is an incoming message, only CommandType "Request" has a domain header
  78. // All outgoing domain messages have the domain header
  79. domain_message_header =
  80. std::make_unique<IPC::DomainMessageHeader>(rp.PopRaw<IPC::DomainMessageHeader>());
  81. }
  82. data_payload_header =
  83. std::make_unique<IPC::DataPayloadHeader>(rp.PopRaw<IPC::DataPayloadHeader>());
  84. data_payload_offset = rp.GetCurrentOffset();
  85. if (domain_message_header && domain_message_header->command ==
  86. IPC::DomainMessageHeader::CommandType::CloseVirtualHandle) {
  87. // CloseVirtualHandle command does not have SFC* or any data
  88. return;
  89. }
  90. if (incoming) {
  91. ASSERT(data_payload_header->magic == Common::MakeMagic('S', 'F', 'C', 'I'));
  92. } else {
  93. ASSERT(data_payload_header->magic == Common::MakeMagic('S', 'F', 'C', 'O'));
  94. }
  95. rp.SetCurrentOffset(buffer_c_offset);
  96. // For Inline buffers, the response data is written directly to buffer_c_offset
  97. // and in this case we don't have any BufferDescriptorC on the request.
  98. if (command_header->buf_c_descriptor_flags >
  99. IPC::CommandHeader::BufferDescriptorCFlag::InlineDescriptor) {
  100. if (command_header->buf_c_descriptor_flags ==
  101. IPC::CommandHeader::BufferDescriptorCFlag::OneDescriptor) {
  102. buffer_c_desciptors.push_back(rp.PopRaw<IPC::BufferDescriptorC>());
  103. } else {
  104. unsigned num_buf_c_descriptors =
  105. static_cast<unsigned>(command_header->buf_c_descriptor_flags.Value()) - 2;
  106. // This is used to detect possible underflows, in case something is broken
  107. // with the two ifs above and the flags value is == 0 || == 1.
  108. ASSERT(num_buf_c_descriptors < 14);
  109. for (unsigned i = 0; i < num_buf_c_descriptors; ++i) {
  110. buffer_c_desciptors.push_back(rp.PopRaw<IPC::BufferDescriptorC>());
  111. }
  112. }
  113. }
  114. rp.SetCurrentOffset(data_payload_offset);
  115. command = rp.Pop<u32_le>();
  116. rp.Skip(1, false); // The command is actually an u64, but we don't use the high part.
  117. }
  118. ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf,
  119. Process& src_process,
  120. HandleTable& src_table) {
  121. ParseCommandBuffer(src_cmdbuf, true);
  122. if (command_header->type == IPC::CommandType::Close) {
  123. // Close does not populate the rest of the IPC header
  124. return RESULT_SUCCESS;
  125. }
  126. // The data_size already includes the payload header, the padding and the domain header.
  127. size_t size = data_payload_offset + command_header->data_size -
  128. sizeof(IPC::DataPayloadHeader) / sizeof(u32) - 4;
  129. if (domain_message_header)
  130. size -= sizeof(IPC::DomainMessageHeader) / sizeof(u32);
  131. std::copy_n(src_cmdbuf, size, cmd_buf.begin());
  132. return RESULT_SUCCESS;
  133. }
  134. ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, Process& dst_process,
  135. HandleTable& dst_table) {
  136. // The header was already built in the internal command buffer. Attempt to parse it to verify
  137. // the integrity and then copy it over to the target command buffer.
  138. ParseCommandBuffer(cmd_buf.data(), false);
  139. // The data_size already includes the payload header, the padding and the domain header.
  140. size_t size = data_payload_offset + command_header->data_size -
  141. sizeof(IPC::DataPayloadHeader) / sizeof(u32) - 4;
  142. if (domain_message_header)
  143. size -= sizeof(IPC::DomainMessageHeader) / sizeof(u32);
  144. std::copy_n(cmd_buf.begin(), size, dst_cmdbuf);
  145. if (command_header->enable_handle_descriptor) {
  146. ASSERT_MSG(!move_objects.empty() || !copy_objects.empty(),
  147. "Handle descriptor bit set but no handles to translate");
  148. // We write the translated handles at a specific offset in the command buffer, this space
  149. // was already reserved when writing the header.
  150. size_t current_offset =
  151. (sizeof(IPC::CommandHeader) + sizeof(IPC::HandleDescriptorHeader)) / sizeof(u32);
  152. ASSERT_MSG(!handle_descriptor_header->send_current_pid, "Sending PID is not implemented");
  153. ASSERT_MSG(copy_objects.size() == handle_descriptor_header->num_handles_to_copy);
  154. ASSERT_MSG(move_objects.size() == handle_descriptor_header->num_handles_to_move);
  155. // We don't make a distinction between copy and move handles when translating since HLE
  156. // services don't deal with handles directly. However, the guest applications might check
  157. // for specific values in each of these descriptors.
  158. for (auto& object : copy_objects) {
  159. ASSERT(object != nullptr);
  160. dst_cmdbuf[current_offset++] = Kernel::g_handle_table.Create(object).Unwrap();
  161. }
  162. for (auto& object : move_objects) {
  163. ASSERT(object != nullptr);
  164. dst_cmdbuf[current_offset++] = Kernel::g_handle_table.Create(object).Unwrap();
  165. }
  166. }
  167. // TODO(Subv): Translate the X/A/B/W buffers.
  168. if (IsDomain()) {
  169. ASSERT(domain_message_header->num_objects == domain_objects.size());
  170. // Write the domain objects to the command buffer, these go after the raw untranslated data.
  171. // TODO(Subv): This completely ignores C buffers.
  172. size_t domain_offset = size - domain_message_header->num_objects;
  173. auto& request_handlers = domain->request_handlers;
  174. for (auto& object : domain_objects) {
  175. request_handlers.emplace_back(object);
  176. dst_cmdbuf[domain_offset++] = static_cast<u32_le>(request_handlers.size());
  177. }
  178. }
  179. return RESULT_SUCCESS;
  180. }
  181. } // namespace Kernel