public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
From: Jamie Lokier <jamie@shareable.org>
To: Linus Torvalds <torvalds@osdl.org>
Cc: chris@scary.beasts.org, Andrew Morton <akpm@osdl.org>,
	Kernel Mailing List <linux-kernel@vger.kernel.org>
Subject: Re: [PATCH] Add 64-bit get_user and __get_user for i386
Date: Tue, 20 Apr 2004 18:41:47 +0100	[thread overview]
Message-ID: <20040420174147.GA20924@mail.shareable.org> (raw)
In-Reply-To: <Pine.LNX.4.58.0404191945490.29941@ppc970.osdl.org>

Linus Torvalds wrote:
> On Tue, 20 Apr 2004, Jamie Lokier wrote:
> > Changed the registers for all __get_user_X so they could all be
> > consistent: %ecx now contains the address, and %eax or %edx:%eax
> > contain the result.
> 
> This clobbers unnecessary registers. You now use three registers instead 
> of just two.

Wrong.  It uses two registers for the 1/2/4-byte cases (%ecx and %eax).
It uses three for 8-byte, which is necessary: 8-byte result + error code.

That's achieved by using different asm output constraints: "=a" for
1/2/4 bytes and "=A" for 8 bytes.

> > There was a boundary bug in getuser.S: get_user(x,(char *)0xbfffffff)
> > would fail.  Fixed.
> 
> Hmm. I'll believe you.

Just look, it's obvious.  This is the old code 

__get_user_4:
	addl $3,%eax
	jc bad_get_user
	GET_THREAD_INFO(%edx)
	cmpl TI_ADDR_LIMIT(%edx),%eax
	jae bad_get_user

Change "jae" to "ja" and it's fine.  (x86_64 has copied this bug).

> > +/*
> > + * Careful: we have to cast the result to the type of the pointer for
> > + * sign reasons.
> > + */
> ...
> > +	__typeof__(*(ptr)) __gu_val;					\
> ...
> > +	(x) = (__typeof__(*(ptr)))__gu_val;				\
> 
> And exactly why do you want to cast the value to a type that it already 
> has?

Well spotted.  It's an artifact of experiments with __gu_val being
long, long long etc. for various sizes of get_user() and seeing
unusual warnings when *ptr and/or x are pointers.

Patch re-rolled.  The only change is the removal of that cast,
and a clarifying comment in getuser.S.

I've checked it compiles to identical code.

Enjoy,
-- Jamie


Subject: [PATCH] Add 64-bit get_user and __get_user for i386
Patch: uaccess64-2.6.5-jl4
 
Added 64-bit get_user and __get_user for i386.
 
Changed the registers for all __get_user_X so they could all be
consistent: %ecx now contains the address, and %eax or %edx:%eax
contain the result.
 
There was a boundary bug in getuser.S: get_user(x,(char *)0xbfffffff)
would fail.  Fixed.
 
Added might_sleep() to get_user().
 
Added the 8-byte constant-length cases for __copy_from_user() and
__copy_to_user().

Changed constant-length short copy_from_user() to be out of line (it was
inline), as this is just another way to write get_user().  It calls the
same routines as get_user() and one test showed equivalent uses now
generate identical code (sendfile64).  Constant-length
__copy_from_user() remains inline, just like __get_user().
 
Very slight cosmetic moving of a few definitions into a more
consistent arrangement.
 
This patch shaved about 1.2k off my kernel, due to the un-inlining of
copy_from_user() calls which are equivalent to get_user().



diff -urN --exclude-from=dontdiff dual-2.6.5/arch/i386/kernel/i386_ksyms.c uaccess64-2.6.5/arch/i386/kernel/i386_ksyms.c
--- dual-2.6.5/arch/i386/kernel/i386_ksyms.c	2004-04-14 08:28:07.000000000 +0100
+++ uaccess64-2.6.5/arch/i386/kernel/i386_ksyms.c	2004-04-20 01:55:10.000000000 +0100
@@ -103,6 +103,7 @@
 EXPORT_SYMBOL_NOVERS(__get_user_1);
 EXPORT_SYMBOL_NOVERS(__get_user_2);
 EXPORT_SYMBOL_NOVERS(__get_user_4);
+EXPORT_SYMBOL_NOVERS(__get_user_8);
 
 EXPORT_SYMBOL(strpbrk);
 EXPORT_SYMBOL(strstr);
diff -urN --exclude-from=dontdiff dual-2.6.5/arch/i386/lib/getuser.S uaccess64-2.6.5/arch/i386/lib/getuser.S
--- dual-2.6.5/arch/i386/lib/getuser.S	2003-12-18 02:57:59.000000000 +0000
+++ uaccess64-2.6.5/arch/i386/lib/getuser.S	2004-04-20 18:15:24.000000000 +0100
@@ -14,10 +14,11 @@
 /*
  * __get_user_X
  *
- * Inputs:	%eax contains the address
+ * Inputs:	%ecx contains the address
  *
- * Outputs:	%eax is error code (0 or -EFAULT)
- *		%edx contains zero-extended value
+ * Outputs:	%ecx is error code (0 or -EFAULT)
+ *		%eax contains zero-extended value
+ *		%edx is high part of 64 bit result, or preserved if <= 32 bits
  *
  * These functions should not modify any other registers,
  * as they get called from within inline assembly.
@@ -27,44 +28,61 @@
 .align 4
 .globl __get_user_1
 __get_user_1:
-	GET_THREAD_INFO(%edx)
-	cmpl TI_ADDR_LIMIT(%edx),%eax
-	jae bad_get_user
-1:	movzbl (%eax),%edx
-	xorl %eax,%eax
+	GET_THREAD_INFO(%eax)
+	cmpl TI_ADDR_LIMIT(%eax),%ecx
+	ja bad_get_user
+1:	movzbl (%ecx),%eax
+	xorl %ecx,%ecx
 	ret
 
 .align 4
 .globl __get_user_2
 __get_user_2:
-	addl $1,%eax
+	addl $1,%ecx
 	jc bad_get_user
-	GET_THREAD_INFO(%edx)
-	cmpl TI_ADDR_LIMIT(%edx),%eax
-	jae bad_get_user
-2:	movzwl -1(%eax),%edx
-	xorl %eax,%eax
+	GET_THREAD_INFO(%eax)
+	cmpl TI_ADDR_LIMIT(%eax),%ecx
+	ja bad_get_user
+2:	movzwl -1(%ecx),%eax
+	xorl %ecx,%ecx
 	ret
 
 .align 4
 .globl __get_user_4
 __get_user_4:
-	addl $3,%eax
+	addl $3,%ecx
 	jc bad_get_user
-	GET_THREAD_INFO(%edx)
-	cmpl TI_ADDR_LIMIT(%edx),%eax
-	jae bad_get_user
-3:	movl -3(%eax),%edx
-	xorl %eax,%eax
+	GET_THREAD_INFO(%eax)
+	cmpl TI_ADDR_LIMIT(%eax),%ecx
+	ja bad_get_user
+3:	movl -3(%ecx),%eax
+	xorl %ecx,%ecx
 	ret
 
-bad_get_user:
+.align 4
+.globl __get_user_8
+__get_user_8:
+	addl $7,%ecx
+	jc bad_get_user_8
+	GET_THREAD_INFO(%eax)
+	cmpl TI_ADDR_LIMIT(%eax),%ecx
+	ja bad_get_user_8
+4:	movl -7(%ecx),%eax
+5:	movl -3(%ecx),%edx
+	xorl %ecx,%ecx
+	ret
+
+bad_get_user_8:
 	xorl %edx,%edx
-	movl $-14,%eax
+bad_get_user:
+	xorl %eax,%eax
+	movl $-14,%ecx
 	ret
 
 .section __ex_table,"a"
 	.long 1b,bad_get_user
 	.long 2b,bad_get_user
 	.long 3b,bad_get_user
+	.long 4b,bad_get_user_8
+	.long 5b,bad_get_user_8
 .previous
diff -urN --exclude-from=dontdiff dual-2.6.5/include/asm-i386/uaccess.h uaccess64-2.6.5/include/asm-i386/uaccess.h
--- dual-2.6.5/include/asm-i386/uaccess.h	2003-12-18 02:58:16.000000000 +0000
+++ uaccess64-2.6.5/include/asm-i386/uaccess.h	2004-04-20 17:49:53.000000000 +0100
@@ -140,17 +140,7 @@
  * accesses to the same area of user memory).
  */
 
-extern void __get_user_1(void);
-extern void __get_user_2(void);
-extern void __get_user_4(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 */
 /**
  * get_user: - Get a simple variable from user space.
  * @x:   Variable to store result.
@@ -168,19 +158,36 @@
  * 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.
+ */
 #define get_user(x,ptr)							\
-({	int __ret_gu,__val_gu;						\
+({									\
+	__typeof__(*(ptr)) __gu_val;					\
+	int __gu_err;							\
+	might_sleep();							\
 	switch(sizeof (*(ptr))) {					\
-	case 1:  __get_user_x(1,__ret_gu,__val_gu,ptr); break;		\
-	case 2:  __get_user_x(2,__ret_gu,__val_gu,ptr); break;		\
-	case 4:  __get_user_x(4,__ret_gu,__val_gu,ptr); break;		\
-	default: __get_user_x(X,__ret_gu,__val_gu,ptr); break;		\
+	case 1: __get_user_x(1,__gu_val,ptr,__gu_err,"=a"); break;	\
+	case 2: __get_user_x(2,__gu_val,ptr,__gu_err,"=a"); break;	\
+	case 4: __get_user_x(4,__gu_val,ptr,__gu_err,"=a"); break;	\
+	case 8: __get_user_x(8,__gu_val,ptr,__gu_err,"=A"); break;	\
+	default: __get_user_bad(); __gu_val = 0; __gu_err = 0;		\
 	}								\
-	(x) = (__typeof__(*(ptr)))__val_gu;				\
-	__ret_gu;							\
+	(x) = __gu_val;							\
+	__gu_err;							\
 })
 
-extern void __put_user_bad(void);
+extern void __get_user_1(void);
+extern void __get_user_2(void);
+extern void __get_user_4(void);
+extern void __get_user_8(void);
+
+#define __get_user_x(size, x, ptr, err, rtype)		\
+	__asm__ __volatile__("call __get_user_" #size	\
+		: "=c" (err), rtype (x)			\
+		: "0" (ptr));
+
 
 /**
  * put_user: - Write a simple value into user space.
@@ -255,33 +262,17 @@
 	__pu_err;						\
 })
 
-
 #define __put_user_check(x,ptr,size)					\
 ({									\
 	long __pu_err = -EFAULT;					\
 	__typeof__(*(ptr)) *__pu_addr = (ptr);				\
-	might_sleep();						\
+	might_sleep();							\
 	if (access_ok(VERIFY_WRITE,__pu_addr,size))			\
 		__put_user_size((x),__pu_addr,(size),__pu_err,-EFAULT);	\
 	__pu_err;							\
 })							
 
-#define __put_user_u64(x, addr, err)				\
-	__asm__ __volatile__(					\
-		"1:	movl %%eax,0(%2)\n"			\
-		"2:	movl %%edx,4(%2)\n"			\
-		"3:\n"						\
-		".section .fixup,\"ax\"\n"			\
-		"4:	movl %3,%0\n"				\
-		"	jmp 3b\n"				\
-		".previous\n"					\
-		".section __ex_table,\"a\"\n"			\
-		"	.align 4\n"				\
-		"	.long 1b,4b\n"				\
-		"	.long 2b,4b\n"				\
-		".previous"					\
-		: "=r"(err)					\
-		: "A" (x), "r" (addr), "i"(-EFAULT), "0"(err))
+extern void __put_user_bad(void);
 
 #ifdef CONFIG_X86_WP_WORKS_OK
 
@@ -292,8 +283,8 @@
 	case 1: __put_user_asm(x,ptr,retval,"b","b","iq",errret);break;	\
 	case 2: __put_user_asm(x,ptr,retval,"w","w","ir",errret);break; \
 	case 4: __put_user_asm(x,ptr,retval,"l","","ir",errret); break;	\
-	case 8: __put_user_u64((__typeof__(*ptr))(x),ptr,retval); break;\
-	  default: __put_user_bad();					\
+	case 8: __put_user_u64(x,ptr,retval,errret); break;		\
+	default: __put_user_bad();					\
 	}								\
 } while (0)
 
@@ -301,7 +292,7 @@
 
 #define __put_user_size(x,ptr,size,retval,errret)			\
 do {									\
-	__typeof__(*(ptr)) __pus_tmp = x;				\
+	__typeof__(*(ptr)) __pus_tmp = (x);				\
 	retval = 0;							\
 									\
 	if(unlikely(__copy_to_user_ll(ptr, &__pus_tmp, size) != 0))	\
@@ -332,12 +323,31 @@
 		: "=r"(err)						\
 		: ltype (x), "m"(__m(addr)), "i"(errret), "0"(err))
 
+#define __put_user_u64(x, addr, err, errret)				\
+	__asm__ __volatile__(						\
+		"1:	movl %%eax,%2\n"				\
+		"2:	movl %%edx,%3\n"				\
+		"3:\n"							\
+		".section .fixup,\"ax\"\n"				\
+		"4:	movl %4,%0\n"					\
+		"	jmp 3b\n"					\
+		".previous\n"						\
+		".section __ex_table,\"a\"\n"				\
+		"	.align 4\n"					\
+		"	.long 1b,4b\n"					\
+		"	.long 2b,4b\n"					\
+		".previous"						\
+		: "=r" (err)						\
+		: "A" (x), "m" (__m(addr)), "m" (__m(4+(char*)addr)),	\
+		  "i" (errret), "0" (err))
+
 
 #define __get_user_nocheck(x,ptr,size)				\
 ({								\
-	long __gu_err, __gu_val;				\
+	__typeof__(*(ptr)) __gu_val;				\
+	int __gu_err;						\
 	__get_user_size(__gu_val,(ptr),(size),__gu_err,-EFAULT);\
-	(x) = (__typeof__(*(ptr)))__gu_val;			\
+	(x) = __gu_val;						\
 	__gu_err;						\
 })
 
@@ -350,7 +360,8 @@
 	case 1: __get_user_asm(x,ptr,retval,"b","b","=q",errret);break;	\
 	case 2: __get_user_asm(x,ptr,retval,"w","w","=r",errret);break;	\
 	case 4: __get_user_asm(x,ptr,retval,"l","","=r",errret);break;	\
-	default: (x) = __get_user_bad();				\
+	case 8: __get_user_u64(x,ptr,retval,errret);break;		\
+	default: __get_user_bad(); (x) = 0;				\
 	}								\
 } while (0)
 
@@ -370,6 +381,26 @@
 		: "=r"(err), ltype (x)					\
 		: "m"(__m(addr)), "i"(errret), "0"(err))
 
+#define __get_user_u64(x, addr, err, errret)				\
+	__asm__ __volatile__(						\
+		"1:	movl %2,%%eax\n"				\
+		"2:	movl %3,%%edx\n"				\
+		"3:\n"							\
+		".section .fixup,\"ax\"\n"				\
+		"4:	movl %4,%0\n"					\
+		"	xorl %%eax,%%eax\n"				\
+		"	xorl %%edx,%%edx\n"				\
+		"	jmp 3b\n"					\
+		".previous\n"						\
+		".section __ex_table,\"a\"\n"				\
+		"	.align 4\n"					\
+		"	.long 1b,4b\n"					\
+		"	.long 2b,4b\n"					\
+		".previous"						\
+		: "=r" (err), "=A" (x)					\
+		: "m" (__m(addr)), "m" (__m(4+(char*)addr)),		\
+		  "i" (errret), "0" (err));
+
 
 unsigned long __copy_to_user_ll(void __user *to, const void *from, unsigned long n);
 unsigned long __copy_from_user_ll(void *to, const void __user *from, unsigned long n);
@@ -411,6 +442,9 @@
 		case 4:
 			__put_user_size(*(u32 *)from, (u32 *)to, 4, ret, 4);
 			return ret;
+		case 8:
+			__put_user_size(*(u64 *)from, (u64 *)to, 8, ret, 8);
+			return ret;
 		}
 	}
 	return __copy_to_user_ll(to, from, n);
@@ -449,6 +483,9 @@
 		case 4:
 			__get_user_size(*(u32 *)to, from, 4, ret, 4);
 			return ret;
+		case 8:
+			__get_user_size(*(u64 *)to, from, 8, ret, 8);
+			return ret;
 		}
 	}
 	return __copy_from_user_ll(to, from, n);
@@ -496,8 +533,24 @@
 copy_from_user(void *to, const void __user *from, unsigned long n)
 {
 	might_sleep();
+	if (__builtin_constant_p(n)) {
+		switch (n) {
+		case 1:
+			__get_user_x(1, *(u8 *)to, from, n, "=a");
+			return n ? 1 : 0;
+		case 2:
+			__get_user_x(2, *(u16 *)to, from, n, "=a");
+			return n ? 2 : 0;
+		case 4:
+			__get_user_x(4, *(u32 *)to, from, n, "=a");
+			return n ? 4 : 0;
+		case 8:
+			__get_user_x(8, *(u64 *)to, from, n, "=A");
+			return n ? 8 : 0;
+		}
+	}
 	if (access_ok(VERIFY_READ, from, n))
-		n = __copy_from_user(to, from, n);
+		n = __copy_from_user_ll(to, from, n);
 	else
 		memset(to, 0, n);
 	return n;

  parent reply	other threads:[~2004-04-20 17:42 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2004-04-18  0:28 Nasty 2.6 sendfile() bug / regression; affects vsftpd chris
2004-04-18  3:12 ` Linus Torvalds
2004-04-18 13:49   ` chris
2004-04-19  0:46   ` Jamie Lokier
2004-04-19  5:28     ` Linus Torvalds
2004-04-19  9:44       ` [PATCH] Add 64-bit get_user and __get_user for i386 Jamie Lokier
2004-04-19 10:14         ` Jamie Lokier
2004-04-19 10:17           ` [PATCH] Use get_user for 64-bit read in sendfile64 Jamie Lokier
2004-04-19 10:36             ` Jamie Lokier
2004-04-19 14:56         ` [PATCH] Add 64-bit get_user and __get_user for i386 Linus Torvalds
     [not found]           ` <20040420020922.GA18348@mail.shareable.org>
     [not found]             ` <Pine.LNX.4.58.0404191945490.29941@ppc970.osdl.org>
2004-04-20 17:41               ` Jamie Lokier [this message]
2004-04-21  1:15                 ` Linus Torvalds
2004-04-20 23:21             ` Jim Wilson

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20040420174147.GA20924@mail.shareable.org \
    --to=jamie@shareable.org \
    --cc=akpm@osdl.org \
    --cc=chris@scary.beasts.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=torvalds@osdl.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox