From ff9d3ee1d6043f8889ac3f4bc80686f170fc1aad Mon Sep 17 00:00:00 2001
From: Michael Scire <SciresM@gmail.com>
Date: Fri, 31 Jul 2020 01:59:46 -0700
Subject: [PATCH] kern: SvcGetDebugThreadParam

---
 .../kern_k_auto_object_container.hpp          |   2 +-
 .../include/mesosphere/kern_k_thread.hpp      |   1 +
 .../mesosphere/svc/kern_svc_results.hpp       |   1 +
 libmesosphere/source/kern_k_thread.cpp        |  35 ++++++
 libmesosphere/source/svc/kern_svc_debug.cpp   | 103 +++++++++++++++++-
 .../include/vapours/results/svc_results.hpp   |   1 +
 6 files changed, 140 insertions(+), 3 deletions(-)

diff --git a/libmesosphere/include/mesosphere/kern_k_auto_object_container.hpp b/libmesosphere/include/mesosphere/kern_k_auto_object_container.hpp
index 3d501bdf..f89839e8 100644
--- a/libmesosphere/include/mesosphere/kern_k_auto_object_container.hpp
+++ b/libmesosphere/include/mesosphere/kern_k_auto_object_container.hpp
@@ -23,7 +23,7 @@ namespace ams::kern {
     class KAutoObjectWithListContainer {
         NON_COPYABLE(KAutoObjectWithListContainer);
         NON_MOVEABLE(KAutoObjectWithListContainer);
-        private:
+        public:
             using ListType = util::IntrusiveRedBlackTreeMemberTraits<&KAutoObjectWithList::list_node>::TreeType<KAutoObjectWithList>;
         public:
             class ListAccessor : public KScopedLightLock {
diff --git a/libmesosphere/include/mesosphere/kern_k_thread.hpp b/libmesosphere/include/mesosphere/kern_k_thread.hpp
index 9cce690b..6a13f6a5 100644
--- a/libmesosphere/include/mesosphere/kern_k_thread.hpp
+++ b/libmesosphere/include/mesosphere/kern_k_thread.hpp
@@ -522,6 +522,7 @@ namespace ams::kern {
                 return ConditionVariableThreadTreeTraits::IsValid();
             }
 
+            static KThread *GetThreadFromId(u64 thread_id);
             static Result GetThreadList(s32 *out_num_threads, ams::kern::svc::KUserPointer<u64 *> out_thread_ids, s32 max_out_count);
 
             using ConditionVariableThreadTreeType = ConditionVariableThreadTree;
diff --git a/libmesosphere/include/mesosphere/svc/kern_svc_results.hpp b/libmesosphere/include/mesosphere/svc/kern_svc_results.hpp
index 4b84670a..a1aa17d2 100644
--- a/libmesosphere/include/mesosphere/svc/kern_svc_results.hpp
+++ b/libmesosphere/include/mesosphere/svc/kern_svc_results.hpp
@@ -71,6 +71,7 @@ namespace ams::kern::svc {
     /* 260 */ using ::ams::svc::ResultMessageTooLarge;
 
     /* 517 */ using ::ams::svc::ResultInvalidProcessId;
+    /* 518 */ using ::ams::svc::ResultInvalidThreadId;
     /* 520 */ using ::ams::svc::ResultProcessTerminated;
 
 }
diff --git a/libmesosphere/source/kern_k_thread.cpp b/libmesosphere/source/kern_k_thread.cpp
index 7d6be53b..ae84126f 100644
--- a/libmesosphere/source/kern_k_thread.cpp
+++ b/libmesosphere/source/kern_k_thread.cpp
@@ -1190,6 +1190,41 @@ namespace ams::kern {
         return std::addressof(this->GetContext());
     }
 
+    KThread *KThread::GetThreadFromId(u64 thread_id) {
+        /* Lock the list. */
+        KThread::ListAccessor accessor;
+        const auto end = accessor.end();
+
+        /* Define helper object to find the thread. */
+        class IdObjectHelper : public KAutoObjectWithListContainer::ListType::value_type {
+            private:
+                u64 id;
+            public:
+                constexpr explicit IdObjectHelper(u64 id) : id(id) { /* ... */ }
+                virtual u64 GetId() const override { return this->id; }
+        };
+
+        /* Find the object with the right id. */
+        const auto it = accessor.find(IdObjectHelper(thread_id));
+
+        /* Check to make sure we found the thread. */
+        if (it == end) {
+            return nullptr;
+        }
+
+        /* Get the thread. */
+        KThread *thread = static_cast<KThread *>(std::addressof(*it));
+
+        /* Open the thread. */
+        if (AMS_LIKELY(thread->Open())) {
+            MESOSPHERE_ASSERT(thread->GetId() == thread_id);
+            return thread;
+        }
+
+        /* We failed to find the thread. */
+        return nullptr;
+    }
+
     Result KThread::GetThreadList(s32 *out_num_threads, ams::kern::svc::KUserPointer<u64 *> out_thread_ids, s32 max_out_count) {
         /* Lock the list. */
         KThread::ListAccessor accessor;
diff --git a/libmesosphere/source/svc/kern_svc_debug.cpp b/libmesosphere/source/svc/kern_svc_debug.cpp
index 81a4e9d7..6df5aa24 100644
--- a/libmesosphere/source/svc/kern_svc_debug.cpp
+++ b/libmesosphere/source/svc/kern_svc_debug.cpp
@@ -246,6 +246,105 @@ namespace ams::kern::svc {
             return ResultSuccess();
         }
 
+        Result GetDebugThreadParam(uint64_t *out_64, uint32_t *out_32, ams::svc::Handle debug_handle, uint64_t thread_id, ams::svc::DebugThreadParam param) {
+            /* Get the debug object. */
+            KScopedAutoObject debug = GetCurrentProcess().GetHandleTable().GetObject<KDebug>(debug_handle);
+            R_UNLESS(debug.IsNotNull(), svc::ResultInvalidHandle());
+
+            /* Get the thread from its id. */
+            KScopedAutoObject thread = KThread::GetThreadFromId(thread_id);
+            R_UNLESS(thread.IsNotNull(), svc::ResultInvalidThreadId());
+
+            /* Get the process from the debug object. */
+            KScopedAutoObject process = debug->GetProcess();
+            R_UNLESS(process.IsNotNull(), svc::ResultProcessTerminated());
+
+            /* Verify that the process is the thread's parent. */
+            R_UNLESS(process.GetPointerUnsafe() == thread->GetOwnerProcess(), svc::ResultInvalidThreadId());
+
+            /* Get the parameter. */
+            switch (param) {
+                case ams::svc::DebugThreadParam_Priority:
+                    {
+                        /* Get the priority. */
+                        *out_32 = thread->GetPriority();
+                    }
+                    break;
+                case ams::svc::DebugThreadParam_State:
+                    {
+                        /* Get the thread state and suspend status. */
+                        KThread::ThreadState state;
+                        bool suspended_user;
+                        bool suspended_debug;
+                        {
+                            KScopedSchedulerLock sl;
+
+                            state           = thread->GetState();
+                            suspended_user  = thread->IsSuspendRequested(KThread::SuspendType_Thread);
+                            suspended_debug = thread->IsSuspendRequested(KThread::SuspendType_Debug);
+                        }
+
+                        /* Set the suspend flags. */
+                        *out_32 = 0;
+                        if (suspended_user) {
+                            *out_32 |= ams::svc::ThreadSuspend_User;
+                        }
+                        if (suspended_debug) {
+                            *out_32 |= ams::svc::ThreadSuspend_Debug;
+                        }
+
+                        /* Set the state. */
+                        switch (state) {
+                            case KThread::ThreadState_Initialized:
+                                {
+                                    *out_64 = ams::svc::ThreadState_Initializing;
+                                }
+                                break;
+                            case KThread::ThreadState_Waiting:
+                                {
+                                    *out_64 = ams::svc::ThreadState_Waiting;
+                                }
+                                break;
+                            case KThread::ThreadState_Runnable:
+                                {
+                                    *out_64 = ams::svc::ThreadState_Running;
+                                }
+                                break;
+                            case KThread::ThreadState_Terminated:
+                                {
+                                    *out_64 = ams::svc::ThreadState_Terminated;
+                                }
+                                break;
+                            default:
+                                return svc::ResultInvalidState();
+                        }
+                    }
+                    break;
+                case ams::svc::DebugThreadParam_IdealCore:
+                    {
+                        /* Get the ideal core. */
+                        *out_32 = thread->GetIdealCore();
+                    }
+                    break;
+                case ams::svc::DebugThreadParam_CurrentCore:
+                    {
+                        /* Get the current core. */
+                        *out_32 = thread->GetActiveCore();
+                    }
+                    break;
+                case ams::svc::DebugThreadParam_AffinityMask:
+                    {
+                        /* Get the affinity mask. */
+                        *out_32 = thread->GetAffinityMask().GetAffinityMask();
+                    }
+                    break;
+                default:
+                    return ams::svc::ResultInvalidEnumValue();
+            }
+
+            return ResultSuccess();
+        }
+
     }
 
     /* =============================    64 ABI    ============================= */
@@ -299,7 +398,7 @@ namespace ams::kern::svc {
     }
 
     Result GetDebugThreadParam64(uint64_t *out_64, uint32_t *out_32, ams::svc::Handle debug_handle, uint64_t thread_id, ams::svc::DebugThreadParam param) {
-        MESOSPHERE_PANIC("Stubbed SvcGetDebugThreadParam64 was called.");
+        return GetDebugThreadParam(out_64, out_32, debug_handle, thread_id, param);
     }
 
     /* ============================= 64From32 ABI ============================= */
@@ -353,7 +452,7 @@ namespace ams::kern::svc {
     }
 
     Result GetDebugThreadParam64From32(uint64_t *out_64, uint32_t *out_32, ams::svc::Handle debug_handle, uint64_t thread_id, ams::svc::DebugThreadParam param) {
-        MESOSPHERE_PANIC("Stubbed SvcGetDebugThreadParam64From32 was called.");
+        return GetDebugThreadParam(out_64, out_32, debug_handle, thread_id, param);
     }
 
 }
diff --git a/libvapours/include/vapours/results/svc_results.hpp b/libvapours/include/vapours/results/svc_results.hpp
index 1ac86e89..d07d50e5 100644
--- a/libvapours/include/vapours/results/svc_results.hpp
+++ b/libvapours/include/vapours/results/svc_results.hpp
@@ -74,6 +74,7 @@ namespace ams::svc {
     R_DEFINE_ERROR_RESULT(MessageTooLarge,              260);
 
     R_DEFINE_ERROR_RESULT(InvalidProcessId,             517);
+    R_DEFINE_ERROR_RESULT(InvalidThreadId,              518);
     R_DEFINE_ERROR_RESULT(ProcessTerminated,            520);
 
 }