linux-stable/arch/x86/kernel/cpu/rdrand.c
Borislav Petkov 7879fc4bdc x86/rdrand: Sanity-check RDRAND output
It turned out recently that on certain AMD F15h and F16h machines, due
to the BIOS dropping the ball after resume, yet again, RDRAND would not
function anymore:

  c49a0a8013 ("x86/CPU/AMD: Clear RDRAND CPUID bit on AMD family 15h/16h")

Add a silly test to the CPU bringup path, to sanity-check the random
data RDRAND returns and scream as loudly as possible if that returned
random data doesn't change.

Suggested-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Borislav Petkov <bp@suse.de>
Cc: Pu Wen <puwen@hygon.cn>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: x86-ml <x86@kernel.org>
Link: https://lkml.kernel.org/r/CAHk-=wjWPDauemCmLTKbdMYFB0UveMszZpcrwoUkJRRWKrqaTw@mail.gmail.com
2019-10-01 19:55:32 +02:00

66 lines
1.4 KiB
C

// 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 <asm/processor.h>
#include <asm/archrandom.h>
#include <asm/sections.h>
static int __init x86_rdrand_setup(char *s)
{
setup_clear_cpu_cap(X86_FEATURE_RDRAND);
setup_clear_cpu_cap(X86_FEATURE_RDSEED);
return 1;
}
__setup("nordrand", x86_rdrand_setup);
/*
* RDRAND has Built-In-Self-Test (BIST) that runs on every invocation.
* Run the instruction a few times as a sanity check.
* If it fails, it is simple to disable RDRAND here.
*/
#define SANITY_CHECK_LOOPS 8
#ifdef CONFIG_ARCH_RANDOM
void x86_init_rdrand(struct cpuinfo_x86 *c)
{
unsigned int changed = 0;
unsigned long tmp, prev;
int i;
if (!cpu_has(c, X86_FEATURE_RDRAND))
return;
for (i = 0; i < SANITY_CHECK_LOOPS; i++) {
if (!rdrand_long(&tmp)) {
clear_cpu_cap(c, X86_FEATURE_RDRAND);
pr_warn_once("rdrand: disabled\n");
return;
}
}
/*
* Stupid sanity-check whether RDRAND does *actually* generate
* some at least random-looking data.
*/
prev = tmp;
for (i = 0; i < SANITY_CHECK_LOOPS; i++) {
if (rdrand_long(&tmp)) {
if (prev != tmp)
changed++;
prev = tmp;
}
}
if (WARN_ON_ONCE(!changed))
pr_emerg(
"RDRAND gives funky smelling output, might consider not using it by booting with \"nordrand\"");
}
#endif