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