18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */ 28c2ecf20Sopenharmony_ci#ifndef _ASM_X86_CHECKSUM_32_H 38c2ecf20Sopenharmony_ci#define _ASM_X86_CHECKSUM_32_H 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ci#include <linux/in6.h> 68c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci/* 98c2ecf20Sopenharmony_ci * computes the checksum of a memory block at buff, length len, 108c2ecf20Sopenharmony_ci * and adds in "sum" (32-bit) 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * returns a 32-bit number suitable for feeding into itself 138c2ecf20Sopenharmony_ci * or csum_tcpudp_magic 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * this function must be called with even lengths, except 168c2ecf20Sopenharmony_ci * for the last fragment, which may be odd 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * it's best to have buff aligned on a 32-bit boundary 198c2ecf20Sopenharmony_ci */ 208c2ecf20Sopenharmony_ciasmlinkage __wsum csum_partial(const void *buff, int len, __wsum sum); 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* 238c2ecf20Sopenharmony_ci * the same as csum_partial, but copies from src while it 248c2ecf20Sopenharmony_ci * checksums, and handles user-space pointer exceptions correctly, when needed. 258c2ecf20Sopenharmony_ci * 268c2ecf20Sopenharmony_ci * here even more important to align src and dst on a 32-bit (or even 278c2ecf20Sopenharmony_ci * better 64-bit) boundary 288c2ecf20Sopenharmony_ci */ 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ciasmlinkage __wsum csum_partial_copy_generic(const void *src, void *dst, int len); 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/* 338c2ecf20Sopenharmony_ci * Note: when you get a NULL pointer exception here this means someone 348c2ecf20Sopenharmony_ci * passed in an incorrect kernel address to one of these functions. 358c2ecf20Sopenharmony_ci * 368c2ecf20Sopenharmony_ci * If you use these functions directly please don't forget the 378c2ecf20Sopenharmony_ci * access_ok(). 388c2ecf20Sopenharmony_ci */ 398c2ecf20Sopenharmony_cistatic inline __wsum csum_partial_copy_nocheck(const void *src, void *dst, int len) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci return csum_partial_copy_generic(src, dst, len); 428c2ecf20Sopenharmony_ci} 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic inline __wsum csum_and_copy_from_user(const void __user *src, 458c2ecf20Sopenharmony_ci void *dst, int len) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci __wsum ret; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci might_sleep(); 508c2ecf20Sopenharmony_ci if (!user_access_begin(src, len)) 518c2ecf20Sopenharmony_ci return 0; 528c2ecf20Sopenharmony_ci ret = csum_partial_copy_generic((__force void *)src, dst, len); 538c2ecf20Sopenharmony_ci user_access_end(); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci return ret; 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/* 598c2ecf20Sopenharmony_ci * This is a version of ip_compute_csum() optimized for IP headers, 608c2ecf20Sopenharmony_ci * which always checksum on 4 octet boundaries. 618c2ecf20Sopenharmony_ci * 628c2ecf20Sopenharmony_ci * By Jorge Cwik <jorge@laser.satlink.net>, adapted for linux by 638c2ecf20Sopenharmony_ci * Arnt Gulbrandsen. 648c2ecf20Sopenharmony_ci */ 658c2ecf20Sopenharmony_cistatic inline __sum16 ip_fast_csum(const void *iph, unsigned int ihl) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci unsigned int sum; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci asm volatile("movl (%1), %0 ;\n" 708c2ecf20Sopenharmony_ci "subl $4, %2 ;\n" 718c2ecf20Sopenharmony_ci "jbe 2f ;\n" 728c2ecf20Sopenharmony_ci "addl 4(%1), %0 ;\n" 738c2ecf20Sopenharmony_ci "adcl 8(%1), %0 ;\n" 748c2ecf20Sopenharmony_ci "adcl 12(%1), %0;\n" 758c2ecf20Sopenharmony_ci "1: adcl 16(%1), %0 ;\n" 768c2ecf20Sopenharmony_ci "lea 4(%1), %1 ;\n" 778c2ecf20Sopenharmony_ci "decl %2 ;\n" 788c2ecf20Sopenharmony_ci "jne 1b ;\n" 798c2ecf20Sopenharmony_ci "adcl $0, %0 ;\n" 808c2ecf20Sopenharmony_ci "movl %0, %2 ;\n" 818c2ecf20Sopenharmony_ci "shrl $16, %0 ;\n" 828c2ecf20Sopenharmony_ci "addw %w2, %w0 ;\n" 838c2ecf20Sopenharmony_ci "adcl $0, %0 ;\n" 848c2ecf20Sopenharmony_ci "notl %0 ;\n" 858c2ecf20Sopenharmony_ci "2: ;\n" 868c2ecf20Sopenharmony_ci /* Since the input registers which are loaded with iph and ihl 878c2ecf20Sopenharmony_ci are modified, we must also specify them as outputs, or gcc 888c2ecf20Sopenharmony_ci will assume they contain their original values. */ 898c2ecf20Sopenharmony_ci : "=r" (sum), "=r" (iph), "=r" (ihl) 908c2ecf20Sopenharmony_ci : "1" (iph), "2" (ihl) 918c2ecf20Sopenharmony_ci : "memory"); 928c2ecf20Sopenharmony_ci return (__force __sum16)sum; 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci/* 968c2ecf20Sopenharmony_ci * Fold a partial checksum 978c2ecf20Sopenharmony_ci */ 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic inline __sum16 csum_fold(__wsum sum) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci asm("addl %1, %0 ;\n" 1028c2ecf20Sopenharmony_ci "adcl $0xffff, %0 ;\n" 1038c2ecf20Sopenharmony_ci : "=r" (sum) 1048c2ecf20Sopenharmony_ci : "r" ((__force u32)sum << 16), 1058c2ecf20Sopenharmony_ci "0" ((__force u32)sum & 0xffff0000)); 1068c2ecf20Sopenharmony_ci return (__force __sum16)(~(__force u32)sum >> 16); 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic inline __wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr, 1108c2ecf20Sopenharmony_ci __u32 len, __u8 proto, 1118c2ecf20Sopenharmony_ci __wsum sum) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci asm("addl %1, %0 ;\n" 1148c2ecf20Sopenharmony_ci "adcl %2, %0 ;\n" 1158c2ecf20Sopenharmony_ci "adcl %3, %0 ;\n" 1168c2ecf20Sopenharmony_ci "adcl $0, %0 ;\n" 1178c2ecf20Sopenharmony_ci : "=r" (sum) 1188c2ecf20Sopenharmony_ci : "g" (daddr), "g"(saddr), 1198c2ecf20Sopenharmony_ci "g" ((len + proto) << 8), "0" (sum)); 1208c2ecf20Sopenharmony_ci return sum; 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci/* 1248c2ecf20Sopenharmony_ci * computes the checksum of the TCP/UDP pseudo-header 1258c2ecf20Sopenharmony_ci * returns a 16-bit checksum, already complemented 1268c2ecf20Sopenharmony_ci */ 1278c2ecf20Sopenharmony_cistatic inline __sum16 csum_tcpudp_magic(__be32 saddr, __be32 daddr, 1288c2ecf20Sopenharmony_ci __u32 len, __u8 proto, 1298c2ecf20Sopenharmony_ci __wsum sum) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci return csum_fold(csum_tcpudp_nofold(saddr, daddr, len, proto, sum)); 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci/* 1358c2ecf20Sopenharmony_ci * this routine is used for miscellaneous IP-like checksums, mainly 1368c2ecf20Sopenharmony_ci * in icmp.c 1378c2ecf20Sopenharmony_ci */ 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic inline __sum16 ip_compute_csum(const void *buff, int len) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci return csum_fold(csum_partial(buff, len, 0)); 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci#define _HAVE_ARCH_IPV6_CSUM 1458c2ecf20Sopenharmony_cistatic inline __sum16 csum_ipv6_magic(const struct in6_addr *saddr, 1468c2ecf20Sopenharmony_ci const struct in6_addr *daddr, 1478c2ecf20Sopenharmony_ci __u32 len, __u8 proto, __wsum sum) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci asm("addl 0(%1), %0 ;\n" 1508c2ecf20Sopenharmony_ci "adcl 4(%1), %0 ;\n" 1518c2ecf20Sopenharmony_ci "adcl 8(%1), %0 ;\n" 1528c2ecf20Sopenharmony_ci "adcl 12(%1), %0 ;\n" 1538c2ecf20Sopenharmony_ci "adcl 0(%2), %0 ;\n" 1548c2ecf20Sopenharmony_ci "adcl 4(%2), %0 ;\n" 1558c2ecf20Sopenharmony_ci "adcl 8(%2), %0 ;\n" 1568c2ecf20Sopenharmony_ci "adcl 12(%2), %0 ;\n" 1578c2ecf20Sopenharmony_ci "adcl %3, %0 ;\n" 1588c2ecf20Sopenharmony_ci "adcl %4, %0 ;\n" 1598c2ecf20Sopenharmony_ci "adcl $0, %0 ;\n" 1608c2ecf20Sopenharmony_ci : "=&r" (sum) 1618c2ecf20Sopenharmony_ci : "r" (saddr), "r" (daddr), 1628c2ecf20Sopenharmony_ci "r" (htonl(len)), "r" (htonl(proto)), "0" (sum) 1638c2ecf20Sopenharmony_ci : "memory"); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci return csum_fold(sum); 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci/* 1698c2ecf20Sopenharmony_ci * Copy and checksum to user 1708c2ecf20Sopenharmony_ci */ 1718c2ecf20Sopenharmony_cistatic inline __wsum csum_and_copy_to_user(const void *src, 1728c2ecf20Sopenharmony_ci void __user *dst, 1738c2ecf20Sopenharmony_ci int len) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci __wsum ret; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci might_sleep(); 1788c2ecf20Sopenharmony_ci if (!user_access_begin(dst, len)) 1798c2ecf20Sopenharmony_ci return 0; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci ret = csum_partial_copy_generic(src, (__force void *)dst, len); 1828c2ecf20Sopenharmony_ci user_access_end(); 1838c2ecf20Sopenharmony_ci return ret; 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci#endif /* _ASM_X86_CHECKSUM_32_H */ 187