cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

qcom-rng.c (5088B)


      1// SPDX-License-Identifier: GPL-2.0
      2// Copyright (c) 2017-18 Linaro Limited
      3//
      4// Based on msm-rng.c and downstream driver
      5
      6#include <crypto/internal/rng.h>
      7#include <linux/acpi.h>
      8#include <linux/clk.h>
      9#include <linux/crypto.h>
     10#include <linux/io.h>
     11#include <linux/iopoll.h>
     12#include <linux/module.h>
     13#include <linux/of.h>
     14#include <linux/platform_device.h>
     15
     16/* Device specific register offsets */
     17#define PRNG_DATA_OUT		0x0000
     18#define PRNG_STATUS		0x0004
     19#define PRNG_LFSR_CFG		0x0100
     20#define PRNG_CONFIG		0x0104
     21
     22/* Device specific register masks and config values */
     23#define PRNG_LFSR_CFG_MASK	0x0000ffff
     24#define PRNG_LFSR_CFG_CLOCKS	0x0000dddd
     25#define PRNG_CONFIG_HW_ENABLE	BIT(1)
     26#define PRNG_STATUS_DATA_AVAIL	BIT(0)
     27
     28#define WORD_SZ			4
     29
     30struct qcom_rng {
     31	struct mutex lock;
     32	void __iomem *base;
     33	struct clk *clk;
     34	unsigned int skip_init;
     35};
     36
     37struct qcom_rng_ctx {
     38	struct qcom_rng *rng;
     39};
     40
     41static struct qcom_rng *qcom_rng_dev;
     42
     43static int qcom_rng_read(struct qcom_rng *rng, u8 *data, unsigned int max)
     44{
     45	unsigned int currsize = 0;
     46	u32 val;
     47	int ret;
     48
     49	/* read random data from hardware */
     50	do {
     51		ret = readl_poll_timeout(rng->base + PRNG_STATUS, val,
     52					 val & PRNG_STATUS_DATA_AVAIL,
     53					 200, 10000);
     54		if (ret)
     55			return ret;
     56
     57		val = readl_relaxed(rng->base + PRNG_DATA_OUT);
     58		if (!val)
     59			return -EINVAL;
     60
     61		if ((max - currsize) >= WORD_SZ) {
     62			memcpy(data, &val, WORD_SZ);
     63			data += WORD_SZ;
     64			currsize += WORD_SZ;
     65		} else {
     66			/* copy only remaining bytes */
     67			memcpy(data, &val, max - currsize);
     68			break;
     69		}
     70	} while (currsize < max);
     71
     72	return 0;
     73}
     74
     75static int qcom_rng_generate(struct crypto_rng *tfm,
     76			     const u8 *src, unsigned int slen,
     77			     u8 *dstn, unsigned int dlen)
     78{
     79	struct qcom_rng_ctx *ctx = crypto_rng_ctx(tfm);
     80	struct qcom_rng *rng = ctx->rng;
     81	int ret;
     82
     83	ret = clk_prepare_enable(rng->clk);
     84	if (ret)
     85		return ret;
     86
     87	mutex_lock(&rng->lock);
     88
     89	ret = qcom_rng_read(rng, dstn, dlen);
     90
     91	mutex_unlock(&rng->lock);
     92	clk_disable_unprepare(rng->clk);
     93
     94	return ret;
     95}
     96
     97static int qcom_rng_seed(struct crypto_rng *tfm, const u8 *seed,
     98			 unsigned int slen)
     99{
    100	return 0;
    101}
    102
    103static int qcom_rng_enable(struct qcom_rng *rng)
    104{
    105	u32 val;
    106	int ret;
    107
    108	ret = clk_prepare_enable(rng->clk);
    109	if (ret)
    110		return ret;
    111
    112	/* Enable PRNG only if it is not already enabled */
    113	val = readl_relaxed(rng->base + PRNG_CONFIG);
    114	if (val & PRNG_CONFIG_HW_ENABLE)
    115		goto already_enabled;
    116
    117	val = readl_relaxed(rng->base + PRNG_LFSR_CFG);
    118	val &= ~PRNG_LFSR_CFG_MASK;
    119	val |= PRNG_LFSR_CFG_CLOCKS;
    120	writel(val, rng->base + PRNG_LFSR_CFG);
    121
    122	val = readl_relaxed(rng->base + PRNG_CONFIG);
    123	val |= PRNG_CONFIG_HW_ENABLE;
    124	writel(val, rng->base + PRNG_CONFIG);
    125
    126already_enabled:
    127	clk_disable_unprepare(rng->clk);
    128
    129	return 0;
    130}
    131
    132static int qcom_rng_init(struct crypto_tfm *tfm)
    133{
    134	struct qcom_rng_ctx *ctx = crypto_tfm_ctx(tfm);
    135
    136	ctx->rng = qcom_rng_dev;
    137
    138	if (!ctx->rng->skip_init)
    139		return qcom_rng_enable(ctx->rng);
    140
    141	return 0;
    142}
    143
    144static struct rng_alg qcom_rng_alg = {
    145	.generate	= qcom_rng_generate,
    146	.seed		= qcom_rng_seed,
    147	.seedsize	= 0,
    148	.base		= {
    149		.cra_name		= "stdrng",
    150		.cra_driver_name	= "qcom-rng",
    151		.cra_flags		= CRYPTO_ALG_TYPE_RNG,
    152		.cra_priority		= 300,
    153		.cra_ctxsize		= sizeof(struct qcom_rng_ctx),
    154		.cra_module		= THIS_MODULE,
    155		.cra_init		= qcom_rng_init,
    156	}
    157};
    158
    159static int qcom_rng_probe(struct platform_device *pdev)
    160{
    161	struct qcom_rng *rng;
    162	int ret;
    163
    164	rng = devm_kzalloc(&pdev->dev, sizeof(*rng), GFP_KERNEL);
    165	if (!rng)
    166		return -ENOMEM;
    167
    168	platform_set_drvdata(pdev, rng);
    169	mutex_init(&rng->lock);
    170
    171	rng->base = devm_platform_ioremap_resource(pdev, 0);
    172	if (IS_ERR(rng->base))
    173		return PTR_ERR(rng->base);
    174
    175	/* ACPI systems have clk already on, so skip clk_get */
    176	if (!has_acpi_companion(&pdev->dev)) {
    177		rng->clk = devm_clk_get(&pdev->dev, "core");
    178		if (IS_ERR(rng->clk))
    179			return PTR_ERR(rng->clk);
    180	}
    181
    182
    183	rng->skip_init = (unsigned long)device_get_match_data(&pdev->dev);
    184
    185	qcom_rng_dev = rng;
    186	ret = crypto_register_rng(&qcom_rng_alg);
    187	if (ret) {
    188		dev_err(&pdev->dev, "Register crypto rng failed: %d\n", ret);
    189		qcom_rng_dev = NULL;
    190	}
    191
    192	return ret;
    193}
    194
    195static int qcom_rng_remove(struct platform_device *pdev)
    196{
    197	crypto_unregister_rng(&qcom_rng_alg);
    198
    199	qcom_rng_dev = NULL;
    200
    201	return 0;
    202}
    203
    204#if IS_ENABLED(CONFIG_ACPI)
    205static const struct acpi_device_id qcom_rng_acpi_match[] = {
    206	{ .id = "QCOM8160", .driver_data = 1 },
    207	{}
    208};
    209MODULE_DEVICE_TABLE(acpi, qcom_rng_acpi_match);
    210#endif
    211
    212static const struct of_device_id qcom_rng_of_match[] = {
    213	{ .compatible = "qcom,prng", .data = (void *)0},
    214	{ .compatible = "qcom,prng-ee", .data = (void *)1},
    215	{}
    216};
    217MODULE_DEVICE_TABLE(of, qcom_rng_of_match);
    218
    219static struct platform_driver qcom_rng_driver = {
    220	.probe = qcom_rng_probe,
    221	.remove =  qcom_rng_remove,
    222	.driver = {
    223		.name = KBUILD_MODNAME,
    224		.of_match_table = of_match_ptr(qcom_rng_of_match),
    225		.acpi_match_table = ACPI_PTR(qcom_rng_acpi_match),
    226	}
    227};
    228module_platform_driver(qcom_rng_driver);
    229
    230MODULE_ALIAS("platform:" KBUILD_MODNAME);
    231MODULE_DESCRIPTION("Qualcomm random number generator driver");
    232MODULE_LICENSE("GPL v2");