root/arch/alpha/lib/csum_partial_copy.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. from64to16
  2. csum_partial_cfu_aligned
  3. csum_partial_cfu_dest_aligned
  4. csum_partial_cfu_src_aligned
  5. csum_partial_cfu_unaligned
  6. csum_partial_copy_from_user
  7. csum_partial_copy_nocheck

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  * csum_partial_copy - do IP checksumming and copy
   4  *
   5  * (C) Copyright 1996 Linus Torvalds
   6  * accelerated versions (and 21264 assembly versions ) contributed by
   7  *      Rick Gorton     <rick.gorton@alpha-processor.com>
   8  *
   9  * Don't look at this too closely - you'll go mad. The things
  10  * we do for performance..
  11  */
  12 
  13 #include <linux/types.h>
  14 #include <linux/string.h>
  15 #include <linux/uaccess.h>
  16 
  17 
  18 #define ldq_u(x,y) \
  19 __asm__ __volatile__("ldq_u %0,%1":"=r" (x):"m" (*(const unsigned long *)(y)))
  20 
  21 #define stq_u(x,y) \
  22 __asm__ __volatile__("stq_u %1,%0":"=m" (*(unsigned long *)(y)):"r" (x))
  23 
  24 #define extql(x,y,z) \
  25 __asm__ __volatile__("extql %1,%2,%0":"=r" (z):"r" (x),"r" (y))
  26 
  27 #define extqh(x,y,z) \
  28 __asm__ __volatile__("extqh %1,%2,%0":"=r" (z):"r" (x),"r" (y))
  29 
  30 #define mskql(x,y,z) \
  31 __asm__ __volatile__("mskql %1,%2,%0":"=r" (z):"r" (x),"r" (y))
  32 
  33 #define mskqh(x,y,z) \
  34 __asm__ __volatile__("mskqh %1,%2,%0":"=r" (z):"r" (x),"r" (y))
  35 
  36 #define insql(x,y,z) \
  37 __asm__ __volatile__("insql %1,%2,%0":"=r" (z):"r" (x),"r" (y))
  38 
  39 #define insqh(x,y,z) \
  40 __asm__ __volatile__("insqh %1,%2,%0":"=r" (z):"r" (x),"r" (y))
  41 
  42 
  43 #define __get_user_u(x,ptr)                             \
  44 ({                                                      \
  45         long __guu_err;                                 \
  46         __asm__ __volatile__(                           \
  47         "1:     ldq_u %0,%2\n"                          \
  48         "2:\n"                                          \
  49         EXC(1b,2b,%0,%1)                                \
  50                 : "=r"(x), "=r"(__guu_err)              \
  51                 : "m"(__m(ptr)), "1"(0));               \
  52         __guu_err;                                      \
  53 })
  54 
  55 #define __put_user_u(x,ptr)                             \
  56 ({                                                      \
  57         long __puu_err;                                 \
  58         __asm__ __volatile__(                           \
  59         "1:     stq_u %2,%1\n"                          \
  60         "2:\n"                                          \
  61         EXC(1b,2b,$31,%0)                               \
  62                 : "=r"(__puu_err)                       \
  63                 : "m"(__m(addr)), "rJ"(x), "0"(0));     \
  64         __puu_err;                                      \
  65 })
  66 
  67 
  68 static inline unsigned short from64to16(unsigned long x)
  69 {
  70         /* Using extract instructions is a bit more efficient
  71            than the original shift/bitmask version.  */
  72 
  73         union {
  74                 unsigned long   ul;
  75                 unsigned int    ui[2];
  76                 unsigned short  us[4];
  77         } in_v, tmp_v, out_v;
  78 
  79         in_v.ul = x;
  80         tmp_v.ul = (unsigned long) in_v.ui[0] + (unsigned long) in_v.ui[1];
  81 
  82         /* Since the bits of tmp_v.sh[3] are going to always be zero,
  83            we don't have to bother to add that in.  */
  84         out_v.ul = (unsigned long) tmp_v.us[0] + (unsigned long) tmp_v.us[1]
  85                         + (unsigned long) tmp_v.us[2];
  86 
  87         /* Similarly, out_v.us[2] is always zero for the final add.  */
  88         return out_v.us[0] + out_v.us[1];
  89 }
  90 
  91 
  92 
  93 /*
  94  * Ok. This isn't fun, but this is the EASY case.
  95  */
  96 static inline unsigned long
  97 csum_partial_cfu_aligned(const unsigned long __user *src, unsigned long *dst,
  98                          long len, unsigned long checksum,
  99                          int *errp)
 100 {
 101         unsigned long carry = 0;
 102         int err = 0;
 103 
 104         while (len >= 0) {
 105                 unsigned long word;
 106                 err |= __get_user(word, src);
 107                 checksum += carry;
 108                 src++;
 109                 checksum += word;
 110                 len -= 8;
 111                 carry = checksum < word;
 112                 *dst = word;
 113                 dst++;
 114         }
 115         len += 8;
 116         checksum += carry;
 117         if (len) {
 118                 unsigned long word, tmp;
 119                 err |= __get_user(word, src);
 120                 tmp = *dst;
 121                 mskql(word, len, word);
 122                 checksum += word;
 123                 mskqh(tmp, len, tmp);
 124                 carry = checksum < word;
 125                 *dst = word | tmp;
 126                 checksum += carry;
 127         }
 128         if (err && errp) *errp = err;
 129         return checksum;
 130 }
 131 
 132 /*
 133  * This is even less fun, but this is still reasonably
 134  * easy.
 135  */
 136 static inline unsigned long
 137 csum_partial_cfu_dest_aligned(const unsigned long __user *src,
 138                               unsigned long *dst,
 139                               unsigned long soff,
 140                               long len, unsigned long checksum,
 141                               int *errp)
 142 {
 143         unsigned long first;
 144         unsigned long word, carry;
 145         unsigned long lastsrc = 7+len+(unsigned long)src;
 146         int err = 0;
 147 
 148         err |= __get_user_u(first,src);
 149         carry = 0;
 150         while (len >= 0) {
 151                 unsigned long second;
 152 
 153                 err |= __get_user_u(second, src+1);
 154                 extql(first, soff, word);
 155                 len -= 8;
 156                 src++;
 157                 extqh(second, soff, first);
 158                 checksum += carry;
 159                 word |= first;
 160                 first = second;
 161                 checksum += word;
 162                 *dst = word;
 163                 dst++;
 164                 carry = checksum < word;
 165         }
 166         len += 8;
 167         checksum += carry;
 168         if (len) {
 169                 unsigned long tmp;
 170                 unsigned long second;
 171                 err |= __get_user_u(second, lastsrc);
 172                 tmp = *dst;
 173                 extql(first, soff, word);
 174                 extqh(second, soff, first);
 175                 word |= first;
 176                 mskql(word, len, word);
 177                 checksum += word;
 178                 mskqh(tmp, len, tmp);
 179                 carry = checksum < word;
 180                 *dst = word | tmp;
 181                 checksum += carry;
 182         }
 183         if (err && errp) *errp = err;
 184         return checksum;
 185 }
 186 
 187 /*
 188  * This is slightly less fun than the above..
 189  */
 190 static inline unsigned long
 191 csum_partial_cfu_src_aligned(const unsigned long __user *src,
 192                              unsigned long *dst,
 193                              unsigned long doff,
 194                              long len, unsigned long checksum,
 195                              unsigned long partial_dest,
 196                              int *errp)
 197 {
 198         unsigned long carry = 0;
 199         unsigned long word;
 200         unsigned long second_dest;
 201         int err = 0;
 202 
 203         mskql(partial_dest, doff, partial_dest);
 204         while (len >= 0) {
 205                 err |= __get_user(word, src);
 206                 len -= 8;
 207                 insql(word, doff, second_dest);
 208                 checksum += carry;
 209                 stq_u(partial_dest | second_dest, dst);
 210                 src++;
 211                 checksum += word;
 212                 insqh(word, doff, partial_dest);
 213                 carry = checksum < word;
 214                 dst++;
 215         }
 216         len += 8;
 217         if (len) {
 218                 checksum += carry;
 219                 err |= __get_user(word, src);
 220                 mskql(word, len, word);
 221                 len -= 8;
 222                 checksum += word;
 223                 insql(word, doff, second_dest);
 224                 len += doff;
 225                 carry = checksum < word;
 226                 partial_dest |= second_dest;
 227                 if (len >= 0) {
 228                         stq_u(partial_dest, dst);
 229                         if (!len) goto out;
 230                         dst++;
 231                         insqh(word, doff, partial_dest);
 232                 }
 233                 doff = len;
 234         }
 235         ldq_u(second_dest, dst);
 236         mskqh(second_dest, doff, second_dest);
 237         stq_u(partial_dest | second_dest, dst);
 238 out:
 239         checksum += carry;
 240         if (err && errp) *errp = err;
 241         return checksum;
 242 }
 243 
 244 /*
 245  * This is so totally un-fun that it's frightening. Don't
 246  * look at this too closely, you'll go blind.
 247  */
 248 static inline unsigned long
 249 csum_partial_cfu_unaligned(const unsigned long __user * src,
 250                            unsigned long * dst,
 251                            unsigned long soff, unsigned long doff,
 252                            long len, unsigned long checksum,
 253                            unsigned long partial_dest,
 254                            int *errp)
 255 {
 256         unsigned long carry = 0;
 257         unsigned long first;
 258         unsigned long lastsrc;
 259         int err = 0;
 260 
 261         err |= __get_user_u(first, src);
 262         lastsrc = 7+len+(unsigned long)src;
 263         mskql(partial_dest, doff, partial_dest);
 264         while (len >= 0) {
 265                 unsigned long second, word;
 266                 unsigned long second_dest;
 267 
 268                 err |= __get_user_u(second, src+1);
 269                 extql(first, soff, word);
 270                 checksum += carry;
 271                 len -= 8;
 272                 extqh(second, soff, first);
 273                 src++;
 274                 word |= first;
 275                 first = second;
 276                 insql(word, doff, second_dest);
 277                 checksum += word;
 278                 stq_u(partial_dest | second_dest, dst);
 279                 carry = checksum < word;
 280                 insqh(word, doff, partial_dest);
 281                 dst++;
 282         }
 283         len += doff;
 284         checksum += carry;
 285         if (len >= 0) {
 286                 unsigned long second, word;
 287                 unsigned long second_dest;
 288 
 289                 err |= __get_user_u(second, lastsrc);
 290                 extql(first, soff, word);
 291                 extqh(second, soff, first);
 292                 word |= first;
 293                 first = second;
 294                 mskql(word, len-doff, word);
 295                 checksum += word;
 296                 insql(word, doff, second_dest);
 297                 carry = checksum < word;
 298                 stq_u(partial_dest | second_dest, dst);
 299                 if (len) {
 300                         ldq_u(second_dest, dst+1);
 301                         insqh(word, doff, partial_dest);
 302                         mskqh(second_dest, len, second_dest);
 303                         stq_u(partial_dest | second_dest, dst+1);
 304                 }
 305                 checksum += carry;
 306         } else {
 307                 unsigned long second, word;
 308                 unsigned long second_dest;
 309 
 310                 err |= __get_user_u(second, lastsrc);
 311                 extql(first, soff, word);
 312                 extqh(second, soff, first);
 313                 word |= first;
 314                 ldq_u(second_dest, dst);
 315                 mskql(word, len-doff, word);
 316                 checksum += word;
 317                 mskqh(second_dest, len, second_dest);
 318                 carry = checksum < word;
 319                 insql(word, doff, word);
 320                 stq_u(partial_dest | word | second_dest, dst);
 321                 checksum += carry;
 322         }
 323         if (err && errp) *errp = err;
 324         return checksum;
 325 }
 326 
 327 __wsum
 328 csum_partial_copy_from_user(const void __user *src, void *dst, int len,
 329                                __wsum sum, int *errp)
 330 {
 331         unsigned long checksum = (__force u32) sum;
 332         unsigned long soff = 7 & (unsigned long) src;
 333         unsigned long doff = 7 & (unsigned long) dst;
 334 
 335         if (len) {
 336                 if (!access_ok(src, len)) {
 337                         if (errp) *errp = -EFAULT;
 338                         memset(dst, 0, len);
 339                         return sum;
 340                 }
 341                 if (!doff) {
 342                         if (!soff)
 343                                 checksum = csum_partial_cfu_aligned(
 344                                         (const unsigned long __user *) src,
 345                                         (unsigned long *) dst,
 346                                         len-8, checksum, errp);
 347                         else
 348                                 checksum = csum_partial_cfu_dest_aligned(
 349                                         (const unsigned long __user *) src,
 350                                         (unsigned long *) dst,
 351                                         soff, len-8, checksum, errp);
 352                 } else {
 353                         unsigned long partial_dest;
 354                         ldq_u(partial_dest, dst);
 355                         if (!soff)
 356                                 checksum = csum_partial_cfu_src_aligned(
 357                                         (const unsigned long __user *) src,
 358                                         (unsigned long *) dst,
 359                                         doff, len-8, checksum,
 360                                         partial_dest, errp);
 361                         else
 362                                 checksum = csum_partial_cfu_unaligned(
 363                                         (const unsigned long __user *) src,
 364                                         (unsigned long *) dst,
 365                                         soff, doff, len-8, checksum,
 366                                         partial_dest, errp);
 367                 }
 368                 checksum = from64to16 (checksum);
 369         }
 370         return (__force __wsum)checksum;
 371 }
 372 EXPORT_SYMBOL(csum_partial_copy_from_user);
 373 
 374 __wsum
 375 csum_partial_copy_nocheck(const void *src, void *dst, int len, __wsum sum)
 376 {
 377         __wsum checksum;
 378         mm_segment_t oldfs = get_fs();
 379         set_fs(KERNEL_DS);
 380         checksum = csum_partial_copy_from_user((__force const void __user *)src,
 381                                                 dst, len, sum, NULL);
 382         set_fs(oldfs);
 383         return checksum;
 384 }
 385 EXPORT_SYMBOL(csum_partial_copy_nocheck);

/* [<][>][^][v][top][bottom][index][help] */