aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/x86/Kconfig2
-rw-r--r--arch/x86/Makefile2
-rw-r--r--arch/x86/include/asm/processor.h5
-rw-r--r--arch/x86/kernel/cpu/mcheck/mce-inject.c542
-rw-r--r--arch/x86/ras/Kconfig11
-rw-r--r--arch/x86/ras/Makefile2
-rw-r--r--arch/x86/ras/mce_amd_inj.c492
7 files changed, 532 insertions, 524 deletions
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 0efb4c9497bc..4371b6b5cbe4 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -1082,7 +1082,7 @@ config X86_MCE_THRESHOLD
1082 def_bool y 1082 def_bool y
1083 1083
1084config X86_MCE_INJECT 1084config X86_MCE_INJECT
1085 depends on X86_MCE && X86_LOCAL_APIC && X86_MCELOG_LEGACY 1085 depends on X86_MCE && X86_LOCAL_APIC && DEBUG_FS
1086 tristate "Machine check injector support" 1086 tristate "Machine check injector support"
1087 ---help--- 1087 ---help---
1088 Provide support for injecting machine checks for testing purposes. 1088 Provide support for injecting machine checks for testing purposes.
diff --git a/arch/x86/Makefile b/arch/x86/Makefile
index bf240b920473..ad2db82e9953 100644
--- a/arch/x86/Makefile
+++ b/arch/x86/Makefile
@@ -257,8 +257,6 @@ drivers-$(CONFIG_PM) += arch/x86/power/
257 257
258drivers-$(CONFIG_FB) += arch/x86/video/ 258drivers-$(CONFIG_FB) += arch/x86/video/
259 259
260drivers-$(CONFIG_RAS) += arch/x86/ras/
261
262#### 260####
263# boot loader support. Several targets are kept for legacy purposes 261# boot loader support. Several targets are kept for legacy purposes
264 262
diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h
index 3cada998a402..71f6fba95aa6 100644
--- a/arch/x86/include/asm/processor.h
+++ b/arch/x86/include/asm/processor.h
@@ -901,8 +901,13 @@ static inline int mpx_disable_management(void)
901} 901}
902#endif /* CONFIG_X86_INTEL_MPX */ 902#endif /* CONFIG_X86_INTEL_MPX */
903 903
904#ifdef CONFIG_CPU_SUP_AMD
904extern u16 amd_get_nb_id(int cpu); 905extern u16 amd_get_nb_id(int cpu);
905extern u32 amd_get_nodes_per_socket(void); 906extern u32 amd_get_nodes_per_socket(void);
907#else
908static inline u16 amd_get_nb_id(int cpu) { return 0; }
909static inline u32 amd_get_nodes_per_socket(void) { return 0; }
910#endif
906 911
907static inline uint32_t hypervisor_cpuid_base(const char *sig, uint32_t leaves) 912static inline uint32_t hypervisor_cpuid_base(const char *sig, uint32_t leaves)
908{ 913{
diff --git a/arch/x86/kernel/cpu/mcheck/mce-inject.c b/arch/x86/kernel/cpu/mcheck/mce-inject.c
index 99165b206df3..7170186938e5 100644
--- a/arch/x86/kernel/cpu/mcheck/mce-inject.c
+++ b/arch/x86/kernel/cpu/mcheck/mce-inject.c
@@ -10,23 +10,108 @@
10 * Authors: 10 * Authors:
11 * Andi Kleen 11 * Andi Kleen
12 * Ying Huang 12 * Ying Huang
13 *
14 * The AMD part (from mce_amd_inj.c): a simple MCE injection facility
15 * for testing different aspects of the RAS code. This driver should be
16 * built as module so that it can be loaded on production kernels for
17 * testing purposes.
18 *
19 * This file may be distributed under the terms of the GNU General Public
20 * License version 2.
21 *
22 * Copyright (c) 2010-17: Borislav Petkov <bp@alien8.de>
23 * Advanced Micro Devices Inc.
13 */ 24 */
14#include <linux/uaccess.h> 25
15#include <linux/module.h> 26#include <linux/cpu.h>
16#include <linux/timer.h> 27#include <linux/debugfs.h>
17#include <linux/kernel.h> 28#include <linux/device.h>
18#include <linux/string.h>
19#include <linux/fs.h> 29#include <linux/fs.h>
20#include <linux/preempt.h> 30#include <linux/gfp.h>
21#include <linux/smp.h>
22#include <linux/notifier.h>
23#include <linux/kdebug.h> 31#include <linux/kdebug.h>
24#include <linux/cpu.h> 32#include <linux/kernel.h>
33#include <linux/kobject.h>
34#include <linux/module.h>
35#include <linux/notifier.h>
36#include <linux/pci.h>
37#include <linux/preempt.h>
25#include <linux/sched.h> 38#include <linux/sched.h>
26#include <linux/gfp.h> 39#include <linux/smp.h>
27#include <asm/mce.h> 40#include <linux/string.h>
41#include <linux/timer.h>
42#include <linux/uaccess.h>
43
44#include <asm/amd_nb.h>
28#include <asm/apic.h> 45#include <asm/apic.h>
46#include <asm/irq_vectors.h>
47#include <asm/mce.h>
29#include <asm/nmi.h> 48#include <asm/nmi.h>
49#include <asm/smp.h>
50
51#include "mce-internal.h"
52
53/*
54 * Collect all the MCi_XXX settings
55 */
56static struct mce i_mce;
57static struct dentry *dfs_inj;
58
59static u8 n_banks;
60
61#define MAX_FLAG_OPT_SIZE 3
62#define NBCFG 0x44
63
64enum injection_type {
65 SW_INJ = 0, /* SW injection, simply decode the error */
66 HW_INJ, /* Trigger a #MC */
67 DFR_INT_INJ, /* Trigger Deferred error interrupt */
68 THR_INT_INJ, /* Trigger threshold interrupt */
69 N_INJ_TYPES,
70};
71
72static const char * const flags_options[] = {
73 [SW_INJ] = "sw",
74 [HW_INJ] = "hw",
75 [DFR_INT_INJ] = "df",
76 [THR_INT_INJ] = "th",
77 NULL
78};
79
80/* Set default injection to SW_INJ */
81static enum injection_type inj_type = SW_INJ;
82
83#define MCE_INJECT_SET(reg) \
84static int inj_##reg##_set(void *data, u64 val) \
85{ \
86 struct mce *m = (struct mce *)data; \
87 \
88 m->reg = val; \
89 return 0; \
90}
91
92MCE_INJECT_SET(status);
93MCE_INJECT_SET(misc);
94MCE_INJECT_SET(addr);
95MCE_INJECT_SET(synd);
96
97#define MCE_INJECT_GET(reg) \
98static int inj_##reg##_get(void *data, u64 *val) \
99{ \
100 struct mce *m = (struct mce *)data; \
101 \
102 *val = m->reg; \
103 return 0; \
104}
105
106MCE_INJECT_GET(status);
107MCE_INJECT_GET(misc);
108MCE_INJECT_GET(addr);
109MCE_INJECT_GET(synd);
110
111DEFINE_SIMPLE_ATTRIBUTE(status_fops, inj_status_get, inj_status_set, "%llx\n");
112DEFINE_SIMPLE_ATTRIBUTE(misc_fops, inj_misc_get, inj_misc_set, "%llx\n");
113DEFINE_SIMPLE_ATTRIBUTE(addr_fops, inj_addr_get, inj_addr_set, "%llx\n");
114DEFINE_SIMPLE_ATTRIBUTE(synd_fops, inj_synd_get, inj_synd_set, "%llx\n");
30 115
31/* Update fake mce registers on current CPU. */ 116/* Update fake mce registers on current CPU. */
32static void inject_mce(struct mce *m) 117static void inject_mce(struct mce *m)
@@ -143,7 +228,7 @@ static int raise_local(void)
143 return ret; 228 return ret;
144} 229}
145 230
146static void raise_mce(struct mce *m) 231static void __maybe_unused raise_mce(struct mce *m)
147{ 232{
148 int context = MCJ_CTX(m->inject_flags); 233 int context = MCJ_CTX(m->inject_flags);
149 234
@@ -198,6 +283,7 @@ static void raise_mce(struct mce *m)
198 } 283 }
199} 284}
200 285
286#ifdef CONFIG_X86_MCELOG_LEGACY
201/* Error injection interface */ 287/* Error injection interface */
202static ssize_t mce_write(struct file *filp, const char __user *ubuf, 288static ssize_t mce_write(struct file *filp, const char __user *ubuf,
203 size_t usize, loff_t *off) 289 size_t usize, loff_t *off)
@@ -232,21 +318,445 @@ static ssize_t mce_write(struct file *filp, const char __user *ubuf,
232 mutex_unlock(&mce_inject_mutex); 318 mutex_unlock(&mce_inject_mutex);
233 return usize; 319 return usize;
234} 320}
321#endif /* CONFIG_X86_MCELOG_LEGACY */
322
323/*
324 * Caller needs to be make sure this cpu doesn't disappear
325 * from under us, i.e.: get_cpu/put_cpu.
326 */
327static int toggle_hw_mce_inject(unsigned int cpu, bool enable)
328{
329 u32 l, h;
330 int err;
331
332 err = rdmsr_on_cpu(cpu, MSR_K7_HWCR, &l, &h);
333 if (err) {
334 pr_err("%s: error reading HWCR\n", __func__);
335 return err;
336 }
337
338 enable ? (l |= BIT(18)) : (l &= ~BIT(18));
339
340 err = wrmsr_on_cpu(cpu, MSR_K7_HWCR, l, h);
341 if (err)
342 pr_err("%s: error writing HWCR\n", __func__);
343
344 return err;
345}
346
347static int __set_inj(const char *buf)
348{
349 int i;
350
351 for (i = 0; i < N_INJ_TYPES; i++) {
352 if (!strncmp(flags_options[i], buf, strlen(flags_options[i]))) {
353 inj_type = i;
354 return 0;
355 }
356 }
357 return -EINVAL;
358}
359
360static ssize_t flags_read(struct file *filp, char __user *ubuf,
361 size_t cnt, loff_t *ppos)
362{
363 char buf[MAX_FLAG_OPT_SIZE];
364 int n;
365
366 n = sprintf(buf, "%s\n", flags_options[inj_type]);
367
368 return simple_read_from_buffer(ubuf, cnt, ppos, buf, n);
369}
370
371static ssize_t flags_write(struct file *filp, const char __user *ubuf,
372 size_t cnt, loff_t *ppos)
373{
374 char buf[MAX_FLAG_OPT_SIZE], *__buf;
375 int err;
376
377 if (cnt > MAX_FLAG_OPT_SIZE)
378 return -EINVAL;
379
380 if (copy_from_user(&buf, ubuf, cnt))
381 return -EFAULT;
382
383 buf[cnt - 1] = 0;
384
385 /* strip whitespace */
386 __buf = strstrip(buf);
387
388 err = __set_inj(__buf);
389 if (err) {
390 pr_err("%s: Invalid flags value: %s\n", __func__, __buf);
391 return err;
392 }
393
394 *ppos += cnt;
395
396 return cnt;
397}
398
399static const struct file_operations flags_fops = {
400 .read = flags_read,
401 .write = flags_write,
402 .llseek = generic_file_llseek,
403};
404
405/*
406 * On which CPU to inject?
407 */
408MCE_INJECT_GET(extcpu);
409
410static int inj_extcpu_set(void *data, u64 val)
411{
412 struct mce *m = (struct mce *)data;
413
414 if (val >= nr_cpu_ids || !cpu_online(val)) {
415 pr_err("%s: Invalid CPU: %llu\n", __func__, val);
416 return -EINVAL;
417 }
418 m->extcpu = val;
419 return 0;
420}
421
422DEFINE_SIMPLE_ATTRIBUTE(extcpu_fops, inj_extcpu_get, inj_extcpu_set, "%llu\n");
423
424static void trigger_mce(void *info)
425{
426 asm volatile("int $18");
427}
428
429static void trigger_dfr_int(void *info)
430{
431 asm volatile("int %0" :: "i" (DEFERRED_ERROR_VECTOR));
432}
433
434static void trigger_thr_int(void *info)
435{
436 asm volatile("int %0" :: "i" (THRESHOLD_APIC_VECTOR));
437}
438
439static u32 get_nbc_for_node(int node_id)
440{
441 struct cpuinfo_x86 *c = &boot_cpu_data;
442 u32 cores_per_node;
443
444 cores_per_node = (c->x86_max_cores * smp_num_siblings) / amd_get_nodes_per_socket();
445
446 return cores_per_node * node_id;
447}
448
449static void toggle_nb_mca_mst_cpu(u16 nid)
450{
451 struct amd_northbridge *nb;
452 struct pci_dev *F3;
453 u32 val;
454 int err;
455
456 nb = node_to_amd_nb(nid);
457 if (!nb)
458 return;
459
460 F3 = nb->misc;
461 if (!F3)
462 return;
463
464 err = pci_read_config_dword(F3, NBCFG, &val);
465 if (err) {
466 pr_err("%s: Error reading F%dx%03x.\n",
467 __func__, PCI_FUNC(F3->devfn), NBCFG);
468 return;
469 }
470
471 if (val & BIT(27))
472 return;
473
474 pr_err("%s: Set D18F3x44[NbMcaToMstCpuEn] which BIOS hasn't done.\n",
475 __func__);
476
477 val |= BIT(27);
478 err = pci_write_config_dword(F3, NBCFG, val);
479 if (err)
480 pr_err("%s: Error writing F%dx%03x.\n",
481 __func__, PCI_FUNC(F3->devfn), NBCFG);
482}
235 483
236static int inject_init(void) 484static void prepare_msrs(void *info)
237{ 485{
486 struct mce m = *(struct mce *)info;
487 u8 b = m.bank;
488
489 wrmsrl(MSR_IA32_MCG_STATUS, m.mcgstatus);
490
491 if (boot_cpu_has(X86_FEATURE_SMCA)) {
492 if (m.inject_flags == DFR_INT_INJ) {
493 wrmsrl(MSR_AMD64_SMCA_MCx_DESTAT(b), m.status);
494 wrmsrl(MSR_AMD64_SMCA_MCx_DEADDR(b), m.addr);
495 } else {
496 wrmsrl(MSR_AMD64_SMCA_MCx_STATUS(b), m.status);
497 wrmsrl(MSR_AMD64_SMCA_MCx_ADDR(b), m.addr);
498 }
499
500 wrmsrl(MSR_AMD64_SMCA_MCx_MISC(b), m.misc);
501 wrmsrl(MSR_AMD64_SMCA_MCx_SYND(b), m.synd);
502 } else {
503 wrmsrl(MSR_IA32_MCx_STATUS(b), m.status);
504 wrmsrl(MSR_IA32_MCx_ADDR(b), m.addr);
505 wrmsrl(MSR_IA32_MCx_MISC(b), m.misc);
506 }
507}
508
509static void do_inject(void)
510{
511 u64 mcg_status = 0;
512 unsigned int cpu = i_mce.extcpu;
513 u8 b = i_mce.bank;
514
515 rdtscll(i_mce.tsc);
516
517 if (i_mce.misc)
518 i_mce.status |= MCI_STATUS_MISCV;
519
520 if (i_mce.synd)
521 i_mce.status |= MCI_STATUS_SYNDV;
522
523 if (inj_type == SW_INJ) {
524 mce_inject_log(&i_mce);
525 return;
526 }
527
528 /* prep MCE global settings for the injection */
529 mcg_status = MCG_STATUS_MCIP | MCG_STATUS_EIPV;
530
531 if (!(i_mce.status & MCI_STATUS_PCC))
532 mcg_status |= MCG_STATUS_RIPV;
533
534 /*
535 * Ensure necessary status bits for deferred errors:
536 * - MCx_STATUS[Deferred]: make sure it is a deferred error
537 * - MCx_STATUS[UC] cleared: deferred errors are _not_ UC
538 */
539 if (inj_type == DFR_INT_INJ) {
540 i_mce.status |= MCI_STATUS_DEFERRED;
541 i_mce.status |= (i_mce.status & ~MCI_STATUS_UC);
542 }
543
544 /*
545 * For multi node CPUs, logging and reporting of bank 4 errors happens
546 * only on the node base core. Refer to D18F3x44[NbMcaToMstCpuEn] for
547 * Fam10h and later BKDGs.
548 */
549 if (static_cpu_has(X86_FEATURE_AMD_DCM) &&
550 b == 4 &&
551 boot_cpu_data.x86 < 0x17) {
552 toggle_nb_mca_mst_cpu(amd_get_nb_id(cpu));
553 cpu = get_nbc_for_node(amd_get_nb_id(cpu));
554 }
555
556 get_online_cpus();
557 if (!cpu_online(cpu))
558 goto err;
559
560 toggle_hw_mce_inject(cpu, true);
561
562 i_mce.mcgstatus = mcg_status;
563 i_mce.inject_flags = inj_type;
564 smp_call_function_single(cpu, prepare_msrs, &i_mce, 0);
565
566 toggle_hw_mce_inject(cpu, false);
567
568 switch (inj_type) {
569 case DFR_INT_INJ:
570 smp_call_function_single(cpu, trigger_dfr_int, NULL, 0);
571 break;
572 case THR_INT_INJ:
573 smp_call_function_single(cpu, trigger_thr_int, NULL, 0);
574 break;
575 default:
576 smp_call_function_single(cpu, trigger_mce, NULL, 0);
577 }
578
579err:
580 put_online_cpus();
581
582}
583
584/*
585 * This denotes into which bank we're injecting and triggers
586 * the injection, at the same time.
587 */
588static int inj_bank_set(void *data, u64 val)
589{
590 struct mce *m = (struct mce *)data;
591
592 if (val >= n_banks) {
593 pr_err("Non-existent MCE bank: %llu\n", val);
594 return -EINVAL;
595 }
596
597 m->bank = val;
598 do_inject();
599
600 return 0;
601}
602
603MCE_INJECT_GET(bank);
604
605DEFINE_SIMPLE_ATTRIBUTE(bank_fops, inj_bank_get, inj_bank_set, "%llu\n");
606
607static const char readme_msg[] =
608"Description of the files and their usages:\n"
609"\n"
610"Note1: i refers to the bank number below.\n"
611"Note2: See respective BKDGs for the exact bit definitions of the files below\n"
612"as they mirror the hardware registers.\n"
613"\n"
614"status:\t Set MCi_STATUS: the bits in that MSR control the error type and\n"
615"\t attributes of the error which caused the MCE.\n"
616"\n"
617"misc:\t Set MCi_MISC: provide auxiliary info about the error. It is mostly\n"
618"\t used for error thresholding purposes and its validity is indicated by\n"
619"\t MCi_STATUS[MiscV].\n"
620"\n"
621"synd:\t Set MCi_SYND: provide syndrome info about the error. Only valid on\n"
622"\t Scalable MCA systems, and its validity is indicated by MCi_STATUS[SyndV].\n"
623"\n"
624"addr:\t Error address value to be written to MCi_ADDR. Log address information\n"
625"\t associated with the error.\n"
626"\n"
627"cpu:\t The CPU to inject the error on.\n"
628"\n"
629"bank:\t Specify the bank you want to inject the error into: the number of\n"
630"\t banks in a processor varies and is family/model-specific, therefore, the\n"
631"\t supplied value is sanity-checked. Setting the bank value also triggers the\n"
632"\t injection.\n"
633"\n"
634"flags:\t Injection type to be performed. Writing to this file will trigger a\n"
635"\t real machine check, an APIC interrupt or invoke the error decoder routines\n"
636"\t for AMD processors.\n"
637"\n"
638"\t Allowed error injection types:\n"
639"\t - \"sw\": Software error injection. Decode error to a human-readable \n"
640"\t format only. Safe to use.\n"
641"\t - \"hw\": Hardware error injection. Causes the #MC exception handler to \n"
642"\t handle the error. Be warned: might cause system panic if MCi_STATUS[PCC] \n"
643"\t is set. Therefore, consider setting (debugfs_mountpoint)/mce/fake_panic \n"
644"\t before injecting.\n"
645"\t - \"df\": Trigger APIC interrupt for Deferred error. Causes deferred \n"
646"\t error APIC interrupt handler to handle the error if the feature is \n"
647"\t is present in hardware. \n"
648"\t - \"th\": Trigger APIC interrupt for Threshold errors. Causes threshold \n"
649"\t APIC interrupt handler to handle the error. \n"
650"\n";
651
652static ssize_t
653inj_readme_read(struct file *filp, char __user *ubuf,
654 size_t cnt, loff_t *ppos)
655{
656 return simple_read_from_buffer(ubuf, cnt, ppos,
657 readme_msg, strlen(readme_msg));
658}
659
660static const struct file_operations readme_fops = {
661 .read = inj_readme_read,
662};
663
664static struct dfs_node {
665 char *name;
666 struct dentry *d;
667 const struct file_operations *fops;
668 umode_t perm;
669} dfs_fls[] = {
670 { .name = "status", .fops = &status_fops, .perm = S_IRUSR | S_IWUSR },
671 { .name = "misc", .fops = &misc_fops, .perm = S_IRUSR | S_IWUSR },
672 { .name = "addr", .fops = &addr_fops, .perm = S_IRUSR | S_IWUSR },
673 { .name = "synd", .fops = &synd_fops, .perm = S_IRUSR | S_IWUSR },
674 { .name = "bank", .fops = &bank_fops, .perm = S_IRUSR | S_IWUSR },
675 { .name = "flags", .fops = &flags_fops, .perm = S_IRUSR | S_IWUSR },
676 { .name = "cpu", .fops = &extcpu_fops, .perm = S_IRUSR | S_IWUSR },
677 { .name = "README", .fops = &readme_fops, .perm = S_IRUSR | S_IRGRP | S_IROTH },
678};
679
680static int __init debugfs_init(void)
681{
682 unsigned int i;
683 u64 cap;
684
685 rdmsrl(MSR_IA32_MCG_CAP, cap);
686 n_banks = cap & MCG_BANKCNT_MASK;
687
688 dfs_inj = debugfs_create_dir("mce-inject", NULL);
689 if (!dfs_inj)
690 return -EINVAL;
691
692 for (i = 0; i < ARRAY_SIZE(dfs_fls); i++) {
693 dfs_fls[i].d = debugfs_create_file(dfs_fls[i].name,
694 dfs_fls[i].perm,
695 dfs_inj,
696 &i_mce,
697 dfs_fls[i].fops);
698
699 if (!dfs_fls[i].d)
700 goto err_dfs_add;
701 }
702
703 return 0;
704
705err_dfs_add:
706 while (i-- > 0)
707 debugfs_remove(dfs_fls[i].d);
708
709 debugfs_remove(dfs_inj);
710 dfs_inj = NULL;
711
712 return -ENODEV;
713}
714
715static int __init inject_init(void)
716{
717 int err;
718
238 if (!alloc_cpumask_var(&mce_inject_cpumask, GFP_KERNEL)) 719 if (!alloc_cpumask_var(&mce_inject_cpumask, GFP_KERNEL))
239 return -ENOMEM; 720 return -ENOMEM;
721
722#ifdef CONFIG_X86_MCELOG_LEGACY
723 register_mce_write_callback(mce_write);
724#endif
725
726 register_nmi_handler(NMI_LOCAL, mce_raise_notify, 0, "mce_notify");
727
728 err = debugfs_init();
729 if (err) {
730 free_cpumask_var(mce_inject_cpumask);
731 return err;
732 }
733
240 pr_info("Machine check injector initialized\n"); 734 pr_info("Machine check injector initialized\n");
241 register_mce_write_callback(mce_write); 735
242 register_nmi_handler(NMI_LOCAL, mce_raise_notify, 0,
243 "mce_notify");
244 return 0; 736 return 0;
245} 737}
246 738
247module_init(inject_init); 739module_init(inject_init);
740
248/* 741/*
249 * Cannot tolerate unloading currently because we cannot 742 * Cannot tolerate unloading currently because we cannot
250 * guarantee all openers of mce_chrdev will get a reference to us. 743 * guarantee all openers of mce_chrdev will get a reference to us.
251 */ 744 */
745#ifndef CONFIG_X86_MCELOG_LEGACY
746static void __exit inject_exit(void)
747{
748
749 debugfs_remove_recursive(dfs_inj);
750 dfs_inj = NULL;
751
752 memset(&dfs_fls, 0, sizeof(dfs_fls));
753
754 unregister_nmi_handler(NMI_LOCAL, "mce_notify");
755
756 free_cpumask_var(mce_inject_cpumask);
757}
758
759module_exit(inject_exit);
760#endif
761
252MODULE_LICENSE("GPL"); 762MODULE_LICENSE("GPL");
diff --git a/arch/x86/ras/Kconfig b/arch/x86/ras/Kconfig
index 2a2d89d39af6..bb026699ad19 100644
--- a/arch/x86/ras/Kconfig
+++ b/arch/x86/ras/Kconfig
@@ -1,13 +1,3 @@
1config MCE_AMD_INJ
2 tristate "Simple MCE injection interface for AMD processors"
3 depends on RAS && X86_MCE && DEBUG_FS && AMD_NB
4 default n
5 help
6 This is a simple debugfs interface to inject MCEs and test different
7 aspects of the MCE handling code.
8
9 WARNING: Do not even assume this interface is staying stable!
10
11config RAS_CEC 1config RAS_CEC
12 bool "Correctable Errors Collector" 2 bool "Correctable Errors Collector"
13 depends on X86_MCE && MEMORY_FAILURE && DEBUG_FS 3 depends on X86_MCE && MEMORY_FAILURE && DEBUG_FS
@@ -20,4 +10,3 @@ config RAS_CEC
20 10
21 Bear in mind that this is absolutely useless if your platform doesn't 11 Bear in mind that this is absolutely useless if your platform doesn't
22 have ECC DIMMs and doesn't have DRAM ECC checking enabled in the BIOS. 12 have ECC DIMMs and doesn't have DRAM ECC checking enabled in the BIOS.
23
diff --git a/arch/x86/ras/Makefile b/arch/x86/ras/Makefile
deleted file mode 100644
index 5f94546db280..000000000000
--- a/arch/x86/ras/Makefile
+++ /dev/null
@@ -1,2 +0,0 @@
1obj-$(CONFIG_MCE_AMD_INJ) += mce_amd_inj.o
2
diff --git a/arch/x86/ras/mce_amd_inj.c b/arch/x86/ras/mce_amd_inj.c
deleted file mode 100644
index 8730c2882fff..000000000000
--- a/arch/x86/ras/mce_amd_inj.c
+++ /dev/null
@@ -1,492 +0,0 @@
1/*
2 * A simple MCE injection facility for testing different aspects of the RAS
3 * code. This driver should be built as module so that it can be loaded
4 * on production kernels for testing purposes.
5 *
6 * This file may be distributed under the terms of the GNU General Public
7 * License version 2.
8 *
9 * Copyright (c) 2010-15: Borislav Petkov <bp@alien8.de>
10 * Advanced Micro Devices Inc.
11 */
12
13#include <linux/kobject.h>
14#include <linux/debugfs.h>
15#include <linux/device.h>
16#include <linux/module.h>
17#include <linux/cpu.h>
18#include <linux/string.h>
19#include <linux/uaccess.h>
20#include <linux/pci.h>
21
22#include <asm/mce.h>
23#include <asm/smp.h>
24#include <asm/amd_nb.h>
25#include <asm/irq_vectors.h>
26
27#include "../kernel/cpu/mcheck/mce-internal.h"
28
29/*
30 * Collect all the MCi_XXX settings
31 */
32static struct mce i_mce;
33static struct dentry *dfs_inj;
34
35static u8 n_banks;
36
37#define MAX_FLAG_OPT_SIZE 3
38#define NBCFG 0x44
39
40enum injection_type {
41 SW_INJ = 0, /* SW injection, simply decode the error */
42 HW_INJ, /* Trigger a #MC */
43 DFR_INT_INJ, /* Trigger Deferred error interrupt */
44 THR_INT_INJ, /* Trigger threshold interrupt */
45 N_INJ_TYPES,
46};
47
48static const char * const flags_options[] = {
49 [SW_INJ] = "sw",
50 [HW_INJ] = "hw",
51 [DFR_INT_INJ] = "df",
52 [THR_INT_INJ] = "th",
53 NULL
54};
55
56/* Set default injection to SW_INJ */
57static enum injection_type inj_type = SW_INJ;
58
59#define MCE_INJECT_SET(reg) \
60static int inj_##reg##_set(void *data, u64 val) \
61{ \
62 struct mce *m = (struct mce *)data; \
63 \
64 m->reg = val; \
65 return 0; \
66}
67
68MCE_INJECT_SET(status);
69MCE_INJECT_SET(misc);
70MCE_INJECT_SET(addr);
71MCE_INJECT_SET(synd);
72
73#define MCE_INJECT_GET(reg) \
74static int inj_##reg##_get(void *data, u64 *val) \
75{ \
76 struct mce *m = (struct mce *)data; \
77 \
78 *val = m->reg; \
79 return 0; \
80}
81
82MCE_INJECT_GET(status);
83MCE_INJECT_GET(misc);
84MCE_INJECT_GET(addr);
85MCE_INJECT_GET(synd);
86
87DEFINE_SIMPLE_ATTRIBUTE(status_fops, inj_status_get, inj_status_set, "%llx\n");
88DEFINE_SIMPLE_ATTRIBUTE(misc_fops, inj_misc_get, inj_misc_set, "%llx\n");
89DEFINE_SIMPLE_ATTRIBUTE(addr_fops, inj_addr_get, inj_addr_set, "%llx\n");
90DEFINE_SIMPLE_ATTRIBUTE(synd_fops, inj_synd_get, inj_synd_set, "%llx\n");
91
92/*
93 * Caller needs to be make sure this cpu doesn't disappear
94 * from under us, i.e.: get_cpu/put_cpu.
95 */
96static int toggle_hw_mce_inject(unsigned int cpu, bool enable)
97{
98 u32 l, h;
99 int err;
100
101 err = rdmsr_on_cpu(cpu, MSR_K7_HWCR, &l, &h);
102 if (err) {
103 pr_err("%s: error reading HWCR\n", __func__);
104 return err;
105 }
106
107 enable ? (l |= BIT(18)) : (l &= ~BIT(18));
108
109 err = wrmsr_on_cpu(cpu, MSR_K7_HWCR, l, h);
110 if (err)
111 pr_err("%s: error writing HWCR\n", __func__);
112
113 return err;
114}
115
116static int __set_inj(const char *buf)
117{
118 int i;
119
120 for (i = 0; i < N_INJ_TYPES; i++) {
121 if (!strncmp(flags_options[i], buf, strlen(flags_options[i]))) {
122 inj_type = i;
123 return 0;
124 }
125 }
126 return -EINVAL;
127}
128
129static ssize_t flags_read(struct file *filp, char __user *ubuf,
130 size_t cnt, loff_t *ppos)
131{
132 char buf[MAX_FLAG_OPT_SIZE];
133 int n;
134
135 n = sprintf(buf, "%s\n", flags_options[inj_type]);
136
137 return simple_read_from_buffer(ubuf, cnt, ppos, buf, n);
138}
139
140static ssize_t flags_write(struct file *filp, const char __user *ubuf,
141 size_t cnt, loff_t *ppos)
142{
143 char buf[MAX_FLAG_OPT_SIZE], *__buf;
144 int err;
145
146 if (cnt > MAX_FLAG_OPT_SIZE)
147 return -EINVAL;
148
149 if (copy_from_user(&buf, ubuf, cnt))
150 return -EFAULT;
151
152 buf[cnt - 1] = 0;
153
154 /* strip whitespace */
155 __buf = strstrip(buf);
156
157 err = __set_inj(__buf);
158 if (err) {
159 pr_err("%s: Invalid flags value: %s\n", __func__, __buf);
160 return err;
161 }
162
163 *ppos += cnt;
164
165 return cnt;
166}
167
168static const struct file_operations flags_fops = {
169 .read = flags_read,
170 .write = flags_write,
171 .llseek = generic_file_llseek,
172};
173
174/*
175 * On which CPU to inject?
176 */
177MCE_INJECT_GET(extcpu);
178
179static int inj_extcpu_set(void *data, u64 val)
180{
181 struct mce *m = (struct mce *)data;
182
183 if (val >= nr_cpu_ids || !cpu_online(val)) {
184 pr_err("%s: Invalid CPU: %llu\n", __func__, val);
185 return -EINVAL;
186 }
187 m->extcpu = val;
188 return 0;
189}
190
191DEFINE_SIMPLE_ATTRIBUTE(extcpu_fops, inj_extcpu_get, inj_extcpu_set, "%llu\n");
192
193static void trigger_mce(void *info)
194{
195 asm volatile("int $18");
196}
197
198static void trigger_dfr_int(void *info)
199{
200 asm volatile("int %0" :: "i" (DEFERRED_ERROR_VECTOR));
201}
202
203static void trigger_thr_int(void *info)
204{
205 asm volatile("int %0" :: "i" (THRESHOLD_APIC_VECTOR));
206}
207
208static u32 get_nbc_for_node(int node_id)
209{
210 struct cpuinfo_x86 *c = &boot_cpu_data;
211 u32 cores_per_node;
212
213 cores_per_node = (c->x86_max_cores * smp_num_siblings) / amd_get_nodes_per_socket();
214
215 return cores_per_node * node_id;
216}
217
218static void toggle_nb_mca_mst_cpu(u16 nid)
219{
220 struct pci_dev *F3 = node_to_amd_nb(nid)->misc;
221 u32 val;
222 int err;
223
224 if (!F3)
225 return;
226
227 err = pci_read_config_dword(F3, NBCFG, &val);
228 if (err) {
229 pr_err("%s: Error reading F%dx%03x.\n",
230 __func__, PCI_FUNC(F3->devfn), NBCFG);
231 return;
232 }
233
234 if (val & BIT(27))
235 return;
236
237 pr_err("%s: Set D18F3x44[NbMcaToMstCpuEn] which BIOS hasn't done.\n",
238 __func__);
239
240 val |= BIT(27);
241 err = pci_write_config_dword(F3, NBCFG, val);
242 if (err)
243 pr_err("%s: Error writing F%dx%03x.\n",
244 __func__, PCI_FUNC(F3->devfn), NBCFG);
245}
246
247static void prepare_msrs(void *info)
248{
249 struct mce m = *(struct mce *)info;
250 u8 b = m.bank;
251
252 wrmsrl(MSR_IA32_MCG_STATUS, m.mcgstatus);
253
254 if (boot_cpu_has(X86_FEATURE_SMCA)) {
255 if (m.inject_flags == DFR_INT_INJ) {
256 wrmsrl(MSR_AMD64_SMCA_MCx_DESTAT(b), m.status);
257 wrmsrl(MSR_AMD64_SMCA_MCx_DEADDR(b), m.addr);
258 } else {
259 wrmsrl(MSR_AMD64_SMCA_MCx_STATUS(b), m.status);
260 wrmsrl(MSR_AMD64_SMCA_MCx_ADDR(b), m.addr);
261 }
262
263 wrmsrl(MSR_AMD64_SMCA_MCx_MISC(b), m.misc);
264 wrmsrl(MSR_AMD64_SMCA_MCx_SYND(b), m.synd);
265 } else {
266 wrmsrl(MSR_IA32_MCx_STATUS(b), m.status);
267 wrmsrl(MSR_IA32_MCx_ADDR(b), m.addr);
268 wrmsrl(MSR_IA32_MCx_MISC(b), m.misc);
269 }
270}
271
272static void do_inject(void)
273{
274 u64 mcg_status = 0;
275 unsigned int cpu = i_mce.extcpu;
276 u8 b = i_mce.bank;
277
278 rdtscll(i_mce.tsc);
279
280 if (i_mce.misc)
281 i_mce.status |= MCI_STATUS_MISCV;
282
283 if (i_mce.synd)
284 i_mce.status |= MCI_STATUS_SYNDV;
285
286 if (inj_type == SW_INJ) {
287 mce_inject_log(&i_mce);
288 return;
289 }
290
291 /* prep MCE global settings for the injection */
292 mcg_status = MCG_STATUS_MCIP | MCG_STATUS_EIPV;
293
294 if (!(i_mce.status & MCI_STATUS_PCC))
295 mcg_status |= MCG_STATUS_RIPV;
296
297 /*
298 * Ensure necessary status bits for deferred errors:
299 * - MCx_STATUS[Deferred]: make sure it is a deferred error
300 * - MCx_STATUS[UC] cleared: deferred errors are _not_ UC
301 */
302 if (inj_type == DFR_INT_INJ) {
303 i_mce.status |= MCI_STATUS_DEFERRED;
304 i_mce.status |= (i_mce.status & ~MCI_STATUS_UC);
305 }
306
307 /*
308 * For multi node CPUs, logging and reporting of bank 4 errors happens
309 * only on the node base core. Refer to D18F3x44[NbMcaToMstCpuEn] for
310 * Fam10h and later BKDGs.
311 */
312 if (static_cpu_has(X86_FEATURE_AMD_DCM) &&
313 b == 4 &&
314 boot_cpu_data.x86 < 0x17) {
315 toggle_nb_mca_mst_cpu(amd_get_nb_id(cpu));
316 cpu = get_nbc_for_node(amd_get_nb_id(cpu));
317 }
318
319 get_online_cpus();
320 if (!cpu_online(cpu))
321 goto err;
322
323 toggle_hw_mce_inject(cpu, true);
324
325 i_mce.mcgstatus = mcg_status;
326 i_mce.inject_flags = inj_type;
327 smp_call_function_single(cpu, prepare_msrs, &i_mce, 0);
328
329 toggle_hw_mce_inject(cpu, false);
330
331 switch (inj_type) {
332 case DFR_INT_INJ:
333 smp_call_function_single(cpu, trigger_dfr_int, NULL, 0);
334 break;
335 case THR_INT_INJ:
336 smp_call_function_single(cpu, trigger_thr_int, NULL, 0);
337 break;
338 default:
339 smp_call_function_single(cpu, trigger_mce, NULL, 0);
340 }
341
342err:
343 put_online_cpus();
344
345}
346
347/*
348 * This denotes into which bank we're injecting and triggers
349 * the injection, at the same time.
350 */
351static int inj_bank_set(void *data, u64 val)
352{
353 struct mce *m = (struct mce *)data;
354
355 if (val >= n_banks) {
356 pr_err("Non-existent MCE bank: %llu\n", val);
357 return -EINVAL;
358 }
359
360 m->bank = val;
361 do_inject();
362
363 return 0;
364}
365
366MCE_INJECT_GET(bank);
367
368DEFINE_SIMPLE_ATTRIBUTE(bank_fops, inj_bank_get, inj_bank_set, "%llu\n");
369
370static const char readme_msg[] =
371"Description of the files and their usages:\n"
372"\n"
373"Note1: i refers to the bank number below.\n"
374"Note2: See respective BKDGs for the exact bit definitions of the files below\n"
375"as they mirror the hardware registers.\n"
376"\n"
377"status:\t Set MCi_STATUS: the bits in that MSR control the error type and\n"
378"\t attributes of the error which caused the MCE.\n"
379"\n"
380"misc:\t Set MCi_MISC: provide auxiliary info about the error. It is mostly\n"
381"\t used for error thresholding purposes and its validity is indicated by\n"
382"\t MCi_STATUS[MiscV].\n"
383"\n"
384"synd:\t Set MCi_SYND: provide syndrome info about the error. Only valid on\n"
385"\t Scalable MCA systems, and its validity is indicated by MCi_STATUS[SyndV].\n"
386"\n"
387"addr:\t Error address value to be written to MCi_ADDR. Log address information\n"
388"\t associated with the error.\n"
389"\n"
390"cpu:\t The CPU to inject the error on.\n"
391"\n"
392"bank:\t Specify the bank you want to inject the error into: the number of\n"
393"\t banks in a processor varies and is family/model-specific, therefore, the\n"
394"\t supplied value is sanity-checked. Setting the bank value also triggers the\n"
395"\t injection.\n"
396"\n"
397"flags:\t Injection type to be performed. Writing to this file will trigger a\n"
398"\t real machine check, an APIC interrupt or invoke the error decoder routines\n"
399"\t for AMD processors.\n"
400"\n"
401"\t Allowed error injection types:\n"
402"\t - \"sw\": Software error injection. Decode error to a human-readable \n"
403"\t format only. Safe to use.\n"
404"\t - \"hw\": Hardware error injection. Causes the #MC exception handler to \n"
405"\t handle the error. Be warned: might cause system panic if MCi_STATUS[PCC] \n"
406"\t is set. Therefore, consider setting (debugfs_mountpoint)/mce/fake_panic \n"
407"\t before injecting.\n"
408"\t - \"df\": Trigger APIC interrupt for Deferred error. Causes deferred \n"
409"\t error APIC interrupt handler to handle the error if the feature is \n"
410"\t is present in hardware. \n"
411"\t - \"th\": Trigger APIC interrupt for Threshold errors. Causes threshold \n"
412"\t APIC interrupt handler to handle the error. \n"
413"\n";
414
415static ssize_t
416inj_readme_read(struct file *filp, char __user *ubuf,
417 size_t cnt, loff_t *ppos)
418{
419 return simple_read_from_buffer(ubuf, cnt, ppos,
420 readme_msg, strlen(readme_msg));
421}
422
423static const struct file_operations readme_fops = {
424 .read = inj_readme_read,
425};
426
427static struct dfs_node {
428 char *name;
429 struct dentry *d;
430 const struct file_operations *fops;
431 umode_t perm;
432} dfs_fls[] = {
433 { .name = "status", .fops = &status_fops, .perm = S_IRUSR | S_IWUSR },
434 { .name = "misc", .fops = &misc_fops, .perm = S_IRUSR | S_IWUSR },
435 { .name = "addr", .fops = &addr_fops, .perm = S_IRUSR | S_IWUSR },
436 { .name = "synd", .fops = &synd_fops, .perm = S_IRUSR | S_IWUSR },
437 { .name = "bank", .fops = &bank_fops, .perm = S_IRUSR | S_IWUSR },
438 { .name = "flags", .fops = &flags_fops, .perm = S_IRUSR | S_IWUSR },
439 { .name = "cpu", .fops = &extcpu_fops, .perm = S_IRUSR | S_IWUSR },
440 { .name = "README", .fops = &readme_fops, .perm = S_IRUSR | S_IRGRP | S_IROTH },
441};
442
443static int __init init_mce_inject(void)
444{
445 unsigned int i;
446 u64 cap;
447
448 rdmsrl(MSR_IA32_MCG_CAP, cap);
449 n_banks = cap & MCG_BANKCNT_MASK;
450
451 dfs_inj = debugfs_create_dir("mce-inject", NULL);
452 if (!dfs_inj)
453 return -EINVAL;
454
455 for (i = 0; i < ARRAY_SIZE(dfs_fls); i++) {
456 dfs_fls[i].d = debugfs_create_file(dfs_fls[i].name,
457 dfs_fls[i].perm,
458 dfs_inj,
459 &i_mce,
460 dfs_fls[i].fops);
461
462 if (!dfs_fls[i].d)
463 goto err_dfs_add;
464 }
465
466 return 0;
467
468err_dfs_add:
469 while (i-- > 0)
470 debugfs_remove(dfs_fls[i].d);
471
472 debugfs_remove(dfs_inj);
473 dfs_inj = NULL;
474
475 return -ENODEV;
476}
477
478static void __exit exit_mce_inject(void)
479{
480
481 debugfs_remove_recursive(dfs_inj);
482 dfs_inj = NULL;
483
484 memset(&dfs_fls, 0, sizeof(dfs_fls));
485}
486module_init(init_mce_inject);
487module_exit(exit_mce_inject);
488
489MODULE_LICENSE("GPL");
490MODULE_AUTHOR("Borislav Petkov <bp@alien8.de>");
491MODULE_AUTHOR("AMD Inc.");
492MODULE_DESCRIPTION("MCE injection facility for RAS testing");