diff options
author | Liam <byteslice@airmail.cc> | 2022-05-31 01:35:01 +0200 |
---|---|---|
committer | Liam <byteslice@airmail.cc> | 2022-06-01 06:01:25 +0200 |
commit | fb4b3c127f7c390358d7f4cadd2f58de116fec48 (patch) | |
tree | e2588a859d364a32be0cd9e0401a345c6492d1a7 /src/core/debugger/gdbstub_arch.cpp | |
parent | Merge pull request #8368 from german77/seventimes (diff) | |
download | yuzu-fb4b3c127f7c390358d7f4cadd2f58de116fec48.tar yuzu-fb4b3c127f7c390358d7f4cadd2f58de116fec48.tar.gz yuzu-fb4b3c127f7c390358d7f4cadd2f58de116fec48.tar.bz2 yuzu-fb4b3c127f7c390358d7f4cadd2f58de116fec48.tar.lz yuzu-fb4b3c127f7c390358d7f4cadd2f58de116fec48.tar.xz yuzu-fb4b3c127f7c390358d7f4cadd2f58de116fec48.tar.zst yuzu-fb4b3c127f7c390358d7f4cadd2f58de116fec48.zip |
Diffstat (limited to 'src/core/debugger/gdbstub_arch.cpp')
-rw-r--r-- | src/core/debugger/gdbstub_arch.cpp | 406 |
1 files changed, 406 insertions, 0 deletions
diff --git a/src/core/debugger/gdbstub_arch.cpp b/src/core/debugger/gdbstub_arch.cpp new file mode 100644 index 000000000..99e3893a9 --- /dev/null +++ b/src/core/debugger/gdbstub_arch.cpp @@ -0,0 +1,406 @@ +// 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 <typename T> +static T HexToValue(std::string_view hex) { + static_assert(std::is_trivially_copyable_v<T>); + T value{}; + const auto mem{Common::HexStringToVector(hex, false)}; + std::memcpy(&value, mem.data(), std::min(mem.size(), sizeof(T))); + return value; +} + +template <typename T> +static std::string ValueToHex(const T value) { + static_assert(std::is_trivially_copyable_v<T>); + std::array<u8, sizeof(T)> mem{}; + std::memcpy(mem.data(), &value, sizeof(T)); + 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 GDBStubA64::GetTargetXML() const { + constexpr const char* target_xml = + R"(<?xml version="1.0"?> +<!DOCTYPE target SYSTEM "gdb-target.dtd"> +<target version="1.0"> + <feature name="org.gnu.gdb.aarch64.core"> + <reg name="x0" bitsize="64"/> + <reg name="x1" bitsize="64"/> + <reg name="x2" bitsize="64"/> + <reg name="x3" bitsize="64"/> + <reg name="x4" bitsize="64"/> + <reg name="x5" bitsize="64"/> + <reg name="x6" bitsize="64"/> + <reg name="x7" bitsize="64"/> + <reg name="x8" bitsize="64"/> + <reg name="x9" bitsize="64"/> + <reg name="x10" bitsize="64"/> + <reg name="x11" bitsize="64"/> + <reg name="x12" bitsize="64"/> + <reg name="x13" bitsize="64"/> + <reg name="x14" bitsize="64"/> + <reg name="x15" bitsize="64"/> + <reg name="x16" bitsize="64"/> + <reg name="x17" bitsize="64"/> + <reg name="x18" bitsize="64"/> + <reg name="x19" bitsize="64"/> + <reg name="x20" bitsize="64"/> + <reg name="x21" bitsize="64"/> + <reg name="x22" bitsize="64"/> + <reg name="x23" bitsize="64"/> + <reg name="x24" bitsize="64"/> + <reg name="x25" bitsize="64"/> + <reg name="x26" bitsize="64"/> + <reg name="x27" bitsize="64"/> + <reg name="x28" bitsize="64"/> + <reg name="x29" bitsize="64"/> + <reg name="x30" bitsize="64"/> + <reg name="sp" bitsize="64" type="data_ptr"/> + <reg name="pc" bitsize="64" type="code_ptr"/> + <flags id="pstate_flags" size="4"> + <field name="SP" start="0" end="0"/> + <field name="" start="1" end="1"/> + <field name="EL" start="2" end="3"/> + <field name="nRW" start="4" end="4"/> + <field name="" start="5" end="5"/> + <field name="F" start="6" end="6"/> + <field name="I" start="7" end="7"/> + <field name="A" start="8" end="8"/> + <field name="D" start="9" end="9"/> + <field name="IL" start="20" end="20"/> + <field name="SS" start="21" end="21"/> + <field name="V" start="28" end="28"/> + <field name="C" start="29" end="29"/> + <field name="Z" start="30" end="30"/> + <field name="N" start="31" end="31"/> + </flags> + <reg name="pstate" bitsize="32" type="pstate_flags"/> + </feature> + <feature name="org.gnu.gdb.aarch64.fpu"> + </feature> +</target>)"; + + return target_xml; +} + +std::string GDBStubA64::RegRead(const Kernel::KThread* thread, size_t id) const { + if (!thread) { + return ""; + } + + const auto& context{thread->GetContext64()}; + const auto& gprs{context.cpu_registers}; + const auto& fprs{context.vector_registers}; + + if (id <= SP_REGISTER) { + return ValueToHex(gprs[id]); + } else if (id == PC_REGISTER) { + return ValueToHex(context.pc); + } else if (id == PSTATE_REGISTER) { + return ValueToHex(context.pstate); + } else if (id >= Q0_REGISTER && id < FPCR_REGISTER) { + return ValueToHex(fprs[id - Q0_REGISTER]); + } else if (id == FPCR_REGISTER) { + return ValueToHex(context.fpcr); + } else if (id == FPSR_REGISTER) { + return ValueToHex(context.fpsr); + } else { + return ""; + } +} + +void GDBStubA64::RegWrite(Kernel::KThread* thread, size_t id, std::string_view value) const { + if (!thread) { + return; + } + + auto& context{thread->GetContext64()}; + + if (id <= SP_REGISTER) { + context.cpu_registers[id] = HexToValue<u64>(value); + } else if (id == PC_REGISTER) { + context.pc = HexToValue<u64>(value); + } else if (id == PSTATE_REGISTER) { + context.pstate = HexToValue<u32>(value); + } else if (id >= Q0_REGISTER && id < FPCR_REGISTER) { + context.vector_registers[id - Q0_REGISTER] = HexToValue<u128>(value); + } else if (id == FPCR_REGISTER) { + context.fpcr = HexToValue<u32>(value); + } else if (id == FPSR_REGISTER) { + context.fpsr = HexToValue<u32>(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 GDBStubA32::GetTargetXML() const { + constexpr const char* target_xml = + R"(<?xml version="1.0"?> +<!DOCTYPE target SYSTEM "gdb-target.dtd"> +<target version="1.0"> + <feature name="org.gnu.gdb.arm.core"> + <reg name="r0" bitsize="32" type="uint32"/> + <reg name="r1" bitsize="32" type="uint32"/> + <reg name="r2" bitsize="32" type="uint32"/> + <reg name="r3" bitsize="32" type="uint32"/> + <reg name="r4" bitsize="32" type="uint32"/> + <reg name="r5" bitsize="32" type="uint32"/> + <reg name="r6" bitsize="32" type="uint32"/> + <reg name="r7" bitsize="32" type="uint32"/> + <reg name="r8" bitsize="32" type="uint32"/> + <reg name="r9" bitsize="32" type="uint32"/> + <reg name="r10" bitsize="32" type="uint32"/> + <reg name="r11" bitsize="32" type="uint32"/> + <reg name="r12" bitsize="32" type="uint32"/> + <reg name="sp" bitsize="32" type="data_ptr"/> + <reg name="lr" bitsize="32" type="code_ptr"/> + <reg name="pc" bitsize="32" type="code_ptr"/> + <!-- The CPSR is register 25, rather than register 16, because + the FPA registers historically were placed between the PC + and the CPSR in the "g" packet. --> + <reg name="cpsr" bitsize="32" regnum="25"/> + </feature> + <feature name="org.gnu.gdb.arm.vfp"> + <vector id="neon_uint8x8" type="uint8" count="8"/> + <vector id="neon_uint16x4" type="uint16" count="4"/> + <vector id="neon_uint32x2" type="uint32" count="2"/> + <vector id="neon_float32x2" type="ieee_single" count="2"/> + <union id="neon_d"> + <field name="u8" type="neon_uint8x8"/> + <field name="u16" type="neon_uint16x4"/> + <field name="u32" type="neon_uint32x2"/> + <field name="u64" type="uint64"/> + <field name="f32" type="neon_float32x2"/> + <field name="f64" type="ieee_double"/> + </union> + <vector id="neon_uint8x16" type="uint8" count="16"/> + <vector id="neon_uint16x8" type="uint16" count="8"/> + <vector id="neon_uint32x4" type="uint32" count="4"/> + <vector id="neon_uint64x2" type="uint64" count="2"/> + <vector id="neon_float32x4" type="ieee_single" count="4"/> + <vector id="neon_float64x2" type="ieee_double" count="2"/> + <union id="neon_q"> + <field name="u8" type="neon_uint8x16"/> + <field name="u16" type="neon_uint16x8"/> + <field name="u32" type="neon_uint32x4"/> + <field name="u64" type="neon_uint64x2"/> + <field name="f32" type="neon_float32x4"/> + <field name="f64" type="neon_float64x2"/> + </union> + <reg name="d0" bitsize="64" type="neon_d" regnum="32"/> + <reg name="d1" bitsize="64" type="neon_d"/> + <reg name="d2" bitsize="64" type="neon_d"/> + <reg name="d3" bitsize="64" type="neon_d"/> + <reg name="d4" bitsize="64" type="neon_d"/> + <reg name="d5" bitsize="64" type="neon_d"/> + <reg name="d6" bitsize="64" type="neon_d"/> + <reg name="d7" bitsize="64" type="neon_d"/> + <reg name="d8" bitsize="64" type="neon_d"/> + <reg name="d9" bitsize="64" type="neon_d"/> + <reg name="d10" bitsize="64" type="neon_d"/> + <reg name="d11" bitsize="64" type="neon_d"/> + <reg name="d12" bitsize="64" type="neon_d"/> + <reg name="d13" bitsize="64" type="neon_d"/> + <reg name="d14" bitsize="64" type="neon_d"/> + <reg name="d15" bitsize="64" type="neon_d"/> + <reg name="d16" bitsize="64" type="neon_d"/> + <reg name="d17" bitsize="64" type="neon_d"/> + <reg name="d18" bitsize="64" type="neon_d"/> + <reg name="d19" bitsize="64" type="neon_d"/> + <reg name="d20" bitsize="64" type="neon_d"/> + <reg name="d21" bitsize="64" type="neon_d"/> + <reg name="d22" bitsize="64" type="neon_d"/> + <reg name="d23" bitsize="64" type="neon_d"/> + <reg name="d24" bitsize="64" type="neon_d"/> + <reg name="d25" bitsize="64" type="neon_d"/> + <reg name="d26" bitsize="64" type="neon_d"/> + <reg name="d27" bitsize="64" type="neon_d"/> + <reg name="d28" bitsize="64" type="neon_d"/> + <reg name="d29" bitsize="64" type="neon_d"/> + <reg name="d30" bitsize="64" type="neon_d"/> + <reg name="d31" bitsize="64" type="neon_d"/> + + <reg name="q0" bitsize="128" type="neon_q" regnum="64"/> + <reg name="q1" bitsize="128" type="neon_q"/> + <reg name="q2" bitsize="128" type="neon_q"/> + <reg name="q3" bitsize="128" type="neon_q"/> + <reg name="q4" bitsize="128" type="neon_q"/> + <reg name="q5" bitsize="128" type="neon_q"/> + <reg name="q6" bitsize="128" type="neon_q"/> + <reg name="q7" bitsize="128" type="neon_q"/> + <reg name="q8" bitsize="128" type="neon_q"/> + <reg name="q9" bitsize="128" type="neon_q"/> + <reg name="q10" bitsize="128" type="neon_q"/> + <reg name="q10" bitsize="128" type="neon_q"/> + <reg name="q12" bitsize="128" type="neon_q"/> + <reg name="q13" bitsize="128" type="neon_q"/> + <reg name="q14" bitsize="128" type="neon_q"/> + <reg name="q15" bitsize="128" type="neon_q"/> + + <reg name="fpscr" bitsize="32" type="int" group="float" regnum="80"/> + </feature> +</target>)"; + + return target_xml; +} + +std::string GDBStubA32::RegRead(const Kernel::KThread* thread, size_t id) const { + if (!thread) { + return ""; + } + + const auto& context{thread->GetContext32()}; + const auto& gprs{context.cpu_registers}; + const auto& fprs{context.extension_registers}; + + if (id <= PC_REGISTER) { + return ValueToHex(gprs[id]); + } else if (id == CPSR_REGISTER) { + return ValueToHex(context.cpsr); + } else if (id >= D0_REGISTER && id < Q0_REGISTER) { + const u64 dN{GetSIMDRegister<u64>(fprs, id - D0_REGISTER)}; + return ValueToHex(dN); + } else if (id >= Q0_REGISTER && id < FPSCR_REGISTER) { + const u128 qN{GetSIMDRegister<u128>(fprs, id - Q0_REGISTER)}; + return ValueToHex(qN); + } else if (id == FPSCR_REGISTER) { + return ValueToHex(context.fpscr); + } else { + return ""; + } +} + +void GDBStubA32::RegWrite(Kernel::KThread* thread, size_t id, std::string_view value) const { + if (!thread) { + return; + } + + auto& context{thread->GetContext32()}; + auto& fprs{context.extension_registers}; + + if (id <= PC_REGISTER) { + context.cpu_registers[id] = HexToValue<u32>(value); + } else if (id == CPSR_REGISTER) { + context.cpsr = HexToValue<u32>(value); + } else if (id >= D0_REGISTER && id < Q0_REGISTER) { + PutSIMDRegister(fprs, id - D0_REGISTER, HexToValue<u64>(value)); + } else if (id >= Q0_REGISTER && id < FPSCR_REGISTER) { + PutSIMDRegister(fprs, id - Q0_REGISTER, HexToValue<u128>(value)); + } else if (id == FPSCR_REGISTER) { + context.fpscr = HexToValue<u32>(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 |