Browse Source
Merge pull request #12236 from liamwhite/cpu-refactor
Merge pull request #12236 from liamwhite/cpu-refactor
core: refactor emulated cpu core activationpull/15/merge
committed by
GitHub
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
47 changed files with 2925 additions and 3275 deletions
-
2.codespellrc
-
2src/core/CMakeLists.txt
-
217src/core/arm/arm_interface.cpp
-
221src/core/arm/arm_interface.h
-
351src/core/arm/debug.cpp
-
35src/core/arm/debug.h
-
325src/core/arm/dynarmic/arm_dynarmic_32.cpp
-
92src/core/arm/dynarmic/arm_dynarmic_32.h
-
340src/core/arm/dynarmic/arm_dynarmic_64.cpp
-
77src/core/arm/dynarmic/arm_dynarmic_64.h
-
4src/core/arm/dynarmic/dynarmic_cp15.cpp
-
8src/core/arm/dynarmic/dynarmic_cp15.h
-
8src/core/arm/dynarmic/dynarmic_exclusive_monitor.h
-
255src/core/arm/nce/arm_nce.cpp
-
70src/core/arm/nce/arm_nce.h
-
80src/core/arm/nce/arm_nce.s
-
8src/core/arm/nce/guest_context.h
-
2src/core/arm/nce/patcher.cpp
-
25src/core/core.cpp
-
22src/core/core.h
-
8src/core/cpu_manager.cpp
-
243src/core/debugger/gdbstub.cpp
-
72src/core/debugger/gdbstub_arch.cpp
-
1src/core/debugger/gdbstub_arch.h
-
18src/core/hle/kernel/k_page_table_base.cpp
-
40src/core/hle/kernel/k_process.cpp
-
9src/core/hle/kernel/k_process.h
-
4src/core/hle/kernel/k_process_page_table.h
-
16src/core/hle/kernel/k_scheduler.cpp
-
80src/core/hle/kernel/k_thread.cpp
-
29src/core/hle/kernel/k_thread.h
-
28src/core/hle/kernel/kernel.cpp
-
7src/core/hle/kernel/kernel.h
-
251src/core/hle/kernel/physical_core.cpp
-
57src/core/hle/kernel/physical_core.h
-
2955src/core/hle/kernel/svc.cpp
-
14src/core/hle/kernel/svc.h
-
4src/core/hle/kernel/svc/svc_exception.cpp
-
31src/core/hle/kernel/svc/svc_light_ipc.cpp
-
22src/core/hle/kernel/svc/svc_secure_monitor_call.cpp
-
50src/core/hle/kernel/svc/svc_thread.cpp
-
59src/core/hle/kernel/svc_generator.py
-
6src/core/hle/service/jit/jit.cpp
-
10src/core/memory.cpp
-
2src/core/memory.h
-
32src/core/reporter.cpp
-
8src/yuzu/debugger/wait_tree.cpp
@ -0,0 +1,351 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|||
|
|||
#include "common/demangle.h"
|
|||
#include "core/arm/debug.h"
|
|||
#include "core/arm/symbols.h"
|
|||
#include "core/hle/kernel/k_process.h"
|
|||
#include "core/hle/kernel/k_thread.h"
|
|||
#include "core/memory.h"
|
|||
|
|||
namespace Core { |
|||
|
|||
namespace { |
|||
|
|||
std::optional<std::string> GetNameFromThreadType64(Core::Memory::Memory& memory, |
|||
const Kernel::KThread& thread) { |
|||
// Read thread type from TLS
|
|||
const VAddr tls_thread_type{memory.Read64(thread.GetTlsAddress() + 0x1f8)}; |
|||
const VAddr argument_thread_type{thread.GetArgument()}; |
|||
|
|||
if (argument_thread_type && tls_thread_type != argument_thread_type) { |
|||
// Probably not created by nnsdk, no name available.
|
|||
return std::nullopt; |
|||
} |
|||
|
|||
if (!tls_thread_type) { |
|||
return std::nullopt; |
|||
} |
|||
|
|||
const u16 version{memory.Read16(tls_thread_type + 0x46)}; |
|||
VAddr name_pointer{}; |
|||
if (version == 1) { |
|||
name_pointer = memory.Read64(tls_thread_type + 0x1a0); |
|||
} else { |
|||
name_pointer = memory.Read64(tls_thread_type + 0x1a8); |
|||
} |
|||
|
|||
if (!name_pointer) { |
|||
// No name provided.
|
|||
return std::nullopt; |
|||
} |
|||
|
|||
return memory.ReadCString(name_pointer, 256); |
|||
} |
|||
|
|||
std::optional<std::string> GetNameFromThreadType32(Core::Memory::Memory& memory, |
|||
const Kernel::KThread& thread) { |
|||
// Read thread type from TLS
|
|||
const VAddr tls_thread_type{memory.Read32(thread.GetTlsAddress() + 0x1fc)}; |
|||
const VAddr argument_thread_type{thread.GetArgument()}; |
|||
|
|||
if (argument_thread_type && tls_thread_type != argument_thread_type) { |
|||
// Probably not created by nnsdk, no name available.
|
|||
return std::nullopt; |
|||
} |
|||
|
|||
if (!tls_thread_type) { |
|||
return std::nullopt; |
|||
} |
|||
|
|||
const u16 version{memory.Read16(tls_thread_type + 0x26)}; |
|||
VAddr name_pointer{}; |
|||
if (version == 1) { |
|||
name_pointer = memory.Read32(tls_thread_type + 0xe4); |
|||
} else { |
|||
name_pointer = memory.Read32(tls_thread_type + 0xe8); |
|||
} |
|||
|
|||
if (!name_pointer) { |
|||
// No name provided.
|
|||
return std::nullopt; |
|||
} |
|||
|
|||
return memory.ReadCString(name_pointer, 256); |
|||
} |
|||
|
|||
constexpr std::array<u64, 2> SegmentBases{ |
|||
0x60000000ULL, |
|||
0x7100000000ULL, |
|||
}; |
|||
|
|||
void SymbolicateBacktrace(const Kernel::KProcess* process, std::vector<BacktraceEntry>& out) { |
|||
auto modules = FindModules(process); |
|||
|
|||
const bool is_64 = process->Is64Bit(); |
|||
|
|||
std::map<std::string, Symbols::Symbols> symbols; |
|||
for (const auto& module : modules) { |
|||
symbols.insert_or_assign(module.second, |
|||
Symbols::GetSymbols(module.first, process->GetMemory(), is_64)); |
|||
} |
|||
|
|||
for (auto& entry : out) { |
|||
VAddr base = 0; |
|||
for (auto iter = modules.rbegin(); iter != modules.rend(); ++iter) { |
|||
const auto& module{*iter}; |
|||
if (entry.original_address >= module.first) { |
|||
entry.module = module.second; |
|||
base = module.first; |
|||
break; |
|||
} |
|||
} |
|||
|
|||
entry.offset = entry.original_address - base; |
|||
entry.address = SegmentBases[is_64] + entry.offset; |
|||
|
|||
if (entry.module.empty()) { |
|||
entry.module = "unknown"; |
|||
} |
|||
|
|||
const auto symbol_set = symbols.find(entry.module); |
|||
if (symbol_set != symbols.end()) { |
|||
const auto symbol = Symbols::GetSymbolName(symbol_set->second, entry.offset); |
|||
if (symbol) { |
|||
entry.name = Common::DemangleSymbol(*symbol); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
std::vector<BacktraceEntry> GetAArch64Backtrace(const Kernel::KProcess* process, |
|||
const Kernel::Svc::ThreadContext& ctx) { |
|||
std::vector<BacktraceEntry> out; |
|||
auto& memory = process->GetMemory(); |
|||
auto pc = ctx.pc, lr = ctx.lr, fp = ctx.fp; |
|||
|
|||
out.push_back({"", 0, pc, 0, ""}); |
|||
|
|||
// fp (= x29) points to the previous frame record.
|
|||
// Frame records are two words long:
|
|||
// fp+0 : pointer to previous frame record
|
|||
// fp+8 : value of lr for frame
|
|||
for (size_t i = 0; i < 256; i++) { |
|||
out.push_back({"", 0, lr, 0, ""}); |
|||
if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 16)) { |
|||
break; |
|||
} |
|||
lr = memory.Read64(fp + 8); |
|||
fp = memory.Read64(fp); |
|||
} |
|||
|
|||
SymbolicateBacktrace(process, out); |
|||
|
|||
return out; |
|||
} |
|||
|
|||
std::vector<BacktraceEntry> GetAArch32Backtrace(const Kernel::KProcess* process, |
|||
const Kernel::Svc::ThreadContext& ctx) { |
|||
std::vector<BacktraceEntry> out; |
|||
auto& memory = process->GetMemory(); |
|||
auto pc = ctx.pc, lr = ctx.lr, fp = ctx.fp; |
|||
|
|||
out.push_back({"", 0, pc, 0, ""}); |
|||
|
|||
// fp (= r11) points to the last frame record.
|
|||
// Frame records are two words long:
|
|||
// fp+0 : pointer to previous frame record
|
|||
// fp+4 : value of lr for frame
|
|||
for (size_t i = 0; i < 256; i++) { |
|||
out.push_back({"", 0, lr, 0, ""}); |
|||
if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 8)) { |
|||
break; |
|||
} |
|||
lr = memory.Read32(fp + 4); |
|||
fp = memory.Read32(fp); |
|||
} |
|||
|
|||
SymbolicateBacktrace(process, out); |
|||
|
|||
return out; |
|||
} |
|||
|
|||
} // namespace
|
|||
|
|||
std::optional<std::string> GetThreadName(const Kernel::KThread* thread) { |
|||
const auto* process = thread->GetOwnerProcess(); |
|||
if (process->Is64Bit()) { |
|||
return GetNameFromThreadType64(process->GetMemory(), *thread); |
|||
} else { |
|||
return GetNameFromThreadType32(process->GetMemory(), *thread); |
|||
} |
|||
} |
|||
|
|||
std::string_view GetThreadWaitReason(const Kernel::KThread* thread) { |
|||
switch (thread->GetWaitReasonForDebugging()) { |
|||
case Kernel::ThreadWaitReasonForDebugging::Sleep: |
|||
return "Sleep"; |
|||
case Kernel::ThreadWaitReasonForDebugging::IPC: |
|||
return "IPC"; |
|||
case Kernel::ThreadWaitReasonForDebugging::Synchronization: |
|||
return "Synchronization"; |
|||
case Kernel::ThreadWaitReasonForDebugging::ConditionVar: |
|||
return "ConditionVar"; |
|||
case Kernel::ThreadWaitReasonForDebugging::Arbitration: |
|||
return "Arbitration"; |
|||
case Kernel::ThreadWaitReasonForDebugging::Suspended: |
|||
return "Suspended"; |
|||
default: |
|||
return "Unknown"; |
|||
} |
|||
} |
|||
|
|||
std::string GetThreadState(const Kernel::KThread* thread) { |
|||
switch (thread->GetState()) { |
|||
case Kernel::ThreadState::Initialized: |
|||
return "Initialized"; |
|||
case Kernel::ThreadState::Waiting: |
|||
return fmt::format("Waiting ({})", GetThreadWaitReason(thread)); |
|||
case Kernel::ThreadState::Runnable: |
|||
return "Runnable"; |
|||
case Kernel::ThreadState::Terminated: |
|||
return "Terminated"; |
|||
default: |
|||
return "Unknown"; |
|||
} |
|||
} |
|||
|
|||
Kernel::KProcessAddress GetModuleEnd(const Kernel::KProcess* process, |
|||
Kernel::KProcessAddress base) { |
|||
Kernel::KMemoryInfo mem_info; |
|||
Kernel::Svc::MemoryInfo svc_mem_info; |
|||
Kernel::Svc::PageInfo page_info; |
|||
VAddr cur_addr{GetInteger(base)}; |
|||
auto& page_table = process->GetPageTable(); |
|||
|
|||
// Expect: r-x Code (.text)
|
|||
R_ASSERT(page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info), cur_addr)); |
|||
svc_mem_info = mem_info.GetSvcMemoryInfo(); |
|||
cur_addr = svc_mem_info.base_address + svc_mem_info.size; |
|||
if (svc_mem_info.state != Kernel::Svc::MemoryState::Code || |
|||
svc_mem_info.permission != Kernel::Svc::MemoryPermission::ReadExecute) { |
|||
return cur_addr - 1; |
|||
} |
|||
|
|||
// Expect: r-- Code (.rodata)
|
|||
R_ASSERT(page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info), cur_addr)); |
|||
svc_mem_info = mem_info.GetSvcMemoryInfo(); |
|||
cur_addr = svc_mem_info.base_address + svc_mem_info.size; |
|||
if (svc_mem_info.state != Kernel::Svc::MemoryState::Code || |
|||
svc_mem_info.permission != Kernel::Svc::MemoryPermission::Read) { |
|||
return cur_addr - 1; |
|||
} |
|||
|
|||
// Expect: rw- CodeData (.data)
|
|||
R_ASSERT(page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info), cur_addr)); |
|||
svc_mem_info = mem_info.GetSvcMemoryInfo(); |
|||
cur_addr = svc_mem_info.base_address + svc_mem_info.size; |
|||
return cur_addr - 1; |
|||
} |
|||
|
|||
Loader::AppLoader::Modules FindModules(const Kernel::KProcess* process) { |
|||
Loader::AppLoader::Modules modules; |
|||
|
|||
auto& page_table = process->GetPageTable(); |
|||
auto& memory = process->GetMemory(); |
|||
VAddr cur_addr = 0; |
|||
|
|||
// Look for executable sections in Code or AliasCode regions.
|
|||
while (true) { |
|||
Kernel::KMemoryInfo mem_info{}; |
|||
Kernel::Svc::PageInfo page_info{}; |
|||
R_ASSERT( |
|||
page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info), cur_addr)); |
|||
auto svc_mem_info = mem_info.GetSvcMemoryInfo(); |
|||
|
|||
if (svc_mem_info.permission == Kernel::Svc::MemoryPermission::ReadExecute && |
|||
(svc_mem_info.state == Kernel::Svc::MemoryState::Code || |
|||
svc_mem_info.state == Kernel::Svc::MemoryState::AliasCode)) { |
|||
// Try to read the module name from its path.
|
|||
constexpr s32 PathLengthMax = 0x200; |
|||
struct { |
|||
u32 zero; |
|||
s32 path_length; |
|||
std::array<char, PathLengthMax> path; |
|||
} module_path; |
|||
|
|||
if (memory.ReadBlock(svc_mem_info.base_address + svc_mem_info.size, &module_path, |
|||
sizeof(module_path))) { |
|||
if (module_path.zero == 0 && module_path.path_length > 0) { |
|||
// Truncate module name.
|
|||
module_path.path[PathLengthMax - 1] = '\0'; |
|||
|
|||
// Ignore leading directories.
|
|||
char* path_pointer = module_path.path.data(); |
|||
|
|||
for (s32 i = 0; i < std::min(PathLengthMax, module_path.path_length) && |
|||
module_path.path[i] != '\0'; |
|||
i++) { |
|||
if (module_path.path[i] == '/' || module_path.path[i] == '\\') { |
|||
path_pointer = module_path.path.data() + i + 1; |
|||
} |
|||
} |
|||
|
|||
// Insert output.
|
|||
modules.emplace(svc_mem_info.base_address, path_pointer); |
|||
} |
|||
} |
|||
} |
|||
|
|||
// Check if we're done.
|
|||
const uintptr_t next_address = svc_mem_info.base_address + svc_mem_info.size; |
|||
if (next_address <= cur_addr) { |
|||
break; |
|||
} |
|||
|
|||
cur_addr = next_address; |
|||
} |
|||
|
|||
return modules; |
|||
} |
|||
|
|||
Kernel::KProcessAddress FindMainModuleEntrypoint(const Kernel::KProcess* process) { |
|||
// Do we have any loaded executable sections?
|
|||
auto modules = FindModules(process); |
|||
|
|||
if (modules.size() >= 2) { |
|||
// If we have two or more, the first one is rtld and the second is main.
|
|||
return std::next(modules.begin())->first; |
|||
} else if (!modules.empty()) { |
|||
// If we only have one, this is the main module.
|
|||
return modules.begin()->first; |
|||
} |
|||
|
|||
// As a last resort, use the start of the code region.
|
|||
return GetInteger(process->GetPageTable().GetCodeRegionStart()); |
|||
} |
|||
|
|||
void InvalidateInstructionCacheRange(const Kernel::KProcess* process, u64 address, u64 size) { |
|||
for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { |
|||
auto* interface = process->GetArmInterface(i); |
|||
if (interface) { |
|||
interface->InvalidateCacheRange(address, size); |
|||
} |
|||
} |
|||
} |
|||
|
|||
std::vector<BacktraceEntry> GetBacktraceFromContext(const Kernel::KProcess* process, |
|||
const Kernel::Svc::ThreadContext& ctx) { |
|||
if (process->Is64Bit()) { |
|||
return GetAArch64Backtrace(process, ctx); |
|||
} else { |
|||
return GetAArch32Backtrace(process, ctx); |
|||
} |
|||
} |
|||
|
|||
std::vector<BacktraceEntry> GetBacktrace(const Kernel::KThread* thread) { |
|||
Kernel::Svc::ThreadContext ctx = thread->GetContext(); |
|||
return GetBacktraceFromContext(thread->GetOwnerProcess(), ctx); |
|||
} |
|||
|
|||
} // namespace Core
|
|||
@ -0,0 +1,35 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project |
|||
// SPDX-License-Identifier: GPL-2.0-or-later |
|||
|
|||
#pragma once |
|||
|
|||
#include <optional> |
|||
|
|||
#include "core/hle/kernel/k_thread.h" |
|||
#include "core/loader/loader.h" |
|||
|
|||
namespace Core { |
|||
|
|||
std::optional<std::string> GetThreadName(const Kernel::KThread* thread); |
|||
std::string_view GetThreadWaitReason(const Kernel::KThread* thread); |
|||
std::string GetThreadState(const Kernel::KThread* thread); |
|||
|
|||
Loader::AppLoader::Modules FindModules(const Kernel::KProcess* process); |
|||
Kernel::KProcessAddress GetModuleEnd(const Kernel::KProcess* process, Kernel::KProcessAddress base); |
|||
Kernel::KProcessAddress FindMainModuleEntrypoint(const Kernel::KProcess* process); |
|||
|
|||
void InvalidateInstructionCacheRange(const Kernel::KProcess* process, u64 address, u64 size); |
|||
|
|||
struct BacktraceEntry { |
|||
std::string module; |
|||
u64 address; |
|||
u64 original_address; |
|||
u64 offset; |
|||
std::string name; |
|||
}; |
|||
|
|||
std::vector<BacktraceEntry> GetBacktraceFromContext(const Kernel::KProcess* process, |
|||
const Kernel::Svc::ThreadContext& ctx); |
|||
std::vector<BacktraceEntry> GetBacktrace(const Kernel::KThread* thread); |
|||
|
|||
} // namespace Core |
|||
2955
src/core/hle/kernel/svc.cpp
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
Write
Preview
Loading…
Cancel
Save
Reference in new issue