cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

checksum.c (4671B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * arch/alpha/lib/checksum.c
      4 *
      5 * This file contains network checksum routines that are better done
      6 * in an architecture-specific manner due to speed..
      7 * Comments in other versions indicate that the algorithms are from RFC1071
      8 *
      9 * accelerated versions (and 21264 assembly versions ) contributed by
     10 *	Rick Gorton	<rick.gorton@alpha-processor.com>
     11 */
     12 
     13#include <linux/module.h>
     14#include <linux/string.h>
     15
     16#include <asm/byteorder.h>
     17
     18static inline unsigned short from64to16(unsigned long x)
     19{
     20	/* Using extract instructions is a bit more efficient
     21	   than the original shift/bitmask version.  */
     22
     23	union {
     24		unsigned long	ul;
     25		unsigned int	ui[2];
     26		unsigned short	us[4];
     27	} in_v, tmp_v, out_v;
     28
     29	in_v.ul = x;
     30	tmp_v.ul = (unsigned long) in_v.ui[0] + (unsigned long) in_v.ui[1];
     31
     32	/* Since the bits of tmp_v.sh[3] are going to always be zero,
     33	   we don't have to bother to add that in.  */
     34	out_v.ul = (unsigned long) tmp_v.us[0] + (unsigned long) tmp_v.us[1]
     35			+ (unsigned long) tmp_v.us[2];
     36
     37	/* Similarly, out_v.us[2] is always zero for the final add.  */
     38	return out_v.us[0] + out_v.us[1];
     39}
     40
     41/*
     42 * computes the checksum of the TCP/UDP pseudo-header
     43 * returns a 16-bit checksum, already complemented.
     44 */
     45__sum16 csum_tcpudp_magic(__be32 saddr, __be32 daddr,
     46			  __u32 len, __u8 proto, __wsum sum)
     47{
     48	return (__force __sum16)~from64to16(
     49		(__force u64)saddr + (__force u64)daddr +
     50		(__force u64)sum + ((len + proto) << 8));
     51}
     52EXPORT_SYMBOL(csum_tcpudp_magic);
     53
     54__wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr,
     55			  __u32 len, __u8 proto, __wsum sum)
     56{
     57	unsigned long result;
     58
     59	result = (__force u64)saddr + (__force u64)daddr +
     60		 (__force u64)sum + ((len + proto) << 8);
     61
     62	/* Fold down to 32-bits so we don't lose in the typedef-less 
     63	   network stack.  */
     64	/* 64 to 33 */
     65	result = (result & 0xffffffff) + (result >> 32);
     66	/* 33 to 32 */
     67	result = (result & 0xffffffff) + (result >> 32);
     68	return (__force __wsum)result;
     69}
     70EXPORT_SYMBOL(csum_tcpudp_nofold);
     71
     72/*
     73 * Do a 64-bit checksum on an arbitrary memory area..
     74 *
     75 * This isn't a great routine, but it's not _horrible_ either. The
     76 * inner loop could be unrolled a bit further, and there are better
     77 * ways to do the carry, but this is reasonable.
     78 */
     79static inline unsigned long do_csum(const unsigned char * buff, int len)
     80{
     81	int odd, count;
     82	unsigned long result = 0;
     83
     84	if (len <= 0)
     85		goto out;
     86	odd = 1 & (unsigned long) buff;
     87	if (odd) {
     88		result = *buff << 8;
     89		len--;
     90		buff++;
     91	}
     92	count = len >> 1;		/* nr of 16-bit words.. */
     93	if (count) {
     94		if (2 & (unsigned long) buff) {
     95			result += *(unsigned short *) buff;
     96			count--;
     97			len -= 2;
     98			buff += 2;
     99		}
    100		count >>= 1;		/* nr of 32-bit words.. */
    101		if (count) {
    102			if (4 & (unsigned long) buff) {
    103				result += *(unsigned int *) buff;
    104				count--;
    105				len -= 4;
    106				buff += 4;
    107			}
    108			count >>= 1;	/* nr of 64-bit words.. */
    109			if (count) {
    110				unsigned long carry = 0;
    111				do {
    112					unsigned long w = *(unsigned long *) buff;
    113					count--;
    114					buff += 8;
    115					result += carry;
    116					result += w;
    117					carry = (w > result);
    118				} while (count);
    119				result += carry;
    120				result = (result & 0xffffffff) + (result >> 32);
    121			}
    122			if (len & 4) {
    123				result += *(unsigned int *) buff;
    124				buff += 4;
    125			}
    126		}
    127		if (len & 2) {
    128			result += *(unsigned short *) buff;
    129			buff += 2;
    130		}
    131	}
    132	if (len & 1)
    133		result += *buff;
    134	result = from64to16(result);
    135	if (odd)
    136		result = ((result >> 8) & 0xff) | ((result & 0xff) << 8);
    137out:
    138	return result;
    139}
    140
    141/*
    142 *	This is a version of ip_compute_csum() optimized for IP headers,
    143 *	which always checksum on 4 octet boundaries.
    144 */
    145__sum16 ip_fast_csum(const void *iph, unsigned int ihl)
    146{
    147	return (__force __sum16)~do_csum(iph,ihl*4);
    148}
    149EXPORT_SYMBOL(ip_fast_csum);
    150
    151/*
    152 * computes the checksum of a memory block at buff, length len,
    153 * and adds in "sum" (32-bit)
    154 *
    155 * returns a 32-bit number suitable for feeding into itself
    156 * or csum_tcpudp_magic
    157 *
    158 * this function must be called with even lengths, except
    159 * for the last fragment, which may be odd
    160 *
    161 * it's best to have buff aligned on a 32-bit boundary
    162 */
    163__wsum csum_partial(const void *buff, int len, __wsum sum)
    164{
    165	unsigned long result = do_csum(buff, len);
    166
    167	/* add in old sum, and carry.. */
    168	result += (__force u32)sum;
    169	/* 32+c bits -> 32 bits */
    170	result = (result & 0xffffffff) + (result >> 32);
    171	return (__force __wsum)result;
    172}
    173
    174EXPORT_SYMBOL(csum_partial);
    175
    176/*
    177 * this routine is used for miscellaneous IP-like checksums, mainly
    178 * in icmp.c
    179 */
    180__sum16 ip_compute_csum(const void *buff, int len)
    181{
    182	return (__force __sum16)~from64to16(do_csum(buff,len));
    183}
    184EXPORT_SYMBOL(ip_compute_csum);