162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * arch/alpha/lib/checksum.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * This file contains network checksum routines that are better done
662306a36Sopenharmony_ci * in an architecture-specific manner due to speed..
762306a36Sopenharmony_ci * Comments in other versions indicate that the algorithms are from RFC1071
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * accelerated versions (and 21264 assembly versions ) contributed by
1062306a36Sopenharmony_ci *	Rick Gorton	<rick.gorton@alpha-processor.com>
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/string.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <asm/byteorder.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistatic inline unsigned short from64to16(unsigned long x)
1962306a36Sopenharmony_ci{
2062306a36Sopenharmony_ci	/* Using extract instructions is a bit more efficient
2162306a36Sopenharmony_ci	   than the original shift/bitmask version.  */
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci	union {
2462306a36Sopenharmony_ci		unsigned long	ul;
2562306a36Sopenharmony_ci		unsigned int	ui[2];
2662306a36Sopenharmony_ci		unsigned short	us[4];
2762306a36Sopenharmony_ci	} in_v, tmp_v, out_v;
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	in_v.ul = x;
3062306a36Sopenharmony_ci	tmp_v.ul = (unsigned long) in_v.ui[0] + (unsigned long) in_v.ui[1];
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	/* Since the bits of tmp_v.sh[3] are going to always be zero,
3362306a36Sopenharmony_ci	   we don't have to bother to add that in.  */
3462306a36Sopenharmony_ci	out_v.ul = (unsigned long) tmp_v.us[0] + (unsigned long) tmp_v.us[1]
3562306a36Sopenharmony_ci			+ (unsigned long) tmp_v.us[2];
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	/* Similarly, out_v.us[2] is always zero for the final add.  */
3862306a36Sopenharmony_ci	return out_v.us[0] + out_v.us[1];
3962306a36Sopenharmony_ci}
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci/*
4262306a36Sopenharmony_ci * computes the checksum of the TCP/UDP pseudo-header
4362306a36Sopenharmony_ci * returns a 16-bit checksum, already complemented.
4462306a36Sopenharmony_ci */
4562306a36Sopenharmony_ci__sum16 csum_tcpudp_magic(__be32 saddr, __be32 daddr,
4662306a36Sopenharmony_ci			  __u32 len, __u8 proto, __wsum sum)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	return (__force __sum16)~from64to16(
4962306a36Sopenharmony_ci		(__force u64)saddr + (__force u64)daddr +
5062306a36Sopenharmony_ci		(__force u64)sum + ((len + proto) << 8));
5162306a36Sopenharmony_ci}
5262306a36Sopenharmony_ciEXPORT_SYMBOL(csum_tcpudp_magic);
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci__wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr,
5562306a36Sopenharmony_ci			  __u32 len, __u8 proto, __wsum sum)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	unsigned long result;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	result = (__force u64)saddr + (__force u64)daddr +
6062306a36Sopenharmony_ci		 (__force u64)sum + ((len + proto) << 8);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	/* Fold down to 32-bits so we don't lose in the typedef-less
6362306a36Sopenharmony_ci	   network stack.  */
6462306a36Sopenharmony_ci	/* 64 to 33 */
6562306a36Sopenharmony_ci	result = (result & 0xffffffff) + (result >> 32);
6662306a36Sopenharmony_ci	/* 33 to 32 */
6762306a36Sopenharmony_ci	result = (result & 0xffffffff) + (result >> 32);
6862306a36Sopenharmony_ci	return (__force __wsum)result;
6962306a36Sopenharmony_ci}
7062306a36Sopenharmony_ciEXPORT_SYMBOL(csum_tcpudp_nofold);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci/*
7362306a36Sopenharmony_ci * Do a 64-bit checksum on an arbitrary memory area..
7462306a36Sopenharmony_ci *
7562306a36Sopenharmony_ci * This isn't a great routine, but it's not _horrible_ either. The
7662306a36Sopenharmony_ci * inner loop could be unrolled a bit further, and there are better
7762306a36Sopenharmony_ci * ways to do the carry, but this is reasonable.
7862306a36Sopenharmony_ci */
7962306a36Sopenharmony_cistatic inline unsigned long do_csum(const unsigned char * buff, int len)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	int odd, count;
8262306a36Sopenharmony_ci	unsigned long result = 0;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	if (len <= 0)
8562306a36Sopenharmony_ci		goto out;
8662306a36Sopenharmony_ci	odd = 1 & (unsigned long) buff;
8762306a36Sopenharmony_ci	if (odd) {
8862306a36Sopenharmony_ci		result = *buff << 8;
8962306a36Sopenharmony_ci		len--;
9062306a36Sopenharmony_ci		buff++;
9162306a36Sopenharmony_ci	}
9262306a36Sopenharmony_ci	count = len >> 1;		/* nr of 16-bit words.. */
9362306a36Sopenharmony_ci	if (count) {
9462306a36Sopenharmony_ci		if (2 & (unsigned long) buff) {
9562306a36Sopenharmony_ci			result += *(unsigned short *) buff;
9662306a36Sopenharmony_ci			count--;
9762306a36Sopenharmony_ci			len -= 2;
9862306a36Sopenharmony_ci			buff += 2;
9962306a36Sopenharmony_ci		}
10062306a36Sopenharmony_ci		count >>= 1;		/* nr of 32-bit words.. */
10162306a36Sopenharmony_ci		if (count) {
10262306a36Sopenharmony_ci			if (4 & (unsigned long) buff) {
10362306a36Sopenharmony_ci				result += *(unsigned int *) buff;
10462306a36Sopenharmony_ci				count--;
10562306a36Sopenharmony_ci				len -= 4;
10662306a36Sopenharmony_ci				buff += 4;
10762306a36Sopenharmony_ci			}
10862306a36Sopenharmony_ci			count >>= 1;	/* nr of 64-bit words.. */
10962306a36Sopenharmony_ci			if (count) {
11062306a36Sopenharmony_ci				unsigned long carry = 0;
11162306a36Sopenharmony_ci				do {
11262306a36Sopenharmony_ci					unsigned long w = *(unsigned long *) buff;
11362306a36Sopenharmony_ci					count--;
11462306a36Sopenharmony_ci					buff += 8;
11562306a36Sopenharmony_ci					result += carry;
11662306a36Sopenharmony_ci					result += w;
11762306a36Sopenharmony_ci					carry = (w > result);
11862306a36Sopenharmony_ci				} while (count);
11962306a36Sopenharmony_ci				result += carry;
12062306a36Sopenharmony_ci				result = (result & 0xffffffff) + (result >> 32);
12162306a36Sopenharmony_ci			}
12262306a36Sopenharmony_ci			if (len & 4) {
12362306a36Sopenharmony_ci				result += *(unsigned int *) buff;
12462306a36Sopenharmony_ci				buff += 4;
12562306a36Sopenharmony_ci			}
12662306a36Sopenharmony_ci		}
12762306a36Sopenharmony_ci		if (len & 2) {
12862306a36Sopenharmony_ci			result += *(unsigned short *) buff;
12962306a36Sopenharmony_ci			buff += 2;
13062306a36Sopenharmony_ci		}
13162306a36Sopenharmony_ci	}
13262306a36Sopenharmony_ci	if (len & 1)
13362306a36Sopenharmony_ci		result += *buff;
13462306a36Sopenharmony_ci	result = from64to16(result);
13562306a36Sopenharmony_ci	if (odd)
13662306a36Sopenharmony_ci		result = ((result >> 8) & 0xff) | ((result & 0xff) << 8);
13762306a36Sopenharmony_ciout:
13862306a36Sopenharmony_ci	return result;
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci/*
14262306a36Sopenharmony_ci *	This is a version of ip_compute_csum() optimized for IP headers,
14362306a36Sopenharmony_ci *	which always checksum on 4 octet boundaries.
14462306a36Sopenharmony_ci */
14562306a36Sopenharmony_ci__sum16 ip_fast_csum(const void *iph, unsigned int ihl)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	return (__force __sum16)~do_csum(iph,ihl*4);
14862306a36Sopenharmony_ci}
14962306a36Sopenharmony_ciEXPORT_SYMBOL(ip_fast_csum);
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci/*
15262306a36Sopenharmony_ci * computes the checksum of a memory block at buff, length len,
15362306a36Sopenharmony_ci * and adds in "sum" (32-bit)
15462306a36Sopenharmony_ci *
15562306a36Sopenharmony_ci * returns a 32-bit number suitable for feeding into itself
15662306a36Sopenharmony_ci * or csum_tcpudp_magic
15762306a36Sopenharmony_ci *
15862306a36Sopenharmony_ci * this function must be called with even lengths, except
15962306a36Sopenharmony_ci * for the last fragment, which may be odd
16062306a36Sopenharmony_ci *
16162306a36Sopenharmony_ci * it's best to have buff aligned on a 32-bit boundary
16262306a36Sopenharmony_ci */
16362306a36Sopenharmony_ci__wsum csum_partial(const void *buff, int len, __wsum sum)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	unsigned long result = do_csum(buff, len);
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	/* add in old sum, and carry.. */
16862306a36Sopenharmony_ci	result += (__force u32)sum;
16962306a36Sopenharmony_ci	/* 32+c bits -> 32 bits */
17062306a36Sopenharmony_ci	result = (result & 0xffffffff) + (result >> 32);
17162306a36Sopenharmony_ci	return (__force __wsum)result;
17262306a36Sopenharmony_ci}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ciEXPORT_SYMBOL(csum_partial);
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci/*
17762306a36Sopenharmony_ci * this routine is used for miscellaneous IP-like checksums, mainly
17862306a36Sopenharmony_ci * in icmp.c
17962306a36Sopenharmony_ci */
18062306a36Sopenharmony_ci__sum16 ip_compute_csum(const void *buff, int len)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	return (__force __sum16)~from64to16(do_csum(buff,len));
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ciEXPORT_SYMBOL(ip_compute_csum);
185