diff options
Diffstat (limited to 'lldb/source/Plugins/Process/Linux')
4 files changed, 869 insertions, 0 deletions
diff --git a/lldb/source/Plugins/Process/Linux/CMakeLists.txt b/lldb/source/Plugins/Process/Linux/CMakeLists.txt index bfbfb2ff707..8291fef467e 100644 --- a/lldb/source/Plugins/Process/Linux/CMakeLists.txt +++ b/lldb/source/Plugins/Process/Linux/CMakeLists.txt @@ -9,6 +9,7 @@ add_lldb_library(lldbPluginProcessLinux NativeRegisterContextLinux_arm64.cpp NativeRegisterContextLinux_x86_64.cpp NativeRegisterContextLinux_mips64.cpp + NativeRegisterContextLinux_s390x.cpp NativeThreadLinux.cpp ProcFileReader.cpp SingleStepCheck.cpp diff --git a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp index ea0972cf58c..15498eab838 100644 --- a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp +++ b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp @@ -2247,6 +2247,7 @@ NativeProcessLinux::GetSoftwareBreakpointPCOffset(uint32_t &actual_opcode_size) // FIXME put this behind a breakpoint protocol class that can be // set per architecture. Need ARM, MIPS support here. static const uint8_t g_i386_opcode [] = { 0xCC }; + static const uint8_t g_s390x_opcode[] = { 0x00, 0x01 }; switch (m_arch.GetMachine ()) { @@ -2255,6 +2256,10 @@ NativeProcessLinux::GetSoftwareBreakpointPCOffset(uint32_t &actual_opcode_size) actual_opcode_size = static_cast<uint32_t> (sizeof(g_i386_opcode)); return Error (); + case llvm::Triple::systemz: + actual_opcode_size = static_cast<uint32_t> (sizeof(g_s390x_opcode)); + return Error (); + case llvm::Triple::arm: case llvm::Triple::aarch64: case llvm::Triple::mips64: @@ -2294,6 +2299,7 @@ NativeProcessLinux::GetSoftwareBreakpointTrapOpcode (size_t trap_opcode_size_hin static const uint8_t g_i386_opcode [] = { 0xCC }; static const uint8_t g_mips64_opcode[] = { 0x00, 0x00, 0x00, 0x0d }; static const uint8_t g_mips64el_opcode[] = { 0x0d, 0x00, 0x00, 0x00 }; + static const uint8_t g_s390x_opcode[] = { 0x00, 0x01 }; static const uint8_t g_thumb_breakpoint_opcode[] = { 0x01, 0xde }; switch (m_arch.GetMachine ()) @@ -2337,6 +2343,11 @@ NativeProcessLinux::GetSoftwareBreakpointTrapOpcode (size_t trap_opcode_size_hin actual_opcode_size = sizeof(g_mips64el_opcode); return Error (); + case llvm::Triple::systemz: + trap_opcode_bytes = g_s390x_opcode; + actual_opcode_size = sizeof(g_s390x_opcode); + return Error (); + default: assert(false && "CPU type not supported!"); return Error ("CPU type not supported"); diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_s390x.cpp b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_s390x.cpp new file mode 100644 index 00000000000..b09ad400d90 --- /dev/null +++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_s390x.cpp @@ -0,0 +1,716 @@ +//===-- NativeRegisterContextLinux_s390x.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(__s390x__) && defined(__linux__) + +#include "NativeRegisterContextLinux_s390x.h" + +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Host/HostInfo.h" + +#include "Plugins/Process/Utility/RegisterContextLinux_s390x.h" + +#include <asm/ptrace.h> +#include <linux/uio.h> +#include <sys/ptrace.h> + +using namespace lldb_private; +using namespace lldb_private::process_linux; + +// ---------------------------------------------------------------------------- +// Private namespace. +// ---------------------------------------------------------------------------- + +namespace +{ + // s390x 64-bit general purpose registers. + static const uint32_t g_gpr_regnums_s390x[] = + { + lldb_r0_s390x, + lldb_r1_s390x, + lldb_r2_s390x, + lldb_r3_s390x, + lldb_r4_s390x, + lldb_r5_s390x, + lldb_r6_s390x, + lldb_r7_s390x, + lldb_r8_s390x, + lldb_r9_s390x, + lldb_r10_s390x, + lldb_r11_s390x, + lldb_r12_s390x, + lldb_r13_s390x, + lldb_r14_s390x, + lldb_r15_s390x, + lldb_acr0_s390x, + lldb_acr1_s390x, + lldb_acr2_s390x, + lldb_acr3_s390x, + lldb_acr4_s390x, + lldb_acr5_s390x, + lldb_acr6_s390x, + lldb_acr7_s390x, + lldb_acr8_s390x, + lldb_acr9_s390x, + lldb_acr10_s390x, + lldb_acr11_s390x, + lldb_acr12_s390x, + lldb_acr13_s390x, + lldb_acr14_s390x, + lldb_acr15_s390x, + lldb_pswm_s390x, + lldb_pswa_s390x, + LLDB_INVALID_REGNUM // register sets need to end with this flag + }; + static_assert((sizeof(g_gpr_regnums_s390x) / sizeof(g_gpr_regnums_s390x[0])) - 1 == k_num_gpr_registers_s390x, + "g_gpr_regnums_s390x has wrong number of register infos"); + + // s390x 64-bit floating point registers. + static const uint32_t g_fpu_regnums_s390x[] = + { + lldb_f0_s390x, + lldb_f1_s390x, + lldb_f2_s390x, + lldb_f3_s390x, + lldb_f4_s390x, + lldb_f5_s390x, + lldb_f6_s390x, + lldb_f7_s390x, + lldb_f8_s390x, + lldb_f9_s390x, + lldb_f10_s390x, + lldb_f11_s390x, + lldb_f12_s390x, + lldb_f13_s390x, + lldb_f14_s390x, + lldb_f15_s390x, + lldb_fpc_s390x, + LLDB_INVALID_REGNUM // register sets need to end with this flag + }; + static_assert((sizeof(g_fpu_regnums_s390x) / sizeof(g_fpu_regnums_s390x[0])) - 1 == k_num_fpr_registers_s390x, + "g_fpu_regnums_s390x has wrong number of register infos"); + + // s390x Linux operating-system information. + static const uint32_t g_linux_regnums_s390x[] = + { + lldb_orig_r2_s390x, + lldb_last_break_s390x, + lldb_system_call_s390x, + LLDB_INVALID_REGNUM // register sets need to end with this flag + }; + static_assert((sizeof(g_linux_regnums_s390x) / sizeof(g_linux_regnums_s390x[0])) - 1 == k_num_linux_registers_s390x, + "g_linux_regnums_s390x has wrong number of register infos"); + + // Number of register sets provided by this context. + enum + { + k_num_register_sets = 3 + }; + + // Register sets for s390x 64-bit. + static const RegisterSet g_reg_sets_s390x[k_num_register_sets] = + { + { "General Purpose Registers", "gpr", k_num_gpr_registers_s390x, g_gpr_regnums_s390x }, + { "Floating Point Registers", "fpr", k_num_fpr_registers_s390x, g_fpu_regnums_s390x }, + { "Linux Operating System Data", "linux", k_num_linux_registers_s390x, g_linux_regnums_s390x }, + }; +} + +#define REG_CONTEXT_SIZE (sizeof(s390_regs) + sizeof(s390_fp_regs) + 4) + +// ---------------------------------------------------------------------------- +// Required ptrace defines. +// ---------------------------------------------------------------------------- + +#define NT_S390_LAST_BREAK 0x306 /* s390 breaking event address */ +#define NT_S390_SYSTEM_CALL 0x307 /* s390 system call restart data */ + +NativeRegisterContextLinux * +NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux(const ArchSpec &target_arch, + NativeThreadProtocol &native_thread, + uint32_t concrete_frame_idx) +{ + return new NativeRegisterContextLinux_s390x(target_arch, native_thread, concrete_frame_idx); +} + +// ---------------------------------------------------------------------------- +// NativeRegisterContextLinux_s390x members. +// ---------------------------------------------------------------------------- + +static RegisterInfoInterface * +CreateRegisterInfoInterface(const ArchSpec &target_arch) +{ + assert((HostInfo::GetArchitecture().GetAddressByteSize() == 8) && + "Register setting path assumes this is a 64-bit host"); + return new RegisterContextLinux_s390x(target_arch); +} + +NativeRegisterContextLinux_s390x::NativeRegisterContextLinux_s390x(const ArchSpec &target_arch, + NativeThreadProtocol &native_thread, + uint32_t concrete_frame_idx) + : NativeRegisterContextLinux(native_thread, concrete_frame_idx, CreateRegisterInfoInterface(target_arch)) +{ + // Set up data about ranges of valid registers. + switch (target_arch.GetMachine()) + { + case llvm::Triple::systemz: + m_reg_info.num_registers = k_num_registers_s390x; + m_reg_info.num_gpr_registers = k_num_gpr_registers_s390x; + m_reg_info.num_fpr_registers = k_num_fpr_registers_s390x; + m_reg_info.last_gpr = k_last_gpr_s390x; + m_reg_info.first_fpr = k_first_fpr_s390x; + m_reg_info.last_fpr = k_last_fpr_s390x; + break; + default: + assert(false && "Unhandled target architecture."); + break; + } + + // Clear out the watchpoint state. + m_watchpoint_addr = LLDB_INVALID_ADDRESS; +} + +uint32_t +NativeRegisterContextLinux_s390x::GetRegisterSetCount() const +{ + uint32_t sets = 0; + for (uint32_t set_index = 0; set_index < k_num_register_sets; ++set_index) + { + if (IsRegisterSetAvailable(set_index)) + ++sets; + } + + return sets; +} + +uint32_t +NativeRegisterContextLinux_s390x::GetUserRegisterCount() const +{ + uint32_t count = 0; + for (uint32_t set_index = 0; set_index < k_num_register_sets; ++set_index) + { + const RegisterSet *set = GetRegisterSet(set_index); + if (set) + count += set->num_registers; + } + return count; +} + +const RegisterSet * +NativeRegisterContextLinux_s390x::GetRegisterSet(uint32_t set_index) const +{ + if (!IsRegisterSetAvailable(set_index)) + return nullptr; + + switch (GetRegisterInfoInterface().GetTargetArchitecture().GetMachine()) + { + case llvm::Triple::systemz: + return &g_reg_sets_s390x[set_index]; + default: + assert(false && "Unhandled target architecture."); + return nullptr; + } + + return nullptr; +} + +bool +NativeRegisterContextLinux_s390x::IsRegisterSetAvailable(uint32_t set_index) const +{ + return set_index < k_num_register_sets; +} + +bool +NativeRegisterContextLinux_s390x::IsGPR(uint32_t reg_index) const +{ + // GPRs come first. "orig_r2" counts as GPR since it is part of the GPR register area. + return reg_index <= m_reg_info.last_gpr || reg_index == lldb_orig_r2_s390x; +} + +bool +NativeRegisterContextLinux_s390x::IsFPR(uint32_t reg_index) const +{ + return (m_reg_info.first_fpr <= reg_index && reg_index <= m_reg_info.last_fpr); +} + +Error +NativeRegisterContextLinux_s390x::ReadRegister(const RegisterInfo *reg_info, RegisterValue ®_value) +{ + if (!reg_info) + return Error("reg_info NULL"); + + const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; + if (reg == LLDB_INVALID_REGNUM) + return Error("register \"%s\" is an internal-only lldb register, cannot read directly", reg_info->name); + + if (IsGPR(reg)) + { + s390_regs regs; + Error error = DoReadGPR(®s, sizeof(regs)); + if (error.Fail()) + return error; + + uint8_t *src = (uint8_t *)®s + reg_info->byte_offset; + assert(reg_info->byte_offset + reg_info->byte_size <= sizeof(regs)); + switch (reg_info->byte_size) + { + case 4: + reg_value.SetUInt32(*(uint32_t *)src); + break; + case 8: + reg_value.SetUInt64(*(uint64_t *)src); + break; + default: + assert(false && "Unhandled data size."); + return Error("unhandled byte size: %" PRIu32, reg_info->byte_size); + } + return Error(); + } + + if (IsFPR(reg)) + { + s390_fp_regs fp_regs; + Error error = DoReadFPR(&fp_regs, sizeof(fp_regs)); + if (error.Fail()) + return error; + + // byte_offset is just the offset within FPR, not the whole user area. + uint8_t *src = (uint8_t *)&fp_regs + reg_info->byte_offset; + assert(reg_info->byte_offset + reg_info->byte_size <= sizeof(fp_regs)); + switch (reg_info->byte_size) + { + case 4: + reg_value.SetUInt32(*(uint32_t *)src); + break; + case 8: + reg_value.SetUInt64(*(uint64_t *)src); + break; + default: + assert(false && "Unhandled data size."); + return Error("unhandled byte size: %" PRIu32, reg_info->byte_size); + } + return Error(); + } + + if (reg == lldb_last_break_s390x) + { + uint64_t last_break; + Error error = DoReadRegisterSet(NT_S390_LAST_BREAK, &last_break, 8); + if (error.Fail()) + return error; + + reg_value.SetUInt64(last_break); + return Error(); + } + + if (reg == lldb_system_call_s390x) + { + uint32_t system_call; + Error error = DoReadRegisterSet(NT_S390_SYSTEM_CALL, &system_call, 4); + if (error.Fail()) + return error; + + reg_value.SetUInt32(system_call); + return Error(); + } + + return Error("failed - register wasn't recognized"); +} + +Error +NativeRegisterContextLinux_s390x::WriteRegister(const RegisterInfo *reg_info, const RegisterValue ®_value) +{ + if (!reg_info) + return Error("reg_info NULL"); + + const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; + if (reg == LLDB_INVALID_REGNUM) + return Error("register \"%s\" is an internal-only lldb register, cannot write directly", reg_info->name); + + if (IsGPR(reg)) + { + s390_regs regs; + Error error = DoReadGPR(®s, sizeof(regs)); + if (error.Fail()) + return error; + + uint8_t *dst = (uint8_t *)®s + reg_info->byte_offset; + assert(reg_info->byte_offset + reg_info->byte_size <= sizeof(regs)); + switch (reg_info->byte_size) + { + case 4: + *(uint32_t *)dst = reg_value.GetAsUInt32(); + break; + case 8: + *(uint64_t *)dst = reg_value.GetAsUInt64(); + break; + default: + assert(false && "Unhandled data size."); + return Error("unhandled byte size: %" PRIu32, reg_info->byte_size); + } + return DoWriteGPR(®s, sizeof(regs)); + } + + if (IsFPR(reg)) + { + s390_fp_regs fp_regs; + Error error = DoReadFPR(&fp_regs, sizeof(fp_regs)); + if (error.Fail()) + return error; + + // byte_offset is just the offset within fp_regs, not the whole user area. + uint8_t *dst = (uint8_t *)&fp_regs + reg_info->byte_offset; + assert(reg_info->byte_offset + reg_info->byte_size <= sizeof(fp_regs)); + switch (reg_info->byte_size) + { + case 4: + *(uint32_t *)dst = reg_value.GetAsUInt32(); + break; + case 8: + *(uint64_t *)dst = reg_value.GetAsUInt64(); + break; + default: + assert(false && "Unhandled data size."); + return Error("unhandled byte size: %" PRIu32, reg_info->byte_size); + } + return DoWriteFPR(&fp_regs, sizeof(fp_regs)); + } + + if (reg == lldb_last_break_s390x) + { + return Error("The last break address is read-only"); + } + + if (reg == lldb_system_call_s390x) + { + uint32_t system_call = reg_value.GetAsUInt32(); + return DoWriteRegisterSet(NT_S390_SYSTEM_CALL, &system_call, 4); + } + + return Error("failed - register wasn't recognized"); +} + +Error +NativeRegisterContextLinux_s390x::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; + } + + 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; + } + + error = DoReadGPR(dst, sizeof(s390_regs)); + dst += sizeof(s390_regs); + if (error.Fail()) + return error; + + error = DoReadFPR(dst, sizeof(s390_fp_regs)); + dst += sizeof(s390_fp_regs); + if (error.Fail()) + return error; + + // Ignore errors if the regset is unsupported (happens on older kernels). + DoReadRegisterSet(NT_S390_SYSTEM_CALL, dst, 4); + dst += 4; + + // To enable inferior function calls while the process is stopped in + // an interrupted system call, we need to clear the system call flag. + // It will be restored to its original value by WriteAllRegisterValues. + // Again we ignore error if the regset is unsupported. + uint32_t system_call = 0; + DoWriteRegisterSet(NT_S390_SYSTEM_CALL, &system_call, 4); + + return error; +} + +Error +NativeRegisterContextLinux_s390x::WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) +{ + Error error; + + if (!data_sp) + { + error.SetErrorStringWithFormat("NativeRegisterContextLinux_s390x::%s invalid data_sp provided", __FUNCTION__); + return error; + } + + if (data_sp->GetByteSize() != REG_CONTEXT_SIZE) + { + error.SetErrorStringWithFormat( + "NativeRegisterContextLinux_s390x::%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_s390x::%s DataBuffer::GetBytes() returned a null pointer", __FUNCTION__); + return error; + } + + error = DoWriteGPR(src, sizeof(s390_regs)); + src += sizeof(s390_regs); + if (error.Fail()) + return error; + + error = DoWriteFPR(src, sizeof(s390_fp_regs)); + src += sizeof(s390_fp_regs); + if (error.Fail()) + return error; + + // Ignore errors if the regset is unsupported (happens on older kernels). + DoWriteRegisterSet(NT_S390_SYSTEM_CALL, src, 4); + src += 4; + + return error; +} + +Error +NativeRegisterContextLinux_s390x::DoReadRegisterValue(uint32_t offset, const char *reg_name, uint32_t size, + RegisterValue &value) +{ + return Error("DoReadRegisterValue unsupported"); +} + +Error +NativeRegisterContextLinux_s390x::DoWriteRegisterValue(uint32_t offset, const char *reg_name, + const RegisterValue &value) +{ + return Error("DoWriteRegisterValue unsupported"); +} + +Error +NativeRegisterContextLinux_s390x::PeekUserArea(uint32_t offset, void *buf, size_t buf_size) +{ + ptrace_area parea; + parea.len = buf_size; + parea.process_addr = (addr_t)buf; + parea.kernel_addr = offset; + + return NativeProcessLinux::PtraceWrapper(PTRACE_PEEKUSR_AREA, m_thread.GetID(), &parea); +} + +Error +NativeRegisterContextLinux_s390x::PokeUserArea(uint32_t offset, const void *buf, size_t buf_size) +{ + ptrace_area parea; + parea.len = buf_size; + parea.process_addr = (addr_t)buf; + parea.kernel_addr = offset; + + return NativeProcessLinux::PtraceWrapper(PTRACE_POKEUSR_AREA, m_thread.GetID(), &parea); +} + +Error +NativeRegisterContextLinux_s390x::DoReadGPR(void *buf, size_t buf_size) +{ + assert(buf_size == sizeof(s390_regs)); + return PeekUserArea(offsetof(user_regs_struct, psw), buf, buf_size); +} + +Error +NativeRegisterContextLinux_s390x::DoWriteGPR(void *buf, size_t buf_size) +{ + assert(buf_size == sizeof(s390_regs)); + return PokeUserArea(offsetof(user_regs_struct, psw), buf, buf_size); +} + +Error +NativeRegisterContextLinux_s390x::DoReadFPR(void *buf, size_t buf_size) +{ + assert(buf_size == sizeof(s390_fp_regs)); + return PeekUserArea(offsetof(user_regs_struct, fp_regs), buf, buf_size); +} + +Error +NativeRegisterContextLinux_s390x::DoWriteFPR(void *buf, size_t buf_size) +{ + assert(buf_size == sizeof(s390_fp_regs)); + return PokeUserArea(offsetof(user_regs_struct, fp_regs), buf, buf_size); +} + +Error +NativeRegisterContextLinux_s390x::DoReadRegisterSet(uint32_t regset, void *buf, size_t buf_size) +{ + struct iovec iov; + iov.iov_base = buf; + iov.iov_len = buf_size; + + return ReadRegisterSet(&iov, buf_size, regset); +} + +Error +NativeRegisterContextLinux_s390x::DoWriteRegisterSet(uint32_t regset, const void *buf, size_t buf_size) +{ + struct iovec iov; + iov.iov_base = const_cast<void *>(buf); + iov.iov_len = buf_size; + + return WriteRegisterSet(&iov, buf_size, regset); +} + +Error +NativeRegisterContextLinux_s390x::IsWatchpointHit(uint32_t wp_index, bool &is_hit) +{ + per_lowcore_bits per_lowcore; + + if (wp_index >= NumSupportedHardwareWatchpoints()) + return Error("Watchpoint index out of range"); + + if (m_watchpoint_addr == LLDB_INVALID_ADDRESS) + { + is_hit = false; + return Error(); + } + + Error error = PeekUserArea(offsetof(user_regs_struct, per_info.lowcore), &per_lowcore, sizeof(per_lowcore)); + if (error.Fail()) + { + is_hit = false; + return error; + } + + is_hit = (per_lowcore.perc_storage_alteration == 1 && per_lowcore.perc_store_real_address == 0); + + if (is_hit) + { + // Do not report this watchpoint again. + memset(&per_lowcore, 0, sizeof(per_lowcore)); + PokeUserArea(offsetof(user_regs_struct, per_info.lowcore), &per_lowcore, sizeof(per_lowcore)); + } + + return Error(); +} + +Error +NativeRegisterContextLinux_s390x::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; + return error; + } + else if (is_hit) + { + return error; + } + } + wp_index = LLDB_INVALID_INDEX32; + return Error(); +} + +Error +NativeRegisterContextLinux_s390x::IsWatchpointVacant(uint32_t wp_index, bool &is_vacant) +{ + if (wp_index >= NumSupportedHardwareWatchpoints()) + return Error("Watchpoint index out of range"); + + is_vacant = m_watchpoint_addr == LLDB_INVALID_ADDRESS; + + return Error(); +} + +bool +NativeRegisterContextLinux_s390x::ClearHardwareWatchpoint(uint32_t wp_index) +{ + per_struct per_info; + + if (wp_index >= NumSupportedHardwareWatchpoints()) + return false; + + Error error = PeekUserArea(offsetof(user_regs_struct, per_info), &per_info, sizeof(per_info)); + if (error.Fail()) + return false; + + per_info.control_regs.bits.em_storage_alteration = 0; + per_info.control_regs.bits.storage_alt_space_ctl = 0; + per_info.starting_addr = 0; + per_info.ending_addr = 0; + + error = PokeUserArea(offsetof(user_regs_struct, per_info), &per_info, sizeof(per_info)); + if (error.Fail()) + return false; + + m_watchpoint_addr = LLDB_INVALID_ADDRESS; + return true; +} + +Error +NativeRegisterContextLinux_s390x::ClearAllHardwareWatchpoints() +{ + if (ClearHardwareWatchpoint(0)) + return Error(); + return Error("Clearing all hardware watchpoints failed."); +} + +uint32_t +NativeRegisterContextLinux_s390x::SetHardwareWatchpoint(lldb::addr_t addr, size_t size, uint32_t watch_flags) +{ + per_struct per_info; + + if (watch_flags != 0x1) + return LLDB_INVALID_INDEX32; + + if (m_watchpoint_addr != LLDB_INVALID_ADDRESS) + return LLDB_INVALID_INDEX32; + + Error error = PeekUserArea(offsetof(user_regs_struct, per_info), &per_info, sizeof(per_info)); + if (error.Fail()) + return LLDB_INVALID_INDEX32; + + per_info.control_regs.bits.em_storage_alteration = 1; + per_info.control_regs.bits.storage_alt_space_ctl = 1; + per_info.starting_addr = addr; + per_info.ending_addr = addr + size - 1; + + error = PokeUserArea(offsetof(user_regs_struct, per_info), &per_info, sizeof(per_info)); + if (error.Fail()) + return LLDB_INVALID_INDEX32; + + m_watchpoint_addr = addr; + return 0; +} + +lldb::addr_t +NativeRegisterContextLinux_s390x::GetWatchpointAddress(uint32_t wp_index) +{ + if (wp_index >= NumSupportedHardwareWatchpoints()) + return LLDB_INVALID_ADDRESS; + return m_watchpoint_addr; +} + +uint32_t +NativeRegisterContextLinux_s390x::NumSupportedHardwareWatchpoints() +{ + return 1; +} + +#endif // defined(__s390x__) && defined(__linux__) diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_s390x.h b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_s390x.h new file mode 100644 index 00000000000..8cd4ab7f124 --- /dev/null +++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_s390x.h @@ -0,0 +1,141 @@ +//===-- NativeRegisterContextLinux_s390x.h ----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#if defined(__s390x__) && defined(__linux__) + +#ifndef lldb_NativeRegisterContextLinux_s390x_h +#define lldb_NativeRegisterContextLinux_s390x_h + +#include "Plugins/Process/Linux/NativeRegisterContextLinux.h" +#include "Plugins/Process/Utility/RegisterContext_s390x.h" +#include "Plugins/Process/Utility/lldb-s390x-register-enums.h" + +namespace lldb_private +{ +namespace process_linux +{ + +class NativeProcessLinux; + +class NativeRegisterContextLinux_s390x : public NativeRegisterContextLinux +{ +public: + NativeRegisterContextLinux_s390x(const ArchSpec &target_arch, NativeThreadProtocol &native_thread, + uint32_t concrete_frame_idx); + + uint32_t + GetRegisterSetCount() const override; + + const RegisterSet * + GetRegisterSet(uint32_t set_index) const override; + + uint32_t + GetUserRegisterCount() const override; + + Error + ReadRegister(const RegisterInfo *reg_info, RegisterValue ®_value) override; + + Error + WriteRegister(const RegisterInfo *reg_info, const RegisterValue ®_value) override; + + Error + ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override; + + Error + WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override; + + Error + IsWatchpointHit(uint32_t wp_index, bool &is_hit) override; + + Error + GetWatchpointHitIndex(uint32_t &wp_index, lldb::addr_t trap_addr) override; + + Error + IsWatchpointVacant(uint32_t wp_index, bool &is_vacant) override; + + bool + ClearHardwareWatchpoint(uint32_t wp_index) override; + + Error + ClearAllHardwareWatchpoints() override; + + uint32_t + SetHardwareWatchpoint(lldb::addr_t addr, size_t size, uint32_t watch_flags) override; + + lldb::addr_t + GetWatchpointAddress(uint32_t wp_index) override; + + uint32_t + NumSupportedHardwareWatchpoints() override; + +protected: + Error + DoReadRegisterValue(uint32_t offset, const char *reg_name, uint32_t size, RegisterValue &value) override; + + Error + DoWriteRegisterValue(uint32_t offset, const char *reg_name, const RegisterValue &value) override; + + Error + DoReadGPR(void *buf, size_t buf_size) override; + + Error + DoWriteGPR(void *buf, size_t buf_size) override; + + Error + DoReadFPR(void *buf, size_t buf_size) override; + + Error + DoWriteFPR(void *buf, size_t buf_size) override; + +private: + // Info about register ranges. + struct RegInfo + { + uint32_t num_registers; + uint32_t num_gpr_registers; + uint32_t num_fpr_registers; + + uint32_t last_gpr; + uint32_t first_fpr; + uint32_t last_fpr; + }; + + // Private member variables. + RegInfo m_reg_info; + lldb::addr_t m_watchpoint_addr; + + // Private member methods. + bool + IsRegisterSetAvailable(uint32_t set_index) const; + + bool + IsGPR(uint32_t reg_index) const; + + bool + IsFPR(uint32_t reg_index) const; + + Error + PeekUserArea(uint32_t offset, void *buf, size_t buf_size); + + Error + PokeUserArea(uint32_t offset, const void *buf, size_t buf_size); + + Error + DoReadRegisterSet(uint32_t regset, void *buf, size_t buf_size); + + Error + DoWriteRegisterSet(uint32_t regset, const void *buf, size_t buf_size); +}; + +} // namespace process_linux +} // namespace lldb_private + +#endif // #ifndef lldb_NativeRegisterContextLinux_s390x_h + +#endif // defined(__s390x__) && defined(__linux__) |