Jelajahi Sumber

kernel/svc: Implement svcGetThreadList

Similarly like svcGetProcessList, this retrieves the list of threads
from the current process. In the kernel itself, a process instance
maintains a list of threads, which are used within this function.

Threads are registered to a process' thread list at thread
initialization, and unregistered from the list upon thread destruction
(if said thread has a non-null owning process).

We assert on the debug event case, as we currently don't implement
kernel debug objects.
Lioncash 7 tahun lalu
induk
melakukan
28719ee3b4

+ 8 - 0
src/core/hle/kernel/process.cpp

@@ -80,6 +80,14 @@ u64 Process::GetTotalPhysicalMemoryUsed() const {
     return vm_manager.GetCurrentHeapSize() + main_thread_stack_size + code_memory_size;
 }
 
+void Process::RegisterThread(const Thread* thread) {
+    thread_list.push_back(thread);
+}
+
+void Process::UnregisterThread(const Thread* thread) {
+    thread_list.remove(thread);
+}
+
 ResultCode Process::ClearSignalState() {
     if (status == ProcessStatus::Exited) {
         LOG_ERROR(Kernel, "called on a terminated process instance.");

+ 17 - 0
src/core/hle/kernel/process.h

@@ -7,6 +7,7 @@
 #include <array>
 #include <bitset>
 #include <cstddef>
+#include <list>
 #include <string>
 #include <vector>
 #include <boost/container/static_vector.hpp>
@@ -189,6 +190,19 @@ public:
     /// Retrieves the total physical memory used by this process in bytes.
     u64 GetTotalPhysicalMemoryUsed() const;
 
+    /// Gets the list of all threads created with this process as their owner.
+    const std::list<const Thread*>& GetThreadList() const {
+        return thread_list;
+    }
+
+    /// Registers a thread as being created under this process,
+    /// adding it to this process' thread list.
+    void RegisterThread(const Thread* thread);
+
+    /// Unregisters a thread from this process, removing it
+    /// from this process' thread list.
+    void UnregisterThread(const Thread* thread);
+
     /// Clears the signaled state of the process if and only if it's signaled.
     ///
     /// @pre The process must not be already terminated. If this is called on a
@@ -308,6 +322,9 @@ private:
     /// Random values for svcGetInfo RandomEntropy
     std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy;
 
+    /// List of threads that are running with this process as their owner.
+    std::list<const Thread*> thread_list;
+
     /// System context
     Core::System& system;
 

+ 41 - 1
src/core/hle/kernel/svc.cpp

@@ -2020,6 +2020,46 @@ static ResultCode GetProcessList(u32* out_num_processes, VAddr out_process_ids,
     return RESULT_SUCCESS;
 }
 
+ResultCode GetThreadList(u32* out_num_threads, VAddr out_thread_ids, u32 out_thread_ids_size,
+                         Handle debug_handle) {
+    // TODO: Handle this case when debug events are supported.
+    UNIMPLEMENTED_IF(debug_handle != InvalidHandle);
+
+    LOG_DEBUG(Kernel_SVC, "called. out_thread_ids=0x{:016X}, out_thread_ids_size={}",
+              out_thread_ids, out_thread_ids_size);
+
+    // If the size is negative or larger than INT32_MAX / sizeof(u64)
+    if ((out_thread_ids_size & 0xF0000000) != 0) {
+        LOG_ERROR(Kernel_SVC, "Supplied size outside [0, 0x0FFFFFFF] range. size={}",
+                  out_thread_ids_size);
+        return ERR_OUT_OF_RANGE;
+    }
+
+    const auto* const current_process = Core::System::GetInstance().Kernel().CurrentProcess();
+    const auto& vm_manager = current_process->VMManager();
+    const auto total_copy_size = out_thread_ids_size * sizeof(u64);
+
+    if (out_thread_ids_size > 0 &&
+        !vm_manager.IsWithinAddressSpace(out_thread_ids, total_copy_size)) {
+        LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}",
+                  out_thread_ids, out_thread_ids + total_copy_size);
+        return ERR_INVALID_ADDRESS_STATE;
+    }
+
+    const auto& thread_list = current_process->GetThreadList();
+    const auto num_threads = thread_list.size();
+    const auto copy_amount = std::min(std::size_t{out_thread_ids_size}, num_threads);
+
+    auto list_iter = thread_list.cbegin();
+    for (std::size_t i = 0; i < copy_amount; ++i, ++list_iter) {
+        Memory::Write64(out_thread_ids, (*list_iter)->GetThreadID());
+        out_thread_ids += sizeof(u64);
+    }
+
+    *out_num_threads = static_cast<u32>(num_threads);
+    return RESULT_SUCCESS;
+}
+
 namespace {
 struct FunctionDef {
     using Func = void();
@@ -2133,7 +2173,7 @@ static const FunctionDef SVC_Table[] = {
     {0x63, nullptr, "GetDebugEvent"},
     {0x64, nullptr, "ContinueDebugEvent"},
     {0x65, SvcWrap<GetProcessList>, "GetProcessList"},
-    {0x66, nullptr, "GetThreadList"},
+    {0x66, SvcWrap<GetThreadList>, "GetThreadList"},
     {0x67, nullptr, "GetDebugThreadContext"},
     {0x68, nullptr, "SetDebugThreadContext"},
     {0x69, nullptr, "QueryDebugProcessMemory"},

+ 4 - 0
src/core/hle/kernel/thread.cpp

@@ -62,6 +62,8 @@ void Thread::Stop() {
     }
     wait_objects.clear();
 
+    owner_process->UnregisterThread(this);
+
     // Mark the TLS slot in the thread's page as free.
     owner_process->FreeTLSSlot(tls_address);
 }
@@ -202,6 +204,8 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
     thread->scheduler->AddThread(thread);
     thread->tls_address = thread->owner_process->MarkNextAvailableTLSSlotAsUsed(*thread);
 
+    thread->owner_process->RegisterThread(thread.get());
+
     // TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used
     // to initialize the context
     ResetThreadContext(thread->context, stack_top, entry_point, arg);