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