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.
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.
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]
21 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
22 * Use is subject to license terms.
25 #pragma ident "%Z%%M% %I% %E% SMI"
28 * Given a physical address and an optional syndrome, determine the
29 * name of the memory module that contains it.
32 #include <sys/errno.h>
33 #include <sys/types.h>
36 #include <mcamd_api.h>
37 #include <mcamd_err.h>
39 #define MC_SYSADDR_MSB 39
40 #define MC_SYSADDR_LSB 3
45 #define BITS(val, high, low) \
46 ((val) & (((2ULL << (high)) - 1) & ~((1ULL << (low)) - 1)))
49 * iaddr_gen generates a "normalized" DRAM controller input address
50 * from a system address (physical address) if it falls within the
51 * mapped range for this memory controller. Normalisation is
52 * performed by subtracting the node base address from the system address,
53 * allowing from hoisting, and excising any bits being used in node
57 iaddr_gen(struct mcamd_hdl
*hdl
, mcamd_node_t
*mc
, uint64_t pa
,
61 uint64_t mcnum
, base
, lim
, dramaddr
, ilen
, ilsel
, top
, holesz
;
63 if (!mcamd_get_numprops(hdl
,
64 mc
, MCAMD_PROP_NUM
, &mcnum
,
65 mc
, MCAMD_PROP_BASE_ADDR
, &base
,
66 mc
, MCAMD_PROP_LIM_ADDR
, &lim
,
67 mc
, MCAMD_PROP_ILEN
, &ilen
,
68 mc
, MCAMD_PROP_ILSEL
, &ilsel
,
69 mc
, MCAMD_PROP_DRAMHOLE_SIZE
, &holesz
,
71 mcamd_dprintf(hdl
, MCAMD_DBG_ERR
, "iaddr_gen: failed to "
72 "lookup required properties");
73 return (mcamd_set_errno(hdl
, EMCAMD_TREEINVALID
));
77 * A node with no mapped memory (no active chip-selects is usually
78 * mapped with base and lim both zero. We'll cover that case and
79 * any other where the range is 0.
82 return (mcamd_set_errno(hdl
, EMCAMD_NOADDR
));
84 if (pa
< base
|| pa
> lim
) {
85 mcamd_dprintf(hdl
, MCAMD_DBG_FLOW
, "iaddr_gen: PA 0x%llx not "
86 "in range [0x%llx, 0x%llx] of MC %d\n", pa
, base
, lim
,
88 return (mcamd_set_errno(hdl
, EMCAMD_NOADDR
));
92 * Rev E and later added the DRAM Hole Address Register for
93 * memory hoisting. In earlier revisions memory hoisting is
94 * achieved by following some algorithm to modify the CS bases etc,
95 * and this pa to unum algorithm will simply see those modified
96 * values. But if the Hole Address Register is being used then
97 * we need to reduce any address at or above 4GB by the size of
100 if (holesz
!= 0 && pa
>= 0x100000000) {
102 mcamd_dprintf(hdl
, MCAMD_DBG_FLOW
, "iaddr_gen: dram hole "
103 "valid; pa decremented from 0x%llx to 0x%llx for "
104 "a dramhole size of 0x%llx\n", orig
, pa
, holesz
);
107 dramaddr
= BITS(pa
, 39, 0) - BITS(base
, 39, 24);
112 if (ilen
!= 1 && ilen
!= 3 && ilen
!= 7) {
113 mcamd_dprintf(hdl
, MCAMD_DBG_ERR
, "Invalid intlven "
114 "of %d for MC %d\n", (int)ilen
, (int)mcnum
);
115 return (mcamd_set_errno(hdl
, EMCAMD_TREEINVALID
));
118 if ((pailsel
= BITS(pa
, 14, 12) >> 12 & ilen
) != ilsel
) {
119 mcamd_dprintf(hdl
, MCAMD_DBG_FLOW
, "iaddr_gen: "
120 "PA 0x%llx in a %d-way node interleave indicates "
121 "selection %d, MC %d has ilsel of %d\n",
122 pa
, (int)ilen
+ 1, pailsel
, (int)mcnum
, (int)ilsel
);
123 return (mcamd_set_errno(hdl
, EMCAMD_NOADDR
));
127 top
= BITS(dramaddr
, 36, 13) >> 1;
129 top
= BITS(dramaddr
, 37, 14) >> 2;
131 top
= BITS(dramaddr
, 38, 15) >> 3;
133 top
= BITS(dramaddr
, 35, 12);
136 *iaddrp
= top
| BITS(dramaddr
, 11, 0);
138 mcamd_dprintf(hdl
, MCAMD_DBG_FLOW
, "iaddr_gen: PA 0x%llx in range "
139 "[0x%llx, 0x%llx] of MC %d; normalized address for cs compare "
140 "is 0x%llx\n", pa
, base
, lim
, (int)mcnum
, *iaddrp
);
146 * cs_match determines whether the given DRAM controller input address
147 * would be responded to by the given chip-select (which may or may not
148 * be interleaved with other chip-selects). Since we include nodes
149 * for spare chip-selects (if any) and those marked TestFail (if any)
150 * we must check chip-select-bank-enable.
153 cs_match(struct mcamd_hdl
*hdl
, uint64_t iaddr
, mcamd_node_t
*cs
)
155 uint64_t csnum
, csbase
, csmask
, csbe
;
158 if (!mcamd_get_numprops(hdl
,
159 cs
, MCAMD_PROP_NUM
, &csnum
,
160 cs
, MCAMD_PROP_BASE_ADDR
, &csbase
,
161 cs
, MCAMD_PROP_MASK
, &csmask
,
162 cs
, MCAMD_PROP_CSBE
, &csbe
,
164 mcamd_dprintf(hdl
, MCAMD_DBG_ERR
, "cs_match: failed to lookup "
165 "required properties\n");
170 match
= ((iaddr
& ~csmask
) == (csbase
& ~csmask
));
172 mcamd_dprintf(hdl
, MCAMD_DBG_FLOW
, "cs_match: iaddr 0x%llx "
173 "does %smatch CS %d (base 0x%llx, mask 0x%llx)\n", iaddr
,
174 match
? "" : "not ", (int)csnum
, csbase
, csmask
);
176 mcamd_dprintf(hdl
, MCAMD_DBG_FLOW
, "cs_match: iaddr 0x%llx "
177 "does not match disabled CS %d\n", iaddr
, (int)csnum
);
184 * Given a chip-select node determine whether it has been substituted
185 * by the online spare chip-select.
187 static mcamd_node_t
*
188 cs_sparedto(struct mcamd_hdl
*hdl
, mcamd_node_t
*cs
, mcamd_node_t
*mc
)
190 uint64_t csnum
, badcsnum
, sparecsnum
, tmpcsnum
;
192 if (!mcamd_get_numprops(hdl
,
193 cs
, MCAMD_PROP_NUM
, &csnum
,
194 mc
, MCAMD_PROP_BADCS
, &badcsnum
,
195 mc
, MCAMD_PROP_SPARECS
, &sparecsnum
,
197 mcamd_dprintf(hdl
, MCAMD_DBG_ERR
, "cs_sparedto: failed to "
198 "lookup required properties\n");
202 if ((badcsnum
== MC_INVALNUM
&& sparecsnum
== MC_INVALNUM
) ||
206 for (cs
= mcamd_cs_next(hdl
, mc
, NULL
); cs
!= NULL
;
207 cs
= mcamd_cs_next(hdl
, mc
, cs
)) {
208 if (!mcamd_get_numprop(hdl
, cs
, MCAMD_PROP_NUM
, &tmpcsnum
)) {
209 mcamd_dprintf(hdl
, MCAMD_DBG_ERR
, "cs_sparedto: "
210 "fail to lookup csnum - cannot reroute to spare\n");
213 if (tmpcsnum
== sparecsnum
)
218 mcamd_dprintf(hdl
, MCAMD_DBG_FLOW
, "cs_sparedto: cs#%d is "
219 "redirected to active online spare of cs#%d\n", csnum
,
222 mcamd_dprintf(hdl
, MCAMD_DBG_ERR
, "cs_sparedto: cs#%d is "
223 "redirected but cannot find spare cs# - cannout reroute to "
224 "cs#%d\n", csnum
, sparecsnum
);
231 * Having determined which node and chip-select an address maps to,
232 * as well as whether it is a dimm1, dimm2 or dimm1/dimm2 pair
233 * involved, fill the unum structure including an optional dimm offset
237 unum_fill(struct mcamd_hdl
*hdl
, mcamd_node_t
*cs
, int which
,
238 uint64_t iaddr
, mc_unum_t
*unump
, int incloff
)
240 uint64_t chipnum
, csnum
, dimm1
, dimm2
, ranknum
;
241 mcamd_node_t
*mc
, *dimm
;
245 if ((mc
= mcamd_cs_mc(hdl
, cs
)) == NULL
||
246 !mcamd_get_numprops(hdl
,
247 mc
, MCAMD_PROP_NUM
, &chipnum
,
248 cs
, MCAMD_PROP_NUM
, &csnum
,
249 cs
, MCAMD_PROP_DIMMRANK
, &ranknum
,
251 mcamd_dprintf(hdl
, MCAMD_DBG_ERR
, "unum_fill: failed to "
252 "lookup required properties\n");
253 return (mcamd_set_errno(hdl
, EMCAMD_TREEINVALID
));
256 if ((which
& CSDIMM1
) &&
257 !mcamd_get_numprop(hdl
, cs
, MCAMD_PROP_CSDIMM1
, &dimm1
) ||
259 !mcamd_get_numprop(hdl
, cs
, MCAMD_PROP_CSDIMM2
, &dimm2
)) {
260 mcamd_dprintf(hdl
, MCAMD_DBG_ERR
, "unum_fill: failed to "
261 "lookup dimm1/dimm2 properties\n");
262 return (mcamd_set_errno(hdl
, EMCAMD_TREEINVALID
));
265 unump
->unum_board
= 0;
266 unump
->unum_chip
= (int)chipnum
;
268 unump
->unum_chan
= MC_INVALNUM
;
269 unump
->unum_cs
= (int)csnum
;
270 unump
->unum_rank
= (int)ranknum
;
272 for (i
= 0; i
< MC_UNUM_NDIMM
; i
++) {
273 unump
->unum_dimms
[i
] = MC_INVALNUM
;
277 unump
->unum_dimms
[0] = (int)dimm1
;
278 offsetdimm
= (int)dimm1
;
281 unump
->unum_dimms
[0] = (int)dimm2
;
282 offsetdimm
= (int)dimm2
;
284 case CSDIMM1
| CSDIMM2
:
285 unump
->unum_dimms
[0] = (int)dimm1
;
286 unump
->unum_dimms
[1] = (int)dimm2
;
287 offsetdimm
= (int)dimm1
;
292 unump
->unum_offset
= MCAMD_RC_INVALID_OFFSET
;
297 * We wish to calculate a dimm offset. In the paired case we will
298 * lookup dimm1 (see offsetdimm above).
300 for (dimm
= mcamd_dimm_next(hdl
, mc
, NULL
); dimm
!= NULL
;
301 dimm
= mcamd_dimm_next(hdl
, mc
, dimm
)) {
303 if (!mcamd_get_numprop(hdl
, dimm
, MCAMD_PROP_NUM
, &dnum
)) {
304 mcamd_dprintf(hdl
, MCAMD_DBG_ERR
, "unum_fill: failed "
305 "to lookup dimm number property\n");
308 if (dnum
== offsetdimm
)
313 mcamd_dprintf(hdl
, MCAMD_DBG_ERR
, "unum_fill: failed to "
314 "find dimm with number %d for offset calculation\n",
316 unump
->unum_offset
= MCAMD_RC_INVALID_OFFSET
;
317 return (mcamd_set_errno(hdl
, EMCAMD_TREEINVALID
));
321 * mc_pa_to_offset sets the offset to an invalid value if
324 (void) mc_pa_to_offset(hdl
, mc
, cs
, iaddr
, &unump
->unum_offset
);
330 * We have translated a system address to a (node, chip-select), and wish
331 * to determine the associated dimm or dimms.
333 * A (node, chip-select) pair identifies one (in 64-bit MC mode) or two (in
334 * 128-bit MC mode) DIMMs. In the case of a single dimm it is usually in a
335 * lodimm (channel A) slot, but if mismatched dimm support is present it may
336 * be an updimm (channel B).
338 * Where just one dimm is associated with the chip-select we are done.
339 * Where there are two dimms associated with the chip-select we can
340 * use the ECC type and/or syndrome to determine which of the pair we
341 * resolve to, if the error is correctable. If the error is uncorrectable
342 * then in 64/8 ECC mode we can still resolve to a single dimm (since ECC
343 * is calculated and checked on each half of the data separately), but
344 * in ChipKill mode we cannot resolve down to a single dimm.
347 mc_whichdimm(struct mcamd_hdl
*hdl
, mcamd_node_t
*cs
, uint64_t pa
,
348 uint8_t valid_lo
, uint32_t synd
, int syndtype
)
350 int lobit
, hibit
, data
, check
;
351 uint64_t dimm1
, dimm2
;
356 * Read the associated dimm instance numbers. The provider must
357 * assure that if there is just one dimm then it is in the first
358 * property, and if there are two then the first must be on
361 if (!mcamd_get_numprops(hdl
,
362 cs
, MCAMD_PROP_CSDIMM1
, &dimm1
,
363 cs
, MCAMD_PROP_CSDIMM2
, &dimm2
,
365 mcamd_dprintf(hdl
, MCAMD_DBG_ERR
, "mc_whichdimm: failed to "
366 "lookup required properties");
367 return (mcamd_set_errno(hdl
, EMCAMD_TREEINVALID
));
369 ndimm
= (dimm1
!= MC_INVALNUM
) + (dimm2
!= MC_INVALNUM
);
371 mcamd_dprintf(hdl
, MCAMD_DBG_ERR
, "mc_whichdimm: found no "
372 "dimms associated with chip-select");
373 return (mcamd_set_errno(hdl
, EMCAMD_TREEINVALID
));
377 mcamd_dprintf(hdl
, MCAMD_DBG_FLOW
, "mc_whichdimm: just one "
378 "dimm associated with this chip-select");
383 * 64/8 ECC is checked separately for the upper and lower
384 * halves, so even an uncorrectable error is contained within
385 * one of the two halves. If we have sufficient address resolution
386 * then we can determine which DIMM.
388 if (syndtype
== AMD_SYNDTYPE_ECC
) {
389 if (valid_lo
<= MC_SYSADDR_LSB
) {
390 mcamd_dprintf(hdl
, MCAMD_DBG_FLOW
, "mc_whichdimm: 64/8 "
391 "ECC in 128-bit mode, PA 0x%llx is in %s half\n",
392 pa
, pa
& 0x8 ? "upper" : "lower");
393 return (pa
& 0x8 ? CSDIMM2
: CSDIMM1
);
395 mcamd_dprintf(hdl
, MCAMD_DBG_FLOW
, "mc_whichdimm: "
396 "64/8 ECC in 128-bit mode, PA 0x%llx with least "
397 "significant valid bit %d cannot be resolved to "
398 "a single DIMM\n", pa
, valid_lo
);
399 return (mcamd_set_errno(hdl
, EMCAMD_INSUFF_RES
));
406 if (mcamd_cksynd_decode(hdl
, synd
, &sym
, &pat
)) {
408 * A correctable ChipKill syndrome and we can tell
409 * which half the error was in from the symbol number.
411 if (mcamd_cksym_decode(hdl
, sym
, &lobit
, &hibit
, &data
,
413 return (mcamd_set_errno(hdl
, EMCAMD_SYNDINVALID
));
415 if (data
&& hibit
<= 63 || check
&& hibit
<= 7) {
416 mcamd_dprintf(hdl
, MCAMD_DBG_FLOW
, "mc_whichdimm: "
417 "ChipKill symbol %d (%s %d..%d), so LODIMM\n", sym
,
418 data
? "data" : "check", lobit
, hibit
);
421 mcamd_dprintf(hdl
, MCAMD_DBG_FLOW
, "mc_whichdimm: "
422 "ChipKill symbol %d (%s %d..%d), so UPDIMM\n", sym
,
423 data
? "data" : "check", lobit
, hibit
);
428 * An uncorrectable error while in ChipKill ECC mode - can't
429 * tell which dimm or dimms the errors lie within.
431 mcamd_dprintf(hdl
, MCAMD_DBG_FLOW
, "mc_whichhdimm: "
432 "uncorrectable ChipKill, could be either LODIMM "
434 return (CSDIMM1
| CSDIMM2
);
439 * Brute-force BKDG pa to cs translation, coded to look as much like the
440 * BKDG code as possible.
443 mc_bkdg_patounum(struct mcamd_hdl
*hdl
, mcamd_node_t
*mc
, uint64_t pa
,
444 uint8_t valid_lo
, uint32_t synd
, int syndtype
,
451 * Raw registers as per BKDG
454 uint32_t DramBase
, DramLimit
;
455 uint32_t CSBase
, CSMask
;
457 * Variables as per BKDG
460 uint32_t SystemAddr
= (uint32_t)(pa
>> 8);
461 uint64_t IntlvEn
, IntlvSel
;
463 uint32_t InputAddr
, Temp
;
465 if (!mcamd_get_numprops(hdl
,
466 mc
, MCAMD_PROP_NUM
, &mcnum
,
467 mc
, MCAMD_PROP_REV
, &rev
, NULL
) || !mcamd_get_cfgregs(hdl
,
468 mc
, MCAMD_REG_DRAMBASE
, &DramBase
,
469 mc
, MCAMD_REG_DRAMLIMIT
, &DramLimit
,
470 mc
, MCAMD_REG_DRAMHOLE
, &HoleEn
, NULL
)) {
471 mcamd_dprintf(hdl
, MCAMD_DBG_ERR
, "mc_bkdg_patounm: failed "
472 "to lookup required properties and registers\n");
473 return (mcamd_set_errno(hdl
, EMCAMD_TREEINVALID
));
477 * BKDG line to skip Why
479 * F1Offset = ... Register already read,
480 * DramBase = Get_PCI() and retrieved above.
481 * DramEn = ... Function only called for enabled nodes.
483 IntlvEn
= (DramBase
& 0x00000700) >> 8;
484 DramBase
&= 0xffff0000;
485 /* DramLimit = Get_PCI() Retrieved above */
486 IntlvSel
= (DramLimit
& 0x00000700) >> 8;
487 DramLimit
|= 0x0000ffff;
488 /* HoleEn = ... Retrieved above */
489 HoleOffset
= (HoleEn
& 0x0000ff00) << 8;
490 HoleEn
&= 0x00000001;
492 if (!(DramBase
<= SystemAddr
&& SystemAddr
<= DramLimit
)) {
493 mcamd_dprintf(hdl
, MCAMD_DBG_FLOW
, "mc_bkdg_patounum: "
494 "SystemAddr 0x%x derived from PA 0x%llx is not in the "
495 "address range [0x%x, 0x%x] of MC %d\n",
496 SystemAddr
, pa
, DramBase
, DramLimit
, (int)mcnum
);
497 return (mcamd_set_errno(hdl
, EMCAMD_NOADDR
));
500 if (HoleEn
&& SystemAddr
> 0x00ffffff)
501 InputAddr
= SystemAddr
- HoleOffset
;
503 InputAddr
= SystemAddr
- DramBase
;
506 if (IntlvSel
== ((SystemAddr
>> 4) & IntlvEn
)) {
518 return (mcamd_set_errno(hdl
,
519 EMCAMD_TREEINVALID
));
521 Temp
= (InputAddr
>> (4 + Ilog
)) << 4;
522 InputAddr
= (Temp
| (SystemAddr
& 0x0000000f));
525 mcamd_dprintf(hdl
, MCAMD_DBG_FLOW
, "mc_bkdg_patounum: "
526 "Node interleaving, MC node %d not selected\n",
528 return (mcamd_set_errno(hdl
, EMCAMD_NOADDR
));
532 if (!MC_REV_MATCH(rev
, MC_F_REVS_FG
))
535 for (cs
= mcamd_cs_next(hdl
, mc
, NULL
); cs
!= NULL
;
536 cs
= mcamd_cs_next(hdl
, mc
, cs
)) {
537 uint64_t csnum
, CSEn
;
539 if (!mcamd_get_cfgregs(hdl
,
540 cs
, MCAMD_REG_CSBASE
, &CSBase
,
541 cs
, MCAMD_REG_CSMASK
, &CSMask
,
543 !mcamd_get_numprops(hdl
,
544 cs
, MCAMD_PROP_NUM
, &csnum
,
545 cs
, MCAMD_PROP_CSBE
, &CSEn
,
547 mcamd_dprintf(hdl
, MCAMD_DBG_ERR
, "mc_bkdg_patounm: "
548 "failed to read cs registers\n");
549 return (mcamd_set_errno(hdl
, EMCAMD_TREEINVALID
));
553 * BKDG line to skip Why
555 * F2Offset = Register already read,
556 * F2MaskOffset (rev F) Register already read
557 * CSBase = Register already read
558 * CSEn = We only keep enabled cs.
560 if (MC_REV_MATCH(rev
, MC_F_REVS_FG
)) {
561 CSBase
&= 0x1ff83fe0;
562 /* CSMask = Get_PCI() Retrieved above */
563 CSMask
= (CSMask
| 0x0007c01f) & 0x1fffffff;
565 CSBase
&= 0xffe0fe00;
566 /* CSMask = Get_PCI() Retrieved above */
567 CSMask
= (CSMask
| 0x001f01ff) & 0x3fffffff;
570 if (CSEn
&& (InputAddr
& ~CSMask
) == (CSBase
& ~CSMask
)) {
571 mcamd_node_t
*sparecs
;
573 mcamd_dprintf(hdl
, MCAMD_DBG_FLOW
, "mc_bkdg_patounum: "
574 "match for chip select %d of MC %d\n", (int)csnum
,
577 if ((sparecs
= cs_sparedto(hdl
, cs
, mc
)) != NULL
)
580 if ((which
= mc_whichdimm(hdl
, cs
, pa
, valid_lo
,
581 synd
, syndtype
)) < 0)
582 return (-1); /* errno is set for us */
585 * The BKDG algorithm drops low-order bits that
586 * are unimportant in deriving chip-select but are
587 * included in row/col/bank mapping, so do not
588 * perform offset calculation in this case.
590 if (unum_fill(hdl
, cs
, which
, InputAddr
, unump
, 0) < 0)
591 return (-1); /* errno is set for us */
597 mcamd_dprintf(hdl
, MCAMD_DBG_ERR
, "mc_bkdg_patounum: in range "
598 "for MC %d but no cs responds\n", (int)mcnum
);
600 return (mcamd_set_errno(hdl
, EMCAMD_NOADDR
));
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.
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
)
615 mcamd_node_t
*cs
, *sparecs
;
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.
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");
632 if (iaddr_gen(hdl
, mc
, pa
, &iaddr
) < 0)
633 return (-1); /* errno is set for us */
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
))
642 return (mcamd_set_errno(hdl
, EMCAMD_NOADDR
));
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.
651 if ((sparecs
= cs_sparedto(hdl
, cs
, mc
)) != NULL
) {
655 if ((which
= mc_whichdimm(hdl
, cs
, pa
, valid_lo
, synd
,
657 return (-1); /* errno is set for us */
659 if (unum_fill(hdl
, cs
, which
, iaddr
, unump
, 1) < 0)
660 return (-1); /* errno is set for us */
663 mcamd_dprintf(hdl
, MCAMD_DBG_FLOW
, "bkdgres=%d res=0\n", bkdgres
);
664 /* offset is not checked - see note in BKDG algorithm */
666 mcamd_dprintf(hdl
, MCAMD_DBG_ERR
, "BKDG alg failed while "
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]);
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
);
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
,
699 mcamd_dprintf(hdl
, MCAMD_DBG_FLOW
, "mcamd_patounum: pa=0x%llx, "
700 "synd=0x%x, syndtype=%d\n", pa
, synd
, syndtype
);
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
));
708 if (!mcamd_synd_validate(hdl
, synd
, syndtype
))
709 return (mcamd_set_errno(hdl
, EMCAMD_SYNDINVALID
));
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)
717 if (mcamd_errno(hdl
) != EMCAMD_NOADDR
)
721 return (-1); /* errno is set for us */