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