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 ::memset(&m_hwp_regs, 0, sizeof(m_hwp_regs));
107 ::memset(&m_hbp_regs, 0, sizeof(m_hbp_regs));
108
109 // Refer to:
110 // https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#control-and-status-registers-related-to-watchpoints
111 // 14 is just a maximum value, query hardware for actual watchpoint count.
112 m_max_hwp_supported = 14;
113 m_max_hbp_supported = 14;
114 m_refresh_hwdebug_info = true;
115
116 m_gpr_is_valid = false;
117 m_fpu_is_valid = false;
118 m_lsx_is_valid = false;
119 m_lasx_is_valid = false;
120}
121
123NativeRegisterContextLinux_loongarch64::GetRegisterInfo() const {
124 return static_cast<const RegisterInfoPOSIX_loongarch64 &>(
126}
127
128uint32_t NativeRegisterContextLinux_loongarch64::GetRegisterSetCount() const {
129 return GetRegisterInfo().GetRegisterSetCount();
130}
131
132const RegisterSet *NativeRegisterContextLinux_loongarch64::GetRegisterSet(
133 uint32_t set_index) const {
134 return GetRegisterInfo().GetRegisterSet(set_index);
135}
136
137uint32_t NativeRegisterContextLinux_loongarch64::GetUserRegisterCount() const {
138 uint32_t count = 0;
139 for (uint32_t set_index = 0; set_index < GetRegisterSetCount(); ++set_index)
140 count += GetRegisterSet(set_index)->num_registers;
141 return count;
142}
143
144Status NativeRegisterContextLinux_loongarch64::ReadRegister(
145 const RegisterInfo *reg_info, RegisterValue &reg_value) {
147
148 if (!reg_info) {
149 error = Status::FromErrorString("reg_info NULL");
150 return error;
151 }
152
153 const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
154
155 if (reg == LLDB_INVALID_REGNUM)
157 "no lldb regnum for %s",
158 reg_info && reg_info->name ? reg_info->name : "<unknown register>");
159
160 uint8_t *src = nullptr;
161 uint32_t offset = LLDB_INVALID_INDEX32;
162
163 if (IsGPR(reg)) {
164 error = ReadGPR();
165 if (error.Fail())
166 return error;
167
168 offset = reg_info->byte_offset;
169 assert(offset < GetGPRSize());
170 src = (uint8_t *)GetGPRBuffer() + offset;
171
172 } else if (IsFPR(reg)) {
173 error = ReadFPR();
174 if (error.Fail())
175 return error;
176
177 offset = CalculateFprOffset(reg_info);
178 assert(offset < GetFPRSize());
179 src = (uint8_t *)GetFPRBuffer() + offset;
180 } else if (IsLSX(reg)) {
181 error = ReadLSX();
182 if (error.Fail())
183 return error;
184
185 offset = CalculateLsxOffset(reg_info);
186 assert(offset < sizeof(m_lsx));
187 src = (uint8_t *)&m_lsx + offset;
188 } else if (IsLASX(reg)) {
189 error = ReadLASX();
190 if (error.Fail())
191 return error;
192
193 offset = CalculateLasxOffset(reg_info);
194 assert(offset < sizeof(m_lasx));
195 src = (uint8_t *)&m_lasx + offset;
196 } else
198 "failed - register wasn't recognized to be a GPR or an FPR, "
199 "write strategy unknown");
200
201 reg_value.SetFromMemoryData(*reg_info, src, reg_info->byte_size,
203
204 return error;
205}
206
207Status NativeRegisterContextLinux_loongarch64::WriteRegister(
208 const RegisterInfo *reg_info, const RegisterValue &reg_value) {
210
211 if (!reg_info)
212 return Status::FromErrorString("reg_info NULL");
213
214 const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
215
216 if (reg == LLDB_INVALID_REGNUM)
218 "no lldb regnum for %s",
219 reg_info->name != nullptr ? reg_info->name : "<unknown register>");
220
221 uint8_t *dst = nullptr;
222 uint32_t offset = LLDB_INVALID_INDEX32;
223
224 if (IsGPR(reg)) {
225 error = ReadGPR();
226 if (error.Fail())
227 return error;
228
229 assert(reg_info->byte_offset < GetGPRSize());
230 dst = (uint8_t *)GetGPRBuffer() + reg_info->byte_offset;
231 ::memcpy(dst, reg_value.GetBytes(), reg_info->byte_size);
232
233 return WriteGPR();
234 } else if (IsFPR(reg)) {
235 error = ReadFPR();
236 if (error.Fail())
237 return error;
238
239 offset = CalculateFprOffset(reg_info);
240 assert(offset < GetFPRSize());
241 dst = (uint8_t *)GetFPRBuffer() + offset;
242 ::memcpy(dst, reg_value.GetBytes(), reg_info->byte_size);
243
244 return WriteFPR();
245 } else if (IsLSX(reg)) {
246 error = ReadLSX();
247 if (error.Fail())
248 return error;
249
250 offset = CalculateLsxOffset(reg_info);
251 assert(offset < sizeof(m_lsx));
252 dst = (uint8_t *)&m_lsx + offset;
253 ::memcpy(dst, reg_value.GetBytes(), reg_info->byte_size);
254
255 return WriteLSX();
256 } else if (IsLASX(reg)) {
257 error = ReadLASX();
258 if (error.Fail())
259 return error;
260
261 offset = CalculateLasxOffset(reg_info);
262 assert(offset < sizeof(m_lasx));
263 dst = (uint8_t *)&m_lasx + offset;
264 ::memcpy(dst, reg_value.GetBytes(), reg_info->byte_size);
265
266 return WriteLASX();
267 }
268
269 return Status::FromErrorString("Failed to write register value");
270}
271
272Status NativeRegisterContextLinux_loongarch64::ReadAllRegisterValues(
275
276 data_sp.reset(new DataBufferHeap(REG_CONTEXT_SIZE, 0));
277
278 error = ReadGPR();
279 if (error.Fail())
280 return error;
281
282 error = ReadFPR();
283 if (error.Fail())
284 return error;
285
286 error = ReadLSX();
287 if (error.Fail())
288 return error;
289
290 error = ReadLASX();
291 if (error.Fail())
292 return error;
293
294 uint8_t *dst = data_sp->GetBytes();
295 ::memcpy(dst, GetGPRBuffer(), GetGPRSize());
296 dst += GetGPRSize();
297 ::memcpy(dst, GetFPRBuffer(), GetFPRSize());
298 dst += GetFPRSize();
299 ::memcpy(dst, &m_lsx, sizeof(m_lsx));
300 dst += sizeof(m_lsx);
301 ::memcpy(dst, &m_lasx, sizeof(m_lasx));
302
303 return error;
304}
305
306Status NativeRegisterContextLinux_loongarch64::WriteAllRegisterValues(
307 const lldb::DataBufferSP &data_sp) {
309
310 if (!data_sp) {
312 "NativeRegisterContextLinux_loongarch64::%s invalid data_sp provided",
313 __FUNCTION__);
314 return error;
315 }
316
317 if (data_sp->GetByteSize() != REG_CONTEXT_SIZE) {
319 "NativeRegisterContextLinux_loongarch64::%s data_sp contained "
320 "mismatched data size, expected %" PRIu64 ", actual %" PRIu64,
321 __FUNCTION__, REG_CONTEXT_SIZE, data_sp->GetByteSize());
322 return error;
323 }
324
325 const uint8_t *src = data_sp->GetBytes();
326 if (src == nullptr) {
328 "NativeRegisterContextLinux_loongarch64::%s "
329 "DataBuffer::GetBytes() returned a null "
330 "pointer",
331 __FUNCTION__);
332 return error;
333 }
334 ::memcpy(GetGPRBuffer(), src, GetRegisterInfoInterface().GetGPRSize());
335
336 error = WriteGPR();
337 if (error.Fail())
338 return error;
339
340 src += GetRegisterInfoInterface().GetGPRSize();
341 ::memcpy(GetFPRBuffer(), src, GetFPRSize());
342 m_fpu_is_valid = true;
343 error = WriteFPR();
344 if (error.Fail())
345 return error;
346
347 // Currently, we assume that LoongArch always support LASX.
348 // TODO: check whether LSX/LASX exists.
349 src += GetFPRSize();
350 ::memcpy(&m_lsx, src, sizeof(m_lsx));
351 m_lsx_is_valid = true;
352 error = WriteLSX();
353 if (error.Fail())
354 return error;
355
356 src += sizeof(m_lsx);
357 ::memcpy(&m_lasx, src, sizeof(m_lasx));
358 m_lasx_is_valid = true;
359 error = WriteLASX();
360 if (error.Fail())
361 return error;
362
363 return error;
364}
365
366bool NativeRegisterContextLinux_loongarch64::IsGPR(unsigned reg) const {
367 return GetRegisterInfo().GetRegisterSetFromRegisterIndex(reg) ==
369}
370
371bool NativeRegisterContextLinux_loongarch64::IsFPR(unsigned reg) const {
372 return GetRegisterInfo().GetRegisterSetFromRegisterIndex(reg) ==
374}
375
376bool NativeRegisterContextLinux_loongarch64::IsLSX(unsigned reg) const {
377 return GetRegisterInfo().GetRegisterSetFromRegisterIndex(reg) ==
379}
380
381bool NativeRegisterContextLinux_loongarch64::IsLASX(unsigned reg) const {
382 return GetRegisterInfo().GetRegisterSetFromRegisterIndex(reg) ==
384}
385
386Status NativeRegisterContextLinux_loongarch64::ReadGPR() {
388
389 if (m_gpr_is_valid)
390 return error;
391
392 struct iovec ioVec;
393 ioVec.iov_base = GetGPRBuffer();
394 ioVec.iov_len = GetGPRSize();
395
396 error = ReadRegisterSet(&ioVec, GetGPRSize(), NT_PRSTATUS);
397
398 if (error.Success())
399 m_gpr_is_valid = true;
400
401 return error;
402}
403
404Status NativeRegisterContextLinux_loongarch64::WriteGPR() {
405 Status error = ReadGPR();
406 if (error.Fail())
407 return error;
408
409 struct iovec ioVec;
410 ioVec.iov_base = GetGPRBuffer();
411 ioVec.iov_len = GetGPRSize();
412
413 m_gpr_is_valid = false;
414
415 return WriteRegisterSet(&ioVec, GetGPRSize(), NT_PRSTATUS);
416}
417
418Status NativeRegisterContextLinux_loongarch64::ReadFPR() {
420
421 if (m_fpu_is_valid)
422 return error;
423
424 struct iovec ioVec;
425 ioVec.iov_base = GetFPRBuffer();
426 ioVec.iov_len = GetFPRSize();
427
428 error = ReadRegisterSet(&ioVec, GetFPRSize(), NT_FPREGSET);
429
430 if (error.Success())
431 m_fpu_is_valid = true;
432
433 return error;
434}
435
436Status NativeRegisterContextLinux_loongarch64::WriteFPR() {
437 Status error = ReadFPR();
438 if (error.Fail())
439 return error;
440
441 struct iovec ioVec;
442 ioVec.iov_base = GetFPRBuffer();
443 ioVec.iov_len = GetFPRSize();
444
445 m_fpu_is_valid = false;
446 m_lsx_is_valid = false;
447 m_lasx_is_valid = false;
448
449 return WriteRegisterSet(&ioVec, GetFPRSize(), NT_FPREGSET);
450}
451
452Status NativeRegisterContextLinux_loongarch64::ReadLSX() {
454
455 if (m_lsx_is_valid)
456 return error;
457
458 struct iovec ioVec;
459 ioVec.iov_base = &m_lsx;
460 ioVec.iov_len = sizeof(m_lsx);
461
462 error = ReadRegisterSet(&ioVec, sizeof(m_lsx), NT_LOONGARCH_LSX);
463
464 if (error.Success())
465 m_lsx_is_valid = true;
466
467 return error;
468}
469
470Status NativeRegisterContextLinux_loongarch64::WriteLSX() {
471 Status error = ReadLSX();
472 if (error.Fail())
473 return error;
474
475 struct iovec ioVec;
476 ioVec.iov_base = &m_lsx;
477 ioVec.iov_len = sizeof(m_lsx);
478
479 m_fpu_is_valid = false;
480 m_lsx_is_valid = false;
481 m_lasx_is_valid = false;
482
483 return WriteRegisterSet(&ioVec, sizeof(m_lsx), NT_LOONGARCH_LSX);
484}
485
486Status NativeRegisterContextLinux_loongarch64::ReadLASX() {
488
489 if (m_lasx_is_valid)
490 return error;
491
492 struct iovec ioVec;
493 ioVec.iov_base = &m_lasx;
494 ioVec.iov_len = sizeof(m_lasx);
495
496 error = ReadRegisterSet(&ioVec, sizeof(m_lasx), NT_LOONGARCH_LASX);
497
498 if (error.Success())
499 m_lasx_is_valid = true;
500
501 return error;
502}
503
504Status NativeRegisterContextLinux_loongarch64::WriteLASX() {
505 Status error = ReadLASX();
506 if (error.Fail())
507 return error;
508
509 struct iovec ioVec;
510 ioVec.iov_base = &m_lasx;
511 ioVec.iov_len = sizeof(m_lasx);
512
513 m_fpu_is_valid = false;
514 m_lsx_is_valid = false;
515 m_lasx_is_valid = false;
516
517 return WriteRegisterSet(&ioVec, sizeof(m_lasx), NT_LOONGARCH_LASX);
518}
519
520void NativeRegisterContextLinux_loongarch64::InvalidateAllRegisters() {
521 m_gpr_is_valid = false;
522 m_fpu_is_valid = false;
523 m_lsx_is_valid = false;
524 m_lasx_is_valid = false;
525}
526
527uint32_t NativeRegisterContextLinux_loongarch64::CalculateFprOffset(
528 const RegisterInfo *reg_info) const {
529 return reg_info->byte_offset - GetGPRSize();
530}
531
532uint32_t NativeRegisterContextLinux_loongarch64::CalculateLsxOffset(
533 const RegisterInfo *reg_info) const {
534 return reg_info->byte_offset - GetGPRSize() - sizeof(m_fpr);
535}
536
537uint32_t NativeRegisterContextLinux_loongarch64::CalculateLasxOffset(
538 const RegisterInfo *reg_info) const {
539 return reg_info->byte_offset - GetGPRSize() - sizeof(m_fpr) - sizeof(m_lsx);
540}
541
542std::vector<uint32_t>
543NativeRegisterContextLinux_loongarch64::GetExpeditedRegisters(
544 ExpeditedRegs expType) const {
545 std::vector<uint32_t> expedited_reg_nums =
547
548 return expedited_reg_nums;
549}
550
551llvm::Error NativeRegisterContextLinux_loongarch64::ReadHardwareDebugInfo() {
552 if (!m_refresh_hwdebug_info)
553 return llvm::Error::success();
554
555 ::pid_t tid = m_thread.GetID();
556
557 int regset = NT_LOONGARCH_HW_WATCH;
558 struct iovec ioVec;
559 struct loongarch_user_watch_state dreg_state;
561
562 ioVec.iov_base = &dreg_state;
563 ioVec.iov_len = sizeof(dreg_state);
565 &ioVec, ioVec.iov_len);
566 if (error.Fail())
567 return error.ToError();
568
569 m_max_hwp_supported = dreg_state.dbg_info & 0x3f;
570
571 regset = NT_LOONGARCH_HW_BREAK;
573 &ioVec, ioVec.iov_len);
574 if (error.Fail())
575 return error.ToError();
576
577 m_max_hbp_supported = dreg_state.dbg_info & 0x3f;
578
579 m_refresh_hwdebug_info = false;
580
581 return llvm::Error::success();
582}
583
584llvm::Error NativeRegisterContextLinux_loongarch64::WriteHardwareDebugRegs(
585 DREGType hwbType) {
586 struct iovec ioVec;
587 struct loongarch_user_watch_state dreg_state;
588 int regset;
589
590 memset(&dreg_state, 0, sizeof(dreg_state));
591 ioVec.iov_base = &dreg_state;
592
593 switch (hwbType) {
594 case eDREGTypeWATCH:
595 regset = NT_LOONGARCH_HW_WATCH;
596 ioVec.iov_len = sizeof(dreg_state.dbg_info) +
597 (sizeof(dreg_state.dbg_regs[0]) * m_max_hwp_supported);
598
599 for (uint32_t i = 0; i < m_max_hwp_supported; i++) {
600 dreg_state.dbg_regs[i].addr = m_hwp_regs[i].address;
601 dreg_state.dbg_regs[i].ctrl = m_hwp_regs[i].control;
602 }
603 break;
604 case eDREGTypeBREAK:
605 regset = NT_LOONGARCH_HW_BREAK;
606 ioVec.iov_len = sizeof(dreg_state.dbg_info) +
607 (sizeof(dreg_state.dbg_regs[0]) * m_max_hbp_supported);
608
609 for (uint32_t i = 0; i < m_max_hbp_supported; i++) {
610 dreg_state.dbg_regs[i].addr = m_hbp_regs[i].address;
611 dreg_state.dbg_regs[i].ctrl = m_hbp_regs[i].control;
612 }
613 break;
614 }
615
617 &regset, &ioVec, ioVec.iov_len)
618 .ToError();
619}
620#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.