mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2024-12-18 00:12:03 +00:00
kern: implement SvcSendSyncRequest(WithUserBuffer)
This commit is contained in:
parent
4f12449acf
commit
1b2203d102
8 changed files with 228 additions and 10 deletions
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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<KMemoryAttribute>(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<KPhysicalAddress>, 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>(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,
|
||||
|
|
|
@ -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());
|
||||
|
|
27
libraries/libmesosphere/source/kern_k_session_request.cpp
Normal file
27
libraries/libmesosphere/source/kern_k_session_request.cpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <mesosphere.hpp>
|
||||
|
||||
namespace ams::kern {
|
||||
|
||||
void KSessionRequest::SessionMappings::Finalize() {
|
||||
if (this->mappings) {
|
||||
KPageBuffer::Free(reinterpret_cast<KPageBuffer *>(this->mappings));
|
||||
this->mappings = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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<KClientSession>(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<const ams::svc::Handle *> handles, int32_t num_handles, ams::svc::Handle reply_target, int64_t timeout_ns) {
|
||||
return ReplyAndReceiveImpl(out_index, 0, 0, Null<KPhysicalAddress>, 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) {
|
||||
|
|
Loading…
Reference in a new issue