Index: lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux.h =================================================================== --- lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux.h +++ lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux.h @@ -31,6 +31,9 @@ // Invalidates cached values in register context data structures virtual void InvalidateAllRegisters(){} + // Configures register context based on target capabilities + virtual void ConfigureRegisterContext() {} + protected: lldb::ByteOrder GetByteOrder() const; Index: lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h =================================================================== --- lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h +++ lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h @@ -14,11 +14,25 @@ #include "Plugins/Process/Linux/NativeRegisterContextLinux.h" #include "Plugins/Process/Utility/lldb-arm64-register-enums.h" +#include + +#ifndef SVE_PT_REGS_SVE +#define INCLUDE_LINUX_PTRACE_DEFINITIONS_FOR_SVE_ARM64 +#include "Plugins/Process/Linux/LinuxPTraceDefines_arm64sve.h" +#endif + namespace lldb_private { namespace process_linux { class NativeProcessLinux; +enum class SVE_STATE { + SVE_STATE_UNKNOWN, + SVE_STATE_DISABLED, + SVE_STATE_FPSIMD, + SVE_STATE_FULL +}; + class NativeRegisterContextLinux_arm64 : public NativeRegisterContextLinux { public: NativeRegisterContextLinux_arm64(const ArchSpec &target_arch, @@ -42,6 +56,8 @@ void InvalidateAllRegisters() override; + void ConfigureRegisterContext() override; + // Hardware breakpoints/watchpoint management functions uint32_t NumSupportedHardwareBreakpoints() override; @@ -88,17 +104,34 @@ Status WriteFPR() override; + Status ReadAllSVE(); + + Status WriteAllSVE(); + + Status ReadSVEHeader(); + + Status WriteSVEHeader(); + void *GetGPRBuffer() override { return &m_gpr_arm64; } void *GetFPRBuffer() override { return &m_fpr; } size_t GetFPRSize() override { return sizeof(m_fpr); } + void *GetSVEHeader() { return &m_sve_header; } + + void *GetSVEBuffer(); + + size_t GetSVEHeaderSize() { return sizeof(m_sve_header); } + + size_t GetSVEBufferSize() { return m_sve_ptrace_payload.size(); } + private: struct RegInfo { uint32_t num_registers; uint32_t num_gpr_registers; uint32_t num_fpr_registers; + uint32_t num_sve_registers; uint32_t last_gpr; uint32_t first_fpr; @@ -107,6 +140,9 @@ uint32_t first_fpr_v; uint32_t last_fpr_v; + uint32_t first_sve; + uint32_t last_sve; + uint32_t gpr_flags; }; @@ -131,11 +167,20 @@ bool m_gpr_is_valid; bool m_fpu_is_valid; + bool m_sve_buffer_is_valid; + + bool m_sve_header_is_valid; + bool m_sve_update_reg_infos; GPR m_gpr_arm64; // 64-bit general purpose registers. + RegInfo m_reg_info; FPU m_fpr; // floating-point registers including extended register sets. + mutable SVE_STATE m_sve_state; + struct user_sve_header m_sve_header; + std::vector m_sve_ptrace_payload; + // Debug register info for hardware breakpoints and watchpoints management. struct DREG { lldb::addr_t address; // Breakpoint/watchpoint address value. @@ -157,11 +202,25 @@ bool IsFPR(unsigned reg) const; + bool IsSVE(unsigned reg) const; + + bool IsSVEZReg(unsigned reg) const; + + bool IsSVEPReg(unsigned reg) const; + + bool IsSVERegVG(unsigned reg) const; + + uint64_t GetSVERegVG() { return m_sve_header.vl / 8; } + + void SetSVERegVG(uint64_t vg) { m_sve_header.vl = vg * 8; } + Status ReadHardwareDebugInfo(); Status WriteHardwareDebugRegs(int hwbType); uint32_t CalculateFprOffset(const RegisterInfo *reg_info) const; + + uint32_t CalculateSVEOffset(uint32_t reg_num) const; }; } // namespace process_linux Index: lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp =================================================================== --- lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp +++ lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp @@ -28,8 +28,10 @@ #include // NT_PRSTATUS and NT_FPREGSET definition #include -// user_hwdebug_state definition -#include + +#ifndef NT_ARM_SVE +#define NT_ARM_SVE 0x405 /* ARM Scalable Vector Extension */ +#endif #define REG_CONTEXT_SIZE (GetGPRSize() + GetFPRSize()) @@ -95,9 +97,34 @@ 1) == k_num_fpr_registers_arm64, "g_fpu_regnums_arm64 has wrong number of register infos"); +// ARM64 SVE registers. +static const uint32_t g_sve_regnums_arm64[] = { + sve_vg_arm64, + + sve_z0_arm64, sve_z1_arm64, sve_z2_arm64, sve_z3_arm64, + sve_z4_arm64, sve_z5_arm64, sve_z6_arm64, sve_z7_arm64, + sve_z8_arm64, sve_z9_arm64, sve_z10_arm64, sve_z11_arm64, + sve_z12_arm64, sve_z13_arm64, sve_z14_arm64, sve_z15_arm64, + sve_z16_arm64, sve_z17_arm64, sve_z18_arm64, sve_z19_arm64, + sve_z20_arm64, sve_z21_arm64, sve_z22_arm64, sve_z23_arm64, + sve_z24_arm64, sve_z25_arm64, sve_z26_arm64, sve_z27_arm64, + sve_z28_arm64, sve_z29_arm64, sve_z30_arm64, sve_z31_arm64, + + sve_p0_arm64, sve_p1_arm64, sve_p2_arm64, sve_p3_arm64, + sve_p4_arm64, sve_p5_arm64, sve_p6_arm64, sve_p7_arm64, + sve_p8_arm64, sve_p9_arm64, sve_p10_arm64, sve_p11_arm64, + sve_p12_arm64, sve_p13_arm64, sve_p14_arm64, sve_p15_arm64, + + sve_ffr_arm64, + LLDB_INVALID_REGNUM // register sets need to end with this flag +}; +static_assert(((sizeof g_sve_regnums_arm64 / sizeof g_sve_regnums_arm64[0]) - + 1) == k_num_sve_registers_arm64, + "g_sve_regnums_arm64 has wrong number of register infos"); + namespace { // Number of register sets provided by this context. -enum { k_num_register_sets = 2 }; +enum { k_num_register_sets = 3 }; } // Register sets for ARM64. @@ -105,7 +132,8 @@ {"General Purpose Registers", "gpr", k_num_gpr_registers_arm64, g_gpr_regnums_arm64}, {"Floating Point Registers", "fpu", k_num_fpr_registers_arm64, - g_fpu_regnums_arm64}}; + g_fpu_regnums_arm64}, + {"SVE Registers", "sve", k_num_sve_registers_arm64, g_sve_regnums_arm64}}; std::unique_ptr NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux( @@ -131,12 +159,16 @@ m_reg_info.num_registers = k_num_registers_arm64; m_reg_info.num_gpr_registers = k_num_gpr_registers_arm64; m_reg_info.num_fpr_registers = k_num_fpr_registers_arm64; + m_reg_info.num_sve_registers = k_num_sve_registers_arm64; + m_reg_info.last_gpr = k_last_gpr_arm64; m_reg_info.first_fpr = k_first_fpr_arm64; m_reg_info.last_fpr = k_last_fpr_arm64; m_reg_info.first_fpr_v = fpu_v0_arm64; m_reg_info.last_fpr_v = fpu_v31_arm64; m_reg_info.gpr_flags = gpr_cpsr_arm64; + m_reg_info.first_sve = sve_vg_arm64; + m_reg_info.last_sve = sve_ffr_arm64; break; default: llvm_unreachable("Unhandled target architecture."); @@ -147,6 +179,7 @@ ::memset(&m_gpr_arm64, 0, sizeof(m_gpr_arm64)); ::memset(&m_hwp_regs, 0, sizeof(m_hwp_regs)); ::memset(&m_hbr_regs, 0, sizeof(m_hbr_regs)); + ::memset(&m_sve_header, 0, sizeof(m_sve_header)); // 16 is just a maximum value, query hardware for actual watchpoint count m_max_hwp_supported = 16; @@ -155,25 +188,39 @@ m_gpr_is_valid = false; m_fpu_is_valid = false; + m_sve_buffer_is_valid = false; + + m_sve_header_is_valid = false; + m_sve_update_reg_infos = true; + + // SVE is not enabled until we query user_sve_header + m_sve_state = SVE_STATE::SVE_STATE_UNKNOWN; } uint32_t NativeRegisterContextLinux_arm64::GetRegisterSetCount() const { - return k_num_register_sets; + if (m_sve_state == SVE_STATE::SVE_STATE_FPSIMD || + m_sve_state == SVE_STATE::SVE_STATE_FULL) + + return k_num_register_sets; + else + return k_num_register_sets - 1; } const RegisterSet * NativeRegisterContextLinux_arm64::GetRegisterSet(uint32_t set_index) const { - if (set_index < k_num_register_sets) + if (set_index < GetRegisterSetCount()) return &g_reg_sets_arm64[set_index]; return nullptr; } uint32_t NativeRegisterContextLinux_arm64::GetUserRegisterCount() const { - uint32_t count = 0; - for (uint32_t set_index = 0; set_index < k_num_register_sets; ++set_index) - count += g_reg_sets_arm64[set_index].num_registers; - return count; + if (m_sve_state == SVE_STATE::SVE_STATE_FPSIMD || + m_sve_state == SVE_STATE::SVE_STATE_FULL) + return k_num_gpr_registers_arm64 + k_num_fpr_registers_arm64 + + k_num_sve_registers_arm64; + + return k_num_gpr_registers_arm64 + k_num_fpr_registers_arm64; } Status @@ -195,6 +242,8 @@ uint8_t *src; uint32_t offset; + uint64_t sve_vg; + std::vector sve_reg_non_live; if (IsGPR(reg)) { if (!m_gpr_is_valid) { @@ -208,15 +257,70 @@ src = (uint8_t *)GetGPRBuffer() + offset; } else if (IsFPR(reg)) { - if (!m_fpu_is_valid) { + if (m_sve_state == SVE_STATE::SVE_STATE_DISABLED) { + // SVE is disabled take legacy route for FPU register access + if (!m_fpu_is_valid) { - error = ReadFPR(); - if (error.Fail()) - return error; + error = ReadFPR(); + if (error.Fail()) + return error; + } + offset = CalculateFprOffset(reg_info); + assert(offset < GetFPRSize()); + src = (uint8_t *)GetFPRBuffer() + offset; + } else { + // SVE enabled, we will read and cache SVE ptrace data + if (!m_sve_buffer_is_valid) { + error = ReadAllSVE(); + if (error.Fail()) + return error; + } + // Extract SVE Z register value register number for this reg_info + uint32_t sve_reg_num = LLDB_INVALID_REGNUM; + if (reg_info->value_regs && + reg_info->value_regs[0] != LLDB_INVALID_REGNUM) + sve_reg_num = reg_info->value_regs[0]; + else if (reg == fpu_fpcr_arm64 || reg == fpu_fpsr_arm64) + sve_reg_num = reg; + if (sve_reg_num != LLDB_INVALID_REGNUM) { + offset = CalculateSVEOffset(sve_reg_num); + assert(offset < GetSVEBufferSize()); + src = (uint8_t *)GetSVEBuffer() + offset; + } + } + } else if (IsSVERegVG(reg)) { + + sve_vg = GetSVERegVG(); + src = (uint8_t *)&sve_vg; + + } else if (IsSVE(reg)) { + if (m_sve_state == SVE_STATE::SVE_STATE_DISABLED) { + return Status("SVE disabled or not supported"); + } else { + // SVE enabled, we will read and cache SVE ptrace data + if (!m_sve_buffer_is_valid) { + error = ReadAllSVE(); + if (error.Fail()) + return error; + } + if (m_sve_state == SVE_STATE::SVE_STATE_FPSIMD) { + sve_reg_non_live.resize(reg_info->byte_size, 0); + // In FPSIMD state SVE payload mirrors legacy fpsimd struct and so just + // copy 16 bytes of v register to the start of z register. All other + // SVE register will be set to zero. + if (IsSVEZReg(reg)) { + offset = CalculateSVEOffset(reg); + assert(offset < GetSVEBufferSize()); + ::memcpy(sve_reg_non_live.data(), (uint8_t *)GetSVEBuffer() + offset, + 16); + } + src = sve_reg_non_live.data(); + } else if (m_sve_state == SVE_STATE::SVE_STATE_FULL) { + offset = CalculateSVEOffset(reg); + assert(offset < GetSVEBufferSize()); + src = (uint8_t *)GetSVEBuffer() + offset; + } } - offset = CalculateFprOffset(reg_info); - assert(offset < GetFPRSize()); - src = (uint8_t *)GetFPRBuffer() + offset; } else return Status("failed - register wasn't recognized to be a GPR or an FPR, " "write strategy unknown"); @@ -243,6 +347,7 @@ uint8_t *dst; uint32_t offset; + std::vector sve_reg_non_live; if (IsGPR(reg)) { if (!m_gpr_is_valid) { @@ -259,20 +364,96 @@ return WriteGPR(); } else if (IsFPR(reg)) { - if (!m_fpu_is_valid) { - error = ReadFPR(); - if (error.Fail()) - return error; + if (m_sve_state == SVE_STATE::SVE_STATE_DISABLED) { + // SVE is disabled take legacy route for FPU register access + if (!m_fpu_is_valid) { + 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(); + } else { + // SVE enabled, we will read and cache SVE ptrace data + if (!m_sve_buffer_is_valid) { + error = ReadAllSVE(); + if (error.Fail()) + return error; + } + // Extract SVE Z register value register number for this reg_info + uint32_t sve_reg_num = LLDB_INVALID_REGNUM; + if (reg_info->value_regs && + reg_info->value_regs[0] != LLDB_INVALID_REGNUM) + sve_reg_num = reg_info->value_regs[0]; + else if (reg == fpu_fpcr_arm64 || reg == fpu_fpsr_arm64) + sve_reg_num = reg; + if (sve_reg_num != LLDB_INVALID_REGNUM) { + offset = CalculateSVEOffset(sve_reg_num); + assert(offset < GetSVEBufferSize()); + dst = (uint8_t *)GetSVEBuffer() + offset; + ::memcpy(dst, reg_value.GetBytes(), reg_info->byte_size); + return WriteAllSVE(); + } } - offset = CalculateFprOffset(reg_info); - assert(offset < GetFPRSize()); - dst = (uint8_t *)GetFPRBuffer() + offset; - - ::memcpy(dst, reg_value.GetBytes(), reg_info->byte_size); + } else if (IsSVERegVG(reg)) { + return Status("SVE state change operation not supported"); + } else if (IsSVE(reg)) { + if (m_sve_state == SVE_STATE::SVE_STATE_DISABLED) { + return Status("SVE disabled or not supported"); + } else { + // Target has SVE enabled, we will read and cache SVE ptrace data + if (!m_sve_buffer_is_valid) { + error = ReadAllSVE(); + if (error.Fail()) + return error; + } - return WriteFPR(); + // If target supports SVE but currently in FPSIMD mode. + if (m_sve_state == SVE_STATE::SVE_STATE_FPSIMD) { + // Here we will check if writing this SVE register enables + // SVE_STATE_FULL + bool set_sve_state_full = false; + const uint8_t *reg_bytes = (const uint8_t *)reg_value.GetBytes(); + if (IsSVEZReg(reg)) { + for (uint32_t i = 16; i < reg_info->byte_size; i++) { + if (reg_bytes[i]) { + set_sve_state_full = true; + break; + } + } + } else if (IsSVEPReg(reg) || reg == sve_ffr_arm64) { + for (uint32_t i = 0; i < reg_info->byte_size; i++) { + if (reg_bytes[i]) { + set_sve_state_full = true; + break; + } + } + } + if (set_sve_state_full) { + return Status("SVE state change operation not supported"); + } else if (!set_sve_state_full && IsSVEZReg(reg)) { + // We are writing a Z register which is zero beyond 16 bytes so copy + // first 16 bytes only as SVE payload mirrors legacy fpsimd structure + offset = CalculateSVEOffset(reg); + assert(offset < GetSVEBufferSize()); + dst = (uint8_t *)GetSVEBuffer() + offset; + ::memcpy(dst, reg_value.GetBytes(), 16); + return WriteAllSVE(); + } + } else if (m_sve_state == SVE_STATE::SVE_STATE_FULL) { + offset = CalculateSVEOffset(reg); + assert(offset < GetSVEBufferSize()); + dst = (uint8_t *)GetSVEBuffer() + offset; + ::memcpy(dst, reg_value.GetBytes(), reg_info->byte_size); + return WriteAllSVE(); + } + } } - return error; } @@ -351,6 +532,22 @@ return (m_reg_info.first_fpr <= reg && reg <= m_reg_info.last_fpr); } +bool NativeRegisterContextLinux_arm64::IsSVE(unsigned reg) const { + return (m_reg_info.first_sve <= reg && reg <= m_reg_info.last_sve); +} + +bool NativeRegisterContextLinux_arm64::IsSVEZReg(unsigned reg) const { + return (sve_z0_arm64 <= reg && reg <= sve_z31_arm64); +} + +bool NativeRegisterContextLinux_arm64::IsSVEPReg(unsigned reg) const { + return (sve_p0_arm64 <= reg && reg <= sve_p15_arm64); +} + +bool NativeRegisterContextLinux_arm64::IsSVERegVG(unsigned reg) const { + return (m_reg_info.first_sve == reg); +} + uint32_t NativeRegisterContextLinux_arm64::NumSupportedHardwareBreakpoints() { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_BREAKPOINTS)); @@ -770,138 +967,256 @@ return m_hwp_regs[wp_index].hit_addr; else return LLDB_INVALID_ADDRESS; -} - -Status NativeRegisterContextLinux_arm64::ReadHardwareDebugInfo() { - if (!m_refresh_hwdebug_info) { - return Status(); } - ::pid_t tid = m_thread.GetID(); + Status NativeRegisterContextLinux_arm64::ReadHardwareDebugInfo() { + if (!m_refresh_hwdebug_info) { + return Status(); + } - int regset = NT_ARM_HW_WATCH; - struct iovec ioVec; - struct user_hwdebug_state dreg_state; - Status error; + ::pid_t tid = m_thread.GetID(); - ioVec.iov_base = &dreg_state; - ioVec.iov_len = sizeof(dreg_state); - error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, tid, ®set, - &ioVec, ioVec.iov_len); + int regset = NT_ARM_HW_WATCH; + struct iovec ioVec; + struct user_hwdebug_state dreg_state; + Status error; + + ioVec.iov_base = &dreg_state; + ioVec.iov_len = sizeof(dreg_state); + error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, tid, ®set, + &ioVec, ioVec.iov_len); + + if (error.Fail()) + return error; + + m_max_hwp_supported = dreg_state.dbg_info & 0xff; + + regset = NT_ARM_HW_BREAK; + error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, tid, ®set, + &ioVec, ioVec.iov_len); + + if (error.Fail()) + return error; + + m_max_hbp_supported = dreg_state.dbg_info & 0xff; + m_refresh_hwdebug_info = false; - if (error.Fail()) return error; + } - m_max_hwp_supported = dreg_state.dbg_info & 0xff; + Status NativeRegisterContextLinux_arm64::WriteHardwareDebugRegs(int hwbType) { + struct iovec ioVec; + struct user_hwdebug_state dreg_state; + Status error; - regset = NT_ARM_HW_BREAK; - error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, tid, ®set, - &ioVec, ioVec.iov_len); + memset(&dreg_state, 0, sizeof(dreg_state)); + ioVec.iov_base = &dreg_state; + + if (hwbType == eDREGTypeWATCH) { + hwbType = NT_ARM_HW_WATCH; + ioVec.iov_len = sizeof(dreg_state.dbg_info) + sizeof(dreg_state.pad) + + (sizeof(dreg_state.dbg_regs[0]) * m_max_hwp_supported); + + for (uint32_t i = 0; i < m_max_hwp_supported; i++) { + dreg_state.dbg_regs[i].addr = m_hwp_regs[i].address; + dreg_state.dbg_regs[i].ctrl = m_hwp_regs[i].control; + } + } else { + hwbType = NT_ARM_HW_BREAK; + ioVec.iov_len = sizeof(dreg_state.dbg_info) + sizeof(dreg_state.pad) + + (sizeof(dreg_state.dbg_regs[0]) * m_max_hbp_supported); + + for (uint32_t i = 0; i < m_max_hbp_supported; i++) { + dreg_state.dbg_regs[i].addr = m_hbr_regs[i].address; + dreg_state.dbg_regs[i].ctrl = m_hbr_regs[i].control; + } + } + + return NativeProcessLinux::PtraceWrapper(PTRACE_SETREGSET, m_thread.GetID(), + &hwbType, &ioVec, ioVec.iov_len); + } + + Status NativeRegisterContextLinux_arm64::ReadGPR() { + Status 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; - if (error.Fail()) return error; + } - m_max_hbp_supported = dreg_state.dbg_info & 0xff; - m_refresh_hwdebug_info = false; + Status NativeRegisterContextLinux_arm64::WriteGPR() { + struct iovec ioVec; - return error; -} + m_gpr_is_valid = false; -Status NativeRegisterContextLinux_arm64::WriteHardwareDebugRegs(int hwbType) { - struct iovec ioVec; - struct user_hwdebug_state dreg_state; - Status error; + ioVec.iov_base = GetGPRBuffer(); + ioVec.iov_len = GetGPRSize(); - memset(&dreg_state, 0, sizeof(dreg_state)); - ioVec.iov_base = &dreg_state; + return WriteRegisterSet(&ioVec, GetGPRSize(), NT_PRSTATUS); + } - if (hwbType == eDREGTypeWATCH) { - hwbType = NT_ARM_HW_WATCH; - ioVec.iov_len = sizeof(dreg_state.dbg_info) + sizeof(dreg_state.pad) + - (sizeof(dreg_state.dbg_regs[0]) * m_max_hwp_supported); + Status NativeRegisterContextLinux_arm64::ReadFPR() { + Status error; - for (uint32_t i = 0; i < m_max_hwp_supported; i++) { - dreg_state.dbg_regs[i].addr = m_hwp_regs[i].address; - dreg_state.dbg_regs[i].ctrl = m_hwp_regs[i].control; - } - } else { - hwbType = NT_ARM_HW_BREAK; - ioVec.iov_len = sizeof(dreg_state.dbg_info) + sizeof(dreg_state.pad) + - (sizeof(dreg_state.dbg_regs[0]) * m_max_hbp_supported); - - for (uint32_t i = 0; i < m_max_hbp_supported; i++) { - dreg_state.dbg_regs[i].addr = m_hbr_regs[i].address; - dreg_state.dbg_regs[i].ctrl = m_hbr_regs[i].control; - } + 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; } - return NativeProcessLinux::PtraceWrapper(PTRACE_SETREGSET, m_thread.GetID(), - &hwbType, &ioVec, ioVec.iov_len); -} + Status NativeRegisterContextLinux_arm64::WriteFPR() { + struct iovec ioVec; -Status NativeRegisterContextLinux_arm64::ReadGPR() { - Status error; + m_fpu_is_valid = false; - struct iovec ioVec; + ioVec.iov_base = GetFPRBuffer(); + ioVec.iov_len = GetFPRSize(); - ioVec.iov_base = GetGPRBuffer(); - ioVec.iov_len = GetGPRSize(); + return WriteRegisterSet(&ioVec, GetFPRSize(), NT_FPREGSET); + } - error = ReadRegisterSet(&ioVec, GetGPRSize(), NT_PRSTATUS); + Status NativeRegisterContextLinux_arm64::ReadSVEHeader() { + Status error; - if (error.Success()) - m_gpr_is_valid = true; + struct iovec ioVec; - return error; -} + ioVec.iov_base = GetSVEHeader(); + ioVec.iov_len = GetSVEHeaderSize(); -Status NativeRegisterContextLinux_arm64::WriteGPR() { - struct iovec ioVec; + error = ReadRegisterSet(&ioVec, GetSVEHeaderSize(), NT_ARM_SVE); - m_gpr_is_valid = false; + m_sve_header_is_valid = true; - ioVec.iov_base = GetGPRBuffer(); - ioVec.iov_len = GetGPRSize(); + return error; + } - return WriteRegisterSet(&ioVec, GetGPRSize(), NT_PRSTATUS); -} + Status NativeRegisterContextLinux_arm64::WriteSVEHeader() { + Status error; -Status NativeRegisterContextLinux_arm64::ReadFPR() { - Status error; + struct iovec ioVec; - struct iovec ioVec; + ioVec.iov_base = GetSVEHeader(); + ioVec.iov_len = GetSVEHeaderSize(); - ioVec.iov_base = GetFPRBuffer(); - ioVec.iov_len = GetFPRSize(); + m_sve_buffer_is_valid = false; + m_sve_header_is_valid = false; - error = ReadRegisterSet(&ioVec, GetFPRSize(), NT_FPREGSET); + return WriteRegisterSet(&ioVec, GetSVEHeaderSize(), NT_ARM_SVE); + } - if (error.Success()) - m_fpu_is_valid = true; + Status NativeRegisterContextLinux_arm64::ReadAllSVE() { + Status error; - return error; -} + struct iovec ioVec; -Status NativeRegisterContextLinux_arm64::WriteFPR() { - struct iovec ioVec; + ioVec.iov_base = GetSVEBuffer(); + ioVec.iov_len = GetSVEBufferSize(); - m_fpu_is_valid = false; + error = ReadRegisterSet(&ioVec, GetSVEBufferSize(), NT_ARM_SVE); - ioVec.iov_base = GetFPRBuffer(); - ioVec.iov_len = GetFPRSize(); + if (error.Success()) + m_sve_buffer_is_valid = true; - return WriteRegisterSet(&ioVec, GetFPRSize(), NT_FPREGSET); -} + return error; + } -void NativeRegisterContextLinux_arm64::InvalidateAllRegisters() { - m_gpr_is_valid = false; - m_fpu_is_valid = false; -} + Status NativeRegisterContextLinux_arm64::WriteAllSVE() { + Status error; -uint32_t NativeRegisterContextLinux_arm64::CalculateFprOffset( - const RegisterInfo *reg_info) const { - return reg_info->byte_offset - - GetRegisterInfoAtIndex(m_reg_info.first_fpr)->byte_offset; -} + struct iovec ioVec; + + ioVec.iov_base = GetSVEBuffer(); + ioVec.iov_len = GetSVEBufferSize(); + + m_sve_buffer_is_valid = false; + m_sve_header_is_valid = false; + + return WriteRegisterSet(&ioVec, GetSVEBufferSize(), NT_ARM_SVE); + } + + void NativeRegisterContextLinux_arm64::InvalidateAllRegisters() { + m_gpr_is_valid = false; + m_fpu_is_valid = false; + m_sve_buffer_is_valid = false; + m_sve_header_is_valid = false; + } + + void NativeRegisterContextLinux_arm64::ConfigureRegisterContext() { + if (!m_sve_header_is_valid) { + Status error = ReadSVEHeader(); + + if (error.Success()) { + if ((m_sve_header.flags & SVE_PT_REGS_MASK) == SVE_PT_REGS_FPSIMD) + m_sve_state = SVE_STATE::SVE_STATE_FPSIMD; + else if ((m_sve_header.flags & SVE_PT_REGS_MASK) == SVE_PT_REGS_SVE) + m_sve_state = SVE_STATE::SVE_STATE_FULL; + + if (sve_vl_valid(m_sve_header.vl)) { + size_t vq = sve_vq_from_vl(m_sve_header.vl); + SetRegisterInfoMode(vq); + m_reg_info.num_registers = k_num_registers_arm64_sve; + m_sve_ptrace_payload.resize(SVE_PT_SIZE(vq, SVE_PT_REGS_SVE)); + } else { + m_sve_state = SVE_STATE::SVE_STATE_DISABLED; + SetRegisterInfoMode(eRegisterInfoModeAArch64); + } + } else { + m_sve_state = SVE_STATE::SVE_STATE_DISABLED; + SetRegisterInfoMode(eRegisterInfoModeAArch64); + } + } + } + + uint32_t NativeRegisterContextLinux_arm64::CalculateFprOffset( + const RegisterInfo *reg_info) const { + return reg_info->byte_offset - + GetRegisterInfoAtIndex(m_reg_info.first_fpr)->byte_offset; + } + + uint32_t + NativeRegisterContextLinux_arm64::CalculateSVEOffset(uint32_t reg_num) const { + if (m_sve_state == SVE_STATE::SVE_STATE_FPSIMD) { + if (IsSVEZReg(reg_num)) + return (reg_num - sve_z0_arm64) * 16; + else if (reg_num == fpu_fpsr_arm64) + return 32 * 16; + else if (reg_num == fpu_fpcr_arm64) + return (32 * 16) + 4; + } else if (m_sve_state == SVE_STATE::SVE_STATE_FULL) { + size_t vq = sve_vq_from_vl(m_sve_header.vl); + if (IsSVEZReg(reg_num)) + return SVE_PT_SVE_ZREG_OFFSET(vq, reg_num - sve_z0_arm64); + else if (IsSVEPReg(reg_num)) + return SVE_PT_SVE_PREG_OFFSET(vq, reg_num - sve_p0_arm64); + else if (reg_num == sve_ffr_arm64) + return SVE_PT_SVE_FFR_OFFSET(vq); + else if (reg_num == fpu_fpsr_arm64) + return SVE_PT_SVE_FPSR_OFFSET(vq); + else if (reg_num == fpu_fpcr_arm64) + return SVE_PT_SVE_FPCR_OFFSET(vq); + } + return 0; + } + + void *NativeRegisterContextLinux_arm64::GetSVEBuffer() { + if (m_sve_state == SVE_STATE::SVE_STATE_FPSIMD) + return m_sve_ptrace_payload.data() + SVE_PT_FPSIMD_OFFSET; + return m_sve_ptrace_payload.data(); + } #endif // defined (__arm64__) || defined (__aarch64__) Index: lldb/source/Plugins/Process/Linux/NativeThreadLinux.h =================================================================== --- lldb/source/Plugins/Process/Linux/NativeThreadLinux.h +++ lldb/source/Plugins/Process/Linux/NativeThreadLinux.h @@ -39,6 +39,9 @@ std::string &description) override; NativeRegisterContextLinux &GetRegisterContext() override { + if (m_reg_context_up && IsStopped(nullptr)) + m_reg_context_up->ConfigureRegisterContext(); + return *m_reg_context_up; } Index: lldb/test/API/commands/register/register/aarch64_sve_registers/Makefile =================================================================== --- /dev/null +++ lldb/test/API/commands/register/register/aarch64_sve_registers/Makefile @@ -0,0 +1,5 @@ +C_SOURCES := main.c + +CFLAGS_EXTRAS := -march=armv8-a+sve + +include Makefile.rules Index: lldb/test/API/commands/register/register/aarch64_sve_registers/TestSVERegisters.py =================================================================== --- /dev/null +++ lldb/test/API/commands/register/register/aarch64_sve_registers/TestSVERegisters.py @@ -0,0 +1,129 @@ +""" +Test the AArch64 SVE registers. +""" + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + +class RegisterCommandsTestCase(TestBase): + + def check_sve_register_size(self, set, name, expected): + reg_value = set.GetChildMemberWithName(name) + self.assertTrue(reg_value.IsValid(), + 'Verify we have a register named "%s"' % (name)) + self.assertEqual(reg_value.GetByteSize(), expected, + 'Verify "%s" == %i' % (name, expected)) + + mydir = TestBase.compute_mydir(__file__) + @skipIf + def test_sve_registers_configuration(self): + """Test AArch64 SVE registers size configuration.""" + self.build() + self.line = line_number('main.c', '// Set a break point here.') + + exe = self.getBuildArtifact("a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line(self, "main.c", self.line, num_expected_locations=1) + self.runCmd("run", RUN_SUCCEEDED) + + self.expect("thread backtrace", STOPPED_DUE_TO_BREAKPOINT, + substrs = ["stop reason = breakpoint 1."]) + + target = self.dbg.GetSelectedTarget() + process = target.GetProcess() + thread = process.GetThreadAtIndex(0) + currentFrame = thread.GetFrameAtIndex(0) + + has_sve = False + for registerSet in currentFrame.GetRegisters(): + if 'sve registers' in registerSet.GetName().lower(): + has_sve = True + + if not has_sve: + self.skipTest('SVE registers must be supported.') + + registerSets = process.GetThreadAtIndex(0).GetFrameAtIndex(0).GetRegisters() + + sve_registers = registerSets.GetValueAtIndex(2) + + vg_reg = sve_registers.GetChildMemberWithName("vg") + + vg_reg_value = sve_registers.GetChildMemberWithName("vg").GetValueAsUnsigned() + + z_reg_size = vg_reg_value * 8 + + p_reg_size = z_reg_size / 8 + + for i in range(32): + self.check_sve_register_size(sve_registers, 'z%i' % (i), z_reg_size) + + for i in range(16): + self.check_sve_register_size(sve_registers, 'p%i' % (i), p_reg_size) + + self.check_sve_register_size(sve_registers, 'ffr', p_reg_size) + + mydir = TestBase.compute_mydir(__file__) + @no_debug_info_test + @skipIf + def test_sve_registers_read_write(self): + """Test AArch64 SVE registers read and write.""" + self.build() + self.line = line_number('main.c', '// Set a break point here.') + + exe = self.getBuildArtifact("a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line(self, "main.c", self.line, num_expected_locations=1) + self.runCmd("run", RUN_SUCCEEDED) + + self.expect("thread backtrace", STOPPED_DUE_TO_BREAKPOINT, + substrs = ["stop reason = breakpoint 1."]) + + target = self.dbg.GetSelectedTarget() + process = target.GetProcess() + thread = process.GetThreadAtIndex(0) + currentFrame = thread.GetFrameAtIndex(0) + + has_sve = False + for registerSet in currentFrame.GetRegisters(): + if 'sve registers' in registerSet.GetName().lower(): + has_sve = True + + if not has_sve: + self.skipTest('SVE registers must be supported.') + + registerSets = process.GetThreadAtIndex(0).GetFrameAtIndex(0).GetRegisters() + + sve_registers = registerSets.GetValueAtIndex(2) + + vg_reg = sve_registers.GetChildMemberWithName("vg") + + vg_reg_value = sve_registers.GetChildMemberWithName("vg").GetValueAsUnsigned() + + z_reg_size = vg_reg_value * 8 + + p_reg_size = int(z_reg_size / 8) + + z_regs_value = '{' + ' '.join(('0x9d' for _ in range(z_reg_size))) + '}' + + p_regs_value = '{' + ' '.join(('0xee' for _ in range(p_reg_size))) + '}' + + for i in range(32): + self.runCmd('register write ' + 'z%i' % (i) + " '" + z_regs_value + "'") + + for i in range(32): + self.expect("register read " + 'z%i' % (i), substrs = [z_regs_value]) + + for i in range(16): + self.runCmd('register write ' + 'p%i' % (i) + " '" + p_regs_value + "'") + + for i in range(16): + self.expect("register read " + 'p%i' % (i), substrs = [p_regs_value]) + + self.runCmd('register write ' + 'ffr ' + "'" + p_regs_value + "'") + + self.expect("register read " + 'ffr', substrs = [p_regs_value]) + Index: lldb/test/API/commands/register/register/aarch64_sve_registers/main.c =================================================================== --- /dev/null +++ lldb/test/API/commands/register/register/aarch64_sve_registers/main.c @@ -0,0 +1,5 @@ +int main() { + asm volatile("ptrue p0.s\n\t"); + asm volatile("fcpy z0.s, p0/m, #5.00000000\n\t"); + return 0; // Set a break point here. +} \ No newline at end of file