arch/powerpc/platforms/powernv/rng.c

Source file repositories/reference/linux-study-clean/arch/powerpc/platforms/powernv/rng.c

File Facts

System
Linux kernel
Corpus path
arch/powerpc/platforms/powernv/rng.c
Extension
.c
Size
4005 bytes
Lines
201
Domain
Architecture Layer
Bucket
arch/powerpc
Inferred role
Architecture Layer: exported/initcall integration point
Status
integration implementation candidate

Why This File Exists

CPU and platform-specific kernel glue: boot entry, traps, syscall entry, interrupts, page tables, context switch, and low-level barriers.

Dependency Surface

Detected Declarations

Annotated Snippet

struct pnv_rng {
	void __iomem *regs;
	void __iomem *regs_real;
	unsigned long mask;
};

static DEFINE_PER_CPU(struct pnv_rng *, pnv_rng);

static unsigned long rng_whiten(struct pnv_rng *rng, unsigned long val)
{
	unsigned long parity;

	/* Calculate the parity of the value */
	asm (".machine push;   \
	      .machine power7; \
	      popcntd %0,%1;   \
	      .machine pop;"
	     : "=r" (parity) : "r" (val));

	/* xor our value with the previous mask */
	val ^= rng->mask;

	/* update the mask based on the parity of this value */
	rng->mask = (rng->mask << 1) | (parity & 1);

	return val;
}

static int pnv_get_random_darn(unsigned long *v)
{
	unsigned long val;

	/* Using DARN with L=1 - 64-bit conditioned random number */
	asm volatile(PPC_DARN(%0, 1) : "=r"(val));

	if (val == DARN_ERR)
		return 0;

	*v = val;

	return 1;
}

static int __init initialise_darn(void)
{
	unsigned long val;
	int i;

	if (!cpu_has_feature(CPU_FTR_ARCH_300))
		return -ENODEV;

	for (i = 0; i < 10; i++) {
		if (pnv_get_random_darn(&val)) {
			ppc_md.get_random_seed = pnv_get_random_darn;
			return 0;
		}
	}
	return -EIO;
}

int pnv_get_random_long(unsigned long *v)
{
	struct pnv_rng *rng;

	if (mfmsr() & MSR_DR) {
		rng = get_cpu_var(pnv_rng);
		*v = rng_whiten(rng, in_be64(rng->regs));
		put_cpu_var(rng);
	} else {
		rng = raw_cpu_read(pnv_rng);
		*v = rng_whiten(rng, __raw_rm_readq(rng->regs_real));
	}
	return 1;
}
EXPORT_SYMBOL_GPL(pnv_get_random_long);

static __init void rng_init_per_cpu(struct pnv_rng *rng,
				    struct device_node *dn)
{
	int chip_id, cpu;

	chip_id = of_get_ibm_chip_id(dn);
	if (chip_id == -1)
		pr_warn("No ibm,chip-id found for %pOF.\n", dn);

	for_each_possible_cpu(cpu) {
		if (per_cpu(pnv_rng, cpu) == NULL ||
		    cpu_to_chip_id(cpu) == chip_id) {
			per_cpu(pnv_rng, cpu) = rng;
		}

Annotation

Implementation Notes