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