| //===-- NativeRegisterContextLinux_mips64.cpp ---------------*- C++ -*-===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #if defined (__mips__) |
| |
| #include "NativeRegisterContextLinux_mips64.h" |
| |
| // C Includes |
| // C++ Includes |
| |
| // Other libraries and framework includes |
| #include "lldb/Core/Error.h" |
| #include "lldb/Core/RegisterValue.h" |
| #include "lldb/Core/Log.h" |
| #include "lldb/Core/DataBufferHeap.h" |
| #include "lldb/Host/HostInfo.h" |
| #include "lldb/Core/EmulateInstruction.h" |
| #include "lldb/lldb-enumerations.h" |
| #include "lldb/lldb-private-enumerations.h" |
| #include "Plugins/Process/Linux/NativeProcessLinux.h" |
| #include "Plugins/Process/Linux/Procfs.h" |
| #include "Plugins/Process/Utility/RegisterContextLinux_mips64.h" |
| #include "Plugins/Process/Utility/RegisterContextLinux_mips.h" |
| #define NT_MIPS_MSA 0x600 |
| #define CONFIG5_FRE (1 << 8) |
| #define SR_FR (1 << 26) |
| #define NUM_REGISTERS 32 |
| |
| #include <sys/ptrace.h> |
| #include <asm/ptrace.h> |
| |
| #ifndef PTRACE_GET_WATCH_REGS |
| enum pt_watch_style |
| { |
| pt_watch_style_mips32, |
| pt_watch_style_mips64 |
| }; |
| struct mips32_watch_regs |
| { |
| uint32_t watchlo[8]; |
| uint16_t watchhi[8]; |
| uint16_t watch_masks[8]; |
| uint32_t num_valid; |
| } __attribute__((aligned(8))); |
| |
| struct mips64_watch_regs |
| { |
| uint64_t watchlo[8]; |
| uint16_t watchhi[8]; |
| uint16_t watch_masks[8]; |
| uint32_t num_valid; |
| } __attribute__((aligned(8))); |
| |
| struct pt_watch_regs |
| { |
| enum pt_watch_style style; |
| union |
| { |
| struct mips32_watch_regs mips32; |
| struct mips64_watch_regs mips64; |
| }; |
| }; |
| |
| #define PTRACE_GET_WATCH_REGS 0xd0 |
| #define PTRACE_SET_WATCH_REGS 0xd1 |
| #endif |
| |
| #define W (1 << 0) |
| #define R (1 << 1) |
| #define I (1 << 2) |
| |
| #define IRW (I | R | W) |
| |
| struct pt_watch_regs default_watch_regs; |
| |
| using namespace lldb_private; |
| using namespace lldb_private::process_linux; |
| |
| // ---------------------------------------------------------------------------- |
| // Private namespace. |
| // ---------------------------------------------------------------------------- |
| |
| namespace |
| { |
| // mips general purpose registers. |
| const uint32_t |
| g_gp_regnums_mips[] = |
| { |
| gpr_zero_mips, |
| gpr_r1_mips, |
| gpr_r2_mips, |
| gpr_r3_mips, |
| gpr_r4_mips, |
| gpr_r5_mips, |
| gpr_r6_mips, |
| gpr_r7_mips, |
| gpr_r8_mips, |
| gpr_r9_mips, |
| gpr_r10_mips, |
| gpr_r11_mips, |
| gpr_r12_mips, |
| gpr_r13_mips, |
| gpr_r14_mips, |
| gpr_r15_mips, |
| gpr_r16_mips, |
| gpr_r17_mips, |
| gpr_r18_mips, |
| gpr_r19_mips, |
| gpr_r20_mips, |
| gpr_r21_mips, |
| gpr_r22_mips, |
| gpr_r23_mips, |
| gpr_r24_mips, |
| gpr_r25_mips, |
| gpr_r26_mips, |
| gpr_r27_mips, |
| gpr_gp_mips, |
| gpr_sp_mips, |
| gpr_r30_mips, |
| gpr_ra_mips, |
| gpr_sr_mips, |
| gpr_mullo_mips, |
| gpr_mulhi_mips, |
| gpr_badvaddr_mips, |
| gpr_cause_mips, |
| gpr_pc_mips, |
| gpr_config5_mips, |
| LLDB_INVALID_REGNUM // register sets need to end with this flag |
| }; |
| |
| static_assert((sizeof(g_gp_regnums_mips) / sizeof(g_gp_regnums_mips[0])) - 1 == k_num_gpr_registers_mips, |
| "g_gp_regnums_mips has wrong number of register infos"); |
| |
| // mips floating point registers. |
| const uint32_t |
| g_fp_regnums_mips[] = |
| { |
| fpr_f0_mips, |
| fpr_f1_mips, |
| fpr_f2_mips, |
| fpr_f3_mips, |
| fpr_f4_mips, |
| fpr_f5_mips, |
| fpr_f6_mips, |
| fpr_f7_mips, |
| fpr_f8_mips, |
| fpr_f9_mips, |
| fpr_f10_mips, |
| fpr_f11_mips, |
| fpr_f12_mips, |
| fpr_f13_mips, |
| fpr_f14_mips, |
| fpr_f15_mips, |
| fpr_f16_mips, |
| fpr_f17_mips, |
| fpr_f18_mips, |
| fpr_f19_mips, |
| fpr_f20_mips, |
| fpr_f21_mips, |
| fpr_f22_mips, |
| fpr_f23_mips, |
| fpr_f24_mips, |
| fpr_f25_mips, |
| fpr_f26_mips, |
| fpr_f27_mips, |
| fpr_f28_mips, |
| fpr_f29_mips, |
| fpr_f30_mips, |
| fpr_f31_mips, |
| fpr_fcsr_mips, |
| fpr_fir_mips, |
| fpr_config5_mips, |
| LLDB_INVALID_REGNUM // register sets need to end with this flag |
| }; |
| |
| static_assert((sizeof(g_fp_regnums_mips) / sizeof(g_fp_regnums_mips[0])) - 1 == k_num_fpr_registers_mips, |
| "g_fp_regnums_mips has wrong number of register infos"); |
| |
| // mips MSA registers. |
| const uint32_t |
| g_msa_regnums_mips[] = |
| { |
| msa_w0_mips, |
| msa_w1_mips, |
| msa_w2_mips, |
| msa_w3_mips, |
| msa_w4_mips, |
| msa_w5_mips, |
| msa_w6_mips, |
| msa_w7_mips, |
| msa_w8_mips, |
| msa_w9_mips, |
| msa_w10_mips, |
| msa_w11_mips, |
| msa_w12_mips, |
| msa_w13_mips, |
| msa_w14_mips, |
| msa_w15_mips, |
| msa_w16_mips, |
| msa_w17_mips, |
| msa_w18_mips, |
| msa_w19_mips, |
| msa_w20_mips, |
| msa_w21_mips, |
| msa_w22_mips, |
| msa_w23_mips, |
| msa_w24_mips, |
| msa_w25_mips, |
| msa_w26_mips, |
| msa_w27_mips, |
| msa_w28_mips, |
| msa_w29_mips, |
| msa_w30_mips, |
| msa_w31_mips, |
| msa_fcsr_mips, |
| msa_fir_mips, |
| msa_mcsr_mips, |
| msa_mir_mips, |
| msa_config5_mips, |
| LLDB_INVALID_REGNUM // register sets need to end with this flag |
| }; |
| |
| static_assert((sizeof(g_msa_regnums_mips) / sizeof(g_msa_regnums_mips[0])) - 1 == k_num_msa_registers_mips, |
| "g_msa_regnums_mips has wrong number of register infos"); |
| |
| // mips64 general purpose registers. |
| const uint32_t |
| g_gp_regnums_mips64[] = |
| { |
| gpr_zero_mips64, |
| gpr_r1_mips64, |
| gpr_r2_mips64, |
| gpr_r3_mips64, |
| gpr_r4_mips64, |
| gpr_r5_mips64, |
| gpr_r6_mips64, |
| gpr_r7_mips64, |
| gpr_r8_mips64, |
| gpr_r9_mips64, |
| gpr_r10_mips64, |
| gpr_r11_mips64, |
| gpr_r12_mips64, |
| gpr_r13_mips64, |
| gpr_r14_mips64, |
| gpr_r15_mips64, |
| gpr_r16_mips64, |
| gpr_r17_mips64, |
| gpr_r18_mips64, |
| gpr_r19_mips64, |
| gpr_r20_mips64, |
| gpr_r21_mips64, |
| gpr_r22_mips64, |
| gpr_r23_mips64, |
| gpr_r24_mips64, |
| gpr_r25_mips64, |
| gpr_r26_mips64, |
| gpr_r27_mips64, |
| gpr_gp_mips64, |
| gpr_sp_mips64, |
| gpr_r30_mips64, |
| gpr_ra_mips64, |
| gpr_sr_mips64, |
| gpr_mullo_mips64, |
| gpr_mulhi_mips64, |
| gpr_badvaddr_mips64, |
| gpr_cause_mips64, |
| gpr_pc_mips64, |
| gpr_config5_mips64, |
| LLDB_INVALID_REGNUM // register sets need to end with this flag |
| }; |
| |
| static_assert((sizeof(g_gp_regnums_mips64) / sizeof(g_gp_regnums_mips64[0])) - 1 == k_num_gpr_registers_mips64, |
| "g_gp_regnums_mips64 has wrong number of register infos"); |
| |
| // mips64 floating point registers. |
| const uint32_t |
| g_fp_regnums_mips64[] = |
| { |
| fpr_f0_mips64, |
| fpr_f1_mips64, |
| fpr_f2_mips64, |
| fpr_f3_mips64, |
| fpr_f4_mips64, |
| fpr_f5_mips64, |
| fpr_f6_mips64, |
| fpr_f7_mips64, |
| fpr_f8_mips64, |
| fpr_f9_mips64, |
| fpr_f10_mips64, |
| fpr_f11_mips64, |
| fpr_f12_mips64, |
| fpr_f13_mips64, |
| fpr_f14_mips64, |
| fpr_f15_mips64, |
| fpr_f16_mips64, |
| fpr_f17_mips64, |
| fpr_f18_mips64, |
| fpr_f19_mips64, |
| fpr_f20_mips64, |
| fpr_f21_mips64, |
| fpr_f22_mips64, |
| fpr_f23_mips64, |
| fpr_f24_mips64, |
| fpr_f25_mips64, |
| fpr_f26_mips64, |
| fpr_f27_mips64, |
| fpr_f28_mips64, |
| fpr_f29_mips64, |
| fpr_f30_mips64, |
| fpr_f31_mips64, |
| fpr_fcsr_mips64, |
| fpr_fir_mips64, |
| fpr_config5_mips64, |
| LLDB_INVALID_REGNUM // register sets need to end with this flag |
| }; |
| |
| static_assert((sizeof(g_fp_regnums_mips64) / sizeof(g_fp_regnums_mips64[0])) - 1 == k_num_fpr_registers_mips64, |
| "g_fp_regnums_mips64 has wrong number of register infos"); |
| |
| // mips64 MSA registers. |
| const uint32_t |
| g_msa_regnums_mips64[] = |
| { |
| msa_w0_mips64, |
| msa_w1_mips64, |
| msa_w2_mips64, |
| msa_w3_mips64, |
| msa_w4_mips64, |
| msa_w5_mips64, |
| msa_w6_mips64, |
| msa_w7_mips64, |
| msa_w8_mips64, |
| msa_w9_mips64, |
| msa_w10_mips64, |
| msa_w11_mips64, |
| msa_w12_mips64, |
| msa_w13_mips64, |
| msa_w14_mips64, |
| msa_w15_mips64, |
| msa_w16_mips64, |
| msa_w17_mips64, |
| msa_w18_mips64, |
| msa_w19_mips64, |
| msa_w20_mips64, |
| msa_w21_mips64, |
| msa_w22_mips64, |
| msa_w23_mips64, |
| msa_w24_mips64, |
| msa_w25_mips64, |
| msa_w26_mips64, |
| msa_w27_mips64, |
| msa_w28_mips64, |
| msa_w29_mips64, |
| msa_w30_mips64, |
| msa_w31_mips64, |
| msa_fcsr_mips64, |
| msa_fir_mips64, |
| msa_mcsr_mips64, |
| msa_mir_mips64, |
| msa_config5_mips64, |
| LLDB_INVALID_REGNUM // register sets need to end with this flag |
| }; |
| |
| static_assert((sizeof(g_msa_regnums_mips64) / sizeof(g_msa_regnums_mips64[0])) - 1 == k_num_msa_registers_mips64, |
| "g_msa_regnums_mips64 has wrong number of register infos"); |
| |
| // Number of register sets provided by this context. |
| enum |
| { |
| k_num_register_sets = 3 |
| }; |
| |
| // Register sets for mips. |
| static const RegisterSet |
| g_reg_sets_mips[k_num_register_sets] = |
| { |
| { "General Purpose Registers", "gpr", k_num_gpr_registers_mips, g_gp_regnums_mips }, |
| { "Floating Point Registers", "fpu", k_num_fpr_registers_mips, g_fp_regnums_mips }, |
| { "MSA Registers", "msa", k_num_msa_registers_mips, g_msa_regnums_mips } |
| }; |
| |
| // Register sets for mips64. |
| static const RegisterSet |
| g_reg_sets_mips64[k_num_register_sets] = |
| { |
| { "General Purpose Registers", "gpr", k_num_gpr_registers_mips64, g_gp_regnums_mips64 }, |
| { "Floating Point Registers", "fpu", k_num_fpr_registers_mips64, g_fp_regnums_mips64 }, |
| { "MSA Registers", "msa", k_num_msa_registers_mips64, g_msa_regnums_mips64 }, |
| }; |
| |
| } // end of anonymous namespace |
| |
| NativeRegisterContextLinux* |
| NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux(const ArchSpec& target_arch, |
| NativeThreadProtocol &native_thread, |
| uint32_t concrete_frame_idx) |
| { |
| return new NativeRegisterContextLinux_mips64(target_arch, native_thread, concrete_frame_idx); |
| } |
| |
| #define REG_CONTEXT_SIZE (GetRegisterInfoInterface ().GetGPRSize () + sizeof(FPR_linux_mips) + sizeof(MSA_linux_mips)) |
| |
| // ---------------------------------------------------------------------------- |
| // NativeRegisterContextLinux_mips64 members. |
| // ---------------------------------------------------------------------------- |
| |
| static RegisterInfoInterface* |
| CreateRegisterInfoInterface(const ArchSpec& target_arch) |
| { |
| if (HostInfo::GetArchitecture().GetAddressByteSize() == 4) |
| { |
| // 32-bit hosts run with a RegisterContextLinux_mips context. |
| return new RegisterContextLinux_mips(target_arch); |
| } |
| else |
| { |
| assert((HostInfo::GetArchitecture().GetAddressByteSize() == 8) && |
| "Register setting path assumes this is a 64-bit host"); |
| // mips64 hosts know how to work with 64-bit and 32-bit EXEs using the mips64 register context. |
| return new RegisterContextLinux_mips64 (target_arch); |
| } |
| } |
| |
| NativeRegisterContextLinux_mips64::NativeRegisterContextLinux_mips64 (const ArchSpec& target_arch, |
| NativeThreadProtocol &native_thread, |
| uint32_t concrete_frame_idx) : |
| NativeRegisterContextLinux (native_thread, concrete_frame_idx, CreateRegisterInfoInterface(target_arch)) |
| { |
| switch (target_arch.GetMachine ()) |
| { |
| case llvm::Triple::mips: |
| case llvm::Triple::mipsel: |
| m_reg_info.num_registers = k_num_registers_mips; |
| m_reg_info.num_gpr_registers = k_num_gpr_registers_mips; |
| m_reg_info.num_fpr_registers = k_num_fpr_registers_mips; |
| m_reg_info.last_gpr = k_last_gpr_mips; |
| m_reg_info.first_fpr = k_first_fpr_mips; |
| m_reg_info.last_fpr = k_last_fpr_mips; |
| m_reg_info.first_msa = k_first_msa_mips; |
| m_reg_info.last_msa = k_last_msa_mips; |
| break; |
| case llvm::Triple::mips64: |
| case llvm::Triple::mips64el: |
| m_reg_info.num_registers = k_num_registers_mips64; |
| m_reg_info.num_gpr_registers = k_num_gpr_registers_mips64; |
| m_reg_info.num_fpr_registers = k_num_fpr_registers_mips64; |
| m_reg_info.last_gpr = k_last_gpr_mips64; |
| m_reg_info.first_fpr = k_first_fpr_mips64; |
| m_reg_info.last_fpr = k_last_fpr_mips64; |
| m_reg_info.first_msa = k_first_msa_mips64; |
| m_reg_info.last_msa = k_last_msa_mips64; |
| break; |
| default: |
| assert(false && "Unhandled target architecture."); |
| break; |
| } |
| |
| // Initialize m_iovec to point to the buffer and buffer size |
| // using the conventions of Berkeley style UIO structures, as required |
| // by PTRACE extensions. |
| m_iovec.iov_base = &m_msa; |
| m_iovec.iov_len = sizeof(MSA_linux_mips); |
| |
| // init h/w watchpoint addr map |
| for (int index = 0;index <= MAX_NUM_WP; index++) |
| hw_addr_map[index] = LLDB_INVALID_ADDRESS; |
| |
| ::memset(&m_gpr, 0, sizeof(GPR_linux_mips)); |
| ::memset(&m_fpr, 0, sizeof(FPR_linux_mips)); |
| ::memset(&m_msa, 0, sizeof(MSA_linux_mips)); |
| } |
| |
| uint32_t |
| NativeRegisterContextLinux_mips64::GetRegisterSetCount () const |
| { |
| return k_num_register_sets; |
| } |
| |
| lldb::addr_t |
| NativeRegisterContextLinux_mips64::GetPCfromBreakpointLocation (lldb::addr_t fail_value) |
| { |
| Error error; |
| RegisterValue pc_value; |
| lldb::addr_t pc = fail_value; |
| Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS)); |
| |
| if (log) |
| log->Printf ("NativeRegisterContextLinux_mips64::%s Reading PC from breakpoint location", __FUNCTION__); |
| |
| // PC register is at index 34 of the register array |
| const RegisterInfo *const pc_info_p = GetRegisterInfoAtIndex (34); |
| |
| error = ReadRegister (pc_info_p, pc_value); |
| if (error.Success ()) |
| { |
| pc = pc_value.GetAsUInt64 (); |
| |
| // CAUSE register is at index 37 of the register array |
| const RegisterInfo *const cause_info_p = GetRegisterInfoAtIndex (37); |
| RegisterValue cause_value; |
| |
| ReadRegister (cause_info_p, cause_value); |
| |
| uint64_t cause = cause_value.GetAsUInt64 (); |
| |
| if (log) |
| log->Printf ("NativeRegisterContextLinux_mips64::%s PC 0x%" PRIx64 " Cause 0x%" PRIx64, __FUNCTION__, pc, cause); |
| |
| /* |
| * The breakpoint might be in a delay slot. In this case PC points |
| * to the delayed branch instruction rather then the instruction |
| * in the delay slot. If the CAUSE.BD flag is set then adjust the |
| * PC based on the size of the branch instruction. |
| */ |
| if ((cause & (1 << 31)) != 0) |
| { |
| lldb::addr_t branch_delay = 0; |
| branch_delay = 4; // FIXME - Adjust according to size of branch instruction at PC |
| pc = pc + branch_delay; |
| pc_value.SetUInt64 (pc); |
| WriteRegister (pc_info_p, pc_value); |
| |
| if (log) |
| log->Printf ("NativeRegisterContextLinux_mips64::%s New PC 0x%" PRIx64, __FUNCTION__, pc); |
| } |
| } |
| |
| return pc; |
| } |
| |
| const RegisterSet * |
| NativeRegisterContextLinux_mips64::GetRegisterSet (uint32_t set_index) const |
| { |
| if (set_index >= k_num_register_sets) |
| return nullptr; |
| |
| switch (GetRegisterInfoInterface ().GetTargetArchitecture ().GetMachine ()) |
| { |
| case llvm::Triple::mips64: |
| case llvm::Triple::mips64el: |
| return &g_reg_sets_mips64[set_index]; |
| case llvm::Triple::mips: |
| case llvm::Triple::mipsel: |
| return &g_reg_sets_mips[set_index]; |
| default: |
| assert (false && "Unhandled target architecture."); |
| return nullptr; |
| } |
| |
| return nullptr; |
| } |
| |
| lldb_private::Error |
| NativeRegisterContextLinux_mips64::ReadRegister (const RegisterInfo *reg_info, RegisterValue ®_value) |
| { |
| Error error; |
| |
| if (!reg_info) |
| { |
| error.SetErrorString ("reg_info NULL"); |
| return error; |
| } |
| |
| const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; |
| if (reg == LLDB_INVALID_REGNUM) |
| { |
| // This is likely an internal register for lldb use only and should not be directly queried. |
| error.SetErrorStringWithFormat ("register \"%s\" is an internal-only lldb register, cannot read directly", reg_info->name); |
| return error; |
| } |
| |
| if (IsMSA(reg) && !IsMSAAvailable()) |
| { |
| error.SetErrorString ("MSA not available on this processor"); |
| return error; |
| } |
| |
| if (IsMSA(reg) || IsFPR(reg)) |
| { |
| uint8_t *src; |
| |
| error = ReadCP1(); |
| |
| if (!error.Success()) |
| { |
| error.SetErrorString ("failed to read co-processor 1 register"); |
| return error; |
| } |
| |
| if (IsFPR(reg)) |
| { |
| assert (reg_info->byte_offset < sizeof(UserArea)); |
| src = (uint8_t *)&m_fpr + reg_info->byte_offset - (sizeof(m_gpr)); |
| } |
| else |
| { |
| assert (reg_info->byte_offset < sizeof(UserArea)); |
| src = (uint8_t *)&m_msa + reg_info->byte_offset - (sizeof(m_gpr) + sizeof(m_fpr)); |
| } |
| switch (reg_info->byte_size) |
| { |
| case 4: |
| reg_value.SetUInt32(*(uint32_t *)src); |
| break; |
| case 8: |
| reg_value.SetUInt64(*(uint64_t *)src); |
| break; |
| case 16: |
| reg_value.SetBytes((const void *)src, 16, GetByteOrder()); |
| break; |
| default: |
| assert(false && "Unhandled data size."); |
| error.SetErrorStringWithFormat ("unhandled byte size: %" PRIu32, reg_info->byte_size); |
| break; |
| } |
| } |
| else |
| { |
| error = ReadRegisterRaw(reg, reg_value); |
| if (error.Success()) |
| { |
| // If our return byte size was greater than the return value reg size, then |
| // use the type specified by reg_info rather than the uint64_t default |
| if (reg_value.GetByteSize() > reg_info->byte_size) |
| reg_value.SetType(reg_info); |
| } |
| } |
| |
| return error; |
| } |
| |
| lldb_private::Error |
| NativeRegisterContextLinux_mips64::WriteRegister (const RegisterInfo *reg_info, const RegisterValue ®_value) |
| { |
| Error error; |
| |
| assert (reg_info && "reg_info is null"); |
| |
| const uint32_t reg_index = reg_info->kinds[lldb::eRegisterKindLLDB]; |
| |
| if (reg_index == LLDB_INVALID_REGNUM) |
| return Error ("no lldb regnum for %s", reg_info && reg_info->name ? reg_info->name : "<unknown register>"); |
| |
| if (IsMSA(reg_index) && !IsMSAAvailable()) |
| { |
| error.SetErrorString ("MSA not available on this processor"); |
| return error; |
| } |
| |
| if (IsFPR(reg_index) || IsMSA(reg_index)) |
| { |
| uint8_t *dst; |
| uint64_t *src; |
| |
| // Initialise the FP and MSA buffers by reading all co-processor 1 registers |
| ReadCP1(); |
| |
| if (IsFPR(reg_index)) |
| { |
| assert (reg_info->byte_offset < sizeof(UserArea)); |
| dst = (uint8_t *)&m_fpr + reg_info->byte_offset - (sizeof(m_gpr)); |
| } |
| else |
| { |
| assert (reg_info->byte_offset < sizeof(UserArea)); |
| dst = (uint8_t *)&m_msa + reg_info->byte_offset - (sizeof(m_gpr) + sizeof(m_fpr)); |
| } |
| switch (reg_info->byte_size) |
| { |
| case 4: |
| *(uint32_t *)dst = reg_value.GetAsUInt32(); |
| break; |
| case 8: |
| *(uint64_t *)dst = reg_value.GetAsUInt64(); |
| break; |
| case 16: |
| src = (uint64_t *)reg_value.GetBytes(); |
| *(uint64_t *)dst = *src; |
| *(uint64_t *)(dst + 8) = *(src + 1); |
| break; |
| default: |
| assert(false && "Unhandled data size."); |
| error.SetErrorStringWithFormat ("unhandled byte size: %" PRIu32, reg_info->byte_size); |
| break; |
| } |
| error = WriteCP1(); |
| if (!error.Success()) |
| { |
| error.SetErrorString ("failed to write co-processor 1 register"); |
| return error; |
| } |
| } |
| else |
| { |
| error = WriteRegisterRaw(reg_index, reg_value); |
| } |
| |
| return error; |
| } |
| |
| Error |
| NativeRegisterContextLinux_mips64::ReadAllRegisterValues (lldb::DataBufferSP &data_sp) |
| { |
| Error error; |
| |
| data_sp.reset (new DataBufferHeap (REG_CONTEXT_SIZE, 0)); |
| if (!data_sp) |
| { |
| error.SetErrorStringWithFormat ("failed to allocate DataBufferHeap instance of size %" PRIu64, REG_CONTEXT_SIZE); |
| return error; |
| } |
| |
| error = ReadGPR(); |
| if (!error.Success()) |
| { |
| error.SetErrorString ("ReadGPR() failed"); |
| return error; |
| } |
| |
| error = ReadCP1(); |
| if (!error.Success()) |
| { |
| error.SetErrorString ("ReadCP1() failed"); |
| return error; |
| } |
| |
| uint8_t *dst = data_sp->GetBytes (); |
| if (dst == nullptr) |
| { |
| error.SetErrorStringWithFormat ("DataBufferHeap instance of size %" PRIu64 " returned a null pointer", REG_CONTEXT_SIZE); |
| return error; |
| } |
| |
| ::memcpy (dst, &m_gpr, GetRegisterInfoInterface ().GetGPRSize ()); |
| dst += GetRegisterInfoInterface ().GetGPRSize (); |
| |
| ::memcpy (dst, &m_fpr, GetFPRSize ()); |
| dst += GetFPRSize (); |
| |
| ::memcpy (dst, &m_msa, sizeof(MSA_linux_mips)); |
| |
| return error; |
| } |
| |
| Error |
| NativeRegisterContextLinux_mips64::WriteAllRegisterValues (const lldb::DataBufferSP &data_sp) |
| { |
| Error error; |
| |
| if (!data_sp) |
| { |
| error.SetErrorStringWithFormat ("NativeRegisterContextLinux_mips64::%s invalid data_sp provided", __FUNCTION__); |
| return error; |
| } |
| |
| if (data_sp->GetByteSize () != REG_CONTEXT_SIZE) |
| { |
| error.SetErrorStringWithFormat ("NativeRegisterContextLinux_mips64::%s data_sp contained mismatched data size, expected %" PRIu64 ", actual %" PRIu64, __FUNCTION__, REG_CONTEXT_SIZE, data_sp->GetByteSize ()); |
| return error; |
| } |
| |
| |
| uint8_t *src = data_sp->GetBytes (); |
| if (src == nullptr) |
| { |
| error.SetErrorStringWithFormat ("NativeRegisterContextLinux_mips64::%s DataBuffer::GetBytes() returned a null pointer", __FUNCTION__); |
| return error; |
| } |
| |
| ::memcpy (&m_gpr, src, GetRegisterInfoInterface ().GetGPRSize ()); |
| src += GetRegisterInfoInterface ().GetGPRSize (); |
| |
| ::memcpy (&m_fpr, src, GetFPRSize ()); |
| src += GetFPRSize (); |
| |
| ::memcpy (&m_msa, src, sizeof(MSA_linux_mips)); |
| |
| error = WriteGPR(); |
| if (!error.Success()) |
| { |
| error.SetErrorStringWithFormat ("NativeRegisterContextLinux_mips64::%s WriteGPR() failed", __FUNCTION__); |
| return error; |
| } |
| |
| error = WriteCP1(); |
| if (!error.Success()) |
| { |
| error.SetErrorStringWithFormat ("NativeRegisterContextLinux_mips64::%s WriteCP1() failed", __FUNCTION__); |
| return error; |
| } |
| |
| return error; |
| } |
| |
| Error |
| NativeRegisterContextLinux_mips64::ReadCP1() |
| { |
| Error error; |
| |
| uint8_t *src, *dst; |
| |
| lldb::ByteOrder byte_order = GetByteOrder(); |
| |
| uint32_t IsBigEndian = (byte_order == lldb::eByteOrderBig); |
| |
| if (IsMSAAvailable()) |
| { |
| error = NativeRegisterContextLinux::ReadRegisterSet(&m_iovec, sizeof(MSA_linux_mips), NT_MIPS_MSA); |
| src = (uint8_t *)&m_msa + (IsBigEndian * 8); |
| dst = (uint8_t *)&m_fpr; |
| for ( int i = 0; i < NUM_REGISTERS; i++) |
| { |
| // Copy fp values from msa buffer fetched via ptrace |
| *(uint64_t *) dst = *(uint64_t *) src; |
| src = src + 16; |
| dst = dst + 8; |
| } |
| m_fpr.fir = m_msa.fir; |
| m_fpr.fcsr = m_msa.fcsr; |
| m_fpr.config5 = m_msa.config5; |
| } |
| else |
| { |
| error = NativeRegisterContextLinux::ReadFPR(); |
| } |
| |
| if (IsFR0() || IsFRE()) |
| { |
| src = (uint8_t *)&m_fpr + 4 + (IsBigEndian * 4); |
| dst = (uint8_t *)&m_fpr + 8 + (IsBigEndian * 4); |
| for (int i = 0; i < (NUM_REGISTERS / 2); i++) |
| { |
| // copy odd single from top of neighbouring even double |
| *(uint32_t *) dst = *(uint32_t *) src; |
| src = src + 16; |
| dst = dst + 16; |
| } |
| } |
| |
| return error; |
| } |
| |
| Error |
| NativeRegisterContextLinux_mips64::WriteCP1() |
| { |
| Error error; |
| |
| uint8_t *src, *dst; |
| |
| lldb::ByteOrder byte_order = GetByteOrder(); |
| |
| uint32_t IsBigEndian = (byte_order == lldb::eByteOrderBig); |
| |
| if (IsFR0() || IsFRE()) |
| { |
| src = (uint8_t *)&m_fpr + 8 + (IsBigEndian * 4); |
| dst = (uint8_t *)&m_fpr + 4 + (IsBigEndian * 4); |
| for (int i = 0; i < (NUM_REGISTERS / 2); i++) |
| { |
| // copy odd single to top of neighbouring even double |
| *(uint32_t *) dst = *(uint32_t *) src; |
| src = src + 16; |
| dst = dst + 16; |
| } |
| } |
| |
| if (IsMSAAvailable()) |
| { |
| dst = (uint8_t *)&m_msa + (IsBigEndian * 8); |
| src = (uint8_t *)&m_fpr; |
| for (int i = 0; i < NUM_REGISTERS; i++) |
| { |
| // Copy fp values to msa buffer for ptrace |
| *(uint64_t *) dst = *(uint64_t *) src; |
| dst = dst + 16; |
| src = src + 8; |
| } |
| m_msa.fir = m_fpr.fir; |
| m_msa.fcsr = m_fpr.fcsr; |
| m_msa.config5 = m_fpr.config5; |
| error = NativeRegisterContextLinux::WriteRegisterSet(&m_iovec, sizeof(MSA_linux_mips), NT_MIPS_MSA); |
| } |
| else |
| { |
| error = NativeRegisterContextLinux::WriteFPR(); |
| } |
| |
| return error; |
| } |
| |
| bool |
| NativeRegisterContextLinux_mips64::IsFR0() |
| { |
| const RegisterInfo *const reg_info_p = GetRegisterInfoAtIndex (gpr_sr_mips64); |
| |
| RegisterValue reg_value; |
| ReadRegister (reg_info_p, reg_value); |
| |
| uint64_t value = reg_value.GetAsUInt64(); |
| |
| return (!(value & SR_FR)); |
| } |
| |
| bool |
| NativeRegisterContextLinux_mips64::IsFRE() |
| { |
| const RegisterInfo *const reg_info_p = GetRegisterInfoAtIndex (gpr_config5_mips64); |
| |
| RegisterValue reg_value; |
| ReadRegister (reg_info_p, reg_value); |
| |
| uint64_t config5 = reg_value.GetAsUInt64(); |
| |
| return (config5 & CONFIG5_FRE); |
| } |
| |
| bool |
| NativeRegisterContextLinux_mips64::IsFPR(uint32_t reg_index) const |
| { |
| return (m_reg_info.first_fpr <= reg_index && reg_index <= m_reg_info.last_fpr); |
| } |
| |
| static uint32_t |
| GetWatchHi (struct pt_watch_regs *regs, uint32_t index) |
| { |
| Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); |
| if (regs->style == pt_watch_style_mips32) |
| return regs->mips32.watchhi[index]; |
| else if (regs->style == pt_watch_style_mips64) |
| return regs->mips64.watchhi[index]; |
| else |
| log->Printf("Invalid watch register style"); |
| return 0; |
| } |
| |
| static void |
| SetWatchHi (struct pt_watch_regs *regs, uint32_t index, uint16_t value) |
| { |
| Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); |
| if (regs->style == pt_watch_style_mips32) |
| regs->mips32.watchhi[index] = value; |
| else if (regs->style == pt_watch_style_mips64) |
| regs->mips64.watchhi[index] = value; |
| else |
| log->Printf("Invalid watch register style"); |
| return; |
| } |
| |
| static lldb::addr_t |
| GetWatchLo (struct pt_watch_regs *regs, uint32_t index) |
| { |
| Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); |
| if (regs->style == pt_watch_style_mips32) |
| return regs->mips32.watchlo[index]; |
| else if (regs->style == pt_watch_style_mips64) |
| return regs->mips64.watchlo[index]; |
| else |
| log->Printf("Invalid watch register style"); |
| return LLDB_INVALID_ADDRESS; |
| } |
| |
| static void |
| SetWatchLo (struct pt_watch_regs *regs, uint32_t index, uint64_t value) |
| { |
| Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); |
| if (regs->style == pt_watch_style_mips32) |
| regs->mips32.watchlo[index] = (uint32_t) value; |
| else if (regs->style == pt_watch_style_mips64) |
| regs->mips64.watchlo[index] = value; |
| else |
| log->Printf("Invalid watch register style"); |
| return; |
| } |
| |
| static uint32_t |
| GetIRWMask (struct pt_watch_regs *regs, uint32_t index) |
| { |
| Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); |
| if (regs->style == pt_watch_style_mips32) |
| return regs->mips32.watch_masks[index] & IRW; |
| else if (regs->style == pt_watch_style_mips64) |
| return regs->mips64.watch_masks[index] & IRW; |
| else |
| log->Printf("Invalid watch register style"); |
| return 0; |
| } |
| |
| static uint32_t |
| GetRegMask (struct pt_watch_regs *regs, uint32_t index) |
| { |
| Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); |
| if (regs->style == pt_watch_style_mips32) |
| return regs->mips32.watch_masks[index] & ~IRW; |
| else if (regs->style == pt_watch_style_mips64) |
| return regs->mips64.watch_masks[index] & ~IRW; |
| else |
| log->Printf("Invalid watch register style"); |
| return 0; |
| } |
| |
| static lldb::addr_t |
| GetRangeMask (lldb::addr_t mask) |
| { |
| lldb::addr_t mask_bit = 1; |
| while (mask_bit < mask) |
| { |
| mask = mask | mask_bit; |
| mask_bit <<= 1; |
| } |
| return mask; |
| } |
| |
| static int |
| GetVacantWatchIndex (struct pt_watch_regs *regs, lldb::addr_t addr, uint32_t size, uint32_t irw, uint32_t num_valid) |
| { |
| lldb::addr_t last_byte = addr + size - 1; |
| lldb::addr_t mask = GetRangeMask (addr ^ last_byte) | IRW; |
| lldb::addr_t base_addr = addr & ~mask; |
| |
| // Check if this address is already watched by previous watch points. |
| lldb::addr_t lo; |
| uint16_t hi; |
| uint32_t vacant_watches = 0; |
| for (uint32_t index = 0; index < num_valid; index++) |
| { |
| lo = GetWatchLo (regs, index); |
| if (lo != 0 && irw == ((uint32_t) lo & irw)) |
| { |
| hi = GetWatchHi (regs, index) | IRW; |
| lo &= ~(lldb::addr_t) hi; |
| if (addr >= lo && last_byte <= (lo + hi)) |
| return index; |
| } |
| else |
| vacant_watches++; |
| } |
| |
| // Now try to find a vacant index |
| if(vacant_watches > 0) |
| { |
| vacant_watches = 0; |
| for (uint32_t index = 0; index < num_valid; index++) |
| { |
| lo = GetWatchLo (regs, index); |
| if (lo == 0 |
| && irw == (GetIRWMask (regs, index) & irw)) |
| { |
| if (mask <= (GetRegMask (regs, index) | IRW)) |
| { |
| // It fits, we can use it. |
| SetWatchLo (regs, index, base_addr | irw); |
| SetWatchHi (regs, index, mask & ~IRW); |
| return index; |
| } |
| else |
| { |
| // It doesn't fit, but has the proper IRW capabilities |
| vacant_watches++; |
| } |
| } |
| } |
| |
| if (vacant_watches > 1) |
| { |
| // Split this watchpoint accross several registers |
| struct pt_watch_regs regs_copy; |
| regs_copy = *regs; |
| lldb::addr_t break_addr; |
| uint32_t segment_size; |
| for (uint32_t index = 0; index < num_valid; index++) |
| { |
| lo = GetWatchLo (®s_copy, index); |
| hi = GetRegMask (®s_copy, index) | IRW; |
| if (lo == 0 && irw == (hi & irw)) |
| { |
| lo = addr & ~(lldb::addr_t) hi; |
| break_addr = lo + hi + 1; |
| if (break_addr >= addr + size) |
| segment_size = size; |
| else |
| segment_size = break_addr - addr; |
| mask = GetRangeMask (addr ^ (addr + segment_size - 1)); |
| SetWatchLo (®s_copy, index, (addr & ~mask) | irw); |
| SetWatchHi (®s_copy, index, mask & ~IRW); |
| if (break_addr >= addr + size) |
| { |
| *regs = regs_copy; |
| return index; |
| } |
| size = addr + size - break_addr; |
| addr = break_addr; |
| } |
| } |
| } |
| } |
| return 0; |
| } |
| |
| bool |
| NativeRegisterContextLinux_mips64::IsMSA(uint32_t reg_index) const |
| { |
| return (m_reg_info.first_msa <= reg_index && reg_index <= m_reg_info.last_msa); |
| } |
| |
| bool |
| NativeRegisterContextLinux_mips64::IsMSAAvailable() |
| { |
| Error error = NativeRegisterContextLinux::ReadRegisterSet(&m_msa, sizeof(MSA_linux_mips), NT_MIPS_MSA); |
| |
| if (error.Success() && m_msa.mir) |
| { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| Error |
| NativeRegisterContextLinux_mips64::IsWatchpointHit (uint32_t wp_index, bool &is_hit) |
| { |
| if (wp_index >= NumSupportedHardwareWatchpoints()) |
| return Error("Watchpoint index out of range"); |
| |
| // reading the current state of watch regs |
| struct pt_watch_regs watch_readback; |
| NativeProcessProtocolSP process_sp (m_thread.GetProcess ()); |
| NativeProcessLinux *const process_p = static_cast<NativeProcessLinux*> (process_sp.get ()); |
| Error error = process_p->DoOperation([&] { |
| return DoReadWatchPointRegisterValue(m_thread.GetID(), static_cast<void *>(&watch_readback)); |
| }); |
| |
| if (GetWatchHi (&watch_readback, wp_index) & (IRW)) |
| { |
| // clear hit flag in watchhi |
| SetWatchHi (&watch_readback, wp_index, (GetWatchHi (&watch_readback, wp_index) & ~(IRW))); |
| process_p->DoOperation([&] { |
| return DoWriteWatchPointRegisterValue(m_thread.GetID(), static_cast<void *>(&watch_readback)); |
| }); |
| |
| is_hit = true; |
| return error; |
| } |
| is_hit = false; |
| return error; |
| } |
| |
| Error |
| NativeRegisterContextLinux_mips64::GetWatchpointHitIndex(uint32_t &wp_index, lldb::addr_t trap_addr) { |
| uint32_t num_hw_wps = NumSupportedHardwareWatchpoints(); |
| for (wp_index = 0; wp_index < num_hw_wps; ++wp_index) |
| { |
| bool is_hit; |
| Error error = IsWatchpointHit(wp_index, is_hit); |
| if (error.Fail()) { |
| wp_index = LLDB_INVALID_INDEX32; |
| } else if (is_hit) { |
| return error; |
| } |
| } |
| wp_index = LLDB_INVALID_INDEX32; |
| return Error(); |
| } |
| |
| Error |
| NativeRegisterContextLinux_mips64::IsWatchpointVacant (uint32_t wp_index, bool &is_vacant) |
| { |
| is_vacant = false; |
| return Error("MIPS TODO: NativeRegisterContextLinux_mips64::IsWatchpointVacant not implemented"); |
| } |
| |
| bool |
| NativeRegisterContextLinux_mips64::ClearHardwareWatchpoint(uint32_t wp_index) |
| { |
| if (wp_index >= NumSupportedHardwareWatchpoints()) |
| return false; |
| |
| struct pt_watch_regs regs; |
| // First reading the current state of watch regs |
| NativeProcessProtocolSP process_sp (m_thread.GetProcess ()); |
| NativeProcessLinux *const process_p = static_cast<NativeProcessLinux*> (process_sp.get ()); |
| process_p->DoOperation([&] { |
| return DoReadWatchPointRegisterValue(m_thread.GetID(), static_cast<void*>(®s)); |
| }); |
| |
| if (regs.style == pt_watch_style_mips32) |
| { |
| regs.mips32.watchlo[wp_index] = default_watch_regs.mips32.watchlo[wp_index]; |
| regs.mips32.watchhi[wp_index] = default_watch_regs.mips32.watchhi[wp_index]; |
| regs.mips32.watch_masks[wp_index] = default_watch_regs.mips32.watch_masks[wp_index]; |
| } |
| else // pt_watch_style_mips64 |
| { |
| regs.mips64.watchlo[wp_index] = default_watch_regs.mips64.watchlo[wp_index]; |
| regs.mips64.watchhi[wp_index] = default_watch_regs.mips64.watchhi[wp_index]; |
| regs.mips64.watch_masks[wp_index] = default_watch_regs.mips64.watch_masks[wp_index]; |
| } |
| |
| Error error = process_p->DoOperation([&] { |
| return DoWriteWatchPointRegisterValue(m_thread.GetID(), static_cast<void *>(®s)); |
| }); |
| if(!error.Fail()) |
| { |
| hw_addr_map[wp_index] = LLDB_INVALID_ADDRESS; |
| return true; |
| } |
| return false; |
| } |
| |
| Error |
| NativeRegisterContextLinux_mips64::ClearAllHardwareWatchpoints() |
| { |
| NativeProcessProtocolSP process_sp (m_thread.GetProcess ()); |
| NativeProcessLinux *const process_p = static_cast<NativeProcessLinux *> (process_sp.get ()); |
| return process_p->DoOperation([&] { |
| return DoWriteWatchPointRegisterValue(m_thread.GetID(), static_cast<void *>(&default_watch_regs)); |
| }); |
| } |
| |
| Error |
| NativeRegisterContextLinux_mips64::SetHardwareWatchpointWithIndex ( |
| lldb::addr_t addr, size_t size, uint32_t watch_flags, uint32_t wp_index) |
| { |
| Error error; |
| error.SetErrorString ("MIPS TODO: NativeRegisterContextLinux_mips64::SetHardwareWatchpointWithIndex not implemented"); |
| return error; |
| } |
| |
| uint32_t |
| NativeRegisterContextLinux_mips64::SetHardwareWatchpoint ( |
| lldb::addr_t addr, size_t size, uint32_t watch_flags) |
| { |
| struct pt_watch_regs regs; |
| |
| // First reading the current state of watch regs |
| NativeProcessProtocolSP process_sp (m_thread.GetProcess ()); |
| NativeProcessLinux *const process_p = static_cast<NativeProcessLinux*> (process_sp.get ()); |
| process_p->DoOperation([&] { |
| return DoReadWatchPointRegisterValue(m_thread.GetID(), static_cast<void *>(®s)); |
| }); |
| |
| // Try if a new watch point fits in this state |
| int index = GetVacantWatchIndex (®s, addr, size, watch_flags, NumSupportedHardwareWatchpoints()); |
| |
| // New watchpoint doesn't fit |
| if (!index) |
| return LLDB_INVALID_INDEX32; |
| |
| |
| // It fits, so we go ahead with updating the state of watch regs |
| process_p->DoOperation([&] { |
| return DoWriteWatchPointRegisterValue(m_thread.GetID(), static_cast<void *>(®s)); |
| }); |
| |
| // Storing exact address |
| hw_addr_map[index] = addr; |
| return index; |
| } |
| |
| lldb::addr_t |
| NativeRegisterContextLinux_mips64::GetWatchpointAddress (uint32_t wp_index) |
| { |
| if (wp_index >= NumSupportedHardwareWatchpoints()) |
| return LLDB_INVALID_ADDRESS; |
| |
| return hw_addr_map[wp_index]; |
| } |
| |
| struct EmulatorBaton |
| { |
| lldb::addr_t m_watch_hit_addr; |
| NativeProcessLinux* m_process; |
| NativeRegisterContext* m_reg_context; |
| |
| EmulatorBaton(NativeProcessLinux* process, NativeRegisterContext* reg_context) : |
| m_watch_hit_addr(LLDB_INVALID_ADDRESS), |
| m_process(process), |
| m_reg_context(reg_context) |
| {} |
| }; |
| |
| static size_t |
| ReadMemoryCallback (EmulateInstruction *instruction, void *baton, |
| const EmulateInstruction::Context &context, lldb::addr_t addr, |
| void *dst, size_t length) |
| { |
| size_t bytes_read; |
| EmulatorBaton* emulator_baton = static_cast<EmulatorBaton*>(baton); |
| emulator_baton->m_process->ReadMemory(addr, dst, length, bytes_read); |
| return bytes_read; |
| } |
| |
| static size_t |
| WriteMemoryCallback (EmulateInstruction *instruction, void *baton, |
| const EmulateInstruction::Context &context, |
| lldb::addr_t addr, const void *dst, size_t length) |
| { |
| return length; |
| } |
| |
| static bool |
| ReadRegisterCallback (EmulateInstruction *instruction, void *baton, |
| const RegisterInfo *reg_info, RegisterValue ®_value) |
| { |
| EmulatorBaton* emulator_baton = static_cast<EmulatorBaton*>(baton); |
| |
| const RegisterInfo* full_reg_info = emulator_baton->m_reg_context->GetRegisterInfo( |
| lldb::eRegisterKindDWARF, reg_info->kinds[lldb::eRegisterKindDWARF]); |
| |
| Error error = emulator_baton->m_reg_context->ReadRegister(full_reg_info, reg_value); |
| if (error.Success()) |
| return true; |
| |
| return false; |
| } |
| |
| static bool |
| WriteRegisterCallback (EmulateInstruction *instruction, void *baton, |
| const EmulateInstruction::Context &context, |
| const RegisterInfo *reg_info, const RegisterValue ®_value) |
| { |
| if (reg_info->kinds[lldb::eRegisterKindDWARF] == gcc_dwarf_bad_mips64) |
| { |
| EmulatorBaton* emulator_baton = static_cast<EmulatorBaton*>(baton); |
| emulator_baton->m_watch_hit_addr = reg_value.GetAsUInt64 (); |
| } |
| |
| return true; |
| } |
| |
| /* |
| * MIPS Linux kernel returns a masked address (last 3bits are masked) |
| * when a HW watchpoint is hit. However user may not have set a watchpoint |
| * on this address. Emulate instruction at PC and find the base address of |
| * the load/store instruction. This will give the exact address used to |
| * read/write the variable. Send this exact address to client so that |
| * it can decide to stop or continue the thread. |
| */ |
| lldb::addr_t |
| NativeRegisterContextLinux_mips64::GetWatchpointHitAddress (uint32_t wp_index) |
| { |
| if (wp_index >= NumSupportedHardwareWatchpoints()) |
| return LLDB_INVALID_ADDRESS; |
| |
| lldb_private::ArchSpec arch; |
| arch = GetRegisterInfoInterface().GetTargetArchitecture(); |
| std::unique_ptr<EmulateInstruction> emulator_ap( |
| EmulateInstruction::FindPlugin(arch, lldb_private::eInstructionTypeAny, nullptr)); |
| |
| if (emulator_ap == nullptr) |
| return LLDB_INVALID_ADDRESS; |
| |
| EmulatorBaton baton(static_cast<NativeProcessLinux*>(m_thread.GetProcess().get()), this); |
| emulator_ap->SetBaton (&baton); |
| emulator_ap->SetReadMemCallback (&ReadMemoryCallback); |
| emulator_ap->SetReadRegCallback (&ReadRegisterCallback); |
| emulator_ap->SetWriteMemCallback (&WriteMemoryCallback); |
| emulator_ap->SetWriteRegCallback (&WriteRegisterCallback); |
| |
| if (!emulator_ap->ReadInstruction()) |
| return LLDB_INVALID_ADDRESS; |
| |
| if (emulator_ap->EvaluateInstruction(lldb::eEmulateInstructionOptionNone)) |
| return baton.m_watch_hit_addr; |
| |
| return LLDB_INVALID_ADDRESS; |
| } |
| |
| uint32_t |
| NativeRegisterContextLinux_mips64::NumSupportedHardwareWatchpoints () |
| { |
| Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); |
| struct pt_watch_regs regs; |
| static int num_valid = 0; |
| if (!num_valid) |
| { |
| NativeProcessProtocolSP process_sp (m_thread.GetProcess ()); |
| if (!process_sp) |
| { |
| printf ("NativeProcessProtocol is NULL"); |
| return 0; |
| } |
| |
| NativeProcessLinux *const process_p = static_cast<NativeProcessLinux*> (process_sp.get ()); |
| process_p->DoOperation([&] { |
| return DoReadWatchPointRegisterValue(m_thread.GetID(), static_cast<void *>(®s)); |
| }); |
| default_watch_regs = regs; // Keeping default watch regs values for future use |
| switch (regs.style) |
| { |
| case pt_watch_style_mips32: |
| num_valid = regs.mips32.num_valid; // Using num_valid as cache |
| return num_valid; |
| case pt_watch_style_mips64: |
| num_valid = regs.mips64.num_valid; |
| return num_valid; |
| default: |
| log->Printf("NativeRegisterContextLinux_mips64::%s Error: Unrecognized watch register style", __FUNCTION__); |
| } |
| return 0; |
| } |
| return num_valid; |
| } |
| Error |
| NativeRegisterContextLinux_mips64::DoReadRegisterValue(uint32_t offset, |
| const char* reg_name, |
| uint32_t size, |
| RegisterValue &value) |
| { |
| GPR_linux_mips regs; |
| ::memset(®s, 0, sizeof(GPR_linux_mips)); |
| Error error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGS, m_thread.GetID(), NULL, ®s, sizeof regs); |
| if (error.Success()) |
| { |
| lldb_private::ArchSpec arch; |
| if (m_thread.GetProcess()->GetArchitecture(arch)) |
| value.SetBytes((void *)(((unsigned char *)(®s)) + offset), 8, arch.GetByteOrder()); |
| else |
| error.SetErrorString("failed to get architecture"); |
| } |
| return error; |
| } |
| |
| Error |
| NativeRegisterContextLinux_mips64::DoWriteRegisterValue(uint32_t offset, |
| const char* reg_name, |
| const RegisterValue &value) |
| { |
| elf_gregset_t regs; |
| Error error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGS, m_thread.GetID(), NULL, ®s, sizeof regs); |
| if (error.Success()) |
| { |
| ::memcpy((void *)(((unsigned char *)(®s)) + offset), value.GetBytes(), 8); |
| error = NativeProcessLinux::PtraceWrapper(PTRACE_SETREGS, m_thread.GetID(), NULL, ®s, sizeof regs); |
| } |
| return error; |
| } |
| |
| Error |
| NativeRegisterContextLinux_mips64::DoReadWatchPointRegisterValue(lldb::tid_t tid, void* watch_readback) |
| { |
| return NativeProcessLinux::PtraceWrapper( PTRACE_GET_WATCH_REGS, m_thread.GetID(), watch_readback); |
| } |
| |
| Error |
| NativeRegisterContextLinux_mips64::DoWriteWatchPointRegisterValue(lldb::tid_t tid, void* watch_reg_value) |
| { |
| return NativeProcessLinux::PtraceWrapper(PTRACE_SET_WATCH_REGS, m_thread.GetID(), watch_reg_value); |
| } |
| |
| #endif // defined (__mips__) |