blob: 17da953879976edc3cf3e082b866ee6dc67c5030 [file] [log] [blame]
Greg Kroah-Hartmanb2441312017-11-01 15:07:57 +01001/* SPDX-License-Identifier: GPL-2.0 */
H. Peter Anvin1965aae2008-10-22 22:26:29 -07002#ifndef _ASM_X86_CHECKSUM_32_H
3#define _ASM_X86_CHECKSUM_32_H
Linus Torvalds1da177e2005-04-16 15:20:36 -07004
5#include <linux/in6.h>
Andy Lutomirski13d4ea02016-07-14 13:22:57 -07006#include <linux/uaccess.h>
Alexey Dobriyana9ed8812005-06-23 00:08:32 -07007
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 Viroe8b95082020-07-12 23:53:10 -040030asmlinkage __wsum csum_partial_copy_generic(const void *src, void *dst, int len);
Linus Torvalds1da177e2005-04-16 15:20:36 -070031
32/*
33 * Note: when you get a NULL pointer exception here this means someone
34 * passed in an incorrect kernel address to one of these functions.
35 *
36 * If you use these functions directly please don't forget the
Jesper Juhle49332b2005-05-01 08:59:08 -070037 * access_ok().
Linus Torvalds1da177e2005-04-16 15:20:36 -070038 */
Al Virocc44c172020-07-11 00:12:07 -040039static inline __wsum csum_partial_copy_nocheck(const void *src, void *dst, int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -070040{
Al Viroe8b95082020-07-12 23:53:10 -040041 return csum_partial_copy_generic(src, dst, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -070042}
43
Al Viro0a5ea222020-02-16 16:50:00 -050044static inline __wsum csum_and_copy_from_user(const void __user *src,
Al Viroc693cc42020-07-11 00:27:49 -040045 void *dst, int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -070046{
H. Peter Anvin7263dda2013-08-30 15:43:03 -070047 __wsum ret;
48
Linus Torvalds1da177e2005-04-16 15:20:36 -070049 might_sleep();
Al Viroc693cc42020-07-11 00:27:49 -040050 if (!user_access_begin(src, len))
51 return 0;
Al Viroe8b95082020-07-12 23:53:10 -040052 ret = csum_partial_copy_generic((__force void *)src, dst, len);
Al Viro0a5ea222020-02-16 16:50:00 -050053 user_access_end();
H. Peter Anvin7263dda2013-08-30 15:43:03 -070054
Al Viroe8b95082020-07-12 23:53:10 -040055 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -070056}
57
58/*
59 * This is a version of ip_compute_csum() optimized for IP headers,
60 * which always checksum on 4 octet boundaries.
61 *
62 * By Jorge Cwik <jorge@laser.satlink.net>, adapted for linux by
63 * Arnt Gulbrandsen.
64 */
Al Viro72685fc2006-11-14 21:21:37 -080065static inline __sum16 ip_fast_csum(const void *iph, unsigned int ihl)
Linus Torvalds1da177e2005-04-16 15:20:36 -070066{
67 unsigned int sum;
68
Joe Perches0883e912008-03-23 01:01:49 -070069 asm volatile("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. */
Joe Perches0883e912008-03-23 01:01:49 -070089 : "=r" (sum), "=r" (iph), "=r" (ihl)
90 : "1" (iph), "2" (ihl)
91 : "memory");
Al Viro72685fc2006-11-14 21:21:37 -080092 return (__force __sum16)sum;
Linus Torvalds1da177e2005-04-16 15:20:36 -070093}
94
95/*
96 * Fold a partial checksum
97 */
98
Al Viro72685fc2006-11-14 21:21:37 -080099static inline __sum16 csum_fold(__wsum sum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100{
Joe Perches0883e912008-03-23 01:01:49 -0700101 asm("addl %1, %0 ;\n"
102 "adcl $0xffff, %0 ;\n"
103 : "=r" (sum)
104 : "r" ((__force u32)sum << 16),
105 "0" ((__force u32)sum & 0xffff0000));
Al Viro72685fc2006-11-14 21:21:37 -0800106 return (__force __sum16)(~(__force u32)sum >> 16);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107}
108
Al Viro72685fc2006-11-14 21:21:37 -0800109static inline __wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr,
Alexander Duyck01cfbad2016-03-11 14:05:34 -0800110 __u32 len, __u8 proto,
Joe Perches0883e912008-03-23 01:01:49 -0700111 __wsum sum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112{
Joe Perches0883e912008-03-23 01:01:49 -0700113 asm("addl %1, %0 ;\n"
114 "adcl %2, %0 ;\n"
115 "adcl %3, %0 ;\n"
116 "adcl $0, %0 ;\n"
117 : "=r" (sum)
118 : "g" (daddr), "g"(saddr),
119 "g" ((len + proto) << 8), "0" (sum));
120 return sum;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121}
122
123/*
124 * computes the checksum of the TCP/UDP pseudo-header
125 * returns a 16-bit checksum, already complemented
126 */
Al Viro72685fc2006-11-14 21:21:37 -0800127static inline __sum16 csum_tcpudp_magic(__be32 saddr, __be32 daddr,
Alexander Duyck01cfbad2016-03-11 14:05:34 -0800128 __u32 len, __u8 proto,
Joe Perches0883e912008-03-23 01:01:49 -0700129 __wsum sum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130{
Joe Perches0883e912008-03-23 01:01:49 -0700131 return csum_fold(csum_tcpudp_nofold(saddr, daddr, len, proto, sum));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132}
133
134/*
135 * this routine is used for miscellaneous IP-like checksums, mainly
136 * in icmp.c
137 */
138
Al Viro72685fc2006-11-14 21:21:37 -0800139static inline __sum16 ip_compute_csum(const void *buff, int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140{
Joe Perches0883e912008-03-23 01:01:49 -0700141 return csum_fold(csum_partial(buff, len, 0));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142}
143
144#define _HAVE_ARCH_IPV6_CSUM
Joe Perches0883e912008-03-23 01:01:49 -0700145static inline __sum16 csum_ipv6_magic(const struct in6_addr *saddr,
146 const struct in6_addr *daddr,
Alexander Duyck1e940822016-03-11 14:05:41 -0800147 __u32 len, __u8 proto, __wsum sum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148{
Joe Perches0883e912008-03-23 01:01:49 -0700149 asm("addl 0(%1), %0 ;\n"
150 "adcl 4(%1), %0 ;\n"
151 "adcl 8(%1), %0 ;\n"
152 "adcl 12(%1), %0 ;\n"
153 "adcl 0(%2), %0 ;\n"
154 "adcl 4(%2), %0 ;\n"
155 "adcl 8(%2), %0 ;\n"
156 "adcl 12(%2), %0 ;\n"
157 "adcl %3, %0 ;\n"
158 "adcl %4, %0 ;\n"
159 "adcl $0, %0 ;\n"
160 : "=&r" (sum)
161 : "r" (saddr), "r" (daddr),
Samuel Thibault392d8142009-10-01 15:44:02 -0700162 "r" (htonl(len)), "r" (htonl(proto)), "0" (sum)
163 : "memory");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164
165 return csum_fold(sum);
166}
167
168/*
169 * Copy and checksum to user
170 */
Joe Perches0883e912008-03-23 01:01:49 -0700171static inline __wsum csum_and_copy_to_user(const void *src,
172 void __user *dst,
Al Viroc693cc42020-07-11 00:27:49 -0400173 int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174{
H. Peter Anvin7263dda2013-08-30 15:43:03 -0700175 __wsum ret;
176
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177 might_sleep();
Al Viroc693cc42020-07-11 00:27:49 -0400178 if (!user_access_begin(dst, len))
179 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180
Al Viroe8b95082020-07-12 23:53:10 -0400181 ret = csum_partial_copy_generic(src, (__force void *)dst, len);
Al Viroc693cc42020-07-11 00:27:49 -0400182 user_access_end();
Al Viroe8b95082020-07-12 23:53:10 -0400183 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184}
185
H. Peter Anvin1965aae2008-10-22 22:26:29 -0700186#endif /* _ASM_X86_CHECKSUM_32_H */