186144Stmm/*-
286144Stmm * Copyright (c) 1990 The Regents of the University of California.
386144Stmm * All rights reserved.
486144Stmm *
586144Stmm * Redistribution and use in source and binary forms, with or without
686144Stmm * modification, are permitted provided that the following conditions
786144Stmm * are met:
886144Stmm * 1. Redistributions of source code must retain the above copyright
986144Stmm *    notice, this list of conditions and the following disclaimer.
1086144Stmm * 2. Redistributions in binary form must reproduce the above copyright
1186144Stmm *    notice, this list of conditions and the following disclaimer in the
1286144Stmm *    documentation and/or other materials provided with the distribution.
1386144Stmm * 4. Neither the name of the University nor the names of its contributors
1486144Stmm *    may be used to endorse or promote products derived from this software
1586144Stmm *    without specific prior written permission.
1686144Stmm *
1786144Stmm * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
1886144Stmm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1986144Stmm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2086144Stmm * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2186144Stmm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2286144Stmm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2386144Stmm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2486144Stmm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2586144Stmm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2686144Stmm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2786144Stmm * SUCH DAMAGE.
2886144Stmm */
2986144Stmm/*-
3086144Stmm * Copyright (c) 2001 by Thomas Moestl <tmm@FreeBSD.org>.
3186144Stmm * All rights reserved.
3286144Stmm *
3386144Stmm * Redistribution and use in source and binary forms, with or without
3486144Stmm * modification, are permitted provided that the following conditions
3586144Stmm * are met:
3686144Stmm * 1. Redistributions of source code must retain the above copyright
3786144Stmm *    notice, this list of conditions and the following disclaimer.
3886144Stmm * 2. Redistributions in binary form must reproduce the above copyright
3986144Stmm *    notice, this list of conditions and the following disclaimer in the
4086144Stmm *    documentation and/or other materials provided with the distribution.
4186144Stmm *
4286144Stmm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
4386144Stmm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
4486144Stmm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
4586144Stmm * IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
4686144Stmm * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
4786144Stmm * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
4886144Stmm * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
4986144Stmm * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
5086144Stmm * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
5186144Stmm * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
5286144Stmm *
5386144Stmm *	from tahoe:	in_cksum.c	1.2	86/01/05
5486144Stmm *	from:		@(#)in_cksum.c	1.3 (Berkeley) 1/19/91
5586144Stmm * 	from: FreeBSD: src/sys/i386/i386/in_cksum.c,v 1.22 2000/11/25
5686144Stmm */
5786144Stmm
58180298Smarius#include <sys/cdefs.h>
59180298Smarius__FBSDID("$FreeBSD: releng/10.3/sys/sparc64/sparc64/in_cksum.c 180299 2008-07-05 15:44:56Z marius $");
60180298Smarius
6186144Stmm#include <sys/param.h>
6286144Stmm#include <sys/systm.h>
6386144Stmm#include <sys/mbuf.h>
6486144Stmm
6586144Stmm#include <netinet/in.h>
6686144Stmm#include <netinet/in_systm.h>
6786144Stmm#include <netinet/ip.h>
6886144Stmm
6986144Stmm#include <machine/in_cksum.h>
7086144Stmm
7186144Stmm/*
7286144Stmm * Checksum routine for Internet Protocol family headers.
7386144Stmm *
7486144Stmm * This routine is very heavily used in the network
7586144Stmm * code and should be modified for each CPU to be as fast as possible.
7686144Stmm *
77180298Smarius * This implementation is a sparc64 version.  Most code was taken over
78180298Smarius * and adapted from the i386.  Some optimizations were changed to achieve
79180298Smarius * (hopefully) better performance.
80180298Smarius * This uses 64-bit loads, but 32-bit additions due to the lack of a 64-bit
8186144Stmm * add-with-carry operation.
8286144Stmm */
8386144Stmm
8486144Stmm/*
8586144Stmm * REDUCE() is actually not used that frequently... maybe a C implementation
8686144Stmm * would suffice.
8786144Stmm */
88180299Smarius#define	REDUCE(sum, tmp) __asm(						\
89180298Smarius	"sll %2, 16, %1\n"						\
90180298Smarius	"addcc %2, %1, %0\n"						\
91180298Smarius	"srl %0, 16, %0\n"						\
92180299Smarius	"addc %0, 0, %0" : "=r" (sum), "=&r" (tmp) : "0" (sum) : "cc")
9386144Stmm
9486144Stmm/*
95180298Smarius * Note that some of these macros depend on the flags being preserved
96180299Smarius * between calls, thus they have to be used within a single __asm().
9786144Stmm */
98180299Smarius#define	LD64_ADD32(n, mod)						\
99180298Smarius	"ldx [%3 + " #n "], %1\n"					\
100180298Smarius	"add" #mod " %2, %1, %0\n"					\
101180298Smarius	"srlx %1, 32, %1\n"						\
102180299Smarius	"addccc %0, %1, %0\n"
10386144Stmm
104180299Smarius#define	LD32_ADD32(n, mod)						\
105180298Smarius	"lduw [%3 + " #n "], %1\n"					\
106180299Smarius	"add" #mod " %2, %1, %0\n"
10786144Stmm
108180299Smarius#define	MOP(sum, tmp, addr)						\
109180299Smarius	"addc %2, 0, %0"						\
110180299Smarius	: "=r" (sum), "=&r" (tmp) : "0" (sum), "r" (addr) : "cc"
11186144Stmm
11286144Stmmu_short
11386144Stmmin_cksum_skip(struct mbuf *m, int len, int skip)
11486144Stmm{
11586144Stmm	u_short *w;
11686144Stmm	unsigned long tmp, sum = 0;
11786144Stmm	int mlen = 0;
11886144Stmm	int byte_swapped = 0;
11986144Stmm	u_short	su = 0;
12086144Stmm
12186144Stmm	len -= skip;
12286144Stmm	for (; skip > 0 && m != NULL; m = m->m_next) {
12386144Stmm		if (m->m_len > skip) {
12486144Stmm			mlen = m->m_len - skip;
12586144Stmm			w = (u_short *)(mtod(m, u_char *) + skip);
12686144Stmm			goto skip_start;
12786144Stmm		} else
12886144Stmm			skip -= m->m_len;
12986144Stmm	}
13086144Stmm
13186144Stmm	for (; m != NULL && len > 0; m = m->m_next) {
13286144Stmm		if (m->m_len == 0)
13386144Stmm			continue;
13486144Stmm		w = mtod(m, u_short *);
13586144Stmm		if (mlen == -1) {
13686144Stmm			/*
13786144Stmm			 * The first byte of this mbuf is the continuation
13886144Stmm			 * of a word spanning between this mbuf and the
13986144Stmm			 * last mbuf.
14086144Stmm			 *
14186144Stmm			 * The high order byte of su is already saved when
14286144Stmm			 * scanning previous mbuf.  sum was REDUCEd when we
14386144Stmm			 * found mlen == -1
14486144Stmm			 */
14586144Stmm			sum += su | *(u_char *)w;
14686144Stmm			w = (u_short *)((u_char *)w + 1);
14786144Stmm			mlen = m->m_len - 1;
14886144Stmm			len--;
14986144Stmm		} else
15086144Stmm			mlen = m->m_len;
15186144Stmmskip_start:
15286144Stmm		if (len < mlen)
15386144Stmm			mlen = len;
15486144Stmm		len -= mlen;
15586144Stmm		/*
15686144Stmm		 * Force to a 8-byte boundary first so that we can use
15786144Stmm		 * LD64_ADD32.
15886144Stmm		 */
15986144Stmm		if (((u_long)w & 7) != 0) {
16086144Stmm			REDUCE(sum, tmp);
16186144Stmm			if (((u_long)w & 1) != 0 && mlen >= 1) {
16286144Stmm				sum <<= 8;
16386144Stmm				su = *(u_char *)w << 8;
16486144Stmm				w = (u_short *)((u_char *)w + 1);
16586144Stmm				mlen--;
16686144Stmm				byte_swapped = 1;
16786144Stmm			}
16886144Stmm			if (((u_long)w & 2) != 0 && mlen >= 2) {
16986144Stmm				sum += *w++;
17086144Stmm				mlen -= 2;
17186144Stmm			}
17286144Stmm			if (((u_long)w & 4) != 0 && mlen >= 4) {
173180299Smarius				__asm(
174180299Smarius				    LD32_ADD32(0, cc)
175180299Smarius				    MOP(sum, tmp, w)
176180299Smarius				);
17786144Stmm				w += 2;
17886144Stmm				mlen -= 4;
17986144Stmm			}
18086144Stmm		}
18186144Stmm		/*
18286144Stmm		 * Do as much of the checksum as possible 64 bits at at time.
18386144Stmm		 * In fact, this loop is unrolled to make overhead from
18486144Stmm		 * branches &c small.
18586144Stmm		 */
18686144Stmm		for (; mlen >= 64; mlen -= 64) {
187180299Smarius			__asm(
188180299Smarius			    LD64_ADD32(0, cc)
189180299Smarius			    LD64_ADD32(8, ccc)
190180299Smarius			    LD64_ADD32(16, ccc)
191180299Smarius			    LD64_ADD32(24, ccc)
192180299Smarius			    LD64_ADD32(32, ccc)
193180299Smarius			    LD64_ADD32(40, ccc)
194180299Smarius			    LD64_ADD32(48, ccc)
195180299Smarius			    LD64_ADD32(56, ccc)
196180299Smarius			    MOP(sum, tmp, w)
197180299Smarius			);
19886144Stmm			w += 32;
19986144Stmm		}
20086144Stmm		if (mlen >= 32) {
201180299Smarius			__asm(
202180299Smarius			    LD64_ADD32(0, cc)
203180299Smarius			    LD64_ADD32(8, ccc)
204180299Smarius			    LD64_ADD32(16, ccc)
205180299Smarius			    LD64_ADD32(24, ccc)
206180299Smarius			    MOP(sum, tmp, w)
207180299Smarius			);
20886144Stmm			w += 16;
20986144Stmm			mlen -= 32;
21086144Stmm		}
21186144Stmm		if (mlen >= 16) {
212180299Smarius			__asm(
213180299Smarius			    LD64_ADD32(0, cc)
214180299Smarius			    LD64_ADD32(8, ccc)
215180299Smarius			    MOP(sum, tmp, w)
216180299Smarius			);
21786144Stmm			w += 8;
21886144Stmm			mlen -= 16;
21986144Stmm		}
22086144Stmm		if (mlen >= 8) {
221180299Smarius			__asm(
222180299Smarius			    LD64_ADD32(0, cc)
223180299Smarius			    MOP(sum, tmp, w)
224180299Smarius			);
22586144Stmm			w += 4;
22686144Stmm			mlen -= 8;
22786144Stmm		}
22886144Stmm		REDUCE(sum, tmp);
22986144Stmm		while ((mlen -= 2) >= 0)
23086144Stmm			sum += *w++;
23186144Stmm		if (byte_swapped) {
23286144Stmm			sum <<= 8;
23386144Stmm			byte_swapped = 0;
23486144Stmm			if (mlen == -1) {
23586144Stmm				su |= *(u_char *)w;
23686144Stmm				sum += su;
23786144Stmm				mlen = 0;
23886144Stmm			} else
23986144Stmm				mlen = -1;
24086144Stmm		} else if (mlen == -1) {
24186144Stmm			/*
24286144Stmm			 * This mbuf has odd number of bytes.
243180298Smarius			 * There could be a word split between
24486144Stmm			 * this mbuf and the next mbuf.
24586144Stmm			 * Save the last byte (to prepend to next mbuf).
24686144Stmm			 */
24786144Stmm			su = *(u_char *)w << 8;
24886144Stmm		}
24986144Stmm	}
25086144Stmm
25186144Stmm	if (len)
25286144Stmm		printf("%s: out of data by %d\n", __func__, len);
25386144Stmm	if (mlen == -1) {
254180298Smarius		/*
255180298Smarius		 * The last mbuf has odd # of bytes.  Follow the
256180298Smarius		 * standard (the odd byte is shifted left by 8 bits).
257180298Smarius		 */
25886144Stmm		sum += su & 0xff00;
25986144Stmm	}
26086144Stmm	REDUCE(sum, tmp);
26186144Stmm	return (~sum & 0xffff);
26286144Stmm}
263