1 // SPDX-License-Identifier: GPL-2.0
2 #include <linux/oprofile.h>
3 #include <linux/sched.h>
5 #include <linux/uaccess.h>
6 #include <asm/ptrace.h>
7 #include <asm/stacktrace.h>
8 #include <linux/stacktrace.h>
9 #include <linux/kernel.h>
10 #include <asm/sections.h>
19 static inline int get_mem(unsigned long addr
, unsigned long *result
)
21 unsigned long *address
= (unsigned long *) addr
;
22 if (!access_ok(address
, sizeof(unsigned long)))
24 if (__copy_from_user_inatomic(result
, address
, sizeof(unsigned long)))
30 * These two instruction helpers were taken from process.c
32 static inline int is_ra_save_ins(union mips_instruction
*ip
)
34 /* sw / sd $ra, offset($sp) */
35 return (ip
->i_format
.opcode
== sw_op
|| ip
->i_format
.opcode
== sd_op
)
36 && ip
->i_format
.rs
== 29 && ip
->i_format
.rt
== 31;
39 static inline int is_sp_move_ins(union mips_instruction
*ip
)
41 /* addiu/daddiu sp,sp,-imm */
42 if (ip
->i_format
.rs
!= 29 || ip
->i_format
.rt
!= 29)
44 if (ip
->i_format
.opcode
== addiu_op
|| ip
->i_format
.opcode
== daddiu_op
)
50 * Looks for specific instructions that mark the end of a function.
51 * This usually means we ran into the code area of the previous function.
53 static inline int is_end_of_function_marker(union mips_instruction
*ip
)
56 if (ip
->r_format
.func
== jr_op
&& ip
->r_format
.rs
== 31)
59 if (ip
->i_format
.opcode
== lui_op
&& ip
->i_format
.rt
== 28)
65 * TODO for userspace stack unwinding:
66 * - handle cases where the stack is adjusted inside a function
67 * (generally doesn't happen)
68 * - find optimal value for max_instr_check
69 * - try to find a better way to handle leaf functions
72 static inline int unwind_user_frame(struct stackframe
*old_frame
,
73 const unsigned int max_instr_check
)
75 struct stackframe new_frame
= *old_frame
;
77 size_t stack_size
= 0;
80 if (old_frame
->pc
== 0 || old_frame
->sp
== 0 || old_frame
->ra
== 0)
83 for (addr
= new_frame
.pc
; (addr
+ max_instr_check
> new_frame
.pc
)
84 && (!ra_offset
|| !stack_size
); --addr
) {
85 union mips_instruction ip
;
87 if (get_mem(addr
, (unsigned long *) &ip
))
90 if (is_sp_move_ins(&ip
)) {
91 int stack_adjustment
= ip
.i_format
.simmediate
;
92 if (stack_adjustment
> 0)
93 /* This marks the end of the previous function,
94 which means we overran. */
96 stack_size
= (unsigned long) stack_adjustment
;
97 } else if (is_ra_save_ins(&ip
)) {
98 int ra_slot
= ip
.i_format
.simmediate
;
100 /* This shouldn't happen. */
103 } else if (is_end_of_function_marker(&ip
))
107 if (!ra_offset
|| !stack_size
)
111 new_frame
.ra
= old_frame
->sp
+ ra_offset
;
112 if (get_mem(new_frame
.ra
, &(new_frame
.ra
)))
117 new_frame
.sp
= old_frame
->sp
+ stack_size
;
118 if (get_mem(new_frame
.sp
, &(new_frame
.sp
)))
122 if (new_frame
.sp
> old_frame
->sp
)
126 new_frame
.pc
= old_frame
->ra
;
127 *old_frame
= new_frame
;
132 static inline void do_user_backtrace(unsigned long low_addr
,
133 struct stackframe
*frame
,
136 const unsigned int max_instr_check
= 512;
137 const unsigned long high_addr
= low_addr
+ THREAD_SIZE
;
139 while (depth
-- && !unwind_user_frame(frame
, max_instr_check
)) {
140 oprofile_add_trace(frame
->ra
);
141 if (frame
->sp
< low_addr
|| frame
->sp
> high_addr
)
146 #ifndef CONFIG_KALLSYMS
147 static inline void do_kernel_backtrace(unsigned long low_addr
,
148 struct stackframe
*frame
,
149 unsigned int depth
) { }
151 static inline void do_kernel_backtrace(unsigned long low_addr
,
152 struct stackframe
*frame
,
155 while (depth
-- && frame
->pc
) {
156 frame
->pc
= unwind_stack_by_address(low_addr
,
160 oprofile_add_trace(frame
->ra
);
165 void notrace
op_mips_backtrace(struct pt_regs
*const regs
, unsigned int depth
)
167 struct stackframe frame
= { .sp
= regs
->regs
[29],
169 .ra
= regs
->regs
[31] };
170 const int userspace
= user_mode(regs
);
171 const unsigned long low_addr
= ALIGN(frame
.sp
, THREAD_SIZE
);
174 do_user_backtrace(low_addr
, &frame
, depth
);
176 do_kernel_backtrace(low_addr
, &frame
, depth
);