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