From 36eb78a3ceaae2242daab6af24e9815dace601c3 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sat, 18 Jul 2020 20:03:27 -0700 Subject: [PATCH] kern: implement SvcDebugActiveProcess, svcGetDebugEvent, SvcWaitProcessWideKeyAtomic --- .../mesosphere/arch/arm64/kern_k_debug.hpp | 5 + .../mesosphere/kern_k_capabilities.hpp | 8 + .../include/mesosphere/kern_k_debug_base.hpp | 30 ++ .../include/mesosphere/kern_k_event_info.hpp | 45 ++- .../include/mesosphere/kern_k_process.hpp | 26 ++ .../include/mesosphere/kern_k_thread.hpp | 18 +- .../mesosphere/svc/kern_svc_results.hpp | 1 + .../source/kern_k_condition_variable.cpp | 80 +++- .../source/kern_k_debug_base.cpp | 370 ++++++++++++++++++ .../source/kern_k_interrupt_task_manager.cpp | 14 +- .../libmesosphere/source/kern_k_process.cpp | 27 ++ .../svc/kern_svc_condition_variable.cpp | 34 +- .../source/svc/kern_svc_debug.cpp | 64 ++- .../svc/kern_svc_device_address_space.cpp | 9 +- .../source/svc/kern_svc_interrupt_event.cpp | 9 +- .../source/svc/kern_svc_thread.cpp | 11 +- .../include/vapours/results/svc_results.hpp | 1 + 17 files changed, 728 insertions(+), 24 deletions(-) create mode 100644 libraries/libmesosphere/source/kern_k_debug_base.cpp diff --git a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_debug.hpp b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_debug.hpp index 8bf3c8f6f..0a1aa42bc 100644 --- a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_debug.hpp +++ b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_debug.hpp @@ -30,6 +30,11 @@ namespace ams::kern::arch::arm64 { class KDebug final : public KAutoObjectWithSlabHeapAndContainer { MESOSPHERE_AUTOOBJECT_TRAITS(KDebug, KSynchronizationObject); public: + explicit KDebug() { /* ... */ } + virtual ~KDebug() { /* ... */ } + + static void PostDestroy(uintptr_t arg) { /* ... */ } + /* TODO: This is a placeholder definition. */ }; diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_capabilities.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_capabilities.hpp index 7aab28a84..3022ea502 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_capabilities.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_capabilities.hpp @@ -283,6 +283,14 @@ namespace ams::kern { } } + constexpr bool IsPermittedDebug() const { + return this->debug_capabilities.Get(); + } + + constexpr bool CanForceDebug() const { + return this->debug_capabilities.Get(); + } + /* TODO: Member functions. */ }; diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_debug_base.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_debug_base.hpp index b1515b379..18127f0ed 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_debug_base.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_debug_base.hpp @@ -16,12 +16,42 @@ #pragma once #include #include +#include +#include +#include namespace ams::kern { class KDebugBase : public KSynchronizationObject { + protected: + using DebugEventList = util::IntrusiveListBaseTraits::ListType; + private: + DebugEventList event_info_list; + u32 continue_flags; + KProcess *process; + KLightLock lock; + KProcess::State old_process_state; public: + explicit KDebugBase() { /* ... */ } + virtual ~KDebugBase() { /* ... */ } + public: + void Initialize(); + Result Attach(KProcess *process); + + Result GetDebugEventInfo(ams::svc::lp64::DebugEventInfo *out); + Result GetDebugEventInfo(ams::svc::ilp32::DebugEventInfo *out); + /* TODO: This is a placeholder definition. */ + private: + KScopedAutoObject GetProcess(); + + void PushDebugEvent(ams::svc::DebugEvent event, uintptr_t param0 = 0, uintptr_t param1 = 0, uintptr_t param2 = 0, uintptr_t param3 = 0, uintptr_t param4 = 0); + void EnqueueDebugEventInfo(KEventInfo *info); + public: + virtual void OnFinalizeSynchronizationObject() override; + virtual bool IsSignaled() const override; + public: + static KEventInfo *CreateDebugEvent(ams::svc::DebugEvent event, uintptr_t param0, uintptr_t param1, uintptr_t param2, uintptr_t param3, uintptr_t param4, u64 thread_id); }; } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_event_info.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_event_info.hpp index c458da021..3c1e846a2 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_event_info.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_event_info.hpp @@ -21,7 +21,50 @@ namespace ams::kern { class KEventInfo : public KSlabAllocated, public util::IntrusiveListBaseNode { public: - /* TODO: This is a placeholder definition. */ + struct InfoCreateThread { + u32 thread_id; + uintptr_t tls_address; + uintptr_t entrypoint; + }; + + struct InfoExitProcess { + ams::svc::ProcessExitReason reason; + }; + + struct InfoExitThread { + ams::svc::ThreadExitReason reason; + }; + + struct InfoException { + ams::svc::DebugException exception_type; + s32 exception_data_count; + uintptr_t exception_address; + uintptr_t exception_data[4]; + }; + + struct InfoSystemCall { + s64 tick; + s32 id; + }; + public: + ams::svc::DebugEvent event; + u32 thread_id; + u32 flags; + bool is_attached; + bool continue_flag; + bool ignore_continue; + bool close_once; + union { + InfoCreateThread create_thread; + InfoExitProcess exit_process; + InfoExitThread exit_thread; + InfoException exception; + InfoSystemCall system_call; + } info; + KThread *debug_thread; + public: + explicit KEventInfo() : is_attached(), continue_flag(), ignore_continue() { /* ... */ } + ~KEventInfo() { /* ... */ } }; } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp index 72b2dde2f..4f410ad03 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp @@ -131,6 +131,8 @@ namespace ams::kern { constexpr u64 GetProcessId() const { return this->process_id; } + constexpr State GetState() const { return this->state; } + constexpr u64 GetCoreMask() const { return this->capabilities.GetCoreMask(); } constexpr u64 GetPriorityMask() const { return this->capabilities.GetPriorityMask(); } @@ -139,6 +141,8 @@ namespace ams::kern { constexpr bool CheckThreadPriority(s32 prio) const { return ((1ul << prio) & this->GetPriorityMask()) != 0; } + constexpr u32 GetCreateProcessFlags() const { return this->flags; } + constexpr bool Is64Bit() const { return this->flags & ams::svc::CreateProcessFlag_Is64Bit; } constexpr KProcessAddress GetEntryPoint() const { return this->code_address; } @@ -147,10 +151,32 @@ namespace ams::kern { return this->is_suspended; } + constexpr bool IsTerminated() const { + return this->state == State_Terminated; + } + + constexpr bool IsAttachedToDebugger() const { + return this->attached_object != nullptr; + } + constexpr bool IsPermittedInterrupt(int32_t interrupt_id) const { return this->capabilities.IsPermittedInterrupt(interrupt_id); } + constexpr bool IsPermittedDebug() const { + return this->capabilities.IsPermittedDebug(); + } + + constexpr bool CanForceDebug() const { + return this->capabilities.CanForceDebug(); + } + + ThreadList &GetThreadList() { return this->thread_list; } + const ThreadList &GetThreadList() const { return this->thread_list; } + + KProcess::State SetDebugObject(void *debug_object); + KEventInfo *GetJitDebugInfo(); + bool EnterUserException(); bool LeaveUserException(); bool ReleaseUserException(KThread *thread); diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp index 83aed923b..90bbacb09 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp @@ -311,10 +311,21 @@ namespace ams::kern { this->priority = priority; } - constexpr void ClearConditionVariableTree() { + constexpr void SetConditionVariable(ConditionVariableThreadTree *tree, KProcessAddress address, uintptr_t cv_key, u32 value) { + this->condvar_tree = tree; + this->condvar_key = cv_key; + this->address_key = address; + this->address_key_value = value; + } + + constexpr void ClearConditionVariable() { this->condvar_tree = nullptr; } + constexpr bool IsWaitingForConditionVariable() const { + return this->condvar_tree != nullptr; + } + constexpr void SetupForAddressArbiterCompare(uintptr_t address, int priority) { this->condvar_key = address; this->priority = priority; @@ -394,6 +405,8 @@ namespace ams::kern { constexpr KProcess *GetOwnerProcess() const { return this->parent; } constexpr bool IsUserThread() const { return this->parent != nullptr; } + constexpr uintptr_t GetEntrypoint() const { return this->entrypoint; } + constexpr KProcessAddress GetThreadLocalRegionAddress() const { return this->tls_address; } constexpr void *GetThreadLocalRegionHeapAddress() const { return this->tls_heap_address; } @@ -403,6 +416,9 @@ namespace ams::kern { constexpr u16 GetUserPreemptionState() const { return *GetPointer(this->tls_address + 0x100); } constexpr void SetKernelPreemptionState(u16 state) const { *GetPointer(this->tls_address + 0x100 + sizeof(u16)) = state; } + constexpr void SetDebugAttached() { this->debug_attached = true; } + constexpr bool IsAttachedToDebugger() const { return this->debug_attached; } + void AddCpuTime(s64 amount) { this->cpu_time += amount; } diff --git a/libraries/libmesosphere/include/mesosphere/svc/kern_svc_results.hpp b/libraries/libmesosphere/include/mesosphere/svc/kern_svc_results.hpp index 9d3d26a08..f53ba6d0a 100644 --- a/libraries/libmesosphere/include/mesosphere/svc/kern_svc_results.hpp +++ b/libraries/libmesosphere/include/mesosphere/svc/kern_svc_results.hpp @@ -67,6 +67,7 @@ namespace ams::kern::svc { /* 259 */ using ::ams::svc::ResultOutOfAddressSpace; /* 260 */ using ::ams::svc::ResultMessageTooLarge; + /* 517 */ using ::ams::svc::ResultInvalidProcessId; /* 520 */ using ::ams::svc::ResultProcessTerminated; } diff --git a/libraries/libmesosphere/source/kern_k_condition_variable.cpp b/libraries/libmesosphere/source/kern_k_condition_variable.cpp index 3d2ba92fe..850b03217 100644 --- a/libraries/libmesosphere/source/kern_k_condition_variable.cpp +++ b/libraries/libmesosphere/source/kern_k_condition_variable.cpp @@ -146,7 +146,7 @@ namespace ams::kern { } it = this->tree.erase(it); - target_thread->ClearConditionVariableTree(); + target_thread->ClearConditionVariable(); ++num_waiters; } } @@ -168,7 +168,83 @@ namespace ams::kern { } Result KConditionVariable::Wait(KProcessAddress addr, uintptr_t key, u32 value, s64 timeout) { - MESOSPHERE_UNIMPLEMENTED(); + /* Prepare to wait. */ + KThread *cur_thread = GetCurrentThreadPointer(); + KHardwareTimer *timer; + + { + KScopedSchedulerLockAndSleep slp(std::addressof(timer), cur_thread, timeout); + + /* Set the synced object. */ + cur_thread->SetSyncedObject(nullptr, ams::svc::ResultTimedOut()); + + /* Check that the thread isn't terminating. */ + if (cur_thread->IsTerminationRequested()) { + slp.CancelSleep(); + return svc::ResultTerminationRequested(); + } + + /* Update the value and process for the next owner. */ + { + /* Remove waiter thread. */ + s32 num_waiters; + KThread *next_owner_thread = cur_thread->RemoveWaiterByKey(std::addressof(num_waiters), GetInteger(addr)); + + /* Update for the next owner thread. */ + u32 next_value = 0; + if (next_owner_thread != nullptr) { + /* Get the next tag value. */ + next_value = next_owner_thread->GetAddressKeyValue(); + if (num_waiters > 1) { + next_value |= ams::svc::HandleWaitMask; + } + + /* Wake up the next owner. */ + next_owner_thread->SetSyncedObject(nullptr, ResultSuccess()); + next_owner_thread->Wakeup(); + } + + /* Write the value to userspace. */ + if (!WriteToUser(addr, std::addressof(next_value))) { + slp.CancelSleep(); + return svc::ResultInvalidCurrentMemory(); + } + } + + /* Update condition variable tracking. */ + { + cur_thread->SetConditionVariable(std::addressof(this->tree), addr, key, value); + this->tree.insert(*cur_thread); + } + + /* If the timeout is non-zero, set the thread as waiting. */ + if (timeout != 0) { + cur_thread->SetState(KThread::ThreadState_Waiting); + } + } + + /* Cancel the timer wait. */ + if (timer != nullptr) { + timer->CancelTask(cur_thread); + } + + /* Remove from the condition variable. */ + { + KScopedSchedulerLock sl; + + if (KThread *owner = cur_thread->GetLockOwner(); owner != nullptr) { + owner->RemoveWaiter(cur_thread); + } + + if (cur_thread->IsWaitingForConditionVariable()) { + this->tree.erase(this->tree.iterator_to(*cur_thread)); + cur_thread->ClearConditionVariable(); + } + } + + /* Get the result. */ + KSynchronizationObject *dummy; + return cur_thread->GetWaitResult(std::addressof(dummy)); } } diff --git a/libraries/libmesosphere/source/kern_k_debug_base.cpp b/libraries/libmesosphere/source/kern_k_debug_base.cpp new file mode 100644 index 000000000..59b527013 --- /dev/null +++ b/libraries/libmesosphere/source/kern_k_debug_base.cpp @@ -0,0 +1,370 @@ +/* + * 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 KDebugBase::Initialize() { + /* Clear the process and continue flags. */ + this->process = nullptr; + this->continue_flags = 0; + } + + Result KDebugBase::Attach(KProcess *target) { + /* Check that the process isn't null. */ + MESOSPHERE_ASSERT(target != nullptr); + + /* Attach to the process. */ + { + /* Lock both ourselves, the target process, and the scheduler. */ + KScopedLightLock state_lk(target->GetStateLock()); + KScopedLightLock list_lk(target->GetListLock()); + KScopedLightLock this_lk(this->lock); + KScopedSchedulerLock sl; + + /* Check that the process isn't already being debugged. */ + R_UNLESS(!target->IsAttachedToDebugger(), svc::ResultBusy()); + + { + /* Ensure the process is in a state that allows for debugging. */ + const KProcess::State state = target->GetState(); + switch (state) { + case KProcess::State_Created: + case KProcess::State_Running: + case KProcess::State_Crashed: + break; + case KProcess::State_CreatedAttached: + case KProcess::State_RunningAttached: + case KProcess::State_DebugBreak: + return svc::ResultBusy(); + case KProcess::State_Terminating: + case KProcess::State_Terminated: + return svc::ResultProcessTerminated(); + MESOSPHERE_UNREACHABLE_DEFAULT_CASE(); + } + + /* Set our process member, and open a reference to the target. */ + this->process = target; + this->process->Open(); + + /* Set ourselves as the process's attached object. */ + this->old_process_state = this->process->SetDebugObject(this); + + /* Send an event for our attaching to the process. */ + this->PushDebugEvent(ams::svc::DebugEvent_AttachProcess); + + /* Send events for attaching to each thread in the process. */ + { + auto end = this->process->GetThreadList().end(); + for (auto it = this->process->GetThreadList().begin(); it != end; ++it) { + /* Request that we suspend the thread. */ + it->RequestSuspend(KThread::SuspendType_Debug); + + /* If the thread is in a state for us to do so, generate the event. */ + if (const auto thread_state = it->GetState(); thread_state == KThread::ThreadState_Runnable || thread_state == KThread::ThreadState_Waiting) { + /* Mark the thread as attached to. */ + it->SetDebugAttached(); + + /* Send the event. */ + this->PushDebugEvent(ams::svc::DebugEvent_AttachThread, it->GetId(), GetInteger(it->GetThreadLocalRegionAddress()), it->GetEntrypoint()); + } + } + } + + /* Send the process's jit debug info, if relevant. */ + if (KEventInfo *jit_info = this->process->GetJitDebugInfo(); jit_info != nullptr) { + this->EnqueueDebugEventInfo(jit_info); + } + + /* Send an exception event to represent our attaching. */ + this->PushDebugEvent(ams::svc::DebugEvent_Exception, ams::svc::DebugException_DebuggerAttached); + + /* Signal. */ + this->NotifyAvailable(); + } + } + + return ResultSuccess(); + } + + KEventInfo *KDebugBase::CreateDebugEvent(ams::svc::DebugEvent event, uintptr_t param0, uintptr_t param1, uintptr_t param2, uintptr_t param3, uintptr_t param4, u64 cur_thread_id) { + /* Allocate a new event. */ + KEventInfo *info = KEventInfo::Allocate(); + + /* Populate the event info. */ + if (info != nullptr) { + /* Set common fields. */ + info->event = event; + info->thread_id = 0; + info->flags = 1; /* TODO: enum this in ams::svc */ + + /* Set event specific fields. */ + switch (event) { + case ams::svc::DebugEvent_AttachProcess: + { + /* ... */ + } + break; + case ams::svc::DebugEvent_AttachThread: + { + /* Set the thread id. */ + info->thread_id = param0; + + /* Set the thread creation info. */ + info->info.create_thread.thread_id = param0; + info->info.create_thread.tls_address = param1; + info->info.create_thread.entrypoint = param2; + } + break; + case ams::svc::DebugEvent_ExitProcess: + { + /* Set the exit reason. */ + info->info.exit_process.reason = static_cast(param0); + + /* Clear the thread id and flags. */ + info->thread_id = 0; + info->flags = 0 /* TODO: enum this in ams::svc */; + } + break; + case ams::svc::DebugEvent_ExitThread: + { + /* Set the thread id. */ + info->thread_id = param0; + + /* Set the exit reason. */ + info->info.exit_thread.reason = static_cast(param1); + } + break; + case ams::svc::DebugEvent_Exception: + { + /* Set the thread id. */ + info->thread_id = cur_thread_id; + + /* Set the exception type, and clear the count. */ + info->info.exception.exception_type = static_cast(param0); + info->info.exception.exception_data_count = 0; + switch (static_cast(param0)) { + case ams::svc::DebugException_UndefinedInstruction: + case ams::svc::DebugException_BreakPoint: + case ams::svc::DebugException_UndefinedSystemCall: + { + info->info.exception.exception_address = param1; + + info->info.exception.exception_data_count = 1; + info->info.exception.exception_data[0] = param2; + } + break; + case ams::svc::DebugException_DebuggerAttached: + { + info->thread_id = 0; + + info->info.exception.exception_address = 0; + } + break; + case ams::svc::DebugException_UserBreak: + { + info->info.exception.exception_address = param1; + + info->info.exception.exception_data_count = 3; + info->info.exception.exception_data[0] = param2; + info->info.exception.exception_data[1] = param3; + info->info.exception.exception_data[2] = param4; + } + break; + case ams::svc::DebugException_DebuggerBreak: + { + info->thread_id = 0; + + info->info.exception.exception_address = 0; + + info->info.exception.exception_data_count = 4; + info->info.exception.exception_data[0] = param1; + info->info.exception.exception_data[1] = param2; + info->info.exception.exception_data[2] = param3; + info->info.exception.exception_data[3] = param4; + } + break; + case ams::svc::DebugException_MemorySystemError: + { + info->info.exception.exception_address = 0; + } + break; + case ams::svc::DebugException_InstructionAbort: + case ams::svc::DebugException_DataAbort: + case ams::svc::DebugException_AlignmentFault: + default: + { + info->info.exception.exception_address = param1; + } + break; + } + } + break; + } + } + + return info; + } + + void KDebugBase::PushDebugEvent(ams::svc::DebugEvent event, uintptr_t param0, uintptr_t param1, uintptr_t param2, uintptr_t param3, uintptr_t param4) { + /* Create and enqueue and event. */ + if (KEventInfo *new_info = CreateDebugEvent(event, param0, param1, param2, param3, param4, GetCurrentThread().GetId()); new_info != nullptr) { + this->EnqueueDebugEventInfo(new_info); + } + } + + void KDebugBase::EnqueueDebugEventInfo(KEventInfo *info) { + /* Lock the scheduler. */ + KScopedSchedulerLock sl; + + /* Push the event to the back of the list. */ + this->event_info_list.push_back(*info); + } + + + KScopedAutoObject KDebugBase::GetProcess() { + /* Lock ourselves. */ + KScopedLightLock lk(this->lock); + + return this->process; + } + + Result KDebugBase::GetDebugEventInfo(ams::svc::lp64::DebugEventInfo *out) { + /* Get the attached process. */ + KScopedAutoObject process = this->GetProcess(); + R_UNLESS(process.IsNotNull(), svc::ResultProcessTerminated()); + + /* Pop an event info from our queue. */ + KEventInfo *info = nullptr; + { + KScopedSchedulerLock sl; + + /* Check that we have an event to dequeue. */ + R_UNLESS(!this->event_info_list.empty(), svc::ResultNoEvent()); + + /* Pop the event from the front of the queue. */ + info = std::addressof(this->event_info_list.front()); + this->event_info_list.pop_front(); + } + MESOSPHERE_ASSERT(info != nullptr); + + /* Free the event info once we're done with it. */ + ON_SCOPE_EXIT { KEventInfo::Free(info); }; + + /* Set common fields. */ + out->type = info->event; + out->thread_id = info->thread_id; + out->flags = info->flags; + + /* Set event specific fields. */ + switch (info->event) { + case ams::svc::DebugEvent_AttachProcess: + { + out->info.attach_process.program_id = process->GetProgramId(); + out->info.attach_process.process_id = process->GetId(); + out->info.attach_process.flags = process->GetCreateProcessFlags(); + out->info.attach_process.user_exception_context_address = GetInteger(process->GetProcessLocalRegionAddress()); + + std::memcpy(out->info.attach_process.name, process->GetName(), sizeof(out->info.attach_process.name)); + } + break; + case ams::svc::DebugEvent_AttachThread: + { + out->info.attach_thread.thread_id = info->info.create_thread.thread_id; + out->info.attach_thread.tls_address = info->info.create_thread.tls_address; + out->info.attach_thread.entrypoint = info->info.create_thread.entrypoint; + } + break; + case ams::svc::DebugEvent_ExitProcess: + { + out->info.exit_process.reason = info->info.exit_process.reason; + } + break; + case ams::svc::DebugEvent_ExitThread: + { + out->info.exit_thread.reason = info->info.exit_thread.reason; + } + break; + case ams::svc::DebugEvent_Exception: + { + out->info.exception.type = info->info.exception.exception_type; + out->info.exception.address = info->info.exception.exception_address; + + switch (info->info.exception.exception_type) { + case ams::svc::DebugException_UndefinedInstruction: + { + MESOSPHERE_ASSERT(info->info.exception.exception_data_count == 1); + out->info.exception.specific.undefined_instruction.insn = info->info.exception.exception_data[0]; + } + break; + case ams::svc::DebugException_BreakPoint: + { + MESOSPHERE_ASSERT(info->info.exception.exception_data_count == 1); + out->info.exception.specific.break_point.type = static_cast(info->info.exception.exception_data[0]); + out->info.exception.specific.break_point.address = 0; + } + break; + case ams::svc::DebugException_UserBreak: + { + MESOSPHERE_ASSERT(info->info.exception.exception_data_count == 3); + out->info.exception.specific.user_break.break_reason = static_cast(info->info.exception.exception_data[0]); + out->info.exception.specific.user_break.address = info->info.exception.exception_data[1]; + out->info.exception.specific.user_break.size = info->info.exception.exception_data[2]; + } + break; + case ams::svc::DebugException_DebuggerBreak: + { + MESOSPHERE_ASSERT(info->info.exception.exception_data_count == 4); + out->info.exception.specific.debugger_break.active_thread_ids[0] = info->info.exception.exception_data[0]; + out->info.exception.specific.debugger_break.active_thread_ids[1] = info->info.exception.exception_data[1]; + out->info.exception.specific.debugger_break.active_thread_ids[2] = info->info.exception.exception_data[2]; + out->info.exception.specific.debugger_break.active_thread_ids[3] = info->info.exception.exception_data[3]; + } + break; + case ams::svc::DebugException_UndefinedSystemCall: + { + MESOSPHERE_ASSERT(info->info.exception.exception_data_count == 1); + out->info.exception.specific.undefined_system_call.id = info->info.exception.exception_data[0]; + } + break; + default: + { + /* ... */ + } + break; + } + } + break; + } + + return ResultSuccess(); + } + + Result KDebugBase::GetDebugEventInfo(ams::svc::ilp32::DebugEventInfo *out) { + MESOSPHERE_UNIMPLEMENTED(); + } + + void KDebugBase::OnFinalizeSynchronizationObject() { + MESOSPHERE_UNIMPLEMENTED(); + } + + bool KDebugBase::IsSignaled() const { + KScopedSchedulerLock sl; + + return (!this->event_info_list.empty()) || this->process == nullptr || this->process->IsTerminated(); + } + +} diff --git a/libraries/libmesosphere/source/kern_k_interrupt_task_manager.cpp b/libraries/libmesosphere/source/kern_k_interrupt_task_manager.cpp index 492516a25..189b45357 100644 --- a/libraries/libmesosphere/source/kern_k_interrupt_task_manager.cpp +++ b/libraries/libmesosphere/source/kern_k_interrupt_task_manager.cpp @@ -18,9 +18,9 @@ namespace ams::kern { void KInterruptTaskManager::TaskQueue::Enqueue(KInterruptTask *task) { - MESOSPHERE_ASSERT(task->GetNextTask() == nullptr); MESOSPHERE_ASSERT(task != this->head); MESOSPHERE_ASSERT(task != this->tail); + MESOSPHERE_AUDIT(task->GetNextTask() == nullptr); /* Insert the task into the queue. */ if (this->tail != nullptr) { @@ -30,19 +30,31 @@ namespace ams::kern { } this->tail = task; + + /* Set the next task for auditing. */ + #if defined (MESOSPHERE_BUILD_FOR_AUDITING) + task->SetNextTask(GetDummyInterruptTask()); + #endif } void KInterruptTaskManager::TaskQueue::Dequeue() { MESOSPHERE_ASSERT(this->head != nullptr); MESOSPHERE_ASSERT(this->tail != nullptr); + MESOSPHERE_AUDIT(this->tail->GetNextTask() == GetDummyInterruptTask()); /* Pop the task from the front of the queue. */ + KInterruptTask *old_head = this->head; + if (this->head == this->tail) { this->head = nullptr; this->tail = nullptr; } else { this->head = this->head->GetNextTask(); } + + #if defined (MESOSPHERE_BUILD_FOR_AUDITING) + old_head->SetNextTask(nullptr); + #endif } void KInterruptTaskManager::ThreadFunction(uintptr_t arg) { diff --git a/libraries/libmesosphere/source/kern_k_process.cpp b/libraries/libmesosphere/source/kern_k_process.cpp index e048a6ec8..9725478e2 100644 --- a/libraries/libmesosphere/source/kern_k_process.cpp +++ b/libraries/libmesosphere/source/kern_k_process.cpp @@ -451,6 +451,33 @@ namespace ams::kern { return ResultSuccess(); } + KProcess::State KProcess::SetDebugObject(void *debug_object) { + /* Cache our state to return it to the debug object. */ + const auto old_state = this->state; + + /* Set the object. */ + this->attached_object = debug_object; + + /* Update our state. */ + if (this->state != State_DebugBreak) { + this->state = (this->state != State_Created) ? State_DebugBreak : State_CreatedAttached; + + /* Signal. */ + this->is_signaled = true; + this->NotifyAvailable(); + } + + return old_state; + } + + KEventInfo *KProcess::GetJitDebugInfo() { + if (this->is_jit_debug) { + return KDebugBase::CreateDebugEvent(this->jit_debug_event_type, this->jit_debug_exception_type, this->jit_debug_params[0], this->jit_debug_params[1], this->jit_debug_params[2], this->jit_debug_params[3], this->jit_debug_thread_id); + } else { + return nullptr; + } + } + void KProcess::SetPreemptionState() { MESOSPHERE_UNIMPLEMENTED(); } diff --git a/libraries/libmesosphere/source/svc/kern_svc_condition_variable.cpp b/libraries/libmesosphere/source/svc/kern_svc_condition_variable.cpp index 0a3522421..660149aec 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_condition_variable.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_condition_variable.cpp @@ -21,7 +21,37 @@ namespace ams::kern::svc { namespace { + constexpr bool IsKernelAddress(uintptr_t address) { + return KernelVirtualAddressSpaceBase <= address && address < KernelVirtualAddressSpaceEnd; + } + + Result WaitProcessWideKeyAtomic(uintptr_t address, uintptr_t cv_key, uint32_t tag, int64_t timeout_ns) { + /* Validate input. */ + R_UNLESS(AMS_LIKELY(!IsKernelAddress(address)), svc::ResultInvalidCurrentMemory()); + R_UNLESS(util::IsAligned(address, sizeof(int32_t)), svc::ResultInvalidAddress()); + + /* Convert timeout from nanoseconds to ticks. */ + 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 on the condition variable. */ + return GetCurrentProcess().WaitConditionVariable(address, util::AlignDown(cv_key, sizeof(u32)), tag, timeout); + } + void SignalProcessWideKey(uintptr_t cv_key, int32_t count) { + /* Signal the condition variable. */ return GetCurrentProcess().SignalConditionVariable(util::AlignDown(cv_key, sizeof(u32)), count); } @@ -30,7 +60,7 @@ namespace ams::kern::svc { /* ============================= 64 ABI ============================= */ Result WaitProcessWideKeyAtomic64(ams::svc::Address address, ams::svc::Address cv_key, uint32_t tag, int64_t timeout_ns) { - MESOSPHERE_PANIC("Stubbed SvcWaitProcessWideKeyAtomic64 was called."); + return WaitProcessWideKeyAtomic(address, cv_key, tag, timeout_ns); } void SignalProcessWideKey64(ams::svc::Address cv_key, int32_t count) { @@ -40,7 +70,7 @@ namespace ams::kern::svc { /* ============================= 64From32 ABI ============================= */ Result WaitProcessWideKeyAtomic64From32(ams::svc::Address address, ams::svc::Address cv_key, uint32_t tag, int64_t timeout_ns) { - MESOSPHERE_PANIC("Stubbed SvcWaitProcessWideKeyAtomic64From32 was called."); + return WaitProcessWideKeyAtomic(address, cv_key, tag, timeout_ns); } void SignalProcessWideKey64From32(ams::svc::Address cv_key, int32_t count) { diff --git a/libraries/libmesosphere/source/svc/kern_svc_debug.cpp b/libraries/libmesosphere/source/svc/kern_svc_debug.cpp index 3aee4aeed..f9da78302 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_debug.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_debug.cpp @@ -21,14 +21,70 @@ namespace ams::kern::svc { namespace { + Result DebugActiveProcess(ams::svc::Handle *out_handle, uint64_t process_id) { + /* Get the process from its id. */ + KProcess *process = KProcess::GetProcessFromId(process_id); + R_UNLESS(process != nullptr, svc::ResultInvalidProcessId()); + /* Close the reference we opened to the process on scope exit. */ + ON_SCOPE_EXIT { process->Close(); }; + + /* Check that the debugging is allowed. */ + if (!process->IsPermittedDebug()) { + R_UNLESS(GetCurrentProcess().CanForceDebug(), svc::ResultInvalidState()); + } + + /* Disallow debugging one's own processs, to prevent softlocks. */ + R_UNLESS(process != GetCurrentProcessPointer(), svc::ResultInvalidState()); + + /* Get the current handle table. */ + auto &handle_table = GetCurrentProcess().GetHandleTable(); + + /* Create a new debug object. */ + KDebug *debug = KDebug::Create(); + R_UNLESS(debug != nullptr, svc::ResultOutOfResource()); + ON_SCOPE_EXIT { debug->Close(); }; + + /* Initialize the debug object. */ + debug->Initialize(); + + /* Register the debug object. */ + KDebug::Register(debug); + + /* Try to attach to the target process. */ + R_TRY(debug->Attach(process)); + + /* Add the new debug object to the handle table. */ + R_TRY(handle_table.Add(out_handle, debug)); + + return ResultSuccess(); + } + + template + Result GetDebugEvent(KUserPointer out_info, ams::svc::Handle debug_handle) { + /* Get the debug object. */ + KScopedAutoObject debug = GetCurrentProcess().GetHandleTable().GetObject(debug_handle); + R_UNLESS(debug.IsNotNull(), svc::ResultInvalidHandle()); + + /* Create and clear a new event info. */ + EventInfoType info; + std::memset(std::addressof(info), 0, sizeof(info)); + + /* Get the next info from the debug object. */ + R_TRY(debug->GetDebugEventInfo(std::addressof(info))); + + /* Copy the info out to the user. */ + R_TRY(out_info.CopyFrom(std::addressof(info))); + + return ResultSuccess(); + } } /* ============================= 64 ABI ============================= */ Result DebugActiveProcess64(ams::svc::Handle *out_handle, uint64_t process_id) { - MESOSPHERE_PANIC("Stubbed SvcDebugActiveProcess64 was called."); + return DebugActiveProcess(out_handle, process_id); } Result BreakDebugProcess64(ams::svc::Handle debug_handle) { @@ -40,7 +96,7 @@ namespace ams::kern::svc { } Result GetDebugEvent64(KUserPointer out_info, ams::svc::Handle debug_handle) { - MESOSPHERE_PANIC("Stubbed SvcGetDebugEvent64 was called."); + return GetDebugEvent(out_info, debug_handle); } Result ContinueDebugEvent64(ams::svc::Handle debug_handle, uint32_t flags, KUserPointer thread_ids, int32_t num_thread_ids) { @@ -82,7 +138,7 @@ namespace ams::kern::svc { /* ============================= 64From32 ABI ============================= */ Result DebugActiveProcess64From32(ams::svc::Handle *out_handle, uint64_t process_id) { - MESOSPHERE_PANIC("Stubbed SvcDebugActiveProcess64From32 was called."); + return DebugActiveProcess(out_handle, process_id); } Result BreakDebugProcess64From32(ams::svc::Handle debug_handle) { @@ -94,7 +150,7 @@ namespace ams::kern::svc { } Result GetDebugEvent64From32(KUserPointer out_info, ams::svc::Handle debug_handle) { - MESOSPHERE_PANIC("Stubbed SvcGetDebugEvent64From32 was called."); + return GetDebugEvent(out_info, debug_handle); } Result ContinueDebugEvent64From32(ams::svc::Handle debug_handle, uint32_t flags, KUserPointer thread_ids, int32_t num_thread_ids) { diff --git a/libraries/libmesosphere/source/svc/kern_svc_device_address_space.cpp b/libraries/libmesosphere/source/svc/kern_svc_device_address_space.cpp index 67a31326a..84f36b0bf 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_device_address_space.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_device_address_space.cpp @@ -35,17 +35,18 @@ namespace ams::kern::svc { R_UNLESS((das_address < das_address + das_size), svc::ResultInvalidMemoryRegion()); /* Create the device address space. */ - KScopedAutoObject das = KDeviceAddressSpace::Create(); - R_UNLESS(das.IsNotNull(), svc::ResultOutOfResource()); + KDeviceAddressSpace *das = KDeviceAddressSpace::Create(); + R_UNLESS(das != nullptr, svc::ResultOutOfResource()); + ON_SCOPE_EXIT { das->Close(); }; /* Initialize the device address space. */ R_TRY(das->Initialize(das_address, das_size)); /* Register the device address space. */ - R_TRY(KDeviceAddressSpace::Register(das.GetPointerUnsafe())); + R_TRY(KDeviceAddressSpace::Register(das)); /* Add to the handle table. */ - R_TRY(GetCurrentProcess().GetHandleTable().Add(out, das.GetPointerUnsafe())); + R_TRY(GetCurrentProcess().GetHandleTable().Add(out, das)); return ResultSuccess(); } diff --git a/libraries/libmesosphere/source/svc/kern_svc_interrupt_event.cpp b/libraries/libmesosphere/source/svc/kern_svc_interrupt_event.cpp index bcc414017..6019820d8 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_interrupt_event.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_interrupt_event.cpp @@ -43,17 +43,18 @@ namespace ams::kern::svc { auto &handle_table = process.GetHandleTable(); /* Create the interrupt event. */ - KScopedAutoObject event = KInterruptEvent::Create(); - R_UNLESS(event.IsNotNull(), svc::ResultOutOfResource()); + KInterruptEvent *event = KInterruptEvent::Create(); + R_UNLESS(event != nullptr, svc::ResultOutOfResource()); + ON_SCOPE_EXIT { event->Close(); }; /* Initialize the event. */ R_TRY(event->Initialize(interrupt_id, type)); /* Register the event. */ - R_TRY(KInterruptEvent::Register(event.GetPointerUnsafe())); + R_TRY(KInterruptEvent::Register(event)); /* Add the event to the handle table. */ - R_TRY(handle_table.Add(out, event.GetPointerUnsafe())); + R_TRY(handle_table.Add(out, event)); return ResultSuccess(); } diff --git a/libraries/libmesosphere/source/svc/kern_svc_thread.cpp b/libraries/libmesosphere/source/svc/kern_svc_thread.cpp index 81fdc89fd..d1314b6a6 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_thread.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_thread.cpp @@ -44,13 +44,14 @@ namespace ams::kern::svc { R_UNLESS(thread_reservation.Succeeded(), svc::ResultLimitReached()); /* Create the thread. */ - KScopedAutoObject thread = KThread::Create(); - R_UNLESS(thread.IsNotNull(), svc::ResultOutOfResource()); + KThread *thread = KThread::Create(); + R_UNLESS(thread != nullptr, svc::ResultOutOfResource()); + ON_SCOPE_EXIT { thread->Close(); }; /* Initialize the thread. */ { KScopedLightLock lk(process.GetStateLock()); - R_TRY(KThread::InitializeUserThread(thread.GetPointerUnsafe(), reinterpret_cast(static_cast(f)), arg, stack_bottom, priority, core_id, std::addressof(process))); + R_TRY(KThread::InitializeUserThread(thread, reinterpret_cast(static_cast(f)), arg, stack_bottom, priority, core_id, std::addressof(process))); } /* Commit the thread reservation. */ @@ -60,10 +61,10 @@ namespace ams::kern::svc { thread->GetContext().CloneFpuStatus(); /* Register the new thread. */ - R_TRY(KThread::Register(thread.GetPointerUnsafe())); + R_TRY(KThread::Register(thread)); /* Add the thread to the handle table. */ - R_TRY(process.GetHandleTable().Add(out, thread.GetPointerUnsafe())); + R_TRY(process.GetHandleTable().Add(out, thread)); return ResultSuccess(); } diff --git a/libraries/libvapours/include/vapours/results/svc_results.hpp b/libraries/libvapours/include/vapours/results/svc_results.hpp index 4adeacaf2..2e3a62dd6 100644 --- a/libraries/libvapours/include/vapours/results/svc_results.hpp +++ b/libraries/libvapours/include/vapours/results/svc_results.hpp @@ -70,6 +70,7 @@ namespace ams::svc { R_DEFINE_ERROR_RESULT(OutOfAddressSpace, 259); R_DEFINE_ERROR_RESULT(MessageTooLarge, 260); + R_DEFINE_ERROR_RESULT(InvalidProcessId, 517); R_DEFINE_ERROR_RESULT(ProcessTerminated, 520); }