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 occurred and the
78 * address contains the (negative) exception code.
82 static unsigned long follow_table(struct mm_struct
*mm
,
83 unsigned long address
, int write
)
85 unsigned long *table
= (unsigned long *)__pa(mm
->pgd
);
87 if (unlikely(address
> mm
->context
.asce_limit
- 1))
89 switch (mm
->context
.asce_bits
& _ASCE_TYPE_MASK
) {
90 case _ASCE_TYPE_REGION1
:
91 table
= table
+ ((address
>> 53) & 0x7ff);
92 if (unlikely(*table
& _REGION_ENTRY_INVALID
))
94 table
= (unsigned long *)(*table
& _REGION_ENTRY_ORIGIN
);
96 case _ASCE_TYPE_REGION2
:
97 table
= table
+ ((address
>> 42) & 0x7ff);
98 if (unlikely(*table
& _REGION_ENTRY_INVALID
))
100 table
= (unsigned long *)(*table
& _REGION_ENTRY_ORIGIN
);
102 case _ASCE_TYPE_REGION3
:
103 table
= table
+ ((address
>> 31) & 0x7ff);
104 if (unlikely(*table
& _REGION_ENTRY_INVALID
))
106 table
= (unsigned long *)(*table
& _REGION_ENTRY_ORIGIN
);
108 case _ASCE_TYPE_SEGMENT
:
109 table
= table
+ ((address
>> 20) & 0x7ff);
110 if (unlikely(*table
& _SEGMENT_ENTRY_INVALID
))
112 if (unlikely(*table
& _SEGMENT_ENTRY_LARGE
)) {
113 if (write
&& (*table
& _SEGMENT_ENTRY_PROTECT
))
115 return (*table
& _SEGMENT_ENTRY_ORIGIN_LARGE
) +
116 (address
& ~_SEGMENT_ENTRY_ORIGIN_LARGE
);
118 table
= (unsigned long *)(*table
& _SEGMENT_ENTRY_ORIGIN
);
120 table
= table
+ ((address
>> 12) & 0xff);
121 if (unlikely(*table
& _PAGE_INVALID
))
123 if (write
&& (*table
& _PAGE_PROTECT
))
125 return (*table
& PAGE_MASK
) + (address
& ~PAGE_MASK
);
128 #else /* CONFIG_64BIT */
130 static unsigned long follow_table(struct mm_struct
*mm
,
131 unsigned long address
, int write
)
133 unsigned long *table
= (unsigned long *)__pa(mm
->pgd
);
135 table
= table
+ ((address
>> 20) & 0x7ff);
136 if (unlikely(*table
& _SEGMENT_ENTRY_INVALID
))
138 table
= (unsigned long *)(*table
& _SEGMENT_ENTRY_ORIGIN
);
139 table
= table
+ ((address
>> 12) & 0xff);
140 if (unlikely(*table
& _PAGE_INVALID
))
142 if (write
&& (*table
& _PAGE_PROTECT
))
144 return (*table
& PAGE_MASK
) + (address
& ~PAGE_MASK
);
147 #endif /* CONFIG_64BIT */
149 static __always_inline
size_t __user_copy_pt(unsigned long uaddr
, void *kptr
,
150 size_t n
, int write_user
)
152 struct mm_struct
*mm
= current
->mm
;
153 unsigned long offset
, done
, size
, kaddr
;
160 spin_lock(&mm
->page_table_lock
);
162 kaddr
= follow_table(mm
, uaddr
, write_user
);
163 if (IS_ERR_VALUE(kaddr
))
166 offset
= uaddr
& ~PAGE_MASK
;
167 size
= min(n
- done
, PAGE_SIZE
- offset
);
172 from
= (void *) kaddr
;
175 memcpy(to
, from
, size
);
179 spin_unlock(&mm
->page_table_lock
);
182 spin_unlock(&mm
->page_table_lock
);
183 if (__handle_fault(uaddr
, -kaddr
, write_user
))
189 * Do DAT for user address by page table walk, return kernel address.
190 * This function needs to be called with current->mm->page_table_lock held.
192 static __always_inline
unsigned long __dat_user_addr(unsigned long uaddr
,
195 struct mm_struct
*mm
= current
->mm
;
200 kaddr
= follow_table(mm
, uaddr
, write
);
201 if (IS_ERR_VALUE(kaddr
))
206 spin_unlock(&mm
->page_table_lock
);
207 rc
= __handle_fault(uaddr
, -kaddr
, write
);
208 spin_lock(&mm
->page_table_lock
);
214 static size_t copy_from_user_pt(size_t n
, const void __user
*from
, void *to
)
218 if (segment_eq(get_fs(), KERNEL_DS
))
219 return copy_in_kernel(n
, (void __user
*) to
, from
);
220 rc
= __user_copy_pt((unsigned long) from
, to
, n
, 0);
222 memset(to
+ n
- rc
, 0, rc
);
226 static size_t copy_to_user_pt(size_t n
, void __user
*to
, const void *from
)
228 if (segment_eq(get_fs(), KERNEL_DS
))
229 return copy_in_kernel(n
, to
, (void __user
*) from
);
230 return __user_copy_pt((unsigned long) to
, (void *) from
, n
, 1);
233 static size_t clear_user_pt(size_t n
, void __user
*to
)
235 void *zpage
= (void *) empty_zero_page
;
236 long done
, size
, ret
;
240 if (n
- done
> PAGE_SIZE
)
244 if (segment_eq(get_fs(), KERNEL_DS
))
245 ret
= copy_in_kernel(n
, to
, (void __user
*) zpage
);
247 ret
= __user_copy_pt((unsigned long) to
, zpage
, size
, 1);
251 return ret
+ n
- done
;
256 static size_t strnlen_user_pt(size_t count
, const char __user
*src
)
258 unsigned long uaddr
= (unsigned long) src
;
259 struct mm_struct
*mm
= current
->mm
;
260 unsigned long offset
, done
, len
, kaddr
;
263 if (unlikely(!count
))
265 if (segment_eq(get_fs(), KERNEL_DS
))
266 return strnlen_kernel(count
, src
);
271 spin_lock(&mm
->page_table_lock
);
273 kaddr
= follow_table(mm
, uaddr
, 0);
274 if (IS_ERR_VALUE(kaddr
))
277 offset
= uaddr
& ~PAGE_MASK
;
278 len
= min(count
- done
, PAGE_SIZE
- offset
);
279 len_str
= strnlen((char *) kaddr
, len
);
282 } while ((len_str
== len
) && (done
< count
));
283 spin_unlock(&mm
->page_table_lock
);
286 spin_unlock(&mm
->page_table_lock
);
287 if (__handle_fault(uaddr
, -kaddr
, 0))
292 static size_t strncpy_from_user_pt(size_t count
, const char __user
*src
,
295 size_t done
, len
, offset
, len_str
;
297 if (unlikely(!count
))
301 offset
= (size_t)src
& ~PAGE_MASK
;
302 len
= min(count
- done
, PAGE_SIZE
- offset
);
303 if (segment_eq(get_fs(), KERNEL_DS
)) {
304 if (copy_in_kernel(len
, (void __user
*) dst
, src
))
307 if (__user_copy_pt((unsigned long) src
, dst
, len
, 0))
310 len_str
= strnlen(dst
, len
);
314 } while ((len_str
== len
) && (done
< count
));
318 static size_t copy_in_user_pt(size_t n
, void __user
*to
,
319 const void __user
*from
)
321 struct mm_struct
*mm
= current
->mm
;
322 unsigned long offset_max
, uaddr
, done
, size
, error_code
;
323 unsigned long uaddr_from
= (unsigned long) from
;
324 unsigned long uaddr_to
= (unsigned long) to
;
325 unsigned long kaddr_to
, kaddr_from
;
328 if (segment_eq(get_fs(), KERNEL_DS
))
329 return copy_in_kernel(n
, to
, from
);
334 spin_lock(&mm
->page_table_lock
);
338 kaddr_from
= follow_table(mm
, uaddr_from
, 0);
339 error_code
= kaddr_from
;
340 if (IS_ERR_VALUE(error_code
))
345 kaddr_to
= follow_table(mm
, uaddr_to
, 1);
346 error_code
= (unsigned long) kaddr_to
;
347 if (IS_ERR_VALUE(error_code
))
350 offset_max
= max(uaddr_from
& ~PAGE_MASK
,
351 uaddr_to
& ~PAGE_MASK
);
352 size
= min(n
- done
, PAGE_SIZE
- offset_max
);
354 memcpy((void *) kaddr_to
, (void *) kaddr_from
, size
);
359 spin_unlock(&mm
->page_table_lock
);
362 spin_unlock(&mm
->page_table_lock
);
363 if (__handle_fault(uaddr
, -error_code
, write_user
))
368 #define __futex_atomic_op(insn, ret, oldval, newval, uaddr, oparg) \
369 asm volatile("0: l %1,0(%6)\n" \
371 "2: cs %1,%2,0(%6)\n" \
375 EX_TABLE(0b,4b) EX_TABLE(2b,4b) EX_TABLE(3b,4b) \
376 : "=d" (ret), "=&d" (oldval), "=&d" (newval), \
378 : "0" (-EFAULT), "d" (oparg), "a" (uaddr), \
379 "m" (*uaddr) : "cc" );
381 static int __futex_atomic_op_pt(int op
, u32 __user
*uaddr
, int oparg
, int *old
)
383 int oldval
= 0, newval
, ret
;
387 __futex_atomic_op("lr %2,%5\n",
388 ret
, oldval
, newval
, uaddr
, oparg
);
391 __futex_atomic_op("lr %2,%1\nar %2,%5\n",
392 ret
, oldval
, newval
, uaddr
, oparg
);
395 __futex_atomic_op("lr %2,%1\nor %2,%5\n",
396 ret
, oldval
, newval
, uaddr
, oparg
);
399 __futex_atomic_op("lr %2,%1\nnr %2,%5\n",
400 ret
, oldval
, newval
, uaddr
, oparg
);
403 __futex_atomic_op("lr %2,%1\nxr %2,%5\n",
404 ret
, oldval
, newval
, uaddr
, oparg
);
414 int futex_atomic_op_pt(int op
, u32 __user
*uaddr
, int oparg
, int *old
)
418 if (segment_eq(get_fs(), KERNEL_DS
))
419 return __futex_atomic_op_pt(op
, uaddr
, oparg
, old
);
420 if (unlikely(!current
->mm
))
422 spin_lock(¤t
->mm
->page_table_lock
);
423 uaddr
= (u32 __force __user
*)
424 __dat_user_addr((__force
unsigned long) uaddr
, 1);
426 spin_unlock(¤t
->mm
->page_table_lock
);
429 get_page(virt_to_page(uaddr
));
430 spin_unlock(¤t
->mm
->page_table_lock
);
431 ret
= __futex_atomic_op_pt(op
, uaddr
, oparg
, old
);
432 put_page(virt_to_page(uaddr
));
436 static int __futex_atomic_cmpxchg_pt(u32
*uval
, u32 __user
*uaddr
,
437 u32 oldval
, u32 newval
)
441 asm volatile("0: cs %1,%4,0(%5)\n"
444 EX_TABLE(0b
,2b
) EX_TABLE(1b
,2b
)
445 : "=d" (ret
), "+d" (oldval
), "=m" (*uaddr
)
446 : "0" (-EFAULT
), "d" (newval
), "a" (uaddr
), "m" (*uaddr
)
452 int futex_atomic_cmpxchg_pt(u32
*uval
, u32 __user
*uaddr
,
453 u32 oldval
, u32 newval
)
457 if (segment_eq(get_fs(), KERNEL_DS
))
458 return __futex_atomic_cmpxchg_pt(uval
, uaddr
, oldval
, newval
);
459 if (unlikely(!current
->mm
))
461 spin_lock(¤t
->mm
->page_table_lock
);
462 uaddr
= (u32 __force __user
*)
463 __dat_user_addr((__force
unsigned long) uaddr
, 1);
465 spin_unlock(¤t
->mm
->page_table_lock
);
468 get_page(virt_to_page(uaddr
));
469 spin_unlock(¤t
->mm
->page_table_lock
);
470 ret
= __futex_atomic_cmpxchg_pt(uval
, uaddr
, oldval
, newval
);
471 put_page(virt_to_page(uaddr
));
475 struct uaccess_ops uaccess_pt
= {
476 .copy_from_user
= copy_from_user_pt
,
477 .copy_to_user
= copy_to_user_pt
,
478 .copy_in_user
= copy_in_user_pt
,
479 .clear_user
= clear_user_pt
,
480 .strnlen_user
= strnlen_user_pt
,
481 .strncpy_from_user
= strncpy_from_user_pt
,
482 .futex_atomic_op
= futex_atomic_op_pt
,
483 .futex_atomic_cmpxchg
= futex_atomic_cmpxchg_pt
,