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         if (pfmri)
 692                 nvlist_free(pfmri);
 693 
 694         return (nerr == 0 ? 0 : -1);
 695 }
 696 
 697 static int
 698 amd_htconfig(topo_mod_t *mod, tnode_t *cnode, nvlist_t *htnvl)
 699 {
 700         nvpair_t *nvp;
 701         int nerr = 0;
 702 
 703         if (strcmp(topo_node_name(cnode), CHIP_NODE_NAME) != 0) {
 704                 whinge(mod, &nerr, "amd_htconfig: must pass a chip node!");
 705                 return (-1);
 706         }
 707 
 708         for (nvp = nvlist_next_nvpair(htnvl, NULL); nvp != NULL;
 709             nvp = nvlist_next_nvpair(htnvl, nvp)) {
 710                 if (nvprop_add(mod, nvp, PGNAME(CHIP), cnode) != 0)
 711                         nerr++;
 712         }
 713 
 714         return (nerr == 0 ? 0 : -1);
 715 }
 716 
 717 void
 718 amd_mc_create(topo_mod_t *mod,  uint16_t smbid, tnode_t *pnode,
 719     const char *name, nvlist_t *auth, int32_t procnodeid,
 720     int32_t procnodes_per_pkg, int family,
 721     int model, int *nerrp)
 722 {
 723         tnode_t *mcnode;
 724         nvlist_t *rfmri, *fmri;
 725         nvpair_t *nvp;
 726         nvlist_t *mc = NULL;
 727         int i, err;
 728         int mcnum = procnodeid % procnodes_per_pkg;
 729         char *serial = NULL;
 730         char *part = NULL;
 731         char *rev = NULL;
 732 
 733         /*
 734          * Return with no error for anything before AMD family 0xf - we
 735          * won't generate even a generic memory topology for earlier
 736          * families.
 737          */
 738         if (family < 0xf)
 739                 return;
 740 
 741         if (topo_node_lookup(pnode, name, mcnum) != NULL)
 742                 return;
 743 
 744         if (FM_AWARE_SMBIOS(mod)) {
 745                 (void) topo_node_resource(pnode, &rfmri, &err);
 746                 (void) nvlist_lookup_string(rfmri, "serial", &serial);
 747                 (void) nvlist_lookup_string(rfmri, "part", &part);
 748                 (void) nvlist_lookup_string(rfmri, "revision", &rev);
 749         }
 750 
 751         if (mkrsrc(mod, pnode, name, mcnum, auth, &fmri) != 0) {
 752                 if (FM_AWARE_SMBIOS(mod))
 753                         nvlist_free(rfmri);
 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                 nvlist_free(rfmri);
 763         }
 764 
 765         if ((mcnode = topo_node_bind(mod, pnode, name, mcnum,
 766             fmri)) == NULL) {
 767                 nvlist_free(fmri);
 768                 whinge(mod, nerrp, "mc_create: mc bind failed\n");
 769                 return;
 770         }
 771         if (topo_node_fru_set(mcnode, NULL, 0, &err) < 0)
 772                 whinge(mod, nerrp, "mc_create: topo_node_fru_set failed\n");
 773 
 774         if (FM_AWARE_SMBIOS(mod)) {
 775                 if (topo_node_label_set(mcnode, NULL, &err) == -1)
 776                         topo_mod_dprintf(mod, "Failed to set label\n");
 777         }
 778 
 779         nvlist_free(fmri);
 780 
 781         if (topo_pgroup_create(mcnode, &mc_pgroup, &err) < 0)
 782                 whinge(mod, nerrp, "mc_create: topo_pgroup_create failed\n");
 783 
 784         if (topo_prop_set_int32(mcnode, PGNAME(MCT), MCT_PROCNODE_ID,
 785             TOPO_PROP_IMMUTABLE, procnodeid, nerrp) != 0)
 786                 whinge(mod, nerrp, "mc_create: topo_prop_set_int32 failed to"
 787                     "add node id\n");
 788 
 789         if ((mc = amd_lookup_by_mcid(mod, topo_node_instance(pnode))) == NULL) {
 790                 /*
 791                  * If a memory-controller driver exists for this chip model
 792                  * it has not attached or has otherwise malfunctioned;
 793                  * alternatively no memory-controller driver exists for this
 794                  * (presumably newly-released) cpu model.  We fallback to
 795                  * creating a generic maximal topology.
 796                  */
 797                 if (amd_generic_mc_create(mod, smbid, pnode, mcnode,
 798                     family, model, auth) != 0)
 799                         whinge(mod, nerrp,
 800                             "mc_create: amd_generic_mc_create failed\n");
 801                 return;
 802         }
 803 
 804         /*
 805          * Add memory controller properties
 806          */
 807         for (nvp = nvlist_next_nvpair(mc, NULL); nvp != NULL;
 808             nvp = nvlist_next_nvpair(mc, nvp)) {
 809                 char *name = nvpair_name(nvp);
 810                 data_type_t type = nvpair_type(nvp);
 811 
 812                 if (type == DATA_TYPE_NVLIST_ARRAY &&
 813                     (strcmp(name, "cslist") == 0 ||
 814                     strcmp(name, "dimmlist") == 0)) {
 815                         continue;
 816                 } else if (type == DATA_TYPE_UINT8 &&
 817                     strcmp(name, MC_NVLIST_VERSTR) == 0) {
 818                         continue;
 819                 } else if (type == DATA_TYPE_NVLIST &&
 820                     strcmp(name, "htconfig") == 0) {
 821                         nvlist_t *htnvl;
 822 
 823                         (void) nvpair_value_nvlist(nvp, &htnvl);
 824                         if (amd_htconfig(mod, pnode, htnvl) != 0)
 825                                 whinge(mod, nerrp,
 826                                     "mc_create: amd_htconfig failed\n");
 827                 } else {
 828                         if (nvprop_add(mod, nvp, PGNAME(MCT), mcnode) != 0)
 829                                 whinge(mod, nerrp,
 830                                     "mc_create: nvprop_add failed\n");
 831                 }
 832         }
 833 
 834         if (amd_dramchan_create(mod, mcnode, CHAN_NODE_NAME, auth) != 0 ||
 835             amd_cs_create(mod, mcnode, CS_NODE_NAME, mc, auth) != 0 ||
 836             amd_dimm_create(mod, smbid, mcnode, DIMM_NODE_NAME, mc, auth) != 0)
 837                 whinge(mod, nerrp, "mc_create: create children failed\n");
 838 
 839         /*
 840          * Free the fmris for the chip-selects allocated in amd_cs_create
 841          */
 842         for (i = 0; i < MC_CHIP_NCS; i++) {
 843                 if (cs_fmri[i] != NULL) {
 844                         nvlist_free(cs_fmri[i]);
 845                         cs_fmri[i] = NULL;
 846                 }
 847         }
 848 
 849         nvlist_free(mc);
 850 }