Print this page
acpica-unix2-20130823
PANKOVs restructure
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/uts/i86pc/cpu/amd_opteron/ao_mca.c
+++ new/usr/src/uts/i86pc/cpu/amd_opteron/ao_mca.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21
22 22 /*
23 23 * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
24 24 */
25 25
26 26 #include <sys/types.h>
27 27 #include <sys/regset.h>
28 28 #include <sys/privregs.h>
29 29 #include <sys/pci_impl.h>
30 30 #include <sys/cpuvar.h>
31 31 #include <sys/x86_archext.h>
32 32 #include <sys/cmn_err.h>
33 33 #include <sys/systm.h>
34 34 #include <sys/sysmacros.h>
35 35 #include <sys/pghw.h>
36 36 #include <sys/cyclic.h>
37 37 #include <sys/sysevent.h>
38 38 #include <sys/smbios.h>
39 39 #include <sys/mca_x86.h>
40 40 #include <sys/mca_amd.h>
↓ open down ↓ |
40 lines elided |
↑ open up ↑ |
41 41 #include <sys/mc.h>
42 42 #include <sys/mc_amd.h>
43 43 #include <sys/psw.h>
44 44 #include <sys/ddi.h>
45 45 #include <sys/sunddi.h>
46 46 #include <sys/sdt.h>
47 47 #include <sys/fm/util.h>
48 48 #include <sys/fm/protocol.h>
49 49 #include <sys/fm/cpu/AMD.h>
50 50 #include <sys/fm/smb/fmsmb.h>
51 -#include <sys/acpi/acpi.h>
52 -#include <sys/acpi/acpi_pci.h>
51 +#include <acpica/include/acpi.h>
53 52 #include <sys/acpica.h>
54 53 #include <sys/cpu_module.h>
55 54
56 55 #include "ao.h"
57 56 #include "ao_mca_disp.h"
58 57
59 58 #define AO_F_REVS_FG (X86_CHIPREV_AMD_F_REV_F | X86_CHIPREV_AMD_F_REV_G)
60 59
61 60 int ao_mca_smi_disable = 1; /* attempt to disable SMI polling */
62 61
63 62 extern int x86gentopo_legacy; /* x86 generic topology support */
64 63
65 64 struct ao_ctl_init {
66 65 uint32_t ctl_revmask; /* rev(s) to which this applies */
67 66 uint64_t ctl_bits; /* mca ctl reg bitmask to set */
68 67 };
69 68
70 69 /*
71 70 * Additional NB MCA ctl initialization for revs F and G
72 71 */
73 72 static const struct ao_ctl_init ao_nb_ctl_init[] = {
74 73 { AO_F_REVS_FG, AMD_NB_CTL_INIT_REV_FG },
75 74 { X86_CHIPREV_UNKNOWN, 0 }
76 75 };
77 76
78 77 typedef struct ao_bank_cfg {
79 78 uint64_t bank_ctl_init_cmn; /* Common init value */
80 79 const struct ao_ctl_init *bank_ctl_init_extra; /* Extra for each rev */
81 80 void (*bank_misc_initfunc)(cmi_hdl_t, ao_ms_data_t *, uint32_t);
82 81 uint_t bank_ctl_mask;
83 82 } ao_bank_cfg_t;
84 83
85 84 static void nb_mcamisc_init(cmi_hdl_t, ao_ms_data_t *, uint32_t);
86 85
87 86 static const ao_bank_cfg_t ao_bank_cfgs[] = {
88 87 { AMD_DC_CTL_INIT_CMN, NULL, NULL, AMD_MSR_DC_MASK },
89 88 { AMD_IC_CTL_INIT_CMN, NULL, NULL, AMD_MSR_IC_MASK },
90 89 { AMD_BU_CTL_INIT_CMN, NULL, NULL, AMD_MSR_BU_MASK },
91 90 { AMD_LS_CTL_INIT_CMN, NULL, NULL, AMD_MSR_LS_MASK },
92 91 { AMD_NB_CTL_INIT_CMN, &ao_nb_ctl_init[0], nb_mcamisc_init,
93 92 AMD_MSR_NB_MASK },
94 93 };
95 94
96 95 static int ao_nbanks = sizeof (ao_bank_cfgs) / sizeof (ao_bank_cfgs[0]);
97 96
98 97 /*
99 98 * This is quite awful but necessary to work around x86 system vendor's view of
100 99 * the world. Other operating systems (you know who you are) don't understand
101 100 * Opteron-specific error handling, so BIOS and system vendors often hide these
102 101 * conditions from them by using SMI polling to copy out any errors from the
103 102 * machine-check registers. When Solaris runs on a system with this feature,
104 103 * we want to disable the SMI polling so we can use FMA instead. Sadly, there
105 104 * isn't even a standard self-describing way to express the whole situation,
106 105 * so we have to resort to hard-coded values. This should all be changed to
107 106 * be a self-describing vendor-specific SMBIOS structure in the future.
108 107 */
109 108 static const struct ao_smi_disable {
110 109 const char *asd_sys_vendor; /* SMB_TYPE_SYSTEM vendor prefix */
111 110 const char *asd_sys_product; /* SMB_TYPE_SYSTEM product prefix */
112 111 const char *asd_bios_vendor; /* SMB_TYPE_BIOS vendor prefix */
113 112 uint8_t asd_code; /* output code for SMI disable */
114 113 } ao_smi_disable[] = {
115 114 { "Sun Microsystems", "Galaxy12",
116 115 "American Megatrends", 0x59 },
117 116 { "Sun Microsystems", "Sun Fire X4100 Server",
118 117 "American Megatrends", 0x59 },
119 118 { "Sun Microsystems", "Sun Fire X4200 Server",
120 119 "American Megatrends", 0x59 },
121 120 { NULL, NULL, NULL, 0 }
122 121 };
123 122
124 123 static int
125 124 ao_disp_match_r4(uint16_t ref, uint8_t r4)
126 125 {
127 126 static const uint16_t ao_r4_map[] = {
128 127 AO_MCA_R4_BIT_ERR, /* MCAX86_ERRCODE_RRRR_ERR */
129 128 AO_MCA_R4_BIT_RD, /* MCAX86_ERRCODE_RRRR_RD */
130 129 AO_MCA_R4_BIT_WR, /* MCAX86_ERRCODE_RRRR_WR */
131 130 AO_MCA_R4_BIT_DRD, /* MCAX86_ERRCODE_RRRR_DRD */
132 131 AO_MCA_R4_BIT_DWR, /* MCAX86_ERRCODE_RRRR_DWR */
133 132 AO_MCA_R4_BIT_IRD, /* MCAX86_ERRCODE_RRRR_IRD */
134 133 AO_MCA_R4_BIT_PREFETCH, /* MCAX86_ERRCODE_RRRR_PREFETCH */
135 134 AO_MCA_R4_BIT_EVICT, /* MCAX86_ERRCODE_RRRR_EVICT */
136 135 AO_MCA_R4_BIT_SNOOP /* MCAX86_ERRCODE_RRRR_SNOOP */
137 136 };
138 137
139 138 ASSERT(r4 < sizeof (ao_r4_map) / sizeof (uint16_t));
140 139
141 140 return ((ref & ao_r4_map[r4]) != 0);
142 141 }
143 142
144 143 static int
145 144 ao_disp_match_pp(uint8_t ref, uint8_t pp)
146 145 {
147 146 static const uint8_t ao_pp_map[] = {
148 147 AO_MCA_PP_BIT_SRC, /* MCAX86_ERRCODE_PP_SRC */
149 148 AO_MCA_PP_BIT_RES, /* MCAX86_ERRCODE_PP_RES */
150 149 AO_MCA_PP_BIT_OBS, /* MCAX86_ERRCODE_PP_OBS */
151 150 AO_MCA_PP_BIT_GEN /* MCAX86_ERRCODE_PP_GEN */
152 151 };
153 152
154 153 ASSERT(pp < sizeof (ao_pp_map) / sizeof (uint8_t));
155 154
156 155 return ((ref & ao_pp_map[pp]) != 0);
157 156 }
158 157
159 158 static int
160 159 ao_disp_match_ii(uint8_t ref, uint8_t ii)
161 160 {
162 161 static const uint8_t ao_ii_map[] = {
163 162 AO_MCA_II_BIT_MEM, /* MCAX86_ERRCODE_II_MEM */
164 163 0,
165 164 AO_MCA_II_BIT_IO, /* MCAX86_ERRCODE_II_IO */
166 165 AO_MCA_II_BIT_GEN /* MCAX86_ERRCODE_II_GEN */
167 166 };
168 167
169 168 ASSERT(ii < sizeof (ao_ii_map) / sizeof (uint8_t));
170 169
171 170 return ((ref & ao_ii_map[ii]) != 0);
172 171 }
173 172
174 173 static uint8_t
175 174 bit_strip(uint16_t *codep, uint16_t mask, uint16_t shift)
176 175 {
177 176 uint8_t val = (*codep & mask) >> shift;
178 177 *codep &= ~mask;
179 178 return (val);
180 179 }
181 180
182 181 #define BIT_STRIP(codep, name) \
183 182 bit_strip(codep, MCAX86_ERRCODE_##name##_MASK, \
184 183 MCAX86_ERRCODE_##name##_SHIFT)
185 184
186 185 /*ARGSUSED*/
187 186 static int
188 187 ao_disp_match_one(const ao_error_disp_t *aed, uint64_t status, uint32_t rev,
189 188 int bankno)
190 189 {
191 190 uint16_t code = MCAX86_ERRCODE(status);
192 191 uint8_t extcode = AMD_EXT_ERRCODE(status);
193 192 uint64_t stat_mask = aed->aed_stat_mask;
194 193 uint64_t stat_mask_res = aed->aed_stat_mask_res;
195 194
196 195 /*
197 196 * If the bank's status register indicates overflow, then we can no
198 197 * longer rely on the value of CECC: our experience with actual fault
199 198 * injection has shown that multiple CE's overwriting each other shows
200 199 * AMD_BANK_STAT_CECC and AMD_BANK_STAT_UECC both set to zero. This
201 200 * should be clarified in a future BKDG or by the Revision Guide.
202 201 * This behaviour is fixed in revision F.
203 202 */
204 203 if (bankno == AMD_MCA_BANK_NB &&
205 204 !X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_F_REV_F) &&
206 205 status & MSR_MC_STATUS_OVER) {
207 206 stat_mask &= ~AMD_BANK_STAT_CECC;
208 207 stat_mask_res &= ~AMD_BANK_STAT_CECC;
209 208 }
210 209
211 210 if ((status & stat_mask) != stat_mask_res)
212 211 return (0);
213 212
214 213 /*
215 214 * r4 and pp bits are stored separately, so we mask off and compare them
216 215 * for the code types that use them. Once we've taken the r4 and pp
217 216 * bits out of the equation, we can directly compare the resulting code
218 217 * with the one stored in the ao_error_disp_t.
219 218 */
220 219 if (AMD_ERRCODE_ISMEM(code)) {
221 220 uint8_t r4 = BIT_STRIP(&code, RRRR);
222 221
223 222 if (!ao_disp_match_r4(aed->aed_stat_r4_bits, r4))
224 223 return (0);
225 224
226 225 } else if (AMD_ERRCODE_ISBUS(code)) {
227 226 uint8_t r4 = BIT_STRIP(&code, RRRR);
228 227 uint8_t pp = BIT_STRIP(&code, PP);
229 228 uint8_t ii = BIT_STRIP(&code, II);
230 229
231 230 if (!ao_disp_match_r4(aed->aed_stat_r4_bits, r4) ||
232 231 !ao_disp_match_pp(aed->aed_stat_pp_bits, pp) ||
233 232 !ao_disp_match_ii(aed->aed_stat_ii_bits, ii))
234 233 return (0);
235 234 }
236 235
237 236 return (code == aed->aed_stat_code && extcode == aed->aed_stat_extcode);
238 237 }
239 238
240 239 /*ARGSUSED*/
241 240 cms_cookie_t
242 241 ao_ms_disp_match(cmi_hdl_t hdl, int ismc, int banknum, uint64_t status,
243 242 uint64_t addr, uint64_t misc, void *mslogout)
244 243 {
245 244 ao_ms_data_t *ao = cms_hdl_getcmsdata(hdl);
246 245 uint32_t rev = ao->ao_ms_shared->aos_chiprev;
247 246 const ao_error_disp_t *aed;
248 247
249 248 for (aed = ao_error_disp[banknum]; aed->aed_stat_mask != 0; aed++) {
250 249 if (ao_disp_match_one(aed, status, rev, banknum))
251 250 return ((cms_cookie_t)aed);
252 251 }
253 252
254 253 return (NULL);
255 254 }
256 255
257 256 /*ARGSUSED*/
258 257 void
259 258 ao_ms_ereport_class(cmi_hdl_t hdl, cms_cookie_t mscookie,
260 259 const char **cpuclsp, const char **leafclsp)
261 260 {
262 261 const ao_error_disp_t *aed = mscookie;
263 262
264 263 if (aed != NULL) {
265 264 *cpuclsp = FM_EREPORT_CPU_AMD;
266 265 *leafclsp = aed->aed_class;
267 266 }
268 267 }
269 268
270 269 static int
271 270 ao_chip_once(ao_ms_data_t *ao, enum ao_cfgonce_bitnum what)
272 271 {
273 272 return (atomic_set_long_excl(&ao->ao_ms_shared->aos_cfgonce,
274 273 what) == 0 ? B_TRUE : B_FALSE);
275 274 }
276 275
277 276 /*
278 277 * This knob exists in case any platform has a problem with our default
279 278 * policy of disabling any interrupt registered in the NB MC4_MISC
280 279 * register. Setting this may cause Solaris and external entities
281 280 * who also have an interest in this register to argue over available
282 281 * telemetry (so setting it is generally not recommended).
283 282 */
284 283 int ao_nb_cfg_mc4misc_noseize = 0;
285 284
286 285 /*
287 286 * The BIOS may have setup to receive SMI on counter overflow. It may also
288 287 * have locked various fields or made them read-only. We will clear any
289 288 * SMI request and leave the register locked. We will also clear the
290 289 * counter and enable counting - while we don't use the counter it is nice
291 290 * to have it enabled for verification and debug work.
292 291 */
293 292 static void
294 293 nb_mcamisc_init(cmi_hdl_t hdl, ao_ms_data_t *ao, uint32_t rev)
295 294 {
296 295 uint64_t val, nval;
297 296
298 297 if (!X86_CHIPREV_MATCH(rev, AO_F_REVS_FG))
299 298 return;
300 299
301 300 if (cmi_hdl_rdmsr(hdl, AMD_MSR_NB_MISC, &val) != CMI_SUCCESS)
302 301 return;
303 302
304 303 ao->ao_ms_shared->aos_bcfg_nb_misc = val;
305 304
306 305 if (ao_nb_cfg_mc4misc_noseize)
307 306 return; /* stash BIOS value, but no changes */
308 307
309 308
310 309 /*
311 310 * The Valid bit tells us whether the CtrP bit is defined; if it
312 311 * is the CtrP bit tells us whether an ErrCount field is present.
313 312 * If not then there is nothing for us to do.
314 313 */
315 314 if (!(val & AMD_NB_MISC_VALID) || !(val & AMD_NB_MISC_CTRP))
316 315 return;
317 316
318 317
319 318 nval = val;
320 319 nval |= AMD_NB_MISC_CNTEN; /* enable ECC error counting */
321 320 nval &= ~AMD_NB_MISC_ERRCOUNT_MASK; /* clear ErrCount */
322 321 nval &= ~AMD_NB_MISC_OVRFLW; /* clear Ovrflw */
323 322 nval &= ~AMD_NB_MISC_INTTYPE_MASK; /* no interrupt on overflow */
324 323 nval |= AMD_NB_MISC_LOCKED;
325 324
326 325 if (nval != val) {
327 326 uint64_t locked = val & AMD_NB_MISC_LOCKED;
328 327
329 328 if (locked)
330 329 ao_bankstatus_prewrite(hdl, ao);
331 330
332 331 (void) cmi_hdl_wrmsr(hdl, AMD_MSR_NB_MISC, nval);
333 332
334 333 if (locked)
335 334 ao_bankstatus_postwrite(hdl, ao);
336 335 }
337 336 }
338 337
339 338 /*
340 339 * NorthBridge (NB) MCA Configuration.
341 340 *
342 341 * We add and remove bits from the BIOS-configured value, rather than
343 342 * writing an absolute value. The variables ao_nb_cfg_{add,remove}_cmn and
344 343 * ap_nb_cfg_{add,remove}_revFG are available for modification via kmdb
345 344 * and /etc/system. The revision-specific adds and removes are applied
346 345 * after the common changes, and one write is made to the config register.
347 346 * These are not intended for watchdog configuration via these variables -
348 347 * use the watchdog policy below.
349 348 */
350 349
351 350 /*
352 351 * Bits to be added to the NB configuration register - all revs.
353 352 */
354 353 uint32_t ao_nb_cfg_add_cmn = AMD_NB_CFG_ADD_CMN;
355 354
356 355 /*
357 356 * Bits to be cleared from the NB configuration register - all revs.
358 357 */
359 358 uint32_t ao_nb_cfg_remove_cmn = AMD_NB_CFG_REMOVE_CMN;
360 359
361 360 /*
362 361 * Bits to be added to the NB configuration register - revs F and G.
363 362 */
364 363 uint32_t ao_nb_cfg_add_revFG = AMD_NB_CFG_ADD_REV_FG;
365 364
366 365 /*
367 366 * Bits to be cleared from the NB configuration register - revs F and G.
368 367 */
369 368 uint32_t ao_nb_cfg_remove_revFG = AMD_NB_CFG_REMOVE_REV_FG;
370 369
371 370 struct ao_nb_cfg {
372 371 uint32_t cfg_revmask;
373 372 uint32_t *cfg_add_p;
374 373 uint32_t *cfg_remove_p;
375 374 };
376 375
377 376 static const struct ao_nb_cfg ao_cfg_extra[] = {
378 377 { AO_F_REVS_FG, &ao_nb_cfg_add_revFG, &ao_nb_cfg_remove_revFG },
379 378 { X86_CHIPREV_UNKNOWN, NULL, NULL }
380 379 };
381 380
382 381 /*
383 382 * Bits to be used if we configure the NorthBridge (NB) Watchdog. The watchdog
384 383 * triggers a machine check exception when no response to an NB system access
385 384 * occurs within a specified time interval.
386 385 */
387 386 uint32_t ao_nb_cfg_wdog =
388 387 AMD_NB_CFG_WDOGTMRCNTSEL_4095 |
389 388 AMD_NB_CFG_WDOGTMRBASESEL_1MS;
390 389
391 390 /*
392 391 * The default watchdog policy is to enable it (at the above rate) if it
393 392 * is disabled; if it is enabled then we leave it enabled at the rate
394 393 * chosen by the BIOS.
395 394 */
396 395 enum {
397 396 AO_NB_WDOG_LEAVEALONE, /* Don't touch watchdog config */
398 397 AO_NB_WDOG_DISABLE, /* Always disable watchdog */
399 398 AO_NB_WDOG_ENABLE_IF_DISABLED, /* If disabled, enable at our rate */
400 399 AO_NB_WDOG_ENABLE_FORCE_RATE /* Enable and set our rate */
401 400 } ao_nb_watchdog_policy = AO_NB_WDOG_ENABLE_IF_DISABLED;
402 401
403 402 static void
404 403 ao_nb_cfg(ao_ms_data_t *ao, uint32_t rev)
405 404 {
406 405 const struct ao_nb_cfg *nbcp = &ao_cfg_extra[0];
407 406 uint_t procnodeid = pg_plat_hw_instance_id(CPU, PGHW_PROCNODE);
408 407 uint32_t val;
409 408
410 409 /*
411 410 * Read the NorthBridge (NB) configuration register in PCI space,
412 411 * modify the settings accordingly, and store the new value back.
413 412 * Note that the stashed BIOS config value aos_bcfg_nb_cfg is used
414 413 * in ereport payload population to determine ECC syndrome type for
415 414 * memory errors.
416 415 */
417 416 ao->ao_ms_shared->aos_bcfg_nb_cfg = val =
418 417 ao_pcicfg_read(procnodeid, MC_FUNC_MISCCTL, MC_CTL_REG_NBCFG);
419 418
420 419 switch (ao_nb_watchdog_policy) {
421 420 case AO_NB_WDOG_LEAVEALONE:
422 421 break;
423 422
424 423 case AO_NB_WDOG_DISABLE:
425 424 val &= ~AMD_NB_CFG_WDOGTMRBASESEL_MASK;
426 425 val &= ~AMD_NB_CFG_WDOGTMRCNTSEL_MASK;
427 426 val |= AMD_NB_CFG_WDOGTMRDIS;
428 427 break;
429 428
430 429 default:
431 430 cmn_err(CE_NOTE, "ao_nb_watchdog_policy=%d unrecognised, "
432 431 "using default policy", ao_nb_watchdog_policy);
433 432 /*FALLTHRU*/
434 433
435 434 case AO_NB_WDOG_ENABLE_IF_DISABLED:
436 435 if (!(val & AMD_NB_CFG_WDOGTMRDIS))
437 436 break; /* if enabled leave rate intact */
438 437 /*FALLTHRU*/
439 438
440 439 case AO_NB_WDOG_ENABLE_FORCE_RATE:
441 440 val &= ~AMD_NB_CFG_WDOGTMRBASESEL_MASK;
442 441 val &= ~AMD_NB_CFG_WDOGTMRCNTSEL_MASK;
443 442 val &= ~AMD_NB_CFG_WDOGTMRDIS;
444 443 val |= ao_nb_cfg_wdog;
445 444 break;
446 445 }
447 446
448 447 /*
449 448 * Now apply bit adds and removes, first those common to all revs
450 449 * and then the revision-specific ones.
451 450 */
452 451 val &= ~ao_nb_cfg_remove_cmn;
453 452 val |= ao_nb_cfg_add_cmn;
454 453
455 454 while (nbcp->cfg_revmask != X86_CHIPREV_UNKNOWN) {
456 455 if (X86_CHIPREV_MATCH(rev, nbcp->cfg_revmask)) {
457 456 val &= ~(*nbcp->cfg_remove_p);
458 457 val |= *nbcp->cfg_add_p;
459 458 }
460 459 nbcp++;
461 460 }
462 461
463 462 ao_pcicfg_write(procnodeid, MC_FUNC_MISCCTL, MC_CTL_REG_NBCFG, val);
464 463 }
465 464
466 465 static void
467 466 ao_dram_cfg(ao_ms_data_t *ao, uint32_t rev)
468 467 {
469 468 uint_t procnodeid = pg_plat_hw_instance_id(CPU, PGHW_PROCNODE);
470 469 union mcreg_dramcfg_lo dcfglo;
471 470
472 471 ao->ao_ms_shared->aos_bcfg_dcfg_lo = MCREG_VAL32(&dcfglo) =
473 472 ao_pcicfg_read(procnodeid, MC_FUNC_DRAMCTL, MC_DC_REG_DRAMCFGLO);
474 473 ao->ao_ms_shared->aos_bcfg_dcfg_hi =
475 474 ao_pcicfg_read(procnodeid, MC_FUNC_DRAMCTL, MC_DC_REG_DRAMCFGHI);
476 475 #ifdef OPTERON_ERRATUM_172
477 476 if (X86_CHIPREV_MATCH(rev, AO_F_REVS_FG) &&
478 477 MCREG_FIELD_F_revFG(&dcfglo, ParEn)) {
479 478 MCREG_FIELD_F_revFG(&dcfglo, ParEn) = 0;
480 479 ao_pcicfg_write(procnodeid, MC_FUNC_DRAMCTL,
481 480 MC_DC_REG_DRAMCFGLO, MCREG_VAL32(&dcfglo));
482 481 }
483 482 #endif
484 483 }
485 484
486 485 /*
487 486 * This knob exists in case any platform has a problem with our default
488 487 * policy of disabling any interrupt registered in the online spare
489 488 * control register. Setting this may cause Solaris and external entities
490 489 * who also have an interest in this register to argue over available
491 490 * telemetry (so setting it is generally not recommended).
492 491 */
493 492 int ao_nb_cfg_sparectl_noseize = 0;
494 493
495 494 /*
496 495 * Setup the online spare control register (revs F and G). We disable
497 496 * any interrupt registered by the BIOS and zero all error counts.
498 497 */
499 498 static void
500 499 ao_sparectl_cfg(ao_ms_data_t *ao)
501 500 {
502 501 uint_t procnodeid = pg_plat_hw_instance_id(CPU, PGHW_PROCNODE);
503 502 union mcreg_sparectl sparectl;
504 503 int chan, cs;
505 504
506 505 ao->ao_ms_shared->aos_bcfg_nb_sparectl = MCREG_VAL32(&sparectl) =
507 506 ao_pcicfg_read(procnodeid, MC_FUNC_MISCCTL, MC_CTL_REG_SPARECTL);
508 507
509 508 if (ao_nb_cfg_sparectl_noseize)
510 509 return; /* stash BIOS value, but no changes */
511 510
512 511 /*
513 512 * If the BIOS has requested SMI interrupt type for ECC count
514 513 * overflow for a chip-select or channel force those off.
515 514 */
516 515 MCREG_FIELD_F_revFG(&sparectl, EccErrInt) = 0;
517 516 MCREG_FIELD_F_revFG(&sparectl, SwapDoneInt) = 0;
518 517
519 518 /*
520 519 * Zero EccErrCnt and write this back to all chan/cs combinations.
521 520 */
522 521 MCREG_FIELD_F_revFG(&sparectl, EccErrCntWrEn) = 1;
523 522 MCREG_FIELD_F_revFG(&sparectl, EccErrCnt) = 0;
524 523 for (chan = 0; chan < MC_CHIP_NDRAMCHAN; chan++) {
525 524 MCREG_FIELD_F_revFG(&sparectl, EccErrCntDramChan) = chan;
526 525
527 526 for (cs = 0; cs < MC_CHIP_NCS; cs++) {
528 527 MCREG_FIELD_F_revFG(&sparectl, EccErrCntDramCs) = cs;
529 528 ao_pcicfg_write(procnodeid, MC_FUNC_MISCCTL,
530 529 MC_CTL_REG_SPARECTL, MCREG_VAL32(&sparectl));
531 530 }
532 531 }
533 532 }
534 533
535 534 int ao_forgive_uc = 0; /* For test/debug only */
536 535 int ao_forgive_pcc = 0; /* For test/debug only */
537 536 int ao_fake_poison = 0; /* For test/debug only */
538 537
539 538 uint32_t
540 539 ao_ms_error_action(cmi_hdl_t hdl, int ismc, int banknum,
541 540 uint64_t status, uint64_t addr, uint64_t misc, void *mslogout)
542 541 {
543 542 const ao_error_disp_t *aed;
544 543 uint32_t retval = 0;
545 544 uint8_t when;
546 545 int en;
547 546
548 547 if (ao_forgive_uc)
549 548 retval |= CMS_ERRSCOPE_CLEARED_UC;
550 549
551 550 if (ao_forgive_pcc)
552 551 retval |= CMS_ERRSCOPE_CURCONTEXT_OK;
553 552
554 553 if (ao_fake_poison && status & MSR_MC_STATUS_UC)
555 554 retval |= CMS_ERRSCOPE_POISONED;
556 555
557 556 if (retval)
558 557 return (retval);
559 558
560 559 aed = ao_ms_disp_match(hdl, ismc, banknum, status, addr, misc,
561 560 mslogout);
562 561
563 562 /*
564 563 * If we do not recognise the error let the cpu module apply
565 564 * the generic criteria to decide how to react.
566 565 */
567 566 if (aed == NULL)
568 567 return (0);
569 568
570 569 en = (status & MSR_MC_STATUS_EN) != 0;
571 570
572 571 if ((when = aed->aed_panic_when) == AO_AED_PANIC_NEVER)
573 572 retval |= CMS_ERRSCOPE_IGNORE_ERR;
574 573
575 574 if ((when & AO_AED_PANIC_ALWAYS) ||
576 575 ((when & AO_AED_PANIC_IFMCE) && (en || ismc)))
577 576 retval |= CMS_ERRSCOPE_FORCE_FATAL;
578 577
579 578 /*
580 579 * The original AMD implementation would panic on a machine check
581 580 * (not a poll) if the status overflow bit was set, with an
582 581 * exception for the case of rev F or later with an NB error
583 582 * indicating CECC. This came from the perception that the
584 583 * overflow bit was not correctly managed on rev E and earlier, for
585 584 * example that repeated correctable memeory errors did not set
586 585 * OVER but somehow clear CECC.
587 586 *
588 587 * We will leave the generic support to evaluate overflow errors
589 588 * and decide to panic on their individual merits, e.g., if PCC
590 589 * is set and so on. The AMD docs do say (as Intel does) that
591 590 * the status information is *all* from the higher-priority
592 591 * error in the case of an overflow, so it is at least as serious
593 592 * as the original and we can decide panic etc based on it.
594 593 */
595 594
596 595 return (retval);
597 596 }
598 597
599 598 /*
600 599 * Will need to change for family 0x10
601 600 */
602 601 static uint_t
603 602 ao_ereport_synd(ao_ms_data_t *ao, uint64_t status, uint_t *typep,
604 603 int is_nb)
605 604 {
606 605 if (is_nb) {
607 606 if (ao->ao_ms_shared->aos_bcfg_nb_cfg &
608 607 AMD_NB_CFG_CHIPKILLECCEN) {
609 608 *typep = AMD_SYNDTYPE_CHIPKILL;
610 609 return (AMD_NB_STAT_CKSYND(status));
611 610 } else {
612 611 *typep = AMD_SYNDTYPE_ECC;
613 612 return (AMD_BANK_SYND(status));
614 613 }
615 614 } else {
616 615 *typep = AMD_SYNDTYPE_ECC;
617 616 return (AMD_BANK_SYND(status));
618 617 }
619 618 }
620 619
621 620 static nvlist_t *
622 621 ao_ereport_create_resource_elem(cmi_hdl_t hdl, nv_alloc_t *nva,
623 622 mc_unum_t *unump, int dimmnum)
624 623 {
625 624 nvlist_t *nvl, *snvl;
626 625 nvlist_t *board_list = NULL;
627 626
628 627 if ((nvl = fm_nvlist_create(nva)) == NULL) /* freed by caller */
629 628 return (NULL);
630 629
631 630 if ((snvl = fm_nvlist_create(nva)) == NULL) {
632 631 fm_nvlist_destroy(nvl, nva ? FM_NVA_RETAIN : FM_NVA_FREE);
633 632 return (NULL);
634 633 }
635 634
636 635 (void) nvlist_add_uint64(snvl, FM_FMRI_HC_SPECIFIC_OFFSET,
637 636 unump->unum_offset);
638 637
639 638 if (!x86gentopo_legacy) {
640 639 board_list = cmi_hdl_smb_bboard(hdl);
641 640
642 641 if (board_list == NULL) {
643 642 fm_nvlist_destroy(nvl,
644 643 nva ? FM_NVA_RETAIN : FM_NVA_FREE);
645 644 fm_nvlist_destroy(snvl,
646 645 nva ? FM_NVA_RETAIN : FM_NVA_FREE);
647 646 return (NULL);
648 647 }
649 648
650 649 fm_fmri_hc_create(nvl, FM_HC_SCHEME_VERSION, NULL, snvl,
651 650 board_list, 4,
652 651 "chip", cmi_hdl_smb_chipid(hdl),
653 652 "memory-controller", unump->unum_mc,
654 653 "dimm", unump->unum_dimms[dimmnum],
655 654 "rank", unump->unum_rank);
656 655 } else {
657 656 fm_fmri_hc_set(nvl, FM_HC_SCHEME_VERSION, NULL, snvl, 5,
658 657 "motherboard", unump->unum_board,
659 658 "chip", unump->unum_chip,
660 659 "memory-controller", unump->unum_mc,
661 660 "dimm", unump->unum_dimms[dimmnum],
662 661 "rank", unump->unum_rank);
663 662 }
664 663
665 664 fm_nvlist_destroy(snvl, nva ? FM_NVA_RETAIN : FM_NVA_FREE);
666 665
667 666 return (nvl);
668 667 }
669 668
670 669 static void
671 670 ao_ereport_add_resource(cmi_hdl_t hdl, nvlist_t *payload, nv_alloc_t *nva,
672 671 mc_unum_t *unump)
673 672 {
674 673
675 674 nvlist_t *elems[MC_UNUM_NDIMM];
676 675 int nelems = 0;
677 676 int i;
678 677
679 678 for (i = 0; i < MC_UNUM_NDIMM; i++) {
680 679 if (unump->unum_dimms[i] == MC_INVALNUM)
681 680 break;
682 681
683 682 if ((elems[nelems] = ao_ereport_create_resource_elem(hdl, nva,
684 683 unump, i)) == NULL)
685 684 break;
686 685
687 686 nelems++;
688 687 }
689 688
690 689 if (nelems == 0)
691 690 return;
692 691
693 692 fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_RESOURCE,
694 693 DATA_TYPE_NVLIST_ARRAY, nelems, elems, NULL);
695 694
696 695 for (i = 0; i < nelems; i++)
697 696 fm_nvlist_destroy(elems[i], nva ? FM_NVA_RETAIN : FM_NVA_FREE);
698 697 }
699 698
700 699 /*ARGSUSED*/
701 700 void
702 701 ao_ms_ereport_add_logout(cmi_hdl_t hdl, nvlist_t *ereport,
703 702 nv_alloc_t *nva, int banknum, uint64_t status, uint64_t addr,
704 703 uint64_t misc, void *mslogout, cms_cookie_t mscookie)
705 704 {
706 705 ao_ms_data_t *ao = cms_hdl_getcmsdata(hdl);
707 706 const ao_error_disp_t *aed = mscookie;
708 707 uint_t synd, syndtype;
709 708 uint64_t members;
710 709
711 710 if (aed == NULL)
712 711 return;
713 712
714 713 members = aed->aed_ereport_members;
715 714
716 715 synd = ao_ereport_synd(ao, status, &syndtype,
717 716 banknum == AMD_MCA_BANK_NB);
718 717
719 718 if (members & FM_EREPORT_PAYLOAD_FLAG_SYND) {
720 719 fm_payload_set(ereport, FM_EREPORT_PAYLOAD_NAME_SYND,
721 720 DATA_TYPE_UINT16, synd, NULL);
722 721 }
723 722
724 723 if (members & FM_EREPORT_PAYLOAD_FLAG_SYND_TYPE) {
725 724 fm_payload_set(ereport, FM_EREPORT_PAYLOAD_NAME_SYND_TYPE,
726 725 DATA_TYPE_STRING, (syndtype == AMD_SYNDTYPE_CHIPKILL ?
727 726 "C4" : "E"), NULL);
728 727 }
729 728
730 729 if (members & FM_EREPORT_PAYLOAD_FLAG_RESOURCE) {
731 730 mc_unum_t unum;
732 731
733 732 if (((aed->aed_flags & AO_AED_FLAGS_ADDRTYPE) ==
734 733 AO_AED_F_PHYSICAL) && (status & MSR_MC_STATUS_ADDRV) &&
735 734 cmi_mc_patounum(addr, aed->aed_addrvalid_hi,
736 735 aed->aed_addrvalid_lo, synd, syndtype, &unum) ==
737 736 CMI_SUCCESS)
738 737 ao_ereport_add_resource(hdl, ereport, nva, &unum);
739 738 }
740 739 }
741 740
742 741 /*ARGSUSED*/
743 742 boolean_t
744 743 ao_ms_ereport_includestack(cmi_hdl_t hdl, cms_cookie_t mscookie)
745 744 {
746 745 const ao_error_disp_t *aed = mscookie;
747 746
748 747 if (aed == NULL)
749 748 return (0);
750 749
751 750 return ((aed->aed_ereport_members &
752 751 FM_EREPORT_PAYLOAD_FLAG_STACK) != 0);
753 752 }
754 753
755 754 cms_errno_t
756 755 ao_ms_msrinject(cmi_hdl_t hdl, uint_t msr, uint64_t val)
757 756 {
758 757 ao_ms_data_t *ao = cms_hdl_getcmsdata(hdl);
759 758 cms_errno_t rv = CMSERR_BADMSRWRITE;
760 759
761 760 ao_bankstatus_prewrite(hdl, ao);
762 761 if (cmi_hdl_wrmsr(hdl, msr, val) == CMI_SUCCESS)
763 762 rv = CMS_SUCCESS;
764 763 ao_bankstatus_postwrite(hdl, ao);
765 764
766 765 return (rv);
767 766 }
768 767
769 768 /*ARGSUSED*/
770 769 uint64_t
771 770 ao_ms_mcgctl_val(cmi_hdl_t hdl, int nbanks, uint64_t def)
772 771 {
773 772 return ((1ULL << nbanks) - 1);
774 773 }
775 774
776 775 boolean_t
777 776 ao_ms_bankctl_skipinit(cmi_hdl_t hdl, int banknum)
778 777 {
779 778 ao_ms_data_t *ao = cms_hdl_getcmsdata(hdl);
780 779
781 780 if (banknum != AMD_MCA_BANK_NB)
782 781 return (B_FALSE);
783 782
784 783 /*
785 784 * If we are the first to atomically set the "I'll do it" bit
786 785 * then return B_FALSE (do not skip), otherwise skip with B_TRUE.
787 786 */
788 787 return (ao_chip_once(ao, AO_CFGONCE_NBMCA) == B_TRUE ?
789 788 B_FALSE : B_TRUE);
790 789 }
791 790
792 791 uint64_t
793 792 ao_ms_bankctl_val(cmi_hdl_t hdl, int banknum, uint64_t def)
794 793 {
795 794 ao_ms_data_t *ao = cms_hdl_getcmsdata(hdl);
796 795 const struct ao_ctl_init *extrap;
797 796 const ao_bank_cfg_t *bankcfg;
798 797 uint64_t mcictl;
799 798 uint32_t rev = ao->ao_ms_shared->aos_chiprev;
800 799
801 800 if (banknum >= sizeof (ao_bank_cfgs) / sizeof (ao_bank_cfgs[0]))
802 801 return (def);
803 802
804 803 bankcfg = &ao_bank_cfgs[banknum];
805 804 extrap = bankcfg->bank_ctl_init_extra;
806 805
807 806 mcictl = bankcfg->bank_ctl_init_cmn;
808 807
809 808 while (extrap != NULL && extrap->ctl_revmask != X86_CHIPREV_UNKNOWN) {
810 809 if (X86_CHIPREV_MATCH(rev, extrap->ctl_revmask))
811 810 mcictl |= extrap->ctl_bits;
812 811 extrap++;
813 812 }
814 813
815 814 return (mcictl);
816 815 }
817 816
818 817 /*ARGSUSED*/
819 818 void
820 819 ao_bankstatus_prewrite(cmi_hdl_t hdl, ao_ms_data_t *ao)
821 820 {
822 821 #ifndef __xpv
823 822 uint64_t hwcr;
824 823
825 824 if (cmi_hdl_rdmsr(hdl, MSR_AMD_HWCR, &hwcr) != CMI_SUCCESS)
826 825 return;
827 826
828 827 ao->ao_ms_hwcr_val = hwcr;
829 828
830 829 if (!(hwcr & AMD_HWCR_MCI_STATUS_WREN)) {
831 830 hwcr |= AMD_HWCR_MCI_STATUS_WREN;
832 831 (void) cmi_hdl_wrmsr(hdl, MSR_AMD_HWCR, hwcr);
833 832 }
834 833 #endif
835 834 }
836 835
837 836 /*ARGSUSED*/
838 837 void
839 838 ao_bankstatus_postwrite(cmi_hdl_t hdl, ao_ms_data_t *ao)
840 839 {
841 840 #ifndef __xpv
842 841 uint64_t hwcr = ao->ao_ms_hwcr_val;
843 842
844 843 if (!(hwcr & AMD_HWCR_MCI_STATUS_WREN)) {
845 844 hwcr &= ~AMD_HWCR_MCI_STATUS_WREN;
846 845 (void) cmi_hdl_wrmsr(hdl, MSR_AMD_HWCR, hwcr);
847 846 }
848 847 #endif
849 848 }
850 849
851 850 void
852 851 ao_ms_mca_init(cmi_hdl_t hdl, int nbanks)
853 852 {
854 853 ao_ms_data_t *ao = cms_hdl_getcmsdata(hdl);
855 854 uint32_t rev = ao->ao_ms_shared->aos_chiprev;
856 855 ao_ms_mca_t *mca = &ao->ao_ms_mca;
857 856 uint64_t *maskp;
858 857 int i;
859 858
860 859 maskp = mca->ao_mca_bios_cfg.bcfg_bank_mask = kmem_zalloc(nbanks *
861 860 sizeof (uint64_t), KM_SLEEP);
862 861
863 862 /*
864 863 * Read the bank ctl mask MSRs, but only as many as we know
865 864 * certainly exist - don't calculate the register address.
866 865 * Also initialize the MCi_MISC register where required.
867 866 */
868 867 for (i = 0; i < MIN(nbanks, ao_nbanks); i++) {
869 868 (void) cmi_hdl_rdmsr(hdl, ao_bank_cfgs[i].bank_ctl_mask,
870 869 maskp++);
871 870 if (ao_bank_cfgs[i].bank_misc_initfunc != NULL)
872 871 ao_bank_cfgs[i].bank_misc_initfunc(hdl, ao, rev);
873 872
874 873 }
875 874
876 875 if (ao_chip_once(ao, AO_CFGONCE_NBCFG) == B_TRUE) {
877 876 ao_nb_cfg(ao, rev);
878 877
879 878 if (X86_CHIPREV_MATCH(rev, AO_F_REVS_FG))
880 879 ao_sparectl_cfg(ao);
881 880 }
882 881
883 882 if (ao_chip_once(ao, AO_CFGONCE_DRAMCFG) == B_TRUE)
884 883 ao_dram_cfg(ao, rev);
885 884
886 885 ao_procnode_scrubber_enable(hdl, ao);
887 886 }
888 887
889 888 /*
890 889 * Note that although this cpu module is loaded before the PSMs are
891 890 * loaded (and hence before acpica is loaded), this function is
892 891 * called from post_startup(), after PSMs are initialized and acpica
893 892 * is loaded.
894 893 */
895 894 static int
896 895 ao_acpi_find_smicmd(int *asd_port)
897 896 {
898 897 ACPI_TABLE_FADT *fadt = NULL;
899 898
900 899 /*
901 900 * AcpiGetTable works even if ACPI is disabled, so a failure
902 901 * here means we weren't able to retreive a pointer to the FADT.
903 902 */
904 903 if (AcpiGetTable(ACPI_SIG_FADT, 1, (ACPI_TABLE_HEADER **)&fadt) !=
905 904 AE_OK)
906 905 return (-1);
907 906
908 907 ASSERT(fadt != NULL);
909 908
910 909 *asd_port = fadt->SmiCommand;
911 910 return (0);
912 911 }
913 912
914 913 /*ARGSUSED*/
915 914 void
916 915 ao_ms_post_startup(cmi_hdl_t hdl)
917 916 {
918 917 const struct ao_smi_disable *asd;
919 918 id_t id;
920 919 int rv = -1, asd_port;
921 920
922 921 smbios_system_t sy;
923 922 smbios_bios_t sb;
924 923 smbios_info_t si;
925 924
926 925 /*
927 926 * Fetch the System and BIOS vendor strings from SMBIOS and see if they
928 927 * match a value in our table. If so, disable SMI error polling. This
929 928 * is grotesque and should be replaced by self-describing vendor-
930 929 * specific SMBIOS data or a specification enhancement instead.
931 930 */
932 931 if (ao_mca_smi_disable && ksmbios != NULL &&
933 932 smbios_info_bios(ksmbios, &sb) != SMB_ERR &&
934 933 (id = smbios_info_system(ksmbios, &sy)) != SMB_ERR &&
935 934 smbios_info_common(ksmbios, id, &si) != SMB_ERR) {
936 935
937 936 for (asd = ao_smi_disable; asd->asd_sys_vendor != NULL; asd++) {
938 937 if (strncmp(asd->asd_sys_vendor, si.smbi_manufacturer,
939 938 strlen(asd->asd_sys_vendor)) != 0 ||
940 939 strncmp(asd->asd_sys_product, si.smbi_product,
941 940 strlen(asd->asd_sys_product)) != 0 ||
942 941 strncmp(asd->asd_bios_vendor, sb.smbb_vendor,
943 942 strlen(asd->asd_bios_vendor)) != 0)
944 943 continue;
945 944
946 945 /*
947 946 * Look for the SMI_CMD port in the ACPI FADT,
948 947 * if the port is 0, this platform doesn't support
949 948 * SMM, so there is no SMI error polling to disable.
950 949 */
951 950 if ((rv = ao_acpi_find_smicmd(&asd_port)) == 0 &&
952 951 asd_port != 0) {
953 952 cmn_err(CE_CONT, "?SMI polling disabled in "
954 953 "favor of Solaris Fault Management for "
955 954 "AMD Processors\n");
956 955
957 956 outb(asd_port, asd->asd_code);
958 957
959 958 } else if (rv < 0) {
960 959 cmn_err(CE_CONT, "?Solaris Fault Management "
961 960 "for AMD Processors could not disable SMI "
962 961 "polling because an error occurred while "
963 962 "trying to determine the SMI command port "
964 963 "from the ACPI FADT table\n");
965 964 }
966 965 break;
967 966 }
968 967 }
969 968 }
↓ open down ↓ |
907 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX