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 @@ -126,6 +126,7 @@ struct user_pac_mask m_pac_mask; uint64_t m_mte_ctrl_reg; + uint64_t m_sme_ctrl_reg; uint64_t m_tls_tpidr_reg; @@ -157,10 +158,14 @@ Status WriteTLSTPIDR(); + // SVCR is a pseudo register and we do not allow writes to it. + Status ReadSMEControl(); + bool IsSVE(unsigned reg) const; bool IsPAuth(unsigned reg) const; bool IsMTE(unsigned reg) const; bool IsTLS(unsigned reg) const; + bool IsSME(unsigned reg) const; uint64_t GetSVERegVG() { return CurrentSVEStateData().m_header.vl / 8; } @@ -172,6 +177,8 @@ void *GetTLSTPIDR() { return &m_tls_tpidr_reg; } + void *GetSMEControl() { return &m_sme_ctrl_reg; } + void *GetSVEBuffer() { return CurrentSVEStateData().m_buffer.data(); } size_t GetSVEBufferSize() { return CurrentSVEStateData().m_buffer.size(); } @@ -186,6 +193,8 @@ size_t GetTLSTPIDRSize() { return sizeof(m_tls_tpidr_reg); } + size_t GetSMEControlSize() { return sizeof(m_sme_ctrl_reg); } + llvm::Error ReadHardwareDebugInfo() override; llvm::Error WriteHardwareDebugRegs(DREGType hwbType) override; 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 @@ -135,6 +135,7 @@ m_mte_ctrl_reg = 0; m_tls_tpidr_reg = 0; + m_sme_ctrl_reg = 0; // 16 is just a maximum value, query hardware for actual watchpoint count m_max_hwp_supported = 16; @@ -320,6 +321,13 @@ offset = reg_info->byte_offset - GetRegisterInfo().GetMTEOffset(); assert(offset < GetMTEControlSize()); src = (uint8_t *)GetMTEControl() + offset; + } else if (IsSME(reg)) { + // This is a pseduo so it never fails. + ReadSMEControl(); + + offset = reg_info->byte_offset - GetRegisterInfo().GetSMEOffset(); + assert(offset < GetSMEControlSize()); + src = (uint8_t *)GetSMEControl() + offset; } else return Status("failed - register wasn't recognized to be a GPR or an FPR, " "write strategy unknown"); @@ -521,6 +529,8 @@ ::memcpy(dst, reg_value.GetBytes(), reg_info->byte_size); return WriteTLSTPIDR(); + } else if (IsSME(reg)) { + return Status("Writing to SVCR is not supported."); } return Status("Failed to write register value"); @@ -734,6 +744,10 @@ return GetRegisterInfo().IsTLSReg(reg); } +bool NativeRegisterContextLinux_arm64::IsSME(unsigned reg) const { + return GetRegisterInfo().IsSMEReg(reg); +} + llvm::Error NativeRegisterContextLinux_arm64::ReadHardwareDebugInfo() { if (!m_refresh_hwdebug_info) { return llvm::Error::success(); @@ -980,6 +994,16 @@ return WriteRegisterSet(&ioVec, state.m_buffer.size(), state.m_regset); } +Status NativeRegisterContextLinux_arm64::ReadSMEControl() { + // The real register is SVCR and is accessible from EL0. However we don't want + // to have to JIT code into the target process so we'll just recreate it using + // what we know from ptrace. + // Bit 1 indicates whether streaming mode is active. + // Bit 2 indicates whether the array storage is active (not yet implemented). + m_sme_ctrl_reg = m_sve_state == SVEState::Streaming; + return {}; +} + Status NativeRegisterContextLinux_arm64::ReadMTEControl() { Status error; 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 @@ -106,6 +106,8 @@ void AddRegSetTLS(); + void AddRegSetSME(); + uint32_t ConfigureVectorLength(uint32_t sve_vq); bool VectorSizeIsValid(uint32_t vq) { @@ -127,6 +129,7 @@ bool IsPAuthReg(unsigned reg) const; bool IsMTEReg(unsigned reg) const; bool IsTLSReg(unsigned reg) const; + bool IsSMEReg(unsigned reg) const; uint32_t GetRegNumSVEZ0() const; uint32_t GetRegNumSVEFFR() const; @@ -136,6 +139,7 @@ uint32_t GetPAuthOffset() const; uint32_t GetMTEOffset() const; uint32_t GetTLSOffset() const; + uint32_t GetSMEOffset() const; private: typedef std::map> @@ -163,6 +167,7 @@ std::vector pauth_regnum_collection; std::vector m_mte_regnum_collection; std::vector m_tls_regnum_collection; + std::vector m_sme_regnum_collection; }; #endif 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 @@ -81,6 +81,9 @@ static lldb_private::RegisterInfo g_register_infos_tls[] = { DEFINE_EXTENSION_REG(tpidr)}; +static lldb_private::RegisterInfo g_register_infos_sme[] = { + DEFINE_EXTENSION_REG(svcr)}; + // Number of register sets provided by this context. enum { k_num_gpr_registers = gpr_w28 - gpr_x0 + 1, @@ -89,6 +92,7 @@ k_num_mte_register = 1, k_num_tls_register = 1, k_num_pauth_register = 2, + k_num_sme_register = 1, k_num_register_sets_default = 2, k_num_register_sets = 3 }; @@ -196,6 +200,9 @@ static const lldb_private::RegisterSet g_reg_set_tls_arm64 = { "Thread Local Storage Registers", "tls", k_num_tls_register, nullptr}; +static const lldb_private::RegisterSet g_reg_set_sme_arm64 = { + "Scalable Matrix Extension Registers", "sme", k_num_sme_register, nullptr}; + RegisterInfoPOSIX_arm64::RegisterInfoPOSIX_arm64( const lldb_private::ArchSpec &target_arch, lldb_private::Flags opt_regsets) : lldb_private::RegisterInfoAndSetInterface(target_arch), @@ -240,6 +247,9 @@ // done as a dynamic set. AddRegSetTLS(); + if (m_opt_regsets.AllSet(eRegsetMaskSSVE)) + AddRegSetSME(); + m_register_info_count = m_dynamic_reg_infos.size(); m_register_info_p = m_dynamic_reg_infos.data(); m_register_set_p = m_dynamic_reg_sets.data(); @@ -338,6 +348,21 @@ m_dynamic_reg_sets.back().registers = m_tls_regnum_collection.data(); } +void RegisterInfoPOSIX_arm64::AddRegSetSME() { + uint32_t sme_regnum = m_dynamic_reg_infos.size(); + m_sme_regnum_collection.push_back(sme_regnum); + m_dynamic_reg_infos.push_back(g_register_infos_sme[0]); + m_dynamic_reg_infos[sme_regnum].byte_offset = + m_dynamic_reg_infos[sme_regnum - 1].byte_offset + + m_dynamic_reg_infos[sme_regnum - 1].byte_size; + m_dynamic_reg_infos[sme_regnum].kinds[lldb::eRegisterKindLLDB] = sme_regnum; + + m_per_regset_regnum_range[m_register_set_count] = + std::make_pair(sme_regnum, sme_regnum + 1); + m_dynamic_reg_sets.push_back(g_reg_set_sme_arm64); + m_dynamic_reg_sets.back().registers = m_sme_regnum_collection.data(); +} + uint32_t RegisterInfoPOSIX_arm64::ConfigureVectorLength(uint32_t sve_vq) { // sve_vq contains SVE Quad vector length in context of AArch64 SVE. // SVE register infos if enabled cannot be disabled by selecting sve_vq = 0. @@ -433,6 +458,10 @@ return llvm::is_contained(m_tls_regnum_collection, reg); } +bool RegisterInfoPOSIX_arm64::IsSMEReg(unsigned reg) const { + return llvm::is_contained(m_sme_regnum_collection, reg); +} + uint32_t RegisterInfoPOSIX_arm64::GetRegNumSVEZ0() const { return sve_z0; } uint32_t RegisterInfoPOSIX_arm64::GetRegNumSVEFFR() const { return sve_ffr; } @@ -454,3 +483,7 @@ uint32_t RegisterInfoPOSIX_arm64::GetTLSOffset() const { return m_register_info_p[m_tls_regnum_collection[0]].byte_offset; } + +uint32_t RegisterInfoPOSIX_arm64::GetSMEOffset() const { + return m_register_info_p[m_sme_regnum_collection[0]].byte_offset; +} 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 --- 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 @@ -22,7 +22,12 @@ reg_value.GetByteSize(), expected, 'Verify "%s" == %i' % (name, expected) ) - def check_sve_regs_read(self, z_reg_size): + def check_sve_regs_read(self, z_reg_size, expected_mode): + if self.isAArch64SME(): + expected_value = "1" if expected_mode == Mode.SSVE else "0" + self.expect("register read svcr", substrs=[ + "0x000000000000000" + expected_value]) + p_reg_size = int(z_reg_size / 8) for i in range(32): @@ -162,7 +167,7 @@ vg_reg_value = sve_registers.GetChildMemberWithName("vg").GetValueAsUnsigned() z_reg_size = vg_reg_value * 8 - self.check_sve_regs_read(z_reg_size) + self.check_sve_regs_read(z_reg_size, start_mode) # Evaluate simple expression and print function expr_eval_func address. self.expect("expression expr_eval_func", substrs=["= 0x"]) @@ -174,7 +179,7 @@ # We called a jitted function above which must not have changed SVE # vector length or register values. - self.check_sve_regs_read(z_reg_size) + self.check_sve_regs_read(z_reg_size, start_mode) self.check_sve_regs_read_after_write(z_reg_size)