xref: /linux/arch/s390/kernel/unwind_bc.c (revision a1ff5a7d78a036d6c2178ee5acd6ba4946243800)
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 
52*7e17eac2SIlya Leoshkevich /* Avoid KMSAN false positives from touching uninitialized frames. */
53*7e17eac2SIlya Leoshkevich __no_kmsan_checks
unwind_next_frame(struct unwind_state * state)5478c98f90SMartin Schwidefsky bool unwind_next_frame(struct unwind_state *state)
5578c98f90SMartin Schwidefsky {
5678c98f90SMartin Schwidefsky 	struct stack_info *info = &state->stack_info;
5778c98f90SMartin Schwidefsky 	struct stack_frame *sf;
5878c98f90SMartin Schwidefsky 	struct pt_regs *regs;
5978c98f90SMartin Schwidefsky 	unsigned long sp, ip;
6078c98f90SMartin Schwidefsky 	bool reliable;
6178c98f90SMartin Schwidefsky 
6278c98f90SMartin Schwidefsky 	regs = state->regs;
6378c98f90SMartin Schwidefsky 	if (unlikely(regs)) {
64a1d863acSIlya Leoshkevich 		sp = state->sp;
6578c98f90SMartin Schwidefsky 		sf = (struct stack_frame *) sp;
6620955746SVasily Gorbik 		ip = READ_ONCE_NOCHECK(sf->gprs[8]);
6778c98f90SMartin Schwidefsky 		reliable = false;
6878c98f90SMartin Schwidefsky 		regs = NULL;
69708b1376SVasily Gorbik 		/* skip bogus %r14 or if is the same as regs->psw.addr */
70708b1376SVasily Gorbik 		if (!__kernel_text_address(ip) || state->ip == unwind_recover_ret_addr(state, ip)) {
71bf018ee6SVasily Gorbik 			state->regs = NULL;
72bf018ee6SVasily Gorbik 			return unwind_next_frame(state);
73bf018ee6SVasily Gorbik 		}
7478c98f90SMartin Schwidefsky 	} else {
7578c98f90SMartin Schwidefsky 		sf = (struct stack_frame *) state->sp;
7620955746SVasily Gorbik 		sp = READ_ONCE_NOCHECK(sf->back_chain);
7778c98f90SMartin Schwidefsky 		if (likely(sp)) {
7878c98f90SMartin Schwidefsky 			/* Non-zero back-chain points to the previous frame */
7978c98f90SMartin Schwidefsky 			if (unlikely(outside_of_stack(state, sp))) {
8078c98f90SMartin Schwidefsky 				if (!update_stack_info(state, sp))
8178c98f90SMartin Schwidefsky 					goto out_err;
8278c98f90SMartin Schwidefsky 			}
8378c98f90SMartin Schwidefsky 			sf = (struct stack_frame *) sp;
8420955746SVasily Gorbik 			ip = READ_ONCE_NOCHECK(sf->gprs[8]);
8578c98f90SMartin Schwidefsky 			reliable = true;
8678c98f90SMartin Schwidefsky 		} else {
8778c98f90SMartin Schwidefsky 			/* No back-chain, look for a pt_regs structure */
8878c98f90SMartin Schwidefsky 			sp = state->sp + STACK_FRAME_OVERHEAD;
8978c98f90SMartin Schwidefsky 			if (!on_stack(info, sp, sizeof(struct pt_regs)))
9067f55934SVasily Gorbik 				goto out_err;
9178c98f90SMartin Schwidefsky 			regs = (struct pt_regs *) sp;
92eef06cbfSVasily Gorbik 			if (is_final_pt_regs(state, regs))
9378c98f90SMartin Schwidefsky 				goto out_stop;
9420955746SVasily Gorbik 			ip = READ_ONCE_NOCHECK(regs->psw.addr);
9597806dfbSVasily Gorbik 			sp = READ_ONCE_NOCHECK(regs->gprs[15]);
9697806dfbSVasily Gorbik 			if (unlikely(outside_of_stack(state, sp))) {
9797806dfbSVasily Gorbik 				if (!update_stack_info(state, sp))
9897806dfbSVasily Gorbik 					goto out_err;
9997806dfbSVasily Gorbik 			}
10078c98f90SMartin Schwidefsky 			reliable = true;
10178c98f90SMartin Schwidefsky 		}
10278c98f90SMartin Schwidefsky 	}
10378c98f90SMartin Schwidefsky 
104be2d11b2SMiroslav Benes 	/* Sanity check: ABI requires SP to be aligned 8 bytes. */
105be2d11b2SMiroslav Benes 	if (sp & 0x7)
106be2d11b2SMiroslav Benes 		goto out_err;
107be2d11b2SMiroslav Benes 
10878c98f90SMartin Schwidefsky 	/* Update unwind state */
10978c98f90SMartin Schwidefsky 	state->sp = sp;
11078c98f90SMartin Schwidefsky 	state->regs = regs;
11178c98f90SMartin Schwidefsky 	state->reliable = reliable;
112d81675b6SVasily Gorbik 	state->ip = unwind_recover_ret_addr(state, ip);
11378c98f90SMartin Schwidefsky 	return true;
11478c98f90SMartin Schwidefsky 
11578c98f90SMartin Schwidefsky out_err:
11678c98f90SMartin Schwidefsky 	state->error = true;
11778c98f90SMartin Schwidefsky out_stop:
11878c98f90SMartin Schwidefsky 	state->stack_info.type = STACK_TYPE_UNKNOWN;
11978c98f90SMartin Schwidefsky 	return false;
12078c98f90SMartin Schwidefsky }
12178c98f90SMartin Schwidefsky EXPORT_SYMBOL_GPL(unwind_next_frame);
12278c98f90SMartin Schwidefsky 
123*7e17eac2SIlya Leoshkevich /* Avoid KMSAN false positives from touching uninitialized frames. */
124*7e17eac2SIlya Leoshkevich __no_kmsan_checks
__unwind_start(struct unwind_state * state,struct task_struct * task,struct pt_regs * regs,unsigned long first_frame)12578c98f90SMartin Schwidefsky void __unwind_start(struct unwind_state *state, struct task_struct *task,
126222ee908SVasily Gorbik 		    struct pt_regs *regs, unsigned long first_frame)
12778c98f90SMartin Schwidefsky {
12878c98f90SMartin Schwidefsky 	struct stack_info *info = &state->stack_info;
12978c98f90SMartin Schwidefsky 	struct stack_frame *sf;
130222ee908SVasily Gorbik 	unsigned long ip, sp;
13178c98f90SMartin Schwidefsky 
13278c98f90SMartin Schwidefsky 	memset(state, 0, sizeof(*state));
13378c98f90SMartin Schwidefsky 	state->task = task;
13478c98f90SMartin Schwidefsky 	state->regs = regs;
13578c98f90SMartin Schwidefsky 
13678c98f90SMartin Schwidefsky 	/* Don't even attempt to start from user mode regs: */
13778c98f90SMartin Schwidefsky 	if (regs && user_mode(regs)) {
13878c98f90SMartin Schwidefsky 		info->type = STACK_TYPE_UNKNOWN;
13978c98f90SMartin Schwidefsky 		return;
14078c98f90SMartin Schwidefsky 	}
14178c98f90SMartin Schwidefsky 
142222ee908SVasily Gorbik 	/* Get the instruction pointer from pt_regs or the stack frame */
143222ee908SVasily Gorbik 	if (regs) {
144222ee908SVasily Gorbik 		ip = regs->psw.addr;
145222ee908SVasily Gorbik 		sp = regs->gprs[15];
146222ee908SVasily Gorbik 	} else if (task == current) {
147222ee908SVasily Gorbik 		sp = current_frame_address();
148222ee908SVasily Gorbik 	} else {
149222ee908SVasily Gorbik 		sp = task->thread.ksp;
150222ee908SVasily Gorbik 	}
151222ee908SVasily Gorbik 
15278c98f90SMartin Schwidefsky 	/* Get current stack pointer and initialize stack info */
153222ee908SVasily Gorbik 	if (!update_stack_info(state, sp)) {
15478c98f90SMartin Schwidefsky 		/* Something is wrong with the stack pointer */
15578c98f90SMartin Schwidefsky 		info->type = STACK_TYPE_UNKNOWN;
15678c98f90SMartin Schwidefsky 		state->error = true;
15778c98f90SMartin Schwidefsky 		return;
15878c98f90SMartin Schwidefsky 	}
15978c98f90SMartin Schwidefsky 
160222ee908SVasily Gorbik 	if (!regs) {
161222ee908SVasily Gorbik 		/* Stack frame is within valid stack */
16278c98f90SMartin Schwidefsky 		sf = (struct stack_frame *)sp;
16320955746SVasily Gorbik 		ip = READ_ONCE_NOCHECK(sf->gprs[8]);
16478c98f90SMartin Schwidefsky 	}
16578c98f90SMartin Schwidefsky 
16678c98f90SMartin Schwidefsky 	/* Update unwind state */
16778c98f90SMartin Schwidefsky 	state->sp = sp;
168222ee908SVasily Gorbik 	state->reliable = true;
169d81675b6SVasily Gorbik 	state->ip = unwind_recover_ret_addr(state, ip);
170222ee908SVasily Gorbik 
171222ee908SVasily Gorbik 	if (!first_frame)
172222ee908SVasily Gorbik 		return;
173222ee908SVasily Gorbik 	/* Skip through the call chain to the specified starting frame */
174222ee908SVasily Gorbik 	while (!unwind_done(state)) {
175222ee908SVasily Gorbik 		if (on_stack(&state->stack_info, first_frame, sizeof(struct stack_frame))) {
176222ee908SVasily Gorbik 			if (state->sp >= first_frame)
177222ee908SVasily Gorbik 				break;
178222ee908SVasily Gorbik 		}
179222ee908SVasily Gorbik 		unwind_next_frame(state);
180222ee908SVasily Gorbik 	}
18178c98f90SMartin Schwidefsky }
18278c98f90SMartin Schwidefsky EXPORT_SYMBOL_GPL(__unwind_start);
183