]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * Copyright (C) 2002 - 2003 Jeff Dike (jdike@addtoit.com) | |
3 | * Licensed under the GPL | |
4 | */ | |
5 | ||
e9c52716 | 6 | #include "linux/compiler.h" |
1da177e4 LT |
7 | #include "linux/stddef.h" |
8 | #include "linux/kernel.h" | |
9 | #include "linux/string.h" | |
10 | #include "linux/fs.h" | |
47e5243a | 11 | #include "linux/hardirq.h" |
1da177e4 LT |
12 | #include "linux/highmem.h" |
13 | #include "asm/page.h" | |
14 | #include "asm/pgtable.h" | |
15 | #include "asm/uaccess.h" | |
16 | #include "kern_util.h" | |
4fef0c10 | 17 | #include "os.h" |
1da177e4 LT |
18 | |
19 | extern void *um_virt_to_phys(struct task_struct *task, unsigned long addr, | |
20 | pte_t *pte_out); | |
21 | ||
22 | static unsigned long maybe_map(unsigned long virt, int is_write) | |
23 | { | |
24 | pte_t pte; | |
25 | int err; | |
26 | ||
27 | void *phys = um_virt_to_phys(current, virt, &pte); | |
28 | int dummy_code; | |
29 | ||
30 | if(IS_ERR(phys) || (is_write && !pte_write(pte))){ | |
31 | err = handle_page_fault(virt, 0, is_write, 1, &dummy_code); | |
32 | if(err) | |
2d58cc9a | 33 | return(-1UL); |
1da177e4 LT |
34 | phys = um_virt_to_phys(current, virt, NULL); |
35 | } | |
2d58cc9a JD |
36 | if(IS_ERR(phys)) |
37 | phys = (void *) -1; | |
38 | ||
1da177e4 LT |
39 | return((unsigned long) phys); |
40 | } | |
41 | ||
47e5243a | 42 | static int do_op_one_page(unsigned long addr, int len, int is_write, |
1da177e4 LT |
43 | int (*op)(unsigned long addr, int len, void *arg), void *arg) |
44 | { | |
45 | struct page *page; | |
46 | int n; | |
47 | ||
48 | addr = maybe_map(addr, is_write); | |
2d58cc9a | 49 | if(addr == -1UL) |
1da177e4 LT |
50 | return(-1); |
51 | ||
52 | page = phys_to_page(addr); | |
47e5243a PBG |
53 | addr = (unsigned long) kmap_atomic(page, KM_UML_USERCOPY) + (addr & ~PAGE_MASK); |
54 | ||
1da177e4 | 55 | n = (*op)(addr, len, arg); |
47e5243a PBG |
56 | |
57 | kunmap_atomic(page, KM_UML_USERCOPY); | |
1da177e4 LT |
58 | |
59 | return(n); | |
60 | } | |
61 | ||
62 | static void do_buffer_op(void *jmpbuf, void *arg_ptr) | |
63 | { | |
64 | va_list args; | |
65 | unsigned long addr; | |
66 | int len, is_write, size, remain, n; | |
67 | int (*op)(unsigned long, int, void *); | |
68 | void *arg; | |
69 | int *res; | |
70 | ||
e9c52716 | 71 | va_copy(args, *(va_list *)arg_ptr); |
1da177e4 LT |
72 | addr = va_arg(args, unsigned long); |
73 | len = va_arg(args, int); | |
74 | is_write = va_arg(args, int); | |
75 | op = va_arg(args, void *); | |
76 | arg = va_arg(args, void *); | |
77 | res = va_arg(args, int *); | |
78 | va_end(args); | |
79 | size = min(PAGE_ALIGN(addr) - addr, (unsigned long) len); | |
80 | remain = len; | |
81 | ||
82 | current->thread.fault_catcher = jmpbuf; | |
47e5243a | 83 | n = do_op_one_page(addr, size, is_write, op, arg); |
1da177e4 LT |
84 | if(n != 0){ |
85 | *res = (n < 0 ? remain : 0); | |
86 | goto out; | |
87 | } | |
88 | ||
89 | addr += size; | |
90 | remain -= size; | |
91 | if(remain == 0){ | |
92 | *res = 0; | |
93 | goto out; | |
94 | } | |
95 | ||
96 | while(addr < ((addr + remain) & PAGE_MASK)){ | |
47e5243a | 97 | n = do_op_one_page(addr, PAGE_SIZE, is_write, op, arg); |
1da177e4 LT |
98 | if(n != 0){ |
99 | *res = (n < 0 ? remain : 0); | |
100 | goto out; | |
101 | } | |
102 | ||
103 | addr += PAGE_SIZE; | |
104 | remain -= PAGE_SIZE; | |
105 | } | |
106 | if(remain == 0){ | |
107 | *res = 0; | |
108 | goto out; | |
109 | } | |
110 | ||
47e5243a | 111 | n = do_op_one_page(addr, remain, is_write, op, arg); |
1da177e4 LT |
112 | if(n != 0) |
113 | *res = (n < 0 ? remain : 0); | |
114 | else *res = 0; | |
115 | out: | |
116 | current->thread.fault_catcher = NULL; | |
117 | } | |
118 | ||
119 | static int buffer_op(unsigned long addr, int len, int is_write, | |
120 | int (*op)(unsigned long addr, int len, void *arg), | |
121 | void *arg) | |
122 | { | |
123 | int faulted, res; | |
124 | ||
125 | faulted = setjmp_wrapper(do_buffer_op, addr, len, is_write, op, arg, | |
126 | &res); | |
127 | if(!faulted) | |
128 | return(res); | |
129 | ||
130 | return(addr + len - (unsigned long) current->thread.fault_addr); | |
131 | } | |
132 | ||
133 | static int copy_chunk_from_user(unsigned long from, int len, void *arg) | |
134 | { | |
135 | unsigned long *to_ptr = arg, to = *to_ptr; | |
136 | ||
137 | memcpy((void *) to, (void *) from, len); | |
138 | *to_ptr += len; | |
139 | return(0); | |
140 | } | |
141 | ||
6aa802ce | 142 | int copy_from_user(void *to, const void __user *from, int n) |
1da177e4 LT |
143 | { |
144 | if(segment_eq(get_fs(), KERNEL_DS)){ | |
145 | memcpy(to, (__force void*)from, n); | |
146 | return(0); | |
147 | } | |
148 | ||
7a590611 | 149 | return(access_ok(VERIFY_READ, from, n) ? |
1da177e4 LT |
150 | buffer_op((unsigned long) from, n, 0, copy_chunk_from_user, &to): |
151 | n); | |
152 | } | |
153 | ||
154 | static int copy_chunk_to_user(unsigned long to, int len, void *arg) | |
155 | { | |
156 | unsigned long *from_ptr = arg, from = *from_ptr; | |
157 | ||
158 | memcpy((void *) to, (void *) from, len); | |
159 | *from_ptr += len; | |
160 | return(0); | |
161 | } | |
162 | ||
6aa802ce | 163 | int copy_to_user(void __user *to, const void *from, int n) |
1da177e4 LT |
164 | { |
165 | if(segment_eq(get_fs(), KERNEL_DS)){ | |
166 | memcpy((__force void*)to, from, n); | |
167 | return(0); | |
168 | } | |
169 | ||
7a590611 | 170 | return(access_ok(VERIFY_WRITE, to, n) ? |
1da177e4 LT |
171 | buffer_op((unsigned long) to, n, 1, copy_chunk_to_user, &from) : |
172 | n); | |
173 | } | |
174 | ||
175 | static int strncpy_chunk_from_user(unsigned long from, int len, void *arg) | |
176 | { | |
177 | char **to_ptr = arg, *to = *to_ptr; | |
178 | int n; | |
179 | ||
180 | strncpy(to, (void *) from, len); | |
181 | n = strnlen(to, len); | |
182 | *to_ptr += n; | |
183 | ||
184 | if(n < len) | |
185 | return(1); | |
186 | return(0); | |
187 | } | |
188 | ||
6aa802ce | 189 | int strncpy_from_user(char *dst, const char __user *src, int count) |
1da177e4 LT |
190 | { |
191 | int n; | |
192 | char *ptr = dst; | |
193 | ||
194 | if(segment_eq(get_fs(), KERNEL_DS)){ | |
195 | strncpy(dst, (__force void*)src, count); | |
196 | return(strnlen(dst, count)); | |
197 | } | |
198 | ||
7a590611 | 199 | if(!access_ok(VERIFY_READ, src, 1)) |
1da177e4 LT |
200 | return(-EFAULT); |
201 | ||
202 | n = buffer_op((unsigned long) src, count, 0, strncpy_chunk_from_user, | |
203 | &ptr); | |
204 | if(n != 0) | |
205 | return(-EFAULT); | |
206 | return(strnlen(dst, count)); | |
207 | } | |
208 | ||
209 | static int clear_chunk(unsigned long addr, int len, void *unused) | |
210 | { | |
211 | memset((void *) addr, 0, len); | |
212 | return(0); | |
213 | } | |
214 | ||
6aa802ce | 215 | int __clear_user(void __user *mem, int len) |
1da177e4 LT |
216 | { |
217 | return(buffer_op((unsigned long) mem, len, 1, clear_chunk, NULL)); | |
218 | } | |
219 | ||
6aa802ce | 220 | int clear_user(void __user *mem, int len) |
1da177e4 LT |
221 | { |
222 | if(segment_eq(get_fs(), KERNEL_DS)){ | |
223 | memset((__force void*)mem, 0, len); | |
224 | return(0); | |
225 | } | |
226 | ||
7a590611 | 227 | return(access_ok(VERIFY_WRITE, mem, len) ? |
1da177e4 LT |
228 | buffer_op((unsigned long) mem, len, 1, clear_chunk, NULL) : len); |
229 | } | |
230 | ||
231 | static int strnlen_chunk(unsigned long str, int len, void *arg) | |
232 | { | |
233 | int *len_ptr = arg, n; | |
234 | ||
235 | n = strnlen((void *) str, len); | |
236 | *len_ptr += n; | |
237 | ||
238 | if(n < len) | |
239 | return(1); | |
240 | return(0); | |
241 | } | |
242 | ||
6aa802ce | 243 | int strnlen_user(const void __user *str, int len) |
1da177e4 LT |
244 | { |
245 | int count = 0, n; | |
246 | ||
247 | if(segment_eq(get_fs(), KERNEL_DS)) | |
248 | return(strnlen((__force char*)str, len) + 1); | |
249 | ||
250 | n = buffer_op((unsigned long) str, len, 0, strnlen_chunk, &count); | |
251 | if(n == 0) | |
252 | return(count + 1); | |
253 | return(-EFAULT); | |
254 | } | |
255 | ||
256 | /* | |
257 | * Overrides for Emacs so that we follow Linus's tabbing style. | |
258 | * Emacs will notice this stuff at the end of the file and automatically | |
259 | * adjust the settings for this buffer only. This must remain at the end | |
260 | * of the file. | |
261 | * --------------------------------------------------------------------------- | |
262 | * Local variables: | |
263 | * c-file-style: "linux" | |
264 | * End: | |
265 | */ |