aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/sh/oprofile/backtrace.c118
-rw-r--r--arch/sh/oprofile/common.c9
2 files changed, 127 insertions, 0 deletions
diff --git a/arch/sh/oprofile/backtrace.c b/arch/sh/oprofile/backtrace.c
new file mode 100644
index 000000000000..418e834654c1
--- /dev/null
+++ b/arch/sh/oprofile/backtrace.c
@@ -0,0 +1,118 @@
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
24/* Limit to stop backtracing too far. */
25static int backtrace_limit = 20;
26
27static unsigned long *
28user_backtrace(unsigned long *stackaddr, struct pt_regs *regs)
29{
30 unsigned long buf_stack;
31
32 /* Also check accessibility of address */
33 if (!access_ok(VERIFY_READ, stackaddr, sizeof(unsigned long)))
34 return NULL;
35
36 if (__copy_from_user_inatomic(&buf_stack, stackaddr, sizeof(unsigned long)))
37 return NULL;
38
39 /* Quick paranoia check */
40 if (buf_stack & 3)
41 return NULL;
42
43 oprofile_add_trace(buf_stack);
44
45 stackaddr++;
46
47 return stackaddr;
48}
49
50/*
51 * | | /\ Higher addresses
52 * | |
53 * --------------- stack base (address of current_thread_info)
54 * | thread info |
55 * . .
56 * | stack |
57 * --------------- saved regs->regs[15] value if valid
58 * . .
59 * --------------- struct pt_regs stored on stack (struct pt_regs *)
60 * | |
61 * . .
62 * | |
63 * --------------- ???
64 * | |
65 * | | \/ Lower addresses
66 *
67 * Thus, &pt_regs <-> stack base restricts the valid(ish) fp values
68 */
69static int valid_kernel_stack(unsigned long *stackaddr, struct pt_regs *regs)
70{
71 unsigned long stack = (unsigned long)regs;
72 unsigned long stack_base = (stack & ~(THREAD_SIZE - 1)) + THREAD_SIZE;
73
74 return ((unsigned long)stackaddr > stack) && ((unsigned long)stackaddr < stack_base);
75}
76
77static unsigned long *
78kernel_backtrace(unsigned long *stackaddr, struct pt_regs *regs)
79{
80 unsigned long addr;
81
82 /*
83 * If not a valid kernel address, keep going till we find one
84 * or the SP stops being a valid address.
85 */
86 do {
87 addr = *stackaddr++;
88
89 if (__kernel_text_address(addr)) {
90 oprofile_add_trace(addr);
91 break;
92 }
93 } while (valid_kernel_stack(stackaddr, regs));
94
95 return stackaddr;
96}
97
98void sh_backtrace(struct pt_regs * const regs, unsigned int depth)
99{
100 unsigned long *stackaddr;
101
102 /*
103 * Paranoia - clip max depth as we could get lost in the weeds.
104 */
105 if (depth > backtrace_limit)
106 depth = backtrace_limit;
107
108 stackaddr = (unsigned long *)regs->regs[15];
109 if (!user_mode(regs)) {
110 while (depth-- && valid_kernel_stack(stackaddr, regs))
111 stackaddr = kernel_backtrace(stackaddr, regs);
112
113 return;
114 }
115
116 while (depth-- && (stackaddr != NULL))
117 stackaddr = user_backtrace(stackaddr, regs);
118}
diff --git a/arch/sh/oprofile/common.c b/arch/sh/oprofile/common.c
index a4d8c0c9a63c..1d97d64cb95f 100644
--- a/arch/sh/oprofile/common.c
+++ b/arch/sh/oprofile/common.c
@@ -27,6 +27,8 @@ static struct op_sh_model *model;
27 27
28static struct op_counter_config ctr[20]; 28static struct op_counter_config ctr[20];
29 29
30extern void sh_backtrace(struct pt_regs * const regs, unsigned int depth);
31
30static int op_sh_setup(void) 32static int op_sh_setup(void)
31{ 33{
32 /* Pre-compute the values to stuff in the hardware registers. */ 34 /* Pre-compute the values to stuff in the hardware registers. */
@@ -85,6 +87,13 @@ int __init oprofile_arch_init(struct oprofile_operations *ops)
85 struct op_sh_model *lmodel = NULL; 87 struct op_sh_model *lmodel = NULL;
86 int ret; 88 int ret;
87 89
90 /*
91 * Always assign the backtrace op. If the counter initialization
92 * fails, we fall back to the timer which will still make use of
93 * this.
94 */
95 ops->backtrace = sh_backtrace;
96
88 switch (current_cpu_data.type) { 97 switch (current_cpu_data.type) {
89 /* SH-4 types */ 98 /* SH-4 types */
90 case CPU_SH7750: 99 case CPU_SH7750: