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