xref: /xnu-12377.1.9/osfmk/arm64/dbgwrap.c (revision f6217f891ac0bb64f3d375211650a4c1ff8ca1ea)
1 /*
2  * Copyright (c) 2017 Apple Inc. All rights reserved.
3  *
4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5  *
6  * This file contains Original Code and/or Modifications of Original Code
7  * as defined in and that are subject to the Apple Public Source License
8  * Version 2.0 (the 'License'). You may not use this file except in
9  * compliance with the License. The rights granted to you under the License
10  * may not be used to create, or enable the creation or redistribution of,
11  * unlawful or unlicensed copies of an Apple operating system, or to
12  * circumvent, violate, or enable the circumvention or violation of, any
13  * terms of an Apple operating system software license agreement.
14  *
15  * Please obtain a copy of the License at
16  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17  *
18  * The Original Code and all software distributed under the License are
19  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23  * Please see the License for the specific language governing rights and
24  * limitations under the License.
25  *
26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27  */
28 
29 #include <arm/cpu_data_internal.h>
30 #include <arm/dbgwrap.h>
31 #include <arm64/proc_reg.h>
32 #include <machine/atomic.h>
33 #include <pexpert/arm64/board_config.h>
34 
35 #define DBGWRAP_REG_OFFSET      0
36 #define DBGWRAP_DBGHALT         (1ULL << 31)
37 #define DBGWRAP_DBGACK          (1ULL << 28)
38 
39 #define EDDTRRX_REG_OFFSET      0x80
40 #define EDITR_REG_OFFSET        0x84
41 #define EDSCR_REG_OFFSET        0x88
42 #define EDSCR_TXFULL            (1ULL << 29)
43 #define EDSCR_ITE               (1ULL << 24)
44 #define EDSCR_MA                (1ULL << 20)
45 #define EDSCR_ERR               (1ULL << 6)
46 #define EDDTRTX_REG_OFFSET      0x8C
47 #define EDRCR_REG_OFFSET        0x90
48 #define EDRCR_CSE               (1ULL << 2)
49 #define EDPRSR_REG_OFFSET       0x314
50 #define EDPRSR_OSLK             (1ULL << 5)
51 
52 #define MAX_EDITR_RETRIES       16
53 
54 /* Older SoCs require 32-bit accesses for DBGWRAP;
55  * newer ones require 64-bit accesses. */
56 #ifdef HAS_32BIT_DBGWRAP
57 typedef uint32_t dbgwrap_reg_t;
58 #else
59 typedef uint64_t dbgwrap_reg_t;
60 #endif
61 
62 #if DEVELOPMENT || DEBUG
63 #define MAX_STUFFED_INSTRS      64
64 uint32_t stuffed_instrs[MAX_STUFFED_INSTRS];
65 volatile uint32_t stuffed_instr_count = 0;
66 #endif
67 
68 static volatile uint32_t halt_from_cpu = (uint32_t)-1;
69 
70 boolean_t
ml_dbgwrap_cpu_is_halted(int cpu_index)71 ml_dbgwrap_cpu_is_halted(int cpu_index)
72 {
73 	cpu_data_t *cdp = cpu_datap(cpu_index);
74 	if ((cdp == NULL) || (cdp->coresight_base[CORESIGHT_UTT] == 0)) {
75 		return FALSE;
76 	}
77 
78 	return (*(volatile dbgwrap_reg_t *)(cdp->coresight_base[CORESIGHT_UTT] + DBGWRAP_REG_OFFSET) & DBGWRAP_DBGACK) != 0;
79 }
80 
81 dbgwrap_status_t
ml_dbgwrap_wait_cpu_halted(int cpu_index,uint64_t timeout_ns)82 ml_dbgwrap_wait_cpu_halted(int cpu_index, uint64_t timeout_ns)
83 {
84 	cpu_data_t *cdp = cpu_datap(cpu_index);
85 	if ((cdp == NULL) || (cdp->coresight_base[CORESIGHT_UTT] == 0)) {
86 		return DBGWRAP_ERR_UNSUPPORTED;
87 	}
88 
89 	volatile dbgwrap_reg_t *dbgWrapReg = (volatile dbgwrap_reg_t *)(cdp->coresight_base[CORESIGHT_UTT] + DBGWRAP_REG_OFFSET);
90 
91 	uint64_t interval;
92 	nanoseconds_to_absolutetime(timeout_ns, &interval);
93 	uint64_t deadline = mach_absolute_time() + interval;
94 	while (!(*dbgWrapReg & DBGWRAP_DBGACK)) {
95 		if (mach_absolute_time() > deadline) {
96 			return DBGWRAP_ERR_HALT_TIMEOUT;
97 		}
98 	}
99 
100 	return DBGWRAP_SUCCESS;
101 }
102 
103 dbgwrap_status_t
ml_dbgwrap_halt_cpu(int cpu_index,uint64_t timeout_ns)104 ml_dbgwrap_halt_cpu(int cpu_index, uint64_t timeout_ns)
105 {
106 	cpu_data_t *cdp = cpu_datap(cpu_index);
107 	if ((cdp == NULL) || (cdp->coresight_base[CORESIGHT_UTT] == 0) || (cdp->coresight_base[CORESIGHT_ED] == 0)) {
108 		return DBGWRAP_ERR_UNSUPPORTED;
109 	}
110 
111 	/* Only one cpu is allowed to initiate the halt sequence, to prevent cpus from cross-halting
112 	 * each other.  The first cpu to request a halt may then halt any and all other cpus besides itself. */
113 	int curcpu = cpu_number();
114 	if (cpu_index == curcpu) {
115 		return DBGWRAP_ERR_SELF_HALT;
116 	}
117 
118 	if (!os_atomic_cmpxchg(&halt_from_cpu, (uint32_t)-1, (unsigned int)curcpu, acq_rel) &&
119 	    (halt_from_cpu != (uint32_t)curcpu)) {
120 		return DBGWRAP_ERR_INPROGRESS;
121 	}
122 
123 	/* Accessing EDPRSR or DBGWRAP when cluster is powered off would result in LLC bus error. Hence, check
124 	 * the cluster power status to make sure these PIO registers are accessible.*/
125 	if (!PE_cpu_power_check_kdp(cpu_index)) {
126 		return DBGWRAP_WARN_CPU_OFFLINE;
127 	}
128 
129 	/* Ensure memory-mapped coresight registers can be written */
130 	*((volatile uint32_t *)(cdp->coresight_base[CORESIGHT_ED] + ARM_DEBUG_OFFSET_DBGLAR)) = ARM_DBG_LOCK_ACCESS_KEY;
131 
132 	/* A core that is not fully powered (e.g. idling in wfi) can still be halted; the dbgwrap
133 	 * register and certain coresight registers such EDPRSR are in the always-on domain and their
134 	 * values are retained over ACC power down. And the OS lock defaults to being set but we clear
135 	 * it first thing when CPU is up, so use that to detect the offline state of individual CPU. */
136 	if (*((volatile uint32_t *)(cdp->coresight_base[CORESIGHT_ED] + EDPRSR_REG_OFFSET)) & EDPRSR_OSLK) {
137 		return DBGWRAP_WARN_CPU_OFFLINE;
138 	}
139 
140 	volatile dbgwrap_reg_t *dbgWrapReg = (volatile dbgwrap_reg_t *)(cdp->coresight_base[CORESIGHT_UTT] + DBGWRAP_REG_OFFSET);
141 
142 	if (ml_dbgwrap_cpu_is_halted(cpu_index)) {
143 		return DBGWRAP_WARN_ALREADY_HALTED;
144 	}
145 
146 
147 	/* Clear all other writable bits besides dbgHalt; none of the power-down or reset bits must be set. */
148 	*dbgWrapReg = DBGWRAP_DBGHALT;
149 
150 
151 	if (timeout_ns != 0) {
152 		dbgwrap_status_t stat = ml_dbgwrap_wait_cpu_halted(cpu_index, timeout_ns);
153 		return stat;
154 	} else {
155 		return DBGWRAP_SUCCESS;
156 	}
157 }
158 
159 static void
ml_dbgwrap_stuff_instr(cpu_data_t * cdp,uint32_t instr,uint64_t timeout_ns,dbgwrap_status_t * status)160 ml_dbgwrap_stuff_instr(cpu_data_t *cdp, uint32_t instr, uint64_t timeout_ns, dbgwrap_status_t *status)
161 {
162 	if (*status < 0) {
163 		return;
164 	}
165 
166 	volatile uint32_t *editr = (volatile uint32_t *)(cdp->coresight_base[CORESIGHT_ED] + EDITR_REG_OFFSET);
167 	volatile uint32_t *edscr = (volatile uint32_t *)(cdp->coresight_base[CORESIGHT_ED] + EDSCR_REG_OFFSET);
168 	volatile uint32_t *edrcr = (volatile uint32_t *)(cdp->coresight_base[CORESIGHT_ED] + EDRCR_REG_OFFSET);
169 
170 	int retries = 0;
171 
172 	uint64_t interval;
173 	nanoseconds_to_absolutetime(timeout_ns, &interval);
174 	uint64_t deadline = mach_absolute_time() + interval;
175 
176 #if DEVELOPMENT || DEBUG
177 	uint32_t stuffed_instr_index = os_atomic_inc(&stuffed_instr_count, relaxed);
178 	stuffed_instrs[(stuffed_instr_index - 1) % MAX_STUFFED_INSTRS] = instr;
179 #endif
180 
181 	do {
182 		*editr = instr;
183 		volatile uint32_t edscr_val;
184 		while (!((edscr_val = *edscr) & EDSCR_ITE)) {
185 			if (mach_absolute_time() > deadline) {
186 				*status = DBGWRAP_ERR_INSTR_TIMEOUT;
187 				return;
188 			}
189 			if (edscr_val & EDSCR_ERR) {
190 				break;
191 			}
192 		}
193 		if (edscr_val & EDSCR_ERR) {
194 			/* If memory access mode was enable by a debugger, clear it.
195 			 * This will cause ERR to be set on any attempt to use EDITR. */
196 			if (edscr_val & EDSCR_MA) {
197 				*edscr = edscr_val & ~EDSCR_MA;
198 			}
199 			*edrcr = EDRCR_CSE;
200 			++retries;
201 		} else {
202 			break;
203 		}
204 	} while (retries < MAX_EDITR_RETRIES);
205 
206 	if (retries >= MAX_EDITR_RETRIES) {
207 		*status = DBGWRAP_ERR_INSTR_ERROR;
208 		return;
209 	}
210 }
211 
212 static uint64_t
ml_dbgwrap_read_dtr(cpu_data_t * cdp,uint64_t timeout_ns,dbgwrap_status_t * status)213 ml_dbgwrap_read_dtr(cpu_data_t *cdp, uint64_t timeout_ns, dbgwrap_status_t *status)
214 {
215 	if (*status < 0) {
216 		return 0;
217 	}
218 
219 	uint64_t interval;
220 	nanoseconds_to_absolutetime(timeout_ns, &interval);
221 	uint64_t deadline = mach_absolute_time() + interval;
222 
223 	/* Per armv8 debug spec, writes to DBGDTR_EL0 on target cpu will set EDSCR.TXFull,
224 	 * with bits 63:32 available in EDDTRRX and bits 31:0 availabe in EDDTRTX. */
225 	volatile uint32_t *edscr = (volatile uint32_t *)(cdp->coresight_base[CORESIGHT_ED] + EDSCR_REG_OFFSET);
226 
227 	while (!(*edscr & EDSCR_TXFULL)) {
228 		if (*edscr & EDSCR_ERR) {
229 			*status = DBGWRAP_ERR_INSTR_ERROR;
230 			return 0;
231 		}
232 		if (mach_absolute_time() > deadline) {
233 			*status = DBGWRAP_ERR_INSTR_TIMEOUT;
234 			return 0;
235 		}
236 	}
237 
238 	uint32_t dtrrx = *((volatile uint32_t*)(cdp->coresight_base[CORESIGHT_ED] + EDDTRRX_REG_OFFSET));
239 	uint32_t dtrtx = *((volatile uint32_t*)(cdp->coresight_base[CORESIGHT_ED] + EDDTRTX_REG_OFFSET));
240 
241 	return ((uint64_t)dtrrx << 32) | dtrtx;
242 }
243 
244 dbgwrap_status_t
ml_dbgwrap_halt_cpu_with_state(int cpu_index,uint64_t timeout_ns,dbgwrap_thread_state_t * state)245 ml_dbgwrap_halt_cpu_with_state(int cpu_index, uint64_t timeout_ns, dbgwrap_thread_state_t *state)
246 {
247 	cpu_data_t *cdp = cpu_datap(cpu_index);
248 	if ((cdp == NULL) || (cdp->coresight_base[CORESIGHT_ED] == 0)) {
249 		return DBGWRAP_ERR_UNSUPPORTED;
250 	}
251 
252 	dbgwrap_status_t status = ml_dbgwrap_halt_cpu(cpu_index, timeout_ns);
253 
254 	if (status == DBGWRAP_WARN_CPU_OFFLINE) {
255 		bzero(state, sizeof(*state));
256 		return status;
257 	}
258 
259 	uint32_t instr;
260 
261 	for (unsigned int i = 0; i < (sizeof(state->x) / sizeof(state->x[0])); ++i) {
262 		instr = (0xD51U << 20) | (2 << 19) | (3 << 16) | (4 << 8) | i; // msr DBGDTR0, x<i>
263 		ml_dbgwrap_stuff_instr(cdp, instr, timeout_ns, &status);
264 		state->x[i] = ml_dbgwrap_read_dtr(cdp, timeout_ns, &status);
265 	}
266 
267 	instr = (0xD51U << 20) | (2 << 19) | (3 << 16) | (4 << 8) | 29; // msr DBGDTR0, fp
268 	ml_dbgwrap_stuff_instr(cdp, instr, timeout_ns, &status);
269 	state->fp = ml_dbgwrap_read_dtr(cdp, timeout_ns, &status);
270 
271 	instr = (0xD51U << 20) | (2 << 19) | (3 << 16) | (4 << 8) | 30; // msr DBGDTR0, lr
272 	ml_dbgwrap_stuff_instr(cdp, instr, timeout_ns, &status);
273 	state->lr = ml_dbgwrap_read_dtr(cdp, timeout_ns, &status);
274 
275 	/* Stack pointer (x31) can't be used as a register operand for msr; register 31 is treated as xzr
276 	 * rather than sp when used as the transfer operand there.  Instead, load sp into a GPR
277 	 * we've already saved off and then store that register in the DTR.  I've chosen x18
278 	 * as the temporary GPR since it's reserved by the arm64 ABI and unused by xnu, so overwriting
279 	 * it poses the least risk of causing trouble for external debuggers. */
280 
281 	instr = (0x91U << 24) | (31 << 5) | 18; // mov x18, sp
282 	ml_dbgwrap_stuff_instr(cdp, instr, timeout_ns, &status);
283 	instr = (0xD51U << 20) | (2 << 19) | (3 << 16) | (4 << 8) | 18; // msr DBGDTR0, x18
284 	ml_dbgwrap_stuff_instr(cdp, instr, timeout_ns, &status);
285 	state->sp = ml_dbgwrap_read_dtr(cdp, timeout_ns, &status);
286 
287 	/* reading PC (e.g. through adr) is undefined in debug state.  Instead use DLR_EL0,
288 	 * which contains PC at time of entry into debug state.*/
289 
290 	instr = (0xD53U << 20) | (1 << 19) | (3 << 16) | (4 << 12) | (5 << 8) | (1 << 5) | 18; // mrs    x18, DLR_EL0
291 	ml_dbgwrap_stuff_instr(cdp, instr, timeout_ns, &status);
292 	instr = (0xD51U << 20) | (2 << 19) | (3 << 16) | (4 << 8) | 18; // msr DBGDTR0, x18
293 	ml_dbgwrap_stuff_instr(cdp, instr, timeout_ns, &status);
294 	state->pc = ml_dbgwrap_read_dtr(cdp, timeout_ns, &status);
295 
296 	/* reading CPSR is undefined in debug state.  Instead use DSPSR_EL0,
297 	 * which contains CPSR at time of entry into debug state.*/
298 	instr = (0xD53U << 20) | (1 << 19) | (3 << 16) | (4 << 12) | (5 << 8) | 18; // mrs    x18, DSPSR_EL0
299 	ml_dbgwrap_stuff_instr(cdp, instr, timeout_ns, &status);
300 	instr = (0xD51U << 20) | (2 << 19) | (3 << 16) | (4 << 8) | 18; // msr DBGDTR0, x18
301 	ml_dbgwrap_stuff_instr(cdp, instr, timeout_ns, &status);
302 	state->cpsr = (uint32_t)ml_dbgwrap_read_dtr(cdp, timeout_ns, &status);
303 
304 	return status;
305 }
306