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(mask: 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(mask: 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(bp_index: 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(hwbType: 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(mask: 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(hwbType: 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(mask: 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(mask: 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(bp_index: 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(hwbType: 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(mask: 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(mask: 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(wp_index: 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(hwbType: 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(mask: 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(hwbType: 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(wp_index: 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(hwbType: 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(mask: 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(mask: 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(mask: 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(hit_addr: 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(mask: 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(mask: 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 | |