| //===-- NativeRegisterContextLinux_s390x.cpp ------------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #if defined(__s390x__) && defined(__linux__) | |
| #include "NativeRegisterContextLinux_s390x.h" | |
| #include "Plugins/Process/Linux/NativeProcessLinux.h" | |
| #include "Plugins/Process/Utility/RegisterContextLinux_s390x.h" | |
| #include "lldb/Host/HostInfo.h" | |
| #include "lldb/Utility/DataBufferHeap.h" | |
| #include "lldb/Utility/Log.h" | |
| #include "lldb/Utility/RegisterValue.h" | |
| #include "lldb/Utility/Status.h" | |
| #include <sys/ptrace.h> | |
| #include <sys/uio.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, NativeThreadLinux &native_thread) { | |
| return std::make_unique<NativeRegisterContextLinux_s390x>(target_arch, | |
| native_thread); | |
| } | |
| llvm::Expected<ArchSpec> | |
| NativeRegisterContextLinux::DetermineArchitecture(lldb::tid_t tid) { | |
| return HostInfo::GetArchitecture(); | |
| } | |
| // 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) | |
| : NativeRegisterContextRegisterInfo( | |
| native_thread, CreateRegisterInfoInterface(target_arch)), | |
| NativeRegisterContextLinux(native_thread) { | |
| // 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::FromErrorString("reg_info NULL"); | |
| const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; | |
| if (reg == LLDB_INVALID_REGNUM) | |
| return Status::FromErrorStringWithFormat( | |
| "register \"%s\" is an internal-only lldb register, cannot " | |
| "read directly", | |
| reg_info->name); | |
| if (IsGPR(reg)) { | |
| Status error = ReadGPR(); | |
| if (error.Fail()) | |
| return error; | |
| uint8_t *src = (uint8_t *)&m_regs + reg_info->byte_offset; | |
| assert(reg_info->byte_offset + reg_info->byte_size <= sizeof(m_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::FromErrorStringWithFormat("unhandled byte size: %" PRIu32, | |
| reg_info->byte_size); | |
| } | |
| return Status(); | |
| } | |
| if (IsFPR(reg)) { | |
| Status error = ReadFPR(); | |
| if (error.Fail()) | |
| return error; | |
| // byte_offset is just the offset within FPR, not the whole user area. | |
| uint8_t *src = (uint8_t *)&m_fp_regs + reg_info->byte_offset; | |
| assert(reg_info->byte_offset + reg_info->byte_size <= sizeof(m_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::FromErrorStringWithFormat("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::FromErrorString("failed - register wasn't recognized"); | |
| } | |
| Status NativeRegisterContextLinux_s390x::WriteRegister( | |
| const RegisterInfo *reg_info, const RegisterValue ®_value) { | |
| if (!reg_info) | |
| return Status::FromErrorString("reg_info NULL"); | |
| const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; | |
| if (reg == LLDB_INVALID_REGNUM) | |
| return Status::FromErrorStringWithFormat( | |
| "register \"%s\" is an internal-only lldb register, cannot " | |
| "write directly", | |
| reg_info->name); | |
| if (IsGPR(reg)) { | |
| Status error = ReadGPR(); | |
| if (error.Fail()) | |
| return error; | |
| uint8_t *dst = (uint8_t *)&m_regs + reg_info->byte_offset; | |
| assert(reg_info->byte_offset + reg_info->byte_size <= sizeof(m_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::FromErrorStringWithFormat("unhandled byte size: %" PRIu32, | |
| reg_info->byte_size); | |
| } | |
| return WriteGPR(); | |
| } | |
| if (IsFPR(reg)) { | |
| Status error = ReadFPR(); | |
| if (error.Fail()) | |
| return error; | |
| // byte_offset is just the offset within fp_regs, not the whole user area. | |
| uint8_t *dst = (uint8_t *)&m_fp_regs + reg_info->byte_offset; | |
| assert(reg_info->byte_offset + reg_info->byte_size <= sizeof(m_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::FromErrorStringWithFormat("unhandled byte size: %" PRIu32, | |
| reg_info->byte_size); | |
| } | |
| return WriteFPR(); | |
| } | |
| if (reg == lldb_last_break_s390x) { | |
| return Status::FromErrorString("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::FromErrorString("failed - register wasn't recognized"); | |
| } | |
| Status NativeRegisterContextLinux_s390x::ReadAllRegisterValues( | |
| lldb::WritableDataBufferSP &data_sp) { | |
| Status error; | |
| data_sp.reset(new DataBufferHeap(REG_CONTEXT_SIZE, 0)); | |
| uint8_t *dst = data_sp->GetBytes(); | |
| error = ReadGPR(); | |
| if (error.Fail()) | |
| return error; | |
| memcpy(dst, GetGPRBuffer(), GetGPRSize()); | |
| dst += GetGPRSize(); | |
| error = ReadFPR(); | |
| if (error.Fail()) | |
| return error; | |
| memcpy(dst, GetFPRBuffer(), GetFPRSize()); | |
| dst += GetFPRSize(); | |
| // 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 = Status::FromErrorStringWithFormat( | |
| "NativeRegisterContextLinux_s390x::%s invalid data_sp provided", | |
| __FUNCTION__); | |
| return error; | |
| } | |
| if (data_sp->GetByteSize() != REG_CONTEXT_SIZE) { | |
| error = Status::FromErrorStringWithFormat( | |
| "NativeRegisterContextLinux_s390x::%s data_sp contained mismatched " | |
| "data size, expected %" PRIu64 ", actual %" PRIu64, | |
| __FUNCTION__, REG_CONTEXT_SIZE, data_sp->GetByteSize()); | |
| return error; | |
| } | |
| const uint8_t *src = data_sp->GetBytes(); | |
| if (src == nullptr) { | |
| error = Status::FromErrorStringWithFormat( | |
| "NativeRegisterContextLinux_s390x::%s " | |
| "DataBuffer::GetBytes() returned a null " | |
| "pointer", | |
| __FUNCTION__); | |
| return error; | |
| } | |
| memcpy(GetGPRBuffer(), src, GetGPRSize()); | |
| src += GetGPRSize(); | |
| error = WriteGPR(); | |
| if (error.Fail()) | |
| return error; | |
| memcpy(GetFPRBuffer(), src, GetFPRSize()); | |
| src += GetFPRSize(); | |
| error = WriteFPR(); | |
| 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::FromErrorString("DoReadRegisterValue unsupported"); | |
| } | |
| Status NativeRegisterContextLinux_s390x::DoWriteRegisterValue( | |
| uint32_t offset, const char *reg_name, const RegisterValue &value) { | |
| return Status::FromErrorString("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::ReadGPR() { | |
| return PeekUserArea(offsetof(user_regs_struct, psw), GetGPRBuffer(), | |
| GetGPRSize()); | |
| } | |
| Status NativeRegisterContextLinux_s390x::WriteGPR() { | |
| return PokeUserArea(offsetof(user_regs_struct, psw), GetGPRBuffer(), | |
| GetGPRSize()); | |
| } | |
| Status NativeRegisterContextLinux_s390x::ReadFPR() { | |
| return PeekUserArea(offsetof(user_regs_struct, fp_regs), GetGPRBuffer(), | |
| GetGPRSize()); | |
| } | |
| Status NativeRegisterContextLinux_s390x::WriteFPR() { | |
| return PokeUserArea(offsetof(user_regs_struct, fp_regs), GetGPRBuffer(), | |
| GetGPRSize()); | |
| } | |
| 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::FromErrorString("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::FromErrorString("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::FromErrorString("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__) |