18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* Copyright(c) 2016-2020 Intel Corporation. All rights reserved. */ 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#include <linux/jump_label.h> 58c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 68c2ecf20Sopenharmony_ci#include <linux/export.h> 78c2ecf20Sopenharmony_ci#include <linux/string.h> 88c2ecf20Sopenharmony_ci#include <linux/types.h> 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <asm/mce.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#ifdef CONFIG_X86_MCE 138c2ecf20Sopenharmony_ci/* 148c2ecf20Sopenharmony_ci * See COPY_MC_TEST for self-test of the copy_mc_fragile() 158c2ecf20Sopenharmony_ci * implementation. 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_cistatic DEFINE_STATIC_KEY_FALSE(copy_mc_fragile_key); 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_civoid enable_copy_mc_fragile(void) 208c2ecf20Sopenharmony_ci{ 218c2ecf20Sopenharmony_ci static_branch_inc(©_mc_fragile_key); 228c2ecf20Sopenharmony_ci} 238c2ecf20Sopenharmony_ci#define copy_mc_fragile_enabled (static_branch_unlikely(©_mc_fragile_key)) 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* 268c2ecf20Sopenharmony_ci * Similar to copy_user_handle_tail, probe for the write fault point, or 278c2ecf20Sopenharmony_ci * source exception point. 288c2ecf20Sopenharmony_ci */ 298c2ecf20Sopenharmony_ci__visible notrace unsigned long 308c2ecf20Sopenharmony_cicopy_mc_fragile_handle_tail(char *to, char *from, unsigned len) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci for (; len; --len, to++, from++) 338c2ecf20Sopenharmony_ci if (copy_mc_fragile(to, from, 1)) 348c2ecf20Sopenharmony_ci break; 358c2ecf20Sopenharmony_ci return len; 368c2ecf20Sopenharmony_ci} 378c2ecf20Sopenharmony_ci#else 388c2ecf20Sopenharmony_ci/* 398c2ecf20Sopenharmony_ci * No point in doing careful copying, or consulting a static key when 408c2ecf20Sopenharmony_ci * there is no #MC handler in the CONFIG_X86_MCE=n case. 418c2ecf20Sopenharmony_ci */ 428c2ecf20Sopenharmony_civoid enable_copy_mc_fragile(void) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci} 458c2ecf20Sopenharmony_ci#define copy_mc_fragile_enabled (0) 468c2ecf20Sopenharmony_ci#endif 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ciunsigned long copy_mc_enhanced_fast_string(void *dst, const void *src, unsigned len); 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci/** 518c2ecf20Sopenharmony_ci * copy_mc_to_kernel - memory copy that handles source exceptions 528c2ecf20Sopenharmony_ci * 538c2ecf20Sopenharmony_ci * @dst: destination address 548c2ecf20Sopenharmony_ci * @src: source address 558c2ecf20Sopenharmony_ci * @len: number of bytes to copy 568c2ecf20Sopenharmony_ci * 578c2ecf20Sopenharmony_ci * Call into the 'fragile' version on systems that benefit from avoiding 588c2ecf20Sopenharmony_ci * corner case poison consumption scenarios, For example, accessing 598c2ecf20Sopenharmony_ci * poison across 2 cachelines with a single instruction. Almost all 608c2ecf20Sopenharmony_ci * other uses case can use copy_mc_enhanced_fast_string() for a fast 618c2ecf20Sopenharmony_ci * recoverable copy, or fallback to plain memcpy. 628c2ecf20Sopenharmony_ci * 638c2ecf20Sopenharmony_ci * Return 0 for success, or number of bytes not copied if there was an 648c2ecf20Sopenharmony_ci * exception. 658c2ecf20Sopenharmony_ci */ 668c2ecf20Sopenharmony_ciunsigned long __must_check copy_mc_to_kernel(void *dst, const void *src, unsigned len) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci if (copy_mc_fragile_enabled) 698c2ecf20Sopenharmony_ci return copy_mc_fragile(dst, src, len); 708c2ecf20Sopenharmony_ci if (static_cpu_has(X86_FEATURE_ERMS)) 718c2ecf20Sopenharmony_ci return copy_mc_enhanced_fast_string(dst, src, len); 728c2ecf20Sopenharmony_ci memcpy(dst, src, len); 738c2ecf20Sopenharmony_ci return 0; 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(copy_mc_to_kernel); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ciunsigned long __must_check copy_mc_to_user(void __user *dst, const void *src, unsigned len) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci unsigned long ret; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci if (copy_mc_fragile_enabled) { 828c2ecf20Sopenharmony_ci __uaccess_begin(); 838c2ecf20Sopenharmony_ci ret = copy_mc_fragile((__force void *)dst, src, len); 848c2ecf20Sopenharmony_ci __uaccess_end(); 858c2ecf20Sopenharmony_ci return ret; 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci if (static_cpu_has(X86_FEATURE_ERMS)) { 898c2ecf20Sopenharmony_ci __uaccess_begin(); 908c2ecf20Sopenharmony_ci ret = copy_mc_enhanced_fast_string((__force void *)dst, src, len); 918c2ecf20Sopenharmony_ci __uaccess_end(); 928c2ecf20Sopenharmony_ci return ret; 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci return copy_user_generic((__force void *)dst, src, len); 968c2ecf20Sopenharmony_ci} 97