root/arch/powerpc/platforms/powernv/rng.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. powernv_hwrng_present
  2. rng_whiten
  3. powernv_get_random_real_mode
  4. powernv_get_random_darn
  5. initialise_darn
  6. powernv_get_random_long
  7. rng_init_per_cpu
  8. rng_create
  9. rng_init

   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 
  21 #define DARN_ERR 0xFFFFFFFFFFFFFFFFul
  22 
  23 struct powernv_rng {
  24         void __iomem *regs;
  25         void __iomem *regs_real;
  26         unsigned long mask;
  27 };
  28 
  29 static DEFINE_PER_CPU(struct powernv_rng *, powernv_rng);
  30 
  31 
  32 int powernv_hwrng_present(void)
  33 {
  34         struct powernv_rng *rng;
  35 
  36         rng = get_cpu_var(powernv_rng);
  37         put_cpu_var(rng);
  38         return rng != NULL;
  39 }
  40 
  41 static unsigned long rng_whiten(struct powernv_rng *rng, unsigned long val)
  42 {
  43         unsigned long parity;
  44 
  45         /* Calculate the parity of the value */
  46         asm ("popcntd %0,%1" : "=r" (parity) : "r" (val));
  47 
  48         /* xor our value with the previous mask */
  49         val ^= rng->mask;
  50 
  51         /* update the mask based on the parity of this value */
  52         rng->mask = (rng->mask << 1) | (parity & 1);
  53 
  54         return val;
  55 }
  56 
  57 int powernv_get_random_real_mode(unsigned long *v)
  58 {
  59         struct powernv_rng *rng;
  60 
  61         rng = raw_cpu_read(powernv_rng);
  62 
  63         *v = rng_whiten(rng, __raw_rm_readq(rng->regs_real));
  64 
  65         return 1;
  66 }
  67 
  68 int powernv_get_random_darn(unsigned long *v)
  69 {
  70         unsigned long val;
  71 
  72         /* Using DARN with L=1 - 64-bit conditioned random number */
  73         asm volatile(PPC_DARN(%0, 1) : "=r"(val));
  74 
  75         if (val == DARN_ERR)
  76                 return 0;
  77 
  78         *v = val;
  79 
  80         return 1;
  81 }
  82 
  83 static int initialise_darn(void)
  84 {
  85         unsigned long val;
  86         int i;
  87 
  88         if (!cpu_has_feature(CPU_FTR_ARCH_300))
  89                 return -ENODEV;
  90 
  91         for (i = 0; i < 10; i++) {
  92                 if (powernv_get_random_darn(&val)) {
  93                         ppc_md.get_random_seed = powernv_get_random_darn;
  94                         return 0;
  95                 }
  96         }
  97 
  98         pr_warn("Unable to use DARN for get_random_seed()\n");
  99 
 100         return -EIO;
 101 }
 102 
 103 int powernv_get_random_long(unsigned long *v)
 104 {
 105         struct powernv_rng *rng;
 106 
 107         rng = get_cpu_var(powernv_rng);
 108 
 109         *v = rng_whiten(rng, in_be64(rng->regs));
 110 
 111         put_cpu_var(rng);
 112 
 113         return 1;
 114 }
 115 EXPORT_SYMBOL_GPL(powernv_get_random_long);
 116 
 117 static __init void rng_init_per_cpu(struct powernv_rng *rng,
 118                                     struct device_node *dn)
 119 {
 120         int chip_id, cpu;
 121 
 122         chip_id = of_get_ibm_chip_id(dn);
 123         if (chip_id == -1)
 124                 pr_warn("No ibm,chip-id found for %pOF.\n", dn);
 125 
 126         for_each_possible_cpu(cpu) {
 127                 if (per_cpu(powernv_rng, cpu) == NULL ||
 128                     cpu_to_chip_id(cpu) == chip_id) {
 129                         per_cpu(powernv_rng, cpu) = rng;
 130                 }
 131         }
 132 }
 133 
 134 static __init int rng_create(struct device_node *dn)
 135 {
 136         struct powernv_rng *rng;
 137         struct resource res;
 138         unsigned long val;
 139 
 140         rng = kzalloc(sizeof(*rng), GFP_KERNEL);
 141         if (!rng)
 142                 return -ENOMEM;
 143 
 144         if (of_address_to_resource(dn, 0, &res)) {
 145                 kfree(rng);
 146                 return -ENXIO;
 147         }
 148 
 149         rng->regs_real = (void __iomem *)res.start;
 150 
 151         rng->regs = of_iomap(dn, 0);
 152         if (!rng->regs) {
 153                 kfree(rng);
 154                 return -ENXIO;
 155         }
 156 
 157         val = in_be64(rng->regs);
 158         rng->mask = val;
 159 
 160         rng_init_per_cpu(rng, dn);
 161 
 162         pr_info_once("Registering arch random hook.\n");
 163 
 164         ppc_md.get_random_seed = powernv_get_random_long;
 165 
 166         return 0;
 167 }
 168 
 169 static __init int rng_init(void)
 170 {
 171         struct device_node *dn;
 172         int rc;
 173 
 174         for_each_compatible_node(dn, NULL, "ibm,power-rng") {
 175                 rc = rng_create(dn);
 176                 if (rc) {
 177                         pr_err("Failed creating rng for %pOF (%d).\n",
 178                                 dn, rc);
 179                         continue;
 180                 }
 181 
 182                 /* Create devices for hwrng driver */
 183                 of_platform_device_create(dn, NULL, NULL);
 184         }
 185 
 186         initialise_darn();
 187 
 188         return 0;
 189 }
 190 machine_subsys_initcall(powernv, rng_init);

/* [<][>][^][v][top][bottom][index][help] */