/* * 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::svc { /* ============================= Common ============================= */ 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) { /* TODO */ MESOSPHERE_UNIMPLEMENTED(); } /* Receive a message. */ { /* Convert the timeout from nanoseconds to ticks. */ /* NOTE: Nintendo does not use this conversion logic in WaitSynchronization... */ s64 timeout; if (timeout_ns > 0) { const ams::svc::Tick offset_tick(TimeSpan::FromNanoSeconds(timeout_ns)); if (AMS_LIKELY(offset_tick > 0)) { timeout = KHardwareTimer::GetTick() + offset_tick + 2; if (AMS_UNLIKELY(timeout <= 0)) { timeout = std::numeric_limits::max(); } } else { timeout = std::numeric_limits::max(); } } else { timeout = timeout_ns; } /* Wait for a message. */ while (true) { s32 index; Result result = Kernel::GetSynchronization().Wait(std::addressof(index), objs, num_objects, timeout); if (svc::ResultTimedOut::Includes(result)) { return result; } if (R_SUCCEEDED(result)) { KServerSession *session = objs[index]->DynamicCast(); if (session != nullptr) { result = session->ReceiveRequest(message, buffer_size, message_paddr); if (svc::ResultNotFound::Includes(result)) { continue; } } } *out_index = index; return result; } } } ALWAYS_INLINE Result ReplyAndReceiveImpl(int32_t *out_index, uintptr_t message, size_t buffer_size, KPhysicalAddress message_paddr, KUserPointer user_handles, int32_t num_handles, ams::svc::Handle reply_target, int64_t timeout_ns) { /* Ensure number of handles is valid. */ R_UNLESS(0 <= num_handles && num_handles <= ams::svc::ArgumentHandleCountMax, svc::ResultOutOfRange()); /* Get the synchronization context. */ auto &handle_table = GetCurrentProcess().GetHandleTable(); KSynchronizationObject **objs = GetCurrentThread().GetSynchronizationObjectBuffer(); ams::svc::Handle *handles = GetCurrentThread().GetHandleBuffer(); /* Copy user handles. */ if (num_handles > 0) { /* Ensure that we can try to get the handles. */ R_UNLESS(GetCurrentProcess().GetPageTable().Contains(KProcessAddress(user_handles.GetUnsafePointer()), num_handles * sizeof(ams::svc::Handle)), svc::ResultInvalidPointer()); /* Get the handles. */ R_TRY(user_handles.CopyArrayTo(handles, num_handles)); /* Convert the handles to objects. */ R_UNLESS(handle_table.GetMultipleObjects(objs, handles, num_handles), svc::ResultInvalidHandle()); } /* Ensure handles are closed when we're done. */ ON_SCOPE_EXIT { for (auto i = 0; i < num_handles; ++i) { objs[i]->Close(); } }; 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); } } /* ============================= 64 ABI ============================= */ Result SendSyncRequest64(ams::svc::Handle session_handle) { return SendSyncRequest(session_handle); } Result SendSyncRequestWithUserBuffer64(ams::svc::Address message_buffer, ams::svc::Size message_buffer_size, ams::svc::Handle session_handle) { 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) { MESOSPHERE_PANIC("Stubbed SvcSendAsyncRequestWithUserBuffer64 was called."); } Result ReplyAndReceive64(int32_t *out_index, KUserPointer handles, int32_t num_handles, ams::svc::Handle reply_target, int64_t timeout_ns) { return ReplyAndReceive(out_index, handles, num_handles, reply_target, timeout_ns); } Result ReplyAndReceiveWithUserBuffer64(int32_t *out_index, ams::svc::Address message_buffer, ams::svc::Size message_buffer_size, KUserPointer handles, int32_t num_handles, ams::svc::Handle reply_target, int64_t timeout_ns) { MESOSPHERE_PANIC("Stubbed SvcReplyAndReceiveWithUserBuffer64 was called."); } /* ============================= 64From32 ABI ============================= */ Result SendSyncRequest64From32(ams::svc::Handle session_handle) { return SendSyncRequest(session_handle); } Result SendSyncRequestWithUserBuffer64From32(ams::svc::Address message_buffer, ams::svc::Size message_buffer_size, ams::svc::Handle session_handle) { 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) { MESOSPHERE_PANIC("Stubbed SvcSendAsyncRequestWithUserBuffer64From32 was called."); } Result ReplyAndReceive64From32(int32_t *out_index, KUserPointer handles, int32_t num_handles, ams::svc::Handle reply_target, int64_t timeout_ns) { return ReplyAndReceive(out_index, handles, num_handles, reply_target, timeout_ns); } Result ReplyAndReceiveWithUserBuffer64From32(int32_t *out_index, ams::svc::Address message_buffer, ams::svc::Size message_buffer_size, KUserPointer handles, int32_t num_handles, ams::svc::Handle reply_target, int64_t timeout_ns) { MESOSPHERE_PANIC("Stubbed SvcReplyAndReceiveWithUserBuffer64From32 was called."); } }