Commit | Line | Data |
---|---|---|
b2441318 | 1 | /* SPDX-License-Identifier: GPL-2.0 */ |
eed417dd AB |
2 | #ifndef __ASM_GENERIC_UACCESS_H |
3 | #define __ASM_GENERIC_UACCESS_H | |
4 | ||
5 | /* | |
6 | * User space memory access functions, these should work | |
0a4a6647 | 7 | * on any machine that has kernel and user data in the same |
eed417dd AB |
8 | * address space, e.g. all NOMMU machines. |
9 | */ | |
eed417dd AB |
10 | #include <linux/string.h> |
11 | ||
bd79f947 CH |
12 | #ifdef CONFIG_UACCESS_MEMCPY |
13 | static inline __must_check unsigned long | |
14 | raw_copy_from_user(void *to, const void __user * from, unsigned long n) | |
15 | { | |
16 | if (__builtin_constant_p(n)) { | |
17 | switch(n) { | |
18 | case 1: | |
19 | *(u8 *)to = *(u8 __force *)from; | |
20 | return 0; | |
21 | case 2: | |
22 | *(u16 *)to = *(u16 __force *)from; | |
23 | return 0; | |
24 | case 4: | |
25 | *(u32 *)to = *(u32 __force *)from; | |
26 | return 0; | |
6edd1dba CH |
27 | #ifdef CONFIG_64BIT |
28 | case 8: | |
29 | *(u64 *)to = *(u64 __force *)from; | |
30 | return 0; | |
31 | #endif | |
bd79f947 CH |
32 | } |
33 | } | |
34 | ||
35 | memcpy(to, (const void __force *)from, n); | |
36 | return 0; | |
37 | } | |
38 | ||
39 | static inline __must_check unsigned long | |
40 | raw_copy_to_user(void __user *to, const void *from, unsigned long n) | |
41 | { | |
42 | if (__builtin_constant_p(n)) { | |
43 | switch(n) { | |
44 | case 1: | |
45 | *(u8 __force *)to = *(u8 *)from; | |
46 | return 0; | |
47 | case 2: | |
48 | *(u16 __force *)to = *(u16 *)from; | |
49 | return 0; | |
50 | case 4: | |
51 | *(u32 __force *)to = *(u32 *)from; | |
52 | return 0; | |
6edd1dba CH |
53 | #ifdef CONFIG_64BIT |
54 | case 8: | |
55 | *(u64 __force *)to = *(u64 *)from; | |
56 | return 0; | |
57 | #endif | |
bd79f947 CH |
58 | default: |
59 | break; | |
60 | } | |
61 | } | |
62 | ||
63 | memcpy((void __force *)to, from, n); | |
64 | return 0; | |
65 | } | |
66 | #define INLINE_COPY_FROM_USER | |
67 | #define INLINE_COPY_TO_USER | |
68 | #endif /* CONFIG_UACCESS_MEMCPY */ | |
69 | ||
eed417dd AB |
70 | #define MAKE_MM_SEG(s) ((mm_segment_t) { (s) }) |
71 | ||
72 | #ifndef KERNEL_DS | |
73 | #define KERNEL_DS MAKE_MM_SEG(~0UL) | |
74 | #endif | |
75 | ||
76 | #ifndef USER_DS | |
77 | #define USER_DS MAKE_MM_SEG(TASK_SIZE - 1) | |
78 | #endif | |
79 | ||
80 | #ifndef get_fs | |
eed417dd AB |
81 | #define get_fs() (current_thread_info()->addr_limit) |
82 | ||
83 | static inline void set_fs(mm_segment_t fs) | |
84 | { | |
85 | current_thread_info()->addr_limit = fs; | |
86 | } | |
87 | #endif | |
88 | ||
10a6007b | 89 | #ifndef segment_eq |
eed417dd | 90 | #define segment_eq(a, b) ((a).seg == (b).seg) |
10a6007b | 91 | #endif |
eed417dd | 92 | |
96d4f267 | 93 | #define access_ok(addr, size) __access_ok((unsigned long)(addr),(size)) |
eed417dd AB |
94 | |
95 | /* | |
96 | * The architecture should really override this if possible, at least | |
97 | * doing a check on the get_fs() | |
98 | */ | |
99 | #ifndef __access_ok | |
100 | static inline int __access_ok(unsigned long addr, unsigned long size) | |
101 | { | |
102 | return 1; | |
103 | } | |
104 | #endif | |
105 | ||
eed417dd AB |
106 | /* |
107 | * These are the main single-value transfer routines. They automatically | |
108 | * use the right size if we just have the right pointer type. | |
109 | * This version just falls back to copy_{from,to}_user, which should | |
110 | * provide a fast-path for small values. | |
111 | */ | |
112 | #define __put_user(x, ptr) \ | |
113 | ({ \ | |
114 | __typeof__(*(ptr)) __x = (x); \ | |
115 | int __pu_err = -EFAULT; \ | |
116 | __chk_user_ptr(ptr); \ | |
117 | switch (sizeof (*(ptr))) { \ | |
118 | case 1: \ | |
119 | case 2: \ | |
120 | case 4: \ | |
121 | case 8: \ | |
122 | __pu_err = __put_user_fn(sizeof (*(ptr)), \ | |
123 | ptr, &__x); \ | |
124 | break; \ | |
125 | default: \ | |
126 | __put_user_bad(); \ | |
127 | break; \ | |
128 | } \ | |
129 | __pu_err; \ | |
130 | }) | |
131 | ||
132 | #define put_user(x, ptr) \ | |
133 | ({ \ | |
1985296a | 134 | void __user *__p = (ptr); \ |
e0acd0bd | 135 | might_fault(); \ |
96d4f267 | 136 | access_ok(__p, sizeof(*ptr)) ? \ |
1985296a | 137 | __put_user((x), ((__typeof__(*(ptr)) __user *)__p)) : \ |
eed417dd AB |
138 | -EFAULT; \ |
139 | }) | |
140 | ||
05d88a49 VG |
141 | #ifndef __put_user_fn |
142 | ||
eed417dd AB |
143 | static inline int __put_user_fn(size_t size, void __user *ptr, void *x) |
144 | { | |
d597580d | 145 | return unlikely(raw_copy_to_user(ptr, x, size)) ? -EFAULT : 0; |
eed417dd AB |
146 | } |
147 | ||
05d88a49 VG |
148 | #define __put_user_fn(sz, u, k) __put_user_fn(sz, u, k) |
149 | ||
150 | #endif | |
151 | ||
eed417dd AB |
152 | extern int __put_user_bad(void) __attribute__((noreturn)); |
153 | ||
154 | #define __get_user(x, ptr) \ | |
155 | ({ \ | |
156 | int __gu_err = -EFAULT; \ | |
157 | __chk_user_ptr(ptr); \ | |
158 | switch (sizeof(*(ptr))) { \ | |
159 | case 1: { \ | |
c1aad8dc | 160 | unsigned char __x = 0; \ |
eed417dd AB |
161 | __gu_err = __get_user_fn(sizeof (*(ptr)), \ |
162 | ptr, &__x); \ | |
163 | (x) = *(__force __typeof__(*(ptr)) *) &__x; \ | |
164 | break; \ | |
165 | }; \ | |
166 | case 2: { \ | |
c1aad8dc | 167 | unsigned short __x = 0; \ |
eed417dd AB |
168 | __gu_err = __get_user_fn(sizeof (*(ptr)), \ |
169 | ptr, &__x); \ | |
170 | (x) = *(__force __typeof__(*(ptr)) *) &__x; \ | |
171 | break; \ | |
172 | }; \ | |
173 | case 4: { \ | |
c1aad8dc | 174 | unsigned int __x = 0; \ |
eed417dd AB |
175 | __gu_err = __get_user_fn(sizeof (*(ptr)), \ |
176 | ptr, &__x); \ | |
177 | (x) = *(__force __typeof__(*(ptr)) *) &__x; \ | |
178 | break; \ | |
179 | }; \ | |
180 | case 8: { \ | |
c1aad8dc | 181 | unsigned long long __x = 0; \ |
eed417dd AB |
182 | __gu_err = __get_user_fn(sizeof (*(ptr)), \ |
183 | ptr, &__x); \ | |
184 | (x) = *(__force __typeof__(*(ptr)) *) &__x; \ | |
185 | break; \ | |
186 | }; \ | |
187 | default: \ | |
188 | __get_user_bad(); \ | |
189 | break; \ | |
190 | } \ | |
191 | __gu_err; \ | |
192 | }) | |
193 | ||
194 | #define get_user(x, ptr) \ | |
195 | ({ \ | |
1985296a | 196 | const void __user *__p = (ptr); \ |
e0acd0bd | 197 | might_fault(); \ |
96d4f267 | 198 | access_ok(__p, sizeof(*ptr)) ? \ |
1985296a | 199 | __get_user((x), (__typeof__(*(ptr)) __user *)__p) :\ |
9ad18b75 | 200 | ((x) = (__typeof__(*(ptr)))0,-EFAULT); \ |
eed417dd AB |
201 | }) |
202 | ||
05d88a49 | 203 | #ifndef __get_user_fn |
eed417dd AB |
204 | static inline int __get_user_fn(size_t size, const void __user *ptr, void *x) |
205 | { | |
d597580d | 206 | return unlikely(raw_copy_from_user(x, ptr, size)) ? -EFAULT : 0; |
eed417dd AB |
207 | } |
208 | ||
05d88a49 VG |
209 | #define __get_user_fn(sz, u, k) __get_user_fn(sz, u, k) |
210 | ||
211 | #endif | |
212 | ||
eed417dd AB |
213 | extern int __get_user_bad(void) __attribute__((noreturn)); |
214 | ||
eed417dd AB |
215 | /* |
216 | * Copy a null terminated string from userspace. | |
217 | */ | |
218 | #ifndef __strncpy_from_user | |
219 | static inline long | |
220 | __strncpy_from_user(char *dst, const char __user *src, long count) | |
221 | { | |
222 | char *tmp; | |
223 | strncpy(dst, (const char __force *)src, count); | |
224 | for (tmp = dst; *tmp && count > 0; tmp++, count--) | |
225 | ; | |
226 | return (tmp - dst); | |
227 | } | |
228 | #endif | |
229 | ||
230 | static inline long | |
231 | strncpy_from_user(char *dst, const char __user *src, long count) | |
232 | { | |
96d4f267 | 233 | if (!access_ok(src, 1)) |
eed417dd AB |
234 | return -EFAULT; |
235 | return __strncpy_from_user(dst, src, count); | |
236 | } | |
237 | ||
238 | /* | |
239 | * Return the size of a string (including the ending 0) | |
240 | * | |
241 | * Return 0 on exception, a value greater than N if too long | |
242 | */ | |
7f509a9e | 243 | #ifndef __strnlen_user |
830f5800 | 244 | #define __strnlen_user(s, n) (strnlen((s), (n)) + 1) |
7f509a9e G |
245 | #endif |
246 | ||
830f5800 MS |
247 | /* |
248 | * Unlike strnlen, strnlen_user includes the nul terminator in | |
249 | * its returned count. Callers should check for a returned value | |
250 | * greater than N as an indication the string is too long. | |
251 | */ | |
eed417dd AB |
252 | static inline long strnlen_user(const char __user *src, long n) |
253 | { | |
96d4f267 | 254 | if (!access_ok(src, 1)) |
9844813f | 255 | return 0; |
7f509a9e | 256 | return __strnlen_user(src, n); |
eed417dd | 257 | } |
eed417dd | 258 | |
eed417dd AB |
259 | /* |
260 | * Zero Userspace | |
261 | */ | |
262 | #ifndef __clear_user | |
263 | static inline __must_check unsigned long | |
264 | __clear_user(void __user *to, unsigned long n) | |
265 | { | |
266 | memset((void __force *)to, 0, n); | |
267 | return 0; | |
268 | } | |
269 | #endif | |
270 | ||
271 | static inline __must_check unsigned long | |
272 | clear_user(void __user *to, unsigned long n) | |
273 | { | |
e0acd0bd | 274 | might_fault(); |
96d4f267 | 275 | if (!access_ok(to, n)) |
eed417dd AB |
276 | return n; |
277 | ||
278 | return __clear_user(to, n); | |
279 | } | |
280 | ||
aaa2e7ac AV |
281 | #include <asm/extable.h> |
282 | ||
eed417dd | 283 | #endif /* __ASM_GENERIC_UACCESS_H */ |