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> |
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> |
52 | NativeRegisterContextLinux::DetermineArchitecture(lldb::tid_t tid) { |
53 | return HostInfo::GetArchitecture(); |
54 | } |
55 | |
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 & |
70 | NativeRegisterContextLinux_loongarch64::GetRegisterInfo() const { |
71 | return static_cast<const RegisterInfoPOSIX_loongarch64 &>( |
72 | NativeRegisterContextRegisterInfo::GetRegisterInfoInterface()); |
73 | } |
74 | |
75 | uint32_t NativeRegisterContextLinux_loongarch64::GetRegisterSetCount() const { |
76 | return GetRegisterInfo().GetRegisterSetCount(); |
77 | } |
78 | |
79 | const RegisterSet *NativeRegisterContextLinux_loongarch64::GetRegisterSet( |
80 | uint32_t set_index) const { |
81 | return GetRegisterInfo().GetRegisterSet(set_index); |
82 | } |
83 | |
84 | 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 | |
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 | |
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 | |
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 | |
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 | |
245 | bool NativeRegisterContextLinux_loongarch64::IsGPR(unsigned reg) const { |
246 | return GetRegisterInfo().GetRegisterSetFromRegisterIndex(reg) == |
247 | RegisterInfoPOSIX_loongarch64::GPRegSet; |
248 | } |
249 | |
250 | bool NativeRegisterContextLinux_loongarch64::IsFPR(unsigned reg) const { |
251 | return GetRegisterInfo().GetRegisterSetFromRegisterIndex(reg) == |
252 | RegisterInfoPOSIX_loongarch64::FPRegSet; |
253 | } |
254 | |
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 | |
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 | |
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 | |
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 | |
319 | void NativeRegisterContextLinux_loongarch64::InvalidateAllRegisters() { |
320 | m_gpr_is_valid = false; |
321 | m_fpu_is_valid = false; |
322 | } |
323 | |
324 | 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> |
330 | 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 | |