178c98f90SMartin Schwidefsky /* SPDX-License-Identifier: GPL-2.0 */ 278c98f90SMartin Schwidefsky #include <linux/sched.h> 378c98f90SMartin Schwidefsky #include <linux/sched/task.h> 478c98f90SMartin Schwidefsky #include <linux/sched/task_stack.h> 578c98f90SMartin Schwidefsky #include <linux/interrupt.h> 678c98f90SMartin Schwidefsky #include <asm/sections.h> 778c98f90SMartin Schwidefsky #include <asm/ptrace.h> 878c98f90SMartin Schwidefsky #include <asm/bitops.h> 978c98f90SMartin Schwidefsky #include <asm/stacktrace.h> 1078c98f90SMartin Schwidefsky #include <asm/unwind.h> 1178c98f90SMartin Schwidefsky 1278c98f90SMartin Schwidefsky unsigned long unwind_get_return_address(struct unwind_state *state) 1378c98f90SMartin Schwidefsky { 1478c98f90SMartin Schwidefsky if (unwind_done(state)) 1578c98f90SMartin Schwidefsky return 0; 1678c98f90SMartin Schwidefsky return __kernel_text_address(state->ip) ? state->ip : 0; 1778c98f90SMartin Schwidefsky } 1878c98f90SMartin Schwidefsky EXPORT_SYMBOL_GPL(unwind_get_return_address); 1978c98f90SMartin Schwidefsky 2078c98f90SMartin Schwidefsky static bool outside_of_stack(struct unwind_state *state, unsigned long sp) 2178c98f90SMartin Schwidefsky { 2278c98f90SMartin Schwidefsky return (sp <= state->sp) || 239a159190SVasily Gorbik (sp > state->stack_info.end - sizeof(struct stack_frame)); 2478c98f90SMartin Schwidefsky } 2578c98f90SMartin Schwidefsky 2678c98f90SMartin Schwidefsky static bool update_stack_info(struct unwind_state *state, unsigned long sp) 2778c98f90SMartin Schwidefsky { 2878c98f90SMartin Schwidefsky struct stack_info *info = &state->stack_info; 2978c98f90SMartin Schwidefsky unsigned long *mask = &state->stack_mask; 3078c98f90SMartin Schwidefsky 3178c98f90SMartin Schwidefsky /* New stack pointer leaves the current stack */ 3278c98f90SMartin Schwidefsky if (get_stack_info(sp, state->task, info, mask) != 0 || 3378c98f90SMartin Schwidefsky !on_stack(info, sp, sizeof(struct stack_frame))) 3478c98f90SMartin Schwidefsky /* 'sp' does not point to a valid stack */ 3578c98f90SMartin Schwidefsky return false; 3678c98f90SMartin Schwidefsky return true; 3778c98f90SMartin Schwidefsky } 3878c98f90SMartin Schwidefsky 3978c98f90SMartin Schwidefsky bool unwind_next_frame(struct unwind_state *state) 4078c98f90SMartin Schwidefsky { 4178c98f90SMartin Schwidefsky struct stack_info *info = &state->stack_info; 4278c98f90SMartin Schwidefsky struct stack_frame *sf; 4378c98f90SMartin Schwidefsky struct pt_regs *regs; 4478c98f90SMartin Schwidefsky unsigned long sp, ip; 4578c98f90SMartin Schwidefsky bool reliable; 4678c98f90SMartin Schwidefsky 4778c98f90SMartin Schwidefsky regs = state->regs; 4878c98f90SMartin Schwidefsky if (unlikely(regs)) { 49a1d863acSIlya Leoshkevich if (state->reuse_sp) { 50a1d863acSIlya Leoshkevich sp = state->sp; 51a1d863acSIlya Leoshkevich state->reuse_sp = false; 52a1d863acSIlya Leoshkevich } else { 5320955746SVasily Gorbik sp = READ_ONCE_NOCHECK(regs->gprs[15]); 5478c98f90SMartin Schwidefsky if (unlikely(outside_of_stack(state, sp))) { 5578c98f90SMartin Schwidefsky if (!update_stack_info(state, sp)) 5678c98f90SMartin Schwidefsky goto out_err; 5778c98f90SMartin Schwidefsky } 58a1d863acSIlya Leoshkevich } 5978c98f90SMartin Schwidefsky sf = (struct stack_frame *) sp; 6020955746SVasily Gorbik ip = READ_ONCE_NOCHECK(sf->gprs[8]); 6178c98f90SMartin Schwidefsky reliable = false; 6278c98f90SMartin Schwidefsky regs = NULL; 6378c98f90SMartin Schwidefsky } else { 6478c98f90SMartin Schwidefsky sf = (struct stack_frame *) state->sp; 6520955746SVasily Gorbik sp = READ_ONCE_NOCHECK(sf->back_chain); 6678c98f90SMartin Schwidefsky if (likely(sp)) { 6778c98f90SMartin Schwidefsky /* Non-zero back-chain points to the previous frame */ 6878c98f90SMartin Schwidefsky if (unlikely(outside_of_stack(state, sp))) { 6978c98f90SMartin Schwidefsky if (!update_stack_info(state, sp)) 7078c98f90SMartin Schwidefsky goto out_err; 7178c98f90SMartin Schwidefsky } 7278c98f90SMartin Schwidefsky sf = (struct stack_frame *) sp; 7320955746SVasily Gorbik ip = READ_ONCE_NOCHECK(sf->gprs[8]); 7478c98f90SMartin Schwidefsky reliable = true; 7578c98f90SMartin Schwidefsky } else { 7678c98f90SMartin Schwidefsky /* No back-chain, look for a pt_regs structure */ 7778c98f90SMartin Schwidefsky sp = state->sp + STACK_FRAME_OVERHEAD; 7878c98f90SMartin Schwidefsky if (!on_stack(info, sp, sizeof(struct pt_regs))) 79*67f55934SVasily Gorbik goto out_err; 8078c98f90SMartin Schwidefsky regs = (struct pt_regs *) sp; 8120955746SVasily Gorbik if (READ_ONCE_NOCHECK(regs->psw.mask) & PSW_MASK_PSTATE) 8278c98f90SMartin Schwidefsky goto out_stop; 8320955746SVasily Gorbik ip = READ_ONCE_NOCHECK(regs->psw.addr); 8478c98f90SMartin Schwidefsky reliable = true; 8578c98f90SMartin Schwidefsky } 8678c98f90SMartin Schwidefsky } 8778c98f90SMartin Schwidefsky 88c2f2093eSMiroslav Benes ip = ftrace_graph_ret_addr(state->task, &state->graph_idx, ip, (void *) sp); 8978c98f90SMartin Schwidefsky 9078c98f90SMartin Schwidefsky /* Update unwind state */ 9178c98f90SMartin Schwidefsky state->sp = sp; 9278c98f90SMartin Schwidefsky state->ip = ip; 9378c98f90SMartin Schwidefsky state->regs = regs; 9478c98f90SMartin Schwidefsky state->reliable = reliable; 9578c98f90SMartin Schwidefsky return true; 9678c98f90SMartin Schwidefsky 9778c98f90SMartin Schwidefsky out_err: 9878c98f90SMartin Schwidefsky state->error = true; 9978c98f90SMartin Schwidefsky out_stop: 10078c98f90SMartin Schwidefsky state->stack_info.type = STACK_TYPE_UNKNOWN; 10178c98f90SMartin Schwidefsky return false; 10278c98f90SMartin Schwidefsky } 10378c98f90SMartin Schwidefsky EXPORT_SYMBOL_GPL(unwind_next_frame); 10478c98f90SMartin Schwidefsky 10578c98f90SMartin Schwidefsky void __unwind_start(struct unwind_state *state, struct task_struct *task, 10678c98f90SMartin Schwidefsky struct pt_regs *regs, unsigned long sp) 10778c98f90SMartin Schwidefsky { 10878c98f90SMartin Schwidefsky struct stack_info *info = &state->stack_info; 10978c98f90SMartin Schwidefsky unsigned long *mask = &state->stack_mask; 110a1d863acSIlya Leoshkevich bool reliable, reuse_sp; 11178c98f90SMartin Schwidefsky struct stack_frame *sf; 11278c98f90SMartin Schwidefsky unsigned long ip; 11378c98f90SMartin Schwidefsky 11478c98f90SMartin Schwidefsky memset(state, 0, sizeof(*state)); 11578c98f90SMartin Schwidefsky state->task = task; 11678c98f90SMartin Schwidefsky state->regs = regs; 11778c98f90SMartin Schwidefsky 11878c98f90SMartin Schwidefsky /* Don't even attempt to start from user mode regs: */ 11978c98f90SMartin Schwidefsky if (regs && user_mode(regs)) { 12078c98f90SMartin Schwidefsky info->type = STACK_TYPE_UNKNOWN; 12178c98f90SMartin Schwidefsky return; 12278c98f90SMartin Schwidefsky } 12378c98f90SMartin Schwidefsky 12478c98f90SMartin Schwidefsky /* Get current stack pointer and initialize stack info */ 12578c98f90SMartin Schwidefsky if (get_stack_info(sp, task, info, mask) != 0 || 12678c98f90SMartin Schwidefsky !on_stack(info, sp, sizeof(struct stack_frame))) { 12778c98f90SMartin Schwidefsky /* Something is wrong with the stack pointer */ 12878c98f90SMartin Schwidefsky info->type = STACK_TYPE_UNKNOWN; 12978c98f90SMartin Schwidefsky state->error = true; 13078c98f90SMartin Schwidefsky return; 13178c98f90SMartin Schwidefsky } 13278c98f90SMartin Schwidefsky 13378c98f90SMartin Schwidefsky /* Get the instruction pointer from pt_regs or the stack frame */ 13478c98f90SMartin Schwidefsky if (regs) { 13520955746SVasily Gorbik ip = READ_ONCE_NOCHECK(regs->psw.addr); 13678c98f90SMartin Schwidefsky reliable = true; 137a1d863acSIlya Leoshkevich reuse_sp = true; 13878c98f90SMartin Schwidefsky } else { 13978c98f90SMartin Schwidefsky sf = (struct stack_frame *) sp; 14020955746SVasily Gorbik ip = READ_ONCE_NOCHECK(sf->gprs[8]); 14178c98f90SMartin Schwidefsky reliable = false; 142a1d863acSIlya Leoshkevich reuse_sp = false; 14378c98f90SMartin Schwidefsky } 14478c98f90SMartin Schwidefsky 145c2f2093eSMiroslav Benes ip = ftrace_graph_ret_addr(state->task, &state->graph_idx, ip, NULL); 14678c98f90SMartin Schwidefsky 14778c98f90SMartin Schwidefsky /* Update unwind state */ 14878c98f90SMartin Schwidefsky state->sp = sp; 14978c98f90SMartin Schwidefsky state->ip = ip; 15078c98f90SMartin Schwidefsky state->reliable = reliable; 151a1d863acSIlya Leoshkevich state->reuse_sp = reuse_sp; 15278c98f90SMartin Schwidefsky } 15378c98f90SMartin Schwidefsky EXPORT_SYMBOL_GPL(__unwind_start); 154