initial commit

This commit is contained in:
2026-08-09 22:38:33 -07:00
commit 9d0aeafebd
70668 changed files with 29808204 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_CRYPTO_DEV_MARVELL_CESA) += marvell-cesa.o
marvell-cesa-objs := cesa.o cipher.o hash.o tdma.o

View File

@@ -0,0 +1,622 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* 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 >
*/
#include <linux/delay.h>
#include <linux/dma-mapping.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"
/* Limit of the crypto queue before reaching the backlog */
#define CESA_CRYPTO_DEFAULT_MAX_QLEN 128
struct mv_cesa_dev *cesa_dev;
struct crypto_async_request *
mv_cesa_dequeue_req_locked(struct mv_cesa_engine *engine,
struct crypto_async_request **backlog)
{
struct crypto_async_request *req;
*backlog = crypto_get_backlog(&engine->queue);
req = crypto_dequeue_request(&engine->queue);
if (!req)
return NULL;
return req;
}
static void mv_cesa_rearm_engine(struct mv_cesa_engine *engine)
{
struct crypto_async_request *req = NULL, *backlog = NULL;
struct mv_cesa_ctx *ctx;
spin_lock_bh(&engine->lock);
if (!engine->req) {
req = mv_cesa_dequeue_req_locked(engine, &backlog);
engine->req = req;
}
spin_unlock_bh(&engine->lock);
if (!req)
return;
if (backlog)
backlog->complete(backlog, -EINPROGRESS);
ctx = crypto_tfm_ctx(req->tfm);
ctx->ops->step(req);
}
static int mv_cesa_std_process(struct mv_cesa_engine *engine, u32 status)
{
struct crypto_async_request *req;
struct mv_cesa_ctx *ctx;
int res;
req = engine->req;
ctx = crypto_tfm_ctx(req->tfm);
res = ctx->ops->process(req, status);
if (res == 0) {
ctx->ops->complete(req);
mv_cesa_engine_enqueue_complete_request(engine, req);
} else if (res == -EINPROGRESS) {
ctx->ops->step(req);
}
return res;
}
static int mv_cesa_int_process(struct mv_cesa_engine *engine, u32 status)
{
if (engine->chain.first && engine->chain.last)
return mv_cesa_tdma_process(engine, status);
return mv_cesa_std_process(engine, status);
}
static inline void
mv_cesa_complete_req(struct mv_cesa_ctx *ctx, struct crypto_async_request *req,
int res)
{
ctx->ops->cleanup(req);
local_bh_disable();
req->complete(req, res);
local_bh_enable();
}
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);
/* Process fetched requests */
res = mv_cesa_int_process(engine, status & mask);
ret = IRQ_HANDLED;
spin_lock_bh(&engine->lock);
req = engine->req;
if (res != -EINPROGRESS)
engine->req = NULL;
spin_unlock_bh(&engine->lock);
ctx = crypto_tfm_ctx(req->tfm);
if (res && res != -EINPROGRESS)
mv_cesa_complete_req(ctx, req, res);
/* Launch the next pending request */
mv_cesa_rearm_engine(engine);
/* Iterate over the complete queue */
while (true) {
req = mv_cesa_engine_dequeue_complete_request(engine);
if (!req)
break;
ctx = crypto_tfm_ctx(req->tfm);
mv_cesa_complete_req(ctx, req, 0);
}
}
return ret;
}
int mv_cesa_queue_req(struct crypto_async_request *req,
struct mv_cesa_req *creq)
{
int ret;
struct mv_cesa_engine *engine = creq->engine;
spin_lock_bh(&engine->lock);
ret = crypto_enqueue_request(&engine->queue, req);
if ((mv_cesa_req_get_type(creq) == CESA_DMA_REQ) &&
(ret == -EINPROGRESS || ret == -EBUSY))
mv_cesa_tdma_chain(engine, creq);
spin_unlock_bh(&engine->lock);
if (ret != -EINPROGRESS)
return ret;
mv_cesa_rearm_engine(engine);
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_skcipher(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_skcipher(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_skcipher(cesa->caps->cipher_algs[i]);
}
static struct skcipher_alg *orion_cipher_algs[] = {
&mv_cesa_ecb_des_alg,
&mv_cesa_cbc_des_alg,
&mv_cesa_ecb_des3_ede_alg,
&mv_cesa_cbc_des3_ede_alg,
&mv_cesa_ecb_aes_alg,
&mv_cesa_cbc_aes_alg,
};
static struct ahash_alg *orion_ahash_algs[] = {
&mv_md5_alg,
&mv_sha1_alg,
&mv_ahmac_md5_alg,
&mv_ahmac_sha1_alg,
};
static struct skcipher_alg *armada_370_cipher_algs[] = {
&mv_cesa_ecb_des_alg,
&mv_cesa_cbc_des_alg,
&mv_cesa_ecb_des3_ede_alg,
&mv_cesa_cbc_des3_ede_alg,
&mv_cesa_ecb_aes_alg,
&mv_cesa_cbc_aes_alg,
};
static struct ahash_alg *armada_370_ahash_algs[] = {
&mv_md5_alg,
&mv_sha1_alg,
&mv_sha256_alg,
&mv_ahmac_md5_alg,
&mv_ahmac_sha1_alg,
&mv_ahmac_sha256_alg,
};
static const struct mv_cesa_caps orion_caps = {
.nengines = 1,
.cipher_algs = orion_cipher_algs,
.ncipher_algs = ARRAY_SIZE(orion_cipher_algs),
.ahash_algs = orion_ahash_algs,
.nahash_algs = ARRAY_SIZE(orion_ahash_algs),
.has_tdma = false,
};
static const struct mv_cesa_caps kirkwood_caps = {
.nengines = 1,
.cipher_algs = orion_cipher_algs,
.ncipher_algs = ARRAY_SIZE(orion_cipher_algs),
.ahash_algs = orion_ahash_algs,
.nahash_algs = ARRAY_SIZE(orion_ahash_algs),
.has_tdma = true,
};
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),
.has_tdma = true,
};
static const struct mv_cesa_caps armada_xp_caps = {
.nengines = 2,
.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),
.has_tdma = true,
};
static const struct of_device_id mv_cesa_of_match_table[] = {
{ .compatible = "marvell,orion-crypto", .data = &orion_caps },
{ .compatible = "marvell,kirkwood-crypto", .data = &kirkwood_caps },
{ .compatible = "marvell,dove-crypto", .data = &kirkwood_caps },
{ .compatible = "marvell,armada-370-crypto", .data = &armada_370_caps },
{ .compatible = "marvell,armada-xp-crypto", .data = &armada_xp_caps },
{ .compatible = "marvell,armada-375-crypto", .data = &armada_xp_caps },
{ .compatible = "marvell,armada-38x-crypto", .data = &armada_xp_caps },
{}
};
MODULE_DEVICE_TABLE(of, mv_cesa_of_match_table);
static void
mv_cesa_conf_mbus_windows(struct mv_cesa_engine *engine,
const struct mbus_dram_target_info *dram)
{
void __iomem *iobase = engine->regs;
int i;
for (i = 0; i < 4; i++) {
writel(0, iobase + CESA_TDMA_WINDOW_CTRL(i));
writel(0, iobase + CESA_TDMA_WINDOW_BASE(i));
}
for (i = 0; i < dram->num_cs; i++) {
const struct mbus_dram_window *cs = dram->cs + i;
writel(((cs->size - 1) & 0xffff0000) |
(cs->mbus_attr << 8) |
(dram->mbus_dram_target_id << 4) | 1,
iobase + CESA_TDMA_WINDOW_CTRL(i));
writel(cs->base, iobase + CESA_TDMA_WINDOW_BASE(i));
}
}
static int mv_cesa_dev_dma_init(struct mv_cesa_dev *cesa)
{
struct device *dev = cesa->dev;
struct mv_cesa_dev_dma *dma;
if (!cesa->caps->has_tdma)
return 0;
dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL);
if (!dma)
return -ENOMEM;
dma->tdma_desc_pool = dmam_pool_create("tdma_desc", dev,
sizeof(struct mv_cesa_tdma_desc),
16, 0);
if (!dma->tdma_desc_pool)
return -ENOMEM;
dma->op_pool = dmam_pool_create("cesa_op", dev,
sizeof(struct mv_cesa_op_ctx), 16, 0);
if (!dma->op_pool)
return -ENOMEM;
dma->cache_pool = dmam_pool_create("cesa_cache", dev,
CESA_MAX_HASH_BLOCK_SIZE, 1, 0);
if (!dma->cache_pool)
return -ENOMEM;
dma->padding_pool = dmam_pool_create("cesa_padding", dev, 72, 1, 0);
if (!dma->padding_pool)
return -ENOMEM;
cesa->dma = dma;
return 0;
}
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_gen_pool_get(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 = dma_map_resource(cesa->dev, res->start,
cesa->sram_size,
DMA_BIDIRECTIONAL, 0);
if (dma_mapping_error(cesa->dev, engine->sram_dma))
return -ENOMEM;
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)
gen_pool_free(engine->pool, (unsigned long)engine->sram,
cesa->sram_size);
else
dma_unmap_resource(cesa->dev, engine->sram_dma,
cesa->sram_size, DMA_BIDIRECTIONAL, 0);
}
static int mv_cesa_probe(struct platform_device *pdev)
{
const struct mv_cesa_caps *caps = &orion_caps;
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;
int irq, ret, i, cpu;
u32 sram_size;
if (cesa_dev) {
dev_err(&pdev->dev, "Only one CESA device authorized\n");
return -EEXIST;
}
if (dev->of_node) {
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_kcalloc(dev, caps->nengines, sizeof(*engines),
GFP_KERNEL);
if (!cesa->engines)
return -ENOMEM;
spin_lock_init(&cesa->lock);
cesa->regs = devm_platform_ioremap_resource_byname(pdev, "regs");
if (IS_ERR(cesa->regs))
return PTR_ERR(cesa->regs);
ret = mv_cesa_dev_dma_init(cesa);
if (ret)
return ret;
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;
}
engine->irq = irq;
/*
* 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);
if (dram && cesa->caps->has_tdma)
mv_cesa_conf_mbus_windows(engine, dram);
writel(0, engine->regs + CESA_SA_INT_STATUS);
writel(CESA_SA_CFG_STOP_DIG_ERR,
engine->regs + CESA_SA_CFG);
writel(engine->sram_dma & CESA_SA_SRAM_MSK,
engine->regs + CESA_SA_DESC_P0);
ret = devm_request_threaded_irq(dev, irq, NULL, mv_cesa_int,
IRQF_ONESHOT,
dev_name(&pdev->dev),
engine);
if (ret)
goto err_cleanup;
/* Set affinity */
cpu = cpumask_local_spread(engine->id, NUMA_NO_NODE);
irq_set_affinity_hint(irq, get_cpu_mask(cpu));
crypto_init_queue(&engine->queue, CESA_CRYPTO_DEFAULT_MAX_QLEN);
atomic_set(&engine->load, 0);
INIT_LIST_HEAD(&engine->complete_queue);
}
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);
if (cesa->engines[i].irq > 0)
irq_set_affinity_hint(cesa->engines[i].irq, NULL);
}
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);
irq_set_affinity_hint(cesa->engines[i].irq, NULL);
}
return 0;
}
static const struct platform_device_id mv_cesa_plat_id_table[] = {
{ .name = "mv_crypto" },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(platform, mv_cesa_plat_id_table);
static struct platform_driver marvell_cesa = {
.probe = mv_cesa_probe,
.remove = mv_cesa_remove,
.id_table = mv_cesa_plat_id_table,
.driver = {
.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");

View File

@@ -0,0 +1,886 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __MARVELL_CESA_H__
#define __MARVELL_CESA_H__
#include <crypto/internal/hash.h>
#include <crypto/internal/skcipher.h>
#include <linux/dma-direction.h>
#include <linux/dmapool.h>
#define CESA_ENGINE_OFF(i) (((i) * 0x2000))
#define CESA_TDMA_BYTE_CNT 0x800
#define CESA_TDMA_SRC_ADDR 0x810
#define CESA_TDMA_DST_ADDR 0x820
#define CESA_TDMA_NEXT_ADDR 0x830
#define CESA_TDMA_CONTROL 0x840
#define CESA_TDMA_DST_BURST GENMASK(2, 0)
#define CESA_TDMA_DST_BURST_32B 3
#define CESA_TDMA_DST_BURST_128B 4
#define CESA_TDMA_OUT_RD_EN BIT(4)
#define CESA_TDMA_SRC_BURST GENMASK(8, 6)
#define CESA_TDMA_SRC_BURST_32B (3 << 6)
#define CESA_TDMA_SRC_BURST_128B (4 << 6)
#define CESA_TDMA_CHAIN BIT(9)
#define CESA_TDMA_BYTE_SWAP BIT(11)
#define CESA_TDMA_NO_BYTE_SWAP BIT(11)
#define CESA_TDMA_EN BIT(12)
#define CESA_TDMA_FETCH_ND BIT(13)
#define CESA_TDMA_ACT BIT(14)
#define CESA_TDMA_CUR 0x870
#define CESA_TDMA_ERROR_CAUSE 0x8c8
#define CESA_TDMA_ERROR_MSK 0x8cc
#define CESA_TDMA_WINDOW_BASE(x) (((x) * 0x8) + 0xa00)
#define CESA_TDMA_WINDOW_CTRL(x) (((x) * 0x8) + 0xa04)
#define CESA_IVDIG(x) (0xdd00 + ((x) * 4) + \
(((x) < 5) ? 0 : 0x14))
#define CESA_SA_CMD 0xde00
#define CESA_SA_CMD_EN_CESA_SA_ACCL0 BIT(0)
#define CESA_SA_CMD_EN_CESA_SA_ACCL1 BIT(1)
#define CESA_SA_CMD_DISABLE_SEC BIT(2)
#define CESA_SA_DESC_P0 0xde04
#define CESA_SA_DESC_P1 0xde14
#define CESA_SA_CFG 0xde08
#define CESA_SA_CFG_STOP_DIG_ERR GENMASK(1, 0)
#define CESA_SA_CFG_DIG_ERR_CONT 0
#define CESA_SA_CFG_DIG_ERR_SKIP 1
#define CESA_SA_CFG_DIG_ERR_STOP 3
#define CESA_SA_CFG_CH0_W_IDMA BIT(7)
#define CESA_SA_CFG_CH1_W_IDMA BIT(8)
#define CESA_SA_CFG_ACT_CH0_IDMA BIT(9)
#define CESA_SA_CFG_ACT_CH1_IDMA BIT(10)
#define CESA_SA_CFG_MULTI_PKT BIT(11)
#define CESA_SA_CFG_PARA_DIS BIT(13)
#define CESA_SA_ACCEL_STATUS 0xde0c
#define CESA_SA_ST_ACT_0 BIT(0)
#define CESA_SA_ST_ACT_1 BIT(1)
/*
* CESA_SA_FPGA_INT_STATUS looks like a FPGA leftover and is documented only
* in Errata 4.12. It looks like that it was part of an IRQ-controller in FPGA
* and someone forgot to remove it while switching to the core and moving to
* CESA_SA_INT_STATUS.
*/
#define CESA_SA_FPGA_INT_STATUS 0xdd68
#define CESA_SA_INT_STATUS 0xde20
#define CESA_SA_INT_AUTH_DONE BIT(0)
#define CESA_SA_INT_DES_E_DONE BIT(1)
#define CESA_SA_INT_AES_E_DONE BIT(2)
#define CESA_SA_INT_AES_D_DONE BIT(3)
#define CESA_SA_INT_ENC_DONE BIT(4)
#define CESA_SA_INT_ACCEL0_DONE BIT(5)
#define CESA_SA_INT_ACCEL1_DONE BIT(6)
#define CESA_SA_INT_ACC0_IDMA_DONE BIT(7)
#define CESA_SA_INT_ACC1_IDMA_DONE BIT(8)
#define CESA_SA_INT_IDMA_DONE BIT(9)
#define CESA_SA_INT_IDMA_OWN_ERR BIT(10)
#define CESA_SA_INT_MSK 0xde24
#define CESA_SA_DESC_CFG_OP_MAC_ONLY 0
#define CESA_SA_DESC_CFG_OP_CRYPT_ONLY 1
#define CESA_SA_DESC_CFG_OP_MAC_CRYPT 2
#define CESA_SA_DESC_CFG_OP_CRYPT_MAC 3
#define CESA_SA_DESC_CFG_OP_MSK GENMASK(1, 0)
#define CESA_SA_DESC_CFG_MACM_SHA256 (1 << 4)
#define CESA_SA_DESC_CFG_MACM_HMAC_SHA256 (3 << 4)
#define CESA_SA_DESC_CFG_MACM_MD5 (4 << 4)
#define CESA_SA_DESC_CFG_MACM_SHA1 (5 << 4)
#define CESA_SA_DESC_CFG_MACM_HMAC_MD5 (6 << 4)
#define CESA_SA_DESC_CFG_MACM_HMAC_SHA1 (7 << 4)
#define CESA_SA_DESC_CFG_MACM_MSK GENMASK(6, 4)
#define CESA_SA_DESC_CFG_CRYPTM_DES (1 << 8)
#define CESA_SA_DESC_CFG_CRYPTM_3DES (2 << 8)
#define CESA_SA_DESC_CFG_CRYPTM_AES (3 << 8)
#define CESA_SA_DESC_CFG_CRYPTM_MSK GENMASK(9, 8)
#define CESA_SA_DESC_CFG_DIR_ENC (0 << 12)
#define CESA_SA_DESC_CFG_DIR_DEC (1 << 12)
#define CESA_SA_DESC_CFG_CRYPTCM_ECB (0 << 16)
#define CESA_SA_DESC_CFG_CRYPTCM_CBC (1 << 16)
#define CESA_SA_DESC_CFG_CRYPTCM_MSK BIT(16)
#define CESA_SA_DESC_CFG_3DES_EEE (0 << 20)
#define CESA_SA_DESC_CFG_3DES_EDE (1 << 20)
#define CESA_SA_DESC_CFG_AES_LEN_128 (0 << 24)
#define CESA_SA_DESC_CFG_AES_LEN_192 (1 << 24)
#define CESA_SA_DESC_CFG_AES_LEN_256 (2 << 24)
#define CESA_SA_DESC_CFG_AES_LEN_MSK GENMASK(25, 24)
#define CESA_SA_DESC_CFG_NOT_FRAG (0 << 30)
#define CESA_SA_DESC_CFG_FIRST_FRAG (1 << 30)
#define CESA_SA_DESC_CFG_LAST_FRAG (2 << 30)
#define CESA_SA_DESC_CFG_MID_FRAG (3 << 30)
#define CESA_SA_DESC_CFG_FRAG_MSK GENMASK(31, 30)
/*
* /-----------\ 0
* | ACCEL CFG | 4 * 8
* |-----------| 0x20
* | CRYPT KEY | 8 * 4
* |-----------| 0x40
* | IV IN | 4 * 4
* |-----------| 0x40 (inplace)
* | IV BUF | 4 * 4
* |-----------| 0x80
* | DATA IN | 16 * x (max ->max_req_size)
* |-----------| 0x80 (inplace operation)
* | DATA OUT | 16 * x (max ->max_req_size)
* \-----------/ SRAM size
*/
/*
* Hashing memory map:
* /-----------\ 0
* | ACCEL CFG | 4 * 8
* |-----------| 0x20
* | Inner IV | 8 * 4
* |-----------| 0x40
* | Outer IV | 8 * 4
* |-----------| 0x60
* | Output BUF| 8 * 4
* |-----------| 0x80
* | DATA IN | 64 * x (max ->max_req_size)
* \-----------/ SRAM size
*/
#define CESA_SA_CFG_SRAM_OFFSET 0x00
#define CESA_SA_DATA_SRAM_OFFSET 0x80
#define CESA_SA_CRYPT_KEY_SRAM_OFFSET 0x20
#define CESA_SA_CRYPT_IV_SRAM_OFFSET 0x40
#define CESA_SA_MAC_IIV_SRAM_OFFSET 0x20
#define CESA_SA_MAC_OIV_SRAM_OFFSET 0x40
#define CESA_SA_MAC_DIG_SRAM_OFFSET 0x60
#define CESA_SA_DESC_CRYPT_DATA(offset) \
cpu_to_le32((CESA_SA_DATA_SRAM_OFFSET + (offset)) | \
((CESA_SA_DATA_SRAM_OFFSET + (offset)) << 16))
#define CESA_SA_DESC_CRYPT_IV(offset) \
cpu_to_le32((CESA_SA_CRYPT_IV_SRAM_OFFSET + (offset)) | \
((CESA_SA_CRYPT_IV_SRAM_OFFSET + (offset)) << 16))
#define CESA_SA_DESC_CRYPT_KEY(offset) \
cpu_to_le32(CESA_SA_CRYPT_KEY_SRAM_OFFSET + (offset))
#define CESA_SA_DESC_MAC_DATA(offset) \
cpu_to_le32(CESA_SA_DATA_SRAM_OFFSET + (offset))
#define CESA_SA_DESC_MAC_DATA_MSK cpu_to_le32(GENMASK(15, 0))
#define CESA_SA_DESC_MAC_TOTAL_LEN(total_len) cpu_to_le32((total_len) << 16)
#define CESA_SA_DESC_MAC_TOTAL_LEN_MSK cpu_to_le32(GENMASK(31, 16))
#define CESA_SA_DESC_MAC_SRC_TOTAL_LEN_MAX 0xffff
#define CESA_SA_DESC_MAC_DIGEST(offset) \
cpu_to_le32(CESA_SA_MAC_DIG_SRAM_OFFSET + (offset))
#define CESA_SA_DESC_MAC_DIGEST_MSK cpu_to_le32(GENMASK(15, 0))
#define CESA_SA_DESC_MAC_FRAG_LEN(frag_len) cpu_to_le32((frag_len) << 16)
#define CESA_SA_DESC_MAC_FRAG_LEN_MSK cpu_to_le32(GENMASK(31, 16))
#define CESA_SA_DESC_MAC_IV(offset) \
cpu_to_le32((CESA_SA_MAC_IIV_SRAM_OFFSET + (offset)) | \
((CESA_SA_MAC_OIV_SRAM_OFFSET + (offset)) << 16))
#define CESA_SA_SRAM_SIZE 2048
#define CESA_SA_SRAM_PAYLOAD_SIZE (cesa_dev->sram_size - \
CESA_SA_DATA_SRAM_OFFSET)
#define CESA_SA_DEFAULT_SRAM_SIZE 2048
#define CESA_SA_MIN_SRAM_SIZE 1024
#define CESA_SA_SRAM_MSK (2048 - 1)
#define CESA_MAX_HASH_BLOCK_SIZE 64
#define CESA_HASH_BLOCK_SIZE_MSK (CESA_MAX_HASH_BLOCK_SIZE - 1)
/**
* struct mv_cesa_sec_accel_desc - security accelerator descriptor
* @config: engine config
* @enc_p: input and output data pointers for a cipher operation
* @enc_len: cipher operation length
* @enc_key_p: cipher key pointer
* @enc_iv: cipher IV pointers
* @mac_src_p: input pointer and total hash length
* @mac_digest: digest pointer and hash operation length
* @mac_iv: hmac IV pointers
*
* Structure passed to the CESA engine to describe the crypto operation
* to be executed.
*/
struct mv_cesa_sec_accel_desc {
__le32 config;
__le32 enc_p;
__le32 enc_len;
__le32 enc_key_p;
__le32 enc_iv;
__le32 mac_src_p;
__le32 mac_digest;
__le32 mac_iv;
};
/**
* struct mv_cesa_skcipher_op_ctx - cipher operation context
* @key: cipher key
* @iv: cipher IV
*
* Context associated to a cipher operation.
*/
struct mv_cesa_skcipher_op_ctx {
__le32 key[8];
u32 iv[4];
};
/**
* struct mv_cesa_hash_op_ctx - hash or hmac operation context
* @key: cipher key
* @iv: cipher IV
*
* Context associated to an hash or hmac operation.
*/
struct mv_cesa_hash_op_ctx {
u32 iv[16];
__le32 hash[8];
};
/**
* struct mv_cesa_op_ctx - crypto operation context
* @desc: CESA descriptor
* @ctx: context associated to the crypto operation
*
* Context associated to a crypto operation.
*/
struct mv_cesa_op_ctx {
struct mv_cesa_sec_accel_desc desc;
union {
struct mv_cesa_skcipher_op_ctx skcipher;
struct mv_cesa_hash_op_ctx hash;
} ctx;
};
/* TDMA descriptor flags */
#define CESA_TDMA_DST_IN_SRAM BIT(31)
#define CESA_TDMA_SRC_IN_SRAM BIT(30)
#define CESA_TDMA_END_OF_REQ BIT(29)
#define CESA_TDMA_BREAK_CHAIN BIT(28)
#define CESA_TDMA_SET_STATE BIT(27)
#define CESA_TDMA_TYPE_MSK GENMASK(26, 0)
#define CESA_TDMA_DUMMY 0
#define CESA_TDMA_DATA 1
#define CESA_TDMA_OP 2
#define CESA_TDMA_RESULT 3
/**
* struct mv_cesa_tdma_desc - TDMA descriptor
* @byte_cnt: number of bytes to transfer
* @src: DMA address of the source
* @dst: DMA address of the destination
* @next_dma: DMA address of the next TDMA descriptor
* @cur_dma: DMA address of this TDMA descriptor
* @next: pointer to the next TDMA descriptor
* @op: CESA operation attached to this TDMA descriptor
* @data: raw data attached to this TDMA descriptor
* @flags: flags describing the TDMA transfer. See the
* "TDMA descriptor flags" section above
*
* TDMA descriptor used to create a transfer chain describing a crypto
* operation.
*/
struct mv_cesa_tdma_desc {
__le32 byte_cnt;
union {
__le32 src;
u32 src_dma;
};
union {
__le32 dst;
u32 dst_dma;
};
__le32 next_dma;
/* Software state */
dma_addr_t cur_dma;
struct mv_cesa_tdma_desc *next;
union {
struct mv_cesa_op_ctx *op;
void *data;
};
u32 flags;
};
/**
* struct mv_cesa_sg_dma_iter - scatter-gather iterator
* @dir: transfer direction
* @sg: scatter list
* @offset: current position in the scatter list
* @op_offset: current position in the crypto operation
*
* Iterator used to iterate over a scatterlist while creating a TDMA chain for
* a crypto operation.
*/
struct mv_cesa_sg_dma_iter {
enum dma_data_direction dir;
struct scatterlist *sg;
unsigned int offset;
unsigned int op_offset;
};
/**
* struct mv_cesa_dma_iter - crypto operation iterator
* @len: the crypto operation length
* @offset: current position in the crypto operation
* @op_len: sub-operation length (the crypto engine can only act on 2kb
* chunks)
*
* Iterator used to create a TDMA chain for a given crypto operation.
*/
struct mv_cesa_dma_iter {
unsigned int len;
unsigned int offset;
unsigned int op_len;
};
/**
* struct mv_cesa_tdma_chain - TDMA chain
* @first: first entry in the TDMA chain
* @last: last entry in the TDMA chain
*
* Stores a TDMA chain for a specific crypto operation.
*/
struct mv_cesa_tdma_chain {
struct mv_cesa_tdma_desc *first;
struct mv_cesa_tdma_desc *last;
};
struct mv_cesa_engine;
/**
* struct mv_cesa_caps - CESA device capabilities
* @engines: number of engines
* @has_tdma: whether this device has a TDMA block
* @cipher_algs: supported cipher algorithms
* @ncipher_algs: number of supported cipher algorithms
* @ahash_algs: supported hash algorithms
* @nahash_algs: number of supported hash algorithms
*
* Structure used to describe CESA device capabilities.
*/
struct mv_cesa_caps {
int nengines;
bool has_tdma;
struct skcipher_alg **cipher_algs;
int ncipher_algs;
struct ahash_alg **ahash_algs;
int nahash_algs;
};
/**
* struct mv_cesa_dev_dma - DMA pools
* @tdma_desc_pool: TDMA desc pool
* @op_pool: crypto operation pool
* @cache_pool: data cache pool (used by hash implementation when the
* hash request is smaller than the hash block size)
* @padding_pool: padding pool (used by hash implementation when hardware
* padding cannot be used)
*
* Structure containing the different DMA pools used by this driver.
*/
struct mv_cesa_dev_dma {
struct dma_pool *tdma_desc_pool;
struct dma_pool *op_pool;
struct dma_pool *cache_pool;
struct dma_pool *padding_pool;
};
/**
* struct mv_cesa_dev - CESA device
* @caps: device capabilities
* @regs: device registers
* @sram_size: usable SRAM size
* @lock: device lock
* @engines: array of engines
* @dma: dma pools
*
* Structure storing CESA device information.
*/
struct mv_cesa_dev {
const struct mv_cesa_caps *caps;
void __iomem *regs;
struct device *dev;
unsigned int sram_size;
spinlock_t lock;
struct mv_cesa_engine *engines;
struct mv_cesa_dev_dma *dma;
};
/**
* struct mv_cesa_engine - CESA engine
* @id: engine id
* @regs: engine registers
* @sram: SRAM memory region
* @sram_dma: DMA address of the SRAM memory region
* @lock: engine lock
* @req: current crypto request
* @clk: engine clk
* @zclk: engine zclk
* @max_req_len: maximum chunk length (useful to create the TDMA chain)
* @int_mask: interrupt mask cache
* @pool: memory pool pointing to the memory region reserved in
* SRAM
* @queue: fifo of the pending crypto requests
* @load: engine load counter, useful for load balancing
* @chain: list of the current tdma descriptors being processed
* by this engine.
* @complete_queue: fifo of the processed requests by the engine
*
* Structure storing CESA engine information.
*/
struct mv_cesa_engine {
int id;
void __iomem *regs;
void __iomem *sram;
dma_addr_t sram_dma;
spinlock_t lock;
struct crypto_async_request *req;
struct clk *clk;
struct clk *zclk;
size_t max_req_len;
u32 int_mask;
struct gen_pool *pool;
struct crypto_queue queue;
atomic_t load;
struct mv_cesa_tdma_chain chain;
struct list_head complete_queue;
int irq;
};
/**
* struct mv_cesa_req_ops - CESA request operations
* @process: process a request chunk result (should return 0 if the
* operation, -EINPROGRESS if it needs more steps or an error
* code)
* @step: launch the crypto operation on the next chunk
* @cleanup: cleanup the crypto request (release associated data)
* @complete: complete the request, i.e copy result or context from sram when
* needed.
*/
struct mv_cesa_req_ops {
int (*process)(struct crypto_async_request *req, u32 status);
void (*step)(struct crypto_async_request *req);
void (*cleanup)(struct crypto_async_request *req);
void (*complete)(struct crypto_async_request *req);
};
/**
* struct mv_cesa_ctx - CESA operation context
* @ops: crypto operations
*
* Base context structure inherited by operation specific ones.
*/
struct mv_cesa_ctx {
const struct mv_cesa_req_ops *ops;
};
/**
* struct mv_cesa_hash_ctx - CESA hash operation context
* @base: base context structure
*
* Hash context structure.
*/
struct mv_cesa_hash_ctx {
struct mv_cesa_ctx base;
};
/**
* struct mv_cesa_hash_ctx - CESA hmac operation context
* @base: base context structure
* @iv: initialization vectors
*
* HMAC context structure.
*/
struct mv_cesa_hmac_ctx {
struct mv_cesa_ctx base;
__be32 iv[16];
};
/**
* enum mv_cesa_req_type - request type definitions
* @CESA_STD_REQ: standard request
* @CESA_DMA_REQ: DMA request
*/
enum mv_cesa_req_type {
CESA_STD_REQ,
CESA_DMA_REQ,
};
/**
* struct mv_cesa_req - CESA request
* @engine: engine associated with this request
* @chain: list of tdma descriptors associated with this request
*/
struct mv_cesa_req {
struct mv_cesa_engine *engine;
struct mv_cesa_tdma_chain chain;
};
/**
* struct mv_cesa_sg_std_iter - CESA scatter-gather iterator for standard
* requests
* @iter: sg mapping iterator
* @offset: current offset in the SG entry mapped in memory
*/
struct mv_cesa_sg_std_iter {
struct sg_mapping_iter iter;
unsigned int offset;
};
/**
* struct mv_cesa_skcipher_std_req - cipher standard request
* @op: operation context
* @offset: current operation offset
* @size: size of the crypto operation
*/
struct mv_cesa_skcipher_std_req {
struct mv_cesa_op_ctx op;
unsigned int offset;
unsigned int size;
bool skip_ctx;
};
/**
* struct mv_cesa_skcipher_req - cipher request
* @req: type specific request information
* @src_nents: number of entries in the src sg list
* @dst_nents: number of entries in the dest sg list
*/
struct mv_cesa_skcipher_req {
struct mv_cesa_req base;
struct mv_cesa_skcipher_std_req std;
int src_nents;
int dst_nents;
};
/**
* struct mv_cesa_ahash_std_req - standard hash request
* @offset: current operation offset
*/
struct mv_cesa_ahash_std_req {
unsigned int offset;
};
/**
* struct mv_cesa_ahash_dma_req - DMA hash request
* @padding: padding buffer
* @padding_dma: DMA address of the padding buffer
* @cache_dma: DMA address of the cache buffer
*/
struct mv_cesa_ahash_dma_req {
u8 *padding;
dma_addr_t padding_dma;
u8 *cache;
dma_addr_t cache_dma;
};
/**
* struct mv_cesa_ahash_req - hash request
* @req: type specific request information
* @cache: cache buffer
* @cache_ptr: write pointer in the cache buffer
* @len: hash total length
* @src_nents: number of entries in the scatterlist
* @last_req: define whether the current operation is the last one
* or not
* @state: hash state
*/
struct mv_cesa_ahash_req {
struct mv_cesa_req base;
union {
struct mv_cesa_ahash_dma_req dma;
struct mv_cesa_ahash_std_req std;
} req;
struct mv_cesa_op_ctx op_tmpl;
u8 cache[CESA_MAX_HASH_BLOCK_SIZE];
unsigned int cache_ptr;
u64 len;
int src_nents;
bool last_req;
bool algo_le;
u32 state[8];
};
/* CESA functions */
extern struct mv_cesa_dev *cesa_dev;
static inline void
mv_cesa_engine_enqueue_complete_request(struct mv_cesa_engine *engine,
struct crypto_async_request *req)
{
list_add_tail(&req->list, &engine->complete_queue);
}
static inline struct crypto_async_request *
mv_cesa_engine_dequeue_complete_request(struct mv_cesa_engine *engine)
{
struct crypto_async_request *req;
req = list_first_entry_or_null(&engine->complete_queue,
struct crypto_async_request,
list);
if (req)
list_del(&req->list);
return req;
}
static inline enum mv_cesa_req_type
mv_cesa_req_get_type(struct mv_cesa_req *req)
{
return req->chain.first ? CESA_DMA_REQ : CESA_STD_REQ;
}
static inline void mv_cesa_update_op_cfg(struct mv_cesa_op_ctx *op,
u32 cfg, u32 mask)
{
op->desc.config &= cpu_to_le32(~mask);
op->desc.config |= cpu_to_le32(cfg);
}
static inline u32 mv_cesa_get_op_cfg(const struct mv_cesa_op_ctx *op)
{
return le32_to_cpu(op->desc.config);
}
static inline void mv_cesa_set_op_cfg(struct mv_cesa_op_ctx *op, u32 cfg)
{
op->desc.config = cpu_to_le32(cfg);
}
static inline void mv_cesa_adjust_op(struct mv_cesa_engine *engine,
struct mv_cesa_op_ctx *op)
{
u32 offset = engine->sram_dma & CESA_SA_SRAM_MSK;
op->desc.enc_p = CESA_SA_DESC_CRYPT_DATA(offset);
op->desc.enc_key_p = CESA_SA_DESC_CRYPT_KEY(offset);
op->desc.enc_iv = CESA_SA_DESC_CRYPT_IV(offset);
op->desc.mac_src_p &= ~CESA_SA_DESC_MAC_DATA_MSK;
op->desc.mac_src_p |= CESA_SA_DESC_MAC_DATA(offset);
op->desc.mac_digest &= ~CESA_SA_DESC_MAC_DIGEST_MSK;
op->desc.mac_digest |= CESA_SA_DESC_MAC_DIGEST(offset);
op->desc.mac_iv = CESA_SA_DESC_MAC_IV(offset);
}
static inline void mv_cesa_set_crypt_op_len(struct mv_cesa_op_ctx *op, int len)
{
op->desc.enc_len = cpu_to_le32(len);
}
static inline void mv_cesa_set_mac_op_total_len(struct mv_cesa_op_ctx *op,
int len)
{
op->desc.mac_src_p &= ~CESA_SA_DESC_MAC_TOTAL_LEN_MSK;
op->desc.mac_src_p |= CESA_SA_DESC_MAC_TOTAL_LEN(len);
}
static inline void mv_cesa_set_mac_op_frag_len(struct mv_cesa_op_ctx *op,
int len)
{
op->desc.mac_digest &= ~CESA_SA_DESC_MAC_FRAG_LEN_MSK;
op->desc.mac_digest |= CESA_SA_DESC_MAC_FRAG_LEN(len);
}
static inline void mv_cesa_set_int_mask(struct mv_cesa_engine *engine,
u32 int_mask)
{
if (int_mask == engine->int_mask)
return;
writel_relaxed(int_mask, engine->regs + CESA_SA_INT_MSK);
engine->int_mask = int_mask;
}
static inline u32 mv_cesa_get_int_mask(struct mv_cesa_engine *engine)
{
return engine->int_mask;
}
static inline bool mv_cesa_mac_op_is_first_frag(const struct mv_cesa_op_ctx *op)
{
return (mv_cesa_get_op_cfg(op) & CESA_SA_DESC_CFG_FRAG_MSK) ==
CESA_SA_DESC_CFG_FIRST_FRAG;
}
int mv_cesa_queue_req(struct crypto_async_request *req,
struct mv_cesa_req *creq);
struct crypto_async_request *
mv_cesa_dequeue_req_locked(struct mv_cesa_engine *engine,
struct crypto_async_request **backlog);
static inline struct mv_cesa_engine *mv_cesa_select_engine(int weight)
{
int i;
u32 min_load = U32_MAX;
struct mv_cesa_engine *selected = NULL;
for (i = 0; i < cesa_dev->caps->nengines; i++) {
struct mv_cesa_engine *engine = cesa_dev->engines + i;
u32 load = atomic_read(&engine->load);
if (load < min_load) {
min_load = load;
selected = engine;
}
}
atomic_add(weight, &selected->load);
return selected;
}
/*
* Helper function that indicates whether a crypto request needs to be
* cleaned up or not after being enqueued using mv_cesa_queue_req().
*/
static inline int mv_cesa_req_needs_cleanup(struct crypto_async_request *req,
int ret)
{
/*
* The queue still had some space, the request was queued
* normally, so there's no need to clean it up.
*/
if (ret == -EINPROGRESS)
return false;
/*
* The queue had not space left, but since the request is
* flagged with CRYPTO_TFM_REQ_MAY_BACKLOG, it was added to
* the backlog and will be processed later. There's no need to
* clean it up.
*/
if (ret == -EBUSY)
return false;
/* Request wasn't queued, we need to clean it up */
return true;
}
/* TDMA functions */
static inline void mv_cesa_req_dma_iter_init(struct mv_cesa_dma_iter *iter,
unsigned int len)
{
iter->len = len;
iter->op_len = min(len, CESA_SA_SRAM_PAYLOAD_SIZE);
iter->offset = 0;
}
static inline void mv_cesa_sg_dma_iter_init(struct mv_cesa_sg_dma_iter *iter,
struct scatterlist *sg,
enum dma_data_direction dir)
{
iter->op_offset = 0;
iter->offset = 0;
iter->sg = sg;
iter->dir = dir;
}
static inline unsigned int
mv_cesa_req_dma_iter_transfer_len(struct mv_cesa_dma_iter *iter,
struct mv_cesa_sg_dma_iter *sgiter)
{
return min(iter->op_len - sgiter->op_offset,
sg_dma_len(sgiter->sg) - sgiter->offset);
}
bool mv_cesa_req_dma_iter_next_transfer(struct mv_cesa_dma_iter *chain,
struct mv_cesa_sg_dma_iter *sgiter,
unsigned int len);
static inline bool mv_cesa_req_dma_iter_next_op(struct mv_cesa_dma_iter *iter)
{
iter->offset += iter->op_len;
iter->op_len = min(iter->len - iter->offset,
CESA_SA_SRAM_PAYLOAD_SIZE);
return iter->op_len;
}
void mv_cesa_dma_step(struct mv_cesa_req *dreq);
static inline int mv_cesa_dma_process(struct mv_cesa_req *dreq,
u32 status)
{
if (!(status & CESA_SA_INT_ACC0_IDMA_DONE))
return -EINPROGRESS;
if (status & CESA_SA_INT_IDMA_OWN_ERR)
return -EINVAL;
return 0;
}
void mv_cesa_dma_prepare(struct mv_cesa_req *dreq,
struct mv_cesa_engine *engine);
void mv_cesa_dma_cleanup(struct mv_cesa_req *dreq);
void mv_cesa_tdma_chain(struct mv_cesa_engine *engine,
struct mv_cesa_req *dreq);
int mv_cesa_tdma_process(struct mv_cesa_engine *engine, u32 status);
static inline void
mv_cesa_tdma_desc_iter_init(struct mv_cesa_tdma_chain *chain)
{
memset(chain, 0, sizeof(*chain));
}
int mv_cesa_dma_add_result_op(struct mv_cesa_tdma_chain *chain, dma_addr_t src,
u32 size, u32 flags, gfp_t gfp_flags);
struct mv_cesa_op_ctx *mv_cesa_dma_add_op(struct mv_cesa_tdma_chain *chain,
const struct mv_cesa_op_ctx *op_templ,
bool skip_ctx,
gfp_t flags);
int mv_cesa_dma_add_data_transfer(struct mv_cesa_tdma_chain *chain,
dma_addr_t dst, dma_addr_t src, u32 size,
u32 flags, gfp_t gfp_flags);
int mv_cesa_dma_add_dummy_launch(struct mv_cesa_tdma_chain *chain, gfp_t flags);
int mv_cesa_dma_add_dummy_end(struct mv_cesa_tdma_chain *chain, gfp_t flags);
int mv_cesa_dma_add_op_transfers(struct mv_cesa_tdma_chain *chain,
struct mv_cesa_dma_iter *dma_iter,
struct mv_cesa_sg_dma_iter *sgiter,
gfp_t gfp_flags);
/* Algorithm definitions */
extern struct ahash_alg mv_md5_alg;
extern struct ahash_alg mv_sha1_alg;
extern struct ahash_alg mv_sha256_alg;
extern struct ahash_alg mv_ahmac_md5_alg;
extern struct ahash_alg mv_ahmac_sha1_alg;
extern struct ahash_alg mv_ahmac_sha256_alg;
extern struct skcipher_alg mv_cesa_ecb_des_alg;
extern struct skcipher_alg mv_cesa_cbc_des_alg;
extern struct skcipher_alg mv_cesa_ecb_des3_ede_alg;
extern struct skcipher_alg mv_cesa_cbc_des3_ede_alg;
extern struct skcipher_alg mv_cesa_ecb_aes_alg;
extern struct skcipher_alg mv_cesa_cbc_aes_alg;
#endif /* __MARVELL_CESA_H__ */

View File

@@ -0,0 +1,807 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* 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 >
*/
#include <crypto/aes.h>
#include <crypto/internal/des.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include "cesa.h"
struct mv_cesa_des_ctx {
struct mv_cesa_ctx base;
u8 key[DES_KEY_SIZE];
};
struct mv_cesa_des3_ctx {
struct mv_cesa_ctx base;
u8 key[DES3_EDE_KEY_SIZE];
};
struct mv_cesa_aes_ctx {
struct mv_cesa_ctx base;
struct crypto_aes_ctx aes;
};
struct mv_cesa_skcipher_dma_iter {
struct mv_cesa_dma_iter base;
struct mv_cesa_sg_dma_iter src;
struct mv_cesa_sg_dma_iter dst;
};
static inline void
mv_cesa_skcipher_req_iter_init(struct mv_cesa_skcipher_dma_iter *iter,
struct skcipher_request *req)
{
mv_cesa_req_dma_iter_init(&iter->base, req->cryptlen);
mv_cesa_sg_dma_iter_init(&iter->src, req->src, DMA_TO_DEVICE);
mv_cesa_sg_dma_iter_init(&iter->dst, req->dst, DMA_FROM_DEVICE);
}
static inline bool
mv_cesa_skcipher_req_iter_next_op(struct mv_cesa_skcipher_dma_iter *iter)
{
iter->src.op_offset = 0;
iter->dst.op_offset = 0;
return mv_cesa_req_dma_iter_next_op(&iter->base);
}
static inline void
mv_cesa_skcipher_dma_cleanup(struct skcipher_request *req)
{
struct mv_cesa_skcipher_req *creq = skcipher_request_ctx(req);
if (req->dst != req->src) {
dma_unmap_sg(cesa_dev->dev, req->dst, creq->dst_nents,
DMA_FROM_DEVICE);
dma_unmap_sg(cesa_dev->dev, req->src, creq->src_nents,
DMA_TO_DEVICE);
} else {
dma_unmap_sg(cesa_dev->dev, req->src, creq->src_nents,
DMA_BIDIRECTIONAL);
}
mv_cesa_dma_cleanup(&creq->base);
}
static inline void mv_cesa_skcipher_cleanup(struct skcipher_request *req)
{
struct mv_cesa_skcipher_req *creq = skcipher_request_ctx(req);
if (mv_cesa_req_get_type(&creq->base) == CESA_DMA_REQ)
mv_cesa_skcipher_dma_cleanup(req);
}
static void mv_cesa_skcipher_std_step(struct skcipher_request *req)
{
struct mv_cesa_skcipher_req *creq = skcipher_request_ctx(req);
struct mv_cesa_skcipher_std_req *sreq = &creq->std;
struct mv_cesa_engine *engine = creq->base.engine;
size_t len = min_t(size_t, req->cryptlen - sreq->offset,
CESA_SA_SRAM_PAYLOAD_SIZE);
mv_cesa_adjust_op(engine, &sreq->op);
memcpy_toio(engine->sram, &sreq->op, sizeof(sreq->op));
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_toio(engine->sram, &sreq->op, sizeof(sreq->op));
sreq->skip_ctx = true;
} else {
memcpy_toio(engine->sram, &sreq->op, sizeof(sreq->op.desc));
}
mv_cesa_set_int_mask(engine, CESA_SA_INT_ACCEL0_DONE);
writel_relaxed(CESA_SA_CFG_PARA_DIS, engine->regs + CESA_SA_CFG);
WARN_ON(readl(engine->regs + CESA_SA_CMD) &
CESA_SA_CMD_EN_CESA_SA_ACCL0);
writel(CESA_SA_CMD_EN_CESA_SA_ACCL0, engine->regs + CESA_SA_CMD);
}
static int mv_cesa_skcipher_std_process(struct skcipher_request *req,
u32 status)
{
struct mv_cesa_skcipher_req *creq = skcipher_request_ctx(req);
struct mv_cesa_skcipher_std_req *sreq = &creq->std;
struct mv_cesa_engine *engine = creq->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->cryptlen)
return -EINPROGRESS;
return 0;
}
static int mv_cesa_skcipher_process(struct crypto_async_request *req,
u32 status)
{
struct skcipher_request *skreq = skcipher_request_cast(req);
struct mv_cesa_skcipher_req *creq = skcipher_request_ctx(skreq);
struct mv_cesa_req *basereq = &creq->base;
if (mv_cesa_req_get_type(basereq) == CESA_STD_REQ)
return mv_cesa_skcipher_std_process(skreq, status);
return mv_cesa_dma_process(basereq, status);
}
static void mv_cesa_skcipher_step(struct crypto_async_request *req)
{
struct skcipher_request *skreq = skcipher_request_cast(req);
struct mv_cesa_skcipher_req *creq = skcipher_request_ctx(skreq);
if (mv_cesa_req_get_type(&creq->base) == CESA_DMA_REQ)
mv_cesa_dma_step(&creq->base);
else
mv_cesa_skcipher_std_step(skreq);
}
static inline void
mv_cesa_skcipher_dma_prepare(struct skcipher_request *req)
{
struct mv_cesa_skcipher_req *creq = skcipher_request_ctx(req);
struct mv_cesa_req *basereq = &creq->base;
mv_cesa_dma_prepare(basereq, basereq->engine);
}
static inline void
mv_cesa_skcipher_std_prepare(struct skcipher_request *req)
{
struct mv_cesa_skcipher_req *creq = skcipher_request_ctx(req);
struct mv_cesa_skcipher_std_req *sreq = &creq->std;
sreq->size = 0;
sreq->offset = 0;
}
static inline void mv_cesa_skcipher_prepare(struct crypto_async_request *req,
struct mv_cesa_engine *engine)
{
struct skcipher_request *skreq = skcipher_request_cast(req);
struct mv_cesa_skcipher_req *creq = skcipher_request_ctx(skreq);
creq->base.engine = engine;
if (mv_cesa_req_get_type(&creq->base) == CESA_DMA_REQ)
mv_cesa_skcipher_dma_prepare(skreq);
else
mv_cesa_skcipher_std_prepare(skreq);
}
static inline void
mv_cesa_skcipher_req_cleanup(struct crypto_async_request *req)
{
struct skcipher_request *skreq = skcipher_request_cast(req);
mv_cesa_skcipher_cleanup(skreq);
}
static void
mv_cesa_skcipher_complete(struct crypto_async_request *req)
{
struct skcipher_request *skreq = skcipher_request_cast(req);
struct mv_cesa_skcipher_req *creq = skcipher_request_ctx(skreq);
struct mv_cesa_engine *engine = creq->base.engine;
unsigned int ivsize;
atomic_sub(skreq->cryptlen, &engine->load);
ivsize = crypto_skcipher_ivsize(crypto_skcipher_reqtfm(skreq));
if (mv_cesa_req_get_type(&creq->base) == CESA_DMA_REQ) {
struct mv_cesa_req *basereq;
basereq = &creq->base;
memcpy(skreq->iv, basereq->chain.last->op->ctx.skcipher.iv,
ivsize);
} else {
memcpy_fromio(skreq->iv,
engine->sram + CESA_SA_CRYPT_IV_SRAM_OFFSET,
ivsize);
}
}
static const struct mv_cesa_req_ops mv_cesa_skcipher_req_ops = {
.step = mv_cesa_skcipher_step,
.process = mv_cesa_skcipher_process,
.cleanup = mv_cesa_skcipher_req_cleanup,
.complete = mv_cesa_skcipher_complete,
};
static void mv_cesa_skcipher_cra_exit(struct crypto_tfm *tfm)
{
void *ctx = crypto_tfm_ctx(tfm);
memzero_explicit(ctx, tfm->__crt_alg->cra_ctxsize);
}
static int mv_cesa_skcipher_cra_init(struct crypto_tfm *tfm)
{
struct mv_cesa_ctx *ctx = crypto_tfm_ctx(tfm);
ctx->ops = &mv_cesa_skcipher_req_ops;
crypto_skcipher_set_reqsize(__crypto_skcipher_cast(tfm),
sizeof(struct mv_cesa_skcipher_req));
return 0;
}
static int mv_cesa_aes_setkey(struct crypto_skcipher *cipher, const u8 *key,
unsigned int len)
{
struct crypto_tfm *tfm = crypto_skcipher_tfm(cipher);
struct mv_cesa_aes_ctx *ctx = crypto_tfm_ctx(tfm);
int remaining;
int offset;
int ret;
int i;
ret = aes_expandkey(&ctx->aes, key, len);
if (ret)
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] = ctx->aes.key_enc[offset + i];
return 0;
}
static int mv_cesa_des_setkey(struct crypto_skcipher *cipher, const u8 *key,
unsigned int len)
{
struct mv_cesa_des_ctx *ctx = crypto_skcipher_ctx(cipher);
int err;
err = verify_skcipher_des_key(cipher, key);
if (err)
return err;
memcpy(ctx->key, key, DES_KEY_SIZE);
return 0;
}
static int mv_cesa_des3_ede_setkey(struct crypto_skcipher *cipher,
const u8 *key, unsigned int len)
{
struct mv_cesa_des_ctx *ctx = crypto_skcipher_ctx(cipher);
int err;
err = verify_skcipher_des3_key(cipher, key);
if (err)
return err;
memcpy(ctx->key, key, DES3_EDE_KEY_SIZE);
return 0;
}
static int mv_cesa_skcipher_dma_req_init(struct skcipher_request *req,
const struct mv_cesa_op_ctx *op_templ)
{
struct mv_cesa_skcipher_req *creq = skcipher_request_ctx(req);
gfp_t flags = (req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP) ?
GFP_KERNEL : GFP_ATOMIC;
struct mv_cesa_req *basereq = &creq->base;
struct mv_cesa_skcipher_dma_iter iter;
bool skip_ctx = false;
int ret;
basereq->chain.first = NULL;
basereq->chain.last = NULL;
if (req->src != req->dst) {
ret = dma_map_sg(cesa_dev->dev, req->src, creq->src_nents,
DMA_TO_DEVICE);
if (!ret)
return -ENOMEM;
ret = dma_map_sg(cesa_dev->dev, req->dst, creq->dst_nents,
DMA_FROM_DEVICE);
if (!ret) {
ret = -ENOMEM;
goto err_unmap_src;
}
} else {
ret = dma_map_sg(cesa_dev->dev, req->src, creq->src_nents,
DMA_BIDIRECTIONAL);
if (!ret)
return -ENOMEM;
}
mv_cesa_tdma_desc_iter_init(&basereq->chain);
mv_cesa_skcipher_req_iter_init(&iter, req);
do {
struct mv_cesa_op_ctx *op;
op = mv_cesa_dma_add_op(&basereq->chain, op_templ, skip_ctx,
flags);
if (IS_ERR(op)) {
ret = PTR_ERR(op);
goto err_free_tdma;
}
skip_ctx = true;
mv_cesa_set_crypt_op_len(op, iter.base.op_len);
/* Add input transfers */
ret = mv_cesa_dma_add_op_transfers(&basereq->chain, &iter.base,
&iter.src, flags);
if (ret)
goto err_free_tdma;
/* Add dummy desc to launch the crypto operation */
ret = mv_cesa_dma_add_dummy_launch(&basereq->chain, flags);
if (ret)
goto err_free_tdma;
/* Add output transfers */
ret = mv_cesa_dma_add_op_transfers(&basereq->chain, &iter.base,
&iter.dst, flags);
if (ret)
goto err_free_tdma;
} while (mv_cesa_skcipher_req_iter_next_op(&iter));
/* Add output data for IV */
ret = mv_cesa_dma_add_result_op(&basereq->chain,
CESA_SA_CFG_SRAM_OFFSET,
CESA_SA_DATA_SRAM_OFFSET,
CESA_TDMA_SRC_IN_SRAM, flags);
if (ret)
goto err_free_tdma;
basereq->chain.last->flags |= CESA_TDMA_END_OF_REQ;
return 0;
err_free_tdma:
mv_cesa_dma_cleanup(basereq);
if (req->dst != req->src)
dma_unmap_sg(cesa_dev->dev, req->dst, creq->dst_nents,
DMA_FROM_DEVICE);
err_unmap_src:
dma_unmap_sg(cesa_dev->dev, req->src, creq->src_nents,
req->dst != req->src ? DMA_TO_DEVICE : DMA_BIDIRECTIONAL);
return ret;
}
static inline int
mv_cesa_skcipher_std_req_init(struct skcipher_request *req,
const struct mv_cesa_op_ctx *op_templ)
{
struct mv_cesa_skcipher_req *creq = skcipher_request_ctx(req);
struct mv_cesa_skcipher_std_req *sreq = &creq->std;
struct mv_cesa_req *basereq = &creq->base;
sreq->op = *op_templ;
sreq->skip_ctx = false;
basereq->chain.first = NULL;
basereq->chain.last = NULL;
return 0;
}
static int mv_cesa_skcipher_req_init(struct skcipher_request *req,
struct mv_cesa_op_ctx *tmpl)
{
struct mv_cesa_skcipher_req *creq = skcipher_request_ctx(req);
struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
unsigned int blksize = crypto_skcipher_blocksize(tfm);
int ret;
if (!IS_ALIGNED(req->cryptlen, blksize))
return -EINVAL;
creq->src_nents = sg_nents_for_len(req->src, req->cryptlen);
if (creq->src_nents < 0) {
dev_err(cesa_dev->dev, "Invalid number of src SG");
return creq->src_nents;
}
creq->dst_nents = sg_nents_for_len(req->dst, req->cryptlen);
if (creq->dst_nents < 0) {
dev_err(cesa_dev->dev, "Invalid number of dst SG");
return creq->dst_nents;
}
mv_cesa_update_op_cfg(tmpl, CESA_SA_DESC_CFG_OP_CRYPT_ONLY,
CESA_SA_DESC_CFG_OP_MSK);
if (cesa_dev->caps->has_tdma)
ret = mv_cesa_skcipher_dma_req_init(req, tmpl);
else
ret = mv_cesa_skcipher_std_req_init(req, tmpl);
return ret;
}
static int mv_cesa_skcipher_queue_req(struct skcipher_request *req,
struct mv_cesa_op_ctx *tmpl)
{
int ret;
struct mv_cesa_skcipher_req *creq = skcipher_request_ctx(req);
struct mv_cesa_engine *engine;
ret = mv_cesa_skcipher_req_init(req, tmpl);
if (ret)
return ret;
engine = mv_cesa_select_engine(req->cryptlen);
mv_cesa_skcipher_prepare(&req->base, engine);
ret = mv_cesa_queue_req(&req->base, &creq->base);
if (mv_cesa_req_needs_cleanup(&req->base, ret))
mv_cesa_skcipher_cleanup(req);
return ret;
}
static int mv_cesa_des_op(struct skcipher_request *req,
struct mv_cesa_op_ctx *tmpl)
{
struct mv_cesa_des_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
mv_cesa_update_op_cfg(tmpl, CESA_SA_DESC_CFG_CRYPTM_DES,
CESA_SA_DESC_CFG_CRYPTM_MSK);
memcpy(tmpl->ctx.skcipher.key, ctx->key, DES_KEY_SIZE);
return mv_cesa_skcipher_queue_req(req, tmpl);
}
static int mv_cesa_ecb_des_encrypt(struct skcipher_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_des_op(req, &tmpl);
}
static int mv_cesa_ecb_des_decrypt(struct skcipher_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_des_op(req, &tmpl);
}
struct skcipher_alg mv_cesa_ecb_des_alg = {
.setkey = mv_cesa_des_setkey,
.encrypt = mv_cesa_ecb_des_encrypt,
.decrypt = mv_cesa_ecb_des_decrypt,
.min_keysize = DES_KEY_SIZE,
.max_keysize = DES_KEY_SIZE,
.base = {
.cra_name = "ecb(des)",
.cra_driver_name = "mv-ecb-des",
.cra_priority = 300,
.cra_flags = CRYPTO_ALG_KERN_DRIVER_ONLY | CRYPTO_ALG_ASYNC |
CRYPTO_ALG_ALLOCATES_MEMORY,
.cra_blocksize = DES_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct mv_cesa_des_ctx),
.cra_alignmask = 0,
.cra_module = THIS_MODULE,
.cra_init = mv_cesa_skcipher_cra_init,
.cra_exit = mv_cesa_skcipher_cra_exit,
},
};
static int mv_cesa_cbc_des_op(struct skcipher_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.skcipher.iv, req->iv, DES_BLOCK_SIZE);
return mv_cesa_des_op(req, tmpl);
}
static int mv_cesa_cbc_des_encrypt(struct skcipher_request *req)
{
struct mv_cesa_op_ctx tmpl;
mv_cesa_set_op_cfg(&tmpl, CESA_SA_DESC_CFG_DIR_ENC);
return mv_cesa_cbc_des_op(req, &tmpl);
}
static int mv_cesa_cbc_des_decrypt(struct skcipher_request *req)
{
struct mv_cesa_op_ctx tmpl;
mv_cesa_set_op_cfg(&tmpl, CESA_SA_DESC_CFG_DIR_DEC);
return mv_cesa_cbc_des_op(req, &tmpl);
}
struct skcipher_alg mv_cesa_cbc_des_alg = {
.setkey = mv_cesa_des_setkey,
.encrypt = mv_cesa_cbc_des_encrypt,
.decrypt = mv_cesa_cbc_des_decrypt,
.min_keysize = DES_KEY_SIZE,
.max_keysize = DES_KEY_SIZE,
.ivsize = DES_BLOCK_SIZE,
.base = {
.cra_name = "cbc(des)",
.cra_driver_name = "mv-cbc-des",
.cra_priority = 300,
.cra_flags = CRYPTO_ALG_KERN_DRIVER_ONLY | CRYPTO_ALG_ASYNC |
CRYPTO_ALG_ALLOCATES_MEMORY,
.cra_blocksize = DES_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct mv_cesa_des_ctx),
.cra_alignmask = 0,
.cra_module = THIS_MODULE,
.cra_init = mv_cesa_skcipher_cra_init,
.cra_exit = mv_cesa_skcipher_cra_exit,
},
};
static int mv_cesa_des3_op(struct skcipher_request *req,
struct mv_cesa_op_ctx *tmpl)
{
struct mv_cesa_des3_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
mv_cesa_update_op_cfg(tmpl, CESA_SA_DESC_CFG_CRYPTM_3DES,
CESA_SA_DESC_CFG_CRYPTM_MSK);
memcpy(tmpl->ctx.skcipher.key, ctx->key, DES3_EDE_KEY_SIZE);
return mv_cesa_skcipher_queue_req(req, tmpl);
}
static int mv_cesa_ecb_des3_ede_encrypt(struct skcipher_request *req)
{
struct mv_cesa_op_ctx tmpl;
mv_cesa_set_op_cfg(&tmpl,
CESA_SA_DESC_CFG_CRYPTCM_ECB |
CESA_SA_DESC_CFG_3DES_EDE |
CESA_SA_DESC_CFG_DIR_ENC);
return mv_cesa_des3_op(req, &tmpl);
}
static int mv_cesa_ecb_des3_ede_decrypt(struct skcipher_request *req)
{
struct mv_cesa_op_ctx tmpl;
mv_cesa_set_op_cfg(&tmpl,
CESA_SA_DESC_CFG_CRYPTCM_ECB |
CESA_SA_DESC_CFG_3DES_EDE |
CESA_SA_DESC_CFG_DIR_DEC);
return mv_cesa_des3_op(req, &tmpl);
}
struct skcipher_alg mv_cesa_ecb_des3_ede_alg = {
.setkey = mv_cesa_des3_ede_setkey,
.encrypt = mv_cesa_ecb_des3_ede_encrypt,
.decrypt = mv_cesa_ecb_des3_ede_decrypt,
.min_keysize = DES3_EDE_KEY_SIZE,
.max_keysize = DES3_EDE_KEY_SIZE,
.base = {
.cra_name = "ecb(des3_ede)",
.cra_driver_name = "mv-ecb-des3-ede",
.cra_priority = 300,
.cra_flags = CRYPTO_ALG_KERN_DRIVER_ONLY | CRYPTO_ALG_ASYNC |
CRYPTO_ALG_ALLOCATES_MEMORY,
.cra_blocksize = DES3_EDE_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct mv_cesa_des3_ctx),
.cra_alignmask = 0,
.cra_module = THIS_MODULE,
.cra_init = mv_cesa_skcipher_cra_init,
.cra_exit = mv_cesa_skcipher_cra_exit,
},
};
static int mv_cesa_cbc_des3_op(struct skcipher_request *req,
struct mv_cesa_op_ctx *tmpl)
{
memcpy(tmpl->ctx.skcipher.iv, req->iv, DES3_EDE_BLOCK_SIZE);
return mv_cesa_des3_op(req, tmpl);
}
static int mv_cesa_cbc_des3_ede_encrypt(struct skcipher_request *req)
{
struct mv_cesa_op_ctx tmpl;
mv_cesa_set_op_cfg(&tmpl,
CESA_SA_DESC_CFG_CRYPTCM_CBC |
CESA_SA_DESC_CFG_3DES_EDE |
CESA_SA_DESC_CFG_DIR_ENC);
return mv_cesa_cbc_des3_op(req, &tmpl);
}
static int mv_cesa_cbc_des3_ede_decrypt(struct skcipher_request *req)
{
struct mv_cesa_op_ctx tmpl;
mv_cesa_set_op_cfg(&tmpl,
CESA_SA_DESC_CFG_CRYPTCM_CBC |
CESA_SA_DESC_CFG_3DES_EDE |
CESA_SA_DESC_CFG_DIR_DEC);
return mv_cesa_cbc_des3_op(req, &tmpl);
}
struct skcipher_alg mv_cesa_cbc_des3_ede_alg = {
.setkey = mv_cesa_des3_ede_setkey,
.encrypt = mv_cesa_cbc_des3_ede_encrypt,
.decrypt = mv_cesa_cbc_des3_ede_decrypt,
.min_keysize = DES3_EDE_KEY_SIZE,
.max_keysize = DES3_EDE_KEY_SIZE,
.ivsize = DES3_EDE_BLOCK_SIZE,
.base = {
.cra_name = "cbc(des3_ede)",
.cra_driver_name = "mv-cbc-des3-ede",
.cra_priority = 300,
.cra_flags = CRYPTO_ALG_KERN_DRIVER_ONLY | CRYPTO_ALG_ASYNC |
CRYPTO_ALG_ALLOCATES_MEMORY,
.cra_blocksize = DES3_EDE_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct mv_cesa_des3_ctx),
.cra_alignmask = 0,
.cra_module = THIS_MODULE,
.cra_init = mv_cesa_skcipher_cra_init,
.cra_exit = mv_cesa_skcipher_cra_exit,
},
};
static int mv_cesa_aes_op(struct skcipher_request *req,
struct mv_cesa_op_ctx *tmpl)
{
struct mv_cesa_aes_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
int 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.skcipher.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);
return mv_cesa_skcipher_queue_req(req, tmpl);
}
static int mv_cesa_ecb_aes_encrypt(struct skcipher_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 skcipher_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 skcipher_alg mv_cesa_ecb_aes_alg = {
.setkey = mv_cesa_aes_setkey,
.encrypt = mv_cesa_ecb_aes_encrypt,
.decrypt = mv_cesa_ecb_aes_decrypt,
.min_keysize = AES_MIN_KEY_SIZE,
.max_keysize = AES_MAX_KEY_SIZE,
.base = {
.cra_name = "ecb(aes)",
.cra_driver_name = "mv-ecb-aes",
.cra_priority = 300,
.cra_flags = CRYPTO_ALG_KERN_DRIVER_ONLY | CRYPTO_ALG_ASYNC |
CRYPTO_ALG_ALLOCATES_MEMORY,
.cra_blocksize = AES_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct mv_cesa_aes_ctx),
.cra_alignmask = 0,
.cra_module = THIS_MODULE,
.cra_init = mv_cesa_skcipher_cra_init,
.cra_exit = mv_cesa_skcipher_cra_exit,
},
};
static int mv_cesa_cbc_aes_op(struct skcipher_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.skcipher.iv, req->iv, AES_BLOCK_SIZE);
return mv_cesa_aes_op(req, tmpl);
}
static int mv_cesa_cbc_aes_encrypt(struct skcipher_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 skcipher_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 skcipher_alg mv_cesa_cbc_aes_alg = {
.setkey = mv_cesa_aes_setkey,
.encrypt = mv_cesa_cbc_aes_encrypt,
.decrypt = mv_cesa_cbc_aes_decrypt,
.min_keysize = AES_MIN_KEY_SIZE,
.max_keysize = AES_MAX_KEY_SIZE,
.ivsize = AES_BLOCK_SIZE,
.base = {
.cra_name = "cbc(aes)",
.cra_driver_name = "mv-cbc-aes",
.cra_priority = 300,
.cra_flags = CRYPTO_ALG_KERN_DRIVER_ONLY | CRYPTO_ALG_ASYNC |
CRYPTO_ALG_ALLOCATES_MEMORY,
.cra_blocksize = AES_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct mv_cesa_aes_ctx),
.cra_alignmask = 0,
.cra_module = THIS_MODULE,
.cra_init = mv_cesa_skcipher_cra_init,
.cra_exit = mv_cesa_skcipher_cra_exit,
},
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,352 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Provide TDMA helper functions used by cipher and hash algorithm
* implementations.
*
* 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 >
*/
#include "cesa.h"
bool mv_cesa_req_dma_iter_next_transfer(struct mv_cesa_dma_iter *iter,
struct mv_cesa_sg_dma_iter *sgiter,
unsigned int len)
{
if (!sgiter->sg)
return false;
sgiter->op_offset += len;
sgiter->offset += len;
if (sgiter->offset == sg_dma_len(sgiter->sg)) {
if (sg_is_last(sgiter->sg))
return false;
sgiter->offset = 0;
sgiter->sg = sg_next(sgiter->sg);
}
if (sgiter->op_offset == iter->op_len)
return false;
return true;
}
void mv_cesa_dma_step(struct mv_cesa_req *dreq)
{
struct mv_cesa_engine *engine = dreq->engine;
writel_relaxed(0, engine->regs + CESA_SA_CFG);
mv_cesa_set_int_mask(engine, CESA_SA_INT_ACC0_IDMA_DONE);
writel_relaxed(CESA_TDMA_DST_BURST_128B | CESA_TDMA_SRC_BURST_128B |
CESA_TDMA_NO_BYTE_SWAP | CESA_TDMA_EN,
engine->regs + CESA_TDMA_CONTROL);
writel_relaxed(CESA_SA_CFG_ACT_CH0_IDMA | CESA_SA_CFG_MULTI_PKT |
CESA_SA_CFG_CH0_W_IDMA | CESA_SA_CFG_PARA_DIS,
engine->regs + CESA_SA_CFG);
writel_relaxed(dreq->chain.first->cur_dma,
engine->regs + CESA_TDMA_NEXT_ADDR);
WARN_ON(readl(engine->regs + CESA_SA_CMD) &
CESA_SA_CMD_EN_CESA_SA_ACCL0);
writel(CESA_SA_CMD_EN_CESA_SA_ACCL0, engine->regs + CESA_SA_CMD);
}
void mv_cesa_dma_cleanup(struct mv_cesa_req *dreq)
{
struct mv_cesa_tdma_desc *tdma;
for (tdma = dreq->chain.first; tdma;) {
struct mv_cesa_tdma_desc *old_tdma = tdma;
u32 type = tdma->flags & CESA_TDMA_TYPE_MSK;
if (type == CESA_TDMA_OP)
dma_pool_free(cesa_dev->dma->op_pool, tdma->op,
le32_to_cpu(tdma->src));
tdma = tdma->next;
dma_pool_free(cesa_dev->dma->tdma_desc_pool, old_tdma,
old_tdma->cur_dma);
}
dreq->chain.first = NULL;
dreq->chain.last = NULL;
}
void mv_cesa_dma_prepare(struct mv_cesa_req *dreq,
struct mv_cesa_engine *engine)
{
struct mv_cesa_tdma_desc *tdma;
for (tdma = dreq->chain.first; tdma; tdma = tdma->next) {
if (tdma->flags & CESA_TDMA_DST_IN_SRAM)
tdma->dst = cpu_to_le32(tdma->dst_dma + engine->sram_dma);
if (tdma->flags & CESA_TDMA_SRC_IN_SRAM)
tdma->src = cpu_to_le32(tdma->src_dma + engine->sram_dma);
if ((tdma->flags & CESA_TDMA_TYPE_MSK) == CESA_TDMA_OP)
mv_cesa_adjust_op(engine, tdma->op);
}
}
void mv_cesa_tdma_chain(struct mv_cesa_engine *engine,
struct mv_cesa_req *dreq)
{
if (engine->chain.first == NULL && engine->chain.last == NULL) {
engine->chain.first = dreq->chain.first;
engine->chain.last = dreq->chain.last;
} else {
struct mv_cesa_tdma_desc *last;
last = engine->chain.last;
last->next = dreq->chain.first;
engine->chain.last = dreq->chain.last;
/*
* Break the DMA chain if the CESA_TDMA_BREAK_CHAIN is set on
* the last element of the current chain, or if the request
* being queued needs the IV regs to be set before lauching
* the request.
*/
if (!(last->flags & CESA_TDMA_BREAK_CHAIN) &&
!(dreq->chain.first->flags & CESA_TDMA_SET_STATE))
last->next_dma = cpu_to_le32(dreq->chain.first->cur_dma);
}
}
int mv_cesa_tdma_process(struct mv_cesa_engine *engine, u32 status)
{
struct crypto_async_request *req = NULL;
struct mv_cesa_tdma_desc *tdma = NULL, *next = NULL;
dma_addr_t tdma_cur;
int res = 0;
tdma_cur = readl(engine->regs + CESA_TDMA_CUR);
for (tdma = engine->chain.first; tdma; tdma = next) {
spin_lock_bh(&engine->lock);
next = tdma->next;
spin_unlock_bh(&engine->lock);
if (tdma->flags & CESA_TDMA_END_OF_REQ) {
struct crypto_async_request *backlog = NULL;
struct mv_cesa_ctx *ctx;
u32 current_status;
spin_lock_bh(&engine->lock);
/*
* if req is NULL, this means we're processing the
* request in engine->req.
*/
if (!req)
req = engine->req;
else
req = mv_cesa_dequeue_req_locked(engine,
&backlog);
/* Re-chaining to the next request */
engine->chain.first = tdma->next;
tdma->next = NULL;
/* If this is the last request, clear the chain */
if (engine->chain.first == NULL)
engine->chain.last = NULL;
spin_unlock_bh(&engine->lock);
ctx = crypto_tfm_ctx(req->tfm);
current_status = (tdma->cur_dma == tdma_cur) ?
status : CESA_SA_INT_ACC0_IDMA_DONE;
res = ctx->ops->process(req, current_status);
ctx->ops->complete(req);
if (res == 0)
mv_cesa_engine_enqueue_complete_request(engine,
req);
if (backlog)
backlog->complete(backlog, -EINPROGRESS);
}
if (res || tdma->cur_dma == tdma_cur)
break;
}
/*
* Save the last request in error to engine->req, so that the core
* knows which request was fautly
*/
if (res) {
spin_lock_bh(&engine->lock);
engine->req = req;
spin_unlock_bh(&engine->lock);
}
return res;
}
static struct mv_cesa_tdma_desc *
mv_cesa_dma_add_desc(struct mv_cesa_tdma_chain *chain, gfp_t flags)
{
struct mv_cesa_tdma_desc *new_tdma = NULL;
dma_addr_t dma_handle;
new_tdma = dma_pool_zalloc(cesa_dev->dma->tdma_desc_pool, flags,
&dma_handle);
if (!new_tdma)
return ERR_PTR(-ENOMEM);
new_tdma->cur_dma = dma_handle;
if (chain->last) {
chain->last->next_dma = cpu_to_le32(dma_handle);
chain->last->next = new_tdma;
} else {
chain->first = new_tdma;
}
chain->last = new_tdma;
return new_tdma;
}
int mv_cesa_dma_add_result_op(struct mv_cesa_tdma_chain *chain, dma_addr_t src,
u32 size, u32 flags, gfp_t gfp_flags)
{
struct mv_cesa_tdma_desc *tdma, *op_desc;
tdma = mv_cesa_dma_add_desc(chain, gfp_flags);
if (IS_ERR(tdma))
return PTR_ERR(tdma);
/* We re-use an existing op_desc object to retrieve the context
* and result instead of allocating a new one.
* There is at least one object of this type in a CESA crypto
* req, just pick the first one in the chain.
*/
for (op_desc = chain->first; op_desc; op_desc = op_desc->next) {
u32 type = op_desc->flags & CESA_TDMA_TYPE_MSK;
if (type == CESA_TDMA_OP)
break;
}
if (!op_desc)
return -EIO;
tdma->byte_cnt = cpu_to_le32(size | BIT(31));
tdma->src_dma = src;
tdma->dst_dma = op_desc->src_dma;
tdma->op = op_desc->op;
flags &= (CESA_TDMA_DST_IN_SRAM | CESA_TDMA_SRC_IN_SRAM);
tdma->flags = flags | CESA_TDMA_RESULT;
return 0;
}
struct mv_cesa_op_ctx *mv_cesa_dma_add_op(struct mv_cesa_tdma_chain *chain,
const struct mv_cesa_op_ctx *op_templ,
bool skip_ctx,
gfp_t flags)
{
struct mv_cesa_tdma_desc *tdma;
struct mv_cesa_op_ctx *op;
dma_addr_t dma_handle;
unsigned int size;
tdma = mv_cesa_dma_add_desc(chain, flags);
if (IS_ERR(tdma))
return ERR_CAST(tdma);
op = dma_pool_alloc(cesa_dev->dma->op_pool, flags, &dma_handle);
if (!op)
return ERR_PTR(-ENOMEM);
*op = *op_templ;
size = skip_ctx ? sizeof(op->desc) : sizeof(*op);
tdma = chain->last;
tdma->op = op;
tdma->byte_cnt = cpu_to_le32(size | BIT(31));
tdma->src = cpu_to_le32(dma_handle);
tdma->dst_dma = CESA_SA_CFG_SRAM_OFFSET;
tdma->flags = CESA_TDMA_DST_IN_SRAM | CESA_TDMA_OP;
return op;
}
int mv_cesa_dma_add_data_transfer(struct mv_cesa_tdma_chain *chain,
dma_addr_t dst, dma_addr_t src, u32 size,
u32 flags, gfp_t gfp_flags)
{
struct mv_cesa_tdma_desc *tdma;
tdma = mv_cesa_dma_add_desc(chain, gfp_flags);
if (IS_ERR(tdma))
return PTR_ERR(tdma);
tdma->byte_cnt = cpu_to_le32(size | BIT(31));
tdma->src_dma = src;
tdma->dst_dma = dst;
flags &= (CESA_TDMA_DST_IN_SRAM | CESA_TDMA_SRC_IN_SRAM);
tdma->flags = flags | CESA_TDMA_DATA;
return 0;
}
int mv_cesa_dma_add_dummy_launch(struct mv_cesa_tdma_chain *chain, gfp_t flags)
{
struct mv_cesa_tdma_desc *tdma;
tdma = mv_cesa_dma_add_desc(chain, flags);
return PTR_ERR_OR_ZERO(tdma);
}
int mv_cesa_dma_add_dummy_end(struct mv_cesa_tdma_chain *chain, gfp_t flags)
{
struct mv_cesa_tdma_desc *tdma;
tdma = mv_cesa_dma_add_desc(chain, flags);
if (IS_ERR(tdma))
return PTR_ERR(tdma);
tdma->byte_cnt = cpu_to_le32(BIT(31));
return 0;
}
int mv_cesa_dma_add_op_transfers(struct mv_cesa_tdma_chain *chain,
struct mv_cesa_dma_iter *dma_iter,
struct mv_cesa_sg_dma_iter *sgiter,
gfp_t gfp_flags)
{
u32 flags = sgiter->dir == DMA_TO_DEVICE ?
CESA_TDMA_DST_IN_SRAM : CESA_TDMA_SRC_IN_SRAM;
unsigned int len;
do {
dma_addr_t dst, src;
int ret;
len = mv_cesa_req_dma_iter_transfer_len(dma_iter, sgiter);
if (sgiter->dir == DMA_TO_DEVICE) {
dst = CESA_SA_DATA_SRAM_OFFSET + sgiter->op_offset;
src = sg_dma_address(sgiter->sg) + sgiter->offset;
} else {
dst = sg_dma_address(sgiter->sg) + sgiter->offset;
src = CESA_SA_DATA_SRAM_OFFSET + sgiter->op_offset;
}
ret = mv_cesa_dma_add_data_transfer(chain, dst, src, len,
flags, gfp_flags);
if (ret)
return ret;
} while (mv_cesa_req_dma_iter_next_transfer(dma_iter, sgiter, len));
return 0;
}