diff options
| -rw-r--r-- | src/core/hle/kernel/hle_ipc.cpp | 94 | ||||
| -rw-r--r-- | src/core/hle/kernel/hle_ipc.h | 37 | ||||
| -rw-r--r-- | src/core/hle/service/service.cpp | 17 |
3 files changed, 130 insertions, 18 deletions
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index 5a74645c7..656405dd6 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | #include "core/hle/kernel/handle_table.h" | 8 | #include "core/hle/kernel/handle_table.h" |
| 9 | #include "core/hle/kernel/hle_ipc.h" | 9 | #include "core/hle/kernel/hle_ipc.h" |
| 10 | #include "core/hle/kernel/kernel.h" | 10 | #include "core/hle/kernel/kernel.h" |
| 11 | #include "core/hle/kernel/process.h" | ||
| 11 | #include "core/hle/kernel/server_session.h" | 12 | #include "core/hle/kernel/server_session.h" |
| 12 | 13 | ||
| 13 | namespace Kernel { | 14 | namespace Kernel { |
| @@ -24,12 +25,97 @@ void SessionRequestHandler::ClientDisconnected(SharedPtr<ServerSession> server_s | |||
| 24 | 25 | ||
| 25 | HLERequestContext::~HLERequestContext() = default; | 26 | HLERequestContext::~HLERequestContext() = default; |
| 26 | 27 | ||
| 27 | SharedPtr<Object> HLERequestContext::GetIncomingHandle(Handle id_from_cmdbuf) const { | 28 | SharedPtr<Object> HLERequestContext::GetIncomingHandle(u32 id_from_cmdbuf) const { |
| 28 | return Kernel::g_handle_table.GetGeneric(id_from_cmdbuf); | 29 | ASSERT(id_from_cmdbuf < request_handles.size()); |
| 30 | return request_handles[id_from_cmdbuf]; | ||
| 29 | } | 31 | } |
| 30 | 32 | ||
| 31 | Handle HLERequestContext::AddOutgoingHandle(SharedPtr<Object> object) { | 33 | u32 HLERequestContext::AddOutgoingHandle(SharedPtr<Object> object) { |
| 32 | return Kernel::g_handle_table.Create(object).Unwrap(); | 34 | request_handles.push_back(std::move(object)); |
| 35 | return request_handles.size() - 1; | ||
| 36 | } | ||
| 37 | |||
| 38 | ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const u32_le* src_cmdbuf, | ||
| 39 | Process& src_process, | ||
| 40 | HandleTable& src_table) { | ||
| 41 | IPC::Header header{src_cmdbuf[0]}; | ||
| 42 | |||
| 43 | size_t untranslated_size = 1u + header.normal_params_size; | ||
| 44 | size_t command_size = untranslated_size + header.translate_params_size; | ||
| 45 | ASSERT(command_size <= IPC::COMMAND_BUFFER_LENGTH); // TODO(yuriks): Return error | ||
| 46 | |||
| 47 | std::copy_n(src_cmdbuf, untranslated_size, cmd_buf.begin()); | ||
| 48 | |||
| 49 | size_t i = untranslated_size; | ||
| 50 | while (i < command_size) { | ||
| 51 | u32 descriptor = cmd_buf[i] = src_cmdbuf[i]; | ||
| 52 | i += 1; | ||
| 53 | |||
| 54 | switch (IPC::GetDescriptorType(descriptor)) { | ||
| 55 | case IPC::DescriptorType::CopyHandle: | ||
| 56 | case IPC::DescriptorType::MoveHandle: { | ||
| 57 | u32 num_handles = IPC::HandleNumberFromDesc(descriptor); | ||
| 58 | ASSERT(i + num_handles <= command_size); // TODO(yuriks): Return error | ||
| 59 | for (u32 j = 0; j < num_handles; ++j) { | ||
| 60 | Handle handle = src_cmdbuf[i]; | ||
| 61 | SharedPtr<Object> object = src_table.GetGeneric(handle); | ||
| 62 | ASSERT(object != nullptr); // TODO(yuriks): Return error | ||
| 63 | if (descriptor == IPC::DescriptorType::MoveHandle) { | ||
| 64 | src_table.Close(handle); | ||
| 65 | } | ||
| 66 | |||
| 67 | cmd_buf[i++] = AddOutgoingHandle(std::move(object)); | ||
| 68 | } | ||
| 69 | break; | ||
| 70 | } | ||
| 71 | case IPC::DescriptorType::CallingPid: { | ||
| 72 | cmd_buf[i++] = src_process.process_id; | ||
| 73 | break; | ||
| 74 | } | ||
| 75 | default: | ||
| 76 | UNIMPLEMENTED_MSG("Unsupported handle translation: 0x%08X", descriptor); | ||
| 77 | } | ||
| 78 | } | ||
| 79 | |||
| 80 | return RESULT_SUCCESS; | ||
| 81 | } | ||
| 82 | |||
| 83 | ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, Process& dst_process, | ||
| 84 | HandleTable& dst_table) const { | ||
| 85 | IPC::Header header{cmd_buf[0]}; | ||
| 86 | |||
| 87 | size_t untranslated_size = 1u + header.normal_params_size; | ||
| 88 | size_t command_size = untranslated_size + header.translate_params_size; | ||
| 89 | ASSERT(command_size <= IPC::COMMAND_BUFFER_LENGTH); | ||
| 90 | |||
| 91 | std::copy_n(cmd_buf.begin(), untranslated_size, dst_cmdbuf); | ||
| 92 | |||
| 93 | size_t i = untranslated_size; | ||
| 94 | while (i < command_size) { | ||
| 95 | u32 descriptor = dst_cmdbuf[i] = cmd_buf[i]; | ||
| 96 | i += 1; | ||
| 97 | |||
| 98 | switch (IPC::GetDescriptorType(descriptor)) { | ||
| 99 | case IPC::DescriptorType::CopyHandle: | ||
| 100 | case IPC::DescriptorType::MoveHandle: { | ||
| 101 | // HLE services don't use handles, so we treat both CopyHandle and MoveHandle equally | ||
| 102 | u32 num_handles = IPC::HandleNumberFromDesc(descriptor); | ||
| 103 | ASSERT(i + num_handles <= command_size); | ||
| 104 | for (u32 j = 0; j < num_handles; ++j) { | ||
| 105 | SharedPtr<Object> object = GetIncomingHandle(cmd_buf[i]); | ||
| 106 | |||
| 107 | // TODO(yuriks): Figure out the proper error handling for if this fails | ||
| 108 | Handle handle = dst_table.Create(object).Unwrap(); | ||
| 109 | dst_cmdbuf[i++] = handle; | ||
| 110 | } | ||
| 111 | break; | ||
| 112 | } | ||
| 113 | default: | ||
| 114 | UNIMPLEMENTED_MSG("Unsupported handle translation: 0x%08X", descriptor); | ||
| 115 | } | ||
| 116 | } | ||
| 117 | |||
| 118 | return RESULT_SUCCESS; | ||
| 33 | } | 119 | } |
| 34 | 120 | ||
| 35 | } // namespace Kernel | 121 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index f23daa7ea..d6ebf113c 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | #include <memory> | 8 | #include <memory> |
| 9 | #include <vector> | 9 | #include <vector> |
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | #include "common/swap.h" | ||
| 11 | #include "core/hle/ipc.h" | 12 | #include "core/hle/ipc.h" |
| 12 | #include "core/hle/kernel/kernel.h" | 13 | #include "core/hle/kernel/kernel.h" |
| 13 | #include "core/hle/kernel/server_session.h" | 14 | #include "core/hle/kernel/server_session.h" |
| @@ -18,6 +19,9 @@ class ServiceFrameworkBase; | |||
| 18 | 19 | ||
| 19 | namespace Kernel { | 20 | namespace Kernel { |
| 20 | 21 | ||
| 22 | class HandleTable; | ||
| 23 | class Process; | ||
| 24 | |||
| 21 | /** | 25 | /** |
| 22 | * Interface implemented by HLE Session handlers. | 26 | * Interface implemented by HLE Session handlers. |
| 23 | * This can be provided to a ServerSession in order to hook into several relevant events | 27 | * This can be provided to a ServerSession in order to hook into several relevant events |
| @@ -62,6 +66,20 @@ protected: | |||
| 62 | * Class containing information about an in-flight IPC request being handled by an HLE service | 66 | * Class containing information about an in-flight IPC request being handled by an HLE service |
| 63 | * implementation. Services should avoid using old global APIs (e.g. Kernel::GetCommandBuffer()) and | 67 | * implementation. Services should avoid using old global APIs (e.g. Kernel::GetCommandBuffer()) and |
| 64 | * when possible use the APIs in this class to service the request. | 68 | * when possible use the APIs in this class to service the request. |
| 69 | * | ||
| 70 | * HLE handle protocol | ||
| 71 | * =================== | ||
| 72 | * | ||
| 73 | * To avoid needing HLE services to keep a separate handle table, or having to directly modify the | ||
| 74 | * requester's table, a tweaked protocol is used to receive and send handles in requests. The kernel | ||
| 75 | * will decode the incoming handles into object pointers and insert a id in the buffer where the | ||
| 76 | * handle would normally be. The service then calls GetIncomingHandle() with that id to get the | ||
| 77 | * pointer to the object. Similarly, instead of inserting a handle into the command buffer, the | ||
| 78 | * service calls AddOutgoingHandle() and stores the returned id where the handle would normally go. | ||
| 79 | * | ||
| 80 | * The end result is similar to just giving services their own real handle tables, but since these | ||
| 81 | * ids are local to a specific context, it avoids requiring services to manage handles for objects | ||
| 82 | * across multiple calls and ensuring that unneeded handles are cleaned up. | ||
| 65 | */ | 83 | */ |
| 66 | class HLERequestContext { | 84 | class HLERequestContext { |
| 67 | public: | 85 | public: |
| @@ -80,14 +98,29 @@ public: | |||
| 80 | return session; | 98 | return session; |
| 81 | } | 99 | } |
| 82 | 100 | ||
| 83 | SharedPtr<Object> GetIncomingHandle(Handle id_from_cmdbuf) const; | 101 | /** |
| 84 | Handle AddOutgoingHandle(SharedPtr<Object> object); | 102 | * Resolves a object id from the request command buffer into a pointer to an object. See the |
| 103 | * "HLE handle protocol" section in the class documentation for more details. | ||
| 104 | */ | ||
| 105 | SharedPtr<Object> GetIncomingHandle(u32 id_from_cmdbuf) const; | ||
| 106 | |||
| 107 | /** | ||
| 108 | * Adds an outgoing object to the response, returning the id which should be used to reference | ||
| 109 | * it. See the "HLE handle protocol" section in the class documentation for more details. | ||
| 110 | */ | ||
| 111 | u32 AddOutgoingHandle(SharedPtr<Object> object); | ||
| 85 | 112 | ||
| 86 | private: | 113 | private: |
| 87 | friend class Service::ServiceFrameworkBase; | 114 | friend class Service::ServiceFrameworkBase; |
| 88 | 115 | ||
| 116 | ResultCode PopulateFromIncomingCommandBuffer(const u32_le* src_cmdbuf, Process& src_process, | ||
| 117 | HandleTable& src_table); | ||
| 118 | ResultCode WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, Process& dst_process, | ||
| 119 | HandleTable& dst_table) const; | ||
| 120 | |||
| 89 | std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf; | 121 | std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf; |
| 90 | SharedPtr<ServerSession> session; | 122 | SharedPtr<ServerSession> session; |
| 123 | std::vector<SharedPtr<Object>> request_handles; | ||
| 91 | }; | 124 | }; |
| 92 | 125 | ||
| 93 | } // namespace Kernel | 126 | } // namespace Kernel |
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 35582b0ff..791a65c19 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include "common/string_util.h" | 9 | #include "common/string_util.h" |
| 10 | #include "core/hle/ipc.h" | 10 | #include "core/hle/ipc.h" |
| 11 | #include "core/hle/kernel/client_port.h" | 11 | #include "core/hle/kernel/client_port.h" |
| 12 | #include "core/hle/kernel/process.h" | ||
| 12 | #include "core/hle/kernel/server_port.h" | 13 | #include "core/hle/kernel/server_port.h" |
| 13 | #include "core/hle/kernel/server_session.h" | 14 | #include "core/hle/kernel/server_session.h" |
| 14 | #include "core/hle/service/ac/ac.h" | 15 | #include "core/hle/service/ac/ac.h" |
| @@ -172,24 +173,16 @@ void ServiceFrameworkBase::HandleSyncRequest(SharedPtr<ServerSession> server_ses | |||
| 172 | 173 | ||
| 173 | // TODO(yuriks): The kernel should be the one handling this as part of translation after | 174 | // TODO(yuriks): The kernel should be the one handling this as part of translation after |
| 174 | // everything else is migrated | 175 | // everything else is migrated |
| 175 | IPC::Header request_header{cmd_buf[0]}; | ||
| 176 | size_t request_size = | ||
| 177 | 1 + request_header.normal_params_size + request_header.translate_params_size; | ||
| 178 | ASSERT(request_size <= IPC::COMMAND_BUFFER_LENGTH); // TODO(yuriks): Return error | ||
| 179 | |||
| 180 | Kernel::HLERequestContext context; | 176 | Kernel::HLERequestContext context; |
| 181 | std::copy_n(cmd_buf, request_size, context.cmd_buf.begin()); | ||
| 182 | context.session = std::move(server_session); | 177 | context.session = std::move(server_session); |
| 178 | context.PopulateFromIncomingCommandBuffer(cmd_buf, *Kernel::g_current_process, | ||
| 179 | Kernel::g_handle_table); | ||
| 183 | 180 | ||
| 184 | LOG_TRACE(Service, "%s", | 181 | LOG_TRACE(Service, "%s", |
| 185 | MakeFunctionString(info->name, GetServiceName().c_str(), cmd_buf).c_str()); | 182 | MakeFunctionString(info->name, GetServiceName().c_str(), cmd_buf).c_str()); |
| 186 | handler_invoker(this, info->handler_callback, context); | 183 | handler_invoker(this, info->handler_callback, context); |
| 187 | 184 | context.WriteToOutgoingCommandBuffer(cmd_buf, *Kernel::g_current_process, | |
| 188 | IPC::Header response_header{context.cmd_buf[0]}; | 185 | Kernel::g_handle_table); |
| 189 | size_t response_size = | ||
| 190 | 1 + response_header.normal_params_size + response_header.translate_params_size; | ||
| 191 | ASSERT(response_size <= IPC::COMMAND_BUFFER_LENGTH); | ||
| 192 | std::copy_n(context.cmd_buf.begin(), response_size, cmd_buf); | ||
| 193 | } | 186 | } |
| 194 | 187 | ||
| 195 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 188 | //////////////////////////////////////////////////////////////////////////////////////////////////// |