| .. | .. |
|---|
| 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); |
|---|