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