linux/arch/x86/kernel/cpu/mcheck/mce-inject.c
<<
>>
Prefs
   1/*
   2 * Machine check injection support.
   3 * Copyright 2008 Intel Corporation.
   4 *
   5 * This program is free software; you can redistribute it and/or
   6 * modify it under the terms of the GNU General Public License
   7 * as published by the Free Software Foundation; version 2
   8 * of the License.
   9 *
  10 * Authors:
  11 * Andi Kleen
  12 * Ying Huang
  13 */
  14#include <linux/uaccess.h>
  15#include <linux/module.h>
  16#include <linux/timer.h>
  17#include <linux/kernel.h>
  18#include <linux/string.h>
  19#include <linux/fs.h>
  20#include <linux/smp.h>
  21#include <linux/notifier.h>
  22#include <linux/kdebug.h>
  23#include <linux/cpu.h>
  24#include <linux/sched.h>
  25#include <linux/gfp.h>
  26#include <asm/mce.h>
  27#include <asm/apic.h>
  28#include <asm/nmi.h>
  29
  30/* Update fake mce registers on current CPU. */
  31static void inject_mce(struct mce *m)
  32{
  33        struct mce *i = &per_cpu(injectm, m->extcpu);
  34
  35        /* Make sure noone reads partially written injectm */
  36        i->finished = 0;
  37        mb();
  38        m->finished = 0;
  39        /* First set the fields after finished */
  40        i->extcpu = m->extcpu;
  41        mb();
  42        /* Now write record in order, finished last (except above) */
  43        memcpy(i, m, sizeof(struct mce));
  44        /* Finally activate it */
  45        mb();
  46        i->finished = 1;
  47}
  48
  49static void raise_poll(struct mce *m)
  50{
  51        unsigned long flags;
  52        mce_banks_t b;
  53
  54        memset(&b, 0xff, sizeof(mce_banks_t));
  55        local_irq_save(flags);
  56        machine_check_poll(0, &b);
  57        local_irq_restore(flags);
  58        m->finished = 0;
  59}
  60
  61static void raise_exception(struct mce *m, struct pt_regs *pregs)
  62{
  63        struct pt_regs regs;
  64        unsigned long flags;
  65
  66        if (!pregs) {
  67                memset(&regs, 0, sizeof(struct pt_regs));
  68                regs.ip = m->ip;
  69                regs.cs = m->cs;
  70                pregs = &regs;
  71        }
  72        /* in mcheck exeception handler, irq will be disabled */
  73        local_irq_save(flags);
  74        do_machine_check(pregs, 0);
  75        local_irq_restore(flags);
  76        m->finished = 0;
  77}
  78
  79static cpumask_var_t mce_inject_cpumask;
  80
  81static int mce_raise_notify(struct notifier_block *self,
  82                            unsigned long val, void *data)
  83{
  84        struct die_args *args = (struct die_args *)data;
  85        int cpu = smp_processor_id();
  86        struct mce *m = &__get_cpu_var(injectm);
  87        if (val != DIE_NMI || !cpumask_test_cpu(cpu, mce_inject_cpumask))
  88                return NOTIFY_DONE;
  89        cpumask_clear_cpu(cpu, mce_inject_cpumask);
  90        if (m->inject_flags & MCJ_EXCEPTION)
  91                raise_exception(m, args->regs);
  92        else if (m->status)
  93                raise_poll(m);
  94        return NOTIFY_STOP;
  95}
  96
  97static struct notifier_block mce_raise_nb = {
  98        .notifier_call = mce_raise_notify,
  99        .priority = NMI_LOCAL_NORMAL_PRIOR,
 100};
 101
 102/* Inject mce on current CPU */
 103static int raise_local(void)
 104{
 105        struct mce *m = &__get_cpu_var(injectm);
 106        int context = MCJ_CTX(m->inject_flags);
 107        int ret = 0;
 108        int cpu = m->extcpu;
 109
 110        if (m->inject_flags & MCJ_EXCEPTION) {
 111                printk(KERN_INFO "Triggering MCE exception on CPU %d\n", cpu);
 112                switch (context) {
 113                case MCJ_CTX_IRQ:
 114                        /*
 115                         * Could do more to fake interrupts like
 116                         * calling irq_enter, but the necessary
 117                         * machinery isn't exported currently.
 118                         */
 119                        /*FALL THROUGH*/
 120                case MCJ_CTX_PROCESS:
 121                        raise_exception(m, NULL);
 122                        break;
 123                default:
 124                        printk(KERN_INFO "Invalid MCE context\n");
 125                        ret = -EINVAL;
 126                }
 127                printk(KERN_INFO "MCE exception done on CPU %d\n", cpu);
 128        } else if (m->status) {
 129                printk(KERN_INFO "Starting machine check poll CPU %d\n", cpu);
 130                raise_poll(m);
 131                mce_notify_irq();
 132                printk(KERN_INFO "Machine check poll done on CPU %d\n", cpu);
 133        } else
 134                m->finished = 0;
 135
 136        return ret;
 137}
 138
 139static void raise_mce(struct mce *m)
 140{
 141        int context = MCJ_CTX(m->inject_flags);
 142
 143        inject_mce(m);
 144
 145        if (context == MCJ_CTX_RANDOM)
 146                return;
 147
 148#ifdef CONFIG_X86_LOCAL_APIC
 149        if (m->inject_flags & MCJ_NMI_BROADCAST) {
 150                unsigned long start;
 151                int cpu;
 152                get_online_cpus();
 153                cpumask_copy(mce_inject_cpumask, cpu_online_mask);
 154                cpumask_clear_cpu(get_cpu(), mce_inject_cpumask);
 155                for_each_online_cpu(cpu) {
 156                        struct mce *mcpu = &per_cpu(injectm, cpu);
 157                        if (!mcpu->finished ||
 158                            MCJ_CTX(mcpu->inject_flags) != MCJ_CTX_RANDOM)
 159                                cpumask_clear_cpu(cpu, mce_inject_cpumask);
 160                }
 161                if (!cpumask_empty(mce_inject_cpumask))
 162                        apic->send_IPI_mask(mce_inject_cpumask, NMI_VECTOR);
 163                start = jiffies;
 164                while (!cpumask_empty(mce_inject_cpumask)) {
 165                        if (!time_before(jiffies, start + 2*HZ)) {
 166                                printk(KERN_ERR
 167                                "Timeout waiting for mce inject NMI %lx\n",
 168                                        *cpumask_bits(mce_inject_cpumask));
 169                                break;
 170                        }
 171                        cpu_relax();
 172                }
 173                raise_local();
 174                put_cpu();
 175                put_online_cpus();
 176        } else
 177#endif
 178                raise_local();
 179}
 180
 181/* Error injection interface */
 182static ssize_t mce_write(struct file *filp, const char __user *ubuf,
 183                         size_t usize, loff_t *off)
 184{
 185        struct mce m;
 186
 187        if (!capable(CAP_SYS_ADMIN))
 188                return -EPERM;
 189        /*
 190         * There are some cases where real MSR reads could slip
 191         * through.
 192         */
 193        if (!boot_cpu_has(X86_FEATURE_MCE) || !boot_cpu_has(X86_FEATURE_MCA))
 194                return -EIO;
 195
 196        if ((unsigned long)usize > sizeof(struct mce))
 197                usize = sizeof(struct mce);
 198        if (copy_from_user(&m, ubuf, usize))
 199                return -EFAULT;
 200
 201        if (m.extcpu >= num_possible_cpus() || !cpu_online(m.extcpu))
 202                return -EINVAL;
 203
 204        /*
 205         * Need to give user space some time to set everything up,
 206         * so do it a jiffie or two later everywhere.
 207         */
 208        schedule_timeout(2);
 209        raise_mce(&m);
 210        return usize;
 211}
 212
 213static int inject_init(void)
 214{
 215        if (!alloc_cpumask_var(&mce_inject_cpumask, GFP_KERNEL))
 216                return -ENOMEM;
 217        printk(KERN_INFO "Machine check injector initialized\n");
 218        mce_chrdev_ops.write = mce_write;
 219        register_die_notifier(&mce_raise_nb);
 220        return 0;
 221}
 222
 223module_init(inject_init);
 224/*
 225 * Cannot tolerate unloading currently because we cannot
 226 * guarantee all openers of mce_chrdev will get a reference to us.
 227 */
 228MODULE_LICENSE("GPL");
 229