mirror of
https://github.com/yuzu-emu/yuzu.git
synced 2025-06-11 06:28:16 -05:00
core: refactor emulated cpu core activation
This commit is contained in:
@ -16,6 +16,7 @@
|
||||
#include "common/settings.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/arm/debug.h"
|
||||
#include "core/core.h"
|
||||
#include "core/debugger/gdbstub.h"
|
||||
#include "core/debugger/gdbstub_arch.h"
|
||||
@ -310,7 +311,7 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction
|
||||
const auto mem{Common::HexStringToVector(mem_substr, false)};
|
||||
|
||||
if (system.ApplicationMemory().WriteBlock(addr, mem.data(), size)) {
|
||||
system.InvalidateCpuInstructionCacheRange(addr, size);
|
||||
Core::InvalidateInstructionCacheRange(system.ApplicationProcess(), addr, size);
|
||||
SendReply(GDB_STUB_REPLY_OK);
|
||||
} else {
|
||||
SendReply(GDB_STUB_REPLY_ERR);
|
||||
@ -363,7 +364,7 @@ void GDBStub::HandleBreakpointInsert(std::string_view command) {
|
||||
case BreakpointType::Software:
|
||||
replaced_instructions[addr] = system.ApplicationMemory().Read32(addr);
|
||||
system.ApplicationMemory().Write32(addr, arch->BreakpointInstruction());
|
||||
system.InvalidateCpuInstructionCacheRange(addr, sizeof(u32));
|
||||
Core::InvalidateInstructionCacheRange(system.ApplicationProcess(), addr, sizeof(u32));
|
||||
success = true;
|
||||
break;
|
||||
case BreakpointType::WriteWatch:
|
||||
@ -411,7 +412,7 @@ void GDBStub::HandleBreakpointRemove(std::string_view command) {
|
||||
const auto orig_insn{replaced_instructions.find(addr)};
|
||||
if (orig_insn != replaced_instructions.end()) {
|
||||
system.ApplicationMemory().Write32(addr, orig_insn->second);
|
||||
system.InvalidateCpuInstructionCacheRange(addr, sizeof(u32));
|
||||
Core::InvalidateInstructionCacheRange(system.ApplicationProcess(), addr, sizeof(u32));
|
||||
replaced_instructions.erase(addr);
|
||||
success = true;
|
||||
}
|
||||
@ -442,114 +443,6 @@ void GDBStub::HandleBreakpointRemove(std::string_view command) {
|
||||
}
|
||||
}
|
||||
|
||||
// Structure offsets are from Atmosphere
|
||||
// See osdbg_thread_local_region.os.horizon.hpp and osdbg_thread_type.os.horizon.hpp
|
||||
|
||||
static 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);
|
||||
}
|
||||
|
||||
static 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);
|
||||
}
|
||||
|
||||
static std::optional<std::string> GetThreadName(Core::System& system,
|
||||
const Kernel::KThread& thread) {
|
||||
if (system.ApplicationProcess()->Is64Bit()) {
|
||||
return GetNameFromThreadType64(system.ApplicationMemory(), thread);
|
||||
} else {
|
||||
return GetNameFromThreadType32(system.ApplicationMemory(), thread);
|
||||
}
|
||||
}
|
||||
|
||||
static 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";
|
||||
}
|
||||
}
|
||||
|
||||
static 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";
|
||||
}
|
||||
}
|
||||
|
||||
static std::string PaginateBuffer(std::string_view buffer, std::string_view request) {
|
||||
const auto amount{request.substr(request.find(',') + 1)};
|
||||
const auto offset_val{static_cast<u64>(strtoll(request.data(), nullptr, 16))};
|
||||
@ -562,120 +455,6 @@ static std::string PaginateBuffer(std::string_view buffer, std::string_view requ
|
||||
}
|
||||
}
|
||||
|
||||
static VAddr GetModuleEnd(Kernel::KProcessPageTable& page_table, VAddr base) {
|
||||
Kernel::KMemoryInfo mem_info;
|
||||
Kernel::Svc::MemoryInfo svc_mem_info;
|
||||
Kernel::Svc::PageInfo page_info;
|
||||
VAddr cur_addr{base};
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
static Loader::AppLoader::Modules FindModules(Core::System& system) {
|
||||
Loader::AppLoader::Modules modules;
|
||||
|
||||
auto& page_table = system.ApplicationProcess()->GetPageTable();
|
||||
auto& memory = system.ApplicationMemory();
|
||||
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;
|
||||
}
|
||||
|
||||
static VAddr FindMainModuleEntrypoint(Core::System& system) {
|
||||
Loader::AppLoader::Modules modules;
|
||||
system.GetAppLoader().ReadNSOModules(modules);
|
||||
|
||||
// Do we have a module named main?
|
||||
const auto main = std::find_if(modules.begin(), modules.end(),
|
||||
[](const auto& key) { return key.second == "main"; });
|
||||
|
||||
if (main != modules.end()) {
|
||||
return main->first;
|
||||
}
|
||||
|
||||
// Do we have any loaded executable sections?
|
||||
modules = FindModules(system);
|
||||
if (!modules.empty()) {
|
||||
return modules.begin()->first;
|
||||
}
|
||||
|
||||
// As a last resort, use the start of the code region.
|
||||
return GetInteger(system.ApplicationProcess()->GetPageTable().GetCodeRegionStart());
|
||||
}
|
||||
|
||||
void GDBStub::HandleQuery(std::string_view command) {
|
||||
if (command.starts_with("TStatus")) {
|
||||
// no tracepoint support
|
||||
@ -687,10 +466,10 @@ void GDBStub::HandleQuery(std::string_view command) {
|
||||
const auto target_xml{arch->GetTargetXML()};
|
||||
SendReply(PaginateBuffer(target_xml, command.substr(30)));
|
||||
} else if (command.starts_with("Offsets")) {
|
||||
const auto main_offset = FindMainModuleEntrypoint(system);
|
||||
SendReply(fmt::format("TextSeg={:x}", main_offset));
|
||||
const auto main_offset = Core::FindMainModuleEntrypoint(system.ApplicationProcess());
|
||||
SendReply(fmt::format("TextSeg={:x}", GetInteger(main_offset)));
|
||||
} else if (command.starts_with("Xfer:libraries:read::")) {
|
||||
auto modules = FindModules(system);
|
||||
auto modules = Core::FindModules(system.ApplicationProcess());
|
||||
|
||||
std::string buffer;
|
||||
buffer += R"(<?xml version="1.0"?>)";
|
||||
@ -720,14 +499,14 @@ void GDBStub::HandleQuery(std::string_view command) {
|
||||
|
||||
const auto& threads = system.ApplicationProcess()->GetThreadList();
|
||||
for (const auto& thread : threads) {
|
||||
auto thread_name{GetThreadName(system, thread)};
|
||||
auto thread_name{Core::GetThreadName(&thread)};
|
||||
if (!thread_name) {
|
||||
thread_name = fmt::format("Thread {:d}", thread.GetThreadId());
|
||||
}
|
||||
|
||||
buffer += fmt::format(R"(<thread id="{:x}" core="{:d}" name="{}">{}</thread>)",
|
||||
thread.GetThreadId(), thread.GetActiveCore(),
|
||||
EscapeXML(*thread_name), GetThreadState(thread));
|
||||
EscapeXML(*thread_name), GetThreadState(&thread));
|
||||
}
|
||||
|
||||
buffer += "</threads>";
|
||||
@ -856,7 +635,7 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
|
||||
reply = "Fastmem is not enabled.\n";
|
||||
}
|
||||
} else if (command_str == "get info") {
|
||||
auto modules = FindModules(system);
|
||||
auto modules = Core::FindModules(process);
|
||||
|
||||
reply = fmt::format("Process: {:#x} ({})\n"
|
||||
"Program Id: {:#018x}\n",
|
||||
@ -880,7 +659,7 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
|
||||
|
||||
for (const auto& [vaddr, name] : modules) {
|
||||
reply += fmt::format(" {:#012x} - {:#012x} {}\n", vaddr,
|
||||
GetModuleEnd(page_table, vaddr), name);
|
||||
GetInteger(Core::GetModuleEnd(process, vaddr)), name);
|
||||
}
|
||||
} else if (command_str == "get mappings") {
|
||||
reply = "Mappings:\n";
|
||||
|
@ -24,21 +24,6 @@ static std::string ValueToHex(const T value) {
|
||||
return Common::HexToString(mem);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static T GetSIMDRegister(const std::array<u32, 64>& simd_regs, size_t offset) {
|
||||
static_assert(std::is_trivially_copyable_v<T>);
|
||||
T value{};
|
||||
std::memcpy(&value, reinterpret_cast<const u8*>(simd_regs.data()) + sizeof(T) * offset,
|
||||
sizeof(T));
|
||||
return value;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void PutSIMDRegister(std::array<u32, 64>& simd_regs, size_t offset, const T value) {
|
||||
static_assert(std::is_trivially_copyable_v<T>);
|
||||
std::memcpy(reinterpret_cast<u8*>(simd_regs.data()) + sizeof(T) * offset, &value, sizeof(T));
|
||||
}
|
||||
|
||||
// For sample XML files see the GDB source /gdb/features
|
||||
// This XML defines what the registers are for this specific ARM device
|
||||
std::string_view GDBStubA64::GetTargetXML() const {
|
||||
@ -184,12 +169,16 @@ std::string GDBStubA64::RegRead(const Kernel::KThread* thread, size_t id) const
|
||||
return "";
|
||||
}
|
||||
|
||||
const auto& context{thread->GetContext64()};
|
||||
const auto& gprs{context.cpu_registers};
|
||||
const auto& fprs{context.vector_registers};
|
||||
const auto& context{thread->GetContext()};
|
||||
const auto& gprs{context.r};
|
||||
const auto& fprs{context.v};
|
||||
|
||||
if (id < SP_REGISTER) {
|
||||
if (id < FP_REGISTER) {
|
||||
return ValueToHex(gprs[id]);
|
||||
} else if (id == FP_REGISTER) {
|
||||
return ValueToHex(context.fp);
|
||||
} else if (id == LR_REGISTER) {
|
||||
return ValueToHex(context.lr);
|
||||
} else if (id == SP_REGISTER) {
|
||||
return ValueToHex(context.sp);
|
||||
} else if (id == PC_REGISTER) {
|
||||
@ -212,10 +201,14 @@ void GDBStubA64::RegWrite(Kernel::KThread* thread, size_t id, std::string_view v
|
||||
return;
|
||||
}
|
||||
|
||||
auto& context{thread->GetContext64()};
|
||||
auto& context{thread->GetContext()};
|
||||
|
||||
if (id < SP_REGISTER) {
|
||||
context.cpu_registers[id] = HexToValue<u64>(value);
|
||||
if (id < FP_REGISTER) {
|
||||
context.r[id] = HexToValue<u64>(value);
|
||||
} else if (id == FP_REGISTER) {
|
||||
context.fp = HexToValue<u64>(value);
|
||||
} else if (id == LR_REGISTER) {
|
||||
context.lr = HexToValue<u64>(value);
|
||||
} else if (id == SP_REGISTER) {
|
||||
context.sp = HexToValue<u64>(value);
|
||||
} else if (id == PC_REGISTER) {
|
||||
@ -223,7 +216,7 @@ void GDBStubA64::RegWrite(Kernel::KThread* thread, size_t id, std::string_view v
|
||||
} else if (id == PSTATE_REGISTER) {
|
||||
context.pstate = HexToValue<u32>(value);
|
||||
} else if (id >= Q0_REGISTER && id < FPSR_REGISTER) {
|
||||
context.vector_registers[id - Q0_REGISTER] = HexToValue<u128>(value);
|
||||
context.v[id - Q0_REGISTER] = HexToValue<u128>(value);
|
||||
} else if (id == FPSR_REGISTER) {
|
||||
context.fpsr = HexToValue<u32>(value);
|
||||
} else if (id == FPCR_REGISTER) {
|
||||
@ -381,22 +374,20 @@ std::string GDBStubA32::RegRead(const Kernel::KThread* thread, size_t id) const
|
||||
return "";
|
||||
}
|
||||
|
||||
const auto& context{thread->GetContext32()};
|
||||
const auto& gprs{context.cpu_registers};
|
||||
const auto& fprs{context.extension_registers};
|
||||
const auto& context{thread->GetContext()};
|
||||
const auto& gprs{context.r};
|
||||
const auto& fprs{context.v};
|
||||
|
||||
if (id <= PC_REGISTER) {
|
||||
return ValueToHex(gprs[id]);
|
||||
return ValueToHex(static_cast<u32>(gprs[id]));
|
||||
} else if (id == CPSR_REGISTER) {
|
||||
return ValueToHex(context.cpsr);
|
||||
return ValueToHex(context.pstate);
|
||||
} else if (id >= D0_REGISTER && id < Q0_REGISTER) {
|
||||
const u64 dN{GetSIMDRegister<u64>(fprs, id - D0_REGISTER)};
|
||||
return ValueToHex(dN);
|
||||
return ValueToHex(fprs[id - D0_REGISTER][0]);
|
||||
} else if (id >= Q0_REGISTER && id < FPSCR_REGISTER) {
|
||||
const u128 qN{GetSIMDRegister<u128>(fprs, id - Q0_REGISTER)};
|
||||
return ValueToHex(qN);
|
||||
return ValueToHex(fprs[id - Q0_REGISTER]);
|
||||
} else if (id == FPSCR_REGISTER) {
|
||||
return ValueToHex(context.fpscr);
|
||||
return ValueToHex(context.fpcr | context.fpsr);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
@ -407,19 +398,20 @@ void GDBStubA32::RegWrite(Kernel::KThread* thread, size_t id, std::string_view v
|
||||
return;
|
||||
}
|
||||
|
||||
auto& context{thread->GetContext32()};
|
||||
auto& fprs{context.extension_registers};
|
||||
auto& context{thread->GetContext()};
|
||||
auto& fprs{context.v};
|
||||
|
||||
if (id <= PC_REGISTER) {
|
||||
context.cpu_registers[id] = HexToValue<u32>(value);
|
||||
context.r[id] = HexToValue<u32>(value);
|
||||
} else if (id == CPSR_REGISTER) {
|
||||
context.cpsr = HexToValue<u32>(value);
|
||||
context.pstate = HexToValue<u32>(value);
|
||||
} else if (id >= D0_REGISTER && id < Q0_REGISTER) {
|
||||
PutSIMDRegister(fprs, id - D0_REGISTER, HexToValue<u64>(value));
|
||||
fprs[id - D0_REGISTER] = {HexToValue<u64>(value), 0};
|
||||
} else if (id >= Q0_REGISTER && id < FPSCR_REGISTER) {
|
||||
PutSIMDRegister(fprs, id - Q0_REGISTER, HexToValue<u128>(value));
|
||||
fprs[id - Q0_REGISTER] = HexToValue<u128>(value);
|
||||
} else if (id == FPSCR_REGISTER) {
|
||||
context.fpscr = HexToValue<u32>(value);
|
||||
context.fpcr = HexToValue<u32>(value);
|
||||
context.fpsr = HexToValue<u32>(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,7 @@ public:
|
||||
u32 BreakpointInstruction() const override;
|
||||
|
||||
private:
|
||||
static constexpr u32 FP_REGISTER = 29;
|
||||
static constexpr u32 LR_REGISTER = 30;
|
||||
static constexpr u32 SP_REGISTER = 31;
|
||||
static constexpr u32 PC_REGISTER = 32;
|
||||
|
Reference in New Issue
Block a user