xref: /linux/arch/s390/kernel/unwind_bc.c (revision 67f5593419878798bb306632cdca0698a2dd3cbd)
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