summaryrefslogtreecommitdiffstats
path: root/arch/s390/include/asm/stacktrace.h
blob: 90488b0c26f6043149b4531d6f4677d4bae10541 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _ASM_S390_STACKTRACE_H
#define _ASM_S390_STACKTRACE_H

#include <linux/uaccess.h>
#include <linux/ptrace.h>
#include <asm/switch_to.h>

enum stack_type {
	STACK_TYPE_UNKNOWN,
	STACK_TYPE_TASK,
	STACK_TYPE_IRQ,
	STACK_TYPE_NODAT,
	STACK_TYPE_RESTART,
};

struct stack_info {
	enum stack_type type;
	unsigned long begin, end;
};

const char *stack_type_name(enum stack_type type);
int get_stack_info(unsigned long sp, struct task_struct *task,
		   struct stack_info *info, unsigned long *visit_mask);

static inline bool on_stack(struct stack_info *info,
			    unsigned long addr, size_t len)
{
	if (info->type == STACK_TYPE_UNKNOWN)
		return false;
	if (addr + len < addr)
		return false;
	return addr >= info->begin && addr + len <= info->end;
}

static __always_inline unsigned long get_stack_pointer(struct task_struct *task,
						       struct pt_regs *regs)
{
	if (regs)
		return (unsigned long) kernel_stack_pointer(regs);
	if (task == current)
		return current_stack_pointer();
	return (unsigned long) task->thread.ksp;
}

/*
 * Stack layout of a C stack frame.
 */
#ifndef __PACK_STACK
struct stack_frame {
	unsigned long back_chain;
	unsigned long empty1[5];
	unsigned long gprs[10];
	unsigned int  empty2[8];
};
#else
struct stack_frame {
	unsigned long empty1[5];
	unsigned int  empty2[8];
	unsigned long gprs[10];
	unsigned long back_chain;
};
#endif

/*
 * Unlike current_stack_pointer() which simply returns current value of %r15
 * current_frame_address() returns function stack frame address, which matches
 * %r15 upon function invocation. It may differ from %r15 later if function
 * allocates stack for local variables or new stack frame to call other
 * functions.
 */
#define current_frame_address()						\
	((unsigned long)__builtin_frame_address(0) -			\
	 offsetof(struct stack_frame, back_chain))

#define CALL_ARGS_0()							\
	register unsigned long r2 asm("2")
#define CALL_ARGS_1(arg1)						\
	register unsigned long r2 asm("2") = (unsigned long)(arg1)
#define CALL_ARGS_2(arg1, arg2)						\
	CALL_ARGS_1(arg1);						\
	register unsigned long r3 asm("3") = (unsigned long)(arg2)
#define CALL_ARGS_3(arg1, arg2, arg3)					\
	CALL_ARGS_2(arg1, arg2);					\
	register unsigned long r4 asm("4") = (unsigned long)(arg3)
#define CALL_ARGS_4(arg1, arg2, arg3, arg4)				\
	CALL_ARGS_3(arg1, arg2, arg3);					\
	register unsigned long r4 asm("5") = (unsigned long)(arg4)
#define CALL_ARGS_5(arg1, arg2, arg3, arg4, arg5)			\
	CALL_ARGS_4(arg1, arg2, arg3, arg4);				\
	register unsigned long r4 asm("6") = (unsigned long)(arg5)

/*
 * To keep this simple mark register 2-6 as being changed (volatile)
 * by the called function, even though register 6 is saved/nonvolatile.
 */
#define CALL_FMT_0 "=&d" (r2)
#define CALL_FMT_1 "+&d" (r2)
#define CALL_FMT_2 CALL_FMT_1, "+&d" (r3)
#define CALL_FMT_3 CALL_FMT_2, "+&d" (r4)
#define CALL_FMT_4 CALL_FMT_3, "+&d" (r5)
#define CALL_FMT_5 CALL_FMT_4, "+&d" (r6)

#define CALL_CLOBBER_5 "0", "1", "14", "cc", "memory"
#define CALL_CLOBBER_4 CALL_CLOBBER_5
#define CALL_CLOBBER_3 CALL_CLOBBER_4, "5"
#define CALL_CLOBBER_2 CALL_CLOBBER_3, "4"
#define CALL_CLOBBER_1 CALL_CLOBBER_2, "3"
#define CALL_CLOBBER_0 CALL_CLOBBER_1

#define CALL_ON_STACK(fn, stack, nr, args...)				\
({									\
	unsigned long frame = current_frame_address();			\
	CALL_ARGS_##nr(args);						\
	unsigned long prev;						\
									\
	asm volatile(							\
		"	la	%[_prev],0(15)\n"			\
		"	lg	15,%[_stack]\n"				\
		"	stg	%[_frame],%[_bc](15)\n"			\
		"	brasl	14,%[_fn]\n"				\
		"	la	15,0(%[_prev])\n"			\
		: [_prev] "=&a" (prev), CALL_FMT_##nr			\
		: [_stack] "R" (stack),					\
		  [_bc] "i" (offsetof(struct stack_frame, back_chain)),	\
		  [_frame] "d" (frame),					\
		  [_fn] "X" (fn) : CALL_CLOBBER_##nr);			\
	r2;								\
})

#define CALL_LARGS_0(...)						\
	long dummy = 0
#define CALL_LARGS_1(t1, a1)						\
	long arg1  = (long)(t1)(a1)
#define CALL_LARGS_2(t1, a1, t2, a2)					\
	CALL_LARGS_1(t1, a1);						\
	long arg2 = (long)(t2)(a2)
#define CALL_LARGS_3(t1, a1, t2, a2, t3, a3)				\
	CALL_LARGS_2(t1, a1, t2, a2);					\
	long arg3 = (long)(t3)(a3)
#define CALL_LARGS_4(t1, a1, t2, a2, t3, a3, t4, a4)			\
	CALL_LARGS_3(t1, a1, t2, a2, t3, a3);				\
	long arg4  = (long)(t4)(a4)
#define CALL_LARGS_5(t1, a1, t2, a2, t3, a3, t4, a4, t5, a5)		\
	CALL_LARGS_4(t1, a1, t2, a2, t3, a3, t4, a4);			\
	long arg5 = (long)(t5)(a5)

#define CALL_REGS_0							\
	register long r2 asm("2") = dummy
#define CALL_REGS_1							\
	register long r2 asm("2") = arg1
#define CALL_REGS_2							\
	CALL_REGS_1;							\
	register long r3 asm("3") = arg2
#define CALL_REGS_3							\
	CALL_REGS_2;							\
	register long r4 asm("4") = arg3
#define CALL_REGS_4							\
	CALL_REGS_3;							\
	register long r5 asm("5") = arg4
#define CALL_REGS_5							\
	CALL_REGS_4;							\
	register long r6 asm("6") = arg5

#define CALL_TYPECHECK_0(...)
#define CALL_TYPECHECK_1(t, a, ...)					\
	typecheck(t, a)
#define CALL_TYPECHECK_2(t, a, ...)					\
	CALL_TYPECHECK_1(__VA_ARGS__);					\
	typecheck(t, a)
#define CALL_TYPECHECK_3(t, a, ...)					\
	CALL_TYPECHECK_2(__VA_ARGS__);					\
	typecheck(t, a)
#define CALL_TYPECHECK_4(t, a, ...)					\
	CALL_TYPECHECK_3(__VA_ARGS__);					\
	typecheck(t, a)
#define CALL_TYPECHECK_5(t, a, ...)					\
	CALL_TYPECHECK_4(__VA_ARGS__);					\
	typecheck(t, a)

#define CALL_PARM_0(...) void
#define CALL_PARM_1(t, a, ...) t
#define CALL_PARM_2(t, a, ...) t, CALL_PARM_1(__VA_ARGS__)
#define CALL_PARM_3(t, a, ...) t, CALL_PARM_2(__VA_ARGS__)
#define CALL_PARM_4(t, a, ...) t, CALL_PARM_3(__VA_ARGS__)
#define CALL_PARM_5(t, a, ...) t, CALL_PARM_4(__VA_ARGS__)
#define CALL_PARM_6(t, a, ...) t, CALL_PARM_5(__VA_ARGS__)

/*
 * Use call_on_stack() to call a function switching to a specified
 * stack. Proper sign and zero extension of function arguments is
 * done. Usage:
 *
 * rc = call_on_stack(nr, stack, rettype, fn, t1, a1, t2, a2, ...)
 *
 * - nr specifies the number of function arguments of fn.
 * - stack specifies the stack to be used.
 * - fn is the function to be called.
 * - rettype is the return type of fn.
 * - t1, a1, ... are pairs, where t1 must match the type of the first
 *   argument of fn, t2 the second, etc. a1 is the corresponding
 *   first function argument (not name), etc.
 */
#define call_on_stack(nr, stack, rettype, fn, ...)			\
({									\
	rettype (*__fn)(CALL_PARM_##nr(__VA_ARGS__)) = fn;		\
	unsigned long frame = current_frame_address();			\
	unsigned long __stack = stack;					\
	unsigned long prev;						\
	CALL_LARGS_##nr(__VA_ARGS__);					\
	CALL_REGS_##nr;							\
									\
	CALL_TYPECHECK_##nr(__VA_ARGS__);				\
	asm volatile(							\
		"	lgr	%[_prev],15\n"				\
		"	lg	15,%[_stack]\n"				\
		"	stg	%[_frame],%[_bc](15)\n"			\
		"	brasl	14,%[_fn]\n"				\
		"	lgr	15,%[_prev]\n"				\
		: [_prev] "=&d" (prev), CALL_FMT_##nr			\
		: [_stack] "R" (__stack),				\
		  [_bc] "i" (offsetof(struct stack_frame, back_chain)),	\
		  [_frame] "d" (frame),					\
		  [_fn] "X" (__fn) : CALL_CLOBBER_##nr);		\
	(rettype)r2;							\
})

#define CALL_ON_STACK_NORETURN(fn, stack)				\
({									\
	asm volatile(							\
		"	la	15,0(%[_stack])\n"			\
		"	xc	%[_bc](8,15),%[_bc](15)\n"		\
		"	brasl	14,%[_fn]\n"				\
		::[_bc] "i" (offsetof(struct stack_frame, back_chain)),	\
		  [_stack] "a" (stack), [_fn] "X" (fn));		\
	BUG();								\
})

#endif /* _ASM_S390_STACKTRACE_H */