xref: /openbmc/linux/crypto/scatterwalk.c (revision a77e393c)
1 /*
2  * Cryptographic API.
3  *
4  * Cipher operations.
5  *
6  * Copyright (c) 2002 James Morris <jmorris@intercode.com.au>
7  *               2002 Adam J. Richter <adam@yggdrasil.com>
8  *               2004 Jean-Luc Cooke <jlcooke@certainkey.com>
9  *
10  * This program is free software; you can redistribute it and/or modify it
11  * under the terms of the GNU General Public License as published by the Free
12  * Software Foundation; either version 2 of the License, or (at your option)
13  * any later version.
14  *
15  */
16 
17 #include <crypto/scatterwalk.h>
18 #include <linux/kernel.h>
19 #include <linux/mm.h>
20 #include <linux/module.h>
21 #include <linux/scatterlist.h>
22 
23 static inline void memcpy_dir(void *buf, void *sgdata, size_t nbytes, int out)
24 {
25 	void *src = out ? buf : sgdata;
26 	void *dst = out ? sgdata : buf;
27 
28 	memcpy(dst, src, nbytes);
29 }
30 
31 void scatterwalk_copychunks(void *buf, struct scatter_walk *walk,
32 			    size_t nbytes, int out)
33 {
34 	for (;;) {
35 		unsigned int len_this_page = scatterwalk_pagelen(walk);
36 		u8 *vaddr;
37 
38 		if (len_this_page > nbytes)
39 			len_this_page = nbytes;
40 
41 		if (out != 2) {
42 			vaddr = scatterwalk_map(walk);
43 			memcpy_dir(buf, vaddr, len_this_page, out);
44 			scatterwalk_unmap(vaddr);
45 		}
46 
47 		scatterwalk_advance(walk, len_this_page);
48 
49 		if (nbytes == len_this_page)
50 			break;
51 
52 		buf += len_this_page;
53 		nbytes -= len_this_page;
54 
55 		scatterwalk_pagedone(walk, out & 1, 1);
56 	}
57 }
58 EXPORT_SYMBOL_GPL(scatterwalk_copychunks);
59 
60 void scatterwalk_map_and_copy(void *buf, struct scatterlist *sg,
61 			      unsigned int start, unsigned int nbytes, int out)
62 {
63 	struct scatter_walk walk;
64 	struct scatterlist tmp[2];
65 
66 	if (!nbytes)
67 		return;
68 
69 	sg = scatterwalk_ffwd(tmp, sg, start);
70 
71 	if (sg_page(sg) == virt_to_page(buf) &&
72 	    sg->offset == offset_in_page(buf))
73 		return;
74 
75 	scatterwalk_start(&walk, sg);
76 	scatterwalk_copychunks(buf, &walk, nbytes, out);
77 	scatterwalk_done(&walk, out, 0);
78 }
79 EXPORT_SYMBOL_GPL(scatterwalk_map_and_copy);
80 
81 struct scatterlist *scatterwalk_ffwd(struct scatterlist dst[2],
82 				     struct scatterlist *src,
83 				     unsigned int len)
84 {
85 	for (;;) {
86 		if (!len)
87 			return src;
88 
89 		if (src->length > len)
90 			break;
91 
92 		len -= src->length;
93 		src = sg_next(src);
94 	}
95 
96 	sg_init_table(dst, 2);
97 	sg_set_page(dst, sg_page(src), src->length - len, src->offset + len);
98 	scatterwalk_crypto_chain(dst, sg_next(src), 0, 2);
99 
100 	return dst;
101 }
102 EXPORT_SYMBOL_GPL(scatterwalk_ffwd);
103