brintos

brintos / llvm-project-archived public Read only

0
0
Text · 17.2 KiB · c484195 Raw
621 lines · cpp
1//===-- NativeRegisterContextLinux_loongarch64.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(__loongarch__) && __loongarch_grlen == 6410 11#include "NativeRegisterContextLinux_loongarch64.h"12 13#include "lldb/Host/HostInfo.h"14#include "lldb/Host/linux/Ptrace.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 20#include "Plugins/Process/Linux/NativeProcessLinux.h"21#include "Plugins/Process/Linux/Procfs.h"22#include "Plugins/Process/Utility/RegisterInfoPOSIX_loongarch64.h"23#include "Plugins/Process/Utility/lldb-loongarch-register-enums.h"24 25// NT_PRSTATUS and NT_FPREGSET definition26#include <elf.h>27// struct iovec definition28#include <sys/uio.h>29 30// LoongArch SIMD eXtension registers31#ifndef NT_LOONGARCH_LSX32#define NT_LOONGARCH_LSX 0xa0233#endif34 35// LoongArch Advanced SIMD eXtension registers36#ifndef NT_LOONGARCH_LASX37#define NT_LOONGARCH_LASX 0xa0338#endif39 40// LoongArch hardware breakpoint registers41#ifndef NT_LOONGARCH_HW_BREAK42#define NT_LOONGARCH_HW_BREAK 0xa0543#endif44 45// LoongArch hardware watchpoint registers46#ifndef NT_LOONGARCH_HW_WATCH47#define NT_LOONGARCH_HW_WATCH 0xa0648#endif49 50#define REG_CONTEXT_SIZE                                                       \51  (GetGPRSize() + GetFPRSize() + sizeof(m_lsx) + sizeof(m_lasx))52 53// ptrace has a struct type user_watch_state, which was replaced by54// user_watch_state_v2 when more watchpoints were added, so this file55// may be built on systems with one or both in the system headers.56// The type below has the same layout as user_watch_state_v2 but will57// not clash with that name if it exists. We can use the v2 layout even58// on old kernels as we will only see 8 watchpoints and the kernel will59// truncate any extra data we send to it.60struct loongarch_user_watch_state {61  uint64_t dbg_info;62  struct {63    uint64_t addr;64    uint64_t mask;65    uint32_t ctrl;66    uint32_t pad;67  } dbg_regs[14];68};69 70using namespace lldb;71using namespace lldb_private;72using namespace lldb_private::process_linux;73 74std::unique_ptr<NativeRegisterContextLinux>75NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux(76    const ArchSpec &target_arch, NativeThreadLinux &native_thread) {77  switch (target_arch.GetMachine()) {78  case llvm::Triple::loongarch64: {79    Flags opt_regsets;80    auto register_info_up = std::make_unique<RegisterInfoPOSIX_loongarch64>(81        target_arch, opt_regsets);82    return std::make_unique<NativeRegisterContextLinux_loongarch64>(83        target_arch, native_thread, std::move(register_info_up));84  }85  default:86    llvm_unreachable("have no register context for architecture");87  }88}89 90llvm::Expected<ArchSpec>91NativeRegisterContextLinux::DetermineArchitecture(lldb::tid_t tid) {92  return HostInfo::GetArchitecture();93}94 95NativeRegisterContextLinux_loongarch64::NativeRegisterContextLinux_loongarch64(96    const ArchSpec &target_arch, NativeThreadProtocol &native_thread,97    std::unique_ptr<RegisterInfoPOSIX_loongarch64> register_info_up)98    : NativeRegisterContextRegisterInfo(native_thread,99                                        register_info_up.release()),100      NativeRegisterContextLinux(native_thread) {101  ::memset(&m_fpr, 0, sizeof(m_fpr));102  ::memset(&m_gpr, 0, sizeof(m_gpr));103  ::memset(&m_lsx, 0, sizeof(m_lsx));104  ::memset(&m_lasx, 0, sizeof(m_lasx));105 106  ::memset(&m_hwp_regs, 0, sizeof(m_hwp_regs));107  ::memset(&m_hbp_regs, 0, sizeof(m_hbp_regs));108 109  // Refer to:110  // https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#control-and-status-registers-related-to-watchpoints111  // 14 is just a maximum value, query hardware for actual watchpoint count.112  m_max_hwp_supported = 14;113  m_max_hbp_supported = 14;114  m_refresh_hwdebug_info = true;115 116  m_gpr_is_valid = false;117  m_fpu_is_valid = false;118  m_lsx_is_valid = false;119  m_lasx_is_valid = false;120}121 122const RegisterInfoPOSIX_loongarch64 &123NativeRegisterContextLinux_loongarch64::GetRegisterInfo() const {124  return static_cast<const RegisterInfoPOSIX_loongarch64 &>(125      NativeRegisterContextRegisterInfo::GetRegisterInfoInterface());126}127 128uint32_t NativeRegisterContextLinux_loongarch64::GetRegisterSetCount() const {129  return GetRegisterInfo().GetRegisterSetCount();130}131 132const RegisterSet *NativeRegisterContextLinux_loongarch64::GetRegisterSet(133    uint32_t set_index) const {134  return GetRegisterInfo().GetRegisterSet(set_index);135}136 137uint32_t NativeRegisterContextLinux_loongarch64::GetUserRegisterCount() const {138  uint32_t count = 0;139  for (uint32_t set_index = 0; set_index < GetRegisterSetCount(); ++set_index)140    count += GetRegisterSet(set_index)->num_registers;141  return count;142}143 144Status NativeRegisterContextLinux_loongarch64::ReadRegister(145    const RegisterInfo *reg_info, RegisterValue &reg_value) {146  Status error;147 148  if (!reg_info) {149    error = Status::FromErrorString("reg_info NULL");150    return error;151  }152 153  const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];154 155  if (reg == LLDB_INVALID_REGNUM)156    return Status::FromErrorStringWithFormat(157        "no lldb regnum for %s",158        reg_info && reg_info->name ? reg_info->name : "<unknown register>");159 160  uint8_t *src = nullptr;161  uint32_t offset = LLDB_INVALID_INDEX32;162 163  if (IsGPR(reg)) {164    error = ReadGPR();165    if (error.Fail())166      return error;167 168    offset = reg_info->byte_offset;169    assert(offset < GetGPRSize());170    src = (uint8_t *)GetGPRBuffer() + offset;171 172  } else if (IsFPR(reg)) {173    error = ReadFPR();174    if (error.Fail())175      return error;176 177    offset = CalculateFprOffset(reg_info);178    assert(offset < GetFPRSize());179    src = (uint8_t *)GetFPRBuffer() + offset;180  } else if (IsLSX(reg)) {181    error = ReadLSX();182    if (error.Fail())183      return error;184 185    offset = CalculateLsxOffset(reg_info);186    assert(offset < sizeof(m_lsx));187    src = (uint8_t *)&m_lsx + offset;188  } else if (IsLASX(reg)) {189    error = ReadLASX();190    if (error.Fail())191      return error;192 193    offset = CalculateLasxOffset(reg_info);194    assert(offset < sizeof(m_lasx));195    src = (uint8_t *)&m_lasx + offset;196  } else197    return Status::FromErrorString(198        "failed - register wasn't recognized to be a GPR or an FPR, "199        "write strategy unknown");200 201  reg_value.SetFromMemoryData(*reg_info, src, reg_info->byte_size,202                              eByteOrderLittle, error);203 204  return error;205}206 207Status NativeRegisterContextLinux_loongarch64::WriteRegister(208    const RegisterInfo *reg_info, const RegisterValue &reg_value) {209  Status error;210 211  if (!reg_info)212    return Status::FromErrorString("reg_info NULL");213 214  const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];215 216  if (reg == LLDB_INVALID_REGNUM)217    return Status::FromErrorStringWithFormat(218        "no lldb regnum for %s",219        reg_info->name != nullptr ? reg_info->name : "<unknown register>");220 221  uint8_t *dst = nullptr;222  uint32_t offset = LLDB_INVALID_INDEX32;223 224  if (IsGPR(reg)) {225    error = ReadGPR();226    if (error.Fail())227      return error;228 229    assert(reg_info->byte_offset < GetGPRSize());230    dst = (uint8_t *)GetGPRBuffer() + reg_info->byte_offset;231    ::memcpy(dst, reg_value.GetBytes(), reg_info->byte_size);232 233    return WriteGPR();234  } else if (IsFPR(reg)) {235    error = ReadFPR();236    if (error.Fail())237      return error;238 239    offset = CalculateFprOffset(reg_info);240    assert(offset < GetFPRSize());241    dst = (uint8_t *)GetFPRBuffer() + offset;242    ::memcpy(dst, reg_value.GetBytes(), reg_info->byte_size);243 244    return WriteFPR();245  } else if (IsLSX(reg)) {246    error = ReadLSX();247    if (error.Fail())248      return error;249 250    offset = CalculateLsxOffset(reg_info);251    assert(offset < sizeof(m_lsx));252    dst = (uint8_t *)&m_lsx + offset;253    ::memcpy(dst, reg_value.GetBytes(), reg_info->byte_size);254 255    return WriteLSX();256  } else if (IsLASX(reg)) {257    error = ReadLASX();258    if (error.Fail())259      return error;260 261    offset = CalculateLasxOffset(reg_info);262    assert(offset < sizeof(m_lasx));263    dst = (uint8_t *)&m_lasx + offset;264    ::memcpy(dst, reg_value.GetBytes(), reg_info->byte_size);265 266    return WriteLASX();267  }268 269  return Status::FromErrorString("Failed to write register value");270}271 272Status NativeRegisterContextLinux_loongarch64::ReadAllRegisterValues(273    lldb::WritableDataBufferSP &data_sp) {274  Status error;275 276  data_sp.reset(new DataBufferHeap(REG_CONTEXT_SIZE, 0));277 278  error = ReadGPR();279  if (error.Fail())280    return error;281 282  error = ReadFPR();283  if (error.Fail())284    return error;285 286  error = ReadLSX();287  if (error.Fail())288    return error;289 290  error = ReadLASX();291  if (error.Fail())292    return error;293 294  uint8_t *dst = data_sp->GetBytes();295  ::memcpy(dst, GetGPRBuffer(), GetGPRSize());296  dst += GetGPRSize();297  ::memcpy(dst, GetFPRBuffer(), GetFPRSize());298  dst += GetFPRSize();299  ::memcpy(dst, &m_lsx, sizeof(m_lsx));300  dst += sizeof(m_lsx);301  ::memcpy(dst, &m_lasx, sizeof(m_lasx));302 303  return error;304}305 306Status NativeRegisterContextLinux_loongarch64::WriteAllRegisterValues(307    const lldb::DataBufferSP &data_sp) {308  Status error;309 310  if (!data_sp) {311    error = Status::FromErrorStringWithFormat(312        "NativeRegisterContextLinux_loongarch64::%s invalid data_sp provided",313        __FUNCTION__);314    return error;315  }316 317  if (data_sp->GetByteSize() != REG_CONTEXT_SIZE) {318    error = Status::FromErrorStringWithFormat(319        "NativeRegisterContextLinux_loongarch64::%s data_sp contained "320        "mismatched data size, expected %" PRIu64 ", actual %" PRIu64,321        __FUNCTION__, REG_CONTEXT_SIZE, data_sp->GetByteSize());322    return error;323  }324 325  const uint8_t *src = data_sp->GetBytes();326  if (src == nullptr) {327    error = Status::FromErrorStringWithFormat(328        "NativeRegisterContextLinux_loongarch64::%s "329        "DataBuffer::GetBytes() returned a null "330        "pointer",331        __FUNCTION__);332    return error;333  }334  ::memcpy(GetGPRBuffer(), src, GetRegisterInfoInterface().GetGPRSize());335 336  error = WriteGPR();337  if (error.Fail())338    return error;339 340  src += GetRegisterInfoInterface().GetGPRSize();341  ::memcpy(GetFPRBuffer(), src, GetFPRSize());342  m_fpu_is_valid = true;343  error = WriteFPR();344  if (error.Fail())345    return error;346 347  // Currently, we assume that LoongArch always support LASX.348  // TODO: check whether LSX/LASX exists.349  src += GetFPRSize();350  ::memcpy(&m_lsx, src, sizeof(m_lsx));351  m_lsx_is_valid = true;352  error = WriteLSX();353  if (error.Fail())354    return error;355 356  src += sizeof(m_lsx);357  ::memcpy(&m_lasx, src, sizeof(m_lasx));358  m_lasx_is_valid = true;359  error = WriteLASX();360  if (error.Fail())361    return error;362 363  return error;364}365 366bool NativeRegisterContextLinux_loongarch64::IsGPR(unsigned reg) const {367  return GetRegisterInfo().GetRegisterSetFromRegisterIndex(reg) ==368         RegisterInfoPOSIX_loongarch64::GPRegSet;369}370 371bool NativeRegisterContextLinux_loongarch64::IsFPR(unsigned reg) const {372  return GetRegisterInfo().GetRegisterSetFromRegisterIndex(reg) ==373         RegisterInfoPOSIX_loongarch64::FPRegSet;374}375 376bool NativeRegisterContextLinux_loongarch64::IsLSX(unsigned reg) const {377  return GetRegisterInfo().GetRegisterSetFromRegisterIndex(reg) ==378         RegisterInfoPOSIX_loongarch64::LSXRegSet;379}380 381bool NativeRegisterContextLinux_loongarch64::IsLASX(unsigned reg) const {382  return GetRegisterInfo().GetRegisterSetFromRegisterIndex(reg) ==383         RegisterInfoPOSIX_loongarch64::LASXRegSet;384}385 386Status NativeRegisterContextLinux_loongarch64::ReadGPR() {387  Status error;388 389  if (m_gpr_is_valid)390    return error;391 392  struct iovec ioVec;393  ioVec.iov_base = GetGPRBuffer();394  ioVec.iov_len = GetGPRSize();395 396  error = ReadRegisterSet(&ioVec, GetGPRSize(), NT_PRSTATUS);397 398  if (error.Success())399    m_gpr_is_valid = true;400 401  return error;402}403 404Status NativeRegisterContextLinux_loongarch64::WriteGPR() {405  Status error = ReadGPR();406  if (error.Fail())407    return error;408 409  struct iovec ioVec;410  ioVec.iov_base = GetGPRBuffer();411  ioVec.iov_len = GetGPRSize();412 413  m_gpr_is_valid = false;414 415  return WriteRegisterSet(&ioVec, GetGPRSize(), NT_PRSTATUS);416}417 418Status NativeRegisterContextLinux_loongarch64::ReadFPR() {419  Status error;420 421  if (m_fpu_is_valid)422    return error;423 424  struct iovec ioVec;425  ioVec.iov_base = GetFPRBuffer();426  ioVec.iov_len = GetFPRSize();427 428  error = ReadRegisterSet(&ioVec, GetFPRSize(), NT_FPREGSET);429 430  if (error.Success())431    m_fpu_is_valid = true;432 433  return error;434}435 436Status NativeRegisterContextLinux_loongarch64::WriteFPR() {437  Status error = ReadFPR();438  if (error.Fail())439    return error;440 441  struct iovec ioVec;442  ioVec.iov_base = GetFPRBuffer();443  ioVec.iov_len = GetFPRSize();444 445  m_fpu_is_valid = false;446  m_lsx_is_valid = false;447  m_lasx_is_valid = false;448 449  return WriteRegisterSet(&ioVec, GetFPRSize(), NT_FPREGSET);450}451 452Status NativeRegisterContextLinux_loongarch64::ReadLSX() {453  Status error;454 455  if (m_lsx_is_valid)456    return error;457 458  struct iovec ioVec;459  ioVec.iov_base = &m_lsx;460  ioVec.iov_len = sizeof(m_lsx);461 462  error = ReadRegisterSet(&ioVec, sizeof(m_lsx), NT_LOONGARCH_LSX);463 464  if (error.Success())465    m_lsx_is_valid = true;466 467  return error;468}469 470Status NativeRegisterContextLinux_loongarch64::WriteLSX() {471  Status error = ReadLSX();472  if (error.Fail())473    return error;474 475  struct iovec ioVec;476  ioVec.iov_base = &m_lsx;477  ioVec.iov_len = sizeof(m_lsx);478 479  m_fpu_is_valid = false;480  m_lsx_is_valid = false;481  m_lasx_is_valid = false;482 483  return WriteRegisterSet(&ioVec, sizeof(m_lsx), NT_LOONGARCH_LSX);484}485 486Status NativeRegisterContextLinux_loongarch64::ReadLASX() {487  Status error;488 489  if (m_lasx_is_valid)490    return error;491 492  struct iovec ioVec;493  ioVec.iov_base = &m_lasx;494  ioVec.iov_len = sizeof(m_lasx);495 496  error = ReadRegisterSet(&ioVec, sizeof(m_lasx), NT_LOONGARCH_LASX);497 498  if (error.Success())499    m_lasx_is_valid = true;500 501  return error;502}503 504Status NativeRegisterContextLinux_loongarch64::WriteLASX() {505  Status error = ReadLASX();506  if (error.Fail())507    return error;508 509  struct iovec ioVec;510  ioVec.iov_base = &m_lasx;511  ioVec.iov_len = sizeof(m_lasx);512 513  m_fpu_is_valid = false;514  m_lsx_is_valid = false;515  m_lasx_is_valid = false;516 517  return WriteRegisterSet(&ioVec, sizeof(m_lasx), NT_LOONGARCH_LASX);518}519 520void NativeRegisterContextLinux_loongarch64::InvalidateAllRegisters() {521  m_gpr_is_valid = false;522  m_fpu_is_valid = false;523  m_lsx_is_valid = false;524  m_lasx_is_valid = false;525}526 527uint32_t NativeRegisterContextLinux_loongarch64::CalculateFprOffset(528    const RegisterInfo *reg_info) const {529  return reg_info->byte_offset - GetGPRSize();530}531 532uint32_t NativeRegisterContextLinux_loongarch64::CalculateLsxOffset(533    const RegisterInfo *reg_info) const {534  return reg_info->byte_offset - GetGPRSize() - sizeof(m_fpr);535}536 537uint32_t NativeRegisterContextLinux_loongarch64::CalculateLasxOffset(538    const RegisterInfo *reg_info) const {539  return reg_info->byte_offset - GetGPRSize() - sizeof(m_fpr) - sizeof(m_lsx);540}541 542std::vector<uint32_t>543NativeRegisterContextLinux_loongarch64::GetExpeditedRegisters(544    ExpeditedRegs expType) const {545  std::vector<uint32_t> expedited_reg_nums =546      NativeRegisterContext::GetExpeditedRegisters(expType);547 548  return expedited_reg_nums;549}550 551llvm::Error NativeRegisterContextLinux_loongarch64::ReadHardwareDebugInfo() {552  if (!m_refresh_hwdebug_info)553    return llvm::Error::success();554 555  ::pid_t tid = m_thread.GetID();556 557  int regset = NT_LOONGARCH_HW_WATCH;558  struct iovec ioVec;559  struct loongarch_user_watch_state dreg_state;560  Status error;561 562  ioVec.iov_base = &dreg_state;563  ioVec.iov_len = sizeof(dreg_state);564  error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, tid, &regset,565                                            &ioVec, ioVec.iov_len);566  if (error.Fail())567    return error.ToError();568 569  m_max_hwp_supported = dreg_state.dbg_info & 0x3f;570 571  regset = NT_LOONGARCH_HW_BREAK;572  error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, tid, &regset,573                                            &ioVec, ioVec.iov_len);574  if (error.Fail())575    return error.ToError();576 577  m_max_hbp_supported = dreg_state.dbg_info & 0x3f;578 579  m_refresh_hwdebug_info = false;580 581  return llvm::Error::success();582}583 584llvm::Error NativeRegisterContextLinux_loongarch64::WriteHardwareDebugRegs(585    DREGType hwbType) {586  struct iovec ioVec;587  struct loongarch_user_watch_state dreg_state;588  int regset;589 590  memset(&dreg_state, 0, sizeof(dreg_state));591  ioVec.iov_base = &dreg_state;592 593  switch (hwbType) {594  case eDREGTypeWATCH:595    regset = NT_LOONGARCH_HW_WATCH;596    ioVec.iov_len = sizeof(dreg_state.dbg_info) +597                    (sizeof(dreg_state.dbg_regs[0]) * m_max_hwp_supported);598 599    for (uint32_t i = 0; i < m_max_hwp_supported; i++) {600      dreg_state.dbg_regs[i].addr = m_hwp_regs[i].address;601      dreg_state.dbg_regs[i].ctrl = m_hwp_regs[i].control;602    }603    break;604  case eDREGTypeBREAK:605    regset = NT_LOONGARCH_HW_BREAK;606    ioVec.iov_len = sizeof(dreg_state.dbg_info) +607                    (sizeof(dreg_state.dbg_regs[0]) * m_max_hbp_supported);608 609    for (uint32_t i = 0; i < m_max_hbp_supported; i++) {610      dreg_state.dbg_regs[i].addr = m_hbp_regs[i].address;611      dreg_state.dbg_regs[i].ctrl = m_hbp_regs[i].control;612    }613    break;614  }615 616  return NativeProcessLinux::PtraceWrapper(PTRACE_SETREGSET, m_thread.GetID(),617                                           &regset, &ioVec, ioVec.iov_len)618      .ToError();619}620#endif // defined(__loongarch__) && __loongarch_grlen == 64621