xref: /openbsd-src/gnu/llvm/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp (revision 46035553bfdd96e63c94e32da0210227ec2e3cf1)
1 //===-- NativeRegisterContextLinux_arm64.cpp --------------------*- C++ -*-===//
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 #if defined(__arm64__) || defined(__aarch64__)
10 
11 #include "NativeRegisterContextLinux_arm.h"
12 #include "NativeRegisterContextLinux_arm64.h"
13 
14 
15 #include "lldb/Host/common/NativeProcessProtocol.h"
16 #include "lldb/Utility/DataBufferHeap.h"
17 #include "lldb/Utility/Log.h"
18 #include "lldb/Utility/RegisterValue.h"
19 #include "lldb/Utility/Status.h"
20 
21 #include "Plugins/Process/Linux/NativeProcessLinux.h"
22 #include "Plugins/Process/Linux/Procfs.h"
23 #include "Plugins/Process/POSIX/ProcessPOSIXLog.h"
24 #include "Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h"
25 
26 // System includes - They have to be included after framework includes because
27 // they define some macros which collide with variable names in other modules
28 #include <sys/socket.h>
29 // NT_PRSTATUS and NT_FPREGSET definition
30 #include <elf.h>
31 // user_hwdebug_state definition
32 #include <asm/ptrace.h>
33 
34 #define REG_CONTEXT_SIZE (GetGPRSize() + GetFPRSize())
35 
36 using namespace lldb;
37 using namespace lldb_private;
38 using namespace lldb_private::process_linux;
39 
40 // ARM64 general purpose registers.
41 static const uint32_t g_gpr_regnums_arm64[] = {
42     gpr_x0_arm64,       gpr_x1_arm64,   gpr_x2_arm64,  gpr_x3_arm64,
43     gpr_x4_arm64,       gpr_x5_arm64,   gpr_x6_arm64,  gpr_x7_arm64,
44     gpr_x8_arm64,       gpr_x9_arm64,   gpr_x10_arm64, gpr_x11_arm64,
45     gpr_x12_arm64,      gpr_x13_arm64,  gpr_x14_arm64, gpr_x15_arm64,
46     gpr_x16_arm64,      gpr_x17_arm64,  gpr_x18_arm64, gpr_x19_arm64,
47     gpr_x20_arm64,      gpr_x21_arm64,  gpr_x22_arm64, gpr_x23_arm64,
48     gpr_x24_arm64,      gpr_x25_arm64,  gpr_x26_arm64, gpr_x27_arm64,
49     gpr_x28_arm64,      gpr_fp_arm64,   gpr_lr_arm64,  gpr_sp_arm64,
50     gpr_pc_arm64,       gpr_cpsr_arm64, gpr_w0_arm64,  gpr_w1_arm64,
51     gpr_w2_arm64,       gpr_w3_arm64,   gpr_w4_arm64,  gpr_w5_arm64,
52     gpr_w6_arm64,       gpr_w7_arm64,   gpr_w8_arm64,  gpr_w9_arm64,
53     gpr_w10_arm64,      gpr_w11_arm64,  gpr_w12_arm64, gpr_w13_arm64,
54     gpr_w14_arm64,      gpr_w15_arm64,  gpr_w16_arm64, gpr_w17_arm64,
55     gpr_w18_arm64,      gpr_w19_arm64,  gpr_w20_arm64, gpr_w21_arm64,
56     gpr_w22_arm64,      gpr_w23_arm64,  gpr_w24_arm64, gpr_w25_arm64,
57     gpr_w26_arm64,      gpr_w27_arm64,  gpr_w28_arm64,
58     LLDB_INVALID_REGNUM // register sets need to end with this flag
59 };
60 static_assert(((sizeof g_gpr_regnums_arm64 / sizeof g_gpr_regnums_arm64[0]) -
61                1) == k_num_gpr_registers_arm64,
62               "g_gpr_regnums_arm64 has wrong number of register infos");
63 
64 // ARM64 floating point registers.
65 static const uint32_t g_fpu_regnums_arm64[] = {
66     fpu_v0_arm64,       fpu_v1_arm64,   fpu_v2_arm64,  fpu_v3_arm64,
67     fpu_v4_arm64,       fpu_v5_arm64,   fpu_v6_arm64,  fpu_v7_arm64,
68     fpu_v8_arm64,       fpu_v9_arm64,   fpu_v10_arm64, fpu_v11_arm64,
69     fpu_v12_arm64,      fpu_v13_arm64,  fpu_v14_arm64, fpu_v15_arm64,
70     fpu_v16_arm64,      fpu_v17_arm64,  fpu_v18_arm64, fpu_v19_arm64,
71     fpu_v20_arm64,      fpu_v21_arm64,  fpu_v22_arm64, fpu_v23_arm64,
72     fpu_v24_arm64,      fpu_v25_arm64,  fpu_v26_arm64, fpu_v27_arm64,
73     fpu_v28_arm64,      fpu_v29_arm64,  fpu_v30_arm64, fpu_v31_arm64,
74     fpu_s0_arm64,       fpu_s1_arm64,   fpu_s2_arm64,  fpu_s3_arm64,
75     fpu_s4_arm64,       fpu_s5_arm64,   fpu_s6_arm64,  fpu_s7_arm64,
76     fpu_s8_arm64,       fpu_s9_arm64,   fpu_s10_arm64, fpu_s11_arm64,
77     fpu_s12_arm64,      fpu_s13_arm64,  fpu_s14_arm64, fpu_s15_arm64,
78     fpu_s16_arm64,      fpu_s17_arm64,  fpu_s18_arm64, fpu_s19_arm64,
79     fpu_s20_arm64,      fpu_s21_arm64,  fpu_s22_arm64, fpu_s23_arm64,
80     fpu_s24_arm64,      fpu_s25_arm64,  fpu_s26_arm64, fpu_s27_arm64,
81     fpu_s28_arm64,      fpu_s29_arm64,  fpu_s30_arm64, fpu_s31_arm64,
82 
83     fpu_d0_arm64,       fpu_d1_arm64,   fpu_d2_arm64,  fpu_d3_arm64,
84     fpu_d4_arm64,       fpu_d5_arm64,   fpu_d6_arm64,  fpu_d7_arm64,
85     fpu_d8_arm64,       fpu_d9_arm64,   fpu_d10_arm64, fpu_d11_arm64,
86     fpu_d12_arm64,      fpu_d13_arm64,  fpu_d14_arm64, fpu_d15_arm64,
87     fpu_d16_arm64,      fpu_d17_arm64,  fpu_d18_arm64, fpu_d19_arm64,
88     fpu_d20_arm64,      fpu_d21_arm64,  fpu_d22_arm64, fpu_d23_arm64,
89     fpu_d24_arm64,      fpu_d25_arm64,  fpu_d26_arm64, fpu_d27_arm64,
90     fpu_d28_arm64,      fpu_d29_arm64,  fpu_d30_arm64, fpu_d31_arm64,
91     fpu_fpsr_arm64,     fpu_fpcr_arm64,
92     LLDB_INVALID_REGNUM // register sets need to end with this flag
93 };
94 static_assert(((sizeof g_fpu_regnums_arm64 / sizeof g_fpu_regnums_arm64[0]) -
95                1) == k_num_fpr_registers_arm64,
96               "g_fpu_regnums_arm64 has wrong number of register infos");
97 
98 namespace {
99 // Number of register sets provided by this context.
100 enum { k_num_register_sets = 2 };
101 }
102 
103 // Register sets for ARM64.
104 static const RegisterSet g_reg_sets_arm64[k_num_register_sets] = {
105     {"General Purpose Registers", "gpr", k_num_gpr_registers_arm64,
106      g_gpr_regnums_arm64},
107     {"Floating Point Registers", "fpu", k_num_fpr_registers_arm64,
108      g_fpu_regnums_arm64}};
109 
110 std::unique_ptr<NativeRegisterContextLinux>
111 NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux(
112     const ArchSpec &target_arch, NativeThreadProtocol &native_thread) {
113   switch (target_arch.GetMachine()) {
114   case llvm::Triple::arm:
115     return std::make_unique<NativeRegisterContextLinux_arm>(target_arch,
116                                                              native_thread);
117   case llvm::Triple::aarch64:
118     return std::make_unique<NativeRegisterContextLinux_arm64>(target_arch,
119                                                                native_thread);
120   default:
121     llvm_unreachable("have no register context for architecture");
122   }
123 }
124 
125 NativeRegisterContextLinux_arm64::NativeRegisterContextLinux_arm64(
126     const ArchSpec &target_arch, NativeThreadProtocol &native_thread)
127     : NativeRegisterContextLinux(native_thread,
128                                  new RegisterInfoPOSIX_arm64(target_arch)) {
129   switch (target_arch.GetMachine()) {
130   case llvm::Triple::aarch64:
131     m_reg_info.num_registers = k_num_registers_arm64;
132     m_reg_info.num_gpr_registers = k_num_gpr_registers_arm64;
133     m_reg_info.num_fpr_registers = k_num_fpr_registers_arm64;
134     m_reg_info.last_gpr = k_last_gpr_arm64;
135     m_reg_info.first_fpr = k_first_fpr_arm64;
136     m_reg_info.last_fpr = k_last_fpr_arm64;
137     m_reg_info.first_fpr_v = fpu_v0_arm64;
138     m_reg_info.last_fpr_v = fpu_v31_arm64;
139     m_reg_info.gpr_flags = gpr_cpsr_arm64;
140     break;
141   default:
142     llvm_unreachable("Unhandled target architecture.");
143     break;
144   }
145 
146   ::memset(&m_fpr, 0, sizeof(m_fpr));
147   ::memset(&m_gpr_arm64, 0, sizeof(m_gpr_arm64));
148   ::memset(&m_hwp_regs, 0, sizeof(m_hwp_regs));
149   ::memset(&m_hbr_regs, 0, sizeof(m_hbr_regs));
150 
151   // 16 is just a maximum value, query hardware for actual watchpoint count
152   m_max_hwp_supported = 16;
153   m_max_hbp_supported = 16;
154   m_refresh_hwdebug_info = true;
155 
156   m_gpr_is_valid = false;
157   m_fpu_is_valid = false;
158 }
159 
160 uint32_t NativeRegisterContextLinux_arm64::GetRegisterSetCount() const {
161   return k_num_register_sets;
162 }
163 
164 const RegisterSet *
165 NativeRegisterContextLinux_arm64::GetRegisterSet(uint32_t set_index) const {
166   if (set_index < k_num_register_sets)
167     return &g_reg_sets_arm64[set_index];
168 
169   return nullptr;
170 }
171 
172 uint32_t NativeRegisterContextLinux_arm64::GetUserRegisterCount() const {
173   uint32_t count = 0;
174   for (uint32_t set_index = 0; set_index < k_num_register_sets; ++set_index)
175     count += g_reg_sets_arm64[set_index].num_registers;
176   return count;
177 }
178 
179 Status
180 NativeRegisterContextLinux_arm64::ReadRegister(const RegisterInfo *reg_info,
181                                                RegisterValue &reg_value) {
182   Status error;
183 
184   if (!reg_info) {
185     error.SetErrorString("reg_info NULL");
186     return error;
187   }
188 
189   const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
190 
191   if (reg == LLDB_INVALID_REGNUM)
192     return Status("no lldb regnum for %s", reg_info && reg_info->name
193                                                ? reg_info->name
194                                                : "<unknown register>");
195 
196   uint8_t *src;
197   uint32_t offset;
198 
199   if (IsGPR(reg)) {
200     if (!m_gpr_is_valid) {
201       error = ReadGPR();
202       if (error.Fail())
203         return error;
204     }
205 
206     offset = reg_info->byte_offset;
207     assert(offset < GetGPRSize());
208     src = (uint8_t *)GetGPRBuffer() + offset;
209 
210   } else if (IsFPR(reg)) {
211     if (!m_fpu_is_valid) {
212 
213       error = ReadFPR();
214       if (error.Fail())
215         return error;
216     }
217     offset = CalculateFprOffset(reg_info);
218     assert(offset < GetFPRSize());
219     src = (uint8_t *)GetFPRBuffer() + offset;
220   } else
221     return Status("failed - register wasn't recognized to be a GPR or an FPR, "
222                   "write strategy unknown");
223 
224   reg_value.SetFromMemoryData(reg_info, src, reg_info->byte_size,
225                               eByteOrderLittle, error);
226 
227   return error;
228 }
229 
230 Status NativeRegisterContextLinux_arm64::WriteRegister(
231     const RegisterInfo *reg_info, const RegisterValue &reg_value) {
232   Status error;
233 
234   if (!reg_info)
235     return Status("reg_info NULL");
236 
237   const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
238 
239   if (reg == LLDB_INVALID_REGNUM)
240     return Status("no lldb regnum for %s", reg_info && reg_info->name
241                                                ? reg_info->name
242                                                : "<unknown register>");
243 
244   uint8_t *dst;
245   uint32_t offset;
246 
247   if (IsGPR(reg)) {
248     if (!m_gpr_is_valid) {
249       error = ReadGPR();
250       if (error.Fail())
251         return error;
252     }
253 
254     offset = reg_info->byte_offset;
255     assert(offset < GetGPRSize());
256     dst = (uint8_t *)GetGPRBuffer() + offset;
257 
258     ::memcpy(dst, reg_value.GetBytes(), reg_info->byte_size);
259 
260     return WriteGPR();
261   } else if (IsFPR(reg)) {
262     if (!m_fpu_is_valid) {
263       error = ReadFPR();
264       if (error.Fail())
265         return error;
266     }
267     offset = CalculateFprOffset(reg_info);
268     assert(offset < GetFPRSize());
269     dst = (uint8_t *)GetFPRBuffer() + offset;
270 
271     ::memcpy(dst, reg_value.GetBytes(), reg_info->byte_size);
272 
273     return WriteFPR();
274   }
275 
276   return error;
277 }
278 
279 Status NativeRegisterContextLinux_arm64::ReadAllRegisterValues(
280     lldb::DataBufferSP &data_sp) {
281   Status error;
282 
283   data_sp.reset(new DataBufferHeap(REG_CONTEXT_SIZE, 0));
284   if (!m_gpr_is_valid) {
285     error = ReadGPR();
286     if (error.Fail())
287       return error;
288   }
289 
290   if (!m_fpu_is_valid) {
291     error = ReadFPR();
292     if (error.Fail())
293       return error;
294   }
295   uint8_t *dst = data_sp->GetBytes();
296   ::memcpy(dst, GetGPRBuffer(), GetGPRSize());
297   dst += GetGPRSize();
298   ::memcpy(dst, GetFPRBuffer(), GetFPRSize());
299 
300   return error;
301 }
302 
303 Status NativeRegisterContextLinux_arm64::WriteAllRegisterValues(
304     const lldb::DataBufferSP &data_sp) {
305   Status error;
306 
307   if (!data_sp) {
308     error.SetErrorStringWithFormat(
309         "NativeRegisterContextLinux_x86_64::%s invalid data_sp provided",
310         __FUNCTION__);
311     return error;
312   }
313 
314   if (data_sp->GetByteSize() != REG_CONTEXT_SIZE) {
315     error.SetErrorStringWithFormat(
316         "NativeRegisterContextLinux_x86_64::%s data_sp contained mismatched "
317         "data size, expected %" PRIu64 ", actual %" PRIu64,
318         __FUNCTION__, REG_CONTEXT_SIZE, data_sp->GetByteSize());
319     return error;
320   }
321 
322   uint8_t *src = data_sp->GetBytes();
323   if (src == nullptr) {
324     error.SetErrorStringWithFormat("NativeRegisterContextLinux_x86_64::%s "
325                                    "DataBuffer::GetBytes() returned a null "
326                                    "pointer",
327                                    __FUNCTION__);
328     return error;
329   }
330   ::memcpy(GetGPRBuffer(), src, GetRegisterInfoInterface().GetGPRSize());
331 
332   error = WriteGPR();
333   if (error.Fail())
334     return error;
335 
336   src += GetRegisterInfoInterface().GetGPRSize();
337   ::memcpy(GetFPRBuffer(), src, GetFPRSize());
338 
339   error = WriteFPR();
340   if (error.Fail())
341     return error;
342 
343   return error;
344 }
345 
346 bool NativeRegisterContextLinux_arm64::IsGPR(unsigned reg) const {
347   return reg <= m_reg_info.last_gpr; // GPR's come first.
348 }
349 
350 bool NativeRegisterContextLinux_arm64::IsFPR(unsigned reg) const {
351   return (m_reg_info.first_fpr <= reg && reg <= m_reg_info.last_fpr);
352 }
353 
354 uint32_t NativeRegisterContextLinux_arm64::NumSupportedHardwareBreakpoints() {
355   Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_BREAKPOINTS));
356 
357   LLDB_LOGF(log, "NativeRegisterContextLinux_arm64::%s()", __FUNCTION__);
358 
359   Status error;
360 
361   // Read hardware breakpoint and watchpoint information.
362   error = ReadHardwareDebugInfo();
363 
364   if (error.Fail())
365     return 0;
366 
367   return m_max_hbp_supported;
368 }
369 
370 uint32_t
371 NativeRegisterContextLinux_arm64::SetHardwareBreakpoint(lldb::addr_t addr,
372                                                         size_t size) {
373   Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_BREAKPOINTS));
374   LLDB_LOG(log, "addr: {0:x}, size: {1:x}", addr, size);
375 
376   // Read hardware breakpoint and watchpoint information.
377   Status error = ReadHardwareDebugInfo();
378 
379   if (error.Fail())
380     return LLDB_INVALID_INDEX32;
381 
382   uint32_t control_value = 0, bp_index = 0;
383 
384   // Check if size has a valid hardware breakpoint length.
385   if (size != 4)
386     return LLDB_INVALID_INDEX32; // Invalid size for a AArch64 hardware
387                                  // breakpoint
388 
389   // Check 4-byte alignment for hardware breakpoint target address.
390   if (addr & 0x03)
391     return LLDB_INVALID_INDEX32; // Invalid address, should be 4-byte aligned.
392 
393   // Setup control value
394   control_value = 0;
395   control_value |= ((1 << size) - 1) << 5;
396   control_value |= (2 << 1) | 1;
397 
398   // Iterate over stored breakpoints and find a free bp_index
399   bp_index = LLDB_INVALID_INDEX32;
400   for (uint32_t i = 0; i < m_max_hbp_supported; i++) {
401     if ((m_hbr_regs[i].control & 1) == 0) {
402       bp_index = i; // Mark last free slot
403     } else if (m_hbr_regs[i].address == addr) {
404       return LLDB_INVALID_INDEX32; // We do not support duplicate breakpoints.
405     }
406   }
407 
408   if (bp_index == LLDB_INVALID_INDEX32)
409     return LLDB_INVALID_INDEX32;
410 
411   // Update breakpoint in local cache
412   m_hbr_regs[bp_index].real_addr = addr;
413   m_hbr_regs[bp_index].address = addr;
414   m_hbr_regs[bp_index].control = control_value;
415 
416   // PTRACE call to set corresponding hardware breakpoint register.
417   error = WriteHardwareDebugRegs(eDREGTypeBREAK);
418 
419   if (error.Fail()) {
420     m_hbr_regs[bp_index].address = 0;
421     m_hbr_regs[bp_index].control &= ~1;
422 
423     return LLDB_INVALID_INDEX32;
424   }
425 
426   return bp_index;
427 }
428 
429 bool NativeRegisterContextLinux_arm64::ClearHardwareBreakpoint(
430     uint32_t hw_idx) {
431   Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_BREAKPOINTS));
432   LLDB_LOG(log, "hw_idx: {0}", hw_idx);
433 
434   // Read hardware breakpoint and watchpoint information.
435   Status error = ReadHardwareDebugInfo();
436 
437   if (error.Fail())
438     return false;
439 
440   if (hw_idx >= m_max_hbp_supported)
441     return false;
442 
443   // Create a backup we can revert to in case of failure.
444   lldb::addr_t tempAddr = m_hbr_regs[hw_idx].address;
445   uint32_t tempControl = m_hbr_regs[hw_idx].control;
446 
447   m_hbr_regs[hw_idx].control &= ~1;
448   m_hbr_regs[hw_idx].address = 0;
449 
450   // PTRACE call to clear corresponding hardware breakpoint register.
451   error = WriteHardwareDebugRegs(eDREGTypeBREAK);
452 
453   if (error.Fail()) {
454     m_hbr_regs[hw_idx].control = tempControl;
455     m_hbr_regs[hw_idx].address = tempAddr;
456 
457     return false;
458   }
459 
460   return true;
461 }
462 
463 Status NativeRegisterContextLinux_arm64::GetHardwareBreakHitIndex(
464     uint32_t &bp_index, lldb::addr_t trap_addr) {
465   Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_BREAKPOINTS));
466 
467   LLDB_LOGF(log, "NativeRegisterContextLinux_arm64::%s()", __FUNCTION__);
468 
469   lldb::addr_t break_addr;
470 
471   for (bp_index = 0; bp_index < m_max_hbp_supported; ++bp_index) {
472     break_addr = m_hbr_regs[bp_index].address;
473 
474     if ((m_hbr_regs[bp_index].control & 0x1) && (trap_addr == break_addr)) {
475       m_hbr_regs[bp_index].hit_addr = trap_addr;
476       return Status();
477     }
478   }
479 
480   bp_index = LLDB_INVALID_INDEX32;
481   return Status();
482 }
483 
484 Status NativeRegisterContextLinux_arm64::ClearAllHardwareBreakpoints() {
485   Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_BREAKPOINTS));
486 
487   LLDB_LOGF(log, "NativeRegisterContextLinux_arm64::%s()", __FUNCTION__);
488 
489   Status error;
490 
491   // Read hardware breakpoint and watchpoint information.
492   error = ReadHardwareDebugInfo();
493 
494   if (error.Fail())
495     return error;
496 
497   lldb::addr_t tempAddr = 0;
498   uint32_t tempControl = 0;
499 
500   for (uint32_t i = 0; i < m_max_hbp_supported; i++) {
501     if (m_hbr_regs[i].control & 0x01) {
502       // Create a backup we can revert to in case of failure.
503       tempAddr = m_hbr_regs[i].address;
504       tempControl = m_hbr_regs[i].control;
505 
506       // Clear watchpoints in local cache
507       m_hbr_regs[i].control &= ~1;
508       m_hbr_regs[i].address = 0;
509 
510       // Ptrace call to update hardware debug registers
511       error = WriteHardwareDebugRegs(eDREGTypeBREAK);
512 
513       if (error.Fail()) {
514         m_hbr_regs[i].control = tempControl;
515         m_hbr_regs[i].address = tempAddr;
516 
517         return error;
518       }
519     }
520   }
521 
522   return Status();
523 }
524 
525 uint32_t NativeRegisterContextLinux_arm64::NumSupportedHardwareWatchpoints() {
526   Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS));
527 
528   // Read hardware breakpoint and watchpoint information.
529   Status error = ReadHardwareDebugInfo();
530 
531   if (error.Fail())
532     return 0;
533 
534   LLDB_LOG(log, "{0}", m_max_hwp_supported);
535   return m_max_hwp_supported;
536 }
537 
538 uint32_t NativeRegisterContextLinux_arm64::SetHardwareWatchpoint(
539     lldb::addr_t addr, size_t size, uint32_t watch_flags) {
540   Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS));
541   LLDB_LOG(log, "addr: {0:x}, size: {1:x} watch_flags: {2:x}", addr, size,
542            watch_flags);
543 
544   // Read hardware breakpoint and watchpoint information.
545   Status error = ReadHardwareDebugInfo();
546 
547   if (error.Fail())
548     return LLDB_INVALID_INDEX32;
549 
550   uint32_t control_value = 0, wp_index = 0;
551   lldb::addr_t real_addr = addr;
552 
553   // Check if we are setting watchpoint other than read/write/access Also
554   // update watchpoint flag to match AArch64 write-read bit configuration.
555   switch (watch_flags) {
556   case 1:
557     watch_flags = 2;
558     break;
559   case 2:
560     watch_flags = 1;
561     break;
562   case 3:
563     break;
564   default:
565     return LLDB_INVALID_INDEX32;
566   }
567 
568   // Check if size has a valid hardware watchpoint length.
569   if (size != 1 && size != 2 && size != 4 && size != 8)
570     return LLDB_INVALID_INDEX32;
571 
572   // Check 8-byte alignment for hardware watchpoint target address. Below is a
573   // hack to recalculate address and size in order to make sure we can watch
574   // non 8-byte alligned addresses as well.
575   if (addr & 0x07) {
576     uint8_t watch_mask = (addr & 0x07) + size;
577 
578     if (watch_mask > 0x08)
579       return LLDB_INVALID_INDEX32;
580     else if (watch_mask <= 0x02)
581       size = 2;
582     else if (watch_mask <= 0x04)
583       size = 4;
584     else
585       size = 8;
586 
587     addr = addr & (~0x07);
588   }
589 
590   // Setup control value
591   control_value = watch_flags << 3;
592   control_value |= ((1 << size) - 1) << 5;
593   control_value |= (2 << 1) | 1;
594 
595   // Iterate over stored watchpoints and find a free wp_index
596   wp_index = LLDB_INVALID_INDEX32;
597   for (uint32_t i = 0; i < m_max_hwp_supported; i++) {
598     if ((m_hwp_regs[i].control & 1) == 0) {
599       wp_index = i; // Mark last free slot
600     } else if (m_hwp_regs[i].address == addr) {
601       return LLDB_INVALID_INDEX32; // We do not support duplicate watchpoints.
602     }
603   }
604 
605   if (wp_index == LLDB_INVALID_INDEX32)
606     return LLDB_INVALID_INDEX32;
607 
608   // Update watchpoint in local cache
609   m_hwp_regs[wp_index].real_addr = real_addr;
610   m_hwp_regs[wp_index].address = addr;
611   m_hwp_regs[wp_index].control = control_value;
612 
613   // PTRACE call to set corresponding watchpoint register.
614   error = WriteHardwareDebugRegs(eDREGTypeWATCH);
615 
616   if (error.Fail()) {
617     m_hwp_regs[wp_index].address = 0;
618     m_hwp_regs[wp_index].control &= ~1;
619 
620     return LLDB_INVALID_INDEX32;
621   }
622 
623   return wp_index;
624 }
625 
626 bool NativeRegisterContextLinux_arm64::ClearHardwareWatchpoint(
627     uint32_t wp_index) {
628   Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS));
629   LLDB_LOG(log, "wp_index: {0}", wp_index);
630 
631   // Read hardware breakpoint and watchpoint information.
632   Status error = ReadHardwareDebugInfo();
633 
634   if (error.Fail())
635     return false;
636 
637   if (wp_index >= m_max_hwp_supported)
638     return false;
639 
640   // Create a backup we can revert to in case of failure.
641   lldb::addr_t tempAddr = m_hwp_regs[wp_index].address;
642   uint32_t tempControl = m_hwp_regs[wp_index].control;
643 
644   // Update watchpoint in local cache
645   m_hwp_regs[wp_index].control &= ~1;
646   m_hwp_regs[wp_index].address = 0;
647 
648   // Ptrace call to update hardware debug registers
649   error = WriteHardwareDebugRegs(eDREGTypeWATCH);
650 
651   if (error.Fail()) {
652     m_hwp_regs[wp_index].control = tempControl;
653     m_hwp_regs[wp_index].address = tempAddr;
654 
655     return false;
656   }
657 
658   return true;
659 }
660 
661 Status NativeRegisterContextLinux_arm64::ClearAllHardwareWatchpoints() {
662   // Read hardware breakpoint and watchpoint information.
663   Status error = ReadHardwareDebugInfo();
664 
665   if (error.Fail())
666     return error;
667 
668   lldb::addr_t tempAddr = 0;
669   uint32_t tempControl = 0;
670 
671   for (uint32_t i = 0; i < m_max_hwp_supported; i++) {
672     if (m_hwp_regs[i].control & 0x01) {
673       // Create a backup we can revert to in case of failure.
674       tempAddr = m_hwp_regs[i].address;
675       tempControl = m_hwp_regs[i].control;
676 
677       // Clear watchpoints in local cache
678       m_hwp_regs[i].control &= ~1;
679       m_hwp_regs[i].address = 0;
680 
681       // Ptrace call to update hardware debug registers
682       error = WriteHardwareDebugRegs(eDREGTypeWATCH);
683 
684       if (error.Fail()) {
685         m_hwp_regs[i].control = tempControl;
686         m_hwp_regs[i].address = tempAddr;
687 
688         return error;
689       }
690     }
691   }
692 
693   return Status();
694 }
695 
696 uint32_t
697 NativeRegisterContextLinux_arm64::GetWatchpointSize(uint32_t wp_index) {
698   Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS));
699   LLDB_LOG(log, "wp_index: {0}", wp_index);
700 
701   switch ((m_hwp_regs[wp_index].control >> 5) & 0xff) {
702   case 0x01:
703     return 1;
704   case 0x03:
705     return 2;
706   case 0x0f:
707     return 4;
708   case 0xff:
709     return 8;
710   default:
711     return 0;
712   }
713 }
714 bool NativeRegisterContextLinux_arm64::WatchpointIsEnabled(uint32_t wp_index) {
715   Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS));
716   LLDB_LOG(log, "wp_index: {0}", wp_index);
717 
718   if ((m_hwp_regs[wp_index].control & 0x1) == 0x1)
719     return true;
720   else
721     return false;
722 }
723 
724 Status NativeRegisterContextLinux_arm64::GetWatchpointHitIndex(
725     uint32_t &wp_index, lldb::addr_t trap_addr) {
726   Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS));
727   LLDB_LOG(log, "wp_index: {0}, trap_addr: {1:x}", wp_index, trap_addr);
728 
729   uint32_t watch_size;
730   lldb::addr_t watch_addr;
731 
732   for (wp_index = 0; wp_index < m_max_hwp_supported; ++wp_index) {
733     watch_size = GetWatchpointSize(wp_index);
734     watch_addr = m_hwp_regs[wp_index].address;
735 
736     if (WatchpointIsEnabled(wp_index) && trap_addr >= watch_addr &&
737         trap_addr < watch_addr + watch_size) {
738       m_hwp_regs[wp_index].hit_addr = trap_addr;
739       return Status();
740     }
741   }
742 
743   wp_index = LLDB_INVALID_INDEX32;
744   return Status();
745 }
746 
747 lldb::addr_t
748 NativeRegisterContextLinux_arm64::GetWatchpointAddress(uint32_t wp_index) {
749   Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS));
750   LLDB_LOG(log, "wp_index: {0}", wp_index);
751 
752   if (wp_index >= m_max_hwp_supported)
753     return LLDB_INVALID_ADDRESS;
754 
755   if (WatchpointIsEnabled(wp_index))
756     return m_hwp_regs[wp_index].real_addr;
757   else
758     return LLDB_INVALID_ADDRESS;
759 }
760 
761 lldb::addr_t
762 NativeRegisterContextLinux_arm64::GetWatchpointHitAddress(uint32_t wp_index) {
763   Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS));
764   LLDB_LOG(log, "wp_index: {0}", wp_index);
765 
766   if (wp_index >= m_max_hwp_supported)
767     return LLDB_INVALID_ADDRESS;
768 
769   if (WatchpointIsEnabled(wp_index))
770     return m_hwp_regs[wp_index].hit_addr;
771   else
772     return LLDB_INVALID_ADDRESS;
773 }
774 
775 Status NativeRegisterContextLinux_arm64::ReadHardwareDebugInfo() {
776   if (!m_refresh_hwdebug_info) {
777     return Status();
778   }
779 
780   ::pid_t tid = m_thread.GetID();
781 
782   int regset = NT_ARM_HW_WATCH;
783   struct iovec ioVec;
784   struct user_hwdebug_state dreg_state;
785   Status error;
786 
787   ioVec.iov_base = &dreg_state;
788   ioVec.iov_len = sizeof(dreg_state);
789   error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, tid, &regset,
790                                             &ioVec, ioVec.iov_len);
791 
792   if (error.Fail())
793     return error;
794 
795   m_max_hwp_supported = dreg_state.dbg_info & 0xff;
796 
797   regset = NT_ARM_HW_BREAK;
798   error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, tid, &regset,
799                                             &ioVec, ioVec.iov_len);
800 
801   if (error.Fail())
802     return error;
803 
804   m_max_hbp_supported = dreg_state.dbg_info & 0xff;
805   m_refresh_hwdebug_info = false;
806 
807   return error;
808 }
809 
810 Status NativeRegisterContextLinux_arm64::WriteHardwareDebugRegs(int hwbType) {
811   struct iovec ioVec;
812   struct user_hwdebug_state dreg_state;
813   Status error;
814 
815   memset(&dreg_state, 0, sizeof(dreg_state));
816   ioVec.iov_base = &dreg_state;
817 
818   if (hwbType == eDREGTypeWATCH) {
819     hwbType = NT_ARM_HW_WATCH;
820     ioVec.iov_len = sizeof(dreg_state.dbg_info) + sizeof(dreg_state.pad) +
821                     (sizeof(dreg_state.dbg_regs[0]) * m_max_hwp_supported);
822 
823     for (uint32_t i = 0; i < m_max_hwp_supported; i++) {
824       dreg_state.dbg_regs[i].addr = m_hwp_regs[i].address;
825       dreg_state.dbg_regs[i].ctrl = m_hwp_regs[i].control;
826     }
827   } else {
828     hwbType = NT_ARM_HW_BREAK;
829     ioVec.iov_len = sizeof(dreg_state.dbg_info) + sizeof(dreg_state.pad) +
830                     (sizeof(dreg_state.dbg_regs[0]) * m_max_hbp_supported);
831 
832     for (uint32_t i = 0; i < m_max_hbp_supported; i++) {
833       dreg_state.dbg_regs[i].addr = m_hbr_regs[i].address;
834       dreg_state.dbg_regs[i].ctrl = m_hbr_regs[i].control;
835     }
836   }
837 
838   return NativeProcessLinux::PtraceWrapper(PTRACE_SETREGSET, m_thread.GetID(),
839                                            &hwbType, &ioVec, ioVec.iov_len);
840 }
841 
842 Status NativeRegisterContextLinux_arm64::ReadGPR() {
843   Status error;
844 
845   struct iovec ioVec;
846 
847   ioVec.iov_base = GetGPRBuffer();
848   ioVec.iov_len = GetGPRSize();
849 
850   error = ReadRegisterSet(&ioVec, GetGPRSize(), NT_PRSTATUS);
851 
852   if (error.Success())
853     m_gpr_is_valid = true;
854 
855   return error;
856 }
857 
858 Status NativeRegisterContextLinux_arm64::WriteGPR() {
859   struct iovec ioVec;
860 
861   m_gpr_is_valid = false;
862 
863   ioVec.iov_base = GetGPRBuffer();
864   ioVec.iov_len = GetGPRSize();
865 
866   return WriteRegisterSet(&ioVec, GetGPRSize(), NT_PRSTATUS);
867 }
868 
869 Status NativeRegisterContextLinux_arm64::ReadFPR() {
870   Status error;
871 
872   struct iovec ioVec;
873 
874   ioVec.iov_base = GetFPRBuffer();
875   ioVec.iov_len = GetFPRSize();
876 
877   error = ReadRegisterSet(&ioVec, GetFPRSize(), NT_FPREGSET);
878 
879   if (error.Success())
880     m_fpu_is_valid = true;
881 
882   return error;
883 }
884 
885 Status NativeRegisterContextLinux_arm64::WriteFPR() {
886   struct iovec ioVec;
887 
888   m_fpu_is_valid = false;
889 
890   ioVec.iov_base = GetFPRBuffer();
891   ioVec.iov_len = GetFPRSize();
892 
893   return WriteRegisterSet(&ioVec, GetFPRSize(), NT_FPREGSET);
894 }
895 
896 void NativeRegisterContextLinux_arm64::InvalidateAllRegisters() {
897   m_gpr_is_valid = false;
898   m_fpu_is_valid = false;
899 }
900 
901 uint32_t NativeRegisterContextLinux_arm64::CalculateFprOffset(
902     const RegisterInfo *reg_info) const {
903   return reg_info->byte_offset -
904          GetRegisterInfoAtIndex(m_reg_info.first_fpr)->byte_offset;
905 }
906 
907 #endif // defined (__arm64__) || defined (__aarch64__)
908