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
This commit is contained in:
@@ -33,8 +33,16 @@ extern void init_thread_xstate(void);
|
|||||||
extern int dump_fpu(struct pt_regs *, struct user_i387_struct *);
|
extern int dump_fpu(struct pt_regs *, struct user_i387_struct *);
|
||||||
|
|
||||||
extern user_regset_active_fn fpregs_active, xfpregs_active;
|
extern user_regset_active_fn fpregs_active, xfpregs_active;
|
||||||
extern user_regset_get_fn fpregs_get, xfpregs_get, fpregs_soft_get;
|
extern user_regset_get_fn fpregs_get, xfpregs_get, fpregs_soft_get,
|
||||||
extern user_regset_set_fn fpregs_set, xfpregs_set, fpregs_soft_set;
|
xstateregs_get;
|
||||||
|
extern user_regset_set_fn fpregs_set, xfpregs_set, fpregs_soft_set,
|
||||||
|
xstateregs_set;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* xstateregs_active == fpregs_active. Please refer to the comment
|
||||||
|
* at the definition of fpregs_active.
|
||||||
|
*/
|
||||||
|
#define xstateregs_active fpregs_active
|
||||||
|
|
||||||
extern struct _fpx_sw_bytes fx_sw_reserved;
|
extern struct _fpx_sw_bytes fx_sw_reserved;
|
||||||
#ifdef CONFIG_IA32_EMULATION
|
#ifdef CONFIG_IA32_EMULATION
|
||||||
|
@@ -1,5 +1,63 @@
|
|||||||
|
#ifndef _ASM_X86_USER_H
|
||||||
|
#define _ASM_X86_USER_H
|
||||||
|
|
||||||
#ifdef CONFIG_X86_32
|
#ifdef CONFIG_X86_32
|
||||||
# include "user_32.h"
|
# include "user_32.h"
|
||||||
#else
|
#else
|
||||||
# include "user_64.h"
|
# include "user_64.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <asm/types.h>
|
||||||
|
|
||||||
|
struct user_ymmh_regs {
|
||||||
|
/* 16 * 16 bytes for each YMMH-reg */
|
||||||
|
__u32 ymmh_space[64];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct user_xsave_hdr {
|
||||||
|
__u64 xstate_bv;
|
||||||
|
__u64 reserved1[2];
|
||||||
|
__u64 reserved2[5];
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The structure layout of user_xstateregs, used for exporting the
|
||||||
|
* extended register state through ptrace and core-dump (NT_X86_XSTATE note)
|
||||||
|
* interfaces will be same as the memory layout of xsave used by the processor
|
||||||
|
* (except for the bytes 464..511, which can be used by the software) and hence
|
||||||
|
* the size of this structure varies depending on the features supported by the
|
||||||
|
* processor and OS. The size of the structure that users need to use can be
|
||||||
|
* obtained by doing:
|
||||||
|
* cpuid_count(0xd, 0, &eax, &ptrace_xstateregs_struct_size, &ecx, &edx);
|
||||||
|
* i.e., cpuid.(eax=0xd,ecx=0).ebx will be the size that user (debuggers, etc.)
|
||||||
|
* need to use.
|
||||||
|
*
|
||||||
|
* For now, only the first 8 bytes of the software usable bytes[464..471] will
|
||||||
|
* be used and will be set to OS enabled xstate mask (which is same as the
|
||||||
|
* 64bit mask returned by the xgetbv's xCR0). Users (analyzing core dump
|
||||||
|
* remotely, etc.) can use this mask as well as the mask saved in the
|
||||||
|
* xstate_hdr bytes and interpret what states the processor/OS supports
|
||||||
|
* and what states are in modified/initialized conditions for the
|
||||||
|
* particular process/thread.
|
||||||
|
*
|
||||||
|
* Also when the user modifies certain state FP/SSE/etc through the
|
||||||
|
* ptrace interface, they must ensure that the xsave_hdr.xstate_bv
|
||||||
|
* bytes[512..519] of the memory layout are updated correspondingly.
|
||||||
|
* i.e., for example when FP state is modified to a non-init state,
|
||||||
|
* xsave_hdr.xstate_bv's bit 0 must be set to '1', when SSE is modified to
|
||||||
|
* non-init state, xsave_hdr.xstate_bv's bit 1 must to be set to '1', etc.
|
||||||
|
*/
|
||||||
|
#define USER_XSTATE_FX_SW_WORDS 6
|
||||||
|
#define USER_XSTATE_XCR0_WORD 0
|
||||||
|
|
||||||
|
struct user_xstateregs {
|
||||||
|
struct {
|
||||||
|
__u64 fpx_space[58];
|
||||||
|
__u64 xstate_fx_sw[USER_XSTATE_FX_SW_WORDS];
|
||||||
|
} i387;
|
||||||
|
struct user_xsave_hdr xsave_hdr;
|
||||||
|
struct user_ymmh_regs ymmh;
|
||||||
|
/* further processor state extensions go here */
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _ASM_X86_USER_H */
|
||||||
|
@@ -27,9 +27,11 @@
|
|||||||
extern unsigned int xstate_size;
|
extern unsigned int xstate_size;
|
||||||
extern u64 pcntxt_mask;
|
extern u64 pcntxt_mask;
|
||||||
extern struct xsave_struct *init_xstate_buf;
|
extern struct xsave_struct *init_xstate_buf;
|
||||||
|
extern u64 xstate_fx_sw_bytes[USER_XSTATE_FX_SW_WORDS];
|
||||||
|
|
||||||
extern void xsave_cntxt_init(void);
|
extern void xsave_cntxt_init(void);
|
||||||
extern void xsave_init(void);
|
extern void xsave_init(void);
|
||||||
|
extern void update_regset_xstate_info(unsigned int size, u64 xstate_mask);
|
||||||
extern int init_fpu(struct task_struct *child);
|
extern int init_fpu(struct task_struct *child);
|
||||||
extern int check_for_xstate(struct i387_fxsave_struct __user *buf,
|
extern int check_for_xstate(struct i387_fxsave_struct __user *buf,
|
||||||
void __user *fpstate,
|
void __user *fpstate,
|
||||||
|
@@ -164,6 +164,11 @@ int init_fpu(struct task_struct *tsk)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The xstateregs_active() routine is the same as the fpregs_active() routine,
|
||||||
|
* as the "regset->n" for the xstate regset will be updated based on the feature
|
||||||
|
* capabilites supported by the xsave.
|
||||||
|
*/
|
||||||
int fpregs_active(struct task_struct *target, const struct user_regset *regset)
|
int fpregs_active(struct task_struct *target, const struct user_regset *regset)
|
||||||
{
|
{
|
||||||
return tsk_used_math(target) ? regset->n : 0;
|
return tsk_used_math(target) ? regset->n : 0;
|
||||||
@@ -204,8 +209,6 @@ int xfpregs_set(struct task_struct *target, const struct user_regset *regset,
|
|||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
set_stopped_child_used_math(target);
|
|
||||||
|
|
||||||
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
|
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
|
||||||
&target->thread.xstate->fxsave, 0, -1);
|
&target->thread.xstate->fxsave, 0, -1);
|
||||||
|
|
||||||
@@ -224,6 +227,68 @@ int xfpregs_set(struct task_struct *target, const struct user_regset *regset,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int xstateregs_get(struct task_struct *target, const struct user_regset *regset,
|
||||||
|
unsigned int pos, unsigned int count,
|
||||||
|
void *kbuf, void __user *ubuf)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!cpu_has_xsave)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
ret = init_fpu(target);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copy the 48bytes defined by the software first into the xstate
|
||||||
|
* memory layout in the thread struct, so that we can copy the entire
|
||||||
|
* xstateregs to the user using one user_regset_copyout().
|
||||||
|
*/
|
||||||
|
memcpy(&target->thread.xstate->fxsave.sw_reserved,
|
||||||
|
xstate_fx_sw_bytes, sizeof(xstate_fx_sw_bytes));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copy the xstate memory layout.
|
||||||
|
*/
|
||||||
|
ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
|
||||||
|
&target->thread.xstate->xsave, 0, -1);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int xstateregs_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;
|
||||||
|
struct xsave_hdr_struct *xsave_hdr;
|
||||||
|
|
||||||
|
if (!cpu_has_xsave)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
ret = init_fpu(target);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
|
||||||
|
&target->thread.xstate->xsave, 0, -1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* mxcsr reserved bits must be masked to zero for security reasons.
|
||||||
|
*/
|
||||||
|
target->thread.xstate->fxsave.mxcsr &= mxcsr_feature_mask;
|
||||||
|
|
||||||
|
xsave_hdr = &target->thread.xstate->xsave.xsave_hdr;
|
||||||
|
|
||||||
|
xsave_hdr->xstate_bv &= pcntxt_mask;
|
||||||
|
/*
|
||||||
|
* These bits must be zero.
|
||||||
|
*/
|
||||||
|
xsave_hdr->reserved1[0] = xsave_hdr->reserved1[1] = 0;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
#if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION
|
#if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -404,8 +469,6 @@ int fpregs_set(struct task_struct *target, const struct user_regset *regset,
|
|||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
set_stopped_child_used_math(target);
|
|
||||||
|
|
||||||
if (!HAVE_HWFP)
|
if (!HAVE_HWFP)
|
||||||
return fpregs_soft_set(target, regset, pos, count, kbuf, ubuf);
|
return fpregs_soft_set(target, regset, pos, count, kbuf, ubuf);
|
||||||
|
|
||||||
|
@@ -48,6 +48,7 @@ enum x86_regset {
|
|||||||
REGSET_FP,
|
REGSET_FP,
|
||||||
REGSET_XFP,
|
REGSET_XFP,
|
||||||
REGSET_IOPERM64 = REGSET_XFP,
|
REGSET_IOPERM64 = REGSET_XFP,
|
||||||
|
REGSET_XSTATE,
|
||||||
REGSET_TLS,
|
REGSET_TLS,
|
||||||
REGSET_IOPERM32,
|
REGSET_IOPERM32,
|
||||||
};
|
};
|
||||||
@@ -1563,7 +1564,7 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
|
|||||||
|
|
||||||
#ifdef CONFIG_X86_64
|
#ifdef CONFIG_X86_64
|
||||||
|
|
||||||
static const struct user_regset x86_64_regsets[] = {
|
static struct user_regset x86_64_regsets[] __read_mostly = {
|
||||||
[REGSET_GENERAL] = {
|
[REGSET_GENERAL] = {
|
||||||
.core_note_type = NT_PRSTATUS,
|
.core_note_type = NT_PRSTATUS,
|
||||||
.n = sizeof(struct user_regs_struct) / sizeof(long),
|
.n = sizeof(struct user_regs_struct) / sizeof(long),
|
||||||
@@ -1576,6 +1577,12 @@ static const struct user_regset x86_64_regsets[] = {
|
|||||||
.size = sizeof(long), .align = sizeof(long),
|
.size = sizeof(long), .align = sizeof(long),
|
||||||
.active = xfpregs_active, .get = xfpregs_get, .set = xfpregs_set
|
.active = xfpregs_active, .get = xfpregs_get, .set = xfpregs_set
|
||||||
},
|
},
|
||||||
|
[REGSET_XSTATE] = {
|
||||||
|
.core_note_type = NT_X86_XSTATE,
|
||||||
|
.size = sizeof(u64), .align = sizeof(u64),
|
||||||
|
.active = xstateregs_active, .get = xstateregs_get,
|
||||||
|
.set = xstateregs_set
|
||||||
|
},
|
||||||
[REGSET_IOPERM64] = {
|
[REGSET_IOPERM64] = {
|
||||||
.core_note_type = NT_386_IOPERM,
|
.core_note_type = NT_386_IOPERM,
|
||||||
.n = IO_BITMAP_LONGS,
|
.n = IO_BITMAP_LONGS,
|
||||||
@@ -1601,7 +1608,7 @@ static const struct user_regset_view user_x86_64_view = {
|
|||||||
#endif /* CONFIG_X86_64 */
|
#endif /* CONFIG_X86_64 */
|
||||||
|
|
||||||
#if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION
|
#if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION
|
||||||
static const struct user_regset x86_32_regsets[] = {
|
static struct user_regset x86_32_regsets[] __read_mostly = {
|
||||||
[REGSET_GENERAL] = {
|
[REGSET_GENERAL] = {
|
||||||
.core_note_type = NT_PRSTATUS,
|
.core_note_type = NT_PRSTATUS,
|
||||||
.n = sizeof(struct user_regs_struct32) / sizeof(u32),
|
.n = sizeof(struct user_regs_struct32) / sizeof(u32),
|
||||||
@@ -1620,6 +1627,12 @@ static const struct user_regset x86_32_regsets[] = {
|
|||||||
.size = sizeof(u32), .align = sizeof(u32),
|
.size = sizeof(u32), .align = sizeof(u32),
|
||||||
.active = xfpregs_active, .get = xfpregs_get, .set = xfpregs_set
|
.active = xfpregs_active, .get = xfpregs_get, .set = xfpregs_set
|
||||||
},
|
},
|
||||||
|
[REGSET_XSTATE] = {
|
||||||
|
.core_note_type = NT_X86_XSTATE,
|
||||||
|
.size = sizeof(u64), .align = sizeof(u64),
|
||||||
|
.active = xstateregs_active, .get = xstateregs_get,
|
||||||
|
.set = xstateregs_set
|
||||||
|
},
|
||||||
[REGSET_TLS] = {
|
[REGSET_TLS] = {
|
||||||
.core_note_type = NT_386_TLS,
|
.core_note_type = NT_386_TLS,
|
||||||
.n = GDT_ENTRY_TLS_ENTRIES, .bias = GDT_ENTRY_TLS_MIN,
|
.n = GDT_ENTRY_TLS_ENTRIES, .bias = GDT_ENTRY_TLS_MIN,
|
||||||
@@ -1642,6 +1655,23 @@ static const struct user_regset_view user_x86_32_view = {
|
|||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This represents bytes 464..511 in the memory layout exported through
|
||||||
|
* the REGSET_XSTATE interface.
|
||||||
|
*/
|
||||||
|
u64 xstate_fx_sw_bytes[USER_XSTATE_FX_SW_WORDS];
|
||||||
|
|
||||||
|
void update_regset_xstate_info(unsigned int size, u64 xstate_mask)
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_X86_64
|
||||||
|
x86_64_regsets[REGSET_XSTATE].n = size / sizeof(u64);
|
||||||
|
#endif
|
||||||
|
#if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION
|
||||||
|
x86_32_regsets[REGSET_XSTATE].n = size / sizeof(u64);
|
||||||
|
#endif
|
||||||
|
xstate_fx_sw_bytes[USER_XSTATE_XCR0_WORD] = xstate_mask;
|
||||||
|
}
|
||||||
|
|
||||||
const struct user_regset_view *task_user_regset_view(struct task_struct *task)
|
const struct user_regset_view *task_user_regset_view(struct task_struct *task)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_IA32_EMULATION
|
#ifdef CONFIG_IA32_EMULATION
|
||||||
|
@@ -337,6 +337,7 @@ void __ref xsave_cntxt_init(void)
|
|||||||
cpuid_count(0xd, 0, &eax, &ebx, &ecx, &edx);
|
cpuid_count(0xd, 0, &eax, &ebx, &ecx, &edx);
|
||||||
xstate_size = ebx;
|
xstate_size = ebx;
|
||||||
|
|
||||||
|
update_regset_xstate_info(xstate_size, pcntxt_mask);
|
||||||
prepare_fx_sw_frame();
|
prepare_fx_sw_frame();
|
||||||
|
|
||||||
setup_xstate_init();
|
setup_xstate_init();
|
||||||
|
@@ -349,7 +349,11 @@ typedef struct elf64_shdr {
|
|||||||
#define ELF_OSABI ELFOSABI_NONE
|
#define ELF_OSABI ELFOSABI_NONE
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Notes used in ET_CORE */
|
/*
|
||||||
|
* Notes used in ET_CORE. Architectures export some of the arch register sets
|
||||||
|
* using the corresponding note types via the PTRACE_GETREGSET and
|
||||||
|
* PTRACE_SETREGSET requests.
|
||||||
|
*/
|
||||||
#define NT_PRSTATUS 1
|
#define NT_PRSTATUS 1
|
||||||
#define NT_PRFPREG 2
|
#define NT_PRFPREG 2
|
||||||
#define NT_PRPSINFO 3
|
#define NT_PRPSINFO 3
|
||||||
@@ -361,6 +365,7 @@ typedef struct elf64_shdr {
|
|||||||
#define NT_PPC_VSX 0x102 /* PowerPC VSX registers */
|
#define NT_PPC_VSX 0x102 /* PowerPC VSX registers */
|
||||||
#define NT_386_TLS 0x200 /* i386 TLS slots (struct user_desc) */
|
#define NT_386_TLS 0x200 /* i386 TLS slots (struct user_desc) */
|
||||||
#define NT_386_IOPERM 0x201 /* x86 io permission bitmap (1=deny) */
|
#define NT_386_IOPERM 0x201 /* x86 io permission bitmap (1=deny) */
|
||||||
|
#define NT_X86_XSTATE 0x202 /* x86 extended state using xsave */
|
||||||
#define NT_S390_HIGH_GPRS 0x300 /* s390 upper register halves */
|
#define NT_S390_HIGH_GPRS 0x300 /* s390 upper register halves */
|
||||||
#define NT_S390_TIMER 0x301 /* s390 timer register */
|
#define NT_S390_TIMER 0x301 /* s390 timer register */
|
||||||
#define NT_S390_TODCMP 0x302 /* s390 TOD clock comparator register */
|
#define NT_S390_TODCMP 0x302 /* s390 TOD clock comparator register */
|
||||||
|
@@ -27,6 +27,26 @@
|
|||||||
#define PTRACE_GETSIGINFO 0x4202
|
#define PTRACE_GETSIGINFO 0x4202
|
||||||
#define PTRACE_SETSIGINFO 0x4203
|
#define PTRACE_SETSIGINFO 0x4203
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generic ptrace interface that exports the architecture specific regsets
|
||||||
|
* using the corresponding NT_* types (which are also used in the core dump).
|
||||||
|
* Please note that the NT_PRSTATUS note type in a core dump contains a full
|
||||||
|
* 'struct elf_prstatus'. But the user_regset for NT_PRSTATUS contains just the
|
||||||
|
* elf_gregset_t that is the pr_reg field of 'struct elf_prstatus'. For all the
|
||||||
|
* other user_regset flavors, the user_regset layout and the ELF core dump note
|
||||||
|
* payload are exactly the same layout.
|
||||||
|
*
|
||||||
|
* This interface usage is as follows:
|
||||||
|
* struct iovec iov = { buf, len};
|
||||||
|
*
|
||||||
|
* ret = ptrace(PTRACE_GETREGSET/PTRACE_SETREGSET, pid, NT_XXX_TYPE, &iov);
|
||||||
|
*
|
||||||
|
* On the successful completion, iov.len will be updated by the kernel,
|
||||||
|
* specifying how much the kernel has written/read to/from the user's iov.buf.
|
||||||
|
*/
|
||||||
|
#define PTRACE_GETREGSET 0x4204
|
||||||
|
#define PTRACE_SETREGSET 0x4205
|
||||||
|
|
||||||
/* options set using PTRACE_SETOPTIONS */
|
/* options set using PTRACE_SETOPTIONS */
|
||||||
#define PTRACE_O_TRACESYSGOOD 0x00000001
|
#define PTRACE_O_TRACESYSGOOD 0x00000001
|
||||||
#define PTRACE_O_TRACEFORK 0x00000002
|
#define PTRACE_O_TRACEFORK 0x00000002
|
||||||
|
@@ -22,6 +22,7 @@
|
|||||||
#include <linux/pid_namespace.h>
|
#include <linux/pid_namespace.h>
|
||||||
#include <linux/syscalls.h>
|
#include <linux/syscalls.h>
|
||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
|
#include <linux/regset.h>
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -511,6 +512,47 @@ static int ptrace_resume(struct task_struct *child, long request, long data)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_HAVE_ARCH_TRACEHOOK
|
||||||
|
|
||||||
|
static const struct user_regset *
|
||||||
|
find_regset(const struct user_regset_view *view, unsigned int type)
|
||||||
|
{
|
||||||
|
const struct user_regset *regset;
|
||||||
|
int n;
|
||||||
|
|
||||||
|
for (n = 0; n < view->n; ++n) {
|
||||||
|
regset = view->regsets + n;
|
||||||
|
if (regset->core_note_type == type)
|
||||||
|
return regset;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ptrace_regset(struct task_struct *task, int req, unsigned int type,
|
||||||
|
struct iovec *kiov)
|
||||||
|
{
|
||||||
|
const struct user_regset_view *view = task_user_regset_view(task);
|
||||||
|
const struct user_regset *regset = find_regset(view, type);
|
||||||
|
int regset_no;
|
||||||
|
|
||||||
|
if (!regset || (kiov->iov_len % regset->size) != 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
regset_no = regset - view->regsets;
|
||||||
|
kiov->iov_len = min(kiov->iov_len,
|
||||||
|
(__kernel_size_t) (regset->n * regset->size));
|
||||||
|
|
||||||
|
if (req == PTRACE_GETREGSET)
|
||||||
|
return copy_regset_to_user(task, view, regset_no, 0,
|
||||||
|
kiov->iov_len, kiov->iov_base);
|
||||||
|
else
|
||||||
|
return copy_regset_from_user(task, view, regset_no, 0,
|
||||||
|
kiov->iov_len, kiov->iov_base);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
int ptrace_request(struct task_struct *child, long request,
|
int ptrace_request(struct task_struct *child, long request,
|
||||||
long addr, long data)
|
long addr, long data)
|
||||||
{
|
{
|
||||||
@@ -573,6 +615,26 @@ int ptrace_request(struct task_struct *child, long request,
|
|||||||
return 0;
|
return 0;
|
||||||
return ptrace_resume(child, request, SIGKILL);
|
return ptrace_resume(child, request, SIGKILL);
|
||||||
|
|
||||||
|
#ifdef CONFIG_HAVE_ARCH_TRACEHOOK
|
||||||
|
case PTRACE_GETREGSET:
|
||||||
|
case PTRACE_SETREGSET:
|
||||||
|
{
|
||||||
|
struct iovec kiov;
|
||||||
|
struct iovec __user *uiov = (struct iovec __user *) data;
|
||||||
|
|
||||||
|
if (!access_ok(VERIFY_WRITE, uiov, sizeof(*uiov)))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
if (__get_user(kiov.iov_base, &uiov->iov_base) ||
|
||||||
|
__get_user(kiov.iov_len, &uiov->iov_len))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
ret = ptrace_regset(child, request, addr, &kiov);
|
||||||
|
if (!ret)
|
||||||
|
ret = __put_user(kiov.iov_len, &uiov->iov_len);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -711,6 +773,32 @@ int compat_ptrace_request(struct task_struct *child, compat_long_t request,
|
|||||||
else
|
else
|
||||||
ret = ptrace_setsiginfo(child, &siginfo);
|
ret = ptrace_setsiginfo(child, &siginfo);
|
||||||
break;
|
break;
|
||||||
|
#ifdef CONFIG_HAVE_ARCH_TRACEHOOK
|
||||||
|
case PTRACE_GETREGSET:
|
||||||
|
case PTRACE_SETREGSET:
|
||||||
|
{
|
||||||
|
struct iovec kiov;
|
||||||
|
struct compat_iovec __user *uiov =
|
||||||
|
(struct compat_iovec __user *) datap;
|
||||||
|
compat_uptr_t ptr;
|
||||||
|
compat_size_t len;
|
||||||
|
|
||||||
|
if (!access_ok(VERIFY_WRITE, uiov, sizeof(*uiov)))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
if (__get_user(ptr, &uiov->iov_base) ||
|
||||||
|
__get_user(len, &uiov->iov_len))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
kiov.iov_base = compat_ptr(ptr);
|
||||||
|
kiov.iov_len = len;
|
||||||
|
|
||||||
|
ret = ptrace_regset(child, request, addr, &kiov);
|
||||||
|
if (!ret)
|
||||||
|
ret = __put_user(kiov.iov_len, &uiov->iov_len);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
default:
|
default:
|
||||||
ret = ptrace_request(child, request, addr, data);
|
ret = ptrace_request(child, request, addr, data);
|
||||||
|
Reference in New Issue
Block a user