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 ®_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 ®_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, ®set,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, ®set,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 ®set, &ioVec, ioVec.iov_len)618 .ToError();619}620#endif // defined(__loongarch__) && __loongarch_grlen == 64621