//===-- 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 "lldb/Host/HostInfo.h" #include "lldb/Utility/DataBufferHeap.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/RegisterValue.h" #include "lldb/Utility/Status.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" // System includes - They have to be included after framework includes because // they define some macros which collide with variable names in other modules #include #include // NT_PRSTATUS and NT_FPREGSET definition #include using namespace lldb; using namespace lldb_private; using namespace lldb_private::process_linux; std::unique_ptr 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(target_arch, opt_regsets); return std::make_unique( target_arch, native_thread, std::move(register_info_up)); } default: llvm_unreachable("have no register context for architecture"); } } llvm::Expected NativeRegisterContextLinux::DetermineArchitecture(lldb::tid_t tid) { return HostInfo::GetArchitecture(); } NativeRegisterContextLinux_riscv64::NativeRegisterContextLinux_riscv64( const ArchSpec &target_arch, NativeThreadProtocol &native_thread, std::unique_ptr 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( 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 : ""); 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 : ""); 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(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(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 NativeRegisterContextLinux_riscv64::GetExpeditedRegisters( ExpeditedRegs expType) const { std::vector expedited_reg_nums = NativeRegisterContext::GetExpeditedRegisters(expType); return expedited_reg_nums; } #endif // defined (__riscv) && __riscv_xlen == 64