root/usr/src/uts/common/inet/ip/ipcsum.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright (c) 1992,1997 by Sun Microsystems, Inc.
 * All rights reserved.
 */
/* Copyright (c) 1990 Mentat Inc. */

#include <sys/types.h>
#include <sys/stream.h>
#include <sys/ddi.h>
#include <sys/isa_defs.h>
#include <inet/common.h>

#define FOLD_SUM(sum) \
{ sum = (sum >> 16) + (sum & 0xFFFF); sum = (sum >> 16) + (sum & 0xFFFF); }
#define U16AM(p, i, m)  ((((uint16_t *)(p))[i]) & (uint32_t)(m))

/*
 * For maximum efficiency, these access macros should be redone for
 * machines that can access unaligned data.  NOTE: these assume
 * ability to fetch from a zero extended 'uint8_t' and 'uint16_t'.  Add explicit
 * masks in the U8_FETCH, U16_FETCH, PREV_TWO and NEXT_TWO as needed.
 */

#ifdef  _LITTLE_ENDIAN
#define U8_FETCH_FIRST(p)       ((p)[0])
#define U8_FETCH_SECOND(p)      (((uint32_t)(p)[0]) << 8)
#define PREV_ONE(p)             U16AM(p, -1, 0xFF00)
#define NEXT_ONE(p)             U16AM(p, 0, 0xFF)
#else
#define U8_FETCH_FIRST(p)       ((uint32_t)((p)[0]) << 8)
#define U8_FETCH_SECOND(p)      ((p)[0])
#define PREV_ONE(p)             U16AM(p, -1, 0xFF)
#define NEXT_ONE(p)             U16AM(p, 0, 0xFF00)
#endif

#define U16_FETCH(p)            U8_FETCH_FIRST(p) + U8_FETCH_SECOND(p+1)
#define PREV_TWO(p)             ((uint32_t)(((uint16_t *)(p))[-1]))
#define NEXT_TWO(p)             ((uint32_t)(((uint16_t *)(p))[0]))

/*
 * Return the ones complement checksum from the mblk chain at mp,
 * after skipping offset bytes, and adding in the supplied partial
 * sum.  Note that a final complement of the return value is needed
 * if no further contributions to the checksum are forthcoming.
 */
uint16_t
ip_csum(mp, offset, sum)
        mblk_t *mp;
        int     offset;
        uint32_t        sum;
{
        uint8_t *startp = mp->b_rptr + offset;
        uint8_t *endp = mp->b_wptr;
/* >= 0x2 means flipped for memory align, 0x1 means last count was odd */
        int     odd_total = 0;

#ifdef  TEST_COVERAGE
        mblk_t *safe_mp;
#define INIT_COVERAGE() (safe_mp = mp, safe_mp->b_next = NULL)
#define MARK_COVERAGE(flag) (safe_mp->b_next = \
        (mblk_t *)((uint32_t)safe_mp->b_next | flag))
#else
#define INIT_COVERAGE() /* */
#define MARK_COVERAGE(flag)     /* */
#endif

        for (;;) {
                INIT_COVERAGE();
                if ((endp - startp) < 10) {
                        MARK_COVERAGE(0x1);
                        while ((endp - startp) >= 2) {
                                MARK_COVERAGE(0x2);
                                sum += U16_FETCH(startp);
                                startp += 2;
                        }
                        if ((endp - startp) >= 1) {
                                MARK_COVERAGE(0x4);
                                odd_total = 1;
                                sum += U8_FETCH_FIRST(startp);
                        }
                        MARK_COVERAGE(0x8);
                        FOLD_SUM(sum);
                        goto next_frag;
                }
                if ((uint32_t)startp & 0x1) {
                        MARK_COVERAGE(0x10);
                        odd_total = 3;
                        startp++;
                        sum = (sum << 8) + PREV_ONE(startp);
                }
                if ((uint32_t)startp & 0x2) {
                        MARK_COVERAGE(0x20);
                        startp += 2;
                        sum += PREV_TWO(startp);
                }
                if ((uint32_t)endp & 0x1) {
                        MARK_COVERAGE(0x40);
                        odd_total ^= 0x1;
                        endp--;
                        sum += NEXT_ONE(endp);
                }
                if ((uint32_t)endp & 0x2) {
                        MARK_COVERAGE(0x80);
                        endp -= 2;
                        sum += NEXT_TWO(endp);
                }

                {
#ifdef  NOT_ALL_PTRS_EQUAL
#define INC_PTR(cnt)    ptr += cnt
#define INC_ENDPTR(cnt) endptr += cnt
                        uint32_t        *ptr = (uint32_t *)startp;
                        uint32_t        *endptr = (uint32_t *)endp;
#else
#define INC_PTR(cnt)    startp += (cnt * sizeof (uint32_t))
#define INC_ENDPTR(cnt) endp += (cnt * sizeof (uint32_t))
#define ptr             ((uint32_t *)startp)
#define endptr          ((uint32_t *)endp)
#endif


#ifdef  USE_FETCH_AND_SHIFT
                        uint32_t        u1, u2;
                        uint32_t        mask = 0xFFFF;
#define LOAD1(i)        u1 = ptr[i]
#define LOAD2(i)        u2 = ptr[i]
#define SUM1(i)         sum += (u1 & mask) + (u1 >> 16)
#define SUM2(i)         sum += (u2 & mask) + (u2 >> 16)
#endif

#ifdef  USE_FETCH_AND_ADDC
                        uint32_t        u1, u2;
#define LOAD1(i)        u1 = ptr[i]
#define LOAD2(i)        u2 = ptr[i]
#define SUM1(i)         sum += u1
#define SUM2(i)         sum += u2
#endif

#ifdef  USE_ADDC
#define SUM1(i)         sum += ptr[i]
#endif

#ifdef  USE_POSTINC
#define SUM1(i)         sum += *((uint16_t *)ptr)++; sum += *((uint16_t *)ptr)++
#undef  INC_PTR
#define INC_PTR(i)      /* */
#endif

#ifndef LOAD1
#define LOAD1(i)        /* */
#endif

#ifndef LOAD2
#define LOAD2(i)        /* */
#endif

#ifndef SUM2
#define SUM2(i)         SUM1(i)
#endif

/* USE_INDEXING is the default */
#ifndef SUM1
#define SUM1(i)
        sum += ((uint16_t *)ptr)[i * 2]; sum += ((uint16_t *)ptr)[(i * 2) + 1]
#endif

                LOAD1(0);
                INC_ENDPTR(-8);
                if (ptr <= endptr) {
                        MARK_COVERAGE(0x100);
                        do {
                                LOAD2(1); SUM1(0);
                                LOAD1(2); SUM2(1);
                                LOAD2(3); SUM1(2);
                                LOAD1(4); SUM2(3);
                                LOAD2(5); SUM1(4);
                                LOAD1(6); SUM2(5);
                                LOAD2(7); SUM1(6);
                                LOAD1(8); SUM2(7);
                                INC_PTR(8);
                        } while (ptr <= endptr);
                }
#ifdef USE_TAIL_SWITCH
                switch ((endptr + 8) - ptr) {
                case 7: LOAD2(6); SUM2(6);
                case 6: LOAD2(5); SUM2(5);
                case 5: LOAD2(4); SUM2(4);
                case 4: LOAD2(3); SUM2(3);
                case 3: LOAD2(2); SUM2(2);
                case 2: LOAD2(1); SUM2(1);
                case 1: SUM1(0);
                case 0: break;
                }
#else
                INC_ENDPTR(4);
                if (ptr <= endptr) {
                        MARK_COVERAGE(0x200);
                        LOAD2(1); SUM1(0);
                        LOAD1(2); SUM2(1);
                        LOAD2(3); SUM1(2);
                        LOAD1(4); SUM2(3);
                        INC_PTR(4);
                }
                INC_ENDPTR(4);
                if (ptr < endptr) {
                        MARK_COVERAGE(0x400);
                        do {
                                SUM1(0); LOAD1(1);
                                INC_PTR(1);
                        } while (ptr < endptr);
                }
#endif
                }

                FOLD_SUM(sum);
                if (odd_total > 1) {
                        MARK_COVERAGE(0x800);
                        sum = ((sum << 8) | (sum >> 8)) & 0xFFFF;
                        odd_total -= 2;
                }
next_frag:
                mp = mp->b_cont;
                if (!mp) {
                        MARK_COVERAGE(0x1000);
                        {
                        uint32_t        u1 = sum;
                        return ((uint16_t)u1);
                        }
                }
                MARK_COVERAGE(0x4000);
                startp = mp->b_rptr;
                endp = mp->b_wptr;
                if (odd_total && (endp > startp)) {
                        MARK_COVERAGE(0x8000);
                        odd_total = 0;
                        sum += U8_FETCH_SECOND(startp);
                        startp++;
                }
        }
}
#undef  endptr
#undef  INIT_COVERAGE
#undef  INC_PTR
#undef  INC_ENDPTR
#undef  LOAD1
#undef  LOAD2
#undef  MARK_COVERAGE
#undef  ptr
#undef  SUM1
#undef  SUM2



#undef  FOLD_SUM
#undef  NEXT_ONE
#undef  NEXT_TWO
#undef  PREV_ONE
#undef  PREV_TWO
#undef  U8_FETCH_FIRST
#undef  U8_FETCH_SECOND
#undef  U16AM
#undef  U16_FETCH