~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/arch/powerpc/platforms/powernv/rng.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0-or-later
  2 /*
  3  * Copyright 2013, Michael Ellerman, IBM Corporation.
  4  */
  5 
  6 #define pr_fmt(fmt)     "powernv-rng: " fmt
  7 
  8 #include <linux/kernel.h>
  9 #include <linux/of.h>
 10 #include <linux/of_address.h>
 11 #include <linux/of_platform.h>
 12 #include <linux/slab.h>
 13 #include <linux/smp.h>
 14 #include <asm/archrandom.h>
 15 #include <asm/cputable.h>
 16 #include <asm/io.h>
 17 #include <asm/prom.h>
 18 #include <asm/machdep.h>
 19 #include <asm/smp.h>
 20 #include "powernv.h"
 21 
 22 #define DARN_ERR 0xFFFFFFFFFFFFFFFFul
 23 
 24 struct pnv_rng {
 25         void __iomem *regs;
 26         void __iomem *regs_real;
 27         unsigned long mask;
 28 };
 29 
 30 static DEFINE_PER_CPU(struct pnv_rng *, pnv_rng);
 31 
 32 static unsigned long rng_whiten(struct pnv_rng *rng, unsigned long val)
 33 {
 34         unsigned long parity;
 35 
 36         /* Calculate the parity of the value */
 37         asm (".machine push;   \
 38               .machine power7; \
 39               popcntd %0,%1;   \
 40               .machine pop;"
 41              : "=r" (parity) : "r" (val));
 42 
 43         /* xor our value with the previous mask */
 44         val ^= rng->mask;
 45 
 46         /* update the mask based on the parity of this value */
 47         rng->mask = (rng->mask << 1) | (parity & 1);
 48 
 49         return val;
 50 }
 51 
 52 static int pnv_get_random_darn(unsigned long *v)
 53 {
 54         unsigned long val;
 55 
 56         /* Using DARN with L=1 - 64-bit conditioned random number */
 57         asm volatile(PPC_DARN(%0, 1) : "=r"(val));
 58 
 59         if (val == DARN_ERR)
 60                 return 0;
 61 
 62         *v = val;
 63 
 64         return 1;
 65 }
 66 
 67 static int __init initialise_darn(void)
 68 {
 69         unsigned long val;
 70         int i;
 71 
 72         if (!cpu_has_feature(CPU_FTR_ARCH_300))
 73                 return -ENODEV;
 74 
 75         for (i = 0; i < 10; i++) {
 76                 if (pnv_get_random_darn(&val)) {
 77                         ppc_md.get_random_seed = pnv_get_random_darn;
 78                         return 0;
 79                 }
 80         }
 81         return -EIO;
 82 }
 83 
 84 int pnv_get_random_long(unsigned long *v)
 85 {
 86         struct pnv_rng *rng;
 87 
 88         if (mfmsr() & MSR_DR) {
 89                 rng = get_cpu_var(pnv_rng);
 90                 *v = rng_whiten(rng, in_be64(rng->regs));
 91                 put_cpu_var(rng);
 92         } else {
 93                 rng = raw_cpu_read(pnv_rng);
 94                 *v = rng_whiten(rng, __raw_rm_readq(rng->regs_real));
 95         }
 96         return 1;
 97 }
 98 EXPORT_SYMBOL_GPL(pnv_get_random_long);
 99 
100 static __init void rng_init_per_cpu(struct pnv_rng *rng,
101                                     struct device_node *dn)
102 {
103         int chip_id, cpu;
104 
105         chip_id = of_get_ibm_chip_id(dn);
106         if (chip_id == -1)
107                 pr_warn("No ibm,chip-id found for %pOF.\n", dn);
108 
109         for_each_possible_cpu(cpu) {
110                 if (per_cpu(pnv_rng, cpu) == NULL ||
111                     cpu_to_chip_id(cpu) == chip_id) {
112                         per_cpu(pnv_rng, cpu) = rng;
113                 }
114         }
115 }
116 
117 static __init int rng_create(struct device_node *dn)
118 {
119         struct pnv_rng *rng;
120         struct resource res;
121         unsigned long val;
122 
123         rng = kzalloc(sizeof(*rng), GFP_KERNEL);
124         if (!rng)
125                 return -ENOMEM;
126 
127         if (of_address_to_resource(dn, 0, &res)) {
128                 kfree(rng);
129                 return -ENXIO;
130         }
131 
132         rng->regs_real = (void __iomem *)res.start;
133 
134         rng->regs = of_iomap(dn, 0);
135         if (!rng->regs) {
136                 kfree(rng);
137                 return -ENXIO;
138         }
139 
140         val = in_be64(rng->regs);
141         rng->mask = val;
142 
143         rng_init_per_cpu(rng, dn);
144 
145         ppc_md.get_random_seed = pnv_get_random_long;
146 
147         return 0;
148 }
149 
150 static int __init pnv_get_random_long_early(unsigned long *v)
151 {
152         struct device_node *dn;
153 
154         if (!slab_is_available())
155                 return 0;
156 
157         if (cmpxchg(&ppc_md.get_random_seed, pnv_get_random_long_early,
158                     NULL) != pnv_get_random_long_early)
159                 return 0;
160 
161         for_each_compatible_node(dn, NULL, "ibm,power-rng")
162                 rng_create(dn);
163 
164         if (!ppc_md.get_random_seed)
165                 return 0;
166         return ppc_md.get_random_seed(v);
167 }
168 
169 void __init pnv_rng_init(void)
170 {
171         struct device_node *dn;
172 
173         /* Prefer darn over the rest. */
174         if (!initialise_darn())
175                 return;
176 
177         dn = of_find_compatible_node(NULL, NULL, "ibm,power-rng");
178         if (dn)
179                 ppc_md.get_random_seed = pnv_get_random_long_early;
180 
181         of_node_put(dn);
182 }
183 
184 static int __init pnv_rng_late_init(void)
185 {
186         struct device_node *dn;
187         unsigned long v;
188 
189         /* In case it wasn't called during init for some other reason. */
190         if (ppc_md.get_random_seed == pnv_get_random_long_early)
191                 pnv_get_random_long_early(&v);
192 
193         if (ppc_md.get_random_seed == pnv_get_random_long) {
194                 for_each_compatible_node(dn, NULL, "ibm,power-rng")
195                         of_platform_device_create(dn, NULL, NULL);
196         }
197 
198         return 0;
199 }
200 machine_subsys_initcall(powernv, pnv_rng_late_init);
201 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php