uaccess_64.h 6.3 KB
Newer Older
H
H. Peter Anvin 已提交
1 2
#ifndef _ASM_X86_UACCESS_64_H
#define _ASM_X86_UACCESS_64_H
L
Linus Torvalds 已提交
3 4 5 6 7 8

/*
 * User space memory access functions
 */
#include <linux/compiler.h>
#include <linux/errno.h>
9
#include <linux/lockdep.h>
10 11
#include <asm/alternative.h>
#include <asm/cpufeature.h>
L
Linus Torvalds 已提交
12 13 14 15 16 17 18
#include <asm/page.h>

/*
 * Copy To/From Userspace
 */

/* Handles exceptions in both to and from, but doesn't do access_ok */
19
__must_check unsigned long
20 21
copy_user_enhanced_fast_string(void *to, const void *from, unsigned len);
__must_check unsigned long
22 23 24 25 26 27 28 29 30
copy_user_generic_string(void *to, const void *from, unsigned len);
__must_check unsigned long
copy_user_generic_unrolled(void *to, const void *from, unsigned len);

static __always_inline __must_check unsigned long
copy_user_generic(void *to, const void *from, unsigned len)
{
	unsigned ret;

31 32 33 34 35 36
	/*
	 * If CPU has ERMS feature, use copy_user_enhanced_fast_string.
	 * Otherwise, if CPU has rep_good feature, use copy_user_generic_string.
	 * Otherwise, use copy_user_generic_unrolled.
	 */
	alternative_call_2(copy_user_generic_unrolled,
37 38
			 copy_user_generic_string,
			 X86_FEATURE_REP_GOOD,
39 40
			 copy_user_enhanced_fast_string,
			 X86_FEATURE_ERMS,
41 42 43 44 45 46
			 ASM_OUTPUT2("=a" (ret), "=D" (to), "=S" (from),
				     "=d" (len)),
			 "1" (to), "2" (from), "3" (len)
			 : "memory", "rcx", "r8", "r9", "r10", "r11");
	return ret;
}
47 48 49 50 51

__must_check unsigned long
copy_in_user(void __user *to, const void __user *from, unsigned len);

static __always_inline __must_check
52
int __copy_from_user_nocheck(void *dst, const void __user *src, unsigned size)
53
{
54
	int ret = 0;
55

L
Linus Torvalds 已提交
56
	if (!__builtin_constant_p(size))
57 58 59 60
		return copy_user_generic(dst, (__force void *)src, size);
	switch (size) {
	case 1:__get_user_asm(*(u8 *)dst, (u8 __user *)src,
			      ret, "b", "b", "=q", 1);
L
Linus Torvalds 已提交
61
		return ret;
62 63
	case 2:__get_user_asm(*(u16 *)dst, (u16 __user *)src,
			      ret, "w", "w", "=r", 2);
L
Linus Torvalds 已提交
64
		return ret;
65 66 67 68 69
	case 4:__get_user_asm(*(u32 *)dst, (u32 __user *)src,
			      ret, "l", "k", "=r", 4);
		return ret;
	case 8:__get_user_asm(*(u64 *)dst, (u64 __user *)src,
			      ret, "q", "", "=r", 8);
L
Linus Torvalds 已提交
70 71
		return ret;
	case 10:
72
		__get_user_asm(*(u64 *)dst, (u64 __user *)src,
73
			       ret, "q", "", "=r", 10);
74 75 76 77 78 79
		if (unlikely(ret))
			return ret;
		__get_user_asm(*(u16 *)(8 + (char *)dst),
			       (u16 __user *)(8 + (char __user *)src),
			       ret, "w", "w", "=r", 2);
		return ret;
L
Linus Torvalds 已提交
80
	case 16:
81 82 83 84 85 86 87 88
		__get_user_asm(*(u64 *)dst, (u64 __user *)src,
			       ret, "q", "", "=r", 16);
		if (unlikely(ret))
			return ret;
		__get_user_asm(*(u64 *)(8 + (char *)dst),
			       (u64 __user *)(8 + (char __user *)src),
			       ret, "q", "", "=r", 8);
		return ret;
L
Linus Torvalds 已提交
89
	default:
90
		return copy_user_generic(dst, (__force void *)src, size);
L
Linus Torvalds 已提交
91
	}
92
}
L
Linus Torvalds 已提交
93

94
static __always_inline __must_check
95 96 97 98 99 100 101 102
int __copy_from_user(void *dst, const void __user *src, unsigned size)
{
	might_fault();
	return __copy_from_user_nocheck(dst, src, size);
}

static __always_inline __must_check
int __copy_to_user_nocheck(void __user *dst, const void *src, unsigned size)
103
{
104
	int ret = 0;
105

L
Linus Torvalds 已提交
106
	if (!__builtin_constant_p(size))
107 108 109 110
		return copy_user_generic((__force void *)dst, src, size);
	switch (size) {
	case 1:__put_user_asm(*(u8 *)src, (u8 __user *)dst,
			      ret, "b", "b", "iq", 1);
L
Linus Torvalds 已提交
111
		return ret;
112 113
	case 2:__put_user_asm(*(u16 *)src, (u16 __user *)dst,
			      ret, "w", "w", "ir", 2);
L
Linus Torvalds 已提交
114
		return ret;
115 116 117 118
	case 4:__put_user_asm(*(u32 *)src, (u32 __user *)dst,
			      ret, "l", "k", "ir", 4);
		return ret;
	case 8:__put_user_asm(*(u64 *)src, (u64 __user *)dst,
119
			      ret, "q", "", "er", 8);
L
Linus Torvalds 已提交
120 121
		return ret;
	case 10:
122
		__put_user_asm(*(u64 *)src, (u64 __user *)dst,
123
			       ret, "q", "", "er", 10);
124 125
		if (unlikely(ret))
			return ret;
L
Linus Torvalds 已提交
126
		asm("":::"memory");
127 128 129
		__put_user_asm(4[(u16 *)src], 4 + (u16 __user *)dst,
			       ret, "w", "w", "ir", 2);
		return ret;
L
Linus Torvalds 已提交
130
	case 16:
131
		__put_user_asm(*(u64 *)src, (u64 __user *)dst,
132
			       ret, "q", "", "er", 16);
133 134
		if (unlikely(ret))
			return ret;
L
Linus Torvalds 已提交
135
		asm("":::"memory");
136
		__put_user_asm(1[(u64 *)src], 1 + (u64 __user *)dst,
137
			       ret, "q", "", "er", 8);
138
		return ret;
L
Linus Torvalds 已提交
139
	default:
140
		return copy_user_generic((__force void *)dst, src, size);
L
Linus Torvalds 已提交
141
	}
142
}
L
Linus Torvalds 已提交
143

144 145 146 147 148 149 150
static __always_inline __must_check
int __copy_to_user(void __user *dst, const void *src, unsigned size)
{
	might_fault();
	return __copy_to_user_nocheck(dst, src, size);
}

151 152
static __always_inline __must_check
int __copy_in_user(void __user *dst, const void __user *src, unsigned size)
153
{
154
	int ret = 0;
155

156
	might_fault();
L
Linus Torvalds 已提交
157
	if (!__builtin_constant_p(size))
158 159 160 161
		return copy_user_generic((__force void *)dst,
					 (__force void *)src, size);
	switch (size) {
	case 1: {
L
Linus Torvalds 已提交
162
		u8 tmp;
163 164
		__get_user_asm(tmp, (u8 __user *)src,
			       ret, "b", "b", "=q", 1);
L
Linus Torvalds 已提交
165
		if (likely(!ret))
166 167
			__put_user_asm(tmp, (u8 __user *)dst,
				       ret, "b", "b", "iq", 1);
L
Linus Torvalds 已提交
168 169
		return ret;
	}
170
	case 2: {
L
Linus Torvalds 已提交
171
		u16 tmp;
172 173
		__get_user_asm(tmp, (u16 __user *)src,
			       ret, "w", "w", "=r", 2);
L
Linus Torvalds 已提交
174
		if (likely(!ret))
175 176
			__put_user_asm(tmp, (u16 __user *)dst,
				       ret, "w", "w", "ir", 2);
L
Linus Torvalds 已提交
177 178 179
		return ret;
	}

180
	case 4: {
L
Linus Torvalds 已提交
181
		u32 tmp;
182 183
		__get_user_asm(tmp, (u32 __user *)src,
			       ret, "l", "k", "=r", 4);
L
Linus Torvalds 已提交
184
		if (likely(!ret))
185 186
			__put_user_asm(tmp, (u32 __user *)dst,
				       ret, "l", "k", "ir", 4);
L
Linus Torvalds 已提交
187 188
		return ret;
	}
189
	case 8: {
L
Linus Torvalds 已提交
190
		u64 tmp;
191 192
		__get_user_asm(tmp, (u64 __user *)src,
			       ret, "q", "", "=r", 8);
L
Linus Torvalds 已提交
193
		if (likely(!ret))
194
			__put_user_asm(tmp, (u64 __user *)dst,
195
				       ret, "q", "", "er", 8);
L
Linus Torvalds 已提交
196 197 198
		return ret;
	}
	default:
199 200
		return copy_user_generic((__force void *)dst,
					 (__force void *)src, size);
L
Linus Torvalds 已提交
201
	}
202
}
L
Linus Torvalds 已提交
203

204 205 206
static __must_check __always_inline int
__copy_from_user_inatomic(void *dst, const void __user *src, unsigned size)
{
207
	return __copy_from_user_nocheck(dst, src, size);
208
}
209 210 211 212

static __must_check __always_inline int
__copy_to_user_inatomic(void __user *dst, const void *src, unsigned size)
{
213
	return __copy_to_user_nocheck(dst, src, size);
214
}
L
Linus Torvalds 已提交
215

216 217
extern long __copy_user_nocache(void *dst, const void __user *src,
				unsigned size, int zerorest);
218

219 220
static inline int
__copy_from_user_nocache(void *dst, const void __user *src, unsigned size)
221
{
222
	might_fault();
223
	return __copy_user_nocache(dst, src, size, 1);
224 225
}

226 227 228
static inline int
__copy_from_user_inatomic_nocache(void *dst, const void __user *src,
				  unsigned size)
229
{
230
	return __copy_user_nocache(dst, src, size, 0);
231 232
}

233 234 235
unsigned long
copy_user_handle_tail(char *to, char *from, unsigned len, unsigned zerorest);

H
H. Peter Anvin 已提交
236
#endif /* _ASM_X86_UACCESS_64_H */