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