Branch data Line data Source code
1 : : /* SPDX-License-Identifier: GPL-2.0 */
2 : : #ifndef _ASM_X86_UACCESS_64_H
3 : : #define _ASM_X86_UACCESS_64_H
4 : :
5 : : /*
6 : : * User space memory access functions
7 : : */
8 : : #include <linux/compiler.h>
9 : : #include <linux/lockdep.h>
10 : : #include <linux/kasan-checks.h>
11 : : #include <asm/alternative.h>
12 : : #include <asm/cpufeatures.h>
13 : : #include <asm/page.h>
14 : :
15 : : /*
16 : : * Copy To/From Userspace
17 : : */
18 : :
19 : : /* Handles exceptions in both to and from, but doesn't do access_ok */
20 : : __must_check unsigned long
21 : : copy_user_enhanced_fast_string(void *to, const void *from, unsigned len);
22 : : __must_check unsigned long
23 : : copy_user_generic_string(void *to, const void *from, unsigned len);
24 : : __must_check unsigned long
25 : : copy_user_generic_unrolled(void *to, const void *from, unsigned len);
26 : :
27 : : static __always_inline __must_check unsigned long
28 : 8345445 : copy_user_generic(void *to, const void *from, unsigned len)
29 : : {
30 : 8345445 : unsigned ret;
31 : :
32 : : /*
33 : : * If CPU has ERMS feature, use copy_user_enhanced_fast_string.
34 : : * Otherwise, if CPU has rep_good feature, use copy_user_generic_string.
35 : : * Otherwise, use copy_user_generic_unrolled.
36 : : */
37 : 8345445 : alternative_call_2(copy_user_generic_unrolled,
38 : : copy_user_generic_string,
39 : : X86_FEATURE_REP_GOOD,
40 : : copy_user_enhanced_fast_string,
41 : : X86_FEATURE_ERMS,
42 : : ASM_OUTPUT2("=a" (ret), "=D" (to), "=S" (from),
43 : : "=d" (len)),
44 : : "1" (to), "2" (from), "3" (len)
45 : : : "memory", "rcx", "r8", "r9", "r10", "r11");
46 [ - - - - : 8345445 : return ret;
+ - - - -
# # # ]
47 : : }
48 : :
49 : : static __always_inline __must_check unsigned long
50 : 0 : copy_to_user_mcsafe(void *to, const void *from, unsigned len)
51 : : {
52 : 0 : unsigned long ret;
53 : :
54 : 0 : __uaccess_begin();
55 : : /*
56 : : * Note, __memcpy_mcsafe() is explicitly used since it can
57 : : * handle exceptions / faults. memcpy_mcsafe() may fall back to
58 : : * memcpy() which lacks this handling.
59 : : */
60 : 0 : ret = __memcpy_mcsafe(to, from, len);
61 : 0 : __uaccess_end();
62 : 0 : return ret;
63 : : }
64 : :
65 : : static __always_inline __must_check unsigned long
66 : 4686687 : raw_copy_from_user(void *dst, const void __user *src, unsigned long size)
67 : : {
68 : 4686687 : int ret = 0;
69 : :
70 [ + - + - ]: 4686687 : if (!__builtin_constant_p(size))
71 : 4615089 : return copy_user_generic(dst, (__force void *)src, size);
72 [ # # # # : 71598 : switch (size) {
# # # # #
# # # #
# ]
73 : : case 1:
74 : 0 : __uaccess_begin_nospec();
75 : 0 : __get_user_asm_nozero(*(u8 *)dst, (u8 __user *)src,
76 : : ret, "b", "b", "=q", 1);
77 : 0 : __uaccess_end();
78 : 0 : return ret;
79 : : case 2:
80 : 0 : __uaccess_begin_nospec();
81 : 0 : __get_user_asm_nozero(*(u16 *)dst, (u16 __user *)src,
82 : : ret, "w", "w", "=r", 2);
83 : 0 : __uaccess_end();
84 : 0 : return ret;
85 : : case 4:
86 : 0 : __uaccess_begin_nospec();
87 : 0 : __get_user_asm_nozero(*(u32 *)dst, (u32 __user *)src,
88 : : ret, "l", "k", "=r", 4);
89 : 0 : __uaccess_end();
90 [ # # # # ]: 0 : return ret;
91 : : case 8:
92 : 71598 : __uaccess_begin_nospec();
93 : 71598 : __get_user_asm_nozero(*(u64 *)dst, (u64 __user *)src,
94 : : ret, "q", "", "=r", 8);
95 : 71598 : __uaccess_end();
96 [ - + # # ]: 71598 : return ret;
97 : : case 10:
98 : 0 : __uaccess_begin_nospec();
99 : 0 : __get_user_asm_nozero(*(u64 *)dst, (u64 __user *)src,
100 : : ret, "q", "", "=r", 10);
101 [ # # # # ]: 0 : if (likely(!ret))
102 : 0 : __get_user_asm_nozero(*(u16 *)(8 + (char *)dst),
103 : : (u16 __user *)(8 + (char __user *)src),
104 : : ret, "w", "w", "=r", 2);
105 : 0 : __uaccess_end();
106 : 0 : return ret;
107 : : case 16:
108 : 0 : __uaccess_begin_nospec();
109 : 0 : __get_user_asm_nozero(*(u64 *)dst, (u64 __user *)src,
110 : : ret, "q", "", "=r", 16);
111 [ # # # # ]: 0 : if (likely(!ret))
112 : 0 : __get_user_asm_nozero(*(u64 *)(8 + (char *)dst),
113 : : (u64 __user *)(8 + (char __user *)src),
114 : : ret, "q", "", "=r", 8);
115 : 0 : __uaccess_end();
116 [ # # ]: 0 : return ret;
117 : 0 : default:
118 : 0 : return copy_user_generic(dst, (__force void *)src, size);
119 : : }
120 : : }
121 : :
122 : : static __always_inline __must_check unsigned long
123 : 3801954 : raw_copy_to_user(void __user *dst, const void *src, unsigned long size)
124 : : {
125 : 3801954 : int ret = 0;
126 : :
127 [ + - # # ]: 3801954 : if (!__builtin_constant_p(size))
128 : 3657498 : return copy_user_generic((__force void *)dst, src, size);
129 [ # # # # : 144456 : switch (size) {
# # # # #
# # # #
# ]
130 : : case 1:
131 : 0 : __uaccess_begin();
132 [ # # # # : 0 : __put_user_asm(*(u8 *)src, (u8 __user *)dst,
# # ]
133 : : ret, "b", "b", "iq", 1);
134 : 0 : __uaccess_end();
135 [ # # ]: 0 : return ret;
136 : : case 2:
137 : 0 : __uaccess_begin();
138 [ # # # # ]: 0 : __put_user_asm(*(u16 *)src, (u16 __user *)dst,
139 : : ret, "w", "w", "ir", 2);
140 : 0 : __uaccess_end();
141 : 0 : return ret;
142 : : case 4:
143 : 0 : __uaccess_begin();
144 [ # # # # ]: 0 : __put_user_asm(*(u32 *)src, (u32 __user *)dst,
145 : : ret, "l", "k", "ir", 4);
146 : 0 : __uaccess_end();
147 [ # # ]: 0 : return ret;
148 : : case 8:
149 : 71598 : __uaccess_begin();
150 [ + - # # ]: 71598 : __put_user_asm(*(u64 *)src, (u64 __user *)dst,
151 : : ret, "q", "", "er", 8);
152 : 71598 : __uaccess_end();
153 [ + - ]: 71598 : return ret;
154 : : case 10:
155 : 0 : __uaccess_begin();
156 [ # # # # ]: 0 : __put_user_asm(*(u64 *)src, (u64 __user *)dst,
157 : : ret, "q", "", "er", 10);
158 [ # # # # ]: 0 : if (likely(!ret)) {
159 : 0 : asm("":::"memory");
160 [ # # # # ]: 0 : __put_user_asm(4[(u16 *)src], 4 + (u16 __user *)dst,
161 : : ret, "w", "w", "ir", 2);
162 : : }
163 : 0 : __uaccess_end();
164 : 0 : return ret;
165 : : case 16:
166 : 0 : __uaccess_begin();
167 [ # # # # : 0 : __put_user_asm(*(u64 *)src, (u64 __user *)dst,
# # # # ]
168 : : ret, "q", "", "er", 16);
169 [ # # # # : 0 : if (likely(!ret)) {
# # # # ]
170 : 0 : asm("":::"memory");
171 [ # # # # : 0 : __put_user_asm(1[(u64 *)src], 1 + (u64 __user *)dst,
# # # # ]
172 : : ret, "q", "", "er", 8);
173 : : }
174 : 0 : __uaccess_end();
175 [ # # # # ]: 0 : return ret;
176 : 0 : default:
177 : 72858 : return copy_user_generic((__force void *)dst, src, size);
178 : : }
179 : : }
180 : :
181 : : static __always_inline __must_check
182 : 0 : unsigned long raw_copy_in_user(void __user *dst, const void __user *src, unsigned long size)
183 : : {
184 : 0 : return copy_user_generic((__force void *)dst,
185 : : (__force void *)src, size);
186 : : }
187 : :
188 : : extern long __copy_user_nocache(void *dst, const void __user *src,
189 : : unsigned size, int zerorest);
190 : :
191 : : extern long __copy_user_flushcache(void *dst, const void __user *src, unsigned size);
192 : : extern void memcpy_page_flushcache(char *to, struct page *page, size_t offset,
193 : : size_t len);
194 : :
195 : : static inline int
196 : 0 : __copy_from_user_inatomic_nocache(void *dst, const void __user *src,
197 : : unsigned size)
198 : : {
199 : 0 : kasan_check_write(dst, size);
200 [ # # # # ]: 0 : return __copy_user_nocache(dst, src, size, 0);
201 : : }
202 : :
203 : : static inline int
204 : 0 : __copy_from_user_flushcache(void *dst, const void __user *src, unsigned size)
205 : : {
206 : 0 : kasan_check_write(dst, size);
207 : 0 : return __copy_user_flushcache(dst, src, size);
208 : : }
209 : :
210 : : unsigned long
211 : : mcsafe_handle_tail(char *to, char *from, unsigned len);
212 : :
213 : : #endif /* _ASM_X86_UACCESS_64_H */
|