Merge tag 'ras_core_for_v5.16_rc1' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-microblaze.git] / arch / x86 / kernel / cpu / mce / core.c
index 2af4b42..6ed3653 100644 (file)
@@ -121,8 +121,6 @@ mce_banks_t mce_banks_ce_disabled;
 static struct work_struct mce_work;
 static struct irq_work mce_irq_work;
 
-static void (*quirk_no_way_out)(int bank, struct mce *m, struct pt_regs *regs);
-
 /*
  * CPU/chipset specific EDAC code can register a notifier call here to print
  * MCE errors in a human-readable form.
@@ -176,53 +174,27 @@ void mce_unregister_decode_chain(struct notifier_block *nb)
 }
 EXPORT_SYMBOL_GPL(mce_unregister_decode_chain);
 
-static inline u32 ctl_reg(int bank)
-{
-       return MSR_IA32_MCx_CTL(bank);
-}
-
-static inline u32 status_reg(int bank)
-{
-       return MSR_IA32_MCx_STATUS(bank);
-}
-
-static inline u32 addr_reg(int bank)
-{
-       return MSR_IA32_MCx_ADDR(bank);
-}
-
-static inline u32 misc_reg(int bank)
-{
-       return MSR_IA32_MCx_MISC(bank);
-}
-
-static inline u32 smca_ctl_reg(int bank)
-{
-       return MSR_AMD64_SMCA_MCx_CTL(bank);
-}
-
-static inline u32 smca_status_reg(int bank)
+u32 mca_msr_reg(int bank, enum mca_msr reg)
 {
-       return MSR_AMD64_SMCA_MCx_STATUS(bank);
-}
+       if (mce_flags.smca) {
+               switch (reg) {
+               case MCA_CTL:    return MSR_AMD64_SMCA_MCx_CTL(bank);
+               case MCA_ADDR:   return MSR_AMD64_SMCA_MCx_ADDR(bank);
+               case MCA_MISC:   return MSR_AMD64_SMCA_MCx_MISC(bank);
+               case MCA_STATUS: return MSR_AMD64_SMCA_MCx_STATUS(bank);
+               }
+       }
 
-static inline u32 smca_addr_reg(int bank)
-{
-       return MSR_AMD64_SMCA_MCx_ADDR(bank);
-}
+       switch (reg) {
+       case MCA_CTL:    return MSR_IA32_MCx_CTL(bank);
+       case MCA_ADDR:   return MSR_IA32_MCx_ADDR(bank);
+       case MCA_MISC:   return MSR_IA32_MCx_MISC(bank);
+       case MCA_STATUS: return MSR_IA32_MCx_STATUS(bank);
+       }
 
-static inline u32 smca_misc_reg(int bank)
-{
-       return MSR_AMD64_SMCA_MCx_MISC(bank);
+       return 0;
 }
 
-struct mca_msr_regs msr_ops = {
-       .ctl    = ctl_reg,
-       .status = status_reg,
-       .addr   = addr_reg,
-       .misc   = misc_reg
-};
-
 static void __print_mce(struct mce *m)
 {
        pr_emerg(HW_ERR "CPU %d: Machine Check%s: %Lx Bank %d: %016Lx\n",
@@ -362,11 +334,11 @@ static int msr_to_offset(u32 msr)
 
        if (msr == mca_cfg.rip_msr)
                return offsetof(struct mce, ip);
-       if (msr == msr_ops.status(bank))
+       if (msr == mca_msr_reg(bank, MCA_STATUS))
                return offsetof(struct mce, status);
-       if (msr == msr_ops.addr(bank))
+       if (msr == mca_msr_reg(bank, MCA_ADDR))
                return offsetof(struct mce, addr);
-       if (msr == msr_ops.misc(bank))
+       if (msr == mca_msr_reg(bank, MCA_MISC))
                return offsetof(struct mce, misc);
        if (msr == MSR_IA32_MCG_STATUS)
                return offsetof(struct mce, mcgstatus);
@@ -667,10 +639,10 @@ static struct notifier_block mce_default_nb = {
 static void mce_read_aux(struct mce *m, int i)
 {
        if (m->status & MCI_STATUS_MISCV)
-               m->misc = mce_rdmsrl(msr_ops.misc(i));
+               m->misc = mce_rdmsrl(mca_msr_reg(i, MCA_MISC));
 
        if (m->status & MCI_STATUS_ADDRV) {
-               m->addr = mce_rdmsrl(msr_ops.addr(i));
+               m->addr = mce_rdmsrl(mca_msr_reg(i, MCA_ADDR));
 
                /*
                 * Mask the reported address by the reported granularity.
@@ -740,7 +712,7 @@ bool machine_check_poll(enum mcp_flags flags, mce_banks_t *b)
                m.bank = i;
 
                barrier();
-               m.status = mce_rdmsrl(msr_ops.status(i));
+               m.status = mce_rdmsrl(mca_msr_reg(i, MCA_STATUS));
 
                /* If this entry is not valid, ignore it */
                if (!(m.status & MCI_STATUS_VAL))
@@ -808,7 +780,7 @@ clear_it:
                /*
                 * Clear state for this bank.
                 */
-               mce_wrmsrl(msr_ops.status(i), 0);
+               mce_wrmsrl(mca_msr_reg(i, MCA_STATUS), 0);
        }
 
        /*
@@ -822,6 +794,34 @@ clear_it:
 }
 EXPORT_SYMBOL_GPL(machine_check_poll);
 
+/*
+ * During IFU recovery Sandy Bridge -EP4S processors set the RIPV and
+ * EIPV bits in MCG_STATUS to zero on the affected logical processor (SDM
+ * Vol 3B Table 15-20). But this confuses both the code that determines
+ * whether the machine check occurred in kernel or user mode, and also
+ * the severity assessment code. Pretend that EIPV was set, and take the
+ * ip/cs values from the pt_regs that mce_gather_info() ignored earlier.
+ */
+static void quirk_sandybridge_ifu(int bank, struct mce *m, struct pt_regs *regs)
+{
+       if (bank != 0)
+               return;
+       if ((m->mcgstatus & (MCG_STATUS_EIPV|MCG_STATUS_RIPV)) != 0)
+               return;
+       if ((m->status & (MCI_STATUS_OVER|MCI_STATUS_UC|
+                         MCI_STATUS_EN|MCI_STATUS_MISCV|MCI_STATUS_ADDRV|
+                         MCI_STATUS_PCC|MCI_STATUS_S|MCI_STATUS_AR|
+                         MCACOD)) !=
+                        (MCI_STATUS_UC|MCI_STATUS_EN|
+                         MCI_STATUS_MISCV|MCI_STATUS_ADDRV|MCI_STATUS_S|
+                         MCI_STATUS_AR|MCACOD_INSTR))
+               return;
+
+       m->mcgstatus |= MCG_STATUS_EIPV;
+       m->ip = regs->ip;
+       m->cs = regs->cs;
+}
+
 /*
  * Do a quick check if any of the events requires a panic.
  * This decides if we keep the events around or clear them.
@@ -833,13 +833,13 @@ static int mce_no_way_out(struct mce *m, char **msg, unsigned long *validp,
        int i;
 
        for (i = 0; i < this_cpu_read(mce_num_banks); i++) {
-               m->status = mce_rdmsrl(msr_ops.status(i));
+               m->status = mce_rdmsrl(mca_msr_reg(i, MCA_STATUS));
                if (!(m->status & MCI_STATUS_VAL))
                        continue;
 
                __set_bit(i, validp);
-               if (quirk_no_way_out)
-                       quirk_no_way_out(i, m, regs);
+               if (mce_flags.snb_ifu_quirk)
+                       quirk_sandybridge_ifu(i, m, regs);
 
                m->bank = i;
                if (mce_severity(m, regs, mca_cfg.tolerant, &tmp, true) >= MCE_PANIC_SEVERITY) {
@@ -1126,7 +1126,7 @@ static void mce_clear_state(unsigned long *toclear)
 
        for (i = 0; i < this_cpu_read(mce_num_banks); i++) {
                if (test_bit(i, toclear))
-                       mce_wrmsrl(msr_ops.status(i), 0);
+                       mce_wrmsrl(mca_msr_reg(i, MCA_STATUS), 0);
        }
 }
 
@@ -1185,7 +1185,7 @@ static void __mc_scan_banks(struct mce *m, struct pt_regs *regs, struct mce *fin
                m->addr = 0;
                m->bank = i;
 
-               m->status = mce_rdmsrl(msr_ops.status(i));
+               m->status = mce_rdmsrl(mca_msr_reg(i, MCA_STATUS));
                if (!(m->status & MCI_STATUS_VAL))
                        continue;
 
@@ -1254,7 +1254,7 @@ static void kill_me_maybe(struct callback_head *cb)
                flags |= MF_MUST_KILL;
 
        ret = memory_failure(p->mce_addr >> PAGE_SHIFT, flags);
-       if (!ret && !(p->mce_kflags & MCE_IN_KERNEL_COPYIN)) {
+       if (!ret) {
                set_mce_nospec(p->mce_addr >> PAGE_SHIFT, p->mce_whole_page);
                sync_core();
                return;
@@ -1268,15 +1268,21 @@ static void kill_me_maybe(struct callback_head *cb)
        if (ret == -EHWPOISON)
                return;
 
-       if (p->mce_vaddr != (void __user *)-1l) {
-               force_sig_mceerr(BUS_MCEERR_AR, p->mce_vaddr, PAGE_SHIFT);
-       } else {
-               pr_err("Memory error not recovered");
-               kill_me_now(cb);
-       }
+       pr_err("Memory error not recovered");
+       kill_me_now(cb);
+}
+
+static void kill_me_never(struct callback_head *cb)
+{
+       struct task_struct *p = container_of(cb, struct task_struct, mce_kill_me);
+
+       p->mce_count = 0;
+       pr_err("Kernel accessed poison in user space at %llx\n", p->mce_addr);
+       if (!memory_failure(p->mce_addr >> PAGE_SHIFT, 0))
+               set_mce_nospec(p->mce_addr >> PAGE_SHIFT, p->mce_whole_page);
 }
 
-static void queue_task_work(struct mce *m, char *msg, int kill_current_task)
+static void queue_task_work(struct mce *m, char *msg, void (*func)(struct callback_head *))
 {
        int count = ++current->mce_count;
 
@@ -1286,11 +1292,7 @@ static void queue_task_work(struct mce *m, char *msg, int kill_current_task)
                current->mce_kflags = m->kflags;
                current->mce_ripv = !!(m->mcgstatus & MCG_STATUS_RIPV);
                current->mce_whole_page = whole_page(m);
-
-               if (kill_current_task)
-                       current->mce_kill_me.func = kill_me_now;
-               else
-                       current->mce_kill_me.func = kill_me_maybe;
+               current->mce_kill_me.func = func;
        }
 
        /* Ten is likely overkill. Don't expect more than two faults before task_work() */
@@ -1308,6 +1310,15 @@ static void queue_task_work(struct mce *m, char *msg, int kill_current_task)
        task_work_add(current, &current->mce_kill_me, TWA_RESUME);
 }
 
+/* Handle unconfigured int18 (should never happen) */
+static noinstr void unexpected_machine_check(struct pt_regs *regs)
+{
+       instrumentation_begin();
+       pr_err("CPU#%d: Unexpected int18 (Machine Check)\n",
+              smp_processor_id());
+       instrumentation_end();
+}
+
 /*
  * The actual machine check handler. This only handles real
  * exceptions when something got corrupted coming in through int 18.
@@ -1328,36 +1339,43 @@ static void queue_task_work(struct mce *m, char *msg, int kill_current_task)
  */
 noinstr void do_machine_check(struct pt_regs *regs)
 {
+       int worst = 0, order, no_way_out, kill_current_task, lmce;
        DECLARE_BITMAP(valid_banks, MAX_NR_BANKS);
        DECLARE_BITMAP(toclear, MAX_NR_BANKS);
        struct mca_config *cfg = &mca_cfg;
        struct mce m, *final;
        char *msg = NULL;
-       int worst = 0;
+
+       if (unlikely(mce_flags.p5))
+               return pentium_machine_check(regs);
+       else if (unlikely(mce_flags.winchip))
+               return winchip_machine_check(regs);
+       else if (unlikely(!mca_cfg.initialized))
+               return unexpected_machine_check(regs);
 
        /*
         * Establish sequential order between the CPUs entering the machine
         * check handler.
         */
-       int order = -1;
+       order = -1;
 
        /*
         * If no_way_out gets set, there is no safe way to recover from this
         * MCE.  If mca_cfg.tolerant is cranked up, we'll try anyway.
         */
-       int no_way_out = 0;
+       no_way_out = 0;
 
        /*
         * If kill_current_task is not set, there might be a way to recover from this
         * error.
         */
-       int kill_current_task = 0;
+       kill_current_task = 0;
 
        /*
         * MCEs are always local on AMD. Same is determined by MCG_STATUS_LMCES
         * on Intel.
         */
-       int lmce = 1;
+       lmce = 1;
 
        this_cpu_inc(mce_exception_count);
 
@@ -1441,7 +1459,10 @@ noinstr void do_machine_check(struct pt_regs *regs)
                /* If this triggers there is no way to recover. Die hard. */
                BUG_ON(!on_thread_stack() || !user_mode(regs));
 
-               queue_task_work(&m, msg, kill_current_task);
+               if (kill_current_task)
+                       queue_task_work(&m, msg, kill_me_now);
+               else
+                       queue_task_work(&m, msg, kill_me_maybe);
 
        } else {
                /*
@@ -1459,7 +1480,7 @@ noinstr void do_machine_check(struct pt_regs *regs)
                }
 
                if (m.kflags & MCE_IN_KERNEL_COPYIN)
-                       queue_task_work(&m, msg, kill_current_task);
+                       queue_task_work(&m, msg, kill_me_never);
        }
 out:
        mce_wrmsrl(MSR_IA32_MCG_STATUS, 0);
@@ -1669,8 +1690,8 @@ static void __mcheck_cpu_init_clear_banks(void)
 
                if (!b->init)
                        continue;
-               wrmsrl(msr_ops.ctl(i), b->ctl);
-               wrmsrl(msr_ops.status(i), 0);
+               wrmsrl(mca_msr_reg(i, MCA_CTL), b->ctl);
+               wrmsrl(mca_msr_reg(i, MCA_STATUS), 0);
        }
 }
 
@@ -1696,39 +1717,11 @@ static void __mcheck_cpu_check_banks(void)
                if (!b->init)
                        continue;
 
-               rdmsrl(msr_ops.ctl(i), msrval);
+               rdmsrl(mca_msr_reg(i, MCA_CTL), msrval);
                b->init = !!msrval;
        }
 }
 
-/*
- * During IFU recovery Sandy Bridge -EP4S processors set the RIPV and
- * EIPV bits in MCG_STATUS to zero on the affected logical processor (SDM
- * Vol 3B Table 15-20). But this confuses both the code that determines
- * whether the machine check occurred in kernel or user mode, and also
- * the severity assessment code. Pretend that EIPV was set, and take the
- * ip/cs values from the pt_regs that mce_gather_info() ignored earlier.
- */
-static void quirk_sandybridge_ifu(int bank, struct mce *m, struct pt_regs *regs)
-{
-       if (bank != 0)
-               return;
-       if ((m->mcgstatus & (MCG_STATUS_EIPV|MCG_STATUS_RIPV)) != 0)
-               return;
-       if ((m->status & (MCI_STATUS_OVER|MCI_STATUS_UC|
-                         MCI_STATUS_EN|MCI_STATUS_MISCV|MCI_STATUS_ADDRV|
-                         MCI_STATUS_PCC|MCI_STATUS_S|MCI_STATUS_AR|
-                         MCACOD)) !=
-                        (MCI_STATUS_UC|MCI_STATUS_EN|
-                         MCI_STATUS_MISCV|MCI_STATUS_ADDRV|MCI_STATUS_S|
-                         MCI_STATUS_AR|MCACOD_INSTR))
-               return;
-
-       m->mcgstatus |= MCG_STATUS_EIPV;
-       m->ip = regs->ip;
-       m->cs = regs->cs;
-}
-
 /* Add per CPU specific workarounds here */
 static int __mcheck_cpu_apply_quirks(struct cpuinfo_x86 *c)
 {
@@ -1802,7 +1795,7 @@ static int __mcheck_cpu_apply_quirks(struct cpuinfo_x86 *c)
                        cfg->bootlog = 0;
 
                if (c->x86 == 6 && c->x86_model == 45)
-                       quirk_no_way_out = quirk_sandybridge_ifu;
+                       mce_flags.snb_ifu_quirk = 1;
        }
 
        if (c->x86_vendor == X86_VENDOR_ZHAOXIN) {
@@ -1832,9 +1825,11 @@ static int __mcheck_cpu_ancient_init(struct cpuinfo_x86 *c)
        switch (c->x86_vendor) {
        case X86_VENDOR_INTEL:
                intel_p5_mcheck_init(c);
+               mce_flags.p5 = 1;
                return 1;
        case X86_VENDOR_CENTAUR:
                winchip_mcheck_init(c);
+               mce_flags.winchip = 1;
                return 1;
        default:
                return 0;
@@ -1853,13 +1848,6 @@ static void __mcheck_cpu_init_early(struct cpuinfo_x86 *c)
                mce_flags.succor         = !!cpu_has(c, X86_FEATURE_SUCCOR);
                mce_flags.smca           = !!cpu_has(c, X86_FEATURE_SMCA);
                mce_flags.amd_threshold  = 1;
-
-               if (mce_flags.smca) {
-                       msr_ops.ctl     = smca_ctl_reg;
-                       msr_ops.status  = smca_status_reg;
-                       msr_ops.addr    = smca_addr_reg;
-                       msr_ops.misc    = smca_misc_reg;
-               }
        }
 }
 
@@ -1989,18 +1977,6 @@ bool filter_mce(struct mce *m)
        return false;
 }
 
-/* Handle unconfigured int18 (should never happen) */
-static noinstr void unexpected_machine_check(struct pt_regs *regs)
-{
-       instrumentation_begin();
-       pr_err("CPU#%d: Unexpected int18 (Machine Check)\n",
-              smp_processor_id());
-       instrumentation_end();
-}
-
-/* Call the installed machine check handler for this CPU setup. */
-void (*machine_check_vector)(struct pt_regs *) = unexpected_machine_check;
-
 static __always_inline void exc_machine_check_kernel(struct pt_regs *regs)
 {
        irqentry_state_t irq_state;
@@ -2011,31 +1987,22 @@ static __always_inline void exc_machine_check_kernel(struct pt_regs *regs)
         * Only required when from kernel mode. See
         * mce_check_crashing_cpu() for details.
         */
-       if (machine_check_vector == do_machine_check &&
-           mce_check_crashing_cpu())
+       if (mca_cfg.initialized && mce_check_crashing_cpu())
                return;
 
        irq_state = irqentry_nmi_enter(regs);
-       /*
-        * The call targets are marked noinstr, but objtool can't figure
-        * that out because it's an indirect call. Annotate it.
-        */
-       instrumentation_begin();
 
-       machine_check_vector(regs);
+       do_machine_check(regs);
 
-       instrumentation_end();
        irqentry_nmi_exit(regs, irq_state);
 }
 
 static __always_inline void exc_machine_check_user(struct pt_regs *regs)
 {
        irqentry_enter_from_user_mode(regs);
-       instrumentation_begin();
 
-       machine_check_vector(regs);
+       do_machine_check(regs);
 
-       instrumentation_end();
        irqentry_exit_to_user_mode(regs);
 }
 
@@ -2102,7 +2069,7 @@ void mcheck_cpu_init(struct cpuinfo_x86 *c)
                return;
        }
 
-       machine_check_vector = do_machine_check;
+       mca_cfg.initialized = 1;
 
        __mcheck_cpu_init_early(c);
        __mcheck_cpu_init_generic();
@@ -2210,7 +2177,6 @@ int __init mcheck_init(void)
        mce_register_decode_chain(&early_nb);
        mce_register_decode_chain(&mce_uc_nb);
        mce_register_decode_chain(&mce_default_nb);
-       mcheck_vendor_init_severity();
 
        INIT_WORK(&mce_work, mce_gen_pool_process);
        init_irq_work(&mce_irq_work, mce_irq_work_cb);
@@ -2235,7 +2201,7 @@ static void mce_disable_error_reporting(void)
                struct mce_bank *b = &mce_banks[i];
 
                if (b->init)
-                       wrmsrl(msr_ops.ctl(i), 0);
+                       wrmsrl(mca_msr_reg(i, MCA_CTL), 0);
        }
        return;
 }
@@ -2587,7 +2553,7 @@ static void mce_reenable_cpu(void)
                struct mce_bank *b = &mce_banks[i];
 
                if (b->init)
-                       wrmsrl(msr_ops.ctl(i), b->ctl);
+                       wrmsrl(mca_msr_reg(i, MCA_CTL), b->ctl);
        }
 }