Kernel: Basic support for IPC translation for HLE services
This commit is contained in:
		| @@ -8,6 +8,7 @@ | ||||
| #include "core/hle/kernel/handle_table.h" | ||||
| #include "core/hle/kernel/hle_ipc.h" | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| #include "core/hle/kernel/process.h" | ||||
| #include "core/hle/kernel/server_session.h" | ||||
|  | ||||
| namespace Kernel { | ||||
| @@ -24,12 +25,97 @@ void SessionRequestHandler::ClientDisconnected(SharedPtr<ServerSession> server_s | ||||
|  | ||||
| HLERequestContext::~HLERequestContext() = default; | ||||
|  | ||||
| SharedPtr<Object> HLERequestContext::GetIncomingHandle(Handle id_from_cmdbuf) const { | ||||
|     return Kernel::g_handle_table.GetGeneric(id_from_cmdbuf); | ||||
| SharedPtr<Object> HLERequestContext::GetIncomingHandle(u32 id_from_cmdbuf) const { | ||||
|     ASSERT(id_from_cmdbuf < request_handles.size()); | ||||
|     return request_handles[id_from_cmdbuf]; | ||||
| } | ||||
|  | ||||
| Handle HLERequestContext::AddOutgoingHandle(SharedPtr<Object> object) { | ||||
|     return Kernel::g_handle_table.Create(object).Unwrap(); | ||||
| u32 HLERequestContext::AddOutgoingHandle(SharedPtr<Object> object) { | ||||
|     request_handles.push_back(std::move(object)); | ||||
|     return request_handles.size() - 1; | ||||
| } | ||||
|  | ||||
| ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const u32_le* src_cmdbuf, | ||||
|                                                                 Process& src_process, | ||||
|                                                                 HandleTable& src_table) { | ||||
|     IPC::Header header{src_cmdbuf[0]}; | ||||
|  | ||||
|     size_t untranslated_size = 1u + header.normal_params_size; | ||||
|     size_t command_size = untranslated_size + header.translate_params_size; | ||||
|     ASSERT(command_size <= IPC::COMMAND_BUFFER_LENGTH); // TODO(yuriks): Return error | ||||
|  | ||||
|     std::copy_n(src_cmdbuf, untranslated_size, cmd_buf.begin()); | ||||
|  | ||||
|     size_t i = untranslated_size; | ||||
|     while (i < command_size) { | ||||
|         u32 descriptor = cmd_buf[i] = src_cmdbuf[i]; | ||||
|         i += 1; | ||||
|  | ||||
|         switch (IPC::GetDescriptorType(descriptor)) { | ||||
|         case IPC::DescriptorType::CopyHandle: | ||||
|         case IPC::DescriptorType::MoveHandle: { | ||||
|             u32 num_handles = IPC::HandleNumberFromDesc(descriptor); | ||||
|             ASSERT(i + num_handles <= command_size); // TODO(yuriks): Return error | ||||
|             for (u32 j = 0; j < num_handles; ++j) { | ||||
|                 Handle handle = src_cmdbuf[i]; | ||||
|                 SharedPtr<Object> object = src_table.GetGeneric(handle); | ||||
|                 ASSERT(object != nullptr); // TODO(yuriks): Return error | ||||
|                 if (descriptor == IPC::DescriptorType::MoveHandle) { | ||||
|                     src_table.Close(handle); | ||||
|                 } | ||||
|  | ||||
|                 cmd_buf[i++] = AddOutgoingHandle(std::move(object)); | ||||
|             } | ||||
|             break; | ||||
|         } | ||||
|         case IPC::DescriptorType::CallingPid: { | ||||
|             cmd_buf[i++] = src_process.process_id; | ||||
|             break; | ||||
|         } | ||||
|         default: | ||||
|             UNIMPLEMENTED_MSG("Unsupported handle translation: 0x%08X", descriptor); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
|  | ||||
| ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, Process& dst_process, | ||||
|                                                            HandleTable& dst_table) const { | ||||
|     IPC::Header header{cmd_buf[0]}; | ||||
|  | ||||
|     size_t untranslated_size = 1u + header.normal_params_size; | ||||
|     size_t command_size = untranslated_size + header.translate_params_size; | ||||
|     ASSERT(command_size <= IPC::COMMAND_BUFFER_LENGTH); | ||||
|  | ||||
|     std::copy_n(cmd_buf.begin(), untranslated_size, dst_cmdbuf); | ||||
|  | ||||
|     size_t i = untranslated_size; | ||||
|     while (i < command_size) { | ||||
|         u32 descriptor = dst_cmdbuf[i] = cmd_buf[i]; | ||||
|         i += 1; | ||||
|  | ||||
|         switch (IPC::GetDescriptorType(descriptor)) { | ||||
|         case IPC::DescriptorType::CopyHandle: | ||||
|         case IPC::DescriptorType::MoveHandle: { | ||||
|             // HLE services don't use handles, so we treat both CopyHandle and MoveHandle equally | ||||
|             u32 num_handles = IPC::HandleNumberFromDesc(descriptor); | ||||
|             ASSERT(i + num_handles <= command_size); | ||||
|             for (u32 j = 0; j < num_handles; ++j) { | ||||
|                 SharedPtr<Object> object = GetIncomingHandle(cmd_buf[i]); | ||||
|  | ||||
|                 // TODO(yuriks): Figure out the proper error handling for if this fails | ||||
|                 Handle handle = dst_table.Create(object).Unwrap(); | ||||
|                 dst_cmdbuf[i++] = handle; | ||||
|             } | ||||
|             break; | ||||
|         } | ||||
|         default: | ||||
|             UNIMPLEMENTED_MSG("Unsupported handle translation: 0x%08X", descriptor); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
|  | ||||
| } // namespace Kernel | ||||
|   | ||||
| @@ -8,6 +8,7 @@ | ||||
| #include <memory> | ||||
| #include <vector> | ||||
| #include "common/common_types.h" | ||||
| #include "common/swap.h" | ||||
| #include "core/hle/ipc.h" | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| #include "core/hle/kernel/server_session.h" | ||||
| @@ -18,6 +19,9 @@ class ServiceFrameworkBase; | ||||
|  | ||||
| namespace Kernel { | ||||
|  | ||||
| class HandleTable; | ||||
| class Process; | ||||
|  | ||||
| /** | ||||
|  * Interface implemented by HLE Session handlers. | ||||
|  * This can be provided to a ServerSession in order to hook into several relevant events | ||||
| @@ -62,6 +66,20 @@ protected: | ||||
|  * Class containing information about an in-flight IPC request being handled by an HLE service | ||||
|  * implementation. Services should avoid using old global APIs (e.g. Kernel::GetCommandBuffer()) and | ||||
|  * when possible use the APIs in this class to service the request. | ||||
|  * | ||||
|  * HLE handle protocol | ||||
|  * =================== | ||||
|  * | ||||
|  * To avoid needing HLE services to keep a separate handle table, or having to directly modify the | ||||
|  * requester's table, a tweaked protocol is used to receive and send handles in requests. The kernel | ||||
|  * will decode the incoming handles into object pointers and insert a id in the buffer where the | ||||
|  * handle would normally be. The service then calls GetIncomingHandle() with that id to get the | ||||
|  * pointer to the object. Similarly, instead of inserting a handle into the command buffer, the | ||||
|  * service calls AddOutgoingHandle() and stores the returned id where the handle would normally go. | ||||
|  * | ||||
|  * The end result is similar to just giving services their own real handle tables, but since these | ||||
|  * ids are local to a specific context, it avoids requiring services to manage handles for objects | ||||
|  * across multiple calls and ensuring that unneeded handles are cleaned up. | ||||
|  */ | ||||
| class HLERequestContext { | ||||
| public: | ||||
| @@ -80,14 +98,29 @@ public: | ||||
|         return session; | ||||
|     } | ||||
|  | ||||
|     SharedPtr<Object> GetIncomingHandle(Handle id_from_cmdbuf) const; | ||||
|     Handle AddOutgoingHandle(SharedPtr<Object> object); | ||||
|     /** | ||||
|      * Resolves a object id from the request command buffer into a pointer to an object. See the | ||||
|      * "HLE handle protocol" section in the class documentation for more details. | ||||
|      */ | ||||
|     SharedPtr<Object> GetIncomingHandle(u32 id_from_cmdbuf) const; | ||||
|  | ||||
|     /** | ||||
|      * Adds an outgoing object to the response, returning the id which should be used to reference | ||||
|      * it. See the "HLE handle protocol" section in the class documentation for more details. | ||||
|      */ | ||||
|     u32 AddOutgoingHandle(SharedPtr<Object> object); | ||||
|  | ||||
| private: | ||||
|     friend class Service::ServiceFrameworkBase; | ||||
|  | ||||
|     ResultCode PopulateFromIncomingCommandBuffer(const u32_le* src_cmdbuf, Process& src_process, | ||||
|                                                  HandleTable& src_table); | ||||
|     ResultCode WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, Process& dst_process, | ||||
|                                             HandleTable& dst_table) const; | ||||
|  | ||||
|     std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf; | ||||
|     SharedPtr<ServerSession> session; | ||||
|     std::vector<SharedPtr<Object>> request_handles; | ||||
| }; | ||||
|  | ||||
| } // namespace Kernel | ||||
|   | ||||
| @@ -9,6 +9,7 @@ | ||||
| #include "common/string_util.h" | ||||
| #include "core/hle/ipc.h" | ||||
| #include "core/hle/kernel/client_port.h" | ||||
| #include "core/hle/kernel/process.h" | ||||
| #include "core/hle/kernel/server_port.h" | ||||
| #include "core/hle/kernel/server_session.h" | ||||
| #include "core/hle/service/ac/ac.h" | ||||
| @@ -172,24 +173,16 @@ void ServiceFrameworkBase::HandleSyncRequest(SharedPtr<ServerSession> server_ses | ||||
|  | ||||
|     // TODO(yuriks): The kernel should be the one handling this as part of translation after | ||||
|     // everything else is migrated | ||||
|     IPC::Header request_header{cmd_buf[0]}; | ||||
|     size_t request_size = | ||||
|         1 + request_header.normal_params_size + request_header.translate_params_size; | ||||
|     ASSERT(request_size <= IPC::COMMAND_BUFFER_LENGTH); // TODO(yuriks): Return error | ||||
|  | ||||
|     Kernel::HLERequestContext context; | ||||
|     std::copy_n(cmd_buf, request_size, context.cmd_buf.begin()); | ||||
|     context.session = std::move(server_session); | ||||
|     context.PopulateFromIncomingCommandBuffer(cmd_buf, *Kernel::g_current_process, | ||||
|                                               Kernel::g_handle_table); | ||||
|  | ||||
|     LOG_TRACE(Service, "%s", | ||||
|               MakeFunctionString(info->name, GetServiceName().c_str(), cmd_buf).c_str()); | ||||
|     handler_invoker(this, info->handler_callback, context); | ||||
|  | ||||
|     IPC::Header response_header{context.cmd_buf[0]}; | ||||
|     size_t response_size = | ||||
|         1 + response_header.normal_params_size + response_header.translate_params_size; | ||||
|     ASSERT(response_size <= IPC::COMMAND_BUFFER_LENGTH); | ||||
|     std::copy_n(context.cmd_buf.begin(), response_size, cmd_buf); | ||||
|     context.WriteToOutgoingCommandBuffer(cmd_buf, *Kernel::g_current_process, | ||||
|                                          Kernel::g_handle_table); | ||||
| } | ||||
|  | ||||
| //////////////////////////////////////////////////////////////////////////////////////////////////// | ||||
|   | ||||
		Reference in New Issue
	
	Block a user