1 | //===-- NativeRegisterContextLinux_ppc64le.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 | // This implementation is related to the OpenPOWER ABI for Power Architecture |
10 | // 64-bit ELF V2 ABI |
11 | |
12 | #if defined(__powerpc64__) |
13 | |
14 | #include "NativeRegisterContextLinux_ppc64le.h" |
15 | |
16 | #include "lldb/Host/HostInfo.h" |
17 | #include "lldb/Host/common/NativeProcessProtocol.h" |
18 | #include "lldb/Utility/DataBufferHeap.h" |
19 | #include "lldb/Utility/Log.h" |
20 | #include "lldb/Utility/RegisterValue.h" |
21 | #include "lldb/Utility/Status.h" |
22 | |
23 | #include "Plugins/Process/Linux/NativeProcessLinux.h" |
24 | #include "Plugins/Process/Linux/Procfs.h" |
25 | #include "Plugins/Process/POSIX/ProcessPOSIXLog.h" |
26 | #include "Plugins/Process/Utility/RegisterInfoPOSIX_ppc64le.h" |
27 | |
28 | // System includes - They have to be included after framework includes because |
29 | // they define some macros which collide with variable names in other modules |
30 | #include <sys/socket.h> |
31 | #include <elf.h> |
32 | #include <asm/ptrace.h> |
33 | |
34 | #define REG_CONTEXT_SIZE \ |
35 | (GetGPRSize() + GetFPRSize() + sizeof(m_vmx_ppc64le) + sizeof(m_vsx_ppc64le)) |
36 | using namespace lldb; |
37 | using namespace lldb_private; |
38 | using namespace lldb_private::process_linux; |
39 | |
40 | static const uint32_t g_gpr_regnums_ppc64le[] = { |
41 | gpr_r0_ppc64le, gpr_r1_ppc64le, gpr_r2_ppc64le, gpr_r3_ppc64le, |
42 | gpr_r4_ppc64le, gpr_r5_ppc64le, gpr_r6_ppc64le, gpr_r7_ppc64le, |
43 | gpr_r8_ppc64le, gpr_r9_ppc64le, gpr_r10_ppc64le, gpr_r11_ppc64le, |
44 | gpr_r12_ppc64le, gpr_r13_ppc64le, gpr_r14_ppc64le, gpr_r15_ppc64le, |
45 | gpr_r16_ppc64le, gpr_r17_ppc64le, gpr_r18_ppc64le, gpr_r19_ppc64le, |
46 | gpr_r20_ppc64le, gpr_r21_ppc64le, gpr_r22_ppc64le, gpr_r23_ppc64le, |
47 | gpr_r24_ppc64le, gpr_r25_ppc64le, gpr_r26_ppc64le, gpr_r27_ppc64le, |
48 | gpr_r28_ppc64le, gpr_r29_ppc64le, gpr_r30_ppc64le, gpr_r31_ppc64le, |
49 | gpr_pc_ppc64le, gpr_msr_ppc64le, gpr_origr3_ppc64le, gpr_ctr_ppc64le, |
50 | gpr_lr_ppc64le, gpr_xer_ppc64le, gpr_cr_ppc64le, gpr_softe_ppc64le, |
51 | gpr_trap_ppc64le, |
52 | LLDB_INVALID_REGNUM // register sets need to end with this flag |
53 | }; |
54 | |
55 | static const uint32_t g_fpr_regnums_ppc64le[] = { |
56 | fpr_f0_ppc64le, fpr_f1_ppc64le, fpr_f2_ppc64le, fpr_f3_ppc64le, |
57 | fpr_f4_ppc64le, fpr_f5_ppc64le, fpr_f6_ppc64le, fpr_f7_ppc64le, |
58 | fpr_f8_ppc64le, fpr_f9_ppc64le, fpr_f10_ppc64le, fpr_f11_ppc64le, |
59 | fpr_f12_ppc64le, fpr_f13_ppc64le, fpr_f14_ppc64le, fpr_f15_ppc64le, |
60 | fpr_f16_ppc64le, fpr_f17_ppc64le, fpr_f18_ppc64le, fpr_f19_ppc64le, |
61 | fpr_f20_ppc64le, fpr_f21_ppc64le, fpr_f22_ppc64le, fpr_f23_ppc64le, |
62 | fpr_f24_ppc64le, fpr_f25_ppc64le, fpr_f26_ppc64le, fpr_f27_ppc64le, |
63 | fpr_f28_ppc64le, fpr_f29_ppc64le, fpr_f30_ppc64le, fpr_f31_ppc64le, |
64 | fpr_fpscr_ppc64le, |
65 | LLDB_INVALID_REGNUM // register sets need to end with this flag |
66 | }; |
67 | |
68 | static const uint32_t g_vmx_regnums_ppc64le[] = { |
69 | vmx_vr0_ppc64le, vmx_vr1_ppc64le, vmx_vr2_ppc64le, vmx_vr3_ppc64le, |
70 | vmx_vr4_ppc64le, vmx_vr5_ppc64le, vmx_vr6_ppc64le, vmx_vr7_ppc64le, |
71 | vmx_vr8_ppc64le, vmx_vr9_ppc64le, vmx_vr10_ppc64le, vmx_vr11_ppc64le, |
72 | vmx_vr12_ppc64le, vmx_vr13_ppc64le, vmx_vr14_ppc64le, vmx_vr15_ppc64le, |
73 | vmx_vr16_ppc64le, vmx_vr17_ppc64le, vmx_vr18_ppc64le, vmx_vr19_ppc64le, |
74 | vmx_vr20_ppc64le, vmx_vr21_ppc64le, vmx_vr22_ppc64le, vmx_vr23_ppc64le, |
75 | vmx_vr24_ppc64le, vmx_vr25_ppc64le, vmx_vr26_ppc64le, vmx_vr27_ppc64le, |
76 | vmx_vr28_ppc64le, vmx_vr29_ppc64le, vmx_vr30_ppc64le, vmx_vr31_ppc64le, |
77 | vmx_vscr_ppc64le, vmx_vrsave_ppc64le, |
78 | LLDB_INVALID_REGNUM // register sets need to end with this flag |
79 | }; |
80 | |
81 | static const uint32_t g_vsx_regnums_ppc64le[] = { |
82 | vsx_vs0_ppc64le, vsx_vs1_ppc64le, vsx_vs2_ppc64le, vsx_vs3_ppc64le, |
83 | vsx_vs4_ppc64le, vsx_vs5_ppc64le, vsx_vs6_ppc64le, vsx_vs7_ppc64le, |
84 | vsx_vs8_ppc64le, vsx_vs9_ppc64le, vsx_vs10_ppc64le, vsx_vs11_ppc64le, |
85 | vsx_vs12_ppc64le, vsx_vs13_ppc64le, vsx_vs14_ppc64le, vsx_vs15_ppc64le, |
86 | vsx_vs16_ppc64le, vsx_vs17_ppc64le, vsx_vs18_ppc64le, vsx_vs19_ppc64le, |
87 | vsx_vs20_ppc64le, vsx_vs21_ppc64le, vsx_vs22_ppc64le, vsx_vs23_ppc64le, |
88 | vsx_vs24_ppc64le, vsx_vs25_ppc64le, vsx_vs26_ppc64le, vsx_vs27_ppc64le, |
89 | vsx_vs28_ppc64le, vsx_vs29_ppc64le, vsx_vs30_ppc64le, vsx_vs31_ppc64le, |
90 | vsx_vs32_ppc64le, vsx_vs33_ppc64le, vsx_vs34_ppc64le, vsx_vs35_ppc64le, |
91 | vsx_vs36_ppc64le, vsx_vs37_ppc64le, vsx_vs38_ppc64le, vsx_vs39_ppc64le, |
92 | vsx_vs40_ppc64le, vsx_vs41_ppc64le, vsx_vs42_ppc64le, vsx_vs43_ppc64le, |
93 | vsx_vs44_ppc64le, vsx_vs45_ppc64le, vsx_vs46_ppc64le, vsx_vs47_ppc64le, |
94 | vsx_vs48_ppc64le, vsx_vs49_ppc64le, vsx_vs50_ppc64le, vsx_vs51_ppc64le, |
95 | vsx_vs52_ppc64le, vsx_vs53_ppc64le, vsx_vs54_ppc64le, vsx_vs55_ppc64le, |
96 | vsx_vs56_ppc64le, vsx_vs57_ppc64le, vsx_vs58_ppc64le, vsx_vs59_ppc64le, |
97 | vsx_vs60_ppc64le, vsx_vs61_ppc64le, vsx_vs62_ppc64le, vsx_vs63_ppc64le, |
98 | LLDB_INVALID_REGNUM // register sets need to end with this flag |
99 | }; |
100 | |
101 | // Number of register sets provided by this context. |
102 | static constexpr int k_num_register_sets = 4; |
103 | |
104 | static const RegisterSet g_reg_sets_ppc64le[k_num_register_sets] = { |
105 | {"General Purpose Registers" , "gpr" , k_num_gpr_registers_ppc64le, |
106 | g_gpr_regnums_ppc64le}, |
107 | {"Floating Point Registers" , "fpr" , k_num_fpr_registers_ppc64le, |
108 | g_fpr_regnums_ppc64le}, |
109 | {"AltiVec/VMX Registers" , "vmx" , k_num_vmx_registers_ppc64le, |
110 | g_vmx_regnums_ppc64le}, |
111 | {"VSX Registers" , "vsx" , k_num_vsx_registers_ppc64le, |
112 | g_vsx_regnums_ppc64le}, |
113 | }; |
114 | |
115 | std::unique_ptr<NativeRegisterContextLinux> |
116 | NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux( |
117 | const ArchSpec &target_arch, NativeThreadLinux &native_thread) { |
118 | switch (target_arch.GetMachine()) { |
119 | case llvm::Triple::ppc64le: |
120 | return std::make_unique<NativeRegisterContextLinux_ppc64le>(target_arch, |
121 | native_thread); |
122 | default: |
123 | llvm_unreachable("have no register context for architecture" ); |
124 | } |
125 | } |
126 | |
127 | llvm::Expected<ArchSpec> |
128 | NativeRegisterContextLinux::DetermineArchitecture(lldb::tid_t tid) { |
129 | return HostInfo::GetArchitecture(); |
130 | } |
131 | |
132 | NativeRegisterContextLinux_ppc64le::NativeRegisterContextLinux_ppc64le( |
133 | const ArchSpec &target_arch, NativeThreadProtocol &native_thread) |
134 | : NativeRegisterContextRegisterInfo( |
135 | native_thread, new RegisterInfoPOSIX_ppc64le(target_arch)), |
136 | NativeRegisterContextLinux(native_thread) { |
137 | if (target_arch.GetMachine() != llvm::Triple::ppc64le) { |
138 | llvm_unreachable("Unhandled target architecture." ); |
139 | } |
140 | |
141 | ::memset(&m_gpr_ppc64le, 0, sizeof(m_gpr_ppc64le)); |
142 | ::memset(&m_fpr_ppc64le, 0, sizeof(m_fpr_ppc64le)); |
143 | ::memset(&m_vmx_ppc64le, 0, sizeof(m_vmx_ppc64le)); |
144 | ::memset(&m_vsx_ppc64le, 0, sizeof(m_vsx_ppc64le)); |
145 | ::memset(&m_hwp_regs, 0, sizeof(m_hwp_regs)); |
146 | } |
147 | |
148 | uint32_t NativeRegisterContextLinux_ppc64le::GetRegisterSetCount() const { |
149 | return k_num_register_sets; |
150 | } |
151 | |
152 | const RegisterSet * |
153 | NativeRegisterContextLinux_ppc64le::GetRegisterSet(uint32_t set_index) const { |
154 | if (set_index < k_num_register_sets) |
155 | return &g_reg_sets_ppc64le[set_index]; |
156 | |
157 | return nullptr; |
158 | } |
159 | |
160 | uint32_t NativeRegisterContextLinux_ppc64le::GetUserRegisterCount() const { |
161 | uint32_t count = 0; |
162 | for (uint32_t set_index = 0; set_index < k_num_register_sets; ++set_index) |
163 | count += g_reg_sets_ppc64le[set_index].num_registers; |
164 | return count; |
165 | } |
166 | |
167 | Status NativeRegisterContextLinux_ppc64le::ReadRegister( |
168 | const RegisterInfo *reg_info, RegisterValue ®_value) { |
169 | Status error; |
170 | |
171 | if (!reg_info) { |
172 | error.SetErrorString("reg_info NULL" ); |
173 | return error; |
174 | } |
175 | |
176 | const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; |
177 | |
178 | if (IsFPR(reg)) { |
179 | error = ReadFPR(); |
180 | if (error.Fail()) |
181 | return error; |
182 | |
183 | // Get pointer to m_fpr_ppc64le variable and set the data from it. |
184 | uint32_t fpr_offset = CalculateFprOffset(reg_info); |
185 | assert(fpr_offset < sizeof m_fpr_ppc64le); |
186 | uint8_t *src = (uint8_t *)&m_fpr_ppc64le + fpr_offset; |
187 | reg_value.SetFromMemoryData(*reg_info, src, reg_info->byte_size, |
188 | eByteOrderLittle, error); |
189 | } else if (IsVSX(reg)) { |
190 | uint32_t vsx_offset = CalculateVsxOffset(reg_info); |
191 | assert(vsx_offset < sizeof(m_vsx_ppc64le)); |
192 | |
193 | if (vsx_offset < sizeof(m_vsx_ppc64le) / 2) { |
194 | error = ReadVSX(); |
195 | if (error.Fail()) |
196 | return error; |
197 | |
198 | error = ReadFPR(); |
199 | if (error.Fail()) |
200 | return error; |
201 | |
202 | uint64_t value[2]; |
203 | uint8_t *dst, *src; |
204 | dst = (uint8_t *)&value; |
205 | src = (uint8_t *)&m_vsx_ppc64le + vsx_offset / 2; |
206 | ::memcpy(dst, src, 8); |
207 | dst += 8; |
208 | src = (uint8_t *)&m_fpr_ppc64le + vsx_offset / 2; |
209 | ::memcpy(dst, src, 8); |
210 | reg_value.SetFromMemoryData(*reg_info, &value, reg_info->byte_size, |
211 | eByteOrderLittle, error); |
212 | } else { |
213 | error = ReadVMX(); |
214 | if (error.Fail()) |
215 | return error; |
216 | |
217 | // Get pointer to m_vmx_ppc64le variable and set the data from it. |
218 | uint32_t vmx_offset = vsx_offset - sizeof(m_vsx_ppc64le) / 2; |
219 | uint8_t *src = (uint8_t *)&m_vmx_ppc64le + vmx_offset; |
220 | reg_value.SetFromMemoryData(*reg_info, src, reg_info->byte_size, |
221 | eByteOrderLittle, error); |
222 | } |
223 | } else if (IsVMX(reg)) { |
224 | error = ReadVMX(); |
225 | if (error.Fail()) |
226 | return error; |
227 | |
228 | // Get pointer to m_vmx_ppc64le variable and set the data from it. |
229 | uint32_t vmx_offset = CalculateVmxOffset(reg_info); |
230 | assert(vmx_offset < sizeof m_vmx_ppc64le); |
231 | uint8_t *src = (uint8_t *)&m_vmx_ppc64le + vmx_offset; |
232 | reg_value.SetFromMemoryData(*reg_info, src, reg_info->byte_size, |
233 | eByteOrderLittle, error); |
234 | } else if (IsGPR(reg)) { |
235 | error = ReadGPR(); |
236 | if (error.Fail()) |
237 | return error; |
238 | |
239 | uint8_t *src = (uint8_t *) &m_gpr_ppc64le + reg_info->byte_offset; |
240 | reg_value.SetFromMemoryData(*reg_info, src, reg_info->byte_size, |
241 | eByteOrderLittle, error); |
242 | } else { |
243 | return Status("failed - register wasn't recognized to be a GPR, FPR, VSX " |
244 | "or VMX, read strategy unknown" ); |
245 | } |
246 | |
247 | return error; |
248 | } |
249 | |
250 | Status NativeRegisterContextLinux_ppc64le::WriteRegister( |
251 | const RegisterInfo *reg_info, const RegisterValue ®_value) { |
252 | Status error; |
253 | if (!reg_info) |
254 | return Status("reg_info NULL" ); |
255 | |
256 | const uint32_t reg_index = reg_info->kinds[lldb::eRegisterKindLLDB]; |
257 | if (reg_index == LLDB_INVALID_REGNUM) |
258 | return Status("no lldb regnum for %s" , reg_info && reg_info->name |
259 | ? reg_info->name |
260 | : "<unknown register>" ); |
261 | |
262 | if (IsGPR(reg_index)) { |
263 | error = ReadGPR(); |
264 | if (error.Fail()) |
265 | return error; |
266 | |
267 | uint8_t *dst = (uint8_t *)&m_gpr_ppc64le + reg_info->byte_offset; |
268 | ::memcpy(dst, reg_value.GetBytes(), reg_value.GetByteSize()); |
269 | |
270 | error = WriteGPR(); |
271 | if (error.Fail()) |
272 | return error; |
273 | |
274 | return Status(); |
275 | } |
276 | |
277 | if (IsFPR(reg_index)) { |
278 | error = ReadFPR(); |
279 | if (error.Fail()) |
280 | return error; |
281 | |
282 | // Get pointer to m_fpr_ppc64le variable and set the data to it. |
283 | uint32_t fpr_offset = CalculateFprOffset(reg_info); |
284 | assert(fpr_offset < GetFPRSize()); |
285 | uint8_t *dst = (uint8_t *)&m_fpr_ppc64le + fpr_offset; |
286 | ::memcpy(dst, reg_value.GetBytes(), reg_value.GetByteSize()); |
287 | |
288 | error = WriteFPR(); |
289 | if (error.Fail()) |
290 | return error; |
291 | |
292 | return Status(); |
293 | } |
294 | |
295 | if (IsVMX(reg_index)) { |
296 | error = ReadVMX(); |
297 | if (error.Fail()) |
298 | return error; |
299 | |
300 | // Get pointer to m_vmx_ppc64le variable and set the data to it. |
301 | uint32_t vmx_offset = CalculateVmxOffset(reg_info); |
302 | assert(vmx_offset < sizeof(m_vmx_ppc64le)); |
303 | uint8_t *dst = (uint8_t *)&m_vmx_ppc64le + vmx_offset; |
304 | ::memcpy(dst, reg_value.GetBytes(), reg_value.GetByteSize()); |
305 | |
306 | error = WriteVMX(); |
307 | if (error.Fail()) |
308 | return error; |
309 | |
310 | return Status(); |
311 | } |
312 | |
313 | if (IsVSX(reg_index)) { |
314 | uint32_t vsx_offset = CalculateVsxOffset(reg_info); |
315 | assert(vsx_offset < sizeof(m_vsx_ppc64le)); |
316 | |
317 | if (vsx_offset < sizeof(m_vsx_ppc64le) / 2) { |
318 | error = ReadVSX(); |
319 | if (error.Fail()) |
320 | return error; |
321 | |
322 | error = ReadFPR(); |
323 | if (error.Fail()) |
324 | return error; |
325 | |
326 | uint64_t value[2]; |
327 | ::memcpy(value, reg_value.GetBytes(), 16); |
328 | uint8_t *dst, *src; |
329 | src = (uint8_t *)value; |
330 | dst = (uint8_t *)&m_vsx_ppc64le + vsx_offset / 2; |
331 | ::memcpy(dst, src, 8); |
332 | src += 8; |
333 | dst = (uint8_t *)&m_fpr_ppc64le + vsx_offset / 2; |
334 | ::memcpy(dst, src, 8); |
335 | |
336 | WriteVSX(); |
337 | WriteFPR(); |
338 | } else { |
339 | error = ReadVMX(); |
340 | if (error.Fail()) |
341 | return error; |
342 | |
343 | // Get pointer to m_vmx_ppc64le variable and set the data from it. |
344 | uint32_t vmx_offset = vsx_offset - sizeof(m_vsx_ppc64le) / 2; |
345 | uint8_t *dst = (uint8_t *)&m_vmx_ppc64le + vmx_offset; |
346 | ::memcpy(dst, reg_value.GetBytes(), reg_value.GetByteSize()); |
347 | WriteVMX(); |
348 | } |
349 | |
350 | return Status(); |
351 | } |
352 | |
353 | return Status("failed - register wasn't recognized to be a GPR, FPR, VSX " |
354 | "or VMX, write strategy unknown" ); |
355 | } |
356 | |
357 | Status NativeRegisterContextLinux_ppc64le::ReadAllRegisterValues( |
358 | lldb::WritableDataBufferSP &data_sp) { |
359 | Status error; |
360 | |
361 | data_sp.reset(new DataBufferHeap(REG_CONTEXT_SIZE, 0)); |
362 | error = ReadGPR(); |
363 | if (error.Fail()) |
364 | return error; |
365 | |
366 | error = ReadFPR(); |
367 | if (error.Fail()) |
368 | return error; |
369 | |
370 | error = ReadVMX(); |
371 | if (error.Fail()) |
372 | return error; |
373 | |
374 | error = ReadVSX(); |
375 | if (error.Fail()) |
376 | return error; |
377 | |
378 | uint8_t *dst = data_sp->GetBytes(); |
379 | ::memcpy(dst, &m_gpr_ppc64le, GetGPRSize()); |
380 | dst += GetGPRSize(); |
381 | ::memcpy(dst, &m_fpr_ppc64le, GetFPRSize()); |
382 | dst += GetFPRSize(); |
383 | ::memcpy(dst, &m_vmx_ppc64le, sizeof(m_vmx_ppc64le)); |
384 | dst += sizeof(m_vmx_ppc64le); |
385 | ::memcpy(dst, &m_vsx_ppc64le, sizeof(m_vsx_ppc64le)); |
386 | |
387 | return error; |
388 | } |
389 | |
390 | Status NativeRegisterContextLinux_ppc64le::WriteAllRegisterValues( |
391 | const lldb::DataBufferSP &data_sp) { |
392 | Status error; |
393 | |
394 | if (!data_sp) { |
395 | error.SetErrorStringWithFormat( |
396 | "NativeRegisterContextLinux_ppc64le::%s invalid data_sp provided" , |
397 | __FUNCTION__); |
398 | return error; |
399 | } |
400 | |
401 | if (data_sp->GetByteSize() != REG_CONTEXT_SIZE) { |
402 | error.SetErrorStringWithFormat( |
403 | "NativeRegisterContextLinux_ppc64le::%s data_sp contained mismatched " |
404 | "data size, expected %" PRIu64 ", actual %" PRIu64, |
405 | __FUNCTION__, REG_CONTEXT_SIZE, data_sp->GetByteSize()); |
406 | return error; |
407 | } |
408 | |
409 | const uint8_t *src = data_sp->GetBytes(); |
410 | if (src == nullptr) { |
411 | error.SetErrorStringWithFormat("NativeRegisterContextLinux_ppc64le::%s " |
412 | "DataBuffer::GetBytes() returned a null " |
413 | "pointer" , |
414 | __FUNCTION__); |
415 | return error; |
416 | } |
417 | |
418 | ::memcpy(&m_gpr_ppc64le, src, GetGPRSize()); |
419 | error = WriteGPR(); |
420 | |
421 | if (error.Fail()) |
422 | return error; |
423 | |
424 | src += GetGPRSize(); |
425 | ::memcpy(&m_fpr_ppc64le, src, GetFPRSize()); |
426 | |
427 | error = WriteFPR(); |
428 | if (error.Fail()) |
429 | return error; |
430 | |
431 | src += GetFPRSize(); |
432 | ::memcpy(&m_vmx_ppc64le, src, sizeof(m_vmx_ppc64le)); |
433 | |
434 | error = WriteVMX(); |
435 | if (error.Fail()) |
436 | return error; |
437 | |
438 | src += sizeof(m_vmx_ppc64le); |
439 | ::memcpy(&m_vsx_ppc64le, src, sizeof(m_vsx_ppc64le)); |
440 | error = WriteVSX(); |
441 | |
442 | return error; |
443 | } |
444 | |
445 | bool NativeRegisterContextLinux_ppc64le::IsGPR(unsigned reg) const { |
446 | return reg <= k_last_gpr_ppc64le; // GPR's come first. |
447 | } |
448 | |
449 | bool NativeRegisterContextLinux_ppc64le::IsFPR(unsigned reg) const { |
450 | return (k_first_fpr_ppc64le <= reg && reg <= k_last_fpr_ppc64le); |
451 | } |
452 | |
453 | uint32_t NativeRegisterContextLinux_ppc64le::CalculateFprOffset( |
454 | const RegisterInfo *reg_info) const { |
455 | return reg_info->byte_offset - |
456 | GetRegisterInfoAtIndex(k_first_fpr_ppc64le)->byte_offset; |
457 | } |
458 | |
459 | uint32_t NativeRegisterContextLinux_ppc64le::CalculateVmxOffset( |
460 | const RegisterInfo *reg_info) const { |
461 | return reg_info->byte_offset - |
462 | GetRegisterInfoAtIndex(k_first_vmx_ppc64le)->byte_offset; |
463 | } |
464 | |
465 | uint32_t NativeRegisterContextLinux_ppc64le::CalculateVsxOffset( |
466 | const RegisterInfo *reg_info) const { |
467 | return reg_info->byte_offset - |
468 | GetRegisterInfoAtIndex(k_first_vsx_ppc64le)->byte_offset; |
469 | } |
470 | |
471 | Status NativeRegisterContextLinux_ppc64le::ReadVMX() { |
472 | int regset = NT_PPC_VMX; |
473 | return NativeProcessLinux::PtraceWrapper(PTRACE_GETVRREGS, m_thread.GetID(), |
474 | ®set, &m_vmx_ppc64le, |
475 | sizeof(m_vmx_ppc64le)); |
476 | } |
477 | |
478 | Status NativeRegisterContextLinux_ppc64le::WriteVMX() { |
479 | int regset = NT_PPC_VMX; |
480 | return NativeProcessLinux::PtraceWrapper(PTRACE_SETVRREGS, m_thread.GetID(), |
481 | ®set, &m_vmx_ppc64le, |
482 | sizeof(m_vmx_ppc64le)); |
483 | } |
484 | |
485 | Status NativeRegisterContextLinux_ppc64le::ReadVSX() { |
486 | int regset = NT_PPC_VSX; |
487 | return NativeProcessLinux::PtraceWrapper(PTRACE_GETVSRREGS, m_thread.GetID(), |
488 | ®set, &m_vsx_ppc64le, |
489 | sizeof(m_vsx_ppc64le)); |
490 | } |
491 | |
492 | Status NativeRegisterContextLinux_ppc64le::WriteVSX() { |
493 | int regset = NT_PPC_VSX; |
494 | return NativeProcessLinux::PtraceWrapper(PTRACE_SETVSRREGS, m_thread.GetID(), |
495 | ®set, &m_vsx_ppc64le, |
496 | sizeof(m_vsx_ppc64le)); |
497 | } |
498 | |
499 | bool NativeRegisterContextLinux_ppc64le::IsVMX(unsigned reg) { |
500 | return (reg >= k_first_vmx_ppc64le) && (reg <= k_last_vmx_ppc64le); |
501 | } |
502 | |
503 | bool NativeRegisterContextLinux_ppc64le::IsVSX(unsigned reg) { |
504 | return (reg >= k_first_vsx_ppc64le) && (reg <= k_last_vsx_ppc64le); |
505 | } |
506 | |
507 | uint32_t NativeRegisterContextLinux_ppc64le::NumSupportedHardwareWatchpoints() { |
508 | Log *log = GetLog(POSIXLog::Watchpoints); |
509 | |
510 | // Read hardware breakpoint and watchpoint information. |
511 | Status error = ReadHardwareDebugInfo(); |
512 | |
513 | if (error.Fail()) |
514 | return 0; |
515 | |
516 | LLDB_LOG(log, "{0}" , m_max_hwp_supported); |
517 | return m_max_hwp_supported; |
518 | } |
519 | |
520 | uint32_t NativeRegisterContextLinux_ppc64le::SetHardwareWatchpoint( |
521 | lldb::addr_t addr, size_t size, uint32_t watch_flags) { |
522 | Log *log = GetLog(POSIXLog::Watchpoints); |
523 | LLDB_LOG(log, "addr: {0:x}, size: {1:x} watch_flags: {2:x}" , addr, size, |
524 | watch_flags); |
525 | |
526 | // Read hardware breakpoint and watchpoint information. |
527 | Status error = ReadHardwareDebugInfo(); |
528 | |
529 | if (error.Fail()) |
530 | return LLDB_INVALID_INDEX32; |
531 | |
532 | uint32_t control_value = 0, wp_index = 0; |
533 | lldb::addr_t real_addr = addr; |
534 | uint32_t rw_mode = 0; |
535 | |
536 | // Check if we are setting watchpoint other than read/write/access Update |
537 | // watchpoint flag to match ppc64le write-read bit configuration. |
538 | switch (watch_flags) { |
539 | case eWatchpointKindWrite: |
540 | rw_mode = PPC_BREAKPOINT_TRIGGER_WRITE; |
541 | watch_flags = 2; |
542 | break; |
543 | case eWatchpointKindRead: |
544 | rw_mode = PPC_BREAKPOINT_TRIGGER_READ; |
545 | watch_flags = 1; |
546 | break; |
547 | case (eWatchpointKindRead | eWatchpointKindWrite): |
548 | rw_mode = PPC_BREAKPOINT_TRIGGER_RW; |
549 | break; |
550 | default: |
551 | return LLDB_INVALID_INDEX32; |
552 | } |
553 | |
554 | // Check if size has a valid hardware watchpoint length. |
555 | if (size != 1 && size != 2 && size != 4 && size != 8) |
556 | return LLDB_INVALID_INDEX32; |
557 | |
558 | // Check 8-byte alignment for hardware watchpoint target address. Below is a |
559 | // hack to recalculate address and size in order to make sure we can watch |
560 | // non 8-byte aligned addresses as well. |
561 | if (addr & 0x07) { |
562 | |
563 | addr_t begin = llvm::alignDown(addr, 8); |
564 | addr_t end = llvm::alignTo(addr + size, 8); |
565 | size = llvm::PowerOf2Ceil(end - begin); |
566 | |
567 | addr = addr & (~0x07); |
568 | } |
569 | |
570 | // Setup control value |
571 | control_value = watch_flags << 3; |
572 | control_value |= ((1 << size) - 1) << 5; |
573 | control_value |= (2 << 1) | 1; |
574 | |
575 | // Iterate over stored watchpoints and find a free wp_index |
576 | wp_index = LLDB_INVALID_INDEX32; |
577 | for (uint32_t i = 0; i < m_max_hwp_supported; i++) { |
578 | if ((m_hwp_regs[i].control & 1) == 0) { |
579 | wp_index = i; // Mark last free slot |
580 | } else if (m_hwp_regs[i].address == addr) { |
581 | return LLDB_INVALID_INDEX32; // We do not support duplicate watchpoints. |
582 | } |
583 | } |
584 | |
585 | if (wp_index == LLDB_INVALID_INDEX32) |
586 | return LLDB_INVALID_INDEX32; |
587 | |
588 | // Update watchpoint in local cache |
589 | m_hwp_regs[wp_index].real_addr = real_addr; |
590 | m_hwp_regs[wp_index].address = addr; |
591 | m_hwp_regs[wp_index].control = control_value; |
592 | m_hwp_regs[wp_index].mode = rw_mode; |
593 | |
594 | // PTRACE call to set corresponding watchpoint register. |
595 | error = WriteHardwareDebugRegs(); |
596 | |
597 | if (error.Fail()) { |
598 | m_hwp_regs[wp_index].address = 0; |
599 | m_hwp_regs[wp_index].control &= llvm::maskTrailingZeros<uint32_t>(1); |
600 | |
601 | return LLDB_INVALID_INDEX32; |
602 | } |
603 | |
604 | return wp_index; |
605 | } |
606 | |
607 | bool NativeRegisterContextLinux_ppc64le::ClearHardwareWatchpoint( |
608 | uint32_t wp_index) { |
609 | Log *log = GetLog(POSIXLog::Watchpoints); |
610 | LLDB_LOG(log, "wp_index: {0}" , wp_index); |
611 | |
612 | // Read hardware breakpoint and watchpoint information. |
613 | Status error = ReadHardwareDebugInfo(); |
614 | |
615 | if (error.Fail()) |
616 | return false; |
617 | |
618 | if (wp_index >= m_max_hwp_supported) |
619 | return false; |
620 | |
621 | // Create a backup we can revert to in case of failure. |
622 | lldb::addr_t tempAddr = m_hwp_regs[wp_index].address; |
623 | uint32_t tempControl = m_hwp_regs[wp_index].control; |
624 | long *tempSlot = reinterpret_cast<long *>(m_hwp_regs[wp_index].slot); |
625 | |
626 | // Update watchpoint in local cache |
627 | m_hwp_regs[wp_index].control &= llvm::maskTrailingZeros<uint32_t>(1); |
628 | m_hwp_regs[wp_index].address = 0; |
629 | m_hwp_regs[wp_index].slot = 0; |
630 | m_hwp_regs[wp_index].mode = 0; |
631 | |
632 | // Ptrace call to update hardware debug registers |
633 | error = NativeProcessLinux::PtraceWrapper(PPC_PTRACE_DELHWDEBUG, |
634 | m_thread.GetID(), 0, tempSlot); |
635 | |
636 | if (error.Fail()) { |
637 | m_hwp_regs[wp_index].control = tempControl; |
638 | m_hwp_regs[wp_index].address = tempAddr; |
639 | m_hwp_regs[wp_index].slot = reinterpret_cast<long>(tempSlot); |
640 | |
641 | return false; |
642 | } |
643 | |
644 | return true; |
645 | } |
646 | |
647 | uint32_t |
648 | NativeRegisterContextLinux_ppc64le::GetWatchpointSize(uint32_t wp_index) { |
649 | Log *log = GetLog(POSIXLog::Watchpoints); |
650 | LLDB_LOG(log, "wp_index: {0}" , wp_index); |
651 | |
652 | unsigned control = (m_hwp_regs[wp_index].control >> 5) & 0xff; |
653 | if (llvm::isPowerOf2_32(control + 1)) { |
654 | return llvm::popcount(control); |
655 | } |
656 | |
657 | return 0; |
658 | } |
659 | |
660 | bool NativeRegisterContextLinux_ppc64le::WatchpointIsEnabled( |
661 | uint32_t wp_index) { |
662 | Log *log = GetLog(POSIXLog::Watchpoints); |
663 | LLDB_LOG(log, "wp_index: {0}" , wp_index); |
664 | |
665 | return !!((m_hwp_regs[wp_index].control & 0x1) == 0x1); |
666 | } |
667 | |
668 | Status NativeRegisterContextLinux_ppc64le::GetWatchpointHitIndex( |
669 | uint32_t &wp_index, lldb::addr_t trap_addr) { |
670 | Log *log = GetLog(POSIXLog::Watchpoints); |
671 | LLDB_LOG(log, "wp_index: {0}, trap_addr: {1:x}" , wp_index, trap_addr); |
672 | |
673 | uint32_t watch_size; |
674 | lldb::addr_t watch_addr; |
675 | |
676 | for (wp_index = 0; wp_index < m_max_hwp_supported; ++wp_index) { |
677 | watch_size = GetWatchpointSize(wp_index); |
678 | watch_addr = m_hwp_regs[wp_index].address; |
679 | |
680 | if (WatchpointIsEnabled(wp_index) && trap_addr >= watch_addr && |
681 | trap_addr <= watch_addr + watch_size) { |
682 | m_hwp_regs[wp_index].hit_addr = trap_addr; |
683 | return Status(); |
684 | } |
685 | } |
686 | |
687 | wp_index = LLDB_INVALID_INDEX32; |
688 | return Status(); |
689 | } |
690 | |
691 | lldb::addr_t |
692 | NativeRegisterContextLinux_ppc64le::GetWatchpointAddress(uint32_t wp_index) { |
693 | Log *log = GetLog(POSIXLog::Watchpoints); |
694 | LLDB_LOG(log, "wp_index: {0}" , wp_index); |
695 | |
696 | if (wp_index >= m_max_hwp_supported) |
697 | return LLDB_INVALID_ADDRESS; |
698 | |
699 | if (WatchpointIsEnabled(wp_index)) |
700 | return m_hwp_regs[wp_index].real_addr; |
701 | else |
702 | return LLDB_INVALID_ADDRESS; |
703 | } |
704 | |
705 | lldb::addr_t |
706 | NativeRegisterContextLinux_ppc64le::GetWatchpointHitAddress(uint32_t wp_index) { |
707 | Log *log = GetLog(POSIXLog::Watchpoints); |
708 | LLDB_LOG(log, "wp_index: {0}" , wp_index); |
709 | |
710 | if (wp_index >= m_max_hwp_supported) |
711 | return LLDB_INVALID_ADDRESS; |
712 | |
713 | if (WatchpointIsEnabled(wp_index)) |
714 | return m_hwp_regs[wp_index].hit_addr; |
715 | |
716 | return LLDB_INVALID_ADDRESS; |
717 | } |
718 | |
719 | Status NativeRegisterContextLinux_ppc64le::ReadHardwareDebugInfo() { |
720 | if (!m_refresh_hwdebug_info) { |
721 | return Status(); |
722 | } |
723 | |
724 | ::pid_t tid = m_thread.GetID(); |
725 | |
726 | struct ppc_debug_info hwdebug_info; |
727 | Status error; |
728 | |
729 | error = NativeProcessLinux::PtraceWrapper( |
730 | PPC_PTRACE_GETHWDBGINFO, tid, 0, &hwdebug_info, sizeof(hwdebug_info)); |
731 | |
732 | if (error.Fail()) |
733 | return error; |
734 | |
735 | m_max_hwp_supported = hwdebug_info.num_data_bps; |
736 | m_max_hbp_supported = hwdebug_info.num_instruction_bps; |
737 | m_refresh_hwdebug_info = false; |
738 | |
739 | return error; |
740 | } |
741 | |
742 | Status NativeRegisterContextLinux_ppc64le::WriteHardwareDebugRegs() { |
743 | struct ppc_hw_breakpoint reg_state; |
744 | Status error; |
745 | long ret; |
746 | |
747 | for (uint32_t i = 0; i < m_max_hwp_supported; i++) { |
748 | reg_state.addr = m_hwp_regs[i].address; |
749 | reg_state.trigger_type = m_hwp_regs[i].mode; |
750 | reg_state.version = 1; |
751 | reg_state.addr_mode = PPC_BREAKPOINT_MODE_EXACT; |
752 | reg_state.condition_mode = PPC_BREAKPOINT_CONDITION_NONE; |
753 | reg_state.addr2 = 0; |
754 | reg_state.condition_value = 0; |
755 | |
756 | error = NativeProcessLinux::PtraceWrapper(PPC_PTRACE_SETHWDEBUG, |
757 | m_thread.GetID(), 0, ®_state, |
758 | sizeof(reg_state), &ret); |
759 | |
760 | if (error.Fail()) |
761 | return error; |
762 | |
763 | m_hwp_regs[i].slot = ret; |
764 | } |
765 | |
766 | return error; |
767 | } |
768 | |
769 | #endif // defined(__powerpc64__) |
770 | |