| 1 | //===-- DNBArchImplARM64.h --------------------------------------*- C++ -*-===// |
| 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 | #ifndef LLDB_TOOLS_DEBUGSERVER_SOURCE_MACOSX_ARM64_DNBARCHIMPLARM64_H |
| 10 | #define LLDB_TOOLS_DEBUGSERVER_SOURCE_MACOSX_ARM64_DNBARCHIMPLARM64_H |
| 11 | |
| 12 | #if defined(__arm__) || defined(__arm64__) || defined(__aarch64__) |
| 13 | |
| 14 | #include <mach/thread_status.h> |
| 15 | #include <map> |
| 16 | #include <vector> |
| 17 | |
| 18 | #if !defined(ARM_SME_STATE) |
| 19 | #include "sme_thread_status.h" |
| 20 | #endif |
| 21 | |
| 22 | #if defined(ARM_THREAD_STATE64_COUNT) |
| 23 | |
| 24 | #include "DNBArch.h" |
| 25 | |
| 26 | class MachThread; |
| 27 | |
| 28 | class DNBArchMachARM64 : public DNBArchProtocol { |
| 29 | public: |
| 30 | enum { kMaxNumThumbITBreakpoints = 4 }; |
| 31 | |
| 32 | DNBArchMachARM64(MachThread *thread) |
| 33 | : m_thread(thread), m_state(), m_disabled_watchpoints(), |
| 34 | m_disabled_breakpoints(), m_watchpoint_hw_index(-1), |
| 35 | m_watchpoint_did_occur(false), |
| 36 | m_watchpoint_resume_single_step_enabled(false), |
| 37 | m_saved_register_states() { |
| 38 | m_disabled_watchpoints.resize(16); |
| 39 | m_disabled_breakpoints.resize(16); |
| 40 | memset(&m_dbg_save, 0, sizeof(m_dbg_save)); |
| 41 | } |
| 42 | |
| 43 | struct WatchpointSpec { |
| 44 | nub_addr_t aligned_start; |
| 45 | nub_addr_t requested_start; |
| 46 | nub_size_t aligned_size; |
| 47 | nub_size_t requested_size; |
| 48 | }; |
| 49 | |
| 50 | virtual ~DNBArchMachARM64() {} |
| 51 | |
| 52 | static void Initialize(); |
| 53 | static const DNBRegisterSetInfo *GetRegisterSetInfo(nub_size_t *num_reg_sets); |
| 54 | |
| 55 | bool GetRegisterValue(uint32_t set, uint32_t reg, |
| 56 | DNBRegisterValue *value) override; |
| 57 | bool SetRegisterValue(uint32_t set, uint32_t reg, |
| 58 | const DNBRegisterValue *value) override; |
| 59 | nub_size_t GetRegisterContext(void *buf, nub_size_t buf_len) override; |
| 60 | nub_size_t SetRegisterContext(const void *buf, nub_size_t buf_len) override; |
| 61 | uint32_t SaveRegisterState() override; |
| 62 | bool RestoreRegisterState(uint32_t save_id) override; |
| 63 | |
| 64 | kern_return_t GetRegisterState(int set, bool force) override; |
| 65 | kern_return_t SetRegisterState(int set) override; |
| 66 | bool RegisterSetStateIsValid(int set) const override; |
| 67 | |
| 68 | uint64_t GetPC(uint64_t failValue) override; // Get program counter |
| 69 | kern_return_t SetPC(uint64_t value) override; |
| 70 | uint64_t GetSP(uint64_t failValue) override; // Get stack pointer |
| 71 | void ThreadWillResume() override; |
| 72 | bool ThreadDidStop() override; |
| 73 | bool NotifyException(MachException::Data &exc) override; |
| 74 | |
| 75 | static DNBArchProtocol *Create(MachThread *thread); |
| 76 | static const uint8_t *SoftwareBreakpointOpcode(nub_size_t byte_size); |
| 77 | static uint32_t GetCPUType(); |
| 78 | |
| 79 | uint32_t NumSupportedHardwareBreakpoints() override; |
| 80 | uint32_t NumSupportedHardwareWatchpoints() override; |
| 81 | |
| 82 | uint32_t EnableHardwareBreakpoint(nub_addr_t addr, nub_size_t size, |
| 83 | bool also_set_on_task) override; |
| 84 | bool DisableHardwareBreakpoint(uint32_t hw_break_index, |
| 85 | bool also_set_on_task) override; |
| 86 | std::vector<WatchpointSpec> |
| 87 | AlignRequestedWatchpoint(nub_addr_t requested_addr, |
| 88 | nub_size_t requested_size); |
| 89 | uint32_t EnableHardwareWatchpoint(nub_addr_t addr, nub_size_t size, bool read, |
| 90 | bool write, bool also_set_on_task) override; |
| 91 | uint32_t SetBASWatchpoint(WatchpointSpec wp, bool read, bool write, |
| 92 | bool also_set_on_task); |
| 93 | uint32_t SetMASKWatchpoint(WatchpointSpec wp, bool read, bool write, |
| 94 | bool also_set_on_task); |
| 95 | bool DisableHardwareWatchpoint(uint32_t hw_break_index, |
| 96 | bool also_set_on_task) override; |
| 97 | bool DisableHardwareWatchpoint_helper(uint32_t hw_break_index, |
| 98 | bool also_set_on_task); |
| 99 | |
| 100 | kern_return_t EnableHardwareSingleStep(bool enable); |
| 101 | static bool FixGenericRegisterNumber(uint32_t &set, uint32_t ®); |
| 102 | |
| 103 | enum RegisterSet { |
| 104 | e_regSetALL = REGISTER_SET_ALL, |
| 105 | e_regSetGPR, // ARM_THREAD_STATE64, |
| 106 | e_regSetVFP, // ARM_NEON_STATE64, |
| 107 | e_regSetEXC, // ARM_EXCEPTION_STATE64, |
| 108 | e_regSetSVE, // ARM_SVE_Z_STATE1, ARM_SVE_Z_STATE2, ARM_SVE_P_STATE |
| 109 | e_regSetSME, // ARM_SME_STATE, ARM_SME_ZA_STATE1..16, ARM_SME2_STATE |
| 110 | e_regSetDBG, // ARM_DEBUG_STATE64, |
| 111 | kNumRegisterSets |
| 112 | }; |
| 113 | |
| 114 | enum { |
| 115 | e_regSetGPRCount = ARM_THREAD_STATE64_COUNT, |
| 116 | e_regSetVFPCount = ARM_NEON_STATE64_COUNT, |
| 117 | e_regSetEXCCount = ARM_EXCEPTION_STATE64_COUNT, |
| 118 | e_regSetDBGCount = ARM_DEBUG_STATE64_COUNT, |
| 119 | }; |
| 120 | |
| 121 | enum { Read = 0, Write = 1, kNumErrors = 2 }; |
| 122 | |
| 123 | typedef arm_thread_state64_t GPR; |
| 124 | typedef arm_neon_state64_t FPU; |
| 125 | typedef arm_exception_state64_t EXC; |
| 126 | |
| 127 | struct SVE { |
| 128 | uint8_t z[32][256]; // arm_sve_z_state_t z[2] |
| 129 | uint8_t p[16][32]; // arm_sve_p_state_t p |
| 130 | }; |
| 131 | |
| 132 | struct SME { |
| 133 | uint64_t svcr; // arm_sme_state_t |
| 134 | uint64_t tpidr2; // arm_sme_state_t |
| 135 | uint16_t svl_b; // arm_sme_state_t |
| 136 | |
| 137 | std::vector<uint8_t> za; |
| 138 | uint8_t zt0[64]; |
| 139 | |
| 140 | SME() { |
| 141 | if (DNBArchMachARM64::CPUHasSME()) { |
| 142 | int svl = GetSMEMaxSVL(); |
| 143 | za.resize(svl * svl, 0); |
| 144 | } |
| 145 | } |
| 146 | }; |
| 147 | |
| 148 | static const DNBRegisterInfo g_gpr_registers[]; |
| 149 | static const DNBRegisterInfo g_exc_registers[]; |
| 150 | |
| 151 | static const size_t k_num_gpr_registers; |
| 152 | static const size_t k_num_exc_registers; |
| 153 | static const size_t k_num_all_registers; |
| 154 | |
| 155 | struct Context { |
| 156 | GPR gpr; |
| 157 | FPU vfp; |
| 158 | SVE sve; |
| 159 | SME sme; |
| 160 | EXC exc; |
| 161 | }; |
| 162 | |
| 163 | struct State { |
| 164 | Context context; |
| 165 | arm_debug_state64_t dbg; |
| 166 | kern_return_t gpr_errs[2]; // Read/Write errors |
| 167 | kern_return_t vfp_errs[2]; // Read/Write errors |
| 168 | kern_return_t sve_errs[2]; // Read/Write errors |
| 169 | kern_return_t sme_errs[2]; // Read/Write errors |
| 170 | kern_return_t exc_errs[2]; // Read/Write errors |
| 171 | kern_return_t dbg_errs[2]; // Read/Write errors |
| 172 | State() { |
| 173 | uint32_t i; |
| 174 | for (i = 0; i < kNumErrors; i++) { |
| 175 | gpr_errs[i] = -1; |
| 176 | vfp_errs[i] = -1; |
| 177 | sve_errs[i] = -1; |
| 178 | sme_errs[i] = -1; |
| 179 | exc_errs[i] = -1; |
| 180 | dbg_errs[i] = -1; |
| 181 | } |
| 182 | } |
| 183 | void InvalidateRegisterSetState(int set) { SetError(set, Read, -1); } |
| 184 | |
| 185 | void InvalidateAllRegisterStates() { SetError(e_regSetALL, Read, -1); } |
| 186 | |
| 187 | kern_return_t GetError(int set, uint32_t err_idx) const { |
| 188 | if (err_idx < kNumErrors) { |
| 189 | switch (set) { |
| 190 | // When getting all errors, just OR all values together to see if |
| 191 | // we got any kind of error. |
| 192 | case e_regSetALL: |
| 193 | return gpr_errs[err_idx] | vfp_errs[err_idx] | exc_errs[err_idx] | |
| 194 | sve_errs[err_idx] | sme_errs[err_idx] | dbg_errs[err_idx]; |
| 195 | case e_regSetGPR: |
| 196 | return gpr_errs[err_idx]; |
| 197 | case e_regSetVFP: |
| 198 | return vfp_errs[err_idx]; |
| 199 | case e_regSetSVE: |
| 200 | return sve_errs[err_idx]; |
| 201 | case e_regSetSME: |
| 202 | return sme_errs[err_idx]; |
| 203 | case e_regSetEXC: |
| 204 | return exc_errs[err_idx]; |
| 205 | // case e_regSetDBG: return dbg_errs[err_idx]; |
| 206 | default: |
| 207 | break; |
| 208 | } |
| 209 | } |
| 210 | return -1; |
| 211 | } |
| 212 | bool SetError(int set, uint32_t err_idx, kern_return_t err) { |
| 213 | if (err_idx < kNumErrors) { |
| 214 | switch (set) { |
| 215 | case e_regSetALL: |
| 216 | gpr_errs[err_idx] = err; |
| 217 | vfp_errs[err_idx] = err; |
| 218 | sve_errs[err_idx] = err; |
| 219 | sme_errs[err_idx] = err; |
| 220 | dbg_errs[err_idx] = err; |
| 221 | exc_errs[err_idx] = err; |
| 222 | return true; |
| 223 | |
| 224 | case e_regSetGPR: |
| 225 | gpr_errs[err_idx] = err; |
| 226 | return true; |
| 227 | |
| 228 | case e_regSetVFP: |
| 229 | vfp_errs[err_idx] = err; |
| 230 | return true; |
| 231 | |
| 232 | case e_regSetSVE: |
| 233 | sve_errs[err_idx] = err; |
| 234 | return true; |
| 235 | |
| 236 | case e_regSetSME: |
| 237 | sme_errs[err_idx] = err; |
| 238 | return true; |
| 239 | |
| 240 | case e_regSetEXC: |
| 241 | exc_errs[err_idx] = err; |
| 242 | return true; |
| 243 | |
| 244 | // case e_regSetDBG: |
| 245 | // dbg_errs[err_idx] = err; |
| 246 | // return true; |
| 247 | default: |
| 248 | break; |
| 249 | } |
| 250 | } |
| 251 | return false; |
| 252 | } |
| 253 | bool RegsAreValid(int set) const { |
| 254 | return GetError(set, Read) == KERN_SUCCESS; |
| 255 | } |
| 256 | }; |
| 257 | |
| 258 | kern_return_t GetGPRState(bool force); |
| 259 | kern_return_t GetVFPState(bool force); |
| 260 | kern_return_t GetSVEState(bool force); |
| 261 | kern_return_t GetSMEState(bool force); |
| 262 | kern_return_t GetEXCState(bool force); |
| 263 | kern_return_t GetDBGState(bool force); |
| 264 | |
| 265 | kern_return_t SetGPRState(); |
| 266 | kern_return_t SetVFPState(); |
| 267 | kern_return_t SetSVEState(); |
| 268 | kern_return_t SetSMEState(); |
| 269 | kern_return_t SetEXCState(); |
| 270 | kern_return_t SetDBGState(bool also_set_on_task); |
| 271 | |
| 272 | // Helper functions for watchpoint implementaions. |
| 273 | |
| 274 | typedef arm_debug_state64_t DBG; |
| 275 | |
| 276 | void ClearWatchpointOccurred(); |
| 277 | bool HasWatchpointOccurred(); |
| 278 | bool IsWatchpointEnabled(const DBG &debug_state, uint32_t hw_index); |
| 279 | nub_addr_t GetWatchpointAddressByIndex(uint32_t hw_index); |
| 280 | nub_addr_t GetWatchAddress(const DBG &debug_state, uint32_t hw_index); |
| 281 | virtual bool ReenableHardwareWatchpoint(uint32_t hw_break_index); |
| 282 | virtual bool ReenableHardwareWatchpoint_helper(uint32_t hw_break_index); |
| 283 | uint32_t GetHardwareWatchpointHit(nub_addr_t &addr) override; |
| 284 | |
| 285 | class disabled_watchpoint { |
| 286 | public: |
| 287 | disabled_watchpoint() { |
| 288 | addr = 0; |
| 289 | control = 0; |
| 290 | } |
| 291 | nub_addr_t addr; |
| 292 | uint32_t control; |
| 293 | }; |
| 294 | |
| 295 | static bool CPUHasSME(); |
| 296 | static bool CPUHasSME2(); |
| 297 | static unsigned int GetSMEMaxSVL(); |
| 298 | |
| 299 | private: |
| 300 | static DNBRegisterInfo *get_vfp_registerinfo(size_t &num_vfp_registers); |
| 301 | static DNBRegisterInfo *get_sve_registerinfo(size_t &num_sve_registers); |
| 302 | static DNBRegisterInfo *get_sme_registerinfo(size_t &num_sme_registers); |
| 303 | static void initialize_reg_sets(); |
| 304 | |
| 305 | MachThread *m_thread; |
| 306 | State m_state; |
| 307 | arm_debug_state64_t m_dbg_save; |
| 308 | |
| 309 | // arm64 doesn't keep the disabled watchpoint and breakpoint values in the |
| 310 | // debug register context like armv7; |
| 311 | // we need to save them aside when we disable them temporarily. |
| 312 | std::vector<disabled_watchpoint> m_disabled_watchpoints; |
| 313 | std::vector<disabled_watchpoint> m_disabled_breakpoints; |
| 314 | |
| 315 | // The following member variables should be updated atomically. |
| 316 | int32_t m_watchpoint_hw_index; |
| 317 | bool m_watchpoint_did_occur; |
| 318 | bool m_watchpoint_resume_single_step_enabled; |
| 319 | |
| 320 | typedef std::map<uint32_t, Context> SaveRegisterStates; |
| 321 | SaveRegisterStates m_saved_register_states; |
| 322 | |
| 323 | DNBArchMachARM64(const DNBArchMachARM64 &) = delete; |
| 324 | DNBArchMachARM64 &operator=(const DNBArchMachARM64 &) = delete; |
| 325 | }; |
| 326 | |
| 327 | #endif // #if defined (ARM_THREAD_STATE64_COUNT) |
| 328 | #endif // #if defined (__arm__) |
| 329 | #endif // LLDB_TOOLS_DEBUGSERVER_SOURCE_MACOSX_ARM64_DNBARCHIMPLARM64_H |
| 330 | |