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

iproc-rng200.c (6090B)


      1/*
      2* Copyright (C) 2015 Broadcom Corporation
      3*
      4* This program is free software; you can redistribute it and/or
      5* modify it under the terms of the GNU General Public License as
      6* published by the Free Software Foundation version 2.
      7*
      8* This program is distributed "as is" WITHOUT ANY WARRANTY of any
      9* kind, whether express or implied; without even the implied warranty
     10* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     11* GNU General Public License for more details.
     12*/
     13/*
     14 * DESCRIPTION: The Broadcom iProc RNG200 Driver
     15 */
     16
     17#include <linux/hw_random.h>
     18#include <linux/init.h>
     19#include <linux/io.h>
     20#include <linux/kernel.h>
     21#include <linux/module.h>
     22#include <linux/of_address.h>
     23#include <linux/of_platform.h>
     24#include <linux/platform_device.h>
     25#include <linux/delay.h>
     26
     27/* Registers */
     28#define RNG_CTRL_OFFSET					0x00
     29#define RNG_CTRL_RNG_RBGEN_MASK				0x00001FFF
     30#define RNG_CTRL_RNG_RBGEN_ENABLE			0x00000001
     31
     32#define RNG_SOFT_RESET_OFFSET				0x04
     33#define RNG_SOFT_RESET					0x00000001
     34
     35#define RBG_SOFT_RESET_OFFSET				0x08
     36#define RBG_SOFT_RESET					0x00000001
     37
     38#define RNG_INT_STATUS_OFFSET				0x18
     39#define RNG_INT_STATUS_MASTER_FAIL_LOCKOUT_IRQ_MASK	0x80000000
     40#define RNG_INT_STATUS_STARTUP_TRANSITIONS_MET_IRQ_MASK	0x00020000
     41#define RNG_INT_STATUS_NIST_FAIL_IRQ_MASK		0x00000020
     42#define RNG_INT_STATUS_TOTAL_BITS_COUNT_IRQ_MASK	0x00000001
     43
     44#define RNG_FIFO_DATA_OFFSET				0x20
     45
     46#define RNG_FIFO_COUNT_OFFSET				0x24
     47#define RNG_FIFO_COUNT_RNG_FIFO_COUNT_MASK		0x000000FF
     48
     49struct iproc_rng200_dev {
     50	struct hwrng rng;
     51	void __iomem *base;
     52};
     53
     54#define to_rng_priv(rng)	container_of(rng, struct iproc_rng200_dev, rng)
     55
     56static void iproc_rng200_enable_set(void __iomem *rng_base, bool enable)
     57{
     58	u32 val;
     59
     60	val = ioread32(rng_base + RNG_CTRL_OFFSET);
     61	val &= ~RNG_CTRL_RNG_RBGEN_MASK;
     62
     63	if (enable)
     64		val |= RNG_CTRL_RNG_RBGEN_ENABLE;
     65
     66	iowrite32(val, rng_base + RNG_CTRL_OFFSET);
     67}
     68
     69static void iproc_rng200_restart(void __iomem *rng_base)
     70{
     71	uint32_t val;
     72
     73	iproc_rng200_enable_set(rng_base, false);
     74
     75	/* Clear all interrupt status */
     76	iowrite32(0xFFFFFFFFUL, rng_base + RNG_INT_STATUS_OFFSET);
     77
     78	/* Reset RNG and RBG */
     79	val = ioread32(rng_base + RBG_SOFT_RESET_OFFSET);
     80	val |= RBG_SOFT_RESET;
     81	iowrite32(val, rng_base + RBG_SOFT_RESET_OFFSET);
     82
     83	val = ioread32(rng_base + RNG_SOFT_RESET_OFFSET);
     84	val |= RNG_SOFT_RESET;
     85	iowrite32(val, rng_base + RNG_SOFT_RESET_OFFSET);
     86
     87	val = ioread32(rng_base + RNG_SOFT_RESET_OFFSET);
     88	val &= ~RNG_SOFT_RESET;
     89	iowrite32(val, rng_base + RNG_SOFT_RESET_OFFSET);
     90
     91	val = ioread32(rng_base + RBG_SOFT_RESET_OFFSET);
     92	val &= ~RBG_SOFT_RESET;
     93	iowrite32(val, rng_base + RBG_SOFT_RESET_OFFSET);
     94
     95	iproc_rng200_enable_set(rng_base, true);
     96}
     97
     98static int iproc_rng200_read(struct hwrng *rng, void *buf, size_t max,
     99			     bool wait)
    100{
    101	struct iproc_rng200_dev *priv = to_rng_priv(rng);
    102	uint32_t num_remaining = max;
    103	uint32_t status;
    104
    105	#define MAX_RESETS_PER_READ	1
    106	uint32_t num_resets = 0;
    107
    108	#define MAX_IDLE_TIME	(1 * HZ)
    109	unsigned long idle_endtime = jiffies + MAX_IDLE_TIME;
    110
    111	while ((num_remaining > 0) && time_before(jiffies, idle_endtime)) {
    112
    113		/* Is RNG sane? If not, reset it. */
    114		status = ioread32(priv->base + RNG_INT_STATUS_OFFSET);
    115		if ((status & (RNG_INT_STATUS_MASTER_FAIL_LOCKOUT_IRQ_MASK |
    116			RNG_INT_STATUS_NIST_FAIL_IRQ_MASK)) != 0) {
    117
    118			if (num_resets >= MAX_RESETS_PER_READ)
    119				return max - num_remaining;
    120
    121			iproc_rng200_restart(priv->base);
    122			num_resets++;
    123		}
    124
    125		/* Are there any random numbers available? */
    126		if ((ioread32(priv->base + RNG_FIFO_COUNT_OFFSET) &
    127				RNG_FIFO_COUNT_RNG_FIFO_COUNT_MASK) > 0) {
    128
    129			if (num_remaining >= sizeof(uint32_t)) {
    130				/* Buffer has room to store entire word */
    131				*(uint32_t *)buf = ioread32(priv->base +
    132							RNG_FIFO_DATA_OFFSET);
    133				buf += sizeof(uint32_t);
    134				num_remaining -= sizeof(uint32_t);
    135			} else {
    136				/* Buffer can only store partial word */
    137				uint32_t rnd_number = ioread32(priv->base +
    138							RNG_FIFO_DATA_OFFSET);
    139				memcpy(buf, &rnd_number, num_remaining);
    140				buf += num_remaining;
    141				num_remaining = 0;
    142			}
    143
    144			/* Reset the IDLE timeout */
    145			idle_endtime = jiffies + MAX_IDLE_TIME;
    146		} else {
    147			if (!wait)
    148				/* Cannot wait, return immediately */
    149				return max - num_remaining;
    150
    151			/* Can wait, give others chance to run */
    152			usleep_range(min(num_remaining * 10, 500U), 500);
    153		}
    154	}
    155
    156	return max - num_remaining;
    157}
    158
    159static int iproc_rng200_init(struct hwrng *rng)
    160{
    161	struct iproc_rng200_dev *priv = to_rng_priv(rng);
    162
    163	iproc_rng200_enable_set(priv->base, true);
    164
    165	return 0;
    166}
    167
    168static void iproc_rng200_cleanup(struct hwrng *rng)
    169{
    170	struct iproc_rng200_dev *priv = to_rng_priv(rng);
    171
    172	iproc_rng200_enable_set(priv->base, false);
    173}
    174
    175static int iproc_rng200_probe(struct platform_device *pdev)
    176{
    177	struct iproc_rng200_dev *priv;
    178	struct device *dev = &pdev->dev;
    179	int ret;
    180
    181	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
    182	if (!priv)
    183		return -ENOMEM;
    184
    185	/* Map peripheral */
    186	priv->base = devm_platform_ioremap_resource(pdev, 0);
    187	if (IS_ERR(priv->base)) {
    188		dev_err(dev, "failed to remap rng regs\n");
    189		return PTR_ERR(priv->base);
    190	}
    191
    192	priv->rng.name = "iproc-rng200";
    193	priv->rng.read = iproc_rng200_read;
    194	priv->rng.init = iproc_rng200_init;
    195	priv->rng.cleanup = iproc_rng200_cleanup;
    196
    197	/* Register driver */
    198	ret = devm_hwrng_register(dev, &priv->rng);
    199	if (ret) {
    200		dev_err(dev, "hwrng registration failed\n");
    201		return ret;
    202	}
    203
    204	dev_info(dev, "hwrng registered\n");
    205
    206	return 0;
    207}
    208
    209static const struct of_device_id iproc_rng200_of_match[] = {
    210	{ .compatible = "brcm,bcm2711-rng200", },
    211	{ .compatible = "brcm,bcm7211-rng200", },
    212	{ .compatible = "brcm,bcm7278-rng200", },
    213	{ .compatible = "brcm,iproc-rng200", },
    214	{},
    215};
    216MODULE_DEVICE_TABLE(of, iproc_rng200_of_match);
    217
    218static struct platform_driver iproc_rng200_driver = {
    219	.driver = {
    220		.name		= "iproc-rng200",
    221		.of_match_table = iproc_rng200_of_match,
    222	},
    223	.probe		= iproc_rng200_probe,
    224};
    225module_platform_driver(iproc_rng200_driver);
    226
    227MODULE_AUTHOR("Broadcom");
    228MODULE_DESCRIPTION("iProc RNG200 Random Number Generator driver");
    229MODULE_LICENSE("GPL v2");