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