162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Driver to expose SEC4 PRNG via crypto RNG API 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2022 NXP 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/completion.h> 1062306a36Sopenharmony_ci#include <crypto/internal/rng.h> 1162306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include "compat.h" 1462306a36Sopenharmony_ci#include "regs.h" 1562306a36Sopenharmony_ci#include "intern.h" 1662306a36Sopenharmony_ci#include "desc_constr.h" 1762306a36Sopenharmony_ci#include "jr.h" 1862306a36Sopenharmony_ci#include "error.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci/* 2162306a36Sopenharmony_ci * Length of used descriptors, see caam_init_desc() 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_ci#define CAAM_PRNG_MAX_DESC_LEN (CAAM_CMD_SZ + \ 2462306a36Sopenharmony_ci CAAM_CMD_SZ + \ 2562306a36Sopenharmony_ci CAAM_CMD_SZ + CAAM_PTR_SZ_MAX) 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/* prng per-device context */ 2862306a36Sopenharmony_cistruct caam_prng_ctx { 2962306a36Sopenharmony_ci int err; 3062306a36Sopenharmony_ci struct completion done; 3162306a36Sopenharmony_ci}; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistruct caam_prng_alg { 3462306a36Sopenharmony_ci struct rng_alg rng; 3562306a36Sopenharmony_ci bool registered; 3662306a36Sopenharmony_ci}; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic void caam_prng_done(struct device *jrdev, u32 *desc, u32 err, 3962306a36Sopenharmony_ci void *context) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci struct caam_prng_ctx *jctx = context; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci jctx->err = err ? caam_jr_strstatus(jrdev, err) : 0; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci complete(&jctx->done); 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic u32 *caam_init_reseed_desc(u32 *desc) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci init_job_desc(desc, 0); /* + 1 cmd_sz */ 5162306a36Sopenharmony_ci /* Generate random bytes: + 1 cmd_sz */ 5262306a36Sopenharmony_ci append_operation(desc, OP_TYPE_CLASS1_ALG | OP_ALG_ALGSEL_RNG | 5362306a36Sopenharmony_ci OP_ALG_AS_FINALIZE); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci print_hex_dump_debug("prng reseed desc@: ", DUMP_PREFIX_ADDRESS, 5662306a36Sopenharmony_ci 16, 4, desc, desc_bytes(desc), 1); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci return desc; 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic u32 *caam_init_prng_desc(u32 *desc, dma_addr_t dst_dma, u32 len) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci init_job_desc(desc, 0); /* + 1 cmd_sz */ 6462306a36Sopenharmony_ci /* Generate random bytes: + 1 cmd_sz */ 6562306a36Sopenharmony_ci append_operation(desc, OP_ALG_ALGSEL_RNG | OP_TYPE_CLASS1_ALG); 6662306a36Sopenharmony_ci /* Store bytes: + 1 cmd_sz + caam_ptr_sz */ 6762306a36Sopenharmony_ci append_fifo_store(desc, dst_dma, 6862306a36Sopenharmony_ci len, FIFOST_TYPE_RNGSTORE); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci print_hex_dump_debug("prng job desc@: ", DUMP_PREFIX_ADDRESS, 7162306a36Sopenharmony_ci 16, 4, desc, desc_bytes(desc), 1); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci return desc; 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic int caam_prng_generate(struct crypto_rng *tfm, 7762306a36Sopenharmony_ci const u8 *src, unsigned int slen, 7862306a36Sopenharmony_ci u8 *dst, unsigned int dlen) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci unsigned int aligned_dlen = ALIGN(dlen, dma_get_cache_alignment()); 8162306a36Sopenharmony_ci struct caam_prng_ctx ctx; 8262306a36Sopenharmony_ci struct device *jrdev; 8362306a36Sopenharmony_ci dma_addr_t dst_dma; 8462306a36Sopenharmony_ci u32 *desc; 8562306a36Sopenharmony_ci u8 *buf; 8662306a36Sopenharmony_ci int ret; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci if (aligned_dlen < dlen) 8962306a36Sopenharmony_ci return -EOVERFLOW; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci buf = kzalloc(aligned_dlen, GFP_KERNEL); 9262306a36Sopenharmony_ci if (!buf) 9362306a36Sopenharmony_ci return -ENOMEM; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci jrdev = caam_jr_alloc(); 9662306a36Sopenharmony_ci ret = PTR_ERR_OR_ZERO(jrdev); 9762306a36Sopenharmony_ci if (ret) { 9862306a36Sopenharmony_ci pr_err("Job Ring Device allocation failed\n"); 9962306a36Sopenharmony_ci kfree(buf); 10062306a36Sopenharmony_ci return ret; 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci desc = kzalloc(CAAM_PRNG_MAX_DESC_LEN, GFP_KERNEL); 10462306a36Sopenharmony_ci if (!desc) { 10562306a36Sopenharmony_ci ret = -ENOMEM; 10662306a36Sopenharmony_ci goto out1; 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci dst_dma = dma_map_single(jrdev, buf, dlen, DMA_FROM_DEVICE); 11062306a36Sopenharmony_ci if (dma_mapping_error(jrdev, dst_dma)) { 11162306a36Sopenharmony_ci dev_err(jrdev, "Failed to map destination buffer memory\n"); 11262306a36Sopenharmony_ci ret = -ENOMEM; 11362306a36Sopenharmony_ci goto out; 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci init_completion(&ctx.done); 11762306a36Sopenharmony_ci ret = caam_jr_enqueue(jrdev, 11862306a36Sopenharmony_ci caam_init_prng_desc(desc, dst_dma, dlen), 11962306a36Sopenharmony_ci caam_prng_done, &ctx); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci if (ret == -EINPROGRESS) { 12262306a36Sopenharmony_ci wait_for_completion(&ctx.done); 12362306a36Sopenharmony_ci ret = ctx.err; 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci dma_unmap_single(jrdev, dst_dma, dlen, DMA_FROM_DEVICE); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci if (!ret) 12962306a36Sopenharmony_ci memcpy(dst, buf, dlen); 13062306a36Sopenharmony_ciout: 13162306a36Sopenharmony_ci kfree(desc); 13262306a36Sopenharmony_ciout1: 13362306a36Sopenharmony_ci caam_jr_free(jrdev); 13462306a36Sopenharmony_ci kfree(buf); 13562306a36Sopenharmony_ci return ret; 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic void caam_prng_exit(struct crypto_tfm *tfm) {} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic int caam_prng_init(struct crypto_tfm *tfm) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci return 0; 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic int caam_prng_seed(struct crypto_rng *tfm, 14662306a36Sopenharmony_ci const u8 *seed, unsigned int slen) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci struct caam_prng_ctx ctx; 14962306a36Sopenharmony_ci struct device *jrdev; 15062306a36Sopenharmony_ci u32 *desc; 15162306a36Sopenharmony_ci int ret; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci if (slen) { 15462306a36Sopenharmony_ci pr_err("Seed length should be zero\n"); 15562306a36Sopenharmony_ci return -EINVAL; 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci jrdev = caam_jr_alloc(); 15962306a36Sopenharmony_ci ret = PTR_ERR_OR_ZERO(jrdev); 16062306a36Sopenharmony_ci if (ret) { 16162306a36Sopenharmony_ci pr_err("Job Ring Device allocation failed\n"); 16262306a36Sopenharmony_ci return ret; 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci desc = kzalloc(CAAM_PRNG_MAX_DESC_LEN, GFP_KERNEL); 16662306a36Sopenharmony_ci if (!desc) { 16762306a36Sopenharmony_ci caam_jr_free(jrdev); 16862306a36Sopenharmony_ci return -ENOMEM; 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci init_completion(&ctx.done); 17262306a36Sopenharmony_ci ret = caam_jr_enqueue(jrdev, 17362306a36Sopenharmony_ci caam_init_reseed_desc(desc), 17462306a36Sopenharmony_ci caam_prng_done, &ctx); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if (ret == -EINPROGRESS) { 17762306a36Sopenharmony_ci wait_for_completion(&ctx.done); 17862306a36Sopenharmony_ci ret = ctx.err; 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci kfree(desc); 18262306a36Sopenharmony_ci caam_jr_free(jrdev); 18362306a36Sopenharmony_ci return ret; 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cistatic struct caam_prng_alg caam_prng_alg = { 18762306a36Sopenharmony_ci .rng = { 18862306a36Sopenharmony_ci .generate = caam_prng_generate, 18962306a36Sopenharmony_ci .seed = caam_prng_seed, 19062306a36Sopenharmony_ci .seedsize = 0, 19162306a36Sopenharmony_ci .base = { 19262306a36Sopenharmony_ci .cra_name = "stdrng", 19362306a36Sopenharmony_ci .cra_driver_name = "prng-caam", 19462306a36Sopenharmony_ci .cra_priority = 500, 19562306a36Sopenharmony_ci .cra_ctxsize = sizeof(struct caam_prng_ctx), 19662306a36Sopenharmony_ci .cra_module = THIS_MODULE, 19762306a36Sopenharmony_ci .cra_init = caam_prng_init, 19862306a36Sopenharmony_ci .cra_exit = caam_prng_exit, 19962306a36Sopenharmony_ci }, 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci}; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_civoid caam_prng_unregister(void *data) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci if (caam_prng_alg.registered) 20662306a36Sopenharmony_ci crypto_unregister_rng(&caam_prng_alg.rng); 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ciint caam_prng_register(struct device *ctrldev) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci struct caam_drv_private *priv = dev_get_drvdata(ctrldev); 21262306a36Sopenharmony_ci u32 rng_inst; 21362306a36Sopenharmony_ci int ret = 0; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci /* Check for available RNG blocks before registration */ 21662306a36Sopenharmony_ci if (priv->era < 10) 21762306a36Sopenharmony_ci rng_inst = (rd_reg32(&priv->jr[0]->perfmon.cha_num_ls) & 21862306a36Sopenharmony_ci CHA_ID_LS_RNG_MASK) >> CHA_ID_LS_RNG_SHIFT; 21962306a36Sopenharmony_ci else 22062306a36Sopenharmony_ci rng_inst = rd_reg32(&priv->jr[0]->vreg.rng) & CHA_VER_NUM_MASK; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci if (!rng_inst) { 22362306a36Sopenharmony_ci dev_dbg(ctrldev, "RNG block is not available... skipping registering algorithm\n"); 22462306a36Sopenharmony_ci return ret; 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci ret = crypto_register_rng(&caam_prng_alg.rng); 22862306a36Sopenharmony_ci if (ret) { 22962306a36Sopenharmony_ci dev_err(ctrldev, 23062306a36Sopenharmony_ci "couldn't register rng crypto alg: %d\n", 23162306a36Sopenharmony_ci ret); 23262306a36Sopenharmony_ci return ret; 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci caam_prng_alg.registered = true; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci dev_info(ctrldev, 23862306a36Sopenharmony_ci "rng crypto API alg registered %s\n", caam_prng_alg.rng.base.cra_driver_name); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci return 0; 24162306a36Sopenharmony_ci} 242