1*78c98f90SMartin Schwidefsky /* SPDX-License-Identifier: GPL-2.0 */ 2*78c98f90SMartin Schwidefsky #include <linux/sched.h> 3*78c98f90SMartin Schwidefsky #include <linux/sched/task.h> 4*78c98f90SMartin Schwidefsky #include <linux/sched/task_stack.h> 5*78c98f90SMartin Schwidefsky #include <linux/interrupt.h> 6*78c98f90SMartin Schwidefsky #include <asm/sections.h> 7*78c98f90SMartin Schwidefsky #include <asm/ptrace.h> 8*78c98f90SMartin Schwidefsky #include <asm/bitops.h> 9*78c98f90SMartin Schwidefsky #include <asm/stacktrace.h> 10*78c98f90SMartin Schwidefsky #include <asm/unwind.h> 11*78c98f90SMartin Schwidefsky 12*78c98f90SMartin Schwidefsky unsigned long unwind_get_return_address(struct unwind_state *state) 13*78c98f90SMartin Schwidefsky { 14*78c98f90SMartin Schwidefsky if (unwind_done(state)) 15*78c98f90SMartin Schwidefsky return 0; 16*78c98f90SMartin Schwidefsky return __kernel_text_address(state->ip) ? state->ip : 0; 17*78c98f90SMartin Schwidefsky } 18*78c98f90SMartin Schwidefsky EXPORT_SYMBOL_GPL(unwind_get_return_address); 19*78c98f90SMartin Schwidefsky 20*78c98f90SMartin Schwidefsky static bool outside_of_stack(struct unwind_state *state, unsigned long sp) 21*78c98f90SMartin Schwidefsky { 22*78c98f90SMartin Schwidefsky return (sp <= state->sp) || 23*78c98f90SMartin Schwidefsky (sp + sizeof(struct stack_frame) > state->stack_info.end); 24*78c98f90SMartin Schwidefsky } 25*78c98f90SMartin Schwidefsky 26*78c98f90SMartin Schwidefsky static bool update_stack_info(struct unwind_state *state, unsigned long sp) 27*78c98f90SMartin Schwidefsky { 28*78c98f90SMartin Schwidefsky struct stack_info *info = &state->stack_info; 29*78c98f90SMartin Schwidefsky unsigned long *mask = &state->stack_mask; 30*78c98f90SMartin Schwidefsky 31*78c98f90SMartin Schwidefsky /* New stack pointer leaves the current stack */ 32*78c98f90SMartin Schwidefsky if (get_stack_info(sp, state->task, info, mask) != 0 || 33*78c98f90SMartin Schwidefsky !on_stack(info, sp, sizeof(struct stack_frame))) 34*78c98f90SMartin Schwidefsky /* 'sp' does not point to a valid stack */ 35*78c98f90SMartin Schwidefsky return false; 36*78c98f90SMartin Schwidefsky return true; 37*78c98f90SMartin Schwidefsky } 38*78c98f90SMartin Schwidefsky 39*78c98f90SMartin Schwidefsky bool unwind_next_frame(struct unwind_state *state) 40*78c98f90SMartin Schwidefsky { 41*78c98f90SMartin Schwidefsky struct stack_info *info = &state->stack_info; 42*78c98f90SMartin Schwidefsky struct stack_frame *sf; 43*78c98f90SMartin Schwidefsky struct pt_regs *regs; 44*78c98f90SMartin Schwidefsky unsigned long sp, ip; 45*78c98f90SMartin Schwidefsky bool reliable; 46*78c98f90SMartin Schwidefsky 47*78c98f90SMartin Schwidefsky regs = state->regs; 48*78c98f90SMartin Schwidefsky if (unlikely(regs)) { 49*78c98f90SMartin Schwidefsky sp = READ_ONCE_TASK_STACK(state->task, regs->gprs[15]); 50*78c98f90SMartin Schwidefsky if (unlikely(outside_of_stack(state, sp))) { 51*78c98f90SMartin Schwidefsky if (!update_stack_info(state, sp)) 52*78c98f90SMartin Schwidefsky goto out_err; 53*78c98f90SMartin Schwidefsky } 54*78c98f90SMartin Schwidefsky sf = (struct stack_frame *) sp; 55*78c98f90SMartin Schwidefsky ip = READ_ONCE_TASK_STACK(state->task, sf->gprs[8]); 56*78c98f90SMartin Schwidefsky reliable = false; 57*78c98f90SMartin Schwidefsky regs = NULL; 58*78c98f90SMartin Schwidefsky } else { 59*78c98f90SMartin Schwidefsky sf = (struct stack_frame *) state->sp; 60*78c98f90SMartin Schwidefsky sp = READ_ONCE_TASK_STACK(state->task, sf->back_chain); 61*78c98f90SMartin Schwidefsky if (likely(sp)) { 62*78c98f90SMartin Schwidefsky /* Non-zero back-chain points to the previous frame */ 63*78c98f90SMartin Schwidefsky if (unlikely(outside_of_stack(state, sp))) { 64*78c98f90SMartin Schwidefsky if (!update_stack_info(state, sp)) 65*78c98f90SMartin Schwidefsky goto out_err; 66*78c98f90SMartin Schwidefsky } 67*78c98f90SMartin Schwidefsky sf = (struct stack_frame *) sp; 68*78c98f90SMartin Schwidefsky ip = READ_ONCE_TASK_STACK(state->task, sf->gprs[8]); 69*78c98f90SMartin Schwidefsky reliable = true; 70*78c98f90SMartin Schwidefsky } else { 71*78c98f90SMartin Schwidefsky /* No back-chain, look for a pt_regs structure */ 72*78c98f90SMartin Schwidefsky sp = state->sp + STACK_FRAME_OVERHEAD; 73*78c98f90SMartin Schwidefsky if (!on_stack(info, sp, sizeof(struct pt_regs))) 74*78c98f90SMartin Schwidefsky goto out_stop; 75*78c98f90SMartin Schwidefsky regs = (struct pt_regs *) sp; 76*78c98f90SMartin Schwidefsky if (user_mode(regs)) 77*78c98f90SMartin Schwidefsky goto out_stop; 78*78c98f90SMartin Schwidefsky ip = READ_ONCE_TASK_STACK(state->task, regs->psw.addr); 79*78c98f90SMartin Schwidefsky reliable = true; 80*78c98f90SMartin Schwidefsky } 81*78c98f90SMartin Schwidefsky } 82*78c98f90SMartin Schwidefsky 83*78c98f90SMartin Schwidefsky #ifdef CONFIG_FUNCTION_GRAPH_TRACER 84*78c98f90SMartin Schwidefsky /* Decode any ftrace redirection */ 85*78c98f90SMartin Schwidefsky if (ip == (unsigned long) return_to_handler) 86*78c98f90SMartin Schwidefsky ip = ftrace_graph_ret_addr(state->task, &state->graph_idx, 87*78c98f90SMartin Schwidefsky ip, NULL); 88*78c98f90SMartin Schwidefsky #endif 89*78c98f90SMartin Schwidefsky 90*78c98f90SMartin Schwidefsky /* Update unwind state */ 91*78c98f90SMartin Schwidefsky state->sp = sp; 92*78c98f90SMartin Schwidefsky state->ip = ip; 93*78c98f90SMartin Schwidefsky state->regs = regs; 94*78c98f90SMartin Schwidefsky state->reliable = reliable; 95*78c98f90SMartin Schwidefsky return true; 96*78c98f90SMartin Schwidefsky 97*78c98f90SMartin Schwidefsky out_err: 98*78c98f90SMartin Schwidefsky state->error = true; 99*78c98f90SMartin Schwidefsky out_stop: 100*78c98f90SMartin Schwidefsky state->stack_info.type = STACK_TYPE_UNKNOWN; 101*78c98f90SMartin Schwidefsky return false; 102*78c98f90SMartin Schwidefsky } 103*78c98f90SMartin Schwidefsky EXPORT_SYMBOL_GPL(unwind_next_frame); 104*78c98f90SMartin Schwidefsky 105*78c98f90SMartin Schwidefsky void __unwind_start(struct unwind_state *state, struct task_struct *task, 106*78c98f90SMartin Schwidefsky struct pt_regs *regs, unsigned long sp) 107*78c98f90SMartin Schwidefsky { 108*78c98f90SMartin Schwidefsky struct stack_info *info = &state->stack_info; 109*78c98f90SMartin Schwidefsky unsigned long *mask = &state->stack_mask; 110*78c98f90SMartin Schwidefsky struct stack_frame *sf; 111*78c98f90SMartin Schwidefsky unsigned long ip; 112*78c98f90SMartin Schwidefsky bool reliable; 113*78c98f90SMartin Schwidefsky 114*78c98f90SMartin Schwidefsky memset(state, 0, sizeof(*state)); 115*78c98f90SMartin Schwidefsky state->task = task; 116*78c98f90SMartin Schwidefsky state->regs = regs; 117*78c98f90SMartin Schwidefsky 118*78c98f90SMartin Schwidefsky /* Don't even attempt to start from user mode regs: */ 119*78c98f90SMartin Schwidefsky if (regs && user_mode(regs)) { 120*78c98f90SMartin Schwidefsky info->type = STACK_TYPE_UNKNOWN; 121*78c98f90SMartin Schwidefsky return; 122*78c98f90SMartin Schwidefsky } 123*78c98f90SMartin Schwidefsky 124*78c98f90SMartin Schwidefsky /* Get current stack pointer and initialize stack info */ 125*78c98f90SMartin Schwidefsky if (get_stack_info(sp, task, info, mask) != 0 || 126*78c98f90SMartin Schwidefsky !on_stack(info, sp, sizeof(struct stack_frame))) { 127*78c98f90SMartin Schwidefsky /* Something is wrong with the stack pointer */ 128*78c98f90SMartin Schwidefsky info->type = STACK_TYPE_UNKNOWN; 129*78c98f90SMartin Schwidefsky state->error = true; 130*78c98f90SMartin Schwidefsky return; 131*78c98f90SMartin Schwidefsky } 132*78c98f90SMartin Schwidefsky 133*78c98f90SMartin Schwidefsky /* Get the instruction pointer from pt_regs or the stack frame */ 134*78c98f90SMartin Schwidefsky if (regs) { 135*78c98f90SMartin Schwidefsky ip = READ_ONCE_TASK_STACK(state->task, regs->psw.addr); 136*78c98f90SMartin Schwidefsky reliable = true; 137*78c98f90SMartin Schwidefsky } else { 138*78c98f90SMartin Schwidefsky sf = (struct stack_frame *) sp; 139*78c98f90SMartin Schwidefsky ip = READ_ONCE_TASK_STACK(state->task, sf->gprs[8]); 140*78c98f90SMartin Schwidefsky reliable = false; 141*78c98f90SMartin Schwidefsky } 142*78c98f90SMartin Schwidefsky 143*78c98f90SMartin Schwidefsky #ifdef CONFIG_FUNCTION_GRAPH_TRACER 144*78c98f90SMartin Schwidefsky /* Decode any ftrace redirection */ 145*78c98f90SMartin Schwidefsky if (ip == (unsigned long) return_to_handler) 146*78c98f90SMartin Schwidefsky ip = ftrace_graph_ret_addr(state->task, &state->graph_idx, 147*78c98f90SMartin Schwidefsky ip, NULL); 148*78c98f90SMartin Schwidefsky #endif 149*78c98f90SMartin Schwidefsky 150*78c98f90SMartin Schwidefsky /* Update unwind state */ 151*78c98f90SMartin Schwidefsky state->sp = sp; 152*78c98f90SMartin Schwidefsky state->ip = ip; 153*78c98f90SMartin Schwidefsky state->reliable = reliable; 154*78c98f90SMartin Schwidefsky } 155*78c98f90SMartin Schwidefsky EXPORT_SYMBOL_GPL(__unwind_start); 156