1 //===-- NativeRegisterContextFreeBSD_arm64.cpp ----------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #if defined(__aarch64__)
10 
11 #include "NativeRegisterContextFreeBSD_arm64.h"
12 
13 #include "lldb/Utility/DataBufferHeap.h"
14 #include "lldb/Utility/RegisterValue.h"
15 #include "lldb/Utility/Status.h"
16 
17 #include "Plugins/Process/FreeBSD/NativeProcessFreeBSD.h"
18 #include "Plugins/Process/POSIX/ProcessPOSIXLog.h"
19 #include "Plugins/Process/Utility/RegisterFlagsDetector_arm64.h"
20 #include "Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h"
21 
22 // clang-format off
23 #include <sys/param.h>
24 #include <sys/ptrace.h>
25 #include <sys/types.h>
26 // clang-format on
27 
28 using namespace lldb;
29 using namespace lldb_private;
30 using namespace lldb_private::process_freebsd;
31 
32 // A NativeRegisterContext is constructed per thread, but all threads' registers
33 // will contain the same fields. Therefore this mutex prevents each instance
34 // competing with the other, and subsequent instances from having to detect the
35 // fields all over again.
36 static std::mutex g_register_flags_detector_mutex;
37 static Arm64RegisterFlagsDetector g_register_flags_detector;
38 
39 NativeRegisterContextFreeBSD *
40 NativeRegisterContextFreeBSD::CreateHostNativeRegisterContextFreeBSD(
41     const ArchSpec &target_arch, NativeThreadFreeBSD &native_thread) {
42   std::lock_guard<std::mutex> lock(g_register_flags_detector_mutex);
43   if (!g_register_flags_detector.HasDetected()) {
44     NativeProcessFreeBSD &process = native_thread.GetProcess();
45     g_register_flags_detector.DetectFields(
46         process.GetAuxValue(AuxVector::AUXV_FREEBSD_AT_HWCAP).value_or(0),
47         process.GetAuxValue(AuxVector::AUXV_AT_HWCAP2).value_or(0));
48   }
49 
50   return new NativeRegisterContextFreeBSD_arm64(target_arch, native_thread);
51 }
52 
53 NativeRegisterContextFreeBSD_arm64::NativeRegisterContextFreeBSD_arm64(
54     const ArchSpec &target_arch, NativeThreadFreeBSD &native_thread)
55     : NativeRegisterContextRegisterInfo(
56           native_thread, new RegisterInfoPOSIX_arm64(target_arch, 0))
57 #ifdef LLDB_HAS_FREEBSD_WATCHPOINT
58       ,
59       m_read_dbreg(false)
60 #endif
61 {
62   g_register_flags_detector.UpdateRegisterInfo(
63       GetRegisterInfoInterface().GetRegisterInfo(),
64       GetRegisterInfoInterface().GetRegisterCount());
65 
66   ::memset(&m_hwp_regs, 0, sizeof(m_hwp_regs));
67   ::memset(&m_hbp_regs, 0, sizeof(m_hbp_regs));
68 }
69 
70 RegisterInfoPOSIX_arm64 &
71 NativeRegisterContextFreeBSD_arm64::GetRegisterInfo() const {
72   return static_cast<RegisterInfoPOSIX_arm64 &>(*m_register_info_interface_up);
73 }
74 
75 uint32_t NativeRegisterContextFreeBSD_arm64::GetRegisterSetCount() const {
76   return GetRegisterInfo().GetRegisterSetCount();
77 }
78 
79 const RegisterSet *
80 NativeRegisterContextFreeBSD_arm64::GetRegisterSet(uint32_t set_index) const {
81   return GetRegisterInfo().GetRegisterSet(set_index);
82 }
83 
84 uint32_t NativeRegisterContextFreeBSD_arm64::GetUserRegisterCount() const {
85   uint32_t count = 0;
86   for (uint32_t set_index = 0; set_index < GetRegisterSetCount(); ++set_index)
87     count += GetRegisterSet(set_index)->num_registers;
88   return count;
89 }
90 
91 Status NativeRegisterContextFreeBSD_arm64::ReadRegisterSet(uint32_t set) {
92   switch (set) {
93   case RegisterInfoPOSIX_arm64::GPRegSet:
94     return NativeProcessFreeBSD::PtraceWrapper(PT_GETREGS, m_thread.GetID(),
95                                                m_reg_data.data());
96   case RegisterInfoPOSIX_arm64::FPRegSet:
97     return NativeProcessFreeBSD::PtraceWrapper(
98         PT_GETFPREGS, m_thread.GetID(),
99         m_reg_data.data() + sizeof(RegisterInfoPOSIX_arm64::GPR));
100   }
101   llvm_unreachable("NativeRegisterContextFreeBSD_arm64::ReadRegisterSet");
102 }
103 
104 Status NativeRegisterContextFreeBSD_arm64::WriteRegisterSet(uint32_t set) {
105   switch (set) {
106   case RegisterInfoPOSIX_arm64::GPRegSet:
107     return NativeProcessFreeBSD::PtraceWrapper(PT_SETREGS, m_thread.GetID(),
108                                                m_reg_data.data());
109   case RegisterInfoPOSIX_arm64::FPRegSet:
110     return NativeProcessFreeBSD::PtraceWrapper(
111         PT_SETFPREGS, m_thread.GetID(),
112         m_reg_data.data() + sizeof(RegisterInfoPOSIX_arm64::GPR));
113   }
114   llvm_unreachable("NativeRegisterContextFreeBSD_arm64::WriteRegisterSet");
115 }
116 
117 Status
118 NativeRegisterContextFreeBSD_arm64::ReadRegister(const RegisterInfo *reg_info,
119                                                  RegisterValue &reg_value) {
120   Status error;
121 
122   if (!reg_info) {
123     error.SetErrorString("reg_info NULL");
124     return error;
125   }
126 
127   const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
128 
129   if (reg == LLDB_INVALID_REGNUM)
130     return Status("no lldb regnum for %s", reg_info && reg_info->name
131                                                ? reg_info->name
132                                                : "<unknown register>");
133 
134   uint32_t set = GetRegisterInfo().GetRegisterSetFromRegisterIndex(reg);
135   error = ReadRegisterSet(set);
136   if (error.Fail())
137     return error;
138 
139   assert(reg_info->byte_offset + reg_info->byte_size <= m_reg_data.size());
140   reg_value.SetBytes(m_reg_data.data() + reg_info->byte_offset,
141                      reg_info->byte_size, endian::InlHostByteOrder());
142   return error;
143 }
144 
145 Status NativeRegisterContextFreeBSD_arm64::WriteRegister(
146     const RegisterInfo *reg_info, const RegisterValue &reg_value) {
147   Status error;
148 
149   if (!reg_info)
150     return Status("reg_info NULL");
151 
152   const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
153 
154   if (reg == LLDB_INVALID_REGNUM)
155     return Status("no lldb regnum for %s", reg_info && reg_info->name
156                                                ? reg_info->name
157                                                : "<unknown register>");
158 
159   uint32_t set = GetRegisterInfo().GetRegisterSetFromRegisterIndex(reg);
160   error = ReadRegisterSet(set);
161   if (error.Fail())
162     return error;
163 
164   assert(reg_info->byte_offset + reg_info->byte_size <= m_reg_data.size());
165   ::memcpy(m_reg_data.data() + reg_info->byte_offset, reg_value.GetBytes(),
166            reg_info->byte_size);
167 
168   return WriteRegisterSet(set);
169 }
170 
171 Status NativeRegisterContextFreeBSD_arm64::ReadAllRegisterValues(
172     lldb::WritableDataBufferSP &data_sp) {
173   Status error;
174 
175   error = ReadRegisterSet(RegisterInfoPOSIX_arm64::GPRegSet);
176   if (error.Fail())
177     return error;
178 
179   error = ReadRegisterSet(RegisterInfoPOSIX_arm64::FPRegSet);
180   if (error.Fail())
181     return error;
182 
183   data_sp.reset(new DataBufferHeap(m_reg_data.size(), 0));
184   uint8_t *dst = data_sp->GetBytes();
185   ::memcpy(dst, m_reg_data.data(), m_reg_data.size());
186 
187   return error;
188 }
189 
190 Status NativeRegisterContextFreeBSD_arm64::WriteAllRegisterValues(
191     const lldb::DataBufferSP &data_sp) {
192   Status error;
193 
194   if (!data_sp) {
195     error.SetErrorStringWithFormat(
196         "NativeRegisterContextFreeBSD_arm64::%s invalid data_sp provided",
197         __FUNCTION__);
198     return error;
199   }
200 
201   if (data_sp->GetByteSize() != m_reg_data.size()) {
202     error.SetErrorStringWithFormat(
203         "NativeRegisterContextFreeBSD_arm64::%s data_sp contained mismatched "
204         "data size, expected %" PRIu64 ", actual %" PRIu64,
205         __FUNCTION__, m_reg_data.size(), data_sp->GetByteSize());
206     return error;
207   }
208 
209   const uint8_t *src = data_sp->GetBytes();
210   if (src == nullptr) {
211     error.SetErrorStringWithFormat("NativeRegisterContextFreeBSD_arm64::%s "
212                                    "DataBuffer::GetBytes() returned a null "
213                                    "pointer",
214                                    __FUNCTION__);
215     return error;
216   }
217   ::memcpy(m_reg_data.data(), src, m_reg_data.size());
218 
219   error = WriteRegisterSet(RegisterInfoPOSIX_arm64::GPRegSet);
220   if (error.Fail())
221     return error;
222 
223   return WriteRegisterSet(RegisterInfoPOSIX_arm64::FPRegSet);
224 }
225 
226 llvm::Error NativeRegisterContextFreeBSD_arm64::CopyHardwareWatchpointsFrom(
227     NativeRegisterContextFreeBSD &source) {
228 #ifdef LLDB_HAS_FREEBSD_WATCHPOINT
229   auto &r_source = static_cast<NativeRegisterContextFreeBSD_arm64 &>(source);
230   llvm::Error error = r_source.ReadHardwareDebugInfo();
231   if (error)
232     return error;
233 
234   m_dbreg = r_source.m_dbreg;
235   m_hbp_regs = r_source.m_hbp_regs;
236   m_hwp_regs = r_source.m_hwp_regs;
237   m_max_hbp_supported = r_source.m_max_hbp_supported;
238   m_max_hwp_supported = r_source.m_max_hwp_supported;
239   m_read_dbreg = true;
240 
241   // on FreeBSD this writes both breakpoints and watchpoints
242   return WriteHardwareDebugRegs(eDREGTypeWATCH);
243 #else
244   return llvm::Error::success();
245 #endif
246 }
247 
248 llvm::Error NativeRegisterContextFreeBSD_arm64::ReadHardwareDebugInfo() {
249 #ifdef LLDB_HAS_FREEBSD_WATCHPOINT
250   Log *log = GetLog(POSIXLog::Registers);
251 
252   // we're fully stateful, so no need to reread control registers ever
253   if (m_read_dbreg)
254     return llvm::Error::success();
255 
256   Status res = NativeProcessFreeBSD::PtraceWrapper(PT_GETDBREGS,
257                                                    m_thread.GetID(), &m_dbreg);
258   if (res.Fail())
259     return res.ToError();
260 
261   LLDB_LOG(log, "m_dbreg read: debug_ver={0}, nbkpts={1}, nwtpts={2}",
262            m_dbreg.db_debug_ver, m_dbreg.db_nbkpts, m_dbreg.db_nwtpts);
263   m_max_hbp_supported = m_dbreg.db_nbkpts;
264   m_max_hwp_supported = m_dbreg.db_nwtpts;
265   assert(m_max_hbp_supported <= m_hbp_regs.size());
266   assert(m_max_hwp_supported <= m_hwp_regs.size());
267 
268   m_read_dbreg = true;
269   return llvm::Error::success();
270 #else
271   return llvm::createStringError(
272       llvm::inconvertibleErrorCode(),
273       "Hardware breakpoints/watchpoints require FreeBSD 14.0");
274 #endif
275 }
276 
277 llvm::Error
278 NativeRegisterContextFreeBSD_arm64::WriteHardwareDebugRegs(DREGType) {
279 #ifdef LLDB_HAS_FREEBSD_WATCHPOINT
280   assert(m_read_dbreg && "dbregs must be read before writing them back");
281 
282   // copy data from m_*_regs to m_dbreg before writing it back
283   for (uint32_t i = 0; i < m_max_hbp_supported; i++) {
284     m_dbreg.db_breakregs[i].dbr_addr = m_hbp_regs[i].address;
285     m_dbreg.db_breakregs[i].dbr_ctrl = m_hbp_regs[i].control;
286   }
287   for (uint32_t i = 0; i < m_max_hwp_supported; i++) {
288     m_dbreg.db_watchregs[i].dbw_addr = m_hwp_regs[i].address;
289     m_dbreg.db_watchregs[i].dbw_ctrl = m_hwp_regs[i].control;
290   }
291 
292   return NativeProcessFreeBSD::PtraceWrapper(PT_SETDBREGS, m_thread.GetID(),
293                                              &m_dbreg)
294       .ToError();
295 #else
296   return llvm::createStringError(
297       llvm::inconvertibleErrorCode(),
298       "Hardware breakpoints/watchpoints require FreeBSD 14.0");
299 #endif
300 }
301 
302 #endif // defined (__aarch64__)
303