diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h --- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h +++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h @@ -14,6 +14,8 @@ #include "Plugins/Process/Linux/NativeRegisterContextLinux.h" #include "Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h" +#include + namespace lldb_private { namespace process_linux { @@ -97,11 +99,19 @@ private: bool m_gpr_is_valid; bool m_fpu_is_valid; + bool m_sve_buffer_is_valid; + + bool m_sve_header_is_valid; RegisterInfoPOSIX_arm64::GPR m_gpr_arm64; // 64-bit general purpose registers. RegisterInfoPOSIX_arm64::FPU m_fpr; // floating-point registers including extended register sets. + + SVEState 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. @@ -123,6 +133,28 @@ bool IsFPR(unsigned reg) const; + Status ReadAllSVE(); + + Status WriteAllSVE(); + + Status ReadSVEHeader(); + + Status WriteSVEHeader(); + + bool IsSVE(unsigned reg) const; + + uint64_t GetSVERegVG() { return m_sve_header.vl / 8; } + + void SetSVERegVG(uint64_t vg) { m_sve_header.vl = vg * 8; } + + 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(); } + Status ReadHardwareDebugInfo(); Status WriteHardwareDebugRegs(int hwbType); @@ -130,6 +162,10 @@ uint32_t CalculateFprOffset(const RegisterInfo *reg_info) const; RegisterInfoPOSIX_arm64 &GetRegisterInfo() const; + + void ConfigureRegisterContext(); + + uint32_t CalculateSVEOffset(const RegisterInfo *reg_info) const; }; } // namespace process_linux diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp --- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp +++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp @@ -21,14 +21,17 @@ #include "Plugins/Process/Linux/NativeProcessLinux.h" #include "Plugins/Process/Linux/Procfs.h" #include "Plugins/Process/POSIX/ProcessPOSIXLog.h" +#include "Plugins/Process/Utility/RegisterInfoPOSIX_arm64.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 // 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()) @@ -59,14 +62,21 @@ ::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; m_max_hbp_supported = 16; + m_refresh_hwdebug_info = true; m_gpr_is_valid = false; m_fpu_is_valid = false; + m_sve_buffer_is_valid = false; + m_sve_header_is_valid = false; + + // SVE is not enabled until we query user_sve_header + m_sve_state = SVEState::Unknown; } RegisterInfoPOSIX_arm64 & @@ -109,28 +119,96 @@ uint8_t *src; uint32_t offset; + uint64_t sve_vg; + std::vector sve_reg_non_live; if (IsGPR(reg)) { - if (!m_gpr_is_valid) { - error = ReadGPR(); - if (error.Fail()) - return error; - } + error = ReadGPR(); + if (error.Fail()) + return error; offset = reg_info->byte_offset; assert(offset < GetGPRSize()); src = (uint8_t *)GetGPRBuffer() + offset; } else if (IsFPR(reg)) { - if (!m_fpu_is_valid) { - + if (m_sve_state == SVEState::Disabled) { + // SVE is disabled take legacy route for FPU register access 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 + error = ReadAllSVE(); + if (error.Fail()) + return error; + + // FPSR and FPCR will be located right after Z registers in + // SVEState::FPSIMD while in SVEState::Full they will be located at the + // end of register data after an alignment correction based on currently + // selected vector length. + uint32_t sve_reg_num = LLDB_INVALID_REGNUM; + if (reg == GetRegisterInfo().GetRegNumFPSR()) { + sve_reg_num = reg; + if (m_sve_state == SVEState::Full) + offset = SVE_PT_SVE_FPSR_OFFSET(sve_vq_from_vl(m_sve_header.vl)); + else if (m_sve_state == SVEState::FPSIMD) + offset = SVE_PT_FPSIMD_OFFSET + (32 * 16); + } else if (reg == GetRegisterInfo().GetRegNumFPCR()) { + sve_reg_num = reg; + if (m_sve_state == SVEState::Full) + offset = SVE_PT_SVE_FPCR_OFFSET(sve_vq_from_vl(m_sve_header.vl)); + else if (m_sve_state == SVEState::FPSIMD) + offset = SVE_PT_FPSIMD_OFFSET + (32 * 16) + 4; + } else { + // Extract SVE Z register value register number for this reg_info + if (reg_info->value_regs && + reg_info->value_regs[0] != LLDB_INVALID_REGNUM) + sve_reg_num = reg_info->value_regs[0]; + offset = CalculateSVEOffset(GetRegisterInfoAtIndex(sve_reg_num)); + } + + offset = CalculateSVEOffset(reg_info); + assert(offset < GetSVEBufferSize()); + src = (uint8_t *)GetSVEBuffer() + offset; + } + } else if (IsSVE(reg)) { + + if (m_sve_state == SVEState::Disabled || m_sve_state == SVEState::Unknown) + return Status("SVE disabled or not supported"); + + if (GetRegisterInfo().IsSVERegVG(reg)) { + sve_vg = GetSVERegVG(); + src = (uint8_t *)&sve_vg; + } else { + // SVE enabled, we will read and cache SVE ptrace data + error = ReadAllSVE(); + if (error.Fail()) + return error; + + if (m_sve_state == SVEState::FPSIMD) { + // 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. + sve_reg_non_live.resize(reg_info->byte_size, 0); + src = sve_reg_non_live.data(); + + if (GetRegisterInfo().IsSVEZReg(reg)) { + offset = CalculateSVEOffset(reg_info); + assert(offset < GetSVEBufferSize()); + ::memcpy(sve_reg_non_live.data(), (uint8_t *)GetSVEBuffer() + offset, + 16); + } + } else { + offset = CalculateSVEOffset(reg_info); + 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"); @@ -157,37 +235,125 @@ uint8_t *dst; uint32_t offset; + std::vector sve_reg_non_live; if (IsGPR(reg)) { - if (!m_gpr_is_valid) { - error = ReadGPR(); - if (error.Fail()) - return error; - } - - offset = reg_info->byte_offset; - assert(offset < GetGPRSize()); - dst = (uint8_t *)GetGPRBuffer() + offset; + 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)) { - if (!m_fpu_is_valid) { + if (m_sve_state == SVEState::Disabled) { + // SVE is disabled take legacy route for FPU register access 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); + 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 + error = ReadAllSVE(); + if (error.Fail()) + return error; + + // FPSR and FPCR will be located right after Z registers in + // SVEState::FPSIMD while in SVEState::Full they will be located at the + // end of register data after an alignment correction based on currently + // selected vector length. + uint32_t sve_reg_num = LLDB_INVALID_REGNUM; + if (reg == GetRegisterInfo().GetRegNumFPSR()) { + sve_reg_num = reg; + if (m_sve_state == SVEState::Full) + offset = SVE_PT_SVE_FPSR_OFFSET(sve_vq_from_vl(m_sve_header.vl)); + else if (m_sve_state == SVEState::FPSIMD) + offset = SVE_PT_FPSIMD_OFFSET + (32 * 16); + } else if (reg == GetRegisterInfo().GetRegNumFPCR()) { + sve_reg_num = reg; + if (m_sve_state == SVEState::Full) + offset = SVE_PT_SVE_FPCR_OFFSET(sve_vq_from_vl(m_sve_header.vl)); + else if (m_sve_state == SVEState::FPSIMD) + offset = SVE_PT_FPSIMD_OFFSET + (32 * 16) + 4; + } else { + // Extract SVE Z register value register number for this reg_info + if (reg_info->value_regs && + reg_info->value_regs[0] != LLDB_INVALID_REGNUM) + sve_reg_num = reg_info->value_regs[0]; + offset = CalculateSVEOffset(GetRegisterInfoAtIndex(sve_reg_num)); + } - return WriteFPR(); + offset = CalculateSVEOffset(reg_info); + assert(offset < GetSVEBufferSize()); + dst = (uint8_t *)GetSVEBuffer() + offset; + ::memcpy(dst, reg_value.GetBytes(), reg_info->byte_size); + return WriteAllSVE(); + } + } else if (IsSVE(reg)) { + if (m_sve_state == SVEState::Disabled || m_sve_state == SVEState::Unknown) + return Status("SVE disabled or not supported"); + else { + if (GetRegisterInfo().IsSVERegVG(reg)) + return Status("SVE state change operation not supported"); + + // Target has SVE enabled, we will read and cache SVE ptrace data + error = ReadAllSVE(); + if (error.Fail()) + return error; + + // If target supports SVE but currently in FPSIMD mode. + if (m_sve_state == SVEState::FPSIMD) { + // Here we will check if writing this SVE register enables + // SVEState::Full + bool set_sve_state_full = false; + const uint8_t *reg_bytes = (const uint8_t *)reg_value.GetBytes(); + if (GetRegisterInfo().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 (GetRegisterInfo().IsSVEPReg(reg) || + reg == GetRegisterInfo().GetRegNumSVEFFR()) { + 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 && GetRegisterInfo().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_info); + assert(offset < GetSVEBufferSize()); + dst = (uint8_t *)GetSVEBuffer() + offset; + ::memcpy(dst, reg_value.GetBytes(), 16); + + return WriteAllSVE(); + } else + return Status("SVE state change operation not supported"); + } else { + offset = CalculateSVEOffset(reg_info); + assert(offset < GetSVEBufferSize()); + dst = (uint8_t *)GetSVEBuffer() + offset; + ::memcpy(dst, reg_value.GetBytes(), reg_info->byte_size); + return WriteAllSVE(); + } + } } - return error; + return Status("Failed to write register value"); } Status NativeRegisterContextLinux_arm64::ReadAllRegisterValues( @@ -195,17 +361,15 @@ Status error; data_sp.reset(new DataBufferHeap(REG_CONTEXT_SIZE, 0)); - if (!m_gpr_is_valid) { - error = ReadGPR(); - if (error.Fail()) - return error; - } - if (!m_fpu_is_valid) { - error = ReadFPR(); - if (error.Fail()) - return error; - } + error = ReadGPR(); + if (error.Fail()) + return error; + + error = ReadFPR(); + if (error.Fail()) + return error; + uint8_t *dst = data_sp->GetBytes(); ::memcpy(dst, GetGPRBuffer(), GetGPRSize()); dst += GetGPRSize(); @@ -271,6 +435,13 @@ return false; } +bool NativeRegisterContextLinux_arm64::IsSVE(unsigned reg) const { + if (GetRegisterInfo().GetRegisterSetFromRegisterIndex(reg) == + RegisterInfoPOSIX_arm64::SVERegSet) + return true; + return false; +} + uint32_t NativeRegisterContextLinux_arm64::NumSupportedHardwareBreakpoints() { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_BREAKPOINTS)); @@ -762,8 +933,10 @@ Status NativeRegisterContextLinux_arm64::ReadGPR() { Status error; - struct iovec ioVec; + if (m_gpr_is_valid) + return error; + struct iovec ioVec; ioVec.iov_base = GetGPRBuffer(); ioVec.iov_len = GetGPRSize(); @@ -776,21 +949,26 @@ } Status NativeRegisterContextLinux_arm64::WriteGPR() { - struct iovec ioVec; - - m_gpr_is_valid = false; + 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_arm64::ReadFPR() { Status error; - struct iovec ioVec; + if (m_fpu_is_valid) + return error; + struct iovec ioVec; ioVec.iov_base = GetFPRBuffer(); ioVec.iov_len = GetFPRSize(); @@ -803,19 +981,122 @@ } Status NativeRegisterContextLinux_arm64::WriteFPR() { - struct iovec ioVec; - - m_fpu_is_valid = false; + 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_arm64::InvalidateAllRegisters() { m_gpr_is_valid = false; m_fpu_is_valid = false; + m_sve_buffer_is_valid = false; + m_sve_header_is_valid = false; + + // Update SVE registers in case there is change in configuration. + ConfigureRegisterContext(); +} + +Status NativeRegisterContextLinux_arm64::ReadSVEHeader() { + Status error; + + if (m_sve_header_is_valid) + return error; + + struct iovec ioVec; + ioVec.iov_base = GetSVEHeader(); + ioVec.iov_len = GetSVEHeaderSize(); + + error = ReadRegisterSet(&ioVec, GetSVEHeaderSize(), NT_ARM_SVE); + + m_sve_header_is_valid = true; + + return error; +} + +Status NativeRegisterContextLinux_arm64::WriteSVEHeader() { + Status error; + + error = ReadSVEHeader(); + if (error.Fail()) + return error; + + struct iovec ioVec; + ioVec.iov_base = GetSVEHeader(); + ioVec.iov_len = GetSVEHeaderSize(); + + m_sve_buffer_is_valid = false; + m_sve_header_is_valid = false; + m_fpu_is_valid = false; + + return WriteRegisterSet(&ioVec, GetSVEHeaderSize(), NT_ARM_SVE); +} + +Status NativeRegisterContextLinux_arm64::ReadAllSVE() { + Status error; + + if (m_sve_buffer_is_valid) + return error; + + struct iovec ioVec; + ioVec.iov_base = GetSVEBuffer(); + ioVec.iov_len = GetSVEBufferSize(); + + error = ReadRegisterSet(&ioVec, GetSVEBufferSize(), NT_ARM_SVE); + + if (error.Success()) + m_sve_buffer_is_valid = true; + + return error; +} + +Status NativeRegisterContextLinux_arm64::WriteAllSVE() { + Status error; + + error = ReadAllSVE(); + if (error.Fail()) + return error; + + struct iovec ioVec; + + ioVec.iov_base = GetSVEBuffer(); + ioVec.iov_len = GetSVEBufferSize(); + + m_sve_buffer_is_valid = false; + m_sve_header_is_valid = false; + m_fpu_is_valid = false; + + return WriteRegisterSet(&ioVec, GetSVEBufferSize(), NT_ARM_SVE); +} + +void NativeRegisterContextLinux_arm64::ConfigureRegisterContext() { + // Read SVE configuration data and configure register infos. + if (!m_sve_header_is_valid && m_sve_state != SVEState::Disabled) { + Status error = ReadSVEHeader(); + if (!error.Success() && m_sve_state == SVEState::Unknown) { + m_sve_state = SVEState::Disabled; + GetRegisterInfo().ConfigureVectorRegisterInfos( + RegisterInfoPOSIX_arm64::eVectorQuadwordAArch64); + } else { + if ((m_sve_header.flags & SVE_PT_REGS_MASK) == SVE_PT_REGS_FPSIMD) + m_sve_state = SVEState::FPSIMD; + else if ((m_sve_header.flags & SVE_PT_REGS_MASK) == SVE_PT_REGS_SVE) + m_sve_state = SVEState::Full; + + uint32_t vq = RegisterInfoPOSIX_arm64::eVectorQuadwordAArch64SVE; + if (sve_vl_valid(m_sve_header.vl)) + vq = sve_vq_from_vl(m_sve_header.vl); + GetRegisterInfo().ConfigureVectorRegisterInfos(vq); + m_sve_ptrace_payload.resize(SVE_PT_SIZE(vq, SVE_PT_REGS_SVE)); + } + } } uint32_t NativeRegisterContextLinux_arm64::CalculateFprOffset( @@ -823,4 +1104,27 @@ return reg_info->byte_offset - GetGPRSize(); } +uint32_t NativeRegisterContextLinux_arm64::CalculateSVEOffset( + const RegisterInfo *reg_info) const { + // Start of Z0 data is after GPRs plus 8 bytes of vg register + uint32_t sve_reg_offset = LLDB_INVALID_INDEX32; + if (m_sve_state == SVEState::FPSIMD) { + const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; + sve_reg_offset = + SVE_PT_FPSIMD_OFFSET + (reg - GetRegisterInfo().GetRegNumSVEZ0()) * 16; + } else if (m_sve_state == SVEState::Full) { + uint32_t sve_z0_offset = GetGPRSize() + 8; + sve_reg_offset = + SVE_SIG_REGS_OFFSET + reg_info->byte_offset - sve_z0_offset; + } + return sve_reg_offset; +} + +void *NativeRegisterContextLinux_arm64::GetSVEBuffer() { + if (m_sve_state == SVEState::FPSIMD) + return m_sve_ptrace_payload.data() + SVE_PT_FPSIMD_OFFSET; + + return m_sve_ptrace_payload.data(); +} + #endif // defined (__arm64__) || defined (__aarch64__) diff --git a/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm64.h b/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm64.h --- a/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm64.h +++ b/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm64.h @@ -66,7 +66,9 @@ uint32_t GetRegNumSVEZ0() const { return m_register_info_up->GetRegNumSVEZ0(); } - + uint32_t GetRegNumSVEFFR() const { + return m_register_info_up->GetRegNumSVEFFR(); + } uint32_t GetRegNumFPCR() const { return m_register_info_up->GetRegNumFPCR(); } uint32_t GetRegNumFPSR() const { return m_register_info_up->GetRegNumFPSR(); } diff --git a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h --- a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h +++ b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h @@ -98,6 +98,7 @@ bool IsSVERegVG(unsigned reg) const; uint32_t GetRegNumSVEZ0() const; + uint32_t GetRegNumSVEFFR() const; uint32_t GetRegNumFPCR() const; uint32_t GetRegNumFPSR() const; diff --git a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp --- a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp +++ b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp @@ -336,6 +336,8 @@ uint32_t RegisterInfoPOSIX_arm64::GetRegNumSVEZ0() const { return sve_z0; } +uint32_t RegisterInfoPOSIX_arm64::GetRegNumSVEFFR() const { return sve_ffr; } + uint32_t RegisterInfoPOSIX_arm64::GetRegNumFPCR() const { return fpu_fpcr; } uint32_t RegisterInfoPOSIX_arm64::GetRegNumFPSR() const { return fpu_fpsr; } diff --git a/lldb/test/API/commands/register/register/aarch64_sve_registers/rw_access_static_config/Makefile b/lldb/test/API/commands/register/register/aarch64_sve_registers/rw_access_static_config/Makefile new file mode 100644 --- /dev/null +++ b/lldb/test/API/commands/register/register/aarch64_sve_registers/rw_access_static_config/Makefile @@ -0,0 +1,5 @@ +C_SOURCES := main.c + +CFLAGS_EXTRAS := -march=armv8-a+sve + +include Makefile.rules diff --git a/lldb/test/API/commands/register/register/aarch64_sve_registers/rw_access_static_config/TestSVERegisters.py b/lldb/test/API/commands/register/register/aarch64_sve_registers/rw_access_static_config/TestSVERegisters.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/commands/register/register/aarch64_sve_registers/rw_access_static_config/TestSVERegisters.py @@ -0,0 +1,176 @@ +""" +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 targetHasSVE(self): + triple = self.dbg.GetSelectedPlatform().GetTriple() + + # TODO other platforms, please implement this function + if not re.match(".*-.*-linux", triple): + return False + + # Need to do something different for non-Linux/Android targets + cpuinfo_path = self.getBuildArtifact("cpuinfo") + if configuration.lldb_platform_name: + self.runCmd('platform get-file "/proc/cpuinfo" ' + cpuinfo_path) + + f = open(cpuinfo_path, 'r') + cpuinfo = f.read() + f.close() + return " sve " in cpuinfo + + 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__) + + @no_debug_info_test + @skipIf(archs=no_match(["aarch64"])) + @skipIf(oslist=no_match(['linux'])) + 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) + + if not self.targetHasSVE(): + self.skipTest('SVE registers must be supported.') + + 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 'Scalable Vector Extension Registers' in registerSet.GetName(): + has_sve = True + + 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) + + @no_debug_info_test + @skipIf(archs=no_match(["aarch64"])) + @skipIf(oslist=no_match(['linux'])) + 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) + + if not self.targetHasSVE(): + self.skipTest('SVE registers must be supported.') + + 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 'Scalable Vector Extension Registers' in registerSet.GetName(): + has_sve = True + + 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) + + for i in range(32): + z_regs_value = '{' + \ + ' '.join('0x{:02x}'.format(i + 1) + for _ in range(z_reg_size)) + '}' + self.expect("register read " + 'z%i' % + (i), substrs=[z_regs_value]) + + p_value_bytes = ['0xff', '0x55', '0x11', '0x01', '0x00'] + for i in range(16): + p_regs_value = '{' + \ + ' '.join(p_value_bytes[i % 5] for _ in range(p_reg_size)) + '}' + self.expect("register read " + 'p%i' % (i), substrs=[p_regs_value]) + + self.expect("register read ffr", substrs=[p_regs_value]) + + 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]) diff --git a/lldb/test/API/commands/register/register/aarch64_sve_registers/rw_access_static_config/main.c b/lldb/test/API/commands/register/register/aarch64_sve_registers/rw_access_static_config/main.c new file mode 100644 --- /dev/null +++ b/lldb/test/API/commands/register/register/aarch64_sve_registers/rw_access_static_config/main.c @@ -0,0 +1,53 @@ +int main() { + asm volatile("setffr\n\t"); + asm volatile("ptrue p0.b\n\t"); + asm volatile("ptrue p1.h\n\t"); + asm volatile("ptrue p2.s\n\t"); + asm volatile("ptrue p3.d\n\t"); + asm volatile("pfalse p4.b\n\t"); + asm volatile("ptrue p5.b\n\t"); + asm volatile("ptrue p6.h\n\t"); + asm volatile("ptrue p7.s\n\t"); + asm volatile("ptrue p8.d\n\t"); + asm volatile("pfalse p9.b\n\t"); + asm volatile("ptrue p10.b\n\t"); + asm volatile("ptrue p11.h\n\t"); + asm volatile("ptrue p12.s\n\t"); + asm volatile("ptrue p13.d\n\t"); + asm volatile("pfalse p14.b\n\t"); + asm volatile("ptrue p15.b\n\t"); + + asm volatile("cpy z0.b, p0/z, #1\n\t"); + asm volatile("cpy z1.b, p5/z, #2\n\t"); + asm volatile("cpy z2.b, p10/z, #3\n\t"); + asm volatile("cpy z3.b, p15/z, #4\n\t"); + asm volatile("cpy z4.b, p0/z, #5\n\t"); + asm volatile("cpy z5.b, p5/z, #6\n\t"); + asm volatile("cpy z6.b, p10/z, #7\n\t"); + asm volatile("cpy z7.b, p15/z, #8\n\t"); + asm volatile("cpy z8.b, p0/z, #9\n\t"); + asm volatile("cpy z9.b, p5/z, #10\n\t"); + asm volatile("cpy z10.b, p10/z, #11\n\t"); + asm volatile("cpy z11.b, p15/z, #12\n\t"); + asm volatile("cpy z12.b, p0/z, #13\n\t"); + asm volatile("cpy z13.b, p5/z, #14\n\t"); + asm volatile("cpy z14.b, p10/z, #15\n\t"); + asm volatile("cpy z15.b, p15/z, #16\n\t"); + asm volatile("cpy z16.b, p0/z, #17\n\t"); + asm volatile("cpy z17.b, p5/z, #18\n\t"); + asm volatile("cpy z18.b, p10/z, #19\n\t"); + asm volatile("cpy z19.b, p15/z, #20\n\t"); + asm volatile("cpy z20.b, p0/z, #21\n\t"); + asm volatile("cpy z21.b, p5/z, #22\n\t"); + asm volatile("cpy z22.b, p10/z, #23\n\t"); + asm volatile("cpy z23.b, p15/z, #24\n\t"); + asm volatile("cpy z24.b, p0/z, #25\n\t"); + asm volatile("cpy z25.b, p5/z, #26\n\t"); + asm volatile("cpy z26.b, p10/z, #27\n\t"); + asm volatile("cpy z27.b, p15/z, #28\n\t"); + asm volatile("cpy z28.b, p0/z, #29\n\t"); + asm volatile("cpy z29.b, p5/z, #30\n\t"); + asm volatile("cpy z30.b, p10/z, #31\n\t"); + asm volatile("cpy z31.b, p15/z, #32\n\t"); + return 0; // Set a break point here. +}