Skip to content

Commit 8f2ffb1

Browse files
committed
[lldb][AArch64] Add type marker to ReadAll/WriteALLRegisterValues data
While working in support for SME's ZA register, I found a circumstance where restoring ZA after SVE, when the current SVE mode is non-streaming, will kick the process back into FPSIMD mode. Meaning the SVE values that you just wrote are now cut off at 128 bit. The fix for that is to write ZA then SVE. Problem with that is, the current ReadAll/WriteAll makes a lot of assumptions about the saved data length. This patch changes the format so there is a "type" written before each data block. This tells WriteAllRegisterValues what it's looking at without brittle checks on length, or assumptions about ordering. If we want to change the order of restoration, all we now have to do is change the order of saving. This exposes a bug where the TLS registers are not restored. This will be fixed by https://reviews.llvm.org/D156512 in some form, depending on what lands first. Existing SVE tests certainly check restoration and when I got this wrong, many, many tests failed. So I think we have enough coverage already, and more will be coming with future ZA changes. Reviewed By: omjavaid Differential Revision: https://reviews.llvm.org/D156687
1 parent 77b7b1a commit 8f2ffb1

File tree

2 files changed

+128
-97
lines changed

2 files changed

+128
-97
lines changed

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

Lines changed: 126 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -499,76 +499,119 @@ Status NativeRegisterContextLinux_arm64::WriteRegister(
499499
return Status("Failed to write register value");
500500
}
501501

502-
Status NativeRegisterContextLinux_arm64::ReadAllRegisterValues(
503-
lldb::WritableDataBufferSP &data_sp) {
504-
// AArch64 register data must contain GPRs and either FPR or SVE registers.
505-
// SVE registers can be non-streaming (aka SVE) or streaming (aka SSVE).
506-
// Finally an optional MTE register. Pointer Authentication (PAC) registers
507-
// are read-only and will be skipped.
502+
enum RegisterSetType : uint32_t {
503+
GPR,
504+
SVE, // Used for SVE and SSVE.
505+
FPR, // When there is no SVE, or SVE in FPSIMD mode.
506+
MTE,
507+
TLS,
508+
};
509+
510+
static uint8_t *AddRegisterSetType(uint8_t *dst,
511+
RegisterSetType register_set_type) {
512+
*(reinterpret_cast<uint32_t *>(dst)) = register_set_type;
513+
return dst + sizeof(uint32_t);
514+
}
508515

509-
// In order to create register data checkpoint we first read all register
510-
// values if not done already and calculate total size of register set data.
511-
// We store all register values in data_sp by copying full PTrace data that
512-
// corresponds to register sets enabled by current register context.
516+
static uint8_t *AddSavedRegistersData(uint8_t *dst, void *src, size_t size) {
517+
::memcpy(dst, src, size);
518+
return dst + size;
519+
}
513520

521+
static uint8_t *AddSavedRegisters(uint8_t *dst,
522+
enum RegisterSetType register_set_type,
523+
void *src, size_t size) {
524+
dst = AddRegisterSetType(dst, register_set_type);
525+
return AddSavedRegistersData(dst, src, size);
526+
}
527+
528+
Status
529+
NativeRegisterContextLinux_arm64::CacheAllRegisters(uint32_t &cached_size) {
514530
Status error;
515-
uint32_t reg_data_byte_size = GetGPRBufferSize();
531+
cached_size = sizeof(RegisterSetType) + GetGPRBufferSize();
516532
error = ReadGPR();
517533
if (error.Fail())
518534
return error;
519535

520536
// If SVE is enabled we need not copy FPR separately.
521537
if (GetRegisterInfo().IsSVEEnabled() || GetRegisterInfo().IsSSVEEnabled()) {
522-
reg_data_byte_size += GetSVEBufferSize();
523-
// Also store the current SVE mode.
524-
reg_data_byte_size += sizeof(uint32_t);
538+
// Store mode and register data.
539+
cached_size +=
540+
sizeof(RegisterSetType) + sizeof(m_sve_state) + GetSVEBufferSize();
525541
error = ReadAllSVE();
526542
} else {
527-
reg_data_byte_size += GetFPRSize();
543+
cached_size += sizeof(RegisterSetType) + GetFPRSize();
528544
error = ReadFPR();
529545
}
530546
if (error.Fail())
531547
return error;
532548

533549
if (GetRegisterInfo().IsMTEEnabled()) {
534-
reg_data_byte_size += GetMTEControlSize();
550+
cached_size += sizeof(RegisterSetType) + GetMTEControlSize();
535551
error = ReadMTEControl();
536552
if (error.Fail())
537553
return error;
538554
}
539555

540556
// tpidr is always present but tpidr2 depends on SME.
541-
reg_data_byte_size += GetTLSBufferSize();
557+
cached_size += sizeof(RegisterSetType) + GetTLSBufferSize();
542558
error = ReadTLS();
559+
560+
return error;
561+
}
562+
563+
Status NativeRegisterContextLinux_arm64::ReadAllRegisterValues(
564+
lldb::WritableDataBufferSP &data_sp) {
565+
// AArch64 register data must contain GPRs and either FPR or SVE registers.
566+
// SVE registers can be non-streaming (aka SVE) or streaming (aka SSVE).
567+
// Finally an optional MTE register. Pointer Authentication (PAC) registers
568+
// are read-only and will be skipped.
569+
570+
// In order to create register data checkpoint we first read all register
571+
// values if not done already and calculate total size of register set data.
572+
// We store all register values in data_sp by copying full PTrace data that
573+
// corresponds to register sets enabled by current register context.
574+
575+
uint32_t reg_data_byte_size = 0;
576+
Status error = CacheAllRegisters(reg_data_byte_size);
543577
if (error.Fail())
544578
return error;
545579

546580
data_sp.reset(new DataBufferHeap(reg_data_byte_size, 0));
547581
uint8_t *dst = data_sp->GetBytes();
548582

549-
::memcpy(dst, GetGPRBuffer(), GetGPRBufferSize());
550-
dst += GetGPRBufferSize();
583+
dst = AddSavedRegisters(dst, RegisterSetType::GPR, GetGPRBuffer(),
584+
GetGPRBufferSize());
551585

552586
if (GetRegisterInfo().IsSVEEnabled() || GetRegisterInfo().IsSSVEEnabled()) {
553-
*dst = static_cast<uint8_t>(m_sve_state);
587+
dst = AddRegisterSetType(dst, RegisterSetType::SVE);
588+
*(reinterpret_cast<SVEState *>(dst)) = m_sve_state;
554589
dst += sizeof(m_sve_state);
555-
::memcpy(dst, GetSVEBuffer(), GetSVEBufferSize());
556-
dst += GetSVEBufferSize();
590+
dst = AddSavedRegistersData(dst, GetSVEBuffer(), GetSVEBufferSize());
557591
} else {
558-
::memcpy(dst, GetFPRBuffer(), GetFPRSize());
559-
dst += GetFPRSize();
592+
dst = AddSavedRegisters(dst, RegisterSetType::FPR, GetFPRBuffer(),
593+
GetFPRSize());
560594
}
561595

562596
if (GetRegisterInfo().IsMTEEnabled()) {
563-
::memcpy(dst, GetMTEControl(), GetMTEControlSize());
564-
dst += GetMTEControlSize();
597+
dst = AddSavedRegisters(dst, RegisterSetType::MTE, GetMTEControl(),
598+
GetMTEControlSize());
565599
}
566600

567-
::memcpy(dst, GetTLSBuffer(), GetTLSBufferSize());
601+
dst = AddSavedRegisters(dst, RegisterSetType::TLS, GetTLSBuffer(),
602+
GetTLSBufferSize());
568603

569604
return error;
570605
}
571606

607+
static Status RestoreRegisters(void *buffer, const uint8_t **src, size_t len,
608+
bool &is_valid, std::function<Status()> writer) {
609+
::memcpy(buffer, *src, len);
610+
is_valid = true;
611+
*src += len;
612+
return writer();
613+
}
614+
572615
Status NativeRegisterContextLinux_arm64::WriteAllRegisterValues(
573616
const lldb::DataBufferSP &data_sp) {
574617
// AArch64 register data must contain GPRs, either FPR or SVE registers
@@ -599,7 +642,8 @@ Status NativeRegisterContextLinux_arm64::WriteAllRegisterValues(
599642
return error;
600643
}
601644

602-
uint64_t reg_data_min_size = GetGPRBufferSize() + GetFPRSize();
645+
uint64_t reg_data_min_size =
646+
GetGPRBufferSize() + GetFPRSize() + 2 * (sizeof(RegisterSetType));
603647
if (data_sp->GetByteSize() < reg_data_min_size) {
604648
error.SetErrorStringWithFormat(
605649
"NativeRegisterContextLinux_arm64::%s data_sp contained insufficient "
@@ -608,82 +652,67 @@ Status NativeRegisterContextLinux_arm64::WriteAllRegisterValues(
608652
return error;
609653
}
610654

611-
// Register data starts with GPRs
612-
::memcpy(GetGPRBuffer(), src, GetGPRBufferSize());
613-
m_gpr_is_valid = true;
614-
615-
error = WriteGPR();
616-
if (error.Fail())
617-
return error;
618-
619-
src += GetGPRBufferSize();
620-
621-
// Verify if register data may contain SVE register values.
622-
bool contains_sve_reg_data =
623-
(data_sp->GetByteSize() > (reg_data_min_size + GetSVEHeaderSize()));
624-
625-
if (contains_sve_reg_data) {
626-
// Restore to the correct mode, streaming or not.
627-
m_sve_state = static_cast<SVEState>(*src);
628-
src += sizeof(m_sve_state);
629-
630-
// We have SVE register data first write SVE header.
631-
::memcpy(GetSVEHeader(), src, GetSVEHeaderSize());
632-
if (!sve::vl_valid(m_sve_header.vl)) {
633-
m_sve_header_is_valid = false;
634-
error.SetErrorStringWithFormat("NativeRegisterContextLinux_arm64::%s "
635-
"Invalid SVE header in data_sp",
636-
__FUNCTION__);
637-
return error;
638-
}
639-
m_sve_header_is_valid = true;
640-
error = WriteSVEHeader();
641-
if (error.Fail())
642-
return error;
643-
644-
// SVE header has been written configure SVE vector length if needed.
645-
ConfigureRegisterContext();
655+
const uint8_t *end = src + data_sp->GetByteSize();
656+
while (src < end) {
657+
const RegisterSetType kind =
658+
*reinterpret_cast<const RegisterSetType *>(src);
659+
src += sizeof(RegisterSetType);
660+
661+
switch (kind) {
662+
case RegisterSetType::GPR:
663+
error = RestoreRegisters(
664+
GetGPRBuffer(), &src, GetGPRBufferSize(), m_gpr_is_valid,
665+
std::bind(&NativeRegisterContextLinux_arm64::WriteGPR, this));
666+
break;
667+
case RegisterSetType::SVE:
668+
// Restore to the correct mode, streaming or not.
669+
m_sve_state = static_cast<SVEState>(*src);
670+
src += sizeof(m_sve_state);
671+
672+
// First write SVE header. We do not use RestoreRegisters because we do
673+
// not want src to be modified yet.
674+
::memcpy(GetSVEHeader(), src, GetSVEHeaderSize());
675+
if (!sve::vl_valid(m_sve_header.vl)) {
676+
m_sve_header_is_valid = false;
677+
error.SetErrorStringWithFormat("NativeRegisterContextLinux_arm64::%s "
678+
"Invalid SVE header in data_sp",
679+
__FUNCTION__);
680+
return error;
681+
}
682+
m_sve_header_is_valid = true;
683+
error = WriteSVEHeader();
684+
if (error.Fail())
685+
return error;
646686

647-
// Make sure data_sp contains sufficient data to write all SVE registers.
648-
reg_data_min_size = GetGPRBufferSize() + GetSVEBufferSize();
649-
if (data_sp->GetByteSize() < reg_data_min_size) {
650-
error.SetErrorStringWithFormat(
651-
"NativeRegisterContextLinux_arm64::%s data_sp contained insufficient "
652-
"register data bytes, expected %" PRIu64 ", actual %" PRIu64,
653-
__FUNCTION__, reg_data_min_size, data_sp->GetByteSize());
654-
return error;
687+
// SVE header has been written configure SVE vector length if needed.
688+
ConfigureRegisterContext();
689+
690+
// Write header and register data, incrementing src this time.
691+
error = RestoreRegisters(
692+
GetSVEBuffer(), &src, GetSVEBufferSize(), m_sve_buffer_is_valid,
693+
std::bind(&NativeRegisterContextLinux_arm64::WriteAllSVE, this));
694+
break;
695+
case RegisterSetType::FPR:
696+
error = RestoreRegisters(
697+
GetFPRBuffer(), &src, GetFPRSize(), m_fpu_is_valid,
698+
std::bind(&NativeRegisterContextLinux_arm64::WriteFPR, this));
699+
break;
700+
case RegisterSetType::MTE:
701+
error = RestoreRegisters(
702+
GetMTEControl(), &src, GetMTEControlSize(), m_mte_ctrl_is_valid,
703+
std::bind(&NativeRegisterContextLinux_arm64::WriteMTEControl, this));
704+
break;
705+
case RegisterSetType::TLS:
706+
error = RestoreRegisters(
707+
GetTLSBuffer(), &src, GetTLSBufferSize(), m_tls_is_valid,
708+
std::bind(&NativeRegisterContextLinux_arm64::WriteTLS, this));
709+
break;
655710
}
656711

657-
::memcpy(GetSVEBuffer(), src, GetSVEBufferSize());
658-
m_sve_buffer_is_valid = true;
659-
error = WriteAllSVE();
660-
src += GetSVEBufferSize();
661-
} else {
662-
::memcpy(GetFPRBuffer(), src, GetFPRSize());
663-
m_fpu_is_valid = true;
664-
error = WriteFPR();
665-
src += GetFPRSize();
666-
}
667-
668-
if (error.Fail())
669-
return error;
670-
671-
if (GetRegisterInfo().IsMTEEnabled() &&
672-
data_sp->GetByteSize() > reg_data_min_size) {
673-
::memcpy(GetMTEControl(), src, GetMTEControlSize());
674-
m_mte_ctrl_is_valid = true;
675-
error = WriteMTEControl();
676712
if (error.Fail())
677713
return error;
678-
src += GetMTEControlSize();
679714
}
680715

681-
// There is always a TLS set. It changes size based on system properties, it's
682-
// not something an expression can change.
683-
::memcpy(GetTLSBuffer(), src, GetTLSBufferSize());
684-
m_tls_is_valid = true;
685-
error = WriteTLS();
686-
687716
return error;
688717
}
689718

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,8 @@ class NativeRegisterContextLinux_arm64
181181
void ConfigureRegisterContext();
182182

183183
uint32_t CalculateSVEOffset(const RegisterInfo *reg_info) const;
184+
185+
Status CacheAllRegisters(uint32_t &cached_size);
184186
};
185187

186188
} // namespace process_linux

0 commit comments

Comments
 (0)