Skip to content

Commit 43812c8

Browse files
committed
[lldb][AArch64] Linux corefile support for SME
This adds the ability to read streaming SVE registers, ZA, SVCR and SVG from core files. Streaming SVE is in a new note NT_ARM_SSVE but otherwise has the same format as SVE. So I've done the same as I did for live processes and reused the existing SVE state with an extra state for the mode variable. ZA is in a note NT_ARM_ZA and again the handling matches live processes. Except that it gets setup only once. A disabled ZA reads as 0s as usual. SVCR and SVG are pseudo registers, generated from the notes. An important detail is that the notes represent what you would have got if you read from ptrace at the time of the crash. This means that for a corefile in non-streaming mode, there is still an NT_ARM_SSVE note and we check the header flags to tell if it is active. We cannot just say if you have the note you're in streaming mode. The kernel does not provide register values for the inactive mode and even if it did, they would be undefined, so if we find streaming state, we ignore the non-streaming state. Same for ZA, a disabled ZA still has the header in the note. The tests do not cover all combinations but enough different vector lengths, modes and ZA states to be confident. Reviewed By: omjavaid Differential Revision: https://reviews.llvm.org/D158500
1 parent 7078993 commit 43812c8

10 files changed

+383
-18
lines changed

lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm64.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,15 +54,18 @@ class RegisterContextPOSIX_arm64 : public lldb_private::RegisterContext {
5454
size_t GetFPUSize() { return sizeof(RegisterInfoPOSIX_arm64::FPU); }
5555

5656
bool IsSVE(unsigned reg) const;
57-
bool IsSME(unsigned reg) const;
5857
bool IsPAuth(unsigned reg) const;
5958
bool IsTLS(unsigned reg) const;
59+
bool IsSME(unsigned reg) const;
6060

6161
bool IsSVEZ(unsigned reg) const { return m_register_info_up->IsSVEZReg(reg); }
6262
bool IsSVEP(unsigned reg) const { return m_register_info_up->IsSVEPReg(reg); }
6363
bool IsSVEVG(unsigned reg) const {
6464
return m_register_info_up->IsSVERegVG(reg);
6565
}
66+
bool IsSMEZA(unsigned reg) const {
67+
return m_register_info_up->IsSMERegZA(reg);
68+
}
6669

6770
uint32_t GetRegNumSVEZ0() const {
6871
return m_register_info_up->GetRegNumSVEZ0();

lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm64.cpp

Lines changed: 110 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,13 @@ RegisterContextCorePOSIX_arm64::Create(Thread &thread, const ArchSpec &arch,
2323
llvm::ArrayRef<CoreNote> notes) {
2424
Flags opt_regsets = RegisterInfoPOSIX_arm64::eRegsetMaskDefault;
2525

26+
DataExtractor ssve_data =
27+
getRegset(notes, arch.GetTriple(), AARCH64_SSVE_Desc);
28+
if (ssve_data.GetByteSize() >= sizeof(sve::user_sve_header))
29+
opt_regsets.Set(RegisterInfoPOSIX_arm64::eRegsetMaskSSVE);
30+
2631
DataExtractor sve_data = getRegset(notes, arch.GetTriple(), AARCH64_SVE_Desc);
27-
if (sve_data.GetByteSize() > sizeof(sve::user_sve_header))
32+
if (sve_data.GetByteSize() >= sizeof(sve::user_sve_header))
2833
opt_regsets.Set(RegisterInfoPOSIX_arm64::eRegsetMaskSVE);
2934

3035
// Pointer Authentication register set data is based on struct
@@ -40,6 +45,11 @@ RegisterContextCorePOSIX_arm64::Create(Thread &thread, const ArchSpec &arch,
4045
if (tls_data.GetByteSize() >= sizeof(uint64_t))
4146
opt_regsets.Set(RegisterInfoPOSIX_arm64::eRegsetMaskTLS);
4247

48+
DataExtractor za_data = getRegset(notes, arch.GetTriple(), AARCH64_ZA_Desc);
49+
// Nothing if ZA is not present, just the header if it is disabled.
50+
if (za_data.GetByteSize() >= sizeof(sve::user_za_header))
51+
opt_regsets.Set(RegisterInfoPOSIX_arm64::eRegsetMaskZA);
52+
4353
auto register_info_up =
4454
std::make_unique<RegisterInfoPOSIX_arm64>(arch, opt_regsets);
4555
return std::unique_ptr<RegisterContextCorePOSIX_arm64>(
@@ -51,6 +61,8 @@ RegisterContextCorePOSIX_arm64::RegisterContextCorePOSIX_arm64(
5161
Thread &thread, std::unique_ptr<RegisterInfoPOSIX_arm64> register_info,
5262
const DataExtractor &gpregset, llvm::ArrayRef<CoreNote> notes)
5363
: RegisterContextPOSIX_arm64(thread, std::move(register_info)) {
64+
::memset(&m_sme_pseudo_regs, 0, sizeof(m_sme_pseudo_regs));
65+
5466
m_gpr_data.SetData(std::make_shared<DataBufferHeap>(gpregset.GetDataStart(),
5567
gpregset.GetByteSize()));
5668
m_gpr_data.SetByteOrder(gpregset.GetByteOrder());
@@ -59,7 +71,15 @@ RegisterContextCorePOSIX_arm64::RegisterContextCorePOSIX_arm64(
5971
m_register_info_up->GetTargetArchitecture().GetTriple();
6072
m_fpr_data = getRegset(notes, target_triple, FPR_Desc);
6173

62-
if (m_register_info_up->IsSVEEnabled())
74+
if (m_register_info_up->IsSSVEEnabled()) {
75+
m_sve_data = getRegset(notes, target_triple, AARCH64_SSVE_Desc);
76+
lldb::offset_t flags_offset = 12;
77+
uint16_t flags = m_sve_data.GetU32(&flags_offset);
78+
if ((flags & sve::ptrace_regs_mask) == sve::ptrace_regs_sve)
79+
m_sve_state = SVEState::Streaming;
80+
}
81+
82+
if (m_sve_state != SVEState::Streaming && m_register_info_up->IsSVEEnabled())
6383
m_sve_data = getRegset(notes, target_triple, AARCH64_SVE_Desc);
6484

6585
if (m_register_info_up->IsPAuthEnabled())
@@ -68,6 +88,9 @@ RegisterContextCorePOSIX_arm64::RegisterContextCorePOSIX_arm64(
6888
if (m_register_info_up->IsTLSEnabled())
6989
m_tls_data = getRegset(notes, target_triple, AARCH64_TLS_Desc);
7090

91+
if (m_register_info_up->IsZAEnabled())
92+
m_za_data = getRegset(notes, target_triple, AARCH64_ZA_Desc);
93+
7194
ConfigureRegisterContext();
7295
}
7396

@@ -95,15 +118,18 @@ void RegisterContextCorePOSIX_arm64::ConfigureRegisterContext() {
95118
if (m_sve_data.GetByteSize() > sizeof(sve::user_sve_header)) {
96119
uint64_t sve_header_field_offset = 8;
97120
m_sve_vector_length = m_sve_data.GetU16(&sve_header_field_offset);
98-
sve_header_field_offset = 12;
99-
uint16_t sve_header_flags_field =
100-
m_sve_data.GetU16(&sve_header_field_offset);
101-
if ((sve_header_flags_field & sve::ptrace_regs_mask) ==
102-
sve::ptrace_regs_fpsimd)
103-
m_sve_state = SVEState::FPSIMD;
104-
else if ((sve_header_flags_field & sve::ptrace_regs_mask) ==
105-
sve::ptrace_regs_sve)
106-
m_sve_state = SVEState::Full;
121+
122+
if (m_sve_state != SVEState::Streaming) {
123+
sve_header_field_offset = 12;
124+
uint16_t sve_header_flags_field =
125+
m_sve_data.GetU16(&sve_header_field_offset);
126+
if ((sve_header_flags_field & sve::ptrace_regs_mask) ==
127+
sve::ptrace_regs_fpsimd)
128+
m_sve_state = SVEState::FPSIMD;
129+
else if ((sve_header_flags_field & sve::ptrace_regs_mask) ==
130+
sve::ptrace_regs_sve)
131+
m_sve_state = SVEState::Full;
132+
}
107133

108134
if (!sve::vl_valid(m_sve_vector_length)) {
109135
m_sve_state = SVEState::Disabled;
@@ -115,6 +141,23 @@ void RegisterContextCorePOSIX_arm64::ConfigureRegisterContext() {
115141
if (m_sve_state != SVEState::Disabled)
116142
m_register_info_up->ConfigureVectorLengthSVE(
117143
sve::vq_from_vl(m_sve_vector_length));
144+
145+
if (m_sve_state == SVEState::Streaming)
146+
m_sme_pseudo_regs.ctrl_reg |= 1;
147+
148+
if (m_za_data.GetByteSize() >= sizeof(sve::user_za_header)) {
149+
lldb::offset_t vlen_offset = 8;
150+
uint16_t svl = m_za_data.GetU16(&vlen_offset);
151+
m_sme_pseudo_regs.svg_reg = svl / 8;
152+
m_register_info_up->ConfigureVectorLengthZA(svl / 16);
153+
154+
// If there is register data then ZA is active. The size of the note may be
155+
// misleading here so we use the size field of the embedded header.
156+
lldb::offset_t size_offset = 0;
157+
uint32_t size = m_za_data.GetU32(&size_offset);
158+
if (size > sizeof(sve::user_za_header))
159+
m_sme_pseudo_regs.ctrl_reg |= 1 << 1;
160+
}
118161
}
119162

120163
uint32_t RegisterContextCorePOSIX_arm64::CalculateSVEOffset(
@@ -124,7 +167,8 @@ uint32_t RegisterContextCorePOSIX_arm64::CalculateSVEOffset(
124167
if (m_sve_state == SVEState::FPSIMD) {
125168
const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
126169
sve_reg_offset = sve::ptrace_fpsimd_offset + (reg - GetRegNumSVEZ0()) * 16;
127-
} else if (m_sve_state == SVEState::Full) {
170+
} else if (m_sve_state == SVEState::Full ||
171+
m_sve_state == SVEState::Streaming) {
128172
uint32_t sve_z0_offset = GetGPRSize() + 16;
129173
sve_reg_offset =
130174
sve::SigRegsOffset() + reg_info->byte_offset - sve_z0_offset;
@@ -163,19 +207,19 @@ bool RegisterContextCorePOSIX_arm64::ReadRegister(const RegisterInfo *reg_info,
163207
}
164208
} else {
165209
// FPSR and FPCR will be located right after Z registers in
166-
// SVEState::FPSIMD while in SVEState::Full they will be located at the
167-
// end of register data after an alignment correction based on currently
168-
// selected vector length.
210+
// SVEState::FPSIMD while in SVEState::Full/SVEState::Streaming they will
211+
// be located at the end of register data after an alignment correction
212+
// based on currently selected vector length.
169213
uint32_t sve_reg_num = LLDB_INVALID_REGNUM;
170214
if (reg == GetRegNumFPSR()) {
171215
sve_reg_num = reg;
172-
if (m_sve_state == SVEState::Full)
216+
if (m_sve_state == SVEState::Full || m_sve_state == SVEState::Streaming)
173217
offset = sve::PTraceFPSROffset(sve::vq_from_vl(m_sve_vector_length));
174218
else if (m_sve_state == SVEState::FPSIMD)
175219
offset = sve::ptrace_fpsimd_offset + (32 * 16);
176220
} else if (reg == GetRegNumFPCR()) {
177221
sve_reg_num = reg;
178-
if (m_sve_state == SVEState::Full)
222+
if (m_sve_state == SVEState::Full || m_sve_state == SVEState::Streaming)
179223
offset = sve::PTraceFPCROffset(sve::vq_from_vl(m_sve_vector_length));
180224
else if (m_sve_state == SVEState::FPSIMD)
181225
offset = sve::ptrace_fpsimd_offset + (32 * 16) + 4;
@@ -217,6 +261,7 @@ bool RegisterContextCorePOSIX_arm64::ReadRegister(const RegisterInfo *reg_info,
217261
error);
218262
} break;
219263
case SVEState::Full:
264+
case SVEState::Streaming:
220265
offset = CalculateSVEOffset(reg_info);
221266
assert(offset < m_sve_data.GetByteSize());
222267
value.SetFromMemoryData(*reg_info, GetSVEBuffer(offset),
@@ -237,6 +282,54 @@ bool RegisterContextCorePOSIX_arm64::ReadRegister(const RegisterInfo *reg_info,
237282
assert(offset < m_tls_data.GetByteSize());
238283
value.SetFromMemoryData(*reg_info, m_tls_data.GetDataStart() + offset,
239284
reg_info->byte_size, lldb::eByteOrderLittle, error);
285+
} else if (IsSME(reg)) {
286+
// If you had SME in the process, active or otherwise, there will at least
287+
// be a ZA header. No header, no SME at all.
288+
if (m_za_data.GetByteSize() < sizeof(sve::user_za_header))
289+
return false;
290+
291+
if (!IsSMEZA(reg)) {
292+
offset = reg_info->byte_offset - m_register_info_up->GetSMEOffset();
293+
assert(offset < sizeof(m_sme_pseudo_regs));
294+
// Host endian since these values are derived instead of being read from a
295+
// core file note.
296+
value.SetFromMemoryData(
297+
*reg_info, reinterpret_cast<uint8_t *>(&m_sme_pseudo_regs) + offset,
298+
reg_info->byte_size, lldb_private::endian::InlHostByteOrder(), error);
299+
} else {
300+
// If the process did not have the SME extension.
301+
if (m_za_data.GetByteSize() < sizeof(sve::user_za_header))
302+
return false;
303+
304+
// Don't use the size of the note to tell whether ZA is enabled. There may
305+
// be non-register padding data after the header. Use the embedded
306+
// header's size field instead.
307+
lldb::offset_t size_offset = 0;
308+
uint32_t size = m_za_data.GetU32(&size_offset);
309+
bool za_enabled = size > sizeof(sve::user_za_header);
310+
311+
size_t za_note_size = m_za_data.GetByteSize();
312+
// For a disabled ZA we fake a value of all 0s.
313+
if (!za_enabled) {
314+
uint64_t svl = m_sme_pseudo_regs.svg_reg * 8;
315+
za_note_size = sizeof(sve::user_za_header) + (svl * svl);
316+
}
317+
318+
const uint8_t *src = nullptr;
319+
std::vector<uint8_t> disabled_za_data;
320+
321+
if (za_enabled)
322+
src = m_za_data.GetDataStart();
323+
else {
324+
disabled_za_data.resize(za_note_size);
325+
std::fill(disabled_za_data.begin(), disabled_za_data.end(), 0);
326+
src = disabled_za_data.data();
327+
}
328+
329+
value.SetFromMemoryData(*reg_info, src + sizeof(sve::user_za_header),
330+
reg_info->byte_size, lldb::eByteOrderLittle,
331+
error);
332+
}
240333
} else
241334
return false;
242335

lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm64.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,20 @@ class RegisterContextCorePOSIX_arm64 : public RegisterContextPOSIX_arm64 {
5858
lldb_private::DataExtractor m_sve_data;
5959
lldb_private::DataExtractor m_pac_data;
6060
lldb_private::DataExtractor m_tls_data;
61+
lldb_private::DataExtractor m_za_data;
6162

6263
SVEState m_sve_state;
6364
uint16_t m_sve_vector_length = 0;
6465

66+
// These are pseudo registers derived from the values in SSVE and ZA data.
67+
struct __attribute__((packed)) sme_pseudo_regs {
68+
uint64_t ctrl_reg;
69+
uint64_t svg_reg;
70+
};
71+
static_assert(sizeof(sme_pseudo_regs) == 16);
72+
73+
struct sme_pseudo_regs m_sme_pseudo_regs;
74+
6575
const uint8_t *GetSVEBuffer(uint64_t offset = 0);
6676

6777
void ConfigureRegisterContext();

lldb/source/Plugins/Process/elf-core/RegisterUtilities.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,10 @@ constexpr RegsetDesc AARCH64_SVE_Desc[] = {
119119
{llvm::Triple::Linux, llvm::Triple::aarch64, llvm::ELF::NT_ARM_SVE},
120120
};
121121

122+
constexpr RegsetDesc AARCH64_SSVE_Desc[] = {
123+
{llvm::Triple::Linux, llvm::Triple::aarch64, llvm::ELF::NT_ARM_SSVE},
124+
};
125+
122126
constexpr RegsetDesc AARCH64_ZA_Desc[] = {
123127
{llvm::Triple::Linux, llvm::Triple::aarch64, llvm::ELF::NT_ARM_ZA},
124128
};
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
"""
2+
Check that LLDB can read Scalable Matrix Extension (SME) data from core files.
3+
"""
4+
5+
6+
import lldb
7+
import itertools
8+
from enum import Enum
9+
from lldbsuite.test.decorators import *
10+
from lldbsuite.test.lldbtest import *
11+
12+
13+
class Mode(Enum):
14+
SVE = 0
15+
SSVE = 1
16+
17+
18+
class ZA(Enum):
19+
Disabled = 0
20+
Enabled = 1
21+
22+
23+
class AArch64LinuxSMECoreFileTestCase(TestBase):
24+
NO_DEBUG_INFO_TESTCASE = True
25+
26+
# SME introduces an extra SVE mode "streaming mode" and an array storage
27+
# register "ZA". ZA can be enabled or disabled independent of streaming mode.
28+
# Vector length can also be different between the streaming and non-streaming
29+
# mode. Therefore this test checks a few combinations, but not all.
30+
#
31+
# The numbers in the core file names are options to the crashing program,
32+
# see main.c for their meaning. The test case names will also explain them.
33+
34+
def check_corefile(self, corefile):
35+
self.runCmd("target create --core " + corefile)
36+
37+
_, sve_mode, vl, svl, za = corefile.split("_")
38+
39+
sve_mode = Mode(int(sve_mode))
40+
vl = int(vl)
41+
svl = int(svl)
42+
za = ZA(int(za))
43+
44+
self.expect("register read tpidr2", substrs=["0x1122334455667788"])
45+
46+
# In streaming mode, vg is the same as svg. 'g' is for granule which is
47+
# 8 bytes.
48+
if sve_mode == Mode.SSVE:
49+
self.expect("register read vg", substrs=["0x{:016x}".format(svl // 8)])
50+
else:
51+
self.expect("register read vg", substrs=["0x{:016x}".format(vl // 8)])
52+
53+
# svg is always the streaming mode vector length.
54+
self.expect("register read svg", substrs=["0x{:016x}".format(svl // 8)])
55+
56+
svcr = 1 if sve_mode == Mode.SSVE else 0
57+
if za == ZA.Enabled:
58+
svcr |= 2
59+
self.expect("register read svcr", substrs=["0x{:016x}".format(svcr)])
60+
61+
repeat_bytes = lambda v, n: " ".join(["0x{:02x}".format(v)] * n)
62+
63+
sve_vl = svl if sve_mode == Mode.SSVE else vl
64+
for i in range(0, 32):
65+
# Each element is set to the register number + 1, for example:
66+
# z0 = {0x01 0x01 0x01 ... }
67+
expected = "{{{}}}".format(repeat_bytes(i + 1, sve_vl))
68+
self.expect("register read z{}".format(i), substrs=[expected])
69+
70+
# The P registers cycle between a few values.
71+
# p0 = {0xff 0xff ... }
72+
# p1 = {0x55 0x55 ... }
73+
# ...
74+
# P registers and FFR have 1 bit per byte element in a vector.
75+
p_value = lambda v: "{{{}}}".format(repeat_bytes(v, sve_vl // 8))
76+
expected_p_values = [p_value(v) for v in [0xFF, 0x55, 0x11, 0x01, 0x00]]
77+
expected_p_values = itertools.cycle(expected_p_values)
78+
79+
for i in range(0, 15):
80+
expected = next(expected_p_values)
81+
self.expect("register read p{}".format(i), substrs=[expected])
82+
83+
self.expect(
84+
"register read ffr",
85+
substrs=["{{{}}}".format(repeat_bytes(0xFF, sve_vl // 8))],
86+
)
87+
88+
if za == ZA.Enabled:
89+
# Each row of ZA is set to the row number plus 1. For example:
90+
# za = {0x01 0x01 0x01 0x01 <repeat until end of row> 0x02 0x02 ...
91+
make_row = repeat_bytes
92+
else:
93+
# When ZA is disabled lldb shows it as 0s.
94+
make_row = lambda _, n: repeat_bytes(0, n)
95+
96+
expected_za = "{{{}}}".format(
97+
" ".join([make_row(i + 1, svl) for i in range(svl)])
98+
)
99+
self.expect("register read za", substrs=[expected_za])
100+
101+
@skipIfLLVMTargetMissing("AArch64")
102+
def test_sme_core_file_ssve_vl32_svl16_za_enabled(self):
103+
self.check_corefile("core_1_32_16_1")
104+
105+
@skipIfLLVMTargetMissing("AArch64")
106+
def test_sme_core_file_ssve_vl16_svl32_za_disabled(self):
107+
self.check_corefile("core_1_16_32_0")
108+
109+
@skipIfLLVMTargetMissing("AArch64")
110+
def test_sme_core_file_sve_vl16_svl32_za_enabled(self):
111+
self.check_corefile("core_0_16_32_1")
112+
113+
@skipIfLLVMTargetMissing("AArch64")
114+
def test_sme_core_file_sve_vl32_svl16_za_disabled(self):
115+
self.check_corefile("core_0_32_16_0")
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

0 commit comments

Comments
 (0)