1 /* SPDX-License-Identifier: GPL-2.0 */ 2 #ifndef _ASM_X86_CHECKSUM_64_H 3 #define _ASM_X86_CHECKSUM_64_H 4 5 /* 6 * Checksums for x86-64 7 * Copyright 2002 by Andi Kleen, SuSE Labs 8 * with some code from asm-x86/checksum.h 9 */ 10 11 #include <linux/compiler.h> 12 #include <linux/in6.h> 13 #include <asm/byteorder.h> 14 15 /** 16 * csum_fold - Fold and invert a 32bit checksum. 17 * @sum: 32bit unfolded sum 18 * 19 * Fold a 32bit running checksum to 16bit and invert it. This is usually 20 * the last step before putting a checksum into a packet. 21 * Make sure not to mix with 64bit checksums. 22 * 23 * Returns: new checksum value 24 */ 25 static inline __sum16 csum_fold(__wsum sum) 26 { 27 asm(" addl %1,%0\n" 28 " adcl $0xffff,%0" 29 : "=r" (sum) 30 : "r" ((__force u32)sum << 16), 31 "0" ((__force u32)sum & 0xffff0000)); 32 return (__force __sum16)(~(__force u32)sum >> 16); 33 } 34 35 /* 36 * This is a version of ip_compute_csum() optimized for IP headers, 37 * which always checksum on 4 octet boundaries. 38 * 39 * By Jorge Cwik <jorge@laser.satlink.net>, adapted for linux by 40 * Arnt Gulbrandsen. 41 */ 42 43 /** 44 * ip_fast_csum - Compute the IPv4 header checksum efficiently. 45 * @iph: ipv4 header 46 * @ihl: length of header / 4 47 * 48 * Returns: header checksum 49 */ 50 static inline __sum16 ip_fast_csum(const void *iph, unsigned int ihl) 51 { 52 unsigned int sum; 53 54 asm(" movl (%1), %0\n" 55 " subl $4, %2\n" 56 " jbe 2f\n" 57 " addl 4(%1), %0\n" 58 " adcl 8(%1), %0\n" 59 " adcl 12(%1), %0\n" 60 "1: adcl 16(%1), %0\n" 61 " lea 4(%1), %1\n" 62 " decl %2\n" 63 " jne 1b\n" 64 " adcl $0, %0\n" 65 " movl %0, %2\n" 66 " shrl $16, %0\n" 67 " addw %w2, %w0\n" 68 " adcl $0, %0\n" 69 " notl %0\n" 70 "2:" 71 /* Since the input registers which are loaded with iph and ihl 72 are modified, we must also specify them as outputs, or gcc 73 will assume they contain their original values. */ 74 : "=r" (sum), "=r" (iph), "=r" (ihl) 75 : "1" (iph), "2" (ihl) 76 : "memory"); 77 return (__force __sum16)sum; 78 } 79 80 /** 81 * csum_tcpudp_nofold - Compute an IPv4 pseudo header checksum. 82 * @saddr: source address 83 * @daddr: destination address 84 * @len: length of packet 85 * @proto: ip protocol of packet 86 * @sum: initial sum to be added in (32bit unfolded) 87 * 88 * Returns: the pseudo header checksum the input data. Result is 89 * 32bit unfolded. 90 */ 91 static inline __wsum 92 csum_tcpudp_nofold(__be32 saddr, __be32 daddr, __u32 len, 93 __u8 proto, __wsum sum) 94 { 95 asm(" addl %1, %0\n" 96 " adcl %2, %0\n" 97 " adcl %3, %0\n" 98 " adcl $0, %0\n" 99 : "=r" (sum) 100 : "g" (daddr), "g" (saddr), 101 "g" ((len + proto)<<8), "0" (sum)); 102 return sum; 103 } 104 105 106 /** 107 * csum_tcpudp_magic - Compute an IPv4 pseudo header checksum. 108 * @saddr: source address 109 * @daddr: destination address 110 * @len: length of packet 111 * @proto: ip protocol of packet 112 * @sum: initial sum to be added in (32bit unfolded) 113 * 114 * Returns: the 16bit pseudo header checksum the input data already 115 * complemented and ready to be filled in. 116 */ 117 static inline __sum16 csum_tcpudp_magic(__be32 saddr, __be32 daddr, 118 __u32 len, __u8 proto, 119 __wsum sum) 120 { 121 return csum_fold(csum_tcpudp_nofold(saddr, daddr, len, proto, sum)); 122 } 123 124 /** 125 * csum_partial - Compute an internet checksum. 126 * @buff: buffer to be checksummed 127 * @len: length of buffer. 128 * @sum: initial sum to be added in (32bit unfolded) 129 * 130 * Returns: the 32bit unfolded internet checksum of the buffer. 131 * Before filling it in it needs to be csum_fold()'ed. 132 * buff should be aligned to a 64bit boundary if possible. 133 */ 134 extern __wsum csum_partial(const void *buff, int len, __wsum sum); 135 136 /* Do not call this directly. Use the wrappers below */ 137 extern __visible __wsum csum_partial_copy_generic(const void *src, void *dst, int len); 138 139 extern __wsum csum_and_copy_from_user(const void __user *src, void *dst, int len); 140 extern __wsum csum_and_copy_to_user(const void *src, void __user *dst, int len); 141 extern __wsum csum_partial_copy_nocheck(const void *src, void *dst, int len); 142 143 /** 144 * ip_compute_csum - Compute an 16bit IP checksum. 145 * @buff: buffer address. 146 * @len: length of buffer. 147 * 148 * Returns: the 16bit folded/inverted checksum of the passed buffer. 149 * Ready to fill in. 150 */ 151 extern __sum16 ip_compute_csum(const void *buff, int len); 152 153 static inline unsigned add32_with_carry(unsigned a, unsigned b) 154 { 155 asm("addl %2,%0\n\t" 156 "adcl $0,%0" 157 : "=r" (a) 158 : "0" (a), "rm" (b)); 159 return a; 160 } 161 162 #define _HAVE_ARCH_IPV6_CSUM 1 163 164 /** 165 * csum_ipv6_magic - Compute checksum of an IPv6 pseudo header. 166 * @_saddr: source address 167 * @_daddr: destination address 168 * @len: length of packet 169 * @proto: protocol of packet 170 * @sum: initial sum (32bit unfolded) to be added in 171 * 172 * Computes an IPv6 pseudo header checksum. This sum is added the checksum 173 * into UDP/TCP packets and contains some link layer information. 174 * 175 * Returns: the unfolded 32bit checksum. 176 */ 177 178 static inline __sum16 csum_ipv6_magic( 179 const struct in6_addr *_saddr, const struct in6_addr *_daddr, 180 __u32 len, __u8 proto, __wsum sum) 181 { 182 const unsigned long *saddr = (const unsigned long *)_saddr; 183 const unsigned long *daddr = (const unsigned long *)_daddr; 184 __u64 sum64; 185 186 sum64 = (__force __u64)htonl(len) + (__force __u64)htons(proto) + 187 (__force __u64)sum; 188 189 asm(" addq %1,%[sum64]\n" 190 " adcq %2,%[sum64]\n" 191 " adcq %3,%[sum64]\n" 192 " adcq %4,%[sum64]\n" 193 " adcq $0,%[sum64]\n" 194 195 : [sum64] "+r" (sum64) 196 : "m" (saddr[0]), "m" (saddr[1]), 197 "m" (daddr[0]), "m" (daddr[1])); 198 199 return csum_fold( 200 (__force __wsum)add32_with_carry(sum64 & 0xffffffff, sum64>>32)); 201 } 202 203 #define HAVE_ARCH_CSUM_ADD 204 static inline __wsum csum_add(__wsum csum, __wsum addend) 205 { 206 return (__force __wsum)add32_with_carry((__force unsigned)csum, 207 (__force unsigned)addend); 208 } 209 210 #endif /* _ASM_X86_CHECKSUM_64_H */ 211