Skip to content

[lldb][debugserver] Read/write SME registers on arm64 (#119171) #9774

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,25 @@ bool ArchitectureAArch64::ReconfigureRegisterInfo(DynamicRegisterInfo &reg_info,
if (reg_value != fail_value && reg_value <= 32)
svg_reg_value = reg_value;
}
if (!svg_reg_value) {
const RegisterInfo *darwin_svg_reg_info = reg_info.GetRegisterInfo("svl");
if (darwin_svg_reg_info) {
uint32_t svg_reg_num = darwin_svg_reg_info->kinds[eRegisterKindLLDB];
uint64_t reg_value =
reg_context.ReadRegisterAsUnsigned(svg_reg_num, fail_value);
// UpdateARM64SVERegistersInfos and UpdateARM64SMERegistersInfos
// expect the number of 8-byte granules; darwin provides number of
// bytes.
if (reg_value != fail_value && reg_value <= 256) {
svg_reg_value = reg_value / 8;
// Apple hardware only implements Streaming SVE mode, so
// the non-streaming Vector Length is not reported by the
// kernel. Set both svg and vg to this svl value.
if (!vg_reg_value)
vg_reg_value = reg_value / 8;
}
}
}

if (!vg_reg_value && !svg_reg_value)
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,24 @@ def tearDown(self):
self.dbg.GetSelectedTarget().GetProcess().Destroy()
TestBase.tearDown(self)

# on macOS, detect if the current machine is arm64 and supports SME
def get_sme_available(self):
if self.getArchitecture() != "arm64":
return None
try:
sysctl_output = subprocess.check_output(
["sysctl", "hw.optional.arm.FEAT_SME"]
).decode("utf-8")
except subprocess.CalledProcessError:
return None
m = re.match(r"hw\.optional\.arm\.FEAT_SME: (\w+)", sysctl_output)
if m:
if int(m.group(1)) == 1:
return True
else:
return False
return None

@skipIfiOSSimulator
@skipIf(archs=no_match(["amd64", "arm", "i386", "x86_64"]))
@expectedFailureAll(oslist=["freebsd", "netbsd"], bugnumber="llvm.org/pr48371")
Expand All @@ -32,11 +50,19 @@ def test_register_commands(self):
# verify that logging does not assert
self.log_enable("registers")

error_str_matched = False
if self.get_sme_available() and self.platformIsDarwin():
# On Darwin AArch64 SME machines, we will have unavailable
# registers when not in Streaming SVE Mode/SME, so
# `register read -a` will report that some registers
# could not be read. This is expected.
error_str_matched = True

self.expect(
"register read -a",
MISSING_EXPECTED_REGISTERS,
substrs=["registers were unavailable"],
matching=False,
matching=error_str_matched,
)

all_registers = self.res.GetOutput()
Expand All @@ -60,7 +86,7 @@ def test_register_commands(self):
self.runCmd("register read q15") # may be available

self.expect(
"register read -s 4", substrs=["invalid register set index: 4"], error=True
"register read -s 8", substrs=["invalid register set index: 8"], error=True
)

@skipIfiOSSimulator
Expand Down
5 changes: 5 additions & 0 deletions lldb/test/API/macosx/sme-registers/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
C_SOURCES := main.c

CFLAGS_EXTRAS := -mcpu=apple-m4

include Makefile.rules
217 changes: 217 additions & 0 deletions lldb/test/API/macosx/sme-registers/TestSMERegistersDarwin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
import lldb
from lldbsuite.test.lldbtest import *
from lldbsuite.test.decorators import *
import lldbsuite.test.lldbutil as lldbutil
import os


class TestSMERegistersDarwin(TestBase):
NO_DEBUG_INFO_TESTCASE = True
mydir = TestBase.compute_mydir(__file__)

@skipIfRemote
@skipUnlessDarwin
@skipUnlessFeature("hw.optional.arm.FEAT_SME")
@skipUnlessFeature("hw.optional.arm.FEAT_SME2")
# thread_set_state/thread_get_state only avail in macOS 15.4+
@skipIf(macos_version=["<", "15.4"])
def test(self):
"""Test that we can read the contents of the SME/SVE registers on Darwin"""
self.build()
(target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
self, "break before sme", lldb.SBFileSpec("main.c")
)
frame = thread.GetFrameAtIndex(0)
self.assertTrue(frame.IsValid())

self.assertTrue(
target.BreakpointCreateBySourceRegex(
"break while sme", lldb.SBFileSpec("main.c")
).IsValid()
)
self.assertTrue(
target.BreakpointCreateBySourceRegex(
"break after sme", lldb.SBFileSpec("main.c")
).IsValid()
)

if self.TraceOn():
self.runCmd("reg read -a")

self.assertTrue(frame.register["svl"].GetError().Fail())
self.assertTrue(frame.register["z0"].GetError().Fail())
self.assertTrue(frame.register["p0"].GetError().Fail())
self.assertTrue(frame.register["za"].GetError().Fail())
self.assertTrue(frame.register["zt0"].GetError().Fail())

process.Continue()
frame = thread.GetFrameAtIndex(0)
self.assertEqual(thread.GetStopReason(), lldb.eStopReasonBreakpoint)

# Now in SME enabled mode
self.assertTrue(frame.register["svl"].GetError().Success())
self.assertTrue(frame.register["z0"].GetError().Success())
self.assertTrue(frame.register["p0"].GetError().Success())
self.assertTrue(frame.register["za"].GetError().Success())
self.assertTrue(frame.register["zt0"].GetError().Success())

# SSVE and SME modes should be enabled (reflecting PSTATE.SM and PSTATE.ZA)
svcr = frame.register["svcr"]
self.assertEqual(svcr.GetValueAsUnsigned(), 3)

svl_reg = frame.register["svl"]
svl = svl_reg.GetValueAsUnsigned()

z0 = frame.register["z0"]
self.assertEqual(z0.GetNumChildren(), svl)
self.assertEqual(z0.GetChildAtIndex(0).GetValueAsUnsigned(), 0x1)
self.assertEqual(z0.GetChildAtIndex(svl - 1).GetValueAsUnsigned(), 0x1)

z31 = frame.register["z31"]
self.assertEqual(z31.GetNumChildren(), svl)
self.assertEqual(z31.GetChildAtIndex(0).GetValueAsUnsigned(), 32)
self.assertEqual(z31.GetChildAtIndex(svl - 1).GetValueAsUnsigned(), 32)

p0 = frame.register["p0"]
self.assertEqual(p0.GetNumChildren(), svl / 8)
self.assertEqual(p0.GetChildAtIndex(0).GetValueAsUnsigned(), 0xFF)
self.assertEqual(
p0.GetChildAtIndex(p0.GetNumChildren() - 1).GetValueAsUnsigned(), 0xFF
)

p15 = frame.register["p15"]
self.assertEqual(p15.GetNumChildren(), svl / 8)
self.assertEqual(p15.GetChildAtIndex(0).GetValueAsUnsigned(), 0xFF)
self.assertEqual(
p15.GetChildAtIndex(p15.GetNumChildren() - 1).GetValueAsUnsigned(), 0xFF
)

za = frame.register["za"]
self.assertEqual(za.GetNumChildren(), (svl * svl))
za_0 = za.GetChildAtIndex(0)
self.assertEqual(za_0.GetValueAsUnsigned(), 4)
za_final = za.GetChildAtIndex(za.GetNumChildren() - 1)
self.assertEqual(za_final.GetValueAsUnsigned(), 67)

zt0 = frame.register["zt0"]
self.assertEqual(zt0.GetNumChildren(), 64)
zt0_0 = zt0.GetChildAtIndex(0)
self.assertEqual(zt0_0.GetValueAsUnsigned(), 0)
zt0_final = zt0.GetChildAtIndex(63)
self.assertEqual(zt0_final.GetValueAsUnsigned(), 63)

# Modify all of the registers, instruction step, confirm that the
# registers have the new values. Without the instruction step, it's
# possible debugserver or lldb could lie about the write succeeding.

z0_old_values = []
z0_new_values = []
z0_new_str = '"{'
for i in range(svl):
z0_old_values.append(z0.GetChildAtIndex(i).GetValueAsUnsigned())
z0_new_values.append(z0_old_values[i] + 5)
z0_new_str = z0_new_str + ("0x%02x " % z0_new_values[i])
z0_new_str = z0_new_str + '}"'
self.runCmd("reg write z0 %s" % z0_new_str)

z31_old_values = []
z31_new_values = []
z31_new_str = '"{'
for i in range(svl):
z31_old_values.append(z31.GetChildAtIndex(i).GetValueAsUnsigned())
z31_new_values.append(z31_old_values[i] + 3)
z31_new_str = z31_new_str + ("0x%02x " % z31_new_values[i])
z31_new_str = z31_new_str + '}"'
self.runCmd("reg write z31 %s" % z31_new_str)

p0_old_values = []
p0_new_values = []
p0_new_str = '"{'
for i in range(int(svl / 8)):
p0_old_values.append(p0.GetChildAtIndex(i).GetValueAsUnsigned())
p0_new_values.append(p0_old_values[i] - 5)
p0_new_str = p0_new_str + ("0x%02x " % p0_new_values[i])
p0_new_str = p0_new_str + '}"'
self.runCmd("reg write p0 %s" % p0_new_str)

p15_old_values = []
p15_new_values = []
p15_new_str = '"{'
for i in range(int(svl / 8)):
p15_old_values.append(p15.GetChildAtIndex(i).GetValueAsUnsigned())
p15_new_values.append(p15_old_values[i] - 8)
p15_new_str = p15_new_str + ("0x%02x " % p15_new_values[i])
p15_new_str = p15_new_str + '}"'
self.runCmd("reg write p15 %s" % p15_new_str)

za_old_values = []
za_new_values = []
za_new_str = '"{'
for i in range(svl * svl):
za_old_values.append(za.GetChildAtIndex(i).GetValueAsUnsigned())
za_new_values.append(za_old_values[i] + 7)
za_new_str = za_new_str + ("0x%02x " % za_new_values[i])
za_new_str = za_new_str + '}"'
self.runCmd("reg write za %s" % za_new_str)

zt0_old_values = []
zt0_new_values = []
zt0_new_str = '"{'
for i in range(64):
zt0_old_values.append(zt0.GetChildAtIndex(i).GetValueAsUnsigned())
zt0_new_values.append(zt0_old_values[i] + 2)
zt0_new_str = zt0_new_str + ("0x%02x " % zt0_new_values[i])
zt0_new_str = zt0_new_str + '}"'
self.runCmd("reg write zt0 %s" % zt0_new_str)

thread.StepInstruction(False)
frame = thread.GetFrameAtIndex(0)

if self.TraceOn():
self.runCmd("reg read -a")

z0 = frame.register["z0"]
for i in range(z0.GetNumChildren()):
self.assertEqual(
z0_new_values[i], z0.GetChildAtIndex(i).GetValueAsUnsigned()
)

z31 = frame.register["z31"]
for i in range(z31.GetNumChildren()):
self.assertEqual(
z31_new_values[i], z31.GetChildAtIndex(i).GetValueAsUnsigned()
)

p0 = frame.register["p0"]
for i in range(p0.GetNumChildren()):
self.assertEqual(
p0_new_values[i], p0.GetChildAtIndex(i).GetValueAsUnsigned()
)

p15 = frame.register["p15"]
for i in range(p15.GetNumChildren()):
self.assertEqual(
p15_new_values[i], p15.GetChildAtIndex(i).GetValueAsUnsigned()
)

za = frame.register["za"]
for i in range(za.GetNumChildren()):
self.assertEqual(
za_new_values[i], za.GetChildAtIndex(i).GetValueAsUnsigned()
)

zt0 = frame.register["zt0"]
for i in range(zt0.GetNumChildren()):
self.assertEqual(
zt0_new_values[i], zt0.GetChildAtIndex(i).GetValueAsUnsigned()
)

process.Continue()
frame = thread.GetFrameAtIndex(0)
self.assertEqual(thread.GetStopReason(), lldb.eStopReasonBreakpoint)

self.assertTrue(frame.register["svl"].GetError().Fail())
self.assertTrue(frame.register["z0"].GetError().Fail())
self.assertTrue(frame.register["p0"].GetError().Fail())
self.assertTrue(frame.register["za"].GetError().Fail())
self.assertTrue(frame.register["zt0"].GetError().Fail())
Loading