diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2016-01-06 20:12:03 +0000 |
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2016-01-06 20:12:03 +0000 |
| commit | 9e6d35490a6542f9c97607f93c2ef8ca8e03cbcc (patch) | |
| tree | dd2a1ddf0476664c2b823409c36cbccd52662ca7 /source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.cpp | |
| parent | 3bd2e91faeb9eeec1aae82c64a3253afff551cfd (diff) | |
Notes
Diffstat (limited to 'source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.cpp')
| -rw-r--r-- | source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.cpp | 1431 |
1 files changed, 1431 insertions, 0 deletions
diff --git a/source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.cpp b/source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.cpp new file mode 100644 index 000000000000..3cfeaf5546bc --- /dev/null +++ b/source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.cpp @@ -0,0 +1,1431 @@ +//===-- 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/Host/Host.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, NativeRegisterContextLinux_mips64::IsMSAAvailable()); + } + 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::IsMSAAvailable()); + } +} + +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 (gpr_pc_mips64); + + 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 (gpr_cause_mips64); + 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); + } + + 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]; + if(log) + 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; + if(log) + 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]; + if(log) + 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; + if(log) + 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; + if(log) + 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; + if(log) + 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 LLDB_INVALID_INDEX32; +} + +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() +{ + MSA_linux_mips msa_buf; + unsigned int regset = NT_MIPS_MSA; + + Error error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, Host::GetCurrentProcessID(), static_cast<void *>(®set), &msa_buf, sizeof(MSA_linux_mips)); + + if (error.Success() && msa_buf.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; + Error error = 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))); + 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 + 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 = 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() +{ + 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 + 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 == LLDB_INVALID_INDEX32) + return LLDB_INVALID_INDEX32; + + + // It fits, so we go ahead with updating the state of watch regs + 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] == 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) + { + 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: + if(log) + 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)); + + // Clear all bits in RegisterValue before writing actual value read from ptrace to avoid garbage value in 32-bit MSB + value.SetBytes((void *)(((unsigned char *)®s) + offset), 8, GetByteOrder()); + 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 + 4 * (arch.GetMachine() == llvm::Triple::mips)), arch.GetFlags() & lldb_private::ArchSpec::eMIPSABI_O32 ? 4 : 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) +{ + GPR_linux_mips regs; + 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)) + { + ::memcpy((void *)(((unsigned char *)(®s)) + offset), value.GetBytes(), arch.GetFlags() & lldb_private::ArchSpec::eMIPSABI_O32 ? 4 : 8); + error = NativeProcessLinux::PtraceWrapper(PTRACE_SETREGS, m_thread.GetID(), NULL, ®s, sizeof regs); + } + else + error.SetErrorString("failed to get architecture"); + } + 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__) |
