1 //===-- NativeRegisterContextDBReg_arm64.cpp ------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "NativeRegisterContextDBReg_arm64.h"
11 #include "lldb/Utility/LLDBLog.h"
12 #include "lldb/Utility/Log.h"
13 #include "lldb/Utility/RegisterValue.h"
15 using namespace lldb_private
;
17 // E (bit 0), used to enable breakpoint/watchpoint
18 constexpr uint32_t g_enable_bit
= 1;
19 // PAC (bits 2:1): 0b10
20 constexpr uint32_t g_pac_bits
= (2 << 1);
22 // Returns appropriate control register bits for the specified size
23 static constexpr inline uint64_t GetSizeBits(int size
) {
24 // BAS (bits 12:5) hold a bit-mask of addresses to watch
25 // e.g. 0b00000001 means 1 byte at address
26 // 0b00000011 means 2 bytes (addr..addr+1)
28 // 0b11111111 means 8 bytes (addr..addr+7)
29 return ((1 << size
) - 1) << 5;
32 uint32_t NativeRegisterContextDBReg_arm64::NumSupportedHardwareBreakpoints() {
33 Log
*log
= GetLog(LLDBLog::Breakpoints
);
34 llvm::Error error
= ReadHardwareDebugInfo();
36 LLDB_LOG_ERROR(log
, std::move(error
),
37 "failed to read debug registers: {0}");
41 return m_max_hbp_supported
;
45 NativeRegisterContextDBReg_arm64::SetHardwareBreakpoint(lldb::addr_t addr
,
47 Log
*log
= GetLog(LLDBLog::Breakpoints
);
48 LLDB_LOG(log
, "addr: {0:x}, size: {1:x}", addr
, size
);
50 // Read hardware breakpoint and watchpoint information.
51 llvm::Error error
= ReadHardwareDebugInfo();
54 log
, std::move(error
),
55 "unable to set breakpoint: failed to read debug registers: {0}");
56 return LLDB_INVALID_INDEX32
;
59 uint32_t control_value
= 0, bp_index
= 0;
61 // Check if size has a valid hardware breakpoint length.
63 return LLDB_INVALID_INDEX32
; // Invalid size for a AArch64 hardware
66 // Check 4-byte alignment for hardware breakpoint target address.
68 return LLDB_INVALID_INDEX32
; // Invalid address, should be 4-byte aligned.
70 // Setup control value
71 control_value
= g_enable_bit
| g_pac_bits
| GetSizeBits(size
);
73 // Iterate over stored breakpoints and find a free bp_index
74 bp_index
= LLDB_INVALID_INDEX32
;
75 for (uint32_t i
= 0; i
< m_max_hbp_supported
; i
++) {
76 if (!BreakpointIsEnabled(i
))
77 bp_index
= i
; // Mark last free slot
78 else if (m_hbp_regs
[i
].address
== addr
)
79 return LLDB_INVALID_INDEX32
; // We do not support duplicate breakpoints.
82 if (bp_index
== LLDB_INVALID_INDEX32
)
83 return LLDB_INVALID_INDEX32
;
85 // Update breakpoint in local cache
86 m_hbp_regs
[bp_index
].real_addr
= addr
;
87 m_hbp_regs
[bp_index
].address
= addr
;
88 m_hbp_regs
[bp_index
].control
= control_value
;
90 // PTRACE call to set corresponding hardware breakpoint register.
91 error
= WriteHardwareDebugRegs(eDREGTypeBREAK
);
94 m_hbp_regs
[bp_index
].address
= 0;
95 m_hbp_regs
[bp_index
].control
&= ~1;
98 log
, std::move(error
),
99 "unable to set breakpoint: failed to write debug registers: {0}");
100 return LLDB_INVALID_INDEX32
;
106 bool NativeRegisterContextDBReg_arm64::ClearHardwareBreakpoint(
108 Log
*log
= GetLog(LLDBLog::Breakpoints
);
109 LLDB_LOG(log
, "hw_idx: {0}", hw_idx
);
111 // Read hardware breakpoint and watchpoint information.
112 llvm::Error error
= ReadHardwareDebugInfo();
115 log
, std::move(error
),
116 "unable to clear breakpoint: failed to read debug registers: {0}");
120 if (hw_idx
>= m_max_hbp_supported
)
123 // Create a backup we can revert to in case of failure.
124 lldb::addr_t tempAddr
= m_hbp_regs
[hw_idx
].address
;
125 uint32_t tempControl
= m_hbp_regs
[hw_idx
].control
;
127 m_hbp_regs
[hw_idx
].control
&= ~g_enable_bit
;
128 m_hbp_regs
[hw_idx
].address
= 0;
130 // PTRACE call to clear corresponding hardware breakpoint register.
131 error
= WriteHardwareDebugRegs(eDREGTypeBREAK
);
134 m_hbp_regs
[hw_idx
].control
= tempControl
;
135 m_hbp_regs
[hw_idx
].address
= tempAddr
;
138 log
, std::move(error
),
139 "unable to clear breakpoint: failed to write debug registers: {0}");
146 Status
NativeRegisterContextDBReg_arm64::GetHardwareBreakHitIndex(
147 uint32_t &bp_index
, lldb::addr_t trap_addr
) {
148 Log
*log
= GetLog(LLDBLog::Breakpoints
);
150 LLDB_LOGF(log
, "NativeRegisterContextDBReg_arm64::%s()", __FUNCTION__
);
152 lldb::addr_t break_addr
;
154 for (bp_index
= 0; bp_index
< m_max_hbp_supported
; ++bp_index
) {
155 break_addr
= m_hbp_regs
[bp_index
].address
;
157 if (BreakpointIsEnabled(bp_index
) && trap_addr
== break_addr
) {
158 m_hbp_regs
[bp_index
].hit_addr
= trap_addr
;
163 bp_index
= LLDB_INVALID_INDEX32
;
167 Status
NativeRegisterContextDBReg_arm64::ClearAllHardwareBreakpoints() {
168 Log
*log
= GetLog(LLDBLog::Breakpoints
);
170 LLDB_LOGF(log
, "NativeRegisterContextDBReg_arm64::%s()", __FUNCTION__
);
172 // Read hardware breakpoint and watchpoint information.
173 llvm::Error error
= ReadHardwareDebugInfo();
175 return Status(std::move(error
));
177 for (uint32_t i
= 0; i
< m_max_hbp_supported
; i
++) {
178 if (BreakpointIsEnabled(i
)) {
179 // Create a backup we can revert to in case of failure.
180 lldb::addr_t tempAddr
= m_hbp_regs
[i
].address
;
181 uint32_t tempControl
= m_hbp_regs
[i
].control
;
183 // Clear watchpoints in local cache
184 m_hbp_regs
[i
].control
&= ~g_enable_bit
;
185 m_hbp_regs
[i
].address
= 0;
187 // Ptrace call to update hardware debug registers
188 error
= WriteHardwareDebugRegs(eDREGTypeBREAK
);
191 m_hbp_regs
[i
].control
= tempControl
;
192 m_hbp_regs
[i
].address
= tempAddr
;
194 return Status(std::move(error
));
202 bool NativeRegisterContextDBReg_arm64::BreakpointIsEnabled(uint32_t bp_index
) {
203 if ((m_hbp_regs
[bp_index
].control
& g_enable_bit
) != 0)
209 uint32_t NativeRegisterContextDBReg_arm64::NumSupportedHardwareWatchpoints() {
210 Log
*log
= GetLog(LLDBLog::Watchpoints
);
211 llvm::Error error
= ReadHardwareDebugInfo();
213 LLDB_LOG_ERROR(log
, std::move(error
),
214 "failed to read debug registers: {0}");
218 return m_max_hwp_supported
;
221 uint32_t NativeRegisterContextDBReg_arm64::SetHardwareWatchpoint(
222 lldb::addr_t addr
, size_t size
, uint32_t watch_flags
) {
223 Log
*log
= GetLog(LLDBLog::Watchpoints
);
224 LLDB_LOG(log
, "addr: {0:x}, size: {1:x} watch_flags: {2:x}", addr
, size
,
227 // Read hardware breakpoint and watchpoint information.
228 llvm::Error error
= ReadHardwareDebugInfo();
231 log
, std::move(error
),
232 "unable to set watchpoint: failed to read debug registers: {0}");
233 return LLDB_INVALID_INDEX32
;
236 uint32_t control_value
= 0, wp_index
= 0;
237 lldb::addr_t real_addr
= addr
;
239 // Check if we are setting watchpoint other than read/write/access Also
240 // update watchpoint flag to match AArch64 write-read bit configuration.
241 switch (watch_flags
) {
251 return LLDB_INVALID_INDEX32
;
254 // Check if size has a valid hardware watchpoint length.
255 if (size
!= 1 && size
!= 2 && size
!= 4 && size
!= 8)
256 return LLDB_INVALID_INDEX32
;
258 // Check 8-byte alignment for hardware watchpoint target address. Below is a
259 // hack to recalculate address and size in order to make sure we can watch
260 // non 8-byte aligned addresses as well.
262 uint8_t watch_mask
= (addr
& 0x07) + size
;
264 if (watch_mask
> 0x08)
265 return LLDB_INVALID_INDEX32
;
266 else if (watch_mask
<= 0x02)
268 else if (watch_mask
<= 0x04)
273 addr
= addr
& (~0x07);
276 // Setup control value
277 control_value
= g_enable_bit
| g_pac_bits
| GetSizeBits(size
);
278 control_value
|= watch_flags
<< 3;
280 // Iterate over stored watchpoints and find a free wp_index
281 wp_index
= LLDB_INVALID_INDEX32
;
282 for (uint32_t i
= 0; i
< m_max_hwp_supported
; i
++) {
283 if (!WatchpointIsEnabled(i
))
284 wp_index
= i
; // Mark last free slot
285 else if (m_hwp_regs
[i
].address
== addr
) {
286 return LLDB_INVALID_INDEX32
; // We do not support duplicate watchpoints.
290 if (wp_index
== LLDB_INVALID_INDEX32
)
291 return LLDB_INVALID_INDEX32
;
293 // Update watchpoint in local cache
294 m_hwp_regs
[wp_index
].real_addr
= real_addr
;
295 m_hwp_regs
[wp_index
].address
= addr
;
296 m_hwp_regs
[wp_index
].control
= control_value
;
298 // PTRACE call to set corresponding watchpoint register.
299 error
= WriteHardwareDebugRegs(eDREGTypeWATCH
);
302 m_hwp_regs
[wp_index
].address
= 0;
303 m_hwp_regs
[wp_index
].control
&= ~g_enable_bit
;
306 log
, std::move(error
),
307 "unable to set watchpoint: failed to write debug registers: {0}");
308 return LLDB_INVALID_INDEX32
;
314 bool NativeRegisterContextDBReg_arm64::ClearHardwareWatchpoint(
316 Log
*log
= GetLog(LLDBLog::Watchpoints
);
317 LLDB_LOG(log
, "wp_index: {0}", wp_index
);
319 // Read hardware breakpoint and watchpoint information.
320 llvm::Error error
= ReadHardwareDebugInfo();
323 log
, std::move(error
),
324 "unable to clear watchpoint: failed to read debug registers: {0}");
328 if (wp_index
>= m_max_hwp_supported
)
331 // Create a backup we can revert to in case of failure.
332 lldb::addr_t tempAddr
= m_hwp_regs
[wp_index
].address
;
333 uint32_t tempControl
= m_hwp_regs
[wp_index
].control
;
335 // Update watchpoint in local cache
336 m_hwp_regs
[wp_index
].control
&= ~g_enable_bit
;
337 m_hwp_regs
[wp_index
].address
= 0;
339 // Ptrace call to update hardware debug registers
340 error
= WriteHardwareDebugRegs(eDREGTypeWATCH
);
343 m_hwp_regs
[wp_index
].control
= tempControl
;
344 m_hwp_regs
[wp_index
].address
= tempAddr
;
347 log
, std::move(error
),
348 "unable to clear watchpoint: failed to write debug registers: {0}");
355 Status
NativeRegisterContextDBReg_arm64::ClearAllHardwareWatchpoints() {
356 // Read hardware breakpoint and watchpoint information.
357 llvm::Error error
= ReadHardwareDebugInfo();
359 return Status(std::move(error
));
361 for (uint32_t i
= 0; i
< m_max_hwp_supported
; i
++) {
362 if (WatchpointIsEnabled(i
)) {
363 // Create a backup we can revert to in case of failure.
364 lldb::addr_t tempAddr
= m_hwp_regs
[i
].address
;
365 uint32_t tempControl
= m_hwp_regs
[i
].control
;
367 // Clear watchpoints in local cache
368 m_hwp_regs
[i
].control
&= ~g_enable_bit
;
369 m_hwp_regs
[i
].address
= 0;
371 // Ptrace call to update hardware debug registers
372 error
= WriteHardwareDebugRegs(eDREGTypeWATCH
);
375 m_hwp_regs
[i
].control
= tempControl
;
376 m_hwp_regs
[i
].address
= tempAddr
;
378 return Status(std::move(error
));
387 NativeRegisterContextDBReg_arm64::GetWatchpointSize(uint32_t wp_index
) {
388 Log
*log
= GetLog(LLDBLog::Watchpoints
);
389 LLDB_LOG(log
, "wp_index: {0}", wp_index
);
391 switch ((m_hwp_regs
[wp_index
].control
>> 5) & 0xff) {
405 bool NativeRegisterContextDBReg_arm64::WatchpointIsEnabled(uint32_t wp_index
) {
406 Log
*log
= GetLog(LLDBLog::Watchpoints
);
407 LLDB_LOG(log
, "wp_index: {0}", wp_index
);
409 if ((m_hwp_regs
[wp_index
].control
& g_enable_bit
) != 0)
415 Status
NativeRegisterContextDBReg_arm64::GetWatchpointHitIndex(
416 uint32_t &wp_index
, lldb::addr_t trap_addr
) {
417 Log
*log
= GetLog(LLDBLog::Watchpoints
);
418 LLDB_LOG(log
, "wp_index: {0}, trap_addr: {1:x}", wp_index
, trap_addr
);
420 // Read hardware breakpoint and watchpoint information.
421 llvm::Error error
= ReadHardwareDebugInfo();
423 return Status(std::move(error
));
425 // Mask off ignored bits from watchpoint trap address.
426 trap_addr
= FixWatchpointHitAddress(trap_addr
);
429 lldb::addr_t watch_addr
;
431 for (wp_index
= 0; wp_index
< m_max_hwp_supported
; ++wp_index
) {
432 watch_size
= GetWatchpointSize(wp_index
);
433 watch_addr
= m_hwp_regs
[wp_index
].address
;
435 if (WatchpointIsEnabled(wp_index
) && trap_addr
>= watch_addr
&&
436 trap_addr
< watch_addr
+ watch_size
) {
437 m_hwp_regs
[wp_index
].hit_addr
= trap_addr
;
442 wp_index
= LLDB_INVALID_INDEX32
;
447 NativeRegisterContextDBReg_arm64::GetWatchpointAddress(uint32_t wp_index
) {
448 Log
*log
= GetLog(LLDBLog::Watchpoints
);
449 LLDB_LOG(log
, "wp_index: {0}", wp_index
);
451 if (wp_index
>= m_max_hwp_supported
)
452 return LLDB_INVALID_ADDRESS
;
454 if (WatchpointIsEnabled(wp_index
))
455 return m_hwp_regs
[wp_index
].real_addr
;
456 return LLDB_INVALID_ADDRESS
;
460 NativeRegisterContextDBReg_arm64::GetWatchpointHitAddress(uint32_t wp_index
) {
461 Log
*log
= GetLog(LLDBLog::Watchpoints
);
462 LLDB_LOG(log
, "wp_index: {0}", wp_index
);
464 if (wp_index
>= m_max_hwp_supported
)
465 return LLDB_INVALID_ADDRESS
;
467 if (WatchpointIsEnabled(wp_index
))
468 return m_hwp_regs
[wp_index
].hit_addr
;
469 return LLDB_INVALID_ADDRESS
;