PATCH: crypto: Add CryptoAPI support for the Geode LX AES Module

Jordan Crouse jordan.crouse at amd.com
Tue Nov 7 22:37:10 EST 2006


Commit:     f4f038f60ce939186f5347a8c02f06be4ec71c6b
Parent:     e0dc167e4220d0441ac3fa31564e48d0c55ca32e
commit f4f038f60ce939186f5347a8c02f06be4ec71c6b
Author:     Jordan Crouse <jordan.crouse at amd.com>
AuthorDate: Fri Jun 2 11:12:03 2006 -0600
Commit:     David Woodhouse <dwmw2 at infradead.org>
CommitDate: Mon Jun 19 16:23:02 2006 +0100

    PATCH: crypto: Add CryptoAPI support for the Geode LX AES Module
    
    Add CryptoAPI support for the Geode LX hardware based AES encryption
    engine.
    
    Signed-off-by: Jordan Crouse <jordan.crouse at amd.com>
---
 drivers/crypto/Kconfig     |    9 +
 drivers/crypto/Makefile    |    1 
 drivers/crypto/geode-aes.c |  395 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/crypto/geode-aes.h |   42 +++++
 4 files changed, 447 insertions(+), 0 deletions(-)

diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig
index 4263935..efc5c72 100644
--- a/drivers/crypto/Kconfig
+++ b/drivers/crypto/Kconfig
@@ -20,4 +20,13 @@ config CRYPTO_DEV_PADLOCK_AES
 	help
 	  Use VIA PadLock for AES algorithm.
 
+config CRYPTO_DEV_GEODE
+	tristate "Support for the Geode LX AES engine"
+	depends on CRYPTO && X86_32
+	help
+	  Say 'Y' here to use the AMD Geode LX processor on-board AES
+	  engine for the CryptoAPI AES alogrithm.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called geode-aes.
 endmenu
diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile
index 45426ca..9093ed0 100644
--- a/drivers/crypto/Makefile
+++ b/drivers/crypto/Makefile
@@ -1,5 +1,6 @@
 
 obj-$(CONFIG_CRYPTO_DEV_PADLOCK) += padlock.o
+obj-$(CONFIG_CRYPTO_DEV_GEODE) += geode-aes.o
 
 padlock-objs-$(CONFIG_CRYPTO_DEV_PADLOCK_AES) += padlock-aes.o
 
diff --git a/drivers/crypto/geode-aes.c b/drivers/crypto/geode-aes.c
new file mode 100644
index 0000000..90ccec4
--- /dev/null
+++ b/drivers/crypto/geode-aes.c
@@ -0,0 +1,395 @@
+/* CryptoAPI interface for the Geode LX hardware AES encryption module
+ * Copyright (C) 2004-2006, Advanced Micro Devices, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+#include <linux/crypto.h>
+
+#include <asm/io.h>
+#include <asm/delay.h>
+
+#include "geode-aes.h"
+
+/* Register definitions */
+
+#define AES_CTRLA_REG  0x0000
+
+#define AES_CTRL_START     0x01
+#define AES_CTRL_DECRYPT   0x00
+#define AES_CTRL_ENCRYPT   0x02
+#define AES_CTRL_WRKEY     0x04
+#define AES_CTRL_DCA       0x08
+#define AES_CTRL_SCA       0x10
+#define AES_CTRL_CBC       0x20
+
+#define AES_INTR_REG  0x0008
+
+#define AES_INTRA_PENDING (1 << 16)
+#define AES_INTRB_PENDING (1 << 17)
+
+#define AES_INTR_PENDING  (AES_INTRA_PENDING | AES_INTRB_PENDING)
+#define AES_INTR_MASK     0x07
+
+#define AES_SOURCEA_REG   0x0010
+#define AES_DSTA_REG      0x0014
+#define AES_LENA_REG      0x0018
+#define AES_WRITEKEY0_REG 0x0030
+#define AES_WRITEIV0_REG  0x0040
+
+/* Useful macros */
+
+#define SET_KEY(key) _writefield(AES_WRITEKEY0_REG, key)
+#define SET_IV(iv)   _writefield(AES_WRITEIV0_REG, iv)
+#define GET_IV(iv)   _readfield(AES_WRITEIV0_REG, iv)
+
+/* Static structures */
+
+static void __iomem * _iobase;
+static DEFINE_MUTEX(emutex);
+
+static inline void AWRITE(unsigned long val, unsigned short reg) {
+#ifdef DEBUG
+	printk("[AES] W [%x]=%x\n", reg, val);
+#endif
+	iowrite32(val, _iobase + reg);
+}
+
+static inline unsigned int AREAD(unsigned short reg) {
+	unsigned int val = ioread32(_iobase + reg);
+#ifdef DEBUG
+	printk("[AES] R [%x]=%x\n", reg, val);
+#endif
+	return val;
+}
+
+/* Write a 128 bit field (either a writable key or IV) */
+static inline void
+_writefield(u32 offset, void *value)
+{
+	int i;
+	for(i = 0; i < 4; i++)
+		AWRITE(((u32 *) value)[i], offset + (i * 4));
+}
+
+/* Read a 128 bit field (either a writable key or IV) */
+static inline void
+_readfield(u32 offset, void *value)
+{
+	int i;
+	for(i = 0; i < 4; i++)
+		((u32 *) value)[i] = AREAD(offset + (i * 4));
+}
+
+static void
+_crypt(void *src, void *dst, int len, u32 flags)
+{
+	u32 status;
+
+	AWRITE(__pa(src), AES_SOURCEA_REG);
+	AWRITE(__pa(dst), AES_DSTA_REG);
+	AWRITE(len,  AES_LENA_REG);
+
+	/* Start the operation */
+	AWRITE(AES_CTRL_START | flags, AES_CTRLA_REG);
+
+	/* According to the silicon developers, the status will only
+	 * fail to clear on an catastrophic failure, so an infinite
+	 * loop is valid here
+	 */
+
+	do
+		status = AREAD(AES_INTR_REG);
+	while(!(status & AES_INTRA_PENDING));
+
+	/* Clear the event */
+	AWRITE((status & 0xFF) | AES_INTRA_PENDING, AES_INTR_REG);
+}
+
+unsigned int
+geode_aes_crypt(struct geode_aes_op *op)
+{
+	u32 flags = 0;
+
+	if (op->len == 0 || op->src == op->dst)
+		return 0;
+
+	if (mutex_lock_interruptible(&emutex))
+		return 0;
+
+	if (op->mode == AES_MODE_CBC) {
+		flags |= AES_CTRL_CBC;
+		SET_IV(op->iv);
+	}
+
+	if (op->flags & AES_FLAGS_USRKEY) {
+		flags |= AES_CTRL_WRKEY;
+		SET_KEY(op->key);
+	}
+
+	if (op->flags & AES_FLAGS_COHERENT)
+		flags |= (AES_CTRL_DCA | AES_CTRL_SCA);
+
+	if (op->dir == AES_DIR_ENCRYPT)
+		flags |= AES_CTRL_ENCRYPT;
+
+	_crypt(op->src, op->dst, op->len, flags);
+
+	if (op->mode == AES_MODE_CBC)
+		GET_IV(op->iv);
+
+	mutex_unlock(&emutex);
+	return op->len;
+}
+
+/* CRYPTO-API Functions */
+
+static int
+geode_aes_setkey(void *data, const u8 *key, unsigned int len, u32 *flags)
+{
+
+	struct geode_aes_op *op = (struct geode_aes_op *) data;
+
+	if (len != AES_KEY_LENGTH) {
+		*flags |= CRYPTO_TFM_RES_BAD_KEY_LEN;
+		return -EINVAL;
+	}
+
+	memcpy(op->key, key, len);
+	return 0;
+}
+
+static void
+geode_aes_encrypt(void *ctx, u8 *out, const u8 *in)
+{
+	struct geode_aes_op *op = (struct geode_aes_op *) ctx;
+
+	if ((out == NULL) || (in == NULL))
+		return;
+
+	op->src = (void *) in;
+	op->dst = (void *) out;
+	op->mode = AES_MODE_CBC;
+	op->flags = 0;
+	op->len = AES_MIN_BLOCK_SIZE;
+	op->dir = AES_DIR_ENCRYPT;
+
+	geode_aes_crypt(op);
+}
+
+
+static void
+geode_aes_decrypt(void *ctx, u8 *out, const u8 *in)
+{
+	struct geode_aes_op *op = (struct geode_aes_op *) ctx;
+
+	if ((out == NULL) || (in == NULL))
+		return;
+
+	op->src = (void *) in;
+	op->dst = (void *) out;
+	op->mode = AES_MODE_CBC;
+	op->flags = 0;
+	op->len = AES_MIN_BLOCK_SIZE;
+	op->dir = AES_DIR_DECRYPT;
+
+	geode_aes_crypt(op);
+}
+
+static unsigned int
+geode_aes_encrypt_ecb(const struct cipher_desc *desc, u8 *out,
+		      const u8 *in, unsigned int nbytes)
+{
+	struct geode_aes_op *op = crypto_tfm_ctx(desc->tfm);
+
+	op->src = (void *) in;
+	op->dst = (void *) out;
+	op->mode = AES_MODE_ECB;
+	op->flags = 0;
+	op->len = nbytes - (nbytes % AES_MIN_BLOCK_SIZE);
+	op->dir = AES_DIR_ENCRYPT;
+
+	return geode_aes_crypt(op);
+}
+
+static unsigned int
+geode_aes_decrypt_ecb(const struct cipher_desc *desc, u8 *out,
+		      const u8 *in, unsigned int nbytes)
+{
+	struct geode_aes_op *op = crypto_tfm_ctx(desc->tfm);
+
+	op->src = (void *) in;
+	op->dst = (void *) out;
+	op->mode = AES_MODE_ECB;
+	op->flags = 0;
+	op->len = nbytes - (nbytes % AES_MIN_BLOCK_SIZE);
+	op->dir = AES_DIR_DECRYPT;
+
+	return geode_aes_crypt(op);
+}
+
+static unsigned int
+geode_aes_encrypt_cbc(const struct cipher_desc *desc, u8 *out,
+		      const u8 *in, unsigned int nbytes)
+{
+	struct geode_aes_op *op = crypto_tfm_ctx(desc->tfm);
+	unsigned int ret;
+
+	op->src = (void *) in;
+	op->dst = (void *) out;
+	op->mode = AES_MODE_CBC;
+	op->len = nbytes - (nbytes % AES_MIN_BLOCK_SIZE);
+	op->dir = AES_DIR_ENCRYPT;
+
+	memcpy(op->iv, desc->info, AES_IV_LENGTH);
+
+	ret = geode_aes_crypt(op);
+
+	memcpy(desc->info, op->iv, AES_IV_LENGTH);
+	return ret;
+}
+
+static unsigned int
+geode_aes_decrypt_cbc(const struct cipher_desc *desc, u8 *out,
+		      const u8 *in, unsigned int nbytes)
+{
+	struct geode_aes_op *op = crypto_tfm_ctx(desc->tfm);
+	unsigned int ret;
+
+	op->src = (void *) in;
+	op->dst = (void *) out;
+	op->mode = AES_MODE_CBC;
+	op->len = nbytes - (nbytes % AES_MIN_BLOCK_SIZE);
+	op->dir = AES_DIR_DECRYPT;
+
+	memcpy(op->iv, desc->info, AES_IV_LENGTH);
+
+	ret = geode_aes_crypt(op);
+
+	memcpy(desc->info, op->iv, AES_IV_LENGTH);
+	return ret;
+}
+
+static struct crypto_alg geode_aes_crypto = {
+	.cra_name               =       "aes",
+	.cra_driver_name	=       "geode-aes",
+	.cra_priority           =       110,
+	.cra_alignmask          =       15,
+	.cra_flags		=	CRYPTO_ALG_TYPE_CIPHER,
+	.cra_blocksize		=	AES_MIN_BLOCK_SIZE,
+	.cra_ctxsize		=	sizeof(struct geode_aes_op),
+	.cra_module		=	THIS_MODULE,
+	.cra_list		=	LIST_HEAD_INIT(geode_aes_crypto.cra_list),
+	.cra_u			=	{
+		.cipher = {
+			.cia_min_keysize	=  AES_KEY_LENGTH,
+			.cia_max_keysize	=  AES_KEY_LENGTH,
+			.cia_setkey		=  geode_aes_setkey,
+			.cia_encrypt		=  geode_aes_encrypt,
+			.cia_decrypt		=  geode_aes_decrypt,
+			.cia_encrypt_ecb	=  geode_aes_encrypt_ecb,
+			.cia_decrypt_ecb	=  geode_aes_decrypt_ecb,
+			.cia_encrypt_cbc	=  geode_aes_encrypt_cbc,
+			.cia_decrypt_cbc	=  geode_aes_decrypt_cbc,
+		}
+	}
+};
+
+static void
+geode_aes_remove(struct pci_dev *dev)
+{
+	crypto_unregister_alg(&geode_aes_crypto);
+
+	pci_iounmap(dev, _iobase);
+	_iobase = NULL;
+
+	pci_release_regions(dev);
+	pci_disable_device(dev);
+
+	mutex_destroy(&emutex);
+}
+
+
+static int
+geode_aes_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+	int ret;
+
+	if ((ret = pci_enable_device(dev)))
+		return ret;
+
+	if ((ret = pci_request_regions(dev, "geode-aes")))
+		goto eenable;
+
+	_iobase = pci_iomap(dev, 0, 0);
+
+	if (_iobase == NULL) {
+		ret = -ENOMEM;
+		goto erequest;
+	}
+
+	/* Clear any pending activity */
+	AWRITE(AES_INTR_PENDING | AES_INTR_MASK, AES_INTR_REG);
+
+	mutex_init(&emutex);
+
+	ret = crypto_register_alg(&geode_aes_crypto);
+
+	if (ret == 0) {
+	  printk(KERN_NOTICE "geode-aes: GEODE AES engine enabled.\n");
+	  return 0;
+	}
+
+	crypto_unregister_alg(&geode_aes_crypto);
+
+	pci_iounmap(dev, _iobase);
+
+ erequest:
+	pci_release_regions(dev);
+
+ eenable:
+	pci_disable_device(dev);
+
+	return ret;
+}
+
+struct pci_device_id geode_aes_tbl[] = {
+	{ PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LX_AES, PCI_ANY_ID, PCI_ANY_ID} ,
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, geode_aes_tbl);
+
+static struct pci_driver geode_aes_driver = {
+	name:      "Geode LX AES",
+	id_table:  geode_aes_tbl,
+	probe:     geode_aes_probe,
+	remove:    __devexit_p(geode_aes_remove)
+};
+
+static int __devinit
+geode_aes_init(void)
+{
+	return pci_module_init(&geode_aes_driver);
+}
+
+static void __devexit
+geode_aes_exit(void)
+{
+	pci_unregister_driver(&geode_aes_driver);
+}
+
+MODULE_AUTHOR("Advanced Micro Devices, Inc.");
+MODULE_DESCRIPTION("Geode LX Hardware AES driver");
+MODULE_LICENSE("GPL");
+
+module_init(geode_aes_init);
+module_exit(geode_aes_exit);
diff --git a/drivers/crypto/geode-aes.h b/drivers/crypto/geode-aes.h
new file mode 100644
index 0000000..97f9eee
--- /dev/null
+++ b/drivers/crypto/geode-aes.h
@@ -0,0 +1,42 @@
+/* Copyright (C) 2003-2006, Advanced Micro Devices, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef _GEODE_AES_H_
+#define _GEODE_AES_H_
+
+#define AES_KEY_LENGTH 16
+#define AES_IV_LENGTH  16
+
+#define AES_MIN_BLOCK_SIZE 16
+
+#define AES_MODE_ECB 0
+#define AES_MODE_CBC 1
+
+#define AES_DIR_DECRYPT 0
+#define AES_DIR_ENCRYPT 1
+
+#define AES_FLAGS_USRKEY   (1 << 0)
+#define AES_FLAGS_COHERENT (1 << 1)
+
+struct geode_aes_op {
+
+  void *src;
+  void *dst;
+
+  u32 mode;
+  u32 dir;
+  u32 flags;
+  int len;
+
+  u8 key[AES_KEY_LENGTH];
+  u8 iv[AES_IV_LENGTH];
+};
+
+unsigned int geode_aes_crypt(struct geode_aes_op *);
+
+#endif


More information about the Commits-kernel mailing list