| //===-- 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__) |