diff options
Diffstat (limited to 'drivers/char/hw_random')
| -rw-r--r-- | drivers/char/hw_random/Kconfig | 135 | ||||
| -rw-r--r-- | drivers/char/hw_random/Makefile | 4 | ||||
| -rw-r--r-- | drivers/char/hw_random/atmel-rng.c | 23 | ||||
| -rw-r--r-- | drivers/char/hw_random/bcm2835-rng.c | 11 | ||||
| -rw-r--r-- | drivers/char/hw_random/core.c | 55 | ||||
| -rw-r--r-- | drivers/char/hw_random/exynos-rng.c | 1 | ||||
| -rw-r--r-- | drivers/char/hw_random/msm-rng.c | 197 | ||||
| -rw-r--r-- | drivers/char/hw_random/n2-drv.c | 25 | ||||
| -rw-r--r-- | drivers/char/hw_random/nomadik-rng.c | 14 | ||||
| -rw-r--r-- | drivers/char/hw_random/octeon-rng.c | 1 | ||||
| -rw-r--r-- | drivers/char/hw_random/omap-rng.c | 4 | ||||
| -rw-r--r-- | drivers/char/hw_random/omap3-rom-rng.c | 140 | ||||
| -rw-r--r-- | drivers/char/hw_random/pasemi-rng.c | 1 | ||||
| -rw-r--r-- | drivers/char/hw_random/picoxcell-rng.c | 198 | ||||
| -rw-r--r-- | drivers/char/hw_random/powernv-rng.c | 81 | ||||
| -rw-r--r-- | drivers/char/hw_random/ppc4xx-rng.c | 1 | ||||
| -rw-r--r-- | drivers/char/hw_random/pseries-rng.c | 19 | ||||
| -rw-r--r-- | drivers/char/hw_random/timeriomem-rng.c | 46 | ||||
| -rw-r--r-- | drivers/char/hw_random/via-rng.c | 2 | ||||
| -rw-r--r-- | drivers/char/hw_random/virtio-rng.c | 120 |
20 files changed, 680 insertions, 398 deletions
diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig index 0aa9d91daef..836b061ced3 100644 --- a/drivers/char/hw_random/Kconfig +++ b/drivers/char/hw_random/Kconfig @@ -2,7 +2,7 @@ # Hardware Random Number Generator (RNG) configuration # -config HW_RANDOM +menuconfig HW_RANDOM tristate "Hardware Random Number Generator Core support" default m ---help--- @@ -20,9 +20,11 @@ config HW_RANDOM If unsure, say Y. +if HW_RANDOM + config HW_RANDOM_TIMERIOMEM tristate "Timer IOMEM HW Random Number Generator support" - depends on HW_RANDOM && HAS_IOMEM + depends on HAS_IOMEM ---help--- This driver provides kernel-side support for a generic Random Number Generator used by reading a 'dumb' iomem address that @@ -36,7 +38,7 @@ config HW_RANDOM_TIMERIOMEM config HW_RANDOM_INTEL tristate "Intel HW Random Number Generator support" - depends on HW_RANDOM && (X86 || IA64) && PCI + depends on (X86 || IA64) && PCI default HW_RANDOM ---help--- This driver provides kernel-side support for the Random Number @@ -49,7 +51,7 @@ config HW_RANDOM_INTEL config HW_RANDOM_AMD tristate "AMD HW Random Number Generator support" - depends on HW_RANDOM && (X86 || PPC_MAPLE) && PCI + depends on (X86 || PPC_MAPLE) && PCI default HW_RANDOM ---help--- This driver provides kernel-side support for the Random Number @@ -62,8 +64,8 @@ config HW_RANDOM_AMD config HW_RANDOM_ATMEL tristate "Atmel Random Number Generator support" - depends on HW_RANDOM && HAVE_CLK - default (HW_RANDOM && ARCH_AT91) + depends on ARCH_AT91 && HAVE_CLK + default HW_RANDOM ---help--- This driver provides kernel-side support for the Random Number Generator hardware found on Atmel AT91 devices. @@ -75,7 +77,7 @@ config HW_RANDOM_ATMEL config HW_RANDOM_BCM63XX tristate "Broadcom BCM63xx Random Number Generator support" - depends on HW_RANDOM && BCM63XX + depends on BCM63XX default HW_RANDOM ---help--- This driver provides kernel-side support for the Random Number @@ -88,7 +90,7 @@ config HW_RANDOM_BCM63XX config HW_RANDOM_BCM2835 tristate "Broadcom BCM2835 Random Number Generator support" - depends on HW_RANDOM && ARCH_BCM2835 + depends on ARCH_BCM2835 default HW_RANDOM ---help--- This driver provides kernel-side support for the Random Number @@ -101,7 +103,7 @@ config HW_RANDOM_BCM2835 config HW_RANDOM_GEODE tristate "AMD Geode HW Random Number Generator support" - depends on HW_RANDOM && X86_32 && PCI + depends on X86_32 && PCI default HW_RANDOM ---help--- This driver provides kernel-side support for the Random Number @@ -114,7 +116,7 @@ config HW_RANDOM_GEODE config HW_RANDOM_N2RNG tristate "Niagara2 Random Number Generator support" - depends on HW_RANDOM && SPARC64 + depends on SPARC64 default HW_RANDOM ---help--- This driver provides kernel-side support for the Random Number @@ -127,7 +129,7 @@ config HW_RANDOM_N2RNG config HW_RANDOM_VIA tristate "VIA HW Random Number Generator support" - depends on HW_RANDOM && X86 + depends on X86 default HW_RANDOM ---help--- This driver provides kernel-side support for the Random Number @@ -140,7 +142,7 @@ config HW_RANDOM_VIA config HW_RANDOM_IXP4XX tristate "Intel IXP4xx NPU HW Pseudo-Random Number Generator support" - depends on HW_RANDOM && ARCH_IXP4XX + depends on ARCH_IXP4XX default HW_RANDOM ---help--- This driver provides kernel-side support for the Pseudo-Random @@ -153,7 +155,7 @@ config HW_RANDOM_IXP4XX config HW_RANDOM_OMAP tristate "OMAP Random Number Generator support" - depends on HW_RANDOM && (ARCH_OMAP16XX || ARCH_OMAP2PLUS) + depends on ARCH_OMAP16XX || ARCH_OMAP2PLUS default HW_RANDOM ---help--- This driver provides kernel-side support for the Random Number @@ -165,9 +167,22 @@ config HW_RANDOM_OMAP If unsure, say Y. +config HW_RANDOM_OMAP3_ROM + tristate "OMAP3 ROM Random Number Generator support" + depends on ARCH_OMAP3 + default HW_RANDOM + ---help--- + This driver provides kernel-side support for the Random Number + Generator hardware found on OMAP34xx processors. + + To compile this driver as a module, choose M here: the + module will be called omap3-rom-rng. + + If unsure, say Y. + config HW_RANDOM_OCTEON tristate "Octeon Random Number Generator support" - depends on HW_RANDOM && CAVIUM_OCTEON_SOC + depends on CAVIUM_OCTEON_SOC default HW_RANDOM ---help--- This driver provides kernel-side support for the Random Number @@ -180,7 +195,7 @@ config HW_RANDOM_OCTEON config HW_RANDOM_PASEMI tristate "PA Semi HW Random Number Generator support" - depends on HW_RANDOM && PPC_PASEMI + depends on PPC_PASEMI default HW_RANDOM ---help--- This driver provides kernel-side support for the Random Number @@ -193,7 +208,7 @@ config HW_RANDOM_PASEMI config HW_RANDOM_VIRTIO tristate "VirtIO Random Number Generator support" - depends on HW_RANDOM && VIRTIO + depends on VIRTIO ---help--- This driver provides kernel-side support for the virtual Random Number Generator hardware. @@ -203,7 +218,7 @@ config HW_RANDOM_VIRTIO config HW_RANDOM_TX4939 tristate "TX4939 Random Number Generator support" - depends on HW_RANDOM && SOC_TX4939 + depends on SOC_TX4939 default HW_RANDOM ---help--- This driver provides kernel-side support for the Random Number @@ -216,7 +231,8 @@ config HW_RANDOM_TX4939 config HW_RANDOM_MXC_RNGA tristate "Freescale i.MX RNGA Random Number Generator" - depends on HW_RANDOM && ARCH_HAS_RNGA + depends on ARCH_HAS_RNGA + default HW_RANDOM ---help--- This driver provides kernel-side support for the Random Number Generator hardware found on Freescale i.MX processors. @@ -228,7 +244,8 @@ config HW_RANDOM_MXC_RNGA config HW_RANDOM_NOMADIK tristate "ST-Ericsson Nomadik Random Number Generator support" - depends on HW_RANDOM && ARCH_NOMADIK + depends on ARCH_NOMADIK + default HW_RANDOM ---help--- This driver provides kernel-side support for the Random Number Generator hardware found on ST-Ericsson SoCs (8815 and 8500). @@ -238,21 +255,10 @@ config HW_RANDOM_NOMADIK If unsure, say Y. -config HW_RANDOM_PICOXCELL - tristate "Picochip picoXcell true random number generator support" - depends on HW_RANDOM && ARCH_PICOXCELL && PICOXCELL_PC3X3 - ---help--- - This driver provides kernel-side support for the Random Number - Generator hardware found on Picochip PC3x3 and later devices. - - To compile this driver as a module, choose M here: the - module will be called picoxcell-rng. - - If unsure, say Y. - config HW_RANDOM_PPC4XX tristate "PowerPC 4xx generic true random number generator support" - depends on HW_RANDOM && PPC && 4xx + depends on PPC && 4xx + default HW_RANDOM ---help--- This driver provides the kernel-side support for the TRNG hardware found in the security function of some PowerPC 4xx SoCs. @@ -262,24 +268,9 @@ config HW_RANDOM_PPC4XX If unsure, say N. -config UML_RANDOM - depends on UML - tristate "Hardware random number generator" - help - This option enables UML's "hardware" random number generator. It - attaches itself to the host's /dev/random, supplying as much entropy - as the host has, rather than the small amount the UML gets from its - own drivers. It registers itself as a standard hardware random number - generator, major 10, minor 183, and the canonical device name is - /dev/hwrng. - The way to make use of this is to install the rng-tools package - (check your distro, or download from - http://sourceforge.net/projects/gkernel/). rngd periodically reads - /dev/hwrng and injects the entropy into /dev/random. - config HW_RANDOM_PSERIES tristate "pSeries HW Random Number Generator support" - depends on HW_RANDOM && PPC64 && IBMVIO + depends on PPC64 && IBMVIO default HW_RANDOM ---help--- This driver provides kernel-side support for the Random Number @@ -290,9 +281,23 @@ config HW_RANDOM_PSERIES If unsure, say Y. +config HW_RANDOM_POWERNV + tristate "PowerNV Random Number Generator support" + depends on PPC_POWERNV + default HW_RANDOM + ---help--- + This is the driver for Random Number Generator hardware found + in POWER7+ and above machines for PowerNV platform. + + To compile this driver as a module, choose M here: the + module will be called powernv-rng. + + If unsure, say Y. + config HW_RANDOM_EXYNOS tristate "EXYNOS HW random number generator support" - depends on HW_RANDOM && HAS_IOMEM && HAVE_CLK + depends on ARCH_EXYNOS + default HW_RANDOM ---help--- This driver provides kernel-side support for the Random Number Generator hardware found on EXYNOS SOCs. @@ -304,7 +309,7 @@ config HW_RANDOM_EXYNOS config HW_RANDOM_TPM tristate "TPM HW Random Number Generator support" - depends on HW_RANDOM && TCG_TPM + depends on TCG_TPM default HW_RANDOM ---help--- This driver provides kernel-side support for the Random Number @@ -314,3 +319,33 @@ config HW_RANDOM_TPM module will be called tpm-rng. If unsure, say Y. + +config HW_RANDOM_MSM + tristate "Qualcomm SoCs Random Number Generator support" + depends on HW_RANDOM && ARCH_QCOM + default HW_RANDOM + ---help--- + This driver provides kernel-side support for the Random Number + Generator hardware found on Qualcomm SoCs. + + To compile this driver as a module, choose M here. the + module will be called msm-rng. + + If unsure, say Y. + +endif # HW_RANDOM + +config UML_RANDOM + depends on UML + tristate "Hardware random number generator" + help + This option enables UML's "hardware" random number generator. It + attaches itself to the host's /dev/random, supplying as much entropy + as the host has, rather than the small amount the UML gets from its + own drivers. It registers itself as a standard hardware random number + generator, major 10, minor 183, and the canonical device name is + /dev/hwrng. + The way to make use of this is to install the rng-tools package + (check your distro, or download from + http://sourceforge.net/projects/gkernel/). rngd periodically reads + /dev/hwrng and injects the entropy into /dev/random. diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile index bed467c9300..199ed283e14 100644 --- a/drivers/char/hw_random/Makefile +++ b/drivers/char/hw_random/Makefile @@ -15,15 +15,17 @@ n2-rng-y := n2-drv.o n2-asm.o obj-$(CONFIG_HW_RANDOM_VIA) += via-rng.o obj-$(CONFIG_HW_RANDOM_IXP4XX) += ixp4xx-rng.o obj-$(CONFIG_HW_RANDOM_OMAP) += omap-rng.o +obj-$(CONFIG_HW_RANDOM_OMAP3_ROM) += omap3-rom-rng.o obj-$(CONFIG_HW_RANDOM_PASEMI) += pasemi-rng.o obj-$(CONFIG_HW_RANDOM_VIRTIO) += virtio-rng.o obj-$(CONFIG_HW_RANDOM_TX4939) += tx4939-rng.o obj-$(CONFIG_HW_RANDOM_MXC_RNGA) += mxc-rnga.o obj-$(CONFIG_HW_RANDOM_OCTEON) += octeon-rng.o obj-$(CONFIG_HW_RANDOM_NOMADIK) += nomadik-rng.o -obj-$(CONFIG_HW_RANDOM_PICOXCELL) += picoxcell-rng.o obj-$(CONFIG_HW_RANDOM_PPC4XX) += ppc4xx-rng.o obj-$(CONFIG_HW_RANDOM_PSERIES) += pseries-rng.o +obj-$(CONFIG_HW_RANDOM_POWERNV) += powernv-rng.o obj-$(CONFIG_HW_RANDOM_EXYNOS) += exynos-rng.o obj-$(CONFIG_HW_RANDOM_TPM) += tpm-rng.o obj-$(CONFIG_HW_RANDOM_BCM2835) += bcm2835-rng.o +obj-$(CONFIG_HW_RANDOM_MSM) += msm-rng.o diff --git a/drivers/char/hw_random/atmel-rng.c b/drivers/char/hw_random/atmel-rng.c index bf9fc6b7932..851bc7e20ad 100644 --- a/drivers/char/hw_random/atmel-rng.c +++ b/drivers/char/hw_random/atmel-rng.c @@ -54,29 +54,22 @@ static int atmel_trng_probe(struct platform_device *pdev) struct resource *res; int ret; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -EINVAL; - trng = devm_kzalloc(&pdev->dev, sizeof(*trng), GFP_KERNEL); if (!trng) return -ENOMEM; - if (!devm_request_mem_region(&pdev->dev, res->start, - resource_size(res), pdev->name)) - return -EBUSY; - - trng->base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); - if (!trng->base) - return -EBUSY; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + trng->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(trng->base)) + return PTR_ERR(trng->base); - trng->clk = clk_get(&pdev->dev, NULL); + trng->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(trng->clk)) return PTR_ERR(trng->clk); ret = clk_enable(trng->clk); if (ret) - goto err_enable; + return ret; writel(TRNG_KEY | 1, trng->base + TRNG_CR); trng->rng.name = pdev->name; @@ -92,9 +85,6 @@ static int atmel_trng_probe(struct platform_device *pdev) err_register: clk_disable(trng->clk); -err_enable: - clk_put(trng->clk); - return ret; } @@ -106,7 +96,6 @@ static int atmel_trng_remove(struct platform_device *pdev) writel(TRNG_KEY, trng->base + TRNG_CR); clk_disable(trng->clk); - clk_put(trng->clk); return 0; } diff --git a/drivers/char/hw_random/bcm2835-rng.c b/drivers/char/hw_random/bcm2835-rng.c index 43577ca780e..e900961cdd2 100644 --- a/drivers/char/hw_random/bcm2835-rng.c +++ b/drivers/char/hw_random/bcm2835-rng.c @@ -8,7 +8,6 @@ */ #include <linux/hw_random.h> -#include <linux/init.h> #include <linux/io.h> #include <linux/kernel.h> #include <linux/module.h> @@ -62,18 +61,18 @@ static int bcm2835_rng_probe(struct platform_device *pdev) } bcm2835_rng_ops.priv = (unsigned long)rng_base; + /* set warm-up count & enable */ + __raw_writel(RNG_WARMUP_COUNT, rng_base + RNG_STATUS); + __raw_writel(RNG_RBGEN, rng_base + RNG_CTRL); + /* register driver */ err = hwrng_register(&bcm2835_rng_ops); if (err) { dev_err(dev, "hwrng registration failed\n"); iounmap(rng_base); - } else { + } else dev_info(dev, "hwrng registered\n"); - /* set warm-up count & enable */ - __raw_writel(RNG_WARMUP_COUNT, rng_base + RNG_STATUS); - __raw_writel(RNG_RBGEN, rng_base + RNG_CTRL); - } return err; } diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c index a0f7724852e..c4419ea1ab0 100644 --- a/drivers/char/hw_random/core.c +++ b/drivers/char/hw_random/core.c @@ -37,10 +37,10 @@ #include <linux/kernel.h> #include <linux/fs.h> #include <linux/sched.h> -#include <linux/init.h> #include <linux/miscdevice.h> #include <linux/delay.h> #include <linux/slab.h> +#include <linux/random.h> #include <asm/uaccess.h> @@ -55,16 +55,41 @@ static DEFINE_MUTEX(rng_mutex); static int data_avail; static u8 *rng_buffer; +static inline int rng_get_data(struct hwrng *rng, u8 *buffer, size_t size, + int wait); + static size_t rng_buffer_size(void) { return SMP_CACHE_BYTES < 32 ? 32 : SMP_CACHE_BYTES; } +static void add_early_randomness(struct hwrng *rng) +{ + unsigned char bytes[16]; + int bytes_read; + + /* + * Currently only virtio-rng cannot return data during device + * probe, and that's handled in virtio-rng.c itself. If there + * are more such devices, this call to rng_get_data can be + * made conditional here instead of doing it per-device. + */ + bytes_read = rng_get_data(rng, bytes, sizeof(bytes), 1); + if (bytes_read > 0) + add_device_randomness(bytes, bytes_read); +} + static inline int hwrng_init(struct hwrng *rng) { - if (!rng->init) - return 0; - return rng->init(rng); + if (rng->init) { + int ret; + + ret = rng->init(rng); + if (ret) + return ret; + } + add_early_randomness(rng); + return 0; } static inline void hwrng_cleanup(struct hwrng *rng) @@ -302,7 +327,6 @@ err_misc_dereg: int hwrng_register(struct hwrng *rng) { - int must_register_misc; int err = -EINVAL; struct hwrng *old_rng, *tmp; @@ -327,7 +351,6 @@ int hwrng_register(struct hwrng *rng) goto out_unlock; } - must_register_misc = (current_rng == NULL); old_rng = current_rng; if (!old_rng) { err = hwrng_init(rng); @@ -336,18 +359,28 @@ int hwrng_register(struct hwrng *rng) current_rng = rng; } err = 0; - if (must_register_misc) { + if (!old_rng) { err = register_miscdev(); if (err) { - if (!old_rng) { - hwrng_cleanup(rng); - current_rng = NULL; - } + hwrng_cleanup(rng); + current_rng = NULL; goto out_unlock; } } INIT_LIST_HEAD(&rng->list); list_add_tail(&rng->list, &rng_list); + + if (old_rng && !rng->init) { + /* + * Use a new device's input to add some randomness to + * the system. If this rng device isn't going to be + * used right away, its init function hasn't been + * called yet; so only use the randomness from devices + * that don't need an init callback. + */ + add_early_randomness(rng); + } + out_unlock: mutex_unlock(&rng_mutex); out: diff --git a/drivers/char/hw_random/exynos-rng.c b/drivers/char/hw_random/exynos-rng.c index 402ccfb625c..9f8277cc44b 100644 --- a/drivers/char/hw_random/exynos-rng.c +++ b/drivers/char/hw_random/exynos-rng.c @@ -22,7 +22,6 @@ #include <linux/hw_random.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/init.h> #include <linux/io.h> #include <linux/platform_device.h> #include <linux/clk.h> diff --git a/drivers/char/hw_random/msm-rng.c b/drivers/char/hw_random/msm-rng.c new file mode 100644 index 00000000000..148521e51dc --- /dev/null +++ b/drivers/char/hw_random/msm-rng.c @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/hw_random.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> + +/* Device specific register offsets */ +#define PRNG_DATA_OUT 0x0000 +#define PRNG_STATUS 0x0004 +#define PRNG_LFSR_CFG 0x0100 +#define PRNG_CONFIG 0x0104 + +/* Device specific register masks and config values */ +#define PRNG_LFSR_CFG_MASK 0x0000ffff +#define PRNG_LFSR_CFG_CLOCKS 0x0000dddd +#define PRNG_CONFIG_HW_ENABLE BIT(1) +#define PRNG_STATUS_DATA_AVAIL BIT(0) + +#define MAX_HW_FIFO_DEPTH 16 +#define MAX_HW_FIFO_SIZE (MAX_HW_FIFO_DEPTH * 4) +#define WORD_SZ 4 + +struct msm_rng { + void __iomem *base; + struct clk *clk; + struct hwrng hwrng; +}; + +#define to_msm_rng(p) container_of(p, struct msm_rng, hwrng) + +static int msm_rng_enable(struct hwrng *hwrng, int enable) +{ + struct msm_rng *rng = to_msm_rng(hwrng); + u32 val; + int ret; + + ret = clk_prepare_enable(rng->clk); + if (ret) + return ret; + + if (enable) { + /* Enable PRNG only if it is not already enabled */ + val = readl_relaxed(rng->base + PRNG_CONFIG); + if (val & PRNG_CONFIG_HW_ENABLE) + goto already_enabled; + + val = readl_relaxed(rng->base + PRNG_LFSR_CFG); + val &= ~PRNG_LFSR_CFG_MASK; + val |= PRNG_LFSR_CFG_CLOCKS; + writel(val, rng->base + PRNG_LFSR_CFG); + + val = readl_relaxed(rng->base + PRNG_CONFIG); + val |= PRNG_CONFIG_HW_ENABLE; + writel(val, rng->base + PRNG_CONFIG); + } else { + val = readl_relaxed(rng->base + PRNG_CONFIG); + val &= ~PRNG_CONFIG_HW_ENABLE; + writel(val, rng->base + PRNG_CONFIG); + } + +already_enabled: + clk_disable_unprepare(rng->clk); + return 0; +} + +static int msm_rng_read(struct hwrng *hwrng, void *data, size_t max, bool wait) +{ + struct msm_rng *rng = to_msm_rng(hwrng); + size_t currsize = 0; + u32 *retdata = data; + size_t maxsize; + int ret; + u32 val; + + /* calculate max size bytes to transfer back to caller */ + maxsize = min_t(size_t, MAX_HW_FIFO_SIZE, max); + + /* no room for word data */ + if (maxsize < WORD_SZ) + return 0; + + ret = clk_prepare_enable(rng->clk); + if (ret) + return ret; + + /* read random data from hardware */ + do { + val = readl_relaxed(rng->base + PRNG_STATUS); + if (!(val & PRNG_STATUS_DATA_AVAIL)) + break; + + val = readl_relaxed(rng->base + PRNG_DATA_OUT); + if (!val) + break; + + *retdata++ = val; + currsize += WORD_SZ; + + /* make sure we stay on 32bit boundary */ + if ((maxsize - currsize) < WORD_SZ) + break; + } while (currsize < maxsize); + + clk_disable_unprepare(rng->clk); + + return currsize; +} + +static int msm_rng_init(struct hwrng *hwrng) +{ + return msm_rng_enable(hwrng, 1); +} + +static void msm_rng_cleanup(struct hwrng *hwrng) +{ + msm_rng_enable(hwrng, 0); +} + +static int msm_rng_probe(struct platform_device *pdev) +{ + struct resource *res; + struct msm_rng *rng; + int ret; + + rng = devm_kzalloc(&pdev->dev, sizeof(*rng), GFP_KERNEL); + if (!rng) + return -ENOMEM; + + platform_set_drvdata(pdev, rng); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + rng->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(rng->base)) + return PTR_ERR(rng->base); + + rng->clk = devm_clk_get(&pdev->dev, "core"); + if (IS_ERR(rng->clk)) + return PTR_ERR(rng->clk); + + rng->hwrng.name = KBUILD_MODNAME, + rng->hwrng.init = msm_rng_init, + rng->hwrng.cleanup = msm_rng_cleanup, + rng->hwrng.read = msm_rng_read, + + ret = hwrng_register(&rng->hwrng); + if (ret) { + dev_err(&pdev->dev, "failed to register hwrng\n"); + return ret; + } + + return 0; +} + +static int msm_rng_remove(struct platform_device *pdev) +{ + struct msm_rng *rng = platform_get_drvdata(pdev); + + hwrng_unregister(&rng->hwrng); + return 0; +} + +static const struct of_device_id msm_rng_of_match[] = { + { .compatible = "qcom,prng", }, + {} +}; +MODULE_DEVICE_TABLE(of, msm_rng_of_match); + +static struct platform_driver msm_rng_driver = { + .probe = msm_rng_probe, + .remove = msm_rng_remove, + .driver = { + .name = KBUILD_MODNAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(msm_rng_of_match), + } +}; +module_platform_driver(msm_rng_driver); + +MODULE_ALIAS("platform:" KBUILD_MODNAME); +MODULE_AUTHOR("The Linux Foundation"); +MODULE_DESCRIPTION("Qualcomm MSM random number generator driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/char/hw_random/n2-drv.c b/drivers/char/hw_random/n2-drv.c index f9beed54d0c..292a5889f67 100644 --- a/drivers/char/hw_random/n2-drv.c +++ b/drivers/char/hw_random/n2-drv.c @@ -7,7 +7,6 @@ #include <linux/module.h> #include <linux/types.h> #include <linux/delay.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/workqueue.h> #include <linux/preempt.h> @@ -633,7 +632,7 @@ static int n2rng_probe(struct platform_device *op) multi_capable = (match->data != NULL); n2rng_driver_version(); - np = kzalloc(sizeof(*np), GFP_KERNEL); + np = devm_kzalloc(&op->dev, sizeof(*np), GFP_KERNEL); if (!np) goto out; np->op = op; @@ -654,7 +653,7 @@ static int n2rng_probe(struct platform_device *op) &np->hvapi_minor)) { dev_err(&op->dev, "Cannot register suitable " "HVAPI version.\n"); - goto out_free; + goto out; } } @@ -677,15 +676,16 @@ static int n2rng_probe(struct platform_device *op) dev_info(&op->dev, "Registered RNG HVAPI major %lu minor %lu\n", np->hvapi_major, np->hvapi_minor); - np->units = kzalloc(sizeof(struct n2rng_unit) * np->num_units, - GFP_KERNEL); + np->units = devm_kzalloc(&op->dev, + sizeof(struct n2rng_unit) * np->num_units, + GFP_KERNEL); err = -ENOMEM; if (!np->units) goto out_hvapi_unregister; err = n2rng_init_control(np); if (err) - goto out_free_units; + goto out_hvapi_unregister; dev_info(&op->dev, "Found %s RNG, units: %d\n", ((np->flags & N2RNG_FLAG_MULTI) ? @@ -698,7 +698,7 @@ static int n2rng_probe(struct platform_device *op) err = hwrng_register(&np->hwrng); if (err) - goto out_free_units; + goto out_hvapi_unregister; platform_set_drvdata(op, np); @@ -706,15 +706,9 @@ static int n2rng_probe(struct platform_device *op) return 0; -out_free_units: - kfree(np->units); - np->units = NULL; - out_hvapi_unregister: sun4v_hvapi_unregister(HV_GRP_RNG); -out_free: - kfree(np); out: return err; } @@ -731,11 +725,6 @@ static int n2rng_remove(struct platform_device *op) sun4v_hvapi_unregister(HV_GRP_RNG); - kfree(np->units); - np->units = NULL; - - kfree(np); - return 0; } diff --git a/drivers/char/hw_random/nomadik-rng.c b/drivers/char/hw_random/nomadik-rng.c index 232b87fb5fc..9c858157724 100644 --- a/drivers/char/hw_random/nomadik-rng.c +++ b/drivers/char/hw_random/nomadik-rng.c @@ -10,7 +10,6 @@ #include <linux/kernel.h> #include <linux/module.h> -#include <linux/init.h> #include <linux/device.h> #include <linux/amba/bus.h> #include <linux/hw_random.h> @@ -44,7 +43,7 @@ static int nmk_rng_probe(struct amba_device *dev, const struct amba_id *id) void __iomem *base; int ret; - rng_clk = clk_get(&dev->dev, NULL); + rng_clk = devm_clk_get(&dev->dev, NULL); if (IS_ERR(rng_clk)) { dev_err(&dev->dev, "could not get rng clock\n"); ret = PTR_ERR(rng_clk); @@ -57,33 +56,28 @@ static int nmk_rng_probe(struct amba_device *dev, const struct amba_id *id) if (ret) goto out_clk; ret = -ENOMEM; - base = ioremap(dev->res.start, resource_size(&dev->res)); + base = devm_ioremap(&dev->dev, dev->res.start, + resource_size(&dev->res)); if (!base) goto out_release; nmk_rng.priv = (unsigned long)base; ret = hwrng_register(&nmk_rng); if (ret) - goto out_unmap; + goto out_release; return 0; -out_unmap: - iounmap(base); out_release: amba_release_regions(dev); out_clk: clk_disable(rng_clk); - clk_put(rng_clk); return ret; } static int nmk_rng_remove(struct amba_device *dev) { - void __iomem *base = (void __iomem *)nmk_rng.priv; hwrng_unregister(&nmk_rng); - iounmap(base); amba_release_regions(dev); clk_disable(rng_clk); - clk_put(rng_clk); return 0; } diff --git a/drivers/char/hw_random/octeon-rng.c b/drivers/char/hw_random/octeon-rng.c index f2885dbe184..b5cc3420c65 100644 --- a/drivers/char/hw_random/octeon-rng.c +++ b/drivers/char/hw_random/octeon-rng.c @@ -10,7 +10,6 @@ */ #include <linux/module.h> -#include <linux/init.h> #include <linux/platform_device.h> #include <linux/device.h> #include <linux/hw_random.h> diff --git a/drivers/char/hw_random/omap-rng.c b/drivers/char/hw_random/omap-rng.c index 9b89ff4881d..f66ea258382 100644 --- a/drivers/char/hw_random/omap-rng.c +++ b/drivers/char/hw_random/omap-rng.c @@ -369,10 +369,8 @@ static int omap_rng_probe(struct platform_device *pdev) int ret; priv = devm_kzalloc(dev, sizeof(struct omap_rng_dev), GFP_KERNEL); - if (!priv) { - dev_err(&pdev->dev, "could not allocate memory\n"); + if (!priv) return -ENOMEM; - }; omap_rng_ops.priv = (unsigned long)priv; platform_set_drvdata(pdev, priv); diff --git a/drivers/char/hw_random/omap3-rom-rng.c b/drivers/char/hw_random/omap3-rom-rng.c new file mode 100644 index 00000000000..6f2eaffed62 --- /dev/null +++ b/drivers/char/hw_random/omap3-rom-rng.c @@ -0,0 +1,140 @@ +/* + * omap3-rom-rng.c - RNG driver for TI OMAP3 CPU family + * + * Copyright (C) 2009 Nokia Corporation + * Author: Juha Yrjola <juha.yrjola@solidboot.com> + * + * Copyright (C) 2013 Pali Rohár <pali.rohar@gmail.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/random.h> +#include <linux/hw_random.h> +#include <linux/timer.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/platform_device.h> + +#define RNG_RESET 0x01 +#define RNG_GEN_PRNG_HW_INIT 0x02 +#define RNG_GEN_HW 0x08 + +/* param1: ptr, param2: count, param3: flag */ +static u32 (*omap3_rom_rng_call)(u32, u32, u32); + +static struct timer_list idle_timer; +static int rng_idle; +static struct clk *rng_clk; + +static void omap3_rom_rng_idle(unsigned long data) +{ + int r; + + r = omap3_rom_rng_call(0, 0, RNG_RESET); + if (r != 0) { + pr_err("reset failed: %d\n", r); + return; + } + clk_disable_unprepare(rng_clk); + rng_idle = 1; +} + +static int omap3_rom_rng_get_random(void *buf, unsigned int count) +{ + u32 r; + u32 ptr; + + del_timer_sync(&idle_timer); + if (rng_idle) { + clk_prepare_enable(rng_clk); + r = omap3_rom_rng_call(0, 0, RNG_GEN_PRNG_HW_INIT); + if (r != 0) { + clk_disable_unprepare(rng_clk); + pr_err("HW init failed: %d\n", r); + return -EIO; + } + rng_idle = 0; + } + + ptr = virt_to_phys(buf); + r = omap3_rom_rng_call(ptr, count, RNG_GEN_HW); + mod_timer(&idle_timer, jiffies + msecs_to_jiffies(500)); + if (r != 0) + return -EINVAL; + return 0; +} + +static int omap3_rom_rng_data_present(struct hwrng *rng, int wait) +{ + return 1; +} + +static int omap3_rom_rng_data_read(struct hwrng *rng, u32 *data) +{ + int r; + + r = omap3_rom_rng_get_random(data, 4); + if (r < 0) + return r; + return 4; +} + +static struct hwrng omap3_rom_rng_ops = { + .name = "omap3-rom", + .data_present = omap3_rom_rng_data_present, + .data_read = omap3_rom_rng_data_read, +}; + +static int omap3_rom_rng_probe(struct platform_device *pdev) +{ + pr_info("initializing\n"); + + omap3_rom_rng_call = pdev->dev.platform_data; + if (!omap3_rom_rng_call) { + pr_err("omap3_rom_rng_call is NULL\n"); + return -EINVAL; + } + + setup_timer(&idle_timer, omap3_rom_rng_idle, 0); + rng_clk = devm_clk_get(&pdev->dev, "ick"); + if (IS_ERR(rng_clk)) { + pr_err("unable to get RNG clock\n"); + return PTR_ERR(rng_clk); + } + + /* Leave the RNG in reset state. */ + clk_prepare_enable(rng_clk); + omap3_rom_rng_idle(0); + + return hwrng_register(&omap3_rom_rng_ops); +} + +static int omap3_rom_rng_remove(struct platform_device *pdev) +{ + hwrng_unregister(&omap3_rom_rng_ops); + clk_disable_unprepare(rng_clk); + return 0; +} + +static struct platform_driver omap3_rom_rng_driver = { + .driver = { + .name = "omap3-rom-rng", + .owner = THIS_MODULE, + }, + .probe = omap3_rom_rng_probe, + .remove = omap3_rom_rng_remove, +}; + +module_platform_driver(omap3_rom_rng_driver); + +MODULE_ALIAS("platform:omap3-rom-rng"); +MODULE_AUTHOR("Juha Yrjola"); +MODULE_AUTHOR("Pali Rohár <pali.rohar@gmail.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/char/hw_random/pasemi-rng.c b/drivers/char/hw_random/pasemi-rng.c index c6df5b29af0..c66279bb6ef 100644 --- a/drivers/char/hw_random/pasemi-rng.c +++ b/drivers/char/hw_random/pasemi-rng.c @@ -24,6 +24,7 @@ #include <linux/platform_device.h> #include <linux/hw_random.h> #include <linux/delay.h> +#include <linux/of_address.h> #include <linux/of_platform.h> #include <asm/io.h> diff --git a/drivers/char/hw_random/picoxcell-rng.c b/drivers/char/hw_random/picoxcell-rng.c deleted file mode 100644 index 3d4c2293c6f..00000000000 --- a/drivers/char/hw_random/picoxcell-rng.c +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Copyright (c) 2010-2011 Picochip Ltd., Jamie Iles - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * All enquiries to support@picochip.com - */ -#include <linux/clk.h> -#include <linux/delay.h> -#include <linux/err.h> -#include <linux/hw_random.h> -#include <linux/io.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/platform_device.h> - -#define DATA_REG_OFFSET 0x0200 -#define CSR_REG_OFFSET 0x0278 -#define CSR_OUT_EMPTY_MASK (1 << 24) -#define CSR_FAULT_MASK (1 << 1) -#define TRNG_BLOCK_RESET_MASK (1 << 0) -#define TAI_REG_OFFSET 0x0380 - -/* - * The maximum amount of time in microseconds to spend waiting for data if the - * core wants us to wait. The TRNG should generate 32 bits every 320ns so a - * timeout of 20us seems reasonable. The TRNG does builtin tests of the data - * for randomness so we can't always assume there is data present. - */ -#define PICO_TRNG_TIMEOUT 20 - -static void __iomem *rng_base; -static struct clk *rng_clk; -static struct device *rng_dev; - -static inline u32 picoxcell_trng_read_csr(void) -{ - return __raw_readl(rng_base + CSR_REG_OFFSET); -} - -static inline bool picoxcell_trng_is_empty(void) -{ - return picoxcell_trng_read_csr() & CSR_OUT_EMPTY_MASK; -} - -/* - * Take the random number generator out of reset and make sure the interrupts - * are masked. We shouldn't need to get large amounts of random bytes so just - * poll the status register. The hardware generates 32 bits every 320ns so we - * shouldn't have to wait long enough to warrant waiting for an IRQ. - */ -static void picoxcell_trng_start(void) -{ - __raw_writel(0, rng_base + TAI_REG_OFFSET); - __raw_writel(0, rng_base + CSR_REG_OFFSET); -} - -static void picoxcell_trng_reset(void) -{ - __raw_writel(TRNG_BLOCK_RESET_MASK, rng_base + CSR_REG_OFFSET); - __raw_writel(TRNG_BLOCK_RESET_MASK, rng_base + TAI_REG_OFFSET); - picoxcell_trng_start(); -} - -/* - * Get some random data from the random number generator. The hw_random core - * layer provides us with locking. - */ -static int picoxcell_trng_read(struct hwrng *rng, void *buf, size_t max, - bool wait) -{ - int i; - - /* Wait for some data to become available. */ - for (i = 0; i < PICO_TRNG_TIMEOUT && picoxcell_trng_is_empty(); ++i) { - if (!wait) - return 0; - - udelay(1); - } - - if (picoxcell_trng_read_csr() & CSR_FAULT_MASK) { - dev_err(rng_dev, "fault detected, resetting TRNG\n"); - picoxcell_trng_reset(); - return -EIO; - } - - if (i == PICO_TRNG_TIMEOUT) - return 0; - - *(u32 *)buf = __raw_readl(rng_base + DATA_REG_OFFSET); - return sizeof(u32); -} - -static struct hwrng picoxcell_trng = { - .name = "picoxcell", - .read = picoxcell_trng_read, -}; - -static int picoxcell_trng_probe(struct platform_device *pdev) -{ - int ret; - struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - - if (!mem) { - dev_warn(&pdev->dev, "no memory resource\n"); - return -ENOMEM; - } - - if (!devm_request_mem_region(&pdev->dev, mem->start, resource_size(mem), - "picoxcell_trng")) { - dev_warn(&pdev->dev, "unable to request io mem\n"); - return -EBUSY; - } - - rng_base = devm_ioremap(&pdev->dev, mem->start, resource_size(mem)); - if (!rng_base) { - dev_warn(&pdev->dev, "unable to remap io mem\n"); - return -ENOMEM; - } - - rng_clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(rng_clk)) { - dev_warn(&pdev->dev, "no clk\n"); - return PTR_ERR(rng_clk); - } - - ret = clk_enable(rng_clk); - if (ret) { - dev_warn(&pdev->dev, "unable to enable clk\n"); - goto err_enable; - } - - picoxcell_trng_start(); - ret = hwrng_register(&picoxcell_trng); - if (ret) - goto err_register; - - rng_dev = &pdev->dev; - dev_info(&pdev->dev, "pixoxcell random number generator active\n"); - - return 0; - -err_register: - clk_disable(rng_clk); -err_enable: - clk_put(rng_clk); - - return ret; -} - -static int picoxcell_trng_remove(struct platform_device *pdev) -{ - hwrng_unregister(&picoxcell_trng); - clk_disable(rng_clk); - clk_put(rng_clk); - - return 0; -} - -#ifdef CONFIG_PM -static int picoxcell_trng_suspend(struct device *dev) -{ - clk_disable(rng_clk); - - return 0; -} - -static int picoxcell_trng_resume(struct device *dev) -{ - return clk_enable(rng_clk); -} - -static const struct dev_pm_ops picoxcell_trng_pm_ops = { - .suspend = picoxcell_trng_suspend, - .resume = picoxcell_trng_resume, -}; -#endif /* CONFIG_PM */ - -static struct platform_driver picoxcell_trng_driver = { - .probe = picoxcell_trng_probe, - .remove = picoxcell_trng_remove, - .driver = { - .name = "picoxcell-trng", - .owner = THIS_MODULE, -#ifdef CONFIG_PM - .pm = &picoxcell_trng_pm_ops, -#endif /* CONFIG_PM */ - }, -}; - -module_platform_driver(picoxcell_trng_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Jamie Iles"); -MODULE_DESCRIPTION("Picochip picoXcell TRNG driver"); diff --git a/drivers/char/hw_random/powernv-rng.c b/drivers/char/hw_random/powernv-rng.c new file mode 100644 index 00000000000..3f4f6320456 --- /dev/null +++ b/drivers/char/hw_random/powernv-rng.c @@ -0,0 +1,81 @@ +/* + * Copyright 2013 Michael Ellerman, Guo Chao, IBM Corp. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/random.h> +#include <linux/hw_random.h> + +static int powernv_rng_read(struct hwrng *rng, void *data, size_t max, bool wait) +{ + unsigned long *buf; + int i, len; + + /* We rely on rng_buffer_size() being >= sizeof(unsigned long) */ + len = max / sizeof(unsigned long); + + buf = (unsigned long *)data; + + for (i = 0; i < len; i++) + powernv_get_random_long(buf++); + + return len * sizeof(unsigned long); +} + +static struct hwrng powernv_hwrng = { + .name = "powernv-rng", + .read = powernv_rng_read, +}; + +static int powernv_rng_remove(struct platform_device *pdev) +{ + hwrng_unregister(&powernv_hwrng); + + return 0; +} + +static int powernv_rng_probe(struct platform_device *pdev) +{ + int rc; + + rc = hwrng_register(&powernv_hwrng); + if (rc) { + /* We only register one device, ignore any others */ + if (rc == -EEXIST) + rc = -ENODEV; + + return rc; + } + + pr_info("Registered powernv hwrng.\n"); + + return 0; +} + +static struct of_device_id powernv_rng_match[] = { + { .compatible = "ibm,power-rng",}, + {}, +}; +MODULE_DEVICE_TABLE(of, powernv_rng_match); + +static struct platform_driver powernv_rng_driver = { + .driver = { + .name = "powernv_rng", + .of_match_table = powernv_rng_match, + }, + .probe = powernv_rng_probe, + .remove = powernv_rng_remove, +}; +module_platform_driver(powernv_rng_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Bare metal HWRNG driver for POWER7+ and above"); diff --git a/drivers/char/hw_random/ppc4xx-rng.c b/drivers/char/hw_random/ppc4xx-rng.c index 732c330805f..521f76b0934 100644 --- a/drivers/char/hw_random/ppc4xx-rng.c +++ b/drivers/char/hw_random/ppc4xx-rng.c @@ -13,6 +13,7 @@ #include <linux/platform_device.h> #include <linux/hw_random.h> #include <linux/delay.h> +#include <linux/of_address.h> #include <linux/of_platform.h> #include <asm/io.h> diff --git a/drivers/char/hw_random/pseries-rng.c b/drivers/char/hw_random/pseries-rng.c index 5f1197929f0..ab7ffdec0ec 100644 --- a/drivers/char/hw_random/pseries-rng.c +++ b/drivers/char/hw_random/pseries-rng.c @@ -17,18 +17,25 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/kernel.h> #include <linux/module.h> #include <linux/hw_random.h> #include <asm/vio.h> -#define MODULE_NAME "pseries-rng" static int pseries_rng_data_read(struct hwrng *rng, u32 *data) { - if (plpar_hcall(H_RANDOM, (unsigned long *)data) != H_SUCCESS) { - printk(KERN_ERR "pseries rng hcall error\n"); - return 0; + int rc; + + rc = plpar_hcall(H_RANDOM, (unsigned long *)data); + if (rc != H_SUCCESS) { + pr_err_ratelimited("H_RANDOM call failed %d\n", rc); + return -EIO; } + + /* The hypervisor interface returns 64 bits */ return 8; } @@ -47,7 +54,7 @@ static unsigned long pseries_rng_get_desired_dma(struct vio_dev *vdev) }; static struct hwrng pseries_rng = { - .name = MODULE_NAME, + .name = KBUILD_MODNAME, .data_read = pseries_rng_data_read, }; @@ -70,7 +77,7 @@ static struct vio_device_id pseries_rng_driver_ids[] = { MODULE_DEVICE_TABLE(vio, pseries_rng_driver_ids); static struct vio_driver pseries_rng_driver = { - .name = MODULE_NAME, + .name = KBUILD_MODNAME, .probe = pseries_rng_probe, .remove = pseries_rng_remove, .get_desired_dma = pseries_rng_get_desired_dma, diff --git a/drivers/char/hw_random/timeriomem-rng.c b/drivers/char/hw_random/timeriomem-rng.c index d2120ba8f3f..b6ab9ac3f34 100644 --- a/drivers/char/hw_random/timeriomem-rng.c +++ b/drivers/char/hw_random/timeriomem-rng.c @@ -79,7 +79,7 @@ static int timeriomem_rng_data_read(struct hwrng *rng, u32 *data) priv->expires = cur + delay; priv->present = 0; - INIT_COMPLETION(priv->completion); + reinit_completion(&priv->completion); mod_timer(&priv->timer, priv->expires); return 4; @@ -118,11 +118,10 @@ static int timeriomem_rng_probe(struct platform_device *pdev) } /* Allocate memory for the device structure (and zero it) */ - priv = kzalloc(sizeof(struct timeriomem_rng_private_data), GFP_KERNEL); - if (!priv) { - dev_err(&pdev->dev, "failed to allocate device structure.\n"); + priv = devm_kzalloc(&pdev->dev, + sizeof(struct timeriomem_rng_private_data), GFP_KERNEL); + if (!priv) return -ENOMEM; - } platform_set_drvdata(pdev, priv); @@ -134,17 +133,16 @@ static int timeriomem_rng_probe(struct platform_device *pdev) period = i; else { dev_err(&pdev->dev, "missing period\n"); - err = -EINVAL; - goto out_free; + return -EINVAL; } - } else + } else { period = pdata->period; + } priv->period = usecs_to_jiffies(period); if (priv->period < 1) { dev_err(&pdev->dev, "period is less than one jiffy\n"); - err = -EINVAL; - goto out_free; + return -EINVAL; } priv->expires = jiffies; @@ -160,24 +158,16 @@ static int timeriomem_rng_probe(struct platform_device *pdev) priv->timeriomem_rng_ops.data_read = timeriomem_rng_data_read; priv->timeriomem_rng_ops.priv = (unsigned long)priv; - if (!request_mem_region(res->start, resource_size(res), - dev_name(&pdev->dev))) { - dev_err(&pdev->dev, "request_mem_region failed\n"); - err = -EBUSY; + priv->io_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->io_base)) { + err = PTR_ERR(priv->io_base); goto out_timer; } - priv->io_base = ioremap(res->start, resource_size(res)); - if (priv->io_base == NULL) { - dev_err(&pdev->dev, "ioremap failed\n"); - err = -EIO; - goto out_release_io; - } - err = hwrng_register(&priv->timeriomem_rng_ops); if (err) { dev_err(&pdev->dev, "problem registering\n"); - goto out; + goto out_timer; } dev_info(&pdev->dev, "32bits from 0x%p @ %dus\n", @@ -185,30 +175,18 @@ static int timeriomem_rng_probe(struct platform_device *pdev) return 0; -out: - iounmap(priv->io_base); -out_release_io: - release_mem_region(res->start, resource_size(res)); out_timer: del_timer_sync(&priv->timer); -out_free: - kfree(priv); return err; } static int timeriomem_rng_remove(struct platform_device *pdev) { struct timeriomem_rng_private_data *priv = platform_get_drvdata(pdev); - struct resource *res; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); hwrng_unregister(&priv->timeriomem_rng_ops); del_timer_sync(&priv->timer); - iounmap(priv->io_base); - release_mem_region(res->start, resource_size(res)); - kfree(priv); return 0; } diff --git a/drivers/char/hw_random/via-rng.c b/drivers/char/hw_random/via-rng.c index e737772ad69..de5a6dcfb3e 100644 --- a/drivers/char/hw_random/via-rng.c +++ b/drivers/char/hw_random/via-rng.c @@ -221,7 +221,7 @@ static void __exit mod_exit(void) module_init(mod_init); module_exit(mod_exit); -static struct x86_cpu_id via_rng_cpu_id[] = { +static struct x86_cpu_id __maybe_unused via_rng_cpu_id[] = { X86_FEATURE_MATCH(X86_FEATURE_XSTORE), {} }; diff --git a/drivers/char/hw_random/virtio-rng.c b/drivers/char/hw_random/virtio-rng.c index ef46a9cfd83..e9b15bc18b4 100644 --- a/drivers/char/hw_random/virtio-rng.c +++ b/drivers/char/hw_random/virtio-rng.c @@ -25,102 +25,140 @@ #include <linux/virtio_rng.h> #include <linux/module.h> -static struct virtqueue *vq; -static unsigned int data_avail; -static DECLARE_COMPLETION(have_data); -static bool busy; +static DEFINE_IDA(rng_index_ida); + +struct virtrng_info { + struct virtio_device *vdev; + struct hwrng hwrng; + struct virtqueue *vq; + unsigned int data_avail; + struct completion have_data; + bool busy; + char name[25]; + int index; +}; + +static bool probe_done; static void random_recv_done(struct virtqueue *vq) { + struct virtrng_info *vi = vq->vdev->priv; + /* We can get spurious callbacks, e.g. shared IRQs + virtio_pci. */ - if (!virtqueue_get_buf(vq, &data_avail)) + if (!virtqueue_get_buf(vi->vq, &vi->data_avail)) return; - complete(&have_data); + complete(&vi->have_data); } /* The host will fill any buffer we give it with sweet, sweet randomness. */ -static void register_buffer(u8 *buf, size_t size) +static void register_buffer(struct virtrng_info *vi, u8 *buf, size_t size) { struct scatterlist sg; sg_init_one(&sg, buf, size); /* There should always be room for one buffer. */ - if (virtqueue_add_inbuf(vq, &sg, 1, buf, GFP_KERNEL) < 0) - BUG(); + virtqueue_add_inbuf(vi->vq, &sg, 1, buf, GFP_KERNEL); - virtqueue_kick(vq); + virtqueue_kick(vi->vq); } static int virtio_read(struct hwrng *rng, void *buf, size_t size, bool wait) { int ret; + struct virtrng_info *vi = (struct virtrng_info *)rng->priv; - if (!busy) { - busy = true; - init_completion(&have_data); - register_buffer(buf, size); + /* + * Don't ask host for data till we're setup. This call can + * happen during hwrng_register(), after commit d9e7972619. + */ + if (unlikely(!probe_done)) + return 0; + + if (!vi->busy) { + vi->busy = true; + init_completion(&vi->have_data); + register_buffer(vi, buf, size); } if (!wait) return 0; - ret = wait_for_completion_killable(&have_data); + ret = wait_for_completion_killable(&vi->have_data); if (ret < 0) return ret; - busy = false; + vi->busy = false; - return data_avail; + return vi->data_avail; } static void virtio_cleanup(struct hwrng *rng) { - if (busy) - wait_for_completion(&have_data); -} + struct virtrng_info *vi = (struct virtrng_info *)rng->priv; - -static struct hwrng virtio_hwrng = { - .name = "virtio", - .cleanup = virtio_cleanup, - .read = virtio_read, -}; + if (vi->busy) + wait_for_completion(&vi->have_data); +} static int probe_common(struct virtio_device *vdev) { - int err; + int err, index; + struct virtrng_info *vi = NULL; - if (vq) { - /* We only support one device for now */ - return -EBUSY; + vi = kzalloc(sizeof(struct virtrng_info), GFP_KERNEL); + if (!vi) + return -ENOMEM; + + vi->index = index = ida_simple_get(&rng_index_ida, 0, 0, GFP_KERNEL); + if (index < 0) { + kfree(vi); + return index; } + sprintf(vi->name, "virtio_rng.%d", index); + init_completion(&vi->have_data); + + vi->hwrng = (struct hwrng) { + .read = virtio_read, + .cleanup = virtio_cleanup, + .priv = (unsigned long)vi, + .name = vi->name, + }; + vdev->priv = vi; + /* We expect a single virtqueue. */ - vq = virtio_find_single_vq(vdev, random_recv_done, "input"); - if (IS_ERR(vq)) { - err = PTR_ERR(vq); - vq = NULL; + vi->vq = virtio_find_single_vq(vdev, random_recv_done, "input"); + if (IS_ERR(vi->vq)) { + err = PTR_ERR(vi->vq); + vi->vq = NULL; + kfree(vi); + ida_simple_remove(&rng_index_ida, index); return err; } - err = hwrng_register(&virtio_hwrng); + err = hwrng_register(&vi->hwrng); if (err) { vdev->config->del_vqs(vdev); - vq = NULL; + vi->vq = NULL; + kfree(vi); + ida_simple_remove(&rng_index_ida, index); return err; } + probe_done = true; return 0; } static void remove_common(struct virtio_device *vdev) { + struct virtrng_info *vi = vdev->priv; vdev->config->reset(vdev); - busy = false; - hwrng_unregister(&virtio_hwrng); + vi->busy = false; + hwrng_unregister(&vi->hwrng); vdev->config->del_vqs(vdev); - vq = NULL; + ida_simple_remove(&rng_index_ida, vi->index); + kfree(vi); } static int virtrng_probe(struct virtio_device *vdev) @@ -133,7 +171,7 @@ static void virtrng_remove(struct virtio_device *vdev) remove_common(vdev); } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int virtrng_freeze(struct virtio_device *vdev) { remove_common(vdev); @@ -157,7 +195,7 @@ static struct virtio_driver virtio_rng_driver = { .id_table = id_table, .probe = virtrng_probe, .remove = virtrng_remove, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .freeze = virtrng_freeze, .restore = virtrng_restore, #endif |
