1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  * Copyright (c) 2011 Bayard G. Bell. All rights reserved.
  26  */
  27 
  28 #include <sys/types.h>
  29 #include <sys/time.h>
  30 #include <sys/nvpair.h>
  31 #include <sys/cmn_err.h>
  32 #include <sys/cred.h>
  33 #include <sys/open.h>
  34 #include <sys/ddi.h>
  35 #include <sys/sunddi.h>
  36 #include <sys/conf.h>
  37 #include <sys/modctl.h>
  38 #include <sys/cyclic.h>
  39 #include <sys/errorq.h>
  40 #include <sys/stat.h>
  41 #include <sys/cpuvar.h>
  42 #include <sys/mc_intel.h>
  43 #include <sys/mc.h>
  44 #include <sys/fm/protocol.h>
  45 #include "nb_log.h"
  46 #include "nb5000.h"
  47 
  48 nvlist_t *inb_mc_nvl;
  49 krwlock_t inb_mc_lock;
  50 
  51 char *inb_mc_snapshot;
  52 uint_t nb_config_gen;
  53 uint_t inb_mc_snapshotgen;
  54 size_t inb_mc_snapshotsz;
  55 static dev_info_t *inb_dip;
  56 int nb_allow_detach = 0;
  57 int nb_no_smbios;
  58 
  59 static uint64_t
  60 rank_to_base(uint8_t branch, uint8_t rank, uint8_t *interleave, uint64_t *limit,
  61     uint64_t *hole_base, uint64_t *hole_size, uint8_t *wayp,
  62     uint8_t *branch_interleavep)
  63 {
  64         uint8_t i, j;
  65         uint64_t base = 0;
  66         uint64_t lt = 0;
  67         uint64_t h = 0;
  68         uint64_t hs = 0;
  69         uint8_t il = 1;
  70         uint8_t way = 0;
  71         uint8_t branch_interleave = 0;
  72 
  73         for (i = 0; i < NB_MEM_RANK_SELECT; i++) {
  74                 for (j = 0; j < NB_RANKS_IN_SELECT; j++) {
  75                         if (nb_ranks[branch][i].rank[j] == rank) {
  76                                 base = nb_ranks[branch][i].base;
  77                                 lt = nb_ranks[branch][i].limit;
  78                                 il = nb_ranks[branch][i].interleave;
  79                                 h = nb_ranks[branch][i].hole_base;
  80                                 hs = nb_ranks[branch][i].hole_size;
  81                                 way = j;
  82                                 branch_interleave =
  83                                     nb_ranks[branch][i].branch_interleave;
  84                                 i = NB_MEM_RANK_SELECT;
  85                                 break;
  86                         }
  87                 }
  88         }
  89         if (lt == 0) {
  90                 for (i = 0; lt == 0 && i < NB_MEM_BRANCH_SELECT; i++) {
  91                         if (nb_banks[i].way[branch] &&
  92                             base >= nb_banks[i].base &&
  93                             base < nb_banks[i].base + nb_banks[i].limit) {
  94                                 lt = nb_banks[i].limit;
  95                                 break;
  96                         }
  97                 }
  98         }
  99         *interleave = il;
 100         *limit = lt;
 101         *hole_base = h;
 102         *hole_size = hs;
 103         *wayp = way;
 104         *branch_interleavep = branch_interleave;
 105         return (base);
 106 }
 107 
 108 /*ARGSUSED*/
 109 void
 110 inb_rank(nvlist_t *newdimm, nb_dimm_t *nb_dimm, uint8_t channel, uint32_t dimm)
 111 {
 112         nvlist_t **newrank;
 113         int i;
 114 
 115         newrank = kmem_zalloc(sizeof (nvlist_t *) * nb_dimm->nranks, KM_SLEEP);
 116         for (i = 0; i < nb_dimm->nranks; i++) {
 117                 uint64_t dimm_base;
 118                 uint64_t limit;
 119                 uint8_t interleave;
 120                 uint8_t way;
 121                 uint8_t branch_interleave;
 122                 uint64_t hole_base;
 123                 uint64_t hole_size;
 124 
 125                 dimm_base = rank_to_base(channel/nb_channels_per_branch,
 126                     nb_dimm->start_rank + i, &interleave,
 127                     &limit, &hole_base, &hole_size, &way, &branch_interleave);
 128                 (void) nvlist_alloc(&newrank[i], NV_UNIQUE_NAME, KM_SLEEP);
 129 
 130                 (void) nvlist_add_uint64(newrank[i], "dimm-rank-base",
 131                     dimm_base);
 132                 if (hole_size) {
 133                         (void) nvlist_add_uint64(newrank[i], "dimm-hole",
 134                             hole_base);
 135                         (void) nvlist_add_uint64(newrank[i], "dimm-hole-size",
 136                             hole_size);
 137                 }
 138                 (void) nvlist_add_uint64(newrank[i], "dimm-rank-limit",
 139                     limit);
 140                 if (interleave > 1) {
 141                         (void) nvlist_add_uint32(newrank[i],
 142                             "dimm-rank-interleave", (uint32_t)interleave);
 143                         (void) nvlist_add_uint32(newrank[i],
 144                             "dimm-rank-interleave-way", (uint32_t)way);
 145                         if (branch_interleave) {
 146                                 (void) nvlist_add_uint32(newrank[i],
 147                                     "dimm-rank-interleave-branch", (uint32_t)1);
 148                         }
 149                 }
 150         }
 151         (void) nvlist_add_nvlist_array(newdimm, MCINTEL_NVLIST_RANKS, newrank,
 152             nb_dimm->nranks);
 153         for (i = 0; i < nb_dimm->nranks; i++)
 154                 nvlist_free(newrank[i]);
 155         kmem_free(newrank, sizeof (nvlist_t *) * nb_dimm->nranks);
 156 }
 157 
 158 nvlist_t *
 159 inb_dimm(nb_dimm_t *nb_dimm, uint8_t channel, uint32_t dimm)
 160 {
 161         nvlist_t *newdimm;
 162         uint8_t t;
 163         char sbuf[65];
 164 
 165         (void) nvlist_alloc(&newdimm, NV_UNIQUE_NAME, KM_SLEEP);
 166         (void) nvlist_add_uint32(newdimm, "dimm-number", dimm);
 167 
 168         if (nb_dimm->dimm_size >= 1024*1024*1024) {
 169                 (void) snprintf(sbuf, sizeof (sbuf), "%dG",
 170                     (int)(nb_dimm->dimm_size / (1024*1024*1024)));
 171         } else {
 172                 (void) snprintf(sbuf, sizeof (sbuf), "%dM",
 173                     (int)(nb_dimm->dimm_size / (1024*1024)));
 174         }
 175         (void) nvlist_add_string(newdimm, "dimm-size", sbuf);
 176         (void) nvlist_add_uint64(newdimm, "size", nb_dimm->dimm_size);
 177         (void) nvlist_add_uint32(newdimm, "nbanks", (uint32_t)nb_dimm->nbanks);
 178         (void) nvlist_add_uint32(newdimm, "ncolumn",
 179             (uint32_t)nb_dimm->ncolumn);
 180         (void) nvlist_add_uint32(newdimm, "nrow", (uint32_t)nb_dimm->nrow);
 181         (void) nvlist_add_uint32(newdimm, "width", (uint32_t)nb_dimm->width);
 182         (void) nvlist_add_int32(newdimm, MCINTEL_NVLIST_1ST_RANK,
 183             (int32_t)nb_dimm->start_rank);
 184         (void) nvlist_add_uint32(newdimm, "ranks", (uint32_t)nb_dimm->nranks);
 185         inb_rank(newdimm, nb_dimm, channel, dimm);
 186         (void) nvlist_add_uint32(newdimm, "manufacture-id",
 187             (uint32_t)nb_dimm->manufacture_id);
 188         (void) nvlist_add_uint32(newdimm, "manufacture-location",
 189             (uint32_t)nb_dimm->manufacture_location);
 190         (void) nvlist_add_uint32(newdimm, "manufacture-week",
 191             (uint32_t)nb_dimm->manufacture_week);
 192         (void) nvlist_add_uint32(newdimm, "manufacture-year",
 193             (uint32_t)nb_dimm->manufacture_year + 2000);
 194         /* create Sun Serial number from SPD data */
 195         (void) snprintf(sbuf, sizeof (sbuf), "%04x%02x%02x%02x%08x",
 196             (uint32_t)nb_dimm->manufacture_id & 0x7fff,
 197             (uint32_t)nb_dimm->manufacture_location,
 198             (uint32_t)nb_dimm->manufacture_year,
 199             (uint32_t)nb_dimm->manufacture_week,
 200             nb_dimm->serial_number);
 201         (void) nvlist_add_string(newdimm, FM_FMRI_HC_SERIAL_ID, sbuf);
 202         if (nb_dimm->part_number && nb_dimm->part_number[0]) {
 203                 t = sizeof (nb_dimm->part_number);
 204                 (void) strncpy(sbuf, nb_dimm->part_number, t);
 205                 sbuf[t] = 0;
 206                 (void) nvlist_add_string(newdimm, FM_FMRI_HC_PART, sbuf);
 207         }
 208         if (nb_dimm->revision && nb_dimm->revision[0]) {
 209                 t = sizeof (nb_dimm->revision);
 210                 (void) strncpy(sbuf, nb_dimm->revision, t);
 211                 sbuf[t] = 0;
 212                 (void) nvlist_add_string(newdimm, FM_FMRI_HC_REVISION, sbuf);
 213         }
 214         t = sizeof (nb_dimm->label);
 215         (void) strncpy(sbuf, nb_dimm->label, t);
 216         sbuf[t] = 0;
 217         (void) nvlist_add_string(newdimm, FM_FAULT_FRU_LABEL, sbuf);
 218         return (newdimm);
 219 }
 220 
 221 static void
 222 inb_dimmlist(nvlist_t *nvl)
 223 {
 224         nvlist_t **dimmlist;
 225         nvlist_t **newchannel;
 226         int nchannels = nb_number_memory_controllers * nb_channels_per_branch;
 227         int nd;
 228         uint8_t i, j;
 229         nb_dimm_t **dimmpp;
 230         nb_dimm_t *dimmp;
 231 
 232         dimmlist =  kmem_zalloc(sizeof (nvlist_t *) * nb_dimms_per_channel,
 233             KM_SLEEP);
 234         newchannel = kmem_zalloc(sizeof (nvlist_t *) * nchannels, KM_SLEEP);
 235         dimmpp = nb_dimms;
 236         for (i = 0; i < nchannels; i++) {
 237                 (void) nvlist_alloc(&newchannel[i], NV_UNIQUE_NAME, KM_SLEEP);
 238                 nd = 0;
 239                 for (j = 0; j < nb_dimms_per_channel; j++) {
 240                         dimmp = *dimmpp;
 241                         if (dimmp != NULL) {
 242                                 dimmlist[nd] = inb_dimm(dimmp, i, (uint32_t)j);
 243                                 nd++;
 244                         }
 245                         dimmpp++;
 246                 }
 247                 if (nd) {
 248                         (void) nvlist_add_nvlist_array(newchannel[i],
 249                             "memory-dimms", dimmlist, nd);
 250                         for (j = 0; j < nd; j++)
 251                                 nvlist_free(dimmlist[j]);
 252                 }
 253         }
 254         (void) nvlist_add_nvlist_array(nvl, MCINTEL_NVLIST_MC, newchannel,
 255             nchannels);
 256         for (i = 0; i < nchannels; i++)
 257                 nvlist_free(newchannel[i]);
 258         kmem_free(dimmlist, sizeof (nvlist_t *) * nb_dimms_per_channel);
 259         kmem_free(newchannel, sizeof (nvlist_t *) * nchannels);
 260 }
 261 
 262 static char *
 263 inb_mc_name()
 264 {
 265         char *mc;
 266 
 267         switch (nb_chipset) {
 268         case INTEL_NB_7300:
 269                 mc = "Intel 7300";
 270                 break;
 271         case INTEL_NB_5400:
 272                 mc = "Intel 5400";
 273                 break;
 274         case INTEL_NB_5400A:
 275                 mc = "Intel 5400A";
 276                 break;
 277         case INTEL_NB_5400B:
 278                 mc = "Intel 5400B";
 279                 break;
 280         case INTEL_NB_5100:
 281                 mc = "Intel 5100";
 282                 break;
 283         case INTEL_NB_5000P:
 284                 mc = "Intel 5000P";
 285                 break;
 286         case INTEL_NB_5000V:
 287                 mc = "Intel 5000V";
 288                 break;
 289         case INTEL_NB_5000X:
 290                 mc = "Intel 5000X";
 291                 break;
 292         case INTEL_NB_5000Z:
 293                 mc = "Intel 5000Z";
 294                 break;
 295         default:
 296                 mc = "Intel 5000";
 297                 break;
 298         }
 299         return (mc);
 300 }
 301 
 302 static void
 303 inb_create_nvl()
 304 {
 305         nvlist_t *nvl;
 306 
 307         (void) nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP);
 308         (void) nvlist_add_uint8(nvl, MCINTEL_NVLIST_VERSTR,
 309             MCINTEL_NVLIST_VERS);
 310         (void) nvlist_add_string(nvl, "memory-controller", inb_mc_name());
 311         if (nb_chipset == INTEL_NB_5100)
 312                 (void) nvlist_add_uint8(nvl, MCINTEL_NVLIST_NMEM,
 313                     (uint8_t)nb_number_memory_controllers);
 314         inb_dimmlist(nvl);
 315 
 316         nvlist_free(inb_mc_nvl);
 317         inb_mc_nvl = nvl;
 318 }
 319 
 320 static void
 321 inb_mc_snapshot_destroy()
 322 {
 323         ASSERT(RW_LOCK_HELD(&inb_mc_lock));
 324 
 325         if (inb_mc_snapshot == NULL)
 326                 return;
 327 
 328         kmem_free(inb_mc_snapshot, inb_mc_snapshotsz);
 329         inb_mc_snapshot = NULL;
 330         inb_mc_snapshotsz = 0;
 331         inb_mc_snapshotgen++;
 332 }
 333 
 334 static int
 335 inb_mc_snapshot_update()
 336 {
 337         ASSERT(RW_LOCK_HELD(&inb_mc_lock));
 338 
 339         if (inb_mc_snapshot != NULL)
 340                 return (0);
 341 
 342         if (nvlist_pack(inb_mc_nvl, &inb_mc_snapshot, &inb_mc_snapshotsz,
 343             NV_ENCODE_XDR, KM_SLEEP) != 0)
 344                 return (-1);
 345 
 346         return (0);
 347 }
 348 
 349 /*ARGSUSED*/
 350 static int
 351 inb_mc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
 352     int *rvalp)
 353 {
 354         int rc = 0;
 355         mc_snapshot_info_t mcs;
 356 
 357         if (cmd != MC_IOC_SNAPSHOT_INFO && cmd != MC_IOC_SNAPSHOT)
 358                 return (EINVAL);
 359 
 360         rw_enter(&inb_mc_lock, RW_READER);
 361         if (inb_mc_nvl == NULL || inb_mc_snapshotgen != nb_config_gen) {
 362                 if (!rw_tryupgrade(&inb_mc_lock)) {
 363                         rw_exit(&inb_mc_lock);
 364                         return (EAGAIN);
 365                 }
 366                 if (inb_mc_nvl)
 367                         inb_mc_snapshot_destroy();
 368                 inb_create_nvl();
 369                 nb_config_gen = inb_mc_snapshotgen;
 370                 (void) inb_mc_snapshot_update();
 371         }
 372         switch (cmd) {
 373         case MC_IOC_SNAPSHOT_INFO:
 374                 mcs.mcs_size = (uint32_t)inb_mc_snapshotsz;
 375                 mcs.mcs_gen = inb_mc_snapshotgen;
 376 
 377                 if (ddi_copyout(&mcs, (void *)arg, sizeof (mc_snapshot_info_t),
 378                     mode) < 0)
 379                         rc = EFAULT;
 380                 break;
 381         case MC_IOC_SNAPSHOT:
 382                 if (ddi_copyout(inb_mc_snapshot, (void *)arg, inb_mc_snapshotsz,
 383                     mode) < 0)
 384                         rc = EFAULT;
 385                 break;
 386         }
 387         rw_exit(&inb_mc_lock);
 388         return (rc);
 389 }
 390 
 391 /*ARGSUSED*/
 392 static int
 393 inb_mc_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
 394     void **result)
 395 {
 396         if ((infocmd != DDI_INFO_DEVT2DEVINFO &&
 397             infocmd != DDI_INFO_DEVT2INSTANCE) || inb_dip == NULL) {
 398                 *result = NULL;
 399                 return (DDI_FAILURE);
 400         }
 401         if (infocmd == DDI_INFO_DEVT2DEVINFO)
 402                 *result = inb_dip;
 403         else
 404                 *result = (void *)(uintptr_t)ddi_get_instance(inb_dip);
 405         return (0);
 406 }
 407 
 408 static int
 409 inb_mc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
 410 {
 411         if (cmd == DDI_RESUME) {
 412                 nb_dev_reinit();
 413                 return (DDI_SUCCESS);
 414         }
 415         if (cmd != DDI_ATTACH)
 416                 return (DDI_FAILURE);
 417         if (inb_dip == NULL) {
 418                 inb_dip = dip;
 419                 nb_no_smbios = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
 420                     DDI_PROP_DONTPASS, "no-smbios", 0);
 421                 nb_pci_cfg_setup(dip);
 422                 (void) ddi_prop_update_string(DDI_DEV_T_NONE, dip, "model",
 423                     inb_mc_name());
 424                 if (nb_dev_init()) {
 425                         nb_pci_cfg_free();
 426                         inb_dip = NULL;
 427                         return (DDI_FAILURE);
 428                 }
 429                 if (ddi_create_minor_node(dip, "mc-intel", S_IFCHR, 0,
 430                     "ddi_mem_ctrl", 0) != DDI_SUCCESS) {
 431                         cmn_err(CE_WARN, "failed to create minor node"
 432                             " for memory controller\n");
 433                 }
 434                 cmi_hdl_walk(inb_mc_register, NULL, NULL, NULL);
 435         }
 436 
 437         return (DDI_SUCCESS);
 438 }
 439 
 440 /*ARGSUSED*/
 441 static int
 442 inb_mc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
 443 {
 444         if (nb_allow_detach && cmd == DDI_DETACH && dip == inb_dip) {
 445                 rw_enter(&inb_mc_lock, RW_WRITER);
 446                 inb_mc_snapshot_destroy();
 447                 rw_exit(&inb_mc_lock);
 448                 inb_dip = NULL;
 449                 return (DDI_SUCCESS);
 450         } else if (cmd == DDI_SUSPEND || cmd == DDI_PM_SUSPEND) {
 451                 return (DDI_SUCCESS);
 452         } else {
 453                 return (DDI_FAILURE);
 454         }
 455 }
 456 
 457 /*ARGSUSED*/
 458 static int
 459 inb_mc_open(dev_t *devp, int flag, int otyp, cred_t *credp)
 460 {
 461         if (otyp != OTYP_CHR)
 462                 return (EINVAL);
 463 
 464         rw_enter(&inb_mc_lock, RW_READER);
 465         if (getminor(*devp) >= 1) {
 466                 rw_exit(&inb_mc_lock);
 467                 return (EINVAL);
 468         }
 469         rw_exit(&inb_mc_lock);
 470 
 471         return (0);
 472 }
 473 
 474 /*ARGSUSED*/
 475 static int
 476 inb_mc_close(dev_t dev, int flag, int otyp, cred_t *credp)
 477 {
 478         return (0);
 479 }
 480 
 481 
 482 static struct cb_ops inb_mc_cb_ops = {
 483         inb_mc_open,
 484         inb_mc_close,
 485         nodev,          /* not a block driver */
 486         nodev,          /* no print routine */
 487         nodev,          /* no dump routine */
 488         nodev,          /* no read routine */
 489         nodev,          /* no write routine */
 490         inb_mc_ioctl,
 491         nodev,          /* no devmap routine */
 492         nodev,          /* no mmap routine */
 493         nodev,          /* no segmap routine */
 494         nochpoll,       /* no chpoll routine */
 495         ddi_prop_op,
 496         0,              /* not a STREAMS driver */
 497         D_NEW | D_MP,   /* safe for multi-thread/multi-processor */
 498 };
 499 
 500 static struct dev_ops inb_mc_ops = {
 501         DEVO_REV,               /* devo_rev */
 502         0,                      /* devo_refcnt */
 503         inb_mc_getinfo,         /* devo_getinfo */
 504         nulldev,                /* devo_identify */
 505         nulldev,                /* devo_probe */
 506         inb_mc_attach,          /* devo_attach */
 507         inb_mc_detach,          /* devo_detach */
 508         nodev,                  /* devo_reset */
 509         &inb_mc_cb_ops,             /* devo_cb_ops */
 510         NULL,                   /* devo_bus_ops */
 511         NULL,                   /* devo_power */
 512         ddi_quiesce_not_needed,         /* devo_quiesce */
 513 };
 514 
 515 static struct modldrv modldrv = {
 516         &mod_driverops,
 517         "Intel 5000 Memory Controller Hub Module",
 518         &inb_mc_ops
 519 };
 520 
 521 static struct modlinkage modlinkage = {
 522         MODREV_1,
 523         { (void *)&modldrv, NULL }
 524 };
 525 
 526 int
 527 _init(void)
 528 {
 529         int err;
 530 
 531         err = nb_init();
 532         if (err == 0 && (err = mod_install(&modlinkage)) == 0)
 533                 rw_init(&inb_mc_lock, NULL, RW_DRIVER, NULL);
 534 
 535         return (err);
 536 }
 537 
 538 int
 539 _info(struct modinfo *modinfop)
 540 {
 541         return (mod_info(&modlinkage, modinfop));
 542 }
 543 
 544 int
 545 _fini(void)
 546 {
 547         int err;
 548 
 549         if ((err = mod_remove(&modlinkage)) == 0) {
 550                 nb_unload();
 551                 rw_destroy(&inb_mc_lock);
 552         }
 553 
 554         return (err);
 555 }