2 * User access functions based on page table walks for enhanced
3 * system layout without hardware support.
5 * Copyright IBM Corp. 2006, 2012
6 * Author(s): Gerald Schaefer (gerald.schaefer@de.ibm.com)
9 #include <linux/errno.h>
10 #include <linux/hardirq.h>
12 #include <linux/hugetlb.h>
13 #include <asm/uaccess.h>
14 #include <asm/futex.h>
25 static size_t strnlen_kernel(size_t count
, const char __user
*src
)
27 register unsigned long reg0
asm("0") = 0UL;
28 unsigned long tmp1
, tmp2
;
36 " la %0,1(%3)\n" /* strnlen_kernel results includes \0 */
40 : "+a" (count
), "+a" (src
), "=a" (tmp1
), "=a" (tmp2
)
41 : "d" (reg0
) : "cc", "memory");
45 static size_t copy_in_kernel(size_t count
, void __user
*to
,
46 const void __user
*from
)
55 "1: mvc 0(1,%1),0(%2)\n"
61 "2: mvc 0(256,%1),0(%2)\n"
66 "4: ex %0,1b-0b(%3)\n"
69 EX_TABLE(1b
,6b
) EX_TABLE(2b
,0b
) EX_TABLE(4b
,0b
)
70 : "+a" (count
), "+a" (to
), "+a" (from
), "=a" (tmp1
)
76 * Returns kernel address for user virtual address. If the returned address is
77 * >= -4095 (IS_ERR_VALUE(x) returns true), a fault has occured and the address
78 * contains the (negative) exception code.
81 static unsigned long follow_table(struct mm_struct
*mm
,
82 unsigned long address
, int write
)
84 unsigned long *table
= (unsigned long *)__pa(mm
->pgd
);
86 switch (mm
->context
.asce_bits
& _ASCE_TYPE_MASK
) {
87 case _ASCE_TYPE_REGION1
:
88 table
= table
+ ((address
>> 53) & 0x7ff);
89 if (unlikely(*table
& _REGION_ENTRY_INV
))
91 table
= (unsigned long *)(*table
& _REGION_ENTRY_ORIGIN
);
93 case _ASCE_TYPE_REGION2
:
94 table
= table
+ ((address
>> 42) & 0x7ff);
95 if (unlikely(*table
& _REGION_ENTRY_INV
))
97 table
= (unsigned long *)(*table
& _REGION_ENTRY_ORIGIN
);
99 case _ASCE_TYPE_REGION3
:
100 table
= table
+ ((address
>> 31) & 0x7ff);
101 if (unlikely(*table
& _REGION_ENTRY_INV
))
103 table
= (unsigned long *)(*table
& _REGION_ENTRY_ORIGIN
);
105 case _ASCE_TYPE_SEGMENT
:
106 table
= table
+ ((address
>> 20) & 0x7ff);
107 if (unlikely(*table
& _SEGMENT_ENTRY_INV
))
109 if (unlikely(*table
& _SEGMENT_ENTRY_LARGE
)) {
110 if (write
&& (*table
& _SEGMENT_ENTRY_RO
))
112 return (*table
& _SEGMENT_ENTRY_ORIGIN_LARGE
) +
113 (address
& ~_SEGMENT_ENTRY_ORIGIN_LARGE
);
115 table
= (unsigned long *)(*table
& _SEGMENT_ENTRY_ORIGIN
);
117 table
= table
+ ((address
>> 12) & 0xff);
118 if (unlikely(*table
& _PAGE_INVALID
))
120 if (write
&& (*table
& _PAGE_RO
))
122 return (*table
& PAGE_MASK
) + (address
& ~PAGE_MASK
);
125 #else /* CONFIG_64BIT */
127 static unsigned long follow_table(struct mm_struct
*mm
,
128 unsigned long address
, int write
)
130 unsigned long *table
= (unsigned long *)__pa(mm
->pgd
);
132 table
= table
+ ((address
>> 20) & 0x7ff);
133 if (unlikely(*table
& _SEGMENT_ENTRY_INV
))
135 table
= (unsigned long *)(*table
& _SEGMENT_ENTRY_ORIGIN
);
136 table
= table
+ ((address
>> 12) & 0xff);
137 if (unlikely(*table
& _PAGE_INVALID
))
139 if (write
&& (*table
& _PAGE_RO
))
141 return (*table
& PAGE_MASK
) + (address
& ~PAGE_MASK
);
144 #endif /* CONFIG_64BIT */
146 static __always_inline
size_t __user_copy_pt(unsigned long uaddr
, void *kptr
,
147 size_t n
, int write_user
)
149 struct mm_struct
*mm
= current
->mm
;
150 unsigned long offset
, done
, size
, kaddr
;
155 spin_lock(&mm
->page_table_lock
);
157 kaddr
= follow_table(mm
, uaddr
, write_user
);
158 if (IS_ERR_VALUE(kaddr
))
161 offset
= uaddr
& ~PAGE_MASK
;
162 size
= min(n
- done
, PAGE_SIZE
- offset
);
167 from
= (void *) kaddr
;
170 memcpy(to
, from
, size
);
174 spin_unlock(&mm
->page_table_lock
);
177 spin_unlock(&mm
->page_table_lock
);
178 if (__handle_fault(uaddr
, -kaddr
, write_user
))
184 * Do DAT for user address by page table walk, return kernel address.
185 * This function needs to be called with current->mm->page_table_lock held.
187 static __always_inline
unsigned long __dat_user_addr(unsigned long uaddr
,
190 struct mm_struct
*mm
= current
->mm
;
195 kaddr
= follow_table(mm
, uaddr
, write
);
196 if (IS_ERR_VALUE(kaddr
))
201 spin_unlock(&mm
->page_table_lock
);
202 rc
= __handle_fault(uaddr
, -kaddr
, write
);
203 spin_lock(&mm
->page_table_lock
);
209 size_t copy_from_user_pt(size_t n
, const void __user
*from
, void *to
)
213 if (segment_eq(get_fs(), KERNEL_DS
))
214 return copy_in_kernel(n
, (void __user
*) to
, from
);
215 rc
= __user_copy_pt((unsigned long) from
, to
, n
, 0);
217 memset(to
+ n
- rc
, 0, rc
);
221 size_t copy_to_user_pt(size_t n
, void __user
*to
, const void *from
)
223 if (segment_eq(get_fs(), KERNEL_DS
))
224 return copy_in_kernel(n
, to
, (void __user
*) from
);
225 return __user_copy_pt((unsigned long) to
, (void *) from
, n
, 1);
228 static size_t clear_user_pt(size_t n
, void __user
*to
)
230 void *zpage
= (void *) empty_zero_page
;
231 long done
, size
, ret
;
235 if (n
- done
> PAGE_SIZE
)
239 if (segment_eq(get_fs(), KERNEL_DS
))
240 ret
= copy_in_kernel(n
, to
, (void __user
*) zpage
);
242 ret
= __user_copy_pt((unsigned long) to
, zpage
, size
, 1);
246 return ret
+ n
- done
;
251 static size_t strnlen_user_pt(size_t count
, const char __user
*src
)
253 unsigned long uaddr
= (unsigned long) src
;
254 struct mm_struct
*mm
= current
->mm
;
255 unsigned long offset
, done
, len
, kaddr
;
258 if (unlikely(!count
))
260 if (segment_eq(get_fs(), KERNEL_DS
))
261 return strnlen_kernel(count
, src
);
264 spin_lock(&mm
->page_table_lock
);
266 kaddr
= follow_table(mm
, uaddr
, 0);
267 if (IS_ERR_VALUE(kaddr
))
270 offset
= uaddr
& ~PAGE_MASK
;
271 len
= min(count
- done
, PAGE_SIZE
- offset
);
272 len_str
= strnlen((char *) kaddr
, len
);
275 } while ((len_str
== len
) && (done
< count
));
276 spin_unlock(&mm
->page_table_lock
);
279 spin_unlock(&mm
->page_table_lock
);
280 if (__handle_fault(uaddr
, -kaddr
, 0))
285 static size_t strncpy_from_user_pt(size_t count
, const char __user
*src
,
288 size_t done
, len
, offset
, len_str
;
290 if (unlikely(!count
))
294 offset
= (size_t)src
& ~PAGE_MASK
;
295 len
= min(count
- done
, PAGE_SIZE
- offset
);
296 if (segment_eq(get_fs(), KERNEL_DS
)) {
297 if (copy_in_kernel(len
, (void __user
*) dst
, src
))
300 if (__user_copy_pt((unsigned long) src
, dst
, len
, 0))
303 len_str
= strnlen(dst
, len
);
307 } while ((len_str
== len
) && (done
< count
));
311 static size_t copy_in_user_pt(size_t n
, void __user
*to
,
312 const void __user
*from
)
314 struct mm_struct
*mm
= current
->mm
;
315 unsigned long offset_max
, uaddr
, done
, size
, error_code
;
316 unsigned long uaddr_from
= (unsigned long) from
;
317 unsigned long uaddr_to
= (unsigned long) to
;
318 unsigned long kaddr_to
, kaddr_from
;
321 if (segment_eq(get_fs(), KERNEL_DS
))
322 return copy_in_kernel(n
, to
, from
);
325 spin_lock(&mm
->page_table_lock
);
329 kaddr_from
= follow_table(mm
, uaddr_from
, 0);
330 error_code
= kaddr_from
;
331 if (IS_ERR_VALUE(error_code
))
336 kaddr_to
= follow_table(mm
, uaddr_to
, 1);
337 error_code
= (unsigned long) kaddr_to
;
338 if (IS_ERR_VALUE(error_code
))
341 offset_max
= max(uaddr_from
& ~PAGE_MASK
,
342 uaddr_to
& ~PAGE_MASK
);
343 size
= min(n
- done
, PAGE_SIZE
- offset_max
);
345 memcpy((void *) kaddr_to
, (void *) kaddr_from
, size
);
350 spin_unlock(&mm
->page_table_lock
);
353 spin_unlock(&mm
->page_table_lock
);
354 if (__handle_fault(uaddr
, -error_code
, write_user
))
359 #define __futex_atomic_op(insn, ret, oldval, newval, uaddr, oparg) \
360 asm volatile("0: l %1,0(%6)\n" \
362 "2: cs %1,%2,0(%6)\n" \
366 EX_TABLE(0b,4b) EX_TABLE(2b,4b) EX_TABLE(3b,4b) \
367 : "=d" (ret), "=&d" (oldval), "=&d" (newval), \
369 : "0" (-EFAULT), "d" (oparg), "a" (uaddr), \
370 "m" (*uaddr) : "cc" );
372 static int __futex_atomic_op_pt(int op
, u32 __user
*uaddr
, int oparg
, int *old
)
374 int oldval
= 0, newval
, ret
;
378 __futex_atomic_op("lr %2,%5\n",
379 ret
, oldval
, newval
, uaddr
, oparg
);
382 __futex_atomic_op("lr %2,%1\nar %2,%5\n",
383 ret
, oldval
, newval
, uaddr
, oparg
);
386 __futex_atomic_op("lr %2,%1\nor %2,%5\n",
387 ret
, oldval
, newval
, uaddr
, oparg
);
390 __futex_atomic_op("lr %2,%1\nnr %2,%5\n",
391 ret
, oldval
, newval
, uaddr
, oparg
);
394 __futex_atomic_op("lr %2,%1\nxr %2,%5\n",
395 ret
, oldval
, newval
, uaddr
, oparg
);
405 int futex_atomic_op_pt(int op
, u32 __user
*uaddr
, int oparg
, int *old
)
409 if (segment_eq(get_fs(), KERNEL_DS
))
410 return __futex_atomic_op_pt(op
, uaddr
, oparg
, old
);
411 spin_lock(¤t
->mm
->page_table_lock
);
412 uaddr
= (u32 __force __user
*)
413 __dat_user_addr((__force
unsigned long) uaddr
, 1);
415 spin_unlock(¤t
->mm
->page_table_lock
);
418 get_page(virt_to_page(uaddr
));
419 spin_unlock(¤t
->mm
->page_table_lock
);
420 ret
= __futex_atomic_op_pt(op
, uaddr
, oparg
, old
);
421 put_page(virt_to_page(uaddr
));
425 static int __futex_atomic_cmpxchg_pt(u32
*uval
, u32 __user
*uaddr
,
426 u32 oldval
, u32 newval
)
430 asm volatile("0: cs %1,%4,0(%5)\n"
433 EX_TABLE(0b
,2b
) EX_TABLE(1b
,2b
)
434 : "=d" (ret
), "+d" (oldval
), "=m" (*uaddr
)
435 : "0" (-EFAULT
), "d" (newval
), "a" (uaddr
), "m" (*uaddr
)
441 int futex_atomic_cmpxchg_pt(u32
*uval
, u32 __user
*uaddr
,
442 u32 oldval
, u32 newval
)
446 if (segment_eq(get_fs(), KERNEL_DS
))
447 return __futex_atomic_cmpxchg_pt(uval
, uaddr
, oldval
, newval
);
448 spin_lock(¤t
->mm
->page_table_lock
);
449 uaddr
= (u32 __force __user
*)
450 __dat_user_addr((__force
unsigned long) uaddr
, 1);
452 spin_unlock(¤t
->mm
->page_table_lock
);
455 get_page(virt_to_page(uaddr
));
456 spin_unlock(¤t
->mm
->page_table_lock
);
457 ret
= __futex_atomic_cmpxchg_pt(uval
, uaddr
, oldval
, newval
);
458 put_page(virt_to_page(uaddr
));
462 struct uaccess_ops uaccess_pt
= {
463 .copy_from_user
= copy_from_user_pt
,
464 .copy_from_user_small
= copy_from_user_pt
,
465 .copy_to_user
= copy_to_user_pt
,
466 .copy_to_user_small
= copy_to_user_pt
,
467 .copy_in_user
= copy_in_user_pt
,
468 .clear_user
= clear_user_pt
,
469 .strnlen_user
= strnlen_user_pt
,
470 .strncpy_from_user
= strncpy_from_user_pt
,
471 .futex_atomic_op
= futex_atomic_op_pt
,
472 .futex_atomic_cmpxchg
= futex_atomic_cmpxchg_pt
,