1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 *
21 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
22 * Use is subject to license terms.
23 */
24
25 /*
26 * Given a physical address and an optional syndrome, determine the
27 * name of the memory module that contains it.
28 */
29
30 #include <sys/errno.h>
31 #include <sys/types.h>
32 #include <sys/mc.h>
33
34 #include <mcamd_api.h>
35 #include <mcamd_err.h>
36
37 #define MC_SYSADDR_MSB 39
38 #define MC_SYSADDR_LSB 3
39
40 #define CSDIMM1 0x1
41 #define CSDIMM2 0x2
42
43 #define BITS(val, high, low) \
44 ((val) & (((2ULL << (high)) - 1) & ~((1ULL << (low)) - 1)))
45
46 /*
47 * iaddr_gen generates a "normalized" DRAM controller input address
48 * from a system address (physical address) if it falls within the
49 * mapped range for this memory controller. Normalisation is
50 * performed by subtracting the node base address from the system address,
51 * allowing from hoisting, and excising any bits being used in node
52 * interleaving.
53 */
54 static int
iaddr_gen(struct mcamd_hdl * hdl,mcamd_node_t * mc,uint64_t pa,uint64_t * iaddrp)55 iaddr_gen(struct mcamd_hdl *hdl, mcamd_node_t *mc, uint64_t pa,
56 uint64_t *iaddrp)
57 {
58 uint64_t orig = pa;
59 uint64_t mcnum, base, lim, dramaddr, ilen, ilsel, top, holesz;
60
61 if (!mcamd_get_numprops(hdl,
62 mc, MCAMD_PROP_NUM, &mcnum,
63 mc, MCAMD_PROP_BASE_ADDR, &base,
64 mc, MCAMD_PROP_LIM_ADDR, &lim,
65 mc, MCAMD_PROP_ILEN, &ilen,
66 mc, MCAMD_PROP_ILSEL, &ilsel,
67 mc, MCAMD_PROP_DRAMHOLE_SIZE, &holesz,
68 NULL)) {
69 mcamd_dprintf(hdl, MCAMD_DBG_ERR, "iaddr_gen: failed to "
70 "lookup required properties");
71 return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
72 }
73
74 /*
75 * A node with no mapped memory (no active chip-selects is usually
76 * mapped with base and lim both zero. We'll cover that case and
77 * any other where the range is 0.
78 */
79 if (base == lim)
80 return (mcamd_set_errno(hdl, EMCAMD_NOADDR));
81
82 if (pa < base || pa > lim) {
83 mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "iaddr_gen: PA 0x%llx not "
84 "in range [0x%llx, 0x%llx] of MC %d\n", pa, base, lim,
85 (int)mcnum);
86 return (mcamd_set_errno(hdl, EMCAMD_NOADDR));
87 }
88
89 /*
90 * Rev E and later added the DRAM Hole Address Register for
91 * memory hoisting. In earlier revisions memory hoisting is
92 * achieved by following some algorithm to modify the CS bases etc,
93 * and this pa to unum algorithm will simply see those modified
94 * values. But if the Hole Address Register is being used then
95 * we need to reduce any address at or above 4GB by the size of
96 * the hole.
97 */
98 if (holesz != 0 && pa >= 0x100000000) {
99 pa -= holesz;
100 mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "iaddr_gen: dram hole "
101 "valid; pa decremented from 0x%llx to 0x%llx for "
102 "a dramhole size of 0x%llx\n", orig, pa, holesz);
103 }
104
105 dramaddr = BITS(pa, 39, 0) - BITS(base, 39, 24);
106
107 if (ilen != 0) {
108 int pailsel;
109
110 if (ilen != 1 && ilen != 3 && ilen != 7) {
111 mcamd_dprintf(hdl, MCAMD_DBG_ERR, "Invalid intlven "
112 "of %d for MC %d\n", (int)ilen, (int)mcnum);
113 return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
114 }
115
116 if ((pailsel = BITS(pa, 14, 12) >> 12 & ilen) != ilsel) {
117 mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "iaddr_gen: "
118 "PA 0x%llx in a %d-way node interleave indicates "
119 "selection %d, MC %d has ilsel of %d\n",
120 pa, (int)ilen + 1, pailsel, (int)mcnum, (int)ilsel);
121 return (mcamd_set_errno(hdl, EMCAMD_NOADDR));
122 }
123
124 if (ilen == 1)
125 top = BITS(dramaddr, 36, 13) >> 1;
126 else if (ilen == 3)
127 top = BITS(dramaddr, 37, 14) >> 2;
128 else if (ilen == 7)
129 top = BITS(dramaddr, 38, 15) >> 3;
130 } else {
131 top = BITS(dramaddr, 35, 12);
132 }
133
134 *iaddrp = top | BITS(dramaddr, 11, 0);
135
136 mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "iaddr_gen: PA 0x%llx in range "
137 "[0x%llx, 0x%llx] of MC %d; normalized address for cs compare "
138 "is 0x%llx\n", pa, base, lim, (int)mcnum, *iaddrp);
139
140 return (0);
141 }
142
143 /*
144 * cs_match determines whether the given DRAM controller input address
145 * would be responded to by the given chip-select (which may or may not
146 * be interleaved with other chip-selects). Since we include nodes
147 * for spare chip-selects (if any) and those marked TestFail (if any)
148 * we must check chip-select-bank-enable.
149 */
150 static int
cs_match(struct mcamd_hdl * hdl,uint64_t iaddr,mcamd_node_t * cs)151 cs_match(struct mcamd_hdl *hdl, uint64_t iaddr, mcamd_node_t *cs)
152 {
153 uint64_t csnum, csbase, csmask, csbe;
154 int match = 0;
155
156 if (!mcamd_get_numprops(hdl,
157 cs, MCAMD_PROP_NUM, &csnum,
158 cs, MCAMD_PROP_BASE_ADDR, &csbase,
159 cs, MCAMD_PROP_MASK, &csmask,
160 cs, MCAMD_PROP_CSBE, &csbe,
161 NULL)) {
162 mcamd_dprintf(hdl, MCAMD_DBG_ERR, "cs_match: failed to lookup "
163 "required properties\n");
164 return (0);
165 }
166
167 if (csbe) {
168 match = ((iaddr & ~csmask) == (csbase & ~csmask));
169
170 mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "cs_match: iaddr 0x%llx "
171 "does %smatch CS %d (base 0x%llx, mask 0x%llx)\n", iaddr,
172 match ? "" : "not ", (int)csnum, csbase, csmask);
173 } else {
174 mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "cs_match: iaddr 0x%llx "
175 "does not match disabled CS %d\n", iaddr, (int)csnum);
176 }
177
178 return (match);
179 }
180
181 /*
182 * Given a chip-select node determine whether it has been substituted
183 * by the online spare chip-select.
184 */
185 static mcamd_node_t *
cs_sparedto(struct mcamd_hdl * hdl,mcamd_node_t * cs,mcamd_node_t * mc)186 cs_sparedto(struct mcamd_hdl *hdl, mcamd_node_t *cs, mcamd_node_t *mc)
187 {
188 uint64_t csnum, badcsnum, sparecsnum, tmpcsnum;
189
190 if (!mcamd_get_numprops(hdl,
191 cs, MCAMD_PROP_NUM, &csnum,
192 mc, MCAMD_PROP_BADCS, &badcsnum,
193 mc, MCAMD_PROP_SPARECS, &sparecsnum,
194 NULL)) {
195 mcamd_dprintf(hdl, MCAMD_DBG_ERR, "cs_sparedto: failed to "
196 "lookup required properties\n");
197 return (NULL);
198 }
199
200 if ((badcsnum == MC_INVALNUM && sparecsnum == MC_INVALNUM) ||
201 csnum != badcsnum)
202 return (NULL);
203
204 for (cs = mcamd_cs_next(hdl, mc, NULL); cs != NULL;
205 cs = mcamd_cs_next(hdl, mc, cs)) {
206 if (!mcamd_get_numprop(hdl, cs, MCAMD_PROP_NUM, &tmpcsnum)) {
207 mcamd_dprintf(hdl, MCAMD_DBG_ERR, "cs_sparedto: "
208 "fail to lookup csnum - cannot reroute to spare\n");
209 return (NULL);
210 }
211 if (tmpcsnum == sparecsnum)
212 break;
213 }
214
215 if (cs != NULL) {
216 mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "cs_sparedto: cs#%d is "
217 "redirected to active online spare of cs#%d\n", csnum,
218 sparecsnum);
219 } else {
220 mcamd_dprintf(hdl, MCAMD_DBG_ERR, "cs_sparedto: cs#%d is "
221 "redirected but cannot find spare cs# - cannout reroute to "
222 "cs#%d\n", csnum, sparecsnum);
223 }
224
225 return (cs);
226 }
227
228 /*
229 * Having determined which node and chip-select an address maps to,
230 * as well as whether it is a dimm1, dimm2 or dimm1/dimm2 pair
231 * involved, fill the unum structure including an optional dimm offset
232 * member.
233 */
234 static int
unum_fill(struct mcamd_hdl * hdl,mcamd_node_t * cs,int which,uint64_t iaddr,mc_unum_t * unump,int incloff)235 unum_fill(struct mcamd_hdl *hdl, mcamd_node_t *cs, int which,
236 uint64_t iaddr, mc_unum_t *unump, int incloff)
237 {
238 uint64_t chipnum, csnum, dimm1, dimm2, ranknum;
239 mcamd_node_t *mc, *dimm;
240 int offsetdimm;
241 int i;
242
243 if ((mc = mcamd_cs_mc(hdl, cs)) == NULL ||
244 !mcamd_get_numprops(hdl,
245 mc, MCAMD_PROP_NUM, &chipnum,
246 cs, MCAMD_PROP_NUM, &csnum,
247 cs, MCAMD_PROP_DIMMRANK, &ranknum,
248 NULL)) {
249 mcamd_dprintf(hdl, MCAMD_DBG_ERR, "unum_fill: failed to "
250 "lookup required properties\n");
251 return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
252 }
253
254 if ((which & CSDIMM1) &&
255 !mcamd_get_numprop(hdl, cs, MCAMD_PROP_CSDIMM1, &dimm1) ||
256 (which & CSDIMM2) &&
257 !mcamd_get_numprop(hdl, cs, MCAMD_PROP_CSDIMM2, &dimm2)) {
258 mcamd_dprintf(hdl, MCAMD_DBG_ERR, "unum_fill: failed to "
259 "lookup dimm1/dimm2 properties\n");
260 return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
261 }
262
263 unump->unum_board = 0;
264 unump->unum_chip = (int)chipnum;
265 unump->unum_mc = 0;
266 unump->unum_chan = MC_INVALNUM;
267 unump->unum_cs = (int)csnum;
268 unump->unum_rank = (int)ranknum;
269
270 for (i = 0; i < MC_UNUM_NDIMM; i++) {
271 unump->unum_dimms[i] = MC_INVALNUM;
272 }
273 switch (which) {
274 case CSDIMM1:
275 unump->unum_dimms[0] = (int)dimm1;
276 offsetdimm = (int)dimm1;
277 break;
278 case CSDIMM2:
279 unump->unum_dimms[0] = (int)dimm2;
280 offsetdimm = (int)dimm2;
281 break;
282 case CSDIMM1 | CSDIMM2:
283 unump->unum_dimms[0] = (int)dimm1;
284 unump->unum_dimms[1] = (int)dimm2;
285 offsetdimm = (int)dimm1;
286 break;
287 }
288
289 if (!incloff) {
290 unump->unum_offset = MCAMD_RC_INVALID_OFFSET;
291 return (0);
292 }
293
294 /*
295 * We wish to calculate a dimm offset. In the paired case we will
296 * lookup dimm1 (see offsetdimm above).
297 */
298 for (dimm = mcamd_dimm_next(hdl, mc, NULL); dimm != NULL;
299 dimm = mcamd_dimm_next(hdl, mc, dimm)) {
300 uint64_t dnum;
301 if (!mcamd_get_numprop(hdl, dimm, MCAMD_PROP_NUM, &dnum)) {
302 mcamd_dprintf(hdl, MCAMD_DBG_ERR, "unum_fill: failed "
303 "to lookup dimm number property\n");
304 continue;
305 }
306 if (dnum == offsetdimm)
307 break;
308 }
309
310 if (dimm == NULL) {
311 mcamd_dprintf(hdl, MCAMD_DBG_ERR, "unum_fill: failed to "
312 "find dimm with number %d for offset calculation\n",
313 offsetdimm);
314 unump->unum_offset = MCAMD_RC_INVALID_OFFSET;
315 return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
316 }
317
318 /*
319 * mc_pa_to_offset sets the offset to an invalid value if
320 * it hits an error.
321 */
322 (void) mc_pa_to_offset(hdl, mc, cs, iaddr, &unump->unum_offset);
323
324 return (0);
325 }
326
327 /*
328 * We have translated a system address to a (node, chip-select), and wish
329 * to determine the associated dimm or dimms.
330 *
331 * A (node, chip-select) pair identifies one (in 64-bit MC mode) or two (in
332 * 128-bit MC mode) DIMMs. In the case of a single dimm it is usually in a
333 * lodimm (channel A) slot, but if mismatched dimm support is present it may
334 * be an updimm (channel B).
335 *
336 * Where just one dimm is associated with the chip-select we are done.
337 * Where there are two dimms associated with the chip-select we can
338 * use the ECC type and/or syndrome to determine which of the pair we
339 * resolve to, if the error is correctable. If the error is uncorrectable
340 * then in 64/8 ECC mode we can still resolve to a single dimm (since ECC
341 * is calculated and checked on each half of the data separately), but
342 * in ChipKill mode we cannot resolve down to a single dimm.
343 */
344 static int
mc_whichdimm(struct mcamd_hdl * hdl,mcamd_node_t * cs,uint64_t pa,uint8_t valid_lo,uint32_t synd,int syndtype)345 mc_whichdimm(struct mcamd_hdl *hdl, mcamd_node_t *cs, uint64_t pa,
346 uint8_t valid_lo, uint32_t synd, int syndtype)
347 {
348 int lobit, hibit, data, check;
349 uint64_t dimm1, dimm2;
350 uint_t sym, pat;
351 int ndimm;
352
353 /*
354 * Read the associated dimm instance numbers. The provider must
355 * assure that if there is just one dimm then it is in the first
356 * property, and if there are two then the first must be on
357 * channel A.
358 */
359 if (!mcamd_get_numprops(hdl,
360 cs, MCAMD_PROP_CSDIMM1, &dimm1,
361 cs, MCAMD_PROP_CSDIMM2, &dimm2,
362 NULL)) {
363 mcamd_dprintf(hdl, MCAMD_DBG_ERR, "mc_whichdimm: failed to "
364 "lookup required properties");
365 return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
366 }
367 ndimm = (dimm1 != MC_INVALNUM) + (dimm2 != MC_INVALNUM);
368 if (ndimm == 0) {
369 mcamd_dprintf(hdl, MCAMD_DBG_ERR, "mc_whichdimm: found no "
370 "dimms associated with chip-select");
371 return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
372 }
373
374 if (ndimm == 1) {
375 mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mc_whichdimm: just one "
376 "dimm associated with this chip-select");
377 return (CSDIMM1);
378 }
379
380 /*
381 * 64/8 ECC is checked separately for the upper and lower
382 * halves, so even an uncorrectable error is contained within
383 * one of the two halves. If we have sufficient address resolution
384 * then we can determine which DIMM.
385 */
386 if (syndtype == AMD_SYNDTYPE_ECC) {
387 if (valid_lo <= MC_SYSADDR_LSB) {
388 mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mc_whichdimm: 64/8 "
389 "ECC in 128-bit mode, PA 0x%llx is in %s half\n",
390 pa, pa & 0x8 ? "upper" : "lower");
391 return (pa & 0x8 ? CSDIMM2 : CSDIMM1);
392 } else {
393 mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mc_whichdimm: "
394 "64/8 ECC in 128-bit mode, PA 0x%llx with least "
395 "significant valid bit %d cannot be resolved to "
396 "a single DIMM\n", pa, valid_lo);
397 return (mcamd_set_errno(hdl, EMCAMD_INSUFF_RES));
398 }
399 }
400
401 /*
402 * ChipKill ECC
403 */
404 if (mcamd_cksynd_decode(hdl, synd, &sym, &pat)) {
405 /*
406 * A correctable ChipKill syndrome and we can tell
407 * which half the error was in from the symbol number.
408 */
409 if (mcamd_cksym_decode(hdl, sym, &lobit, &hibit, &data,
410 &check) == 0)
411 return (mcamd_set_errno(hdl, EMCAMD_SYNDINVALID));
412
413 if (data && hibit <= 63 || check && hibit <= 7) {
414 mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mc_whichdimm: "
415 "ChipKill symbol %d (%s %d..%d), so LODIMM\n", sym,
416 data ? "data" : "check", lobit, hibit);
417 return (CSDIMM1);
418 } else {
419 mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mc_whichdimm: "
420 "ChipKill symbol %d (%s %d..%d), so UPDIMM\n", sym,
421 data ? "data" : "check", lobit, hibit);
422 return (CSDIMM2);
423 }
424 } else {
425 /*
426 * An uncorrectable error while in ChipKill ECC mode - can't
427 * tell which dimm or dimms the errors lie within.
428 */
429 mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mc_whichhdimm: "
430 "uncorrectable ChipKill, could be either LODIMM "
431 "or UPDIMM\n");
432 return (CSDIMM1 | CSDIMM2);
433 }
434 }
435
436 #ifdef DEBUG
437 /*
438 * Brute-force BKDG pa to cs translation, coded to look as much like the
439 * BKDG code as possible.
440 */
441 static int
mc_bkdg_patounum(struct mcamd_hdl * hdl,mcamd_node_t * mc,uint64_t pa,uint8_t valid_lo,uint32_t synd,int syndtype,mc_unum_t * unump)442 mc_bkdg_patounum(struct mcamd_hdl *hdl, mcamd_node_t *mc, uint64_t pa,
443 uint8_t valid_lo, uint32_t synd, int syndtype,
444 mc_unum_t *unump)
445 {
446 int which;
447 uint64_t mcnum, rev;
448 mcamd_node_t *cs;
449 /*
450 * Raw registers as per BKDG
451 */
452 uint32_t HoleEn;
453 uint32_t DramBase, DramLimit;
454 uint32_t CSBase, CSMask;
455 /*
456 * Variables as per BKDG
457 */
458 int Ilog;
459 uint32_t SystemAddr = (uint32_t)(pa >> 8);
460 uint64_t IntlvEn, IntlvSel;
461 uint32_t HoleOffset;
462 uint32_t InputAddr, Temp;
463
464 if (!mcamd_get_numprops(hdl,
465 mc, MCAMD_PROP_NUM, &mcnum,
466 mc, MCAMD_PROP_REV, &rev, NULL) || !mcamd_get_cfgregs(hdl,
467 mc, MCAMD_REG_DRAMBASE, &DramBase,
468 mc, MCAMD_REG_DRAMLIMIT, &DramLimit,
469 mc, MCAMD_REG_DRAMHOLE, &HoleEn, NULL)) {
470 mcamd_dprintf(hdl, MCAMD_DBG_ERR, "mc_bkdg_patounm: failed "
471 "to lookup required properties and registers\n");
472 return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
473 }
474
475 /*
476 * BKDG line to skip Why
477 *
478 * F1Offset = ... Register already read,
479 * DramBase = Get_PCI() and retrieved above.
480 * DramEn = ... Function only called for enabled nodes.
481 */
482 IntlvEn = (DramBase & 0x00000700) >> 8;
483 DramBase &= 0xffff0000;
484 /* DramLimit = Get_PCI() Retrieved above */
485 IntlvSel = (DramLimit & 0x00000700) >> 8;
486 DramLimit |= 0x0000ffff;
487 /* HoleEn = ... Retrieved above */
488 HoleOffset = (HoleEn & 0x0000ff00) << 8;
489 HoleEn &= 0x00000001;
490
491 if (!(DramBase <= SystemAddr && SystemAddr <= DramLimit)) {
492 mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mc_bkdg_patounum: "
493 "SystemAddr 0x%x derived from PA 0x%llx is not in the "
494 "address range [0x%x, 0x%x] of MC %d\n",
495 SystemAddr, pa, DramBase, DramLimit, (int)mcnum);
496 return (mcamd_set_errno(hdl, EMCAMD_NOADDR));
497 }
498
499 if (HoleEn && SystemAddr > 0x00ffffff)
500 InputAddr = SystemAddr - HoleOffset;
501
502 InputAddr = SystemAddr - DramBase;
503
504 if (IntlvEn) {
505 if (IntlvSel == ((SystemAddr >> 4) & IntlvEn)) {
506 switch (IntlvEn) {
507 case 1:
508 Ilog = 1;
509 break;
510 case 3:
511 Ilog = 2;
512 break;
513 case 7:
514 Ilog = 3;
515 break;
516 default:
517 return (mcamd_set_errno(hdl,
518 EMCAMD_TREEINVALID));
519 }
520 Temp = (InputAddr >> (4 + Ilog)) << 4;
521 InputAddr = (Temp | (SystemAddr & 0x0000000f));
522 } else {
523 /* not this node */
524 mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mc_bkdg_patounum: "
525 "Node interleaving, MC node %d not selected\n",
526 (int)mcnum);
527 return (mcamd_set_errno(hdl, EMCAMD_NOADDR));
528 }
529 }
530
531 if (!MC_REV_MATCH(rev, MC_F_REVS_FG))
532 InputAddr <<= 4;
533
534 for (cs = mcamd_cs_next(hdl, mc, NULL); cs != NULL;
535 cs = mcamd_cs_next(hdl, mc, cs)) {
536 uint64_t csnum, CSEn;
537
538 if (!mcamd_get_cfgregs(hdl,
539 cs, MCAMD_REG_CSBASE, &CSBase,
540 cs, MCAMD_REG_CSMASK, &CSMask,
541 NULL) ||
542 !mcamd_get_numprops(hdl,
543 cs, MCAMD_PROP_NUM, &csnum,
544 cs, MCAMD_PROP_CSBE, &CSEn,
545 NULL)) {
546 mcamd_dprintf(hdl, MCAMD_DBG_ERR, "mc_bkdg_patounm: "
547 "failed to read cs registers\n");
548 return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
549 }
550
551 /*
552 * BKDG line to skip Why
553 *
554 * F2Offset = Register already read,
555 * F2MaskOffset (rev F) Register already read
556 * CSBase = Register already read
557 * CSEn = We only keep enabled cs.
558 */
559 if (MC_REV_MATCH(rev, MC_F_REVS_FG)) {
560 CSBase &= 0x1ff83fe0;
561 /* CSMask = Get_PCI() Retrieved above */
562 CSMask = (CSMask | 0x0007c01f) & 0x1fffffff;
563 } else {
564 CSBase &= 0xffe0fe00;
565 /* CSMask = Get_PCI() Retrieved above */
566 CSMask = (CSMask | 0x001f01ff) & 0x3fffffff;
567 }
568
569 if (CSEn && (InputAddr & ~CSMask) == (CSBase & ~CSMask)) {
570 mcamd_node_t *sparecs;
571
572 mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mc_bkdg_patounum: "
573 "match for chip select %d of MC %d\n", (int)csnum,
574 (int)mcnum);
575
576 if ((sparecs = cs_sparedto(hdl, cs, mc)) != NULL)
577 cs = sparecs;
578
579 if ((which = mc_whichdimm(hdl, cs, pa, valid_lo,
580 synd, syndtype)) < 0)
581 return (-1); /* errno is set for us */
582
583 /*
584 * The BKDG algorithm drops low-order bits that
585 * are unimportant in deriving chip-select but are
586 * included in row/col/bank mapping, so do not
587 * perform offset calculation in this case.
588 */
589 if (unum_fill(hdl, cs, which, InputAddr, unump, 0) < 0)
590 return (-1); /* errno is set for us */
591
592 return (0);
593 }
594 }
595
596 mcamd_dprintf(hdl, MCAMD_DBG_ERR, "mc_bkdg_patounum: in range "
597 "for MC %d but no cs responds\n", (int)mcnum);
598
599 return (mcamd_set_errno(hdl, EMCAMD_NOADDR));
600 }
601 #endif /* DEBUG */
602
603 /*
604 * Called for each memory controller to see if the given address is
605 * mapped to this node (as determined in iaddr_gen) and, if so, which
606 * chip-select on this node responds.
607 */
608
609 /*ARGSUSED*/
610 static int
mc_patounum(struct mcamd_hdl * hdl,mcamd_node_t * mc,uint64_t pa,uint8_t valid_lo,uint32_t synd,int syndtype,mc_unum_t * unump)611 mc_patounum(struct mcamd_hdl *hdl, mcamd_node_t *mc, uint64_t pa,
612 uint8_t valid_lo, uint32_t synd, int syndtype, mc_unum_t *unump)
613 {
614 uint64_t iaddr;
615 mcamd_node_t *cs, *sparecs;
616 int which;
617 #ifdef DEBUG
618 mc_unum_t bkdg_unum;
619 int bkdgres;
620
621 /*
622 * We perform the translation twice, once using the brute-force
623 * approach of the BKDG and again using a more elegant but more
624 * difficult to review against the BKDG approach.
625 */
626 mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "BKDG brute-force method begins\n");
627 bkdgres = mc_bkdg_patounum(hdl, mc, pa, valid_lo, synd,
628 syndtype, &bkdg_unum);
629 mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "BKDG brute-force method ends\n");
630 #endif
631
632 if (iaddr_gen(hdl, mc, pa, &iaddr) < 0)
633 return (-1); /* errno is set for us */
634
635 for (cs = mcamd_cs_next(hdl, mc, NULL); cs != NULL;
636 cs = mcamd_cs_next(hdl, mc, cs)) {
637 if (cs_match(hdl, iaddr, cs))
638 break;
639 }
640
641 if (cs == NULL)
642 return (mcamd_set_errno(hdl, EMCAMD_NOADDR));
643
644 /*
645 * If the spare chip-select has been swapped in for the one just
646 * matched then it is really the spare that we are after. Note that
647 * when the swap is done the csbase, csmask and CSBE of the spare
648 * rank do not change - accesses to the bad rank (as nominated in
649 * the Online Spare Control Register) are redirect to the spare.
650 */
651 if ((sparecs = cs_sparedto(hdl, cs, mc)) != NULL) {
652 cs = sparecs;
653 }
654
655 if ((which = mc_whichdimm(hdl, cs, pa, valid_lo, synd,
656 syndtype)) < 0)
657 return (-1); /* errno is set for us */
658
659 if (unum_fill(hdl, cs, which, iaddr, unump, 1) < 0)
660 return (-1); /* errno is set for us */
661
662 #ifdef DEBUG
663 mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "bkdgres=%d res=0\n", bkdgres);
664 /* offset is not checked - see note in BKDG algorithm */
665 if (bkdgres != 0) {
666 mcamd_dprintf(hdl, MCAMD_DBG_ERR, "BKDG alg failed while "
667 "ours succeeded\n");
668 } else if (!(unump->unum_board == bkdg_unum.unum_board &&
669 unump->unum_chip == bkdg_unum.unum_chip &&
670 unump->unum_mc == bkdg_unum.unum_mc &&
671 unump->unum_chan == bkdg_unum.unum_chan &&
672 unump->unum_cs == bkdg_unum.unum_cs &&
673 unump->unum_dimms[0] == bkdg_unum.unum_dimms[0] &&
674 unump->unum_dimms[1] == bkdg_unum.unum_dimms[1])) {
675 mcamd_dprintf(hdl, MCAMD_DBG_ERR,
676 "BKDG: node %d mc %d cs %d dimm(s) %d/%d\n"
677 "Ours: node 5d mc %d cs %d dimm(s) %d/%d\n",
678 bkdg_unum.unum_chip, bkdg_unum.unum_mc, bkdg_unum.unum_cs,
679 bkdg_unum.unum_dimms[0], bkdg_unum.unum_dimms[1],
680 unump->unum_chip, unump->unum_mc, unump->unum_cs,
681 unump->unum_dimms[0], unump->unum_dimms[1]);
682 }
683 #endif /* DEBUG */
684
685 mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "Result: chip %d mc %d cs %d "
686 "offset 0x%llx\n", unump->unum_chip, unump->unum_mc,
687 unump->unum_cs, unump->unum_offset);
688
689 return (0);
690 }
691
692 int
mcamd_patounum(struct mcamd_hdl * hdl,mcamd_node_t * root,uint64_t pa,uint8_t valid_hi,uint8_t valid_lo,uint32_t synd,int syndtype,mc_unum_t * unump)693 mcamd_patounum(struct mcamd_hdl *hdl, mcamd_node_t *root, uint64_t pa,
694 uint8_t valid_hi, uint8_t valid_lo, uint32_t synd, int syndtype,
695 mc_unum_t *unump)
696 {
697 mcamd_node_t *mc;
698
699 mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mcamd_patounum: pa=0x%llx, "
700 "synd=0x%x, syndtype=%d\n", pa, synd, syndtype);
701
702 if (valid_hi < MC_SYSADDR_MSB) {
703 mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mcamd_patounum: require "
704 "pa<%d> to be valid\n", MC_SYSADDR_MSB);
705 return (mcamd_set_errno(hdl, EMCAMD_INSUFF_RES));
706 }
707
708 if (!mcamd_synd_validate(hdl, synd, syndtype))
709 return (mcamd_set_errno(hdl, EMCAMD_SYNDINVALID));
710
711 for (mc = mcamd_mc_next(hdl, root, NULL); mc != NULL;
712 mc = mcamd_mc_next(hdl, root, mc)) {
713 if (mc_patounum(hdl, mc, pa, valid_lo, synd,
714 syndtype, unump) == 0)
715 return (0);
716
717 if (mcamd_errno(hdl) != EMCAMD_NOADDR)
718 break;
719 }
720
721 return (-1); /* errno is set for us */
722 }
723