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 &reg_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 &reg_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