KVM: emulator: emulate SALC
[firefly-linux-kernel-4.4.55.git] / arch / xtensa / oprofile / backtrace.c
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
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
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                 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
165 void 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 }