xref: /illumos-gate/usr/src/common/mc/mc-amd/mcamd_patounum.c (revision 60a3f738d56f92ae8b80e4b62a2331c6e1f2311f)
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 2006 Sun Microsystems, Inc.  All rights reserved.
22  * Use is subject to license terms.
23  */
24 
25 #pragma ident	"%Z%%M%	%I%	%E% SMI"
26 
27 /*
28  * Given a physical address and an optional syndrome, determine the
29  * name of the memory module that contains it.
30  */
31 
32 #include <sys/errno.h>
33 #include <sys/types.h>
34 #include <sys/mc.h>
35 
36 #include <mcamd_api.h>
37 #include <mcamd_err.h>
38 
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
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
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 *
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
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 = chipnum;
265 	unump->unum_mc = 0;
266 	unump->unum_cs = csnum;
267 	unump->unum_rank = ranknum;
268 
269 	for (i = 0; i < MC_UNUM_NDIMM; i++) {
270 		unump->unum_dimms[i] = MC_INVALNUM;
271 	}
272 	switch (which) {
273 	case CSDIMM1:
274 		unump->unum_dimms[0] = dimm1;
275 		offsetdimm = dimm1;
276 		break;
277 	case CSDIMM2:
278 		unump->unum_dimms[0] = dimm2;
279 		offsetdimm = dimm2;
280 		break;
281 	case CSDIMM1 | CSDIMM2:
282 		unump->unum_dimms[0] = dimm1;
283 		unump->unum_dimms[1] = dimm2;
284 		offsetdimm = dimm1;
285 		break;
286 	}
287 
288 	if (!incloff) {
289 		unump->unum_offset = MCAMD_RC_INVALID_OFFSET;
290 		return (0);
291 	}
292 
293 	/*
294 	 * We wish to calculate a dimm offset.  In the paired case we will
295 	 * lookup dimm1 (see offsetdimm above).
296 	 */
297 	for (dimm = mcamd_dimm_next(hdl, mc, NULL); dimm != NULL;
298 	    dimm = mcamd_dimm_next(hdl, mc, dimm)) {
299 		uint64_t dnum;
300 		if (!mcamd_get_numprop(hdl, dimm, MCAMD_PROP_NUM, &dnum)) {
301 			mcamd_dprintf(hdl, MCAMD_DBG_ERR, "unum_fill: failed "
302 			    "to lookup dimm number property\n");
303 			continue;
304 		}
305 		if (dnum == offsetdimm)
306 			break;
307 	}
308 
309 	if (dimm == NULL) {
310 		mcamd_dprintf(hdl, MCAMD_DBG_ERR, "unum_fill: failed to "
311 		    "find dimm with number %d for offset calculation\n",
312 		    offsetdimm);
313 		unump->unum_offset = MCAMD_RC_INVALID_OFFSET;
314 		return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
315 	}
316 
317 	/*
318 	 * mc_pa_to_offset sets the offset to an invalid value if
319 	 * it hits an error.
320 	 */
321 	(void) mc_pa_to_offset(hdl, mc, cs, iaddr, &unump->unum_offset);
322 
323 	return (0);
324 }
325 
326 /*
327  * We have translated a system address to a (node, chip-select), and wish
328  * to determine the associated dimm or dimms.
329  *
330  * A (node, chip-select) pair identifies one (in 64-bit MC mode) or two (in
331  * 128-bit MC mode) DIMMs.  In the case of a single dimm it is usually in a
332  * lodimm (channel A) slot, but if mismatched dimm support is present it may
333  * be an updimm (channel B).
334  *
335  * Where just one dimm is associated with the chip-select we are done.
336  * Where there are two dimms associated with the chip-select we can
337  * use the ECC type and/or syndrome to determine which of the pair we
338  * resolve to, if the error is correctable.  If the error is uncorrectable
339  * then in 64/8 ECC mode we can still resolve to a single dimm (since ECC
340  * is calculated and checked on each half of the data separately), but
341  * in ChipKill mode we cannot resolve down to a single dimm.
342  */
343 static int
344 mc_whichdimm(struct mcamd_hdl *hdl, mcamd_node_t *cs, uint64_t pa,
345     uint32_t synd, int syndtype)
346 {
347 	int lobit, hibit, data, check;
348 	uint64_t dimm1, dimm2;
349 	uint_t sym, pat;
350 	int ndimm;
351 
352 	/*
353 	 * Read the associated dimm instance numbers.  The provider must
354 	 * assure that if there is just one dimm then it is in the first
355 	 * property, and if there are two then the first must be on
356 	 * channel A.
357 	 */
358 	if (!mcamd_get_numprops(hdl,
359 	    cs, MCAMD_PROP_CSDIMM1, &dimm1,
360 	    cs, MCAMD_PROP_CSDIMM2, &dimm2,
361 	    NULL)) {
362 		mcamd_dprintf(hdl, MCAMD_DBG_ERR, "mc_whichdimm: failed to "
363 		    "lookup required properties");
364 		return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
365 	}
366 	ndimm = (dimm1 != MC_INVALNUM) + (dimm2 != MC_INVALNUM);
367 	if (ndimm == 0) {
368 		mcamd_dprintf(hdl, MCAMD_DBG_ERR, "mc_whichdimm: found no "
369 		    "dimms associated with chip-select");
370 		return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
371 	}
372 
373 	if (ndimm == 1) {
374 		mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mc_whichdimm: just one "
375 		    "dimm associated with this chip-select");
376 		return (CSDIMM1);
377 	}
378 
379 	/*
380 	 * 64/8 ECC is checked separately for the upper and lower
381 	 * halves, so even an uncorrectable error is contained within
382 	 * one of the two halves.  The error address is accurate to
383 	 * 8 bytes, so bit 4 distinguises upper from lower.
384 	 */
385 	if (syndtype == AMD_SYNDTYPE_ECC) {
386 		mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mc_whichdimm: 64/8 ECC "
387 		    "and PA 0x%llx is in %s half\n", pa,
388 		    pa & 8 ? "lower" : "upper");
389 		return (pa & 8 ? CSDIMM2 : CSDIMM1);
390 	}
391 
392 	/*
393 	 * ChipKill ECC
394 	 */
395 	if (mcamd_cksynd_decode(hdl, synd, &sym, &pat)) {
396 		/*
397 		 * A correctable ChipKill syndrome and we can tell
398 		 * which half the error was in from the symbol number.
399 		 */
400 		if (mcamd_cksym_decode(hdl, sym, &lobit, &hibit, &data,
401 		    &check) == 0)
402 			return (mcamd_set_errno(hdl, EMCAMD_SYNDINVALID));
403 
404 		if (data && hibit <= 63 || check && hibit <= 7) {
405 			mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mc_whichdimm: "
406 			    "ChipKill symbol %d (%s %d..%d), so LODIMM\n", sym,
407 			    data ? "data" : "check", lobit, hibit);
408 			return (CSDIMM1);
409 		} else {
410 			mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mc_whichdimm: "
411 			    "ChipKill symbol %d (%s %d..%d), so UPDIMM\n", sym,
412 			    data ? "data" : "check", lobit, hibit);
413 			return (CSDIMM2);
414 		}
415 	} else {
416 		/*
417 		 * An uncorrectable error while in ChipKill ECC mode - can't
418 		 * tell which dimm or dimms the errors lie within.
419 		 */
420 		mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mc_whichhdimm: "
421 		    "uncorrectable ChipKill, could be either LODIMM "
422 		    "or UPDIMM\n");
423 		return (CSDIMM1 | CSDIMM2);
424 	}
425 }
426 
427 /*
428  * Brute-force BKDG pa to cs translation, coded to look as much like the
429  * BKDG code as possible.
430  */
431 static int
432 mc_bkdg_patounum(struct mcamd_hdl *hdl, mcamd_node_t *mc, uint64_t pa,
433     uint32_t synd, int syndtype, mc_unum_t *unump)
434 {
435 	int which;
436 	uint64_t mcnum, rev;
437 	mcamd_node_t *cs;
438 	/*
439 	 * Raw registers as per BKDG
440 	 */
441 	uint32_t HoleEn;
442 	uint32_t DramBase, DramLimit;
443 	uint32_t CSBase,  CSMask;
444 	/*
445 	 * Variables as per BKDG
446 	 */
447 	int Ilog;
448 	uint32_t SystemAddr = (uint32_t)(pa >> 8);
449 	uint64_t IntlvEn, IntlvSel;
450 	uint32_t HoleOffset;
451 	uint32_t InputAddr, Temp;
452 
453 	if (!mcamd_get_numprops(hdl,
454 	    mc, MCAMD_PROP_NUM, &mcnum,
455 	    mc, MCAMD_PROP_REV, &rev, NULL) || !mcamd_get_cfgregs(hdl,
456 	    mc, MCAMD_REG_DRAMBASE, &DramBase,
457 	    mc, MCAMD_REG_DRAMLIMIT, &DramLimit,
458 	    mc, MCAMD_REG_DRAMHOLE, &HoleEn, NULL)) {
459 		mcamd_dprintf(hdl, MCAMD_DBG_ERR, "mc_bkdg_patounm: failed "
460 		    "to lookup required properties and registers\n");
461 		return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
462 	}
463 
464 	/*
465 	 * BKDG line to skip		Why
466 	 *
467 	 * F1Offset = ...		Register already read,
468 	 * DramBase = Get_PCI()		and retrieved above.
469 	 * DramEn = ...			Function only called for enabled nodes.
470 	 */
471 	IntlvEn = (DramBase & 0x00000700) >> 8;
472 	DramBase &= 0xffff0000;
473 	/* DramLimit = Get_PCI()	Retrieved above */
474 	IntlvSel = (DramLimit & 0x00000700) >> 8;
475 	DramLimit |= 0x0000ffff;
476 	/* HoleEn = ...			Retrieved above */
477 	HoleOffset = (HoleEn & 0x0000ff00) << 8;
478 	HoleEn &= 0x00000001;
479 
480 	if (!(DramBase <= SystemAddr && SystemAddr <= DramLimit)) {
481 		mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mc_bkdg_patounum: "
482 		    "SystemAddr 0x%x derived from PA 0x%llx is not in the "
483 		    "address range [0x%x, 0x%x] of MC %d\n",
484 		    SystemAddr, pa, DramBase, DramLimit, (int)mcnum);
485 		return (mcamd_set_errno(hdl, EMCAMD_NOADDR));
486 	}
487 
488 	if (HoleEn && SystemAddr > 0x00ffffff)
489 		InputAddr = SystemAddr - HoleOffset;
490 
491 	InputAddr = SystemAddr - DramBase;
492 
493 	if (IntlvEn) {
494 		if (IntlvSel == ((SystemAddr >> 4) & IntlvEn)) {
495 			switch (IntlvEn) {
496 			case 1:
497 				Ilog = 1;
498 				break;
499 			case 3:
500 				Ilog = 2;
501 				break;
502 			case 7:
503 				Ilog = 3;
504 				break;
505 			default:
506 				return (mcamd_set_errno(hdl,
507 				    EMCAMD_TREEINVALID));
508 			}
509 			Temp = (InputAddr >> (4 + Ilog)) << 4;
510 			InputAddr = (Temp | (SystemAddr & 0x0000000f));
511 		} else {
512 			/* not this node */
513 			mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mc_bkdg_patounum: "
514 			    "Node interleaving, MC node %d not selected\n",
515 			    (int)mcnum);
516 			return (mcamd_set_errno(hdl, EMCAMD_NOADDR));
517 		}
518 	}
519 
520 	if (!MC_REV_MATCH(rev, MC_REVS_FG))
521 		InputAddr <<= 4;
522 
523 	for (cs = mcamd_cs_next(hdl, mc, NULL); cs != NULL;
524 	    cs = mcamd_cs_next(hdl, mc, cs)) {
525 		uint64_t csnum, CSEn;
526 
527 		if (!mcamd_get_cfgregs(hdl,
528 		    cs, MCAMD_REG_CSBASE, &CSBase,
529 		    cs, MCAMD_REG_CSMASK, &CSMask,
530 		    NULL) ||
531 		    !mcamd_get_numprops(hdl,
532 		    cs, MCAMD_PROP_NUM, &csnum,
533 		    cs, MCAMD_PROP_CSBE, &CSEn,
534 		    NULL)) {
535 			mcamd_dprintf(hdl, MCAMD_DBG_ERR, "mc_bkdg_patounm: "
536 			    "failed to read cs registers\n");
537 			return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
538 		}
539 
540 		/*
541 		 * BKDG line to skip		Why
542 		 *
543 		 * F2Offset =			Register already read,
544 		 * F2MaskOffset (rev F)		Register already read
545 		 * CSBase =			Register already read
546 		 * CSEn =			We only keep enabled cs.
547 		 */
548 		if (MC_REV_MATCH(rev, MC_REVS_FG)) {
549 			CSBase &= 0x1ff83fe0;
550 			/* CSMask = Get_PCI()		Retrieved above */
551 			CSMask = (CSMask | 0x0007c01f) & 0x1fffffff;
552 		} else {
553 			CSBase &= 0xffe0fe00;
554 			/* CSMask = Get_PCI()		Retrieved above */
555 			CSMask = (CSMask | 0x001f01ff) & 0x3fffffff;
556 		}
557 
558 		if (CSEn && (InputAddr & ~CSMask) == (CSBase & ~CSMask)) {
559 			mcamd_node_t *sparecs;
560 
561 			mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mc_bkdg_patounum: "
562 			    "match for chip select %d of MC %d\n", (int)csnum,
563 			    (int)mcnum);
564 
565 			if ((sparecs = cs_sparedto(hdl, cs, mc)) != NULL)
566 				cs = sparecs;
567 
568 			if ((which = mc_whichdimm(hdl, cs, pa, synd,
569 			    syndtype)) < 0)
570 				return (-1); /* errno is set for us */
571 
572 			/*
573 			 * The BKDG algorithm drops low-order bits that
574 			 * are unimportant in deriving chip-select but are
575 			 * included in row/col/bank mapping, so do not
576 			 * perform offset calculation in this case.
577 			 */
578 			if (unum_fill(hdl, cs, which, InputAddr, unump, 0) < 0)
579 				return (-1); /* errno is set for us */
580 
581 			return (0);
582 		}
583 	}
584 
585 	mcamd_dprintf(hdl, MCAMD_DBG_ERR, "mc_bkdg_patounum: in range "
586 	    "for MC %d but no cs responds\n", (int)mcnum);
587 
588 	return (mcamd_set_errno(hdl, EMCAMD_NOADDR));
589 }
590 
591 /*
592  * Called for each memory controller to see if the given address is
593  * mapped to this node (as determined in iaddr_gen) and, if so, which
594  * chip-select on this node responds.
595  */
596 
597 /*ARGSUSED*/
598 static int
599 mc_patounum(struct mcamd_hdl *hdl, mcamd_node_t *mc, uint64_t pa,
600     uint32_t synd, int syndtype, mc_unum_t *unump)
601 {
602 	uint64_t iaddr;
603 	mcamd_node_t *cs, *sparecs;
604 	int which;
605 #ifdef DEBUG
606 	mc_unum_t bkdg_unum;
607 	int bkdgres;
608 
609 	/*
610 	 * We perform the translation twice, once using the brute-force
611 	 * approach of the BKDG and again using a more elegant but more
612 	 * difficult to review against the BKDG approach.
613 	 */
614 	mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "BKDG brute-force method begins\n");
615 	bkdgres = mc_bkdg_patounum(hdl, mc, pa, synd, syndtype, &bkdg_unum);
616 	mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "BKDG brute-force method ends\n");
617 #endif
618 
619 	if (iaddr_gen(hdl, mc, pa, &iaddr) < 0)
620 		return (-1); /* errno is set for us */
621 
622 	for (cs = mcamd_cs_next(hdl, mc, NULL); cs != NULL;
623 	    cs = mcamd_cs_next(hdl, mc, cs)) {
624 		if (cs_match(hdl, iaddr, cs))
625 			break;
626 	}
627 
628 	if (cs == NULL)
629 		return (mcamd_set_errno(hdl, EMCAMD_NOADDR));
630 
631 	/*
632 	 * If the spare chip-select has been swapped in for the one just
633 	 * matched then it is really the spare that we are after.  Note that
634 	 * when the swap is done the csbase, csmask and CSBE of the spare
635 	 * rank do not change - accesses to the bad rank (as nominated in
636 	 * the Online Spare Control Register) are redirect to the spare.
637 	 */
638 	if ((sparecs = cs_sparedto(hdl, cs, mc)) != NULL) {
639 		cs = sparecs;
640 	}
641 
642 	if ((which = mc_whichdimm(hdl, cs, pa, synd, syndtype)) < 0)
643 		return (-1); /* errno is set for us */
644 
645 	if (unum_fill(hdl, cs, which, iaddr, unump, 1) < 0)
646 		return (-1); /* errno is set for us */
647 
648 #ifdef DEBUG
649 	mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "bkdgres=%d res=0\n", bkdgres);
650 	/* offset is not checked - see note in BKDG algorithm */
651 	if (bkdgres != 0) {
652 		mcamd_dprintf(hdl, MCAMD_DBG_ERR, "BKDG alg failed while "
653 		    "ours succeeded\n");
654 	} else if (!(unump->unum_board == bkdg_unum.unum_board &&
655 	    unump->unum_chip == bkdg_unum.unum_chip &&
656 	    unump->unum_mc == bkdg_unum.unum_mc &&
657 	    unump->unum_cs == bkdg_unum.unum_cs &&
658 	    unump->unum_dimms[0] == bkdg_unum.unum_dimms[0] &&
659 	    unump->unum_dimms[1] == bkdg_unum.unum_dimms[1])) {
660 		mcamd_dprintf(hdl, MCAMD_DBG_ERR,
661 		    "BKDG: node %d mc %d cs %d dimm(s) %d/%d\n"
662 		    "Ours: node 5d mc %d cs %d dimm(s) %d/%d\n",
663 		    bkdg_unum.unum_chip, bkdg_unum.unum_mc, bkdg_unum.unum_cs,
664 		    bkdg_unum.unum_dimms[0], bkdg_unum.unum_dimms[1],
665 		    unump->unum_chip, unump->unum_mc, unump->unum_cs,
666 		    unump->unum_dimms[0], unump->unum_dimms[1]);
667 	}
668 #endif /* DEBUG */
669 
670 	mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "Result: chip %d mc %d cs %d "
671 	    "offset 0x%llx\n", unump->unum_chip, unump->unum_mc,
672 	    unump->unum_cs, unump->unum_offset);
673 
674 	return (0);
675 }
676 
677 int
678 mcamd_patounum(struct mcamd_hdl *hdl, mcamd_node_t *root, uint64_t pa,
679     uint32_t synd, int syndtype, mc_unum_t *unump)
680 {
681 	mcamd_node_t *mc;
682 
683 	mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mcamd_patounum: pa=0x%llx, "
684 	    "synd=0x%x, syndtype=%d\n", pa, synd, syndtype);
685 
686 	/*
687 	 * Consider allowing syndrome 0 to act as a generic multibit
688 	 * syndrome.  For example icache inf_sys_ecc1 captures an address
689 	 * but no syndrome - we can still resolve this to a dimm or dimms.
690 	 */
691 	if (!mcamd_synd_validate(hdl, synd, syndtype))
692 		return (mcamd_set_errno(hdl, EMCAMD_SYNDINVALID));
693 
694 	for (mc = mcamd_mc_next(hdl, root, NULL); mc != NULL;
695 	    mc = mcamd_mc_next(hdl, root, mc)) {
696 		if (mc_patounum(hdl, mc, pa, synd, syndtype, unump) == 0)
697 			return (0);
698 
699 		if (mcamd_errno(hdl) != EMCAMD_NOADDR)
700 			break;
701 	}
702 
703 	return (-1); /* errno is set for us */
704 }
705