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 if (inb_mc_nvl) 317 nvlist_free(inb_mc_nvl); 318 inb_mc_nvl = nvl; 319 } 320 321 static void 322 inb_mc_snapshot_destroy() 323 { 324 ASSERT(RW_LOCK_HELD(&inb_mc_lock)); 325 326 if (inb_mc_snapshot == NULL) 327 return; 328 329 kmem_free(inb_mc_snapshot, inb_mc_snapshotsz); 330 inb_mc_snapshot = NULL; 331 inb_mc_snapshotsz = 0; 332 inb_mc_snapshotgen++; 333 } 334 335 static int 336 inb_mc_snapshot_update() 337 { 338 ASSERT(RW_LOCK_HELD(&inb_mc_lock)); 339 340 if (inb_mc_snapshot != NULL) 341 return (0); 342 343 if (nvlist_pack(inb_mc_nvl, &inb_mc_snapshot, &inb_mc_snapshotsz, 344 NV_ENCODE_XDR, KM_SLEEP) != 0) 345 return (-1); 346 347 return (0); 348 } 349 350 /*ARGSUSED*/ 351 static int 352 inb_mc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, 353 int *rvalp) 354 { 355 int rc = 0; 356 mc_snapshot_info_t mcs; 357 358 if (cmd != MC_IOC_SNAPSHOT_INFO && cmd != MC_IOC_SNAPSHOT) 359 return (EINVAL); 360 361 rw_enter(&inb_mc_lock, RW_READER); 362 if (inb_mc_nvl == NULL || inb_mc_snapshotgen != nb_config_gen) { 363 if (!rw_tryupgrade(&inb_mc_lock)) { 364 rw_exit(&inb_mc_lock); 365 return (EAGAIN); 366 } 367 if (inb_mc_nvl) 368 inb_mc_snapshot_destroy(); 369 inb_create_nvl(); 370 nb_config_gen = inb_mc_snapshotgen; 371 (void) inb_mc_snapshot_update(); 372 } 373 switch (cmd) { 374 case MC_IOC_SNAPSHOT_INFO: 375 mcs.mcs_size = (uint32_t)inb_mc_snapshotsz; 376 mcs.mcs_gen = inb_mc_snapshotgen; 377 378 if (ddi_copyout(&mcs, (void *)arg, sizeof (mc_snapshot_info_t), 379 mode) < 0) 380 rc = EFAULT; 381 break; 382 case MC_IOC_SNAPSHOT: 383 if (ddi_copyout(inb_mc_snapshot, (void *)arg, inb_mc_snapshotsz, 384 mode) < 0) 385 rc = EFAULT; 386 break; 387 } 388 rw_exit(&inb_mc_lock); 389 return (rc); 390 } 391 392 /*ARGSUSED*/ 393 static int 394 inb_mc_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, 395 void **result) 396 { 397 if ((infocmd != DDI_INFO_DEVT2DEVINFO && 398 infocmd != DDI_INFO_DEVT2INSTANCE) || inb_dip == NULL) { 399 *result = NULL; 400 return (DDI_FAILURE); 401 } 402 if (infocmd == DDI_INFO_DEVT2DEVINFO) 403 *result = inb_dip; 404 else 405 *result = (void *)(uintptr_t)ddi_get_instance(inb_dip); 406 return (0); 407 } 408 409 static int 410 inb_mc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 411 { 412 if (cmd == DDI_RESUME) { 413 nb_dev_reinit(); 414 return (DDI_SUCCESS); 415 } 416 if (cmd != DDI_ATTACH) 417 return (DDI_FAILURE); 418 if (inb_dip == NULL) { 419 inb_dip = dip; 420 nb_no_smbios = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 421 DDI_PROP_DONTPASS, "no-smbios", 0); 422 nb_pci_cfg_setup(dip); 423 (void) ddi_prop_update_string(DDI_DEV_T_NONE, dip, "model", 424 inb_mc_name()); 425 if (nb_dev_init()) { 426 nb_pci_cfg_free(); 427 inb_dip = NULL; 428 return (DDI_FAILURE); 429 } 430 if (ddi_create_minor_node(dip, "mc-intel", S_IFCHR, 0, 431 "ddi_mem_ctrl", 0) != DDI_SUCCESS) { 432 cmn_err(CE_WARN, "failed to create minor node" 433 " for memory controller\n"); 434 } 435 cmi_hdl_walk(inb_mc_register, NULL, NULL, NULL); 436 } 437 438 return (DDI_SUCCESS); 439 } 440 441 /*ARGSUSED*/ 442 static int 443 inb_mc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 444 { 445 if (nb_allow_detach && cmd == DDI_DETACH && dip == inb_dip) { 446 rw_enter(&inb_mc_lock, RW_WRITER); 447 inb_mc_snapshot_destroy(); 448 rw_exit(&inb_mc_lock); 449 inb_dip = NULL; 450 return (DDI_SUCCESS); 451 } else if (cmd == DDI_SUSPEND || cmd == DDI_PM_SUSPEND) { 452 return (DDI_SUCCESS); 453 } else { 454 return (DDI_FAILURE); 455 } 456 } 457 458 /*ARGSUSED*/ 459 static int 460 inb_mc_open(dev_t *devp, int flag, int otyp, cred_t *credp) 461 { 462 if (otyp != OTYP_CHR) 463 return (EINVAL); 464 465 rw_enter(&inb_mc_lock, RW_READER); 466 if (getminor(*devp) >= 1) { 467 rw_exit(&inb_mc_lock); 468 return (EINVAL); 469 } 470 rw_exit(&inb_mc_lock); 471 472 return (0); 473 } 474 475 /*ARGSUSED*/ 476 static int 477 inb_mc_close(dev_t dev, int flag, int otyp, cred_t *credp) 478 { 479 return (0); 480 } 481 482 483 static struct cb_ops inb_mc_cb_ops = { 484 inb_mc_open, 485 inb_mc_close, 486 nodev, /* not a block driver */ 487 nodev, /* no print routine */ 488 nodev, /* no dump routine */ 489 nodev, /* no read routine */ 490 nodev, /* no write routine */ 491 inb_mc_ioctl, 492 nodev, /* no devmap routine */ 493 nodev, /* no mmap routine */ 494 nodev, /* no segmap routine */ 495 nochpoll, /* no chpoll routine */ 496 ddi_prop_op, 497 0, /* not a STREAMS driver */ 498 D_NEW | D_MP, /* safe for multi-thread/multi-processor */ 499 }; 500 501 static struct dev_ops inb_mc_ops = { 502 DEVO_REV, /* devo_rev */ 503 0, /* devo_refcnt */ 504 inb_mc_getinfo, /* devo_getinfo */ 505 nulldev, /* devo_identify */ 506 nulldev, /* devo_probe */ 507 inb_mc_attach, /* devo_attach */ 508 inb_mc_detach, /* devo_detach */ 509 nodev, /* devo_reset */ 510 &inb_mc_cb_ops, /* devo_cb_ops */ 511 NULL, /* devo_bus_ops */ 512 NULL, /* devo_power */ 513 ddi_quiesce_not_needed, /* devo_quiesce */ 514 }; 515 516 static struct modldrv modldrv = { 517 &mod_driverops, 518 "Intel 5000 Memory Controller Hub Module", 519 &inb_mc_ops 520 }; 521 522 static struct modlinkage modlinkage = { 523 MODREV_1, 524 (void *)&modldrv, 525 NULL 526 }; 527 528 int 529 _init(void) 530 { 531 int err; 532 533 err = nb_init(); 534 if (err == 0 && (err = mod_install(&modlinkage)) == 0) 535 rw_init(&inb_mc_lock, NULL, RW_DRIVER, NULL); 536 537 return (err); 538 } 539 540 int 541 _info(struct modinfo *modinfop) 542 { 543 return (mod_info(&modlinkage, modinfop)); 544 } 545 546 int 547 _fini(void) 548 { 549 int err; 550 551 if ((err = mod_remove(&modlinkage)) == 0) { 552 nb_unload(); 553 rw_destroy(&inb_mc_lock); 554 } 555 556 return (err); 557 } 558