| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | * INET		An implementation of the TCP/IP protocol suite for the LINUX | 
|  | 3 | *		operating system.  INET is implemented using the  BSD Socket | 
|  | 4 | *		interface as the means of communication with the user level. | 
|  | 5 | * | 
|  | 6 | *		IP/TCP/UDP checksumming routines | 
|  | 7 | * | 
|  | 8 | * Authors:	Jorge Cwik, <jorge@laser.satlink.net> | 
|  | 9 | *		Arnt Gulbrandsen, <agulbra@nvg.unit.no> | 
|  | 10 | *		Tom May, <ftom@netcom.com> | 
|  | 11 | *		Andreas Schwab, <schwab@issan.informatik.uni-dortmund.de> | 
|  | 12 | *		Lots of code moved from tcp.c and ip.c; see those files | 
|  | 13 | *		for more names. | 
|  | 14 | * | 
|  | 15 | * 03/02/96	Jes Sorensen, Andreas Schwab, Roman Hodek: | 
|  | 16 | *		Fixed some nasty bugs, causing some horrible crashes. | 
|  | 17 | *		A: At some points, the sum (%0) was used as | 
|  | 18 | *		length-counter instead of the length counter | 
|  | 19 | *		(%1). Thanks to Roman Hodek for pointing this out. | 
|  | 20 | *		B: GCC seems to mess up if one uses too many | 
|  | 21 | *		data-registers to hold input values and one tries to | 
|  | 22 | *		specify d0 and d1 as scratch registers. Letting gcc | 
|  | 23 | *		choose these registers itself solves the problem. | 
|  | 24 | * | 
|  | 25 | *		This program is free software; you can redistribute it and/or | 
|  | 26 | *		modify it under the terms of the GNU General Public License | 
|  | 27 | *		as published by the Free Software Foundation; either version | 
|  | 28 | *		2 of the License, or (at your option) any later version. | 
|  | 29 | * | 
|  | 30 | * 1998/8/31	Andreas Schwab: | 
|  | 31 | *		Zero out rest of buffer on exception in | 
|  | 32 | *		csum_partial_copy_from_user. | 
|  | 33 | */ | 
|  | 34 |  | 
|  | 35 | #include <linux/module.h> | 
|  | 36 | #include <net/checksum.h> | 
|  | 37 |  | 
|  | 38 | /* | 
|  | 39 | * computes a partial checksum, e.g. for TCP/UDP fragments | 
|  | 40 | */ | 
|  | 41 |  | 
|  | 42 | unsigned int | 
|  | 43 | csum_partial (const unsigned char *buff, int len, unsigned int sum) | 
|  | 44 | { | 
|  | 45 | unsigned long tmp1, tmp2; | 
|  | 46 | /* | 
|  | 47 | * Experiments with ethernet and slip connections show that buff | 
|  | 48 | * is aligned on either a 2-byte or 4-byte boundary. | 
|  | 49 | */ | 
|  | 50 | __asm__("movel %2,%3\n\t" | 
|  | 51 | "btst #1,%3\n\t"	/* Check alignment */ | 
|  | 52 | "jeq 2f\n\t" | 
|  | 53 | "subql #2,%1\n\t"	/* buff%4==2: treat first word */ | 
|  | 54 | "jgt 1f\n\t" | 
|  | 55 | "addql #2,%1\n\t"	/* len was == 2, treat only rest */ | 
|  | 56 | "jra 4f\n" | 
|  | 57 | "1:\t" | 
|  | 58 | "addw %2@+,%0\n\t"	/* add first word to sum */ | 
|  | 59 | "clrl %3\n\t" | 
|  | 60 | "addxl %3,%0\n"		/* add X bit */ | 
|  | 61 | "2:\t" | 
|  | 62 | /* unrolled loop for the main part: do 8 longs at once */ | 
|  | 63 | "movel %1,%3\n\t"	/* save len in tmp1 */ | 
|  | 64 | "lsrl #5,%1\n\t"	/* len/32 */ | 
|  | 65 | "jeq 2f\n\t"		/* not enough... */ | 
|  | 66 | "subql #1,%1\n" | 
|  | 67 | "1:\t" | 
|  | 68 | "movel %2@+,%4\n\t" | 
|  | 69 | "addxl %4,%0\n\t" | 
|  | 70 | "movel %2@+,%4\n\t" | 
|  | 71 | "addxl %4,%0\n\t" | 
|  | 72 | "movel %2@+,%4\n\t" | 
|  | 73 | "addxl %4,%0\n\t" | 
|  | 74 | "movel %2@+,%4\n\t" | 
|  | 75 | "addxl %4,%0\n\t" | 
|  | 76 | "movel %2@+,%4\n\t" | 
|  | 77 | "addxl %4,%0\n\t" | 
|  | 78 | "movel %2@+,%4\n\t" | 
|  | 79 | "addxl %4,%0\n\t" | 
|  | 80 | "movel %2@+,%4\n\t" | 
|  | 81 | "addxl %4,%0\n\t" | 
|  | 82 | "movel %2@+,%4\n\t" | 
|  | 83 | "addxl %4,%0\n\t" | 
|  | 84 | "dbra %1,1b\n\t" | 
|  | 85 | "clrl %4\n\t" | 
|  | 86 | "addxl %4,%0\n\t"	/* add X bit */ | 
|  | 87 | "clrw %1\n\t" | 
|  | 88 | "subql #1,%1\n\t" | 
|  | 89 | "jcc 1b\n" | 
|  | 90 | "2:\t" | 
|  | 91 | "movel %3,%1\n\t"	/* restore len from tmp1 */ | 
|  | 92 | "andw #0x1c,%3\n\t"	/* number of rest longs */ | 
|  | 93 | "jeq 4f\n\t" | 
|  | 94 | "lsrw #2,%3\n\t" | 
|  | 95 | "subqw #1,%3\n" | 
|  | 96 | "3:\t" | 
|  | 97 | /* loop for rest longs */ | 
|  | 98 | "movel %2@+,%4\n\t" | 
|  | 99 | "addxl %4,%0\n\t" | 
|  | 100 | "dbra %3,3b\n\t" | 
|  | 101 | "clrl %4\n\t" | 
|  | 102 | "addxl %4,%0\n"		/* add X bit */ | 
|  | 103 | "4:\t" | 
|  | 104 | /* now check for rest bytes that do not fit into longs */ | 
|  | 105 | "andw #3,%1\n\t" | 
|  | 106 | "jeq 7f\n\t" | 
|  | 107 | "clrl %4\n\t"		/* clear tmp2 for rest bytes */ | 
|  | 108 | "subqw #2,%1\n\t" | 
|  | 109 | "jlt 5f\n\t" | 
|  | 110 | "movew %2@+,%4\n\t"	/* have rest >= 2: get word */ | 
|  | 111 | "swap %4\n\t"		/* into bits 16..31 */ | 
|  | 112 | "tstw %1\n\t"		/* another byte? */ | 
|  | 113 | "jeq 6f\n" | 
|  | 114 | "5:\t" | 
|  | 115 | "moveb %2@,%4\n\t"	/* have odd rest: get byte */ | 
|  | 116 | "lslw #8,%4\n\t"	/* into bits 8..15; 16..31 untouched */ | 
|  | 117 | "6:\t" | 
|  | 118 | "addl %4,%0\n\t"	/* now add rest long to sum */ | 
|  | 119 | "clrl %4\n\t" | 
|  | 120 | "addxl %4,%0\n"		/* add X bit */ | 
|  | 121 | "7:\t" | 
|  | 122 | : "=d" (sum), "=d" (len), "=a" (buff), | 
|  | 123 | "=&d" (tmp1), "=&d" (tmp2) | 
|  | 124 | : "0" (sum), "1" (len), "2" (buff) | 
|  | 125 | ); | 
|  | 126 | return(sum); | 
|  | 127 | } | 
|  | 128 |  | 
|  | 129 | EXPORT_SYMBOL(csum_partial); | 
|  | 130 |  | 
|  | 131 |  | 
|  | 132 | /* | 
|  | 133 | * copy from user space while checksumming, with exception handling. | 
|  | 134 | */ | 
|  | 135 |  | 
|  | 136 | unsigned int | 
| Al Viro | 6225d85 | 2006-01-12 01:06:28 -0800 | [diff] [blame] | 137 | csum_partial_copy_from_user(const unsigned char __user *src, unsigned char *dst, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 138 | int len, int sum, int *csum_err) | 
|  | 139 | { | 
|  | 140 | /* | 
|  | 141 | * GCC doesn't like more than 10 operands for the asm | 
|  | 142 | * statements so we have to use tmp2 for the error | 
|  | 143 | * code. | 
|  | 144 | */ | 
|  | 145 | unsigned long tmp1, tmp2; | 
|  | 146 |  | 
|  | 147 | __asm__("movel %2,%4\n\t" | 
|  | 148 | "btst #1,%4\n\t"	/* Check alignment */ | 
|  | 149 | "jeq 2f\n\t" | 
|  | 150 | "subql #2,%1\n\t"	/* buff%4==2: treat first word */ | 
|  | 151 | "jgt 1f\n\t" | 
|  | 152 | "addql #2,%1\n\t"	/* len was == 2, treat only rest */ | 
|  | 153 | "jra 4f\n" | 
|  | 154 | "1:\n" | 
|  | 155 | "10:\t" | 
|  | 156 | "movesw %2@+,%4\n\t"	/* add first word to sum */ | 
|  | 157 | "addw %4,%0\n\t" | 
|  | 158 | "movew %4,%3@+\n\t" | 
|  | 159 | "clrl %4\n\t" | 
|  | 160 | "addxl %4,%0\n"		/* add X bit */ | 
|  | 161 | "2:\t" | 
|  | 162 | /* unrolled loop for the main part: do 8 longs at once */ | 
|  | 163 | "movel %1,%4\n\t"	/* save len in tmp1 */ | 
|  | 164 | "lsrl #5,%1\n\t"	/* len/32 */ | 
|  | 165 | "jeq 2f\n\t"		/* not enough... */ | 
|  | 166 | "subql #1,%1\n" | 
|  | 167 | "1:\n" | 
|  | 168 | "11:\t" | 
|  | 169 | "movesl %2@+,%5\n\t" | 
|  | 170 | "addxl %5,%0\n\t" | 
|  | 171 | "movel %5,%3@+\n\t" | 
|  | 172 | "12:\t" | 
|  | 173 | "movesl %2@+,%5\n\t" | 
|  | 174 | "addxl %5,%0\n\t" | 
|  | 175 | "movel %5,%3@+\n\t" | 
|  | 176 | "13:\t" | 
|  | 177 | "movesl %2@+,%5\n\t" | 
|  | 178 | "addxl %5,%0\n\t" | 
|  | 179 | "movel %5,%3@+\n\t" | 
|  | 180 | "14:\t" | 
|  | 181 | "movesl %2@+,%5\n\t" | 
|  | 182 | "addxl %5,%0\n\t" | 
|  | 183 | "movel %5,%3@+\n\t" | 
|  | 184 | "15:\t" | 
|  | 185 | "movesl %2@+,%5\n\t" | 
|  | 186 | "addxl %5,%0\n\t" | 
|  | 187 | "movel %5,%3@+\n\t" | 
|  | 188 | "16:\t" | 
|  | 189 | "movesl %2@+,%5\n\t" | 
|  | 190 | "addxl %5,%0\n\t" | 
|  | 191 | "movel %5,%3@+\n\t" | 
|  | 192 | "17:\t" | 
|  | 193 | "movesl %2@+,%5\n\t" | 
|  | 194 | "addxl %5,%0\n\t" | 
|  | 195 | "movel %5,%3@+\n\t" | 
|  | 196 | "18:\t" | 
|  | 197 | "movesl %2@+,%5\n\t" | 
|  | 198 | "addxl %5,%0\n\t" | 
|  | 199 | "movel %5,%3@+\n\t" | 
|  | 200 | "dbra %1,1b\n\t" | 
|  | 201 | "clrl %5\n\t" | 
|  | 202 | "addxl %5,%0\n\t"	/* add X bit */ | 
|  | 203 | "clrw %1\n\t" | 
|  | 204 | "subql #1,%1\n\t" | 
|  | 205 | "jcc 1b\n" | 
|  | 206 | "2:\t" | 
|  | 207 | "movel %4,%1\n\t"	/* restore len from tmp1 */ | 
|  | 208 | "andw #0x1c,%4\n\t"	/* number of rest longs */ | 
|  | 209 | "jeq 4f\n\t" | 
|  | 210 | "lsrw #2,%4\n\t" | 
|  | 211 | "subqw #1,%4\n" | 
|  | 212 | "3:\n" | 
|  | 213 | /* loop for rest longs */ | 
|  | 214 | "19:\t" | 
|  | 215 | "movesl %2@+,%5\n\t" | 
|  | 216 | "addxl %5,%0\n\t" | 
|  | 217 | "movel %5,%3@+\n\t" | 
|  | 218 | "dbra %4,3b\n\t" | 
|  | 219 | "clrl %5\n\t" | 
|  | 220 | "addxl %5,%0\n"		/* add X bit */ | 
|  | 221 | "4:\t" | 
|  | 222 | /* now check for rest bytes that do not fit into longs */ | 
|  | 223 | "andw #3,%1\n\t" | 
|  | 224 | "jeq 7f\n\t" | 
|  | 225 | "clrl %5\n\t"		/* clear tmp2 for rest bytes */ | 
|  | 226 | "subqw #2,%1\n\t" | 
|  | 227 | "jlt 5f\n\t" | 
|  | 228 | "20:\t" | 
|  | 229 | "movesw %2@+,%5\n\t"	/* have rest >= 2: get word */ | 
|  | 230 | "movew %5,%3@+\n\t" | 
|  | 231 | "swap %5\n\t"		/* into bits 16..31 */ | 
|  | 232 | "tstw %1\n\t"		/* another byte? */ | 
|  | 233 | "jeq 6f\n" | 
|  | 234 | "5:\n" | 
|  | 235 | "21:\t" | 
|  | 236 | "movesb %2@,%5\n\t"	/* have odd rest: get byte */ | 
|  | 237 | "moveb %5,%3@+\n\t" | 
|  | 238 | "lslw #8,%5\n\t"	/* into bits 8..15; 16..31 untouched */ | 
|  | 239 | "6:\t" | 
|  | 240 | "addl %5,%0\n\t"	/* now add rest long to sum */ | 
|  | 241 | "clrl %5\n\t" | 
|  | 242 | "addxl %5,%0\n\t"	/* add X bit */ | 
|  | 243 | "7:\t" | 
|  | 244 | "clrl %5\n"		/* no error - clear return value */ | 
|  | 245 | "8:\n" | 
|  | 246 | ".section .fixup,\"ax\"\n" | 
|  | 247 | ".even\n" | 
|  | 248 | /* If any exception occurs zero out the rest. | 
|  | 249 | Similarities with the code above are intentional :-) */ | 
|  | 250 | "90:\t" | 
|  | 251 | "clrw %3@+\n\t" | 
|  | 252 | "movel %1,%4\n\t" | 
|  | 253 | "lsrl #5,%1\n\t" | 
|  | 254 | "jeq 1f\n\t" | 
|  | 255 | "subql #1,%1\n" | 
|  | 256 | "91:\t" | 
|  | 257 | "clrl %3@+\n" | 
|  | 258 | "92:\t" | 
|  | 259 | "clrl %3@+\n" | 
|  | 260 | "93:\t" | 
|  | 261 | "clrl %3@+\n" | 
|  | 262 | "94:\t" | 
|  | 263 | "clrl %3@+\n" | 
|  | 264 | "95:\t" | 
|  | 265 | "clrl %3@+\n" | 
|  | 266 | "96:\t" | 
|  | 267 | "clrl %3@+\n" | 
|  | 268 | "97:\t" | 
|  | 269 | "clrl %3@+\n" | 
|  | 270 | "98:\t" | 
|  | 271 | "clrl %3@+\n\t" | 
|  | 272 | "dbra %1,91b\n\t" | 
|  | 273 | "clrw %1\n\t" | 
|  | 274 | "subql #1,%1\n\t" | 
|  | 275 | "jcc 91b\n" | 
|  | 276 | "1:\t" | 
|  | 277 | "movel %4,%1\n\t" | 
|  | 278 | "andw #0x1c,%4\n\t" | 
|  | 279 | "jeq 1f\n\t" | 
|  | 280 | "lsrw #2,%4\n\t" | 
|  | 281 | "subqw #1,%4\n" | 
|  | 282 | "99:\t" | 
|  | 283 | "clrl %3@+\n\t" | 
|  | 284 | "dbra %4,99b\n\t" | 
|  | 285 | "1:\t" | 
|  | 286 | "andw #3,%1\n\t" | 
|  | 287 | "jeq 9f\n" | 
|  | 288 | "100:\t" | 
|  | 289 | "clrw %3@+\n\t" | 
|  | 290 | "tstw %1\n\t" | 
|  | 291 | "jeq 9f\n" | 
|  | 292 | "101:\t" | 
|  | 293 | "clrb %3@+\n" | 
|  | 294 | "9:\t" | 
|  | 295 | #define STR(X) STR1(X) | 
|  | 296 | #define STR1(X) #X | 
|  | 297 | "moveq #-" STR(EFAULT) ",%5\n\t" | 
|  | 298 | "jra 8b\n" | 
|  | 299 | ".previous\n" | 
|  | 300 | ".section __ex_table,\"a\"\n" | 
|  | 301 | ".long 10b,90b\n" | 
|  | 302 | ".long 11b,91b\n" | 
|  | 303 | ".long 12b,92b\n" | 
|  | 304 | ".long 13b,93b\n" | 
|  | 305 | ".long 14b,94b\n" | 
|  | 306 | ".long 15b,95b\n" | 
|  | 307 | ".long 16b,96b\n" | 
|  | 308 | ".long 17b,97b\n" | 
|  | 309 | ".long 18b,98b\n" | 
|  | 310 | ".long 19b,99b\n" | 
|  | 311 | ".long 20b,100b\n" | 
|  | 312 | ".long 21b,101b\n" | 
|  | 313 | ".previous" | 
|  | 314 | : "=d" (sum), "=d" (len), "=a" (src), "=a" (dst), | 
|  | 315 | "=&d" (tmp1), "=d" (tmp2) | 
|  | 316 | : "0" (sum), "1" (len), "2" (src), "3" (dst) | 
|  | 317 | ); | 
|  | 318 |  | 
|  | 319 | *csum_err = tmp2; | 
|  | 320 |  | 
|  | 321 | return(sum); | 
|  | 322 | } | 
|  | 323 |  | 
|  | 324 | /* | 
|  | 325 | * copy from kernel space while checksumming, otherwise like csum_partial | 
|  | 326 | */ | 
|  | 327 |  | 
|  | 328 | unsigned int | 
|  | 329 | csum_partial_copy_nocheck(const unsigned char *src, unsigned char *dst, int len, int sum) | 
|  | 330 | { | 
|  | 331 | unsigned long tmp1, tmp2; | 
|  | 332 | __asm__("movel %2,%4\n\t" | 
|  | 333 | "btst #1,%4\n\t"	/* Check alignment */ | 
|  | 334 | "jeq 2f\n\t" | 
|  | 335 | "subql #2,%1\n\t"	/* buff%4==2: treat first word */ | 
|  | 336 | "jgt 1f\n\t" | 
|  | 337 | "addql #2,%1\n\t"	/* len was == 2, treat only rest */ | 
|  | 338 | "jra 4f\n" | 
|  | 339 | "1:\t" | 
|  | 340 | "movew %2@+,%4\n\t"	/* add first word to sum */ | 
|  | 341 | "addw %4,%0\n\t" | 
|  | 342 | "movew %4,%3@+\n\t" | 
|  | 343 | "clrl %4\n\t" | 
|  | 344 | "addxl %4,%0\n"		/* add X bit */ | 
|  | 345 | "2:\t" | 
|  | 346 | /* unrolled loop for the main part: do 8 longs at once */ | 
|  | 347 | "movel %1,%4\n\t"	/* save len in tmp1 */ | 
|  | 348 | "lsrl #5,%1\n\t"	/* len/32 */ | 
|  | 349 | "jeq 2f\n\t"		/* not enough... */ | 
|  | 350 | "subql #1,%1\n" | 
|  | 351 | "1:\t" | 
|  | 352 | "movel %2@+,%5\n\t" | 
|  | 353 | "addxl %5,%0\n\t" | 
|  | 354 | "movel %5,%3@+\n\t" | 
|  | 355 | "movel %2@+,%5\n\t" | 
|  | 356 | "addxl %5,%0\n\t" | 
|  | 357 | "movel %5,%3@+\n\t" | 
|  | 358 | "movel %2@+,%5\n\t" | 
|  | 359 | "addxl %5,%0\n\t" | 
|  | 360 | "movel %5,%3@+\n\t" | 
|  | 361 | "movel %2@+,%5\n\t" | 
|  | 362 | "addxl %5,%0\n\t" | 
|  | 363 | "movel %5,%3@+\n\t" | 
|  | 364 | "movel %2@+,%5\n\t" | 
|  | 365 | "addxl %5,%0\n\t" | 
|  | 366 | "movel %5,%3@+\n\t" | 
|  | 367 | "movel %2@+,%5\n\t" | 
|  | 368 | "addxl %5,%0\n\t" | 
|  | 369 | "movel %5,%3@+\n\t" | 
|  | 370 | "movel %2@+,%5\n\t" | 
|  | 371 | "addxl %5,%0\n\t" | 
|  | 372 | "movel %5,%3@+\n\t" | 
|  | 373 | "movel %2@+,%5\n\t" | 
|  | 374 | "addxl %5,%0\n\t" | 
|  | 375 | "movel %5,%3@+\n\t" | 
|  | 376 | "dbra %1,1b\n\t" | 
|  | 377 | "clrl %5\n\t" | 
|  | 378 | "addxl %5,%0\n\t"	/* add X bit */ | 
|  | 379 | "clrw %1\n\t" | 
|  | 380 | "subql #1,%1\n\t" | 
|  | 381 | "jcc 1b\n" | 
|  | 382 | "2:\t" | 
|  | 383 | "movel %4,%1\n\t"	/* restore len from tmp1 */ | 
|  | 384 | "andw #0x1c,%4\n\t"	/* number of rest longs */ | 
|  | 385 | "jeq 4f\n\t" | 
|  | 386 | "lsrw #2,%4\n\t" | 
|  | 387 | "subqw #1,%4\n" | 
|  | 388 | "3:\t" | 
|  | 389 | /* loop for rest longs */ | 
|  | 390 | "movel %2@+,%5\n\t" | 
|  | 391 | "addxl %5,%0\n\t" | 
|  | 392 | "movel %5,%3@+\n\t" | 
|  | 393 | "dbra %4,3b\n\t" | 
|  | 394 | "clrl %5\n\t" | 
|  | 395 | "addxl %5,%0\n"		/* add X bit */ | 
|  | 396 | "4:\t" | 
|  | 397 | /* now check for rest bytes that do not fit into longs */ | 
|  | 398 | "andw #3,%1\n\t" | 
|  | 399 | "jeq 7f\n\t" | 
|  | 400 | "clrl %5\n\t"		/* clear tmp2 for rest bytes */ | 
|  | 401 | "subqw #2,%1\n\t" | 
|  | 402 | "jlt 5f\n\t" | 
|  | 403 | "movew %2@+,%5\n\t"	/* have rest >= 2: get word */ | 
|  | 404 | "movew %5,%3@+\n\t" | 
|  | 405 | "swap %5\n\t"		/* into bits 16..31 */ | 
|  | 406 | "tstw %1\n\t"		/* another byte? */ | 
|  | 407 | "jeq 6f\n" | 
|  | 408 | "5:\t" | 
|  | 409 | "moveb %2@,%5\n\t"	/* have odd rest: get byte */ | 
|  | 410 | "moveb %5,%3@+\n\t" | 
|  | 411 | "lslw #8,%5\n"		/* into bits 8..15; 16..31 untouched */ | 
|  | 412 | "6:\t" | 
|  | 413 | "addl %5,%0\n\t"	/* now add rest long to sum */ | 
|  | 414 | "clrl %5\n\t" | 
|  | 415 | "addxl %5,%0\n"		/* add X bit */ | 
|  | 416 | "7:\t" | 
|  | 417 | : "=d" (sum), "=d" (len), "=a" (src), "=a" (dst), | 
|  | 418 | "=&d" (tmp1), "=&d" (tmp2) | 
|  | 419 | : "0" (sum), "1" (len), "2" (src), "3" (dst) | 
|  | 420 | ); | 
|  | 421 | return(sum); | 
|  | 422 | } |