~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/arch/x86/kernel/cpu/mce/inject.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0-only
  2 /*
  3  * Machine check injection support.
  4  * Copyright 2008 Intel Corporation.
  5  *
  6  * Authors:
  7  * Andi Kleen
  8  * Ying Huang
  9  *
 10  * The AMD part (from mce_amd_inj.c): a simple MCE injection facility
 11  * for testing different aspects of the RAS code. This driver should be
 12  * built as module so that it can be loaded on production kernels for
 13  * testing purposes.
 14  *
 15  * Copyright (c) 2010-17:  Borislav Petkov <bp@alien8.de>
 16  *                         Advanced Micro Devices Inc.
 17  */
 18 
 19 #include <linux/cpu.h>
 20 #include <linux/debugfs.h>
 21 #include <linux/kernel.h>
 22 #include <linux/module.h>
 23 #include <linux/notifier.h>
 24 #include <linux/pci.h>
 25 #include <linux/uaccess.h>
 26 
 27 #include <asm/amd_nb.h>
 28 #include <asm/apic.h>
 29 #include <asm/irq_vectors.h>
 30 #include <asm/mce.h>
 31 #include <asm/nmi.h>
 32 #include <asm/smp.h>
 33 
 34 #include "internal.h"
 35 
 36 static bool hw_injection_possible = true;
 37 
 38 /*
 39  * Collect all the MCi_XXX settings
 40  */
 41 static struct mce i_mce;
 42 static struct dentry *dfs_inj;
 43 
 44 #define MAX_FLAG_OPT_SIZE       4
 45 #define NBCFG                   0x44
 46 
 47 enum injection_type {
 48         SW_INJ = 0,     /* SW injection, simply decode the error */
 49         HW_INJ,         /* Trigger a #MC */
 50         DFR_INT_INJ,    /* Trigger Deferred error interrupt */
 51         THR_INT_INJ,    /* Trigger threshold interrupt */
 52         N_INJ_TYPES,
 53 };
 54 
 55 static const char * const flags_options[] = {
 56         [SW_INJ] = "sw",
 57         [HW_INJ] = "hw",
 58         [DFR_INT_INJ] = "df",
 59         [THR_INT_INJ] = "th",
 60         NULL
 61 };
 62 
 63 /* Set default injection to SW_INJ */
 64 static enum injection_type inj_type = SW_INJ;
 65 
 66 #define MCE_INJECT_SET(reg)                                             \
 67 static int inj_##reg##_set(void *data, u64 val)                         \
 68 {                                                                       \
 69         struct mce *m = (struct mce *)data;                             \
 70                                                                         \
 71         m->reg = val;                                                   \
 72         return 0;                                                       \
 73 }
 74 
 75 MCE_INJECT_SET(status);
 76 MCE_INJECT_SET(misc);
 77 MCE_INJECT_SET(addr);
 78 MCE_INJECT_SET(synd);
 79 
 80 #define MCE_INJECT_GET(reg)                                             \
 81 static int inj_##reg##_get(void *data, u64 *val)                        \
 82 {                                                                       \
 83         struct mce *m = (struct mce *)data;                             \
 84                                                                         \
 85         *val = m->reg;                                                  \
 86         return 0;                                                       \
 87 }
 88 
 89 MCE_INJECT_GET(status);
 90 MCE_INJECT_GET(misc);
 91 MCE_INJECT_GET(addr);
 92 MCE_INJECT_GET(synd);
 93 MCE_INJECT_GET(ipid);
 94 
 95 DEFINE_SIMPLE_ATTRIBUTE(status_fops, inj_status_get, inj_status_set, "%llx\n");
 96 DEFINE_SIMPLE_ATTRIBUTE(misc_fops, inj_misc_get, inj_misc_set, "%llx\n");
 97 DEFINE_SIMPLE_ATTRIBUTE(addr_fops, inj_addr_get, inj_addr_set, "%llx\n");
 98 DEFINE_SIMPLE_ATTRIBUTE(synd_fops, inj_synd_get, inj_synd_set, "%llx\n");
 99 
100 /* Use the user provided IPID value on a sw injection. */
101 static int inj_ipid_set(void *data, u64 val)
102 {
103         struct mce *m = (struct mce *)data;
104 
105         if (cpu_feature_enabled(X86_FEATURE_SMCA)) {
106                 if (inj_type == SW_INJ)
107                         m->ipid = val;
108         }
109 
110         return 0;
111 }
112 
113 DEFINE_SIMPLE_ATTRIBUTE(ipid_fops, inj_ipid_get, inj_ipid_set, "%llx\n");
114 
115 static void setup_inj_struct(struct mce *m)
116 {
117         memset(m, 0, sizeof(struct mce));
118 
119         m->cpuvendor = boot_cpu_data.x86_vendor;
120         m->time      = ktime_get_real_seconds();
121         m->cpuid     = cpuid_eax(1);
122         m->microcode = boot_cpu_data.microcode;
123 }
124 
125 /* Update fake mce registers on current CPU. */
126 static void inject_mce(struct mce *m)
127 {
128         struct mce *i = &per_cpu(injectm, m->extcpu);
129 
130         /* Make sure no one reads partially written injectm */
131         i->finished = 0;
132         mb();
133         m->finished = 0;
134         /* First set the fields after finished */
135         i->extcpu = m->extcpu;
136         mb();
137         /* Now write record in order, finished last (except above) */
138         memcpy(i, m, sizeof(struct mce));
139         /* Finally activate it */
140         mb();
141         i->finished = 1;
142 }
143 
144 static void raise_poll(struct mce *m)
145 {
146         unsigned long flags;
147         mce_banks_t b;
148 
149         memset(&b, 0xff, sizeof(mce_banks_t));
150         local_irq_save(flags);
151         machine_check_poll(0, &b);
152         local_irq_restore(flags);
153         m->finished = 0;
154 }
155 
156 static void raise_exception(struct mce *m, struct pt_regs *pregs)
157 {
158         struct pt_regs regs;
159         unsigned long flags;
160 
161         if (!pregs) {
162                 memset(&regs, 0, sizeof(struct pt_regs));
163                 regs.ip = m->ip;
164                 regs.cs = m->cs;
165                 pregs = &regs;
166         }
167         /* do_machine_check() expects interrupts disabled -- at least */
168         local_irq_save(flags);
169         do_machine_check(pregs);
170         local_irq_restore(flags);
171         m->finished = 0;
172 }
173 
174 static cpumask_var_t mce_inject_cpumask;
175 static DEFINE_MUTEX(mce_inject_mutex);
176 
177 static int mce_raise_notify(unsigned int cmd, struct pt_regs *regs)
178 {
179         int cpu = smp_processor_id();
180         struct mce *m = this_cpu_ptr(&injectm);
181         if (!cpumask_test_cpu(cpu, mce_inject_cpumask))
182                 return NMI_DONE;
183         cpumask_clear_cpu(cpu, mce_inject_cpumask);
184         if (m->inject_flags & MCJ_EXCEPTION)
185                 raise_exception(m, regs);
186         else if (m->status)
187                 raise_poll(m);
188         return NMI_HANDLED;
189 }
190 
191 static void mce_irq_ipi(void *info)
192 {
193         int cpu = smp_processor_id();
194         struct mce *m = this_cpu_ptr(&injectm);
195 
196         if (cpumask_test_cpu(cpu, mce_inject_cpumask) &&
197                         m->inject_flags & MCJ_EXCEPTION) {
198                 cpumask_clear_cpu(cpu, mce_inject_cpumask);
199                 raise_exception(m, NULL);
200         }
201 }
202 
203 /* Inject mce on current CPU */
204 static int raise_local(void)
205 {
206         struct mce *m = this_cpu_ptr(&injectm);
207         int context = MCJ_CTX(m->inject_flags);
208         int ret = 0;
209         int cpu = m->extcpu;
210 
211         if (m->inject_flags & MCJ_EXCEPTION) {
212                 pr_info("Triggering MCE exception on CPU %d\n", cpu);
213                 switch (context) {
214                 case MCJ_CTX_IRQ:
215                         /*
216                          * Could do more to fake interrupts like
217                          * calling irq_enter, but the necessary
218                          * machinery isn't exported currently.
219                          */
220                         fallthrough;
221                 case MCJ_CTX_PROCESS:
222                         raise_exception(m, NULL);
223                         break;
224                 default:
225                         pr_info("Invalid MCE context\n");
226                         ret = -EINVAL;
227                 }
228                 pr_info("MCE exception done on CPU %d\n", cpu);
229         } else if (m->status) {
230                 pr_info("Starting machine check poll CPU %d\n", cpu);
231                 raise_poll(m);
232                 mce_notify_irq();
233                 pr_info("Machine check poll done on CPU %d\n", cpu);
234         } else
235                 m->finished = 0;
236 
237         return ret;
238 }
239 
240 static void __maybe_unused raise_mce(struct mce *m)
241 {
242         int context = MCJ_CTX(m->inject_flags);
243 
244         inject_mce(m);
245 
246         if (context == MCJ_CTX_RANDOM)
247                 return;
248 
249         if (m->inject_flags & (MCJ_IRQ_BROADCAST | MCJ_NMI_BROADCAST)) {
250                 unsigned long start;
251                 int cpu;
252 
253                 cpus_read_lock();
254                 cpumask_copy(mce_inject_cpumask, cpu_online_mask);
255                 cpumask_clear_cpu(get_cpu(), mce_inject_cpumask);
256                 for_each_online_cpu(cpu) {
257                         struct mce *mcpu = &per_cpu(injectm, cpu);
258                         if (!mcpu->finished ||
259                             MCJ_CTX(mcpu->inject_flags) != MCJ_CTX_RANDOM)
260                                 cpumask_clear_cpu(cpu, mce_inject_cpumask);
261                 }
262                 if (!cpumask_empty(mce_inject_cpumask)) {
263                         if (m->inject_flags & MCJ_IRQ_BROADCAST) {
264                                 /*
265                                  * don't wait because mce_irq_ipi is necessary
266                                  * to be sync with following raise_local
267                                  */
268                                 preempt_disable();
269                                 smp_call_function_many(mce_inject_cpumask,
270                                         mce_irq_ipi, NULL, 0);
271                                 preempt_enable();
272                         } else if (m->inject_flags & MCJ_NMI_BROADCAST)
273                                 __apic_send_IPI_mask(mce_inject_cpumask, NMI_VECTOR);
274                 }
275                 start = jiffies;
276                 while (!cpumask_empty(mce_inject_cpumask)) {
277                         if (!time_before(jiffies, start + 2*HZ)) {
278                                 pr_err("Timeout waiting for mce inject %lx\n",
279                                         *cpumask_bits(mce_inject_cpumask));
280                                 break;
281                         }
282                         cpu_relax();
283                 }
284                 raise_local();
285                 put_cpu();
286                 cpus_read_unlock();
287         } else {
288                 preempt_disable();
289                 raise_local();
290                 preempt_enable();
291         }
292 }
293 
294 static int mce_inject_raise(struct notifier_block *nb, unsigned long val,
295                             void *data)
296 {
297         struct mce *m = (struct mce *)data;
298 
299         if (!m)
300                 return NOTIFY_DONE;
301 
302         mutex_lock(&mce_inject_mutex);
303         raise_mce(m);
304         mutex_unlock(&mce_inject_mutex);
305 
306         return NOTIFY_DONE;
307 }
308 
309 static struct notifier_block inject_nb = {
310         .notifier_call  = mce_inject_raise,
311 };
312 
313 /*
314  * Caller needs to be make sure this cpu doesn't disappear
315  * from under us, i.e.: get_cpu/put_cpu.
316  */
317 static int toggle_hw_mce_inject(unsigned int cpu, bool enable)
318 {
319         u32 l, h;
320         int err;
321 
322         err = rdmsr_on_cpu(cpu, MSR_K7_HWCR, &l, &h);
323         if (err) {
324                 pr_err("%s: error reading HWCR\n", __func__);
325                 return err;
326         }
327 
328         enable ? (l |= BIT(18)) : (l &= ~BIT(18));
329 
330         err = wrmsr_on_cpu(cpu, MSR_K7_HWCR, l, h);
331         if (err)
332                 pr_err("%s: error writing HWCR\n", __func__);
333 
334         return err;
335 }
336 
337 static int __set_inj(const char *buf)
338 {
339         int i;
340 
341         for (i = 0; i < N_INJ_TYPES; i++) {
342                 if (!strncmp(flags_options[i], buf, strlen(flags_options[i]))) {
343                         if (i > SW_INJ && !hw_injection_possible)
344                                 continue;
345                         inj_type = i;
346                         return 0;
347                 }
348         }
349         return -EINVAL;
350 }
351 
352 static ssize_t flags_read(struct file *filp, char __user *ubuf,
353                           size_t cnt, loff_t *ppos)
354 {
355         char buf[MAX_FLAG_OPT_SIZE];
356         int n;
357 
358         n = sprintf(buf, "%s\n", flags_options[inj_type]);
359 
360         return simple_read_from_buffer(ubuf, cnt, ppos, buf, n);
361 }
362 
363 static ssize_t flags_write(struct file *filp, const char __user *ubuf,
364                            size_t cnt, loff_t *ppos)
365 {
366         char buf[MAX_FLAG_OPT_SIZE], *__buf;
367         int err;
368 
369         if (!cnt || cnt > MAX_FLAG_OPT_SIZE)
370                 return -EINVAL;
371 
372         if (copy_from_user(&buf, ubuf, cnt))
373                 return -EFAULT;
374 
375         buf[cnt - 1] = 0;
376 
377         /* strip whitespace */
378         __buf = strstrip(buf);
379 
380         err = __set_inj(__buf);
381         if (err) {
382                 pr_err("%s: Invalid flags value: %s\n", __func__, __buf);
383                 return err;
384         }
385 
386         *ppos += cnt;
387 
388         return cnt;
389 }
390 
391 static const struct file_operations flags_fops = {
392         .read           = flags_read,
393         .write          = flags_write,
394         .llseek         = generic_file_llseek,
395 };
396 
397 /*
398  * On which CPU to inject?
399  */
400 MCE_INJECT_GET(extcpu);
401 
402 static int inj_extcpu_set(void *data, u64 val)
403 {
404         struct mce *m = (struct mce *)data;
405 
406         if (val >= nr_cpu_ids || !cpu_online(val)) {
407                 pr_err("%s: Invalid CPU: %llu\n", __func__, val);
408                 return -EINVAL;
409         }
410         m->extcpu = val;
411         return 0;
412 }
413 
414 DEFINE_SIMPLE_ATTRIBUTE(extcpu_fops, inj_extcpu_get, inj_extcpu_set, "%llu\n");
415 
416 static void trigger_mce(void *info)
417 {
418         asm volatile("int $18");
419 }
420 
421 static void trigger_dfr_int(void *info)
422 {
423         asm volatile("int %0" :: "i" (DEFERRED_ERROR_VECTOR));
424 }
425 
426 static void trigger_thr_int(void *info)
427 {
428         asm volatile("int %0" :: "i" (THRESHOLD_APIC_VECTOR));
429 }
430 
431 static u32 get_nbc_for_node(int node_id)
432 {
433         u32 cores_per_node;
434 
435         cores_per_node = topology_num_threads_per_package() / topology_amd_nodes_per_pkg();
436         return cores_per_node * node_id;
437 }
438 
439 static void toggle_nb_mca_mst_cpu(u16 nid)
440 {
441         struct amd_northbridge *nb;
442         struct pci_dev *F3;
443         u32 val;
444         int err;
445 
446         nb = node_to_amd_nb(nid);
447         if (!nb)
448                 return;
449 
450         F3 = nb->misc;
451         if (!F3)
452                 return;
453 
454         err = pci_read_config_dword(F3, NBCFG, &val);
455         if (err) {
456                 pr_err("%s: Error reading F%dx%03x.\n",
457                        __func__, PCI_FUNC(F3->devfn), NBCFG);
458                 return;
459         }
460 
461         if (val & BIT(27))
462                 return;
463 
464         pr_err("%s: Set D18F3x44[NbMcaToMstCpuEn] which BIOS hasn't done.\n",
465                __func__);
466 
467         val |= BIT(27);
468         err = pci_write_config_dword(F3, NBCFG, val);
469         if (err)
470                 pr_err("%s: Error writing F%dx%03x.\n",
471                        __func__, PCI_FUNC(F3->devfn), NBCFG);
472 }
473 
474 static void prepare_msrs(void *info)
475 {
476         struct mce m = *(struct mce *)info;
477         u8 b = m.bank;
478 
479         wrmsrl(MSR_IA32_MCG_STATUS, m.mcgstatus);
480 
481         if (boot_cpu_has(X86_FEATURE_SMCA)) {
482                 if (m.inject_flags == DFR_INT_INJ) {
483                         wrmsrl(MSR_AMD64_SMCA_MCx_DESTAT(b), m.status);
484                         wrmsrl(MSR_AMD64_SMCA_MCx_DEADDR(b), m.addr);
485                 } else {
486                         wrmsrl(MSR_AMD64_SMCA_MCx_STATUS(b), m.status);
487                         wrmsrl(MSR_AMD64_SMCA_MCx_ADDR(b), m.addr);
488                 }
489 
490                 wrmsrl(MSR_AMD64_SMCA_MCx_SYND(b), m.synd);
491 
492                 if (m.misc)
493                         wrmsrl(MSR_AMD64_SMCA_MCx_MISC(b), m.misc);
494         } else {
495                 wrmsrl(MSR_IA32_MCx_STATUS(b), m.status);
496                 wrmsrl(MSR_IA32_MCx_ADDR(b), m.addr);
497 
498                 if (m.misc)
499                         wrmsrl(MSR_IA32_MCx_MISC(b), m.misc);
500         }
501 }
502 
503 static void do_inject(void)
504 {
505         u64 mcg_status = 0;
506         unsigned int cpu = i_mce.extcpu;
507         u8 b = i_mce.bank;
508 
509         i_mce.tsc = rdtsc_ordered();
510 
511         i_mce.status |= MCI_STATUS_VAL;
512 
513         if (i_mce.misc)
514                 i_mce.status |= MCI_STATUS_MISCV;
515 
516         if (i_mce.synd)
517                 i_mce.status |= MCI_STATUS_SYNDV;
518 
519         if (inj_type == SW_INJ) {
520                 mce_log(&i_mce);
521                 return;
522         }
523 
524         /* prep MCE global settings for the injection */
525         mcg_status = MCG_STATUS_MCIP | MCG_STATUS_EIPV;
526 
527         if (!(i_mce.status & MCI_STATUS_PCC))
528                 mcg_status |= MCG_STATUS_RIPV;
529 
530         /*
531          * Ensure necessary status bits for deferred errors:
532          * - MCx_STATUS[Deferred]: make sure it is a deferred error
533          * - MCx_STATUS[UC] cleared: deferred errors are _not_ UC
534          */
535         if (inj_type == DFR_INT_INJ) {
536                 i_mce.status |= MCI_STATUS_DEFERRED;
537                 i_mce.status &= ~MCI_STATUS_UC;
538         }
539 
540         /*
541          * For multi node CPUs, logging and reporting of bank 4 errors happens
542          * only on the node base core. Refer to D18F3x44[NbMcaToMstCpuEn] for
543          * Fam10h and later BKDGs.
544          */
545         if (boot_cpu_has(X86_FEATURE_AMD_DCM) &&
546             b == 4 &&
547             boot_cpu_data.x86 < 0x17) {
548                 toggle_nb_mca_mst_cpu(topology_amd_node_id(cpu));
549                 cpu = get_nbc_for_node(topology_amd_node_id(cpu));
550         }
551 
552         cpus_read_lock();
553         if (!cpu_online(cpu))
554                 goto err;
555 
556         toggle_hw_mce_inject(cpu, true);
557 
558         i_mce.mcgstatus = mcg_status;
559         i_mce.inject_flags = inj_type;
560         smp_call_function_single(cpu, prepare_msrs, &i_mce, 0);
561 
562         toggle_hw_mce_inject(cpu, false);
563 
564         switch (inj_type) {
565         case DFR_INT_INJ:
566                 smp_call_function_single(cpu, trigger_dfr_int, NULL, 0);
567                 break;
568         case THR_INT_INJ:
569                 smp_call_function_single(cpu, trigger_thr_int, NULL, 0);
570                 break;
571         default:
572                 smp_call_function_single(cpu, trigger_mce, NULL, 0);
573         }
574 
575 err:
576         cpus_read_unlock();
577 
578 }
579 
580 /*
581  * This denotes into which bank we're injecting and triggers
582  * the injection, at the same time.
583  */
584 static int inj_bank_set(void *data, u64 val)
585 {
586         struct mce *m = (struct mce *)data;
587         u8 n_banks;
588         u64 cap;
589 
590         /* Get bank count on target CPU so we can handle non-uniform values. */
591         rdmsrl_on_cpu(m->extcpu, MSR_IA32_MCG_CAP, &cap);
592         n_banks = cap & MCG_BANKCNT_MASK;
593 
594         if (val >= n_banks) {
595                 pr_err("MCA bank %llu non-existent on CPU%d\n", val, m->extcpu);
596                 return -EINVAL;
597         }
598 
599         m->bank = val;
600 
601         /*
602          * sw-only injection allows to write arbitrary values into the MCA
603          * registers because it tests only the decoding paths.
604          */
605         if (inj_type == SW_INJ)
606                 goto inject;
607 
608         /*
609          * Read IPID value to determine if a bank is populated on the target
610          * CPU.
611          */
612         if (cpu_feature_enabled(X86_FEATURE_SMCA)) {
613                 u64 ipid;
614 
615                 if (rdmsrl_on_cpu(m->extcpu, MSR_AMD64_SMCA_MCx_IPID(val), &ipid)) {
616                         pr_err("Error reading IPID on CPU%d\n", m->extcpu);
617                         return -EINVAL;
618                 }
619 
620                 if (!ipid) {
621                         pr_err("Cannot inject into unpopulated bank %llu\n", val);
622                         return -ENODEV;
623                 }
624         }
625 
626 inject:
627         do_inject();
628 
629         /* Reset injection struct */
630         setup_inj_struct(&i_mce);
631 
632         return 0;
633 }
634 
635 MCE_INJECT_GET(bank);
636 
637 DEFINE_SIMPLE_ATTRIBUTE(bank_fops, inj_bank_get, inj_bank_set, "%llu\n");
638 
639 static const char readme_msg[] =
640 "Description of the files and their usages:\n"
641 "\n"
642 "Note1: i refers to the bank number below.\n"
643 "Note2: See respective BKDGs for the exact bit definitions of the files below\n"
644 "as they mirror the hardware registers.\n"
645 "\n"
646 "status:\t Set MCi_STATUS: the bits in that MSR control the error type and\n"
647 "\t attributes of the error which caused the MCE.\n"
648 "\n"
649 "misc:\t Set MCi_MISC: provide auxiliary info about the error. It is mostly\n"
650 "\t used for error thresholding purposes and its validity is indicated by\n"
651 "\t MCi_STATUS[MiscV].\n"
652 "\n"
653 "synd:\t Set MCi_SYND: provide syndrome info about the error. Only valid on\n"
654 "\t Scalable MCA systems, and its validity is indicated by MCi_STATUS[SyndV].\n"
655 "\n"
656 "addr:\t Error address value to be written to MCi_ADDR. Log address information\n"
657 "\t associated with the error.\n"
658 "\n"
659 "cpu:\t The CPU to inject the error on.\n"
660 "\n"
661 "bank:\t Specify the bank you want to inject the error into: the number of\n"
662 "\t banks in a processor varies and is family/model-specific, therefore, the\n"
663 "\t supplied value is sanity-checked. Setting the bank value also triggers the\n"
664 "\t injection.\n"
665 "\n"
666 "flags:\t Injection type to be performed. Writing to this file will trigger a\n"
667 "\t real machine check, an APIC interrupt or invoke the error decoder routines\n"
668 "\t for AMD processors.\n"
669 "\n"
670 "\t Allowed error injection types:\n"
671 "\t  - \"sw\": Software error injection. Decode error to a human-readable \n"
672 "\t    format only. Safe to use.\n"
673 "\t  - \"hw\": Hardware error injection. Causes the #MC exception handler to \n"
674 "\t    handle the error. Be warned: might cause system panic if MCi_STATUS[PCC] \n"
675 "\t    is set. Therefore, consider setting (debugfs_mountpoint)/mce/fake_panic \n"
676 "\t    before injecting.\n"
677 "\t  - \"df\": Trigger APIC interrupt for Deferred error. Causes deferred \n"
678 "\t    error APIC interrupt handler to handle the error if the feature is \n"
679 "\t    is present in hardware. \n"
680 "\t  - \"th\": Trigger APIC interrupt for Threshold errors. Causes threshold \n"
681 "\t    APIC interrupt handler to handle the error. \n"
682 "\n"
683 "ipid:\t IPID (AMD-specific)\n"
684 "\n";
685 
686 static ssize_t
687 inj_readme_read(struct file *filp, char __user *ubuf,
688                        size_t cnt, loff_t *ppos)
689 {
690         return simple_read_from_buffer(ubuf, cnt, ppos,
691                                         readme_msg, strlen(readme_msg));
692 }
693 
694 static const struct file_operations readme_fops = {
695         .read           = inj_readme_read,
696 };
697 
698 static struct dfs_node {
699         char *name;
700         const struct file_operations *fops;
701         umode_t perm;
702 } dfs_fls[] = {
703         { .name = "status",     .fops = &status_fops, .perm = S_IRUSR | S_IWUSR },
704         { .name = "misc",       .fops = &misc_fops,   .perm = S_IRUSR | S_IWUSR },
705         { .name = "addr",       .fops = &addr_fops,   .perm = S_IRUSR | S_IWUSR },
706         { .name = "synd",       .fops = &synd_fops,   .perm = S_IRUSR | S_IWUSR },
707         { .name = "ipid",       .fops = &ipid_fops,   .perm = S_IRUSR | S_IWUSR },
708         { .name = "bank",       .fops = &bank_fops,   .perm = S_IRUSR | S_IWUSR },
709         { .name = "flags",      .fops = &flags_fops,  .perm = S_IRUSR | S_IWUSR },
710         { .name = "cpu",        .fops = &extcpu_fops, .perm = S_IRUSR | S_IWUSR },
711         { .name = "README",     .fops = &readme_fops, .perm = S_IRUSR | S_IRGRP | S_IROTH },
712 };
713 
714 static void __init debugfs_init(void)
715 {
716         unsigned int i;
717 
718         dfs_inj = debugfs_create_dir("mce-inject", NULL);
719 
720         for (i = 0; i < ARRAY_SIZE(dfs_fls); i++)
721                 debugfs_create_file(dfs_fls[i].name, dfs_fls[i].perm, dfs_inj,
722                                     &i_mce, dfs_fls[i].fops);
723 }
724 
725 static void check_hw_inj_possible(void)
726 {
727         int cpu;
728         u8 bank;
729 
730         /*
731          * This behavior exists only on SMCA systems though its not directly
732          * related to SMCA.
733          */
734         if (!cpu_feature_enabled(X86_FEATURE_SMCA))
735                 return;
736 
737         cpu = get_cpu();
738 
739         for (bank = 0; bank < MAX_NR_BANKS; ++bank) {
740                 u64 status = MCI_STATUS_VAL, ipid;
741 
742                 /* Check whether bank is populated */
743                 rdmsrl(MSR_AMD64_SMCA_MCx_IPID(bank), ipid);
744                 if (!ipid)
745                         continue;
746 
747                 toggle_hw_mce_inject(cpu, true);
748 
749                 wrmsrl_safe(mca_msr_reg(bank, MCA_STATUS), status);
750                 rdmsrl_safe(mca_msr_reg(bank, MCA_STATUS), &status);
751                 wrmsrl_safe(mca_msr_reg(bank, MCA_STATUS), 0);
752 
753                 if (!status) {
754                         hw_injection_possible = false;
755                         pr_warn("Platform does not allow *hardware* error injection."
756                                 "Try using APEI EINJ instead.\n");
757                 }
758 
759                 toggle_hw_mce_inject(cpu, false);
760 
761                 break;
762         }
763 
764         put_cpu();
765 }
766 
767 static int __init inject_init(void)
768 {
769         if (!alloc_cpumask_var(&mce_inject_cpumask, GFP_KERNEL))
770                 return -ENOMEM;
771 
772         check_hw_inj_possible();
773 
774         debugfs_init();
775 
776         register_nmi_handler(NMI_LOCAL, mce_raise_notify, 0, "mce_notify");
777         mce_register_injector_chain(&inject_nb);
778 
779         setup_inj_struct(&i_mce);
780 
781         pr_info("Machine check injector initialized\n");
782 
783         return 0;
784 }
785 
786 static void __exit inject_exit(void)
787 {
788 
789         mce_unregister_injector_chain(&inject_nb);
790         unregister_nmi_handler(NMI_LOCAL, "mce_notify");
791 
792         debugfs_remove_recursive(dfs_inj);
793         dfs_inj = NULL;
794 
795         memset(&dfs_fls, 0, sizeof(dfs_fls));
796 
797         free_cpumask_var(mce_inject_cpumask);
798 }
799 
800 module_init(inject_init);
801 module_exit(inject_exit);
802 MODULE_DESCRIPTION("Machine check injection support");
803 MODULE_LICENSE("GPL");
804 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php