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