18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright 2002, 2003 Andi Kleen, SuSE Labs. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Wrappers of assembly checksum functions for x86-64. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci#include <asm/checksum.h> 88c2ecf20Sopenharmony_ci#include <linux/export.h> 98c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 108c2ecf20Sopenharmony_ci#include <asm/smap.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci/** 138c2ecf20Sopenharmony_ci * csum_and_copy_from_user - Copy and checksum from user space. 148c2ecf20Sopenharmony_ci * @src: source address (user space) 158c2ecf20Sopenharmony_ci * @dst: destination address 168c2ecf20Sopenharmony_ci * @len: number of bytes to be copied. 178c2ecf20Sopenharmony_ci * @isum: initial sum that is added into the result (32bit unfolded) 188c2ecf20Sopenharmony_ci * @errp: set to -EFAULT for an bad source address. 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci * Returns an 32bit unfolded checksum of the buffer. 218c2ecf20Sopenharmony_ci * src and dst are best aligned to 64bits. 228c2ecf20Sopenharmony_ci */ 238c2ecf20Sopenharmony_ci__wsum 248c2ecf20Sopenharmony_cicsum_and_copy_from_user(const void __user *src, void *dst, int len) 258c2ecf20Sopenharmony_ci{ 268c2ecf20Sopenharmony_ci __wsum sum; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci might_sleep(); 298c2ecf20Sopenharmony_ci if (!user_access_begin(src, len)) 308c2ecf20Sopenharmony_ci return 0; 318c2ecf20Sopenharmony_ci sum = csum_partial_copy_generic((__force const void *)src, dst, len); 328c2ecf20Sopenharmony_ci user_access_end(); 338c2ecf20Sopenharmony_ci return sum; 348c2ecf20Sopenharmony_ci} 358c2ecf20Sopenharmony_ciEXPORT_SYMBOL(csum_and_copy_from_user); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/** 388c2ecf20Sopenharmony_ci * csum_and_copy_to_user - Copy and checksum to user space. 398c2ecf20Sopenharmony_ci * @src: source address 408c2ecf20Sopenharmony_ci * @dst: destination address (user space) 418c2ecf20Sopenharmony_ci * @len: number of bytes to be copied. 428c2ecf20Sopenharmony_ci * @isum: initial sum that is added into the result (32bit unfolded) 438c2ecf20Sopenharmony_ci * @errp: set to -EFAULT for an bad destination address. 448c2ecf20Sopenharmony_ci * 458c2ecf20Sopenharmony_ci * Returns an 32bit unfolded checksum of the buffer. 468c2ecf20Sopenharmony_ci * src and dst are best aligned to 64bits. 478c2ecf20Sopenharmony_ci */ 488c2ecf20Sopenharmony_ci__wsum 498c2ecf20Sopenharmony_cicsum_and_copy_to_user(const void *src, void __user *dst, int len) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci __wsum sum; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci might_sleep(); 548c2ecf20Sopenharmony_ci if (!user_access_begin(dst, len)) 558c2ecf20Sopenharmony_ci return 0; 568c2ecf20Sopenharmony_ci sum = csum_partial_copy_generic(src, (void __force *)dst, len); 578c2ecf20Sopenharmony_ci user_access_end(); 588c2ecf20Sopenharmony_ci return sum; 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ciEXPORT_SYMBOL(csum_and_copy_to_user); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci/** 638c2ecf20Sopenharmony_ci * csum_partial_copy_nocheck - Copy and checksum. 648c2ecf20Sopenharmony_ci * @src: source address 658c2ecf20Sopenharmony_ci * @dst: destination address 668c2ecf20Sopenharmony_ci * @len: number of bytes to be copied. 678c2ecf20Sopenharmony_ci * @sum: initial sum that is added into the result (32bit unfolded) 688c2ecf20Sopenharmony_ci * 698c2ecf20Sopenharmony_ci * Returns an 32bit unfolded checksum of the buffer. 708c2ecf20Sopenharmony_ci */ 718c2ecf20Sopenharmony_ci__wsum 728c2ecf20Sopenharmony_cicsum_partial_copy_nocheck(const void *src, void *dst, int len) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci return csum_partial_copy_generic(src, dst, len); 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ciEXPORT_SYMBOL(csum_partial_copy_nocheck); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci__sum16 csum_ipv6_magic(const struct in6_addr *saddr, 798c2ecf20Sopenharmony_ci const struct in6_addr *daddr, 808c2ecf20Sopenharmony_ci __u32 len, __u8 proto, __wsum sum) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci __u64 rest, sum64; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci rest = (__force __u64)htonl(len) + (__force __u64)htons(proto) + 858c2ecf20Sopenharmony_ci (__force __u64)sum; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci asm(" addq (%[saddr]),%[sum]\n" 888c2ecf20Sopenharmony_ci " adcq 8(%[saddr]),%[sum]\n" 898c2ecf20Sopenharmony_ci " adcq (%[daddr]),%[sum]\n" 908c2ecf20Sopenharmony_ci " adcq 8(%[daddr]),%[sum]\n" 918c2ecf20Sopenharmony_ci " adcq $0,%[sum]\n" 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci : [sum] "=r" (sum64) 948c2ecf20Sopenharmony_ci : "[sum]" (rest), [saddr] "r" (saddr), [daddr] "r" (daddr)); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci return csum_fold( 978c2ecf20Sopenharmony_ci (__force __wsum)add32_with_carry(sum64 & 0xffffffff, sum64>>32)); 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ciEXPORT_SYMBOL(csum_ipv6_magic); 100