blob: 67d3630c4e8953c74773ce17aaf1bf2c197c1930 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001#ifndef _I386_CHECKSUM_H
2#define _I386_CHECKSUM_H
3
4#include <linux/in6.h>
5
Alexey Dobriyana9ed8812005-06-23 00:08:32 -07006#include <asm/uaccess.h>
7
Linus Torvalds1da177e2005-04-16 15:20:36 -07008/*
9 * computes the checksum of a memory block at buff, length len,
10 * and adds in "sum" (32-bit)
11 *
12 * returns a 32-bit number suitable for feeding into itself
13 * or csum_tcpudp_magic
14 *
15 * this function must be called with even lengths, except
16 * for the last fragment, which may be odd
17 *
18 * it's best to have buff aligned on a 32-bit boundary
19 */
20asmlinkage unsigned int csum_partial(const unsigned char * buff, int len, unsigned int sum);
21
22/*
23 * the same as csum_partial, but copies from src while it
24 * checksums, and handles user-space pointer exceptions correctly, when needed.
25 *
26 * here even more important to align src and dst on a 32-bit (or even
27 * better 64-bit) boundary
28 */
29
30asmlinkage unsigned int csum_partial_copy_generic(const unsigned char *src, unsigned char *dst,
31 int len, int sum, int *src_err_ptr, int *dst_err_ptr);
32
33/*
34 * Note: when you get a NULL pointer exception here this means someone
35 * passed in an incorrect kernel address to one of these functions.
36 *
37 * If you use these functions directly please don't forget the
Jesper Juhle49332b2005-05-01 08:59:08 -070038 * access_ok().
Linus Torvalds1da177e2005-04-16 15:20:36 -070039 */
40static __inline__
41unsigned int csum_partial_copy_nocheck (const unsigned char *src, unsigned char *dst,
42 int len, int sum)
43{
44 return csum_partial_copy_generic ( src, dst, len, sum, NULL, NULL);
45}
46
47static __inline__
48unsigned int csum_partial_copy_from_user(const unsigned char __user *src, unsigned char *dst,
49 int len, int sum, int *err_ptr)
50{
51 might_sleep();
52 return csum_partial_copy_generic((__force unsigned char *)src, dst,
53 len, sum, err_ptr, NULL);
54}
55
56/*
57 * This is a version of ip_compute_csum() optimized for IP headers,
58 * which always checksum on 4 octet boundaries.
59 *
60 * By Jorge Cwik <jorge@laser.satlink.net>, adapted for linux by
61 * Arnt Gulbrandsen.
62 */
63static inline unsigned short ip_fast_csum(unsigned char * iph,
64 unsigned int ihl)
65{
66 unsigned int sum;
67
68 __asm__ __volatile__(
69 "movl (%1), %0 ;\n"
70 "subl $4, %2 ;\n"
71 "jbe 2f ;\n"
72 "addl 4(%1), %0 ;\n"
73 "adcl 8(%1), %0 ;\n"
74 "adcl 12(%1), %0 ;\n"
75"1: adcl 16(%1), %0 ;\n"
76 "lea 4(%1), %1 ;\n"
77 "decl %2 ;\n"
78 "jne 1b ;\n"
79 "adcl $0, %0 ;\n"
80 "movl %0, %2 ;\n"
81 "shrl $16, %0 ;\n"
82 "addw %w2, %w0 ;\n"
83 "adcl $0, %0 ;\n"
84 "notl %0 ;\n"
85"2: ;\n"
Thomas Graf2c656492005-08-20 17:24:25 -070086 /* Since the input registers which are loaded with iph and ihl
Linus Torvalds1da177e2005-04-16 15:20:36 -070087 are modified, we must also specify them as outputs, or gcc
88 will assume they contain their original values. */
89 : "=r" (sum), "=r" (iph), "=r" (ihl)
90 : "1" (iph), "2" (ihl)
91 : "memory");
92 return(sum);
93}
94
95/*
96 * Fold a partial checksum
97 */
98
99static inline unsigned int csum_fold(unsigned int sum)
100{
101 __asm__(
102 "addl %1, %0 ;\n"
103 "adcl $0xffff, %0 ;\n"
104 : "=r" (sum)
105 : "r" (sum << 16), "0" (sum & 0xffff0000)
106 );
107 return (~sum) >> 16;
108}
109
110static inline unsigned long csum_tcpudp_nofold(unsigned long saddr,
111 unsigned long daddr,
112 unsigned short len,
113 unsigned short proto,
114 unsigned int sum)
115{
116 __asm__(
117 "addl %1, %0 ;\n"
118 "adcl %2, %0 ;\n"
119 "adcl %3, %0 ;\n"
120 "adcl $0, %0 ;\n"
121 : "=r" (sum)
122 : "g" (daddr), "g"(saddr), "g"((ntohs(len)<<16)+proto*256), "0"(sum));
123 return sum;
124}
125
126/*
127 * computes the checksum of the TCP/UDP pseudo-header
128 * returns a 16-bit checksum, already complemented
129 */
130static inline unsigned short int csum_tcpudp_magic(unsigned long saddr,
131 unsigned long daddr,
132 unsigned short len,
133 unsigned short proto,
134 unsigned int sum)
135{
136 return csum_fold(csum_tcpudp_nofold(saddr,daddr,len,proto,sum));
137}
138
139/*
140 * this routine is used for miscellaneous IP-like checksums, mainly
141 * in icmp.c
142 */
143
144static inline unsigned short ip_compute_csum(unsigned char * buff, int len)
145{
146 return csum_fold (csum_partial(buff, len, 0));
147}
148
149#define _HAVE_ARCH_IPV6_CSUM
150static __inline__ unsigned short int csum_ipv6_magic(struct in6_addr *saddr,
151 struct in6_addr *daddr,
152 __u32 len,
153 unsigned short proto,
154 unsigned int sum)
155{
156 __asm__(
157 "addl 0(%1), %0 ;\n"
158 "adcl 4(%1), %0 ;\n"
159 "adcl 8(%1), %0 ;\n"
160 "adcl 12(%1), %0 ;\n"
161 "adcl 0(%2), %0 ;\n"
162 "adcl 4(%2), %0 ;\n"
163 "adcl 8(%2), %0 ;\n"
164 "adcl 12(%2), %0 ;\n"
165 "adcl %3, %0 ;\n"
166 "adcl %4, %0 ;\n"
167 "adcl $0, %0 ;\n"
168 : "=&r" (sum)
169 : "r" (saddr), "r" (daddr),
170 "r"(htonl(len)), "r"(htonl(proto)), "0"(sum));
171
172 return csum_fold(sum);
173}
174
175/*
176 * Copy and checksum to user
177 */
178#define HAVE_CSUM_COPY_USER
179static __inline__ unsigned int csum_and_copy_to_user(const unsigned char *src,
180 unsigned char __user *dst,
181 int len, int sum,
182 int *err_ptr)
183{
184 might_sleep();
185 if (access_ok(VERIFY_WRITE, dst, len))
186 return csum_partial_copy_generic(src, (__force unsigned char *)dst, len, sum, NULL, err_ptr);
187
188 if (len)
189 *err_ptr = -EFAULT;
190
191 return -1; /* invalid checksum */
192}
193
194#endif