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, 524 NULL 525 }; 526 527 int 528 _init(void) 529 { 530 int err; 531 532 err = nb_init(); 533 if (err == 0 && (err = mod_install(&modlinkage)) == 0) 534 rw_init(&inb_mc_lock, NULL, RW_DRIVER, NULL); 535 536 return (err); 537 } 538 539 int 540 _info(struct modinfo *modinfop) 541 { 542 return (mod_info(&modlinkage, modinfop)); 543 } 544 545 int 546 _fini(void) 547 { 548 int err; 549 550 if ((err = mod_remove(&modlinkage)) == 0) { 551 nb_unload(); 552 rw_destroy(&inb_mc_lock); 553 } 554 555 return (err); 556 }