geode-aes.c revision 1f4e4773761d0aa622411469b54d6570005a66b1
1 /* Copyright (C) 2004-2006, Advanced Micro Devices, Inc.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 */
8
9#include <linux/module.h>
10#include <linux/kernel.h>
11#include <linux/pci.h>
12#include <linux/pci_ids.h>
13#include <linux/crypto.h>
14#include <linux/spinlock.h>
15#include <crypto/algapi.h>
16#include <crypto/aes.h>
17
18#include <asm/io.h>
19#include <asm/delay.h>
20
21#include "geode-aes.h"
22
23/* Register definitions */
24
25#define AES_CTRLA_REG  0x0000
26
27#define AES_CTRL_START     0x01
28#define AES_CTRL_DECRYPT   0x00
29#define AES_CTRL_ENCRYPT   0x02
30#define AES_CTRL_WRKEY     0x04
31#define AES_CTRL_DCA       0x08
32#define AES_CTRL_SCA       0x10
33#define AES_CTRL_CBC       0x20
34
35#define AES_INTR_REG  0x0008
36
37#define AES_INTRA_PENDING (1 << 16)
38#define AES_INTRB_PENDING (1 << 17)
39
40#define AES_INTR_PENDING  (AES_INTRA_PENDING | AES_INTRB_PENDING)
41#define AES_INTR_MASK     0x07
42
43#define AES_SOURCEA_REG   0x0010
44#define AES_DSTA_REG      0x0014
45#define AES_LENA_REG      0x0018
46#define AES_WRITEKEY0_REG 0x0030
47#define AES_WRITEIV0_REG  0x0040
48
49/*  A very large counter that is used to gracefully bail out of an
50 *  operation in case of trouble
51 */
52
53#define AES_OP_TIMEOUT    0x50000
54
55/* Static structures */
56
57static void __iomem * _iobase;
58static spinlock_t lock;
59
60/* Write a 128 bit field (either a writable key or IV) */
61static inline void
62_writefield(u32 offset, void *value)
63{
64	int i;
65	for(i = 0; i < 4; i++)
66		iowrite32(((u32 *) value)[i], _iobase + offset + (i * 4));
67}
68
69/* Read a 128 bit field (either a writable key or IV) */
70static inline void
71_readfield(u32 offset, void *value)
72{
73	int i;
74	for(i = 0; i < 4; i++)
75		((u32 *) value)[i] = ioread32(_iobase + offset + (i * 4));
76}
77
78static int
79do_crypt(void *src, void *dst, int len, u32 flags)
80{
81	u32 status;
82	u32 counter = AES_OP_TIMEOUT;
83
84	iowrite32(virt_to_phys(src), _iobase + AES_SOURCEA_REG);
85	iowrite32(virt_to_phys(dst), _iobase + AES_DSTA_REG);
86	iowrite32(len,  _iobase + AES_LENA_REG);
87
88	/* Start the operation */
89	iowrite32(AES_CTRL_START | flags, _iobase + AES_CTRLA_REG);
90
91	do {
92		status = ioread32(_iobase + AES_INTR_REG);
93		cpu_relax();
94	} while(!(status & AES_INTRA_PENDING) && --counter);
95
96	/* Clear the event */
97	iowrite32((status & 0xFF) | AES_INTRA_PENDING, _iobase + AES_INTR_REG);
98	return counter ? 0 : 1;
99}
100
101static unsigned int
102geode_aes_crypt(struct geode_aes_op *op)
103{
104	u32 flags = 0;
105	unsigned long iflags;
106	int ret;
107
108	if (op->len == 0)
109		return 0;
110
111	/* If the source and destination is the same, then
112	 * we need to turn on the coherent flags, otherwise
113	 * we don't need to worry
114	 */
115
116	flags |= (AES_CTRL_DCA | AES_CTRL_SCA);
117
118	if (op->dir == AES_DIR_ENCRYPT)
119		flags |= AES_CTRL_ENCRYPT;
120
121	/* Start the critical section */
122
123	spin_lock_irqsave(&lock, iflags);
124
125	if (op->mode == AES_MODE_CBC) {
126		flags |= AES_CTRL_CBC;
127		_writefield(AES_WRITEIV0_REG, op->iv);
128	}
129
130	if (!(op->flags & AES_FLAGS_HIDDENKEY)) {
131		flags |= AES_CTRL_WRKEY;
132		_writefield(AES_WRITEKEY0_REG, op->key);
133	}
134
135	ret = do_crypt(op->src, op->dst, op->len, flags);
136	BUG_ON(ret);
137
138	if (op->mode == AES_MODE_CBC)
139		_readfield(AES_WRITEIV0_REG, op->iv);
140
141	spin_unlock_irqrestore(&lock, iflags);
142
143	return op->len;
144}
145
146/* CRYPTO-API Functions */
147
148static int
149geode_setkey(struct crypto_tfm *tfm, const u8 *key, unsigned int len)
150{
151	struct geode_aes_op *op = crypto_tfm_ctx(tfm);
152
153	if (len != AES_KEY_LENGTH) {
154		tfm->crt_flags |= CRYPTO_TFM_RES_BAD_KEY_LEN;
155		return -EINVAL;
156	}
157
158	memcpy(op->key, key, len);
159	return 0;
160}
161
162static void
163geode_encrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in)
164{
165	struct geode_aes_op *op = crypto_tfm_ctx(tfm);
166
167	if ((out == NULL) || (in == NULL))
168		return;
169
170	op->src = (void *) in;
171	op->dst = (void *) out;
172	op->mode = AES_MODE_ECB;
173	op->flags = 0;
174	op->len = AES_MIN_BLOCK_SIZE;
175	op->dir = AES_DIR_ENCRYPT;
176
177	geode_aes_crypt(op);
178}
179
180
181static void
182geode_decrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in)
183{
184	struct geode_aes_op *op = crypto_tfm_ctx(tfm);
185
186	if ((out == NULL) || (in == NULL))
187		return;
188
189	op->src = (void *) in;
190	op->dst = (void *) out;
191	op->mode = AES_MODE_ECB;
192	op->flags = 0;
193	op->len = AES_MIN_BLOCK_SIZE;
194	op->dir = AES_DIR_DECRYPT;
195
196	geode_aes_crypt(op);
197}
198
199
200static struct crypto_alg geode_alg = {
201	.cra_name               =       "aes",
202	.cra_driver_name	=       "geode-aes-128",
203	.cra_priority           =       300,
204	.cra_alignmask          =       15,
205	.cra_flags		=	CRYPTO_ALG_TYPE_CIPHER,
206	.cra_blocksize		=	AES_MIN_BLOCK_SIZE,
207	.cra_ctxsize		=	sizeof(struct geode_aes_op),
208	.cra_module		=	THIS_MODULE,
209	.cra_list		=	LIST_HEAD_INIT(geode_alg.cra_list),
210	.cra_u			=	{
211		.cipher = {
212			.cia_min_keysize	=  AES_KEY_LENGTH,
213			.cia_max_keysize	=  AES_KEY_LENGTH,
214			.cia_setkey		=  geode_setkey,
215			.cia_encrypt		=  geode_encrypt,
216			.cia_decrypt		=  geode_decrypt
217		}
218	}
219};
220
221static int
222geode_cbc_decrypt(struct blkcipher_desc *desc,
223		  struct scatterlist *dst, struct scatterlist *src,
224		  unsigned int nbytes)
225{
226	struct geode_aes_op *op = crypto_blkcipher_ctx(desc->tfm);
227	struct blkcipher_walk walk;
228	int err, ret;
229
230	blkcipher_walk_init(&walk, dst, src, nbytes);
231	err = blkcipher_walk_virt(desc, &walk);
232	memcpy(op->iv, walk.iv, AES_IV_LENGTH);
233
234	while((nbytes = walk.nbytes)) {
235		op->src = walk.src.virt.addr,
236		op->dst = walk.dst.virt.addr;
237		op->mode = AES_MODE_CBC;
238		op->len = nbytes - (nbytes % AES_MIN_BLOCK_SIZE);
239		op->dir = AES_DIR_DECRYPT;
240
241		ret = geode_aes_crypt(op);
242
243		nbytes -= ret;
244		err = blkcipher_walk_done(desc, &walk, nbytes);
245	}
246
247	memcpy(walk.iv, op->iv, AES_IV_LENGTH);
248	return err;
249}
250
251static int
252geode_cbc_encrypt(struct blkcipher_desc *desc,
253		  struct scatterlist *dst, struct scatterlist *src,
254		  unsigned int nbytes)
255{
256	struct geode_aes_op *op = crypto_blkcipher_ctx(desc->tfm);
257	struct blkcipher_walk walk;
258	int err, ret;
259
260	blkcipher_walk_init(&walk, dst, src, nbytes);
261	err = blkcipher_walk_virt(desc, &walk);
262	memcpy(op->iv, walk.iv, AES_IV_LENGTH);
263
264	while((nbytes = walk.nbytes)) {
265		op->src = walk.src.virt.addr,
266		op->dst = walk.dst.virt.addr;
267		op->mode = AES_MODE_CBC;
268		op->len = nbytes - (nbytes % AES_MIN_BLOCK_SIZE);
269		op->dir = AES_DIR_ENCRYPT;
270
271		ret = geode_aes_crypt(op);
272		nbytes -= ret;
273		err = blkcipher_walk_done(desc, &walk, nbytes);
274	}
275
276	memcpy(walk.iv, op->iv, AES_IV_LENGTH);
277	return err;
278}
279
280static struct crypto_alg geode_cbc_alg = {
281	.cra_name		=	"cbc(aes)",
282	.cra_driver_name	=	"cbc-aes-geode-128",
283	.cra_priority		=	400,
284	.cra_flags		=	CRYPTO_ALG_TYPE_BLKCIPHER,
285	.cra_blocksize		=	AES_MIN_BLOCK_SIZE,
286	.cra_ctxsize		=	sizeof(struct geode_aes_op),
287	.cra_alignmask		=	15,
288	.cra_type		=	&crypto_blkcipher_type,
289	.cra_module		=	THIS_MODULE,
290	.cra_list		=	LIST_HEAD_INIT(geode_cbc_alg.cra_list),
291	.cra_u			=	{
292		.blkcipher = {
293			.min_keysize		=	AES_KEY_LENGTH,
294			.max_keysize		=	AES_KEY_LENGTH,
295			.setkey			=	geode_setkey,
296			.encrypt		=	geode_cbc_encrypt,
297			.decrypt		=	geode_cbc_decrypt,
298			.ivsize			=	AES_IV_LENGTH,
299		}
300	}
301};
302
303static int
304geode_ecb_decrypt(struct blkcipher_desc *desc,
305		  struct scatterlist *dst, struct scatterlist *src,
306		  unsigned int nbytes)
307{
308	struct geode_aes_op *op = crypto_blkcipher_ctx(desc->tfm);
309	struct blkcipher_walk walk;
310	int err, ret;
311
312	blkcipher_walk_init(&walk, dst, src, nbytes);
313	err = blkcipher_walk_virt(desc, &walk);
314
315	while((nbytes = walk.nbytes)) {
316		op->src = walk.src.virt.addr,
317		op->dst = walk.dst.virt.addr;
318		op->mode = AES_MODE_ECB;
319		op->len = nbytes - (nbytes % AES_MIN_BLOCK_SIZE);
320		op->dir = AES_DIR_DECRYPT;
321
322		ret = geode_aes_crypt(op);
323		nbytes -= ret;
324		err = blkcipher_walk_done(desc, &walk, nbytes);
325	}
326
327	return err;
328}
329
330static int
331geode_ecb_encrypt(struct blkcipher_desc *desc,
332		  struct scatterlist *dst, struct scatterlist *src,
333		  unsigned int nbytes)
334{
335	struct geode_aes_op *op = crypto_blkcipher_ctx(desc->tfm);
336	struct blkcipher_walk walk;
337	int err, ret;
338
339	blkcipher_walk_init(&walk, dst, src, nbytes);
340	err = blkcipher_walk_virt(desc, &walk);
341
342	while((nbytes = walk.nbytes)) {
343		op->src = walk.src.virt.addr,
344		op->dst = walk.dst.virt.addr;
345		op->mode = AES_MODE_ECB;
346		op->len = nbytes - (nbytes % AES_MIN_BLOCK_SIZE);
347		op->dir = AES_DIR_ENCRYPT;
348
349		ret = geode_aes_crypt(op);
350		nbytes -= ret;
351		ret =  blkcipher_walk_done(desc, &walk, nbytes);
352	}
353
354	return err;
355}
356
357static struct crypto_alg geode_ecb_alg = {
358	.cra_name		=	"ecb(aes)",
359	.cra_driver_name	=	"ecb-aes-geode-128",
360	.cra_priority		=	400,
361	.cra_flags		=	CRYPTO_ALG_TYPE_BLKCIPHER,
362	.cra_blocksize		=	AES_MIN_BLOCK_SIZE,
363	.cra_ctxsize		=	sizeof(struct geode_aes_op),
364	.cra_alignmask		=	15,
365	.cra_type		=	&crypto_blkcipher_type,
366	.cra_module		=	THIS_MODULE,
367	.cra_list		=	LIST_HEAD_INIT(geode_ecb_alg.cra_list),
368	.cra_u			=	{
369		.blkcipher = {
370			.min_keysize		=	AES_KEY_LENGTH,
371			.max_keysize		=	AES_KEY_LENGTH,
372			.setkey			=	geode_setkey,
373			.encrypt		=	geode_ecb_encrypt,
374			.decrypt		=	geode_ecb_decrypt,
375		}
376	}
377};
378
379static void
380geode_aes_remove(struct pci_dev *dev)
381{
382	crypto_unregister_alg(&geode_alg);
383	crypto_unregister_alg(&geode_ecb_alg);
384	crypto_unregister_alg(&geode_cbc_alg);
385
386	pci_iounmap(dev, _iobase);
387	_iobase = NULL;
388
389	pci_release_regions(dev);
390	pci_disable_device(dev);
391}
392
393
394static int
395geode_aes_probe(struct pci_dev *dev, const struct pci_device_id *id)
396{
397	int ret;
398
399	if ((ret = pci_enable_device(dev)))
400		return ret;
401
402	if ((ret = pci_request_regions(dev, "geode-aes-128")))
403		goto eenable;
404
405	_iobase = pci_iomap(dev, 0, 0);
406
407	if (_iobase == NULL) {
408		ret = -ENOMEM;
409		goto erequest;
410	}
411
412	spin_lock_init(&lock);
413
414	/* Clear any pending activity */
415	iowrite32(AES_INTR_PENDING | AES_INTR_MASK, _iobase + AES_INTR_REG);
416
417	if ((ret = crypto_register_alg(&geode_alg)))
418		goto eiomap;
419
420	if ((ret = crypto_register_alg(&geode_ecb_alg)))
421		goto ealg;
422
423	if ((ret = crypto_register_alg(&geode_cbc_alg)))
424		goto eecb;
425
426	printk(KERN_NOTICE "geode-aes: GEODE AES engine enabled.\n");
427	return 0;
428
429 eecb:
430	crypto_unregister_alg(&geode_ecb_alg);
431
432 ealg:
433	crypto_unregister_alg(&geode_alg);
434
435 eiomap:
436	pci_iounmap(dev, _iobase);
437
438 erequest:
439	pci_release_regions(dev);
440
441 eenable:
442	pci_disable_device(dev);
443
444	printk(KERN_ERR "geode-aes:  GEODE AES initialization failed.\n");
445	return ret;
446}
447
448static struct pci_device_id geode_aes_tbl[] = {
449	{ PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LX_AES, PCI_ANY_ID, PCI_ANY_ID} ,
450	{ 0, }
451};
452
453MODULE_DEVICE_TABLE(pci, geode_aes_tbl);
454
455static struct pci_driver geode_aes_driver = {
456	.name = "Geode LX AES",
457	.id_table = geode_aes_tbl,
458	.probe = geode_aes_probe,
459	.remove = __devexit_p(geode_aes_remove)
460};
461
462static int __init
463geode_aes_init(void)
464{
465	return pci_register_driver(&geode_aes_driver);
466}
467
468static void __exit
469geode_aes_exit(void)
470{
471	pci_unregister_driver(&geode_aes_driver);
472}
473
474MODULE_AUTHOR("Advanced Micro Devices, Inc.");
475MODULE_DESCRIPTION("Geode LX Hardware AES driver");
476MODULE_LICENSE("GPL");
477
478module_init(geode_aes_init);
479module_exit(geode_aes_exit);
480