Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Copyright 2013, Michael Ellerman, IBM Corporation.
0004  */
0005 
0006 #define pr_fmt(fmt) "powernv-rng: " fmt
0007 
0008 #include <linux/kernel.h>
0009 #include <linux/of.h>
0010 #include <linux/of_address.h>
0011 #include <linux/of_platform.h>
0012 #include <linux/slab.h>
0013 #include <linux/smp.h>
0014 #include <asm/archrandom.h>
0015 #include <asm/cputable.h>
0016 #include <asm/io.h>
0017 #include <asm/prom.h>
0018 #include <asm/machdep.h>
0019 #include <asm/smp.h>
0020 #include "powernv.h"
0021 
0022 #define DARN_ERR 0xFFFFFFFFFFFFFFFFul
0023 
0024 struct pnv_rng {
0025     void __iomem *regs;
0026     void __iomem *regs_real;
0027     unsigned long mask;
0028 };
0029 
0030 static DEFINE_PER_CPU(struct pnv_rng *, pnv_rng);
0031 
0032 static unsigned long rng_whiten(struct pnv_rng *rng, unsigned long val)
0033 {
0034     unsigned long parity;
0035 
0036     /* Calculate the parity of the value */
0037     asm (".machine push;   \
0038           .machine power7; \
0039           popcntd %0,%1;   \
0040           .machine pop;"
0041          : "=r" (parity) : "r" (val));
0042 
0043     /* xor our value with the previous mask */
0044     val ^= rng->mask;
0045 
0046     /* update the mask based on the parity of this value */
0047     rng->mask = (rng->mask << 1) | (parity & 1);
0048 
0049     return val;
0050 }
0051 
0052 static int pnv_get_random_darn(unsigned long *v)
0053 {
0054     unsigned long val;
0055 
0056     /* Using DARN with L=1 - 64-bit conditioned random number */
0057     asm volatile(PPC_DARN(%0, 1) : "=r"(val));
0058 
0059     if (val == DARN_ERR)
0060         return 0;
0061 
0062     *v = val;
0063 
0064     return 1;
0065 }
0066 
0067 static int __init initialise_darn(void)
0068 {
0069     unsigned long val;
0070     int i;
0071 
0072     if (!cpu_has_feature(CPU_FTR_ARCH_300))
0073         return -ENODEV;
0074 
0075     for (i = 0; i < 10; i++) {
0076         if (pnv_get_random_darn(&val)) {
0077             ppc_md.get_random_seed = pnv_get_random_darn;
0078             return 0;
0079         }
0080     }
0081     return -EIO;
0082 }
0083 
0084 int pnv_get_random_long(unsigned long *v)
0085 {
0086     struct pnv_rng *rng;
0087 
0088     if (mfmsr() & MSR_DR) {
0089         rng = get_cpu_var(pnv_rng);
0090         *v = rng_whiten(rng, in_be64(rng->regs));
0091         put_cpu_var(rng);
0092     } else {
0093         rng = raw_cpu_read(pnv_rng);
0094         *v = rng_whiten(rng, __raw_rm_readq(rng->regs_real));
0095     }
0096     return 1;
0097 }
0098 EXPORT_SYMBOL_GPL(pnv_get_random_long);
0099 
0100 static __init void rng_init_per_cpu(struct pnv_rng *rng,
0101                     struct device_node *dn)
0102 {
0103     int chip_id, cpu;
0104 
0105     chip_id = of_get_ibm_chip_id(dn);
0106     if (chip_id == -1)
0107         pr_warn("No ibm,chip-id found for %pOF.\n", dn);
0108 
0109     for_each_possible_cpu(cpu) {
0110         if (per_cpu(pnv_rng, cpu) == NULL ||
0111             cpu_to_chip_id(cpu) == chip_id) {
0112             per_cpu(pnv_rng, cpu) = rng;
0113         }
0114     }
0115 }
0116 
0117 static __init int rng_create(struct device_node *dn)
0118 {
0119     struct pnv_rng *rng;
0120     struct resource res;
0121     unsigned long val;
0122 
0123     rng = kzalloc(sizeof(*rng), GFP_KERNEL);
0124     if (!rng)
0125         return -ENOMEM;
0126 
0127     if (of_address_to_resource(dn, 0, &res)) {
0128         kfree(rng);
0129         return -ENXIO;
0130     }
0131 
0132     rng->regs_real = (void __iomem *)res.start;
0133 
0134     rng->regs = of_iomap(dn, 0);
0135     if (!rng->regs) {
0136         kfree(rng);
0137         return -ENXIO;
0138     }
0139 
0140     val = in_be64(rng->regs);
0141     rng->mask = val;
0142 
0143     rng_init_per_cpu(rng, dn);
0144 
0145     ppc_md.get_random_seed = pnv_get_random_long;
0146 
0147     return 0;
0148 }
0149 
0150 static int __init pnv_get_random_long_early(unsigned long *v)
0151 {
0152     struct device_node *dn;
0153 
0154     if (!slab_is_available())
0155         return 0;
0156 
0157     if (cmpxchg(&ppc_md.get_random_seed, pnv_get_random_long_early,
0158             NULL) != pnv_get_random_long_early)
0159         return 0;
0160 
0161     for_each_compatible_node(dn, NULL, "ibm,power-rng")
0162         rng_create(dn);
0163 
0164     if (!ppc_md.get_random_seed)
0165         return 0;
0166     return ppc_md.get_random_seed(v);
0167 }
0168 
0169 void __init pnv_rng_init(void)
0170 {
0171     struct device_node *dn;
0172 
0173     /* Prefer darn over the rest. */
0174     if (!initialise_darn())
0175         return;
0176 
0177     dn = of_find_compatible_node(NULL, NULL, "ibm,power-rng");
0178     if (dn)
0179         ppc_md.get_random_seed = pnv_get_random_long_early;
0180 
0181     of_node_put(dn);
0182 }
0183 
0184 static int __init pnv_rng_late_init(void)
0185 {
0186     struct device_node *dn;
0187     unsigned long v;
0188 
0189     /* In case it wasn't called during init for some other reason. */
0190     if (ppc_md.get_random_seed == pnv_get_random_long_early)
0191         pnv_get_random_long_early(&v);
0192 
0193     if (ppc_md.get_random_seed == pnv_get_random_long) {
0194         for_each_compatible_node(dn, NULL, "ibm,power-rng")
0195             of_platform_device_create(dn, NULL, NULL);
0196     }
0197 
0198     return 0;
0199 }
0200 machine_subsys_initcall(powernv, pnv_rng_late_init);