brintos

brintos / llvm-project-archived public Read only

0
0
Text · 20.3 KiB · 84ba53b Raw
626 lines · cpp
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-exception6//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 22using namespace lldb_private;23using namespace lldb_private::process_linux;24 25// Private namespace.26 27namespace {28// s390x 64-bit general purpose registers.29static 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 flag40};41static_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.47static 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 flag54};55static_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.61static 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 flag64};65static_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.72enum { k_num_register_sets = 3 };73 74// Register sets for s390x 64-bit.75static 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 92std::unique_ptr<NativeRegisterContextLinux>93NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux(94    const ArchSpec &target_arch, NativeThreadLinux &native_thread) {95  return std::make_unique<NativeRegisterContextLinux_s390x>(target_arch,96                                                             native_thread);97}98 99llvm::Expected<ArchSpec>100NativeRegisterContextLinux::DetermineArchitecture(lldb::tid_t tid) {101  return HostInfo::GetArchitecture();102}103 104// NativeRegisterContextLinux_s390x members.105 106static RegisterInfoInterface *107CreateRegisterInfoInterface(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 113NativeRegisterContextLinux_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 137uint32_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 147uint32_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 157const RegisterSet *158NativeRegisterContextLinux_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 173bool NativeRegisterContextLinux_s390x::IsRegisterSetAvailable(174    uint32_t set_index) const {175  return set_index < k_num_register_sets;176}177 178bool NativeRegisterContextLinux_s390x::IsGPR(uint32_t reg_index) const {179  // GPRs come first.  "orig_r2" counts as GPR since it is part of the GPR180  // register area.181  return reg_index <= m_reg_info.last_gpr || reg_index == lldb_orig_r2_s390x;182}183 184bool 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 189Status190NativeRegisterContextLinux_s390x::ReadRegister(const RegisterInfo *reg_info,191                                               RegisterValue &reg_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 270Status NativeRegisterContextLinux_s390x::WriteRegister(271    const RegisterInfo *reg_info, const RegisterValue &reg_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 339Status 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 an362  // interrupted system call, we need to clear the system call flag. It will be363  // restored to its original value by WriteAllRegisterValues. Again we ignore364  // 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 371Status 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 419Status 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 425Status NativeRegisterContextLinux_s390x::DoWriteRegisterValue(426    uint32_t offset, const char *reg_name, const RegisterValue &value) {427  return Status::FromErrorString("DoWriteRegisterValue unsupported");428}429 430Status 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 442Status 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 454Status NativeRegisterContextLinux_s390x::ReadGPR() {455  return PeekUserArea(offsetof(user_regs_struct, psw), GetGPRBuffer(),456                      GetGPRSize());457}458 459Status NativeRegisterContextLinux_s390x::WriteGPR() {460  return PokeUserArea(offsetof(user_regs_struct, psw), GetGPRBuffer(),461                      GetGPRSize());462}463 464Status NativeRegisterContextLinux_s390x::ReadFPR() {465  return PeekUserArea(offsetof(user_regs_struct, fp_regs), GetGPRBuffer(),466                      GetGPRSize());467}468 469Status NativeRegisterContextLinux_s390x::WriteFPR() {470  return PokeUserArea(offsetof(user_regs_struct, fp_regs), GetGPRBuffer(),471                      GetGPRSize());472}473 474Status 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 484Status 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 494Status 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 526Status 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 543Status 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 553bool 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 579Status NativeRegisterContextLinux_s390x::ClearAllHardwareWatchpoints() {580  if (ClearHardwareWatchpoint(0))581    return Status();582  return Status::FromErrorString("Clearing all hardware watchpoints failed.");583}584 585uint32_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 614lldb::addr_t615NativeRegisterContextLinux_s390x::GetWatchpointAddress(uint32_t wp_index) {616  if (wp_index >= NumSupportedHardwareWatchpoints())617    return LLDB_INVALID_ADDRESS;618  return m_watchpoint_addr;619}620 621uint32_t NativeRegisterContextLinux_s390x::NumSupportedHardwareWatchpoints() {622  return 1;623}624 625#endif // defined(__s390x__) && defined(__linux__)626