cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

rng.c (4286B)


      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
     24struct powernv_rng {
     25	void __iomem *regs;
     26	void __iomem *regs_real;
     27	unsigned long mask;
     28};
     29
     30static DEFINE_PER_CPU(struct powernv_rng *, powernv_rng);
     31
     32int 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
     41static 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 (".machine push;   \
     47	      .machine power7; \
     48	      popcntd %0,%1;   \
     49	      .machine pop;"
     50	     : "=r" (parity) : "r" (val));
     51
     52	/* xor our value with the previous mask */
     53	val ^= rng->mask;
     54
     55	/* update the mask based on the parity of this value */
     56	rng->mask = (rng->mask << 1) | (parity & 1);
     57
     58	return val;
     59}
     60
     61int powernv_get_random_real_mode(unsigned long *v)
     62{
     63	struct powernv_rng *rng;
     64
     65	rng = raw_cpu_read(powernv_rng);
     66
     67	*v = rng_whiten(rng, __raw_rm_readq(rng->regs_real));
     68
     69	return 1;
     70}
     71
     72static int powernv_get_random_darn(unsigned long *v)
     73{
     74	unsigned long val;
     75
     76	/* Using DARN with L=1 - 64-bit conditioned random number */
     77	asm volatile(PPC_DARN(%0, 1) : "=r"(val));
     78
     79	if (val == DARN_ERR)
     80		return 0;
     81
     82	*v = val;
     83
     84	return 1;
     85}
     86
     87static int __init initialise_darn(void)
     88{
     89	unsigned long val;
     90	int i;
     91
     92	if (!cpu_has_feature(CPU_FTR_ARCH_300))
     93		return -ENODEV;
     94
     95	for (i = 0; i < 10; i++) {
     96		if (powernv_get_random_darn(&val)) {
     97			ppc_md.get_random_seed = powernv_get_random_darn;
     98			return 0;
     99		}
    100	}
    101	return -EIO;
    102}
    103
    104int powernv_get_random_long(unsigned long *v)
    105{
    106	struct powernv_rng *rng;
    107
    108	rng = get_cpu_var(powernv_rng);
    109
    110	*v = rng_whiten(rng, in_be64(rng->regs));
    111
    112	put_cpu_var(rng);
    113
    114	return 1;
    115}
    116EXPORT_SYMBOL_GPL(powernv_get_random_long);
    117
    118static __init void rng_init_per_cpu(struct powernv_rng *rng,
    119				    struct device_node *dn)
    120{
    121	int chip_id, cpu;
    122
    123	chip_id = of_get_ibm_chip_id(dn);
    124	if (chip_id == -1)
    125		pr_warn("No ibm,chip-id found for %pOF.\n", dn);
    126
    127	for_each_possible_cpu(cpu) {
    128		if (per_cpu(powernv_rng, cpu) == NULL ||
    129		    cpu_to_chip_id(cpu) == chip_id) {
    130			per_cpu(powernv_rng, cpu) = rng;
    131		}
    132	}
    133}
    134
    135static __init int rng_create(struct device_node *dn)
    136{
    137	struct powernv_rng *rng;
    138	struct resource res;
    139	unsigned long val;
    140
    141	rng = kzalloc(sizeof(*rng), GFP_KERNEL);
    142	if (!rng)
    143		return -ENOMEM;
    144
    145	if (of_address_to_resource(dn, 0, &res)) {
    146		kfree(rng);
    147		return -ENXIO;
    148	}
    149
    150	rng->regs_real = (void __iomem *)res.start;
    151
    152	rng->regs = of_iomap(dn, 0);
    153	if (!rng->regs) {
    154		kfree(rng);
    155		return -ENXIO;
    156	}
    157
    158	val = in_be64(rng->regs);
    159	rng->mask = val;
    160
    161	rng_init_per_cpu(rng, dn);
    162
    163	ppc_md.get_random_seed = powernv_get_random_long;
    164
    165	return 0;
    166}
    167
    168static int __init pnv_get_random_long_early(unsigned long *v)
    169{
    170	struct device_node *dn;
    171
    172	if (!slab_is_available())
    173		return 0;
    174
    175	if (cmpxchg(&ppc_md.get_random_seed, pnv_get_random_long_early,
    176		    NULL) != pnv_get_random_long_early)
    177		return 0;
    178
    179	for_each_compatible_node(dn, NULL, "ibm,power-rng")
    180		rng_create(dn);
    181
    182	if (!ppc_md.get_random_seed)
    183		return 0;
    184	return ppc_md.get_random_seed(v);
    185}
    186
    187void __init pnv_rng_init(void)
    188{
    189	struct device_node *dn;
    190
    191	/* Prefer darn over the rest. */
    192	if (!initialise_darn())
    193		return;
    194
    195	dn = of_find_compatible_node(NULL, NULL, "ibm,power-rng");
    196	if (dn)
    197		ppc_md.get_random_seed = pnv_get_random_long_early;
    198
    199	of_node_put(dn);
    200}
    201
    202static int __init pnv_rng_late_init(void)
    203{
    204	struct device_node *dn;
    205	unsigned long v;
    206
    207	/* In case it wasn't called during init for some other reason. */
    208	if (ppc_md.get_random_seed == pnv_get_random_long_early)
    209		pnv_get_random_long_early(&v);
    210
    211	if (ppc_md.get_random_seed == powernv_get_random_long) {
    212		for_each_compatible_node(dn, NULL, "ibm,power-rng")
    213			of_platform_device_create(dn, NULL, NULL);
    214	}
    215
    216	return 0;
    217}
    218machine_subsys_initcall(powernv, pnv_rng_late_init);