scatterwalk.c 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * Cryptographic API.
  4. *
  5. * Cipher operations.
  6. *
  7. * Copyright (c) 2002 James Morris <jmorris@intercode.com.au>
  8. * 2002 Adam J. Richter <adam@yggdrasil.com>
  9. * 2004 Jean-Luc Cooke <jlcooke@certainkey.com>
  10. */
  11. #include <crypto/scatterwalk.h>
  12. #include <linux/kernel.h>
  13. #include <linux/mm.h>
  14. #include <linux/module.h>
  15. #include <linux/scatterlist.h>
  16. void scatterwalk_skip(struct scatter_walk *walk, unsigned int nbytes)
  17. {
  18. struct scatterlist *sg = walk->sg;
  19. nbytes += walk->offset - sg->offset;
  20. while (nbytes > sg->length) {
  21. nbytes -= sg->length;
  22. sg = sg_next(sg);
  23. }
  24. walk->sg = sg;
  25. walk->offset = sg->offset + nbytes;
  26. }
  27. EXPORT_SYMBOL_GPL(scatterwalk_skip);
  28. inline void memcpy_from_scatterwalk(void *buf, struct scatter_walk *walk,
  29. unsigned int nbytes)
  30. {
  31. do {
  32. unsigned int to_copy;
  33. to_copy = scatterwalk_next(walk, nbytes);
  34. memcpy(buf, walk->addr, to_copy);
  35. scatterwalk_done_src(walk, to_copy);
  36. buf += to_copy;
  37. nbytes -= to_copy;
  38. } while (nbytes);
  39. }
  40. EXPORT_SYMBOL_GPL(memcpy_from_scatterwalk);
  41. inline void memcpy_to_scatterwalk(struct scatter_walk *walk, const void *buf,
  42. unsigned int nbytes)
  43. {
  44. do {
  45. unsigned int to_copy;
  46. to_copy = scatterwalk_next(walk, nbytes);
  47. memcpy(walk->addr, buf, to_copy);
  48. scatterwalk_done_dst(walk, to_copy);
  49. buf += to_copy;
  50. nbytes -= to_copy;
  51. } while (nbytes);
  52. }
  53. EXPORT_SYMBOL_GPL(memcpy_to_scatterwalk);
  54. void memcpy_from_sglist(void *buf, struct scatterlist *sg,
  55. unsigned int start, unsigned int nbytes)
  56. {
  57. struct scatter_walk walk;
  58. if (unlikely(nbytes == 0)) /* in case sg == NULL */
  59. return;
  60. scatterwalk_start_at_pos(&walk, sg, start);
  61. memcpy_from_scatterwalk(buf, &walk, nbytes);
  62. }
  63. EXPORT_SYMBOL_GPL(memcpy_from_sglist);
  64. void memcpy_to_sglist(struct scatterlist *sg, unsigned int start,
  65. const void *buf, unsigned int nbytes)
  66. {
  67. struct scatter_walk walk;
  68. if (unlikely(nbytes == 0)) /* in case sg == NULL */
  69. return;
  70. scatterwalk_start_at_pos(&walk, sg, start);
  71. memcpy_to_scatterwalk(&walk, buf, nbytes);
  72. }
  73. EXPORT_SYMBOL_GPL(memcpy_to_sglist);
  74. /**
  75. * memcpy_sglist() - Copy data from one scatterlist to another
  76. * @dst: The destination scatterlist. Can be NULL if @nbytes == 0.
  77. * @src: The source scatterlist. Can be NULL if @nbytes == 0.
  78. * @nbytes: Number of bytes to copy
  79. *
  80. * The scatterlists can describe exactly the same memory, in which case this
  81. * function is a no-op. No other overlaps are supported.
  82. *
  83. * Context: Any context
  84. */
  85. void memcpy_sglist(struct scatterlist *dst, struct scatterlist *src,
  86. unsigned int nbytes)
  87. {
  88. unsigned int src_offset, dst_offset;
  89. if (unlikely(nbytes == 0)) /* in case src and/or dst is NULL */
  90. return;
  91. src_offset = src->offset;
  92. dst_offset = dst->offset;
  93. for (;;) {
  94. /* Compute the length to copy this step. */
  95. unsigned int len = min3(src->offset + src->length - src_offset,
  96. dst->offset + dst->length - dst_offset,
  97. nbytes);
  98. struct page *src_page = sg_page(src);
  99. struct page *dst_page = sg_page(dst);
  100. const void *src_virt;
  101. void *dst_virt;
  102. if (IS_ENABLED(CONFIG_HIGHMEM)) {
  103. /* HIGHMEM: we may have to actually map the pages. */
  104. const unsigned int src_oip = offset_in_page(src_offset);
  105. const unsigned int dst_oip = offset_in_page(dst_offset);
  106. const unsigned int limit = PAGE_SIZE;
  107. /* Further limit len to not cross a page boundary. */
  108. len = min3(len, limit - src_oip, limit - dst_oip);
  109. /* Compute the source and destination pages. */
  110. src_page += src_offset / PAGE_SIZE;
  111. dst_page += dst_offset / PAGE_SIZE;
  112. if (src_page != dst_page) {
  113. /* Copy between different pages. */
  114. memcpy_page(dst_page, dst_oip,
  115. src_page, src_oip, len);
  116. flush_dcache_page(dst_page);
  117. } else if (src_oip != dst_oip) {
  118. /* Copy between different parts of same page. */
  119. dst_virt = kmap_local_page(dst_page);
  120. memcpy(dst_virt + dst_oip, dst_virt + src_oip,
  121. len);
  122. kunmap_local(dst_virt);
  123. flush_dcache_page(dst_page);
  124. } /* Else, it's the same memory. No action needed. */
  125. } else {
  126. /*
  127. * !HIGHMEM: no mapping needed. Just work in the linear
  128. * buffer of each sg entry. Note that we can cross page
  129. * boundaries, as they are not significant in this case.
  130. */
  131. src_virt = page_address(src_page) + src_offset;
  132. dst_virt = page_address(dst_page) + dst_offset;
  133. if (src_virt != dst_virt) {
  134. memcpy(dst_virt, src_virt, len);
  135. if (ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE)
  136. __scatterwalk_flush_dcache_pages(
  137. dst_page, dst_offset, len);
  138. } /* Else, it's the same memory. No action needed. */
  139. }
  140. nbytes -= len;
  141. if (nbytes == 0) /* No more to copy? */
  142. break;
  143. /*
  144. * There's more to copy. Advance the offsets by the length
  145. * copied this step, and advance the sg entries as needed.
  146. */
  147. src_offset += len;
  148. if (src_offset >= src->offset + src->length) {
  149. src = sg_next(src);
  150. src_offset = src->offset;
  151. }
  152. dst_offset += len;
  153. if (dst_offset >= dst->offset + dst->length) {
  154. dst = sg_next(dst);
  155. dst_offset = dst->offset;
  156. }
  157. }
  158. }
  159. EXPORT_SYMBOL_GPL(memcpy_sglist);
  160. struct scatterlist *scatterwalk_ffwd(struct scatterlist dst[2],
  161. struct scatterlist *src,
  162. unsigned int len)
  163. {
  164. for (;;) {
  165. if (!len)
  166. return src;
  167. if (src->length > len)
  168. break;
  169. len -= src->length;
  170. src = sg_next(src);
  171. }
  172. sg_init_table(dst, 2);
  173. sg_set_page(dst, sg_page(src), src->length - len, src->offset + len);
  174. scatterwalk_crypto_chain(dst, sg_next(src), 2);
  175. return dst;
  176. }
  177. EXPORT_SYMBOL_GPL(scatterwalk_ffwd);