1af732203SDimitry Andric //===-- NativeRegisterContextDBReg_arm64.cpp ------------------------------===//
2af732203SDimitry Andric //
3af732203SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4af732203SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5af732203SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6af732203SDimitry Andric //
7af732203SDimitry Andric //===----------------------------------------------------------------------===//
8af732203SDimitry Andric 
9af732203SDimitry Andric #include "NativeRegisterContextDBReg_arm64.h"
10af732203SDimitry Andric 
11af732203SDimitry Andric #include "lldb/Utility/Log.h"
12af732203SDimitry Andric #include "lldb/Utility/RegisterValue.h"
13af732203SDimitry Andric 
14af732203SDimitry Andric using namespace lldb_private;
15af732203SDimitry Andric 
16af732203SDimitry Andric // E (bit 0), used to enable breakpoint/watchpoint
17af732203SDimitry Andric constexpr uint32_t g_enable_bit = 1;
18af732203SDimitry Andric // PAC (bits 2:1): 0b10
19af732203SDimitry Andric constexpr uint32_t g_pac_bits = (2 << 1);
20af732203SDimitry Andric 
21af732203SDimitry Andric // Returns appropriate control register bits for the specified size
GetSizeBits(int size)22af732203SDimitry Andric static constexpr inline uint64_t GetSizeBits(int size) {
23af732203SDimitry Andric   // BAS (bits 12:5) hold a bit-mask of addresses to watch
24af732203SDimitry Andric   // e.g. 0b00000001 means 1 byte at address
25af732203SDimitry Andric   //      0b00000011 means 2 bytes (addr..addr+1)
26af732203SDimitry Andric   //      ...
27af732203SDimitry Andric   //      0b11111111 means 8 bytes (addr..addr+7)
28af732203SDimitry Andric   return ((1 << size) - 1) << 5;
29af732203SDimitry Andric }
30af732203SDimitry Andric 
NumSupportedHardwareBreakpoints()31af732203SDimitry Andric uint32_t NativeRegisterContextDBReg_arm64::NumSupportedHardwareBreakpoints() {
32af732203SDimitry Andric   Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
33af732203SDimitry Andric   llvm::Error error = ReadHardwareDebugInfo();
34af732203SDimitry Andric   if (error) {
35af732203SDimitry Andric     LLDB_LOG_ERROR(log, std::move(error),
36af732203SDimitry Andric                    "failed to read debug registers: {0}");
37af732203SDimitry Andric     return 0;
38af732203SDimitry Andric   }
39af732203SDimitry Andric 
40af732203SDimitry Andric   return m_max_hbp_supported;
41af732203SDimitry Andric }
42af732203SDimitry Andric 
43af732203SDimitry Andric uint32_t
SetHardwareBreakpoint(lldb::addr_t addr,size_t size)44af732203SDimitry Andric NativeRegisterContextDBReg_arm64::SetHardwareBreakpoint(lldb::addr_t addr,
45af732203SDimitry Andric                                                         size_t size) {
46af732203SDimitry Andric   Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
47af732203SDimitry Andric   LLDB_LOG(log, "addr: {0:x}, size: {1:x}", addr, size);
48af732203SDimitry Andric 
49af732203SDimitry Andric   // Read hardware breakpoint and watchpoint information.
50af732203SDimitry Andric   llvm::Error error = ReadHardwareDebugInfo();
51af732203SDimitry Andric   if (error) {
52af732203SDimitry Andric     LLDB_LOG_ERROR(
53af732203SDimitry Andric         log, std::move(error),
54af732203SDimitry Andric         "unable to set breakpoint: failed to read debug registers: {0}");
55af732203SDimitry Andric     return LLDB_INVALID_INDEX32;
56af732203SDimitry Andric   }
57af732203SDimitry Andric 
58af732203SDimitry Andric   uint32_t control_value = 0, bp_index = 0;
59af732203SDimitry Andric 
60af732203SDimitry Andric   // Check if size has a valid hardware breakpoint length.
61af732203SDimitry Andric   if (size != 4)
62af732203SDimitry Andric     return LLDB_INVALID_INDEX32; // Invalid size for a AArch64 hardware
63af732203SDimitry Andric                                  // breakpoint
64af732203SDimitry Andric 
65af732203SDimitry Andric   // Check 4-byte alignment for hardware breakpoint target address.
66af732203SDimitry Andric   if (addr & 0x03)
67af732203SDimitry Andric     return LLDB_INVALID_INDEX32; // Invalid address, should be 4-byte aligned.
68af732203SDimitry Andric 
69af732203SDimitry Andric   // Setup control value
70af732203SDimitry Andric   control_value = g_enable_bit | g_pac_bits | GetSizeBits(size);
71af732203SDimitry Andric 
72af732203SDimitry Andric   // Iterate over stored breakpoints and find a free bp_index
73af732203SDimitry Andric   bp_index = LLDB_INVALID_INDEX32;
74af732203SDimitry Andric   for (uint32_t i = 0; i < m_max_hbp_supported; i++) {
75af732203SDimitry Andric     if (!BreakpointIsEnabled(i))
76af732203SDimitry Andric       bp_index = i; // Mark last free slot
77af732203SDimitry Andric     else if (m_hbp_regs[i].address == addr)
78af732203SDimitry Andric       return LLDB_INVALID_INDEX32; // We do not support duplicate breakpoints.
79af732203SDimitry Andric   }
80af732203SDimitry Andric 
81af732203SDimitry Andric   if (bp_index == LLDB_INVALID_INDEX32)
82af732203SDimitry Andric     return LLDB_INVALID_INDEX32;
83af732203SDimitry Andric 
84af732203SDimitry Andric   // Update breakpoint in local cache
85af732203SDimitry Andric   m_hbp_regs[bp_index].real_addr = addr;
86af732203SDimitry Andric   m_hbp_regs[bp_index].address = addr;
87af732203SDimitry Andric   m_hbp_regs[bp_index].control = control_value;
88af732203SDimitry Andric 
89af732203SDimitry Andric   // PTRACE call to set corresponding hardware breakpoint register.
90af732203SDimitry Andric   error = WriteHardwareDebugRegs(eDREGTypeBREAK);
91af732203SDimitry Andric 
92af732203SDimitry Andric   if (error) {
93af732203SDimitry Andric     m_hbp_regs[bp_index].address = 0;
94af732203SDimitry Andric     m_hbp_regs[bp_index].control &= ~1;
95af732203SDimitry Andric 
96af732203SDimitry Andric     LLDB_LOG_ERROR(
97af732203SDimitry Andric         log, std::move(error),
98af732203SDimitry Andric         "unable to set breakpoint: failed to write debug registers: {0}");
99af732203SDimitry Andric     return LLDB_INVALID_INDEX32;
100af732203SDimitry Andric   }
101af732203SDimitry Andric 
102af732203SDimitry Andric   return bp_index;
103af732203SDimitry Andric }
104af732203SDimitry Andric 
ClearHardwareBreakpoint(uint32_t hw_idx)105af732203SDimitry Andric bool NativeRegisterContextDBReg_arm64::ClearHardwareBreakpoint(
106af732203SDimitry Andric     uint32_t hw_idx) {
107af732203SDimitry Andric   Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
108af732203SDimitry Andric   LLDB_LOG(log, "hw_idx: {0}", hw_idx);
109af732203SDimitry Andric 
110af732203SDimitry Andric   // Read hardware breakpoint and watchpoint information.
111af732203SDimitry Andric   llvm::Error error = ReadHardwareDebugInfo();
112af732203SDimitry Andric   if (error) {
113af732203SDimitry Andric     LLDB_LOG_ERROR(
114af732203SDimitry Andric         log, std::move(error),
115af732203SDimitry Andric         "unable to clear breakpoint: failed to read debug registers: {0}");
116af732203SDimitry Andric     return false;
117af732203SDimitry Andric   }
118af732203SDimitry Andric 
119af732203SDimitry Andric   if (hw_idx >= m_max_hbp_supported)
120af732203SDimitry Andric     return false;
121af732203SDimitry Andric 
122af732203SDimitry Andric   // Create a backup we can revert to in case of failure.
123af732203SDimitry Andric   lldb::addr_t tempAddr = m_hbp_regs[hw_idx].address;
124af732203SDimitry Andric   uint32_t tempControl = m_hbp_regs[hw_idx].control;
125af732203SDimitry Andric 
126af732203SDimitry Andric   m_hbp_regs[hw_idx].control &= ~g_enable_bit;
127af732203SDimitry Andric   m_hbp_regs[hw_idx].address = 0;
128af732203SDimitry Andric 
129af732203SDimitry Andric   // PTRACE call to clear corresponding hardware breakpoint register.
130af732203SDimitry Andric   error = WriteHardwareDebugRegs(eDREGTypeBREAK);
131af732203SDimitry Andric 
132af732203SDimitry Andric   if (error) {
133af732203SDimitry Andric     m_hbp_regs[hw_idx].control = tempControl;
134af732203SDimitry Andric     m_hbp_regs[hw_idx].address = tempAddr;
135af732203SDimitry Andric 
136af732203SDimitry Andric     LLDB_LOG_ERROR(
137af732203SDimitry Andric         log, std::move(error),
138af732203SDimitry Andric         "unable to clear breakpoint: failed to write debug registers: {0}");
139af732203SDimitry Andric     return false;
140af732203SDimitry Andric   }
141af732203SDimitry Andric 
142af732203SDimitry Andric   return true;
143af732203SDimitry Andric }
144af732203SDimitry Andric 
GetHardwareBreakHitIndex(uint32_t & bp_index,lldb::addr_t trap_addr)145af732203SDimitry Andric Status NativeRegisterContextDBReg_arm64::GetHardwareBreakHitIndex(
146af732203SDimitry Andric     uint32_t &bp_index, lldb::addr_t trap_addr) {
147af732203SDimitry Andric   Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
148af732203SDimitry Andric 
149af732203SDimitry Andric   LLDB_LOGF(log, "NativeRegisterContextDBReg_arm64::%s()", __FUNCTION__);
150af732203SDimitry Andric 
151af732203SDimitry Andric   lldb::addr_t break_addr;
152af732203SDimitry Andric 
153af732203SDimitry Andric   for (bp_index = 0; bp_index < m_max_hbp_supported; ++bp_index) {
154af732203SDimitry Andric     break_addr = m_hbp_regs[bp_index].address;
155af732203SDimitry Andric 
156af732203SDimitry Andric     if (BreakpointIsEnabled(bp_index) && trap_addr == break_addr) {
157af732203SDimitry Andric       m_hbp_regs[bp_index].hit_addr = trap_addr;
158af732203SDimitry Andric       return Status();
159af732203SDimitry Andric     }
160af732203SDimitry Andric   }
161af732203SDimitry Andric 
162af732203SDimitry Andric   bp_index = LLDB_INVALID_INDEX32;
163af732203SDimitry Andric   return Status();
164af732203SDimitry Andric }
165af732203SDimitry Andric 
ClearAllHardwareBreakpoints()166af732203SDimitry Andric Status NativeRegisterContextDBReg_arm64::ClearAllHardwareBreakpoints() {
167af732203SDimitry Andric   Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
168af732203SDimitry Andric 
169af732203SDimitry Andric   LLDB_LOGF(log, "NativeRegisterContextDBReg_arm64::%s()", __FUNCTION__);
170af732203SDimitry Andric 
171af732203SDimitry Andric   // Read hardware breakpoint and watchpoint information.
172af732203SDimitry Andric   llvm::Error error = ReadHardwareDebugInfo();
173af732203SDimitry Andric   if (error)
174af732203SDimitry Andric     return Status(std::move(error));
175af732203SDimitry Andric 
176af732203SDimitry Andric   for (uint32_t i = 0; i < m_max_hbp_supported; i++) {
177af732203SDimitry Andric     if (BreakpointIsEnabled(i)) {
178af732203SDimitry Andric       // Create a backup we can revert to in case of failure.
179af732203SDimitry Andric       lldb::addr_t tempAddr = m_hbp_regs[i].address;
180af732203SDimitry Andric       uint32_t tempControl = m_hbp_regs[i].control;
181af732203SDimitry Andric 
182af732203SDimitry Andric       // Clear watchpoints in local cache
183af732203SDimitry Andric       m_hbp_regs[i].control &= ~g_enable_bit;
184af732203SDimitry Andric       m_hbp_regs[i].address = 0;
185af732203SDimitry Andric 
186af732203SDimitry Andric       // Ptrace call to update hardware debug registers
187af732203SDimitry Andric       error = WriteHardwareDebugRegs(eDREGTypeBREAK);
188af732203SDimitry Andric 
189af732203SDimitry Andric       if (error) {
190af732203SDimitry Andric         m_hbp_regs[i].control = tempControl;
191af732203SDimitry Andric         m_hbp_regs[i].address = tempAddr;
192af732203SDimitry Andric 
193af732203SDimitry Andric         return Status(std::move(error));
194af732203SDimitry Andric       }
195af732203SDimitry Andric     }
196af732203SDimitry Andric   }
197af732203SDimitry Andric 
198af732203SDimitry Andric   return Status();
199af732203SDimitry Andric }
200af732203SDimitry Andric 
BreakpointIsEnabled(uint32_t bp_index)201af732203SDimitry Andric bool NativeRegisterContextDBReg_arm64::BreakpointIsEnabled(uint32_t bp_index) {
202af732203SDimitry Andric   if ((m_hbp_regs[bp_index].control & g_enable_bit) != 0)
203af732203SDimitry Andric     return true;
204af732203SDimitry Andric   else
205af732203SDimitry Andric     return false;
206af732203SDimitry Andric }
207af732203SDimitry Andric 
NumSupportedHardwareWatchpoints()208af732203SDimitry Andric uint32_t NativeRegisterContextDBReg_arm64::NumSupportedHardwareWatchpoints() {
209af732203SDimitry Andric   Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS));
210af732203SDimitry Andric   llvm::Error error = ReadHardwareDebugInfo();
211af732203SDimitry Andric   if (error) {
212af732203SDimitry Andric     LLDB_LOG_ERROR(log, std::move(error),
213af732203SDimitry Andric                    "failed to read debug registers: {0}");
214af732203SDimitry Andric     return 0;
215af732203SDimitry Andric   }
216af732203SDimitry Andric 
217af732203SDimitry Andric   return m_max_hwp_supported;
218af732203SDimitry Andric }
219af732203SDimitry Andric 
SetHardwareWatchpoint(lldb::addr_t addr,size_t size,uint32_t watch_flags)220af732203SDimitry Andric uint32_t NativeRegisterContextDBReg_arm64::SetHardwareWatchpoint(
221af732203SDimitry Andric     lldb::addr_t addr, size_t size, uint32_t watch_flags) {
222af732203SDimitry Andric   Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS));
223af732203SDimitry Andric   LLDB_LOG(log, "addr: {0:x}, size: {1:x} watch_flags: {2:x}", addr, size,
224af732203SDimitry Andric            watch_flags);
225af732203SDimitry Andric 
226af732203SDimitry Andric   // Read hardware breakpoint and watchpoint information.
227af732203SDimitry Andric   llvm::Error error = ReadHardwareDebugInfo();
228af732203SDimitry Andric   if (error) {
229af732203SDimitry Andric     LLDB_LOG_ERROR(
230af732203SDimitry Andric         log, std::move(error),
231af732203SDimitry Andric         "unable to set watchpoint: failed to read debug registers: {0}");
232af732203SDimitry Andric     return LLDB_INVALID_INDEX32;
233af732203SDimitry Andric   }
234af732203SDimitry Andric 
235af732203SDimitry Andric   uint32_t control_value = 0, wp_index = 0;
236af732203SDimitry Andric   lldb::addr_t real_addr = addr;
237af732203SDimitry Andric 
238af732203SDimitry Andric   // Check if we are setting watchpoint other than read/write/access Also
239af732203SDimitry Andric   // update watchpoint flag to match AArch64 write-read bit configuration.
240af732203SDimitry Andric   switch (watch_flags) {
241af732203SDimitry Andric   case 1:
242af732203SDimitry Andric     watch_flags = 2;
243af732203SDimitry Andric     break;
244af732203SDimitry Andric   case 2:
245af732203SDimitry Andric     watch_flags = 1;
246af732203SDimitry Andric     break;
247af732203SDimitry Andric   case 3:
248af732203SDimitry Andric     break;
249af732203SDimitry Andric   default:
250af732203SDimitry Andric     return LLDB_INVALID_INDEX32;
251af732203SDimitry Andric   }
252af732203SDimitry Andric 
253af732203SDimitry Andric   // Check if size has a valid hardware watchpoint length.
254af732203SDimitry Andric   if (size != 1 && size != 2 && size != 4 && size != 8)
255af732203SDimitry Andric     return LLDB_INVALID_INDEX32;
256af732203SDimitry Andric 
257af732203SDimitry Andric   // Check 8-byte alignment for hardware watchpoint target address. Below is a
258af732203SDimitry Andric   // hack to recalculate address and size in order to make sure we can watch
259af732203SDimitry Andric   // non 8-byte aligned addresses as well.
260af732203SDimitry Andric   if (addr & 0x07) {
261af732203SDimitry Andric     uint8_t watch_mask = (addr & 0x07) + size;
262af732203SDimitry Andric 
263af732203SDimitry Andric     if (watch_mask > 0x08)
264af732203SDimitry Andric       return LLDB_INVALID_INDEX32;
265af732203SDimitry Andric     else if (watch_mask <= 0x02)
266af732203SDimitry Andric       size = 2;
267af732203SDimitry Andric     else if (watch_mask <= 0x04)
268af732203SDimitry Andric       size = 4;
269af732203SDimitry Andric     else
270af732203SDimitry Andric       size = 8;
271af732203SDimitry Andric 
272af732203SDimitry Andric     addr = addr & (~0x07);
273af732203SDimitry Andric   }
274af732203SDimitry Andric 
275af732203SDimitry Andric   // Setup control value
276af732203SDimitry Andric   control_value = g_enable_bit | g_pac_bits | GetSizeBits(size);
277af732203SDimitry Andric   control_value |= watch_flags << 3;
278af732203SDimitry Andric 
279af732203SDimitry Andric   // Iterate over stored watchpoints and find a free wp_index
280af732203SDimitry Andric   wp_index = LLDB_INVALID_INDEX32;
281af732203SDimitry Andric   for (uint32_t i = 0; i < m_max_hwp_supported; i++) {
282af732203SDimitry Andric     if (!WatchpointIsEnabled(i))
283af732203SDimitry Andric       wp_index = i; // Mark last free slot
284af732203SDimitry Andric     else if (m_hwp_regs[i].address == addr) {
285af732203SDimitry Andric       return LLDB_INVALID_INDEX32; // We do not support duplicate watchpoints.
286af732203SDimitry Andric     }
287af732203SDimitry Andric   }
288af732203SDimitry Andric 
289af732203SDimitry Andric   if (wp_index == LLDB_INVALID_INDEX32)
290af732203SDimitry Andric     return LLDB_INVALID_INDEX32;
291af732203SDimitry Andric 
292af732203SDimitry Andric   // Update watchpoint in local cache
293af732203SDimitry Andric   m_hwp_regs[wp_index].real_addr = real_addr;
294af732203SDimitry Andric   m_hwp_regs[wp_index].address = addr;
295af732203SDimitry Andric   m_hwp_regs[wp_index].control = control_value;
296af732203SDimitry Andric 
297af732203SDimitry Andric   // PTRACE call to set corresponding watchpoint register.
298af732203SDimitry Andric   error = WriteHardwareDebugRegs(eDREGTypeWATCH);
299af732203SDimitry Andric 
300af732203SDimitry Andric   if (error) {
301af732203SDimitry Andric     m_hwp_regs[wp_index].address = 0;
302af732203SDimitry Andric     m_hwp_regs[wp_index].control &= ~g_enable_bit;
303af732203SDimitry Andric 
304af732203SDimitry Andric     LLDB_LOG_ERROR(
305af732203SDimitry Andric         log, std::move(error),
306af732203SDimitry Andric         "unable to set watchpoint: failed to write debug registers: {0}");
307af732203SDimitry Andric     return LLDB_INVALID_INDEX32;
308af732203SDimitry Andric   }
309af732203SDimitry Andric 
310af732203SDimitry Andric   return wp_index;
311af732203SDimitry Andric }
312af732203SDimitry Andric 
ClearHardwareWatchpoint(uint32_t wp_index)313af732203SDimitry Andric bool NativeRegisterContextDBReg_arm64::ClearHardwareWatchpoint(
314af732203SDimitry Andric     uint32_t wp_index) {
315af732203SDimitry Andric   Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS));
316af732203SDimitry Andric   LLDB_LOG(log, "wp_index: {0}", wp_index);
317af732203SDimitry Andric 
318af732203SDimitry Andric   // Read hardware breakpoint and watchpoint information.
319af732203SDimitry Andric   llvm::Error error = ReadHardwareDebugInfo();
320af732203SDimitry Andric   if (error) {
321af732203SDimitry Andric     LLDB_LOG_ERROR(
322af732203SDimitry Andric         log, std::move(error),
323af732203SDimitry Andric         "unable to clear watchpoint: failed to read debug registers: {0}");
324af732203SDimitry Andric     return false;
325af732203SDimitry Andric   }
326af732203SDimitry Andric 
327af732203SDimitry Andric   if (wp_index >= m_max_hwp_supported)
328af732203SDimitry Andric     return false;
329af732203SDimitry Andric 
330af732203SDimitry Andric   // Create a backup we can revert to in case of failure.
331af732203SDimitry Andric   lldb::addr_t tempAddr = m_hwp_regs[wp_index].address;
332af732203SDimitry Andric   uint32_t tempControl = m_hwp_regs[wp_index].control;
333af732203SDimitry Andric 
334af732203SDimitry Andric   // Update watchpoint in local cache
335af732203SDimitry Andric   m_hwp_regs[wp_index].control &= ~g_enable_bit;
336af732203SDimitry Andric   m_hwp_regs[wp_index].address = 0;
337af732203SDimitry Andric 
338af732203SDimitry Andric   // Ptrace call to update hardware debug registers
339af732203SDimitry Andric   error = WriteHardwareDebugRegs(eDREGTypeWATCH);
340af732203SDimitry Andric 
341af732203SDimitry Andric   if (error) {
342af732203SDimitry Andric     m_hwp_regs[wp_index].control = tempControl;
343af732203SDimitry Andric     m_hwp_regs[wp_index].address = tempAddr;
344af732203SDimitry Andric 
345af732203SDimitry Andric     LLDB_LOG_ERROR(
346af732203SDimitry Andric         log, std::move(error),
347af732203SDimitry Andric         "unable to clear watchpoint: failed to write debug registers: {0}");
348af732203SDimitry Andric     return false;
349af732203SDimitry Andric   }
350af732203SDimitry Andric 
351af732203SDimitry Andric   return true;
352af732203SDimitry Andric }
353af732203SDimitry Andric 
ClearAllHardwareWatchpoints()354af732203SDimitry Andric Status NativeRegisterContextDBReg_arm64::ClearAllHardwareWatchpoints() {
355af732203SDimitry Andric   // Read hardware breakpoint and watchpoint information.
356af732203SDimitry Andric   llvm::Error error = ReadHardwareDebugInfo();
357af732203SDimitry Andric   if (error)
358af732203SDimitry Andric     return Status(std::move(error));
359af732203SDimitry Andric 
360af732203SDimitry Andric   for (uint32_t i = 0; i < m_max_hwp_supported; i++) {
361af732203SDimitry Andric     if (WatchpointIsEnabled(i)) {
362af732203SDimitry Andric       // Create a backup we can revert to in case of failure.
363af732203SDimitry Andric       lldb::addr_t tempAddr = m_hwp_regs[i].address;
364af732203SDimitry Andric       uint32_t tempControl = m_hwp_regs[i].control;
365af732203SDimitry Andric 
366af732203SDimitry Andric       // Clear watchpoints in local cache
367af732203SDimitry Andric       m_hwp_regs[i].control &= ~g_enable_bit;
368af732203SDimitry Andric       m_hwp_regs[i].address = 0;
369af732203SDimitry Andric 
370af732203SDimitry Andric       // Ptrace call to update hardware debug registers
371af732203SDimitry Andric       error = WriteHardwareDebugRegs(eDREGTypeWATCH);
372af732203SDimitry Andric 
373af732203SDimitry Andric       if (error) {
374af732203SDimitry Andric         m_hwp_regs[i].control = tempControl;
375af732203SDimitry Andric         m_hwp_regs[i].address = tempAddr;
376af732203SDimitry Andric 
377af732203SDimitry Andric         return Status(std::move(error));
378af732203SDimitry Andric       }
379af732203SDimitry Andric     }
380af732203SDimitry Andric   }
381af732203SDimitry Andric 
382af732203SDimitry Andric   return Status();
383af732203SDimitry Andric }
384af732203SDimitry Andric 
385af732203SDimitry Andric uint32_t
GetWatchpointSize(uint32_t wp_index)386af732203SDimitry Andric NativeRegisterContextDBReg_arm64::GetWatchpointSize(uint32_t wp_index) {
387af732203SDimitry Andric   Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS));
388af732203SDimitry Andric   LLDB_LOG(log, "wp_index: {0}", wp_index);
389af732203SDimitry Andric 
390af732203SDimitry Andric   switch ((m_hwp_regs[wp_index].control >> 5) & 0xff) {
391af732203SDimitry Andric   case 0x01:
392af732203SDimitry Andric     return 1;
393af732203SDimitry Andric   case 0x03:
394af732203SDimitry Andric     return 2;
395af732203SDimitry Andric   case 0x0f:
396af732203SDimitry Andric     return 4;
397af732203SDimitry Andric   case 0xff:
398af732203SDimitry Andric     return 8;
399af732203SDimitry Andric   default:
400af732203SDimitry Andric     return 0;
401af732203SDimitry Andric   }
402af732203SDimitry Andric }
403af732203SDimitry Andric 
WatchpointIsEnabled(uint32_t wp_index)404af732203SDimitry Andric bool NativeRegisterContextDBReg_arm64::WatchpointIsEnabled(uint32_t wp_index) {
405af732203SDimitry Andric   Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS));
406af732203SDimitry Andric   LLDB_LOG(log, "wp_index: {0}", wp_index);
407af732203SDimitry Andric 
408af732203SDimitry Andric   if ((m_hwp_regs[wp_index].control & g_enable_bit) != 0)
409af732203SDimitry Andric     return true;
410af732203SDimitry Andric   else
411af732203SDimitry Andric     return false;
412af732203SDimitry Andric }
413af732203SDimitry Andric 
GetWatchpointHitIndex(uint32_t & wp_index,lldb::addr_t trap_addr)414af732203SDimitry Andric Status NativeRegisterContextDBReg_arm64::GetWatchpointHitIndex(
415af732203SDimitry Andric     uint32_t &wp_index, lldb::addr_t trap_addr) {
416af732203SDimitry Andric   Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS));
417af732203SDimitry Andric   LLDB_LOG(log, "wp_index: {0}, trap_addr: {1:x}", wp_index, trap_addr);
418af732203SDimitry Andric 
419af732203SDimitry Andric   // Read hardware breakpoint and watchpoint information.
420af732203SDimitry Andric   llvm::Error error = ReadHardwareDebugInfo();
421af732203SDimitry Andric   if (error)
422af732203SDimitry Andric     return Status(std::move(error));
423af732203SDimitry Andric 
424*5f7ddb14SDimitry Andric   // Mask off ignored bits from watchpoint trap address.
425*5f7ddb14SDimitry Andric   trap_addr = FixWatchpointHitAddress(trap_addr);
426*5f7ddb14SDimitry Andric 
427af732203SDimitry Andric   uint32_t watch_size;
428af732203SDimitry Andric   lldb::addr_t watch_addr;
429af732203SDimitry Andric 
430af732203SDimitry Andric   for (wp_index = 0; wp_index < m_max_hwp_supported; ++wp_index) {
431af732203SDimitry Andric     watch_size = GetWatchpointSize(wp_index);
432af732203SDimitry Andric     watch_addr = m_hwp_regs[wp_index].address;
433af732203SDimitry Andric 
434af732203SDimitry Andric     if (WatchpointIsEnabled(wp_index) && trap_addr >= watch_addr &&
435af732203SDimitry Andric         trap_addr < watch_addr + watch_size) {
436af732203SDimitry Andric       m_hwp_regs[wp_index].hit_addr = trap_addr;
437af732203SDimitry Andric       return Status();
438af732203SDimitry Andric     }
439af732203SDimitry Andric   }
440af732203SDimitry Andric 
441af732203SDimitry Andric   wp_index = LLDB_INVALID_INDEX32;
442af732203SDimitry Andric   return Status();
443af732203SDimitry Andric }
444af732203SDimitry Andric 
445af732203SDimitry Andric lldb::addr_t
GetWatchpointAddress(uint32_t wp_index)446af732203SDimitry Andric NativeRegisterContextDBReg_arm64::GetWatchpointAddress(uint32_t wp_index) {
447af732203SDimitry Andric   Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS));
448af732203SDimitry Andric   LLDB_LOG(log, "wp_index: {0}", wp_index);
449af732203SDimitry Andric 
450af732203SDimitry Andric   if (wp_index >= m_max_hwp_supported)
451af732203SDimitry Andric     return LLDB_INVALID_ADDRESS;
452af732203SDimitry Andric 
453af732203SDimitry Andric   if (WatchpointIsEnabled(wp_index))
454af732203SDimitry Andric     return m_hwp_regs[wp_index].real_addr;
455af732203SDimitry Andric   return LLDB_INVALID_ADDRESS;
456af732203SDimitry Andric }
457af732203SDimitry Andric 
458af732203SDimitry Andric lldb::addr_t
GetWatchpointHitAddress(uint32_t wp_index)459af732203SDimitry Andric NativeRegisterContextDBReg_arm64::GetWatchpointHitAddress(uint32_t wp_index) {
460af732203SDimitry Andric   Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS));
461af732203SDimitry Andric   LLDB_LOG(log, "wp_index: {0}", wp_index);
462af732203SDimitry Andric 
463af732203SDimitry Andric   if (wp_index >= m_max_hwp_supported)
464af732203SDimitry Andric     return LLDB_INVALID_ADDRESS;
465af732203SDimitry Andric 
466af732203SDimitry Andric   if (WatchpointIsEnabled(wp_index))
467af732203SDimitry Andric     return m_hwp_regs[wp_index].hit_addr;
468af732203SDimitry Andric   return LLDB_INVALID_ADDRESS;
469af732203SDimitry Andric }
470