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