xref: /llvm-project/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_riscv64.cpp (revision 660e34fd38c3fb39fba1871bbf5b2eb3a48bf277)
1 //===-- NativeRegisterContextLinux_riscv64.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(__riscv) && __riscv_xlen == 64
10 
11 #include "NativeRegisterContextLinux_riscv64.h"
12 
13 #include "lldb/Host/HostInfo.h"
14 #include "lldb/Utility/DataBufferHeap.h"
15 #include "lldb/Utility/Log.h"
16 #include "lldb/Utility/RegisterValue.h"
17 #include "lldb/Utility/Status.h"
18 
19 #include "Plugins/Process/Linux/NativeProcessLinux.h"
20 #include "Plugins/Process/Linux/Procfs.h"
21 #include "Plugins/Process/Utility/RegisterInfoPOSIX_riscv64.h"
22 #include "Plugins/Process/Utility/lldb-riscv-register-enums.h"
23 
24 // System includes - They have to be included after framework includes because
25 // they define some macros which collide with variable names in other modules
26 #include <sys/ptrace.h>
27 #include <sys/uio.h>
28 // NT_PRSTATUS and NT_FPREGSET definition
29 #include <elf.h>
30 
31 using namespace lldb;
32 using namespace lldb_private;
33 using namespace lldb_private::process_linux;
34 
35 std::unique_ptr<NativeRegisterContextLinux>
36 NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux(
37     const ArchSpec &target_arch, NativeThreadLinux &native_thread) {
38   switch (target_arch.GetMachine()) {
39   case llvm::Triple::riscv64: {
40     Flags opt_regsets(RegisterInfoPOSIX_riscv64::eRegsetMaskDefault);
41 
42     RegisterInfoPOSIX_riscv64::FPR fpr;
43     struct iovec ioVec;
44     ioVec.iov_base = &fpr;
45     ioVec.iov_len = sizeof(fpr);
46     unsigned int regset = NT_FPREGSET;
47 
48     if (NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET,
49                                           native_thread.GetID(), &regset,
50                                           &ioVec, sizeof(fpr))
51             .Success()) {
52       opt_regsets.Set(RegisterInfoPOSIX_riscv64::eRegsetMaskFP);
53     }
54 
55     auto register_info_up =
56         std::make_unique<RegisterInfoPOSIX_riscv64>(target_arch, opt_regsets);
57     return std::make_unique<NativeRegisterContextLinux_riscv64>(
58         target_arch, native_thread, std::move(register_info_up));
59   }
60   default:
61     llvm_unreachable("have no register context for architecture");
62   }
63 }
64 
65 llvm::Expected<ArchSpec>
66 NativeRegisterContextLinux::DetermineArchitecture(lldb::tid_t tid) {
67   return HostInfo::GetArchitecture();
68 }
69 
70 NativeRegisterContextLinux_riscv64::NativeRegisterContextLinux_riscv64(
71     const ArchSpec &target_arch, NativeThreadProtocol &native_thread,
72     std::unique_ptr<RegisterInfoPOSIX_riscv64> register_info_up)
73     : NativeRegisterContextRegisterInfo(native_thread,
74                                         register_info_up.release()),
75       NativeRegisterContextLinux(native_thread) {
76   ::memset(&m_fpr, 0, sizeof(m_fpr));
77   ::memset(&m_gpr, 0, sizeof(m_gpr));
78 
79   m_gpr_is_valid = false;
80   m_fpu_is_valid = false;
81 }
82 
83 const RegisterInfoPOSIX_riscv64 &
84 NativeRegisterContextLinux_riscv64::GetRegisterInfo() const {
85   return static_cast<const RegisterInfoPOSIX_riscv64 &>(
86       NativeRegisterContextRegisterInfo::GetRegisterInfoInterface());
87 }
88 
89 uint32_t NativeRegisterContextLinux_riscv64::GetRegisterSetCount() const {
90   return GetRegisterInfo().GetRegisterSetCount();
91 }
92 
93 const RegisterSet *
94 NativeRegisterContextLinux_riscv64::GetRegisterSet(uint32_t set_index) const {
95   return GetRegisterInfo().GetRegisterSet(set_index);
96 }
97 
98 uint32_t NativeRegisterContextLinux_riscv64::GetUserRegisterCount() const {
99   uint32_t count = 0;
100   for (uint32_t set_index = 0; set_index < GetRegisterSetCount(); ++set_index)
101     count += GetRegisterSet(set_index)->num_registers;
102   return count;
103 }
104 
105 Status
106 NativeRegisterContextLinux_riscv64::ReadRegister(const RegisterInfo *reg_info,
107                                                  RegisterValue &reg_value) {
108   Status error;
109 
110   if (!reg_info) {
111     error = Status::FromErrorString("reg_info NULL");
112     return error;
113   }
114 
115   const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
116 
117   if (reg == LLDB_INVALID_REGNUM)
118     return Status::FromErrorStringWithFormat(
119         "no lldb regnum for %s",
120         reg_info && reg_info->name ? reg_info->name : "<unknown register>");
121 
122   if (reg == gpr_x0_riscv) {
123     reg_value.SetUInt(0, reg_info->byte_size);
124     return error;
125   }
126 
127   uint8_t *src = nullptr;
128   uint32_t offset = LLDB_INVALID_INDEX32;
129 
130   if (IsGPR(reg)) {
131     error = ReadGPR();
132     if (error.Fail())
133       return error;
134 
135     offset = reg_info->byte_offset;
136     assert(offset < GetGPRSize());
137     src = (uint8_t *)GetGPRBuffer() + offset;
138 
139   } else if (IsFPR(reg)) {
140     error = ReadFPR();
141     if (error.Fail())
142       return error;
143 
144     offset = CalculateFprOffset(reg_info);
145     assert(offset < GetFPRSize());
146     src = (uint8_t *)GetFPRBuffer() + offset;
147   } else
148     return Status::FromErrorString(
149         "failed - register wasn't recognized to be a GPR or an FPR, "
150         "write strategy unknown");
151 
152   reg_value.SetFromMemoryData(*reg_info, src, reg_info->byte_size,
153                               eByteOrderLittle, error);
154 
155   return error;
156 }
157 
158 Status NativeRegisterContextLinux_riscv64::WriteRegister(
159     const RegisterInfo *reg_info, const RegisterValue &reg_value) {
160   Status error;
161 
162   if (!reg_info)
163     return Status::FromErrorString("reg_info NULL");
164 
165   const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
166 
167   if (reg == LLDB_INVALID_REGNUM)
168     return Status::FromErrorStringWithFormat(
169         "no lldb regnum for %s",
170         reg_info->name != nullptr ? reg_info->name : "<unknown register>");
171 
172   if (reg == gpr_x0_riscv) {
173     // do nothing.
174     return error;
175   }
176 
177   uint8_t *dst = nullptr;
178   uint32_t offset = LLDB_INVALID_INDEX32;
179 
180   if (IsGPR(reg)) {
181     error = ReadGPR();
182     if (error.Fail())
183       return error;
184 
185     assert(reg_info->byte_offset < GetGPRSize());
186     dst = (uint8_t *)GetGPRBuffer() + reg_info->byte_offset;
187     ::memcpy(dst, reg_value.GetBytes(), reg_info->byte_size);
188 
189     return WriteGPR();
190   } else if (IsFPR(reg)) {
191     error = ReadFPR();
192     if (error.Fail())
193       return error;
194 
195     offset = CalculateFprOffset(reg_info);
196     assert(offset < GetFPRSize());
197     dst = (uint8_t *)GetFPRBuffer() + offset;
198     ::memcpy(dst, reg_value.GetBytes(), reg_info->byte_size);
199 
200     return WriteFPR();
201   }
202 
203   return Status::FromErrorString("Failed to write register value");
204 }
205 
206 Status NativeRegisterContextLinux_riscv64::ReadAllRegisterValues(
207     lldb::WritableDataBufferSP &data_sp) {
208   Status error;
209 
210   data_sp.reset(new DataBufferHeap(GetRegContextSize(), 0));
211 
212   error = ReadGPR();
213   if (error.Fail())
214     return error;
215 
216   if (GetRegisterInfo().IsFPPresent()) {
217     error = ReadFPR();
218     if (error.Fail())
219       return error;
220   }
221 
222   uint8_t *dst = const_cast<uint8_t *>(data_sp->GetBytes());
223   ::memcpy(dst, GetGPRBuffer(), GetGPRSize());
224   dst += GetGPRSize();
225   if (GetRegisterInfo().IsFPPresent())
226     ::memcpy(dst, GetFPRBuffer(), GetFPRSize());
227 
228   return error;
229 }
230 
231 Status NativeRegisterContextLinux_riscv64::WriteAllRegisterValues(
232     const lldb::DataBufferSP &data_sp) {
233   Status error;
234 
235   if (!data_sp) {
236     error = Status::FromErrorStringWithFormat(
237         "NativeRegisterContextLinux_riscv64::%s invalid data_sp provided",
238         __FUNCTION__);
239     return error;
240   }
241 
242   if (data_sp->GetByteSize() != GetRegContextSize()) {
243     error = Status::FromErrorStringWithFormat(
244         "NativeRegisterContextLinux_riscv64::%s data_sp contained mismatched "
245         "data size, expected %" PRIu64 ", actual %" PRIu64,
246         __FUNCTION__, GetRegContextSize(), data_sp->GetByteSize());
247     return error;
248   }
249 
250   uint8_t *src = const_cast<uint8_t *>(data_sp->GetBytes());
251   if (src == nullptr) {
252     error = Status::FromErrorStringWithFormat(
253         "NativeRegisterContextLinux_riscv64::%s "
254         "DataBuffer::GetBytes() returned a null "
255         "pointer",
256         __FUNCTION__);
257     return error;
258   }
259   ::memcpy(GetGPRBuffer(), src, GetRegisterInfoInterface().GetGPRSize());
260 
261   error = WriteGPR();
262   if (error.Fail())
263     return error;
264 
265   src += GetRegisterInfoInterface().GetGPRSize();
266 
267   if (GetRegisterInfo().IsFPPresent()) {
268     ::memcpy(GetFPRBuffer(), src, GetFPRSize());
269 
270     error = WriteFPR();
271     if (error.Fail())
272       return error;
273   }
274 
275   return error;
276 }
277 
278 size_t NativeRegisterContextLinux_riscv64::GetRegContextSize() {
279   size_t size = GetGPRSize();
280   if (GetRegisterInfo().IsFPPresent())
281     size += GetFPRSize();
282   return size;
283 }
284 
285 bool NativeRegisterContextLinux_riscv64::IsGPR(unsigned reg) const {
286   return GetRegisterInfo().GetRegisterSetFromRegisterIndex(reg) ==
287          RegisterInfoPOSIX_riscv64::GPRegSet;
288 }
289 
290 bool NativeRegisterContextLinux_riscv64::IsFPR(unsigned reg) const {
291   return GetRegisterInfo().IsFPReg(reg);
292 }
293 
294 Status NativeRegisterContextLinux_riscv64::ReadGPR() {
295   Status error;
296 
297   if (m_gpr_is_valid)
298     return error;
299 
300   struct iovec ioVec;
301   ioVec.iov_base = GetGPRBuffer();
302   ioVec.iov_len = GetGPRSize();
303 
304   error = ReadRegisterSet(&ioVec, GetGPRSize(), NT_PRSTATUS);
305 
306   if (error.Success())
307     m_gpr_is_valid = true;
308 
309   return error;
310 }
311 
312 Status NativeRegisterContextLinux_riscv64::WriteGPR() {
313   Status error = ReadGPR();
314   if (error.Fail())
315     return error;
316 
317   struct iovec ioVec;
318   ioVec.iov_base = GetGPRBuffer();
319   ioVec.iov_len = GetGPRSize();
320 
321   m_gpr_is_valid = false;
322 
323   return WriteRegisterSet(&ioVec, GetGPRSize(), NT_PRSTATUS);
324 }
325 
326 Status NativeRegisterContextLinux_riscv64::ReadFPR() {
327   Status error;
328 
329   if (m_fpu_is_valid)
330     return error;
331 
332   struct iovec ioVec;
333   ioVec.iov_base = GetFPRBuffer();
334   ioVec.iov_len = GetFPRSize();
335 
336   error = ReadRegisterSet(&ioVec, GetFPRSize(), NT_FPREGSET);
337 
338   if (error.Success())
339     m_fpu_is_valid = true;
340 
341   return error;
342 }
343 
344 Status NativeRegisterContextLinux_riscv64::WriteFPR() {
345   Status error = ReadFPR();
346   if (error.Fail())
347     return error;
348 
349   struct iovec ioVec;
350   ioVec.iov_base = GetFPRBuffer();
351   ioVec.iov_len = GetFPRSize();
352 
353   m_fpu_is_valid = false;
354 
355   return WriteRegisterSet(&ioVec, GetFPRSize(), NT_FPREGSET);
356 }
357 
358 void NativeRegisterContextLinux_riscv64::InvalidateAllRegisters() {
359   m_gpr_is_valid = false;
360   m_fpu_is_valid = false;
361 }
362 
363 uint32_t NativeRegisterContextLinux_riscv64::CalculateFprOffset(
364     const RegisterInfo *reg_info) const {
365   return reg_info->byte_offset - GetGPRSize();
366 }
367 
368 std::vector<uint32_t> NativeRegisterContextLinux_riscv64::GetExpeditedRegisters(
369     ExpeditedRegs expType) const {
370   std::vector<uint32_t> expedited_reg_nums =
371       NativeRegisterContext::GetExpeditedRegisters(expType);
372 
373   return expedited_reg_nums;
374 }
375 
376 #endif // defined (__riscv) && __riscv_xlen == 64
377