These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / arch / x86 / kernel / cpu / mcheck / mce_amd.c
index 55ad9b3..e99b150 100644 (file)
@@ -1,19 +1,13 @@
 /*
- *  (c) 2005-2012 Advanced Micro Devices, Inc.
+ *  (c) 2005-2015 Advanced Micro Devices, Inc.
  *  Your use of this code is subject to the terms and conditions of the
  *  GNU general public license version 2. See "COPYING" or
  *  http://www.gnu.org/licenses/gpl.html
  *
  *  Written by Jacob Shin - AMD, Inc.
- *
  *  Maintained by: Borislav Petkov <bp@alien8.de>
  *
- *  April 2006
- *     - added support for AMD Family 0x10 processors
- *  May 2012
- *     - major scrubbing
- *
- *  All MC4_MISCi registers are shared between multi-cores
+ *  All MC4_MISCi registers are shared between cores on a node.
  */
 #include <linux/interrupt.h>
 #include <linux/notifier.h>
@@ -32,6 +26,7 @@
 #include <asm/idle.h>
 #include <asm/mce.h>
 #include <asm/msr.h>
+#include <asm/trace/irq_vectors.h>
 
 #define NR_BLOCKS         9
 #define THRESHOLD_MAX     0xFFF
 #define MASK_BLKPTR_LO    0xFF000000
 #define MCG_XBLK_ADDR     0xC0000400
 
+/* Deferred error settings */
+#define MSR_CU_DEF_ERR         0xC0000410
+#define MASK_DEF_LVTOFF                0x000000F0
+#define MASK_DEF_INT_TYPE      0x00000006
+#define DEF_LVT_OFF            0x2
+#define DEF_INT_TYPE_APIC      0x2
+
 static const char * const th_names[] = {
        "load_store",
        "insn_fetch",
@@ -60,6 +62,13 @@ static DEFINE_PER_CPU(struct threshold_bank **, threshold_banks);
 static DEFINE_PER_CPU(unsigned char, bank_map);        /* see which banks are on */
 
 static void amd_threshold_interrupt(void);
+static void amd_deferred_error_interrupt(void);
+
+static void default_deferred_error_interrupt(void)
+{
+       pr_err("Unexpected deferred interrupt at vector %x\n", DEFERRED_ERROR_VECTOR);
+}
+void (*deferred_error_int_vector)(void) = default_deferred_error_interrupt;
 
 /*
  * CPU Initialization
@@ -196,7 +205,7 @@ static void mce_threshold_block_init(struct threshold_block *b, int offset)
        threshold_restart_bank(&tr);
 };
 
-static int setup_APIC_mce(int reserved, int new)
+static int setup_APIC_mce_threshold(int reserved, int new)
 {
        if (reserved < 0 && !setup_APIC_eilvt(new, THRESHOLD_APIC_VECTOR,
                                              APIC_EILVT_MSG_FIX, 0))
@@ -205,6 +214,39 @@ static int setup_APIC_mce(int reserved, int new)
        return reserved;
 }
 
+static int setup_APIC_deferred_error(int reserved, int new)
+{
+       if (reserved < 0 && !setup_APIC_eilvt(new, DEFERRED_ERROR_VECTOR,
+                                             APIC_EILVT_MSG_FIX, 0))
+               return new;
+
+       return reserved;
+}
+
+static void deferred_error_interrupt_enable(struct cpuinfo_x86 *c)
+{
+       u32 low = 0, high = 0;
+       int def_offset = -1, def_new;
+
+       if (rdmsr_safe(MSR_CU_DEF_ERR, &low, &high))
+               return;
+
+       def_new = (low & MASK_DEF_LVTOFF) >> 4;
+       if (!(low & MASK_DEF_LVTOFF)) {
+               pr_err(FW_BUG "Your BIOS is not setting up LVT offset 0x2 for deferred error IRQs correctly.\n");
+               def_new = DEF_LVT_OFF;
+               low = (low & ~MASK_DEF_LVTOFF) | (DEF_LVT_OFF << 4);
+       }
+
+       def_offset = setup_APIC_deferred_error(def_offset, def_new);
+       if ((def_offset == def_new) &&
+           (deferred_error_int_vector != amd_deferred_error_interrupt))
+               deferred_error_int_vector = amd_deferred_error_interrupt;
+
+       low = (low & ~MASK_DEF_INT_TYPE) | DEF_INT_TYPE_APIC;
+       wrmsr(MSR_CU_DEF_ERR, low, high);
+}
+
 /* cpu init entry point, called from mce.c with preempt off */
 void mce_amd_feature_init(struct cpuinfo_x86 *c)
 {
@@ -252,7 +294,7 @@ void mce_amd_feature_init(struct cpuinfo_x86 *c)
 
                        b.interrupt_enable = 1;
                        new     = (high & MASK_LVTOFF_HI) >> 20;
-                       offset  = setup_APIC_mce(offset, new);
+                       offset  = setup_APIC_mce_threshold(offset, new);
 
                        if ((offset == new) &&
                            (mce_threshold_vector != amd_threshold_interrupt))
@@ -262,6 +304,73 @@ init:
                        mce_threshold_block_init(&b, offset);
                }
        }
+
+       if (mce_flags.succor)
+               deferred_error_interrupt_enable(c);
+}
+
+static void __log_error(unsigned int bank, bool threshold_err, u64 misc)
+{
+       struct mce m;
+       u64 status;
+
+       rdmsrl(MSR_IA32_MCx_STATUS(bank), status);
+       if (!(status & MCI_STATUS_VAL))
+               return;
+
+       mce_setup(&m);
+
+       m.status = status;
+       m.bank = bank;
+
+       if (threshold_err)
+               m.misc = misc;
+
+       if (m.status & MCI_STATUS_ADDRV)
+               rdmsrl(MSR_IA32_MCx_ADDR(bank), m.addr);
+
+       mce_log(&m);
+       wrmsrl(MSR_IA32_MCx_STATUS(bank), 0);
+}
+
+static inline void __smp_deferred_error_interrupt(void)
+{
+       inc_irq_stat(irq_deferred_error_count);
+       deferred_error_int_vector();
+}
+
+asmlinkage __visible void smp_deferred_error_interrupt(void)
+{
+       entering_irq();
+       __smp_deferred_error_interrupt();
+       exiting_ack_irq();
+}
+
+asmlinkage __visible void smp_trace_deferred_error_interrupt(void)
+{
+       entering_irq();
+       trace_deferred_error_apic_entry(DEFERRED_ERROR_VECTOR);
+       __smp_deferred_error_interrupt();
+       trace_deferred_error_apic_exit(DEFERRED_ERROR_VECTOR);
+       exiting_ack_irq();
+}
+
+/* APIC interrupt handler for deferred errors */
+static void amd_deferred_error_interrupt(void)
+{
+       u64 status;
+       unsigned int bank;
+
+       for (bank = 0; bank < mca_cfg.banks; ++bank) {
+               rdmsrl(MSR_IA32_MCx_STATUS(bank), status);
+
+               if (!(status & MCI_STATUS_VAL) ||
+                   !(status & MCI_STATUS_DEFERRED))
+                       continue;
+
+               __log_error(bank, false, 0);
+               break;
+       }
 }
 
 /*
@@ -273,12 +382,12 @@ init:
  * the interrupt goes off when error_count reaches threshold_limit.
  * the handler will simply log mcelog w/ software defined bank number.
  */
+
 static void amd_threshold_interrupt(void)
 {
        u32 low = 0, high = 0, address = 0;
        int cpu = smp_processor_id();
        unsigned int bank, block;
-       struct mce m;
 
        /* assume first bank caused it */
        for (bank = 0; bank < mca_cfg.banks; ++bank) {
@@ -321,15 +430,7 @@ static void amd_threshold_interrupt(void)
        return;
 
 log:
-       mce_setup(&m);
-       rdmsrl(MSR_IA32_MCx_STATUS(bank), m.status);
-       if (!(m.status & MCI_STATUS_VAL))
-               return;
-       m.misc = ((u64)high << 32) | low;
-       m.bank = bank;
-       mce_log(&m);
-
-       wrmsrl(MSR_IA32_MCx_STATUS(bank), 0);
+       __log_error(bank, true, ((u64)high << 32) | low);
 }
 
 /*