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#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#define	MC_SYSADDR_MSB	39
40#define	MC_SYSADDR_LSB	3
41
42#define	CSDIMM1	0x1
43#define	CSDIMM2	0x2
44
45#define	BITS(val, high, low) \
46	((val) & (((2ULL << (high)) - 1) & ~((1ULL << (low)) - 1)))
47
48/*
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
54 * interleaving.
55 */
56static int
57iaddr_gen(struct mcamd_hdl *hdl, mcamd_node_t *mc, uint64_t pa,
58    uint64_t *iaddrp)
59{
60	uint64_t orig = pa;
61	uint64_t mcnum, base, lim, dramaddr, ilen, ilsel, top, holesz;
62
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,
70	    NULL)) {
71		mcamd_dprintf(hdl, MCAMD_DBG_ERR, "iaddr_gen: failed to "
72		    "lookup required properties");
73		return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
74	}
75
76	/*
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.
80	 */
81	if (base == lim)
82		return (mcamd_set_errno(hdl, EMCAMD_NOADDR));
83
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,
87		    (int)mcnum);
88		return (mcamd_set_errno(hdl, EMCAMD_NOADDR));
89	}
90
91	/*
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
98	 * the hole.
99	 */
100	if (holesz != 0 && pa >= 0x100000000) {
101		pa -= holesz;
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);
105	}
106
107	dramaddr = BITS(pa, 39, 0) - BITS(base, 39, 24);
108
109	if (ilen != 0) {
110		int pailsel;
111
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));
116		}
117
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));
124		}
125
126		if (ilen == 1)
127			top = BITS(dramaddr, 36, 13) >> 1;
128		else if (ilen == 3)
129			top = BITS(dramaddr, 37, 14) >> 2;
130		else if (ilen == 7)
131			top = BITS(dramaddr, 38, 15) >> 3;
132	} else {
133		top = BITS(dramaddr, 35, 12);
134	}
135
136	*iaddrp = top | BITS(dramaddr, 11, 0);
137
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);
141
142	return (0);
143}
144
145/*
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.
151 */
152static int
153cs_match(struct mcamd_hdl *hdl, uint64_t iaddr, mcamd_node_t *cs)
154{
155	uint64_t csnum, csbase, csmask, csbe;
156	int match = 0;
157
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,
163	    NULL)) {
164		mcamd_dprintf(hdl, MCAMD_DBG_ERR, "cs_match: failed to lookup "
165		    "required properties\n");
166		return (0);
167	}
168
169	if (csbe) {
170		match = ((iaddr & ~csmask) == (csbase & ~csmask));
171
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);
175	} else {
176		mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "cs_match: iaddr 0x%llx "
177		    "does not match disabled CS %d\n", iaddr, (int)csnum);
178	}
179
180	return (match);
181}
182
183/*
184 * Given a chip-select node determine whether it has been substituted
185 * by the online spare chip-select.
186 */
187static mcamd_node_t *
188cs_sparedto(struct mcamd_hdl *hdl, mcamd_node_t *cs, mcamd_node_t *mc)
189{
190	uint64_t csnum, badcsnum, sparecsnum, tmpcsnum;
191
192	if (!mcamd_get_numprops(hdl,
193	    cs, MCAMD_PROP_NUM, &csnum,
194	    mc, MCAMD_PROP_BADCS, &badcsnum,
195	    mc, MCAMD_PROP_SPARECS, &sparecsnum,
196	    NULL)) {
197		mcamd_dprintf(hdl, MCAMD_DBG_ERR, "cs_sparedto: failed to "
198		    "lookup required properties\n");
199		return (NULL);
200	}
201
202	if ((badcsnum == MC_INVALNUM && sparecsnum == MC_INVALNUM) ||
203	    csnum != badcsnum)
204		return (NULL);
205
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");
211			return (NULL);
212		}
213		if (tmpcsnum == sparecsnum)
214			break;
215	}
216
217	if (cs != NULL) {
218		mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "cs_sparedto: cs#%d is "
219		    "redirected to active online spare of cs#%d\n", csnum,
220		    sparecsnum);
221	} else {
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);
225	}
226
227	return (cs);
228}
229
230/*
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
234 * member.
235 */
236static int
237unum_fill(struct mcamd_hdl *hdl, mcamd_node_t *cs, int which,
238    uint64_t iaddr, mc_unum_t *unump, int incloff)
239{
240	uint64_t chipnum, csnum, dimm1, dimm2, ranknum;
241	mcamd_node_t *mc, *dimm;
242	int offsetdimm;
243	int i;
244
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,
250	    NULL)) {
251		mcamd_dprintf(hdl, MCAMD_DBG_ERR, "unum_fill: failed to "
252		    "lookup required properties\n");
253		return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
254	}
255
256	if ((which & CSDIMM1) &&
257	    !mcamd_get_numprop(hdl, cs, MCAMD_PROP_CSDIMM1, &dimm1) ||
258	    (which & CSDIMM2) &&
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));
263	}
264
265	unump->unum_board = 0;
266	unump->unum_chip = (int)chipnum;
267	unump->unum_mc = 0;
268	unump->unum_chan = MC_INVALNUM;
269	unump->unum_cs = (int)csnum;
270	unump->unum_rank = (int)ranknum;
271
272	for (i = 0; i < MC_UNUM_NDIMM; i++) {
273		unump->unum_dimms[i] = MC_INVALNUM;
274	}
275	switch (which) {
276	case CSDIMM1:
277		unump->unum_dimms[0] = (int)dimm1;
278		offsetdimm = (int)dimm1;
279		break;
280	case CSDIMM2:
281		unump->unum_dimms[0] = (int)dimm2;
282		offsetdimm = (int)dimm2;
283		break;
284	case CSDIMM1 | CSDIMM2:
285		unump->unum_dimms[0] = (int)dimm1;
286		unump->unum_dimms[1] = (int)dimm2;
287		offsetdimm = (int)dimm1;
288		break;
289	}
290
291	if (!incloff) {
292		unump->unum_offset = MCAMD_RC_INVALID_OFFSET;
293		return (0);
294	}
295
296	/*
297	 * We wish to calculate a dimm offset.  In the paired case we will
298	 * lookup dimm1 (see offsetdimm above).
299	 */
300	for (dimm = mcamd_dimm_next(hdl, mc, NULL); dimm != NULL;
301	    dimm = mcamd_dimm_next(hdl, mc, dimm)) {
302		uint64_t dnum;
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");
306			continue;
307		}
308		if (dnum == offsetdimm)
309			break;
310	}
311
312	if (dimm == NULL) {
313		mcamd_dprintf(hdl, MCAMD_DBG_ERR, "unum_fill: failed to "
314		    "find dimm with number %d for offset calculation\n",
315		    offsetdimm);
316		unump->unum_offset = MCAMD_RC_INVALID_OFFSET;
317		return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
318	}
319
320	/*
321	 * mc_pa_to_offset sets the offset to an invalid value if
322	 * it hits an error.
323	 */
324	(void) mc_pa_to_offset(hdl, mc, cs, iaddr, &unump->unum_offset);
325
326	return (0);
327}
328
329/*
330 * We have translated a system address to a (node, chip-select), and wish
331 * to determine the associated dimm or dimms.
332 *
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).
337 *
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.
345 */
346static int
347mc_whichdimm(struct mcamd_hdl *hdl, mcamd_node_t *cs, uint64_t pa,
348    uint8_t valid_lo, uint32_t synd, int syndtype)
349{
350	int lobit, hibit, data, check;
351	uint64_t dimm1, dimm2;
352	uint_t sym, pat;
353	int ndimm;
354
355	/*
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
359	 * channel A.
360	 */
361	if (!mcamd_get_numprops(hdl,
362	    cs, MCAMD_PROP_CSDIMM1, &dimm1,
363	    cs, MCAMD_PROP_CSDIMM2, &dimm2,
364	    NULL)) {
365		mcamd_dprintf(hdl, MCAMD_DBG_ERR, "mc_whichdimm: failed to "
366		    "lookup required properties");
367		return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
368	}
369	ndimm = (dimm1 != MC_INVALNUM) + (dimm2 != MC_INVALNUM);
370	if (ndimm == 0) {
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));
374	}
375
376	if (ndimm == 1) {
377		mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mc_whichdimm: just one "
378		    "dimm associated with this chip-select");
379		return (CSDIMM1);
380	}
381
382	/*
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.
387	 */
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);
394		} else {
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));
400		}
401	}
402
403	/*
404	 * ChipKill ECC
405	 */
406	if (mcamd_cksynd_decode(hdl, synd, &sym, &pat)) {
407		/*
408		 * A correctable ChipKill syndrome and we can tell
409		 * which half the error was in from the symbol number.
410		 */
411		if (mcamd_cksym_decode(hdl, sym, &lobit, &hibit, &data,
412		    &check) == 0)
413			return (mcamd_set_errno(hdl, EMCAMD_SYNDINVALID));
414
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);
419			return (CSDIMM1);
420		} else {
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);
424			return (CSDIMM2);
425		}
426	} else {
427		/*
428		 * An uncorrectable error while in ChipKill ECC mode - can't
429		 * tell which dimm or dimms the errors lie within.
430		 */
431		mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mc_whichhdimm: "
432		    "uncorrectable ChipKill, could be either LODIMM "
433		    "or UPDIMM\n");
434		return (CSDIMM1 | CSDIMM2);
435	}
436}
437
438/*
439 * Brute-force BKDG pa to cs translation, coded to look as much like the
440 * BKDG code as possible.
441 */
442static int
443mc_bkdg_patounum(struct mcamd_hdl *hdl, mcamd_node_t *mc, uint64_t pa,
444    uint8_t valid_lo, uint32_t synd, int syndtype,
445    mc_unum_t *unump)
446{
447	int which;
448	uint64_t mcnum, rev;
449	mcamd_node_t *cs;
450	/*
451	 * Raw registers as per BKDG
452	 */
453	uint32_t HoleEn;
454	uint32_t DramBase, DramLimit;
455	uint32_t CSBase,  CSMask;
456	/*
457	 * Variables as per BKDG
458	 */
459	int Ilog;
460	uint32_t SystemAddr = (uint32_t)(pa >> 8);
461	uint64_t IntlvEn, IntlvSel;
462	uint32_t HoleOffset;
463	uint32_t InputAddr, Temp;
464
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));
474	}
475
476	/*
477	 * BKDG line to skip		Why
478	 *
479	 * F1Offset = ...		Register already read,
480	 * DramBase = Get_PCI()		and retrieved above.
481	 * DramEn = ...			Function only called for enabled nodes.
482	 */
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;
491
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));
498	}
499
500	if (HoleEn && SystemAddr > 0x00ffffff)
501		InputAddr = SystemAddr - HoleOffset;
502
503	InputAddr = SystemAddr - DramBase;
504
505	if (IntlvEn) {
506		if (IntlvSel == ((SystemAddr >> 4) & IntlvEn)) {
507			switch (IntlvEn) {
508			case 1:
509				Ilog = 1;
510				break;
511			case 3:
512				Ilog = 2;
513				break;
514			case 7:
515				Ilog = 3;
516				break;
517			default:
518				return (mcamd_set_errno(hdl,
519				    EMCAMD_TREEINVALID));
520			}
521			Temp = (InputAddr >> (4 + Ilog)) << 4;
522			InputAddr = (Temp | (SystemAddr & 0x0000000f));
523		} else {
524			/* not this node */
525			mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mc_bkdg_patounum: "
526			    "Node interleaving, MC node %d not selected\n",
527			    (int)mcnum);
528			return (mcamd_set_errno(hdl, EMCAMD_NOADDR));
529		}
530	}
531
532	if (!MC_REV_MATCH(rev, MC_F_REVS_FG))
533		InputAddr <<= 4;
534
535	for (cs = mcamd_cs_next(hdl, mc, NULL); cs != NULL;
536	    cs = mcamd_cs_next(hdl, mc, cs)) {
537		uint64_t csnum, CSEn;
538
539		if (!mcamd_get_cfgregs(hdl,
540		    cs, MCAMD_REG_CSBASE, &CSBase,
541		    cs, MCAMD_REG_CSMASK, &CSMask,
542		    NULL) ||
543		    !mcamd_get_numprops(hdl,
544		    cs, MCAMD_PROP_NUM, &csnum,
545		    cs, MCAMD_PROP_CSBE, &CSEn,
546		    NULL)) {
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));
550		}
551
552		/*
553		 * BKDG line to skip		Why
554		 *
555		 * F2Offset =			Register already read,
556		 * F2MaskOffset (rev F)		Register already read
557		 * CSBase =			Register already read
558		 * CSEn =			We only keep enabled cs.
559		 */
560		if (MC_REV_MATCH(rev, MC_F_REVS_FG)) {
561			CSBase &= 0x1ff83fe0;
562			/* CSMask = Get_PCI()		Retrieved above */
563			CSMask = (CSMask | 0x0007c01f) & 0x1fffffff;
564		} else {
565			CSBase &= 0xffe0fe00;
566			/* CSMask = Get_PCI()		Retrieved above */
567			CSMask = (CSMask | 0x001f01ff) & 0x3fffffff;
568		}
569
570		if (CSEn && (InputAddr & ~CSMask) == (CSBase & ~CSMask)) {
571			mcamd_node_t *sparecs;
572
573			mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mc_bkdg_patounum: "
574			    "match for chip select %d of MC %d\n", (int)csnum,
575			    (int)mcnum);
576
577			if ((sparecs = cs_sparedto(hdl, cs, mc)) != NULL)
578				cs = sparecs;
579
580			if ((which = mc_whichdimm(hdl, cs, pa, valid_lo,
581			    synd, syndtype)) < 0)
582				return (-1); /* errno is set for us */
583
584			/*
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.
589			 */
590			if (unum_fill(hdl, cs, which, InputAddr, unump, 0) < 0)
591				return (-1); /* errno is set for us */
592
593			return (0);
594		}
595	}
596
597	mcamd_dprintf(hdl, MCAMD_DBG_ERR, "mc_bkdg_patounum: in range "
598	    "for MC %d but no cs responds\n", (int)mcnum);
599
600	return (mcamd_set_errno(hdl, EMCAMD_NOADDR));
601}
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*/
610static int
611mc_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
692int
693mcamd_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