csum.c 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Checksum library
  4. *
  5. * Influenced by arch/arm64/lib/csum.c
  6. * Copyright (C) 2023-2024 Rivos Inc.
  7. */
  8. #include <linux/bitops.h>
  9. #include <linux/compiler.h>
  10. #include <linux/jump_label.h>
  11. #include <linux/kasan-checks.h>
  12. #include <linux/kernel.h>
  13. #include <asm/cpufeature.h>
  14. #include <net/checksum.h>
  15. /* Default version is sufficient for 32 bit */
  16. #ifndef CONFIG_32BIT
  17. __sum16 csum_ipv6_magic(const struct in6_addr *saddr,
  18. const struct in6_addr *daddr,
  19. __u32 len, __u8 proto, __wsum csum)
  20. {
  21. unsigned int ulen, uproto;
  22. unsigned long sum = (__force unsigned long)csum;
  23. sum += (__force unsigned long)saddr->s6_addr32[0];
  24. sum += (__force unsigned long)saddr->s6_addr32[1];
  25. sum += (__force unsigned long)saddr->s6_addr32[2];
  26. sum += (__force unsigned long)saddr->s6_addr32[3];
  27. sum += (__force unsigned long)daddr->s6_addr32[0];
  28. sum += (__force unsigned long)daddr->s6_addr32[1];
  29. sum += (__force unsigned long)daddr->s6_addr32[2];
  30. sum += (__force unsigned long)daddr->s6_addr32[3];
  31. ulen = (__force unsigned int)htonl((unsigned int)len);
  32. sum += ulen;
  33. uproto = (__force unsigned int)htonl(proto);
  34. sum += uproto;
  35. /*
  36. * Zbb support saves 4 instructions, so not worth checking without
  37. * alternatives if supported
  38. */
  39. if (IS_ENABLED(CONFIG_RISCV_ISA_ZBB) &&
  40. IS_ENABLED(CONFIG_RISCV_ALTERNATIVE)) {
  41. unsigned long fold_temp;
  42. /*
  43. * Zbb is likely available when the kernel is compiled with Zbb
  44. * support, so nop when Zbb is available and jump when Zbb is
  45. * not available.
  46. */
  47. asm goto(ALTERNATIVE("j %l[no_zbb]", "nop", 0,
  48. RISCV_ISA_EXT_ZBB, 1)
  49. :
  50. :
  51. :
  52. : no_zbb);
  53. asm(".option push \n\
  54. .option arch,+zbb \n\
  55. rori %[fold_temp], %[sum], 32 \n\
  56. add %[sum], %[fold_temp], %[sum] \n\
  57. srli %[sum], %[sum], 32 \n\
  58. not %[fold_temp], %[sum] \n\
  59. roriw %[sum], %[sum], 16 \n\
  60. subw %[sum], %[fold_temp], %[sum] \n\
  61. .option pop"
  62. : [sum] "+r" (sum), [fold_temp] "=&r" (fold_temp));
  63. return (__force __sum16)(sum >> 16);
  64. }
  65. no_zbb:
  66. sum += ror64(sum, 32);
  67. sum >>= 32;
  68. return csum_fold((__force __wsum)sum);
  69. }
  70. EXPORT_SYMBOL(csum_ipv6_magic);
  71. #endif /* !CONFIG_32BIT */
  72. #ifdef CONFIG_32BIT
  73. #define OFFSET_MASK 3
  74. #elif CONFIG_64BIT
  75. #define OFFSET_MASK 7
  76. #endif
  77. static inline __no_sanitize_address unsigned long
  78. do_csum_common(const unsigned long *ptr, const unsigned long *end,
  79. unsigned long data)
  80. {
  81. unsigned int shift;
  82. unsigned long csum = 0, carry = 0;
  83. /*
  84. * Do 32-bit reads on RV32 and 64-bit reads otherwise. This should be
  85. * faster than doing 32-bit reads on architectures that support larger
  86. * reads.
  87. */
  88. while (ptr < end) {
  89. csum += data;
  90. carry += csum < data;
  91. data = *(ptr++);
  92. }
  93. /*
  94. * Perform alignment (and over-read) bytes on the tail if any bytes
  95. * leftover.
  96. */
  97. shift = ((long)ptr - (long)end) * 8;
  98. #ifdef __LITTLE_ENDIAN
  99. data = (data << shift) >> shift;
  100. #else
  101. data = (data >> shift) << shift;
  102. #endif
  103. csum += data;
  104. carry += csum < data;
  105. csum += carry;
  106. csum += csum < carry;
  107. return csum;
  108. }
  109. /*
  110. * Algorithm accounts for buff being misaligned.
  111. * If buff is not aligned, will over-read bytes but not use the bytes that it
  112. * shouldn't. The same thing will occur on the tail-end of the read.
  113. */
  114. static inline __no_sanitize_address unsigned int
  115. do_csum_with_alignment(const unsigned char *buff, int len)
  116. {
  117. unsigned int offset, shift;
  118. unsigned long csum, data;
  119. const unsigned long *ptr, *end;
  120. /*
  121. * Align address to closest word (double word on rv64) that comes before
  122. * buff. This should always be in the same page and cache line.
  123. * Directly call KASAN with the alignment we will be using.
  124. */
  125. offset = (unsigned long)buff & OFFSET_MASK;
  126. kasan_check_read(buff, len);
  127. ptr = (const unsigned long *)(buff - offset);
  128. /*
  129. * Clear the most significant bytes that were over-read if buff was not
  130. * aligned.
  131. */
  132. shift = offset * 8;
  133. data = *(ptr++);
  134. #ifdef __LITTLE_ENDIAN
  135. data = (data >> shift) << shift;
  136. #else
  137. data = (data << shift) >> shift;
  138. #endif
  139. end = (const unsigned long *)(buff + len);
  140. csum = do_csum_common(ptr, end, data);
  141. #ifdef CC_HAS_ASM_GOTO_TIED_OUTPUT
  142. /*
  143. * Zbb support saves 6 instructions, so not worth checking without
  144. * alternatives if supported
  145. */
  146. if (IS_ENABLED(CONFIG_RISCV_ISA_ZBB) &&
  147. IS_ENABLED(CONFIG_RISCV_ALTERNATIVE)) {
  148. unsigned long fold_temp;
  149. /*
  150. * Zbb is likely available when the kernel is compiled with Zbb
  151. * support, so nop when Zbb is available and jump when Zbb is
  152. * not available.
  153. */
  154. asm goto(ALTERNATIVE("j %l[no_zbb]", "nop", 0,
  155. RISCV_ISA_EXT_ZBB, 1)
  156. :
  157. :
  158. :
  159. : no_zbb);
  160. #ifdef CONFIG_32BIT
  161. asm_goto_output(".option push \n\
  162. .option arch,+zbb \n\
  163. rori %[fold_temp], %[csum], 16 \n\
  164. andi %[offset], %[offset], 1 \n\
  165. add %[csum], %[fold_temp], %[csum] \n\
  166. beq %[offset], zero, %l[end] \n\
  167. rev8 %[csum], %[csum] \n\
  168. .option pop"
  169. : [csum] "+r" (csum), [fold_temp] "=&r" (fold_temp)
  170. : [offset] "r" (offset)
  171. :
  172. : end);
  173. return (unsigned short)csum;
  174. #else /* !CONFIG_32BIT */
  175. asm_goto_output(".option push \n\
  176. .option arch,+zbb \n\
  177. rori %[fold_temp], %[csum], 32 \n\
  178. add %[csum], %[fold_temp], %[csum] \n\
  179. srli %[csum], %[csum], 32 \n\
  180. roriw %[fold_temp], %[csum], 16 \n\
  181. addw %[csum], %[fold_temp], %[csum] \n\
  182. andi %[offset], %[offset], 1 \n\
  183. beq %[offset], zero, %l[end] \n\
  184. rev8 %[csum], %[csum] \n\
  185. .option pop"
  186. : [csum] "+r" (csum), [fold_temp] "=&r" (fold_temp)
  187. : [offset] "r" (offset)
  188. :
  189. : end);
  190. return (csum << 16) >> 48;
  191. #endif /* !CONFIG_32BIT */
  192. end:
  193. return csum >> 16;
  194. }
  195. no_zbb:
  196. #endif /* CC_HAS_ASM_GOTO_TIED_OUTPUT */
  197. #ifndef CONFIG_32BIT
  198. csum += ror64(csum, 32);
  199. csum >>= 32;
  200. #endif
  201. csum = (u32)csum + ror32((u32)csum, 16);
  202. if (offset & 1)
  203. return (u16)swab32(csum);
  204. return csum >> 16;
  205. }
  206. /*
  207. * Does not perform alignment, should only be used if machine has fast
  208. * misaligned accesses, or when buff is known to be aligned.
  209. */
  210. static inline __no_sanitize_address unsigned int
  211. do_csum_no_alignment(const unsigned char *buff, int len)
  212. {
  213. unsigned long csum, data;
  214. const unsigned long *ptr, *end;
  215. ptr = (const unsigned long *)(buff);
  216. data = *(ptr++);
  217. kasan_check_read(buff, len);
  218. end = (const unsigned long *)(buff + len);
  219. csum = do_csum_common(ptr, end, data);
  220. /*
  221. * Zbb support saves 6 instructions, so not worth checking without
  222. * alternatives if supported
  223. */
  224. if (IS_ENABLED(CONFIG_RISCV_ISA_ZBB) &&
  225. IS_ENABLED(CONFIG_RISCV_ALTERNATIVE)) {
  226. unsigned long fold_temp;
  227. /*
  228. * Zbb is likely available when the kernel is compiled with Zbb
  229. * support, so nop when Zbb is available and jump when Zbb is
  230. * not available.
  231. */
  232. asm goto(ALTERNATIVE("j %l[no_zbb]", "nop", 0,
  233. RISCV_ISA_EXT_ZBB, 1)
  234. :
  235. :
  236. :
  237. : no_zbb);
  238. #ifdef CONFIG_32BIT
  239. asm (".option push \n\
  240. .option arch,+zbb \n\
  241. rori %[fold_temp], %[csum], 16 \n\
  242. add %[csum], %[fold_temp], %[csum] \n\
  243. .option pop"
  244. : [csum] "+r" (csum), [fold_temp] "=&r" (fold_temp)
  245. :
  246. : );
  247. #else /* !CONFIG_32BIT */
  248. asm (".option push \n\
  249. .option arch,+zbb \n\
  250. rori %[fold_temp], %[csum], 32 \n\
  251. add %[csum], %[fold_temp], %[csum] \n\
  252. srli %[csum], %[csum], 32 \n\
  253. roriw %[fold_temp], %[csum], 16 \n\
  254. addw %[csum], %[fold_temp], %[csum] \n\
  255. .option pop"
  256. : [csum] "+r" (csum), [fold_temp] "=&r" (fold_temp)
  257. :
  258. : );
  259. #endif /* !CONFIG_32BIT */
  260. return csum >> 16;
  261. }
  262. no_zbb:
  263. #ifndef CONFIG_32BIT
  264. csum += ror64(csum, 32);
  265. csum >>= 32;
  266. #endif
  267. csum = (u32)csum + ror32((u32)csum, 16);
  268. return csum >> 16;
  269. }
  270. /*
  271. * Perform a checksum on an arbitrary memory address.
  272. * Will do a light-weight address alignment if buff is misaligned, unless
  273. * cpu supports fast misaligned accesses.
  274. */
  275. unsigned int do_csum(const unsigned char *buff, int len)
  276. {
  277. if (unlikely(len <= 0))
  278. return 0;
  279. /*
  280. * Significant performance gains can be seen by not doing alignment
  281. * on machines with fast misaligned accesses.
  282. *
  283. * There is some duplicate code between the "with_alignment" and
  284. * "no_alignment" implmentations, but the overlap is too awkward to be
  285. * able to fit in one function without introducing multiple static
  286. * branches. The largest chunk of overlap was delegated into the
  287. * do_csum_common function.
  288. */
  289. if (has_fast_unaligned_accesses() || (((unsigned long)buff & OFFSET_MASK) == 0))
  290. return do_csum_no_alignment(buff, len);
  291. return do_csum_with_alignment(buff, len);
  292. }