[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