diff options
Diffstat (limited to 'arch/x86/um')
-rw-r--r-- | arch/x86/um/Makefile | 2 | ||||
-rw-r--r-- | arch/x86/um/asm/elf.h | 2 | ||||
-rw-r--r-- | arch/x86/um/asm/ptrace.h | 10 | ||||
-rw-r--r-- | arch/x86/um/os-Linux/registers.c | 145 | ||||
-rw-r--r-- | arch/x86/um/ptrace.c | 267 | ||||
-rw-r--r-- | arch/x86/um/ptrace_32.c | 88 | ||||
-rw-r--r-- | arch/x86/um/ptrace_64.c | 37 | ||||
-rw-r--r-- | arch/x86/um/shared/sysdep/ptrace.h | 8 | ||||
-rw-r--r-- | arch/x86/um/shared/sysdep/ptrace_32.h | 4 | ||||
-rw-r--r-- | arch/x86/um/shared/sysdep/ptrace_64.h | 4 | ||||
-rw-r--r-- | arch/x86/um/shared/sysdep/ptrace_user.h | 6 | ||||
-rw-r--r-- | arch/x86/um/signal.c | 322 | ||||
-rw-r--r-- | arch/x86/um/user-offsets.c | 8 |
13 files changed, 449 insertions, 454 deletions
diff --git a/arch/x86/um/Makefile b/arch/x86/um/Makefile index 36e67fc97c22..b42c31cd2390 100644 --- a/arch/x86/um/Makefile +++ b/arch/x86/um/Makefile @@ -10,7 +10,7 @@ else endif obj-y = bugs_$(BITS).o delay.o fault.o \ - ptrace_$(BITS).o ptrace_user.o setjmp_$(BITS).o signal.o \ + ptrace.o ptrace_$(BITS).o ptrace_user.o setjmp_$(BITS).o signal.o \ stub_segv.o \ sys_call_table_$(BITS).o sysrq_$(BITS).o tls_$(BITS).o \ mem_$(BITS).o subarch.o os-Linux/ diff --git a/arch/x86/um/asm/elf.h b/arch/x86/um/asm/elf.h index 6052200fe925..62ed5d68a978 100644 --- a/arch/x86/um/asm/elf.h +++ b/arch/x86/um/asm/elf.h @@ -8,6 +8,8 @@ #include <asm/user.h> #include <skas.h> +#define CORE_DUMP_USE_REGSET + #ifdef CONFIG_X86_32 #define R_386_NONE 0 diff --git a/arch/x86/um/asm/ptrace.h b/arch/x86/um/asm/ptrace.h index 2fef3da55533..2641d28d115c 100644 --- a/arch/x86/um/asm/ptrace.h +++ b/arch/x86/um/asm/ptrace.h @@ -2,6 +2,16 @@ #ifndef __UM_X86_PTRACE_H #define __UM_X86_PTRACE_H +/* This is here because signal.c needs the REGSET_FP_LEGACY definition */ +enum { + REGSET_GENERAL, +#ifdef CONFIG_X86_32 + REGSET_FP_LEGACY, +#endif + REGSET_FP, + REGSET_XSTATE, +}; + #include <linux/compiler.h> #ifndef CONFIG_X86_32 #define __FRAME_OFFSETS /* Needed to get the R* macros */ diff --git a/arch/x86/um/os-Linux/registers.c b/arch/x86/um/os-Linux/registers.c index f8e5516d9708..76eaeb93928c 100644 --- a/arch/x86/um/os-Linux/registers.c +++ b/arch/x86/um/os-Linux/registers.c @@ -16,133 +16,58 @@ #include <asm/sigcontext.h> #include <linux/elf.h> #include <registers.h> +#include <sys/mman.h> -static int have_xstate_support; +unsigned long host_fp_size; -static int save_i387_registers(int pid, unsigned long *fp_regs) -{ - if (ptrace(PTRACE_GETFPREGS, pid, 0, fp_regs) < 0) - return -errno; - return 0; -} - -static int save_fp_registers(int pid, unsigned long *fp_regs) +int get_fp_registers(int pid, unsigned long *regs) { -#ifdef PTRACE_GETREGSET - struct iovec iov; + struct iovec iov = { + .iov_base = regs, + .iov_len = host_fp_size, + }; - if (have_xstate_support) { - iov.iov_base = fp_regs; - iov.iov_len = FP_SIZE * sizeof(unsigned long); - if (ptrace(PTRACE_GETREGSET, pid, NT_X86_XSTATE, &iov) < 0) - return -errno; - return 0; - } else -#endif - return save_i387_registers(pid, fp_regs); -} - -static int restore_i387_registers(int pid, unsigned long *fp_regs) -{ - if (ptrace(PTRACE_SETFPREGS, pid, 0, fp_regs) < 0) + if (ptrace(PTRACE_GETREGSET, pid, NT_X86_XSTATE, &iov) < 0) return -errno; return 0; } -static int restore_fp_registers(int pid, unsigned long *fp_regs) -{ -#ifdef PTRACE_SETREGSET - struct iovec iov; - if (have_xstate_support) { - iov.iov_base = fp_regs; - iov.iov_len = FP_SIZE * sizeof(unsigned long); - if (ptrace(PTRACE_SETREGSET, pid, NT_X86_XSTATE, &iov) < 0) - return -errno; - return 0; - } else -#endif - return restore_i387_registers(pid, fp_regs); -} - -#ifdef __i386__ -int have_fpx_regs = 1; -static int save_fpx_registers(int pid, unsigned long *fp_regs) +int put_fp_registers(int pid, unsigned long *regs) { - if (ptrace(PTRACE_GETFPXREGS, pid, 0, fp_regs) < 0) - return -errno; - return 0; -} + struct iovec iov = { + .iov_base = regs, + .iov_len = host_fp_size, + }; -static int restore_fpx_registers(int pid, unsigned long *fp_regs) -{ - if (ptrace(PTRACE_SETFPXREGS, pid, 0, fp_regs) < 0) + if (ptrace(PTRACE_SETREGSET, pid, NT_X86_XSTATE, &iov) < 0) return -errno; return 0; } -int get_fp_registers(int pid, unsigned long *regs) -{ - if (have_fpx_regs) - return save_fpx_registers(pid, regs); - else - return save_fp_registers(pid, regs); -} - -int put_fp_registers(int pid, unsigned long *regs) -{ - if (have_fpx_regs) - return restore_fpx_registers(pid, regs); - else - return restore_fp_registers(pid, regs); -} - -void arch_init_registers(int pid) -{ - struct user_fpxregs_struct fpx_regs; - int err; - - err = ptrace(PTRACE_GETFPXREGS, pid, 0, &fpx_regs); - if (!err) - return; - - if (errno != EIO) - panic("check_ptrace : PTRACE_GETFPXREGS failed, errno = %d", - errno); - - have_fpx_regs = 0; -} -#else - -int get_fp_registers(int pid, unsigned long *regs) +int arch_init_registers(int pid) { - return save_fp_registers(pid, regs); + struct iovec iov = { + /* Just use plenty of space, it does not cost us anything */ + .iov_len = 2 * 1024 * 1024, + }; + int ret; + + iov.iov_base = mmap(NULL, iov.iov_len, PROT_WRITE | PROT_READ, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + if (iov.iov_base == MAP_FAILED) + return -ENOMEM; + + /* GDB has x86_xsave_length, which uses x86_cpuid_count */ + ret = ptrace(PTRACE_GETREGSET, pid, NT_X86_XSTATE, &iov); + if (ret) + ret = -errno; + munmap(iov.iov_base, 2 * 1024 * 1024); + + host_fp_size = iov.iov_len; + + return ret; } -int put_fp_registers(int pid, unsigned long *regs) -{ - return restore_fp_registers(pid, regs); -} - -void arch_init_registers(int pid) -{ -#ifdef PTRACE_GETREGSET - void * fp_regs; - struct iovec iov; - - fp_regs = malloc(FP_SIZE * sizeof(unsigned long)); - if(fp_regs == NULL) - return; - - iov.iov_base = fp_regs; - iov.iov_len = FP_SIZE * sizeof(unsigned long); - if (ptrace(PTRACE_GETREGSET, pid, NT_X86_XSTATE, &iov) == 0) - have_xstate_support = 1; - - free(fp_regs); -#endif -} -#endif - unsigned long get_thread_reg(int reg, jmp_buf *buf) { switch (reg) { diff --git a/arch/x86/um/ptrace.c b/arch/x86/um/ptrace.c new file mode 100644 index 000000000000..54d924bc45ce --- /dev/null +++ b/arch/x86/um/ptrace.c @@ -0,0 +1,267 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/sched.h> +#include <linux/elf.h> +#include <linux/regset.h> +#include <asm/user32.h> +#include <asm/sigcontext.h> + +#ifdef CONFIG_X86_32 +/* + * FPU tag word conversions. + */ + +static inline unsigned short twd_i387_to_fxsr(unsigned short twd) +{ + unsigned int tmp; /* to avoid 16 bit prefixes in the code */ + + /* Transform each pair of bits into 01 (valid) or 00 (empty) */ + tmp = ~twd; + tmp = (tmp | (tmp>>1)) & 0x5555; /* 0V0V0V0V0V0V0V0V */ + /* and move the valid bits to the lower byte. */ + tmp = (tmp | (tmp >> 1)) & 0x3333; /* 00VV00VV00VV00VV */ + tmp = (tmp | (tmp >> 2)) & 0x0f0f; /* 0000VVVV0000VVVV */ + tmp = (tmp | (tmp >> 4)) & 0x00ff; /* 00000000VVVVVVVV */ + return tmp; +} + +static inline unsigned long twd_fxsr_to_i387(struct user_fxsr_struct *fxsave) +{ + struct _fpxreg *st = NULL; + unsigned long twd = (unsigned long) fxsave->twd; + unsigned long tag; + unsigned long ret = 0xffff0000; + int i; + +#define FPREG_ADDR(f, n) ((char *)&(f)->st_space + (n) * 16) + + for (i = 0; i < 8; i++) { + if (twd & 0x1) { + st = (struct _fpxreg *) FPREG_ADDR(fxsave, i); + + switch (st->exponent & 0x7fff) { + case 0x7fff: + tag = 2; /* Special */ + break; + case 0x0000: + if (!st->significand[0] && + !st->significand[1] && + !st->significand[2] && + !st->significand[3]) { + tag = 1; /* Zero */ + } else { + tag = 2; /* Special */ + } + break; + default: + if (st->significand[3] & 0x8000) + tag = 0; /* Valid */ + else + tag = 2; /* Special */ + break; + } + } else { + tag = 3; /* Empty */ + } + ret |= (tag << (2 * i)); + twd = twd >> 1; + } + return ret; +} + +/* Get/set the old 32bit i387 registers (pre-FPX) */ +static int fpregs_legacy_get(struct task_struct *target, + const struct user_regset *regset, + struct membuf to) +{ + struct user_fxsr_struct *fxsave = (void *)target->thread.regs.regs.fp; + int i; + + membuf_store(&to, (unsigned long)fxsave->cwd | 0xffff0000ul); + membuf_store(&to, (unsigned long)fxsave->swd | 0xffff0000ul); + membuf_store(&to, twd_fxsr_to_i387(fxsave)); + membuf_store(&to, fxsave->fip); + membuf_store(&to, fxsave->fcs | ((unsigned long)fxsave->fop << 16)); + membuf_store(&to, fxsave->foo); + membuf_store(&to, fxsave->fos); + + for (i = 0; i < 8; i++) + membuf_write(&to, (void *)fxsave->st_space + i * 16, 10); + + return 0; +} + +static int fpregs_legacy_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + struct user_fxsr_struct *fxsave = (void *)target->thread.regs.regs.fp; + const struct user_i387_struct *from; + struct user_i387_struct buf; + int i; + + if (ubuf) { + if (copy_from_user(&buf, ubuf, sizeof(buf))) + return -EFAULT; + from = &buf; + } else { + from = kbuf; + } + + fxsave->cwd = (unsigned short)(from->cwd & 0xffff); + fxsave->swd = (unsigned short)(from->swd & 0xffff); + fxsave->twd = twd_i387_to_fxsr((unsigned short)(from->twd & 0xffff)); + fxsave->fip = from->fip; + fxsave->fop = (unsigned short)((from->fcs & 0xffff0000ul) >> 16); + fxsave->fcs = (from->fcs & 0xffff); + fxsave->foo = from->foo; + fxsave->fos = from->fos; + + for (i = 0; i < 8; i++) { + memcpy((void *)fxsave->st_space + i * 16, + (void *)from->st_space + i * 10, 10); + } + + return 0; +} +#endif + +static int genregs_get(struct task_struct *target, + const struct user_regset *regset, + struct membuf to) +{ + int reg; + + for (reg = 0; to.left; reg++) + membuf_store(&to, getreg(target, reg * sizeof(unsigned long))); + return 0; +} + +static int genregs_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + int ret = 0; + + if (kbuf) { + const unsigned long *k = kbuf; + + while (count >= sizeof(*k) && !ret) { + ret = putreg(target, pos, *k++); + count -= sizeof(*k); + pos += sizeof(*k); + } + } else { + const unsigned long __user *u = ubuf; + + while (count >= sizeof(*u) && !ret) { + unsigned long word; + + ret = __get_user(word, u++); + if (ret) + break; + ret = putreg(target, pos, word); + count -= sizeof(*u); + pos += sizeof(*u); + } + } + return ret; +} + +static int generic_fpregs_active(struct task_struct *target, const struct user_regset *regset) +{ + return regset->n; +} + +static int generic_fpregs_get(struct task_struct *target, + const struct user_regset *regset, + struct membuf to) +{ + void *fpregs = task_pt_regs(target)->regs.fp; + + membuf_write(&to, fpregs, regset->size * regset->n); + return 0; +} + +static int generic_fpregs_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + void *fpregs = task_pt_regs(target)->regs.fp; + + return user_regset_copyin(&pos, &count, &kbuf, &ubuf, + fpregs, 0, regset->size * regset->n); +} + +static struct user_regset uml_regsets[] __ro_after_init = { + [REGSET_GENERAL] = { + .core_note_type = NT_PRSTATUS, + .n = sizeof(struct user_regs_struct) / sizeof(long), + .size = sizeof(long), + .align = sizeof(long), + .regset_get = genregs_get, + .set = genregs_set + }, +#ifdef CONFIG_X86_32 + /* Old FP registers, they are needed in signal frames */ + [REGSET_FP_LEGACY] = { + .core_note_type = NT_PRFPREG, + .n = sizeof(struct user_i387_ia32_struct) / sizeof(long), + .size = sizeof(long), + .align = sizeof(long), + .active = generic_fpregs_active, + .regset_get = fpregs_legacy_get, + .set = fpregs_legacy_set, + }, +#endif + [REGSET_FP] = { +#ifdef CONFIG_X86_32 + .core_note_type = NT_PRXFPREG, + .n = sizeof(struct user32_fxsr_struct) / sizeof(long), +#else + .core_note_type = NT_PRFPREG, + .n = sizeof(struct user_i387_struct) / sizeof(long), +#endif + .size = sizeof(long), + .align = sizeof(long), + .active = generic_fpregs_active, + .regset_get = generic_fpregs_get, + .set = generic_fpregs_set, + }, + [REGSET_XSTATE] = { + .core_note_type = NT_X86_XSTATE, + .size = sizeof(long), + .align = sizeof(long), + .active = generic_fpregs_active, + .regset_get = generic_fpregs_get, + .set = generic_fpregs_set, + }, + /* TODO: Add TLS regset for 32bit */ +}; + +const struct user_regset_view user_uml_view = { +#ifdef CONFIG_X86_32 + .name = "i386", .e_machine = EM_386, +#else + .name = "x86_64", .e_machine = EM_X86_64, +#endif + .regsets = uml_regsets, .n = ARRAY_SIZE(uml_regsets) +}; + +const struct user_regset_view * +task_user_regset_view(struct task_struct *tsk) +{ + return &user_uml_view; +} + +static int __init init_regset_xstate_info(void) +{ + uml_regsets[REGSET_XSTATE].n = + host_fp_size / uml_regsets[REGSET_XSTATE].size; + + return 0; +} +arch_initcall(init_regset_xstate_info); diff --git a/arch/x86/um/ptrace_32.c b/arch/x86/um/ptrace_32.c index b8b85a52eb6f..3af3cb821524 100644 --- a/arch/x86/um/ptrace_32.c +++ b/arch/x86/um/ptrace_32.c @@ -6,6 +6,7 @@ #include <linux/mm.h> #include <linux/sched.h> #include <linux/uaccess.h> +#include <linux/regset.h> #include <asm/ptrace-abi.h> #include <registers.h> #include <skas.h> @@ -168,69 +169,6 @@ int peek_user(struct task_struct *child, long addr, long data) return put_user(tmp, (unsigned long __user *) data); } -/* FIXME: Do the required conversions instead of erroring out */ -extern int have_fpx_regs; - -static int get_fpregs(struct user_i387_struct __user *buf, struct task_struct *child) -{ - int n; - - if (have_fpx_regs) - return -EINVAL; - - n = copy_to_user(buf, &child->thread.regs.regs.fp, - sizeof(struct user_i387_struct)); - if(n > 0) - return -EFAULT; - - return n; -} - -static int set_fpregs(struct user_i387_struct __user *buf, struct task_struct *child) -{ - int n; - - if (have_fpx_regs) - return -EINVAL; - - n = copy_from_user(&child->thread.regs.regs.fp, buf, - sizeof(struct user_i387_struct)); - if (n > 0) - return -EFAULT; - - return 0; -} - -static int get_fpxregs(struct user_fxsr_struct __user *buf, struct task_struct *child) -{ - int n; - - if (!have_fpx_regs) - return -EINVAL; - - n = copy_to_user(buf, &child->thread.regs.regs.fp, - sizeof(struct user_fxsr_struct)); - if(n > 0) - return -EFAULT; - - return n; -} - -static int set_fpxregs(struct user_fxsr_struct __user *buf, struct task_struct *child) -{ - int n; - - if (!have_fpx_regs) - return -EINVAL; - - n = copy_from_user(&child->thread.regs.regs.fp, buf, - sizeof(struct user_fxsr_struct)); - if (n > 0) - return -EFAULT; - - return 0; -} - long subarch_ptrace(struct task_struct *child, long request, unsigned long addr, unsigned long data) { @@ -238,17 +176,25 @@ long subarch_ptrace(struct task_struct *child, long request, void __user *datap = (void __user *) data; switch (request) { case PTRACE_GETFPREGS: /* Get the child FPU state. */ - ret = get_fpregs(datap, child); - break; + return copy_regset_to_user(child, task_user_regset_view(child), + REGSET_FP_LEGACY, + 0, sizeof(struct user_i387_struct), + datap); case PTRACE_SETFPREGS: /* Set the child FPU state. */ - ret = set_fpregs(datap, child); - break; + return copy_regset_from_user(child, task_user_regset_view(child), + REGSET_FP_LEGACY, + 0, sizeof(struct user_i387_struct), + datap); case PTRACE_GETFPXREGS: /* Get the child FPU state. */ - ret = get_fpxregs(datap, child); - break; + return copy_regset_to_user(child, task_user_regset_view(child), + REGSET_FP, + 0, sizeof(struct user_fxsr_struct), + datap); case PTRACE_SETFPXREGS: /* Set the child FPU state. */ - ret = set_fpxregs(datap, child); - break; + return copy_regset_from_user(child, task_user_regset_view(child), + REGSET_FP, + 0, sizeof(struct user_fxsr_struct), + datap); default: ret = -EIO; } diff --git a/arch/x86/um/ptrace_64.c b/arch/x86/um/ptrace_64.c index f8bbad29cd0b..e0d4120a45c8 100644 --- a/arch/x86/um/ptrace_64.c +++ b/arch/x86/um/ptrace_64.c @@ -8,6 +8,7 @@ #include <linux/mm.h> #include <linux/sched.h> #include <linux/errno.h> +#include <linux/regset.h> #define __FRAME_OFFSETS #include <asm/ptrace.h> #include <linux/uaccess.h> @@ -188,30 +189,6 @@ int peek_user(struct task_struct *child, long addr, long data) return put_user(tmp, (unsigned long *) data); } -static int get_fpregs(struct user_i387_struct __user *buf, struct task_struct *child) -{ - int n; - - n = copy_to_user(buf, &child->thread.regs.regs.fp, - sizeof(struct user_i387_struct)); - if (n > 0) - return -EFAULT; - - return n; -} - -static int set_fpregs(struct user_i387_struct __user *buf, struct task_struct *child) -{ - int n; - - n = copy_from_user(&child->thread.regs.regs.fp, buf, - sizeof(struct user_i387_struct)); - if (n > 0) - return -EFAULT; - - return 0; -} - long subarch_ptrace(struct task_struct *child, long request, unsigned long addr, unsigned long data) { @@ -220,11 +197,15 @@ long subarch_ptrace(struct task_struct *child, long request, switch (request) { case PTRACE_GETFPREGS: /* Get the child FPU state. */ - ret = get_fpregs(datap, child); - break; + return copy_regset_to_user(child, task_user_regset_view(child), + REGSET_FP, + 0, sizeof(struct user_i387_struct), + datap); case PTRACE_SETFPREGS: /* Set the child FPU state. */ - ret = set_fpregs(datap, child); - break; + return copy_regset_from_user(child, task_user_regset_view(child), + REGSET_FP, + 0, sizeof(struct user_i387_struct), + datap); case PTRACE_ARCH_PRCTL: /* XXX Calls ptrace on the host - needs some SMP thinking */ ret = arch_prctl(child, data, (void __user *) addr); diff --git a/arch/x86/um/shared/sysdep/ptrace.h b/arch/x86/um/shared/sysdep/ptrace.h index 6ca4ecabc55b..2dd4ca6713f8 100644 --- a/arch/x86/um/shared/sysdep/ptrace.h +++ b/arch/x86/um/shared/sysdep/ptrace.h @@ -56,12 +56,16 @@ struct syscall_args { UPT_SYSCALL_ARG5(r), \ UPT_SYSCALL_ARG6(r) } } ) +extern unsigned long host_fp_size; + struct uml_pt_regs { unsigned long gp[MAX_REG_NR]; - unsigned long fp[MAX_FP_NR]; struct faultinfo faultinfo; long syscall; int is_user; + + /* Dynamically sized FP registers (holds an XSTATE) */ + unsigned long fp[]; }; #define EMPTY_UML_PT_REGS { } @@ -72,4 +76,6 @@ struct uml_pt_regs { extern int user_context(unsigned long sp); +extern int arch_init_registers(int pid); + #endif /* __SYSDEP_X86_PTRACE_H */ diff --git a/arch/x86/um/shared/sysdep/ptrace_32.h b/arch/x86/um/shared/sysdep/ptrace_32.h index 0c4989842fbe..2392470cac4d 100644 --- a/arch/x86/um/shared/sysdep/ptrace_32.h +++ b/arch/x86/um/shared/sysdep/ptrace_32.h @@ -6,8 +6,6 @@ #ifndef __SYSDEP_I386_PTRACE_H #define __SYSDEP_I386_PTRACE_H -#define MAX_FP_NR HOST_FPX_SIZE - #define UPT_SYSCALL_ARG1(r) UPT_BX(r) #define UPT_SYSCALL_ARG2(r) UPT_CX(r) #define UPT_SYSCALL_ARG3(r) UPT_DX(r) @@ -15,6 +13,4 @@ #define UPT_SYSCALL_ARG5(r) UPT_DI(r) #define UPT_SYSCALL_ARG6(r) UPT_BP(r) -extern void arch_init_registers(int pid); - #endif diff --git a/arch/x86/um/shared/sysdep/ptrace_64.h b/arch/x86/um/shared/sysdep/ptrace_64.h index 0dc223aa1c2d..e73573ac871f 100644 --- a/arch/x86/um/shared/sysdep/ptrace_64.h +++ b/arch/x86/um/shared/sysdep/ptrace_64.h @@ -8,8 +8,6 @@ #ifndef __SYSDEP_X86_64_PTRACE_H #define __SYSDEP_X86_64_PTRACE_H -#define MAX_FP_NR HOST_FP_SIZE - #define REGS_R8(r) ((r)[HOST_R8]) #define REGS_R9(r) ((r)[HOST_R9]) #define REGS_R10(r) ((r)[HOST_R10]) @@ -57,6 +55,4 @@ #define UPT_SYSCALL_ARG5(r) UPT_R8(r) #define UPT_SYSCALL_ARG6(r) UPT_R9(r) -extern void arch_init_registers(int pid); - #endif diff --git a/arch/x86/um/shared/sysdep/ptrace_user.h b/arch/x86/um/shared/sysdep/ptrace_user.h index 1d1a824fa652..98da23120538 100644 --- a/arch/x86/um/shared/sysdep/ptrace_user.h +++ b/arch/x86/um/shared/sysdep/ptrace_user.h @@ -11,12 +11,6 @@ #define REGS_IP_INDEX HOST_IP #define REGS_SP_INDEX HOST_SP -#ifdef __i386__ -#define FP_SIZE ((HOST_FPX_SIZE > HOST_FP_SIZE) ? HOST_FPX_SIZE : HOST_FP_SIZE) -#else -#define FP_SIZE HOST_FP_SIZE -#endif - /* * glibc before 2.27 does not include PTRACE_SYSEMU_SINGLESTEP in its enum, * ensure we have a definition by (re-)defining it here. diff --git a/arch/x86/um/signal.c b/arch/x86/um/signal.c index fa78c74e00cb..94b2b2bbae31 100644 --- a/arch/x86/um/signal.c +++ b/arch/x86/um/signal.c @@ -16,145 +16,24 @@ #include <registers.h> #include <skas.h> -#ifdef CONFIG_X86_32 - -/* - * FPU tag word conversions. - */ - -static inline unsigned short twd_i387_to_fxsr(unsigned short twd) -{ - unsigned int tmp; /* to avoid 16 bit prefixes in the code */ - - /* Transform each pair of bits into 01 (valid) or 00 (empty) */ - tmp = ~twd; - tmp = (tmp | (tmp>>1)) & 0x5555; /* 0V0V0V0V0V0V0V0V */ - /* and move the valid bits to the lower byte. */ - tmp = (tmp | (tmp >> 1)) & 0x3333; /* 00VV00VV00VV00VV */ - tmp = (tmp | (tmp >> 2)) & 0x0f0f; /* 0000VVVV0000VVVV */ - tmp = (tmp | (tmp >> 4)) & 0x00ff; /* 00000000VVVVVVVV */ - return tmp; -} - -static inline unsigned long twd_fxsr_to_i387(struct user_fxsr_struct *fxsave) -{ - struct _fpxreg *st = NULL; - unsigned long twd = (unsigned long) fxsave->twd; - unsigned long tag; - unsigned long ret = 0xffff0000; - int i; - -#define FPREG_ADDR(f, n) ((char *)&(f)->st_space + (n) * 16) - - for (i = 0; i < 8; i++) { - if (twd & 0x1) { - st = (struct _fpxreg *) FPREG_ADDR(fxsave, i); - - switch (st->exponent & 0x7fff) { - case 0x7fff: - tag = 2; /* Special */ - break; - case 0x0000: - if ( !st->significand[0] && - !st->significand[1] && - !st->significand[2] && - !st->significand[3] ) { - tag = 1; /* Zero */ - } else { - tag = 2; /* Special */ - } - break; - default: - if (st->significand[3] & 0x8000) { - tag = 0; /* Valid */ - } else { - tag = 2; /* Special */ - } - break; - } - } else { - tag = 3; /* Empty */ - } - ret |= (tag << (2 * i)); - twd = twd >> 1; - } - return ret; -} - -static int convert_fxsr_to_user(struct _fpstate __user *buf, - struct user_fxsr_struct *fxsave) -{ - unsigned long env[7]; - struct _fpreg __user *to; - struct _fpxreg *from; - int i; - - env[0] = (unsigned long)fxsave->cwd | 0xffff0000ul; - env[1] = (unsigned long)fxsave->swd | 0xffff0000ul; - env[2] = twd_fxsr_to_i387(fxsave); - env[3] = fxsave->fip; - env[4] = fxsave->fcs | ((unsigned long)fxsave->fop << 16); - env[5] = fxsave->foo; - env[6] = fxsave->fos; - - if (__copy_to_user(buf, env, 7 * sizeof(unsigned long))) - return 1; - - to = &buf->_st[0]; - from = (struct _fpxreg *) &fxsave->st_space[0]; - for (i = 0; i < 8; i++, to++, from++) { - unsigned long __user *t = (unsigned long __user *)to; - unsigned long *f = (unsigned long *)from; - - if (__put_user(*f, t) || - __put_user(*(f + 1), t + 1) || - __put_user(from->exponent, &to->exponent)) - return 1; - } - return 0; -} - -static int convert_fxsr_from_user(struct user_fxsr_struct *fxsave, - struct _fpstate __user *buf) -{ - unsigned long env[7]; - struct _fpxreg *to; - struct _fpreg __user *from; - int i; - - if (copy_from_user( env, buf, 7 * sizeof(long))) - return 1; - - fxsave->cwd = (unsigned short)(env[0] & 0xffff); - fxsave->swd = (unsigned short)(env[1] & 0xffff); - fxsave->twd = twd_i387_to_fxsr((unsigned short)(env[2] & 0xffff)); - fxsave->fip = env[3]; - fxsave->fop = (unsigned short)((env[4] & 0xffff0000ul) >> 16); - fxsave->fcs = (env[4] & 0xffff); - fxsave->foo = env[5]; - fxsave->fos = env[6]; - - to = (struct _fpxreg *) &fxsave->st_space[0]; - from = &buf->_st[0]; - for (i = 0; i < 8; i++, to++, from++) { - unsigned long *t = (unsigned long *)to; - unsigned long __user *f = (unsigned long __user *)from; - - if (__get_user(*t, f) || - __get_user(*(t + 1), f + 1) || - __get_user(to->exponent, &from->exponent)) - return 1; - } - return 0; -} - -extern int have_fpx_regs; +#include <linux/regset.h> +#include <asm/sigframe.h> +#ifdef CONFIG_X86_32 +struct _xstate_64 { + struct _fpstate_64 fpstate; + struct _header xstate_hdr; + struct _ymmh_state ymmh; + /* New processor state extensions go here: */ +}; +#else +#define _xstate_64 _xstate #endif static int copy_sc_from_user(struct pt_regs *regs, struct sigcontext __user *from) { + struct _xstate_64 __user *from_fp64; struct sigcontext sc; int err; @@ -203,31 +82,26 @@ static int copy_sc_from_user(struct pt_regs *regs, #undef GETREG #ifdef CONFIG_X86_32 - if (have_fpx_regs) { - struct user_fxsr_struct *fpx; - fpx = (void *)®s->regs.fp; - - err = convert_fxsr_from_user(fpx, (void *)sc.fpstate); - if (err) - return 1; - } else { - BUILD_BUG_ON(sizeof(regs->regs.fp) > sizeof(struct _fpstate)); - err = copy_from_user(regs->regs.fp, (void *)sc.fpstate, - sizeof(regs->regs.fp)); - if (err) - return 1; - } + from_fp64 = ((void *)sc.fpstate) + offsetof(struct _fpstate_32, _fxsr_env); #else - { - /* FIXME: Save/restore extended state (past the 16 YMM regs) */ - BUILD_BUG_ON(sizeof(regs->regs.fp) < sizeof(struct _xstate)); + from_fp64 = (void *)sc.fpstate; +#endif - err = copy_from_user(regs->regs.fp, (void *)sc.fpstate, - sizeof(struct _xstate)); - if (err) - return 1; - } + err = copy_from_user(regs->regs.fp, from_fp64, host_fp_size); + if (err) + return 1; + +#ifdef CONFIG_X86_32 + /* Data is duplicated and this copy is the important one */ + err = copy_regset_from_user(current, + task_user_regset_view(current), + REGSET_FP_LEGACY, 0, + sizeof(struct user_i387_struct), + (void *)sc.fpstate); + if (err < 0) + return err; #endif + return 0; } @@ -235,6 +109,7 @@ static int copy_sc_to_user(struct sigcontext __user *to, struct _xstate __user *to_fp, struct pt_regs *regs, unsigned long mask) { + struct _xstate_64 __user *to_fp64; struct sigcontext sc; struct faultinfo * fi = ¤t->thread.arch.faultinfo; int err; @@ -286,36 +161,44 @@ static int copy_sc_to_user(struct sigcontext __user *to, return 1; #ifdef CONFIG_X86_32 - if (have_fpx_regs) { - struct user_fxsr_struct *fpx; - - fpx = (void *)®s->regs.fp; + err = copy_regset_to_user(current, + task_user_regset_view(current), + REGSET_FP_LEGACY, 0, + sizeof(struct _fpstate_32), to_fp); + if (err < 0) + return err; - err = convert_fxsr_to_user(&to_fp->fpstate, fpx); - if (err) - return 1; + __put_user(X86_FXSR_MAGIC, &to_fp->fpstate.magic); - err |= __put_user(fpx->swd, &to_fp->fpstate.status); - err |= __put_user(X86_FXSR_MAGIC, &to_fp->fpstate.magic); - if (err) - return 1; - - } else { - if (copy_to_user(to_fp, regs->regs.fp, sizeof(regs->regs.fp))) - return 1; - - /* Need to fill in the sw_reserved bits ... */ - BUILD_BUG_ON(offsetof(typeof(*to_fp), - fpstate.sw_reserved.magic1) >= - sizeof(struct _fpstate)); - __put_user(0, &to_fp->fpstate.sw_reserved.magic1); - __put_user(sizeof(struct _fpstate), - &to_fp->fpstate.sw_reserved.extended_size); - } + BUILD_BUG_ON(offsetof(struct _xstate, xstate_hdr) != + offsetof(struct _xstate_64, xstate_hdr) + + offsetof(struct _fpstate_32, _fxsr_env)); + to_fp64 = (void *)to_fp + offsetof(struct _fpstate_32, _fxsr_env); #else - if (copy_to_user(to_fp, regs->regs.fp, sizeof(struct _xstate))) + to_fp64 = to_fp; +#endif /* CONFIG_X86_32 */ + + if (copy_to_user(to_fp64, regs->regs.fp, host_fp_size)) return 1; + + /* + * Put magic/size values for userspace. We do not bother to verify them + * later on, however, userspace needs them should it try to read the + * XSTATE data. And ptrace does not fill in these parts. + */ + BUILD_BUG_ON(sizeof(int) != FP_XSTATE_MAGIC2_SIZE); +#ifdef CONFIG_X86_32 + __put_user(offsetof(struct _fpstate_32, _fxsr_env) + + host_fp_size + FP_XSTATE_MAGIC2_SIZE, + &to_fp64->fpstate.sw_reserved.extended_size); +#else + __put_user(host_fp_size + FP_XSTATE_MAGIC2_SIZE, + &to_fp64->fpstate.sw_reserved.extended_size); #endif + __put_user(host_fp_size, &to_fp64->fpstate.sw_reserved.xstate_size); + + __put_user(FP_XSTATE_MAGIC1, &to_fp64->fpstate.sw_reserved.magic1); + __put_user(FP_XSTATE_MAGIC2, (int *)((void *)to_fp64 + host_fp_size)); return 0; } @@ -333,34 +216,15 @@ static int copy_ucontext_to_user(struct ucontext __user *uc, return err; } -struct sigframe -{ - char __user *pretcode; - int sig; - struct sigcontext sc; - struct _xstate fpstate; - unsigned long extramask[_NSIG_WORDS-1]; - char retcode[8]; -}; - -struct rt_sigframe -{ - char __user *pretcode; - int sig; - struct siginfo __user *pinfo; - void __user *puc; - struct siginfo info; - struct ucontext uc; - struct _xstate fpstate; - char retcode[8]; -}; - int setup_signal_stack_sc(unsigned long stack_top, struct ksignal *ksig, struct pt_regs *regs, sigset_t *mask) { + size_t math_size = offsetof(struct _fpstate_32, _fxsr_env) + + host_fp_size + FP_XSTATE_MAGIC2_SIZE; struct sigframe __user *frame; void __user *restorer; int err = 0, sig = ksig->sig; + unsigned long fp_to; /* This is the same calculation as i386 - ((sp + 4) & 15) == 0 */ stack_top = ((stack_top + 4) & -16UL) - 4; @@ -368,13 +232,21 @@ int setup_signal_stack_sc(unsigned long stack_top, struct ksignal *ksig, if (!access_ok(frame, sizeof(*frame))) return 1; + /* Add required space for math frame */ + frame = (struct sigframe __user *)((unsigned long)frame - math_size); + restorer = frame->retcode; if (ksig->ka.sa.sa_flags & SA_RESTORER) restorer = ksig->ka.sa.sa_restorer; - err |= __put_user(restorer, &frame->pretcode); + err |= __put_user(restorer, (void **)&frame->pretcode); err |= __put_user(sig, &frame->sig); - err |= copy_sc_to_user(&frame->sc, &frame->fpstate, regs, mask->sig[0]); + + fp_to = (unsigned long)frame + sizeof(*frame); + + err |= copy_sc_to_user(&frame->sc, + (struct _xstate __user *)fp_to, + regs, mask->sig[0]); if (_NSIG_WORDS > 1) err |= __copy_to_user(&frame->extramask, &mask->sig[1], sizeof(frame->extramask)); @@ -404,26 +276,35 @@ int setup_signal_stack_sc(unsigned long stack_top, struct ksignal *ksig, int setup_signal_stack_si(unsigned long stack_top, struct ksignal *ksig, struct pt_regs *regs, sigset_t *mask) { + size_t math_size = offsetof(struct _fpstate_32, _fxsr_env) + + host_fp_size + FP_XSTATE_MAGIC2_SIZE; struct rt_sigframe __user *frame; void __user *restorer; int err = 0, sig = ksig->sig; + unsigned long fp_to; stack_top &= -8UL; frame = (struct rt_sigframe __user *) stack_top - 1; if (!access_ok(frame, sizeof(*frame))) return 1; + /* Add required space for math frame */ + frame = (struct rt_sigframe __user *)((unsigned long)frame - math_size); + restorer = frame->retcode; if (ksig->ka.sa.sa_flags & SA_RESTORER) restorer = ksig->ka.sa.sa_restorer; - err |= __put_user(restorer, &frame->pretcode); + err |= __put_user(restorer, (void **)&frame->pretcode); err |= __put_user(sig, &frame->sig); - err |= __put_user(&frame->info, &frame->pinfo); - err |= __put_user(&frame->uc, &frame->puc); + err |= __put_user(&frame->info, (void **)&frame->pinfo); + err |= __put_user(&frame->uc, (void **)&frame->puc); err |= copy_siginfo_to_user(&frame->info, &ksig->info); - err |= copy_ucontext_to_user(&frame->uc, &frame->fpstate, mask, - PT_REGS_SP(regs)); + + fp_to = (unsigned long)frame + sizeof(*frame); + + err |= copy_ucontext_to_user(&frame->uc, (struct _xstate __user *)fp_to, + mask, PT_REGS_SP(regs)); /* * This is movl $,%eax ; int $0x80 @@ -475,27 +356,24 @@ SYSCALL_DEFINE0(sigreturn) #else -struct rt_sigframe -{ - char __user *pretcode; - struct ucontext uc; - struct siginfo info; - struct _xstate fpstate; -}; - int setup_signal_stack_si(unsigned long stack_top, struct ksignal *ksig, struct pt_regs *regs, sigset_t *set) { + unsigned long math_size = host_fp_size + FP_XSTATE_MAGIC2_SIZE; struct rt_sigframe __user *frame; int err = 0, sig = ksig->sig; unsigned long fp_to; frame = (struct rt_sigframe __user *) round_down(stack_top - sizeof(struct rt_sigframe), 16); + + /* Add required space for math frame */ + frame = (struct rt_sigframe __user *)((unsigned long)frame - math_size); + /* Subtract 128 for a red zone and 8 for proper alignment */ frame = (struct rt_sigframe __user *) ((unsigned long) frame - 128 - 8); - if (!access_ok(frame, sizeof(*frame))) + if (!access_ok(frame, sizeof(*frame) + math_size)) goto out; if (ksig->ka.sa.sa_flags & SA_SIGINFO) { @@ -508,10 +386,12 @@ int setup_signal_stack_si(unsigned long stack_top, struct ksignal *ksig, err |= __put_user(0, &frame->uc.uc_flags); err |= __put_user(0, &frame->uc.uc_link); err |= __save_altstack(&frame->uc.uc_stack, PT_REGS_SP(regs)); - err |= copy_sc_to_user(&frame->uc.uc_mcontext, &frame->fpstate, regs, - set->sig[0]); - fp_to = (unsigned long)&frame->fpstate; + fp_to = (unsigned long)frame + sizeof(*frame); + + err |= copy_sc_to_user(&frame->uc.uc_mcontext, + (struct _xstate __user *)fp_to, + regs, set->sig[0]); err |= __put_user(fp_to, &frame->uc.uc_mcontext.fpstate); if (sizeof(*set) == 16) { diff --git a/arch/x86/um/user-offsets.c b/arch/x86/um/user-offsets.c index 1c77d9946199..d6e1cd9956bf 100644 --- a/arch/x86/um/user-offsets.c +++ b/arch/x86/um/user-offsets.c @@ -20,9 +20,6 @@ void foo(void); void foo(void) { #ifdef __i386__ - DEFINE_LONGS(HOST_FP_SIZE, sizeof(struct user_fpregs_struct)); - DEFINE_LONGS(HOST_FPX_SIZE, sizeof(struct user_fpxregs_struct)); - DEFINE(HOST_IP, EIP); DEFINE(HOST_SP, UESP); DEFINE(HOST_EFLAGS, EFL); @@ -41,11 +38,6 @@ void foo(void) DEFINE(HOST_GS, GS); DEFINE(HOST_ORIG_AX, ORIG_EAX); #else -#ifdef FP_XSTATE_MAGIC1 - DEFINE_LONGS(HOST_FP_SIZE, 2696); -#else - DEFINE(HOST_FP_SIZE, sizeof(struct _fpstate) / sizeof(unsigned long)); -#endif DEFINE_LONGS(HOST_BX, RBX); DEFINE_LONGS(HOST_CX, RCX); DEFINE_LONGS(HOST_DI, RDI); |