| // SPDX-License-Identifier: GPL-2.0-only |
| /* |
| * This file is part of the Linux kernel. |
| * |
| * Copyright (c) 2011, Intel Corporation |
| * Authors: Fenghua Yu <fenghua.yu@intel.com>, |
| * H. Peter Anvin <hpa@linux.intel.com> |
| */ |
| #include <linux/printk.h> |
| |
| #include <asm/processor.h> |
| #include <asm/archrandom.h> |
| #include <asm/sections.h> |
| |
| /* |
| * RDRAND has Built-In-Self-Test (BIST) that runs on every invocation. |
| * Run the instruction a few times as a sanity check. Also make sure |
| * it's not outputting the same value over and over, which has happened |
| * as a result of past CPU bugs. |
| * |
| * If it fails, it is simple to disable RDRAND and RDSEED here. |
| */ |
| |
| void x86_init_rdrand(struct cpuinfo_x86 *c) |
| { |
| enum { SAMPLES = 8, MIN_CHANGE = 5 }; |
| unsigned long sample, prev; |
| bool failure = false; |
| size_t i, changed; |
| |
| if (!cpu_has(c, X86_FEATURE_RDRAND)) |
| return; |
| |
| for (changed = 0, i = 0; i < SAMPLES; ++i) { |
| if (!rdrand_long(&sample)) { |
| failure = true; |
| break; |
| } |
| changed += i && sample != prev; |
| prev = sample; |
| } |
| if (changed < MIN_CHANGE) |
| failure = true; |
| |
| if (failure) { |
| clear_cpu_cap(c, X86_FEATURE_RDRAND); |
| clear_cpu_cap(c, X86_FEATURE_RDSEED); |
| pr_emerg("RDRAND is not reliable on this platform; disabling.\n"); |
| } |
| } |