DavidSpickett created this revision.
Herald added a subscriber: kristof.beyls.
Herald added a project: All.
DavidSpickett requested review of this revision.
Herald added a project: LLDB.
Herald added a subscriber: lldb-commits.

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.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D154930

Files:
  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
  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
  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

Index: lldb/test/API/linux/aarch64/tls_registers/main.c
===================================================================
--- /dev/null
+++ lldb/test/API/linux/aarch64/tls_registers/main.c
@@ -0,0 +1,57 @@
+#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[]) {
+  uint64_t (*getter)(void) = get_tpidr;
+  void (*setter)(uint64_t) = set_tpidr;
+
+  // Expect just the register number.
+  if (argc != 2)
+    return 1;
+
+  switch (argv[1][0]) {
+  case '1':
+    break;
+  case '2':
+    getter = get_tpidr2;
+    setter = set_tpidr2;
+    break;
+  default:
+    return 1;
+  }
+
+  uint64_t original = getter();
+  uint64_t pattern = 0x1122334455667788;
+  setter(pattern);
+
+  // Set break point at this line.
+  // lldb will now set its own pattern for us to find.
+
+  uint64_t new_value = getter();
+  volatile bool reg_was_set = new_value == 0x8877665544332211;
+
+  setter(original);
+
+  return 0; // Set break point 2 at this line.
+}
Index: lldb/test/API/linux/aarch64/tls_registers/TestAArch64LinuxTLSRegisters.py
===================================================================
--- lldb/test/API/linux/aarch64/tls_registers/TestAArch64LinuxTLSRegisters.py
+++ lldb/test/API/linux/aarch64/tls_registers/TestAArch64LinuxTLSRegisters.py
@@ -1,5 +1,5 @@
 """
-Test lldb's ability to read and write the AArch64 TLS register tpidr.
+Test lldb's ability to read and write the AArch64 TLS registers.
 """
 
 import lldb
@@ -8,12 +8,10 @@
 from lldbsuite.test import lldbutil
 
 
-class AArch64LinuxTLSRegister(TestBase):
+class AArch64LinuxTLSRegisters(TestBase):
     NO_DEBUG_INFO_TESTCASE = True
 
-    @skipUnlessArch("aarch64")
-    @skipUnlessPlatform(["linux"])
-    def test_tls(self):
+    def setup(self, register_name):
         self.build()
         self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET)
 
@@ -31,6 +29,8 @@
             num_expected_locations=1,
         )
 
+        self.runCmd("settings set target.run-args {}".format(
+            {"tpidr": 1, "tpidr2": 2}[register_name]))
         self.runCmd("run", RUN_SUCCEEDED)
 
         if self.process().GetState() == lldb.eStateExited:
@@ -42,19 +42,24 @@
             substrs=["stopped", "stop reason = breakpoint"],
         )
 
+    def check_tls_reg(self, register_name):
+        self.setup(register_name)
+
         # 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.")
+        tls_reg = tls_regs.GetChildMemberWithName(register_name)
+        self.assertTrue(tls_reg.IsValid(), "{} register not found.".format(
+            register_name))
 
-        self.assertEqual(tpidr.GetValueAsUnsigned(), 0x1122334455667788)
+        self.assertEqual(tls_reg.GetValueAsUnsigned(), 0x1122334455667788)
 
         # Set our own value for the program to find.
-        self.expect("register write tpidr 0x{:x}".format(0x8877665544332211))
+        self.expect("register write {} 0x{:x}".format(
+                    register_name, 0x8877665544332211))
         self.expect("continue")
 
         self.expect(
@@ -63,4 +68,32 @@
             substrs=["stopped", "stop reason = breakpoint"],
         )
 
-        self.expect("p tpidr_was_set", substrs=["true"])
\ No newline at end of file
+        self.expect("p reg_was_set", substrs=["true"])
+
+    @skipUnlessArch("aarch64")
+    @skipUnlessPlatform(["linux"])
+    def test_tpidr(self):
+        self.check_tls_reg("tpidr")
+
+    @skipUnlessArch("aarch64")
+    @skipUnlessPlatform(["linux"])
+    def test_tpidr2(self):
+        if not self.isAArch64SME():
+            self.skipTest("SME must enabled.")
+
+        self.check_tls_reg("tpidr2")
+
+    @skipUnlessArch("aarch64")
+    @skipUnlessPlatform(["linux"])
+    def test_tpidr2_no_sme(self):
+        if self.isAArch64SME():
+            self.skipTest("SME must not be enabled.")
+
+        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")
Index: lldb/test/API/linux/aarch64/tls_register/Makefile
===================================================================
--- /dev/null
+++ lldb/test/API/linux/aarch64/tls_register/Makefile
@@ -1,3 +0,0 @@
-C_SOURCES := main.c
-
-include Makefile.rules
Index: lldb/test/API/linux/aarch64/tls_register/main.c
===================================================================
--- 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.
-}
Index: lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h
===================================================================
--- lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h
+++ lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h
@@ -104,7 +104,7 @@
 
   void AddRegSetMTE();
 
-  void AddRegSetTLS();
+  void AddRegSetTLS(bool has_tpidr2);
 
   void AddRegSetSME();
 
Index: lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp
===================================================================
--- lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp
+++ lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp
@@ -79,7 +79,9 @@
     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)};
 
 static lldb_private::RegisterInfo g_register_infos_sme[] = {
     DEFINE_EXTENSION_REG(svcr)};
@@ -90,7 +92,7 @@
   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_sme_register = 1,
   k_num_register_sets_default = 2,
@@ -197,8 +199,7 @@
 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.
 
 static const lldb_private::RegisterSet g_reg_set_sme_arm64 = {
     "Scalable Matrix Extension Registers", "sme", k_num_sme_register, nullptr};
@@ -243,9 +244,10 @@
       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.
+      bool has_tpidr2 = m_opt_regsets.AllSet(eRegsetMaskSSVE);
+      AddRegSetTLS(has_tpidr2);
 
       if (m_opt_regsets.AllSet(eRegsetMaskSSVE))
         AddRegSetSME();
@@ -333,18 +335,23 @@
   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();
 }
 
Index: lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h
===================================================================
--- lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h
+++ lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h
@@ -82,9 +82,10 @@
   bool m_gpr_is_valid;
   bool m_fpu_is_valid;
   bool m_mte_ctrl_is_valid;
-  bool m_tls_tpidr_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.
 
@@ -128,7 +129,13 @@
   uint64_t m_mte_ctrl_reg;
   uint64_t m_sme_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;
 
@@ -154,9 +161,9 @@
 
   Status WriteMTEControl();
 
-  Status ReadTLSTPIDR();
+  Status ReadTLS();
 
-  Status WriteTLSTPIDR();
+  Status WriteTLS();
 
   // SVCR is a pseudo register and we do not allow writes to it.
   Status ReadSMEControl();
@@ -175,7 +182,7 @@
 
   void *GetMTEControl() { return &m_mte_ctrl_reg; }
 
-  void *GetTLSTPIDR() { return &m_tls_tpidr_reg; }
+  void *GetTLS() { return &m_tls_regs; }
 
   void *GetSMEControl() { return &m_sme_ctrl_reg; }
 
@@ -191,7 +198,7 @@
 
   size_t GetMTEControlSize() { return sizeof(m_mte_ctrl_reg); }
 
-  size_t GetTLSTPIDRSize() { return sizeof(m_tls_tpidr_reg); }
+  size_t GetTLSSize() { return m_tls_size; }
 
   size_t GetSMEControlSize() { return sizeof(m_sme_ctrl_reg); }
 
Index: lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp
===================================================================
--- lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp
+++ lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp
@@ -132,10 +132,10 @@
   ::memset(&m_hwp_regs, 0, sizeof(m_hwp_regs));
   ::memset(&m_hbp_regs, 0, sizeof(m_hbp_regs));
   ::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;
   m_sme_ctrl_reg = 0;
+  m_mte_ctrl_reg = 0;
 
   // 16 is just a maximum value, query hardware for actual watchpoint count
   m_max_hwp_supported = 16;
@@ -147,7 +147,11 @@
   m_fpu_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;
@@ -266,13 +270,13 @@
       m_sve_state = previous_sve_state;
     }
   } 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 < GetTLSSize());
+    src = (uint8_t *)GetTLS() + offset;
   } else if (IsSVE(reg)) {
     if (m_sve_state == SVEState::Disabled || m_sve_state == SVEState::Unknown)
       return Status("SVE disabled or not supported");
@@ -519,16 +523,16 @@
 
     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 < GetTLSSize());
+    dst = (uint8_t *)GetTLS() + offset;
     ::memcpy(dst, reg_value.GetBytes(), reg_info->byte_size);
 
-    return WriteTLSTPIDR();
+    return WriteTLS();
   } else if (IsSME(reg)) {
     return Status("Writing to SVCR is not supported.");
   }
@@ -574,9 +578,9 @@
       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 += GetTLSSize();
+  error = ReadTLS();
   if (error.Fail())
     return error;
 
@@ -599,7 +603,7 @@
   if (GetRegisterInfo().IsMTEEnabled())
     ::memcpy(dst, GetMTEControl(), GetMTEControlSize());
 
-  ::memcpy(dst, GetTLSTPIDR(), GetTLSTPIDRSize());
+  ::memcpy(dst, GetTLS(), GetTLSSize());
 
   return error;
 }
@@ -891,7 +895,7 @@
   m_ssve_state_data.Invalidate();
   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();
@@ -1038,38 +1042,38 @@
   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 = GetTLS();
+  ioVec.iov_len = GetTLSSize();
 
-  error = ReadRegisterSet(&ioVec, GetTLSTPIDRSize(), NT_ARM_TLS);
+  error = ReadRegisterSet(&ioVec, GetTLSSize(), 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 = GetTLS();
+  ioVec.iov_len = GetTLSSize();
 
-  m_tls_tpidr_is_valid = false;
+  m_tls_is_valid = false;
 
-  return WriteRegisterSet(&ioVec, GetTLSTPIDRSize(), NT_ARM_TLS);
+  return WriteRegisterSet(&ioVec, GetTLSSize(), NT_ARM_TLS);
 }
 
 void NativeRegisterContextLinux_arm64::ConfigureRegisterContext() {
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to