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. 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 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 * 22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <mcamd_api.h> 29 #include <mcamd_err.h> 30 #include <mcamd_rowcol_impl.h> 31 32 /* 33 * Convenience structures to stash MC and CS properties in. Some of these 34 * are read directly, while others are then calculated. 35 */ 36 struct rcp_mc { 37 uint64_t num; /* corresponding chip number */ 38 uint64_t rev; /* revision */ 39 uint64_t width; /* access width */ 40 uint64_t base; /* MC base address */ 41 uint64_t lim; /* MC limit address */ 42 uint64_t csbnkmap; /* chip-select bank map */ 43 uint64_t intlven; /* Node-interleave mask */ 44 uint64_t intlvsel; /* Node-interleave selection for this node */ 45 uint64_t csintlvfctr; /* chip-select interleave factor on this node */ 46 int bnkswzl; /* bank-swizzle mode - derived */ 47 }; 48 49 struct rcp_cs { 50 uint64_t num; /* chip-select number */ 51 uint64_t base; /* chip-select base address */ 52 uint64_t mask; /* chip-select mask */ 53 }; 54 55 static int 56 getmcprops(struct mcamd_hdl *hdl, mcamd_node_t *mc, const char *caller, 57 struct rcp_mc *pp) 58 { 59 if (!mcamd_get_numprop(hdl, mc, MCAMD_PROP_NUM, &pp->num) || 60 !mcamd_get_numprop(hdl, mc, MCAMD_PROP_REV, &pp->rev) || 61 !mcamd_get_numprop(hdl, mc, MCAMD_PROP_ACCESS_WIDTH, &pp->width) || 62 !mcamd_get_numprop(hdl, mc, MCAMD_PROP_BASE_ADDR, &pp->base) || 63 !mcamd_get_numprop(hdl, mc, MCAMD_PROP_LIM_ADDR, &pp->lim) || 64 !mcamd_get_numprop(hdl, mc, MCAMD_PROP_CSBANKMAP, &pp->csbnkmap) || 65 !mcamd_get_numprop(hdl, mc, MCAMD_PROP_DRAM_ILEN, &pp->intlven) || 66 !mcamd_get_numprop(hdl, mc, MCAMD_PROP_DRAM_ILSEL, &pp->intlvsel) || 67 !mcamd_get_numprop(hdl, mc, MCAMD_PROP_CSBANK_INTLV, 68 &pp->csintlvfctr)) { 69 mcamd_dprintf(hdl, MCAMD_DBG_ERR, "%s: failed to read mc " 70 "props for mc 0x%p\n", caller, mc); 71 return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID)); 72 } 73 74 pp->bnkswzl = ((pp->csbnkmap & MC_DC_BAM_CSBANK_SWIZZLE) != 0); 75 76 return (0); 77 } 78 79 static int 80 getcsprops(struct mcamd_hdl *hdl, mcamd_node_t *cs, const char *caller, 81 struct rcp_cs *csp) 82 { 83 if (!mcamd_get_numprop(hdl, cs, MCAMD_PROP_NUM, &csp->num) || 84 !mcamd_get_numprop(hdl, cs, MCAMD_PROP_BASE_ADDR, &csp->base) || 85 !mcamd_get_numprop(hdl, cs, MCAMD_PROP_MASK, &csp->mask)) { 86 mcamd_dprintf(hdl, MCAMD_DBG_ERR, "%s: failed to read cs " 87 "props for cs 0x%p\n", caller, cs); 88 return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID)); 89 } 90 91 return (0); 92 } 93 94 static int 95 gettbls(struct mcamd_hdl *hdl, uint_t csmode, struct rcp_mc *mcpp, 96 const struct bankaddr_mode **bamp, const struct csrcb_map **rcbmp, 97 struct csintlv_desc *csid, const char *caller) 98 { 99 if (bamp && (*bamp = rct_bankaddr_mode(mcpp->rev, csmode)) == NULL) { 100 mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "%s: no bank address mode " 101 "table for MC rev %d csmode %d\n", caller, 102 (int)mcpp->rev, csmode); 103 return (mcamd_set_errno(hdl, EMCAMD_NOTSUP)); 104 } 105 106 if (rcbmp && (*rcbmp = rct_rcbmap(mcpp->rev, mcpp->width, 107 csmode)) == NULL) { 108 mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "%s: no dram address map " 109 "table for MC rev %d csmode %d\n", caller, 110 (int)mcpp->rev, csmode); 111 return (mcamd_set_errno(hdl, EMCAMD_NOTSUP)); 112 } 113 114 if (csid) { 115 if (mcpp->csintlvfctr != 0) { 116 rct_csintlv_bits(mcpp->rev, mcpp->width, csmode, 117 mcpp->csintlvfctr, csid); 118 if (csid->csi_factor == 0) { 119 mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "%s: " 120 "could not work out cs interleave " 121 "paramters for MC rev %d, width %d, " 122 "csmode %d, factor %d\n", caller, 123 (int)mcpp->rev, (int)mcpp->width, csmode, 124 (int)mcpp->csintlvfctr); 125 return (mcamd_set_errno(hdl, EMCAMD_NOTSUP)); 126 } 127 } else { 128 csid->csi_factor = 0; 129 } 130 } 131 132 return (0); 133 } 134 135 static uint64_t 136 iaddr_add(struct mcamd_hdl *hdl, uint64_t in, uint64_t add, const char *what) 137 { 138 uint64_t new = in | add; 139 140 mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "%s: 0x%llx | 0x%llx --> 0x%llx", 141 what, in, add, new); 142 143 return (add); 144 } 145 146 /* 147 * Where the number of row/col address bits is ambiguous (affects CG and 148 * earlier only) we will assign the "floating" bit to row address. If 149 * we adopt the same convention in address reconstruction then all should work. 150 */ 151 static uint32_t 152 iaddr_to_row(struct mcamd_hdl *hdl, const struct bankaddr_mode *bamp, 153 const struct csrcb_map *rcbm, struct csintlv_desc *csid, uint64_t iaddr) 154 { 155 uint32_t addr = 0; 156 int abitno, ibitno; 157 int nbits = bamp->bam_nrows; 158 int swapped = 0; 159 160 for (abitno = 0; abitno < nbits; abitno++) { 161 ibitno = rcbm->csrcb_rowbits[abitno]; 162 if (MC_RC_CSI_SWAPPED_BIT(csid, ibitno)) { 163 ibitno = MC_RC_CSI_BITSWAP(csid, ibitno); 164 swapped++; 165 } 166 if (iaddr & (1 << ibitno)) 167 addr |= (1 << abitno); 168 } 169 170 mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "iaddr_to_row: iaddr 0x%llx --> " 171 "row 0x%x (%d bits swapped for cs intlv)\n", iaddr, addr, swapped); 172 173 return (addr); 174 } 175 176 /*ARGSUSED*/ 177 static uint64_t 178 row_to_iaddr(struct mcamd_hdl *hdl, const struct bankaddr_mode *bamp, 179 const struct csrcb_map *rcbm, struct csintlv_desc *csid, uint32_t rowaddr) 180 { 181 uint64_t iaddr = 0; 182 int abitno, ibitno; 183 int nbits = bamp->bam_nrows; 184 185 for (abitno = 0; abitno < nbits; abitno++) { 186 if (BIT(rowaddr, abitno) == 0) 187 continue; 188 ibitno = rcbm->csrcb_rowbits[abitno]; 189 if (MC_RC_CSI_SWAPPED_BIT(csid, ibitno)) { 190 ibitno = MC_RC_CSI_BITSWAP(csid, ibitno); 191 } 192 SETBIT(iaddr, ibitno); 193 } 194 195 return (iaddr); 196 } 197 198 199 static uint32_t 200 iaddr_to_col(struct mcamd_hdl *hdl, const struct bankaddr_mode *bamp, 201 const struct csrcb_map *rcbm, uint64_t iaddr) 202 { 203 uint32_t addr = 0; 204 int abitno, ibitno, bias = 0; 205 int nbits = bamp->bam_ncols; 206 207 /* 208 * Knock off a column bit if the numbers are ambiguous 209 */ 210 if (bamp->bam_ambig) 211 nbits--; 212 213 for (abitno = 0; abitno < nbits; abitno++) { 214 if (abitno == MC_PC_COLADDRBIT) 215 bias = 1; 216 217 ibitno = rcbm->csrcb_colbits[abitno + bias]; 218 219 if (iaddr & (1 << ibitno)) 220 SETBIT(addr, abitno); 221 } 222 223 mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "iaddr_to_col: iaddr 0x%llx --> " 224 "col 0x%x\n", iaddr, addr); 225 226 return (addr); 227 } 228 229 /*ARGSUSED*/ 230 static uint64_t 231 col_to_iaddr(struct mcamd_hdl *hdl, const struct bankaddr_mode *bamp, 232 const struct csrcb_map *rcbm, uint32_t coladdr) 233 { 234 uint64_t iaddr = 0; 235 int abitno, ibitno, bias = 0; 236 int nbits = bamp->bam_ncols; 237 238 /* 239 * Knock off a column bit if the numbers are ambiguous 240 */ 241 if (bamp->bam_ambig) 242 nbits--; 243 244 for (abitno = 0; abitno < nbits; abitno++) { 245 if (BIT(coladdr, abitno) == 0) 246 continue; 247 248 if (abitno == MC_PC_COLADDRBIT) 249 bias = 1; 250 251 ibitno = rcbm->csrcb_colbits[abitno + bias]; 252 SETBIT(iaddr, ibitno); 253 } 254 255 return (iaddr); 256 } 257 258 /* 259 * Extract bank bit arguments and xor them together. Tables for 260 * non bank-swizzling should have all but the first argument zero. 261 */ 262 static uint32_t 263 iaddr_to_bank(struct mcamd_hdl *hdl, const struct csrcb_map *rcbm, 264 int bnkswzl, uint64_t iaddr) 265 { 266 uint32_t addr = 0; 267 int abitno, ibitno, i; 268 int bnkargs = bnkswzl ? MC_RC_BANKARGS : 1; 269 270 for (abitno = 0; abitno < MC_RC_BANKBITS; abitno++) { 271 uint32_t val = 0; 272 for (i = 0; i < bnkargs; i++) { 273 ibitno = rcbm->csrcb_bankargs[abitno][i]; 274 val ^= ((iaddr >> ibitno) & 0x1); 275 } 276 addr |= (val << abitno); 277 } 278 279 mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "iaddr_to_bank: iaddr 0x%llx --> " 280 "bank 0x%x\n", iaddr, addr); 281 282 return (addr); 283 } 284 285 /* 286 * bank_to_iaddr requires the iaddr reconstructed thus far with at least the 287 * row bits repopulated. That's because in bank swizzle mode 288 * the bank bits are the result of xor'ing three original iaddr bits 289 * together - two of which come from the row address and the third we 290 * can reconstruct here. Note that a zero bankaddr bit *can* result 291 * in a nonzero iaddr bit (unlike in row and col reconstruction). 292 */ 293 /*ARGSUSED*/ 294 static uint64_t 295 bank_to_iaddr(struct mcamd_hdl *hdl, const struct csrcb_map *rcbm, 296 int bnkswzl, uint64_t partiaddr, uint32_t bankaddr) 297 { 298 uint64_t iaddr = 0; 299 int abitno, pibitno, i; 300 301 for (abitno = 0; abitno < MC_RC_BANKBITS; abitno++) { 302 uint32_t val = BITVAL(bankaddr, abitno); 303 if (bnkswzl) { 304 for (i = 1; i < MC_RC_BANKARGS; i++) { 305 pibitno = rcbm->csrcb_bankargs[abitno][i]; 306 val ^= BITVAL(partiaddr, pibitno); 307 } 308 } 309 if (val) 310 SETBIT(iaddr, rcbm->csrcb_bankargs[abitno][0]); 311 } 312 313 return (iaddr); 314 } 315 316 static int 317 iaddr_to_rcb(struct mcamd_hdl *hdl, uint_t csmode, struct rcp_mc *mcpp, 318 uint64_t iaddr, uint32_t *rowp, uint32_t *colp, uint32_t *bankp) 319 { 320 const struct bankaddr_mode *bamp; 321 const struct csrcb_map *rcbm; 322 struct csintlv_desc csi; 323 324 if (gettbls(hdl, csmode, mcpp, &bamp, &rcbm, &csi, "iaddr_to_rcb") < 0) 325 return (-1); /* errno already set */ 326 327 *rowp = iaddr_to_row(hdl, bamp, rcbm, &csi, iaddr); 328 *colp = iaddr_to_col(hdl, bamp, rcbm, iaddr); 329 *bankp = iaddr_to_bank(hdl, rcbm, mcpp->bnkswzl, iaddr); 330 331 return (0); 332 } 333 334 /* 335 * Take a reconstructed InputAddr and undo the normalization described in 336 * BKDG 3.29 3.4.4 to include the base address of the MC if no node 337 * interleave or to insert the node interleave selection bits. 338 */ 339 static int 340 iaddr_unnormalize(struct mcamd_hdl *hdl, struct rcp_mc *mcpp, uint64_t iaddr, 341 uint64_t *rsltp) 342 { 343 uint64_t dramaddr; 344 int intlvbits; 345 346 switch (mcpp->intlven) { 347 case 0x0: 348 intlvbits = 0; 349 break; 350 case 0x1: 351 intlvbits = 1; 352 break; 353 case 0x3: 354 intlvbits = 2; 355 break; 356 case 0x7: 357 intlvbits = 3; 358 break; 359 default: 360 mcamd_dprintf(hdl, MCAMD_DBG_ERR, "iaddr_unnormalize: " 361 "illegal IntlvEn of %d for MC 0x%p\n", 362 (int)mcpp->intlven, (int)mcpp->num); 363 return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID)); 364 } 365 366 if (intlvbits != 0) { 367 /* 368 * For a 2/4/8 way interleave iaddr was formed by excising 369 * 1, 2, or 3 bits 12:12, 13:12, or 14:12 from dramaddr, 370 * the removed bits having done their job by selecting the 371 * responding node. So we must move bits 35:12 of the 372 * reconstructed iaddr up to make a 1, 2 or 3 bit hole and 373 * then fill those bits with the current IntlvSel value for 374 * this node. The node base address must be zero if nodes 375 * are interleaved. 376 */ 377 dramaddr = (BITS(iaddr, 35, 12) << intlvbits) | 378 (mcpp->intlvsel << 12) | BITS(iaddr, 11, 0); 379 } else { 380 dramaddr = iaddr + mcpp->base; 381 } 382 383 *rsltp = dramaddr; 384 385 mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "iaddr_unnormalize: iaddr 0x%llx " 386 "intlven 0x%x intlvsel 0x%x MC base 0x%llx --> 0x%llx\n", 387 iaddr, (int)mcpp->intlven, (int)mcpp->intlvsel, (int)mcpp->base, 388 dramaddr); 389 390 return (0); 391 } 392 393 int 394 mc_pa_to_offset(struct mcamd_hdl *hdl, mcamd_node_t *mc, mcamd_node_t *cs, 395 mcamd_node_t *dimm, uint64_t iaddr, uint64_t *offsetp) 396 { 397 mcamd_dimm_offset_un_t offset_un; 398 uint_t csmode; 399 uint32_t bankaddr, rowaddr, coladdr; 400 int rank; 401 mcamd_node_t *tcs; 402 struct rcp_mc mcp; 403 struct rcp_cs csp; 404 405 *offsetp = MCAMD_RC_INVALID_OFFSET; 406 407 if (getmcprops(hdl, mc, "mc_dimm_offset", &mcp) < 0 || 408 getcsprops(hdl, cs, "mc_dimm_offset", &csp) < 0) 409 return (-1); /* errno already set */ 410 411 csmode = MC_CS_MODE(mcp.csbnkmap, csp.num); 412 413 /* 414 * Convert chip-select number 0 .. 7 to a DIMM rank 0 .. 3. The 415 * rank is the index of the member of the dimm mcd_cs array which 416 * matches cs. 417 */ 418 for (rank = 0, tcs = mcamd_cs_next(hdl, (mcamd_node_t *)dimm, NULL); 419 tcs != NULL; 420 rank++, tcs = mcamd_cs_next(hdl, (mcamd_node_t *)dimm, tcs)) { 421 struct rcp_cs tcsp; 422 423 if (getcsprops(hdl, tcs, "mc_dimm_offset", &tcsp) < 0) 424 return (-1); /* errno already set */ 425 if (tcsp.num == csp.num) 426 break; 427 } 428 if (rank == MC_CHIP_DIMMRANKMAX) { 429 mcamd_dprintf(hdl, MCAMD_DBG_ERR, "mcamd_dimm_offset: " 430 "iteration over chip-selects of dimm 0x%p failed " 431 "to match on expected csnum %d\n", dimm, (int)csp.num); 432 return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID)); 433 } 434 435 if (iaddr_to_rcb(hdl, csmode, &mcp, iaddr, &rowaddr, 436 &coladdr, &bankaddr) < 0) 437 return (-1); /* errno already set */ 438 439 offset_un.do_offset = 0; 440 441 offset_un.do_valid = 1; 442 offset_un.do_version = MCAMD_OFFSET_VERSION; 443 offset_un.do_rank = rank; 444 offset_un.do_row = rowaddr; 445 offset_un.do_bank = bankaddr; 446 offset_un.do_col = coladdr; 447 448 *offsetp = offset_un.do_offset; 449 450 return (0); 451 } 452 453 /* 454 * Given a MC and DIMM and offset (dimm rank, row, col, internal bank) we 455 * find the corresponding chip-select for the rank and then reconstruct 456 * a system address. In the absence of serial number support it is possible 457 * that we may be asked to perform this operation on a dimm which has been 458 * swapped, perhaps even for a dimm of different size and number of ranks. 459 * This may happen if fmadm repair has not been used. There are some 460 * unused bits in the offset and we could guard against this a little 461 * by recording in those bit some of the physical characteristic of the 462 * original DIMM such as size, number of ranks etc. 463 */ 464 int 465 mc_offset_to_pa(struct mcamd_hdl *hdl, mcamd_node_t *mc, mcamd_node_t *dimm, 466 uint64_t offset, uint64_t *pap) 467 { 468 mcamd_node_t *cs; 469 mcamd_dimm_offset_un_t off_un; 470 uint32_t rank, rowaddr, bankaddr, coladdr; 471 int i; 472 uint64_t iaddr = 0; 473 const struct bankaddr_mode *bamp; 474 const struct csrcb_map *rcbm; 475 struct csintlv_desc csi; 476 struct rcp_mc mcp; 477 struct rcp_cs csp; 478 uint64_t csmode; 479 int maskhi_hi = MC_DC_CSM_MASKHI_HIBIT; 480 int maskhi_lo = MC_DC_CSM_MASKHI_LOBIT; 481 int masklo_hi = MC_DC_CSM_MASKLO_HIBIT; 482 int masklo_lo = MC_DC_CSM_MASKLO_LOBIT; 483 484 off_un.do_offset = offset; 485 rank = off_un.do_rank; 486 bankaddr = off_un.do_bank; 487 rowaddr = off_un.do_row; 488 coladdr = off_un.do_col; 489 490 if (getmcprops(hdl, mc, "mc_offset_to_pa", &mcp) < 0) 491 return (-1); /* errno already set */ 492 493 /* 494 * Find the rank'th chip-select on this dimm. 495 */ 496 i = 0; 497 cs = mcamd_cs_next(hdl, dimm, NULL); 498 while (i != rank && cs != NULL) { 499 cs = mcamd_cs_next(hdl, dimm, cs); 500 i++; 501 } 502 if (i != rank || cs == NULL) { 503 mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mc_offset_to_pa: Current " 504 "dimm in this slot does not have an %d'th cs\n", 505 rank); 506 return (mcamd_set_errno(hdl, EMCAMD_NOADDR)); 507 } 508 509 if (getcsprops(hdl, cs, "mc_offset_to_pa", &csp) < 0) 510 return (-1); /* errno already set */ 511 512 csmode = MC_CS_MODE(mcp.csbnkmap, csp.num); 513 514 if (gettbls(hdl, csmode, &mcp, &bamp, &rcbm, &csi, 515 "mc_offset_to_pa") < 0) 516 return (-1); /* errno already set */ 517 518 /*CONSTANTCONDITION*/ 519 if (MC_DC_CSM_UNMASKED_BITS != 0) { 520 iaddr |= iaddr_add(hdl, iaddr, 521 BITS(csp.base, maskhi_hi + MC_DC_CSM_UNMASKED_BITS, 522 maskhi_hi + 1), "unmaskable cs basehi bits"); 523 } 524 525 iaddr |= iaddr_add(hdl, iaddr, 526 BITS(csp.base, maskhi_hi, maskhi_lo) & 527 ~BITS(csp.mask, maskhi_hi, maskhi_lo), 528 "cs basehi bits not being masked"); 529 530 if (mcp.csintlvfctr != 0) { 531 iaddr |= iaddr_add(hdl, iaddr, 532 BITS(csp.base, masklo_hi, masklo_lo) & 533 ~BITS(csp.mask, masklo_hi, masklo_lo), 534 "cs baselo bits not being masked"); 535 } 536 537 iaddr |= iaddr_add(hdl, iaddr, 538 row_to_iaddr(hdl, bamp, rcbm, &csi, rowaddr), 539 "add iaddr bits from row"); 540 541 iaddr |= iaddr_add(hdl, iaddr, 542 col_to_iaddr(hdl, bamp, rcbm, coladdr), 543 "add iaddr bits from col"); 544 545 iaddr |= iaddr_add(hdl, iaddr, 546 bank_to_iaddr(hdl, rcbm, mcp.bnkswzl, iaddr, bankaddr), 547 "add iaddr bits from bank"); 548 549 if (iaddr_unnormalize(hdl, &mcp, iaddr, pap) < 0) 550 return (-1); /* errno already set */ 551 552 return (0); 553 } 554 555 int 556 mcamd_cs_size(struct mcamd_hdl *hdl, mcamd_node_t *mc, int csnum, size_t *szp) 557 { 558 uint_t csmode; 559 struct rcp_mc mcp; 560 const struct bankaddr_mode *bamp; 561 562 if (getmcprops(hdl, mc, "mcamd_cs_size", &mcp) < 0) 563 return (-1); /* errno already set */ 564 565 csmode = MC_CS_MODE(mcp.csbnkmap, csnum); 566 567 if (gettbls(hdl, csmode, &mcp, &bamp, NULL, NULL, "mcamd_cs_size") < 0) 568 return (-1); /* errno already set */ 569 570 *szp = MC_CS_SIZE(bamp, mcp.width); 571 572 return (0); 573 } 574