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