1/* SPDX-License-Identifier: GPL-2.0 */
2/*
3 *    S390 fast network checksum routines
4 *
5 *  S390 version
6 *    Copyright IBM Corp. 1999
7 *    Author(s): Ulrich Hild        (first version)
8 *               Martin Schwidefsky (heavily optimized CKSM version)
9 *               D.J. Barrow        (third attempt)
10 */
11
12#ifndef _S390_CHECKSUM_H
13#define _S390_CHECKSUM_H
14
15#include <linux/uaccess.h>
16#include <linux/in6.h>
17
18/*
19 * Computes the checksum of a memory block at buff, length len,
20 * and adds in "sum" (32-bit).
21 *
22 * Returns a 32-bit number suitable for feeding into itself
23 * or csum_tcpudp_magic.
24 *
25 * This function must be called with even lengths, except
26 * for the last fragment, which may be odd.
27 *
28 * It's best to have buff aligned on a 32-bit boundary.
29 */
30static inline __wsum csum_partial(const void *buff, int len, __wsum sum)
31{
32	register unsigned long reg2 asm("2") = (unsigned long) buff;
33	register unsigned long reg3 asm("3") = (unsigned long) len;
34
35	asm volatile(
36		"0:	cksm	%0,%1\n"	/* do checksum on longs */
37		"	jo	0b\n"
38		: "+d" (sum), "+d" (reg2), "+d" (reg3) : : "cc", "memory");
39	return sum;
40}
41
42/*
43 * Fold a partial checksum without adding pseudo headers.
44 */
45static inline __sum16 csum_fold(__wsum sum)
46{
47	u32 csum = (__force u32) sum;
48
49	csum += (csum >> 16) | (csum << 16);
50	csum >>= 16;
51	return (__force __sum16) ~csum;
52}
53
54/*
55 * This is a version of ip_compute_csum() optimized for IP headers,
56 * which always checksums on 4 octet boundaries.
57 */
58static inline __sum16 ip_fast_csum(const void *iph, unsigned int ihl)
59{
60	__u64 csum = 0;
61	__u32 *ptr = (u32 *)iph;
62
63	csum += *ptr++;
64	csum += *ptr++;
65	csum += *ptr++;
66	csum += *ptr++;
67	ihl -= 4;
68	while (ihl--)
69		csum += *ptr++;
70	csum += (csum >> 32) | (csum << 32);
71	return csum_fold((__force __wsum)(csum >> 32));
72}
73
74/*
75 * Computes the checksum of the TCP/UDP pseudo-header.
76 * Returns a 32-bit checksum.
77 */
78static inline __wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr, __u32 len,
79					__u8 proto, __wsum sum)
80{
81	__u64 csum = (__force __u64)sum;
82
83	csum += (__force __u32)saddr;
84	csum += (__force __u32)daddr;
85	csum += len;
86	csum += proto;
87	csum += (csum >> 32) | (csum << 32);
88	return (__force __wsum)(csum >> 32);
89}
90
91/*
92 * Computes the checksum of the TCP/UDP pseudo-header.
93 * Returns a 16-bit checksum, already complemented.
94 */
95static inline __sum16 csum_tcpudp_magic(__be32 saddr, __be32 daddr, __u32 len,
96					__u8 proto, __wsum sum)
97{
98	return csum_fold(csum_tcpudp_nofold(saddr, daddr, len, proto, sum));
99}
100
101/*
102 * Used for miscellaneous IP-like checksums, mainly icmp.
103 */
104static inline __sum16 ip_compute_csum(const void *buff, int len)
105{
106	return csum_fold(csum_partial(buff, len, 0));
107}
108
109#define _HAVE_ARCH_IPV6_CSUM
110static inline __sum16 csum_ipv6_magic(const struct in6_addr *saddr,
111				      const struct in6_addr *daddr,
112				      __u32 len, __u8 proto, __wsum csum)
113{
114	__u64 sum = (__force __u64)csum;
115
116	sum += (__force __u32)saddr->s6_addr32[0];
117	sum += (__force __u32)saddr->s6_addr32[1];
118	sum += (__force __u32)saddr->s6_addr32[2];
119	sum += (__force __u32)saddr->s6_addr32[3];
120	sum += (__force __u32)daddr->s6_addr32[0];
121	sum += (__force __u32)daddr->s6_addr32[1];
122	sum += (__force __u32)daddr->s6_addr32[2];
123	sum += (__force __u32)daddr->s6_addr32[3];
124	sum += len;
125	sum += proto;
126	sum += (sum >> 32) | (sum << 32);
127	return csum_fold((__force __wsum)(sum >> 32));
128}
129
130#endif /* _S390_CHECKSUM_H */
131