LLDB mainline
NativeRegisterContextLinux_riscv64.cpp
Go to the documentation of this file.
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
12
13#include "lldb/Host/HostInfo.h"
15#include "lldb/Utility/Log.h"
17#include "lldb/Utility/Status.h"
18
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
31using namespace lldb;
32using namespace lldb_private;
33using namespace lldb_private::process_linux;
34
35std::unique_ptr<NativeRegisterContextLinux>
36NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux(
37 const ArchSpec &target_arch, NativeThreadLinux &native_thread) {
38 switch (target_arch.GetMachine()) {
39 case llvm::Triple::riscv64: {
41
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()) {
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
65llvm::Expected<ArchSpec>
66NativeRegisterContextLinux::DetermineArchitecture(lldb::tid_t tid) {
67 return HostInfo::GetArchitecture();
68}
69
70NativeRegisterContextLinux_riscv64::NativeRegisterContextLinux_riscv64(
71 const ArchSpec &target_arch, NativeThreadProtocol &native_thread,
72 std::unique_ptr<RegisterInfoPOSIX_riscv64> register_info_up)
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
84NativeRegisterContextLinux_riscv64::GetRegisterInfo() const {
85 return static_cast<const RegisterInfoPOSIX_riscv64 &>(
86 NativeRegisterContextRegisterInfo::GetRegisterInfoInterface());
87}
88
89uint32_t NativeRegisterContextLinux_riscv64::GetRegisterSetCount() const {
90 return GetRegisterInfo().GetRegisterSetCount();
91}
92
93const RegisterSet *
94NativeRegisterContextLinux_riscv64::GetRegisterSet(uint32_t set_index) const {
95 return GetRegisterInfo().GetRegisterSet(set_index);
96}
97
98uint32_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
105Status
106NativeRegisterContextLinux_riscv64::ReadRegister(const RegisterInfo *reg_info,
107 RegisterValue &reg_value) {
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
158Status NativeRegisterContextLinux_riscv64::WriteRegister(
159 const RegisterInfo *reg_info, const RegisterValue &reg_value) {
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
206Status NativeRegisterContextLinux_riscv64::ReadAllRegisterValues(
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
231Status NativeRegisterContextLinux_riscv64::WriteAllRegisterValues(
232 const lldb::DataBufferSP &data_sp) {
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
278size_t NativeRegisterContextLinux_riscv64::GetRegContextSize() {
279 size_t size = GetGPRSize();
280 if (GetRegisterInfo().IsFPPresent())
281 size += GetFPRSize();
282 return size;
283}
284
285bool NativeRegisterContextLinux_riscv64::IsGPR(unsigned reg) const {
286 return GetRegisterInfo().GetRegisterSetFromRegisterIndex(reg) ==
288}
289
290bool NativeRegisterContextLinux_riscv64::IsFPR(unsigned reg) const {
291 return GetRegisterInfo().IsFPReg(reg);
292}
293
294Status NativeRegisterContextLinux_riscv64::ReadGPR() {
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
312Status 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
326Status NativeRegisterContextLinux_riscv64::ReadFPR() {
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
344Status 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
358void NativeRegisterContextLinux_riscv64::InvalidateAllRegisters() {
359 m_gpr_is_valid = false;
360 m_fpu_is_valid = false;
361}
362
363uint32_t NativeRegisterContextLinux_riscv64::CalculateFprOffset(
364 const RegisterInfo *reg_info) const {
365 return reg_info->byte_offset - GetGPRSize();
366}
367
368std::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
static llvm::raw_ostream & error(Stream &strm)
#define PTRACE_GETREGSET
Definition: Ptrace.h:36
size_t GetRegisterSetCount() const override
An architecture specification class.
Definition: ArchSpec.h:31
llvm::Triple::ArchType GetMachine() const
Returns a machine family for the current architecture.
Definition: ArchSpec.cpp:701
A subclass of DataBuffer that stores a data buffer on the heap.
A class to manage flags.
Definition: Flags.h:22
uint32_t SetFromMemoryData(const RegisterInfo &reg_info, const void *src, uint32_t src_len, lldb::ByteOrder src_byte_order, Status &error)
bool SetUInt(uint64_t uint, uint32_t byte_size)
const void * GetBytes() const
An error handling class.
Definition: Status.h:118
#define LLDB_INVALID_INDEX32
Definition: lldb-defines.h:83
#define LLDB_INVALID_REGNUM
Definition: lldb-defines.h:87
A class that represents a running process on the host machine.
Definition: SBAddress.h:15
std::shared_ptr< lldb_private::DataBuffer > DataBufferSP
Definition: lldb-forward.h:336
std::shared_ptr< lldb_private::WritableDataBuffer > WritableDataBufferSP
Definition: lldb-forward.h:337
uint64_t tid_t
Definition: lldb-types.h:84
@ eRegisterKindLLDB
lldb's internal register numbers
Every register is described in detail including its name, alternate name (optional),...
uint32_t byte_offset
The byte offset in the register context data where this register's value is found.
uint32_t byte_size
Size in bytes of the register.
uint32_t kinds[lldb::kNumRegisterKinds]
Holds all of the various register numbers for all register kinds.
const char * name
Name of this register, can't be NULL.
Registers are grouped into register sets.
size_t num_registers
The number of registers in REGISTERS array below.