x86/bugs: Separate AMD E400 erratum and C1E bug
authorThomas Gleixner <tglx@linutronix.de>
Fri, 9 Dec 2016 18:29:09 +0000 (19:29 +0100)
committerThomas Gleixner <tglx@linutronix.de>
Fri, 9 Dec 2016 20:23:20 +0000 (21:23 +0100)
The workaround for the AMD Erratum E400 (Local APIC timer stops in C1E
state) is a two step process:

 - Selection of the E400 aware idle routine

 - Detection whether the platform is affected

The idle routine selection happens for possibly affected CPUs depending on
family/model/stepping information. These range of CPUs is not necessarily
affected as the decision whether to enable the C1E feature is made by the
firmware. Unfortunately there is no way to query this at early boot.

The current implementation polls a MSR in the E400 aware idle routine to
detect whether the CPU is affected. This is inefficient on non affected
CPUs because every idle entry has to do the MSR read.

There is a better way to detect this before going idle for the first time
which requires to seperate the bug flags:

  X86_BUG_AMD_E400  - Selects the E400 aware idle routine and
     enables the detection

  X86_BUG_AMD_APIC_C1E  - Set when the platform is affected by E400

Replace the current X86_BUG_AMD_APIC_C1E usage by the new X86_BUG_AMD_E400
bug bit to select the idle routine which currently does an unconditional
detection poll. X86_BUG_AMD_APIC_C1E is going to be used in later patches
to remove the MSR polling and simplify the handling of this misfeature.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Borislav Petkov <bp@suse.de>
Cc: Jiri Olsa <jolsa@redhat.com>
Link: http://lkml.kernel.org/r/20161209182912.2726-3-bp@alien8.de
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
arch/x86/include/asm/cpufeatures.h
arch/x86/kernel/cpu/amd.c
arch/x86/kernel/process.c

index a39629206864e5bb74aaddea15ca1ab762877042..ed10b5bf9b93d73b3102263c7b025af6fa9b47e9 100644 (file)
 #define X86_BUG_NULL_SEG       X86_BUG(10) /* Nulling a selector preserves the base */
 #define X86_BUG_SWAPGS_FENCE   X86_BUG(11) /* SWAPGS without input dep on GS */
 #define X86_BUG_MONITOR                X86_BUG(12) /* IPI required to wake up remote CPU */
+#define X86_BUG_AMD_E400       X86_BUG(13) /* CPU is among the affected by Erratum 400 */
+
 #endif /* _ASM_X86_CPUFEATURES_H */
index b81fe2d63e15751c2cb7e61fd10dc85cc7f906b0..ae26c282f5d35a081fb7c06fd74a6de3861c02b0 100644 (file)
 
 #include "cpu.h"
 
+static const int amd_erratum_383[];
+static const int amd_erratum_400[];
+static bool cpu_has_amd_erratum(struct cpuinfo_x86 *cpu, const int *erratum);
+
 /*
  * nodes_per_socket: Stores the number of nodes per socket.
  * Refer to Fam15h Models 00-0fh BKDG - CPUID Fn8000_001E_ECX
@@ -589,11 +593,16 @@ static void early_init_amd(struct cpuinfo_x86 *c)
        /* F16h erratum 793, CVE-2013-6885 */
        if (c->x86 == 0x16 && c->x86_model <= 0xf)
                msr_set_bit(MSR_AMD64_LS_CFG, 15);
-}
 
-static const int amd_erratum_383[];
-static const int amd_erratum_400[];
-static bool cpu_has_amd_erratum(struct cpuinfo_x86 *cpu, const int *erratum);
+       /*
+        * Check whether the machine is affected by erratum 400. This is
+        * used to select the proper idle routine and to enable the check
+        * whether the machine is affected in arch_post_acpi_init(), which
+        * sets the X86_BUG_AMD_APIC_C1E bug depending on the MSR check.
+        */
+       if (cpu_has_amd_erratum(c, amd_erratum_400))
+               set_cpu_bug(c, X86_BUG_AMD_E400);
+}
 
 static void init_amd_k8(struct cpuinfo_x86 *c)
 {
@@ -774,9 +783,6 @@ static void init_amd(struct cpuinfo_x86 *c)
        if (c->x86 > 0x11)
                set_cpu_cap(c, X86_FEATURE_ARAT);
 
-       if (cpu_has_amd_erratum(c, amd_erratum_400))
-               set_cpu_bug(c, X86_BUG_AMD_APIC_C1E);
-
        rdmsr_safe(MSR_AMD64_PATCH_LEVEL, &c->microcode, &dummy);
 
        /* 3DNow or LM implies PREFETCHW */
index ee023919e4766beae93979da69e7145a10750a5c..29bbbce4f2fdb6b55417d424db49a1f8ced5b413 100644 (file)
@@ -401,8 +401,7 @@ void select_idle_routine(const struct cpuinfo_x86 *c)
        if (x86_idle || boot_option_idle_override == IDLE_POLL)
                return;
 
-       if (cpu_has_bug(c, X86_BUG_AMD_APIC_C1E)) {
-               /* E400: APIC timer interrupt does not wake up CPU from C1e */
+       if (boot_cpu_has_bug(X86_BUG_AMD_E400)) {
                pr_info("using AMD E400 aware idle routine\n");
                x86_idle = amd_e400_idle;
        } else if (prefer_mwait_c1_over_halt(c)) {