From be1b40228141bf1d3a18f0d9a3cb46ba702c9842 Mon Sep 17 00:00:00 2001 From: LABBE Corentin Date: Thu, 22 Oct 2015 17:03:18 +0200 Subject: [PATCH] crypto: sun8i-ce Add Allwinner Crypto Engine cryptographic accelerator Add support for the Crypto Engine included in Allwinner SoC H3 and A64. The Crypto Engine is a hardware cryptographic accelerator that support: - MD5 and SHA1/SHA224/SHA25/SHA384/SHA512 hash algorithms - AES block cipher in CBC/ECB mode with 128/196/256bits keys. Signed-off-by: Corentin Labbe --- drivers/staging/sun8i-ss/Kconfig | 28 + drivers/staging/sun8i-ss/Makefile | 4 + drivers/staging/sun8i-ss/sun8i-ce-cipher.c | 365 +++++++++++ drivers/staging/sun8i-ss/sun8i-ce-core.c | 934 +++++++++++++++++++++++++++++ drivers/staging/sun8i-ss/sun8i-ce-hash.c | 907 ++++++++++++++++++++++++++++ drivers/staging/sun8i-ss/sun8i-ce-hwrng.c | 170 ++++++ drivers/staging/sun8i-ss/sun8i-ce-rsa.c | 351 +++++++++++ drivers/staging/sun8i-ss/sun8i-ss.h | 290 +++++++++ 8 files changed, 3049 insertions(+) create mode 100644 drivers/staging/sun8i-ss/Kconfig create mode 100644 drivers/staging/sun8i-ss/Makefile create mode 100644 drivers/staging/sun8i-ss/sun8i-ce-cipher.c create mode 100644 drivers/staging/sun8i-ss/sun8i-ce-core.c create mode 100644 drivers/staging/sun8i-ss/sun8i-ce-hash.c create mode 100644 drivers/staging/sun8i-ss/sun8i-ce-hwrng.c create mode 100644 drivers/staging/sun8i-ss/sun8i-ce-rsa.c create mode 100644 drivers/staging/sun8i-ss/sun8i-ss.h diff --git a/drivers/staging/sun8i-ss/Kconfig b/drivers/staging/sun8i-ss/Kconfig new file mode 100644 index 0000000..81dfbc8 --- /dev/null +++ b/drivers/staging/sun8i-ss/Kconfig @@ -0,0 +1,28 @@ +config CRYPTO_DEV_SUN8I_SS + tristate "Support for Allwinner Crypto Engine cryptographic accelerator" + select CRYPTO_BLKCIPHER + select CRYPTO_ENGINE + help + Select y here for having support for the crypto Engine availlable on + Allwinner SoC H3 and A64. + The Crypto Engine handle AES/DES/3DES ciphers in CBC mode + and MD5, SHA1, SHA224, SHA256, SHA384 and SHA512 hash algorithms. + + To compile this driver as a module, choose M here: the module + will be called sun8i-ss. + +config CRYPTO_DEV_SUN8I_SS_PRNG + bool "Support for sun8i Allwinner Security System PRNG" + depends on CRYPTO_DEV_SUN8I_SS + select HW_RANDOM + help + This driver provides kernel-side support for the Pseudo-Random + Number Generator found in the sun8i Security System. + +config CRYPTO_DEV_SUN8I_SS_RSA + bool "Support for sun8i Allwinner Security System RSA" + depends on CRYPTO_DEV_SUN8I_SS + select CRYPTO_RSA + help + This driver provides kernel-side support for the RSA TODO + found in the sun8i Security System. diff --git a/drivers/staging/sun8i-ss/Makefile b/drivers/staging/sun8i-ss/Makefile new file mode 100644 index 0000000..2991becd --- /dev/null +++ b/drivers/staging/sun8i-ss/Makefile @@ -0,0 +1,4 @@ +obj-$(CONFIG_CRYPTO_DEV_SUN8I_SS) += sun8i-ss.o +sun8i-ss-y += sun8i-ce-core.o sun8i-ce-hash.o sun8i-ce-cipher.o +sun8i-ss-$(CONFIG_CRYPTO_DEV_SUN8I_SS_PRNG) += sun8i-ce-hwrng.o +sun8i-ss-$(CONFIG_CRYPTO_DEV_SUN8I_SS_RSA) += sun8i-ce-rsa.o diff --git a/drivers/staging/sun8i-ss/sun8i-ce-cipher.c b/drivers/staging/sun8i-ss/sun8i-ce-cipher.c new file mode 100644 index 0000000..a7fabcc --- /dev/null +++ b/drivers/staging/sun8i-ss/sun8i-ce-cipher.c @@ -0,0 +1,365 @@ +/* + * sun8i-ce-cipher.c - hardware cryptographic accelerator for + * Allwinner H3/A64 SoC + * + * Copyright (C) 2016-2017 Corentin LABBE + * + * This file add support for AES cipher with 128,192,256 bits keysize in + * CBC and ECB mode. + * + * You could find a link for the datasheet in Documentation/arm/sunxi/README + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include "sun8i-ss.h" + +int sun8i_ss_cipher(struct ablkcipher_request *areq) +{ + struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(areq); + struct sun8i_tfm_ctx *op = crypto_ablkcipher_ctx(tfm); + struct sun8i_ss_ctx *ss = op->ss; + struct sun8i_cipher_req_ctx *rctx = ablkcipher_request_ctx(areq); + int flow = ss->flow; + struct ss_task *cet; + int nr_sgs, nr_sgd; + struct scatterlist *sg; + struct scatterlist *in_sg = areq->src; + struct scatterlist *out_sg = areq->dst; + int i; + int ret; + int no_chunk, chunked_src, chunked_dst; + int err = 0; + unsigned int todo, len; + struct crypto_alg *alg = tfm->base.__crt_alg; + /*struct crypto_alg *alg = &(crypto_ablkcipher_tfm(tfm))->__crt_alg;*/ + struct sun8i_ss_alg_template *algt; + + algt = container_of(alg, struct sun8i_ss_alg_template, alg.crypto); + + /*dev_info(ss->dev, "%s %u %x\n", __func__, areq->nbytes, rctx->common);*/ + + chunked_src = 1; + sg = areq->src; + while (sg && chunked_src == 1) { + if ((sg->length % 4) != 0) + chunked_src = 0; + if (!IS_ALIGNED(sg->offset, sizeof(u32))) { + /*dev_info(ss->dev, "Align problem on src\n");*/ + chunked_src = 0; + } + sg = sg_next(sg); + } + chunked_dst = 1; + sg = areq->dst; + while (sg && chunked_dst == 1) { + if ((sg->length % 4) != 0) + chunked_dst = 0; + if (!IS_ALIGNED(sg->offset, sizeof(u32))) { + /*dev_info(ss->dev, "Align problem on dst\n");*/ + chunked_dst = 0; + } + sg = sg_next(sg); + } + + /* on SS, src and dst SG must have the same len */ + + if (chunked_src == 0 || chunked_dst == 0 || sg_nents(in_sg) > 8) { + struct blkcipher_desc fallback_desc = { + .tfm = op->fallback_tfm, + .info = areq->info, + .flags = 0, + }; + if (rctx->op_dir & SS_DECRYPTION) + return crypto_blkcipher_decrypt_iv(&fallback_desc, + areq->dst, areq->src, areq->nbytes); + else + return crypto_blkcipher_encrypt_iv(&fallback_desc, + areq->dst, areq->src, areq->nbytes); + } + + flow = rctx->flow; + + mutex_lock(&ss->chanlock[flow]); + + cet = ss->tl[flow]; + memset(cet, 0, sizeof(struct ss_task)); + + cet->t_id = flow; + cet->t_common_ctl = ss->variant->alg_cipher[algt->ce_algo_id]; + cet->t_common_ctl = rctx->op_dir | BIT(31); + cet->t_dlen = areq->nbytes / 4; + + cet->t_sym_ctl = ss->variant->op_mode[algt->ce_blockmode]; + cet->t_sym_ctl |= op->keymode; + + ss->chanlist[flow].op_mode = ss->variant->op_mode[algt->ce_blockmode]; + ss->chanlist[flow].op_dir = rctx->op_dir; + ss->chanlist[flow].method = ss->variant->alg_cipher[algt->ce_algo_id]; + ss->chanlist[flow].keylen = op->keylen; + +/* + for (i = 0; i < 8; i++) { + cet->t_src[i].len = 0; + cet->t_dst[i].len = 0; + } + cet->next = 0; +*/ + cet->t_key = dma_map_single(ss->dev, op->key, op->keylen, + DMA_TO_DEVICE); + if (dma_mapping_error(ss->dev, cet->t_key)) { + dev_err(ss->dev, "Cannot DMA MAP KEY\n"); + err = -EFAULT; + goto theend; + } + + if (areq->info) { + ss->chanlist[flow].ivlen = crypto_ablkcipher_ivsize(tfm); + ss->chanlist[flow].bounce_iv = kzalloc(ss->chanlist[flow].ivlen, + GFP_KERNEL | GFP_DMA); + if (!ss->chanlist[flow].bounce_iv) { + err = -ENOMEM; + goto theend; + } + memcpy(ss->chanlist[flow].bounce_iv, areq->info, + crypto_ablkcipher_ivsize(tfm)); + ss->chanlist[flow].next_iv = kzalloc(ss->chanlist[flow].ivlen, + GFP_KERNEL | GFP_DMA); + if (!ss->chanlist[flow].next_iv) { + err = -ENOMEM; + goto theend; + } + } + + /* check for chunked SGs */ + no_chunk = 1; + sg = areq->src; + while (sg && no_chunk == 1) { + if ((sg->length % 4) != 0) + no_chunk = 0; + if (!IS_ALIGNED(sg->offset, sizeof(u32))) { + dev_info(ss->dev, "Align problem on src\n"); + no_chunk = 0; + } + sg = sg_next(sg); + } + if (no_chunk == 0 || sg_nents(in_sg) > 8) { + dev_info(ss->dev, "Bounce src\n"); + ret = sun8i_ss_bounce_src(areq, flow); + if (ret) { + dev_err(ss->dev, "Cannot bounce src\n"); + err = -EFAULT; + goto theend; + } + in_sg = ss->chanlist[flow].bounce_src; + } + no_chunk = 1; + sg = areq->dst; + while (sg && no_chunk == 1) { + if ((sg->length % 4) != 0) + no_chunk = 0; + if (!IS_ALIGNED(sg->offset, sizeof(u32))) { + dev_info(ss->dev, "Align problem on dst\n"); + no_chunk = 0; + } + sg = sg_next(sg); + } + if (no_chunk == 0) { + dev_info(ss->dev, "Bounce dst\n"); + ret = sun8i_ss_bounce_dst(areq, flow); + if (ret) { + dev_err(ss->dev, "Cannot bounce dst\n"); + err = -EFAULT; + goto theend; + } + out_sg = ss->chanlist[flow].bounce_dst; + } + + if (in_sg == out_sg) { + nr_sgs = dma_map_sg(ss->dev, in_sg, sg_nents(in_sg), + DMA_BIDIRECTIONAL); + if (nr_sgs < 0 || nr_sgs > 8) { + dev_info(ss->dev, "Invalid sg number %d\n", nr_sgs); + err = -EINVAL; + goto theend; + } + nr_sgd = nr_sgs; + } else { + nr_sgs = dma_map_sg(ss->dev, in_sg, sg_nents(in_sg), + DMA_TO_DEVICE); + if (nr_sgs < 0 || nr_sgs > 8) { + dev_info(ss->dev, "Invalid sg number %d\n", nr_sgs); + err = -EINVAL; + goto theend; + } + nr_sgd = dma_map_sg(ss->dev, out_sg, sg_nents(out_sg), + DMA_FROM_DEVICE); + if (nr_sgd < 0 || nr_sgd > 8) { + dev_info(ss->dev, "Invalid sg number %d\n", nr_sgd); + err = -EINVAL; + goto theend; + } + } + + len = areq->nbytes; + for_each_sg(in_sg, sg, nr_sgs, i) { + cet->t_src[i].addr = sg_dma_address(sg); + todo = min(len, sg_dma_len(sg)); + cet->t_src[i].len = todo / 4; + len -= todo; + } + + len = areq->nbytes; + for_each_sg(out_sg, sg, nr_sgd, i) { + cet->t_dst[i].addr = sg_dma_address(sg); + todo = min(len, sg_dma_len(sg)); + cet->t_dst[i].len = todo / 4; + len -= todo; + } + + err = sun8i_ce_run_task(ss, flow, "cipher"); + + if (areq->info) { + memcpy(areq->info, ss->chanlist[flow].next_iv, + ss->chanlist[flow].ivlen); + kzfree(ss->chanlist[flow].bounce_iv); + kzfree(ss->chanlist[flow].next_iv); + ss->chanlist[flow].bounce_iv = NULL; + ss->chanlist[flow].next_iv = NULL; + } + + dma_unmap_single(ss->dev, cet->t_key, op->keylen, DMA_TO_DEVICE); + if (in_sg == out_sg) { + dma_unmap_sg(ss->dev, in_sg, nr_sgs, DMA_BIDIRECTIONAL); + } else { + dma_unmap_sg(ss->dev, in_sg, nr_sgs, DMA_TO_DEVICE); + dma_unmap_sg(ss->dev, out_sg, nr_sgd, DMA_FROM_DEVICE); + } + + if (areq->dst != out_sg) { + dev_info(ss->dev, "Copy back\n"); + sg_copy_from_buffer(areq->dst, sg_nents(areq->dst), + ss->chanlist[flow].bufdst, areq->nbytes); + kfree(ss->chanlist[flow].bufsrc); + kfree(ss->chanlist[flow].bufdst); + ss->chanlist[flow].bufsrc = NULL; + ss->chanlist[flow].bufdst = NULL; + kfree(ss->chanlist[flow].bounce_src); + kfree(ss->chanlist[flow].bounce_dst); + ss->chanlist[flow].bounce_src = NULL; + ss->chanlist[flow].bounce_dst = NULL; + } +theend: + mutex_unlock(&ss->chanlock[flow]); + + return err; +} + +int sun8i_ss_cbc_aes_decrypt(struct ablkcipher_request *areq) +{ + struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(areq); + struct sun8i_tfm_ctx *op = crypto_ablkcipher_ctx(tfm); + struct sun8i_cipher_req_ctx *rctx = ablkcipher_request_ctx(areq); + int e = get_engine_number(op->ss); + + rctx->op_dir = SS_DECRYPTION; + rctx->flow = e; + + return crypto_transfer_cipher_request_to_engine(op->ss->engines[e], + areq); +} + +int sun8i_ss_cbc_aes_encrypt(struct ablkcipher_request *areq) +{ + struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(areq); + struct sun8i_tfm_ctx *op = crypto_ablkcipher_ctx(tfm); + struct sun8i_cipher_req_ctx *rctx = ablkcipher_request_ctx(areq); + int e = get_engine_number(op->ss); + + rctx->op_dir = SS_ENCRYPTION; + rctx->flow = e; + + return crypto_transfer_cipher_request_to_engine(op->ss->engines[e], + areq); +} + +int sun8i_ss_cipher_init(struct crypto_tfm *tfm) +{ + struct sun8i_tfm_ctx *op = crypto_tfm_ctx(tfm); + struct crypto_alg *alg = tfm->__crt_alg; + struct sun8i_ss_alg_template *algt; + + memset(op, 0, sizeof(struct sun8i_tfm_ctx)); + + algt = container_of(alg, struct sun8i_ss_alg_template, alg.crypto); + op->ss = algt->ss; + + tfm->crt_ablkcipher.reqsize = sizeof(struct sun8i_cipher_req_ctx); + + op->fallback_tfm = crypto_alloc_blkcipher(crypto_tfm_alg_name(tfm), + 0, CRYPTO_ALG_ASYNC | CRYPTO_ALG_NEED_FALLBACK); + if (IS_ERR(op->fallback_tfm)) { + dev_err(op->ss->dev, "ERROR: Cannot allocate fallback\n"); + return PTR_ERR(op->fallback_tfm); + } + + return 0; +} + +void sun8i_ss_cipher_exit(struct crypto_tfm *tfm) +{ + struct sun8i_tfm_ctx *op = crypto_tfm_ctx(tfm); + + crypto_free_blkcipher(op->fallback_tfm); +} + +int sun8i_ss_aes_setkey(struct crypto_ablkcipher *tfm, const u8 *key, + unsigned int keylen) +{ + struct sun8i_tfm_ctx *op = crypto_ablkcipher_ctx(tfm); + struct sun8i_ss_ctx *ss = op->ss; + + switch (keylen) { + case 128 / 8: + op->keymode = SS_AES_128BITS; + break; + case 192 / 8: + op->keymode = SS_AES_192BITS; + break; + case 256 / 8: + op->keymode = SS_AES_256BITS; + break; + default: + dev_err(ss->dev, "ERROR: Invalid keylen %u\n", keylen); + crypto_ablkcipher_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN); + return -EINVAL; + } + op->keylen = keylen; + op->key = kzalloc(keylen, GFP_KERNEL); + if (!op->key) + return -ENOMEM; + memcpy(op->key, key, keylen); + + return crypto_blkcipher_setkey(op->fallback_tfm, key, keylen); +} + +int handle_cipher_request(struct crypto_engine *engine, + struct ablkcipher_request *breq) +{ + int err; + + err = sun8i_ss_cipher(breq); + crypto_finalize_cipher_request(engine, breq, err); + + return 0; +} + diff --git a/drivers/staging/sun8i-ss/sun8i-ce-core.c b/drivers/staging/sun8i-ss/sun8i-ce-core.c new file mode 100644 index 0000000..4ad28f7 --- /dev/null +++ b/drivers/staging/sun8i-ss/sun8i-ce-core.c @@ -0,0 +1,934 @@ +/* + * sun8i-ce-core.c - hardware cryptographic accelerator for Allwinner H3/A64 SoC + * + * Copyright (C) 2015-2017 Corentin Labbe + * + * Core file which registers crypto algorithms supported by the CryptoEngine. + * + * You could find a link for the datasheet in Documentation/arm/sunxi/README + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sun8i-ss.h" + +static const struct ce_variant ce_h3_variant = { + .alg_hash = { CE_ID_NOTSUPP, CE_OP_MD5, CE_OP_SHA1, CE_OP_SHA224, + CE_OP_SHA256, CE_OP_SHA384, CE_OP_SHA512, + }, + .alg_cipher = { CE_ID_NOTSUPP, CE_OP_AES, CE_OP_DES, CE_OP_3DES, }, + .op_mode = { CE_ID_NOTSUPP, CE_ECB, CE_CBC, CE_ID_NOTSUPP, + CE_ID_NOTSUPP, CE_ID_NOTSUPP, CE_ID_NOTSUPP, CE_ID_NOTSUPP + }, + .prng = CE_OP_PRNG, + .trng = CE_ID_NOTSUPP, +}; + +static const struct ce_variant ce_a64_variant = { + .alg_hash = { CE_ID_NOTSUPP, CE_OP_MD5, CE_OP_SHA1, CE_OP_SHA224, + CE_OP_SHA256, CE_ID_NOTSUPP, CE_ID_NOTSUPP, + }, + .alg_cipher = { CE_ID_NOTSUPP, CE_OP_AES, CE_OP_DES, CE_OP_3DES, }, + .op_mode = { CE_ID_NOTSUPP, CE_ECB, CE_CBC, CE_ID_NOTSUPP, + CE_ID_NOTSUPP, CE_ID_NOTSUPP, CE_ID_NOTSUPP, CE_ID_NOTSUPP + }, + .prng = CE_OP_PRNG, + .trng = CE_ID_NOTSUPP, +}; + +static const struct ce_variant ce_a83t_variant = { + .alg_hash = { CE_ID_NOTSUPP, SS_OP_MD5, SS_OP_SHA1, SS_OP_SHA224, + SS_OP_SHA256, CE_ID_NOTSUPP, CE_ID_NOTSUPP, + }, + .alg_cipher = { CE_ID_NOTSUPP, SS_OP_AES, SS_OP_DES, SS_OP_3DES, }, + .op_mode = { CE_ID_NOTSUPP, CE_ID_NOTSUPP, SS_CBC, CE_ID_NOTSUPP, + CE_ID_NOTSUPP, CE_ID_NOTSUPP, CE_ID_NOTSUPP, CE_ID_NOTSUPP + }, + .prng = SS_OP_PRNG, + .trng = CE_ID_NOTSUPP, + .is_ss = true, +}; + +static const u32 ce_md5_init[MD5_DIGEST_SIZE / 4] = { + MD5_H0, MD5_H1, MD5_H2, MD5_H3 +}; + +static const u32 ce_sha1_init[SHA1_DIGEST_SIZE / 4] = { + cpu_to_be32(SHA1_H0), cpu_to_be32(SHA1_H1), + cpu_to_be32(SHA1_H2), cpu_to_be32(SHA1_H3), + cpu_to_be32(SHA1_H4), +}; + +static const u32 ce_sha224_init[SHA256_DIGEST_SIZE / 4] = { + cpu_to_be32(SHA224_H0), cpu_to_be32(SHA224_H1), + cpu_to_be32(SHA224_H2), cpu_to_be32(SHA224_H3), + cpu_to_be32(SHA224_H4), cpu_to_be32(SHA224_H5), + cpu_to_be32(SHA224_H6), cpu_to_be32(SHA224_H7), +}; + +static const u32 ce_sha256_init[SHA256_DIGEST_SIZE / 4] = { + cpu_to_be32(SHA256_H0), cpu_to_be32(SHA256_H1), + cpu_to_be32(SHA256_H2), cpu_to_be32(SHA256_H3), + cpu_to_be32(SHA256_H4), cpu_to_be32(SHA256_H5), + cpu_to_be32(SHA256_H6), cpu_to_be32(SHA256_H7), +}; + +static const u64 ce_sha384_init[SHA512_DIGEST_SIZE / 8] = { + cpu_to_be64(SHA384_H0), cpu_to_be64(SHA384_H1), + cpu_to_be64(SHA384_H2), cpu_to_be64(SHA384_H3), + cpu_to_be64(SHA384_H4), cpu_to_be64(SHA384_H5), + cpu_to_be64(SHA384_H6), cpu_to_be64(SHA384_H7), +}; + +static const u64 ce_sha512_init[SHA512_DIGEST_SIZE / 8] = { + cpu_to_be64(SHA512_H0), cpu_to_be64(SHA512_H1), + cpu_to_be64(SHA512_H2), cpu_to_be64(SHA512_H3), + cpu_to_be64(SHA512_H4), cpu_to_be64(SHA512_H5), + cpu_to_be64(SHA512_H6), cpu_to_be64(SHA512_H7), +}; + +int get_engine_number(struct sun8i_ss_ctx *ss) +{ + int e = ss->flow; + + ss->flow++; + if (ss->flow >= MAXCHAN) + ss->flow = 0; + + return e; +} + +int sun8i_ss_run_task(struct sun8i_ss_ctx *ss, int flow, const char *name) +{ + int err = 0; + u32 v = 1; + struct ss_task *cet = ss->tl[flow]; + int i; + + /* choose between stream0/stream1 */ + if (flow) + v |= BIT(31); + else + v |= BIT(30); + + v |= ss->chanlist[flow].op_mode; + v |= ss->chanlist[flow].method; + v |= ss->chanlist[flow].op_dir; + + switch (ss->chanlist[flow].keylen) { + case 192 / 8: + v |= SS_AES_192BITS << 7; + break; + case 256 / 8: + v |= SS_AES_256BITS << 7; + break; + } + + /* enable INT */ + writel(BIT(flow), ss->base + SS_INT_CTL_REG); + + writel(cet->t_key, ss->base + SS_KEY_ADR_REG); + writel(cet->t_iv, ss->base + SS_IV_ADR_REG); + + for (i = 0; i < 8; i++) { + if (!cet->t_src[i].addr) + break; + dev_info(ss->dev, "Processing SG %d\n", i); + writel(cet->t_src[i].addr, ss->base + SS_SRC_ADR_REG); + writel(cet->t_dst[i].addr, ss->base + SS_DST_ADR_REG); + writel(cet->t_dst[i].len, ss->base + SS_LEN_ADR_REG); + writel(v, ss->base + SS_CTL_REG); + } + + return err; +} +int sun8i_ce_run_task(struct sun8i_ss_ctx *ss, int flow, const char *name) +{ + u32 v; + int err = 0; + struct ss_task *cet = ss->tl[flow]; + + if (ss->chanlist[flow].bounce_iv) { + cet->t_iv = dma_map_single(ss->dev, + ss->chanlist[flow].bounce_iv, + ss->chanlist[flow].ivlen, + DMA_BIDIRECTIONAL); + if (dma_mapping_error(ss->dev, cet->t_iv)) { + dev_err(ss->dev, "Cannot DMA MAP IV\n"); + return -EFAULT; + } + } + if (ss->chanlist[flow].next_iv) { + cet->t_ctr = dma_map_single(ss->dev, + ss->chanlist[flow].next_iv, + ss->chanlist[flow].ivlen, + DMA_FROM_DEVICE); + if (dma_mapping_error(ss->dev, cet->t_ctr)) { + dev_err(ss->dev, "Cannot DMA MAP IV\n"); + err = -EFAULT; + goto err_next_iv; + } + } + + mutex_lock(&ss->mlock); + + v = readl(ss->base + CE_ICR); + v |= 1 << flow; + writel(v, ss->base + CE_ICR); + + reinit_completion(&ss->chanlist[flow].complete); + writel(ss->ce_t_phy[flow], ss->base + CE_TDQ); + + ss->chanlist[flow].status = 0; + /* Be sure all data is written before enabling the task */ + wmb(); + + writel(1, ss->base + CE_TLR); + mutex_unlock(&ss->mlock); + + wait_for_completion_interruptible_timeout(&ss->chanlist[flow].complete, + msecs_to_jiffies(5000)); + + if (ss->chanlist[flow].status == 0) { + dev_err(ss->dev, "DMA timeout for %s\n", name); + err = -EINVAL; + } + + v = readl(ss->base + CE_ESR); + if (v) { + dev_err(ss->dev, "CE ERROR %x\n", v); + err = -EFAULT; + } + + if (ss->chanlist[flow].next_iv) { + dma_unmap_single(ss->dev, cet->t_ctr, + ss->chanlist[flow].ivlen, + DMA_FROM_DEVICE); + } +err_next_iv: + if (ss->chanlist[flow].bounce_iv) { + dma_unmap_single(ss->dev, cet->t_iv, + ss->chanlist[flow].ivlen, + DMA_BIDIRECTIONAL); + } + + return err; +} + +/* compact an sglist to a more "compact" sglist + * With a maximum of 8 SGs + * */ +int sun8i_ss_compact(struct scatterlist *sg, unsigned int len) +{ + int numsg; + struct scatterlist *sglist; + int i; + void *buf; + unsigned int offset = 0; + int copied; + + /* determine the number of sgs necessary */ + numsg = len / PAGE_SIZE + 1; + if (numsg > 8) + return -EINVAL; + sglist = kcalloc(numsg, sizeof(struct scatterlist), GFP_KERNEL); + if (!sglist) + return -ENOMEM; + sg_init_table(sglist, numsg); + for (i = 0; i < numsg; i++) { + buf = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + sg_set_buf(&sglist[i], buf, PAGE_SIZE); + copied = sg_pcopy_to_buffer(sg, sg_nents(sg), buf, PAGE_SIZE, + offset); + pr_info("%d Copied %d at %u\n", i, copied, offset); + offset += copied; + } + return 0; +} + +/* copy all data from an sg to a plain buffer for channel flow */ +int sun8i_ss_bounce_src(struct ablkcipher_request *areq, int flow) +{ + struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(areq); + struct sun8i_tfm_ctx *op = crypto_ablkcipher_ctx(tfm); + struct sun8i_ss_ctx *ss = op->ss; + + if (areq->nbytes > PAGE_SIZE) + return -EINVAL; + + ss->chanlist[flow].bufsrc = kmalloc(areq->nbytes, GFP_KERNEL); + if (!ss->chanlist[flow].bufsrc) + return -ENOMEM; + + sg_copy_to_buffer(areq->src, sg_nents(areq->src), + ss->chanlist[flow].bufsrc, areq->nbytes); + + ss->chanlist[flow].bounce_src = kcalloc(1, sizeof(struct scatterlist), + GFP_KERNEL); + if (!ss->chanlist[flow].bounce_src) + return -ENOMEM; + + sg_init_table(ss->chanlist[flow].bounce_src, 1); + sg_set_buf(ss->chanlist[flow].bounce_src, ss->chanlist[flow].bufsrc, + areq->nbytes); + + return 0; +} + +/* create a destination bounce buffer */ +int sun8i_ss_bounce_dst(struct ablkcipher_request *areq, int flow) +{ + struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(areq); + struct sun8i_tfm_ctx *op = crypto_ablkcipher_ctx(tfm); + struct sun8i_ss_ctx *ss = op->ss; + + if (areq->nbytes > PAGE_SIZE) + return -EINVAL; + + ss->chanlist[flow].bufdst = kmalloc(areq->nbytes, GFP_KERNEL); + if (!ss->chanlist[flow].bufdst) + return -ENOMEM; + + ss->chanlist[flow].bounce_dst = kcalloc(1, sizeof(struct scatterlist), + GFP_KERNEL); + if (!ss->chanlist[flow].bounce_dst) + return -ENOMEM; + + sg_init_table(ss->chanlist[flow].bounce_dst, 1); + sg_set_buf(ss->chanlist[flow].bounce_dst, ss->chanlist[flow].bufdst, + areq->nbytes); + + return 0; +} + +int handle_hash_request(struct crypto_engine *engine, + struct ahash_request *areq) +{ + int err; + + err = sun8i_hash(areq); + crypto_finalize_hash_request(engine, areq, err); + + return 0; +} + +irqreturn_t ss_irq_handler(int irq, void *data) +{ + u32 p; + struct sun8i_ss_ctx *ss = (struct sun8i_ss_ctx *)data; + int flow = 0; + + p = readl(ss->base + SS_INT_STA_REG); + for (flow = 0; flow < 2; flow++) { + if (p & BIT(flow)) { + writel(BIT(flow), ss->base + SS_INT_STA_REG); + ss->chanlist[flow].status = 1; + complete(&ss->chanlist[flow].complete); + } + } + + return IRQ_HANDLED; +} + +irqreturn_t ce_irq_handler(int irq, void *data) +{ + u32 p; + struct sun8i_ss_ctx *ss = (struct sun8i_ss_ctx *)data; + int flow = 0; + + p = readl(ss->base + CE_ISR); + /*dev_info(ss->dev, "%s %d, %x\n", __func__, irq, p);*/ + for (flow = 0; flow < MAXCHAN; flow++) { + if (p & (1 << flow)) { + writel(1 << flow, ss->base + CE_ISR); + /*dev_info(ss->dev, "Acked %d\n", flow);*/ + ss->chanlist[flow].status = 1; + complete(&ss->chanlist[flow].complete); + } + } + + return IRQ_HANDLED; +} + +static struct sun8i_ss_alg_template ss_algs[] = { +{ .type = CRYPTO_ALG_TYPE_ABLKCIPHER, + .ce_algo_id = CE_ID_CIPHER_AES, + .ce_blockmode = CE_ID_MODE_CBC, + .alg.crypto = { + .cra_name = "cbc(aes)", + .cra_driver_name = "cbc-aes-sun8i-ss", + .cra_priority = 300, + .cra_blocksize = AES_BLOCK_SIZE, + .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC | + CRYPTO_ALG_NEED_FALLBACK, + .cra_ctxsize = sizeof(struct sun8i_tfm_ctx), + .cra_module = THIS_MODULE, + .cra_alignmask = 3, + .cra_type = &crypto_ablkcipher_type, + .cra_init = sun8i_ss_cipher_init, + .cra_exit = sun8i_ss_cipher_exit, + .cra_ablkcipher = { + .min_keysize = AES_MIN_KEY_SIZE, + .max_keysize = AES_MAX_KEY_SIZE, + .ivsize = AES_BLOCK_SIZE, + .setkey = sun8i_ss_aes_setkey, + .encrypt = sun8i_ss_cbc_aes_encrypt, + .decrypt = sun8i_ss_cbc_aes_decrypt, + } + } +}, +{ .type = CRYPTO_ALG_TYPE_AHASH, + .mode = CE_OP_MD5, + .ce_algo_id = CE_ID_HASH_MD5, + .hash_init = ce_md5_init, + .alg.hash = { + .init = sun8i_hash_init, + .update = sun8i_hash_update, + .final = sun8i_hash_final, + .finup = sun8i_hash_finup, + .digest = sun8i_hash_digest, + .export = sun8i_hash_export_md5, + .import = sun8i_hash_import_md5, + .halg = { + .digestsize = MD5_DIGEST_SIZE, + .statesize = sizeof(struct md5_state), + .base = { + .cra_name = "md5", + .cra_driver_name = "md5-sun8i-ss", + .cra_priority = 300, + .cra_alignmask = 3, + .cra_flags = CRYPTO_ALG_TYPE_AHASH | + CRYPTO_ALG_ASYNC | + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = MD5_HMAC_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct sun8i_hash_reqctx), + .cra_module = THIS_MODULE, + .cra_type = &crypto_ahash_type, + .cra_init = sun8i_hash_crainit, + } + } + } +}, +{ .type = CRYPTO_ALG_TYPE_AHASH, + .mode = CE_OP_SHA1, + .ce_algo_id = CE_ID_HASH_SHA1, + .hash_init = ce_sha1_init, + .alg.hash = { + .init = sun8i_hash_init, + .update = sun8i_hash_update, + .final = sun8i_hash_final, + .finup = sun8i_hash_finup, + .digest = sun8i_hash_digest, + .export = sun8i_hash_export_sha1, + .import = sun8i_hash_import_sha1, + .halg = { + .digestsize = SHA1_DIGEST_SIZE, + .statesize = sizeof(struct sha1_state), + .base = { + .cra_name = "sha1", + .cra_driver_name = "sha1-sun8i-ss", + .cra_priority = 300, + .cra_alignmask = 3, + .cra_flags = CRYPTO_ALG_TYPE_AHASH | + CRYPTO_ALG_ASYNC | + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SHA1_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct sun8i_hash_reqctx), + .cra_module = THIS_MODULE, + .cra_type = &crypto_ahash_type, + .cra_init = sun8i_hash_crainit + } + } + } +}, +{ .type = CRYPTO_ALG_TYPE_AHASH, + .mode = CE_OP_SHA224, + .ce_algo_id = CE_ID_HASH_SHA224, + .hash_init = ce_sha224_init, + .alg.hash = { + .init = sun8i_hash_init, + .update = sun8i_hash_update, + .final = sun8i_hash_final, + .finup = sun8i_hash_finup, + .digest = sun8i_hash_digest, + .export = sun8i_hash_export_sha256, + .import = sun8i_hash_import_sha256, + .halg = { + .digestsize = SHA224_DIGEST_SIZE, + .statesize = sizeof(struct sha256_state), + .base = { + .cra_name = "sha224", + .cra_driver_name = "sha224-sun8i-ss", + .cra_priority = 300, + .cra_alignmask = 3, + .cra_flags = CRYPTO_ALG_TYPE_AHASH | + CRYPTO_ALG_ASYNC | + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SHA224_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct sun8i_hash_reqctx), + .cra_module = THIS_MODULE, + .cra_type = &crypto_ahash_type, + .cra_init = sun8i_hash_crainit + } + } + } +}, +{ .type = CRYPTO_ALG_TYPE_AHASH, + .mode = CE_OP_SHA256, + .ce_algo_id = CE_ID_HASH_SHA256, + .hash_init = ce_sha256_init, + .alg.hash = { + .init = sun8i_hash_init, + .update = sun8i_hash_update, + .final = sun8i_hash_final, + .finup = sun8i_hash_finup, + .digest = sun8i_hash_digest, + .export = sun8i_hash_export_sha256, + .import = sun8i_hash_import_sha256, + .halg = { + .digestsize = SHA256_DIGEST_SIZE, + .statesize = sizeof(struct sha256_state), + .base = { + .cra_name = "sha256", + .cra_driver_name = "sha256-sun8i-ss", + .cra_priority = 300, + .cra_alignmask = 3, + .cra_flags = CRYPTO_ALG_TYPE_AHASH | + CRYPTO_ALG_ASYNC | + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SHA256_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct sun8i_hash_reqctx), + .cra_module = THIS_MODULE, + .cra_type = &crypto_ahash_type, + .cra_init = sun8i_hash_crainit + } + } + } +}, +{ .type = CRYPTO_ALG_TYPE_AHASH, + .mode = CE_OP_SHA384, + .ce_algo_id = CE_ID_HASH_SHA384, + .hash_init = ce_sha384_init, + .alg.hash = { + .init = sun8i_hash_init, + .update = sun8i_hash_update, + .final = sun8i_hash_final, + .finup = sun8i_hash_finup, + .digest = sun8i_hash_digest, + .export = sun8i_hash_export_sha512, + .import = sun8i_hash_import_sha512, + .halg = { + .digestsize = SHA384_DIGEST_SIZE, + .statesize = sizeof(struct sha512_state), + .base = { + .cra_name = "sha384", + .cra_driver_name = "sha384-sun8i-ss", + .cra_priority = 300, + .cra_alignmask = 3, + .cra_flags = CRYPTO_ALG_TYPE_AHASH | + CRYPTO_ALG_ASYNC | + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SHA384_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct sun8i_hash_reqctx), + .cra_module = THIS_MODULE, + .cra_type = &crypto_ahash_type, + .cra_init = sun8i_hash_crainit + } + } + } +}, +{ .type = CRYPTO_ALG_TYPE_AHASH, + .mode = CE_OP_SHA512, + .ce_algo_id = CE_ID_HASH_SHA512, + .hash_init = ce_sha512_init, + .alg.hash = { + .init = sun8i_hash_init, + .update = sun8i_hash_update, + .final = sun8i_hash_final, + .finup = sun8i_hash_finup, + .digest = sun8i_hash_digest, + .export = sun8i_hash_export_sha512, + .import = sun8i_hash_import_sha512, + .halg = { + .digestsize = SHA512_DIGEST_SIZE, + .statesize = sizeof(struct sha512_state), + .base = { + .cra_name = "sha512", + .cra_driver_name = "sha512-sun8i-ss", + .cra_priority = 300, + .cra_alignmask = 3, + .cra_flags = CRYPTO_ALG_TYPE_AHASH | + CRYPTO_ALG_ASYNC | + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SHA512_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct sun8i_hash_reqctx), + .cra_module = THIS_MODULE, + .cra_type = &crypto_ahash_type, + .cra_init = sun8i_hash_crainit + } + } + } +}, +#ifdef CONFIG_CRYPTO_DEV_SUN8I_SS_RSA +{ + .type = CRYPTO_ALG_TYPE_AKCIPHER, + .alg.rsa = { + .encrypt = sun8i_rsa_encrypt, + .decrypt = sun8i_rsa_decrypt, + .sign = sun8i_rsa_sign, + .verify = sun8i_rsa_verify, + .set_priv_key = sun8i_rsa_set_priv_key, + .set_pub_key = sun8i_rsa_set_pub_key, + .max_size = sun8i_rsa_max_size, + .init = sun8i_rsa_init, + .exit = sun8i_rsa_exit, + .base = { + .cra_name = "rsa", + .cra_driver_name = "rsa-sun8i-ce", + .cra_priority = 300, + .cra_flags = CRYPTO_ALG_TYPE_AKCIPHER | + CRYPTO_ALG_ASYNC | CRYPTO_ALG_NEED_FALLBACK, + .cra_ctxsize = sizeof(struct sun8i_tfm_rsa_ctx), + .cra_module = THIS_MODULE, + .cra_alignmask = 3, + } + } +} +#endif +}; + +static int sun8i_ss_probe(struct platform_device *pdev) +{ + struct resource *res; + u32 v; + int err, i, ce_method; + struct sun8i_ss_ctx *ss; + + if (!pdev->dev.of_node) + return -ENODEV; + + ss = devm_kzalloc(&pdev->dev, sizeof(*ss), GFP_KERNEL); + if (!ss) + return -ENOMEM; + + ss->variant = of_device_get_match_data(&pdev->dev); + if (!ss->variant) { + dev_err(&pdev->dev, "Missing Crypto Engine variant\n"); + return -EINVAL; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ss->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(ss->base)) { + err = PTR_ERR(ss->base); + dev_err(&pdev->dev, "Cannot request MMIO %d\n", err); + return err; + } + + ss->busclk = devm_clk_get(&pdev->dev, "ahb1_ce"); + if (IS_ERR(ss->busclk)) { + err = PTR_ERR(ss->busclk); + dev_err(&pdev->dev, "Cannot get AHB SS clock err=%d\n", err); + return err; + } + dev_dbg(&pdev->dev, "clock ahb_ss acquired\n"); + + ss->ssclk = devm_clk_get(&pdev->dev, "mod"); + if (IS_ERR(ss->ssclk)) { + err = PTR_ERR(ss->ssclk); + dev_err(&pdev->dev, "Cannot get SS clock err=%d\n", err); + return err; + } + + ss->reset = devm_reset_control_get_optional(&pdev->dev, "ahb"); + if (IS_ERR(ss->reset)) { + if (PTR_ERR(ss->reset) == -EPROBE_DEFER) + return PTR_ERR(ss->reset); + dev_info(&pdev->dev, "no reset control found\n"); + ss->reset = NULL; + } + + /* Enable both clocks */ + err = clk_prepare_enable(ss->busclk); + if (err != 0) { + dev_err(&pdev->dev, "Cannot prepare_enable busclk\n"); + return err; + } + + err = clk_prepare_enable(ss->ssclk); + if (err != 0) { + dev_err(&pdev->dev, "Cannot prepare_enable ssclk\n"); + goto error_clk; + } + /* Deassert reset if we have a reset control */ + if (ss->reset) { + err = reset_control_deassert(ss->reset); + if (err) { + dev_err(&pdev->dev, "Cannot deassert reset control\n"); + goto error_ssclk; + } + } + + ss->nsbase = ioremap(0x01c15800, 0x40); + if (ss->nsbase) { + v = readl(ss->nsbase + CE_CTR); + v &= 0x07; + dev_info(&pdev->dev, "CE_S Die ID %x\n", v); + } + iounmap(ss->nsbase); + /* + ss->nsbase = ioremap(0x01ce000, 0x40); + v = BIT(15); + writel(v, ss->nsbase + 0x0C); + for (i = 0; i < 0x40; i += 4) { + v = readl(ss->nsbase + i); + dev_info(&pdev->dev, "SMC_%x %x\n", i, v); + } + iounmap(ss->nsbase); + ss->nsbase = ioremap(0x01c23400, 0x40); + for (i = 0; i < 0x24; i += 4) { + v = readl(ss->nsbase + i); + dev_info(&pdev->dev, "SMTA_%x %x\n", i, v); + } + iounmap(ss->nsbase); + ss->nsbase = ioremap(0x01F01400, 0x1FF); + writel(1, ss->nsbase + 0x1F4); + for (i = 0; i < 0x1FF; i += 4) { + v = readl(ss->nsbase + i); + dev_info(&pdev->dev, "R_PCRM_%x %x\n", i, v); + } + iounmap(ss->nsbase); + */ + v = readl(ss->base + CE_CTR); + v >>= 16; + v &= 0x07; + dev_info(&pdev->dev, "CE_NS Die ID %x\n", v); + + ss->dev = &pdev->dev; + platform_set_drvdata(pdev, ss); + + mutex_init(&ss->mlock); + + for (i = 0; i < MAXCHAN; i++) { + init_completion(&ss->chanlist[i].complete); + mutex_init(&ss->chanlock[i]); + + ss->engines[i] = crypto_engine_alloc_init(ss->dev, 1); + if (!ss->engines[i]) { + dev_err(ss->dev, "Cannot request engine\n"); + goto error_engine; + } + ss->engines[i]->cipher_one_request = handle_cipher_request; + ss->engines[i]->hash_one_request = handle_hash_request; + err = crypto_engine_start(ss->engines[i]); + if (err) { + dev_err(ss->dev, "Cannot request engine\n"); + goto error_engine; + } + } + /* Get Secure IRQ */ + ss->irq = platform_get_irq(pdev, 0); + if (ss->irq < 0) { + dev_err(ss->dev, "Cannot get S IRQ\n"); + goto error_clk; + } + + err = devm_request_irq(&pdev->dev, ss->irq, ce_irq_handler, 0, + "sun8i-ce-s", ss); + if (err < 0) { + dev_err(ss->dev, "Cannot request S IRQ\n"); + goto error_clk; + } + + /* Get Non Secure IRQ */ + ss->ns_irq = platform_get_irq(pdev, 1); + if (ss->ns_irq < 0) { + dev_err(ss->dev, "Cannot get NS IRQ\n"); + goto error_clk; + } + + err = devm_request_irq(&pdev->dev, ss->ns_irq, ce_irq_handler, 0, + "sun8i-ce-ns", ss); + if (err < 0) { + dev_err(ss->dev, "Cannot request NS IRQ\n"); + goto error_clk; + } + + for (i = 0; i < MAXCHAN; i++) { + ss->tl[i] = dma_alloc_coherent(ss->dev, sizeof(struct ss_task), + &ss->ce_t_phy[i], GFP_KERNEL); + if (!ss->tl[i]) { + dev_err(ss->dev, "Cannot get DMA memory for task %d\n", + i); + err = -EINVAL; + return err; + } + } + + for (i = 0; i < ARRAY_SIZE(ss_algs); i++) { + ss_algs[i].ss = ss; + switch (ss_algs[i].type) { + case CRYPTO_ALG_TYPE_ABLKCIPHER: + err = crypto_register_alg(&ss_algs[i].alg.crypto); + if (err != 0) { + dev_err(ss->dev, "Fail to register %s\n", + ss_algs[i].alg.crypto.cra_name); + goto error_alg; + } + break; + case CRYPTO_ALG_TYPE_AKCIPHER: + err = crypto_register_akcipher(&ss_algs[i].alg.rsa); + if (err != 0) { + dev_err(ss->dev, "Fail to register RSA %s\n", + ss_algs[i].alg.rsa.base.cra_name); + goto error_alg; + } + break; + case CRYPTO_ALG_TYPE_AHASH: + err = ss->variant->alg_hash[ss_algs[i].ce_algo_id]; + if (err == CE_ID_NOTSUPP) + break; + err = crypto_register_ahash(&ss_algs[i].alg.hash); + if (err != 0) { + dev_err(ss->dev, "Fail to register %s\n", + ss_algs[i].alg.hash.halg.base.cra_name); + goto error_alg; + } + break; + } + } + + ce_method = ss->variant->prng; + if (ce_method != CE_ID_NOTSUPP) + sun8i_ce_hwrng_register(&ss->prng, "Sun8i-ce PRNG", + PRNG_SEED_SIZE, PRNG_DATA_SIZE, + ce_method, ss); + + ce_method = ss->variant->trng; + if (ce_method != CE_ID_NOTSUPP) + sun8i_ce_hwrng_register(&ss->trng, "Sun8i-ce TRNG", 0, + TRNG_DATA_SIZE, ce_method, ss); + + return 0; +error_alg: + i--; + for (; i >= 0; i--) { + switch (ss_algs[i].type) { + case CRYPTO_ALG_TYPE_ABLKCIPHER: + crypto_unregister_alg(&ss_algs[i].alg.crypto); + break; + case CRYPTO_ALG_TYPE_AHASH: + err = ss->variant->alg_hash[ss_algs[i].ce_algo_id]; + if (err == CE_ID_NOTSUPP) + break; + crypto_unregister_ahash(&ss_algs[i].alg.hash); + break; + case CRYPTO_ALG_TYPE_AKCIPHER: + crypto_unregister_akcipher(&ss_algs[i].alg.rsa); + break; + } + } + if (ss->reset) + reset_control_assert(ss->reset); +error_engine: + while (i >= 0) { + crypto_engine_exit(ss->engines[i]); + i--; + } +error_clk: + clk_disable_unprepare(ss->ssclk); +error_ssclk: + clk_disable_unprepare(ss->busclk); + return err; +} + +static int sun8i_ss_remove(struct platform_device *pdev) +{ + int i, timeout, id; + struct sun8i_ss_ctx *ss = platform_get_drvdata(pdev); + + sun8i_ce_hwrng_unregister(&ss->prng.hwrng); + /*sun8i_ce_hwrng_unregister(&ss->trng.hwrng);*/ + + for (i = 0; i < ARRAY_SIZE(ss_algs); i++) { + switch (ss_algs[i].type) { + case CRYPTO_ALG_TYPE_ABLKCIPHER: + crypto_unregister_alg(&ss_algs[i].alg.crypto); + break; + case CRYPTO_ALG_TYPE_AHASH: + id = ss_algs[i].ce_algo_id; + if (ss->variant->alg_hash[id] == CE_ID_NOTSUPP) + break; + crypto_unregister_ahash(&ss_algs[i].alg.hash); + break; + case CRYPTO_ALG_TYPE_AKCIPHER: + crypto_unregister_akcipher(&ss_algs[i].alg.rsa); + break; + } + } + for (i = 0; i < MAXCHAN; i++) { + crypto_engine_exit(ss->engines[i]); + timeout = 0; + while (mutex_is_locked(&ss->chanlock[i]) && timeout < 10) { + dev_info(ss->dev, "Wait for %d %d\n", i, timeout); + timeout++; + msleep(20); + } + } + + /* TODO check that any request are still under work */ + + if (ss->reset) + reset_control_assert(ss->reset); + clk_disable_unprepare(ss->busclk); + return 0; +} + +static const struct of_device_id h3_ss_crypto_of_match_table[] = { + { .compatible = "allwinner,sun8i-h3-crypto", + .data = &ce_h3_variant }, + { .compatible = "allwinner,sun50i-a64-crypto", + .data = &ce_a64_variant }, + { .compatible = "allwinner,sun8i-a83t-crypto", + .data = &ce_a83t_variant }, + {} +}; +MODULE_DEVICE_TABLE(of, h3_ss_crypto_of_match_table); + +static struct platform_driver sun8i_ss_driver = { + .probe = sun8i_ss_probe, + .remove = sun8i_ss_remove, + .driver = { + .name = "sun8i-ss", + .of_match_table = h3_ss_crypto_of_match_table, + }, +}; + +module_platform_driver(sun8i_ss_driver); + +MODULE_DESCRIPTION("Allwinner Security System cryptographic accelerator"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Corentin Labbe "); diff --git a/drivers/staging/sun8i-ss/sun8i-ce-hash.c b/drivers/staging/sun8i-ss/sun8i-ce-hash.c new file mode 100644 index 0000000..ab60056 --- /dev/null +++ b/drivers/staging/sun8i-ss/sun8i-ce-hash.c @@ -0,0 +1,907 @@ +/* + * sun8i-ce-hash.c - hardware cryptographic accelerator for Allwinner H3/A64 SoC + * + * Copyright (C) 2015-2017 Corentin Labbe + * + * This file add support for MD5 and SHA1/SHA224/SHA256/SHA384/SHA512. + * + * You could find the datasheet in Documentation/arm/sunxi/README + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#include "sun8i-ss.h" +#include +#include +#include +#include + +/* This is a totally arbitrary value */ +#define SS_TIMEOUT 100 +#define SG_ZC 1 + +/*#define DEBUG*/ +static int digest_size(struct ahash_request *areq) +{ + struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq); + struct ahash_alg *alg = __crypto_ahash_alg(tfm->base.__crt_alg); + struct sun8i_ss_alg_template *algt; + int digestsize; + + algt = container_of(alg, struct sun8i_ss_alg_template, alg.hash); + digestsize = algt->alg.hash.halg.digestsize; + if (digestsize == SHA224_DIGEST_SIZE) + digestsize = SHA256_DIGEST_SIZE; + if (digestsize == SHA384_DIGEST_SIZE) + digestsize = SHA512_DIGEST_SIZE; + return digestsize; +} + +int sun8i_hash_crainit(struct crypto_tfm *tfm) +{ + struct sun8i_tfm_ctx *op = crypto_tfm_ctx(tfm); + struct ahash_alg *alg = __crypto_ahash_alg(tfm->__crt_alg); + struct sun8i_ss_alg_template *algt; + + memset(op, 0, sizeof(struct sun8i_tfm_ctx)); + + algt = container_of(alg, struct sun8i_ss_alg_template, alg.hash); + op->ss = algt->ss; + + crypto_ahash_set_reqsize(__crypto_ahash_cast(tfm), + sizeof(struct sun8i_hash_reqctx)); + +#ifdef DEBUG + dev_info(op->ss->dev, "%s ====================\n", __func__); +#endif + return 0; +} + +int sun8i_hash_exit(struct ahash_request *areq) +{ +/* struct sun8i_hash_reqctx *op = ahash_request_ctx(areq);*/ + +/* crypto_free_shash(op->fallback_tfm);*/ + return 0; +} + +/* sun8i_hash_init: initialize request context */ +int sun8i_hash_init(struct ahash_request *areq) +{ + struct sun8i_hash_reqctx *op = ahash_request_ctx(areq); + struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq); + struct ahash_alg *alg = __crypto_ahash_alg(tfm->base.__crt_alg); + struct sun8i_ss_alg_template *algt; + + memset(op, 0, sizeof(struct sun8i_hash_reqctx)); + + algt = container_of(alg, struct sun8i_ss_alg_template, alg.hash); + op->mode = algt->mode; + + /* FALLBACK */ + /* + op->fallback_tfm = crypto_alloc_shash(crypto_ahash_alg_name(tfm), 0, + CRYPTO_ALG_NEED_FALLBACK); + if (IS_ERR(op->fallback_tfm)) { + dev_err(algt->ss->dev, "Fallback driver cound no be loaded\n"); + return PTR_ERR(op->fallback_tfm); + }*/ + + op->hash = kmalloc(digest_size(areq), GFP_KERNEL | GFP_ATOMIC); + if (!op->hash) + return -ENOMEM; + /*dev_info(algt->ss->dev, "Alloc %p\n", op->hash);*/ + op->hash[0] = 0; +#ifdef DEBUG + dev_info(algt->ss->dev, "%s ====================\n", __func__); +#endif + + return 0; +} + +int sun8i_hash_export_md5(struct ahash_request *areq, void *out) +{ + struct sun8i_hash_reqctx *op = ahash_request_ctx(areq); + struct md5_state *octx = out; + struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq); + struct ahash_alg *alg = __crypto_ahash_alg(tfm->base.__crt_alg); + struct sun8i_ss_alg_template *algt; + + algt = container_of(alg, struct sun8i_ss_alg_template, alg.hash); + + octx->byte_count = op->byte_count + op->blen; + + if (op->blen > MD5_BLOCK_WORDS * 4) { + pr_err("Cannot export MD5\n"); + return -EINVAL; + } + + if (op->buf[0]) + memcpy(octx->block, op->buf[0], op->blen); + + if (op->byte_count > 0) + memcpy(octx->hash, op->hash, MD5_BLOCK_WORDS); + else + memcpy(octx->hash, algt->hash_init, MD5_BLOCK_WORDS); + + return 0; +} + +int sun8i_hash_import_md5(struct ahash_request *areq, const void *in) +{ + struct sun8i_hash_reqctx *op = ahash_request_ctx(areq); + const struct md5_state *ictx = in; + + sun8i_hash_init(areq); + + op->byte_count = ictx->byte_count & ~0x3F; + op->blen = ictx->byte_count & 0x3F; + + op->buf[0] = kzalloc(PAGE_SIZE, GFP_KERNEL | GFP_ATOMIC); + if (!op->buf[0]) + return -ENOMEM; + + if (!op->sgbounce[0]) + op->sgbounce[0] = kzalloc(sizeof(*op->sgbounce[0]), + GFP_KERNEL | GFP_ATOMIC); + if (!op->sgbounce[0]) + return -ENOMEM; + + sg_init_one(op->sgbounce[0], op->buf[0], PAGE_SIZE); + + if (op->blen) + memcpy(op->buf[0], ictx->block, op->blen); + memcpy(op->hash, ictx->hash, MD5_DIGEST_SIZE); + + return 0; +} + +int sun8i_hash_export_sha1(struct ahash_request *areq, void *out) +{ + struct sun8i_hash_reqctx *op = ahash_request_ctx(areq); + struct sha1_state *octx = out; + struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq); + struct ahash_alg *alg = __crypto_ahash_alg(tfm->base.__crt_alg); + struct sun8i_ss_alg_template *algt; + + algt = container_of(alg, struct sun8i_ss_alg_template, alg.hash); + + if (op->blen > SHA1_DIGEST_SIZE * 4) { + pr_err("Cannot export SHA1\n"); + return -EINVAL; + } + + octx->count = op->byte_count + op->blen; + + memcpy(octx->buffer, op->buf[0], op->blen); + + if (op->byte_count > 0) + memcpy(octx->state, op->hash, SHA1_DIGEST_SIZE); + else + memcpy(octx->state, algt->hash_init, SHA1_DIGEST_SIZE); + + return 0; +} + +int sun8i_hash_import_sha1(struct ahash_request *areq, const void *in) +{ + struct sun8i_hash_reqctx *op = ahash_request_ctx(areq); + const struct sha1_state *ictx = in; + + sun8i_hash_init(areq); + + op->byte_count = ictx->count & ~0x3F; + op->blen = ictx->count & 0x3F; + + op->buf[0] = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!op->buf[0]) + return -ENOMEM; + + if (!op->sgbounce[0]) + op->sgbounce[0] = kzalloc(sizeof(*op->sgbounce[0]), GFP_KERNEL); + if (!op->sgbounce[0]) + return -ENOMEM; + + sg_init_one(op->sgbounce[0], op->buf[0], PAGE_SIZE); + + if (op->blen) + memcpy(op->buf[0], ictx->buffer, op->blen); + + memcpy(op->hash, ictx->state, SHA1_DIGEST_SIZE); + + return 0; +} + +int sun8i_hash_export_sha256(struct ahash_request *areq, void *out) +{ + struct sun8i_hash_reqctx *op = ahash_request_ctx(areq); + struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq); + struct ahash_alg *alg = __crypto_ahash_alg(tfm->base.__crt_alg); + struct sun8i_ss_alg_template *algt; + struct sha256_state *octx = out; + + algt = container_of(alg, struct sun8i_ss_alg_template, alg.hash); + + if (op->blen > SHA224_DIGEST_SIZE * 4) { + pr_err("Cannot export SHA224\n"); + return -EINVAL; + } + + octx->count = op->byte_count + op->blen; + if (op->blen) + memcpy(octx->buf, op->buf[0], op->blen); + + if (op->byte_count > 0) + memcpy(octx->state, op->hash, SHA256_DIGEST_SIZE); + else + memcpy(octx->state, algt->hash_init, SHA256_DIGEST_SIZE); + + return 0; +} + +int sun8i_hash_import_sha256(struct ahash_request *areq, const void *in) +{ + struct sun8i_hash_reqctx *op = ahash_request_ctx(areq); + const struct sha256_state *ictx = in; + + sun8i_hash_init(areq); + + op->byte_count = ictx->count & ~0x3F; + op->blen = ictx->count & 0x3F; + + op->buf[0] = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!op->buf[0]) + return -ENOMEM; + + if (!op->sgbounce[0]) + op->sgbounce[0] = kzalloc(sizeof(*op->sgbounce[0]), GFP_KERNEL); + if (!op->sgbounce[0]) + return -ENOMEM; + + sg_init_one(op->sgbounce[0], op->buf[0], PAGE_SIZE); + + if (op->blen) + memcpy(op->buf[0], ictx->buf, op->blen); + memcpy(op->hash, ictx->state, SHA256_DIGEST_SIZE); + + return 0; +} + +int sun8i_hash_import_sha512(struct ahash_request *areq, const void *in) +{ + struct sun8i_hash_reqctx *op = ahash_request_ctx(areq); + const struct sha512_state *ictx = in; + + sun8i_hash_init(areq); + + op->byte_count = ictx->count[0] & ~0x7F; + op->blen = ictx->count[0] & 0x7F; + + op->byte_count2 = ictx->count[1]; + + op->buf[0] = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!op->buf[0]) + return -ENOMEM; + + if (!op->sgbounce[0]) + op->sgbounce[0] = kzalloc(sizeof(*op->sgbounce[0]), GFP_KERNEL); + if (!op->sgbounce[0]) + return -ENOMEM; + + sg_init_one(op->sgbounce[0], op->buf[0], PAGE_SIZE); + + if (op->blen) + memcpy(op->buf[0], ictx->buf, op->blen); + memcpy(op->hash, ictx->state, SHA512_DIGEST_SIZE); + + return 0; +} + +int sun8i_hash_export_sha512(struct ahash_request *areq, void *out) +{ + struct sun8i_hash_reqctx *op = ahash_request_ctx(areq); + struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq); + struct ahash_alg *alg = __crypto_ahash_alg(tfm->base.__crt_alg); + struct sun8i_ss_alg_template *algt; + struct sha512_state *octx = out; + + algt = container_of(alg, struct sun8i_ss_alg_template, alg.hash); + + if (op->blen > SHA512_DIGEST_SIZE * 4) { + pr_err("Cannot export SHA512\n"); + return -EINVAL; + } + + octx->count[1] = op->byte_count2; + octx->count[0] = op->byte_count + op->blen; + if (octx->count[0] < op->blen) + op->byte_count2++; + + if (op->blen) + memcpy(octx->buf, op->buf[0], op->blen); + + if (op->byte_count > 0) + memcpy(octx->state, op->hash, SHA512_DIGEST_SIZE); + else + memcpy(octx->state, algt->hash_init, SHA512_DIGEST_SIZE); + return 0; +} + +int sun8i_ss_do_task(struct ahash_request *areq, int j) +{ + struct sun8i_hash_reqctx *op = ahash_request_ctx(areq); + struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq); + struct sun8i_tfm_ctx *tfmctx = crypto_ahash_ctx(tfm); + struct sun8i_ss_ctx *ss = tfmctx->ss; + struct ahash_alg *alg = __crypto_ahash_alg(tfm->base.__crt_alg); + struct sun8i_ss_alg_template *algt; + struct ss_task *cet; + int flow = 3; + int nr_sgb, i, todo; + struct scatterlist *sg; + unsigned int len = j; + int digestsize; + u32 v; + int sgnum = 0; + int ret; + + flow = op->flow; + /*dev_info(ss->dev, "Hash flow %d\n", flow);*/ + +#ifdef DEBUG + dev_info(ss->dev, "%s %s %d\n", __func__, crypto_tfm_alg_name(areq->base.tfm), j); +#endif + + algt = container_of(alg, struct sun8i_ss_alg_template, alg.hash); + digestsize = algt->alg.hash.halg.digestsize; + if (digestsize == SHA224_DIGEST_SIZE) + digestsize = SHA256_DIGEST_SIZE; + if (digestsize == SHA384_DIGEST_SIZE) + digestsize = SHA512_DIGEST_SIZE; + + cet = ss->tl[flow]; + memset(cet, 0, sizeof(struct ss_task)); + cet->t_id = flow; + cet->t_common_ctl = op->mode | BIT(31); + + cet->t_dlen = j / 4; + + nr_sgb = op->cursg + 1; + for (i = 0; i < nr_sgb; i++) { + sg = op->sgbounce[i]; + if (!sg) + break; + /*dev_info(ss->dev, "DEBUG SG %d %p\n", i, op->buf[i]);*/ + } + nr_sgb = op->cursg + 1; + for (i = 0; i < nr_sgb; i++) { + sg = op->sgbounce[i]; + if (!sg) + break; + /*dev_info(ss->dev, "SGmap %d\n", i);*/ + ret = dma_map_sg(ss->dev, sg, 1, DMA_TO_DEVICE); + if (ret < 1) + dev_err(ss->dev, "SG DMA MAP ERROR\n"); + cet->t_src[sgnum + i].addr = sg_dma_address(sg); + todo = min(len, sg_dma_len(sg)); + cet->t_src[sgnum + i].len = todo / 4; +#ifdef DEBUG + dev_info(ss->dev, "SG %d %u\n", sgnum + i, todo); +#endif + len -= todo; + } + + cet->t_dst[0].len = digestsize / 4; + cet->t_dst[0].addr = dma_map_single(ss->dev, op->hash, cet->t_dst[0].len, DMA_FROM_DEVICE); + if (dma_mapping_error(ss->dev, cet->t_dst[0].addr)) { + dev_err(ss->dev, "Cannot DMA MAP RESULT\n"); + } + + /* TODO need to do a flag for non std IV */ + if (op->hash[0] != 0) { + ss->chanlist[flow].bounce_iv = kmalloc(digestsize, GFP_KERNEL | GFP_DMA); + ss->chanlist[flow].ivlen = digestsize; + cet->t_common_ctl |= BIT(16); + memcpy(ss->chanlist[flow].bounce_iv, op->hash, digestsize); + } + ret = sun8i_ce_run_task(ss, flow, "hash"); + if (op->hash[0] != 0) { + kzfree(ss->chanlist[flow].bounce_iv); + ss->chanlist[flow].bounce_iv = NULL; + } + + for (i = 0; i < nr_sgb; i++) { + sg = op->sgbounce[i]; + if (!sg) + break; + dma_unmap_sg(ss->dev, sg, 1, DMA_TO_DEVICE); + } + + dma_unmap_single(ss->dev, cet->t_dst[0].addr, cet->t_dst[0].len, + DMA_FROM_DEVICE); + + v = readl(ss->base + CE_ESR); + if (v) { + dev_err(ss->dev, "CE ERROR %x %x\n", v, v >> flow * 4); + } + /*dev_info(ss->dev, "Fin Upload %d %u %p %p\n", op->blen, areq->nbytes, + * op->buf, op->hash);*/ + return ret; +} + +#define SS_HASH_UPDATE 1 +#define SS_HASH_FINAL 2 + +/* Fill op->sgbounce with sg from areq->src + * skip "skip" sg */ +int sun8i_ss_hashtask_zc(struct ahash_request *areq) +{ + struct sun8i_hash_reqctx *op = ahash_request_ctx(areq); + struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq); + struct sun8i_tfm_ctx *tfmctx = crypto_ahash_ctx(tfm); + struct sun8i_ss_ctx *ss = tfmctx->ss; + + int i = op->cursg; + int zlen = 0; + + if (!op->src_sg) { + dev_err(ss->dev, "SG is NULL\n"); + return -EINVAL; + } + + do { + op->sgbounce[i] = op->src_sg; + op->sgflag[i] = SG_ZC; + zlen += op->src_sg->length; +#ifdef DEBUG + dev_info(ss->dev, "ZCMap %d %u\n", i, op->src_sg->length); +#endif + op->src_sg = sg_next(op->src_sg); + i++; + /* TODO round zlen to bs */ + if (i > 7 && zlen > 64) { + op->cursg = i; + sun8i_ss_do_task(areq, zlen); + + i = 0; + op->sgbounce[i] = NULL; + op->byte_count += zlen; + zlen = 0; + op->cursg = 0; + } + if (i > 7) { + dev_err(ss->dev, "%s Trop de SG\n", __func__); + return 0; + } + } while (op->src_sg); + op->cursg = i; + +#ifdef DEBUG + dev_info(ss->dev, "%s end with cursg=%d\n", __func__, i); +#endif + return 0; +} + +/* copy data in a compact sg + * copy data from areq->src offset ??? + * to op->sgbounce on sg=op->cursg + */ +int sun8i_ss_hashtask_bounce(struct ahash_request *areq) +{ + struct sun8i_hash_reqctx *op = ahash_request_ctx(areq); + struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq); + struct sun8i_tfm_ctx *tfmctx = crypto_ahash_ctx(tfm); + struct sun8i_ss_ctx *ss = tfmctx->ss; + struct ahash_alg *alg = __crypto_ahash_alg(tfm->base.__crt_alg); + struct sun8i_ss_alg_template *algt; + int cursg = 0; + int offset; + int bs = 64;/* TODO */ + unsigned long j; + unsigned long tocopy = 0; + int i; + + int sg_src_offset = 0; + /* sg_src_len: how many bytes remains in SG src */ + unsigned int sg_src_len = areq->nbytes; + unsigned long copied, max; + + algt = container_of(alg, struct sun8i_ss_alg_template, alg.hash); + bs = algt->alg.hash.halg.base.cra_blocksize; + +start_copy: + /* in which sg we need to copy ? */ + cursg = op->blen / PAGE_SIZE; + cursg = op->cursg; + offset = op->blen % PAGE_SIZE; + + if (cursg > 7) { + dev_err(ss->dev, "ERROR: MAXIMUM SG\n"); + return -EINVAL; + } + +/* if (cursg == 0 && offset == 0 && !op->buf[0]) + sg_init_table(op->sgbounce, 8);*/ + + if (!op->buf[cursg]) { + /*dev_info(ss->dev, "Allocate SG %d\n", cursg);*/ + if (!op->sgbounce[cursg]) + op->sgbounce[cursg] = kzalloc(sizeof(*op->sgbounce[cursg]), + GFP_KERNEL | GFP_ATOMIC); + if (!op->sgbounce[cursg]) + return -ENOMEM; + + op->buf[cursg] = kzalloc(PAGE_SIZE, GFP_KERNEL | GFP_ATOMIC); + if (!op->buf[cursg]) + return -ENOMEM; + sg_init_one(op->sgbounce[cursg], op->buf[cursg], PAGE_SIZE); + } + + if (areq->nbytes == 0) + return 0; + + /* if the request is not final, we need to round to bs */ + if ((op->flags & SS_HASH_FINAL) == 0 && sg_src_len + op->blen > bs) + max = ((sg_src_len + op->blen) / bs) * bs - op->blen; + else + max = sg_src_len; + + tocopy = min(max, PAGE_SIZE - offset); + + if (tocopy == 0) { +#ifdef DEBUG + dev_info(ss->dev, "Nocopy %d (sg %d) (src offset %d/%u) %lu %lu\n", + offset, cursg, sg_src_offset, areq->nbytes, max, tocopy); +#endif + return 0; + } + + copied = sg_pcopy_to_buffer(op->src_sg, sg_nents(op->src_sg), + op->buf[cursg] + offset, tocopy, + sg_src_offset); +#ifdef DEBUG + dev_info(ss->dev, "Copied %lu at %d (sg %d) (src offset %d/%u) %lu %lu sgsrclen=%u\n", + copied, offset, cursg, sg_src_offset, areq->nbytes, max, tocopy, sg_src_len); +#endif + sg_src_len -= copied; + sg_src_offset += copied; + op->blen += copied; + if (op->blen % PAGE_SIZE == 0) + op->cursg++; + + /* maximum supported by hw */ + if (cursg == 7 && op->blen == PAGE_SIZE) { +#ifdef DEBUG + dev_info(ss->dev, "NEED UPLOAD (MAXIMUM)\n"); +#endif + } + + if (sg_src_len > bs) + goto start_copy; + + if (op->blen >= bs && (op->flags & SS_HASH_FINAL) == 0) { + j = op->blen - op->blen % bs; +#ifdef DEBUG + dev_info(ss->dev, "NEED UPLOAD %lu sg_src_len=%d\n", j, sg_src_len); +#endif + /*sg_mark_end(op->sgbounce[cursg]);*/ + sun8i_ss_do_task(areq, j); + op->cursg = 0; + op->byte_count += j; + memset(op->buf[0], 0, PAGE_SIZE); + /*sg_init_table(op->sgbounce, 8);*/ + for (i = 0; i < 8; i++) { + if (op->buf[i]) { + memset(op->sgbounce[i], 0, sizeof(struct scatterlist)); + sg_init_one(op->sgbounce[i], op->buf[i], PAGE_SIZE); + } + } + op->blen = 0; + } + + if (sg_src_len > 0) + goto start_copy; + + op->cursg = cursg; + +#ifdef DEBUG + dev_info(ss->dev, "%s end %llu %lu\n", __func__, op->byte_count, op->blen); +#endif + return 0; +} + +/* + * sun8i_hash_update: update hash engine + * + * Could be used for both SHA1 and MD5 + * Write data by step of 32bits and put then in the SS. + * + * Since we cannot leave partial data and hash state in the engine, + * we need to get the hash state at the end of this function. + * We can get the hash state every 64 bytes + * + * So the first work is to get the number of bytes to write to SS modulo 64 + * The extra bytes will go to a temporary buffer op->buf storing op->blen bytes + * + * So at the begin of update() + * if op->blen + areq->nbytes < 64 + * => all data will be written to wait buffer (op->buf) and end=0 + * if not, write all data from op->buf to the device and position end to + * complete to 64bytes + * + * example 1: + * update1 60o => op->blen=60 + * update2 60o => need one more word to have 64 bytes + * end=4 + * so write all data from op->buf and one word of SGs + * write remaining data in op->buf + * final state op->blen=56 + */ +int sun8i_hash(struct ahash_request *areq) +{ + struct sun8i_hash_reqctx *op = ahash_request_ctx(areq); + struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq); + struct sun8i_tfm_ctx *tfmctx = crypto_ahash_ctx(tfm); + struct sun8i_ss_ctx *ss = tfmctx->ss; + struct ahash_alg *alg = __crypto_ahash_alg(tfm->base.__crt_alg); + struct sun8i_ss_alg_template *algt; + int err = 0; + int no_chunk = 1; + struct scatterlist *sg; + unsigned int index, padlen; + int j; + int zeros; + __be64 bits; + int digestsize; + int bs = 64; + int cursg; + int i; + int rawdata = 0; + + algt = container_of(alg, struct sun8i_ss_alg_template, alg.hash); + digestsize = algt->alg.hash.halg.digestsize; + if (digestsize == SHA224_DIGEST_SIZE) + digestsize = SHA256_DIGEST_SIZE; + if (digestsize == SHA384_DIGEST_SIZE) + digestsize = SHA512_DIGEST_SIZE; + bs = algt->alg.hash.halg.base.cra_blocksize; + + if (!op->src_sg) + op->src_sg = areq->src; + + i = sg_nents(op->src_sg); + if (i > 7) { + dev_err(ss->dev, "MAXIMUM SG %d\n", i); + return -EINVAL; + } + +#ifdef DEBUG + dev_info(ss->dev, "%s nbytes=%u flags=%x blen=%lu %s\n", __func__, areq->nbytes, + op->flags, op->blen, crypto_tfm_alg_name(areq->base.tfm)); +#endif + + if ((op->flags & SS_HASH_UPDATE) == 0) + goto hash_final2; + + /* If we cannot work on a full block and more data could come, bounce data */ + if (op->blen + areq->nbytes < bs && (op->flags & SS_HASH_FINAL) == 0) + return sun8i_ss_hashtask_bounce(areq); + + /* does the data need to be bounced ? */ + sg = op->src_sg; + while (sg && no_chunk == 1) { + if ((sg->length % 4) != 0) + no_chunk = 0; + if (!IS_ALIGNED(sg->offset, sizeof(u32))) { +#ifdef DEBUG + dev_info(ss->dev, "Align problem on src\n"); +#endif + no_chunk = 0; + } + sg = sg_next(sg); + } + + if (no_chunk == 0 || areq->nbytes == 0 || (areq->nbytes % bs != 0) || op->blen > 0) { + sun8i_ss_hashtask_bounce(areq); + } else { + sun8i_ss_hashtask_zc(areq); + if (areq->nbytes >= bs && (op->flags & SS_HASH_FINAL) == 0) { + op->cursg--; + j = areq->nbytes - areq->nbytes % bs; +#ifdef DEBUG + dev_info(ss->dev, "Will upload %d of %u\n", j, areq->nbytes); +#endif + sun8i_ss_do_task(areq, j); + if (op->buf[0]) + memset(op->buf[0], 0, PAGE_SIZE); + op->cursg = 0; + op->byte_count += j; + op->blen = 0; + for (i = 0; i < 8; i++) { + /*dev_info(ss->dev, "Clean %d f=%d %p %p\n", i, op->sgflag[i], op->buf[i], op->sgbounce[i]);*/ + /* If sg is a bounce sg, zero it*/ + if (op->buf[i] && op->sgflag[i] == 0) { + if (op->sgbounce[i]) { + memset(op->sgbounce[i], 0, sizeof(struct scatterlist)); + sg_init_one(op->sgbounce[i], + op->buf[i], PAGE_SIZE); + } + } else { + op->sgflag[i] = 0; + } + } + return 0; + } + /*op->blen = areq->nbytes;*/ + rawdata = areq->nbytes; /*TODO a retablir avec le zc ?*/ + } +/* + if (op->blen >= bs && (op->flags & SS_HASH_FINAL) == 0) { + j = op->blen - op->blen % bs; + dev_info(ss->dev, "Will upload %d of %lu\n", j, op->blen); + }*/ + + if ((op->flags & SS_HASH_FINAL) == 0) + return 0; + +hash_final2: + + cursg = op->blen / PAGE_SIZE; + cursg = op->cursg; + if (!op->buf[cursg]) { + /*dev_err(ss->dev, "No buffer on sg %d\n", cursg);*/ + op->buf[cursg] = kzalloc(PAGE_SIZE, GFP_KERNEL | GFP_ATOMIC); + if (!op->buf[cursg]) + return -ENOMEM; + if (!op->sgbounce[cursg]) + op->sgbounce[cursg] = kzalloc(sizeof(*op->sgbounce[cursg]), + GFP_KERNEL | GFP_ATOMIC); + if (!op->sgbounce[cursg]) + return -ENOMEM; + sg_init_one(op->sgbounce[cursg], op->buf[cursg], PAGE_SIZE); + /*dev_info(ss->dev, "Alloc SG %d %lu\n", cursg, PAGE_SIZE);*/ + /*return -EINVAL;*/ + } + + j = op->blen; + op->byte_count += (op->blen / 4) * 4 + rawdata; + op->buf[cursg][j] = 1 << 7; + j += 4; + + if (op->mode == CE_OP_MD5 || op->mode == CE_OP_SHA1 || + op->mode == CE_OP_SHA224 || op->mode == CE_OP_SHA256) { + index = (op->byte_count + 4) & 0x3f; + op->byte_count += op->blen % 4; + padlen = (index < 56) ? (56 - index) : (120 - index); + zeros = padlen; + } else { + op->byte_count += op->blen % 4; + index = (op->byte_count + 4) & 0x7f; + padlen = (index < 112) ? (112 - index) : (240 - index); + zeros = padlen; + } + /*memset(op->buf + j, 0, zeros);*/ + j += zeros; + + /* TODO use switch */ + if (op->mode == CE_OP_MD5) { + ((u32 *)op->buf[cursg])[j / 4] = (op->byte_count << 3) & 0xffffffff; + j += 4; + ((u32 *)op->buf[cursg])[j / 4] = (op->byte_count >> 29) & 0xffffffff; + j += 4; + } else { + if (op->mode == CE_OP_SHA1 || op->mode == CE_OP_SHA224 || + op->mode == CE_OP_SHA256) { + bits = cpu_to_be64(op->byte_count << 3); + ((u32 *)op->buf[cursg])[j / 4] = bits & 0xffffffff; + j += 4; + ((u32 *)op->buf[cursg])[j / 4] = (bits >> 32) & 0xffffffff; + j += 4; + } else { + bits = cpu_to_be64(op->byte_count >> 61 | op->byte_count2 << 3); + ((u32 *)op->buf[cursg])[j / 4] = bits & 0xffffffff; + j += 4; + ((u32 *)op->buf[cursg])[j / 4] = (bits >> 32) & 0xffffffff; + j += 4; + bits = cpu_to_be64(op->byte_count << 3); + ((u32 *)op->buf[cursg])[j / 4] = bits & 0xffffffff; + j += 4; + ((u32 *)op->buf[cursg])[j / 4] = (bits >> 32) & 0xffffffff; + j += 4; + } + } + + /*dev_info(ss->dev, "Plop j=%d rawdata=%d\n", j, rawdata);*/ + sun8i_ss_do_task(areq, j + rawdata); + + if ((op->flags & SS_HASH_FINAL) == 0) { + op->byte_count += j; + memset(op->buf[0], 0, PAGE_SIZE); + /*sg_init_table(op->sgbounce, 8);*/ + for (i = 0; i < 8; i++) { + if (op->buf[i]) { + memset(op->sgbounce[i], 0, + sizeof(struct scatterlist)); + sg_init_one(op->sgbounce[i], op->buf[i], + PAGE_SIZE); + } + } + op->blen = 0; + } else { + memcpy(areq->result, op->hash, digestsize); + /* clean allocated */ + /*dev_info(ss->dev, "Clean %p\n", op->hash);*/ + kfree(op->hash); + op->hash = NULL; + for (i = 0; i < 8; i++) { + kfree(op->buf[i]); + op->buf[i] = NULL; + } + } + + return err; +} + +int sun8i_hash_final(struct ahash_request *areq) +{ + struct sun8i_hash_reqctx *op = ahash_request_ctx(areq); + struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq); + struct sun8i_tfm_ctx *tfmctx = crypto_ahash_ctx(tfm); + struct sun8i_ss_ctx *ss = tfmctx->ss; + int e = get_engine_number(ss); + + op->flow = e; + op->flags = SS_HASH_FINAL; + return crypto_transfer_hash_request_to_engine(ss->engines[e], areq); +} + +int sun8i_hash_update(struct ahash_request *areq) +{ + struct sun8i_hash_reqctx *op = ahash_request_ctx(areq); + struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq); + struct sun8i_tfm_ctx *tfmctx = crypto_ahash_ctx(tfm); + struct sun8i_ss_ctx *ss = tfmctx->ss; + int e = get_engine_number(ss); + + op->flow = e; + op->flags = SS_HASH_UPDATE; + return crypto_transfer_hash_request_to_engine(ss->engines[e], areq); + return sun8i_hash(areq); +} + +/* sun8i_hash_finup: finalize hashing operation after an update */ +int sun8i_hash_finup(struct ahash_request *areq) +{ + struct sun8i_hash_reqctx *op = ahash_request_ctx(areq); + struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq); + struct sun8i_tfm_ctx *tfmctx = crypto_ahash_ctx(tfm); + struct sun8i_ss_ctx *ss = tfmctx->ss; + int e = get_engine_number(ss); + + op->flow = e; + op->flags = SS_HASH_UPDATE | SS_HASH_FINAL; + return crypto_transfer_hash_request_to_engine(ss->engines[e], areq); + return sun8i_hash(areq); +} + +/* combo of init/update/final functions */ +int sun8i_hash_digest(struct ahash_request *areq) +{ + int err; + struct sun8i_hash_reqctx *op = ahash_request_ctx(areq); + struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq); + struct sun8i_tfm_ctx *tfmctx = crypto_ahash_ctx(tfm); + struct sun8i_ss_ctx *ss = tfmctx->ss; + int e = get_engine_number(ss); + + err = sun8i_hash_init(areq); + if (err != 0) + return err; + + op->flow = e; + op->flags = SS_HASH_UPDATE | SS_HASH_FINAL; + return crypto_transfer_hash_request_to_engine(ss->engines[e], areq); + return sun8i_hash(areq); +} diff --git a/drivers/staging/sun8i-ss/sun8i-ce-hwrng.c b/drivers/staging/sun8i-ss/sun8i-ce-hwrng.c new file mode 100644 index 0000000..667f266 --- /dev/null +++ b/drivers/staging/sun8i-ss/sun8i-ce-hwrng.c @@ -0,0 +1,170 @@ +#include "sun8i-ss.h" + +static int sun8i_ce_seed(struct sun8i_ce_hwrng *sch) +{ + sch->seed = kmalloc(sch->seedsize, GFP_KERNEL | GFP_DMA | GFP_ATOMIC); + if (!sch->seed) + return -ENOMEM; + + dev_info(sch->ss->dev, "%s\n", __func__); + get_random_bytes(sch->seed, sch->seedsize); + return 0; +} + +static void sun8i_ce_schedule_async_seed(struct random_ready_callback *rdy) +{ + struct sun8i_ce_hwrng *sch; + struct sun8i_ss_ctx *ss; + + sch = container_of(rdy, struct sun8i_ce_hwrng, random_ready); + ss = sch->ss; + + dev_info(ss->dev, "%s\n", __func__); + sun8i_ce_seed(sch); +} + +static int sun8i_ce_hwrng_init(struct hwrng *hwrng) +{ + struct sun8i_ss_ctx *ss; + struct sun8i_ce_hwrng *sch; + int ret = 0; + + sch = container_of(hwrng, struct sun8i_ce_hwrng, hwrng); + ss = sch->ss; + dev_info(ss->dev, "%s\n", __func__); + + if (sch->seedsize > 0) { + sch->random_ready.owner = THIS_MODULE; + sch->random_ready.func = sun8i_ce_schedule_async_seed; + + ret = add_random_ready_callback(&sch->random_ready); + dev_info(ss->dev, "%s rready=%d\n", __func__, ret); + + switch (ret) { + case 0: + break; + case -EALREADY: + /* Random pool is ready, seed now */ + ret = sun8i_ce_seed(sch); + sch->random_ready.func = NULL; + break; + default: + sch->random_ready.func = NULL; + } + } + return ret; +} + +static int sun8i_ce_hwrng_read(struct hwrng *hwrng, void *buf, + size_t max, bool wait) +{ + size_t len; + int flow = 3, ret; + struct ss_task *cet; + struct sun8i_ss_ctx *ss; + struct sun8i_ce_hwrng *sch; + void *data; + + /* TODO get flow number */ + sch = container_of(hwrng, struct sun8i_ce_hwrng, hwrng); + ss = sch->ss; + + if (sch->seedsize && !sch->seed) { + dev_err(ss->dev, "Not seeded\n"); + return -EAGAIN; + } + data = kmalloc(sch->datasize, GFP_KERNEL | GFP_DMA); + if (!data) + return -ENOMEM; + + len = min_t(size_t, max, sch->datasize); + + /*pr_info("%s %u (%u %u)\n", sch->name, max, sch->seedsize, sch->datasize);*/ + + mutex_lock(&ss->chanlock[flow]); + + cet = ss->tl[flow]; + memset(cet, 0, sizeof(struct ss_task)); + cet->t_id = flow; + cet->t_common_ctl = sch->ce_op | BIT(31); + cet->t_dlen = sch->datasize / 4; + + cet->t_dst[0].addr = dma_map_single(ss->dev, data, sch->datasize, + DMA_FROM_DEVICE); + if (dma_mapping_error(ss->dev, cet->t_dst[0].addr)) { + dev_err(ss->dev, "Cannot DMA MAP DST DATA\n"); + ret = -EFAULT; + goto fail; + } + cet->t_dst[0].len = sch->datasize / 4; + + cet->t_key = cet->t_dst[0].addr; + if (sch->seed) { + cet->t_iv = dma_map_single(ss->dev, sch->seed, sch->seedsize, + DMA_TO_DEVICE); + if (dma_mapping_error(ss->dev, cet->t_iv)) { + dev_err(ss->dev, "Cannot DMA MAP SEED\n"); + ret = -EFAULT; + goto ce_rng_iv_err; + } + } + + ret = sun8i_ce_run_task(ss, flow, sch->hwrng.name); + + if (sch->seed) + dma_unmap_single(ss->dev, cet->t_iv, sch->seedsize, + DMA_TO_DEVICE); +ce_rng_iv_err: + dma_unmap_single(ss->dev, cet->t_dst[0].addr, sch->datasize, + DMA_FROM_DEVICE); + +fail: + mutex_unlock(&ss->chanlock[flow]); + if (!ret) { + memcpy(buf, data, len); + /*print_hex_dump(KERN_INFO, "RNG ", DUMP_PREFIX_NONE, 16, 1, data, + sch->datasize, false);*/ + } + kzfree(data); + if (ret) + return ret; + + return len; +} + +int sun8i_ce_hwrng_register(struct sun8i_ce_hwrng *h, const char *name, + unsigned int seedsize, unsigned int datasize, + u32 ce_op, struct sun8i_ss_ctx *ss) +{ + h->name = name; + h->ce_op = ce_op; + h->ss = ss; + h->seedsize = seedsize; + h->datasize = datasize; + + h->hwrng.name = name; + h->hwrng.init = sun8i_ce_hwrng_init; + h->hwrng.read = sun8i_ce_hwrng_read; + h->hwrng.quality = 1000; + + dev_info(ss->dev, "Registered %s\n", name); + + return hwrng_register(&h->hwrng); +} + +void sun8i_ce_hwrng_unregister(struct hwrng *hwrng) +{ + struct sun8i_ce_hwrng *sch; + + if (!hwrng) + return; + + sch = container_of(hwrng, struct sun8i_ce_hwrng, hwrng); + + if (sch->seedsize && sch->random_ready.func) + del_random_ready_callback(&sch->random_ready); + + kfree(sch->seed); + + hwrng_unregister(hwrng); +} diff --git a/drivers/staging/sun8i-ss/sun8i-ce-rsa.c b/drivers/staging/sun8i-ss/sun8i-ce-rsa.c new file mode 100644 index 0000000..bd664a7 --- /dev/null +++ b/drivers/staging/sun8i-ss/sun8i-ce-rsa.c @@ -0,0 +1,351 @@ +/* + * sun8i-ce-cipher.c - hardware cryptographic accelerator for + * Allwinner H3/A64 SoC + * + * Copyright (C) 2016-2017 Corentin LABBE + * + * This file add support for AES cipher with 128,192,256 bits keysize in + * CBC and ECB mode. + * + * You could find a link for the datasheet in Documentation/arm/sunxi/README + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sun8i-ss.h" + +int sun8i_rsa_init(struct crypto_akcipher *tfm) +{ + struct sun8i_tfm_rsa_ctx *ctx = akcipher_tfm_ctx(tfm); + struct akcipher_alg *alg = crypto_akcipher_alg(tfm); + struct sun8i_ss_alg_template *algt; + + algt = container_of(alg, struct sun8i_ss_alg_template, alg.rsa); + ctx->ss = algt->ss; + + dev_info(ctx->ss->dev, "%s\n", __func__); + + ctx->fallback = crypto_alloc_akcipher("rsa", 0, CRYPTO_ALG_NEED_FALLBACK); + if (IS_ERR(ctx->fallback)) { + dev_err(ctx->ss->dev, "ERROR: Cannot allocate fallback\n"); + return PTR_ERR(ctx->fallback); + } + /*dev_info(ctx->ss->dev, "Use %s as fallback\n", ctx->fallback->base.cra_driver_name);*/ + + return 0; +} + +void sun8i_rsa_exit(struct crypto_akcipher *tfm) +{ + struct sun8i_tfm_rsa_ctx *ctx = akcipher_tfm_ctx(tfm); + + dev_info(ctx->ss->dev, "%s\n", __func__); + crypto_free_akcipher(ctx->fallback); +} + +static inline u8 *caam_read_raw_data(const u8 *buf, size_t *nbytes) +{ + u8 *val; + + while (!*buf && *nbytes) { + buf++; + (*nbytes)--; + } + + val = kzalloc(*nbytes, GFP_DMA | GFP_KERNEL); + if (!val) + return NULL; + + memcpy(val, buf, *nbytes); + return val; +} + +/* IV is pubmodulus + * + * mode MUL(2) IV size + * mode EXP(0) key size (so key is modulus ?) + */ +int sun8i_rsa_encrypt(struct akcipher_request *req) +{ + struct crypto_akcipher *tfm = crypto_akcipher_reqtfm(req); + struct sun8i_tfm_rsa_ctx *ctx = akcipher_tfm_ctx(tfm); + int flow = 0; + struct ss_task *cet; + struct sun8i_ss_ctx *ss = ctx->ss; + int err = 0; + u8 *modulus; + int nr_sgs, nr_sgd; + u32 v; + int i; + unsigned int todo, len; + struct scatterlist *sg; + void *sgb, *exp, *tmp; + u8 *p; + u8 *s, *t; + u8 u; + + dev_info(ctx->ss->dev, "%s modulus %zu e=%zu d=%zu c=%zu slen=%u dlen=%u\n", __func__, + ctx->rsa_key.n_sz, ctx->rsa_key.e_sz, ctx->rsa_key.d_sz, + ctx->rsa_key.n_sz, + req->src_len, req->dst_len); + + cet = ctx->ss->tl[flow]; + memset(cet, 0, sizeof(struct ss_task)); + + cet->t_id = flow; + cet->t_common_ctl = 32 | BIT(31); +#define RSA_LENDIV 4 + cet->t_dlen = req->src_len / RSA_LENDIV; + + modulus = caam_read_raw_data(ctx->rsa_key.n, &ctx->rsa_key.n_sz); + if (!modulus) { + dev_err(ss->dev, "Cannot get modulus\n"); + err = -EFAULT; + goto theend; + } + + dev_info(ss->dev, "Final modulus size %u\n", ctx->rsa_key.n_sz); + + exp = kzalloc(ctx->rsa_key.n_sz, GFP_KERNEL | GFP_DMA); + if (!exp) + return -ENOMEM; + /*memset(exp, 0xFF, ctx->rsa_key.n_sz);*/ + memcpy(exp, ctx->rsa_key.e, ctx->rsa_key.e_sz); + p = exp; + /*p[0] = 0x01;*/ + /*p[3] = 3;*/ + /*p[3] = 0x11;*/ + print_hex_dump(KERN_INFO, "EXP ", DUMP_PREFIX_NONE, 16, 1, exp, ctx->rsa_key.n_sz, false); + + /*cet->t_key = dma_map_single(ss->dev, ctx->rsa_key.e, ctx->rsa_key.e_sz, DMA_TO_DEVICE);*/ + /*cet->t_key = dma_map_single(ss->dev, modulus, ctx->rsa_key.n_sz, DMA_TO_DEVICE);*/ + cet->t_key = dma_map_single(ss->dev, exp, ctx->rsa_key.n_sz, DMA_TO_DEVICE); + if (dma_mapping_error(ss->dev, cet->t_key)) { + dev_err(ss->dev, "Cannot DMA MAP KEY\n"); + err = -EFAULT; + goto theend; + } + + tmp = kzalloc(ctx->rsa_key.n_sz, GFP_KERNEL | GFP_DMA); + memcpy(tmp, modulus, ctx->rsa_key.n_sz); + s = modulus; + t = tmp; + for (i = 0; i < ctx->rsa_key.n_sz; i++) + s[i] = t[ctx->rsa_key.n_sz - i - 1]; + i = 0; + while (i < ctx->rsa_key.n_sz) { + u = s[i]; + s[i] = s[i + 3]; + s[i + 3] = u; + u = s[i + 1]; + s[i + 1] = s[i + 2]; + s[i + 2] = u; + i += 4; + } + + cet->t_iv = dma_map_single(ss->dev, modulus, ctx->rsa_key.n_sz, DMA_TO_DEVICE); + /*cet->t_iv = dma_map_single(ss->dev, exp, ctx->rsa_key.n_sz, DMA_TO_DEVICE);*/ + /*cet->t_iv = dma_map_single(ss->dev, ctx->rsa_key.e, ctx->rsa_key.e_sz, DMA_TO_DEVICE);*/ + if (dma_mapping_error(ss->dev, cet->t_iv)) { + dev_err(ss->dev, "Cannot DMA MAP IV\n"); + err = -EFAULT; + goto theend; + } + + print_hex_dump(KERN_INFO, "KEY ", DUMP_PREFIX_NONE, 16, 1, ctx->rsa_key.e, ctx->rsa_key.e_sz, false); + + print_hex_dump(KERN_INFO, "MOD ", DUMP_PREFIX_NONE, 16, 1, modulus, ctx->rsa_key.n_sz, false); + +/* + nr_sgs = dma_map_sg(ss->dev, req->src, sg_nents(req->src), DMA_TO_DEVICE); + if (nr_sgs < 0) { + dev_err(ss->dev, "Cannot DMA MAP src\n"); + err = -EFAULT; + goto theend; + } +*/ + sgb = kzalloc(ctx->rsa_key.n_sz, GFP_KERNEL | GFP_DMA); + if (!sgb) + return -ENOMEM; + memset(sgb, 0xFF, ctx->rsa_key.n_sz); + err = sg_copy_to_buffer(req->src, sg_nents(req->src), sgb, req->src_len); +/* + tmp = kzalloc(ctx->rsa_key.n_sz, GFP_KERNEL | GFP_DMA); + memcpy(tmp, sgb, ctx->rsa_key.n_sz); + s = sgb; + t = tmp; + for (i = 0; i < ctx->rsa_key.n_sz; i++) + s[i] = t[ctx->rsa_key.n_sz - i - 1]; +*/ + print_hex_dump(KERN_INFO, "SRC ", DUMP_PREFIX_NONE, 16, 1, sgb, ctx->rsa_key.n_sz, false); + + cet->t_src[0].addr = dma_map_single(ss->dev, sgb, ctx->rsa_key.n_sz, DMA_TO_DEVICE); + if (dma_mapping_error(ss->dev, cet->t_src[0].addr)) { + dev_err(ss->dev, "Cannot DMA MAP SRC\n"); + err = -EFAULT; + goto theend; + } + + nr_sgd = dma_map_sg(ss->dev, req->dst, sg_nents(req->dst), DMA_FROM_DEVICE); + if (nr_sgd < 0) { + dev_err(ss->dev, "Cannot DMA MAP dst\n"); + err = -EFAULT; + goto theend; + } +/* + len = req->src_len; + for_each_sg(req->src, sg, nr_sgs, i) { + cet->t_src[i].addr = sg_dma_address(sg); + todo = min(len, sg_dma_len(sg)); + cet->t_src[i].len = todo / RSA_LENDIV; + dev_info(ss->dev, "SRC %d %u\n", i, todo); + len -= todo; + }*/ + + req->dst_len = req->src_len; + req->dst_len = ctx->rsa_key.n_sz; + len = req->dst_len; + for_each_sg(req->dst, sg, nr_sgd, i) { + cet->t_dst[i].addr = sg_dma_address(sg); + todo = min(len, sg_dma_len(sg)); + cet->t_dst[i].len = todo / RSA_LENDIV; + dev_info(ss->dev, "DST %d %u\n", i, todo); + len -= todo; + } + + /* HACKS */ + /*cet->t_asym_ctl |= 2 << 16;*/ + switch (ctx->rsa_key.n_sz * 8) { + case 512: + dev_info(ss->dev, "RSA 512\n"); + break; + case 1024: + dev_info(ss->dev, "RSA 1024\n"); + cet->t_asym_ctl |= 1 << 28; + break; + case 2048: + dev_info(ss->dev, "RSA 2048\n"); + cet->t_asym_ctl |= 2 << 28; + break; + case 4096: + cet->t_asym_ctl |= 3 << 28; + break; + default: + dev_info(ss->dev, "RSA invalid\n"); + } + cet->t_src[0].len = ctx->rsa_key.n_sz / RSA_LENDIV; + /*cet->t_dst[0].len = ctx->rsa_key.n_sz / RSA_LENDIV;*/ + cet->t_dlen = ctx->rsa_key.n_sz / RSA_LENDIV; + + dev_info(ss->dev, "SRC %u\n", cet->t_src[0].len); + dev_info(ss->dev, "DST %u\n", cet->t_dst[0].len); + + dev_info(ss->dev, "CTL %x %x %x\n", cet->t_common_ctl, cet->t_sym_ctl, cet->t_asym_ctl); + + err = sun8i_ce_run_task(ss, flow, "RSA"); +/* + v = readl(ss->base + CE_ICR); + v |= 1 << flow; + writel(v, ss->base + CE_ICR); + + reinit_completion(&ss->chanlist[flow].complete); + writel(ss->ce_t_phy[flow], ss->base + CE_TDQ); + + ss->chanlist[flow].status = 0; + wmb(); + + writel(1, ss->base + CE_TLR); + + wait_for_completion_interruptible_timeout(&ss->chanlist[flow].complete, + msecs_to_jiffies(5000)); + + if (ss->chanlist[flow].status == 0) { + dev_err(ss->dev, "DMA timeout\n"); + err = -EINVAL; + } + + v = readl(ss->base + CE_ESR); + if (v) + dev_info(ss->dev, "CE ERROR %x\n", v); + else + err = 0; +*/ + /*dma_unmap_sg(ss->dev, req->src, nr_sgs, DMA_TO_DEVICE);*/ + dma_unmap_single(ss->dev, cet->t_src[0].addr, ctx->rsa_key.n_sz, DMA_TO_DEVICE); + dma_unmap_sg(ss->dev, req->dst, nr_sgd, DMA_FROM_DEVICE); + dma_unmap_single(ss->dev, cet->t_key, ctx->rsa_key.n_sz, DMA_TO_DEVICE); + dma_unmap_single(ss->dev, cet->t_iv, ctx->rsa_key.n_sz, DMA_TO_DEVICE); + + /*sg_copy_to_buffer(req->dst, sg_nents(req->dst), modulus, req->dst_len);*/ + /*print_hex_dump(KERN_INFO, "DST ", DUMP_PREFIX_NONE, 16, 1, modulus, ctx->rsa_key.n_sz, false);*/ + + kfree(modulus); +theend: + return err; +} + +int sun8i_rsa_decrypt(struct akcipher_request *req) +{ + pr_info("%s\n", __func__); + return 0; +} + +int sun8i_rsa_sign(struct akcipher_request *req) +{ + pr_info("%s\n", __func__); + return 0; +} + +int sun8i_rsa_verify(struct akcipher_request *req) +{ + pr_info("%s\n", __func__); + return 0; +} + +int sun8i_rsa_set_priv_key(struct crypto_akcipher *tfm, const void *key, unsigned int keylen) +{ + struct sun8i_tfm_rsa_ctx *ctx = akcipher_tfm_ctx(tfm); + int ret; + + pr_info("%s keylen=%u\n", __func__, keylen); + + ret = rsa_parse_priv_key(&ctx->rsa_key, key, keylen); + if (ret) { + dev_err(ctx->ss->dev, "Invalid key\n"); + return ret; + } + + return 0; +} + +int sun8i_rsa_set_pub_key(struct crypto_akcipher *tfm, const void *key, unsigned int keylen) +{ + pr_info("%s\n", __func__); + return 0; +} + +int sun8i_rsa_max_size(struct crypto_akcipher *tfm) +{ + pr_info("%s\n", __func__); + + return 4096 / 8; +} + diff --git a/drivers/staging/sun8i-ss/sun8i-ss.h b/drivers/staging/sun8i-ss/sun8i-ss.h new file mode 100644 index 0000000..6829f1a --- /dev/null +++ b/drivers/staging/sun8i-ss/sun8i-ss.h @@ -0,0 +1,290 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* CE Registers */ +#define CE_TDQ 0x00 +#define CE_CTR 0x04 +#define CE_ICR 0x08 +#define CE_ISR 0x0C +#define CE_TLR 0x10 +#define CE_TSR 0x14 +#define CE_ESR 0x18 +#define CE_CSSGR 0x1C +#define CE_CDSGR 0x20 +#define CE_CSAR 0x24 +#define CE_CDAR 0x28 +#define CE_TPR 0x2C + +/* Operation direction - bit 8 */ +#define SS_ENCRYPTION 0 +#define SS_DECRYPTION BIT(8) + +/* CE Method H3/A64 */ +#define CE_OP_AES 0 +#define CE_OP_DES 1 +#define CE_OP_3DES 2 +#define CE_OP_MD5 16 +#define CE_OP_SHA1 17 +#define CE_OP_SHA224 18 +#define CE_OP_SHA256 19 +#define CE_OP_SHA384 20 +#define CE_OP_SHA512 21 +#define CE_OP_TRNG 48 +#define CE_OP_PRNG 49 + +/* SS Method A83T */ +#define SS_OP_AES 0 +#define SS_OP_DES 1 +#define SS_OP_3DES 2 +#define SS_OP_MD5 3 +#define SS_OP_PRNG 4 +#define SS_OP_SHA1 6 +#define SS_OP_SHA224 7 +#define SS_OP_SHA256 8 + +/* A80/A83T SS Registers */ +#define SS_CTL_REG 0x00 +#define SS_INT_CTL_REG 0x04 +#define SS_INT_STA_REG 0x08 +#define SS_KEY_ADR_REG 0x10 +#define SS_IV_ADR_REG 0x18 +#define SS_SRC_ADR_REG 0x20 +#define SS_DST_ADR_REG 0x28 +#define SS_LEN_ADR_REG 0x30 + +#define CE_ID_HASH_MD5 1 +#define CE_ID_HASH_SHA1 2 +#define CE_ID_HASH_SHA224 3 +#define CE_ID_HASH_SHA256 4 +#define CE_ID_HASH_SHA384 5 +#define CE_ID_HASH_SHA512 6 +#define CE_ID_HASH_MAX 7 +#define CE_ID_NOTSUPP 0xFF + +#define CE_ID_CIPHER_AES 1 +#define CE_ID_CIPHER_DES 2 +#define CE_ID_CIPHER_3DES 3 +#define CE_ID_CIPHER_MAX 4 + +#define CE_ID_MODE_ECB 1 +#define CE_ID_MODE_CBC 2 +#define CE_ID_MODE_CTR 3 +#define CE_ID_MODE_CTS 4 +#define CE_ID_MODE_OFB 5 +#define CE_ID_MODE_CFB 6 +#define CE_ID_MODE_CBCMAC 7 +#define CE_ID_MODE_MAX 8 + +#define SS_AES_128BITS 0 +#define SS_AES_192BITS 1 +#define SS_AES_256BITS 2 + +#define CE_ECB 0 +#define CE_CBC BIT(8) + +#define SS_ECB 0 +#define SS_CBC BIT(13) + +#define TRNG_DATA_SIZE (256 / 8) +#define PRNG_DATA_SIZE (160 / 8) +#define PRNG_SEED_SIZE ((175 / 8) * 8) + +#define MAXCHAN 4 +#define MAX_SG 8 + +struct ce_variant { + char alg_hash[CE_ID_HASH_MAX]; + char alg_cipher[CE_ID_CIPHER_MAX]; + u32 op_mode[CE_ID_MODE_MAX]; + char prng; + char trng; + bool is_ss; +}; + +struct plop { + u32 addr; + u32 len; +} __packed; + +struct ss_task { + u32 t_id; + u32 t_common_ctl; + u32 t_sym_ctl; + u32 t_asym_ctl; + u32 t_key; + u32 t_iv; + u32 t_ctr; + u32 t_dlen; + struct plop t_src[MAX_SG]; + struct plop t_dst[MAX_SG]; + u32 next; + u32 reserved[3]; +} __packed __aligned(8); + +struct sun8i_ce_chan { + struct scatterlist *bounce_src; + struct scatterlist *bounce_dst; + void *bufsrc; + void *bufdst; + /* IV to use */ + void *bounce_iv; + void *next_iv; + unsigned int ivlen; + struct completion complete; + int status; + u32 method; + u32 op_dir; + u32 op_mode; + unsigned int keylen; +}; + +struct sun8i_ce_hwrng { + const char *name; + struct hwrng hwrng; + unsigned int datasize; + unsigned int seedsize; + void *seed; + u32 ce_op; + struct sun8i_ss_ctx *ss; + struct random_ready_callback random_ready; + struct work_struct seed_work; +}; + +struct sun8i_ss_ctx { + void __iomem *base; + void __iomem *nsbase; + int irq; + int ns_irq; + struct clk *busclk; + struct clk *ssclk; + struct reset_control *reset; + struct device *dev; + struct resource *res; + struct mutex mlock; /* control the use of the device */ + struct mutex chanlock[MAXCHAN]; + struct ss_task *tl[MAXCHAN] ____cacheline_aligned; + dma_addr_t ce_t_phy[MAXCHAN] ____cacheline_aligned; + struct sun8i_ce_chan chanlist[MAXCHAN]; + struct crypto_engine *engines[MAXCHAN]; + int flow; /* flow to use in next request */ + struct sun8i_ce_hwrng prng; + struct sun8i_ce_hwrng trng; + const struct ce_variant *variant; +}; + +struct sun8i_cipher_req_ctx { + u32 op_dir; + int flow; +}; + +struct sun8i_hash_reqctx { + struct scatterlist *sgbounce[MAX_SG]; + u32 mode; + u64 byte_count; + u64 byte_count2;/* for sha384 sha512*/ + u32 *hash; + char *buf[MAX_SG]; + int sgflag[MAX_SG]; + unsigned long blen; + unsigned int bsize; + int flags; + int cursg; + struct scatterlist *src_sg; + /*struct crypto_shash *fallback_tfm;*/ + int flow; +}; + +struct sun8i_tfm_ctx { + u32 *key; + u32 keylen; + u32 keymode; + struct sun8i_ss_ctx *ss; + struct crypto_blkcipher *fallback_tfm; +}; + +struct sun8i_tfm_rsa_ctx { + struct sun8i_ss_ctx *ss; + struct rsa_key rsa_key; + struct crypto_akcipher *fallback; +}; + +struct sun8i_ss_alg_template { + u32 type; + u32 mode; + u32 ce_algo_id; + u32 ce_blockmode; + const void *hash_init; + union { + struct crypto_alg crypto; + struct ahash_alg hash; + struct akcipher_alg rsa; + struct skcipher_alg skc; + } alg; + struct sun8i_ss_ctx *ss; +}; + +int sun8i_ss_cipher(struct ablkcipher_request *areq); +int sun8i_ss_thread(void *data); +int sun8i_ce_enqueue(struct crypto_async_request *areq, u32 type); + +int sun8i_hash_init(struct ahash_request *areq); +int sun8i_hash_export_md5(struct ahash_request *areq, void *out); +int sun8i_hash_import_md5(struct ahash_request *areq, const void *in); +int sun8i_hash_export_sha1(struct ahash_request *areq, void *out); +int sun8i_hash_import_sha1(struct ahash_request *areq, const void *in); +int sun8i_hash_export_sha224(struct ahash_request *areq, void *out); +int sun8i_hash_import_sha224(struct ahash_request *areq, const void *in); +int sun8i_hash_export_sha256(struct ahash_request *areq, void *out); +int sun8i_hash_import_sha256(struct ahash_request *areq, const void *in); +int sun8i_hash_export_sha512(struct ahash_request *areq, void *out); +int sun8i_hash_import_sha512(struct ahash_request *areq, const void *in); +int sun8i_hash_update(struct ahash_request *areq); +int sun8i_hash_finup(struct ahash_request *areq); +int sun8i_hash_digest(struct ahash_request *areq); +int sun8i_hash_final(struct ahash_request *areq); +int sun8i_hash_crainit(struct crypto_tfm *tfm); +int sun8i_hash_craexit(struct crypto_tfm *tfm); +int sun8i_hash(struct ahash_request *areq); + +int sun8i_ss_compact(struct scatterlist *sg, unsigned int len); +int sun8i_ss_bounce_dst(struct ablkcipher_request *areq, int flow); +int sun8i_ss_bounce_src(struct ablkcipher_request *areq, int flow); + +int sun8i_ss_aes_setkey(struct crypto_ablkcipher *tfm, const u8 *key, + unsigned int keylen); +int sun8i_ss_cipher_init(struct crypto_tfm *tfm); +void sun8i_ss_cipher_exit(struct crypto_tfm *tfm); +int sun8i_ss_cbc_aes_decrypt(struct ablkcipher_request *areq); +int sun8i_ss_cbc_aes_encrypt(struct ablkcipher_request *areq); +int handle_cipher_request(struct crypto_engine *engine, + struct ablkcipher_request *breq); + +int get_engine_number(struct sun8i_ss_ctx *ss); + +int sun8i_rsa_encrypt(struct akcipher_request *req); +int sun8i_rsa_decrypt(struct akcipher_request *req); +int sun8i_rsa_sign(struct akcipher_request *req); +int sun8i_rsa_verify(struct akcipher_request *req); +int sun8i_rsa_set_priv_key(struct crypto_akcipher *tfm, const void *key, + unsigned int keylen); +int sun8i_rsa_set_pub_key(struct crypto_akcipher *tfm, const void *key, + unsigned int keylen); +int sun8i_rsa_max_size(struct crypto_akcipher *tfm); +int sun8i_rsa_init(struct crypto_akcipher *tfm); +void sun8i_rsa_exit(struct crypto_akcipher *tfm); + +int sun8i_ce_run_task(struct sun8i_ss_ctx *ss, int flow, const char *name); + +/*int sun8i_ce_hwrng_register(struct hwrng *hwrng);*/ +void sun8i_ce_hwrng_unregister(struct hwrng *hwrng); +int sun8i_ce_hwrng_register(struct sun8i_ce_hwrng *h, const char *name, + unsigned int seed_size, unsigned int datasize, + u32 ce_op, struct sun8i_ss_ctx *ss);