1771c4c9cSMichał Górny //===-- NativeRegisterContextDBReg_arm64.cpp ------------------------------===//
2771c4c9cSMichał Górny //
3771c4c9cSMichał Górny // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4771c4c9cSMichał Górny // See https://llvm.org/LICENSE.txt for license information.
5771c4c9cSMichał Górny // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6771c4c9cSMichał Górny //
7771c4c9cSMichał Górny //===----------------------------------------------------------------------===//
8771c4c9cSMichał Górny 
9771c4c9cSMichał Górny #include "NativeRegisterContextDBReg_arm64.h"
10771c4c9cSMichał Górny 
11*c34698a8SPavel Labath #include "lldb/Utility/LLDBLog.h"
12771c4c9cSMichał Górny #include "lldb/Utility/Log.h"
13771c4c9cSMichał Górny #include "lldb/Utility/RegisterValue.h"
14771c4c9cSMichał Górny 
15771c4c9cSMichał Górny using namespace lldb_private;
16771c4c9cSMichał Górny 
17771c4c9cSMichał Górny // E (bit 0), used to enable breakpoint/watchpoint
18771c4c9cSMichał Górny constexpr uint32_t g_enable_bit = 1;
19771c4c9cSMichał Górny // PAC (bits 2:1): 0b10
20771c4c9cSMichał Górny constexpr uint32_t g_pac_bits = (2 << 1);
21771c4c9cSMichał Górny 
22771c4c9cSMichał Górny // Returns appropriate control register bits for the specified size
GetSizeBits(int size)23771c4c9cSMichał Górny static constexpr inline uint64_t GetSizeBits(int size) {
24771c4c9cSMichał Górny   // BAS (bits 12:5) hold a bit-mask of addresses to watch
25771c4c9cSMichał Górny   // e.g. 0b00000001 means 1 byte at address
26771c4c9cSMichał Górny   //      0b00000011 means 2 bytes (addr..addr+1)
27771c4c9cSMichał Górny   //      ...
28771c4c9cSMichał Górny   //      0b11111111 means 8 bytes (addr..addr+7)
29771c4c9cSMichał Górny   return ((1 << size) - 1) << 5;
30771c4c9cSMichał Górny }
31771c4c9cSMichał Górny 
NumSupportedHardwareBreakpoints()32771c4c9cSMichał Górny uint32_t NativeRegisterContextDBReg_arm64::NumSupportedHardwareBreakpoints() {
33a007a6d8SPavel Labath   Log *log = GetLog(LLDBLog::Breakpoints);
34771c4c9cSMichał Górny   llvm::Error error = ReadHardwareDebugInfo();
35771c4c9cSMichał Górny   if (error) {
36771c4c9cSMichał Górny     LLDB_LOG_ERROR(log, std::move(error),
37771c4c9cSMichał Górny                    "failed to read debug registers: {0}");
38771c4c9cSMichał Górny     return 0;
39771c4c9cSMichał Górny   }
40771c4c9cSMichał Górny 
41771c4c9cSMichał Górny   return m_max_hbp_supported;
42771c4c9cSMichał Górny }
43771c4c9cSMichał Górny 
44771c4c9cSMichał Górny uint32_t
SetHardwareBreakpoint(lldb::addr_t addr,size_t size)45771c4c9cSMichał Górny NativeRegisterContextDBReg_arm64::SetHardwareBreakpoint(lldb::addr_t addr,
46771c4c9cSMichał Górny                                                         size_t size) {
47a007a6d8SPavel Labath   Log *log = GetLog(LLDBLog::Breakpoints);
48771c4c9cSMichał Górny   LLDB_LOG(log, "addr: {0:x}, size: {1:x}", addr, size);
49771c4c9cSMichał Górny 
50771c4c9cSMichał Górny   // Read hardware breakpoint and watchpoint information.
51771c4c9cSMichał Górny   llvm::Error error = ReadHardwareDebugInfo();
52771c4c9cSMichał Górny   if (error) {
53771c4c9cSMichał Górny     LLDB_LOG_ERROR(
54771c4c9cSMichał Górny         log, std::move(error),
55771c4c9cSMichał Górny         "unable to set breakpoint: failed to read debug registers: {0}");
56771c4c9cSMichał Górny     return LLDB_INVALID_INDEX32;
57771c4c9cSMichał Górny   }
58771c4c9cSMichał Górny 
59771c4c9cSMichał Górny   uint32_t control_value = 0, bp_index = 0;
60771c4c9cSMichał Górny 
61771c4c9cSMichał Górny   // Check if size has a valid hardware breakpoint length.
62771c4c9cSMichał Górny   if (size != 4)
63771c4c9cSMichał Górny     return LLDB_INVALID_INDEX32; // Invalid size for a AArch64 hardware
64771c4c9cSMichał Górny                                  // breakpoint
65771c4c9cSMichał Górny 
66771c4c9cSMichał Górny   // Check 4-byte alignment for hardware breakpoint target address.
67771c4c9cSMichał Górny   if (addr & 0x03)
68771c4c9cSMichał Górny     return LLDB_INVALID_INDEX32; // Invalid address, should be 4-byte aligned.
69771c4c9cSMichał Górny 
70771c4c9cSMichał Górny   // Setup control value
71771c4c9cSMichał Górny   control_value = g_enable_bit | g_pac_bits | GetSizeBits(size);
72771c4c9cSMichał Górny 
73771c4c9cSMichał Górny   // Iterate over stored breakpoints and find a free bp_index
74771c4c9cSMichał Górny   bp_index = LLDB_INVALID_INDEX32;
75771c4c9cSMichał Górny   for (uint32_t i = 0; i < m_max_hbp_supported; i++) {
76771c4c9cSMichał Górny     if (!BreakpointIsEnabled(i))
77771c4c9cSMichał Górny       bp_index = i; // Mark last free slot
78771c4c9cSMichał Górny     else if (m_hbp_regs[i].address == addr)
79771c4c9cSMichał Górny       return LLDB_INVALID_INDEX32; // We do not support duplicate breakpoints.
80771c4c9cSMichał Górny   }
81771c4c9cSMichał Górny 
82771c4c9cSMichał Górny   if (bp_index == LLDB_INVALID_INDEX32)
83771c4c9cSMichał Górny     return LLDB_INVALID_INDEX32;
84771c4c9cSMichał Górny 
85771c4c9cSMichał Górny   // Update breakpoint in local cache
86771c4c9cSMichał Górny   m_hbp_regs[bp_index].real_addr = addr;
87771c4c9cSMichał Górny   m_hbp_regs[bp_index].address = addr;
88771c4c9cSMichał Górny   m_hbp_regs[bp_index].control = control_value;
89771c4c9cSMichał Górny 
90771c4c9cSMichał Górny   // PTRACE call to set corresponding hardware breakpoint register.
91771c4c9cSMichał Górny   error = WriteHardwareDebugRegs(eDREGTypeBREAK);
92771c4c9cSMichał Górny 
93771c4c9cSMichał Górny   if (error) {
94771c4c9cSMichał Górny     m_hbp_regs[bp_index].address = 0;
95771c4c9cSMichał Górny     m_hbp_regs[bp_index].control &= ~1;
96771c4c9cSMichał Górny 
97771c4c9cSMichał Górny     LLDB_LOG_ERROR(
98771c4c9cSMichał Górny         log, std::move(error),
99771c4c9cSMichał Górny         "unable to set breakpoint: failed to write debug registers: {0}");
100771c4c9cSMichał Górny     return LLDB_INVALID_INDEX32;
101771c4c9cSMichał Górny   }
102771c4c9cSMichał Górny 
103771c4c9cSMichał Górny   return bp_index;
104771c4c9cSMichał Górny }
105771c4c9cSMichał Górny 
ClearHardwareBreakpoint(uint32_t hw_idx)106771c4c9cSMichał Górny bool NativeRegisterContextDBReg_arm64::ClearHardwareBreakpoint(
107771c4c9cSMichał Górny     uint32_t hw_idx) {
108a007a6d8SPavel Labath   Log *log = GetLog(LLDBLog::Breakpoints);
109771c4c9cSMichał Górny   LLDB_LOG(log, "hw_idx: {0}", hw_idx);
110771c4c9cSMichał Górny 
111771c4c9cSMichał Górny   // Read hardware breakpoint and watchpoint information.
112771c4c9cSMichał Górny   llvm::Error error = ReadHardwareDebugInfo();
113771c4c9cSMichał Górny   if (error) {
114771c4c9cSMichał Górny     LLDB_LOG_ERROR(
115771c4c9cSMichał Górny         log, std::move(error),
116771c4c9cSMichał Górny         "unable to clear breakpoint: failed to read debug registers: {0}");
117771c4c9cSMichał Górny     return false;
118771c4c9cSMichał Górny   }
119771c4c9cSMichał Górny 
120771c4c9cSMichał Górny   if (hw_idx >= m_max_hbp_supported)
121771c4c9cSMichał Górny     return false;
122771c4c9cSMichał Górny 
123771c4c9cSMichał Górny   // Create a backup we can revert to in case of failure.
124771c4c9cSMichał Górny   lldb::addr_t tempAddr = m_hbp_regs[hw_idx].address;
125771c4c9cSMichał Górny   uint32_t tempControl = m_hbp_regs[hw_idx].control;
126771c4c9cSMichał Górny 
127771c4c9cSMichał Górny   m_hbp_regs[hw_idx].control &= ~g_enable_bit;
128771c4c9cSMichał Górny   m_hbp_regs[hw_idx].address = 0;
129771c4c9cSMichał Górny 
130771c4c9cSMichał Górny   // PTRACE call to clear corresponding hardware breakpoint register.
131771c4c9cSMichał Górny   error = WriteHardwareDebugRegs(eDREGTypeBREAK);
132771c4c9cSMichał Górny 
133771c4c9cSMichał Górny   if (error) {
134771c4c9cSMichał Górny     m_hbp_regs[hw_idx].control = tempControl;
135771c4c9cSMichał Górny     m_hbp_regs[hw_idx].address = tempAddr;
136771c4c9cSMichał Górny 
137771c4c9cSMichał Górny     LLDB_LOG_ERROR(
138771c4c9cSMichał Górny         log, std::move(error),
139771c4c9cSMichał Górny         "unable to clear breakpoint: failed to write debug registers: {0}");
140771c4c9cSMichał Górny     return false;
141771c4c9cSMichał Górny   }
142771c4c9cSMichał Górny 
143771c4c9cSMichał Górny   return true;
144771c4c9cSMichał Górny }
145771c4c9cSMichał Górny 
GetHardwareBreakHitIndex(uint32_t & bp_index,lldb::addr_t trap_addr)146771c4c9cSMichał Górny Status NativeRegisterContextDBReg_arm64::GetHardwareBreakHitIndex(
147771c4c9cSMichał Górny     uint32_t &bp_index, lldb::addr_t trap_addr) {
148a007a6d8SPavel Labath   Log *log = GetLog(LLDBLog::Breakpoints);
149771c4c9cSMichał Górny 
150771c4c9cSMichał Górny   LLDB_LOGF(log, "NativeRegisterContextDBReg_arm64::%s()", __FUNCTION__);
151771c4c9cSMichał Górny 
152771c4c9cSMichał Górny   lldb::addr_t break_addr;
153771c4c9cSMichał Górny 
154771c4c9cSMichał Górny   for (bp_index = 0; bp_index < m_max_hbp_supported; ++bp_index) {
155771c4c9cSMichał Górny     break_addr = m_hbp_regs[bp_index].address;
156771c4c9cSMichał Górny 
157771c4c9cSMichał Górny     if (BreakpointIsEnabled(bp_index) && trap_addr == break_addr) {
158771c4c9cSMichał Górny       m_hbp_regs[bp_index].hit_addr = trap_addr;
159771c4c9cSMichał Górny       return Status();
160771c4c9cSMichał Górny     }
161771c4c9cSMichał Górny   }
162771c4c9cSMichał Górny 
163771c4c9cSMichał Górny   bp_index = LLDB_INVALID_INDEX32;
164771c4c9cSMichał Górny   return Status();
165771c4c9cSMichał Górny }
166771c4c9cSMichał Górny 
ClearAllHardwareBreakpoints()167771c4c9cSMichał Górny Status NativeRegisterContextDBReg_arm64::ClearAllHardwareBreakpoints() {
168a007a6d8SPavel Labath   Log *log = GetLog(LLDBLog::Breakpoints);
169771c4c9cSMichał Górny 
170771c4c9cSMichał Górny   LLDB_LOGF(log, "NativeRegisterContextDBReg_arm64::%s()", __FUNCTION__);
171771c4c9cSMichał Górny 
172771c4c9cSMichał Górny   // Read hardware breakpoint and watchpoint information.
173771c4c9cSMichał Górny   llvm::Error error = ReadHardwareDebugInfo();
174771c4c9cSMichał Górny   if (error)
175771c4c9cSMichał Górny     return Status(std::move(error));
176771c4c9cSMichał Górny 
177771c4c9cSMichał Górny   for (uint32_t i = 0; i < m_max_hbp_supported; i++) {
178771c4c9cSMichał Górny     if (BreakpointIsEnabled(i)) {
179771c4c9cSMichał Górny       // Create a backup we can revert to in case of failure.
180771c4c9cSMichał Górny       lldb::addr_t tempAddr = m_hbp_regs[i].address;
181771c4c9cSMichał Górny       uint32_t tempControl = m_hbp_regs[i].control;
182771c4c9cSMichał Górny 
183771c4c9cSMichał Górny       // Clear watchpoints in local cache
184771c4c9cSMichał Górny       m_hbp_regs[i].control &= ~g_enable_bit;
185771c4c9cSMichał Górny       m_hbp_regs[i].address = 0;
186771c4c9cSMichał Górny 
187771c4c9cSMichał Górny       // Ptrace call to update hardware debug registers
188771c4c9cSMichał Górny       error = WriteHardwareDebugRegs(eDREGTypeBREAK);
189771c4c9cSMichał Górny 
190771c4c9cSMichał Górny       if (error) {
191771c4c9cSMichał Górny         m_hbp_regs[i].control = tempControl;
192771c4c9cSMichał Górny         m_hbp_regs[i].address = tempAddr;
193771c4c9cSMichał Górny 
194771c4c9cSMichał Górny         return Status(std::move(error));
195771c4c9cSMichał Górny       }
196771c4c9cSMichał Górny     }
197771c4c9cSMichał Górny   }
198771c4c9cSMichał Górny 
199771c4c9cSMichał Górny   return Status();
200771c4c9cSMichał Górny }
201771c4c9cSMichał Górny 
BreakpointIsEnabled(uint32_t bp_index)202771c4c9cSMichał Górny bool NativeRegisterContextDBReg_arm64::BreakpointIsEnabled(uint32_t bp_index) {
203771c4c9cSMichał Górny   if ((m_hbp_regs[bp_index].control & g_enable_bit) != 0)
204771c4c9cSMichał Górny     return true;
205771c4c9cSMichał Górny   else
206771c4c9cSMichał Górny     return false;
207771c4c9cSMichał Górny }
208771c4c9cSMichał Górny 
NumSupportedHardwareWatchpoints()209771c4c9cSMichał Górny uint32_t NativeRegisterContextDBReg_arm64::NumSupportedHardwareWatchpoints() {
210a007a6d8SPavel Labath   Log *log = GetLog(LLDBLog::Watchpoints);
211771c4c9cSMichał Górny   llvm::Error error = ReadHardwareDebugInfo();
212771c4c9cSMichał Górny   if (error) {
213771c4c9cSMichał Górny     LLDB_LOG_ERROR(log, std::move(error),
214771c4c9cSMichał Górny                    "failed to read debug registers: {0}");
215771c4c9cSMichał Górny     return 0;
216771c4c9cSMichał Górny   }
217771c4c9cSMichał Górny 
218771c4c9cSMichał Górny   return m_max_hwp_supported;
219771c4c9cSMichał Górny }
220771c4c9cSMichał Górny 
SetHardwareWatchpoint(lldb::addr_t addr,size_t size,uint32_t watch_flags)221771c4c9cSMichał Górny uint32_t NativeRegisterContextDBReg_arm64::SetHardwareWatchpoint(
222771c4c9cSMichał Górny     lldb::addr_t addr, size_t size, uint32_t watch_flags) {
223a007a6d8SPavel Labath   Log *log = GetLog(LLDBLog::Watchpoints);
224771c4c9cSMichał Górny   LLDB_LOG(log, "addr: {0:x}, size: {1:x} watch_flags: {2:x}", addr, size,
225771c4c9cSMichał Górny            watch_flags);
226771c4c9cSMichał Górny 
227771c4c9cSMichał Górny   // Read hardware breakpoint and watchpoint information.
228771c4c9cSMichał Górny   llvm::Error error = ReadHardwareDebugInfo();
229771c4c9cSMichał Górny   if (error) {
230771c4c9cSMichał Górny     LLDB_LOG_ERROR(
231771c4c9cSMichał Górny         log, std::move(error),
232771c4c9cSMichał Górny         "unable to set watchpoint: failed to read debug registers: {0}");
233771c4c9cSMichał Górny     return LLDB_INVALID_INDEX32;
234771c4c9cSMichał Górny   }
235771c4c9cSMichał Górny 
236771c4c9cSMichał Górny   uint32_t control_value = 0, wp_index = 0;
237771c4c9cSMichał Górny   lldb::addr_t real_addr = addr;
238771c4c9cSMichał Górny 
239771c4c9cSMichał Górny   // Check if we are setting watchpoint other than read/write/access Also
240771c4c9cSMichał Górny   // update watchpoint flag to match AArch64 write-read bit configuration.
241771c4c9cSMichał Górny   switch (watch_flags) {
242771c4c9cSMichał Górny   case 1:
243771c4c9cSMichał Górny     watch_flags = 2;
244771c4c9cSMichał Górny     break;
245771c4c9cSMichał Górny   case 2:
246771c4c9cSMichał Górny     watch_flags = 1;
247771c4c9cSMichał Górny     break;
248771c4c9cSMichał Górny   case 3:
249771c4c9cSMichał Górny     break;
250771c4c9cSMichał Górny   default:
251771c4c9cSMichał Górny     return LLDB_INVALID_INDEX32;
252771c4c9cSMichał Górny   }
253771c4c9cSMichał Górny 
254771c4c9cSMichał Górny   // Check if size has a valid hardware watchpoint length.
255771c4c9cSMichał Górny   if (size != 1 && size != 2 && size != 4 && size != 8)
256771c4c9cSMichał Górny     return LLDB_INVALID_INDEX32;
257771c4c9cSMichał Górny 
258771c4c9cSMichał Górny   // Check 8-byte alignment for hardware watchpoint target address. Below is a
259771c4c9cSMichał Górny   // hack to recalculate address and size in order to make sure we can watch
260771c4c9cSMichał Górny   // non 8-byte aligned addresses as well.
261771c4c9cSMichał Górny   if (addr & 0x07) {
262771c4c9cSMichał Górny     uint8_t watch_mask = (addr & 0x07) + size;
263771c4c9cSMichał Górny 
264771c4c9cSMichał Górny     if (watch_mask > 0x08)
265771c4c9cSMichał Górny       return LLDB_INVALID_INDEX32;
266771c4c9cSMichał Górny     else if (watch_mask <= 0x02)
267771c4c9cSMichał Górny       size = 2;
268771c4c9cSMichał Górny     else if (watch_mask <= 0x04)
269771c4c9cSMichał Górny       size = 4;
270771c4c9cSMichał Górny     else
271771c4c9cSMichał Górny       size = 8;
272771c4c9cSMichał Górny 
273771c4c9cSMichał Górny     addr = addr & (~0x07);
274771c4c9cSMichał Górny   }
275771c4c9cSMichał Górny 
276771c4c9cSMichał Górny   // Setup control value
277771c4c9cSMichał Górny   control_value = g_enable_bit | g_pac_bits | GetSizeBits(size);
278771c4c9cSMichał Górny   control_value |= watch_flags << 3;
279771c4c9cSMichał Górny 
280771c4c9cSMichał Górny   // Iterate over stored watchpoints and find a free wp_index
281771c4c9cSMichał Górny   wp_index = LLDB_INVALID_INDEX32;
282771c4c9cSMichał Górny   for (uint32_t i = 0; i < m_max_hwp_supported; i++) {
283771c4c9cSMichał Górny     if (!WatchpointIsEnabled(i))
284771c4c9cSMichał Górny       wp_index = i; // Mark last free slot
285771c4c9cSMichał Górny     else if (m_hwp_regs[i].address == addr) {
286771c4c9cSMichał Górny       return LLDB_INVALID_INDEX32; // We do not support duplicate watchpoints.
287771c4c9cSMichał Górny     }
288771c4c9cSMichał Górny   }
289771c4c9cSMichał Górny 
290771c4c9cSMichał Górny   if (wp_index == LLDB_INVALID_INDEX32)
291771c4c9cSMichał Górny     return LLDB_INVALID_INDEX32;
292771c4c9cSMichał Górny 
293771c4c9cSMichał Górny   // Update watchpoint in local cache
294771c4c9cSMichał Górny   m_hwp_regs[wp_index].real_addr = real_addr;
295771c4c9cSMichał Górny   m_hwp_regs[wp_index].address = addr;
296771c4c9cSMichał Górny   m_hwp_regs[wp_index].control = control_value;
297771c4c9cSMichał Górny 
298771c4c9cSMichał Górny   // PTRACE call to set corresponding watchpoint register.
299771c4c9cSMichał Górny   error = WriteHardwareDebugRegs(eDREGTypeWATCH);
300771c4c9cSMichał Górny 
301771c4c9cSMichał Górny   if (error) {
302771c4c9cSMichał Górny     m_hwp_regs[wp_index].address = 0;
303771c4c9cSMichał Górny     m_hwp_regs[wp_index].control &= ~g_enable_bit;
304771c4c9cSMichał Górny 
305771c4c9cSMichał Górny     LLDB_LOG_ERROR(
306771c4c9cSMichał Górny         log, std::move(error),
307771c4c9cSMichał Górny         "unable to set watchpoint: failed to write debug registers: {0}");
308771c4c9cSMichał Górny     return LLDB_INVALID_INDEX32;
309771c4c9cSMichał Górny   }
310771c4c9cSMichał Górny 
311771c4c9cSMichał Górny   return wp_index;
312771c4c9cSMichał Górny }
313771c4c9cSMichał Górny 
ClearHardwareWatchpoint(uint32_t wp_index)314771c4c9cSMichał Górny bool NativeRegisterContextDBReg_arm64::ClearHardwareWatchpoint(
315771c4c9cSMichał Górny     uint32_t wp_index) {
316a007a6d8SPavel Labath   Log *log = GetLog(LLDBLog::Watchpoints);
317771c4c9cSMichał Górny   LLDB_LOG(log, "wp_index: {0}", wp_index);
318771c4c9cSMichał Górny 
319771c4c9cSMichał Górny   // Read hardware breakpoint and watchpoint information.
320771c4c9cSMichał Górny   llvm::Error error = ReadHardwareDebugInfo();
321771c4c9cSMichał Górny   if (error) {
322771c4c9cSMichał Górny     LLDB_LOG_ERROR(
323771c4c9cSMichał Górny         log, std::move(error),
324771c4c9cSMichał Górny         "unable to clear watchpoint: failed to read debug registers: {0}");
325771c4c9cSMichał Górny     return false;
326771c4c9cSMichał Górny   }
327771c4c9cSMichał Górny 
328771c4c9cSMichał Górny   if (wp_index >= m_max_hwp_supported)
329771c4c9cSMichał Górny     return false;
330771c4c9cSMichał Górny 
331771c4c9cSMichał Górny   // Create a backup we can revert to in case of failure.
332771c4c9cSMichał Górny   lldb::addr_t tempAddr = m_hwp_regs[wp_index].address;
333771c4c9cSMichał Górny   uint32_t tempControl = m_hwp_regs[wp_index].control;
334771c4c9cSMichał Górny 
335771c4c9cSMichał Górny   // Update watchpoint in local cache
336771c4c9cSMichał Górny   m_hwp_regs[wp_index].control &= ~g_enable_bit;
337771c4c9cSMichał Górny   m_hwp_regs[wp_index].address = 0;
338771c4c9cSMichał Górny 
339771c4c9cSMichał Górny   // Ptrace call to update hardware debug registers
340771c4c9cSMichał Górny   error = WriteHardwareDebugRegs(eDREGTypeWATCH);
341771c4c9cSMichał Górny 
342771c4c9cSMichał Górny   if (error) {
343771c4c9cSMichał Górny     m_hwp_regs[wp_index].control = tempControl;
344771c4c9cSMichał Górny     m_hwp_regs[wp_index].address = tempAddr;
345771c4c9cSMichał Górny 
346771c4c9cSMichał Górny     LLDB_LOG_ERROR(
347771c4c9cSMichał Górny         log, std::move(error),
348771c4c9cSMichał Górny         "unable to clear watchpoint: failed to write debug registers: {0}");
349771c4c9cSMichał Górny     return false;
350771c4c9cSMichał Górny   }
351771c4c9cSMichał Górny 
352771c4c9cSMichał Górny   return true;
353771c4c9cSMichał Górny }
354771c4c9cSMichał Górny 
ClearAllHardwareWatchpoints()355771c4c9cSMichał Górny Status NativeRegisterContextDBReg_arm64::ClearAllHardwareWatchpoints() {
356771c4c9cSMichał Górny   // Read hardware breakpoint and watchpoint information.
357771c4c9cSMichał Górny   llvm::Error error = ReadHardwareDebugInfo();
358771c4c9cSMichał Górny   if (error)
359771c4c9cSMichał Górny     return Status(std::move(error));
360771c4c9cSMichał Górny 
361771c4c9cSMichał Górny   for (uint32_t i = 0; i < m_max_hwp_supported; i++) {
362771c4c9cSMichał Górny     if (WatchpointIsEnabled(i)) {
363771c4c9cSMichał Górny       // Create a backup we can revert to in case of failure.
364771c4c9cSMichał Górny       lldb::addr_t tempAddr = m_hwp_regs[i].address;
365771c4c9cSMichał Górny       uint32_t tempControl = m_hwp_regs[i].control;
366771c4c9cSMichał Górny 
367771c4c9cSMichał Górny       // Clear watchpoints in local cache
368771c4c9cSMichał Górny       m_hwp_regs[i].control &= ~g_enable_bit;
369771c4c9cSMichał Górny       m_hwp_regs[i].address = 0;
370771c4c9cSMichał Górny 
371771c4c9cSMichał Górny       // Ptrace call to update hardware debug registers
372771c4c9cSMichał Górny       error = WriteHardwareDebugRegs(eDREGTypeWATCH);
373771c4c9cSMichał Górny 
374771c4c9cSMichał Górny       if (error) {
375771c4c9cSMichał Górny         m_hwp_regs[i].control = tempControl;
376771c4c9cSMichał Górny         m_hwp_regs[i].address = tempAddr;
377771c4c9cSMichał Górny 
378771c4c9cSMichał Górny         return Status(std::move(error));
379771c4c9cSMichał Górny       }
380771c4c9cSMichał Górny     }
381771c4c9cSMichał Górny   }
382771c4c9cSMichał Górny 
383771c4c9cSMichał Górny   return Status();
384771c4c9cSMichał Górny }
385771c4c9cSMichał Górny 
386771c4c9cSMichał Górny uint32_t
GetWatchpointSize(uint32_t wp_index)387771c4c9cSMichał Górny NativeRegisterContextDBReg_arm64::GetWatchpointSize(uint32_t wp_index) {
388a007a6d8SPavel Labath   Log *log = GetLog(LLDBLog::Watchpoints);
389771c4c9cSMichał Górny   LLDB_LOG(log, "wp_index: {0}", wp_index);
390771c4c9cSMichał Górny 
391771c4c9cSMichał Górny   switch ((m_hwp_regs[wp_index].control >> 5) & 0xff) {
392771c4c9cSMichał Górny   case 0x01:
393771c4c9cSMichał Górny     return 1;
394771c4c9cSMichał Górny   case 0x03:
395771c4c9cSMichał Górny     return 2;
396771c4c9cSMichał Górny   case 0x0f:
397771c4c9cSMichał Górny     return 4;
398771c4c9cSMichał Górny   case 0xff:
399771c4c9cSMichał Górny     return 8;
400771c4c9cSMichał Górny   default:
401771c4c9cSMichał Górny     return 0;
402771c4c9cSMichał Górny   }
403771c4c9cSMichał Górny }
404771c4c9cSMichał Górny 
WatchpointIsEnabled(uint32_t wp_index)405771c4c9cSMichał Górny bool NativeRegisterContextDBReg_arm64::WatchpointIsEnabled(uint32_t wp_index) {
406a007a6d8SPavel Labath   Log *log = GetLog(LLDBLog::Watchpoints);
407771c4c9cSMichał Górny   LLDB_LOG(log, "wp_index: {0}", wp_index);
408771c4c9cSMichał Górny 
409771c4c9cSMichał Górny   if ((m_hwp_regs[wp_index].control & g_enable_bit) != 0)
410771c4c9cSMichał Górny     return true;
411771c4c9cSMichał Górny   else
412771c4c9cSMichał Górny     return false;
413771c4c9cSMichał Górny }
414771c4c9cSMichał Górny 
GetWatchpointHitIndex(uint32_t & wp_index,lldb::addr_t trap_addr)415771c4c9cSMichał Górny Status NativeRegisterContextDBReg_arm64::GetWatchpointHitIndex(
416771c4c9cSMichał Górny     uint32_t &wp_index, lldb::addr_t trap_addr) {
417a007a6d8SPavel Labath   Log *log = GetLog(LLDBLog::Watchpoints);
418771c4c9cSMichał Górny   LLDB_LOG(log, "wp_index: {0}, trap_addr: {1:x}", wp_index, trap_addr);
419771c4c9cSMichał Górny 
420771c4c9cSMichał Górny   // Read hardware breakpoint and watchpoint information.
421771c4c9cSMichał Górny   llvm::Error error = ReadHardwareDebugInfo();
422771c4c9cSMichał Górny   if (error)
423771c4c9cSMichał Górny     return Status(std::move(error));
424771c4c9cSMichał Górny 
4255e6aabd4SMuhammad Omair Javaid   // Mask off ignored bits from watchpoint trap address.
4265e6aabd4SMuhammad Omair Javaid   trap_addr = FixWatchpointHitAddress(trap_addr);
4275e6aabd4SMuhammad Omair Javaid 
428771c4c9cSMichał Górny   uint32_t watch_size;
429771c4c9cSMichał Górny   lldb::addr_t watch_addr;
430771c4c9cSMichał Górny 
431771c4c9cSMichał Górny   for (wp_index = 0; wp_index < m_max_hwp_supported; ++wp_index) {
432771c4c9cSMichał Górny     watch_size = GetWatchpointSize(wp_index);
433771c4c9cSMichał Górny     watch_addr = m_hwp_regs[wp_index].address;
434771c4c9cSMichał Górny 
435771c4c9cSMichał Górny     if (WatchpointIsEnabled(wp_index) && trap_addr >= watch_addr &&
436771c4c9cSMichał Górny         trap_addr < watch_addr + watch_size) {
437771c4c9cSMichał Górny       m_hwp_regs[wp_index].hit_addr = trap_addr;
438771c4c9cSMichał Górny       return Status();
439771c4c9cSMichał Górny     }
440771c4c9cSMichał Górny   }
441771c4c9cSMichał Górny 
442771c4c9cSMichał Górny   wp_index = LLDB_INVALID_INDEX32;
443771c4c9cSMichał Górny   return Status();
444771c4c9cSMichał Górny }
445771c4c9cSMichał Górny 
446771c4c9cSMichał Górny lldb::addr_t
GetWatchpointAddress(uint32_t wp_index)447771c4c9cSMichał Górny NativeRegisterContextDBReg_arm64::GetWatchpointAddress(uint32_t wp_index) {
448a007a6d8SPavel Labath   Log *log = GetLog(LLDBLog::Watchpoints);
449771c4c9cSMichał Górny   LLDB_LOG(log, "wp_index: {0}", wp_index);
450771c4c9cSMichał Górny 
451771c4c9cSMichał Górny   if (wp_index >= m_max_hwp_supported)
452771c4c9cSMichał Górny     return LLDB_INVALID_ADDRESS;
453771c4c9cSMichał Górny 
454771c4c9cSMichał Górny   if (WatchpointIsEnabled(wp_index))
455771c4c9cSMichał Górny     return m_hwp_regs[wp_index].real_addr;
456771c4c9cSMichał Górny   return LLDB_INVALID_ADDRESS;
457771c4c9cSMichał Górny }
458771c4c9cSMichał Górny 
459771c4c9cSMichał Górny lldb::addr_t
GetWatchpointHitAddress(uint32_t wp_index)460771c4c9cSMichał Górny NativeRegisterContextDBReg_arm64::GetWatchpointHitAddress(uint32_t wp_index) {
461a007a6d8SPavel Labath   Log *log = GetLog(LLDBLog::Watchpoints);
462771c4c9cSMichał Górny   LLDB_LOG(log, "wp_index: {0}", wp_index);
463771c4c9cSMichał Górny 
464771c4c9cSMichał Górny   if (wp_index >= m_max_hwp_supported)
465771c4c9cSMichał Górny     return LLDB_INVALID_ADDRESS;
466771c4c9cSMichał Górny 
467771c4c9cSMichał Górny   if (WatchpointIsEnabled(wp_index))
468771c4c9cSMichał Górny     return m_hwp_regs[wp_index].hit_addr;
469771c4c9cSMichał Górny   return LLDB_INVALID_ADDRESS;
470771c4c9cSMichał Górny }
471