/* * Copyright (c) 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 #include "osdbg_thread_info.os.horizon.hpp" #include "osdbg_thread_type.os.horizon.hpp" #include "osdbg_thread_local_region.os.horizon.hpp" #include "../../os/impl/os_thread_manager_impl.os.horizon.hpp" namespace ams::osdbg::impl { namespace { s32 ConvertToUserPriority(s32 horizon_priority) { return horizon_priority - os::impl::UserThreadPriorityOffset; } s32 GetCurrentThreadPriorityImpl(const ThreadInfo *info) { u64 dummy; u32 horizon_priority; if (R_FAILED(svc::GetDebugThreadParam(std::addressof(dummy), std::addressof(horizon_priority), info->_debug_handle, info->_debug_info_create_thread.thread_id, svc::DebugThreadParam_Priority))) { return info->_base_priority; } return ConvertToUserPriority(static_cast(horizon_priority)); } void FillWithCurrentInfoImpl(ThreadInfo *info, const auto &thread_type_impl) { /* Set fields. */ info->_base_priority = thread_type_impl._base_priority; info->_current_priority = GetCurrentThreadPriorityImpl(info); info->_stack_size = thread_type_impl._stack_size; info->_stack = thread_type_impl._stack; info->_argument = thread_type_impl._argument; info->_function = thread_type_impl._thread_function; info->_name_pointer = thread_type_impl._name_pointer; } } Result ThreadInfoHorizonImpl::FillWithCurrentInfo(ThreadInfo *info) { /* Detect lp64. */ const bool is_lp64 = IsLp64(info); /* Ensure that we have a thread type. */ if (info->_thread_type == nullptr) { /* Ensure we exit with correct thread type. */ auto thread_guard = SCOPE_GUARD { info->_thread_type = nullptr; }; /* Set the target thread type. */ GetTargetThreadType(info); /* If it's still nullptr, we failed to get the thread type. */ R_UNLESS(info->_thread_type != nullptr, osdbg::ResultCannotGetThreadInfo()); /* Check that the thread type is valid. */ R_UNLESS(info->_thread_type_type != ThreadTypeType_Unknown, osdbg::ResultUnsupportedThreadVersion()); /* We successfully got the thread type. */ thread_guard.Cancel(); } /* Read and process the thread type. */ ThreadTypeCommon thread_type; switch (info->_thread_type_type) { case ThreadTypeType_Nintendo: if (is_lp64) { /* Read in the thread type. */ R_TRY(svc::ReadDebugProcessMemory(reinterpret_cast(std::addressof(thread_type.lp64)), info->_debug_handle, reinterpret_cast(info->_thread_type), sizeof(thread_type.lp64))); /* Process different versions. */ switch (thread_type.lp64._version) { case 0x0000: case 0xFFFF: FillWithCurrentInfoImpl(info, thread_type.lp64_v0); break; case 0x0001: FillWithCurrentInfoImpl(info, thread_type.lp64); break; default: return osdbg::ResultUnsupportedThreadVersion(); } } else { /* Read in the thread type. */ R_TRY(svc::ReadDebugProcessMemory(reinterpret_cast(std::addressof(thread_type.ilp32)), info->_debug_handle, reinterpret_cast(info->_thread_type), sizeof(thread_type.ilp32))); /* Process different versions. */ switch (thread_type.ilp32._version) { case 0x0000: case 0xFFFF: FillWithCurrentInfoImpl(info, thread_type.ilp32_v0); break; case 0x0001: FillWithCurrentInfoImpl(info, thread_type.ilp32); break; default: return osdbg::ResultUnsupportedThreadVersion(); } } break; case ThreadTypeType_Stratosphere: { /* Read in the thread type. */ R_TRY(svc::ReadDebugProcessMemory(reinterpret_cast(std::addressof(thread_type.stratosphere)), info->_debug_handle, reinterpret_cast(info->_thread_type), sizeof(thread_type.stratosphere))); /* Set fields. */ const auto &thread_type_impl = thread_type.stratosphere; /* Check that our thread version is valid. */ R_UNLESS(thread_type_impl.version == 0x0000 || thread_type_impl.version == 0xFFFF, osdbg::ResultUnsupportedThreadVersion()); info->_base_priority = thread_type_impl.base_priority; info->_current_priority = GetCurrentThreadPriorityImpl(info); info->_stack_size = thread_type_impl.stack_size; info->_stack = reinterpret_cast(thread_type_impl.stack); info->_argument = reinterpret_cast(thread_type_impl.argument); info->_function = reinterpret_cast(thread_type_impl.function); info->_name_pointer = reinterpret_cast(thread_type_impl.name_pointer); } break; case ThreadTypeType_Libnx: { /* Read in the thread type. */ R_TRY(svc::ReadDebugProcessMemory(reinterpret_cast(std::addressof(thread_type.libnx)), info->_debug_handle, reinterpret_cast(info->_thread_type), sizeof(thread_type.libnx))); /* Set fields. */ const auto &thread_type_impl = thread_type.libnx; /* NOTE: libnx does not store/track base priority anywhere. */ info->_base_priority = -1; info->_current_priority = GetCurrentThreadPriorityImpl(info); if (info->_current_priority != info->_base_priority) { info->_base_priority = info->_current_priority; } info->_stack_size = thread_type_impl.stack_sz; info->_stack = reinterpret_cast(thread_type_impl.stack_mirror); /* Parse thread entry args. */ { LibnxThreadEntryArgs thread_entry_args; if (R_SUCCEEDED(svc::ReadDebugProcessMemory(reinterpret_cast(std::addressof(thread_entry_args)), info->_debug_handle, info->_stack + info->_stack_size, sizeof(LibnxThreadEntryArgs)))) { info->_argument = thread_entry_args.arg; info->_function = thread_entry_args.entry; } else { /* Failed to read the argument/function. */ info->_argument = 0; info->_function = 0; } } /* Libnx threads don't have names. */ info->_name_pointer = 0; } break; default: return osdbg::ResultUnsupportedThreadVersion(); } return ResultSuccess(); } }