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