diff options
author | Borislav Petkov <borislav.petkov@amd.com> | 2010-08-18 09:11:35 -0400 |
---|---|---|
committer | Borislav Petkov <bp@amd64.org> | 2010-10-21 08:48:00 -0400 |
commit | 888ab8e6eb2e41179cdc8edf5d0abd1cce0f0370 (patch) | |
tree | 4e7eb4f0ad3ebb10d75e7764355ebf3f4a316f72 /drivers | |
parent | 47ca08a40b043815134d489e21870b53276f1a4a (diff) |
EDAC, MCE: Adjust DC decoders to F14h
Add a per-family data cache decoders. Since there is a certain overlap
between the different DC MCE signatures, reuse functionality between the
families as far as possible.
Signed-off-by: Borislav Petkov <borislav.petkov@amd.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/edac/mce_amd.c | 158 | ||||
-rw-r--r-- | drivers/edac/mce_amd.h | 40 |
2 files changed, 171 insertions, 27 deletions
diff --git a/drivers/edac/mce_amd.c b/drivers/edac/mce_amd.c index 5eb8042d0c6a..33985aa61356 100644 --- a/drivers/edac/mce_amd.c +++ b/drivers/edac/mce_amd.c | |||
@@ -1,6 +1,10 @@ | |||
1 | #include <linux/module.h> | 1 | #include <linux/module.h> |
2 | #include <linux/slab.h> | ||
3 | |||
2 | #include "mce_amd.h" | 4 | #include "mce_amd.h" |
3 | 5 | ||
6 | static struct amd_decoder_ops *fam_ops; | ||
7 | |||
4 | static bool report_gart_errors; | 8 | static bool report_gart_errors; |
5 | static void (*nb_bus_decoder)(int node_id, struct mce *m, u32 nbcfg); | 9 | static void (*nb_bus_decoder)(int node_id, struct mce *m, u32 nbcfg); |
6 | 10 | ||
@@ -97,41 +101,116 @@ const char *ext_msgs[] = { | |||
97 | }; | 101 | }; |
98 | EXPORT_SYMBOL_GPL(ext_msgs); | 102 | EXPORT_SYMBOL_GPL(ext_msgs); |
99 | 103 | ||
100 | static void amd_decode_dc_mce(struct mce *m) | 104 | static bool f10h_dc_mce(u16 ec) |
101 | { | 105 | { |
102 | u32 ec = m->status & 0xffff; | 106 | u8 r4 = (ec >> 4) & 0xf; |
103 | u32 xec = (m->status >> 16) & 0xf; | 107 | bool ret = false; |
104 | 108 | ||
105 | pr_emerg(HW_ERR "Data Cache Error: "); | 109 | if (r4 == R4_GEN) { |
110 | pr_cont("during data scrub.\n"); | ||
111 | return true; | ||
112 | } | ||
106 | 113 | ||
107 | if (xec == 1 && TLB_ERROR(ec)) | 114 | if (MEM_ERROR(ec)) { |
108 | pr_cont(": %s TLB multimatch.\n", LL_MSG(ec)); | 115 | u8 ll = ec & 0x3; |
109 | else if (xec == 0) { | 116 | ret = true; |
110 | if (m->status & (1ULL << 40)) | ||
111 | pr_cont(" during Data Scrub.\n"); | ||
112 | else if (TLB_ERROR(ec)) | ||
113 | pr_cont(": %s TLB parity error.\n", LL_MSG(ec)); | ||
114 | else if (MEM_ERROR(ec)) { | ||
115 | u8 ll = ec & 0x3; | ||
116 | u8 tt = (ec >> 2) & 0x3; | ||
117 | u8 rrrr = (ec >> 4) & 0xf; | ||
118 | 117 | ||
119 | /* see F10h BKDG (31116), Table 92. */ | 118 | if (ll == LL_L2) |
120 | if (ll == 0x1) { | 119 | pr_cont("during L1 linefill from L2.\n"); |
121 | if (tt != 0x1) | 120 | else if (ll == LL_L1) |
122 | goto wrong_dc_mce; | 121 | pr_cont("Data/Tag %s error.\n", RRRR_MSG(ec)); |
122 | else | ||
123 | ret = false; | ||
124 | } | ||
125 | return ret; | ||
126 | } | ||
123 | 127 | ||
124 | pr_cont(": Data/Tag %s error.\n", RRRR_MSG(ec)); | 128 | static bool k8_dc_mce(u16 ec) |
129 | { | ||
130 | if (BUS_ERROR(ec)) { | ||
131 | pr_cont("during system linefill.\n"); | ||
132 | return true; | ||
133 | } | ||
125 | 134 | ||
126 | } else if (ll == 0x2 && rrrr == 0x3) | 135 | return f10h_dc_mce(ec); |
127 | pr_cont(" during L1 linefill from L2.\n"); | 136 | } |
128 | else | 137 | |
129 | goto wrong_dc_mce; | 138 | static bool f14h_dc_mce(u16 ec) |
130 | } else if (BUS_ERROR(ec) && boot_cpu_data.x86 == 0xf) | 139 | { |
131 | pr_cont(" during system linefill.\n"); | 140 | u8 r4 = (ec >> 4) & 0xf; |
141 | u8 ll = ec & 0x3; | ||
142 | u8 tt = (ec >> 2) & 0x3; | ||
143 | u8 ii = tt; | ||
144 | bool ret = true; | ||
145 | |||
146 | if (MEM_ERROR(ec)) { | ||
147 | |||
148 | if (tt != TT_DATA || ll != LL_L1) | ||
149 | return false; | ||
150 | |||
151 | switch (r4) { | ||
152 | case R4_DRD: | ||
153 | case R4_DWR: | ||
154 | pr_cont("Data/Tag parity error due to %s.\n", | ||
155 | (r4 == R4_DRD ? "load/hw prf" : "store")); | ||
156 | break; | ||
157 | case R4_EVICT: | ||
158 | pr_cont("Copyback parity error on a tag miss.\n"); | ||
159 | break; | ||
160 | case R4_SNOOP: | ||
161 | pr_cont("Tag parity error during snoop.\n"); | ||
162 | break; | ||
163 | default: | ||
164 | ret = false; | ||
165 | } | ||
166 | } else if (BUS_ERROR(ec)) { | ||
167 | |||
168 | if ((ii != II_MEM && ii != II_IO) || ll != LL_LG) | ||
169 | return false; | ||
170 | |||
171 | pr_cont("System read data error on a "); | ||
172 | |||
173 | switch (r4) { | ||
174 | case R4_RD: | ||
175 | pr_cont("TLB reload.\n"); | ||
176 | break; | ||
177 | case R4_DWR: | ||
178 | pr_cont("store.\n"); | ||
179 | break; | ||
180 | case R4_DRD: | ||
181 | pr_cont("load.\n"); | ||
182 | break; | ||
183 | default: | ||
184 | ret = false; | ||
185 | } | ||
186 | } else { | ||
187 | ret = false; | ||
188 | } | ||
189 | |||
190 | return ret; | ||
191 | } | ||
192 | |||
193 | static void amd_decode_dc_mce(struct mce *m) | ||
194 | { | ||
195 | u16 ec = m->status & 0xffff; | ||
196 | u8 xec = (m->status >> 16) & 0xf; | ||
197 | |||
198 | pr_emerg(HW_ERR "Data Cache Error: "); | ||
199 | |||
200 | /* TLB error signatures are the same across families */ | ||
201 | if (TLB_ERROR(ec)) { | ||
202 | u8 tt = (ec >> 2) & 0x3; | ||
203 | |||
204 | if (tt == TT_DATA) { | ||
205 | pr_cont("%s TLB %s.\n", LL_MSG(ec), | ||
206 | (xec ? "multimatch" : "parity error")); | ||
207 | return; | ||
208 | } | ||
132 | else | 209 | else |
133 | goto wrong_dc_mce; | 210 | goto wrong_dc_mce; |
134 | } else | 211 | } |
212 | |||
213 | if (!fam_ops->dc_mce(ec)) | ||
135 | goto wrong_dc_mce; | 214 | goto wrong_dc_mce; |
136 | 215 | ||
137 | return; | 216 | return; |
@@ -395,6 +474,30 @@ static int __init mce_amd_init(void) | |||
395 | if (boot_cpu_data.x86 < 0xf || boot_cpu_data.x86 > 0x11) | 474 | if (boot_cpu_data.x86 < 0xf || boot_cpu_data.x86 > 0x11) |
396 | return 0; | 475 | return 0; |
397 | 476 | ||
477 | fam_ops = kzalloc(sizeof(struct amd_decoder_ops), GFP_KERNEL); | ||
478 | if (!fam_ops) | ||
479 | return -ENOMEM; | ||
480 | |||
481 | switch (boot_cpu_data.x86) { | ||
482 | case 0xf: | ||
483 | fam_ops->dc_mce = k8_dc_mce; | ||
484 | break; | ||
485 | |||
486 | case 0x10: | ||
487 | fam_ops->dc_mce = f10h_dc_mce; | ||
488 | break; | ||
489 | |||
490 | case 0x14: | ||
491 | fam_ops->dc_mce = f14h_dc_mce; | ||
492 | break; | ||
493 | |||
494 | default: | ||
495 | printk(KERN_WARNING "Huh? What family is that: %d?!\n", | ||
496 | boot_cpu_data.x86); | ||
497 | kfree(fam_ops); | ||
498 | return -EINVAL; | ||
499 | } | ||
500 | |||
398 | atomic_notifier_chain_register(&x86_mce_decoder_chain, &amd_mce_dec_nb); | 501 | atomic_notifier_chain_register(&x86_mce_decoder_chain, &amd_mce_dec_nb); |
399 | 502 | ||
400 | return 0; | 503 | return 0; |
@@ -405,6 +508,7 @@ early_initcall(mce_amd_init); | |||
405 | static void __exit mce_amd_exit(void) | 508 | static void __exit mce_amd_exit(void) |
406 | { | 509 | { |
407 | atomic_notifier_chain_unregister(&x86_mce_decoder_chain, &amd_mce_dec_nb); | 510 | atomic_notifier_chain_unregister(&x86_mce_decoder_chain, &amd_mce_dec_nb); |
511 | kfree(fam_ops); | ||
408 | } | 512 | } |
409 | 513 | ||
410 | MODULE_DESCRIPTION("AMD MCE decoder"); | 514 | MODULE_DESCRIPTION("AMD MCE decoder"); |
diff --git a/drivers/edac/mce_amd.h b/drivers/edac/mce_amd.h index 2712a906afdf..85985c225442 100644 --- a/drivers/edac/mce_amd.h +++ b/drivers/edac/mce_amd.h | |||
@@ -44,6 +44,39 @@ | |||
44 | #define K8_NBSH_UECC BIT(13) | 44 | #define K8_NBSH_UECC BIT(13) |
45 | #define K8_NBSH_ERR_SCRUBER BIT(8) | 45 | #define K8_NBSH_ERR_SCRUBER BIT(8) |
46 | 46 | ||
47 | enum tt_ids { | ||
48 | TT_INSTR = 0, | ||
49 | TT_DATA, | ||
50 | TT_GEN, | ||
51 | TT_RESV, | ||
52 | }; | ||
53 | |||
54 | enum ll_ids { | ||
55 | LL_RESV = 0, | ||
56 | LL_L1, | ||
57 | LL_L2, | ||
58 | LL_LG, | ||
59 | }; | ||
60 | |||
61 | enum ii_ids { | ||
62 | II_MEM = 0, | ||
63 | II_RESV, | ||
64 | II_IO, | ||
65 | II_GEN, | ||
66 | }; | ||
67 | |||
68 | enum rrrr_ids { | ||
69 | R4_GEN = 0, | ||
70 | R4_RD, | ||
71 | R4_WR, | ||
72 | R4_DRD, | ||
73 | R4_DWR, | ||
74 | R4_IRD, | ||
75 | R4_PREF, | ||
76 | R4_EVICT, | ||
77 | R4_SNOOP, | ||
78 | }; | ||
79 | |||
47 | extern const char *tt_msgs[]; | 80 | extern const char *tt_msgs[]; |
48 | extern const char *ll_msgs[]; | 81 | extern const char *ll_msgs[]; |
49 | extern const char *rrrr_msgs[]; | 82 | extern const char *rrrr_msgs[]; |
@@ -63,6 +96,13 @@ struct err_regs { | |||
63 | u32 nbeal; | 96 | u32 nbeal; |
64 | }; | 97 | }; |
65 | 98 | ||
99 | /* | ||
100 | * per-family decoder ops | ||
101 | */ | ||
102 | struct amd_decoder_ops { | ||
103 | bool (*dc_mce)(u16); | ||
104 | }; | ||
105 | |||
66 | void amd_report_gart_errors(bool); | 106 | void amd_report_gart_errors(bool); |
67 | void amd_register_ecc_decoder(void (*f)(int, struct mce *, u32)); | 107 | void amd_register_ecc_decoder(void (*f)(int, struct mce *, u32)); |
68 | void amd_unregister_ecc_decoder(void (*f)(int, struct mce *, u32)); | 108 | void amd_unregister_ecc_decoder(void (*f)(int, struct mce *, u32)); |