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 llvm::make_unique<NativeRegisterContextLinux_arm>(target_arch,
116                                                              native_thread);
117   case llvm::Triple::aarch64:
118     return llvm::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 
157 uint32_t NativeRegisterContextLinux_arm64::GetRegisterSetCount() const {
158   return k_num_register_sets;
159 }
160 
161 const RegisterSet *
162 NativeRegisterContextLinux_arm64::GetRegisterSet(uint32_t set_index) const {
163   if (set_index < k_num_register_sets)
164     return &g_reg_sets_arm64[set_index];
165 
166   return nullptr;
167 }
168 
169 uint32_t NativeRegisterContextLinux_arm64::GetUserRegisterCount() const {
170   uint32_t count = 0;
171   for (uint32_t set_index = 0; set_index < k_num_register_sets; ++set_index)
172     count += g_reg_sets_arm64[set_index].num_registers;
173   return count;
174 }
175 
176 Status
177 NativeRegisterContextLinux_arm64::ReadRegister(const RegisterInfo *reg_info,
178                                                RegisterValue &reg_value) {
179   Status error;
180 
181   if (!reg_info) {
182     error.SetErrorString("reg_info NULL");
183     return error;
184   }
185 
186   const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
187 
188   if (IsFPR(reg)) {
189     error = ReadFPR();
190     if (error.Fail())
191       return error;
192   } else {
193     uint32_t full_reg = reg;
194     bool is_subreg = reg_info->invalidate_regs &&
195                      (reg_info->invalidate_regs[0] != LLDB_INVALID_REGNUM);
196 
197     if (is_subreg) {
198       // Read the full aligned 64-bit register.
199       full_reg = reg_info->invalidate_regs[0];
200     }
201 
202     error = ReadRegisterRaw(full_reg, reg_value);
203 
204     if (error.Success()) {
205       // If our read was not aligned (for ah,bh,ch,dh), shift our returned
206       // value one byte to the right.
207       if (is_subreg && (reg_info->byte_offset & 0x1))
208         reg_value.SetUInt64(reg_value.GetAsUInt64() >> 8);
209 
210       // If our return byte size was greater than the return value reg size,
211       // then use the type specified by reg_info rather than the uint64_t
212       // default
213       if (reg_value.GetByteSize() > reg_info->byte_size)
214         reg_value.SetType(reg_info);
215     }
216     return error;
217   }
218 
219   // Get pointer to m_fpr variable and set the data from it.
220   uint32_t fpr_offset = CalculateFprOffset(reg_info);
221   assert(fpr_offset < sizeof m_fpr);
222   uint8_t *src = (uint8_t *)&m_fpr + fpr_offset;
223   reg_value.SetFromMemoryData(reg_info, src, reg_info->byte_size,
224                               eByteOrderLittle, error);
225 
226   return error;
227 }
228 
229 Status NativeRegisterContextLinux_arm64::WriteRegister(
230     const RegisterInfo *reg_info, const RegisterValue &reg_value) {
231   if (!reg_info)
232     return Status("reg_info NULL");
233 
234   const uint32_t reg_index = reg_info->kinds[lldb::eRegisterKindLLDB];
235   if (reg_index == LLDB_INVALID_REGNUM)
236     return Status("no lldb regnum for %s", reg_info && reg_info->name
237                                                ? reg_info->name
238                                                : "<unknown register>");
239 
240   if (IsGPR(reg_index))
241     return WriteRegisterRaw(reg_index, reg_value);
242 
243   if (IsFPR(reg_index)) {
244     // Get pointer to m_fpr variable and set the data to it.
245     uint32_t fpr_offset = CalculateFprOffset(reg_info);
246     assert(fpr_offset < sizeof m_fpr);
247     uint8_t *dst = (uint8_t *)&m_fpr + fpr_offset;
248     switch (reg_info->byte_size) {
249     case 2:
250       *(uint16_t *)dst = reg_value.GetAsUInt16();
251       break;
252     case 4:
253       *(uint32_t *)dst = reg_value.GetAsUInt32();
254       break;
255     case 8:
256       *(uint64_t *)dst = reg_value.GetAsUInt64();
257       break;
258     default:
259       assert(false && "Unhandled data size.");
260       return Status("unhandled register data size %" PRIu32,
261                     reg_info->byte_size);
262     }
263 
264     Status error = WriteFPR();
265     if (error.Fail())
266       return error;
267 
268     return Status();
269   }
270 
271   return Status("failed - register wasn't recognized to be a GPR or an FPR, "
272                 "write strategy unknown");
273 }
274 
275 Status NativeRegisterContextLinux_arm64::ReadAllRegisterValues(
276     lldb::DataBufferSP &data_sp) {
277   Status error;
278 
279   data_sp.reset(new DataBufferHeap(REG_CONTEXT_SIZE, 0));
280   if (!data_sp)
281     return Status("failed to allocate DataBufferHeap instance of size %" PRIu64,
282                   REG_CONTEXT_SIZE);
283 
284   error = ReadGPR();
285   if (error.Fail())
286     return error;
287 
288   error = ReadFPR();
289   if (error.Fail())
290     return error;
291 
292   uint8_t *dst = data_sp->GetBytes();
293   if (dst == nullptr) {
294     error.SetErrorStringWithFormat("DataBufferHeap instance of size %" PRIu64
295                                    " returned a null pointer",
296                                    REG_CONTEXT_SIZE);
297     return error;
298   }
299 
300   ::memcpy(dst, &m_gpr_arm64, GetGPRSize());
301   dst += GetGPRSize();
302   ::memcpy(dst, &m_fpr, sizeof(m_fpr));
303 
304   return error;
305 }
306 
307 Status NativeRegisterContextLinux_arm64::WriteAllRegisterValues(
308     const lldb::DataBufferSP &data_sp) {
309   Status error;
310 
311   if (!data_sp) {
312     error.SetErrorStringWithFormat(
313         "NativeRegisterContextLinux_x86_64::%s invalid data_sp provided",
314         __FUNCTION__);
315     return error;
316   }
317 
318   if (data_sp->GetByteSize() != REG_CONTEXT_SIZE) {
319     error.SetErrorStringWithFormat(
320         "NativeRegisterContextLinux_x86_64::%s data_sp contained mismatched "
321         "data size, expected %" PRIu64 ", actual %" PRIu64,
322         __FUNCTION__, REG_CONTEXT_SIZE, data_sp->GetByteSize());
323     return error;
324   }
325 
326   uint8_t *src = data_sp->GetBytes();
327   if (src == nullptr) {
328     error.SetErrorStringWithFormat("NativeRegisterContextLinux_x86_64::%s "
329                                    "DataBuffer::GetBytes() returned a null "
330                                    "pointer",
331                                    __FUNCTION__);
332     return error;
333   }
334   ::memcpy(&m_gpr_arm64, src, GetRegisterInfoInterface().GetGPRSize());
335 
336   error = WriteGPR();
337   if (error.Fail())
338     return error;
339 
340   src += GetRegisterInfoInterface().GetGPRSize();
341   ::memcpy(&m_fpr, src, sizeof(m_fpr));
342 
343   error = WriteFPR();
344   if (error.Fail())
345     return error;
346 
347   return error;
348 }
349 
350 bool NativeRegisterContextLinux_arm64::IsGPR(unsigned reg) const {
351   return reg <= m_reg_info.last_gpr; // GPR's come first.
352 }
353 
354 bool NativeRegisterContextLinux_arm64::IsFPR(unsigned reg) const {
355   return (m_reg_info.first_fpr <= reg && reg <= m_reg_info.last_fpr);
356 }
357 
358 uint32_t NativeRegisterContextLinux_arm64::NumSupportedHardwareBreakpoints() {
359   Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_BREAKPOINTS));
360 
361   if (log)
362     log->Printf("NativeRegisterContextLinux_arm64::%s()", __FUNCTION__);
363 
364   Status error;
365 
366   // Read hardware breakpoint and watchpoint information.
367   error = ReadHardwareDebugInfo();
368 
369   if (error.Fail())
370     return 0;
371 
372   return m_max_hbp_supported;
373 }
374 
375 uint32_t
376 NativeRegisterContextLinux_arm64::SetHardwareBreakpoint(lldb::addr_t addr,
377                                                         size_t size) {
378   Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_BREAKPOINTS));
379   LLDB_LOG(log, "addr: {0:x}, size: {1:x}", addr, size);
380 
381   // Read hardware breakpoint and watchpoint information.
382   Status error = ReadHardwareDebugInfo();
383 
384   if (error.Fail())
385     return LLDB_INVALID_INDEX32;
386 
387   uint32_t control_value = 0, bp_index = 0;
388 
389   // Check if size has a valid hardware breakpoint length.
390   if (size != 4)
391     return LLDB_INVALID_INDEX32; // Invalid size for a AArch64 hardware
392                                  // breakpoint
393 
394   // Check 4-byte alignment for hardware breakpoint target address.
395   if (addr & 0x03)
396     return LLDB_INVALID_INDEX32; // Invalid address, should be 4-byte aligned.
397 
398   // Setup control value
399   control_value = 0;
400   control_value |= ((1 << size) - 1) << 5;
401   control_value |= (2 << 1) | 1;
402 
403   // Iterate over stored breakpoints and find a free bp_index
404   bp_index = LLDB_INVALID_INDEX32;
405   for (uint32_t i = 0; i < m_max_hbp_supported; i++) {
406     if ((m_hbr_regs[i].control & 1) == 0) {
407       bp_index = i; // Mark last free slot
408     } else if (m_hbr_regs[i].address == addr) {
409       return LLDB_INVALID_INDEX32; // We do not support duplicate breakpoints.
410     }
411   }
412 
413   if (bp_index == LLDB_INVALID_INDEX32)
414     return LLDB_INVALID_INDEX32;
415 
416   // Update breakpoint in local cache
417   m_hbr_regs[bp_index].real_addr = addr;
418   m_hbr_regs[bp_index].address = addr;
419   m_hbr_regs[bp_index].control = control_value;
420 
421   // PTRACE call to set corresponding hardware breakpoint register.
422   error = WriteHardwareDebugRegs(eDREGTypeBREAK);
423 
424   if (error.Fail()) {
425     m_hbr_regs[bp_index].address = 0;
426     m_hbr_regs[bp_index].control &= ~1;
427 
428     return LLDB_INVALID_INDEX32;
429   }
430 
431   return bp_index;
432 }
433 
434 bool NativeRegisterContextLinux_arm64::ClearHardwareBreakpoint(
435     uint32_t hw_idx) {
436   Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_BREAKPOINTS));
437   LLDB_LOG(log, "hw_idx: {0}", hw_idx);
438 
439   // Read hardware breakpoint and watchpoint information.
440   Status error = ReadHardwareDebugInfo();
441 
442   if (error.Fail())
443     return false;
444 
445   if (hw_idx >= m_max_hbp_supported)
446     return false;
447 
448   // Create a backup we can revert to in case of failure.
449   lldb::addr_t tempAddr = m_hbr_regs[hw_idx].address;
450   uint32_t tempControl = m_hbr_regs[hw_idx].control;
451 
452   m_hbr_regs[hw_idx].control &= ~1;
453   m_hbr_regs[hw_idx].address = 0;
454 
455   // PTRACE call to clear corresponding hardware breakpoint register.
456   error = WriteHardwareDebugRegs(eDREGTypeBREAK);
457 
458   if (error.Fail()) {
459     m_hbr_regs[hw_idx].control = tempControl;
460     m_hbr_regs[hw_idx].address = tempAddr;
461 
462     return false;
463   }
464 
465   return true;
466 }
467 
468 Status NativeRegisterContextLinux_arm64::GetHardwareBreakHitIndex(
469     uint32_t &bp_index, lldb::addr_t trap_addr) {
470   Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_BREAKPOINTS));
471 
472   if (log)
473     log->Printf("NativeRegisterContextLinux_arm64::%s()", __FUNCTION__);
474 
475   lldb::addr_t break_addr;
476 
477   for (bp_index = 0; bp_index < m_max_hbp_supported; ++bp_index) {
478     break_addr = m_hbr_regs[bp_index].address;
479 
480     if ((m_hbr_regs[bp_index].control & 0x1) && (trap_addr == break_addr)) {
481       m_hbr_regs[bp_index].hit_addr = trap_addr;
482       return Status();
483     }
484   }
485 
486   bp_index = LLDB_INVALID_INDEX32;
487   return Status();
488 }
489 
490 Status NativeRegisterContextLinux_arm64::ClearAllHardwareBreakpoints() {
491   Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_BREAKPOINTS));
492 
493   if (log)
494     log->Printf("NativeRegisterContextLinux_arm64::%s()", __FUNCTION__);
495 
496   Status error;
497 
498   // Read hardware breakpoint and watchpoint information.
499   error = ReadHardwareDebugInfo();
500 
501   if (error.Fail())
502     return error;
503 
504   lldb::addr_t tempAddr = 0;
505   uint32_t tempControl = 0;
506 
507   for (uint32_t i = 0; i < m_max_hbp_supported; i++) {
508     if (m_hbr_regs[i].control & 0x01) {
509       // Create a backup we can revert to in case of failure.
510       tempAddr = m_hbr_regs[i].address;
511       tempControl = m_hbr_regs[i].control;
512 
513       // Clear watchpoints in local cache
514       m_hbr_regs[i].control &= ~1;
515       m_hbr_regs[i].address = 0;
516 
517       // Ptrace call to update hardware debug registers
518       error = WriteHardwareDebugRegs(eDREGTypeBREAK);
519 
520       if (error.Fail()) {
521         m_hbr_regs[i].control = tempControl;
522         m_hbr_regs[i].address = tempAddr;
523 
524         return error;
525       }
526     }
527   }
528 
529   return Status();
530 }
531 
532 uint32_t NativeRegisterContextLinux_arm64::NumSupportedHardwareWatchpoints() {
533   Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS));
534 
535   // Read hardware breakpoint and watchpoint information.
536   Status error = ReadHardwareDebugInfo();
537 
538   if (error.Fail())
539     return 0;
540 
541   LLDB_LOG(log, "{0}", m_max_hwp_supported);
542   return m_max_hwp_supported;
543 }
544 
545 uint32_t NativeRegisterContextLinux_arm64::SetHardwareWatchpoint(
546     lldb::addr_t addr, size_t size, uint32_t watch_flags) {
547   Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS));
548   LLDB_LOG(log, "addr: {0:x}, size: {1:x} watch_flags: {2:x}", addr, size,
549            watch_flags);
550 
551   // Read hardware breakpoint and watchpoint information.
552   Status error = ReadHardwareDebugInfo();
553 
554   if (error.Fail())
555     return LLDB_INVALID_INDEX32;
556 
557   uint32_t control_value = 0, wp_index = 0;
558   lldb::addr_t real_addr = addr;
559 
560   // Check if we are setting watchpoint other than read/write/access Also
561   // update watchpoint flag to match AArch64 write-read bit configuration.
562   switch (watch_flags) {
563   case 1:
564     watch_flags = 2;
565     break;
566   case 2:
567     watch_flags = 1;
568     break;
569   case 3:
570     break;
571   default:
572     return LLDB_INVALID_INDEX32;
573   }
574 
575   // Check if size has a valid hardware watchpoint length.
576   if (size != 1 && size != 2 && size != 4 && size != 8)
577     return LLDB_INVALID_INDEX32;
578 
579   // Check 8-byte alignment for hardware watchpoint target address. Below is a
580   // hack to recalculate address and size in order to make sure we can watch
581   // non 8-byte alligned addresses as well.
582   if (addr & 0x07) {
583     uint8_t watch_mask = (addr & 0x07) + size;
584 
585     if (watch_mask > 0x08)
586       return LLDB_INVALID_INDEX32;
587     else if (watch_mask <= 0x02)
588       size = 2;
589     else if (watch_mask <= 0x04)
590       size = 4;
591     else
592       size = 8;
593 
594     addr = addr & (~0x07);
595   }
596 
597   // Setup control value
598   control_value = watch_flags << 3;
599   control_value |= ((1 << size) - 1) << 5;
600   control_value |= (2 << 1) | 1;
601 
602   // Iterate over stored watchpoints and find a free wp_index
603   wp_index = LLDB_INVALID_INDEX32;
604   for (uint32_t i = 0; i < m_max_hwp_supported; i++) {
605     if ((m_hwp_regs[i].control & 1) == 0) {
606       wp_index = i; // Mark last free slot
607     } else if (m_hwp_regs[i].address == addr) {
608       return LLDB_INVALID_INDEX32; // We do not support duplicate watchpoints.
609     }
610   }
611 
612   if (wp_index == LLDB_INVALID_INDEX32)
613     return LLDB_INVALID_INDEX32;
614 
615   // Update watchpoint in local cache
616   m_hwp_regs[wp_index].real_addr = real_addr;
617   m_hwp_regs[wp_index].address = addr;
618   m_hwp_regs[wp_index].control = control_value;
619 
620   // PTRACE call to set corresponding watchpoint register.
621   error = WriteHardwareDebugRegs(eDREGTypeWATCH);
622 
623   if (error.Fail()) {
624     m_hwp_regs[wp_index].address = 0;
625     m_hwp_regs[wp_index].control &= ~1;
626 
627     return LLDB_INVALID_INDEX32;
628   }
629 
630   return wp_index;
631 }
632 
633 bool NativeRegisterContextLinux_arm64::ClearHardwareWatchpoint(
634     uint32_t wp_index) {
635   Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS));
636   LLDB_LOG(log, "wp_index: {0}", wp_index);
637 
638   // Read hardware breakpoint and watchpoint information.
639   Status error = ReadHardwareDebugInfo();
640 
641   if (error.Fail())
642     return false;
643 
644   if (wp_index >= m_max_hwp_supported)
645     return false;
646 
647   // Create a backup we can revert to in case of failure.
648   lldb::addr_t tempAddr = m_hwp_regs[wp_index].address;
649   uint32_t tempControl = m_hwp_regs[wp_index].control;
650 
651   // Update watchpoint in local cache
652   m_hwp_regs[wp_index].control &= ~1;
653   m_hwp_regs[wp_index].address = 0;
654 
655   // Ptrace call to update hardware debug registers
656   error = WriteHardwareDebugRegs(eDREGTypeWATCH);
657 
658   if (error.Fail()) {
659     m_hwp_regs[wp_index].control = tempControl;
660     m_hwp_regs[wp_index].address = tempAddr;
661 
662     return false;
663   }
664 
665   return true;
666 }
667 
668 Status NativeRegisterContextLinux_arm64::ClearAllHardwareWatchpoints() {
669   // Read hardware breakpoint and watchpoint information.
670   Status error = ReadHardwareDebugInfo();
671 
672   if (error.Fail())
673     return error;
674 
675   lldb::addr_t tempAddr = 0;
676   uint32_t tempControl = 0;
677 
678   for (uint32_t i = 0; i < m_max_hwp_supported; i++) {
679     if (m_hwp_regs[i].control & 0x01) {
680       // Create a backup we can revert to in case of failure.
681       tempAddr = m_hwp_regs[i].address;
682       tempControl = m_hwp_regs[i].control;
683 
684       // Clear watchpoints in local cache
685       m_hwp_regs[i].control &= ~1;
686       m_hwp_regs[i].address = 0;
687 
688       // Ptrace call to update hardware debug registers
689       error = WriteHardwareDebugRegs(eDREGTypeWATCH);
690 
691       if (error.Fail()) {
692         m_hwp_regs[i].control = tempControl;
693         m_hwp_regs[i].address = tempAddr;
694 
695         return error;
696       }
697     }
698   }
699 
700   return Status();
701 }
702 
703 uint32_t
704 NativeRegisterContextLinux_arm64::GetWatchpointSize(uint32_t wp_index) {
705   Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS));
706   LLDB_LOG(log, "wp_index: {0}", wp_index);
707 
708   switch ((m_hwp_regs[wp_index].control >> 5) & 0xff) {
709   case 0x01:
710     return 1;
711   case 0x03:
712     return 2;
713   case 0x0f:
714     return 4;
715   case 0xff:
716     return 8;
717   default:
718     return 0;
719   }
720 }
721 bool NativeRegisterContextLinux_arm64::WatchpointIsEnabled(uint32_t wp_index) {
722   Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS));
723   LLDB_LOG(log, "wp_index: {0}", wp_index);
724 
725   if ((m_hwp_regs[wp_index].control & 0x1) == 0x1)
726     return true;
727   else
728     return false;
729 }
730 
731 Status NativeRegisterContextLinux_arm64::GetWatchpointHitIndex(
732     uint32_t &wp_index, lldb::addr_t trap_addr) {
733   Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS));
734   LLDB_LOG(log, "wp_index: {0}, trap_addr: {1:x}", wp_index, trap_addr);
735 
736   uint32_t watch_size;
737   lldb::addr_t watch_addr;
738 
739   for (wp_index = 0; wp_index < m_max_hwp_supported; ++wp_index) {
740     watch_size = GetWatchpointSize(wp_index);
741     watch_addr = m_hwp_regs[wp_index].address;
742 
743     if (WatchpointIsEnabled(wp_index) && trap_addr >= watch_addr &&
744         trap_addr < watch_addr + watch_size) {
745       m_hwp_regs[wp_index].hit_addr = trap_addr;
746       return Status();
747     }
748   }
749 
750   wp_index = LLDB_INVALID_INDEX32;
751   return Status();
752 }
753 
754 lldb::addr_t
755 NativeRegisterContextLinux_arm64::GetWatchpointAddress(uint32_t wp_index) {
756   Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS));
757   LLDB_LOG(log, "wp_index: {0}", wp_index);
758 
759   if (wp_index >= m_max_hwp_supported)
760     return LLDB_INVALID_ADDRESS;
761 
762   if (WatchpointIsEnabled(wp_index))
763     return m_hwp_regs[wp_index].real_addr;
764   else
765     return LLDB_INVALID_ADDRESS;
766 }
767 
768 lldb::addr_t
769 NativeRegisterContextLinux_arm64::GetWatchpointHitAddress(uint32_t wp_index) {
770   Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS));
771   LLDB_LOG(log, "wp_index: {0}", wp_index);
772 
773   if (wp_index >= m_max_hwp_supported)
774     return LLDB_INVALID_ADDRESS;
775 
776   if (WatchpointIsEnabled(wp_index))
777     return m_hwp_regs[wp_index].hit_addr;
778   else
779     return LLDB_INVALID_ADDRESS;
780 }
781 
782 Status NativeRegisterContextLinux_arm64::ReadHardwareDebugInfo() {
783   if (!m_refresh_hwdebug_info) {
784     return Status();
785   }
786 
787   ::pid_t tid = m_thread.GetID();
788 
789   int regset = NT_ARM_HW_WATCH;
790   struct iovec ioVec;
791   struct user_hwdebug_state dreg_state;
792   Status error;
793 
794   ioVec.iov_base = &dreg_state;
795   ioVec.iov_len = sizeof(dreg_state);
796   error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, tid, &regset,
797                                             &ioVec, ioVec.iov_len);
798 
799   if (error.Fail())
800     return error;
801 
802   m_max_hwp_supported = dreg_state.dbg_info & 0xff;
803 
804   regset = NT_ARM_HW_BREAK;
805   error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, tid, &regset,
806                                             &ioVec, ioVec.iov_len);
807 
808   if (error.Fail())
809     return error;
810 
811   m_max_hbp_supported = dreg_state.dbg_info & 0xff;
812   m_refresh_hwdebug_info = false;
813 
814   return error;
815 }
816 
817 Status NativeRegisterContextLinux_arm64::WriteHardwareDebugRegs(int hwbType) {
818   struct iovec ioVec;
819   struct user_hwdebug_state dreg_state;
820   Status error;
821 
822   memset(&dreg_state, 0, sizeof(dreg_state));
823   ioVec.iov_base = &dreg_state;
824 
825   if (hwbType == eDREGTypeWATCH) {
826     hwbType = NT_ARM_HW_WATCH;
827     ioVec.iov_len = sizeof(dreg_state.dbg_info) + sizeof(dreg_state.pad) +
828                     (sizeof(dreg_state.dbg_regs[0]) * m_max_hwp_supported);
829 
830     for (uint32_t i = 0; i < m_max_hwp_supported; i++) {
831       dreg_state.dbg_regs[i].addr = m_hwp_regs[i].address;
832       dreg_state.dbg_regs[i].ctrl = m_hwp_regs[i].control;
833     }
834   } else {
835     hwbType = NT_ARM_HW_BREAK;
836     ioVec.iov_len = sizeof(dreg_state.dbg_info) + sizeof(dreg_state.pad) +
837                     (sizeof(dreg_state.dbg_regs[0]) * m_max_hbp_supported);
838 
839     for (uint32_t i = 0; i < m_max_hbp_supported; i++) {
840       dreg_state.dbg_regs[i].addr = m_hbr_regs[i].address;
841       dreg_state.dbg_regs[i].ctrl = m_hbr_regs[i].control;
842     }
843   }
844 
845   return NativeProcessLinux::PtraceWrapper(PTRACE_SETREGSET, m_thread.GetID(),
846                                            &hwbType, &ioVec, ioVec.iov_len);
847 }
848 
849 Status NativeRegisterContextLinux_arm64::DoReadRegisterValue(
850     uint32_t offset, const char *reg_name, uint32_t size,
851     RegisterValue &value) {
852   Status error;
853   if (offset > sizeof(struct user_pt_regs)) {
854     offset -= sizeof(struct user_pt_regs);
855     if (offset > sizeof(struct user_fpsimd_state)) {
856       error.SetErrorString("invalid offset value");
857       return error;
858     }
859     elf_fpregset_t regs;
860     int regset = NT_FPREGSET;
861     struct iovec ioVec;
862 
863     ioVec.iov_base = &regs;
864     ioVec.iov_len = sizeof regs;
865     error = NativeProcessLinux::PtraceWrapper(
866         PTRACE_GETREGSET, m_thread.GetID(), &regset, &ioVec, sizeof regs);
867     if (error.Success()) {
868       value.SetBytes((void *)(((unsigned char *)(&regs)) + offset), 16,
869                      m_thread.GetProcess().GetByteOrder());
870     }
871   } else {
872     elf_gregset_t regs;
873     int regset = NT_PRSTATUS;
874     struct iovec ioVec;
875 
876     ioVec.iov_base = &regs;
877     ioVec.iov_len = sizeof regs;
878     error = NativeProcessLinux::PtraceWrapper(
879         PTRACE_GETREGSET, m_thread.GetID(), &regset, &ioVec, sizeof regs);
880     if (error.Success()) {
881       value.SetBytes((void *)(((unsigned char *)(regs)) + offset), 8,
882                      m_thread.GetProcess().GetByteOrder());
883     }
884   }
885   return error;
886 }
887 
888 Status NativeRegisterContextLinux_arm64::DoWriteRegisterValue(
889     uint32_t offset, const char *reg_name, const RegisterValue &value) {
890   Status error;
891   ::pid_t tid = m_thread.GetID();
892   if (offset > sizeof(struct user_pt_regs)) {
893     offset -= sizeof(struct user_pt_regs);
894     if (offset > sizeof(struct user_fpsimd_state)) {
895       error.SetErrorString("invalid offset value");
896       return error;
897     }
898     elf_fpregset_t regs;
899     int regset = NT_FPREGSET;
900     struct iovec ioVec;
901 
902     ioVec.iov_base = &regs;
903     ioVec.iov_len = sizeof regs;
904     error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, tid, &regset,
905                                               &ioVec, sizeof regs);
906 
907     if (error.Success()) {
908       ::memcpy((void *)(((unsigned char *)(&regs)) + offset), value.GetBytes(),
909                16);
910       error = NativeProcessLinux::PtraceWrapper(PTRACE_SETREGSET, tid, &regset,
911                                                 &ioVec, sizeof regs);
912     }
913   } else {
914     elf_gregset_t regs;
915     int regset = NT_PRSTATUS;
916     struct iovec ioVec;
917 
918     ioVec.iov_base = &regs;
919     ioVec.iov_len = sizeof regs;
920     error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, tid, &regset,
921                                               &ioVec, sizeof regs);
922     if (error.Success()) {
923       ::memcpy((void *)(((unsigned char *)(&regs)) + offset), value.GetBytes(),
924                8);
925       error = NativeProcessLinux::PtraceWrapper(PTRACE_SETREGSET, tid, &regset,
926                                                 &ioVec, sizeof regs);
927     }
928   }
929   return error;
930 }
931 
932 Status NativeRegisterContextLinux_arm64::DoReadGPR(void *buf, size_t buf_size) {
933   int regset = NT_PRSTATUS;
934   struct iovec ioVec;
935   Status error;
936 
937   ioVec.iov_base = buf;
938   ioVec.iov_len = buf_size;
939   return NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, m_thread.GetID(),
940                                            &regset, &ioVec, buf_size);
941 }
942 
943 Status NativeRegisterContextLinux_arm64::DoWriteGPR(void *buf,
944                                                     size_t buf_size) {
945   int regset = NT_PRSTATUS;
946   struct iovec ioVec;
947   Status error;
948 
949   ioVec.iov_base = buf;
950   ioVec.iov_len = buf_size;
951   return NativeProcessLinux::PtraceWrapper(PTRACE_SETREGSET, m_thread.GetID(),
952                                            &regset, &ioVec, buf_size);
953 }
954 
955 Status NativeRegisterContextLinux_arm64::DoReadFPR(void *buf, size_t buf_size) {
956   int regset = NT_FPREGSET;
957   struct iovec ioVec;
958   Status error;
959 
960   ioVec.iov_base = buf;
961   ioVec.iov_len = buf_size;
962   return NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, m_thread.GetID(),
963                                            &regset, &ioVec, buf_size);
964 }
965 
966 Status NativeRegisterContextLinux_arm64::DoWriteFPR(void *buf,
967                                                     size_t buf_size) {
968   int regset = NT_FPREGSET;
969   struct iovec ioVec;
970   Status error;
971 
972   ioVec.iov_base = buf;
973   ioVec.iov_len = buf_size;
974   return NativeProcessLinux::PtraceWrapper(PTRACE_SETREGSET, m_thread.GetID(),
975                                            &regset, &ioVec, buf_size);
976 }
977 
978 uint32_t NativeRegisterContextLinux_arm64::CalculateFprOffset(
979     const RegisterInfo *reg_info) const {
980   return reg_info->byte_offset -
981          GetRegisterInfoAtIndex(m_reg_info.first_fpr)->byte_offset;
982 }
983 
984 #endif // defined (__arm64__) || defined (__aarch64__)
985