lkml.org 
[lkml]   [2013]   [Sep]   [26]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 1/3] powerpc: Implement arch_get_random_long/int() for powernv
    Date
    Add the plumbing to implement arch_get_random_long/int(). It didn't seem
    worth adding an extra ppc_md hook for int, so we reuse the one for long.

    Add an implementation for powernv based on the hwrng found in power7+
    systems. We whiten the output of the hwrng, and the result passes all
    the dieharder tests.

    Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
    ---
    arch/powerpc/Kconfig | 3 +
    arch/powerpc/include/asm/archrandom.h | 32 +++++++++
    arch/powerpc/include/asm/machdep.h | 4 ++
    arch/powerpc/platforms/powernv/Kconfig | 1 +
    arch/powerpc/platforms/powernv/Makefile | 2 +-
    arch/powerpc/platforms/powernv/rng.c | 122 ++++++++++++++++++++++++++++++++
    6 files changed, 163 insertions(+), 1 deletion(-)
    create mode 100644 arch/powerpc/include/asm/archrandom.h
    create mode 100644 arch/powerpc/platforms/powernv/rng.c

    diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
    index 38f3b7e..45f16f7 100644
    --- a/arch/powerpc/Kconfig
    +++ b/arch/powerpc/Kconfig
    @@ -1009,6 +1009,9 @@ config PHYSICAL_START
    default "0x00000000"
    endif

    +config ARCH_RANDOM
    + def_bool n
    +
    source "net/Kconfig"

    source "drivers/Kconfig"
    diff --git a/arch/powerpc/include/asm/archrandom.h b/arch/powerpc/include/asm/archrandom.h
    new file mode 100644
    index 0000000..d853d16
    --- /dev/null
    +++ b/arch/powerpc/include/asm/archrandom.h
    @@ -0,0 +1,32 @@
    +#ifndef _ASM_POWERPC_ARCHRANDOM_H
    +#define _ASM_POWERPC_ARCHRANDOM_H
    +
    +#ifdef CONFIG_ARCH_RANDOM
    +
    +#include <asm/machdep.h>
    +
    +static inline int arch_get_random_long(unsigned long *v)
    +{
    + if (ppc_md.get_random_long)
    + return ppc_md.get_random_long(v);
    +
    + return 0;
    +}
    +
    +static inline int arch_get_random_int(unsigned int *v)
    +{
    + unsigned long val;
    + int rc;
    +
    + rc = arch_get_random_long(&val);
    + if (rc)
    + *v = val;
    +
    + return rc;
    +}
    +
    +int powernv_get_random_long(unsigned long *v);
    +
    +#endif /* CONFIG_ARCH_RANDOM */
    +
    +#endif /* _ASM_POWERPC_ARCHRANDOM_H */
    diff --git a/arch/powerpc/include/asm/machdep.h b/arch/powerpc/include/asm/machdep.h
    index 8b48090..ce6cc2a 100644
    --- a/arch/powerpc/include/asm/machdep.h
    +++ b/arch/powerpc/include/asm/machdep.h
    @@ -263,6 +263,10 @@ struct machdep_calls {
    ssize_t (*cpu_probe)(const char *, size_t);
    ssize_t (*cpu_release)(const char *, size_t);
    #endif
    +
    +#ifdef CONFIG_ARCH_RANDOM
    + int (*get_random_long)(unsigned long *v);
    +#endif
    };

    extern void e500_idle(void);
    diff --git a/arch/powerpc/platforms/powernv/Kconfig b/arch/powerpc/platforms/powernv/Kconfig
    index 6fae5eb..2108464 100644
    --- a/arch/powerpc/platforms/powernv/Kconfig
    +++ b/arch/powerpc/platforms/powernv/Kconfig
    @@ -9,6 +9,7 @@ config PPC_POWERNV
    select EPAPR_BOOT
    select PPC_INDIRECT_PIO
    select PPC_UDBG_16550
    + select ARCH_RANDOM
    default y

    config POWERNV_MSI
    diff --git a/arch/powerpc/platforms/powernv/Makefile b/arch/powerpc/platforms/powernv/Makefile
    index 300c437..6760a86 100644
    --- a/arch/powerpc/platforms/powernv/Makefile
    +++ b/arch/powerpc/platforms/powernv/Makefile
    @@ -1,5 +1,5 @@
    obj-y += setup.o opal-takeover.o opal-wrappers.o opal.o
    -obj-y += opal-rtc.o opal-nvram.o opal-lpc.o
    +obj-y += opal-rtc.o opal-nvram.o opal-lpc.o rng.o

    obj-$(CONFIG_SMP) += smp.o
    obj-$(CONFIG_PCI) += pci.o pci-p5ioc2.o pci-ioda.o
    diff --git a/arch/powerpc/platforms/powernv/rng.c b/arch/powerpc/platforms/powernv/rng.c
    new file mode 100644
    index 0000000..51d2e6a
    --- /dev/null
    +++ b/arch/powerpc/platforms/powernv/rng.c
    @@ -0,0 +1,122 @@
    +/*
    + * Copyright 2013, Michael Ellerman, IBM Corporation.
    + *
    + * 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) "powernv-rng: " fmt
    +
    +#include <linux/kernel.h>
    +#include <linux/of.h>
    +#include <linux/of_platform.h>
    +#include <linux/slab.h>
    +#include <asm/archrandom.h>
    +#include <asm/io.h>
    +#include <asm/machdep.h>
    +
    +
    +struct powernv_rng {
    + void __iomem *regs;
    + unsigned long mask;
    +};
    +
    +static DEFINE_PER_CPU(struct powernv_rng *, powernv_rng);
    +
    +
    +static unsigned long rng_whiten(struct powernv_rng *rng, unsigned long val)
    +{
    + unsigned long parity;
    +
    + /* Calculate the parity of the value */
    + asm ("popcntd %0,%1" : "=r" (parity) : "r" (val));
    +
    + /* xor our value with the previous mask */
    + val ^= rng->mask;
    +
    + /* update the mask based on the parity of this value */
    + rng->mask = (rng->mask << 1) | (parity & 1);
    +
    + return val;
    +}
    +
    +int powernv_get_random_long(unsigned long *v)
    +{
    + struct powernv_rng *rng;
    +
    + rng = get_cpu_var(powernv_rng);
    +
    + *v = rng_whiten(rng, in_be64(rng->regs));
    +
    + put_cpu_var(rng);
    +
    + return 1;
    +}
    +EXPORT_SYMBOL_GPL(powernv_get_random_long);
    +
    +static __init void rng_init_per_cpu(struct powernv_rng *rng,
    + struct device_node *dn)
    +{
    + int chip_id, cpu;
    +
    + chip_id = of_get_ibm_chip_id(dn);
    + if (chip_id == -1)
    + pr_warn("no ibm,chip-id found for %s\n", dn->full_name);
    +
    + for_each_possible_cpu(cpu) {
    + if (per_cpu(powernv_rng, cpu) == NULL ||
    + cpu_to_chip_id(cpu) == chip_id) {
    + per_cpu(powernv_rng, cpu) = rng;
    + }
    + }
    +}
    +
    +static __init int rng_create(struct device_node *dn)
    +{
    + struct powernv_rng *rng;
    + unsigned long val;
    +
    + rng = kzalloc(sizeof(*rng), GFP_KERNEL);
    + if (!rng)
    + return -ENOMEM;
    +
    + rng->regs = of_iomap(dn, 0);
    + if (!rng->regs) {
    + kfree(rng);
    + return -ENXIO;
    + }
    +
    + val = in_be64(rng->regs);
    + rng->mask = val;
    +
    + rng_init_per_cpu(rng, dn);
    +
    + pr_info_once("registering arch random hook\n");
    +
    + ppc_md.get_random_long = powernv_get_random_long;
    +
    + return 0;
    +}
    +
    +static __init int rng_init(void)
    +{
    + struct device_node *dn;
    + int rc;
    +
    + for_each_compatible_node(dn, NULL, "ibm,power-rng") {
    + rc = rng_create(dn);
    + if (rc) {
    + pr_err("failed creating rng for %s (%d)\n",
    + dn->full_name, rc);
    + continue;
    + }
    +
    + /* Create devices for hwrng driver */
    + of_platform_device_create(dn, NULL, NULL);
    + }
    +
    + return 0;
    +}
    +subsys_initcall(rng_init);
    --
    1.8.1.2


    \
     
     \ /
      Last update: 2013-09-26 09:01    [W:3.137 / U:0.012 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site