xref: /freebsd/sys/cddl/dev/dtrace/amd64/dtrace_isa.c (revision 39beb93c3f8bdbf72a61fda42300b5ebed7390c8)
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/pcpu.h>
35 
36 #include <machine/frame.h>
37 #include <machine/md_var.h>
38 #include <machine/reg.h>
39 #include <machine/stack.h>
40 
41 #include <vm/vm.h>
42 #include <vm/vm_param.h>
43 #include <vm/pmap.h>
44 
45 extern uintptr_t kernbase;
46 uintptr_t kernelbase = (uintptr_t) &kernbase;
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 void
54 dtrace_getpcstack(pc_t *pcstack, int pcstack_limit, int aframes,
55     uint32_t *intrpc)
56 {
57 	int depth = 0;
58 	register_t rbp;
59 	struct amd64_frame *frame;
60 	vm_offset_t callpc;
61 	pc_t caller = (pc_t) solaris_cpu[curcpu].cpu_dtrace_caller;
62 
63 	if (intrpc != 0)
64 		pcstack[depth++] = (pc_t) intrpc;
65 
66 	aframes++;
67 
68 	__asm __volatile("movq %%rbp,%0" : "=r" (rbp));
69 
70 	frame = (struct amd64_frame *)rbp;
71 	while (depth < pcstack_limit) {
72 		if (!INKERNEL((long) frame))
73 			break;
74 
75 		callpc = frame->f_retaddr;
76 
77 		if (!INKERNEL(callpc))
78 			break;
79 
80 		if (aframes > 0) {
81 			aframes--;
82 			if ((aframes == 0) && (caller != 0)) {
83 				pcstack[depth++] = caller;
84 			}
85 		}
86 		else {
87 			pcstack[depth++] = callpc;
88 		}
89 
90 		if (frame->f_frame <= frame ||
91 		    (vm_offset_t)frame->f_frame >=
92 		    (vm_offset_t)rbp + KSTACK_PAGES * PAGE_SIZE)
93 			break;
94 		frame = frame->f_frame;
95 	}
96 
97 	for (; depth < pcstack_limit; depth++) {
98 		pcstack[depth] = 0;
99 	}
100 }
101 
102 static int
103 dtrace_getustack_common(uint64_t *pcstack, int pcstack_limit, uintptr_t pc,
104     uintptr_t sp)
105 {
106 	volatile uint16_t *flags =
107 	    (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags;
108 	struct amd64_frame *frame;
109 	int ret = 0;
110 
111 	ASSERT(pcstack == NULL || pcstack_limit > 0);
112 
113 	while (pc != 0 && sp != 0) {
114 		ret++;
115 		if (pcstack != NULL) {
116 			*pcstack++ = (uint64_t)pc;
117 			pcstack_limit--;
118 			if (pcstack_limit <= 0)
119 				break;
120 		}
121 
122 		frame = (struct amd64_frame *) sp;
123 
124 		pc = dtrace_fulword(&frame->f_retaddr);
125 		sp = dtrace_fulword(&frame->f_frame);
126 
127 		/*
128 		 * This is totally bogus:  if we faulted, we're going to clear
129 		 * the fault and break.  This is to deal with the apparently
130 		 * broken Java stacks on x86.
131 		 */
132 		if (*flags & CPU_DTRACE_FAULT) {
133 			*flags &= ~CPU_DTRACE_FAULT;
134 			break;
135 		}
136 	}
137 
138 	return (ret);
139 }
140 
141 void
142 dtrace_getupcstack(uint64_t *pcstack, int pcstack_limit)
143 {
144 	proc_t *p = curproc;
145 	struct trapframe *tf;
146 	uintptr_t pc, sp;
147 	volatile uint16_t *flags =
148 	    (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags;
149 	int n;
150 
151 	if (*flags & CPU_DTRACE_FAULT)
152 		return;
153 
154 	if (pcstack_limit <= 0)
155 		return;
156 
157 	/*
158 	 * If there's no user context we still need to zero the stack.
159 	 */
160 	if (p == NULL || (tf = curthread->td_frame) == NULL)
161 		goto zero;
162 
163 	*pcstack++ = (uint64_t)p->p_pid;
164 	pcstack_limit--;
165 
166 	if (pcstack_limit <= 0)
167 		return;
168 
169 	pc = tf->tf_rip;
170 	sp = tf->tf_rsp;
171 
172 	if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) {
173 		*pcstack++ = (uint64_t)pc;
174 		pcstack_limit--;
175 		if (pcstack_limit <= 0)
176 			return;
177 
178 		pc = dtrace_fulword((void *) sp);
179 	}
180 
181 	n = dtrace_getustack_common(pcstack, pcstack_limit, pc, sp);
182 	ASSERT(n >= 0);
183 	ASSERT(n <= pcstack_limit);
184 
185 	pcstack += n;
186 	pcstack_limit -= n;
187 
188 zero:
189 	while (pcstack_limit-- > 0)
190 		*pcstack++ = 0;
191 }
192 
193 int
194 dtrace_getustackdepth(void)
195 {
196 	proc_t *p = curproc;
197 	struct trapframe *tf;
198 	uintptr_t pc, sp;
199 	int n = 0;
200 
201 	if (p == NULL || (tf = curthread->td_frame) == NULL)
202 		return (0);
203 
204 	if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_FAULT))
205 		return (-1);
206 
207 	pc = tf->tf_rip;
208 	sp = tf->tf_rsp;
209 
210 	if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) {
211 		n++;
212 
213 		pc = dtrace_fulword((void *) sp);
214 	}
215 
216 	n += dtrace_getustack_common(NULL, 0, pc, sp);
217 
218 	return (n);
219 }
220 
221 #ifdef notyet
222 void
223 dtrace_getufpstack(uint64_t *pcstack, uint64_t *fpstack, int pcstack_limit)
224 {
225 	klwp_t *lwp = ttolwp(curthread);
226 	proc_t *p = curproc;
227 	struct regs *rp;
228 	uintptr_t pc, sp, oldcontext;
229 	volatile uint16_t *flags =
230 	    (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags;
231 	size_t s1, s2;
232 
233 	if (*flags & CPU_DTRACE_FAULT)
234 		return;
235 
236 	if (pcstack_limit <= 0)
237 		return;
238 
239 	/*
240 	 * If there's no user context we still need to zero the stack.
241 	 */
242 	if (lwp == NULL || p == NULL || (rp = lwp->lwp_regs) == NULL)
243 		goto zero;
244 
245 	*pcstack++ = (uint64_t)p->p_pid;
246 	pcstack_limit--;
247 
248 	if (pcstack_limit <= 0)
249 		return;
250 
251 	pc = rp->r_pc;
252 	sp = rp->r_fp;
253 	oldcontext = lwp->lwp_oldcontext;
254 
255 	s1 = sizeof (struct xframe) + 2 * sizeof (long);
256 	s2 = s1 + sizeof (siginfo_t);
257 
258 	if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) {
259 		*pcstack++ = (uint64_t)pc;
260 		*fpstack++ = 0;
261 		pcstack_limit--;
262 		if (pcstack_limit <= 0)
263 			return;
264 
265 		if (p->p_model == DATAMODEL_NATIVE)
266 			pc = dtrace_fulword((void *)rp->r_sp);
267 		else
268 			pc = dtrace_fuword32((void *)rp->r_sp);
269 	}
270 
271 	while (pc != 0 && sp != 0) {
272 		*pcstack++ = (uint64_t)pc;
273 		*fpstack++ = sp;
274 		pcstack_limit--;
275 		if (pcstack_limit <= 0)
276 			break;
277 
278 		if (oldcontext == sp + s1 || oldcontext == sp + s2) {
279 			ucontext_t *ucp = (ucontext_t *)oldcontext;
280 			greg_t *gregs = ucp->uc_mcontext.gregs;
281 
282 			sp = dtrace_fulword(&gregs[REG_FP]);
283 			pc = dtrace_fulword(&gregs[REG_PC]);
284 
285 			oldcontext = dtrace_fulword(&ucp->uc_link);
286 		} else {
287 			struct xframe *fr = (struct xframe *)sp;
288 
289 			pc = dtrace_fulword(&fr->fr_savpc);
290 			sp = dtrace_fulword(&fr->fr_savfp);
291 		}
292 
293 		/*
294 		 * This is totally bogus:  if we faulted, we're going to clear
295 		 * the fault and break.  This is to deal with the apparently
296 		 * broken Java stacks on x86.
297 		 */
298 		if (*flags & CPU_DTRACE_FAULT) {
299 			*flags &= ~CPU_DTRACE_FAULT;
300 			break;
301 		}
302 	}
303 
304 zero:
305 	while (pcstack_limit-- > 0)
306 		*pcstack++ = NULL;
307 }
308 #endif
309 
310 /*ARGSUSED*/
311 uint64_t
312 dtrace_getarg(int arg, int aframes)
313 {
314 	uintptr_t val;
315 	struct amd64_frame *fp = (struct amd64_frame *)dtrace_getfp();
316 	uintptr_t *stack;
317 	int i;
318 
319 	/*
320 	 * A total of 6 arguments are passed via registers; any argument with
321 	 * index of 5 or lower is therefore in a register.
322 	 */
323 	int inreg = 5;
324 
325 	for (i = 1; i <= aframes; i++) {
326 		fp = fp->f_frame;
327 
328 		if (fp->f_retaddr == (long)dtrace_invop_callsite) {
329 			/*
330 			 * In the case of amd64, we will use the pointer to the
331 			 * regs structure that was pushed when we took the
332 			 * trap.  To get this structure, we must increment
333 			 * beyond the frame structure, and then again beyond
334 			 * the calling RIP stored in dtrace_invop().  If the
335 			 * argument that we're seeking is passed on the stack,
336 			 * we'll pull the true stack pointer out of the saved
337 			 * registers and decrement our argument by the number
338 			 * of arguments passed in registers; if the argument
339 			 * we're seeking is passed in regsiters, we can just
340 			 * load it directly.
341 			 */
342 			struct reg *rp = (struct reg *)((uintptr_t)&fp[1] +
343 			    sizeof (uintptr_t));
344 
345 			if (arg <= inreg) {
346 				stack = (uintptr_t *)&rp->r_rdi;
347 			} else {
348 				stack = (uintptr_t *)(rp->r_rsp);
349 				arg -= inreg;
350 			}
351 			goto load;
352 		}
353 
354 	}
355 
356 	/*
357 	 * We know that we did not come through a trap to get into
358 	 * dtrace_probe() -- the provider simply called dtrace_probe()
359 	 * directly.  As this is the case, we need to shift the argument
360 	 * that we're looking for:  the probe ID is the first argument to
361 	 * dtrace_probe(), so the argument n will actually be found where
362 	 * one would expect to find argument (n + 1).
363 	 */
364 	arg++;
365 
366 	if (arg <= inreg) {
367 		/*
368 		 * This shouldn't happen.  If the argument is passed in a
369 		 * register then it should have been, well, passed in a
370 		 * register...
371 		 */
372 		DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP);
373 		return (0);
374 	}
375 
376 	arg -= (inreg + 1);
377 	stack = (uintptr_t *)&fp[1];
378 
379 load:
380 	DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
381 	val = stack[arg];
382 	DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT);
383 
384 	return (val);
385 	return (0);
386 }
387 
388 int
389 dtrace_getstackdepth(int aframes)
390 {
391 	int depth = 0;
392 	struct amd64_frame *frame;
393 	vm_offset_t rbp;
394 
395 	aframes++;
396 	rbp = dtrace_getfp();
397 	frame = (struct amd64_frame *)rbp;
398 	depth++;
399 	for(;;) {
400 		if (!INKERNEL((long) frame))
401 			break;
402 		if (!INKERNEL((long) frame->f_frame))
403 			break;
404 		depth++;
405 		if (frame->f_frame <= frame ||
406 		    (vm_offset_t)frame->f_frame >=
407 		    (vm_offset_t)rbp + KSTACK_PAGES * PAGE_SIZE)
408 			break;
409 		frame = frame->f_frame;
410 	}
411 	if (depth < aframes)
412 		return 0;
413 	else
414 		return depth - aframes;
415 }
416 
417 #ifdef notyet
418 ulong_t
419 dtrace_getreg(struct regs *rp, uint_t reg)
420 {
421 #if defined(__amd64)
422 	int regmap[] = {
423 		REG_GS,		/* GS */
424 		REG_FS,		/* FS */
425 		REG_ES,		/* ES */
426 		REG_DS,		/* DS */
427 		REG_RDI,	/* EDI */
428 		REG_RSI,	/* ESI */
429 		REG_RBP,	/* EBP */
430 		REG_RSP,	/* ESP */
431 		REG_RBX,	/* EBX */
432 		REG_RDX,	/* EDX */
433 		REG_RCX,	/* ECX */
434 		REG_RAX,	/* EAX */
435 		REG_TRAPNO,	/* TRAPNO */
436 		REG_ERR,	/* ERR */
437 		REG_RIP,	/* EIP */
438 		REG_CS,		/* CS */
439 		REG_RFL,	/* EFL */
440 		REG_RSP,	/* UESP */
441 		REG_SS		/* SS */
442 	};
443 
444 	if (reg <= SS) {
445 		if (reg >= sizeof (regmap) / sizeof (int)) {
446 			DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP);
447 			return (0);
448 		}
449 
450 		reg = regmap[reg];
451 	} else {
452 		reg -= SS + 1;
453 	}
454 
455 	switch (reg) {
456 	case REG_RDI:
457 		return (rp->r_rdi);
458 	case REG_RSI:
459 		return (rp->r_rsi);
460 	case REG_RDX:
461 		return (rp->r_rdx);
462 	case REG_RCX:
463 		return (rp->r_rcx);
464 	case REG_R8:
465 		return (rp->r_r8);
466 	case REG_R9:
467 		return (rp->r_r9);
468 	case REG_RAX:
469 		return (rp->r_rax);
470 	case REG_RBX:
471 		return (rp->r_rbx);
472 	case REG_RBP:
473 		return (rp->r_rbp);
474 	case REG_R10:
475 		return (rp->r_r10);
476 	case REG_R11:
477 		return (rp->r_r11);
478 	case REG_R12:
479 		return (rp->r_r12);
480 	case REG_R13:
481 		return (rp->r_r13);
482 	case REG_R14:
483 		return (rp->r_r14);
484 	case REG_R15:
485 		return (rp->r_r15);
486 	case REG_DS:
487 		return (rp->r_ds);
488 	case REG_ES:
489 		return (rp->r_es);
490 	case REG_FS:
491 		return (rp->r_fs);
492 	case REG_GS:
493 		return (rp->r_gs);
494 	case REG_TRAPNO:
495 		return (rp->r_trapno);
496 	case REG_ERR:
497 		return (rp->r_err);
498 	case REG_RIP:
499 		return (rp->r_rip);
500 	case REG_CS:
501 		return (rp->r_cs);
502 	case REG_SS:
503 		return (rp->r_ss);
504 	case REG_RFL:
505 		return (rp->r_rfl);
506 	case REG_RSP:
507 		return (rp->r_rsp);
508 	default:
509 		DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP);
510 		return (0);
511 	}
512 
513 #else
514 	if (reg > SS) {
515 		DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP);
516 		return (0);
517 	}
518 
519 	return ((&rp->r_gs)[reg]);
520 #endif
521 }
522 #endif
523 
524 static int
525 dtrace_copycheck(uintptr_t uaddr, uintptr_t kaddr, size_t size)
526 {
527 	ASSERT(kaddr >= kernelbase && kaddr + size >= kaddr);
528 
529 	if (uaddr + size >= kernelbase || uaddr + size < uaddr) {
530 		DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
531 		cpu_core[curcpu].cpuc_dtrace_illval = uaddr;
532 		return (0);
533 	}
534 
535 	return (1);
536 }
537 
538 void
539 dtrace_copyin(uintptr_t uaddr, uintptr_t kaddr, size_t size,
540     volatile uint16_t *flags)
541 {
542 	if (dtrace_copycheck(uaddr, kaddr, size))
543 		dtrace_copy(uaddr, kaddr, size);
544 }
545 
546 void
547 dtrace_copyout(uintptr_t kaddr, uintptr_t uaddr, size_t size,
548     volatile uint16_t *flags)
549 {
550 	if (dtrace_copycheck(uaddr, kaddr, size))
551 		dtrace_copy(kaddr, uaddr, size);
552 }
553 
554 void
555 dtrace_copyinstr(uintptr_t uaddr, uintptr_t kaddr, size_t size,
556     volatile uint16_t *flags)
557 {
558 	if (dtrace_copycheck(uaddr, kaddr, size))
559 		dtrace_copystr(uaddr, kaddr, size, flags);
560 }
561 
562 void
563 dtrace_copyoutstr(uintptr_t kaddr, uintptr_t uaddr, size_t size,
564     volatile uint16_t *flags)
565 {
566 	if (dtrace_copycheck(uaddr, kaddr, size))
567 		dtrace_copystr(kaddr, uaddr, size, flags);
568 }
569 
570 uint8_t
571 dtrace_fuword8(void *uaddr)
572 {
573 	if ((uintptr_t)uaddr >= kernelbase) {
574 		DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
575 		cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
576 		return (0);
577 	}
578 	return (dtrace_fuword8_nocheck(uaddr));
579 }
580 
581 uint16_t
582 dtrace_fuword16(void *uaddr)
583 {
584 	if ((uintptr_t)uaddr >= kernelbase) {
585 		DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
586 		cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
587 		return (0);
588 	}
589 	return (dtrace_fuword16_nocheck(uaddr));
590 }
591 
592 uint32_t
593 dtrace_fuword32(void *uaddr)
594 {
595 	if ((uintptr_t)uaddr >= kernelbase) {
596 		DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
597 		cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
598 		return (0);
599 	}
600 	return (dtrace_fuword32_nocheck(uaddr));
601 }
602 
603 uint64_t
604 dtrace_fuword64(void *uaddr)
605 {
606 	if ((uintptr_t)uaddr >= kernelbase) {
607 		DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
608 		cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
609 		return (0);
610 	}
611 	return (dtrace_fuword64_nocheck(uaddr));
612 }
613