blob: 3d90e1018ee228c443e838632b386bc5b224b000 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/* uaccess.h: userspace accessor functions
2 *
3 * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
4 * Written by David Howells (dhowells@redhat.com)
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 */
11
12#ifndef _ASM_UACCESS_H
13#define _ASM_UACCESS_H
14
15/*
16 * User space memory access functions
17 */
18#include <linux/sched.h>
19#include <linux/mm.h>
20#include <asm/segment.h>
21#include <asm/sections.h>
22
23#define HAVE_ARCH_UNMAPPED_AREA /* we decide where to put mmaps */
24
Al Viroa8a77572006-06-23 02:04:03 -070025#define __ptr(x) ((unsigned long __force *)(x))
Linus Torvalds1da177e2005-04-16 15:20:36 -070026
27#define VERIFY_READ 0
28#define VERIFY_WRITE 1
29
30#define __addr_ok(addr) ((unsigned long)(addr) < get_addr_limit())
31
32/*
33 * check that a range of addresses falls within the current address limit
34 */
35static inline int ___range_ok(unsigned long addr, unsigned long size)
36{
37#ifdef CONFIG_MMU
38 int flag = -EFAULT, tmp;
39
40 asm volatile (
41 " addcc %3,%2,%1,icc0 \n" /* set C-flag if addr+size>4GB */
42 " subcc.p %1,%4,gr0,icc1 \n" /* jump if addr+size>limit */
43 " bc icc0,#0,0f \n"
44 " bhi icc1,#0,0f \n"
45 " setlos #0,%0 \n" /* mark okay */
46 "0: \n"
47 : "=r"(flag), "=&r"(tmp)
48 : "r"(addr), "r"(size), "r"(get_addr_limit()), "0"(flag)
49 );
50
51 return flag;
52
53#else
54
55 if (addr < memory_start ||
56 addr > memory_end ||
57 size > memory_end - memory_start ||
58 addr + size > memory_end)
59 return -EFAULT;
60
61 return 0;
62#endif
63}
64
65#define __range_ok(addr,size) ___range_ok((unsigned long) (addr), (unsigned long) (size))
66
Al Viroa8a77572006-06-23 02:04:03 -070067#define access_ok(type,addr,size) (__range_ok((void __user *)(addr), (size)) == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -070068#define __access_ok(addr,size) (__range_ok((addr), (size)) == 0)
69
Linus Torvalds1da177e2005-04-16 15:20:36 -070070/*
71 * The exception table consists of pairs of addresses: the first is the
72 * address of an instruction that is allowed to fault, and the second is
73 * the address at which the program should continue. No registers are
74 * modified, so it is entirely up to the continuation code to figure out
75 * what to do.
76 *
77 * All the routines below use bits of fixup code that are out of line
78 * with the main instruction path. This means when everything is well,
79 * we don't even have to jump over them. Further, they do not intrude
80 * on our cache or tlb entries.
81 */
82struct exception_table_entry
83{
84 unsigned long insn, fixup;
85};
86
87/* Returns 0 if exception not found and fixup otherwise. */
88extern unsigned long search_exception_table(unsigned long);
89
90
91/*
92 * These are the main single-value transfer routines. They automatically
93 * use the right size if we just have the right pointer type.
94 */
95#define __put_user(x, ptr) \
96({ \
97 int __pu_err = 0; \
98 \
99 typeof(*(ptr)) __pu_val = (x); \
Al Viroa8a77572006-06-23 02:04:03 -0700100 __chk_user_ptr(ptr); \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101 \
102 switch (sizeof (*(ptr))) { \
103 case 1: \
104 __put_user_asm(__pu_err, __pu_val, ptr, "b", "r"); \
105 break; \
106 case 2: \
107 __put_user_asm(__pu_err, __pu_val, ptr, "h", "r"); \
108 break; \
109 case 4: \
110 __put_user_asm(__pu_err, __pu_val, ptr, "", "r"); \
111 break; \
112 case 8: \
113 __put_user_asm(__pu_err, __pu_val, ptr, "d", "e"); \
114 break; \
115 default: \
116 __pu_err = __put_user_bad(); \
117 break; \
118 } \
119 __pu_err; \
120})
121
122#define put_user(x, ptr) \
123({ \
Al Viroa8a77572006-06-23 02:04:03 -0700124 typeof(*(ptr)) __user *_p = (ptr); \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125 int _e; \
126 \
127 _e = __range_ok(_p, sizeof(*_p)); \
128 if (_e == 0) \
129 _e = __put_user((x), _p); \
130 _e; \
131})
132
133extern int __put_user_bad(void);
134
135/*
136 * Tell gcc we read from memory instead of writing: this is because
137 * we do not write to any memory gcc knows about, so there are no
138 * aliasing issues.
139 */
140
141#ifdef CONFIG_MMU
142
143#define __put_user_asm(err,x,ptr,dsize,constraint) \
144do { \
145 asm volatile("1: st"dsize"%I1 %2,%M1 \n" \
146 "2: \n" \
147 ".subsection 2 \n" \
148 "3: setlos %3,%0 \n" \
149 " bra 2b \n" \
150 ".previous \n" \
151 ".section __ex_table,\"a\" \n" \
152 " .balign 8 \n" \
153 " .long 1b,3b \n" \
154 ".previous" \
155 : "=r" (err) \
156 : "m" (*__ptr(ptr)), constraint (x), "i"(-EFAULT), "0"(err) \
157 : "memory"); \
158} while (0)
159
160#else
161
162#define __put_user_asm(err,x,ptr,bwl,con) \
163do { \
164 asm(" st"bwl"%I0 %1,%M0 \n" \
165 " membar \n" \
166 : \
167 : "m" (*__ptr(ptr)), con (x) \
168 : "memory"); \
169} while (0)
170
171#endif
172
173/*****************************************************************************/
174/*
175 *
176 */
177#define __get_user(x, ptr) \
178({ \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179 int __gu_err = 0; \
Al Viroa8a77572006-06-23 02:04:03 -0700180 __chk_user_ptr(ptr); \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181 \
182 switch (sizeof(*(ptr))) { \
Al Viroa8a77572006-06-23 02:04:03 -0700183 case 1: { \
184 unsigned char __gu_val; \
185 __get_user_asm(__gu_err, __gu_val, ptr, "ub", "=r"); \
186 (x) = *(__force __typeof__(*(ptr)) *) &__gu_val; \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187 break; \
Al Viroa8a77572006-06-23 02:04:03 -0700188 } \
189 case 2: { \
190 unsigned short __gu_val; \
191 __get_user_asm(__gu_err, __gu_val, ptr, "uh", "=r"); \
192 (x) = *(__force __typeof__(*(ptr)) *) &__gu_val; \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700193 break; \
Al Viroa8a77572006-06-23 02:04:03 -0700194 } \
195 case 4: { \
196 unsigned int __gu_val; \
197 __get_user_asm(__gu_err, __gu_val, ptr, "", "=r"); \
198 (x) = *(__force __typeof__(*(ptr)) *) &__gu_val; \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199 break; \
Al Viroa8a77572006-06-23 02:04:03 -0700200 } \
201 case 8: { \
202 unsigned long long __gu_val; \
203 __get_user_asm(__gu_err, __gu_val, ptr, "d", "=e"); \
204 (x) = *(__force __typeof__(*(ptr)) *) &__gu_val; \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205 break; \
Al Viroa8a77572006-06-23 02:04:03 -0700206 } \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207 default: \
208 __gu_err = __get_user_bad(); \
209 break; \
210 } \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211 __gu_err; \
212})
213
214#define get_user(x, ptr) \
215({ \
Al Viroa8a77572006-06-23 02:04:03 -0700216 const typeof(*(ptr)) __user *_p = (ptr);\
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217 int _e; \
218 \
219 _e = __range_ok(_p, sizeof(*_p)); \
220 if (likely(_e == 0)) \
221 _e = __get_user((x), _p); \
222 else \
223 (x) = (typeof(x)) 0; \
224 _e; \
225})
226
227extern int __get_user_bad(void);
228
229#ifdef CONFIG_MMU
230
231#define __get_user_asm(err,x,ptr,dtype,constraint) \
232do { \
233 asm("1: ld"dtype"%I2 %M2,%1 \n" \
234 "2: \n" \
235 ".subsection 2 \n" \
236 "3: setlos %3,%0 \n" \
237 " setlos #0,%1 \n" \
238 " bra 2b \n" \
239 ".previous \n" \
240 ".section __ex_table,\"a\" \n" \
241 " .balign 8 \n" \
242 " .long 1b,3b \n" \
243 ".previous" \
244 : "=r" (err), constraint (x) \
245 : "m" (*__ptr(ptr)), "i"(-EFAULT), "0"(err) \
246 ); \
247} while(0)
248
249#else
250
251#define __get_user_asm(err,x,ptr,bwl,con) \
252 asm(" ld"bwl"%I1 %M1,%0 \n" \
253 " membar \n" \
254 : con(x) \
255 : "m" (*__ptr(ptr)))
256
257#endif
258
259/*****************************************************************************/
260/*
261 *
262 */
Al Viroa8a77572006-06-23 02:04:03 -0700263#define ____force(x) (__force void *)(void __user *)(x)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264#ifdef CONFIG_MMU
265extern long __memset_user(void *dst, unsigned long count);
266extern long __memcpy_user(void *dst, const void *src, unsigned long count);
267
Al Viroa8a77572006-06-23 02:04:03 -0700268#define clear_user(dst,count) __memset_user(____force(dst), (count))
269#define __copy_from_user_inatomic(to, from, n) __memcpy_user((to), ____force(from), (n))
270#define __copy_to_user_inatomic(to, from, n) __memcpy_user(____force(to), (from), (n))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271
272#else
273
Al Viroa8a77572006-06-23 02:04:03 -0700274#define clear_user(dst,count) (memset(____force(dst), 0, (count)), 0)
275#define __copy_from_user_inatomic(to, from, n) (memcpy((to), ____force(from), (n)), 0)
276#define __copy_to_user_inatomic(to, from, n) (memcpy(____force(to), (from), (n)), 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277
278#endif
279
280static inline unsigned long __must_check
281__copy_to_user(void __user *to, const void *from, unsigned long n)
282{
283 might_sleep();
284 return __copy_to_user_inatomic(to, from, n);
285}
286
287static inline unsigned long
288__copy_from_user(void *to, const void __user *from, unsigned long n)
289{
290 might_sleep();
291 return __copy_from_user_inatomic(to, from, n);
292}
293
Al Viroa8a77572006-06-23 02:04:03 -0700294static inline long copy_from_user(void *to, const void __user *from, unsigned long n)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295{
296 unsigned long ret = n;
297
298 if (likely(__access_ok(from, n)))
299 ret = __copy_from_user(to, from, n);
300
301 if (unlikely(ret != 0))
302 memset(to + (n - ret), 0, ret);
303
304 return ret;
305}
306
Al Viroa8a77572006-06-23 02:04:03 -0700307static inline long copy_to_user(void __user *to, const void *from, unsigned long n)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308{
309 return likely(__access_ok(to, n)) ? __copy_to_user(to, from, n) : n;
310}
311
Al Viroa8a77572006-06-23 02:04:03 -0700312extern long strncpy_from_user(char *dst, const char __user *src, long count);
313extern long strnlen_user(const char __user *src, long count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314
315#define strlen_user(str) strnlen_user(str, 32767)
316
317extern unsigned long search_exception_table(unsigned long addr);
318
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319#endif /* _ASM_UACCESS_H */