mce_amd_inj.c 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  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-14: Borislav Petkov <bp@alien8.de>
  10. * Advanced Micro Devices Inc.
  11. */
  12. #include <linux/kobject.h>
  13. #include <linux/debugfs.h>
  14. #include <linux/device.h>
  15. #include <linux/module.h>
  16. #include <linux/cpu.h>
  17. #include <asm/mce.h>
  18. #include "mce_amd.h"
  19. /*
  20. * Collect all the MCi_XXX settings
  21. */
  22. static struct mce i_mce;
  23. static struct dentry *dfs_inj;
  24. #define MCE_INJECT_SET(reg) \
  25. static int inj_##reg##_set(void *data, u64 val) \
  26. { \
  27. struct mce *m = (struct mce *)data; \
  28. \
  29. m->reg = val; \
  30. return 0; \
  31. }
  32. MCE_INJECT_SET(status);
  33. MCE_INJECT_SET(misc);
  34. MCE_INJECT_SET(addr);
  35. #define MCE_INJECT_GET(reg) \
  36. static int inj_##reg##_get(void *data, u64 *val) \
  37. { \
  38. struct mce *m = (struct mce *)data; \
  39. \
  40. *val = m->reg; \
  41. return 0; \
  42. }
  43. MCE_INJECT_GET(status);
  44. MCE_INJECT_GET(misc);
  45. MCE_INJECT_GET(addr);
  46. DEFINE_SIMPLE_ATTRIBUTE(status_fops, inj_status_get, inj_status_set, "%llx\n");
  47. DEFINE_SIMPLE_ATTRIBUTE(misc_fops, inj_misc_get, inj_misc_set, "%llx\n");
  48. DEFINE_SIMPLE_ATTRIBUTE(addr_fops, inj_addr_get, inj_addr_set, "%llx\n");
  49. /*
  50. * Caller needs to be make sure this cpu doesn't disappear
  51. * from under us, i.e.: get_cpu/put_cpu.
  52. */
  53. static int toggle_hw_mce_inject(unsigned int cpu, bool enable)
  54. {
  55. u32 l, h;
  56. int err;
  57. err = rdmsr_on_cpu(cpu, MSR_K7_HWCR, &l, &h);
  58. if (err) {
  59. pr_err("%s: error reading HWCR\n", __func__);
  60. return err;
  61. }
  62. enable ? (l |= BIT(18)) : (l &= ~BIT(18));
  63. err = wrmsr_on_cpu(cpu, MSR_K7_HWCR, l, h);
  64. if (err)
  65. pr_err("%s: error writing HWCR\n", __func__);
  66. return err;
  67. }
  68. static int flags_get(void *data, u64 *val)
  69. {
  70. struct mce *m = (struct mce *)data;
  71. *val = m->inject_flags;
  72. return 0;
  73. }
  74. static int flags_set(void *data, u64 val)
  75. {
  76. struct mce *m = (struct mce *)data;
  77. m->inject_flags = (u8)val;
  78. return 0;
  79. }
  80. DEFINE_SIMPLE_ATTRIBUTE(flags_fops, flags_get, flags_set, "%llu\n");
  81. /*
  82. * On which CPU to inject?
  83. */
  84. MCE_INJECT_GET(extcpu);
  85. static int inj_extcpu_set(void *data, u64 val)
  86. {
  87. struct mce *m = (struct mce *)data;
  88. if (val >= nr_cpu_ids || !cpu_online(val)) {
  89. pr_err("%s: Invalid CPU: %llu\n", __func__, val);
  90. return -EINVAL;
  91. }
  92. m->extcpu = val;
  93. return 0;
  94. }
  95. DEFINE_SIMPLE_ATTRIBUTE(extcpu_fops, inj_extcpu_get, inj_extcpu_set, "%llu\n");
  96. static void trigger_mce(void *info)
  97. {
  98. asm volatile("int $18");
  99. }
  100. static void do_inject(void)
  101. {
  102. u64 mcg_status = 0;
  103. unsigned int cpu = i_mce.extcpu;
  104. u8 b = i_mce.bank;
  105. if (!(i_mce.inject_flags & MCJ_EXCEPTION)) {
  106. amd_decode_mce(NULL, 0, &i_mce);
  107. return;
  108. }
  109. get_online_cpus();
  110. if (!cpu_online(cpu))
  111. goto err;
  112. /* prep MCE global settings for the injection */
  113. mcg_status = MCG_STATUS_MCIP | MCG_STATUS_EIPV;
  114. if (!(i_mce.status & MCI_STATUS_PCC))
  115. mcg_status |= MCG_STATUS_RIPV;
  116. toggle_hw_mce_inject(cpu, true);
  117. wrmsr_on_cpu(cpu, MSR_IA32_MCG_STATUS,
  118. (u32)mcg_status, (u32)(mcg_status >> 32));
  119. wrmsr_on_cpu(cpu, MSR_IA32_MCx_STATUS(b),
  120. (u32)i_mce.status, (u32)(i_mce.status >> 32));
  121. wrmsr_on_cpu(cpu, MSR_IA32_MCx_ADDR(b),
  122. (u32)i_mce.addr, (u32)(i_mce.addr >> 32));
  123. wrmsr_on_cpu(cpu, MSR_IA32_MCx_MISC(b),
  124. (u32)i_mce.misc, (u32)(i_mce.misc >> 32));
  125. toggle_hw_mce_inject(cpu, false);
  126. smp_call_function_single(cpu, trigger_mce, NULL, 0);
  127. err:
  128. put_online_cpus();
  129. }
  130. /*
  131. * This denotes into which bank we're injecting and triggers
  132. * the injection, at the same time.
  133. */
  134. static int inj_bank_set(void *data, u64 val)
  135. {
  136. struct mce *m = (struct mce *)data;
  137. if (val > 5) {
  138. if (boot_cpu_data.x86 != 0x15 || val > 6) {
  139. pr_err("Non-existent MCE bank: %llu\n", val);
  140. return -EINVAL;
  141. }
  142. }
  143. m->bank = val;
  144. do_inject();
  145. return 0;
  146. }
  147. static int inj_bank_get(void *data, u64 *val)
  148. {
  149. struct mce *m = (struct mce *)data;
  150. *val = m->bank;
  151. return 0;
  152. }
  153. DEFINE_SIMPLE_ATTRIBUTE(bank_fops, inj_bank_get, inj_bank_set, "%llu\n");
  154. static struct dfs_node {
  155. char *name;
  156. struct dentry *d;
  157. const struct file_operations *fops;
  158. } dfs_fls[] = {
  159. { .name = "status", .fops = &status_fops },
  160. { .name = "misc", .fops = &misc_fops },
  161. { .name = "addr", .fops = &addr_fops },
  162. { .name = "bank", .fops = &bank_fops },
  163. { .name = "flags", .fops = &flags_fops },
  164. { .name = "cpu", .fops = &extcpu_fops },
  165. };
  166. static int __init init_mce_inject(void)
  167. {
  168. int i;
  169. dfs_inj = debugfs_create_dir("mce-inject", NULL);
  170. if (!dfs_inj)
  171. return -EINVAL;
  172. for (i = 0; i < ARRAY_SIZE(dfs_fls); i++) {
  173. dfs_fls[i].d = debugfs_create_file(dfs_fls[i].name,
  174. S_IRUSR | S_IWUSR,
  175. dfs_inj,
  176. &i_mce,
  177. dfs_fls[i].fops);
  178. if (!dfs_fls[i].d)
  179. goto err_dfs_add;
  180. }
  181. return 0;
  182. err_dfs_add:
  183. while (--i >= 0)
  184. debugfs_remove(dfs_fls[i].d);
  185. debugfs_remove(dfs_inj);
  186. dfs_inj = NULL;
  187. return -ENOMEM;
  188. }
  189. static void __exit exit_mce_inject(void)
  190. {
  191. int i;
  192. for (i = 0; i < ARRAY_SIZE(dfs_fls); i++)
  193. debugfs_remove(dfs_fls[i].d);
  194. memset(&dfs_fls, 0, sizeof(dfs_fls));
  195. debugfs_remove(dfs_inj);
  196. dfs_inj = NULL;
  197. }
  198. module_init(init_mce_inject);
  199. module_exit(exit_mce_inject);
  200. MODULE_LICENSE("GPL");
  201. MODULE_AUTHOR("Borislav Petkov <bp@alien8.de>");
  202. MODULE_AUTHOR("AMD Inc.");
  203. MODULE_DESCRIPTION("MCE injection facility for RAS testing");