aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/x86/Kconfig1
-rw-r--r--arch/x86/include/asm/string_64.h10
-rw-r--r--arch/x86/include/asm/uaccess_64.h14
-rw-r--r--arch/x86/lib/memcpy_64.S102
-rw-r--r--arch/x86/lib/usercopy_64.c21
-rw-r--r--drivers/nvdimm/claim.c3
-rw-r--r--drivers/nvdimm/pmem.c6
-rw-r--r--include/linux/string.h4
-rw-r--r--include/linux/uio.h15
-rw-r--r--lib/iov_iter.c61
10 files changed, 169 insertions, 68 deletions
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index f2ee6a8ffe65..1fe24b624d44 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -62,6 +62,7 @@ config X86
62 select ARCH_HAS_PMEM_API if X86_64 62 select ARCH_HAS_PMEM_API if X86_64
63 select ARCH_HAS_REFCOUNT 63 select ARCH_HAS_REFCOUNT
64 select ARCH_HAS_UACCESS_FLUSHCACHE if X86_64 64 select ARCH_HAS_UACCESS_FLUSHCACHE if X86_64
65 select ARCH_HAS_UACCESS_MCSAFE if X86_64
65 select ARCH_HAS_SET_MEMORY 66 select ARCH_HAS_SET_MEMORY
66 select ARCH_HAS_SG_CHAIN 67 select ARCH_HAS_SG_CHAIN
67 select ARCH_HAS_STRICT_KERNEL_RWX 68 select ARCH_HAS_STRICT_KERNEL_RWX
diff --git a/arch/x86/include/asm/string_64.h b/arch/x86/include/asm/string_64.h
index 533f74c300c2..d33f92b9fa22 100644
--- a/arch/x86/include/asm/string_64.h
+++ b/arch/x86/include/asm/string_64.h
@@ -116,7 +116,8 @@ int strcmp(const char *cs, const char *ct);
116#endif 116#endif
117 117
118#define __HAVE_ARCH_MEMCPY_MCSAFE 1 118#define __HAVE_ARCH_MEMCPY_MCSAFE 1
119__must_check int memcpy_mcsafe_unrolled(void *dst, const void *src, size_t cnt); 119__must_check unsigned long __memcpy_mcsafe(void *dst, const void *src,
120 size_t cnt);
120DECLARE_STATIC_KEY_FALSE(mcsafe_key); 121DECLARE_STATIC_KEY_FALSE(mcsafe_key);
121 122
122/** 123/**
@@ -131,14 +132,15 @@ DECLARE_STATIC_KEY_FALSE(mcsafe_key);
131 * actually do machine check recovery. Everyone else can just 132 * actually do machine check recovery. Everyone else can just
132 * use memcpy(). 133 * use memcpy().
133 * 134 *
134 * Return 0 for success, -EFAULT for fail 135 * Return 0 for success, or number of bytes not copied if there was an
136 * exception.
135 */ 137 */
136static __always_inline __must_check int 138static __always_inline __must_check unsigned long
137memcpy_mcsafe(void *dst, const void *src, size_t cnt) 139memcpy_mcsafe(void *dst, const void *src, size_t cnt)
138{ 140{
139#ifdef CONFIG_X86_MCE 141#ifdef CONFIG_X86_MCE
140 if (static_branch_unlikely(&mcsafe_key)) 142 if (static_branch_unlikely(&mcsafe_key))
141 return memcpy_mcsafe_unrolled(dst, src, cnt); 143 return __memcpy_mcsafe(dst, src, cnt);
142 else 144 else
143#endif 145#endif
144 memcpy(dst, src, cnt); 146 memcpy(dst, src, cnt);
diff --git a/arch/x86/include/asm/uaccess_64.h b/arch/x86/include/asm/uaccess_64.h
index 62546b3a398e..62acb613114b 100644
--- a/arch/x86/include/asm/uaccess_64.h
+++ b/arch/x86/include/asm/uaccess_64.h
@@ -47,6 +47,17 @@ copy_user_generic(void *to, const void *from, unsigned len)
47} 47}
48 48
49static __always_inline __must_check unsigned long 49static __always_inline __must_check unsigned long
50copy_to_user_mcsafe(void *to, const void *from, unsigned len)
51{
52 unsigned long ret;
53
54 __uaccess_begin();
55 ret = memcpy_mcsafe(to, from, len);
56 __uaccess_end();
57 return ret;
58}
59
60static __always_inline __must_check unsigned long
50raw_copy_from_user(void *dst, const void __user *src, unsigned long size) 61raw_copy_from_user(void *dst, const void __user *src, unsigned long size)
51{ 62{
52 int ret = 0; 63 int ret = 0;
@@ -194,4 +205,7 @@ __copy_from_user_flushcache(void *dst, const void __user *src, unsigned size)
194unsigned long 205unsigned long
195copy_user_handle_tail(char *to, char *from, unsigned len); 206copy_user_handle_tail(char *to, char *from, unsigned len);
196 207
208unsigned long
209mcsafe_handle_tail(char *to, char *from, unsigned len);
210
197#endif /* _ASM_X86_UACCESS_64_H */ 211#endif /* _ASM_X86_UACCESS_64_H */
diff --git a/arch/x86/lib/memcpy_64.S b/arch/x86/lib/memcpy_64.S
index 9a53a06e5a3e..c3b527a9f95d 100644
--- a/arch/x86/lib/memcpy_64.S
+++ b/arch/x86/lib/memcpy_64.S
@@ -184,11 +184,11 @@ ENDPROC(memcpy_orig)
184 184
185#ifndef CONFIG_UML 185#ifndef CONFIG_UML
186/* 186/*
187 * memcpy_mcsafe_unrolled - memory copy with machine check exception handling 187 * __memcpy_mcsafe - memory copy with machine check exception handling
188 * Note that we only catch machine checks when reading the source addresses. 188 * Note that we only catch machine checks when reading the source addresses.
189 * Writes to target are posted and don't generate machine checks. 189 * Writes to target are posted and don't generate machine checks.
190 */ 190 */
191ENTRY(memcpy_mcsafe_unrolled) 191ENTRY(__memcpy_mcsafe)
192 cmpl $8, %edx 192 cmpl $8, %edx
193 /* Less than 8 bytes? Go to byte copy loop */ 193 /* Less than 8 bytes? Go to byte copy loop */
194 jb .L_no_whole_words 194 jb .L_no_whole_words
@@ -204,58 +204,29 @@ ENTRY(memcpy_mcsafe_unrolled)
204 subl $8, %ecx 204 subl $8, %ecx
205 negl %ecx 205 negl %ecx
206 subl %ecx, %edx 206 subl %ecx, %edx
207.L_copy_leading_bytes: 207.L_read_leading_bytes:
208 movb (%rsi), %al 208 movb (%rsi), %al
209.L_write_leading_bytes:
209 movb %al, (%rdi) 210 movb %al, (%rdi)
210 incq %rsi 211 incq %rsi
211 incq %rdi 212 incq %rdi
212 decl %ecx 213 decl %ecx
213 jnz .L_copy_leading_bytes 214 jnz .L_read_leading_bytes
214 215
215.L_8byte_aligned: 216.L_8byte_aligned:
216 /* Figure out how many whole cache lines (64-bytes) to copy */
217 movl %edx, %ecx
218 andl $63, %edx
219 shrl $6, %ecx
220 jz .L_no_whole_cache_lines
221
222 /* Loop copying whole cache lines */
223.L_cache_w0: movq (%rsi), %r8
224.L_cache_w1: movq 1*8(%rsi), %r9
225.L_cache_w2: movq 2*8(%rsi), %r10
226.L_cache_w3: movq 3*8(%rsi), %r11
227 movq %r8, (%rdi)
228 movq %r9, 1*8(%rdi)
229 movq %r10, 2*8(%rdi)
230 movq %r11, 3*8(%rdi)
231.L_cache_w4: movq 4*8(%rsi), %r8
232.L_cache_w5: movq 5*8(%rsi), %r9
233.L_cache_w6: movq 6*8(%rsi), %r10
234.L_cache_w7: movq 7*8(%rsi), %r11
235 movq %r8, 4*8(%rdi)
236 movq %r9, 5*8(%rdi)
237 movq %r10, 6*8(%rdi)
238 movq %r11, 7*8(%rdi)
239 leaq 64(%rsi), %rsi
240 leaq 64(%rdi), %rdi
241 decl %ecx
242 jnz .L_cache_w0
243
244 /* Are there any trailing 8-byte words? */
245.L_no_whole_cache_lines:
246 movl %edx, %ecx 217 movl %edx, %ecx
247 andl $7, %edx 218 andl $7, %edx
248 shrl $3, %ecx 219 shrl $3, %ecx
249 jz .L_no_whole_words 220 jz .L_no_whole_words
250 221
251 /* Copy trailing words */ 222.L_read_words:
252.L_copy_trailing_words:
253 movq (%rsi), %r8 223 movq (%rsi), %r8
254 mov %r8, (%rdi) 224.L_write_words:
255 leaq 8(%rsi), %rsi 225 movq %r8, (%rdi)
256 leaq 8(%rdi), %rdi 226 addq $8, %rsi
227 addq $8, %rdi
257 decl %ecx 228 decl %ecx
258 jnz .L_copy_trailing_words 229 jnz .L_read_words
259 230
260 /* Any trailing bytes? */ 231 /* Any trailing bytes? */
261.L_no_whole_words: 232.L_no_whole_words:
@@ -264,38 +235,53 @@ ENTRY(memcpy_mcsafe_unrolled)
264 235
265 /* Copy trailing bytes */ 236 /* Copy trailing bytes */
266 movl %edx, %ecx 237 movl %edx, %ecx
267.L_copy_trailing_bytes: 238.L_read_trailing_bytes:
268 movb (%rsi), %al 239 movb (%rsi), %al
240.L_write_trailing_bytes:
269 movb %al, (%rdi) 241 movb %al, (%rdi)
270 incq %rsi 242 incq %rsi
271 incq %rdi 243 incq %rdi
272 decl %ecx 244 decl %ecx
273 jnz .L_copy_trailing_bytes 245 jnz .L_read_trailing_bytes
274 246
275 /* Copy successful. Return zero */ 247 /* Copy successful. Return zero */
276.L_done_memcpy_trap: 248.L_done_memcpy_trap:
277 xorq %rax, %rax 249 xorq %rax, %rax
278 ret 250 ret
279ENDPROC(memcpy_mcsafe_unrolled) 251ENDPROC(__memcpy_mcsafe)
280EXPORT_SYMBOL_GPL(memcpy_mcsafe_unrolled) 252EXPORT_SYMBOL_GPL(__memcpy_mcsafe)
281 253
282 .section .fixup, "ax" 254 .section .fixup, "ax"
283 /* Return -EFAULT for any failure */ 255 /*
284.L_memcpy_mcsafe_fail: 256 * Return number of bytes not copied for any failure. Note that
285 mov $-EFAULT, %rax 257 * there is no "tail" handling since the source buffer is 8-byte
258 * aligned and poison is cacheline aligned.
259 */
260.E_read_words:
261 shll $3, %ecx
262.E_leading_bytes:
263 addl %edx, %ecx
264.E_trailing_bytes:
265 mov %ecx, %eax
286 ret 266 ret
287 267
268 /*
269 * For write fault handling, given the destination is unaligned,
270 * we handle faults on multi-byte writes with a byte-by-byte
271 * copy up to the write-protected page.
272 */
273.E_write_words:
274 shll $3, %ecx
275 addl %edx, %ecx
276 movl %ecx, %edx
277 jmp mcsafe_handle_tail
278
288 .previous 279 .previous
289 280
290 _ASM_EXTABLE_FAULT(.L_copy_leading_bytes, .L_memcpy_mcsafe_fail) 281 _ASM_EXTABLE_FAULT(.L_read_leading_bytes, .E_leading_bytes)
291 _ASM_EXTABLE_FAULT(.L_cache_w0, .L_memcpy_mcsafe_fail) 282 _ASM_EXTABLE_FAULT(.L_read_words, .E_read_words)
292 _ASM_EXTABLE_FAULT(.L_cache_w1, .L_memcpy_mcsafe_fail) 283 _ASM_EXTABLE_FAULT(.L_read_trailing_bytes, .E_trailing_bytes)
293 _ASM_EXTABLE_FAULT(.L_cache_w2, .L_memcpy_mcsafe_fail) 284 _ASM_EXTABLE(.L_write_leading_bytes, .E_leading_bytes)
294 _ASM_EXTABLE_FAULT(.L_cache_w3, .L_memcpy_mcsafe_fail) 285 _ASM_EXTABLE(.L_write_words, .E_write_words)
295 _ASM_EXTABLE_FAULT(.L_cache_w4, .L_memcpy_mcsafe_fail) 286 _ASM_EXTABLE(.L_write_trailing_bytes, .E_trailing_bytes)
296 _ASM_EXTABLE_FAULT(.L_cache_w5, .L_memcpy_mcsafe_fail)
297 _ASM_EXTABLE_FAULT(.L_cache_w6, .L_memcpy_mcsafe_fail)
298 _ASM_EXTABLE_FAULT(.L_cache_w7, .L_memcpy_mcsafe_fail)
299 _ASM_EXTABLE_FAULT(.L_copy_trailing_words, .L_memcpy_mcsafe_fail)
300 _ASM_EXTABLE_FAULT(.L_copy_trailing_bytes, .L_memcpy_mcsafe_fail)
301#endif 287#endif
diff --git a/arch/x86/lib/usercopy_64.c b/arch/x86/lib/usercopy_64.c
index a624dcc4de10..9c5606d88f61 100644
--- a/arch/x86/lib/usercopy_64.c
+++ b/arch/x86/lib/usercopy_64.c
@@ -74,6 +74,27 @@ copy_user_handle_tail(char *to, char *from, unsigned len)
74 return len; 74 return len;
75} 75}
76 76
77/*
78 * Similar to copy_user_handle_tail, probe for the write fault point,
79 * but reuse __memcpy_mcsafe in case a new read error is encountered.
80 * clac() is handled in _copy_to_iter_mcsafe().
81 */
82__visible unsigned long
83mcsafe_handle_tail(char *to, char *from, unsigned len)
84{
85 for (; len; --len, to++, from++) {
86 /*
87 * Call the assembly routine back directly since
88 * memcpy_mcsafe() may silently fallback to memcpy.
89 */
90 unsigned long rem = __memcpy_mcsafe(to, from, 1);
91
92 if (rem)
93 break;
94 }
95 return len;
96}
97
77#ifdef CONFIG_ARCH_HAS_UACCESS_FLUSHCACHE 98#ifdef CONFIG_ARCH_HAS_UACCESS_FLUSHCACHE
78/** 99/**
79 * clean_cache_range - write back a cache range with CLWB 100 * clean_cache_range - write back a cache range with CLWB
diff --git a/drivers/nvdimm/claim.c b/drivers/nvdimm/claim.c
index 30852270484f..2e96b34bc936 100644
--- a/drivers/nvdimm/claim.c
+++ b/drivers/nvdimm/claim.c
@@ -276,7 +276,8 @@ static int nsio_rw_bytes(struct nd_namespace_common *ndns,
276 if (rw == READ) { 276 if (rw == READ) {
277 if (unlikely(is_bad_pmem(&nsio->bb, sector, sz_align))) 277 if (unlikely(is_bad_pmem(&nsio->bb, sector, sz_align)))
278 return -EIO; 278 return -EIO;
279 return memcpy_mcsafe(buf, nsio->addr + offset, size); 279 if (memcpy_mcsafe(buf, nsio->addr + offset, size) != 0)
280 return -EIO;
280 } 281 }
281 282
282 if (unlikely(is_bad_pmem(&nsio->bb, sector, sz_align))) { 283 if (unlikely(is_bad_pmem(&nsio->bb, sector, sz_align))) {
diff --git a/drivers/nvdimm/pmem.c b/drivers/nvdimm/pmem.c
index 9d714926ecf5..e023d6aa22b5 100644
--- a/drivers/nvdimm/pmem.c
+++ b/drivers/nvdimm/pmem.c
@@ -101,15 +101,15 @@ static blk_status_t read_pmem(struct page *page, unsigned int off,
101 void *pmem_addr, unsigned int len) 101 void *pmem_addr, unsigned int len)
102{ 102{
103 unsigned int chunk; 103 unsigned int chunk;
104 int rc; 104 unsigned long rem;
105 void *mem; 105 void *mem;
106 106
107 while (len) { 107 while (len) {
108 mem = kmap_atomic(page); 108 mem = kmap_atomic(page);
109 chunk = min_t(unsigned int, len, PAGE_SIZE); 109 chunk = min_t(unsigned int, len, PAGE_SIZE);
110 rc = memcpy_mcsafe(mem + off, pmem_addr, chunk); 110 rem = memcpy_mcsafe(mem + off, pmem_addr, chunk);
111 kunmap_atomic(mem); 111 kunmap_atomic(mem);
112 if (rc) 112 if (rem)
113 return BLK_STS_IOERR; 113 return BLK_STS_IOERR;
114 len -= chunk; 114 len -= chunk;
115 off = 0; 115 off = 0;
diff --git a/include/linux/string.h b/include/linux/string.h
index dd39a690c841..4a5a0eb7df51 100644
--- a/include/linux/string.h
+++ b/include/linux/string.h
@@ -147,8 +147,8 @@ extern int memcmp(const void *,const void *,__kernel_size_t);
147extern void * memchr(const void *,int,__kernel_size_t); 147extern void * memchr(const void *,int,__kernel_size_t);
148#endif 148#endif
149#ifndef __HAVE_ARCH_MEMCPY_MCSAFE 149#ifndef __HAVE_ARCH_MEMCPY_MCSAFE
150static inline __must_check int memcpy_mcsafe(void *dst, const void *src, 150static inline __must_check unsigned long memcpy_mcsafe(void *dst,
151 size_t cnt) 151 const void *src, size_t cnt)
152{ 152{
153 memcpy(dst, src, cnt); 153 memcpy(dst, src, cnt);
154 return 0; 154 return 0;
diff --git a/include/linux/uio.h b/include/linux/uio.h
index e67e12adb136..f5766e853a77 100644
--- a/include/linux/uio.h
+++ b/include/linux/uio.h
@@ -154,6 +154,12 @@ size_t _copy_from_iter_flushcache(void *addr, size_t bytes, struct iov_iter *i);
154#define _copy_from_iter_flushcache _copy_from_iter_nocache 154#define _copy_from_iter_flushcache _copy_from_iter_nocache
155#endif 155#endif
156 156
157#ifdef CONFIG_ARCH_HAS_UACCESS_MCSAFE
158size_t _copy_to_iter_mcsafe(void *addr, size_t bytes, struct iov_iter *i);
159#else
160#define _copy_to_iter_mcsafe _copy_to_iter
161#endif
162
157static __always_inline __must_check 163static __always_inline __must_check
158size_t copy_from_iter_flushcache(void *addr, size_t bytes, struct iov_iter *i) 164size_t copy_from_iter_flushcache(void *addr, size_t bytes, struct iov_iter *i)
159{ 165{
@@ -163,6 +169,15 @@ size_t copy_from_iter_flushcache(void *addr, size_t bytes, struct iov_iter *i)
163 return _copy_from_iter_flushcache(addr, bytes, i); 169 return _copy_from_iter_flushcache(addr, bytes, i);
164} 170}
165 171
172static __always_inline __must_check
173size_t copy_to_iter_mcsafe(void *addr, size_t bytes, struct iov_iter *i)
174{
175 if (unlikely(!check_copy_size(addr, bytes, false)))
176 return 0;
177 else
178 return _copy_to_iter_mcsafe(addr, bytes, i);
179}
180
166size_t iov_iter_zero(size_t bytes, struct iov_iter *); 181size_t iov_iter_zero(size_t bytes, struct iov_iter *);
167unsigned long iov_iter_alignment(const struct iov_iter *i); 182unsigned long iov_iter_alignment(const struct iov_iter *i);
168unsigned long iov_iter_gap_alignment(const struct iov_iter *i); 183unsigned long iov_iter_gap_alignment(const struct iov_iter *i);
diff --git a/lib/iov_iter.c b/lib/iov_iter.c
index fdae394172fa..7e43cd54c84c 100644
--- a/lib/iov_iter.c
+++ b/lib/iov_iter.c
@@ -573,6 +573,67 @@ size_t _copy_to_iter(const void *addr, size_t bytes, struct iov_iter *i)
573} 573}
574EXPORT_SYMBOL(_copy_to_iter); 574EXPORT_SYMBOL(_copy_to_iter);
575 575
576#ifdef CONFIG_ARCH_HAS_UACCESS_MCSAFE
577static int copyout_mcsafe(void __user *to, const void *from, size_t n)
578{
579 if (access_ok(VERIFY_WRITE, to, n)) {
580 kasan_check_read(from, n);
581 n = copy_to_user_mcsafe((__force void *) to, from, n);
582 }
583 return n;
584}
585
586static unsigned long memcpy_mcsafe_to_page(struct page *page, size_t offset,
587 const char *from, size_t len)
588{
589 unsigned long ret;
590 char *to;
591
592 to = kmap_atomic(page);
593 ret = memcpy_mcsafe(to + offset, from, len);
594 kunmap_atomic(to);
595
596 return ret;
597}
598
599size_t _copy_to_iter_mcsafe(const void *addr, size_t bytes, struct iov_iter *i)
600{
601 const char *from = addr;
602 unsigned long rem, curr_addr, s_addr = (unsigned long) addr;
603
604 if (unlikely(i->type & ITER_PIPE)) {
605 WARN_ON(1);
606 return 0;
607 }
608 if (iter_is_iovec(i))
609 might_fault();
610 iterate_and_advance(i, bytes, v,
611 copyout_mcsafe(v.iov_base, (from += v.iov_len) - v.iov_len, v.iov_len),
612 ({
613 rem = memcpy_mcsafe_to_page(v.bv_page, v.bv_offset,
614 (from += v.bv_len) - v.bv_len, v.bv_len);
615 if (rem) {
616 curr_addr = (unsigned long) from;
617 bytes = curr_addr - s_addr - rem;
618 return bytes;
619 }
620 }),
621 ({
622 rem = memcpy_mcsafe(v.iov_base, (from += v.iov_len) - v.iov_len,
623 v.iov_len);
624 if (rem) {
625 curr_addr = (unsigned long) from;
626 bytes = curr_addr - s_addr - rem;
627 return bytes;
628 }
629 })
630 )
631
632 return bytes;
633}
634EXPORT_SYMBOL_GPL(_copy_to_iter_mcsafe);
635#endif /* CONFIG_ARCH_HAS_UACCESS_MCSAFE */
636
576size_t _copy_from_iter(void *addr, size_t bytes, struct iov_iter *i) 637size_t _copy_from_iter(void *addr, size_t bytes, struct iov_iter *i)
577{ 638{
578 char *to = addr; 639 char *to = addr;