diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-02-28 13:59:44 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-02-28 13:59:44 -0500 |
commit | 6f5621cb160fada5bc52c2961ddc86e69e478912 (patch) | |
tree | 921577f89b2bbc69813eabfd21ceedb4d7200133 | |
parent | c7e15899d07c9813c1aa96b21699d2d9c8314c4b (diff) | |
parent | 6dbbe14f21368a45aedba7eab0221857b8ad8d16 (diff) |
Merge branch 'x86-ptrace-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip
* 'x86-ptrace-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip:
x86, ptrace: Remove set_stopped_child_used_math() in [x]fpregs_set
x86, ptrace: Simplify xstateregs_get()
ptrace: Fix ptrace_regset() comments and diagnose errors specifically
parisc: Disable CONFIG_HAVE_ARCH_TRACEHOOK
ptrace: Add support for generic PTRACE_GETREGSET/PTRACE_SETREGSET
x86, ptrace: regset extensions to support xstate
-rw-r--r-- | arch/x86/include/asm/i387.h | 12 | ||||
-rw-r--r-- | arch/x86/include/asm/user.h | 58 | ||||
-rw-r--r-- | arch/x86/include/asm/xsave.h | 2 | ||||
-rw-r--r-- | arch/x86/kernel/i387.c | 71 | ||||
-rw-r--r-- | arch/x86/kernel/ptrace.c | 34 | ||||
-rw-r--r-- | arch/x86/kernel/xsave.c | 1 | ||||
-rw-r--r-- | include/linux/elf.h | 7 | ||||
-rw-r--r-- | include/linux/ptrace.h | 20 | ||||
-rw-r--r-- | kernel/ptrace.c | 88 |
9 files changed, 284 insertions, 9 deletions
diff --git a/arch/x86/include/asm/i387.h b/arch/x86/include/asm/i387.h index ebfb8a9e11f7..da2930924501 100644 --- a/arch/x86/include/asm/i387.h +++ b/arch/x86/include/asm/i387.h | |||
@@ -33,8 +33,16 @@ extern void init_thread_xstate(void); | |||
33 | extern int dump_fpu(struct pt_regs *, struct user_i387_struct *); | 33 | extern int dump_fpu(struct pt_regs *, struct user_i387_struct *); |
34 | 34 | ||
35 | extern user_regset_active_fn fpregs_active, xfpregs_active; | 35 | extern user_regset_active_fn fpregs_active, xfpregs_active; |
36 | extern user_regset_get_fn fpregs_get, xfpregs_get, fpregs_soft_get; | 36 | extern user_regset_get_fn fpregs_get, xfpregs_get, fpregs_soft_get, |
37 | extern user_regset_set_fn fpregs_set, xfpregs_set, fpregs_soft_set; | 37 | xstateregs_get; |
38 | extern user_regset_set_fn fpregs_set, xfpregs_set, fpregs_soft_set, | ||
39 | xstateregs_set; | ||
40 | |||
41 | /* | ||
42 | * xstateregs_active == fpregs_active. Please refer to the comment | ||
43 | * at the definition of fpregs_active. | ||
44 | */ | ||
45 | #define xstateregs_active fpregs_active | ||
38 | 46 | ||
39 | extern struct _fpx_sw_bytes fx_sw_reserved; | 47 | extern struct _fpx_sw_bytes fx_sw_reserved; |
40 | #ifdef CONFIG_IA32_EMULATION | 48 | #ifdef CONFIG_IA32_EMULATION |
diff --git a/arch/x86/include/asm/user.h b/arch/x86/include/asm/user.h index 999873b22e7f..24532c7da3d6 100644 --- a/arch/x86/include/asm/user.h +++ b/arch/x86/include/asm/user.h | |||
@@ -1,5 +1,63 @@ | |||
1 | #ifndef _ASM_X86_USER_H | ||
2 | #define _ASM_X86_USER_H | ||
3 | |||
1 | #ifdef CONFIG_X86_32 | 4 | #ifdef CONFIG_X86_32 |
2 | # include "user_32.h" | 5 | # include "user_32.h" |
3 | #else | 6 | #else |
4 | # include "user_64.h" | 7 | # include "user_64.h" |
5 | #endif | 8 | #endif |
9 | |||
10 | #include <asm/types.h> | ||
11 | |||
12 | struct user_ymmh_regs { | ||
13 | /* 16 * 16 bytes for each YMMH-reg */ | ||
14 | __u32 ymmh_space[64]; | ||
15 | }; | ||
16 | |||
17 | struct user_xsave_hdr { | ||
18 | __u64 xstate_bv; | ||
19 | __u64 reserved1[2]; | ||
20 | __u64 reserved2[5]; | ||
21 | }; | ||
22 | |||
23 | /* | ||
24 | * The structure layout of user_xstateregs, used for exporting the | ||
25 | * extended register state through ptrace and core-dump (NT_X86_XSTATE note) | ||
26 | * interfaces will be same as the memory layout of xsave used by the processor | ||
27 | * (except for the bytes 464..511, which can be used by the software) and hence | ||
28 | * the size of this structure varies depending on the features supported by the | ||
29 | * processor and OS. The size of the structure that users need to use can be | ||
30 | * obtained by doing: | ||
31 | * cpuid_count(0xd, 0, &eax, &ptrace_xstateregs_struct_size, &ecx, &edx); | ||
32 | * i.e., cpuid.(eax=0xd,ecx=0).ebx will be the size that user (debuggers, etc.) | ||
33 | * need to use. | ||
34 | * | ||
35 | * For now, only the first 8 bytes of the software usable bytes[464..471] will | ||
36 | * be used and will be set to OS enabled xstate mask (which is same as the | ||
37 | * 64bit mask returned by the xgetbv's xCR0). Users (analyzing core dump | ||
38 | * remotely, etc.) can use this mask as well as the mask saved in the | ||
39 | * xstate_hdr bytes and interpret what states the processor/OS supports | ||
40 | * and what states are in modified/initialized conditions for the | ||
41 | * particular process/thread. | ||
42 | * | ||
43 | * Also when the user modifies certain state FP/SSE/etc through the | ||
44 | * ptrace interface, they must ensure that the xsave_hdr.xstate_bv | ||
45 | * bytes[512..519] of the memory layout are updated correspondingly. | ||
46 | * i.e., for example when FP state is modified to a non-init state, | ||
47 | * xsave_hdr.xstate_bv's bit 0 must be set to '1', when SSE is modified to | ||
48 | * non-init state, xsave_hdr.xstate_bv's bit 1 must to be set to '1', etc. | ||
49 | */ | ||
50 | #define USER_XSTATE_FX_SW_WORDS 6 | ||
51 | #define USER_XSTATE_XCR0_WORD 0 | ||
52 | |||
53 | struct user_xstateregs { | ||
54 | struct { | ||
55 | __u64 fpx_space[58]; | ||
56 | __u64 xstate_fx_sw[USER_XSTATE_FX_SW_WORDS]; | ||
57 | } i387; | ||
58 | struct user_xsave_hdr xsave_hdr; | ||
59 | struct user_ymmh_regs ymmh; | ||
60 | /* further processor state extensions go here */ | ||
61 | }; | ||
62 | |||
63 | #endif /* _ASM_X86_USER_H */ | ||
diff --git a/arch/x86/include/asm/xsave.h b/arch/x86/include/asm/xsave.h index 727acc152344..ddc04ccad03b 100644 --- a/arch/x86/include/asm/xsave.h +++ b/arch/x86/include/asm/xsave.h | |||
@@ -27,9 +27,11 @@ | |||
27 | extern unsigned int xstate_size; | 27 | extern unsigned int xstate_size; |
28 | extern u64 pcntxt_mask; | 28 | extern u64 pcntxt_mask; |
29 | extern struct xsave_struct *init_xstate_buf; | 29 | extern struct xsave_struct *init_xstate_buf; |
30 | extern u64 xstate_fx_sw_bytes[USER_XSTATE_FX_SW_WORDS]; | ||
30 | 31 | ||
31 | extern void xsave_cntxt_init(void); | 32 | extern void xsave_cntxt_init(void); |
32 | extern void xsave_init(void); | 33 | extern void xsave_init(void); |
34 | extern void update_regset_xstate_info(unsigned int size, u64 xstate_mask); | ||
33 | extern int init_fpu(struct task_struct *child); | 35 | extern int init_fpu(struct task_struct *child); |
34 | extern int check_for_xstate(struct i387_fxsave_struct __user *buf, | 36 | extern int check_for_xstate(struct i387_fxsave_struct __user *buf, |
35 | void __user *fpstate, | 37 | void __user *fpstate, |
diff --git a/arch/x86/kernel/i387.c b/arch/x86/kernel/i387.c index f2f8540a7f3d..c01a2b846d47 100644 --- a/arch/x86/kernel/i387.c +++ b/arch/x86/kernel/i387.c | |||
@@ -164,6 +164,11 @@ int init_fpu(struct task_struct *tsk) | |||
164 | return 0; | 164 | return 0; |
165 | } | 165 | } |
166 | 166 | ||
167 | /* | ||
168 | * The xstateregs_active() routine is the same as the fpregs_active() routine, | ||
169 | * as the "regset->n" for the xstate regset will be updated based on the feature | ||
170 | * capabilites supported by the xsave. | ||
171 | */ | ||
167 | int fpregs_active(struct task_struct *target, const struct user_regset *regset) | 172 | int fpregs_active(struct task_struct *target, const struct user_regset *regset) |
168 | { | 173 | { |
169 | return tsk_used_math(target) ? regset->n : 0; | 174 | return tsk_used_math(target) ? regset->n : 0; |
@@ -204,8 +209,6 @@ int xfpregs_set(struct task_struct *target, const struct user_regset *regset, | |||
204 | if (ret) | 209 | if (ret) |
205 | return ret; | 210 | return ret; |
206 | 211 | ||
207 | set_stopped_child_used_math(target); | ||
208 | |||
209 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | 212 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, |
210 | &target->thread.xstate->fxsave, 0, -1); | 213 | &target->thread.xstate->fxsave, 0, -1); |
211 | 214 | ||
@@ -224,6 +227,68 @@ int xfpregs_set(struct task_struct *target, const struct user_regset *regset, | |||
224 | return ret; | 227 | return ret; |
225 | } | 228 | } |
226 | 229 | ||
230 | int xstateregs_get(struct task_struct *target, const struct user_regset *regset, | ||
231 | unsigned int pos, unsigned int count, | ||
232 | void *kbuf, void __user *ubuf) | ||
233 | { | ||
234 | int ret; | ||
235 | |||
236 | if (!cpu_has_xsave) | ||
237 | return -ENODEV; | ||
238 | |||
239 | ret = init_fpu(target); | ||
240 | if (ret) | ||
241 | return ret; | ||
242 | |||
243 | /* | ||
244 | * Copy the 48bytes defined by the software first into the xstate | ||
245 | * memory layout in the thread struct, so that we can copy the entire | ||
246 | * xstateregs to the user using one user_regset_copyout(). | ||
247 | */ | ||
248 | memcpy(&target->thread.xstate->fxsave.sw_reserved, | ||
249 | xstate_fx_sw_bytes, sizeof(xstate_fx_sw_bytes)); | ||
250 | |||
251 | /* | ||
252 | * Copy the xstate memory layout. | ||
253 | */ | ||
254 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | ||
255 | &target->thread.xstate->xsave, 0, -1); | ||
256 | return ret; | ||
257 | } | ||
258 | |||
259 | int xstateregs_set(struct task_struct *target, const struct user_regset *regset, | ||
260 | unsigned int pos, unsigned int count, | ||
261 | const void *kbuf, const void __user *ubuf) | ||
262 | { | ||
263 | int ret; | ||
264 | struct xsave_hdr_struct *xsave_hdr; | ||
265 | |||
266 | if (!cpu_has_xsave) | ||
267 | return -ENODEV; | ||
268 | |||
269 | ret = init_fpu(target); | ||
270 | if (ret) | ||
271 | return ret; | ||
272 | |||
273 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | ||
274 | &target->thread.xstate->xsave, 0, -1); | ||
275 | |||
276 | /* | ||
277 | * mxcsr reserved bits must be masked to zero for security reasons. | ||
278 | */ | ||
279 | target->thread.xstate->fxsave.mxcsr &= mxcsr_feature_mask; | ||
280 | |||
281 | xsave_hdr = &target->thread.xstate->xsave.xsave_hdr; | ||
282 | |||
283 | xsave_hdr->xstate_bv &= pcntxt_mask; | ||
284 | /* | ||
285 | * These bits must be zero. | ||
286 | */ | ||
287 | xsave_hdr->reserved1[0] = xsave_hdr->reserved1[1] = 0; | ||
288 | |||
289 | return ret; | ||
290 | } | ||
291 | |||
227 | #if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION | 292 | #if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION |
228 | 293 | ||
229 | /* | 294 | /* |
@@ -404,8 +469,6 @@ int fpregs_set(struct task_struct *target, const struct user_regset *regset, | |||
404 | if (ret) | 469 | if (ret) |
405 | return ret; | 470 | return ret; |
406 | 471 | ||
407 | set_stopped_child_used_math(target); | ||
408 | |||
409 | if (!HAVE_HWFP) | 472 | if (!HAVE_HWFP) |
410 | return fpregs_soft_set(target, regset, pos, count, kbuf, ubuf); | 473 | return fpregs_soft_set(target, regset, pos, count, kbuf, ubuf); |
411 | 474 | ||
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c index d03146f71b2f..2d96aab82a48 100644 --- a/arch/x86/kernel/ptrace.c +++ b/arch/x86/kernel/ptrace.c | |||
@@ -48,6 +48,7 @@ enum x86_regset { | |||
48 | REGSET_FP, | 48 | REGSET_FP, |
49 | REGSET_XFP, | 49 | REGSET_XFP, |
50 | REGSET_IOPERM64 = REGSET_XFP, | 50 | REGSET_IOPERM64 = REGSET_XFP, |
51 | REGSET_XSTATE, | ||
51 | REGSET_TLS, | 52 | REGSET_TLS, |
52 | REGSET_IOPERM32, | 53 | REGSET_IOPERM32, |
53 | }; | 54 | }; |
@@ -1563,7 +1564,7 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, | |||
1563 | 1564 | ||
1564 | #ifdef CONFIG_X86_64 | 1565 | #ifdef CONFIG_X86_64 |
1565 | 1566 | ||
1566 | static const struct user_regset x86_64_regsets[] = { | 1567 | static struct user_regset x86_64_regsets[] __read_mostly = { |
1567 | [REGSET_GENERAL] = { | 1568 | [REGSET_GENERAL] = { |
1568 | .core_note_type = NT_PRSTATUS, | 1569 | .core_note_type = NT_PRSTATUS, |
1569 | .n = sizeof(struct user_regs_struct) / sizeof(long), | 1570 | .n = sizeof(struct user_regs_struct) / sizeof(long), |
@@ -1576,6 +1577,12 @@ static const struct user_regset x86_64_regsets[] = { | |||
1576 | .size = sizeof(long), .align = sizeof(long), | 1577 | .size = sizeof(long), .align = sizeof(long), |
1577 | .active = xfpregs_active, .get = xfpregs_get, .set = xfpregs_set | 1578 | .active = xfpregs_active, .get = xfpregs_get, .set = xfpregs_set |
1578 | }, | 1579 | }, |
1580 | [REGSET_XSTATE] = { | ||
1581 | .core_note_type = NT_X86_XSTATE, | ||
1582 | .size = sizeof(u64), .align = sizeof(u64), | ||
1583 | .active = xstateregs_active, .get = xstateregs_get, | ||
1584 | .set = xstateregs_set | ||
1585 | }, | ||
1579 | [REGSET_IOPERM64] = { | 1586 | [REGSET_IOPERM64] = { |
1580 | .core_note_type = NT_386_IOPERM, | 1587 | .core_note_type = NT_386_IOPERM, |
1581 | .n = IO_BITMAP_LONGS, | 1588 | .n = IO_BITMAP_LONGS, |
@@ -1601,7 +1608,7 @@ static const struct user_regset_view user_x86_64_view = { | |||
1601 | #endif /* CONFIG_X86_64 */ | 1608 | #endif /* CONFIG_X86_64 */ |
1602 | 1609 | ||
1603 | #if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION | 1610 | #if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION |
1604 | static const struct user_regset x86_32_regsets[] = { | 1611 | static struct user_regset x86_32_regsets[] __read_mostly = { |
1605 | [REGSET_GENERAL] = { | 1612 | [REGSET_GENERAL] = { |
1606 | .core_note_type = NT_PRSTATUS, | 1613 | .core_note_type = NT_PRSTATUS, |
1607 | .n = sizeof(struct user_regs_struct32) / sizeof(u32), | 1614 | .n = sizeof(struct user_regs_struct32) / sizeof(u32), |
@@ -1620,6 +1627,12 @@ static const struct user_regset x86_32_regsets[] = { | |||
1620 | .size = sizeof(u32), .align = sizeof(u32), | 1627 | .size = sizeof(u32), .align = sizeof(u32), |
1621 | .active = xfpregs_active, .get = xfpregs_get, .set = xfpregs_set | 1628 | .active = xfpregs_active, .get = xfpregs_get, .set = xfpregs_set |
1622 | }, | 1629 | }, |
1630 | [REGSET_XSTATE] = { | ||
1631 | .core_note_type = NT_X86_XSTATE, | ||
1632 | .size = sizeof(u64), .align = sizeof(u64), | ||
1633 | .active = xstateregs_active, .get = xstateregs_get, | ||
1634 | .set = xstateregs_set | ||
1635 | }, | ||
1623 | [REGSET_TLS] = { | 1636 | [REGSET_TLS] = { |
1624 | .core_note_type = NT_386_TLS, | 1637 | .core_note_type = NT_386_TLS, |
1625 | .n = GDT_ENTRY_TLS_ENTRIES, .bias = GDT_ENTRY_TLS_MIN, | 1638 | .n = GDT_ENTRY_TLS_ENTRIES, .bias = GDT_ENTRY_TLS_MIN, |
@@ -1642,6 +1655,23 @@ static const struct user_regset_view user_x86_32_view = { | |||
1642 | }; | 1655 | }; |
1643 | #endif | 1656 | #endif |
1644 | 1657 | ||
1658 | /* | ||
1659 | * This represents bytes 464..511 in the memory layout exported through | ||
1660 | * the REGSET_XSTATE interface. | ||
1661 | */ | ||
1662 | u64 xstate_fx_sw_bytes[USER_XSTATE_FX_SW_WORDS]; | ||
1663 | |||
1664 | void update_regset_xstate_info(unsigned int size, u64 xstate_mask) | ||
1665 | { | ||
1666 | #ifdef CONFIG_X86_64 | ||
1667 | x86_64_regsets[REGSET_XSTATE].n = size / sizeof(u64); | ||
1668 | #endif | ||
1669 | #if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION | ||
1670 | x86_32_regsets[REGSET_XSTATE].n = size / sizeof(u64); | ||
1671 | #endif | ||
1672 | xstate_fx_sw_bytes[USER_XSTATE_XCR0_WORD] = xstate_mask; | ||
1673 | } | ||
1674 | |||
1645 | const struct user_regset_view *task_user_regset_view(struct task_struct *task) | 1675 | const struct user_regset_view *task_user_regset_view(struct task_struct *task) |
1646 | { | 1676 | { |
1647 | #ifdef CONFIG_IA32_EMULATION | 1677 | #ifdef CONFIG_IA32_EMULATION |
diff --git a/arch/x86/kernel/xsave.c b/arch/x86/kernel/xsave.c index c5ee17e8c6d9..782c3a362ec6 100644 --- a/arch/x86/kernel/xsave.c +++ b/arch/x86/kernel/xsave.c | |||
@@ -337,6 +337,7 @@ void __ref xsave_cntxt_init(void) | |||
337 | cpuid_count(0xd, 0, &eax, &ebx, &ecx, &edx); | 337 | cpuid_count(0xd, 0, &eax, &ebx, &ecx, &edx); |
338 | xstate_size = ebx; | 338 | xstate_size = ebx; |
339 | 339 | ||
340 | update_regset_xstate_info(xstate_size, pcntxt_mask); | ||
340 | prepare_fx_sw_frame(); | 341 | prepare_fx_sw_frame(); |
341 | 342 | ||
342 | setup_xstate_init(); | 343 | setup_xstate_init(); |
diff --git a/include/linux/elf.h b/include/linux/elf.h index 39ad4b230a4a..ad990c5f63f6 100644 --- a/include/linux/elf.h +++ b/include/linux/elf.h | |||
@@ -349,7 +349,11 @@ typedef struct elf64_shdr { | |||
349 | #define ELF_OSABI ELFOSABI_NONE | 349 | #define ELF_OSABI ELFOSABI_NONE |
350 | #endif | 350 | #endif |
351 | 351 | ||
352 | /* Notes used in ET_CORE */ | 352 | /* |
353 | * Notes used in ET_CORE. Architectures export some of the arch register sets | ||
354 | * using the corresponding note types via the PTRACE_GETREGSET and | ||
355 | * PTRACE_SETREGSET requests. | ||
356 | */ | ||
353 | #define NT_PRSTATUS 1 | 357 | #define NT_PRSTATUS 1 |
354 | #define NT_PRFPREG 2 | 358 | #define NT_PRFPREG 2 |
355 | #define NT_PRPSINFO 3 | 359 | #define NT_PRPSINFO 3 |
@@ -361,6 +365,7 @@ typedef struct elf64_shdr { | |||
361 | #define NT_PPC_VSX 0x102 /* PowerPC VSX registers */ | 365 | #define NT_PPC_VSX 0x102 /* PowerPC VSX registers */ |
362 | #define NT_386_TLS 0x200 /* i386 TLS slots (struct user_desc) */ | 366 | #define NT_386_TLS 0x200 /* i386 TLS slots (struct user_desc) */ |
363 | #define NT_386_IOPERM 0x201 /* x86 io permission bitmap (1=deny) */ | 367 | #define NT_386_IOPERM 0x201 /* x86 io permission bitmap (1=deny) */ |
368 | #define NT_X86_XSTATE 0x202 /* x86 extended state using xsave */ | ||
364 | #define NT_S390_HIGH_GPRS 0x300 /* s390 upper register halves */ | 369 | #define NT_S390_HIGH_GPRS 0x300 /* s390 upper register halves */ |
365 | #define NT_S390_TIMER 0x301 /* s390 timer register */ | 370 | #define NT_S390_TIMER 0x301 /* s390 timer register */ |
366 | #define NT_S390_TODCMP 0x302 /* s390 TOD clock comparator register */ | 371 | #define NT_S390_TODCMP 0x302 /* s390 TOD clock comparator register */ |
diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h index 56f2d63a5cbb..c5eab89da51e 100644 --- a/include/linux/ptrace.h +++ b/include/linux/ptrace.h | |||
@@ -27,6 +27,26 @@ | |||
27 | #define PTRACE_GETSIGINFO 0x4202 | 27 | #define PTRACE_GETSIGINFO 0x4202 |
28 | #define PTRACE_SETSIGINFO 0x4203 | 28 | #define PTRACE_SETSIGINFO 0x4203 |
29 | 29 | ||
30 | /* | ||
31 | * Generic ptrace interface that exports the architecture specific regsets | ||
32 | * using the corresponding NT_* types (which are also used in the core dump). | ||
33 | * Please note that the NT_PRSTATUS note type in a core dump contains a full | ||
34 | * 'struct elf_prstatus'. But the user_regset for NT_PRSTATUS contains just the | ||
35 | * elf_gregset_t that is the pr_reg field of 'struct elf_prstatus'. For all the | ||
36 | * other user_regset flavors, the user_regset layout and the ELF core dump note | ||
37 | * payload are exactly the same layout. | ||
38 | * | ||
39 | * This interface usage is as follows: | ||
40 | * struct iovec iov = { buf, len}; | ||
41 | * | ||
42 | * ret = ptrace(PTRACE_GETREGSET/PTRACE_SETREGSET, pid, NT_XXX_TYPE, &iov); | ||
43 | * | ||
44 | * On the successful completion, iov.len will be updated by the kernel, | ||
45 | * specifying how much the kernel has written/read to/from the user's iov.buf. | ||
46 | */ | ||
47 | #define PTRACE_GETREGSET 0x4204 | ||
48 | #define PTRACE_SETREGSET 0x4205 | ||
49 | |||
30 | /* options set using PTRACE_SETOPTIONS */ | 50 | /* options set using PTRACE_SETOPTIONS */ |
31 | #define PTRACE_O_TRACESYSGOOD 0x00000001 | 51 | #define PTRACE_O_TRACESYSGOOD 0x00000001 |
32 | #define PTRACE_O_TRACEFORK 0x00000002 | 52 | #define PTRACE_O_TRACEFORK 0x00000002 |
diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 23bd09cd042e..42ad8ae729a0 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include <linux/pid_namespace.h> | 22 | #include <linux/pid_namespace.h> |
23 | #include <linux/syscalls.h> | 23 | #include <linux/syscalls.h> |
24 | #include <linux/uaccess.h> | 24 | #include <linux/uaccess.h> |
25 | #include <linux/regset.h> | ||
25 | 26 | ||
26 | 27 | ||
27 | /* | 28 | /* |
@@ -511,6 +512,47 @@ static int ptrace_resume(struct task_struct *child, long request, long data) | |||
511 | return 0; | 512 | return 0; |
512 | } | 513 | } |
513 | 514 | ||
515 | #ifdef CONFIG_HAVE_ARCH_TRACEHOOK | ||
516 | |||
517 | static const struct user_regset * | ||
518 | find_regset(const struct user_regset_view *view, unsigned int type) | ||
519 | { | ||
520 | const struct user_regset *regset; | ||
521 | int n; | ||
522 | |||
523 | for (n = 0; n < view->n; ++n) { | ||
524 | regset = view->regsets + n; | ||
525 | if (regset->core_note_type == type) | ||
526 | return regset; | ||
527 | } | ||
528 | |||
529 | return NULL; | ||
530 | } | ||
531 | |||
532 | static int ptrace_regset(struct task_struct *task, int req, unsigned int type, | ||
533 | struct iovec *kiov) | ||
534 | { | ||
535 | const struct user_regset_view *view = task_user_regset_view(task); | ||
536 | const struct user_regset *regset = find_regset(view, type); | ||
537 | int regset_no; | ||
538 | |||
539 | if (!regset || (kiov->iov_len % regset->size) != 0) | ||
540 | return -EINVAL; | ||
541 | |||
542 | regset_no = regset - view->regsets; | ||
543 | kiov->iov_len = min(kiov->iov_len, | ||
544 | (__kernel_size_t) (regset->n * regset->size)); | ||
545 | |||
546 | if (req == PTRACE_GETREGSET) | ||
547 | return copy_regset_to_user(task, view, regset_no, 0, | ||
548 | kiov->iov_len, kiov->iov_base); | ||
549 | else | ||
550 | return copy_regset_from_user(task, view, regset_no, 0, | ||
551 | kiov->iov_len, kiov->iov_base); | ||
552 | } | ||
553 | |||
554 | #endif | ||
555 | |||
514 | int ptrace_request(struct task_struct *child, long request, | 556 | int ptrace_request(struct task_struct *child, long request, |
515 | long addr, long data) | 557 | long addr, long data) |
516 | { | 558 | { |
@@ -573,6 +615,26 @@ int ptrace_request(struct task_struct *child, long request, | |||
573 | return 0; | 615 | return 0; |
574 | return ptrace_resume(child, request, SIGKILL); | 616 | return ptrace_resume(child, request, SIGKILL); |
575 | 617 | ||
618 | #ifdef CONFIG_HAVE_ARCH_TRACEHOOK | ||
619 | case PTRACE_GETREGSET: | ||
620 | case PTRACE_SETREGSET: | ||
621 | { | ||
622 | struct iovec kiov; | ||
623 | struct iovec __user *uiov = (struct iovec __user *) data; | ||
624 | |||
625 | if (!access_ok(VERIFY_WRITE, uiov, sizeof(*uiov))) | ||
626 | return -EFAULT; | ||
627 | |||
628 | if (__get_user(kiov.iov_base, &uiov->iov_base) || | ||
629 | __get_user(kiov.iov_len, &uiov->iov_len)) | ||
630 | return -EFAULT; | ||
631 | |||
632 | ret = ptrace_regset(child, request, addr, &kiov); | ||
633 | if (!ret) | ||
634 | ret = __put_user(kiov.iov_len, &uiov->iov_len); | ||
635 | break; | ||
636 | } | ||
637 | #endif | ||
576 | default: | 638 | default: |
577 | break; | 639 | break; |
578 | } | 640 | } |
@@ -711,6 +773,32 @@ int compat_ptrace_request(struct task_struct *child, compat_long_t request, | |||
711 | else | 773 | else |
712 | ret = ptrace_setsiginfo(child, &siginfo); | 774 | ret = ptrace_setsiginfo(child, &siginfo); |
713 | break; | 775 | break; |
776 | #ifdef CONFIG_HAVE_ARCH_TRACEHOOK | ||
777 | case PTRACE_GETREGSET: | ||
778 | case PTRACE_SETREGSET: | ||
779 | { | ||
780 | struct iovec kiov; | ||
781 | struct compat_iovec __user *uiov = | ||
782 | (struct compat_iovec __user *) datap; | ||
783 | compat_uptr_t ptr; | ||
784 | compat_size_t len; | ||
785 | |||
786 | if (!access_ok(VERIFY_WRITE, uiov, sizeof(*uiov))) | ||
787 | return -EFAULT; | ||
788 | |||
789 | if (__get_user(ptr, &uiov->iov_base) || | ||
790 | __get_user(len, &uiov->iov_len)) | ||
791 | return -EFAULT; | ||
792 | |||
793 | kiov.iov_base = compat_ptr(ptr); | ||
794 | kiov.iov_len = len; | ||
795 | |||
796 | ret = ptrace_regset(child, request, addr, &kiov); | ||
797 | if (!ret) | ||
798 | ret = __put_user(kiov.iov_len, &uiov->iov_len); | ||
799 | break; | ||
800 | } | ||
801 | #endif | ||
714 | 802 | ||
715 | default: | 803 | default: |
716 | ret = ptrace_request(child, request, addr, data); | 804 | ret = ptrace_request(child, request, addr, data); |