.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * 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 | 4 | */ |
---|
9 | 5 | |
---|
10 | 6 | #define pr_fmt(fmt) "powernv-rng: " fmt |
---|
.. | .. |
---|
21 | 17 | #include <asm/prom.h> |
---|
22 | 18 | #include <asm/machdep.h> |
---|
23 | 19 | #include <asm/smp.h> |
---|
| 20 | +#include "powernv.h" |
---|
24 | 21 | |
---|
25 | 22 | #define DARN_ERR 0xFFFFFFFFFFFFFFFFul |
---|
26 | 23 | |
---|
.. | .. |
---|
31 | 28 | }; |
---|
32 | 29 | |
---|
33 | 30 | static DEFINE_PER_CPU(struct powernv_rng *, powernv_rng); |
---|
34 | | - |
---|
35 | 31 | |
---|
36 | 32 | int powernv_hwrng_present(void) |
---|
37 | 33 | { |
---|
.. | .. |
---|
47 | 43 | unsigned long parity; |
---|
48 | 44 | |
---|
49 | 45 | /* Calculate the parity of the value */ |
---|
50 | | - asm ("popcntd %0,%1" : "=r" (parity) : "r" (val)); |
---|
| 46 | + asm (".machine push; \ |
---|
| 47 | + .machine power7; \ |
---|
| 48 | + popcntd %0,%1; \ |
---|
| 49 | + .machine pop;" |
---|
| 50 | + : "=r" (parity) : "r" (val)); |
---|
51 | 51 | |
---|
52 | 52 | /* xor our value with the previous mask */ |
---|
53 | 53 | val ^= rng->mask; |
---|
.. | .. |
---|
63 | 63 | struct powernv_rng *rng; |
---|
64 | 64 | |
---|
65 | 65 | rng = raw_cpu_read(powernv_rng); |
---|
| 66 | + if (!rng) |
---|
| 67 | + return 0; |
---|
66 | 68 | |
---|
67 | 69 | *v = rng_whiten(rng, __raw_rm_readq(rng->regs_real)); |
---|
68 | 70 | |
---|
69 | 71 | return 1; |
---|
70 | 72 | } |
---|
71 | 73 | |
---|
72 | | -int powernv_get_random_darn(unsigned long *v) |
---|
| 74 | +static int powernv_get_random_darn(unsigned long *v) |
---|
73 | 75 | { |
---|
74 | 76 | unsigned long val; |
---|
75 | 77 | |
---|
.. | .. |
---|
98 | 100 | return 0; |
---|
99 | 101 | } |
---|
100 | 102 | } |
---|
101 | | - |
---|
102 | | - pr_warn("Unable to use DARN for get_random_seed()\n"); |
---|
103 | | - |
---|
104 | 103 | return -EIO; |
---|
105 | 104 | } |
---|
106 | 105 | |
---|
.. | .. |
---|
163 | 162 | |
---|
164 | 163 | rng_init_per_cpu(rng, dn); |
---|
165 | 164 | |
---|
166 | | - pr_info_once("Registering arch random hook.\n"); |
---|
167 | | - |
---|
168 | 165 | ppc_md.get_random_seed = powernv_get_random_long; |
---|
169 | 166 | |
---|
170 | 167 | return 0; |
---|
171 | 168 | } |
---|
172 | 169 | |
---|
173 | | -static __init int rng_init(void) |
---|
| 170 | +static int __init pnv_get_random_long_early(unsigned long *v) |
---|
174 | 171 | { |
---|
175 | 172 | struct device_node *dn; |
---|
176 | | - int rc; |
---|
177 | 173 | |
---|
178 | | - for_each_compatible_node(dn, NULL, "ibm,power-rng") { |
---|
179 | | - rc = rng_create(dn); |
---|
180 | | - if (rc) { |
---|
181 | | - pr_err("Failed creating rng for %pOF (%d).\n", |
---|
182 | | - dn, rc); |
---|
183 | | - continue; |
---|
184 | | - } |
---|
| 174 | + if (!slab_is_available()) |
---|
| 175 | + return 0; |
---|
185 | 176 | |
---|
186 | | - /* Create devices for hwrng driver */ |
---|
187 | | - of_platform_device_create(dn, NULL, NULL); |
---|
| 177 | + if (cmpxchg(&ppc_md.get_random_seed, pnv_get_random_long_early, |
---|
| 178 | + NULL) != pnv_get_random_long_early) |
---|
| 179 | + return 0; |
---|
| 180 | + |
---|
| 181 | + for_each_compatible_node(dn, NULL, "ibm,power-rng") |
---|
| 182 | + rng_create(dn); |
---|
| 183 | + |
---|
| 184 | + if (!ppc_md.get_random_seed) |
---|
| 185 | + return 0; |
---|
| 186 | + return ppc_md.get_random_seed(v); |
---|
| 187 | +} |
---|
| 188 | + |
---|
| 189 | +void __init pnv_rng_init(void) |
---|
| 190 | +{ |
---|
| 191 | + struct device_node *dn; |
---|
| 192 | + |
---|
| 193 | + /* Prefer darn over the rest. */ |
---|
| 194 | + if (!initialise_darn()) |
---|
| 195 | + return; |
---|
| 196 | + |
---|
| 197 | + dn = of_find_compatible_node(NULL, NULL, "ibm,power-rng"); |
---|
| 198 | + if (dn) |
---|
| 199 | + ppc_md.get_random_seed = pnv_get_random_long_early; |
---|
| 200 | + |
---|
| 201 | + of_node_put(dn); |
---|
| 202 | +} |
---|
| 203 | + |
---|
| 204 | +static int __init pnv_rng_late_init(void) |
---|
| 205 | +{ |
---|
| 206 | + struct device_node *dn; |
---|
| 207 | + unsigned long v; |
---|
| 208 | + |
---|
| 209 | + /* In case it wasn't called during init for some other reason. */ |
---|
| 210 | + if (ppc_md.get_random_seed == pnv_get_random_long_early) |
---|
| 211 | + pnv_get_random_long_early(&v); |
---|
| 212 | + |
---|
| 213 | + if (ppc_md.get_random_seed == powernv_get_random_long) { |
---|
| 214 | + for_each_compatible_node(dn, NULL, "ibm,power-rng") |
---|
| 215 | + of_platform_device_create(dn, NULL, NULL); |
---|
188 | 216 | } |
---|
189 | | - |
---|
190 | | - initialise_darn(); |
---|
191 | 217 | |
---|
192 | 218 | return 0; |
---|
193 | 219 | } |
---|
194 | | -machine_subsys_initcall(powernv, rng_init); |
---|
| 220 | +machine_subsys_initcall(powernv, pnv_rng_late_init); |
---|