// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "common/hex_util.h" #include "core/debugger/gdbstub_arch.h" #include "core/hle/kernel/k_thread.h" namespace Core { template static T HexToValue(std::string_view hex) { static_assert(std::is_trivially_copyable_v); T value{}; const auto mem{Common::HexStringToVector(hex, false)}; std::memcpy(&value, mem.data(), std::min(mem.size(), sizeof(T))); return value; } template static std::string ValueToHex(const T value) { static_assert(std::is_trivially_copyable_v); std::array mem{}; std::memcpy(mem.data(), &value, sizeof(T)); return Common::HexToString(mem); } // 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 { return R"( aarch64 )"; } std::string GDBStubA64::RegRead(const Kernel::KThread* thread, size_t id) const { if (!thread) { return ""; } const auto& context{thread->GetContext()}; const auto& gprs{context.r}; const auto& fprs{context.v}; 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) { return ValueToHex(context.pc); } else if (id == PSTATE_REGISTER) { return ValueToHex(context.pstate); } else if (id >= Q0_REGISTER && id < FPSR_REGISTER) { return ValueToHex(fprs[id - Q0_REGISTER]); } else if (id == FPSR_REGISTER) { return ValueToHex(context.fpsr); } else if (id == FPCR_REGISTER) { return ValueToHex(context.fpcr); } else { return ""; } } void GDBStubA64::RegWrite(Kernel::KThread* thread, size_t id, std::string_view value) const { if (!thread) { return; } auto& context{thread->GetContext()}; if (id < FP_REGISTER) { context.r[id] = HexToValue(value); } else if (id == FP_REGISTER) { context.fp = HexToValue(value); } else if (id == LR_REGISTER) { context.lr = HexToValue(value); } else if (id == SP_REGISTER) { context.sp = HexToValue(value); } else if (id == PC_REGISTER) { context.pc = HexToValue(value); } else if (id == PSTATE_REGISTER) { context.pstate = HexToValue(value); } else if (id >= Q0_REGISTER && id < FPSR_REGISTER) { context.v[id - Q0_REGISTER] = HexToValue(value); } else if (id == FPSR_REGISTER) { context.fpsr = HexToValue(value); } else if (id == FPCR_REGISTER) { context.fpcr = HexToValue(value); } } std::string GDBStubA64::ReadRegisters(const Kernel::KThread* thread) const { std::string output; for (size_t reg = 0; reg <= FPCR_REGISTER; reg++) { output += RegRead(thread, reg); } return output; } void GDBStubA64::WriteRegisters(Kernel::KThread* thread, std::string_view register_data) const { for (size_t i = 0, reg = 0; reg <= FPCR_REGISTER; reg++) { if (reg <= SP_REGISTER || reg == PC_REGISTER) { RegWrite(thread, reg, register_data.substr(i, 16)); i += 16; } else if (reg == PSTATE_REGISTER || reg == FPCR_REGISTER || reg == FPSR_REGISTER) { RegWrite(thread, reg, register_data.substr(i, 8)); i += 8; } else if (reg >= Q0_REGISTER && reg < FPCR_REGISTER) { RegWrite(thread, reg, register_data.substr(i, 32)); i += 32; } } } std::string GDBStubA64::ThreadStatus(const Kernel::KThread* thread, u8 signal) const { return fmt::format("T{:02x}{:02x}:{};{:02x}:{};{:02x}:{};thread:{:x};", signal, PC_REGISTER, RegRead(thread, PC_REGISTER), SP_REGISTER, RegRead(thread, SP_REGISTER), LR_REGISTER, RegRead(thread, LR_REGISTER), thread->GetThreadId()); } u32 GDBStubA64::BreakpointInstruction() const { // A64: brk #0 return 0xd4200000; } std::string_view GDBStubA32::GetTargetXML() const { return R"( arm )"; } std::string GDBStubA32::RegRead(const Kernel::KThread* thread, size_t id) const { if (!thread) { return ""; } const auto& context{thread->GetContext()}; const auto& gprs{context.r}; const auto& fprs{context.v}; if (id <= PC_REGISTER) { return ValueToHex(static_cast(gprs[id])); } else if (id == CPSR_REGISTER) { return ValueToHex(context.pstate); } else if (id >= D0_REGISTER && id < Q0_REGISTER) { return ValueToHex(fprs[id - D0_REGISTER][0]); } else if (id >= Q0_REGISTER && id < FPSCR_REGISTER) { return ValueToHex(fprs[id - Q0_REGISTER]); } else if (id == FPSCR_REGISTER) { return ValueToHex(context.fpcr | context.fpsr); } else { return ""; } } void GDBStubA32::RegWrite(Kernel::KThread* thread, size_t id, std::string_view value) const { if (!thread) { return; } auto& context{thread->GetContext()}; auto& fprs{context.v}; if (id <= PC_REGISTER) { context.r[id] = HexToValue(value); } else if (id == CPSR_REGISTER) { context.pstate = HexToValue(value); } else if (id >= D0_REGISTER && id < Q0_REGISTER) { fprs[id - D0_REGISTER] = {HexToValue(value), 0}; } else if (id >= Q0_REGISTER && id < FPSCR_REGISTER) { fprs[id - Q0_REGISTER] = HexToValue(value); } else if (id == FPSCR_REGISTER) { context.fpcr = HexToValue(value); context.fpsr = HexToValue(value); } } std::string GDBStubA32::ReadRegisters(const Kernel::KThread* thread) const { std::string output; for (size_t reg = 0; reg <= FPSCR_REGISTER; reg++) { const bool gpr{reg <= PC_REGISTER}; const bool dfpr{reg >= D0_REGISTER && reg < Q0_REGISTER}; const bool qfpr{reg >= Q0_REGISTER && reg < FPSCR_REGISTER}; if (!(gpr || dfpr || qfpr || reg == CPSR_REGISTER || reg == FPSCR_REGISTER)) { continue; } output += RegRead(thread, reg); } return output; } void GDBStubA32::WriteRegisters(Kernel::KThread* thread, std::string_view register_data) const { for (size_t i = 0, reg = 0; reg <= FPSCR_REGISTER; reg++) { const bool gpr{reg <= PC_REGISTER}; const bool dfpr{reg >= D0_REGISTER && reg < Q0_REGISTER}; const bool qfpr{reg >= Q0_REGISTER && reg < FPSCR_REGISTER}; if (gpr || reg == CPSR_REGISTER || reg == FPSCR_REGISTER) { RegWrite(thread, reg, register_data.substr(i, 8)); i += 8; } else if (dfpr) { RegWrite(thread, reg, register_data.substr(i, 16)); i += 16; } else if (qfpr) { RegWrite(thread, reg, register_data.substr(i, 32)); i += 32; } if (reg == PC_REGISTER) { reg = CPSR_REGISTER - 1; } else if (reg == CPSR_REGISTER) { reg = D0_REGISTER - 1; } } } std::string GDBStubA32::ThreadStatus(const Kernel::KThread* thread, u8 signal) const { return fmt::format("T{:02x}{:02x}:{};{:02x}:{};{:02x}:{};thread:{:x};", signal, PC_REGISTER, RegRead(thread, PC_REGISTER), SP_REGISTER, RegRead(thread, SP_REGISTER), LR_REGISTER, RegRead(thread, LR_REGISTER), thread->GetThreadId()); } u32 GDBStubA32::BreakpointInstruction() const { // A32: trap // T32: trap + b #4 return 0xe7ffdefe; } } // namespace Core