DavidSpickett updated this revision to Diff 541570.
DavidSpickett added a comment.

Note the behaviour of tpidr2 on a system without SME.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D154930/new/

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,59 @@
+#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;
+
+  // Note that accessing tpidr2 on a system without it will SIGILL. That is why
+  // we have this option instead of trying to set both regardless.
+  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 present.")
+
+        self.check_tls_reg("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")
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,9 @@
       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));
 
       if (m_opt_regsets.AllSet(eRegsetMaskSSVE))
         AddRegSetSME();
@@ -333,18 +334,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
@@ -83,10 +83,11 @@
   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.
 
@@ -109,7 +110,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;
 
@@ -129,9 +136,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();
@@ -152,7 +159,7 @@
 
   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(); }
 
@@ -166,12 +173,12 @@
 
   size_t GetMTEControlSize() { return sizeof(m_mte_ctrl_reg); }
 
-  size_t GetTLSTPIDRSize() { return sizeof(m_tls_tpidr_reg); }
-
   void *GetSMEControl() { return &m_sme_ctrl_reg; }
 
   size_t GetSMEControlSize() { return sizeof(m_sme_ctrl_reg); }
 
+  size_t GetTLSBufferSize() { return m_tls_size; }
+
   llvm::Error ReadHardwareDebugInfo() override;
 
   llvm::Error WriteHardwareDebugRegs(DREGType hwbType) override;
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,9 +132,9 @@
   ::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;
   m_sme_ctrl_reg = 0;
 
   // 16 is just a maximum value, query hardware for actual watchpoint count
@@ -149,7 +149,11 @@
   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;
@@ -256,13 +260,13 @@
       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");
@@ -488,16 +492,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 < GetTLSBufferSize());
+    dst = (uint8_t *)GetTLSBuffer() + 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.");
   }
@@ -543,9 +547,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 += GetTLSBufferSize();
+  error = ReadTLS();
   if (error.Fail())
     return error;
 
@@ -568,7 +572,7 @@
   if (GetRegisterInfo().IsMTEEnabled())
     ::memcpy(dst, GetMTEControl(), GetMTEControlSize());
 
-  ::memcpy(dst, GetTLSTPIDR(), GetTLSTPIDRSize());
+  ::memcpy(dst, GetTLSBuffer(), GetTLSBufferSize());
 
   return error;
 }
@@ -859,7 +863,7 @@
   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();
@@ -1003,38 +1007,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 = 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() {
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to