xref: /openbmc/linux/arch/x86/kernel/cpu/rdrand.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
1a61127c2SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
249d859d7SH. Peter Anvin /*
349d859d7SH. Peter Anvin  * This file is part of the Linux kernel.
449d859d7SH. Peter Anvin  *
549d859d7SH. Peter Anvin  * Copyright (c) 2011, Intel Corporation
649d859d7SH. Peter Anvin  * Authors: Fenghua Yu <fenghua.yu@intel.com>,
749d859d7SH. Peter Anvin  *          H. Peter Anvin <hpa@linux.intel.com>
849d859d7SH. Peter Anvin  */
949d859d7SH. Peter Anvin 
1049d859d7SH. Peter Anvin #include <asm/processor.h>
1149d859d7SH. Peter Anvin #include <asm/archrandom.h>
1249d859d7SH. Peter Anvin #include <asm/sections.h>
1349d859d7SH. Peter Anvin 
1449d859d7SH. Peter Anvin /*
150007bcccSLen Brown  * RDRAND has Built-In-Self-Test (BIST) that runs on every invocation.
16*049f9ae9SJason A. Donenfeld  * Run the instruction a few times as a sanity check. Also make sure
17*049f9ae9SJason A. Donenfeld  * it's not outputting the same value over and over, which has happened
18*049f9ae9SJason A. Donenfeld  * as a result of past CPU bugs.
19*049f9ae9SJason A. Donenfeld  *
20*049f9ae9SJason A. Donenfeld  * If it fails, it is simple to disable RDRAND and RDSEED here.
2149d859d7SH. Peter Anvin  */
2249d859d7SH. Peter Anvin 
x86_init_rdrand(struct cpuinfo_x86 * c)23148f9bb8SPaul Gortmaker void x86_init_rdrand(struct cpuinfo_x86 *c)
2449d859d7SH. Peter Anvin {
25*049f9ae9SJason A. Donenfeld 	enum { SAMPLES = 8, MIN_CHANGE = 5 };
26*049f9ae9SJason A. Donenfeld 	unsigned long sample, prev;
27*049f9ae9SJason A. Donenfeld 	bool failure = false;
28*049f9ae9SJason A. Donenfeld 	size_t i, changed;
2949d859d7SH. Peter Anvin 
3049d859d7SH. Peter Anvin 	if (!cpu_has(c, X86_FEATURE_RDRAND))
310007bcccSLen Brown 		return;
3249d859d7SH. Peter Anvin 
33*049f9ae9SJason A. Donenfeld 	for (changed = 0, i = 0; i < SAMPLES; ++i) {
34*049f9ae9SJason A. Donenfeld 		if (!rdrand_long(&sample)) {
35*049f9ae9SJason A. Donenfeld 			failure = true;
36*049f9ae9SJason A. Donenfeld 			break;
37*049f9ae9SJason A. Donenfeld 		}
38*049f9ae9SJason A. Donenfeld 		changed += i && sample != prev;
39*049f9ae9SJason A. Donenfeld 		prev = sample;
40*049f9ae9SJason A. Donenfeld 	}
41*049f9ae9SJason A. Donenfeld 	if (changed < MIN_CHANGE)
42*049f9ae9SJason A. Donenfeld 		failure = true;
43*049f9ae9SJason A. Donenfeld 
44*049f9ae9SJason A. Donenfeld 	if (failure) {
4549d859d7SH. Peter Anvin 		clear_cpu_cap(c, X86_FEATURE_RDRAND);
46*049f9ae9SJason A. Donenfeld 		clear_cpu_cap(c, X86_FEATURE_RDSEED);
47*049f9ae9SJason A. Donenfeld 		pr_emerg("RDRAND is not reliable on this platform; disabling.\n");
480007bcccSLen Brown 	}
490007bcccSLen Brown }
50