1/* 2 * This file is subject to the terms and conditions of the GNU General Public 3 * License. See the file "COPYING" in the main directory of this archive 4 * for more details. 5 * 6 * Copyright (C) 1995, 1996, 1997, 1998, 1999 by Ralf Baechle 7 * Copyright (C) 1999 Silicon Graphics, Inc. 8 * Copyright (C) 2001 Thiemo Seufer. 9 * Copyright (C) 2002 Maciej W. Rozycki 10 */ 11#ifndef _ASM_CHECKSUM_H 12#define _ASM_CHECKSUM_H 13 14#include <asm/uaccess.h> 15 16/* 17 * computes the checksum of a memory block at buff, length len, 18 * and adds in "sum" (32-bit) 19 * 20 * returns a 32-bit number suitable for feeding into itself 21 * or csum_tcpudp_magic 22 * 23 * this function must be called with even lengths, except 24 * for the last fragment, which may be odd 25 * 26 * it's best to have buff aligned on a 32-bit boundary 27 */ 28unsigned int csum_partial(const unsigned char *buff, int len, unsigned int sum); 29 30/* 31 * this is a new version of the above that records errors it finds in *errp, 32 * but continues and zeros the rest of the buffer. 33 */ 34#define csum_partial_copy_nocheck csum_partial_copy 35 36/* 37 * this is a new version of the above that records errors it finds in *errp, 38 * but continues and zeros the rest of the buffer. 39 */ 40unsigned int csum_partial_copy_from_user(const char *src, char *dst, int len, 41 unsigned int sum, int *errp); 42 43/* 44 * Copy and checksum to user 45 */ 46#define HAVE_CSUM_COPY_USER 47static inline unsigned int csum_and_copy_to_user (const char *src, char *dst, 48 int len, int sum, 49 int *err_ptr) 50{ 51 sum = csum_partial(src, len, sum); 52 53 if (copy_to_user(dst, src, len)) { 54 *err_ptr = -EFAULT; 55 return -1; 56 } 57 58 return sum; 59} 60 61/* 62 * the same as csum_partial, but copies from user space (but on MIPS 63 * we have just one address space, so this is identical to the above) 64 * 65 * this is obsolete and will go away. 66 */ 67#define csum_partial_copy_fromuser csum_partial_copy 68unsigned int csum_partial_copy(const char *src, char *dst, int len, 69 unsigned int sum); 70 71/* 72 * Fold a partial checksum without adding pseudo headers 73 */ 74static inline unsigned short int csum_fold(unsigned int sum) 75{ 76 __asm__( 77 ".set\tnoat\t\t\t# csum_fold\n\t" 78 "sll\t$1,%0,16\n\t" 79 "addu\t%0,$1\n\t" 80 "sltu\t$1,%0,$1\n\t" 81 "srl\t%0,%0,16\n\t" 82 "addu\t%0,$1\n\t" 83 "xori\t%0,0xffff\n\t" 84 ".set\tat" 85 : "=r" (sum) 86 : "0" (sum)); 87 88 return sum; 89} 90 91/* 92 * This is a version of ip_compute_csum() optimized for IP headers, 93 * which always checksum on 4 octet boundaries. 94 * 95 * By Jorge Cwik <jorge@laser.satlink.net>, adapted for linux by 96 * Arnt Gulbrandsen. 97 */ 98static inline unsigned short ip_fast_csum(unsigned char *iph, 99 unsigned int ihl) 100{ 101 unsigned int sum; 102 unsigned long dummy; 103 104 __asm__ __volatile__( 105 ".set\tnoreorder\t\t\t# ip_fast_csum\n\t" 106 ".set\tnoat\n\t" 107 "lw\t%0, (%1)\n\t" 108 "subu\t%2, 4\n\t" 109 "dsll\t%2, 2\n\t" 110 "lw\t%3, 4(%1)\n\t" 111 "daddu\t%2, %1\n\t" 112 "addu\t%0, %3\n\t" 113 "sltu\t$1, %0, %3\n\t" 114 "lw\t%3, 8(%1)\n\t" 115 "addu\t%0, $1\n\t" 116 "addu\t%0, %3\n\t" 117 "sltu\t$1, %0, %3\n\t" 118 "lw\t%3, 12(%1)\n\t" 119 "addu\t%0, $1\n\t" 120 "addu\t%0, %3\n\t" 121 "sltu\t$1, %0, %3\n\t" 122 "addu\t%0, $1\n" 123 124 "1:\tlw\t%3, 16(%1)\n\t" 125 "daddiu\t%1, 4\n" 126 "addu\t%0, %3\n\t" 127 "sltu\t$1, %0, %3\n\t" 128 "bne\t%2, %1, 1b\n\t" 129 " addu\t%0, $1\n" 130 131 "2:\t.set\tat\n\t" 132 ".set\treorder" 133 : "=&r" (sum), "=&r" (iph), "=&r" (ihl), "=&r" (dummy) 134 : "1" (iph), "2" (ihl)); 135 136 return csum_fold(sum); 137} 138 139/* 140 * computes the checksum of the TCP/UDP pseudo-header 141 * returns a 16-bit checksum, already complemented 142 * 143 * Cast unsigned short expressions to unsigned long explicitly 144 * to avoid surprises resulting from implicit promotions to 145 * signed int. --macro 146 */ 147static inline unsigned long csum_tcpudp_nofold(unsigned long saddr, 148 unsigned long daddr, 149 unsigned short len, 150 unsigned short proto, 151 unsigned int sum) 152{ 153 __asm__( 154 ".set\tnoat\t\t\t# csum_tcpudp_nofold\n\t" 155 "daddu\t%0, %2\n\t" 156 "daddu\t%0, %3\n\t" 157 "daddu\t%0, %4\n\t" 158 "dsll32\t$1, %0, 0\n\t" 159 "daddu\t%0, $1\n\t" 160 "dsrl32\t%0, %0, 0\n\t" 161 ".set\tat" 162 : "=&r" (sum) 163 : "0" (daddr), "r"(saddr), 164#ifdef __MIPSEL__ 165 "r" (((unsigned long)ntohs(len)<<16)+proto*256), 166#else 167 "r" (((unsigned long)(proto)<<16)+len), 168#endif 169 "r" (sum)); 170 171 return sum; 172} 173 174/* 175 * computes the checksum of the TCP/UDP pseudo-header 176 * returns a 16-bit checksum, already complemented 177 */ 178static inline unsigned short int csum_tcpudp_magic(unsigned long saddr, 179 unsigned long daddr, 180 unsigned short len, 181 unsigned short proto, 182 unsigned int sum) 183{ 184 return csum_fold(csum_tcpudp_nofold(saddr, daddr, len, proto, sum)); 185} 186 187/* 188 * this routine is used for miscellaneous IP-like checksums, mainly 189 * in icmp.c 190 */ 191static inline unsigned short ip_compute_csum(unsigned char * buff, int len) 192{ 193 return csum_fold(csum_partial(buff, len, 0)); 194} 195 196#define _HAVE_ARCH_IPV6_CSUM 197static __inline__ unsigned short int csum_ipv6_magic(struct in6_addr *saddr, 198 struct in6_addr *daddr, 199 __u32 len, 200 unsigned short proto, 201 unsigned int sum) 202{ 203 __asm__( 204 ".set\tpush\t\t\t# csum_ipv6_magic\n\t" 205 ".set\tnoreorder\n\t" 206 ".set\tnoat\n\t" 207 "addu\t%0, %5\t\t\t# proto (long in network byte order)\n\t" 208 "sltu\t$1, %0, %5\n\t" 209 "addu\t%0, $1\n\t" 210 211 "addu\t%0, %6\t\t\t# csum\n\t" 212 "sltu\t$1, %0, %6\n\t" 213 "lw\t%1, 0(%2)\t\t\t# four words source address\n\t" 214 "addu\t%0, $1\n\t" 215 "addu\t%0, %1\n\t" 216 "sltu\t$1, %0, %1\n\t" 217 218 "lw\t%1, 4(%2)\n\t" 219 "addu\t%0, $1\n\t" 220 "addu\t%0, %1\n\t" 221 "sltu\t$1, %0, %1\n\t" 222 223 "lw\t%1, 8(%2)\n\t" 224 "addu\t%0, $1\n\t" 225 "addu\t%0, %1\n\t" 226 "sltu\t$1, %0, %1\n\t" 227 228 "lw\t%1, 12(%2)\n\t" 229 "addu\t%0, $1\n\t" 230 "addu\t%0, %1\n\t" 231 "sltu\t$1, %0, %1\n\t" 232 233 "lw\t%1, 0(%3)\n\t" 234 "addu\t%0, $1\n\t" 235 "addu\t%0, %1\n\t" 236 "sltu\t$1, %0, %1\n\t" 237 238 "lw\t%1, 4(%3)\n\t" 239 "addu\t%0, $1\n\t" 240 "addu\t%0, %1\n\t" 241 "sltu\t$1, %0, %1\n\t" 242 243 "lw\t%1, 8(%3)\n\t" 244 "addu\t%0, $1\n\t" 245 "addu\t%0, %1\n\t" 246 "sltu\t$1, %0, %1\n\t" 247 248 "lw\t%1, 12(%3)\n\t" 249 "addu\t%0, $1\n\t" 250 "addu\t%0, %1\n\t" 251 "sltu\t$1, %0, %1\n\t" 252 253 "addu\t%0, $1\t\t\t# Add final carry\n\t" 254 ".set\tpop" 255 : "=&r" (sum), "=&r" (proto) 256 : "r" (saddr), "r" (daddr), 257 "0" (htonl(len)), "1" (htonl(proto)), "r" (sum)); 258 259 return csum_fold(sum); 260} 261 262#endif /* _ASM_CHECKSUM_H */ 263