]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
x86: uaccess: introduce try and catch framework
authorHiroshi Shimamoto <h-shimamoto@ct.jp.nec.com>
Fri, 23 Jan 2009 23:49:41 +0000 (15:49 -0800)
committerH. Peter Anvin <hpa@linux.intel.com>
Sat, 24 Jan 2009 01:17:36 +0000 (17:17 -0800)
Impact: introduce new uaccess exception handling framework

Introduce {get|put}_user_try and {get|put}_user_catch as new uaccess exception
handling framework.
{get|put}_user_try begins exception block and {get|put}_user_catch(err) ends
the block and gets err if an exception occured in {get|put}_user_ex() in the
block. The exception is stored thread_info->uaccess_err.

The example usage of this framework is below;
int func()
{
int err = 0;

get_user_try {
get_user_ex(...);
get_user_ex(...);
:
} get_user_catch(err);

return err;
}

Note: get_user_ex() is not clear the value when an exception occurs, it's
different from the behavior of __get_user(), but I think it doesn't matter.

Signed-off-by: Hiroshi Shimamoto <h-shimamoto@ct.jp.nec.com>
Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
arch/x86/include/asm/thread_info.h
arch/x86/include/asm/uaccess.h
arch/x86/mm/extable.c

index 98789647baa9b8a6d1e414f9e40c95c70f704a11..3f90aeb456bc19a83b23334ab875a9c56de1bbd3 100644 (file)
@@ -40,6 +40,7 @@ struct thread_info {
                                                */
        __u8                    supervisor_stack[0];
 #endif
+       int                     uaccess_err;
 };
 
 #define INIT_THREAD_INFO(tsk)                  \
index 69d2757cca9bb8e801e0a931dfb61839b3dd0269..0ec6de4bcb0bc5a70613be5787578be47be14b11 100644 (file)
@@ -199,12 +199,22 @@ extern int __get_user_bad(void);
                     : "=r" (err)                                       \
                     : "A" (x), "r" (addr), "i" (-EFAULT), "0" (err))
 
+#define __put_user_asm_ex_u64(x, addr)                                 \
+       asm volatile("1:        movl %%eax,0(%1)\n"                     \
+                    "2:        movl %%edx,4(%1)\n"                     \
+                    "3:\n"                                             \
+                    _ASM_EXTABLE(1b, 2b - 1b)                          \
+                    _ASM_EXTABLE(2b, 3b - 2b)                          \
+                    : : "A" (x), "r" (addr))
+
 #define __put_user_x8(x, ptr, __ret_pu)                                \
        asm volatile("call __put_user_8" : "=a" (__ret_pu)      \
                     : "A" ((typeof(*(ptr)))(x)), "c" (ptr) : "ebx")
 #else
 #define __put_user_asm_u64(x, ptr, retval) \
        __put_user_asm(x, ptr, retval, "q", "", "Zr", -EFAULT)
+#define __put_user_asm_ex_u64(x, addr) \
+       __put_user_asm_ex(x, addr, "q", "", "Zr")
 #define __put_user_x8(x, ptr, __ret_pu) __put_user_x(8, x, ptr, __ret_pu)
 #endif
 
@@ -286,6 +296,27 @@ do {                                                                       \
        }                                                               \
 } while (0)
 
+#define __put_user_size_ex(x, ptr, size)                               \
+do {                                                                   \
+       __chk_user_ptr(ptr);                                            \
+       switch (size) {                                                 \
+       case 1:                                                         \
+               __put_user_asm_ex(x, ptr, "b", "b", "iq");              \
+               break;                                                  \
+       case 2:                                                         \
+               __put_user_asm_ex(x, ptr, "w", "w", "ir");              \
+               break;                                                  \
+       case 4:                                                         \
+               __put_user_asm_ex(x, ptr, "l", "k", "ir");              \
+               break;                                                  \
+       case 8:                                                         \
+               __put_user_asm_ex_u64((__typeof__(*ptr))(x), ptr);      \
+               break;                                                  \
+       default:                                                        \
+               __put_user_bad();                                       \
+       }                                                               \
+} while (0)
+
 #else
 
 #define __put_user_size(x, ptr, size, retval, errret)                  \
@@ -311,9 +342,12 @@ do {                                                                       \
 
 #ifdef CONFIG_X86_32
 #define __get_user_asm_u64(x, ptr, retval, errret)     (x) = __get_user_bad()
+#define __get_user_asm_ex_u64(x, ptr)                  (x) = __get_user_bad()
 #else
 #define __get_user_asm_u64(x, ptr, retval, errret) \
         __get_user_asm(x, ptr, retval, "q", "", "=r", errret)
+#define __get_user_asm_ex_u64(x, ptr) \
+        __get_user_asm_ex(x, ptr, "q", "", "=r")
 #endif
 
 #define __get_user_size(x, ptr, size, retval, errret)                  \
@@ -350,6 +384,33 @@ do {                                                                       \
                     : "=r" (err), ltype(x)                             \
                     : "m" (__m(addr)), "i" (errret), "0" (err))
 
+#define __get_user_size_ex(x, ptr, size)                               \
+do {                                                                   \
+       __chk_user_ptr(ptr);                                            \
+       switch (size) {                                                 \
+       case 1:                                                         \
+               __get_user_asm_ex(x, ptr, "b", "b", "=q");              \
+               break;                                                  \
+       case 2:                                                         \
+               __get_user_asm_ex(x, ptr, "w", "w", "=r");              \
+               break;                                                  \
+       case 4:                                                         \
+               __get_user_asm_ex(x, ptr, "l", "k", "=r");              \
+               break;                                                  \
+       case 8:                                                         \
+               __get_user_asm_ex_u64(x, ptr);                          \
+               break;                                                  \
+       default:                                                        \
+               (x) = __get_user_bad();                                 \
+       }                                                               \
+} while (0)
+
+#define __get_user_asm_ex(x, addr, itype, rtype, ltype)                        \
+       asm volatile("1:        mov"itype" %1,%"rtype"0\n"              \
+                    "2:\n"                                             \
+                    _ASM_EXTABLE(1b, 2b - 1b)                          \
+                    : ltype(x) : "m" (__m(addr)))
+
 #define __put_user_nocheck(x, ptr, size)                       \
 ({                                                             \
        int __pu_err;                                           \
@@ -385,6 +446,26 @@ struct __large_struct { unsigned long buf[100]; };
                     _ASM_EXTABLE(1b, 3b)                               \
                     : "=r"(err)                                        \
                     : ltype(x), "m" (__m(addr)), "i" (errret), "0" (err))
+
+#define __put_user_asm_ex(x, addr, itype, rtype, ltype)                        \
+       asm volatile("1:        mov"itype" %"rtype"0,%1\n"              \
+                    "2:\n"                                             \
+                    _ASM_EXTABLE(1b, 2b - 1b)                          \
+                    : : ltype(x), "m" (__m(addr)))
+
+/*
+ * uaccess_try and catch
+ */
+#define uaccess_try    do {                                            \
+       int prev_err = current_thread_info()->uaccess_err;              \
+       current_thread_info()->uaccess_err = 0;                         \
+       barrier();
+
+#define uaccess_catch(err)                                             \
+       (err) |= current_thread_info()->uaccess_err;                    \
+       current_thread_info()->uaccess_err = prev_err;                  \
+} while (0)
+
 /**
  * __get_user: - Get a simple variable from user space, with less checking.
  * @x:   Variable to store result.
@@ -408,6 +489,7 @@ struct __large_struct { unsigned long buf[100]; };
 
 #define __get_user(x, ptr)                                             \
        __get_user_nocheck((x), (ptr), sizeof(*(ptr)))
+
 /**
  * __put_user: - Write a simple value into user space, with less checking.
  * @x:   Value to copy to user space.
@@ -434,6 +516,27 @@ struct __large_struct { unsigned long buf[100]; };
 #define __get_user_unaligned __get_user
 #define __put_user_unaligned __put_user
 
+/*
+ * {get|put}_user_try and catch
+ *
+ * get_user_try {
+ *     get_user_ex(...);
+ * } get_user_catch(err)
+ */
+#define get_user_try           uaccess_try
+#define get_user_catch(err)    uaccess_catch(err)
+#define put_user_try           uaccess_try
+#define put_user_catch(err)    uaccess_catch(err)
+
+#define get_user_ex(x, ptr)    do {                                    \
+       unsigned long __gue_val;                                        \
+       __get_user_size_ex((__gue_val), (ptr), (sizeof(*(ptr))));       \
+       (x) = (__force __typeof__(*(ptr)))__gue_val;                    \
+} while (0)
+
+#define put_user_ex(x, ptr)                                            \
+       __put_user_size_ex((__typeof__(*(ptr)))(x), (ptr), sizeof(*(ptr)))
+
 /*
  * movsl can be slow when source and dest are not both 8-byte aligned
  */
index 7e8db53528a7618820bd338ccaaf1039a14f241f..61b41ca3b5a2ed4bbf1b9100fd870578885182ad 100644 (file)
@@ -23,6 +23,12 @@ int fixup_exception(struct pt_regs *regs)
 
        fixup = search_exception_tables(regs->ip);
        if (fixup) {
+               /* If fixup is less than 16, it means uaccess error */
+               if (fixup->fixup < 16) {
+                       current_thread_info()->uaccess_err = -EFAULT;
+                       regs->ip += fixup->fixup;
+                       return 1;
+               }
                regs->ip = fixup->fixup;
                return 1;
        }