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