1937c30d7SJussi Kivilinna /*
2937c30d7SJussi Kivilinna  * Glue Code for SSE2 assembler versions of Serpent Cipher
3937c30d7SJussi Kivilinna  *
4937c30d7SJussi Kivilinna  * Copyright (c) 2011 Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
5937c30d7SJussi Kivilinna  *
6937c30d7SJussi Kivilinna  * Glue code based on aesni-intel_glue.c by:
7937c30d7SJussi Kivilinna  *  Copyright (C) 2008, Intel Corp.
8937c30d7SJussi Kivilinna  *    Author: Huang Ying <ying.huang@intel.com>
9937c30d7SJussi Kivilinna  *
10937c30d7SJussi Kivilinna  * CBC & ECB parts based on code (crypto/cbc.c,ecb.c) by:
11937c30d7SJussi Kivilinna  *   Copyright (c) 2006 Herbert Xu <herbert@gondor.apana.org.au>
12937c30d7SJussi Kivilinna  * CTR part based on code (crypto/ctr.c) by:
13937c30d7SJussi Kivilinna  *   (C) Copyright IBM Corp. 2007 - Joy Latten <latten@us.ibm.com>
14937c30d7SJussi Kivilinna  *
15937c30d7SJussi Kivilinna  * This program is free software; you can redistribute it and/or modify
16937c30d7SJussi Kivilinna  * it under the terms of the GNU General Public License as published by
17937c30d7SJussi Kivilinna  * the Free Software Foundation; either version 2 of the License, or
18937c30d7SJussi Kivilinna  * (at your option) any later version.
19937c30d7SJussi Kivilinna  *
20937c30d7SJussi Kivilinna  * This program is distributed in the hope that it will be useful,
21937c30d7SJussi Kivilinna  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22937c30d7SJussi Kivilinna  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23937c30d7SJussi Kivilinna  * GNU General Public License for more details.
24937c30d7SJussi Kivilinna  *
25937c30d7SJussi Kivilinna  * You should have received a copy of the GNU General Public License
26937c30d7SJussi Kivilinna  * along with this program; if not, write to the Free Software
27937c30d7SJussi Kivilinna  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
28937c30d7SJussi Kivilinna  * USA
29937c30d7SJussi Kivilinna  *
30937c30d7SJussi Kivilinna  */
31937c30d7SJussi Kivilinna 
32937c30d7SJussi Kivilinna #include <linux/module.h>
33937c30d7SJussi Kivilinna #include <linux/hardirq.h>
34937c30d7SJussi Kivilinna #include <linux/types.h>
35937c30d7SJussi Kivilinna #include <linux/crypto.h>
36937c30d7SJussi Kivilinna #include <linux/err.h>
37937c30d7SJussi Kivilinna #include <crypto/algapi.h>
38937c30d7SJussi Kivilinna #include <crypto/serpent.h>
39937c30d7SJussi Kivilinna #include <crypto/cryptd.h>
40937c30d7SJussi Kivilinna #include <crypto/b128ops.h>
41937c30d7SJussi Kivilinna #include <crypto/ctr.h>
4218482053SJussi Kivilinna #include <crypto/lrw.h>
435962f8b6SJussi Kivilinna #include <crypto/xts.h>
44937c30d7SJussi Kivilinna #include <asm/i387.h>
453387e7d6SJussi Kivilinna #include <asm/serpent-sse2.h>
46937c30d7SJussi Kivilinna #include <crypto/scatterwalk.h>
47937c30d7SJussi Kivilinna #include <linux/workqueue.h>
48937c30d7SJussi Kivilinna #include <linux/spinlock.h>
49937c30d7SJussi Kivilinna 
50937c30d7SJussi Kivilinna struct async_serpent_ctx {
51937c30d7SJussi Kivilinna 	struct cryptd_ablkcipher *cryptd_tfm;
52937c30d7SJussi Kivilinna };
53937c30d7SJussi Kivilinna 
54937c30d7SJussi Kivilinna static inline bool serpent_fpu_begin(bool fpu_enabled, unsigned int nbytes)
55937c30d7SJussi Kivilinna {
56937c30d7SJussi Kivilinna 	if (fpu_enabled)
57937c30d7SJussi Kivilinna 		return true;
58937c30d7SJussi Kivilinna 
59937c30d7SJussi Kivilinna 	/* SSE2 is only used when chunk to be processed is large enough, so
60937c30d7SJussi Kivilinna 	 * do not enable FPU until it is necessary.
61937c30d7SJussi Kivilinna 	 */
62937c30d7SJussi Kivilinna 	if (nbytes < SERPENT_BLOCK_SIZE * SERPENT_PARALLEL_BLOCKS)
63937c30d7SJussi Kivilinna 		return false;
64937c30d7SJussi Kivilinna 
65937c30d7SJussi Kivilinna 	kernel_fpu_begin();
66937c30d7SJussi Kivilinna 	return true;
67937c30d7SJussi Kivilinna }
68937c30d7SJussi Kivilinna 
69937c30d7SJussi Kivilinna static inline void serpent_fpu_end(bool fpu_enabled)
70937c30d7SJussi Kivilinna {
71937c30d7SJussi Kivilinna 	if (fpu_enabled)
72937c30d7SJussi Kivilinna 		kernel_fpu_end();
73937c30d7SJussi Kivilinna }
74937c30d7SJussi Kivilinna 
75937c30d7SJussi Kivilinna static int ecb_crypt(struct blkcipher_desc *desc, struct blkcipher_walk *walk,
76937c30d7SJussi Kivilinna 		     bool enc)
77937c30d7SJussi Kivilinna {
78937c30d7SJussi Kivilinna 	bool fpu_enabled = false;
79937c30d7SJussi Kivilinna 	struct serpent_ctx *ctx = crypto_blkcipher_ctx(desc->tfm);
80937c30d7SJussi Kivilinna 	const unsigned int bsize = SERPENT_BLOCK_SIZE;
81937c30d7SJussi Kivilinna 	unsigned int nbytes;
82937c30d7SJussi Kivilinna 	int err;
83937c30d7SJussi Kivilinna 
84937c30d7SJussi Kivilinna 	err = blkcipher_walk_virt(desc, walk);
85937c30d7SJussi Kivilinna 	desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP;
86937c30d7SJussi Kivilinna 
87937c30d7SJussi Kivilinna 	while ((nbytes = walk->nbytes)) {
88937c30d7SJussi Kivilinna 		u8 *wsrc = walk->src.virt.addr;
89937c30d7SJussi Kivilinna 		u8 *wdst = walk->dst.virt.addr;
90937c30d7SJussi Kivilinna 
91937c30d7SJussi Kivilinna 		fpu_enabled = serpent_fpu_begin(fpu_enabled, nbytes);
92937c30d7SJussi Kivilinna 
93937c30d7SJussi Kivilinna 		/* Process multi-block batch */
94937c30d7SJussi Kivilinna 		if (nbytes >= bsize * SERPENT_PARALLEL_BLOCKS) {
95937c30d7SJussi Kivilinna 			do {
96937c30d7SJussi Kivilinna 				if (enc)
97937c30d7SJussi Kivilinna 					serpent_enc_blk_xway(ctx, wdst, wsrc);
98937c30d7SJussi Kivilinna 				else
99937c30d7SJussi Kivilinna 					serpent_dec_blk_xway(ctx, wdst, wsrc);
100937c30d7SJussi Kivilinna 
101937c30d7SJussi Kivilinna 				wsrc += bsize * SERPENT_PARALLEL_BLOCKS;
102937c30d7SJussi Kivilinna 				wdst += bsize * SERPENT_PARALLEL_BLOCKS;
103937c30d7SJussi Kivilinna 				nbytes -= bsize * SERPENT_PARALLEL_BLOCKS;
104937c30d7SJussi Kivilinna 			} while (nbytes >= bsize * SERPENT_PARALLEL_BLOCKS);
105937c30d7SJussi Kivilinna 
106937c30d7SJussi Kivilinna 			if (nbytes < bsize)
107937c30d7SJussi Kivilinna 				goto done;
108937c30d7SJussi Kivilinna 		}
109937c30d7SJussi Kivilinna 
110937c30d7SJussi Kivilinna 		/* Handle leftovers */
111937c30d7SJussi Kivilinna 		do {
112937c30d7SJussi Kivilinna 			if (enc)
113937c30d7SJussi Kivilinna 				__serpent_encrypt(ctx, wdst, wsrc);
114937c30d7SJussi Kivilinna 			else
115937c30d7SJussi Kivilinna 				__serpent_decrypt(ctx, wdst, wsrc);
116937c30d7SJussi Kivilinna 
117937c30d7SJussi Kivilinna 			wsrc += bsize;
118937c30d7SJussi Kivilinna 			wdst += bsize;
119937c30d7SJussi Kivilinna 			nbytes -= bsize;
120937c30d7SJussi Kivilinna 		} while (nbytes >= bsize);
121937c30d7SJussi Kivilinna 
122937c30d7SJussi Kivilinna done:
123937c30d7SJussi Kivilinna 		err = blkcipher_walk_done(desc, walk, nbytes);
124937c30d7SJussi Kivilinna 	}
125937c30d7SJussi Kivilinna 
126937c30d7SJussi Kivilinna 	serpent_fpu_end(fpu_enabled);
127937c30d7SJussi Kivilinna 	return err;
128937c30d7SJussi Kivilinna }
129937c30d7SJussi Kivilinna 
130937c30d7SJussi Kivilinna static int ecb_encrypt(struct blkcipher_desc *desc, struct scatterlist *dst,
131937c30d7SJussi Kivilinna 		       struct scatterlist *src, unsigned int nbytes)
132937c30d7SJussi Kivilinna {
133937c30d7SJussi Kivilinna 	struct blkcipher_walk walk;
134937c30d7SJussi Kivilinna 
135937c30d7SJussi Kivilinna 	blkcipher_walk_init(&walk, dst, src, nbytes);
136937c30d7SJussi Kivilinna 	return ecb_crypt(desc, &walk, true);
137937c30d7SJussi Kivilinna }
138937c30d7SJussi Kivilinna 
139937c30d7SJussi Kivilinna static int ecb_decrypt(struct blkcipher_desc *desc, struct scatterlist *dst,
140937c30d7SJussi Kivilinna 		       struct scatterlist *src, unsigned int nbytes)
141937c30d7SJussi Kivilinna {
142937c30d7SJussi Kivilinna 	struct blkcipher_walk walk;
143937c30d7SJussi Kivilinna 
144937c30d7SJussi Kivilinna 	blkcipher_walk_init(&walk, dst, src, nbytes);
145937c30d7SJussi Kivilinna 	return ecb_crypt(desc, &walk, false);
146937c30d7SJussi Kivilinna }
147937c30d7SJussi Kivilinna 
148937c30d7SJussi Kivilinna static unsigned int __cbc_encrypt(struct blkcipher_desc *desc,
149937c30d7SJussi Kivilinna 				  struct blkcipher_walk *walk)
150937c30d7SJussi Kivilinna {
151937c30d7SJussi Kivilinna 	struct serpent_ctx *ctx = crypto_blkcipher_ctx(desc->tfm);
152937c30d7SJussi Kivilinna 	const unsigned int bsize = SERPENT_BLOCK_SIZE;
153937c30d7SJussi Kivilinna 	unsigned int nbytes = walk->nbytes;
154937c30d7SJussi Kivilinna 	u128 *src = (u128 *)walk->src.virt.addr;
155937c30d7SJussi Kivilinna 	u128 *dst = (u128 *)walk->dst.virt.addr;
156937c30d7SJussi Kivilinna 	u128 *iv = (u128 *)walk->iv;
157937c30d7SJussi Kivilinna 
158937c30d7SJussi Kivilinna 	do {
159937c30d7SJussi Kivilinna 		u128_xor(dst, src, iv);
160937c30d7SJussi Kivilinna 		__serpent_encrypt(ctx, (u8 *)dst, (u8 *)dst);
161937c30d7SJussi Kivilinna 		iv = dst;
162937c30d7SJussi Kivilinna 
163937c30d7SJussi Kivilinna 		src += 1;
164937c30d7SJussi Kivilinna 		dst += 1;
165937c30d7SJussi Kivilinna 		nbytes -= bsize;
166937c30d7SJussi Kivilinna 	} while (nbytes >= bsize);
167937c30d7SJussi Kivilinna 
168937c30d7SJussi Kivilinna 	u128_xor((u128 *)walk->iv, (u128 *)walk->iv, iv);
169937c30d7SJussi Kivilinna 	return nbytes;
170937c30d7SJussi Kivilinna }
171937c30d7SJussi Kivilinna 
172937c30d7SJussi Kivilinna static int cbc_encrypt(struct blkcipher_desc *desc, struct scatterlist *dst,
173937c30d7SJussi Kivilinna 		       struct scatterlist *src, unsigned int nbytes)
174937c30d7SJussi Kivilinna {
175937c30d7SJussi Kivilinna 	struct blkcipher_walk walk;
176937c30d7SJussi Kivilinna 	int err;
177937c30d7SJussi Kivilinna 
178937c30d7SJussi Kivilinna 	blkcipher_walk_init(&walk, dst, src, nbytes);
179937c30d7SJussi Kivilinna 	err = blkcipher_walk_virt(desc, &walk);
180937c30d7SJussi Kivilinna 
181937c30d7SJussi Kivilinna 	while ((nbytes = walk.nbytes)) {
182937c30d7SJussi Kivilinna 		nbytes = __cbc_encrypt(desc, &walk);
183937c30d7SJussi Kivilinna 		err = blkcipher_walk_done(desc, &walk, nbytes);
184937c30d7SJussi Kivilinna 	}
185937c30d7SJussi Kivilinna 
186937c30d7SJussi Kivilinna 	return err;
187937c30d7SJussi Kivilinna }
188937c30d7SJussi Kivilinna 
189937c30d7SJussi Kivilinna static unsigned int __cbc_decrypt(struct blkcipher_desc *desc,
190937c30d7SJussi Kivilinna 				  struct blkcipher_walk *walk)
191937c30d7SJussi Kivilinna {
192937c30d7SJussi Kivilinna 	struct serpent_ctx *ctx = crypto_blkcipher_ctx(desc->tfm);
193937c30d7SJussi Kivilinna 	const unsigned int bsize = SERPENT_BLOCK_SIZE;
194937c30d7SJussi Kivilinna 	unsigned int nbytes = walk->nbytes;
195937c30d7SJussi Kivilinna 	u128 *src = (u128 *)walk->src.virt.addr;
196937c30d7SJussi Kivilinna 	u128 *dst = (u128 *)walk->dst.virt.addr;
197937c30d7SJussi Kivilinna 	u128 ivs[SERPENT_PARALLEL_BLOCKS - 1];
198937c30d7SJussi Kivilinna 	u128 last_iv;
199937c30d7SJussi Kivilinna 	int i;
200937c30d7SJussi Kivilinna 
201937c30d7SJussi Kivilinna 	/* Start of the last block. */
202937c30d7SJussi Kivilinna 	src += nbytes / bsize - 1;
203937c30d7SJussi Kivilinna 	dst += nbytes / bsize - 1;
204937c30d7SJussi Kivilinna 
205937c30d7SJussi Kivilinna 	last_iv = *src;
206937c30d7SJussi Kivilinna 
207937c30d7SJussi Kivilinna 	/* Process multi-block batch */
208937c30d7SJussi Kivilinna 	if (nbytes >= bsize * SERPENT_PARALLEL_BLOCKS) {
209937c30d7SJussi Kivilinna 		do {
210937c30d7SJussi Kivilinna 			nbytes -= bsize * (SERPENT_PARALLEL_BLOCKS - 1);
211937c30d7SJussi Kivilinna 			src -= SERPENT_PARALLEL_BLOCKS - 1;
212937c30d7SJussi Kivilinna 			dst -= SERPENT_PARALLEL_BLOCKS - 1;
213937c30d7SJussi Kivilinna 
214937c30d7SJussi Kivilinna 			for (i = 0; i < SERPENT_PARALLEL_BLOCKS - 1; i++)
215937c30d7SJussi Kivilinna 				ivs[i] = src[i];
216937c30d7SJussi Kivilinna 
217937c30d7SJussi Kivilinna 			serpent_dec_blk_xway(ctx, (u8 *)dst, (u8 *)src);
218937c30d7SJussi Kivilinna 
219937c30d7SJussi Kivilinna 			for (i = 0; i < SERPENT_PARALLEL_BLOCKS - 1; i++)
220937c30d7SJussi Kivilinna 				u128_xor(dst + (i + 1), dst + (i + 1), ivs + i);
221937c30d7SJussi Kivilinna 
222937c30d7SJussi Kivilinna 			nbytes -= bsize;
223937c30d7SJussi Kivilinna 			if (nbytes < bsize)
224937c30d7SJussi Kivilinna 				goto done;
225937c30d7SJussi Kivilinna 
226937c30d7SJussi Kivilinna 			u128_xor(dst, dst, src - 1);
227937c30d7SJussi Kivilinna 			src -= 1;
228937c30d7SJussi Kivilinna 			dst -= 1;
229937c30d7SJussi Kivilinna 		} while (nbytes >= bsize * SERPENT_PARALLEL_BLOCKS);
230937c30d7SJussi Kivilinna 
231937c30d7SJussi Kivilinna 		if (nbytes < bsize)
232937c30d7SJussi Kivilinna 			goto done;
233937c30d7SJussi Kivilinna 	}
234937c30d7SJussi Kivilinna 
235937c30d7SJussi Kivilinna 	/* Handle leftovers */
236937c30d7SJussi Kivilinna 	for (;;) {
237937c30d7SJussi Kivilinna 		__serpent_decrypt(ctx, (u8 *)dst, (u8 *)src);
238937c30d7SJussi Kivilinna 
239937c30d7SJussi Kivilinna 		nbytes -= bsize;
240937c30d7SJussi Kivilinna 		if (nbytes < bsize)
241937c30d7SJussi Kivilinna 			break;
242937c30d7SJussi Kivilinna 
243937c30d7SJussi Kivilinna 		u128_xor(dst, dst, src - 1);
244937c30d7SJussi Kivilinna 		src -= 1;
245937c30d7SJussi Kivilinna 		dst -= 1;
246937c30d7SJussi Kivilinna 	}
247937c30d7SJussi Kivilinna 
248937c30d7SJussi Kivilinna done:
249937c30d7SJussi Kivilinna 	u128_xor(dst, dst, (u128 *)walk->iv);
250937c30d7SJussi Kivilinna 	*(u128 *)walk->iv = last_iv;
251937c30d7SJussi Kivilinna 
252937c30d7SJussi Kivilinna 	return nbytes;
253937c30d7SJussi Kivilinna }
254937c30d7SJussi Kivilinna 
255937c30d7SJussi Kivilinna static int cbc_decrypt(struct blkcipher_desc *desc, struct scatterlist *dst,
256937c30d7SJussi Kivilinna 		       struct scatterlist *src, unsigned int nbytes)
257937c30d7SJussi Kivilinna {
258937c30d7SJussi Kivilinna 	bool fpu_enabled = false;
259937c30d7SJussi Kivilinna 	struct blkcipher_walk walk;
260937c30d7SJussi Kivilinna 	int err;
261937c30d7SJussi Kivilinna 
262937c30d7SJussi Kivilinna 	blkcipher_walk_init(&walk, dst, src, nbytes);
263937c30d7SJussi Kivilinna 	err = blkcipher_walk_virt(desc, &walk);
264937c30d7SJussi Kivilinna 	desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP;
265937c30d7SJussi Kivilinna 
266937c30d7SJussi Kivilinna 	while ((nbytes = walk.nbytes)) {
267937c30d7SJussi Kivilinna 		fpu_enabled = serpent_fpu_begin(fpu_enabled, nbytes);
268937c30d7SJussi Kivilinna 		nbytes = __cbc_decrypt(desc, &walk);
269937c30d7SJussi Kivilinna 		err = blkcipher_walk_done(desc, &walk, nbytes);
270937c30d7SJussi Kivilinna 	}
271937c30d7SJussi Kivilinna 
272937c30d7SJussi Kivilinna 	serpent_fpu_end(fpu_enabled);
273937c30d7SJussi Kivilinna 	return err;
274937c30d7SJussi Kivilinna }
275937c30d7SJussi Kivilinna 
276937c30d7SJussi Kivilinna static inline void u128_to_be128(be128 *dst, const u128 *src)
277937c30d7SJussi Kivilinna {
278937c30d7SJussi Kivilinna 	dst->a = cpu_to_be64(src->a);
279937c30d7SJussi Kivilinna 	dst->b = cpu_to_be64(src->b);
280937c30d7SJussi Kivilinna }
281937c30d7SJussi Kivilinna 
282937c30d7SJussi Kivilinna static inline void be128_to_u128(u128 *dst, const be128 *src)
283937c30d7SJussi Kivilinna {
284937c30d7SJussi Kivilinna 	dst->a = be64_to_cpu(src->a);
285937c30d7SJussi Kivilinna 	dst->b = be64_to_cpu(src->b);
286937c30d7SJussi Kivilinna }
287937c30d7SJussi Kivilinna 
288937c30d7SJussi Kivilinna static inline void u128_inc(u128 *i)
289937c30d7SJussi Kivilinna {
290937c30d7SJussi Kivilinna 	i->b++;
291937c30d7SJussi Kivilinna 	if (!i->b)
292937c30d7SJussi Kivilinna 		i->a++;
293937c30d7SJussi Kivilinna }
294937c30d7SJussi Kivilinna 
295937c30d7SJussi Kivilinna static void ctr_crypt_final(struct blkcipher_desc *desc,
296937c30d7SJussi Kivilinna 			    struct blkcipher_walk *walk)
297937c30d7SJussi Kivilinna {
298937c30d7SJussi Kivilinna 	struct serpent_ctx *ctx = crypto_blkcipher_ctx(desc->tfm);
299937c30d7SJussi Kivilinna 	u8 *ctrblk = walk->iv;
300937c30d7SJussi Kivilinna 	u8 keystream[SERPENT_BLOCK_SIZE];
301937c30d7SJussi Kivilinna 	u8 *src = walk->src.virt.addr;
302937c30d7SJussi Kivilinna 	u8 *dst = walk->dst.virt.addr;
303937c30d7SJussi Kivilinna 	unsigned int nbytes = walk->nbytes;
304937c30d7SJussi Kivilinna 
305937c30d7SJussi Kivilinna 	__serpent_encrypt(ctx, keystream, ctrblk);
306937c30d7SJussi Kivilinna 	crypto_xor(keystream, src, nbytes);
307937c30d7SJussi Kivilinna 	memcpy(dst, keystream, nbytes);
308937c30d7SJussi Kivilinna 
309937c30d7SJussi Kivilinna 	crypto_inc(ctrblk, SERPENT_BLOCK_SIZE);
310937c30d7SJussi Kivilinna }
311937c30d7SJussi Kivilinna 
312937c30d7SJussi Kivilinna static unsigned int __ctr_crypt(struct blkcipher_desc *desc,
313937c30d7SJussi Kivilinna 				struct blkcipher_walk *walk)
314937c30d7SJussi Kivilinna {
315937c30d7SJussi Kivilinna 	struct serpent_ctx *ctx = crypto_blkcipher_ctx(desc->tfm);
316937c30d7SJussi Kivilinna 	const unsigned int bsize = SERPENT_BLOCK_SIZE;
317937c30d7SJussi Kivilinna 	unsigned int nbytes = walk->nbytes;
318937c30d7SJussi Kivilinna 	u128 *src = (u128 *)walk->src.virt.addr;
319937c30d7SJussi Kivilinna 	u128 *dst = (u128 *)walk->dst.virt.addr;
320937c30d7SJussi Kivilinna 	u128 ctrblk;
321937c30d7SJussi Kivilinna 	be128 ctrblocks[SERPENT_PARALLEL_BLOCKS];
322937c30d7SJussi Kivilinna 	int i;
323937c30d7SJussi Kivilinna 
324937c30d7SJussi Kivilinna 	be128_to_u128(&ctrblk, (be128 *)walk->iv);
325937c30d7SJussi Kivilinna 
326937c30d7SJussi Kivilinna 	/* Process multi-block batch */
327937c30d7SJussi Kivilinna 	if (nbytes >= bsize * SERPENT_PARALLEL_BLOCKS) {
328937c30d7SJussi Kivilinna 		do {
329937c30d7SJussi Kivilinna 			/* create ctrblks for parallel encrypt */
330937c30d7SJussi Kivilinna 			for (i = 0; i < SERPENT_PARALLEL_BLOCKS; i++) {
331937c30d7SJussi Kivilinna 				if (dst != src)
332937c30d7SJussi Kivilinna 					dst[i] = src[i];
333937c30d7SJussi Kivilinna 
334937c30d7SJussi Kivilinna 				u128_to_be128(&ctrblocks[i], &ctrblk);
335937c30d7SJussi Kivilinna 				u128_inc(&ctrblk);
336937c30d7SJussi Kivilinna 			}
337937c30d7SJussi Kivilinna 
338937c30d7SJussi Kivilinna 			serpent_enc_blk_xway_xor(ctx, (u8 *)dst,
339937c30d7SJussi Kivilinna 						 (u8 *)ctrblocks);
340937c30d7SJussi Kivilinna 
341937c30d7SJussi Kivilinna 			src += SERPENT_PARALLEL_BLOCKS;
342937c30d7SJussi Kivilinna 			dst += SERPENT_PARALLEL_BLOCKS;
343937c30d7SJussi Kivilinna 			nbytes -= bsize * SERPENT_PARALLEL_BLOCKS;
344937c30d7SJussi Kivilinna 		} while (nbytes >= bsize * SERPENT_PARALLEL_BLOCKS);
345937c30d7SJussi Kivilinna 
346937c30d7SJussi Kivilinna 		if (nbytes < bsize)
347937c30d7SJussi Kivilinna 			goto done;
348937c30d7SJussi Kivilinna 	}
349937c30d7SJussi Kivilinna 
350937c30d7SJussi Kivilinna 	/* Handle leftovers */
351937c30d7SJussi Kivilinna 	do {
352937c30d7SJussi Kivilinna 		if (dst != src)
353937c30d7SJussi Kivilinna 			*dst = *src;
354937c30d7SJussi Kivilinna 
355937c30d7SJussi Kivilinna 		u128_to_be128(&ctrblocks[0], &ctrblk);
356937c30d7SJussi Kivilinna 		u128_inc(&ctrblk);
357937c30d7SJussi Kivilinna 
358937c30d7SJussi Kivilinna 		__serpent_encrypt(ctx, (u8 *)ctrblocks, (u8 *)ctrblocks);
359937c30d7SJussi Kivilinna 		u128_xor(dst, dst, (u128 *)ctrblocks);
360937c30d7SJussi Kivilinna 
361937c30d7SJussi Kivilinna 		src += 1;
362937c30d7SJussi Kivilinna 		dst += 1;
363937c30d7SJussi Kivilinna 		nbytes -= bsize;
364937c30d7SJussi Kivilinna 	} while (nbytes >= bsize);
365937c30d7SJussi Kivilinna 
366937c30d7SJussi Kivilinna done:
367937c30d7SJussi Kivilinna 	u128_to_be128((be128 *)walk->iv, &ctrblk);
368937c30d7SJussi Kivilinna 	return nbytes;
369937c30d7SJussi Kivilinna }
370937c30d7SJussi Kivilinna 
371937c30d7SJussi Kivilinna static int ctr_crypt(struct blkcipher_desc *desc, struct scatterlist *dst,
372937c30d7SJussi Kivilinna 		     struct scatterlist *src, unsigned int nbytes)
373937c30d7SJussi Kivilinna {
374937c30d7SJussi Kivilinna 	bool fpu_enabled = false;
375937c30d7SJussi Kivilinna 	struct blkcipher_walk walk;
376937c30d7SJussi Kivilinna 	int err;
377937c30d7SJussi Kivilinna 
378937c30d7SJussi Kivilinna 	blkcipher_walk_init(&walk, dst, src, nbytes);
379937c30d7SJussi Kivilinna 	err = blkcipher_walk_virt_block(desc, &walk, SERPENT_BLOCK_SIZE);
380937c30d7SJussi Kivilinna 	desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP;
381937c30d7SJussi Kivilinna 
382937c30d7SJussi Kivilinna 	while ((nbytes = walk.nbytes) >= SERPENT_BLOCK_SIZE) {
383937c30d7SJussi Kivilinna 		fpu_enabled = serpent_fpu_begin(fpu_enabled, nbytes);
384937c30d7SJussi Kivilinna 		nbytes = __ctr_crypt(desc, &walk);
385937c30d7SJussi Kivilinna 		err = blkcipher_walk_done(desc, &walk, nbytes);
386937c30d7SJussi Kivilinna 	}
387937c30d7SJussi Kivilinna 
388937c30d7SJussi Kivilinna 	serpent_fpu_end(fpu_enabled);
389937c30d7SJussi Kivilinna 
390937c30d7SJussi Kivilinna 	if (walk.nbytes) {
391937c30d7SJussi Kivilinna 		ctr_crypt_final(desc, &walk);
392937c30d7SJussi Kivilinna 		err = blkcipher_walk_done(desc, &walk, 0);
393937c30d7SJussi Kivilinna 	}
394937c30d7SJussi Kivilinna 
395937c30d7SJussi Kivilinna 	return err;
396937c30d7SJussi Kivilinna }
397937c30d7SJussi Kivilinna 
39818482053SJussi Kivilinna struct crypt_priv {
39918482053SJussi Kivilinna 	struct serpent_ctx *ctx;
40018482053SJussi Kivilinna 	bool fpu_enabled;
40118482053SJussi Kivilinna };
40218482053SJussi Kivilinna 
40318482053SJussi Kivilinna static void encrypt_callback(void *priv, u8 *srcdst, unsigned int nbytes)
40418482053SJussi Kivilinna {
40518482053SJussi Kivilinna 	const unsigned int bsize = SERPENT_BLOCK_SIZE;
40618482053SJussi Kivilinna 	struct crypt_priv *ctx = priv;
40718482053SJussi Kivilinna 	int i;
40818482053SJussi Kivilinna 
40918482053SJussi Kivilinna 	ctx->fpu_enabled = serpent_fpu_begin(ctx->fpu_enabled, nbytes);
41018482053SJussi Kivilinna 
41118482053SJussi Kivilinna 	if (nbytes == bsize * SERPENT_PARALLEL_BLOCKS) {
41218482053SJussi Kivilinna 		serpent_enc_blk_xway(ctx->ctx, srcdst, srcdst);
41318482053SJussi Kivilinna 		return;
41418482053SJussi Kivilinna 	}
41518482053SJussi Kivilinna 
41618482053SJussi Kivilinna 	for (i = 0; i < nbytes / bsize; i++, srcdst += bsize)
41718482053SJussi Kivilinna 		__serpent_encrypt(ctx->ctx, srcdst, srcdst);
41818482053SJussi Kivilinna }
41918482053SJussi Kivilinna 
42018482053SJussi Kivilinna static void decrypt_callback(void *priv, u8 *srcdst, unsigned int nbytes)
42118482053SJussi Kivilinna {
42218482053SJussi Kivilinna 	const unsigned int bsize = SERPENT_BLOCK_SIZE;
42318482053SJussi Kivilinna 	struct crypt_priv *ctx = priv;
42418482053SJussi Kivilinna 	int i;
42518482053SJussi Kivilinna 
42618482053SJussi Kivilinna 	ctx->fpu_enabled = serpent_fpu_begin(ctx->fpu_enabled, nbytes);
42718482053SJussi Kivilinna 
42818482053SJussi Kivilinna 	if (nbytes == bsize * SERPENT_PARALLEL_BLOCKS) {
42918482053SJussi Kivilinna 		serpent_dec_blk_xway(ctx->ctx, srcdst, srcdst);
43018482053SJussi Kivilinna 		return;
43118482053SJussi Kivilinna 	}
43218482053SJussi Kivilinna 
43318482053SJussi Kivilinna 	for (i = 0; i < nbytes / bsize; i++, srcdst += bsize)
43418482053SJussi Kivilinna 		__serpent_decrypt(ctx->ctx, srcdst, srcdst);
43518482053SJussi Kivilinna }
43618482053SJussi Kivilinna 
43718482053SJussi Kivilinna struct serpent_lrw_ctx {
43818482053SJussi Kivilinna 	struct lrw_table_ctx lrw_table;
43918482053SJussi Kivilinna 	struct serpent_ctx serpent_ctx;
44018482053SJussi Kivilinna };
44118482053SJussi Kivilinna 
44218482053SJussi Kivilinna static int lrw_serpent_setkey(struct crypto_tfm *tfm, const u8 *key,
44318482053SJussi Kivilinna 			      unsigned int keylen)
44418482053SJussi Kivilinna {
44518482053SJussi Kivilinna 	struct serpent_lrw_ctx *ctx = crypto_tfm_ctx(tfm);
44618482053SJussi Kivilinna 	int err;
44718482053SJussi Kivilinna 
44818482053SJussi Kivilinna 	err = __serpent_setkey(&ctx->serpent_ctx, key, keylen -
44918482053SJussi Kivilinna 							SERPENT_BLOCK_SIZE);
45018482053SJussi Kivilinna 	if (err)
45118482053SJussi Kivilinna 		return err;
45218482053SJussi Kivilinna 
45318482053SJussi Kivilinna 	return lrw_init_table(&ctx->lrw_table, key + keylen -
45418482053SJussi Kivilinna 						SERPENT_BLOCK_SIZE);
45518482053SJussi Kivilinna }
45618482053SJussi Kivilinna 
45718482053SJussi Kivilinna static int lrw_encrypt(struct blkcipher_desc *desc, struct scatterlist *dst,
45818482053SJussi Kivilinna 		       struct scatterlist *src, unsigned int nbytes)
45918482053SJussi Kivilinna {
46018482053SJussi Kivilinna 	struct serpent_lrw_ctx *ctx = crypto_blkcipher_ctx(desc->tfm);
46118482053SJussi Kivilinna 	be128 buf[SERPENT_PARALLEL_BLOCKS];
46218482053SJussi Kivilinna 	struct crypt_priv crypt_ctx = {
46318482053SJussi Kivilinna 		.ctx = &ctx->serpent_ctx,
46418482053SJussi Kivilinna 		.fpu_enabled = false,
46518482053SJussi Kivilinna 	};
46618482053SJussi Kivilinna 	struct lrw_crypt_req req = {
46718482053SJussi Kivilinna 		.tbuf = buf,
46818482053SJussi Kivilinna 		.tbuflen = sizeof(buf),
46918482053SJussi Kivilinna 
47018482053SJussi Kivilinna 		.table_ctx = &ctx->lrw_table,
47118482053SJussi Kivilinna 		.crypt_ctx = &crypt_ctx,
47218482053SJussi Kivilinna 		.crypt_fn = encrypt_callback,
47318482053SJussi Kivilinna 	};
47418482053SJussi Kivilinna 	int ret;
47518482053SJussi Kivilinna 
476d3564338SJussi Kivilinna 	desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP;
47718482053SJussi Kivilinna 	ret = lrw_crypt(desc, dst, src, nbytes, &req);
47818482053SJussi Kivilinna 	serpent_fpu_end(crypt_ctx.fpu_enabled);
47918482053SJussi Kivilinna 
48018482053SJussi Kivilinna 	return ret;
48118482053SJussi Kivilinna }
48218482053SJussi Kivilinna 
48318482053SJussi Kivilinna static int lrw_decrypt(struct blkcipher_desc *desc, struct scatterlist *dst,
48418482053SJussi Kivilinna 		       struct scatterlist *src, unsigned int nbytes)
48518482053SJussi Kivilinna {
48618482053SJussi Kivilinna 	struct serpent_lrw_ctx *ctx = crypto_blkcipher_ctx(desc->tfm);
48718482053SJussi Kivilinna 	be128 buf[SERPENT_PARALLEL_BLOCKS];
48818482053SJussi Kivilinna 	struct crypt_priv crypt_ctx = {
48918482053SJussi Kivilinna 		.ctx = &ctx->serpent_ctx,
49018482053SJussi Kivilinna 		.fpu_enabled = false,
49118482053SJussi Kivilinna 	};
49218482053SJussi Kivilinna 	struct lrw_crypt_req req = {
49318482053SJussi Kivilinna 		.tbuf = buf,
49418482053SJussi Kivilinna 		.tbuflen = sizeof(buf),
49518482053SJussi Kivilinna 
49618482053SJussi Kivilinna 		.table_ctx = &ctx->lrw_table,
49718482053SJussi Kivilinna 		.crypt_ctx = &crypt_ctx,
49818482053SJussi Kivilinna 		.crypt_fn = decrypt_callback,
49918482053SJussi Kivilinna 	};
50018482053SJussi Kivilinna 	int ret;
50118482053SJussi Kivilinna 
502d3564338SJussi Kivilinna 	desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP;
50318482053SJussi Kivilinna 	ret = lrw_crypt(desc, dst, src, nbytes, &req);
50418482053SJussi Kivilinna 	serpent_fpu_end(crypt_ctx.fpu_enabled);
50518482053SJussi Kivilinna 
50618482053SJussi Kivilinna 	return ret;
50718482053SJussi Kivilinna }
50818482053SJussi Kivilinna 
50918482053SJussi Kivilinna static void lrw_exit_tfm(struct crypto_tfm *tfm)
51018482053SJussi Kivilinna {
51118482053SJussi Kivilinna 	struct serpent_lrw_ctx *ctx = crypto_tfm_ctx(tfm);
51218482053SJussi Kivilinna 
51318482053SJussi Kivilinna 	lrw_free_table(&ctx->lrw_table);
51418482053SJussi Kivilinna }
51518482053SJussi Kivilinna 
5165962f8b6SJussi Kivilinna struct serpent_xts_ctx {
5175962f8b6SJussi Kivilinna 	struct serpent_ctx tweak_ctx;
5185962f8b6SJussi Kivilinna 	struct serpent_ctx crypt_ctx;
5195962f8b6SJussi Kivilinna };
5205962f8b6SJussi Kivilinna 
5215962f8b6SJussi Kivilinna static int xts_serpent_setkey(struct crypto_tfm *tfm, const u8 *key,
5225962f8b6SJussi Kivilinna 			      unsigned int keylen)
5235962f8b6SJussi Kivilinna {
5245962f8b6SJussi Kivilinna 	struct serpent_xts_ctx *ctx = crypto_tfm_ctx(tfm);
5255962f8b6SJussi Kivilinna 	u32 *flags = &tfm->crt_flags;
5265962f8b6SJussi Kivilinna 	int err;
5275962f8b6SJussi Kivilinna 
5285962f8b6SJussi Kivilinna 	/* key consists of keys of equal size concatenated, therefore
5295962f8b6SJussi Kivilinna 	 * the length must be even
5305962f8b6SJussi Kivilinna 	 */
5315962f8b6SJussi Kivilinna 	if (keylen % 2) {
5325962f8b6SJussi Kivilinna 		*flags |= CRYPTO_TFM_RES_BAD_KEY_LEN;
5335962f8b6SJussi Kivilinna 		return -EINVAL;
5345962f8b6SJussi Kivilinna 	}
5355962f8b6SJussi Kivilinna 
5365962f8b6SJussi Kivilinna 	/* first half of xts-key is for crypt */
5375962f8b6SJussi Kivilinna 	err = __serpent_setkey(&ctx->crypt_ctx, key, keylen / 2);
5385962f8b6SJussi Kivilinna 	if (err)
5395962f8b6SJussi Kivilinna 		return err;
5405962f8b6SJussi Kivilinna 
5415962f8b6SJussi Kivilinna 	/* second half of xts-key is for tweak */
5425962f8b6SJussi Kivilinna 	return __serpent_setkey(&ctx->tweak_ctx, key + keylen / 2, keylen / 2);
5435962f8b6SJussi Kivilinna }
5445962f8b6SJussi Kivilinna 
5455962f8b6SJussi Kivilinna static int xts_encrypt(struct blkcipher_desc *desc, struct scatterlist *dst,
5465962f8b6SJussi Kivilinna 		       struct scatterlist *src, unsigned int nbytes)
5475962f8b6SJussi Kivilinna {
5485962f8b6SJussi Kivilinna 	struct serpent_xts_ctx *ctx = crypto_blkcipher_ctx(desc->tfm);
5495962f8b6SJussi Kivilinna 	be128 buf[SERPENT_PARALLEL_BLOCKS];
5505962f8b6SJussi Kivilinna 	struct crypt_priv crypt_ctx = {
5515962f8b6SJussi Kivilinna 		.ctx = &ctx->crypt_ctx,
5525962f8b6SJussi Kivilinna 		.fpu_enabled = false,
5535962f8b6SJussi Kivilinna 	};
5545962f8b6SJussi Kivilinna 	struct xts_crypt_req req = {
5555962f8b6SJussi Kivilinna 		.tbuf = buf,
5565962f8b6SJussi Kivilinna 		.tbuflen = sizeof(buf),
5575962f8b6SJussi Kivilinna 
5585962f8b6SJussi Kivilinna 		.tweak_ctx = &ctx->tweak_ctx,
5595962f8b6SJussi Kivilinna 		.tweak_fn = XTS_TWEAK_CAST(__serpent_encrypt),
5605962f8b6SJussi Kivilinna 		.crypt_ctx = &crypt_ctx,
5615962f8b6SJussi Kivilinna 		.crypt_fn = encrypt_callback,
5625962f8b6SJussi Kivilinna 	};
5635962f8b6SJussi Kivilinna 	int ret;
5645962f8b6SJussi Kivilinna 
565d3564338SJussi Kivilinna 	desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP;
5665962f8b6SJussi Kivilinna 	ret = xts_crypt(desc, dst, src, nbytes, &req);
5675962f8b6SJussi Kivilinna 	serpent_fpu_end(crypt_ctx.fpu_enabled);
5685962f8b6SJussi Kivilinna 
5695962f8b6SJussi Kivilinna 	return ret;
5705962f8b6SJussi Kivilinna }
5715962f8b6SJussi Kivilinna 
5725962f8b6SJussi Kivilinna static int xts_decrypt(struct blkcipher_desc *desc, struct scatterlist *dst,
5735962f8b6SJussi Kivilinna 		       struct scatterlist *src, unsigned int nbytes)
5745962f8b6SJussi Kivilinna {
5755962f8b6SJussi Kivilinna 	struct serpent_xts_ctx *ctx = crypto_blkcipher_ctx(desc->tfm);
5765962f8b6SJussi Kivilinna 	be128 buf[SERPENT_PARALLEL_BLOCKS];
5775962f8b6SJussi Kivilinna 	struct crypt_priv crypt_ctx = {
5785962f8b6SJussi Kivilinna 		.ctx = &ctx->crypt_ctx,
5795962f8b6SJussi Kivilinna 		.fpu_enabled = false,
5805962f8b6SJussi Kivilinna 	};
5815962f8b6SJussi Kivilinna 	struct xts_crypt_req req = {
5825962f8b6SJussi Kivilinna 		.tbuf = buf,
5835962f8b6SJussi Kivilinna 		.tbuflen = sizeof(buf),
5845962f8b6SJussi Kivilinna 
5855962f8b6SJussi Kivilinna 		.tweak_ctx = &ctx->tweak_ctx,
5865962f8b6SJussi Kivilinna 		.tweak_fn = XTS_TWEAK_CAST(__serpent_encrypt),
5875962f8b6SJussi Kivilinna 		.crypt_ctx = &crypt_ctx,
5885962f8b6SJussi Kivilinna 		.crypt_fn = decrypt_callback,
5895962f8b6SJussi Kivilinna 	};
5905962f8b6SJussi Kivilinna 	int ret;
5915962f8b6SJussi Kivilinna 
592d3564338SJussi Kivilinna 	desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP;
5935962f8b6SJussi Kivilinna 	ret = xts_crypt(desc, dst, src, nbytes, &req);
5945962f8b6SJussi Kivilinna 	serpent_fpu_end(crypt_ctx.fpu_enabled);
5955962f8b6SJussi Kivilinna 
5965962f8b6SJussi Kivilinna 	return ret;
5975962f8b6SJussi Kivilinna }
5985962f8b6SJussi Kivilinna 
599937c30d7SJussi Kivilinna static int ablk_set_key(struct crypto_ablkcipher *tfm, const u8 *key,
600937c30d7SJussi Kivilinna 			unsigned int key_len)
601937c30d7SJussi Kivilinna {
602937c30d7SJussi Kivilinna 	struct async_serpent_ctx *ctx = crypto_ablkcipher_ctx(tfm);
603937c30d7SJussi Kivilinna 	struct crypto_ablkcipher *child = &ctx->cryptd_tfm->base;
604937c30d7SJussi Kivilinna 	int err;
605937c30d7SJussi Kivilinna 
606937c30d7SJussi Kivilinna 	crypto_ablkcipher_clear_flags(child, CRYPTO_TFM_REQ_MASK);
607937c30d7SJussi Kivilinna 	crypto_ablkcipher_set_flags(child, crypto_ablkcipher_get_flags(tfm)
608937c30d7SJussi Kivilinna 				    & CRYPTO_TFM_REQ_MASK);
609937c30d7SJussi Kivilinna 	err = crypto_ablkcipher_setkey(child, key, key_len);
610937c30d7SJussi Kivilinna 	crypto_ablkcipher_set_flags(tfm, crypto_ablkcipher_get_flags(child)
611937c30d7SJussi Kivilinna 				    & CRYPTO_TFM_RES_MASK);
612937c30d7SJussi Kivilinna 	return err;
613937c30d7SJussi Kivilinna }
614937c30d7SJussi Kivilinna 
615937c30d7SJussi Kivilinna static int __ablk_encrypt(struct ablkcipher_request *req)
616937c30d7SJussi Kivilinna {
617937c30d7SJussi Kivilinna 	struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(req);
618937c30d7SJussi Kivilinna 	struct async_serpent_ctx *ctx = crypto_ablkcipher_ctx(tfm);
619937c30d7SJussi Kivilinna 	struct blkcipher_desc desc;
620937c30d7SJussi Kivilinna 
621937c30d7SJussi Kivilinna 	desc.tfm = cryptd_ablkcipher_child(ctx->cryptd_tfm);
622937c30d7SJussi Kivilinna 	desc.info = req->info;
623937c30d7SJussi Kivilinna 	desc.flags = 0;
624937c30d7SJussi Kivilinna 
625937c30d7SJussi Kivilinna 	return crypto_blkcipher_crt(desc.tfm)->encrypt(
626937c30d7SJussi Kivilinna 		&desc, req->dst, req->src, req->nbytes);
627937c30d7SJussi Kivilinna }
628937c30d7SJussi Kivilinna 
629937c30d7SJussi Kivilinna static int ablk_encrypt(struct ablkcipher_request *req)
630937c30d7SJussi Kivilinna {
631937c30d7SJussi Kivilinna 	struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(req);
632937c30d7SJussi Kivilinna 	struct async_serpent_ctx *ctx = crypto_ablkcipher_ctx(tfm);
633937c30d7SJussi Kivilinna 
634937c30d7SJussi Kivilinna 	if (!irq_fpu_usable()) {
635937c30d7SJussi Kivilinna 		struct ablkcipher_request *cryptd_req =
636937c30d7SJussi Kivilinna 			ablkcipher_request_ctx(req);
637937c30d7SJussi Kivilinna 
638937c30d7SJussi Kivilinna 		memcpy(cryptd_req, req, sizeof(*req));
639937c30d7SJussi Kivilinna 		ablkcipher_request_set_tfm(cryptd_req, &ctx->cryptd_tfm->base);
640937c30d7SJussi Kivilinna 
641937c30d7SJussi Kivilinna 		return crypto_ablkcipher_encrypt(cryptd_req);
642937c30d7SJussi Kivilinna 	} else {
643937c30d7SJussi Kivilinna 		return __ablk_encrypt(req);
644937c30d7SJussi Kivilinna 	}
645937c30d7SJussi Kivilinna }
646937c30d7SJussi Kivilinna 
647937c30d7SJussi Kivilinna static int ablk_decrypt(struct ablkcipher_request *req)
648937c30d7SJussi Kivilinna {
649937c30d7SJussi Kivilinna 	struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(req);
650937c30d7SJussi Kivilinna 	struct async_serpent_ctx *ctx = crypto_ablkcipher_ctx(tfm);
651937c30d7SJussi Kivilinna 
652937c30d7SJussi Kivilinna 	if (!irq_fpu_usable()) {
653937c30d7SJussi Kivilinna 		struct ablkcipher_request *cryptd_req =
654937c30d7SJussi Kivilinna 			ablkcipher_request_ctx(req);
655937c30d7SJussi Kivilinna 
656937c30d7SJussi Kivilinna 		memcpy(cryptd_req, req, sizeof(*req));
657937c30d7SJussi Kivilinna 		ablkcipher_request_set_tfm(cryptd_req, &ctx->cryptd_tfm->base);
658937c30d7SJussi Kivilinna 
659937c30d7SJussi Kivilinna 		return crypto_ablkcipher_decrypt(cryptd_req);
660937c30d7SJussi Kivilinna 	} else {
661937c30d7SJussi Kivilinna 		struct blkcipher_desc desc;
662937c30d7SJussi Kivilinna 
663937c30d7SJussi Kivilinna 		desc.tfm = cryptd_ablkcipher_child(ctx->cryptd_tfm);
664937c30d7SJussi Kivilinna 		desc.info = req->info;
665937c30d7SJussi Kivilinna 		desc.flags = 0;
666937c30d7SJussi Kivilinna 
667937c30d7SJussi Kivilinna 		return crypto_blkcipher_crt(desc.tfm)->decrypt(
668937c30d7SJussi Kivilinna 			&desc, req->dst, req->src, req->nbytes);
669937c30d7SJussi Kivilinna 	}
670937c30d7SJussi Kivilinna }
671937c30d7SJussi Kivilinna 
672937c30d7SJussi Kivilinna static void ablk_exit(struct crypto_tfm *tfm)
673937c30d7SJussi Kivilinna {
674937c30d7SJussi Kivilinna 	struct async_serpent_ctx *ctx = crypto_tfm_ctx(tfm);
675937c30d7SJussi Kivilinna 
676937c30d7SJussi Kivilinna 	cryptd_free_ablkcipher(ctx->cryptd_tfm);
677937c30d7SJussi Kivilinna }
678937c30d7SJussi Kivilinna 
679435d3e51SJussi Kivilinna static int ablk_init(struct crypto_tfm *tfm)
680937c30d7SJussi Kivilinna {
681937c30d7SJussi Kivilinna 	struct async_serpent_ctx *ctx = crypto_tfm_ctx(tfm);
682435d3e51SJussi Kivilinna 	struct cryptd_ablkcipher *cryptd_tfm;
683435d3e51SJussi Kivilinna 	char drv_name[CRYPTO_MAX_ALG_NAME];
684435d3e51SJussi Kivilinna 
685435d3e51SJussi Kivilinna 	snprintf(drv_name, sizeof(drv_name), "__driver-%s",
686435d3e51SJussi Kivilinna 					crypto_tfm_alg_driver_name(tfm));
687435d3e51SJussi Kivilinna 
688435d3e51SJussi Kivilinna 	cryptd_tfm = cryptd_alloc_ablkcipher(drv_name, 0, 0);
689435d3e51SJussi Kivilinna 	if (IS_ERR(cryptd_tfm))
690435d3e51SJussi Kivilinna 		return PTR_ERR(cryptd_tfm);
691937c30d7SJussi Kivilinna 
692937c30d7SJussi Kivilinna 	ctx->cryptd_tfm = cryptd_tfm;
693937c30d7SJussi Kivilinna 	tfm->crt_ablkcipher.reqsize = sizeof(struct ablkcipher_request) +
694937c30d7SJussi Kivilinna 		crypto_ablkcipher_reqsize(&cryptd_tfm->base);
695937c30d7SJussi Kivilinna 
69635474c3bSJussi Kivilinna 	return 0;
69735474c3bSJussi Kivilinna }
69835474c3bSJussi Kivilinna 
69935474c3bSJussi Kivilinna static struct crypto_alg serpent_algs[10] = { {
70035474c3bSJussi Kivilinna 	.cra_name		= "__ecb-serpent-sse2",
70135474c3bSJussi Kivilinna 	.cra_driver_name	= "__driver-ecb-serpent-sse2",
70235474c3bSJussi Kivilinna 	.cra_priority		= 0,
70335474c3bSJussi Kivilinna 	.cra_flags		= CRYPTO_ALG_TYPE_BLKCIPHER,
70435474c3bSJussi Kivilinna 	.cra_blocksize		= SERPENT_BLOCK_SIZE,
70535474c3bSJussi Kivilinna 	.cra_ctxsize		= sizeof(struct serpent_ctx),
70635474c3bSJussi Kivilinna 	.cra_alignmask		= 0,
70735474c3bSJussi Kivilinna 	.cra_type		= &crypto_blkcipher_type,
70835474c3bSJussi Kivilinna 	.cra_module		= THIS_MODULE,
70935474c3bSJussi Kivilinna 	.cra_list		= LIST_HEAD_INIT(serpent_algs[0].cra_list),
71035474c3bSJussi Kivilinna 	.cra_u = {
71135474c3bSJussi Kivilinna 		.blkcipher = {
71235474c3bSJussi Kivilinna 			.min_keysize	= SERPENT_MIN_KEY_SIZE,
71335474c3bSJussi Kivilinna 			.max_keysize	= SERPENT_MAX_KEY_SIZE,
71435474c3bSJussi Kivilinna 			.setkey		= serpent_setkey,
71535474c3bSJussi Kivilinna 			.encrypt	= ecb_encrypt,
71635474c3bSJussi Kivilinna 			.decrypt	= ecb_decrypt,
71735474c3bSJussi Kivilinna 		},
71835474c3bSJussi Kivilinna 	},
71935474c3bSJussi Kivilinna }, {
72035474c3bSJussi Kivilinna 	.cra_name		= "__cbc-serpent-sse2",
72135474c3bSJussi Kivilinna 	.cra_driver_name	= "__driver-cbc-serpent-sse2",
72235474c3bSJussi Kivilinna 	.cra_priority		= 0,
72335474c3bSJussi Kivilinna 	.cra_flags		= CRYPTO_ALG_TYPE_BLKCIPHER,
72435474c3bSJussi Kivilinna 	.cra_blocksize		= SERPENT_BLOCK_SIZE,
72535474c3bSJussi Kivilinna 	.cra_ctxsize		= sizeof(struct serpent_ctx),
72635474c3bSJussi Kivilinna 	.cra_alignmask		= 0,
72735474c3bSJussi Kivilinna 	.cra_type		= &crypto_blkcipher_type,
72835474c3bSJussi Kivilinna 	.cra_module		= THIS_MODULE,
72935474c3bSJussi Kivilinna 	.cra_list		= LIST_HEAD_INIT(serpent_algs[1].cra_list),
73035474c3bSJussi Kivilinna 	.cra_u = {
73135474c3bSJussi Kivilinna 		.blkcipher = {
73235474c3bSJussi Kivilinna 			.min_keysize	= SERPENT_MIN_KEY_SIZE,
73335474c3bSJussi Kivilinna 			.max_keysize	= SERPENT_MAX_KEY_SIZE,
73435474c3bSJussi Kivilinna 			.setkey		= serpent_setkey,
73535474c3bSJussi Kivilinna 			.encrypt	= cbc_encrypt,
73635474c3bSJussi Kivilinna 			.decrypt	= cbc_decrypt,
73735474c3bSJussi Kivilinna 		},
73835474c3bSJussi Kivilinna 	},
73935474c3bSJussi Kivilinna }, {
74035474c3bSJussi Kivilinna 	.cra_name		= "__ctr-serpent-sse2",
74135474c3bSJussi Kivilinna 	.cra_driver_name	= "__driver-ctr-serpent-sse2",
74235474c3bSJussi Kivilinna 	.cra_priority		= 0,
74335474c3bSJussi Kivilinna 	.cra_flags		= CRYPTO_ALG_TYPE_BLKCIPHER,
74435474c3bSJussi Kivilinna 	.cra_blocksize		= 1,
74535474c3bSJussi Kivilinna 	.cra_ctxsize		= sizeof(struct serpent_ctx),
74635474c3bSJussi Kivilinna 	.cra_alignmask		= 0,
74735474c3bSJussi Kivilinna 	.cra_type		= &crypto_blkcipher_type,
74835474c3bSJussi Kivilinna 	.cra_module		= THIS_MODULE,
74935474c3bSJussi Kivilinna 	.cra_list		= LIST_HEAD_INIT(serpent_algs[2].cra_list),
75035474c3bSJussi Kivilinna 	.cra_u = {
75135474c3bSJussi Kivilinna 		.blkcipher = {
75235474c3bSJussi Kivilinna 			.min_keysize	= SERPENT_MIN_KEY_SIZE,
75335474c3bSJussi Kivilinna 			.max_keysize	= SERPENT_MAX_KEY_SIZE,
75435474c3bSJussi Kivilinna 			.ivsize		= SERPENT_BLOCK_SIZE,
75535474c3bSJussi Kivilinna 			.setkey		= serpent_setkey,
75635474c3bSJussi Kivilinna 			.encrypt	= ctr_crypt,
75735474c3bSJussi Kivilinna 			.decrypt	= ctr_crypt,
75835474c3bSJussi Kivilinna 		},
75935474c3bSJussi Kivilinna 	},
76035474c3bSJussi Kivilinna }, {
76135474c3bSJussi Kivilinna 	.cra_name		= "__lrw-serpent-sse2",
76235474c3bSJussi Kivilinna 	.cra_driver_name	= "__driver-lrw-serpent-sse2",
76335474c3bSJussi Kivilinna 	.cra_priority		= 0,
76435474c3bSJussi Kivilinna 	.cra_flags		= CRYPTO_ALG_TYPE_BLKCIPHER,
76535474c3bSJussi Kivilinna 	.cra_blocksize		= SERPENT_BLOCK_SIZE,
76635474c3bSJussi Kivilinna 	.cra_ctxsize		= sizeof(struct serpent_lrw_ctx),
76735474c3bSJussi Kivilinna 	.cra_alignmask		= 0,
76835474c3bSJussi Kivilinna 	.cra_type		= &crypto_blkcipher_type,
76935474c3bSJussi Kivilinna 	.cra_module		= THIS_MODULE,
77035474c3bSJussi Kivilinna 	.cra_list		= LIST_HEAD_INIT(serpent_algs[3].cra_list),
77135474c3bSJussi Kivilinna 	.cra_exit		= lrw_exit_tfm,
77235474c3bSJussi Kivilinna 	.cra_u = {
77335474c3bSJussi Kivilinna 		.blkcipher = {
77435474c3bSJussi Kivilinna 			.min_keysize	= SERPENT_MIN_KEY_SIZE +
77535474c3bSJussi Kivilinna 					  SERPENT_BLOCK_SIZE,
77635474c3bSJussi Kivilinna 			.max_keysize	= SERPENT_MAX_KEY_SIZE +
77735474c3bSJussi Kivilinna 					  SERPENT_BLOCK_SIZE,
77835474c3bSJussi Kivilinna 			.ivsize		= SERPENT_BLOCK_SIZE,
77935474c3bSJussi Kivilinna 			.setkey		= lrw_serpent_setkey,
78035474c3bSJussi Kivilinna 			.encrypt	= lrw_encrypt,
78135474c3bSJussi Kivilinna 			.decrypt	= lrw_decrypt,
78235474c3bSJussi Kivilinna 		},
78335474c3bSJussi Kivilinna 	},
78435474c3bSJussi Kivilinna }, {
78535474c3bSJussi Kivilinna 	.cra_name		= "__xts-serpent-sse2",
78635474c3bSJussi Kivilinna 	.cra_driver_name	= "__driver-xts-serpent-sse2",
78735474c3bSJussi Kivilinna 	.cra_priority		= 0,
78835474c3bSJussi Kivilinna 	.cra_flags		= CRYPTO_ALG_TYPE_BLKCIPHER,
78935474c3bSJussi Kivilinna 	.cra_blocksize		= SERPENT_BLOCK_SIZE,
79035474c3bSJussi Kivilinna 	.cra_ctxsize		= sizeof(struct serpent_xts_ctx),
79135474c3bSJussi Kivilinna 	.cra_alignmask		= 0,
79235474c3bSJussi Kivilinna 	.cra_type		= &crypto_blkcipher_type,
79335474c3bSJussi Kivilinna 	.cra_module		= THIS_MODULE,
79435474c3bSJussi Kivilinna 	.cra_list		= LIST_HEAD_INIT(serpent_algs[4].cra_list),
79535474c3bSJussi Kivilinna 	.cra_u = {
79635474c3bSJussi Kivilinna 		.blkcipher = {
79735474c3bSJussi Kivilinna 			.min_keysize	= SERPENT_MIN_KEY_SIZE * 2,
79835474c3bSJussi Kivilinna 			.max_keysize	= SERPENT_MAX_KEY_SIZE * 2,
79935474c3bSJussi Kivilinna 			.ivsize		= SERPENT_BLOCK_SIZE,
80035474c3bSJussi Kivilinna 			.setkey		= xts_serpent_setkey,
80135474c3bSJussi Kivilinna 			.encrypt	= xts_encrypt,
80235474c3bSJussi Kivilinna 			.decrypt	= xts_decrypt,
80335474c3bSJussi Kivilinna 		},
80435474c3bSJussi Kivilinna 	},
80535474c3bSJussi Kivilinna }, {
80635474c3bSJussi Kivilinna 	.cra_name		= "ecb(serpent)",
80735474c3bSJussi Kivilinna 	.cra_driver_name	= "ecb-serpent-sse2",
80835474c3bSJussi Kivilinna 	.cra_priority		= 400,
80935474c3bSJussi Kivilinna 	.cra_flags		= CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
81035474c3bSJussi Kivilinna 	.cra_blocksize		= SERPENT_BLOCK_SIZE,
81135474c3bSJussi Kivilinna 	.cra_ctxsize		= sizeof(struct async_serpent_ctx),
81235474c3bSJussi Kivilinna 	.cra_alignmask		= 0,
81335474c3bSJussi Kivilinna 	.cra_type		= &crypto_ablkcipher_type,
81435474c3bSJussi Kivilinna 	.cra_module		= THIS_MODULE,
81535474c3bSJussi Kivilinna 	.cra_list		= LIST_HEAD_INIT(serpent_algs[5].cra_list),
816435d3e51SJussi Kivilinna 	.cra_init		= ablk_init,
81735474c3bSJussi Kivilinna 	.cra_exit		= ablk_exit,
81835474c3bSJussi Kivilinna 	.cra_u = {
81935474c3bSJussi Kivilinna 		.ablkcipher = {
82035474c3bSJussi Kivilinna 			.min_keysize	= SERPENT_MIN_KEY_SIZE,
82135474c3bSJussi Kivilinna 			.max_keysize	= SERPENT_MAX_KEY_SIZE,
82235474c3bSJussi Kivilinna 			.setkey		= ablk_set_key,
82335474c3bSJussi Kivilinna 			.encrypt	= ablk_encrypt,
82435474c3bSJussi Kivilinna 			.decrypt	= ablk_decrypt,
82535474c3bSJussi Kivilinna 		},
82635474c3bSJussi Kivilinna 	},
82735474c3bSJussi Kivilinna }, {
82835474c3bSJussi Kivilinna 	.cra_name		= "cbc(serpent)",
82935474c3bSJussi Kivilinna 	.cra_driver_name	= "cbc-serpent-sse2",
83035474c3bSJussi Kivilinna 	.cra_priority		= 400,
83135474c3bSJussi Kivilinna 	.cra_flags		= CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
83235474c3bSJussi Kivilinna 	.cra_blocksize		= SERPENT_BLOCK_SIZE,
83335474c3bSJussi Kivilinna 	.cra_ctxsize		= sizeof(struct async_serpent_ctx),
83435474c3bSJussi Kivilinna 	.cra_alignmask		= 0,
83535474c3bSJussi Kivilinna 	.cra_type		= &crypto_ablkcipher_type,
83635474c3bSJussi Kivilinna 	.cra_module		= THIS_MODULE,
83735474c3bSJussi Kivilinna 	.cra_list		= LIST_HEAD_INIT(serpent_algs[6].cra_list),
838435d3e51SJussi Kivilinna 	.cra_init		= ablk_init,
83935474c3bSJussi Kivilinna 	.cra_exit		= ablk_exit,
84035474c3bSJussi Kivilinna 	.cra_u = {
84135474c3bSJussi Kivilinna 		.ablkcipher = {
84235474c3bSJussi Kivilinna 			.min_keysize	= SERPENT_MIN_KEY_SIZE,
84335474c3bSJussi Kivilinna 			.max_keysize	= SERPENT_MAX_KEY_SIZE,
84435474c3bSJussi Kivilinna 			.ivsize		= SERPENT_BLOCK_SIZE,
84535474c3bSJussi Kivilinna 			.setkey		= ablk_set_key,
84635474c3bSJussi Kivilinna 			.encrypt	= __ablk_encrypt,
84735474c3bSJussi Kivilinna 			.decrypt	= ablk_decrypt,
84835474c3bSJussi Kivilinna 		},
84935474c3bSJussi Kivilinna 	},
85035474c3bSJussi Kivilinna }, {
851937c30d7SJussi Kivilinna 	.cra_name		= "ctr(serpent)",
852937c30d7SJussi Kivilinna 	.cra_driver_name	= "ctr-serpent-sse2",
853937c30d7SJussi Kivilinna 	.cra_priority		= 400,
854937c30d7SJussi Kivilinna 	.cra_flags		= CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
855937c30d7SJussi Kivilinna 	.cra_blocksize		= 1,
856937c30d7SJussi Kivilinna 	.cra_ctxsize		= sizeof(struct async_serpent_ctx),
857937c30d7SJussi Kivilinna 	.cra_alignmask		= 0,
858937c30d7SJussi Kivilinna 	.cra_type		= &crypto_ablkcipher_type,
859937c30d7SJussi Kivilinna 	.cra_module		= THIS_MODULE,
86035474c3bSJussi Kivilinna 	.cra_list		= LIST_HEAD_INIT(serpent_algs[7].cra_list),
861435d3e51SJussi Kivilinna 	.cra_init		= ablk_init,
862937c30d7SJussi Kivilinna 	.cra_exit		= ablk_exit,
863937c30d7SJussi Kivilinna 	.cra_u = {
864937c30d7SJussi Kivilinna 		.ablkcipher = {
865937c30d7SJussi Kivilinna 			.min_keysize	= SERPENT_MIN_KEY_SIZE,
866937c30d7SJussi Kivilinna 			.max_keysize	= SERPENT_MAX_KEY_SIZE,
867937c30d7SJussi Kivilinna 			.ivsize		= SERPENT_BLOCK_SIZE,
868937c30d7SJussi Kivilinna 			.setkey		= ablk_set_key,
869937c30d7SJussi Kivilinna 			.encrypt	= ablk_encrypt,
870937c30d7SJussi Kivilinna 			.decrypt	= ablk_encrypt,
871937c30d7SJussi Kivilinna 			.geniv		= "chainiv",
872937c30d7SJussi Kivilinna 		},
873937c30d7SJussi Kivilinna 	},
87435474c3bSJussi Kivilinna }, {
87518482053SJussi Kivilinna 	.cra_name		= "lrw(serpent)",
87618482053SJussi Kivilinna 	.cra_driver_name	= "lrw-serpent-sse2",
87718482053SJussi Kivilinna 	.cra_priority		= 400,
87818482053SJussi Kivilinna 	.cra_flags		= CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
87918482053SJussi Kivilinna 	.cra_blocksize		= SERPENT_BLOCK_SIZE,
88018482053SJussi Kivilinna 	.cra_ctxsize		= sizeof(struct async_serpent_ctx),
88118482053SJussi Kivilinna 	.cra_alignmask		= 0,
88218482053SJussi Kivilinna 	.cra_type		= &crypto_ablkcipher_type,
88318482053SJussi Kivilinna 	.cra_module		= THIS_MODULE,
88435474c3bSJussi Kivilinna 	.cra_list		= LIST_HEAD_INIT(serpent_algs[8].cra_list),
885435d3e51SJussi Kivilinna 	.cra_init		= ablk_init,
88618482053SJussi Kivilinna 	.cra_exit		= ablk_exit,
88718482053SJussi Kivilinna 	.cra_u = {
88818482053SJussi Kivilinna 		.ablkcipher = {
88918482053SJussi Kivilinna 			.min_keysize	= SERPENT_MIN_KEY_SIZE +
89018482053SJussi Kivilinna 					  SERPENT_BLOCK_SIZE,
89118482053SJussi Kivilinna 			.max_keysize	= SERPENT_MAX_KEY_SIZE +
89218482053SJussi Kivilinna 					  SERPENT_BLOCK_SIZE,
89318482053SJussi Kivilinna 			.ivsize		= SERPENT_BLOCK_SIZE,
89418482053SJussi Kivilinna 			.setkey		= ablk_set_key,
89518482053SJussi Kivilinna 			.encrypt	= ablk_encrypt,
89618482053SJussi Kivilinna 			.decrypt	= ablk_decrypt,
89718482053SJussi Kivilinna 		},
89818482053SJussi Kivilinna 	},
89935474c3bSJussi Kivilinna }, {
9005962f8b6SJussi Kivilinna 	.cra_name		= "xts(serpent)",
9015962f8b6SJussi Kivilinna 	.cra_driver_name	= "xts-serpent-sse2",
9025962f8b6SJussi Kivilinna 	.cra_priority		= 400,
9035962f8b6SJussi Kivilinna 	.cra_flags		= CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
9045962f8b6SJussi Kivilinna 	.cra_blocksize		= SERPENT_BLOCK_SIZE,
9055962f8b6SJussi Kivilinna 	.cra_ctxsize		= sizeof(struct async_serpent_ctx),
9065962f8b6SJussi Kivilinna 	.cra_alignmask		= 0,
9075962f8b6SJussi Kivilinna 	.cra_type		= &crypto_ablkcipher_type,
9085962f8b6SJussi Kivilinna 	.cra_module		= THIS_MODULE,
90935474c3bSJussi Kivilinna 	.cra_list		= LIST_HEAD_INIT(serpent_algs[9].cra_list),
910435d3e51SJussi Kivilinna 	.cra_init		= ablk_init,
9115962f8b6SJussi Kivilinna 	.cra_exit		= ablk_exit,
9125962f8b6SJussi Kivilinna 	.cra_u = {
9135962f8b6SJussi Kivilinna 		.ablkcipher = {
9145962f8b6SJussi Kivilinna 			.min_keysize	= SERPENT_MIN_KEY_SIZE * 2,
9155962f8b6SJussi Kivilinna 			.max_keysize	= SERPENT_MAX_KEY_SIZE * 2,
9165962f8b6SJussi Kivilinna 			.ivsize		= SERPENT_BLOCK_SIZE,
9175962f8b6SJussi Kivilinna 			.setkey		= ablk_set_key,
9185962f8b6SJussi Kivilinna 			.encrypt	= ablk_encrypt,
9195962f8b6SJussi Kivilinna 			.decrypt	= ablk_decrypt,
9205962f8b6SJussi Kivilinna 		},
9215962f8b6SJussi Kivilinna 	},
92235474c3bSJussi Kivilinna } };
9235962f8b6SJussi Kivilinna 
924937c30d7SJussi Kivilinna static int __init serpent_sse2_init(void)
925937c30d7SJussi Kivilinna {
926937c30d7SJussi Kivilinna 	if (!cpu_has_xmm2) {
927937c30d7SJussi Kivilinna 		printk(KERN_INFO "SSE2 instructions are not detected.\n");
928937c30d7SJussi Kivilinna 		return -ENODEV;
929937c30d7SJussi Kivilinna 	}
930937c30d7SJussi Kivilinna 
93135474c3bSJussi Kivilinna 	return crypto_register_algs(serpent_algs, ARRAY_SIZE(serpent_algs));
932937c30d7SJussi Kivilinna }
933937c30d7SJussi Kivilinna 
934937c30d7SJussi Kivilinna static void __exit serpent_sse2_exit(void)
935937c30d7SJussi Kivilinna {
93635474c3bSJussi Kivilinna 	crypto_unregister_algs(serpent_algs, ARRAY_SIZE(serpent_algs));
937937c30d7SJussi Kivilinna }
938937c30d7SJussi Kivilinna 
939937c30d7SJussi Kivilinna module_init(serpent_sse2_init);
940937c30d7SJussi Kivilinna module_exit(serpent_sse2_exit);
941937c30d7SJussi Kivilinna 
942937c30d7SJussi Kivilinna MODULE_DESCRIPTION("Serpent Cipher Algorithm, SSE2 optimized");
943937c30d7SJussi Kivilinna MODULE_LICENSE("GPL");
944937c30d7SJussi Kivilinna MODULE_ALIAS("serpent");
945