xref: /kernel/linux/linux-6.6/arch/x86/um/asm/checksum.h (revision 62306a36)
162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
262306a36Sopenharmony_ci#ifndef __UM_CHECKSUM_H
362306a36Sopenharmony_ci#define __UM_CHECKSUM_H
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci#include <linux/string.h>
662306a36Sopenharmony_ci#include <linux/in6.h>
762306a36Sopenharmony_ci#include <linux/uaccess.h>
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci/*
1062306a36Sopenharmony_ci * computes the checksum of a memory block at buff, length len,
1162306a36Sopenharmony_ci * and adds in "sum" (32-bit)
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci * returns a 32-bit number suitable for feeding into itself
1462306a36Sopenharmony_ci * or csum_tcpudp_magic
1562306a36Sopenharmony_ci *
1662306a36Sopenharmony_ci * this function must be called with even lengths, except
1762306a36Sopenharmony_ci * for the last fragment, which may be odd
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci * it's best to have buff aligned on a 32-bit boundary
2062306a36Sopenharmony_ci */
2162306a36Sopenharmony_ciextern __wsum csum_partial(const void *buff, int len, __wsum sum);
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/**
2462306a36Sopenharmony_ci * csum_fold - Fold and invert a 32bit checksum.
2562306a36Sopenharmony_ci * sum: 32bit unfolded sum
2662306a36Sopenharmony_ci *
2762306a36Sopenharmony_ci * Fold a 32bit running checksum to 16bit and invert it. This is usually
2862306a36Sopenharmony_ci * the last step before putting a checksum into a packet.
2962306a36Sopenharmony_ci * Make sure not to mix with 64bit checksums.
3062306a36Sopenharmony_ci */
3162306a36Sopenharmony_cistatic inline __sum16 csum_fold(__wsum sum)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	__asm__(
3462306a36Sopenharmony_ci		"  addl %1,%0\n"
3562306a36Sopenharmony_ci		"  adcl $0xffff,%0"
3662306a36Sopenharmony_ci		: "=r" (sum)
3762306a36Sopenharmony_ci		: "r" ((__force u32)sum << 16),
3862306a36Sopenharmony_ci		  "0" ((__force u32)sum & 0xffff0000)
3962306a36Sopenharmony_ci	);
4062306a36Sopenharmony_ci	return (__force __sum16)(~(__force u32)sum >> 16);
4162306a36Sopenharmony_ci}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci/**
4462306a36Sopenharmony_ci * csum_tcpup_nofold - Compute an IPv4 pseudo header checksum.
4562306a36Sopenharmony_ci * @saddr: source address
4662306a36Sopenharmony_ci * @daddr: destination address
4762306a36Sopenharmony_ci * @len: length of packet
4862306a36Sopenharmony_ci * @proto: ip protocol of packet
4962306a36Sopenharmony_ci * @sum: initial sum to be added in (32bit unfolded)
5062306a36Sopenharmony_ci *
5162306a36Sopenharmony_ci * Returns the pseudo header checksum the input data. Result is
5262306a36Sopenharmony_ci * 32bit unfolded.
5362306a36Sopenharmony_ci */
5462306a36Sopenharmony_cistatic inline __wsum
5562306a36Sopenharmony_cicsum_tcpudp_nofold(__be32 saddr, __be32 daddr, __u32 len,
5662306a36Sopenharmony_ci		  __u8 proto, __wsum sum)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	asm("  addl %1, %0\n"
5962306a36Sopenharmony_ci	    "  adcl %2, %0\n"
6062306a36Sopenharmony_ci	    "  adcl %3, %0\n"
6162306a36Sopenharmony_ci	    "  adcl $0, %0\n"
6262306a36Sopenharmony_ci		: "=r" (sum)
6362306a36Sopenharmony_ci	    : "g" (daddr), "g" (saddr), "g" ((len + proto) << 8), "0" (sum));
6462306a36Sopenharmony_ci	return sum;
6562306a36Sopenharmony_ci}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci/*
6862306a36Sopenharmony_ci * computes the checksum of the TCP/UDP pseudo-header
6962306a36Sopenharmony_ci * returns a 16-bit checksum, already complemented
7062306a36Sopenharmony_ci */
7162306a36Sopenharmony_cistatic inline __sum16 csum_tcpudp_magic(__be32 saddr, __be32 daddr,
7262306a36Sopenharmony_ci					__u32 len, __u8 proto,
7362306a36Sopenharmony_ci					__wsum sum)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	return csum_fold(csum_tcpudp_nofold(saddr,daddr,len,proto,sum));
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci/**
7962306a36Sopenharmony_ci * ip_fast_csum - Compute the IPv4 header checksum efficiently.
8062306a36Sopenharmony_ci * iph: ipv4 header
8162306a36Sopenharmony_ci * ihl: length of header / 4
8262306a36Sopenharmony_ci */
8362306a36Sopenharmony_cistatic inline __sum16 ip_fast_csum(const void *iph, unsigned int ihl)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	unsigned int sum;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	asm(	"  movl (%1), %0\n"
8862306a36Sopenharmony_ci		"  subl $4, %2\n"
8962306a36Sopenharmony_ci		"  jbe 2f\n"
9062306a36Sopenharmony_ci		"  addl 4(%1), %0\n"
9162306a36Sopenharmony_ci		"  adcl 8(%1), %0\n"
9262306a36Sopenharmony_ci		"  adcl 12(%1), %0\n"
9362306a36Sopenharmony_ci		"1: adcl 16(%1), %0\n"
9462306a36Sopenharmony_ci		"  lea 4(%1), %1\n"
9562306a36Sopenharmony_ci		"  decl %2\n"
9662306a36Sopenharmony_ci		"  jne	1b\n"
9762306a36Sopenharmony_ci		"  adcl $0, %0\n"
9862306a36Sopenharmony_ci		"  movl %0, %2\n"
9962306a36Sopenharmony_ci		"  shrl $16, %0\n"
10062306a36Sopenharmony_ci		"  addw %w2, %w0\n"
10162306a36Sopenharmony_ci		"  adcl $0, %0\n"
10262306a36Sopenharmony_ci		"  notl %0\n"
10362306a36Sopenharmony_ci		"2:"
10462306a36Sopenharmony_ci	/* Since the input registers which are loaded with iph and ipl
10562306a36Sopenharmony_ci	   are modified, we must also specify them as outputs, or gcc
10662306a36Sopenharmony_ci	   will assume they contain their original values. */
10762306a36Sopenharmony_ci	: "=r" (sum), "=r" (iph), "=r" (ihl)
10862306a36Sopenharmony_ci	: "1" (iph), "2" (ihl)
10962306a36Sopenharmony_ci	: "memory");
11062306a36Sopenharmony_ci	return (__force __sum16)sum;
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci#ifdef CONFIG_X86_32
11462306a36Sopenharmony_ci# include "checksum_32.h"
11562306a36Sopenharmony_ci#else
11662306a36Sopenharmony_ci# include "checksum_64.h"
11762306a36Sopenharmony_ci#endif
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci#endif
120