mcamd_patounum.c (7aec1d6e) mcamd_patounum.c (8a40a695)
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
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, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.

--- 16 unchanged lines hidden (view full) ---

32
33#include <sys/errno.h>
34#include <sys/types.h>
35#include <sys/mc.h>
36
37#include <mcamd_api.h>
38#include <mcamd_err.h>
39
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.

--- 16 unchanged lines hidden (view full) ---

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
40extern int mc_pa_to_offset(struct mcamd_hdl *, mcamd_node_t *, mcamd_node_t *,
41 mcamd_node_t *, uint64_t, uint64_t *);
42
39
43#define LO_DIMM 0x1
44#define UP_DIMM 0x2
40#define CSDIMM1 0x1
41#define CSDIMM2 0x2
45
46#define BITS(val, high, low) \
47 ((val) & (((2ULL << (high)) - 1) & ~((1ULL << (low)) - 1)))
48
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 */
49static int
50iaddr_gen(struct mcamd_hdl *hdl, mcamd_node_t *mc, uint64_t pa,
51 uint64_t *iaddrp)
52{
53 uint64_t orig = pa;
54static int
55iaddr_gen(struct mcamd_hdl *hdl, mcamd_node_t *mc, uint64_t pa,
56 uint64_t *iaddrp)
57{
58 uint64_t orig = pa;
54 uint64_t mcnum, base, lim, dramaddr, ilen, ilsel, top, dramhole;
59 uint64_t mcnum, base, lim, dramaddr, ilen, ilsel, top, holesz;
55
60
56 if (!mcamd_get_numprop(hdl, mc, MCAMD_PROP_NUM, &mcnum) ||
57 !mcamd_get_numprop(hdl, mc, MCAMD_PROP_BASE_ADDR, &base) ||
58 !mcamd_get_numprop(hdl, mc, MCAMD_PROP_LIM_ADDR, &lim) ||
59 !mcamd_get_numprop(hdl, mc, MCAMD_PROP_DRAM_ILEN, &ilen) ||
60 !mcamd_get_numprop(hdl, mc, MCAMD_PROP_DRAM_ILSEL, &ilsel) ||
61 !mcamd_get_numprop(hdl, mc, MCAMD_PROP_DRAM_HOLE, &dramhole)) {
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)) {
62 mcamd_dprintf(hdl, MCAMD_DBG_ERR, "iaddr_gen: failed to "
63 "lookup required properties");
64 return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
65 }
66
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
67 if (pa < base || pa > lim) {
68 mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "iaddr_gen: PA 0x%llx not "
69 "in range [0x%llx, 0x%llx] of MC %d\n", pa, base, lim,
70 (int)mcnum);
71 return (mcamd_set_errno(hdl, EMCAMD_NOADDR));
72 }
73
74 /*
75 * Rev E and later added the DRAM Hole Address Register for
76 * memory hoisting. In earlier revisions memory hoisting is
77 * achieved by following some algorithm to modify the CS bases etc,
78 * and this pa to unum algorithm will simply see those modified
79 * values. But if the Hole Address Register is being used then
80 * we need to reduce any address at or above 4GB by the size of
81 * the hole.
82 */
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 */
83 if (dramhole & MC_DC_HOLE_VALID && pa >= 0x100000000) {
84 uint64_t holesize = (dramhole & MC_DC_HOLE_OFFSET_MASK) <<
85 MC_DC_HOLE_OFFSET_LSHIFT;
86 pa -= holesize;
98 if (holesz != 0 && pa >= 0x100000000) {
99 pa -= holesz;
87 mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "iaddr_gen: dram hole "
88 "valid; pa decremented from 0x%llx to 0x%llx for "
100 mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "iaddr_gen: dram hole "
101 "valid; pa decremented from 0x%llx to 0x%llx for "
89 "a dramhole size of 0x%llx\n", orig, pa, holesize);
102 "a dramhole size of 0x%llx\n", orig, pa, holesz);
90 }
91
92 dramaddr = BITS(pa, 39, 0) - BITS(base, 39, 24);
93
94 if (ilen != 0) {
95 int pailsel;
96
97 if (ilen != 1 && ilen != 3 && ilen != 7) {

--- 24 unchanged lines hidden (view full) ---

122
123 mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "iaddr_gen: PA 0x%llx in range "
124 "[0x%llx, 0x%llx] of MC %d; normalized address for cs compare "
125 "is 0x%llx\n", pa, base, lim, (int)mcnum, *iaddrp);
126
127 return (0);
128}
129
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) {

--- 24 unchanged lines hidden (view full) ---

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 */
130static int
131cs_match(struct mcamd_hdl *hdl, uint64_t iaddr, mcamd_node_t *cs)
132{
150static int
151cs_match(struct mcamd_hdl *hdl, uint64_t iaddr, mcamd_node_t *cs)
152{
133 uint64_t csnum, csbase, csmask;
134 int match;
153 uint64_t csnum, csbase, csmask, csbe;
154 int match = 0;
135
155
136 if (!mcamd_get_numprop(hdl, cs, MCAMD_PROP_NUM, &csnum) ||
137 !mcamd_get_numprop(hdl, cs, MCAMD_PROP_BASE_ADDR, &csbase) ||
138 !mcamd_get_numprop(hdl, cs, MCAMD_PROP_MASK, &csmask)) {
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)) {
139 mcamd_dprintf(hdl, MCAMD_DBG_ERR, "cs_match: failed to lookup "
140 "required properties\n");
141 return (0);
142 }
143
162 mcamd_dprintf(hdl, MCAMD_DBG_ERR, "cs_match: failed to lookup "
163 "required properties\n");
164 return (0);
165 }
166
144 match = ((iaddr & ~csmask) == (csbase & ~csmask));
167 if (csbe) {
168 match = ((iaddr & ~csmask) == (csbase & ~csmask));
145
169
146 mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "cs_match: iaddr 0x%llx does "
147 "%smatch CS %d (base 0x%llx, mask 0x%llx)\n", iaddr,
148 match ? "" : "not ", (int)csnum, csbase, csmask);
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 }
149
150 return (match);
151}
152
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 */
185static mcamd_node_t *
186cs_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 */
153static int
154unum_fill(struct mcamd_hdl *hdl, mcamd_node_t *cs, int which,
234static int
235unum_fill(struct mcamd_hdl *hdl, mcamd_node_t *cs, int which,
155 uint64_t iaddr, struct mc_unum *unump, int incloff)
236 uint64_t iaddr, mc_unum_t *unump, int incloff)
156{
237{
238 uint64_t chipnum, csnum, dimm1, dimm2, ranknum;
157 mcamd_node_t *mc, *dimm;
239 mcamd_node_t *mc, *dimm;
158 uint64_t chipnum, csnum, lonum, upnum;
159 int i;
160 int offsetdimm;
240 int offsetdimm;
241 int i;
161
162 if ((mc = mcamd_cs_mc(hdl, cs)) == NULL ||
242
243 if ((mc = mcamd_cs_mc(hdl, cs)) == NULL ||
163 !mcamd_get_numprop(hdl, mc, MCAMD_PROP_NUM, &chipnum) ||
164 !mcamd_get_numprop(hdl, cs, MCAMD_PROP_NUM, &csnum)) {
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)) {
165 mcamd_dprintf(hdl, MCAMD_DBG_ERR, "unum_fill: failed to "
166 "lookup required properties\n");
167 return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
168 }
169
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
170 if ((which & LO_DIMM) &&
171 !mcamd_get_numprop(hdl, cs, MCAMD_PROP_LODIMM, &lonum) ||
172 (which & UP_DIMM) &&
173 !mcamd_get_numprop(hdl, cs, MCAMD_PROP_UPDIMM, &upnum)) {
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)) {
174 mcamd_dprintf(hdl, MCAMD_DBG_ERR, "unum_fill: failed to "
258 mcamd_dprintf(hdl, MCAMD_DBG_ERR, "unum_fill: failed to "
175 "lookup lodimm/hidimm properties\n");
259 "lookup dimm1/dimm2 properties\n");
176 return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
177 }
178
179 unump->unum_board = 0;
180 unump->unum_chip = chipnum;
181 unump->unum_mc = 0;
182 unump->unum_cs = csnum;
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;
183
184 for (i = 0; i < MC_UNUM_NDIMM; i++) {
268
269 for (i = 0; i < MC_UNUM_NDIMM; i++) {
185 unump->unum_dimms[i] = -1;
270 unump->unum_dimms[i] = MC_INVALNUM;
186 }
187 switch (which) {
271 }
272 switch (which) {
188 case LO_DIMM:
189 unump->unum_dimms[0] = lonum;
190 offsetdimm = lonum;
273 case CSDIMM1:
274 unump->unum_dimms[0] = dimm1;
275 offsetdimm = dimm1;
191 break;
276 break;
192 case UP_DIMM:
193 unump->unum_dimms[0] = upnum;
194 offsetdimm = upnum;
277 case CSDIMM2:
278 unump->unum_dimms[0] = dimm2;
279 offsetdimm = dimm2;
195 break;
280 break;
196 case LO_DIMM | UP_DIMM:
197 unump->unum_dimms[0] = lonum;
198 unump->unum_dimms[1] = upnum;
199 offsetdimm = lonum;
281 case CSDIMM1 | CSDIMM2:
282 unump->unum_dimms[0] = dimm1;
283 unump->unum_dimms[1] = dimm2;
284 offsetdimm = dimm1;
200 break;
201 }
202
203 if (!incloff) {
204 unump->unum_offset = MCAMD_RC_INVALID_OFFSET;
205 return (0);
206 }
207
208 /*
209 * We wish to calculate a dimm offset. In the paired case we will
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
210 * lookup the lodimm (see offsetdimm above).
295 * lookup dimm1 (see offsetdimm above).
211 */
212 for (dimm = mcamd_dimm_next(hdl, mc, NULL); dimm != NULL;
213 dimm = mcamd_dimm_next(hdl, mc, dimm)) {
214 uint64_t dnum;
215 if (!mcamd_get_numprop(hdl, dimm, MCAMD_PROP_NUM, &dnum)) {
216 mcamd_dprintf(hdl, MCAMD_DBG_ERR, "unum_fill: failed "
217 "to lookup dimm number property\n");
218 continue;

--- 9 unchanged lines hidden (view full) ---

228 unump->unum_offset = MCAMD_RC_INVALID_OFFSET;
229 return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
230 }
231
232 /*
233 * mc_pa_to_offset sets the offset to an invalid value if
234 * it hits an error.
235 */
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;

--- 9 unchanged lines hidden (view full) ---

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 */
236 (void) mc_pa_to_offset(hdl, mc, cs, dimm, iaddr, &unump->unum_offset);
321 (void) mc_pa_to_offset(hdl, mc, cs, iaddr, &unump->unum_offset);
237
238 return (0);
239}
240
241/*
322
323 return (0);
324}
325
326/*
242 * We have translated a system address to a (node, chip-select). That
243 * identifies one (in 64-bit MC mode) or two (in 128-bit MC mode DIMMs,
244 * either a lodimm or a lodimm/updimm pair. For all cases except an
245 * uncorrectable ChipKill error we can interpret the address alignment and
246 * syndrome to deduce whether we are on the lodimm or updimm.
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.
247 */
248static int
342 */
343static int
249mc_whichdimm(struct mcamd_hdl *hdl, mcamd_node_t *mc, uint64_t pa,
344mc_whichdimm(struct mcamd_hdl *hdl, mcamd_node_t *cs, uint64_t pa,
250 uint32_t synd, int syndtype)
251{
345 uint32_t synd, int syndtype)
346{
252 uint64_t accwidth;
253 uint_t sym, pat;
254 int lobit, hibit, data, check;
347 int lobit, hibit, data, check;
348 uint64_t dimm1, dimm2;
349 uint_t sym, pat;
350 int ndimm;
255
351
256 if (!mcamd_get_numprop(hdl, mc, MCAMD_PROP_ACCESS_WIDTH, &accwidth) ||
257 (accwidth != 64 && accwidth != 128)) {
258 mcamd_dprintf(hdl, MCAMD_DBG_ERR, "mc_whichdimm: failed "
259 "to lookup required properties\n");
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");
260 return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
261 }
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 }
262
372
263 /*
264 * In 64 bit mode only LO dimms are occupied.
265 */
266 if (accwidth == 64) {
267 mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mc_whichdimm: 64-bit mode "
268 "therefore LO_DIMM\n");
269 return (LO_DIMM);
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);
270 }
271
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 */
272 if (syndtype == AMD_SYNDTYPE_ECC) {
385 if (syndtype == AMD_SYNDTYPE_ECC) {
273 /*
274 * 64/8 ECC is checked separately for the upper and lower
275 * halves, so even an uncorrectable error is contained within
276 * one of the two halves. The error address is accurate to
277 * 8 bytes, so bit 4 distinguises upper from lower.
278 */
279 mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mc_whichdimm: 64/8 ECC "
280 "and PA 0x%llx is in %s half\n", pa,
281 pa & 8 ? "lower" : "upper");
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");
282 return (pa & 8 ? UP_DIMM : LO_DIMM);
389 return (pa & 8 ? CSDIMM2 : CSDIMM1);
283 }
284
285 /*
390 }
391
392 /*
286 * ChipKill ECC (necessarily in 128-bit mode.
393 * ChipKill ECC
287 */
288 if (mcamd_cksynd_decode(hdl, synd, &sym, &pat)) {
289 /*
290 * A correctable ChipKill syndrome and we can tell
291 * which half the error was in from the symbol number.
292 */
293 if (mcamd_cksym_decode(hdl, sym, &lobit, &hibit, &data,
294 &check) == 0)
295 return (mcamd_set_errno(hdl, EMCAMD_SYNDINVALID));
296
297 if (data && hibit <= 63 || check && hibit <= 7) {
298 mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mc_whichdimm: "
299 "ChipKill symbol %d (%s %d..%d), so LODIMM\n", sym,
300 data ? "data" : "check", lobit, hibit);
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);
301 return (LO_DIMM);
408 return (CSDIMM1);
302 } else {
303 mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mc_whichdimm: "
304 "ChipKill symbol %d (%s %d..%d), so UPDIMM\n", sym,
305 data ? "data" : "check", lobit, hibit);
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);
306 return (UP_DIMM);
413 return (CSDIMM2);
307 }
308 } else {
309 /*
310 * An uncorrectable error while in ChipKill ECC mode - can't
311 * tell which dimm or dimms the errors lie within.
312 */
313 mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mc_whichhdimm: "
314 "uncorrectable ChipKill, could be either LODIMM "
315 "or UPDIMM\n");
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");
316 return (LO_DIMM | UP_DIMM);
423 return (CSDIMM1 | CSDIMM2);
317 }
318}
319
320/*
424 }
425}
426
427/*
321 * Brute-force BKDG pa to cs translation. The following is from BKDG 3.29
322 * so is for revisions prior to F. It is coded to look as much like the
428 * Brute-force BKDG pa to cs translation, coded to look as much like the
323 * BKDG code as possible.
324 */
325static int
326mc_bkdg_patounum(struct mcamd_hdl *hdl, mcamd_node_t *mc, uint64_t pa,
429 * BKDG code as possible.
430 */
431static int
432mc_bkdg_patounum(struct mcamd_hdl *hdl, mcamd_node_t *mc, uint64_t pa,
327 uint32_t synd, int syndtype, struct mc_unum *unump)
433 uint32_t synd, int syndtype, mc_unum_t *unump)
328{
329 int which;
434{
435 int which;
330 uint64_t mcnum;
436 uint64_t mcnum, rev;
331 mcamd_node_t *cs;
332 /*
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 /*
333 * Variables as per BKDG
334 */
335 int Ilog;
336 uint32_t SystemAddr = (uint32_t)(pa >> 8);
337 uint64_t IntlvEn, IntlvSel;
445 * Variables as per BKDG
446 */
447 int Ilog;
448 uint32_t SystemAddr = (uint32_t)(pa >> 8);
449 uint64_t IntlvEn, IntlvSel;
338 uint32_t DramBase, DramLimit; /* assume DramEn */
339 uint32_t HoleOffset, HoleEn;
340 uint32_t CSBase, CSMask; /* assuume CSBE */
450 uint32_t HoleOffset;
341 uint32_t InputAddr, Temp;
342
451 uint32_t InputAddr, Temp;
452
343 /*
344 * Additional variables which we need since we will reading
345 * MC properties instead of PCI config space, and the MC properties
346 * are stored in a cooked state.
347 */
348 uint64_t prop_drambase, prop_dramlimit, prop_dramhole;
349 uint64_t prop_intlven, prop_intlvsel;
350 uint64_t prop_csbase, prop_csmask;
351
352 if (!mcamd_get_numprop(hdl, mc, MCAMD_PROP_NUM, &mcnum) ||
353 !mcamd_get_numprop(hdl, mc, MCAMD_PROP_BASE_ADDR, &prop_drambase) ||
354 !mcamd_get_numprop(hdl, mc, MCAMD_PROP_LIM_ADDR, &prop_dramlimit) ||
355 !mcamd_get_numprop(hdl, mc, MCAMD_PROP_DRAM_HOLE, &prop_dramhole) ||
356 !mcamd_get_numprop(hdl, mc, MCAMD_PROP_DRAM_ILEN, &prop_intlven) ||
357 !mcamd_get_numprop(hdl, mc, MCAMD_PROP_DRAM_ILSEL,
358 &prop_intlvsel)) {
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)) {
359 mcamd_dprintf(hdl, MCAMD_DBG_ERR, "mc_bkdg_patounm: failed "
459 mcamd_dprintf(hdl, MCAMD_DBG_ERR, "mc_bkdg_patounm: failed "
360 "to lookup required properties\n");
460 "to lookup required properties and registers\n");
361 return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
362 }
363
364 /*
461 return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
462 }
463
464 /*
365 * Brute force deconstruction of the MC properties. If we decide to
366 * keep this then we need some of the mcamd.g defines available to us.
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.
367 */
470 */
368 DramBase = ((prop_drambase >> 8) & 0xffff0000) | (prop_intlven << 8);
369 IntlvEn = (DramBase & 0x00000700) >> 8;
370 DramBase &= 0xffff0000;
471 IntlvEn = (DramBase & 0x00000700) >> 8;
472 DramBase &= 0xffff0000;
371 DramLimit = ((prop_dramlimit >> 8) & 0xffff0000) | (prop_intlvsel << 8);
473 /* DramLimit = Get_PCI() Retrieved above */
372 IntlvSel = (DramLimit & 0x00000700) >> 8;
373 DramLimit |= 0x0000ffff;
474 IntlvSel = (DramLimit & 0x00000700) >> 8;
475 DramLimit |= 0x0000ffff;
374 HoleEn = prop_dramhole; /* uncooked */
476 /* HoleEn = ... Retrieved above */
375 HoleOffset = (HoleEn & 0x0000ff00) << 8;
376 HoleEn &= 0x00000001;
377
378 if (!(DramBase <= SystemAddr && SystemAddr <= DramLimit)) {
379 mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mc_bkdg_patounum: "
380 "SystemAddr 0x%x derived from PA 0x%llx is not in the "
381 "address range [0x%x, 0x%x] of MC %d\n",
382 SystemAddr, pa, DramBase, DramLimit, (int)mcnum);
383 return (mcamd_set_errno(hdl, EMCAMD_NOADDR));
384 }
385
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
386 if (IntlvEn) {
387 if (IntlvSel == ((SystemAddr >> 4) & IntlvEn)) {
388 switch (IntlvEn) {
389 case 1:
390 Ilog = 1;
391 break;
392 case 3:
393 Ilog = 2;
394 break;
395 case 7:
396 Ilog = 3;
397 break;
398 default:
399 return (mcamd_set_errno(hdl,
400 EMCAMD_TREEINVALID));
401 }
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 }
402 Temp = (SystemAddr >> (4 + Ilog)) << 4;
403 InputAddr = (Temp | (SystemAddr & 0x0000000f)) << 4;
509 Temp = (InputAddr >> (4 + Ilog)) << 4;
510 InputAddr = (Temp | (SystemAddr & 0x0000000f));
404 } else {
405 /* not this node */
406 mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mc_bkdg_patounum: "
407 "Node interleaving, MC node %d not selected\n",
408 (int)mcnum);
409 return (mcamd_set_errno(hdl, EMCAMD_NOADDR));
410 }
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 }
411 } else {
412 /* No interleave */
413 InputAddr = (SystemAddr - DramBase) << 4;
414 }
415
518 }
519
416 if (HoleEn && SystemAddr > 0x00ffffff)
417 InputAddr -= HoleOffset;
520 if (!MC_REV_MATCH(rev, MC_REVS_FG))
521 InputAddr <<= 4;
418
419 for (cs = mcamd_cs_next(hdl, mc, NULL); cs != NULL;
420 cs = mcamd_cs_next(hdl, mc, cs)) {
522
523 for (cs = mcamd_cs_next(hdl, mc, NULL); cs != NULL;
524 cs = mcamd_cs_next(hdl, mc, cs)) {
421 uint64_t csnum;
525 uint64_t csnum, CSEn;
422
526
423 if (!mcamd_get_numprop(hdl, cs, MCAMD_PROP_BASE_ADDR,
424 &prop_csbase) ||
425 !mcamd_get_numprop(hdl, cs, MCAMD_PROP_MASK,
426 &prop_csmask) ||
427 !mcamd_get_numprop(hdl, cs, MCAMD_PROP_NUM, &csnum)) {
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)) {
428 mcamd_dprintf(hdl, MCAMD_DBG_ERR, "mc_bkdg_patounm: "
535 mcamd_dprintf(hdl, MCAMD_DBG_ERR, "mc_bkdg_patounm: "
429 "failed to read cs properties\n");
536 "failed to read cs registers\n");
430 return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
431 }
432
537 return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
538 }
539
433 CSBase = ((prop_csbase >> 4) & 0xffe00000) |
434 ((prop_csbase >> 4) & 0x0000fe00);
435 CSBase &= 0xffe0fe00;
436 CSMask = ((prop_csmask >> 4) & 0x3fe00000) |
437 ((prop_csmask >> 4) & 0x0000fe00);
438 CSMask = (CSMask | 0x001f01ff) & 0x3fffffff;
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 }
439
557
440 if (((InputAddr & ~CSMask) == (CSBase & ~CSMask))) {
558 if (CSEn && (InputAddr & ~CSMask) == (CSBase & ~CSMask)) {
559 mcamd_node_t *sparecs;
560
441 mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mc_bkdg_patounum: "
442 "match for chip select %d of MC %d\n", (int)csnum,
443 (int)mcnum);
444
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
445 if ((which = mc_whichdimm(hdl, mc, pa, synd,
565 if ((sparecs = cs_sparedto(hdl, cs, mc)) != NULL)
566 cs = sparecs;
567
568 if ((which = mc_whichdimm(hdl, cs, pa, synd,
446 syndtype)) < 0)
447 return (-1); /* errno is set for us */
448
449 /*
450 * The BKDG algorithm drops low-order bits that
451 * are unimportant in deriving chip-select but are
452 * included in row/col/bank mapping, so do not
453 * perform offset calculation in this case.

--- 6 unchanged lines hidden (view full) ---

460 }
461
462 mcamd_dprintf(hdl, MCAMD_DBG_ERR, "mc_bkdg_patounum: in range "
463 "for MC %d but no cs responds\n", (int)mcnum);
464
465 return (mcamd_set_errno(hdl, EMCAMD_NOADDR));
466}
467
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.

--- 6 unchanged lines hidden (view full) ---

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
468/*ARGSUSED*/
469static int
470mc_patounum(struct mcamd_hdl *hdl, mcamd_node_t *mc, uint64_t pa,
597/*ARGSUSED*/
598static int
599mc_patounum(struct mcamd_hdl *hdl, mcamd_node_t *mc, uint64_t pa,
471 uint32_t synd, int syndtype, struct mc_unum *unump)
600 uint32_t synd, int syndtype, mc_unum_t *unump)
472{
473 uint64_t iaddr;
601{
602 uint64_t iaddr;
474 mcamd_node_t *cs;
603 mcamd_node_t *cs, *sparecs;
475 int which;
476#ifdef DEBUG
604 int which;
605#ifdef DEBUG
477 struct mc_unum bkdg_unum;
606 mc_unum_t bkdg_unum;
478 int bkdgres;
479
480 /*
481 * We perform the translation twice, once using the brute-force
482 * approach of the BKDG and again using a more elegant but more
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
483 * difficult to review against the BKDG approach. Note that both
484 * approaches need to change for rev F since it increases max CS
485 * size and so iaddr calculation etc changes.
612 * difficult to review against the BKDG approach.
486 */
487 mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "BKDG brute-force method begins\n");
488 bkdgres = mc_bkdg_patounum(hdl, mc, pa, synd, syndtype, &bkdg_unum);
489 mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "BKDG brute-force method ends\n");
490#endif
491
492 if (iaddr_gen(hdl, mc, pa, &iaddr) < 0)
493 return (-1); /* errno is set for us */
494
495 for (cs = mcamd_cs_next(hdl, mc, NULL); cs != NULL;
496 cs = mcamd_cs_next(hdl, mc, cs)) {
497 if (cs_match(hdl, iaddr, cs))
498 break;
499 }
500
501 if (cs == NULL)
502 return (mcamd_set_errno(hdl, EMCAMD_NOADDR));
503
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
504 if ((which = mc_whichdimm(hdl, mc, pa, synd, syndtype)) < 0)
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)
505 return (-1); /* errno is set for us */
506
507 if (unum_fill(hdl, cs, which, iaddr, unump, 1) < 0)
508 return (-1); /* errno is set for us */
509
510#ifdef DEBUG
511 mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "bkdgres=%d res=0\n", bkdgres);
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);
512#ifndef _KERNEL
513 /* offset is not checked - see note in BKDG algorithm */
650 /* offset is not checked - see note in BKDG algorithm */
514 assert(bkdgres == 0 && unump->unum_board == bkdg_unum.unum_board &&
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 &&
515 unump->unum_chip == bkdg_unum.unum_chip &&
516 unump->unum_mc == bkdg_unum.unum_mc &&
517 unump->unum_cs == bkdg_unum.unum_cs &&
518 unump->unum_dimms[0] == bkdg_unum.unum_dimms[0] &&
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] &&
519 unump->unum_dimms[1] == bkdg_unum.unum_dimms[1]);
520#endif /* !_KERNEL */
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 }
521#endif /* DEBUG */
522
523 mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "Result: chip %d mc %d cs %d "
524 "offset 0x%llx\n", unump->unum_chip, unump->unum_mc,
525 unump->unum_cs, unump->unum_offset);
526
527 return (0);
528}
529
530int
531mcamd_patounum(struct mcamd_hdl *hdl, mcamd_node_t *root, uint64_t pa,
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
677int
678mcamd_patounum(struct mcamd_hdl *hdl, mcamd_node_t *root, uint64_t pa,
532 uint32_t synd, int syndtype, struct mc_unum *unump)
679 uint32_t synd, int syndtype, mc_unum_t *unump)
533{
534 mcamd_node_t *mc;
535
536 mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mcamd_patounum: pa=0x%llx, "
537 "synd=0x%x, syndtype=%d\n", pa, synd, syndtype);
538
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 */
539 if (!mcamd_synd_validate(hdl, synd, syndtype))
540 return (mcamd_set_errno(hdl, EMCAMD_SYNDINVALID));
541
542 for (mc = mcamd_mc_next(hdl, root, NULL); mc != NULL;
543 mc = mcamd_mc_next(hdl, root, mc)) {
544 if (mc_patounum(hdl, mc, pa, synd, syndtype, unump) == 0)
545 return (0);
546
547 if (mcamd_errno(hdl) != EMCAMD_NOADDR)
548 break;
549 }
550
551 return (-1); /* errno is set for us */
552}
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}