diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h --- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h +++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h @@ -181,6 +181,8 @@ void ConfigureRegisterContext(); uint32_t CalculateSVEOffset(const RegisterInfo *reg_info) const; + + Status CacheAllRegisters(uint32_t &cached_size); }; } // namespace process_linux diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp --- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp +++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp @@ -499,76 +499,119 @@ return Status("Failed to write register value"); } -Status NativeRegisterContextLinux_arm64::ReadAllRegisterValues( - lldb::WritableDataBufferSP &data_sp) { - // AArch64 register data must contain GPRs and either FPR or SVE registers. - // SVE registers can be non-streaming (aka SVE) or streaming (aka SSVE). - // Finally an optional MTE register. Pointer Authentication (PAC) registers - // are read-only and will be skipped. +enum RegisterSetType : uint32_t { + GPR, + SVE, // Used for SVE and SSVE. + FPR, // When there is no SVE, or SVE in FPSIMD mode. + MTE, + TLS, +}; + +static uint8_t *AddRegisterSetType(uint8_t *dst, + RegisterSetType register_set_type) { + *(reinterpret_cast(dst)) = register_set_type; + return dst + sizeof(uint32_t); +} - // In order to create register data checkpoint we first read all register - // values if not done already and calculate total size of register set data. - // We store all register values in data_sp by copying full PTrace data that - // corresponds to register sets enabled by current register context. +static uint8_t *AddSavedRegistersData(uint8_t *dst, void *src, size_t size) { + ::memcpy(dst, src, size); + return dst + size; +} +static uint8_t *AddSavedRegisters(uint8_t *dst, + enum RegisterSetType register_set_type, + void *src, size_t size) { + dst = AddRegisterSetType(dst, register_set_type); + return AddSavedRegistersData(dst, src, size); +} + +Status +NativeRegisterContextLinux_arm64::CacheAllRegisters(uint32_t &cached_size) { Status error; - uint32_t reg_data_byte_size = GetGPRBufferSize(); + cached_size = sizeof(RegisterSetType) + GetGPRBufferSize(); error = ReadGPR(); if (error.Fail()) return error; // If SVE is enabled we need not copy FPR separately. if (GetRegisterInfo().IsSVEEnabled() || GetRegisterInfo().IsSSVEEnabled()) { - reg_data_byte_size += GetSVEBufferSize(); - // Also store the current SVE mode. - reg_data_byte_size += sizeof(uint32_t); + // Store mode and register data. + cached_size += + sizeof(RegisterSetType) + sizeof(m_sve_state) + GetSVEBufferSize(); error = ReadAllSVE(); } else { - reg_data_byte_size += GetFPRSize(); + cached_size += sizeof(RegisterSetType) + GetFPRSize(); error = ReadFPR(); } if (error.Fail()) return error; if (GetRegisterInfo().IsMTEEnabled()) { - reg_data_byte_size += GetMTEControlSize(); + cached_size += sizeof(RegisterSetType) + GetMTEControlSize(); error = ReadMTEControl(); if (error.Fail()) return error; } // tpidr is always present but tpidr2 depends on SME. - reg_data_byte_size += GetTLSBufferSize(); + cached_size += sizeof(RegisterSetType) + GetTLSBufferSize(); error = ReadTLS(); + + return error; +} + +Status NativeRegisterContextLinux_arm64::ReadAllRegisterValues( + lldb::WritableDataBufferSP &data_sp) { + // AArch64 register data must contain GPRs and either FPR or SVE registers. + // SVE registers can be non-streaming (aka SVE) or streaming (aka SSVE). + // Finally an optional MTE register. Pointer Authentication (PAC) registers + // are read-only and will be skipped. + + // In order to create register data checkpoint we first read all register + // values if not done already and calculate total size of register set data. + // We store all register values in data_sp by copying full PTrace data that + // corresponds to register sets enabled by current register context. + + uint32_t reg_data_byte_size = 0; + Status error = CacheAllRegisters(reg_data_byte_size); if (error.Fail()) return error; data_sp.reset(new DataBufferHeap(reg_data_byte_size, 0)); uint8_t *dst = data_sp->GetBytes(); - ::memcpy(dst, GetGPRBuffer(), GetGPRBufferSize()); - dst += GetGPRBufferSize(); + dst = AddSavedRegisters(dst, RegisterSetType::GPR, GetGPRBuffer(), + GetGPRBufferSize()); if (GetRegisterInfo().IsSVEEnabled() || GetRegisterInfo().IsSSVEEnabled()) { - *dst = static_cast(m_sve_state); + dst = AddRegisterSetType(dst, RegisterSetType::SVE); + *(reinterpret_cast(dst)) = m_sve_state; dst += sizeof(m_sve_state); - ::memcpy(dst, GetSVEBuffer(), GetSVEBufferSize()); - dst += GetSVEBufferSize(); + dst = AddSavedRegistersData(dst, GetSVEBuffer(), GetSVEBufferSize()); } else { - ::memcpy(dst, GetFPRBuffer(), GetFPRSize()); - dst += GetFPRSize(); + dst = AddSavedRegisters(dst, RegisterSetType::FPR, GetFPRBuffer(), + GetFPRSize()); } if (GetRegisterInfo().IsMTEEnabled()) { - ::memcpy(dst, GetMTEControl(), GetMTEControlSize()); - dst += GetMTEControlSize(); + dst = AddSavedRegisters(dst, RegisterSetType::MTE, GetMTEControl(), + GetMTEControlSize()); } - ::memcpy(dst, GetTLSBuffer(), GetTLSBufferSize()); + dst = AddSavedRegisters(dst, RegisterSetType::TLS, GetTLSBuffer(), + GetTLSBufferSize()); return error; } +static Status RestoreRegisters(void *buffer, const uint8_t **src, size_t len, + bool &is_valid, std::function writer) { + ::memcpy(buffer, *src, len); + is_valid = true; + *src += len; + return writer(); +} + Status NativeRegisterContextLinux_arm64::WriteAllRegisterValues( const lldb::DataBufferSP &data_sp) { // AArch64 register data must contain GPRs, either FPR or SVE registers @@ -599,7 +642,8 @@ return error; } - uint64_t reg_data_min_size = GetGPRBufferSize() + GetFPRSize(); + uint64_t reg_data_min_size = + GetGPRBufferSize() + GetFPRSize() + 2 * (sizeof(RegisterSetType)); if (data_sp->GetByteSize() < reg_data_min_size) { error.SetErrorStringWithFormat( "NativeRegisterContextLinux_arm64::%s data_sp contained insufficient " @@ -608,82 +652,67 @@ return error; } - // Register data starts with GPRs - ::memcpy(GetGPRBuffer(), src, GetGPRBufferSize()); - m_gpr_is_valid = true; - - error = WriteGPR(); - if (error.Fail()) - return error; - - src += GetGPRBufferSize(); - - // Verify if register data may contain SVE register values. - bool contains_sve_reg_data = - (data_sp->GetByteSize() > (reg_data_min_size + GetSVEHeaderSize())); - - if (contains_sve_reg_data) { - // Restore to the correct mode, streaming or not. - m_sve_state = static_cast(*src); - src += sizeof(m_sve_state); - - // We have SVE register data first write SVE header. - ::memcpy(GetSVEHeader(), src, GetSVEHeaderSize()); - if (!sve::vl_valid(m_sve_header.vl)) { - m_sve_header_is_valid = false; - error.SetErrorStringWithFormat("NativeRegisterContextLinux_arm64::%s " - "Invalid SVE header in data_sp", - __FUNCTION__); - return error; - } - m_sve_header_is_valid = true; - error = WriteSVEHeader(); - if (error.Fail()) - return error; - - // SVE header has been written configure SVE vector length if needed. - ConfigureRegisterContext(); + const uint8_t *end = src + data_sp->GetByteSize(); + while (src < end) { + const RegisterSetType kind = + *reinterpret_cast(src); + src += sizeof(RegisterSetType); + + switch (kind) { + case RegisterSetType::GPR: + error = RestoreRegisters( + GetGPRBuffer(), &src, GetGPRBufferSize(), m_gpr_is_valid, + std::bind(&NativeRegisterContextLinux_arm64::WriteGPR, this)); + break; + case RegisterSetType::SVE: + // Restore to the correct mode, streaming or not. + m_sve_state = static_cast(*src); + src += sizeof(m_sve_state); + + // First write SVE header. We do not use RestoreRegisters because we do + // not want src to be modified yet. + ::memcpy(GetSVEHeader(), src, GetSVEHeaderSize()); + if (!sve::vl_valid(m_sve_header.vl)) { + m_sve_header_is_valid = false; + error.SetErrorStringWithFormat("NativeRegisterContextLinux_arm64::%s " + "Invalid SVE header in data_sp", + __FUNCTION__); + return error; + } + m_sve_header_is_valid = true; + error = WriteSVEHeader(); + if (error.Fail()) + return error; - // Make sure data_sp contains sufficient data to write all SVE registers. - reg_data_min_size = GetGPRBufferSize() + GetSVEBufferSize(); - if (data_sp->GetByteSize() < reg_data_min_size) { - error.SetErrorStringWithFormat( - "NativeRegisterContextLinux_arm64::%s data_sp contained insufficient " - "register data bytes, expected %" PRIu64 ", actual %" PRIu64, - __FUNCTION__, reg_data_min_size, data_sp->GetByteSize()); - return error; + // SVE header has been written configure SVE vector length if needed. + ConfigureRegisterContext(); + + // Write header and register data, incrementing src this time. + error = RestoreRegisters( + GetSVEBuffer(), &src, GetSVEBufferSize(), m_sve_buffer_is_valid, + std::bind(&NativeRegisterContextLinux_arm64::WriteAllSVE, this)); + break; + case RegisterSetType::FPR: + error = RestoreRegisters( + GetFPRBuffer(), &src, GetFPRSize(), m_fpu_is_valid, + std::bind(&NativeRegisterContextLinux_arm64::WriteFPR, this)); + break; + case RegisterSetType::MTE: + error = RestoreRegisters( + GetMTEControl(), &src, GetMTEControlSize(), m_mte_ctrl_is_valid, + std::bind(&NativeRegisterContextLinux_arm64::WriteMTEControl, this)); + break; + case RegisterSetType::TLS: + error = RestoreRegisters( + GetTLSBuffer(), &src, GetTLSBufferSize(), m_tls_is_valid, + std::bind(&NativeRegisterContextLinux_arm64::WriteTLS, this)); + break; } - ::memcpy(GetSVEBuffer(), src, GetSVEBufferSize()); - m_sve_buffer_is_valid = true; - error = WriteAllSVE(); - src += GetSVEBufferSize(); - } else { - ::memcpy(GetFPRBuffer(), src, GetFPRSize()); - m_fpu_is_valid = true; - error = WriteFPR(); - src += GetFPRSize(); - } - - if (error.Fail()) - return error; - - if (GetRegisterInfo().IsMTEEnabled() && - data_sp->GetByteSize() > reg_data_min_size) { - ::memcpy(GetMTEControl(), src, GetMTEControlSize()); - m_mte_ctrl_is_valid = true; - error = WriteMTEControl(); if (error.Fail()) return error; - src += GetMTEControlSize(); } - // There is always a TLS set. It changes size based on system properties, it's - // not something an expression can change. - ::memcpy(GetTLSBuffer(), src, GetTLSBufferSize()); - m_tls_is_valid = true; - error = WriteTLS(); - return error; }