// SPDX-License-Identifier: GPL-2.0 /* * sun8i-ce-core.c - hardware cryptographic offloader for * Allwinner H3/A64/H5/H2+/H6/R40 SoC * * Copyright (C) 2015-2019 Corentin Labbe <clabbe.montjoie@gmail.com> * * Core file which registers crypto algorithms supported by the CryptoEngine. * * You could find a link for the datasheet in Documentation/arch/arm/sunxi.rst
*/
/* * mod clock is lower on H3 than other SoC due to some DMA timeout occurring * with high value. * If you want to tune mod clock, loading driver and passing selftest is * insufficient, you need to test with some LUKS test (mount and write to it)
*/ staticconststruct ce_variant ce_h3_variant = {
.alg_cipher = { CE_ALG_AES, CE_ALG_DES, CE_ALG_3DES,
},
.alg_hash = { CE_ALG_MD5, CE_ALG_SHA1, CE_ALG_SHA224, CE_ALG_SHA256,
CE_ALG_SHA384, CE_ALG_SHA512
},
.op_mode = { CE_OP_ECB, CE_OP_CBC
},
.ce_clks = {
{ "bus", 0, 200000000 },
{ "mod", 50000000, 0 },
},
.esr = ESR_H3,
.prng = CE_ALG_PRNG,
.trng = CE_ID_NOTSUPP,
};
/* * sun8i_ce_get_engine_number() get the next channel slot * This is a simple round-robin way of getting the next channel * The flow 3 is reserve for xRNG operations
*/ int sun8i_ce_get_engine_number(struct sun8i_ce_dev *ce)
{ return atomic_inc_return(&ce->flow) % (MAXFLOW - 1);
}
int sun8i_ce_run_task(struct sun8i_ce_dev *ce, int flow, constchar *name)
{
u32 v; int err = 0; struct ce_task *cet = ce->chanlist[flow].tl;
ce->chanlist[flow].status = 0; /* Be sure all data is written before enabling the task */
wmb();
/* Only H6 needs to write a part of t_common_ctl along with "1", but since it is ignored * on older SoCs, we have no reason to complicate things.
*/
v = 1 | ((le32_to_cpu(ce->chanlist[flow].tl->t_common_ctl) & 0x7F) << 8);
writel(v, ce->base + CE_TLR);
mutex_unlock(&ce->mlock);
if (ce->chanlist[flow].status == 0) {
dev_err(ce->dev, "DMA timeout for %s on flow %d\n", name, flow);
err = -EFAULT;
} /* No need to lock for this read, the channel is locked so * nothing could modify the error value for this channel
*/
v = readl(ce->base + CE_ESR); switch (ce->variant->esr) { case ESR_H3: /* Sadly, the error bit is not per flow */ if (v) {
dev_err(ce->dev, "CE ERROR: %x for flow %x\n", v, flow);
err = -EFAULT;
print_hex_dump(KERN_INFO, "TASK: ", DUMP_PREFIX_NONE, 16, 4,
cet, sizeof(struct ce_task), false);
} if (v & CE_ERR_ALGO_NOTSUP)
dev_err(ce->dev, "CE ERROR: algorithm not supported\n"); if (v & CE_ERR_DATALEN)
dev_err(ce->dev, "CE ERROR: data length error\n"); if (v & CE_ERR_KEYSRAM)
dev_err(ce->dev, "CE ERROR: keysram access error for AES\n"); break; case ESR_A64: case ESR_D1: case ESR_H5: case ESR_R40:
v >>= (flow * 4);
v &= 0xF; if (v) {
dev_err(ce->dev, "CE ERROR: %x for flow %x\n", v, flow);
err = -EFAULT;
print_hex_dump(KERN_INFO, "TASK: ", DUMP_PREFIX_NONE, 16, 4,
cet, sizeof(struct ce_task), false);
} if (v & CE_ERR_ALGO_NOTSUP)
dev_err(ce->dev, "CE ERROR: algorithm not supported\n"); if (v & CE_ERR_DATALEN)
dev_err(ce->dev, "CE ERROR: data length error\n"); if (v & CE_ERR_KEYSRAM)
dev_err(ce->dev, "CE ERROR: keysram access error for AES\n"); break; case ESR_H6:
v >>= (flow * 8);
v &= 0xFF; if (v) {
dev_err(ce->dev, "CE ERROR: %x for flow %x\n", v, flow);
err = -EFAULT;
print_hex_dump(KERN_INFO, "TASK: ", DUMP_PREFIX_NONE, 16, 4,
cet, sizeof(struct ce_task), false);
} if (v & CE_ERR_ALGO_NOTSUP)
dev_err(ce->dev, "CE ERROR: algorithm not supported\n"); if (v & CE_ERR_DATALEN)
dev_err(ce->dev, "CE ERROR: data length error\n"); if (v & CE_ERR_KEYSRAM)
dev_err(ce->dev, "CE ERROR: keysram access error for AES\n"); if (v & CE_ERR_ADDR_INVALID)
dev_err(ce->dev, "CE ERROR: address invalid\n"); if (v & CE_ERR_KEYLADDER)
dev_err(ce->dev, "CE ERROR: key ladder configuration error\n"); break;
}
for (i = 0; i < MAXFLOW; i++)
seq_printf(seq, "Channel %d: nreq %lu\n", i, #ifdef CONFIG_CRYPTO_DEV_SUN8I_CE_DEBUG
ce->chanlist[i].stat_req); #else
0ul); #endif
for (i = 0; i < ARRAY_SIZE(ce_algs); i++) { if (!ce_algs[i].ce) continue; switch (ce_algs[i].type) { case CRYPTO_ALG_TYPE_SKCIPHER:
seq_printf(seq, "%s %s reqs=%lu fallback=%lu\n",
ce_algs[i].alg.skcipher.base.base.cra_driver_name,
ce_algs[i].alg.skcipher.base.base.cra_name,
ce_algs[i].stat_req, ce_algs[i].stat_fb);
seq_printf(seq, "\tLast fallback is: %s\n",
ce_algs[i].fbname);
seq_printf(seq, "\tFallback due to 0 length: %lu\n",
ce_algs[i].stat_fb_len0);
seq_printf(seq, "\tFallback due to length !mod16: %lu\n",
ce_algs[i].stat_fb_mod16);
seq_printf(seq, "\tFallback due to length < IV: %lu\n",
ce_algs[i].stat_fb_leniv);
seq_printf(seq, "\tFallback due to source alignment: %lu\n",
ce_algs[i].stat_fb_srcali);
seq_printf(seq, "\tFallback due to dest alignment: %lu\n",
ce_algs[i].stat_fb_dstali);
seq_printf(seq, "\tFallback due to source length: %lu\n",
ce_algs[i].stat_fb_srclen);
seq_printf(seq, "\tFallback due to dest length: %lu\n",
ce_algs[i].stat_fb_dstlen);
seq_printf(seq, "\tFallback due to SG numbers: %lu\n",
ce_algs[i].stat_fb_maxsg); break; case CRYPTO_ALG_TYPE_AHASH:
seq_printf(seq, "%s %s reqs=%lu fallback=%lu\n",
ce_algs[i].alg.hash.base.halg.base.cra_driver_name,
ce_algs[i].alg.hash.base.halg.base.cra_name,
ce_algs[i].stat_req, ce_algs[i].stat_fb);
seq_printf(seq, "\tLast fallback is: %s\n",
ce_algs[i].fbname);
seq_printf(seq, "\tFallback due to 0 length: %lu\n",
ce_algs[i].stat_fb_len0);
seq_printf(seq, "\tFallback due to length: %lu\n",
ce_algs[i].stat_fb_srclen);
seq_printf(seq, "\tFallback due to alignment: %lu\n",
ce_algs[i].stat_fb_srcali);
seq_printf(seq, "\tFallback due to SG numbers: %lu\n",
ce_algs[i].stat_fb_maxsg); break; case CRYPTO_ALG_TYPE_RNG:
seq_printf(seq, "%s %s reqs=%lu bytes=%lu\n",
ce_algs[i].alg.rng.base.cra_driver_name,
ce_algs[i].alg.rng.base.cra_name,
ce_algs[i].stat_req, ce_algs[i].stat_bytes); break;
}
} #ifdefined(CONFIG_CRYPTO_DEV_SUN8I_CE_TRNG) && \ defined(CONFIG_CRYPTO_DEV_SUN8I_CE_DEBUG)
seq_printf(seq, "HWRNG %lu %lu\n",
ce->hwrng_stat_req, ce->hwrng_stat_bytes); #endif return 0;
}
DEFINE_SHOW_ATTRIBUTE(sun8i_ce_debugfs);
staticvoid sun8i_ce_free_chanlist(struct sun8i_ce_dev *ce, int i)
{ while (i >= 0) {
crypto_engine_exit(ce->chanlist[i].engine); if (ce->chanlist[i].tl)
dma_free_coherent(ce->dev, sizeof(struct ce_task),
ce->chanlist[i].tl,
ce->chanlist[i].t_phy);
i--;
}
}
/* * Allocate the channel list structure
*/ staticint sun8i_ce_allocate_chanlist(struct sun8i_ce_dev *ce)
{ int i, err;
/* * Power management strategy: The device is suspended unless a TFM exists for * one of the algorithms proposed by this driver.
*/ staticint sun8i_ce_pm_suspend(struct device *dev)
{ struct sun8i_ce_dev *ce = dev_get_drvdata(dev); int i;
reset_control_assert(ce->reset); for (i = 0; i < CE_MAX_CLOCKS; i++)
clk_disable_unprepare(ce->ceclks[i]); return 0;
}
for (i = 0; i < ARRAY_SIZE(ce_algs); i++) { if (!ce_algs[i].ce) continue; switch (ce_algs[i].type) { case CRYPTO_ALG_TYPE_SKCIPHER:
dev_info(ce->dev, "Unregister %d %s\n", i,
ce_algs[i].alg.skcipher.base.base.cra_name);
crypto_engine_unregister_skcipher(&ce_algs[i].alg.skcipher); break; case CRYPTO_ALG_TYPE_AHASH:
dev_info(ce->dev, "Unregister %d %s\n", i,
ce_algs[i].alg.hash.base.halg.base.cra_name);
crypto_engine_unregister_ahash(&ce_algs[i].alg.hash); break; case CRYPTO_ALG_TYPE_RNG:
dev_info(ce->dev, "Unregister %d %s\n", i,
ce_algs[i].alg.rng.base.cra_name);
crypto_unregister_rng(&ce_algs[i].alg.rng); break;
}
}
}
ce->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(ce->base)) return PTR_ERR(ce->base);
err = sun8i_ce_get_clks(ce); if (err) return err;
/* Get Non Secure IRQ */
irq = platform_get_irq(pdev, 0); if (irq < 0) return irq;
ce->reset = devm_reset_control_get(&pdev->dev, NULL); if (IS_ERR(ce->reset)) return dev_err_probe(&pdev->dev, PTR_ERR(ce->reset), "No reset control found\n");
mutex_init(&ce->mlock);
mutex_init(&ce->rnglock);
err = sun8i_ce_allocate_chanlist(ce); if (err) return err;
err = sun8i_ce_pm_init(ce); if (err) goto error_pm;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.