62e4e4d0273e26fda1d5e63aed9787a8a829029c
[safe/jmp/linux-2.6] / arch / sh / oprofile / backtrace.c
1 /*
2  * SH specific backtracing code for oprofile
3  *
4  * Copyright 2007 STMicroelectronics Ltd.
5  *
6  * Author: Dave Peverley <dpeverley@mpc-data.co.uk>
7  *
8  * Based on ARM oprofile backtrace code by Richard Purdie and in turn, i386
9  * oprofile backtrace code by John Levon, David Smith
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License version 2 as
13  * published by the Free Software Foundation.
14  *
15  */
16 #include <linux/oprofile.h>
17 #include <linux/sched.h>
18 #include <linux/kallsyms.h>
19 #include <linux/mm.h>
20 #include <asm/ptrace.h>
21 #include <asm/uaccess.h>
22 #include <asm/sections.h>
23 #include <asm/stacktrace.h>
24
25 static void backtrace_warning_symbol(void *data, char *msg,
26                                      unsigned long symbol)
27 {
28         /* Ignore warnings */
29 }
30
31 static void backtrace_warning(void *data, char *msg)
32 {
33         /* Ignore warnings */
34 }
35
36 static int backtrace_stack(void *data, char *name)
37 {
38         /* Yes, we want all stacks */
39         return 0;
40 }
41
42 static void backtrace_address(void *data, unsigned long addr, int reliable)
43 {
44         unsigned int *depth = data;
45
46         if ((*depth)--)
47                 oprofile_add_trace(addr);
48 }
49
50 static struct stacktrace_ops backtrace_ops = {
51         .warning = backtrace_warning,
52         .warning_symbol = backtrace_warning_symbol,
53         .stack = backtrace_stack,
54         .address = backtrace_address,
55 };
56
57 /* Limit to stop backtracing too far. */
58 static int backtrace_limit = 20;
59
60 static unsigned long *
61 user_backtrace(unsigned long *stackaddr, struct pt_regs *regs)
62 {
63         unsigned long buf_stack;
64
65         /* Also check accessibility of address */
66         if (!access_ok(VERIFY_READ, stackaddr, sizeof(unsigned long)))
67                 return NULL;
68
69         if (__copy_from_user_inatomic(&buf_stack, stackaddr, sizeof(unsigned long)))
70                 return NULL;
71
72         /* Quick paranoia check */
73         if (buf_stack & 3)
74                 return NULL;
75
76         oprofile_add_trace(buf_stack);
77
78         stackaddr++;
79
80         return stackaddr;
81 }
82
83 /*
84  * |             | /\ Higher addresses
85  * |             |
86  * --------------- stack base (address of current_thread_info)
87  * | thread info |
88  * .             .
89  * |    stack    |
90  * --------------- saved regs->regs[15] value if valid
91  * .             .
92  * --------------- struct pt_regs stored on stack (struct pt_regs *)
93  * |             |
94  * .             .
95  * |             |
96  * --------------- ???
97  * |             |
98  * |             | \/ Lower addresses
99  *
100  * Thus, &pt_regs <-> stack base restricts the valid(ish) fp values
101  */
102 static int valid_kernel_stack(unsigned long *stackaddr, struct pt_regs *regs)
103 {
104         unsigned long stack = (unsigned long)regs;
105         unsigned long stack_base = (stack & ~(THREAD_SIZE - 1)) + THREAD_SIZE;
106
107         return ((unsigned long)stackaddr > stack) && ((unsigned long)stackaddr < stack_base);
108 }
109
110 void sh_backtrace(struct pt_regs * const regs, unsigned int depth)
111 {
112         unsigned long *stackaddr;
113
114         /*
115          * Paranoia - clip max depth as we could get lost in the weeds.
116          */
117         if (depth > backtrace_limit)
118                 depth = backtrace_limit;
119
120         stackaddr = (unsigned long *)regs->regs[15];
121         if (!user_mode(regs)) {
122                 if (depth)
123                         dump_trace(NULL, regs, stackaddr,
124                                    &backtrace_ops, &depth);
125                 return;
126         }
127
128         while (depth-- && (stackaddr != NULL))
129                 stackaddr = user_backtrace(stackaddr, regs);
130 }