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