| //===-- NativeRegisterContextLinux_riscv64.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(__riscv) && __riscv_xlen == 64 | |
| #include "NativeRegisterContextLinux_riscv64.h" | |
| #include "Plugins/Process/Linux/NativeProcessLinux.h" | |
| #include "Plugins/Process/Linux/Procfs.h" | |
| #include "Plugins/Process/Utility/RegisterInfoPOSIX_riscv64.h" | |
| #include "Plugins/Process/Utility/lldb-riscv-register-enums.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" | |
| // System includes - They have to be included after framework includes because | |
| // they define some macros which collide with variable names in other modules | |
| #include <elf.h> | |
| #include <sys/ptrace.h> | |
| #include <sys/uio.h> | |
| #ifndef PTRACE_GETREGSET | |
| #define PTRACE_GETREGSET 0x4204 | |
| #endif | |
| using namespace lldb; | |
| using namespace lldb_private; | |
| using namespace lldb_private::process_linux; | |
| std::unique_ptr<NativeRegisterContextLinux> | |
| NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux( | |
| const ArchSpec &target_arch, NativeThreadLinux &native_thread) { | |
| switch (target_arch.GetMachine()) { | |
| case llvm::Triple::riscv64: { | |
| Flags opt_regsets(RegisterInfoPOSIX_riscv64::eRegsetMaskDefault); | |
| RegisterInfoPOSIX_riscv64::FPR fpr; | |
| struct iovec ioVec; | |
| ioVec.iov_base = &fpr; | |
| ioVec.iov_len = sizeof(fpr); | |
| unsigned int regset = NT_FPREGSET; | |
| if (NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, | |
| native_thread.GetID(), ®set, | |
| &ioVec, sizeof(fpr)) | |
| .Success()) { | |
| opt_regsets.Set(RegisterInfoPOSIX_riscv64::eRegsetMaskFP); | |
| } | |
| auto register_info_up = | |
| std::make_unique<RegisterInfoPOSIX_riscv64>(target_arch, opt_regsets); | |
| return std::make_unique<NativeRegisterContextLinux_riscv64>( | |
| target_arch, native_thread, std::move(register_info_up)); | |
| } | |
| default: | |
| llvm_unreachable("have no register context for architecture"); | |
| } | |
| } | |
| llvm::Expected<ArchSpec> | |
| NativeRegisterContextLinux::DetermineArchitecture(lldb::tid_t tid) { | |
| return HostInfo::GetArchitecture(); | |
| } | |
| NativeRegisterContextLinux_riscv64::NativeRegisterContextLinux_riscv64( | |
| const ArchSpec &target_arch, NativeThreadProtocol &native_thread, | |
| std::unique_ptr<RegisterInfoPOSIX_riscv64> register_info_up) | |
| : NativeRegisterContextRegisterInfo(native_thread, | |
| register_info_up.release()), | |
| NativeRegisterContextLinux(native_thread) { | |
| ::memset(&m_fpr, 0, sizeof(m_fpr)); | |
| ::memset(&m_gpr, 0, sizeof(m_gpr)); | |
| m_gpr_is_valid = false; | |
| m_fpu_is_valid = false; | |
| } | |
| const RegisterInfoPOSIX_riscv64 & | |
| NativeRegisterContextLinux_riscv64::GetRegisterInfo() const { | |
| return static_cast<const RegisterInfoPOSIX_riscv64 &>( | |
| NativeRegisterContextRegisterInfo::GetRegisterInfoInterface()); | |
| } | |
| uint32_t NativeRegisterContextLinux_riscv64::GetRegisterSetCount() const { | |
| return GetRegisterInfo().GetRegisterSetCount(); | |
| } | |
| const RegisterSet * | |
| NativeRegisterContextLinux_riscv64::GetRegisterSet(uint32_t set_index) const { | |
| return GetRegisterInfo().GetRegisterSet(set_index); | |
| } | |
| uint32_t NativeRegisterContextLinux_riscv64::GetUserRegisterCount() const { | |
| uint32_t count = 0; | |
| for (uint32_t set_index = 0; set_index < GetRegisterSetCount(); ++set_index) | |
| count += GetRegisterSet(set_index)->num_registers; | |
| return count; | |
| } | |
| Status | |
| NativeRegisterContextLinux_riscv64::ReadRegister(const RegisterInfo *reg_info, | |
| RegisterValue ®_value) { | |
| Status error; | |
| if (!reg_info) { | |
| error = Status::FromErrorString("reg_info NULL"); | |
| return error; | |
| } | |
| const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; | |
| if (reg == LLDB_INVALID_REGNUM) | |
| return Status::FromErrorStringWithFormat( | |
| "no lldb regnum for %s", | |
| reg_info && reg_info->name ? reg_info->name : "<unknown register>"); | |
| if (reg == gpr_x0_riscv) { | |
| reg_value.SetUInt(0, reg_info->byte_size); | |
| return error; | |
| } | |
| uint8_t *src = nullptr; | |
| uint32_t offset = LLDB_INVALID_INDEX32; | |
| if (IsGPR(reg)) { | |
| error = ReadGPR(); | |
| if (error.Fail()) | |
| return error; | |
| offset = reg_info->byte_offset; | |
| assert(offset < GetGPRSize()); | |
| src = (uint8_t *)GetGPRBuffer() + offset; | |
| } else if (IsFPR(reg)) { | |
| error = ReadFPR(); | |
| if (error.Fail()) | |
| return error; | |
| offset = CalculateFprOffset(reg_info); | |
| assert(offset < GetFPRSize()); | |
| src = (uint8_t *)GetFPRBuffer() + offset; | |
| } else | |
| return Status::FromErrorString( | |
| "failed - register wasn't recognized to be a GPR or an FPR, " | |
| "write strategy unknown"); | |
| reg_value.SetFromMemoryData(*reg_info, src, reg_info->byte_size, | |
| eByteOrderLittle, error); | |
| return error; | |
| } | |
| Status NativeRegisterContextLinux_riscv64::WriteRegister( | |
| const RegisterInfo *reg_info, const RegisterValue ®_value) { | |
| Status error; | |
| 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( | |
| "no lldb regnum for %s", | |
| reg_info->name != nullptr ? reg_info->name : "<unknown register>"); | |
| if (reg == gpr_x0_riscv) { | |
| // do nothing. | |
| return error; | |
| } | |
| uint8_t *dst = nullptr; | |
| uint32_t offset = LLDB_INVALID_INDEX32; | |
| if (IsGPR(reg)) { | |
| error = ReadGPR(); | |
| if (error.Fail()) | |
| return error; | |
| assert(reg_info->byte_offset < GetGPRSize()); | |
| dst = (uint8_t *)GetGPRBuffer() + reg_info->byte_offset; | |
| ::memcpy(dst, reg_value.GetBytes(), reg_info->byte_size); | |
| return WriteGPR(); | |
| } else if (IsFPR(reg)) { | |
| error = ReadFPR(); | |
| if (error.Fail()) | |
| return error; | |
| offset = CalculateFprOffset(reg_info); | |
| assert(offset < GetFPRSize()); | |
| dst = (uint8_t *)GetFPRBuffer() + offset; | |
| ::memcpy(dst, reg_value.GetBytes(), reg_info->byte_size); | |
| return WriteFPR(); | |
| } | |
| return Status::FromErrorString("Failed to write register value"); | |
| } | |
| Status NativeRegisterContextLinux_riscv64::ReadAllRegisterValues( | |
| lldb::WritableDataBufferSP &data_sp) { | |
| Status error; | |
| data_sp.reset(new DataBufferHeap(GetRegContextSize(), 0)); | |
| error = ReadGPR(); | |
| if (error.Fail()) | |
| return error; | |
| if (GetRegisterInfo().IsFPPresent()) { | |
| error = ReadFPR(); | |
| if (error.Fail()) | |
| return error; | |
| } | |
| uint8_t *dst = const_cast<uint8_t *>(data_sp->GetBytes()); | |
| ::memcpy(dst, GetGPRBuffer(), GetGPRSize()); | |
| dst += GetGPRSize(); | |
| if (GetRegisterInfo().IsFPPresent()) | |
| ::memcpy(dst, GetFPRBuffer(), GetFPRSize()); | |
| return error; | |
| } | |
| Status NativeRegisterContextLinux_riscv64::WriteAllRegisterValues( | |
| const lldb::DataBufferSP &data_sp) { | |
| Status error; | |
| if (!data_sp) { | |
| error = Status::FromErrorStringWithFormat( | |
| "NativeRegisterContextLinux_riscv64::%s invalid data_sp provided", | |
| __FUNCTION__); | |
| return error; | |
| } | |
| if (data_sp->GetByteSize() != GetRegContextSize()) { | |
| error = Status::FromErrorStringWithFormat( | |
| "NativeRegisterContextLinux_riscv64::%s data_sp contained mismatched " | |
| "data size, expected %" PRIu64 ", actual %" PRIu64, | |
| __FUNCTION__, GetRegContextSize(), data_sp->GetByteSize()); | |
| return error; | |
| } | |
| uint8_t *src = const_cast<uint8_t *>(data_sp->GetBytes()); | |
| if (src == nullptr) { | |
| error = Status::FromErrorStringWithFormat( | |
| "NativeRegisterContextLinux_riscv64::%s " | |
| "DataBuffer::GetBytes() returned a null " | |
| "pointer", | |
| __FUNCTION__); | |
| return error; | |
| } | |
| ::memcpy(GetGPRBuffer(), src, GetRegisterInfoInterface().GetGPRSize()); | |
| error = WriteGPR(); | |
| if (error.Fail()) | |
| return error; | |
| src += GetRegisterInfoInterface().GetGPRSize(); | |
| if (GetRegisterInfo().IsFPPresent()) { | |
| ::memcpy(GetFPRBuffer(), src, GetFPRSize()); | |
| error = WriteFPR(); | |
| if (error.Fail()) | |
| return error; | |
| } | |
| return error; | |
| } | |
| size_t NativeRegisterContextLinux_riscv64::GetRegContextSize() { | |
| size_t size = GetGPRSize(); | |
| if (GetRegisterInfo().IsFPPresent()) | |
| size += GetFPRSize(); | |
| return size; | |
| } | |
| bool NativeRegisterContextLinux_riscv64::IsGPR(unsigned reg) const { | |
| return GetRegisterInfo().GetRegisterSetFromRegisterIndex(reg) == | |
| RegisterInfoPOSIX_riscv64::GPRegSet; | |
| } | |
| bool NativeRegisterContextLinux_riscv64::IsFPR(unsigned reg) const { | |
| return GetRegisterInfo().IsFPReg(reg); | |
| } | |
| Status NativeRegisterContextLinux_riscv64::ReadGPR() { | |
| Status error; | |
| if (m_gpr_is_valid) | |
| return error; | |
| struct iovec ioVec; | |
| ioVec.iov_base = GetGPRBuffer(); | |
| ioVec.iov_len = GetGPRSize(); | |
| error = ReadRegisterSet(&ioVec, GetGPRSize(), NT_PRSTATUS); | |
| if (error.Success()) | |
| m_gpr_is_valid = true; | |
| return error; | |
| } | |
| Status NativeRegisterContextLinux_riscv64::WriteGPR() { | |
| Status error = ReadGPR(); | |
| if (error.Fail()) | |
| return error; | |
| struct iovec ioVec; | |
| ioVec.iov_base = GetGPRBuffer(); | |
| ioVec.iov_len = GetGPRSize(); | |
| m_gpr_is_valid = false; | |
| return WriteRegisterSet(&ioVec, GetGPRSize(), NT_PRSTATUS); | |
| } | |
| Status NativeRegisterContextLinux_riscv64::ReadFPR() { | |
| Status error; | |
| if (m_fpu_is_valid) | |
| return error; | |
| struct iovec ioVec; | |
| ioVec.iov_base = GetFPRBuffer(); | |
| ioVec.iov_len = GetFPRSize(); | |
| error = ReadRegisterSet(&ioVec, GetFPRSize(), NT_FPREGSET); | |
| if (error.Success()) | |
| m_fpu_is_valid = true; | |
| return error; | |
| } | |
| Status NativeRegisterContextLinux_riscv64::WriteFPR() { | |
| Status error = ReadFPR(); | |
| if (error.Fail()) | |
| return error; | |
| struct iovec ioVec; | |
| ioVec.iov_base = GetFPRBuffer(); | |
| ioVec.iov_len = GetFPRSize(); | |
| m_fpu_is_valid = false; | |
| return WriteRegisterSet(&ioVec, GetFPRSize(), NT_FPREGSET); | |
| } | |
| void NativeRegisterContextLinux_riscv64::InvalidateAllRegisters() { | |
| m_gpr_is_valid = false; | |
| m_fpu_is_valid = false; | |
| } | |
| uint32_t NativeRegisterContextLinux_riscv64::CalculateFprOffset( | |
| const RegisterInfo *reg_info) const { | |
| return reg_info->byte_offset - GetGPRSize(); | |
| } | |
| std::vector<uint32_t> NativeRegisterContextLinux_riscv64::GetExpeditedRegisters( | |
| ExpeditedRegs expType) const { | |
| std::vector<uint32_t> expedited_reg_nums = | |
| NativeRegisterContext::GetExpeditedRegisters(expType); | |
| return expedited_reg_nums; | |
| } | |
| #endif // defined (__riscv) && __riscv_xlen == 64 |