]>
Commit | Line | Data |
---|---|---|
4ff1e447 JK |
1 | /* Dwarfless register access for s390x */ |
2 | ||
ed82a324 | 3 | @__private30 global _reg_offsets[22] |
4ff1e447 | 4 | |
eefd579b DS |
5 | %{ |
6 | #include <asm/ptrace.h> | |
3ecf49ee WC |
7 | #ifdef STAPCONF_STACKTRACE_H |
8 | #include <asm/stacktrace.h> | |
9 | #endif | |
eefd579b DS |
10 | |
11 | /* | |
12 | * The following routines help with getting arguments off the s390x | |
13 | * stack. For more details see: | |
14 | * | |
15 | * "S/390 ELF Application Binary Interface Supplement" | |
16 | * <http://refspecs.linuxfoundation.org/ELF/zSeries/lzsabi0_s390/x414.html> | |
17 | */ | |
18 | ||
19 | /* | |
20 | * This is a copy of the kernel's kernel_stack_pointer(), which is | |
21 | * only available on newer kernels. | |
22 | */ | |
23 | static inline unsigned long _stp_kernel_stack_pointer(struct pt_regs *regs) | |
24 | { | |
25 | return regs->gprs[15] & PSW_ADDR_INSN; | |
26 | } | |
27 | ||
28 | /* | |
29 | * This is a copy of the kernel's unexported regs_within_kernel_stack(). | |
30 | */ | |
31 | static int | |
32 | _stp_regs_within_kernel_stack(struct pt_regs *regs, unsigned long addr) | |
33 | { | |
34 | unsigned long ksp = _stp_kernel_stack_pointer(regs); | |
35 | ||
36 | return (addr & ~(THREAD_SIZE - 1)) == (ksp & ~(THREAD_SIZE - 1)); | |
37 | } | |
38 | %} | |
39 | ||
40 | /* | |
41 | * _stp_get_kernel_stack_param() - get Nth parameter from the stack | |
42 | * n:stack parameter number (starts from 0) | |
43 | * | |
44 | * _stp_get_kernel_stack_param() returns nth parameter from | |
45 | * the kernel stack. If the nth entry is NOT in the kernel stack, this | |
46 | * returns 0. | |
47 | * | |
48 | * This is based on the kernel's (unexported) | |
49 | * regs_get_kernel_stack_nth() function. | |
50 | */ | |
51 | function _stp_get_kernel_stack_param:long(n:long) | |
52 | %{ | |
aeb73b70 | 53 | __label__ deref_fault; |
eefd579b DS |
54 | unsigned long addr; |
55 | struct pt_regs *regs; | |
56 | ||
57 | regs = CONTEXT->kregs; | |
58 | if (!regs) { | |
59 | CONTEXT->last_error = "No registers available in this context"; | |
60 | return; | |
61 | } | |
62 | ||
63 | // The parameters start just after the stack frame. | |
64 | addr = (_stp_kernel_stack_pointer(regs) + sizeof(struct stack_frame) | |
65 | + STAP_ARG_n * sizeof(long)); | |
66 | if (!_stp_regs_within_kernel_stack(regs, addr)) { | |
67 | STAP_RETVALUE = 0; | |
68 | return; | |
69 | } | |
70 | STAP_RETVALUE = kread((unsigned long *)addr); | |
71 | return; | |
72 | ||
eefd579b | 73 | deref_fault: /* branched to from kread() */ |
aeb73b70 RH |
74 | snprintf (CONTEXT->error_buffer, sizeof(CONTEXT->error_buffer), |
75 | "kernel fault at %#lx accessing stack param(%d)", | |
76 | addr, (int)STAP_ARG_n); | |
77 | CONTEXT->last_error = CONTEXT->error_buffer; | |
eefd579b DS |
78 | %} |
79 | ||
6ae3ec1c | 80 | probe init { |
4ff1e447 JK |
81 | /* Same order as pt_regs */ |
82 | _reg_offsets["args"] = 0 | |
83 | _reg_offsets["psw.mask"] = 8 | |
84 | _reg_offsets["psw.addr"] = 16 | |
85 | _reg_offsets["r0"] = 24 | |
86 | _reg_offsets["r1"] = 32 | |
87 | _reg_offsets["r2"] = 40 | |
88 | _reg_offsets["r3"] = 48 | |
89 | _reg_offsets["r4"] = 56 | |
90 | _reg_offsets["r5"] = 64 | |
91 | _reg_offsets["r6"] = 72 | |
92 | _reg_offsets["r7"] = 80 | |
93 | _reg_offsets["r8"] = 88 | |
94 | _reg_offsets["r9"] = 96 | |
95 | _reg_offsets["r10"] = 104 | |
96 | _reg_offsets["r11"] = 112 | |
97 | _reg_offsets["r12"] = 120 | |
98 | _reg_offsets["r13"] = 128 | |
99 | _reg_offsets["r14"] = 136 | |
100 | _reg_offsets["r15"] = 144 | |
101 | ||
102 | _reg_offsets["orig_gpr2"] = 152 | |
103 | _reg_offsets["ilc"] = 160 | |
104 | _reg_offsets["trap"] = 162 | |
105 | ||
106 | /* | |
107 | * If we ever need to support s390 (31-bit arch), we can | |
108 | * get to the register offsets by using just a | |
109 | * reg32_offset = _reg_offsets["reg"]/2 | |
110 | * or somesuch | |
111 | */ | |
4ff1e447 JK |
112 | } |
113 | ||
114 | ||
115 | /* | |
116 | * Though the flag says 31bit, asm-s390/thread_info.h comment | |
117 | * says "32bit process" | |
118 | */ | |
b583125d | 119 | function probing_32bit_app:long() %{ /* pure */ |
e04b5d74 | 120 | if (CONTEXT->user_mode_p && _stp_is_compat_task()) |
b7b482fb | 121 | STAP_RETVALUE = 1; |
4ff1e447 | 122 | else |
b7b482fb | 123 | STAP_RETVALUE = 0; |
4ff1e447 JK |
124 | %} |
125 | ||
92c25572 MW |
126 | function _stp_probing_kernel: long () { |
127 | return !user_mode(); | |
128 | } | |
4ff1e447 | 129 | |
93f70a9a FL |
130 | function arch_bytes:long() %{ /* pure */ |
131 | STAP_RETVALUE = sizeof(long); | |
132 | %} | |
133 | ||
134 | function uarch_bytes:long() { | |
247e2766 FL |
135 | assert(user_mode(), "requires user mode") |
136 | return probing_32bit_app() ? 4 : 8 | |
93f70a9a FL |
137 | } |
138 | ||
4ff1e447 JK |
139 | function _stp_get_register_by_offset:long (offset:long) %{ /* pure */ |
140 | long value; | |
d9aed31e | 141 | struct pt_regs *regs; |
5409ddea FCE |
142 | if (CONTEXT->sregs) |
143 | regs = CONTEXT->sregs; | |
144 | else | |
145 | regs = (CONTEXT->user_mode_p ? CONTEXT->uregs : CONTEXT->kregs); | |
d9aed31e | 146 | if (!regs) { |
ba4e4ff4 JS |
147 | CONTEXT->last_error = "No registers available in this context"; |
148 | return; | |
149 | } | |
b7b482fb | 150 | if (STAP_ARG_offset < 0 || STAP_ARG_offset > sizeof(struct pt_regs) - sizeof(unsigned short)) { |
ba4e4ff4 | 151 | snprintf(CONTEXT->error_buffer, sizeof(CONTEXT->error_buffer), |
08fcfd65 | 152 | "Bad register offset: %lld", |
b7b482fb | 153 | (long long)STAP_ARG_offset); |
ba4e4ff4 JS |
154 | CONTEXT->last_error = CONTEXT->error_buffer; |
155 | return; | |
156 | } | |
4ff1e447 | 157 | |
b7b482fb SM |
158 | if (STAP_ARG_offset < sizeof(struct pt_regs) - 2 * sizeof(unsigned short)) |
159 | memcpy(&value, ((char *)regs) + STAP_ARG_offset, | |
4ff1e447 JK |
160 | sizeof(value)); |
161 | else { | |
162 | /* ilc or trap */ | |
163 | unsigned short us_value; | |
b7b482fb | 164 | memcpy(&us_value, ((char *)regs) + STAP_ARG_offset, |
4ff1e447 JK |
165 | sizeof(us_value)); |
166 | value = us_value; // not sign-extended | |
167 | } | |
b7b482fb | 168 | STAP_RETVALUE = value; |
4ff1e447 JK |
169 | %} |
170 | ||
171 | function _stp_sign_extend32:long (value:long) { | |
172 | if (value & 0x80000000) | |
173 | value |= (0xffffffff << 32) | |
174 | return value | |
175 | } | |
176 | ||
177 | function _stp_register:long (name:string, sign_extend:long) { | |
5409ddea FCE |
178 | # don't assert this: will get *regs state checked in _stp_get_register_by_offset, and better |
179 | # assert(registers_valid(), "cannot access CPU registers in this context") | |
4ff1e447 | 180 | offset = _reg_offsets[name] |
5409ddea | 181 | assert(offset != 0 || (name in _reg_offsets), "Unknown register: " . name) |
4ff1e447 JK |
182 | value = _stp_get_register_by_offset(offset) |
183 | if (probing_32bit_app()) { | |
184 | if (sign_extend) | |
185 | value = _stp_sign_extend32(value) | |
186 | else | |
187 | value &= 0xffffffff | |
188 | } | |
189 | return value | |
190 | } | |
191 | ||
192 | /* Return the named register value as a signed value. */ | |
193 | function register:long (name:string) { | |
194 | return _stp_register(name, 1) | |
195 | } | |
196 | ||
197 | /* | |
198 | * Return the named register value as an unsigned value. Specifically, | |
199 | * don't sign-extend the register value when promoting it to 64 bits. | |
200 | */ | |
201 | function u_register:long (name:string) { | |
202 | return _stp_register(name, 0) | |
203 | } | |
204 | ||
205 | /* | |
206 | * Return the value of function arg #argnum (1=first arg). | |
207 | * If truncate=1, mask off the top 32 bits. | |
208 | * If sign_extend=1 and (truncate=1 or the probepoint we've hit is in a | |
209 | * 32-bit app), sign-extend the 32-bit value. | |
7a563a0f | 210 | * If force64=1, return a 64-bit value even if we're in a 32-bit app. |
4ff1e447 | 211 | */ |
f861d05e | 212 | function _stp_arg:long (argnum:long, sign_extend:long, truncate:long) |
7a563a0f DS |
213 | { |
214 | return _stp_arg2(argnum, sign_extend, truncate, 0) | |
215 | } | |
216 | function _stp_arg2:long (argnum:long, sign_extend:long, truncate:long, | |
217 | force64:long) | |
eefd579b DS |
218 | { |
219 | val = 0 | |
247e2766 | 220 | assert(!(argnum < 1 || argnum > 8), sprintf("Cannot access arg(%d)", argnum)) |
4ff1e447 | 221 | |
eefd579b DS |
222 | /* |
223 | * Why not use syscall_get_arguments() here? On the s390x, | |
224 | * syscall_get_arguments() is only defined to work on the | |
225 | * pt_regs structure that gets intialized when a context | |
226 | * switch from user space to kernel space happens due to a | |
227 | * system call. This pt_regs structure is returned by | |
228 | * 'task_pt_regs(current)'. | |
229 | * | |
230 | * This function is designed to get the argument of the | |
231 | * current kernel function, which may or may not be a | |
232 | * syscall. So, we have to roll our own. | |
233 | */ | |
f861d05e | 234 | |
eefd579b DS |
235 | if (argnum == 1) |
236 | val = u_register("r2") | |
237 | else if (argnum == 2) | |
238 | val = u_register("r3") | |
239 | else if (argnum == 3) | |
240 | val = u_register("r4") | |
241 | else if (argnum == 4) | |
242 | val = u_register("r5") | |
243 | else if (argnum == 5) | |
244 | val = u_register("r6") | |
5409ddea FCE |
245 | else if (argnum == 6 && %{ CONTEXT->sregs != NULL %} ) // linux syscall arg6 goes into r7 |
246 | val = u_register("r7") | |
eefd579b | 247 | else if (argnum >= 6) |
5409ddea | 248 | val = _stp_get_kernel_stack_param(argnum - 6); |
eefd579b | 249 | |
7a563a0f | 250 | if ((truncate || @__compat_task) && !force64) { |
eefd579b DS |
251 | /* High bits may be garbage. */ |
252 | val = (val & 0xffffffff) | |
253 | if (sign_extend) | |
254 | val = _stp_sign_extend32(val) | |
4ff1e447 | 255 | } |
eefd579b DS |
256 | return val |
257 | } | |
4ff1e447 JK |
258 | |
259 | /* Return the value of function arg #argnum (1=first arg) as a signed int. */ | |
260 | function int_arg:long (argnum:long) { | |
7a563a0f | 261 | return _stp_arg2(argnum, 1, 1, 0) |
4ff1e447 JK |
262 | } |
263 | ||
264 | /* Return the value of function arg #argnum (1=first arg) as an unsigned int. */ | |
265 | function uint_arg:long (argnum:long) { | |
7a563a0f | 266 | return _stp_arg2(argnum, 0, 1, 0) |
4ff1e447 JK |
267 | } |
268 | ||
269 | function long_arg:long (argnum:long) { | |
7a563a0f | 270 | return _stp_arg2(argnum, 1, 0, 0) |
4ff1e447 JK |
271 | } |
272 | ||
273 | function ulong_arg:long (argnum:long) { | |
7a563a0f | 274 | return _stp_arg2(argnum, 0, 0, 0) |
4ff1e447 JK |
275 | } |
276 | ||
277 | function longlong_arg:long (argnum:long) { | |
278 | if (probing_32bit_app()) { | |
7a563a0f DS |
279 | highbits = _stp_arg2(argnum, 0, 1, 0) |
280 | lowbits = _stp_arg2(argnum+1, 0, 1, 0) | |
4ff1e447 JK |
281 | return ((highbits << 32) | lowbits) |
282 | } else | |
7a563a0f | 283 | return _stp_arg2(argnum, 0, 0, 1) |
4ff1e447 JK |
284 | } |
285 | ||
286 | function ulonglong_arg:long (argnum:long) { | |
287 | return longlong_arg(argnum) | |
288 | } | |
289 | ||
290 | function pointer_arg:long (argnum:long) { | |
7a563a0f | 291 | return _stp_arg2(argnum, 0, 0, 0) |
4ff1e447 JK |
292 | } |
293 | ||
294 | function s32_arg:long (argnum:long) { | |
295 | return int_arg(argnum) | |
296 | } | |
297 | ||
298 | function u32_arg:long (argnum:long) { | |
299 | return uint_arg(argnum) | |
300 | } | |
301 | ||
302 | function s64_arg:long (argnum:long) { | |
303 | return longlong_arg(argnum) | |
304 | } | |
305 | ||
306 | function u64_arg:long (argnum:long) { | |
307 | return ulonglong_arg(argnum) | |
308 | } | |
309 | ||
29d0edeb | 310 | function asmlinkage() %{ /* pure */ %} |
4ff1e447 | 311 | |
29d0edeb | 312 | function fastcall() %{ /* pure */ %} |
4ff1e447 | 313 | |
309d67d8 | 314 | function regparm(n:long) %{ |
4ff1e447 JK |
315 | snprintf(CONTEXT->error_buffer, sizeof(CONTEXT->error_buffer), |
316 | "regparm is invalid on s390."); | |
317 | CONTEXT->last_error = CONTEXT->error_buffer; | |
318 | %} |