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