• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
19 extern int common_exception_return;
20 
21 /* A struct that maps to the part of the frame containing the a0 and
22  * a1 registers.
23  */
24 struct frame_start {
25 	unsigned long a0;
26 	unsigned long a1;
27 };
28 
xtensa_backtrace_user(struct pt_regs * regs,unsigned int depth)29 static 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 
xtensa_backtrace_kernel(struct pt_regs * regs,unsigned int depth)114 static 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 		oprofile_add_trace(pc);
136 		if (pc == (unsigned long) &common_exception_return) {
137 			regs = (struct pt_regs *)a1;
138 			if (user_mode(regs)) {
139 				pc = regs->pc;
140 				if (pc != 0 && pc <= TASK_SIZE)
141 					oprofile_add_trace(pc);
142 				else
143 					return;
144 				return xtensa_backtrace_user(regs, depth);
145 			}
146 			a0 = regs->areg[0];
147 			a1 = regs->areg[1];
148 			continue;
149 		}
150 
151 		psp = (unsigned long *)a1;
152 
153 		a0 = *(psp - 4);
154 		a1 = *(psp - 3);
155 
156 		if (a1 <= (unsigned long)psp)
157 			return;
158 
159 	}
160 	return;
161 }
162 
xtensa_backtrace(struct pt_regs * const regs,unsigned int depth)163 void xtensa_backtrace(struct pt_regs * const regs, unsigned int depth)
164 {
165 	if (user_mode(regs))
166 		xtensa_backtrace_user(regs, depth);
167 	else
168 		xtensa_backtrace_kernel(regs, depth);
169 }
170