1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Network Checksum & Copy routine 4 * 5 * Copyright (C) 1999, 2003-2004 Hewlett-Packard Co 6 * Stephane Eranian <eranian@hpl.hp.com> 7 * 8 * Most of the code has been imported from Linux/Alpha 9 */ 10 11 #include <linux/module.h> 12 #include <linux/types.h> 13 #include <linux/string.h> 14 15 #include <linux/uaccess.h> 16 17 /* 18 * XXX Fixme: those 2 inlines are meant for debugging and will go away 19 */ 20 static inline unsigned 21 short from64to16(unsigned long x) 22 { 23 /* add up 32-bit words for 33 bits */ 24 x = (x & 0xffffffff) + (x >> 32); 25 /* add up 16-bit and 17-bit words for 17+c bits */ 26 x = (x & 0xffff) + (x >> 16); 27 /* add up 16-bit and 2-bit for 16+c bit */ 28 x = (x & 0xffff) + (x >> 16); 29 /* add up carry.. */ 30 x = (x & 0xffff) + (x >> 16); 31 return x; 32 } 33 34 static inline 35 unsigned long do_csum_c(const unsigned char * buff, int len, unsigned int psum) 36 { 37 int odd, count; 38 unsigned long result = (unsigned long)psum; 39 40 if (len <= 0) 41 goto out; 42 odd = 1 & (unsigned long) buff; 43 if (odd) { 44 result = *buff << 8; 45 len--; 46 buff++; 47 } 48 count = len >> 1; /* nr of 16-bit words.. */ 49 if (count) { 50 if (2 & (unsigned long) buff) { 51 result += *(unsigned short *) buff; 52 count--; 53 len -= 2; 54 buff += 2; 55 } 56 count >>= 1; /* nr of 32-bit words.. */ 57 if (count) { 58 if (4 & (unsigned long) buff) { 59 result += *(unsigned int *) buff; 60 count--; 61 len -= 4; 62 buff += 4; 63 } 64 count >>= 1; /* nr of 64-bit words.. */ 65 if (count) { 66 unsigned long carry = 0; 67 do { 68 unsigned long w = *(unsigned long *) buff; 69 count--; 70 buff += 8; 71 result += carry; 72 result += w; 73 carry = (w > result); 74 } while (count); 75 result += carry; 76 result = (result & 0xffffffff) + (result >> 32); 77 } 78 if (len & 4) { 79 result += *(unsigned int *) buff; 80 buff += 4; 81 } 82 } 83 if (len & 2) { 84 result += *(unsigned short *) buff; 85 buff += 2; 86 } 87 } 88 if (len & 1) 89 result += *buff; 90 91 result = from64to16(result); 92 93 if (odd) 94 result = ((result >> 8) & 0xff) | ((result & 0xff) << 8); 95 96 out: 97 return result; 98 } 99 100 /* 101 * XXX Fixme 102 * 103 * This is very ugly but temporary. THIS NEEDS SERIOUS ENHANCEMENTS. 104 * But it's very tricky to get right even in C. 105 */ 106 extern unsigned long do_csum(const unsigned char *, long); 107 108 __wsum 109 csum_partial_copy_from_user(const void __user *src, void *dst, 110 int len, __wsum psum, int *errp) 111 { 112 unsigned long result; 113 114 /* XXX Fixme 115 * for now we separate the copy from checksum for obvious 116 * alignment difficulties. Look at the Alpha code and you'll be 117 * scared. 118 */ 119 120 if (__copy_from_user(dst, src, len) != 0 && errp) 121 *errp = -EFAULT; 122 123 result = do_csum(dst, len); 124 125 /* add in old sum, and carry.. */ 126 result += (__force u32)psum; 127 /* 32+c bits -> 32 bits */ 128 result = (result & 0xffffffff) + (result >> 32); 129 return (__force __wsum)result; 130 } 131 132 EXPORT_SYMBOL(csum_partial_copy_from_user); 133 134 __wsum 135 csum_partial_copy_nocheck(const void *src, void *dst, int len, __wsum sum) 136 { 137 return csum_partial_copy_from_user((__force const void __user *)src, 138 dst, len, sum, NULL); 139 } 140 141 EXPORT_SYMBOL(csum_partial_copy_nocheck); 142