aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/xtensa/Kconfig1
-rw-r--r--arch/xtensa/Makefile1
-rw-r--r--arch/xtensa/kernel/entry.S2
-rw-r--r--arch/xtensa/oprofile/Makefile9
-rw-r--r--arch/xtensa/oprofile/backtrace.c171
-rw-r--r--arch/xtensa/oprofile/init.c26
6 files changed, 209 insertions, 1 deletions
diff --git a/arch/xtensa/Kconfig b/arch/xtensa/Kconfig
index f83780f8f290..f61f7362c8f6 100644
--- a/arch/xtensa/Kconfig
+++ b/arch/xtensa/Kconfig
@@ -17,6 +17,7 @@ config XTENSA
17 select ARCH_WANT_OPTIONAL_GPIOLIB 17 select ARCH_WANT_OPTIONAL_GPIOLIB
18 select CLONE_BACKWARDS 18 select CLONE_BACKWARDS
19 select IRQ_DOMAIN 19 select IRQ_DOMAIN
20 select HAVE_OPROFILE
20 help 21 help
21 Xtensa processors are 32-bit RISC machines designed by Tensilica 22 Xtensa processors are 32-bit RISC machines designed by Tensilica
22 primarily for embedded systems. These processors are both 23 primarily for embedded systems. These processors are both
diff --git a/arch/xtensa/Makefile b/arch/xtensa/Makefile
index afec8f000c4a..136224b74d4f 100644
--- a/arch/xtensa/Makefile
+++ b/arch/xtensa/Makefile
@@ -87,6 +87,7 @@ core-y += arch/xtensa/kernel/ arch/xtensa/mm/
87core-y += $(buildvar) $(buildplf) 87core-y += $(buildvar) $(buildplf)
88 88
89libs-y += arch/xtensa/lib/ $(LIBGCC) 89libs-y += arch/xtensa/lib/ $(LIBGCC)
90drivers-$(CONFIG_OPROFILE) += arch/xtensa/oprofile/
90 91
91ifneq ($(CONFIG_BUILTIN_DTB),"") 92ifneq ($(CONFIG_BUILTIN_DTB),"")
92core-$(CONFIG_OF) += arch/xtensa/boot/dts/ 93core-$(CONFIG_OF) += arch/xtensa/boot/dts/
diff --git a/arch/xtensa/kernel/entry.S b/arch/xtensa/kernel/entry.S
index 0ace2acbbad0..70d5a9e33573 100644
--- a/arch/xtensa/kernel/entry.S
+++ b/arch/xtensa/kernel/entry.S
@@ -399,7 +399,7 @@ common_exception:
399 callx4 a4 399 callx4 a4
400 400
401 /* Jump here for exception exit */ 401 /* Jump here for exception exit */
402 402 .global common_exception_return
403common_exception_return: 403common_exception_return:
404 404
405 /* Jump if we are returning from kernel exceptions. */ 405 /* Jump if we are returning from kernel exceptions. */
diff --git a/arch/xtensa/oprofile/Makefile b/arch/xtensa/oprofile/Makefile
new file mode 100644
index 000000000000..69ffbe80f184
--- /dev/null
+++ b/arch/xtensa/oprofile/Makefile
@@ -0,0 +1,9 @@
1obj-$(CONFIG_OPROFILE) += oprofile.o
2
3DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \
4 oprof.o cpu_buffer.o buffer_sync.o \
5 event_buffer.o oprofile_files.o \
6 oprofilefs.o oprofile_stats.o \
7 timer_int.o )
8
9oprofile-y := $(DRIVER_OBJS) init.o backtrace.o
diff --git a/arch/xtensa/oprofile/backtrace.c b/arch/xtensa/oprofile/backtrace.c
new file mode 100644
index 000000000000..66f32ee2c982
--- /dev/null
+++ b/arch/xtensa/oprofile/backtrace.c
@@ -0,0 +1,171 @@
1/**
2 * @file backtrace.c
3 *
4 * @remark Copyright 2008 Tensilica Inc.
5 * @remark Read the file COPYING
6 *
7 */
8
9#include <linux/oprofile.h>
10#include <linux/sched.h>
11#include <linux/mm.h>
12#include <asm/ptrace.h>
13#include <asm/uaccess.h>
14#include <asm/traps.h>
15
16/* Address of common_exception_return, used to check the
17 * transition from kernel to user space.
18 */
19extern int common_exception_return;
20
21/* A struct that maps to the part of the frame containing the a0 and
22 * a1 registers.
23 */
24struct frame_start {
25 unsigned long a0;
26 unsigned long a1;
27};
28
29static void xtensa_backtrace_user(struct pt_regs *regs, unsigned int depth)
30{
31 unsigned long windowstart = regs->windowstart;
32 unsigned long windowbase = regs->windowbase;
33 unsigned long a0 = regs->areg[0];
34 unsigned long a1 = regs->areg[1];
35 unsigned long pc = MAKE_PC_FROM_RA(a0, regs->pc);
36 int index;
37
38 /* First add the current PC to the trace. */
39 if (pc != 0 && pc <= TASK_SIZE)
40 oprofile_add_trace(pc);
41 else
42 return;
43
44 /* Two steps:
45 *
46 * 1. Look through the register window for the
47 * previous PCs in the call trace.
48 *
49 * 2. Look on the stack.
50 */
51
52 /* Step 1. */
53 /* Rotate WINDOWSTART to move the bit corresponding to
54 * the current window to the bit #0.
55 */
56 windowstart = (windowstart << WSBITS | windowstart) >> windowbase;
57
58 /* Look for bits that are set, they correspond to
59 * valid windows.
60 */
61 for (index = WSBITS - 1; (index > 0) && depth; depth--, index--)
62 if (windowstart & (1 << index)) {
63 /* Read a0 and a1 from the
64 * corresponding position in AREGs.
65 */
66 a0 = regs->areg[index * 4];
67 a1 = regs->areg[index * 4 + 1];
68 /* Get the PC from a0 and a1. */
69 pc = MAKE_PC_FROM_RA(a0, pc);
70
71 /* Add the PC to the trace. */
72 if (pc != 0 && pc <= TASK_SIZE)
73 oprofile_add_trace(pc);
74 else
75 return;
76 }
77
78 /* Step 2. */
79 /* We are done with the register window, we need to
80 * look through the stack.
81 */
82 if (depth > 0) {
83 /* Start from the a1 register. */
84 /* a1 = regs->areg[1]; */
85 while (a0 != 0 && depth--) {
86
87 struct frame_start frame_start;
88 /* Get the location for a1, a0 for the
89 * previous frame from the current a1.
90 */
91 unsigned long *psp = (unsigned long *)a1;
92 psp -= 4;
93
94 /* Check if the region is OK to access. */
95 if (!access_ok(VERIFY_READ, psp, sizeof(frame_start)))
96 return;
97 /* Copy a1, a0 from user space stack frame. */
98 if (__copy_from_user_inatomic(&frame_start, psp,
99 sizeof(frame_start)))
100 return;
101
102 a0 = frame_start.a0;
103 a1 = frame_start.a1;
104 pc = MAKE_PC_FROM_RA(a0, pc);
105
106 if (pc != 0 && pc <= TASK_SIZE)
107 oprofile_add_trace(pc);
108 else
109 return;
110 }
111 }
112}
113
114static void xtensa_backtrace_kernel(struct pt_regs *regs, unsigned int depth)
115{
116 unsigned long pc = regs->pc;
117 unsigned long *psp;
118 unsigned long sp_start, sp_end;
119 unsigned long a0 = regs->areg[0];
120 unsigned long a1 = regs->areg[1];
121
122 sp_start = a1 & ~(THREAD_SIZE-1);
123 sp_end = sp_start + THREAD_SIZE;
124
125 /* Spill the register window to the stack first. */
126 spill_registers();
127
128 /* Read the stack frames one by one and create the PC
129 * from the a0 and a1 registers saved there.
130 */
131 while (a1 > sp_start && a1 < sp_end && depth--) {
132 pc = MAKE_PC_FROM_RA(a0, pc);
133
134 /* Add the PC to the trace. */
135 if (kernel_text_address(pc))
136 oprofile_add_trace(pc);
137
138 if (pc == (unsigned long) &common_exception_return) {
139 regs = (struct pt_regs *)a1;
140 if (user_mode(regs)) {
141 pc = regs->pc;
142 if (pc != 0 && pc <= TASK_SIZE)
143 oprofile_add_trace(pc);
144 else
145 return;
146 return xtensa_backtrace_user(regs, depth);
147 }
148 a0 = regs->areg[0];
149 a1 = regs->areg[1];
150 continue;
151 }
152
153 psp = (unsigned long *)a1;
154
155 a0 = *(psp - 4);
156 a1 = *(psp - 3);
157
158 if (a1 <= (unsigned long)psp)
159 return;
160
161 }
162 return;
163}
164
165void xtensa_backtrace(struct pt_regs * const regs, unsigned int depth)
166{
167 if (user_mode(regs))
168 xtensa_backtrace_user(regs, depth);
169 else
170 xtensa_backtrace_kernel(regs, depth);
171}
diff --git a/arch/xtensa/oprofile/init.c b/arch/xtensa/oprofile/init.c
new file mode 100644
index 000000000000..a67eea379766
--- /dev/null
+++ b/arch/xtensa/oprofile/init.c
@@ -0,0 +1,26 @@
1/**
2 * @file init.c
3 *
4 * @remark Copyright 2008 Tensilica Inc.
5 * @remark Read the file COPYING
6 *
7 */
8
9#include <linux/kernel.h>
10#include <linux/oprofile.h>
11#include <linux/errno.h>
12#include <linux/init.h>
13
14
15extern void xtensa_backtrace(struct pt_regs *const regs, unsigned int depth);
16
17int __init oprofile_arch_init(struct oprofile_operations *ops)
18{
19 ops->backtrace = xtensa_backtrace;
20 return -ENODEV;
21}
22
23
24void oprofile_arch_exit(void)
25{
26}