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>
42937c30d7SJussi Kivilinna #include <asm/i387.h>
43937c30d7SJussi Kivilinna #include <asm/serpent.h>
44937c30d7SJussi Kivilinna #include <crypto/scatterwalk.h>
45937c30d7SJussi Kivilinna #include <linux/workqueue.h>
46937c30d7SJussi Kivilinna #include <linux/spinlock.h>
47937c30d7SJussi Kivilinna 
48937c30d7SJussi Kivilinna struct async_serpent_ctx {
49937c30d7SJussi Kivilinna 	struct cryptd_ablkcipher *cryptd_tfm;
50937c30d7SJussi Kivilinna };
51937c30d7SJussi Kivilinna 
52937c30d7SJussi Kivilinna static inline bool serpent_fpu_begin(bool fpu_enabled, unsigned int nbytes)
53937c30d7SJussi Kivilinna {
54937c30d7SJussi Kivilinna 	if (fpu_enabled)
55937c30d7SJussi Kivilinna 		return true;
56937c30d7SJussi Kivilinna 
57937c30d7SJussi Kivilinna 	/* SSE2 is only used when chunk to be processed is large enough, so
58937c30d7SJussi Kivilinna 	 * do not enable FPU until it is necessary.
59937c30d7SJussi Kivilinna 	 */
60937c30d7SJussi Kivilinna 	if (nbytes < SERPENT_BLOCK_SIZE * SERPENT_PARALLEL_BLOCKS)
61937c30d7SJussi Kivilinna 		return false;
62937c30d7SJussi Kivilinna 
63937c30d7SJussi Kivilinna 	kernel_fpu_begin();
64937c30d7SJussi Kivilinna 	return true;
65937c30d7SJussi Kivilinna }
66937c30d7SJussi Kivilinna 
67937c30d7SJussi Kivilinna static inline void serpent_fpu_end(bool fpu_enabled)
68937c30d7SJussi Kivilinna {
69937c30d7SJussi Kivilinna 	if (fpu_enabled)
70937c30d7SJussi Kivilinna 		kernel_fpu_end();
71937c30d7SJussi Kivilinna }
72937c30d7SJussi Kivilinna 
73937c30d7SJussi Kivilinna static int ecb_crypt(struct blkcipher_desc *desc, struct blkcipher_walk *walk,
74937c30d7SJussi Kivilinna 		     bool enc)
75937c30d7SJussi Kivilinna {
76937c30d7SJussi Kivilinna 	bool fpu_enabled = false;
77937c30d7SJussi Kivilinna 	struct serpent_ctx *ctx = crypto_blkcipher_ctx(desc->tfm);
78937c30d7SJussi Kivilinna 	const unsigned int bsize = SERPENT_BLOCK_SIZE;
79937c30d7SJussi Kivilinna 	unsigned int nbytes;
80937c30d7SJussi Kivilinna 	int err;
81937c30d7SJussi Kivilinna 
82937c30d7SJussi Kivilinna 	err = blkcipher_walk_virt(desc, walk);
83937c30d7SJussi Kivilinna 	desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP;
84937c30d7SJussi Kivilinna 
85937c30d7SJussi Kivilinna 	while ((nbytes = walk->nbytes)) {
86937c30d7SJussi Kivilinna 		u8 *wsrc = walk->src.virt.addr;
87937c30d7SJussi Kivilinna 		u8 *wdst = walk->dst.virt.addr;
88937c30d7SJussi Kivilinna 
89937c30d7SJussi Kivilinna 		fpu_enabled = serpent_fpu_begin(fpu_enabled, nbytes);
90937c30d7SJussi Kivilinna 
91937c30d7SJussi Kivilinna 		/* Process multi-block batch */
92937c30d7SJussi Kivilinna 		if (nbytes >= bsize * SERPENT_PARALLEL_BLOCKS) {
93937c30d7SJussi Kivilinna 			do {
94937c30d7SJussi Kivilinna 				if (enc)
95937c30d7SJussi Kivilinna 					serpent_enc_blk_xway(ctx, wdst, wsrc);
96937c30d7SJussi Kivilinna 				else
97937c30d7SJussi Kivilinna 					serpent_dec_blk_xway(ctx, wdst, wsrc);
98937c30d7SJussi Kivilinna 
99937c30d7SJussi Kivilinna 				wsrc += bsize * SERPENT_PARALLEL_BLOCKS;
100937c30d7SJussi Kivilinna 				wdst += bsize * SERPENT_PARALLEL_BLOCKS;
101937c30d7SJussi Kivilinna 				nbytes -= bsize * SERPENT_PARALLEL_BLOCKS;
102937c30d7SJussi Kivilinna 			} while (nbytes >= bsize * SERPENT_PARALLEL_BLOCKS);
103937c30d7SJussi Kivilinna 
104937c30d7SJussi Kivilinna 			if (nbytes < bsize)
105937c30d7SJussi Kivilinna 				goto done;
106937c30d7SJussi Kivilinna 		}
107937c30d7SJussi Kivilinna 
108937c30d7SJussi Kivilinna 		/* Handle leftovers */
109937c30d7SJussi Kivilinna 		do {
110937c30d7SJussi Kivilinna 			if (enc)
111937c30d7SJussi Kivilinna 				__serpent_encrypt(ctx, wdst, wsrc);
112937c30d7SJussi Kivilinna 			else
113937c30d7SJussi Kivilinna 				__serpent_decrypt(ctx, wdst, wsrc);
114937c30d7SJussi Kivilinna 
115937c30d7SJussi Kivilinna 			wsrc += bsize;
116937c30d7SJussi Kivilinna 			wdst += bsize;
117937c30d7SJussi Kivilinna 			nbytes -= bsize;
118937c30d7SJussi Kivilinna 		} while (nbytes >= bsize);
119937c30d7SJussi Kivilinna 
120937c30d7SJussi Kivilinna done:
121937c30d7SJussi Kivilinna 		err = blkcipher_walk_done(desc, walk, nbytes);
122937c30d7SJussi Kivilinna 	}
123937c30d7SJussi Kivilinna 
124937c30d7SJussi Kivilinna 	serpent_fpu_end(fpu_enabled);
125937c30d7SJussi Kivilinna 	return err;
126937c30d7SJussi Kivilinna }
127937c30d7SJussi Kivilinna 
128937c30d7SJussi Kivilinna static int ecb_encrypt(struct blkcipher_desc *desc, struct scatterlist *dst,
129937c30d7SJussi Kivilinna 		       struct scatterlist *src, unsigned int nbytes)
130937c30d7SJussi Kivilinna {
131937c30d7SJussi Kivilinna 	struct blkcipher_walk walk;
132937c30d7SJussi Kivilinna 
133937c30d7SJussi Kivilinna 	blkcipher_walk_init(&walk, dst, src, nbytes);
134937c30d7SJussi Kivilinna 	return ecb_crypt(desc, &walk, true);
135937c30d7SJussi Kivilinna }
136937c30d7SJussi Kivilinna 
137937c30d7SJussi Kivilinna static int ecb_decrypt(struct blkcipher_desc *desc, struct scatterlist *dst,
138937c30d7SJussi Kivilinna 		       struct scatterlist *src, unsigned int nbytes)
139937c30d7SJussi Kivilinna {
140937c30d7SJussi Kivilinna 	struct blkcipher_walk walk;
141937c30d7SJussi Kivilinna 
142937c30d7SJussi Kivilinna 	blkcipher_walk_init(&walk, dst, src, nbytes);
143937c30d7SJussi Kivilinna 	return ecb_crypt(desc, &walk, false);
144937c30d7SJussi Kivilinna }
145937c30d7SJussi Kivilinna 
146937c30d7SJussi Kivilinna static struct crypto_alg blk_ecb_alg = {
147937c30d7SJussi Kivilinna 	.cra_name		= "__ecb-serpent-sse2",
148937c30d7SJussi Kivilinna 	.cra_driver_name	= "__driver-ecb-serpent-sse2",
149937c30d7SJussi Kivilinna 	.cra_priority		= 0,
150937c30d7SJussi Kivilinna 	.cra_flags		= CRYPTO_ALG_TYPE_BLKCIPHER,
151937c30d7SJussi Kivilinna 	.cra_blocksize		= SERPENT_BLOCK_SIZE,
152937c30d7SJussi Kivilinna 	.cra_ctxsize		= sizeof(struct serpent_ctx),
153937c30d7SJussi Kivilinna 	.cra_alignmask		= 0,
154937c30d7SJussi Kivilinna 	.cra_type		= &crypto_blkcipher_type,
155937c30d7SJussi Kivilinna 	.cra_module		= THIS_MODULE,
156937c30d7SJussi Kivilinna 	.cra_list		= LIST_HEAD_INIT(blk_ecb_alg.cra_list),
157937c30d7SJussi Kivilinna 	.cra_u = {
158937c30d7SJussi Kivilinna 		.blkcipher = {
159937c30d7SJussi Kivilinna 			.min_keysize	= SERPENT_MIN_KEY_SIZE,
160937c30d7SJussi Kivilinna 			.max_keysize	= SERPENT_MAX_KEY_SIZE,
161937c30d7SJussi Kivilinna 			.setkey		= serpent_setkey,
162937c30d7SJussi Kivilinna 			.encrypt	= ecb_encrypt,
163937c30d7SJussi Kivilinna 			.decrypt	= ecb_decrypt,
164937c30d7SJussi Kivilinna 		},
165937c30d7SJussi Kivilinna 	},
166937c30d7SJussi Kivilinna };
167937c30d7SJussi Kivilinna 
168937c30d7SJussi Kivilinna static unsigned int __cbc_encrypt(struct blkcipher_desc *desc,
169937c30d7SJussi Kivilinna 				  struct blkcipher_walk *walk)
170937c30d7SJussi Kivilinna {
171937c30d7SJussi Kivilinna 	struct serpent_ctx *ctx = crypto_blkcipher_ctx(desc->tfm);
172937c30d7SJussi Kivilinna 	const unsigned int bsize = SERPENT_BLOCK_SIZE;
173937c30d7SJussi Kivilinna 	unsigned int nbytes = walk->nbytes;
174937c30d7SJussi Kivilinna 	u128 *src = (u128 *)walk->src.virt.addr;
175937c30d7SJussi Kivilinna 	u128 *dst = (u128 *)walk->dst.virt.addr;
176937c30d7SJussi Kivilinna 	u128 *iv = (u128 *)walk->iv;
177937c30d7SJussi Kivilinna 
178937c30d7SJussi Kivilinna 	do {
179937c30d7SJussi Kivilinna 		u128_xor(dst, src, iv);
180937c30d7SJussi Kivilinna 		__serpent_encrypt(ctx, (u8 *)dst, (u8 *)dst);
181937c30d7SJussi Kivilinna 		iv = dst;
182937c30d7SJussi Kivilinna 
183937c30d7SJussi Kivilinna 		src += 1;
184937c30d7SJussi Kivilinna 		dst += 1;
185937c30d7SJussi Kivilinna 		nbytes -= bsize;
186937c30d7SJussi Kivilinna 	} while (nbytes >= bsize);
187937c30d7SJussi Kivilinna 
188937c30d7SJussi Kivilinna 	u128_xor((u128 *)walk->iv, (u128 *)walk->iv, iv);
189937c30d7SJussi Kivilinna 	return nbytes;
190937c30d7SJussi Kivilinna }
191937c30d7SJussi Kivilinna 
192937c30d7SJussi Kivilinna static int cbc_encrypt(struct blkcipher_desc *desc, struct scatterlist *dst,
193937c30d7SJussi Kivilinna 		       struct scatterlist *src, unsigned int nbytes)
194937c30d7SJussi Kivilinna {
195937c30d7SJussi Kivilinna 	struct blkcipher_walk walk;
196937c30d7SJussi Kivilinna 	int err;
197937c30d7SJussi Kivilinna 
198937c30d7SJussi Kivilinna 	blkcipher_walk_init(&walk, dst, src, nbytes);
199937c30d7SJussi Kivilinna 	err = blkcipher_walk_virt(desc, &walk);
200937c30d7SJussi Kivilinna 
201937c30d7SJussi Kivilinna 	while ((nbytes = walk.nbytes)) {
202937c30d7SJussi Kivilinna 		nbytes = __cbc_encrypt(desc, &walk);
203937c30d7SJussi Kivilinna 		err = blkcipher_walk_done(desc, &walk, nbytes);
204937c30d7SJussi Kivilinna 	}
205937c30d7SJussi Kivilinna 
206937c30d7SJussi Kivilinna 	return err;
207937c30d7SJussi Kivilinna }
208937c30d7SJussi Kivilinna 
209937c30d7SJussi Kivilinna static unsigned int __cbc_decrypt(struct blkcipher_desc *desc,
210937c30d7SJussi Kivilinna 				  struct blkcipher_walk *walk)
211937c30d7SJussi Kivilinna {
212937c30d7SJussi Kivilinna 	struct serpent_ctx *ctx = crypto_blkcipher_ctx(desc->tfm);
213937c30d7SJussi Kivilinna 	const unsigned int bsize = SERPENT_BLOCK_SIZE;
214937c30d7SJussi Kivilinna 	unsigned int nbytes = walk->nbytes;
215937c30d7SJussi Kivilinna 	u128 *src = (u128 *)walk->src.virt.addr;
216937c30d7SJussi Kivilinna 	u128 *dst = (u128 *)walk->dst.virt.addr;
217937c30d7SJussi Kivilinna 	u128 ivs[SERPENT_PARALLEL_BLOCKS - 1];
218937c30d7SJussi Kivilinna 	u128 last_iv;
219937c30d7SJussi Kivilinna 	int i;
220937c30d7SJussi Kivilinna 
221937c30d7SJussi Kivilinna 	/* Start of the last block. */
222937c30d7SJussi Kivilinna 	src += nbytes / bsize - 1;
223937c30d7SJussi Kivilinna 	dst += nbytes / bsize - 1;
224937c30d7SJussi Kivilinna 
225937c30d7SJussi Kivilinna 	last_iv = *src;
226937c30d7SJussi Kivilinna 
227937c30d7SJussi Kivilinna 	/* Process multi-block batch */
228937c30d7SJussi Kivilinna 	if (nbytes >= bsize * SERPENT_PARALLEL_BLOCKS) {
229937c30d7SJussi Kivilinna 		do {
230937c30d7SJussi Kivilinna 			nbytes -= bsize * (SERPENT_PARALLEL_BLOCKS - 1);
231937c30d7SJussi Kivilinna 			src -= SERPENT_PARALLEL_BLOCKS - 1;
232937c30d7SJussi Kivilinna 			dst -= SERPENT_PARALLEL_BLOCKS - 1;
233937c30d7SJussi Kivilinna 
234937c30d7SJussi Kivilinna 			for (i = 0; i < SERPENT_PARALLEL_BLOCKS - 1; i++)
235937c30d7SJussi Kivilinna 				ivs[i] = src[i];
236937c30d7SJussi Kivilinna 
237937c30d7SJussi Kivilinna 			serpent_dec_blk_xway(ctx, (u8 *)dst, (u8 *)src);
238937c30d7SJussi Kivilinna 
239937c30d7SJussi Kivilinna 			for (i = 0; i < SERPENT_PARALLEL_BLOCKS - 1; i++)
240937c30d7SJussi Kivilinna 				u128_xor(dst + (i + 1), dst + (i + 1), ivs + i);
241937c30d7SJussi Kivilinna 
242937c30d7SJussi Kivilinna 			nbytes -= bsize;
243937c30d7SJussi Kivilinna 			if (nbytes < bsize)
244937c30d7SJussi Kivilinna 				goto done;
245937c30d7SJussi Kivilinna 
246937c30d7SJussi Kivilinna 			u128_xor(dst, dst, src - 1);
247937c30d7SJussi Kivilinna 			src -= 1;
248937c30d7SJussi Kivilinna 			dst -= 1;
249937c30d7SJussi Kivilinna 		} while (nbytes >= bsize * SERPENT_PARALLEL_BLOCKS);
250937c30d7SJussi Kivilinna 
251937c30d7SJussi Kivilinna 		if (nbytes < bsize)
252937c30d7SJussi Kivilinna 			goto done;
253937c30d7SJussi Kivilinna 	}
254937c30d7SJussi Kivilinna 
255937c30d7SJussi Kivilinna 	/* Handle leftovers */
256937c30d7SJussi Kivilinna 	for (;;) {
257937c30d7SJussi Kivilinna 		__serpent_decrypt(ctx, (u8 *)dst, (u8 *)src);
258937c30d7SJussi Kivilinna 
259937c30d7SJussi Kivilinna 		nbytes -= bsize;
260937c30d7SJussi Kivilinna 		if (nbytes < bsize)
261937c30d7SJussi Kivilinna 			break;
262937c30d7SJussi Kivilinna 
263937c30d7SJussi Kivilinna 		u128_xor(dst, dst, src - 1);
264937c30d7SJussi Kivilinna 		src -= 1;
265937c30d7SJussi Kivilinna 		dst -= 1;
266937c30d7SJussi Kivilinna 	}
267937c30d7SJussi Kivilinna 
268937c30d7SJussi Kivilinna done:
269937c30d7SJussi Kivilinna 	u128_xor(dst, dst, (u128 *)walk->iv);
270937c30d7SJussi Kivilinna 	*(u128 *)walk->iv = last_iv;
271937c30d7SJussi Kivilinna 
272937c30d7SJussi Kivilinna 	return nbytes;
273937c30d7SJussi Kivilinna }
274937c30d7SJussi Kivilinna 
275937c30d7SJussi Kivilinna static int cbc_decrypt(struct blkcipher_desc *desc, struct scatterlist *dst,
276937c30d7SJussi Kivilinna 		       struct scatterlist *src, unsigned int nbytes)
277937c30d7SJussi Kivilinna {
278937c30d7SJussi Kivilinna 	bool fpu_enabled = false;
279937c30d7SJussi Kivilinna 	struct blkcipher_walk walk;
280937c30d7SJussi Kivilinna 	int err;
281937c30d7SJussi Kivilinna 
282937c30d7SJussi Kivilinna 	blkcipher_walk_init(&walk, dst, src, nbytes);
283937c30d7SJussi Kivilinna 	err = blkcipher_walk_virt(desc, &walk);
284937c30d7SJussi Kivilinna 	desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP;
285937c30d7SJussi Kivilinna 
286937c30d7SJussi Kivilinna 	while ((nbytes = walk.nbytes)) {
287937c30d7SJussi Kivilinna 		fpu_enabled = serpent_fpu_begin(fpu_enabled, nbytes);
288937c30d7SJussi Kivilinna 		nbytes = __cbc_decrypt(desc, &walk);
289937c30d7SJussi Kivilinna 		err = blkcipher_walk_done(desc, &walk, nbytes);
290937c30d7SJussi Kivilinna 	}
291937c30d7SJussi Kivilinna 
292937c30d7SJussi Kivilinna 	serpent_fpu_end(fpu_enabled);
293937c30d7SJussi Kivilinna 	return err;
294937c30d7SJussi Kivilinna }
295937c30d7SJussi Kivilinna 
296937c30d7SJussi Kivilinna static struct crypto_alg blk_cbc_alg = {
297937c30d7SJussi Kivilinna 	.cra_name		= "__cbc-serpent-sse2",
298937c30d7SJussi Kivilinna 	.cra_driver_name	= "__driver-cbc-serpent-sse2",
299937c30d7SJussi Kivilinna 	.cra_priority		= 0,
300937c30d7SJussi Kivilinna 	.cra_flags		= CRYPTO_ALG_TYPE_BLKCIPHER,
301937c30d7SJussi Kivilinna 	.cra_blocksize		= SERPENT_BLOCK_SIZE,
302937c30d7SJussi Kivilinna 	.cra_ctxsize		= sizeof(struct serpent_ctx),
303937c30d7SJussi Kivilinna 	.cra_alignmask		= 0,
304937c30d7SJussi Kivilinna 	.cra_type		= &crypto_blkcipher_type,
305937c30d7SJussi Kivilinna 	.cra_module		= THIS_MODULE,
306937c30d7SJussi Kivilinna 	.cra_list		= LIST_HEAD_INIT(blk_cbc_alg.cra_list),
307937c30d7SJussi Kivilinna 	.cra_u = {
308937c30d7SJussi Kivilinna 		.blkcipher = {
309937c30d7SJussi Kivilinna 			.min_keysize	= SERPENT_MIN_KEY_SIZE,
310937c30d7SJussi Kivilinna 			.max_keysize	= SERPENT_MAX_KEY_SIZE,
311937c30d7SJussi Kivilinna 			.setkey		= serpent_setkey,
312937c30d7SJussi Kivilinna 			.encrypt	= cbc_encrypt,
313937c30d7SJussi Kivilinna 			.decrypt	= cbc_decrypt,
314937c30d7SJussi Kivilinna 		},
315937c30d7SJussi Kivilinna 	},
316937c30d7SJussi Kivilinna };
317937c30d7SJussi Kivilinna 
318937c30d7SJussi Kivilinna static inline void u128_to_be128(be128 *dst, const u128 *src)
319937c30d7SJussi Kivilinna {
320937c30d7SJussi Kivilinna 	dst->a = cpu_to_be64(src->a);
321937c30d7SJussi Kivilinna 	dst->b = cpu_to_be64(src->b);
322937c30d7SJussi Kivilinna }
323937c30d7SJussi Kivilinna 
324937c30d7SJussi Kivilinna static inline void be128_to_u128(u128 *dst, const be128 *src)
325937c30d7SJussi Kivilinna {
326937c30d7SJussi Kivilinna 	dst->a = be64_to_cpu(src->a);
327937c30d7SJussi Kivilinna 	dst->b = be64_to_cpu(src->b);
328937c30d7SJussi Kivilinna }
329937c30d7SJussi Kivilinna 
330937c30d7SJussi Kivilinna static inline void u128_inc(u128 *i)
331937c30d7SJussi Kivilinna {
332937c30d7SJussi Kivilinna 	i->b++;
333937c30d7SJussi Kivilinna 	if (!i->b)
334937c30d7SJussi Kivilinna 		i->a++;
335937c30d7SJussi Kivilinna }
336937c30d7SJussi Kivilinna 
337937c30d7SJussi Kivilinna static void ctr_crypt_final(struct blkcipher_desc *desc,
338937c30d7SJussi Kivilinna 			    struct blkcipher_walk *walk)
339937c30d7SJussi Kivilinna {
340937c30d7SJussi Kivilinna 	struct serpent_ctx *ctx = crypto_blkcipher_ctx(desc->tfm);
341937c30d7SJussi Kivilinna 	u8 *ctrblk = walk->iv;
342937c30d7SJussi Kivilinna 	u8 keystream[SERPENT_BLOCK_SIZE];
343937c30d7SJussi Kivilinna 	u8 *src = walk->src.virt.addr;
344937c30d7SJussi Kivilinna 	u8 *dst = walk->dst.virt.addr;
345937c30d7SJussi Kivilinna 	unsigned int nbytes = walk->nbytes;
346937c30d7SJussi Kivilinna 
347937c30d7SJussi Kivilinna 	__serpent_encrypt(ctx, keystream, ctrblk);
348937c30d7SJussi Kivilinna 	crypto_xor(keystream, src, nbytes);
349937c30d7SJussi Kivilinna 	memcpy(dst, keystream, nbytes);
350937c30d7SJussi Kivilinna 
351937c30d7SJussi Kivilinna 	crypto_inc(ctrblk, SERPENT_BLOCK_SIZE);
352937c30d7SJussi Kivilinna }
353937c30d7SJussi Kivilinna 
354937c30d7SJussi Kivilinna static unsigned int __ctr_crypt(struct blkcipher_desc *desc,
355937c30d7SJussi Kivilinna 				struct blkcipher_walk *walk)
356937c30d7SJussi Kivilinna {
357937c30d7SJussi Kivilinna 	struct serpent_ctx *ctx = crypto_blkcipher_ctx(desc->tfm);
358937c30d7SJussi Kivilinna 	const unsigned int bsize = SERPENT_BLOCK_SIZE;
359937c30d7SJussi Kivilinna 	unsigned int nbytes = walk->nbytes;
360937c30d7SJussi Kivilinna 	u128 *src = (u128 *)walk->src.virt.addr;
361937c30d7SJussi Kivilinna 	u128 *dst = (u128 *)walk->dst.virt.addr;
362937c30d7SJussi Kivilinna 	u128 ctrblk;
363937c30d7SJussi Kivilinna 	be128 ctrblocks[SERPENT_PARALLEL_BLOCKS];
364937c30d7SJussi Kivilinna 	int i;
365937c30d7SJussi Kivilinna 
366937c30d7SJussi Kivilinna 	be128_to_u128(&ctrblk, (be128 *)walk->iv);
367937c30d7SJussi Kivilinna 
368937c30d7SJussi Kivilinna 	/* Process multi-block batch */
369937c30d7SJussi Kivilinna 	if (nbytes >= bsize * SERPENT_PARALLEL_BLOCKS) {
370937c30d7SJussi Kivilinna 		do {
371937c30d7SJussi Kivilinna 			/* create ctrblks for parallel encrypt */
372937c30d7SJussi Kivilinna 			for (i = 0; i < SERPENT_PARALLEL_BLOCKS; i++) {
373937c30d7SJussi Kivilinna 				if (dst != src)
374937c30d7SJussi Kivilinna 					dst[i] = src[i];
375937c30d7SJussi Kivilinna 
376937c30d7SJussi Kivilinna 				u128_to_be128(&ctrblocks[i], &ctrblk);
377937c30d7SJussi Kivilinna 				u128_inc(&ctrblk);
378937c30d7SJussi Kivilinna 			}
379937c30d7SJussi Kivilinna 
380937c30d7SJussi Kivilinna 			serpent_enc_blk_xway_xor(ctx, (u8 *)dst,
381937c30d7SJussi Kivilinna 						 (u8 *)ctrblocks);
382937c30d7SJussi Kivilinna 
383937c30d7SJussi Kivilinna 			src += SERPENT_PARALLEL_BLOCKS;
384937c30d7SJussi Kivilinna 			dst += SERPENT_PARALLEL_BLOCKS;
385937c30d7SJussi Kivilinna 			nbytes -= bsize * SERPENT_PARALLEL_BLOCKS;
386937c30d7SJussi Kivilinna 		} while (nbytes >= bsize * SERPENT_PARALLEL_BLOCKS);
387937c30d7SJussi Kivilinna 
388937c30d7SJussi Kivilinna 		if (nbytes < bsize)
389937c30d7SJussi Kivilinna 			goto done;
390937c30d7SJussi Kivilinna 	}
391937c30d7SJussi Kivilinna 
392937c30d7SJussi Kivilinna 	/* Handle leftovers */
393937c30d7SJussi Kivilinna 	do {
394937c30d7SJussi Kivilinna 		if (dst != src)
395937c30d7SJussi Kivilinna 			*dst = *src;
396937c30d7SJussi Kivilinna 
397937c30d7SJussi Kivilinna 		u128_to_be128(&ctrblocks[0], &ctrblk);
398937c30d7SJussi Kivilinna 		u128_inc(&ctrblk);
399937c30d7SJussi Kivilinna 
400937c30d7SJussi Kivilinna 		__serpent_encrypt(ctx, (u8 *)ctrblocks, (u8 *)ctrblocks);
401937c30d7SJussi Kivilinna 		u128_xor(dst, dst, (u128 *)ctrblocks);
402937c30d7SJussi Kivilinna 
403937c30d7SJussi Kivilinna 		src += 1;
404937c30d7SJussi Kivilinna 		dst += 1;
405937c30d7SJussi Kivilinna 		nbytes -= bsize;
406937c30d7SJussi Kivilinna 	} while (nbytes >= bsize);
407937c30d7SJussi Kivilinna 
408937c30d7SJussi Kivilinna done:
409937c30d7SJussi Kivilinna 	u128_to_be128((be128 *)walk->iv, &ctrblk);
410937c30d7SJussi Kivilinna 	return nbytes;
411937c30d7SJussi Kivilinna }
412937c30d7SJussi Kivilinna 
413937c30d7SJussi Kivilinna static int ctr_crypt(struct blkcipher_desc *desc, struct scatterlist *dst,
414937c30d7SJussi Kivilinna 		     struct scatterlist *src, unsigned int nbytes)
415937c30d7SJussi Kivilinna {
416937c30d7SJussi Kivilinna 	bool fpu_enabled = false;
417937c30d7SJussi Kivilinna 	struct blkcipher_walk walk;
418937c30d7SJussi Kivilinna 	int err;
419937c30d7SJussi Kivilinna 
420937c30d7SJussi Kivilinna 	blkcipher_walk_init(&walk, dst, src, nbytes);
421937c30d7SJussi Kivilinna 	err = blkcipher_walk_virt_block(desc, &walk, SERPENT_BLOCK_SIZE);
422937c30d7SJussi Kivilinna 	desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP;
423937c30d7SJussi Kivilinna 
424937c30d7SJussi Kivilinna 	while ((nbytes = walk.nbytes) >= SERPENT_BLOCK_SIZE) {
425937c30d7SJussi Kivilinna 		fpu_enabled = serpent_fpu_begin(fpu_enabled, nbytes);
426937c30d7SJussi Kivilinna 		nbytes = __ctr_crypt(desc, &walk);
427937c30d7SJussi Kivilinna 		err = blkcipher_walk_done(desc, &walk, nbytes);
428937c30d7SJussi Kivilinna 	}
429937c30d7SJussi Kivilinna 
430937c30d7SJussi Kivilinna 	serpent_fpu_end(fpu_enabled);
431937c30d7SJussi Kivilinna 
432937c30d7SJussi Kivilinna 	if (walk.nbytes) {
433937c30d7SJussi Kivilinna 		ctr_crypt_final(desc, &walk);
434937c30d7SJussi Kivilinna 		err = blkcipher_walk_done(desc, &walk, 0);
435937c30d7SJussi Kivilinna 	}
436937c30d7SJussi Kivilinna 
437937c30d7SJussi Kivilinna 	return err;
438937c30d7SJussi Kivilinna }
439937c30d7SJussi Kivilinna 
440937c30d7SJussi Kivilinna static struct crypto_alg blk_ctr_alg = {
441937c30d7SJussi Kivilinna 	.cra_name		= "__ctr-serpent-sse2",
442937c30d7SJussi Kivilinna 	.cra_driver_name	= "__driver-ctr-serpent-sse2",
443937c30d7SJussi Kivilinna 	.cra_priority		= 0,
444937c30d7SJussi Kivilinna 	.cra_flags		= CRYPTO_ALG_TYPE_BLKCIPHER,
445937c30d7SJussi Kivilinna 	.cra_blocksize		= 1,
446937c30d7SJussi Kivilinna 	.cra_ctxsize		= sizeof(struct serpent_ctx),
447937c30d7SJussi Kivilinna 	.cra_alignmask		= 0,
448937c30d7SJussi Kivilinna 	.cra_type		= &crypto_blkcipher_type,
449937c30d7SJussi Kivilinna 	.cra_module		= THIS_MODULE,
450937c30d7SJussi Kivilinna 	.cra_list		= LIST_HEAD_INIT(blk_ctr_alg.cra_list),
451937c30d7SJussi Kivilinna 	.cra_u = {
452937c30d7SJussi Kivilinna 		.blkcipher = {
453937c30d7SJussi Kivilinna 			.min_keysize	= SERPENT_MIN_KEY_SIZE,
454937c30d7SJussi Kivilinna 			.max_keysize	= SERPENT_MAX_KEY_SIZE,
455937c30d7SJussi Kivilinna 			.ivsize		= SERPENT_BLOCK_SIZE,
456937c30d7SJussi Kivilinna 			.setkey		= serpent_setkey,
457937c30d7SJussi Kivilinna 			.encrypt	= ctr_crypt,
458937c30d7SJussi Kivilinna 			.decrypt	= ctr_crypt,
459937c30d7SJussi Kivilinna 		},
460937c30d7SJussi Kivilinna 	},
461937c30d7SJussi Kivilinna };
462937c30d7SJussi Kivilinna 
463937c30d7SJussi Kivilinna static int ablk_set_key(struct crypto_ablkcipher *tfm, const u8 *key,
464937c30d7SJussi Kivilinna 			unsigned int key_len)
465937c30d7SJussi Kivilinna {
466937c30d7SJussi Kivilinna 	struct async_serpent_ctx *ctx = crypto_ablkcipher_ctx(tfm);
467937c30d7SJussi Kivilinna 	struct crypto_ablkcipher *child = &ctx->cryptd_tfm->base;
468937c30d7SJussi Kivilinna 	int err;
469937c30d7SJussi Kivilinna 
470937c30d7SJussi Kivilinna 	crypto_ablkcipher_clear_flags(child, CRYPTO_TFM_REQ_MASK);
471937c30d7SJussi Kivilinna 	crypto_ablkcipher_set_flags(child, crypto_ablkcipher_get_flags(tfm)
472937c30d7SJussi Kivilinna 				    & CRYPTO_TFM_REQ_MASK);
473937c30d7SJussi Kivilinna 	err = crypto_ablkcipher_setkey(child, key, key_len);
474937c30d7SJussi Kivilinna 	crypto_ablkcipher_set_flags(tfm, crypto_ablkcipher_get_flags(child)
475937c30d7SJussi Kivilinna 				    & CRYPTO_TFM_RES_MASK);
476937c30d7SJussi Kivilinna 	return err;
477937c30d7SJussi Kivilinna }
478937c30d7SJussi Kivilinna 
479937c30d7SJussi Kivilinna static int __ablk_encrypt(struct ablkcipher_request *req)
480937c30d7SJussi Kivilinna {
481937c30d7SJussi Kivilinna 	struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(req);
482937c30d7SJussi Kivilinna 	struct async_serpent_ctx *ctx = crypto_ablkcipher_ctx(tfm);
483937c30d7SJussi Kivilinna 	struct blkcipher_desc desc;
484937c30d7SJussi Kivilinna 
485937c30d7SJussi Kivilinna 	desc.tfm = cryptd_ablkcipher_child(ctx->cryptd_tfm);
486937c30d7SJussi Kivilinna 	desc.info = req->info;
487937c30d7SJussi Kivilinna 	desc.flags = 0;
488937c30d7SJussi Kivilinna 
489937c30d7SJussi Kivilinna 	return crypto_blkcipher_crt(desc.tfm)->encrypt(
490937c30d7SJussi Kivilinna 		&desc, req->dst, req->src, req->nbytes);
491937c30d7SJussi Kivilinna }
492937c30d7SJussi Kivilinna 
493937c30d7SJussi Kivilinna static int ablk_encrypt(struct ablkcipher_request *req)
494937c30d7SJussi Kivilinna {
495937c30d7SJussi Kivilinna 	struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(req);
496937c30d7SJussi Kivilinna 	struct async_serpent_ctx *ctx = crypto_ablkcipher_ctx(tfm);
497937c30d7SJussi Kivilinna 
498937c30d7SJussi Kivilinna 	if (!irq_fpu_usable()) {
499937c30d7SJussi Kivilinna 		struct ablkcipher_request *cryptd_req =
500937c30d7SJussi Kivilinna 			ablkcipher_request_ctx(req);
501937c30d7SJussi Kivilinna 
502937c30d7SJussi Kivilinna 		memcpy(cryptd_req, req, sizeof(*req));
503937c30d7SJussi Kivilinna 		ablkcipher_request_set_tfm(cryptd_req, &ctx->cryptd_tfm->base);
504937c30d7SJussi Kivilinna 
505937c30d7SJussi Kivilinna 		return crypto_ablkcipher_encrypt(cryptd_req);
506937c30d7SJussi Kivilinna 	} else {
507937c30d7SJussi Kivilinna 		return __ablk_encrypt(req);
508937c30d7SJussi Kivilinna 	}
509937c30d7SJussi Kivilinna }
510937c30d7SJussi Kivilinna 
511937c30d7SJussi Kivilinna static int ablk_decrypt(struct ablkcipher_request *req)
512937c30d7SJussi Kivilinna {
513937c30d7SJussi Kivilinna 	struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(req);
514937c30d7SJussi Kivilinna 	struct async_serpent_ctx *ctx = crypto_ablkcipher_ctx(tfm);
515937c30d7SJussi Kivilinna 
516937c30d7SJussi Kivilinna 	if (!irq_fpu_usable()) {
517937c30d7SJussi Kivilinna 		struct ablkcipher_request *cryptd_req =
518937c30d7SJussi Kivilinna 			ablkcipher_request_ctx(req);
519937c30d7SJussi Kivilinna 
520937c30d7SJussi Kivilinna 		memcpy(cryptd_req, req, sizeof(*req));
521937c30d7SJussi Kivilinna 		ablkcipher_request_set_tfm(cryptd_req, &ctx->cryptd_tfm->base);
522937c30d7SJussi Kivilinna 
523937c30d7SJussi Kivilinna 		return crypto_ablkcipher_decrypt(cryptd_req);
524937c30d7SJussi Kivilinna 	} else {
525937c30d7SJussi Kivilinna 		struct blkcipher_desc desc;
526937c30d7SJussi Kivilinna 
527937c30d7SJussi Kivilinna 		desc.tfm = cryptd_ablkcipher_child(ctx->cryptd_tfm);
528937c30d7SJussi Kivilinna 		desc.info = req->info;
529937c30d7SJussi Kivilinna 		desc.flags = 0;
530937c30d7SJussi Kivilinna 
531937c30d7SJussi Kivilinna 		return crypto_blkcipher_crt(desc.tfm)->decrypt(
532937c30d7SJussi Kivilinna 			&desc, req->dst, req->src, req->nbytes);
533937c30d7SJussi Kivilinna 	}
534937c30d7SJussi Kivilinna }
535937c30d7SJussi Kivilinna 
536937c30d7SJussi Kivilinna static void ablk_exit(struct crypto_tfm *tfm)
537937c30d7SJussi Kivilinna {
538937c30d7SJussi Kivilinna 	struct async_serpent_ctx *ctx = crypto_tfm_ctx(tfm);
539937c30d7SJussi Kivilinna 
540937c30d7SJussi Kivilinna 	cryptd_free_ablkcipher(ctx->cryptd_tfm);
541937c30d7SJussi Kivilinna }
542937c30d7SJussi Kivilinna 
543937c30d7SJussi Kivilinna static void ablk_init_common(struct crypto_tfm *tfm,
544937c30d7SJussi Kivilinna 			     struct cryptd_ablkcipher *cryptd_tfm)
545937c30d7SJussi Kivilinna {
546937c30d7SJussi Kivilinna 	struct async_serpent_ctx *ctx = crypto_tfm_ctx(tfm);
547937c30d7SJussi Kivilinna 
548937c30d7SJussi Kivilinna 	ctx->cryptd_tfm = cryptd_tfm;
549937c30d7SJussi Kivilinna 	tfm->crt_ablkcipher.reqsize = sizeof(struct ablkcipher_request) +
550937c30d7SJussi Kivilinna 		crypto_ablkcipher_reqsize(&cryptd_tfm->base);
551937c30d7SJussi Kivilinna }
552937c30d7SJussi Kivilinna 
553937c30d7SJussi Kivilinna static int ablk_ecb_init(struct crypto_tfm *tfm)
554937c30d7SJussi Kivilinna {
555937c30d7SJussi Kivilinna 	struct cryptd_ablkcipher *cryptd_tfm;
556937c30d7SJussi Kivilinna 
557937c30d7SJussi Kivilinna 	cryptd_tfm = cryptd_alloc_ablkcipher("__driver-ecb-serpent-sse2", 0, 0);
558937c30d7SJussi Kivilinna 	if (IS_ERR(cryptd_tfm))
559937c30d7SJussi Kivilinna 		return PTR_ERR(cryptd_tfm);
560937c30d7SJussi Kivilinna 	ablk_init_common(tfm, cryptd_tfm);
561937c30d7SJussi Kivilinna 	return 0;
562937c30d7SJussi Kivilinna }
563937c30d7SJussi Kivilinna 
564937c30d7SJussi Kivilinna static struct crypto_alg ablk_ecb_alg = {
565937c30d7SJussi Kivilinna 	.cra_name		= "ecb(serpent)",
566937c30d7SJussi Kivilinna 	.cra_driver_name	= "ecb-serpent-sse2",
567937c30d7SJussi Kivilinna 	.cra_priority		= 400,
568937c30d7SJussi Kivilinna 	.cra_flags		= CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
569937c30d7SJussi Kivilinna 	.cra_blocksize		= SERPENT_BLOCK_SIZE,
570937c30d7SJussi Kivilinna 	.cra_ctxsize		= sizeof(struct async_serpent_ctx),
571937c30d7SJussi Kivilinna 	.cra_alignmask		= 0,
572937c30d7SJussi Kivilinna 	.cra_type		= &crypto_ablkcipher_type,
573937c30d7SJussi Kivilinna 	.cra_module		= THIS_MODULE,
574937c30d7SJussi Kivilinna 	.cra_list		= LIST_HEAD_INIT(ablk_ecb_alg.cra_list),
575937c30d7SJussi Kivilinna 	.cra_init		= ablk_ecb_init,
576937c30d7SJussi Kivilinna 	.cra_exit		= ablk_exit,
577937c30d7SJussi Kivilinna 	.cra_u = {
578937c30d7SJussi Kivilinna 		.ablkcipher = {
579937c30d7SJussi Kivilinna 			.min_keysize	= SERPENT_MIN_KEY_SIZE,
580937c30d7SJussi Kivilinna 			.max_keysize	= SERPENT_MAX_KEY_SIZE,
581937c30d7SJussi Kivilinna 			.setkey		= ablk_set_key,
582937c30d7SJussi Kivilinna 			.encrypt	= ablk_encrypt,
583937c30d7SJussi Kivilinna 			.decrypt	= ablk_decrypt,
584937c30d7SJussi Kivilinna 		},
585937c30d7SJussi Kivilinna 	},
586937c30d7SJussi Kivilinna };
587937c30d7SJussi Kivilinna 
588937c30d7SJussi Kivilinna static int ablk_cbc_init(struct crypto_tfm *tfm)
589937c30d7SJussi Kivilinna {
590937c30d7SJussi Kivilinna 	struct cryptd_ablkcipher *cryptd_tfm;
591937c30d7SJussi Kivilinna 
592937c30d7SJussi Kivilinna 	cryptd_tfm = cryptd_alloc_ablkcipher("__driver-cbc-serpent-sse2", 0, 0);
593937c30d7SJussi Kivilinna 	if (IS_ERR(cryptd_tfm))
594937c30d7SJussi Kivilinna 		return PTR_ERR(cryptd_tfm);
595937c30d7SJussi Kivilinna 	ablk_init_common(tfm, cryptd_tfm);
596937c30d7SJussi Kivilinna 	return 0;
597937c30d7SJussi Kivilinna }
598937c30d7SJussi Kivilinna 
599937c30d7SJussi Kivilinna static struct crypto_alg ablk_cbc_alg = {
600937c30d7SJussi Kivilinna 	.cra_name		= "cbc(serpent)",
601937c30d7SJussi Kivilinna 	.cra_driver_name	= "cbc-serpent-sse2",
602937c30d7SJussi Kivilinna 	.cra_priority		= 400,
603937c30d7SJussi Kivilinna 	.cra_flags		= CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
604937c30d7SJussi Kivilinna 	.cra_blocksize		= SERPENT_BLOCK_SIZE,
605937c30d7SJussi Kivilinna 	.cra_ctxsize		= sizeof(struct async_serpent_ctx),
606937c30d7SJussi Kivilinna 	.cra_alignmask		= 0,
607937c30d7SJussi Kivilinna 	.cra_type		= &crypto_ablkcipher_type,
608937c30d7SJussi Kivilinna 	.cra_module		= THIS_MODULE,
609937c30d7SJussi Kivilinna 	.cra_list		= LIST_HEAD_INIT(ablk_cbc_alg.cra_list),
610937c30d7SJussi Kivilinna 	.cra_init		= ablk_cbc_init,
611937c30d7SJussi Kivilinna 	.cra_exit		= ablk_exit,
612937c30d7SJussi Kivilinna 	.cra_u = {
613937c30d7SJussi Kivilinna 		.ablkcipher = {
614937c30d7SJussi Kivilinna 			.min_keysize	= SERPENT_MIN_KEY_SIZE,
615937c30d7SJussi Kivilinna 			.max_keysize	= SERPENT_MAX_KEY_SIZE,
616937c30d7SJussi Kivilinna 			.ivsize		= SERPENT_BLOCK_SIZE,
617937c30d7SJussi Kivilinna 			.setkey		= ablk_set_key,
618937c30d7SJussi Kivilinna 			.encrypt	= __ablk_encrypt,
619937c30d7SJussi Kivilinna 			.decrypt	= ablk_decrypt,
620937c30d7SJussi Kivilinna 		},
621937c30d7SJussi Kivilinna 	},
622937c30d7SJussi Kivilinna };
623937c30d7SJussi Kivilinna 
624937c30d7SJussi Kivilinna static int ablk_ctr_init(struct crypto_tfm *tfm)
625937c30d7SJussi Kivilinna {
626937c30d7SJussi Kivilinna 	struct cryptd_ablkcipher *cryptd_tfm;
627937c30d7SJussi Kivilinna 
628937c30d7SJussi Kivilinna 	cryptd_tfm = cryptd_alloc_ablkcipher("__driver-ctr-serpent-sse2", 0, 0);
629937c30d7SJussi Kivilinna 	if (IS_ERR(cryptd_tfm))
630937c30d7SJussi Kivilinna 		return PTR_ERR(cryptd_tfm);
631937c30d7SJussi Kivilinna 	ablk_init_common(tfm, cryptd_tfm);
632937c30d7SJussi Kivilinna 	return 0;
633937c30d7SJussi Kivilinna }
634937c30d7SJussi Kivilinna 
635937c30d7SJussi Kivilinna static struct crypto_alg ablk_ctr_alg = {
636937c30d7SJussi Kivilinna 	.cra_name		= "ctr(serpent)",
637937c30d7SJussi Kivilinna 	.cra_driver_name	= "ctr-serpent-sse2",
638937c30d7SJussi Kivilinna 	.cra_priority		= 400,
639937c30d7SJussi Kivilinna 	.cra_flags		= CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
640937c30d7SJussi Kivilinna 	.cra_blocksize		= 1,
641937c30d7SJussi Kivilinna 	.cra_ctxsize		= sizeof(struct async_serpent_ctx),
642937c30d7SJussi Kivilinna 	.cra_alignmask		= 0,
643937c30d7SJussi Kivilinna 	.cra_type		= &crypto_ablkcipher_type,
644937c30d7SJussi Kivilinna 	.cra_module		= THIS_MODULE,
645937c30d7SJussi Kivilinna 	.cra_list		= LIST_HEAD_INIT(ablk_ctr_alg.cra_list),
646937c30d7SJussi Kivilinna 	.cra_init		= ablk_ctr_init,
647937c30d7SJussi Kivilinna 	.cra_exit		= ablk_exit,
648937c30d7SJussi Kivilinna 	.cra_u = {
649937c30d7SJussi Kivilinna 		.ablkcipher = {
650937c30d7SJussi Kivilinna 			.min_keysize	= SERPENT_MIN_KEY_SIZE,
651937c30d7SJussi Kivilinna 			.max_keysize	= SERPENT_MAX_KEY_SIZE,
652937c30d7SJussi Kivilinna 			.ivsize		= SERPENT_BLOCK_SIZE,
653937c30d7SJussi Kivilinna 			.setkey		= ablk_set_key,
654937c30d7SJussi Kivilinna 			.encrypt	= ablk_encrypt,
655937c30d7SJussi Kivilinna 			.decrypt	= ablk_encrypt,
656937c30d7SJussi Kivilinna 			.geniv		= "chainiv",
657937c30d7SJussi Kivilinna 		},
658937c30d7SJussi Kivilinna 	},
659937c30d7SJussi Kivilinna };
660937c30d7SJussi Kivilinna 
661937c30d7SJussi Kivilinna static int __init serpent_sse2_init(void)
662937c30d7SJussi Kivilinna {
663937c30d7SJussi Kivilinna 	int err;
664937c30d7SJussi Kivilinna 
665937c30d7SJussi Kivilinna 	if (!cpu_has_xmm2) {
666937c30d7SJussi Kivilinna 		printk(KERN_INFO "SSE2 instructions are not detected.\n");
667937c30d7SJussi Kivilinna 		return -ENODEV;
668937c30d7SJussi Kivilinna 	}
669937c30d7SJussi Kivilinna 
670937c30d7SJussi Kivilinna 	err = crypto_register_alg(&blk_ecb_alg);
671937c30d7SJussi Kivilinna 	if (err)
672937c30d7SJussi Kivilinna 		goto blk_ecb_err;
673937c30d7SJussi Kivilinna 	err = crypto_register_alg(&blk_cbc_alg);
674937c30d7SJussi Kivilinna 	if (err)
675937c30d7SJussi Kivilinna 		goto blk_cbc_err;
676937c30d7SJussi Kivilinna 	err = crypto_register_alg(&blk_ctr_alg);
677937c30d7SJussi Kivilinna 	if (err)
678937c30d7SJussi Kivilinna 		goto blk_ctr_err;
679937c30d7SJussi Kivilinna 	err = crypto_register_alg(&ablk_ecb_alg);
680937c30d7SJussi Kivilinna 	if (err)
681937c30d7SJussi Kivilinna 		goto ablk_ecb_err;
682937c30d7SJussi Kivilinna 	err = crypto_register_alg(&ablk_cbc_alg);
683937c30d7SJussi Kivilinna 	if (err)
684937c30d7SJussi Kivilinna 		goto ablk_cbc_err;
685937c30d7SJussi Kivilinna 	err = crypto_register_alg(&ablk_ctr_alg);
686937c30d7SJussi Kivilinna 	if (err)
687937c30d7SJussi Kivilinna 		goto ablk_ctr_err;
688937c30d7SJussi Kivilinna 	return err;
689937c30d7SJussi Kivilinna 
690937c30d7SJussi Kivilinna ablk_ctr_err:
691937c30d7SJussi Kivilinna 	crypto_unregister_alg(&ablk_cbc_alg);
692937c30d7SJussi Kivilinna ablk_cbc_err:
693937c30d7SJussi Kivilinna 	crypto_unregister_alg(&ablk_ecb_alg);
694937c30d7SJussi Kivilinna ablk_ecb_err:
695937c30d7SJussi Kivilinna 	crypto_unregister_alg(&blk_ctr_alg);
696937c30d7SJussi Kivilinna blk_ctr_err:
697937c30d7SJussi Kivilinna 	crypto_unregister_alg(&blk_cbc_alg);
698937c30d7SJussi Kivilinna blk_cbc_err:
699937c30d7SJussi Kivilinna 	crypto_unregister_alg(&blk_ecb_alg);
700937c30d7SJussi Kivilinna blk_ecb_err:
701937c30d7SJussi Kivilinna 	return err;
702937c30d7SJussi Kivilinna }
703937c30d7SJussi Kivilinna 
704937c30d7SJussi Kivilinna static void __exit serpent_sse2_exit(void)
705937c30d7SJussi Kivilinna {
706937c30d7SJussi Kivilinna 	crypto_unregister_alg(&ablk_ctr_alg);
707937c30d7SJussi Kivilinna 	crypto_unregister_alg(&ablk_cbc_alg);
708937c30d7SJussi Kivilinna 	crypto_unregister_alg(&ablk_ecb_alg);
709937c30d7SJussi Kivilinna 	crypto_unregister_alg(&blk_ctr_alg);
710937c30d7SJussi Kivilinna 	crypto_unregister_alg(&blk_cbc_alg);
711937c30d7SJussi Kivilinna 	crypto_unregister_alg(&blk_ecb_alg);
712937c30d7SJussi Kivilinna }
713937c30d7SJussi Kivilinna 
714937c30d7SJussi Kivilinna module_init(serpent_sse2_init);
715937c30d7SJussi Kivilinna module_exit(serpent_sse2_exit);
716937c30d7SJussi Kivilinna 
717937c30d7SJussi Kivilinna MODULE_DESCRIPTION("Serpent Cipher Algorithm, SSE2 optimized");
718937c30d7SJussi Kivilinna MODULE_LICENSE("GPL");
719937c30d7SJussi Kivilinna MODULE_ALIAS("serpent");
720