| From 8901f8beea3a70f92be8c0b80313260502f03727 Mon Sep 17 00:00:00 2001 |
| From: Muhammad Omair Javaid <omair.javaid@linaro.org> |
| Date: Thu, 9 Sep 2021 16:04:43 +0500 |
| Subject: [PATCH] AArch64 SVE restore SVE registers after expression |
| |
| This patch fixes register save/restore on expression call to also include SVE registers. |
| |
| This will fix expression calls like: |
| |
| re re p1 |
| |
| <Register Value P1 before expression> |
| |
| p <var-name or function call> |
| |
| re re p1 |
| |
| <Register Value P1 after expression> |
| |
| In above example register P1 should remain the same before and after the expression evaluation. |
| |
| Reviewed By: DavidSpickett |
| |
| Differential Revision: https://reviews.llvm.org/D108739 |
| --- |
| .../NativeRegisterContextLinux_arm64.cpp | 142 ++++++++++++++---- |
| .../Linux/NativeRegisterContextLinux_arm64.h | 2 +- |
| .../Process/Utility/RegisterInfoPOSIX_arm64.h | 1 + |
| .../TestSVERegisters.py | 91 ++++++----- |
| .../rw_access_static_config/main.c | 19 ++- |
| 5 files changed, 189 insertions(+), 66 deletions(-) |
| |
| diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp |
| index ebde0a499acf..f28bddcb9a99 100644 |
| --- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp |
| +++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp |
| @@ -46,8 +46,6 @@ |
| #define HWCAP_PACA (1 << 30) |
| #define HWCAP2_MTE (1 << 18) |
| |
| -#define REG_CONTEXT_SIZE (GetGPRSize() + GetFPRSize()) |
| - |
| using namespace lldb; |
| using namespace lldb_private; |
| using namespace lldb_private::process_linux; |
| @@ -452,30 +450,73 @@ Status NativeRegisterContextLinux_arm64::WriteRegister( |
| |
| Status NativeRegisterContextLinux_arm64::ReadAllRegisterValues( |
| lldb::DataBufferSP &data_sp) { |
| - Status error; |
| + // AArch64 register data must contain GPRs, either FPR or SVE registers |
| + // and optional MTE register. Pointer Authentication (PAC) registers are |
| + // read-only and will be skiped. |
| |
| - data_sp.reset(new DataBufferHeap(REG_CONTEXT_SIZE, 0)); |
| + // 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. |
| |
| + Status error; |
| + uint32_t reg_data_byte_size = GetGPRBufferSize(); |
| error = ReadGPR(); |
| if (error.Fail()) |
| return error; |
| |
| - error = ReadFPR(); |
| + // If SVE is enabled we need not copy FPR separately. |
| + if (GetRegisterInfo().IsSVEEnabled()) { |
| + reg_data_byte_size += GetSVEBufferSize(); |
| + error = ReadAllSVE(); |
| + } else { |
| + reg_data_byte_size += GetFPRSize(); |
| + error = ReadFPR(); |
| + } |
| if (error.Fail()) |
| return error; |
| |
| + if (GetRegisterInfo().IsMTEEnabled()) { |
| + reg_data_byte_size += GetMTEControlSize(); |
| + error = ReadMTEControl(); |
| + if (error.Fail()) |
| + return error; |
| + } |
| + |
| + data_sp.reset(new DataBufferHeap(reg_data_byte_size, 0)); |
| uint8_t *dst = data_sp->GetBytes(); |
| - ::memcpy(dst, GetGPRBuffer(), GetGPRSize()); |
| - dst += GetGPRSize(); |
| - ::memcpy(dst, GetFPRBuffer(), GetFPRSize()); |
| + |
| + ::memcpy(dst, GetGPRBuffer(), GetGPRBufferSize()); |
| + dst += GetGPRBufferSize(); |
| + |
| + if (GetRegisterInfo().IsSVEEnabled()) { |
| + ::memcpy(dst, GetSVEBuffer(), GetSVEBufferSize()); |
| + dst += GetSVEBufferSize(); |
| + } else { |
| + ::memcpy(dst, GetFPRBuffer(), GetFPRSize()); |
| + dst += GetFPRSize(); |
| + } |
| + |
| + if (GetRegisterInfo().IsMTEEnabled()) |
| + ::memcpy(dst, GetMTEControl(), GetMTEControlSize()); |
| |
| return error; |
| } |
| |
| Status NativeRegisterContextLinux_arm64::WriteAllRegisterValues( |
| const lldb::DataBufferSP &data_sp) { |
| - Status error; |
| + // AArch64 register data must contain GPRs, either FPR or SVE registers |
| + // and optional MTE register. Pointer Authentication (PAC) registers are |
| + // read-only and will be skiped. |
| + |
| + // We store all register values in data_sp by copying full PTrace data that |
| + // corresponds to register sets enabled by current register context. In order |
| + // to restore from register data checkpoint we will first restore GPRs, based |
| + // on size of remaining register data either SVE or FPRs should be restored |
| + // next. SVE is not enabled if we have register data size less than or equal |
| + // to size of GPR + FPR + MTE. |
| |
| + Status error; |
| if (!data_sp) { |
| error.SetErrorStringWithFormat( |
| "NativeRegisterContextLinux_arm64::%s invalid data_sp provided", |
| @@ -483,14 +524,6 @@ Status NativeRegisterContextLinux_arm64::WriteAllRegisterValues( |
| return error; |
| } |
| |
| - if (data_sp->GetByteSize() != REG_CONTEXT_SIZE) { |
| - error.SetErrorStringWithFormat( |
| - "NativeRegisterContextLinux_arm64::%s data_sp contained mismatched " |
| - "data size, expected %" PRIu64 ", actual %" PRIu64, |
| - __FUNCTION__, REG_CONTEXT_SIZE, data_sp->GetByteSize()); |
| - return error; |
| - } |
| - |
| uint8_t *src = data_sp->GetBytes(); |
| if (src == nullptr) { |
| error.SetErrorStringWithFormat("NativeRegisterContextLinux_arm64::%s " |
| @@ -499,19 +532,79 @@ Status NativeRegisterContextLinux_arm64::WriteAllRegisterValues( |
| __FUNCTION__); |
| return error; |
| } |
| - ::memcpy(GetGPRBuffer(), src, GetRegisterInfoInterface().GetGPRSize()); |
| + |
| + uint64_t reg_data_min_size = GetGPRBufferSize() + GetFPRSize(); |
| + if (data_sp->GetByteSize() < reg_data_min_size) { |
| + error.SetErrorStringWithFormat( |
| + "NativeRegisterContextLinux_arm64::%s data_sp contained insufficient " |
| + "register data bytes, expected at least %" PRIu64 ", actual %" PRIu64, |
| + __FUNCTION__, reg_data_min_size, data_sp->GetByteSize()); |
| + return error; |
| + } |
| + |
| + // Register data starts with GPRs |
| + ::memcpy(GetGPRBuffer(), src, GetGPRBufferSize()); |
| + m_gpr_is_valid = true; |
| |
| error = WriteGPR(); |
| if (error.Fail()) |
| return error; |
| |
| - src += GetRegisterInfoInterface().GetGPRSize(); |
| - ::memcpy(GetFPRBuffer(), src, GetFPRSize()); |
| + 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) { |
| + // 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(); |
| + |
| + // 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; |
| + } |
| + |
| + ::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(); |
| + } |
| |
| - error = WriteFPR(); |
| 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(); |
| + } |
| + |
| return error; |
| } |
| |
| @@ -864,13 +957,6 @@ uint32_t NativeRegisterContextLinux_arm64::CalculateSVEOffset( |
| return sve_reg_offset; |
| } |
| |
| -void *NativeRegisterContextLinux_arm64::GetSVEBuffer() { |
| - if (m_sve_state == SVEState::FPSIMD) |
| - return m_sve_ptrace_payload.data() + sve::ptrace_fpsimd_offset; |
| - |
| - return m_sve_ptrace_payload.data(); |
| -} |
| - |
| std::vector<uint32_t> NativeRegisterContextLinux_arm64::GetExpeditedRegisters( |
| ExpeditedRegs expType) const { |
| std::vector<uint32_t> expedited_reg_nums = |
| diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h |
| index 4dfc78b5b282..2f8a4a601181 100644 |
| --- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h |
| +++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h |
| @@ -139,7 +139,7 @@ private: |
| |
| void *GetMTEControl() { return &m_mte_ctrl_reg; } |
| |
| - void *GetSVEBuffer(); |
| + void *GetSVEBuffer() { return m_sve_ptrace_payload.data(); }; |
| |
| size_t GetSVEHeaderSize() { return sizeof(m_sve_header); } |
| |
| diff --git a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h |
| index ba873ba4436b..96cab49d5ac8 100644 |
| --- a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h |
| +++ b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h |
| @@ -110,6 +110,7 @@ public: |
| |
| bool IsSVEEnabled() const { return m_opt_regsets.AnySet(eRegsetMaskSVE); } |
| bool IsPAuthEnabled() const { return m_opt_regsets.AnySet(eRegsetMaskPAuth); } |
| + bool IsMTEEnabled() const { return m_opt_regsets.AnySet(eRegsetMaskMTE); } |
| |
| bool IsSVEReg(unsigned reg) const; |
| bool IsSVEZReg(unsigned reg) const; |
| diff --git a/lldb/test/API/commands/register/register/aarch64_sve_registers/rw_access_static_config/TestSVERegisters.py b/lldb/test/API/commands/register/register/aarch64_sve_registers/rw_access_static_config/TestSVERegisters.py |
| index b243a6692d85..dd2277b92273 100644 |
| --- a/lldb/test/API/commands/register/register/aarch64_sve_registers/rw_access_static_config/TestSVERegisters.py |
| +++ b/lldb/test/API/commands/register/register/aarch64_sve_registers/rw_access_static_config/TestSVERegisters.py |
| @@ -17,6 +17,51 @@ class RegisterCommandsTestCase(TestBase): |
| self.assertEqual(reg_value.GetByteSize(), expected, |
| 'Verify "%s" == %i' % (name, expected)) |
| |
| + def check_sve_regs_read(self, z_reg_size): |
| + p_reg_size = int(z_reg_size / 8) |
| + |
| + for i in range(32): |
| + z_regs_value = '{' + \ |
| + ' '.join('0x{:02x}'.format(i + 1) |
| + for _ in range(z_reg_size)) + '}' |
| + self.expect("register read " + 'z%i' % |
| + (i), substrs=[z_regs_value]) |
| + |
| + p_value_bytes = ['0xff', '0x55', '0x11', '0x01', '0x00'] |
| + for i in range(16): |
| + p_regs_value = '{' + \ |
| + ' '.join(p_value_bytes[i % 5] for _ in range(p_reg_size)) + '}' |
| + self.expect("register read " + 'p%i' % (i), substrs=[p_regs_value]) |
| + |
| + self.expect("register read ffr", substrs=[p_regs_value]) |
| + |
| + def check_sve_regs_read_after_write(self, z_reg_size): |
| + p_reg_size = int(z_reg_size / 8) |
| + |
| + z_regs_value = '{' + \ |
| + ' '.join(('0x9d' for _ in range(z_reg_size))) + '}' |
| + |
| + p_regs_value = '{' + \ |
| + ' '.join(('0xee' for _ in range(p_reg_size))) + '}' |
| + |
| + for i in range(32): |
| + self.runCmd('register write ' + 'z%i' % |
| + (i) + " '" + z_regs_value + "'") |
| + |
| + for i in range(32): |
| + self.expect("register read " + 'z%i' % (i), substrs=[z_regs_value]) |
| + |
| + for i in range(16): |
| + self.runCmd('register write ' + 'p%i' % |
| + (i) + " '" + p_regs_value + "'") |
| + |
| + for i in range(16): |
| + self.expect("register read " + 'p%i' % (i), substrs=[p_regs_value]) |
| + |
| + self.runCmd('register write ' + 'ffr ' + "'" + p_regs_value + "'") |
| + |
| + self.expect("register read " + 'ffr', substrs=[p_regs_value]) |
| + |
| mydir = TestBase.compute_mydir(__file__) |
| |
| @no_debug_info_test |
| @@ -117,43 +162,17 @@ class RegisterCommandsTestCase(TestBase): |
| |
| z_reg_size = vg_reg_value * 8 |
| |
| - p_reg_size = int(z_reg_size / 8) |
| - |
| - for i in range(32): |
| - z_regs_value = '{' + \ |
| - ' '.join('0x{:02x}'.format(i + 1) |
| - for _ in range(z_reg_size)) + '}' |
| - self.expect("register read " + 'z%i' % |
| - (i), substrs=[z_regs_value]) |
| + self.check_sve_regs_read(z_reg_size) |
| |
| - p_value_bytes = ['0xff', '0x55', '0x11', '0x01', '0x00'] |
| - for i in range(16): |
| - p_regs_value = '{' + \ |
| - ' '.join(p_value_bytes[i % 5] for _ in range(p_reg_size)) + '}' |
| - self.expect("register read " + 'p%i' % (i), substrs=[p_regs_value]) |
| + # Evaluate simple expression and print function expr_eval_func address. |
| + self.expect("p expr_eval_func", substrs=["= 0x"]) |
| |
| - self.expect("register read ffr", substrs=[p_regs_value]) |
| + # Evaluate expression call function expr_eval_func. |
| + self.expect_expr("expr_eval_func()", |
| + result_type="int", result_value="1") |
| |
| - z_regs_value = '{' + \ |
| - ' '.join(('0x9d' for _ in range(z_reg_size))) + '}' |
| + # We called a jitted function above which must not have changed SVE |
| + # vector length or register values. |
| + self.check_sve_regs_read(z_reg_size) |
| |
| - p_regs_value = '{' + \ |
| - ' '.join(('0xee' for _ in range(p_reg_size))) + '}' |
| - |
| - for i in range(32): |
| - self.runCmd('register write ' + 'z%i' % |
| - (i) + " '" + z_regs_value + "'") |
| - |
| - for i in range(32): |
| - self.expect("register read " + 'z%i' % (i), substrs=[z_regs_value]) |
| - |
| - for i in range(16): |
| - self.runCmd('register write ' + 'p%i' % |
| - (i) + " '" + p_regs_value + "'") |
| - |
| - for i in range(16): |
| - self.expect("register read " + 'p%i' % (i), substrs=[p_regs_value]) |
| - |
| - self.runCmd('register write ' + 'ffr ' + "'" + p_regs_value + "'") |
| - |
| - self.expect("register read " + 'ffr', substrs=[p_regs_value]) |
| + self.check_sve_regs_read_after_write(z_reg_size) |
| diff --git a/lldb/test/API/commands/register/register/aarch64_sve_registers/rw_access_static_config/main.c b/lldb/test/API/commands/register/register/aarch64_sve_registers/rw_access_static_config/main.c |
| index 0c2573864eeb..79ff587ab732 100644 |
| --- a/lldb/test/API/commands/register/register/aarch64_sve_registers/rw_access_static_config/main.c |
| +++ b/lldb/test/API/commands/register/register/aarch64_sve_registers/rw_access_static_config/main.c |
| @@ -1,4 +1,6 @@ |
| -int main() { |
| +#include <sys/prctl.h> |
| + |
| +void write_sve_regs() { |
| asm volatile("setffr\n\t"); |
| asm volatile("ptrue p0.b\n\t"); |
| asm volatile("ptrue p1.h\n\t"); |
| @@ -49,5 +51,20 @@ int main() { |
| asm volatile("cpy z29.b, p5/z, #30\n\t"); |
| asm volatile("cpy z30.b, p10/z, #31\n\t"); |
| asm volatile("cpy z31.b, p15/z, #32\n\t"); |
| +} |
| + |
| +// This function will be called using jitted expression call. We change vector |
| +// length and write SVE registers. Our program context should restore to |
| +// orignal vector length and register values after expression evaluation. |
| +int expr_eval_func() { |
| + prctl(PR_SVE_SET_VL, 8 * 2); |
| + write_sve_regs(); |
| + prctl(PR_SVE_SET_VL, 8 * 4); |
| + write_sve_regs(); |
| + return 1; |
| +} |
| + |
| +int main() { |
| + write_sve_regs(); |
| return 0; // Set a break point here. |
| } |
| -- |
| 2.34.1.703.g22d0c6ccf7-goog |
| |