blob: f50de69517384b712c1266a3c98626b888d6953a [file] [log] [blame]
H. Peter Anvin1965aae2008-10-22 22:26:29 -07001#ifndef _ASM_X86_CHECKSUM_32_H
2#define _ASM_X86_CHECKSUM_32_H
Linus Torvalds1da177e2005-04-16 15:20:36 -07003
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 */
Al Viro72685fc2006-11-14 21:21:37 -080020asmlinkage __wsum csum_partial(const void *buff, int len, __wsum sum);
Linus Torvalds1da177e2005-04-16 15:20:36 -070021
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
Al Viro72685fc2006-11-14 21:21:37 -080030asmlinkage __wsum csum_partial_copy_generic(const void *src, void *dst,
Joe Perches0883e912008-03-23 01:01:49 -070031 int len, __wsum sum,
32 int *src_err_ptr, int *dst_err_ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -070033
34/*
35 * Note: when you get a NULL pointer exception here this means someone
36 * passed in an incorrect kernel address to one of these functions.
37 *
38 * If you use these functions directly please don't forget the
Jesper Juhle49332b2005-05-01 08:59:08 -070039 * access_ok().
Linus Torvalds1da177e2005-04-16 15:20:36 -070040 */
Joe Perches0883e912008-03-23 01:01:49 -070041static inline __wsum csum_partial_copy_nocheck(const void *src, void *dst,
42 int len, __wsum sum)
Linus Torvalds1da177e2005-04-16 15:20:36 -070043{
Joe Perches0883e912008-03-23 01:01:49 -070044 return csum_partial_copy_generic(src, dst, len, sum, NULL, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -070045}
46
Joe Perches0883e912008-03-23 01:01:49 -070047static inline __wsum csum_partial_copy_from_user(const void __user *src,
48 void *dst,
49 int len, __wsum sum,
50 int *err_ptr)
Linus Torvalds1da177e2005-04-16 15:20:36 -070051{
H. Peter Anvin7263dda2013-08-30 15:43:03 -070052 __wsum ret;
53
Linus Torvalds1da177e2005-04-16 15:20:36 -070054 might_sleep();
H. Peter Anvin7263dda2013-08-30 15:43:03 -070055 stac();
56 ret = csum_partial_copy_generic((__force void *)src, dst,
57 len, sum, err_ptr, NULL);
58 clac();
59
60 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -070061}
62
63/*
64 * This is a version of ip_compute_csum() optimized for IP headers,
65 * which always checksum on 4 octet boundaries.
66 *
67 * By Jorge Cwik <jorge@laser.satlink.net>, adapted for linux by
68 * Arnt Gulbrandsen.
69 */
Al Viro72685fc2006-11-14 21:21:37 -080070static inline __sum16 ip_fast_csum(const void *iph, unsigned int ihl)
Linus Torvalds1da177e2005-04-16 15:20:36 -070071{
72 unsigned int sum;
73
Joe Perches0883e912008-03-23 01:01:49 -070074 asm volatile("movl (%1), %0 ;\n"
75 "subl $4, %2 ;\n"
76 "jbe 2f ;\n"
77 "addl 4(%1), %0 ;\n"
78 "adcl 8(%1), %0 ;\n"
79 "adcl 12(%1), %0;\n"
80 "1: adcl 16(%1), %0 ;\n"
81 "lea 4(%1), %1 ;\n"
82 "decl %2 ;\n"
83 "jne 1b ;\n"
84 "adcl $0, %0 ;\n"
85 "movl %0, %2 ;\n"
86 "shrl $16, %0 ;\n"
87 "addw %w2, %w0 ;\n"
88 "adcl $0, %0 ;\n"
89 "notl %0 ;\n"
90 "2: ;\n"
Thomas Graf2c656492005-08-20 17:24:25 -070091 /* Since the input registers which are loaded with iph and ihl
Linus Torvalds1da177e2005-04-16 15:20:36 -070092 are modified, we must also specify them as outputs, or gcc
93 will assume they contain their original values. */
Joe Perches0883e912008-03-23 01:01:49 -070094 : "=r" (sum), "=r" (iph), "=r" (ihl)
95 : "1" (iph), "2" (ihl)
96 : "memory");
Al Viro72685fc2006-11-14 21:21:37 -080097 return (__force __sum16)sum;
Linus Torvalds1da177e2005-04-16 15:20:36 -070098}
99
100/*
101 * Fold a partial checksum
102 */
103
Al Viro72685fc2006-11-14 21:21:37 -0800104static inline __sum16 csum_fold(__wsum sum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700105{
Joe Perches0883e912008-03-23 01:01:49 -0700106 asm("addl %1, %0 ;\n"
107 "adcl $0xffff, %0 ;\n"
108 : "=r" (sum)
109 : "r" ((__force u32)sum << 16),
110 "0" ((__force u32)sum & 0xffff0000));
Al Viro72685fc2006-11-14 21:21:37 -0800111 return (__force __sum16)(~(__force u32)sum >> 16);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112}
113
Al Viro72685fc2006-11-14 21:21:37 -0800114static inline __wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr,
Joe Perches0883e912008-03-23 01:01:49 -0700115 unsigned short len,
116 unsigned short proto,
117 __wsum sum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118{
Joe Perches0883e912008-03-23 01:01:49 -0700119 asm("addl %1, %0 ;\n"
120 "adcl %2, %0 ;\n"
121 "adcl %3, %0 ;\n"
122 "adcl $0, %0 ;\n"
123 : "=r" (sum)
124 : "g" (daddr), "g"(saddr),
125 "g" ((len + proto) << 8), "0" (sum));
126 return sum;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127}
128
129/*
130 * computes the checksum of the TCP/UDP pseudo-header
131 * returns a 16-bit checksum, already complemented
132 */
Al Viro72685fc2006-11-14 21:21:37 -0800133static inline __sum16 csum_tcpudp_magic(__be32 saddr, __be32 daddr,
Joe Perches0883e912008-03-23 01:01:49 -0700134 unsigned short len,
135 unsigned short proto,
136 __wsum sum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137{
Joe Perches0883e912008-03-23 01:01:49 -0700138 return csum_fold(csum_tcpudp_nofold(saddr, daddr, len, proto, sum));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139}
140
141/*
142 * this routine is used for miscellaneous IP-like checksums, mainly
143 * in icmp.c
144 */
145
Al Viro72685fc2006-11-14 21:21:37 -0800146static inline __sum16 ip_compute_csum(const void *buff, int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147{
Joe Perches0883e912008-03-23 01:01:49 -0700148 return csum_fold(csum_partial(buff, len, 0));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149}
150
151#define _HAVE_ARCH_IPV6_CSUM
Joe Perches0883e912008-03-23 01:01:49 -0700152static inline __sum16 csum_ipv6_magic(const struct in6_addr *saddr,
153 const struct in6_addr *daddr,
154 __u32 len, unsigned short proto,
155 __wsum sum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156{
Joe Perches0883e912008-03-23 01:01:49 -0700157 asm("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),
Samuel Thibault392d8142009-10-01 15:44:02 -0700170 "r" (htonl(len)), "r" (htonl(proto)), "0" (sum)
171 : "memory");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172
173 return csum_fold(sum);
174}
175
176/*
177 * Copy and checksum to user
178 */
179#define HAVE_CSUM_COPY_USER
Joe Perches0883e912008-03-23 01:01:49 -0700180static inline __wsum csum_and_copy_to_user(const void *src,
181 void __user *dst,
182 int len, __wsum sum,
183 int *err_ptr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184{
H. Peter Anvin7263dda2013-08-30 15:43:03 -0700185 __wsum ret;
186
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187 might_sleep();
H. Peter Anvin7263dda2013-08-30 15:43:03 -0700188 if (access_ok(VERIFY_WRITE, dst, len)) {
189 stac();
190 ret = csum_partial_copy_generic(src, (__force void *)dst,
191 len, sum, NULL, err_ptr);
192 clac();
193 return ret;
194 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195
196 if (len)
197 *err_ptr = -EFAULT;
198
Al Viro72685fc2006-11-14 21:21:37 -0800199 return (__force __wsum)-1; /* invalid checksum */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200}
201
H. Peter Anvin1965aae2008-10-22 22:26:29 -0700202#endif /* _ASM_X86_CHECKSUM_32_H */