Merge tag 'backlight-next-4.21' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-microblaze.git] / arch / um / kernel / skas / uaccess.c
1 /*
2  * Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
3  * Licensed under the GPL
4  */
5
6 #include <linux/err.h>
7 #include <linux/highmem.h>
8 #include <linux/mm.h>
9 #include <linux/module.h>
10 #include <linux/sched.h>
11 #include <asm/current.h>
12 #include <asm/page.h>
13 #include <asm/pgtable.h>
14 #include <kern_util.h>
15 #include <os.h>
16
17 pte_t *virt_to_pte(struct mm_struct *mm, unsigned long addr)
18 {
19         pgd_t *pgd;
20         pud_t *pud;
21         pmd_t *pmd;
22
23         if (mm == NULL)
24                 return NULL;
25
26         pgd = pgd_offset(mm, addr);
27         if (!pgd_present(*pgd))
28                 return NULL;
29
30         pud = pud_offset(pgd, addr);
31         if (!pud_present(*pud))
32                 return NULL;
33
34         pmd = pmd_offset(pud, addr);
35         if (!pmd_present(*pmd))
36                 return NULL;
37
38         return pte_offset_kernel(pmd, addr);
39 }
40
41 static pte_t *maybe_map(unsigned long virt, int is_write)
42 {
43         pte_t *pte = virt_to_pte(current->mm, virt);
44         int err, dummy_code;
45
46         if ((pte == NULL) || !pte_present(*pte) ||
47             (is_write && !pte_write(*pte))) {
48                 err = handle_page_fault(virt, 0, is_write, 1, &dummy_code);
49                 if (err)
50                         return NULL;
51                 pte = virt_to_pte(current->mm, virt);
52         }
53         if (!pte_present(*pte))
54                 pte = NULL;
55
56         return pte;
57 }
58
59 static int do_op_one_page(unsigned long addr, int len, int is_write,
60                  int (*op)(unsigned long addr, int len, void *arg), void *arg)
61 {
62         jmp_buf buf;
63         struct page *page;
64         pte_t *pte;
65         int n;
66
67         pte = maybe_map(addr, is_write);
68         if (pte == NULL)
69                 return -1;
70
71         page = pte_page(*pte);
72 #ifdef CONFIG_64BIT
73         pagefault_disable();
74         addr = (unsigned long) page_address(page) +
75                 (addr & ~PAGE_MASK);
76 #else
77         addr = (unsigned long) kmap_atomic(page) +
78                 (addr & ~PAGE_MASK);
79 #endif
80         n = (*op)(addr, len, arg);
81
82 #ifdef CONFIG_64BIT
83         pagefault_enable();
84 #else
85         kunmap_atomic((void *)addr);
86 #endif
87
88         return n;
89 }
90
91 static long buffer_op(unsigned long addr, int len, int is_write,
92                       int (*op)(unsigned long, int, void *), void *arg)
93 {
94         long size, remain, n;
95
96         size = min(PAGE_ALIGN(addr) - addr, (unsigned long) len);
97         remain = len;
98
99         n = do_op_one_page(addr, size, is_write, op, arg);
100         if (n != 0) {
101                 remain = (n < 0 ? remain : 0);
102                 goto out;
103         }
104
105         addr += size;
106         remain -= size;
107         if (remain == 0)
108                 goto out;
109
110         while (addr < ((addr + remain) & PAGE_MASK)) {
111                 n = do_op_one_page(addr, PAGE_SIZE, is_write, op, arg);
112                 if (n != 0) {
113                         remain = (n < 0 ? remain : 0);
114                         goto out;
115                 }
116
117                 addr += PAGE_SIZE;
118                 remain -= PAGE_SIZE;
119         }
120         if (remain == 0)
121                 goto out;
122
123         n = do_op_one_page(addr, remain, is_write, op, arg);
124         if (n != 0) {
125                 remain = (n < 0 ? remain : 0);
126                 goto out;
127         }
128
129         return 0;
130  out:
131         return remain;
132 }
133
134 static int copy_chunk_from_user(unsigned long from, int len, void *arg)
135 {
136         unsigned long *to_ptr = arg, to = *to_ptr;
137
138         memcpy((void *) to, (void *) from, len);
139         *to_ptr += len;
140         return 0;
141 }
142
143 unsigned long raw_copy_from_user(void *to, const void __user *from, unsigned long n)
144 {
145         if (uaccess_kernel()) {
146                 memcpy(to, (__force void*)from, n);
147                 return 0;
148         }
149
150         return buffer_op((unsigned long) from, n, 0, copy_chunk_from_user, &to);
151 }
152 EXPORT_SYMBOL(raw_copy_from_user);
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
163 unsigned long raw_copy_to_user(void __user *to, const void *from, unsigned long n)
164 {
165         if (uaccess_kernel()) {
166                 memcpy((__force void *) to, from, n);
167                 return 0;
168         }
169
170         return buffer_op((unsigned long) to, n, 1, copy_chunk_to_user, &from);
171 }
172 EXPORT_SYMBOL(raw_copy_to_user);
173
174 static int strncpy_chunk_from_user(unsigned long from, int len, void *arg)
175 {
176         char **to_ptr = arg, *to = *to_ptr;
177         int n;
178
179         strncpy(to, (void *) from, len);
180         n = strnlen(to, len);
181         *to_ptr += n;
182
183         if (n < len)
184                 return 1;
185         return 0;
186 }
187
188 long __strncpy_from_user(char *dst, const char __user *src, long count)
189 {
190         long n;
191         char *ptr = dst;
192
193         if (uaccess_kernel()) {
194                 strncpy(dst, (__force void *) src, count);
195                 return strnlen(dst, count);
196         }
197
198         n = buffer_op((unsigned long) src, count, 0, strncpy_chunk_from_user,
199                       &ptr);
200         if (n != 0)
201                 return -EFAULT;
202         return strnlen(dst, count);
203 }
204 EXPORT_SYMBOL(__strncpy_from_user);
205
206 static int clear_chunk(unsigned long addr, int len, void *unused)
207 {
208         memset((void *) addr, 0, len);
209         return 0;
210 }
211
212 unsigned long __clear_user(void __user *mem, unsigned long len)
213 {
214         if (uaccess_kernel()) {
215                 memset((__force void*)mem, 0, len);
216                 return 0;
217         }
218
219         return buffer_op((unsigned long) mem, len, 1, clear_chunk, NULL);
220 }
221 EXPORT_SYMBOL(__clear_user);
222
223 static int strnlen_chunk(unsigned long str, int len, void *arg)
224 {
225         int *len_ptr = arg, n;
226
227         n = strnlen((void *) str, len);
228         *len_ptr += n;
229
230         if (n < len)
231                 return 1;
232         return 0;
233 }
234
235 long __strnlen_user(const void __user *str, long len)
236 {
237         int count = 0, n;
238
239         if (uaccess_kernel())
240                 return strnlen((__force char*)str, len) + 1;
241
242         n = buffer_op((unsigned long) str, len, 0, strnlen_chunk, &count);
243         if (n == 0)
244                 return count + 1;
245         return 0;
246 }
247 EXPORT_SYMBOL(__strnlen_user);