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 @@ -115,6 +115,7 @@ uint64_t m_mte_ctrl_reg; struct sme_pseudo_regs { + uint64_t ctrl_reg; uint64_t svg_reg; }; @@ -162,6 +163,9 @@ // Instead use WriteZA and ensure you have the correct ZA buffer size set // beforehand if you wish to disable it. + // SVCR is a pseudo register and we do not allow writes to it. + Status ReadSMEControl(); + bool IsSVE(unsigned reg) const; bool IsSME(unsigned reg) const; bool IsPAuth(unsigned reg) const; 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 @@ -360,6 +360,9 @@ if (error.Fail()) return error; + // This is a psuedo so it never fails. + ReadSMEControl(); + offset = reg_info->byte_offset - GetRegisterInfo().GetSMEOffset(); assert(offset < GetSMEPseudoBufferSize()); src = (uint8_t *)GetSMEPseudoBuffer() + offset; @@ -550,7 +553,7 @@ return WriteTLS(); } else if (IsSME(reg)) { if (!GetRegisterInfo().IsSMERegZA(reg)) - return Status("Writing to SVG is not supported."); + return Status("Writing to SVG or SVCR is not supported."); error = ReadZA(); if (error.Fail()) @@ -1160,6 +1163,24 @@ return WriteRegisterSet(&ioVec, GetSVEBufferSize(), GetSVERegSet()); } +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 0 indicates whether streaming mode is active. + m_sme_pseudo_regs.ctrl_reg = m_sve_state == SVEState::Streaming; + + // Bit 1 indicates whether the array storage is active. + // It is active if we can read the header and the size field tells us that + // there is register data following it. + Status error = ReadZAHeader(); + if (error.Success() && (m_za_header.size > sizeof(m_za_header))) + m_sme_pseudo_regs.ctrl_reg |= 2; + + return {}; +} + Status NativeRegisterContextLinux_arm64::ReadMTEControl() { Status error; 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 @@ -84,6 +84,7 @@ DEFINE_EXTENSION_REG(tpidr2)}; static lldb_private::RegisterInfo g_register_infos_sme[] = { + DEFINE_EXTENSION_REG(svcr), DEFINE_EXTENSION_REG(svg), // 16 is a default size we will change later. {"za", nullptr, 16, 0, lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, @@ -97,7 +98,7 @@ k_num_mte_register = 1, // Number of TLS registers is dynamic so it is not listed here. k_num_pauth_register = 2, - k_num_sme_register = 2, + k_num_sme_register = 3, k_num_register_sets_default = 2, k_num_register_sets = 3 }; @@ -449,7 +450,7 @@ // dynamic set and is just 1 register so we make an exception to const here. lldb_private::RegisterInfo *non_const_reginfo = const_cast(m_register_info_p); - non_const_reginfo[m_sme_regnum_collection[1]].byte_size = + non_const_reginfo[m_sme_regnum_collection[2]].byte_size = (za_vq * 16) * (za_vq * 16); } @@ -473,7 +474,7 @@ } bool RegisterInfoPOSIX_arm64::IsSMERegZA(unsigned reg) const { - return reg == m_sme_regnum_collection[1]; + return reg == m_sme_regnum_collection[2]; } bool RegisterInfoPOSIX_arm64::IsPAuthReg(unsigned reg) const { @@ -503,7 +504,7 @@ uint32_t RegisterInfoPOSIX_arm64::GetRegNumSVEVG() const { return sve_vg; } uint32_t RegisterInfoPOSIX_arm64::GetRegNumSMESVG() const { - return m_sme_regnum_collection[0]; + return m_sme_regnum_collection[1]; } uint32_t RegisterInfoPOSIX_arm64::GetPAuthOffset() const { diff --git a/lldb/test/API/commands/register/register/aarch64_dynamic_regset/TestArm64DynamicRegsets.py b/lldb/test/API/commands/register/register/aarch64_dynamic_regset/TestArm64DynamicRegsets.py --- a/lldb/test/API/commands/register/register/aarch64_dynamic_regset/TestArm64DynamicRegsets.py +++ b/lldb/test/API/commands/register/register/aarch64_dynamic_regset/TestArm64DynamicRegsets.py @@ -171,6 +171,11 @@ svg = sme_registers.GetChildMemberWithName("svg").GetValueAsUnsigned() self.assertEqual(vg, svg) + # SVCR should be SVCR.SM | SVCR.ZA aka 3 because streaming mode is on + # and ZA is enabled. + svcr = sme_registers.GetChildMemberWithName("svcr").GetValueAsUnsigned() + self.assertEqual(3, svcr) + @no_debug_info_test @skipIf(archs=no_match(["aarch64"])) @skipIf(oslist=no_match(["linux"])) @@ -191,6 +196,10 @@ self.assertTrue(sme_registers.IsValid()) svg = sme_registers.GetChildMemberWithName("svg").GetValueAsUnsigned() + # We are not in streaming mode, ZA is disabled, so this should be 0. + svcr = sme_registers.GetChildMemberWithName("svcr").GetValueAsUnsigned() + self.assertEqual(0, svcr) + svl = svg * 8 # A disabled ZA is shown as all 0s. self.expect("register read za", substrs=[self.make_za_value(svl, lambda r: 0)]) @@ -199,3 +208,6 @@ # it back. self.runCmd("register write za '{}'".format(za_value)) self.expect("register read za", substrs=[za_value]) + + # Now SVCR.ZA should be set, which is bit 1. + self.expect("register read svcr", substrs=["0x0000000000000002"]) 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 @@ -24,7 +24,15 @@ 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(): + # This test uses SMSTART SM, which only enables streaming mode, + # leaving ZA disabled. + 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): @@ -168,7 +176,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"]) @@ -184,7 +192,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) 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 --- 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 @@ -9,7 +9,7 @@ #define PR_SME_SET_VL 63 #endif -#define SMSTART() asm volatile("msr s0_3_c4_c7_3, xzr" /*smstart*/) +#define SMSTART_SM() asm volatile("msr s0_3_c4_c3_3, xzr" /*smstart sm*/) void write_sve_regs() { // We assume the smefa64 feature is present, which allows ffr access @@ -130,18 +130,18 @@ // Note that doing a syscall brings you back to non-streaming mode, so we // don't need to SMSTOP here. if (streaming) - SMSTART(); + SMSTART_SM(); write_sve_regs_expr(); prctl(SET_VL_OPT, 8 * 4); if (streaming) - SMSTART(); + SMSTART_SM(); write_sve_regs_expr(); return 1; } int main() { #ifdef START_SSVE - SMSTART(); + SMSTART_SM(); #endif write_sve_regs(); diff --git a/lldb/test/API/commands/register/register/aarch64_za_register/za_save_restore/TestZARegisterSaveRestore.py b/lldb/test/API/commands/register/register/aarch64_za_register/za_save_restore/TestZARegisterSaveRestore.py --- a/lldb/test/API/commands/register/register/aarch64_za_register/za_save_restore/TestZARegisterSaveRestore.py +++ b/lldb/test/API/commands/register/register/aarch64_za_register/za_save_restore/TestZARegisterSaveRestore.py @@ -164,6 +164,10 @@ self.runCmd("register read " + sve_reg_names) sve_values = self.res.GetOutput() + svcr_value = 1 if sve_mode == Mode.SSVE else 0 + if za_state == ZA.Enabled: + svcr_value += 2 + def check_regs(): if za_state == ZA.Enabled: self.check_za(start_vl) @@ -175,6 +179,7 @@ self.assertEqual(start_vg, self.read_vg()) self.expect("register read " + sve_reg_names, substrs=[sve_values]) + self.expect("register read svcr", substrs=["0x{:016x}".format(svcr_value)]) for expr in exprs: expr_cmd = "expression {}()".format(expr)