LLDB mainline
NativeRegisterContextLinux_loongarch64.cpp
Go to the documentation of this file.
1//===-- NativeRegisterContextLinux_loongarch64.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(__loongarch__) && __loongarch_grlen == 64
10
12
13#include "lldb/Host/HostInfo.h"
16#include "lldb/Utility/Log.h"
18#include "lldb/Utility/Status.h"
19
24
25// NT_PRSTATUS and NT_FPREGSET definition
26#include <elf.h>
27// struct iovec definition
28#include <sys/uio.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::loongarch64: {
41 Flags opt_regsets;
42 auto register_info_up = std::make_unique<RegisterInfoPOSIX_loongarch64>(
43 target_arch, opt_regsets);
44 return std::make_unique<NativeRegisterContextLinux_loongarch64>(
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_loongarch64::NativeRegisterContextLinux_loongarch64(
58 const ArchSpec &target_arch, NativeThreadProtocol &native_thread,
59 std::unique_ptr<RegisterInfoPOSIX_loongarch64> 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 ::memset(&m_hwp_regs, 0, sizeof(m_hwp_regs));
67 ::memset(&m_hbp_regs, 0, sizeof(m_hbp_regs));
68
69 // Refer to:
70 // https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#control-and-status-registers-related-to-watchpoints
71 // 14 is just a maximum value, query hardware for actual watchpoint count.
72 m_max_hwp_supported = 14;
73 m_max_hbp_supported = 14;
74 m_refresh_hwdebug_info = true;
75
76 m_gpr_is_valid = false;
77 m_fpu_is_valid = false;
78}
79
81NativeRegisterContextLinux_loongarch64::GetRegisterInfo() const {
82 return static_cast<const RegisterInfoPOSIX_loongarch64 &>(
83 NativeRegisterContextRegisterInfo::GetRegisterInfoInterface());
84}
85
86uint32_t NativeRegisterContextLinux_loongarch64::GetRegisterSetCount() const {
87 return GetRegisterInfo().GetRegisterSetCount();
88}
89
90const RegisterSet *NativeRegisterContextLinux_loongarch64::GetRegisterSet(
91 uint32_t set_index) const {
92 return GetRegisterInfo().GetRegisterSet(set_index);
93}
94
95uint32_t NativeRegisterContextLinux_loongarch64::GetUserRegisterCount() const {
96 uint32_t count = 0;
97 for (uint32_t set_index = 0; set_index < GetRegisterSetCount(); ++set_index)
98 count += GetRegisterSet(set_index)->num_registers;
99 return count;
100}
101
102Status NativeRegisterContextLinux_loongarch64::ReadRegister(
103 const RegisterInfo *reg_info, RegisterValue &reg_value) {
105
106 if (!reg_info) {
107 error = Status::FromErrorString("reg_info NULL");
108 return error;
109 }
110
111 const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
112
113 if (reg == LLDB_INVALID_REGNUM)
114 return Status::FromErrorStringWithFormat(
115 "no lldb regnum for %s",
116 reg_info && reg_info->name ? reg_info->name : "<unknown register>");
117
118 uint8_t *src = nullptr;
119 uint32_t offset = LLDB_INVALID_INDEX32;
120
121 if (IsGPR(reg)) {
122 error = ReadGPR();
123 if (error.Fail())
124 return error;
125
126 offset = reg_info->byte_offset;
127 assert(offset < GetGPRSize());
128 src = (uint8_t *)GetGPRBuffer() + offset;
129
130 } else if (IsFPR(reg)) {
131 error = ReadFPR();
132 if (error.Fail())
133 return error;
134
135 offset = CalculateFprOffset(reg_info);
136 assert(offset < GetFPRSize());
137 src = (uint8_t *)GetFPRBuffer() + offset;
138 } else
139 return Status::FromErrorString(
140 "failed - register wasn't recognized to be a GPR or an FPR, "
141 "write strategy unknown");
142
143 reg_value.SetFromMemoryData(*reg_info, src, reg_info->byte_size,
144 eByteOrderLittle, error);
145
146 return error;
147}
148
149Status NativeRegisterContextLinux_loongarch64::WriteRegister(
150 const RegisterInfo *reg_info, const RegisterValue &reg_value) {
152
153 if (!reg_info)
154 return Status::FromErrorString("reg_info NULL");
155
156 const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
157
158 if (reg == LLDB_INVALID_REGNUM)
159 return Status::FromErrorStringWithFormat(
160 "no lldb regnum for %s",
161 reg_info->name != nullptr ? reg_info->name : "<unknown register>");
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::FromErrorString("Failed to write register value");
190}
191
192Status NativeRegisterContextLinux_loongarch64::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 = data_sp->GetBytes();
207 ::memcpy(dst, GetGPRBuffer(), GetGPRSize());
208 dst += GetGPRSize();
209 ::memcpy(dst, GetFPRBuffer(), GetFPRSize());
210
211 return error;
212}
213
214Status NativeRegisterContextLinux_loongarch64::WriteAllRegisterValues(
215 const lldb::DataBufferSP &data_sp) {
217
218 if (!data_sp) {
219 error = Status::FromErrorStringWithFormat(
220 "NativeRegisterContextLinux_loongarch64::%s invalid data_sp provided",
221 __FUNCTION__);
222 return error;
223 }
224
225 if (data_sp->GetByteSize() != REG_CONTEXT_SIZE) {
226 error = Status::FromErrorStringWithFormat(
227 "NativeRegisterContextLinux_loongarch64::%s data_sp contained "
228 "mismatched data size, expected %" PRIu64 ", actual %" PRIu64,
229 __FUNCTION__, REG_CONTEXT_SIZE, data_sp->GetByteSize());
230 return error;
231 }
232
233 const uint8_t *src = data_sp->GetBytes();
234 if (src == nullptr) {
235 error = Status::FromErrorStringWithFormat(
236 "NativeRegisterContextLinux_loongarch64::%s "
237 "DataBuffer::GetBytes() returned a null "
238 "pointer",
239 __FUNCTION__);
240 return error;
241 }
242 ::memcpy(GetGPRBuffer(), src, GetRegisterInfoInterface().GetGPRSize());
243
244 error = WriteGPR();
245 if (error.Fail())
246 return error;
247
248 src += GetRegisterInfoInterface().GetGPRSize();
249 ::memcpy(GetFPRBuffer(), src, GetFPRSize());
250
251 error = WriteFPR();
252 if (error.Fail())
253 return error;
254
255 return error;
256}
257
258bool NativeRegisterContextLinux_loongarch64::IsGPR(unsigned reg) const {
259 return GetRegisterInfo().GetRegisterSetFromRegisterIndex(reg) ==
261}
262
263bool NativeRegisterContextLinux_loongarch64::IsFPR(unsigned reg) const {
264 return GetRegisterInfo().GetRegisterSetFromRegisterIndex(reg) ==
266}
267
268Status NativeRegisterContextLinux_loongarch64::ReadGPR() {
270
271 if (m_gpr_is_valid)
272 return error;
273
274 struct iovec ioVec;
275 ioVec.iov_base = GetGPRBuffer();
276 ioVec.iov_len = GetGPRSize();
277
278 error = ReadRegisterSet(&ioVec, GetGPRSize(), NT_PRSTATUS);
279
280 if (error.Success())
281 m_gpr_is_valid = true;
282
283 return error;
284}
285
286Status NativeRegisterContextLinux_loongarch64::WriteGPR() {
287 Status error = ReadGPR();
288 if (error.Fail())
289 return error;
290
291 struct iovec ioVec;
292 ioVec.iov_base = GetGPRBuffer();
293 ioVec.iov_len = GetGPRSize();
294
295 m_gpr_is_valid = false;
296
297 return WriteRegisterSet(&ioVec, GetGPRSize(), NT_PRSTATUS);
298}
299
300Status NativeRegisterContextLinux_loongarch64::ReadFPR() {
302
303 if (m_fpu_is_valid)
304 return error;
305
306 struct iovec ioVec;
307 ioVec.iov_base = GetFPRBuffer();
308 ioVec.iov_len = GetFPRSize();
309
310 error = ReadRegisterSet(&ioVec, GetFPRSize(), NT_FPREGSET);
311
312 if (error.Success())
313 m_fpu_is_valid = true;
314
315 return error;
316}
317
318Status NativeRegisterContextLinux_loongarch64::WriteFPR() {
319 Status error = ReadFPR();
320 if (error.Fail())
321 return error;
322
323 struct iovec ioVec;
324 ioVec.iov_base = GetFPRBuffer();
325 ioVec.iov_len = GetFPRSize();
326
327 m_fpu_is_valid = false;
328
329 return WriteRegisterSet(&ioVec, GetFPRSize(), NT_FPREGSET);
330}
331
332void NativeRegisterContextLinux_loongarch64::InvalidateAllRegisters() {
333 m_gpr_is_valid = false;
334 m_fpu_is_valid = false;
335}
336
337uint32_t NativeRegisterContextLinux_loongarch64::CalculateFprOffset(
338 const RegisterInfo *reg_info) const {
339 return reg_info->byte_offset - GetGPRSize();
340}
341
342std::vector<uint32_t>
343NativeRegisterContextLinux_loongarch64::GetExpeditedRegisters(
344 ExpeditedRegs expType) const {
345 std::vector<uint32_t> expedited_reg_nums =
346 NativeRegisterContext::GetExpeditedRegisters(expType);
347
348 return expedited_reg_nums;
349}
350
351llvm::Error NativeRegisterContextLinux_loongarch64::ReadHardwareDebugInfo() {
352 if (!m_refresh_hwdebug_info)
353 return llvm::Error::success();
354
355 ::pid_t tid = m_thread.GetID();
356
357 int regset = NT_LOONGARCH_HW_WATCH;
358 struct iovec ioVec;
359 struct user_watch_state dreg_state;
361
362 ioVec.iov_base = &dreg_state;
363 ioVec.iov_len = sizeof(dreg_state);
364 error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, tid, &regset,
365 &ioVec, ioVec.iov_len);
366 if (error.Fail())
367 return error.ToError();
368
369 m_max_hwp_supported = dreg_state.dbg_info & 0x3f;
370
371 regset = NT_LOONGARCH_HW_BREAK;
372 error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, tid, &regset,
373 &ioVec, ioVec.iov_len);
374 if (error.Fail())
375 return error.ToError();
376
377 m_max_hbp_supported = dreg_state.dbg_info & 0x3f;
378
379 m_refresh_hwdebug_info = false;
380
381 return llvm::Error::success();
382}
383
384llvm::Error NativeRegisterContextLinux_loongarch64::WriteHardwareDebugRegs(
385 DREGType hwbType) {
386 struct iovec ioVec;
387 struct user_watch_state dreg_state;
388 int regset;
389
390 memset(&dreg_state, 0, sizeof(dreg_state));
391 ioVec.iov_base = &dreg_state;
392
393 switch (hwbType) {
394 case eDREGTypeWATCH:
395 regset = NT_LOONGARCH_HW_WATCH;
396 ioVec.iov_len = sizeof(dreg_state.dbg_info) +
397 (sizeof(dreg_state.dbg_regs[0]) * m_max_hwp_supported);
398
399 for (uint32_t i = 0; i < m_max_hwp_supported; i++) {
400 dreg_state.dbg_regs[i].addr = m_hwp_regs[i].address;
401 dreg_state.dbg_regs[i].ctrl = m_hwp_regs[i].control;
402 }
403 break;
404 case eDREGTypeBREAK:
405 regset = NT_LOONGARCH_HW_BREAK;
406 ioVec.iov_len = sizeof(dreg_state.dbg_info) +
407 (sizeof(dreg_state.dbg_regs[0]) * m_max_hbp_supported);
408
409 for (uint32_t i = 0; i < m_max_hbp_supported; i++) {
410 dreg_state.dbg_regs[i].addr = m_hbp_regs[i].address;
411 dreg_state.dbg_regs[i].ctrl = m_hbp_regs[i].control;
412 }
413 break;
414 }
415
416 return NativeProcessLinux::PtraceWrapper(PTRACE_SETREGSET, m_thread.GetID(),
417 &regset, &ioVec, ioVec.iov_len)
418 .ToError();
419}
420#endif // defined(__loongarch__) && __loongarch_grlen == 64
static llvm::raw_ostream & error(Stream &strm)
#define PTRACE_SETREGSET
Definition: Ptrace.h:39
#define PTRACE_GETREGSET
Definition: Ptrace.h:36
#define REG_CONTEXT_SIZE
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)
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.