mirror of
				https://git.kernel.org/pub/scm/linux/kernel/git/chenhuacai/linux-loongson
				synced 2025-10-31 01:24:43 +00:00 
			
		
		
		
	 aa7bd94249
			
		
	
	
		aa7bd94249
		
	
	
	
	
		
			
			Signed-off-by: Al Viro <viro@zeniv.linux.org.uk> Signed-off-by: H. Peter Anvin <hpa@zytor.com>
		
			
				
	
	
		
			145 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			145 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * Licensed under the GPL
 | |
|  */
 | |
| 
 | |
| #ifndef __UM_SYSDEP_CHECKSUM_H
 | |
| #define __UM_SYSDEP_CHECKSUM_H
 | |
| 
 | |
| #include "linux/string.h"
 | |
| #include "linux/in6.h"
 | |
| #include "asm/uaccess.h"
 | |
| 
 | |
| extern __wsum csum_partial(const void *buff, int len, __wsum sum);
 | |
| 
 | |
| /*
 | |
|  *	Note: when you get a NULL pointer exception here this means someone
 | |
|  *	passed in an incorrect kernel address to one of these functions.
 | |
|  *
 | |
|  *	If you use these functions directly please don't forget the
 | |
|  *	access_ok().
 | |
|  */
 | |
| 
 | |
| static __inline__
 | |
| __wsum csum_partial_copy_nocheck(const void *src, void *dst,
 | |
| 				       int len, __wsum sum)
 | |
| {
 | |
| 	memcpy(dst, src, len);
 | |
| 	return(csum_partial(dst, len, sum));
 | |
| }
 | |
| 
 | |
| static __inline__
 | |
| __wsum csum_partial_copy_from_user(const void __user *src,
 | |
|                                          void *dst, int len, __wsum sum,
 | |
|                                          int *err_ptr)
 | |
| {
 | |
|         if (copy_from_user(dst, src, len)) {
 | |
|                 *err_ptr = -EFAULT;
 | |
|                 return (__force __wsum)-1;
 | |
|         }
 | |
|         return csum_partial(dst, len, sum);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * csum_fold - Fold and invert a 32bit checksum.
 | |
|  * sum: 32bit unfolded sum
 | |
|  *
 | |
|  * Fold a 32bit running checksum to 16bit and invert it. This is usually
 | |
|  * the last step before putting a checksum into a packet.
 | |
|  * Make sure not to mix with 64bit checksums.
 | |
|  */
 | |
| static inline __sum16 csum_fold(__wsum sum)
 | |
| {
 | |
| 	__asm__(
 | |
| 		"  addl %1,%0\n"
 | |
| 		"  adcl $0xffff,%0"
 | |
| 		: "=r" (sum)
 | |
| 		: "r" ((__force u32)sum << 16),
 | |
| 		  "0" ((__force u32)sum & 0xffff0000)
 | |
| 	);
 | |
| 	return (__force __sum16)(~(__force u32)sum >> 16);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * csum_tcpup_nofold - Compute an IPv4 pseudo header checksum.
 | |
|  * @saddr: source address
 | |
|  * @daddr: destination address
 | |
|  * @len: length of packet
 | |
|  * @proto: ip protocol of packet
 | |
|  * @sum: initial sum to be added in (32bit unfolded)
 | |
|  *
 | |
|  * Returns the pseudo header checksum the input data. Result is
 | |
|  * 32bit unfolded.
 | |
|  */
 | |
| static inline __wsum
 | |
| csum_tcpudp_nofold(__be32 saddr, __be32 daddr, unsigned short len,
 | |
| 		   unsigned short proto, __wsum sum)
 | |
| {
 | |
| 	asm("  addl %1, %0\n"
 | |
| 	    "  adcl %2, %0\n"
 | |
| 	    "  adcl %3, %0\n"
 | |
| 	    "  adcl $0, %0\n"
 | |
| 		: "=r" (sum)
 | |
| 	    : "g" (daddr), "g" (saddr), "g" ((len + proto) << 8), "0" (sum));
 | |
| 	return sum;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * computes the checksum of the TCP/UDP pseudo-header
 | |
|  * returns a 16-bit checksum, already complemented
 | |
|  */
 | |
| static inline __sum16 csum_tcpudp_magic(__be32 saddr, __be32 daddr,
 | |
| 					   unsigned short len,
 | |
| 					   unsigned short proto,
 | |
| 					   __wsum sum)
 | |
| {
 | |
| 	return csum_fold(csum_tcpudp_nofold(saddr,daddr,len,proto,sum));
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * ip_fast_csum - Compute the IPv4 header checksum efficiently.
 | |
|  * iph: ipv4 header
 | |
|  * ihl: length of header / 4
 | |
|  */
 | |
| static inline __sum16 ip_fast_csum(const void *iph, unsigned int ihl)
 | |
| {
 | |
| 	unsigned int sum;
 | |
| 
 | |
| 	asm(	"  movl (%1), %0\n"
 | |
| 		"  subl $4, %2\n"
 | |
| 		"  jbe 2f\n"
 | |
| 		"  addl 4(%1), %0\n"
 | |
| 		"  adcl 8(%1), %0\n"
 | |
| 		"  adcl 12(%1), %0\n"
 | |
| 		"1: adcl 16(%1), %0\n"
 | |
| 		"  lea 4(%1), %1\n"
 | |
| 		"  decl %2\n"
 | |
| 		"  jne	1b\n"
 | |
| 		"  adcl $0, %0\n"
 | |
| 		"  movl %0, %2\n"
 | |
| 		"  shrl $16, %0\n"
 | |
| 		"  addw %w2, %w0\n"
 | |
| 		"  adcl $0, %0\n"
 | |
| 		"  notl %0\n"
 | |
| 		"2:"
 | |
| 	/* Since the input registers which are loaded with iph and ipl
 | |
| 	   are modified, we must also specify them as outputs, or gcc
 | |
| 	   will assume they contain their original values. */
 | |
| 	: "=r" (sum), "=r" (iph), "=r" (ihl)
 | |
| 	: "1" (iph), "2" (ihl)
 | |
| 	: "memory");
 | |
| 	return (__force __sum16)sum;
 | |
| }
 | |
| 
 | |
| static inline unsigned add32_with_carry(unsigned a, unsigned b)
 | |
| {
 | |
|         asm("addl %2,%0\n\t"
 | |
|             "adcl $0,%0"
 | |
|             : "=r" (a)
 | |
|             : "0" (a), "r" (b));
 | |
|         return a;
 | |
| }
 | |
| 
 | |
| extern __sum16 ip_compute_csum(const void *buff, int len);
 | |
| 
 | |
| #endif
 |