1//===-- NativeRegisterContextFreeBSD_powerpc.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(__powerpc__)
10
11#include "NativeRegisterContextFreeBSD_powerpc.h"
12
13#include "lldb/Host/HostInfo.h"
14#include "lldb/Utility/DataBufferHeap.h"
15#include "lldb/Utility/RegisterValue.h"
16#include "lldb/Utility/Status.h"
17
18#include "Plugins/Process/FreeBSD/NativeProcessFreeBSD.h"
19// for register enum definitions
20#include "Plugins/Process/Utility/RegisterContextPOSIX_powerpc.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#include <optional>
28
29using namespace lldb;
30using namespace lldb_private;
31using namespace lldb_private::process_freebsd;
32
33static const uint32_t g_gpr_regnums[] = {
34    gpr_r0_powerpc,  gpr_r1_powerpc,  gpr_r2_powerpc,  gpr_r3_powerpc,
35    gpr_r4_powerpc,  gpr_r5_powerpc,  gpr_r6_powerpc,  gpr_r7_powerpc,
36    gpr_r8_powerpc,  gpr_r9_powerpc,  gpr_r10_powerpc, gpr_r11_powerpc,
37    gpr_r12_powerpc, gpr_r13_powerpc, gpr_r14_powerpc, gpr_r15_powerpc,
38    gpr_r16_powerpc, gpr_r17_powerpc, gpr_r18_powerpc, gpr_r19_powerpc,
39    gpr_r20_powerpc, gpr_r21_powerpc, gpr_r22_powerpc, gpr_r23_powerpc,
40    gpr_r24_powerpc, gpr_r25_powerpc, gpr_r26_powerpc, gpr_r27_powerpc,
41    gpr_r28_powerpc, gpr_r29_powerpc, gpr_r30_powerpc, gpr_r31_powerpc,
42    gpr_lr_powerpc,  gpr_cr_powerpc,  gpr_xer_powerpc, gpr_ctr_powerpc,
43    gpr_pc_powerpc,
44};
45
46static const uint32_t g_fpr_regnums[] = {
47    fpr_f0_powerpc,    fpr_f1_powerpc,  fpr_f2_powerpc,  fpr_f3_powerpc,
48    fpr_f4_powerpc,    fpr_f5_powerpc,  fpr_f6_powerpc,  fpr_f7_powerpc,
49    fpr_f8_powerpc,    fpr_f9_powerpc,  fpr_f10_powerpc, fpr_f11_powerpc,
50    fpr_f12_powerpc,   fpr_f13_powerpc, fpr_f14_powerpc, fpr_f15_powerpc,
51    fpr_f16_powerpc,   fpr_f17_powerpc, fpr_f18_powerpc, fpr_f19_powerpc,
52    fpr_f20_powerpc,   fpr_f21_powerpc, fpr_f22_powerpc, fpr_f23_powerpc,
53    fpr_f24_powerpc,   fpr_f25_powerpc, fpr_f26_powerpc, fpr_f27_powerpc,
54    fpr_f28_powerpc,   fpr_f29_powerpc, fpr_f30_powerpc, fpr_f31_powerpc,
55    fpr_fpscr_powerpc,
56};
57
58// Number of register sets provided by this context.
59enum { k_num_register_sets = 2 };
60
61static const RegisterSet g_reg_sets_powerpc[k_num_register_sets] = {
62    {"General Purpose Registers", "gpr", k_num_gpr_registers_powerpc,
63     g_gpr_regnums},
64    {"Floating Point Registers", "fpr", k_num_fpr_registers_powerpc,
65     g_fpr_regnums},
66};
67
68NativeRegisterContextFreeBSD *
69NativeRegisterContextFreeBSD::CreateHostNativeRegisterContextFreeBSD(
70    const ArchSpec &target_arch, NativeThreadProtocol &native_thread) {
71  return new NativeRegisterContextFreeBSD_powerpc(target_arch, native_thread);
72}
73
74static RegisterInfoInterface *
75CreateRegisterInfoInterface(const ArchSpec &target_arch) {
76  if (HostInfo::GetArchitecture().GetAddressByteSize() == 4) {
77    return new RegisterContextFreeBSD_powerpc32(target_arch);
78  } else {
79    assert((HostInfo::GetArchitecture().GetAddressByteSize() == 8) &&
80           "Register setting path assumes this is a 64-bit host");
81    return new RegisterContextFreeBSD_powerpc64(target_arch);
82  }
83}
84
85NativeRegisterContextFreeBSD_powerpc::NativeRegisterContextFreeBSD_powerpc(
86    const ArchSpec &target_arch, NativeThreadProtocol &native_thread)
87    : NativeRegisterContextRegisterInfo(
88          native_thread, CreateRegisterInfoInterface(target_arch)) {}
89
90RegisterContextFreeBSD_powerpc &
91NativeRegisterContextFreeBSD_powerpc::GetRegisterInfo() const {
92  return static_cast<RegisterContextFreeBSD_powerpc &>(
93      *m_register_info_interface_up);
94}
95
96uint32_t NativeRegisterContextFreeBSD_powerpc::GetRegisterSetCount() const {
97  return k_num_register_sets;
98}
99
100const RegisterSet *
101NativeRegisterContextFreeBSD_powerpc::GetRegisterSet(uint32_t set_index) const {
102  switch (GetRegisterInfoInterface().GetTargetArchitecture().GetMachine()) {
103  case llvm::Triple::ppc:
104    return &g_reg_sets_powerpc[set_index];
105  default:
106    llvm_unreachable("Unhandled target architecture.");
107  }
108}
109
110std::optional<NativeRegisterContextFreeBSD_powerpc::RegSetKind>
111NativeRegisterContextFreeBSD_powerpc::GetSetForNativeRegNum(
112    uint32_t reg_num) const {
113  switch (GetRegisterInfoInterface().GetTargetArchitecture().GetMachine()) {
114  case llvm::Triple::ppc:
115    if (reg_num >= k_first_gpr_powerpc && reg_num <= k_last_gpr_powerpc)
116      return GPRegSet;
117    if (reg_num >= k_first_fpr && reg_num <= k_last_fpr)
118      return FPRegSet;
119    break;
120  default:
121    llvm_unreachable("Unhandled target architecture.");
122  }
123
124  llvm_unreachable("Register does not belong to any register set");
125}
126
127uint32_t NativeRegisterContextFreeBSD_powerpc::GetUserRegisterCount() const {
128  uint32_t count = 0;
129  for (uint32_t set_index = 0; set_index < GetRegisterSetCount(); ++set_index)
130    count += GetRegisterSet(set_index)->num_registers;
131  return count;
132}
133
134Status NativeRegisterContextFreeBSD_powerpc::ReadRegisterSet(RegSetKind set) {
135  switch (set) {
136  case GPRegSet:
137    return NativeProcessFreeBSD::PtraceWrapper(PT_GETREGS, m_thread.GetID(),
138                                               m_reg_data.data());
139  case FPRegSet:
140    return NativeProcessFreeBSD::PtraceWrapper(PT_GETFPREGS, m_thread.GetID(),
141                                               m_reg_data.data() + sizeof(reg));
142  }
143  llvm_unreachable("NativeRegisterContextFreeBSD_powerpc::ReadRegisterSet");
144}
145
146Status NativeRegisterContextFreeBSD_powerpc::WriteRegisterSet(RegSetKind set) {
147  switch (set) {
148  case GPRegSet:
149    return NativeProcessFreeBSD::PtraceWrapper(PT_SETREGS, m_thread.GetID(),
150                                               m_reg_data.data());
151  case FPRegSet:
152    return NativeProcessFreeBSD::PtraceWrapper(PT_SETFPREGS, m_thread.GetID(),
153                                               m_reg_data.data() + sizeof(reg));
154  }
155  llvm_unreachable("NativeRegisterContextFreeBSD_powerpc::WriteRegisterSet");
156}
157
158Status
159NativeRegisterContextFreeBSD_powerpc::ReadRegister(const RegisterInfo *reg_info,
160                                                   RegisterValue &reg_value) {
161  Status error;
162
163  if (!reg_info) {
164    error.SetErrorString("reg_info NULL");
165    return error;
166  }
167
168  const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
169
170  if (reg == LLDB_INVALID_REGNUM)
171    return Status("no lldb regnum for %s", reg_info && reg_info->name
172                                               ? reg_info->name
173                                               : "<unknown register>");
174
175  std::optional<RegSetKind> opt_set = GetSetForNativeRegNum(reg);
176  if (!opt_set) {
177    // This is likely an internal register for lldb use only and should not be
178    // directly queried.
179    error.SetErrorStringWithFormat("register \"%s\" is in unrecognized set",
180                                   reg_info->name);
181    return error;
182  }
183
184  RegSetKind set = *opt_set;
185  error = ReadRegisterSet(set);
186  if (error.Fail())
187    return error;
188
189  assert(reg_info->byte_offset + reg_info->byte_size <= m_reg_data.size());
190  reg_value.SetBytes(m_reg_data.data() + reg_info->byte_offset,
191                     reg_info->byte_size, endian::InlHostByteOrder());
192  return error;
193}
194
195Status NativeRegisterContextFreeBSD_powerpc::WriteRegister(
196    const RegisterInfo *reg_info, const RegisterValue &reg_value) {
197  Status error;
198
199  if (!reg_info)
200    return Status("reg_info NULL");
201
202  const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
203
204  if (reg == LLDB_INVALID_REGNUM)
205    return Status("no lldb regnum for %s", reg_info && reg_info->name
206                                               ? reg_info->name
207                                               : "<unknown register>");
208
209  std::optional<RegSetKind> opt_set = GetSetForNativeRegNum(reg);
210  if (!opt_set) {
211    // This is likely an internal register for lldb use only and should not be
212    // directly queried.
213    error.SetErrorStringWithFormat("register \"%s\" is in unrecognized set",
214                                   reg_info->name);
215    return error;
216  }
217
218  RegSetKind set = *opt_set;
219  error = ReadRegisterSet(set);
220  if (error.Fail())
221    return error;
222
223  assert(reg_info->byte_offset + reg_info->byte_size <= m_reg_data.size());
224  ::memcpy(m_reg_data.data() + reg_info->byte_offset, reg_value.GetBytes(),
225           reg_info->byte_size);
226
227  return WriteRegisterSet(set);
228}
229
230Status NativeRegisterContextFreeBSD_powerpc::ReadAllRegisterValues(
231    lldb::WritableDataBufferSP &data_sp) {
232  Status error;
233
234  error = ReadRegisterSet(GPRegSet);
235  if (error.Fail())
236    return error;
237
238  error = ReadRegisterSet(FPRegSet);
239  if (error.Fail())
240    return error;
241
242  data_sp.reset(new DataBufferHeap(m_reg_data.size(), 0));
243  uint8_t *dst = data_sp->GetBytes();
244  ::memcpy(dst, m_reg_data.data(), m_reg_data.size());
245
246  return error;
247}
248
249Status NativeRegisterContextFreeBSD_powerpc::WriteAllRegisterValues(
250    const lldb::DataBufferSP &data_sp) {
251  Status error;
252
253  if (!data_sp) {
254    error.SetErrorStringWithFormat(
255        "NativeRegisterContextFreeBSD_powerpc::%s invalid data_sp provided",
256        __FUNCTION__);
257    return error;
258  }
259
260  if (data_sp->GetByteSize() != m_reg_data.size()) {
261    error.SetErrorStringWithFormat(
262        "NativeRegisterContextFreeBSD_powerpc::%s data_sp contained mismatched "
263        "data size, expected %zu, actual %" PRIu64,
264        __FUNCTION__, m_reg_data.size(), data_sp->GetByteSize());
265    return error;
266  }
267
268  const uint8_t *src = data_sp->GetBytes();
269  if (src == nullptr) {
270    error.SetErrorStringWithFormat("NativeRegisterContextFreeBSD_powerpc::%s "
271                                   "DataBuffer::GetBytes() returned a null "
272                                   "pointer",
273                                   __FUNCTION__);
274    return error;
275  }
276  ::memcpy(m_reg_data.data(), src, m_reg_data.size());
277
278  error = WriteRegisterSet(GPRegSet);
279  if (error.Fail())
280    return error;
281
282  return WriteRegisterSet(FPRegSet);
283}
284
285llvm::Error NativeRegisterContextFreeBSD_powerpc::CopyHardwareWatchpointsFrom(
286    NativeRegisterContextFreeBSD &source) {
287  return llvm::Error::success();
288}
289
290#endif // defined (__powerpc__)
291