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