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