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 2007 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * AMD memory enumeration 31 */ 32 33 #include <sys/types.h> 34 #include <unistd.h> 35 #include <stropts.h> 36 #include <sys/fm/protocol.h> 37 #include <sys/mc.h> 38 #include <sys/mc_amd.h> 39 #include <fm/topo_mod.h> 40 #include <strings.h> 41 #include <sys/stat.h> 42 #include <fcntl.h> 43 44 #include "chip.h" 45 46 #define MAX_CHANNUM 1 47 #define MAX_DIMMNUM 7 48 #define MAX_CSNUM 7 49 50 static const topo_pgroup_info_t cs_pgroup = 51 { PGNAME(CS), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 }; 52 static const topo_pgroup_info_t dimm_pgroup = 53 { PGNAME(DIMM), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 }; 54 static const topo_pgroup_info_t mc_pgroup = 55 { PGNAME(MCT), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 }; 56 static const topo_pgroup_info_t rank_pgroup = 57 { PGNAME(RANK), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 }; 58 static const topo_pgroup_info_t chan_pgroup = 59 { PGNAME(CHAN), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 }; 60 61 static const topo_method_t dimm_methods[] = { 62 { SIMPLE_DIMM_LBL, "Property method", 0, 63 TOPO_STABILITY_INTERNAL, simple_dimm_label}, 64 { SIMPLE_DIMM_LBL_MP, "Property method", 0, 65 TOPO_STABILITY_INTERNAL, simple_dimm_label_mp}, 66 { SEQ_DIMM_LBL, "Property method", 0, 67 TOPO_STABILITY_INTERNAL, seq_dimm_label}, 68 { NULL } 69 }; 70 71 static const topo_method_t rank_methods[] = { 72 { TOPO_METH_ASRU_COMPUTE, TOPO_METH_ASRU_COMPUTE_DESC, 73 TOPO_METH_ASRU_COMPUTE_VERSION, TOPO_STABILITY_INTERNAL, 74 mem_asru_compute }, 75 { NULL } 76 }; 77 78 static const topo_method_t gen_cs_methods[] = { 79 { TOPO_METH_ASRU_COMPUTE, TOPO_METH_ASRU_COMPUTE_DESC, 80 TOPO_METH_ASRU_COMPUTE_VERSION, TOPO_STABILITY_INTERNAL, 81 mem_asru_compute }, 82 { SIMPLE_CS_LBL_MP, "Property method", 0, 83 TOPO_STABILITY_INTERNAL, simple_cs_label_mp}, 84 { NULL } 85 }; 86 87 static nvlist_t *cs_fmri[MC_CHIP_NCS]; 88 89 /* 90 * Called when there is no memory-controller driver to provide topology 91 * information. Generate a maximal memory topology that is appropriate 92 * for the chip revision. The memory-controller node has already been 93 * bound as mcnode, and the parent of that is cnode. 94 * 95 * We create a tree of dram-channel and chip-select nodes below the 96 * memory-controller node. There will be two dram channels and 8 chip-selects 97 * below each, regardless of actual socket type, processor revision and so on. 98 * This is adequate for generic diagnosis up to family 0x10 revision C. 99 * When support for revision D is implemented (or maybe C) we should take 100 * the opportunity to rework the topology tree completely (socket change will 101 * mean there can be no diagnosis history tied to the topology). 102 */ 103 /*ARGSUSED*/ 104 static int 105 amd_generic_mc_create(topo_mod_t *mod, tnode_t *cnode, tnode_t *mcnode, 106 int family, int model, int stepping, nvlist_t *auth) 107 { 108 int chan, cs; 109 110 /* 111 * Elsewhere we have already returned for families less than 0xf. 112 * This "generic" topology is adequate for all of family 0xf and 113 * for revisions A, B and C of family 0x10 (A = model 0, B = model 1, 114 * we'll guess C = model 3 at this point). 115 */ 116 if (family > 0x10 || (family == 0x10 && model > 3)) 117 return (1); 118 119 if (topo_node_range_create(mod, mcnode, CHAN_NODE_NAME, 0, 120 MAX_CHANNUM) < 0) { 121 whinge(mod, NULL, "amd_generic_mc_create: range create for " 122 "channels failed\n"); 123 return (-1); 124 } 125 126 for (chan = 0; chan <= MAX_CHANNUM; chan++) { 127 tnode_t *chnode; 128 nvlist_t *fmri; 129 int err; 130 131 if (mkrsrc(mod, mcnode, CHAN_NODE_NAME, chan, auth, 132 &fmri) != 0) { 133 whinge(mod, NULL, "amd_generic_mc_create: mkrsrc " 134 "failed\n"); 135 return (-1); 136 } 137 138 if ((chnode = topo_node_bind(mod, mcnode, CHAN_NODE_NAME, 139 chan, fmri)) == NULL) { 140 nvlist_free(fmri); 141 whinge(mod, NULL, "amd_generic_mc_create: node " 142 "bind failed\n"); 143 return (-1); 144 } 145 146 nvlist_free(fmri); 147 148 (void) topo_pgroup_create(chnode, &chan_pgroup, &err); 149 150 (void) topo_prop_set_string(chnode, PGNAME(CHAN), "channel", 151 TOPO_PROP_IMMUTABLE, chan == 0 ? "A" : "B", &err); 152 153 if (topo_node_range_create(mod, chnode, CS_NODE_NAME, 154 0, MAX_CSNUM) < 0) { 155 whinge(mod, NULL, "amd_generic_mc_create: " 156 "range create for cs failed\n"); 157 return (-1); 158 } 159 160 for (cs = 0; cs <= MAX_CSNUM; cs++) { 161 tnode_t *csnode; 162 163 if (mkrsrc(mod, chnode, CS_NODE_NAME, cs, auth, 164 &fmri) != 0) { 165 whinge(mod, NULL, "amd_generic_mc_create: " 166 "mkrsrc for cs failed\n"); 167 return (-1); 168 } 169 170 if ((csnode = topo_node_bind(mod, chnode, CS_NODE_NAME, 171 cs, fmri)) == NULL) { 172 nvlist_free(fmri); 173 whinge(mod, NULL, "amd_generic_mc_create: " 174 "bind for cs failed\n"); 175 return (-1); 176 } 177 178 /* 179 * Dynamic ASRU for page faults within a chip-select. 180 * The topology does not represent pages (there are 181 * too many) so when a page is faulted we generate 182 * an ASRU to represent the individual page. 183 */ 184 if (topo_method_register(mod, csnode, 185 gen_cs_methods) < 0) 186 whinge(mod, NULL, "amd_generic_mc_create: " 187 "method registration failed\n"); 188 189 (void) topo_node_asru_set(csnode, fmri, 190 TOPO_ASRU_COMPUTE, &err); 191 192 nvlist_free(fmri); 193 } 194 } 195 196 return (0); 197 } 198 199 static nvlist_t * 200 amd_lookup_by_mcid(topo_mod_t *mod, topo_instance_t id) 201 { 202 mc_snapshot_info_t mcs; 203 void *buf = NULL; 204 uint8_t ver; 205 206 nvlist_t *nvl = NULL; 207 char path[64]; 208 int fd, err; 209 210 (void) snprintf(path, sizeof (path), "/dev/mc/mc%d", id); 211 fd = open(path, O_RDONLY); 212 213 if (fd == -1) { 214 /* 215 * Some v20z and v40z systems may have had the 3rd-party 216 * NWSnps packagae installed which installs a /dev/mc 217 * link. So try again via /devices. 218 */ 219 (void) snprintf(path, sizeof (path), 220 "/devices/pci@0,0/pci1022,1102@%x,2:mc-amd", 221 MC_AMD_DEV_OFFSET + id); 222 fd = open(path, O_RDONLY); 223 } 224 225 if (fd == -1) 226 return (NULL); /* do not whinge */ 227 228 if (ioctl(fd, MC_IOC_SNAPSHOT_INFO, &mcs) == -1 || 229 (buf = topo_mod_alloc(mod, mcs.mcs_size)) == NULL || 230 ioctl(fd, MC_IOC_SNAPSHOT, buf) == -1) { 231 232 whinge(mod, NULL, "mc failed to snapshot %s: %s\n", 233 path, strerror(errno)); 234 235 free(buf); 236 (void) close(fd); 237 return (NULL); 238 } 239 240 (void) close(fd); 241 err = nvlist_unpack(buf, mcs.mcs_size, &nvl, 0); 242 topo_mod_free(mod, buf, mcs.mcs_size); 243 244 245 if (nvlist_lookup_uint8(nvl, MC_NVLIST_VERSTR, &ver) != 0) { 246 whinge(mod, NULL, "mc nvlist is not versioned\n"); 247 nvlist_free(nvl); 248 return (NULL); 249 } else if (ver != MC_NVLIST_VERS1) { 250 whinge(mod, NULL, "mc nvlist version mismatch\n"); 251 nvlist_free(nvl); 252 return (NULL); 253 } 254 255 return (err ? NULL : nvl); 256 } 257 258 int 259 amd_rank_create(topo_mod_t *mod, tnode_t *pnode, nvlist_t *dimmnvl, 260 nvlist_t *auth) 261 { 262 uint64_t *csnumarr; 263 char **csnamearr; 264 uint_t ncs, ncsname; 265 tnode_t *ranknode; 266 nvlist_t *fmri, *pfmri = NULL; 267 uint64_t dsz, rsz; 268 int nerr = 0; 269 int err; 270 int i; 271 272 if (nvlist_lookup_uint64_array(dimmnvl, "csnums", &csnumarr, 273 &ncs) != 0 || nvlist_lookup_string_array(dimmnvl, "csnames", 274 &csnamearr, &ncsname) != 0 || ncs != ncsname) { 275 whinge(mod, &nerr, "amd_rank_create: " 276 "csnums/csnames extraction failed\n"); 277 return (nerr); 278 } 279 280 if (topo_node_resource(pnode, &pfmri, &err) < 0) { 281 whinge(mod, &nerr, "amd_rank_create: parent fmri lookup " 282 "failed\n"); 283 return (nerr); 284 } 285 286 if (topo_node_range_create(mod, pnode, RANK_NODE_NAME, 0, ncs) < 0) { 287 whinge(mod, &nerr, "amd_rank_create: range create failed\n"); 288 nvlist_free(pfmri); 289 return (nerr); 290 } 291 292 if (topo_prop_get_uint64(pnode, PGNAME(DIMM), "size", &dsz, 293 &err) == 0) { 294 rsz = dsz / ncs; 295 } else { 296 whinge(mod, &nerr, "amd_rank_create: parent dimm has no " 297 "size\n"); 298 return (nerr); 299 } 300 301 for (i = 0; i < ncs; i++) { 302 if (mkrsrc(mod, pnode, RANK_NODE_NAME, i, auth, &fmri) < 0) { 303 whinge(mod, &nerr, "amd_rank_create: mkrsrc failed\n"); 304 continue; 305 } 306 307 if ((ranknode = topo_node_bind(mod, pnode, RANK_NODE_NAME, i, 308 fmri)) == NULL) { 309 nvlist_free(fmri); 310 whinge(mod, &nerr, "amd_rank_create: node bind " 311 "failed\n"); 312 continue; 313 } 314 315 nvlist_free(fmri); 316 317 (void) topo_node_fru_set(ranknode, pfmri, 0, &err); 318 319 /* 320 * If a rank is faulted the asru is the associated 321 * chip-select, but if a page within a rank is faulted 322 * the asru is just that page. Hence the dual preconstructed 323 * and computed ASRU. 324 */ 325 if (topo_method_register(mod, ranknode, rank_methods) < 0) 326 whinge(mod, &nerr, "amd_rank_create: " 327 "topo_method_register failed"); 328 329 (void) topo_node_asru_set(ranknode, cs_fmri[csnumarr[i]], 330 TOPO_ASRU_COMPUTE, &err); 331 332 (void) topo_pgroup_create(ranknode, &rank_pgroup, &err); 333 334 (void) topo_prop_set_uint64(ranknode, PGNAME(RANK), "size", 335 TOPO_PROP_IMMUTABLE, rsz, &err); 336 337 (void) topo_prop_set_string(ranknode, PGNAME(RANK), "csname", 338 TOPO_PROP_IMMUTABLE, csnamearr[i], &err); 339 340 (void) topo_prop_set_uint64(ranknode, PGNAME(RANK), "csnum", 341 TOPO_PROP_IMMUTABLE, csnumarr[i], &err); 342 } 343 344 nvlist_free(pfmri); 345 346 return (nerr); 347 } 348 349 static int 350 amd_dimm_create(topo_mod_t *mod, tnode_t *pnode, const char *name, 351 nvlist_t *mc, nvlist_t *auth) 352 { 353 int i, err, nerr = 0; 354 nvpair_t *nvp; 355 tnode_t *dimmnode; 356 nvlist_t *fmri, *asru, **dimmarr = NULL; 357 uint64_t num; 358 uint_t ndimm; 359 360 if (nvlist_lookup_nvlist_array(mc, "dimmlist", &dimmarr, &ndimm) != 0) { 361 whinge(mod, NULL, "amd_dimm_create: dimmlist lookup failed\n"); 362 return (-1); 363 } 364 365 if (ndimm == 0) 366 return (0); /* no dimms present on this node */ 367 368 if (topo_node_range_create(mod, pnode, name, 0, MAX_DIMMNUM) < 0) { 369 whinge(mod, NULL, "amd_dimm_create: range create failed\n"); 370 return (-1); 371 } 372 373 for (i = 0; i < ndimm; i++) { 374 if (nvlist_lookup_uint64(dimmarr[i], "num", &num) != 0) { 375 whinge(mod, &nerr, "amd_dimm_create: dimm num property " 376 "missing\n"); 377 continue; 378 } 379 380 if (mkrsrc(mod, pnode, name, num, auth, &fmri) < 0) { 381 whinge(mod, &nerr, "amd_dimm_create: mkrsrc failed\n"); 382 continue; 383 } 384 385 if ((dimmnode = topo_node_bind(mod, pnode, name, num, fmri)) 386 == NULL) { 387 nvlist_free(fmri); 388 whinge(mod, &nerr, "amd_dimm_create: node bind " 389 "failed\n"); 390 continue; 391 } 392 393 if (topo_method_register(mod, dimmnode, dimm_methods) < 0) 394 whinge(mod, &nerr, "dimm_create: " 395 "topo_method_register failed"); 396 397 /* 398 * Use the mem computation method directly to publish the asru 399 * in the "mem" scheme. 400 */ 401 if (mem_asru_create(mod, fmri, &asru) == 0) { 402 (void) topo_node_asru_set(dimmnode, asru, 0, &err); 403 nvlist_free(asru); 404 } else { 405 406 nvlist_free(fmri); 407 whinge(mod, &nerr, "amd_dimm_create: " 408 "mem_asru_create failed\n"); 409 continue; 410 } 411 412 (void) topo_node_fru_set(dimmnode, fmri, 0, &err); 413 414 nvlist_free(fmri); 415 416 (void) topo_pgroup_create(dimmnode, &dimm_pgroup, &err); 417 418 for (nvp = nvlist_next_nvpair(dimmarr[i], NULL); nvp != NULL; 419 nvp = nvlist_next_nvpair(dimmarr[i], nvp)) { 420 if (nvpair_type(nvp) == DATA_TYPE_UINT64_ARRAY && 421 strcmp(nvpair_name(nvp), "csnums") == 0 || 422 nvpair_type(nvp) == DATA_TYPE_STRING_ARRAY && 423 strcmp(nvpair_name(nvp), "csnames") == 0) 424 continue; /* used in amd_rank_create() */ 425 426 nerr += nvprop_add(mod, nvp, PGNAME(DIMM), dimmnode); 427 } 428 429 nerr += amd_rank_create(mod, dimmnode, dimmarr[i], auth); 430 } 431 432 return (nerr == 0 ? 0 : -1); 433 } 434 435 static int 436 amd_cs_create(topo_mod_t *mod, tnode_t *pnode, const char *name, nvlist_t *mc, 437 nvlist_t *auth) 438 { 439 int i, err, nerr = 0; 440 nvpair_t *nvp; 441 tnode_t *csnode; 442 nvlist_t *fmri, **csarr = NULL; 443 uint64_t csnum; 444 uint_t ncs; 445 446 if (nvlist_lookup_nvlist_array(mc, "cslist", &csarr, &ncs) != 0) 447 return (-1); 448 449 if (ncs == 0) 450 return (0); /* no chip-selects configured on this node */ 451 452 if (topo_node_range_create(mod, pnode, name, 0, MAX_CSNUM) < 0) 453 return (-1); 454 455 for (i = 0; i < ncs; i++) { 456 if (nvlist_lookup_uint64(csarr[i], "num", &csnum) != 0) { 457 whinge(mod, &nerr, "amd_cs_create: cs num property " 458 "missing\n"); 459 continue; 460 } 461 462 if (mkrsrc(mod, pnode, name, csnum, auth, &fmri) != 0) { 463 whinge(mod, &nerr, "amd_cs_create: mkrsrc failed\n"); 464 continue; 465 } 466 467 if ((csnode = topo_node_bind(mod, pnode, name, csnum, fmri)) 468 == NULL) { 469 nvlist_free(fmri); 470 whinge(mod, &nerr, "amd_cs_create: node bind failed\n"); 471 continue; 472 } 473 474 cs_fmri[csnum] = fmri; /* nvlist will be freed in mc_create */ 475 476 (void) topo_node_asru_set(csnode, fmri, 0, &err); 477 478 (void) topo_pgroup_create(csnode, &cs_pgroup, &err); 479 480 for (nvp = nvlist_next_nvpair(csarr[i], NULL); nvp != NULL; 481 nvp = nvlist_next_nvpair(csarr[i], nvp)) { 482 nerr += nvprop_add(mod, nvp, PGNAME(CS), csnode); 483 } 484 } 485 486 return (nerr == 0 ? 0 : -1); 487 } 488 489 static int 490 amd_dramchan_create(topo_mod_t *mod, tnode_t *pnode, const char *name, 491 nvlist_t *auth) 492 { 493 tnode_t *chnode; 494 nvlist_t *fmri; 495 char *socket; 496 int i, nchan; 497 int err, nerr = 0; 498 499 /* 500 * We will enumerate the number of channels present even if only 501 * channel A is in use (i.e., running in 64-bit mode). Only 502 * the socket 754 package has a single channel. 503 */ 504 if (topo_prop_get_string(pnode, PGNAME(MCT), "socket", 505 &socket, &err) == 0 && strcmp(socket, "Socket 754") == 0) 506 nchan = 1; 507 else 508 nchan = 2; 509 510 topo_mod_strfree(mod, socket); 511 512 if (topo_node_range_create(mod, pnode, name, 0, nchan - 1) < 0) 513 return (-1); 514 515 for (i = 0; i < nchan; i++) { 516 if (mkrsrc(mod, pnode, name, i, auth, &fmri) != 0) { 517 whinge(mod, &nerr, "amd_dramchan_create: mkrsrc " 518 "failed\n"); 519 continue; 520 } 521 522 if ((chnode = topo_node_bind(mod, pnode, name, i, fmri)) 523 == NULL) { 524 nvlist_free(fmri); 525 whinge(mod, &nerr, "amd_dramchan_create: node bind " 526 "failed\n"); 527 continue; 528 } 529 530 nvlist_free(fmri); 531 532 (void) topo_pgroup_create(chnode, &chan_pgroup, &err); 533 534 (void) topo_prop_set_string(chnode, PGNAME(CHAN), "channel", 535 TOPO_PROP_IMMUTABLE, i == 0 ? "A" : "B", &err); 536 } 537 538 return (nerr == 0 ? 0 : -1); 539 } 540 541 static int 542 amd_htconfig(topo_mod_t *mod, tnode_t *cnode, nvlist_t *htnvl) 543 { 544 nvpair_t *nvp; 545 int nerr = 0; 546 547 if (strcmp(topo_node_name(cnode), CHIP_NODE_NAME) != 0) { 548 whinge(mod, &nerr, "amd_htconfig: must pass a chip node!"); 549 return (-1); 550 } 551 552 for (nvp = nvlist_next_nvpair(htnvl, NULL); nvp != NULL; 553 nvp = nvlist_next_nvpair(htnvl, nvp)) { 554 if (nvprop_add(mod, nvp, PGNAME(CHIP), cnode) != 0) 555 nerr++; 556 } 557 558 return (nerr == 0 ? 0 : -1); 559 } 560 561 void 562 amd_mc_create(topo_mod_t *mod, tnode_t *pnode, const char *name, nvlist_t *auth, 563 int family, int model, int stepping, int *nerrp) 564 { 565 tnode_t *mcnode; 566 nvlist_t *fmri; 567 nvpair_t *nvp; 568 nvlist_t *mc = NULL; 569 int i; 570 571 /* 572 * Return with no error for anything before AMD family 0xf - we 573 * won't generate even a generic memory topolofy for earlier 574 * families. 575 */ 576 if (family < 0xf) 577 return; 578 579 if (mkrsrc(mod, pnode, name, 0, auth, &fmri) != 0) { 580 whinge(mod, nerrp, "mc_create: mkrsrc failed\n"); 581 return; 582 } 583 584 if (topo_node_range_create(mod, pnode, name, 0, 0) < 0) { 585 nvlist_free(fmri); 586 whinge(mod, nerrp, "mc_create: node range create failed\n"); 587 return; 588 } 589 590 if ((mcnode = topo_node_bind(mod, pnode, name, 0, 591 fmri)) == NULL) { 592 nvlist_free(mc); 593 topo_node_range_destroy(pnode, name); 594 nvlist_free(fmri); 595 whinge(mod, nerrp, "mc_create: mc bind failed\n"); 596 return; 597 } 598 (void) topo_node_fru_set(mcnode, NULL, 0, nerrp); 599 nvlist_free(fmri); 600 601 if ((mc = amd_lookup_by_mcid(mod, topo_node_instance(pnode))) == NULL) { 602 /* 603 * If a memory-controller driver exists for this chip model 604 * it has not attached or has otherwise malfunctioned; 605 * alternatively no memory-controller driver exists for this 606 * (presumably newly-released) cpu model. We fallback to 607 * creating a generic maximal topology. 608 */ 609 if (amd_generic_mc_create(mod, pnode, mcnode, 610 family, model, stepping, auth) != 0) 611 ++*nerrp; 612 return; 613 } 614 615 /* 616 * Add memory controller properties 617 */ 618 (void) topo_pgroup_create(mcnode, &mc_pgroup, nerrp); 619 620 for (nvp = nvlist_next_nvpair(mc, NULL); nvp != NULL; 621 nvp = nvlist_next_nvpair(mc, nvp)) { 622 char *name = nvpair_name(nvp); 623 data_type_t type = nvpair_type(nvp); 624 625 if (type == DATA_TYPE_NVLIST_ARRAY && 626 (strcmp(name, "cslist") == 0 || 627 strcmp(name, "dimmlist") == 0)) { 628 continue; 629 } else if (type == DATA_TYPE_UINT8 && 630 strcmp(name, MC_NVLIST_VERSTR) == 0) { 631 continue; 632 } else if (type == DATA_TYPE_NVLIST && 633 strcmp(name, "htconfig") == 0) { 634 nvlist_t *htnvl; 635 636 (void) nvpair_value_nvlist(nvp, &htnvl); 637 if (amd_htconfig(mod, pnode, htnvl) != 0) 638 ++*nerrp; 639 } else { 640 if (nvprop_add(mod, nvp, PGNAME(MCT), mcnode) != 0) 641 ++*nerrp; 642 } 643 } 644 645 if (amd_dramchan_create(mod, mcnode, CHAN_NODE_NAME, auth) != 0 || 646 amd_cs_create(mod, mcnode, CS_NODE_NAME, mc, auth) != 0 || 647 amd_dimm_create(mod, mcnode, DIMM_NODE_NAME, mc, auth) != 0) 648 ++*nerrp; 649 650 /* 651 * Free the fmris for the chip-selects allocated in amd_cs_create 652 */ 653 for (i = 0; i < MC_CHIP_NCS; i++) { 654 if (cs_fmri[i] != NULL) { 655 nvlist_free(cs_fmri[i]); 656 cs_fmri[i] = NULL; 657 } 658 } 659 660 nvlist_free(mc); 661 } 662