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