diff options
6 files changed, 231 insertions, 9 deletions
diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_riscv64.cpp b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_riscv64.cpp index 45b6c8ff9905..22fbb9b52e04 100644 --- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_riscv64.cpp +++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_riscv64.cpp @@ -24,14 +24,54 @@ // System includes - They have to be included after framework includes because // they define some macros which collide with variable names in other modules #include <sys/ptrace.h> +#include <sys/syscall.h> #include <sys/uio.h> +#include <unistd.h> // NT_PRSTATUS and NT_FPREGSET definition #include <elf.h> +#ifndef NT_RISCV_VECTOR +#define NT_RISCV_VECTOR 0x901 +#endif +#ifndef __NR_riscv_hwprobe +#define __NR_riscv_hwprobe 258 +#endif +#ifndef RISCV_HWPROBE_KEY_IMA_EXT_0 +#define RISCV_HWPROBE_KEY_IMA_EXT_0 4 +#endif +#ifndef RISCV_HWPROBE_IMA_V +#define RISCV_HWPROBE_IMA_V (1 << 2) +#endif + +struct HWProbeRISCV { + int64_t key; + uint64_t value; +}; + using namespace lldb; using namespace lldb_private; using namespace lldb_private::process_linux; +static uint64_t GetVLENB() { + struct HWProbeRISCV query = {RISCV_HWPROBE_KEY_IMA_EXT_0, 0}; + if (syscall(__NR_riscv_hwprobe, &query, 1, 0, NULL, 0) != 0) + return 0; + + if ((query.value & RISCV_HWPROBE_IMA_V) == 0) + return 0; + + uint64_t vlenb = 0; + asm volatile("csrr %[vlenb], vlenb" : [vlenb] "=r"(vlenb)); + return vlenb; +} + +static RegisterInfoPOSIX_riscv64::VPR CreateVPRBuffer() { + uint64_t vlenb = GetVLENB(); + if (vlenb > 0) + return RegisterInfoPOSIX_riscv64::VPR(vlenb); + return RegisterInfoPOSIX_riscv64::VPR(); +} + std::unique_ptr<NativeRegisterContextLinux> NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux( const ArchSpec &target_arch, NativeThreadLinux &native_thread) { @@ -52,8 +92,10 @@ NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux( opt_regsets.Set(RegisterInfoPOSIX_riscv64::eRegsetMaskFP); } - auto register_info_up = - std::make_unique<RegisterInfoPOSIX_riscv64>(target_arch, opt_regsets); + uint64_t vlenb = GetVLENB(); + + auto register_info_up = std::make_unique<RegisterInfoPOSIX_riscv64>( + target_arch, opt_regsets, vlenb); return std::make_unique<NativeRegisterContextLinux_riscv64>( target_arch, native_thread, std::move(register_info_up)); } @@ -72,12 +114,13 @@ NativeRegisterContextLinux_riscv64::NativeRegisterContextLinux_riscv64( std::unique_ptr<RegisterInfoPOSIX_riscv64> register_info_up) : NativeRegisterContextRegisterInfo(native_thread, register_info_up.release()), - NativeRegisterContextLinux(native_thread) { + NativeRegisterContextLinux(native_thread), m_vpr(CreateVPRBuffer()) { ::memset(&m_fpr, 0, sizeof(m_fpr)); ::memset(&m_gpr, 0, sizeof(m_gpr)); m_gpr_is_valid = false; m_fpu_is_valid = false; + m_vpr_is_valid = false; } const RegisterInfoPOSIX_riscv64 & @@ -144,6 +187,13 @@ NativeRegisterContextLinux_riscv64::ReadRegister(const RegisterInfo *reg_info, offset = CalculateFprOffset(reg_info); assert(offset < GetFPRSize()); src = (uint8_t *)GetFPRBuffer() + offset; + } else if (IsVPR(reg)) { + error = ReadVPR(); + if (error.Fail()) + return error; + + offset = reg_info->byte_offset; + src = static_cast<uint8_t *>(GetVPRBuffer()) + offset; } else return Status::FromErrorString( "failed - register wasn't recognized to be a GPR or an FPR, " @@ -198,6 +248,16 @@ Status NativeRegisterContextLinux_riscv64::WriteRegister( ::memcpy(dst, reg_value.GetBytes(), reg_info->byte_size); return WriteFPR(); + } else if (IsVPR(reg)) { + error = ReadVPR(); + if (error.Fail()) + return error; + + offset = reg_info->byte_offset; + dst = static_cast<uint8_t *>(GetVPRBuffer()) + offset; + ::memcpy(dst, reg_value.GetBytes(), reg_info->byte_size); + + return WriteVPR(); } return Status::FromErrorString("Failed to write register value"); @@ -219,11 +279,21 @@ Status NativeRegisterContextLinux_riscv64::ReadAllRegisterValues( return error; } + if (GetRegisterInfo().IsVPPresent()) { + error = ReadVPR(); + if (error.Fail()) + return error; + } + uint8_t *dst = const_cast<uint8_t *>(data_sp->GetBytes()); ::memcpy(dst, GetGPRBuffer(), GetGPRSize()); dst += GetGPRSize(); - if (GetRegisterInfo().IsFPPresent()) + if (GetRegisterInfo().IsFPPresent()) { ::memcpy(dst, GetFPRBuffer(), GetFPRSize()); + dst += GetFPRSize(); + } + if (GetRegisterInfo().IsVPPresent()) + ::memcpy(dst, GetVPRBuffer(), GetVPRSize()); return error; } @@ -270,6 +340,16 @@ Status NativeRegisterContextLinux_riscv64::WriteAllRegisterValues( error = WriteFPR(); if (error.Fail()) return error; + + src += GetFPRSize(); + } + + if (GetRegisterInfo().IsVPPresent()) { + ::memcpy(GetVPRBuffer(), src, GetVPRSize()); + + error = WriteVPR(); + if (error.Fail()) + return error; } return error; @@ -279,6 +359,8 @@ size_t NativeRegisterContextLinux_riscv64::GetRegContextSize() { size_t size = GetGPRSize(); if (GetRegisterInfo().IsFPPresent()) size += GetFPRSize(); + if (GetRegisterInfo().IsVPPresent()) + size += GetVPRSize(); return size; } @@ -291,6 +373,10 @@ bool NativeRegisterContextLinux_riscv64::IsFPR(unsigned reg) const { return GetRegisterInfo().IsFPReg(reg); } +bool NativeRegisterContextLinux_riscv64::IsVPR(unsigned reg) const { + return GetRegisterInfo().IsVPReg(reg); +} + Status NativeRegisterContextLinux_riscv64::ReadGPR() { Status error; @@ -355,9 +441,50 @@ Status NativeRegisterContextLinux_riscv64::WriteFPR() { return WriteRegisterSet(&ioVec, GetFPRSize(), NT_FPREGSET); } +Status NativeRegisterContextLinux_riscv64::ReadVPR() { + if (m_vpr_is_valid) + return Status(); + + struct iovec ioVec; + ioVec.iov_base = GetVPRBuffer(); + ioVec.iov_len = GetVPRSize(); + + Status error = ReadRegisterSet(&ioVec, GetVPRSize(), NT_RISCV_VECTOR); + if (error.Fail()) + return error; + + // Additionally check the vlenb value. Due to bugs in early versions of + // RVV support in the Linux kernel, it was possible to obtain an invalid + // vector register context even if the PTRACE_GETREGSET call succeeded. + bool is_valid_ctx = + GetVPRBuffer() && + static_cast<RegisterInfoPOSIX_riscv64::VPR::RawVPR *>(GetVPRBuffer()) + ->vlenb > 0; + if (!is_valid_ctx) + return Status::FromErrorString("Invalid vector register context"); + + m_vpr_is_valid = true; + return Status(); +} + +Status NativeRegisterContextLinux_riscv64::WriteVPR() { + Status error = ReadVPR(); + if (error.Fail()) + return error; + + struct iovec ioVec; + ioVec.iov_base = GetVPRBuffer(); + ioVec.iov_len = GetVPRSize(); + + m_vpr_is_valid = false; + + return WriteRegisterSet(&ioVec, GetVPRSize(), NT_RISCV_VECTOR); +} + void NativeRegisterContextLinux_riscv64::InvalidateAllRegisters() { m_gpr_is_valid = false; m_fpu_is_valid = false; + m_vpr_is_valid = false; } uint32_t NativeRegisterContextLinux_riscv64::CalculateFprOffset( diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_riscv64.h b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_riscv64.h index d5cc50131cdc..8e2e6eec9c2c 100644 --- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_riscv64.h +++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_riscv64.h @@ -59,28 +59,41 @@ protected: Status WriteFPR() override; + Status ReadVPR(); + + Status WriteVPR(); + void *GetGPRBuffer() override { return &m_gpr; } void *GetFPRBuffer() override { return &m_fpr; } + void *GetVPRBuffer() { return m_vpr.GetVPR(); } + size_t GetGPRSize() const override { return GetRegisterInfo().GetGPRSize(); } size_t GetFPRSize() override { return GetRegisterInfo().GetFPRSize(); } + size_t GetVPRSize() { return m_vpr.GetSize(); } + private: bool m_gpr_is_valid; bool m_fpu_is_valid; + bool m_vpr_is_valid; RegisterInfoPOSIX_riscv64::GPR m_gpr; RegisterInfoPOSIX_riscv64::FPR m_fpr; + RegisterInfoPOSIX_riscv64::VPR m_vpr; + size_t GetRegContextSize(); bool IsGPR(unsigned reg) const; bool IsFPR(unsigned reg) const; + bool IsVPR(unsigned reg) const; + uint32_t CalculateFprOffset(const RegisterInfo *reg_info) const; const RegisterInfoPOSIX_riscv64 &GetRegisterInfo() const; diff --git a/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_riscv64.cpp b/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_riscv64.cpp index bbcfb9eae100..4297f45fa0f6 100644 --- a/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_riscv64.cpp +++ b/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_riscv64.cpp @@ -79,3 +79,7 @@ bool RegisterContextPOSIX_riscv64::IsGPR(unsigned int reg) { bool RegisterContextPOSIX_riscv64::IsFPR(unsigned int reg) { return m_register_info_up->IsFPReg(reg); } + +bool RegisterContextPOSIX_riscv64::IsVPR(unsigned int reg) { + return m_register_info_up->IsVPReg(reg); +} diff --git a/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_riscv64.h b/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_riscv64.h index 2431ed6ab8c6..8bacd3f994a6 100644 --- a/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_riscv64.h +++ b/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_riscv64.h @@ -50,6 +50,8 @@ protected: bool IsFPR(unsigned reg); + bool IsVPR(unsigned reg); + size_t GetFPRSize() { return sizeof(RegisterInfoPOSIX_riscv64::FPR); } uint32_t GetRegNumFCSR() const { return fpr_fcsr_riscv; } diff --git a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_riscv64.cpp b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_riscv64.cpp index a711e682c9f1..58413c25fc98 100644 --- a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_riscv64.cpp +++ b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_riscv64.cpp @@ -26,6 +26,9 @@ enum { k_num_gpr_registers = gpr_last_riscv - gpr_first_riscv + 1, k_num_fpr_registers = fpr_last_riscv - fpr_first_riscv + 1, + k_num_vcsr_registers = vcsr_last_riscv - vcsr_first_riscv + 1, + k_num_vpr_registers = vpr_last_riscv - vpr_first_riscv + 1, + k_num_vector_registers = k_num_vcsr_registers + k_num_vpr_registers, k_num_register_sets_default = 1 }; @@ -52,9 +55,12 @@ static const lldb_private::RegisterSet g_reg_set_gpr_riscv64 = { g_gpr_regnums_riscv64}; static const lldb_private::RegisterSet g_reg_set_fpr_riscv64 = { "Floating Point Registers", "fpr", k_num_fpr_registers, nullptr}; +static const lldb_private::RegisterSet g_reg_set_vpr_riscv64 = { + "Vector Purpose Registers", "vpr", k_num_vector_registers, nullptr}; RegisterInfoPOSIX_riscv64::RegisterInfoPOSIX_riscv64( - const lldb_private::ArchSpec &target_arch, lldb_private::Flags opt_regsets) + const lldb_private::ArchSpec &target_arch, lldb_private::Flags opt_regsets, + uint64_t vlenb) : lldb_private::RegisterInfoAndSetInterface(target_arch), m_opt_regsets(opt_regsets) { switch (target_arch.GetMachine()) { @@ -66,6 +72,11 @@ RegisterInfoPOSIX_riscv64::RegisterInfoPOSIX_riscv64( if (m_opt_regsets.AnySet(eRegsetMaskFP)) AddRegSetFP(); + if (vlenb > 0) { + m_opt_regsets.Set(eRegsetMaskVP); + AddRegSetVPR(vlenb); + } + break; } default: @@ -107,6 +118,37 @@ void RegisterInfoPOSIX_riscv64::AddRegSetFP() { std::make_pair(register_info_count, m_register_infos.size()); } +void RegisterInfoPOSIX_riscv64::AddRegSetVPR(uint64_t vlenb) { + assert(vlenb && "Target doesn't support V extension"); + + const uint32_t register_info_count = m_register_infos.size(); + const uint32_t register_set_count = m_register_sets.size(); + + m_register_infos.resize(register_info_count + k_num_vector_registers); + memcpy(&m_register_infos[register_info_count], g_register_infos_riscv64_vpr, + sizeof(g_register_infos_riscv64_vpr)); + + for (uint32_t i = 0; i < k_num_vcsr_registers; i++) + m_vp_regnum_collection.push_back(register_info_count + i); + + // Now we know appropriate vlenb, so update byte offsets and sizes for vector + // registers here + constexpr size_t vcsr_size = sizeof(uint64_t); + for (uint32_t i = 0; i < k_num_vpr_registers; i++) { + uint32_t vpr_info_count = register_info_count + k_num_vcsr_registers + i; + m_register_infos[vpr_info_count].byte_size = vlenb; + m_register_infos[vpr_info_count].byte_offset = + (k_num_vcsr_registers * vcsr_size) + (i * vlenb); + m_vp_regnum_collection.push_back(vpr_info_count); + } + + m_register_sets.push_back(g_reg_set_vpr_riscv64); + m_register_sets.back().registers = m_vp_regnum_collection.data(); + + m_per_regset_regnum_range[register_set_count] = + std::make_pair(register_info_count, m_register_infos.size()); +} + uint32_t RegisterInfoPOSIX_riscv64::GetRegisterCount() const { return m_register_infos.size(); } @@ -142,6 +184,10 @@ bool RegisterInfoPOSIX_riscv64::IsFPReg(unsigned reg) const { return llvm::is_contained(m_fp_regnum_collection, reg); } +bool RegisterInfoPOSIX_riscv64::IsVPReg(unsigned reg) const { + return llvm::is_contained(m_vp_regnum_collection, reg); +} + const lldb_private::RegisterSet * RegisterInfoPOSIX_riscv64::GetRegisterSet(size_t set_index) const { if (set_index < GetRegisterSetCount()) diff --git a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_riscv64.h b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_riscv64.h index f8e22c7df3c8..f55c48ecdf27 100644 --- a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_riscv64.h +++ b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_riscv64.h @@ -24,6 +24,7 @@ public: enum { eRegsetMaskDefault = 0, eRegsetMaskFP = 1, + eRegsetMaskVP = 2, eRegsetMaskAll = -1, }; @@ -37,18 +38,42 @@ public: uint32_t fcsr; }; - struct VPR { - // The size should be VLEN*32 in bits, but we don't have VLEN here. - void *vpr; + class VPR { + public: + // __riscv_v_regset_state from Linux ptrace API + struct RawVPR { + uint64_t vstart; + uint64_t vl; + uint64_t vtype; + uint64_t vcsr; + uint64_t vlenb; + uint8_t v_regs[]; + }; + + VPR() = default; + + VPR(uint64_t vlenb) : m_vpr(sizeof(RawVPR) + 32 * vlenb) { + assert(vlenb && "Target doesn't support V extension!"); + } + + void *GetVPR() { return static_cast<void *>(m_vpr.data()); } + + size_t GetSize() const { return m_vpr.size(); } + + private: + std::vector<uint8_t> m_vpr; }; RegisterInfoPOSIX_riscv64(const lldb_private::ArchSpec &target_arch, - lldb_private::Flags opt_regsets); + lldb_private::Flags opt_regsets, + uint64_t vlenb = 0); void AddRegSetGP(); void AddRegSetFP(); + void AddRegSetVPR(uint64_t vlenb); + size_t GetGPRSize() const override; size_t GetFPRSize() const override; @@ -66,8 +91,12 @@ public: bool IsFPPresent() const { return m_opt_regsets.AnySet(eRegsetMaskFP); } + bool IsVPPresent() const { return m_opt_regsets.AnySet(eRegsetMaskVP); } + bool IsFPReg(unsigned reg) const; + bool IsVPReg(unsigned reg) const; + private: std::vector<lldb_private::RegisterInfo> m_register_infos; @@ -79,6 +108,7 @@ private: // Register collections to be stored as reference for m_register_sets items std::vector<uint32_t> m_fp_regnum_collection; + std::vector<uint32_t> m_vp_regnum_collection; lldb_private::Flags m_opt_regsets; }; |
