1 //===-- NativeRegisterContextFreeBSD_arm64.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(__aarch64__) 10 11 #include "NativeRegisterContextFreeBSD_arm64.h" 12 13 #include "lldb/Utility/DataBufferHeap.h" 14 #include "lldb/Utility/RegisterValue.h" 15 #include "lldb/Utility/Status.h" 16 17 #include "Plugins/Process/FreeBSD/NativeProcessFreeBSD.h" 18 #include "Plugins/Process/POSIX/ProcessPOSIXLog.h" 19 #include "Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h" 20 21 // clang-format off 22 #include <sys/param.h> 23 #include <sys/ptrace.h> 24 #include <sys/types.h> 25 // clang-format on 26 27 using namespace lldb; 28 using namespace lldb_private; 29 using namespace lldb_private::process_freebsd; 30 31 NativeRegisterContextFreeBSD * 32 NativeRegisterContextFreeBSD::CreateHostNativeRegisterContextFreeBSD( 33 const ArchSpec &target_arch, NativeThreadProtocol &native_thread) { 34 return new NativeRegisterContextFreeBSD_arm64(target_arch, native_thread); 35 } 36 37 NativeRegisterContextFreeBSD_arm64::NativeRegisterContextFreeBSD_arm64( 38 const ArchSpec &target_arch, NativeThreadProtocol &native_thread) 39 : NativeRegisterContextRegisterInfo( 40 native_thread, new RegisterInfoPOSIX_arm64(target_arch)) 41 #ifdef LLDB_HAS_FREEBSD_WATCHPOINT 42 , 43 m_read_dbreg(false) 44 #endif 45 { 46 GetRegisterInfo().ConfigureVectorRegisterInfos( 47 RegisterInfoPOSIX_arm64::eVectorQuadwordAArch64); 48 ::memset(&m_hwp_regs, 0, sizeof(m_hwp_regs)); 49 ::memset(&m_hbp_regs, 0, sizeof(m_hbp_regs)); 50 } 51 52 RegisterInfoPOSIX_arm64 & 53 NativeRegisterContextFreeBSD_arm64::GetRegisterInfo() const { 54 return static_cast<RegisterInfoPOSIX_arm64 &>(*m_register_info_interface_up); 55 } 56 57 uint32_t NativeRegisterContextFreeBSD_arm64::GetRegisterSetCount() const { 58 return GetRegisterInfo().GetRegisterSetCount(); 59 } 60 61 const RegisterSet * 62 NativeRegisterContextFreeBSD_arm64::GetRegisterSet(uint32_t set_index) const { 63 return GetRegisterInfo().GetRegisterSet(set_index); 64 } 65 66 uint32_t NativeRegisterContextFreeBSD_arm64::GetUserRegisterCount() const { 67 uint32_t count = 0; 68 for (uint32_t set_index = 0; set_index < GetRegisterSetCount(); ++set_index) 69 count += GetRegisterSet(set_index)->num_registers; 70 return count; 71 } 72 73 Status NativeRegisterContextFreeBSD_arm64::ReadRegisterSet(uint32_t set) { 74 switch (set) { 75 case RegisterInfoPOSIX_arm64::GPRegSet: 76 return NativeProcessFreeBSD::PtraceWrapper(PT_GETREGS, m_thread.GetID(), 77 m_reg_data.data()); 78 case RegisterInfoPOSIX_arm64::FPRegSet: 79 return NativeProcessFreeBSD::PtraceWrapper( 80 PT_GETFPREGS, m_thread.GetID(), 81 m_reg_data.data() + sizeof(RegisterInfoPOSIX_arm64::GPR)); 82 case RegisterInfoPOSIX_arm64::SVERegSet: 83 return Status("not supported"); 84 } 85 llvm_unreachable("NativeRegisterContextFreeBSD_arm64::ReadRegisterSet"); 86 } 87 88 Status NativeRegisterContextFreeBSD_arm64::WriteRegisterSet(uint32_t set) { 89 switch (set) { 90 case RegisterInfoPOSIX_arm64::GPRegSet: 91 return NativeProcessFreeBSD::PtraceWrapper(PT_SETREGS, m_thread.GetID(), 92 m_reg_data.data()); 93 case RegisterInfoPOSIX_arm64::FPRegSet: 94 return NativeProcessFreeBSD::PtraceWrapper( 95 PT_SETFPREGS, m_thread.GetID(), 96 m_reg_data.data() + sizeof(RegisterInfoPOSIX_arm64::GPR)); 97 case RegisterInfoPOSIX_arm64::SVERegSet: 98 return Status("not supported"); 99 } 100 llvm_unreachable("NativeRegisterContextFreeBSD_arm64::WriteRegisterSet"); 101 } 102 103 Status 104 NativeRegisterContextFreeBSD_arm64::ReadRegister(const RegisterInfo *reg_info, 105 RegisterValue ®_value) { 106 Status error; 107 108 if (!reg_info) { 109 error.SetErrorString("reg_info NULL"); 110 return error; 111 } 112 113 const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; 114 115 if (reg == LLDB_INVALID_REGNUM) 116 return Status("no lldb regnum for %s", reg_info && reg_info->name 117 ? reg_info->name 118 : "<unknown register>"); 119 120 uint32_t set = GetRegisterInfo().GetRegisterSetFromRegisterIndex(reg); 121 error = ReadRegisterSet(set); 122 if (error.Fail()) 123 return error; 124 125 assert(reg_info->byte_offset + reg_info->byte_size <= m_reg_data.size()); 126 reg_value.SetBytes(m_reg_data.data() + reg_info->byte_offset, 127 reg_info->byte_size, endian::InlHostByteOrder()); 128 return error; 129 } 130 131 Status NativeRegisterContextFreeBSD_arm64::WriteRegister( 132 const RegisterInfo *reg_info, const RegisterValue ®_value) { 133 Status error; 134 135 if (!reg_info) 136 return Status("reg_info NULL"); 137 138 const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; 139 140 if (reg == LLDB_INVALID_REGNUM) 141 return Status("no lldb regnum for %s", reg_info && reg_info->name 142 ? reg_info->name 143 : "<unknown register>"); 144 145 uint32_t set = GetRegisterInfo().GetRegisterSetFromRegisterIndex(reg); 146 error = ReadRegisterSet(set); 147 if (error.Fail()) 148 return error; 149 150 assert(reg_info->byte_offset + reg_info->byte_size <= m_reg_data.size()); 151 ::memcpy(m_reg_data.data() + reg_info->byte_offset, reg_value.GetBytes(), 152 reg_info->byte_size); 153 154 return WriteRegisterSet(set); 155 } 156 157 Status NativeRegisterContextFreeBSD_arm64::ReadAllRegisterValues( 158 lldb::DataBufferSP &data_sp) { 159 Status error; 160 161 error = ReadRegisterSet(RegisterInfoPOSIX_arm64::GPRegSet); 162 if (error.Fail()) 163 return error; 164 165 error = ReadRegisterSet(RegisterInfoPOSIX_arm64::FPRegSet); 166 if (error.Fail()) 167 return error; 168 169 data_sp.reset(new DataBufferHeap(m_reg_data.size(), 0)); 170 uint8_t *dst = data_sp->GetBytes(); 171 ::memcpy(dst, m_reg_data.data(), m_reg_data.size()); 172 173 return error; 174 } 175 176 Status NativeRegisterContextFreeBSD_arm64::WriteAllRegisterValues( 177 const lldb::DataBufferSP &data_sp) { 178 Status error; 179 180 if (!data_sp) { 181 error.SetErrorStringWithFormat( 182 "NativeRegisterContextFreeBSD_arm64::%s invalid data_sp provided", 183 __FUNCTION__); 184 return error; 185 } 186 187 if (data_sp->GetByteSize() != m_reg_data.size()) { 188 error.SetErrorStringWithFormat( 189 "NativeRegisterContextFreeBSD_arm64::%s data_sp contained mismatched " 190 "data size, expected %" PRIu64 ", actual %" PRIu64, 191 __FUNCTION__, m_reg_data.size(), data_sp->GetByteSize()); 192 return error; 193 } 194 195 uint8_t *src = data_sp->GetBytes(); 196 if (src == nullptr) { 197 error.SetErrorStringWithFormat("NativeRegisterContextFreeBSD_arm64::%s " 198 "DataBuffer::GetBytes() returned a null " 199 "pointer", 200 __FUNCTION__); 201 return error; 202 } 203 ::memcpy(m_reg_data.data(), src, m_reg_data.size()); 204 205 error = WriteRegisterSet(RegisterInfoPOSIX_arm64::GPRegSet); 206 if (error.Fail()) 207 return error; 208 209 return WriteRegisterSet(RegisterInfoPOSIX_arm64::FPRegSet); 210 } 211 212 llvm::Error NativeRegisterContextFreeBSD_arm64::CopyHardwareWatchpointsFrom( 213 NativeRegisterContextFreeBSD &source) { 214 #ifdef LLDB_HAS_FREEBSD_WATCHPOINT 215 auto &r_source = static_cast<NativeRegisterContextFreeBSD_arm64 &>(source); 216 llvm::Error error = r_source.ReadHardwareDebugInfo(); 217 if (error) 218 return error; 219 220 m_dbreg = r_source.m_dbreg; 221 m_hbp_regs = r_source.m_hbp_regs; 222 m_hwp_regs = r_source.m_hwp_regs; 223 m_max_hbp_supported = r_source.m_max_hbp_supported; 224 m_max_hwp_supported = r_source.m_max_hwp_supported; 225 m_read_dbreg = true; 226 227 // on FreeBSD this writes both breakpoints and watchpoints 228 return WriteHardwareDebugRegs(eDREGTypeWATCH); 229 #else 230 return llvm::Error::success(); 231 #endif 232 } 233 234 llvm::Error NativeRegisterContextFreeBSD_arm64::ReadHardwareDebugInfo() { 235 #ifdef LLDB_HAS_FREEBSD_WATCHPOINT 236 Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_REGISTERS)); 237 238 // we're fully stateful, so no need to reread control registers ever 239 if (m_read_dbreg) 240 return llvm::Error::success(); 241 242 Status res = NativeProcessFreeBSD::PtraceWrapper(PT_GETDBREGS, 243 m_thread.GetID(), &m_dbreg); 244 if (res.Fail()) 245 return res.ToError(); 246 247 LLDB_LOG(log, "m_dbreg read: debug_ver={0}, nbkpts={1}, nwtpts={2}", 248 m_dbreg.db_debug_ver, m_dbreg.db_nbkpts, m_dbreg.db_nwtpts); 249 m_max_hbp_supported = m_dbreg.db_nbkpts; 250 m_max_hwp_supported = m_dbreg.db_nwtpts; 251 assert(m_max_hbp_supported <= m_hbp_regs.size()); 252 assert(m_max_hwp_supported <= m_hwp_regs.size()); 253 254 m_read_dbreg = true; 255 return llvm::Error::success(); 256 #else 257 return llvm::createStringError( 258 llvm::inconvertibleErrorCode(), 259 "Hardware breakpoints/watchpoints require FreeBSD 14.0"); 260 #endif 261 } 262 263 llvm::Error 264 NativeRegisterContextFreeBSD_arm64::WriteHardwareDebugRegs(DREGType) { 265 #ifdef LLDB_HAS_FREEBSD_WATCHPOINT 266 assert(m_read_dbreg && "dbregs must be read before writing them back"); 267 268 // copy data from m_*_regs to m_dbreg before writing it back 269 for (uint32_t i = 0; i < m_max_hbp_supported; i++) { 270 m_dbreg.db_breakregs[i].dbr_addr = m_hbp_regs[i].address; 271 m_dbreg.db_breakregs[i].dbr_ctrl = m_hbp_regs[i].control; 272 } 273 for (uint32_t i = 0; i < m_max_hwp_supported; i++) { 274 m_dbreg.db_watchregs[i].dbw_addr = m_hwp_regs[i].address; 275 m_dbreg.db_watchregs[i].dbw_ctrl = m_hwp_regs[i].control; 276 } 277 278 return NativeProcessFreeBSD::PtraceWrapper(PT_SETDBREGS, m_thread.GetID(), 279 &m_dbreg) 280 .ToError(); 281 #else 282 return llvm::createStringError( 283 llvm::inconvertibleErrorCode(), 284 "Hardware breakpoints/watchpoints require FreeBSD 14.0"); 285 #endif 286 } 287 288 #endif // defined (__aarch64__) 289