mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 21:30:04 +00:00 
			
		
		
		
	Merge pull request #2774 from yuriks/hle-handles
Add basic support for IPC translation for HLE services
This commit is contained in:
		
						commit
						4325a7c82d
					
				
					 7 changed files with 361 additions and 70 deletions
				
			
		
							
								
								
									
										2
									
								
								externals/boost
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								externals/boost
									
										
									
									
										vendored
									
									
								
							|  | @ -1 +1 @@ | |||
| Subproject commit 351972396392c97a659b9a02f34ce9269293d21f | ||||
| Subproject commit 3abc84abaf63a068cb59a9f9b5675c1947bc6fd9 | ||||
|  | @ -44,6 +44,9 @@ inline u32* GetStaticBuffers(const int offset = 0) { | |||
| 
 | ||||
| namespace IPC { | ||||
| 
 | ||||
| /// Size of the command buffer area, in 32-bit words.
 | ||||
| constexpr size_t COMMAND_BUFFER_LENGTH = 0x100 / sizeof(u32); | ||||
| 
 | ||||
| // These errors are commonly returned by invalid IPC translations, so alias them here for
 | ||||
| // convenience.
 | ||||
| // TODO(yuriks): These will probably go away once translation is implemented inside the kernel.
 | ||||
|  |  | |||
|  | @ -4,19 +4,28 @@ | |||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <array> | ||||
| #include <tuple> | ||||
| #include <type_traits> | ||||
| #include <utility> | ||||
| #include "core/hle/ipc.h" | ||||
| #include "core/hle/kernel/handle_table.h" | ||||
| #include "core/hle/kernel/hle_ipc.h" | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| 
 | ||||
| namespace IPC { | ||||
| 
 | ||||
| class RequestHelperBase { | ||||
| protected: | ||||
|     Kernel::HLERequestContext* context = nullptr; | ||||
|     u32* cmdbuf; | ||||
|     ptrdiff_t index = 1; | ||||
|     Header header; | ||||
| 
 | ||||
| public: | ||||
|     RequestHelperBase(Kernel::HLERequestContext& context, Header desired_header) | ||||
|         : context(&context), cmdbuf(context.CommandBuffer()), header(desired_header) {} | ||||
| 
 | ||||
|     RequestHelperBase(u32* command_buffer, Header command_header) | ||||
|         : cmdbuf(command_buffer), header(command_header) {} | ||||
| 
 | ||||
|  | @ -51,12 +60,27 @@ public: | |||
| 
 | ||||
| class RequestBuilder : public RequestHelperBase { | ||||
| public: | ||||
|     RequestBuilder(Kernel::HLERequestContext& context, Header command_header) | ||||
|         : RequestHelperBase(context, command_header) { | ||||
|         // From this point we will start overwriting the existing command buffer, so it's safe to
 | ||||
|         // release all previous incoming Object pointers since they won't be usable anymore.
 | ||||
|         context.ClearIncomingObjects(); | ||||
|         cmdbuf[0] = header.raw; | ||||
|     } | ||||
| 
 | ||||
|     RequestBuilder(Kernel::HLERequestContext& context, u16 command_id, unsigned normal_params_size, | ||||
|                    unsigned translate_params_size) | ||||
|         : RequestBuilder( | ||||
|               context, Header{MakeHeader(command_id, normal_params_size, translate_params_size)}) {} | ||||
| 
 | ||||
|     RequestBuilder(u32* command_buffer, Header command_header) | ||||
|         : RequestHelperBase(command_buffer, command_header) { | ||||
|         cmdbuf[0] = header.raw; | ||||
|     } | ||||
| 
 | ||||
|     explicit RequestBuilder(u32* command_buffer, u32 command_header) | ||||
|         : RequestBuilder(command_buffer, Header{command_header}) {} | ||||
| 
 | ||||
|     RequestBuilder(u32* command_buffer, u16 command_id, unsigned normal_params_size, | ||||
|                    unsigned translate_params_size) | ||||
|         : RequestBuilder(command_buffer, | ||||
|  | @ -88,6 +112,9 @@ public: | |||
|     template <typename... H> | ||||
|     void PushMoveHandles(H... handles); | ||||
| 
 | ||||
|     template <typename... O> | ||||
|     void PushObjects(Kernel::SharedPtr<O>... pointers); | ||||
| 
 | ||||
|     void PushCurrentPIDHandle(); | ||||
| 
 | ||||
|     void PushStaticBuffer(VAddr buffer_vaddr, u32 size, u8 buffer_id); | ||||
|  | @ -153,6 +180,11 @@ inline void RequestBuilder::PushMoveHandles(H... handles) { | |||
|     Push(static_cast<Kernel::Handle>(handles)...); | ||||
| } | ||||
| 
 | ||||
| template <typename... O> | ||||
| inline void RequestBuilder::PushObjects(Kernel::SharedPtr<O>... pointers) { | ||||
|     PushMoveHandles(context->AddOutgoingHandle(std::move(pointers))...); | ||||
| } | ||||
| 
 | ||||
| inline void RequestBuilder::PushCurrentPIDHandle() { | ||||
|     Push(CallingPidDesc()); | ||||
|     Push(u32(0)); | ||||
|  | @ -171,10 +203,21 @@ inline void RequestBuilder::PushMappedBuffer(VAddr buffer_vaddr, u32 size, | |||
| 
 | ||||
| class RequestParser : public RequestHelperBase { | ||||
| public: | ||||
|     RequestParser(Kernel::HLERequestContext& context, Header desired_header) | ||||
|         : RequestHelperBase(context, desired_header) {} | ||||
| 
 | ||||
|     RequestParser(Kernel::HLERequestContext& context, u16 command_id, unsigned normal_params_size, | ||||
|                   unsigned translate_params_size) | ||||
|         : RequestParser(context, | ||||
|                         Header{MakeHeader(command_id, normal_params_size, translate_params_size)}) { | ||||
|     } | ||||
| 
 | ||||
|     RequestParser(u32* command_buffer, Header command_header) | ||||
|         : RequestHelperBase(command_buffer, command_header) {} | ||||
| 
 | ||||
|     explicit RequestParser(u32* command_buffer, u32 command_header) | ||||
|         : RequestParser(command_buffer, Header{command_header}) {} | ||||
| 
 | ||||
|     RequestParser(u32* command_buffer, u16 command_id, unsigned normal_params_size, | ||||
|                   unsigned translate_params_size) | ||||
|         : RequestParser(command_buffer, | ||||
|  | @ -186,7 +229,10 @@ public: | |||
|             ValidateHeader(); | ||||
|         Header builderHeader{ | ||||
|             MakeHeader(header.command_id, normal_params_size, translate_params_size)}; | ||||
|         return {cmdbuf, builderHeader}; | ||||
|         if (context != nullptr) | ||||
|             return {*context, builderHeader}; | ||||
|         else | ||||
|             return {cmdbuf, builderHeader}; | ||||
|     } | ||||
| 
 | ||||
|     template <typename T> | ||||
|  | @ -198,10 +244,52 @@ public: | |||
|     template <typename First, typename... Other> | ||||
|     void Pop(First& first_value, Other&... other_values); | ||||
| 
 | ||||
|     /// Equivalent to calling `PopHandles<1>()[0]`.
 | ||||
|     Kernel::Handle PopHandle(); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Pops a descriptor containing `N` handles. The handles are returned as an array. The | ||||
|      * descriptor must contain exactly `N` handles, it is not permitted to, for example, call | ||||
|      * PopHandles<1>() twice to read a multi-handle descriptor with 2 handles, or to make a single | ||||
|      * PopHandles<2>() call to read 2 single-handle descriptors. | ||||
|      */ | ||||
|     template <unsigned int N> | ||||
|     std::array<Kernel::Handle, N> PopHandles(); | ||||
| 
 | ||||
|     /// Convenience wrapper around PopHandles() which assigns the handles to the passed references.
 | ||||
|     template <typename... H> | ||||
|     void PopHandles(H&... handles); | ||||
|     void PopHandles(H&... handles) { | ||||
|         std::tie(handles...) = PopHandles<sizeof...(H)>(); | ||||
|     } | ||||
| 
 | ||||
|     /// Equivalent to calling `PopGenericObjects<1>()[0]`.
 | ||||
|     Kernel::SharedPtr<Kernel::Object> PopGenericObject(); | ||||
| 
 | ||||
|     /// Equivalent to calling `std::get<0>(PopObjects<T>())`.
 | ||||
|     template <typename T> | ||||
|     Kernel::SharedPtr<T> PopObject(); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Pop a descriptor containing `N` handles and resolves them to Kernel::Object pointers. If a | ||||
|      * handle is invalid, null is returned for that object instead. The same caveats from | ||||
|      * PopHandles() apply regarding `N` matching the number of handles in the descriptor. | ||||
|      */ | ||||
|     template <unsigned int N> | ||||
|     std::array<Kernel::SharedPtr<Kernel::Object>, N> PopGenericObjects(); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Resolves handles to Kernel::Objects as in PopGenericsObjects(), but then also casts them to | ||||
|      * the passed `T` types, while verifying that the cast is valid. If the type of an object does | ||||
|      * not match, null is returned instead. | ||||
|      */ | ||||
|     template <typename... T> | ||||
|     std::tuple<Kernel::SharedPtr<T>...> PopObjects(); | ||||
| 
 | ||||
|     /// Convenience wrapper around PopObjects() which assigns the handles to the passed references.
 | ||||
|     template <typename... T> | ||||
|     void PopObjects(Kernel::SharedPtr<T>&... pointers) { | ||||
|         std::tie(pointers...) = PopObjects<T...>(); | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|      * @brief Pops the static buffer vaddr | ||||
|  | @ -313,15 +401,54 @@ inline Kernel::Handle RequestParser::PopHandle() { | |||
|     return Pop<Kernel::Handle>(); | ||||
| } | ||||
| 
 | ||||
| template <typename... H> | ||||
| void RequestParser::PopHandles(H&... handles) { | ||||
|     const u32 handle_descriptor = Pop<u32>(); | ||||
|     const int handles_number = sizeof...(H); | ||||
|     DEBUG_ASSERT_MSG(IsHandleDescriptor(handle_descriptor), | ||||
|                      "Tried to pop handle(s) but the descriptor is not a handle descriptor"); | ||||
|     DEBUG_ASSERT_MSG(handles_number == HandleNumberFromDesc(handle_descriptor), | ||||
|                      "Number of handles doesn't match the descriptor"); | ||||
|     Pop(static_cast<Kernel::Handle&>(handles)...); | ||||
| template <unsigned int N> | ||||
| std::array<Kernel::Handle, N> RequestParser::PopHandles() { | ||||
|     u32 handle_descriptor = Pop<u32>(); | ||||
|     ASSERT_MSG(IsHandleDescriptor(handle_descriptor), | ||||
|                "Tried to pop handle(s) but the descriptor is not a handle descriptor"); | ||||
|     ASSERT_MSG(N == HandleNumberFromDesc(handle_descriptor), | ||||
|                "Number of handles doesn't match the descriptor"); | ||||
| 
 | ||||
|     std::array<Kernel::Handle, N> handles{}; | ||||
|     for (Kernel::Handle& handle : handles) { | ||||
|         handle = Pop<Kernel::Handle>(); | ||||
|     } | ||||
|     return handles; | ||||
| } | ||||
| 
 | ||||
| inline Kernel::SharedPtr<Kernel::Object> RequestParser::PopGenericObject() { | ||||
|     Kernel::Handle handle = PopHandle(); | ||||
|     return context->GetIncomingHandle(handle); | ||||
| } | ||||
| 
 | ||||
| template <typename T> | ||||
| Kernel::SharedPtr<T> RequestParser::PopObject() { | ||||
|     return Kernel::DynamicObjectCast<T>(PopGenericObject()); | ||||
| } | ||||
| 
 | ||||
| template <unsigned int N> | ||||
| inline std::array<Kernel::SharedPtr<Kernel::Object>, N> RequestParser::PopGenericObjects() { | ||||
|     std::array<Kernel::Handle, N> handles = PopHandles<N>(); | ||||
|     std::array<Kernel::SharedPtr<Kernel::Object>, N> pointers; | ||||
|     for (int i = 0; i < N; ++i) { | ||||
|         pointers[i] = context->GetIncomingHandle(handles[i]); | ||||
|     } | ||||
|     return pointers; | ||||
| } | ||||
| 
 | ||||
| namespace detail { | ||||
| template <typename... T, size_t... I> | ||||
| std::tuple<Kernel::SharedPtr<T>...> PopObjectsHelper( | ||||
|     std::array<Kernel::SharedPtr<Kernel::Object>, sizeof...(T)>&& pointers, | ||||
|     std::index_sequence<I...>) { | ||||
|     return std::make_tuple(Kernel::DynamicObjectCast<T>(std::move(pointers[I]))...); | ||||
| } | ||||
| } // namespace detail
 | ||||
| 
 | ||||
| template <typename... T> | ||||
| inline std::tuple<Kernel::SharedPtr<T>...> RequestParser::PopObjects() { | ||||
|     return detail::PopObjectsHelper<T...>(PopGenericObjects<sizeof...(T)>(), | ||||
|                                           std::index_sequence_for<T...>{}); | ||||
| } | ||||
| 
 | ||||
| inline VAddr RequestParser::PopStaticBuffer(size_t* data_size, bool useStaticBuffersToGetVaddr) { | ||||
|  |  | |||
|  | @ -5,8 +5,10 @@ | |||
| #include <boost/range/algorithm_ext/erase.hpp> | ||||
| #include "common/assert.h" | ||||
| #include "common/common_types.h" | ||||
| #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 { | ||||
|  | @ -23,4 +25,101 @@ void SessionRequestHandler::ClientDisconnected(SharedPtr<ServerSession> server_s | |||
| 
 | ||||
| HLERequestContext::~HLERequestContext() = default; | ||||
| 
 | ||||
| SharedPtr<Object> HLERequestContext::GetIncomingHandle(u32 id_from_cmdbuf) const { | ||||
|     ASSERT(id_from_cmdbuf < request_handles.size()); | ||||
|     return request_handles[id_from_cmdbuf]; | ||||
| } | ||||
| 
 | ||||
| u32 HLERequestContext::AddOutgoingHandle(SharedPtr<Object> object) { | ||||
|     request_handles.push_back(std::move(object)); | ||||
|     return request_handles.size() - 1; | ||||
| } | ||||
| 
 | ||||
| void HLERequestContext::ClearIncomingObjects() { | ||||
|     request_handles.clear(); | ||||
| } | ||||
| 
 | ||||
| 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
 | ||||
|  |  | |||
|  | @ -4,8 +4,13 @@ | |||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <array> | ||||
| #include <memory> | ||||
| #include <vector> | ||||
| #include <boost/container/small_vector.hpp> | ||||
| #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" | ||||
| 
 | ||||
|  | @ -15,6 +20,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 | ||||
|  | @ -59,14 +67,28 @@ 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: | ||||
|     ~HLERequestContext(); | ||||
| 
 | ||||
|     /// Returns a pointer to the IPC command buffer for this request.
 | ||||
|     u32* CommandBuffer() const { | ||||
|         return cmd_buf; | ||||
|     u32* CommandBuffer() { | ||||
|         return cmd_buf.data(); | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|  | @ -77,11 +99,37 @@ public: | |||
|         return session; | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|      * 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); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Discards all Objects from the context, invalidating all ids. This may be called after reading | ||||
|      * out all incoming objects, so that the buffer memory can be re-used for outgoing handles, but | ||||
|      * this is not required. | ||||
|      */ | ||||
|     void ClearIncomingObjects(); | ||||
| 
 | ||||
| private: | ||||
|     friend class Service::ServiceFrameworkBase; | ||||
| 
 | ||||
|     u32* cmd_buf = nullptr; | ||||
|     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; | ||||
|     // TODO(yuriks): Check common usage of this and optimize size accordingly
 | ||||
|     boost::container::small_vector<SharedPtr<Object>, 8> request_handles; | ||||
| }; | ||||
| 
 | ||||
| } // namespace Kernel
 | ||||
|  |  | |||
|  | @ -2,10 +2,14 @@ | |||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <algorithm> | ||||
| #include <fmt/format.h> | ||||
| #include "common/assert.h" | ||||
| #include "common/logging/log.h" | ||||
| #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" | ||||
|  | @ -160,12 +164,6 @@ void ServiceFrameworkBase::ReportUnimplementedFunction(u32* cmd_buf, const Funct | |||
| void ServiceFrameworkBase::HandleSyncRequest(SharedPtr<ServerSession> server_session) { | ||||
|     u32* cmd_buf = Kernel::GetCommandBuffer(); | ||||
| 
 | ||||
|     // TODO(yuriks): The kernel should be the one handling this as part of translation after
 | ||||
|     // everything else is migrated
 | ||||
|     Kernel::HLERequestContext context; | ||||
|     context.cmd_buf = cmd_buf; | ||||
|     context.session = std::move(server_session); | ||||
| 
 | ||||
|     u32 header_code = cmd_buf[0]; | ||||
|     auto itr = handlers.find(header_code); | ||||
|     const FunctionInfoBase* info = itr == handlers.end() ? nullptr : &itr->second; | ||||
|  | @ -173,9 +171,18 @@ void ServiceFrameworkBase::HandleSyncRequest(SharedPtr<ServerSession> server_ses | |||
|         return ReportUnimplementedFunction(cmd_buf, info); | ||||
|     } | ||||
| 
 | ||||
|     // TODO(yuriks): The kernel should be the one handling this as part of translation after
 | ||||
|     // everything else is migrated
 | ||||
|     Kernel::HLERequestContext context; | ||||
|     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); | ||||
|     context.WriteToOutgoingCommandBuffer(cmd_buf, *Kernel::g_current_process, | ||||
|                                          Kernel::g_handle_table); | ||||
| } | ||||
| 
 | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||
|  |  | |||
|  | @ -7,9 +7,11 @@ | |||
| #include "common/common_types.h" | ||||
| #include "common/logging/log.h" | ||||
| #include "core/hle/ipc.h" | ||||
| #include "core/hle/ipc_helpers.h" | ||||
| #include "core/hle/kernel/client_port.h" | ||||
| #include "core/hle/kernel/client_session.h" | ||||
| #include "core/hle/kernel/handle_table.h" | ||||
| #include "core/hle/kernel/errors.h" | ||||
| #include "core/hle/kernel/hle_ipc.h" | ||||
| #include "core/hle/kernel/semaphore.h" | ||||
| #include "core/hle/kernel/server_session.h" | ||||
| #include "core/hle/service/sm/sm.h" | ||||
|  | @ -30,15 +32,18 @@ constexpr int MAX_PENDING_NOTIFICATIONS = 16; | |||
|  *      1: ResultCode | ||||
|  */ | ||||
| void SRV::RegisterClient(Kernel::HLERequestContext& ctx) { | ||||
|     u32* cmd_buff = ctx.CommandBuffer(); | ||||
|     IPC::RequestParser rp(ctx, 0x1, 0, 2); | ||||
| 
 | ||||
|     if (cmd_buff[1] != IPC::CallingPidDesc()) { | ||||
|         cmd_buff[0] = IPC::MakeHeader(0x0, 0x1, 0); // 0x40
 | ||||
|         cmd_buff[1] = IPC::ERR_INVALID_BUFFER_DESCRIPTOR.raw; | ||||
|     u32 pid_descriptor = rp.Pop<u32>(); | ||||
|     if (pid_descriptor != IPC::CallingPidDesc()) { | ||||
|         IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|         rb.Push(IPC::ERR_INVALID_BUFFER_DESCRIPTOR); | ||||
|         return; | ||||
|     } | ||||
|     cmd_buff[0] = IPC::MakeHeader(0x1, 0x1, 0); // 0x10040
 | ||||
|     cmd_buff[1] = RESULT_SUCCESS.raw;           // No error
 | ||||
|     u32 caller_pid = rp.Pop<u32>(); | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     LOG_WARNING(Service_SRV, "(STUBBED) called"); | ||||
| } | ||||
| 
 | ||||
|  | @ -53,15 +58,14 @@ void SRV::RegisterClient(Kernel::HLERequestContext& ctx) { | |||
|  *      3: Handle to semaphore signaled on process notification | ||||
|  */ | ||||
| void SRV::EnableNotification(Kernel::HLERequestContext& ctx) { | ||||
|     u32* cmd_buff = ctx.CommandBuffer(); | ||||
|     IPC::RequestParser rp(ctx, 0x2, 0, 0); | ||||
| 
 | ||||
|     notification_semaphore = | ||||
|         Kernel::Semaphore::Create(0, MAX_PENDING_NOTIFICATIONS, "SRV:Notification").Unwrap(); | ||||
| 
 | ||||
|     cmd_buff[0] = IPC::MakeHeader(0x2, 0x1, 0x2); // 0x20042
 | ||||
|     cmd_buff[1] = RESULT_SUCCESS.raw;             // No error
 | ||||
|     cmd_buff[2] = IPC::CopyHandleDesc(1); | ||||
|     cmd_buff[3] = Kernel::g_handle_table.Create(notification_semaphore).MoveFrom(); | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     rb.PushObjects(notification_semaphore); | ||||
|     LOG_WARNING(Service_SRV, "(STUBBED) called"); | ||||
| } | ||||
| 
 | ||||
|  | @ -77,43 +81,49 @@ void SRV::EnableNotification(Kernel::HLERequestContext& ctx) { | |||
|  *      3: Service handle | ||||
|  */ | ||||
| void SRV::GetServiceHandle(Kernel::HLERequestContext& ctx) { | ||||
|     ResultCode res = RESULT_SUCCESS; | ||||
|     u32* cmd_buff = ctx.CommandBuffer(); | ||||
|     IPC::RequestParser rp(ctx, 0x5, 4, 0); | ||||
|     auto name_buf = rp.PopRaw<std::array<char, 8>>(); | ||||
|     size_t name_len = rp.Pop<u32>(); | ||||
|     u32 flags = rp.Pop<u32>(); | ||||
| 
 | ||||
|     bool return_port_on_failure = (flags & 1) == 0; | ||||
| 
 | ||||
|     size_t name_len = cmd_buff[3]; | ||||
|     if (name_len > Service::kMaxPortSize) { | ||||
|         cmd_buff[1] = ERR_INVALID_NAME_SIZE.raw; | ||||
|         LOG_ERROR(Service_SRV, "called name_len=0x%X, failed with code=0x%08X", name_len, | ||||
|                   cmd_buff[1]); | ||||
|         IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|         rb.Push(ERR_INVALID_NAME_SIZE); | ||||
|         LOG_ERROR(Service_SRV, "called name_len=0x%X -> ERR_INVALID_NAME_SIZE", name_len); | ||||
|         return; | ||||
|     } | ||||
|     std::string name(reinterpret_cast<const char*>(&cmd_buff[1]), name_len); | ||||
|     bool return_port_on_failure = (cmd_buff[4] & 1) == 0; | ||||
|     std::string name(name_buf.data(), name_len); | ||||
| 
 | ||||
|     // TODO(yuriks): Permission checks go here
 | ||||
| 
 | ||||
|     auto client_port = service_manager->GetServicePort(name); | ||||
|     if (client_port.Failed()) { | ||||
|         cmd_buff[1] = client_port.Code().raw; | ||||
|         LOG_ERROR(Service_SRV, "called service=%s, failed with code=0x%08X", name.c_str(), | ||||
|                   cmd_buff[1]); | ||||
|         IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|         rb.Push(client_port.Code()); | ||||
|         LOG_ERROR(Service_SRV, "called service=%s -> error 0x%08X", name.c_str(), | ||||
|                   client_port.Code().raw); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     auto session = client_port.Unwrap()->Connect(); | ||||
|     cmd_buff[1] = session.Code().raw; | ||||
|     if (session.Succeeded()) { | ||||
|         cmd_buff[3] = Kernel::g_handle_table.Create(session.MoveFrom()).MoveFrom(); | ||||
|         LOG_DEBUG(Service_SRV, "called service=%s, session handle=0x%08X", name.c_str(), | ||||
|                   cmd_buff[3]); | ||||
|         LOG_DEBUG(Service_SRV, "called service=%s -> session=%u", name.c_str(), | ||||
|                   (*session)->GetObjectId()); | ||||
|         IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); | ||||
|         rb.Push(session.Code()); | ||||
|         rb.PushObjects(session.MoveFrom()); | ||||
|     } else if (session.Code() == Kernel::ERR_MAX_CONNECTIONS_REACHED && return_port_on_failure) { | ||||
|         cmd_buff[1] = ERR_MAX_CONNECTIONS_REACHED.raw; | ||||
|         cmd_buff[3] = Kernel::g_handle_table.Create(client_port.MoveFrom()).MoveFrom(); | ||||
|         LOG_WARNING(Service_SRV, "called service=%s, *port* handle=0x%08X", name.c_str(), | ||||
|                     cmd_buff[3]); | ||||
|         LOG_WARNING(Service_SRV, "called service=%s -> ERR_MAX_CONNECTIONS_REACHED, *port*=%u", | ||||
|                     name.c_str(), (*client_port)->GetObjectId()); | ||||
|         IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); | ||||
|         rb.Push(ERR_MAX_CONNECTIONS_REACHED); | ||||
|         rb.PushObjects(client_port.MoveFrom()); | ||||
|     } else { | ||||
|         LOG_ERROR(Service_SRV, "called service=%s, failed with code=0x%08X", name.c_str(), | ||||
|                   cmd_buff[1]); | ||||
|         LOG_ERROR(Service_SRV, "called service=%s -> error 0x%08X", name.c_str(), session.Code()); | ||||
|         IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|         rb.Push(session.Code()); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | @ -127,12 +137,11 @@ void SRV::GetServiceHandle(Kernel::HLERequestContext& ctx) { | |||
|  *      1: ResultCode | ||||
|  */ | ||||
| void SRV::Subscribe(Kernel::HLERequestContext& ctx) { | ||||
|     u32* cmd_buff = ctx.CommandBuffer(); | ||||
|     IPC::RequestParser rp(ctx, 0x9, 1, 0); | ||||
|     u32 notification_id = rp.Pop<u32>(); | ||||
| 
 | ||||
|     u32 notification_id = cmd_buff[1]; | ||||
| 
 | ||||
|     cmd_buff[0] = IPC::MakeHeader(0x9, 0x1, 0); // 0x90040
 | ||||
|     cmd_buff[1] = RESULT_SUCCESS.raw;           // No error
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     LOG_WARNING(Service_SRV, "(STUBBED) called, notification_id=0x%X", notification_id); | ||||
| } | ||||
| 
 | ||||
|  | @ -146,12 +155,11 @@ void SRV::Subscribe(Kernel::HLERequestContext& ctx) { | |||
|  *      1: ResultCode | ||||
|  */ | ||||
| void SRV::Unsubscribe(Kernel::HLERequestContext& ctx) { | ||||
|     u32* cmd_buff = ctx.CommandBuffer(); | ||||
|     IPC::RequestParser rp(ctx, 0xA, 1, 0); | ||||
|     u32 notification_id = rp.Pop<u32>(); | ||||
| 
 | ||||
|     u32 notification_id = cmd_buff[1]; | ||||
| 
 | ||||
|     cmd_buff[0] = IPC::MakeHeader(0xA, 0x1, 0); // 0xA0040
 | ||||
|     cmd_buff[1] = RESULT_SUCCESS.raw;           // No error
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     LOG_WARNING(Service_SRV, "(STUBBED) called, notification_id=0x%X", notification_id); | ||||
| } | ||||
| 
 | ||||
|  | @ -166,13 +174,12 @@ void SRV::Unsubscribe(Kernel::HLERequestContext& ctx) { | |||
|  *      1: ResultCode | ||||
|  */ | ||||
| void SRV::PublishToSubscriber(Kernel::HLERequestContext& ctx) { | ||||
|     u32* cmd_buff = ctx.CommandBuffer(); | ||||
|     IPC::RequestParser rp(ctx, 0xC, 2, 0); | ||||
|     u32 notification_id = rp.Pop<u32>(); | ||||
|     u8 flags = rp.Pop<u8>(); | ||||
| 
 | ||||
|     u32 notification_id = cmd_buff[1]; | ||||
|     u8 flags = cmd_buff[2] & 0xFF; | ||||
| 
 | ||||
|     cmd_buff[0] = IPC::MakeHeader(0xC, 0x1, 0); // 0xC0040
 | ||||
|     cmd_buff[1] = RESULT_SUCCESS.raw;           // No error
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     LOG_WARNING(Service_SRV, "(STUBBED) called, notification_id=0x%X, flags=%u", notification_id, | ||||
|                 flags); | ||||
| } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue