Commit | Line | Data |
---|---|---|
40a8b421 DP |
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> | |
4e14dfc7 MF |
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 | }; | |
40a8b421 DP |
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 | ||
40a8b421 DP |
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)) { | |
4e14dfc7 MF |
122 | if (depth) |
123 | dump_trace(NULL, regs, stackaddr, | |
124 | &backtrace_ops, &depth); | |
40a8b421 DP |
125 | return; |
126 | } | |
127 | ||
128 | while (depth-- && (stackaddr != NULL)) | |
129 | stackaddr = user_backtrace(stackaddr, regs); | |
130 | } |