From 1b2203d10292f6de5b9a4228294dfc68d120733e Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 9 Jul 2020 21:30:29 -0700 Subject: [PATCH] kern: implement SvcSendSyncRequest(WithUserBuffer) --- .../arch/arm64/kern_k_process_page_table.hpp | 4 ++ .../mesosphere/kern_k_client_session.hpp | 6 +- .../mesosphere/kern_k_page_table_base.hpp | 2 + .../source/kern_k_client_session.cpp | 46 ++++++++++++ .../source/kern_k_page_table_base.cpp | 70 +++++++++++++++++++ .../source/kern_k_server_session.cpp | 33 +++++++-- .../source/kern_k_session_request.cpp | 27 +++++++ .../libmesosphere/source/svc/kern_svc_ipc.cpp | 50 +++++++++++-- 8 files changed, 228 insertions(+), 10 deletions(-) create mode 100644 libraries/libmesosphere/source/kern_k_session_request.cpp 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 a04e6d958..8b96deef3 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 @@ -96,6 +96,10 @@ namespace ams::kern::arch::arm64 { return this->page_table.MakeAndOpenPageGroup(out, address, num_pages, state_mask, state, perm_mask, perm, attr_mask, attr); } + Result LockForIpcUserBuffer(KPhysicalAddress *out, KProcessAddress address, size_t size) { + return this->page_table.LockForIpcUserBuffer(out, address, size); + } + Result UnlockForIpcUserBuffer(KProcessAddress address, size_t size) { return this->page_table.UnlockForIpcUserBuffer(address, size); } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_client_session.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_client_session.hpp index 98f5ce005..6998af64f 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_client_session.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_client_session.hpp @@ -38,9 +38,11 @@ namespace ams::kern { static void PostDestroy(uintptr_t arg) { /* ... */ } - constexpr const KSession *GetParent() const { return this->parent; } + constexpr KSession *GetParent() const { return this->parent; } + + Result SendSyncRequest(uintptr_t address, size_t size); + Result SendAsyncRequest(KWritableEvent *event, uintptr_t address, size_t size); - /* TODO: More of KClientSession. */ void OnServerClosed(); }; 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 f9e800dab..2da10818a 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp @@ -230,6 +230,7 @@ namespace ams::kern { return this->CheckMemoryState(nullptr, nullptr, nullptr, addr, size, state_mask, state, perm_mask, perm, attr_mask, attr, ignore_attr); } + Result LockMemoryAndOpen(KPageGroup *out_pg, KPhysicalAddress *out_paddr, KProcessAddress addr, size_t size, u32 state_mask, u32 state, u32 perm_mask, u32 perm, u32 attr_mask, u32 attr, KMemoryPermission new_perm, u32 lock_attr); Result UnlockMemory(KProcessAddress addr, size_t size, u32 state_mask, u32 state, u32 perm_mask, u32 perm, u32 attr_mask, u32 attr, KMemoryPermission new_perm, u32 lock_attr, const KPageGroup *pg); Result QueryInfoImpl(KMemoryInfo *out_info, ams::svc::PageInfo *out_page, KProcessAddress address) const; @@ -277,6 +278,7 @@ namespace ams::kern { Result MakeAndOpenPageGroup(KPageGroup *out, KProcessAddress address, size_t num_pages, u32 state_mask, u32 state, u32 perm_mask, u32 perm, u32 attr_mask, u32 attr); + Result LockForIpcUserBuffer(KPhysicalAddress *out, KProcessAddress address, size_t size); Result UnlockForIpcUserBuffer(KProcessAddress address, size_t size); public: KProcessAddress GetAddressSpaceStart() const { return this->address_space_start; } diff --git a/libraries/libmesosphere/source/kern_k_client_session.cpp b/libraries/libmesosphere/source/kern_k_client_session.cpp index 76c8aff66..7f164a5c0 100644 --- a/libraries/libmesosphere/source/kern_k_client_session.cpp +++ b/libraries/libmesosphere/source/kern_k_client_session.cpp @@ -28,4 +28,50 @@ namespace ams::kern { MESOSPHERE_ASSERT_THIS(); } + Result KClientSession::SendSyncRequest(uintptr_t address, size_t size) { + MESOSPHERE_ASSERT_THIS(); + + /* Create a session request. */ + KSessionRequest *request = KSessionRequest::Create(); + R_UNLESS(request != nullptr, svc::ResultOutOfResource()); + ON_SCOPE_EXIT { request->Close(); }; + + /* Initialize the request. */ + request->Initialize(nullptr, address, size); + + /* Send the request. */ + { + KScopedSchedulerLock sl; + + GetCurrentThread().SetSyncedObject(nullptr, ResultSuccess()); + + R_TRY(this->parent->OnRequest(request)); + } + + /* Get the result. */ + KSynchronizationObject *dummy; + return GetCurrentThread().GetWaitResult(std::addressof(dummy)); + } + + Result KClientSession::SendAsyncRequest(KWritableEvent *event, uintptr_t address, size_t size) { + MESOSPHERE_ASSERT_THIS(); + + /* Create a session request. */ + KSessionRequest *request = KSessionRequest::Create(); + R_UNLESS(request != nullptr, svc::ResultOutOfResource()); + ON_SCOPE_EXIT { request->Close(); }; + + /* Initialize the request. */ + request->Initialize(event, address, size); + + /* Send the request. */ + { + KScopedSchedulerLock sl; + + R_TRY(this->parent->OnRequest(request)); + } + + return ResultSuccess(); + } + } diff --git a/libraries/libmesosphere/source/kern_k_page_table_base.cpp b/libraries/libmesosphere/source/kern_k_page_table_base.cpp index 9d4fc98fe..bfb4b0f4d 100644 --- a/libraries/libmesosphere/source/kern_k_page_table_base.cpp +++ b/libraries/libmesosphere/source/kern_k_page_table_base.cpp @@ -439,6 +439,67 @@ namespace ams::kern { return ResultSuccess(); } + Result KPageTableBase::LockMemoryAndOpen(KPageGroup *out_pg, KPhysicalAddress *out_paddr, KProcessAddress addr, size_t size, u32 state_mask, u32 state, u32 perm_mask, u32 perm, u32 attr_mask, u32 attr, KMemoryPermission new_perm, u32 lock_attr) { + /* Validate basic preconditions. */ + MESOSPHERE_ASSERT((attr_mask & lock_attr) == lock_attr); + MESOSPHERE_ASSERT((attr & lock_attr) == lock_attr); + + /* Validate the lock request. */ + const size_t num_pages = size / PageSize; + R_UNLESS(this->Contains(addr, size), svc::ResultInvalidCurrentMemory()); + + /* Lock the table. */ + KScopedLightLock lk(this->general_lock); + + /* Check that the output page group is empty, if it exists. */ + if (out_pg) { + MESOSPHERE_ASSERT(out_pg->GetNumPages() == 0); + } + + /* Check the state. */ + KMemoryState old_state; + KMemoryPermission old_perm; + KMemoryAttribute old_attr; + R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm), std::addressof(old_attr), addr, size, state_mask | KMemoryState_FlagReferenceCounted, state | KMemoryState_FlagReferenceCounted, perm_mask, perm, attr_mask, attr)); + + /* Get the physical address, if we're supposed to. */ + if (out_paddr != nullptr) { + MESOSPHERE_ABORT_UNLESS(this->GetPhysicalAddress(out_paddr, addr)); + } + + /* Make the page group, if we're supposed to. */ + if (out_pg != nullptr) { + R_TRY(this->MakePageGroup(*out_pg, addr, num_pages)); + } + + /* Create an update allocator. */ + KMemoryBlockManagerUpdateAllocator allocator(this->memory_block_slab_manager); + R_TRY(allocator.GetResult()); + + /* Decide on new perm and attr. */ + new_perm = (new_perm != KMemoryPermission_None) ? new_perm : old_perm; + KMemoryAttribute new_attr = static_cast(old_attr | lock_attr); + + /* Update permission, if we need to. */ + if (new_perm != old_perm) { + /* We're going to perform an update, so create a helper. */ + KScopedPageTableUpdater updater(this); + + const KPageProperties properties = { new_perm, false, (old_attr & KMemoryAttribute_Uncached) != 0, true }; + R_TRY(this->Operate(updater.GetPageList(), addr, num_pages, Null, false, properties, OperationType_ChangePermissions, false)); + } + + /* Apply the memory block updates. */ + this->memory_block_manager.Update(std::addressof(allocator), addr, num_pages, old_state, new_perm, new_attr); + + /* If we have an output group, open. */ + if (out_pg) { + out_pg->Open(); + } + + return ResultSuccess(); + } + Result KPageTableBase::UnlockMemory(KProcessAddress addr, size_t size, u32 state_mask, u32 state, u32 perm_mask, u32 perm, u32 attr_mask, u32 attr, KMemoryPermission new_perm, u32 lock_attr, const KPageGroup *pg) { /* Validate basic preconditions. */ MESOSPHERE_ASSERT((attr_mask & lock_attr) == lock_attr); @@ -1213,6 +1274,15 @@ namespace ams::kern { return ResultSuccess(); } + Result KPageTableBase::LockForIpcUserBuffer(KPhysicalAddress *out, KProcessAddress address, size_t size) { + return this->LockMemoryAndOpen(nullptr, out, address, size, + KMemoryState_FlagCanIpcUserBuffer, KMemoryState_FlagCanIpcUserBuffer, + KMemoryPermission_All, KMemoryPermission_UserReadWrite, + KMemoryAttribute_All, KMemoryAttribute_None, + static_cast(KMemoryPermission_NotMapped | KMemoryPermission_KernelReadWrite), + KMemoryAttribute_AnyLocked | KMemoryAttribute_Locked); + } + Result KPageTableBase::UnlockForIpcUserBuffer(KProcessAddress address, size_t size) { return this->UnlockMemory(address, size, KMemoryState_FlagCanIpcUserBuffer, KMemoryState_FlagCanIpcUserBuffer, diff --git a/libraries/libmesosphere/source/kern_k_server_session.cpp b/libraries/libmesosphere/source/kern_k_server_session.cpp index 49f095f9f..c72bf9af8 100644 --- a/libraries/libmesosphere/source/kern_k_server_session.cpp +++ b/libraries/libmesosphere/source/kern_k_server_session.cpp @@ -44,10 +44,6 @@ namespace ams::kern { this->parent->Close(); } - Result KServerSession::OnRequest(KSessionRequest *request) { - MESOSPHERE_UNIMPLEMENTED(); - } - Result KServerSession::ReceiveRequest(uintptr_t message, uintptr_t buffer_size, KPhysicalAddress message_paddr) { MESOSPHERE_UNIMPLEMENTED(); } @@ -56,6 +52,35 @@ namespace ams::kern { MESOSPHERE_UNIMPLEMENTED(); } + Result KServerSession::OnRequest(KSessionRequest *request) { + MESOSPHERE_ASSERT_THIS(); + MESOSPHERE_ASSERT(KScheduler::IsSchedulerLockedByCurrentThread()); + + /* Ensure that we can handle new requests. */ + R_UNLESS(!this->parent->IsServerClosed(), svc::ResultSessionClosed()); + + /* If there's no event, this is synchronous, so we should check for thread termination. */ + if (request->GetEvent() == nullptr) { + KThread *thread = request->GetThread(); + R_UNLESS(!thread->IsTerminationRequested(), svc::ResultTerminationRequested()); + thread->SetState(KThread::ThreadState_Waiting); + } + + /* Get whether we're empty. */ + const bool was_empty = this->request_list.empty(); + + /* Add the request to the list. */ + request->Open(); + this->request_list.push_back(*request); + + /* If we were empty, signal. */ + if (was_empty) { + this->NotifyAvailable(); + } + + return ResultSuccess(); + } + bool KServerSession::IsSignaledImpl() const { MESOSPHERE_ASSERT_THIS(); MESOSPHERE_ASSERT(KScheduler::IsSchedulerLockedByCurrentThread()); diff --git a/libraries/libmesosphere/source/kern_k_session_request.cpp b/libraries/libmesosphere/source/kern_k_session_request.cpp new file mode 100644 index 000000000..8799bedb3 --- /dev/null +++ b/libraries/libmesosphere/source/kern_k_session_request.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +namespace ams::kern { + + void KSessionRequest::SessionMappings::Finalize() { + if (this->mappings) { + KPageBuffer::Free(reinterpret_cast(this->mappings)); + this->mappings = nullptr; + } + } + +} diff --git a/libraries/libmesosphere/source/svc/kern_svc_ipc.cpp b/libraries/libmesosphere/source/svc/kern_svc_ipc.cpp index f6d0c9c51..803e7b5fc 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_ipc.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_ipc.cpp @@ -21,6 +21,19 @@ namespace ams::kern::svc { namespace { + ALWAYS_INLINE Result SendSyncRequestImpl(uintptr_t message, size_t buffer_size, ams::svc::Handle session_handle) { + /* Get the client session. */ + KScopedAutoObject session = GetCurrentProcess().GetHandleTable().GetObject(session_handle); + R_UNLESS(session.IsNotNull(), svc::ResultInvalidHandle()); + + /* Get the parent, and persist a reference to it until we're done. */ + KScopedAutoObject parent = session->GetParent(); + MESOSPHERE_ASSERT(parent.IsNotNull()); + + /* Send the request. */ + return session->SendSyncRequest(message, buffer_size); + } + ALWAYS_INLINE Result ReplyAndReceiveImpl(int32_t *out_index, uintptr_t message, size_t buffer_size, KPhysicalAddress message_paddr, KSynchronizationObject **objs, int32_t num_objects, ams::svc::Handle reply_target, int64_t timeout_ns) { /* Reply to the target, if one is specified. */ if (reply_target != ams::svc::InvalidHandle) { @@ -102,6 +115,35 @@ namespace ams::kern::svc { return ReplyAndReceiveImpl(out_index, message, buffer_size, message_paddr, objs, num_handles, reply_target, timeout_ns); } + ALWAYS_INLINE Result SendSyncRequest(ams::svc::Handle session_handle) { + return SendSyncRequestImpl(0, 0, session_handle); + } + + ALWAYS_INLINE Result SendSyncRequestWithUserBuffer(uintptr_t message, size_t buffer_size, ams::svc::Handle session_handle) { + /* Validate that the message buffer is page aligned and does not overflow. */ + R_UNLESS(util::IsAligned(message, PageSize), svc::ResultInvalidAddress()); + R_UNLESS(buffer_size > 0, svc::ResultInvalidSize()); + R_UNLESS(util::IsAligned(buffer_size, PageSize), svc::ResultInvalidSize()); + R_UNLESS(message < message + buffer_size, svc::ResultInvalidCurrentMemory()); + + /* Get the process page table. */ + auto &page_table = GetCurrentProcess().GetPageTable(); + + /* Lock the mesage buffer. */ + R_TRY(page_table.LockForIpcUserBuffer(nullptr, message, buffer_size)); + + /* Ensure that even if we fail, we unlock the message buffer when done. */ + auto unlock_guard = SCOPE_GUARD { page_table.UnlockForIpcUserBuffer(message, buffer_size); }; + + /* Send the request. */ + MESOSPHERE_ASSERT(message != 0); + R_TRY(SendSyncRequestImpl(message, buffer_size, session_handle)); + + /* We sent the request successfully, so cancel our guard and check the unlock result. */ + unlock_guard.Cancel(); + return page_table.UnlockForIpcUserBuffer(message, buffer_size); + } + ALWAYS_INLINE Result ReplyAndReceive(int32_t *out_index, KUserPointer handles, int32_t num_handles, ams::svc::Handle reply_target, int64_t timeout_ns) { return ReplyAndReceiveImpl(out_index, 0, 0, Null, handles, num_handles, reply_target, timeout_ns); } @@ -111,11 +153,11 @@ namespace ams::kern::svc { /* ============================= 64 ABI ============================= */ Result SendSyncRequest64(ams::svc::Handle session_handle) { - MESOSPHERE_PANIC("Stubbed SvcSendSyncRequest64 was called."); + return SendSyncRequest(session_handle); } Result SendSyncRequestWithUserBuffer64(ams::svc::Address message_buffer, ams::svc::Size message_buffer_size, ams::svc::Handle session_handle) { - MESOSPHERE_PANIC("Stubbed SvcSendSyncRequestWithUserBuffer64 was called."); + return SendSyncRequestWithUserBuffer(message_buffer, message_buffer_size, session_handle); } Result SendAsyncRequestWithUserBuffer64(ams::svc::Handle *out_event_handle, ams::svc::Address message_buffer, ams::svc::Size message_buffer_size, ams::svc::Handle session_handle) { @@ -133,11 +175,11 @@ namespace ams::kern::svc { /* ============================= 64From32 ABI ============================= */ Result SendSyncRequest64From32(ams::svc::Handle session_handle) { - MESOSPHERE_PANIC("Stubbed SvcSendSyncRequest64From32 was called."); + return SendSyncRequest(session_handle); } Result SendSyncRequestWithUserBuffer64From32(ams::svc::Address message_buffer, ams::svc::Size message_buffer_size, ams::svc::Handle session_handle) { - MESOSPHERE_PANIC("Stubbed SvcSendSyncRequestWithUserBuffer64From32 was called."); + return SendSyncRequestWithUserBuffer(message_buffer, message_buffer_size, session_handle); } Result SendAsyncRequestWithUserBuffer64From32(ams::svc::Handle *out_event_handle, ams::svc::Address message_buffer, ams::svc::Size message_buffer_size, ams::svc::Handle session_handle) {