mirror of
https://github.com/armbian/linux.git
synced 2026-01-06 10:13:00 -08:00
crypto: marvell/cesa - add a new driver for Marvell's CESA
The existing mv_cesa driver supports some features of the CESA IP but is quite limited, and reworking it to support new features (like involving the TDMA engine to offload the CPU) is almost impossible. This driver has been rewritten from scratch to take those new features into account. This commit introduce the base infrastructure allowing us to add support for DMA optimization. It also includes support for one hash (SHA1) and one cipher (AES) algorithm, and enable those features on the Armada 370 SoC. Other algorithms and platforms will be added later on. Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com> Signed-off-by: Arnaud Ebalard <arno@natisbad.org> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
This commit is contained in:
committed by
Herbert Xu
parent
1fa2e9ae1d
commit
f63601fd61
@@ -173,6 +173,21 @@ config CRYPTO_DEV_MV_CESA
|
||||
|
||||
Currently the driver supports AES in ECB and CBC mode without DMA.
|
||||
|
||||
config CRYPTO_DEV_MARVELL_CESA
|
||||
tristate "New Marvell's Cryptographic Engine driver"
|
||||
depends on (PLAT_ORION || ARCH_MVEBU || COMPILE_TEST) && HAS_DMA && HAS_IOMEM
|
||||
select CRYPTO_AES
|
||||
select CRYPTO_DES
|
||||
select CRYPTO_BLKCIPHER
|
||||
select CRYPTO_HASH
|
||||
select SRAM
|
||||
help
|
||||
This driver allows you to utilize the Cryptographic Engines and
|
||||
Security Accelerator (CESA) which can be found on the Armada 370.
|
||||
|
||||
This driver is aimed at replacing the mv_cesa driver. This will only
|
||||
happen once it has received proper testing.
|
||||
|
||||
config CRYPTO_DEV_NIAGARA2
|
||||
tristate "Niagara2 Stream Processing Unit driver"
|
||||
select CRYPTO_DES
|
||||
|
||||
@@ -9,6 +9,7 @@ obj-$(CONFIG_CRYPTO_DEV_HIFN_795X) += hifn_795x.o
|
||||
obj-$(CONFIG_CRYPTO_DEV_IMGTEC_HASH) += img-hash.o
|
||||
obj-$(CONFIG_CRYPTO_DEV_IXP4XX) += ixp4xx_crypto.o
|
||||
obj-$(CONFIG_CRYPTO_DEV_MV_CESA) += mv_cesa.o
|
||||
obj-$(CONFIG_CRYPTO_DEV_MARVELL_CESA) += marvell/
|
||||
obj-$(CONFIG_CRYPTO_DEV_MXS_DCP) += mxs-dcp.o
|
||||
obj-$(CONFIG_CRYPTO_DEV_NIAGARA2) += n2_crypto.o
|
||||
n2_crypto-y := n2_core.o n2_asm.o
|
||||
|
||||
2
drivers/crypto/marvell/Makefile
Normal file
2
drivers/crypto/marvell/Makefile
Normal file
@@ -0,0 +1,2 @@
|
||||
obj-$(CONFIG_CRYPTO_DEV_MARVELL_CESA) += marvell-cesa.o
|
||||
marvell-cesa-objs := cesa.o cipher.o hash.o
|
||||
417
drivers/crypto/marvell/cesa.c
Normal file
417
drivers/crypto/marvell/cesa.c
Normal file
@@ -0,0 +1,417 @@
|
||||
/*
|
||||
* Support for Marvell's Cryptographic Engine and Security Accelerator (CESA)
|
||||
* that can be found on the following platform: Orion, Kirkwood, Armada. This
|
||||
* driver supports the TDMA engine on platforms on which it is available.
|
||||
*
|
||||
* Author: Boris Brezillon <boris.brezillon@free-electrons.com>
|
||||
* Author: Arnaud Ebalard <arno@natisbad.org>
|
||||
*
|
||||
* This work is based on an initial version written by
|
||||
* Sebastian Andrzej Siewior < sebastian at breakpoint dot cc >
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/genalloc.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/mbus.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/of_irq.h>
|
||||
|
||||
#include "cesa.h"
|
||||
|
||||
struct mv_cesa_dev *cesa_dev;
|
||||
|
||||
static void mv_cesa_dequeue_req_unlocked(struct mv_cesa_engine *engine)
|
||||
{
|
||||
struct crypto_async_request *req, *backlog;
|
||||
struct mv_cesa_ctx *ctx;
|
||||
|
||||
spin_lock_bh(&cesa_dev->lock);
|
||||
backlog = crypto_get_backlog(&cesa_dev->queue);
|
||||
req = crypto_dequeue_request(&cesa_dev->queue);
|
||||
engine->req = req;
|
||||
spin_unlock_bh(&cesa_dev->lock);
|
||||
|
||||
if (!req)
|
||||
return;
|
||||
|
||||
if (backlog)
|
||||
backlog->complete(backlog, -EINPROGRESS);
|
||||
|
||||
ctx = crypto_tfm_ctx(req->tfm);
|
||||
ctx->ops->prepare(req, engine);
|
||||
ctx->ops->step(req);
|
||||
}
|
||||
|
||||
static irqreturn_t mv_cesa_int(int irq, void *priv)
|
||||
{
|
||||
struct mv_cesa_engine *engine = priv;
|
||||
struct crypto_async_request *req;
|
||||
struct mv_cesa_ctx *ctx;
|
||||
u32 status, mask;
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
|
||||
while (true) {
|
||||
int res;
|
||||
|
||||
mask = mv_cesa_get_int_mask(engine);
|
||||
status = readl(engine->regs + CESA_SA_INT_STATUS);
|
||||
|
||||
if (!(status & mask))
|
||||
break;
|
||||
|
||||
/*
|
||||
* TODO: avoid clearing the FPGA_INT_STATUS if this not
|
||||
* relevant on some platforms.
|
||||
*/
|
||||
writel(~status, engine->regs + CESA_SA_FPGA_INT_STATUS);
|
||||
writel(~status, engine->regs + CESA_SA_INT_STATUS);
|
||||
|
||||
ret = IRQ_HANDLED;
|
||||
spin_lock_bh(&engine->lock);
|
||||
req = engine->req;
|
||||
spin_unlock_bh(&engine->lock);
|
||||
if (req) {
|
||||
ctx = crypto_tfm_ctx(req->tfm);
|
||||
res = ctx->ops->process(req, status & mask);
|
||||
if (res != -EINPROGRESS) {
|
||||
spin_lock_bh(&engine->lock);
|
||||
engine->req = NULL;
|
||||
mv_cesa_dequeue_req_unlocked(engine);
|
||||
spin_unlock_bh(&engine->lock);
|
||||
ctx->ops->cleanup(req);
|
||||
local_bh_disable();
|
||||
req->complete(req, res);
|
||||
local_bh_enable();
|
||||
} else {
|
||||
ctx->ops->step(req);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int mv_cesa_queue_req(struct crypto_async_request *req)
|
||||
{
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
spin_lock_bh(&cesa_dev->lock);
|
||||
ret = crypto_enqueue_request(&cesa_dev->queue, req);
|
||||
spin_unlock_bh(&cesa_dev->lock);
|
||||
|
||||
if (ret != -EINPROGRESS)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < cesa_dev->caps->nengines; i++) {
|
||||
spin_lock_bh(&cesa_dev->engines[i].lock);
|
||||
if (!cesa_dev->engines[i].req)
|
||||
mv_cesa_dequeue_req_unlocked(&cesa_dev->engines[i]);
|
||||
spin_unlock_bh(&cesa_dev->engines[i].lock);
|
||||
}
|
||||
|
||||
return -EINPROGRESS;
|
||||
}
|
||||
|
||||
static int mv_cesa_add_algs(struct mv_cesa_dev *cesa)
|
||||
{
|
||||
int ret;
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < cesa->caps->ncipher_algs; i++) {
|
||||
ret = crypto_register_alg(cesa->caps->cipher_algs[i]);
|
||||
if (ret)
|
||||
goto err_unregister_crypto;
|
||||
}
|
||||
|
||||
for (i = 0; i < cesa->caps->nahash_algs; i++) {
|
||||
ret = crypto_register_ahash(cesa->caps->ahash_algs[i]);
|
||||
if (ret)
|
||||
goto err_unregister_ahash;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_unregister_ahash:
|
||||
for (j = 0; j < i; j++)
|
||||
crypto_unregister_ahash(cesa->caps->ahash_algs[j]);
|
||||
i = cesa->caps->ncipher_algs;
|
||||
|
||||
err_unregister_crypto:
|
||||
for (j = 0; j < i; j++)
|
||||
crypto_unregister_alg(cesa->caps->cipher_algs[j]);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void mv_cesa_remove_algs(struct mv_cesa_dev *cesa)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < cesa->caps->nahash_algs; i++)
|
||||
crypto_unregister_ahash(cesa->caps->ahash_algs[i]);
|
||||
|
||||
for (i = 0; i < cesa->caps->ncipher_algs; i++)
|
||||
crypto_unregister_alg(cesa->caps->cipher_algs[i]);
|
||||
}
|
||||
|
||||
static struct crypto_alg *armada_370_cipher_algs[] = {
|
||||
&mv_cesa_ecb_aes_alg,
|
||||
&mv_cesa_cbc_aes_alg,
|
||||
};
|
||||
|
||||
static struct ahash_alg *armada_370_ahash_algs[] = {
|
||||
&mv_sha1_alg,
|
||||
&mv_ahmac_sha1_alg,
|
||||
};
|
||||
|
||||
static const struct mv_cesa_caps armada_370_caps = {
|
||||
.nengines = 1,
|
||||
.cipher_algs = armada_370_cipher_algs,
|
||||
.ncipher_algs = ARRAY_SIZE(armada_370_cipher_algs),
|
||||
.ahash_algs = armada_370_ahash_algs,
|
||||
.nahash_algs = ARRAY_SIZE(armada_370_ahash_algs),
|
||||
};
|
||||
|
||||
static const struct of_device_id mv_cesa_of_match_table[] = {
|
||||
{ .compatible = "marvell,armada-370-crypto", .data = &armada_370_caps },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mv_cesa_of_match_table);
|
||||
|
||||
static int mv_cesa_get_sram(struct platform_device *pdev, int idx)
|
||||
{
|
||||
struct mv_cesa_dev *cesa = platform_get_drvdata(pdev);
|
||||
struct mv_cesa_engine *engine = &cesa->engines[idx];
|
||||
const char *res_name = "sram";
|
||||
struct resource *res;
|
||||
|
||||
engine->pool = of_get_named_gen_pool(cesa->dev->of_node,
|
||||
"marvell,crypto-srams",
|
||||
idx);
|
||||
if (engine->pool) {
|
||||
engine->sram = gen_pool_dma_alloc(engine->pool,
|
||||
cesa->sram_size,
|
||||
&engine->sram_dma);
|
||||
if (engine->sram)
|
||||
return 0;
|
||||
|
||||
engine->pool = NULL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (cesa->caps->nengines > 1) {
|
||||
if (!idx)
|
||||
res_name = "sram0";
|
||||
else
|
||||
res_name = "sram1";
|
||||
}
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||
res_name);
|
||||
if (!res || resource_size(res) < cesa->sram_size)
|
||||
return -EINVAL;
|
||||
|
||||
engine->sram = devm_ioremap_resource(cesa->dev, res);
|
||||
if (IS_ERR(engine->sram))
|
||||
return PTR_ERR(engine->sram);
|
||||
|
||||
engine->sram_dma = phys_to_dma(cesa->dev,
|
||||
(phys_addr_t)res->start);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mv_cesa_put_sram(struct platform_device *pdev, int idx)
|
||||
{
|
||||
struct mv_cesa_dev *cesa = platform_get_drvdata(pdev);
|
||||
struct mv_cesa_engine *engine = &cesa->engines[idx];
|
||||
|
||||
if (!engine->pool)
|
||||
return;
|
||||
|
||||
gen_pool_free(engine->pool, (unsigned long)engine->sram,
|
||||
cesa->sram_size);
|
||||
}
|
||||
|
||||
static int mv_cesa_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct mv_cesa_caps *caps = NULL;
|
||||
const struct mbus_dram_target_info *dram;
|
||||
const struct of_device_id *match;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct mv_cesa_dev *cesa;
|
||||
struct mv_cesa_engine *engines;
|
||||
struct resource *res;
|
||||
int irq, ret, i;
|
||||
u32 sram_size;
|
||||
|
||||
if (cesa_dev) {
|
||||
dev_err(&pdev->dev, "Only one CESA device authorized\n");
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
if (!dev->of_node)
|
||||
return -ENOTSUPP;
|
||||
|
||||
match = of_match_node(mv_cesa_of_match_table, dev->of_node);
|
||||
if (!match || !match->data)
|
||||
return -ENOTSUPP;
|
||||
|
||||
caps = match->data;
|
||||
|
||||
cesa = devm_kzalloc(dev, sizeof(*cesa), GFP_KERNEL);
|
||||
if (!cesa)
|
||||
return -ENOMEM;
|
||||
|
||||
cesa->caps = caps;
|
||||
cesa->dev = dev;
|
||||
|
||||
sram_size = CESA_SA_DEFAULT_SRAM_SIZE;
|
||||
of_property_read_u32(cesa->dev->of_node, "marvell,crypto-sram-size",
|
||||
&sram_size);
|
||||
if (sram_size < CESA_SA_MIN_SRAM_SIZE)
|
||||
sram_size = CESA_SA_MIN_SRAM_SIZE;
|
||||
|
||||
cesa->sram_size = sram_size;
|
||||
cesa->engines = devm_kzalloc(dev, caps->nengines * sizeof(*engines),
|
||||
GFP_KERNEL);
|
||||
if (!cesa->engines)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_init(&cesa->lock);
|
||||
crypto_init_queue(&cesa->queue, 50);
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
|
||||
cesa->regs = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(cesa->regs))
|
||||
return -ENOMEM;
|
||||
|
||||
dram = mv_mbus_dram_info_nooverlap();
|
||||
|
||||
platform_set_drvdata(pdev, cesa);
|
||||
|
||||
for (i = 0; i < caps->nengines; i++) {
|
||||
struct mv_cesa_engine *engine = &cesa->engines[i];
|
||||
char res_name[7];
|
||||
|
||||
engine->id = i;
|
||||
spin_lock_init(&engine->lock);
|
||||
|
||||
ret = mv_cesa_get_sram(pdev, i);
|
||||
if (ret)
|
||||
goto err_cleanup;
|
||||
|
||||
irq = platform_get_irq(pdev, i);
|
||||
if (irq < 0) {
|
||||
ret = irq;
|
||||
goto err_cleanup;
|
||||
}
|
||||
|
||||
/*
|
||||
* Not all platforms can gate the CESA clocks: do not complain
|
||||
* if the clock does not exist.
|
||||
*/
|
||||
snprintf(res_name, sizeof(res_name), "cesa%d", i);
|
||||
engine->clk = devm_clk_get(dev, res_name);
|
||||
if (IS_ERR(engine->clk)) {
|
||||
engine->clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(engine->clk))
|
||||
engine->clk = NULL;
|
||||
}
|
||||
|
||||
snprintf(res_name, sizeof(res_name), "cesaz%d", i);
|
||||
engine->zclk = devm_clk_get(dev, res_name);
|
||||
if (IS_ERR(engine->zclk))
|
||||
engine->zclk = NULL;
|
||||
|
||||
ret = clk_prepare_enable(engine->clk);
|
||||
if (ret)
|
||||
goto err_cleanup;
|
||||
|
||||
ret = clk_prepare_enable(engine->zclk);
|
||||
if (ret)
|
||||
goto err_cleanup;
|
||||
|
||||
engine->regs = cesa->regs + CESA_ENGINE_OFF(i);
|
||||
|
||||
writel(0, cesa->engines[i].regs + CESA_SA_INT_STATUS);
|
||||
writel(CESA_SA_CFG_STOP_DIG_ERR,
|
||||
cesa->engines[i].regs + CESA_SA_CFG);
|
||||
writel(engine->sram_dma & CESA_SA_SRAM_MSK,
|
||||
cesa->engines[i].regs + CESA_SA_DESC_P0);
|
||||
|
||||
ret = devm_request_threaded_irq(dev, irq, NULL, mv_cesa_int,
|
||||
IRQF_ONESHOT,
|
||||
dev_name(&pdev->dev),
|
||||
&cesa->engines[i]);
|
||||
if (ret)
|
||||
goto err_cleanup;
|
||||
}
|
||||
|
||||
cesa_dev = cesa;
|
||||
|
||||
ret = mv_cesa_add_algs(cesa);
|
||||
if (ret) {
|
||||
cesa_dev = NULL;
|
||||
goto err_cleanup;
|
||||
}
|
||||
|
||||
dev_info(dev, "CESA device successfully registered\n");
|
||||
|
||||
return 0;
|
||||
|
||||
err_cleanup:
|
||||
for (i = 0; i < caps->nengines; i++) {
|
||||
clk_disable_unprepare(cesa->engines[i].zclk);
|
||||
clk_disable_unprepare(cesa->engines[i].clk);
|
||||
mv_cesa_put_sram(pdev, i);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mv_cesa_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct mv_cesa_dev *cesa = platform_get_drvdata(pdev);
|
||||
int i;
|
||||
|
||||
mv_cesa_remove_algs(cesa);
|
||||
|
||||
for (i = 0; i < cesa->caps->nengines; i++) {
|
||||
clk_disable_unprepare(cesa->engines[i].zclk);
|
||||
clk_disable_unprepare(cesa->engines[i].clk);
|
||||
mv_cesa_put_sram(pdev, i);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver marvell_cesa = {
|
||||
.probe = mv_cesa_probe,
|
||||
.remove = mv_cesa_remove,
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "marvell-cesa",
|
||||
.of_match_table = mv_cesa_of_match_table,
|
||||
},
|
||||
};
|
||||
module_platform_driver(marvell_cesa);
|
||||
|
||||
MODULE_ALIAS("platform:mv_crypto");
|
||||
MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");
|
||||
MODULE_AUTHOR("Arnaud Ebalard <arno@natisbad.org>");
|
||||
MODULE_DESCRIPTION("Support for Marvell's cryptographic engine");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
554
drivers/crypto/marvell/cesa.h
Normal file
554
drivers/crypto/marvell/cesa.h
Normal file
File diff suppressed because it is too large
Load Diff
331
drivers/crypto/marvell/cipher.c
Normal file
331
drivers/crypto/marvell/cipher.c
Normal file
@@ -0,0 +1,331 @@
|
||||
/*
|
||||
* Cipher algorithms supported by the CESA: DES, 3DES and AES.
|
||||
*
|
||||
* Author: Boris Brezillon <boris.brezillon@free-electrons.com>
|
||||
* Author: Arnaud Ebalard <arno@natisbad.org>
|
||||
*
|
||||
* This work is based on an initial version written by
|
||||
* Sebastian Andrzej Siewior < sebastian at breakpoint dot cc >
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <crypto/aes.h>
|
||||
|
||||
#include "cesa.h"
|
||||
|
||||
struct mv_cesa_aes_ctx {
|
||||
struct mv_cesa_ctx base;
|
||||
struct crypto_aes_ctx aes;
|
||||
};
|
||||
|
||||
static void mv_cesa_ablkcipher_std_step(struct ablkcipher_request *req)
|
||||
{
|
||||
struct mv_cesa_ablkcipher_req *creq = ablkcipher_request_ctx(req);
|
||||
struct mv_cesa_ablkcipher_std_req *sreq = &creq->req.std;
|
||||
struct mv_cesa_engine *engine = sreq->base.engine;
|
||||
size_t len = min_t(size_t, req->nbytes - sreq->offset,
|
||||
CESA_SA_SRAM_PAYLOAD_SIZE);
|
||||
|
||||
len = sg_pcopy_to_buffer(req->src, creq->src_nents,
|
||||
engine->sram + CESA_SA_DATA_SRAM_OFFSET,
|
||||
len, sreq->offset);
|
||||
|
||||
sreq->size = len;
|
||||
mv_cesa_set_crypt_op_len(&sreq->op, len);
|
||||
|
||||
/* FIXME: only update enc_len field */
|
||||
if (!sreq->skip_ctx) {
|
||||
memcpy(engine->sram, &sreq->op, sizeof(sreq->op));
|
||||
sreq->skip_ctx = true;
|
||||
} else {
|
||||
memcpy(engine->sram, &sreq->op, sizeof(sreq->op.desc));
|
||||
}
|
||||
|
||||
mv_cesa_set_int_mask(engine, CESA_SA_INT_ACCEL0_DONE);
|
||||
writel(CESA_SA_CFG_PARA_DIS, engine->regs + CESA_SA_CFG);
|
||||
writel(CESA_SA_CMD_EN_CESA_SA_ACCL0, engine->regs + CESA_SA_CMD);
|
||||
}
|
||||
|
||||
static int mv_cesa_ablkcipher_std_process(struct ablkcipher_request *req,
|
||||
u32 status)
|
||||
{
|
||||
struct mv_cesa_ablkcipher_req *creq = ablkcipher_request_ctx(req);
|
||||
struct mv_cesa_ablkcipher_std_req *sreq = &creq->req.std;
|
||||
struct mv_cesa_engine *engine = sreq->base.engine;
|
||||
size_t len;
|
||||
|
||||
len = sg_pcopy_from_buffer(req->dst, creq->dst_nents,
|
||||
engine->sram + CESA_SA_DATA_SRAM_OFFSET,
|
||||
sreq->size, sreq->offset);
|
||||
|
||||
sreq->offset += len;
|
||||
if (sreq->offset < req->nbytes)
|
||||
return -EINPROGRESS;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mv_cesa_ablkcipher_process(struct crypto_async_request *req,
|
||||
u32 status)
|
||||
{
|
||||
struct ablkcipher_request *ablkreq = ablkcipher_request_cast(req);
|
||||
struct mv_cesa_ablkcipher_req *creq = ablkcipher_request_ctx(ablkreq);
|
||||
struct mv_cesa_ablkcipher_std_req *sreq = &creq->req.std;
|
||||
struct mv_cesa_engine *engine = sreq->base.engine;
|
||||
int ret;
|
||||
|
||||
ret = mv_cesa_ablkcipher_std_process(ablkreq, status);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
memcpy(ablkreq->info, engine->sram + CESA_SA_CRYPT_IV_SRAM_OFFSET,
|
||||
crypto_ablkcipher_ivsize(crypto_ablkcipher_reqtfm(ablkreq)));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mv_cesa_ablkcipher_step(struct crypto_async_request *req)
|
||||
{
|
||||
struct ablkcipher_request *ablkreq = ablkcipher_request_cast(req);
|
||||
|
||||
mv_cesa_ablkcipher_std_step(ablkreq);
|
||||
}
|
||||
|
||||
static inline void
|
||||
mv_cesa_ablkcipher_std_prepare(struct ablkcipher_request *req)
|
||||
{
|
||||
struct mv_cesa_ablkcipher_req *creq = ablkcipher_request_ctx(req);
|
||||
struct mv_cesa_ablkcipher_std_req *sreq = &creq->req.std;
|
||||
struct mv_cesa_engine *engine = sreq->base.engine;
|
||||
|
||||
sreq->size = 0;
|
||||
sreq->offset = 0;
|
||||
mv_cesa_adjust_op(engine, &sreq->op);
|
||||
memcpy(engine->sram, &sreq->op, sizeof(sreq->op));
|
||||
}
|
||||
|
||||
static inline void mv_cesa_ablkcipher_prepare(struct crypto_async_request *req,
|
||||
struct mv_cesa_engine *engine)
|
||||
{
|
||||
struct ablkcipher_request *ablkreq = ablkcipher_request_cast(req);
|
||||
struct mv_cesa_ablkcipher_req *creq = ablkcipher_request_ctx(ablkreq);
|
||||
|
||||
creq->req.base.engine = engine;
|
||||
|
||||
mv_cesa_ablkcipher_std_prepare(ablkreq);
|
||||
}
|
||||
|
||||
static inline void
|
||||
mv_cesa_ablkcipher_req_cleanup(struct crypto_async_request *req)
|
||||
{
|
||||
}
|
||||
|
||||
static const struct mv_cesa_req_ops mv_cesa_ablkcipher_req_ops = {
|
||||
.step = mv_cesa_ablkcipher_step,
|
||||
.process = mv_cesa_ablkcipher_process,
|
||||
.prepare = mv_cesa_ablkcipher_prepare,
|
||||
.cleanup = mv_cesa_ablkcipher_req_cleanup,
|
||||
};
|
||||
|
||||
static int mv_cesa_ablkcipher_cra_init(struct crypto_tfm *tfm)
|
||||
{
|
||||
struct mv_cesa_aes_ctx *ctx = crypto_tfm_ctx(tfm);
|
||||
|
||||
ctx->base.ops = &mv_cesa_ablkcipher_req_ops;
|
||||
|
||||
tfm->crt_ablkcipher.reqsize = sizeof(struct mv_cesa_ablkcipher_req);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mv_cesa_aes_setkey(struct crypto_ablkcipher *cipher, const u8 *key,
|
||||
unsigned int len)
|
||||
{
|
||||
struct crypto_tfm *tfm = crypto_ablkcipher_tfm(cipher);
|
||||
struct mv_cesa_aes_ctx *ctx = crypto_tfm_ctx(tfm);
|
||||
int remaining;
|
||||
int offset;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
ret = crypto_aes_expand_key(&ctx->aes, key, len);
|
||||
if (ret) {
|
||||
crypto_ablkcipher_set_flags(cipher, CRYPTO_TFM_RES_BAD_KEY_LEN);
|
||||
return ret;
|
||||
}
|
||||
|
||||
remaining = (ctx->aes.key_length - 16) / 4;
|
||||
offset = ctx->aes.key_length + 24 - remaining;
|
||||
for (i = 0; i < remaining; i++)
|
||||
ctx->aes.key_dec[4 + i] =
|
||||
cpu_to_le32(ctx->aes.key_enc[offset + i]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
mv_cesa_ablkcipher_std_req_init(struct ablkcipher_request *req,
|
||||
const struct mv_cesa_op_ctx *op_templ)
|
||||
{
|
||||
struct mv_cesa_ablkcipher_req *creq = ablkcipher_request_ctx(req);
|
||||
struct mv_cesa_ablkcipher_std_req *sreq = &creq->req.std;
|
||||
|
||||
sreq->base.type = CESA_STD_REQ;
|
||||
sreq->op = *op_templ;
|
||||
sreq->skip_ctx = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mv_cesa_ablkcipher_req_init(struct ablkcipher_request *req,
|
||||
struct mv_cesa_op_ctx *tmpl)
|
||||
{
|
||||
struct mv_cesa_ablkcipher_req *creq = ablkcipher_request_ctx(req);
|
||||
struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(req);
|
||||
unsigned int blksize = crypto_ablkcipher_blocksize(tfm);
|
||||
|
||||
if (!IS_ALIGNED(req->nbytes, blksize))
|
||||
return -EINVAL;
|
||||
|
||||
creq->src_nents = sg_nents_for_len(req->src, req->nbytes);
|
||||
creq->dst_nents = sg_nents_for_len(req->dst, req->nbytes);
|
||||
|
||||
mv_cesa_update_op_cfg(tmpl, CESA_SA_DESC_CFG_OP_CRYPT_ONLY,
|
||||
CESA_SA_DESC_CFG_OP_MSK);
|
||||
|
||||
return mv_cesa_ablkcipher_std_req_init(req, tmpl);
|
||||
}
|
||||
|
||||
static int mv_cesa_aes_op(struct ablkcipher_request *req,
|
||||
struct mv_cesa_op_ctx *tmpl)
|
||||
{
|
||||
struct mv_cesa_aes_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
|
||||
int ret, i;
|
||||
u32 *key;
|
||||
u32 cfg;
|
||||
|
||||
cfg = CESA_SA_DESC_CFG_CRYPTM_AES;
|
||||
|
||||
if (mv_cesa_get_op_cfg(tmpl) & CESA_SA_DESC_CFG_DIR_DEC)
|
||||
key = ctx->aes.key_dec;
|
||||
else
|
||||
key = ctx->aes.key_enc;
|
||||
|
||||
for (i = 0; i < ctx->aes.key_length / sizeof(u32); i++)
|
||||
tmpl->ctx.blkcipher.key[i] = cpu_to_le32(key[i]);
|
||||
|
||||
if (ctx->aes.key_length == 24)
|
||||
cfg |= CESA_SA_DESC_CFG_AES_LEN_192;
|
||||
else if (ctx->aes.key_length == 32)
|
||||
cfg |= CESA_SA_DESC_CFG_AES_LEN_256;
|
||||
|
||||
mv_cesa_update_op_cfg(tmpl, cfg,
|
||||
CESA_SA_DESC_CFG_CRYPTM_MSK |
|
||||
CESA_SA_DESC_CFG_AES_LEN_MSK);
|
||||
|
||||
ret = mv_cesa_ablkcipher_req_init(req, tmpl);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return mv_cesa_queue_req(&req->base);
|
||||
}
|
||||
|
||||
static int mv_cesa_ecb_aes_encrypt(struct ablkcipher_request *req)
|
||||
{
|
||||
struct mv_cesa_op_ctx tmpl;
|
||||
|
||||
mv_cesa_set_op_cfg(&tmpl,
|
||||
CESA_SA_DESC_CFG_CRYPTCM_ECB |
|
||||
CESA_SA_DESC_CFG_DIR_ENC);
|
||||
|
||||
return mv_cesa_aes_op(req, &tmpl);
|
||||
}
|
||||
|
||||
static int mv_cesa_ecb_aes_decrypt(struct ablkcipher_request *req)
|
||||
{
|
||||
struct mv_cesa_op_ctx tmpl;
|
||||
|
||||
mv_cesa_set_op_cfg(&tmpl,
|
||||
CESA_SA_DESC_CFG_CRYPTCM_ECB |
|
||||
CESA_SA_DESC_CFG_DIR_DEC);
|
||||
|
||||
return mv_cesa_aes_op(req, &tmpl);
|
||||
}
|
||||
|
||||
struct crypto_alg mv_cesa_ecb_aes_alg = {
|
||||
.cra_name = "ecb(aes)",
|
||||
.cra_driver_name = "mv-ecb-aes",
|
||||
.cra_priority = 300,
|
||||
.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
|
||||
CRYPTO_ALG_KERN_DRIVER_ONLY | CRYPTO_ALG_ASYNC,
|
||||
.cra_blocksize = AES_BLOCK_SIZE,
|
||||
.cra_ctxsize = sizeof(struct mv_cesa_aes_ctx),
|
||||
.cra_alignmask = 0,
|
||||
.cra_type = &crypto_ablkcipher_type,
|
||||
.cra_module = THIS_MODULE,
|
||||
.cra_init = mv_cesa_ablkcipher_cra_init,
|
||||
.cra_u = {
|
||||
.ablkcipher = {
|
||||
.min_keysize = AES_MIN_KEY_SIZE,
|
||||
.max_keysize = AES_MAX_KEY_SIZE,
|
||||
.setkey = mv_cesa_aes_setkey,
|
||||
.encrypt = mv_cesa_ecb_aes_encrypt,
|
||||
.decrypt = mv_cesa_ecb_aes_decrypt,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static int mv_cesa_cbc_aes_op(struct ablkcipher_request *req,
|
||||
struct mv_cesa_op_ctx *tmpl)
|
||||
{
|
||||
mv_cesa_update_op_cfg(tmpl, CESA_SA_DESC_CFG_CRYPTCM_CBC,
|
||||
CESA_SA_DESC_CFG_CRYPTCM_MSK);
|
||||
memcpy(tmpl->ctx.blkcipher.iv, req->info, AES_BLOCK_SIZE);
|
||||
|
||||
return mv_cesa_aes_op(req, tmpl);
|
||||
}
|
||||
|
||||
static int mv_cesa_cbc_aes_encrypt(struct ablkcipher_request *req)
|
||||
{
|
||||
struct mv_cesa_op_ctx tmpl;
|
||||
|
||||
mv_cesa_set_op_cfg(&tmpl, CESA_SA_DESC_CFG_DIR_ENC);
|
||||
|
||||
return mv_cesa_cbc_aes_op(req, &tmpl);
|
||||
}
|
||||
|
||||
static int mv_cesa_cbc_aes_decrypt(struct ablkcipher_request *req)
|
||||
{
|
||||
struct mv_cesa_op_ctx tmpl;
|
||||
|
||||
mv_cesa_set_op_cfg(&tmpl, CESA_SA_DESC_CFG_DIR_DEC);
|
||||
|
||||
return mv_cesa_cbc_aes_op(req, &tmpl);
|
||||
}
|
||||
|
||||
struct crypto_alg mv_cesa_cbc_aes_alg = {
|
||||
.cra_name = "cbc(aes)",
|
||||
.cra_driver_name = "mv-cbc-aes",
|
||||
.cra_priority = 300,
|
||||
.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
|
||||
CRYPTO_ALG_KERN_DRIVER_ONLY | CRYPTO_ALG_ASYNC,
|
||||
.cra_blocksize = AES_BLOCK_SIZE,
|
||||
.cra_ctxsize = sizeof(struct mv_cesa_aes_ctx),
|
||||
.cra_alignmask = 0,
|
||||
.cra_type = &crypto_ablkcipher_type,
|
||||
.cra_module = THIS_MODULE,
|
||||
.cra_init = mv_cesa_ablkcipher_cra_init,
|
||||
.cra_u = {
|
||||
.ablkcipher = {
|
||||
.min_keysize = AES_MIN_KEY_SIZE,
|
||||
.max_keysize = AES_MAX_KEY_SIZE,
|
||||
.ivsize = AES_BLOCK_SIZE,
|
||||
.setkey = mv_cesa_aes_setkey,
|
||||
.encrypt = mv_cesa_cbc_aes_encrypt,
|
||||
.decrypt = mv_cesa_cbc_aes_decrypt,
|
||||
},
|
||||
},
|
||||
};
|
||||
720
drivers/crypto/marvell/hash.c
Normal file
720
drivers/crypto/marvell/hash.c
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user