aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Ellerman <michael@ellerman.id.au>2013-10-10 23:07:57 -0400
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2013-10-11 01:50:19 -0400
commita4da0d50b2a00b79390092e6248ca88b7d93c81d (patch)
tree4a481f28de594306b612938e037dd015114c2fd7
parentf95dabef4c70e27e5114f4802fe6234ff82ce406 (diff)
powerpc: Implement arch_get_random_long/int() for powernv
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> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
-rw-r--r--arch/powerpc/Kconfig3
-rw-r--r--arch/powerpc/include/asm/archrandom.h32
-rw-r--r--arch/powerpc/include/asm/machdep.h4
-rw-r--r--arch/powerpc/platforms/powernv/Kconfig1
-rw-r--r--arch/powerpc/platforms/powernv/Makefile2
-rw-r--r--arch/powerpc/platforms/powernv/rng.c122
6 files changed, 163 insertions, 1 deletions
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index c5a868bc7aa4..875d815a8e7f 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -1012,6 +1012,9 @@ config PHYSICAL_START
1012 default "0x00000000" 1012 default "0x00000000"
1013endif 1013endif
1014 1014
1015config ARCH_RANDOM
1016 def_bool n
1017
1015source "net/Kconfig" 1018source "net/Kconfig"
1016 1019
1017source "drivers/Kconfig" 1020source "drivers/Kconfig"
diff --git a/arch/powerpc/include/asm/archrandom.h b/arch/powerpc/include/asm/archrandom.h
new file mode 100644
index 000000000000..d853d163ba47
--- /dev/null
+++ b/arch/powerpc/include/asm/archrandom.h
@@ -0,0 +1,32 @@
1#ifndef _ASM_POWERPC_ARCHRANDOM_H
2#define _ASM_POWERPC_ARCHRANDOM_H
3
4#ifdef CONFIG_ARCH_RANDOM
5
6#include <asm/machdep.h>
7
8static inline int arch_get_random_long(unsigned long *v)
9{
10 if (ppc_md.get_random_long)
11 return ppc_md.get_random_long(v);
12
13 return 0;
14}
15
16static inline int arch_get_random_int(unsigned int *v)
17{
18 unsigned long val;
19 int rc;
20
21 rc = arch_get_random_long(&val);
22 if (rc)
23 *v = val;
24
25 return rc;
26}
27
28int powernv_get_random_long(unsigned long *v);
29
30#endif /* CONFIG_ARCH_RANDOM */
31
32#endif /* _ASM_POWERPC_ARCHRANDOM_H */
diff --git a/arch/powerpc/include/asm/machdep.h b/arch/powerpc/include/asm/machdep.h
index 8b480901165a..ce6cc2a7b8b9 100644
--- a/arch/powerpc/include/asm/machdep.h
+++ b/arch/powerpc/include/asm/machdep.h
@@ -263,6 +263,10 @@ struct machdep_calls {
263 ssize_t (*cpu_probe)(const char *, size_t); 263 ssize_t (*cpu_probe)(const char *, size_t);
264 ssize_t (*cpu_release)(const char *, size_t); 264 ssize_t (*cpu_release)(const char *, size_t);
265#endif 265#endif
266
267#ifdef CONFIG_ARCH_RANDOM
268 int (*get_random_long)(unsigned long *v);
269#endif
266}; 270};
267 271
268extern void e500_idle(void); 272extern void e500_idle(void);
diff --git a/arch/powerpc/platforms/powernv/Kconfig b/arch/powerpc/platforms/powernv/Kconfig
index 6fae5eb99ea6..21084645c70a 100644
--- a/arch/powerpc/platforms/powernv/Kconfig
+++ b/arch/powerpc/platforms/powernv/Kconfig
@@ -9,6 +9,7 @@ config PPC_POWERNV
9 select EPAPR_BOOT 9 select EPAPR_BOOT
10 select PPC_INDIRECT_PIO 10 select PPC_INDIRECT_PIO
11 select PPC_UDBG_16550 11 select PPC_UDBG_16550
12 select ARCH_RANDOM
12 default y 13 default y
13 14
14config POWERNV_MSI 15config POWERNV_MSI
diff --git a/arch/powerpc/platforms/powernv/Makefile b/arch/powerpc/platforms/powernv/Makefile
index 300c437d713c..6760a86d5bd0 100644
--- a/arch/powerpc/platforms/powernv/Makefile
+++ b/arch/powerpc/platforms/powernv/Makefile
@@ -1,5 +1,5 @@
1obj-y += setup.o opal-takeover.o opal-wrappers.o opal.o 1obj-y += setup.o opal-takeover.o opal-wrappers.o opal.o
2obj-y += opal-rtc.o opal-nvram.o opal-lpc.o 2obj-y += opal-rtc.o opal-nvram.o opal-lpc.o rng.o
3 3
4obj-$(CONFIG_SMP) += smp.o 4obj-$(CONFIG_SMP) += smp.o
5obj-$(CONFIG_PCI) += pci.o pci-p5ioc2.o pci-ioda.o 5obj-$(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 000000000000..02db7d73cf8b
--- /dev/null
+++ b/arch/powerpc/platforms/powernv/rng.c
@@ -0,0 +1,122 @@
1/*
2 * Copyright 2013, Michael Ellerman, IBM 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
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 */
9
10#define pr_fmt(fmt) "powernv-rng: " fmt
11
12#include <linux/kernel.h>
13#include <linux/of.h>
14#include <linux/of_platform.h>
15#include <linux/slab.h>
16#include <asm/archrandom.h>
17#include <asm/io.h>
18#include <asm/machdep.h>
19
20
21struct powernv_rng {
22 void __iomem *regs;
23 unsigned long mask;
24};
25
26static DEFINE_PER_CPU(struct powernv_rng *, powernv_rng);
27
28
29static unsigned long rng_whiten(struct powernv_rng *rng, unsigned long val)
30{
31 unsigned long parity;
32
33 /* Calculate the parity of the value */
34 asm ("popcntd %0,%1" : "=r" (parity) : "r" (val));
35
36 /* xor our value with the previous mask */
37 val ^= rng->mask;
38
39 /* update the mask based on the parity of this value */
40 rng->mask = (rng->mask << 1) | (parity & 1);
41
42 return val;
43}
44
45int powernv_get_random_long(unsigned long *v)
46{
47 struct powernv_rng *rng;
48
49 rng = get_cpu_var(powernv_rng);
50
51 *v = rng_whiten(rng, in_be64(rng->regs));
52
53 put_cpu_var(rng);
54
55 return 1;
56}
57EXPORT_SYMBOL_GPL(powernv_get_random_long);
58
59static __init void rng_init_per_cpu(struct powernv_rng *rng,
60 struct device_node *dn)
61{
62 int chip_id, cpu;
63
64 chip_id = of_get_ibm_chip_id(dn);
65 if (chip_id == -1)
66 pr_warn("No ibm,chip-id found for %s.\n", dn->full_name);
67
68 for_each_possible_cpu(cpu) {
69 if (per_cpu(powernv_rng, cpu) == NULL ||
70 cpu_to_chip_id(cpu) == chip_id) {
71 per_cpu(powernv_rng, cpu) = rng;
72 }
73 }
74}
75
76static __init int rng_create(struct device_node *dn)
77{
78 struct powernv_rng *rng;
79 unsigned long val;
80
81 rng = kzalloc(sizeof(*rng), GFP_KERNEL);
82 if (!rng)
83 return -ENOMEM;
84
85 rng->regs = of_iomap(dn, 0);
86 if (!rng->regs) {
87 kfree(rng);
88 return -ENXIO;
89 }
90
91 val = in_be64(rng->regs);
92 rng->mask = val;
93
94 rng_init_per_cpu(rng, dn);
95
96 pr_info_once("Registering arch random hook.\n");
97
98 ppc_md.get_random_long = powernv_get_random_long;
99
100 return 0;
101}
102
103static __init int rng_init(void)
104{
105 struct device_node *dn;
106 int rc;
107
108 for_each_compatible_node(dn, NULL, "ibm,power-rng") {
109 rc = rng_create(dn);
110 if (rc) {
111 pr_err("Failed creating rng for %s (%d).\n",
112 dn->full_name, rc);
113 continue;
114 }
115
116 /* Create devices for hwrng driver */
117 of_platform_device_create(dn, NULL, NULL);
118 }
119
120 return 0;
121}
122subsys_initcall(rng_init);