Fedora kernel-2.6.17-1.2142_FC4 patched with stable patch-2.6.17.4-vs2.0.2-rc26.diff
[linux-2.6.git] / arch / sparc64 / lib / user_fixup.c
index 0278e34..19d1fdb 100644 (file)
 
 /* Calculating the exact fault address when using
  * block loads and stores can be very complicated.
+ *
  * Instead of trying to be clever and handling all
  * of the cases, just fix things up simply here.
  */
 
-unsigned long copy_from_user_fixup(void *to, const void __user *from, unsigned long size)
+static unsigned long compute_size(unsigned long start, unsigned long size, unsigned long *offset)
 {
-       char *dst = to;
-       const char __user *src = from;
+       unsigned long fault_addr = current_thread_info()->fault_address;
+       unsigned long end = start + size;
 
-       while (size) {
-               if (__get_user(*dst, src))
-                       break;
-               dst++;
-               src++;
-               size--;
+       if (fault_addr < start || fault_addr >= end) {
+               *offset = 0;
+       } else {
+               *offset = start - fault_addr;
+               size = end - fault_addr;
        }
+       return size;
+}
 
-       if (size)
-               memset(dst, 0, size);
+unsigned long copy_from_user_fixup(void *to, const void __user *from, unsigned long size)
+{
+       unsigned long offset;
+
+       size = compute_size((unsigned long) from, size, &offset);
+       if (likely(size))
+               memset(to + offset, 0, size);
 
        return size;
 }
 
 unsigned long copy_to_user_fixup(void __user *to, const void *from, unsigned long size)
 {
-       char __user *dst = to;
-       const char *src = from;
-
-       while (size) {
-               if (__put_user(*src, dst))
-                       break;
-               dst++;
-               src++;
-               size--;
-       }
+       unsigned long offset;
 
-       return size;
+       return compute_size((unsigned long) to, size, &offset);
 }
 
 unsigned long copy_in_user_fixup(void __user *to, void __user *from, unsigned long size)
 {
-       char __user *dst = to;
-       char __user *src = from;
+       unsigned long fault_addr = current_thread_info()->fault_address;
+       unsigned long start = (unsigned long) to;
+       unsigned long end = start + size;
 
-       while (size) {
-               char tmp;
+       if (fault_addr >= start && fault_addr < end)
+               return end - fault_addr;
 
-               if (__get_user(tmp, src))
-                       break;
-               if (__put_user(tmp, dst))
-                       break;
-               dst++;
-               src++;
-               size--;
-       }
+       start = (unsigned long) from;
+       end = start + size;
+       if (fault_addr >= start && fault_addr < end)
+               return end - fault_addr;
 
        return size;
 }