Commit 0557d64d authored by Al Viro's avatar Al Viro
Browse files

x86: switch to ->regset_get()



	All instances of ->get() in arch/x86 switched; that might or might
not be worth splitting up.  Notes:

	* for xstateregs_get() the amount we want to store is determined at
the boot time; see init_xstate_size() and update_regset_xstate_info() for
details.  task->thread.fpu.state.xsave ends with a flexible array member and
the amount of data in it depends upon the FPU features supported/enabled.

	* fpregs_get() writes slightly less than full ->thread.fpu.state.fsave
(the last word is not copied); we pass the full size of state.fsave and let
membuf_write() trim to the amount declared by regset - __regset_get() will
make sure that the space in buffer is no more than that.

	* copy_xstate_to_user() and its helpers are gone now.

	* fpregs_soft_get() was getting user_regset_copyout() arguments
wrong.  Since "x86: x86 user_regset math_emu" back in 2008...  I really
doubt that it's worth splitting out for -stable, though - you need
a 486SX box for that to trigger...

[Kevin's braino fix for copy_xstate_to_kernel() essentially duplicated here]

Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 7717cb9b
Loading
Loading
Loading
Loading
+2 −2
Original line number Original line Diff line number Diff line
@@ -8,7 +8,7 @@
#include <linux/regset.h>
#include <linux/regset.h>


extern user_regset_active_fn regset_fpregs_active, regset_xregset_fpregs_active;
extern user_regset_active_fn regset_fpregs_active, regset_xregset_fpregs_active;
extern user_regset_get_fn fpregs_get, xfpregs_get, fpregs_soft_get,
extern user_regset_get2_fn fpregs_get, xfpregs_get, fpregs_soft_get,
				 xstateregs_get;
				 xstateregs_get;
extern user_regset_set_fn fpregs_set, xfpregs_set, fpregs_soft_set,
extern user_regset_set_fn fpregs_set, xfpregs_set, fpregs_soft_set,
				 xstateregs_set;
				 xstateregs_set;
+2 −2
Original line number Original line Diff line number Diff line
@@ -71,8 +71,8 @@ extern void __init update_regset_xstate_info(unsigned int size,
void *get_xsave_addr(struct xregs_state *xsave, int xfeature_nr);
void *get_xsave_addr(struct xregs_state *xsave, int xfeature_nr);
const void *get_xsave_field_ptr(int xfeature_nr);
const void *get_xsave_field_ptr(int xfeature_nr);
int using_compacted_format(void);
int using_compacted_format(void);
int copy_xstate_to_kernel(void *kbuf, struct xregs_state *xsave, unsigned int offset, unsigned int size);
struct membuf;
int copy_xstate_to_user(void __user *ubuf, struct xregs_state *xsave, unsigned int offset, unsigned int size);
void copy_xstate_to_kernel(struct membuf to, struct xregs_state *xsave);
int copy_kernel_to_xstate(struct xregs_state *xsave, const void *kbuf);
int copy_kernel_to_xstate(struct xregs_state *xsave, const void *kbuf);
int copy_user_to_xstate(struct xregs_state *xsave, const void __user *ubuf);
int copy_user_to_xstate(struct xregs_state *xsave, const void __user *ubuf);
void copy_supervisor_to_kernel(struct xregs_state *xsave);
void copy_supervisor_to_kernel(struct xregs_state *xsave);
+15 −24
Original line number Original line Diff line number Diff line
@@ -27,8 +27,7 @@ int regset_xregset_fpregs_active(struct task_struct *target, const struct user_r
}
}


int xfpregs_get(struct task_struct *target, const struct user_regset *regset,
int xfpregs_get(struct task_struct *target, const struct user_regset *regset,
		unsigned int pos, unsigned int count,
		struct membuf to)
		void *kbuf, void __user *ubuf)
{
{
	struct fpu *fpu = &target->thread.fpu;
	struct fpu *fpu = &target->thread.fpu;


@@ -38,8 +37,7 @@ int xfpregs_get(struct task_struct *target, const struct user_regset *regset,
	fpu__prepare_read(fpu);
	fpu__prepare_read(fpu);
	fpstate_sanitize_xstate(fpu);
	fpstate_sanitize_xstate(fpu);


	return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
	return membuf_write(&to, &fpu->state.fxsave, sizeof(struct fxregs_state));
				   &fpu->state.fxsave, 0, -1);
}
}


int xfpregs_set(struct task_struct *target, const struct user_regset *regset,
int xfpregs_set(struct task_struct *target, const struct user_regset *regset,
@@ -74,12 +72,10 @@ int xfpregs_set(struct task_struct *target, const struct user_regset *regset,
}
}


int xstateregs_get(struct task_struct *target, const struct user_regset *regset,
int xstateregs_get(struct task_struct *target, const struct user_regset *regset,
		unsigned int pos, unsigned int count,
		struct membuf to)
		void *kbuf, void __user *ubuf)
{
{
	struct fpu *fpu = &target->thread.fpu;
	struct fpu *fpu = &target->thread.fpu;
	struct xregs_state *xsave;
	struct xregs_state *xsave;
	int ret;


	if (!boot_cpu_has(X86_FEATURE_XSAVE))
	if (!boot_cpu_has(X86_FEATURE_XSAVE))
		return -ENODEV;
		return -ENODEV;
@@ -89,10 +85,8 @@ int xstateregs_get(struct task_struct *target, const struct user_regset *regset,
	fpu__prepare_read(fpu);
	fpu__prepare_read(fpu);


	if (using_compacted_format()) {
	if (using_compacted_format()) {
		if (kbuf)
		copy_xstate_to_kernel(to, xsave);
			ret = copy_xstate_to_kernel(kbuf, xsave, pos, count);
		return 0;
		else
			ret = copy_xstate_to_user(ubuf, xsave, pos, count);
	} else {
	} else {
		fpstate_sanitize_xstate(fpu);
		fpstate_sanitize_xstate(fpu);
		/*
		/*
@@ -105,9 +99,8 @@ int xstateregs_get(struct task_struct *target, const struct user_regset *regset,
		/*
		/*
		 * Copy the xstate memory layout.
		 * Copy the xstate memory layout.
		 */
		 */
		ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, xsave, 0, -1);
		return membuf_write(&to, xsave, fpu_user_xstate_size);
	}
	}
	return ret;
}
}


int xstateregs_set(struct task_struct *target, const struct user_regset *regset,
int xstateregs_set(struct task_struct *target, const struct user_regset *regset,
@@ -293,8 +286,7 @@ void convert_to_fxsr(struct fxregs_state *fxsave,
}
}


int fpregs_get(struct task_struct *target, const struct user_regset *regset,
int fpregs_get(struct task_struct *target, const struct user_regset *regset,
	       unsigned int pos, unsigned int count,
	       struct membuf to)
	       void *kbuf, void __user *ubuf)
{
{
	struct fpu *fpu = &target->thread.fpu;
	struct fpu *fpu = &target->thread.fpu;
	struct user_i387_ia32_struct env;
	struct user_i387_ia32_struct env;
@@ -302,23 +294,22 @@ int fpregs_get(struct task_struct *target, const struct user_regset *regset,
	fpu__prepare_read(fpu);
	fpu__prepare_read(fpu);


	if (!boot_cpu_has(X86_FEATURE_FPU))
	if (!boot_cpu_has(X86_FEATURE_FPU))
		return fpregs_soft_get(target, regset, pos, count, kbuf, ubuf);
		return fpregs_soft_get(target, regset, to);


	if (!boot_cpu_has(X86_FEATURE_FXSR))
	if (!boot_cpu_has(X86_FEATURE_FXSR)) {
		return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
		return membuf_write(&to, &fpu->state.fsave,
					   &fpu->state.fsave, 0,
				    sizeof(struct fregs_state));
					   -1);
	}


	fpstate_sanitize_xstate(fpu);
	fpstate_sanitize_xstate(fpu);


	if (kbuf && pos == 0 && count == sizeof(env)) {
	if (to.left == sizeof(env)) {
		convert_from_fxsr(kbuf, target);
		convert_from_fxsr(to.p, target);
		return 0;
		return 0;
	}
	}


	convert_from_fxsr(&env, target);
	convert_from_fxsr(&env, target);

	return membuf_write(&to, &env, sizeof(env));
	return user_regset_copyout(&pos, &count, &kbuf, &ubuf, &env, 0, -1);
}
}


int fpregs_set(struct task_struct *target, const struct user_regset *regset,
int fpregs_set(struct task_struct *target, const struct user_regset *regset,
+2 −1
Original line number Original line Diff line number Diff line
@@ -172,7 +172,8 @@ int copy_fpstate_to_sigframe(void __user *buf, void __user *buf_fx, int size)


	if (!static_cpu_has(X86_FEATURE_FPU)) {
	if (!static_cpu_has(X86_FEATURE_FPU)) {
		struct user_i387_ia32_struct fp;
		struct user_i387_ia32_struct fp;
		fpregs_soft_get(current, NULL, 0, sizeof(fp), &fp, NULL);
		fpregs_soft_get(current, NULL, (struct membuf){.p = &fp,
						.left = sizeof(fp)});
		return copy_to_user(buf, &fp, sizeof(fp)) ? -EFAULT : 0;
		return copy_to_user(buf, &fp, sizeof(fp)) ? -EFAULT : 0;
	}
	}


+27 −137
Original line number Original line Diff line number Diff line
@@ -1009,32 +1009,20 @@ static inline bool xfeatures_mxcsr_quirk(u64 xfeatures)
	return true;
	return true;
}
}


static void fill_gap(unsigned to, void **kbuf, unsigned *pos, unsigned *count)
static void fill_gap(struct membuf *to, unsigned *last, unsigned offset)
{
{
	if (*pos < to) {
	if (*last >= offset)
		unsigned size = to - *pos;
		return;

	membuf_write(to, (void *)&init_fpstate.xsave + *last, offset - *last);
		if (size > *count)
	*last = offset;
			size = *count;
		memcpy(*kbuf, (void *)&init_fpstate.xsave + *pos, size);
		*kbuf += size;
		*pos += size;
		*count -= size;
	}
}
}


static void copy_part(unsigned offset, unsigned size, void *from,
static void copy_part(struct membuf *to, unsigned *last, unsigned offset,
			void **kbuf, unsigned *pos, unsigned *count)
		      unsigned size, void *from)
{
{
	fill_gap(offset, kbuf, pos, count);
	fill_gap(to, last, offset);
	if (size > *count)
	membuf_write(to, from, size);
		size = *count;
	*last = offset + size;
	if (size) {
		memcpy(*kbuf, from, size);
		*kbuf += size;
		*pos += size;
		*count -= size;
	}
}
}


/*
/*
@@ -1044,19 +1032,14 @@ static void copy_part(unsigned offset, unsigned size, void *from,
 * It supports partial copy but pos always starts from zero. This is called
 * It supports partial copy but pos always starts from zero. This is called
 * from xstateregs_get() and there we check the CPU has XSAVES.
 * from xstateregs_get() and there we check the CPU has XSAVES.
 */
 */
int copy_xstate_to_kernel(void *kbuf, struct xregs_state *xsave, unsigned int offset_start, unsigned int size_total)
void copy_xstate_to_kernel(struct membuf to, struct xregs_state *xsave)
{
{
	struct xstate_header header;
	struct xstate_header header;
	const unsigned off_mxcsr = offsetof(struct fxregs_state, mxcsr);
	const unsigned off_mxcsr = offsetof(struct fxregs_state, mxcsr);
	unsigned count = size_total;
	unsigned size = to.left;
	unsigned last = 0;
	int i;
	int i;


	/*
	 * Currently copy_regset_to_user() starts from pos 0:
	 */
	if (unlikely(offset_start != 0))
		return -EFAULT;

	/*
	/*
	 * The destination is a ptrace buffer; we put in only user xstates:
	 * The destination is a ptrace buffer; we put in only user xstates:
	 */
	 */
@@ -1065,27 +1048,26 @@ int copy_xstate_to_kernel(void *kbuf, struct xregs_state *xsave, unsigned int of
	header.xfeatures &= xfeatures_mask_user();
	header.xfeatures &= xfeatures_mask_user();


	if (header.xfeatures & XFEATURE_MASK_FP)
	if (header.xfeatures & XFEATURE_MASK_FP)
		copy_part(0, off_mxcsr,
		copy_part(&to, &last, 0, off_mxcsr, &xsave->i387);
			  &xsave->i387, &kbuf, &offset_start, &count);
	if (header.xfeatures & (XFEATURE_MASK_SSE | XFEATURE_MASK_YMM))
	if (header.xfeatures & (XFEATURE_MASK_SSE | XFEATURE_MASK_YMM))
		copy_part(off_mxcsr, MXCSR_AND_FLAGS_SIZE,
		copy_part(&to, &last, off_mxcsr,
			  &xsave->i387.mxcsr, &kbuf, &offset_start, &count);
			  MXCSR_AND_FLAGS_SIZE, &xsave->i387.mxcsr);
	if (header.xfeatures & XFEATURE_MASK_FP)
	if (header.xfeatures & XFEATURE_MASK_FP)
		copy_part(offsetof(struct fxregs_state, st_space), 128,
		copy_part(&to, &last, offsetof(struct fxregs_state, st_space),
			  &xsave->i387.st_space, &kbuf, &offset_start, &count);
			  128, &xsave->i387.st_space);
	if (header.xfeatures & XFEATURE_MASK_SSE)
	if (header.xfeatures & XFEATURE_MASK_SSE)
		copy_part(xstate_offsets[XFEATURE_MASK_SSE], 256,
		copy_part(&to, &last, xstate_offsets[XFEATURE_SSE],
			  &xsave->i387.xmm_space, &kbuf, &offset_start, &count);
			  256, &xsave->i387.xmm_space);
	/*
	/*
	 * Fill xsave->i387.sw_reserved value for ptrace frame:
	 * Fill xsave->i387.sw_reserved value for ptrace frame:
	 */
	 */
	copy_part(offsetof(struct fxregs_state, sw_reserved), 48,
	copy_part(&to, &last, offsetof(struct fxregs_state, sw_reserved),
		  xstate_fx_sw_bytes, &kbuf, &offset_start, &count);
		  48, xstate_fx_sw_bytes);
	/*
	/*
	 * Copy xregs_state->header:
	 * Copy xregs_state->header:
	 */
	 */
	copy_part(offsetof(struct xregs_state, header), sizeof(header),
	copy_part(&to, &last, offsetof(struct xregs_state, header),
		  &header, &kbuf, &offset_start, &count);
		  sizeof(header), &header);


	for (i = FIRST_EXTENDED_XFEATURE; i < XFEATURE_MAX; i++) {
	for (i = FIRST_EXTENDED_XFEATURE; i < XFEATURE_MAX; i++) {
		/*
		/*
@@ -1094,104 +1076,12 @@ int copy_xstate_to_kernel(void *kbuf, struct xregs_state *xsave, unsigned int of
		if ((header.xfeatures >> i) & 1) {
		if ((header.xfeatures >> i) & 1) {
			void *src = __raw_xsave_addr(xsave, i);
			void *src = __raw_xsave_addr(xsave, i);


			copy_part(xstate_offsets[i], xstate_sizes[i],
			copy_part(&to, &last, xstate_offsets[i],
				  src, &kbuf, &offset_start, &count);
				  xstate_sizes[i], src);
		}

	}
	fill_gap(size_total, &kbuf, &offset_start, &count);

	return 0;
}

static inline int
__copy_xstate_to_user(void __user *ubuf, const void *data, unsigned int offset, unsigned int size, unsigned int size_total)
{
	if (!size)
		return 0;

	if (offset < size_total) {
		unsigned int copy = min(size, size_total - offset);

		if (__copy_to_user(ubuf + offset, data, copy))
			return -EFAULT;
	}
	return 0;
}

/*
 * Convert from kernel XSAVES compacted format to standard format and copy
 * to a user-space buffer. It supports partial copy but pos always starts from
 * zero. This is called from xstateregs_get() and there we check the CPU
 * has XSAVES.
 */
int copy_xstate_to_user(void __user *ubuf, struct xregs_state *xsave, unsigned int offset_start, unsigned int size_total)
{
	unsigned int offset, size;
	int ret, i;
	struct xstate_header header;

	/*
	 * Currently copy_regset_to_user() starts from pos 0:
	 */
	if (unlikely(offset_start != 0))
		return -EFAULT;

	/*
	 * The destination is a ptrace buffer; we put in only user xstates:
	 */
	memset(&header, 0, sizeof(header));
	header.xfeatures = xsave->header.xfeatures;
	header.xfeatures &= xfeatures_mask_user();

	/*
	 * Copy xregs_state->header:
	 */
	offset = offsetof(struct xregs_state, header);
	size = sizeof(header);

	ret = __copy_xstate_to_user(ubuf, &header, offset, size, size_total);
	if (ret)
		return ret;

	for (i = 0; i < XFEATURE_MAX; i++) {
		/*
		 * Copy only in-use xstates:
		 */
		if ((header.xfeatures >> i) & 1) {
			void *src = __raw_xsave_addr(xsave, i);

			offset = xstate_offsets[i];
			size = xstate_sizes[i];

			/* The next component has to fit fully into the output buffer: */
			if (offset + size > size_total)
				break;

			ret = __copy_xstate_to_user(ubuf, src, offset, size, size_total);
			if (ret)
				return ret;
		}
		}


	}
	}

	fill_gap(&to, &last, size);
	if (xfeatures_mxcsr_quirk(header.xfeatures)) {
		offset = offsetof(struct fxregs_state, mxcsr);
		size = MXCSR_AND_FLAGS_SIZE;
		__copy_xstate_to_user(ubuf, &xsave->i387.mxcsr, offset, size, size_total);
	}

	/*
	 * Fill xsave->i387.sw_reserved value for ptrace frame:
	 */
	offset = offsetof(struct fxregs_state, sw_reserved);
	size = sizeof(xstate_fx_sw_bytes);

	ret = __copy_xstate_to_user(ubuf, xstate_fx_sw_bytes, offset, size, size_total);
	if (ret)
		return ret;

	return 0;
}
}


/*
/*
Loading