diff options
author | Morph <39850852+Morph1984@users.noreply.github.com> | 2023-03-04 04:51:17 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-03-04 04:51:17 +0100 |
commit | ce8f4da63834be0179d98a7720dee47d65f3ec06 (patch) | |
tree | a9a9303a532d374db9ae8255e5f3f2487e370f84 /src/core/hle/service/hle_ipc.h | |
parent | Merge pull request #9855 from liamwhite/kern-16-support (diff) | |
parent | nvnflinger: fix name (diff) | |
download | yuzu-ce8f4da63834be0179d98a7720dee47d65f3ec06.tar yuzu-ce8f4da63834be0179d98a7720dee47d65f3ec06.tar.gz yuzu-ce8f4da63834be0179d98a7720dee47d65f3ec06.tar.bz2 yuzu-ce8f4da63834be0179d98a7720dee47d65f3ec06.tar.lz yuzu-ce8f4da63834be0179d98a7720dee47d65f3ec06.tar.xz yuzu-ce8f4da63834be0179d98a7720dee47d65f3ec06.tar.zst yuzu-ce8f4da63834be0179d98a7720dee47d65f3ec06.zip |
Diffstat (limited to 'src/core/hle/service/hle_ipc.h')
-rw-r--r-- | src/core/hle/service/hle_ipc.h | 408 |
1 files changed, 408 insertions, 0 deletions
diff --git a/src/core/hle/service/hle_ipc.h b/src/core/hle/service/hle_ipc.h new file mode 100644 index 000000000..4bd24c899 --- /dev/null +++ b/src/core/hle/service/hle_ipc.h @@ -0,0 +1,408 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <array> +#include <functional> +#include <memory> +#include <optional> +#include <span> +#include <string> +#include <type_traits> +#include <vector> + +#include "common/assert.h" +#include "common/common_types.h" +#include "common/concepts.h" +#include "common/swap.h" +#include "core/hle/ipc.h" +#include "core/hle/kernel/svc_common.h" + +union Result; + +namespace Core::Memory { +class Memory; +} + +namespace IPC { +class ResponseBuilder; +} + +namespace Service { +class ServiceFrameworkBase; +class ServerManager; +} // namespace Service + +namespace Kernel { +class KAutoObject; +class KernelCore; +class KHandleTable; +class KServerSession; +class KThread; +} // namespace Kernel + +namespace Service { + +using Handle = Kernel::Handle; + +class HLERequestContext; + +/** + * Interface implemented by HLE Session handlers. + * This can be provided to a ServerSession in order to hook into several relevant events + * (such as a new connection or a SyncRequest) so they can be implemented in the emulator. + */ +class SessionRequestHandler : public std::enable_shared_from_this<SessionRequestHandler> { +public: + SessionRequestHandler(Kernel::KernelCore& kernel_, const char* service_name_); + virtual ~SessionRequestHandler(); + + /** + * Handles a sync request from the emulated application. + * @param server_session The ServerSession that was triggered for this sync request, + * it should be used to differentiate which client (As in ClientSession) we're answering to. + * TODO(Subv): Use a wrapper structure to hold all the information relevant to + * this request (ServerSession, Originator thread, Translated command buffer, etc). + * @returns Result the result code of the translate operation. + */ + virtual Result HandleSyncRequest(Kernel::KServerSession& session, + HLERequestContext& context) = 0; + +protected: + Kernel::KernelCore& kernel; +}; + +using SessionRequestHandlerWeakPtr = std::weak_ptr<SessionRequestHandler>; +using SessionRequestHandlerPtr = std::shared_ptr<SessionRequestHandler>; + +/** + * Manages the underlying HLE requests for a session, and whether (or not) the session should be + * treated as a domain. This is managed separately from server sessions, as this state is shared + * when objects are cloned. + */ +class SessionRequestManager final { +public: + explicit SessionRequestManager(Kernel::KernelCore& kernel, + Service::ServerManager& server_manager); + ~SessionRequestManager(); + + bool IsDomain() const { + return is_domain; + } + + void ConvertToDomain() { + domain_handlers = {session_handler}; + is_domain = true; + } + + void ConvertToDomainOnRequestEnd() { + convert_to_domain = true; + } + + std::size_t DomainHandlerCount() const { + return domain_handlers.size(); + } + + bool HasSessionHandler() const { + return session_handler != nullptr; + } + + SessionRequestHandler& SessionHandler() { + return *session_handler; + } + + const SessionRequestHandler& SessionHandler() const { + return *session_handler; + } + + void CloseDomainHandler(std::size_t index) { + if (index < DomainHandlerCount()) { + domain_handlers[index] = nullptr; + } else { + ASSERT_MSG(false, "Unexpected handler index {}", index); + } + } + + SessionRequestHandlerWeakPtr DomainHandler(std::size_t index) const { + ASSERT_MSG(index < DomainHandlerCount(), "Unexpected handler index {}", index); + return domain_handlers.at(index); + } + + void AppendDomainHandler(SessionRequestHandlerPtr&& handler) { + domain_handlers.emplace_back(std::move(handler)); + } + + void SetSessionHandler(SessionRequestHandlerPtr&& handler) { + session_handler = std::move(handler); + } + + bool HasSessionRequestHandler(const HLERequestContext& context) const; + + Result HandleDomainSyncRequest(Kernel::KServerSession* server_session, + HLERequestContext& context); + Result CompleteSyncRequest(Kernel::KServerSession* server_session, HLERequestContext& context); + + Service::ServerManager& GetServerManager() { + return server_manager; + } + + // TODO: remove this when sm: is implemented with the proper IUserInterface + // abstraction, creating a new C++ handler object for each session: + + bool GetIsInitializedForSm() const { + return is_initialized_for_sm; + } + + void SetIsInitializedForSm() { + is_initialized_for_sm = true; + } + +private: + bool convert_to_domain{}; + bool is_domain{}; + bool is_initialized_for_sm{}; + SessionRequestHandlerPtr session_handler; + std::vector<SessionRequestHandlerPtr> domain_handlers; + +private: + Kernel::KernelCore& kernel; + Service::ServerManager& server_manager; +}; + +/** + * Class containing information about an in-flight IPC request being handled by an HLE service + * implementation. + */ +class HLERequestContext { +public: + explicit HLERequestContext(Kernel::KernelCore& kernel, Core::Memory::Memory& memory, + Kernel::KServerSession* session, Kernel::KThread* thread); + ~HLERequestContext(); + + /// Returns a pointer to the IPC command buffer for this request. + [[nodiscard]] u32* CommandBuffer() { + return cmd_buf.data(); + } + + /** + * Returns the session through which this request was made. This can be used as a map key to + * access per-client data on services. + */ + [[nodiscard]] Kernel::KServerSession* Session() { + return server_session; + } + + /// Populates this context with data from the requesting process/thread. + Result PopulateFromIncomingCommandBuffer(const Kernel::KHandleTable& handle_table, + u32_le* src_cmdbuf); + + /// Writes data from this context back to the requesting process/thread. + Result WriteToOutgoingCommandBuffer(Kernel::KThread& requesting_thread); + + [[nodiscard]] u32_le GetHipcCommand() const { + return command; + } + + [[nodiscard]] u32_le GetTipcCommand() const { + return static_cast<u32_le>(command_header->type.Value()) - + static_cast<u32_le>(IPC::CommandType::TIPC_CommandRegion); + } + + [[nodiscard]] u32_le GetCommand() const { + return command_header->IsTipc() ? GetTipcCommand() : GetHipcCommand(); + } + + [[nodiscard]] bool IsTipc() const { + return command_header->IsTipc(); + } + + [[nodiscard]] IPC::CommandType GetCommandType() const { + return command_header->type; + } + + [[nodiscard]] u64 GetPID() const { + return pid; + } + + [[nodiscard]] u32 GetDataPayloadOffset() const { + return data_payload_offset; + } + + [[nodiscard]] const std::vector<IPC::BufferDescriptorX>& BufferDescriptorX() const { + return buffer_x_desciptors; + } + + [[nodiscard]] const std::vector<IPC::BufferDescriptorABW>& BufferDescriptorA() const { + return buffer_a_desciptors; + } + + [[nodiscard]] const std::vector<IPC::BufferDescriptorABW>& BufferDescriptorB() const { + return buffer_b_desciptors; + } + + [[nodiscard]] const std::vector<IPC::BufferDescriptorC>& BufferDescriptorC() const { + return buffer_c_desciptors; + } + + [[nodiscard]] const IPC::DomainMessageHeader& GetDomainMessageHeader() const { + return domain_message_header.value(); + } + + [[nodiscard]] bool HasDomainMessageHeader() const { + return domain_message_header.has_value(); + } + + /// Helper function to get a span of a buffer using the appropriate buffer descriptor + [[nodiscard]] std::span<const u8> ReadBuffer(std::size_t buffer_index = 0) const; + + /// Helper function to read a copy of a buffer using the appropriate buffer descriptor + [[nodiscard]] std::vector<u8> ReadBufferCopy(std::size_t buffer_index = 0) const; + + /// Helper function to write a buffer using the appropriate buffer descriptor + std::size_t WriteBuffer(const void* buffer, std::size_t size, + std::size_t buffer_index = 0) const; + + /// Helper function to write buffer B + std::size_t WriteBufferB(const void* buffer, std::size_t size, + std::size_t buffer_index = 0) const; + + /// Helper function to write buffer C + std::size_t WriteBufferC(const void* buffer, std::size_t size, + std::size_t buffer_index = 0) const; + + /* Helper function to write a buffer using the appropriate buffer descriptor + * + * @tparam T an arbitrary container that satisfies the + * ContiguousContainer concept in the C++ standard library or a trivially copyable type. + * + * @param data The container/data to write into a buffer. + * @param buffer_index The buffer in particular to write to. + */ + template <typename T, typename = std::enable_if_t<!std::is_pointer_v<T>>> + std::size_t WriteBuffer(const T& data, std::size_t buffer_index = 0) const { + if constexpr (Common::IsContiguousContainer<T>) { + using ContiguousType = typename T::value_type; + static_assert(std::is_trivially_copyable_v<ContiguousType>, + "Container to WriteBuffer must contain trivially copyable objects"); + return WriteBuffer(std::data(data), std::size(data) * sizeof(ContiguousType), + buffer_index); + } else { + static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable"); + return WriteBuffer(&data, sizeof(T), buffer_index); + } + } + + /// Helper function to get the size of the input buffer + [[nodiscard]] std::size_t GetReadBufferSize(std::size_t buffer_index = 0) const; + + /// Helper function to get the size of the output buffer + [[nodiscard]] std::size_t GetWriteBufferSize(std::size_t buffer_index = 0) const; + + /// Helper function to derive the number of elements able to be contained in the read buffer + template <typename T> + [[nodiscard]] std::size_t GetReadBufferNumElements(std::size_t buffer_index = 0) const { + return GetReadBufferSize(buffer_index) / sizeof(T); + } + + /// Helper function to derive the number of elements able to be contained in the write buffer + template <typename T> + [[nodiscard]] std::size_t GetWriteBufferNumElements(std::size_t buffer_index = 0) const { + return GetWriteBufferSize(buffer_index) / sizeof(T); + } + + /// Helper function to test whether the input buffer at buffer_index can be read + [[nodiscard]] bool CanReadBuffer(std::size_t buffer_index = 0) const; + + /// Helper function to test whether the output buffer at buffer_index can be written + [[nodiscard]] bool CanWriteBuffer(std::size_t buffer_index = 0) const; + + [[nodiscard]] Handle GetCopyHandle(std::size_t index) const { + return incoming_copy_handles.at(index); + } + + [[nodiscard]] Handle GetMoveHandle(std::size_t index) const { + return incoming_move_handles.at(index); + } + + void AddMoveObject(Kernel::KAutoObject* object) { + outgoing_move_objects.emplace_back(object); + } + + void AddCopyObject(Kernel::KAutoObject* object) { + outgoing_copy_objects.emplace_back(object); + } + + void AddDomainObject(SessionRequestHandlerPtr object) { + outgoing_domain_objects.emplace_back(std::move(object)); + } + + template <typename T> + std::shared_ptr<T> GetDomainHandler(std::size_t index) const { + return std::static_pointer_cast<T>(GetManager()->DomainHandler(index).lock()); + } + + void SetSessionRequestManager(std::weak_ptr<SessionRequestManager> manager_) { + manager = manager_; + } + + [[nodiscard]] std::string Description() const; + + [[nodiscard]] Kernel::KThread& GetThread() { + return *thread; + } + + [[nodiscard]] std::shared_ptr<SessionRequestManager> GetManager() const { + return manager.lock(); + } + + bool GetIsDeferred() const { + return is_deferred; + } + + void SetIsDeferred(bool is_deferred_ = true) { + is_deferred = is_deferred_; + } + +private: + friend class IPC::ResponseBuilder; + + void ParseCommandBuffer(const Kernel::KHandleTable& handle_table, u32_le* src_cmdbuf, + bool incoming); + + std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf; + Kernel::KServerSession* server_session{}; + Kernel::KThread* thread; + + std::vector<Handle> incoming_move_handles; + std::vector<Handle> incoming_copy_handles; + + std::vector<Kernel::KAutoObject*> outgoing_move_objects; + std::vector<Kernel::KAutoObject*> outgoing_copy_objects; + std::vector<SessionRequestHandlerPtr> outgoing_domain_objects; + + std::optional<IPC::CommandHeader> command_header; + std::optional<IPC::HandleDescriptorHeader> handle_descriptor_header; + std::optional<IPC::DataPayloadHeader> data_payload_header; + std::optional<IPC::DomainMessageHeader> domain_message_header; + std::vector<IPC::BufferDescriptorX> buffer_x_desciptors; + std::vector<IPC::BufferDescriptorABW> buffer_a_desciptors; + std::vector<IPC::BufferDescriptorABW> buffer_b_desciptors; + std::vector<IPC::BufferDescriptorABW> buffer_w_desciptors; + std::vector<IPC::BufferDescriptorC> buffer_c_desciptors; + + u32_le command{}; + u64 pid{}; + u32 write_size{}; + u32 data_payload_offset{}; + u32 handles_offset{}; + u32 domain_offset{}; + + std::weak_ptr<SessionRequestManager> manager{}; + bool is_deferred{false}; + + Kernel::KernelCore& kernel; + Core::Memory::Memory& memory; +}; + +} // namespace Service |