From 804aa0e55d387f789fc381ee06e1f3192feda59e Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Fri, 10 Jul 2020 08:49:10 -0700 Subject: [PATCH] ipc: add MapAlias processing logic for Receive --- .../arch/arm64/kern_k_process_page_table.hpp | 11 +++ .../mesosphere/kern_k_page_table_base.hpp | 4 ++ .../mesosphere/kern_k_session_request.hpp | 18 +++++ .../source/kern_k_page_table_base.cpp | 12 ++++ .../source/kern_k_server_session.cpp | 71 ++++++++++++++++++- .../source/kern_k_session_request.cpp | 41 +++++++++++ 6 files changed, 155 insertions(+), 2 deletions(-) diff --git a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_process_page_table.hpp b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_process_page_table.hpp index 774e0da7e..7bc77a147 100644 --- a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_process_page_table.hpp +++ b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_process_page_table.hpp @@ -128,6 +128,17 @@ namespace ams::kern::arch::arm64 { return this->page_table.CopyMemoryFromLinearToLinearWithoutCheckDestination(dst_page_table.page_table, dst_addr, size, dst_state_mask, dst_state, dst_test_perm, dst_attr_mask, dst_attr, src_addr, src_state_mask, src_state, src_test_perm, src_attr_mask, src_attr); } + Result SetupForIpc(KProcessAddress *out_dst_addr, size_t size, KProcessAddress src_addr, KProcessPageTable &src_page_table, KMemoryPermission test_perm, KMemoryState dst_state, bool send) { + return this->page_table.SetupForIpc(out_dst_addr, size, src_addr, src_page_table.page_table, test_perm, dst_state, send); + } + + Result CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state, KProcess *server_process) { + return this->page_table.CleanupForIpcServer(address, size, dst_state, server_process); + } + + Result CleanupForIpcClient(KProcessAddress address, size_t size, KMemoryState dst_state) { + return this->page_table.CleanupForIpcClient(address, size, dst_state); + } bool GetPhysicalAddress(KPhysicalAddress *out, KProcessAddress address) const { return this->page_table.GetPhysicalAddress(out, address); diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp index 5c58b54d1..0c1764895 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp @@ -287,6 +287,10 @@ namespace ams::kern { Result CopyMemoryFromKernelToLinear(KProcessAddress dst_addr, size_t size, u32 dst_state_mask, u32 dst_state, KMemoryPermission dst_test_perm, u32 dst_attr_mask, u32 dst_attr, KProcessAddress src_addr); Result CopyMemoryFromLinearToLinear(KPageTableBase &dst_page_table, KProcessAddress dst_addr, size_t size, u32 dst_state_mask, u32 dst_state, KMemoryPermission dst_test_perm, u32 dst_attr_mask, u32 dst_attr, KProcessAddress src_addr, u32 src_state_mask, u32 src_state, KMemoryPermission src_test_perm, u32 src_attr_mask, u32 src_attr); Result CopyMemoryFromLinearToLinearWithoutCheckDestination(KPageTableBase &dst_page_table, KProcessAddress dst_addr, size_t size, u32 dst_state_mask, u32 dst_state, KMemoryPermission dst_test_perm, u32 dst_attr_mask, u32 dst_attr, KProcessAddress src_addr, u32 src_state_mask, u32 src_state, KMemoryPermission src_test_perm, u32 src_attr_mask, u32 src_attr); + + Result SetupForIpc(KProcessAddress *out_dst_addr, size_t size, KProcessAddress src_addr, KPageTableBase &src_page_table, KMemoryPermission test_perm, KMemoryState dst_state, bool send); + Result CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state, KProcess *server_process); + Result CleanupForIpcClient(KProcessAddress address, size_t size, KMemoryState dst_state); public: KProcessAddress GetAddressSpaceStart() const { return this->address_space_start; } KProcessAddress GetHeapRegionStart() const { return this->heap_region_start; } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_session_request.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_session_request.hpp index 7696e0024..284b7a234 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_session_request.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_session_request.hpp @@ -66,7 +66,13 @@ namespace ams::kern { size_t GetReceiveCount() const { return this->num_recv; } size_t GetExchangeCount() const { return this->num_exch; } + Result PushSend(KProcessAddress client, KProcessAddress server, size_t size, KMemoryState state); + Result PushReceive(KProcessAddress client, KProcessAddress server, size_t size, KMemoryState state); + Result PushExchange(KProcessAddress client, KProcessAddress server, size_t size, KMemoryState state); + /* TODO: More functionality. */ + private: + Result PushMap(KProcessAddress client, KProcessAddress server, size_t size, KMemoryState state, size_t index); }; private: SessionMappings mappings; @@ -140,6 +146,18 @@ namespace ams::kern { size_t GetReceiveCount() const { return this->mappings.GetReceiveCount(); } size_t GetExchangeCount() const { return this->mappings.GetExchangeCount(); } + Result PushSend(KProcessAddress client, KProcessAddress server, size_t size, KMemoryState state) { + return this->mappings.PushSend(client, server, size, state); + } + + Result PushReceive(KProcessAddress client, KProcessAddress server, size_t size, KMemoryState state) { + return this->mappings.PushReceive(client, server, size, state); + } + + Result PushExchange(KProcessAddress client, KProcessAddress server, size_t size, KMemoryState state) { + return this->mappings.PushExchange(client, server, size, state); + } + /* TODO: More functionality. */ }; diff --git a/libraries/libmesosphere/source/kern_k_page_table_base.cpp b/libraries/libmesosphere/source/kern_k_page_table_base.cpp index 95077461f..ede916c0f 100644 --- a/libraries/libmesosphere/source/kern_k_page_table_base.cpp +++ b/libraries/libmesosphere/source/kern_k_page_table_base.cpp @@ -1316,4 +1316,16 @@ namespace ams::kern { MESOSPHERE_UNIMPLEMENTED(); } + Result KPageTableBase::SetupForIpc(KProcessAddress *out_dst_addr, size_t size, KProcessAddress src_addr, KPageTableBase &src_page_table, KMemoryPermission test_perm, KMemoryState dst_state, bool send) { + MESOSPHERE_UNIMPLEMENTED(); + } + + Result KPageTableBase::CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state, KProcess *server_process) { + MESOSPHERE_UNIMPLEMENTED(); + } + + Result KPageTableBase::CleanupForIpcClient(KProcessAddress address, size_t size, KMemoryState dst_state) { + MESOSPHERE_UNIMPLEMENTED(); + } + } diff --git a/libraries/libmesosphere/source/kern_k_server_session.cpp b/libraries/libmesosphere/source/kern_k_server_session.cpp index 062fc20c3..6219dada4 100644 --- a/libraries/libmesosphere/source/kern_k_server_session.cpp +++ b/libraries/libmesosphere/source/kern_k_server_session.cpp @@ -205,7 +205,7 @@ namespace ams::kern { const size_t recv_size = src_desc.GetSize(); uintptr_t recv_pointer = 0; - /* There's nothing to do if there's no size. */ + /* Process the buffer, if it has a size. */ if (recv_size > 0) { /* If using indexing, set index. */ if (dst_recv_list.IsIndex()) { @@ -240,6 +240,64 @@ namespace ams::kern { return ResultSuccess(); } + constexpr ALWAYS_INLINE Result GetMapAliasMemoryState(KMemoryState &out, ipc::MessageBuffer::MapAliasDescriptor::Attribute attr) { + switch (attr) { + case ipc::MessageBuffer::MapAliasDescriptor::Attribute_Ipc: out = KMemoryState_Ipc; break; + case ipc::MessageBuffer::MapAliasDescriptor::Attribute_NonSecureIpc: out = KMemoryState_NonSecureIpc; break; + case ipc::MessageBuffer::MapAliasDescriptor::Attribute_NonDeviceIpc: out = KMemoryState_NonDeviceIpc; break; + default: return svc::ResultInvalidCombination(); + } + + return ResultSuccess(); + } + + ALWAYS_INLINE Result ProcessReceiveMessageMapAliasDescriptors(int &offset, KProcessPageTable &dst_page_table, KProcessPageTable &src_page_table, const ipc::MessageBuffer &dst_msg, const ipc::MessageBuffer &src_msg, KSessionRequest *request, KMemoryPermission perm, bool send) { + /* Get the offset at the start of processing. */ + const int cur_offset = offset; + + /* Get the map alias descriptor. */ + ipc::MessageBuffer::MapAliasDescriptor src_desc(src_msg, cur_offset); + offset += ipc::MessageBuffer::MapAliasDescriptor::GetDataSize() / sizeof(u32); + + /* Extract address/size. */ + const KProcessAddress src_address = src_desc.GetAddress(); + const size_t size = src_desc.GetSize(); + KProcessAddress dst_address = 0; + + /* Determine the result memory state. */ + KMemoryState dst_state; + R_TRY(GetMapAliasMemoryState(dst_state, src_desc.GetAttribute())); + + /* Process the buffer, if it has a size. */ + if (size > 0) { + /* Set up the source pages for ipc. */ + R_TRY(dst_page_table.SetupForIpc(std::addressof(dst_address), size, src_address, src_page_table, perm, dst_state, send)); + + /* Ensure that we clean up on failure. */ + auto setup_guard = SCOPE_GUARD { + dst_page_table.CleanupForIpcServer(dst_address, size, dst_state, request->GetServerProcess()); + src_page_table.CleanupForIpcClient(src_address, size, dst_state); + }; + + /* Push the appropriate mapping. */ + if (perm == KMemoryPermission_UserRead) { + R_TRY(request->PushSend(src_address, dst_address, size, dst_state)); + } else if (send) { + R_TRY(request->PushExchange(src_address, dst_address, size, dst_state)); + } else { + R_TRY(request->PushReceive(src_address, dst_address, size, dst_state)); + } + + /* We successfully pushed the mapping. */ + setup_guard.Cancel(); + } + + /* Set the output descriptor. */ + dst_msg.Set(cur_offset, ipc::MessageBuffer::MapAliasDescriptor(GetVoidPointer(dst_address), size, src_desc.GetAttribute())); + + return ResultSuccess(); + } + ALWAYS_INLINE Result ReceiveMessage(bool &recv_list_broken, uintptr_t dst_message_buffer, size_t dst_buffer_size, KPhysicalAddress dst_message_paddr, KThread &src_thread, uintptr_t src_message_buffer, size_t src_buffer_size, KServerSession *session, KSessionRequest *request) { /* Prepare variables for receive. */ const KThread &dst_thread = GetCurrentThread(); @@ -345,7 +403,16 @@ namespace ams::kern { /* Process any map alias buffers. */ for (auto i = 0; i < src_header.GetMapAliasCount(); ++i) { - MESOSPHERE_UNIMPLEMENTED(); + /* After we process, make sure we track whether the receive list is broken. */ + ON_SCOPE_EXIT { if (offset > dst_recv_list_idx) { recv_list_broken = true; } }; + + /* We process in order send, recv, exch. Buffers after send (recv/exch) are ReadWrite. */ + const KMemoryPermission perm = (i >= src_header.GetSendCount()) ? KMemoryPermission_UserReadWrite : KMemoryPermission_UserRead; + + /* Buffer is send if it is send or exch. */ + const bool send = (i < src_header.GetSendCount()) || (i >= src_header.GetSendCount() + src_header.GetReceiveCount()); + + R_TRY(ProcessReceiveMessageMapAliasDescriptors(offset, dst_page_table, src_page_table, dst_msg, src_msg, request, perm, send)); } /* Process any raw data. */ diff --git a/libraries/libmesosphere/source/kern_k_session_request.cpp b/libraries/libmesosphere/source/kern_k_session_request.cpp index 8799bedb3..8b20c52f5 100644 --- a/libraries/libmesosphere/source/kern_k_session_request.cpp +++ b/libraries/libmesosphere/source/kern_k_session_request.cpp @@ -17,6 +17,47 @@ namespace ams::kern { + Result KSessionRequest::SessionMappings::PushMap(KProcessAddress client, KProcessAddress server, size_t size, KMemoryState state, size_t index) { + /* At most 15 buffers of each type (4-bit descriptor counts). */ + MESOSPHERE_ASSERT(index < ((1ul << 4) - 1) * 3); + + /* Get the mapping. */ + Mapping *mapping; + if (index < NumStaticMappings) { + mapping = std::addressof(this->static_mappings[index]); + } else { + /* Allocate a page for the extra mappings. */ + if (this->mappings == nullptr) { + KPageBuffer *page_buffer = KPageBuffer::Allocate(); + R_UNLESS(page_buffer != nullptr, svc::ResultOutOfMemory()); + + this->mappings = reinterpret_cast(page_buffer); + } + + mapping = std::addressof(this->mappings[index - NumStaticMappings]); + } + + /* Set the mapping. */ + mapping->Set(client, server, size, state); + + return ResultSuccess(); + } + + Result KSessionRequest::SessionMappings::PushSend(KProcessAddress client, KProcessAddress server, size_t size, KMemoryState state) { + MESOSPHERE_ASSERT(this->num_recv == 0); + MESOSPHERE_ASSERT(this->num_exch == 0); + return this->PushMap(client, server, size, state, this->num_send++); + } + + Result KSessionRequest::SessionMappings::PushReceive(KProcessAddress client, KProcessAddress server, size_t size, KMemoryState state) { + MESOSPHERE_ASSERT(this->num_exch == 0); + return this->PushMap(client, server, size, state, this->num_send + this->num_recv++); + } + + Result KSessionRequest::SessionMappings::PushExchange(KProcessAddress client, KProcessAddress server, size_t size, KMemoryState state) { + return this->PushMap(client, server, size, state, this->num_send + this->num_recv + this->num_exch++); + } + void KSessionRequest::SessionMappings::Finalize() { if (this->mappings) { KPageBuffer::Free(reinterpret_cast(this->mappings));