xref: /freebsd/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
1 //===-- NativeRegisterContextDBReg.cpp ------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "NativeRegisterContextDBReg.h"
10 
11 #include "lldb/Utility/LLDBLog.h"
12 #include "lldb/Utility/Log.h"
13 #include "lldb/Utility/RegisterValue.h"
14 
15 using namespace lldb_private;
16 
NumSupportedHardwareBreakpoints()17 uint32_t NativeRegisterContextDBReg::NumSupportedHardwareBreakpoints() {
18   Log *log = GetLog(LLDBLog::Breakpoints);
19 
20   // Read hardware breakpoint and watchpoint information.
21   llvm::Error error = ReadHardwareDebugInfo();
22 
23   if (error) {
24     LLDB_LOG_ERROR(log, std::move(error),
25                    "failed to read debug registers: {0}");
26     return 0;
27   }
28 
29   LLDB_LOG(log, "{0}", m_max_hbp_supported);
30   return m_max_hbp_supported;
31 }
32 
SetHardwareBreakpoint(lldb::addr_t addr,size_t size)33 uint32_t NativeRegisterContextDBReg::SetHardwareBreakpoint(lldb::addr_t addr,
34                                                            size_t size) {
35   Log *log = GetLog(LLDBLog::Breakpoints);
36   LLDB_LOG(log, "addr: {0:x}, size: {1:x}", addr, size);
37 
38   // Read hardware breakpoint and watchpoint information.
39   llvm::Error error = ReadHardwareDebugInfo();
40   if (error) {
41     LLDB_LOG_ERROR(
42         log, std::move(error),
43         "unable to set breakpoint: failed to read debug registers: {0}");
44     return LLDB_INVALID_INDEX32;
45   }
46 
47   uint32_t control_value = 0, bp_index = 0;
48 
49   if (!ValidateBreakpoint(size, addr))
50     return LLDB_INVALID_INDEX32;
51 
52   control_value = MakeBreakControlValue(size);
53 
54   // Iterate over stored breakpoints and find a free bp_index
55   bp_index = LLDB_INVALID_INDEX32;
56   for (uint32_t i = 0; i < m_max_hbp_supported; i++) {
57     if (!BreakpointIsEnabled(i))
58       bp_index = i; // Mark last free slot
59     else if (m_hbp_regs[i].address == addr)
60       return LLDB_INVALID_INDEX32; // We do not support duplicate breakpoints.
61   }
62 
63   if (bp_index == LLDB_INVALID_INDEX32)
64     return LLDB_INVALID_INDEX32;
65 
66   // Update breakpoint in local cache
67   m_hbp_regs[bp_index].real_addr = addr;
68   m_hbp_regs[bp_index].address = addr;
69   m_hbp_regs[bp_index].control = control_value;
70 
71   // PTRACE call to set corresponding hardware breakpoint register.
72   error = WriteHardwareDebugRegs(eDREGTypeBREAK);
73 
74   if (error) {
75     m_hbp_regs[bp_index].address = 0;
76     m_hbp_regs[bp_index].control &= ~m_hw_dbg_enable_bit;
77 
78     LLDB_LOG_ERROR(
79         log, std::move(error),
80         "unable to set breakpoint: failed to write debug registers: {0}");
81     return LLDB_INVALID_INDEX32;
82   }
83 
84   return bp_index;
85 }
86 
ClearHardwareBreakpoint(uint32_t hw_idx)87 bool NativeRegisterContextDBReg::ClearHardwareBreakpoint(uint32_t hw_idx) {
88   Log *log = GetLog(LLDBLog::Breakpoints);
89   LLDB_LOG(log, "hw_idx: {0}", hw_idx);
90 
91   // Read hardware breakpoint and watchpoint information.
92   llvm::Error error = ReadHardwareDebugInfo();
93   if (error) {
94     LLDB_LOG_ERROR(
95         log, std::move(error),
96         "unable to clear breakpoint: failed to read debug registers: {0}");
97     return false;
98   }
99 
100   if (hw_idx >= m_max_hbp_supported)
101     return false;
102 
103   // Create a backup we can revert to in case of failure.
104   lldb::addr_t tempAddr = m_hbp_regs[hw_idx].address;
105   uint32_t tempControl = m_hbp_regs[hw_idx].control;
106 
107   m_hbp_regs[hw_idx].control &= ~m_hw_dbg_enable_bit;
108   m_hbp_regs[hw_idx].address = 0;
109 
110   // PTRACE call to clear corresponding hardware breakpoint register.
111   error = WriteHardwareDebugRegs(eDREGTypeBREAK);
112 
113   if (error) {
114     m_hbp_regs[hw_idx].control = tempControl;
115     m_hbp_regs[hw_idx].address = tempAddr;
116 
117     LLDB_LOG_ERROR(
118         log, std::move(error),
119         "unable to clear breakpoint: failed to write debug registers: {0}");
120     return false;
121   }
122 
123   return true;
124 }
125 
126 Status
GetHardwareBreakHitIndex(uint32_t & bp_index,lldb::addr_t trap_addr)127 NativeRegisterContextDBReg::GetHardwareBreakHitIndex(uint32_t &bp_index,
128                                                      lldb::addr_t trap_addr) {
129   Log *log = GetLog(LLDBLog::Breakpoints);
130 
131   LLDB_LOGF(log, "NativeRegisterContextDBReg::%s()", __FUNCTION__);
132 
133   lldb::addr_t break_addr;
134 
135   for (bp_index = 0; bp_index < m_max_hbp_supported; ++bp_index) {
136     break_addr = m_hbp_regs[bp_index].address;
137 
138     if (BreakpointIsEnabled(bp_index) && trap_addr == break_addr) {
139       m_hbp_regs[bp_index].hit_addr = trap_addr;
140       return Status();
141     }
142   }
143 
144   bp_index = LLDB_INVALID_INDEX32;
145   return Status();
146 }
147 
ClearAllHardwareBreakpoints()148 Status NativeRegisterContextDBReg::ClearAllHardwareBreakpoints() {
149   Log *log = GetLog(LLDBLog::Breakpoints);
150 
151   LLDB_LOGF(log, "NativeRegisterContextDBReg::%s()", __FUNCTION__);
152 
153   // Read hardware breakpoint and watchpoint information.
154   llvm::Error error = ReadHardwareDebugInfo();
155   if (error)
156     return Status::FromError(std::move(error));
157 
158   for (uint32_t i = 0; i < m_max_hbp_supported; i++) {
159     if (!BreakpointIsEnabled(i))
160       continue;
161     // Create a backup we can revert to in case of failure.
162     lldb::addr_t tempAddr = m_hbp_regs[i].address;
163     uint32_t tempControl = m_hbp_regs[i].control;
164 
165     // Clear watchpoints in local cache
166     m_hbp_regs[i].control &= ~m_hw_dbg_enable_bit;
167     m_hbp_regs[i].address = 0;
168 
169     // Ptrace call to update hardware debug registers
170     error = WriteHardwareDebugRegs(eDREGTypeBREAK);
171 
172     if (error) {
173       m_hbp_regs[i].control = tempControl;
174       m_hbp_regs[i].address = tempAddr;
175 
176       return Status::FromError(std::move(error));
177     }
178   }
179 
180   return Status();
181 }
182 
BreakpointIsEnabled(uint32_t bp_index)183 bool NativeRegisterContextDBReg::BreakpointIsEnabled(uint32_t bp_index) {
184   return ((m_hbp_regs[bp_index].control & m_hw_dbg_enable_bit) != 0);
185 }
186 
NumSupportedHardwareWatchpoints()187 uint32_t NativeRegisterContextDBReg::NumSupportedHardwareWatchpoints() {
188   Log *log = GetLog(LLDBLog::Watchpoints);
189   llvm::Error error = ReadHardwareDebugInfo();
190   if (error) {
191     LLDB_LOG_ERROR(log, std::move(error),
192                    "failed to read debug registers: {0}");
193     return 0;
194   }
195 
196   return m_max_hwp_supported;
197 }
198 
SetHardwareWatchpoint(lldb::addr_t addr,size_t size,uint32_t watch_flags)199 uint32_t NativeRegisterContextDBReg::SetHardwareWatchpoint(
200     lldb::addr_t addr, size_t size, uint32_t watch_flags) {
201   Log *log = GetLog(LLDBLog::Watchpoints);
202   LLDB_LOG(log, "addr: {0:x}, size: {1:x} watch_flags: {2:x}", addr, size,
203            watch_flags);
204 
205   // Read hardware breakpoint and watchpoint information.
206   llvm::Error error = ReadHardwareDebugInfo();
207   if (error) {
208     LLDB_LOG_ERROR(
209         log, std::move(error),
210         "unable to set watchpoint: failed to read debug registers: {0}");
211     return LLDB_INVALID_INDEX32;
212   }
213 
214   uint32_t control_value = 0, wp_index = 0;
215   lldb::addr_t real_addr = addr;
216   WatchpointDetails details{size, addr};
217 
218   auto adjusted = AdjustWatchpoint(details);
219   if (adjusted == std::nullopt)
220     return LLDB_INVALID_INDEX32;
221   size = adjusted->size;
222   addr = adjusted->addr;
223 
224   // Check if we are setting watchpoint other than read/write/access Also
225   // update watchpoint flag to match AArch64/LoongArch write-read bit
226   // configuration.
227   switch (watch_flags) {
228   case lldb::eWatchpointKindWrite:
229     watch_flags = 2;
230     break;
231   case lldb::eWatchpointKindRead:
232     watch_flags = 1;
233     break;
234   case (lldb::eWatchpointKindRead | lldb::eWatchpointKindWrite):
235     break;
236   default:
237     return LLDB_INVALID_INDEX32;
238   }
239 
240   control_value = MakeWatchControlValue(size, watch_flags);
241 
242   // Iterate over stored watchpoints and find a free wp_index
243   wp_index = LLDB_INVALID_INDEX32;
244   for (uint32_t i = 0; i < m_max_hwp_supported; i++) {
245     if (!WatchpointIsEnabled(i))
246       wp_index = i; // Mark last free slot
247     else if (m_hwp_regs[i].address == addr) {
248       return LLDB_INVALID_INDEX32; // We do not support duplicate watchpoints.
249     }
250   }
251 
252   if (wp_index == LLDB_INVALID_INDEX32)
253     return LLDB_INVALID_INDEX32;
254 
255   // Update watchpoint in local cache
256   m_hwp_regs[wp_index].real_addr = real_addr;
257   m_hwp_regs[wp_index].address = addr;
258   m_hwp_regs[wp_index].control = control_value;
259 
260   // PTRACE call to set corresponding watchpoint register.
261   error = WriteHardwareDebugRegs(eDREGTypeWATCH);
262 
263   if (error) {
264     m_hwp_regs[wp_index].address = 0;
265     m_hwp_regs[wp_index].control &= ~m_hw_dbg_enable_bit;
266 
267     LLDB_LOG_ERROR(
268         log, std::move(error),
269         "unable to set watchpoint: failed to write debug registers: {0}");
270     return LLDB_INVALID_INDEX32;
271   }
272 
273   return wp_index;
274 }
275 
ClearHardwareWatchpoint(uint32_t wp_index)276 bool NativeRegisterContextDBReg::ClearHardwareWatchpoint(uint32_t wp_index) {
277   Log *log = GetLog(LLDBLog::Watchpoints);
278   LLDB_LOG(log, "wp_index: {0}", wp_index);
279 
280   // Read hardware breakpoint and watchpoint information.
281   llvm::Error error = ReadHardwareDebugInfo();
282   if (error) {
283     LLDB_LOG_ERROR(
284         log, std::move(error),
285         "unable to set watchpoint: failed to read debug registers: {0}");
286     return false;
287   }
288 
289   if (wp_index >= m_max_hwp_supported)
290     return false;
291 
292   // Create a backup we can revert to in case of failure.
293   lldb::addr_t tempAddr = m_hwp_regs[wp_index].address;
294   uint32_t tempControl = m_hwp_regs[wp_index].control;
295 
296   // Update watchpoint in local cache
297   m_hwp_regs[wp_index].control &= ~m_hw_dbg_enable_bit;
298   m_hwp_regs[wp_index].address = 0;
299 
300   // Ptrace call to update hardware debug registers
301   error = WriteHardwareDebugRegs(eDREGTypeWATCH);
302 
303   if (error) {
304     m_hwp_regs[wp_index].control = tempControl;
305     m_hwp_regs[wp_index].address = tempAddr;
306 
307     LLDB_LOG_ERROR(
308         log, std::move(error),
309         "unable to clear watchpoint: failed to read debug registers: {0}");
310     return false;
311   }
312 
313   return true;
314 }
315 
ClearAllHardwareWatchpoints()316 Status NativeRegisterContextDBReg::ClearAllHardwareWatchpoints() {
317   // Read hardware breakpoint and watchpoint information.
318   llvm::Error error = ReadHardwareDebugInfo();
319   if (error)
320     return Status::FromError(std::move(error));
321 
322   for (uint32_t i = 0; i < m_max_hwp_supported; i++) {
323     if (!WatchpointIsEnabled(i))
324       continue;
325     // Create a backup we can revert to in case of failure.
326     lldb::addr_t tempAddr = m_hwp_regs[i].address;
327     uint32_t tempControl = m_hwp_regs[i].control;
328 
329     // Clear watchpoints in local cache
330     m_hwp_regs[i].control = 0;
331     m_hwp_regs[i].address = 0;
332 
333     // Ptrace call to update hardware debug registers
334     error = WriteHardwareDebugRegs(eDREGTypeWATCH);
335 
336     if (error) {
337       m_hwp_regs[i].control = tempControl;
338       m_hwp_regs[i].address = tempAddr;
339 
340       return Status::FromError(std::move(error));
341     }
342   }
343 
344   return Status();
345 }
346 
347 Status
GetWatchpointHitIndex(uint32_t & wp_index,lldb::addr_t trap_addr)348 NativeRegisterContextDBReg::GetWatchpointHitIndex(uint32_t &wp_index,
349                                                   lldb::addr_t trap_addr) {
350   Log *log = GetLog(LLDBLog::Watchpoints);
351   LLDB_LOG(log, "wp_index: {0}, trap_addr: {1:x}", wp_index, trap_addr);
352 
353   // Read hardware breakpoint and watchpoint information.
354   llvm::Error error = ReadHardwareDebugInfo();
355   if (error)
356     return Status::FromError(std::move(error));
357 
358   // AArch64 need mask off ignored bits from watchpoint trap address.
359   trap_addr = FixWatchpointHitAddress(trap_addr);
360 
361   uint32_t watch_size;
362   lldb::addr_t watch_addr;
363 
364   for (wp_index = 0; wp_index < m_max_hwp_supported; ++wp_index) {
365     watch_size = GetWatchpointSize(wp_index);
366     watch_addr = m_hwp_regs[wp_index].address;
367 
368     if (WatchpointIsEnabled(wp_index) && trap_addr >= watch_addr &&
369         trap_addr < watch_addr + watch_size) {
370       m_hwp_regs[wp_index].hit_addr = trap_addr;
371       return Status();
372     }
373   }
374 
375   wp_index = LLDB_INVALID_INDEX32;
376   return Status();
377 }
378 
WatchpointIsEnabled(uint32_t wp_index)379 bool NativeRegisterContextDBReg::WatchpointIsEnabled(uint32_t wp_index) {
380   Log *log = GetLog(LLDBLog::Watchpoints);
381   LLDB_LOG(log, "wp_index: {0}", wp_index);
382   return ((m_hwp_regs[wp_index].control & m_hw_dbg_enable_bit) != 0);
383 }
384 
385 lldb::addr_t
GetWatchpointAddress(uint32_t wp_index)386 NativeRegisterContextDBReg::GetWatchpointAddress(uint32_t wp_index) {
387   Log *log = GetLog(LLDBLog::Watchpoints);
388   LLDB_LOG(log, "wp_index: {0}", wp_index);
389 
390   if (wp_index >= m_max_hwp_supported)
391     return LLDB_INVALID_ADDRESS;
392 
393   if (WatchpointIsEnabled(wp_index))
394     return m_hwp_regs[wp_index].real_addr;
395   return LLDB_INVALID_ADDRESS;
396 }
397 
398 lldb::addr_t
GetWatchpointHitAddress(uint32_t wp_index)399 NativeRegisterContextDBReg::GetWatchpointHitAddress(uint32_t wp_index) {
400   Log *log = GetLog(LLDBLog::Watchpoints);
401   LLDB_LOG(log, "wp_index: {0}", wp_index);
402 
403   if (wp_index >= m_max_hwp_supported)
404     return LLDB_INVALID_ADDRESS;
405 
406   if (WatchpointIsEnabled(wp_index))
407     return m_hwp_regs[wp_index].hit_addr;
408   return LLDB_INVALID_ADDRESS;
409 }
410