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