]>
Commit | Line | Data |
---|---|---|
5a0015d6 CZ |
1 | /* |
2 | * This file is subject to the terms and conditions of the GNU General Public | |
3 | * License. See the file "COPYING" in the main directory of this archive | |
4 | * for more details. | |
5 | * | |
c658eac6 | 6 | * Copyright (C) 2001 - 2007 Tensilica Inc. |
5a0015d6 CZ |
7 | * |
8 | * Joe Taylor <[email protected], [email protected]> | |
9 | * Chris Zankel <[email protected]> | |
10 | * Scott Foehner<[email protected]>, | |
11 | * Kevin Chea | |
12 | * Marc Gauthier<[email protected]> <[email protected]> | |
13 | */ | |
14 | ||
c91e02bd MF |
15 | #include <linux/errno.h> |
16 | #include <linux/hw_breakpoint.h> | |
5a0015d6 | 17 | #include <linux/kernel.h> |
5a0015d6 | 18 | #include <linux/mm.h> |
c91e02bd | 19 | #include <linux/perf_event.h> |
5a0015d6 | 20 | #include <linux/ptrace.h> |
c91e02bd | 21 | #include <linux/sched.h> |
68db0cf1 | 22 | #include <linux/sched/task_stack.h> |
5a0015d6 | 23 | #include <linux/security.h> |
0ee23b50 | 24 | #include <linux/signal.h> |
c91e02bd | 25 | #include <linux/smp.h> |
f984409a | 26 | #include <linux/tracehook.h> |
1b47a683 | 27 | #include <linux/uaccess.h> |
5a0015d6 | 28 | |
c91e02bd MF |
29 | #include <asm/coprocessor.h> |
30 | #include <asm/elf.h> | |
5a0015d6 | 31 | #include <asm/page.h> |
c91e02bd | 32 | #include <asm/pgtable.h> |
5a0015d6 | 33 | #include <asm/ptrace.h> |
5a0015d6 | 34 | |
6d75ca10 CH |
35 | |
36 | void user_enable_single_step(struct task_struct *child) | |
37 | { | |
38 | child->ptrace |= PT_SINGLESTEP; | |
39 | } | |
40 | ||
41 | void user_disable_single_step(struct task_struct *child) | |
42 | { | |
43 | child->ptrace &= ~PT_SINGLESTEP; | |
44 | } | |
45 | ||
5a0015d6 | 46 | /* |
c658eac6 | 47 | * Called by kernel/ptrace.c when detaching to disable single stepping. |
5a0015d6 CZ |
48 | */ |
49 | ||
50 | void ptrace_disable(struct task_struct *child) | |
51 | { | |
52 | /* Nothing to do.. */ | |
53 | } | |
54 | ||
1b47a683 | 55 | static int ptrace_getregs(struct task_struct *child, void __user *uregs) |
5a0015d6 | 56 | { |
c658eac6 CZ |
57 | struct pt_regs *regs = task_pt_regs(child); |
58 | xtensa_gregset_t __user *gregset = uregs; | |
42086cec | 59 | unsigned long wb = regs->windowbase; |
4b2bb03f | 60 | int i; |
c658eac6 CZ |
61 | |
62 | if (!access_ok(VERIFY_WRITE, uregs, sizeof(xtensa_gregset_t))) | |
63 | return -EIO; | |
64 | ||
42086cec CZ |
65 | __put_user(regs->pc, &gregset->pc); |
66 | __put_user(regs->ps & ~(1 << PS_EXCM_BIT), &gregset->ps); | |
67 | __put_user(regs->lbeg, &gregset->lbeg); | |
68 | __put_user(regs->lend, &gregset->lend); | |
69 | __put_user(regs->lcount, &gregset->lcount); | |
70 | __put_user(regs->windowstart, &gregset->windowstart); | |
71 | __put_user(regs->windowbase, &gregset->windowbase); | |
c50842df | 72 | __put_user(regs->threadptr, &gregset->threadptr); |
c658eac6 | 73 | |
4b2bb03f MF |
74 | for (i = 0; i < XCHAL_NUM_AREGS; i++) |
75 | __put_user(regs->areg[i], | |
1b47a683 | 76 | gregset->a + ((wb * 4 + i) % XCHAL_NUM_AREGS)); |
42086cec CZ |
77 | |
78 | return 0; | |
c658eac6 | 79 | } |
5a0015d6 | 80 | |
1b47a683 | 81 | static int ptrace_setregs(struct task_struct *child, void __user *uregs) |
c658eac6 CZ |
82 | { |
83 | struct pt_regs *regs = task_pt_regs(child); | |
84 | xtensa_gregset_t *gregset = uregs; | |
85 | const unsigned long ps_mask = PS_CALLINC_MASK | PS_OWB_MASK; | |
c658eac6 | 86 | unsigned long ps; |
4b2bb03f | 87 | unsigned long wb, ws; |
c658eac6 CZ |
88 | |
89 | if (!access_ok(VERIFY_WRITE, uregs, sizeof(xtensa_gregset_t))) | |
90 | return -EIO; | |
91 | ||
42086cec CZ |
92 | __get_user(regs->pc, &gregset->pc); |
93 | __get_user(ps, &gregset->ps); | |
94 | __get_user(regs->lbeg, &gregset->lbeg); | |
95 | __get_user(regs->lend, &gregset->lend); | |
96 | __get_user(regs->lcount, &gregset->lcount); | |
4b2bb03f | 97 | __get_user(ws, &gregset->windowstart); |
42086cec | 98 | __get_user(wb, &gregset->windowbase); |
c50842df | 99 | __get_user(regs->threadptr, &gregset->threadptr); |
c658eac6 CZ |
100 | |
101 | regs->ps = (regs->ps & ~ps_mask) | (ps & ps_mask) | (1 << PS_EXCM_BIT); | |
102 | ||
42086cec CZ |
103 | if (wb >= XCHAL_NUM_AREGS / 4) |
104 | return -EFAULT; | |
c658eac6 | 105 | |
4b2bb03f MF |
106 | if (wb != regs->windowbase || ws != regs->windowstart) { |
107 | unsigned long rotws, wmask; | |
108 | ||
109 | rotws = (((ws | (ws << WSBITS)) >> wb) & | |
1b47a683 | 110 | ((1 << WSBITS) - 1)) & ~1; |
4b2bb03f MF |
111 | wmask = ((rotws ? WSBITS + 1 - ffs(rotws) : 0) << 4) | |
112 | (rotws & 0xF) | 1; | |
113 | regs->windowbase = wb; | |
114 | regs->windowstart = ws; | |
115 | regs->wmask = wmask; | |
116 | } | |
42086cec | 117 | |
1b47a683 MF |
118 | if (wb != 0 && __copy_from_user(regs->areg + XCHAL_NUM_AREGS - wb * 4, |
119 | gregset->a, wb * 16)) | |
42086cec CZ |
120 | return -EFAULT; |
121 | ||
4b2bb03f | 122 | if (__copy_from_user(regs->areg, gregset->a + wb * 4, |
1b47a683 | 123 | (WSBITS - wb) * 16)) |
42086cec CZ |
124 | return -EFAULT; |
125 | ||
126 | return 0; | |
c658eac6 | 127 | } |
5a0015d6 | 128 | |
5a0015d6 | 129 | |
38a35a78 MF |
130 | #if XTENSA_HAVE_COPROCESSORS |
131 | #define CP_OFFSETS(cp) \ | |
132 | { \ | |
133 | .elf_xtregs_offset = offsetof(elf_xtregs_t, cp), \ | |
134 | .ti_offset = offsetof(struct thread_info, xtregs_cp.cp), \ | |
135 | .sz = sizeof(xtregs_ ## cp ## _t), \ | |
136 | } | |
137 | ||
138 | static const struct { | |
139 | size_t elf_xtregs_offset; | |
140 | size_t ti_offset; | |
141 | size_t sz; | |
142 | } cp_offsets[] = { | |
143 | CP_OFFSETS(cp0), | |
144 | CP_OFFSETS(cp1), | |
145 | CP_OFFSETS(cp2), | |
146 | CP_OFFSETS(cp3), | |
147 | CP_OFFSETS(cp4), | |
148 | CP_OFFSETS(cp5), | |
149 | CP_OFFSETS(cp6), | |
150 | CP_OFFSETS(cp7), | |
151 | }; | |
152 | #endif | |
153 | ||
1b47a683 | 154 | static int ptrace_getxregs(struct task_struct *child, void __user *uregs) |
c658eac6 CZ |
155 | { |
156 | struct pt_regs *regs = task_pt_regs(child); | |
157 | struct thread_info *ti = task_thread_info(child); | |
158 | elf_xtregs_t __user *xtregs = uregs; | |
159 | int ret = 0; | |
38a35a78 | 160 | int i __maybe_unused; |
c658eac6 CZ |
161 | |
162 | if (!access_ok(VERIFY_WRITE, uregs, sizeof(elf_xtregs_t))) | |
163 | return -EIO; | |
164 | ||
165 | #if XTENSA_HAVE_COPROCESSORS | |
166 | /* Flush all coprocessor registers to memory. */ | |
167 | coprocessor_flush_all(ti); | |
38a35a78 MF |
168 | |
169 | for (i = 0; i < ARRAY_SIZE(cp_offsets); ++i) | |
170 | ret |= __copy_to_user((char __user *)xtregs + | |
171 | cp_offsets[i].elf_xtregs_offset, | |
172 | (const char *)ti + | |
173 | cp_offsets[i].ti_offset, | |
174 | cp_offsets[i].sz); | |
c658eac6 CZ |
175 | #endif |
176 | ret |= __copy_to_user(&xtregs->opt, ®s->xtregs_opt, | |
177 | sizeof(xtregs->opt)); | |
178 | ret |= __copy_to_user(&xtregs->user,&ti->xtregs_user, | |
179 | sizeof(xtregs->user)); | |
5a0015d6 | 180 | |
c658eac6 CZ |
181 | return ret ? -EFAULT : 0; |
182 | } | |
183 | ||
1b47a683 | 184 | static int ptrace_setxregs(struct task_struct *child, void __user *uregs) |
c658eac6 CZ |
185 | { |
186 | struct thread_info *ti = task_thread_info(child); | |
187 | struct pt_regs *regs = task_pt_regs(child); | |
188 | elf_xtregs_t *xtregs = uregs; | |
189 | int ret = 0; | |
38a35a78 | 190 | int i __maybe_unused; |
c658eac6 | 191 | |
0d0138eb DR |
192 | if (!access_ok(VERIFY_READ, uregs, sizeof(elf_xtregs_t))) |
193 | return -EFAULT; | |
194 | ||
c658eac6 CZ |
195 | #if XTENSA_HAVE_COPROCESSORS |
196 | /* Flush all coprocessors before we overwrite them. */ | |
197 | coprocessor_flush_all(ti); | |
198 | coprocessor_release_all(ti); | |
199 | ||
38a35a78 MF |
200 | for (i = 0; i < ARRAY_SIZE(cp_offsets); ++i) |
201 | ret |= __copy_from_user((char *)ti + cp_offsets[i].ti_offset, | |
202 | (const char __user *)xtregs + | |
203 | cp_offsets[i].elf_xtregs_offset, | |
204 | cp_offsets[i].sz); | |
c658eac6 CZ |
205 | #endif |
206 | ret |= __copy_from_user(®s->xtregs_opt, &xtregs->opt, | |
207 | sizeof(xtregs->opt)); | |
208 | ret |= __copy_from_user(&ti->xtregs_user, &xtregs->user, | |
209 | sizeof(xtregs->user)); | |
210 | ||
211 | return ret ? -EFAULT : 0; | |
212 | } | |
213 | ||
1b47a683 MF |
214 | static int ptrace_peekusr(struct task_struct *child, long regno, |
215 | long __user *ret) | |
c658eac6 CZ |
216 | { |
217 | struct pt_regs *regs; | |
218 | unsigned long tmp; | |
219 | ||
220 | regs = task_pt_regs(child); | |
221 | tmp = 0; /* Default return value. */ | |
5a0015d6 | 222 | |
c658eac6 | 223 | switch(regno) { |
1b47a683 MF |
224 | case REG_AR_BASE ... REG_AR_BASE + XCHAL_NUM_AREGS - 1: |
225 | tmp = regs->areg[regno - REG_AR_BASE]; | |
226 | break; | |
5a0015d6 | 227 | |
1b47a683 MF |
228 | case REG_A_BASE ... REG_A_BASE + 15: |
229 | tmp = regs->areg[regno - REG_A_BASE]; | |
230 | break; | |
c658eac6 | 231 | |
1b47a683 MF |
232 | case REG_PC: |
233 | tmp = regs->pc; | |
234 | break; | |
c658eac6 | 235 | |
1b47a683 MF |
236 | case REG_PS: |
237 | /* Note: PS.EXCM is not set while user task is running; | |
238 | * its being set in regs is for exception handling | |
239 | * convenience. | |
240 | */ | |
241 | tmp = (regs->ps & ~(1 << PS_EXCM_BIT)); | |
242 | break; | |
c658eac6 | 243 | |
1b47a683 MF |
244 | case REG_WB: |
245 | break; /* tmp = 0 */ | |
c658eac6 | 246 | |
1b47a683 | 247 | case REG_WS: |
c658eac6 CZ |
248 | { |
249 | unsigned long wb = regs->windowbase; | |
250 | unsigned long ws = regs->windowstart; | |
1b47a683 MF |
251 | tmp = ((ws >> wb) | (ws << (WSBITS - wb))) & |
252 | ((1 << WSBITS) - 1); | |
5a0015d6 | 253 | break; |
c658eac6 | 254 | } |
1b47a683 MF |
255 | case REG_LBEG: |
256 | tmp = regs->lbeg; | |
257 | break; | |
c658eac6 | 258 | |
1b47a683 MF |
259 | case REG_LEND: |
260 | tmp = regs->lend; | |
261 | break; | |
c658eac6 | 262 | |
1b47a683 MF |
263 | case REG_LCOUNT: |
264 | tmp = regs->lcount; | |
265 | break; | |
c658eac6 | 266 | |
1b47a683 MF |
267 | case REG_SAR: |
268 | tmp = regs->sar; | |
269 | break; | |
c658eac6 | 270 | |
1b47a683 MF |
271 | case SYSCALL_NR: |
272 | tmp = regs->syscall; | |
273 | break; | |
5a0015d6 | 274 | |
1b47a683 MF |
275 | default: |
276 | return -EIO; | |
c658eac6 CZ |
277 | } |
278 | return put_user(tmp, ret); | |
279 | } | |
5a0015d6 | 280 | |
1b47a683 | 281 | static int ptrace_pokeusr(struct task_struct *child, long regno, long val) |
c658eac6 CZ |
282 | { |
283 | struct pt_regs *regs; | |
284 | regs = task_pt_regs(child); | |
5a0015d6 | 285 | |
c658eac6 | 286 | switch (regno) { |
1b47a683 MF |
287 | case REG_AR_BASE ... REG_AR_BASE + XCHAL_NUM_AREGS - 1: |
288 | regs->areg[regno - REG_AR_BASE] = val; | |
289 | break; | |
c658eac6 | 290 | |
1b47a683 MF |
291 | case REG_A_BASE ... REG_A_BASE + 15: |
292 | regs->areg[regno - REG_A_BASE] = val; | |
293 | break; | |
c658eac6 | 294 | |
1b47a683 MF |
295 | case REG_PC: |
296 | regs->pc = val; | |
297 | break; | |
c658eac6 | 298 | |
1b47a683 MF |
299 | case SYSCALL_NR: |
300 | regs->syscall = val; | |
301 | break; | |
5a0015d6 | 302 | |
1b47a683 MF |
303 | default: |
304 | return -EIO; | |
c658eac6 CZ |
305 | } |
306 | return 0; | |
307 | } | |
308 | ||
c91e02bd MF |
309 | #ifdef CONFIG_HAVE_HW_BREAKPOINT |
310 | static void ptrace_hbptriggered(struct perf_event *bp, | |
311 | struct perf_sample_data *data, | |
312 | struct pt_regs *regs) | |
313 | { | |
314 | int i; | |
c91e02bd MF |
315 | struct arch_hw_breakpoint *bkpt = counter_arch_bp(bp); |
316 | ||
317 | if (bp->attr.bp_type & HW_BREAKPOINT_X) { | |
318 | for (i = 0; i < XCHAL_NUM_IBREAK; ++i) | |
319 | if (current->thread.ptrace_bp[i] == bp) | |
320 | break; | |
321 | i <<= 1; | |
322 | } else { | |
323 | for (i = 0; i < XCHAL_NUM_DBREAK; ++i) | |
324 | if (current->thread.ptrace_wp[i] == bp) | |
325 | break; | |
326 | i = (i << 1) | 1; | |
327 | } | |
328 | ||
f71dd7dc | 329 | force_sig_ptrace_errno_trap(i, (void __user *)bkpt->address); |
c91e02bd MF |
330 | } |
331 | ||
332 | static struct perf_event *ptrace_hbp_create(struct task_struct *tsk, int type) | |
333 | { | |
334 | struct perf_event_attr attr; | |
335 | ||
336 | ptrace_breakpoint_init(&attr); | |
337 | ||
338 | /* Initialise fields to sane defaults. */ | |
339 | attr.bp_addr = 0; | |
340 | attr.bp_len = 1; | |
341 | attr.bp_type = type; | |
342 | attr.disabled = 1; | |
343 | ||
344 | return register_user_hw_breakpoint(&attr, ptrace_hbptriggered, NULL, | |
345 | tsk); | |
346 | } | |
347 | ||
348 | /* | |
349 | * Address bit 0 choose instruction (0) or data (1) break register, bits | |
350 | * 31..1 are the register number. | |
351 | * Both PTRACE_GETHBPREGS and PTRACE_SETHBPREGS transfer two 32-bit words: | |
352 | * address (0) and control (1). | |
353 | * Instruction breakpoint contorl word is 0 to clear breakpoint, 1 to set. | |
354 | * Data breakpoint control word bit 31 is 'trigger on store', bit 30 is | |
355 | * 'trigger on load, bits 29..0 are length. Length 0 is used to clear a | |
356 | * breakpoint. To set a breakpoint length must be a power of 2 in the range | |
357 | * 1..64 and the address must be length-aligned. | |
358 | */ | |
359 | ||
360 | static long ptrace_gethbpregs(struct task_struct *child, long addr, | |
361 | long __user *datap) | |
362 | { | |
363 | struct perf_event *bp; | |
364 | u32 user_data[2] = {0}; | |
365 | bool dbreak = addr & 1; | |
366 | unsigned idx = addr >> 1; | |
367 | ||
368 | if ((!dbreak && idx >= XCHAL_NUM_IBREAK) || | |
369 | (dbreak && idx >= XCHAL_NUM_DBREAK)) | |
370 | return -EINVAL; | |
371 | ||
372 | if (dbreak) | |
373 | bp = child->thread.ptrace_wp[idx]; | |
374 | else | |
375 | bp = child->thread.ptrace_bp[idx]; | |
376 | ||
377 | if (bp) { | |
378 | user_data[0] = bp->attr.bp_addr; | |
379 | user_data[1] = bp->attr.disabled ? 0 : bp->attr.bp_len; | |
380 | if (dbreak) { | |
381 | if (bp->attr.bp_type & HW_BREAKPOINT_R) | |
382 | user_data[1] |= DBREAKC_LOAD_MASK; | |
383 | if (bp->attr.bp_type & HW_BREAKPOINT_W) | |
384 | user_data[1] |= DBREAKC_STOR_MASK; | |
385 | } | |
386 | } | |
387 | ||
388 | if (copy_to_user(datap, user_data, sizeof(user_data))) | |
389 | return -EFAULT; | |
390 | ||
391 | return 0; | |
392 | } | |
393 | ||
394 | static long ptrace_sethbpregs(struct task_struct *child, long addr, | |
395 | long __user *datap) | |
396 | { | |
397 | struct perf_event *bp; | |
398 | struct perf_event_attr attr; | |
399 | u32 user_data[2]; | |
400 | bool dbreak = addr & 1; | |
401 | unsigned idx = addr >> 1; | |
402 | int bp_type = 0; | |
403 | ||
404 | if ((!dbreak && idx >= XCHAL_NUM_IBREAK) || | |
405 | (dbreak && idx >= XCHAL_NUM_DBREAK)) | |
406 | return -EINVAL; | |
407 | ||
408 | if (copy_from_user(user_data, datap, sizeof(user_data))) | |
409 | return -EFAULT; | |
410 | ||
411 | if (dbreak) { | |
412 | bp = child->thread.ptrace_wp[idx]; | |
413 | if (user_data[1] & DBREAKC_LOAD_MASK) | |
414 | bp_type |= HW_BREAKPOINT_R; | |
415 | if (user_data[1] & DBREAKC_STOR_MASK) | |
416 | bp_type |= HW_BREAKPOINT_W; | |
417 | } else { | |
418 | bp = child->thread.ptrace_bp[idx]; | |
419 | bp_type = HW_BREAKPOINT_X; | |
420 | } | |
421 | ||
422 | if (!bp) { | |
423 | bp = ptrace_hbp_create(child, | |
424 | bp_type ? bp_type : HW_BREAKPOINT_RW); | |
425 | if (IS_ERR(bp)) | |
426 | return PTR_ERR(bp); | |
427 | if (dbreak) | |
428 | child->thread.ptrace_wp[idx] = bp; | |
429 | else | |
430 | child->thread.ptrace_bp[idx] = bp; | |
431 | } | |
432 | ||
433 | attr = bp->attr; | |
434 | attr.bp_addr = user_data[0]; | |
435 | attr.bp_len = user_data[1] & ~(DBREAKC_LOAD_MASK | DBREAKC_STOR_MASK); | |
436 | attr.bp_type = bp_type; | |
437 | attr.disabled = !attr.bp_len; | |
438 | ||
439 | return modify_user_hw_breakpoint(bp, &attr); | |
440 | } | |
441 | #endif | |
442 | ||
9b05a69e NK |
443 | long arch_ptrace(struct task_struct *child, long request, |
444 | unsigned long addr, unsigned long data) | |
c658eac6 CZ |
445 | { |
446 | int ret = -EPERM; | |
5ef45079 | 447 | void __user *datap = (void __user *) data; |
c658eac6 CZ |
448 | |
449 | switch (request) { | |
450 | case PTRACE_PEEKTEXT: /* read word at location addr. */ | |
451 | case PTRACE_PEEKDATA: | |
452 | ret = generic_ptrace_peekdata(child, addr, data); | |
453 | break; | |
454 | ||
455 | case PTRACE_PEEKUSR: /* read register specified by addr. */ | |
5ef45079 | 456 | ret = ptrace_peekusr(child, addr, datap); |
c658eac6 CZ |
457 | break; |
458 | ||
459 | case PTRACE_POKETEXT: /* write the word at location addr. */ | |
460 | case PTRACE_POKEDATA: | |
461 | ret = generic_ptrace_pokedata(child, addr, data); | |
462 | break; | |
463 | ||
464 | case PTRACE_POKEUSR: /* write register specified by addr. */ | |
465 | ret = ptrace_pokeusr(child, addr, data); | |
5a0015d6 | 466 | break; |
5a0015d6 | 467 | |
5a0015d6 | 468 | case PTRACE_GETREGS: |
5ef45079 | 469 | ret = ptrace_getregs(child, datap); |
5a0015d6 | 470 | break; |
5a0015d6 CZ |
471 | |
472 | case PTRACE_SETREGS: | |
5ef45079 | 473 | ret = ptrace_setregs(child, datap); |
5a0015d6 | 474 | break; |
5a0015d6 | 475 | |
c658eac6 | 476 | case PTRACE_GETXTREGS: |
5ef45079 | 477 | ret = ptrace_getxregs(child, datap); |
5a0015d6 | 478 | break; |
5a0015d6 | 479 | |
c658eac6 | 480 | case PTRACE_SETXTREGS: |
5ef45079 | 481 | ret = ptrace_setxregs(child, datap); |
5a0015d6 | 482 | break; |
c91e02bd MF |
483 | #ifdef CONFIG_HAVE_HW_BREAKPOINT |
484 | case PTRACE_GETHBPREGS: | |
485 | ret = ptrace_gethbpregs(child, addr, datap); | |
486 | break; | |
5a0015d6 | 487 | |
c91e02bd MF |
488 | case PTRACE_SETHBPREGS: |
489 | ret = ptrace_sethbpregs(child, addr, datap); | |
490 | break; | |
491 | #endif | |
5a0015d6 CZ |
492 | default: |
493 | ret = ptrace_request(child, request, addr, data); | |
c658eac6 | 494 | break; |
5a0015d6 | 495 | } |
c658eac6 | 496 | |
5a0015d6 CZ |
497 | return ret; |
498 | } | |
499 | ||
f984409a | 500 | unsigned long do_syscall_trace_enter(struct pt_regs *regs) |
fc4fb2ad | 501 | { |
1b47a683 | 502 | if (test_thread_flag(TIF_SYSCALL_TRACE) && |
f984409a MF |
503 | tracehook_report_syscall_entry(regs)) |
504 | return -1; | |
505 | ||
506 | return regs->areg[2]; | |
fc4fb2ad CZ |
507 | } |
508 | ||
509 | void do_syscall_trace_leave(struct pt_regs *regs) | |
510 | { | |
f984409a MF |
511 | int step; |
512 | ||
513 | step = test_thread_flag(TIF_SINGLESTEP); | |
514 | ||
515 | if (step || test_thread_flag(TIF_SYSCALL_TRACE)) | |
516 | tracehook_report_syscall_exit(regs, step); | |
fc4fb2ad | 517 | } |