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
17#include "lldb/Host/HostInfo.h"
19#include "lldb/Utility/Log.h"
21#include "lldb/Utility/Status.h"
22
23// System includes - They have to be included after framework includes because
24// they define some macros which collide with variable names in other modules.
25#include <elf.h>
26#include <sys/ptrace.h>
27#include <sys/uio.h>
28
29#ifndef PTRACE_GETREGSET
30#define PTRACE_GETREGSET 0x4204
31#endif
32
33#ifndef PTRACE_SETREGSET
34#define PTRACE_SETREGSET 0x4205
35#endif
36
37// LoongArch SIMD eXtension registers
38#ifndef NT_LOONGARCH_LSX
39#define NT_LOONGARCH_LSX 0xa02
40#endif
41
42// LoongArch Advanced SIMD eXtension registers
43#ifndef NT_LOONGARCH_LASX
44#define NT_LOONGARCH_LASX 0xa03
45#endif
46
47// LoongArch hardware breakpoint registers
48#ifndef NT_LOONGARCH_HW_BREAK
49#define NT_LOONGARCH_HW_BREAK 0xa05
50#endif
51
52// LoongArch hardware watchpoint registers
53#ifndef NT_LOONGARCH_HW_WATCH
54#define NT_LOONGARCH_HW_WATCH 0xa06
55#endif
56
57#define REG_CONTEXT_SIZE \
58 (GetGPRSize() + GetFPRSize() + sizeof(m_lsx) + sizeof(m_lasx))
59
60// ptrace has a struct type user_watch_state, which was replaced by
61// user_watch_state_v2 when more watchpoints were added, so this file
62// may be built on systems with one or both in the system headers.
63// The type below has the same layout as user_watch_state_v2 but will
64// not clash with that name if it exists. We can use the v2 layout even
65// on old kernels as we will only see 8 watchpoints and the kernel will
66// truncate any extra data we send to it.
67struct loongarch_user_watch_state {
68 uint64_t dbg_info;
69 struct {
70 uint64_t addr;
71 uint64_t mask;
72 uint32_t ctrl;
73 uint32_t pad;
74 } dbg_regs[14];
75};
76
77using namespace lldb;
78using namespace lldb_private;
79using namespace lldb_private::process_linux;
80
81std::unique_ptr<NativeRegisterContextLinux>
83 const ArchSpec &target_arch, NativeThreadLinux &native_thread) {
84 switch (target_arch.GetMachine()) {
85 case llvm::Triple::loongarch64: {
86 Flags opt_regsets;
87 auto register_info_up = std::make_unique<RegisterInfoPOSIX_loongarch64>(
88 target_arch, opt_regsets);
89 return std::make_unique<NativeRegisterContextLinux_loongarch64>(
90 target_arch, native_thread, std::move(register_info_up));
91 }
92 default:
93 llvm_unreachable("have no register context for architecture");
94 }
95}
96
97llvm::Expected<ArchSpec>
99 return HostInfo::GetArchitecture();
100}
101
102NativeRegisterContextLinux_loongarch64::NativeRegisterContextLinux_loongarch64(
103 const ArchSpec &target_arch, NativeThreadProtocol &native_thread,
104 std::unique_ptr<RegisterInfoPOSIX_loongarch64> register_info_up)
105 : NativeRegisterContextRegisterInfo(native_thread,
106 register_info_up.release()),
107 NativeRegisterContextLinux(native_thread) {
108 ::memset(&m_fpr, 0, sizeof(m_fpr));
109 ::memset(&m_gpr, 0, sizeof(m_gpr));
110 ::memset(&m_lsx, 0, sizeof(m_lsx));
111 ::memset(&m_lasx, 0, sizeof(m_lasx));
112
113 // Refer to:
114 // https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#control-and-status-registers-related-to-watchpoints
115 // 14 is just a maximum value, query hardware for actual watchpoint count.
116 m_max_hwp_supported = 14;
117 m_max_hbp_supported = 14;
118 m_refresh_hwdebug_info = true;
119
120 m_gpr_is_valid = false;
121 m_fpu_is_valid = false;
122 m_lsx_is_valid = false;
123 m_lasx_is_valid = false;
124}
125
127NativeRegisterContextLinux_loongarch64::GetRegisterInfo() const {
128 return static_cast<const RegisterInfoPOSIX_loongarch64 &>(
130}
131
132uint32_t NativeRegisterContextLinux_loongarch64::GetRegisterSetCount() const {
133 return GetRegisterInfo().GetRegisterSetCount();
134}
135
136const RegisterSet *NativeRegisterContextLinux_loongarch64::GetRegisterSet(
137 uint32_t set_index) const {
138 return GetRegisterInfo().GetRegisterSet(set_index);
139}
140
141uint32_t NativeRegisterContextLinux_loongarch64::GetUserRegisterCount() const {
142 uint32_t count = 0;
143 for (uint32_t set_index = 0; set_index < GetRegisterSetCount(); ++set_index)
144 count += GetRegisterSet(set_index)->num_registers;
145 return count;
146}
147
148Status NativeRegisterContextLinux_loongarch64::ReadRegister(
149 const RegisterInfo *reg_info, RegisterValue &reg_value) {
151
152 if (!reg_info) {
153 error = Status::FromErrorString("reg_info NULL");
154 return error;
155 }
156
157 const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
158
159 if (reg == LLDB_INVALID_REGNUM)
161 "no lldb regnum for %s",
162 reg_info && reg_info->name ? reg_info->name : "<unknown register>");
163
164 uint8_t *src = nullptr;
165 uint32_t offset = LLDB_INVALID_INDEX32;
166
167 if (IsGPR(reg)) {
168 error = ReadGPR();
169 if (error.Fail())
170 return error;
171
172 offset = reg_info->byte_offset;
173 assert(offset < GetGPRSize());
174 src = (uint8_t *)GetGPRBuffer() + offset;
175
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 src = (uint8_t *)GetFPRBuffer() + offset;
184 } else if (IsLSX(reg)) {
185 error = ReadLSX();
186 if (error.Fail())
187 return error;
188
189 offset = CalculateLsxOffset(reg_info);
190 assert(offset < sizeof(m_lsx));
191 src = (uint8_t *)&m_lsx + offset;
192 } else if (IsLASX(reg)) {
193 error = ReadLASX();
194 if (error.Fail())
195 return error;
196
197 offset = CalculateLasxOffset(reg_info);
198 assert(offset < sizeof(m_lasx));
199 src = (uint8_t *)&m_lasx + offset;
200 } else
202 "failed - register wasn't recognized to be a GPR or an FPR, "
203 "write strategy unknown");
204
205 reg_value.SetFromMemoryData(*reg_info, src, reg_info->byte_size,
207
208 return error;
209}
210
211Status NativeRegisterContextLinux_loongarch64::WriteRegister(
212 const RegisterInfo *reg_info, const RegisterValue &reg_value) {
214
215 if (!reg_info)
216 return Status::FromErrorString("reg_info NULL");
217
218 const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
219
220 if (reg == LLDB_INVALID_REGNUM)
222 "no lldb regnum for %s",
223 reg_info->name != nullptr ? reg_info->name : "<unknown register>");
224
225 uint8_t *dst = nullptr;
226 uint32_t offset = LLDB_INVALID_INDEX32;
227
228 if (IsGPR(reg)) {
229 error = ReadGPR();
230 if (error.Fail())
231 return error;
232
233 assert(reg_info->byte_offset < GetGPRSize());
234 dst = (uint8_t *)GetGPRBuffer() + reg_info->byte_offset;
235 ::memcpy(dst, reg_value.GetBytes(), reg_info->byte_size);
236
237 return WriteGPR();
238 } else if (IsFPR(reg)) {
239 error = ReadFPR();
240 if (error.Fail())
241 return error;
242
243 offset = CalculateFprOffset(reg_info);
244 assert(offset < GetFPRSize());
245 dst = (uint8_t *)GetFPRBuffer() + offset;
246 ::memcpy(dst, reg_value.GetBytes(), reg_info->byte_size);
247
248 return WriteFPR();
249 } else if (IsLSX(reg)) {
250 error = ReadLSX();
251 if (error.Fail())
252 return error;
253
254 offset = CalculateLsxOffset(reg_info);
255 assert(offset < sizeof(m_lsx));
256 dst = (uint8_t *)&m_lsx + offset;
257 ::memcpy(dst, reg_value.GetBytes(), reg_info->byte_size);
258
259 return WriteLSX();
260 } else if (IsLASX(reg)) {
261 error = ReadLASX();
262 if (error.Fail())
263 return error;
264
265 offset = CalculateLasxOffset(reg_info);
266 assert(offset < sizeof(m_lasx));
267 dst = (uint8_t *)&m_lasx + offset;
268 ::memcpy(dst, reg_value.GetBytes(), reg_info->byte_size);
269
270 return WriteLASX();
271 }
272
273 return Status::FromErrorString("Failed to write register value");
274}
275
276Status NativeRegisterContextLinux_loongarch64::ReadAllRegisterValues(
279
280 data_sp.reset(new DataBufferHeap(REG_CONTEXT_SIZE, 0));
281
282 error = ReadGPR();
283 if (error.Fail())
284 return error;
285
286 error = ReadFPR();
287 if (error.Fail())
288 return error;
289
290 error = ReadLSX();
291 if (error.Fail())
292 return error;
293
294 error = ReadLASX();
295 if (error.Fail())
296 return error;
297
298 uint8_t *dst = data_sp->GetBytes();
299 ::memcpy(dst, GetGPRBuffer(), GetGPRSize());
300 dst += GetGPRSize();
301 ::memcpy(dst, GetFPRBuffer(), GetFPRSize());
302 dst += GetFPRSize();
303 ::memcpy(dst, &m_lsx, sizeof(m_lsx));
304 dst += sizeof(m_lsx);
305 ::memcpy(dst, &m_lasx, sizeof(m_lasx));
306
307 return error;
308}
309
310Status NativeRegisterContextLinux_loongarch64::WriteAllRegisterValues(
311 const lldb::DataBufferSP &data_sp) {
313
314 if (!data_sp) {
316 "NativeRegisterContextLinux_loongarch64::%s invalid data_sp provided",
317 __FUNCTION__);
318 return error;
319 }
320
321 if (data_sp->GetByteSize() != REG_CONTEXT_SIZE) {
323 "NativeRegisterContextLinux_loongarch64::%s data_sp contained "
324 "mismatched data size, expected %" PRIu64 ", actual %" PRIu64,
325 __FUNCTION__, REG_CONTEXT_SIZE, data_sp->GetByteSize());
326 return error;
327 }
328
329 const uint8_t *src = data_sp->GetBytes();
330 if (src == nullptr) {
332 "NativeRegisterContextLinux_loongarch64::%s "
333 "DataBuffer::GetBytes() returned a null "
334 "pointer",
335 __FUNCTION__);
336 return error;
337 }
338 ::memcpy(GetGPRBuffer(), src, GetRegisterInfoInterface().GetGPRSize());
339
340 error = WriteGPR();
341 if (error.Fail())
342 return error;
343
344 src += GetRegisterInfoInterface().GetGPRSize();
345 ::memcpy(GetFPRBuffer(), src, GetFPRSize());
346 m_fpu_is_valid = true;
347 error = WriteFPR();
348 if (error.Fail())
349 return error;
350
351 // Currently, we assume that LoongArch always support LASX.
352 // TODO: check whether LSX/LASX exists.
353 src += GetFPRSize();
354 ::memcpy(&m_lsx, src, sizeof(m_lsx));
355 m_lsx_is_valid = true;
356 error = WriteLSX();
357 if (error.Fail())
358 return error;
359
360 src += sizeof(m_lsx);
361 ::memcpy(&m_lasx, src, sizeof(m_lasx));
362 m_lasx_is_valid = true;
363 error = WriteLASX();
364 if (error.Fail())
365 return error;
366
367 return error;
368}
369
370bool NativeRegisterContextLinux_loongarch64::IsGPR(unsigned reg) const {
371 return GetRegisterInfo().GetRegisterSetFromRegisterIndex(reg) ==
373}
374
375bool NativeRegisterContextLinux_loongarch64::IsFPR(unsigned reg) const {
376 return GetRegisterInfo().GetRegisterSetFromRegisterIndex(reg) ==
378}
379
380bool NativeRegisterContextLinux_loongarch64::IsLSX(unsigned reg) const {
381 return GetRegisterInfo().GetRegisterSetFromRegisterIndex(reg) ==
383}
384
385bool NativeRegisterContextLinux_loongarch64::IsLASX(unsigned reg) const {
386 return GetRegisterInfo().GetRegisterSetFromRegisterIndex(reg) ==
388}
389
390Status NativeRegisterContextLinux_loongarch64::ReadGPR() {
392
393 if (m_gpr_is_valid)
394 return error;
395
396 struct iovec ioVec;
397 ioVec.iov_base = GetGPRBuffer();
398 ioVec.iov_len = GetGPRSize();
399
400 error = ReadRegisterSet(&ioVec, GetGPRSize(), NT_PRSTATUS);
401
402 if (error.Success())
403 m_gpr_is_valid = true;
404
405 return error;
406}
407
408Status NativeRegisterContextLinux_loongarch64::WriteGPR() {
409 Status error = ReadGPR();
410 if (error.Fail())
411 return error;
412
413 struct iovec ioVec;
414 ioVec.iov_base = GetGPRBuffer();
415 ioVec.iov_len = GetGPRSize();
416
417 m_gpr_is_valid = false;
418
419 return WriteRegisterSet(&ioVec, GetGPRSize(), NT_PRSTATUS);
420}
421
422Status NativeRegisterContextLinux_loongarch64::ReadFPR() {
424
425 if (m_fpu_is_valid)
426 return error;
427
428 struct iovec ioVec;
429 ioVec.iov_base = GetFPRBuffer();
430 ioVec.iov_len = GetFPRSize();
431
432 error = ReadRegisterSet(&ioVec, GetFPRSize(), NT_FPREGSET);
433
434 if (error.Success())
435 m_fpu_is_valid = true;
436
437 return error;
438}
439
440Status NativeRegisterContextLinux_loongarch64::WriteFPR() {
441 Status error = ReadFPR();
442 if (error.Fail())
443 return error;
444
445 struct iovec ioVec;
446 ioVec.iov_base = GetFPRBuffer();
447 ioVec.iov_len = GetFPRSize();
448
449 m_fpu_is_valid = false;
450 m_lsx_is_valid = false;
451 m_lasx_is_valid = false;
452
453 return WriteRegisterSet(&ioVec, GetFPRSize(), NT_FPREGSET);
454}
455
456Status NativeRegisterContextLinux_loongarch64::ReadLSX() {
458
459 if (m_lsx_is_valid)
460 return error;
461
462 struct iovec ioVec;
463 ioVec.iov_base = &m_lsx;
464 ioVec.iov_len = sizeof(m_lsx);
465
466 error = ReadRegisterSet(&ioVec, sizeof(m_lsx), NT_LOONGARCH_LSX);
467
468 if (error.Success())
469 m_lsx_is_valid = true;
470
471 return error;
472}
473
474Status NativeRegisterContextLinux_loongarch64::WriteLSX() {
475 Status error = ReadLSX();
476 if (error.Fail())
477 return error;
478
479 struct iovec ioVec;
480 ioVec.iov_base = &m_lsx;
481 ioVec.iov_len = sizeof(m_lsx);
482
483 m_fpu_is_valid = false;
484 m_lsx_is_valid = false;
485 m_lasx_is_valid = false;
486
487 return WriteRegisterSet(&ioVec, sizeof(m_lsx), NT_LOONGARCH_LSX);
488}
489
490Status NativeRegisterContextLinux_loongarch64::ReadLASX() {
492
493 if (m_lasx_is_valid)
494 return error;
495
496 struct iovec ioVec;
497 ioVec.iov_base = &m_lasx;
498 ioVec.iov_len = sizeof(m_lasx);
499
500 error = ReadRegisterSet(&ioVec, sizeof(m_lasx), NT_LOONGARCH_LASX);
501
502 if (error.Success())
503 m_lasx_is_valid = true;
504
505 return error;
506}
507
508Status NativeRegisterContextLinux_loongarch64::WriteLASX() {
509 Status error = ReadLASX();
510 if (error.Fail())
511 return error;
512
513 struct iovec ioVec;
514 ioVec.iov_base = &m_lasx;
515 ioVec.iov_len = sizeof(m_lasx);
516
517 m_fpu_is_valid = false;
518 m_lsx_is_valid = false;
519 m_lasx_is_valid = false;
520
521 return WriteRegisterSet(&ioVec, sizeof(m_lasx), NT_LOONGARCH_LASX);
522}
523
524void NativeRegisterContextLinux_loongarch64::InvalidateAllRegisters() {
525 m_gpr_is_valid = false;
526 m_fpu_is_valid = false;
527 m_lsx_is_valid = false;
528 m_lasx_is_valid = false;
529}
530
531uint32_t NativeRegisterContextLinux_loongarch64::CalculateFprOffset(
532 const RegisterInfo *reg_info) const {
533 return reg_info->byte_offset - GetGPRSize();
534}
535
536uint32_t NativeRegisterContextLinux_loongarch64::CalculateLsxOffset(
537 const RegisterInfo *reg_info) const {
538 return reg_info->byte_offset - GetGPRSize() - sizeof(m_fpr);
539}
540
541uint32_t NativeRegisterContextLinux_loongarch64::CalculateLasxOffset(
542 const RegisterInfo *reg_info) const {
543 return reg_info->byte_offset - GetGPRSize() - sizeof(m_fpr) - sizeof(m_lsx);
544}
545
546std::vector<uint32_t>
547NativeRegisterContextLinux_loongarch64::GetExpeditedRegisters(
548 ExpeditedRegs expType) const {
549 std::vector<uint32_t> expedited_reg_nums =
551
552 return expedited_reg_nums;
553}
554
555llvm::Error NativeRegisterContextLinux_loongarch64::ReadHardwareDebugInfo() {
556 if (!m_refresh_hwdebug_info)
557 return llvm::Error::success();
558
559 ::pid_t tid = m_thread.GetID();
560
561 int regset = NT_LOONGARCH_HW_WATCH;
562 struct iovec ioVec;
563 struct loongarch_user_watch_state dreg_state;
565
566 ioVec.iov_base = &dreg_state;
567 ioVec.iov_len = sizeof(dreg_state);
569 &ioVec, ioVec.iov_len);
570 if (error.Fail())
571 return error.ToError();
572
573 m_max_hwp_supported = dreg_state.dbg_info & 0x3f;
574
575 regset = NT_LOONGARCH_HW_BREAK;
577 &ioVec, ioVec.iov_len);
578 if (error.Fail())
579 return error.ToError();
580
581 m_max_hbp_supported = dreg_state.dbg_info & 0x3f;
582
583 m_refresh_hwdebug_info = false;
584
585 return llvm::Error::success();
586}
587
588llvm::Error NativeRegisterContextLinux_loongarch64::WriteHardwareDebugRegs(
589 DREGType hwbType) {
590 struct iovec ioVec;
591 struct loongarch_user_watch_state dreg_state;
592 int regset;
593
594 memset(&dreg_state, 0, sizeof(dreg_state));
595 ioVec.iov_base = &dreg_state;
596
597 switch (hwbType) {
598 case eDREGTypeWATCH:
599 regset = NT_LOONGARCH_HW_WATCH;
600 ioVec.iov_len = sizeof(dreg_state.dbg_info) +
601 (sizeof(dreg_state.dbg_regs[0]) * m_max_hwp_supported);
602
603 for (uint32_t i = 0; i < m_max_hwp_supported; i++) {
604 dreg_state.dbg_regs[i].addr = m_hwp_regs[i].address;
605 dreg_state.dbg_regs[i].ctrl = m_hwp_regs[i].control;
606 }
607 break;
608 case eDREGTypeBREAK:
609 regset = NT_LOONGARCH_HW_BREAK;
610 ioVec.iov_len = sizeof(dreg_state.dbg_info) +
611 (sizeof(dreg_state.dbg_regs[0]) * m_max_hbp_supported);
612
613 for (uint32_t i = 0; i < m_max_hbp_supported; i++) {
614 dreg_state.dbg_regs[i].addr = m_hbp_regs[i].address;
615 dreg_state.dbg_regs[i].ctrl = m_hbp_regs[i].control;
616 }
617 break;
618 }
619
621 &regset, &ioVec, ioVec.iov_len)
622 .ToError();
623}
624#endif // defined(__loongarch__) && __loongarch_grlen == 64
static llvm::raw_ostream & error(Stream &strm)
#define PTRACE_SETREGSET
#define PTRACE_GETREGSET
#define REG_CONTEXT_SIZE
A subclass of DataBuffer that stores a data buffer on the heap.
virtual std::vector< uint32_t > GetExpeditedRegisters(ExpeditedRegs expType) const
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
static Status FromErrorStringWithFormat(const char *format,...) __attribute__((format(printf
Definition Status.cpp:106
static Status FromErrorString(const char *str)
Definition Status.h:141
static Status PtraceWrapper(int req, lldb::pid_t pid, void *addr=nullptr, void *data=nullptr, size_t data_size=0, long *result=nullptr)
}
static std::unique_ptr< NativeRegisterContextLinux > CreateHostNativeRegisterContextLinux(const ArchSpec &target_arch, NativeThreadLinux &native_thread)
static llvm::Expected< ArchSpec > DetermineArchitecture(lldb::tid_t tid)
#define LLDB_INVALID_INDEX32
#define LLDB_INVALID_REGNUM
A class that represents a running process on the host machine.
uint64_t pid_t
Definition lldb-types.h:84
std::shared_ptr< lldb_private::DataBuffer > DataBufferSP
std::shared_ptr< lldb_private::WritableDataBuffer > WritableDataBufferSP
uint64_t tid_t
Definition lldb-types.h:85
@ 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.