2008-09-19 23:46:58

by Loc Ho

[permalink] [raw]
Subject: [PATCH] crypto: AMCC Crypto4xx Device Driver

Hi Herbert,

This is the initial release of crypto device driver for AMCC 4xx SoC. I am forwarding this on behave of James Hsiao as his submission did not show up on the mailing list.

-Loc


Signed-off-by: James Hsiao <[email protected]>
Acked-by: Loc Ho <[email protected]>
---
arch/powerpc/boot/dts/kilauea.dts | 10 +
drivers/crypto/Kconfig | 8 +
drivers/crypto/Makefile | 1 +
drivers/crypto/amcc/Makefile | 27 +
drivers/crypto/amcc/crypto4xx_alg.c | 397 ++++++++++
drivers/crypto/amcc/crypto4xx_core.c | 1228 +++++++++++++++++++++++++++++++
drivers/crypto/amcc/crypto4xx_core.h | 265 +++++++
drivers/crypto/amcc/crypto4xx_reg_def.h | 290 ++++++++
drivers/crypto/amcc/crypto4xx_sa.c | 91 +++
drivers/crypto/amcc/crypto4xx_sa.h | 223 ++++++
10 files changed, 2540 insertions(+), 0 deletions(-)
create mode 100644 drivers/crypto/amcc/Makefile
create mode 100644 drivers/crypto/amcc/crypto4xx_alg.c
create mode 100644 drivers/crypto/amcc/crypto4xx_core.c
create mode 100644 drivers/crypto/amcc/crypto4xx_core.h
create mode 100644 drivers/crypto/amcc/crypto4xx_reg_def.h
create mode 100644 drivers/crypto/amcc/crypto4xx_sa.c
create mode 100644 drivers/crypto/amcc/crypto4xx_sa.h

diff --git a/arch/powerpc/boot/dts/kilauea.dts b/arch/powerpc/boot/dts/kilauea.dts
index dececc4..0ccd2e8 100644
--- a/arch/powerpc/boot/dts/kilauea.dts
+++ b/arch/powerpc/boot/dts/kilauea.dts
@@ -94,6 +94,15 @@
dcr-reg = <0x010 0x002>;
};

+ CRYPTO: crypto@ef700000 {
+ device_type = "crypto";
+ compatible = "crypto4xx-crypto", "amcc,crypto4xx-crypto";
+ reg = <0xef700000 0x80400>;
+ interrupt-parent = <&UIC0>;
+ interrupts = <0x17 0x2>;
+
+ };
+
MAL0: mcmal {
compatible = "ibm,mcmal-405ex", "ibm,mcmal2";
dcr-reg = <0x180 0x062>;
@@ -342,5 +351,6 @@
0x0 0x0 0x0 0x3 &UIC2 0xd 0x4 /* swizzled int C */
0x0 0x0 0x0 0x4 &UIC2 0xe 0x4 /* swizzled int D */>;
};
+
};
};
diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig
index e522144..454bd8c 100644
--- a/drivers/crypto/Kconfig
+++ b/drivers/crypto/Kconfig
@@ -200,4 +200,12 @@ config CRYPTO_DEV_IXP4XX
help
Driver for the IXP4xx NPE crypto engine.

+config CRYPTO_DEV_PPC4XX
+ tristate "Driver AMCC PPC4XX crypto accelerator"
+ select CRYPTO_HASH
+ select CRYPTO_ALGAPI
+ select CRYPTO_BLKCIPHER
+ help
+ This option allows you to have support for AMCC crypto acceleration.
+
endif # CRYPTO_HW
diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile
index 73557b2..9bf4a2b 100644
--- a/drivers/crypto/Makefile
+++ b/drivers/crypto/Makefile
@@ -4,3 +4,4 @@ obj-$(CONFIG_CRYPTO_DEV_GEODE) += geode-aes.o
obj-$(CONFIG_CRYPTO_DEV_HIFN_795X) += hifn_795x.o
obj-$(CONFIG_CRYPTO_DEV_TALITOS) += talitos.o
obj-$(CONFIG_CRYPTO_DEV_IXP4XX) += ixp4xx_crypto.o
+obj-$(CONFIG_CRYPTO_DEV_PPC4XX) += amcc/
diff --git a/drivers/crypto/amcc/Makefile b/drivers/crypto/amcc/Makefile
new file mode 100644
index 0000000..4b06655
--- /dev/null
+++ b/drivers/crypto/amcc/Makefile
@@ -0,0 +1,27 @@
+################################################################################
+# (C) Copyright 2007 Applied Micro Circuits Corporation
+# James Hsiao, AMCC, [email protected]
+#
+# 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.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+# MA 02111-1307 USA
+#
+################################################################################
+#
+# Makefile for the AMCC Crypto Acclerator Device Driver
+#
+
+obj-$(CONFIG_CRYPTO_DEV_PPC4XX) += crypto4xx.o
+
+crypto4xx-objs := crypto4xx_core.o crypto4xx_alg.o crypto4xx_sa.o
diff --git a/drivers/crypto/amcc/crypto4xx_alg.c b/drivers/crypto/amcc/crypto4xx_alg.c
new file mode 100644
index 0000000..76fc7ff
--- /dev/null
+++ b/drivers/crypto/amcc/crypto4xx_alg.c
@@ -0,0 +1,397 @@
+/*****************************************************************************
+ * AMCC SoC Crypto4XX Driver
+ *
+ * Copyright (c) 2008 Applied Micro Circuits Corporation.
+ * All rights reserved. James Hsiao <[email protected]>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * @file crypto4xx_alg.c
+ *
+ * This file implements the Linux crypto algorithms.
+ *
+ *****************************************************************************
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/mod_devicetable.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock_types.h>
+#include <linux/highmem.h>
+#include <linux/scatterlist.h>
+#include <linux/crypto.h>
+#include <linux/hash.h>
+#include <linux/pci.h>
+#include <linux/rtnetlink.h>
+#include <crypto/aead.h>
+#include <crypto/algapi.h>
+#include <crypto/des.h>
+#include <crypto/authenc.h>
+#include <crypto/hash.h>
+#include <crypto/internal/hash.h>
+
+#include "crypto4xx_reg_def.h"
+#include "crypto4xx_sa.h"
+#include "crypto4xx_core.h"
+
+static inline int crypto4xx_encrypt(struct ablkcipher_request *req)
+{
+ struct crypto4xx_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+ struct crypto4xx_ctx *rctx = ablkcipher_request_ctx(req);
+ int rc;
+ u32 offset;
+ struct dynamic_sa_ctl *sa = NULL;
+
+ /* Application only provided ptr for the rctx
+ * we alloc memory for it. And along we alloc memory for the sa in it */
+ ctx->use_rctx = 1;
+ rc = crypto4xx_alloc_sa_rctx(ctx, rctx);
+ if (rc)
+ goto err_nomem;
+ sa = (struct dynamic_sa_ctl *)(rctx->sa_out);
+ offset = get_dynamic_sa_offset_iv_field(rctx);
+ /* copy req->iv to state_record->iv */
+ if (req->info)
+ crypto4xx_memcpy_le(rctx->state_record, req->info,
+ get_dynamic_sa_iv_size(rctx));
+ else
+ memset(rctx->state_record, 0, get_dynamic_sa_iv_size(rctx));
+ sa->sa_command_0.bf.dir = CRYPTO_OUTBOUND;
+ rctx->hash_final = 0;
+ rctx->is_hash = 0;
+ rctx->pd_ctl = 0x1;
+ rctx->direction = CRYPTO_OUTBOUND;
+ return crypto4xx_setup_crypto(&req->base);
+
+err_nomem:
+ return -ENOMEM;
+}
+
+static inline int crypto4xx_decrypt(struct ablkcipher_request *req)
+{
+ struct crypto4xx_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+ struct crypto4xx_ctx *rctx = ablkcipher_request_ctx(req);
+ struct dynamic_sa_ctl *sa = NULL;
+ int rc;
+ u32 offset;
+
+ /* Application only provided ptr for the rctx
+ * we alloc memory for it. And along we alloc memory for the sa in it*/
+ ctx->use_rctx = 1;
+ rc = crypto4xx_alloc_sa_rctx(ctx, rctx);
+ if (rc != 0)
+ goto err_nomem;
+ sa = (struct dynamic_sa_ctl *)(rctx->sa_in);
+ offset = get_dynamic_sa_offset_iv_field(rctx);
+ /* copy req->iv to state_record->iv */
+ if (req->info)
+ crypto4xx_memcpy_le(rctx->state_record, req->info,
+ get_dynamic_sa_iv_size(rctx));
+ else
+ memset(rctx->state_record, 0, get_dynamic_sa_iv_size(rctx));
+ sa->sa_command_0.bf.dir = CRYPTO_INBOUND;
+ rctx->hash_final = 0;
+ rctx->is_hash = 0;
+ rctx->pd_ctl = 1;
+ rctx->direction = CRYPTO_INBOUND;
+
+ return crypto4xx_setup_crypto(&req->base);
+
+err_nomem:
+ return -ENOMEM;
+}
+
+/**
+ * AES Functions
+ *
+ */
+static int crypto4xx_setkey_aes(struct crypto_ablkcipher *cipher,
+ const u8 *key,
+ unsigned int keylen,
+ unsigned char cm,
+ u8 fb)
+{
+ struct crypto_tfm *tfm = crypto_ablkcipher_tfm(cipher);
+ struct crypto4xx_ctx *ctx = crypto_tfm_ctx(tfm);
+ struct dynamic_sa_ctl *sa;
+ int rc;
+
+ if ((keylen != 256/8) && (keylen != 128/8) && (keylen != 192/8)) {
+ crypto_ablkcipher_set_flags(cipher,
+ CRYPTO_TFM_RES_BAD_KEY_LEN);
+ return -1;
+ }
+
+ /* Create SA */
+ if (ctx->sa_in_dma_addr || ctx->sa_out_dma_addr)
+ crypto4xx_free_sa(ctx);
+
+ if (keylen == 256/8)
+ crypto4xx_alloc_sa(ctx, SA_AES256_LEN);
+ else if (keylen == 192/8)
+ crypto4xx_alloc_sa(ctx, SA_AES192_LEN);
+ else
+ crypto4xx_alloc_sa(ctx, SA_AES128_LEN);
+
+ if (!ctx->sa_in_dma_addr || !ctx->sa_out_dma_addr)
+ goto err_nomem;
+ if (ctx->state_record_dma_addr == 0) {
+ rc = crypto4xx_alloc_state_record(ctx);
+ if (rc != 0)
+ goto err_nomem_sr;
+ }
+ /* Setup SA */
+ sa = (struct dynamic_sa_ctl *)(ctx->sa_in);
+ ctx->hash_final = 0;
+ sa->sa_command_0.bf.hash_alg = SA_HASH_ALG_NULL;
+ sa->sa_command_0.bf.cipher_alg = SA_CIPHER_ALG_AES;
+ sa->sa_command_0.bf.opcode = SA_OPCODE_ENCRYPT;
+ sa->sa_command_0.bf.load_iv = 2;
+
+ sa->sa_command_1.bf.sa_rev = 1;
+ sa->sa_command_1.bf.copy_payload = 1;
+ sa->sa_command_1.bf.crypto_mode31 = (cm & 4) >> 2;
+ sa->sa_command_1.bf.crypto_mode9_8 = (cm & 3);
+ sa->sa_command_1.bf.feedback_mode = fb;
+ sa->sa_command_1.bf.mutable_bit_proc = 1;
+
+ if (keylen >= 256/8) {
+ crypto4xx_memcpy_le(((struct dynamic_sa_aes256 *)sa)->key,
+ key, keylen);
+ sa->sa_contents = SA_AES256_CONTENTS;
+ sa->sa_command_1.bf.key_len = SA_AES_KEY_LEN_256;
+ } else if (keylen >= 192/8) {
+ crypto4xx_memcpy_le(((struct dynamic_sa_aes192 *)sa)->key,
+ key, keylen);
+ sa->sa_contents = SA_AES192_CONTENTS;
+ sa->sa_command_1.bf.key_len = SA_AES_KEY_LEN_192;
+ } else {
+ crypto4xx_memcpy_le(((struct dynamic_sa_aes128 *)sa)->key,
+ key, keylen);
+ sa->sa_contents = SA_AES128_CONTENTS;
+ sa->sa_command_1.bf.key_len = SA_AES_KEY_LEN_128;
+ }
+ memcpy(ctx->sa_in + get_dynamic_sa_offset_state_ptr_field(ctx),
+ (void *)&(ctx->state_record_dma_addr), 4);
+ memcpy(ctx->sa_out, ctx->sa_in, ctx->sa_len*4);
+ sa = (struct dynamic_sa_ctl *)(ctx->sa_out);
+ sa->sa_command_0.bf.dir = CRYPTO_OUTBOUND;
+
+ return 0;
+
+err_nomem_sr:
+ crypto4xx_free_sa(ctx);
+
+err_nomem:
+ return -ENOMEM;
+
+}
+
+static inline int crypto4xx_setkey_aes_cbc(struct crypto_ablkcipher *cipher,
+ const u8 *key, unsigned int keylen)
+{
+ return crypto4xx_setkey_aes(cipher, key, keylen,
+ CRYPTO_MODE_CBC,
+ CRYPTO_FEEDBACK_MODE_NO_FB);
+}
+
+/**
+ * HASH SHA1 Functions
+ *
+ */
+static int crypto4xx_hash_alg_init(struct crypto_tfm *tfm,
+ unsigned int sa_len,
+ unsigned char ha,
+ unsigned char hm)
+{
+ struct crypto_alg *alg = tfm->__crt_alg;
+ struct crypto4xx_alg *my_alg = crypto_alg_to_crypto4xx_alg(alg);
+ struct crypto4xx_ctx *ctx = crypto_tfm_ctx(tfm);
+ struct dynamic_sa_ctl *sa;
+
+ ctx->dev = my_alg->dev;
+ ctx->is_hash = 1;
+ ctx->hash_final = 0;
+
+ /* Create SA */
+ if (ctx->sa_in_dma_addr || ctx->sa_out_dma_addr)
+ crypto4xx_free_sa(ctx);
+
+ crypto4xx_alloc_sa(ctx, sa_len);
+ if (!ctx->sa_in_dma_addr || !ctx->sa_out_dma_addr)
+ goto err_nomem;
+
+ if (ctx->state_record_dma_addr == 0) {
+ crypto4xx_alloc_state_record(ctx);
+ if (!ctx->state_record_dma_addr)
+ goto err_nomem_sr;
+ } else {
+ CRYPTUTL_PRINTK(KERN_INFO, "crypto4xx_hash_alg_init: "
+ "State Record Already Exists \n");
+ }
+ tfm->crt_ahash.reqsize = sizeof(struct crypto4xx_ctx);
+ sa = (struct dynamic_sa_ctl *)(ctx->sa_in);
+
+ /* Setup hash algorithm and hash mode */
+ sa->sa_command_0.w = 0;
+ sa->sa_command_0.bf.hash_alg = ha;
+ sa->sa_command_0.bf.gather = 0;
+ sa->sa_command_0.bf.save_hash_state = 1;
+ sa->sa_command_0.bf.cipher_alg = SA_CIPHER_ALG_NULL;
+ sa->sa_command_0.bf.opcode = SA_OPCODE_HASH;
+
+ /* load hash state set to no load, since we don't no init idigest */
+ sa->sa_command_0.bf.load_hash_state = 3;
+ sa->sa_command_0.bf.dir = 0;
+ sa->sa_command_0.bf.opcode = SA_OPCODE_HASH;
+ sa->sa_command_1.w = 0;
+ sa->sa_command_1.bf.hmac_muting = 0;
+ /* dynamic sa, need to set it to rev 2 */
+ sa->sa_command_1.bf.sa_rev = 1;
+ sa->sa_command_1.bf.copy_payload = 0;
+ sa->sa_command_1.bf.mutable_bit_proc = 1;
+
+ /* Need to zero hash digest in SA */
+ if (ha == SA_HASH_ALG_SHA1) {
+ sa->sa_contents = SA_HASH160_CONTENTS;
+ memset(((struct dynamic_sa_hash160 *)
+ (ctx->sa_in))->inner_digest, 0, 20);
+ memset(((struct dynamic_sa_hash160 *)
+ (ctx->sa_in))->outer_digest, 0, 20);
+ ((struct dynamic_sa_hash160 *)(ctx->sa_in))->state_ptr
+ = ctx->state_record_dma_addr;
+ } else {
+ CRYPTUTL_PRINTK(KERN_ERR, "ERROR: invalid hash"
+ " algorithm used \n");
+ }
+
+ return 0;
+
+err_nomem_sr:
+ crypto4xx_free_sa(ctx);
+err_nomem:
+ return -ENOMEM;
+
+}
+
+static int crypto4xx_hash_init(struct ahash_request *req)
+{
+ struct crypto4xx_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+ int ds;
+ struct dynamic_sa_ctl *sa;
+
+ CRYPTUTL_PRINTK(KERN_INFO, "crypto4xx_hash_init called ctx=0x%08x,"
+ "ctx->sa = 0x%08x\n",
+ (u32)ctx, (u32)ctx->sa_in);
+ ctx->use_rctx = 0;
+ sa = (struct dynamic_sa_ctl *)(ctx->sa_in);
+ ds = crypto_ahash_digestsize(
+ __crypto_ahash_cast(req->base.tfm));
+ sa->sa_command_0.bf.digest_len = ds>>2;
+ sa->sa_command_0.bf.load_hash_state = SA_LOAD_HASH_FROM_SA;
+ ctx->is_hash = 1;
+ ctx->direction = CRYPTO_INBOUND;
+
+ return 0;
+}
+
+static int crypto4xx_hash_update(struct ahash_request *req)
+{
+ struct crypto4xx_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+
+ ctx->is_hash = 1;
+ ctx->hash_final = 0;
+ ctx->use_rctx = 0;
+ ctx->pd_ctl = 0x11;
+ ctx->direction = CRYPTO_INBOUND;
+ return crypto4xx_setup_crypto(&req->base);
+}
+
+static int crypto4xx_hash_final(struct ahash_request *req)
+{
+ struct crypto4xx_ctx *rctx = ahash_request_ctx(req);
+
+ crypto4xx_free_sa_rctx(rctx);
+ return 0;
+}
+
+static int crypto4xx_hash_digest(struct ahash_request *req)
+{
+ struct crypto4xx_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+
+ ctx->use_rctx = 0;
+ ctx->hash_final = 1;
+ ctx->pd_ctl = 0x11;
+ ctx->direction = CRYPTO_INBOUND;
+ return crypto4xx_setup_crypto(&req->base);
+}
+
+/**
+ * SHA1 and SHA2 Algorithm
+ */
+static int crypto4xx_sha1_alg_init(struct crypto_tfm *tfm)
+{
+ return crypto4xx_hash_alg_init(tfm,
+ SA_HASH160_LEN,
+ SA_HASH_ALG_SHA1,
+ SA_HASH_MODE_HASH);
+}
+
+/**
+ * Support Crypto Algorithms
+ */
+struct crypto_alg crypto4xx_basic_alg[] = {
+
+ /* Crypto AES modes */
+ {.cra_name = "cbc(aes)",
+ .cra_driver_name = "cbc-aes",
+ .cra_priority = CRYPTO4XX_CRYPTO_PRIORITY,
+ .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
+ .cra_blocksize = 16, /* 128-bits block */
+ .cra_ctxsize = sizeof(struct crypto4xx_ctx),
+ .cra_alignmask = 0,
+ .cra_type = &crypto_ablkcipher_type,
+ .cra_module = THIS_MODULE,
+ .cra_u = {.ablkcipher = {
+ .min_keysize = 16, /* AES min key size is 128-bits */
+ .max_keysize = 32, /* AES max key size is 256-bits */
+ .ivsize = 16, /* IV size is 16 bytes */
+ .setkey = crypto4xx_setkey_aes_cbc,
+ .encrypt = crypto4xx_encrypt,
+ .decrypt = crypto4xx_decrypt,
+ } }
+ },
+ /* Hash SHA1, SHA2 */
+ {.cra_name = "sha1",
+ .cra_driver_name = "sha1",
+ .cra_priority = CRYPTO4XX_CRYPTO_PRIORITY,
+ .cra_flags = CRYPTO_ALG_TYPE_AHASH | CRYPTO_ALG_ASYNC,
+ .cra_blocksize = 64, /* SHA1 block size is 512-bits */
+ .cra_ctxsize = sizeof(struct crypto4xx_ctx),
+ .cra_alignmask = 0,
+ .cra_type = &crypto_ahash_type,
+ .cra_init = crypto4xx_sha1_alg_init,
+ .cra_module = THIS_MODULE,
+ .cra_u = {.ahash = {
+ .digestsize = 20, /* Disgest is 160-bits */
+ .init = crypto4xx_hash_init,
+ .update = crypto4xx_hash_update,
+ .final = crypto4xx_hash_final,
+ .digest = crypto4xx_hash_digest,
+ } }
+ },
+ /* Terminator */
+ {.cra_name = "" }
+};
+
+
diff --git a/drivers/crypto/amcc/crypto4xx_core.c b/drivers/crypto/amcc/crypto4xx_core.c
new file mode 100644
index 0000000..216c5db
--- /dev/null
+++ b/drivers/crypto/amcc/crypto4xx_core.c
@@ -0,0 +1,1228 @@
+/****************************************************************************
+ * AMCC SoC Crypto4XX Driver
+ *
+ * Copyright (c) 2008 Applied Micro Circuits Corporation.
+ * All rights reserved. James Hsiao <[email protected]>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * @file crypto4xx_core.c
+ *
+ * This file implements AMCC crypto offload Linux device driver for use with
+ * Linux CryptoAPI.
+ *
+ ****************************************************************************
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/mod_devicetable.h>
+#include <linux/interrupt.h>
+#include <linux/completion.h>
+#include <linux/spinlock_types.h>
+#include <linux/highmem.h>
+#include <linux/random.h>
+#include <linux/scatterlist.h>
+#include <linux/crypto.h>
+#include <crypto/algapi.h>
+#include <crypto/des.h>
+#include <crypto/hash.h>
+#include <crypto/internal/hash.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/proc_fs.h>
+#include <linux/timer.h>
+#include <linux/of_platform.h>
+#include <asm/dcr.h>
+#include <asm/dcr-regs.h>
+#include <asm/cacheflush.h>
+
+#include "crypto4xx_reg_def.h"
+#include "crypto4xx_core.h"
+#include "crypto4xx_sa.h"
+
+#define CRYPTO4XX_CRYPTO_PRIORITY 300
+#define PPC4XX_SEC_VERSION_STR "0.1"
+
+struct crypto4xx_core_device lsec_core;
+
+u32 crypto4xx_write32(u32 reg, u32 val)
+{
+ writel(val, lsec_core.ce_base + reg);
+ return 0;
+}
+
+u32 crypto4xx_read32(u32 reg, u32 *val)
+{
+ *val = readl(lsec_core.ce_base + reg);
+ return 0;
+}
+
+/**
+ * PPC4XX Crypto Engine Initialization Routine
+ */
+int32_t crypto4xx_init(struct crypto4xx_device *dev)
+{
+ u32 rc = 0;
+ union ce_ring_size ring_size;
+ union ce_ring_contol ring_ctrl;
+ union ce_part_ring_size part_ring_size;
+ union ce_io_threshold io_threshold;
+ u32 rand_num;
+
+ union ce_pe_dma_cfg pe_dma_cfg;
+
+ mtdcri(SDR0, SDR0_SRST0, mfdcri(SDR0, SDR0_SRST0) | SRST0_CRYP);
+ mtdcri(SDR0, SDR0_SRST0, mfdcri(SDR0, SDR0_SRST0) & ~SRST0_CRYP);
+
+ crypto4xx_write32(CRYPTO_ENGINE_BYTE_ORDER_CFG, 0x22222);
+
+ /* setup pe dma, include reset sg, pdr and pe, then release reset */
+ pe_dma_cfg.w = 0;
+
+ pe_dma_cfg.bf.bo_sgpd_en = 1;
+ pe_dma_cfg.bf.bo_data_en = 0;
+ pe_dma_cfg.bf.bo_sa_en = 1;
+ pe_dma_cfg.bf.bo_pd_en = 1;
+
+ pe_dma_cfg.bf.dynamic_sa_en = 1;
+ pe_dma_cfg.bf.reset_sg = 1;
+ pe_dma_cfg.bf.reset_pdr = 1;
+ pe_dma_cfg.bf.reset_pe = 1;
+
+ crypto4xx_write32(CRYPTO_ENGINE_PE_DMA_CFG, pe_dma_cfg.w);
+
+ /* un reset pe,sg and pdr */
+ pe_dma_cfg.bf.pe_mode = 0;
+ pe_dma_cfg.bf.reset_sg = 0;
+ pe_dma_cfg.bf.reset_pdr = 0;
+ pe_dma_cfg.bf.reset_pe = 0;
+ pe_dma_cfg.bf.bo_td_en = 0;
+
+ crypto4xx_write32(CRYPTO_ENGINE_PE_DMA_CFG, pe_dma_cfg.w);
+
+ crypto4xx_write32(CRYPTO_ENGINE_PDR_BASE, dev->pdr_pa);
+ crypto4xx_write32(CRYPTO_ENGINE_RDR_BASE, dev->pdr_pa);
+
+ crypto4xx_write32(CRYPTO_ENGINE_PRNG_CTRL, 3);
+ get_random_bytes(&rand_num, sizeof(rand_num));
+ crypto4xx_write32(CRYPTO_ENGINE_PRNG_SEED_L, rand_num);
+ get_random_bytes(&rand_num, sizeof(rand_num));
+ crypto4xx_write32(CRYPTO_ENGINE_PRNG_SEED_L, rand_num);
+
+ ring_size.w = 0;
+ ring_size.bf.ring_offset = PPC4XX_PD_SIZE;
+ ring_size.bf.ring_size = PPC4XX_NUM_PD;
+ crypto4xx_write32(CRYPTO_ENGINE_RING_SIZE, ring_size.w);
+
+ ring_ctrl.w = 0;
+ crypto4xx_write32(CRYPTO_ENGINE_RING_CTRL, ring_ctrl.w);
+ crypto4xx_write32(CRYPTO_ENGINE_DC_CTRL, 1);
+
+ crypto4xx_write32(CRYPTO_ENGINE_GATH_RING_BASE, dev->gdr_pa);
+ crypto4xx_write32(CRYPTO_ENGINE_SCAT_RING_BASE, dev->sdr_pa);
+
+ part_ring_size.w = 0;
+ part_ring_size.bf.sdr_size = PPC4XX_SDR_SIZE;
+ part_ring_size.bf.gdr_size = PPC4XX_GDR_SIZE;
+ crypto4xx_write32(CRYPTO_ENGINE_PART_RING_SIZE, part_ring_size.w);
+
+ crypto4xx_write32(CRYPTO_ENGINE_PART_RING_CFG,
+ 0x0000ffff & PPC4XX_SD_BUFFER_SIZE);
+ io_threshold.w = 0;
+ io_threshold.bf.output_threshold = PPC4XX_OUTPUT_THRESHOLD;
+ io_threshold.bf.input_threshold = PPC4XX_INPUT_THRESHOLD;
+ crypto4xx_write32(CRYPTO_ENGINE_IO_THRESHOLD, io_threshold.w);
+
+ crypto4xx_write32(CRYPTO_ENGINE_PDR_BASE_UADDR, 0x0);
+ crypto4xx_write32(CRYPTO_ENGINE_RDR_BASE_UADDR, 0x0);
+ crypto4xx_write32(CRYPTO_ENGINE_PKT_SRC_UADDR, 0x0);
+ crypto4xx_write32(CRYPTO_ENGINE_PKT_DEST_UADDR, 0x0);
+ crypto4xx_write32(CRYPTO_ENGINE_SA_UADDR, 0x0);
+ crypto4xx_write32(CRYPTO_ENGINE_GATH_RING_BASE_UADDR, 0x0);
+ crypto4xx_write32(CRYPTO_ENGINE_SCAT_RING_BASE_UADDR, 0x0);
+
+ /* un reset pe,sg and pdr */
+ pe_dma_cfg.bf.pe_mode = 1;
+ pe_dma_cfg.bf.reset_sg = 0;
+ pe_dma_cfg.bf.reset_pdr = 0;
+ pe_dma_cfg.bf.reset_pe = 0;
+ pe_dma_cfg.bf.bo_td_en = 0;
+
+ crypto4xx_write32(CRYPTO_ENGINE_PE_DMA_CFG, pe_dma_cfg.w);
+ /*clear all pending interrupt*/
+ crypto4xx_write32(CRYPTO_ENGINE_INT_CLR, 0x3ffff);
+ crypto4xx_write32(CRYPTO_ENGINE_INT_DESCR_CNT, PPC4XX_INT_DESCR_CNT);
+
+ crypto4xx_write32(CRYPTO_ENGINE_INT_TIMEOUT_CNT,
+ PPC4XX_INT_TIMEOUT_CNT);
+ crypto4xx_write32(CRYPTO_ENGINE_INT_CFG, PPC4XX_INT_CFG);
+#ifdef CONFIG_USING_PD_DONE_INT
+ crypto4xx_write32(CRYPTO_ENGINE_INT_EN, CRYPTO_CTX_DONE_INT);
+#else
+ crypto4xx_write32(CRYPTO_ENGINE_INT_EN, CRYPTO_PD_DONE_INT);
+#endif
+
+ return rc;
+}
+
+void crypto4xx_alloc_sa(struct crypto4xx_ctx *ctx, u32 size)
+{
+ ctx->sa_in = pci_alloc_consistent(NULL, size * 4,
+ &ctx->sa_in_dma_addr);
+ ctx->sa_out = pci_alloc_consistent(NULL, size * 4,
+ &ctx->sa_out_dma_addr);
+ ctx->sa_len = size;
+}
+
+void crypto4xx_free_sa(struct crypto4xx_ctx *ctx)
+{
+ if (ctx->sa_in != NULL) {
+ pci_free_consistent(NULL, ctx->sa_len*4,
+ ctx->sa_in, ctx->sa_in_dma_addr);
+ }
+
+ if (ctx->sa_out != NULL) {
+ pci_free_consistent(NULL, ctx->sa_len*4,
+ ctx->sa_out, ctx->sa_out_dma_addr);
+ }
+ ctx->sa_in_dma_addr = 0;
+ ctx->sa_out_dma_addr = 0;
+ ctx->sa_len = 0;
+}
+
+u32 crypto4xx_alloc_state_record(struct crypto4xx_ctx *ctx)
+{
+ ctx->state_record =
+ pci_alloc_consistent(NULL,
+ sizeof(struct dynamic_sa_state_record),
+ &ctx->state_record_dma_addr);
+ if (!ctx->state_record_dma_addr)
+ return -ENOMEM;
+ memset(ctx->state_record, 0, sizeof(struct dynamic_sa_state_record));
+ return 0;
+
+}
+
+void crypto4xx_free_state_record(struct crypto4xx_ctx *ctx)
+{
+ if (ctx->state_record != NULL)
+ pci_free_consistent(NULL,
+ sizeof(struct dynamic_sa_state_record),
+ ctx->state_record,
+ ctx->state_record_dma_addr);
+ ctx->state_record_dma_addr = 0;
+}
+
+/**
+ * alloc memory for the gather ring
+ * no need to alloc buf for the ring
+ * gdr_tail, gdr_head and gdr_count are initialized by this function
+ */
+u32 crypto4xx_build_pdr(struct crypto4xx_device *dev)
+{
+ int i;
+ dev->pdr = pci_alloc_consistent(NULL,
+ sizeof(struct ce_pd) * PPC4XX_NUM_PD,
+ &dev->pdr_pa);
+ if (!dev->pdr)
+ return -ENOMEM;
+ dev->pdr_uinfo = kzalloc(sizeof(struct pd_uinfo) * PPC4XX_NUM_PD,
+ GFP_KERNEL);
+ if (!dev->pdr_uinfo)
+ return -ENOMEM;
+
+ memset(dev->pdr, 0, sizeof(struct ce_pd) * PPC4XX_NUM_PD);
+
+ for (i = 0; i < PPC4XX_NUM_PD; i++) {
+ dev->pd_uinfo_array[i] = (struct pd_uinfo *)(dev->pdr_uinfo +
+ sizeof(struct pd_uinfo) * i);
+ dev->pdp_array[i] = (struct ce_pd *)(dev->pdr +
+ sizeof(struct ce_pd) * i);
+ dev->pd_uinfo_array[i]->state = 0;
+ }
+
+ return 0;
+}
+
+void crypto4xx_destroy_pdr(struct crypto4xx_device *dev)
+{
+ int i;
+ if (dev->pdr != NULL)
+ pci_free_consistent(NULL,
+ sizeof(struct ce_pd) * PPC4XX_NUM_PD,
+ dev->pdr,
+ dev->pdr_pa);
+ if (dev->pdr_uinfo != NULL)
+ kfree(dev->pdr_uinfo);
+ for (i = 0; i < PPC4XX_NUM_PD; i++)
+ dev->pd_uinfo_array[i] = NULL;
+}
+
+u32 crypto4xx_get_pd_from_pdr(struct crypto4xx_device *dev)
+{
+ u32 retval;
+ u32 tmp;
+
+ retval = dev->pdr_head;
+ tmp = (dev->pdr_head + 1) % PPC4XX_NUM_PD;
+
+ if (tmp == dev->pdr_tail)
+ return ERING_WAS_FULL;
+ dev->pdr_head = tmp;
+
+ return retval;
+}
+
+
+u32 crypto4xx_put_pd_to_pdr(struct crypto4xx_device *dev, u32 idx)
+{
+ struct pd_uinfo *pd_uinfo;
+
+ pd_uinfo = dev->pd_uinfo_array[idx];
+ pd_uinfo->state = PD_ENTRY_FREE;
+
+ if (dev->pdr_tail != PPC4XX_LAST_PD)
+ dev->pdr_tail++;
+ else
+ dev->pdr_tail = 0;
+
+ return 0;
+}
+
+struct ce_pd *crypto4xx_get_pdp(struct crypto4xx_device *dev,
+ dma_addr_t *pd_dma, u32 idx)
+{
+ *pd_dma = dev->pdr_pa + sizeof(struct ce_pd) * idx;
+ return dev->pdp_array[idx];
+}
+
+/**
+ * alloc memory for the gather ring
+ * no need to alloc buf for the ring
+ * gdr_tail, gdr_head and gdr_count are initialized by this function
+ */
+u32 crypto4xx_build_gdr(struct crypto4xx_device *dev)
+{
+ dev->gdr = pci_alloc_consistent(NULL,
+ sizeof(struct ce_gd) *
+ PPC4XX_NUM_GD,
+ &dev->gdr_pa);
+ if (!dev->gdr)
+ return -ENOMEM;
+ memset(dev->gdr, 0, sizeof(struct ce_gd) * PPC4XX_NUM_GD);
+ return 0;
+}
+
+void crypto4xx_destroy_gdr(struct crypto4xx_device *dev)
+{
+ pci_free_consistent(NULL,
+ sizeof(struct ce_gd) * PPC4XX_NUM_GD,
+ dev->gdr,
+ dev->gdr_pa);
+}
+
+u32 crypto4xx_get_gd_from_gdr(struct crypto4xx_device *dev)
+{
+ u32 retval;
+ u32 tmp;
+
+ retval = dev->gdr_head;
+ tmp = (dev->gdr_head+1) % PPC4XX_NUM_GD;
+
+ if (tmp == dev->gdr_tail)
+ return ERING_WAS_FULL;
+ dev->gdr_head = tmp;
+ return retval;
+}
+
+u32 crypto4xx_put_gd_to_gdr(struct crypto4xx_device *dev)
+{
+ if (dev->gdr_tail == dev->gdr_head)
+ return 0;
+
+ if (dev->gdr_tail != PPC4XX_LAST_GD)
+ dev->gdr_tail++;
+ else
+ dev->gdr_tail = 0;
+
+ return 0;
+}
+
+struct ce_gd *crypto4xx_get_gdp(struct crypto4xx_device *dev,
+ dma_addr_t *gd_dma, u32 idx)
+{
+ *gd_dma = dev->gdr_pa + sizeof(struct ce_gd)*idx;
+ return (struct ce_gd *) (dev->gdr + sizeof(struct ce_gd) * idx);
+}
+
+/**
+ * alloc memory for the scatter ring
+ * need to alloc buf for the ring
+ * sdr_tail, sdr_head and sdr_count are initialized by this function
+ */
+u32 crypto4xx_build_sdr(struct crypto4xx_device *dev)
+{
+ int i;
+ /* alloc memory for scatter descriptor ring */
+ dev->sdr = pci_alloc_consistent(NULL,
+ sizeof(struct ce_sd) * PPC4XX_NUM_SD,
+ &dev->sdr_pa);
+ if (!dev->sdr)
+ return -ENOMEM;
+ memset(dev->sdr, 0, sizeof(struct ce_sd) * PPC4XX_NUM_SD);
+ for (i = 0; i < PPC4XX_NUM_SD; i++) {
+ dev->sdp_array[i] = (struct ce_sd *)(dev->sdr +
+ sizeof(struct ce_sd) * i);
+ }
+
+ dev->scatter_buffer_size = PPC4XX_SD_BUFFER_SIZE;
+ dev->scatter_buffer_va =
+ pci_alloc_consistent(NULL,
+ dev->scatter_buffer_size * PPC4XX_NUM_SD,
+ &dev->scatter_buffer_pa);
+ if (!dev->scatter_buffer_va)
+ return -ENOMEM;
+
+ return 0;
+}
+
+void crypto4xx_destroy_sdr(struct crypto4xx_device *dev)
+{
+ pci_free_consistent(NULL,
+ sizeof(struct ce_sd) * PPC4XX_NUM_SD,
+ dev->sdr,
+ dev->sdr_pa);
+
+ pci_free_consistent(NULL,
+ dev->scatter_buffer_size * PPC4XX_NUM_SD,
+ dev->scatter_buffer_va,
+ dev->scatter_buffer_pa);
+}
+
+u32 crypto4xx_get_sd_from_sdr(struct crypto4xx_device *dev)
+{
+ u32 retval;
+ u32 tmp;
+
+ retval = dev->sdr_head;
+ tmp = (dev->sdr_head+1) % PPC4XX_NUM_SD;
+
+ if (tmp == dev->sdr_tail)
+ return ERING_WAS_FULL;
+
+ dev->sdr_head = tmp;
+ return retval;
+}
+
+u32 crypto4xx_put_sd_to_sdr(struct crypto4xx_device *dev)
+{
+ if (dev->sdr_tail == dev->sdr_head)
+ return 0;
+
+ if (dev->sdr_tail != PPC4XX_LAST_SD)
+ dev->sdr_tail++;
+ else
+ dev->sdr_tail = 0;
+
+ return 0;
+}
+
+struct ce_sd *crypto4xx_get_sdp(struct crypto4xx_device *dev,
+ dma_addr_t *sd_dma, u32 idx)
+{
+ *sd_dma = dev->sdr_pa + sizeof(struct ce_sd) * idx;
+ return (struct ce_sd *)(dev->sdr + sizeof(struct ce_sd) * idx);
+}
+
+void ppc4xx_fill_one_page(dma_addr_t addr, u32 length,
+ u32 idx, u32 *next_sd,
+ u32 *offset, u32 *nbytes)
+{
+ struct crypto4xx_device *dev = &(lsec_core.dev);
+ u32 len;
+
+ if (length > dev->scatter_buffer_size) {
+ memcpy(phys_to_virt(addr),
+ dev->scatter_buffer_va +
+ idx*dev->scatter_buffer_size + (*offset),
+ dev->scatter_buffer_size);
+ *offset = 0;
+ length -= dev->scatter_buffer_size;
+ *nbytes -= dev->scatter_buffer_size;
+ if (idx == PPC4XX_LAST_SD)
+ idx = 0;
+ else
+ idx++;
+ *next_sd = idx;
+ ppc4xx_fill_one_page(addr + dev->scatter_buffer_size, length,
+ idx, next_sd, offset, nbytes);
+ return;
+ } else if (length < dev->scatter_buffer_size) {
+ memcpy(phys_to_virt(addr),
+ dev->scatter_buffer_va +
+ idx*dev->scatter_buffer_size + (*offset),
+ length);
+ if ((*offset + length) == dev->scatter_buffer_size) {
+ if (idx == PPC4XX_LAST_SD)
+ idx = 0;
+ else
+ idx++;
+ *next_sd = idx;
+ *nbytes -= length;
+ *offset = 0;
+ } else {
+ *next_sd = idx;
+ *nbytes -= length;
+ *offset += length;
+ }
+ return ;
+ } else {
+
+ len = (*nbytes <= dev->scatter_buffer_size) ?
+ *nbytes : dev->scatter_buffer_size;
+ memcpy(phys_to_virt(addr),
+ dev->scatter_buffer_va +
+ idx*dev->scatter_buffer_size + (*offset),
+ len);
+ offset = 0;
+ *nbytes -= len;
+ if (idx == PPC4XX_LAST_SD)
+ *next_sd = 0;
+ else
+ *next_sd = idx+1;
+ return;
+ }
+}
+
+void crypto4xx_copy_pkt_to_dst(struct ce_pd *pd,
+ struct pd_uinfo *pd_uinfo,
+ u32 nbytes,
+ struct scatterlist *dst,
+ u8 type)
+{
+ struct crypto4xx_device *dev = &(lsec_core.dev);
+ struct scatterlist *sg;
+ dma_addr_t addr;
+ u32 this_sd;
+ u32 offset;
+ u32 next_sd;
+ u32 len;
+ u32 i;
+ u32 sg_len;
+
+ this_sd = pd_uinfo->first_sd;
+ next_sd = this_sd;
+ offset = 0;
+ i = 0;
+ while (nbytes) {
+ sg = &dst[i];
+ sg_len = sg->length;
+ /*1: dst->length > sd_buffer_size: so while loop
+ until length done */
+ addr = dma_map_page(NULL, sg_page(sg),
+ sg->offset, sg->length,
+ DMA_TO_DEVICE);
+ if (offset == 0) {
+ len = (nbytes <= sg->length) ? nbytes : sg->length;
+ ppc4xx_fill_one_page(addr, len, this_sd, &next_sd,
+ &offset, &nbytes);
+ if (!nbytes)
+ return;
+ i++;
+ } else {
+ len = (nbytes <=
+ (dev->scatter_buffer_size - offset)) ? nbytes
+ : (dev->scatter_buffer_size
+ - offset);
+ ppc4xx_fill_one_page(addr, len, this_sd, &next_sd,
+ &offset, &nbytes);
+ this_sd = next_sd;
+ /* addr is begin of dest, we just fill len bytes,
+ from left ofer sd*/
+ if (!nbytes)
+ return;
+ sg_len -= len;
+ if (sg_len) {
+ ppc4xx_fill_one_page(addr + len,
+ sg_len, this_sd,
+ &next_sd, &offset, &nbytes);
+ i++;
+ }
+ }
+ this_sd = next_sd;
+ }
+}
+
+u32 crypto4xx_copy_digest_to_dst(struct pd_uinfo *pd_uinfo,
+ struct crypto4xx_ctx *ctx)
+{
+ struct dynamic_sa_ctl *sa = (struct dynamic_sa_ctl *)(ctx->sa_in);
+ struct dynamic_sa_state_record *state_record =
+ (struct dynamic_sa_state_record *)(ctx->state_record);
+
+ if (sa->sa_command_0.bf.hash_alg == SA_HASH_ALG_SHA1) {
+ memcpy((void *)pd_uinfo->dest_va, state_record->save_digest,
+ SA_HASH_ALG_SHA1_DIGEST_SIZE);
+ }
+ return 0;
+}
+
+
+void crypto4xx_ret_sg_desc(struct crypto4xx_device *dev,
+ struct pd_uinfo *pd_uinfo)
+{
+ int i;
+ struct ce_sd *sd = NULL;
+
+ if (pd_uinfo->first_gd != 0xffffffff) {
+ if (pd_uinfo->first_gd <= pd_uinfo->last_gd) {
+ for (i = pd_uinfo->first_gd;
+ i <= pd_uinfo->last_gd; i++)
+ crypto4xx_put_gd_to_gdr(dev);
+
+ } else {
+ for (i = pd_uinfo->first_gd;
+ i < PPC4XX_NUM_GD; i++)
+ crypto4xx_put_gd_to_gdr(dev);
+ for (i = 0; i <= pd_uinfo->last_gd; i++)
+ crypto4xx_put_gd_to_gdr(dev);
+ }
+ }
+
+ if (pd_uinfo->first_sd != 0xffffffff) {
+ if (pd_uinfo->first_sd <= pd_uinfo->last_sd) {
+ for (i = pd_uinfo->first_sd;
+ i <= pd_uinfo->last_sd; i++) {
+ sd = (struct ce_sd *)(dev->sdr +
+ sizeof(struct ce_sd)*i);
+ sd->ctl.done = 0;
+ sd->ctl.rdy = 0;
+ crypto4xx_put_sd_to_sdr(dev);
+ }
+ } else {
+ for (i = pd_uinfo->first_sd; i < PPC4XX_NUM_SD; i++) {
+ sd = (struct ce_sd *)(dev->sdr +
+ sizeof(struct ce_sd)*i);
+ sd->ctl.done = 0;
+ sd->ctl.rdy = 0;
+ crypto4xx_put_sd_to_sdr(dev);
+ }
+ for (i = 0; i <= pd_uinfo->last_sd; i++) {
+ sd = (struct ce_sd *)(dev->sdr +
+ sizeof(struct ce_sd)*i);
+ sd->ctl.done = 0;
+ sd->ctl.rdy = 0;
+ crypto4xx_put_sd_to_sdr(dev);
+ }
+ }
+ }
+
+ pd_uinfo->first_gd = pd_uinfo->last_gd = 0xffffffff;
+ pd_uinfo->first_sd = pd_uinfo->last_sd = 0xffffffff;
+}
+
+
+u32 crypto4xx_ablkcipher_done(struct pd_uinfo *pd_uinfo, struct ce_pd *pd)
+{
+ struct crypto4xx_ctx *ctx;
+ struct crypto4xx_ctx *rctx = NULL;
+ struct ablkcipher_request *ablk_req;
+ struct scatterlist *dst;
+ dma_addr_t addr;
+
+ ablk_req = ablkcipher_request_cast(pd_uinfo->async_req);
+ ctx = crypto_tfm_ctx(ablk_req->base.tfm);
+
+ if (ctx->use_rctx == 1)
+ rctx = ablkcipher_request_ctx(ablk_req);
+
+ if (pd_uinfo->using_sd) {
+ crypto4xx_copy_pkt_to_dst(pd,
+ pd_uinfo,
+ ablk_req->nbytes,
+ ablk_req->dst,
+ CRYPTO_ALG_TYPE_ABLKCIPHER);
+ crypto4xx_ret_sg_desc(&(lsec_core.dev), pd_uinfo);
+ } else {
+ dst = pd_uinfo->dest_va;
+ addr = dma_map_page(NULL, sg_page(dst), dst->offset,
+ dst->length, DMA_FROM_DEVICE);
+ }
+
+ if (rctx != NULL) {
+ if (rctx->sa_in_dma_addr) {
+ pci_free_consistent(NULL,
+ rctx->sa_len * 4,
+ rctx->sa_in,
+ rctx->sa_in_dma_addr);
+ }
+ if (rctx->sa_in_dma_addr) {
+ pci_free_consistent(NULL,
+ rctx->sa_len * 4,
+ rctx->sa_out,
+ rctx->sa_out_dma_addr);
+ }
+ }
+ if (ablk_req->base.complete != NULL)
+ ablk_req->base.complete(&ablk_req->base, 0);
+ return 0;
+}
+
+u32 crypto4xx_ahash_done(struct pd_uinfo *pd_uinfo)
+{
+ struct crypto4xx_ctx *ctx;
+ struct crypto4xx_ctx *rctx = NULL;
+ struct ahash_request *ahash_req;
+
+ ahash_req = ahash_request_cast(pd_uinfo->async_req);
+ ctx = crypto_tfm_ctx(ahash_req->base.tfm);
+
+ crypto4xx_copy_digest_to_dst(pd_uinfo,
+ crypto_tfm_ctx(ahash_req->base.tfm));
+ crypto4xx_ret_sg_desc(&(lsec_core.dev), pd_uinfo);
+
+ if (ctx->use_rctx == 1) {
+ rctx = ahash_request_ctx(ahash_req);
+ if (rctx != NULL) {
+ if (rctx->sa_in_dma_addr) {
+ pci_free_consistent(NULL,
+ rctx->sa_len * 4,
+ rctx->sa_in,
+ rctx->sa_in_dma_addr);
+ }
+ if (rctx->sa_out_dma_addr) {
+ pci_free_consistent(NULL,
+ rctx->sa_len * 4,
+ rctx->sa_out,
+ rctx->sa_out_dma_addr);
+ }
+ }
+ }
+ /* call user provided callback function x */
+ if (ahash_req->base.complete != NULL)
+ ahash_req->base.complete(&ahash_req->base, 0);
+ return 0;
+}
+
+u32 crypto4xx_pd_done(struct crypto4xx_core_device *lsec, u32 idx)
+{
+ struct ce_pd *pd;
+ struct pd_uinfo *pd_uinfo;
+
+ pd = lsec->dev.pdp_array[idx];
+ pd_uinfo = lsec->dev.pd_uinfo_array[idx];
+
+ if (crypto_tfm_alg_type(pd_uinfo->async_req->tfm) ==
+ CRYPTO_ALG_TYPE_ABLKCIPHER)
+ return crypto4xx_ablkcipher_done(pd_uinfo, pd);
+ else
+ return crypto4xx_ahash_done(pd_uinfo);
+ return 0;
+}
+
+u32 crypto4xx_alloc_sa_rctx(struct crypto4xx_ctx *ctx,
+ struct crypto4xx_ctx *rctx)
+{
+ rctx->sa_in = pci_alloc_consistent(NULL, ctx->sa_len*4,
+ &rctx->sa_in_dma_addr);
+ if (!rctx->sa_in)
+ return -ENOMEM;
+
+ rctx->sa_out = pci_alloc_consistent(NULL, ctx->sa_len*4,
+ &rctx->sa_out_dma_addr);
+ if (!rctx->sa_out)
+ return -ENOMEM;
+
+ rctx->sa_len = ctx->sa_len;
+ rctx->state_record = ctx->state_record;
+ rctx->state_record_dma_addr = ctx->state_record_dma_addr;
+ rctx->bypass = ctx->bypass;
+ memcpy(rctx->sa_in, ctx->sa_in, ctx->sa_len * 4);
+ memcpy(rctx->sa_out, ctx->sa_out, ctx->sa_len * 4);
+
+ return 0;
+}
+
+void crypto4xx_free_sa_rctx(struct crypto4xx_ctx *rctx)
+{
+ if (rctx->sa_in != NULL) {
+ pci_free_consistent(NULL,
+ rctx->sa_len * 4,
+ rctx->sa_in,
+ rctx->sa_in_dma_addr);
+ }
+ if (rctx->sa_out != NULL) {
+ pci_free_consistent(NULL,
+ rctx->sa_len * 4,
+ rctx->sa_out,
+ rctx->sa_out_dma_addr);
+ }
+ rctx->sa_len = 0;
+ rctx->state_record = NULL;
+ rctx->state_record_dma_addr = 0;
+}
+
+void crypto4xx_memcpy_le(unsigned int *dst,
+ const unsigned char *buf,
+ int len)
+{
+ /* SA is in big endian */
+ for (; len; buf += 4, len -= 4)
+ *dst++ = cpu_to_le32(*(unsigned int *) buf);
+}
+
+u32 crypto4xx_stop_all(void)
+{
+ crypto4xx_destroy_pdr(&lsec_core.dev);
+ crypto4xx_destroy_sdr(&lsec_core.dev);
+ crypto4xx_destroy_gdr(&lsec_core.dev);
+
+ return 0;
+}
+
+u32 crypto4xx_build_pd(struct crypto4xx_device *dev,
+ struct crypto_async_request *req,
+ u32 pd_entry,
+ struct crypto4xx_ctx *ctx,
+ struct scatterlist *src,
+ struct scatterlist *dst,
+ u16 datalen,
+ u8 type)
+{
+ dma_addr_t addr, pd_dma, sd_dma, gd_dma;
+ struct dynamic_sa_ctl *sa;
+ struct scatterlist *sg;
+ struct ce_pd *pd;
+ struct pd_uinfo *pd_uinfo;
+ unsigned int nbytes = datalen, idx;
+ struct ce_gd *gd = NULL;
+ u32 gd_idx = 0;
+ struct ce_sd *sd = NULL;
+ u32 sd_idx = 0;
+
+ pd = crypto4xx_get_pdp(dev, &pd_dma, pd_entry);
+ pd_uinfo = dev->pd_uinfo_array[pd_entry];
+ pd_uinfo->async_req = req;
+
+ if (ctx->direction == CRYPTO_INBOUND) {
+ pd->sa = ctx->sa_in_dma_addr;
+ sa = (struct dynamic_sa_ctl *)ctx->sa_in;
+ } else {
+ pd->sa = ctx->sa_out_dma_addr;
+ sa = (struct dynamic_sa_ctl *)ctx->sa_out;
+ }
+
+ pd->sa_len = ctx->sa_len;
+
+ /* If first is last then we are single */
+ if (sg_is_last(src)) {
+ pd->src = dma_map_page(NULL, sg_page(src),
+ src->offset, src->length,
+ DMA_TO_DEVICE);
+ /* Disable gather in sa command */
+ sa->sa_command_0.bf.gather = 0;
+ /* Indicate gather array is not used */
+ pd_uinfo->first_gd = pd_uinfo->last_gd = 0xffffffff;
+ } else {
+ src = &src[0];
+ /* get first gd we are going to use */
+ gd_idx = crypto4xx_get_gd_from_gdr(dev);
+ if (gd_idx == ERING_WAS_FULL) {
+ crypto4xx_ret_sg_desc(dev, pd_uinfo);
+ return -EAGAIN;
+ }
+ pd_uinfo->first_gd = gd_idx;
+ gd = crypto4xx_get_gdp(dev, &gd_dma, gd_idx);
+ pd->src = gd_dma;
+ /* Enable gather */
+ sa->sa_command_0.bf.gather = 1;
+ idx = 0;
+
+ /* walk the sg, and setup gather array */
+ /* Seems that CRYPTO_ENGINE DMA is byte align,
+ so we can use ptr directly from sg */
+ while (nbytes != 0) {
+ sg = &src[idx];
+ addr = dma_map_page(NULL, sg_page(sg),
+ sg->offset, sg->length,
+ DMA_TO_DEVICE);
+ gd->ptr = addr;
+ gd->ctl_len.len = sg->length;
+ gd->ctl_len.done = 0;
+ gd->ctl_len.ready = 1;
+ nbytes -= sg->length;
+ if (!nbytes)
+ break;
+ /* Get first gd we are going to use */
+ gd_idx = crypto4xx_get_gd_from_gdr(dev);
+ if (gd_idx == ERING_WAS_FULL) {
+ crypto4xx_ret_sg_desc(dev, pd_uinfo);
+ return -EAGAIN;
+ }
+ gd = crypto4xx_get_gdp(dev, &gd_dma, gd_idx);
+ pd_uinfo->last_gd = gd_idx;
+ idx++;
+ }
+ }
+ if (dev->scatter_buffer_size <= datalen)
+ nbytes = datalen;
+ if (ctx->is_hash || sg_is_last(dst)) {
+ /* we know application give us dst a whole piece of memory */
+ /* no need to use scatter ring */
+ pd_uinfo->using_sd = 0;
+ pd_uinfo->first_sd = pd_uinfo->last_sd = 0xffffffff;
+ pd_uinfo->dest_va = dst;
+ sa->sa_command_0.bf.scatter = 0;
+ if (ctx->is_hash) {
+ pd->dest = virt_to_phys((void *)dst);
+ } else {
+ pd->dest = dma_map_page(NULL, sg_page(dst),
+ dst->offset, dst->length,
+ DMA_TO_DEVICE);
+ }
+
+ } else {
+ sa->sa_command_0.bf.scatter = 1;
+ pd_uinfo->using_sd = 1;
+ pd_uinfo->first_sd = pd_uinfo->last_sd = sd_idx;
+ sd_idx = crypto4xx_get_sd_from_sdr(dev);
+ if (sd_idx == 0xffffffff)
+ return -EAGAIN;
+ sd = crypto4xx_get_sdp(dev, &sd_dma, sd_idx);
+ pd->dest = sd_dma;
+ /* setup scatter descriptor */
+ sd->ctl.done = 0;
+ sd->ctl.rdy = 1;
+ /* sd->ptr should be setup by sd_init routine*/
+ idx = 0;
+ if (nbytes >= PPC4XX_SD_BUFFER_SIZE)
+ nbytes -= PPC4XX_SD_BUFFER_SIZE;
+ else if (nbytes < PPC4XX_SD_BUFFER_SIZE)
+ nbytes = 0;
+ while (nbytes) {
+ sd_idx = crypto4xx_get_sd_from_sdr(dev);
+ if (sd_idx == 0xffffffff) {
+ crypto4xx_ret_sg_desc(dev, pd_uinfo);
+ /*Fixme implement some error code later */
+ return -EAGAIN;
+ }
+ sd = crypto4xx_get_sdp(dev, &sd_dma, sd_idx);
+ pd_uinfo->last_sd = sd_idx;
+ /* setup scatter descriptor */
+ sd->ctl.done = 0;
+ sd->ctl.rdy = 1;
+ if (nbytes >= PPC4XX_SD_BUFFER_SIZE)
+ nbytes -= PPC4XX_SD_BUFFER_SIZE;
+ else
+ /*this SD entry can hold sd_buffer_size,
+ its more the need so done */
+ nbytes = 0;
+ }
+ }
+ pd->pd_ctl.w = ctx->pd_ctl;
+ pd->pd_ctl_len.w = 0x00400000 | (ctx->bypass<<24) | datalen;
+ pd_uinfo->state = PD_ENTRY_INUSE;
+
+ crypto4xx_write32(CRYPTO_ENGINE_INT_DESCR_RD, 1);
+ return -EINPROGRESS;
+
+}
+
+u32 crypto4xx_start_device(struct crypto4xx_device *dev)
+{
+ u32 rc ;
+ rc = crypto4xx_init(dev);
+ return rc;
+}
+
+int crypto4xx_handle_req(struct crypto_async_request *req)
+{
+ struct crypto4xx_ctx *ctx = crypto_tfm_ctx(req->tfm);
+ struct crypto4xx_device *dev = ctx->dev;
+ struct crypto4xx_ctx *rctx;
+ struct pd_uinfo *pd_uinfo;
+
+ int ret = -EAGAIN;
+
+ u32 pd_entry;
+
+ pd_entry = crypto4xx_get_pd_from_pdr(dev); /* index to the entry */
+ if (pd_entry == ERING_WAS_FULL)
+ return -EAGAIN;
+
+ pd_uinfo = dev->pd_uinfo_array[pd_entry];
+
+ if (crypto_tfm_alg_type(req->tfm) == CRYPTO_ALG_TYPE_ABLKCIPHER) {
+ struct ablkcipher_request *ablk_req;
+ ablk_req = ablkcipher_request_cast(req);
+ if (ctx->use_rctx) {
+ rctx = ablkcipher_request_ctx(ablk_req);
+ return crypto4xx_build_pd(dev, req, pd_entry, rctx,
+ ablk_req->src, ablk_req->dst,
+ ablk_req->nbytes, ABLK);
+ } else {
+ return crypto4xx_build_pd(dev, req, pd_entry, ctx,
+ ablk_req->src, ablk_req->dst,
+ ablk_req->nbytes,
+ ABLK);
+ }
+ } else {
+ struct ahash_request *ahash_req;
+ ahash_req = ahash_request_cast(req);
+ if (ctx->use_rctx) {
+ rctx = ahash_request_ctx(ahash_req);
+ return crypto4xx_build_pd(dev, req, pd_entry, rctx,
+ ahash_req->src,
+ (struct scatterlist *)
+ ahash_req->result,
+ ahash_req->nbytes,
+ AHASH);
+ } else {
+ return crypto4xx_build_pd(dev, req, pd_entry, ctx,
+ ahash_req->src,
+ (struct scatterlist *)
+ ahash_req->result,
+ ahash_req->nbytes,
+ AHASH);
+ }
+ }
+ return ret;
+}
+
+int crypto4xx_setup_crypto(struct crypto_async_request *req)
+{
+ return crypto4xx_handle_req(req);
+}
+
+/**
+ * Algorithm Registration Functions
+ *
+ */
+static int crypto4xx_alg_init(struct crypto_tfm *tfm)
+{
+ struct crypto_alg *alg = tfm->__crt_alg;
+ struct crypto4xx_alg *amcc_alg = crypto_alg_to_crypto4xx_alg(alg);
+ struct crypto4xx_ctx *ctx = crypto_tfm_ctx(tfm);
+
+ ctx->dev = amcc_alg->dev;
+ ctx->sa_in = NULL;
+ ctx->sa_out = NULL;
+ ctx->sa_in_dma_addr = 0;
+ ctx->sa_out_dma_addr = 0;
+ ctx->sa_len = 0;
+
+ if (alg->cra_type == &crypto_ablkcipher_type)
+ tfm->crt_ablkcipher.reqsize = sizeof(struct crypto4xx_ctx);
+ else if (alg->cra_type == &crypto_ahash_type)
+ tfm->crt_ahash.reqsize = sizeof(struct crypto4xx_ctx);
+ return 0;
+}
+
+void crypto4xx_alg_exit(struct crypto_tfm *tfm)
+{
+ struct crypto4xx_ctx *ctx = crypto_tfm_ctx(tfm);
+ crypto4xx_free_sa(ctx);
+ crypto4xx_free_state_record(ctx);
+}
+
+static int crypto4xx_register_alg(struct crypto4xx_device *sec_dev,
+ struct crypto_alg *crypto_alg)
+{
+ struct crypto4xx_alg *alg;
+ int i;
+ int rc = 0;
+
+ for (i = 0; crypto_alg[i].cra_name[0] != '\0'; i++) {
+ alg = kzalloc(sizeof(struct crypto4xx_alg), GFP_KERNEL);
+ if (!alg)
+ return -ENOMEM;
+
+ alg->alg = crypto_alg[i];
+ INIT_LIST_HEAD(&alg->alg.cra_list);
+ if (alg->alg.cra_init == NULL)
+ alg->alg.cra_init = crypto4xx_alg_init;
+ if (alg->alg.cra_exit == NULL)
+ alg->alg.cra_exit = crypto4xx_alg_exit;
+ alg->dev = sec_dev;
+ list_add_tail(&alg->entry, &sec_dev->alg_list);
+ rc = crypto_register_alg(&alg->alg);
+ if (rc) {
+ list_del(&alg->entry);
+ kfree(alg);
+ return rc;
+ }
+ }
+ return rc;
+}
+
+static void crypto4xx_unregister_alg(struct crypto4xx_device *sec_dev)
+{
+ struct crypto4xx_alg *alg, *tmp;
+
+ list_for_each_entry_safe(alg, tmp, &sec_dev->alg_list, entry) {
+ list_del(&alg->entry);
+ crypto_unregister_alg(&alg->alg);
+ kfree(alg);
+ }
+}
+
+static void crypto4xx_bh_tasklet_cb(unsigned long data)
+{
+ struct crypto4xx_core_device *lsec;
+ struct pd_uinfo *pd_uinfo;
+ struct ce_pd *pd;
+ u32 tail;
+
+ lsec = (struct crypto4xx_core_device *) data;
+
+ while (lsec->dev.pdr_head != lsec->dev.pdr_tail) {
+ tail = lsec->dev.pdr_tail;
+ pd_uinfo = lsec->dev.pd_uinfo_array[tail];
+ pd = lsec->dev.pdp_array[tail];
+ if ((pd_uinfo->state == PD_ENTRY_INUSE) &&
+ pd->pd_ctl.bf.pe_done &&
+ !pd->pd_ctl.bf.host_ready) {
+ pd->pd_ctl.bf.pe_done = 0;
+ crypto4xx_pd_done(lsec, tail);
+ crypto4xx_put_pd_to_pdr(&(lsec->dev), tail);
+ pd_uinfo->state = PD_ENTRY_FREE;
+ } else {
+ /* if tail not done, break */
+ break;
+ }
+ }
+}
+
+/**
+ * Top Half of isr.
+ */
+static int crypto4xx_ce_interrupt_handler(int irq, void *id)
+{
+ if (lsec_core.ce_base == 0)
+ return 0;
+
+ lsec_core.irq_cnt++;
+ crypto4xx_write32(CRYPTO_ENGINE_INT_CLR, 0x3ffff);
+ tasklet_schedule(&lsec_core.tasklet);
+
+ return IRQ_HANDLED;
+}
+/**
+ * Module Initialization Routine
+ *
+ */
+static int __init crypto4xx_crypto_probe(struct of_device *ofdev,
+ const struct of_device_id *match)
+{
+ int rc;
+ struct resource res;
+
+ lsec_core.ce_base = 0;
+ lsec_core.irq_cnt = 0ll;
+
+ memset(&lsec_core.dev, 0, sizeof(struct crypto4xx_device));
+
+ INIT_LIST_HEAD(&lsec_core.dev.alg_list);
+
+ crypto4xx_build_pdr(&(lsec_core.dev));
+ crypto4xx_build_gdr(&(lsec_core.dev));
+ crypto4xx_build_sdr(&(lsec_core.dev));
+
+ /* Init tasklet for bottom half processing */
+ tasklet_init(&lsec_core.tasklet, crypto4xx_bh_tasklet_cb,
+ (unsigned long)&lsec_core);
+
+ /* Register for Crypto isr, Crypto Engine IRQ */
+ lsec_core.irq = of_irq_to_resource(ofdev->node, 0, NULL);
+ rc = request_irq(lsec_core.irq, crypto4xx_ce_interrupt_handler, 0,
+ lsec_core.dev.name, NULL);
+ if (rc)
+ goto err_register_intr;
+
+ /* include address for kasumi that is eip06*/
+ rc = of_address_to_resource(ofdev->node, 0, &res);
+ if (rc)
+ return -ENODEV;
+
+ lsec_core.ce_phy_address = res.start;
+ lsec_core.ce_base = ioremap(lsec_core.ce_phy_address, 0x80400);
+
+ /* need to setup pdr, rdr, gdr and sdr */
+ rc = crypto4xx_start_device(&lsec_core.dev);
+ if (rc)
+ goto err_register_intr;
+
+ /* Register security algorithms with Linux CryptoAPI */
+ rc = crypto4xx_register_alg(&lsec_core.dev, crypto4xx_basic_alg);
+ if (rc)
+ goto err_register_alg;
+
+ CRYPTUTL_PRINTK(KERN_INFO, "Loaded AMCC PPC4XX crypto "
+ "accelerator driver v%s\n", PPC4XX_SEC_VERSION_STR);
+
+ return rc;
+
+err_register_intr:
+err_register_alg:
+ crypto4xx_unregister_alg(&lsec_core.dev);
+ free_irq(lsec_core.irq, &lsec_core.dev.name);
+ crypto4xx_stop_all();
+
+ return rc;
+}
+
+static int __exit crypto4xx_crypto_remove(struct of_device *dev)
+{
+ free_irq(lsec_core.irq, NULL);
+ /* Un-register with Linux CryptoAPI */
+ crypto4xx_unregister_alg(&lsec_core.dev);
+ /* Free all allocated memory */
+ crypto4xx_stop_all();
+
+ CRYPTUTL_PRINTK(KERN_INFO, "Unloaded AMCC PPC4XX crypto "
+ "accelerator driver v%s\n", PPC4XX_SEC_VERSION_STR);
+
+ return 0;
+}
+
+static struct of_device_id crypto4xx_crypto_match[] = {
+ { .compatible = "crypto4xx-crypto", },
+ { .compatible = "amcc,crypto4xx-crypto",},
+ { },
+};
+
+static struct of_platform_driver crypto4xx_crypto_driver = {
+ .name = "crypto4xx-crypto",
+ .match_table = crypto4xx_crypto_match,
+ .probe = crypto4xx_crypto_probe,
+ .remove = crypto4xx_crypto_remove,
+};
+
+static int __init crypto4xx_lsec_init(void)
+{
+ return of_register_platform_driver(&crypto4xx_crypto_driver);
+}
+
+static void __exit crypto4xx_lsec_exit(void)
+{
+ of_unregister_platform_driver(&crypto4xx_crypto_driver);
+}
+
+module_init(crypto4xx_lsec_init);
+module_exit(crypto4xx_lsec_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("James Hsiao <[email protected]>");
+MODULE_DESCRIPTION("Driver for AMCC PPC4xx crypto accelerator");
+
diff --git a/drivers/crypto/amcc/crypto4xx_core.h b/drivers/crypto/amcc/crypto4xx_core.h
new file mode 100644
index 0000000..27b5d51
--- /dev/null
+++ b/drivers/crypto/amcc/crypto4xx_core.h
@@ -0,0 +1,265 @@
+/*******************************************************************************
+ * AMCC SoC Crypto4XX Driver
+ *
+ * Copyright (c) 2008 Applied Micro Circuits Corporation.
+ * All rights reserved. James Hsiao <[email protected]>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * @file crypto4xx_core.h
+ *
+ * This is the header file for AMCC Crypto offload Linux device driver for
+ * use with Linux CryptoAPI.
+ *
+ *******************************************************************************
+ */
+
+#ifndef __CRYPTO4XX_CORE_H__
+#define __CRYPTO4XX_CORE_H__
+
+/**
+ * Debugging Macro
+ *
+ */
+#define PPC4XX_SEC_DEBUG
+#define PFX "CRYPTO4XX: "
+
+#if !defined(PPC4XX_SEC_DEBUG)
+# define CRYPTUTL_PRINTK(ll, fmt, ...)
+#else
+# define CRYPTUTL_PRINTK(ll, fmt, ...) \
+ do { \
+ printk(ll PFX fmt "\n", ##__VA_ARGS__); \
+} while (0);
+#endif
+
+#define CRYPTO4XX_CRYPTO_PRIORITY 300
+
+#define PPC4XX_LAST_PD 63
+#define PPC4XX_NUM_PD 64
+
+#define PPC4XX_LAST_GD 1023
+#define PPC4XX_NUM_GD 1024
+
+#define PPC4XX_LAST_SD 63
+#define PPC4XX_NUM_SD 64
+
+#define PPC4XX_SD_BUFFER_SIZE 2048
+
+#define PPC4XX_INT_DESCR_CNT 4
+#define PPC4XX_INT_TIMEOUT_CNT 0
+/* FIXme arbitory number*/
+#define PPC4XX_INT_CFG 1
+
+#ifdef CONFIG_405EX
+#define SDR0_SRST0 0x00000200
+#define SRST0_CRYP 0x00000008
+#else
+#define SDR0_SRST0 0x00000201
+#define SRST0_CRYP 0x08000000
+#endif
+
+#define SDR0_CRYP0 0x00004500
+#define CRYP0_INT 0x80000000
+
+/*
+ * These define will be used in crypto4xx_build_pd
+ * AHASH don't have dst scatterlist iso u8*
+ * with the type field it can destinguish what is
+ */
+#define ABLK 0
+#define AHASH 1
+
+#define PD_ENTRY_INUSE 1
+#define PD_ENTRY_FREE 0
+
+#define EALLOC_MEM_FAIL 0xfffffffd
+#define EDOWNSEMA_FAIL 0xfffffffe
+#define ERING_WAS_FULL 0xffffffff
+
+struct crypto4xx_device;
+extern struct crypto4xx_core_device lsec_core;
+extern struct crypto_alg crypto4xx_basic_alg[];
+
+struct pd_uinfo {
+ struct crypto4xx_device *dev;
+ u32 state;
+ u32 using_sd;
+ void *pd_va; /* offset from pdr */
+ void *rd_va; /* offset from rdr, could be
+ same as pdr(same as pd_va)*/
+ u32 first_gd; /* first gather discriptor
+ used by this packet */
+ u32 last_gd; /* last gather discriptor
+ used by this packet */
+ u32 first_sd; /* first scatter discriptor
+ used by this packet */
+ u32 last_sd; /* last scatter discriptor
+ used by this packet */
+ u32 first_done;
+ u32 last_done;
+ struct scatterlist *dest_va;
+ u32 cryptype;
+ struct crypto_async_request *async_req; /* base crypto request
+ for this packet */
+};
+
+struct crypto4xx_device {
+ u8 dev_id; /* Device ID - id of device to
+ send request to */
+ char *name;
+ void *pdr; /* base address of packet
+ descriptor ring */
+ dma_addr_t pdr_pa; /* physical address used to
+ program ce pdr_base_register */
+ void *rdr; /* result descriptor ring, maybe same
+ location as pdr */
+ dma_addr_t rdr_pa; /* physical address used to
+ program ce rdr_base_register */
+ void *gdr; /* gather descriptor ring,
+ for inbound packet/fragments */
+ /* address of particle is
+ from the request, src sg*/
+ dma_addr_t gdr_pa; /* physical address used to
+ program ce gdr_base_register */
+ void *sdr; /* scatter descriptor ring,for outbound
+ packet/fragments
+ must be same size, so init them
+ to 2k each safe for large
+ packets */
+ dma_addr_t sdr_pa; /* physical address used to
+ program ce sdr_base_register */
+ dma_addr_t scatter_buffer_pa;
+ void *scatter_buffer_va;
+ u32 scatter_buffer_size;
+ int pdr_tail;
+ int pdr_head;
+ u32 gdr_tail;
+ u32 gdr_head;
+ u32 sdr_tail;
+ u32 sdr_head;
+ void *pdr_uinfo;
+ struct list_head alg_list; /* List of algorithm supported
+ by this device */
+ struct pd_uinfo *pd_uinfo_array[PPC4XX_NUM_PD];
+ struct ce_pd *pdp_array[PPC4XX_NUM_PD];
+ struct ce_gd *gdp_array[PPC4XX_NUM_GD];
+ struct ce_sd *sdp_array[PPC4XX_NUM_SD];
+};
+
+struct crypto4xx_core_device {
+ struct crypto4xx_device dev;
+ u32 int_status;
+ u32 irq;
+ u64 irq_cnt;
+ struct tasklet_struct tasklet;
+ u64 ce_phy_address;
+ void __iomem *ce_base;
+};
+
+struct crypto4xx_ctx {
+ struct crypto4xx_device *dev;
+ void *sa_in;
+ dma_addr_t sa_in_dma_addr;
+ void *sa_out;
+ dma_addr_t sa_out_dma_addr;
+ void *state_record;
+ dma_addr_t state_record_dma_addr;
+ u16 sa_len;
+ u32 direction;
+ u32 use_rctx;
+ u32 next_hdr;
+ u32 save_iv;
+ u32 pd_ctl_len;
+ u32 pd_ctl;
+ u32 bypass;
+ u32 is_hash;
+ u32 hash_final;
+};
+
+struct crypto4xx_req_ctx {
+ struct crypto4xx_device *dev; /* Device in which
+ operation to send to */
+ void *sa;
+ dma_addr_t sa_dma_addr;
+ u16 sa_len;
+};
+
+struct crypto4xx_alg {
+ struct list_head entry;
+ struct crypto_alg alg;
+ struct crypto4xx_device *dev;
+};
+
+#define crypto_alg_to_crypto4xx_alg(x) \
+ container_of(x, struct crypto4xx_alg, alg)
+
+extern u32 crypto4xx_write32(u32 reg, u32 val);
+extern u32 crypto4xx_read32(u32 reg, u32 *val);
+extern u32 crypto4xx_get_pd_from_pdr(struct crypto4xx_device *dev);
+extern u32 crypto4xx_put_pd_to_pdr(struct crypto4xx_device *dev, u32 idx);
+
+extern struct ce_pd *crypto4xx_get_pdp(struct crypto4xx_device *dev,
+ dma_addr_t *pd_dma, u32 idx);
+extern struct ce_gd *crypto4xx_get_gdp(struct crypto4xx_device *dev,
+ dma_addr_t *gd_dma, u32 idx);
+extern struct ce_sd *crypto4xx_get_sdp(struct crypto4xx_device *dev,
+ dma_addr_t *sd_dma, u32 idx);
+extern void crypto4xx_alloc_sa(struct crypto4xx_ctx *ctx, u32 size);
+extern u32 crypto4xx_alloc_sa_rctx(struct crypto4xx_ctx *ctx,
+ struct crypto4xx_ctx *rctx);
+
+
+extern struct crypto4xx_ctx *crypto4xx_alloc_ctx
+ (struct crypto4xx_ctx *ctx);
+
+extern void crypto4xx_free_ctx(struct crypto4xx_ctx *ctx);
+extern u32 crypto4xx_build_pdr(struct crypto4xx_device *dev);
+extern u32 crypto4xx_build_gdr(struct crypto4xx_device *dev);
+extern u32 crypto4xx_build_sdr(struct crypto4xx_device *dev);
+
+extern u32 crypto4xx_pd_done(struct crypto4xx_core_device *lsec, u32 idx);
+
+extern u32 crypto4xx_start_device(struct crypto4xx_device *dev);
+
+extern u32 crypto4xx_stop_all(void);
+extern u32 crypto4xx_config_clear(void);
+
+extern void crypto4xx_free_sa(struct crypto4xx_ctx *ctx);
+extern u32 crypto4xx_alloc_state_record(struct crypto4xx_ctx *ctx);
+extern void crypto4xx_free_state_record(struct crypto4xx_ctx *ctx);
+
+extern u32 get_dynamic_sa_offset_state_ptr_field(struct crypto4xx_ctx *ctx);
+extern u32 get_dynamic_sa_offset_iv_field(struct crypto4xx_ctx *ctx);
+extern void dump_dynamic_sa_iv_field(struct crypto4xx_ctx *ctx);
+extern u32 get_dynamic_sa_iv_size(struct crypto4xx_ctx *ctx);
+
+
+extern void crypto4xx_memcpy_be(unsigned int *dst,
+ const unsigned char *buf, int len);
+extern void crypto4xx_memcpy_le(unsigned int *dst,
+ const unsigned char *buf, int len);
+extern int crypto4xx_setup_crypto(struct crypto_async_request *req);
+
+extern u32 crypto4xx_check_pd_done(struct crypto4xx_device *dev);
+extern void crypto4xx_alg_exit(struct crypto_tfm *tfm);
+extern void crypto4xx_free_sa_rctx(struct crypto4xx_ctx *rctx);
+extern int crypto4xx_handle_req(struct crypto_async_request *req);
+extern u32 crypto4xx_build_pd(struct crypto4xx_device *dev,
+ struct crypto_async_request *req,
+ u32 pd_entry,
+ struct crypto4xx_ctx *ctx,
+ struct scatterlist *src,
+ struct scatterlist *dst,
+ u16 datalen,
+ u8 type);
+
+#endif
diff --git a/drivers/crypto/amcc/crypto4xx_reg_def.h b/drivers/crypto/amcc/crypto4xx_reg_def.h
new file mode 100644
index 0000000..04b0751
--- /dev/null
+++ b/drivers/crypto/amcc/crypto4xx_reg_def.h
@@ -0,0 +1,290 @@
+/****************************************************************************
+ * AMCC SoC Crypto4XX Driver
+ *
+ * Copyright (c) 2008 Applied Micro Circuits Corporation.
+ * All rights reserved. James Hsiao <[email protected]>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * @file crypto4xx_reg_def.h
+ *
+ * This filr defines the register set for Security Subsystem
+ *
+ ****************************************************************************
+ */
+
+#ifndef __CRYPTO_ENGINE_REG_DEF_H__
+#define __CRYPTO_ENGINE_REG_DEF_H__
+
+/* CRYPTO_ENGINE Register offset */
+#define CRYPTO_ENGINE_DESCRIPTOR 0x00000000
+#define CRYPTO_ENGINE_CTRL_STAT 0x00000000
+#define CRYPTO_ENGINE_SOURCE 0x00000004
+#define CRYPTO_ENGINE_DEST 0x00000008
+#define CRYPTO_ENGINE_SA 0x0000000C
+#define CRYPTO_ENGINE_SA_LENGTH 0x00000010
+#define CRYPTO_ENGINE_LENGTH 0x00000014
+
+
+#define CRYPTO_ENGINE_PE_DMA_CFG 0x00000040
+#define CRYPTO_ENGINE_PE_DMA_STAT 0x00000044
+#define CRYPTO_ENGINE_PDR_BASE 0x00000048
+#define CRYPTO_ENGINE_RDR_BASE 0x0000004c
+#define CRYPTO_ENGINE_RING_SIZE 0x00000050
+#define CRYPTO_ENGINE_RING_CTRL 0x00000054
+#define CRYPTO_ENGINE_INT_RING_STAT 0x00000058
+#define CRYPTO_ENGINE_EXT_RING_STAT 0x0000005c
+#define CRYPTO_ENGINE_IO_THRESHOLD 0x00000060
+#define CRYPTO_ENGINE_GATH_RING_BASE 0x00000064
+#define CRYPTO_ENGINE_SCAT_RING_BASE 0x00000068
+#define CRYPTO_ENGINE_PART_RING_SIZE 0x0000006c
+#define CRYPTO_ENGINE_PART_RING_CFG 0x00000070
+
+#define CRYPTO_ENGINE_PDR_BASE_UADDR 0x00000080
+#define CRYPTO_ENGINE_RDR_BASE_UADDR 0x00000084
+#define CRYPTO_ENGINE_PKT_SRC_UADDR 0x00000088
+#define CRYPTO_ENGINE_PKT_DEST_UADDR 0x0000008c
+#define CRYPTO_ENGINE_SA_UADDR 0x00000090
+#define CRYPTO_ENGINE_GATH_RING_BASE_UADDR 0x000000A0
+#define CRYPTO_ENGINE_SCAT_RING_BASE_UADDR 0x000000A4
+
+#define CRYPTO_ENGINE_SEQ_RD 0x00000408
+#define CRYPTO_ENGINE_SEQ_MASK_RD 0x0000040C
+
+#define CRYPTO_ENGINE_SA_CMD_0 0x00010600
+#define CRYPTO_ENGINE_SA_CMD_1 0x00010604
+
+#define CRYPTO_ENGINE_STATE_PTR 0x000106dc
+#define CRYPTO_ENGINE_STATE_IV 0x00010700
+#define CRYPTO_ENGINE_STATE_HASH_BYTE_CNT_0 0x00010710
+#define CRYPTO_ENGINE_STATE_HASH_BYTE_CNT_1 0x00010714
+
+#define CRYPTO_ENGINE_STATE_IDIGEST_0 0x00010718
+#define CRYPTO_ENGINE_STATE_IDIGEST_1 0x0001071c
+
+#define CRYPTO_ENGINE_DATA_IN 0x00018000
+#define CRYPTO_ENGINE_DATA_OUT 0x0001c000
+
+
+#define CRYPTO_ENGINE_INT_UNMASK_STAT 0x000500a0
+#define CRYPTO_ENGINE_INT_MASK_STAT 0x000500a4
+#define CRYPTO_ENGINE_INT_CLR 0x000500a4
+#define CRYPTO_ENGINE_INT_EN 0x000500a8
+
+#define CRYPTO_ENGINE_INT_PKA 0x00000002
+#define CRYPTO_ENGINE_INT_PDR_DONE 0x00008000
+#define CRYPTO_ENGINE_INT_MA_WR_ERR 0x00020000
+#define CRYPTO_ENGINE_INT_MA_RD_ERR 0x00010000
+#define CRYPTO_ENGINE_INT_PE_ERR 0x00000200
+#define CRYPTO_ENGINE_INT_USER_DMA_ERR 0x00000040
+#define CRYPTO_ENGINE_INT_SLAVE_ERR 0x00000010
+#define CRYPTO_ENGINE_INT_MASTER_ERR 0x00000008
+#define CRYPTO_ENGINE_INT_ERROR 0x00030258
+
+#define CRYPTO_ENGINE_INT_CFG 0x000500ac
+#define CRYPTO_ENGINE_INT_DESCR_RD 0x000500b0
+#define CRYPTO_ENGINE_INT_DESCR_CNT 0x000500b4
+#define CRYPTO_ENGINE_INT_TIMEOUT_CNT 0x000500b8
+
+#define CRYPTO_ENGINE_DC_CTRL 0x00060080
+#define CRYPTO_ENGINE_DEVICE_ID 0x00060084
+#define CRYPTO_ENGINE_DEVICE_INFO 0x00060088
+#define CRYPTO_ENGINE_DMA_USER_SRC 0x00060094
+#define CRYPTO_ENGINE_DMA_USER_DEST 0x00060098
+#define CRYPTO_ENGINE_DMA_USER_CMD 0x0006009C
+
+#define CRYPTO_ENGINE_DMA_CFG 0x000600d4
+#define CRYPTO_ENGINE_BYTE_ORDER_CFG 0x000600d8
+#define CRYPTO_ENGINE_ENDIAN_CFG 0x000600d8
+
+#define CRYPTO_ENGINE_PRNG_STAT 0x00070000
+#define CRYPTO_ENGINE_PRNG_CTRL 0x00070004
+#define CRYPTO_ENGINE_PRNG_SEED_L 0x00070008
+#define CRYPTO_ENGINE_PRNG_SEED_H 0x0007000c
+
+#define CRYPTO_ENGINE_PRNG_RES_0 0x00070020
+#define CRYPTO_ENGINE_PRNG_RES_1 0x00070024
+#define CRYPTO_ENGINE_PRNG_RES_2 0x00070028
+#define CRYPTO_ENGINE_PRNG_RES_3 0x0007002C
+
+#define CRYPTO_ENGINE_PRNG_LFSR_L 0x00070030
+#define CRYPTO_ENGINE_PRNG_LFSR_H 0x00070034
+
+/**
+ * Initilize CRYPTO ENGINE registers, and memory bases.
+ */
+
+#define PPC4XX_PDR_POLL 0x3ff
+#define PPC4XX_OUTPUT_THRESHOLD 2
+#define PPC4XX_INPUT_THRESHOLD 2
+#define PPC4XX_PD_SIZE 6
+#define CRYPTO_CTX_DONE_INT 0x2000
+#define CRYPTO_PD_DONE_INT 0x8000
+/**
+ * all follow define are ad hoc
+ */
+#define PPC4XX_RING_RETRY 100
+#define PPC4XX_RING_POLL 100
+#define PPC4XX_SDR_SIZE PPC4XX_NUM_SD
+#define PPC4XX_GDR_SIZE PPC4XX_NUM_GD
+
+/**
+ *
+ * IPE Generic Security Association (SA) with all possible fields. These will
+ * never likely used except for reference purpose. These structure format
+ * can be not changed as the hardware expects them to be layout as defined.
+ * Field can be removed or reduced but ordering can not be changed.
+ *
+ *
+ */
+
+#define CRYPTO_ENGINE_DMA_CFG_OFFSET 0x40
+union ce_pe_dma_cfg {
+ struct {
+ u32 rsv:7;
+ u32 dir_host:1;
+ u32 rsv1:2;
+ u32 bo_td_en:1;
+ u32 dis_pdr_upd:1;
+ u32 bo_sgpd_en:1;
+ u32 bo_data_en:1;
+ u32 bo_sa_en:1;
+ u32 bo_pd_en:1;
+ u32 rsv2:4;
+ u32 dynamic_sa_en:1;
+ u32 pdr_mode:2;
+ u32 pe_mode:1;
+ u32 rsv3:5;
+ u32 reset_sg:1;
+ u32 reset_pdr:1;
+ u32 reset_pe:1;
+ } bf;
+ u32 w;
+} __attribute__((packed));
+
+#define CRYPTO_ENGINE_PDR_BASE_OFFSET 0x48
+#define CRYPTO_ENGINE_RDR_BASE_OFFSET 0x4c
+
+#define CRYPTO_ENGINE_RING_SIZE_OFFSET 0x50
+union ce_ring_size {
+ struct {
+ u32 ring_offset:16;
+ u32 rsv:6;
+ u32 ring_size:10;
+ } bf;
+ u32 w;
+} __attribute__((packed));
+
+#define CRYPTO_ENGINE_RING_CONTROL_OFFSET 0x54
+union ce_ring_contol {
+ struct {
+ u32 continuous:1;
+ u32 rsv:5;
+ u32 ring_retry_divisor:10;
+ u32 rsv1:4;
+ u32 ring_poll_divisor:10;
+ } bf;
+ u32 w;
+} __attribute__((packed));
+
+#define CRYPTO_ENGINE_IO_THRESHOLD_OFFSET 0x60
+union ce_io_threshold {
+ struct {
+ u32 rsv:6;
+ u32 output_threshold:10;
+ u32 rsv1:6;
+ u32 input_threshold:10;
+ } bf;
+ u32 w;
+} __attribute__((packed));
+
+#define CRYPTO_ENGINE_GATHER_RING_BASE_OFFSET 0x64
+#define CRYPTO_ENGINE_SCATTER_RING_BASE_OFFSET 0x68
+
+union ce_part_ring_size {
+ struct {
+ u32 sdr_size:16;
+ u32 gdr_size:16;
+ } bf;
+ u32 w;
+} __attribute__((packed));
+
+#define MAX_BURST_SIZE_32 0
+#define MAX_BURST_SIZE_64 1
+#define MAX_BURST_SIZE_128 2
+#define MAX_BURST_SIZE_256 3
+
+/* gather descriptor control length */
+struct gd_ctl_len {
+ u32 len:16;
+ u32 rsv:14;
+ u32 done:1;
+ u32 ready:1;
+} __attribute__((packed));
+
+struct ce_gd {
+ u32 ptr;
+ struct gd_ctl_len ctl_len;
+} __attribute__((packed));
+
+struct sd_ctl {
+ u32 ctl:30;
+ u32 done:1;
+ u32 rdy:1;
+} __attribute__((packed));
+
+struct ce_sd {
+ struct sd_ctl ctl;
+} __attribute__((packed));
+
+#define PD_PAD_CTL_32 0x10
+#define PD_PAD_CTL_64 0x20
+#define PD_PAD_CTL_128 0x40
+#define PD_PAD_CTL_256 0x80
+union ce_pd_ctl {
+ struct {
+ u32 pd_pad_ctl:8;
+ u32 status:8;
+ u32 next_hdr:8;
+ u32 rsv:2;
+ u32 cached_sa:1;
+ u32 hash_final:1;
+ u32 init_arc4:1;
+ u32 rsv1:1;
+ u32 pe_done:1;
+ u32 host_ready:1;
+ } bf;
+ u32 w;
+} __attribute__((packed));
+
+union ce_pd_ctl_len {
+ struct {
+ u32 bypass:8;
+ u32 pe_done:1;
+ u32 host_ready:1;
+ u32 rsv:2;
+ u32 pkt_len:20;
+ } bf;
+ u32 w;
+} __attribute__((packed));
+
+struct ce_pd {
+ union ce_pd_ctl pd_ctl;
+ dma_addr_t src;
+ dma_addr_t dest;
+ dma_addr_t sa; /* get from ctx->sa_dma_addr */
+ u32 sa_len; /* only if dynamic sa is used */
+ union ce_pd_ctl_len pd_ctl_len;
+
+} __attribute__((packed));
+
+
+#endif
diff --git a/drivers/crypto/amcc/crypto4xx_sa.c b/drivers/crypto/amcc/crypto4xx_sa.c
new file mode 100644
index 0000000..36ce18d
--- /dev/null
+++ b/drivers/crypto/amcc/crypto4xx_sa.c
@@ -0,0 +1,91 @@
+/****************************************************************************
+ * AMCC SoC Crypto4XX Driver
+ *
+ * Copyright (c) 2008 Applied Micro Circuits Corporation.
+ * All rights reserved. James Hsiao <[email protected]>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * @file crypto4xx_sa.c
+ *
+ * This file implements the security context
+ * assoicate format.
+ *
+ ****************************************************************************
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/mod_devicetable.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock_types.h>
+#include <linux/highmem.h>
+#include <linux/scatterlist.h>
+#include <linux/crypto.h>
+#include <crypto/algapi.h>
+#include <crypto/des.h>
+#include "crypto4xx_reg_def.h"
+#include "crypto4xx_sa.h"
+#include "crypto4xx_core.h"
+
+u32 get_dynamic_sa_offset_iv_field(struct crypto4xx_ctx *ctx)
+{
+ u32 offset;
+ union dynamic_sa_contents cts;
+
+ cts.w = ((struct dynamic_sa_ctl *)(ctx->sa_in))->sa_contents;
+
+ offset = cts.bf.key_size
+ + cts.bf.inner_size
+ + cts.bf.outer_size
+ + cts.bf.spi
+ + cts.bf.seq_num0
+ + cts.bf.seq_num1
+ + cts.bf.seq_num_mask0
+ + cts.bf.seq_num_mask1
+ + cts.bf.seq_num_mask2
+ + cts.bf.seq_num_mask3;
+
+ return sizeof(struct dynamic_sa_ctl) + offset * 4;
+}
+
+u32 get_dynamic_sa_offset_state_ptr_field(struct crypto4xx_ctx *ctx)
+{
+ u32 offset;
+ union dynamic_sa_contents cts;
+
+ cts.w = ((struct dynamic_sa_ctl *)(ctx->sa_in))->sa_contents;
+
+ offset = cts.bf.key_size
+ + cts.bf.inner_size
+ + cts.bf.outer_size
+ + cts.bf.spi
+ + cts.bf.seq_num0
+ + cts.bf.seq_num1
+ + cts.bf.seq_num_mask0
+ + cts.bf.seq_num_mask1
+ + cts.bf.seq_num_mask2
+ + cts.bf.seq_num_mask3
+ + cts.bf.iv0
+ + cts.bf.iv1
+ + cts.bf.iv2
+ + cts.bf.iv3;
+
+ return sizeof(struct dynamic_sa_ctl) + offset * 4;
+}
+
+u32 get_dynamic_sa_iv_size(struct crypto4xx_ctx *ctx)
+{
+ union dynamic_sa_contents cts;
+ cts.w = ((struct dynamic_sa_ctl *)(ctx->sa_in))->sa_contents;
+ return (cts.bf.iv0 + cts.bf.iv1 + cts.bf.iv2 + cts.bf.iv3) * 4;
+}
+
diff --git a/drivers/crypto/amcc/crypto4xx_sa.h b/drivers/crypto/amcc/crypto4xx_sa.h
new file mode 100644
index 0000000..f60a9d8
--- /dev/null
+++ b/drivers/crypto/amcc/crypto4xx_sa.h
@@ -0,0 +1,223 @@
+/****************************************************************************
+ * AMCC SoC Crypto4XX Driver
+ *
+ * Copyright (c) 2008 Applied Micro Circuits Corporation.
+ * All rights reserved. James Hsiao <[email protected]>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * @file crypto4xx_sa.h
+ *
+ * This file defines the security context
+ * assoicate format.
+ *
+ ****************************************************************************
+ */
+
+#ifndef __CRYPTO4XX_SA_H__
+#define __CRYPTO4XX_SA_H__
+
+#define u32 unsigned int
+
+/**
+ *
+ * Contents of Dynamic Security Association (SA) with all possible fields
+ */
+union dynamic_sa_contents {
+ struct {
+ u32 arc4_state_ptr:1;
+ u32 arc4_ij_ptr:1;
+ u32 state_ptr:1;
+ u32 iv3:1;
+ u32 iv2:1;
+ u32 iv1:1;
+ u32 iv0:1;
+ u32 seq_num_mask3:1;
+ u32 seq_num_mask2:1;
+ u32 seq_num_mask1:1;
+ u32 seq_num_mask0:1;
+ u32 seq_num1:1;
+ u32 seq_num0:1;
+ u32 spi:1;
+ u32 outer_size:5;
+ u32 inner_size:5;
+ u32 key_size:4;
+ u32 cmd_size:4;
+ } bf;
+ u32 w;
+} __attribute__((packed));
+
+#define CRYPTO_OUTBOUND 0
+#define CRYPTO_INBOUND 1
+
+#define SA_OPCODE_ENCRYPT 0
+#define SA_OPCODE_DECRYPT 0
+
+#define SA_OPCODE_HASH 3
+
+#define SA_CIPHER_ALG_DES 0
+#define SA_CIPHER_ALG_3DES 1
+#define SA_CIPHER_ALG_ARC4 2
+#define SA_CIPHER_ALG_AES 3
+#define SA_CIPHER_ALG_KASUMI 4
+#define SA_CIPHER_ALG_NULL 15
+
+#define SA_HASH_ALG_MD5 0
+#define SA_HASH_ALG_SHA1 1
+#define SA_HASH_ALG_NULL 15
+
+#define SA_HASH_ALG_SHA1_DIGEST_SIZE 20
+
+#define SA_LOAD_HASH_FROM_SA 0
+#define SA_LOAD_HASH_FROM_STATE 2
+#define SA_LOAD_HASH_NO_LOAD 3
+
+union sa_command_0 {
+ struct {
+ u32 scatter:1;
+ u32 gather:1;
+ u32 save_hash_state:1;
+ u32 save_iv:1;
+ u32 load_hash_state:2;
+ u32 load_iv:2;
+ u32 digest_len:4;
+ u32 hdr_proc:1;
+ u32 extend_pad:1;
+ u32 stream_cipher_pad:1;
+ u32 rsv:1;
+ u32 hash_alg:4;
+ u32 cipher_alg:4;
+ u32 pad_type:2;
+ u32 op_group:2;
+ u32 dir:1;
+ u32 opcode:3;
+ } bf;
+ u32 w;
+} __attribute__((packed));
+
+#define CRYPTO_MODE_ECB 0
+#define CRYPTO_MODE_CBC 1
+
+#define CRYPTO_FEEDBACK_MODE_NO_FB 0
+#define CRYPTO_FEEDBACK_MODE_64BIT_OFB 0
+#define CRYPTO_FEEDBACK_MODE_8BIT_CFB 1
+#define CRYPTO_FEEDBACK_MODE_1BIT_CFB 2
+#define CRYPTO_FEEDBACK_MODE_128BIT_CFB 3
+
+#define SA_AES_KEY_LEN_128 2
+#define SA_AES_KEY_LEN_192 3
+#define SA_AES_KEY_LEN_256 4
+
+/**
+ * The follow 4 defines usage of hmac_muting bit in sa_command_1
+ * In Basic hash mode this bit define simple hash or hmac.
+ * In IPsec mode, this bit define muting control.
+ */
+#define SA_HASH_MODE_HASH 0
+#define SA_HASH_MODE_HMAC 1
+
+union sa_command_1 {
+ struct {
+ u32 crypto_mode31:1;
+ u32 save_arc4_state:1;
+ u32 arc4_stateful:1;
+ u32 key_len:5;
+ u32 hash_crypto_offset:8;
+ u32 sa_rev:2;
+ u32 byte_offset:1;
+ u32 hmac_muting:1;
+ u32 feedback_mode:2;
+ u32 crypto_mode9_8:2;
+ u32 extended_seq_num:1;
+ u32 seq_num_mask:1;
+ u32 mutable_bit_proc:1;
+ u32 ip_version:1;
+ u32 copy_pad:1;
+ u32 copy_payload:1;
+ u32 copy_hdr:1;
+ u32 rsv1:1;
+ } bf;
+ u32 w;
+} __attribute__((packed));
+
+struct dynamic_sa_ctl {
+ u32 sa_contents;
+ union sa_command_0 sa_command_0;
+ union sa_command_1 sa_command_1;
+
+} __attribute__((packed));
+
+/**
+ * State Record for Security Association (SA)
+ */
+struct dynamic_sa_state_record {
+ u32 save_iv[4];
+ u32 save_hash_byte_cnt[2];
+ u32 save_digest[16];
+} __attribute__((packed));
+
+/**
+ * Security Association (SA) for AES128
+ *
+ */
+struct dynamic_sa_aes128 {
+ struct dynamic_sa_ctl ctrl;
+ u32 key[4];
+ u32 iv[4]; /* for CBC, OFC, and CFB mode */
+ u32 state_ptr;
+ u32 reserved;
+} __attribute__((packed));
+
+#define SA_AES128_LEN (sizeof(struct dynamic_sa_aes128)/4)
+#define SA_AES128_CONTENTS 0x3e000042
+
+/*
+ * Security Association (SA) for AES192
+ */
+struct dynamic_sa_aes192 {
+ struct dynamic_sa_ctl ctrl;
+ u32 key[6];
+ u32 iv[4]; /* for CBC, OFC, and CFB mode */
+ u32 state_ptr;
+ u32 reserved;
+} __attribute__((packed));
+
+#define SA_AES192_LEN (sizeof(struct dynamic_sa_aes192)/4)
+#define SA_AES192_CONTENTS 0x3e000062
+
+/**
+ * Security Association (SA) for AES256
+ */
+struct dynamic_sa_aes256 {
+ struct dynamic_sa_ctl ctrl;
+ u32 key[8];
+ u32 iv[4]; /* for CBC, OFC, and CFB mode */
+ u32 state_ptr;
+ u32 reserved;
+} __attribute__((packed));
+
+#define SA_AES256_LEN (sizeof(struct dynamic_sa_aes256)/4)
+#define SA_AES256_CONTENTS 0x3e000082
+
+/**
+ * Security Association (SA) for HASH160: HMAC-SHA1
+ */
+struct dynamic_sa_hash160 {
+ struct dynamic_sa_ctl ctrl;
+ u32 inner_digest[5];
+ u32 outer_digest[5];
+ u32 state_ptr;
+ u32 reserved;
+} __attribute__((packed));
+#define SA_HASH160_LEN (sizeof(struct dynamic_sa_hash160)/4)
+#define SA_HASH160_CONTENTS 0x2000a502
+
+#endif
--
1.5.4-rc3.GIT



2008-09-25 15:49:39

by Kim Phillips

[permalink] [raw]
Subject: Re: [PATCH] crypto: AMCC Crypto4xx Device Driver

On Fri, 19 Sep 2008 16:46:43 -0700
"Loc Ho" <[email protected]> wrote:

> arch/powerpc/boot/dts/kilauea.dts | 10 +

powerpc dts patches are reviewed on linuxppc-dev list. For ppc4xx, it
looks like new bindings are accompanied with a patch to
Documentation/powerpc/booting-without-of.txt.

> diff --git a/arch/powerpc/boot/dts/kilauea.dts b/arch/powerpc/boot/dts/kilauea.dts
> + CRYPTO: crypto@ef700000 {
> + device_type = "crypto";

device_type is deprecated, remove.

> + compatible = "crypto4xx-crypto", "amcc,crypto4xx-crypto";

compatible values should always contain the vendor prefix. is crypto
occuring twice for a reason? how about just a single entry:
"amcc,ppc4xx-crypto"?

> @@ -342,5 +351,6 @@
> 0x0 0x0 0x0 0x3 &UIC2 0xd 0x4 /* swizzled int C */
> 0x0 0x0 0x0 0x4 &UIC2 0xe 0x4 /* swizzled int D */>;
> };
> +

whitespace change not necessary

> +config CRYPTO_DEV_PPC4XX
> + tristate "Driver AMCC PPC4XX crypto accelerator"
> + select CRYPTO_HASH
> + select CRYPTO_ALGAPI
> + select CRYPTO_BLKCIPHER

this looks like an ABLKCIPHER driver (BLKCIPHER depends on
ABLKCIPHER). plus, shouldn't this depend on PPC and 4xx too?

> diff --git a/drivers/crypto/amcc/crypto4xx_alg.c b/drivers/crypto/amcc/crypto4xx_alg.c

> +#include <crypto/internal/hash.h>

does something need to move up to include/crypto/hash.h?

> + /* Application only provided ptr for the rctx
> + * we alloc memory for it. And along we alloc memory for the sa in it */

please see Documentation/CodingStyle for how to format comments.

> + ctx->use_rctx = 1;
> + rc = crypto4xx_alloc_sa_rctx(ctx, rctx);
> + if (rc)
> + goto err_nomem;
> + sa = (struct dynamic_sa_ctl *)(rctx->sa_out);
> + offset = get_dynamic_sa_offset_iv_field(rctx);

where is offset being used?

> + /* copy req->iv to state_record->iv */
> + if (req->info)
> + crypto4xx_memcpy_le(rctx->state_record, req->info,
> + get_dynamic_sa_iv_size(rctx));
> + else
> + memset(rctx->state_record, 0, get_dynamic_sa_iv_size(rctx));
> + sa->sa_command_0.bf.dir = CRYPTO_OUTBOUND;
> + rctx->hash_final = 0;
> + rctx->is_hash = 0;
> + rctx->pd_ctl = 0x1;
> + rctx->direction = CRYPTO_OUTBOUND;
> + return crypto4xx_setup_crypto(&req->base);
> +
> +err_nomem:
> + return -ENOMEM;

not much going on here; doing the return straight up instead of the
goto indirection is easier to read.

> +static inline int crypto4xx_decrypt(struct ablkcipher_request *req)
> +{
> + struct crypto4xx_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
> + struct crypto4xx_ctx *rctx = ablkcipher_request_ctx(req);
> + struct dynamic_sa_ctl *sa = NULL;
> + int rc;
> + u32 offset;
> +
> + /* Application only provided ptr for the rctx
> + * we alloc memory for it. And along we alloc memory for the sa in it*/
> + ctx->use_rctx = 1;
> + rc = crypto4xx_alloc_sa_rctx(ctx, rctx);
> + if (rc != 0)
> + goto err_nomem;

same here (and other places).

> + sa = (struct dynamic_sa_ctl *)(rctx->sa_in);
> + offset = get_dynamic_sa_offset_iv_field(rctx);

again, can't see offset being used here.

> + /* copy req->iv to state_record->iv */
> + if (req->info)
> + crypto4xx_memcpy_le(rctx->state_record, req->info,
> + get_dynamic_sa_iv_size(rctx));
> + else
> + memset(rctx->state_record, 0, get_dynamic_sa_iv_size(rctx));
> + sa->sa_command_0.bf.dir = CRYPTO_INBOUND;
> + rctx->hash_final = 0;
> + rctx->is_hash = 0;
> + rctx->pd_ctl = 1;
> + rctx->direction = CRYPTO_INBOUND;
> +
> + return crypto4xx_setup_crypto(&req->base);
> +
> +err_nomem:
> + return -ENOMEM;
> +}
> +/**
> + * Support Crypto Algorithms
> + */
> +struct crypto_alg crypto4xx_basic_alg[] = {
> +
> + /* Crypto AES modes */
> + {.cra_name = "cbc(aes)",
> + .cra_driver_name = "cbc-aes",

"cbc-aes-ppc4xx"?

> + .cra_priority = CRYPTO4XX_CRYPTO_PRIORITY,
> + .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
> + .cra_blocksize = 16, /* 128-bits block */
> + .cra_ctxsize = sizeof(struct crypto4xx_ctx),
> + .cra_alignmask = 0,
> + .cra_type = &crypto_ablkcipher_type,
> + .cra_module = THIS_MODULE,
> + .cra_u = {.ablkcipher = {
> + .min_keysize = 16, /* AES min key size is 128-bits */
> + .max_keysize = 32, /* AES max key size is 256-bits */
> + .ivsize = 16, /* IV size is 16 bytes */
> + .setkey = crypto4xx_setkey_aes_cbc,
> + .encrypt = crypto4xx_encrypt,
> + .decrypt = crypto4xx_decrypt,
> + } }
> + },
> + /* Hash SHA1, SHA2 */
> + {.cra_name = "sha1",
> + .cra_driver_name = "sha1",

-ppc4xx.

> + .cra_priority = CRYPTO4XX_CRYPTO_PRIORITY,
> + .cra_flags = CRYPTO_ALG_TYPE_AHASH | CRYPTO_ALG_ASYNC,
> + .cra_blocksize = 64, /* SHA1 block size is 512-bits */
> + .cra_ctxsize = sizeof(struct crypto4xx_ctx),
> + .cra_alignmask = 0,
> + .cra_type = &crypto_ahash_type,
> + .cra_init = crypto4xx_sha1_alg_init,
> + .cra_module = THIS_MODULE,
> + .cra_u = {.ahash = {
> + .digestsize = 20, /* Disgest is 160-bits */
> + .init = crypto4xx_hash_init,
> + .update = crypto4xx_hash_update,
> + .final = crypto4xx_hash_final,
> + .digest = crypto4xx_hash_digest,
> + } }
> + },
> + /* Terminator */
> + {.cra_name = "" }

save space and use ARRAY_SIZE

> diff --git a/drivers/crypto/amcc/crypto4xx_core.c b/drivers/crypto/amcc/crypto4xx_core.c
> +struct crypto4xx_core_device lsec_core;

this is global, and it thus doesn't handle > 1 device. shouldn't this
be the device's private data pointed to by its struct device?

> +/**
> + * PPC4XX Crypto Engine Initialization Routine
> + */
> +int32_t crypto4xx_init(struct crypto4xx_device *dev)

this (and the others) should be static, no?

> +#ifdef CONFIG_USING_PD_DONE_INT

this is not being defined anywhere; remove?

> +void crypto4xx_alloc_sa(struct crypto4xx_ctx *ctx, u32 size)
> +{
> + ctx->sa_in = pci_alloc_consistent(NULL, size * 4,

dma_alloc_consistent?

> +void ppc4xx_fill_one_page(dma_addr_t addr, u32 length,
> + u32 idx, u32 *next_sd,
> + u32 *offset, u32 *nbytes)
> +{
> + struct crypto4xx_device *dev = &(lsec_core.dev);
> + u32 len;
> +
> + if (length > dev->scatter_buffer_size) {
> + memcpy(phys_to_virt(addr),
> + dev->scatter_buffer_va +
> + idx*dev->scatter_buffer_size + (*offset),
> + dev->scatter_buffer_size);
> + *offset = 0;
> + length -= dev->scatter_buffer_size;
> + *nbytes -= dev->scatter_buffer_size;
> + if (idx == PPC4XX_LAST_SD)
> + idx = 0;
> + else
> + idx++;
> + *next_sd = idx;
> + ppc4xx_fill_one_page(addr + dev->scatter_buffer_size, length,
> + idx, next_sd, offset, nbytes);

doesn't recursing like this create an opportunity to unnecessarily
overflow the stack?

> + } else {
> + struct ahash_request *ahash_req;
> + ahash_req = ahash_request_cast(req);
> + if (ctx->use_rctx) {
> + rctx = ahash_request_ctx(ahash_req);
> + return crypto4xx_build_pd(dev, req, pd_entry, rctx,
> + ahash_req->src,
> + (struct scatterlist *)
> + ahash_req->result,
> + ahash_req->nbytes,
> + AHASH);
> + } else {
> + return crypto4xx_build_pd(dev, req, pd_entry, ctx,
> + ahash_req->src,
> + (struct scatterlist *)
> + ahash_req->result,
> + ahash_req->nbytes,
> + AHASH);

bad alignment

> + /* include address for kasumi that is eip06*/

where in the code does this comment apply?

> + rc = of_address_to_resource(ofdev->node, 0, &res);
> + if (rc)
> + return -ENODEV;
> +
> + lsec_core.ce_phy_address = res.start;
> + lsec_core.ce_base = ioremap(lsec_core.ce_phy_address, 0x80400);

0x80400 should be obtained from device tree. use of_iomap?

> +
> + /* need to setup pdr, rdr, gdr and sdr */
> + rc = crypto4xx_start_device(&lsec_core.dev);
> + if (rc)
> + goto err_register_intr;
> +
> + /* Register security algorithms with Linux CryptoAPI */
> + rc = crypto4xx_register_alg(&lsec_core.dev, crypto4xx_basic_alg);
> + if (rc)
> + goto err_register_alg;
> +
> + CRYPTUTL_PRINTK(KERN_INFO, "Loaded AMCC PPC4XX crypto "
> + "accelerator driver v%s\n", PPC4XX_SEC_VERSION_STR);
> +
> + return rc;
> +
> +err_register_intr:
> +err_register_alg:

use a single err_register label?

> diff --git a/drivers/crypto/amcc/crypto4xx_core.h b/drivers/crypto/amcc/crypto4xx_core.h
> +/**
> + * Debugging Macro
> + *
> + */
> +#define PPC4XX_SEC_DEBUG
> +#define PFX "CRYPTO4XX: "
> +
> +#if !defined(PPC4XX_SEC_DEBUG)
> +# define CRYPTUTL_PRINTK(ll, fmt, ...)
> +#else
> +# define CRYPTUTL_PRINTK(ll, fmt, ...) \
> + do { \
> + printk(ll PFX fmt "\n", ##__VA_ARGS__); \
> +} while (0);
> +#endif

there already exists a debug print infrastructure for device drivers,
e.g. dev_err, dev_dbg, dev_info..

> +#define PPC4XX_INT_DESCR_CNT 4
> +#define PPC4XX_INT_TIMEOUT_CNT 0
> +/* FIXme arbitory number*/

?

> +#define PPC4XX_INT_CFG 1
> +
> +#ifdef CONFIG_405EX
> +#define SDR0_SRST0 0x00000200
> +#define SRST0_CRYP 0x00000008
> +#else
> +#define SDR0_SRST0 0x00000201
> +#define SRST0_CRYP 0x08000000
> +#endif

4xx should be multiplatform-capable these days (if not, it will be).
The driver should be able to figure out whether it's running on a 405EX
dynamically.

> +struct crypto4xx_device;
> +extern struct crypto4xx_core_device lsec_core;
> +extern struct crypto_alg crypto4xx_basic_alg[];
> +extern u32 crypto4xx_write32(u32 reg, u32 val);
> +extern u32 crypto4xx_read32(u32 reg, u32 *val);
> +extern u32 crypto4xx_get_pd_from_pdr(struct crypto4xx_device *dev);
> +extern u32 crypto4xx_put_pd_to_pdr(struct crypto4xx_device *dev, u32 idx);
> +
> +extern struct ce_pd *crypto4xx_get_pdp(struct crypto4xx_device *dev,
> + dma_addr_t *pd_dma, u32 idx);
> +extern struct ce_gd *crypto4xx_get_gdp(struct crypto4xx_device *dev,
> + dma_addr_t *gd_dma, u32 idx);
> +extern struct ce_sd *crypto4xx_get_sdp(struct crypto4xx_device *dev,
> + dma_addr_t *sd_dma, u32 idx);
> +extern void crypto4xx_alloc_sa(struct crypto4xx_ctx *ctx, u32 size);
> +extern u32 crypto4xx_alloc_sa_rctx(struct crypto4xx_ctx *ctx,
> + struct crypto4xx_ctx *rctx);
> +
> +
> +extern struct crypto4xx_ctx *crypto4xx_alloc_ctx
> + (struct crypto4xx_ctx *ctx);
> +
> +extern void crypto4xx_free_ctx(struct crypto4xx_ctx *ctx);
> +extern u32 crypto4xx_build_pdr(struct crypto4xx_device *dev);
> +extern u32 crypto4xx_build_gdr(struct crypto4xx_device *dev);
> +extern u32 crypto4xx_build_sdr(struct crypto4xx_device *dev);
> +
> +extern u32 crypto4xx_pd_done(struct crypto4xx_core_device *lsec, u32 idx);
> +
> +extern u32 crypto4xx_start_device(struct crypto4xx_device *dev);
> +
> +extern u32 crypto4xx_stop_all(void);
> +extern u32 crypto4xx_config_clear(void);
> +
> +extern void crypto4xx_free_sa(struct crypto4xx_ctx *ctx);
> +extern u32 crypto4xx_alloc_state_record(struct crypto4xx_ctx *ctx);
> +extern void crypto4xx_free_state_record(struct crypto4xx_ctx *ctx);
> +
> +extern u32 get_dynamic_sa_offset_state_ptr_field(struct crypto4xx_ctx *ctx);
> +extern u32 get_dynamic_sa_offset_iv_field(struct crypto4xx_ctx *ctx);
> +extern void dump_dynamic_sa_iv_field(struct crypto4xx_ctx *ctx);
> +extern u32 get_dynamic_sa_iv_size(struct crypto4xx_ctx *ctx);
> +
> +
> +extern void crypto4xx_memcpy_be(unsigned int *dst,
> + const unsigned char *buf, int len);
> +extern void crypto4xx_memcpy_le(unsigned int *dst,
> + const unsigned char *buf, int len);
> +extern int crypto4xx_setup_crypto(struct crypto_async_request *req);
> +
> +extern u32 crypto4xx_check_pd_done(struct crypto4xx_device *dev);
> +extern void crypto4xx_alg_exit(struct crypto_tfm *tfm);
> +extern void crypto4xx_free_sa_rctx(struct crypto4xx_ctx *rctx);
> +extern int crypto4xx_handle_req(struct crypto_async_request *req);
> +extern u32 crypto4xx_build_pd(struct crypto4xx_device *dev,
> + struct crypto_async_request *req,
> + u32 pd_entry,
> + struct crypto4xx_ctx *ctx,
> + struct scatterlist *src,
> + struct scatterlist *dst,
> + u16 datalen,
> + u8 type);

please only use declarations if you have a cyclic dependency in a c file.

also, and probably most importantly, I didn't see any locking code in
this driver - how do you enforce mutually exclusive access to the
device?

Kim