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