diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_debug_base.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_debug_base.hpp index 383a94895..d43a2f016 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_debug_base.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_debug_base.hpp @@ -38,6 +38,8 @@ namespace ams::kern { void Initialize(); Result Attach(KProcess *process); + Result BreakProcess(); + Result TerminateProcess(); Result ContinueDebug(const u32 flags, const u64 *thread_ids, size_t num_thread_ids); diff --git a/libraries/libmesosphere/source/kern_k_debug_base.cpp b/libraries/libmesosphere/source/kern_k_debug_base.cpp index 749093eb2..bdb45f9a5 100644 --- a/libraries/libmesosphere/source/kern_k_debug_base.cpp +++ b/libraries/libmesosphere/source/kern_k_debug_base.cpp @@ -341,6 +341,124 @@ namespace ams::kern { return ResultSuccess(); } + Result KDebugBase::BreakProcess() { + /* Get the attached process. */ + KScopedAutoObject target = this->GetProcess(); + R_UNLESS(target.IsNotNull(), svc::ResultProcessTerminated()); + + /* 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 we're still attached to the process, and that it's not terminated. */ + /* NOTE: Here Nintendo only checks that this->process is not nullptr. */ + R_UNLESS(this->process == target.GetPointerUnsafe(), svc::ResultProcessTerminated()); + R_UNLESS(!target->IsTerminated(), svc::ResultProcessTerminated()); + + /* Get the currently active threads. */ + constexpr u64 ThreadIdNoThread = -1ll; + constexpr u64 ThreadIdUnknownThread = -2ll; + u64 thread_ids[cpu::NumCores]; + for (size_t i = 0; i < util::size(thread_ids); ++i) { + /* Get the currently running thread. */ + KThread *thread = target->GetRunningThread(i); + + /* Check that the thread's idle count is correct. */ + if (target->GetRunningThreadIdleCount(i) == Kernel::GetScheduler(i).GetIdleCount()) { + if (thread != nullptr && static_cast(thread->GetActiveCore()) == i) { + thread_ids[i] = thread->GetId(); + } else { + /* We found an unknown thread. */ + thread_ids[i] = ThreadIdUnknownThread; + } + } else { + /* We didn't find a thread. */ + thread_ids[i] = ThreadIdNoThread; + } + } + + /* Suspend all the threads in the process. */ + { + auto end = target->GetThreadList().end(); + for (auto it = target->GetThreadList().begin(); it != end; ++it) { + /* Request that we suspend the thread. */ + it->RequestSuspend(KThread::SuspendType_Debug); + } + } + + /* Send an exception event to represent our breaking the process. */ + static_assert(util::size(thread_ids) >= 4); + this->PushDebugEvent(ams::svc::DebugEvent_Exception, ams::svc::DebugException_DebuggerBreak, thread_ids[0], thread_ids[1], thread_ids[2], thread_ids[3]); + + /* Signal. */ + this->NotifyAvailable(); + + /* Set the process as breaked. */ + target->SetDebugBreak(); + + return ResultSuccess(); + } + + Result KDebugBase::TerminateProcess() { + /* Get the attached process. If we don't have one, we have nothing to do. */ + KScopedAutoObject target = this->GetProcess(); + R_SUCCEED_IF(target.IsNull()); + + /* Detach from the process. */ + { + /* Lock both ourselves and the target process. */ + KScopedLightLock state_lk(target->GetStateLock()); + KScopedLightLock list_lk(target->GetListLock()); + KScopedLightLock this_lk(this->lock); + + /* Check that we still have our process. */ + if (this->process != nullptr) { + /* Check that our process is the one we got earlier. */ + MESOSPHERE_ASSERT(this->process == target.GetPointerUnsafe()); + + /* Lock the scheduler. */ + KScopedSchedulerLock sl; + + /* Get the process's state. */ + const KProcess::State state = target->GetState(); + + /* Check that the process is in a state where we can terminate it. */ + R_UNLESS(state != KProcess::State_Created, svc::ResultInvalidState()); + R_UNLESS(state != KProcess::State_CreatedAttached, svc::ResultInvalidState()); + + /* Decide on a new state for the process. */ + KProcess::State new_state; + if (state == KProcess::State_RunningAttached) { + /* If the process is running, transition it accordingly. */ + new_state = KProcess::State_Running; + } else if (state == KProcess::State_DebugBreak) { + /* If the process is debug breaked, transition it accordingly. */ + new_state = KProcess::State_Crashed; + } else { + /* Otherwise, don't transition. */ + new_state = state; + } + + /* Detach from the process. */ + target->ClearDebugObject(new_state); + this->process = nullptr; + + /* Clear our continue flags. */ + this->continue_flags = 0; + } + } + + /* Close the reference we held to the process while we were attached to it. */ + target->Close(); + + /* Terminate the process. */ + target->Terminate(); + + return ResultSuccess(); + } + Result KDebugBase::ContinueDebug(const u32 flags, const u64 *thread_ids, size_t num_thread_ids) { /* Get the attached process. */ KScopedAutoObject target = this->GetProcess(); diff --git a/libraries/libmesosphere/source/svc/kern_svc_debug.cpp b/libraries/libmesosphere/source/svc/kern_svc_debug.cpp index 708d2ab01..81a4e9d7f 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_debug.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_debug.cpp @@ -62,6 +62,34 @@ namespace ams::kern::svc { return ResultSuccess(); } + Result BreakDebugProcess(ams::svc::Handle debug_handle) { + /* Only allow invoking the svc on development hardware. */ + R_UNLESS(KTargetSystem::IsDebugMode(), svc::ResultNotImplemented()); + + /* Get the debug object. */ + KScopedAutoObject debug = GetCurrentProcess().GetHandleTable().GetObject(debug_handle); + R_UNLESS(debug.IsNotNull(), svc::ResultInvalidHandle()); + + /* Break the process. */ + R_TRY(debug->BreakProcess()); + + return ResultSuccess(); + } + + Result TerminateDebugProcess(ams::svc::Handle debug_handle) { + /* Only allow invoking the svc on development hardware. */ + R_UNLESS(KTargetSystem::IsDebugMode(), svc::ResultNotImplemented()); + + /* Get the debug object. */ + KScopedAutoObject debug = GetCurrentProcess().GetHandleTable().GetObject(debug_handle); + R_UNLESS(debug.IsNotNull(), svc::ResultInvalidHandle()); + + /* Terminate the process. */ + R_TRY(debug->TerminateProcess()); + + return ResultSuccess(); + } + template Result GetDebugEvent(KUserPointer out_info, ams::svc::Handle debug_handle) { /* Get the debug object. */ @@ -227,11 +255,11 @@ namespace ams::kern::svc { } Result BreakDebugProcess64(ams::svc::Handle debug_handle) { - MESOSPHERE_PANIC("Stubbed SvcBreakDebugProcess64 was called."); + return BreakDebugProcess(debug_handle); } Result TerminateDebugProcess64(ams::svc::Handle debug_handle) { - MESOSPHERE_PANIC("Stubbed SvcTerminateDebugProcess64 was called."); + return TerminateDebugProcess(debug_handle); } Result GetDebugEvent64(KUserPointer out_info, ams::svc::Handle debug_handle) { @@ -281,11 +309,11 @@ namespace ams::kern::svc { } Result BreakDebugProcess64From32(ams::svc::Handle debug_handle) { - MESOSPHERE_PANIC("Stubbed SvcBreakDebugProcess64From32 was called."); + return BreakDebugProcess(debug_handle); } Result TerminateDebugProcess64From32(ams::svc::Handle debug_handle) { - MESOSPHERE_PANIC("Stubbed SvcTerminateDebugProcess64From32 was called."); + return TerminateDebugProcess(debug_handle); } Result GetDebugEvent64From32(KUserPointer out_info, ams::svc::Handle debug_handle) {