Skip to content

Commit d7fab66

Browse files
committed
[lldb][AArch64] Add SME's streaming vector control register
Software can tell if it is in streaming SVE mode by checking the Streaming Vector Control Register (SVCR). "E3.1.9 SVCR, Streaming Vector Control Register" in "Arm® Architecture Reference Manual Supplement, The Scalable Matrix Extension (SME), for Armv9-A" https://developer.arm.com/documentation/ddi0616/latest/ This is especially useful for debug because the names of the SVE registers are the same betweeen non-streaming and streaming mode. The Linux Kernel chose to not put this register behind ptrace, and it can be read from EL0. However, this would mean running code in process to read it. That can be done but we already know all the information just from ptrace. So this is a pseudo register that matches the architectural content. The name is just "svcr", which aligns with GDB's proposed naming, and it's added to the existing SME register set. The SVCR register contains two bits: 0 : Whether streaming SVE mode is enabled (SM) 1 : Whether the array storage is enabled (ZA) Array storage can be active when streaming mode is not, so this register can have any permutation of those bits. This register is currently read only. We can emulate the result of writing to it, using ptrace. However at this point the utility of that is not obvious. Existing tests have been updated to check for appropriate SVCR values at various points. Given that this register is a read only pseudo, there is no need to save and restore it around expressions. Reviewed By: omjavaid Differential Revision: https://reviews.llvm.org/D154927
1 parent 59fbba9 commit d7fab66

File tree

7 files changed

+67
-12
lines changed

7 files changed

+67
-12
lines changed

lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,9 @@ NativeRegisterContextLinux_arm64::ReadRegister(const RegisterInfo *reg_info,
360360
if (error.Fail())
361361
return error;
362362

363+
// This is a psuedo so it never fails.
364+
ReadSMEControl();
365+
363366
offset = reg_info->byte_offset - GetRegisterInfo().GetSMEOffset();
364367
assert(offset < GetSMEPseudoBufferSize());
365368
src = (uint8_t *)GetSMEPseudoBuffer() + offset;
@@ -550,7 +553,7 @@ Status NativeRegisterContextLinux_arm64::WriteRegister(
550553
return WriteTLS();
551554
} else if (IsSME(reg)) {
552555
if (!GetRegisterInfo().IsSMERegZA(reg))
553-
return Status("Writing to SVG is not supported.");
556+
return Status("Writing to SVG or SVCR is not supported.");
554557

555558
error = ReadZA();
556559
if (error.Fail())
@@ -1160,6 +1163,24 @@ Status NativeRegisterContextLinux_arm64::WriteAllSVE() {
11601163
return WriteRegisterSet(&ioVec, GetSVEBufferSize(), GetSVERegSet());
11611164
}
11621165

1166+
Status NativeRegisterContextLinux_arm64::ReadSMEControl() {
1167+
// The real register is SVCR and is accessible from EL0. However we don't want
1168+
// to have to JIT code into the target process so we'll just recreate it using
1169+
// what we know from ptrace.
1170+
1171+
// Bit 0 indicates whether streaming mode is active.
1172+
m_sme_pseudo_regs.ctrl_reg = m_sve_state == SVEState::Streaming;
1173+
1174+
// Bit 1 indicates whether the array storage is active.
1175+
// It is active if we can read the header and the size field tells us that
1176+
// there is register data following it.
1177+
Status error = ReadZAHeader();
1178+
if (error.Success() && (m_za_header.size > sizeof(m_za_header)))
1179+
m_sme_pseudo_regs.ctrl_reg |= 2;
1180+
1181+
return error;
1182+
}
1183+
11631184
Status NativeRegisterContextLinux_arm64::ReadMTEControl() {
11641185
Status error;
11651186

lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ class NativeRegisterContextLinux_arm64
115115
uint64_t m_mte_ctrl_reg;
116116

117117
struct sme_pseudo_regs {
118+
uint64_t ctrl_reg;
118119
uint64_t svg_reg;
119120
};
120121

@@ -162,6 +163,9 @@ class NativeRegisterContextLinux_arm64
162163
// Instead use WriteZA and ensure you have the correct ZA buffer size set
163164
// beforehand if you wish to disable it.
164165

166+
// SVCR is a pseudo register and we do not allow writes to it.
167+
Status ReadSMEControl();
168+
165169
bool IsSVE(unsigned reg) const;
166170
bool IsSME(unsigned reg) const;
167171
bool IsPAuth(unsigned reg) const;

lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ static lldb_private::RegisterInfo g_register_infos_tls[] = {
8484
DEFINE_EXTENSION_REG(tpidr2)};
8585

8686
static lldb_private::RegisterInfo g_register_infos_sme[] = {
87+
DEFINE_EXTENSION_REG(svcr),
8788
DEFINE_EXTENSION_REG(svg),
8889
// 16 is a default size we will change later.
8990
{"za", nullptr, 16, 0, lldb::eEncodingVector, lldb::eFormatVectorOfUInt8,
@@ -97,7 +98,7 @@ enum {
9798
k_num_mte_register = 1,
9899
// Number of TLS registers is dynamic so it is not listed here.
99100
k_num_pauth_register = 2,
100-
k_num_sme_register = 2,
101+
k_num_sme_register = 3,
101102
k_num_register_sets_default = 2,
102103
k_num_register_sets = 3
103104
};
@@ -449,7 +450,7 @@ void RegisterInfoPOSIX_arm64::ConfigureVectorLengthZA(uint32_t za_vq) {
449450
// dynamic set and is just 1 register so we make an exception to const here.
450451
lldb_private::RegisterInfo *non_const_reginfo =
451452
const_cast<lldb_private::RegisterInfo *>(m_register_info_p);
452-
non_const_reginfo[m_sme_regnum_collection[1]].byte_size =
453+
non_const_reginfo[m_sme_regnum_collection[2]].byte_size =
453454
(za_vq * 16) * (za_vq * 16);
454455
}
455456

@@ -473,7 +474,7 @@ bool RegisterInfoPOSIX_arm64::IsSVERegVG(unsigned reg) const {
473474
}
474475

475476
bool RegisterInfoPOSIX_arm64::IsSMERegZA(unsigned reg) const {
476-
return reg == m_sme_regnum_collection[1];
477+
return reg == m_sme_regnum_collection[2];
477478
}
478479

479480
bool RegisterInfoPOSIX_arm64::IsPAuthReg(unsigned reg) const {
@@ -503,7 +504,7 @@ uint32_t RegisterInfoPOSIX_arm64::GetRegNumFPSR() const { return fpu_fpsr; }
503504
uint32_t RegisterInfoPOSIX_arm64::GetRegNumSVEVG() const { return sve_vg; }
504505

505506
uint32_t RegisterInfoPOSIX_arm64::GetRegNumSMESVG() const {
506-
return m_sme_regnum_collection[0];
507+
return m_sme_regnum_collection[1];
507508
}
508509

509510
uint32_t RegisterInfoPOSIX_arm64::GetPAuthOffset() const {

lldb/test/API/commands/register/register/aarch64_dynamic_regset/TestArm64DynamicRegsets.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,13 @@ def test_aarch64_dynamic_regset_config_sme(self):
172172
svg = sme_registers.GetChildMemberWithName("svg").GetValueAsUnsigned()
173173
self.assertEqual(vg, svg)
174174

175+
# SVCR should be SVCR.SM | SVCR.ZA aka 3 because streaming mode is on
176+
# and ZA is enabled.
177+
svcr = sme_registers.GetChildMemberWithName("svcr").GetValueAsUnsigned()
178+
self.assertEqual(3, svcr)
179+
180+
# SVCR is read only so we do not test writing to it.
181+
175182
@no_debug_info_test
176183
@skipIf(archs=no_match(["aarch64"]))
177184
@skipIf(oslist=no_match(["linux"]))
@@ -192,6 +199,10 @@ def test_aarch64_dynamic_regset_config_sme_za_disabled(self):
192199
self.assertTrue(sme_registers.IsValid())
193200
svg = sme_registers.GetChildMemberWithName("svg").GetValueAsUnsigned()
194201

202+
# We are not in streaming mode, ZA is disabled, so this should be 0.
203+
svcr = sme_registers.GetChildMemberWithName("svcr").GetValueAsUnsigned()
204+
self.assertEqual(0, svcr)
205+
195206
svl = svg * 8
196207
# A disabled ZA is shown as all 0s.
197208
self.expect("register read za", substrs=[self.make_za_value(svl, lambda r: 0)])
@@ -200,3 +211,8 @@ def test_aarch64_dynamic_regset_config_sme_za_disabled(self):
200211
# it back.
201212
self.runCmd("register write za '{}'".format(za_value))
202213
self.expect("register read za", substrs=[za_value])
214+
215+
# Now SVCR.ZA should be set, which is bit 1.
216+
self.expect("register read svcr", substrs=["0x0000000000000002"])
217+
218+
# SVCR is read only so we do not test writing to it.

lldb/test/API/commands/register/register/aarch64_sve_registers/rw_access_static_config/TestSVERegisters.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,15 @@ def check_sve_register_size(self, set, name, expected):
2424
reg_value.GetByteSize(), expected, 'Verify "%s" == %i' % (name, expected)
2525
)
2626

27-
def check_sve_regs_read(self, z_reg_size):
27+
def check_sve_regs_read(self, z_reg_size, expected_mode):
28+
if self.isAArch64SME():
29+
# This test uses SMSTART SM, which only enables streaming mode,
30+
# leaving ZA disabled.
31+
expected_value = "1" if expected_mode == Mode.SSVE else "0"
32+
self.expect(
33+
"register read svcr", substrs=["0x000000000000000" + expected_value]
34+
)
35+
2836
p_reg_size = int(z_reg_size / 8)
2937

3038
for i in range(32):
@@ -168,7 +176,7 @@ def sve_registers_read_write_impl(self, start_mode, eval_mode):
168176

169177
vg_reg_value = sve_registers.GetChildMemberWithName("vg").GetValueAsUnsigned()
170178
z_reg_size = vg_reg_value * 8
171-
self.check_sve_regs_read(z_reg_size)
179+
self.check_sve_regs_read(z_reg_size, start_mode)
172180

173181
# Evaluate simple expression and print function expr_eval_func address.
174182
self.expect("expression expr_eval_func", substrs=["= 0x"])
@@ -184,7 +192,7 @@ def sve_registers_read_write_impl(self, start_mode, eval_mode):
184192

185193
# We called a jitted function above which must not have changed SVE
186194
# vector length or register values.
187-
self.check_sve_regs_read(z_reg_size)
195+
self.check_sve_regs_read(z_reg_size, start_mode)
188196

189197
self.check_sve_regs_read_after_write(z_reg_size)
190198

lldb/test/API/commands/register/register/aarch64_sve_registers/rw_access_static_config/main.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
#define PR_SME_SET_VL 63
1010
#endif
1111

12-
#define SMSTART() asm volatile("msr s0_3_c4_c7_3, xzr" /*smstart*/)
12+
#define SMSTART_SM() asm volatile("msr s0_3_c4_c3_3, xzr" /*smstart sm*/)
1313

1414
void write_sve_regs() {
1515
// We assume the smefa64 feature is present, which allows ffr access
@@ -130,18 +130,18 @@ int expr_eval_func(bool streaming) {
130130
// Note that doing a syscall brings you back to non-streaming mode, so we
131131
// don't need to SMSTOP here.
132132
if (streaming)
133-
SMSTART();
133+
SMSTART_SM();
134134
write_sve_regs_expr();
135135
prctl(SET_VL_OPT, 8 * 4);
136136
if (streaming)
137-
SMSTART();
137+
SMSTART_SM();
138138
write_sve_regs_expr();
139139
return 1;
140140
}
141141

142142
int main() {
143143
#ifdef START_SSVE
144-
SMSTART();
144+
SMSTART_SM();
145145
#endif
146146
write_sve_regs();
147147

lldb/test/API/commands/register/register/aarch64_za_register/za_save_restore/TestZARegisterSaveRestore.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,10 @@ def za_expr_test_impl(self, sve_mode, za_state, swap_start_vl):
164164
self.runCmd("register read " + sve_reg_names)
165165
sve_values = self.res.GetOutput()
166166

167+
svcr_value = 1 if sve_mode == Mode.SSVE else 0
168+
if za_state == ZA.Enabled:
169+
svcr_value += 2
170+
167171
def check_regs():
168172
if za_state == ZA.Enabled:
169173
self.check_za(start_vl)
@@ -175,6 +179,7 @@ def check_regs():
175179
self.assertEqual(start_vg, self.read_vg())
176180

177181
self.expect("register read " + sve_reg_names, substrs=[sve_values])
182+
self.expect("register read svcr", substrs=["0x{:016x}".format(svcr_value)])
178183

179184
for expr in exprs:
180185
expr_cmd = "expression {}()".format(expr)

0 commit comments

Comments
 (0)