xref: /freebsd/sys/cddl/dev/dtrace/powerpc/dtrace_isa.c (revision c6ec7d31830ab1c80edae95ad5e4b9dba10c47ac)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  *
22  * $FreeBSD$
23  */
24 /*
25  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
26  * Use is subject to license terms.
27  */
28 #include <sys/cdefs.h>
29 
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/kernel.h>
33 #include <sys/stack.h>
34 #include <sys/sysent.h>
35 #include <sys/pcpu.h>
36 
37 #include <machine/frame.h>
38 #include <machine/md_var.h>
39 #include <machine/reg.h>
40 #include <machine/stack.h>
41 
42 #include <vm/vm.h>
43 #include <vm/vm_param.h>
44 #include <vm/pmap.h>
45 
46 #include "regset.h"
47 
48 uint8_t dtrace_fuword8_nocheck(void *);
49 uint16_t dtrace_fuword16_nocheck(void *);
50 uint32_t dtrace_fuword32_nocheck(void *);
51 uint64_t dtrace_fuword64_nocheck(void *);
52 
53 /* Offset to the LR Save word (ppc32) */
54 #define RETURN_OFFSET	4
55 #define RETURN_OFFSET64	8
56 
57 #define INKERNEL(x)	((x) <= VM_MAX_KERNEL_ADDRESS && \
58 		(x) >= VM_MIN_KERNEL_ADDRESS)
59 
60 greg_t
61 dtrace_getfp(void)
62 {
63 	return (greg_t)__builtin_frame_address(0);
64 }
65 
66 void
67 dtrace_getpcstack(pc_t *pcstack, int pcstack_limit, int aframes,
68     uint32_t *intrpc)
69 {
70 	int depth = 0;
71 	register_t sp;
72 	vm_offset_t callpc;
73 	pc_t caller = (pc_t) solaris_cpu[curcpu].cpu_dtrace_caller;
74 
75 	if (intrpc != 0)
76 		pcstack[depth++] = (pc_t) intrpc;
77 
78 	aframes++;
79 
80 	sp = dtrace_getfp();
81 
82 	while (depth < pcstack_limit) {
83 		if (!INKERNEL((long) sp))
84 			break;
85 
86 		callpc = *(uintptr_t *)(sp + RETURN_OFFSET);
87 
88 		if (!INKERNEL(callpc))
89 			break;
90 
91 		if (aframes > 0) {
92 			aframes--;
93 			if ((aframes == 0) && (caller != 0)) {
94 				pcstack[depth++] = caller;
95 			}
96 		}
97 		else {
98 			pcstack[depth++] = callpc;
99 		}
100 
101 		sp = *(uintptr_t*)sp;
102 	}
103 
104 	for (; depth < pcstack_limit; depth++) {
105 		pcstack[depth] = 0;
106 	}
107 }
108 
109 static int
110 dtrace_getustack_common(uint64_t *pcstack, int pcstack_limit, uintptr_t pc,
111     uintptr_t sp)
112 {
113 	proc_t *p = curproc;
114 	int ret = 0;
115 
116 	ASSERT(pcstack == NULL || pcstack_limit > 0);
117 
118 	while (pc != 0) {
119 		ret++;
120 		if (pcstack != NULL) {
121 			*pcstack++ = (uint64_t)pc;
122 			pcstack_limit--;
123 			if (pcstack_limit <= 0)
124 				break;
125 		}
126 
127 		if (sp == 0)
128 			break;
129 
130 		if (SV_PROC_FLAG(p, SV_ILP32)) {
131 			pc = dtrace_fuword32((void *)(sp + RETURN_OFFSET));
132 			sp = dtrace_fuword32((void *)sp);
133 		}
134 		else {
135 			pc = dtrace_fuword64((void *)(sp + RETURN_OFFSET64));
136 			sp = dtrace_fuword64((void *)sp);
137 		}
138 	}
139 
140 	return (ret);
141 }
142 
143 void
144 dtrace_getupcstack(uint64_t *pcstack, int pcstack_limit)
145 {
146 	proc_t *p = curproc;
147 	struct trapframe *tf;
148 	uintptr_t pc, sp;
149 	volatile uint16_t *flags =
150 	    (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags;
151 	int n;
152 
153 	if (*flags & CPU_DTRACE_FAULT)
154 		return;
155 
156 	if (pcstack_limit <= 0)
157 		return;
158 
159 	/*
160 	 * If there's no user context we still need to zero the stack.
161 	 */
162 	if (p == NULL || (tf = curthread->td_frame) == NULL)
163 		goto zero;
164 
165 	*pcstack++ = (uint64_t)p->p_pid;
166 	pcstack_limit--;
167 
168 	if (pcstack_limit <= 0)
169 		return;
170 
171 	pc = tf->srr0;
172 	sp = tf->fixreg[1];
173 
174 	if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) {
175 		/*
176 		 * In an entry probe.  The frame pointer has not yet been
177 		 * pushed (that happens in the function prologue).  The
178 		 * best approach is to add the current pc as a missing top
179 		 * of stack and back the pc up to the caller, which is stored
180 		 * at the current stack pointer address since the call
181 		 * instruction puts it there right before the branch.
182 		 */
183 
184 		*pcstack++ = (uint64_t)pc;
185 		pcstack_limit--;
186 		if (pcstack_limit <= 0)
187 			return;
188 
189 		pc = tf->lr;
190 	}
191 
192 	n = dtrace_getustack_common(pcstack, pcstack_limit, pc, sp);
193 	ASSERT(n >= 0);
194 	ASSERT(n <= pcstack_limit);
195 
196 	pcstack += n;
197 	pcstack_limit -= n;
198 
199 zero:
200 	while (pcstack_limit-- > 0)
201 		*pcstack++ = 0;
202 }
203 
204 int
205 dtrace_getustackdepth(void)
206 {
207 	proc_t *p = curproc;
208 	struct trapframe *tf;
209 	uintptr_t pc, sp;
210 	int n = 0;
211 
212 	if (p == NULL || (tf = curthread->td_frame) == NULL)
213 		return (0);
214 
215 	if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_FAULT))
216 		return (-1);
217 
218 	pc = tf->srr0;
219 	sp = tf->fixreg[1];
220 
221 	if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) {
222 		/*
223 		 * In an entry probe.  The frame pointer has not yet been
224 		 * pushed (that happens in the function prologue).  The
225 		 * best approach is to add the current pc as a missing top
226 		 * of stack and back the pc up to the caller, which is stored
227 		 * at the current stack pointer address since the call
228 		 * instruction puts it there right before the branch.
229 		 */
230 
231 		if (SV_PROC_FLAG(p, SV_ILP32)) {
232 			pc = dtrace_fuword32((void *) sp);
233 		}
234 		else
235 			pc = dtrace_fuword64((void *) sp);
236 		n++;
237 	}
238 
239 	n += dtrace_getustack_common(NULL, 0, pc, sp);
240 
241 	return (n);
242 }
243 
244 void
245 dtrace_getufpstack(uint64_t *pcstack, uint64_t *fpstack, int pcstack_limit)
246 {
247 	proc_t *p = curproc;
248 	struct trapframe *tf;
249 	uintptr_t pc, sp;
250 	volatile uint16_t *flags =
251 	    (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags;
252 #ifdef notyet	/* XXX signal stack */
253 	uintptr_t oldcontext;
254 	size_t s1, s2;
255 #endif
256 
257 	if (*flags & CPU_DTRACE_FAULT)
258 		return;
259 
260 	if (pcstack_limit <= 0)
261 		return;
262 
263 	/*
264 	 * If there's no user context we still need to zero the stack.
265 	 */
266 	if (p == NULL || (tf = curthread->td_frame) == NULL)
267 		goto zero;
268 
269 	*pcstack++ = (uint64_t)p->p_pid;
270 	pcstack_limit--;
271 
272 	if (pcstack_limit <= 0)
273 		return;
274 
275 	pc = tf->srr0;
276 	sp = tf->fixreg[1];
277 
278 #ifdef notyet /* XXX signal stack */
279 	oldcontext = lwp->lwp_oldcontext;
280 	s1 = sizeof (struct xframe) + 2 * sizeof (long);
281 	s2 = s1 + sizeof (siginfo_t);
282 #endif
283 
284 	if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) {
285 		*pcstack++ = (uint64_t)pc;
286 		*fpstack++ = 0;
287 		pcstack_limit--;
288 		if (pcstack_limit <= 0)
289 			return;
290 
291 		if (SV_PROC_FLAG(p, SV_ILP32)) {
292 			pc = dtrace_fuword32((void *)sp);
293 		}
294 		else {
295 			pc = dtrace_fuword64((void *)sp);
296 		}
297 	}
298 
299 	while (pc != 0) {
300 		*pcstack++ = (uint64_t)pc;
301 		*fpstack++ = sp;
302 		pcstack_limit--;
303 		if (pcstack_limit <= 0)
304 			break;
305 
306 		if (sp == 0)
307 			break;
308 
309 #ifdef notyet /* XXX signal stack */
310 		if (oldcontext == sp + s1 || oldcontext == sp + s2) {
311 			ucontext_t *ucp = (ucontext_t *)oldcontext;
312 			greg_t *gregs = ucp->uc_mcontext.gregs;
313 
314 			sp = dtrace_fulword(&gregs[REG_FP]);
315 			pc = dtrace_fulword(&gregs[REG_PC]);
316 
317 			oldcontext = dtrace_fulword(&ucp->uc_link);
318 		} else
319 #endif /* XXX */
320 		{
321 			if (SV_PROC_FLAG(p, SV_ILP32)) {
322 				pc = dtrace_fuword32((void *)(sp + RETURN_OFFSET));
323 				sp = dtrace_fuword32((void *)sp);
324 			}
325 			else {
326 				pc = dtrace_fuword64((void *)(sp + RETURN_OFFSET64));
327 				sp = dtrace_fuword64((void *)sp);
328 			}
329 		}
330 
331 		/*
332 		 * This is totally bogus:  if we faulted, we're going to clear
333 		 * the fault and break.  This is to deal with the apparently
334 		 * broken Java stacks on x86.
335 		 */
336 		if (*flags & CPU_DTRACE_FAULT) {
337 			*flags &= ~CPU_DTRACE_FAULT;
338 			break;
339 		}
340 	}
341 
342 zero:
343 	while (pcstack_limit-- > 0)
344 		*pcstack++ = 0;
345 }
346 
347 /*ARGSUSED*/
348 uint64_t
349 dtrace_getarg(int arg, int aframes)
350 {
351 	return (0);
352 }
353 
354 #ifdef notyet
355 {
356 	int depth = 0;
357 	register_t sp;
358 	vm_offset_t callpc;
359 	pc_t caller = (pc_t) solaris_cpu[curcpu].cpu_dtrace_caller;
360 
361 	if (intrpc != 0)
362 		pcstack[depth++] = (pc_t) intrpc;
363 
364 	aframes++;
365 
366 	sp = dtrace_getfp();
367 
368 	while (depth < pcstack_limit) {
369 		if (!INKERNEL((long) frame))
370 			break;
371 
372 		callpc = *(void **)(sp + RETURN_OFFSET);
373 
374 		if (!INKERNEL(callpc))
375 			break;
376 
377 		if (aframes > 0) {
378 			aframes--;
379 			if ((aframes == 0) && (caller != 0)) {
380 				pcstack[depth++] = caller;
381 			}
382 		}
383 		else {
384 			pcstack[depth++] = callpc;
385 		}
386 
387 		sp = *(void **)sp;
388 	}
389 
390 	for (; depth < pcstack_limit; depth++) {
391 		pcstack[depth] = 0;
392 	}
393 }
394 #endif
395 
396 int
397 dtrace_getstackdepth(int aframes)
398 {
399 	int depth = 0;
400 	register_t sp;
401 
402 	aframes++;
403 	sp = dtrace_getfp();
404 	depth++;
405 	for(;;) {
406 		if (!INKERNEL((long) sp))
407 			break;
408 		if (!INKERNEL((long) *(void **)sp))
409 			break;
410 		depth++;
411 		sp = *(uintptr_t *)sp;
412 	}
413 	if (depth < aframes)
414 		return 0;
415 	else
416 		return depth - aframes;
417 }
418 
419 ulong_t
420 dtrace_getreg(struct trapframe *rp, uint_t reg)
421 {
422 	if (reg < 32)
423 		return (rp->fixreg[reg]);
424 
425 	switch (reg) {
426 	case 33:
427 		return (rp->lr);
428 	case 34:
429 		return (rp->cr);
430 	case 35:
431 		return (rp->xer);
432 	case 36:
433 		return (rp->ctr);
434 	case 37:
435 		return (rp->srr0);
436 	case 38:
437 		return (rp->srr1);
438 	case 39:
439 		return (rp->exc);
440 	default:
441 		DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP);
442 		return (0);
443 	}
444 }
445 
446 static int
447 dtrace_copycheck(uintptr_t uaddr, uintptr_t kaddr, size_t size)
448 {
449 	ASSERT(INKERNEL(kaddr) && kaddr + size >= kaddr);
450 
451 	if (uaddr + size > VM_MAXUSER_ADDRESS || uaddr + size < uaddr) {
452 		DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
453 		cpu_core[curcpu].cpuc_dtrace_illval = uaddr;
454 		return (0);
455 	}
456 
457 	return (1);
458 }
459 
460 void
461 dtrace_copyin(uintptr_t uaddr, uintptr_t kaddr, size_t size,
462     volatile uint16_t *flags)
463 {
464 	if (dtrace_copycheck(uaddr, kaddr, size))
465 		dtrace_copy(uaddr, kaddr, size);
466 }
467 
468 void
469 dtrace_copyout(uintptr_t kaddr, uintptr_t uaddr, size_t size,
470     volatile uint16_t *flags)
471 {
472 	if (dtrace_copycheck(uaddr, kaddr, size))
473 		dtrace_copy(kaddr, uaddr, size);
474 }
475 
476 void
477 dtrace_copyinstr(uintptr_t uaddr, uintptr_t kaddr, size_t size,
478     volatile uint16_t *flags)
479 {
480 	if (dtrace_copycheck(uaddr, kaddr, size))
481 		dtrace_copystr(uaddr, kaddr, size, flags);
482 }
483 
484 void
485 dtrace_copyoutstr(uintptr_t kaddr, uintptr_t uaddr, size_t size,
486     volatile uint16_t *flags)
487 {
488 	if (dtrace_copycheck(uaddr, kaddr, size))
489 		dtrace_copystr(kaddr, uaddr, size, flags);
490 }
491 
492 uint8_t
493 dtrace_fuword8(void *uaddr)
494 {
495 	if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) {
496 		DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
497 		cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
498 		return (0);
499 	}
500 	return (dtrace_fuword8_nocheck(uaddr));
501 }
502 
503 uint16_t
504 dtrace_fuword16(void *uaddr)
505 {
506 	if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) {
507 		DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
508 		cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
509 		return (0);
510 	}
511 	return (dtrace_fuword16_nocheck(uaddr));
512 }
513 
514 uint32_t
515 dtrace_fuword32(void *uaddr)
516 {
517 	if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) {
518 		DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
519 		cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
520 		return (0);
521 	}
522 	return (dtrace_fuword32_nocheck(uaddr));
523 }
524 
525 uint64_t
526 dtrace_fuword64(void *uaddr)
527 {
528 	if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) {
529 		DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
530 		cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
531 		return (0);
532 	}
533 	return (dtrace_fuword64_nocheck(uaddr));
534 }
535