mirror of
https://github.com/intel/llvm.git
synced 2026-01-22 23:49:22 +08:00
[lldb][AArch64] Read SME2's ZT0 register from Linux core files (#70934)
The ZT0 register is always 64 bytes in size so it is a lot easier to handle than ZA which is scalable. In addition, reading an inactive ZT0 via ptrace returns all 0s, unlike ZA which returns no register data. This means that a corefile from a process where ZA and ZT0 were inactive still contains an NT_ARM_ZT note and we can simply say that if it's there, then we should be able to read from it. Along the way I removed a redundant check on the size of the ZA note. If that note's size is < the ZA header size, we do not have SME, and therefore could not have SME2 either. I have added ZT0 to the existing SME core files tests. This means that you need an SME2 system to generate them (Arm's FVP at this point). I think this is a fair tradeoff given that this is all running in simulation anyway and seperate ZT0 tests would be 99% identical copies of the ZA only tests.
This commit is contained in:
@@ -54,6 +54,13 @@ RegisterContextCorePOSIX_arm64::Create(Thread &thread, const ArchSpec &arch,
|
||||
if (mte_data.GetByteSize() >= sizeof(uint64_t))
|
||||
opt_regsets.Set(RegisterInfoPOSIX_arm64::eRegsetMaskMTE);
|
||||
|
||||
DataExtractor zt_data = getRegset(notes, arch.GetTriple(), AARCH64_ZT_Desc);
|
||||
// Although ZT0 can be in a disabled state like ZA can, the kernel reports
|
||||
// its content as 0s in that state. Therefore even a disabled ZT0 will have
|
||||
// a note containing those 0s. ZT0 is a 512 bit / 64 byte register.
|
||||
if (zt_data.GetByteSize() >= 64)
|
||||
opt_regsets.Set(RegisterInfoPOSIX_arm64::eRegsetMaskZT);
|
||||
|
||||
auto register_info_up =
|
||||
std::make_unique<RegisterInfoPOSIX_arm64>(arch, opt_regsets);
|
||||
return std::unique_ptr<RegisterContextCorePOSIX_arm64>(
|
||||
@@ -98,6 +105,9 @@ RegisterContextCorePOSIX_arm64::RegisterContextCorePOSIX_arm64(
|
||||
if (m_register_info_up->IsMTEPresent())
|
||||
m_mte_data = getRegset(notes, target_triple, AARCH64_MTE_Desc);
|
||||
|
||||
if (m_register_info_up->IsZTPresent())
|
||||
m_zt_data = getRegset(notes, target_triple, AARCH64_ZT_Desc);
|
||||
|
||||
ConfigureRegisterContext();
|
||||
}
|
||||
|
||||
@@ -298,19 +308,7 @@ bool RegisterContextCorePOSIX_arm64::ReadRegister(const RegisterInfo *reg_info,
|
||||
if (m_za_data.GetByteSize() < sizeof(sve::user_za_header))
|
||||
return false;
|
||||
|
||||
if (!IsSMEZA(reg)) {
|
||||
offset = reg_info->byte_offset - m_register_info_up->GetSMEOffset();
|
||||
assert(offset < sizeof(m_sme_pseudo_regs));
|
||||
// Host endian since these values are derived instead of being read from a
|
||||
// core file note.
|
||||
value.SetFromMemoryData(
|
||||
*reg_info, reinterpret_cast<uint8_t *>(&m_sme_pseudo_regs) + offset,
|
||||
reg_info->byte_size, lldb_private::endian::InlHostByteOrder(), error);
|
||||
} else {
|
||||
// If the process did not have the SME extension.
|
||||
if (m_za_data.GetByteSize() < sizeof(sve::user_za_header))
|
||||
return false;
|
||||
|
||||
if (m_register_info_up->IsSMERegZA(reg)) {
|
||||
// Don't use the size of the note to tell whether ZA is enabled. There may
|
||||
// be non-register padding data after the header. Use the embedded
|
||||
// header's size field instead.
|
||||
@@ -339,6 +337,18 @@ bool RegisterContextCorePOSIX_arm64::ReadRegister(const RegisterInfo *reg_info,
|
||||
value.SetFromMemoryData(*reg_info, src + sizeof(sve::user_za_header),
|
||||
reg_info->byte_size, lldb::eByteOrderLittle,
|
||||
error);
|
||||
} else if (m_register_info_up->IsSMERegZT(reg)) {
|
||||
value.SetFromMemoryData(*reg_info, m_zt_data.GetDataStart(),
|
||||
reg_info->byte_size, lldb::eByteOrderLittle,
|
||||
error);
|
||||
} else {
|
||||
offset = reg_info->byte_offset - m_register_info_up->GetSMEOffset();
|
||||
assert(offset < sizeof(m_sme_pseudo_regs));
|
||||
// Host endian since these values are derived instead of being read from a
|
||||
// core file note.
|
||||
value.SetFromMemoryData(
|
||||
*reg_info, reinterpret_cast<uint8_t *>(&m_sme_pseudo_regs) + offset,
|
||||
reg_info->byte_size, lldb_private::endian::InlHostByteOrder(), error);
|
||||
}
|
||||
} else
|
||||
return false;
|
||||
|
||||
@@ -60,6 +60,7 @@ private:
|
||||
lldb_private::DataExtractor m_tls_data;
|
||||
lldb_private::DataExtractor m_za_data;
|
||||
lldb_private::DataExtractor m_mte_data;
|
||||
lldb_private::DataExtractor m_zt_data;
|
||||
|
||||
SVEState m_sve_state = SVEState::Unknown;
|
||||
uint16_t m_sve_vector_length = 0;
|
||||
|
||||
@@ -89,14 +89,19 @@ class AArch64LinuxSMECoreFileTestCase(TestBase):
|
||||
# Each row of ZA is set to the row number plus 1. For example:
|
||||
# za = {0x01 0x01 0x01 0x01 <repeat until end of row> 0x02 0x02 ...
|
||||
make_row = repeat_bytes
|
||||
expected_zt0 = "{{{}}}".format(
|
||||
" ".join(["0x{:02x}".format(i + 1) for i in range(512 // 8)])
|
||||
)
|
||||
else:
|
||||
# When ZA is disabled lldb shows it as 0s.
|
||||
make_row = lambda _, n: repeat_bytes(0, n)
|
||||
expected_zt0 = "{{{}}}".format(" ".join(["0x00" for i in range(512 // 8)]))
|
||||
|
||||
expected_za = "{{{}}}".format(
|
||||
" ".join([make_row(i + 1, svl) for i in range(svl)])
|
||||
)
|
||||
self.expect("register read za", substrs=[expected_za])
|
||||
self.expect("register read zt0", substrs=[expected_zt0])
|
||||
|
||||
@skipIfLLVMTargetMissing("AArch64")
|
||||
def test_sme_core_file_ssve_vl32_svl16_za_enabled(self):
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
9
lldb/test/API/linux/aarch64/sme_core_file/generate.sh
Normal file
9
lldb/test/API/linux/aarch64/sme_core_file/generate.sh
Normal file
@@ -0,0 +1,9 @@
|
||||
run () {
|
||||
./a.out "$@"
|
||||
mv core core_$(echo "$*" | sed 's/ /_/g')
|
||||
}
|
||||
|
||||
run 0 16 32 1
|
||||
run 0 32 16 0
|
||||
run 1 16 32 0
|
||||
run 1 32 16 1
|
||||
@@ -1,16 +1,21 @@
|
||||
// clang-format off
|
||||
// Compile with:
|
||||
// clang -target aarch64-unknown-linux-gnu main.c -o a.out -g -march=armv8.6-a+sve+sme
|
||||
// clang -target aarch64-unknown-linux-gnu main.c -o a.out -g -march=armv8.6-a+sve+sme+sme2
|
||||
//
|
||||
// For minimal corefile size, do this before running the program:
|
||||
// echo 0x20 > /proc/self/coredeump_filter
|
||||
// echo 0x20 > /proc/self/coredump_filter
|
||||
//
|
||||
// Must be run on a system that has SVE and SME, including the smefa64
|
||||
// extension. Example command:
|
||||
// main 0 32 64 1
|
||||
// Must be run on a system that has SVE, SME and SME2, including the smefa64
|
||||
// extension.
|
||||
//
|
||||
// Example command:
|
||||
// ./a.out 0 32 64 1
|
||||
//
|
||||
// This would not enter streaming mode, set non-streaming VL to 32
|
||||
// bytes, streaming VL to 64 bytes and enable ZA.
|
||||
// bytes, streaming VL to 64 bytes and enable ZA and ZT0.
|
||||
//
|
||||
// To generate all the test files, use the generate.sh script that's in this
|
||||
// folder.
|
||||
// clang-format on
|
||||
|
||||
#include <stdbool.h>
|
||||
@@ -94,6 +99,17 @@ void set_za_register(int streaming_vl) {
|
||||
"r"(&data)
|
||||
: "w12");
|
||||
}
|
||||
#undef MAX_VL_BYTES
|
||||
}
|
||||
|
||||
void set_zt0_register() {
|
||||
#define ZTO_LEN (512 / 8)
|
||||
uint8_t data[ZTO_LEN];
|
||||
for (unsigned i = 0; i < ZTO_LEN; ++i)
|
||||
data[i] = i + 1;
|
||||
|
||||
asm volatile("ldr zt0, [%0]" ::"r"(&data));
|
||||
#undef ZT0_LEN
|
||||
}
|
||||
|
||||
void set_tpidr2(uint64_t value) {
|
||||
@@ -132,6 +148,7 @@ int main(int argc, char **argv) {
|
||||
if (strcmp(argv[4], "1") == 0) {
|
||||
SMSTART_ZA;
|
||||
set_za_register(streaming_vl);
|
||||
set_zt0_register();
|
||||
}
|
||||
|
||||
*(volatile char *)(0) = 0; // Crashes here.
|
||||
|
||||
Reference in New Issue
Block a user