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