1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * OMAP Crypto driver common support routines. |
4 | * |
5 | * Copyright (c) 2017 Texas Instruments Incorporated |
6 | * Tero Kristo <t-kristo@ti.com> |
7 | */ |
8 | |
9 | #include <linux/module.h> |
10 | #include <linux/kernel.h> |
11 | #include <linux/scatterlist.h> |
12 | #include <crypto/scatterwalk.h> |
13 | |
14 | #include "omap-crypto.h" |
15 | |
16 | static int omap_crypto_copy_sg_lists(int total, int bs, |
17 | struct scatterlist **sg, |
18 | struct scatterlist *new_sg, u16 flags) |
19 | { |
20 | int n = sg_nents(sg: *sg); |
21 | struct scatterlist *tmp; |
22 | |
23 | if (!(flags & OMAP_CRYPTO_FORCE_SINGLE_ENTRY)) { |
24 | new_sg = kmalloc_array(n, size: sizeof(*sg), GFP_KERNEL); |
25 | if (!new_sg) |
26 | return -ENOMEM; |
27 | |
28 | sg_init_table(new_sg, n); |
29 | } |
30 | |
31 | tmp = new_sg; |
32 | |
33 | while (*sg && total) { |
34 | int len = (*sg)->length; |
35 | |
36 | if (total < len) |
37 | len = total; |
38 | |
39 | if (len > 0) { |
40 | total -= len; |
41 | sg_set_page(sg: tmp, page: sg_page(sg: *sg), len, offset: (*sg)->offset); |
42 | if (total <= 0) |
43 | sg_mark_end(sg: tmp); |
44 | tmp = sg_next(tmp); |
45 | } |
46 | |
47 | *sg = sg_next(*sg); |
48 | } |
49 | |
50 | *sg = new_sg; |
51 | |
52 | return 0; |
53 | } |
54 | |
55 | static int omap_crypto_copy_sgs(int total, int bs, struct scatterlist **sg, |
56 | struct scatterlist *new_sg, u16 flags) |
57 | { |
58 | void *buf; |
59 | int pages; |
60 | int new_len; |
61 | |
62 | new_len = ALIGN(total, bs); |
63 | pages = get_order(size: new_len); |
64 | |
65 | buf = (void *)__get_free_pages(GFP_ATOMIC, order: pages); |
66 | if (!buf) { |
67 | pr_err("%s: Couldn't allocate pages for unaligned cases.\n" , |
68 | __func__); |
69 | return -ENOMEM; |
70 | } |
71 | |
72 | if (flags & OMAP_CRYPTO_COPY_DATA) { |
73 | scatterwalk_map_and_copy(buf, sg: *sg, start: 0, nbytes: total, out: 0); |
74 | if (flags & OMAP_CRYPTO_ZERO_BUF) |
75 | memset(buf + total, 0, new_len - total); |
76 | } |
77 | |
78 | if (!(flags & OMAP_CRYPTO_FORCE_SINGLE_ENTRY)) |
79 | sg_init_table(new_sg, 1); |
80 | |
81 | sg_set_buf(sg: new_sg, buf, buflen: new_len); |
82 | |
83 | *sg = new_sg; |
84 | |
85 | return 0; |
86 | } |
87 | |
88 | static int omap_crypto_check_sg(struct scatterlist *sg, int total, int bs, |
89 | u16 flags) |
90 | { |
91 | int len = 0; |
92 | int num_sg = 0; |
93 | |
94 | if (!IS_ALIGNED(total, bs)) |
95 | return OMAP_CRYPTO_NOT_ALIGNED; |
96 | |
97 | while (sg) { |
98 | num_sg++; |
99 | |
100 | if (!IS_ALIGNED(sg->offset, 4)) |
101 | return OMAP_CRYPTO_NOT_ALIGNED; |
102 | if (!IS_ALIGNED(sg->length, bs)) |
103 | return OMAP_CRYPTO_NOT_ALIGNED; |
104 | #ifdef CONFIG_ZONE_DMA |
105 | if (page_zonenum(page: sg_page(sg)) != ZONE_DMA) |
106 | return OMAP_CRYPTO_NOT_ALIGNED; |
107 | #endif |
108 | |
109 | len += sg->length; |
110 | sg = sg_next(sg); |
111 | |
112 | if (len >= total) |
113 | break; |
114 | } |
115 | |
116 | if ((flags & OMAP_CRYPTO_FORCE_SINGLE_ENTRY) && num_sg > 1) |
117 | return OMAP_CRYPTO_NOT_ALIGNED; |
118 | |
119 | if (len != total) |
120 | return OMAP_CRYPTO_BAD_DATA_LENGTH; |
121 | |
122 | return 0; |
123 | } |
124 | |
125 | int omap_crypto_align_sg(struct scatterlist **sg, int total, int bs, |
126 | struct scatterlist *new_sg, u16 flags, |
127 | u8 flags_shift, unsigned long *dd_flags) |
128 | { |
129 | int ret; |
130 | |
131 | *dd_flags &= ~(OMAP_CRYPTO_COPY_MASK << flags_shift); |
132 | |
133 | if (flags & OMAP_CRYPTO_FORCE_COPY) |
134 | ret = OMAP_CRYPTO_NOT_ALIGNED; |
135 | else |
136 | ret = omap_crypto_check_sg(sg: *sg, total, bs, flags); |
137 | |
138 | if (ret == OMAP_CRYPTO_NOT_ALIGNED) { |
139 | ret = omap_crypto_copy_sgs(total, bs, sg, new_sg, flags); |
140 | if (ret) |
141 | return ret; |
142 | *dd_flags |= OMAP_CRYPTO_DATA_COPIED << flags_shift; |
143 | } else if (ret == OMAP_CRYPTO_BAD_DATA_LENGTH) { |
144 | ret = omap_crypto_copy_sg_lists(total, bs, sg, new_sg, flags); |
145 | if (ret) |
146 | return ret; |
147 | if (!(flags & OMAP_CRYPTO_FORCE_SINGLE_ENTRY)) |
148 | *dd_flags |= OMAP_CRYPTO_SG_COPIED << flags_shift; |
149 | } else if (flags & OMAP_CRYPTO_FORCE_SINGLE_ENTRY) { |
150 | sg_set_buf(sg: new_sg, buf: sg_virt(sg: *sg), buflen: (*sg)->length); |
151 | } |
152 | |
153 | return 0; |
154 | } |
155 | EXPORT_SYMBOL_GPL(omap_crypto_align_sg); |
156 | |
157 | static void omap_crypto_copy_data(struct scatterlist *src, |
158 | struct scatterlist *dst, |
159 | int offset, int len) |
160 | { |
161 | int amt; |
162 | void *srcb, *dstb; |
163 | int srco = 0, dsto = offset; |
164 | |
165 | while (src && dst && len) { |
166 | if (srco >= src->length) { |
167 | srco -= src->length; |
168 | src = sg_next(src); |
169 | continue; |
170 | } |
171 | |
172 | if (dsto >= dst->length) { |
173 | dsto -= dst->length; |
174 | dst = sg_next(dst); |
175 | continue; |
176 | } |
177 | |
178 | amt = min(src->length - srco, dst->length - dsto); |
179 | amt = min(len, amt); |
180 | |
181 | srcb = kmap_atomic(page: sg_page(sg: src)) + srco + src->offset; |
182 | dstb = kmap_atomic(page: sg_page(sg: dst)) + dsto + dst->offset; |
183 | |
184 | memcpy(dstb, srcb, amt); |
185 | |
186 | flush_dcache_page(page: sg_page(sg: dst)); |
187 | |
188 | kunmap_atomic(srcb); |
189 | kunmap_atomic(dstb); |
190 | |
191 | srco += amt; |
192 | dsto += amt; |
193 | len -= amt; |
194 | } |
195 | } |
196 | |
197 | void omap_crypto_cleanup(struct scatterlist *sg, struct scatterlist *orig, |
198 | int offset, int len, u8 flags_shift, |
199 | unsigned long flags) |
200 | { |
201 | void *buf; |
202 | int pages; |
203 | |
204 | flags >>= flags_shift; |
205 | flags &= OMAP_CRYPTO_COPY_MASK; |
206 | |
207 | if (!flags) |
208 | return; |
209 | |
210 | buf = sg_virt(sg); |
211 | pages = get_order(size: len); |
212 | |
213 | if (orig && (flags & OMAP_CRYPTO_DATA_COPIED)) |
214 | omap_crypto_copy_data(src: sg, dst: orig, offset, len); |
215 | |
216 | if (flags & OMAP_CRYPTO_DATA_COPIED) |
217 | free_pages(addr: (unsigned long)buf, order: pages); |
218 | else if (flags & OMAP_CRYPTO_SG_COPIED) |
219 | kfree(objp: sg); |
220 | } |
221 | EXPORT_SYMBOL_GPL(omap_crypto_cleanup); |
222 | |
223 | MODULE_DESCRIPTION("OMAP crypto support library." ); |
224 | MODULE_LICENSE("GPL v2" ); |
225 | MODULE_AUTHOR("Tero Kristo <t-kristo@ti.com>" ); |
226 | |