diff options
| -rw-r--r-- | arch/s390/lib/uaccess_pt.c | 105 |
1 files changed, 77 insertions, 28 deletions
diff --git a/arch/s390/lib/uaccess_pt.c b/arch/s390/lib/uaccess_pt.c index 304e07086ab3..dff631d34b45 100644 --- a/arch/s390/lib/uaccess_pt.c +++ b/arch/s390/lib/uaccess_pt.c | |||
| @@ -14,6 +14,63 @@ | |||
| 14 | #include <asm/futex.h> | 14 | #include <asm/futex.h> |
| 15 | #include "uaccess.h" | 15 | #include "uaccess.h" |
| 16 | 16 | ||
| 17 | #ifndef CONFIG_64BIT | ||
| 18 | #define AHI "ahi" | ||
| 19 | #define SLR "slr" | ||
| 20 | #else | ||
| 21 | #define AHI "aghi" | ||
| 22 | #define SLR "slgr" | ||
| 23 | #endif | ||
| 24 | |||
| 25 | static size_t strnlen_kernel(size_t count, const char __user *src) | ||
| 26 | { | ||
| 27 | register unsigned long reg0 asm("0") = 0UL; | ||
| 28 | unsigned long tmp1, tmp2; | ||
| 29 | |||
| 30 | asm volatile( | ||
| 31 | " la %2,0(%1)\n" | ||
| 32 | " la %3,0(%0,%1)\n" | ||
| 33 | " "SLR" %0,%0\n" | ||
| 34 | "0: srst %3,%2\n" | ||
| 35 | " jo 0b\n" | ||
| 36 | " la %0,1(%3)\n" /* strnlen_kernel results includes \0 */ | ||
| 37 | " "SLR" %0,%1\n" | ||
| 38 | "1:\n" | ||
| 39 | EX_TABLE(0b,1b) | ||
| 40 | : "+a" (count), "+a" (src), "=a" (tmp1), "=a" (tmp2) | ||
| 41 | : "d" (reg0) : "cc", "memory"); | ||
| 42 | return count; | ||
| 43 | } | ||
| 44 | |||
| 45 | static size_t copy_in_kernel(size_t count, void __user *to, | ||
| 46 | const void __user *from) | ||
| 47 | { | ||
| 48 | unsigned long tmp1; | ||
| 49 | |||
| 50 | asm volatile( | ||
| 51 | " "AHI" %0,-1\n" | ||
| 52 | " jo 5f\n" | ||
| 53 | " bras %3,3f\n" | ||
| 54 | "0:"AHI" %0,257\n" | ||
| 55 | "1: mvc 0(1,%1),0(%2)\n" | ||
| 56 | " la %1,1(%1)\n" | ||
| 57 | " la %2,1(%2)\n" | ||
| 58 | " "AHI" %0,-1\n" | ||
| 59 | " jnz 1b\n" | ||
| 60 | " j 5f\n" | ||
| 61 | "2: mvc 0(256,%1),0(%2)\n" | ||
| 62 | " la %1,256(%1)\n" | ||
| 63 | " la %2,256(%2)\n" | ||
| 64 | "3:"AHI" %0,-256\n" | ||
| 65 | " jnm 2b\n" | ||
| 66 | "4: ex %0,1b-0b(%3)\n" | ||
| 67 | "5:"SLR" %0,%0\n" | ||
| 68 | "6:\n" | ||
| 69 | EX_TABLE(1b,6b) EX_TABLE(2b,0b) EX_TABLE(4b,0b) | ||
| 70 | : "+a" (count), "+a" (to), "+a" (from), "=a" (tmp1) | ||
| 71 | : : "cc", "memory"); | ||
| 72 | return count; | ||
| 73 | } | ||
| 17 | 74 | ||
| 18 | /* | 75 | /* |
| 19 | * Returns kernel address for user virtual address. If the returned address is | 76 | * Returns kernel address for user virtual address. If the returned address is |
| @@ -123,10 +180,8 @@ size_t copy_from_user_pt(size_t n, const void __user *from, void *to) | |||
| 123 | { | 180 | { |
| 124 | size_t rc; | 181 | size_t rc; |
| 125 | 182 | ||
| 126 | if (segment_eq(get_fs(), KERNEL_DS)) { | 183 | if (segment_eq(get_fs(), KERNEL_DS)) |
| 127 | memcpy(to, (void __kernel __force *) from, n); | 184 | return copy_in_kernel(n, (void __user *) to, from); |
| 128 | return 0; | ||
| 129 | } | ||
| 130 | rc = __user_copy_pt((unsigned long) from, to, n, 0); | 185 | rc = __user_copy_pt((unsigned long) from, to, n, 0); |
| 131 | if (unlikely(rc)) | 186 | if (unlikely(rc)) |
| 132 | memset(to + n - rc, 0, rc); | 187 | memset(to + n - rc, 0, rc); |
| @@ -135,30 +190,28 @@ size_t copy_from_user_pt(size_t n, const void __user *from, void *to) | |||
| 135 | 190 | ||
| 136 | size_t copy_to_user_pt(size_t n, void __user *to, const void *from) | 191 | size_t copy_to_user_pt(size_t n, void __user *to, const void *from) |
| 137 | { | 192 | { |
| 138 | if (segment_eq(get_fs(), KERNEL_DS)) { | 193 | if (segment_eq(get_fs(), KERNEL_DS)) |
| 139 | memcpy((void __kernel __force *) to, from, n); | 194 | return copy_in_kernel(n, to, (void __user *) from); |
| 140 | return 0; | ||
| 141 | } | ||
| 142 | return __user_copy_pt((unsigned long) to, (void *) from, n, 1); | 195 | return __user_copy_pt((unsigned long) to, (void *) from, n, 1); |
| 143 | } | 196 | } |
| 144 | 197 | ||
| 145 | static size_t clear_user_pt(size_t n, void __user *to) | 198 | static size_t clear_user_pt(size_t n, void __user *to) |
| 146 | { | 199 | { |
| 200 | void *zpage = &empty_zero_page; | ||
| 147 | long done, size, ret; | 201 | long done, size, ret; |
| 148 | 202 | ||
| 149 | if (segment_eq(get_fs(), KERNEL_DS)) { | ||
| 150 | memset((void __kernel __force *) to, 0, n); | ||
| 151 | return 0; | ||
| 152 | } | ||
| 153 | done = 0; | 203 | done = 0; |
| 154 | do { | 204 | do { |
| 155 | if (n - done > PAGE_SIZE) | 205 | if (n - done > PAGE_SIZE) |
| 156 | size = PAGE_SIZE; | 206 | size = PAGE_SIZE; |
| 157 | else | 207 | else |
| 158 | size = n - done; | 208 | size = n - done; |
| 159 | ret = __user_copy_pt((unsigned long) to + done, | 209 | if (segment_eq(get_fs(), KERNEL_DS)) |
| 160 | &empty_zero_page, size, 1); | 210 | ret = copy_in_kernel(n, to, (void __user *) zpage); |
| 211 | else | ||
| 212 | ret = __user_copy_pt((unsigned long) to, zpage, size, 1); | ||
| 161 | done += size; | 213 | done += size; |
| 214 | to += size; | ||
| 162 | if (ret) | 215 | if (ret) |
| 163 | return ret + n - done; | 216 | return ret + n - done; |
| 164 | } while (done < n); | 217 | } while (done < n); |
| @@ -175,7 +228,7 @@ static size_t strnlen_user_pt(size_t count, const char __user *src) | |||
| 175 | if (unlikely(!count)) | 228 | if (unlikely(!count)) |
| 176 | return 0; | 229 | return 0; |
| 177 | if (segment_eq(get_fs(), KERNEL_DS)) | 230 | if (segment_eq(get_fs(), KERNEL_DS)) |
| 178 | return strnlen((const char __kernel __force *) src, count) + 1; | 231 | return strnlen_kernel(count, src); |
| 179 | done = 0; | 232 | done = 0; |
| 180 | retry: | 233 | retry: |
| 181 | spin_lock(&mm->page_table_lock); | 234 | spin_lock(&mm->page_table_lock); |
| @@ -206,19 +259,17 @@ static size_t strncpy_from_user_pt(size_t count, const char __user *src, | |||
| 206 | 259 | ||
| 207 | if (unlikely(!count)) | 260 | if (unlikely(!count)) |
| 208 | return 0; | 261 | return 0; |
| 209 | if (segment_eq(get_fs(), KERNEL_DS)) { | ||
| 210 | len = strnlen((const char __kernel __force *) src, count) + 1; | ||
| 211 | if (len > count) | ||
| 212 | len = count; | ||
| 213 | memcpy(dst, (const char __kernel __force *) src, len); | ||
| 214 | return (dst[len - 1] == '\0') ? len - 1 : len; | ||
| 215 | } | ||
| 216 | done = 0; | 262 | done = 0; |
| 217 | do { | 263 | do { |
| 218 | offset = (size_t)src & ~PAGE_MASK; | 264 | offset = (size_t)src & ~PAGE_MASK; |
| 219 | len = min(count - done, PAGE_SIZE - offset); | 265 | len = min(count - done, PAGE_SIZE - offset); |
| 220 | if (__user_copy_pt((unsigned long) src, dst, len, 0)) | 266 | if (segment_eq(get_fs(), KERNEL_DS)) { |
| 221 | return -EFAULT; | 267 | if (copy_in_kernel(len, (void __user *) dst, src)) |
| 268 | return -EFAULT; | ||
| 269 | } else { | ||
| 270 | if (__user_copy_pt((unsigned long) src, dst, len, 0)) | ||
| 271 | return -EFAULT; | ||
| 272 | } | ||
| 222 | len_str = strnlen(dst, len); | 273 | len_str = strnlen(dst, len); |
| 223 | done += len_str; | 274 | done += len_str; |
| 224 | src += len_str; | 275 | src += len_str; |
| @@ -237,10 +288,8 @@ static size_t copy_in_user_pt(size_t n, void __user *to, | |||
| 237 | unsigned long kaddr_to, kaddr_from; | 288 | unsigned long kaddr_to, kaddr_from; |
| 238 | int write_user; | 289 | int write_user; |
| 239 | 290 | ||
| 240 | if (segment_eq(get_fs(), KERNEL_DS)) { | 291 | if (segment_eq(get_fs(), KERNEL_DS)) |
| 241 | memcpy((void __force *) to, (void __force *) from, n); | 292 | return copy_in_kernel(n, to, from); |
| 242 | return 0; | ||
| 243 | } | ||
| 244 | done = 0; | 293 | done = 0; |
| 245 | retry: | 294 | retry: |
| 246 | spin_lock(&mm->page_table_lock); | 295 | spin_lock(&mm->page_table_lock); |
