initial commit
[freebsd-arm:freebsd-arm.git] / cddl / dev / dtrace / amd64 / dtrace_isa.c
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 }