xref: /openbmc/linux/arch/x86/kernel/unwind_frame.c (revision 7c7900f89770d7fba96100d8a9e18043a1af3973)
1*7c7900f8SJosh Poimboeuf #include <linux/sched.h>
2*7c7900f8SJosh Poimboeuf #include <asm/ptrace.h>
3*7c7900f8SJosh Poimboeuf #include <asm/bitops.h>
4*7c7900f8SJosh Poimboeuf #include <asm/stacktrace.h>
5*7c7900f8SJosh Poimboeuf #include <asm/unwind.h>
6*7c7900f8SJosh Poimboeuf 
7*7c7900f8SJosh Poimboeuf #define FRAME_HEADER_SIZE (sizeof(long) * 2)
8*7c7900f8SJosh Poimboeuf 
9*7c7900f8SJosh Poimboeuf unsigned long unwind_get_return_address(struct unwind_state *state)
10*7c7900f8SJosh Poimboeuf {
11*7c7900f8SJosh Poimboeuf 	unsigned long addr;
12*7c7900f8SJosh Poimboeuf 	unsigned long *addr_p = unwind_get_return_address_ptr(state);
13*7c7900f8SJosh Poimboeuf 
14*7c7900f8SJosh Poimboeuf 	if (unwind_done(state))
15*7c7900f8SJosh Poimboeuf 		return 0;
16*7c7900f8SJosh Poimboeuf 
17*7c7900f8SJosh Poimboeuf 	addr = ftrace_graph_ret_addr(state->task, &state->graph_idx, *addr_p,
18*7c7900f8SJosh Poimboeuf 				     addr_p);
19*7c7900f8SJosh Poimboeuf 
20*7c7900f8SJosh Poimboeuf 	return __kernel_text_address(addr) ? addr : 0;
21*7c7900f8SJosh Poimboeuf }
22*7c7900f8SJosh Poimboeuf EXPORT_SYMBOL_GPL(unwind_get_return_address);
23*7c7900f8SJosh Poimboeuf 
24*7c7900f8SJosh Poimboeuf static bool update_stack_state(struct unwind_state *state, void *addr,
25*7c7900f8SJosh Poimboeuf 			       size_t len)
26*7c7900f8SJosh Poimboeuf {
27*7c7900f8SJosh Poimboeuf 	struct stack_info *info = &state->stack_info;
28*7c7900f8SJosh Poimboeuf 
29*7c7900f8SJosh Poimboeuf 	/*
30*7c7900f8SJosh Poimboeuf 	 * If addr isn't on the current stack, switch to the next one.
31*7c7900f8SJosh Poimboeuf 	 *
32*7c7900f8SJosh Poimboeuf 	 * We may have to traverse multiple stacks to deal with the possibility
33*7c7900f8SJosh Poimboeuf 	 * that 'info->next_sp' could point to an empty stack and 'addr' could
34*7c7900f8SJosh Poimboeuf 	 * be on a subsequent stack.
35*7c7900f8SJosh Poimboeuf 	 */
36*7c7900f8SJosh Poimboeuf 	while (!on_stack(info, addr, len))
37*7c7900f8SJosh Poimboeuf 		if (get_stack_info(info->next_sp, state->task, info,
38*7c7900f8SJosh Poimboeuf 				   &state->stack_mask))
39*7c7900f8SJosh Poimboeuf 			return false;
40*7c7900f8SJosh Poimboeuf 
41*7c7900f8SJosh Poimboeuf 	return true;
42*7c7900f8SJosh Poimboeuf }
43*7c7900f8SJosh Poimboeuf 
44*7c7900f8SJosh Poimboeuf bool unwind_next_frame(struct unwind_state *state)
45*7c7900f8SJosh Poimboeuf {
46*7c7900f8SJosh Poimboeuf 	unsigned long *next_bp;
47*7c7900f8SJosh Poimboeuf 
48*7c7900f8SJosh Poimboeuf 	if (unwind_done(state))
49*7c7900f8SJosh Poimboeuf 		return false;
50*7c7900f8SJosh Poimboeuf 
51*7c7900f8SJosh Poimboeuf 	next_bp = (unsigned long *)*state->bp;
52*7c7900f8SJosh Poimboeuf 
53*7c7900f8SJosh Poimboeuf 	/* make sure the next frame's data is accessible */
54*7c7900f8SJosh Poimboeuf 	if (!update_stack_state(state, next_bp, FRAME_HEADER_SIZE))
55*7c7900f8SJosh Poimboeuf 		return false;
56*7c7900f8SJosh Poimboeuf 
57*7c7900f8SJosh Poimboeuf 	/* move to the next frame */
58*7c7900f8SJosh Poimboeuf 	state->bp = next_bp;
59*7c7900f8SJosh Poimboeuf 	return true;
60*7c7900f8SJosh Poimboeuf }
61*7c7900f8SJosh Poimboeuf EXPORT_SYMBOL_GPL(unwind_next_frame);
62*7c7900f8SJosh Poimboeuf 
63*7c7900f8SJosh Poimboeuf void __unwind_start(struct unwind_state *state, struct task_struct *task,
64*7c7900f8SJosh Poimboeuf 		    struct pt_regs *regs, unsigned long *first_frame)
65*7c7900f8SJosh Poimboeuf {
66*7c7900f8SJosh Poimboeuf 	memset(state, 0, sizeof(*state));
67*7c7900f8SJosh Poimboeuf 	state->task = task;
68*7c7900f8SJosh Poimboeuf 
69*7c7900f8SJosh Poimboeuf 	/* don't even attempt to start from user mode regs */
70*7c7900f8SJosh Poimboeuf 	if (regs && user_mode(regs)) {
71*7c7900f8SJosh Poimboeuf 		state->stack_info.type = STACK_TYPE_UNKNOWN;
72*7c7900f8SJosh Poimboeuf 		return;
73*7c7900f8SJosh Poimboeuf 	}
74*7c7900f8SJosh Poimboeuf 
75*7c7900f8SJosh Poimboeuf 	/* set up the starting stack frame */
76*7c7900f8SJosh Poimboeuf 	state->bp = get_frame_pointer(task, regs);
77*7c7900f8SJosh Poimboeuf 
78*7c7900f8SJosh Poimboeuf 	/* initialize stack info and make sure the frame data is accessible */
79*7c7900f8SJosh Poimboeuf 	get_stack_info(state->bp, state->task, &state->stack_info,
80*7c7900f8SJosh Poimboeuf 		       &state->stack_mask);
81*7c7900f8SJosh Poimboeuf 	update_stack_state(state, state->bp, FRAME_HEADER_SIZE);
82*7c7900f8SJosh Poimboeuf 
83*7c7900f8SJosh Poimboeuf 	/*
84*7c7900f8SJosh Poimboeuf 	 * The caller can provide the address of the first frame directly
85*7c7900f8SJosh Poimboeuf 	 * (first_frame) or indirectly (regs->sp) to indicate which stack frame
86*7c7900f8SJosh Poimboeuf 	 * to start unwinding at.  Skip ahead until we reach it.
87*7c7900f8SJosh Poimboeuf 	 */
88*7c7900f8SJosh Poimboeuf 	while (!unwind_done(state) &&
89*7c7900f8SJosh Poimboeuf 	       (!on_stack(&state->stack_info, first_frame, sizeof(long)) ||
90*7c7900f8SJosh Poimboeuf 			state->bp < first_frame))
91*7c7900f8SJosh Poimboeuf 		unwind_next_frame(state);
92*7c7900f8SJosh Poimboeuf }
93*7c7900f8SJosh Poimboeuf EXPORT_SYMBOL_GPL(__unwind_start);
94