| //===-- 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 "Plugins/Process/Linux/NativeProcessLinux.h" |
| #include "lldb/Core/RegisterValue.h" |
| #include "lldb/Host/HostInfo.h" |
| #include "lldb/Utility/DataBufferHeap.h" |
| #include "lldb/Utility/Log.h" |
| #include "lldb/Utility/Status.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 */ |
| |
| std::unique_ptr<NativeRegisterContextLinux> |
| NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux( |
| const ArchSpec &target_arch, NativeThreadProtocol &native_thread) { |
| return llvm::make_unique<NativeRegisterContextLinux_s390x>(target_arch, |
| native_thread); |
| } |
| |
| // ---------------------------------------------------------------------------- |
| // 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) |
| : NativeRegisterContextLinux(native_thread, |
| 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); |
| } |
| |
| Status |
| NativeRegisterContextLinux_s390x::ReadRegister(const RegisterInfo *reg_info, |
| RegisterValue ®_value) { |
| if (!reg_info) |
| return Status("reg_info NULL"); |
| |
| const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; |
| if (reg == LLDB_INVALID_REGNUM) |
| return Status("register \"%s\" is an internal-only lldb register, cannot " |
| "read directly", |
| reg_info->name); |
| |
| if (IsGPR(reg)) { |
| s390_regs regs; |
| Status 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 Status("unhandled byte size: %" PRIu32, reg_info->byte_size); |
| } |
| return Status(); |
| } |
| |
| if (IsFPR(reg)) { |
| s390_fp_regs fp_regs; |
| Status 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 Status("unhandled byte size: %" PRIu32, reg_info->byte_size); |
| } |
| return Status(); |
| } |
| |
| if (reg == lldb_last_break_s390x) { |
| uint64_t last_break; |
| Status error = DoReadRegisterSet(NT_S390_LAST_BREAK, &last_break, 8); |
| if (error.Fail()) |
| return error; |
| |
| reg_value.SetUInt64(last_break); |
| return Status(); |
| } |
| |
| if (reg == lldb_system_call_s390x) { |
| uint32_t system_call; |
| Status error = DoReadRegisterSet(NT_S390_SYSTEM_CALL, &system_call, 4); |
| if (error.Fail()) |
| return error; |
| |
| reg_value.SetUInt32(system_call); |
| return Status(); |
| } |
| |
| return Status("failed - register wasn't recognized"); |
| } |
| |
| Status NativeRegisterContextLinux_s390x::WriteRegister( |
| const RegisterInfo *reg_info, const RegisterValue ®_value) { |
| if (!reg_info) |
| return Status("reg_info NULL"); |
| |
| const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; |
| if (reg == LLDB_INVALID_REGNUM) |
| return Status("register \"%s\" is an internal-only lldb register, cannot " |
| "write directly", |
| reg_info->name); |
| |
| if (IsGPR(reg)) { |
| s390_regs regs; |
| Status 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 Status("unhandled byte size: %" PRIu32, reg_info->byte_size); |
| } |
| return DoWriteGPR(®s, sizeof(regs)); |
| } |
| |
| if (IsFPR(reg)) { |
| s390_fp_regs fp_regs; |
| Status 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 Status("unhandled byte size: %" PRIu32, reg_info->byte_size); |
| } |
| return DoWriteFPR(&fp_regs, sizeof(fp_regs)); |
| } |
| |
| if (reg == lldb_last_break_s390x) { |
| return Status("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 Status("failed - register wasn't recognized"); |
| } |
| |
| Status NativeRegisterContextLinux_s390x::ReadAllRegisterValues( |
| lldb::DataBufferSP &data_sp) { |
| Status 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; |
| } |
| |
| Status NativeRegisterContextLinux_s390x::WriteAllRegisterValues( |
| const lldb::DataBufferSP &data_sp) { |
| Status 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; |
| } |
| |
| Status NativeRegisterContextLinux_s390x::DoReadRegisterValue( |
| uint32_t offset, const char *reg_name, uint32_t size, |
| RegisterValue &value) { |
| return Status("DoReadRegisterValue unsupported"); |
| } |
| |
| Status NativeRegisterContextLinux_s390x::DoWriteRegisterValue( |
| uint32_t offset, const char *reg_name, const RegisterValue &value) { |
| return Status("DoWriteRegisterValue unsupported"); |
| } |
| |
| Status 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); |
| } |
| |
| Status 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); |
| } |
| |
| Status 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); |
| } |
| |
| Status 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); |
| } |
| |
| Status 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); |
| } |
| |
| Status 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); |
| } |
| |
| Status 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); |
| } |
| |
| Status 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); |
| } |
| |
| Status NativeRegisterContextLinux_s390x::IsWatchpointHit(uint32_t wp_index, |
| bool &is_hit) { |
| per_lowcore_bits per_lowcore; |
| |
| if (wp_index >= NumSupportedHardwareWatchpoints()) |
| return Status("Watchpoint index out of range"); |
| |
| if (m_watchpoint_addr == LLDB_INVALID_ADDRESS) { |
| is_hit = false; |
| return Status(); |
| } |
| |
| Status 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 Status(); |
| } |
| |
| Status 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; |
| Status 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 Status(); |
| } |
| |
| Status NativeRegisterContextLinux_s390x::IsWatchpointVacant(uint32_t wp_index, |
| bool &is_vacant) { |
| if (wp_index >= NumSupportedHardwareWatchpoints()) |
| return Status("Watchpoint index out of range"); |
| |
| is_vacant = m_watchpoint_addr == LLDB_INVALID_ADDRESS; |
| |
| return Status(); |
| } |
| |
| bool NativeRegisterContextLinux_s390x::ClearHardwareWatchpoint( |
| uint32_t wp_index) { |
| per_struct per_info; |
| |
| if (wp_index >= NumSupportedHardwareWatchpoints()) |
| return false; |
| |
| Status 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; |
| } |
| |
| Status NativeRegisterContextLinux_s390x::ClearAllHardwareWatchpoints() { |
| if (ClearHardwareWatchpoint(0)) |
| return Status(); |
| return Status("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; |
| |
| Status 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__) |