uaccess_pt.c 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. /*
  2. * User access functions based on page table walks for enhanced
  3. * system layout without hardware support.
  4. *
  5. * Copyright IBM Corp. 2006, 2012
  6. * Author(s): Gerald Schaefer (gerald.schaefer@de.ibm.com)
  7. */
  8. #include <linux/errno.h>
  9. #include <linux/hardirq.h>
  10. #include <linux/mm.h>
  11. #include <linux/hugetlb.h>
  12. #include <asm/uaccess.h>
  13. #include <asm/futex.h>
  14. #include "uaccess.h"
  15. /*
  16. * Returns kernel address for user virtual address. If the returned address is
  17. * >= -4095 (IS_ERR_VALUE(x) returns true), a fault has occured and the address
  18. * contains the (negative) exception code.
  19. */
  20. static __always_inline unsigned long follow_table(struct mm_struct *mm,
  21. unsigned long addr, int write)
  22. {
  23. pgd_t *pgd;
  24. pud_t *pud;
  25. pmd_t *pmd;
  26. pte_t *ptep;
  27. pgd = pgd_offset(mm, addr);
  28. if (pgd_none(*pgd) || unlikely(pgd_bad(*pgd)))
  29. return -0x3aUL;
  30. pud = pud_offset(pgd, addr);
  31. if (pud_none(*pud) || unlikely(pud_bad(*pud)))
  32. return -0x3bUL;
  33. pmd = pmd_offset(pud, addr);
  34. if (pmd_none(*pmd))
  35. return -0x10UL;
  36. if (pmd_large(*pmd)) {
  37. if (write && (pmd_val(*pmd) & _SEGMENT_ENTRY_RO))
  38. return -0x04UL;
  39. return (pmd_val(*pmd) & HPAGE_MASK) + (addr & ~HPAGE_MASK);
  40. }
  41. if (unlikely(pmd_bad(*pmd)))
  42. return -0x10UL;
  43. ptep = pte_offset_map(pmd, addr);
  44. if (!pte_present(*ptep))
  45. return -0x11UL;
  46. if (write && !pte_write(*ptep))
  47. return -0x04UL;
  48. return (pte_val(*ptep) & PAGE_MASK) + (addr & ~PAGE_MASK);
  49. }
  50. static __always_inline size_t __user_copy_pt(unsigned long uaddr, void *kptr,
  51. size_t n, int write_user)
  52. {
  53. struct mm_struct *mm = current->mm;
  54. unsigned long offset, done, size, kaddr;
  55. void *from, *to;
  56. done = 0;
  57. retry:
  58. spin_lock(&mm->page_table_lock);
  59. do {
  60. kaddr = follow_table(mm, uaddr, write_user);
  61. if (IS_ERR_VALUE(kaddr))
  62. goto fault;
  63. offset = uaddr & ~PAGE_MASK;
  64. size = min(n - done, PAGE_SIZE - offset);
  65. if (write_user) {
  66. to = (void *) kaddr;
  67. from = kptr + done;
  68. } else {
  69. from = (void *) kaddr;
  70. to = kptr + done;
  71. }
  72. memcpy(to, from, size);
  73. done += size;
  74. uaddr += size;
  75. } while (done < n);
  76. spin_unlock(&mm->page_table_lock);
  77. return n - done;
  78. fault:
  79. spin_unlock(&mm->page_table_lock);
  80. if (__handle_fault(uaddr, -kaddr, write_user))
  81. return n - done;
  82. goto retry;
  83. }
  84. /*
  85. * Do DAT for user address by page table walk, return kernel address.
  86. * This function needs to be called with current->mm->page_table_lock held.
  87. */
  88. static __always_inline unsigned long __dat_user_addr(unsigned long uaddr,
  89. int write)
  90. {
  91. struct mm_struct *mm = current->mm;
  92. unsigned long kaddr;
  93. int rc;
  94. retry:
  95. kaddr = follow_table(mm, uaddr, write);
  96. if (IS_ERR_VALUE(kaddr))
  97. goto fault;
  98. return kaddr;
  99. fault:
  100. spin_unlock(&mm->page_table_lock);
  101. rc = __handle_fault(uaddr, -kaddr, write);
  102. spin_lock(&mm->page_table_lock);
  103. if (!rc)
  104. goto retry;
  105. return 0;
  106. }
  107. size_t copy_from_user_pt(size_t n, const void __user *from, void *to)
  108. {
  109. size_t rc;
  110. if (segment_eq(get_fs(), KERNEL_DS)) {
  111. memcpy(to, (void __kernel __force *) from, n);
  112. return 0;
  113. }
  114. rc = __user_copy_pt((unsigned long) from, to, n, 0);
  115. if (unlikely(rc))
  116. memset(to + n - rc, 0, rc);
  117. return rc;
  118. }
  119. size_t copy_to_user_pt(size_t n, void __user *to, const void *from)
  120. {
  121. if (segment_eq(get_fs(), KERNEL_DS)) {
  122. memcpy((void __kernel __force *) to, from, n);
  123. return 0;
  124. }
  125. return __user_copy_pt((unsigned long) to, (void *) from, n, 1);
  126. }
  127. static size_t clear_user_pt(size_t n, void __user *to)
  128. {
  129. long done, size, ret;
  130. if (segment_eq(get_fs(), KERNEL_DS)) {
  131. memset((void __kernel __force *) to, 0, n);
  132. return 0;
  133. }
  134. done = 0;
  135. do {
  136. if (n - done > PAGE_SIZE)
  137. size = PAGE_SIZE;
  138. else
  139. size = n - done;
  140. ret = __user_copy_pt((unsigned long) to + done,
  141. &empty_zero_page, size, 1);
  142. done += size;
  143. if (ret)
  144. return ret + n - done;
  145. } while (done < n);
  146. return 0;
  147. }
  148. static size_t strnlen_user_pt(size_t count, const char __user *src)
  149. {
  150. unsigned long uaddr = (unsigned long) src;
  151. struct mm_struct *mm = current->mm;
  152. unsigned long offset, done, len, kaddr;
  153. size_t len_str;
  154. if (segment_eq(get_fs(), KERNEL_DS))
  155. return strnlen((const char __kernel __force *) src, count) + 1;
  156. done = 0;
  157. retry:
  158. spin_lock(&mm->page_table_lock);
  159. do {
  160. kaddr = follow_table(mm, uaddr, 0);
  161. if (IS_ERR_VALUE(kaddr))
  162. goto fault;
  163. offset = uaddr & ~PAGE_MASK;
  164. len = min(count - done, PAGE_SIZE - offset);
  165. len_str = strnlen((char *) kaddr, len);
  166. done += len_str;
  167. uaddr += len_str;
  168. } while ((len_str == len) && (done < count));
  169. spin_unlock(&mm->page_table_lock);
  170. return done + 1;
  171. fault:
  172. spin_unlock(&mm->page_table_lock);
  173. if (__handle_fault(uaddr, -kaddr, 0))
  174. return 0;
  175. goto retry;
  176. }
  177. static size_t strncpy_from_user_pt(size_t count, const char __user *src,
  178. char *dst)
  179. {
  180. size_t n = strnlen_user_pt(count, src);
  181. if (!n)
  182. return -EFAULT;
  183. if (n > count)
  184. n = count;
  185. if (segment_eq(get_fs(), KERNEL_DS)) {
  186. memcpy(dst, (const char __kernel __force *) src, n);
  187. if (dst[n-1] == '\0')
  188. return n-1;
  189. else
  190. return n;
  191. }
  192. if (__user_copy_pt((unsigned long) src, dst, n, 0))
  193. return -EFAULT;
  194. if (dst[n-1] == '\0')
  195. return n-1;
  196. else
  197. return n;
  198. }
  199. static size_t copy_in_user_pt(size_t n, void __user *to,
  200. const void __user *from)
  201. {
  202. struct mm_struct *mm = current->mm;
  203. unsigned long offset_max, uaddr, done, size, error_code;
  204. unsigned long uaddr_from = (unsigned long) from;
  205. unsigned long uaddr_to = (unsigned long) to;
  206. unsigned long kaddr_to, kaddr_from;
  207. int write_user;
  208. if (segment_eq(get_fs(), KERNEL_DS)) {
  209. memcpy((void __force *) to, (void __force *) from, n);
  210. return 0;
  211. }
  212. done = 0;
  213. retry:
  214. spin_lock(&mm->page_table_lock);
  215. do {
  216. write_user = 0;
  217. uaddr = uaddr_from;
  218. kaddr_from = follow_table(mm, uaddr_from, 0);
  219. error_code = kaddr_from;
  220. if (IS_ERR_VALUE(error_code))
  221. goto fault;
  222. write_user = 1;
  223. uaddr = uaddr_to;
  224. kaddr_to = follow_table(mm, uaddr_to, 1);
  225. error_code = (unsigned long) kaddr_to;
  226. if (IS_ERR_VALUE(error_code))
  227. goto fault;
  228. offset_max = max(uaddr_from & ~PAGE_MASK,
  229. uaddr_to & ~PAGE_MASK);
  230. size = min(n - done, PAGE_SIZE - offset_max);
  231. memcpy((void *) kaddr_to, (void *) kaddr_from, size);
  232. done += size;
  233. uaddr_from += size;
  234. uaddr_to += size;
  235. } while (done < n);
  236. spin_unlock(&mm->page_table_lock);
  237. return n - done;
  238. fault:
  239. spin_unlock(&mm->page_table_lock);
  240. if (__handle_fault(uaddr, -error_code, write_user))
  241. return n - done;
  242. goto retry;
  243. }
  244. #define __futex_atomic_op(insn, ret, oldval, newval, uaddr, oparg) \
  245. asm volatile("0: l %1,0(%6)\n" \
  246. "1: " insn \
  247. "2: cs %1,%2,0(%6)\n" \
  248. "3: jl 1b\n" \
  249. " lhi %0,0\n" \
  250. "4:\n" \
  251. EX_TABLE(0b,4b) EX_TABLE(2b,4b) EX_TABLE(3b,4b) \
  252. : "=d" (ret), "=&d" (oldval), "=&d" (newval), \
  253. "=m" (*uaddr) \
  254. : "0" (-EFAULT), "d" (oparg), "a" (uaddr), \
  255. "m" (*uaddr) : "cc" );
  256. static int __futex_atomic_op_pt(int op, u32 __user *uaddr, int oparg, int *old)
  257. {
  258. int oldval = 0, newval, ret;
  259. switch (op) {
  260. case FUTEX_OP_SET:
  261. __futex_atomic_op("lr %2,%5\n",
  262. ret, oldval, newval, uaddr, oparg);
  263. break;
  264. case FUTEX_OP_ADD:
  265. __futex_atomic_op("lr %2,%1\nar %2,%5\n",
  266. ret, oldval, newval, uaddr, oparg);
  267. break;
  268. case FUTEX_OP_OR:
  269. __futex_atomic_op("lr %2,%1\nor %2,%5\n",
  270. ret, oldval, newval, uaddr, oparg);
  271. break;
  272. case FUTEX_OP_ANDN:
  273. __futex_atomic_op("lr %2,%1\nnr %2,%5\n",
  274. ret, oldval, newval, uaddr, oparg);
  275. break;
  276. case FUTEX_OP_XOR:
  277. __futex_atomic_op("lr %2,%1\nxr %2,%5\n",
  278. ret, oldval, newval, uaddr, oparg);
  279. break;
  280. default:
  281. ret = -ENOSYS;
  282. }
  283. if (ret == 0)
  284. *old = oldval;
  285. return ret;
  286. }
  287. int futex_atomic_op_pt(int op, u32 __user *uaddr, int oparg, int *old)
  288. {
  289. int ret;
  290. if (segment_eq(get_fs(), KERNEL_DS))
  291. return __futex_atomic_op_pt(op, uaddr, oparg, old);
  292. spin_lock(&current->mm->page_table_lock);
  293. uaddr = (u32 __force __user *)
  294. __dat_user_addr((__force unsigned long) uaddr, 1);
  295. if (!uaddr) {
  296. spin_unlock(&current->mm->page_table_lock);
  297. return -EFAULT;
  298. }
  299. get_page(virt_to_page(uaddr));
  300. spin_unlock(&current->mm->page_table_lock);
  301. ret = __futex_atomic_op_pt(op, uaddr, oparg, old);
  302. put_page(virt_to_page(uaddr));
  303. return ret;
  304. }
  305. static int __futex_atomic_cmpxchg_pt(u32 *uval, u32 __user *uaddr,
  306. u32 oldval, u32 newval)
  307. {
  308. int ret;
  309. asm volatile("0: cs %1,%4,0(%5)\n"
  310. "1: la %0,0\n"
  311. "2:\n"
  312. EX_TABLE(0b,2b) EX_TABLE(1b,2b)
  313. : "=d" (ret), "+d" (oldval), "=m" (*uaddr)
  314. : "0" (-EFAULT), "d" (newval), "a" (uaddr), "m" (*uaddr)
  315. : "cc", "memory" );
  316. *uval = oldval;
  317. return ret;
  318. }
  319. int futex_atomic_cmpxchg_pt(u32 *uval, u32 __user *uaddr,
  320. u32 oldval, u32 newval)
  321. {
  322. int ret;
  323. if (segment_eq(get_fs(), KERNEL_DS))
  324. return __futex_atomic_cmpxchg_pt(uval, uaddr, oldval, newval);
  325. spin_lock(&current->mm->page_table_lock);
  326. uaddr = (u32 __force __user *)
  327. __dat_user_addr((__force unsigned long) uaddr, 1);
  328. if (!uaddr) {
  329. spin_unlock(&current->mm->page_table_lock);
  330. return -EFAULT;
  331. }
  332. get_page(virt_to_page(uaddr));
  333. spin_unlock(&current->mm->page_table_lock);
  334. ret = __futex_atomic_cmpxchg_pt(uval, uaddr, oldval, newval);
  335. put_page(virt_to_page(uaddr));
  336. return ret;
  337. }
  338. struct uaccess_ops uaccess_pt = {
  339. .copy_from_user = copy_from_user_pt,
  340. .copy_from_user_small = copy_from_user_pt,
  341. .copy_to_user = copy_to_user_pt,
  342. .copy_to_user_small = copy_to_user_pt,
  343. .copy_in_user = copy_in_user_pt,
  344. .clear_user = clear_user_pt,
  345. .strnlen_user = strnlen_user_pt,
  346. .strncpy_from_user = strncpy_from_user_pt,
  347. .futex_atomic_op = futex_atomic_op_pt,
  348. .futex_atomic_cmpxchg = futex_atomic_cmpxchg_pt,
  349. };