diff options
| -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 | 83 | ||||
| -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 | 1 |
7 files changed, 187 insertions, 4 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..7a8a193b5144 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; |
| @@ -224,6 +229,84 @@ int xfpregs_set(struct task_struct *target, const struct user_regset *regset, | |||
| 224 | return ret; | 229 | return ret; |
| 225 | } | 230 | } |
| 226 | 231 | ||
| 232 | int xstateregs_get(struct task_struct *target, const struct user_regset *regset, | ||
| 233 | unsigned int pos, unsigned int count, | ||
| 234 | void *kbuf, void __user *ubuf) | ||
| 235 | { | ||
| 236 | int ret; | ||
| 237 | |||
| 238 | if (!cpu_has_xsave) | ||
| 239 | return -ENODEV; | ||
| 240 | |||
| 241 | ret = init_fpu(target); | ||
| 242 | if (ret) | ||
| 243 | return ret; | ||
| 244 | |||
| 245 | /* | ||
| 246 | * First copy the fxsave bytes 0..463. | ||
| 247 | */ | ||
| 248 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | ||
| 249 | &target->thread.xstate->xsave, 0, | ||
| 250 | offsetof(struct user_xstateregs, | ||
| 251 | i387.xstate_fx_sw)); | ||
| 252 | if (ret) | ||
| 253 | return ret; | ||
| 254 | |||
| 255 | /* | ||
| 256 | * Copy the 48bytes defined by software. | ||
| 257 | */ | ||
| 258 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | ||
| 259 | xstate_fx_sw_bytes, | ||
| 260 | offsetof(struct user_xstateregs, | ||
| 261 | i387.xstate_fx_sw), | ||
| 262 | offsetof(struct user_xstateregs, | ||
| 263 | xsave_hdr)); | ||
| 264 | if (ret) | ||
| 265 | return ret; | ||
| 266 | |||
| 267 | /* | ||
| 268 | * Copy the rest of xstate memory layout. | ||
| 269 | */ | ||
| 270 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | ||
| 271 | &target->thread.xstate->xsave.xsave_hdr, | ||
| 272 | offsetof(struct user_xstateregs, | ||
| 273 | xsave_hdr), -1); | ||
| 274 | return ret; | ||
| 275 | } | ||
| 276 | |||
| 277 | int xstateregs_set(struct task_struct *target, const struct user_regset *regset, | ||
| 278 | unsigned int pos, unsigned int count, | ||
| 279 | const void *kbuf, const void __user *ubuf) | ||
| 280 | { | ||
| 281 | int ret; | ||
| 282 | struct xsave_hdr_struct *xsave_hdr; | ||
| 283 | |||
| 284 | if (!cpu_has_xsave) | ||
| 285 | return -ENODEV; | ||
| 286 | |||
| 287 | ret = init_fpu(target); | ||
| 288 | if (ret) | ||
| 289 | return ret; | ||
| 290 | |||
| 291 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | ||
| 292 | &target->thread.xstate->xsave, 0, -1); | ||
| 293 | |||
| 294 | /* | ||
| 295 | * mxcsr reserved bits must be masked to zero for security reasons. | ||
| 296 | */ | ||
| 297 | target->thread.xstate->fxsave.mxcsr &= mxcsr_feature_mask; | ||
| 298 | |||
| 299 | xsave_hdr = &target->thread.xstate->xsave.xsave_hdr; | ||
| 300 | |||
| 301 | xsave_hdr->xstate_bv &= pcntxt_mask; | ||
| 302 | /* | ||
| 303 | * These bits must be zero. | ||
| 304 | */ | ||
| 305 | xsave_hdr->reserved1[0] = xsave_hdr->reserved1[1] = 0; | ||
| 306 | |||
| 307 | return ret; | ||
| 308 | } | ||
| 309 | |||
| 227 | #if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION | 310 | #if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION |
| 228 | 311 | ||
| 229 | /* | 312 | /* |
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c index 017d937639fe..16433a59b396 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 | }; |
| @@ -1584,7 +1585,7 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, | |||
| 1584 | 1585 | ||
| 1585 | #ifdef CONFIG_X86_64 | 1586 | #ifdef CONFIG_X86_64 |
| 1586 | 1587 | ||
| 1587 | static const struct user_regset x86_64_regsets[] = { | 1588 | static struct user_regset x86_64_regsets[] __read_mostly = { |
| 1588 | [REGSET_GENERAL] = { | 1589 | [REGSET_GENERAL] = { |
| 1589 | .core_note_type = NT_PRSTATUS, | 1590 | .core_note_type = NT_PRSTATUS, |
| 1590 | .n = sizeof(struct user_regs_struct) / sizeof(long), | 1591 | .n = sizeof(struct user_regs_struct) / sizeof(long), |
| @@ -1597,6 +1598,12 @@ static const struct user_regset x86_64_regsets[] = { | |||
| 1597 | .size = sizeof(long), .align = sizeof(long), | 1598 | .size = sizeof(long), .align = sizeof(long), |
| 1598 | .active = xfpregs_active, .get = xfpregs_get, .set = xfpregs_set | 1599 | .active = xfpregs_active, .get = xfpregs_get, .set = xfpregs_set |
| 1599 | }, | 1600 | }, |
| 1601 | [REGSET_XSTATE] = { | ||
| 1602 | .core_note_type = NT_X86_XSTATE, | ||
| 1603 | .size = sizeof(u64), .align = sizeof(u64), | ||
| 1604 | .active = xstateregs_active, .get = xstateregs_get, | ||
| 1605 | .set = xstateregs_set | ||
| 1606 | }, | ||
| 1600 | [REGSET_IOPERM64] = { | 1607 | [REGSET_IOPERM64] = { |
| 1601 | .core_note_type = NT_386_IOPERM, | 1608 | .core_note_type = NT_386_IOPERM, |
| 1602 | .n = IO_BITMAP_LONGS, | 1609 | .n = IO_BITMAP_LONGS, |
| @@ -1622,7 +1629,7 @@ static const struct user_regset_view user_x86_64_view = { | |||
| 1622 | #endif /* CONFIG_X86_64 */ | 1629 | #endif /* CONFIG_X86_64 */ |
| 1623 | 1630 | ||
| 1624 | #if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION | 1631 | #if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION |
| 1625 | static const struct user_regset x86_32_regsets[] = { | 1632 | static struct user_regset x86_32_regsets[] __read_mostly = { |
| 1626 | [REGSET_GENERAL] = { | 1633 | [REGSET_GENERAL] = { |
| 1627 | .core_note_type = NT_PRSTATUS, | 1634 | .core_note_type = NT_PRSTATUS, |
| 1628 | .n = sizeof(struct user_regs_struct32) / sizeof(u32), | 1635 | .n = sizeof(struct user_regs_struct32) / sizeof(u32), |
| @@ -1641,6 +1648,12 @@ static const struct user_regset x86_32_regsets[] = { | |||
| 1641 | .size = sizeof(u32), .align = sizeof(u32), | 1648 | .size = sizeof(u32), .align = sizeof(u32), |
| 1642 | .active = xfpregs_active, .get = xfpregs_get, .set = xfpregs_set | 1649 | .active = xfpregs_active, .get = xfpregs_get, .set = xfpregs_set |
| 1643 | }, | 1650 | }, |
| 1651 | [REGSET_XSTATE] = { | ||
| 1652 | .core_note_type = NT_X86_XSTATE, | ||
| 1653 | .size = sizeof(u64), .align = sizeof(u64), | ||
| 1654 | .active = xstateregs_active, .get = xstateregs_get, | ||
| 1655 | .set = xstateregs_set | ||
| 1656 | }, | ||
| 1644 | [REGSET_TLS] = { | 1657 | [REGSET_TLS] = { |
| 1645 | .core_note_type = NT_386_TLS, | 1658 | .core_note_type = NT_386_TLS, |
| 1646 | .n = GDT_ENTRY_TLS_ENTRIES, .bias = GDT_ENTRY_TLS_MIN, | 1659 | .n = GDT_ENTRY_TLS_ENTRIES, .bias = GDT_ENTRY_TLS_MIN, |
| @@ -1663,6 +1676,23 @@ static const struct user_regset_view user_x86_32_view = { | |||
| 1663 | }; | 1676 | }; |
| 1664 | #endif | 1677 | #endif |
| 1665 | 1678 | ||
| 1679 | /* | ||
| 1680 | * This represents bytes 464..511 in the memory layout exported through | ||
| 1681 | * the REGSET_XSTATE interface. | ||
| 1682 | */ | ||
| 1683 | u64 xstate_fx_sw_bytes[USER_XSTATE_FX_SW_WORDS]; | ||
| 1684 | |||
| 1685 | void update_regset_xstate_info(unsigned int size, u64 xstate_mask) | ||
| 1686 | { | ||
| 1687 | #ifdef CONFIG_X86_64 | ||
| 1688 | x86_64_regsets[REGSET_XSTATE].n = size / sizeof(u64); | ||
| 1689 | #endif | ||
| 1690 | #if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION | ||
| 1691 | x86_32_regsets[REGSET_XSTATE].n = size / sizeof(u64); | ||
| 1692 | #endif | ||
| 1693 | xstate_fx_sw_bytes[USER_XSTATE_XCR0_WORD] = xstate_mask; | ||
| 1694 | } | ||
| 1695 | |||
| 1666 | const struct user_regset_view *task_user_regset_view(struct task_struct *task) | 1696 | const struct user_regset_view *task_user_regset_view(struct task_struct *task) |
| 1667 | { | 1697 | { |
| 1668 | #ifdef CONFIG_IA32_EMULATION | 1698 | #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 0cc4d55151b7..a8c4af073ce9 100644 --- a/include/linux/elf.h +++ b/include/linux/elf.h | |||
| @@ -361,6 +361,7 @@ typedef struct elf64_shdr { | |||
| 361 | #define NT_PPC_VSX 0x102 /* PowerPC VSX registers */ | 361 | #define NT_PPC_VSX 0x102 /* PowerPC VSX registers */ |
| 362 | #define NT_386_TLS 0x200 /* i386 TLS slots (struct user_desc) */ | 362 | #define NT_386_TLS 0x200 /* i386 TLS slots (struct user_desc) */ |
| 363 | #define NT_386_IOPERM 0x201 /* x86 io permission bitmap (1=deny) */ | 363 | #define NT_386_IOPERM 0x201 /* x86 io permission bitmap (1=deny) */ |
| 364 | #define NT_X86_XSTATE 0x202 /* x86 extended state using xsave */ | ||
| 364 | #define NT_S390_HIGH_GPRS 0x300 /* s390 upper register halves */ | 365 | #define NT_S390_HIGH_GPRS 0x300 /* s390 upper register halves */ |
| 365 | 366 | ||
| 366 | 367 | ||
