| 1 | //===-- NativeRegisterContextLinux_s390x.cpp ------------------------------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #if defined(__s390x__) && defined(__linux__) |
| 10 | |
| 11 | #include "NativeRegisterContextLinux_s390x.h" |
| 12 | #include "Plugins/Process/Linux/NativeProcessLinux.h" |
| 13 | #include "Plugins/Process/Utility/RegisterContextLinux_s390x.h" |
| 14 | #include "lldb/Host/HostInfo.h" |
| 15 | #include "lldb/Utility/DataBufferHeap.h" |
| 16 | #include "lldb/Utility/Log.h" |
| 17 | #include "lldb/Utility/RegisterValue.h" |
| 18 | #include "lldb/Utility/Status.h" |
| 19 | #include <sys/ptrace.h> |
| 20 | #include <sys/uio.h> |
| 21 | |
| 22 | using namespace lldb_private; |
| 23 | using namespace lldb_private::process_linux; |
| 24 | |
| 25 | // Private namespace. |
| 26 | |
| 27 | namespace { |
| 28 | // s390x 64-bit general purpose registers. |
| 29 | static const uint32_t g_gpr_regnums_s390x[] = { |
| 30 | lldb_r0_s390x, lldb_r1_s390x, lldb_r2_s390x, lldb_r3_s390x, |
| 31 | lldb_r4_s390x, lldb_r5_s390x, lldb_r6_s390x, lldb_r7_s390x, |
| 32 | lldb_r8_s390x, lldb_r9_s390x, lldb_r10_s390x, lldb_r11_s390x, |
| 33 | lldb_r12_s390x, lldb_r13_s390x, lldb_r14_s390x, lldb_r15_s390x, |
| 34 | lldb_acr0_s390x, lldb_acr1_s390x, lldb_acr2_s390x, lldb_acr3_s390x, |
| 35 | lldb_acr4_s390x, lldb_acr5_s390x, lldb_acr6_s390x, lldb_acr7_s390x, |
| 36 | lldb_acr8_s390x, lldb_acr9_s390x, lldb_acr10_s390x, lldb_acr11_s390x, |
| 37 | lldb_acr12_s390x, lldb_acr13_s390x, lldb_acr14_s390x, lldb_acr15_s390x, |
| 38 | lldb_pswm_s390x, lldb_pswa_s390x, |
| 39 | LLDB_INVALID_REGNUM // register sets need to end with this flag |
| 40 | }; |
| 41 | static_assert((sizeof(g_gpr_regnums_s390x) / sizeof(g_gpr_regnums_s390x[0])) - |
| 42 | 1 == |
| 43 | k_num_gpr_registers_s390x, |
| 44 | "g_gpr_regnums_s390x has wrong number of register infos" ); |
| 45 | |
| 46 | // s390x 64-bit floating point registers. |
| 47 | static const uint32_t g_fpu_regnums_s390x[] = { |
| 48 | lldb_f0_s390x, lldb_f1_s390x, lldb_f2_s390x, lldb_f3_s390x, |
| 49 | lldb_f4_s390x, lldb_f5_s390x, lldb_f6_s390x, lldb_f7_s390x, |
| 50 | lldb_f8_s390x, lldb_f9_s390x, lldb_f10_s390x, lldb_f11_s390x, |
| 51 | lldb_f12_s390x, lldb_f13_s390x, lldb_f14_s390x, lldb_f15_s390x, |
| 52 | lldb_fpc_s390x, |
| 53 | LLDB_INVALID_REGNUM // register sets need to end with this flag |
| 54 | }; |
| 55 | static_assert((sizeof(g_fpu_regnums_s390x) / sizeof(g_fpu_regnums_s390x[0])) - |
| 56 | 1 == |
| 57 | k_num_fpr_registers_s390x, |
| 58 | "g_fpu_regnums_s390x has wrong number of register infos" ); |
| 59 | |
| 60 | // s390x Linux operating-system information. |
| 61 | static const uint32_t g_linux_regnums_s390x[] = { |
| 62 | lldb_orig_r2_s390x, lldb_last_break_s390x, lldb_system_call_s390x, |
| 63 | LLDB_INVALID_REGNUM // register sets need to end with this flag |
| 64 | }; |
| 65 | static_assert((sizeof(g_linux_regnums_s390x) / |
| 66 | sizeof(g_linux_regnums_s390x[0])) - |
| 67 | 1 == |
| 68 | k_num_linux_registers_s390x, |
| 69 | "g_linux_regnums_s390x has wrong number of register infos" ); |
| 70 | |
| 71 | // Number of register sets provided by this context. |
| 72 | enum { k_num_register_sets = 3 }; |
| 73 | |
| 74 | // Register sets for s390x 64-bit. |
| 75 | static const RegisterSet g_reg_sets_s390x[k_num_register_sets] = { |
| 76 | {"General Purpose Registers" , "gpr" , k_num_gpr_registers_s390x, |
| 77 | g_gpr_regnums_s390x}, |
| 78 | {"Floating Point Registers" , "fpr" , k_num_fpr_registers_s390x, |
| 79 | g_fpu_regnums_s390x}, |
| 80 | {"Linux Operating System Data" , "linux" , k_num_linux_registers_s390x, |
| 81 | g_linux_regnums_s390x}, |
| 82 | }; |
| 83 | } |
| 84 | |
| 85 | #define REG_CONTEXT_SIZE (sizeof(s390_regs) + sizeof(s390_fp_regs) + 4) |
| 86 | |
| 87 | // Required ptrace defines. |
| 88 | |
| 89 | #define NT_S390_LAST_BREAK 0x306 /* s390 breaking event address */ |
| 90 | #define NT_S390_SYSTEM_CALL 0x307 /* s390 system call restart data */ |
| 91 | |
| 92 | std::unique_ptr<NativeRegisterContextLinux> |
| 93 | NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux( |
| 94 | const ArchSpec &target_arch, NativeThreadLinux &native_thread) { |
| 95 | return std::make_unique<NativeRegisterContextLinux_s390x>(target_arch, |
| 96 | native_thread); |
| 97 | } |
| 98 | |
| 99 | llvm::Expected<ArchSpec> |
| 100 | NativeRegisterContextLinux::DetermineArchitecture(lldb::tid_t tid) { |
| 101 | return HostInfo::GetArchitecture(); |
| 102 | } |
| 103 | |
| 104 | // NativeRegisterContextLinux_s390x members. |
| 105 | |
| 106 | static RegisterInfoInterface * |
| 107 | CreateRegisterInfoInterface(const ArchSpec &target_arch) { |
| 108 | assert((HostInfo::GetArchitecture().GetAddressByteSize() == 8) && |
| 109 | "Register setting path assumes this is a 64-bit host" ); |
| 110 | return new RegisterContextLinux_s390x(target_arch); |
| 111 | } |
| 112 | |
| 113 | NativeRegisterContextLinux_s390x::NativeRegisterContextLinux_s390x( |
| 114 | const ArchSpec &target_arch, NativeThreadProtocol &native_thread) |
| 115 | : NativeRegisterContextRegisterInfo( |
| 116 | native_thread, CreateRegisterInfoInterface(target_arch)), |
| 117 | NativeRegisterContextLinux(native_thread) { |
| 118 | // Set up data about ranges of valid registers. |
| 119 | switch (target_arch.GetMachine()) { |
| 120 | case llvm::Triple::systemz: |
| 121 | m_reg_info.num_registers = k_num_registers_s390x; |
| 122 | m_reg_info.num_gpr_registers = k_num_gpr_registers_s390x; |
| 123 | m_reg_info.num_fpr_registers = k_num_fpr_registers_s390x; |
| 124 | m_reg_info.last_gpr = k_last_gpr_s390x; |
| 125 | m_reg_info.first_fpr = k_first_fpr_s390x; |
| 126 | m_reg_info.last_fpr = k_last_fpr_s390x; |
| 127 | break; |
| 128 | default: |
| 129 | assert(false && "Unhandled target architecture." ); |
| 130 | break; |
| 131 | } |
| 132 | |
| 133 | // Clear out the watchpoint state. |
| 134 | m_watchpoint_addr = LLDB_INVALID_ADDRESS; |
| 135 | } |
| 136 | |
| 137 | uint32_t NativeRegisterContextLinux_s390x::GetRegisterSetCount() const { |
| 138 | uint32_t sets = 0; |
| 139 | for (uint32_t set_index = 0; set_index < k_num_register_sets; ++set_index) { |
| 140 | if (IsRegisterSetAvailable(set_index)) |
| 141 | ++sets; |
| 142 | } |
| 143 | |
| 144 | return sets; |
| 145 | } |
| 146 | |
| 147 | uint32_t NativeRegisterContextLinux_s390x::GetUserRegisterCount() const { |
| 148 | uint32_t count = 0; |
| 149 | for (uint32_t set_index = 0; set_index < k_num_register_sets; ++set_index) { |
| 150 | const RegisterSet *set = GetRegisterSet(set_index); |
| 151 | if (set) |
| 152 | count += set->num_registers; |
| 153 | } |
| 154 | return count; |
| 155 | } |
| 156 | |
| 157 | const RegisterSet * |
| 158 | NativeRegisterContextLinux_s390x::GetRegisterSet(uint32_t set_index) const { |
| 159 | if (!IsRegisterSetAvailable(set_index)) |
| 160 | return nullptr; |
| 161 | |
| 162 | switch (GetRegisterInfoInterface().GetTargetArchitecture().GetMachine()) { |
| 163 | case llvm::Triple::systemz: |
| 164 | return &g_reg_sets_s390x[set_index]; |
| 165 | default: |
| 166 | assert(false && "Unhandled target architecture." ); |
| 167 | return nullptr; |
| 168 | } |
| 169 | |
| 170 | return nullptr; |
| 171 | } |
| 172 | |
| 173 | bool NativeRegisterContextLinux_s390x::IsRegisterSetAvailable( |
| 174 | uint32_t set_index) const { |
| 175 | return set_index < k_num_register_sets; |
| 176 | } |
| 177 | |
| 178 | bool NativeRegisterContextLinux_s390x::IsGPR(uint32_t reg_index) const { |
| 179 | // GPRs come first. "orig_r2" counts as GPR since it is part of the GPR |
| 180 | // register area. |
| 181 | return reg_index <= m_reg_info.last_gpr || reg_index == lldb_orig_r2_s390x; |
| 182 | } |
| 183 | |
| 184 | bool NativeRegisterContextLinux_s390x::IsFPR(uint32_t reg_index) const { |
| 185 | return (m_reg_info.first_fpr <= reg_index && |
| 186 | reg_index <= m_reg_info.last_fpr); |
| 187 | } |
| 188 | |
| 189 | Status |
| 190 | NativeRegisterContextLinux_s390x::ReadRegister(const RegisterInfo *reg_info, |
| 191 | RegisterValue ®_value) { |
| 192 | if (!reg_info) |
| 193 | return Status::FromErrorString("reg_info NULL" ); |
| 194 | |
| 195 | const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; |
| 196 | if (reg == LLDB_INVALID_REGNUM) |
| 197 | return Status::FromErrorStringWithFormat( |
| 198 | "register \"%s\" is an internal-only lldb register, cannot " |
| 199 | "read directly" , |
| 200 | reg_info->name); |
| 201 | |
| 202 | if (IsGPR(reg)) { |
| 203 | Status error = ReadGPR(); |
| 204 | if (error.Fail()) |
| 205 | return error; |
| 206 | |
| 207 | uint8_t *src = (uint8_t *)&m_regs + reg_info->byte_offset; |
| 208 | assert(reg_info->byte_offset + reg_info->byte_size <= sizeof(m_regs)); |
| 209 | switch (reg_info->byte_size) { |
| 210 | case 4: |
| 211 | reg_value.SetUInt32(*(uint32_t *)src); |
| 212 | break; |
| 213 | case 8: |
| 214 | reg_value.SetUInt64(*(uint64_t *)src); |
| 215 | break; |
| 216 | default: |
| 217 | assert(false && "Unhandled data size." ); |
| 218 | return Status::FromErrorStringWithFormat("unhandled byte size: %" PRIu32, |
| 219 | reg_info->byte_size); |
| 220 | } |
| 221 | return Status(); |
| 222 | } |
| 223 | |
| 224 | if (IsFPR(reg)) { |
| 225 | Status error = ReadFPR(); |
| 226 | if (error.Fail()) |
| 227 | return error; |
| 228 | |
| 229 | // byte_offset is just the offset within FPR, not the whole user area. |
| 230 | uint8_t *src = (uint8_t *)&m_fp_regs + reg_info->byte_offset; |
| 231 | assert(reg_info->byte_offset + reg_info->byte_size <= sizeof(m_fp_regs)); |
| 232 | switch (reg_info->byte_size) { |
| 233 | case 4: |
| 234 | reg_value.SetUInt32(*(uint32_t *)src); |
| 235 | break; |
| 236 | case 8: |
| 237 | reg_value.SetUInt64(*(uint64_t *)src); |
| 238 | break; |
| 239 | default: |
| 240 | assert(false && "Unhandled data size." ); |
| 241 | return Status::FromErrorStringWithFormat("unhandled byte size: %" PRIu32, |
| 242 | reg_info->byte_size); |
| 243 | } |
| 244 | return Status(); |
| 245 | } |
| 246 | |
| 247 | if (reg == lldb_last_break_s390x) { |
| 248 | uint64_t last_break; |
| 249 | Status error = DoReadRegisterSet(NT_S390_LAST_BREAK, &last_break, 8); |
| 250 | if (error.Fail()) |
| 251 | return error; |
| 252 | |
| 253 | reg_value.SetUInt64(last_break); |
| 254 | return Status(); |
| 255 | } |
| 256 | |
| 257 | if (reg == lldb_system_call_s390x) { |
| 258 | uint32_t system_call; |
| 259 | Status error = DoReadRegisterSet(NT_S390_SYSTEM_CALL, &system_call, 4); |
| 260 | if (error.Fail()) |
| 261 | return error; |
| 262 | |
| 263 | reg_value.SetUInt32(system_call); |
| 264 | return Status(); |
| 265 | } |
| 266 | |
| 267 | return Status::FromErrorString("failed - register wasn't recognized" ); |
| 268 | } |
| 269 | |
| 270 | Status NativeRegisterContextLinux_s390x::WriteRegister( |
| 271 | const RegisterInfo *reg_info, const RegisterValue ®_value) { |
| 272 | if (!reg_info) |
| 273 | return Status::FromErrorString("reg_info NULL" ); |
| 274 | |
| 275 | const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; |
| 276 | if (reg == LLDB_INVALID_REGNUM) |
| 277 | return Status::FromErrorStringWithFormat( |
| 278 | "register \"%s\" is an internal-only lldb register, cannot " |
| 279 | "write directly" , |
| 280 | reg_info->name); |
| 281 | |
| 282 | if (IsGPR(reg)) { |
| 283 | Status error = ReadGPR(); |
| 284 | if (error.Fail()) |
| 285 | return error; |
| 286 | |
| 287 | uint8_t *dst = (uint8_t *)&m_regs + reg_info->byte_offset; |
| 288 | assert(reg_info->byte_offset + reg_info->byte_size <= sizeof(m_regs)); |
| 289 | switch (reg_info->byte_size) { |
| 290 | case 4: |
| 291 | *(uint32_t *)dst = reg_value.GetAsUInt32(); |
| 292 | break; |
| 293 | case 8: |
| 294 | *(uint64_t *)dst = reg_value.GetAsUInt64(); |
| 295 | break; |
| 296 | default: |
| 297 | assert(false && "Unhandled data size." ); |
| 298 | return Status::FromErrorStringWithFormat("unhandled byte size: %" PRIu32, |
| 299 | reg_info->byte_size); |
| 300 | } |
| 301 | return WriteGPR(); |
| 302 | } |
| 303 | |
| 304 | if (IsFPR(reg)) { |
| 305 | Status error = ReadFPR(); |
| 306 | if (error.Fail()) |
| 307 | return error; |
| 308 | |
| 309 | // byte_offset is just the offset within fp_regs, not the whole user area. |
| 310 | uint8_t *dst = (uint8_t *)&m_fp_regs + reg_info->byte_offset; |
| 311 | assert(reg_info->byte_offset + reg_info->byte_size <= sizeof(m_fp_regs)); |
| 312 | switch (reg_info->byte_size) { |
| 313 | case 4: |
| 314 | *(uint32_t *)dst = reg_value.GetAsUInt32(); |
| 315 | break; |
| 316 | case 8: |
| 317 | *(uint64_t *)dst = reg_value.GetAsUInt64(); |
| 318 | break; |
| 319 | default: |
| 320 | assert(false && "Unhandled data size." ); |
| 321 | return Status::FromErrorStringWithFormat("unhandled byte size: %" PRIu32, |
| 322 | reg_info->byte_size); |
| 323 | } |
| 324 | return WriteFPR(); |
| 325 | } |
| 326 | |
| 327 | if (reg == lldb_last_break_s390x) { |
| 328 | return Status::FromErrorString("The last break address is read-only" ); |
| 329 | } |
| 330 | |
| 331 | if (reg == lldb_system_call_s390x) { |
| 332 | uint32_t system_call = reg_value.GetAsUInt32(); |
| 333 | return DoWriteRegisterSet(NT_S390_SYSTEM_CALL, &system_call, 4); |
| 334 | } |
| 335 | |
| 336 | return Status::FromErrorString("failed - register wasn't recognized" ); |
| 337 | } |
| 338 | |
| 339 | Status NativeRegisterContextLinux_s390x::ReadAllRegisterValues( |
| 340 | lldb::WritableDataBufferSP &data_sp) { |
| 341 | Status error; |
| 342 | |
| 343 | data_sp.reset(new DataBufferHeap(REG_CONTEXT_SIZE, 0)); |
| 344 | uint8_t *dst = data_sp->GetBytes(); |
| 345 | error = ReadGPR(); |
| 346 | if (error.Fail()) |
| 347 | return error; |
| 348 | memcpy(dst, GetGPRBuffer(), GetGPRSize()); |
| 349 | dst += GetGPRSize(); |
| 350 | |
| 351 | error = ReadFPR(); |
| 352 | if (error.Fail()) |
| 353 | return error; |
| 354 | memcpy(dst, GetFPRBuffer(), GetFPRSize()); |
| 355 | dst += GetFPRSize(); |
| 356 | |
| 357 | // Ignore errors if the regset is unsupported (happens on older kernels). |
| 358 | DoReadRegisterSet(NT_S390_SYSTEM_CALL, dst, 4); |
| 359 | dst += 4; |
| 360 | |
| 361 | // To enable inferior function calls while the process is stopped in an |
| 362 | // interrupted system call, we need to clear the system call flag. It will be |
| 363 | // restored to its original value by WriteAllRegisterValues. Again we ignore |
| 364 | // error if the regset is unsupported. |
| 365 | uint32_t system_call = 0; |
| 366 | DoWriteRegisterSet(NT_S390_SYSTEM_CALL, &system_call, 4); |
| 367 | |
| 368 | return error; |
| 369 | } |
| 370 | |
| 371 | Status NativeRegisterContextLinux_s390x::WriteAllRegisterValues( |
| 372 | const lldb::DataBufferSP &data_sp) { |
| 373 | Status error; |
| 374 | |
| 375 | if (!data_sp) { |
| 376 | error = Status::FromErrorStringWithFormat( |
| 377 | "NativeRegisterContextLinux_s390x::%s invalid data_sp provided" , |
| 378 | __FUNCTION__); |
| 379 | return error; |
| 380 | } |
| 381 | |
| 382 | if (data_sp->GetByteSize() != REG_CONTEXT_SIZE) { |
| 383 | error = Status::FromErrorStringWithFormat( |
| 384 | "NativeRegisterContextLinux_s390x::%s data_sp contained mismatched " |
| 385 | "data size, expected %" PRIu64 ", actual %" PRIu64, |
| 386 | __FUNCTION__, REG_CONTEXT_SIZE, data_sp->GetByteSize()); |
| 387 | return error; |
| 388 | } |
| 389 | |
| 390 | const uint8_t *src = data_sp->GetBytes(); |
| 391 | if (src == nullptr) { |
| 392 | error = Status::FromErrorStringWithFormat( |
| 393 | "NativeRegisterContextLinux_s390x::%s " |
| 394 | "DataBuffer::GetBytes() returned a null " |
| 395 | "pointer" , |
| 396 | __FUNCTION__); |
| 397 | return error; |
| 398 | } |
| 399 | |
| 400 | memcpy(GetGPRBuffer(), src, GetGPRSize()); |
| 401 | src += GetGPRSize(); |
| 402 | error = WriteGPR(); |
| 403 | if (error.Fail()) |
| 404 | return error; |
| 405 | |
| 406 | memcpy(GetFPRBuffer(), src, GetFPRSize()); |
| 407 | src += GetFPRSize(); |
| 408 | error = WriteFPR(); |
| 409 | if (error.Fail()) |
| 410 | return error; |
| 411 | |
| 412 | // Ignore errors if the regset is unsupported (happens on older kernels). |
| 413 | DoWriteRegisterSet(NT_S390_SYSTEM_CALL, src, 4); |
| 414 | src += 4; |
| 415 | |
| 416 | return error; |
| 417 | } |
| 418 | |
| 419 | Status NativeRegisterContextLinux_s390x::DoReadRegisterValue( |
| 420 | uint32_t offset, const char *reg_name, uint32_t size, |
| 421 | RegisterValue &value) { |
| 422 | return Status::FromErrorString("DoReadRegisterValue unsupported" ); |
| 423 | } |
| 424 | |
| 425 | Status NativeRegisterContextLinux_s390x::DoWriteRegisterValue( |
| 426 | uint32_t offset, const char *reg_name, const RegisterValue &value) { |
| 427 | return Status::FromErrorString("DoWriteRegisterValue unsupported" ); |
| 428 | } |
| 429 | |
| 430 | Status NativeRegisterContextLinux_s390x::PeekUserArea(uint32_t offset, |
| 431 | void *buf, |
| 432 | size_t buf_size) { |
| 433 | ptrace_area parea; |
| 434 | parea.len = buf_size; |
| 435 | parea.process_addr = (addr_t)buf; |
| 436 | parea.kernel_addr = offset; |
| 437 | |
| 438 | return NativeProcessLinux::PtraceWrapper(PTRACE_PEEKUSR_AREA, |
| 439 | m_thread.GetID(), &parea); |
| 440 | } |
| 441 | |
| 442 | Status NativeRegisterContextLinux_s390x::PokeUserArea(uint32_t offset, |
| 443 | const void *buf, |
| 444 | size_t buf_size) { |
| 445 | ptrace_area parea; |
| 446 | parea.len = buf_size; |
| 447 | parea.process_addr = (addr_t)buf; |
| 448 | parea.kernel_addr = offset; |
| 449 | |
| 450 | return NativeProcessLinux::PtraceWrapper(PTRACE_POKEUSR_AREA, |
| 451 | m_thread.GetID(), &parea); |
| 452 | } |
| 453 | |
| 454 | Status NativeRegisterContextLinux_s390x::ReadGPR() { |
| 455 | return PeekUserArea(offsetof(user_regs_struct, psw), GetGPRBuffer(), |
| 456 | GetGPRSize()); |
| 457 | } |
| 458 | |
| 459 | Status NativeRegisterContextLinux_s390x::WriteGPR() { |
| 460 | return PokeUserArea(offsetof(user_regs_struct, psw), GetGPRBuffer(), |
| 461 | GetGPRSize()); |
| 462 | } |
| 463 | |
| 464 | Status NativeRegisterContextLinux_s390x::ReadFPR() { |
| 465 | return PeekUserArea(offsetof(user_regs_struct, fp_regs), GetGPRBuffer(), |
| 466 | GetGPRSize()); |
| 467 | } |
| 468 | |
| 469 | Status NativeRegisterContextLinux_s390x::WriteFPR() { |
| 470 | return PokeUserArea(offsetof(user_regs_struct, fp_regs), GetGPRBuffer(), |
| 471 | GetGPRSize()); |
| 472 | } |
| 473 | |
| 474 | Status NativeRegisterContextLinux_s390x::DoReadRegisterSet(uint32_t regset, |
| 475 | void *buf, |
| 476 | size_t buf_size) { |
| 477 | struct iovec iov; |
| 478 | iov.iov_base = buf; |
| 479 | iov.iov_len = buf_size; |
| 480 | |
| 481 | return ReadRegisterSet(&iov, buf_size, regset); |
| 482 | } |
| 483 | |
| 484 | Status NativeRegisterContextLinux_s390x::DoWriteRegisterSet(uint32_t regset, |
| 485 | const void *buf, |
| 486 | size_t buf_size) { |
| 487 | struct iovec iov; |
| 488 | iov.iov_base = const_cast<void *>(buf); |
| 489 | iov.iov_len = buf_size; |
| 490 | |
| 491 | return WriteRegisterSet(&iov, buf_size, regset); |
| 492 | } |
| 493 | |
| 494 | Status NativeRegisterContextLinux_s390x::IsWatchpointHit(uint32_t wp_index, |
| 495 | bool &is_hit) { |
| 496 | per_lowcore_bits per_lowcore; |
| 497 | |
| 498 | if (wp_index >= NumSupportedHardwareWatchpoints()) |
| 499 | return Status::FromErrorString("Watchpoint index out of range" ); |
| 500 | |
| 501 | if (m_watchpoint_addr == LLDB_INVALID_ADDRESS) { |
| 502 | is_hit = false; |
| 503 | return Status(); |
| 504 | } |
| 505 | |
| 506 | Status error = PeekUserArea(offsetof(user_regs_struct, per_info.lowcore), |
| 507 | &per_lowcore, sizeof(per_lowcore)); |
| 508 | if (error.Fail()) { |
| 509 | is_hit = false; |
| 510 | return error; |
| 511 | } |
| 512 | |
| 513 | is_hit = (per_lowcore.perc_storage_alteration == 1 && |
| 514 | per_lowcore.perc_store_real_address == 0); |
| 515 | |
| 516 | if (is_hit) { |
| 517 | // Do not report this watchpoint again. |
| 518 | memset(&per_lowcore, 0, sizeof(per_lowcore)); |
| 519 | PokeUserArea(offsetof(user_regs_struct, per_info.lowcore), &per_lowcore, |
| 520 | sizeof(per_lowcore)); |
| 521 | } |
| 522 | |
| 523 | return Status(); |
| 524 | } |
| 525 | |
| 526 | Status NativeRegisterContextLinux_s390x::GetWatchpointHitIndex( |
| 527 | uint32_t &wp_index, lldb::addr_t trap_addr) { |
| 528 | uint32_t num_hw_wps = NumSupportedHardwareWatchpoints(); |
| 529 | for (wp_index = 0; wp_index < num_hw_wps; ++wp_index) { |
| 530 | bool is_hit; |
| 531 | Status error = IsWatchpointHit(wp_index, is_hit); |
| 532 | if (error.Fail()) { |
| 533 | wp_index = LLDB_INVALID_INDEX32; |
| 534 | return error; |
| 535 | } else if (is_hit) { |
| 536 | return error; |
| 537 | } |
| 538 | } |
| 539 | wp_index = LLDB_INVALID_INDEX32; |
| 540 | return Status(); |
| 541 | } |
| 542 | |
| 543 | Status NativeRegisterContextLinux_s390x::IsWatchpointVacant(uint32_t wp_index, |
| 544 | bool &is_vacant) { |
| 545 | if (wp_index >= NumSupportedHardwareWatchpoints()) |
| 546 | return Status::FromErrorString("Watchpoint index out of range" ); |
| 547 | |
| 548 | is_vacant = m_watchpoint_addr == LLDB_INVALID_ADDRESS; |
| 549 | |
| 550 | return Status(); |
| 551 | } |
| 552 | |
| 553 | bool NativeRegisterContextLinux_s390x::ClearHardwareWatchpoint( |
| 554 | uint32_t wp_index) { |
| 555 | per_struct per_info; |
| 556 | |
| 557 | if (wp_index >= NumSupportedHardwareWatchpoints()) |
| 558 | return false; |
| 559 | |
| 560 | Status error = PeekUserArea(offsetof(user_regs_struct, per_info), &per_info, |
| 561 | sizeof(per_info)); |
| 562 | if (error.Fail()) |
| 563 | return false; |
| 564 | |
| 565 | per_info.control_regs.bits.em_storage_alteration = 0; |
| 566 | per_info.control_regs.bits.storage_alt_space_ctl = 0; |
| 567 | per_info.starting_addr = 0; |
| 568 | per_info.ending_addr = 0; |
| 569 | |
| 570 | error = PokeUserArea(offsetof(user_regs_struct, per_info), &per_info, |
| 571 | sizeof(per_info)); |
| 572 | if (error.Fail()) |
| 573 | return false; |
| 574 | |
| 575 | m_watchpoint_addr = LLDB_INVALID_ADDRESS; |
| 576 | return true; |
| 577 | } |
| 578 | |
| 579 | Status NativeRegisterContextLinux_s390x::ClearAllHardwareWatchpoints() { |
| 580 | if (ClearHardwareWatchpoint(0)) |
| 581 | return Status(); |
| 582 | return Status::FromErrorString("Clearing all hardware watchpoints failed." ); |
| 583 | } |
| 584 | |
| 585 | uint32_t NativeRegisterContextLinux_s390x::SetHardwareWatchpoint( |
| 586 | lldb::addr_t addr, size_t size, uint32_t watch_flags) { |
| 587 | per_struct per_info; |
| 588 | |
| 589 | if (watch_flags != 0x1) |
| 590 | return LLDB_INVALID_INDEX32; |
| 591 | |
| 592 | if (m_watchpoint_addr != LLDB_INVALID_ADDRESS) |
| 593 | return LLDB_INVALID_INDEX32; |
| 594 | |
| 595 | Status error = PeekUserArea(offsetof(user_regs_struct, per_info), &per_info, |
| 596 | sizeof(per_info)); |
| 597 | if (error.Fail()) |
| 598 | return LLDB_INVALID_INDEX32; |
| 599 | |
| 600 | per_info.control_regs.bits.em_storage_alteration = 1; |
| 601 | per_info.control_regs.bits.storage_alt_space_ctl = 1; |
| 602 | per_info.starting_addr = addr; |
| 603 | per_info.ending_addr = addr + size - 1; |
| 604 | |
| 605 | error = PokeUserArea(offsetof(user_regs_struct, per_info), &per_info, |
| 606 | sizeof(per_info)); |
| 607 | if (error.Fail()) |
| 608 | return LLDB_INVALID_INDEX32; |
| 609 | |
| 610 | m_watchpoint_addr = addr; |
| 611 | return 0; |
| 612 | } |
| 613 | |
| 614 | lldb::addr_t |
| 615 | NativeRegisterContextLinux_s390x::GetWatchpointAddress(uint32_t wp_index) { |
| 616 | if (wp_index >= NumSupportedHardwareWatchpoints()) |
| 617 | return LLDB_INVALID_ADDRESS; |
| 618 | return m_watchpoint_addr; |
| 619 | } |
| 620 | |
| 621 | uint32_t NativeRegisterContextLinux_s390x::NumSupportedHardwareWatchpoints() { |
| 622 | return 1; |
| 623 | } |
| 624 | |
| 625 | #endif // defined(__s390x__) && defined(__linux__) |
| 626 | |