Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3578baae authored by H. Peter Anvin's avatar H. Peter Anvin
Browse files

x86, mm: Redesign get_user with a __builtin_choose_expr hack

Instead of using a bitfield, use an odd little trick using typeof,
__builtin_choose_expr, and sizeof.  __builtin_choose_expr is
explicitly defined to not convert its type (its argument is required
to be a constant expression) so this should be well-defined.

The code is still not 100% preturbation-free versus the baseline
before 64-bit get_user(), but the differences seem to be very small,
mostly related to padding and to gcc deciding when to spill registers.

Cc: Jamie Lokier <jamie@shareable.org>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Russell King <linux@arm.linux.org.uk>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: H. J. Lu <hjl.tools@gmail.com>
Link: http://lkml.kernel.org/r/511A8922.6050908@zytor.com


Signed-off-by: default avatarH. Peter Anvin <hpa@linux.intel.com>
parent 16640165
Loading
Loading
Loading
Loading
+14 −43
Original line number Diff line number Diff line
@@ -125,13 +125,12 @@ extern int __get_user_4(void);
extern int __get_user_8(void);
extern int __get_user_bad(void);

#define __get_user_x(size, ret, x, ptr)		      \
	asm volatile("call __get_user_" #size	      \
		     : "=a" (ret), "=d" (x)	      \
		     : "0" (ptr))		      \

/* Careful: we have to cast the result to the type of the pointer
 * for sign reasons */
/*
 * This is a type: either unsigned long, if the argument fits into
 * that type, or otherwise unsigned long long.
 */
#define __inttype(x) \
__typeof__(__builtin_choose_expr(sizeof(x) > sizeof(0UL), 0ULL, 0UL))

/**
 * get_user: - Get a simple variable from user space.
@@ -149,48 +148,20 @@ extern int __get_user_bad(void);
 *
 * Returns zero on success, or -EFAULT on error.
 * On error, the variable @x is set to zero.
 *
 * Careful: we have to cast the result to the type of the pointer
 * for sign reasons.
 */
#ifdef CONFIG_X86_32
#define __get_user_8(ret, x, ptr)		      \
do {						      \
	register unsigned long long __xx asm("%edx"); \
	asm volatile("call __get_user_8"	      \
		     : "=a" (ret), "=r" (__xx)	      \
		     : "0" (ptr));		      \
	(x) = __xx;				      \
} while (0)

#else
#define __get_user_8(__ret_gu, __val_gu, ptr)				\
		__get_user_x(8, __ret_gu, __val_gu, ptr)
#endif

#define get_user(x, ptr)						\
({									\
	int __ret_gu;							\
	struct {							\
		unsigned long long __val_n : 8*sizeof(*(ptr));		\
	} __val_gu;							\
	register __inttype(*(ptr)) __val_gu asm("%edx");		\
	__chk_user_ptr(ptr);						\
	might_fault();							\
	switch (sizeof(*(ptr))) {					\
	case 1:								\
		__get_user_x(1, __ret_gu, __val_gu.__val_n, ptr);	\
		break;							\
	case 2:								\
		__get_user_x(2, __ret_gu, __val_gu.__val_n, ptr);	\
		break;							\
	case 4:								\
		__get_user_x(4, __ret_gu, __val_gu.__val_n, ptr);	\
		break;							\
	case 8:								\
		__get_user_8(__ret_gu, __val_gu.__val_n, ptr);		\
		break;							\
	default:							\
		__get_user_x(X, __ret_gu, __val_gu.__val_n, ptr);	\
		break;							\
	}								\
	(x) = (__typeof__(*(ptr)))__val_gu.__val_n;			\
	asm volatile("call __get_user_%P3"				\
		     : "=a" (__ret_gu), "=r" (__val_gu)			\
		     : "0" (ptr), "i" (sizeof(*(ptr))));		\
	(x) = (__typeof__(*(ptr))) __val_gu;				\
	__ret_gu;							\
})