]>
Commit | Line | Data |
---|---|---|
78c98f90 MS |
1 | /* SPDX-License-Identifier: GPL-2.0 */ |
2 | #ifndef _ASM_S390_STACKTRACE_H | |
3 | #define _ASM_S390_STACKTRACE_H | |
4 | ||
5 | #include <linux/uaccess.h> | |
6 | #include <linux/ptrace.h> | |
7 | #include <asm/switch_to.h> | |
8 | ||
9 | enum stack_type { | |
10 | STACK_TYPE_UNKNOWN, | |
11 | STACK_TYPE_TASK, | |
12 | STACK_TYPE_IRQ, | |
13 | STACK_TYPE_NODAT, | |
14 | STACK_TYPE_RESTART, | |
15 | }; | |
16 | ||
17 | struct stack_info { | |
18 | enum stack_type type; | |
19 | unsigned long begin, end; | |
20 | }; | |
21 | ||
22 | const char *stack_type_name(enum stack_type type); | |
23 | int get_stack_info(unsigned long sp, struct task_struct *task, | |
24 | struct stack_info *info, unsigned long *visit_mask); | |
25 | ||
26 | static inline bool on_stack(struct stack_info *info, | |
27 | unsigned long addr, size_t len) | |
28 | { | |
29 | if (info->type == STACK_TYPE_UNKNOWN) | |
30 | return false; | |
31 | if (addr + len < addr) | |
32 | return false; | |
0ab0d7ac | 33 | return addr >= info->begin && addr + len <= info->end; |
78c98f90 MS |
34 | } |
35 | ||
36 | static inline unsigned long get_stack_pointer(struct task_struct *task, | |
37 | struct pt_regs *regs) | |
38 | { | |
39 | if (regs) | |
40 | return (unsigned long) kernel_stack_pointer(regs); | |
41 | if (task == current) | |
42 | return current_stack_pointer(); | |
43 | return (unsigned long) task->thread.ksp; | |
44 | } | |
45 | ||
46 | /* | |
47 | * Stack layout of a C stack frame. | |
48 | */ | |
49 | #ifndef __PACK_STACK | |
50 | struct stack_frame { | |
51 | unsigned long back_chain; | |
52 | unsigned long empty1[5]; | |
53 | unsigned long gprs[10]; | |
54 | unsigned int empty2[8]; | |
55 | }; | |
56 | #else | |
57 | struct stack_frame { | |
58 | unsigned long empty1[5]; | |
59 | unsigned int empty2[8]; | |
60 | unsigned long gprs[10]; | |
61 | unsigned long back_chain; | |
62 | }; | |
63 | #endif | |
64 | ||
65 | #define CALL_ARGS_0() \ | |
66 | register unsigned long r2 asm("2") | |
67 | #define CALL_ARGS_1(arg1) \ | |
68 | register unsigned long r2 asm("2") = (unsigned long)(arg1) | |
69 | #define CALL_ARGS_2(arg1, arg2) \ | |
70 | CALL_ARGS_1(arg1); \ | |
71 | register unsigned long r3 asm("3") = (unsigned long)(arg2) | |
72 | #define CALL_ARGS_3(arg1, arg2, arg3) \ | |
73 | CALL_ARGS_2(arg1, arg2); \ | |
74 | register unsigned long r4 asm("4") = (unsigned long)(arg3) | |
75 | #define CALL_ARGS_4(arg1, arg2, arg3, arg4) \ | |
76 | CALL_ARGS_3(arg1, arg2, arg3); \ | |
77 | register unsigned long r4 asm("5") = (unsigned long)(arg4) | |
78 | #define CALL_ARGS_5(arg1, arg2, arg3, arg4, arg5) \ | |
79 | CALL_ARGS_4(arg1, arg2, arg3, arg4); \ | |
80 | register unsigned long r4 asm("6") = (unsigned long)(arg5) | |
81 | ||
82 | #define CALL_FMT_0 "=&d" (r2) : | |
83 | #define CALL_FMT_1 "+&d" (r2) : | |
84 | #define CALL_FMT_2 CALL_FMT_1 "d" (r3), | |
85 | #define CALL_FMT_3 CALL_FMT_2 "d" (r4), | |
86 | #define CALL_FMT_4 CALL_FMT_3 "d" (r5), | |
87 | #define CALL_FMT_5 CALL_FMT_4 "d" (r6), | |
88 | ||
89 | #define CALL_CLOBBER_5 "0", "1", "14", "cc", "memory" | |
90 | #define CALL_CLOBBER_4 CALL_CLOBBER_5 | |
91 | #define CALL_CLOBBER_3 CALL_CLOBBER_4, "5" | |
92 | #define CALL_CLOBBER_2 CALL_CLOBBER_3, "4" | |
93 | #define CALL_CLOBBER_1 CALL_CLOBBER_2, "3" | |
94 | #define CALL_CLOBBER_0 CALL_CLOBBER_1 | |
95 | ||
96 | #define CALL_ON_STACK(fn, stack, nr, args...) \ | |
97 | ({ \ | |
98 | CALL_ARGS_##nr(args); \ | |
99 | unsigned long prev; \ | |
100 | \ | |
101 | asm volatile( \ | |
102 | " la %[_prev],0(15)\n" \ | |
103 | " la 15,0(%[_stack])\n" \ | |
104 | " stg %[_prev],%[_bc](15)\n" \ | |
105 | " brasl 14,%[_fn]\n" \ | |
106 | " la 15,0(%[_prev])\n" \ | |
107 | : [_prev] "=&a" (prev), CALL_FMT_##nr \ | |
108 | [_stack] "a" (stack), \ | |
109 | [_bc] "i" (offsetof(struct stack_frame, back_chain)), \ | |
110 | [_fn] "X" (fn) : CALL_CLOBBER_##nr); \ | |
111 | r2; \ | |
112 | }) | |
113 | ||
114 | #endif /* _ASM_S390_STACKTRACE_H */ |