162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright 2002, 2003 Andi Kleen, SuSE Labs. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Wrappers of assembly checksum functions for x86-64. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci#include <asm/checksum.h> 862306a36Sopenharmony_ci#include <linux/export.h> 962306a36Sopenharmony_ci#include <linux/uaccess.h> 1062306a36Sopenharmony_ci#include <asm/smap.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci/** 1362306a36Sopenharmony_ci * csum_and_copy_from_user - Copy and checksum from user space. 1462306a36Sopenharmony_ci * @src: source address (user space) 1562306a36Sopenharmony_ci * @dst: destination address 1662306a36Sopenharmony_ci * @len: number of bytes to be copied. 1762306a36Sopenharmony_ci * @isum: initial sum that is added into the result (32bit unfolded) 1862306a36Sopenharmony_ci * @errp: set to -EFAULT for an bad source address. 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * Returns an 32bit unfolded checksum of the buffer. 2162306a36Sopenharmony_ci * src and dst are best aligned to 64bits. 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_ci__wsum 2462306a36Sopenharmony_cicsum_and_copy_from_user(const void __user *src, void *dst, int len) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci __wsum sum; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci might_sleep(); 2962306a36Sopenharmony_ci if (!user_access_begin(src, len)) 3062306a36Sopenharmony_ci return 0; 3162306a36Sopenharmony_ci sum = csum_partial_copy_generic((__force const void *)src, dst, len); 3262306a36Sopenharmony_ci user_access_end(); 3362306a36Sopenharmony_ci return sum; 3462306a36Sopenharmony_ci} 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci/** 3762306a36Sopenharmony_ci * csum_and_copy_to_user - Copy and checksum to user space. 3862306a36Sopenharmony_ci * @src: source address 3962306a36Sopenharmony_ci * @dst: destination address (user space) 4062306a36Sopenharmony_ci * @len: number of bytes to be copied. 4162306a36Sopenharmony_ci * @isum: initial sum that is added into the result (32bit unfolded) 4262306a36Sopenharmony_ci * @errp: set to -EFAULT for an bad destination address. 4362306a36Sopenharmony_ci * 4462306a36Sopenharmony_ci * Returns an 32bit unfolded checksum of the buffer. 4562306a36Sopenharmony_ci * src and dst are best aligned to 64bits. 4662306a36Sopenharmony_ci */ 4762306a36Sopenharmony_ci__wsum 4862306a36Sopenharmony_cicsum_and_copy_to_user(const void *src, void __user *dst, int len) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci __wsum sum; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci might_sleep(); 5362306a36Sopenharmony_ci if (!user_access_begin(dst, len)) 5462306a36Sopenharmony_ci return 0; 5562306a36Sopenharmony_ci sum = csum_partial_copy_generic(src, (void __force *)dst, len); 5662306a36Sopenharmony_ci user_access_end(); 5762306a36Sopenharmony_ci return sum; 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci/** 6162306a36Sopenharmony_ci * csum_partial_copy_nocheck - Copy and checksum. 6262306a36Sopenharmony_ci * @src: source address 6362306a36Sopenharmony_ci * @dst: destination address 6462306a36Sopenharmony_ci * @len: number of bytes to be copied. 6562306a36Sopenharmony_ci * @sum: initial sum that is added into the result (32bit unfolded) 6662306a36Sopenharmony_ci * 6762306a36Sopenharmony_ci * Returns an 32bit unfolded checksum of the buffer. 6862306a36Sopenharmony_ci */ 6962306a36Sopenharmony_ci__wsum 7062306a36Sopenharmony_cicsum_partial_copy_nocheck(const void *src, void *dst, int len) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci return csum_partial_copy_generic(src, dst, len); 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ciEXPORT_SYMBOL(csum_partial_copy_nocheck); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci__sum16 csum_ipv6_magic(const struct in6_addr *saddr, 7762306a36Sopenharmony_ci const struct in6_addr *daddr, 7862306a36Sopenharmony_ci __u32 len, __u8 proto, __wsum sum) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci __u64 rest, sum64; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci rest = (__force __u64)htonl(len) + (__force __u64)htons(proto) + 8362306a36Sopenharmony_ci (__force __u64)sum; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci asm(" addq (%[saddr]),%[sum]\n" 8662306a36Sopenharmony_ci " adcq 8(%[saddr]),%[sum]\n" 8762306a36Sopenharmony_ci " adcq (%[daddr]),%[sum]\n" 8862306a36Sopenharmony_ci " adcq 8(%[daddr]),%[sum]\n" 8962306a36Sopenharmony_ci " adcq $0,%[sum]\n" 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci : [sum] "=r" (sum64) 9262306a36Sopenharmony_ci : "[sum]" (rest), [saddr] "r" (saddr), [daddr] "r" (daddr)); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci return csum_fold( 9562306a36Sopenharmony_ci (__force __wsum)add32_with_carry(sum64 & 0xffffffff, sum64>>32)); 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ciEXPORT_SYMBOL(csum_ipv6_magic); 98