]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sys/cddl/dev/dtrace/powerpc/dtrace_isa.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / sys / cddl / dev / dtrace / powerpc / 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  * Portions Copyright 2012,2013 Justin Hibbits <jhibbits@freebsd.org>
23  *
24  * $FreeBSD$
25  */
26 /*
27  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
28  * Use is subject to license terms.
29  */
30 #include <sys/cdefs.h>
31
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/kernel.h>
35 #include <sys/stack.h>
36 #include <sys/sysent.h>
37 #include <sys/pcpu.h>
38
39 #include <machine/frame.h>
40 #include <machine/md_var.h>
41 #include <machine/reg.h>
42 #include <machine/stack.h>
43
44 #include <vm/vm.h>
45 #include <vm/vm_param.h>
46 #include <vm/pmap.h>
47
48 #include "regset.h"
49
50 /* Offset to the LR Save word (ppc32) */
51 #define RETURN_OFFSET   4
52 #define RETURN_OFFSET64 8
53
54 #define INKERNEL(x)     ((x) <= VM_MAX_KERNEL_ADDRESS && \
55                 (x) >= VM_MIN_KERNEL_ADDRESS)
56
57 greg_t
58 dtrace_getfp(void)
59 {
60         return (greg_t)__builtin_frame_address(0);
61 }
62
63 void
64 dtrace_getpcstack(pc_t *pcstack, int pcstack_limit, int aframes,
65     uint32_t *intrpc)
66 {
67         int depth = 0;
68         register_t sp;
69         vm_offset_t callpc;
70         pc_t caller = (pc_t) solaris_cpu[curcpu].cpu_dtrace_caller;
71
72         if (intrpc != 0)
73                 pcstack[depth++] = (pc_t) intrpc;
74
75         aframes++;
76
77         sp = dtrace_getfp();
78
79         while (depth < pcstack_limit) {
80                 if (!INKERNEL((long) sp))
81                         break;
82
83 #ifdef __powerpc64__
84                 callpc = *(uintptr_t *)(sp + RETURN_OFFSET64);
85 #else
86                 callpc = *(uintptr_t *)(sp + RETURN_OFFSET);
87 #endif
88
89                 if (!INKERNEL(callpc))
90                         break;
91
92                 if (aframes > 0) {
93                         aframes--;
94                         if ((aframes == 0) && (caller != 0)) {
95                                 pcstack[depth++] = caller;
96                         }
97                 }
98                 else {
99                         pcstack[depth++] = callpc;
100                 }
101
102                 sp = *(uintptr_t*)sp;
103         }
104
105         for (; depth < pcstack_limit; depth++) {
106                 pcstack[depth] = 0;
107         }
108 }
109
110 static int
111 dtrace_getustack_common(uint64_t *pcstack, int pcstack_limit, uintptr_t pc,
112     uintptr_t sp)
113 {
114         proc_t *p = curproc;
115         int ret = 0;
116
117         ASSERT(pcstack == NULL || pcstack_limit > 0);
118
119         while (pc != 0) {
120                 ret++;
121                 if (pcstack != NULL) {
122                         *pcstack++ = (uint64_t)pc;
123                         pcstack_limit--;
124                         if (pcstack_limit <= 0)
125                                 break;
126                 }
127
128                 if (sp == 0)
129                         break;
130
131                 if (SV_PROC_FLAG(p, SV_ILP32)) {
132                         pc = dtrace_fuword32((void *)(sp + RETURN_OFFSET));
133                         sp = dtrace_fuword32((void *)sp);
134                 }
135                 else {
136                         pc = dtrace_fuword64((void *)(sp + RETURN_OFFSET64));
137                         sp = dtrace_fuword64((void *)sp);
138                 }
139         }
140
141         return (ret);
142 }
143
144 void
145 dtrace_getupcstack(uint64_t *pcstack, int pcstack_limit)
146 {
147         proc_t *p = curproc;
148         struct trapframe *tf;
149         uintptr_t pc, sp;
150         volatile uint16_t *flags =
151             (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags;
152         int n;
153
154         if (*flags & CPU_DTRACE_FAULT)
155                 return;
156
157         if (pcstack_limit <= 0)
158                 return;
159
160         /*
161          * If there's no user context we still need to zero the stack.
162          */
163         if (p == NULL || (tf = curthread->td_frame) == NULL)
164                 goto zero;
165
166         *pcstack++ = (uint64_t)p->p_pid;
167         pcstack_limit--;
168
169         if (pcstack_limit <= 0)
170                 return;
171
172         pc = tf->srr0;
173         sp = tf->fixreg[1];
174
175         if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) {
176                 /* 
177                  * In an entry probe.  The frame pointer has not yet been
178                  * pushed (that happens in the function prologue).  The
179                  * best approach is to add the current pc as a missing top
180                  * of stack and back the pc up to the caller, which is stored
181                  * at the current stack pointer address since the call 
182                  * instruction puts it there right before the branch.
183                  */
184
185                 *pcstack++ = (uint64_t)pc;
186                 pcstack_limit--;
187                 if (pcstack_limit <= 0)
188                         return;
189
190                 pc = tf->lr;
191         }
192
193         n = dtrace_getustack_common(pcstack, pcstack_limit, pc, sp);
194         ASSERT(n >= 0);
195         ASSERT(n <= pcstack_limit);
196
197         pcstack += n;
198         pcstack_limit -= n;
199
200 zero:
201         while (pcstack_limit-- > 0)
202                 *pcstack++ = 0;
203 }
204
205 int
206 dtrace_getustackdepth(void)
207 {
208         proc_t *p = curproc;
209         struct trapframe *tf;
210         uintptr_t pc, sp;
211         int n = 0;
212
213         if (p == NULL || (tf = curthread->td_frame) == NULL)
214                 return (0);
215
216         if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_FAULT))
217                 return (-1);
218
219         pc = tf->srr0;
220         sp = tf->fixreg[1];
221
222         if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) {
223                 /* 
224                  * In an entry probe.  The frame pointer has not yet been
225                  * pushed (that happens in the function prologue).  The
226                  * best approach is to add the current pc as a missing top
227                  * of stack and back the pc up to the caller, which is stored
228                  * at the current stack pointer address since the call 
229                  * instruction puts it there right before the branch.
230                  */
231
232                 if (SV_PROC_FLAG(p, SV_ILP32)) {
233                         pc = dtrace_fuword32((void *) sp);
234                 }
235                 else
236                         pc = dtrace_fuword64((void *) sp);
237                 n++;
238         }
239
240         n += dtrace_getustack_common(NULL, 0, pc, sp);
241
242         return (n);
243 }
244
245 void
246 dtrace_getufpstack(uint64_t *pcstack, uint64_t *fpstack, int pcstack_limit)
247 {
248         proc_t *p = curproc;
249         struct trapframe *tf;
250         uintptr_t pc, sp;
251         volatile uint16_t *flags =
252             (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags;
253 #ifdef notyet   /* XXX signal stack */
254         uintptr_t oldcontext;
255         size_t s1, s2;
256 #endif
257
258         if (*flags & CPU_DTRACE_FAULT)
259                 return;
260
261         if (pcstack_limit <= 0)
262                 return;
263
264         /*
265          * If there's no user context we still need to zero the stack.
266          */
267         if (p == NULL || (tf = curthread->td_frame) == NULL)
268                 goto zero;
269
270         *pcstack++ = (uint64_t)p->p_pid;
271         pcstack_limit--;
272
273         if (pcstack_limit <= 0)
274                 return;
275
276         pc = tf->srr0;
277         sp = tf->fixreg[1];
278
279 #ifdef notyet /* XXX signal stack */
280         oldcontext = lwp->lwp_oldcontext;
281         s1 = sizeof (struct xframe) + 2 * sizeof (long);
282         s2 = s1 + sizeof (siginfo_t);
283 #endif
284
285         if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) {
286                 *pcstack++ = (uint64_t)pc;
287                 *fpstack++ = 0;
288                 pcstack_limit--;
289                 if (pcstack_limit <= 0)
290                         return;
291
292                 if (SV_PROC_FLAG(p, SV_ILP32)) {
293                         pc = dtrace_fuword32((void *)sp);
294                 }
295                 else {
296                         pc = dtrace_fuword64((void *)sp);
297                 }
298         }
299
300         while (pc != 0) {
301                 *pcstack++ = (uint64_t)pc;
302                 *fpstack++ = sp;
303                 pcstack_limit--;
304                 if (pcstack_limit <= 0)
305                         break;
306
307                 if (sp == 0)
308                         break;
309
310 #ifdef notyet /* XXX signal stack */
311                 if (oldcontext == sp + s1 || oldcontext == sp + s2) {
312                         ucontext_t *ucp = (ucontext_t *)oldcontext;
313                         greg_t *gregs = ucp->uc_mcontext.gregs;
314
315                         sp = dtrace_fulword(&gregs[REG_FP]);
316                         pc = dtrace_fulword(&gregs[REG_PC]);
317
318                         oldcontext = dtrace_fulword(&ucp->uc_link);
319                 } else
320 #endif /* XXX */
321                 {
322                         if (SV_PROC_FLAG(p, SV_ILP32)) {
323                                 pc = dtrace_fuword32((void *)(sp + RETURN_OFFSET));
324                                 sp = dtrace_fuword32((void *)sp);
325                         }
326                         else {
327                                 pc = dtrace_fuword64((void *)(sp + RETURN_OFFSET64));
328                                 sp = dtrace_fuword64((void *)sp);
329                         }
330                 }
331
332                 /*
333                  * This is totally bogus:  if we faulted, we're going to clear
334                  * the fault and break.  This is to deal with the apparently
335                  * broken Java stacks on x86.
336                  */
337                 if (*flags & CPU_DTRACE_FAULT) {
338                         *flags &= ~CPU_DTRACE_FAULT;
339                         break;
340                 }
341         }
342
343 zero:
344         while (pcstack_limit-- > 0)
345                 *pcstack++ = 0;
346 }
347
348 /*ARGSUSED*/
349 uint64_t
350 dtrace_getarg(int arg, int aframes)
351 {
352         uintptr_t val;
353         uintptr_t *fp = (uintptr_t *)dtrace_getfp();
354         uintptr_t *stack;
355         int i;
356
357         /*
358          * A total of 8 arguments are passed via registers; any argument with
359          * index of 7 or lower is therefore in a register.
360          */
361         int inreg = 7;
362
363         for (i = 1; i <= aframes; i++) {
364                 fp = (uintptr_t *)*fp;
365
366                 /*
367                  * On ppc32 AIM, and booke, trapexit() is the immediately following
368                  * label.  On ppc64 AIM trapexit() follows a nop.
369                  */
370                 if (((long)(fp[1]) == (long)trapexit) ||
371                                 (((long)(fp[1]) + 4 == (long)trapexit))) {
372                         /*
373                          * In the case of powerpc, we will use the pointer to the regs
374                          * structure that was pushed when we took the trap.  To get this
375                          * structure, we must increment beyond the frame structure.  If the
376                          * argument that we're seeking is passed on the stack, we'll pull
377                          * the true stack pointer out of the saved registers and decrement
378                          * our argument by the number of arguments passed in registers; if
379                          * the argument we're seeking is passed in regsiters, we can just
380                          * load it directly.
381                          */
382 #ifdef __powerpc64__
383                         struct reg *rp = (struct reg *)((uintptr_t)fp[0] + 48);
384 #else
385                         struct reg *rp = (struct reg *)((uintptr_t)fp[0] + 8);
386 #endif
387
388                         if (arg <= inreg) {
389                                 stack = &rp->fixreg[3];
390                         } else {
391                                 stack = (uintptr_t *)(rp->fixreg[1]);
392                                 arg -= inreg;
393                         }
394                         goto load;
395                 }
396
397         }
398
399         /*
400          * We know that we did not come through a trap to get into
401          * dtrace_probe() -- the provider simply called dtrace_probe()
402          * directly.  As this is the case, we need to shift the argument
403          * that we're looking for:  the probe ID is the first argument to
404          * dtrace_probe(), so the argument n will actually be found where
405          * one would expect to find argument (n + 1).
406          */
407         arg++;
408
409         if (arg <= inreg) {
410                 /*
411                  * This shouldn't happen.  If the argument is passed in a
412                  * register then it should have been, well, passed in a
413                  * register...
414                  */
415                 DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP);
416                 return (0);
417         }
418
419         arg -= (inreg + 1);
420         stack = fp + 2;
421
422 load:
423         DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
424         val = stack[arg];
425         DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT);
426
427         return (val);
428         return (0);
429 }
430
431 int
432 dtrace_getstackdepth(int aframes)
433 {
434         int depth = 0;
435         register_t sp;
436
437         aframes++;
438         sp = dtrace_getfp();
439         depth++;
440         for(;;) {
441                 if (!INKERNEL((long) sp))
442                         break;
443                 if (!INKERNEL((long) *(void **)sp))
444                         break;
445                 depth++;
446                 sp = *(uintptr_t *)sp;
447         }
448         if (depth < aframes)
449                 return 0;
450         else
451                 return depth - aframes;
452 }
453
454 ulong_t
455 dtrace_getreg(struct trapframe *rp, uint_t reg)
456 {
457         if (reg < 32)
458                 return (rp->fixreg[reg]);
459
460         switch (reg) {
461         case 33:
462                 return (rp->lr);
463         case 34:
464                 return (rp->cr);
465         case 35:
466                 return (rp->xer);
467         case 36:
468                 return (rp->ctr);
469         case 37:
470                 return (rp->srr0);
471         case 38:
472                 return (rp->srr1);
473         case 39:
474                 return (rp->exc);
475         default:
476                 DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP);
477                 return (0);
478         }
479 }
480
481 static int
482 dtrace_copycheck(uintptr_t uaddr, uintptr_t kaddr, size_t size)
483 {
484         ASSERT(INKERNEL(kaddr) && kaddr + size >= kaddr);
485
486         if (uaddr + size > VM_MAXUSER_ADDRESS || uaddr + size < uaddr) {
487                 DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
488                 cpu_core[curcpu].cpuc_dtrace_illval = uaddr;
489                 return (0);
490         }
491
492         return (1);
493 }
494
495 void
496 dtrace_copyin(uintptr_t uaddr, uintptr_t kaddr, size_t size,
497     volatile uint16_t *flags)
498 {
499         if (dtrace_copycheck(uaddr, kaddr, size))
500                 if (copyin((const void *)uaddr, (void *)kaddr, size)) {
501                         DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
502                         cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
503                 }
504 }
505
506 void
507 dtrace_copyout(uintptr_t kaddr, uintptr_t uaddr, size_t size,
508     volatile uint16_t *flags)
509 {
510         if (dtrace_copycheck(uaddr, kaddr, size)) {
511                 if (copyout((const void *)kaddr, (void *)uaddr, size)) {
512                         DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
513                         cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
514                 }
515         }
516 }
517
518 void
519 dtrace_copyinstr(uintptr_t uaddr, uintptr_t kaddr, size_t size,
520     volatile uint16_t *flags)
521 {
522         size_t actual;
523         int    error;
524
525         if (dtrace_copycheck(uaddr, kaddr, size)) {
526                 error = copyinstr((const void *)uaddr, (void *)kaddr,
527                     size, &actual);
528                 
529                 /* ENAMETOOLONG is not a fault condition. */
530                 if (error && error != ENAMETOOLONG) {
531                         DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
532                         cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
533                 }
534         }
535 }
536
537 /*
538  * The bulk of this function could be replaced to match dtrace_copyinstr() 
539  * if we ever implement a copyoutstr().
540  */
541 void
542 dtrace_copyoutstr(uintptr_t kaddr, uintptr_t uaddr, size_t size,
543     volatile uint16_t *flags)
544 {
545         size_t len;
546
547         if (dtrace_copycheck(uaddr, kaddr, size)) {
548                 len = strlen((const char *)kaddr);
549                 if (len > size)
550                         len = size;
551
552                 if (copyout((const void *)kaddr, (void *)uaddr, len)) {
553                         DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
554                         cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
555                 }
556         }
557 }
558
559 uint8_t
560 dtrace_fuword8(void *uaddr)
561 {
562         if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) {
563                 DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
564                 cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
565                 return (0);
566         }
567         return (fubyte(uaddr));
568 }
569
570 uint16_t
571 dtrace_fuword16(void *uaddr)
572 {
573         uint16_t ret = 0;
574
575         if (dtrace_copycheck((uintptr_t)uaddr, (uintptr_t)&ret, sizeof(ret))) {
576                 if (copyin((const void *)uaddr, (void *)&ret, sizeof(ret))) {
577                         DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
578                         cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
579                 }
580         }
581         return ret;
582 }
583
584 uint32_t
585 dtrace_fuword32(void *uaddr)
586 {
587         if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) {
588                 DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
589                 cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
590                 return (0);
591         }
592         return (fuword32(uaddr));
593 }
594
595 uint64_t
596 dtrace_fuword64(void *uaddr)
597 {
598         uint64_t ret = 0;
599
600         if (dtrace_copycheck((uintptr_t)uaddr, (uintptr_t)&ret, sizeof(ret))) {
601                 if (copyin((const void *)uaddr, (void *)&ret, sizeof(ret))) {
602                         DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
603                         cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
604                 }
605         }
606         return ret;
607 }
608
609 uintptr_t
610 dtrace_fulword(void *uaddr)
611 {
612         uintptr_t ret = 0;
613
614         if (dtrace_copycheck((uintptr_t)uaddr, (uintptr_t)&ret, sizeof(ret))) {
615                 if (copyin((const void *)uaddr, (void *)&ret, sizeof(ret))) {
616                         DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
617                         cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
618                 }
619         }
620         return ret;
621 }