linux/arch/ia64/lib/csum_partial_copy.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Network Checksum & Copy routine
   4 *
   5 * Copyright (C) 1999, 2003-2004 Hewlett-Packard Co
   6 *      Stephane Eranian <eranian@hpl.hp.com>
   7 *
   8 * Most of the code has been imported from Linux/Alpha
   9 */
  10
  11#include <linux/module.h>
  12#include <linux/types.h>
  13#include <linux/string.h>
  14
  15#include <linux/uaccess.h>
  16
  17/*
  18 * XXX Fixme: those 2 inlines are meant for debugging and will go away
  19 */
  20static inline unsigned
  21short from64to16(unsigned long x)
  22{
  23        /* add up 32-bit words for 33 bits */
  24        x = (x & 0xffffffff) + (x >> 32);
  25        /* add up 16-bit and 17-bit words for 17+c bits */
  26        x = (x & 0xffff) + (x >> 16);
  27        /* add up 16-bit and 2-bit for 16+c bit */
  28        x = (x & 0xffff) + (x >> 16);
  29        /* add up carry.. */
  30        x = (x & 0xffff) + (x >> 16);
  31        return x;
  32}
  33
  34static inline
  35unsigned long do_csum_c(const unsigned char * buff, int len, unsigned int psum)
  36{
  37        int odd, count;
  38        unsigned long result = (unsigned long)psum;
  39
  40        if (len <= 0)
  41                goto out;
  42        odd = 1 & (unsigned long) buff;
  43        if (odd) {
  44                result = *buff << 8;
  45                len--;
  46                buff++;
  47        }
  48        count = len >> 1;               /* nr of 16-bit words.. */
  49        if (count) {
  50                if (2 & (unsigned long) buff) {
  51                        result += *(unsigned short *) buff;
  52                        count--;
  53                        len -= 2;
  54                        buff += 2;
  55                }
  56                count >>= 1;            /* nr of 32-bit words.. */
  57                if (count) {
  58                        if (4 & (unsigned long) buff) {
  59                                result += *(unsigned int *) buff;
  60                                count--;
  61                                len -= 4;
  62                                buff += 4;
  63                        }
  64                        count >>= 1;    /* nr of 64-bit words.. */
  65                        if (count) {
  66                                unsigned long carry = 0;
  67                                do {
  68                                        unsigned long w = *(unsigned long *) buff;
  69                                        count--;
  70                                        buff += 8;
  71                                        result += carry;
  72                                        result += w;
  73                                        carry = (w > result);
  74                                } while (count);
  75                                result += carry;
  76                                result = (result & 0xffffffff) + (result >> 32);
  77                        }
  78                        if (len & 4) {
  79                                result += *(unsigned int *) buff;
  80                                buff += 4;
  81                        }
  82                }
  83                if (len & 2) {
  84                        result += *(unsigned short *) buff;
  85                        buff += 2;
  86                }
  87        }
  88        if (len & 1)
  89                result += *buff;
  90
  91        result = from64to16(result);
  92
  93        if (odd)
  94                result = ((result >> 8) & 0xff) | ((result & 0xff) << 8);
  95
  96out:
  97        return result;
  98}
  99
 100/*
 101 * XXX Fixme
 102 *
 103 * This is very ugly but temporary. THIS NEEDS SERIOUS ENHANCEMENTS.
 104 * But it's very tricky to get right even in C.
 105 */
 106extern unsigned long do_csum(const unsigned char *, long);
 107
 108__wsum
 109csum_partial_copy_from_user(const void __user *src, void *dst,
 110                                int len, __wsum psum, int *errp)
 111{
 112        unsigned long result;
 113
 114        /* XXX Fixme
 115         * for now we separate the copy from checksum for obvious
 116         * alignment difficulties. Look at the Alpha code and you'll be
 117         * scared.
 118         */
 119
 120        if (__copy_from_user(dst, src, len) != 0 && errp)
 121                *errp = -EFAULT;
 122
 123        result = do_csum(dst, len);
 124
 125        /* add in old sum, and carry.. */
 126        result += (__force u32)psum;
 127        /* 32+c bits -> 32 bits */
 128        result = (result & 0xffffffff) + (result >> 32);
 129        return (__force __wsum)result;
 130}
 131
 132EXPORT_SYMBOL(csum_partial_copy_from_user);
 133
 134__wsum
 135csum_partial_copy_nocheck(const void *src, void *dst, int len, __wsum sum)
 136{
 137        return csum_partial_copy_from_user((__force const void __user *)src,
 138                                           dst, len, sum, NULL);
 139}
 140
 141EXPORT_SYMBOL(csum_partial_copy_nocheck);
 142