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 #include <unistd.h>
  27 #include <stdio.h>
  28 #include <stdlib.h>
  29 #include <stdarg.h>
  30 #include <string.h>
  31 #include <strings.h>
  32 #include <limits.h>
  33 #include <alloca.h>
  34 #include <kstat.h>
  35 #include <fcntl.h>
  36 #include <errno.h>
  37 #include <libnvpair.h>
  38 #include <sys/types.h>
  39 #include <sys/bitmap.h>
  40 #include <sys/processor.h>
  41 #include <sys/param.h>
  42 #include <sys/fm/protocol.h>
  43 #include <sys/systeminfo.h>
  44 #include <sys/mc.h>
  45 #include <sys/mc_amd.h>
  46 #include <sys/mc_intel.h>
  47 #include <fm/topo_mod.h>
  48 
  49 #include "chip.h"
  50 
  51 #ifndef MAX
  52 #define MAX(a, b)       ((a) > (b) ? (a) : (b))
  53 #endif
  54 
  55 static const topo_pgroup_info_t dimm_channel_pgroup =
  56         { PGNAME(CHAN), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
  57 static const topo_pgroup_info_t dimm_pgroup =
  58         { PGNAME(DIMM), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
  59 static const topo_pgroup_info_t rank_pgroup =
  60         { PGNAME(RANK), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
  61 static const topo_pgroup_info_t mc_pgroup =
  62         { PGNAME(MCT), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
  63 
  64 static const topo_method_t dimm_methods[] = {
  65         { SIMPLE_DIMM_LBL, "Property method", 0, TOPO_STABILITY_INTERNAL,
  66             simple_dimm_label},
  67         { SIMPLE_DIMM_LBL_MP, "Property method", 0, TOPO_STABILITY_INTERNAL,
  68             simple_dimm_label_mp},
  69         { SEQ_DIMM_LBL, "Property method", 0, TOPO_STABILITY_INTERNAL,
  70             seq_dimm_label},
  71         { NULL }
  72 };
  73 
  74 extern const topo_method_t rank_methods[];
  75 extern const topo_method_t ntv_page_retire_methods[];
  76 
  77 static int mc_fd;
  78 
  79 int
  80 mc_offchip_open()
  81 {
  82         mc_fd = open("/dev/mc/mc", O_RDONLY);
  83         return (mc_fd != -1);
  84 }
  85 
  86 static int
  87 mc_onchip(topo_instance_t id)
  88 {
  89         char path[64];
  90 
  91         (void) snprintf(path, sizeof (path), "/dev/mc/mc%d", id);
  92         mc_fd = open(path, O_RDONLY);
  93         return (mc_fd != -1);
  94 }
  95 
  96 static void
  97 mc_add_ranks(topo_mod_t *mod, tnode_t *dnode, nvlist_t *auth, int dimm,
  98     nvlist_t **ranks_nvp, int start_rank, int nranks, char *serial, char *part,
  99     char *rev, int maxranks)
 100 {
 101         int i;
 102         int rank;
 103         tnode_t *rnode;
 104         nvpair_t *nvp;
 105         nvlist_t *fmri;
 106         int err = 0;
 107 
 108         /*
 109          * If start_rank is defined, it is assigned to the first rank of this
 110          * dimm.
 111          */
 112         rank = start_rank >= 0 ? start_rank : dimm * maxranks;
 113         if (topo_node_range_create(mod, dnode, RANK, rank,
 114             rank + nranks - 1) < 0) {
 115                 whinge(mod, NULL, "mc_add_ranks: node range create failed"
 116                     " for rank\n");
 117                 return;
 118         }
 119         for (i = 0; i < nranks; i++) {
 120                 fmri = topo_mod_hcfmri(mod, dnode, FM_HC_SCHEME_VERSION,
 121                     RANK, rank, NULL, auth, part, rev, serial);
 122                 if (fmri == NULL) {
 123                         whinge(mod, NULL,
 124                             "mc_add_ranks: topo_mod_hcfmri failed\n");
 125                         return;
 126                 }
 127                 if ((rnode = topo_node_bind(mod, dnode, RANK, rank,
 128                     fmri)) == NULL) {
 129                         nvlist_free(fmri);
 130                         whinge(mod, NULL, "mc_add_ranks: node bind failed"
 131                             " for ranks\n");
 132                         return;
 133                 }
 134                 (void) topo_node_fru_set(rnode, NULL, 0, &err);
 135 
 136                 if (topo_method_register(mod, rnode, rank_methods) < 0)
 137                         whinge(mod, &err, "rank_create: "
 138                             "topo_method_register failed");
 139 
 140                 if (! is_xpv() && topo_method_register(mod, rnode,
 141                     ntv_page_retire_methods) < 0)
 142                         whinge(mod, &err, "mc_add_ranks: "
 143                             "topo_method_register failed");
 144 
 145                 (void) topo_node_asru_set(rnode, fmri, TOPO_ASRU_COMPUTE, &err);
 146 
 147                 if (FM_AWARE_SMBIOS(mod))
 148                         (void) topo_node_label_set(rnode, NULL, &err);
 149 
 150                 nvlist_free(fmri);
 151 
 152                 (void) topo_pgroup_create(rnode, &rank_pgroup, &err);
 153                 for (nvp = nvlist_next_nvpair(ranks_nvp[i], NULL); nvp != NULL;
 154                     nvp = nvlist_next_nvpair(ranks_nvp[i], nvp)) {
 155                         (void) nvprop_add(mod, nvp, PGNAME(RANK), rnode);
 156                 }
 157                 rank++;
 158         }
 159 }
 160 
 161 static void
 162 mc_add_dimms(topo_mod_t *mod, uint16_t chip_smbid, tnode_t *pnode,
 163     nvlist_t *auth, nvlist_t **nvl, uint_t ndimms, int maxdimms, int maxranks)
 164 {
 165         int i;
 166         nvlist_t *fmri;
 167         tnode_t *dnode;
 168         nvpair_t *nvp;
 169         int err;
 170         nvlist_t **ranks_nvp;
 171         int32_t start_rank = -1;
 172         uint_t nranks = 0;
 173         uint32_t dimm_number;
 174         char *serial = NULL;
 175         char *part = NULL;
 176         char *rev = NULL;
 177         char *label = NULL;
 178         char *name;
 179         id_t smbid;
 180 
 181         if (topo_node_range_create(mod, pnode, DIMM, 0,
 182             maxdimms ? maxdimms-1 : ndimms-1) < 0) {
 183                 whinge(mod, NULL,
 184                     "mc_add_dimms: node range create failed\n");
 185                 return;
 186         }
 187         for (i = 0; i < ndimms; i++) {
 188                 dimm_number = i;
 189                 for (nvp = nvlist_next_nvpair(nvl[i], NULL); nvp != NULL;
 190                     nvp = nvlist_next_nvpair(nvl[i], nvp)) {
 191                         name = nvpair_name(nvp);
 192                         if (strcmp(name, MCINTEL_NVLIST_RANKS) == 0) {
 193                                 (void) nvpair_value_nvlist_array(nvp,
 194                                     &ranks_nvp, &nranks);
 195                         } else if (strcmp(name, MCINTEL_NVLIST_1ST_RANK) == 0) {
 196                                 (void) nvpair_value_int32(nvp, &start_rank);
 197                         } else if (strcmp(name, FM_FMRI_HC_SERIAL_ID) == 0) {
 198                                 (void) nvpair_value_string(nvp, &serial);
 199                         } else if (strcmp(name, FM_FMRI_HC_PART) == 0) {
 200                                 (void) nvpair_value_string(nvp, &part);
 201                         } else if (strcmp(name, FM_FMRI_HC_REVISION) == 0) {
 202                                 (void) nvpair_value_string(nvp, &rev);
 203                         } else if (strcmp(name, FM_FAULT_FRU_LABEL) == 0) {
 204                                 (void) nvpair_value_string(nvp, &label);
 205                         } else if (strcmp(name, MCINTEL_NVLIST_DIMM_NUM) == 0) {
 206                                 (void) nvpair_value_uint32(nvp, &dimm_number);
 207                         }
 208                 }
 209                 fmri = NULL;
 210 
 211                 if (FM_AWARE_SMBIOS(mod)) {
 212                         int channum;
 213 
 214                         channum = topo_node_instance(pnode);
 215                         smbid = memnode_to_smbiosid(mod, chip_smbid,
 216                             DIMM_NODE_NAME, i, &channum);
 217                         if (serial == NULL)
 218                                 serial = (char *)chip_serial_smbios_get(mod,
 219                                     smbid);
 220                         if (part == NULL)
 221                                 part = (char *)chip_part_smbios_get(mod,
 222                                     smbid);
 223                         if (rev == NULL)
 224                                 rev = (char *)chip_rev_smbios_get(mod,
 225                                     smbid);
 226                 }
 227 
 228                 fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION,
 229                     DIMM, dimm_number, NULL, auth, part, rev, serial);
 230                 if (fmri == NULL) {
 231                         whinge(mod, NULL,
 232                             "mc_add_dimms: topo_mod_hcfmri failed\n");
 233                         return;
 234                 }
 235                 if ((dnode = topo_node_bind(mod, pnode, DIMM, dimm_number,
 236                     fmri)) == NULL) {
 237                         nvlist_free(fmri);
 238                         whinge(mod, NULL, "mc_add_dimms: node bind failed"
 239                             " for dimm\n");
 240                         return;
 241                 }
 242 
 243                 if (!FM_AWARE_SMBIOS(mod))
 244                         if (topo_method_register(mod, dnode, dimm_methods) < 0)
 245                                 whinge(mod, NULL, "mc_add_dimms: "
 246                                     "topo_method_register failed");
 247 
 248                 (void) topo_pgroup_create(dnode, &dimm_pgroup, &err);
 249 
 250                 for (nvp = nvlist_next_nvpair(nvl[i], NULL); nvp != NULL;
 251                     nvp = nvlist_next_nvpair(nvl[i], nvp)) {
 252                         name = nvpair_name(nvp);
 253                         if (strcmp(name, MCINTEL_NVLIST_RANKS) != 0 &&
 254                             strcmp(name, FM_FAULT_FRU_LABEL) != 0 &&
 255                             strcmp(name, MCINTEL_NVLIST_1ST_RANK) != 0) {
 256                                 (void) nvprop_add(mod, nvp, PGNAME(DIMM),
 257                                     dnode);
 258                         }
 259                 }
 260 
 261                 if (FM_AWARE_SMBIOS(mod)) {
 262                         nvlist_free(fmri);
 263                         (void) topo_node_resource(dnode, &fmri, &err);
 264                         /*
 265                          * We will use a full absolute parent/child label
 266                          */
 267                         label = (char *)chip_label_smbios_get(mod,
 268                             pnode, smbid, label);
 269                 }
 270 
 271                 (void) topo_node_label_set(dnode, label, &err);
 272 
 273                 if (FM_AWARE_SMBIOS(mod))
 274                         topo_mod_strfree(mod, label);
 275 
 276                 (void) topo_node_fru_set(dnode, fmri, 0, &err);
 277                 (void) topo_node_asru_set(dnode, fmri, 0, &err);
 278                 nvlist_free(fmri);
 279 
 280                 if (nranks) {
 281                         mc_add_ranks(mod, dnode, auth, dimm_number, ranks_nvp,
 282                             start_rank, nranks, serial, part, rev, maxranks);
 283                 }
 284         }
 285 }
 286 
 287 static int
 288 mc_add_channel(topo_mod_t *mod, uint16_t chip_smbid, tnode_t *pnode,
 289     int channel, nvlist_t *auth, nvlist_t *nvl, int maxdimms, int maxranks)
 290 {
 291         tnode_t *mc_channel;
 292         nvlist_t *fmri;
 293         nvlist_t **dimm_nvl;
 294         nvpair_t *nvp;
 295         char *name;
 296         uint_t ndimms;
 297         int err;
 298 
 299         if (mkrsrc(mod, pnode, DRAMCHANNEL, channel, auth, &fmri) != 0) {
 300                 whinge(mod, NULL, "mc_add_channel: mkrsrc failed\n");
 301                 return (-1);
 302         }
 303         if ((mc_channel = topo_node_bind(mod, pnode, DRAMCHANNEL, channel,
 304             fmri)) == NULL) {
 305                 whinge(mod, NULL, "mc_add_channel: node bind failed for %s\n",
 306                     DRAMCHANNEL);
 307                 nvlist_free(fmri);
 308                 return (-1);
 309         }
 310         (void) topo_node_fru_set(mc_channel, NULL, 0, &err);
 311         nvlist_free(fmri);
 312         (void) topo_pgroup_create(mc_channel, &dimm_channel_pgroup, &err);
 313 
 314         if (FM_AWARE_SMBIOS(mod))
 315                 (void) topo_node_label_set(mc_channel, NULL, &err);
 316 
 317         if (nvlist_lookup_nvlist_array(nvl, MCINTEL_NVLIST_DIMMS, &dimm_nvl,
 318             &ndimms) == 0) {
 319                 mc_add_dimms(mod, chip_smbid, mc_channel, auth, dimm_nvl,
 320                     ndimms, maxdimms, maxranks);
 321         }
 322         for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL;
 323             nvp = nvlist_next_nvpair(nvl, nvp)) {
 324                 name = nvpair_name(nvp);
 325                 if (strcmp(name, MCINTEL_NVLIST_DIMMS) != 0) {
 326                         (void) nvprop_add(mod, nvp, PGNAME(CHAN),
 327                             mc_channel);
 328                 }
 329         }
 330 
 331         return (0);
 332 }
 333 
 334 static int
 335 mc_nb_create(topo_mod_t *mod, uint16_t chip_smbid, tnode_t *pnode,
 336     const char *name, nvlist_t *auth, nvlist_t *nvl)
 337 {
 338         int err;
 339         int i, j;
 340         int channel;
 341         uint8_t nmc;
 342         uint8_t maxranks;
 343         uint8_t maxdimms;
 344         tnode_t *mcnode;
 345         nvlist_t *fmri;
 346         nvlist_t **channel_nvl;
 347         nvpair_t *nvp;
 348         char *pname;
 349         uint_t nchannels;
 350 
 351         if (nvlist_lookup_nvlist_array(nvl, MCINTEL_NVLIST_MC, &channel_nvl,
 352             &nchannels) != 0) {
 353                 whinge(mod, NULL,
 354                     "mc_nb_create: failed to find channel information\n");
 355                 return (-1);
 356         }
 357         if (nvlist_lookup_uint8(nvl, MCINTEL_NVLIST_NMEM, &nmc) == 0) {
 358                 /*
 359                  * Assume channels are evenly divided among the controllers.
 360                  * Convert nchannels to channels per controller
 361                  */
 362                 nchannels = nchannels / nmc;
 363         } else {
 364                 /*
 365                  * if number of memory controllers is not specified then there
 366                  * are two channels per controller and the nchannels is total
 367                  * we will set up nmc as number of controllers and convert
 368                  * nchannels to channels per controller
 369                  */
 370                 nmc = nchannels / 2;
 371                 nchannels = nchannels / nmc;
 372         }
 373         if (nvlist_lookup_uint8(nvl, MCINTEL_NVLIST_NRANKS, &maxranks) != 0)
 374                 maxranks = 2;
 375         if (nvlist_lookup_uint8(nvl, MCINTEL_NVLIST_NDIMMS, &maxdimms) != 0)
 376                 maxdimms = 0;
 377         if (topo_node_range_create(mod, pnode, name, 0, nmc-1) < 0) {
 378                 whinge(mod, NULL,
 379                     "mc_nb_create: node range create failed\n");
 380                 return (-1);
 381         }
 382         channel = 0;
 383         for (i = 0; i < nmc; i++) {
 384                 if (mkrsrc(mod, pnode, name, i, auth, &fmri) != 0) {
 385                         whinge(mod, NULL, "mc_nb_create: mkrsrc failed\n");
 386                         return (-1);
 387                 }
 388                 if ((mcnode = topo_node_bind(mod, pnode, name, i,
 389                     fmri)) == NULL) {
 390                         whinge(mod, NULL, "mc_nb_create: node bind failed"
 391                             " for memory-controller\n");
 392                         nvlist_free(fmri);
 393                         return (-1);
 394                 }
 395 
 396                 (void) topo_node_fru_set(mcnode, NULL, 0, &err);
 397                 nvlist_free(fmri);
 398                 (void) topo_pgroup_create(mcnode, &mc_pgroup, &err);
 399 
 400                 if (FM_AWARE_SMBIOS(mod))
 401                         (void) topo_node_label_set(mcnode, NULL, &err);
 402 
 403                 if (topo_node_range_create(mod, mcnode, DRAMCHANNEL, channel,
 404                     channel + nchannels - 1) < 0) {
 405                         whinge(mod, NULL,
 406                             "mc_nb_create: channel node range create failed\n");
 407                         return (-1);
 408                 }
 409                 for (j = 0; j < nchannels; j++) {
 410                         if (mc_add_channel(mod, chip_smbid, mcnode, channel,
 411                             auth, channel_nvl[channel], maxdimms,
 412                             maxranks) < 0) {
 413                                 return (-1);
 414                         }
 415                         channel++;
 416                 }
 417                 for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL;
 418                     nvp = nvlist_next_nvpair(nvl, nvp)) {
 419                         pname = nvpair_name(nvp);
 420                         if (strcmp(pname, MCINTEL_NVLIST_MC) != 0 &&
 421                             strcmp(pname, MCINTEL_NVLIST_NMEM) != 0 &&
 422                             strcmp(pname, MCINTEL_NVLIST_NRANKS) != 0 &&
 423                             strcmp(pname, MCINTEL_NVLIST_NDIMMS) != 0 &&
 424                             strcmp(pname, MCINTEL_NVLIST_VERSTR) != 0 &&
 425                             strcmp(pname, MCINTEL_NVLIST_MEM) != 0) {
 426                                 (void) nvprop_add(mod, nvp, PGNAME(MCT),
 427                                     mcnode);
 428                         }
 429                 }
 430         }
 431 
 432         return (NULL);
 433 }
 434 
 435 int
 436 mc_node_create(topo_mod_t *mod, uint16_t chip_smbid, tnode_t *pnode,
 437     const char *name, nvlist_t *auth)
 438 {
 439         mc_snapshot_info_t mcs;
 440         void *buf = NULL;
 441         nvlist_t *nvl;
 442         uint8_t ver;
 443         int rc;
 444 
 445         if (ioctl(mc_fd, MC_IOC_SNAPSHOT_INFO, &mcs) == -1 ||
 446             (buf = topo_mod_alloc(mod, mcs.mcs_size)) == NULL ||
 447             ioctl(mc_fd, MC_IOC_SNAPSHOT, buf) == -1) {
 448 
 449                 whinge(mod, NULL, "mc failed to snapshot %s\n",
 450                     strerror(errno));
 451 
 452                 free(buf);
 453                 (void) close(mc_fd);
 454                 return (NULL);
 455         }
 456         (void) close(mc_fd);
 457         (void) nvlist_unpack(buf, mcs.mcs_size, &nvl, 0);
 458         topo_mod_free(mod, buf, mcs.mcs_size);
 459 
 460         if (nvlist_lookup_uint8(nvl, MCINTEL_NVLIST_VERSTR, &ver) != 0) {
 461                 whinge(mod, NULL, "mc nvlist is not versioned\n");
 462                 nvlist_free(nvl);
 463                 return (NULL);
 464         } else if (ver != MCINTEL_NVLIST_VERS0) {
 465                 whinge(mod, NULL, "mc nvlist version mismatch\n");
 466                 nvlist_free(nvl);
 467                 return (NULL);
 468         }
 469 
 470         rc = mc_nb_create(mod, chip_smbid, pnode, name, auth, nvl);
 471 
 472         nvlist_free(nvl);
 473         return (rc);
 474 }
 475 
 476 void
 477 onchip_mc_create(topo_mod_t *mod, uint16_t chip_smbid, tnode_t *pnode,
 478     const char *name, nvlist_t *auth)
 479 {
 480         if (mc_onchip(topo_node_instance(pnode)))
 481                 (void) mc_node_create(mod, chip_smbid, pnode, name, auth);
 482 }
 483 
 484 int
 485 mc_offchip_create(topo_mod_t *mod, tnode_t *pnode, const char *name,
 486     nvlist_t *auth)
 487 {
 488         return (mc_node_create(mod, IGNORE_ID, pnode, name, auth));
 489 }