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 22 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * AMD memory enumeration 29 */ 30 31 #include <sys/types.h> 32 #include <unistd.h> 33 #include <stropts.h> 34 #include <sys/fm/protocol.h> 35 #include <sys/mc.h> 36 #include <sys/mc_amd.h> 37 #include <fm/topo_mod.h> 38 #include <strings.h> 39 #include <sys/stat.h> 40 #include <fcntl.h> 41 42 #include "chip.h" 43 44 #define MAX_CHANNUM 1 45 #define MAX_DIMMNUM 7 46 #define MAX_CSNUM 7 47 48 static const topo_pgroup_info_t cs_pgroup = 49 { PGNAME(CS), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 }; 50 static const topo_pgroup_info_t dimm_pgroup = 51 { PGNAME(DIMM), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 }; 52 static const topo_pgroup_info_t mc_pgroup = 53 { PGNAME(MCT), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 }; 54 static const topo_pgroup_info_t rank_pgroup = 55 { PGNAME(RANK), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 }; 56 static const topo_pgroup_info_t chan_pgroup = 57 { PGNAME(CHAN), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 }; 58 59 static const topo_method_t dimm_methods[] = { 60 { SIMPLE_DIMM_LBL, "Property method", 0, 61 TOPO_STABILITY_INTERNAL, simple_dimm_label}, 62 { SIMPLE_DIMM_LBL_MP, "Property method", 0, 63 TOPO_STABILITY_INTERNAL, simple_dimm_label_mp}, 64 { SEQ_DIMM_LBL, "Property method", 0, 65 TOPO_STABILITY_INTERNAL, seq_dimm_label}, 66 { G4_DIMM_LBL, "Property method", 0, 67 TOPO_STABILITY_INTERNAL, g4_dimm_label}, 68 { G12F_DIMM_LBL, "Property method", 0, 69 TOPO_STABILITY_INTERNAL, g12f_dimm_label}, 70 { GET_DIMM_SERIAL, "Property method", 0, 71 TOPO_STABILITY_INTERNAL, get_dimm_serial}, 72 { NULL } 73 }; 74 75 const topo_method_t rank_methods[] = { 76 { TOPO_METH_ASRU_COMPUTE, TOPO_METH_ASRU_COMPUTE_DESC, 77 TOPO_METH_ASRU_COMPUTE_VERSION, TOPO_STABILITY_INTERNAL, 78 mem_asru_compute }, 79 { TOPO_METH_PRESENT, TOPO_METH_PRESENT_DESC, 80 TOPO_METH_PRESENT_VERSION, TOPO_STABILITY_INTERNAL, 81 rank_fmri_present }, 82 { TOPO_METH_REPLACED, TOPO_METH_REPLACED_DESC, 83 TOPO_METH_REPLACED_VERSION, TOPO_STABILITY_INTERNAL, 84 rank_fmri_replaced }, 85 { NULL } 86 }; 87 88 const topo_method_t ntv_page_retire_methods[] = { 89 { TOPO_METH_RETIRE, TOPO_METH_RETIRE_DESC, 90 TOPO_METH_RETIRE_VERSION, TOPO_STABILITY_INTERNAL, 91 ntv_page_retire }, 92 { TOPO_METH_UNRETIRE, TOPO_METH_UNRETIRE_DESC, 93 TOPO_METH_UNRETIRE_VERSION, TOPO_STABILITY_INTERNAL, 94 ntv_page_unretire }, 95 { TOPO_METH_SERVICE_STATE, TOPO_METH_SERVICE_STATE_DESC, 96 TOPO_METH_SERVICE_STATE_VERSION, TOPO_STABILITY_INTERNAL, 97 ntv_page_service_state }, 98 { NULL } 99 }; 100 101 /* 102 * Serials, Labels are obtained from SMBIOS, so 103 * we leave out the related methods, any other 104 * methods that will be added to gen_cs_methods 105 * should be added to x86pi_gen_cs_methods too 106 */ 107 static const topo_method_t x86pi_gen_cs_methods[] = { 108 { TOPO_METH_ASRU_COMPUTE, TOPO_METH_ASRU_COMPUTE_DESC, 109 TOPO_METH_ASRU_COMPUTE_VERSION, TOPO_STABILITY_INTERNAL, 110 mem_asru_compute }, 111 { NULL } 112 }; 113 114 static const topo_method_t gen_cs_methods[] = { 115 { TOPO_METH_ASRU_COMPUTE, TOPO_METH_ASRU_COMPUTE_DESC, 116 TOPO_METH_ASRU_COMPUTE_VERSION, TOPO_STABILITY_INTERNAL, 117 mem_asru_compute }, 118 { SIMPLE_CS_LBL_MP, "Property method", 0, 119 TOPO_STABILITY_INTERNAL, simple_cs_label_mp}, 120 { GET_DIMM_SERIAL, "Property method", 0, 121 TOPO_STABILITY_INTERNAL, get_dimm_serial}, 122 { NULL } 123 }; 124 125 static nvlist_t *cs_fmri[MC_CHIP_NCS]; 126 127 /* 128 * Called when there is no memory-controller driver to provide topology 129 * information. Generate a maximal memory topology that is appropriate 130 * for the chip revision. The memory-controller node has already been 131 * bound as mcnode, and the parent of that is cnode. 132 * 133 * We create a tree of dram-channel and chip-select nodes below the 134 * memory-controller node. There will be two dram channels and 8 chip-selects 135 * below each, regardless of actual socket type, processor revision and so on. 136 * This is adequate for generic diagnosis up to family 0x10 revision D. 137 */ 138 /*ARGSUSED*/ 139 static int 140 amd_generic_mc_create(topo_mod_t *mod, uint16_t smbid, tnode_t *cnode, 141 tnode_t *mcnode, int family, int model, nvlist_t *auth) 142 { 143 int chan, cs; 144 145 /* 146 * Elsewhere we have already returned for families less than 0xf. 147 * This "generic" topology is adequate for all of family 0xf and 148 * for revisions A to D of family 0x10 (for the list of models 149 * in each revision, refer to usr/src/uts/i86pc/os/cpuid_subr.c). 150 * We cover all family 0x10 models, till model 9. 151 */ 152 if (family > 0x10 || (family == 0x10 && model > 9)) 153 return (1); 154 155 if (topo_node_range_create(mod, mcnode, CHAN_NODE_NAME, 0, 156 MAX_CHANNUM) < 0) { 157 whinge(mod, NULL, "amd_generic_mc_create: range create for " 158 "channels failed\n"); 159 return (-1); 160 } 161 162 for (chan = 0; chan <= MAX_CHANNUM; chan++) { 163 tnode_t *chnode; 164 nvlist_t *fmri; 165 int err; 166 167 if (mkrsrc(mod, mcnode, CHAN_NODE_NAME, chan, auth, 168 &fmri) != 0) { 169 whinge(mod, NULL, "amd_generic_mc_create: mkrsrc " 170 "failed\n"); 171 return (-1); 172 } 173 174 if ((chnode = topo_node_bind(mod, mcnode, CHAN_NODE_NAME, 175 chan, fmri)) == NULL) { 176 nvlist_free(fmri); 177 whinge(mod, NULL, "amd_generic_mc_create: node " 178 "bind failed\n"); 179 return (-1); 180 } 181 182 nvlist_free(fmri); 183 184 (void) topo_pgroup_create(chnode, &chan_pgroup, &err); 185 186 (void) topo_prop_set_string(chnode, PGNAME(CHAN), "channel", 187 TOPO_PROP_IMMUTABLE, chan == 0 ? "A" : "B", &err); 188 189 if (FM_AWARE_SMBIOS(mod)) { 190 if (topo_node_label_set(chnode, NULL, &err) == -1) 191 whinge(mod, NULL, "amd_generic_mc_create: " 192 "topo_node_label_set\n"); 193 if (topo_node_fru_set(chnode, NULL, 0, &err) != 0) 194 whinge(mod, NULL, "amd_generic_mc_create: " 195 "topo_node_fru_set failed\n"); 196 } 197 198 if (topo_node_range_create(mod, chnode, CS_NODE_NAME, 199 0, MAX_CSNUM) < 0) { 200 whinge(mod, NULL, "amd_generic_mc_create: " 201 "range create for cs failed\n"); 202 return (-1); 203 } 204 205 for (cs = 0; cs <= MAX_CSNUM; cs++) { 206 tnode_t *csnode; 207 208 if (mkrsrc(mod, chnode, CS_NODE_NAME, cs, auth, 209 &fmri) != 0) { 210 whinge(mod, NULL, "amd_generic_mc_create: " 211 "mkrsrc for cs failed\n"); 212 return (-1); 213 } 214 215 if ((csnode = topo_node_bind(mod, chnode, CS_NODE_NAME, 216 cs, fmri)) == NULL) { 217 nvlist_free(fmri); 218 whinge(mod, NULL, "amd_generic_mc_create: " 219 "bind for cs failed\n"); 220 return (-1); 221 } 222 223 /* 224 * Dynamic ASRU for page faults within a chip-select. 225 * The topology does not represent pages (there are 226 * too many) so when a page is faulted we generate 227 * an ASRU to represent the individual page. 228 * If SMBIOS meets FMA needs, derive labels & serials 229 * for DIMMS and apply to chip-select nodes. 230 * If deriving from SMBIOS, skip IPMI 231 */ 232 if (FM_AWARE_SMBIOS(mod)) { 233 if (topo_method_register(mod, csnode, 234 x86pi_gen_cs_methods) < 0) 235 whinge(mod, NULL, 236 "amd_generic_mc_create: " 237 "method registration failed\n"); 238 } else { 239 if (topo_method_register(mod, csnode, 240 gen_cs_methods) < 0) 241 whinge(mod, NULL, 242 "amd_generic_mc_create: method" 243 "registration failed\n"); 244 } 245 246 (void) topo_node_asru_set(csnode, fmri, 247 TOPO_ASRU_COMPUTE, &err); 248 nvlist_free(fmri); 249 250 /* 251 * If SMBIOS meets FMA needs, set DIMM as the FRU for 252 * the chip-select node. Use the channel & chip-select 253 * numbers to get the DIMM instance. 254 * Send via inst : dram channel number 255 * Receive via inst : dimm instance 256 */ 257 if (FM_AWARE_SMBIOS(mod)) { 258 int inst; 259 id_t dimm_smbid; 260 const char *serial; 261 const char *part; 262 const char *rev; 263 char *label; 264 265 (void) topo_pgroup_create(csnode, 266 &cs_pgroup, &err); 267 inst = chan; 268 dimm_smbid = memnode_to_smbiosid(smbid, 269 CS_NODE_NAME, cs, &inst); 270 serial = chip_serial_smbios_get(mod, 271 dimm_smbid); 272 part = chip_part_smbios_get(mod, 273 dimm_smbid); 274 rev = chip_rev_smbios_get(mod, dimm_smbid); 275 label = (char *)chip_label_smbios_get(mod, 276 chnode, dimm_smbid, NULL); 277 278 (void) topo_prop_set_string(csnode, PGNAME(CS), 279 FM_FMRI_HC_SERIAL_ID, TOPO_PROP_IMMUTABLE, 280 serial, &err); 281 (void) topo_prop_set_string(csnode, PGNAME(CS), 282 FM_FMRI_HC_PART, TOPO_PROP_IMMUTABLE, 283 part, &err); 284 (void) topo_prop_set_string(csnode, PGNAME(CS), 285 FM_FMRI_HC_REVISION, TOPO_PROP_IMMUTABLE, 286 rev, &err); 287 288 /* 289 * We apply DIMM labels to chip-select nodes, 290 * FRU for chip-selects should be DIMMs, and 291 * we do not derive dimm nodes for Family 0x10 292 * so FRU fmri is NULL, but FRU Labels are set, 293 * the FRU labels point to the DIMM. 294 */ 295 (void) topo_node_label_set(csnode, label, &err); 296 topo_mod_strfree(mod, label); 297 } 298 } 299 } 300 301 return (0); 302 } 303 304 static nvlist_t * 305 amd_lookup_by_mcid(topo_mod_t *mod, topo_instance_t id) 306 { 307 mc_snapshot_info_t mcs; 308 void *buf = NULL; 309 uint8_t ver; 310 311 nvlist_t *nvl = NULL; 312 char path[64]; 313 int fd, err; 314 315 (void) snprintf(path, sizeof (path), "/dev/mc/mc%d", id); 316 fd = open(path, O_RDONLY); 317 318 if (fd == -1) { 319 /* 320 * Some v20z and v40z systems may have had the 3rd-party 321 * NWSnps packagae installed which installs a /dev/mc 322 * link. So try again via /devices. 323 */ 324 (void) snprintf(path, sizeof (path), 325 "/devices/pci@0,0/pci1022,1102@%x,2:mc-amd", 326 MC_AMD_DEV_OFFSET + id); 327 fd = open(path, O_RDONLY); 328 } 329 330 if (fd == -1) 331 return (NULL); /* do not whinge */ 332 333 if (ioctl(fd, MC_IOC_SNAPSHOT_INFO, &mcs) == -1 || 334 (buf = topo_mod_alloc(mod, mcs.mcs_size)) == NULL || 335 ioctl(fd, MC_IOC_SNAPSHOT, buf) == -1) { 336 337 whinge(mod, NULL, "mc failed to snapshot %s: %s\n", 338 path, strerror(errno)); 339 340 free(buf); 341 (void) close(fd); 342 return (NULL); 343 } 344 345 (void) close(fd); 346 err = nvlist_unpack(buf, mcs.mcs_size, &nvl, 0); 347 topo_mod_free(mod, buf, mcs.mcs_size); 348 349 if (nvlist_lookup_uint8(nvl, MC_NVLIST_VERSTR, &ver) != 0) { 350 whinge(mod, NULL, "mc nvlist is not versioned\n"); 351 nvlist_free(nvl); 352 return (NULL); 353 } else if (ver != MC_NVLIST_VERS1) { 354 whinge(mod, NULL, "mc nvlist version mismatch\n"); 355 nvlist_free(nvl); 356 return (NULL); 357 } 358 359 return (err ? NULL : nvl); 360 } 361 362 int 363 amd_rank_create(topo_mod_t *mod, tnode_t *pnode, nvlist_t *dimmnvl, 364 nvlist_t *auth) 365 { 366 uint64_t *csnumarr; 367 char **csnamearr; 368 uint_t ncs, ncsname; 369 tnode_t *ranknode; 370 nvlist_t *fmri, *pfmri = NULL; 371 uint64_t dsz, rsz; 372 int nerr = 0; 373 int err; 374 int i; 375 376 if (nvlist_lookup_uint64_array(dimmnvl, "csnums", &csnumarr, 377 &ncs) != 0 || nvlist_lookup_string_array(dimmnvl, "csnames", 378 &csnamearr, &ncsname) != 0 || ncs != ncsname) { 379 whinge(mod, &nerr, "amd_rank_create: " 380 "csnums/csnames extraction failed\n"); 381 return (nerr); 382 } 383 384 if (topo_node_resource(pnode, &pfmri, &err) < 0) { 385 whinge(mod, &nerr, "amd_rank_create: parent fmri lookup " 386 "failed\n"); 387 return (nerr); 388 } 389 390 if (topo_node_range_create(mod, pnode, RANK_NODE_NAME, 0, ncs) < 0) { 391 whinge(mod, &nerr, "amd_rank_create: range create failed\n"); 392 nvlist_free(pfmri); 393 return (nerr); 394 } 395 396 if (topo_prop_get_uint64(pnode, PGNAME(DIMM), "size", &dsz, 397 &err) == 0) { 398 rsz = dsz / ncs; 399 } else { 400 whinge(mod, &nerr, "amd_rank_create: parent dimm has no " 401 "size\n"); 402 return (nerr); 403 } 404 405 for (i = 0; i < ncs; i++) { 406 if (mkrsrc(mod, pnode, RANK_NODE_NAME, i, auth, &fmri) < 0) { 407 whinge(mod, &nerr, "amd_rank_create: mkrsrc failed\n"); 408 continue; 409 } 410 411 if ((ranknode = topo_node_bind(mod, pnode, RANK_NODE_NAME, i, 412 fmri)) == NULL) { 413 nvlist_free(fmri); 414 whinge(mod, &nerr, "amd_rank_create: node bind " 415 "failed\n"); 416 continue; 417 } 418 419 nvlist_free(fmri); 420 if (FM_AWARE_SMBIOS(mod)) 421 (void) topo_node_fru_set(ranknode, NULL, 0, &err); 422 else 423 (void) topo_node_fru_set(ranknode, pfmri, 0, &err); 424 425 /* 426 * If a rank is faulted the asru is the associated 427 * chip-select, but if a page within a rank is faulted 428 * the asru is just that page. Hence the dual preconstructed 429 * and computed ASRU. 430 */ 431 if (topo_method_register(mod, ranknode, rank_methods) < 0) 432 whinge(mod, &nerr, "amd_rank_create: " 433 "topo_method_register failed"); 434 435 if (! is_xpv() && topo_method_register(mod, ranknode, 436 ntv_page_retire_methods) < 0) 437 whinge(mod, &nerr, "amd_rank_create: " 438 "topo_method_register failed"); 439 440 (void) topo_node_asru_set(ranknode, cs_fmri[csnumarr[i]], 441 TOPO_ASRU_COMPUTE, &err); 442 443 (void) topo_pgroup_create(ranknode, &rank_pgroup, &err); 444 445 (void) topo_prop_set_uint64(ranknode, PGNAME(RANK), "size", 446 TOPO_PROP_IMMUTABLE, rsz, &err); 447 448 (void) topo_prop_set_string(ranknode, PGNAME(RANK), "csname", 449 TOPO_PROP_IMMUTABLE, csnamearr[i], &err); 450 451 (void) topo_prop_set_uint64(ranknode, PGNAME(RANK), "csnum", 452 TOPO_PROP_IMMUTABLE, csnumarr[i], &err); 453 } 454 455 nvlist_free(pfmri); 456 457 return (nerr); 458 } 459 460 static int 461 amd_dimm_create(topo_mod_t *mod, uint16_t chip_smbid, tnode_t *pnode, 462 const char *name, nvlist_t *mc, nvlist_t *auth) 463 { 464 int i, err, nerr = 0; 465 int perr = 0; 466 nvpair_t *nvp; 467 tnode_t *dimmnode; 468 nvlist_t *fmri, **dimmarr = NULL; 469 uint64_t num; 470 uint_t ndimm; 471 id_t smbid; 472 const char *serial; 473 const char *part; 474 const char *rev; 475 476 if (nvlist_lookup_nvlist_array(mc, "dimmlist", &dimmarr, &ndimm) != 0) { 477 whinge(mod, NULL, "amd_dimm_create: dimmlist lookup failed\n"); 478 return (-1); 479 } 480 481 if (ndimm == 0) 482 return (0); /* no dimms present on this node */ 483 484 if (topo_node_range_create(mod, pnode, name, 0, MAX_DIMMNUM) < 0) { 485 whinge(mod, NULL, "amd_dimm_create: range create failed\n"); 486 return (-1); 487 } 488 489 for (i = 0; i < ndimm; i++) { 490 if (nvlist_lookup_uint64(dimmarr[i], "num", &num) != 0) { 491 whinge(mod, &nerr, "amd_dimm_create: dimm num property " 492 "missing\n"); 493 continue; 494 } 495 496 if (mkrsrc(mod, pnode, name, num, auth, &fmri) < 0) { 497 whinge(mod, &nerr, "amd_dimm_create: mkrsrc failed\n"); 498 continue; 499 } 500 if (FM_AWARE_SMBIOS(mod)) { 501 smbid = memnode_to_smbiosid(chip_smbid, DIMM_NODE_NAME, 502 i, NULL); 503 serial = chip_serial_smbios_get(mod, smbid); 504 part = chip_part_smbios_get(mod, smbid); 505 rev = chip_rev_smbios_get(mod, smbid); 506 perr += nvlist_add_string(fmri, FM_FMRI_HC_SERIAL_ID, 507 serial); 508 perr += nvlist_add_string(fmri, FM_FMRI_HC_PART, 509 part); 510 perr += nvlist_add_string(fmri, FM_FMRI_HC_REVISION, 511 rev); 512 513 if (perr != 0) 514 whinge(mod, NULL, "amd_dimm_create:" 515 "nvlist_add_string failed\n"); 516 } 517 518 if ((dimmnode = topo_node_bind(mod, pnode, name, num, fmri)) 519 == NULL) { 520 nvlist_free(fmri); 521 whinge(mod, &nerr, "amd_dimm_create: node bind " 522 "failed\n"); 523 continue; 524 } 525 526 if (!FM_AWARE_SMBIOS(mod)) 527 if (topo_method_register(mod, 528 dimmnode, dimm_methods) < 0) 529 whinge(mod, &nerr, "amd_dimm_create: " 530 "topo_method_register failed"); 531 532 (void) topo_pgroup_create(dimmnode, &dimm_pgroup, &err); 533 534 if (FM_AWARE_SMBIOS(mod)) { 535 char *label; 536 537 nvlist_free(fmri); 538 (void) topo_node_resource(dimmnode, 539 &fmri, &err); 540 541 label = (char *)chip_label_smbios_get(mod, 542 pnode, smbid, NULL); 543 if (topo_node_label_set(dimmnode, label, 544 &perr) == -1) 545 topo_mod_dprintf(mod, "Failed" 546 "to set label\n"); 547 topo_mod_strfree(mod, label); 548 549 (void) topo_prop_set_string(dimmnode, PGNAME(DIMM), 550 FM_FMRI_HC_SERIAL_ID, TOPO_PROP_IMMUTABLE, 551 serial, &err); 552 (void) topo_prop_set_string(dimmnode, PGNAME(DIMM), 553 FM_FMRI_HC_PART, TOPO_PROP_IMMUTABLE, 554 part, &err); 555 (void) topo_prop_set_string(dimmnode, PGNAME(DIMM), 556 FM_FMRI_HC_REVISION, TOPO_PROP_IMMUTABLE, 557 rev, &err); 558 } 559 560 (void) topo_node_asru_set(dimmnode, fmri, 0, &err); 561 (void) topo_node_fru_set(dimmnode, fmri, 0, &err); 562 nvlist_free(fmri); 563 564 for (nvp = nvlist_next_nvpair(dimmarr[i], NULL); nvp != NULL; 565 nvp = nvlist_next_nvpair(dimmarr[i], nvp)) { 566 if (nvpair_type(nvp) == DATA_TYPE_UINT64_ARRAY && 567 strcmp(nvpair_name(nvp), "csnums") == 0 || 568 nvpair_type(nvp) == DATA_TYPE_STRING_ARRAY && 569 strcmp(nvpair_name(nvp), "csnames") == 0) 570 continue; /* used in amd_rank_create() */ 571 572 nerr += nvprop_add(mod, nvp, PGNAME(DIMM), dimmnode); 573 } 574 575 nerr += amd_rank_create(mod, dimmnode, dimmarr[i], auth); 576 } 577 578 return (nerr == 0 ? 0 : -1); 579 } 580 581 static int 582 amd_cs_create(topo_mod_t *mod, tnode_t *pnode, const char *name, nvlist_t *mc, 583 nvlist_t *auth) 584 { 585 int i, err, nerr = 0; 586 nvpair_t *nvp; 587 tnode_t *csnode; 588 nvlist_t *fmri, **csarr = NULL; 589 uint64_t csnum; 590 uint_t ncs; 591 592 if (nvlist_lookup_nvlist_array(mc, "cslist", &csarr, &ncs) != 0) 593 return (-1); 594 595 if (ncs == 0) 596 return (0); /* no chip-selects configured on this node */ 597 598 if (topo_node_range_create(mod, pnode, name, 0, MAX_CSNUM) < 0) 599 return (-1); 600 601 for (i = 0; i < ncs; i++) { 602 if (nvlist_lookup_uint64(csarr[i], "num", &csnum) != 0) { 603 whinge(mod, &nerr, "amd_cs_create: cs num property " 604 "missing\n"); 605 continue; 606 } 607 608 if (mkrsrc(mod, pnode, name, csnum, auth, &fmri) != 0) { 609 whinge(mod, &nerr, "amd_cs_create: mkrsrc failed\n"); 610 continue; 611 } 612 613 if ((csnode = topo_node_bind(mod, pnode, name, csnum, fmri)) 614 == NULL) { 615 nvlist_free(fmri); 616 whinge(mod, &nerr, "amd_cs_create: node bind failed\n"); 617 continue; 618 } 619 620 cs_fmri[csnum] = fmri; /* nvlist will be freed in mc_create */ 621 622 (void) topo_node_asru_set(csnode, fmri, 0, &err); 623 624 (void) topo_node_fru_set(csnode, fmri, 0, &err); 625 626 (void) topo_pgroup_create(csnode, &cs_pgroup, &err); 627 628 for (nvp = nvlist_next_nvpair(csarr[i], NULL); nvp != NULL; 629 nvp = nvlist_next_nvpair(csarr[i], nvp)) { 630 nerr += nvprop_add(mod, nvp, PGNAME(CS), csnode); 631 } 632 } 633 634 return (nerr == 0 ? 0 : -1); 635 } 636 637 static int 638 amd_dramchan_create(topo_mod_t *mod, tnode_t *pnode, const char *name, 639 nvlist_t *auth) 640 { 641 tnode_t *chnode; 642 nvlist_t *fmri; 643 char *socket; 644 int i, nchan; 645 nvlist_t *pfmri = NULL; 646 int err, nerr = 0; 647 648 /* 649 * We will enumerate the number of channels present even if only 650 * channel A is in use (i.e., running in 64-bit mode). Only 651 * the socket 754 package has a single channel. 652 */ 653 if (topo_prop_get_string(pnode, PGNAME(MCT), "socket", 654 &socket, &err) == 0 && strcmp(socket, "Socket 754") == 0) 655 nchan = 1; 656 else 657 nchan = 2; 658 659 topo_mod_strfree(mod, socket); 660 661 if (topo_node_range_create(mod, pnode, name, 0, nchan - 1) < 0) 662 return (-1); 663 664 (void) topo_node_fru(pnode, &pfmri, NULL, &err); 665 666 for (i = 0; i < nchan; i++) { 667 if (mkrsrc(mod, pnode, name, i, auth, &fmri) != 0) { 668 whinge(mod, &nerr, "amd_dramchan_create: mkrsrc " 669 "failed\n"); 670 continue; 671 } 672 673 if ((chnode = topo_node_bind(mod, pnode, name, i, fmri)) 674 == NULL) { 675 nvlist_free(fmri); 676 whinge(mod, &nerr, "amd_dramchan_create: node bind " 677 "failed\n"); 678 continue; 679 } 680 681 (void) topo_node_asru_set(chnode, fmri, 0, &err); 682 if (pfmri) 683 (void) topo_node_fru_set(chnode, pfmri, 0, &err); 684 685 nvlist_free(fmri); 686 687 (void) topo_pgroup_create(chnode, &chan_pgroup, &err); 688 689 (void) topo_prop_set_string(chnode, PGNAME(CHAN), "channel", 690 TOPO_PROP_IMMUTABLE, i == 0 ? "A" : "B", &err); 691 } 692 if (pfmri) 693 nvlist_free(pfmri); 694 695 return (nerr == 0 ? 0 : -1); 696 } 697 698 static int 699 amd_htconfig(topo_mod_t *mod, tnode_t *cnode, nvlist_t *htnvl) 700 { 701 nvpair_t *nvp; 702 int nerr = 0; 703 704 if (strcmp(topo_node_name(cnode), CHIP_NODE_NAME) != 0) { 705 whinge(mod, &nerr, "amd_htconfig: must pass a chip node!"); 706 return (-1); 707 } 708 709 for (nvp = nvlist_next_nvpair(htnvl, NULL); nvp != NULL; 710 nvp = nvlist_next_nvpair(htnvl, nvp)) { 711 if (nvprop_add(mod, nvp, PGNAME(CHIP), cnode) != 0) 712 nerr++; 713 } 714 715 return (nerr == 0 ? 0 : -1); 716 } 717 718 void 719 amd_mc_create(topo_mod_t *mod, uint16_t smbid, tnode_t *pnode, 720 const char *name, nvlist_t *auth, int32_t procnodeid, 721 int32_t procnodes_per_pkg, int family, 722 int model, int *nerrp) 723 { 724 tnode_t *mcnode; 725 nvlist_t *fmri; 726 nvpair_t *nvp; 727 nvlist_t *mc = NULL; 728 int i, err; 729 int mcnum = procnodeid % procnodes_per_pkg; 730 char *serial = NULL; 731 char *part = NULL; 732 char *rev = NULL; 733 734 /* 735 * Return with no error for anything before AMD family 0xf - we 736 * won't generate even a generic memory topology for earlier 737 * families. 738 */ 739 if (family < 0xf) 740 return; 741 742 if (topo_node_lookup(pnode, name, mcnum) != NULL) 743 return; 744 745 if (FM_AWARE_SMBIOS(mod)) { 746 (void) topo_node_resource(pnode, &fmri, &err); 747 (void) nvlist_lookup_string(fmri, "serial", &serial); 748 (void) nvlist_lookup_string(fmri, "part", &part); 749 (void) nvlist_lookup_string(fmri, "revision", &rev); 750 nvlist_free(fmri); 751 } 752 753 if (mkrsrc(mod, pnode, name, mcnum, auth, &fmri) != 0) { 754 whinge(mod, nerrp, "mc_create: mkrsrc failed\n"); 755 return; 756 } 757 758 if (FM_AWARE_SMBIOS(mod)) { 759 (void) nvlist_add_string(fmri, "serial", serial); 760 (void) nvlist_add_string(fmri, "part", part); 761 (void) nvlist_add_string(fmri, "revision", rev); 762 } 763 764 if ((mcnode = topo_node_bind(mod, pnode, name, mcnum, 765 fmri)) == NULL) { 766 nvlist_free(fmri); 767 whinge(mod, nerrp, "mc_create: mc bind failed\n"); 768 return; 769 } 770 if (topo_node_fru_set(mcnode, NULL, 0, &err) < 0) 771 whinge(mod, nerrp, "mc_create: topo_node_fru_set failed\n"); 772 773 if (FM_AWARE_SMBIOS(mod)) { 774 if (topo_node_label_set(mcnode, NULL, &err) == -1) 775 topo_mod_dprintf(mod, "Failed to set label\n"); 776 } 777 778 nvlist_free(fmri); 779 780 if (topo_pgroup_create(mcnode, &mc_pgroup, &err) < 0) 781 whinge(mod, nerrp, "mc_create: topo_pgroup_create failed\n"); 782 783 if (topo_prop_set_int32(mcnode, PGNAME(MCT), MCT_PROCNODE_ID, 784 TOPO_PROP_IMMUTABLE, procnodeid, nerrp) != 0) 785 whinge(mod, nerrp, "mc_create: topo_prop_set_int32 failed to" 786 "add node id\n"); 787 788 if ((mc = amd_lookup_by_mcid(mod, topo_node_instance(pnode))) == NULL) { 789 /* 790 * If a memory-controller driver exists for this chip model 791 * it has not attached or has otherwise malfunctioned; 792 * alternatively no memory-controller driver exists for this 793 * (presumably newly-released) cpu model. We fallback to 794 * creating a generic maximal topology. 795 */ 796 if (amd_generic_mc_create(mod, smbid, pnode, mcnode, 797 family, model, auth) != 0) 798 whinge(mod, nerrp, 799 "mc_create: amd_generic_mc_create failed\n"); 800 return; 801 } 802 803 /* 804 * Add memory controller properties 805 */ 806 for (nvp = nvlist_next_nvpair(mc, NULL); nvp != NULL; 807 nvp = nvlist_next_nvpair(mc, nvp)) { 808 char *name = nvpair_name(nvp); 809 data_type_t type = nvpair_type(nvp); 810 811 if (type == DATA_TYPE_NVLIST_ARRAY && 812 (strcmp(name, "cslist") == 0 || 813 strcmp(name, "dimmlist") == 0)) { 814 continue; 815 } else if (type == DATA_TYPE_UINT8 && 816 strcmp(name, MC_NVLIST_VERSTR) == 0) { 817 continue; 818 } else if (type == DATA_TYPE_NVLIST && 819 strcmp(name, "htconfig") == 0) { 820 nvlist_t *htnvl; 821 822 (void) nvpair_value_nvlist(nvp, &htnvl); 823 if (amd_htconfig(mod, pnode, htnvl) != 0) 824 whinge(mod, nerrp, 825 "mc_create: amd_htconfig failed\n"); 826 } else { 827 if (nvprop_add(mod, nvp, PGNAME(MCT), mcnode) != 0) 828 whinge(mod, nerrp, 829 "mc_create: nvprop_add failed\n"); 830 } 831 } 832 833 if (amd_dramchan_create(mod, mcnode, CHAN_NODE_NAME, auth) != 0 || 834 amd_cs_create(mod, mcnode, CS_NODE_NAME, mc, auth) != 0 || 835 amd_dimm_create(mod, smbid, mcnode, DIMM_NODE_NAME, mc, auth) != 0) 836 whinge(mod, nerrp, "mc_create: create children failed\n"); 837 838 /* 839 * Free the fmris for the chip-selects allocated in amd_cs_create 840 */ 841 for (i = 0; i < MC_CHIP_NCS; i++) { 842 if (cs_fmri[i] != NULL) { 843 nvlist_free(cs_fmri[i]); 844 cs_fmri[i] = NULL; 845 } 846 } 847 848 nvlist_free(mc); 849 } 850