diff options
author | Grant Likely <grant.likely@secretlab.ca> | 2010-05-22 00:36:56 -0600 |
---|---|---|
committer | Grant Likely <grant.likely@secretlab.ca> | 2010-05-22 00:36:56 -0600 |
commit | cf9b59e9d3e008591d1f54830f570982bb307a0d (patch) | |
tree | 113478ce8fd8c832ba726ffdf59b82cb46356476 /drivers/crypto | |
parent | 44504b2bebf8b5823c59484e73096a7d6574471d (diff) | |
parent | f4b87dee923342505e1ddba8d34ce9de33e75050 (diff) |
Merge remote branch 'origin' into secretlab/next-devicetree
Merging in current state of Linus' tree to deal with merge conflicts and
build failures in vio.c after merge.
Conflicts:
drivers/i2c/busses/i2c-cpm.c
drivers/i2c/busses/i2c-mpc.c
drivers/net/gianfar.c
Also fixed up one line in arch/powerpc/kernel/vio.c to use the
correct node pointer.
Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
Diffstat (limited to 'drivers/crypto')
-rw-r--r-- | drivers/crypto/Kconfig | 21 | ||||
-rw-r--r-- | drivers/crypto/Makefile | 4 | ||||
-rw-r--r-- | drivers/crypto/geode-aes.c | 36 | ||||
-rw-r--r-- | drivers/crypto/hifn_795x.c | 18 | ||||
-rw-r--r-- | drivers/crypto/mv_cesa.c | 692 | ||||
-rw-r--r-- | drivers/crypto/mv_cesa.h | 40 | ||||
-rw-r--r-- | drivers/crypto/n2_asm.S | 95 | ||||
-rw-r--r-- | drivers/crypto/n2_core.c | 2083 | ||||
-rw-r--r-- | drivers/crypto/n2_core.h | 231 | ||||
-rw-r--r-- | drivers/crypto/omap-sham.c | 1259 | ||||
-rw-r--r-- | drivers/crypto/talitos.c | 699 | ||||
-rw-r--r-- | drivers/crypto/talitos.h | 12 |
12 files changed, 5039 insertions, 151 deletions
diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig index b08403d7d1c..fbf94cf496f 100644 --- a/drivers/crypto/Kconfig +++ b/drivers/crypto/Kconfig @@ -170,6 +170,18 @@ config CRYPTO_DEV_MV_CESA Currently the driver supports AES in ECB and CBC mode without DMA. +config CRYPTO_DEV_NIAGARA2 + tristate "Niagara2 Stream Processing Unit driver" + select CRYPTO_ALGAPI + depends on SPARC64 + help + Each core of a Niagara2 processor contains a Stream + Processing Unit, which itself contains several cryptographic + sub-units. One set provides the Modular Arithmetic Unit, + used for SSL offload. The other set provides the Cipher + Group, which can perform encryption, decryption, hashing, + checksumming, and raw copies. + config CRYPTO_DEV_HIFN_795X tristate "Driver HIFN 795x crypto accelerator chips" select CRYPTO_DES @@ -222,4 +234,13 @@ config CRYPTO_DEV_PPC4XX help This option allows you to have support for AMCC crypto acceleration. +config CRYPTO_DEV_OMAP_SHAM + tristate "Support for OMAP SHA1/MD5 hw accelerator" + depends on ARCH_OMAP2 || ARCH_OMAP3 + select CRYPTO_SHA1 + select CRYPTO_MD5 + help + OMAP processors have SHA1/MD5 hw accelerator. Select this if you + want to use the OMAP module for SHA1/MD5 algorithms. + endif # CRYPTO_HW diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile index 6ffcb3f7f94..6dbbe00c452 100644 --- a/drivers/crypto/Makefile +++ b/drivers/crypto/Makefile @@ -1,8 +1,12 @@ obj-$(CONFIG_CRYPTO_DEV_PADLOCK_AES) += padlock-aes.o obj-$(CONFIG_CRYPTO_DEV_PADLOCK_SHA) += padlock-sha.o obj-$(CONFIG_CRYPTO_DEV_GEODE) += geode-aes.o +obj-$(CONFIG_CRYPTO_DEV_NIAGARA2) += n2_crypto.o +n2_crypto-objs := n2_core.o n2_asm.o obj-$(CONFIG_CRYPTO_DEV_HIFN_795X) += hifn_795x.o obj-$(CONFIG_CRYPTO_DEV_MV_CESA) += mv_cesa.o obj-$(CONFIG_CRYPTO_DEV_TALITOS) += talitos.o obj-$(CONFIG_CRYPTO_DEV_IXP4XX) += ixp4xx_crypto.o obj-$(CONFIG_CRYPTO_DEV_PPC4XX) += amcc/ +obj-$(CONFIG_CRYPTO_DEV_OMAP_SHAM) += omap-sham.o + diff --git a/drivers/crypto/geode-aes.c b/drivers/crypto/geode-aes.c index c7a5a43ba69..09389dd2f96 100644 --- a/drivers/crypto/geode-aes.c +++ b/drivers/crypto/geode-aes.c @@ -15,14 +15,14 @@ #include <crypto/algapi.h> #include <crypto/aes.h> -#include <asm/io.h> -#include <asm/delay.h> +#include <linux/io.h> +#include <linux/delay.h> #include "geode-aes.h" /* Static structures */ -static void __iomem * _iobase; +static void __iomem *_iobase; static spinlock_t lock; /* Write a 128 bit field (either a writable key or IV) */ @@ -30,7 +30,7 @@ static inline void _writefield(u32 offset, void *value) { int i; - for(i = 0; i < 4; i++) + for (i = 0; i < 4; i++) iowrite32(((u32 *) value)[i], _iobase + offset + (i * 4)); } @@ -39,7 +39,7 @@ static inline void _readfield(u32 offset, void *value) { int i; - for(i = 0; i < 4; i++) + for (i = 0; i < 4; i++) ((u32 *) value)[i] = ioread32(_iobase + offset + (i * 4)); } @@ -59,7 +59,7 @@ do_crypt(void *src, void *dst, int len, u32 flags) do { status = ioread32(_iobase + AES_INTR_REG); cpu_relax(); - } while(!(status & AES_INTRA_PENDING) && --counter); + } while (!(status & AES_INTRA_PENDING) && --counter); /* Clear the event */ iowrite32((status & 0xFF) | AES_INTRA_PENDING, _iobase + AES_INTR_REG); @@ -317,7 +317,7 @@ geode_cbc_decrypt(struct blkcipher_desc *desc, err = blkcipher_walk_virt(desc, &walk); op->iv = walk.iv; - while((nbytes = walk.nbytes)) { + while ((nbytes = walk.nbytes)) { op->src = walk.src.virt.addr, op->dst = walk.dst.virt.addr; op->mode = AES_MODE_CBC; @@ -349,7 +349,7 @@ geode_cbc_encrypt(struct blkcipher_desc *desc, err = blkcipher_walk_virt(desc, &walk); op->iv = walk.iv; - while((nbytes = walk.nbytes)) { + while ((nbytes = walk.nbytes)) { op->src = walk.src.virt.addr, op->dst = walk.dst.virt.addr; op->mode = AES_MODE_CBC; @@ -429,7 +429,7 @@ geode_ecb_decrypt(struct blkcipher_desc *desc, blkcipher_walk_init(&walk, dst, src, nbytes); err = blkcipher_walk_virt(desc, &walk); - while((nbytes = walk.nbytes)) { + while ((nbytes = walk.nbytes)) { op->src = walk.src.virt.addr, op->dst = walk.dst.virt.addr; op->mode = AES_MODE_ECB; @@ -459,7 +459,7 @@ geode_ecb_encrypt(struct blkcipher_desc *desc, blkcipher_walk_init(&walk, dst, src, nbytes); err = blkcipher_walk_virt(desc, &walk); - while((nbytes = walk.nbytes)) { + while ((nbytes = walk.nbytes)) { op->src = walk.src.virt.addr, op->dst = walk.dst.virt.addr; op->mode = AES_MODE_ECB; @@ -518,11 +518,12 @@ static int __devinit geode_aes_probe(struct pci_dev *dev, const struct pci_device_id *id) { int ret; - - if ((ret = pci_enable_device(dev))) + ret = pci_enable_device(dev); + if (ret) return ret; - if ((ret = pci_request_regions(dev, "geode-aes"))) + ret = pci_request_regions(dev, "geode-aes"); + if (ret) goto eenable; _iobase = pci_iomap(dev, 0, 0); @@ -537,13 +538,16 @@ geode_aes_probe(struct pci_dev *dev, const struct pci_device_id *id) /* Clear any pending activity */ iowrite32(AES_INTR_PENDING | AES_INTR_MASK, _iobase + AES_INTR_REG); - if ((ret = crypto_register_alg(&geode_alg))) + ret = crypto_register_alg(&geode_alg); + if (ret) goto eiomap; - if ((ret = crypto_register_alg(&geode_ecb_alg))) + ret = crypto_register_alg(&geode_ecb_alg); + if (ret) goto ealg; - if ((ret = crypto_register_alg(&geode_cbc_alg))) + ret = crypto_register_alg(&geode_cbc_alg); + if (ret) goto eecb; printk(KERN_NOTICE "geode-aes: GEODE AES engine enabled.\n"); diff --git a/drivers/crypto/hifn_795x.c b/drivers/crypto/hifn_795x.c index 73e8b1713b5..16fce3aadf4 100644 --- a/drivers/crypto/hifn_795x.c +++ b/drivers/crypto/hifn_795x.c @@ -638,7 +638,7 @@ struct hifn_crypto_alg #define ASYNC_FLAGS_MISALIGNED (1<<0) -struct ablkcipher_walk +struct hifn_cipher_walk { struct scatterlist cache[ASYNC_SCATTERLIST_CACHE]; u32 flags; @@ -657,7 +657,7 @@ struct hifn_request_context u8 *iv; unsigned int ivsize; u8 op, type, mode, unused; - struct ablkcipher_walk walk; + struct hifn_cipher_walk walk; }; #define crypto_alg_to_hifn(a) container_of(a, struct hifn_crypto_alg, alg) @@ -1417,7 +1417,7 @@ static int hifn_setup_dma(struct hifn_device *dev, return 0; } -static int ablkcipher_walk_init(struct ablkcipher_walk *w, +static int hifn_cipher_walk_init(struct hifn_cipher_walk *w, int num, gfp_t gfp_flags) { int i; @@ -1442,7 +1442,7 @@ static int ablkcipher_walk_init(struct ablkcipher_walk *w, return i; } -static void ablkcipher_walk_exit(struct ablkcipher_walk *w) +static void hifn_cipher_walk_exit(struct hifn_cipher_walk *w) { int i; @@ -1486,8 +1486,8 @@ static int ablkcipher_add(unsigned int *drestp, struct scatterlist *dst, return idx; } -static int ablkcipher_walk(struct ablkcipher_request *req, - struct ablkcipher_walk *w) +static int hifn_cipher_walk(struct ablkcipher_request *req, + struct hifn_cipher_walk *w) { struct scatterlist *dst, *t; unsigned int nbytes = req->nbytes, offset, copy, diff; @@ -1600,12 +1600,12 @@ static int hifn_setup_session(struct ablkcipher_request *req) } if (rctx->walk.flags & ASYNC_FLAGS_MISALIGNED) { - err = ablkcipher_walk_init(&rctx->walk, idx, GFP_ATOMIC); + err = hifn_cipher_walk_init(&rctx->walk, idx, GFP_ATOMIC); if (err < 0) return err; } - sg_num = ablkcipher_walk(req, &rctx->walk); + sg_num = hifn_cipher_walk(req, &rctx->walk); if (sg_num < 0) { err = sg_num; goto err_out_exit; @@ -1806,7 +1806,7 @@ static void hifn_process_ready(struct ablkcipher_request *req, int error) kunmap_atomic(saddr, KM_SOFTIRQ0); } - ablkcipher_walk_exit(&rctx->walk); + hifn_cipher_walk_exit(&rctx->walk); } req->base.complete(&req->base, error); diff --git a/drivers/crypto/mv_cesa.c b/drivers/crypto/mv_cesa.c index 6f29012bcc4..e095422b58d 100644 --- a/drivers/crypto/mv_cesa.c +++ b/drivers/crypto/mv_cesa.c @@ -15,8 +15,14 @@ #include <linux/platform_device.h> #include <linux/scatterlist.h> #include <linux/slab.h> +#include <crypto/internal/hash.h> +#include <crypto/sha.h> #include "mv_cesa.h" + +#define MV_CESA "MV-CESA:" +#define MAX_HW_HASH_SIZE 0xFFFF + /* * STM: * /---------------------------------------\ @@ -39,10 +45,12 @@ enum engine_status { * @dst_sg_it: sg iterator for dst * @sg_src_left: bytes left in src to process (scatter list) * @src_start: offset to add to src start position (scatter list) - * @crypt_len: length of current crypt process + * @crypt_len: length of current hw crypt/hash process + * @hw_nbytes: total bytes to process in hw for this request + * @copy_back: whether to copy data back (crypt) or not (hash) * @sg_dst_left: bytes left dst to process in this scatter list * @dst_start: offset to add to dst start position (scatter list) - * @total_req_bytes: total number of bytes processed (request). + * @hw_processed_bytes: number of bytes processed by hw (request). * * sg helper are used to iterate over the scatterlist. Since the size of the * SRAM may be less than the scatter size, this struct struct is used to keep @@ -51,15 +59,19 @@ enum engine_status { struct req_progress { struct sg_mapping_iter src_sg_it; struct sg_mapping_iter dst_sg_it; + void (*complete) (void); + void (*process) (int is_first); /* src mostly */ int sg_src_left; int src_start; int crypt_len; + int hw_nbytes; /* dst mostly */ + int copy_back; int sg_dst_left; int dst_start; - int total_req_bytes; + int hw_processed_bytes; }; struct crypto_priv { @@ -72,10 +84,12 @@ struct crypto_priv { spinlock_t lock; struct crypto_queue queue; enum engine_status eng_st; - struct ablkcipher_request *cur_req; + struct crypto_async_request *cur_req; struct req_progress p; int max_req_size; int sram_size; + int has_sha1; + int has_hmac_sha1; }; static struct crypto_priv *cpg; @@ -97,6 +111,31 @@ struct mv_req_ctx { int decrypt; }; +enum hash_op { + COP_SHA1, + COP_HMAC_SHA1 +}; + +struct mv_tfm_hash_ctx { + struct crypto_shash *fallback; + struct crypto_shash *base_hash; + u32 ivs[2 * SHA1_DIGEST_SIZE / 4]; + int count_add; + enum hash_op op; +}; + +struct mv_req_hash_ctx { + u64 count; + u32 state[SHA1_DIGEST_SIZE / 4]; + u8 buffer[SHA1_BLOCK_SIZE]; + int first_hash; /* marks that we don't have previous state */ + int last_chunk; /* marks that this is the 'final' request */ + int extra_bytes; /* unprocessed bytes in buffer */ + enum hash_op op; + int count_add; + struct scatterlist dummysg; +}; + static void compute_aes_dec_key(struct mv_ctx *ctx) { struct crypto_aes_ctx gen_aes_key; @@ -144,32 +183,51 @@ static int mv_setkey_aes(struct crypto_ablkcipher *cipher, const u8 *key, return 0; } -static void setup_data_in(struct ablkcipher_request *req) +static void copy_src_to_buf(struct req_progress *p, char *dbuf, int len) { int ret; - void *buf; + void *sbuf; + int copied = 0; - if (!cpg->p.sg_src_left) { - ret = sg_miter_next(&cpg->p.src_sg_it); - BUG_ON(!ret); - cpg->p.sg_src_left = cpg->p.src_sg_it.length; - cpg->p.src_start = 0; - } - - cpg->p.crypt_len = min(cpg->p.sg_src_left, cpg->max_req_size); - - buf = cpg->p.src_sg_it.addr; - buf += cpg->p.src_start; + while (1) { + if (!p->sg_src_left) { + ret = sg_miter_next(&p->src_sg_it); + BUG_ON(!ret); + p->sg_src_left = p->src_sg_it.length; + p->src_start = 0; + } - memcpy(cpg->sram + SRAM_DATA_IN_START, buf, cpg->p.crypt_len); + sbuf = p->src_sg_it.addr + p->src_start; + + if (p->sg_src_left <= len - copied) { + memcpy(dbuf + copied, sbuf, p->sg_src_left); + copied += p->sg_src_left; + p->sg_src_left = 0; + if (copied >= len) + break; + } else { + int copy_len = len - copied; + memcpy(dbuf + copied, sbuf, copy_len); + p->src_start += copy_len; + p->sg_src_left -= copy_len; + break; + } + } +} - cpg->p.sg_src_left -= cpg->p.crypt_len; - cpg->p.src_start += cpg->p.crypt_len; +static void setup_data_in(void) +{ + struct req_progress *p = &cpg->p; + int data_in_sram = + min(p->hw_nbytes - p->hw_processed_bytes, cpg->max_req_size); + copy_src_to_buf(p, cpg->sram + SRAM_DATA_IN_START + p->crypt_len, + data_in_sram - p->crypt_len); + p->crypt_len = data_in_sram; } static void mv_process_current_q(int first_block) { - struct ablkcipher_request *req = cpg->cur_req; + struct ablkcipher_request *req = ablkcipher_request_cast(cpg->cur_req); struct mv_ctx *ctx = crypto_tfm_ctx(req->base.tfm); struct mv_req_ctx *req_ctx = ablkcipher_request_ctx(req); struct sec_accel_config op; @@ -179,6 +237,7 @@ static void mv_process_current_q(int first_block) op.config = CFG_OP_CRYPT_ONLY | CFG_ENCM_AES | CFG_ENC_MODE_ECB; break; case COP_AES_CBC: + default: op.config = CFG_OP_CRYPT_ONLY | CFG_ENCM_AES | CFG_ENC_MODE_CBC; op.enc_iv = ENC_IV_POINT(SRAM_DATA_IV) | ENC_IV_BUF_POINT(SRAM_DATA_IV_BUF); @@ -211,7 +270,7 @@ static void mv_process_current_q(int first_block) ENC_P_DST(SRAM_DATA_OUT_START); op.enc_key_p = SRAM_DATA_KEY_P; - setup_data_in(req); + setup_data_in(); op.enc_len = cpg->p.crypt_len; memcpy(cpg->sram + SRAM_CONFIG, &op, sizeof(struct sec_accel_config)); @@ -228,91 +287,294 @@ static void mv_process_current_q(int first_block) static void mv_crypto_algo_completion(void) { - struct ablkcipher_request *req = cpg->cur_req; + struct ablkcipher_request *req = ablkcipher_request_cast(cpg->cur_req); struct mv_req_ctx *req_ctx = ablkcipher_request_ctx(req); + sg_miter_stop(&cpg->p.src_sg_it); + sg_miter_stop(&cpg->p.dst_sg_it); + if (req_ctx->op != COP_AES_CBC) return ; memcpy(req->info, cpg->sram + SRAM_DATA_IV_BUF, 16); } +static void mv_process_hash_current(int first_block) +{ + struct ahash_request *req = ahash_request_cast(cpg->cur_req); + struct mv_req_hash_ctx *req_ctx = ahash_request_ctx(req); + struct req_progress *p = &cpg->p; + struct sec_accel_config op = { 0 }; + int is_last; + + switch (req_ctx->op) { + case COP_SHA1: + default: + op.config = CFG_OP_MAC_ONLY | CFG_MACM_SHA1; + break; + case COP_HMAC_SHA1: + op.config = CFG_OP_MAC_ONLY | CFG_MACM_HMAC_SHA1; + break; + } + + op.mac_src_p = + MAC_SRC_DATA_P(SRAM_DATA_IN_START) | MAC_SRC_TOTAL_LEN((u32) + req_ctx-> + count); + + setup_data_in(); + + op.mac_digest = + MAC_DIGEST_P(SRAM_DIGEST_BUF) | MAC_FRAG_LEN(p->crypt_len); + op.mac_iv = + MAC_INNER_IV_P(SRAM_HMAC_IV_IN) | + MAC_OUTER_IV_P(SRAM_HMAC_IV_OUT); + + is_last = req_ctx->last_chunk + && (p->hw_processed_bytes + p->crypt_len >= p->hw_nbytes) + && (req_ctx->count <= MAX_HW_HASH_SIZE); + if (req_ctx->first_hash) { + if (is_last) + op.config |= CFG_NOT_FRAG; + else + op.config |= CFG_FIRST_FRAG; + + req_ctx->first_hash = 0; + } else { + if (is_last) + op.config |= CFG_LAST_FRAG; + else + op.config |= CFG_MID_FRAG; + } + + memcpy(cpg->sram + SRAM_CONFIG, &op, sizeof(struct sec_accel_config)); + + writel(SRAM_CONFIG, cpg->reg + SEC_ACCEL_DESC_P0); + /* GO */ + writel(SEC_CMD_EN_SEC_ACCL0, cpg->reg + SEC_ACCEL_CMD); + + /* + * XXX: add timer if the interrupt does not occur for some mystery + * reason + */ +} + +static inline int mv_hash_import_sha1_ctx(const struct mv_req_hash_ctx *ctx, + struct shash_desc *desc) +{ + int i; + struct sha1_state shash_state; + + shash_state.count = ctx->count + ctx->count_add; + for (i = 0; i < 5; i++) + shash_state.state[i] = ctx->state[i]; + memcpy(shash_state.buffer, ctx->buffer, sizeof(shash_state.buffer)); + return crypto_shash_import(desc, &shash_state); +} + +static int mv_hash_final_fallback(struct ahash_request *req) +{ + const struct mv_tfm_hash_ctx *tfm_ctx = crypto_tfm_ctx(req->base.tfm); + struct mv_req_hash_ctx *req_ctx = ahash_request_ctx(req); + struct { + struct shash_desc shash; + char ctx[crypto_shash_descsize(tfm_ctx->fallback)]; + } desc; + int rc; + + desc.shash.tfm = tfm_ctx->fallback; + desc.shash.flags = CRYPTO_TFM_REQ_MAY_SLEEP; + if (unlikely(req_ctx->first_hash)) { + crypto_shash_init(&desc.shash); + crypto_shash_update(&desc.shash, req_ctx->buffer, + req_ctx->extra_bytes); + } else { + /* only SHA1 for now.... + */ + rc = mv_hash_import_sha1_ctx(req_ctx, &desc.shash); + if (rc) + goto out; + } + rc = crypto_shash_final(&desc.shash, req->result); +out: + return rc; +} + +static void mv_hash_algo_completion(void) +{ + struct ahash_request *req = ahash_request_cast(cpg->cur_req); + struct mv_req_hash_ctx *ctx = ahash_request_ctx(req); + + if (ctx->extra_bytes) + copy_src_to_buf(&cpg->p, ctx->buffer, ctx->extra_bytes); + sg_miter_stop(&cpg->p.src_sg_it); + + ctx->state[0] = readl(cpg->reg + DIGEST_INITIAL_VAL_A); + ctx->state[1] = readl(cpg->reg + DIGEST_INITIAL_VAL_B); + ctx->state[2] = readl(cpg->reg + DIGEST_INITIAL_VAL_C); + ctx->state[3] = readl(cpg->reg + DIGEST_INITIAL_VAL_D); + ctx->state[4] = readl(cpg->reg + DIGEST_INITIAL_VAL_E); + + if (likely(ctx->last_chunk)) { + if (likely(ctx->count <= MAX_HW_HASH_SIZE)) { + memcpy(req->result, cpg->sram + SRAM_DIGEST_BUF, + crypto_ahash_digestsize(crypto_ahash_reqtfm + (req))); + } else + mv_hash_final_fallback(req); + } +} + static void dequeue_complete_req(void) { - struct ablkcipher_request *req = cpg->cur_req; + struct crypto_async_request *req = cpg->cur_req; void *buf; int ret; + cpg->p.hw_processed_bytes += cpg->p.crypt_len; + if (cpg->p.copy_back) { + int need_copy_len = cpg->p.crypt_len; + int sram_offset = 0; + do { + int dst_copy; + + if (!cpg->p.sg_dst_left) { + ret = sg_miter_next(&cpg->p.dst_sg_it); + BUG_ON(!ret); + cpg->p.sg_dst_left = cpg->p.dst_sg_it.length; + cpg->p.dst_start = 0; + } - cpg->p.total_req_bytes += cpg->p.crypt_len; - do { - int dst_copy; - - if (!cpg->p.sg_dst_left) { - ret = sg_miter_next(&cpg->p.dst_sg_it); - BUG_ON(!ret); - cpg->p.sg_dst_left = cpg->p.dst_sg_it.length; - cpg->p.dst_start = 0; - } - - buf = cpg->p.dst_sg_it.addr; - buf += cpg->p.dst_start; + buf = cpg->p.dst_sg_it.addr; + buf += cpg->p.dst_start; - dst_copy = min(cpg->p.crypt_len, cpg->p.sg_dst_left); + dst_copy = min(need_copy_len, cpg->p.sg_dst_left); - memcpy(buf, cpg->sram + SRAM_DATA_OUT_START, dst_copy); + memcpy(buf, + cpg->sram + SRAM_DATA_OUT_START + sram_offset, + dst_copy); + sram_offset += dst_copy; + cpg->p.sg_dst_left -= dst_copy; + need_copy_len -= dst_copy; + cpg->p.dst_start += dst_copy; + } while (need_copy_len > 0); + } - cpg->p.sg_dst_left -= dst_copy; - cpg->p.crypt_len -= dst_copy; - cpg->p.dst_start += dst_copy; - } while (cpg->p.crypt_len > 0); + cpg->p.crypt_len = 0; BUG_ON(cpg->eng_st != ENGINE_W_DEQUEUE); - if (cpg->p.total_req_bytes < req->nbytes) { + if (cpg->p.hw_processed_bytes < cpg->p.hw_nbytes) { /* process next scatter list entry */ cpg->eng_st = ENGINE_BUSY; - mv_process_current_q(0); + cpg->p.process(0); } else { - sg_miter_stop(&cpg->p.src_sg_it); - sg_miter_stop(&cpg->p.dst_sg_it); - mv_crypto_algo_completion(); + cpg->p.complete(); cpg->eng_st = ENGINE_IDLE; - req->base.complete(&req->base, 0); + local_bh_disable(); + req->complete(req, 0); + local_bh_enable(); } } static int count_sgs(struct scatterlist *sl, unsigned int total_bytes) { int i = 0; - - do { - total_bytes -= sl[i].length; - i++; - - } while (total_bytes > 0); + size_t cur_len; + + while (1) { + cur_len = sl[i].length; + ++i; + if (total_bytes > cur_len) + total_bytes -= cur_len; + else + break; + } return i; } -static void mv_enqueue_new_req(struct ablkcipher_request *req) +static void mv_start_new_crypt_req(struct ablkcipher_request *req) { + struct req_progress *p = &cpg->p; int num_sgs; - cpg->cur_req = req; - memset(&cpg->p, 0, sizeof(struct req_progress)); + cpg->cur_req = &req->base; + memset(p, 0, sizeof(struct req_progress)); + p->hw_nbytes = req->nbytes; + p->complete = mv_crypto_algo_completion; + p->process = mv_process_current_q; + p->copy_back = 1; num_sgs = count_sgs(req->src, req->nbytes); - sg_miter_start(&cpg->p.src_sg_it, req->src, num_sgs, SG_MITER_FROM_SG); + sg_miter_start(&p->src_sg_it, req->src, num_sgs, SG_MITER_FROM_SG); num_sgs = count_sgs(req->dst, req->nbytes); - sg_miter_start(&cpg->p.dst_sg_it, req->dst, num_sgs, SG_MITER_TO_SG); + sg_miter_start(&p->dst_sg_it, req->dst, num_sgs, SG_MITER_TO_SG); + mv_process_current_q(1); } +static void mv_start_new_hash_req(struct ahash_request *req) +{ + struct req_progress *p = &cpg->p; + struct mv_req_hash_ctx *ctx = ahash_request_ctx(req); + const struct mv_tfm_hash_ctx *tfm_ctx = crypto_tfm_ctx(req->base.tfm); + int num_sgs, hw_bytes, old_extra_bytes, rc; + cpg->cur_req = &req->base; + memset(p, 0, sizeof(struct req_progress)); + hw_bytes = req->nbytes + ctx->extra_bytes; + old_extra_bytes = ctx->extra_bytes; + + if (unlikely(ctx->extra_bytes)) { + memcpy(cpg->sram + SRAM_DATA_IN_START, ctx->buffer, + ctx->extra_bytes); + p->crypt_len = ctx->extra_bytes; + } + + memcpy(cpg->sram + SRAM_HMAC_IV_IN, tfm_ctx->ivs, sizeof(tfm_ctx->ivs)); + + if (unlikely(!ctx->first_hash)) { + writel(ctx->state[0], cpg->reg + DIGEST_INITIAL_VAL_A); + writel(ctx->state[1], cpg->reg + DIGEST_INITIAL_VAL_B); + writel(ctx->state[2], cpg->reg + DIGEST_INITIAL_VAL_C); + writel(ctx->state[3], cpg->reg + DIGEST_INITIAL_VAL_D); + writel(ctx->state[4], cpg->reg + DIGEST_INITIAL_VAL_E); + } + + ctx->extra_bytes = hw_bytes % SHA1_BLOCK_SIZE; + if (ctx->extra_bytes != 0 + && (!ctx->last_chunk || ctx->count > MAX_HW_HASH_SIZE)) + hw_bytes -= ctx->extra_bytes; + else + ctx->extra_bytes = 0; + + num_sgs = count_sgs(req->src, req->nbytes); + sg_miter_start(&p->src_sg_it, req->src, num_sgs, SG_MITER_FROM_SG); + + if (hw_bytes) { + p->hw_nbytes = hw_bytes; + p->complete = mv_hash_algo_completion; + p->process = mv_process_hash_current; + + mv_process_hash_current(1); + } else { + copy_src_to_buf(p, ctx->buffer + old_extra_bytes, + ctx->extra_bytes - old_extra_bytes); + sg_miter_stop(&p->src_sg_it); + if (ctx->last_chunk) + rc = mv_hash_final_fallback(req); + else + rc = 0; + cpg->eng_st = ENGINE_IDLE; + local_bh_disable(); + req->base.complete(&req->base, rc); + local_bh_enable(); + } +} + static int queue_manag(void *data) { cpg->eng_st = ENGINE_IDLE; do { - struct ablkcipher_request *req; struct crypto_async_request *async_req = NULL; struct crypto_async_request *backlog; @@ -338,9 +600,18 @@ static int queue_manag(void *data) } if (async_req) { - req = container_of(async_req, - struct ablkcipher_request, base); - mv_enqueue_new_req(req); + if (async_req->tfm->__crt_alg->cra_type != + &crypto_ahash_type) { + struct ablkcipher_request *req = + container_of(async_req, + struct ablkcipher_request, + base); + mv_start_new_crypt_req(req); + } else { + struct ahash_request *req = + ahash_request_cast(async_req); + mv_start_new_hash_req(req); + } async_req = NULL; } @@ -350,13 +621,13 @@ static int queue_manag(void *data) return 0; } -static int mv_handle_req(struct ablkcipher_request *req) +static int mv_handle_req(struct crypto_async_request *req) { unsigned long flags; int ret; spin_lock_irqsave(&cpg->lock, flags); - ret = ablkcipher_enqueue_request(&cpg->queue, req); + ret = crypto_enqueue_request(&cpg->queue, req); spin_unlock_irqrestore(&cpg->lock, flags); wake_up_process(cpg->queue_th); return ret; @@ -369,7 +640,7 @@ static int mv_enc_aes_ecb(struct ablkcipher_request *req) req_ctx->op = COP_AES_ECB; req_ctx->decrypt = 0; - return mv_handle_req(req); + return mv_handle_req(&req->base); } static int mv_dec_aes_ecb(struct ablkcipher_request *req) @@ -381,7 +652,7 @@ static int mv_dec_aes_ecb(struct ablkcipher_request *req) req_ctx->decrypt = 1; compute_aes_dec_key(ctx); - return mv_handle_req(req); + return mv_handle_req(&req->base); } static int mv_enc_aes_cbc(struct ablkcipher_request *req) @@ -391,7 +662,7 @@ static int mv_enc_aes_cbc(struct ablkcipher_request *req) req_ctx->op = COP_AES_CBC; req_ctx->decrypt = 0; - return mv_handle_req(req); + return mv_handle_req(&req->base); } static int mv_dec_aes_cbc(struct ablkcipher_request *req) @@ -403,7 +674,7 @@ static int mv_dec_aes_cbc(struct ablkcipher_request *req) req_ctx->decrypt = 1; compute_aes_dec_key(ctx); - return mv_handle_req(req); + return mv_handle_req(&req->base); } static int mv_cra_init(struct crypto_tfm *tfm) @@ -412,6 +683,215 @@ static int mv_cra_init(struct crypto_tfm *tfm) return 0; } +static void mv_init_hash_req_ctx(struct mv_req_hash_ctx *ctx, int op, + int is_last, unsigned int req_len, + int count_add) +{ + memset(ctx, 0, sizeof(*ctx)); + ctx->op = op; + ctx->count = req_len; + ctx->first_hash = 1; + ctx->last_chunk = is_last; + ctx->count_add = count_add; +} + +static void mv_update_hash_req_ctx(struct mv_req_hash_ctx *ctx, int is_last, + unsigned req_len) +{ + ctx->last_chunk = is_last; + ctx->count += req_len; +} + +static int mv_hash_init(struct ahash_request *req) +{ + const struct mv_tfm_hash_ctx *tfm_ctx = crypto_tfm_ctx(req->base.tfm); + mv_init_hash_req_ctx(ahash_request_ctx(req), tfm_ctx->op, 0, 0, + tfm_ctx->count_add); + return 0; +} + +static int mv_hash_update(struct ahash_request *req) +{ + if (!req->nbytes) + return 0; + + mv_update_hash_req_ctx(ahash_request_ctx(req), 0, req->nbytes); + return mv_handle_req(&req->base); +} + +static int mv_hash_final(struct ahash_request *req) +{ + struct mv_req_hash_ctx *ctx = ahash_request_ctx(req); + /* dummy buffer of 4 bytes */ + sg_init_one(&ctx->dummysg, ctx->buffer, 4); + /* I think I'm allowed to do that... */ + ahash_request_set_crypt(req, &ctx->dummysg, req->result, 0); + mv_update_hash_req_ctx(ctx, 1, 0); + return mv_handle_req(&req->base); +} + +static int mv_hash_finup(struct ahash_request *req) +{ + if (!req->nbytes) + return mv_hash_final(req); + + mv_update_hash_req_ctx(ahash_request_ctx(req), 1, req->nbytes); + return mv_handle_req(&req->base); +} + +static int mv_hash_digest(struct ahash_request *req) +{ + const struct mv_tfm_hash_ctx *tfm_ctx = crypto_tfm_ctx(req->base.tfm); + mv_init_hash_req_ctx(ahash_request_ctx(req), tfm_ctx->op, 1, + req->nbytes, tfm_ctx->count_add); + return mv_handle_req(&req->base); +} + +static void mv_hash_init_ivs(struct mv_tfm_hash_ctx *ctx, const void *istate, + const void *ostate) +{ + const struct sha1_state *isha1_state = istate, *osha1_state = ostate; + int i; + for (i = 0; i < 5; i++) { + ctx->ivs[i] = cpu_to_be32(isha1_state->state[i]); + ctx->ivs[i + 5] = cpu_to_be32(osha1_state->state[i]); + } +} + +static int mv_hash_setkey(struct crypto_ahash *tfm, const u8 * key, + unsigned int keylen) +{ + int rc; + struct mv_tfm_hash_ctx *ctx = crypto_tfm_ctx(&tfm->base); + int bs, ds, ss; + + if (!ctx->base_hash) + return 0; + + rc = crypto_shash_setkey(ctx->fallback, key, keylen); + if (rc) + return rc; + + /* Can't see a way to extract the ipad/opad from the fallback tfm + so I'm basically copying code from the hmac module */ + bs = crypto_shash_blocksize(ctx->base_hash); + ds = crypto_shash_digestsize(ctx->base_hash); + ss = crypto_shash_statesize(ctx->base_hash); + + { + struct { + struct shash_desc shash; + char ctx[crypto_shash_descsize(ctx->base_hash)]; + } desc; + unsigned int i; + char ipad[ss]; + char opad[ss]; + + desc.shash.tfm = ctx->base_hash; + desc.shash.flags = crypto_shash_get_flags(ctx->base_hash) & + CRYPTO_TFM_REQ_MAY_SLEEP; + + if (keylen > bs) { + int err; + + err = + crypto_shash_digest(&desc.shash, key, keylen, ipad); + if (err) + return err; + + keylen = ds; + } else + memcpy(ipad, key, keylen); + + memset(ipad + keylen, 0, bs - keylen); + memcpy(opad, ipad, bs); + + for (i = 0; i < bs; i++) { + ipad[i] ^= 0x36; + opad[i] ^= 0x5c; + } + + rc = crypto_shash_init(&desc.shash) ? : + crypto_shash_update(&desc.shash, ipad, bs) ? : + crypto_shash_export(&desc.shash, ipad) ? : + crypto_shash_init(&desc.shash) ? : + crypto_shash_update(&desc.shash, opad, bs) ? : + crypto_shash_export(&desc.shash, opad); + + if (rc == 0) + mv_hash_init_ivs(ctx, ipad, opad); + + return rc; + } +} + +static int mv_cra_hash_init(struct crypto_tfm *tfm, const char *base_hash_name, + enum hash_op op, int count_add) +{ + const char *fallback_driver_name = tfm->__crt_alg->cra_name; + struct mv_tfm_hash_ctx *ctx = crypto_tfm_ctx(tfm); + struct crypto_shash *fallback_tfm = NULL; + struct crypto_shash *base_hash = NULL; + int err = -ENOMEM; + + ctx->op = op; + ctx->count_add = count_add; + + /* Allocate a fallback and abort if it failed. */ + fallback_tfm = crypto_alloc_shash(fallback_driver_name, 0, + CRYPTO_ALG_NEED_FALLBACK); + if (IS_ERR(fallback_tfm)) { + printk(KERN_WARNING MV_CESA + "Fallback driver '%s' could not be loaded!\n", + fallback_driver_name); + err = PTR_ERR(fallback_tfm); + goto out; + } + ctx->fallback = fallback_tfm; |