mirror of
https://github.com/FEX-Emu/linux.git
synced 2025-02-22 05:16:38 +00:00

Linux will have all kinds of sporadic problems on systems that don't have the CPUID instruction unless CONFIG_M486=y. In particular, sync_core() will explode. I believe that these kernels had a better chance of working before commit 05fb3c199bb0 ("x86/boot: Initialize FPU and X86_FEATURE_ALWAYS even if we don't have CPUID"). That commit inadvertently fixed a serious bug: we used to fail to detect the FPU if CPUID wasn't present. Because we also used to forget to set X86_FEATURE_ALWAYS, we end up with no cpu feature bits set at all. This meant that alternative patching didn't do anything and, if paravirt was disabled, we could plausibly finish the entire boot process without calling sync_core(). Rather than trying to work around these issues, just have the kernel fail loudly if it's running on a CPUID-less 486, doesn't have CPUID, and doesn't have CONFIG_M486 set. Reported-by: Matthew Whitehead <tedheadster@gmail.com> Signed-off-by: Andy Lutomirski <luto@kernel.org> Cc: Borislav Petkov <bp@alien8.de> Cc: Brian Gerst <brgerst@gmail.com> Cc: Denys Vlasenko <dvlasenk@redhat.com> Cc: H. Peter Anvin <hpa@zytor.com> Cc: Josh Poimboeuf <jpoimboe@redhat.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Thomas Gleixner <tglx@linutronix.de> Link: http://lkml.kernel.org/r/70eac6639f23df8be5fe03fa1984aedd5d40077a.1479598603.git.luto@kernel.org Signed-off-by: Ingo Molnar <mingo@kernel.org>
108 lines
2.3 KiB
C
108 lines
2.3 KiB
C
/* -*- linux-c -*- ------------------------------------------------------- *
|
|
*
|
|
* Copyright (C) 1991, 1992 Linus Torvalds
|
|
* Copyright 2007-2008 rPath, Inc. - All Rights Reserved
|
|
*
|
|
* This file is part of the Linux kernel, and is made available under
|
|
* the terms of the GNU General Public License version 2.
|
|
*
|
|
* ----------------------------------------------------------------------- */
|
|
|
|
/*
|
|
* arch/x86/boot/cpu.c
|
|
*
|
|
* Check for obligatory CPU features and abort if the features are not
|
|
* present.
|
|
*/
|
|
|
|
#include "boot.h"
|
|
#ifdef CONFIG_X86_FEATURE_NAMES
|
|
#include "cpustr.h"
|
|
#endif
|
|
|
|
static char *cpu_name(int level)
|
|
{
|
|
static char buf[6];
|
|
|
|
if (level == 64) {
|
|
return "x86-64";
|
|
} else {
|
|
if (level == 15)
|
|
level = 6;
|
|
sprintf(buf, "i%d86", level);
|
|
return buf;
|
|
}
|
|
}
|
|
|
|
static void show_cap_strs(u32 *err_flags)
|
|
{
|
|
int i, j;
|
|
#ifdef CONFIG_X86_FEATURE_NAMES
|
|
const unsigned char *msg_strs = (const unsigned char *)x86_cap_strs;
|
|
for (i = 0; i < NCAPINTS; i++) {
|
|
u32 e = err_flags[i];
|
|
for (j = 0; j < 32; j++) {
|
|
if (msg_strs[0] < i ||
|
|
(msg_strs[0] == i && msg_strs[1] < j)) {
|
|
/* Skip to the next string */
|
|
msg_strs += 2;
|
|
while (*msg_strs++)
|
|
;
|
|
}
|
|
if (e & 1) {
|
|
if (msg_strs[0] == i &&
|
|
msg_strs[1] == j &&
|
|
msg_strs[2])
|
|
printf("%s ", msg_strs+2);
|
|
else
|
|
printf("%d:%d ", i, j);
|
|
}
|
|
e >>= 1;
|
|
}
|
|
}
|
|
#else
|
|
for (i = 0; i < NCAPINTS; i++) {
|
|
u32 e = err_flags[i];
|
|
for (j = 0; j < 32; j++) {
|
|
if (e & 1)
|
|
printf("%d:%d ", i, j);
|
|
e >>= 1;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
int validate_cpu(void)
|
|
{
|
|
u32 *err_flags;
|
|
int cpu_level, req_level;
|
|
|
|
check_cpu(&cpu_level, &req_level, &err_flags);
|
|
|
|
if (cpu_level < req_level) {
|
|
printf("This kernel requires an %s CPU, ",
|
|
cpu_name(req_level));
|
|
printf("but only detected an %s CPU.\n",
|
|
cpu_name(cpu_level));
|
|
return -1;
|
|
}
|
|
|
|
if (CONFIG_X86_MINIMUM_CPU_FAMILY <= 4 && !IS_ENABLED(CONFIG_M486) &&
|
|
!has_eflag(X86_EFLAGS_ID)) {
|
|
printf("This kernel requires a CPU with the CPUID instruction. Build with CONFIG_M486=y to run on this CPU.\n");
|
|
return -1;
|
|
}
|
|
|
|
if (err_flags) {
|
|
puts("This kernel requires the following features "
|
|
"not present on the CPU:\n");
|
|
show_cap_strs(err_flags);
|
|
putchar('\n');
|
|
return -1;
|
|
} else if (check_knl_erratum()) {
|
|
return -1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|