csum-wrappers_64.c 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. /*
  2. * Copyright 2002, 2003 Andi Kleen, SuSE Labs.
  3. * Subject to the GNU Public License v.2
  4. *
  5. * Wrappers of assembly checksum functions for x86-64.
  6. */
  7. #include <asm/checksum.h>
  8. #include <linux/export.h>
  9. #include <linux/uaccess.h>
  10. #include <asm/smap.h>
  11. /**
  12. * csum_partial_copy_from_user - Copy and checksum from user space.
  13. * @src: source address (user space)
  14. * @dst: destination address
  15. * @len: number of bytes to be copied.
  16. * @isum: initial sum that is added into the result (32bit unfolded)
  17. * @errp: set to -EFAULT for an bad source address.
  18. *
  19. * Returns an 32bit unfolded checksum of the buffer.
  20. * src and dst are best aligned to 64bits.
  21. */
  22. __wsum
  23. csum_partial_copy_from_user(const void __user *src, void *dst,
  24. int len, __wsum isum, int *errp)
  25. {
  26. might_sleep();
  27. *errp = 0;
  28. if (!likely(access_ok(VERIFY_READ, src, len)))
  29. goto out_err;
  30. /*
  31. * Why 6, not 7? To handle odd addresses aligned we
  32. * would need to do considerable complications to fix the
  33. * checksum which is defined as an 16bit accumulator. The
  34. * fix alignment code is primarily for performance
  35. * compatibility with 32bit and that will handle odd
  36. * addresses slowly too.
  37. */
  38. if (unlikely((unsigned long)src & 6)) {
  39. while (((unsigned long)src & 6) && len >= 2) {
  40. __u16 val16;
  41. if (__get_user(val16, (const __u16 __user *)src))
  42. goto out_err;
  43. *(__u16 *)dst = val16;
  44. isum = (__force __wsum)add32_with_carry(
  45. (__force unsigned)isum, val16);
  46. src += 2;
  47. dst += 2;
  48. len -= 2;
  49. }
  50. }
  51. stac();
  52. isum = csum_partial_copy_generic((__force const void *)src,
  53. dst, len, isum, errp, NULL);
  54. clac();
  55. if (unlikely(*errp))
  56. goto out_err;
  57. return isum;
  58. out_err:
  59. *errp = -EFAULT;
  60. memset(dst, 0, len);
  61. return isum;
  62. }
  63. EXPORT_SYMBOL(csum_partial_copy_from_user);
  64. /**
  65. * csum_partial_copy_to_user - Copy and checksum to user space.
  66. * @src: source address
  67. * @dst: destination address (user space)
  68. * @len: number of bytes to be copied.
  69. * @isum: initial sum that is added into the result (32bit unfolded)
  70. * @errp: set to -EFAULT for an bad destination address.
  71. *
  72. * Returns an 32bit unfolded checksum of the buffer.
  73. * src and dst are best aligned to 64bits.
  74. */
  75. __wsum
  76. csum_partial_copy_to_user(const void *src, void __user *dst,
  77. int len, __wsum isum, int *errp)
  78. {
  79. __wsum ret;
  80. might_sleep();
  81. if (unlikely(!access_ok(VERIFY_WRITE, dst, len))) {
  82. *errp = -EFAULT;
  83. return 0;
  84. }
  85. if (unlikely((unsigned long)dst & 6)) {
  86. while (((unsigned long)dst & 6) && len >= 2) {
  87. __u16 val16 = *(__u16 *)src;
  88. isum = (__force __wsum)add32_with_carry(
  89. (__force unsigned)isum, val16);
  90. *errp = __put_user(val16, (__u16 __user *)dst);
  91. if (*errp)
  92. return isum;
  93. src += 2;
  94. dst += 2;
  95. len -= 2;
  96. }
  97. }
  98. *errp = 0;
  99. stac();
  100. ret = csum_partial_copy_generic(src, (void __force *)dst,
  101. len, isum, NULL, errp);
  102. clac();
  103. return ret;
  104. }
  105. EXPORT_SYMBOL(csum_partial_copy_to_user);
  106. /**
  107. * csum_partial_copy_nocheck - Copy and checksum.
  108. * @src: source address
  109. * @dst: destination address
  110. * @len: number of bytes to be copied.
  111. * @sum: initial sum that is added into the result (32bit unfolded)
  112. *
  113. * Returns an 32bit unfolded checksum of the buffer.
  114. */
  115. __wsum
  116. csum_partial_copy_nocheck(const void *src, void *dst, int len, __wsum sum)
  117. {
  118. return csum_partial_copy_generic(src, dst, len, sum, NULL, NULL);
  119. }
  120. EXPORT_SYMBOL(csum_partial_copy_nocheck);
  121. __sum16 csum_ipv6_magic(const struct in6_addr *saddr,
  122. const struct in6_addr *daddr,
  123. __u32 len, __u8 proto, __wsum sum)
  124. {
  125. __u64 rest, sum64;
  126. rest = (__force __u64)htonl(len) + (__force __u64)htons(proto) +
  127. (__force __u64)sum;
  128. asm(" addq (%[saddr]),%[sum]\n"
  129. " adcq 8(%[saddr]),%[sum]\n"
  130. " adcq (%[daddr]),%[sum]\n"
  131. " adcq 8(%[daddr]),%[sum]\n"
  132. " adcq $0,%[sum]\n"
  133. : [sum] "=r" (sum64)
  134. : "[sum]" (rest), [saddr] "r" (saddr), [daddr] "r" (daddr));
  135. return csum_fold(
  136. (__force __wsum)add32_with_carry(sum64 & 0xffffffff, sum64>>32));
  137. }
  138. EXPORT_SYMBOL(csum_ipv6_magic);