162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * sun8i-ce-prng.c - hardware cryptographic offloader for 462306a36Sopenharmony_ci * Allwinner H3/A64/H5/H2+/H6/R40 SoC 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (C) 2015-2020 Corentin Labbe <clabbe@baylibre.com> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * This file handle the PRNG 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * You could find a link for the datasheet in Documentation/arch/arm/sunxi.rst 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci#include "sun8i-ce.h" 1362306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1462306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1562306a36Sopenharmony_ci#include <crypto/internal/rng.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ciint sun8i_ce_prng_init(struct crypto_tfm *tfm) 1862306a36Sopenharmony_ci{ 1962306a36Sopenharmony_ci struct sun8i_ce_rng_tfm_ctx *ctx = crypto_tfm_ctx(tfm); 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci memset(ctx, 0, sizeof(struct sun8i_ce_rng_tfm_ctx)); 2262306a36Sopenharmony_ci return 0; 2362306a36Sopenharmony_ci} 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_civoid sun8i_ce_prng_exit(struct crypto_tfm *tfm) 2662306a36Sopenharmony_ci{ 2762306a36Sopenharmony_ci struct sun8i_ce_rng_tfm_ctx *ctx = crypto_tfm_ctx(tfm); 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci kfree_sensitive(ctx->seed); 3062306a36Sopenharmony_ci ctx->seed = NULL; 3162306a36Sopenharmony_ci ctx->slen = 0; 3262306a36Sopenharmony_ci} 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ciint sun8i_ce_prng_seed(struct crypto_rng *tfm, const u8 *seed, 3562306a36Sopenharmony_ci unsigned int slen) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci struct sun8i_ce_rng_tfm_ctx *ctx = crypto_rng_ctx(tfm); 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci if (ctx->seed && ctx->slen != slen) { 4062306a36Sopenharmony_ci kfree_sensitive(ctx->seed); 4162306a36Sopenharmony_ci ctx->slen = 0; 4262306a36Sopenharmony_ci ctx->seed = NULL; 4362306a36Sopenharmony_ci } 4462306a36Sopenharmony_ci if (!ctx->seed) 4562306a36Sopenharmony_ci ctx->seed = kmalloc(slen, GFP_KERNEL | GFP_DMA); 4662306a36Sopenharmony_ci if (!ctx->seed) 4762306a36Sopenharmony_ci return -ENOMEM; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci memcpy(ctx->seed, seed, slen); 5062306a36Sopenharmony_ci ctx->slen = slen; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci return 0; 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ciint sun8i_ce_prng_generate(struct crypto_rng *tfm, const u8 *src, 5662306a36Sopenharmony_ci unsigned int slen, u8 *dst, unsigned int dlen) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci struct sun8i_ce_rng_tfm_ctx *ctx = crypto_rng_ctx(tfm); 5962306a36Sopenharmony_ci struct rng_alg *alg = crypto_rng_alg(tfm); 6062306a36Sopenharmony_ci struct sun8i_ce_alg_template *algt; 6162306a36Sopenharmony_ci struct sun8i_ce_dev *ce; 6262306a36Sopenharmony_ci dma_addr_t dma_iv, dma_dst; 6362306a36Sopenharmony_ci int err = 0; 6462306a36Sopenharmony_ci int flow = 3; 6562306a36Sopenharmony_ci unsigned int todo; 6662306a36Sopenharmony_ci struct sun8i_ce_flow *chan; 6762306a36Sopenharmony_ci struct ce_task *cet; 6862306a36Sopenharmony_ci u32 common, sym; 6962306a36Sopenharmony_ci void *d; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci algt = container_of(alg, struct sun8i_ce_alg_template, alg.rng); 7262306a36Sopenharmony_ci ce = algt->ce; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci if (ctx->slen == 0) { 7562306a36Sopenharmony_ci dev_err(ce->dev, "not seeded\n"); 7662306a36Sopenharmony_ci return -EINVAL; 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci /* we want dlen + seedsize rounded up to a multiple of PRNG_DATA_SIZE */ 8062306a36Sopenharmony_ci todo = dlen + ctx->slen + PRNG_DATA_SIZE * 2; 8162306a36Sopenharmony_ci todo -= todo % PRNG_DATA_SIZE; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci d = kzalloc(todo, GFP_KERNEL | GFP_DMA); 8462306a36Sopenharmony_ci if (!d) { 8562306a36Sopenharmony_ci err = -ENOMEM; 8662306a36Sopenharmony_ci goto err_mem; 8762306a36Sopenharmony_ci } 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci dev_dbg(ce->dev, "%s PRNG slen=%u dlen=%u todo=%u multi=%u\n", __func__, 9062306a36Sopenharmony_ci slen, dlen, todo, todo / PRNG_DATA_SIZE); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci#ifdef CONFIG_CRYPTO_DEV_SUN8I_CE_DEBUG 9362306a36Sopenharmony_ci algt->stat_req++; 9462306a36Sopenharmony_ci algt->stat_bytes += todo; 9562306a36Sopenharmony_ci#endif 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci dma_iv = dma_map_single(ce->dev, ctx->seed, ctx->slen, DMA_TO_DEVICE); 9862306a36Sopenharmony_ci if (dma_mapping_error(ce->dev, dma_iv)) { 9962306a36Sopenharmony_ci dev_err(ce->dev, "Cannot DMA MAP IV\n"); 10062306a36Sopenharmony_ci err = -EFAULT; 10162306a36Sopenharmony_ci goto err_iv; 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci dma_dst = dma_map_single(ce->dev, d, todo, DMA_FROM_DEVICE); 10562306a36Sopenharmony_ci if (dma_mapping_error(ce->dev, dma_dst)) { 10662306a36Sopenharmony_ci dev_err(ce->dev, "Cannot DMA MAP DST\n"); 10762306a36Sopenharmony_ci err = -EFAULT; 10862306a36Sopenharmony_ci goto err_dst; 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci err = pm_runtime_resume_and_get(ce->dev); 11262306a36Sopenharmony_ci if (err < 0) 11362306a36Sopenharmony_ci goto err_pm; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci mutex_lock(&ce->rnglock); 11662306a36Sopenharmony_ci chan = &ce->chanlist[flow]; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci cet = &chan->tl[0]; 11962306a36Sopenharmony_ci memset(cet, 0, sizeof(struct ce_task)); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci cet->t_id = cpu_to_le32(flow); 12262306a36Sopenharmony_ci common = ce->variant->prng | CE_COMM_INT; 12362306a36Sopenharmony_ci cet->t_common_ctl = cpu_to_le32(common); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci /* recent CE (H6) need length in bytes, in word otherwise */ 12662306a36Sopenharmony_ci if (ce->variant->prng_t_dlen_in_bytes) 12762306a36Sopenharmony_ci cet->t_dlen = cpu_to_le32(todo); 12862306a36Sopenharmony_ci else 12962306a36Sopenharmony_ci cet->t_dlen = cpu_to_le32(todo / 4); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci sym = PRNG_LD; 13262306a36Sopenharmony_ci cet->t_sym_ctl = cpu_to_le32(sym); 13362306a36Sopenharmony_ci cet->t_asym_ctl = 0; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci cet->t_key = cpu_to_le32(dma_iv); 13662306a36Sopenharmony_ci cet->t_iv = cpu_to_le32(dma_iv); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci cet->t_dst[0].addr = cpu_to_le32(dma_dst); 13962306a36Sopenharmony_ci cet->t_dst[0].len = cpu_to_le32(todo / 4); 14062306a36Sopenharmony_ci ce->chanlist[flow].timeout = 2000; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci err = sun8i_ce_run_task(ce, 3, "PRNG"); 14362306a36Sopenharmony_ci mutex_unlock(&ce->rnglock); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci pm_runtime_put(ce->dev); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cierr_pm: 14862306a36Sopenharmony_ci dma_unmap_single(ce->dev, dma_dst, todo, DMA_FROM_DEVICE); 14962306a36Sopenharmony_cierr_dst: 15062306a36Sopenharmony_ci dma_unmap_single(ce->dev, dma_iv, ctx->slen, DMA_TO_DEVICE); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci if (!err) { 15362306a36Sopenharmony_ci memcpy(dst, d, dlen); 15462306a36Sopenharmony_ci memcpy(ctx->seed, d + dlen, ctx->slen); 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_cierr_iv: 15762306a36Sopenharmony_ci kfree_sensitive(d); 15862306a36Sopenharmony_cierr_mem: 15962306a36Sopenharmony_ci return err; 16062306a36Sopenharmony_ci} 161