/arch/um/kernel/skas/uaccess.c

https://bitbucket.org/evzijst/gittest · C · 259 lines · 199 code · 45 blank · 15 comment · 31 complexity · 32197441542f94c569f4b89c6fa6fa04 MD5 · raw file

  1. /*
  2. * Copyright (C) 2002 - 2003 Jeff Dike (jdike@addtoit.com)
  3. * Licensed under the GPL
  4. */
  5. #include "linux/stddef.h"
  6. #include "linux/kernel.h"
  7. #include "linux/string.h"
  8. #include "linux/fs.h"
  9. #include "linux/highmem.h"
  10. #include "asm/page.h"
  11. #include "asm/pgtable.h"
  12. #include "asm/uaccess.h"
  13. #include "kern_util.h"
  14. #include "user_util.h"
  15. extern void *um_virt_to_phys(struct task_struct *task, unsigned long addr,
  16. pte_t *pte_out);
  17. static unsigned long maybe_map(unsigned long virt, int is_write)
  18. {
  19. pte_t pte;
  20. int err;
  21. void *phys = um_virt_to_phys(current, virt, &pte);
  22. int dummy_code;
  23. if(IS_ERR(phys) || (is_write && !pte_write(pte))){
  24. err = handle_page_fault(virt, 0, is_write, 1, &dummy_code);
  25. if(err)
  26. return(0);
  27. phys = um_virt_to_phys(current, virt, NULL);
  28. }
  29. return((unsigned long) phys);
  30. }
  31. static int do_op(unsigned long addr, int len, int is_write,
  32. int (*op)(unsigned long addr, int len, void *arg), void *arg)
  33. {
  34. struct page *page;
  35. int n;
  36. addr = maybe_map(addr, is_write);
  37. if(addr == -1)
  38. return(-1);
  39. page = phys_to_page(addr);
  40. addr = (unsigned long) kmap(page) + (addr & ~PAGE_MASK);
  41. n = (*op)(addr, len, arg);
  42. kunmap(page);
  43. return(n);
  44. }
  45. static void do_buffer_op(void *jmpbuf, void *arg_ptr)
  46. {
  47. va_list args;
  48. unsigned long addr;
  49. int len, is_write, size, remain, n;
  50. int (*op)(unsigned long, int, void *);
  51. void *arg;
  52. int *res;
  53. /* Some old gccs recognize __va_copy, but not va_copy */
  54. __va_copy(args, *(va_list *)arg_ptr);
  55. addr = va_arg(args, unsigned long);
  56. len = va_arg(args, int);
  57. is_write = va_arg(args, int);
  58. op = va_arg(args, void *);
  59. arg = va_arg(args, void *);
  60. res = va_arg(args, int *);
  61. va_end(args);
  62. size = min(PAGE_ALIGN(addr) - addr, (unsigned long) len);
  63. remain = len;
  64. current->thread.fault_catcher = jmpbuf;
  65. n = do_op(addr, size, is_write, op, arg);
  66. if(n != 0){
  67. *res = (n < 0 ? remain : 0);
  68. goto out;
  69. }
  70. addr += size;
  71. remain -= size;
  72. if(remain == 0){
  73. *res = 0;
  74. goto out;
  75. }
  76. while(addr < ((addr + remain) & PAGE_MASK)){
  77. n = do_op(addr, PAGE_SIZE, is_write, op, arg);
  78. if(n != 0){
  79. *res = (n < 0 ? remain : 0);
  80. goto out;
  81. }
  82. addr += PAGE_SIZE;
  83. remain -= PAGE_SIZE;
  84. }
  85. if(remain == 0){
  86. *res = 0;
  87. goto out;
  88. }
  89. n = do_op(addr, remain, is_write, op, arg);
  90. if(n != 0)
  91. *res = (n < 0 ? remain : 0);
  92. else *res = 0;
  93. out:
  94. current->thread.fault_catcher = NULL;
  95. }
  96. static int buffer_op(unsigned long addr, int len, int is_write,
  97. int (*op)(unsigned long addr, int len, void *arg),
  98. void *arg)
  99. {
  100. int faulted, res;
  101. faulted = setjmp_wrapper(do_buffer_op, addr, len, is_write, op, arg,
  102. &res);
  103. if(!faulted)
  104. return(res);
  105. return(addr + len - (unsigned long) current->thread.fault_addr);
  106. }
  107. static int copy_chunk_from_user(unsigned long from, int len, void *arg)
  108. {
  109. unsigned long *to_ptr = arg, to = *to_ptr;
  110. memcpy((void *) to, (void *) from, len);
  111. *to_ptr += len;
  112. return(0);
  113. }
  114. int copy_from_user_skas(void *to, const void __user *from, int n)
  115. {
  116. if(segment_eq(get_fs(), KERNEL_DS)){
  117. memcpy(to, (__force void*)from, n);
  118. return(0);
  119. }
  120. return(access_ok_skas(VERIFY_READ, from, n) ?
  121. buffer_op((unsigned long) from, n, 0, copy_chunk_from_user, &to):
  122. n);
  123. }
  124. static int copy_chunk_to_user(unsigned long to, int len, void *arg)
  125. {
  126. unsigned long *from_ptr = arg, from = *from_ptr;
  127. memcpy((void *) to, (void *) from, len);
  128. *from_ptr += len;
  129. return(0);
  130. }
  131. int copy_to_user_skas(void __user *to, const void *from, int n)
  132. {
  133. if(segment_eq(get_fs(), KERNEL_DS)){
  134. memcpy((__force void*)to, from, n);
  135. return(0);
  136. }
  137. return(access_ok_skas(VERIFY_WRITE, to, n) ?
  138. buffer_op((unsigned long) to, n, 1, copy_chunk_to_user, &from) :
  139. n);
  140. }
  141. static int strncpy_chunk_from_user(unsigned long from, int len, void *arg)
  142. {
  143. char **to_ptr = arg, *to = *to_ptr;
  144. int n;
  145. strncpy(to, (void *) from, len);
  146. n = strnlen(to, len);
  147. *to_ptr += n;
  148. if(n < len)
  149. return(1);
  150. return(0);
  151. }
  152. int strncpy_from_user_skas(char *dst, const char __user *src, int count)
  153. {
  154. int n;
  155. char *ptr = dst;
  156. if(segment_eq(get_fs(), KERNEL_DS)){
  157. strncpy(dst, (__force void*)src, count);
  158. return(strnlen(dst, count));
  159. }
  160. if(!access_ok_skas(VERIFY_READ, src, 1))
  161. return(-EFAULT);
  162. n = buffer_op((unsigned long) src, count, 0, strncpy_chunk_from_user,
  163. &ptr);
  164. if(n != 0)
  165. return(-EFAULT);
  166. return(strnlen(dst, count));
  167. }
  168. static int clear_chunk(unsigned long addr, int len, void *unused)
  169. {
  170. memset((void *) addr, 0, len);
  171. return(0);
  172. }
  173. int __clear_user_skas(void __user *mem, int len)
  174. {
  175. return(buffer_op((unsigned long) mem, len, 1, clear_chunk, NULL));
  176. }
  177. int clear_user_skas(void __user *mem, int len)
  178. {
  179. if(segment_eq(get_fs(), KERNEL_DS)){
  180. memset((__force void*)mem, 0, len);
  181. return(0);
  182. }
  183. return(access_ok_skas(VERIFY_WRITE, mem, len) ?
  184. buffer_op((unsigned long) mem, len, 1, clear_chunk, NULL) : len);
  185. }
  186. static int strnlen_chunk(unsigned long str, int len, void *arg)
  187. {
  188. int *len_ptr = arg, n;
  189. n = strnlen((void *) str, len);
  190. *len_ptr += n;
  191. if(n < len)
  192. return(1);
  193. return(0);
  194. }
  195. int strnlen_user_skas(const void __user *str, int len)
  196. {
  197. int count = 0, n;
  198. if(segment_eq(get_fs(), KERNEL_DS))
  199. return(strnlen((__force char*)str, len) + 1);
  200. n = buffer_op((unsigned long) str, len, 0, strnlen_chunk, &count);
  201. if(n == 0)
  202. return(count + 1);
  203. return(-EFAULT);
  204. }
  205. /*
  206. * Overrides for Emacs so that we follow Linus's tabbing style.
  207. * Emacs will notice this stuff at the end of the file and automatically
  208. * adjust the settings for this buffer only. This must remain at the end
  209. * of the file.
  210. * ---------------------------------------------------------------------------
  211. * Local variables:
  212. * c-file-style: "linux"
  213. * End:
  214. */