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
unwind_get_return_address(struct unwind_state * state)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
outside_of_stack(struct unwind_state * state,unsigned long sp)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
update_stack_info(struct unwind_state * state,unsigned long sp)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
is_final_pt_regs(struct unwind_state * state,struct pt_regs * regs)39eef06cbfSVasily Gorbik static inline bool is_final_pt_regs(struct unwind_state *state,
40e76e6961SVasily Gorbik struct pt_regs *regs)
41e76e6961SVasily Gorbik {
42eef06cbfSVasily Gorbik /* user mode or kernel thread pt_regs at the bottom of task stack */
43eef06cbfSVasily Gorbik if (task_pt_regs(state->task) == regs)
44eef06cbfSVasily Gorbik return true;
45eef06cbfSVasily Gorbik
46eef06cbfSVasily Gorbik /* user mode pt_regs at the bottom of irq stack */
47eef06cbfSVasily Gorbik return state->stack_info.type == STACK_TYPE_IRQ &&
48eef06cbfSVasily Gorbik state->stack_info.end - sizeof(struct pt_regs) == (unsigned long)regs &&
49eef06cbfSVasily Gorbik READ_ONCE_NOCHECK(regs->psw.mask) & PSW_MASK_PSTATE;
50e76e6961SVasily Gorbik }
51e76e6961SVasily Gorbik
unwind_next_frame(struct unwind_state * state)5278c98f90SMartin Schwidefsky bool unwind_next_frame(struct unwind_state *state)
5378c98f90SMartin Schwidefsky {
5478c98f90SMartin Schwidefsky struct stack_info *info = &state->stack_info;
5578c98f90SMartin Schwidefsky struct stack_frame *sf;
5678c98f90SMartin Schwidefsky struct pt_regs *regs;
5778c98f90SMartin Schwidefsky unsigned long sp, ip;
5878c98f90SMartin Schwidefsky bool reliable;
5978c98f90SMartin Schwidefsky
6078c98f90SMartin Schwidefsky regs = state->regs;
6178c98f90SMartin Schwidefsky if (unlikely(regs)) {
62a1d863acSIlya Leoshkevich sp = state->sp;
6378c98f90SMartin Schwidefsky sf = (struct stack_frame *) sp;
6420955746SVasily Gorbik ip = READ_ONCE_NOCHECK(sf->gprs[8]);
6578c98f90SMartin Schwidefsky reliable = false;
6678c98f90SMartin Schwidefsky regs = NULL;
67*708b1376SVasily Gorbik /* skip bogus %r14 or if is the same as regs->psw.addr */
68*708b1376SVasily Gorbik if (!__kernel_text_address(ip) || state->ip == unwind_recover_ret_addr(state, ip)) {
69bf018ee6SVasily Gorbik state->regs = NULL;
70bf018ee6SVasily Gorbik return unwind_next_frame(state);
71bf018ee6SVasily Gorbik }
7278c98f90SMartin Schwidefsky } else {
7378c98f90SMartin Schwidefsky sf = (struct stack_frame *) state->sp;
7420955746SVasily Gorbik sp = READ_ONCE_NOCHECK(sf->back_chain);
7578c98f90SMartin Schwidefsky if (likely(sp)) {
7678c98f90SMartin Schwidefsky /* Non-zero back-chain points to the previous frame */
7778c98f90SMartin Schwidefsky if (unlikely(outside_of_stack(state, sp))) {
7878c98f90SMartin Schwidefsky if (!update_stack_info(state, sp))
7978c98f90SMartin Schwidefsky goto out_err;
8078c98f90SMartin Schwidefsky }
8178c98f90SMartin Schwidefsky sf = (struct stack_frame *) sp;
8220955746SVasily Gorbik ip = READ_ONCE_NOCHECK(sf->gprs[8]);
8378c98f90SMartin Schwidefsky reliable = true;
8478c98f90SMartin Schwidefsky } else {
8578c98f90SMartin Schwidefsky /* No back-chain, look for a pt_regs structure */
8678c98f90SMartin Schwidefsky sp = state->sp + STACK_FRAME_OVERHEAD;
8778c98f90SMartin Schwidefsky if (!on_stack(info, sp, sizeof(struct pt_regs)))
8867f55934SVasily Gorbik goto out_err;
8978c98f90SMartin Schwidefsky regs = (struct pt_regs *) sp;
90eef06cbfSVasily Gorbik if (is_final_pt_regs(state, regs))
9178c98f90SMartin Schwidefsky goto out_stop;
9220955746SVasily Gorbik ip = READ_ONCE_NOCHECK(regs->psw.addr);
9397806dfbSVasily Gorbik sp = READ_ONCE_NOCHECK(regs->gprs[15]);
9497806dfbSVasily Gorbik if (unlikely(outside_of_stack(state, sp))) {
9597806dfbSVasily Gorbik if (!update_stack_info(state, sp))
9697806dfbSVasily Gorbik goto out_err;
9797806dfbSVasily Gorbik }
9878c98f90SMartin Schwidefsky reliable = true;
9978c98f90SMartin Schwidefsky }
10078c98f90SMartin Schwidefsky }
10178c98f90SMartin Schwidefsky
102be2d11b2SMiroslav Benes /* Sanity check: ABI requires SP to be aligned 8 bytes. */
103be2d11b2SMiroslav Benes if (sp & 0x7)
104be2d11b2SMiroslav Benes goto out_err;
105be2d11b2SMiroslav Benes
10678c98f90SMartin Schwidefsky /* Update unwind state */
10778c98f90SMartin Schwidefsky state->sp = sp;
10878c98f90SMartin Schwidefsky state->regs = regs;
10978c98f90SMartin Schwidefsky state->reliable = reliable;
110d81675b6SVasily Gorbik state->ip = unwind_recover_ret_addr(state, ip);
11178c98f90SMartin Schwidefsky return true;
11278c98f90SMartin Schwidefsky
11378c98f90SMartin Schwidefsky out_err:
11478c98f90SMartin Schwidefsky state->error = true;
11578c98f90SMartin Schwidefsky out_stop:
11678c98f90SMartin Schwidefsky state->stack_info.type = STACK_TYPE_UNKNOWN;
11778c98f90SMartin Schwidefsky return false;
11878c98f90SMartin Schwidefsky }
11978c98f90SMartin Schwidefsky EXPORT_SYMBOL_GPL(unwind_next_frame);
12078c98f90SMartin Schwidefsky
__unwind_start(struct unwind_state * state,struct task_struct * task,struct pt_regs * regs,unsigned long first_frame)12178c98f90SMartin Schwidefsky void __unwind_start(struct unwind_state *state, struct task_struct *task,
122222ee908SVasily Gorbik struct pt_regs *regs, unsigned long first_frame)
12378c98f90SMartin Schwidefsky {
12478c98f90SMartin Schwidefsky struct stack_info *info = &state->stack_info;
12578c98f90SMartin Schwidefsky struct stack_frame *sf;
126222ee908SVasily Gorbik unsigned long ip, sp;
12778c98f90SMartin Schwidefsky
12878c98f90SMartin Schwidefsky memset(state, 0, sizeof(*state));
12978c98f90SMartin Schwidefsky state->task = task;
13078c98f90SMartin Schwidefsky state->regs = regs;
13178c98f90SMartin Schwidefsky
13278c98f90SMartin Schwidefsky /* Don't even attempt to start from user mode regs: */
13378c98f90SMartin Schwidefsky if (regs && user_mode(regs)) {
13478c98f90SMartin Schwidefsky info->type = STACK_TYPE_UNKNOWN;
13578c98f90SMartin Schwidefsky return;
13678c98f90SMartin Schwidefsky }
13778c98f90SMartin Schwidefsky
138222ee908SVasily Gorbik /* Get the instruction pointer from pt_regs or the stack frame */
139222ee908SVasily Gorbik if (regs) {
140222ee908SVasily Gorbik ip = regs->psw.addr;
141222ee908SVasily Gorbik sp = regs->gprs[15];
142222ee908SVasily Gorbik } else if (task == current) {
143222ee908SVasily Gorbik sp = current_frame_address();
144222ee908SVasily Gorbik } else {
145222ee908SVasily Gorbik sp = task->thread.ksp;
146222ee908SVasily Gorbik }
147222ee908SVasily Gorbik
14878c98f90SMartin Schwidefsky /* Get current stack pointer and initialize stack info */
149222ee908SVasily Gorbik if (!update_stack_info(state, sp)) {
15078c98f90SMartin Schwidefsky /* Something is wrong with the stack pointer */
15178c98f90SMartin Schwidefsky info->type = STACK_TYPE_UNKNOWN;
15278c98f90SMartin Schwidefsky state->error = true;
15378c98f90SMartin Schwidefsky return;
15478c98f90SMartin Schwidefsky }
15578c98f90SMartin Schwidefsky
156222ee908SVasily Gorbik if (!regs) {
157222ee908SVasily Gorbik /* Stack frame is within valid stack */
15878c98f90SMartin Schwidefsky sf = (struct stack_frame *)sp;
15920955746SVasily Gorbik ip = READ_ONCE_NOCHECK(sf->gprs[8]);
16078c98f90SMartin Schwidefsky }
16178c98f90SMartin Schwidefsky
16278c98f90SMartin Schwidefsky /* Update unwind state */
16378c98f90SMartin Schwidefsky state->sp = sp;
164222ee908SVasily Gorbik state->reliable = true;
165d81675b6SVasily Gorbik state->ip = unwind_recover_ret_addr(state, ip);
166222ee908SVasily Gorbik
167222ee908SVasily Gorbik if (!first_frame)
168222ee908SVasily Gorbik return;
169222ee908SVasily Gorbik /* Skip through the call chain to the specified starting frame */
170222ee908SVasily Gorbik while (!unwind_done(state)) {
171222ee908SVasily Gorbik if (on_stack(&state->stack_info, first_frame, sizeof(struct stack_frame))) {
172222ee908SVasily Gorbik if (state->sp >= first_frame)
173222ee908SVasily Gorbik break;
174222ee908SVasily Gorbik }
175222ee908SVasily Gorbik unwind_next_frame(state);
176222ee908SVasily Gorbik }
17778c98f90SMartin Schwidefsky }
17878c98f90SMartin Schwidefsky EXPORT_SYMBOL_GPL(__unwind_start);
179