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