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 */ 56 static int 57 iaddr_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 */ 152 static int 153 cs_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 */ 187 static mcamd_node_t * 188 cs_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 */ 236 static int 237 unum_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 */ 346 static int 347 mc_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 */ 442 static int 443 mc_bkdg_patounum(struct mcamd_hdl *hdl, mcamd_node_t *mc, uint64_t pa, 444 uint8_t valid_lo, uint32_t synd, int syndtype, 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*/ 610 static int 611 mc_patounum(struct mcamd_hdl *hdl, mcamd_node_t *mc, uint64_t pa, 612 uint8_t valid_lo, uint32_t synd, int syndtype, mc_unum_t *unump) 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 692 int 693 mcamd_patounum(struct mcamd_hdl *hdl, mcamd_node_t *root, uint64_t pa, 694 uint8_t valid_hi, uint8_t valid_lo, uint32_t synd, int syndtype, 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