[Lldb-commits] [lldb] efa43d7 - [lldb][AArch64] Add the tpidr2 TLS register that comes with SME

David Spickett via lldb-commits lldb-commits at lists.llvm.org
Wed Jul 26 02:34:19 PDT 2023


Author: David Spickett
Date: 2023-07-26T10:34:13+01:00
New Revision: efa43d785ee600ef4cc14589e4777264f0613ec9

URL: https://github.com/llvm/llvm-project/commit/efa43d785ee600ef4cc14589e4777264f0613ec9
DIFF: https://github.com/llvm/llvm-project/commit/efa43d785ee600ef4cc14589e4777264f0613ec9.diff

LOG: [lldb][AArch64] Add the tpidr2 TLS register that comes with SME

This changes the TLS regset to not only be dynamic in that it could
exist or not (though it always does) but also of a dynamic size.

If SME is present then the regset is 16 bytes and contains both tpidr
and tpidr2.

Testing is the same as tpidr. Write from assembly, read from lldb and
vice versa since we have no way to predict what its value should be
by just running a program.

Reviewed By: omjavaid

Differential Revision: https://reviews.llvm.org/D154930

Added: 
    lldb/test/API/linux/aarch64/tls_registers/Makefile
    lldb/test/API/linux/aarch64/tls_registers/TestAArch64LinuxTLSRegisters.py
    lldb/test/API/linux/aarch64/tls_registers/main.c

Modified: 
    lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp
    lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h
    lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp
    lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h

Removed: 
    lldb/test/API/linux/aarch64/tls_register/Makefile
    lldb/test/API/linux/aarch64/tls_register/TestAArch64LinuxTLSRegister.py
    lldb/test/API/linux/aarch64/tls_register/main.c


################################################################################
diff  --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp
index ba6ec995dc2201..ae9fe7039a9204 100644
--- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp
+++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp
@@ -132,9 +132,9 @@ NativeRegisterContextLinux_arm64::NativeRegisterContextLinux_arm64(
   ::memset(&m_hbp_regs, 0, sizeof(m_hbp_regs));
   ::memset(&m_sve_header, 0, sizeof(m_sve_header));
   ::memset(&m_pac_mask, 0, sizeof(m_pac_mask));
+  ::memset(&m_tls_regs, 0, sizeof(m_tls_regs));
 
   m_mte_ctrl_reg = 0;
-  m_tls_tpidr_reg = 0;
 
   // 16 is just a maximum value, query hardware for actual watchpoint count
   m_max_hwp_supported = 16;
@@ -148,7 +148,11 @@ NativeRegisterContextLinux_arm64::NativeRegisterContextLinux_arm64(
   m_sve_header_is_valid = false;
   m_pac_mask_is_valid = false;
   m_mte_ctrl_is_valid = false;
-  m_tls_tpidr_is_valid = false;
+  m_tls_is_valid = false;
+
+  // SME adds the tpidr2 register
+  m_tls_size = GetRegisterInfo().IsSSVEEnabled() ? sizeof(m_tls_regs)
+                                                 : sizeof(m_tls_regs.tpidr_reg);
 
   if (GetRegisterInfo().IsSVEEnabled() || GetRegisterInfo().IsSSVEEnabled())
     m_sve_state = SVEState::Unknown;
@@ -255,13 +259,13 @@ NativeRegisterContextLinux_arm64::ReadRegister(const RegisterInfo *reg_info,
       src = (uint8_t *)GetSVEBuffer() + offset;
     }
   } else if (IsTLS(reg)) {
-    error = ReadTLSTPIDR();
+    error = ReadTLS();
     if (error.Fail())
       return error;
 
     offset = reg_info->byte_offset - GetRegisterInfo().GetTLSOffset();
-    assert(offset < GetTLSTPIDRSize());
-    src = (uint8_t *)GetTLSTPIDR() + offset;
+    assert(offset < GetTLSBufferSize());
+    src = (uint8_t *)GetTLSBuffer() + offset;
   } else if (IsSVE(reg)) {
     if (m_sve_state == SVEState::Disabled || m_sve_state == SVEState::Unknown)
       return Status("SVE disabled or not supported");
@@ -480,16 +484,16 @@ Status NativeRegisterContextLinux_arm64::WriteRegister(
 
     return WriteMTEControl();
   } else if (IsTLS(reg)) {
-    error = ReadTLSTPIDR();
+    error = ReadTLS();
     if (error.Fail())
       return error;
 
     offset = reg_info->byte_offset - GetRegisterInfo().GetTLSOffset();
-    assert(offset < GetTLSTPIDRSize());
-    dst = (uint8_t *)GetTLSTPIDR() + offset;
+    assert(offset < GetTLSBufferSize());
+    dst = (uint8_t *)GetTLSBuffer() + offset;
     ::memcpy(dst, reg_value.GetBytes(), reg_info->byte_size);
 
-    return WriteTLSTPIDR();
+    return WriteTLS();
   }
 
   return Status("Failed to write register value");
@@ -533,9 +537,9 @@ Status NativeRegisterContextLinux_arm64::ReadAllRegisterValues(
       return error;
   }
 
-  // tpidr is always present but there will be more in future.
-  reg_data_byte_size += GetTLSTPIDRSize();
-  error = ReadTLSTPIDR();
+  // tpidr is always present but tpidr2 depends on SME.
+  reg_data_byte_size += GetTLSBufferSize();
+  error = ReadTLS();
   if (error.Fail())
     return error;
 
@@ -558,7 +562,7 @@ Status NativeRegisterContextLinux_arm64::ReadAllRegisterValues(
   if (GetRegisterInfo().IsMTEEnabled())
     ::memcpy(dst, GetMTEControl(), GetMTEControlSize());
 
-  ::memcpy(dst, GetTLSTPIDR(), GetTLSTPIDRSize());
+  ::memcpy(dst, GetTLSBuffer(), GetTLSBufferSize());
 
   return error;
 }
@@ -845,7 +849,7 @@ void NativeRegisterContextLinux_arm64::InvalidateAllRegisters() {
   m_sve_header_is_valid = false;
   m_pac_mask_is_valid = false;
   m_mte_ctrl_is_valid = false;
-  m_tls_tpidr_is_valid = false;
+  m_tls_is_valid = false;
 
   // Update SVE registers in case there is change in configuration.
   ConfigureRegisterContext();
@@ -979,38 +983,38 @@ Status NativeRegisterContextLinux_arm64::WriteMTEControl() {
   return WriteRegisterSet(&ioVec, GetMTEControlSize(), NT_ARM_TAGGED_ADDR_CTRL);
 }
 
-Status NativeRegisterContextLinux_arm64::ReadTLSTPIDR() {
+Status NativeRegisterContextLinux_arm64::ReadTLS() {
   Status error;
 
-  if (m_tls_tpidr_is_valid)
+  if (m_tls_is_valid)
     return error;
 
   struct iovec ioVec;
-  ioVec.iov_base = GetTLSTPIDR();
-  ioVec.iov_len = GetTLSTPIDRSize();
+  ioVec.iov_base = GetTLSBuffer();
+  ioVec.iov_len = GetTLSBufferSize();
 
-  error = ReadRegisterSet(&ioVec, GetTLSTPIDRSize(), NT_ARM_TLS);
+  error = ReadRegisterSet(&ioVec, GetTLSBufferSize(), NT_ARM_TLS);
 
   if (error.Success())
-    m_tls_tpidr_is_valid = true;
+    m_tls_is_valid = true;
 
   return error;
 }
 
-Status NativeRegisterContextLinux_arm64::WriteTLSTPIDR() {
+Status NativeRegisterContextLinux_arm64::WriteTLS() {
   Status error;
 
-  error = ReadTLSTPIDR();
+  error = ReadTLS();
   if (error.Fail())
     return error;
 
   struct iovec ioVec;
-  ioVec.iov_base = GetTLSTPIDR();
-  ioVec.iov_len = GetTLSTPIDRSize();
+  ioVec.iov_base = GetTLSBuffer();
+  ioVec.iov_len = GetTLSBufferSize();
 
-  m_tls_tpidr_is_valid = false;
+  m_tls_is_valid = false;
 
-  return WriteRegisterSet(&ioVec, GetTLSTPIDRSize(), NT_ARM_TLS);
+  return WriteRegisterSet(&ioVec, GetTLSBufferSize(), NT_ARM_TLS);
 }
 
 void NativeRegisterContextLinux_arm64::ConfigureRegisterContext() {

diff  --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h
index 47c859b102946b..d55dd647c69ed0 100644
--- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h
+++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h
@@ -83,10 +83,11 @@ class NativeRegisterContextLinux_arm64
   bool m_fpu_is_valid;
   bool m_sve_buffer_is_valid;
   bool m_mte_ctrl_is_valid;
-  bool m_tls_tpidr_is_valid;
 
   bool m_sve_header_is_valid;
   bool m_pac_mask_is_valid;
+  bool m_tls_is_valid;
+  size_t m_tls_size;
 
   struct user_pt_regs m_gpr_arm64; // 64-bit general purpose registers.
 
@@ -108,7 +109,13 @@ class NativeRegisterContextLinux_arm64
 
   uint64_t m_mte_ctrl_reg;
 
-  uint64_t m_tls_tpidr_reg;
+  struct tls_regs {
+    uint64_t tpidr_reg;
+    // Only valid when SME is present.
+    uint64_t tpidr2_reg;
+  };
+
+  struct tls_regs m_tls_regs;
 
   bool IsGPR(unsigned reg) const;
 
@@ -128,9 +135,9 @@ class NativeRegisterContextLinux_arm64
 
   Status WriteMTEControl();
 
-  Status ReadTLSTPIDR();
+  Status ReadTLS();
 
-  Status WriteTLSTPIDR();
+  Status WriteTLS();
 
   bool IsSVE(unsigned reg) const;
   bool IsPAuth(unsigned reg) const;
@@ -147,7 +154,7 @@ class NativeRegisterContextLinux_arm64
 
   void *GetMTEControl() { return &m_mte_ctrl_reg; }
 
-  void *GetTLSTPIDR() { return &m_tls_tpidr_reg; }
+  void *GetTLSBuffer() { return &m_tls_regs; }
 
   void *GetSVEBuffer() { return m_sve_ptrace_payload.data(); }
 
@@ -161,7 +168,7 @@ class NativeRegisterContextLinux_arm64
 
   size_t GetMTEControlSize() { return sizeof(m_mte_ctrl_reg); }
 
-  size_t GetTLSTPIDRSize() { return sizeof(m_tls_tpidr_reg); }
+  size_t GetTLSBufferSize() { return m_tls_size; }
 
   llvm::Error ReadHardwareDebugInfo() override;
 

diff  --git a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp
index b168ec941dab11..001d7406ff4347 100644
--- a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp
+++ b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp
@@ -79,7 +79,9 @@ static lldb_private::RegisterInfo g_register_infos_mte[] = {
     DEFINE_EXTENSION_REG(mte_ctrl)};
 
 static lldb_private::RegisterInfo g_register_infos_tls[] = {
-    DEFINE_EXTENSION_REG(tpidr)};
+    DEFINE_EXTENSION_REG(tpidr),
+    // Only present when SME is present
+    DEFINE_EXTENSION_REG(tpidr2)};
 
 // Number of register sets provided by this context.
 enum {
@@ -87,7 +89,7 @@ enum {
   k_num_fpr_registers = fpu_fpcr - fpu_v0 + 1,
   k_num_sve_registers = sve_ffr - sve_vg + 1,
   k_num_mte_register = 1,
-  k_num_tls_register = 1,
+  // Number of TLS registers is dynamic so it is not listed here.
   k_num_pauth_register = 2,
   k_num_register_sets_default = 2,
   k_num_register_sets = 3
@@ -193,8 +195,7 @@ static const lldb_private::RegisterSet g_reg_set_pauth_arm64 = {
 static const lldb_private::RegisterSet g_reg_set_mte_arm64 = {
     "MTE Control Register", "mte", k_num_mte_register, nullptr};
 
-static const lldb_private::RegisterSet g_reg_set_tls_arm64 = {
-    "Thread Local Storage Registers", "tls", k_num_tls_register, nullptr};
+// The size of the TLS set is dynamic, so not listed here.
 
 RegisterInfoPOSIX_arm64::RegisterInfoPOSIX_arm64(
     const lldb_private::ArchSpec &target_arch, lldb_private::Flags opt_regsets)
@@ -236,9 +237,9 @@ RegisterInfoPOSIX_arm64::RegisterInfoPOSIX_arm64(
       if (m_opt_regsets.AllSet(eRegsetMaskMTE))
         AddRegSetMTE();
 
-      // tpidr is always present, but in future there will be others so this is
-      // done as a dynamic set.
-      AddRegSetTLS();
+      // The TLS set always contains tpidr but only has tpidr2 when SME is
+      // present.
+      AddRegSetTLS(m_opt_regsets.AllSet(eRegsetMaskSSVE));
 
       m_register_info_count = m_dynamic_reg_infos.size();
       m_register_info_p = m_dynamic_reg_infos.data();
@@ -323,18 +324,23 @@ void RegisterInfoPOSIX_arm64::AddRegSetMTE() {
   m_dynamic_reg_sets.back().registers = m_mte_regnum_collection.data();
 }
 
-void RegisterInfoPOSIX_arm64::AddRegSetTLS() {
+void RegisterInfoPOSIX_arm64::AddRegSetTLS(bool has_tpidr2) {
   uint32_t tls_regnum = m_dynamic_reg_infos.size();
-  m_tls_regnum_collection.push_back(tls_regnum);
-  m_dynamic_reg_infos.push_back(g_register_infos_tls[0]);
-  m_dynamic_reg_infos[tls_regnum].byte_offset =
-      m_dynamic_reg_infos[tls_regnum - 1].byte_offset +
-      m_dynamic_reg_infos[tls_regnum - 1].byte_size;
-  m_dynamic_reg_infos[tls_regnum].kinds[lldb::eRegisterKindLLDB] = tls_regnum;
+  uint32_t num_regs = has_tpidr2 ? 2 : 1;
+  for (uint32_t i = 0; i < num_regs; i++) {
+    m_tls_regnum_collection.push_back(tls_regnum + i);
+    m_dynamic_reg_infos.push_back(g_register_infos_tls[i]);
+    m_dynamic_reg_infos[tls_regnum + i].byte_offset =
+        m_dynamic_reg_infos[tls_regnum + i - 1].byte_offset +
+        m_dynamic_reg_infos[tls_regnum + i - 1].byte_size;
+    m_dynamic_reg_infos[tls_regnum + i].kinds[lldb::eRegisterKindLLDB] =
+        tls_regnum + i;
+  }
 
   m_per_regset_regnum_range[m_register_set_count] =
-      std::make_pair(tls_regnum, tls_regnum + 1);
-  m_dynamic_reg_sets.push_back(g_reg_set_tls_arm64);
+      std::make_pair(tls_regnum, m_dynamic_reg_infos.size());
+  m_dynamic_reg_sets.push_back(
+      {"Thread Local Storage Registers", "tls", num_regs, nullptr});
   m_dynamic_reg_sets.back().registers = m_tls_regnum_collection.data();
 }
 

diff  --git a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h
index 6e5f798185b4de..8505ebfa9eb971 100644
--- a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h
+++ b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h
@@ -104,7 +104,7 @@ class RegisterInfoPOSIX_arm64
 
   void AddRegSetMTE();
 
-  void AddRegSetTLS();
+  void AddRegSetTLS(bool has_tpidr2);
 
   uint32_t ConfigureVectorLength(uint32_t sve_vq);
 

diff  --git a/lldb/test/API/linux/aarch64/tls_register/TestAArch64LinuxTLSRegister.py b/lldb/test/API/linux/aarch64/tls_register/TestAArch64LinuxTLSRegister.py
deleted file mode 100644
index d4ab5d70cc7643..00000000000000
--- a/lldb/test/API/linux/aarch64/tls_register/TestAArch64LinuxTLSRegister.py
+++ /dev/null
@@ -1,66 +0,0 @@
-"""
-Test lldb's ability to read and write the AArch64 TLS register tpidr.
-"""
-
-import lldb
-from lldbsuite.test.decorators import *
-from lldbsuite.test.lldbtest import *
-from lldbsuite.test import lldbutil
-
-
-class AArch64LinuxTLSRegister(TestBase):
-    NO_DEBUG_INFO_TESTCASE = True
-
-    @skipUnlessArch("aarch64")
-    @skipUnlessPlatform(["linux"])
-    def test_tls(self):
-        self.build()
-        self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET)
-
-        lldbutil.run_break_set_by_file_and_line(
-            self,
-            "main.c",
-            line_number("main.c", "// Set break point at this line."),
-            num_expected_locations=1,
-        )
-
-        lldbutil.run_break_set_by_file_and_line(
-            self,
-            "main.c",
-            line_number("main.c", "// Set break point 2 at this line."),
-            num_expected_locations=1,
-        )
-
-        self.runCmd("run", RUN_SUCCEEDED)
-
-        if self.process().GetState() == lldb.eStateExited:
-            self.fail("Test program failed to run.")
-
-        self.expect(
-            "thread list",
-            STOPPED_DUE_TO_BREAKPOINT,
-            substrs=["stopped", "stop reason = breakpoint"],
-        )
-
-        # Since we can't predict what the value will be, the program has set
-        # a target value for us to find.
-
-        regs = self.thread().GetSelectedFrame().GetRegisters()
-        tls_regs = regs.GetFirstValueByName("Thread Local Storage Registers")
-        self.assertTrue(tls_regs.IsValid(), "No TLS registers found.")
-        tpidr = tls_regs.GetChildMemberWithName("tpidr")
-        self.assertTrue(tpidr.IsValid(), "No tpidr register found.")
-
-        self.assertEqual(tpidr.GetValueAsUnsigned(), 0x1122334455667788)
-
-        # Set our own value for the program to find.
-        self.expect("register write tpidr 0x{:x}".format(0x8877665544332211))
-        self.expect("continue")
-
-        self.expect(
-            "thread list",
-            STOPPED_DUE_TO_BREAKPOINT,
-            substrs=["stopped", "stop reason = breakpoint"],
-        )
-
-        self.expect("p tpidr_was_set", substrs=["true"])
\ No newline at end of file

diff  --git a/lldb/test/API/linux/aarch64/tls_register/main.c b/lldb/test/API/linux/aarch64/tls_register/main.c
deleted file mode 100644
index 7a5fd3f5fcbdff..00000000000000
--- a/lldb/test/API/linux/aarch64/tls_register/main.c
+++ /dev/null
@@ -1,24 +0,0 @@
-#include <stdbool.h>
-#include <stdint.h>
-
-int main() {
-  // Save tpidr to restore later.
-  uint64_t tpidr = 0;
-  __asm__ volatile("mrs %0, tpidr_el0" : "=r"(tpidr));
-
-  // Set a pattern for lldb to find.
-  uint64_t pattern = 0x1122334455667788;
-  __asm__ volatile("msr tpidr_el0, %0" ::"r"(pattern));
-
-  // Set break point at this line.
-  // lldb will now set its own pattern for us to find.
-
-  uint64_t new_tpidr = pattern;
-  __asm__ volatile("mrs %0, tpidr_el0" : "=r"(new_tpidr));
-  volatile bool tpidr_was_set = new_tpidr == 0x8877665544332211;
-
-  // Restore the original.
-  __asm__ volatile("msr tpidr_el0, %0" ::"r"(tpidr));
-
-  return 0; // Set break point 2 at this line.
-}

diff  --git a/lldb/test/API/linux/aarch64/tls_register/Makefile b/lldb/test/API/linux/aarch64/tls_registers/Makefile
similarity index 100%
rename from lldb/test/API/linux/aarch64/tls_register/Makefile
rename to lldb/test/API/linux/aarch64/tls_registers/Makefile

diff  --git a/lldb/test/API/linux/aarch64/tls_registers/TestAArch64LinuxTLSRegisters.py b/lldb/test/API/linux/aarch64/tls_registers/TestAArch64LinuxTLSRegisters.py
new file mode 100644
index 00000000000000..f1bc6b45a9d815
--- /dev/null
+++ b/lldb/test/API/linux/aarch64/tls_registers/TestAArch64LinuxTLSRegisters.py
@@ -0,0 +1,115 @@
+"""
+Test lldb's ability to read and write the AArch64 TLS registers.
+"""
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class AArch64LinuxTLSRegisters(TestBase):
+    NO_DEBUG_INFO_TESTCASE = True
+
+    def setup(self, registers):
+        self.build()
+        self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET)
+
+        lldbutil.run_break_set_by_file_and_line(
+            self,
+            "main.c",
+            line_number("main.c", "// Set break point at this line."),
+            num_expected_locations=1,
+        )
+
+        lldbutil.run_break_set_by_file_and_line(
+            self,
+            "main.c",
+            line_number("main.c", "// Set break point 2 at this line."),
+            num_expected_locations=1,
+        )
+
+        if "tpidr2" in registers:
+            self.runCmd("settings set target.run-args 1")
+        self.runCmd("run", RUN_SUCCEEDED)
+
+        if self.process().GetState() == lldb.eStateExited:
+            self.fail("Test program failed to run.")
+
+        self.expect(
+            "thread list",
+            STOPPED_DUE_TO_BREAKPOINT,
+            substrs=["stopped", "stop reason = breakpoint"],
+        )
+
+    def check_tls_reg(self, registers):
+        self.setup(registers)
+
+        regs = self.thread().GetSelectedFrame().GetRegisters()
+        tls_regs = regs.GetFirstValueByName("Thread Local Storage Registers")
+        self.assertTrue(tls_regs.IsValid(), "No TLS registers found.")
+
+        # Since we can't predict what the value will be, the program has set
+        # a target value for us to find.
+        initial_values = {
+            "tpidr": 0x1122334455667788,
+            "tpidr2": 0x8877665544332211,
+        }
+
+        for register in registers:
+            tls_reg = tls_regs.GetChildMemberWithName(register)
+            self.assertTrue(tls_reg.IsValid(), "{} register not found.".format(
+                register))
+            self.assertEqual(tls_reg.GetValueAsUnsigned(), initial_values[register])
+
+        set_values = {
+            "tpidr": 0x1111222233334444,
+            "tpidr2": 0x4444333322221111,
+        }
+
+        # Set our own value(s) for the program to find.
+        for register in registers:
+            self.expect("register write {} 0x{:x}".format(register,
+                            set_values[register]))
+
+        self.expect("continue")
+
+        self.expect(
+            "thread list",
+            STOPPED_DUE_TO_BREAKPOINT,
+            substrs=["stopped", "stop reason = breakpoint"],
+        )
+
+        for register in registers:
+            self.expect("p {}_was_set".format(register), substrs=["true"])
+
+    @skipUnlessArch("aarch64")
+    @skipUnlessPlatform(["linux"])
+    def test_tls_no_sme(self):
+        if self.isAArch64SME():
+            self.skipTest("SME must not be present.")
+
+        self.check_tls_reg(["tpidr"])
+
+    @skipUnlessArch("aarch64")
+    @skipUnlessPlatform(["linux"])
+    def test_tls_sme(self):
+        if not self.isAArch64SME():
+            self.skipTest("SME must present.")
+
+        self.check_tls_reg(["tpidr", "tpidr2"])
+
+    @skipUnlessArch("aarch64")
+    @skipUnlessPlatform(["linux"])
+    def test_tpidr2_no_sme(self):
+        if self.isAArch64SME():
+            self.skipTest("SME must not be present.")
+
+        self.setup("tpidr")
+
+        regs = self.thread().GetSelectedFrame().GetRegisters()
+        tls_regs = regs.GetFirstValueByName("Thread Local Storage Registers")
+        self.assertTrue(tls_regs.IsValid(), "No TLS registers found.")
+        tls_reg = tls_regs.GetChildMemberWithName("tpidr2")
+        self.assertFalse(tls_reg.IsValid(),
+            "tpdir2 should not be present without SME")

diff  --git a/lldb/test/API/linux/aarch64/tls_registers/main.c b/lldb/test/API/linux/aarch64/tls_registers/main.c
new file mode 100644
index 00000000000000..bd8d7b4d1b6028
--- /dev/null
+++ b/lldb/test/API/linux/aarch64/tls_registers/main.c
@@ -0,0 +1,60 @@
+#include <stdbool.h>
+#include <stdint.h>
+
+uint64_t get_tpidr(void) {
+  uint64_t tpidr = 0;
+  __asm__ volatile("mrs %0, tpidr_el0" : "=r"(tpidr));
+  return tpidr;
+}
+
+uint64_t get_tpidr2(void) {
+  uint64_t tpidr2 = 0;
+  // S3_3_C13_C0_5 means tpidr2, and will work with older tools.
+  __asm__ volatile("mrs %0, S3_3_C13_C0_5" : "=r"(tpidr2));
+  return tpidr2;
+}
+
+void set_tpidr(uint64_t value) {
+  __asm__ volatile("msr tpidr_el0, %0" ::"r"(value));
+}
+
+void set_tpidr2(uint64_t value) {
+  __asm__ volatile("msr S3_3_C13_C0_5, %0" ::"r"(value));
+}
+
+int main(int argc, char *argv[]) {
+  bool use_tpidr2 = argc > 1;
+
+  uint64_t original_tpidr = get_tpidr();
+  // Accessing this on a core without it produces SIGILL. Only do this if
+  // requested.
+  uint64_t original_tpidr2 = 0;
+  if (use_tpidr2)
+    original_tpidr2 = get_tpidr2();
+
+  uint64_t tpidr_pattern = 0x1122334455667788;
+  set_tpidr(tpidr_pattern);
+
+  uint64_t tpidr2_pattern = 0x8877665544332211;
+  if (use_tpidr2)
+    set_tpidr2(tpidr2_pattern);
+
+  // Set break point at this line.
+  // lldb will now set its own pattern(s) for us to find.
+
+  uint64_t new_tpidr = get_tpidr();
+  volatile bool tpidr_was_set = new_tpidr == 0x1111222233334444;
+
+  uint64_t new_tpidr2 = 0;
+  volatile bool tpidr2_was_set = false;
+  if (use_tpidr2) {
+    new_tpidr2 = get_tpidr2();
+    tpidr2_was_set = new_tpidr2 == 0x4444333322221111;
+  }
+
+  set_tpidr(original_tpidr);
+  if (use_tpidr2)
+    set_tpidr2(original_tpidr2);
+
+  return 0; // Set break point 2 at this line.
+}


        


More information about the lldb-commits mailing list