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