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