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