[Lldb-commits] [lldb] 90308a4 - [debugserver] Implement hardware breakpoints for ARM64

Jonas Devlieghere via lldb-commits lldb-commits at lists.llvm.org
Thu Mar 19 11:56:12 PDT 2020


Author: Jonas Devlieghere
Date: 2020-03-19T11:55:48-07:00
New Revision: 90308a4da167c531a16fcecc3927596fffd5d5d5

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

LOG: [debugserver] Implement hardware breakpoints for ARM64

Add support for hardware breakpoints on ARM64.

Differential revision: https://reviews.llvm.org/D76411

Added: 
    

Modified: 
    lldb/tools/debugserver/source/MacOSX/arm64/DNBArchImplARM64.cpp
    lldb/tools/debugserver/source/MacOSX/arm64/DNBArchImplARM64.h

Removed: 
    


################################################################################
diff  --git a/lldb/tools/debugserver/source/MacOSX/arm64/DNBArchImplARM64.cpp b/lldb/tools/debugserver/source/MacOSX/arm64/DNBArchImplARM64.cpp
index f99dbc48b128..e5d4b05d987c 100644
--- a/lldb/tools/debugserver/source/MacOSX/arm64/DNBArchImplARM64.cpp
+++ b/lldb/tools/debugserver/source/MacOSX/arm64/DNBArchImplARM64.cpp
@@ -666,6 +666,112 @@ uint32_t DNBArchMachARM64::NumSupportedHardwareWatchpoints() {
   return g_num_supported_hw_watchpoints;
 }
 
+uint32_t DNBArchMachARM64::NumSupportedHardwareBreakpoints() {
+  // Set the init value to something that will let us know that we need to
+  // autodetect how many breakpoints are supported dynamically...
+  static uint32_t g_num_supported_hw_breakpoints = UINT_MAX;
+  if (g_num_supported_hw_breakpoints == UINT_MAX) {
+    // Set this to zero in case we can't tell if there are any HW breakpoints
+    g_num_supported_hw_breakpoints = 0;
+
+    size_t len;
+    uint32_t n = 0;
+    len = sizeof(n);
+    if (::sysctlbyname("hw.optional.breakpoint", &n, &len, NULL, 0) == 0) {
+      g_num_supported_hw_breakpoints = n;
+      DNBLogThreadedIf(LOG_THREAD, "hw.optional.breakpoint=%u", n);
+    } else {
+// For AArch64 we would need to look at ID_AA64DFR0_EL1 but debugserver runs in
+// EL0 so it can't access that reg.  The kernel should have filled in the
+// sysctls based on it though.
+#if defined(__arm__)
+      uint32_t register_DBGDIDR;
+
+      asm("mrc p14, 0, %0, c0, c0, 0" : "=r"(register_DBGDIDR));
+      uint32_t numWRPs = bits(register_DBGDIDR, 31, 28);
+      // Zero is reserved for the WRP count, so don't increment it if it is zero
+      if (numWRPs > 0)
+        numWRPs++;
+      g_num_supported_hw_breakpoints = numWRPs;
+      DNBLogThreadedIf(LOG_THREAD,
+                       "Number of supported hw breakpoint via asm():  %d",
+                       g_num_supported_hw_breakpoints);
+#endif
+    }
+  }
+  return g_num_supported_hw_breakpoints;
+}
+
+uint32_t DNBArchMachARM64::EnableHardwareBreakpoint(nub_addr_t addr,
+                                                    nub_size_t size,
+                                                    bool also_set_on_task) {
+  DNBLogThreadedIf(LOG_WATCHPOINTS,
+                   "DNBArchMachARM64::EnableHardwareBreakpoint(addr = "
+                   "0x%8.8llx, size = %zu)",
+                   (uint64_t)addr, size);
+
+  const uint32_t num_hw_breakpoints = NumSupportedHardwareBreakpoints();
+
+  nub_addr_t aligned_bp_address = addr;
+  uint32_t control_value = 0;
+
+  switch (size) {
+  case 2:
+    control_value = (0x3 << 5) | 7;
+    aligned_bp_address &= ~1;
+    break;
+  case 4:
+    control_value = (0xfu << 5) | 7;
+    aligned_bp_address &= ~3;
+    break;
+  };
+
+  // Read the debug state
+  kern_return_t kret = GetDBGState(false);
+  if (kret == KERN_SUCCESS) {
+    // Check to make sure we have the needed hardware support
+    uint32_t i = 0;
+
+    for (i = 0; i < num_hw_breakpoints; ++i) {
+      if ((m_state.dbg.__bcr[i] & BCR_ENABLE) == 0)
+        break; // We found an available hw breakpoint slot (in i)
+    }
+
+    // See if we found an available hw breakpoint slot above
+    if (i < num_hw_breakpoints) {
+      m_state.dbg.__bvr[i] = aligned_bp_address;
+      m_state.dbg.__bcr[i] = control_value;
+
+      DNBLogThreadedIf(LOG_WATCHPOINTS,
+                       "DNBArchMachARM64::EnableHardwareBreakpoint() "
+                       "adding breakpoint on address 0x%llx with control "
+                       "register value 0x%x",
+                       (uint64_t)m_state.dbg.__bvr[i],
+                       (uint32_t)m_state.dbg.__bcr[i]);
+
+      // The kernel will set the MDE_ENABLE bit in the MDSCR_EL1 for us
+      // automatically, don't need to do it here.
+      kret = SetDBGState(also_set_on_task);
+
+      DNBLogThreadedIf(LOG_WATCHPOINTS,
+                       "DNBArchMachARM64::"
+                       "EnableHardwareBreakpoint() "
+                       "SetDBGState() => 0x%8.8x.",
+                       kret);
+
+      if (kret == KERN_SUCCESS)
+        return i;
+    } else {
+      DNBLogThreadedIf(LOG_WATCHPOINTS,
+                       "DNBArchMachARM64::"
+                       "EnableHardwareBreakpoint(): All "
+                       "hardware resources (%u) are in use.",
+                       num_hw_breakpoints);
+    }
+  }
+  return INVALID_NUB_HW_INDEX;
+}
+
 uint32_t DNBArchMachARM64::EnableHardwareWatchpoint(nub_addr_t addr,
                                                     nub_size_t size, bool read,
                                                     bool write,
@@ -905,6 +1011,32 @@ bool DNBArchMachARM64::DisableHardwareWatchpoint_helper(uint32_t hw_index,
   return (kret == KERN_SUCCESS);
 }
 
+bool DNBArchMachARM64::DisableHardwareBreakpoint(uint32_t hw_index,
+                                                 bool also_set_on_task) {
+  kern_return_t kret = GetDBGState(false);
+  if (kret != KERN_SUCCESS)
+    return false;
+
+  const uint32_t num_hw_points = NumSupportedHardwareBreakpoints();
+  if (hw_index >= num_hw_points)
+    return false;
+
+  m_disabled_breakpoints[hw_index].addr = m_state.dbg.__bvr[hw_index];
+  m_disabled_breakpoints[hw_index].control = m_state.dbg.__bcr[hw_index];
+
+  m_state.dbg.__bcr[hw_index] = 0;
+  DNBLogThreadedIf(LOG_WATCHPOINTS,
+                   "DNBArchMachARM64::"
+                   "DisableHardwareBreakpoint( %u ) - WVR%u = "
+                   "0x%8.8llx  BCR%u = 0x%8.8llx",
+                   hw_index, hw_index, (uint64_t)m_state.dbg.__bvr[hw_index],
+                   hw_index, (uint64_t)m_state.dbg.__bcr[hw_index]);
+
+  kret = SetDBGState(also_set_on_task);
+
+  return (kret == KERN_SUCCESS);
+}
+
 // This is for checking the Byte Address Select bits in the DBRWCRn_EL1 control
 // register.
 // Returns -1 if the trailing bit patterns are not one of:

diff  --git a/lldb/tools/debugserver/source/MacOSX/arm64/DNBArchImplARM64.h b/lldb/tools/debugserver/source/MacOSX/arm64/DNBArchImplARM64.h
index ea4efa48d026..fafcb73837b7 100644
--- a/lldb/tools/debugserver/source/MacOSX/arm64/DNBArchImplARM64.h
+++ b/lldb/tools/debugserver/source/MacOSX/arm64/DNBArchImplARM64.h
@@ -26,10 +26,12 @@ class DNBArchMachARM64 : public DNBArchProtocol {
 
   DNBArchMachARM64(MachThread *thread)
       : m_thread(thread), m_state(), m_disabled_watchpoints(),
-        m_watchpoint_hw_index(-1), m_watchpoint_did_occur(false),
+        m_disabled_breakpoints(), m_watchpoint_hw_index(-1),
+        m_watchpoint_did_occur(false),
         m_watchpoint_resume_single_step_enabled(false),
         m_saved_register_states() {
     m_disabled_watchpoints.resize(16);
+    m_disabled_breakpoints.resize(16);
     memset(&m_dbg_save, 0, sizeof(m_dbg_save));
   }
 
@@ -62,7 +64,13 @@ class DNBArchMachARM64 : public DNBArchProtocol {
   static const uint8_t *SoftwareBreakpointOpcode(nub_size_t byte_size);
   static uint32_t GetCPUType();
 
+  virtual uint32_t NumSupportedHardwareBreakpoints();
   virtual uint32_t NumSupportedHardwareWatchpoints();
+
+  virtual uint32_t EnableHardwareBreakpoint(nub_addr_t addr, nub_size_t size,
+                                            bool also_set_on_task);
+  virtual bool DisableHardwareBreakpoint(uint32_t hw_break_index,
+                                         bool also_set_on_task);
   virtual uint32_t EnableHardwareWatchpoint(nub_addr_t addr, nub_size_t size,
                                             bool read, bool write,
                                             bool also_set_on_task);
@@ -229,10 +237,11 @@ class DNBArchMachARM64 : public DNBArchProtocol {
   State m_state;
   arm_debug_state64_t m_dbg_save;
 
-  // arm64 doesn't keep the disabled watchpoint values in the debug register
-  // context like armv7;
+  // arm64 doesn't keep the disabled watchpoint and breakpoint values in the
+  // debug register context like armv7;
   // we need to save them aside when we disable them temporarily.
   std::vector<disabled_watchpoint> m_disabled_watchpoints;
+  std::vector<disabled_watchpoint> m_disabled_breakpoints;
 
   // The following member variables should be updated atomically.
   int32_t m_watchpoint_hw_index;


        


More information about the lldb-commits mailing list