sun8i-ce-prng.c (3794B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * sun8i-ce-prng.c - hardware cryptographic offloader for 4 * Allwinner H3/A64/H5/H2+/H6/R40 SoC 5 * 6 * Copyright (C) 2015-2020 Corentin Labbe <clabbe@baylibre.com> 7 * 8 * This file handle the PRNG 9 * 10 * You could find a link for the datasheet in Documentation/arm/sunxi.rst 11 */ 12#include "sun8i-ce.h" 13#include <linux/dma-mapping.h> 14#include <linux/pm_runtime.h> 15#include <crypto/internal/rng.h> 16 17int sun8i_ce_prng_init(struct crypto_tfm *tfm) 18{ 19 struct sun8i_ce_rng_tfm_ctx *ctx = crypto_tfm_ctx(tfm); 20 21 memset(ctx, 0, sizeof(struct sun8i_ce_rng_tfm_ctx)); 22 return 0; 23} 24 25void sun8i_ce_prng_exit(struct crypto_tfm *tfm) 26{ 27 struct sun8i_ce_rng_tfm_ctx *ctx = crypto_tfm_ctx(tfm); 28 29 kfree_sensitive(ctx->seed); 30 ctx->seed = NULL; 31 ctx->slen = 0; 32} 33 34int sun8i_ce_prng_seed(struct crypto_rng *tfm, const u8 *seed, 35 unsigned int slen) 36{ 37 struct sun8i_ce_rng_tfm_ctx *ctx = crypto_rng_ctx(tfm); 38 39 if (ctx->seed && ctx->slen != slen) { 40 kfree_sensitive(ctx->seed); 41 ctx->slen = 0; 42 ctx->seed = NULL; 43 } 44 if (!ctx->seed) 45 ctx->seed = kmalloc(slen, GFP_KERNEL | GFP_DMA); 46 if (!ctx->seed) 47 return -ENOMEM; 48 49 memcpy(ctx->seed, seed, slen); 50 ctx->slen = slen; 51 52 return 0; 53} 54 55int sun8i_ce_prng_generate(struct crypto_rng *tfm, const u8 *src, 56 unsigned int slen, u8 *dst, unsigned int dlen) 57{ 58 struct sun8i_ce_rng_tfm_ctx *ctx = crypto_rng_ctx(tfm); 59 struct rng_alg *alg = crypto_rng_alg(tfm); 60 struct sun8i_ce_alg_template *algt; 61 struct sun8i_ce_dev *ce; 62 dma_addr_t dma_iv, dma_dst; 63 int err = 0; 64 int flow = 3; 65 unsigned int todo; 66 struct sun8i_ce_flow *chan; 67 struct ce_task *cet; 68 u32 common, sym; 69 void *d; 70 71 algt = container_of(alg, struct sun8i_ce_alg_template, alg.rng); 72 ce = algt->ce; 73 74 if (ctx->slen == 0) { 75 dev_err(ce->dev, "not seeded\n"); 76 return -EINVAL; 77 } 78 79 /* we want dlen + seedsize rounded up to a multiple of PRNG_DATA_SIZE */ 80 todo = dlen + ctx->slen + PRNG_DATA_SIZE * 2; 81 todo -= todo % PRNG_DATA_SIZE; 82 83 d = kzalloc(todo, GFP_KERNEL | GFP_DMA); 84 if (!d) { 85 err = -ENOMEM; 86 goto err_mem; 87 } 88 89 dev_dbg(ce->dev, "%s PRNG slen=%u dlen=%u todo=%u multi=%u\n", __func__, 90 slen, dlen, todo, todo / PRNG_DATA_SIZE); 91 92#ifdef CONFIG_CRYPTO_DEV_SUN8I_CE_DEBUG 93 algt->stat_req++; 94 algt->stat_bytes += todo; 95#endif 96 97 dma_iv = dma_map_single(ce->dev, ctx->seed, ctx->slen, DMA_TO_DEVICE); 98 if (dma_mapping_error(ce->dev, dma_iv)) { 99 dev_err(ce->dev, "Cannot DMA MAP IV\n"); 100 err = -EFAULT; 101 goto err_iv; 102 } 103 104 dma_dst = dma_map_single(ce->dev, d, todo, DMA_FROM_DEVICE); 105 if (dma_mapping_error(ce->dev, dma_dst)) { 106 dev_err(ce->dev, "Cannot DMA MAP DST\n"); 107 err = -EFAULT; 108 goto err_dst; 109 } 110 111 err = pm_runtime_resume_and_get(ce->dev); 112 if (err < 0) 113 goto err_pm; 114 115 mutex_lock(&ce->rnglock); 116 chan = &ce->chanlist[flow]; 117 118 cet = &chan->tl[0]; 119 memset(cet, 0, sizeof(struct ce_task)); 120 121 cet->t_id = cpu_to_le32(flow); 122 common = ce->variant->prng | CE_COMM_INT; 123 cet->t_common_ctl = cpu_to_le32(common); 124 125 /* recent CE (H6) need length in bytes, in word otherwise */ 126 if (ce->variant->prng_t_dlen_in_bytes) 127 cet->t_dlen = cpu_to_le32(todo); 128 else 129 cet->t_dlen = cpu_to_le32(todo / 4); 130 131 sym = PRNG_LD; 132 cet->t_sym_ctl = cpu_to_le32(sym); 133 cet->t_asym_ctl = 0; 134 135 cet->t_key = cpu_to_le32(dma_iv); 136 cet->t_iv = cpu_to_le32(dma_iv); 137 138 cet->t_dst[0].addr = cpu_to_le32(dma_dst); 139 cet->t_dst[0].len = cpu_to_le32(todo / 4); 140 ce->chanlist[flow].timeout = 2000; 141 142 err = sun8i_ce_run_task(ce, 3, "PRNG"); 143 mutex_unlock(&ce->rnglock); 144 145 pm_runtime_put(ce->dev); 146 147err_pm: 148 dma_unmap_single(ce->dev, dma_dst, todo, DMA_FROM_DEVICE); 149err_dst: 150 dma_unmap_single(ce->dev, dma_iv, ctx->slen, DMA_TO_DEVICE); 151 152 if (!err) { 153 memcpy(dst, d, dlen); 154 memcpy(ctx->seed, d + dlen, ctx->slen); 155 } 156err_iv: 157 kfree_sensitive(d); 158err_mem: 159 return err; 160}